开发者

In a WinForm process, how to detect when a command line child process is demanding input?

开发者 https://www.devze.com 2023-04-06 04:29 出处:网络
In a WinForm application that calls a third-party command line tool, there may be a time when the tool is expecting user input, such as asking if it should overwrite a file:

In a WinForm application that calls a third-party command line tool, there may be a time when the tool is expecting user input, such as asking if it should overwrite a file:

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;
    }

When that happens, I want to display the message from printf() and the options in a dialog box, and redirect the button click as input to the process. It probably involves using System.Diagnostics.Process.StandardInput to send the input. But without knowing when exactly the tool will be expecting input, I would have no idea when to react accordingly in the GUI. When the process is in this for-loop, my WinForm process will just freeze.

EDIT: Here's the code that unblocks the UI by starting the process in another thread, however I still could not read the output, if the file I select will cause the tool to ask overwriting options. proc_OutputDataReceived (EDIT2: or readStdOut in proc.StandardOutput.BaseStream.BeginRead's case) will never be called , unless the tool does not ask for input).

    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);
    }

Any idea how to do this?开发者_StackOverflow Thanks!


Read from System.Diagnostics.StandardOutput (if you use a blocking read you'll have to do it in a separated thread) until you find a match for that string, then display your messagebox and send the character to the StandardInput of the process according to the user choice.


Quick summary of the things we tried:

  • "normal" asynchronous read with BeginOutputReadLine -> surely fails because the message from the application isn't terminated with a '\n';
  • asynchronous read in 1-byte blocks -> seems to fail because the application isn't flushing the buffer;
  • adding a fflush(stdout) to the C application + previous approach: success! Apparently the program wasn't flushing the output buffer before the getch().

Interestingly, this probably wouldn't happen in a C++ application using the standard iostreams, since cin is tied to cout, and before any input operation on cin happens cout is automatically flushed. I thought it was reasonable that something like this happened also for stdin/stdout, but I can't seem to find any reference to it into the standard (also the fact chat getch() is nonstandard and differs from the other IO functions in being unbuffered may be related).

Fore more info see the comments. :)

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号