PostMessage的字节数组封送处理
本文关键字:处理 数组 字节数 字节 PostMessage | 更新日期: 2023-09-27 17:58:07
我正在尝试将一些C++代码移植到C#,我需要做的一件事是使用PostMessage
将字节数组传递到另一个进程的窗口。我正试图将源代码转移到另一个程序中,这样我就可以确切地看到它的预期,但与此同时,原始C++代码是这样的:
unsigned long result[5] = {0};
//Put some data in the array
unsigned int res = result[0];
Text winName = "window name";
HWND hWnd = FindWindow(winName.getConstPtr(), NULL);
BOOL result = PostMessage(hWnd, WM_COMMAND, 10, res);
这是我现在拥有的:
[DllImport("User32.dll", SetLastError = true, EntryPoint = "FindWindow")]
public static extern IntPtr FindWindow(String lpClassName, String lpWindowName);
[DllImport("User32.dll", SetLastError = true, EntryPoint = "SendMessage")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, ref COPYDATASTRUCT lParam);
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
public int dwData;
public int cbData;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=32)]
public byte[] lpData;
}
public const int WM_COPYDATA = 0x4A;
public static int sendWindowsByteMessage(IntPtr hWnd, int wParam, byte[] data)
{
int result = 0;
if (hWnd != IntPtr.Zero)
{
int len = data.Length;
COPYDATASTRUCT cds;
cds.dwData = wParam;
cds.lpData = data;
cds.cbData = len;
result = SendMessage(hWnd, WM_COPYDATA, wParam, ref cds);
}
return result;
}
//*****//
IntPtr hWnd = MessageHelper.FindWindow(null, windowName);
if (hWnd != IntPtr.Zero)
{
int result = MessageHelper.sendWindowsByteMessage(hWnd, wParam, lParam);
if (result == 0)
{
int errCode = Marshal.GetLastWin32Error();
}
}
注意,我不得不从C++中的PostMessage
切换到C#中的SendMessage
。
因此,现在发生的情况是,我得到的结果和errCode都是0,我认为这意味着消息没有得到处理——事实上,在查看其他应用程序时,我没有看到预期的响应。我已经验证了hWnd != IntPtr.Zero
,所以我认为消息被发布到了正确的窗口,但消息数据是错误的。你知道我做错了什么吗?
更新
在尝试了评论中的建议之后,我仍然没有任何运气。以下是我目前拥有的:
[DllImport("User32.dll", SetLastError = true)]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}
public struct BYTEARRDATA
{
public byte[] data;
}
public static IntPtr IntPtrAlloc<T>(T param)
{
IntPtr retval = Marshal.AllocHGlobal(Marshal.SizeOf(param));
Marshal.StructureToPtr(param, retval, false);
return (retval);
}
public static void IntPtrFree(IntPtr preAllocated)
{
//Ignores errors if preAllocated is IntPtr.Zero!
if (IntPtr.Zero != preAllocated)
{
Marshal.FreeHGlobal(preAllocated);
preAllocated = IntPtr.Zero;
}
}
BYTEARRDATA d;
d.data = data;
IntPtr buffer = IntPtrAlloc(d);
COPYDATASTRUCT cds;
cds.dwData = new IntPtr(wParam);
cds.lpData = buffer;
cds.cbData = Marshal.SizeOf(d);
IntPtr copyDataBuff = IntPtrAlloc(cds);
IntPtr r = SendMessage(hWnd, WM_COPYDATA, IntPtr.Zero, copyDataBuff);
if (r != IntPtr.Zero)
{
result = r.ToInt32();
}
IntPtrFree(copyDataBuff);
copyDataBuff = IntPtr.Zero;
IntPtrFree(buffer);
buffer = IntPtr.Zero;
这是一个64位进程,试图联系一个32位进程,所以可能有一些东西,但我不确定是什么。
问题是COPYDATASTRUCT应该包含一个指针作为最后一个成员,而您正在传递整个数组。
看看pinvoke.net上的例子:http://www.pinvoke.net/default.aspx/Structures/COPYDATASTRUCT.html
评论后的更多信息:
给定这些定义:
const int WM_COPYDATA = 0x004A;
[StructLayout(LayoutKind.Sequential)]
struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}
[DllImport("User32.dll", SetLastError = true, EntryPoint = "FindWindow")]
public static extern IntPtr FindWindow(String lpClassName, String lpWindowName);
[DllImport("User32.dll", SetLastError = true, EntryPoint = "SendMessage")]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, ref COPYDATASTRUCT lParam);
我可以创建两个.NET程序来测试WM_COPYDATA
。以下是接收器的窗口程序:
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_COPYDATA:
label3.Text = "WM_COPYDATA received!";
COPYDATASTRUCT cds = (COPYDATASTRUCT)Marshal.PtrToStructure(m.LParam, typeof(COPYDATASTRUCT));
byte[] buff = new byte[cds.cbData];
Marshal.Copy(cds.lpData, buff, 0, cds.cbData);
string msg = Encoding.ASCII.GetString(buff, 0, cds.cbData);
label4.Text = msg;
m.Result = (IntPtr)1234;
return;
}
base.WndProc(ref m);
}
使用SendMessage
:调用它的代码
Console.WriteLine("{0} bit process.", (IntPtr.Size == 4) ? "32" : "64");
Console.Write("Press ENTER to run test.");
Console.ReadLine();
IntPtr hwnd = FindWindow(null, "JimsForm");
Console.WriteLine("hwnd = {0:X}", hwnd.ToInt64());
var cds = new COPYDATASTRUCT();
byte[] buff = Encoding.ASCII.GetBytes(TestMessage);
cds.dwData = (IntPtr)42;
cds.lpData = Marshal.AllocHGlobal(buff.Length);
Marshal.Copy(buff, 0, cds.lpData, buff.Length);
cds.cbData = buff.Length;
var ret = SendMessage(hwnd, WM_COPYDATA, 0, ref cds);
Console.WriteLine("Return value is {0}", ret);
Marshal.FreeHGlobal(cds.lpData);
当发送方和接收方都是32位进程时以及当它们是64位进程时,这都可以正常工作如果两个进程的"位"不匹配,它将不起作用。
这对于32/64或64/32不起作用有几个原因。想象一下,您的64位程序想要将此消息发送到32位程序。64位程序传递的lParam
值将为8字节长。但是32位程序只能看到4字节的数据。所以该程序不知道从哪里获取数据!
即使这样,COPYDATASTRUCT
结构的大小也是不同的。在32位程序中,它包含两个指针和一个DWORD,总大小为12字节。在64位程序中,COPYDATASTRUCT
长20字节:两个指针,每个指针8字节,长度值为4字节。
相反,你也会遇到类似的问题。
我非常怀疑你是否能让WM_COPYDATA
为32/64或64/32工作。
这将适用于32位发送器到64位接收器,64位发送器到32位接收器。也可以从32工作到32,从64工作到64。您甚至不需要声明COPYDATASTRUCT。非常简单:
const int WM_COPYDATA = 0x004A;
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(String lpClassName, String lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
static IntPtr SendMessage(IntPtr hWnd, byte[] array, int startIndex, int length)
{
IntPtr ptr = Marshal.AllocHGlobal(IntPtr.Size * 3 + length);
Marshal.WriteIntPtr(ptr, 0, IntPtr.Zero);
Marshal.WriteIntPtr(ptr, IntPtr.Size, (IntPtr)length);
IntPtr dataPtr = new IntPtr(ptr.ToInt64() + IntPtr.Size * 3);
Marshal.WriteIntPtr(ptr, IntPtr.Size * 2, dataPtr);
Marshal.Copy(array, startIndex, dataPtr, length);
IntPtr result = SendMessage(hWnd, WM_COPYDATA, IntPtr.Zero, ptr);
Marshal.FreeHGlobal(ptr);
return result;
}
private void button1_Click(object sender, EventArgs e)
{
IntPtr hWnd = FindWindow(null, "Target Window Tittle");
byte[] data = System.Text.Encoding.ASCII.GetBytes("this is the sample text");
SendMessage(hWnd, data, 0, data.Length);
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_COPYDATA:
byte[] b = new Byte[Marshal.ReadInt32(m.LParam, IntPtr.Size)];
IntPtr dataPtr = Marshal.ReadIntPtr(m.LParam, IntPtr.Size * 2);
Marshal.Copy(dataPtr, b, 0, b.Length);
string str = System.Text.Encoding.ASCII.GetString(b);
MessageBox.Show(str);
// m.Result = put result here;
return;
}
base.WndProc(ref m);
}
这可能是一个32位与64位的问题吗?
尝试将COPYDATASTRUCT的dwData成员设置为IntPtr,而不是int。
请参阅此线程以了解相关问题:
http://www.vistax64.com/net-general/156538-apparent-marshalling-related-problem-x64-but-works-x86.html
参见COPYDATASTRUCT:的原始定义
http://msdn.microsoft.com/en-us/library/ms649010(VS.85).aspx
以下是x64上ULONG_PTR的含义:
http://msdn.microsoft.com/en-us/library/aa384255(VS.85).aspx
要存储64位指针值,请使用ULONG_PTR。使用32位编译器编译时,ULONG_PTR值为32位,使用64位编译器编译则为64位。
在IntPtrAlloc
函数中,SizeOf(param)
为您提供了什么?我认为这将是对数组的引用的大小,而不是数组内容的大小。因此,Windows会将.NET数组引用复制到另一个进程中,这完全没有意义。
固定数组,并使用Marshal.UnsafeAddrOfPinnedArrayElement
获得lpData
的正确值。