PostMessage的字节数组封送

问题描述

| 我正在尝试将一些C ++代码移植到C#,而我需要做的一件事情是使用“ 0”将字节数组传递给另一个进程的窗口。我正在尝试将源代码获取到另一个程序,以便我可以确切地看到它的期望,但是与此同时,这是原始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\",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,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,lParam);
    if (result == 0)
    {
        int errCode = Marshal.GetLastWin32Error();
    }
}
请注意,我不得不从在C ++中使用ѭ0to切换到在C#中使用
SendMessage
。 所以现在发生的是,我将result和errCode都设为0,我认为这意味着消息未得到处理-实际上,在查看其他应用程序时,我没有看到预期的响应。我已经验证了“ѭ5”,因此我认为邮件已发布到正确的窗口,但是邮件数据错误。有什么想法我做错了吗? 更新资料 尝试评论中的建议后,我仍然没有运气。这是我目前所拥有的:
[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,IntPtr.Zero,copyDataBuff);
if (r != IntPtr.Zero)
{
    result = r.ToInt32();
}

IntPtrFree(copyDataBuff);
copyDataBuff = IntPtr.Zero;
IntPtrFree(buffer);
buffer = IntPtr.Zero;
这是一个尝试与32位进程联系的64位进程,因此可能存在某些问题,但是我不确定。     

解决方法

        问题在于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\",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,cds.cbData);
            string msg = Encoding.ASCII.GetString(buff,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,cds.lpData,buff.Length);
cds.cbData = buff.Length;
var ret = SendMessage(hwnd,WM_COPYDATA,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个字节。这样,该程序将不知道从何处获取数据! 即使可行,,13结构的大小也有所不同。在32位程序中,它包含两个指针和一个DWORD,总大小为12个字节。在64位程序中,“ 13”长20个字节:两个指针(每个指针8个字节)和4个字节的长度值。 您也有其他类似的问题。 我严重怀疑您是否会得到8英镑才能在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,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,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,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,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,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/zh-CN/library/ms649010(VS.85).aspx 这是x64上ULONG_PTR的含义: http://msdn.microsoft.com/zh-CN/library/aa384255(VS.85).aspx 要存储64位指针值,请使用ULONG_PTR。使用32位编译器编译时,ULONG_PTR值为32位;使用64位编译器编译时,ULONG_PTR值为64位。     ,        在您的
IntPtrAlloc
函数中,
SizeOf(param)
给您什么?我认为这将是对数组的引用的大小,而不是数组内容的大小。因此,Windows会将.NET数组引用复制到另一个进程中,这完全没有意义。 固定阵列,并使用
Marshal.UnsafeAddrOfPinnedArrayElement
获得
lpData
的正确值。