Śledzenie klawiatury lepiej niż używając RegisterHotKey

0

Witam.

Zauważyłem, że mój program (służy do zabijania procesu okna pierwszoplanowego okna np. zawieszonej gry) nie działa w niektórych grach, lub w szczególnych sytuacjach.
Chodzi o to, że wciskam sobie Ctrl+Alt+Shift+K i program ma zakończyć proces od okna pierwszoplanowego. Niestety czasem do mojego programu nie trafia komunikat WM_HOTKEY.

Znacie lepszy sposób niż Global HotKeys?

0

Dla pojedyńczej sesji - DirectInput, dla wszystkich sesji trzeba czytać bezpośrednio z \Device\KeyboardClass0.

#define DIRECTINPUT_VERSION 0x0500
#pragma comment(lib, "dinput.lib")
#include <dinput.h>

DWORD __stdcall HotkeyThread(HANDLE hExitEvent)
{
	BOOL fContinueLoop = TRUE;

	IDirectInput       *dinput;
	IDirectInputDevice *keyboard;

	char keys[256];
	HANDLE hEvents[2];
	DWORD index;
	HWND hwnd = GetForegroundWindow();

	hEvents[0] = CreateEvent(0,0,0,0);
	hEvents[1] = hExitEvent;

	if (!DirectInputCreate(GetModuleHandle(0), 0x0500, &dinput, 0))
	{
		if (!dinput->CreateDevice(GUID_SysKeyboard, &keyboard, 0))
		{
			keyboard->SetDataFormat(&c_dfDIKeyboard);
			keyboard->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE);
			keyboard->SetEventNotification(hEvents[0]);
			keyboard->Acquire();

			while (fContinueLoop)
			{
				// main loop
				index = WaitForMultipleObjects(1, hEvents, FALSE, INFINITE);

				if (!index)
				{
					// jakiś klawisz wciśnięto
					keyboard->GetDeviceState(sizeof(keys), keys);
					if ((keys[DIK_LCONTROL] & 0x80)
					&& (keys[DIK_LMENU] & 0x80) // left alt
					&& (keys[DIK_LSHIFT] & 0x80)
					&& (keys[DIK_K] & 0x80))
					{
						// hotkey action
						MessageBeep(MB_ICONHAND);
					}
				}
				else if (index == 1)
				{
					fContinueLoop = false;
				}
			}
			keyboard->Unacquire();
			keyboard->Release();
		}
		dinput->Release();
	}
	CloseHandle(hEvents[0]);
	return 0;
}

Jeżeli masz jakieś okno w programie, to powyższą funkcję uruchamiasz w osobnym wątku:

int __stdcall WinMain(HINSTANCE hinstance,HINSTANCE,LPSTR,int)
{
	HANDLE hExitEvent = CreateEvent(0,0,0,0);
	DWORD id;
	HANDLE hThread = CreateThread(0,0, HotkeyThread, (void*)hExitEvent, 0, &id);
	// main loop here
	// while (GetMessage())
	MessageBox(0, "wciśnij Ctrl+Alt+Shift+K", "", 0);

	CloseHandle(hExitEvent); // przerywa while w HotkeyThread
	WaitForSingleObject(hThread, INFINITE);
	CloseHandle(hThread);
	return 0;
}
0

Zobacz na jego podpis, a dopiero publikuj kod c++.

Piszę programy w Delphi używając tylko WinAPI.
Nie używam VCL.

Pogrubienie moje.

0

No i co z tego?, ja nie piszę w Delphi ale potrafię przetłumaczyć kod dowolnego języka na dowolny inny język. Programowanie nie polega na zakładaniu klapek na oczy.
Jeżeli on sobie nie poradzi z przetłumaczeniem na Delphi, to pewnie poprosi o pomoc.

0

Dokładnie sapero. Wszystko mi jedno czy C/C++ czy Delphi.
Muszę to uwzględnić w sygnaturze.

Zaraz się pobawię Twoim kodem. Dzięki.

EDIT:

Ok. Mam jeszcze dwa pytania:

1.) Czy wszystkie stałe takie jak np. VK_CONTROL, VK_MENU, lub inne mają identyczne wartości jak te z DirectInput ?
2.) Czy da się jakoś wygodnie porównać stan wszystkich klawiszy, czy trzeba każdy z osobna?
Bo ja robię tak, że user wprowadza do kontrolki edycyjnej klawisze, jakie muszą zostać wciśnięte, aby wykonać akcję.
Chciałbym to sobie zapisać do jakiejś zmiennej i w wątku porównać. Myślę, że trzeba będzie w trakcie edycji kontrolki update-ować nową-drugą tablicę przechowującą stany klawiszy i porównywać je. Na razie robię to na zasadzie porównywania poszczególnych bajtów tablic, ale nie wiem czy tak można? Bo póki co nie działa - tablice niby nie są równe.

0
UINT hk = SendDlgItemMessage(hwnd, 1001, HKM_GETHOTKEY, 0, 0);
UINT dik_index = MapVirtualKey(hk&255, 0);

W dik_index będziesz miał stałą DIK_ dla danego klawisza, wystarczy że sprawdzisz keys[dik_index] + opcjonalnie za pomocą czterech if'ów, czy modyfikatory też są wciśnięte:

int mod = (hk>>8) & 15;
if ((mod & MOD_ALT) && (keys[DIK_LMENU]&128))
	mod &= ~MOD_ALT; // zeruj bit

if ((mod & MOD_CONTROL) && (keys[DIK_LCONTROL]&128))
	mod &= ~MOD_CONTROL;

if ((mod & MOD_SHIFT) && (keys[DIK_LMENU]&128))
	mod &= ~MOD_SHIFT;

if ((mod & MOD_WIN) && (keys[DIK_LWIN]&128))
	mod &= ~MOD_WIN;

if (mod == 0) {modyfikatory pasują}

Stałe MOD_ mają wartości 1,2,4,8, więc ich suma to max 15. Jeżeli MOD_ALT jest ustawiony i klawisz ALT wciśnięcy, to zerujemy MOD_ALT. Podobnie dla pozostałych trzech. Następnie gdy zmienna mod będzie zerem, to albo nie było modyfikatorów w hotkey, albo wszystkie te klawisze są wciśnięte.

Zamiast sprawdzać tylko DIK_LMENU, możesz sprawdzić też prawy ALT - DIK_RMENU, jako alias dla lewego klawisza:

if ((mod & MOD_WIN) && ((keys[DIK_LWIN]|keys[DIK_RWIN])&128))

Co do mojego pierwszego kodu - GetDeviceState może zwrócić błąd DIERR_INPUTLOST, wtedy powinno się wywołać ponownie Acquire(). No ale to są podstawy.

1 użytkowników online, w tym zalogowanych: 0, gości: 1