子プロセスの標準ストリームとして取得できるオブジェクトはStreamWriter/StreamReaderであるため、バイナリ形式での読み書きには向いていません。 また、非同期読み込みでのイベント引数DataReceivedEventArgsも文字列型(≒テキスト形式)として受信されるようになっています。

子プロセスの標準ストリームに対してバイナリ形式の読み書きを行う場合は、まず標準ストリームのStreamWriter/StreamReaderからBaseStreamプロパティを参照することにより、標準ストリームのStreamを取得します。

次に、BaseStreamプロパティから取得したStreamに対して直接読み書きを行うか、StreamからBinaryWriter/BinaryReaderなどを作成して読み書きすることにより、標準ストリームでバイナリ形式のデータを扱うことができるようになります。

parent.exe:子プロセスの標準ストリームからBinaryWriter/BinaryReaderを作成して読み書きする
using System;
using System.Diagnostics;
using System.IO;
using System.Text;

class Sample {
  static void Main()
  {
    // 子プロセスchild.exeの起動オプション
    var psi = new ProcessStartInfo("child.exe") {
      // シェルを使用せず子プロセスを起動する
      // (リダイレクトするために必要)
      // ⚠.NET Core/.NET 5以降ではデフォルトでfalseとなっている
      UseShellExecute = false,

      // 起動した子プロセスの標準入出力をリダイレクトする
      RedirectStandardInput = true,
      RedirectStandardOutput = true,
    };

    // 子プロセスを起動する
    using (var child = Process.Start(psi)) {
      // リダイレクトされた子プロセスの標準入出力を取得する
      var stdin  = child.StandardInput;
      var stdout = child.StandardOutput;

      // BaseStreamを参照して標準入出力のStreamを取得し、
      // BinaryWriter/BinaryReaderを作成する
      using (var stdinWriter = new BinaryWriter(stdin.BaseStream)) {
        // 6バイトのバイナリデータを標準入力へ書き込む
        stdinWriter.Write(new byte[] {0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x21});
      } // 子プロセスの標準入力を閉じて書き込みを終了する

      byte[] output = null;

      using (var stdoutReader = new BinaryReader(stdout.BaseStream)) {
        // 標準出力から最大16バイトのバイナリデータを読み込む
        output = stdoutReader.ReadBytes(16);
      }

      // 子プロセスの終了を待機する
      child.WaitForExit();

      // 読み込んだバイナリデータ文字列に変換して表示する
      Console.WriteLine(Encoding.ASCII.GetString(output));
    }
  }
}
child.exe:標準入力の内容をBASE64エンコードして標準出力に書き込む
using System;
using System.IO;
using System.Security.Cryptography;

class Sample {
  static void Main()
  {
    // 標準入力のStreamを取得
    var input = Console.OpenStandardInput();

    // 標準出力のStreamをベースに、書き込まれる内容を
    // BASE64エンコードするCryptoStreamを作成する
    using (var output = new CryptoStream(
      Console.OpenStandardOutput(),
      new ToBase64Transform(),
      CryptoStreamMode.Write,
      leaveOpen: true
    )) {
      // 入力ストリームの内容を出力ストリームに書き込む
      // (BASE64エンコードされた上で書き込まれる)
      input.CopyTo(output);
    }
  }
}
実行結果
>parent.exe
SGVsbG8h

StandardInput/StandardOutput/StandardErrorで取得できるStreamWriter/StreamReaderは内部でバッファリングが行われます。 このため、StandardInput/StandardOutput/StandardErrorを使った読み書きと、BaseStreamから取得したStreamへの読み書きを混在させると入出力内容に不整合が起こる(入出力されるデータの一部の順序が前後する)可能性があり、両者の使用は排他的とする必要があります。

つまり、BaseStreamから取得したStreamで読み書きを行う場合は、それのみを使って読み書きを行う、あるいはどちらか一方をFlushするまではもう一方を使って読み書きしないようにします。

Windows上では、BaseStreamプロパティから取得したStreamを閉じたあとにStandardOutput/StandardErrorを閉じると、StreamWriterがFlushしようとして例外ObjectDisposedExceptionがスローされます。 そのため、StreamWriterとBaseStreamプロパティから取得したStreamの両方をCloseしないように(二重にCloseする動作とならないように)注意する必要があります。

自プロセスの標準ストリームのStreamを取得する方法については自プロセスの標準入出力 §.標準ストリームの取得を参照してください。

Streamクラスを使った読み書きの方法についてはストリームの基本とStreamクラス、BinaryWriterクラス/BinaryReaderクラスを使った読み書きの方法についてはBinaryReaderクラス・BinaryWriterクラスを参照してください。

MENTAにてテキストチャットによるサポートを行っています。 問題の解決法をお探しの方や.NET/C#を学習中の方はどうぞご利用ください。