Link to home
Start Free TrialLog in
Avatar of gimmeadrink
gimmeadrink

asked on

SetWindowsHookEx

Hiya,

I am having troubles getting SetWindowsHookEx to work.

I have a dll in which i want to set a hook into another (application/process)???
Im concerned about the SetWindowHookEx funciton call. By checking LastError, it tells me that the parameters are incorrect.

I am pretty sure that this is because im sending a HWND instead of a thread id for the target applicaiton (to hook into).

How do i find the threadid for the target applicaiton(/window)????????

Here is some sample code... should i give any more information? I am calling this dll from VB (if this helps)



bool __stdcall SetHook(DWORD dwThreadId) {
      BOOL fOk = FALSE;
      if (dwThreadId != 0) {
            g_hhook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hinstDll, dwThreadId);
            if (fOk = (g_hhook != NULL)) {
                  g_dwThreadIdDIPS = GetCurrentThreadId();
                  fOk = PostThreadMessage(dwThreadId, WM_NULL, 0,0);
                  if (!fOk) {
                        // display LastError
                        DisplayError("PostThreadMessage failed");
                  }
            } else {
                  DisplayError("g_hhook == NULL");
            }
      } else {
            fOk = UnhookWindowsHookEx(g_hhook);
            g_hhook = NULL;
      }
      return (fOk);
}

Thanks for your help


Avatar of PlanetCpp
PlanetCpp

The GetWindowThreadProcessId function retrieves the identifier of the thread that created the specified window and, optionally, the identifier of the process that created the window.

DWORD GetWindowThreadProcessId(
  HWND hWnd,             // handle to window
  LPDWORD lpdwProcessId  // address of variable for process identifier
);
 
Parameters
hWnd
Handle to the window.
lpdwProcessId
Pointer to a 32-bit value that receives the process identifier. If this parameter is not NULL, GetWindowThreadProcessId copies the identifier of the process to the 32-bit value; otherwise, it does not.
Return Values
The return value is the identifier of the thread that created the window.

so
DWORD processid;
GetWindowThreadProcessId(handlehere,&processid);
then pass processid as the threadid in setwindowshookex
Avatar of jkr
If you want the hook to wrk inside other processes also, you need to create a DLL for it and set the thread ID to 0 - see also e.g. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwui/html/msdn_hooks32.asp ("Win32 Hooks") and http://www.codeproject.com/dll/#Hooks for sample code.
 
Avatar of gimmeadrink

ASKER

Thanks for the reply, I am just trying it with my code....

While im doing this, do you know of a simple way to check if my hook has worked? I'm pretty much only using C++ because i cant do what i want to do using VB, this is the first time i have used process specific hooks as well.

No probs if you cant help... as soon as im sure its all working ill assign points

thanks heaps

jkr: thanks, ill have a look at that also.

depending on the hook you can write to a file, use a messagebox (make sure it only fires once by using a flag, maybe a bool variable set to true then only show the message when its false)
or try OutputDebugString, but for some reason i never see the output when i use it.
Hi again, I am still having issues.

i am getting the same invalid arguement problem. Here is the revised code:

bool __stdcall SetHook(DWORD dwHwnd) {
      DWORD dwThreadId;      
      HWND hwnd = reinterpret_cast<HWND>(dwHwnd);
      GetWindowThreadProcessId(hwnd,&dwThreadId);

////// start temp debug code
char *tmp;
tmp =(char *) malloc(900 * sizeof(char));
// check the dwThreadId to ensure its correct!!!
sprintf(tmp,"%d",dwThreadId);      
DisplayError(tmp);


// check the g_hinstDll
tmp =(char *) malloc(900 * sizeof(char));
sprintf(tmp,"%d",g_hinstDll);      
DisplayError(tmp);

//////// end temp debug code.

      BOOL fOk = FALSE;
      if (dwThreadId != 0) {
            g_hhook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hinstDll, dwThreadId);
            if (fOk = (g_hhook != NULL)) {
                  fOk = PostThreadMessage(dwThreadId, WM_NULL, 0,0);
                  if (!fOk) {
                        DisplayError("PostThreadMessage failed");
                  }
            } else {
                  DisplayError("g_hhook == NULL");
            }
      } else {
            fOk = UnhookWindowsHookEx(g_hhook);
            g_hhook = NULL;
      }
      return (fOk);
}


And here is the function header for GetMsgProc

LRESULT __stdcall GetMsgProc (int nCode, WPARAM wParam, LPARAM lParam);

Whew, ok, sorry to post so much code again but im still in trouble.

Obviously one of the parameters are wrong in the following line:

g_hhook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hinstDll, dwThreadId);

I figured WH_GETMESSAGE should be fine.
I checked dwThreadId against the processId i get from VB and it is correct
g_hinstDll is non-null/0 number, but im not sure how to check if it is correct. I get it this way.....

BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD ul_reason_for_call, LPVOID reserved) {
      switch (ul_reason_for_call) {
            case DLL_PROCESS_ATTACH:
                  g_hinstDll = hInst;
                  break;

            case DLL_THREAD_ATTACH:
                  break;

            case DLL_THREAD_DETACH:
                  break;

            case DLL_PROCESS_DETACH:
                  break;
      }
      return TRUE;
}

