レガシー Windows API(概要)について

ここで紹介しているAPIは、Windows95、Windows NT3.5時代のAPI群であり、最新のAPIは含まれていないことに注しして欲しい。なお、当時の解説書は現在よりもより詳細に解説しているため、参考になるはずである。

  • レイアウトを変換する際、崩れているページがあります。
  • 概要およびSampleコードはありますが、APIの解説はありません。

ご要望があれば旧版のWin32 API(HELP形式)をお譲りします。問い合わせフォームより申し込みください。

Windows API パイプの概要

「パイプ」とは、 2つの端を持つ通信経路です。一方の端のハンドルを持つプロセスは、 もう一方の端のハンドルを持つプロセスと通信できます。パイプは、 単方向 (一端が読み取り専用でもう一端が書き込み専用) か、 双方向 (両端で読み書き可能) にできます。Microsoft(R) Win32(R) アプリケーション プログラミング インターフェイス (API) では、 名前なしパイプと名前付きパイプが用意されています。このトピックでは、 パイプを作成、 管理、 および使用する方法について説明します。

次に示すトピックでは、 パイプについて説明しています。

名前なしパイプ

名前付きパイプ

名前付きパイプのモード

名前付きパイプの操作

名前付きパイプのサーバー プロセス

パイプの名前

名前付きパイプのタイプと読み取りモード

ブロック パイプ操作と非ブロック パイプ操作

同期I/OとオーバーラップI/O

リモート クライアントへのライト スルー

パイプのセキュリティ

名前なしパイプの使用

マルチスレッド サーバー

オーバーラップI/Oを使うサーバー

完了ルーチンを使うサーバー

名前付きパイプのクライアント プロセス

名前付きパイプでのトランザクション

パイプ関数

名前なしパイプ

「名前なしパイプ」とは、 親プロセスと子プロセスの間や、 同じ親プロセスを持つ子プロセスの間でデータを転送するための、 名前のない単方向パイプです。関連のないプロセス間の通信に名前なしパイプを使うこともできますが、 そのような場合には名前付きパイプを使った方が簡単です (名前付きパイプを参照してください)。名前なしパイプは常にローカルで、 ネットワークを通じた通信には使えません。

CreatePipe関数は、 名前なしパイプを作成して、 2つのハンドルを返します。1つはパイプの読み取り側を識別し、 もう1つはパイプの書き込み側を識別します。読み取りハンドルではパイプに読み取りアクセスしかできず、 書き込みハンドルでは書き込みアクセスしかできません。パイプで通信するには、 パイプの一端のハンドルをほかのプロセスに渡さなければなりません。通常、 ハンドルを渡すには、 継承を使います。子の場合、 子プロセスは、 親プロセスからハンドルを継承します。ハンドルが継承されるかどうかは、 作成側のプロセスが次のような方法で制御できます。

CreatePipe関数に、 SECURITY_ATTRIBUTES構造体を指定します。この構造体のbInheritHandleメンバがTRUEならば、 ハンドルは継承されます。

DuplicateHandle関数で、 パイプ ハンドルの継承方法を変更します。これによって、 継承可能なパイプ ハンドルから継承不可能な複製を作成したり、 継承不可能なパイプ ハンドルから継承可能な複製を作成できます。

CreateProcess関数に、 親プロセスの継承可能ハンドルを新しい子プロセスが継承するかどうかを指定します。

子プロセスがハンドルを継承すると、 システムは、 そのハンドルが参照するパイプを子プロセスがアクセスできるようにします。しかし、 パイプにアクセスするには、 親プロセスがハンドルの値を子プロセスに渡し、 子プロセスはそれを取得しなければなりません。子の作業を行うには、 SetStdHandle関数とGetStdHandle関数を使います。この2つの関数は、 プロセスの標準ハンドル (標準入力、 標準出力、 標準エラー出力) を設定、 取得します。親プロセスは、 子プロセスを作成する前に、 パイプ ハンドルを指定してSetStdHandleを呼び出します。親プロセスの現在の標準ハンドルは、 子プロセスに継承されます。このため、 子プロセスは、 起動したら、 GetStdHandle関数を使ってハンドルの値を取得できます。親プロセスは、 既存の子プロセスの標準ハンドルに影響を与えずに、 自分自身の標準ハンドルを変更できます。

パイプから読み取るには、 読み取りハンドルを指定してReadFile関数を呼び出します。エラーが発生しなければ、 ReadFileは、 指定されたバイト数を読み取るかパイプの書き込み側のハンドルがすべてクローズされるまで戻りません。

パイプに書き込むには、 書き込みハンドルを指定してWriteFile関数を呼び出します。WriteFileは、 指定されたバイト数を書き込むか、 エラーが発生するまで戻りません。パイプのバッファがいっぱいで、 書き込むデータがまだあるときは、 ほかのプロセスやスレッドがパイプからデータを読み取ってバッファの空き領域が増えるまで、 WriteFileは戻りません。作成側プロセスは、 CreatePipeによってパイプのバッファ サイズを指定するか、 デフォルトのバッファ サイズを使うことができます。

名前なしパイプでは、 非同期 (オーバーラップ) 読み書き操作はサポートされていません。つまり、 名前なしパイプにはReadFileEx関数やWriteFileEx関数は使えません。名前なしパイプの場合、 ReadFile関数やWriteFile関数のlpOverLappedパラメータは無視されます。

名前なしパイプは、 パイプの読み取り側と書き込み側の両方のハンドルがCloseHandle関数でクローズされるまで存在します。

名前付きパイプ

「名前付きパイプ」は、 1つのサーバー プロセスと1つ以上のクライアント プロセスが通信するときに使われる単方向または双方向のパイプです。サーバー プロセスは、 名前を指定してCreateNamedPipe関数を呼び出すことによって名前付きパイプのインスタンスを作成します。1つの名前付きパイプのインスタンスはすべて同じパイプ名を共有しますが、 各インスタンスにはそれぞれ専用のバッファとハンドルがあり、 独立したクライアント サーバー通信経路を構成しています。パイプ名を指定してCreateFile関数やCallNamedPipe関数を呼び出したクライアント プロセスは、 パイプのインスタンスに接続されます。これによって、 複数のクライアント プロセスが同時に同じ名前のパイプを使うことができます。

セキュリティ チェックに通過すればどのクライアント プロセスでも名前付きパイプにアクセスできるため、 関連するプロセス間でも関連しないプロセス間でも通信が容易になります。名前付きパイプをローカルに使って同じマシンのプロセス間で通信したり、 ネットワーク上で使って別のマシンのプロセスを接続することもできます。

Windows NTでは、 プロセスはサーバーとクライアントの両方として動作できるため、 クライアント プロセスとサーバー プロセスの違いは小さくなっており、 ピア ツー ピアの通信が可能です。ここでは、 サーバーとはCreateNamedPipeを使って名前付きパイプを作成したプロセスで、 クライアントとはCreateFileCallNamedPipeを使って名前付きパイプのインスタンスに接続したプロセスです。

パイプの名前

各名前付きパイプには、 ほかの名前付きパイプと区別するための一意な名前がなければなりません。CreateNamedPipe関数、 CreateFile関数、 WaitNamedPipe関数、 CallNamedPipe関数でパイプの名前を指定するときは、 次に示す形式を使います。

\\servername\pipe\pipename

サーバー プロセスは、 ある特定のコンピュータ上で使用する目的でパイプを作成することはできません。このため、 CreateNamedPipeには、 次のように、 ピリオドを使ってローカル コンピュータを指定してください。

\\.\pipe\pipename

CreateFile WaitNamedPipe CallNamedPipeを使うクライアント プロセスは、 ピリオドを使ってローカル パイプを指定できますが、 リモート コンピュータのパイプのオープンするにはサーバー名を指定しなければなりません。

名前のpipename部分には、 数字や特殊記号を含む任意の文字を指定できます。パイプ名全体の文字数は、 256文字までです。パイプ名の大文字と小文字は区別されません。

名前付きパイプのモード

名前付きパイプや名前付きパイプのハンドルに関するモードは、 パイプの用途と動作に影響します。モードには、 パイプの各ハンドルに対してそれぞれ設定できるものと、 1つのパイプのすべてのハンドルで同じ設定にしなければならないものがあります。

サーバー プロセスは、 CreateNamedPipe関数を呼び出すときに、 次に示すモードを設定します。タイプ モードとアクセス モードは、 パイプのすべてのインスタンスで同じでなければなりません。

モード 意味

タイプ PIPE_TYPE_BYTEまたはPIPE_TYPE_MESSAGEです。タイプ モードによって、 名前付きパイプにデータがメッセージのストリームとして書き込まれるか、 バイトのストリームとして書き込まれるかが決まります。CreateNamedPipeでタイプを指定しないときのデフォルトはPIPE_TYPE_BYTEです。バイト パイプのハンドルは、 バイト読み取りモードでなければなりません。メッセージ パイプのハンドルは、 バイト読み取りモードとメッセージ読み取りモードのどちらかにできます。パイプ タイプについて詳しくは、 名前付きパイプのタイプと読み取りモードを参照してください。

アクセス PIPE_ACCESS_INBOUND PIPE_ACCESS_OUTBOUND、 またはPIPE_ACCESS_DUPLEXです。このモードは、 サーバーのパイプ ハンドルの読み書きアクセスを指定するのと等価です。PIPE_ACCESS_INBOUND (入力) GENERIC_READアクセス、 PIPE_ACCESS_OUTBOUND (出力) GENERIC_WRITEアクセス、 PIPE_ACCESS_DUPLEX (双方向) GENERIC_READ | GENERIC_WRITEアクセスにそれぞれ対応します。CreateFileを使って名前付きパイプに接続するクライアント プロセスは、 サーバーが指定したアクセスと互換性のあるアクセスを指定しなければなりません。たとえば、 サーバーが出力パイプを指定している場合、 クライアントはGENERIC_READアクセスを指定しなければなりません。

