3

子コンソール アプリからの出力をキャッチしようとしています。

  • 親がコンソール アプリの場合、すべてが機能します。
  • 親が Windows アプリのConsole.ReadKey()場合、stdInput がリダイレクトされたときに readKey を実行する方法がないという例外が発生し、子は実行に失敗します。ただし、入力をリダイレクトしていません (出力のみ)。

ここで何が欠けていますか?

子コード (プロジェクトをコンソール アプリに設定):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ChildProcess
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            char key = Console.ReadKey().KeyChar;
            Console.Out.WriteLine("stdout");
            Console.Error.WriteLine("stderr");
        }
    }
}

親アプリ コード: (プロジェクトを Windows アプリに設定)

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace RedirectProcessOutput
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            string fileName = @"ChildPRocess.exe";
            string arg = "i";

            string processOutput = "?";

            Process p = new Process();

            p.StartInfo.UseShellExecute = false;
            p.StartInfo.CreateNoWindow = false;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.RedirectStandardError = false;

            p.StartInfo.FileName = fileName;
            p.StartInfo.Arguments = arg;
            p.Start();

            processOutput = p.StandardOutput.ReadToEnd();

            p.WaitForExit();

            Console.WriteLine("The child process output is:" + processOutput);
        }
    }
}

子には独自のコンソールが必要であり、そのための入力をリダイレクトしていないため、親がウィンドウアプリであってもアプリケーションがクラッシュすることなく実行されることを期待していました。ところで-すべてが次の場合に機能します。

  • 子が「ReadKey」を実行していない、または
  • 親は stdout をまったくリダイレ​​クトしていません
4

1 に答える 1

1

親プロセスがコンソールアプリケーションの場合は正常に動作するので、一時的にコンソールアプリケーションに変えてみませんか?

[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool AllocConsole();

[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool FreeConsole();

private static void Main(string[] args)
{
    Process p = new Process();

    p.StartInfo.UseShellExecute = false;
    p.StartInfo.CreateNoWindow = false;
    p.StartInfo.RedirectStandardOutput = true;
    p.StartInfo.RedirectStandardError = false;

    p.StartInfo.FileName = "Child.exe";
    p.StartInfo.Arguments = "i";

    AllocConsole();
    p.Start();
    FreeConsole();

    string processOutput = p.StandardOutput.ReadToEnd();
    p.WaitForExit();

    Debug.WriteLine("The child process output is:" + processOutput);
}

/ /のlock周りを使用すると、それぞれが独自のコンソール ウィンドウを持つ複数の子プロセスを起動することもできます。AllocConsoleStartFreeConsole


これは、正しく機能しなかった古い「ソリューション」です。

CreateProcess フラグを見てください。CREATE_NO_WINDOW を設定しないと、新しいコンソール オブジェクトが得られません。CREATE_NEW_CONSOLE が必要です。[いいえ、これは間違っていました。下記参照]

残念ながら、これは .NET API では公開されていません。Process.Startを使用させれば正しいことをShellExecute行いますが、標準出力をリダイレクトすることはできません。あなたがやろうとしていることは では不可能だと思います。 System.Diagnostics.ProcessP/Invoke が必要CreateProcessです。

ちょうど昨日System.Diagnostics.Process、MS 実装では提供されていない別の機能が必要だったので、独自の代替手段を作成しました。つまり、stdout と stderr の両方を同じストリームにリダイレクトします。私のコードを自由に再利用してください。ただし、まだあまりテストされていないことに注意してください。

ProcessRunner親プロセスがコンソールアプリケーションの場合、子は独自の新しいコンソールウィンドウを取得します(親からのものを再利用するのではなく)System.Diagnostics.Process、それはそれReadKey()からでき、stdout親にリダイレクトされます。

しかし、親プロセスが Windows アプリケーションの場合、これはReadKey()失敗します。stderr出力も表示されません。(これは とまったく同じ動作です。これも機能System.Diagnostics.Processしていないことに気付きませstderrんでした。この理由は、ストリームの 1 つだけをリダイレクトすることはできないためです。STARTF_USESTDHANDLESが設定されるとすぐに、すべてのストリームがリダイレクトされます。 .

親アプリケーションがコンソールの場合、なぜ機能するのかわかりません。リダイレクトされた stdin/stderr ハンドルは、GetStdHandle呼び出された時点で最新のコンソールではなく、現在のコンソールを参照している可能性があります。

ただしGetStdHandle、Windows アプリケーション内で呼び出された場合は、INVALID_HANDLE が返されます。コンソールが作成されると有効になるハンドルを取得する方法があるかどうかはわかりません。回答: そうしないでください。以前にコンソールを作成するだけです (最初の新しいソリューションを参照してください)。

于 2013-04-07T15:55:52.320 に答える