在C# WPF IDE中检测控制台是否等待用户输入
问题:WPF IDE中Process调用程序无法处理用户输入并检测等待状态
我用WPF和C#开发名为EasyBite的语言IDE,已实现输出面板,通过Process类调用解释器并显示输出,运行正常,但程序需要用户输入时会直接终止;直接在命令提示符运行该程序则无此问题。现需实现类似其他IDE的功能:检测程序是否处于等待用户输入的状态,之前尝试遍历ProcessThread检测等待状态的逻辑无效,求解决方案。
当前实现代码
private void StartEasyBiteProcess(string filePath, string compilerPath) { Dispatcher.Invoke(() => { OutputTextBox.AppendText("EasyBite Executing...\n\n"); OutputTextBox.Foreground = new SolidColorBrush(Colors.Green); }); _easyBiteProcess = new Process(); _easyBiteProcess.StartInfo.FileName = Path.Combine(compilerPath, "EasyBite.exe") ; _easyBiteProcess.StartInfo.Arguments = filePath; // Pass the file path as a command-line argument _easyBiteProcess.StartInfo.UseShellExecute = false; _easyBiteProcess.StartInfo.RedirectStandardInput = true; _easyBiteProcess.StartInfo.RedirectStandardOutput = true; _easyBiteProcess.StartInfo.CreateNoWindow = true; _easyBiteProcess.OutputDataReceived += EasyBiteProcess_OutputDataReceived; _easyBiteProcess.EnableRaisingEvents = true; _easyBiteProcess.Exited += EasyBiteProcess_Exited; _easyBiteProcess.Start(); _easyBiteProcess.BeginOutputReadLine(); foreach(ProcessThread thread in process.Threads) if (thread.ThreadState == ThreadState.Wait && thread.WaitReason == ThreadWaitReason.UserRequest) _waitForInput = true; } private void EasyBiteProcess_Exited(object sender, EventArgs e) { Dispatcher.Invoke(() => { OutputTextBox.Text += "EasyBite program exited.\n"; OutputTextBox.Foreground = new SolidColorBrush(Colors.White); }); } private void EasyBiteProcess_OutputDataReceived(object sender, DataReceivedEventArgs e) { Dispatcher.Invoke(() => { if (_waitForInput) { OutputTextBox.IsReadOnly = false; // Handle user input HandleProgramInput(e.Data); _waitForInput = false; // Assuming that the input handling is synchronous and only for one line } else { Console.Read(); // Handle program output OutputTextBox.AppendText("> " + e.Data + "\n"); OutputTextBox.Foreground = new SolidColorBrush(Colors.White); OutputTextBox.CaretIndex = OutputTextBox.Text.Length; OutputTextBox.ScrollToEnd(); // Scrolls to the end of the TextBox } }); } private void HandleProgramInput(string userInput) { // Write user input to the standard input stream of EasyBite.exe using (StreamWriter streamWriter = _easyBiteProcess.StandardInput) { if (streamWriter.BaseStream.CanWrite) { streamWriter.WriteLine(userInput); } } OutputTextBox.AppendText("> " + userInput + "\n"); OutputTextBox.CaretIndex = OutputTextBox.Text.Length; OutputTextBox.ScrollToEnd(); // Scrolls to the end of the TextBox }
无效的尝试逻辑
foreach(ProcessThread thread in process.Threads) if (thread.ThreadState == ThreadState.Wait && thread.WaitReason == ThreadWaitReason.UserRequest)
解决方案
核心问题在于:通过ProcessThread检测等待状态不可靠,ThreadWaitReason.UserRequest并不对应程序等待标准输入的状态。以下是两种可行方案:
方案1:基于输出特征触发输入(最常用)
大多数交互式程序在等待输入时会输出特定提示符(如>、请输入:),可通过捕获这些特征字符串切换到输入模式:
修改OutputDataReceived事件处理逻辑,并添加输入按键监听:
private void EasyBiteProcess_OutputDataReceived(object sender, DataReceivedEventArgs e) { Dispatcher.Invoke(() => { if (e.Data == null) return; // 根据EasyBite实际输出的输入提示符调整判断条件 bool isWaitingForInput = e.Data.EndsWith(">") || e.Data.Contains("请输入:"); if (isWaitingForInput) { OutputTextBox.AppendText(e.Data + "\n"); OutputTextBox.IsReadOnly = false; // 绑定Enter键处理输入 OutputTextBox.KeyDown += OutputTextBox_KeyDownForInput; } else { OutputTextBox.AppendText("> " + e.Data + "\n"); OutputTextBox.Foreground = new SolidColorBrush(Colors.White); OutputTextBox.CaretIndex = OutputTextBox.Text.Length; OutputTextBox.ScrollToEnd(); } }); } // 处理用户输入的Enter键事件 private void OutputTextBox_KeyDownForInput(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) { // 提取用户输入内容(需区分输出与输入区域,建议用单独输入框优化体验) string input = OutputTextBox.Text.Substring(OutputTextBox.Text.LastIndexOf('\n') + 1); OutputTextBox.Text += "\n"; OutputTextBox.IsReadOnly = true; OutputTextBox.KeyDown -= OutputTextBox_KeyDownForInput; // 写入输入到程序,避免关闭流 if (!_easyBiteProcess.HasExited && _easyBiteProcess.StandardInput.BaseStream.CanWrite) { _easyBiteProcess.StandardInput.WriteLine(input); _easyBiteProcess.StandardInput.Flush(); } } }
方案2:异步监控输出流(更灵活)
如果EasyBite没有明确的输入提示符,可通过异步读取输出流,实时判断程序状态:
修改启动代码为异步模式:
private async void StartEasyBiteProcess(string filePath, string compilerPath) { Dispatcher.Invoke(() => { OutputTextBox.AppendText("EasyBite Executing...\n\n"); OutputTextBox.Foreground = new SolidColorBrush(Colors.Green); }); _easyBiteProcess = new Process(); _easyBiteProcess.StartInfo.FileName = Path.Combine(compilerPath, "EasyBite.exe"); _easyBiteProcess.StartInfo.Arguments = filePath; _easyBiteProcess.StartInfo.UseShellExecute = false; _easyBiteProcess.StartInfo.RedirectStandardInput = true; _easyBiteProcess.StartInfo.RedirectStandardOutput = true; _easyBiteProcess.StartInfo.CreateNoWindow = true; _easyBiteProcess.EnableRaisingEvents = true; _easyBiteProcess.Exited += EasyBiteProcess_Exited; _easyBiteProcess.Start(); // 异步读取输出,避免阻塞UI线程 await Task.Run(async () => { while (!_easyBiteProcess.StandardOutput.EndOfStream) { string line = await _easyBiteProcess.StandardOutput.ReadLineAsync(); if (!string.IsNullOrEmpty(line)) { Dispatcher.Invoke(() => { OutputTextBox.AppendText("> " + line + "\n"); OutputTextBox.Foreground = new SolidColorBrush(Colors.White); OutputTextBox.CaretIndex = OutputTextBox.Text.Length; OutputTextBox.ScrollToEnd(); // 同样通过输出特征判断是否等待输入 bool isWaitingForInput = line.EndsWith(">") || line.Contains("请输入:"); if (isWaitingForInput) { OutputTextBox.IsReadOnly = false; OutputTextBox.KeyDown += OutputTextBox_KeyDownForInput; } }); } } }); }
关键注意事项
- 移除
OutputDataReceived中的Console.Read(),避免阻塞UI线程。 - 不要用
using包裹StandardInput,否则会关闭输入流,导致后续无法写入。 - 程序退出时清理资源,比如取消事件绑定、销毁Process实例。
内容的提问来源于stack exchange,提问作者Muhammad Baba Goni
相关产品推荐
相关产品推荐

