パイプを使った通信
同一マシン上で実行されている2つのアプリケーション間でメッセージのやり取りが必要になることはよくあります。Windowsではこれをプロセス間通信(IPC)と呼んでおり、IPCを実装する方法がいくつか用意されています。例えば、メモリマップドファイル、TCP/IPソケット、または名前付きカーネルオブジェクトを使うなどの方法があります。また、いわゆる名前付きパイプを使う方法もあります。名前付きパイプは、セキュリティやファイアウォールの設定をいじらずにメッセージを送信できる便利で直接的な方法です。.NETのパイププログラミングも、.NET 3.5の新しいSystem.IO.Pipes
名前空間によってさらに簡単になっています。
サンプルアプリケーションでは、パイプが次のように通信に使われています。サンプルアプリケーションがパラメータなしで起動されると、アプリケーションは普通にユーザーインターフェースを開いて、パイプ接続を待機するスレッドを開始します。
一方、ユーザーがジャンプリストからコマンドを選択すると、アプリケーションはコマンドラインパラメータ付きで起動されます。この場合、アプリケーションは既存のサーバーアプリケーションのパイプへのパイプ接続を開き、既に実行中のインスタンスにコマンドラインパラメータを送信します。これを受けて、サーバはコマンドに対応する処理を自由に実行できます。
パイプ処理はPipeCommunications
カスタムクラスに実装されています。本稿の主題はジャンプリストであって、パイプ通信ではありませんが、ジャンプリストを操作するときにIPCが必要になる場面が多いので、コードをひととおり見ておいて損はありません。サンプルアプリケーションがパラメータなしで起動されると、メインウィンドウのコンストラクタが次のコードを実行します。
PipeCommunications pipes = new PipeCommunications( (cmd) => { communicationLogTextBox.Invoke( new LogMessageToTextBoxDelegate( LogMessageToTextBox),cmd); }); pipes.CreateListenPipeThread();
ここでは、パイプ経由で受け取ったコマンドを実際に処理するデリゲートを指定して、PipeCommunications
クラスを初期化しています。パイプ通信は独立したスレッドで行われるので、スレッド処理の問題を起こさずにユーザーインターフェース要素に正しくアクセスするために、WinFormsコントロールのInvoke
メソッドを呼び出す必要があります。ラムダ式を使ってイベントハンドラを作成しています。実際の処理はメインフォームのLogMessageToTextBox
メソッドで行われます。
public delegate void LogMessageToTextBoxDelegate(string cmd); ... private void LogMessageToTextBox(string cmd) { communicationLogTextBox.Text = DateTime.Now.ToLongTimeString() + ": Got command: " + cmd + "\r\n" + communicationLogTextBox.Text; }
この実装では、単に受け取ったコマンドを画面上に記録するだけですが(図7)、独自アプリケーションで何らかの処理を実際に行うことも簡単にできます。例えば、データベースにアクセスしたり、ドキュメントを印刷したり、画面上にダイアログボックスを開いたりできます。
メインフォームのコンストラクタによって呼び出されるCreateListenPipeThread
メソッドは、次のコードを実行します。
using System.IO.Pipes; using System.Threading; ... Action<string> processCommandAction; private static AutoResetEvent closeApplicationEvent; ... internal Thread CreateListenPipeThread() { closeApplicationEvent = new AutoResetEvent(false); Thread serverThread = new Thread( new ParameterizedThreadStart( PipeHandlerServerThread)); serverThread.Start(serverThread); return serverThread; }
このコードは、サーバ側のパイプ通信を処理する新しいスレッドを作成します。また、自動リセットイベントを1つ作成します。アプリケーションが終了しようとするときに、Program.csファイルによってこのイベントが通知(設定)されます。このようなイベントを使わないと、メインフォームは正常に閉じても、パイプスレッドが実行されたままになります。従って、アプリケーションを完全に閉じるためにスレッドを正しく終了する必要があります。