2017-03-02 18 views
0

有關Win32 SendMessage函數的正確簽名的信息來源有衝突。SendMessage的真正簽名是什麼,它應該如何在C#中調用?

微軟WINAPI文檔指出它的簽名是:

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

因此,我們有以下幾種類型來解決:

  • LRESULT
  • WPARAM
  • LPARAM

使用微軟的'Windows Data Types'頁面,我能夠做出一些發現。


LRESULT的類型是LONG_PTRLONG_PTR具有以下定義:

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

long在WINAPI始終是一個32位有符號的數量,根據該相同的「Windows Data Types」頁。


接下來是WPARAM,其類型爲一個UINT_PTRUINT_PTR具有以下定義:

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

int(就像long)在WINAPI始終是一個32位有符號的量。


最後,LPARAM,其類型爲另一個LONG_PTR

因此,要總結,我們有:

  • LRESULT - >LONG_PTR - >簽署32位/ 64位取決於平臺。
  • WPARAM - >UINT_PTR - >無符號的32位/ 64位取決於平臺。
  • LPARAM - >LONG_PTR - >根據平臺簽名的32位/ 64位。

就這樣我們在C#簽名(忽略的符號性)將是:

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

P-Invoke.Net的documentation on SendMessage證實了這一 - 它們有幾乎相同的簽名,除了符號性,以及它們的文檔有地說:

2)不要使用 「內部」 或 「整數」 作爲lParam的。您的代碼將在64位窗口上崩潰。只能使用IntPtr,「ref」結構或「out」結構。
3)切勿使用「bool」,「int」或「integer」作爲返回值。你的核心將在64位窗口上崩潰。只能使用IntPtr。使用bool不安全 - pInvoke無法將IntPtr編組爲布爾值。

到目前爲止一切都很有意義。

然而,兩條信息不同意這一點。

一個我自己的代碼,已經在64位和32位上運行了幾個月而沒有崩潰,使用int wparamint lparam。爲什麼不這樣墜毀?

二,在微軟自己的源代碼,System.Windows.Forms.UnsafeNativeMethods,我們看到很多很多的定義都違反了這個分析。最糟糕的,其中上線1044:

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

他們特別聲明lParam爲int,應該崩潰。


什麼是SendMessage正確的簽名?

微軟的簽名被破壞了嗎?

爲什麼沒有'破損'的簽名造成崩潰?

+2

因爲它是一個寄存器調用約定,所以你可以在64位上避開它。 IntPtr是正確的。 –

+0

'lParam'和'wParam'確實是'IntPtr'(指針大小值)。如果您在64位進程中將*指針*傳遞給某些數據,並且此指針超過32位大小 - 如果通過此無效指針引用數據,它將被截斷(如果將其聲明爲* int *)並且您的收件人發生崩潰 – RbMm

+0

@RbMm - 在這個問題的背景下,你的評論沒有意義。 – antiduh

回答

1

在64位Windows(AMD64)上,first 4 function parameters存儲在CPU寄存器中,它們不在堆棧上傳遞。這些寄存器總是在那裏,並且總是64位寬,所以對函數的實際調用總是「有效」的。但參數的內容可能無效。

  • 當參數被指定爲int,而不是IntPtr然後高32位的內容是不確定的。編譯器/封送器很可能會生成清除高位32位的mov(複製到寄存器中),但不能保證,未來的編譯器優化可能會使用該「空閒」空間來處理其他事情。

  • 在運行時,大多數情況下您都不會看到崩潰,只有在進程地址空間的前4個GiB之外分配內存時纔會發生。您可以使用MEM_TOP_DOWN標誌調用VirtualAlloc來強制執行此類分配。如果地址不適合32位,則地址將被截斷,並且響應該消息的代碼將訪問錯誤的地址,並可能導致內存崩潰或損壞或以其他方式失敗。

返回值也存儲在寄存器中,並具有相同的截斷問題。

我不知道爲什麼破碎的簽名不會導致崩潰,也許他們只發送不使用指針的消息?或者他們只是幸運?

相關問題