Недавно я обнаружил странное различие между двумя вызовами Win32 API «PostMessage» и «SendNotifyMessage» (по крайней мере, замечено в Win7 64bit SP1):
Похоже, что собственное окно верхнего уровня другого процесса не получает сообщения, передаваемые (HWND_BROADCAST) с помощью «PostMessage», в то время как оно получает сообщения, передаваемые с помощью «SendNotifyMessage» в своем WndProc.
Отправленное сообщение было зарегистрировано с помощью вызова «RegisterWindowMessage».
Даже используя Spy ++, я не вижу сообщения, прибывающего при использовании «PostMessage». Кроме того, я хочу отметить, что если я отправляю сообщение непосредственно в конкретный HWND с помощью «PostMessage», оно приходит, как и ожидалось. Таким образом, похоже, что внутренняя реализация PostMessage для Windows просто пропускает мое окно при выполнении итерации для выполнения трансляции.
Читая соответствующую документацию MSDN, я не вижу никаких заявлений об этой разнице, и мне интересно, является ли это ошибкой в PostMessage или SendNotifyMessage и могу ли я полагаться на SendNotifyMessage, чтобы продолжать показывать это поведение в будущих версиях Windows.
Так есть ли у кого-нибудь правдоподобное объяснение, почему обе функции по-разному трактуют трансляции в этой ситуации?
Кроме того, я хотел бы спросить, есть ли еще способ использовать PostMessage для широковещательной передачи в собственное окно верхнего уровня, потому что я бы предпочел опубликовать сообщение, потому что я предпочел бы не пропускать очередь сообщений (именно это SendNotifyMessage делает).
Если вам интересно, почему я хочу получить доступ к собственному окну верхнего уровня: в WPF окна скрыты от панели задач (свойство Window.ShowInTaskbar), сделав их собственными окнами верхнего уровня со скрытым окном владельца.
Заранее большое спасибо за любые идеи или комментарии по этой теме.
Вложение: здесь пример, показывающий поведение … просто создайте его и запустите его два раза … второй процесс должен заставить сообщение появиться в первом.
Вот также ссылка на полное решение, включая сборку EXE: Ссылка на полное решение VS
#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;
}
Вам может понадобиться зарегистрировать ваше сообщение, используя RegisterWindowMessage()
— см. раздел «Примечания» Статья MSDN
На страницах документации PostMessage () упоминается, что применяются ограничения уровня целостности:
Начиная с Windows Vista, отправка сообщений зависит от UIPI. Поток процесса может отправлять сообщения только в очереди сообщений потоков в процессах с меньшим или равным уровнем целостности.
Там нет упоминания о таких ограничениях SendNotifyMessage (). Поскольку вы не проверяете возвращаемое значение любого из них, вы можете столкнуться с этим, и вы не узнаете об этом.
Просто добавив это здесь для информации ..
Я смог обойти эту проблему в c #, зарегистрировав объект IMessageFilter на уровне приложения. PreFilterMessage для этого объекта получит сообщение, и я могу обработать его оттуда.
public class FooMessageFilter : IMessageFilter
{
uint UM_FOO = 0;
public event EventHandler OnFoo;
public FooMessageFilter()
{
UM_FOO = Win32.RegisterWindowMessage("UM_FOO");
}
private bool PreFilterMessage(ref Message m)
{
if(m.Msg == UM_FOO)
{
if(OnFoo != null)
OnFoo(this, new EventArgs());
return true;
}
return false;
}
}
Затем я добавил этот фильтр сообщений в контекст приложения в конструкторе формы моего верхнего уровня.
public partial class Form1 : Form
{
private fooFilter = new FooMessagFilter();
public Form1()
{
InitializeComponent();
// Register message filter
Application.AddMessageFilter(fooFilter);
// Subscribe to event
fooFilter.OnFoo += HandleFoo;
}
private HandleFoo(object o, EventArgs e)
{
MessageBox.Show("Foo!");
}
}
Оттуда это было только вопросом подключения событий в моем окне верхнего уровня к фильтру сообщений. Это было необходимо из-за необходимости придерживаться текущей архитектуры и сообщения, полученного от стороннего процесса.