Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add a system "windows hook" so as to be notified of windows being created/activated?

Tags:

c#

winapi

hook

Tried a bunch of things but I can't get it to work consistently amid my taskbar being nuked and other supernatural effects on my desktop UI.

Tried using a open-library http://mwinapi.sourceforge.net/ first. Although it worked nicely as an OO layer for enumerating windows and stuff. It couldn't do hooks properly

Next stop was Dino E.'s post on Windows Hooks in the .Net framework. I ended up writing my own type as I was understanding the text and trying to get this to work.

My intention is to have this app running and have it be able to log all created windows while it is running. Calling all eyeballs...

Update: Snipped since apparently you can't write global windows hooks in .Net / managed code (except some low level mouse or keyboard hooks)

So I switched to C++. Still all WinAPI calls return valid handles but I don't see my filter function being called - don't seem to be receiving any notifications. Still doesn't work... Can someone spot the mistake.

void CWinHookFacade::Hook()
{
    HMODULE hCurrentDll = LoadLibrary(_T("[Path to my hook dll]"));
    m_HookHandle = SetWindowsHookEx(WH_CBT, 
        FilterFunctionForHook, 
        hCurrentDll, 
        0);
    if (m_HookHandle == NULL)
    {
        throw new std::exception("Unable to hook");
    }

}
void CWinHookFacade::Unhook()
{
    if (!UnhookWindowsHookEx(m_HookHandle))
    {
        throw new std::exception("Unhook failed!");
    }
    m_HookHandle = NULL;
}

LRESULT CWinHookFacade::FilterFunctionForHook(int code, WPARAM wParam, LPARAM lParam)
{
    if (code >= 0)
    {
        switch(code)
        {
        case HCBT_CREATEWND:
            wprintf(_T("Created Window"));
            break;
        case HCBT_ACTIVATE:
            wprintf(_T("Activated Window"));
            break;
        case HCBT_DESTROYWND:
            wprintf(_T("Destroy Window"));
            break;
        }
    }

    return CallNextHookEx(m_HookHandle, code, wParam, lParam);
}

Client exe calls the Hook_DLL like this

int _tmain(int argc, _TCHAR* argv[])
{
    CWinHookFacade::Hook();
    getchar();
    CWinHookFacade::Unhook();
}
like image 728
Gishu Avatar asked Jun 08 '09 12:06

Gishu


1 Answers

I think the problems you're having are because you're trying to implement a hook function in C#. Based on pinvoke.net's documentation on SetWindowsHookEx(), it says that you can't do this - the hook procedure must be in an unmanaged DLL. Otherwise, this would load your DLL into all running processes with a message loop, which would in turn cause the CLR to be loaded and started in each process. Not only would this take a long time, but injecting the CLR into all processes probably isn't the best idea. Plus, what happens if a process already has a running CLR that is a different from from what your DLL was built against?

The best approach would be to move this code to an unmanaged C++ DLL, and use some sort of interprocess communication to send the data intercepted by your hook procedure back to your application.

Update

First (and this is probably not causing your issue), why are you calling LoadLibrary() to get the HINSTANCE of your DLL? It would probably be better to call GetModuleHandle() since your DLL is already loaded.

As for why your hook procedure is never called - how have you verified this? Since you are hooking all GUI threads in the system, this means that your DLL needs to be loaded into all GUI processes. It's likely that you won't see the results of calling wprintf() because the other processes don't have a console window up to show the output.

To verify that your DLL is loaded properly, use a program that lists the DLLs loaded by a process (I like Process Explorer). You can use the Find | Find Handle or DLL menu item to search for the name of your DLL - it should show up in all processes with a message loop.

Once you have verified that your DLL is loaded, to see if your hook is called you can attach a debugger to another process (such as Notepad), and then set a breakpoint in your hook function. That should go off whenever a message is sent to the CBT hook. If you don't want to use a debugger, then you can change the calls to wprintf() to OutputDebugString() and run a utility like DebugView to monitor the results.

Finally, since your hook function is called in the context of another process, your m_HookHandle variable will not be valid there. You should store it in a shared data segment so that all loaded instances of your DLL will have the same value.

like image 59
Andy Avatar answered Nov 11 '22 17:11

Andy