最近我發現兩個Win32 API調用「PostMessage」和「SendNotifyMessage」(至少在Win7 64bit SP1上注意到)之間存在一個奇怪的差異: 一個擁有的頂級窗口另一個進程似乎不接收使用「PostMessage」廣播的消息(HWND_BROADCAST),同時接收在其WndProc中使用「SendNotifyMessage」廣播的消息。使用PostMessage與SendNotifyMessage廣播到擁有的窗口
發送的消息已通過調用「RegisterWindowMessage」進行註冊。
即使使用Spy ++,使用「PostMessage」時也看不到消息。另外,我想提一下,如果我用「PostMessage」將消息直接發送到特定的HWND,它會按預期到達。所以它看起來像「PostMessage」的窗口內部實現只是在迭代執行廣播時跳過我的窗口。
閱讀各自的MSDN文檔,我看不到有關此差異的任何聲明,我想知道這是PostMessage還是SendNotifyMessage中的錯誤,以及如果我可以依賴SendNotifyMessage繼續在未來版本的Windows中顯示此行爲。
那麼有人有一個似是而非的解釋,爲什麼這兩種功能在這種情況下對待廣播有所不同?另外,我想詢問是否有任何方法仍然使用PostMessage廣播到一個擁有的頂級窗口,因爲我寧願發佈消息,因爲我寧願不跳過消息隊列(這是SendNotifyMessage的功能)。
如果您好奇我爲什麼要訪問頂級擁有窗口:在WPF中,通過使窗口擁有隱藏所有者窗口的頂級窗口,Windows隱藏在任務欄(Window.ShowInTaskbar屬性)中。
非常感謝您對本主題的任何想法或意見。
附件:這裏是一個示例,顯示行爲...簡單地構建它,並啓動它兩次...第二個過程應該在第一個過程中顯示一條消息。 這裏也是完整的解決方案的鏈接,包括構建EXE:Link to the complete VS solution
#include <windows.h>
#include <stdio.h>
#include <string>
#include <vector>
HWND hwndMain = NULL;
HWND ownerHwnd = NULL;
std::vector<std::string> theOutput;
UINT MyRegisteredMessage1 = 0;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc = NULL;
if (message == MyRegisteredMessage1 && wParam != (WPARAM) hwndMain)
{
if (lParam == (LPARAM) 1)
theOutput.push_back("Got a 'MyRegisteredMessage1' via PostMessage");
if (lParam == (LPARAM) 2)
theOutput.push_back("Got a 'MyRegisteredMessage1' via SendNotifyMessage");
InvalidateRect(hwndMain, NULL, TRUE);
}
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
for(size_t i = 0, pos = 0; i < theOutput.size(); ++i, pos += 20)
TextOutA(hdc, 0, pos, theOutput[i].c_str(), theOutput[i].size());
EndPaint (hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK WndProcHidden(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hWnd, message, wParam, lParam);
}
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
MSG msg;
BOOL bRet;
WNDCLASSA wc;
UNREFERENCED_PARAMETER(lpszCmdLine);
if (!hPrevInstance)
{
wc.style = 0;
wc.lpfnWndProc = (WNDPROC) WndProcHidden;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);;
wc.lpszMenuName = "MainMenu";
wc.lpszClassName = "MyOwnerWindowClass";
if (!RegisterClassA(&wc))
return FALSE;
wc.lpfnWndProc = (WNDPROC) WndProc;
wc.lpszClassName = "MyOwnedWindowClass";
if (!RegisterClassA(&wc))
return FALSE;
}
ownerHwnd = CreateWindowA("MyOwnerWindowClass", "OwnerWindow",
WS_OVERLAPPEDWINDOW, 0, 0, 800, 400, (HWND) NULL,
(HMENU) NULL, hInstance, (LPVOID) NULL);
hwndMain = CreateWindowA("MyOwnedWindowClass", "OwnedWindow",
WS_OVERLAPPEDWINDOW, 0, 0, 800, 400, ownerHwnd,
(HMENU) NULL, hInstance, (LPVOID) NULL);
// only show the "real" window
ShowWindow(hwndMain, nCmdShow);
UpdateWindow(hwndMain);
MyRegisteredMessage1 = RegisterWindowMessageA("MyRegisteredMessage1");
char infoText[256];
_snprintf_s(infoText, 256,
"HWND = %X, registered message code for 'MyRegisteredMessage1' = %d",
hwndMain, MyRegisteredMessage1);
theOutput.push_back(infoText);
InvalidateRect(hwndMain, NULL, TRUE);
PostMessage(HWND_BROADCAST, MyRegisteredMessage1, (WPARAM) hwndMain, (LPARAM) 1);
Sleep(1000);
SendNotifyMessageA(HWND_BROADCAST, MyRegisteredMessage1, (WPARAM) hwndMain, (LPARAM) 2);
while((bRet = ::GetMessage(&msg, NULL, 0, 0)) != 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
這是非常粗糙的。相當於用消防水帶熄滅點燃的火柴。並找出它不工作,因爲窗口未打開。只要不這樣做,通過WindowInteropHelper類獲取您想要發送到的特定窗口句柄。 –
目標窗口是否由調用線程創建? [此MSDN文章](http://msdn.microsoft.com/en-us/library/windows/desktop/ms644953(v = vs.85).aspx)說:「如果窗口是由不同的線程創建的,SendNotifyMessage將消息傳遞給窗口過程並立即返回;它不會等待窗口過程完成處理消息「 –
@Hans Passant:我不太確定我是否正確地得到了您的答案,但我並不試圖發送它在一個應用程序內但在不同應用程序之間(使用不同的技術)另外,我必須在這裏使用窗口消息,我不知道目標窗口句柄,它不僅僅是一個目標窗口。因此,讓我們假設我必須使用上述調用之一:爲什麼PostMessage在這裏不工作而SendNotifyMessage呢? – Neffl