上記以外のパイプ モードは、 各パイプ ハンドルごとに設定できます。つまり、 同じパイプ インスタンスのクライアント側とサーバー側で異なるモードが使えます。CreateNamedPipe関数は、 サーバーのパイプ ハンドルのこのようなモードをすべて設定します。CreateFile関数は、 クライアント側パイプ ハンドルのオーバーラップ モードとライト スルー モードの設定をサポートしています。クライアント プロセスとサーバー プロセスは、 どちらも、 SetNamedPipeHandleState関数を使って、 パイプ ハンドルの読み取りモードと待機モードを設定できます。

モード 意味

読み取り PIPE_READMODE_BYTEまたはPIPE_READMODE_MESSAGEです。バイト読み取りモードでは、 データは名前付きパイプからバイト ストリームとして読み取られます。メッセージ読み取りモードでは、 データは名前付きパイプからメッセージ ストリームとして読み取られます。バイト タイプ パイプではバイト読み取りモードしか使えません。メッセージ タイプ パイプでは、 バイト読み取りモードとメッセージ読み取りモードの両方が使えます。CreateNamedPipeで読み取りモードを指定しなかったときのデフォルトはバイト タイプ モードです。読み取りモードについて詳しくは、 名前付きパイプのタイプと読み取りモードを参照してください。

待機 PIPE_WAITまたはPIPE_NOWAITです。ブロック待機モードでは、 ReadFile関数、 WriteFile関数、 ConnectNamedPipe関数は、 条件が成立するまで (たとえば、 パイプの反対側のプロセスが何らかの動作を実行するまで) 永久に待ち続けます。非ブロック待機モードでは、 操作をすぐに終了できなければ関数は戻ります。待機モードについて詳しくは、 ブロック パイプ操作と非ブロック パイプ操作を参照してください。

オーバーラップ FILE_FLAG_OVERLAPPEDです。オーバーラップ モードでは、 読み書きや接続に時間がかかる関数は、 すぐに戻ります。これによって、 スレッドは、 時間のかかる操作をバックグラウンドで実行しながらほかの処理を実行できます。オーバーラップ モードについて詳しくは、 同期I/OとオーバーラップI/Oを参照してください。

ライト スルー FILE_FLAG_WRITE_THROUGHです。ライト スルー モードでは、 名前付きパイプに書き込む関数は、 書き込んだデータがネットワークを通じて転送され、 リモート コンピュータのパイプのバッファに届くまで戻りません。このモードは、 バイト タイプ パイプの操作で、 クライアント プロセスとサーバー プロセスが異なるコンピュータにあるときだけ影響します。ライト スルー モードが設定されていなければ、 書き込み関数は、 データが転送されていなくても正常に終了します。ライト スルー モードについて詳しくは、 リモート クライアントへのライト スルーを参照してください。

パイプ ハンドルの初期読み取りモードは、 サーバー プロセスがCreateNamedPipeを呼び出すときに指定します。PIPE_READMODE_BYTEを指定するか、 読み取りモードを指定しなければ、 パイプ ハンドルはバイト読み取りモードで作成されます。PIPE_READMODE_MESSAGEPIPE_TYPE_MESSAGEの両方を指定すると、 パイプ ハンドルはメッセージ読み取りモードで作成されます。クライアント プロセスでは、 CreateFile関数が返すパイプ ハンドルの読み取りモードは最初は常にバイト読み取りモードです。クライアント プロセスとサーバー プロセスは、 どちらも、 SetNamedPipeHandleState関数を使ってパイプ ハンドルの読み取りモードを変更できます。メッセージ タイプ パイプの場合、 同じパイプ インスタンスのサーバー ハンドルとクライアント ハンドルの読み取りモードに異なるモードを設定できます。

名前付きパイプの操作

サーバーは、 最初にCreateNamedPipeを呼び出すとき、 同時に存在できるパイプのインスタンスの最大数を指定します。サーバーは、 CreateNamedPipeを呼び出して、 パイプの追加インスタンスをこの個数まで作成できます。関数は、 正常に終了すると、 名前付きパイプ インスタンスのサーバー側ハンドルを返します。

パイプ インスタンスが作成されたら、 クライアント プロセスはCreateFile関数やCallNamedPipe関数を呼び出してそのパイプ インスタンスに接続できます。パイプ インスタンスが利用可能ならば、 CreateFileは、 パイプ インスタンスのクライアント側ハンドルを返します。利用可能なパイプのインスタンスがなければ、 クライアント プロセスはWaitNamedPipe関数を使って利用可能になるのを待つことができます。

また、 クライアント プロセスは、 CallNamedPipe関数を使って、 パイプ インスタンスへの接続 (必要ならば、 利用可能になるまで待機します)、 メッセージの書き込み、 メッセージの読み取り、 パイプ ハンドルのクローズを単一の操作で実行できます。CallNamedPipe関数は、 クライアント プロセスからしか使えません。また、 メッセージ タイプのパイプでしか使えません。

サーバー プロセスは、 パイプ インスタンスのハンドルを指定してConnectNamedPipe関数を呼び出すことによって、 クライアント プロセスのパイプへの接続を判断できます。パイプ ハンドルがブロック モードならば、 ConnectNamedPipe操作はクライアントが接続されるまで戻りません。

クライアント プロセスやサーバー プロセスは、 CallNamedPipeのほかにも、 名前付きパイプを読み書きする関数を呼び出すことができます。このような関数の動作は、 パイプのタイプや指定されたパイプ ハンドルのモードによって異なります。

ReadFile関数とWriteFile関数は、 バイト タイプ パイプとメッセージ タイプ パイプのどちらでも使えます。

ReadFileEx関数とWriteFileEx関数は、 パイプがオーバーラップ操作でオープンされていれば、 バイト タイプ パイプとメッセージ タイプ パイプのどちらでも使えます。

PeekNamedPipe関数は、 バイト タイプ パイプやメッセージ タイプ パイプの内容を削除せずに読み取ることができます。また、 PeekNamedPipeは、 パイプ インスタンスに関する詳細な情報も返します。

呼び出し側プロセスのパイプ ハンドルがメッセージ読み取りモードに設定されていれば、 TransactNamedPipe関数は、 メッセージ タイプ パイプで使えます。この関数は、 要求されたメッセージの書き込みと応答メッセージの読み取りを単一操作で行うため、 ネットワークの効率が向上します。

バイト読み取りモードのハンドルの場合、 パイプ内の利用可能なバイトをすべて読み取るか、 指定されたバイト数を読み取ると、 読み取り操作は正常に完了します。

メッセージ読み取りモードのハンドルの場合、 ReadFile操作やReadFileEx操作は、 メッセージ全体を読み取ったときだけ正常に完了します。指定された読み取りバイト数が次のメッセージのサイズより小さければ、 この関数は、 メッセージをできるだけ読み取ってからFALSEを返します (GetLastError関数はERROR_MORE_DATAを返します)。この場合、 さらにReadFileReadFileEx PeekNamedPipeを呼び出してメッセージの残りを読み取ることができます。PeekNamedPipeは、 メッセージの全体または一部を読み取るとTRUEを返し、 部分的に読み取ったメッセージの残りバイト数を報告します。

メッセージ タイプ パイプに複数の未読メッセージがある場合、 メッセージ読み取りパイプ ハンドルによる読み取り操作は、 メッセージを1つ読み取ると戻ります。バイト読み取りハンドルによる操作ではメッセージは区別されないため、 指定されたバイト数までできる限り読み取ります。

クライアントとサーバーがパイプ インスタンスを使い終わると、 サーバーはDisconnectNamedPipe関数を呼び出して、 クライアント プロセスへの接続をクローズします。この関数は、 クライアント側のハンドルがまだクローズされていなければそれを無効にします。パイプ内の未読データは廃棄されます。パイプに書き込んだバイトまたはメッセージをクライアントがすべて読み取るようにするには、 サーバーは、 まずFlushFileBuffers関数を呼び出してください。この関数は、 クライアントがパイプのデータをすべて読み取るまで戻りません。クライアントが接続解除されたら、 サーバーは、 CloseHandle関数を呼び出してパイプ インスタンスのサーバー側ハンドルをクローズするか、 ConnectNamedPipeを使って、 新しいクライアントがこのパイプ インスタンスに接続できるようにしてください。

名前付きパイプに関する情報を取得するには、 GetNamedPipeInfo関数を呼び出してください。この関数は、 パイプのタイプ、 入力バッファと出力バッファのサイズ、 作成可能なパイプ インスタンスの最大数を返します。GetNamedPipeHandleState関数は、 パイプハンドルの読み取りモードと待機モード、 現在のパイプ インスタンスの個数、 ネットワークを通じて通信するパイプの詳細な関連情報を返します。SetNamedPipeHandleState関数は、 パイプ ハンドルの読み取りモードと待機モードを設定します。また、 リモート サーバーと通信するクライアントの場合、 この関数は、 メッセージが1つ送られるまでに必要な最大バイト数または最大待機時間を設定します (クライアント ハンドルがライト スルー モードでオープンされていないと仮定します)

名前付きパイプのサーバー プロセス

