Seite 1 von 1

Statische Membervariable.

Verfasst: 27.05.2012, 19:57
von Fitim
Hallo zusammen.

Ich versuche mich gerade an einer simplen DirectX Anwendung. Meine C++ Kenntnisse möchte ich doch als eher bescheiden bezeichnen da die Programmierung ein reines Hobby ist.
Ich habe bereits eine simple Win32 Fensterumgebung implementiert und möchte nun einen Tastaturhook verwenden um verschiedene Tastenkombinationen zu verarbeiten.
Dabei bin ich auf ein Designproblem gestossen, auf welches ich keine befriedigende Lösung gefunden habe.
Zuerst mal der betreffende Code:

Code: Alles auswählen

#define WIN32_LEAN_AND_MEAN
#define NOMINMAX

#include <cassert>
#include <exception>
#include <stdexcept>
#include <Windows.h>

//=================================================================================================
// UtilityNonCopyAssignable.hpp
//=================================================================================================

namespace Tutorial
{
	namespace Utility
	{
		class NonCopyAssignable
		{
		protected:

			NonCopyAssignable() {}

		private:

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

//=================================================================================================
// SystemWindow.hpp/cpp
//=================================================================================================

namespace Tutorial
{
	namespace System
	{
		class Window : protected Utility::NonCopyAssignable
		{
		protected:
						
			::HINSTANCE const myInstance;
			::WCHAR const * myClassName;
			::HWND myWindow;

		protected:

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

			Window(::HINSTANCE itsInstance, WCHAR const * className) :
				myInstance(itsInstance),
				myClassName(className)
			{
				auto const windowIcon = ::LoadIconW(nullptr, IDI_APPLICATION); // Todo ...

				::WNDCLASSEXW description = { 0 };
				description.cbSize = sizeof(description);
				description.hbrBackground = reinterpret_cast<::HBRUSH>(::GetStockObject(BLACK_BRUSH));
				description.hCursor = ::LoadCursorW(nullptr, IDC_ARROW);
				description.hIcon = windowIcon;
				description.hIconSm = windowIcon;
				description.hInstance = myInstance;
				description.lpfnWndProc = &WindowProcedure;
				description.lpszClassName = myClassName;
				description.style = CS_DBLCLKS;

				if (!::RegisterClassExW(&description))
				{
					throw std::runtime_error("::RegisterClassExW");
				}

				myWindow = ::CreateWindowExW(
					WS_EX_APPWINDOW,
					myClassName,
					nullptr,
					WS_OVERLAPPEDWINDOW,
					CW_USEDEFAULT,
					CW_USEDEFAULT,
					CW_USEDEFAULT,
					CW_USEDEFAULT,
					nullptr,
					nullptr,
					myInstance,
					reinterpret_cast<::LPVOID>(this));

				if (myWindow == nullptr)
				{		
					// Die Fensterklasse abmelden da der Destruktor nicht mehr aufgerufen wird.
					::UnregisterClassW(myClassName, myInstance);
					throw std::runtime_error("::CreateWindowExW");
				}
			}

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

			virtual ~Window()
			{
				::DestroyWindow(myWindow);
				::UnregisterClassW(myClassName, myInstance);
			}

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

			virtual bool MessageHandler(::UINT message, ::WPARAM firstParameter, ::LPARAM secondParameter)
			{
				return true;
			}

		private:

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

			static ::LRESULT CALLBACK WindowProcedure(::HWND window, ::UINT message, ::WPARAM firstParameter, ::LPARAM secondParameter)
			{
				if (message == WM_NCCREATE)
				{
					// Die beim erstellen des Fensters übergebene Fensterinstanz speichern
					// damit deren Nachrichtenhandler aufgerufen werden kann.
					::SetWindowLongPtrW(
						window,
						GWL_USERDATA,
						reinterpret_cast<::LONG_PTR>(
						reinterpret_cast<::LPCREATESTRUCTW>(secondParameter)->lpCreateParams));
				}
				else
				{
					auto const userWindow = reinterpret_cast<System::Window *>(::GetWindowLongPtrW(window, GWL_USERDATA));

					// try {

					// Den Nachrichtenhandler aufrufen.
					if (userWindow && !userWindow->MessageHandler(message, firstParameter, secondParameter))
					{
						return 0;
					}
					
					// catch (...) {}
				}

				return ::DefWindowProcW(window, message, firstParameter, secondParameter);
			}
		};
	}
}

//=================================================================================================
// SystemKeyboardHook.hpp/cpp
//=================================================================================================

namespace Tutorial
{
	namespace System
	{
		class KeyboardHook : protected Utility::NonCopyAssignable
		{
		private:
						
