Link to home
Start Free TrialLog in
Avatar of flynny
flynnyFlag for United Kingdom of Great Britain and Northern Ireland

asked on

Help using postmessage and WM_USER

HI all,

I've hooked into an external program and am intercepting and processing certain messages. When I detect a WM_WINPOSCHANGED message i want to post a WM_USER message to the bottom of the queue.

i do this using the callback method;

LRESULT CALLBACK AppEventsHook (int nCode, WPARAM wParam, LPARAM lParam)
{
      if(nCode == HC_ACTION)
      {
            LPCWPSTRUCT pCwp = (LPCWPSTRUCT) lParam;            
            switch(pCwp->message)
            { ....

and i post the message as so

::PostThreadMessage(pCwp->hwnd,WM_USER+1,12345,NULL);

i have also tried

::PostThreadMessage(NULL,WM_USER+1,12345,NULL);

at the moment all i try and do is catch the WM_USER message (i catch the WM_WINPOSCHANGED just fine)  and output to a log file to prove that it is working, however it doesnt seems to be posting the WM_USER message properly.

case WM_USER+1:
{
      if(pCwp->wParam == 12345)
      {
            msg.Format("DETECTED MY MESSAGE");                        CLogFile::logmsg(msg);
      }
}

can anyone tell me what i'm doing wrong here?
Avatar of Deepu Abraham
Deepu Abraham
Flag of United States of America image

Did you try PostMessage()?
Avatar of flynny

ASKER

sorry. i apologise

i question PostThreadMessage should have been PostMessage (i was playiung around with different method before i posted.) i'll edit the question.

anyway, yes i did try this and it doesn't seem to be processing the message.
Avatar of flynny

ASKER

also, could i be sending the PostMessage to the wrong HWND?
i assumed posting to pCwp->hwnd would be the right way??

is there anyway i can get hold of the window or thread the callback funtion belongs to?
Avatar of flynny

ASKER

to note i have tried the following without any luck

::PostThreadMessage(::GetWindowThreadProcessId(pCwp->hwnd,NULL),WM_USER+1,12345,NULL);
If more than one process is involved here, 'WM_USER' messages will not be unique. You should rather use

UINT uMyMsg = RegisterWindowMessage("WM_MyUniquePrivateMessage");
Avatar of flynny

ASKER

thanks for the reply jkr.

OK i tried that and it still doesnt seem to be working (i have to remove the WM_PRIVATE_MSG test outside the switch statement as it says its not a const? (error C2051: case expression not constant))

ok so i have

const static UINT WM_PRIVATE_MSG = RegisterWindowMessage("WM_PRIVATE_MSG");

LRESULT CALLBACK AppEventsHook (int nCode, WPARAM wParam, LPARAM lParam)
{
      try
      {
      if(nCode == HC_ACTION)
      {
            LPCWPSTRUCT pCwp = (LPCWPSTRUCT) lParam;

            if(pCwp->message == WM_PRIVATE_MSG)
            {
                  msg.Format("DETECTED WM_USER MESSAGE");
                  CLogFile::logmsg(msg);
            }

where i have tried

::PostMessage(pCwp->hwnd,WM_PRIVATE_MSG,NULL,NULL);
::PostMessage(NULL,WM_PRIVATE_MSG,NULL,NULL);

also if i call getlasterror it return 183 each time. (looking at the docs an error is 0, so it looks like it thinks its posting ok)

any ideas?
You can't use that within a 'switch' unfortunately, but anyway....

BTW, are you sure that you want to post that to the window you are hooking?
Avatar of flynny

ASKER

yes i think so unless you have any other ideas.

As the window hasn't added all the components at this time i simply wanted to wait until it had. I can then perfrom my EmunChildProc to run through all its child windows and hopefully find the window witht he individual text.

My thoughts were (correct me if i'm wrong) that if i could detect the event and then post a message to the bottom of the queue by the time all the other had been processed it would have added all the components so i can perform my add on this user message.

the other option was WaitForInputIdle() but unfortunately this didnt work either. Plus i wasnt sure whether or not i could call this inside a callback proc.

thanks againfor the quick response.
Avatar of flynny

ASKER

hi jkr just for reference, i've tried the WaitForInputIdle and its erroring (returns -1) getlasterror code is 6.
What about using a minimalistic windows app to test the messaging, e.g.


#include <windows.h>
 
#pragma comment(lib,"user32.lib")
 
static char g_szClassName[] = "MyWindowClass";
static HINSTANCE g_hInst = NULL;
 
const static UINT WM_PRIVATE_MSG = RegisterWindowMessage("WM_PRIVATE_MSG");
 
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
 
  if (Message == WM_PRIVATE_MSG) MessageBox(0, "Received message!", "Got it!", MB_ICONEXCLAMATION | MB_OK | MB_SYSTEMMODAL);
  switch(Message)
  {
     case WM_CLOSE:
        DestroyWindow(hwnd);
     break;
     case WM_DESTROY:
        PostQuitMessage(0);
     break;
     default:
        return DefWindowProc(hwnd, Message, wParam, lParam);
  }
  return 0;
}
 
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  LPSTR lpCmdLine, int nCmdShow)
{
  WNDCLASSEX WndClass;
  HWND hwnd;
  MSG Msg;
 
  g_hInst = hInstance;
 
  WndClass.cbSize        = sizeof(WNDCLASSEX);
  WndClass.style         = NULL;
  WndClass.lpfnWndProc   = WndProc;
  WndClass.cbClsExtra    = 0;
  WndClass.cbWndExtra    = 0;
  WndClass.hInstance     = g_hInst;
  WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
  WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
  WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
  WndClass.lpszMenuName  = NULL;
  WndClass.lpszClassName = g_szClassName;
  WndClass.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
 
  if(!RegisterClassEx(&WndClass))
  {
     MessageBox(0, "Window Registration Failed!", "Error!",
        MB_ICONEXCLAMATION | MB_OK | MB_SYSTEMMODAL);
     return 0;
  }
 
  hwnd = CreateWindowEx(
     WS_EX_CLIENTEDGE,
     g_szClassName,
     "The title of my window",
     WS_OVERLAPPEDWINDOW & ~WS_BORDER,
     CW_USEDEFAULT, CW_USEDEFAULT, 320, 240,
     NULL, NULL, g_hInst, NULL);
 
  if(hwnd == NULL)
  {
     MessageBox(0, "Window Creation Failed!", "Error!",
        MB_ICONEXCLAMATION | MB_OK | MB_SYSTEMMODAL);
     return 0;
  }
 
  ShowWindow(hwnd, nCmdShow);
  UpdateWindow(hwnd);
 
  while(GetMessage(&Msg, NULL, 0, 0))
  {
     TranslateMessage(&Msg);
     DispatchMessage(&Msg);
  }
  return Msg.wParam;
}
 
// in the hook:
 
HWND hwnd = FindWindow("MyWindowClass","TestTarget");
::PostMessage(hwnd,WM_PRIVATE_MSG,NULL,NULL);

Open in new window

Avatar of flynny

ASKER

Hi jkr, i tried that. Just so i understand how the code was working. the Translate and Dispatch msgs should put the message back into my callback msg queue should it not?. (sorry if i'm being dumb)

i'm not picking up anything in the log file though still.
Avatar of flynny

ASKER

sorry ignore that last post, i'm with it now. ;)

but no its not picking up the message being posted. so it look like my callback is the problem.
Well, that's a biolerplate Windows app, it is basically the message pump for a small window. And it logs nothing, it only should display a message box when the custom message is receivedvia
HWND hwnd = FindWindow("MyWindowClass","TestTarget");
::PostMessage(hwnd,WM_PRIVATE_MSG,NULL,NULL);

Open in new window

Avatar of flynny

ASKER

hi jkr, the app is recieving the message now ok.

(the problem was i was sending the classname in FindWindow("MyClassName","Target");)

setting to

FindWindow(NULL, "This is the title");

worked and i recieve the message box. So could posting the message to the wrong window?

basically all i do in the WM_WINPOSCHANGED method i the following

HWND foundWND = FindCurrentWindow(hookWindow);

if(foundWND>0)
{
HWND hwndzz = FindWindow(NULL,"The title of my window");
::PostMessage(hwndzz,WM_PRIVATE_MSG,NULL,NULL);
//::PostMessage(foundWND,WM_PRIVATE_MSG,NULL,NULL);
//have also tried
//::PostMessage(NULL,WM_PRIVATE_MSG,NULL,NULL);
//::PostMessage(pCwp->hwnd,WM_PRIVATE_MSG,NULL,NULL);

all with no luck? could it be the way i create the hook?
ASKER CERTIFIED SOLUTION
Avatar of jkr
jkr
Flag of Germany image

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 flynny

ASKER

hi its me thanks for the link and the explanation. Ok, so from looking at the documentation and to clarify.

so looking at the 'GetMsgProc', this will be called every time the get message is processed. So i assume this will be called everytime a message is popped off the stack? This should handle the same message too should it not? as i've changed my creation to

SetWindowsHookEx(/*WH_CALLWNDPROC*/WH_GETMESSAGE, (HOOKPROC)AppEventsHook, hinstDLL, GetWindowThreadProcessId(GetAppWnd(), NULL) );

where
hinstDLL is
static HINSTANCE hinstDLL = LoadLibrary((LPCTSTR) ".\\LaunchDLL.dll");

and getAppWnd() simply returns the handle from the FindWindow call.

also, might it be worth changing my callback proc to

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
from
LRESULT CALLBACK WndProc(UINT Message, WPARAM wParam, LPARAM lParam)
as per your example?



Avatar of flynny

ASKER

hi, just to add i tried making the switch of the callback functions and it made no difference, i'm still getting the same output.

thanks again,
Matt.
What code do you  have inside your hook now?
Avatar of flynny

ASKER

at the moment, i have the following.

i creeate the hook as follows.

appHook = SetWindowsHookEx(/*WH_CALLWNDPROC*/ WH_GETMESSAGE, (HOOKPROC)AppEventsHook, hinstDLL, GetWindowThreadProcessId(GetAppWnd(), NULL));

where GetAppWnd() siply returns the application HWND. (found using the main window title.)

My Callback is then as follows ( i have also tried using a similar overload as you provided in your example but to no avail).

const static UINT WM_PRIVATE_MSG = RegisterWindowMessage("WM_PRIVATE_MSG");

LRESULT CALLBACK AppEventsHook (int nCode, WPARAM wParam, LPARAM lParam)
{
      try
      {
      if(nCode == HC_ACTION)
      {
            LPCWPSTRUCT pCwp = (LPCWPSTRUCT) lParam;

            if(pCwp->message == WM_PRIVATE_MSG)
            {
                  
                        MessageBox(0, "Received message!", "Got it!", MB_ICONEXCLAMATION | MB_OK | MB_SYSTEMMODAL);
                  msg.Format("DETECTED WM_USER MESSAGE");
                  CLogFile::logmsg(msg);
            }
            
            switch(pCwp->message)
            {
            case WM_WINDOWPOSCHANGED:
            {

foundWND = FindCurrentWindow(hookWindow);
//hook window is name of the window in question i want to add the buttons to (i.e. waituntil it has finished creating and populating all its child windows and then test to see if i add the button.

//find current window simply checks to se where or not the windwo has already been hooked into (so buttons are not added every time a WM_WINPOSCHANGED event on that window is called.

if(foundWND>0)
{
::PostMessage(pCwp->hwnd,WM_PRIVATE_MSG,NULL,NULL);
//ahve also tried
::PostMessage(foundWND,WM_PRIVATE_MSG,NULL,NULL);
//and
::PostThreadMessage(GetCurrentProcessId(),WM_PRIVATE_MSG,NULL,NULL);

thanks again for your help jkr, its much appreciated.

Um, in case of a WH_GETMESSAGE hook, that should be
MSG* pMsg = (PMSG)lParam;
 
      if(nCode == HC_ACTION)
      {
 
            if(pMsg->message == WM_PRIVATE_MSG)
            {
                  
                        MessageBox(0, "Received message!", "Got it!", MB_ICONEXCLAMATION | MB_OK | MB_SYSTEMMODAL);
                  msg.Format("DETECTED WM_USER MESSAGE");
                  CLogFile::logmsg(msg);
            }
            
            switch(pMsg->message)
            {
            case WM_WINDOWPOSCHANGED:
            {
// etc.

Open in new window

Avatar of flynny

ASKER

Hi jkr,

thanks for that, a step forward, if i do this kind of message then it doesn't seem to be picking up the WM_WINPOSCHANGED message (i assume it should detect this?).

However, it does pick up other messages, which i tested the code below.

Looking through these messages it was posting numerous messages including a WM_USER. so to test it i simply added a select case for the WM_USER message and posted a WM_PRIVATE_MSG, which worked!

is there any reason that the callback wouldnt pick up the WM_WINDOWPOSCHANGED?




MSG* pMsg = (PMSG)lParam;
 
      if(nCode == HC_ACTION)
      {
 
            if(pMsg->message == WM_PRIVATE_MSG)
            {
                  
                        MessageBox(0, "Received message!", "Got it!", MB_ICONEXCLAMATION | MB_OK | MB_SYSTEMMODAL);
                  msg.Format("DETECTED WM_USER MESSAGE");
                  CLogFile::logmsg(msg);
            }
            
            switch(pMsg->message)
            {
            case WM_WINDOWPOSCHANGED:
            {
break;
}
default:
{
msg.Format("OTHERMSG:%x:",pCwp->message);
CLogFile::logmsg(msg);
}

Open in new window

Hm, the above is fragmented and still uses 'pCwp' in the 'default:' branch. Do you get 'WM_WINDOWPOSCHANGING'? Also, try using

      if(nCode > 0) // *not* '== HC_ACTION'

Open in new window

Avatar of flynny

ASKER

Hi jkr,

i've got the program detecting the message now. i create a WH_CALLWNDPROC and when the window witht he relevant title is detected i hook into it with a WH_GETMESSAGE proc, posting my message onto the queue.

I then detect and check the windows, however i can now at least see all the windows as they have all been added however the text always returns null from them. I assume i'm still not waiting long enough?

So my idea was if the idenftifer field didnt ext keep reposting the message until it does. However when i repost in the WH_GETMESSAGE method its doesn't seem to be posting the message in a similar way to before.
Avatar of flynny

ASKER

sorry i knocked enter an posted without proofing that post.

so

if(!found)
::PostMessage(pCwp->hwnd,0,NULL,NULL);

in the WH_GETMESSAGE proc. As this is catching the messages for the found window i assume pCwp->hwnd woul be correct
SetWindowHook(SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)AppGetEventsHook, hinstDLL, GetWindowThreadProcessId(foundWND, NULL)));::WaitForInputIdle(GetCurrentProcess(),INFINITE);
::PostMessage(foundWND,WM_PRIVATE_MSG,NULL,NULL);
						

Open in new window

You won't receive a 'LPCWPSTRUCT' any longer, thus the pointer will not to valid data. That won't work.
Avatar of flynny

ASKER

sorry i missed the nCode>0 message, must have been typing whilst you posted!. If i replace the ==NC_ACTION to >0 then it doesn't even pick up my WM_PRIVATE_MSG

Also apologies about pasting the code i tried to format it in the code snippet window to ake it more readable for you and missed a }.

i no longer create the 'LPCWPSTRUCT' i create a 'MSG*' (sorry kept the name of the variable just changed the structure in the end).

finally, regards the WM_WINDOWPOSCHANGING its doesnt seem to be picking this up either.

At the moment i'm testing this on windows 2003 server? could this be a problem? at the moment my xp machine is down. I'll try and get this up and running again and see if running on this os do you think this could make a difference?


 
Avatar of flynny

ASKER

hi jkr,

i've finally managed to get it to work with a little bit of fudging. does this method sound ok to you (it works but i would just like to get your opinion on it)

Ok, I hook into my main program using WH_CALLWNDPROC and monitor its messages.

If i detect a WM_WINDOWPOSCHANGED i check to see if it is the window i want to hook into.

if it potentially is (ie. the title matches and the hwnd (which i store) has not already been hooked into), i add a WH_GETMESSAGE hook to this child window an dpost my private message to it. I also set a bool in global memory postedMessage to true, to prevent any further posts being sent to the window.

i pick up the private message and i try and find the identrifier if not i close my hook (unhookwindowshookex) , and set the bool to false.

as further WM_WINDOWPOSCHANGED messages are caught it will repost the WM_PRIVATE _MSG and there is such a call after all the windows have been populated, success!

can you see any pitfalls with this approach as it isn't ideal.
Sounds OK if it works for you, but it is hard to detect any pitfalls without seein gthe code. Yet  personally would have preferred a WH_GETMESSAGE, but...
Avatar of flynny

ASKER

I agree, the problem i seem to have is that it doesn't let a postmessage be made from inside the proc method if it is posted to itself?? if i post to the other window the message (or test program) the message goes across ok.

anyway i'll keep it at that for now it seems to be running ok and held up with a few stress tests.

Thanks for all you help jkr, you've been brilliant.
Avatar of flynny

ASKER

great advice, well explained and directed me quickly towards a solution thanks.