はじめに
x64 と x86 のコードを同一プロセス内で混在させることができないため、名前付きパイプを使用したプロセス間通信 (IPC) で対処した時の備忘録です。
改訂履歴
- 2026/08/10 : 初版公開。
本文
1. 環境
- .NET 8.0
- Visual Studio Community 2022
2. 実装
クライアント側では NamedPipeClientStream を使用して名前付きパイプに接続します。メッセージを送信した後、サーバーからの応答を受信します。CancellationToken を使用して、処理のキャンセルも可能です。
NamedPipeClient.cs
using System.IO.Pipes;
using Microsoft.Extensions.Logging;
namespace NamedPipeExample64;
/// <summary>
/// 名前付きパイプを使用して、サーバーに要求を送信し、応答を受信するためのクラスを提供します。
/// </summary>
public class NamedPipeClient
{
private readonly ILogger _logger;
private readonly string _serverName;
private readonly string _pipeName;
/// <summary>
/// <see cref="NamedPipeClient"/> クラスの新しいインスタンスを初期化します。
/// </summary>
/// <param name="logger">ログ出力に使用するロガー。</param>
/// <param name="serverName">名前付きパイプサーバーの名前。既定値は <c>"."</c> で、ローカルコンピューターを指定します。</param>
/// <param name="pipeName">接続する名前付きパイプの名前。既定値は <c>"NamedPipe"</c> です。</param>
public NamedPipeClient(
ILogger logger,
string serverName = ".",
string pipeName = "NamedPipe")
{
_logger = logger;
_serverName = serverName ?? string.Empty;
_pipeName = pipeName ?? string.Empty;
}
/// <summary>
/// 指定されたメッセージをサーバーに送信し、応答を非同期に受信します。
/// </summary>
/// <param name="message">サーバーに送信するメッセージ。</param>
/// <param name="token">キャンセル要求を監視するトークン。既定値は <see cref="CancellationToken.None"/> です。</param>
/// <returns>処理が正常に完了したかどうかと、サーバーから受信した応答文字列。</returns>
public async Task<(bool IsSuccess, string Response)> ExecuteAsync(
string message,
CancellationToken token = default)
{
try
{
// 名前付きパイプのストリームを生成、接続する。
using var pipe = new NamedPipeClientStream(_serverName, _pipeName, PipeDirection.InOut);
await pipe.ConnectAsync(token);
// 読込と書込のストリームを生成する。
using var reader = new StreamReader(pipe);
using var writer = new StreamWriter(pipe) { AutoFlush = true };
// 書込する。
await writer.WriteLineAsync(message ?? string.Empty);
// 応答を読込して結果を返す。
var response = await reader.ReadLineAsync(token) ?? string.Empty;
return (true, response);
}
catch (OperationCanceledException)
{
// キャンセルは正常終了とする。
return (true, string.Empty);
}
catch (Exception ex)
{
_logger.LogError(ex, "An exception occurred.");
return (false, string.Empty);
}
}
}
サーバー側では NamedPipeServerStream を使用してクライアントからの接続を待機します。メッセージを受信した後、動作確認のため 5 秒待機し、受信したメッセージをそのままクライアントに返信します。クライアントと同様、CancellationToken を使用して、処理のキャンセルも可能です。
NamedPipeServer.cs
using System.IO.Pipes;
using Microsoft.Extensions.Logging;
namespace NamedPipeExample86;
/// <summary>
/// 名前付きパイプを使用して、クライアントから要求を受信し、応答するクラスを提供します。
/// </summary>
public class NamedPipeServer
{
private readonly ILogger _logger;
private readonly string _pipeName;
/// <summary>
/// <see cref="NamedPipeServer"/> クラスの新しいインスタンスを初期化します。
/// </summary>
/// <param name="logger">ログ出力に使用するロガー。</param>
/// <param name="pipeName">接続する名前付きパイプの名前。既定値は <c>"NamedPipe"</c> です。</param>
public NamedPipeServer(ILogger logger, string pipeName = "NamedPipe")
{
_logger = logger;
_pipeName = pipeName ?? string.Empty;
}
/// <summary>
/// クライアントからの要求を受信し、応答を返します。
/// </summary>
/// <param name="token">キャンセル要求を監視するトークン。既定値は <see cref="CancellationToken.None"/> です。</param>
/// <returns>処理が正常に完了したかどうかと、クライアントから受信した要求文字列。</returns>
public async Task<(bool IsSuccess, string Response)> ExecuteAsync(CancellationToken token = default)
{
try
{
// 名前付きパイプのストリームを生成する。
using var pipe = new NamedPipeServerStream(_pipeName, PipeDirection.InOut);
// クライアントからの接続を待機する。
await pipe.WaitForConnectionAsync(token);
// 読込と書込のストリームを生成する。
using var reader = new StreamReader(pipe);
using var writer = new StreamWriter(pipe) { AutoFlush = true };
// 読込する。
var message = await reader.ReadLineAsync(token) ?? string.Empty;
// 動作確認のため 5 秒待機する。
await Task.Delay(TimeSpan.FromSeconds(5), token);
// 応答を書込して結果を返す。
await writer.WriteLineAsync(message);
return (true, message);
}
catch (OperationCanceledException)
{
// キャンセルは正常終了とする。
return (true, string.Empty);
}
catch (Exception ex)
{
_logger.LogError(ex, "An exception occurred.");
return (false, string.Empty);
}
}
}
おわりに
既存の x86 ライブラリを x64 アプリケーションから利用したい場合など、ちょっとした処理を別プロセスに切り出す用途で使えそうです。