Why aren’t low-level hooks injected?


When I described what the HINSTANCE parameter to the SetWindowsHookEx function is for, I neglected to mention why the low-level hooks are not injected.

But then again, it should be obvious.

The low-level hooks let you see input as it arrives at the window manager. At this low level of processing, the window manager hasn't yet decided what window will receive the message. After all, that's the whole point of the low-level hook: to filter the input before the window manager does anything with it. "Deciding what window should get the message" counts as "anything". Consequently, it can't inject the call into the destination window's context even if it wanted to. There is no destination window yet!

So, for lack of a better choice, it uses the context that registered the hook. Of course, all this context-switching does come at a cost. Low-level hooks are consequently very expensive; don't leave them installed when you don't need them.

Comments (10)
  1. JamesW says:

    Nice timing on the subject – I’ve been messing with hooks recently. Are you saying that only certain classes of hook are not injected, such as WH_CALLWNDPROC, or do they all behave like this? I guess what I’m asking is: ‘are all hooks low-level hooks’.

    I’ve been using WH_CBT and the way that bugs in my CBTProc bought the system to its knees made me think this was getting injected into all running processes. I guess it could be the other apps are stalled waiting for my hook procedure to complete though.

    By beef with hooking is that console windows are ‘special’ in that hooking doesn’t work for them. There is a KB article on this:

    http://support.microsoft.com/kb/q108232/

    It contains a (IMHO) weak get-out on the lines of: ‘console windows were designed differently form day one and we ain’t changing them’. It’s annoying because it changes a simple hook procedure into one with evil special case hacks so that it can consider console windows too. Why were console windows designed differently to all the other windows?

  2. Take another look at the linked article. It talks about injection and calls out WH_KEYBOARD_LL and WH_MOUSE_LL as non-injected (LL = low-level). This entry was an elaboration on the special status the non-injected hooks receive. They are the exceptions, not the rule.

  3. JamesW says:

    Thanks for the RTFA pointer – it all makes sense now ;) Just those console windows to worry about…

  4. Try doing a shell hook and getting window close events for console windows. If you get them, you are luckier than I have ever been. I had to resort to another form of "hook" where you register an event message recipient using an undocumented user32 export. Why is Explorer allowed to know this but not us?

  5. Cd-MaN says:

    Wow. The chance to ask an expert.

    I have a little hobby project: I want to make a console window that is resizable like normal windows. My original idea was to set a new window handler function (subclass) for the console window, handle the appropiate window messages and use the SetConsoleScreenBufferSize to approximate the required window size. However the SetWindowLong (ok, I know I should be using SetWindowLongPtr, but this was for testing :) ) gives me an access denied error. Reading on the web I was told that the window procedures for console windows live in a 16 bit stub or something like that, maybe this could be problem? Then I tried to make a local hook for the window, but the problem is it receives none of the messages (like maximize, size, minmaxinfo, etc). I’ve tried WH_CALLWNDPROCRET

    and WH_CALLWNDPROC. Now I’m creating an other process, which has a window and places the console window inside it, emulating the different actions. However this is far less elegant and also harder to implement (requires IPC for certain actions, etc). What would your suggestion be?

    Thank you for posting interesting information to the blog.

    Cd-MaN

  6. JamesW says:

    I’m pretty sure the console behaviour has nothing to do with 16 bit stubs in the NT bases OSes. My theory, based on the trusty tools of blind guessing and believing random USENET posts, is that it has something to do with the console app’s window being owned by csrss.exe rather than the console app itself. I believe csrss.exe also maintains the console app’s message loop too.

    Whatever it is the fact that console apps don’t play nice with hooks really reduces the effectiveness of hooking.

  7. daev says:

    A nice thorough detail-oriented article on how consoles work and how the OS handles CUI-subsystem applications would be an unmitigated blessing. Even Russinovich & Solomon’s "Windows Internals" says nothing about them.

    Raymond, is this your area of expertise? If so, I’ll file a suggestion in the box; if not, I’d appreciate a pointer to more suitable MS blog.

  8. "Red-headed stepchild" indeed! It’s not my area of expertise, and to be honest I don’t know who is the expert here.

  9. TimK says:

    This thread seems to be focusing on SetWindowsHookEx()-type hooks. SetWinEventHook() can also be used for some things, including getting information about console windows. There are even some events that are specific to consoles; though the usefulness of the console events is somewhat lessened by the fact that they are triggered asynchronously (by csrss.exe).

  10. Mike says:

    JamesW: Yes. It’s true that the console window proc and the message loop for the console is in csrss.exe. The reason for this is that you can have more than one console application that writes to the same console and csrss.exe acts as a "console server".

    Cd-MaN: There is a way to inject a dll into another process (in this case csrss.exe) so you can get rid of "Access denied" errors but in the case of csrss.exe you’re playing with fire… Last time I did this I’ve got a BSOD :). Also, you must be an admin to do this.

Comments are closed.

Skip to main content