如何使用来自另一个.NET程序的交互式命令行程序

Kar*_*ten 5 .net c# command-line pipe redirectstandardoutput

我需要为交互式命令行程序编写一个包装器.

这意味着我需要能够通过其标准输入向其他程序发送命令,并通过其标准输出接收响应.

问题是,当输入流仍处于打开状态时,标准输出流似乎被阻止.一旦我关闭输入流,我就得到响应.但后来我无法发送更多命令.

这就是我目前使用的(主要来自这里):

void Main() {
    Process process;
    process = new Process();
    process.StartInfo.FileName = "atprogram.exe";
    process.StartInfo.Arguments = "interactive";

    // Set UseShellExecute to false for redirection.
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.CreateNoWindow = true;

    // Redirect the standard output of the command.  
    // This stream is read asynchronously using an event handler.
    process.StartInfo.RedirectStandardOutput = true;
    // Set our event handler to asynchronously read the output.
    process.OutputDataReceived += (s, e) => Console.WriteLine(e.Data);

    // Redirect standard input as well. This stream is used synchronously.
    process.StartInfo.RedirectStandardInput = true;
    process.Start();

    // Start the asynchronous read of the output stream.
    process.BeginOutputReadLine();

    String inputText;
    do 
    {
        inputText = Console.ReadLine();
        if (inputText == "q")
        {
            process.StandardInput.Close();   // After this line the output stream unblocks
            Console.ReadLine();
            return;
        }
        else if (!String.IsNullOrEmpty(inputText))
        {
            process.StandardInput.WriteLine(inputText);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我也尝试同步读取标准输出流,但结果相同.无限期地对输出流块进行任何方法调用,直到输入流关闭为止 - 偶数Peek()EndOfStream.

有没有办法以全双工的方式与其他进程通信?

Bit*_*ler 1

我尝试用我自己的一个小型测试套件重现您的问题。\n我没有使用事件处理程序,而是以我能想到的最简单的方式来实现:同步。这样就不会给问题增加额外的复杂性。

\n

这里是我用 Rust 编写的小“echoApp”,只是为了咯咯笑,也为了有机会遇到永恒的线路终止战争问题(\\nvs \\rvs \\r\\n)。根据命令行应用程序的编写方式,这确实可能是您的问题之一。

\n
use std::io;\n\nfn main() {\n    let mut counter = 0;\n    loop {\n        let mut input = String::new();\n        let _ = io::stdin().read_line(&mut input);\n        match &input.trim() as &str {\n            "quit" => break,\n            _ => {\n                println!("{}: {}", counter, input);\n                counter += 1;\n            }\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

而且 - 作为一个懒人,不喜欢为如此小的测试创建解决方案,我使用 F# 而不是 C# 作为控制端 - 我认为它很容易阅读:

\n
open System.Diagnostics;\n\nlet echoPath = @"E:\\R\\rustic\\echo\\echoApp\\target\\debug\\echoApp.exe"\n\nlet createControlledProcess path = \n    let p = new Process()\n    p.StartInfo.UseShellExecute <- false\n    p.StartInfo.RedirectStandardInput <- true\n    p.StartInfo.RedirectStandardOutput <- true\n    p.StartInfo.Arguments <- ""\n    p.StartInfo.FileName <- path\n    p.StartInfo.CreateNoWindow <- true\n    p\n\nlet startupControlledProcess (p : Process) =\n    if p.Start() \n    then \n        p.StandardInput.NewLine <- "\\r\\n"\n    else ()\n\nlet shutdownControlledProcess (p : Process) =\n    p.StandardInput.WriteLine("quit");\n    p.WaitForExit()\n    p.Close()\n\nlet interact (p : Process) (arg : string) : string =\n    p.StandardInput.WriteLine(arg);\n    let o = p.StandardOutput.ReadLine()\n    // we get funny empty lines every other time... \n    // probably some line termination problem ( unix \\n vs \\r\\n etc - \n    // who can tell what rust std::io does...?)\n    if o = "" then p.StandardOutput.ReadLine()\n    else o\n\nlet p = createControlledProcess echoPath\nstartupControlledProcess p\nlet results = \n    [\n        interact p "Hello"\n        interact p "World"\n        interact p "Whatever"\n        interact p "floats"\n        interact p "your"\n        interact p "boat"\n    ]\nshutdownControlledProcess p\n
Run Code Online (Sandbox Code Playgroud)\n

在 f# Interactive 中执行此操作(在 Visual Studio 中按 CTRL-A ALT-Enter)会产生:

\n
\n

val echoPath : string = "E:\\R\\rustic\\echo\\echoApp\\target\\debug\\echoApp.exe"

\n

val createControlledProcess : 路径:字符串 -> 进程

\n

valstartupControlledProcess:p:进程->单元

\n

val shutdownControlledProcess : p:进程 -> 单元

\n

val 交互 : p:Process -> arg:string -> string

\n

val p : 进程 = System.Diagnostics.Process

\n

验证结果:字符串列表=

\n

[“0:\xef\xbb\xbfHello”; “1:世界”;“2:随便”;“3:漂浮”;“4:你的”;《5:船》]

\n

验证它:单位 = ()

\n
\n

我无法重现任何阻塞或死锁等。\n因此,在您的情况下,我会尝试调查您的NewLine属性是否需要一些调整(请参阅函数startupControlledProcess。如果受控应用程序无法将输入识别为一行,它可能不会响应,仍在等待输入行的其余部分,您可能会得到您想要的效果。

\n