			::HINSTANCE const myInstance;
			::HOOKPROC const myHookProcedure;
			::HHOOK myHook;

		public:

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

			KeyboardHook(::HINSTANCE const instance, ::HOOKPROC const hookProcedure) :
				myInstance(instance),
				myHookProcedure(hookProcedure),
				myHook(nullptr) {}

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

			~KeyboardHook()
			{
				Enable(false);
			}

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

			void Enable(bool enable)
			{
				if (enable)
				{
					if (myHook == nullptr)
					{
						myHook = ::SetWindowsHookExW(WH_KEYBOARD_LL, myHookProcedure, myInstance, 0);
					}
				}
				else
				{
					if (myHook)
					{
						::UnhookWindowsHookEx(myHook);
						myHook = nullptr;
					}
				}
			}

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

			::LRESULT CallNextHook(int code, ::WPARAM firstParameter, ::LPARAM secondParameter) const
			{
				return ::CallNextHookEx(myHook, code, firstParameter, secondParameter);
			}
		};
	}
}

//=================================================================================================
// SystemKeyboardHookListener.hpp/cpp
//=================================================================================================

namespace Tutorial
{
	namespace System
	{
		class KeyboardHookListener : protected Utility::NonCopyAssignable
		{
		protected:

			static KeyboardHookListener * mySelf;
			KeyboardHook myKeyboardHook;			

		protected:

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

			explicit KeyboardHookListener(::HINSTANCE const instance) :
				myKeyboardHook(instance, &LowLevelKeyboardProcedure)
			{
				// Nur eine Instanz zulassen.
				assert(mySelf == nullptr);
				mySelf = this;
			}

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

			virtual ~KeyboardHookListener()
			{
				mySelf = nullptr;
			}

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

			virtual bool KeystrokeHandler(bool alt, bool shift, bool control, ::DWORD key)
			{
				return true;
			}

		private:

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

			static ::LRESULT CALLBACK LowLevelKeyboardProcedure(int code, ::WPARAM firstParameter, ::LPARAM secondParameter)
			{				
				if (code >= 0 && code == HC_ACTION)
				{
					auto const keyboardData = reinterpret_cast<::KBDLLHOOKSTRUCT *>(secondParameter);
					auto const alt = (keyboardData->flags & LLKHF_ALTDOWN) != 0;
					auto const shift = (::GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
					auto const control = (::GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0;

					// try {

					// Den Nachrichtenhandler aufrufen.
					if (!mySelf->KeystrokeHandler(alt, shift, control, keyboardData->vkCode))
					{
						return 1;
					}
					
					// catch (...) {}
				}

				return mySelf->myKeyboardHook.CallNextHook(code, firstParameter, secondParameter);
			}
		};

		// Statischen Member initialisieren.
		KeyboardHookListener * KeyboardHookListener::mySelf = nullptr;
	}
}

//=================================================================================================
// Application.hpp/cpp
//=================================================================================================

namespace Tutorial
{
	class Application : protected System::Window, protected System::KeyboardHookListener
	{
	private:

		bool myFullscreenState;

	public:

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

		explicit Application(::HINSTANCE const instance) :
			Window(instance, L"RenderWindow"),
			KeyboardHookListener(instance),
			myFullscreenState(false)
		{
			::SetWindowTextW(myWindow, L"Tutorial");
			::ShowWindow(myWindow, SW_SHOWNORMAL);
			::SetForegroundWindow(myWindow);
		}

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

		void Update()
		{
			// ...
		}

	private:

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

		void FullscreenTaskSwitchHandler()
		{
			// ...
		}

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

		bool MessageHandler(::UINT message, ::WPARAM firstParameter, ::LPARAM secondParameter)
		{
			switch (message)
			{
				case WM_CLOSE:
				{
					// Die Nachricht anschliessend nicht weiterverarbeiten da ansonsten ::DestroyWindow 
					// aufgerufen werden würde. Dies aber erledigt der Destruktor schon für uns.
					::PostQuitMessage(0);
					return false;
				}
				case WM_ACTIVATE:
				{
					// Den Tastaturhook nur aktivieren wenn das Fenster aktiv ist.
					myKeyboardHook.Enable(firstParameter != WA_INACTIVE);
					break;
				}
			}

			return true;
		}

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

