การจับเอาท์พุทคอนโซลจากแอปพลิเคชัน. NET (C #)


130

ฉันจะเรียกใช้แอปพลิเคชันคอนโซลจากแอปพลิเคชัน. NET และจับเอาต์พุตทั้งหมดที่สร้างในคอนโซลได้อย่างไร

(โปรดจำไว้ว่าฉันไม่ต้องการบันทึกข้อมูลไว้ในไฟล์ก่อนแล้วจึงวางใจเพราะฉันชอบที่จะรับข้อมูลเป็นแบบสด)



โปรดดูวันที่ของทั้งสองคำถามและดูว่าคำถามใด "ซ้ำกัน"
Gripsoft

"อาจซ้ำกันได้" เป็นวิธีการทำความสะอาด - เพื่อปิดคำถามที่คล้ายกันและเก็บคำตอบที่ดีที่สุดไว้ วันที่ไม่จำเป็น โปรดดูฉันควรลงคะแนนเพื่อปิดคำถามที่ซ้ำกันแม้ว่าจะใหม่กว่ามากและมีคำตอบที่เป็นปัจจุบันมากกว่า หากคุณยอมรับว่าต้องการคำชี้แจงโปรดลงคะแนนในลิงก์เพิ่มคำชี้แจงไปยังความคิดเห็นอัตโนมัติ "อาจซ้ำกัน"
Michael Freidgeim

คำตอบ:


163

สิ่งนี้สามารถทำได้อย่างง่ายดายโดยใช้คุณสมบัติProcessStartInfo.RedirectStandardOutput ตัวอย่างฉบับสมบูรณ์มีอยู่ในเอกสาร MSDN ที่เชื่อมโยง ข้อแม้เดียวคือคุณอาจต้องเปลี่ยนเส้นทางสตรีมข้อผิดพลาดมาตรฐานด้วยเพื่อดูผลลัพธ์ทั้งหมดของแอปพลิเคชันของคุณ

Process compiler = new Process();
compiler.StartInfo.FileName = "csc.exe";
compiler.StartInfo.Arguments = "/r:System.dll /out:sample.exe stdstr.cs";
compiler.StartInfo.UseShellExecute = false;
compiler.StartInfo.RedirectStandardOutput = true;
compiler.Start();    

Console.WriteLine(compiler.StandardOutput.ReadToEnd());

compiler.WaitForExit();

3
หากคุณไม่ต้องการให้ขึ้นบรรทัดใหม่ในตอนท้ายให้ใช้Console.Writeแทน
tm1

2
ควรสังเกตว่าหากคุณใช้ ReadToEnd () ร่วมกับแอปพลิเคชันคอนโซลที่มีความสามารถในการแจ้งให้ผู้ใช้ป้อนข้อมูล เช่น: เขียนทับไฟล์: Y หรือ N? ฯลฯ จากนั้น ReadToEnd อาจส่งผลให้หน่วยความจำรั่วไหลเนื่องจากกระบวนการไม่ออกในขณะที่รอการป้อนข้อมูลของผู้ใช้ วิธีที่ปลอดภัยกว่าในการจับเอาท์พุทคือการใช้กระบวนการตัวจัดการเหตุการณ์ OutputDataReceived และให้กระบวนการแจ้งแอปพลิเคชันของผลลัพธ์ที่จะได้รับ
Baaleos

วิธีการจับภาพหากในกรณีที่โค้ดถูกปรับใช้กับ azure webapp เนื่องจาก compiler.StartInfo.FileName = "csc.exe"; อาจไม่มีอยู่จริง!
Asif Iqbal

วิธีการจับภาพหากในกรณีที่โค้ดถูกปรับใช้กับ azure webapp เนื่องจาก compiler.StartInfo.FileName = "csc.exe"; อาจไม่มีอยู่จริง!
Asif Iqbal

37

นี่คือการปรับปรุงบิตมากกว่าคำตอบที่ได้รับการยอมรับจาก@mdb นอกจากนี้เรายังตรวจจับผลลัพธ์ข้อผิดพลาดของกระบวนการ นอกจากนี้เราจับเอาท์พุทเหล่านี้ผ่านเหตุการณ์เนื่องจากReadToEnd()ไม่ทำงานหากคุณต้องการจับทั้งข้อผิดพลาดและเอาต์พุตปกติ ฉันใช้เวลาพอสมควรในการทำงานนี้เพราะจริงๆแล้วต้องใช้การBeginxxxReadLine()โทรหลังจากนั้นStart()ด้วย

วิธีอะซิงโครนัส:

using System.Diagnostics;

Process process = new Process();

void LaunchProcess()
{
    process.EnableRaisingEvents = true;
    process.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(process_OutputDataReceived);
    process.ErrorDataReceived += new System.Diagnostics.DataReceivedEventHandler(process_ErrorDataReceived);
    process.Exited += new System.EventHandler(process_Exited);

    process.StartInfo.FileName = "some.exe";
    process.StartInfo.Arguments = "param1 param2";
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardError = true;
    process.StartInfo.RedirectStandardOutput = true;

    process.Start();
    process.BeginErrorReadLine();
    process.BeginOutputReadLine();          

    //below line is optional if we want a blocking call
    //process.WaitForExit();
}

void process_Exited(object sender, EventArgs e)
{
    Console.WriteLine(string.Format("process exited with code {0}\n", process.ExitCode.ToString()));
}

void process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
    Console.WriteLine(e.Data + "\n");
}

