Code Snippet
///
/// ApplicationContext-based class to be able to create a second message pump
/// Parts lifted from Stephen Toub and http://blogs.msdn.com/toub/archive/2006/05/03/589423.aspx
///
internal class ProgramApplicationContext : ApplicationContext
{
private delegate IntPtr LowLevelKeyboardProc(
int nCode, IntPtr wParam, IntPtr lParam);
private const int WH_KEYBOARD_LL = 13;
private IntPtr hookID;
public ProgramApplicationContext()
{
Form activeForm = System.Windows.Forms.Form.ActiveForm;
// make sure we're informed of the main forms closure
System.Windows.Forms.Form.ActiveForm.FormClosed += new FormClosedEventHandler(ActiveForm_FormClosed);
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
hookID = SetWindowsHookEx(WH_KEYBOARD_LL, HookCallback,
GetModuleHandle(curModule.ModuleName), 0);
}
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
UnhookWindowsHookEx(hookID);
}
~ProgramApplicationContext()
{
Dispose(false);
}
internal static void StartMonitoringKeystrokes()
{
// create another thread to service another message pump
Thread thread = new Thread(new ThreadStart(ThreadEntry));
thread.IsBackground = false;
thread.Start();
}
private static void ThreadEntry()
{
ProgramApplicationContext ap = new ProgramApplicationContext();
Application.Run(ap);
}
private void ActiveForm_FormClosed(object sender, FormClosedEventArgs e)
{
this.ExitThread();
}
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
Trace.WriteLine(nCode);
return CallNextHookEx(hookID, nCode, wParam, lParam);
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
}