最も簡単なサーバーでは、 CreateNamedPipeを使ってパイプのインスタンスを1つだけ作成し、 単一のクライアントへの接続と通信、 パイプの接続解除とパイプ ハンドルのクローズ、 終了を順に行うだけです。しかし、 通常、 サーバー プロセスは、 複数のクライアント プロセスを通信しなければなりません。パイプ インスタンスを1つだけ使って、 各クライアントと順番に接続、 接続解除することもできますが、 性能は低下します。複数のクライアントを同時に処理するには、 サーバー プロセスは複数のパイプ インスタンスを作成してください。

Win32 APIでは、 複数のパイプ インスタンスを扱うための3つの基本的な方法をサポートしています。サーバー プロセスの行う処理を次に示します。

パイプの各インスタンスごとにスレッド (またはプロセス) を作成します。マルチスレッド サーバー プロセスの例については、 マルチスレッド サーバーを参照してください。

ReadFile関数、 WriteFile関数、 ConnectNamedPipe関数にOVERLAPPED構造体を指定して、 操作をオーバーラップします。オーバーラップ操作を使うサーバー プロセスの例については、 オーバーラップI/Oを使うサーバーを参照してください。

操作が完了すると実行される完了ルーチンを指定してReadFileEx関数やWriteFileEx関数を呼び出して、 操作をオーバーラップします。完了ルーチンを使うサーバーの例については、 完了ルーチンを使うサーバーを参照してください。

マルチスレッド サーバーによる方法は、 各インスタンス ハンドルのスレッドは1つのクライアントだけと通信するため、 簡単に実現できます。システムは、 必要に応じて各スレッドにプロセッサ時間を割り当てます。しかし、 各スレッドはシステム リソースを使うため、 多数のクライアントを扱うサーバーの場合は問題になる可能性があります。また、 あるクライアントが動作するのにほかのクライアントと通信しなければならない場合にも問題が発生します (たとえば、 ネットワーク ゲーム プログラムの場合、 あるプレーヤの移動をほかのプレーヤに知らせなければなりません)

単一スレッド サーバーの場合、 複数のクライアントに影響する操作を調整させやすくなり、 (データベース ファイルなどの) 共有リソースが複数のクライアントから同時にアクセスされないようにする処理も簡単になります。単一スレッド サーバーの問題は、 クライアントの同時要求の処理にプロセッサ時間を割り当てるためオーバーラップ操作を調整させなければならない点です。

名前付きパイプのタイプと読み取りモード

パイプのタイプによってデータを名前付きパイプに書き込む方法が決まり、 読み取りモードによってデータを名前付きパイプから読み取る方法が決まります。データは、 バイトのストリームまたはメッセージのストリームとして名前付きパイプで転送できます。CreateNamedPipe関数を使って名前付きパイプのインスタンスを作成するとき、 サーバー プロセスは、 どのインスタンスにもタイプを指定しなければなりません。

PIPE_TYPE_BYTEを指定するか、 タイプを指定しないと、 バイト パイプが作成されます。バイト パイプの場合、 データはバイトのストリームとしてパイプに書き込まれ、 システムは別の書き込み操作で書き込まれたバイトを区別しません。バイト タイプ パイプのハンドルはバイト読み取りモードにしかできません。

PIPE_TYPE_MESSAGEを指定すると、 メッセージ タイプ パイプが作成されます。メッセージ タイプ パイプの場合、 システムは、 パイプへの各書き込み操作をメッセージ単位として扱います。メッセージ タイプ パイプのハンドルは、 バイト読み取りモードかメッセージ読み取りモードにできます。

ブロック パイプ操作と非ブロック パイプ操作

ReadFile関数、 WriteFile関数、 ConnectNamedPipe関数が無期限に待機する必要が生じた場合、 それらの関数が直ちに戻るかどうかは、 パイプ ハンドルの待機モード (ブロックか非ブロック) によって決まります。デフォルトでは、 CreateNamedPipe関数やCreateFile関数が返す名前付きパイプ ハンドルは、 すべて、 ブロック待機モードを設定して作成されます。サーバー側パイプ ハンドルを非ブロック待機モードに設定するには、 CreateNamedPipePIPE_NOWAITを指定してください。サーバー プロセスやクライアント プロセスがパイプ ハンドルの待機モードを変更するには、 PIPE_WAITPIPE_NOWAITを指定してSetNamedPipeHandleState関数を呼び出してください。

非ブロック待機モードはMicrosoft(R) LAN Managerバージョン2.0との互換性を保つためにサポートされており、 名前付きパイプのオーバーラップ入出力には使えません。代わりに、 オーバーラップI/Oを使って、 時間のかかるReadFileWriteFile ConnectNamedPipeの操作が関数が戻った後でもバックグラウンドで実行されるようにしてください。オーバーラップI/Oについて詳しくは、 同期I/OとオーバーラップI/Oを参照してください。

パイプが空のとき、 ReadFile操作には、 パイプ ハンドルの待機モードが影響します。ブロック待機モードのハンドルの場合、 パイプの反対側のスレッドがデータを書き込んでデータが利用可能になるまで、 関数は正常に完了しません。非ブロック待機モードのハンドルの場合、 関数はすぐにFALSEを返し、 GetLastError関数はERROR_NO_DATAを返します。

パイプのバッファにデータをすべて保持するだけの領域がないとき、 WriteFile操作には、 パイプ ハンドルの待機モードが影響します。ブロック待機モードのハンドルの場合、 パイプの反対側のスレッドがデータを読み取って空き領域ができるまで、 書き込み操作は正常に終了しません。非ブロック待機ハンドルの場合、 すぐにTRUEを返します。メッセージ タイプ パイプの場合はバイトを書き込まずに戻り、 バイト タイプ パイプの場合はバッファが保持できるだけのバイトを書き込んでから戻ります。

パイプ インスタンスにクライアントが接続されておらず、 接続を待っているクライアントもないとき、 ConnectNamedPipe操作には、 パイプ ハンドルの待機モードが影響します。ブロック待機モードのハンドルの場合、 クライアント プロセスがCreateFileCallNamedPipeを呼び出してパイプ インスタンスに接続するまで、 接続操作は正常に終了しません。非ブロック待機モードのハンドルの場合、 接続操作はすぐにFALSEを返し、 GetLastError関数はERROR_PIPE_LISTENINGを返します。

パイプ ハンドルの待機モードは、 TransactNamedPipe関数やCallNamedPipe関数による読み書き操作には影響しません。

同期I/OとオーバーラップI/O

Win32 APIは、 名前付きパイプの同期操作と非同期 (オーバーラップ) 操作の両方をサポートしています。ReadFile関数、 WriteFile関数、 TransactNamedPipe関数、 ConnectNamedPipe関数は、 同期と非同期のどちらでも実行できます。ReadFileEx関数とWriteFileEx関数は、 非同期にしか実行できません。関数を同期的に実行すると、 操作が完了するまで関数は戻りません。つまり、 時間のかかる操作が完了するのを待つ間、 呼び出し側スレッドの実行は無期限にブロックされます。関数を非同期的に実行すると、 操作が完了していなくても関数はすぐに戻ります。これによって、 時間のかかる操作をバックグラウンドで実行し、 呼び出し側のスレッドはほかの作業を実行できます。Win32 APIでは、 この非同期実行は「オーバーラップI/O」とも呼ばれます。

オーバーラップ操作によって、 1つのパイプにデータを同時に読み書きでき、 単一のスレッドが複数のパイプ ハンドルに対して同時にI/O操作を実行できます。このため、 単一スレッド サーバー プロセスは、 複数のクライアント プロセスとの通信を効率的に処理できます。サーバー プロセスのオーバーラップ操作の例については、 オーバーラップI/Oを使うサーバー完了ルーチンを使うサーバーを参照してください。

同期操作を使って複数のクライアントと通信するサーバーは、 各クライアント プロセスについてスレッドを1つずつ作成しなければなりません。これによって、 待機しているスレッドがあるときでも、 ほかのスレッドが動作できます。同期操作を使うマルチスレッド サーバー プロセスの例については、 マルチスレッド サーバーを参照してください。

名前付きパイプでオーバーラップ操作を実行できるようにするには、 (サーバー プロセスの場合はCreateNamedPipe関数、 クライアント プロセスの場合はCreateFileを使って) ハンドルを作成するときに、 FILE_FLAG_OVERLAPPEDフラグを指定してください。このフラグを指定しなければ、 オーバーラップ モードは使用不能になります。パイプ ハンドルのオーバーラップ モードは、 パイプ ハンドルを作成してから変更することはできません。同じパイプ インスタンスのサーバー側ハンドルとクライアント側ハンドルで異なるオーバーラップ モードを設定できます。

ReadFile関数、 WriteFile関数、 TransactNamedPipe関数、 ConnectNamedPipe関数は、 オーバーラップ モードのパイプ ハンドルとOVERLAPPED構造体を指すポインタを指定したときだけ非同期に実行されます。OVERLAPPEDポインタがNULLならば、 パイプ ハンドルがオーバーラップ モードでも、 操作は同期的に実行されます。関数を非同期的に実行すると、 次のいずれかが発生します。

関数が戻るときに操作が完了していれば、 戻り値は操作が成功か失敗かを示します。エラーが発生したときは、 戻り値はFALSEになり、 GetLastError関数はERROR_IO_PENDING以外の値を返します。

関数が戻るときに操作が完了していなければ、 戻り値はFALSEになり、 GetLastErrorERROR_IO_PENDINGを返します。この場合、 呼び出し側のスレッドは、 操作が完了するまで待たなければなりません。操作が完了したら、 呼び出し側スレッドは、 GetOverlappedResult関数を呼び出して結果を調べなければなりません。

