関数テーブルここからは、LSPの開発について説明します。このDLLが実装する関数はws2_32.dllによって適切なタイミングで呼び出されることになっています。LSPが実装する関数の中で最初に呼び出されるのは、WSPStartupです。SPIのバージョンが格納されます。lpWSPDataは、LSPに関する情報を表すWSPDATA構造体のアドレスが格納されます。これは、LSP自身が初期化する必要があります。lpProtocolInfoは、WSAPROTOCOL_INFOW構造体のアドレスが格納されます。この構造体は、ダミーエントリではない、実際のエントリを表しています。UpcallTableは、WSPUPCALLTABLE構造体が格納されます。この構造体には、LSPからws2_32.dllの機能を利用するための関数アドレスが設定されています。lpProcTableは、WSPPROC_TABLE構造体のアドレスが格納されます。これは、LSP自身が初期化する必要があります。関数が成功した場合は、0を返します。WSPStartupで最も重要な引数は、lpProcTableです。ws2_32.dllはこのテーブルを基に、LSPが実装している関数を呼び出すことになっています。WSPPROC_TABLEは、次のように定義されています。LPWSPPROC_TABLE;この構造体に格納されているメンバは、ws2_32.dllから呼び出される可能性のある関数の全リストです。メンバ名から想像できるようになっています。Winsockアプリケーションがacceptを呼び出した場合に呼ばれることになります。lpWSPAcceptのみを初期化するわけにはいきません。全てのメンバを初期化しておく必要があります。そのようなことはありません。その関数のアドレスをWSPPROC_TABLE構造体に指定する方法が成立します。初期化されたWSPPROC_TABLE構造体をlpProcTableにコピーするようにします。LSPが独自に処理したい関数のアドレスをlpProcTableの適切なメンバに指定します。その他の関数は下のエントリのDLLに処理を任すことができます。下に存在するエントリを取得するには、WSPStartupのlpProtocolInfoを利用することになります。これを頼りにすれば下のエントリのWSAPROTOCOL_INFOW構造体を取得することができます。WSCGetProviderPathを呼び出してパスを取得することになります。lpErrno);lpProviderIdは、エントリのGUIDを指定します。lpszProviderDllPathは、DLLのパスを受け取るバッファを指定します。lpProviderDllPathLenは、lpszProviderDllPathのサイズを指定します。lpErrnoは、エラー情報を表す変数のアドレスを指定します。それを基にDLLをロードすることができるようになります。次に、WSPStartupの実装例を示します。0;}FindNextEntryという自作関数は、第1引数に指定したエントリの下に存在するエントリを第2引数に返します。失敗した場合はWSPStartupの引数を正しく初期化できなかったことを示すWSAEPROVIDERFAILEDINITを返します。DLLをロードする部分は、次のようになっています。下に存在するエントリのDLLパスを取得します。この点を考慮してExpandEnvironmentStringsを呼び出すようにしています。これにより、szDllPathには正規のDLLパスが格納されます。ExpandEnvironmentStringsやLoadLibraryは、UNICODE版の関数を呼び出しています。WSPStartupの呼び出しは、次のようになっています。WSAEPROVIDERFAILEDINIT;}各エントリのDLLは、ws2_32.dllとの接点として必ずWSPStartupをエクスポートしています。下のエントリのWSPStartupを呼び出すのは、ws2_32.dllではなくLSP自身です。理由は既に述べたように、下のエントリの関数テーブルを取得するためです。第3引数はlpProtocolInfoではなく、下のエントリを示すnextEntryを指定するようにします。WSPStartupの呼び出しが成功すれば、lpProcTableの初期化を行います。WSPSocket;先のWSPStartupの呼び出しで、g_procTableには下のエントリの関数テーブルが格納されています。よって、これをlpProcTableにコピーし、その後にLSPが実装する独自の関数のアドレスを指定します。LSPが実装しているWSPSocketが呼ばれることになります。g_procTableはグローバルに定義しています。残りの処理は、次のようになっています。これをそのままlpWSPDataにコピーするようにしています。当然ながら、LSP独自の情報を指定しても問題ありません。WSPUPCALLTABLE構造体は、後で必要になるためグローバル変数として保存しておきます。WSPStartupで呼び出しているFindNextEntryは、次のような実装になります。その中からChainEntries[1]と等しいエントリを検索します。検索で一致した場合は下のエントリを発見できたことになります。よって、lpNextEntryに目的のエントリをコピーします。