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