指定するOVERLAPPED構造体のhEventメンバには、 (自動リセットではなく) 手動リセットのイベント オブジェクトのハンドルを設定してください。このオブジェクトは、 CreateEvent関数で作成した同期オブジェクトです。オーバーラップ操作を開始したスレッドは、 イベント オブジェクトを使って、 操作が完了したか調べることができます。イベント オブジェクトについて詳しくは、 同期の概要を参照してください。

イベント オブジェクトの状態は、 シグナル状態か非シグナル状態のどちらかです。処理が完了する前にオーバーラップ関数が戻ると、 システムは、 イベント オブジェクトの状態を非シグナル状態に設定します。操作が完了すると、 システムは、 状態をシグナル状態に設定します。スレッドがオブジェクトの状態を検出するには、 イベント オブジェクトのハンドルを指定して、 待機関数 (WaitForSingleObject WaitForMultipleObjects WaitForSingleObjectEx WaitForMultipleObjectsEx) のいずれかを呼び出してください。これらの関数は、 指定されたオブジェクトの状態がシグナル状態になるか、 タイム アウト時間が経過するまで戻りません。複数のオブジェクトを待機する待機関数の場合、 関数の戻り値は、 シグナル状態になったオブジェクトを示します。これによって、 サーバー プロセスは、 次に示す作業をループで実行できます。

1. 複数のイベント オブジェクトを指定してWaitForMultipleObjectsWaitForMultipleObjectsExを呼び出して、 オブジェクトのいずれかがシグナル状態になるのを待ちます。

2. 待機関数の戻り値を使って、 完了したオーバーラップ操作を判断します。

3. 完了した操作の後始末に必要な作業を実行し、 そのパイプ ハンドルでの次の操作を開始します。たとえば、 同じパイプ ハンドルで別のオーバーラップ操作を開始します。

OVERLAPPED構造体のhEventメンバがイベント オブジェクトのハンドルではなくNULLポインタならば、 呼び出し側スレッドは、 オーバーラップ操作が完了したかどうかをパイプ ハンドルを使って判断してください。この場合、 システムは、 上記のイベント オブジェクトの状態と同様に、 パイプ ハンドルの状態をシグナル状態か非シグナル状態に設定します。オーバーラップ操作を実行するときは、 パイプ ハンドルに頼らずに、 必ずイベント オブジェクトを使ってください。とくに、 同じハンドルに対して複数の操作を同時に実行しているときは、 パイプ ハンドルをこのような目的に使わないでください。これは、 パイプ ハンドルがシグナル状態になる原因の操作を特定できないためです。同じパイプ ハンドルに対して複数の操作を同時に実行するとき、 安全に作業を行う方法は、 各操作に専用のイベント オブジェクトと専用のOVERLAPPED構造体を使う方法しかありません。

ReadFileEx関数とWriteFileEx関数でもオーバーラップI/Oを実現できます。オーバーラップしたReadFile関数やWriteFile関数ではイベント オブジェクトを使って完了をシグナルしますが、 拡張関数には「完了ルーチン」を指定します。完了ルーチンとは、 読み書き操作が完了したときに実行キューに入れられる関数です。ReadFileExWriteFileExを呼び出したスレッドが拡張待機関数 (WaitForSingleObjectEx WaitForMultipleObjectsExSleepEx) のいずれかを呼び出して「アラート可能待機」状態に入るまで、 完了ルーチンは実行されません。指定されたオブジェクトがシグナル状態になるかタイム アウト時間が経過すると戻る点では、 拡張待機関数は通常の待機関数と同じです。しかし、 拡張待機関数は、 fAlertableフラグにTRUEが設定されているときに発生するアラート可能待機も行います。アラート可能待機では、 ReadFileExWriteFileExの完了ルーチンが実行キューにおかれたときにも関数は戻ります。サーバー プロセスは、 拡張関数を使って、 接続されている各クライアントの読み書き操作を連続して実行できます。連続する各読み書き操作には完了ルーチンを指定し、 完了ルーチンが次の操作を開始します。

リモート クライアントへのライト スルー

ネットワークを通じてデータを転送する場合、 名前付きパイプ ハンドルのライト スルー モードは、 バイト タイプ パイプの動作に影響します。名前付きパイプ ハンドルのライト スルー モードを有効にするには、 サーバー プロセスの場合はCreateNamedPipe関数、 クライアント プロセスの場合はCreateFile関数にFILE_FLAG_WRITE_THROUGHフラグを指定してください。このフラグを指定しなければ、 ライト スルー モードは使われません。パイプ ハンドルのライト スルー モードは、 パイプ ハンドルを作成してから変更することはできません。同じパイプ インスタンスのサーバー側ハンドルとクライアント側ハンドルで異なるライト スルー モードを設定できます。

ライト スルー モードが設定されていなければ、 システムは、 最低限のバイト数がたまるか最大バッファ時間が経過するまでデータをバッファに置き、 ネットワーク操作の効率を向上させます。これによって、 複数の書き込み操作が1つのネットワーク転送に統合されます。また、 データがネットワークで転送されていなくても、 データが出力要バッファに入った時点で書き込み操作は正常に終了します。

クライアント プロセスは、 SetNamedPipeHandleState関数を使って、 ライト スルー モードが設定されていないときにパイプのデータが転送されるまでのバイト数と時間を設定できます。

ライト スルー モードが設定されていると、 ネットワークによる転送は遅延されず、 リモート マシンのパイプ バッファにデータが届くまで書き込み操作は完了しません。ライト スルー モードは、 書き込み操作を行うごとに同期しなければならないときに役立ちます。

メッセージ タイプ パイプの場合、 システムは、 常に、 ライト スルー モードが設定されているものとして書き込み操作を実行します。

パイプのセキュリティ

サーバー プロセスは、 ImpersonateNamedPipeClientを使って、 特定のパイプ インスタンスに接続されているクライアント プロセスのセキュリティ トークンを偽装できます。これは、 クライアント プロセスの要求を許可するかどうかを決定するときに役立ちます。たとえば、 名前付きパイプのサーバー プロセスが、 自分が特権付きでアクセスできるデータベースやファイル システムにほかのプロセスがアクセスできるようにしているとします。通常、 サーバーにアクセスを要求するクライアント プロセスのセキュリティ レベルは必要なセキュリティ レベルよりも低くなっています。サーバーがクライアントのセキュリティ トークンを偽装して保護されているデータベースにアクセスすると、 システムは、 クライアントのセキュリティ レベルに基づいて、 サーバーのアクセスを許可または拒否します。処理が完了すると、 サーバーは、 RevertToSelf関数を使って自分のもとのセキュリティ トークンを復元します。

パイプの使用

パイプは、 連続したデータ ストリームをプロセス間で渡さなければならないアプリケーションで役に立ちます。ここでは、 次の処理を実行する方法を説明します。

名前なしパイプを使って子プロセスの標準入力または標準出力をリダイレクトする親プロセス

マルチスレッドと同期操作を使って複数のクライアント プロセスと通信する名前付きサーバー プロセス

オーバーラップ操作を使って複数のクライアント プロセスと通信する単一スレッドの名前付きパイプ サーバー プロセス

完了ルーチンを使って複数のクライアント プロセスと通信する単一スレッドの名前付きパイプ サーバー プロセス

メッセージ タイプの名前付きパイプを使って接続するクライアント プロセス

名前付きパイプでのトランザクション操作を使うクライアント プロセス

名前なしパイプ

通常、 名前なしパイプは、 子プロセスの標準入出力をリダイレクトして、 親プロセスと子プロセスの通信チャネルを作成するのに使われます。子プロセスの標準入出力ハンドルをリダイレクトするには、 親プロセスで次の処理を実行します。

1.GetStdHandle関数を呼び出して現在の標準出力ハンドルを取得し、 後で使うために保存します。

2.CreatePipe関数を呼び出して名前なしパイプを作成します。この関数は、 パイプの読み取り側と書き込み側のハンドルを返します。

3.SetStdHandle関数を呼び出して、 パイプの書き込みハンドルを標準出力にします。

4.CreateProcess関数を呼び出して子プロセスを作成します。子プロセスは、 親プロセスの継承可能ハンドルを継承します。また、 子プロセスは、 GetStdHandle関数を使って、 親プロセスの標準ハンドルの値も継承できます。

5.CloseHandle関数を呼び出して、 親プロセスが持つパイプの書き込み側ハンドルをクローズします。子プロセスが子のハンドルを継承したら、 親プロセスではハンドルのコピーは不要になります。

6.ReadFile関数を呼び出してパイプからデータを読み取ります。親プロセスは、 この操作によって、 子プロセスが標準出力に書き込んだデータを読み取れます。

子プロセスは、 GetStdHandle関数を使って自分の標準出力ハンドルを取得します。このハンドルは、 実際にはパイプの書き込み側のハンドルです。その後、 子プロセスは、 WriteFile関数を使って出力をパイプに書き込みます。

名前なしパイプでは、 データはバイトのストリームとして書き込まれます。つまり、 読み取り側プロセスと書き込み側プロセスがなんらかのプロトコルで読み取りバイト数を定義していなければ、 パイプからデータを読み取るプロセスは各書き込み操作で書き込まれたバイトを区別できません。通常、 プロトコルはないため、 読み取り側プロセスは、 パイプの書き込みハンドルがすべてクローズされ、 ReadFile関数がFALSEを返すまで、 パイプからデータを読み取り続けます。子プロセスの標準出力をリダイレクトしているときに子プロセスがCloseHandleを呼び出すか終了すると (終了するとハンドルは自動的にクローズされます)、 データの読み取りは終了します。親プロセスは、 パイプの書き込み側ハンドルをクローズしてから、 パイプの読み取りを開始してください。そうしないと、 パイプの書き込み側のハンドルがオープンされたままになるため、 ReadFile操作がFALSEを返さなくなります。

