3

Recently I came about a strange difference between the two Win32 API calls "PostMessage" and "SendNotifyMessage" (at least noticed on Win7 64bit SP1): An owned top-level window of another process seems not to receive messages broadcasted (HWND_BROADCAST) with "PostMessage" while it receives messages broadcasted with "SendNotifyMessage" in its WndProc.

The sent message have been registered with the help of a call to "RegisterWindowMessage".

Even using Spy++, I cannot see the message arriving when using "PostMessage". In addition, I want to mention, that if I send the message directly to the specific HWND with "PostMessage", it arrives as expected. So it looks like the windows-internal implementation of "PostMessage" just skips my window when iterating for execution of the broadcast.

Reading the respective MSDN documentation, I cannot see any statement about this difference and I am wondering if this is a bug in PostMessage or in SendNotifyMessage and if I can rely on SendNotifyMessage to continue to show this behavior in future versions of Windows.

So does someone have a plausible explanation why the both functions treat the broadcasts differently in this situation?

In addition, I would want to ask if there is any way to still use PostMessage to broadcast to an owned top-level window, because I would prefer to post the message because I would prefer to not skip the message queue (which is what SendNotifyMessage does).

In case you are curious why I want to reach a top-level owned window: in WPF, windows are hidden from the taskbar (Window.ShowInTaskbar property) by making them owned top-level windows with a hidden owner window.

Thanks a lot in advance for any ideas or comments on this topic.

Attachment: here a sample showing the behavior ... simply build it, and start it two times ... the second process should make a message show up in the first one. Here is also a link to the complete solution including a build 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; 
} 
Neffl
  • 39
  • 1
  • 3
  • 1
    This is incredibly crude. Equivalent to extinguishing a lit match with a fire hose. And finding out it doesn't work because the window isn't opened. Just don't do this, obtain the specific window handle you want to send to with the WindowInteropHelper class. – Hans Passant Apr 13 '13 at 11:26
  • Is the destination window created by the calling thread? [this MSDN article](http://msdn.microsoft.com/en-us/library/windows/desktop/ms644953(v=vs.85).aspx) says "If the window was created by a different thread, SendNotifyMessage passes the message to the window procedure and returns immediately; it does not wait for the window procedure to finish processing the message" – Edward Clements Apr 13 '13 at 11:31
  • @Hans Passant: I am not quite sure if I got your answer correctly, but I am not trying to send it inside one application but between different applications (with different technology). In addition, I am bound to use window messages here and I don't know the target window handle and it is not only one target window. So let's assume that I have to use one of the mentioned calls: why does PostMessage not work here while SendNotifyMessage does? – Neffl Apr 13 '13 at 12:09
  • @Edward Clements: sorry ... I forgot to mention that: the whole story is about two different processes, so yes, two different threads. – Neffl Apr 13 '13 at 12:14
  • 1
    This is a good example of a bad XY question. You are doing something very unreasonable, fire-hosing all the windows and hoping the reach the right one, and then are mystified that Windows treats your attempt in an unreasonable way. Maybe some Microsoft employee will dig through the hundreds of thousands of lines of code involved with this and give you an answer. But that answer will be completely useless to you, it doesn't solve your problem. Don't count on anybody from Microsoft doing this for you. The proper question of course is to how to stop doing something unreasonable. – Hans Passant Apr 13 '13 at 12:43
  • message sent by SendNotifyMessage has higher priority than the message send through PostMessage. Does your window get a lot of messages? – Sheng Jiang 蒋晟 Apr 13 '13 at 13:27
  • @Sheng Jiang: thanks a lot for your reply. Even in a very simply example with no special traffic, that message does not show up using "PostMessage". I will put together a simple sample and post it here later on. – Neffl Apr 13 '13 at 16:12
  • Whilst what you report is accurate, the advice Hans gave you is correct. Don't spam the system with `HWND_BROADCAST`. Just deliver to the desired target. – David Heffernan Apr 13 '13 at 17:04
  • 2
    @Hans Passant: thanks a lot for your opinion. I see your point and I agree that broadcasts are often not so nice. In addition, I am sorry that in your eyes, I posted a "bad XY question". However, the behavior is not documented and in my opinion, it is a valid question to ask if someone maybe can give me a good reason for the observed behavior that I did not think of so far. If there was a good, understandable reason, I would feel a bit safer regarding the usage of "SendNotifyMessage". – Neffl Apr 13 '13 at 17:07

3 Answers3

1

You may need to register your message using RegisterWindowMessage() -- see the Remarks section of this MSDN article

Edward Clements
  • 5,040
  • 2
  • 21
  • 27
  • I registered the messages sent in both cases, so sorry ... this seems not to be the reason ... – Neffl Apr 13 '13 at 11:20
1

Just adding this here for info..

I was able to get around this issue in c# by registering an IMessageFilter object on the Application level. PreFilterMessage on this object will receive the message and I can handle it from there.

public class FooMessageFilter : IMessageFilter
{
    uint UM_FOO = 0;
    public event EventHandler OnFoo;
    
    public FooMessageFilter()
    {
        UM_FOO = Win32.RegisterWindowMessage("UM_FOO");
    }

    public bool PreFilterMessage(ref Message m)
    {
        if(m.Msg == UM_FOO)
        {
            if(OnFoo != null)
                OnFoo(this, new EventArgs());
                
            return true;
        }

        return false;
    }
}

I then added this message filter to the Application context in my owned top-level form's constructor.

public partial class Form1 : Form
{
    private FooMessageFilter fooFilter = new FooMessageFilter();
    public Form1()
    {
        InitializeComponent();
        
        // Register message filter
        Application.AddMessageFilter(fooFilter);
        
        // Subscribe to event
        fooFilter.OnFoo += HandleFoo;
    }
    
    private void HandleFoo(object o, EventArgs e)
    {
        MessageBox.Show("Foo!");
    }
}

From there it was just a matter of hooking up events in my top-level window to the message filter. This was necessary because of the need to adhere to current architecture, and the message originating from a third party process.

Daniel Marschall
  • 3,739
  • 2
  • 28
  • 67
GCD
  • 384
  • 3
  • 14
0

The documentation page(s) for PostMessage() mention that integrity level restrictions apply:

Starting with Windows Vista, message posting is subject to UIPI. The thread of a process can post messages only to message queues of threads in processes of lesser or equal integrity level.

There is no mention of such restrictions on SendNotifyMessage(). Since you don't check the return value of either, you could be running into that, and you wouldn't know it.