Windows DDE通信机制解析

在Windows应用程序开发中,有时需要处理文件关联时的特定情况,例如,当用户通过资源管理器双击一个文件时,如果应用程序已经打开,希望文件在当前实例中打开,而不是启动一个新的实例。为了实现这一点,可以使用DDE(动态数据交换)来与应用程序的实例进行通信。DDE是Windows 3.1时代的产物,它通过Windows消息进行工作。

DDE的工作原理

DDE通信机制通过创建一个虚拟窗口,并为该窗口设置消息泵,等待接收WM_DDE_*系列的消息。当接收到WM_DDE_EXECUTE消息时,触发DDEExecute事件。

以下是一个C#实现的示例,展示了如何创建一个继承自Windows.Forms.NativeWindow的类,并为它提供消息处理事件。

public class NativeWindowWithMessages : System.Windows.Forms.NativeWindow { public event MessageEventHandler ProcessMessage; protected override void WndProc(ref Message m) { if (ProcessMessage != null) { bool Handled = false; ProcessMessage(this, ref m, ref Handled); if (!Handled) base.WndProc(ref m); } else base.WndProc(ref m); } }

这个类提供了一个事件处理器,用于处理完整的DDE过程:

protected void MessageEvent(object sender, ref Message m, ref bool Handled) { // 客户端想要初始化DDE连接 if ((m.Msg == (int)Win32.Msgs.WM_DDE_INITIATE)) { // 获取AppName和ActionName的ATOMs ushort a1 = Win32.Kernel32.GlobalAddAtom(Marshal.StringToHGlobalAnsi(m_AppName)); ushort a2 = Win32.Kernel32.GlobalAddAtom(Marshal.StringToHGlobalAnsi(m_ActionName)); // LParam包含AppName和ActionName的ATOMs ushort s1 = (ushort)((((uint)m.LParam) & 0xFFFF)); ushort s2 = (ushort)(((((uint)m.LParam) & 0xFFFF0000) >> 16)); // 如果ATOMs不相等,则返回 if ((a1 != s1) || (a2 != s2)) return; // 确认连接 IntPtr po = Win32.User32.PackDDElParam((int)Msgs.WM_DDE_ACK, (IntPtr)a1, (IntPtr)a2); Win32.User32.SendMessage(m.WParam, (int)Msgs.WM_DDE_ACK, m_Window.Handle, po); // 释放ATOMs Win32.Kernel32.GlobalDeleteAtom(a1); Win32.Kernel32.GlobalDeleteAtom(a2); isInitiated = true; Handled = true; } // 连接建立后,客户端应发送WM_DDE_EXECUTE消息 if ((m.Msg == (int)Win32.Msgs.WM_DDE_EXECUTE)) { if (!isInitiated) return; // LParam包含执行字符串 IntPtr pV = Win32.Kernel32.GlobalLock(m.LParam); string s3 = System.Runtime.InteropServices.Marshal.PtrToStringAuto(pV); Win32.Kernel32.GlobalUnlock(m.LParam); // 发送确认消息 IntPtr lP = Win32.User32.PackDDElParam((int)Win32.Msgs.WM_DDE_ACK, (IntPtr)1, m.LParam); Win32.User32.PostMessage(m.WParam, (int)Win32.Msgs.WM_DDE_ACK, m_Window.Handle, lP); System.Diagnostics.Debug.WriteLine(s3); // 分割字符串 string[] sarr = s3.Split(new char[] {'[', ']'}); if (sarr.GetUpperBound(0) > -1) { if (OnDDEExecute != null) OnDDEExecute(this, sarr); } Handled = true; } // 客户端应终止连接 if (m.Msg == (int)Win32.Msgs.WM_DDE_TERMINATE) { if (!isInitiated) return; // 确认终止 Win32.User32.PostMessage(m.WParam, (int)Win32.Msgs.WM_DDE_TERMINATE, m_Window.Handle, (IntPtr)0); Handled = true; isInitiated = false; } }

在上述代码中,使用了Win32 API调用进行消息处理,如GlobalLock、GlobalUnlock、GlobalAddAtom、GlobalDeleteAtom、PackDDElParam等,以及System.Runtime.InteropService中的字符串转换函数,如Marshal.PtrToStringAuto和Marshal.StringToHGlobalAnsi。

使用此类或示例项目时,需要将文件类型注册到应用程序。在资源管理器中打开“文件夹选项”,转到“文件类型”标签页。添加或编辑文件扩展名,添加新操作,并指定项目或示例项目的可执行文件。勾选DDE复选框,并输入例如[open("%1")]作为命令。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485