C# Windows Form .Net 和 DOS 控制台

4
我有一个执行批处理文件的Windows表单。我想将控制台中发生的所有事情传输到我的表单面板中。我该怎么做?我的DOS控制台如何与Windows表单面板通信?
谢谢
6个回答

7
您可以从表单应用程序中调用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 标志以防止控制台应用程序出现,只要它不需要与用户交互。 - LBushkin
谢谢...我能否有一种方法来隐藏操作系统窗口?它现在可以工作,但会显示空的操作系统控制台... - user62958
1
在读取输入之前,您不需要等待进程退出,但是之后需要Dispose()该进程。我已经添加了这个。未知的Yahoo,我没有控制台窗口,你确定这是批处理文件吗?尝试设置p.StartInfo.CreateNoWindow = true; - Dour High Arch
4
在调用ReadToEnd之前,绝不能放置WaitForExit()。进程的输出缓冲区可能会填满,这将挂起进程,也就是说它永远不会退出。因此,按照上面的代码,在调用WaitForExit()之前先调用ReadToEnd。 - Cheeso

3

文档指出,如果您想要读取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();

}

2

您应该首先添加对 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

1

我一直在尝试使用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);
        }
    }
}

0

最近我开发了一个小应用程序,其中涉及到批处理文件的交互。我找到了下面这段代码片段,使我能够实现这个功能:

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流中读取和子进程写入该流之间引入了依赖关系。这些依赖关系可能导致死锁条件。当调用者从子进程的重定向流中读取时,它依赖于子进程。调用者等待读操作,直到子进程写入流或关闭流。当子进程写入足够填充其重定向流的数据时,它依赖于父进程。子进程等待下一次写操作,直到父进程从完整的流中读取或关闭流。当调用者和子进程互相等待对方完成操作,而双方都无法继续时,就会发生死锁条件。您可以通过评估调用者和子进程之间的依赖关系来避免死锁。


1
实际上这样做是行不通的,因为stderr和stdout之间存在竞争条件。假设你运行的进程首先写入stdout,然后再写入stderr。通过在stderr上调用ReadToEnd(),你将永远等待。另一方面,如果该进程以相反的顺序写入,则从stderr读取是可以的。但是没有保证。因此存在竞争条件。你需要使用异步读取来避免这种竞争。有关更多信息,请参阅Process.Start()文档。 - Cheeso
我猜这就是为什么这段代码有时会导致我的批处理窗口冻结。我有一种感觉,可能有些地方不对劲。出于教育目的,我将保留我的错误答案。 - Matthew Ruston

0

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

将命令行输出重定向到文件

对于显示大量文本的程序,请考虑将通常显示在stdout上的文本重定向到文件中。 显示大量文本会减慢执行速度; 在工作站上滚动文本可能会导致I / O瓶颈(增加的经过时间)并使用更多CPU时间。

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

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接