標準入力のリダイレクトも標準出力のリダイレクトとほとんど同じですが、 子プロセスの標準入力にパイプの読み取りハンドルを使います。この場合、 親プロセスは、 子プロセスがパイプの書き込みハンドルを継承しないようにしてください。そうしないと、 子プロセスの書き込み側パイプ ハンドルがオープンされたままになるため、 子プロセスのReadFile操作がFALSEを返さなくなります。

通常、 親プロセスは、 パイプの読み書きハンドルを子プロセスが継承できるように作成します。継承可能なハンドルを作成するには、 bInheritHandleメンバにTRUEを設定したSECURITY_ATTRIBUTESを指定してCreatePipeを呼び出します。子プロセスの標準入力をリダイレクトするときは、 パイプの書き込み側ハンドルを子プロセスが継承しないようにしてください。書き込み側ハンドルを子プロセスが継承しないようにするには、 親プロセスは、 DuplicateHandle関数を使って継承不可能なハンドルの複製を作成し、 CloseHandleを使って継承可能なハンドルをクローズしてください。

名前なしパイプを使って子プロセスの標準ハンドルをリダイレクトするプログラムの例は、 プロセスとスレッドの概要を参照してください。

マルチスレッド サーバー

このマルチスレッド サーバー プロセスの例は、 パイプ インスタンスを作成してクライアント プロセスが接続するのを待ってループし続けるメイン スレッドを示しています。クライアント プロセスが接続すると、 サーバー プロセスはそのクライアント用のスレッドを作成し、 ループが開始します。サーバーがCreateNamedPipe関数を呼び出してからConnectNamedPipe関数を呼び出すまでの間に、 クライアント プロセスがパイプ インスタンスに正常に接続する場合があります。このような場合、 ConnectNamedPipeFALSEを返し、 GetLastErrorERROR_PIPE_CONNECTEDを返します。

各パイプ インスタンス用に作成されたスレッドは、 クライアント プロセスがハンドルをクローズするまで、 パイプから要求を読み取り、 応答をパイプに書き込みます。クライアント プロセスがハンドルをクローズすると、 スレッドは、 パイプをフラッシュして接続を解除し、 パイプ ハンドルをクローズして、 終了します。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

VOID InstanceThread(LPVOID);
VOID GetAnswerToRequest(LPTSTR, LPTSTR, LPDWORD);

int xx = 0;

DWORD main(VOID) {
BOOL fConnected;
DWORD dwThreadId;
HANDLE hPipe, hThread;
LPTSTR lpszPipename = "\\\\.\\pipe\\mynamedpipe";

/*
* Main loop creates an instance of the named pipe, and
* then waits for a client to connect to it. When the client
* connects, a thread is created to handle communications
* with that client, and the loop is repeated.
*/

for (;;) {
hPipe = CreateNamedPipe(
lpszPipename,/* pipe name*/
PIPE_ACCESS_DUPLEX,/* read/write access*/
PIPE_TYPE_MESSAGE |/* message type pipe*/
PIPE_READMODE_MESSAGE |/* message-read mode */
PIPE_WAIT,/* blocking mode*/
PIPE_UNLIMITED_INSTANCES, /* max. instances*/
BUFSIZE,/* output buffer size */
BUFSIZE,/* input buffer size*/
PIPE_TIMEOUT,/* client time-out*/
NULL);/* no security attr. */
if (hPipe == INVALID_HANDLE_VALUE)
MyErrExit("CreatePipe");

/*
* Wait for the client to connect; if it succeeds
* the function returns TRUE. If the function returns FALSE,
* GetLastError returns ERROR_PIPE_CONNECTED.
*/

fConnected = ConnectNamedPipe(hPipe, NULL) ?
TRUE :
(GetLastError() == ERROR_PIPE_CONNECTED);

if (fConnected) {

/* Create a thread for this client. */

hThread = CreateThread(
NULL,/* no security attr. */
0,/* default stack size*/
(LPTHREAD_START_ROUTINE) InstanceThread,
(LPVOID) hPipe, /* thread parameter*/
0,/* not suspended*/
&dwThreadId);/* returns thread ID*/
if (hThread == INVALID_HANDLE_VALUE)
MyErrExit("CreateThread");

} else

/* The client could not connect, so close the pipe. */

CloseHandle(hPipe);

}

return 1;
}

VOID InstanceThread(LPVOID lpvParam) {
CHAR chRequest[BUFSIZE];
CHAR chReply[BUFSIZE];
DWORD cbBytesRead, cbReplyBytes, cbWritten;
BOOL fSuccess;
HANDLE hPipe;


/* The thread's parameter is a handle to a pipe instance. */

hPipe = (HANDLE) lpvParam;

while (1) {

/* Read client requests from the pipe. */

fSuccess = ReadFile(
hPipe,/* handle to pipe*/
chRequest,/* buffer to receive data */
BUFSIZE,/* size of buffer*/
&cbBytesRead, /* number of bytes read*/
NULL);/* not overlapped I/O*/
if (! fSuccess || cbBytesRead == 0)
break;
GetAnswerToRequest(chRequest, chReply, &cbReplyBytes);

/* Write the reply to the pipe. */

fSuccess = WriteFile(
hPipe,/* handle to pipe*/
chReply,/* buffer to write from*/
cbReplyBytes, /* number of bytes to write */
&cbWritten,/* number of bytes written*/
NULL);/* not overlapped I/O*/
if (! fSuccess || cbReplyBytes != cbWritten) break;
}

/*
* Flush the pipe to allow the client to read the pipe's contents before
* disconnecting, then disconnect the pipe and close the handle to
* this pipe instance.
*/

FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
}

オーバーラップI/Oを使うサーバー

この例は、 オーバーラップ操作を使って複数のクライアント プロセスに同時に接続する単一スレッド サーバー プロセスです。サーバーは、 一定数のパイプ インスタンスを作成します。各パイプ インスタンスには、 それぞれクライアント プロセスが1つずつ接続されます。クライアント プロセスがパイプ インスタンスを使い終わったら、 サーバーはクライアントから接続解除し、 パイプ インスタンスを再利用して新しいクライアントに接続します。

各パイプ インスタンスには、 イベント オブジェクトを含むOVERLAPPED構造体が関連付けられています。このOVERLAPPED構造体は、 パイプ インスタンスに対するReadFile操作、 WriteFile操作、 ConnectNamedPipe操作でパラメータとして指定します。この例では別のパイプ インスタンスを同時に操作していますが、 同じパイプ インスタンスに複数の操作を同時に実行しないようにしてください。各インスタンスの読み書き操作や接続操作には同じオブジェクトが使われるため、 同じパイプ インスタンスに複数の操作を同時に実行すると、 どの操作が完了したためにイベントがシグナル状態になったのかを判断できなくなります。

また、 各パイプ インスタンスのイベント ハンドルが、 WaitForMultipleObjects関数で使われる配列に格納されています。この関数は、 イベントのいずれかがシグナル状態になるまで待ち、 待機条件が成立したイベントの配列インデックスを返します。この例では、 このインデックスを使って、 パイプ インスタンスの情報を含む構造体を取得しています。サーバーは、 この構造体のfPendingIOメンバを使って、 インスタンスに対する最新のI/O操作が未処理か調べて、 GetOverlappedResult関数の呼び出しが必要かどうか判断します。また、 サーバーは、 この構造体のdwStateメンバを使って、 このインスタンスに次の操作を実行すべきか判断します。

オーバーラップしたReadFile操作、 WriteFile操作、 ConnectNamedPipe操作では、 関数が戻っても操作が完了しているとは限りません。操作が未処理ならば、 関数は、 指定されたOVERLAPPED構造体のイベント オブジェクトを非シグナル状態にして戻ります。未処理の操作が完了すると、 システムは、 イベント オブジェクトをシグナル状態にします。関数が戻る前に操作が完了した場合、 イベント オブジェクトの状態は変化しません。

この例では手動リセット イベント オブジェクトを使っているため、 WaitForMultipleObjects関数はイベント オブジェクトを非シグナル状態に変更しません。この例では、 未処理の操作がなければイベント オブジェクトがシグナル状態にあると仮定しているため、 これは重要です。

ReadFile関数、 WriteFile関数、 ConnectNamedPipe関数は、 戻るときに操作が完了していれば、 操作の結果を示す値を返します。読み書き操作の場合は、 転送したバイト数も返します。操作がまた未処理ならば、 ReadFile関数、 WriteFile関数、 ConnectNamedPipe関数はFALSEを返し、 GetLastError関数はERROR_IO_PENDINGを返します。この場合、 操作が完了した後で、 GetOverlappedResult関数を使って操作の結果を取得できます。GetOverlappedResultは、 未処理だった操作の結果しか返さず、 オーバーラップしたReadFileWriteFile ConnectNamedPipeが戻る前に完了していた操作の結果は報告しません。

マルチスレッド サーバーの例では、 クライアントから接続解除する前に、 FlushFileBuffersを使って、 パイプに書き込まれたデータをクライアントがすべて読み取るようにしていました。フラッシュ操作によって、 クライアントがパイプを空にするのを待つ間サーバー スレッドの実行がブロックされるため、 この方法ではオーバーラップI/Oの目的が達成されません。また、 クライアントが読み取りを完了したことを示すシグナルを受け取ってからクライアントを接続解除しなければなりません。この例では、 シグナルは、 クライアント プロセスがクライアント側ハンドルをクローズした後でパイプから読み取ろうとしたときに生成されるエラーです。

#include <windows.h>

#define CONNECTING_STATE 0
#define READING_STATE 1
#define WRITING_STATE 2
#define INSTANCES 4

typedef struct {
OVERLAPPED oOverlap;
HANDLE hPipeInst;
CHAR chBuf[BUFSIZE];
DWORD cbToWrite;
DWORD dwState;
BOOL fPendingIO;
} PIPEINST, *LPPIPEINST;


VOID DisconnectAndReconnect(DWORD);
BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED);
VOID GetDataToWriteToClient(LPPIPEINST);

PIPEINST Pipe[INSTANCES];
HANDLE hEvents[INSTANCES];

DWORD main(VOID) {
DWORD i, dwWait, cbBytes, dwErr;
BOOL fSuccess;
LPTSTR lpszPipename = "\\\\.\\pipe\\mynamedpipe";

/*
* The initial loop creates several instances of a named pipe,
* along with an event object for each instance. An
* overlapped ConnectNamedPipe operation is started for
* each instance.
*/

for (i = 0; i < INSTANCES; i++) {

/* Create an event object for this instance. */

hEvents[i] = CreateEvent(
NULL,/* no security attr. */
TRUE,/* manual reset event*/
TRUE,/* initial state = signaled */
NULL);/* unnamed event object*/
if (hEvents[i] == NULL)
MyErrExit("CreateEvent");

Pipe[i].oOverlap.hEvent = hEvents[i];

Pipe[i].hPipeInst = CreateNamedPipe(
lpszPipename, /* pipe name*/
PIPE_ACCESS_DUPLEX |/* read/write access*/
FILE_FLAG_OVERLAPPED,/* overlapped mode*/
PIPE_TYPE_MESSAGE |/* message-type pipe*/
PIPE_READMODE_MESSAGE |/* message-read mode*/
PIPE_WAIT,/* blocking mode*/
INSTANCES,/* number of instances */
BUFSIZE,/* output buffer size*/
BUFSIZE,/* input buffer size*/
PIPE_TIMEOUT,/* client time-out*/
NULL);/* no security attr*/
if (Pipe[i].hPipeInst == INVALID_HANDLE_VALUE)
MyErrExit("CreatePipe");

/* Call the subroutine to connect to the new client. */

Pipe[i].fPendingIO = ConnectToNewClient(
Pipe[i].hPipeInst,
&Pipe[i].oOverlap);

Pipe[i].dwState = Pipe[i].fPendingIO ?
CONNECTING_STATE : /* still connecting*/
READING_STATE;/* ready to read*/

}

while (1) {

/*
* Wait for the event object to be signaled, indicating
* completion of an overlapped read, write, or
* connect operation.
*/

dwWait = WaitForMultipleObjects(
INSTANCES,/* number of event objects */
hEvents,/* array of event objects*/
FALSE,/* does not wait for all*/
INFINITE);/* waits indefinitely*/

/* dwWait shows which pipe had completed operation. */

i = dwWait - WAIT_OBJECT_0; /* determines which pipe */
if (i < 0 || i > (INSTANCES - 1))
MyErrExit("index out of range");

/* Get the result if the operation was pending. */

if (Pipe[i].fPendingIO) {
fSuccess = GetOverlappedResult(
Pipe[i].hPipeInst, /* handle to pipe*/
&Pipe[i].oOverlap, /* OVERLAPPED structure */
&cbBytes,/* bytes transferred*/
FALSE); /* do not wait*/

switch (Pipe[i].dwState) {

/* Pending connect operation */

case CONNECTING_STATE:
if (! fSuccess)
MyErrExit("ConnectNamedPipe");
Pipe[i].dwState = READING_STATE;
break;

/* Pending read operation */

case READING_STATE:
if (! fSuccess || cbBytes == 0) {
DisconnectAndReconnect(i);
continue;
}
Pipe[i].dwState = WRITING_STATE;
break;

/* Pending write operation */

case WRITING_STATE:
if (! fSuccess ||
cbBytes != Pipe[i].cbToWrite) {
DisconnectAndReconnect(i);
continue;
}
Pipe[i].dwState = READING_STATE;
break;

default:
MyErrExit("invalid pipe state");

}

}

/* The pipe state determines which operation to do next. */

switch (Pipe[i].dwState) {

/*
* READING_STATE:
* The pipe instance is connected to the client
* and ready to read a request from the client.
*/

case READING_STATE:
fSuccess = ReadFile(
Pipe[i].hPipeInst,
Pipe[i].chBuf,
BUFSIZE,
&cbBytes,
&Pipe[i].oOverlap);

/* The read operation was successfully completed. */

if (fSuccess && cbBytes != 0) {
Pipe[i].fPendingIO = FALSE;
Pipe[i].dwState = WRITING_STATE;
continue;
}

/* The read operation is still pending */

dwErr = GetLastError();
if (! fSuccess &&
(dwErr == ERROR_IO_PENDING)) {
Pipe[i].fPendingIO = TRUE;
continue;
}

/* An error occurred; disconnect from the client. */

DisconnectAndReconnect(i);
break;

/*
* WRITING_STATE:
* The request was successfully read from the client.
* Get the reply data and write it to the client.
*/

case WRITING_STATE:

GetDataToWriteToClient(&Pipe[i]);

fSuccess = WriteFile(
Pipe[i].hPipeInst,
Pipe[i].chBuf,
Pipe[i].cbToWrite,
&cbBytes,
&Pipe[i].oOverlap);

/* Write operation completed successfully */

if (fSuccess &&
cbBytes == Pipe[i].cbToWrite) {
Pipe[i].fPendingIO = FALSE;
Pipe[i].dwState = READING_STATE;
continue;
}

/* The write operation is still pending. */

dwErr = GetLastError();
if (! fSuccess &&
(dwErr == ERROR_IO_PENDING)) {
Pipe[i].fPendingIO = TRUE;
continue;
}

/* An error occurred; disconnect from the client. */

DisconnectAndReconnect(i);
break;

default:
MyErrExit("invalid pipe state");
}
}

return 0;
}


/*
* DisconnectAndReconnect(DWORD)
* This function is called when an error occurs or when the client closes
* its handle to the pipe. Disconnect from this client, and then call
* ConnectNamedPipe to wait for another client to connect.
*/

VOID DisconnectAndReconnect(DWORD i) {

/* Disconnect the pipe instance. */

if (! DisconnectNamedPipe(Pipe[i].hPipeInst) )
MyErrExit("DisconnectNamedPipe");

/* Call a subroutine to connect to the new client. */

Pipe[i].fPendingIO = ConnectToNewClient(
Pipe[i].hPipeInst,
&Pipe[i].oOverlap);

Pipe[i].dwState = Pipe[i].fPendingIO ?
CONNECTING_STATE : /* still connecting*/
READING_STATE;/* ready to read*/

}


/*
* ConnectToNewClient(HANDLE, LPOVERLAPPED)
* This function is called to start an overlapped connect operation. It
* returns TRUE if an operation is pending or FALSE if the connection has
* been completed.
*/

BOOL ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo) {
BOOL fConnected, fPendingIO = FALSE;

/* Start an overlapped connection for this pipe instance. */

fConnected = ConnectNamedPipe(hPipe, lpo);

/* Overlapped ConnectNamedPipe should return FALSE */

if (fConnected)
MyErrExit("ConnectNamedPipe");

switch (GetLastError()) {

/* Overlapped connection in progress */

case ERROR_IO_PENDING:
fPendingIO = TRUE;
break;

/* Client already connected, so signal an event. */

case ERROR_PIPE_CONNECTED:
if (SetEvent(lpo->hEvent))
break;

/* If an error occurs during the connection operation... */

default:
MyErrExit("ConnectNamedPipe");
}

return fPendingIO;
}

ReadFileEx関数とWriteFileEx関数でもオーバーラップI/Oを実現できます。オーバーラップしたReadFile関数やWriteFile関数ではイベント オブジェクトを使って完了をシグナルしますが、 拡張関数には「完了ルーチン」を指定します。完了ルーチンとは、 読み書き操作が完了したときに実行キューに入れられる関数です。ReadFileExWriteFileExを呼び出したスレッドが拡張待機関数 (WaitForSingleObjectEx WaitForMultipleObjectsExSleepEx) のいずれかを呼び出して「アラート可能待機」状態に入るまで、 完了ルーチンは実行されません。指定されたオブジェクトがシグナル状態になるかタイム アウト時間が経過すると戻る点では、 拡張待機関数は通常の待機関数と同じです。しかし、 拡張待機関数は、 fAlertableフラグにTRUEが設定されているときに発生するアラート可能待機も行います。アラート可能待機では、 ReadFileExWriteFileExの完了ルーチンが実行キューにおかれたときにも関数は戻ります。サーバー プロセスは、 拡張関数を使って、 接続されている各クライアントの読み書き操作を連続して実行できます。連続する各読み書き操作には完了ルーチンを指定し、 完了ルーチンが次の操作を開始します。

この例は、 オーバーラップ操作を使って複数のクライアント プロセスに同時に接続する単一スレッド サーバー プロセスです。サーバーは、 一定数のパイプ インスタンスを作成します。各パイプ インスタンスには、 それぞれクライアント プロセスが1つずつ接続されます。クライアント プロセスがパイプ インスタンスを使い終わったら、 サーバーはクライアントから接続解除し、 パイプ インスタンスを再利用して新しいクライアントに接続します。