I also have the following directives at the beginning of the file (which i think is what jkr was referring to???):

#pragma data_seg("Shared")
HHOOK g_hhook = NULL;
#pragma data_seg()



Ok, again, sorry about the mass of code, but i thought id give you as much as i can.

So i have checked all the parameters but one:     (HOOKPROC)GetMsgProc

Is my GetMsgProc funciton wrong? Is there some sort of error relating to process access violations??? if so, i would have thought id get a different error message.

I have made the change to send a processId rather than a hWnd but i still have a problem....

Can you see anything else?

Thanks a lot.
your specifying __stdcall, im not saying this is 100% the problem im not sure but its a callback function you need to use
LRESULT CALLBACK GetMsgProc
also unless you just didnt post it you need to tell windows that the data segment called SHARED is shared, like this:
#pragma data_seg("SHARED")
      HHOOK g_hhook=0;
#pragma data_seg()
#pragma comment(linker, "/section:SHARED,RWS") //<-need this
unless you did it through the linker settings but i never did

try the callback thing and see whats happens
You still are using a Thread ID != 0 - why?
it shouldn't be = 0, he said he wanted to set a hook into another application, i don't think he wanted a system hook. he knows the handle to the app he wants to hook.
>>he said he wanted to set a hook into another application, i don't think he wanted a system hook

That doesn't make a difference - the TID will be invalid in another process' context anyway.
i dont understand what you're saying. he has the hook code in a dll which means he can either insert the hook into one app by specifying the threadid or he can have a global system hook by specifying 0 as the threadid.
>>I have a dll in which i want to set a hook into another (application/process)???
 a few hooks only allow systemwide insertion but his is ok for either.if he uses his WH_GETMESSAGE hook with a thread id of 0 from his dll he will have created a system-wide hook.
[MSDN]
dwThreadId
Specifies the identifier of the thread with which the hook procedure is to be associated. If this parameter is zero, the hook procedure is associated with all existing threads.
Ok, a lot of posts whilst i was sleeping....

Re: #pragma statements, i did actually have the linker part, but i forgot to post it... thanks for your comment tho. I have "Shared" rather than "SHARED", im not sure if that was a problem... but i changed it to all caps.


re: Thread ID != 0, i want to hook into a specific process only. I was checking threadid, and if it was 0, then i would unhook instead. I think i might change that to dwHwnd instead tho, since i am working out the threadid in the code. This way, if dwHwnd is 0 then i will unhook, else i will find the associated threadid (/processid) and hook into that instead.


to my understadning, the last post from PlanetCpp is correct, some hooks can only be system wide (like journal record/playback) and others can be either (of which WH_GETMESSAGE hook is one).

Basically, this whole thing has come about because i want information out of an application.... kinda like a debugger. The way debuggers do it is by hooking into the application using a dll. Windows then loads this dll into the target application's address space, and not just the hooking functionality, but the entire dll. This means that i can use other functions in the dll to get information out of the application (like a debugger would) as i am mapped in the correct address space and do not have access violations.

I feel pretty bad about getting ppl to help me with this, but i am new to C++, writing dlls and thread specific hooks (i have only used system wide hooks before) ... and so far, i think im doing pretty well.

I am pretty confident that i am taking the correct approach here. I have finially found some material that does what i want to do, however, does so in windows 95 (when 32 bit first came out), not XP... im not sure if that is a problem or not.

I will try changing GetMsgProc to ...

LRESULT CALLBACK GetMsgProc

Maybe that is the problem.
jkr: despite whether or not the context will be an issue, its not my issue yet. im pretty sure that if it was my problem atm, i would be getting a different error to invalid argumenet.

In the material that i am trying to follow, it speaks about the issues with process context, and provided that the hookid is shared and the same in calling application and the target application, then windows will allow everything to work properly (in not so many words), but i will test that theory soonish (i hope).

Thanks for you help so far... ill try that change and get back to ya.


Ok, i made the change with the callback function, i also changed nCode to code (as per the ms spec for the function) just in case.

I still got the invalid parameter message.

I then tryed adding the following line just before calling the setwindowshook

dwThreadId = GetCurrentThreadId();

and the hook was successful.

does this mean that my process thread is invalid? Is it possible that it is something to do with context issues that jkr was talking about but just gives a shitty error message? Is it possible that the funciton GetMsgProc needs to be global somehow?

Im really not sure how to allow other processes to see the GetMsgProc funciton in my dll if that is the problem, i thought that it was not an issue (at least not from the literature i have read). I am hoping that my 'processid' is not a correct threadid and therefore is a problem rather than having interprocess issues.

Any ideas?

Thanks
ASKER CERTIFIED SOLUTION
Avatar of PlanetCpp
PlanetCpp

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
thanks for the answer, i actually solved it as well before noticing ur comment.

The problem in my code was....

GetWindowThreadProcessId(hwnd,&dwThreadId);

should have been

dwThreadId = GetWindowThreadProcessId(hwnd,NULL);

You have a combination of the two.

Now onto the next problem, how to get the memory associated with a hwnd once im in the correct address space... blah, i guess thats another question

THanks