2

I created a DLL to hook some events using a CBT hook. It seems to work only for the windows created by the process launching the hook...

My system is Windows 7 x64, but the behaviour is the same also on the x32.

This is the code (sorry but I'm no C++ expert):

#include "windows.h"

extern "C"
{
    static LRESULT CALLBACK CbtProcCb(int nCode, WPARAM wParam, LPARAM lParam);
    HINSTANCE g_hDll       = NULL;
    HWND      g_hNotifyWin = NULL;
    DWORD     g_uNotifyMsg = NULL;
    HHOOK     g_hHook      = NULL;

    __declspec(dllexport) HHOOK SetCbtHook(HWND hWnd, LPCWSTR lStrMsg, DWORD threadId)
    {
        g_hNotifyWin = hWnd;
        g_uNotifyMsg = RegisterWindowMessage(lStrMsg);
        g_hHook      = SetWindowsHookEx(WH_CBT, (HOOKPROC)CbtProcCb, g_hDll, threadId);
        return g_hHook;
    }

    __declspec(dllexport) int UnsetCbtHook()
    {
        if ( !g_hHook )
            return 0;
        return UnhookWindowsHookEx(g_hHook);
    }
}

static LRESULT CALLBACK CbtProcCb(int nCode, WPARAM wParam, LPARAM lParam)
{
    SendNotifyMessage(g_hNotifyWin, g_uNotifyMsg, nCode, wParam); // Send nCode to check the received event
    return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    if ( fdwReason == DLL_PROCESS_ATTACH )
        g_hDll = hinstDLL;
    return true;
}

Any hints?

cyrusza
  • 113
  • 11

2 Answers2

4

When Windows installs globals hook, the DLL implementing the hook function is often loaded in other processes. In those processes, the g_hNotifyWin and g_uNotifyMsg global variables are NULL. That global variables are only not NULL in the context of the process in which the SetCbtHook call took place.

You must have a way to retrieve the correct value for g_hNotifyWin and 'g_uNotifyMsg', in an arbitrary process.

Add:

const char * g_pszClassName = "MyClassName";
const char * g_pszRegisteredMsg = "MyMessage";

to your DLL, and replace "MyClassName" by the class name of the g_hNotifyWin window. See the RegisterClass call in your EXE. Update also "MyMessage".

Then, use the following CbtProcCb function:

static LRESULT CALLBACK CbtProcCb(int nCode, WPARAM wParam, LPARAM lParam)
{
    if ( nCode >= 0 ) {
        if ( g_hNotifyWin == NULL ) g_hNotifyWin = FindWindow( g_pszClassName, NULL );
        if ( g_uNotifyMsg == 0) g_uNotifyMsg = RegisterWindowMessage(g_pszRegisteredMsg);
        if ( g_hNotifyWin && g_uNotifyMsg )
            SendNotifyMessage(g_hNotifyWin, g_uNotifyMsg, nCode, wParam); // Send nCode to check the received event
    }
    return CallNextHookEx(NULL, nCode, wParam, lParam); // first arg useless see MSDN
}

EDIT: As you noted in comment, same problem for g_uNotifyMsg See updated function. You could set up those variables in DllMain, indeed.

The first argument to CallNextHookEx may be NULL.

manuell
  • 7,528
  • 5
  • 31
  • 58
  • Thanks for the post. I tried with your code (slightly modified because I supposed you used g_uNotifyMsg instead of g_hNotifyWin), but the behaviour it's the same. Anyway I suppose that if your guess it's correct, it must be valid also for the other global variables, like g_uNotifyMsg. Could it be correct to initialize them in the DllMain? – cyrusza Dec 03 '13 at 21:59
  • I set all the variables in the DllMain to avoid clutter in the callback. It worked, so your guess was correct! Thank you for the help, really appreciated! Do you think there is any chance to pass these values without hardcoding them? – cyrusza Dec 03 '13 at 22:14
  • 1
    I found a way to pass parameters and keep their consistency over all hooked processes. A shared data segment is what is needed in this case: `#pragma data_seg("")`. This is the source: http://www.flounder.com/hooks.htm – cyrusza Dec 05 '13 at 15:10
  • I wrote yestersay, than your globals are not in shared memory. Read my comment. :-) But not use pragma data_seg, instead use mapping. – Xearinox Dec 05 '13 at 15:57
  • @cyrusza If you build complex systems using several global hooks, you may find shared memory useful (data_seg or Shared Memory via Mapping). For simpler case, don't bother, and learn to work around. – manuell Dec 05 '13 at 17:10
  • @Xearinox yes I noticed, I rated the post as useful. Sorry but this is my first try with this subject. – cyrusza Dec 05 '13 at 17:17
  • @manuell thank you for the hints, but I would like to create a DLL more "generic" as possible as it will be used in a scripting language. I just implemented the data_seg and it works a treat! – cyrusza Dec 05 '13 at 17:18
  • I used to rely on data_seg, an dumped them. Go figure! :-) – manuell Dec 05 '13 at 17:19
1

I guess your ThreadId is not zero, so hook is attached only to the calling process / thread respectively. Read documentation: http://msdn.microsoft.com/en-us/library/windows/desktop/ms644990(v=vs.85).aspx

Xearinox
  • 3,224
  • 2
  • 24
  • 38