C# SendMessage中有误导性的参数类型(…IntPtr wParam…)

C# SendMessage中有误导性的参数类型(…IntPtr wParam…),c#,c++,interop,C#,C++,Interop,在处理中描述的问题时,我偶然发现了参数wParam及其类型IntPtr for SendMessage函数 原始函数的代码类似于 var length = (int)SendMessage(hWnd, WindowMessage.WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero); var buff = new char[length + 1]; var iptr = Marshal.AllocHGlobal(buff.Length * sizeof(char

在处理中描述的问题时,我偶然发现了参数wParam及其类型IntPtr for SendMessage函数

原始函数的代码类似于

var length = (int)SendMessage(hWnd, WindowMessage.WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
var buff = new char[length + 1];
var iptr = Marshal.AllocHGlobal(buff.Length * sizeof(char));
SendMessage(hWnd, WindowMessage.WM_GETTEXT, (IntPtr)(length + 1), iptr);
Marshal.Copy(iptr, buff, 0, length + 1);
Marshal.FreeHGlobal(iptr);
我不喜欢分配和复制的部分,所以我尝试用

var length = 1 + (int)SendMessage(hWnd, WindowMessage.WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
var buffer = new char[length];
var bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
var lengthHandle = GCHandle.Alloc(length, GCHandleType.Pinned);
SendMessage(hWnd, WindowMessage.WM_GETTEXT, lengthHandle.AddrOfPinnedObject(), bufferHandle.AddrOfPinnedObject());
bufferHandle.Free();
lengthHandle.Free();
事实证明,这是行不通的,因为参数wParam不是指针,可以从其IntPtr类型得出结论

关于WM_GETTEXT消息的具体描述可以在thank you SO community上找到,这将引导我们找到以下代码

var length = 1 + (int)SendMessage(hWnd, WindowMessage.WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
var buffer = new char[length];
var bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
SendMessage(hWnd, WindowMessage.WM_GETTEXT, (IntPtr)length, bufferHandle.AddrOfPinnedObject());
bufferHandle.Free();
SendMessage函数是否有专门针对特定消息的库或签名集合


不要误解我的意思,PInvoke已经做了大量的工作,将此函数提供给managed world,我很感激,但是单一方法的多用途特性使其更难使用。

您可以从中复制它们,但请注意,通常我必须修改PInvoke定义,它们通常是32位的,所以他们经常假设sizeofIntPtr==sizeofint,这是非常错误的事情!,或者他们使用过时的ANSI API,但现在应该始终使用Unicode API。。。举例来说,在链接页面中,带有[MarshalAsUnmanagedType.LPStr]字符串lParam的SendMessage使用的是ANSI API,而带有Int32 wParam的SendMessage将在64位中断

您的代码可以更改为:

[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, StringBuilder lParam);

var length = (int)SendMessage(hWnd, WindowMessage.WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
var sb = new StringBuilder(length + 1);
SendMessage(hWnd, WindowMessage.WM_GETTEXT, (IntPtr)sb.Capacity, sb);
string str = sb.ToString();

使用pinvoke子系统支持StringBuilder来封送字符串的事实。

也不要忘记有两个SendMessage函数:SendMessageA和SendMessageW。在C/C++代码中,SeNDeMeST通常是根据Unicode宏扩展到其中的一个宏。提供的示例更接近于这个场景需要的特定签名,谢谢。我猜由于封送,wParam和IntPtr的部分将不得不保持原样。如果需要更严格的签名,那么使用包装器函数将更方便。