		bool KeystrokeHandler(bool alt, bool shift, bool control, ::DWORD key)
		{
			// Verschiedene Tastenkombinationen verwerfen.
			if ((alt && key == VK_ESCAPE) ||
				(control && key == VK_ESCAPE) ||
				(control && shift && key == VK_ESCAPE) ||
				(key == VK_LWIN || key == VK_RWIN))
			{
				return false;
			}
						
			// Einen Vollbildmodustaskwechsel selber implementieren da DXGI intern nur den
			// Fenstermodus wiederherstellt. Wir aber wollen ein minimiertes Fenster welches
			// beim anklicken wieder in den Vollbildmodus wechselt.
			if (alt && key == VK_TAB && myFullscreenState)
			{
				FullscreenTaskSwitchHandler();
				return false;
			}

			return true;
		}
	};
}

//=================================================================================================
// WinMain.cpp
//=================================================================================================

int WINAPI WinMain(::HINSTANCE instance, ::HINSTANCE previousInstance, char * commandLine, int showCommand)
{
	::MSG message = { 0 };

	try
	{
		Tutorial::Application application(instance);

		while (message.message != WM_QUIT)
		{
			// Alle verarbeiteten Nachrichten anschliessend entfernen.
			if (::PeekMessageW(&message, nullptr, 0, 0, PM_REMOVE))
			{
				::TranslateMessage(&message);
				::DispatchMessageW(&message);
			}
			else
			{
				application.Update();
			}
		}
	}
	catch (std::exception const & exception)
	{
		::MessageBoxA(nullptr, exception.what(), "Tutorial Exception", MB_ICONERROR | MB_SETFOREGROUND);
	}

	return static_cast<int>(message.wParam);
}
Die Fensternachrichtenprozedur konnte ich erfolgreich in meine Fensterklasse einbinden, die Tastaturprozedur aber bringe ich nur mit einem statischen Member zum laufen,
da diese auf private Member der Tastaturhookklasse zugreiffen muss. Im Internet bin ich auf das Singleton-Idom gestossen, welches ich aber nicht als schön emfpinde da
eigentlich nichts gegen mehrere Tastaturhookinstanzen spricht. Also habe ich eine Tastaturhooklistenerklasse implementiert, welche nun einfach einen statischen Member
auf sich selber verwendet. Der Nachteil, auch von der Listenerklasse kann nur noch eine Instanz erstellt werden.
Könnt ihr mir einen Tipp geben, wie ich dies bei mehreren Fensters zum laufen bringen könnte?

Besten Dank

Re: Statische Membervariable.

Verfasst: 27.05.2012, 20:56
von Krishty
Hallo und willkommen!

Da mir dein Stil sehr sympathisch erscheint, werde ich mich einfach mal einklinken. Ich sehe da zwei Probleme: Zum einen das von dir angesprochene Problem, einen speziellen Tastatur-Hook-Listener an eine spezielle Fensterinstanz zu kriegen; und zum anderen, dass Hooks und GetAsyncKeyState() so ziemlich die ungünstigste Art und Weise sind, Tastenkombinationen abzufangen. Ich würde beides auf einen Streich entwirren:

Für das Verarbeiten von Tastenkombinationen sieht die WinAPI afaik Accelerators vor. Ich weiß zwar nicht, wie man welche anlegt und einschaltetet; aber ich weiß, dass du dafür an der Stelle, wo du im Augenblick TranslateMessage() aufrufst, stattdessen TranslateAccelerator() aufrufen musst. Danach erscheint der Befehl, der via Accelerator an eine Tastenkombination gebunden war, als WM_COMMAND-Nachricht in deiner Fensterprozedur.

Da deine Fenster sowieso nach Art der Interaktion sortiert sein sollen (jede Art von Fenster hat seine eigene Fensterklasse; und damit dieselbe Fensterprozedur), würde jede Art von Fenster ihre eigene WM_COMMAND-Verarbeitung und ihren eigenen Accelerator Table erhalten.

Damit würden dann sowohl der Hook als auch das Listener-Muster entfallen und du hättest nur noch deine Fensterprozedur.

Mit allem weiteren muss dir jemand helfen, der/die was von der WinAPI versteht ;)

Gruß, Ky

Re: Statische Membervariable.

Verfasst: 27.05.2012, 21:11
von Fitim
Ahhh sehr schön.
Ich denke, die MSDN "About Keyboard Accelerators" wird mir weiterhelfen können.

Besten Dank.

Re: Statische Membervariable.

Verfasst: 28.05.2012, 16:18
von Fitim
Ich habe mich nun an den Acceleratorstables versucht und diese erfolgreich zum laufen gebracht. Mehr oder weniger ;)
Leider funktionieren bei mir aus mir nicht ersichtlichen Gründen verschiedene Tastenkombinationen nicht, z.B. Alt + Tab.
Auch diverse MSDN Einträge haben mir nicht weiterhelfen können. Doch habe ich nun eine befriedigende Lösung zu meinem
oben beschriebenen Problem gefunden.

Hier meine zwischenzeitliche Lösung, bis ich die Acceleratorstables 100% zum laufen gebracht habe:

Code: Alles auswählen

#define WIN32_LEAN_AND_MEAN
#define NOMINMAX

#include <cassert>
#include <exception>
#include <stdexcept>
#include <Windows.h>
#include "../Include/Resource.hpp"

//=================================================================================================
// UtilityNonCopyAssignable.hpp
//=================================================================================================

namespace Tutorial
{
	namespace Utility
	{
		class NonCopyAssignable
		{
		protected:

