0

I am trying to send WM_CLOSE from my app to a target app to tell it to close. I am running in the VS debugger on NET 6.0.

I find the handle of the target app and send a WM_CLOSE message to its handle, but Spy++ says that the message never arrives at the target window, and so of course the target window does not close.

I know I have the right target handle because I can see it in Spy++ (I tag the target window title bar manually with the Spy++ crosshairs), and they match. Also when I get the window title from the handle, it gives the same window title visible in the title bar of the target app. And the return code from the PostMessage call is zero, so PostMessage is saying that it sent the message.

But neither PostMessage nor SendMessage sends a message that is visible in the messages for the target window that are being logged in real-time in the Spy++ rolling display.

Why is my WM_CLOSE message not: (1) visible in Spy++, and (2) not reaching the target app?

public static void
    SendCloseToHandle(IntPtr handle) {
    var result = PostMessage(handle, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
    if (result != 0) {
      FormAppendTextContent($"WM_CLOSE failed to send. {result:X}");
      return;
    }

    //SendMessage(handle, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
    var title = GetWindowTitle(handle);
    FormAppendTextContent($"Posted WM_CLOSE message to {title} '{handle:X}'");
  }
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
Kevin
  • 1,548
  • 2
  • 19
  • 34

1 Answers1

3

And the return code from the PostMessage call is zero, so PostMessage is saying that it sent the message.

That's just wrong. The PostMessage function returns zero on failure. So, assuming your code (as presented) doesn't show the error message, then the PostMessage call is failing, and WM_CLOSE isn't being sent to the target.

You need to change your error-handling to use if (result == 0) { ... and then, in that block, call the GetLastError function to retrieve the error code. If that is 5 (as I suspect), then the call is failing due to incorrect UIPI access, and you will need to address that issue as described in this Q/A: Cross-process PostMessage, UIPI restrictions and UIAccess=”true”.


Note: Calling the GetLastError WinAPI function from C# and/or .Net projects is not trivial; for an in-depth discussion of how to properly do so, see here: WinApi - GetLastError vs. Marshal.GetLastWin32Error.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • Wow. That was an *excellent* answer. You were exactly right on all counts. PostMessage returns a bool, not a uint HRESULT with S_OK=0, (which I was assuming, incorrectly). I don't remember where I copied that ```if (result !=0) ``` from; it came with the PostMessage example from somewhere. And now I understand what's going on with Spy++ and the target app too. THANK YOU. And THANK YOU again for the UIPI link. – Kevin Jun 14 '22 at 23:25
  • 1
    If you are planning to call `GetLastError` make sure your P/Invoke declaration for `PostMessage` has the [`SetLastError`](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.dllimportattribute.setlasterror) attribute set to `true`. Otherwise you'll observe spurious results from the .NET marshaler implementation. – IInspectable Jun 15 '22 at 07:43
  • 1
    In which case, don't call `GetLastError()` directly, make sure you are using `Marshal.GetLastWin32Error()`/`Marshal.GetLastPInvokeError()` instead. – Remy Lebeau Jun 15 '22 at 20:33
  • @RemyLebeau Yeah. As also pointed out by IInspectable. I'm no expert in C# or .NET programming, but the basic premises of the WinAPI still apply, and (hopefully) my answer is helpful "as is". I can (of course) add details, and maybe I should. But the answer in my last link is ... let's say ... *reasonable*. – Adrian Mole Jun 15 '22 at 22:09
  • @Remy See edit. No point in waxing lyrical about an issue that's already been fully addressed. :) – Adrian Mole Jun 15 '22 at 22:30