【问题标题】:In a WinForm process, how to detect when a command line child process is demanding input?在 WinForm 进程中,如何检测命令行子进程何时需要输入?
【发布时间】:2011-09-19 22:34:23
【问题描述】:

在调用第三方命令行工具的 WinForm 应用程序中,有时该工具需要用户输入,例如询问是否应该覆盖文件:

printf("%s already exists, overwrite?: <Y>es, <N>o, <A>ll, <Q>uit?",FName);
for ( ; ; )
    switch ( toupper(getch()) ) {
        case 'A':
            YesToAll=true;
        case '\r': 
        case 'Y':
            remove(FName);
            return true;
        case 0x1B: 
        case 'Q':
            printf("quit\n"); exit(-1);
        case 'N':                       
            return false;
    }

发生这种情况时,我想在对话框中显示来自printf() 的消息和选项,并将按钮单击重定向为进程的输入。它可能涉及使用System.Diagnostics.Process.StandardInput 发送输入。但是,如果不知道该工具何时需要输入,我将不知道何时在 GUI 中做出相应的反应。当进程处于此 for 循环中时,我的 WinForm 进程将冻结。

编辑:这是通过在另一个线程中启动进程来解锁 UI 的代码,但是我仍然无法读取输出,如果我选择的文件会导致工具询问覆盖选项。 proc_OutputDataReceivedEDIT2: 或 readStdOutproc.StandardOutput.BaseStream.BeginRead 的情况下)永远不会被调用,除非该工具不要求输入)。

    private BackgroundWorker worker = new BackgroundWorker();

    private void fileChosenHandler(object sender, EventArgs e)
    {
        OpenFileDialog dialog = sender as OpenFileDialog;
        worker.DoWork += new DoWorkEventHandler(worker_DoWork);
        worker.RunWorkerAsync(dialog.FileName);
    }

    private void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        string exePath = @"F:\test\test.exe";
        Process proc = new Process();
        proc.StartInfo.FileName = exePath; 
        proc.StartInfo.Arguments = "\"" + (string)e.Argument + "\""; 
        proc.StartInfo.UseShellExecute = false; 
        proc.StartInfo.CreateNoWindow = true;
        proc.StartInfo.RedirectStandardOutput = true;
        proc.OutputDataReceived += new DataReceivedEventHandler(proc_OutputDataReceived);

        proc.Start();

        // method 1: read lines
        //proc.BeginOutputReadLine();
        // method 2: read characters
        proc.StandardOutput.BaseStream.BeginRead(stdOutBuffer, 0, stdOutBuffer.Length, readStdOut, proc);

        proc.WaitForExit();

    }

    private void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
    {
        MessageBox.Show("Output: " + e.Data);
    }

    private byte[] stdOutBuffer = new byte[20]; 

    private void readStdOut(IAsyncResult result)
    {
        Process proc = result.AsyncState as Process;

        int bytesNumber = proc.StandardOutput.BaseStream.EndRead(result);
        if (bytesNumber != 0)
        {
            string text = System.Text.Encoding.ASCII.GetString(stdOutBuffer, 0, bytesNumber);
            MessageBox.Show("Output: " + text);
        }

        // set up the callback again
        proc.StandardOutput.BaseStream.BeginRead(stdOutBuffer, 0, stdOutBuffer.Length, readStdOut, proc);
    }

知道怎么做吗?谢谢!

【问题讨论】:

    标签: c# winforms command-line stdout io-redirection


    【解决方案1】:

    System.Diagnostics.StandardOutput 读取(如果您使用阻塞读取,则必须在单独的线程中执行)直到找到该字符串的匹配项,然后显示您的消息框并将字符发送到StandardInput过程根据用户选择。


    我们尝试的事情的快速总结:

    • “正常”异步读取 BeginOutputReadLine -> 肯定会失败,因为来自应用程序的消息没有以 '\n' 终止;
    • 1 字节块中的异步读取 -> 似乎失败了,因为应用程序没有刷新缓冲区;
    • 在 C 应用程序中添加fflush(stdout) + 以前的方法:成功!显然程序没有在getch() 之前刷新输出缓冲区。

    有趣的是,这可能不会在使用标准 iostreams 的 C++ 应用程序中发生,因为 cincout 绑定,并且在 cin 上的任何输入操作发生之前 cout 会自动刷新。我认为stdin/stdout 也发生了这样的事情是合理的,但我似乎无法在标准中找到任何对它的引用(事实上聊天getch() 是非标准的并且与其他未缓冲的 IO 函数可能是相关的)。

    有关更多信息,请参阅 cmets。 :)

    【讨论】:

    • 您好,我尝试使用另一个线程,问题是一样的:我无法读取“文件存在,覆盖?”在工作线程进入循环之前输出。添加了有问题的代码。
    • @Dan7:你指的是哪个循环?在某个时刻,您是否会通过程序的输出获得 MessageBox
    • 如果我选择的文件是一个新文件,MessageBox 将显示所有输出就好了。但如果我选择的文件会导致工具询问覆盖选项,则不会出现MessageBox
    • 嗯,也许我明白了...BeginOutputReadLine 仅在收到每一行后才会通知您,但是当它询问该问题时,程序不要换行(但我认为它会刷新在 getch()) 之前自动缓冲,因此您不会收到通知。
    • 嗯,这很奇怪...如果在printf 之后添加fflush(stdout) 会发生什么?
    猜你喜欢
    • 2015-06-09
    • 2018-08-30
    • 2013-08-09
    • 1970-01-01
    • 1970-01-01
    • 2021-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多