flynny
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_US ER+1,12345 ,NULL);
i have also tried
::PostThreadMessage(NULL,W M_USER+1,1 2345,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?
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->
i have also tried
::PostThreadMessage(NULL,W
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?
Did you try PostMessage()?
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.
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.
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?
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?
ASKER
to note i have tried the following without any luck
::PostThreadMessage(::GetW indowThrea dProcessId (pCwp->hwn d,NULL),WM _USER+1,12 345,NULL);
::PostThreadMessage(::GetW
If more than one process is involved here, 'WM_USER' messages will not be unique. You should rather use
UINT uMyMsg = RegisterWindowMessage("WM_ MyUniquePr ivateMessa ge");
UINT uMyMsg = RegisterWindowMessage("WM_
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_MS G");
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,W M_PRIVATE_ MSG,NULL,N ULL);
::PostMessage(NULL,WM_PRIV ATE_MSG,NU LL,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?
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_
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,W
::PostMessage(NULL,WM_PRIV
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?
BTW, are you sure that you want to post that to the window you are hooking?
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.
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.
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);
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.
i'm not picking up anything in the log file though still.
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.
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);
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(hookWind ow);
if(foundWND>0)
{
HWND hwndzz = FindWindow(NULL,"The title of my window");
::PostMessage(hwndzz,WM_PR IVATE_MSG, NULL,NULL) ;
//::PostMessage(foundWND,W M_PRIVATE_ MSG,NULL,N ULL);
//have also tried
//::PostMessage(NULL,WM_PR IVATE_MSG, NULL,NULL) ;
//::PostMessage(pCwp->hwnd ,WM_PRIVAT E_MSG,NULL ,NULL);
all with no luck? could it be the way i create the hook?
(the problem was i was sending the classname in FindWindow("MyClassName","
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(hookWind
if(foundWND>0)
{
HWND hwndzz = FindWindow(NULL,"The title of my window");
::PostMessage(hwndzz,WM_PR
//::PostMessage(foundWND,W
//have also tried
//::PostMessage(NULL,WM_PR
//::PostMessage(pCwp->hwnd
all with no luck? could it be the way i create the hook?
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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_CALL WNDPROC*/W H_GETMESSA GE, (HOOKPROC)AppEventsHook, hinstDLL, GetWindowThreadProcessId(G etAppWnd() , 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?
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_CALL
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?
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.
thanks again,
Matt.
What code do you have inside your hook now?
ASKER
at the moment, i have the following.
i creeate the hook as follows.
appHook = SetWindowsHookEx(/*WH_CALL WNDPROC*/ WH_GETMESSAGE, (HOOKPROC)AppEventsHook, hinstDLL, GetWindowThreadProcessId(G etAppWnd() , 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_MS G");
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(hookWind ow);
//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,W M_PRIVATE_ MSG,NULL,N ULL);
//ahve also tried
::PostMessage(foundWND,WM_ PRIVATE_MS G,NULL,NUL L);
//and
::PostThreadMessage(GetCur rentProces sId(),WM_P RIVATE_MSG ,NULL,NULL );
thanks again for your help jkr, its much appreciated.
i creeate the hook as follows.
appHook = SetWindowsHookEx(/*WH_CALL
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_
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(hookWind
//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,W
//ahve also tried
::PostMessage(foundWND,WM_
//and
::PostThreadMessage(GetCur
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.
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?
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);
}
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'
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.
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.
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
so
if(!found)
::PostMessage(pCwp->hwnd,0
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);
You won't receive a 'LPCWPSTRUCT' any longer, thus the pointer will not to valid data. That won't work.
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?
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?
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.
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...
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.
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.
ASKER
great advice, well explained and directed me quickly towards a solution thanks.