Link to home
Start Free TrialLog in
Avatar of redhat092798
redhat092798

asked on

SetWindowsHookEx keep crashing the program that I'm hooking.

I have no idea why this happens. Perhaps my codes is not safe...
The following is the hooking part of my hooker program..

In my MainProc:

case WM_CREATE:
if(hAver == NULL)
{
  hAver = FindWindow( "AfxFrameOrView42s", "Aver Remote Control");
}
 if(hAver == NULL)
{
  MessageBox(NULL, "Aver Remote is not up or not detected", "Error",   MB_OK);
}
             
dwThreadID = GetWindowThreadProcessId( hAver, NULL );
       
hook = SetWindowsHookEx( WH_CALLWNDPROC ,
                                 (HOOKPROC)CallWndProc,       
                                 hInst,      
                                 dwThreadID       
                              );
if(hook==NULL)
{
   MessageBox(NULL, "Something is wrong with SetWindowsHookEx",    "Error", MB_OK);
}
break;


And this in the CallBack Procedure of the hook...
LRESULT CALLBACK CallWndProc( int nCode, WPARAM wParam,
LPARAM lParam )
{      
    if (nCode==HC_ACTION)
    {
       if(((CWPSTRUCT *)lParam)->message==WM_GETTEXT)
       {
          MessageBox(NULL, "WM_GETTEXT is sent", "Alright Already", MB_OK);        }

    return CallNextHookEx(hook, nCode, wParam, lParam);
}

I got the message box to appear , then the program that I'm hooking and my program freezes. At least I need to know how I can check where is the problem. By the way, if coincidentally, anyone who is using AVerMedia TVPhone, I'm actually trying to hook the remote control messages. I have seen someone done it to control winamp, I'm trying to do the same but to control another program. Please excuse my unprofessional codes.
ASKER CERTIFIED SOLUTION
Avatar of NickRepin
NickRepin

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
Avatar of NickRepin
NickRepin

It's safer to write the trace messages to the file.

When you call MessageBox() or other functions that create or use a window, your hook proc is called repeatedly and endlessly.
if NULL == hAver then do forget to return( do not do GetWindowThreadProcessId() & SetWindowsHookEx() )
Avatar of redhat092798

ASKER

NickReppin : You I should trace the messages by writing to a file ?
Can I do a SendMessage() to another program that will do something ? In this case , no new window is opened from within my hook proc.

gelbert : I don't quite understand what you are trying to say. I think you are saying  that if hAver == NULL, then I should do a follow-up NOT to do GetWindowThreadProcessId() & SetWindowsHookEx().
Is this right ?
"Avoid Calling SendMessage() Inside a Hook Filter Function"
http://support.microsoft.com/support/kb/articles/Q74/8/57.ASP

gelbert told about this:

if(hAver == NULL)
{
  MessageBox(NULL, "Aver Remote is not up or not detected", "Error",   MB_OK);
// BREAK must be here;
break;
}
   
Thanks for the article link for not to use SendMessage within a hook proc . I understand that PostMessage() is ok to use from that article . I understand what gelbert meant now.

Ok, I almost got it all. One last question, you notice that I was trying to hook WM_GETTEXT message of that particular program.
How do I get what is the actual value that was sent with WM_GETTEXT message. Thats quite crucial to what I want to do. Thats all and I'll wrap this up.
It's not clear what do you want to know.

As far as I understood your code,
some application sends WM_GETTEXT to Aver.

Do you want to know the address of the buffer to store the windows text and the buffer length passed via WM_GETTEXT message, or the window text itself?

The first details you can get

// buf is enpty so far.
char* buf=(char*)  (((CWPSTRUCT *)lParam)->lParam);

DWORD szbuf=(((CWPSTRUCT *)lParam)->wParam);

The window text you can get only if you install WH_CALLWNDPROCRET hook. With this hook, your proc will be called when the SendMessage returns, ie when the Aver filled the buffer with the window text.

(char*)  (((CWPSTRUCT *)lParam)->lParam)

will contains the window text.




ok, I got the part ...

char* buf=(char*)  (((CWPSTRUCT *)lParam)->lParam);

                    DWORD szbuf=(((CWPSTRUCT *)lParam)->wParam);

But my problem is, actually seeing what is that value of lParam, which is the buf here.
I tried writing it to a file, CRASHED.
I tried sending to another program I wrote, CRASHED.

It seems that I can only do a PostMessage in a hook procedure. Everything else causes a crash on the program I'm hooking.
How do I know what is the value. Spy does give its value. It only says that WM_GETTEXT is send and received.
Please help out here. I'm so close to getting to my program to work as I want it. Thank you.
Adjusted points from 200 to 220
I'm increasing the points to 220 since this question is dragging.
Here is the sample how it has to be.
Note the the hook proc must be in a DLL.

//----------
// Main.cpp
//----------

#include <windows.h>
#include <iostream.h>
#include <stdlib.h>
#include <conio.h>

bool setHooks(DWORD threadId);
void removeHooks();

void main(int argc,char* argv[])
{
   HWND w=FindWindow(0,"MSDN Library Visual Studio 6.0");
   if(!w) {
      cout<<"Window not found"<<endl;
      return;      
   }
   DWORD threadId=GetWindowThreadProcessId(w,0);
   if(setHooks(threadId)) {
      cout<<"Press Enter to quit"<<endl;
      _getch();
   }  
   removeHooks();
}

//----------
// Hook.dll
// MUST BE COMPILED WITH /MD OPTION!
// (multithreaded DLL).
//----------
#define STRICT
#include <windows.h>
#include <fstream.h>

// Shared data segment.
#pragma data_seg(".shared")
HHOOK hHook=0;
HHOOK hHook2=0;
#pragma data_seg()
#pragma comment(linker,"/SECTION:.shared,RWS")

HINSTANCE hDll;
ofstream f;
CRITICAL_SECTION cs; // Protects the log file for multithreaded process.

void onMessage(CWPSTRUCT& msg)
{
   EnterCriticalSection(&cs);
   f<<"WM_GETTEXT sent, szbuf="<<msg.wParam<<", buf="<<
      hex<<msg.lParam<<dec<<endl;
   LeaveCriticalSection(&cs);
}

void onMessage2(CWPRETSTRUCT& msg)
{
   EnterCriticalSection(&cs);
   f<<"WM_GETTEXT processed, szbuf="<<msg.wParam<<", buf="<<
      hex<<msg.lParam<<dec<<
      ", txt="<<LPCSTR(msg.lParam)<<endl;
   LeaveCriticalSection(&cs);
}

LRESULT CALLBACK hookProc(int code,WPARAM wParam,LPARAM lParam)
{
   if(code==HC_ACTION) {
      CWPSTRUCT* msg=(CWPSTRUCT*)(lParam);
      if(msg->message==WM_GETTEXT)
         onMessage(*msg);
   }
   return CallNextHookEx(hHook,code,wParam,lParam);
}

LRESULT CALLBACK hookProc2(int code,WPARAM wParam,LPARAM lParam)
{
   if(code==HC_ACTION) {
      CWPRETSTRUCT* msg=(CWPRETSTRUCT*)(lParam);
      if(msg->message==WM_GETTEXT)
         onMessage2(*msg);
   }
   return CallNextHookEx(hHook2,code,wParam,lParam);
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID)
{
   if(fdwReason==DLL_PROCESS_ATTACH) {
      hDll=hinstDLL;
      // Create different log file for each process.
      char buf[120];
      wsprintf(buf,"C:\\HOOK_%lu",GetCurrentProcessId());
      f.open(buf,ios::out | ios::app | ios::trunc);
      InitializeCriticalSection(&cs);
   }
   else if(fdwReason==DLL_PROCESS_DETACH)
      DeleteCriticalSection(&cs);
   return TRUE;
}

_declspec(dllexport) bool setHooks(DWORD threadId)
{
   if(hHook==0)
      hHook=SetWindowsHookEx(WH_CALLWNDPROC,hookProc,hDll,threadId);
   if(hHook2==0)
      hHook2=SetWindowsHookEx(WH_CALLWNDPROCRET,hookProc2,hDll,threadId);
   return hHook!=0 && hHook2!=0;
}

_declspec(dllexport) void removeHooks()
{
   if(hHook) { UnhookWindowsHookEx(hHook); hHook=0; }
   if(hHook2) { UnhookWindowsHookEx(hHook2); hHook2=0; }
}


A warning:  This code will crash spectacularly if applied to a global hook because onMessage1() and onMessage2() rely on the C++ runtime library being present and initialized.
Adjusted points from 220 to 250
Wow, I'm studying the codes. But I think I have to increase some more points for this.
Wow, I'm studying the codes. But I think I have to increase some more points for this.
<<<A warning:  This code will crash spectacularly if applied to a global hook because onMessage1() and onMessage2() rely on the C++ runtime library being present and initialized>>>

NO WAY. Initialization is done by C++ runtime. Linker calls _DllMainCRTStartup which performs the initialization and then calls DllMain.

>>being present
Sorry, what does it mean? Runtime dll?
Sure, you must have runtime dll *IF* you use dynamic linking.
Nick, my comment and your reply are totally out of phase.

The runtime library can be statically or dynamically linked but that is not the point.  The C++ RTL is linked ONLY to a program compiled by a C++ compiler.
However, a global hook will attach to ANY process.  When a hook function using C or C++ runtime library features gets invoked in the context of a VB, Delphi, assembler, etc. program - you get a crash.

As I said above, as long as you hook a thread in your process, you're fine.  Once you move to systemwide hooks or hook a thread in a process not compiled with the same brand (and probably version) of your C++ compiler, only plain API calls are safe.  (onMessage1() and onMessage2() use iostream operations)
Alexo, I'm not agree with you.
At least, in the case of the static linking (that's I always do). And I don't see any problem with dynamic linking so far.

Sorry, could you prove your statement?
Just describe at least the one problem that could be with my sample code above.
I'll pay you 200+A points for the correct answer :).
Dynamic linking: I'm aware that there may be the different versions of RTL, but it means that the system is not properly configured, so please don't mention this problem.
Let me clarify somethings...(Actually I think I already know but I just wanna be sure)

1) The main.cpp and the dll is compiled separately     right ?

2) I compiled them separately. The dll compiled         perfectly.  The main.cpp has three errors. I think
    setHooks() and removeHooks() is not defined or     something. Maybe you left me to to fill up that       part myself but I thought to be  the safe side, I     better asked first.

