PostMessage的字节数组编组

时间:2011-05-31 14:36:28

标签: c# mfc

我正在尝试将一些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;

这是一个试图联系32位进程的64位进程,因此可能存在某些内容,但我不确定是什么。

4 个答案:

答案 0 :(得分:7)

问题是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工作。

答案 1 :(得分:2)

这适用于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);
    }

答案 2 :(得分:0)

可能是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位。

答案 3 :(得分:0)

IntPtrAlloc函数中,SizeOf(param)给你的是什么?我认为这将是对数组的引用的大小,而不是数组内容的大小。因此,Windows会将.NET数组引用复制到另一个进程中,这完全没有意义。

固定数组,并使用Marshal.UnsafeAddrOfPinnedArrayElement获取lpData的正确值。