			NonCopyAssignable() {}

		private:

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

//=================================================================================================
// SystemWindow.hpp/cpp
//=================================================================================================

namespace Tutorial
{
	namespace System
	{
		class Window : protected Utility::NonCopyAssignable
		{
		protected:
						
			::HINSTANCE const myInstance;
			::WCHAR const * myClassName;
			::HWND myWindow;

		private:

			::HACCEL myAcceleratorTable;

		protected:

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

			Window(::HINSTANCE itsInstance, ::WCHAR const * className) :
				myInstance(itsInstance),
				myClassName(className)				
			{
				auto const windowIcon = ::LoadIconW(itsInstance, MAKEINTRESOURCE(IDI_WINDOW_ICON));
								
				::WNDCLASSEXW description = { 0 };
				description.cbSize = sizeof(description);
				description.hbrBackground = reinterpret_cast<::HBRUSH>(::GetStockObject(BLACK_BRUSH));
				description.hCursor = ::LoadCursorW(nullptr, IDC_ARROW);
				description.hIcon = windowIcon;
				description.hIconSm = windowIcon;
				description.hInstance = myInstance;
				description.lpfnWndProc = &WindowProcedure;
				description.lpszClassName = myClassName;
				description.style = CS_DBLCLKS;

				if (!::RegisterClassExW(&description))
				{
					throw std::runtime_error("::RegisterClassExW");
				}

				myWindow = ::CreateWindowExW(
					WS_EX_APPWINDOW,
					myClassName,
					nullptr,
					WS_OVERLAPPEDWINDOW,
					CW_USEDEFAULT,
					CW_USEDEFAULT,
					CW_USEDEFAULT,
					CW_USEDEFAULT,
					nullptr,
					nullptr,
					myInstance,
					reinterpret_cast<::LPVOID>(this));

				if (myWindow == nullptr)
				{		
					// Die Fensterklasse abmelden da der Destruktor nicht mehr aufgerufen wird.
					::UnregisterClassW(myClassName, myInstance);
					throw std::runtime_error("::CreateWindowExW");
				}

				myAcceleratorTable = ::LoadAcceleratorsW(itsInstance, MAKEINTRESOURCE(IDA_ACCELERATOR));
			}

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

			virtual ~Window()
			{
				::DestroyAcceleratorTable(myAcceleratorTable);
				::DestroyWindow(myWindow);
				::UnregisterClassW(myClassName, myInstance);
			}

		public:

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

			void ProcessMessage(::MSG message)
			{
				if (!::TranslateAcceleratorW(myWindow, myAcceleratorTable, &message))
				{
					::TranslateMessage(&message);
					::DispatchMessageW(&message);
				}
			}

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

