Wann gilt eine Klasse als initialisiert?

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Fitim
Beiträge: 12
Registriert: 27.05.2012, 19:29

Wann gilt eine Klasse als initialisiert?

Beitrag von Fitim »

Hallo zusammen,

endlich habe ich mal wieder Zeit gefunden, mich meinem kleinen Spielchen zu widmen. Dazu musste ich mich mal wieder in meinen Code einarbeiten und bin bei bei meiner Fensterimplementation auf ein Konstrukt gestossen,
zu welchem ich eine Frage habe. Aber zuerst mal mein zusammengefasster Fenstercode:

Code: Alles auswählen

#include <exception>
#include <string>
#include <utility>

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define WIN32_LEAN_AND_MEAN
#define STRICT
#define NOGDICAPMASKS           // - CC_*, LC_*, PC_*, CP_*, TC_*, RC_
#define NOSYSMETRICS            // - SM_*
#define NOMENUS                 // - MF_*
#define NOKEYSTATES             // - MK_*
#define NORASTEROPS             // - Binary and Tertiary raster ops
#define OEMRESOURCE             // - OEM Resource values
#define NOATOM                  // - Atom Manager routines
#define NOCLIPBOARD             // - Clipboard routines
#define NOCOLOR                 // - Screen colors
#define NOCTLMGR                // - Control and Dialog routines
#define NODRAWTEXT              // - DrawText() and DT_*
#define NOKERNEL                // - All KERNEL defines and routines
#define NOMEMMGR                // - GMEM_*, LMEM_*, GHND, LHND, associated routines
#define NOMETAFILE              // - typedef METAFILEPICT
#define NOMINMAX                // - Macros min(a,b) and max(a,b)
#define NOOPENFILE              // - OpenFile(), OemToAnsi, AnsiToOem, and OF_*
#define NOSCROLL                // - SB_* and scrolling routines
#define NOSERVICE               // - All Service Controller routines, SERVICE_ equates, etc.
#define NOSOUND                 // - Sound driver routines
#define NOTEXTMETRIC            // - typedef TEXTMETRIC and associated routines
#define NOWH                    // - SetWindowsHook and WH_*
#define NOCOMM                  // - COMM driver routines
#define NOKANJI                 // - Kanji support stuff.
#define NOHELP                  // - Help engine interface.
#define NOPROFILER              // - Profiler interface.
#define NODEFERWINDOWPOS        // - DeferWindowPos routines
#define NOMCX                   // - Modem Configuration Extensions

// #define NOVIRTUALKEYCODES    // - VK_*
// #define NOWINMESSAGES        // - WM_*, EM_*, LB_*, CB_*
// #define NOWINSTYLES          // - WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
// #define NOICONS              // - IDI_*
// #define NOSYSCOMMANDS        // - SC_*
// #define NOSHOWWINDOW         // - SW_*
// #define NOGDI                // - All GDI defines and routines
// #define NOUSER               // - All USER defines and routines
// #define NONLS                // - All NLS defines and routines
// #define NOMB                 // - MB_* and MessageBox()
// #define NOMSG                // - typedef MSG and associated routines
// #define NOWINOFFSETS         // - GWL_*, GCL_*, associated routines

