Interception and resubmition of Win32 API keyboard events

Say you want to intercept the keyboard input events of an application and send the pressed key codes through the network and raise them on the application receiving them.

Interception
To easily intercept the keyboard input all you have to do is to create and install a Windows Hook that will listen for key events:

LRESULT CALLBACK GetMessageProc(int nCode, WPARAM wParam, LPARAM lParam);

MyHook::MyHook()
{
	instance = this;

	HINSTANCE hInstance = GetModuleHandle(0);

	getMsgHook = SetWindowsHookEx(WH_GETMESSAGE, GetMessageProc, hInstance, GetCurrentThreadId());
}

LRESULT CALLBACK GetMessageProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	if(nCode == HC_ACTION)
	{
		MSG *msg = (MSG*)lParam;
		if(wParam & PM_REMOVE)
			MyHook::instance->handleHook(msg->message, msg->hwnd, msg->wParam, msg->lParam);
	}

	return CallNextHookEx(MyHook::instance->getMsgHook, nCode, wParam, lParam);

}

On construction, the above MyHook used the method SetWindowsHookEx to register the hook method GetMessageProc that will be used to intercept windows messages.

The GetMessageProc signature complies with the MessageProc method specification that accepts 3 different arguments. The code that identifies how to process the message, the wParam and the lParam. The lParam contains a pointer to a MSG structure.

We then filter the interesting parts of the MSG structure and pass it to the handleHook method:

void MyHook::handleHook(UINT msg, HWND hwnd, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
		case WM_KEYDOWN:
		case WM_KEYUP:
		case WM_SYSKEYDOWN:
		case WM_SYSKEYUP:
		case WM_SYSDEADCHAR:
		case WM_SYSCHAR:
		case WM_IME_CHAR:
		case WM_IME_COMPOSITION:
		case WM_IME_COMPOSITIONFULL:
		case WM_IME_CONTROL:
		case WM_IME_ENDCOMPOSITION:
		case WM_IME_KEYDOWN:
		case WM_IME_KEYUP:
		case WM_IME_NOTIFY:
		case WM_IME_REQUEST:
		case WM_IME_SELECT:
		case WM_IME_SETCONTEXT:
		case WM_IME_STARTCOMPOSITION:
		case WM_HELP:
		case WM_CANCELMODE: 
		{
                    // Pack msg, wParam and lParam values and send them through the network.
 		    break;
		}
	}
}

Re-submission

To resubmit the key event on the receiving end you can either use SendInput method or key_event method. The following method accepts the received parameter values and raises the key event;

void MyClass::processReceivedKey(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
	DWORD extended = (0x1000000 & lParam) >> 24; // Check if KEYEVENTF_EXTENDEDKEY
	BYTE scanCode = (0xFF0000 & lParam) >> 16;

	// Check if KEYEVENTF_KEYUP, otherwise will be set to down
	LPARAM dwFlags = (msg == WM_KEYUP || msg == WM_SYSKEYUP) ? KEYEVENTF_KEYUP : 0;
	dwFlags |= extended;

	SetForegroundWindow(m_hwndReceiver); // Ensure that desired window is focused

	INPUT ip;
	ZeroMemory(&ip, sizeof(ip));
	ip.type = INPUT_KEYBOARD;
	ip.ki.wVk = wParam;
	ip.ki.wScan = scanCode;
	ip.ki.dwFlags = dwFlags;
	ip.ki.time = 0;
	ip.ki.dwExtraInfo = 0;
	SendInput(1, &ip, sizeof(ip));

	//keybd_event(keyData.wParam, scanCode, dwFlags, 0);
}
Advertisements

About CrazyPenguin

Software Engineer
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s