各パイプ インスタンスには、 イベント オブジェクトを含むOVERLAPPED構造体が関連付けられています。このOVERLAPPED構造体は、 パイプ インスタンスに対するReadFile操作、 WriteFile操作、 ConnectNamedPipe操作でパラメータとして指定します。

完了ルーチンを使うサーバー

前の例と同様に、 この例は、 メッセージ タイプ パイプを作成してオーバーラップ操作を行う単一スレッド サーバー プロセスです。前の例とは異なり、 このサーバー プロセスは、 拡張関数のReadFileExWriteFileExを使ってオーバーラップI/Oを実行します。オーバーラップしたReadFile関数やWriteFile関数ではイベント オブジェクトを使って完了をシグナルしますが、 拡張関数には「完了ルーチン」を指定します。サーバー プロセスはWaitForSingleObjectEx関数を使います。この関数は、 完了ルーチンの実行準備ができたときに戻るアラート可能待機を実行します。また、 待機関数は、 イベント オブジェクトがシグナル状態になったときにも戻ります。この例では、 オーバーラップしたConnectNamedPipe操作が完了したとき (新しいクライアントが接続したとき) にイベント オブジェクトがシグナル状態になります。

最初に、 サーバー プロセスは、 パイプの単一インスタンスを作成し、 オーバーラップしたConnectNamedPipe操作を開始します。クライアントが接続すると、 サーバーは、 そのパイプ インスタンスの記憶域に使われる構造体を割り当てて、 ReadFileEx関数を呼び出してクライアントとの通信を処理するための一連のI/O操作を開始します。各操作には、 次の操作を開始する完了ルーチンを指定します。クライアントが接続解除され、 パイプ インスタンスがクローズすると、 一連の操作は終了します。サーバーは、 新しいクライアントの一連の操作を開始したら、 別のパイプ インスタンスを作成して、 新しいクライアントが接続するのを待ちます。

ReadFileEx関数やWriteFileEx関数のパラメータには、 完了ルーチンと、 OVERLAPPED構造体を指すポインタを指定します。このポインタは、 後で完了ルーチンのlpOverLapパラメータに渡されます。OVERLAPPED構造体には、 各パイプ インスタンスに割り当てられている構造体の最初のメンバを指すポインタがあるため、 完了ルーチンはlpOverLapパラメータを使ってパイプ インスタンスの構造体にアクセスできます。

重複を避けるため、 ConnectToNewClientサブルーチンのコードは示していません。このサブルーチンは、 オーバーラップしたサーバー プロセスで使われているものと同じです。

#include <windows.h>

typedef struct {
OVERLAPPED oOverlap;
HANDLE hPipeInst;
CHAR chBuf[BUFSIZE];
DWORD cbToWrite;
} PIPEINST, *LPPIPEINST;

BOOL CreateAndConnectInstance();
BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED);
VOID GetDataToWriteToClient(LPPIPEINST);
VOID DisconnectAndClose(LPPIPEINST);
VOID CompletedWriteRoutine(DWORD, DWORD, LPOVERLAPPED);
VOID CompletedReadRoutine(DWORD, DWORD, LPOVERLAPPED);

HANDLE hPipe;

DWORD main(VOID) {
HANDLE hConnectEvent;
OVERLAPPED oConnect;
LPPIPEINST lpPipeInst;
DWORD dwWait, cbBytes;
BOOL fSuccess, fPendingIO;

/* Create one event object for the connection operation */

hConnectEvent = CreateEvent(
NULL,/* no security attr. */
TRUE,/* manual reset event*/
TRUE,/* initial state = signaled*/
NULL); /* unnamed event object*/
if (hConnectEvent == NULL)
MyErrExit("CreateEvent");

oConnect.hEvent = hConnectEvent;

/*
* Call a subroutine to create one instance, and wait for
* the client to connect.
*/

fPendingIO = CreateAndConnectInstance(&oConnect);

while (1) {

/*
* Wait for a client to connect, or for a read or write
* operation to be completed, which causes a completion
* routine to be queued for execution.
*/

dwWait = WaitForSingleObjectEx(
hConnectEvent,/* event object to wait for */
INFINITE,/* waits indefinitely*/
TRUE);/* alertable wait enabled */

switch (dwWait) {

/* The wait is satisfied by a completed connection operation. */

case 0:

/* If an operation is pending, get the result of the
* connection operation.
*/

if (fPendingIO) {
fSuccess = GetOverlappedResult(
hPipe,/* pipe handle*/
&oConnect, /* OVERLAPPED structure */
&cbBytes,/* bytes transferred*/
FALSE);/* does not wait*/
if (! fSuccess)
MyErrExit("ConnectNamedPipe");
}

/* Allocate storage for this instance. */

lpPipeInst = (LPPIPEINST) GlobalAlloc(
GPTR, sizeof(PIPEINST));
if (lpPipeInst == NULL)
MyErrExit("GlobalAlloc lpPipeInst");

lpPipeInst->hPipeInst = hPipe;

/*
* Start the read operation for this client.
* Note that this same routine is later used as a
* completion routine after a write operation.
*/

lpPipeInst->cbToWrite = 0;
CompletedWriteRoutine(0, 0,
(LPOVERLAPPED) lpPipeInst);

/* Create new pipe instance for the next client. */

fPendingIO = CreateAndConnectInstance(
&oConnect);
break;

/*
* The wait is satisfied by a completed read or write
* operation. This allows the system to execute the
* completion routine.
*/

case WAIT_IO_COMPLETION:

break;

/* An error occurred in the wait function. */

default:

MyErrExit("WaitForSingleObjectEx");

}

}

return 0;
}

/*
* CompletedWriteRoutine(DWORD, DWORD, LPOVERLAPPED)
* This routine is called as an I/O completion routine after writing to
* the pipe, or when a new client has connected to a pipe instance. It
* starts another read operation.
*/

