tags:

views:

1010

answers:

2

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();
}
+1  A: 

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.

Andy
I knew about the injection part - had my code in a DLL. But didn't know that it is impossible to do in C#. Will try this out in CPP tomorrow and post how it goes...I missed reading the notes section on the pinvoke page to my peril. (I found that ManagedThreadID doesn't work the hard way too)
Gishu
Another thing that threw me off was that the Hook class from http://mwinapi.sourceforge.net/ library has a ctor parameter - bGlobal and is completely managed. Strange...
Gishu
Switched to unmanaged too - still doesn't seem to work. Updated original question...
Gishu
I knew my hook is not getting called coz everything used to crash-n-burn :) Yes I soon figured out logging to a file is a better option than console. The shared data segment thing found me a bit later down the road.
Gishu
+1  A: 

Finally nailed this one. Wrote it up as a blog post in case someone needs this in the future.

Gishu