为何C++的WindowProc函数无法接收C#发送的SendMessage消息?
解决C#向MFC程序发送WM_COPYDATA无响应的问题
我来帮你排查一下这个问题,主要是两个核心原因导致MFC端的WM_COPYDATA分支没反应,咱们一步步来修复:
问题根源
- WM_COPYDATA参数传递完全错误:这个消息要求
lParam必须指向一个标准的COPYDATASTRUCT结构体,你现在传的是普通int变量,MFC根本认不出这是WM_COPYDATA的有效数据,自然不会进入分支。 - 窗口句柄可能没拿到正确的对话框句柄:
Process.MainWindowHandle有时候会因为程序启动顺序的问题,拿到的不是MFC对话框的实际句柄,而是程序的其他窗口(比如启动闪屏)。
第一步:修正C#端的消息发送逻辑
首先得在C#里正确定义COPYDATASTRUCT,然后修改SendMessage的调用方式,确保传递的是符合要求的结构体:
public partial class MainWindow : Window { public const string strFilePath = "C:/Users/gandis/Desktop/Project/ExeC/Release/ExeC.exe"; public const Int32 WM_COPYDATA = 0x004A; // 和C++端完全匹配的COPYDATASTRUCT结构体 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct COPYDATASTRUCT { public IntPtr dwData; // 自定义标识,用来区分不同消息类型 public int cbData; // 数据的字节数 [MarshalAs(UnmanagedType.LPWStr)] public string lpData; // 要传递的字符串数据(如果传数值可以改成对应类型) } // 修正SendMessage的签名,lParam必须是COPYDATASTRUCT的引用 [DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 message, IntPtr wParam, ref COPYDATASTRUCT lParam); public MainWindow() { InitializeComponent(); } private void btn_Start_Click(object sender, RoutedEventArgs e) { IntPtr hWnd = GetHandle(strFilePath); if (hWnd != IntPtr.Zero) { // 构造要发送的数据结构体 COPYDATASTRUCT cds = new COPYDATASTRUCT(); cds.dwData = (IntPtr)1; // 自定义标识,和C++端对应 string sendContent = "来自C#的测试消息"; // Unicode字符串的字节数要算上终止符,所以是(长度+1)*2 cds.cbData = (sendContent.Length + 1) * 2; cds.lpData = sendContent; // 发送WM_COPYDATA消息 SendMessage(hWnd, WM_COPYDATA, IntPtr.Zero, ref cds); } } private IntPtr GetHandle(string strFilePath) { Process proc = GetProcess(strFilePath); // 给程序一点时间完成窗口初始化,避免拿到空句柄 System.Threading.Thread.Sleep(500); IntPtr hWnd = proc.MainWindowHandle; // 如果MainWindowHandle为空,尝试通过窗口标题查找(替换成你的MFC对话框标题) if (hWnd == IntPtr.Zero) { hWnd = FindWindow(null, "ExeC"); } return hWnd; } private Process GetProcess(string strFilePath) { Process proc = new Process(); proc.StartInfo.FileName = strFilePath; proc.Start(); // 设置等待超时,防止无限卡住 proc.WaitForInputIdle(1000); return proc; } // 补充FindWindow的DllImport,用来根据标题找窗口 [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); }
第二步:修正MFC端的消息处理逻辑
你的MFC代码已经有WM_COPYDATA的分支,但需要正确解析结构体,并且必须返回TRUE表示消息已处理,否则系统会认为消息没被接收:
LRESULT CExeCDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_COPYDATA: { // 把lParam转换成COPYDATASTRUCT指针 COPYDATASTRUCT* pCDS = reinterpret_cast<COPYDATASTRUCT*>(lParam); if (pCDS != nullptr) { // 匹配C#端的自定义标识(这里是1) if (pCDS->dwData == reinterpret_cast<DWORD_PTR>(1)) { // 解析字符串数据 CString recvMsg(reinterpret_cast<LPCTSTR>(pCDS->lpData)); AfxMessageBox(_T("收到C#消息:") + recvMsg); } // 如果是传递数值,比如int,可以这样解析: // int recvValue = *reinterpret_cast<int*>(pCDS->lpData); // AfxMessageBox(_T("收到数值:") + CString::Format(_T("%d"), recvValue)); } // 必须返回TRUE,告诉系统消息已处理 return TRUE; } break; default: break; } return CDialog::WindowProc(message, wParam, lParam); }
额外注意事项
- 字符集统一:确保MFC项目设置为使用Unicode字符集(项目属性 -> 常规 -> 字符集),和C#端保持一致,否则字符串会乱码或者解析失败。
- 窗口句柄准确性:如果
MainWindowHandle还是拿不到正确的句柄,可以用EnumWindows枚举所有窗口,通过进程ID匹配找到对应的对话框句柄。 - 权限一致:两个程序要么都以普通用户身份运行,要么都以管理员身份运行,UAC权限隔离会导致跨权限的消息发送失败。
内容的提问来源于stack exchange,提问作者gandis
相关产品推荐
相关产品推荐