VOID CompletedWriteRoutine(DWORD dwErr, DWORD cbWritten,
LPOVERLAPPED lpOverLap) {
LPPIPEINST lpPipeInst;
BOOL fRead = FALSE;

/* lpOverlap points to storage for this instance. */

lpPipeInst = (LPPIPEINST) lpOverLap;

/*
* The write operation has finished, so read the next request (if no
* errorred).
*/

if ((dwErr == 0) && (cbWritten == lpPipeInst->cbToWrite))
fRead = ReadFileEx(
lpPipeInst->hPipeInst,
lpPipeInst->chBuf,
BUFSIZE,
(LPOVERLAPPED) lpPipeInst,
(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedReadRoutine);

/* Disconnect if an error occurred. */

if (! fRead)
DisconnectAndClose(lpPipeInst);

}


/*
* CompletedReadRoutine(DWORD, DWORD, LPOVERLAPPED)
* This routine is called as an I/O completion routine after reading a
* request from the client. It gets data and writes it to the pipe.
*/

VOID CompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead,
LPOVERLAPPED lpOverLap) {
LPPIPEINST lpPipeInst;
BOOL fWrite = FALSE;

/* lpOverlap points to storage for this instance */

lpPipeInst = (LPPIPEINST) lpOverLap;

/*
* The read operation has finished, so write a response (if no
* errorred).
*/

if ((dwErr == 0) && (cbBytesRead != 0)) {
GetDataToWriteToClient(lpPipeInst);

fWrite = WriteFileEx(
lpPipeInst->hPipeInst,
lpPipeInst->chBuf,
lpPipeInst->cbToWrite,
(LPOVERLAPPED) lpPipeInst,
(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedWriteRoutine);
}

/* Disconnect if an error occurred. */

if (! fWrite)
DisconnectAndClose(lpPipeInst);

}


/*
* DisconnectAndClose(LPPIPEINST)
* Called when error occurs or when the client closes its handle
* to the pipe.
*/

VOID DisconnectAndClose(LPPIPEINST lpPipeInst)
{
/* Disconnect the pipe instance. */

if (! DisconnectNamedPipe(lpPipeInst->hPipeInst) )
MyErrExit("DisconnectNamedPipe");

/* Close the handle to the pipe instance. */

CloseHandle(lpPipeInst->hPipeInst);

/* Release the storage for the pipe instance. */

if (lpPipeInst != NULL)
GlobalFree(lpPipeInst);
}


/*
* CreateAndConnectInstance(LPOVERLAPPED)
* This function creates a pipe instance and connects to the new client.
* It returns TRUE if the connection operation is pending FALSE if
* the connection has been completed.
*/

BOOL CreateAndConnectInstance(LPOVERLAPPED lpoOverlap) {

LPTSTR lpszPipename = "\\\\.\\pipe\\mynamedpipe";

hPipe = CreateNamedPipe(
lpszPipename, /* pipe name*/
PIPE_ACCESS_DUPLEX |/* read/write access*/
FILE_FLAG_OVERLAPPED,/* overlapped mode*/
PIPE_TYPE_MESSAGE |/* message-type pipe*/
PIPE_READMODE_MESSAGE |/* message read mode*/
PIPE_WAIT,/* blocking mode*/
PIPE_UNLIMITED_INSTANCES, /* unlimited instances */
BUFSIZE,/* output buffer size*/
BUFSIZE,/* input buffer size*/
PIPE_TIMEOUT,/* client time-out*/
NULL);/* no security attr. */
if (hPipe == INVALID_HANDLE_VALUE)
MyErrExit("CreatePipe");

/* Call a subroutine to connect to the new client. */

return ConnectToNewClient(hPipe, lpoOverlap);

}

名前付きパイプのクライアント プロセス

クライアント プロセスは、 CreateFile関数を使って、 名前付きパイプのハンドルをオープンできます。パイプが存在していても、 そのインスタンスがすべてビジー状態ならば、 CreateFileFALSEを返し、 GetLastError関数はERROR_PIPE_BUSYを返します。この場合、 クライアント プロセスは、 WaitNamedPipe関数を使って、 パイプのインスタンスが利用可能になるのを待つことができます。

指定されたアクセスがサーバーがパイプを作成するときに指定したアクセス (双方向、 出力、 入力) と互換性がないとき、 CreateFile関数は失敗します。双方向 (読み書き) パイプの場合、 クライアントは、 読み取り、 書き込み、 読み書きの各アクセスのいずれかを指定できます。出力パイプ (サーバーからは書き込みのみ) の場合、 クライアントは読み取り専用アクセスを指定しなければなりません。入力パイプ (サーバーからは読み取りのみ) の場合、 クライアントは書き込み専用アクセスを指定しなければなりません。

CreateFileが返すハンドルには、 デフォルトではバイト読み取りモードとブロック待機モードが設定されており、 オーバーラップ モードとライト スルー モードは設定されていません。クライアント プロセスは、 CreateFile関数を呼び出すとき、 FILE_FLAG_OVERLAPPEDを指定してオーバーラップ モードを設定し、 FILE_FLAG_WRITE_THROUGHを指定してライト スルー モードを設定できます。また、 クライアントは、 SetNamedPipeHandleState関数を呼び出すとき、 PIPE_WAITを指定して非ブロック モードを設定し、 PIPE_READMODE_MESSAGEを指定してメッセージ読み取りモードを設定できます。

次の例は、 名前付きパイプをオープンしてパイプ ハンドルをメッセージ読み取りモードに設定し、 WriteFileで要求をサーバーに送って、 ReadFileでサーバーの応答を読み取るクライアントを示しています。このクライアント プロセスは、 これまでに示したメッセージ タイプ サーバーのどれとでも使えます。しかし、 バイト タイプ サーバーの場合は、 クライアント プロセスは、 SetNamedPipeHandleStateを呼び出してメッセージ読み取りモードに変更しようとした時点で失敗します。クライアントがメッセージ読み取りモードでパイプから読み取るため、 ReadFile操作がメッセージを部分的に読み取ってからFALSEを返す場合があります。これは、 メッセージが読み取りバッファよりも大きいときに発生します。この場合、 GetLastErrorERROR_MORE_DATAを返し、 メッセージの残りはReadFileをさらに呼び出して読み取ることができます。

#include <windows.h>

DWORD main(int argc, char *argv[]) {
HANDLE hPipe;
LPVOID lpvMessage;
CHAR chBuf[512];
BOOL fSuccess;
DWORD cbRead, cbWritten, dwMode;
LPTSTR lpszPipename = "\\\\.\\pipe\\mynamedpipe";

/* Try to open a named pipe; wait for it, if necessary. */

while (1) {
hPipe = CreateFile(
lpszPipename,/* pipe name*/
GENERIC_READ |/* read/write access*/
GENERIC_WRITE,
0,/* no sharing*/
NULL,/* no security attr. */
OPEN_EXISTING,/* opens existing pipe */
0,/* default attributes*/
NULL);/* no template file */

/* Break if the pipe handle is valid. */

if (hPipe != INVALID_HANDLE_VALUE)
break;

/* Exit if an error other than ERROR_PIPE_BUSY occurs */

if (GetLastError() != ERROR_PIPE_BUSY)
MyErrExit("Could not open pipe");

/* All pipe instances are busy, so wait for 20 seconds. */

if (! WaitNamedPipe(lpszPipename, 20000) )
MyErrExit("Could not open pipe");

}

/* The pipe connected; change to message read mode. */

dwMode = PIPE_READMODE_MESSAGE;
fSuccess = SetNamedPipeHandleState(
hPipe,/* pipe handle*/
&dwMode,/* new pipe mode*/
NULL,/* don't set max. bytes */
NULL);/* don't set max. time*/
if (! fSuccess)
MyErrExit("SetNamedPipeHandleState");

/* Send a message to the pipe server. */

lpvMessage = (argc > 1) ? argv[1] : "default message";

fSuccess = WriteFile(
hPipe,/* pipe handle*/
lpvMessage,/* message*/
strlen(lpvMessage) + 1, /* message length*/
&cbWritten,/* bytes written*/
NULL);/* not overlapped*/
if (! fSuccess)
MyErrExit("WriteFile");

do {

/* Read from the pipe. */

fSuccess = ReadFile(
hPipe,/* pipe handle*/
chBuf,/* buffer to receive reply*/
512,/* size of buffer*/
&cbRead,/* number of bytes read*/
NULL);/* not overlapped*/

if (! fSuccess && GetLastError() != ERROR_MORE_DATA)
break;

/* Reply from pipe written to stdout. */

if (! WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
chBuf, cbRead, &cbWritten, NULL))
break;

} while (! fSuccess); /* repeat loop if ERROR_MORE_DATA */

CloseHandle(hPipe);

return 0;

}

名前付きパイプでのトランザクション

名前付きパイプ トランザクションとは、 書き込み操作と読み取り操作を1つのネットワーク操作に統合した、 クライアント サーバー通信です。トランザクションは、 双方向のメッセージ タイプ パイプでしか使えません。トランザクションによって、 クライアントとリモート サーバーの間のネットワーク通信の効率が向上します。プロセスは、 TransactNamedPipe関数とCallNamedPipe関数を使って、 名前付きパイプ トランザクションを実行できます。

通常、 TransactNamedPipe関数は、 クライアント プロセスが要求メッセージを名前付きパイプ サーバーに書き込み、 サーバーの応答メッセージを読み取るのに使われます。クライアント プロセスは、 CreateFile関数を呼び出してパイプ ハンドルをオープンするとき、 GENERIC_READ | GENERIC_WRITEアクセスを指定しなければなりません。それから、 クライアント プロセスは、 SetNamedPipeHandleState関数を呼び出して、 パイプ ハンドルをメッセージ読み取りモードに設定します。TransactNamedPipeを呼び出すときに指定した読み取りバッファが小さすぎてサーバーが書き込んだメッセージ全体を保持できないときは、 この関数はFALSEを返し、 GetLastErrorERROR_MORE_DATAを返します。クライアントは、 ReadFileReadFileEx PeekNamedPipeを呼び出してメッセージの残りを読み取ることができます。

通常、 TransactNamedPipeはクライアント プロセスから呼び出されますが、 サーバー プロセスが使うこともできます。

次の例は、 TransactNamedPipeを使うクライアント プロセスを示しています。この例では、 名前付きパイプのクライアント プロセスのクライアント プロセスの例と同様に、 クライアント プロセスがCreateFileを使ってパイプに接続し、 SetNamedPipeHandleStateを使ってパイプ ハンドルの読み取りモードをすでに設定しているものとします。

fSuccess = TransactNamedPipe(
hPipe,/* pipe handle*/
lpszWrite,/* message to server*/
strlen(lpszWrite)+1, /* message length*/
chReadBuf,/* buffer to receive reply */
512, /* size of read buffer*/
&cbRead,/* number of bytes read*/
NULL);/* not overlapped*/

/*
* Exit if an error occurs, unless the error indicates there is more
* data in the message.
*/

if (! fSuccess && (GetLastError() != ERROR_MORE_DATA))
MyErrExit("TransactNamedPipe");

while(1) {

/* Data from the pipe is written to stdout. */

if (! WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
chReadBuf, cbRead, &cbWritten, NULL) )
break;

/* Break if successful TransactNamedPipe or ReadFile. */

if (fSuccess)
break;

/* Read from the pipe if there is more data in the message.*/

fSuccess = ReadFile(
hPipe,/* pipe handle*/
chReadBuf,/* buffer to receive reply*/
512,/* size of buffer*/
&cbRead,/* number of bytes read*/
NULL);/* not overlapped*/

/* Exit if an error other than ERROR_MORE_DATA occurs. */

if (! fSuccess && (GetLastError() != ERROR_MORE_DATA))
break;

}

クライアント プロセスは、 CallNamedPipe関数を使って、 CreateFile操作、 WaitNamedPipe操作 (必要な場合だけ) TransactNamedPipe操作、 CloseHandle操作を1つの操作に統合します。関数はパイプ ハンドルをクローズしてから戻るため、 指定した読み取りバッファのサイズよりもメッセージが大きいときは、 メッセージのバッファに入りきらなかった部分は失われます。

次の例は、 CallNamedPipeの使い方を示しています。

/* Combines connect, wait, write, read, and close */

fSuccess = CallNamedPipe(
lpszPipename,/* pipe name*/
lpszWrite,/* message to server*/
strlen(lpszWrite)+1, /* message length*/
chReadBuf,/* buffer to receive reply */
512,/* size of read buffer*/
&cbRead,/* number of bytes read*/
20000);/* waits for 20 seconds*/

if (fSuccess || GetLastError() == ERROR_MORE_DATA) {

/* Data from the pipe is written to stdout. */

WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
chReadBuf, cbRead, &cbWritten, NULL);

/*
* The pipe is closed; so no more bytes can be read from the
* message.
*/

if (! fSuccess)
printf("\n...extra data in message was lost\n");
}

パイプ関数

パイプに関する関数を次に示します。

CallNamedPipe

ConnectNamedPipe

CreateNamedPipe

CreatePipe

DisconnectNamedPipe

GetNamedPipeHandleState

GetNamedPipeInfo

PeekNamedPipe

SetNamedPipeHandleState

TransactNamedPipe

WaitNamedPipe

▲ページトップに戻る

【本を読んでもよくらからない】 … 個別指導でわかりやすくお教えします