You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 14:34:52