You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

C#中如何等待外部EXE完全启动后再传递执行参数

解决C#启动外部慢启动控制台程序并发送命令的问题

看起来你遇到的核心问题是:这个udecConsole2017.exe并不是在启动时直接解析命令行参数的程序,而是一个交互式控制台程序——它启动完成后才会等待用户输入命令。直接用Process.Start的参数传递方式对它无效,而且等待MainWindowTitle的方式也不适合这类控制台程序(很多控制台程序的窗口标题不会有明显变化,或者启动后很久才会设置标题)。

下面是针对这类场景的完整解决方案,包含等待程序初始化完成、发送命令、捕获输出流的实现:

核心思路

  1. 重定向进程的标准输入/输出/错误流,这样我们可以和程序进行交互(发送命令、读取输出)。
  2. 通过监听程序的输出,判断它是否完成初始化(比如等待程序输出特定的提示符,比如UDEC>之类的标识)。
  3. 确认程序准备好后,通过标准输入发送call命令。
  4. 异步读取输出流,避免主线程阻塞,同时捕获所有执行结果。

完整代码示例

using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        // 配置进程启动信息
        var exePath = @"D:\Program Files\ITASCA\UDEC700\Exe64\udecConsole2017.exe";
        var commandToRun = @"call 'D:\Work\202205\20220525\tunnel-for-cmd.txt'";
        
        var startInfo = new ProcessStartInfo(exePath)
        {
            RedirectStandardInput = true,   // 允许向进程输入命令
            RedirectStandardOutput = true,  // 允许读取进程输出
            RedirectStandardError = true,   // 允许读取错误输出
            UseShellExecute = false,        // 必须设为false才能重定向流
            CreateNoWindow = false,         // 设为true可以隐藏控制台窗口,根据需要调整
            WorkingDirectory = Path.GetDirectoryName(exePath) // 设置进程工作目录,避免路径问题
        };

        using (var process = Process.Start(startInfo))
        {
            if (process == null)
            {
                Console.WriteLine("无法启动目标程序");
                return;
            }

            // 异步读取输出和错误流,防止死锁
            var outputTask = ReadStreamAsync(process.StandardOutput, "输出");
            var errorTask = ReadStreamAsync(process.StandardError, "错误");

            // 等待程序初始化完成:这里需要根据程序实际输出的提示符调整判断逻辑
            // 比如如果程序启动后会输出类似"UDEC> "的提示符,就等待这个字符串出现
            Console.WriteLine("等待程序初始化...");
            var outputReader = process.StandardOutput;
            string line;
            while ((line = await outputReader.ReadLineAsync()) != null)
            {
                Console.WriteLine($"程序输出: {line}");
                // 替换成你观察到的程序初始化完成的标识字符串
                if (line.Contains("UDEC>")) // 示例,需要根据实际情况修改
                {
                    break;
                }
            }

            // 发送命令到程序的标准输入
            Console.WriteLine("发送命令...");
            await process.StandardInput.WriteLineAsync(commandToRun);
            // 如果需要执行完命令后退出程序,可以发送exit命令(根据程序支持情况调整)
            await process.StandardInput.WriteLineAsync("exit");

            // 等待进程结束和流读取完成
            await Task.WhenAll(outputTask, errorTask);
            await process.WaitForExitAsync();

            Console.WriteLine($"进程已退出,退出码: {process.ExitCode}");
        }
    }

    // 异步读取流的辅助方法
    static async Task ReadStreamAsync(StreamReader reader, string streamName)
    {
        string line;
        while ((line = await reader.ReadLineAsync()) != null)
        {
            Console.WriteLine($"{streamName}: {line}");
        }
    }
}

关键细节说明

  • 为什么不用MainWindowTitle:控制台程序的窗口标题通常是可执行文件的名称,启动后不会变化;而且有些程序启动后很久才会创建窗口,这个判断方式不可靠。
  • 判断初始化完成的逻辑:你需要手动运行udecConsole2017.exe,观察它启动完成后输出的最后一行内容(比如某个提示符),然后把代码中的line.Contains("UDEC>")替换成对应的判断条件。
  • 避免死锁:必须异步读取标准输出和错误流(或者在单独的线程中读取),如果主线程阻塞等待输入,同时进程的输出缓冲区满了,就会导致死锁。
  • 工作目录设置:设置WorkingDirectory可以避免程序因为找不到依赖文件或者相对路径错误而出现问题。

额外调试建议

如果不确定程序初始化完成的标识,可以先运行代码,观察控制台输出的内容,找到程序准备好接收命令的那个标志性输出行,再调整判断逻辑。

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

火山引擎 最新活动