简体   繁体   中英

What is the true signature of SendMessage, and how should it be invoked in C#?

There are conflicting sources of information on the proper signature for the Win32 SendMessage function.

Microsoft's winapi documentation states that its signature is the following:

LRESULT WINAPI SendMessage(
    _In_ HWND hWnd, 
    _In_ UINT Msg, 
    _In_ WPARAM wParam, 
    _In_ LPARAM lParam );

So we have the following types to resolve:

  • LRESULT
  • WPARAM
  • LPARAM

Using Microsoft's ' Windows Data Types ' page, I was able to make some findings.


LRESULT is typed as a LONG_PTR . LONG_PTR has the following definition:

#if defined(_WIN64)
 typedef __int64 LONG_PTR; 
#else
 typedef long LONG_PTR;
#endif

long in winapi is always a 32-bit signed quantity, according to that same ' Windows Data Types ' page.


Next up is WPARAM , which is typed as a UINT_PTR . UINT_PTR has the following definition:

#if defined(_WIN64)
 typedef unsigned __int64 UINT_PTR;
#else
 typedef unsigned int UINT_PTR;
#endif

int (just like long ) in winapi is always a 32-bit signed quantity.


Lastly, LPARAM , which is typed as another LONG_PTR .

So, to summarize, we have:

  • LRESULT -> LONG_PTR -> signed 32-bit/64-bit depending on platform.
  • WPARAM -> UINT_PTR -> unsigned 32-bit/64-bit depending on platform.
  • LPARAM -> LONG_PTR -> signed 32-bit/64-bit depending on platform.

And thus our signature in C# (ignoring signedness) would be:

[DllImport( "User32.dll", CharSet = CharSet.Auto )]
static extern IntPtr SendMessage( IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam );

P-Invoke.Net's documentation on SendMessage corroborates this - they have nearly the same signature, excepting signedness, and their documentation has this to say:

2) NEVER use "int" or "integer" as lParam . Your code WILL crash on 64-bit windows. ONLY use IntPtr, a "ref" structure, or an "out" structure.
3) NEVER use "bool", "int", or "integer" as the return value. Your core WILL crash on 64-bit windows. ONLY use IntPtr. It's not safe to use bool - pInvoke cannot marshal an IntPtr to a boolean.

So far everything makes sense.

However, two pieces of information disagree with this.

One, my own code, which has run in 64-bit and 32-bit for months without crashing, has used int wparam and int lparam . Why hasn't this crashed?

Two, in Microsoft's own source code , System.Windows.Forms.UnsafeNativeMethods , we see many, many definitions violate this analysis. The worst of which is on line 1044:

[DllImport(ExternDll.User32, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, int lParam);

They specifically declare lParam as an int, which should crash.


What is the correct signature of SendMessage ?

Is Microsoft's signature broken?

Why haven't the 'broken' signatures been causing crashes?

On 64-bit Windows ( AMD64 ) the first 4 function parameters are stored in CPU registers, they are not passed on the stack. These registers are always there and are always 64-bit wide so the actual call to the function is always going to be "valid". The content of the parameters however might not be valid.

  • When a parameter is specified as int instead of IntPtr then the contents of the upper 32 bits are undefined. The compiler/marshaler is most likely going to generate a mov (copy into the register) that clears the upper 32 bits but there is no guarantee, future compiler optimizations might use that "free" space for something else.

  • At run-time you are not going to see a crash most of the time, it is only going to happen if some memory is allocated outside of the first 4 GiB in the process address-space. You can force such an allocation by calling VirtualAlloc with the MEM_TOP_DOWN flag. If the address does not fit in 32 bits then the address is going to get truncated and the code responding to the message is going to access the wrong address and is probably going to crash or corrupt memory or otherwise fail.

The return value is also stored in a register and has the same truncation issue.

I don't know why the broken signature is not causing crashes, perhaps they are only sending messages that do not use pointers? Or they are just getting lucky?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM