【问题标题】:C# Windows Form .Net and DOS ConsoleC# Windows Form .Net 和 DOS 控制台
【发布时间】:2026-02-08 19:15:01
【问题描述】:

我有一个执行批处理文件的 Windows 窗体。我想将控制台中发生的所有事情转移到表单中的面板上。我怎样才能做到这一点?我的 DOS 控制台如何与我的 windows 窗体面板通信???

谢谢

【问题讨论】:

    标签: c# .net


    【解决方案1】:

    您可以从表单应用程序调用 DOS 或批处理程序并将输出重定向到字符串:

    using (var p = new System.Diagnostics.Process( ))
    {
        p.StartInfo.UseShellExecute = false;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.FileName = PathToBatchFile;
        p.StartInfo.Arguments = args;
        p.Start( );
        string o = p.StandardOutput.ReadToEnd( );
        p.WaitForExit( );
    }
    

    【讨论】:

    • 您可能希望将 WaitForExit() 调用移到从重定向输出流中读取输入的行之前。此外,如果需要,您可以添加 WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden 标志以防止控制台应用程序出现,只要它不需要与用户交互即可。
    • 谢谢...有没有办法让我真正隐藏操作系统窗口???它现在可以工作,但显示空的操作系统控制台......
    • 在读取输入之前不需要 WaitForExit,但之后需要 Dispose() 处理。我已经添加了这个。未知的雅虎,我没有控制台窗口,你确定这是批处理文件吗?尝试设置 p.StartInfo.CreateNoWindow = true;
    • 您不得将 WaitForExit() 放在 ReadToEnd 之前。进程的输出缓冲区可能会填满,这将暂停进程。这意味着它永远不会退出。所以在 WaitForExit() 之前调用 ReadToEnd,就像上面的代码一样。
    【解决方案2】:

    我一直在使用 System.Diagnostics.Process 类来调用基于控制台的应用程序以及格式化和返回输出。我认为它也适用于批处理文件。我会在这里花一点时间来测试一下。下面是一些示例代码:

       System.Diagnostics.ProcessStartInfo start = new System.Diagnostics.ProcessStartInfo();
        start.UseShellExecute = false;
        start.RedirectStandardInput = true;
        start.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
    
        start.RedirectStandardOutput = true;
        start.FileName = "at";
    System.Diagnostics.Process myP = System.Diagnostics.Process.Start(start);
    String strOutput = myP.StandardOutput.ReadToEnd();
    if (strOutput.Contains("There are no entries in the list."))
    {
        litMsg.Text = "There are no jobs";
    }
    else
    {
        strOutput = strOutput.Replace("\r", "");
        foreach (String line in strOutput.Split("\n".ToCharArray()))
        {
            //(0,7)  (7,5)(12, 24)                (36, 14)      (50, )
            //Status ID   Day                     Time          Command Line
            //-------------------------------------------------------------------------------
            //        1   Tomorrow                3:00 AM       dir *
            if (line.Length > 50)
            {
                String Status = line.Substring(0, 7);
                String ID = line.Substring(7, 5);
                String Day = line.Substring(12, 24);
                String Time = line.Substring(35, 14);
                String Command = line.Substring(49);
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      我最近构建了一个小型应用程序,用于与批处理文件进行交互。我找到了允许我这样做的代码片段:

      Process proc = new Process
                     {
                         StartInfo =
                             {
                                 RedirectStandardError = true,
                                 RedirectStandardOutput = true,
                                 UseShellExecute = false,
                             }
                     };
      proc.Start();
      
      string errorMessage = proc.StandardError.ReadToEnd();
      proc.WaitForExit();
      string outputMessage = proc.StandardOutput.ReadToEnd();
      proc.WaitForExit();
      

      从这里,只需将这些字符串引导到您选择的用户控件中。


      编辑

      注意:这不是一般的解决方案。它可能导致死锁。来自RedirectStandardError 的文档:
      同步读取操作在从 StandardError 流读取的调用者和写入该流的子进程之间引入了依赖关系。这些依赖关系可能会导致死锁情况。当调用者从子进程的重定向流中读取时,它依赖于子进程。调用者等待读取操作,直到子写入流或关闭流。当子进程写入足够的数据来填充其重定向流时,它依赖于父进程。子进程等待下一次写入操作,直到父进程从完整流中读取或关闭流。当调用者和子进程互相等待完成一个操作,并且两者都不能继续时,就会出现死锁情况。您可以通过评估调用者和子进程之间的依赖关系来避免死锁。

      【讨论】:

      • 事实上这行不通,因为在标准错误和标准输出之间存在竞争条件。假设您运行的进程首先写入标准输出,然后写入标准错误。通过在 stderr 上调用 ReadToEnd(),您将永远等待。另一方面,如果进程以相反的顺序写入,则从 stderr 读取是可以的。但不能保证。因此存在竞争条件。您需要使用异步读取来避免这种竞争。有关更多信息,请参阅 Process.Start() 上的文档。
      • 我想这就是为什么这段代码有时会导致我的批处理窗口冻结的原因。我有我的感觉,有些事情可能已经不正常了。出于教育目的,我将留下不正确的答案。
      【解决方案4】:

      您应该首先添加对System.Diagnostics 的引用,然后像这样调用批处理文件:

      string myFile = "c:\\path\\to\\batch_file.bat";
      ProcessStartInfo psi = new ProcessStartInfo(myFile);
      psi.UseShellExecute = false;
      psi.CreateNoWindow = true;
      psi.RedirectStandardOutput = true;
      Process proc = Process.Start(psi);
      

      现在,如果您希望调用被阻塞(即,您的应用程序将冻结直到文件完成),那么只需使用 string result = proc.StandardOutput.ReadToEnd() 来读取您的批处理文件的全部输出。

      但是,如果您想让应用程序继续响应并实时显示输出,那么您需要使用BeginOutputReadLine

      【讨论】:

        【解决方案5】:

        我很确定有更好的选择,但您可以将命令的输出路由到临时文件

        Redirecting Command-Line Output to Files

        对于显示大量 文本,考虑重定向文本 通常在标准输出上显示到 文件。显示大量文本将 减慢执行速度;滚动文本 工作站上的终端窗口可以 导致 I/O 瓶颈(增加 经过的时间)并使用更多的 CPU 时间。

        以下命令显示如何运行 该程序更有效地通过 将输出重定向到文件,然后 显示程序输出:

        myprog > results.lis 更多 results.lis 重定向程序的输出 将更改报告的时间,因为 减少屏幕 I/O。

        我认为您也可以将其路由到一个变量,但不是 100% 确定。可能有更好的选择,但至少这是一个。

        【讨论】:

          【解决方案6】:

          The doc states 如果你想同时读取 StandardError 和 StandardOutput,你需要异步读取至少其中一个以避免死锁。

          此外,如果您在其中一个重定向的流上调用 ReadToEnd,则必须在调用 WaitForExit() 之前 这样做。如果在 ReadToEnd 之前 WaitForExit,输出缓冲区可能会填满,从而暂停进程,这意味着它永远不会退出。那将是一个非常漫长的等待。这也在文档中!

          示例:

          string output;
          string error;
          System.Diagnostics.Process p = new System.Diagnostics.Process
              {
                  StartInfo =
                  {
                      FileName = program,
                      Arguments = args,
                      WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden,
                      UseShellExecute = false,
                  }
              };
          
          if (waitForExit)
          {
              StringBuilder sb = new StringBuilder();
              p.StartInfo.RedirectStandardOutput = true;
              p.StartInfo.RedirectStandardError = true;
              Action<Object,DataReceivedEventArgs> stdErrorRead = (o,e) =>
              {
                  if (!String.IsNullOrEmpty(e.Data))
                      sb.Append(e.Data);
              };
          
              p.ErrorDataReceived += stdErrorRead;
              p.Start();
              // begin reading stderr asynchronously
              p.BeginErrorReadLine();
              // read stdout synchronously
              output = p.StandardOutput.ReadToEnd();
              p.WaitForExit();
              // return code is in p.ExitCode
          
              if (sb.Length > 0)
                  error= sb.ToString();
          
          }
          

          【讨论】: