Tuesday, January 12, 2010

Keyboard Listener in WPF

Key board message Hooking in WPF, using C++/CLI

KeyboardListener.h
==================
#pragma once

using namespace System;
using namespace System::Diagnostics;
using namespace System::Runtime::InteropServices;
using namespace System::Runtime::CompilerServices;
using namespace System::Windows::Input;
using namespace System::Windows::Interop;

public ref class CKeyboardListener : System::IDisposable
{

public:
CKeyboardListener(void);
~CKeyboardListener(void);

public:
delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
static LowLevelKeyboardProc^ m_pLowLevelKeyboardProc;

public:
void DisposeHook(void);
static IntPtr SetHook( LowLevelKeyboardProc^ pLowLevelKeyboardProc );
static IntPtr GetWindowHandle(System::Windows::Window^ window );
static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam);

private:
static const int WH_KEYBOARD_LL = 13;
static const int WM_KEYDOWN = 0x0100;
static System::IntPtr m_hookID = IntPtr::Zero;

public :
[DllImport("user32.dll", CharSet = CharSet::Auto, SetLastError = true)]
static IntPtr SetWindowsHookEx(int idHook,LowLevelKeyboardProc^ lpfn, System::IntPtr hMod, unsigned int dwThreadId);

[DllImport("user32.dll", CharSet = CharSet::Auto, SetLastError = true)]
//[return: MarshalAs(UnmanagedType::Bool)]
static bool UnhookWindowsHookEx(System::IntPtr hhk);

[DllImport("user32.dll", CharSet = CharSet::Auto, SetLastError = true)]
static System::IntPtr CallNextHookEx(System::IntPtr hhk, int nCode,
System::IntPtr wParam, System::IntPtr lParam);

[DllImport("kernel32.dll", CharSet = CharSet::Auto, SetLastError = true)]
static IntPtr GetModuleHandle(System::String^ lpModuleName);

[ DllImport("user32.dll") ]
static IntPtr GetForegroundWindow();
};

KeyboardListener.cpp
================
#include "KeyboardListener.h"
#include "MainApp.h"

CKeyboardListener::CKeyboardListener(void)
{
m_pLowLevelKeyboardProc = gcnew LowLevelKeyboardProc( &CKeyboardListener::HookCallback);
m_hookID = SetHook( m_pLowLevelKeyboardProc );
}

CKeyboardListener::~CKeyboardListener(void)
{

}

void CKeyboardListener::DisposeHook(void)
{
UnhookWindowsHookEx( m_hookID );
}

IntPtr CKeyboardListener::SetHook(LowLevelKeyboardProc^ pLowLevelKeyboardProc)
{
Process^ curProcess = nullptr;
ProcessModule^ curModule = nullptr;

while( curProcess = Process::GetCurrentProcess() )
{
while( curModule = curProcess->MainModule )
{
return SetWindowsHookEx(WH_KEYBOARD_LL, pLowLevelKeyboardProc,
GetModuleHandle(curModule->ModuleName), 0);
}
}

return System::IntPtr::Zero;
}

IntPtr CKeyboardListener::GetWindowHandle(System::Windows::Window^ window )
{
System::Windows::Interop::WindowInteropHelper^ pWindow;
pWindow = gcnew System::Windows::Interop::WindowInteropHelper(window);
return pWindow->Handle;
}

IntPtr CKeyboardListener::HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
int l_nWindowCnt = 0;
System::IntPtr l_hForeGndWnd = GetForegroundWindow();
MainApp^ l_pCurApp = (MainApp^) Application::Current;
int l_nVirtualKeyCode = 0;

l_nVirtualKeyCode = Marshal::ReadInt32(lParam);

if ( l_nVirtualKeyCode == 112 ) //If F1 key
{
for each (Window^ hWindow in l_pCurApp->Windows)
{
++l_nWindowCnt;

System::IntPtr l_hWindow = GetWindowHandle(hWindow);

if ( l_hForeGndWnd == l_hWindow )
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
((MainScreen^)l_pCurApp->mainwnd)->LoadHelp(); //Call the LoadHelp function from MainScreen class
return CallNextHookEx( m_hookID, nCode, wParam, lParam);
}
}
}
}

return System::IntPtr::Zero;
}


And in MainApp.cpp we create an instance of the class "CKeyboardListener"

void MainApp::OnStartup( System::Windows::StartupEventArgs^ e )
{
Application::OnStartup(e);
mainwnd = gcnew MainScreen(ResourceAssembly->Location);
m_pKeyboardListener = gcnew CKeyboardListener(); //Start the key board hooking

mainwnd->Show();
}

void MainApp::OnExit( System::Windows::ExitEventArgs^ e )
{
m_pKeyboardListener->DisposeHook();
m_pKeyboardListener = nullptr;
}