			// Muss hier als public implementiert werden da die Tastaturhookprozedur mittels
			// einer Fensterinstanz darauf zugreiffen muss.
			virtual bool KeystrokeHandler(bool alt, bool shift, bool control, ::DWORD key)
			{
				return true;
			}

		protected:

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

			virtual bool MessageHandler(::UINT message, ::WPARAM firstParameter, ::LPARAM secondParameter)
			{
				return true;
			}

		private:

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

			static ::LRESULT CALLBACK WindowProcedure(::HWND window, ::UINT message, ::WPARAM firstParameter, ::LPARAM secondParameter)
			{
				if (message == WM_NCCREATE)
				{
					// Die beim erstellen des Fensters übergebene Fensterinstanz speichern
					// damit deren Nachrichtenhandler aufgerufen werden kann.
					::SetWindowLongPtrW(
						window,
						GWL_USERDATA,
						reinterpret_cast<::LONG_PTR>(
						reinterpret_cast<::LPCREATESTRUCTW>(secondParameter)->lpCreateParams));
				}
				else
				{
					// Den Nachrichtenhandler der Fensterinstanz aufrufen.
					auto const userWindow = reinterpret_cast<System::Window *>(::GetWindowLongPtrW(window, GWL_USERDATA));

					// try {										
					if (userWindow && !userWindow->MessageHandler(message, firstParameter, secondParameter))
					{
						return 0;
					}					
					// catch (...) {}
				}

				return ::DefWindowProcW(window, message, firstParameter, secondParameter);
			}
		};
	}
}

//=================================================================================================
// SystemAccessibilityKeys.hpp/cpp
//=================================================================================================

namespace Tutorial
{
	namespace System
	{
		// Der DXUT.cpp entnommen.
		class AccessibilityKeys : protected Utility::NonCopyAssignable
		{
		private:

			::STICKYKEYS myStartupStickyKeys;
			::TOGGLEKEYS myStartupToggleKeys;
			::FILTERKEYS myStartupFilterKeys;

		public:

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

			AccessibilityKeys()
			{
				myStartupStickyKeys.cbSize = sizeof(::STICKYKEYS);
				myStartupStickyKeys.dwFlags = 0;

				myStartupToggleKeys.cbSize = sizeof(::TOGGLEKEYS);
				myStartupToggleKeys.dwFlags = 0;

				myStartupFilterKeys.cbSize = sizeof(::FILTERKEYS);
				myStartupFilterKeys.dwFlags = 0;

				::SystemParametersInfoW(SPI_GETSTICKYKEYS, sizeof(::STICKYKEYS), &myStartupStickyKeys, 0);			
				::SystemParametersInfoW(SPI_GETTOGGLEKEYS, sizeof(::TOGGLEKEYS), &myStartupToggleKeys, 0);
				::SystemParametersInfoW(SPI_GETFILTERKEYS, sizeof(::FILTERKEYS), &myStartupFilterKeys, 0);
			}

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

			~AccessibilityKeys()
			{
				Enable(true);
			}

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

			void Enable(bool enable)
			{
				if (enable)
				{        
					// Die ursprünglichen Konfigurationen wiederherstellen.
					::SystemParametersInfoW(SPI_SETSTICKYKEYS, sizeof(::STICKYKEYS), &myStartupStickyKeys, 0);
					::SystemParametersInfoW(SPI_SETTOGGLEKEYS, sizeof(::TOGGLEKEYS), &myStartupToggleKeys, 0);
					::SystemParametersInfoW(SPI_SETFILTERKEYS, sizeof(::FILTERKEYS), &myStartupFilterKeys, 0);             
				}
				else
				{
					// Die Konfigurationen der Reihe nach deaktivieren.
					auto stickyKeys = myStartupStickyKeys;
					auto toggleKeys = myStartupToggleKeys;
					auto filterKeys = myStartupFilterKeys;
				
					if ((stickyKeys.dwFlags & SKF_STICKYKEYSON) == 0)
					{
						stickyKeys.dwFlags &= ~SKF_HOTKEYACTIVE;
						stickyKeys.dwFlags &= ~SKF_CONFIRMHOTKEY;

						::SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(::STICKYKEYS), &stickyKeys, 0);
					}

					if ((toggleKeys.dwFlags & TKF_TOGGLEKEYSON) == 0)
					{
						toggleKeys.dwFlags &= ~TKF_HOTKEYACTIVE;
						toggleKeys.dwFlags &= ~TKF_CONFIRMHOTKEY;

						::SystemParametersInfo(SPI_SETTOGGLEKEYS, sizeof(::TOGGLEKEYS), &toggleKeys, 0);
					}

					if ((filterKeys.dwFlags & FKF_FILTERKEYSON) == 0)
					{
						filterKeys.dwFlags &= ~FKF_HOTKEYACTIVE;
						filterKeys.dwFlags &= ~FKF_CONFIRMHOTKEY;

						::SystemParametersInfo(SPI_SETFILTERKEYS, sizeof(::FILTERKEYS), &filterKeys, 0);
					}
				}
			}
		};
	}
}

//=================================================================================================
// Application.hpp/cpp
//=================================================================================================

namespace Tutorial
{
	class Application : public System::Window
	{
	private:

