?
Solved

System-Wide Hooks

Posted on 1999-07-18
10
Medium Priority
?
462 Views
Last Modified: 2013-12-03
This is my first time using the Expert Exchange. I was not aware of it before today.

I am trying to get a system-wide hook to work. Everything is working except the hooks are not system-wide. I have written a DLL and an MFC SDI application to invoke the DLL. The DLL does the SetWindowsHookEx, specifies a callback within itself, specifies zero for threadid, and saves the hook handle in a global variable. The DLL does not use MFC. Everything works, except I only get hooked into my MFC app that calls the DLL. I tried the keyboard and mouse hooks first. I get keyboard and mouse messages but only from my program. So I tried the WH_SYSMSGFILTER hook with similar results. I have set breakpoints in the hook procedures and I get breaks only for hooks from my program.

I have looked at the HOOKS sample from the Microsoft "Win32 Hooks" article. I confess that this is the first DLL I have written. Since the HOOKS sample is a c program (not cpp) and uses things like LibMain it is hard to know what it does differently to make a difference. I do not see anything relevant to my program that it is doing differently.

I have searched the MSDN as much as I can and I have done everything else I can find to do to get hooks into other apps.

I realize that it would help to have the complete source for my DLL to look at but if someone has encountered a problem like this and is aware of something relatively obscure then please let me know.

Otherwise I am sure it is something simple that I am just overlooking.
0
Comment
Question by:SamHobbs
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
10 Comments
 
LVL 20

Expert Comment

by:Madshi
ID: 1403245
Hi Sam, hope we can help you, especially because it is your first question here...  :-)

Would you please show us the code where you call SetWindowsHookEx and the hook function code in the DLL?

Thank you...

Regards, Madshi.
0
 
LVL 3

Expert Comment

by:Laminamia063099
ID: 1403246
HHOOK SetWindowsHookEx(

    int idHook,      // type of hook to install
    HOOKPROC lpfn,      // address of hook procedure
    HINSTANCE hMod,      // handle of application instance
    DWORD dwThreadId       // identity of thread to install hook for
   );

For your hook procedure to monitor ALL threads in the system you must set dwThreadId in the call to SetWindowsHookEx to 0.  This will assure you that your Hook applies to all threads, and not just your current application.  

You must set multiple hooks to make sure that you get the messages sent to windows and the messages posted in each window's message queue, WH_CALLWNDPROC and WH_GETMESSAGE respectively.  Use other WH_ hook setups according to what you want to capture.

If you have other questions, especially about the example DLL, please ask and they shall be answered.

Laminamia
0
 
LVL 3

Author Comment

by:SamHobbs
ID: 1403247
I stated explicitly that I set the dwThreadId to zero. I also made it clear that I am using various hook types. That is, I have used the keyboard, mouse and system messages hook types and I expect only the type of hook callbacks that are to be expected from them.

Other things I have done are to use the #pragma data_seg and I have tried setting the entry point. The documentation is contradictory on whether an entry point needs to be specified, but it does not have an affect on my problem. I thought that perhaps there is a problem with the way the globals are set up but since by using debug breakpoints it appears that the hooks are not being called at all for the other processes I get the impression that the globals are not a problem.

0
NEW Veeam Agent for Microsoft Windows

Backup and recover physical and cloud-based servers and workstations, as well as endpoint devices that belong to remote users. Avoid downtime and data loss quickly and easily for Windows-based physical or public cloud-based workloads!

 
LVL 20

Expert Comment

by:Madshi
ID: 1403248
Have you read my last comment?
0
 
LVL 3

Author Comment

by:SamHobbs
ID: 1403249
Yes, Madshi, I saw your request. Did you see my comment in my original question that I realize that it might help for me to include the source?

Perhaps what you mean to say is that there is not anything obscure that anyone has encountered that would cause a problem like this, but perhaps someone would know what the problem is by looking at my source. I realize that if I am making a stupid mistake then the only way anyone could help me is by looking at the source.

May I wait until the end of today at least before posting the source?

0
 
LVL 86

Expert Comment

by:jkr
ID: 1403250
To install the hook, call a function INSIDE the DLL that does the following:

g_hhk = SetWindowsHookEx ( WH_GETMESSAGE,
( HOOKPROC) HookProc,
g_hThisDll,
0
);

where g_hhk ahd g_hThisDll are global variables in the hook DLL. The DLL's instance handle is stored upon initialization, e.g.