3) I know that in the end I have to put the dll in     the same folder as the main.exe. I got that         part covered, just to reassure you I'm not so         newbie, its just that hooks is out my league.  
1) Yes. If you can run VC from the command line, it looks like this:

cl /LD /MD hook.cpp user32.lib kernel32.lib
cl main.cpp hook.lib user32.lib


2) You have to specify the additional library to link main.cpp with: hook.lib.
The hook.lib is auto created when you link hook.dll.

3) You can put it in the same folder, or in any system folder. Look at LoadLibrary() doc for details about paths.
>> Alexo, I'm not agree with you.
No problem Nick, we'll just have to check which one of us is right.

>> Just describe at least the one problem that could be with my sample code above.

OK, let's make it official.

1) Compile the program + dll as presented above.

2) Substitute "MSDN Library Visual Studio 6.0" with different strings to hook the following programs:

* Any assembly program (I suggest http://grc.com/files/optout.exe)
* Any Delphy program (pick one)
* Any VB program (preferrably an old version of VB).

3) Run it and check that stuff gets written to the files.

4) email me the names of programs you used, the source and the results.

5) Allow me some time to reproduce the results on another system.

>> I'll pay you 200+A points for the correct answer :).

Ditto.  I'm even willing to raise the amount, if you wish.
alexo,

