【问题标题】:How to send Custom Message from C++ MFC to WPF C# application?如何将自定义消息从 C++ MFC 发送到 WPF C# 应用程序?
【发布时间】:2020-11-01 07:01:31
【问题描述】:

我需要将自定义消息/通知从 MFC(C++) 发送到 WPF(C#) 应用程序。 互联网上有一些关于从 C# 到 C++ 应用程序发送消息的主题。 我试图实现我的案例,然后是那些文章,但无法接收来自 WPF 的消息。

下面给出了我尝试过的所有内容:

从 MFC 应用程序中,我尝试使用 PostMessage 和 SendMessage 发送消息 但是这些都不能从 WPF 应用程序接收。

UINT deviceConnected = 0;
deviceConnected = RegisterWindowMessage(L"DEVICE_CONNECTED");
HWND dstWnd = (HWND)GetProp(GetDesktopWindow(), L"DEVICE_CONNECTED_HWND");

const char* message = "This is a custom message";
::PostMessage(dstWnd, deviceConnected, 0, (LPARAM)(LPCTSTR)message);
::SendMessage(dstWnd, deviceConnected, 0, (LPARAM)(LPCTSTR)message);

我也尝试过使用 WM_COPYDATA:

LPCTSTR lpszString = (LPCTSTR)L"This is second message";
COPYDATASTRUCT cds;
cds.dwData = 0;
cds.cbData = sizeof(TCHAR) * (_tcslen(lpszString) + 1);
cds.lpData = (PVOID)lpszString;
::PostMessage(dstWnd, WM_COPYDATA, 0, (LPARAM)(LPVOID)&cds); 
::SendMessage(dstWnd, WM_COPYDATA, 0, (LPARAM)(LPVOID)&cds);

以下是 WPF 代码示例:

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);
private UInt32 deviceAttachedEvent = 0;
const int WM_COPYDATA = 0x4A;
    
protected override void OnSourceInitialized(EventArgs e)
{
    base.OnSourceInitialized(e);
    HwndSource hwndSource = HwndSource.FromVisual(this) as HwndSource;
    if (hwndSource != null)
    {
        hwndSource.AddHook(new HwndSourceHook(WndProc));
    }
}

private IntPtr WndProc(IntPtr hwnd, int msgId, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    IntPtr result = IntPtr.Zero;

    //if the deviceAttachedEvent message id has not been registered...
    if (deviceAttachedEvent == 0)
        deviceAttachedEvent  = RegisterWindowMessage("DEVICE_CONNECTED");

    if ((UInt32)msgId == deviceAttachedEvent )
    {
        //string msg = Marshal.PtrToStringAuto(lParam);
        Console.WriteLine("Received message from MFC");
        //Console.WriteLine(msg);
    }

    if (msgId == WM_COPYDATA)
    {
        COPYDATASTRUCT cds = new COPYDATASTRUCT();
        cds = (COPYDATASTRUCT)Marshal.PtrToStructure(lParam,typeof(COPYDATASTRUCT));
        if (cds.cbData > 0)
        {
            byte[] data = new byte[cds.cbData];
            Marshal.Copy(cds.lpData, data, 0, cds.cbData);
            Encoding unicodeStr = Encoding.ASCII;
            char[] myString = unicodeStr.GetChars(data);
            string returnText = new string(myString);
            MessageBox.Show("ACK Received: " + returnText);
        }
    }
    
    return result;
}

提前致谢。