int APIENTRY DllMain ( HINSTANCE hInstance,
DWORD dwReason,
LPVOID lpReserved
)
{
    if ( dwReason == DLL_PROCESS_ATTACH)
{
#ifdef _DEBUG
OutputDebugString ( "DllMain(): DLL_PROCESS_ATTACH\n");
#endif

g_hThisDll = hInstance;

return ( DisableThreadLibraryCalls ( g_hThisDll));
}

//...

}

(I hate the EE tab eater ;-)

BTW - about shared data segments:
#pragma      data_seg ( ".shared")
HHOOK g_hhk = NULL;
#pragma      data_seg ()
#pragma comment ( linker, "/section:.shared,rws")

Data that should go into such a segment _must_ be initialized...

0
 
LVL 20

Expert Comment

by:Madshi
ID: 1403251
Yes, Sam, of course you can wait as long as you want to...  :-)
I just wanted to be sure that you didn't miss my comment.
Perhaps jkr's code already helps you. But if not, I think it will be difficult for us to help, because without any code we can only guess, where the problem is.
0
 
LVL 3

Author Comment

by:SamHobbs
ID: 1403252
Well, jkr, I think had already tried everything that you are suggesting except the DisableThreadLibraryCalls. It does not seem to make a difference either. So here is what you asked for, Madshi. There is not much to it, and I have tried many other combinations. I have tried to get the tabs out, but you will notice that my formatting style is a bit different.


#define STRICT 1
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

//    -    -    -    -    -    -    -    -    -
// See MS KB Article Q125677
#pragma data_seg(".sdata")
    HHOOK g_hKeyboardHook=NULL;
    HHOOK g_hMouseHook=NULL;
    HHOOK g_hSysMsgHook=NULL;
    HINSTANCE g_hDllInst=NULL;
#pragma data_seg()
// instead of .def? See MS KB Article Q153901
#pragma comment(linker, "/section:.sdata,rws")
//    -    -    -    -    -    -    -    -    -

//    -    -    -    -    -    -    -    -    -
BOOL WINAPI DllMain(HINSTANCE hDllInst, DWORD fdwReason, LPVOID lpvReserved) {
switch(fdwReason) {
    case DLL_PROCESS_ATTACH:
        g_hDllInst = hDllInst;
        return TRUE;
    case DLL_THREAD_ATTACH:        // The return value is ignored.
        break;
    case DLL_THREAD_DETACH:        // The return value is ignored.
        break;
    case DLL_PROCESS_DETACH:    // The return value is ignored.
        break;
    }
return DisableThreadLibraryCalls(hDllInst);
}
//    -    -    -    -    -    -    -    -    -

//    -    -    -    -    -    -    -    -    -
__declspec(dllexport) LRESULT CALLBACK
        KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
    char s[64];
if (wParam > 31 && wParam << 256) {
    char c = LOBYTE(LOWORD(wParam));
    wsprintf(s, "K: %c\n", c);
    }
else
    wsprintf(s, "K: %X\t%lX\t%X\t%X\n", nCode, wParam, HIWORD(lParam), LOWORD(lParam));
OutputDebugString(s);
return CallNextHookEx(g_hKeyboardHook, nCode, wParam, lParam);
}
//    -    -    -    -    -    -    -    -    -
__declspec(dllexport) void HookKeyboard() {
g_hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hDllInst, 0);
}
//    -    -    -    -    -    -    -    -    -
__declspec(dllexport) void UnhookKeyboard() {
    UnhookWindowsHookEx(g_hKeyboardHook);
}
//    -    -    -    -    -    -    -    -    -

//    -    -    -    -    -    -    -    -    -
__declspec(dllexport) LRESULT CALLBACK
        MouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
    MOUSEHOOKSTRUCT *pMouseHookStruct = (MOUSEHOOKSTRUCT *)lParam;
    char s[64];
if (nCode!=HC_ACTION)
    return CallNextHookEx(g_hMouseHook, nCode, wParam, lParam);
if (wParam == WM_MOUSEMOVE || wParam == WM_NCMOUSEMOVE)
    return CallNextHookEx(g_hMouseHook, nCode, wParam, lParam);
wsprintf(s, "M: %lX\t%ld,%ld\t%lu\t%lX\n", wParam, pMouseHookStruct->pt.x, pMouseHookStruct->pt.y,
         pMouseHookStruct->wHitTestCode, pMouseHookStruct->hwnd);