void process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
    Console.WriteLine(e.Data + "\n");
}

5
ขอบคุณที่มองหาสิ่งนี้มานานแล้ว!
C Bauer

3
ขอบคุณ. สมบูรณ์แบบนี้
DrFloyd5

1
คุณจะได้รับตำแหน่งที่มีเกียรติในรายการขอบคุณสำหรับใบสมัครของฉัน
marsh-wiggle

13

ใช้ProcessInfo.RedirectStandardOutputเพื่อเปลี่ยนทิศทางเอาต์พุตเมื่อสร้างกระบวนการคอนโซลของคุณ

จากนั้นคุณสามารถใช้Process.StandardOutputเพื่ออ่านผลลัพธ์ของโปรแกรม

ลิงค์ที่สองมีโค้ดตัวอย่างวิธีการทำ


7

ConsoleAppLauncherเป็นไลบรารีโอเพ่นซอร์สที่สร้างขึ้นเพื่อตอบคำถามนั้นโดยเฉพาะ จับเอาท์พุททั้งหมดที่สร้างขึ้นในคอนโซลและมีอินเทอร์เฟซที่เรียบง่ายเพื่อเริ่มและปิดแอปพลิเคชันคอนโซล

เหตุการณ์ ConsoleOutput จะเริ่มทำงานทุกครั้งเมื่อมีการเขียนบรรทัดใหม่โดยคอนโซลไปยังเอาต์พุตมาตรฐาน / ข้อผิดพลาด บรรทัดจะอยู่ในคิวและรับประกันว่าจะเป็นไปตามลำดับเอาต์พุต

นอกจากนี้ยังมีแพคเกจ NuGet

ตัวอย่างการโทรเพื่อรับเอาต์พุตคอนโซลแบบเต็ม:

// Run simplest shell command and return its output.
public static string GetWindowsVersion()
{
    return ConsoleApp.Run("cmd", "/c ver").Output.Trim();
}

ตัวอย่างพร้อมข้อเสนอแนะสด:

// Run ping.exe asynchronously and return roundtrip times back to the caller in a callback
public static void PingUrl(string url, Action<string> replyHandler)
{
    var regex = new Regex("(time=|Average = )(?<time>.*?ms)", RegexOptions.Compiled);
    var app = new ConsoleApp("ping", url);
    app.ConsoleOutput += (o, args) =>
    {
        var match = regex.Match(args.Line);
        if (match.Success)
        {
            var roundtripTime = match.Groups["time"].Value;
            replyHandler(roundtripTime);
        }
    };
    app.Run();
}

2