well, I'm pretty sure that the code above will work fine as system-wide hook.

I just tested it on my NT with your assembler program, Cbuilder, VB 6, Cbuilder and VB projects running at the same time. No problems at all.

To convert my sample to the global hook, replace
  setHooks(threadId)
with
  setHooks(0).

To make the code absolutely bullet-proof, it's better to remove the following, because some programs may send NULL buffer or may handle WM_GETTEXT incorrectly:

 ", txt="<<LPCSTR(msg.lParam)<<

To compile from the command-line:

cl /LD /MD hook.cpp user32.lib kernel32.lib
cl main.cpp hook.lib user32.lib
Nick, I'll check it out and get back to you...
alexo, none of the links work. Thanks anyway.
Is it possible that a WM_GETTEXT is not sending any value ? Because in the hook_4539827 file that the dll makes, its empty. Nothing. So I'm just wondering. Anyway, I think I have asked enough on this topic for anyone to tolerate. Thank you very much.
The hook_*** files are created anyway regardless of whether window received the WM_GETTEXT message or not.
If the file is empty, then the window had not received WM_GETTEXT message.

To see all messages:

void onMessage(CWPSTRUCT& msg)
{
   EnterCriticalSection(&cs);
   f<<"Msg="<<msg.message<<" is sent, wParam="<<msg.wParam<<", lParam="<<
      hex<<msg.lParam<<dec<<endl;
   LeaveCriticalSection(&cs);
}

void onMessage2(CWPRETSTRUCT& msg)
{
   EnterCriticalSection(&cs);
   f<<"Msg="<<msg.message<<" is processed, wParam="<<msg.wParam<<", lParam="<<      hex<<msg.lParam<<dec<<", result="<<msg.lResult<<endl;
   LeaveCriticalSection(&cs);
}

LRESULT CALLBACK hookProc(int code,WPARAM wParam,LPARAM lParam)
{
   if(code==HC_ACTION) {
      CWPSTRUCT* msg=(CWPSTRUCT*)(lParam);
      onMessage(*msg);
   }
   return CallNextHookEx(hHook,code,wParam,lParam);
}

LRESULT CALLBACK hookProc2(int code,WPARAM wParam,LPARAM lParam)
{
   if(code==HC_ACTION) {
      CWPRETSTRUCT* msg=(CWPRETSTRUCT*)(lParam);
      onMessage2(*msg);
   }
   return CallNextHookEx(hHook2,code,wParam,lParam);
}
Nick,
After some thought I came to the conclusion that you were right and I was confused.  There should be no problems since the DLL probably initializes the C++ RTL.

Go to https://www.experts-exchange.com/jsp/qShow.jsp?ta=winprog&qid=10323651  for your 'A'.
Hi there,
I am trying to retrieve data from a control of class type AfxFrameOrView42s. This control is in some other application.

The tool I am using is VB. Can we do it through Sendmessage.

If anyone is aware of the solution please do let me know.

Cheers
Rahim