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

C# WPF调用Process引发UI卡顿与焦点干扰问题求助

问题描述

我有一个通过USB与被测板通信的可执行文件,我的C# WPF应用需要在轮询循环中使用System.Diagnostics.Process调用该exe。我是Task相关技术的新手,尝试过将Process放入独立Task线程执行以避免阻塞UI,但仍存在以下问题:Process运行时UI闪烁、无法拖动,且会打断其他应用(如Word)的操作。

我曾将Process调用包装进Task.Run,确认使用了独立线程,但问题依旧;给窗口设置Topmost="True"解决了自身UI闪烁问题,但仍会干扰其他应用的焦点。最终发现问题根源是Process运行时的焦点切换,现寻求更完善的解决办法。


当前代码片段

GetData方法

List<string> TestResults = new List<string>();

private Process process;
private TaskCompletionSource<bool> eventHandled;

public async Task<List<string>> GetData(string cmdString)
{
    process = new Process();
    eventHandled = new TaskCompletionSource<bool>();

    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();

    TestResults.Clear();

    process.StartInfo.CreateNoWindow = true;
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardError = true;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.WorkingDirectory = WorkingDirectoryPath;

    process.EnableRaisingEvents = true;
    process.Exited += new EventHandler(ExitedProcess);

    process.OutputDataReceived += OutputDataHandler;
    // process.ErrorDataReceived += ErrorDataHandler;

    process.StartInfo.FileName = CommandExe;

    if (cmdString != null)
    {
        process.StartInfo.Arguments = cmdString;
    }
    else
    {
        return null;
    }

    process.Start();

    process.BeginOutputReadLine();

    // process.BeginErrorReadLine();

    // process.WaitForExit(); // comment out if using Task

    await Task.WhenAll(eventHandled.Task, Task.Delay(100));

    process.Dispose();

    stopwatch.Stop();
    FmpViewModel.Notes.Add($"Call Ended: {stopwatch.ElapsedMilliseconds}ms");

    return TestResults;         
}

private void ExitedProcess(object sender, System.EventArgs e)
{
    eventHandled.TrySetResult(true);
}

private void OutputDataHandler(object sendingProcess,
    DataReceivedEventArgs data)
{
    if (!string.IsNullOrEmpty(data.Data))
    {                
       TestResults.Add(data.Data);
    }
}

Execute方法

private async void Execute(object parameter)
{
    tokenSource = new CancellationTokenSource();
    token = tokenSource.Token;
    //Turn on Cancel button
    CanExecuteEnable = false;
    //Turn off Initialize button
    CanInitEnable = false;

    await ExecuteTest(token);         
}

解决方案

1. 阻止外部Process获取焦点

核心是通过Win32 API控制进程启动时的窗口状态,彻底阻止其抢占焦点:

首先添加P/Invoke声明:

[StructLayout(LayoutKind.Sequential)]
private struct STARTUPINFO
{
    public int cb;
    public string lpReserved;
    public string lpDesktop;
    public string lpTitle;
    public int dwX;
    public int dwY;
    public int dwXSize;
    public int dwYSize;
    public int dwXCountChars;
    public int dwYCountChars;
    public int dwFillAttribute;
    public int dwFlags;
    public short wShowWindow;
    public short cbReserved2;
    public IntPtr lpReserved2;
    public IntPtr hStdInput;
    public IntPtr hStdOutput;
    public IntPtr hStdError;
}

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CreateProcess(
    string lpApplicationName,
    string lpCommandLine,
    IntPtr lpProcessAttributes,
    IntPtr lpThreadAttributes,
    bool bInheritHandles,
    uint dwCreationFlags,
    IntPtr lpEnvironment,
    string lpCurrentDirectory,
    [In] ref STARTUPINFO lpStartupInfo,
    out PROCESS_INFORMATION lpProcessInformation);

[StructLayout(LayoutKind.Sequential)]
private struct PROCESS_INFORMATION
{
    public IntPtr hProcess;
    public IntPtr hThread;
    public uint dwProcessId;
    public uint dwThreadId;
}

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);

private const uint CREATE_NO_WINDOW = 0x08000000;
private const uint STARTF_USESHOWWINDOW = 0x00000001;
private const short SW_HIDE = 0;

然后修改GetData方法,用CreateProcess替代原生Process.Start(),确保进程启动时不激活窗口:

public async Task<List<string>> GetData(string cmdString)
{
    TestResults.Clear();
    var stopwatch = Stopwatch.StartNew();
    var eventHandled = new TaskCompletionSource<bool>();

    var startupInfo = new STARTUPINFO();
    startupInfo.cb = Marshal.SizeOf(startupInfo);
    startupInfo.dwFlags = STARTF_USESHOWWINDOW;
    startupInfo.wShowWindow = SW_HIDE; // 强制隐藏窗口,不激活

    PROCESS_INFORMATION processInfo;
    string commandLine = $"\"{CommandExe}\" {cmdString}";
    if (!CreateProcess(null, commandLine, IntPtr.Zero, IntPtr.Zero, true, CREATE_NO_WINDOW, IntPtr.Zero, WorkingDirectoryPath, ref startupInfo, out processInfo))
    {
        stopwatch.Stop();
        FmpViewModel.Notes.Add($"Failed to start process: {Marshal.GetLastWin32Error()}");
        return null;
    }

    // 绑定进程事件
    var process = Process.GetProcessById((int)processInfo.dwProcessId);
    process.EnableRaisingEvents = true;
    process.Exited += (s, e) => eventHandled.TrySetResult(true);
    process.OutputDataReceived += OutputDataHandler;
    process.ErrorDataReceived += ErrorDataHandler; // 建议启用错误监听

    process.BeginOutputReadLine();
    process.BeginErrorReadLine();

    await eventHandled.Task;

    // 释放系统资源
    process.Dispose();
    CloseHandle(processInfo.hProcess);
    CloseHandle(processInfo.hThread);

    stopwatch.Stop();
    FmpViewModel.Notes.Add($"Call Ended: {stopwatch.ElapsedMilliseconds}ms");
    return TestResults;
}

2. 优化UI线程更新

如果TestResults绑定到UI控件,需确保数据更新在UI线程的低优先级队列执行,避免频繁更新导致闪烁:

private void OutputDataHandler(object sendingProcess, DataReceivedEventArgs data)
{
    if (!string.IsNullOrEmpty(data.Data))
    {
        Application.Current.Dispatcher.InvokeAsync(() =>
        {
            TestResults.Add(data.Data);
        }, DispatcherPriority.Background);
    }
}

3. 轮询循环优化

避免在UI线程直接执行轮询,改用异步循环+Task.Delay,减少UI线程占用:

private async Task ExecuteTest(CancellationToken token)
{
    while (!token.IsCancellationRequested)
    {
        var results = await GetData("your-command");
        // 处理返回结果
        await Task.Delay(1000, token); // 自定义轮询间隔
    }
}

内容的提问来源于stack exchange,提问作者B A

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 06:05:35