ฉันได้เพิ่มวิธีการช่วยเหลือหลายวิธีในแพลตฟอร์ม O2 (โครงการโอเพ่นซอร์ส) ซึ่งช่วยให้คุณสามารถเขียนสคริปต์การโต้ตอบกับกระบวนการอื่นได้อย่างง่ายดายผ่านเอาต์พุตและอินพุตของคอนโซล (ดูhttp://code.google.com/p/o2platform/ ซอร์ส / เรียกดู / trunk / O2_Scripts / APIs / Windows / CmdExe / CmdExeAPI.cs )

นอกจากนี้ยังมีประโยชน์สำหรับคุณอาจเป็น API ที่ช่วยให้สามารถดูเอาต์พุตคอนโซลของกระบวนการปัจจุบัน (ในตัวควบคุมหรือหน้าต่างป๊อปอัปที่มีอยู่) ดูรายละเอียดเพิ่มเติมในบล็อกโพสต์นี้: http://o2platform.wordpress.com/2011/11/26/api_consoleout-cs-inprocess-capture-of-the-console-output/ (บล็อกนี้ยังมีรายละเอียดวิธีการบริโภค เอาต์พุตคอนโซลของกระบวนการใหม่)


ตั้งแต่นั้นมาฉันได้เพิ่มการสนับสนุนเพิ่มเติมสำหรับการใช้ ConsoleOut (ในกรณีนี้หากคุณเริ่มดำเนินการ. NET ด้วยตนเอง) ลองดูที่: วิธีการใช้งานการส่งออกในคอนโซล C # REPL , เพิ่ม 'คอนโซลออก' เพื่อ VisualStudio IDE เป็นหน้าต่างพื้นเมือง , การดูข้อความ 'คอนโซลออก' สร้างขึ้นภายใน UserControls
Dinis ครูซ

2

ฉันสร้างเวอร์ชันรีแอคทีฟที่ยอมรับการเรียกกลับสำหรับ stdOut และ StdErr
onStdOutและonStdErrเรียกว่าแบบอะซิงโครนัส
ทันทีที่ข้อมูลมาถึง (ก่อนที่กระบวนการจะออก)

public static Int32 RunProcess(String path,
                               String args,
                       Action<String> onStdOut = null,
                       Action<String> onStdErr = null)
    {
        var readStdOut = onStdOut != null;
        var readStdErr = onStdErr != null;

        var process = new Process
        {
            StartInfo =
            {
                FileName = path,
                Arguments = args,
                CreateNoWindow = true,
                UseShellExecute = false,
                RedirectStandardOutput = readStdOut,
                RedirectStandardError = readStdErr,
            }
        };

        process.Start();

        if (readStdOut) Task.Run(() => ReadStream(process.StandardOutput, onStdOut));
        if (readStdErr) Task.Run(() => ReadStream(process.StandardError, onStdErr));

        process.WaitForExit();

        return process.ExitCode;
    }

    private static void ReadStream(TextReader textReader, Action<String> callback)
    {
        while (true)
        {
            var line = textReader.ReadLine();
            if (line == null)
                break;

            callback(line);
        }
    }


ตัวอย่างการใช้งาน

สิ่งต่อไปนี้จะทำงานexecutableพร้อมกับargsพิมพ์

  • stdOut เป็นสีขาว
  • stdErr เป็นสีแดง

ไปที่คอนโซล

RunProcess(
    executable,
    args,
    s => { Console.ForegroundColor = ConsoleColor.White; Console.WriteLine(s); },
    s => { Console.ForegroundColor = ConsoleColor.Red;   Console.WriteLine(s); } 
);


1

เพิ่มprocess.StartInfo.**CreateNoWindow** = true;และtimeout.

private static void CaptureConsoleAppOutput(string exeName, string arguments, int timeoutMilliseconds, out int exitCode, out string output)
{
    using (Process process = new Process())
    {
        process.StartInfo.FileName = exeName;
        process.StartInfo.Arguments = arguments;
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.CreateNoWindow = true;
        process.Start();

        output = process.StandardOutput.ReadToEnd();

        bool exited = process.WaitForExit(timeoutMilliseconds);
        if (exited)
        {
            exitCode = process.ExitCode;
        }
        else
        {
            exitCode = -1;
        }
    }
}

เมื่อคุณใช้StandardOutput.ReadToEnd()มันจะไม่กลับไปที่คำสั่งถัดไปจนกว่าจะสิ้นสุดแอพ ดังนั้นการหมดเวลาของคุณใน WaitForExit (timeoutMilliseconds) จึงไม่ทำงาน! (รหัสของคุณจะค้าง!)
S. Serpooshan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.