		bool myActivationState;
		bool myFullscreenState;
		System::AccessibilityKeys myAccessibilityKeys;

	public:

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

		explicit Application(::HINSTANCE const instance) : Window(instance, L"RenderWindow"),
			myActivationState(false),
			myFullscreenState(false)
		{
			::SetWindowTextW(myWindow, L"Tutorial");
			::ShowWindow(myWindow, SW_SHOWNORMAL);
			::SetForegroundWindow(myWindow);
		}

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

		void Update()
		{
			// ...
		}

	private:

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

		// Kann nun als private Methode implementiert werden.
		bool KeystrokeHandler(bool alt, bool shift, bool control, ::DWORD key)
		{
			// Folgende Tastenkombinationen deaktivieren:
			// Alt + Escape
			// Control + Escape
			// Control + Shift + Escape
			// Linke Windowstaste + rechte Windowstaste
			if ((alt && key == VK_ESCAPE) ||
				(control && key == VK_ESCAPE) ||
				(control && shift && key == VK_ESCAPE) ||
				(key == VK_LWIN || key == VK_RWIN))
			{
				return false;
			}

			// Im Vollbildmodus eine eigene Implementation verwenden da DXGI ansonsten
			// einfach nur den Fenstermodus setzen würde. Wir aber wollen ein minimiertes
			// Fenster, welches beim anklicken wieder in den Vollbildmodus wechselt.
			if (alt && VK_TAB && myFullscreenState)
			{
				FullscreenTaskSwitchHandler();
				return false;
			}

			return true;
		}

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

		bool MessageHandler(::UINT message, ::WPARAM firstParameter, ::LPARAM secondParameter)
		{
			switch (message)
			{		
				case WM_GETMINMAXINFO:
				{
					// Die minimale Fenstergrösse (nicht die Klientgrösse) festlegen.
					reinterpret_cast<::MINMAXINFO *>(secondParameter)->ptMinTrackSize.x = 400;
					reinterpret_cast<::MINMAXINFO *>(secondParameter)->ptMinTrackSize.y = 400;
					break;
				}
				case WM_COMMAND:
				{
					// Acceleratortable Test.
					switch (LOWORD(firstParameter))
					{
						case IDA_TEST1: // Irgendeine Tastenkombination.
						{
							break;
						}
						case IDA_TEST2: // Irgendeine Tastenkombination.
						{
							break;
						}
					}
				}
				case WM_SYSCOMMAND:
				{
					switch (firstParameter & 0xFFF0)
					{
						case SC_KEYMENU:
						{
							// Deaktiviert das nervige "Bing" bei verschiedenen Tastenkombinationen
							// wie z.B. Alt + Enter (Fenster- Vollbildmoduswechsel).
							return false;
						}
						case SC_MONITORPOWER:
						case SC_SCREENSAVE:
						{						
							// Im Vollbildmodus den Energiesparmodus sowie den Bildschirmschoner
							// daktivieren.
							if (myFullscreenState)
							{
								return false;
							}

							break;
						}
					}

					break;
				}
				case WM_CLOSE:
				{
					// Die Nachricht anschliessend nicht weiterverarbeiten da ansonsten ::DestroyWindow 
					// aufgerufen werden würde. Dies aber erledigt der Destruktor schon für uns.
					::PostQuitMessage(0);
					return false;
				}
				case WM_ACTIVATE:
				{
					// Die Accessibilitykeys deaktivieren wenn das Fenster aktiv wird.
					myActivationState = firstParameter != WA_INACTIVE;
					myAccessibilityKeys.Enable(!myActivationState);
					break;
				}
			}

			return true;
		}

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