#include <Windows.h>

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#if defined _M_IX86
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_IA64
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_X64
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#else
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Tutorial
{
	class INonCopyAssignable
	{
	protected:

		INonCopyAssignable()
		{
			return;
		}

	private:

		INonCopyAssignable(INonCopyAssignable const & /* itsInstanceToCopy */);
		INonCopyAssignable & operator = (INonCopyAssignable const & /* itsInstanceToAssign */);
	};
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Tutorial 
{
	namespace Windows
	{
		::std::string CreateUTF8String(::std::wstring const & itsWideStringToConvert)
		{
			auto const sizeOfWideStringToConvert = static_cast<int>(itsWideStringToConvert.size());

			if (0 == sizeOfWideStringToConvert)
			{
				return std::string();
			}

			//
			// Die Grösse des zu erstellenden UTF8 Strings ermitteln.
			//
						
			auto const sizeOfStringToCreate = ::WideCharToMultiByte(
				CP_UTF8,
				0,
				&itsWideStringToConvert[0],
				sizeOfWideStringToConvert,
				nullptr,
				0,
				nullptr,
				nullptr);

			if (0 == sizeOfStringToCreate)
			{
				throw std::exception("::WideCharToMultiByte() failed.");
			}

			//
			// Den UTF8 String erstellen und initialisieren.
			//

			::std::string stringToCreate(sizeOfStringToCreate, 0);

			auto const numberOfCharsWritten = ::WideCharToMultiByte(
				CP_UTF8,
				0,
				&itsWideStringToConvert[0],
				sizeOfWideStringToConvert,
				&stringToCreate[0],
				sizeOfStringToCreate,
				nullptr,
				nullptr);

			if (0 == numberOfCharsWritten)
			{
				throw std::exception("::WideCharToMultiByte() failed.");
			}

			return stringToCreate;
		}

		::std::wstring CreateUTF16WideString(::std::string const & itsStringToConvert)
		{
			auto const sizeOfStringToConvert = static_cast<int>(itsStringToConvert.size());

			if (0 == sizeOfStringToConvert)
			{
				return std::wstring();
			}

			//
			// Die Grösse des zu erstellenden UTF16 Widestrings ermitteln.
			//
						
			auto const sizeOfWideStringToCreate = ::MultiByteToWideChar(
				CP_UTF8,
				0,
				&itsStringToConvert[0],
				sizeOfStringToConvert,
				nullptr,
				0);

			if (0 == sizeOfWideStringToCreate)
			{
				throw std::exception("::MultiByteToWideChar() failed.");
			}

			//
			// Den UTF16 Widestring erstellen und initialisieren.
			//

			::std::wstring wideStringToCreate(sizeOfWideStringToCreate, 0);

			auto const numberOfWideCharsWritten = ::MultiByteToWideChar(
				CP_UTF8,
				0,
				&itsStringToConvert[0],
				sizeOfStringToConvert,
				&wideStringToCreate[0],
				sizeOfWideStringToCreate);

			if (0 == numberOfWideCharsWritten)
			{
				throw std::exception("::MultiByteToWideChar() failed.");
			}

			return wideStringToCreate;
		}
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Tutorial 
{
	namespace Windows
	{
		typedef ::std::pair<::size_t, ::size_t> UnsignedSizeType;

		UnsignedSizeType GetWindowClientSize(::HWND const itsWindow)
		{
			if (nullptr == itsWindow)
			{
				return UnsignedSizeType();
			}

			::RECT clientRectangle;
			::GetClientRect(itsWindow, &clientRectangle);

			return UnsignedSizeType(
				static_cast<::size_t>(clientRectangle.right), 
				static_cast<::size_t>(clientRectangle.bottom));
		}

		void SetWindowClientSize(::HWND const itsWindow, UnsignedSizeType const itsClientSize)
		{
			if (nullptr == itsWindow)
			{
				return;
			}

			//
			// Die neue Fenstergrösse anhand des gesetzten Fensterstils und der neuen Klientgrösse
			// berechnen und setzen.
			//

			::RECT windowRectangle =
			{
				0,
				0,
				static_cast<::LONG>(itsClientSize.first),
				static_cast<::LONG>(itsClientSize.second),
			};

			::AdjustWindowRectEx(
				&windowRectangle, 
				static_cast<::DWORD>(::GetWindowLongPtrW(itsWindow, GWL_STYLE)), 
				FALSE, 
				static_cast<::DWORD>(::GetWindowLongPtrW(itsWindow, GWL_EXSTYLE)));

			::SetWindowPos(
				itsWindow,
				nullptr, 
				0,
				0,
				static_cast<int>(windowRectangle.right - windowRectangle.left),
				static_cast<int>(windowRectangle.bottom - windowRectangle.top),
				SWP_NOMOVE |
				SWP_NOZORDER |
				SWP_NOACTIVATE);

			return;
		}

		namespace ScreenArea
		{
			enum Type
			{	
				//
				// Entspricht dem Desktop minus des Taskleiste.
				//

				Workspace,

				//
				// Entspricht dem Desktop mitsamt der Taskleiste.
				//

				Desktop
			};
		}

		void CenterWindowAtNearestScreen(::HWND const itsWindow, ScreenArea::Type const itsScreenAreaArea)
		{
			if (nullptr == itsWindow)
			{
				return;
			}
			
			//
			// Als erstes das nächtsgelegen Anzeigegerät und dessen Eigenschaften ermitteln.
			//

			::RECT windowRectangle;
			::GetWindowRect(itsWindow, &windowRectangle);

			auto const nearestScreen = ::MonitorFromRect(&windowRectangle, MONITOR_DEFAULTTONEAREST);

			::MONITORINFO screenInfo;   
			screenInfo.cbSize = sizeof(::MONITORINFO);
			::GetMonitorInfoW(nearestScreen, &screenInfo);

			//
			// Das Fenster anhand des gewünschten Bereiches zentrieren.
			//

			auto const screenRectangle = (ScreenArea::Desktop == itsScreenAreaArea) ? 				
				screenInfo.rcMonitor :
				screenInfo.rcWork;
			
			auto const screenAreaWidth = static_cast<int>(screenRectangle.right);
			auto const screenAreaHeight = static_cast<int>(screenRectangle.bottom);
			auto const windowWidth = static_cast<int>(windowRectangle.right - windowRectangle.left);
			auto const windowHeight = static_cast<int>(windowRectangle.bottom - windowRectangle.top);

			::SetWindowPos(
				itsWindow,
				nullptr, 
				static_cast<int>((screenAreaWidth - windowWidth) * 0.5),
				static_cast<int>((screenAreaHeight - windowHeight) * 0.5),
				0,
				0,
				SWP_NOSIZE |
				SWP_NOZORDER |
				SWP_NOACTIVATE);

			return;
		}
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Tutorial
{
	namespace Windows
	{
		class IWindowListener
		{
		public:

			virtual bool MessageHandler(
				::HWND const itsWindow,
				::UINT const itsMessage,
				::WPARAM const itsWordParameter,
				::LPARAM const itsLongParameter) = 0;
		};

		void SetWindowListener(::HWND const itsWindow, IWindowListener const * toItsWindowListener)
		{
			if (nullptr == itsWindow)
			{
				return;
			}

			::SetWindowLongPtrW(itsWindow, GWLP_USERDATA, reinterpret_cast<::LONG_PTR>(toItsWindowListener));
			return;
		}
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Tutorial 
{
	namespace Windows
	{	
		namespace Internal
		{
			static ::LRESULT CALLBACK WindowProcedure(
				::HWND itsWindow,
				::UINT itsMessage,
				::WPARAM itsWordParameter,
				::LPARAM itsLongParameter)
			{
				auto processMessage = true;

				//
				// Prüfen, ob ein Fensterbeobachter gesetzt worden ist und wenn ja, dessen
				// Nachrichtenhandler aufrufen.
				//

				auto const toWindowListener = reinterpret_cast<IWindowListener *>(
					::GetWindowLongPtrW(itsWindow, GWLP_USERDATA));
								
				if (nullptr != toWindowListener)
				{
					processMessage = toWindowListener->MessageHandler(
						itsWindow, 
						itsMessage, 
						itsWordParameter, 
						itsLongParameter);
				}
				
				//
				// WM_CLOSE nicht weiterverarbeiten da ansonsten ::DestroyWindow() aufgerufen werden würde.
				// Dies aber erledigt bereits der Destruktor der betreffenden Fensterinstanz.
				//

				if (WM_CLOSE == itsMessage || !processMessage)
				{												
					return 0;
				}
				
				return ::DefWindowProcW(itsWindow, itsMessage, itsWordParameter, itsLongParameter);				
			}
		}
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Tutorial
{
	namespace Windows
	{
		class WindowClass : private INonCopyAssignable
		{
		public:

			WindowClass(
				::std::string const & itsClassName,
				::HINSTANCE const itsModuleInstance,
				::HICON const itsWindowIcon,
				::HCURSOR const itsWindowCursor,
				::HBRUSH const itsWindowBackgroundColor,
				::UINT const itsClassStyle)

				: myClassName(CreateUTF16WideString(itsClassName))
				, myModuleInstance(itsModuleInstance)
			{
				::WNDCLASSEXW const classDescription =
				{
					sizeof(::WNDCLASSEXW),
					itsClassStyle,
					&Internal::WindowProcedure,
					0,
					0,
					myModuleInstance,
					itsWindowIcon,
					itsWindowCursor,
					itsWindowBackgroundColor,
					nullptr,
					myClassName.data(),
					itsWindowIcon
				};

				if (0 == ::RegisterClassExW(&classDescription))
				{
					throw std::exception("::RegisterClassExW() failed.");
				}

				return;
			}

			WindowClass(
				::std::string const & itsClassName,
				::std::string const & itsMenuName,
				::HINSTANCE const itsModuleInstance,
				::HICON const itsWindowIcon,
				::HCURSOR const itsWindowCursor,
				::HBRUSH const itsWindowBackgroundColor,
				::UINT const itsClassStyle)

				: myClassName(CreateUTF16WideString(itsClassName))
				, myModuleInstance(itsModuleInstance)
			{
				auto const menuName = CreateUTF16WideString(itsMenuName);

				::WNDCLASSEXW const classDescription =
				{
					sizeof(::WNDCLASSEXW),
					itsClassStyle,
					&Internal::WindowProcedure,
					0,
					0,
					myModuleInstance,
					itsWindowIcon,
					itsWindowCursor,
					itsWindowBackgroundColor,
					menuName.data(),
					myClassName.data(),
					itsWindowIcon
				};

				if (0 == ::RegisterClassExW(&classDescription))
				{
					throw std::exception("::RegisterClassExW() failed.");
				}

				return;
			}

			~WindowClass()
			{
				::UnregisterClassW(myClassName.data(), myModuleInstance);
				return;
			}

		private:

			::std::wstring const myClassName;
			::HINSTANCE const myModuleInstance;
		};
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Tutorial 
{
	namespace Windows
	{
		class Window : private INonCopyAssignable
		{
		public:

			Window(
				::std::string const & itsWindowClassName,
				::std::string const & itsWindowName,
				::DWORD const itsWindowStyle,
				::DWORD const itsExtendedWindowStyle,
				::HINSTANCE const itsModuleInstance,
				::HMENU const itsMenu,
				::HWND const itsParentWindow)
			{
				auto const windowClassName = CreateUTF16WideString(itsWindowClassName);
				auto const windowName = CreateUTF16WideString(itsWindowName);

				myWindow = ::CreateWindowExW(
					itsExtendedWindowStyle,
					windowClassName.data(),
					windowName.data(),
					itsWindowStyle,
					CW_USEDEFAULT,
					CW_USEDEFAULT,
					CW_USEDEFAULT,
					CW_USEDEFAULT,
					itsParentWindow,
					itsMenu,
					itsModuleInstance,
					nullptr);

				if (nullptr == myWindow)
				{
					throw std::exception("::CreateWindowExW() failed.");
				}

				return;
			}

			~Window()
			{
				//
				// Den Benutzerbereich des Fensters freigeben damit auf dessen Daten nicht mehr
				// zugegriffen werden kann da nun das Fenster nicht mehr gültig ist.
				//

				::SetWindowLongPtrW(myWindow, GWLP_USERDATA, reinterpret_cast<::LONG_PTR>(nullptr));
				::DestroyWindow(myWindow);

				return;
			}

			::HWND GetHandle() const
			{
				return myWindow;
			}

		private:

			::HWND myWindow;
		};
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Tutorial
{
	class Application : private INonCopyAssignable, private Windows::IWindowListener
	{
	public:

		explicit Application(::HINSTANCE const itsModuleInstance)

			//
			// Bevor ein Fenster erstellt werden kann, muss bereits eine entsprechende
			// Fensterklasse registriert worden sein.
			//

			: myWindowClass(
				"RenderWindow",
				itsModuleInstance,
				::LoadIconW(nullptr, IDI_APPLICATION),
				::LoadCursorW(nullptr, IDC_ARROW),
				reinterpret_cast<::HBRUSH>(::GetStockObject(BLACK_BRUSH)),
				CS_DBLCLKS)

			, myWindow(
				"RenderWindow",
				"Tutorial",
				WS_OVERLAPPEDWINDOW,
				WS_EX_OVERLAPPEDWINDOW,
				itsModuleInstance,
				nullptr,
				nullptr)

			, myIsRunningState(true)
			, myHasEndedNormallyState(true)
		{
			auto const window = myWindow.GetHandle();

			//
			// Dem Fenster die eigene Instanz übergeben damit der Nachrichtenhandler
			// aufgerufen werden kann.
			//

			Windows::SetWindowListener(window, this);
			Windows::SetWindowClientSize(window, Windows::UnsignedSizeType(1280, 720));
			Windows::CenterWindowAtNearestScreen(window, Windows::ScreenArea::Workspace);

			::ShowWindow(window, SW_SHOWNORMAL);
			::SetForegroundWindow(window);

			return;
		}

		void RunMessageLoop()
		{
			//
			// Sicherstellen, dass nach einer Ausnhame die Nachrichtenschleife nicht erneut
			// ausgeführt werden kann.
			//

			if (!myHasEndedNormallyState)
			{
				return;
			}

			myHasEndedNormallyState = false;
			::MSG message = { 0 };

			while (myIsRunningState)
			{
				if (::PeekMessageW(&message, nullptr, 0, 0, PM_REMOVE))
				{
					::TranslateMessage(&message);
					::DispatchMessageW(&message);
				}
				else
				{
					//
					// Render();
					//
				}
			}

			myHasEndedNormallyState = true;
			return;
		}

	private:

		bool MessageHandler(
			::HWND const /* itsWindow */,
			::UINT const itsMessage, 
			::WPARAM const /* itsWordParameter */, 
			::LPARAM const /* itsLongParameter */) final override
		{
			if (WM_CLOSE == itsMessage)
			{
				myIsRunningState = false;
			}

			return true;
		}

	private:

		Windows::WindowClass const myWindowClass;
		Windows::Window const myWindow;
		bool myIsRunningState;
		bool myHasEndedNormallyState;
	};
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int WINAPI WinMain(	
	_In_ ::HINSTANCE itsModuleInstance,
	_In_opt_ ::HINSTANCE /* itsPreviousModuleInstance */,
	_In_ ::LPSTR /* toItsCommandLine */,
	_In_ int /* itsShowCommand */) 
{
	using namespace ::Tutorial;

	try
	{
		try
		{
			Application application(itsModuleInstance);
			application.RunMessageLoop();
		}
		catch (std::exception const & exception)
		{
			auto const message = Windows::CreateUTF16WideString(exception.what());
			auto const caption = Windows::CreateUTF16WideString("Tutorial - Exception");

			::MessageBoxW(nullptr, message.data(), caption.data(), MB_ICONERROR | MB_SETFOREGROUND);
		}
	}
	catch (...)
	{
		//
		// Alles im Popo.
		//

		::MessageBoxA(nullptr, "System exception.", "Tutorial - Exception", MB_ICONERROR | MB_SETFOREGROUND);
	}

	return 0;
}
In meiner Anwendungsklasse übergebe ich dem Fenster noch im Konstruktor einen this-Zeiger damit mein Nachrichtenhandler aufgerufen werden kann. Anschliessend führe ich noch weitere Fenstermanipulationen durch, welche WM_ ... Nachrichten auslösen und somit meinen Nachrichtenhandler aufrufen. Doch ist das überhaupt erlaubt? Denn zu diesem Zeitpunkt ist ja der Konstruktor noch nicht verlassen worden, und somit würde meine Nachrichtenprozedur eine noch nicht initialisierte Instanz aufrufen. Oder darf ich meinen this-Zeiger erst z.B. in der Application::RunMessageLoop() Methode setzen?

Besten Dank schon mal im Voraus
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: Wann gilt eine Klasse als initialisiert?

Beitrag von eXile »

Dass du den this-Zeiger als solches im Konstruktorrumpf benutzt, ist unproblematisch. Problematischer wird es, falls:
  • SetWindowLongPtrW aufgerufen wurde
  • Der Konstruktorrumpf noch nicht zu Ende ausgeführt wurde
  • Asynchron eine Windows-Nachricht reinkommt
  • MessageHandler aufgerufen wird
  • MessageHandler virtuelle Funktionen benutzen würde
Da der Konstruktor dann noch nicht durchgelaufen ist, würden keine V-Table-Einträge auf die überschreibende Klasse zeigen, und du würdest die Funktionen der eigenen Klasse aufrufen. Jedoch werden alle Member-Variablen initialisiert sein, da ja man bereits bis zum Konstruktorrumpf vorgestoßen ist.

Wie du schon vorgeschlagen hast, kann man das elegant umgehen, indem man SetWindowLongPtrW erst in RunMessageLoop bei (ich glaube) WM_NCCREATE aufruft.
simbad
Establishment
Beiträge: 132
Registriert: 14.12.2011, 14:30

Re: Wann gilt eine Klasse als initialisiert?

Beitrag von simbad »

Nach dem verlassen des Konstruktors.
Florian Keßeler
Beiträge: 75
Registriert: 24.07.2002, 00:00
Wohnort: Bremen
Kontaktdaten:

Re: Wann gilt eine Klasse als initialisiert?

Beitrag von Florian Keßeler »

simbad hat geschrieben:Nach dem regulären verlassen des Konstruktors.
simbad
Establishment
Beiträge: 132
Registriert: 14.12.2011, 14:30

Re: Wann gilt eine Klasse als initialisiert?

Beitrag von simbad »

Meinetwegen. Bin kein Freund von Exceptions. Das sind dann die irregulären Wege.
Wenn noch mehr dazu gehören wäre eine Aufzählung vielleicht hilfreich für den Fragesteller.
Benutzeravatar
Krishty
Establishment
Beiträge: 8245
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Wann gilt eine Klasse als initialisiert?

Beitrag von Krishty »

Außerdem: Klasse != Instanz

dot und ich haben eine schöne WinAPI-Fensterklasse, die ohne virtuelle Funktionen arbeitet. Vielleicht sollten wir das betagte WndProc-an-Klasse-binden-Tutorial mal überarbeiten; zumal das Ganze keine leichte Kost ist.

Zum Thema: this im Konstruktorrumpf ist kein Problem; in der Initialisierungsliste aber schon (weil die Attribute (umgangssprachlich nicht-statische Member-Variablen) in Reihenfolge ihrer Deklaration initialisiert werden). Die WinAPI schickt z.B. schon bei CreateWindow() die ersten Nachrichten; entsprechend den Initialisierungsregeln der Attribute und erbender Klassen kann man da leicht ein Tohuwabohu auslösen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Fitim
Beiträge: 12
Registriert: 27.05.2012, 19:29

Re: Wann gilt eine Klasse als initialisiert?

Beitrag von Fitim »

Hey Danke für die Antworten,

und schon wieder was gelernt. Nun ich werde wohl eine simple Implementierung ohne Vererbung und virtuellen Methoden verwenden. Haben mir mal schnell was zusammengebastelt:

Code: Alles auswählen

#include <Windows.h>
#include <string>
#include <exception>

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Tutorial
{
	class INonCopyAssignable
	{
	protected:

		INonCopyAssignable()
		{
			return;
		}

	private:

		INonCopyAssignable(INonCopyAssignable const &);
		INonCopyAssignable & operator = (INonCopyAssignable const &);
	};
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Tutorial
{
	class WindowClass : private INonCopyAssignable
	{
	public:

		WindowClass(::HINSTANCE const itsModuleInstance, ::WNDPROC const itsWindowProcedure)

			: myModuleInstance(itsModuleInstance)
		{
			::WNDCLASSEXW classDescription;
			classDescription.cbSize = sizeof(::WNDCLASSEXW);
			classDescription.cbClsExtra = 0;
			classDescription.cbWndExtra = 0;
			classDescription.hbrBackground = reinterpret_cast<::HBRUSH>(::GetStockObject(BLACK_BRUSH));
			classDescription.hIcon = ::LoadIconW(nullptr, IDI_APPLICATION);
			classDescription.hIconSm = ::LoadIconW(nullptr, IDI_APPLICATION);
			classDescription.hCursor = ::LoadCursorW(nullptr, IDC_ARROW);
			classDescription.hInstance = myModuleInstance;
			classDescription.lpfnWndProc = itsWindowProcedure;
			classDescription.lpszClassName = L"TutorialWindow";
			classDescription.lpszMenuName = nullptr;
			classDescription.style = CS_DBLCLKS;

			if (0 == ::RegisterClassExW(&classDescription))
			{
				throw ::std::exception("::RegisterClassExW() failed.");
			}

			return;
		}

		~WindowClass()
		{
			::UnregisterClassW(L"TutorialWindow", myModuleInstance);
			return;
		}

	private:

		::HINSTANCE const myModuleInstance;
	};
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Tutorial
{
	class Window : private INonCopyAssignable
	{
	public:

		explicit Window(::HINSTANCE const itsModuleInstance)

			: myWindow(::CreateWindowExW(
				WS_EX_OVERLAPPEDWINDOW,
				L"TutorialWindow",
				L"Tutorial",
				WS_OVERLAPPEDWINDOW,
				CW_USEDEFAULT,
				CW_USEDEFAULT,
				CW_USEDEFAULT,
				CW_USEDEFAULT,
				nullptr,
				nullptr,
				itsModuleInstance,
				nullptr))
		{
			if (nullptr == myWindow)
			{
				throw ::std::exception("::CreateWindowExW() failed.");
			}

			//
			// Hier das Fenster bereits anzeigen.
			//

			::ShowWindow(myWindow, SW_SHOWNORMAL);
			::SetForegroundWindow(myWindow);

			return;
		}

		~Window()
		{
			//
			// Die Benutzerdaten des Fensters freigeben da das Fenster nun nicht mehr gültig ist.
			//

			::SetWindowLongPtrW(myWindow, GWLP_USERDATA, reinterpret_cast<::LONG_PTR>(nullptr));
			::DestroyWindow(myWindow);

			return;
		}

		::HWND GetHandle() const
		{
			return myWindow;
		}

	private:

		::HWND myWindow;
	};
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Tutorial
{
	class Application
	{
	public:

		Application(::HINSTANCE const itsModuleInstance)

			: myWindowClass(itsModuleInstance, &Application::WindowProcedure)
			, myWindow(itsModuleInstance)
			, myIsRunningState(true)
		{
			return;
		}

		//
		// Führt die Nachrichtenschleife aus.
		//

		void RunMessageLooop()
		{
			//
			// Dem Fenster die eigene Instanz übergeben damit die Nachrichtenprozedur
			// unseren Nachrichtenhandler aufrufen kann.
			//

			::SetWindowLongPtrW(myWindow.GetHandle(), GWLP_USERDATA, reinterpret_cast<::LONG_PTR>(this));

			::MSG message = { 0 };

			while (myIsRunningState)
			{
				if (::PeekMessageW(&message, nullptr, 0, 0, PM_REMOVE))
				{
					::TranslateMessage(&message);
					::DispatchMessageW(&message);
				}
				else
				{
					//
					// Update();
					//
				}
			}

			return;
		}

	private:

		//
		// Leitet die Nachrichten an den internen Nachrichtenhandler weiter.
		//

		static ::LRESULT CALLBACK WindowProcedure(::HWND itsWindow, ::UINT itsMessage, ::WPARAM itsWordParameter, ::LPARAM itsLongParameter)
		{
			auto const toMyself = reinterpret_cast<Application *>(::GetWindowLongPtrW(itsWindow, GWLP_USERDATA));

			if (nullptr != toMyself)
			{
				return toMyself->MessageHandler(itsWindow, itsMessage, itsWordParameter, itsLongParameter);
			}
			else
			{
				return ::DefWindowProcW(itsWindow, itsMessage, itsWordParameter, itsLongParameter);
			}
		}

		//
		// Der Nachrichtenhandler.
		//

		::LRESULT MessageHandler(::HWND itsWindow, ::UINT itsMessage, ::WPARAM itsWordParameter, ::LPARAM itsLongParameter)
		{
			if (WM_CLOSE == itsMessage)
			{
				myIsRunningState = false;
				return 0;
			}
			
			return ::DefWindowProcW(itsWindow, itsMessage, itsWordParameter, itsLongParameter);
		}

	private:

		WindowClass const myWindowClass;
		Window const myWindow;
		bool myIsRunningState;
	};
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int WINAPI WinMain(::HINSTANCE itsModuleInstance, ::HINSTANCE, ::LPSTR, int)
{
	using namespace ::Tutorial;

	try
	{
		Application application(itsModuleInstance);
		application.RunMessageLooop();
	}
	catch (...)
	{
		//
		// ...
		//
	}

	return 0;
}
Ich verwende meinen Nachrichtenhandler nun einfach erst ab Application::RunMessageLoop(), früher brauche ich den ja eigentlich nicht, da ich nur Tastatureingaben sowie Fenstergrössenänderungen verarbeite.
So sollte es eigentlich sicher sein, oder?
Antworten