WPF单实例程序实现:将次实例参数传递给主实例的方法
WPF 单实例运行并传递文件参数实现方案
核心思路
通过 Mutex 检测实例唯一性,再用**进程间通信(IPC)**将第二个实例的参数传递给第一个实例。以下提供两种实用实现方案:
方案一:NamedPipe(推荐,稳定灵活)
基于.NET内置的命名管道实现跨进程通信,不依赖窗口句柄,支持复杂参数传递。
1. 修改 App.xaml.cs 代码
添加必要引用:
using System.IO.Pipes; using System.Threading.Tasks; using System.Diagnostics; using System.IO;
App类字段与Startup事件:
private Mutex _instanceMutex; private NamedPipeServerStream _pipeServer; private void Application_Startup(object sender, StartupEventArgs e) { bool isNewInstance = false; // 用唯一名称避免冲突,建议添加公司前缀或GUID _instanceMutex = new Mutex(true, "MyCompany.MyWPFApp.InstanceMutex", out isNewInstance); if (!isNewInstance) { SendArgsToRunningInstance(e.Args); App.Current.Shutdown(); return; } // 启动管道监听 StartPipeListener(); // 处理当前实例的启动参数 ProcessFileArgs(e.Args); // 加载主窗口 MainWindow = new MainWindow(); MainWindow.Show(); }
2. 实现参数发送方法(第二个实例调用)
private void SendArgsToRunningInstance(string[] args) { try { using (var pipeClient = new NamedPipeClientStream(".", "MyWPFApp.Pipe", PipeDirection.Out)) { pipeClient.Connect(1000); // 1秒超时 using (var writer = new StreamWriter(pipeClient)) { // 用|分隔多个文件路径 string argsStr = string.Join("|", args); writer.WriteLine(argsStr); writer.Flush(); } } } catch (Exception ex) { MessageBox.Show($"无法传递参数:{ex.Message}"); } }
3. 实现管道监听(第一个实例接收参数)
private async void StartPipeListener() { while (true) { try { _pipeServer = new NamedPipeServerStream( "MyWPFApp.Pipe", PipeDirection.In, 1, PipeTransmissionMode.Message, PipeOptions.Asynchronous); await _pipeServer.WaitForConnectionAsync(); using (var reader = new StreamReader(_pipeServer)) { string argsStr = await reader.ReadLineAsync(); if (!string.IsNullOrEmpty(argsStr)) { string[] args = argsStr.Split('|'); // 切换到UI线程处理参数 Application.Current.Dispatcher.Invoke(() => ProcessFileArgs(args)); } } _pipeServer.Disconnect(); } catch (Exception ex) { Debug.WriteLine($"管道监听错误:{ex.Message}"); } finally { _pipeServer?.Dispose(); } } }
4. 实现文件参数处理逻辑
private void ProcessFileArgs(string[] args) { if (args == null || args.Length == 0) return; foreach (string filePath in args) { if (File.Exists(filePath) && (Path.GetExtension(filePath).Equals(".cogu", StringComparison.OrdinalIgnoreCase) || Path.GetExtension(filePath).Equals(".cogd", StringComparison.OrdinalIgnoreCase))) { // 通知主窗口打开文件,需在MainWindow中实现OpenFile方法 if (MainWindow is MainWindow mainWin) { mainWin.OpenFile(filePath); } } } }
5. 退出时释放资源
protected override void OnExit(ExitEventArgs e) { _instanceMutex?.ReleaseMutex(); _instanceMutex?.Dispose(); _pipeServer?.Dispose(); base.OnExit(e); }
6. 主窗口添加文件处理方法
在 MainWindow.xaml.cs 中添加:
public void OpenFile(string filePath) { // 替换为你的实际文件处理逻辑,如加载内容到编辑器 MessageBox.Show($"打开文件:{filePath}"); }
方案二:WM_COPYDATA(轻量,依赖窗口句柄)
基于Windows消息机制实现简单字符串参数传递,适合轻量场景。
1. 定义Windows API与结构体
在App.xaml.cs中添加:
using System.Runtime.InteropServices; private const int WM_COPYDATA = 0x004A; [StructLayout(LayoutKind.Sequential)] private struct COPYDATASTRUCT { public IntPtr dwData; public int cbData; [MarshalAs(UnmanagedType.LPStr)] public string lpData; } [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("user32.dll", SetLastError = true)] private static extern bool SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, ref COPYDATASTRUCT lParam);
2. 修改参数发送逻辑
替换方案一中的 SendArgsToRunningInstance:
private void SendArgsToRunningInstance(string[] args) { // 需确保主窗口Title固定,此处替换为你的主窗口Title IntPtr hwnd = FindWindow(null, "我的WPF应用"); if (hwnd == IntPtr.Zero) { MessageBox.Show("未找到运行中的实例"); return; } string argsStr = string.Join("|", args); COPYDATASTRUCT cds = new COPYDATASTRUCT { dwData = IntPtr.Zero, cbData = argsStr.Length + 1, // 包含字符串结束符 lpData = argsStr }; SendMessage(hwnd, WM_COPYDATA, IntPtr.Zero, ref cds); }
3. 主窗口监听WM_COPYDATA消息
在 MainWindow.xaml.cs 中添加:
protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e); HwndSource source = PresentationSource.FromVisual(this) as HwndSource; source.AddHook(WndProc); } private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (msg == WM_COPYDATA) { COPYDATASTRUCT cds = Marshal.PtrToStructure<COPYDATASTRUCT>(lParam); string[] args = cds.lpData.Split('|'); ProcessFileArgs(args); handled = true; return IntPtr.Zero; } return IntPtr.Zero; } private void ProcessFileArgs(string[] args) { foreach (string filePath in args) { if (File.Exists(filePath) && (Path.GetExtension(filePath).Equals(".cogu", StringComparison.OrdinalIgnoreCase) || Path.GetExtension(filePath).Equals(".cogd", StringComparison.OrdinalIgnoreCase))) { MessageBox.Show($"打开文件:{filePath}"); // 你的文件处理逻辑 } } }
方案对比
- NamedPipe:不依赖窗口状态,支持复杂数据,适合大多数生产场景。
- WM_COPYDATA:代码更简洁,仅支持字符串参数,需主窗口存在且Title固定。
内容的提问来源于stack exchange,提问作者Bird
相关产品推荐
相关产品推荐

