LPWSPSTARTUP
コールバックシグネチャ
INT LPWSPSTARTUP(
WORD wVersionRequested,
WSPDATA* lpWSPData,
WSAPROTOCOL_INFOW* lpProtocolInfo,
WSPUPCALLTABLE UpcallTable,
WSPPROC_TABLE* lpProcTable
);パラメーター
| フィールド | 型 | 説明 |
|---|---|---|
| wVersionRequested | WORD | 呼び出し元が使用できる Windows Sockets SPI サポートの最上位バージョンです。上位バイトはマイナーバージョン (リビジョン) 番号を、下位バイトはメジャーバージョン番号を指定します。 |
| lpWSPData | WSPDATA* | Windows Sockets サービスプロバイダーに関する情報を受け取る WSPDATA データ構造体へのポインターです。 |
| lpProtocolInfo | WSAPROTOCOL_INFOW* | 目的のプロトコルの特性を定義する WSAProtocol_Info 構造体へのポインターです。これは、単一のプロバイダー DLL が複数の異なるサービスプロバイダーをインスタンス化できる場合に特に有用です。 |
| UpcallTable | WSPUPCALLTABLE | WSPUpCallTable 構造体で渡される、Winsock 2 DLL (Ws2_32.dll) のアップコールディスパッチテーブルです。 |
| lpProcTable | WSPPROC_TABLE* | SPI 関数ポインターのテーブルへのポインターです。このテーブルは WSPProc_Table 構造体として返されます。 |
公式ドキュメント
WSPStartup 関数は、クライアントによる Windows Sockets サービスプロバイダーインターフェイス (SPI) の使用を開始します。
戻り値
WSPStartup 関数は、成功した場合は 0 を返します。それ以外の場合は、以下に示すエラーコードのいずれかを返します。
| エラーコード | 意味 |
|---|---|
| ネットワークサブシステムが利用できません。 このエラーは、ネットワークサービスの提供に使用している基盤システムが現在利用できないために、Windows Sockets の実装が現時点で機能できない場合に返されます。 | |
| Winsock.dll のバージョンが範囲外です。このエラーは、要求された Windows Sockets SPI サポートのバージョンが、この特定の Windows Sockets サービスプロバイダーによって提供されていない場合に返されます。 | |
| ブロッキングする Windows Sockets 1.1 の操作が進行中です。 | |
| Windows Sockets の実装がサポートするタスク数の上限に達しました。 | |
| lpWSPData または lpProcTable パラメーターが無効です。 |
解説(Remarks)
Windows Sockets 2 のトランスポートサービスプロバイダーは、サービスプロバイダーの初期化関数として使用される単一のエクスポートされたプロシージャエントリポイント WSPStartup を持つ DLL です。その他のすべてのサービスプロバイダー関数は、WSPStartup 関数の lpProcTable パラメーターで渡されるサービスプロバイダーのディスパッチテーブルを介して Winsock 2 DLL から利用できるようになります。サービスプロバイダー DLL は、必要になったときにのみ WinSock 2 DLL によってメモリに読み込まれ、そのサービスが不要になるとアンロードされます。
サービスプロバイダーインターフェイスでは、トランスポートサービスプロバイダーが DLL のサポートサービスを得るために Winsock 2 DLL を呼び出す (アップコールする) いくつかの状況も定義されています。トランスポートサービスプロバイダーには、WSPStartup 関数に渡される UpcallTable パラメーターで Winsock 2 DLL のアップコールディスパッチテーブルが返されます。
WSPStartup 関数は、Windows Sockets SPI クライアントがプロセスごとに最初に呼び出す Windows Sockets SPI 関数でなければなりません。この関数により、クライアントは必要な Windows Sockets SPI のバージョンを指定し、自身のアップコールディスパッチテーブルを提供できます。Windows Sockets サービスプロバイダーが行うすべてのアップコール (つまり WPU で始まる関数) は、クライアントのアップコールディスパッチテーブルを通じて呼び出されます。また、この関数によってクライアントは、特定の Windows Sockets サービスプロバイダー実装の詳細を取得できます。Windows Sockets SPI クライアントは、WSPStartup の呼び出しが成功した後にのみ、以降の Windows Sockets SPI 関数を発行できます。残りの SPI 関数へのポインターのテーブルは、WSPProc_Table 構造体を返す lpProcTable パラメーターを通じて取得します。
Winsock 2 DLL は、標準的な Windows の動的ライブラリ読み込みメカニズムを使用してサービスプロバイダーのインターフェイス DLL をシステムに読み込み、WSPStartup 関数を呼び出して初期化します。これは通常、インターフェイス DLL が現在メモリに読み込まれていないサービスプロバイダーに関連付けられる新しいソケットを作成するために、アプリケーションが socket 関数または WSASocket 関数を呼び出したときに発生します。
現在の Windows Sockets SPI とは機能的に異なる可能性がある将来のバージョンの Windows Sockets SPI および Ws2_32.dll をサポートするため、 WSPStartup ではネゴシエーションが行われます。 WSPStartup の呼び出し元 (Ws2_32.dll または階層化プロトコル) と Windows Sockets サービスプロバイダーは、自身がサポートできる Windows Sockets の最上位バージョンを互いに通知し、相手の最上位バージョンが受け入れ可能であることをそれぞれ確認します。 WSPStartup に入ると、Windows Sockets サービスプロバイダーはクライアントが要求したバージョンを調べます。このバージョンがサービスプロバイダーのサポートする最下位バージョン以上であれば呼び出しは成功し、サービスプロバイダーは WSPDATA 構造体の wHighVersion メンバーに自身がサポートする最上位バージョンを返し、wVersion メンバーには自身の最上位バージョンと wVersionRequested パラメーターで指定されたバージョンのうち小さい方を返します。その後、Windows Sockets サービスプロバイダーは、Windows Sockets SPI クライアントが wVersion メンバーで指定されたバージョンの Windows Sockets を使用するものと想定します。 WSPDATA 構造体の wVersion メンバーが呼び出し元にとって受け入れられない場合は、 LPWSPCleanup を呼び出したうえで、別の Windows Sockets サービスプロバイダーを探すか、初期化を失敗させる必要があります。
このネゴシエーションにより、Windows Sockets サービスプロバイダーと Windows Sockets SPI クライアントの両方が、一定範囲の Windows Sockets バージョンをサポートできます。バージョンの範囲に重なりがあれば、クライアントは Windows Sockets サービスプロバイダーを問題なく利用できます。
現在の Windows Sockets 仕様のバージョンは 2.2 です。現在の Winsock DLL である Ws2_32.dll は、次のいずれかのバージョンの Windows Sockets 仕様を要求するアプリケーションをサポートします。
- 1.0
- 1.1
- 2.0
- 2.1
- 2.2
より上位のバージョンの Windows Sockets 仕様の新しい構文をすべて利用するには、アプリケーションはその上位バージョンをネゴシエートする必要があります。この場合、wVersionRequested パラメーターはバージョン 2.2 を要求するように設定します。また、適切なヘッダーファイルに対してコンパイルする、新しいライブラリとリンクするなどの特別な対応を含め、アプリケーションはその上位バージョンの Windows Socket 仕様に完全に準拠する必要があります。Winsock 2 をサポートする Winsock2.h ヘッダーファイルは、Microsoft Windows Software Development Kit (SDK) に含まれています。
Windows Sockets バージョン 2.2 は、Windows Server 2008、 Windows Vista、Windows Server 2003、 Windows XP、 Windows 2000、Service Pack 4 (SP4) 以降の Windows NT 4.0、 Windows Me、 Windows 98、および Windows 95 OSR2 でサポートされます。 Windows Sockets バージョン 2.2 は、 Windows Socket 2 Update を適用した Windows 95 でもサポートされます。これらのプラットフォーム上のアプリケーションは、通常 wVersionRequested パラメーターを適切に設定して Winsock 2.2 を要求する必要があります。
Windows 95 および Windows NT 3.51 以前のバージョンでは、Windows Sockets バージョン 1.1 がサポートされる最上位の Windows Sockets 仕様バージョンです。
Winsock DLL がサポートするより低いバージョンの Windows Sockets 仕様を使用するように書かれたアプリケーションや DLL が、WSPStartup 関数を使用してその低いバージョンのネゴシエーションに成功することも、正当かつ可能です。たとえばアプリケーションは、Winsock 2.2 DLL を備えたプラットフォームで、WSPStartup 関数に渡す wVersionRequested パラメーターにバージョン 1.1 を要求できます。この場合、アプリケーションは要求したバージョンの範囲に収まる機能のみに依存する必要があります。新しい Ioctl コード、既存関数の新しい動作、および新しい関数は使用すべきではありません。WSPStartup が提供するバージョンネゴシエーションは、主に Windows 95 や Windows NT 3.51 以前向けに開発された古い Winsock 1.1 アプリケーションを、それ以降のバージョンの Windows でも同じ動作で実行できるようにするために使用されてきました。Winsock 1.1 をサポートする Winsock.h ヘッダーファイルは、Windows SDK に含まれています。
次の表は、 WSPStartup がさまざまな WS2_32.DLL および Windows Sockets サービスプロバイダー (SP) のバージョンと組み合わせてどのように動作するかの例を示しています。
| DLL |
SP |
wVersionRequested | wVersion | wHighVersion | 最終結果 |
|---|---|---|---|---|---|
| 1.1 | 1.1 | 1.1 | 1.1 | 1.1 | 1.1 を使用 |
| 1.0 1.1 | 1.0 | 1.1 | 1.0 | 1.0 | 1.0 を使用 |
| 1.0 | 1.0 1.1 | 1.0 | 1.0 | 1.1 | 1.0 を使用 |
| 1.1 | 1.0 1.1 | 1.1 | 1.1 | 1.1 | 1.1 を使用 |
| 1.1 | 1.0 | 1.1 | 1.0 | 1.0 | DLL が失敗 |
| 1.0 | 1.1 | 1.0 | --- | --- | WSAVERNOTSUPPORTED |
| 1.0 1.1 | 1.0 1.1 | 1.1 | 1.1 | 1.1 | 1.1 を使用 |
| 1.0 1.1 2.0 | 1.1 | 2.0 | 1.1 | 1.1 | 1.1 を使用 |
| 1.0 1.1 2.0 | 2.0 | 2.0 | 2.0 | 2.0 | 2.0 を使用 |
| 1.0 1.1 2.0 2.1 2.2 | 2.2 | 2.2 | 2.2 | 2.2 | 2.2 を使用 |
次のコードの断片は、Windows Sockets SPI のバージョン 2 のみをサポートする Windows Sockets SPI クライアントが WSPStartup を呼び出す方法を示しています。
WORD wVersionRequested;
WSPDATA WSPData;
int err;
WSPUPCALLTABLE upcallTable =
{
/* initialize upcallTable with function pointers */
};
LPWSPPROC_TABLE lpProcTable =
{
/* allocate memory for the ProcTable */
};
wVersionRequested = MAKEWORD( 2, 2 );
err = WSPStartup( wVersionRequested, &WSPData, lpProtocolBuffer, upcallTable, lpProcTable );
if ( err != 0 ) {
/* Tell the user that we could not find a usable */
/* Windows Sockets service provider. */
return;
}
/* Confirm that the Windows Sockets service provider supports 2.2.*/
/* Note that if the service provider supports versions */
/* greater than 2.2 in addition to 2.2, it will still */
/* return 2.2 in wVersion since that is the version we */
/* requested. */
if ( LOBYTE( WSPData.wVersion ) != 2 ||
HIBYTE( WSPData.wVersion ) != 2 ) {
/* Tell the user that we could not find a usable */
/* Windows Sockets service provider. */
LPWSPCleanup( );
return;
}
/* The Windows Sockets service provider is acceptable. Proceed. */
また、次のコードの断片は、バージョン 2.2 のみをサポートする Windows Sockets サービスプロバイダーが WSPStartup のネゴシエーションを行う方法を示しています。
/* Make sure that the version requested is >= 2.2. */
/* The low byte is the major version and the high */
/* byte is the minor version. */
if ( (LOBYTE( wVersionRequested ) < 2) ||
((LOBYTE( wVersionRequested ) == 2) &&
(HIBYTE( wVersionRequested ) < 2))) {
return WSAVERNOTSUPPORTED;
}
/* Since we only support 2.2, set both wVersion and */
/* wHighVersion to 2.2. */
lpWSPData->wVersion = MAKEWORD( 2, 2 );
lpWSPData->wHighVersion = MAKEWORD( 2, 2 );
Windows Sockets SPI クライアントは、 WSPStartup の呼び出しに成功したら、必要に応じて他の Windows Sockets SPI 呼び出しを行えます。Windows Sockets サービスプロバイダーのサービスの使用を終えたときは、サービスプロバイダーがクライアントのために割り当てたリソースを解放できるよう、クライアントは LPWSPCleanup を呼び出す必要があります。
WSPStartup 関数は、各クライアントプロセスが少なくとも 1 回呼び出す必要があり、Winsock 2 DLL やその他のエンティティから複数回呼び出されることもあります。成功した WSPStartup 呼び出しごとに、対応する LPWSPCleanup 関数を呼び出す必要があります。サービスプロバイダーは、プロセスごとに参照カウントを維持する必要があります。WSPStartup の各呼び出しでは、呼び出し元はサービスプロバイダー DLL がサポートする任意のバージョン番号を指定できます。
サービスプロバイダーは、WSPStartup 関数が UpcallTable パラメーターとして受け取るクライアントのアップコールディスパッチテーブルへのポインターを、プロセスごとに保存する必要があります。あるプロセスが WSPStartup を複数回呼び出した場合、サービスプロバイダーは最後に指定されたアップコールディスパッチテーブルのポインターのみを使用する必要があります。
Windows Sockets SPI クライアントは、 WSPDATA 構造体の情報を複数回取得する必要がある場合、 WSPStartup を複数回呼び出すことができます。そのような呼び出しごとに、クライアントはプロバイダーがサポートする任意のバージョン番号を指定できます。
サードパーティ製 DLL が Windows Sockets プロバイダーを利用できるようにするため、成功した WSPStartup 呼び出しごとに 1 回の LPWSPCleanup 呼び出しが必要です。つまり、たとえば WSPStartup が 3 回呼び出された場合、対応する LPWSPCleanup の呼び出しも 3 回行う必要があります。最初の 2 回の LPWSPCleanup 呼び出しは内部カウンターをデクリメントする以外は何も行わず、最後の LPWSPCleanup 呼び出しで必要なリソースの解放がすべて行われます。
クライアントが 16 ビットの Windows Sockets 1.1 クライアントである場合、この関数 (および他のほとんどのサービスプロバイダー関数) は、16 ビットプロセスとして開始されたスレッドで呼び出される可能性があります。16 ビットプロセスの重要な制限の 1 つは、16 ビットプロセスがスレッドを作成できないことです。これは、実装の一部として内部サービススレッドの使用を計画しているサービスプロバイダーの実装者にとって重要な点です。
幸い、サービススレッドの必要性が強い領域は通常 2 つだけです。
- オーバーラップ I/O 完了の実装。
- LPWSPEventSelect の実装。
これら 2 つの領域はいずれも新しい Windows Sockets 2 関数を通じてのみアクセスでき、それらの関数は 32 ビットプロセスからのみ呼び出せます。
次の 2 つの設計ルールを注意深く守れば、サービススレッドを安全に使用できます。
- 16 ビットの Windows Sockets 1.1 クライアントでは利用できない機能に対してのみサービススレッドを使用する。
- サービススレッドは必要になったときにのみ作成する。
内部サービススレッドの使用には、他にもいくつかの注意点があります。第一に、スレッドには一般に何らかのパフォーマンス上のコストが伴います。使用するスレッドはできるだけ少なくし、スレッドの切り替えも可能な限り避けてください。第二に、予期しない実行イベントによって 16 ビットプロセスがスレッドを必要とするコードパスを実行する場合に備え、コードでは常にスレッド作成時のエラーを確認し、(たとえば WSAEOPNOTSUPP を返すなどして) 適切かつ分かりやすく失敗させる必要があります。
階層化サービスプロバイダーはこの関数の実装を提供しますが、同時に、プロトコルチェーンの次の層を初期化するために WSPStartup を呼び出す、この関数のクライアントでもあります。次の層の WSPStartup の呼び出しは、この層の WSPStartup の実行中に行われる場合もあれば、 LPWSPSocket が呼び出されたときなど、遅延されて必要時に行われる場合もあります。いずれの場合も、この関数の lpProtocolInfo パラメーターがプロトコルチェーンの各層を通じて伝播される際には、いくつかの特別な考慮事項が当てはまります。
階層化プロバイダーは、lpProtocolInfo が参照する構造体の ProtocolChain を検索して、チェーン内における自身の位置 (その層自身のカタログエントリの Id を検索することによって) と、チェーン内の次の要素の識別子を特定します。次の要素が別の層である場合、次の層の WSPStartup を呼び出すときに、この層は、変更されていない同じチェーン情報を持つ、変更されていない同じ WSAProtocol_Info 構造体を参照する lpProtocolInfo を次の層に渡す必要があります。ただし、次の層がベースプロトコル (つまりチェーンの最後の要素) である場合、この層はベースプロバイダーの WSPStartup を呼び出す際に置き換えを行います。この場合、lpProtocolInfo パラメーターはベースプロバイダーの WSAPROTOCOL_INFO 構造体を参照する必要があります。
この方針の重要な利点の 1 つは、ベースサービスプロバイダーがプロトコルチェーンを意識する必要がないことです。
この同じ伝播方針は、 LPWSPAddressToString、 LPWSPDuplicateSocket、 LPWSPSocket、 LPWSPStringToAddress など、他の関数の階層化されたシーケンスを通じて WSAPROTOCOL_INFO 構造体を伝播する場合にも当てはまります。
Microsoft 公式リファレンス: 英語 (en-us) · 日本語 (ja-jp) · 原文ソース (GitHub)