Thread: Can't get WH_CBT to work right

  1. #1
    Registered User
    Join Date
    Jun 2008
    Posts
    7

    Can't get WH_CBT to work right

    So I've a simple window program that starts up and hooks to a dll, (I'll try only post relevant parts)

    Window and calling the DLL:
    Code:
    typedef void (*registerCall)(void*);
    registerCall registerHooks;
    
    static HINSTANCE hinstLib=NULL;
    set_dll_stuff() {
    	std::string dll_attrib = "theDLL.dll";
    	hinstLib = LoadLibrary(dll_attrib.c_str());
    	error_window(hinstLib == NULL,  "ERROR: unable to load DLL, ", dll_attrib);
    
    	dll_attrib = "registerHooks";
    	registerHooks = (registerCall)GetProcAddress(hinstLib, dll_attrib.c_str());
    	error_window(registerHooks == NULL, "ERROR: unable to find DLL function", dll_attrib);
    
    	registerHooks(NULL);
    }
    int WINAPI WinMain (HINSTANCE hThisInstance,
                        HINSTANCE hPrevInstance,
                        LPSTR lpszArgument,
                        int nFunsterStil)
    {...}
    
    LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {...}
    DLL side:
    Code:
    HINSTANCE _hInst= NULL;
    DWORD hookThreadId = 0;
    HWND display= NULL;
    
    HHOOK llMouseHookHandle;
    HHOOK llCBTHookHandle;
    
    size_t bufSize = 32;
    char* buf = new char[bufSize]
    
    extern "C"BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason, LPVOID reserved) {
    	switch (reason) {
    	case DLL_PROCESS_ATTACH:
    		_hInst = hInst;
    		break;
    	default:
    		break;
    	}
    }
    
    LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
    	if (nCode < 0)
    		return CallNextHookEx(NULL, nCode, wParam, lParam);
    	
    	MOUSEMOVEPOINT * mmp = (MOUSEMOVEPOINT *)*_lParam;
    
    	POINT pt;
    	pt.x = mmp->x;
    	pt.y = mmp->y;
    
    	if((*_wParam) == WM_LBUTTONUP)
    	{
    		if (WindowFromPoint(pt) != NULL && (display = WindowFromPoint(pt))) {
    			strcpy(buf, "");
    			GetClassNameA(display, buf, bufSize);
    			cout << buf << endl;
    			SendMessage(display, WM_NULL, 0, 0);
    		}
    	}
    }
    
    
    LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam) {
    	cout << "anything!!!" << endl;
    	return CallNextHookEx(NULL, nCode, wParam, lParam);
    }
    
    
    __declspec(dllexport) void __stdcall
    registerHooks(void* _extra_info) {
    	llMouseHookHandle = SetWindowsHookEx(WH_MOUSE_LL, MouseProc, _hInst, 0);
    	error_window(llMouseHookHandle == NULL, "llMouseHookHandle");
    	llCBTHookHandle = SetWindowsHookEx(WH_CBT, CBTProc, _hInst, 0);
    	error_window(llCBTHookHandle == NULL, "llCBTHookHandle");
    
    	hookThreadId = GetCurrentThreadId();
    }
    
    __declspec(dllexport) void __stdcall
    unregisterHooks() {
    	int ret_val = UnhookWindowsHookEx(llMouseHookHandle);
    	error_window(ret_val == 0, "Unhook llMouseHookHandle");
    	ret_val = UnhookWindowsHookEx(llCBTHookHandle);
    	error_window(ret_val == 0, "Unhook llCBTHookHandle");
    }
    All of this compiles and runs as expected, barring CBT, obviously. I know this because I can have my app running as a taskbar tray icon only and get a cout of window names to my console.

    From the CBTProc, I get nothing, no matter what I've tried to do, I've not been able to get it to print out unless the mouse is within it's own process, then if I resize, or move, or whatever, it works like I want it to.

    I've read through quite a few forum posts, from here and other places and there are some who think
    Code:
    	llCBTHookHandle = SetWindowsHookEx(WH_CBT, CBTProc, NULL, GetCurrentThreadId());
    or variations of that. From my efforts of trying to get it to work, though, none of them have.

    Am I doing something elementary that is preventing it to work, or is my whole approach wrong? Would I need to go down the route of injecting my DLL into processes (I've not succeeded in this yet, either, but if I need to I'll incorporate that)?

    All I want is to listen for window opening and closing, to get a handle of a particular window and only run when that's open, then if / when that closes, I want my app to close too.

    Thank you all for any help that you could give me.

  2. #2
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    Give this a shot to see if it resolves your problem...

    Code:
    LRESULT CALLBACK MouseProc (int nCode, WPARAM wParam, LPARAM lParam)
    {
        MOUSEHOOKSTRUCT * pMouseStruct = (MOUSEHOOKSTRUCT *)lParam;
        if (pMouseStruct != NULL)
        {
            POINT pt;
            pt.x = pMouseStruct->pt.x;
            pt.y = pMouseStruct->pt.y;
    
            if (wParam == WM_LBUTTONUP)
            {
                if (WindowFromPoint(pt) != NULL && (display = WindowFromPoint(pt))) {
                    strcpy(buf, "");
                    GetClassNameA(display, buf, bufSize);
                    cout << buf << endl;
                    SendMessage(display, WM_NULL, 0, 0);
                }
            }
        }   
        return CallNextHookEx(NULL,
            nCode,wParam,lParam);
    }
    DLL injection is not required to just get a handle to a window.
    Last edited by BobS0327; 12-13-2008 at 06:45 PM.

  3. #3
    Registered User
    Join Date
    Jun 2008
    Posts
    7
    thanks for the reply Bob, but just so I understand, the only thing you've changed is getting rid of the nCode < 1 part and you're ensuring that pMouseStruct isn't NULL. I just tried that and nothing changed, all you've added was getting rid of nCode < 0 and ensuring that pMouseStruct wasn't NULL, but I already knew that because it is printing out the names of the windows to the console, no problem.

    any more ideas?

  4. #4
    'Allo, 'Allo, Allo
    Join Date
    Apr 2008
    Posts
    639
    any more ideas?
    Assuming that's the entirety of the DLL minus includes then posting your real code would be a good start In it's current state MouseProc uses the undefined _lParam and _wParam variables, that's not a cause for the CBT failure but who knows what else is in your real code that's not here and vice versa. If they've just been left out of the listing, you might want to update them somewhere or the mousehook will always have the same behaviour, renaming them would also be a good idea.

  5. #5
    Registered User
    Join Date
    Jun 2008
    Posts
    7

    Can't get WH_CBT to work right, globally

    I only left in the relevant parts because I thought that would be easier for others to see if / where I'm going wrong, here's the whole lot (the _lParam and_wParam thing was just a typo, based on how I'm working it).

    theDLL.h:
    Code:
    #ifndef THEDLL_H_
    #define THEDLL_H_
    
    #define WINVER 0x0500
    #define _WIN32_WINNT 0x0500
    #include <windows.h>
    #include <stdio.h>
    #include <iostream>
    using namespace std;
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    __declspec(dllexport) void __stdcall
    registerHooks();
    __declspec(dllexport) void __stdcall
    unregisterHooks();
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif /* THEDLL_H_ */
    theDLL.cpp:
    Code:
    #include "theDLL.h"
    
    void ErrorExit(const char*);
    HINSTANCE _hInst = NULL;
    DWORD hookThreadId = 0;
    
    HHOOK llMouseHookHandle;
    HHOOK llCBTHookHandle;
    
    HWND window;
    size_t buf_size = 32;
    char* buf = new char[buf_size];
    
    extern "C"BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason, LPVOID reserved) {
    	cout << "asdf" << endl;
    	switch (reason) {
    	case DLL_PROCESS_ATTACH:
    		_hInst = hInst;
    		break;
    	default:
    		break;
    	}
    	return TRUE;
    }
    
    LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
    	if (nCode < 0)
    		return CallNextHookEx(NULL, nCode, wParam, lParam);
    
    	MOUSEMOVEPOINT * mmp = (MOUSEMOVEPOINT *)lParam;
    
    	POINT pt;
    	pt.x = mmp->x;
    	pt.y = mmp->y;
    
    	switch (wParam) {
    	case WM_LBUTTONDOWN:
    		break;
    	case WM_LBUTTONUP:
    	{
    		window = WindowFromPoint(pt);
    		if (window != NULL) {
    			strcpy(buf, "");
    			GetClassNameA(window, buf, buf_size);
    			cout << buf << endl;
    		}
    	}
    		break;
    	default:
    		break;
    	}
    
    	return CallNextHookEx(NULL, nCode, wParam, lParam);
    }
    
    LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam) {
    	cout << "anything at all" << endl;
    	return CallNextHookEx(NULL, nCode, wParam, lParam);
    }
    
    __declspec(dllexport) void __stdcall
    registerHooks() {
    	if (_hInst == NULL)
    		ErrorExit("register hooks");
    	llMouseHookHandle = SetWindowsHookEx(WH_MOUSE_LL, MouseProc, _hInst, 0);
    	if (llMouseHookHandle == NULL)
    		ErrorExit("llMouseHookHandle");
    	llCBTHookHandle = SetWindowsHookEx(WH_CBT, CBTProc, _hInst, 0);
    	if (llCBTHookHandle == NULL)
    		ErrorExit("llCBTHookHandle");
    
    	hookThreadId = GetCurrentThreadId();
    }
    
    __declspec(dllexport) void __stdcall
    unregisterHooks() {
    	int ret_val = UnhookWindowsHookEx(llMouseHookHandle);
    	if (ret_val == 0)
    		ErrorExit("Unhook llMouseHookHandle");
    	ret_val = UnhookWindowsHookEx(llCBTHookHandle);
    	if (ret_val == 0)
    		ErrorExit("Unhook llCBTHookHandle");
    }
    
    void ErrorExit(const char* pszFunction)
    {
        LPTSTR pszMessage;
        DWORD dwLastError = GetLastError();
    
        FormatMessage(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM |
            FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL,
            dwLastError,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            (LPTSTR)&pszMessage,
            0, NULL );
    
        printf("%s failed with error %d: %s\n", pszFunction, (int)dwLastError, pszMessage);
    
        LocalFree(pszMessage);
        ExitProcess(dwLastError);
    }
    theDLL.def:
    Code:
    LIBRARY "ZZZDll.dll"
    EXPORTS
    registerHooks
    unregisterHooks
    theDLLtester.cpp
    Code:
    #include <windows.h>
    #include <string>
    
    typedef void (*dllCall)();
    dllCall registerHooks, unregisterHooks;
    
    HINSTANCE hinstLib = NULL;
    HWND hwnd = NULL;
    
    void error_window(bool error_state, const std::string win_title, const std::string message) {
    	if (error_state) {
    		MessageBox(GetDesktopWindow(), message.c_str(), win_title.c_str(), MB_OK | MB_ICONERROR);
    		if (hinstLib != NULL)
    			FreeLibrary(hinstLib);
    		if (hwnd != NULL)
    			DestroyWindow(hwnd);
    		exit(0);
    	}
    }
    
    void set_dll_stuff() {
    	std::string dll_attrib = "theDLL.dll";
    	hinstLib = LoadLibrary(dll_attrib.c_str());
    	error_window(hinstLib == NULL,  "ERROR: unable to load DLL, ", dll_attrib);
    
    	dll_attrib = "registerHooks";
    	registerHooks = (dllCall)GetProcAddress(hinstLib, dll_attrib.c_str());
    	error_window(registerHooks == NULL, "ERROR: unable to find DLL function", dll_attrib);
    
    	registerHooks();
    
    	dll_attrib = "unregisterHooks";
    	unregisterHooks = (dllCall)GetProcAddress(hinstLib, dll_attrib.c_str());
    	error_window(unregisterHooks == NULL, "ERROR: unable to find DLL function", dll_attrib);
    }
    
    LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    	switch (message) {
    	    case WM_CREATE:
    	    	set_dll_stuff();
    	    break;
    		case WM_LBUTTONDOWN:
    			break;
    	    case WM_COMMAND:
    	    break;
    		case WM_CLOSE:
    			DestroyWindow(hwnd);
    			break;
    		case WM_DESTROY:
    			unregisterHooks();
    			FreeLibrary(hinstLib);
    			PostQuitMessage(0);
    			break;
    		default:
    			return DefWindowProc(hwnd, message, wParam, lParam);
    		}
    		return 0;
    }
    
    int WINAPI WinMain (HINSTANCE hThisInstance,
                        HINSTANCE hPrevInstance,
                        LPSTR lpszArgument,
                        int nFunsterStil)
    {
    	WNDCLASSEX wc;
    	MSG Msg;
    
    	wc.cbSize = sizeof(WNDCLASSEX);
    	wc.style = 0;
    	wc.lpfnWndProc = WindowProcedure;
    	wc.cbClsExtra = 0;
    	wc.cbWndExtra = 0;
    	wc.hInstance = hThisInstance;
    	wc.hIcon = NULL;
    	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    	wc.lpszMenuName = NULL;
    	wc.lpszClassName = "theDLL window";
    	wc.hIconSm = NULL;
    
    	RegisterClassEx(&wc);
    
    	hwnd = CreateWindowEx(
    			WS_EX_CLIENTEDGE,
    			"theDLL window",
    			"The title of my window",
    			WS_OVERLAPPEDWINDOW,
    			CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
    			NULL, NULL, hThisInstance, NULL);
    
    	ShowWindow(hwnd, nFunsterStil);
    	UpdateWindow(hwnd);
    
    	while(GetMessage(&Msg, NULL, 0, 0) > 0)
    	{
    		TranslateMessage(&Msg);
    		DispatchMessage(&Msg);
    	}
    
    	return Msg.wParam;
    }
    this has just compiled and the global low-level mousehook works as expected, I click on firefox and it prints "MozillaUIWindowClass", I click on the taskbar and it prints "ToolbarWindow32". "anything at all", however only gets printed out when I mess with the tester window. this is clearly only local behaviour and not global, can you see any reason why this would be?
    Last edited by seaders; 12-14-2008 at 06:56 PM.

  6. #6
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    this has just compiled and the global low-level mousehook works as expected, I click on firefox and it prints "MozillaUIWindowClass", I click on the taskbar and it prints "ToolbarWindow32". "anything at all", however only gets printed out when I mess with the tester window. this is clearly only local behaviour and not global, can you see any reason why this would be?
    I'm not sure what you mean by the class name only printing out when you mess with the tester window. I compiled and tried your app and I didn't have to "mess" with the tester window. I think you have to clearly state what exactly you mean by "messing" with the tester window and how it relates to "local behaviour".

  7. #7
    Registered User
    Join Date
    Jun 2008
    Posts
    7
    a global WH_CBT hook should, from MSDN fire every time any window is activated, de-activated, sized, minimized, maximized, everything. however, from what I've written, it's only working locally, ie, only firing when any of those occurrences happen to the window that I've created, not to any other windows.

    there's no difference from the way that it's currently working to the way it would work if I set the hook within the window code and didn't involve the dll at all.

    what I'm trying to create is something that constantly listens for a close window event and when one particular window closes, it should too.

  8. #8
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    Have you tried something like this?

    Code:
    LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam) {
        if (nCode < 0)
            return CallNextHookEx(NULL, nCode, wParam, lParam);
        switch(nCode)
        {
            case  HCBT_ACTIVATE:
                break;
            case HCBT_CREATEWND: // Window is being created
                break;
            case HCBT_DESTROYWND:
                break;
            case HCBT_MINMAX:
                break;
            case HCBT_MOVESIZE:
                break;
            case HCBT_SETFOCUS:
                break;
            case HCBT_SYSCOMMAND:
                break;
            default:
                break;
        }
        // cout << "anything at all" << endl;
        return CallNextHookEx(NULL, nCode, wParam, lParam);
    }
    To test the above code, I set up a few scheduled tasks to create GUI windows and the above code identifies when they are created.

  9. #9
    Registered User
    Join Date
    Jun 2008
    Posts
    7
    no, it wasn't it was waaaay more ridiculous than that! cout couldn't possibly work, because the event was firing under the other processes, so it was trying to print to their console, not my one! as soon as I changed it to log to a file, it worked 100&#37;, goddamn, I can't believe I didn't cop this sooner, it's been absolutely wrecking my head.

    thanks for your patience, Bob

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. strcmp returning 1...
    By Axel in forum C Programming
    Replies: 12
    Last Post: 09-08-2006, 07:48 PM
  2. getline() don't want to work anymore...
    By mikahell in forum C++ Programming
    Replies: 7
    Last Post: 07-31-2006, 10:50 AM
  3. Why don't the tutorials on this site work on my computer?
    By jsrig88 in forum C++ Programming
    Replies: 3
    Last Post: 05-15-2006, 10:39 PM
  4. fopen();
    By GanglyLamb in forum C Programming
    Replies: 8
    Last Post: 11-03-2002, 12:39 PM
  5. DLL __cdecl doesnt seem to work?
    By Xei in forum C++ Programming
    Replies: 6
    Last Post: 08-21-2002, 04:36 PM