Anybody knows how can disable keyboard globally developing hooks in C#?
I have a source code that implements a hook to disable the keyboard in the same application but if I execute it and the keyboard was working in an text editor don't work it (I can keep writing) in Windows 7. I would like capture/disable the keyboard globally (for all applications that are running). My intention is to lock it completely for Windows 7.
This is my hook code:
globalKeyboardHook gkh = new globalKeyboardHook();
/// <summary>
/// Installs the global hook
/// </summary>
public void hook() {
IntPtr hInstance = LoadLibrary("User32");
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
}
public int hookProc(int code, int wParam, ref keyboardHookStruct lParam) {
//indicates if any of underlaing events set e.Handled flag
bool handled = false;
IntPtr lFakeParam = new IntPtr(lParam.vkCode);
if ((code >= 0) && (KeyDown != null || KeyUp != null || wParam == WM_SYSKEYDOWN || wParam == WM_SYSKEYUP))
{
//read structure KeyboardHookStruct at lParam
keyboardHookStruct MyKeyboardHookStruct = (keyboardHookStruct)Marshal.PtrToStructure(lFakeParam, typeof(keyboardHookStruct));
Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(keyData);
log.Debug("cached wParam = [" + wParam + "] vkCode = [" + MyKeyboardHookStruct.vkCode + "] flags = [" + MyKeyboardHookStruct.flags + "]");
if (MyKeyboardHookStruct.vkCode == 144)
{
// NUMLOCK
handled = false;
}
KeyDown(this, e);
}
//if event handled in application do not handoff to other listeners
if (handled)
{
SendKeys.Send("{NUMLOCK}");
SendKeys.Send("{NUMLOCK}");
return 1;
//return CallNextHookEx(hKeyboardHook, nCode, wParam, lFakeParam);
}
else
{
return CallNextHookEx(hhook, code, wParam, ref lParam);
}
}
Thanks in advance!
Related
I am trying to create a C# application that will react on a global hotkey; ALT+H and then when I release the ALT key. This actually does work very well but my problem is that when my application has done whatever it should do with the hotkey then it should stop this hotkey from being processed by other applications. I got most of my code below from this StackOverflow post, Global keyboard capture in C# application, but I have seen somewhere else that return (IntPtr)1; should be able to stop further processing of the key.
But... when I am in e.g. Word or Wordpad and I press ALT+H then Word shows all kind of menus - I don't want it to as I only want my application to do something :-)
I am sorry for this rather long code but I assume it is important so you can get the full overview. There is only 1 place where I use return (IntPtr)1;.
My code:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Windows.Input;
namespace MyNewProgram
{
static class Program
{
// Enable hotkeys
// https://stackoverflow.com/a/604417/2028935
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private const int WM_KEYUP = 0x0101;
private const int WM_SYSKEYUP = 0x0105;
private const int VK_SHIFT = 0x10;
private const int VK_MENU = 0x12;
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
// Other variables
public static bool isAltPressedInThisApp = false;
private static MainWindow MyMainWindow;
// --------------------------------------------------------------------------------------
[STAThread] // STAThreadAttribute indicates that the COM threading model for the application is single-threaded apartment, https://stackoverflow.com/a/1361048/2028935
static void Main()
{
// Hook application to keyboard
_hookID = SetHook(_proc);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MyMainWindow = new MainWindow();
Application.Run(MyMainWindow);
// Unhook application from keyboard
UnhookWindowsHookEx(_hookID);
}
// --------------------------------------------------------------------------------------
// Required functions for globally hooking the keyboard
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern short GetKeyState(int keyCode);
[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);
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
// --------------------------------------------------------------------------------------
// Hook the keyboard - action
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
// React on KEYDOWN
if (nCode >= 0 && ((wParam == (IntPtr)WM_KEYDOWN) || (wParam == (IntPtr)WM_SYSKEYUP)))
{
int vkCode = Marshal.ReadInt32(lParam);
// H
if ((Keys)vkCode == Keys.H)
{
isAltPressedInThisApp = true;
// Is ALT pressed down
if ((GetKeyState(VK_MENU) & 0x8000) != 0)
{
Console.WriteLine("ALT + H");
return (IntPtr)1; // do not allow others to hook this key combo
}
}
}
// React on KEYUP
if (nCode >= 0 && wParam == (IntPtr)WM_KEYUP)
{
int vkCode = Marshal.ReadInt32(lParam);
// Is ALT not pressed down
if ((Keys)vkCode == Keys.LMenu)
{
Console.WriteLine("ALT UP");
}
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
// --------------------------------------------------------------------------------------
// Hook the keyboard
private static IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
}
}
}
}
I am not a solid C# developer as I am taking my first few babysteps here - so why would I throw myself in the deep waters of global hotkeys as one of the first things ;-) Is there anyone that can give some hints here as I probably have a silly mistake somewhere?
The idea behind, whenever the alt key pressed, it starts to swallow keys into one local list. if the pattern we need to see has been seen, it won't send, but for the every other pattern, it starts to send the keys again in the order it receives.
the code for examination phase:
enum WM
{
WM_KEYDOWN = 0x0100,
WM_KEYUP = 0x0101,
WM_SYSKEYUP = 0x0105,
WM_SYSKEYDOWN = 0x0104,
}
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode < 0)
return CallNextHookEx(_hookID, nCode, wParam, lParam);
KBDLLHOOKSTRUCT kbd = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
Enum.TryParse<Keys>($"{kbd.vkCode}", out Keys key);
Enum.TryParse<WM>($"{wParam}", out WM message);
Console.WriteLine($"{message}:{key}");
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
when pressing alt+h and releasing, output:
WM_SYSKEYDOWN:LMenu
WM_SYSKEYDOWN:H
WM_SYSKEYUP:H
WM_KEYUP:LMenu
as you can see, windows sent both alt and h key. the code you provided only catch H key, so the window which receives keyboard messages think that alt pressed.
no one can fetch and filter out previously pressed key, so we need to catch alt key whenever we see it is pressed. if the next key not H, we should send the keys we have fetched in the order we received.
i have written the below code to handle the situation, but i can not be pretty sure how it works on the real windows machine cause i have an osx operating system just because of that i am running windows in the virtual machine which is also change key strokes when i'm pressing.
if it's still not enough, you can wait and i may be able to try and figure it out at my office in the real windows machine. but i think you get the idea and work it out on your own.
[StructLayout(LayoutKind.Sequential)]
public class KBDLLHOOKSTRUCT
{
public uint vkCode;
public uint scanCode;
public KBDLLHOOKSTRUCTFlags flags;
public uint time;
public UIntPtr dwExtraInfo;
}
[Flags]
public enum KBDLLHOOKSTRUCTFlags : uint
{
LLKHF_EXTENDED = 0x01,
LLKHF_INJECTED = 0x10,
LLKHF_ALTDOWN = 0x20,
LLKHF_UP = 0x80,
}
[DllImport("user32.dll")]
static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);
enum WM
{
WM_KEYDOWN = 0x0100,
WM_KEYUP = 0x0101,
WM_SYSKEYUP = 0x0105,
WM_SYSKEYDOWN = 0x0104,
}
private static void ReSendKeys(int index = -1)
{
_index = -1;
var copiedKeys = _swallowedKeys.ToArray();
for (int i = 0; i < copiedKeys.Length; ++i)
{
bool up = copiedKeys[i].Item1 == (IntPtr)WM.WM_SYSKEYUP || copiedKeys[i].Item1 == (IntPtr)WM.WM_KEYUP;
keybd_event((byte)copiedKeys[i].Item2.vkCode, (byte)copiedKeys[i].Item2.scanCode, up ? 2u : 0u, UIntPtr.Zero);
}
_index = index;
_swallowedKeys.Clear();
}
private static List<Tuple<IntPtr, KBDLLHOOKSTRUCT>> _swallowedKeys = new List<Tuple<IntPtr, KBDLLHOOKSTRUCT>>();
private static int _index = 0;
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode < 0)
return CallNextHookEx(_hookID, nCode, wParam, lParam);
KBDLLHOOKSTRUCT kbd = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
Enum.TryParse<Keys>($"{kbd.vkCode}", out Keys key);
Enum.TryParse<WM>($"{wParam}", out WM message);
Console.Write($"{message}:{key}");
// we know that when _index is -1, ReSendKeys function has been called
// so do not filter out first alt key
if (_index == -1)
{
_index++;
Console.WriteLine();
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
// we are at the beginning of the sequence we will catch
// if it's alt key filter it out, and increment the variable
if (_index == 0)
{
_swallowedKeys.Add(new Tuple<IntPtr, KBDLLHOOKSTRUCT>(wParam, kbd));
if (message == WM.WM_SYSKEYDOWN && key == Keys.LMenu)
{
_index++;
Console.WriteLine(" filtered out");
return (IntPtr)1;
}
else
{
_swallowedKeys.Clear();
// do nothing
}
}
if (_index == 1)
{
_swallowedKeys.Add(new Tuple<IntPtr, KBDLLHOOKSTRUCT>(wParam, kbd));
// if the next key is H, then filter it out also
if (message == WM.WM_SYSKEYDOWN && key == Keys.H)
{
_index++;
_swallowedKeys.RemoveAt(_swallowedKeys.Count - 1);
Console.WriteLine(" filtered out");
return (IntPtr)1;
}
// if not, we filtered out wrong sequence, we need to resend them
else
{
Console.WriteLine();
ReSendKeys();
return (IntPtr)1;
}
}
if (_index == 2)
{
_swallowedKeys.Add(new Tuple<IntPtr, KBDLLHOOKSTRUCT>(wParam, kbd));
if (message == WM.WM_SYSKEYUP && key == Keys.H)
{
_index++;
_swallowedKeys.RemoveAt(_swallowedKeys.Count - 1);
Console.WriteLine(" filtered out");
return (IntPtr)1;
}
else
{
// if user pressed H but not released and pressed another key at the same time
// i will pass that situation, if u need to handle something like that, you got the idea, please fill that block of code
}
}
if (_index == 3)
{
_swallowedKeys.Add(new Tuple<IntPtr, KBDLLHOOKSTRUCT>(wParam, kbd));
if (message == WM.WM_KEYUP && key == Keys.LMenu)
{
_index = 0;
_swallowedKeys.Clear();
Console.WriteLine(" filtered out");
Console.WriteLine("shortcut disabled");
return (IntPtr)1;
}
else
{
Console.WriteLine();
// user has been pressed Alt + H, H released but not alt, so we can expect one H again, but we need to send this pressed key with Alt prefixed
ReSendKeys(1);
return (IntPtr)1;
}
}
Console.WriteLine();
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
I have a WPF window with disabled resize capability (WindowStyle=None, ResizeMode = CanMinimize).
It works fine, except one situation. If some application (i.e AutoHotKey) calls a WIN API function ShowWindow on my window with SW_MAXIMIZE option, then my window repositions to (0, 0) coordinate on the desktop with no size change and user is not able to move it over the screen further.
How can I disable this behavior? I want the window to ignore this call on my window.
I have tried reacting to WM_WINDOWPOSCHANGING event, but this works only until user minimizes the window. After it is minimized and restored it becomes repositioned to (0, 0) coordinate again.
private IntPtr ProcessMessage(IntPtr windowHandle, int msg, IntPtr wideParam, IntPtr leftParam, ref bool handled)
{
Msg windowsMessage = (Msg)msg;
switch (windowsMessage)
{
case Msg.WM_WINDOWPOSCHANGING:
{
WindowPos windowPos = (WindowPos)Marshal.PtrToStructure(leftParam, typeof(WindowPos));
if (IsNoClientAction(windowPos) && !IsMinimizing(windowPos) && window.WindowState == WindowState.Normal)
{
windowPos.Flags = SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOMOVE;
handled = true;
Marshal.StructureToPtr(windowPos, leftParam, true);
}
break;
}
}
return IntPtr.Zero;
}
private static bool IsNoClientAction(WindowPos windowPos)
{
return (windowPos.Flags & SetWindowPosFlags.SWP_NOCLIENTSIZE) != 0 || (windowPos.Flags & SetWindowPosFlags.SWP_NOCLIENTMOVE) != 0;
}
private static bool IsMinimizing(WindowPos windowPos)
{
return windowPos.Left == -32000 && windowPos.Top == -32000;
}
I have analyzed what user32.dll does to the window and figured out solution based on that. Before sending any messages to Wndproc it updates GWL_STYLE flags of the window enabling WS_MAXIMIZE for it. For this reason window state becomes corrupted and further behavior can hardly be handled by processing window messages only.
To disable window reaction on ShowWindow with SW_MAXIMIZE option I am setting this flag back when WM_WINDOWPOSCHANGING is processed:
private IntPtr ProcessMessage(IntPtr windowHandle, int msg, IntPtr wideParam, IntPtr leftParam, ref bool handled)
{
Msg windowsMessage = (Msg)msg;
switch (windowsMessage)
{
case Msg.WM_WINDOWPOSCHANGING:
{
WindowPos windowPos = (WindowPos)Marshal.PtrToStructure(leftParam, typeof(WindowPos));
if (IsNoClientAction(windowPos))
{
WindowStyles styles = (WindowStyles)WindowsAPI.GetWindowLongPtr(windowHandle, GWL.GWL_STYLE);
if ((styles & WindowStyles.WS_MAXIMIZE) != 0)
{
windowPos.Flags |= SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOMOVE;
WindowsAPI.SetWindowLongPtr(new HandleRef(this, windowHandle), GWL.GWL_STYLE, (IntPtr)(long)(styles ^ WindowStyles.WS_MAXIMIZE));
handled = true;
Marshal.StructureToPtr(windowPos, leftParam, true);
}
}
break;
}
}
return IntPtr.Zero;
}
private static bool IsNoClientAction(WindowPos windowPos)
{
return (windowPos.Flags & SetWindowPosFlags.SWP_NOCLIENTSIZE) != 0 || (windowPos.Flags & SetWindowPosFlags.SWP_NOCLIENTMOVE) != 0;
}
I'm creating an application that places an icon on the system tray that changes its appearance based on the state of the Caps Lock key. The issue I'm facing is that the hook only works correctly after a key other than Caps Lock is pressed, since that key flips the check after the hook is passed through, making the icon display the wrong state incorrectly.
I would need a way to detect when the Caps Lock key is pressed inside the hook to flip the detected state.
private static NotifyIcon notifyIcon = new NotifyIcon();
private static bool CapsPressed = Control.IsKeyLocked(Keys.CapsLock);
static Icon
AppIcon = CapsIndicator.Properties.Resources.AppIcon,
OnIcon = CapsIndicator.Properties.Resources.OnIcon,
OffIcon = CapsIndicator.Properties.Resources.OffIcon;
static void UpdateIcon() {
notifyIcon.Icon = CapsPressed ? OnIcon : OffIcon;
}
// Hook initializing & other stuff here
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
if (nCode >= 0 && wParam == (IntPtr) WM_KEYDOWN) {
CapsPressed = Control.IsKeyLocked(Keys.CapsLock);
UpdateIcon();
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
The solution isn't very straight-forward, but the key code can be revealed using the lParam pointer argument. You can do this by accessing the 32-bit integer it's pointing to with Marshal.ReadInt32, then, you need to cast it to the Keys type, and finally compare this value with Keys.CapsLock (or any other key you like):
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
if (nCode >= 0 && wParam == (IntPtr) WM_KEYDOWN) {
int vkCode = Marshal.ReadInt32(lParam);
CapsPressed = Control.IsKeyLocked(Keys.CapsLock);
// Flip the detected value if CapsLock is pressed
if ((Keys) vkCode == Keys.CapsLock) CapsPressed = !CapsPressed;
UpdateIcon();
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
some days ago, I got my Microsoft Arc Touch Mouse Surface Edition. Unfortunately, it doesn't have Forward/Back-Buttons (it performs only Page-Up/Page-Down).
So, I've written a little program, that catches the press of the Page_Up/Page_Down Keys and sends instead the Browser-Back/-Forward Button.
On my PC (Win 8.1 Pro) this works quite well. However, on my Surface Pro 2 (for which the program primarly was written) it won't send the Browser_Back-/Forward-Buttons.
For every other key the program seems to work (like Space, letters, numbers).
Here the code for sending Keycodes:
public static uint send(short keyCode) {
INPUT structure = new INPUT();
structure.type = (int)InputType.INPUT_KEYBOARD;
structure.ki.wVk = keyCode;
structure.ki.dwFlags = (int)KEYEVENTF.KEYDOWN;
structure.ki.dwExtraInfo = GetMessageExtraInfo();
INPUT input2 = new INPUT();
structure.type = (int)InputType.INPUT_KEYBOARD;
structure.ki.wVk = keyCode;
input2.mi.dwFlags = (int)KEYEVENTF.KEYUP;
input2.ki.dwExtraInfo = GetMessageExtraInfo();
INPUT[] pInputs = new INPUT[] { structure, input2 };
return SendInput(2, pInputs, Marshal.SizeOf(structure));
}
And here the KeyboardHook:
public static int KeyHookProc(int nCode, IntPtr wParam, IntPtr lParam) {
KeyHookStruct myKeyHookStruct =
(KeyHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyHookStruct));
if (nCode < 0) {
return CallNextHookEx(hHook, nCode, wParam, lParam);
} else {
if (wParam == (IntPtr)0x0100) {
int keyCode = myKeyHookStruct.vkCode;
if (keyCode == 33) {
Input.send(0xA7);
} else if (keyCode == 34) {
Input.send(0xA6);
} else {
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
}
return 1;
}
}
So, if the Page-Down/page-Up button on my keyboard is pressed, the send()-Method is called.
0xA6 and 0xA7 are the Keycodes for Browser_Back and Browser_Forward.
On my Surface, the Page-Up/Page-Down buttons are recognized correctly. The send()-Method is also called, but it won'T send the correct Keycodes. According to Spy++, send() isn't sending anything if the Keycode is 0xA6 or 0xA7 (on my PC it works).
Where is the problem with the Surface Pro 2? What could I do, that it works, or how could I determine why it isn't working?
The code you posted is wrong, as you never set type for input2 (the code use structure.type)
EDIT:
From MSDN docs for SendInput:
This function fails when it is blocked by UIPI. Note that neither
GetLastError nor the return value will indicate the failure was caused
by UIPI blocking.
This function is subject to UIPI. Applications are permitted to inject
input only into applications that are at an equal or lesser integrity
level.
You also should remove the call to GetMessageExtraInfo
New Edit:
I don't like the logic of your KeyHookProc function: if the message is NOT WM_KEYDOWN (0x0100), then you return 1...
I would go for:
if (wParam != (IntPtr)0x0100) {
return CallNextHookEx(hHook, nCode, wParam, lParam);
} else {
int keyCode = myKeyHookStruct.vkCode;
if (keyCode == 33) {
Input.send(0xA7);
} else if (keyCode == 34) {
Input.send(0xA6);
} else {
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
return 1;
}
I wish to write a small tool that will capture a global event when the user presses the Windows button and scrolls the mousewheel up or down. When such an event is captured, I wish to redirect said output to a virtual keystroke combination of Win-+ or Win-- (plus/minus). Can this be done?
If the windows key is reserved, ctrl-alt or such would do.
Since it uses the windows key, the key can be captured globally using a hotkey binding. RegisterHotKey at msdn.
Edit: It seems the mousewheel events are not treated as keys as I assumed and there is no way to make a global hotkey for them.
You will have to make a global window message hook and trap the WM_MOUSEWHEEL message. But you may have to do that in C/C++. A C dll to accomplish this is below, you can call Hook and Unhook from C# to enable and disable the function.
WARNING: I have not tested this code and is provided as a demonstration only.
#include <windows.h>
HINSTANCE myInstance;
HHOOK thehook = 0;
BOOL isWinKeyDown = FALSE;
extern "C" LRESULT __declspec(dllexport)__stdcall CALLBACK HookHandler(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode == WM_KEYDOWN && (wParam == VK_LWIN || wParam == VK_RWIN))
isWinKeyDown = TRUE;
else if (nCode == WM_KEYUP && (wParam == VK_LWIN || wParam == VK_RWIN))
isWinKeyDown = FALSE;
else if (nCode == WM_MOUSEHWHEEL && isWinKeyDown) {
if (HIWORD(wParam) > 0) { //mousewheel up
CallNextHookEx(thehook, WM_KEYDOWN, VK_ADD, 0);
CallNextHookEx(thehook, WM_KEYUP, VK_ADD, 0);
} else { //mousewheel down
CallNextHookEx(thehook, WM_KEYDOWN, VK_SUBTRACT, 0);
CallNextHookEx(thehook, WM_KEYUP, VK_SUBTRACT, 0);
}
return 0;
}
return CallNextHookEx(thehook, nCode, wParam, lParam);
}
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD fwdReason, LPVOID lpvReserved) {
switch(fwdReason)
{
case DLL_PROCESS_ATTACH: {
DisableThreadLibraryCalls(hInstance);
myInstance = hInstance;
} break;
case DLL_THREAD_ATTACH:
break;
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_DETACH:
break;
}
return(TRUE); // The initialization was successful, a FALSE will abort
// the DLL attach
}
extern "C" void __declspec(dllexport) Hook() {
if (!thehook)
thehook = SetWindowsHookEx(WH_CALLWNDPROC, &HookHandler, myInstance, 0);
}
extern "C" void __declspec(dllexport) UnHook() {
if (thehook)
UnhookWindowsHookEx(thehook);
thehook = 0;
}
It can definitely be done via global hooks, here is a great CodeProject example on how to do so.