【问题标题】:C# WndProc method not receiving expected Msg typeC# WndProc 方法未收到预期的 Msg 类型
【发布时间】:2020-12-30 14:08:27
【问题描述】:

我一直在尝试使用记录在案的 COPYDATA API 与另一个软件进行通信。用户 Xanotos 非常乐于助人in this question I asked,它的发送方法已排序并且工作正常。我遇到的问题是接收 WndProc 方法似乎没有捕捉到预期的响应。这是COPYDATA API文档的链接供参考。

当前方法如下。测试表明WndProc 确实收到了消息,但不是我所期望的,即struct,具体取决于发送的消息。

声明:

 [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);

    [StructLayout(LayoutKind.Sequential)]
    public struct COPYDATASTRUCT
    {
        public IntPtr dwData;    // Any value the sender chooses.  Perhaps its main window handle?
        public int cbData;       // The count of bytes in the message.
        public IntPtr lpData;    // The address of the message.
    }
    public struct ExternalGetPositionType
    {
        public double X;
        public double Y;
        public double Z;
        public double W;
    }
    const int WM_COPYDATA = 0x004A;
    const int EXTERNAL_CD_COMMAND_RUN_ASYNC = 0x8001;
    const int EXTERNAL_CD_GET_POSITION_PCS = 0x8011;
    const int EXTERNAL_CD_GET_POSITION_MCS = 0x8012;

发送方法(这些都可以正常工作并返回 true):

public static IntPtr RunAsync(IntPtr hwnd, string str)
    {
        // We have to add a \0 terminator, so len + 1 / len + 2 for Unicode
        int len = Encoding.Default.GetByteCount(str);
        var buff = new byte[len + 1]; // len + 2 for Unicode
        Encoding.Default.GetBytes(str, 0, str.Length, buff, 0);

        IntPtr ret;

        GCHandle h = default(GCHandle);

        try
        {
            h = GCHandle.Alloc(buff, GCHandleType.Pinned);

            var cds = new COPYDATASTRUCT();
            cds.dwData = (IntPtr)EXTERNAL_CD_COMMAND_RUN_ASYNC;
            cds.lpData = h.AddrOfPinnedObject();
            cds.cbData = buff.Length;

            ret = SendMessage(hwnd, WM_COPYDATA, 0, ref cds);
        }
        finally
        {
            if (h.IsAllocated)
            {
                h.Free();
            }
        }

        return ret;
    }

    public static IntPtr GetPosition(IntPtr hwnd, bool pcs, ExternalGetPositionType position)
    {
        // We cheat here... It is much easier to pin an array than to copy around a struct
        var positions = new[]
        {
    position
};

        IntPtr ret;

        GCHandle h = default(GCHandle);

        try
        {
            h = GCHandle.Alloc(positions, GCHandleType.Pinned);

            var cds = new COPYDATASTRUCT();
            cds.dwData = pcs ? (IntPtr)EXTERNAL_CD_GET_POSITION_PCS : (IntPtr)EXTERNAL_CD_GET_POSITION_MCS;
            cds.lpData = h.AddrOfPinnedObject();
            cds.cbData = Marshal.SizeOf<ExternalGetPositionType>();

            ret = SendMessage(hwnd, WM_COPYDATA, 0, ref cds);
        }
        finally
        {
            if (h.IsAllocated)
            {
                h.Free();
            }
        }

        return ret;
    }

WndProc 方法 - 这就是事情没有按预期工作的地方。调用 GetPosition 时,if(m.Msg == WM_COPYDATA) 永远不会返回 true。

protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_COPYDATA) //this does not execute
        {
            COPYDATASTRUCT cds = Marshal.PtrToStructure<COPYDATASTRUCT>(m.LParam);
            label5.Text = "message received";
            if (cds.dwData == (IntPtr)EXTERNAL_CD_COMMAND_RUN_ASYNC)
            {
                label5.Text = "NORMAL";
                string str = Marshal.PtrToStringAnsi(cds.lpData);

                Debug.WriteLine($"EXTERNAL_CD_COMMAND_RUN_ASYNC: {str}");
                toolStripStatusLabel1.Text = $"COMMAND";
                m.Result = (IntPtr)100; // If you want to return a value
            }
            else if (cds.dwData == (IntPtr)EXTERNAL_CD_GET_POSITION_PCS) //this does not execute
            {
                label5.Text = "MSC";
                if (cds.cbData >= Marshal.SizeOf<ExternalGetPositionType>())
                {
                    var position = Marshal.PtrToStructure<ExternalGetPositionType>(cds.lpData);

                    Debug.WriteLine($"EXTERNAL_CD_GET_POSITION_PCS: X = {position.X}, Y = {position.Y}, Z = {position.Z}, W = {position.W}");
                    toolStripStatusLabel1.Text = $"External MCS = {position.X}";
                    label4.Text = position.X.ToString();

                    m.Result = (IntPtr)200;
                }
                else
                {
                    m.Result = (IntPtr)0;
                }
            }
            else if (cds.dwData == (IntPtr)EXTERNAL_CD_GET_POSITION_MCS) //this does not execute
            {
                label5.Text = "MSC"; //this does not execute
                if (cds.cbData >= Marshal.SizeOf<ExternalGetPositionType>())
                {
                    var position = Marshal.PtrToStructure<ExternalGetPositionType>(cds.lpData);

                    Debug.WriteLine($"EXTERNAL_CD_GET_POSITION_MCS: X = {position.X}, Y = {position.Y}, Z = {position.Z}, W = {position.W}");
                    toolStripStatusLabel1.Text = $"External MCS = {position.X}";
                    label4.Text = position.X.ToString(); 

                    m.Result = (IntPtr)300;
                }
                else
                {
                    m.Result = (IntPtr)0;
                }
            }

            return;
        }
        MessageBox.Show(m.Msg.ToString()); //this DOES execute
        
        base.WndProc(ref m);
    }

这两个程序都是 32 位的。

任何想法为什么我没有收到预期的Msg

【问题讨论】:

  • m.Msg 的实际值是多少?十六进制
  • @Charlieface - 由于这种情况,我无法在托管软件的 PC 上进行调试,但 MessageBox.Show 返回变量值。 11、12、799 等,即使我不发送任何信息。 @AlexF - 目标窗口确实收到消息,因为 ret 返回 1
  • WndProc 是 Win32 中的标准消息传递系统。预计会有很多不同的消息通过。你确定你所看到的与你想要的信息有关吗? Spy++ 可以破解任何软件
  • @Charlieface, @AlexF Spy++ 确认目标窗口正在接收消息。 RunAsync 在目的地按预期工作,因为我不期待回复。 GetPosition 已接收、处理,但似乎没有发送 WM_COPYDATA 响应。

标签: c# c++ process sendmessage wm-copydata


【解决方案1】:

您提供的文档不清楚,但似乎您需要将wParam设置为接收窗口句柄才能接收数据作为响应。

在第 3 页底部有一个例子:

::SendMessage(m_pWnd->GetSafeHwnd(), WM_COPYDATA,(WPARAM)this->GetSafeHwnd( ,(LPARAM)&MyCDS);

而你的代码有这个:

ret = SendMessage(hwnd, WM_COPYDATA, 0, ref cds);

你需要

ret = SendMessage(hwnd, WM_COPYDATA, this.Handle, ref cds);

必须还将wParam 上的SendMessage 重新定义为IntPtr。无论如何它都是错误的,并且可能只是因为您使用的是 32 位才有效。

【讨论】:

  • 它总是很简单 - 谢谢,这看起来现在真的会收到一条消息!非常感激。 :)
猜你喜欢
  • 2018-06-21
  • 1970-01-01
  • 2020-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-24
  • 2022-01-22
相关资源
最近更新 更多