.NET与MFC应用程序间通信

在现代软件开发中,经常需要将现有的应用程序与新技术或框架集成。本文将介绍如何在一个现有的MFC文件查看器应用程序中集成.NETFramework的功能。特别是,将使用.NET的FileSystemWatcher类来监控文件系统的变动,并在新文件创建时自动显示。由于MFC不能直接访问这个功能,有两种选择:一是在非托管的Visual C++中实现自己的FileSystemWatcher类;二是在.NET和MFC之间提供某种托管机制或链接。

实现FileSystemWatcher

第一种选择虽然有趣,但耗时较长,因此选择了第二种方案。目标是.NET应用程序在检测到新文件时,能够通知MFC应用程序并自动显示。为了实现这一点,需要在运行的应用程序之间建立一个简单的进程间通信机制。选择了使用WM_COPYDATA消息来传递文件名给MFC应用程序。当MFC应用程序接收到COPYDATA消息时,它会获取文件路径并调用自己的显示函数。

.NET应用程序

.NET应用程序中,首先需要获取目标进程的句柄。通过Init函数启动MFC应用程序作为子进程,并保存主窗口句柄,这是传输数据所需的。这种方法比传统的“查找进程”方法简单得多,它使用Win32函数FindWindow和EnumWindows。此外,它确保了目标应用程序的正确实例句柄。

private void Init(string processName, string cmdParams) { // 启动MFC应用程序作为.NET应用程序中的进程 Process mfcApp = new Process(); mfcApp.StartInfo.FileName = processName; mfcApp.StartInfo.Arguments = cmdParams; // 运行MFC应用程序 mfcApp.Start(); // 下面这行代码非常重要,确保获取 // MFC应用程序的主窗口句柄 if (mfcApp.WaitForInputIdle(5000)) { destMainHandle = mfcApp.MainWindowHandle; } }

为了使用COPY_DATA消息机制向另一个进程发送数据,必须使用以下Win32结构和函数:

public struct COPYDATASTRUCT { public IntPtr dwData; public int cbData; public IntPtr lpData; } [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam); const int WM_COPYDATA = 0x4A;

发送数据到目标进程

下面的函数从.NET应用程序向MFC应用程序发送数据。由于MFC程序不是为Unicode兼容而构建的,因此在发送之前必须将字符串转换为ASCII格式。AStringToCoTaskMemAnsi将.NET程序中的Unicode字符串转换为MFC所需的ASCII格式。

接下来需要做的是分配COPYDATASTRUCT结构的内存。由于数据被发送到非托管世界,可以使用两种Marshal函数之一来分配非托管内存块,AllocCoTaskMem或AllocHGlobal。然后使用System.Runtime.InteropServices来执行SendMessage到MFC应用程序。

public static void SendMessageWithData(IntPtr destHandle, string str, IntPtr srcHandle) { COPYDATASTRUCT cds; cds.dwData = srcHandle; str = str + '\0'; cds.cbData = str.Length + 1; cds.lpData = Marshal.AllocCoTaskMem(str.Length); cds.lpData = Marshal.StringToCoTaskMemAnsi(str); IntPtr iPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(cds)); Marshal.StructureToPtr(cds, iPtr, true); // 发送到MFC应用程序 SendMessage(destHandle, WM_COPYDATA, IntPtr.Zero, iPtr); // 不要忘记释放分配的内存 Marshal.FreeCoTaskMem(cds.lpData); Marshal.FreeCoTaskMem(iPtr); }

MFC应用程序在MainFrame类中创建一个OnCopyData消息处理程序。从COPYDATASRUCT块中轻松地将发送的文件路径字符串转换出来,并可以由MFC应用程序使用。MFC程序接收到来自C#应用程序的消息后,会向调用者发送确认消息。

// CMainFrame消息处理程序 BOOL CMainFrame::OnCopyData(CWnd* pWnd, COPYDATASTRUCT *pCopyDataStruct) { char szbuf[256]; ::ZeroMemory(szbuf, 256); // 从CopyData结构中获取文件路径 strncpy(szbuf, (char*)pCopyDataStruct->lpData, pCopyDataStruct->cbData); CString filePath = CString(szbuf); // 将文件导入MFC应用程序 CMFCTestProgramDoc* pDocument = (CMFCTestProgramDoc *) GetActiveView()->GetDocument(); pDocument->FileImportAutomatic(filePath); // 向调用者发送确认消息 ::SendMessage((HWND)pCopyDataStruct->dwData, WM_COPYDATA, 0, 0); return TRUE; }

接收确认消息

为了从MFC应用程序接收确认消息,必须覆盖C#应用程序的WndProc处理程序。下面的代码展示了如何从消息变量中解码COPYDATASTRUCT结构,以及如何将ASCII字符串转换为UNICODE格式。

protected override void WndProc(ref Message m) { switch (m.Msg) { case WM_COPYDATA: if (m.Msg == WM_COPYDATA) { COPYDATASTRUCT cds = new COPYDATASTRUCT(); cds = (COPYDATASTRUCT)Marshal.PtrToStructure(m.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); m.Result = (IntPtr)1; } } break; } base.WndProc(ref m); }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485