Thread: Global hook only works on one window?

  1. #1
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895

    Question Global hook only works on one window?

    I installed a global keyboard hook to intercept my multimedia keyboard's special keys and delegate them to Winamp (because the drivers for this keyboard are written so poorly it makes me sick).

    When debugging, it seemed to work fine.
    In standalone run, it suddenly didn't work anymore. The hook was set, but it didn't catch anything.

    It turned out that, no matter how I run this thing, it only catches messages that are sent to Visual Studio!

    Does anyone have an explanation for this weird behavior?

    Here's the hook DLL.
    Code:
    #define WINVER 0x0500
    #define _WIN32_WINNT 0x0500
    #include <windows.h>
    
    #pragma data_seg("shared_data")
    // Superglobals
    HHOOK g_hhook = 0;
    HWND g_hwnd = 0;
    #pragma data_seg()
    #pragma comment(linker, "/SECTION:shared_data,S")
    
    HMODULE g_hmodule = 0;
    
    LRESULT CALLBACK MultimediaKeyboardProc(int code, WPARAM w, LPARAM l)
    {
    	if(code < 0 || code == HC_ACTION) {
    		return CallNextHookEx(g_hhook, code, w, l);
    	}
    
    	MessageBox(0, TEXT("Hook called."), TEXT("MultiHook"), 0);
    
    	int winampcmd = 0;
    
    	switch(w) {
    		case VK_MEDIA_NEXT_TRACK:
    			winampcmd = 40048;
    			break;
    
    		case VK_MEDIA_PREV_TRACK:
    			winampcmd = 40044;
    			break;
    
    		case VK_MEDIA_PLAY_PAUSE:
    			{
    				LRESULT playstatus = SendMessage(g_hwnd, WM_USER, 0, 104);
    				winampcmd = (playstatus == 1 || playstatus == 3) ? 40046 : 40045;
    			} break;
    
    		case VK_MEDIA_STOP:
    			winampcmd = 40047;
    			break;
    	}
    
    	if(winampcmd == 0) {
    		return CallNextHookEx(g_hhook, code, w, l);
    	}
    
    	MessageBox(0, TEXT("Hook invoked."), TEXT("MultiHook"), 0);
    	SendMessage(g_hwnd, WM_COMMAND, winampcmd, 0);
    
    	return TRUE;
    }
    
    BOOL WINAPI DllMain(HMODULE hmod, DWORD reason, LPVOID)
    {
    	if(reason == DLL_PROCESS_ATTACH) {
    		g_hmodule = hmod;
    	}
    	return TRUE;
    }
    
    bool WINAPI InstallHook()
    {
    	if(g_hhook != 0) {
    		return false;
    	}
    	g_hwnd = FindWindow(TEXT("Winamp v1.x"), 0);
    	if(!g_hwnd) {
    		MessageBox(0, TEXT("Winamp not found!"), TEXT("MultiHook"), 0);
    		return false;
    	}
    	g_hhook = SetWindowsHookEx(WH_KEYBOARD, MultimediaKeyboardProc, g_hmodule, 0);
    	if(g_hhook) {
    		MessageBox(0, TEXT("Hook set."), TEXT("MultiHook"), 0);
    	} else {
    		MessageBox(0, TEXT("Hook failed."), TEXT("MultiHook"), 0);
    	}
    	return g_hhook != 0;
    }
    
    void WINAPI ClearHook()
    {
    	if(g_hhook != 0) {
    		UnhookWindowsHookEx(g_hhook);
    		MessageBox(0, TEXT("Hook cleared."), TEXT("MultiHook"), 0);
    	}
    }
    And the program that makes use of it.
    Code:
    #include <windows.h>
    #include <shellapi.h>
    
    typedef bool (WINAPI *INST)();
    typedef void (WINAPI *CLEAR)();
    
    void WaitForQuit();
    
    int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
    {
    	HMODULE hmod = LoadLibrary(
    #if defined(_DEBUG) || defined(DEBUG)
    		TEXT("..\\MultimediaHook\\Debug\\")
    #endif
    		TEXT("MultimediaHook.dll"));
    	if(hmod == 0) {
    		return 1;
    	}
    	INST InstallHook = reinterpret_cast<INST>(GetProcAddress(hmod, "InstallHook"));
    	CLEAR ClearHook = reinterpret_cast<CLEAR>(GetProcAddress(hmod, "ClearHook"));
    
    	if(InstallHook && ClearHook) {
    		InstallHook();
    		WaitForQuit();
    		ClearHook();
    	}
    
    	FreeLibrary(hmod);
    
    	return 0;
    }
    
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    
    void WaitForQuit()
    {
    	WNDCLASS wc;
    	ZeroMemory(&wc, sizeof(WNDCLASS));
    	wc.hInstance = GetModuleHandle(0);
    	wc.lpfnWndProc = WndProc;
    	wc.lpszClassName = TEXT("MultimediaHookCommWnd");
    	RegisterClass(&wc);
    	HWND h = CreateWindow(wc.lpszClassName, TEXT("Multimedia Hook Comm Window"),
    		WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, 0, 0, wc.hInstance, 0);
    	NOTIFYICONDATA nid;
    	ZeroMemory(&nid, sizeof(nid));
    	nid.cbSize = sizeof(nid);
    	nid.hWnd = h;
    	nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
    	nid.hIcon = LoadIcon(0, IDI_WINLOGO);
    	lstrcpy(nid.szTip, TEXT("Hook Comm"));
    	nid.uCallbackMessage = WM_USER + 23;
    	Shell_NotifyIcon(NIM_ADD, &nid);
    
    	MSG msg;
    	while(GetMessage(&msg, 0, 0, 0) > 0) {
    		DispatchMessage(&msg);
    	}
    
    	Shell_NotifyIcon(NIM_DELETE, &nid);
    	UnregisterClass(wc.lpszClassName, wc.hInstance);
    }
    
    LRESULT CALLBACK WndProc(HWND h, UINT m, WPARAM w, LPARAM l)
    {
    	if(m == WM_USER+23) {
    		if(l == WM_LBUTTONDOWN) {
    			DestroyWindow(h);
    			PostQuitMessage(0);
    		}
    		return 0;
    	}
    	return DefWindowProc(h, m, w, l);
    }
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  2. #2
    &TH of undefined behavior Fordy's Avatar
    Join Date
    Aug 2001
    Posts
    5,793
    One thing that jumps out at me is that you are calling FindWindow in the process of your original installing program, then using it in a hook procedure that resides in the Winamp process (when it is injected into Winamp's address space by SetWindowsHook).

    When I wrote my first hook I did the same as you, and I found that the HWND was invalid once the module was inside another address space (even though you are using the shared section trick, it still wont work).

    My workaround was to open a MailSlot in the Installing/Monitoring process and then to have a worker thread read on the Mailslot.

    The in the Hook DLL, I would get a handle to the Mailslot and write to it from my Hook Procedure.

    You can also use pipes or memory mapped files, but if you have multiple hooks, then MailSlots work pretty well

  3. #3
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Thanks. I think I will avoid the communication and simply repeat the FindWindow in each process (in DllMain:PROCESS_ATTACH).

    So Visual Studio just happened to have a process space where the handle was valid, too?


    Edit: Actually I checked, the hook never even gets called if the keystroke is not on a VS window. This means that Winamp's HWND never can cause problems in the first place
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  4. #4
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    Unlike most handles hwnds can be used globally(at least on the current desktop).

    Can I ask why you ignore messages with HC_ACTION? I have never used a global windows hook so I am ignorant on this matter(and most others).

    HC_ACTION
    The wParam and lParam parameters contain information about a keystroke message.
    HC_NOREMOVE
    The wParam and lParam parameters contain information about a keystroke message, and the keystroke message has not been removed from the message queue. (An application called the PeekMessage function, specifying the PM_NOREMOVE flag.)
    Is it possible that VS uses a PeekMessage loop while other application do not?

  5. #5
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Ok, where's the "shoot myself" smilie?

    I meant to ignore all non-HC_ACTION messages, the == is supposed to be a !=.

    Thanks alot anonytmouse! You've brought a ray of light in my so far dark day, and it's already 7:10 PM
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  6. #6
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    It works

    Had to do a few more adjustments, but I don't think I'd've ever discovered that one...


    Now I need to reduce its memory footprint. (1.2 MB when both exe and dll are only 5k each!)
    Last edited by CornedBee; 01-19-2004 at 12:27 PM.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. C++ Global Keyboard Hook
    By darknite135 in forum C++ Programming
    Replies: 4
    Last Post: 02-01-2008, 07:36 PM
  2. blocking or passing keys with global hook
    By pmouse in forum Windows Programming
    Replies: 4
    Last Post: 08-29-2007, 02:54 PM
  3. Window Hook
    By paul_uk in forum Windows Programming
    Replies: 5
    Last Post: 11-28-2004, 10:56 AM
  4. my wndProc is out of scope
    By Raison in forum Windows Programming
    Replies: 35
    Last Post: 06-25-2004, 07:23 AM
  5. Invoking MSWord
    By Donn in forum C Programming
    Replies: 21
    Last Post: 09-08-2001, 04:08 PM