Why does Windows SendMessage() always return ZERO, even the message delivery is success? Is there anyway to check the message delivery failure with SendMessage() ?
EDIT
Forgot to mention that I'm using SendMessage() inside a c++ DLL
LRESULT result = ::SendMessage(hwndOtherWindow,WM_COPYDATA, NULL/*(WPARAM)this->GetSafeHwnd()*/,(LPARAM)&structCDS);
"result" is always zero :(, but message delivers to other window successfully
EDIT
BOOL CDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
return /*CDialog::OnCopyData(pWnd, pCopyDataStruct)*/ true; //true is the trick
}
A zero return from SendMessage for WM_COPYDATA means the target application didn't process the message (FALSE = 0).
The message might deliver successfully, but if the target application doesn't handle the message properly (ie, wrong return value, or passing it to the default window procedure) then your SendMessage call will appear to come back with the wrong result.
It might be worth your time to see what the target application's handling of the WM_COPYDATA message is, if possible.
Related
I create a systray icon with:
BOOL TrayMessage(HWND hWnd, DWORD dwMessage)
{
NOTIFYICONDATA nid;
nid.cbSize = sizeof(nid);
nid.hWnd = hWnd;
nid.uID = 1;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
nid.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_MYAPP));
lstrcpy(nid.szTip, L"MyApp");
nid.uCallbackMessage = WM_NOTIFYICON;
return Shell_NotifyIcon(dwMessage, &nid);
}
when the app starts / the window is created:
case WM_CREATE:
if (!TrayMessage(hWnd, NIM_ADD))
MessageBox(hMainWnd, L"Tray error.", 0, 0);
This error messagebox:
never happens when I launch the .exe normally.
only happens after a User Logout / User re-login, once every 5 launches on average (my app is automatically launched on every session startup with a TaskSchedular task)
Of course when the error happens, the icon isn't displayed in taskbar.
What could be the reason?
The systray system is not ready yet (very short after a user logout / login again) ?
The taskbar itself is not ready yet?
Should I move the creation to somewhere outside of WM_CREATE?
Edit: after #RbMm's comment, I tried this:
case WM_CREATE:
TrayMessage(hWnd, NIM_ADD);
// I removed MessageBox(...) from here
uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
...
break;
default:
if (message == uTaskbarRestart)
{
TrayMessage(hWnd, NIM_ADD);
MessageBox(hMainWnd, L"TaskbarRestart", 0, 0);
}
Result of this test: the cases for which the tray icon fails to be displayed are exactly the cases when the MessageBox TaskbarRestart is not shown, i.e. when TaskbarRestart event never comes to the message loop... That's strange...
Note: this only happens after a user logout / re-login.
The current version of MSDN Shell_NotifyIcon doesn't show it anymore (such a shame!), but fortunately, there's an archived version here that gives two interesting informations:
1.
Returns TRUE if successful, or FALSE otherwise. [...]
You can call GetLastError for more specific information about a failure case. The most common cause of failure is that the taskbar window doesn't exist or is unresponsive. GetLastError in that case returns E_FILE_NOT_FOUND.
2.
Handling Shell_NotifyIcon failure
Shell_NotifyIcon will often fail when called during Windows startup (for instance, if your application is listed in HKLM\Software\Microsoft\Windows\CurrentVersion\Run. This appears to be because the system is busy starting applications. The failure is more common on low-spec computers or computers with some brands of antivirus software installed, which seem to be very intensive at startup.
Unfortunately, you cannot rely on the error code returned by GetLastError. When Shell_NotifyIcon returns false, some of the common errors returned by GetLastError are:
ERROR_FILE_NOT_FOUND (2)
ERROR_TIMEOUT (1460)
ERROR_SUCCESS (0)
The most appropriate response to any error returned by Shell_NotifyIcon is to sleep for a period of time and retry.
An explanation of why the error code may differ has been made by Paul Baker, paraphrased from http://groups.google.com/group/microsoft.public.platformsdk.shell/msg/59235b293cbf5dfa and http://groups.google.com/group/microsoft.public.platformsdk.shell/msg/73973287f15c03fc:
Shell_NotifyIcon actually calls SetLastError(0) initially. After that, basically it uses FindWindow to find the tray notification window. If this fails, it will typically return ERROR_FILE_NOT_FOUND. Otherwise it sends a WM_COPYDATA message to the tray notification window, using SendMessageTimeout with a timeout of only 4 seconds. If that message returns zero, then Shell_NotifyIcon will fail with GetLastError returning zero.
Solution:
case WM_CREATE:
...
if (!TrayMessage(hWnd, NIM_ADD))
SetTimer(hWnd, IDT_TIMER1, 4000, (TIMERPROC) NULL);
break;
case WM_TIMER:
TrayMessage(hWnd, NIM_ADD);
KillTimer(IDT_TIMER1);
break;
I am working on writing a simple game engine and I am having trouble handling Windows console events; specifically, I cannot figure out how to pass custom data to the callback handler.
I first call this code to specify my callback function:
SetConsoleCtrlHandler((PHANDLER_ROUTINE)WindowsSystemManager::ConsoleControlHandler, true);
My static-member callback function is defined as:
bool WINAPI WindowsSystemManager::ConsoleControlHandler(DWORD controlType){
if(controlType == CTRL_CLOSE_EVENT){
MessageBox(NULL, L"Close Event Captured", L"Close Event Captured", NULL);
}
return true;
}
Everything works fine - when I click on the close button in the console, this MessageBox pops up. Only problem is, I need to call code that flushes a logging buffer to a log file on this type of shutdown (as well as other clean-up), and the Logger instance is a member in my WindowsSystemManager.
I have dealt with a similar problem of passing custom data to window handles by using SetWindowLongPtr and GetWindowLongPtr successfully, but I can't find any information on how to do this type of thing with console control handlers. Any thoughts?
EDIT: I got this functionality working based on MSalters' suggestions. The final code for the console control handler is here:
bool WINAPI WindowsSystemManager::ConsoleControlHandler(DWORD controlType){
BerserkEngine* engine = (BerserkEngine*)GetWindowLongPtr(GetConsoleWindow(), GWLP_USERDATA);
if(controlType == CTRL_CLOSE_EVENT){
engine->~BerserkEngine();
PostQuitMessage(0);
}
return true;
}
Where I set this custom data pointer in the WindowsSystemManager constructor:
SetWindowLongPtr(GetConsoleWindow(), GWL_USERDATA, (LONG_PTR)this->engine);
I'm not sure why you'd need this. You can have multiple windows, but only one console.
However, GetConsoleWindow will give you the console HWND, on which you might call SetWindowLongPtr. Not very clean (you're not supposed to do this on windows that you don't manage), but it might just work.
I'm trying to get a mouse click event in Qt, this mouse click must be global, and must be received even if the mouse isn't over my app window, I saw in these links:
Receive WM_COPYDATA messages in a Qt app
http://developer.qt.nokia.com/forums/viewthread/8103
That I can get a message from Windows reimplementing the WinEvent. I have tried this and when debuging this event is never raised. Have I missed something?
here's a sample of my code:
bool WindowsUtil::winEvent( MSG * message, long * result ) {
if (message->message == WM_LBUTTONDOWN) {//never got here at all
*result = 0;
return true;
}
// give the event to qt
return false;
}
Finally got it.
I used SetWindowsHookEx on the initialization with WH_MOUSE_LL as a paramenter for low level messages.
So, at the end, no Qt code was needed for getting mouse events.
I have setup a hook on WM_SETTEXT message using WH_CALLWNDPROC.
In hook procedure
CWPSTRUCT* info = (CWPSTRUCT*) lParam;
wchar_t *wsz = NULL;
switch(info->message)
{
case WM_SETTEXT:
wsz = (wchar_t *) info->lParam;
//info->lParam = (LPARAM) L"Hello";
//SendMessage(info->hWnd,WM_SETTEXT,0,(LPARAM)L"HEllo");
//SetWindowText(info->hWnd,L"Hello");
break;
}
Is it possible to change the string as done above in the code.
I tried by using APIs like
SendMessage(info->hWnd,WM_SETTEXT,0,(LPARAM)L"HEllo");
SetWindowText(info->hWnd,L"Hello");
But none of them working.Idea here is to hook WM_SETTEXT message and change the string before it reached destination window.
No, the WH_CALLWNDPROC doesn't allow you to modify messages, the documentation for CallWndProc directly states this.
The WH_GETMESSAGE does allow you to modify the message. See the documentation for GetMsgProc. However, this probably won't work for what you want since it only messages that are retrieved with GetMessage() or PeekMessage() and send messages call the WndProc directly rather than using the message queue.
The way to do what you want is to use the WH_CBT hook and listed for HCBT_CREATEWND events. Then subclass the window as it is created and handle the WM_SETTEXT message.
My application (the bootstrap application for an installer that I'm working on needs to launch some other applications (my installer and third party installers for my installer's prerequisites) and wait for them to complete. In order to allow the GUI to do screen updates while waiting for an app to complete, I put a message pump in the wait loop using the 'MFC-compatible' example in the Visual Studio documentation on idle loop processing as a guideline. My code (which is in a member function of a CWinApp-derived class) is as follows:
if (::CreateProcess(lpAppName, szCmdLineBuffer, NULL, NULL, TRUE, 0, NULL, NULL,
&StartupInfo, &ProcessInfo))
{
::GetExitCodeProcess(ProcessInfo.hProcess, &dwExitCode);
if (bWait)
while (dwExitCode == STILL_ACTIVE)
{
// In order to allow updates of the GUI to happen while we're waiting for
// the application to finish, we must run a mini message pump here to
// allow messages to go through and get processed. This message pump
// performs much like MFC's main message pump found in CWinThread::Run().
MSG msg;
while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
{
if (!PumpMessage())
{
// a termination message (e.g. WM_DESTROY)
// was processed, so we need to stop waiting
dwExitCode = ERROR_CANT_WAIT;
::PostQuitMessage(0);
break;
}
}
// let MFC do its idle processing
LONG nIdle = 0;
while (OnIdle(nIdle++))
;
if (dwExitCode == STILL_ACTIVE) // was a termination message processed?
{
// no; wait for .1 second to see if the application is finished
::WaitForSingleObject(ProcessInfo.hProcess, 100);
::GetExitCodeProcess(ProcessInfo.hProcess, &dwExitCode);
}
}
::CloseHandle(ProcessInfo.hProcess);
::CloseHandle(ProcessInfo.hThread);
}
else
dwExitCode = ::GetLastError();
The problem that I'm having is that, at some point, this message pump seems to free up window and menu handles on the window that I have open at the time this code is run. I did a walk through in the debugger, and at no time did it ever get into the body of the if (!PumpMessage()) statement, so I don't know what's going on here to cause the window and menu handles to go south. If I don't have the message pump, everything works fine, except that the GUI can't update itself while the wait loop is running.
Does anyone have any ideas as to how to make this work? Alternatively, I'd like to launch a worker thread to launch the second app if bWait is TRUE, but I've never done anything with threads before, so I'll need some advice on how to do it without introducing synchronization issues, etc. (Code examples would be greatly appreciated in either case.)
I've also posted this question on the Microsoft forums, and thanks to the help of one Doug Harris at Microsoft, I found out my problem with my HWND and HMENU values was, indeed due to stale CWwnd* and CMenu* pointers (obtained using GetMenu() and GetDialogItem() calls. Getting the pointers again after launching the second app solved that problem. Also, he pointed me to a web site* that showed a better way of doing my loop using MsgWaitForMultipleObjects() to control it that doesn't involve the busy work of waiting a set amount of time and polling the process for an exit code.
My loop now looks like this:
if (bWait)
{
// In order to allow updates of the GUI to happen while we're
// waiting for the application to finish, we must run a message
// pump here to allow messages to go through and get processed.
LONG nIdleCount = 0;
for (;;)
{
MSG msg;
if (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
PumpMessage();
else //if (!OnIdle(nIdleCount++))
{
nIdleCount = 0;
if (!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
{
DWORD nRes = ::MsgWaitForMultipleObjects(1, &ProcessInfo.hProcess,
FALSE, INFINITE, QS_ALLEVENTS);
if (nRes == WAIT_OBJECT_0)
break;
}
}
}
}
::GetExitCodeProcess(ProcessInfo.hProcess, &dwExitCode);
*That Web site, if you're curious, is: http://members.cox.net/doug_web/threads.htm
I think your problem is in WaitForSingleObject
Looking in MSDN you see this
Use caution when calling the wait functions and code that directly or indirectly creates windows. If a thread creates any windows, it must process messages. Message broadcasts are sent to all windows in the system. A thread that uses a wait function with no time-out interval may cause the system to become deadlocked. Two examples of code that indirectly creates windows are DDE and the CoInitialize function. Therefore, if you have a thread that creates windows, use MsgWaitForMultipleObjects or MsgWaitForMultipleObjectsEx, rather than WaitForSingleObject.
In my code in the message pump use use MsgWaitForMultipleObjects (doc).
With a call this call.
MsgWaitForMultipleObjects(1, &ProcessInfo.hProcess, FALSE, 100, QS_ALLEVENTS);
This should stop your problem with the resources dissapearing.
When you say that window and menu handles seem to be being freed, do you mean that you've got actual HWND and HMENU values that no longer seem to work, or have you got MFC CWnd* and CMenu* variables that fail?
If the latter, the problem is most likely that you're getting the CWnd* pointers by calling CWnd::FromHandle() (or CMenu::FromHandle()) somewhere (or calling something that calls them), and OnIdle() is discarding them.
The underlying reason is that MFC maintains a map from window (or menu, etc.) handles to CWnd* objects in the system. When CWnd::FromHandle() is called, it looks for a match in the map: if one is found, it's returned. If not, a new, temporary CWnd is created, added to the map, and returned. The idea behind OnIdle() is that when it's called all message processing is done, so OnIdle() discards any of these temporary CWnd objects that still exist. That's why the CWnd::FromHandle() documentation warns that the returned pointer may be temporary.
The "correct" solution to this is to not hang onto the CWnd* pointers returned from CWnd::FromHandle(). Given the simplicity of your application, it might be easier to just remove the call OnIdle(): this shouldn't have any negative effects on an installer.
Of course, this is all something of a guess, but it sounds plausible...
There is a Windows function called DisableProcessWindowsGhosting (see http://msdn.microsoft.com/en-us/library/ms648415(v=vs.85).aspx) that prevents Windows from 'ghosting' your window, and continue updating the window (your animation).