【问题讨论】:

  • 不清楚,“无法接收消息”是什么意思。编译失败了吗?它是否编译但无法链接?它是否编译和链接但在运行时失败并出现错误?它会在没有错误的情况下失败吗?消息是否真的通过了,但数据不是您所期望的?还有什么?
  • @yeasir007 如果问题仅与在 WPF 上接收消息有关,您是否已隔离?即您是否尝试过从本机 C/C++/MFC 应用程序到另一个应用程序?您还可以在这里查看替代的进程间通信方法:docs.microsoft.com/en-us/windows/win32/ipc/…
  • @IInspectable,消息已从 MFC 应用程序发送,但未从 WPF WndProg 收到此消息。
  • 您是否验证了注册的消息具有相同的(非零)值?您是否验证过,HWNDs 两边是否相同?还要确保理解,系统不会跨进程边界为范围 0 .. WM_USER 之外的消息编组指向数据。已注册的消息超出该范围。
  • 注册消息的调试值在两端是相同的。双方的 HWND 如何相同,我没有明白你的意思。你能说清楚吗?并且注册的消息值范围是一个正值,比如 10079。

标签: c# c++ wpf winapi


【解决方案1】:

我可以使用代码获取 C++ 项目发送的消息(deviceAttachedEventWM_COPYDATA)。 我使用FindWindow(NULL,L"WPFwindow name") 来获取hwnd 句柄,而不是GetProp,因此请确保您拥有正确的窗口句柄。您可以使用EnumPropsEx 来测试您是否拥有"DEVICE_CONNECTED_HWND" 的属性

对于窗口消息deviceConnected: 你在当前进程中发送一个指针,另一个进程通常没有权限直接访问这个地址。需要使用ReadProcessMemory读取该地址中的字符串数据。

另外,请注意项目的字符集。如果字符集是 UNICODE,L"***" 不需要强制转换为 LPCTSTR,如果字符集是多字节字符集,则使用 "***"。或者使用TEXT("") 宏。

另外,您发送了一个宽字节字符串L"This is second message",然后在读取时设置Encoding.ASCII,这将截断字符串,如:

所以我的示例是: C++:

#include <windows.h>
#include <iostream>
#include <tchar.h>
int main()
{
    UINT deviceConnected = 0;
    deviceConnected = RegisterWindowMessage(L"DEVICE_CONNECTED");
    HWND dstWnd = FindWindow(NULL,L"MainWindow");

    const wchar_t* message = TEXT("This is a custom message");
    ::SendMessage(dstWnd, deviceConnected, 0, (LPARAM)(LPCTSTR)message);

    LPCTSTR lpszString = TEXT("This is second message");
    COPYDATASTRUCT cds;
    cds.dwData = 0;
    cds.cbData = sizeof(TCHAR) * (_tcslen(lpszString) + 1);
    cds.lpData = (PVOID)lpszString;

    ::SendMessage(dstWnd, WM_COPYDATA, 0, (LPARAM)(LPVOID)&cds);

}

在 wpf 中:

   private IntPtr WndProc(IntPtr hwnd, int msgId, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        IntPtr result = IntPtr.Zero;

        //if the deviceAttachedEvent message id has not been registered...
        if (deviceAttachedEvent == 0)
            deviceAttachedEvent = RegisterWindowMessage("DEVICE_CONNECTED");

        if ((UInt32)msgId == deviceAttachedEvent)
        {
            //OpenProcess + PROCESS_VM_READ
            //ReadProcessMemory + lParam
            Console.WriteLine("Received message from MFC");
        }

        if (msgId == WM_COPYDATA)
        {
            COPYDATASTRUCT cds = new COPYDATASTRUCT();
            cds = (COPYDATASTRUCT)Marshal.PtrToStructure(lParam, typeof(COPYDATASTRUCT));
            if (cds.cbData > 0)
            {
                byte[] data = new byte[cds.cbData];
                Marshal.Copy(cds.lpData, data, 0, cds.cbData);
                Encoding unicodeStr = Encoding.Unicode;
                char[] myString = unicodeStr.GetChars(data);
                string returnText = new string(myString);
                MessageBox.Show("ACK Received: " + returnText);
            }
        }

        return result;
    }

【讨论】:

  • 谢谢@Drake。这个窗口处理程序是真正的问题。
猜你喜欢
  • 2012-12-07
  • 2013-04-13
  • 2012-12-30
  • 1970-01-01
  • 1970-01-01
  • 2019-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多