OutputDebugString(s);
return CallNextHookEx(g_hMouseHook, nCode, wParam, lParam);
}
//    -    -    -    -    -    -    -    -    -
__declspec(dllexport) void HookMouse() {
g_hMouseHook = SetWindowsHookEx(WH_MOUSE, MouseProc, g_hDllInst, 0);
}
//    -    -    -    -    -    -    -    -    -
__declspec(dllexport) void UnhookMouse() {
    UnhookWindowsHookEx(g_hMouseHook);
}
//    -    -    -    -    -    -    -    -    -

//    -    -    -    -    -    -    -    -    -
__declspec(dllexport) LRESULT CALLBACK
        SysMsgProc(int nCode, WPARAM wParam, LPARAM lParam) {
    MSG *pMsg = (MSG *)lParam;
    char s[64];
if (pMsg->message == WM_MOUSEMOVE || pMsg->message == WM_NCMOUSEMOVE)
    return CallNextHookEx(g_hSysMsgHook, nCode, wParam, lParam);
wsprintf(s, "S: %X\t%lX\t%X\n", nCode,  pMsg->message, pMsg->hwnd);
OutputDebugString(s);
return CallNextHookEx(g_hSysMsgHook, nCode, wParam, lParam);
}
//    -    -    -    -    -    -    -    -    -
__declspec(dllexport) void HookSysMsg() {
g_hSysMsgHook = SetWindowsHookEx(WH_SYSMSGFILTER, SysMsgProc, g_hDllInst, 0);
}
//    -    -    -    -    -    -    -    -    -
__declspec(dllexport) void UnhookSysMsg() {
    UnhookWindowsHookEx(g_hSysMsgHook);
}

0
 
LVL 2

Accepted Solution

by:
zyqwert earned 400 total points
ID: 1403253
You are overlooking something simple.  I tested your code and it is definitely succeeding in setting a global hook.

You are probably running in a development environment like VC++ and looking at the debug window for output.  VC++ acts as a debugger for your process only, so it shows you the OutputDebugStrings for your process only.  When you click on another application, the OutputDebugString executes in your DLL in the other process, and VC++ knows nothing about it.
If you run SoftIce, it catches all of the OutputDebugStrings, and you will see your output when you click on other applications.
You could also replace the OutputDebugString(s) with:

HDC hdc= GetDC(NULL);
TextOut(hdc, 0, 0, s);
ReleaseDC(NULL, hdc);

This will draw your string in the upper left of the screen.

One way I know your code was in effect was I was unable to delete the dll even after the original application ended because the dll was still mapped into other address spaces.

I only tested your mouse hook.  I assume the others are similiar.  Reject this answer if anything is unclear.
0
 
LVL 3

Author Comment

by:SamHobbs
ID: 1403254
Thank you, zyqwert. You are correct about everything you said. My DLL is working good now that you have provided the final piece of the puzzle.

I had been working on this problem for a week before I posted the question but I did not stop working on it after I posted the question. So last night I had gotten a version working but then it stopped working again and I did not know what made the difference. By the time I shut down my system for the night I had gotten enough of the pieces of the puzzle that about the same time you were posting your answer I had decided that something like that was happening. I did not know that the debugger is unable to catch the activity in the other processes, though, so your assistance filled in the final piece of the puzzle.

Your TextOut trick is good too. It is what the HOOKS sample does. I am using PostMessage to send registered messages back to my app, which will probably be a technique useful for the completed project too.

0

Featured Post

[Webinar] Lessons on Recovering from Petya

Skyport is working hard to help customers recover from recent attacks, like the Petya worm. This work has brought to light some important lessons. New malware attacks like this can take down your entire environment. Learn from others mistakes on how to prevent Petya like worms.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

For a while now I'v been searching for a circular progress control, much like the one you get when first starting your Silverlight application. I found a couple that were written in WPF and there were a few written in Silverlight, but all appeared o…
Whether you've completed a degree in computer sciences or you're a self-taught programmer, writing your first lines of code in the real world is always a challenge. Here are some of the most common pitfalls for new programmers.
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
Add bar graphs to Access queries using Unicode block characters. Graphs appear on every record in the color you want. Give life to numbers. Hopes this gives you ideas on visualizing your data in new ways ~ Create a calculated field in a query: …

719 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question