		void FullscreenTaskSwitchHandler()
		{
			// ...
		}
	};
}

//=================================================================================================
// WinMain.cpp
//=================================================================================================

namespace Tutorial
{
	// Wird nur von ::WinMain verwendet.
	namespace WinMainInternal
	{			
		::HHOOK keyboardHook = nullptr;

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

		::LRESULT CALLBACK LowLevelKeyboardProcedure(int code, ::WPARAM firstParameter, ::LPARAM secondParameter)
		{				
			auto const activeWindow = ::GetActiveWindow();

			// Nur fortfahren wenn der Thread ein aktives Fenster besitzt.
			if (code >= 0 && code == HC_ACTION && activeWindow)
			{
				// Die Tastatureingaben an den Tastaturhandler der aktiven Fensterinstanz weiterleiten.
				auto const keyboardData = reinterpret_cast<::KBDLLHOOKSTRUCT *>(secondParameter);
				auto const window = reinterpret_cast<System::Window *>(::GetWindowLongPtrW(activeWindow, GWL_USERDATA));						
				
				// try {
				if (window && !window->KeystrokeHandler(
					// Alt Key
					(keyboardData->flags & LLKHF_ALTDOWN) != 0,
					// Shift Key
					(::GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0,
					// Control Key
					(::GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0,
					// Virutal Key
					keyboardData->vkCode))
				{
					return 1;
				}
				// catch (...) {}
			}

			return ::CallNextHookEx(keyboardHook, code, firstParameter, secondParameter);
		}

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

		// Verwaltet den Tastaturhook.
		struct KeyboardHook : protected Utility::NonCopyAssignable
		{
			explicit KeyboardHook(::HINSTANCE const instance)
			{
				keyboardHook = ::SetWindowsHookExW(WH_KEYBOARD_LL, &LowLevelKeyboardProcedure, instance, 0);
			}

			~KeyboardHook()
			{
				::UnhookWindowsHookEx(keyboardHook);
				keyboardHook = nullptr;
			}
		};
	}
}

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

int WINAPI WinMain(::HINSTANCE instance, ::HINSTANCE previousInstance, char * commandLine, int showCommand)
{
	// Den internen Tastaturhook installieren.
	Tutorial::WinMainInternal::KeyboardHook const keyboardHook(instance);

	::MSG message = { 0 };

	try
	{		
		Tutorial::Application application(instance);

		while (message.message != WM_QUIT)
		{
			// Alle verarbeiteten Nachrichten anschliessend entfernen.
			if (::PeekMessageW(&message, nullptr, 0, 0, PM_REMOVE))
			{
				application.ProcessMessage(message);
			}
			else
			{
				application.Update();
			}			
		}
	}
	catch (std::exception const & exception)
	{
		::MessageBoxA(nullptr, exception.what(), "Tutorial Exception", MB_ICONERROR | MB_SETFOREGROUND);
	}

	return static_cast<int>(message.wParam);
}
Somit werden die Keystrokes immer an den Tastaturhandler des aktiven Fensters im Thread gesendet.
Und dazu brauche ich nur noch eine globale Variable die auch nur von ::WinMain und der Tastaturhookprozedur verwendet wird.

Eine andere Frage stellt sich mir noch.
Ich habe mittlerweile verschiedene Fenstermanipulationsfunktionen (SetWindowClientSize(), CenterWindowAtNearestWorkArea() etc.) geschrieben die in einem
System::Utility Namensbereich liegen und ein ::HWND Argument erwarten. Zuerst wollte ich diese Routinen als Methoden der Fensterklasse implementieren.
Doch dies scheint mir in diesem speziellen Fall nicht nötig da ich ja als "Anwender" mittels einer abgeleiteten Klasse vollen Zugriff auf das ::HWND Handle habe.
Somit spricht eigentlich nichts dagegen, die Fenstermanipulationsfunktionen als simple Routinen zu implementieren, oder?

Re: Statische Membervariable.

Verfasst: 28.05.2012, 21:34
von kimmi
Mach doch statt freier Funktion eine statische Methode daraus. Dann hast du die Zuordnung der Funktion zur Klasse, ohne auf die Attribute der Klasse Zugriff nehmen zum müssen. Sicherlich kannst du sie auch als Funktionen im entsprechenden Namensraum unterbringen, ich bevorzuge allerdings die statischen Methoden.

Gruß Kimmi

Re: Statische Membervariable.

Verfasst: 28.05.2012, 22:04
von dot

Re: Statische Membervariable.

Verfasst: 29.05.2012, 10:22
von BeRsErKeR
dot hat geschrieben:http://www.drdobbs.com/184401197 ;)
Ich kann dem nur beipflichten; allerdings werden viele Leute sowas nie machen. Wer an IntelliSense gewöhnt ist und nicht so viel Dokus liest, der wird wohl heute noch nichts von den globalen Funktionen der STL, wie z.B. std::getline usw., wissen. :D

Re: Statische Membervariable.

Verfasst: 29.05.2012, 11:24
von kimmi
Das hängt sicherlich auch ein wenig davon ab, welchen Coding-Conventions man unterworfen ist bzw. welchen Weg man in der Vergangenheit öfter beschritten hat. Auf jeden FAll danke für den Artikel, hat mich zum Nachdenken angeregt :).

Gruß Kimmi

Re: Statische Membervariable.

Verfasst: 29.05.2012, 12:09
von BeRsErKeR
Ich vermute dass so ziemlich keiner sowas macht. Selbst viele größere Projekte nicht. Ich hab auch viele Jahre gedacht dass Kapselung immer voraussetzt, dass alles schön in die Klasse gepackt wird bis ich den Artikel vor einiger Zeit fand und auch das Buch gekauft hatte. Ich hatte mich früher z.B. immer gewundert wieso es die meisten Funktionen in der STL nur für const char* gibt und nicht für std::string, und als ich dann die globalen Varianten mit std::string fand konnte ich mir nicht erklären wieso man diese nicht mit in die Klassen gepackt hat. Erst nach dem Artikel machte es Klick. ;)

Re: Statische Membervariable.

Verfasst: 29.05.2012, 19:03
von Fitim
Vielen Dank für den Link.
Werde mir nun demnächst ein neues C++ Buch kaufen :)

Re: Statische Membervariable.

Verfasst: 29.05.2012, 19:13
von Krishty
BeRsErKeR hat geschrieben:Ich vermute dass so ziemlich keiner sowas macht.
meeh
Templates, RAII und Ausnahmebehandlung sind die einzigen Gründe, warum ich noch nicht auf C ohne ++ umgesattelt bin.

Ich hasse Wirkung und Zustand. Methoden haben per Definition Zustand und nicht-const-Methoden haben per Definition zusätzlich Wirkung. Ich hasse Methoden.

Re: Statische Membervariable.

Verfasst: 29.05.2012, 19:24
von EyDu
Krishty hat geschrieben:Ich hasse Wirkung und Zustand. Methoden haben per Definition Zustand und nicht-const-Methoden haben per Definition zusätzlich Wirkung. Ich hasse Methoden.
Und dann bist du noch nicht auf [hier eine beliebige funktionale Sprache einsetzen] umgestiegen?

Re: Statische Membervariable.

Verfasst: 29.05.2012, 22:55
von dot
Krishty hat geschrieben:Ich hasse Wirkung und Zustand.
Dann solltest du dich wohl mal in Richtung Funktionale Programmierung umsehen ;)

Re: Statische Membervariable.

Verfasst: 30.05.2012, 17:31
von BeRsErKeR
Krishty hat geschrieben:
BeRsErKeR hat geschrieben:Ich vermute dass so ziemlich keiner sowas macht.
meeh
Templates, RAII und Ausnahmebehandlung sind die einzigen Gründe, warum ich noch nicht auf C ohne ++ umgesattelt bin.

Ich hasse Wirkung und Zustand. Methoden haben per Definition Zustand und nicht-const-Methoden haben per Definition zusätzlich Wirkung. Ich hasse Methoden.
Da wirst du mit C aber auch keine Freude haben. Da hat ja ziemlich viel eine Wirkung. Man denke nur an so Späße wie rand(). Ich finde man kann mit Wirkungen ganz gut umgehen, wenn man sie klein hält und genau spezifiziert/dokumentiert. Beim Assembler gings ja auch viele Jahre, wohl aber bedingt dadurch, dass Befehlssatz und Anzahl der Register überschaubar waren. :)