Results 1 to 12 of 12

Thread: Using SetWindowsHookEx

  1. #1
    Join Date
    Dec 2006
    Location
    Banville
    Posts
    3,914
    Mentioned
    12 Post(s)
    Quoted
    98 Post(s)

    Default Using SetWindowsHookEx

    I can't for the life of me get it to work. The hook procedure you pass needs to be located in a DLL because it is run in the address space of the hooked process (i.e. your DLL is forcefully injected to every process on the system depending on the arguments to SetWindowsHookEx). The DLL and the process which calls SetWindowsHookEx needs to match the bitness of the programs you want to receive callbacks for.

    The one I'm most interested in is CBTProc. You can find a sample project on my GitHub. The only decent example I was able to find is located here, but my code is almost a transliteration of it and it doesn't work. No callbacks are received when manipulating windows as they should per the CBTProc documentation. A reasonable test seems to be to open a File Explorer window, as File Explorer will trigger the WinEventHook callback.
    Last edited by R0b0t1; 06-26-2017 at 03:09 PM.
    The jealous temper of mankind, ever more disposed to censure than
    to praise the work of others, has constantly made the pursuit of new
    methods and systems no less perilous than the search after unknown
    lands and seas.

  2. #2
    Join Date
    Feb 2011
    Location
    The Future.
    Posts
    5,600
    Mentioned
    396 Post(s)
    Quoted
    1598 Post(s)

    Default

    Quote Originally Posted by R0b0t1 View Post
    I can't for the life of me get it to work. The hook procedure you pass needs to be located in a DLL because it is run in the address space of the hooked process (i.e. your DLL is forcefully injected to every process on the system depending on the arguments to SetWindowsHookEx). The DLL and the process which calls SetWindowsHookEx needs to match the bitness of the programs you want to receive callbacks for.

    The one I'm most interested in is CBTProc. You can find a sample project on my GitHub. The only decent example I was able to find is located here, but my code is almost a transliteration of it and it doesn't work. No callbacks are received when manipulating windows as they should per the CBTProc documentation. A reasonable test seems to be to open a File Explorer window, as File Explorer will trigger the WinEventHook callback.

    https://villavu.com/forum/showthread...27#post1387327


    Is while(GetMessage())loop needed? I mean its already hooked. What is its purpose?
    Without a System Message Queue OR a Message Only Window (`CreateWindow("SYSTEM_MESSAGE_QUEUE", 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, 0, 0)`), your hooks will NEVER execute. You'd get no messages and no callbacks. https://stackoverflow.com/questions/...-message-queue
    I am Ggzz..
    Hackintosher

  3. #3
    Join Date
    Dec 2006
    Location
    Banville
    Posts
    3,914
    Mentioned
    12 Post(s)
    Quoted
    98 Post(s)

    Default

    Quote Originally Posted by Brandon View Post
    https://villavu.com/forum/showthread...27#post1387327




    Without a System Message Queue OR a Message Only Window (`CreateWindow("SYSTEM_MESSAGE_QUEUE", 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, 0, 0)`), your hooks will NEVER execute. You'd get no messages and no callbacks. https://stackoverflow.com/questions/...-message-queue
    If you don't mind checking the C++ version of the project it works, but only receives an inconsequential callback. I'll modify the Pascal version time permitting. Any comments on the C++ version?

    Moreover do you know how I'm supposed to synchronize events between the DLL and the hooking process? The only way I know of to allow an unknown number of processes to communicate with a controlling process is local network communication. I'll need to look at what gets passed in through DllMain but I don't think there's any way to recover a handle to the controlling process.

    The documentation implies the code is copied per process so I may be receiving only the one type of event as the only DLL instance with a valid file handle is the one I load to set up the hooks. I think I need a better way to get diagnostic information out of a DLL.
    Last edited by R0b0t1; 06-27-2017 at 04:34 AM.
    The jealous temper of mankind, ever more disposed to censure than
    to praise the work of others, has constantly made the pursuit of new
    methods and systems no less perilous than the search after unknown
    lands and seas.

  4. #4
    Join Date
    Feb 2011
    Location
    The Future.
    Posts
    5,600
    Mentioned
    396 Post(s)
    Quoted
    1598 Post(s)

    Default

    Quote Originally Posted by R0b0t1 View Post
    If you don't mind checking the C++ version of the project it works, but only receives an inconsequential callback. I'll modify the Pascal version time permitting. Any comments on the C++ version?

    Moreover do you know how I'm supposed to synchronize events between the DLL and the hooking process? The only way I know of to allow an unknown number of processes to communicate with a controlling process is local network communication. I'll need to look at what gets passed in through DllMain but I don't think there's any way to recover a handle to the controlling process.

    The documentation implies the code is copied per process so I may be receiving only the one type of event as the only DLL instance with a valid file handle is the one I load to set up the hooks. I think I need a better way to get diagnostic information out of a DLL.

    You should actually have an EventLoop in the C++ version. Especially if it gets injected into console applications or applications without an event loop.

    As for how to synchronize events. Use Events and FileMaps to communicate: https://msdn.microsoft.com/en-us/lib...(v=vs.85).aspx with Global\\\MyMapping as the file name to create a global mapping. Then use CreateEvent and SetEvent to trigger a read or write from the hooking process. The same way you create events, you can also use global mutexes and semaphores for synchronization between processes as well.. Alternatively is to use Sockets like SMART.. I prefer the global mapping with events and locks.
    I am Ggzz..
    Hackintosher

  5. #5
    Join Date
    Dec 2006
    Location
    Banville
    Posts
    3,914
    Mentioned
    12 Post(s)
    Quoted
    98 Post(s)

    Default

    Quote Originally Posted by Brandon View Post
    You should actually have an EventLoop in the C++ version. Especially if it gets injected into console applications or applications without an event loop.
    I do have an event loop, unless you mean something separate from the message loop. Console applications or applications which do not have an event loop can't trigger CBT events, but were you saying I should start a thread from the injected DLL that runs an event loop?

    I looked harder at the documentation and I realized it's possible to give global names to every type of object, unlike POSIX, so thanks for the suggestions. I tried to get something set up with mailslots (just checking that things arrive, as they should be sent when the callback fires) but the DLL instances must not be sending anything.

    I'm surprised this is so hard. It seems like the most common use of SetWindowsHookEx is for hooking one's own process though, and nobody seems to use it like this.
    Last edited by R0b0t1; 06-28-2017 at 04:41 PM.
    The jealous temper of mankind, ever more disposed to censure than
    to praise the work of others, has constantly made the pursuit of new
    methods and systems no less perilous than the search after unknown
    lands and seas.

  6. #6
    Join Date
    Dec 2007
    Posts
    2,112
    Mentioned
    71 Post(s)
    Quoted
    580 Post(s)

    Default

    You can also use VirtualAllocEx / ReadProcessMemory / WriteProcessMemory to do cross-process communication.

  7. #7
    Join Date
    Dec 2006
    Location
    Banville
    Posts
    3,914
    Mentioned
    12 Post(s)
    Quoted
    98 Post(s)

    Default

    I was referred to dwm-win32 by @Wizzup? and it turns out that project uses RegisterShellHookWindow. This seems to internally use SetWindowsHookEx. There are far more results for questions involving this function so I am led to believe that the other ones are hard to use and may not work properly. I think I should stress that I never found a working example of using SetWindowsHookEx, and the one example with code that I found did not use in context hooks (i.e. DLL injection).

    I've also since learned that SetWindowsHookEx used with WH_SHELL should execute the hook in the context of the calling process (as opposed to the hooked process) however when I used it this way it didn't seem to work. I will end up experimenting with SetWindowsHookEx again and hope to ultimately get it working with injected DLLs.
    The jealous temper of mankind, ever more disposed to censure than
    to praise the work of others, has constantly made the pursuit of new
    methods and systems no less perilous than the search after unknown
    lands and seas.

  8. #8
    Join Date
    Dec 2007
    Posts
    2,112
    Mentioned
    71 Post(s)
    Quoted
    580 Post(s)

    Default

    You NEED a message pump in the same context/thread if you are using low level hooks (WH_MOUSE_LL, WH_KEYBOARD_LL). It does NOT have to be injected. Nor does it need to exist in a DLL.

    You don't need a message pump if you use SetWindowsHookEx with WH_MOUSE, WH_KEYBOARD. It needs to be in a DLL and it needs to be in the same process.

    You can maybe try posting the bare minimum code you use to install the hooks.

  9. #9
    Join Date
    Dec 2006
    Location
    Banville
    Posts
    3,914
    Mentioned
    12 Post(s)
    Quoted
    98 Post(s)

    Default

    Quote Originally Posted by Kasi View Post
    You NEED a message pump in the same context/thread if you are using low level hooks (WH_MOUSE_LL, WH_KEYBOARD_LL). It does NOT have to be injected. Nor does it need to exist in a DLL.

    You don't need a message pump if you use SetWindowsHookEx with WH_MOUSE, WH_KEYBOARD. It needs to be in a DLL and it needs to be in the same process.
    I'm not really sure that that is right. I've done what it says to do in the documentation, but my reading of it didn't take that away from it. There seems to be two types of hooks: out of context hooks and in context hooks. Out of context hooks run in the hooking process (probably using the asynchronous procedure call queue). In context hooks run in the process space of the hooked process, and this is done by having the OS forcefully load a DLL into the process you want to hook.

    In the case where you are hooking yourself, you are presumably loading a DLL, calling SetWindowsHookEx and forcing the OS to load another copy of it, and then having that code run with access to any resources you had set up for use in the hook.

    That you can use WH_MOUSE or WH_KEYBOARD in this way and receive global events is probably because the OS broadcasts them to all processes.

    Quote Originally Posted by Kasi View Post
    You can maybe try posting the bare minimum code you use to install the hooks.
    I'll reply in a bit with code as I implement SetWindowsHookEx as the last way to do what I was originally trying to do. You can look at my GitHub for a C version attempting to use WH_CBT as I've just described. A potential problem is misuse of RegisterClassEx (ANSI version) but I receive no error codes from the functions which create a dummy window. The code almost works, but I only had the callback fire for the process that set up the hooks (or that I can tell anyway - if it loads in the rest of the processes and I am using mailslots incorrectly I just never learn about it).
    The jealous temper of mankind, ever more disposed to censure than
    to praise the work of others, has constantly made the pursuit of new
    methods and systems no less perilous than the search after unknown
    lands and seas.

  10. #10
    Join Date
    Dec 2007
    Posts
    2,112
    Mentioned
    71 Post(s)
    Quoted
    580 Post(s)

    Default

    @R0b0t1;


    Out of context, System-Wide:
    CPP Code:
    #include <windows.h>
    #include <iostream>

    HHOOK MouseHook = nullptr;

    LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
        if (lParam)
        {
            MOUSEHOOKSTRUCT* MouseEvent = (MOUSEHOOKSTRUCT*)lParam;
            std::cout << MouseEvent->pt.x << ", " << MouseEvent->pt.y << std::endl;
        }
        return CallNextHookEx(MouseHook, nCode, wParam, lParam);
    }

    int main()
    {
        MouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseProc, nullptr, 0); // 0 for all, anything else filters to that specific thread.

        MSG messages;
        while (GetMessage(&messages, nullptr, 0, 0))
        {
            TranslateMessage(&messages);
            DispatchMessage(&messages);
        }

        UnhookWindowsHookEx(MouseHook);
        return 0;
    }


    In Context:
    CPP Code:
    #include <windows.h>
    #include <iostream>

    std::string DLLPath = "C:/Users/Kasi/Desktop/WH_MOUSE_TEST_DLL/bin/Debug/WH_MOUSE_TEST_DLL.dll";

    HMODULE HOOK = nullptr;

    LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        switch (message)
        {
            case WM_CREATE:
                {
                    HOOK = LoadLibrary(DLLPath.c_str());
                }
                break;

            case WM_DESTROY:
                {
                    FreeLibrary(HOOK);
                    PostQuitMessage(0);
                }
                break;
            default:
                return DefWindowProc(hwnd, message, wParam, lParam);
        }
        return 0;
    }

    int main()
    {
        WNDCLASSEX wincl;
        wincl.hInstance = nullptr;
        wincl.lpszClassName = "Example";
        wincl.lpfnWndProc = WindowProcedure;
        wincl.style = 0;
        wincl.cbSize = sizeof(WNDCLASSEX);
        wincl.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
        wincl.hIconSm = LoadIcon(nullptr, IDI_APPLICATION);
        wincl.hCursor = LoadCursor(nullptr, IDC_ARROW);
        wincl.lpszMenuName = nullptr;
        wincl.cbClsExtra = 0;
        wincl.cbWndExtra = 0;
        wincl.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
        if (!RegisterClassEx(&wincl))
            return 0;

        HWND hwnd = CreateWindowEx(0, "Example", "Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
                                   680, 480, HWND_DESKTOP, nullptr, nullptr, nullptr);

        ShowWindow(hwnd, SW_SHOW);

        MSG messages;
        while (GetMessage(&messages, nullptr, 0, 0))
        {
            TranslateMessage(&messages);
            DispatchMessage(&messages);
        }
        return 0;
    }
    CPP Code:
    #include <windows.h>
    #include <iostream>

    HHOOK MouseHook = nullptr;

    LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
        if (lParam)
        {
            MOUSEHOOKSTRUCT* MouseEvent = (MOUSEHOOKSTRUCT*)lParam;
            std::cout << MouseEvent->pt.x << ", " << MouseEvent->pt.y << std::endl;
        }
        return CallNextHookEx(MouseHook, nCode, wParam, lParam);
    }

    BOOL __stdcall DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
    {
        switch (fdwReason)
        {
            case DLL_PROCESS_ATTACH:
                {
                    MouseHook = SetWindowsHookEx(WH_MOUSE, MouseProc, GetModuleHandle(nullptr),
                                                 GetWindowThreadProcessId(FindWindow(nullptr, "Title"), nullptr));
                }
                break;
            case DLL_PROCESS_DETACH:
                {
                    UnhookWindowsHookEx(MouseHook);
                }
                break;
            case DLL_THREAD_ATTACH:
                break;
            case DLL_THREAD_DETACH:
                break;
        }
        return TRUE;
    }

    Apologies for the C style casts and the message pump.
    Last edited by Kasi; 07-18-2017 at 12:23 PM.

  11. #11
    Join Date
    Dec 2006
    Location
    Banville
    Posts
    3,914
    Mentioned
    12 Post(s)
    Quoted
    98 Post(s)

    Default

    @Kasi, my point is that that works for WH_MOUSE but it doesn't work for any other type of hook which must be in context. You need some form of inter-process communication if it is working properly, because the hook runs in another process and can't print to the hooking process' standard output.
    The jealous temper of mankind, ever more disposed to censure than
    to praise the work of others, has constantly made the pursuit of new
    methods and systems no less perilous than the search after unknown
    lands and seas.

  12. #12
    Join Date
    Dec 2007
    Posts
    2,112
    Mentioned
    71 Post(s)
    Quoted
    580 Post(s)

    Default

    Quote Originally Posted by R0b0t1 View Post
    @Kasi, my point is that that works for WH_MOUSE but it doesn't work for any other type of hook which must be in context. You need some form of inter-process communication if it is working properly, because the hook runs in another process and can't print to the hooking process' standard output.
    You can't spawn a console in that process?
    CPP Code:
    AllocConsole(); // if a console window doesn't already exist.
    ShowWindow(GetConsoleWindow(), SW_SHOW);
    freopen("CONIN$", "r", stdin);
    freopen("CONOUT$", "w", stdout);
    freopen("CONOUT$", "w", stderr);
    std::cout << "Test" << std::endl;

    You can also use several methods already stated in this thread for cross process communication.
    Last edited by Kasi; 07-17-2017 at 07:32 PM.

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •