簡體   English   中英

如何從另一個 .NET 程序使用交互式命令行程序

[英]How to use an interactive command line program from another .NET program

我需要為交互式命令行程序編寫一個包裝器。

這意味着我需要能夠通過其標准輸入向另一個程序發送命令並通過其標准輸出接收響應。

問題是,當輸入流仍然打開時,標准輸出流似乎被阻塞了。 一旦我關閉輸入流,我就會得到響應。 但后來我無法發送更多命令。

這是我目前正在使用的(主要來自這里):

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

我還嘗試同步讀取標准輸出流,但結果相同。 對輸出流的任何方法調用都會無限期地阻塞,直到輸入流關閉 - 甚至是Peek()EndOfStream

有沒有辦法以全雙工方式與其他進程通信?

我試圖用我自己的一個小型測試套件重現您的問題。 我沒有使用事件處理程序,而是以我能想到的最簡單的方式做到這一點:同步。 這樣就不會給問題增加額外的復雜性。

在這里,我用 rust 寫了我的小“echoApp”,只是為了笑聲,也有機會遇到永恆的線路終止戰爭問題( \\n vs \\r vs \\r\\n )。 根據您編寫命令行應用程序的方式,這確實可能是您的問題之一。

use std::io;

fn main() {
    let mut counter = 0;
    loop {
        let mut input = String::new();
        let _ = io::stdin().read_line(&mut input);
        match &input.trim() as &str {
            "quit" => break,
            _ => {
                println!("{}: {}", counter, input);
                counter += 1;
            }
        }
    }
}

而且 - 作為一個懶惰的人,不喜歡為這樣一個小測試創建解決方案,我使用 F# 而不是 C# 作為控制端 - 我認為它很容易閱讀:

open System.Diagnostics;

let echoPath = @"E:\R\rustic\echo\echoApp\target\debug\echoApp.exe"

let createControlledProcess path = 
    let p = new Process()
    p.StartInfo.UseShellExecute <- false
    p.StartInfo.RedirectStandardInput <- true
    p.StartInfo.RedirectStandardOutput <- true
    p.StartInfo.Arguments <- ""
    p.StartInfo.FileName <- path
    p.StartInfo.CreateNoWindow <- true
    p

let startupControlledProcess (p : Process) =
    if p.Start() 
    then 
        p.StandardInput.NewLine <- "\r\n"
    else ()

let shutdownControlledProcess (p : Process) =
    p.StandardInput.WriteLine("quit");
    p.WaitForExit()
    p.Close()

let interact (p : Process) (arg : string) : string =
    p.StandardInput.WriteLine(arg);
    let o = p.StandardOutput.ReadLine()
    // we get funny empty lines every other time... 
    // probably some line termination problem ( unix \n vs \r\n etc - 
    // who can tell what rust std::io does...?)
    if o = "" then p.StandardOutput.ReadLine()
    else o

let p = createControlledProcess echoPath
startupControlledProcess p
let results = 
    [
        interact p "Hello"
        interact p "World"
        interact p "Whatever"
        interact p "floats"
        interact p "your"
        interact p "boat"
    ]
shutdownControlledProcess p

在 f# Interactive 中執行此操作(Visual Studio 中的 CTRL-A ALT-Enter)會產生:

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

val createControlledProcess : path:string -> Process

val startupControlledProcess : p:Process -> unit

val shutdownControlledProcess : p:Process -> unit

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

val p : Process = System.Diagnostics.Process

val 結果:字符串列表 =

["0: 你好"; “1:世界”; “2:隨便”; “3:浮動”; “4:你的”; “5:船”]

驗證它:單位 = ()

我無法重現任何阻塞或死鎖等。因此,在您的情況下,我會嘗試調查您的NewLine屬性是否需要進行一些調整(請參閱函數startupControlledProcess 。如果受控應用程序無法將輸入識別為一行,則它可能不會響應, 仍在等待輸入行的其余部分,您可能會得到您所擁有的效果。

process.BeginOutputReadLine();

不像預期的那樣工作,因為它一直等到輸出流關閉,這將在進程結束時發生,而在其輸入流將關閉時進程將結束。 作為解決方法,只需使用process.StandardOutput.ReadLine()和自己制作的異步的組合

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM