在WinForm进程中,如何检测命令行子进程何时需要输入?
在调用第三方命令行工具的 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_OutputDataReceived
(EDIT2:或 proc.StandardOutput.BaseStream.BeginRead
中的 readStdOut
)永远不会被调用,除非该工具不要求输入)。
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);
}
知道如何做到这一点吗?谢谢!
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? Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
从 System.Diagnostics.StandardOutput 读取(如果您使用阻塞读取,则必须在单独的线程中执行),直到找到该字符串的匹配项,然后显示消息框并发送该字符根据用户的选择到进程的
StandardInput
。快速总结我们尝试过的事情:
BeginOutputReadLine
进行“正常”异步读取 ->肯定会失败,因为来自应用程序的消息不是以'\n'
终止的;fflush(stdout)
添加到 C 应用程序 + 之前的方法:成功!显然,程序在getch()
之前没有刷新输出缓冲区。有趣的是,这在使用标准 iostream 的 C++ 应用程序中可能不会发生,因为 cin 与
cout
相关联,并且在对 cin 进行任何输入操作之前code> 发生时cout
会自动刷新。我认为stdin
/stdout
也发生这样的事情是合理的,但我似乎无法在标准中找到任何对它的引用(也是事实聊天)getch()
是非标准的,与其他 IO 函数不同,可能与无缓冲有关)。更多信息请参阅评论。 :)
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 theStandardInput
of the process according to the user choice.Quick summary of the things we tried:
BeginOutputReadLine
-> surely fails because the message from the application isn't terminated with a'\n'
;fflush(stdout)
to the C application + previous approach: success! Apparently the program wasn't flushing the output buffer before thegetch()
.Interestingly, this probably wouldn't happen in a C++ application using the standard iostreams, since
cin
is tied tocout
, and before any input operation oncin
happenscout
is automatically flushed. I thought it was reasonable that something like this happened also forstdin
/stdout
, but I can't seem to find any reference to it into the standard (also the fact chatgetch()
is nonstandard and differs from the other IO functions in being unbuffered may be related).Fore more info see the comments. :)