EternalWindows
LSP / LSPのインストール

前節では、インストールに必要な情報を用意し、 Windows Vista以降で使用可能なWSCInstallProviderAndChainsを呼び出しました。 今回は、従来のインストールを行うために必要となるWSCInstallProviderについて説明します。

int WSPAPI WSCInstallProvider(
  const LPGUID lpProviderId,
  const LPWSTR lpszProviderDllPath,
  const LPWSAPROTOCOL_INFO lpProtocolInfoList,
  DWORD dwNumberOfEntries,
  LPINT lpErrno
);

lpProviderIdは、LSPのGUIDを指定します。 lpszProviderDllPathは、LSPのファイルパスを指定します。 lpProtocolInfoListは、インストールするLSPの情報を格納したWSAPROTOCOL_INFO構造体の配列を指定します。 WSCInstallProviderAndChainsのように、LSPの下に存在するエントリの情報を指定するわけではないので、注意してください。 dwNumberOfEntriesは、lpProtocolInfoListの要素数を指定します。 lpErrnoは、エラー情報を受け取る変数のアドレスを指定します。 戻り値は、関数が成功した場合に0が返り、失敗した場合にSOCKET_ERRORが返ります。

WSCInstallProviderによってインストールされたエントリは、オーダーの最後尾に設定されることになります。 これでは、ws2_32.dllからの呼び出しを先に検出することができなくなりますから、 下に存在することになるエントリより前方に移動させる必要があります。 適切にソートされたエントリIDの配列をWSCWriteProviderOrderに指定すれば、 新しくオーダーが決定されることになります。

int WSPAPI WSCWriteProviderOrder(
  LPDWORD lpwdCatalogEntryId,
  DWORD dwNumberOfEntries
);

lpwdCatalogEntryIdは、エントリIDの配列を指定します。 dwNumberOfEntriesは、lpwdCatalogEntryIdの要素数を指定します。 戻り値は、関数が成功した場合にERROR_SUCCESSが返ります。

次に、前節で呼び出したInstallLspLegacyの内部を示します。

BOOL InstallLspLegacy(GUID guidProvider, LPWSTR lpszProviderPath, LPWSTR lpszProtocolName, LPWSAPROTOCOL_INFOW lpBaseEntryList, int nEntryCount, LPINT lpnError)
{
	int                 i, j;
	int                 nTotalEntryCount;
	int                 nOrderCount;
	int                 nError;
	DWORD               dwSize;
	DWORD               dwDummyEntryId = 0;
	LPDWORD             lpdwEntryIdOrder;
	WSAPROTOCOL_INFOW   dummyEntry;
	LPWSAPROTOCOL_INFOW lpEntryList;
	LPWSAPROTOCOL_INFOW lpNewEntryList;
	WCHAR               szOverProtocolName[WSAPROTOCOL_LEN];
	
	CopyMemory(&dummyEntry, &lpBaseEntryList[0], sizeof(WSAPROTOCOL_INFOW));

	dummyEntry.iSocketType            = 0;
	dummyEntry.iProtocol              = 0;
	dummyEntry.dwProviderFlags        |= PFL_HIDDEN;
	dummyEntry.dwProviderFlags        &= (~PFL_MATCHES_PROTOCOL_ZERO);
	dummyEntry.ProtocolChain.ChainLen = 0;
	lstrcpyW(dummyEntry.szProtocol, lpszProtocolName);

	if (WSCInstallProvider(&guidProvider, lpszProviderPath, &dummyEntry, 1, lpnError) == SOCKET_ERROR)
		return FALSE;

	WSCEnumProtocols(NULL, NULL, &dwSize, &nError);
	lpEntryList = (LPWSAPROTOCOL_INFOW)HeapAlloc(GetProcessHeap(), 0, dwSize);
	nTotalEntryCount = WSCEnumProtocols(NULL, lpEntryList, &dwSize, &nError);

	for (i = 0; i < nTotalEntryCount; i++) {
		if (IsEqualGUID(lpEntryList[i].ProviderId, guidProvider)) {
			dwDummyEntryId = lpEntryList[i].dwCatalogEntryId;
			break;
		}
	}

	lpNewEntryList = (LPWSAPROTOCOL_INFOW)HeapAlloc(GetProcessHeap(), 0, sizeof(WSAPROTOCOL_INFOW) * nEntryCount);

	for (i = 0; i < nEntryCount; i++) {
		CopyMemory(&lpNewEntryList[i], &lpBaseEntryList[i], sizeof(WSAPROTOCOL_INFOW));

		for (j = lpNewEntryList[i].ProtocolChain.ChainLen; j > 0; j--)
			lpNewEntryList[i].ProtocolChain.ChainEntries[j] = lpNewEntryList[i].ProtocolChain.ChainEntries[j - 1];

		lpNewEntryList[i].ProtocolChain.ChainEntries[0] = dwDummyEntryId;
		lpNewEntryList[i].ProtocolChain.ChainEntries[1] = lpBaseEntryList[i].dwCatalogEntryId;
		lpNewEntryList[i].ProtocolChain.ChainLen++;
		
		wsprintfW(szOverProtocolName, L"%s over [%s]", lpszProtocolName, lpBaseEntryList[i].szProtocol);
		lstrcpyW(lpNewEntryList[i].szProtocol, szOverProtocolName);
		
		CoCreateGuid(&lpNewEntryList[i].ProviderId);
		
		if (WSCInstallProvider(&lpNewEntryList[i].ProviderId, lpszProviderPath, &lpNewEntryList[i], 1, lpnError) == SOCKET_ERROR) {
			WSCDeinstallProvider(&guidProvider, &nError);
			HeapFree(GetProcessHeap(), 0, lpNewEntryList);
			HeapFree(GetProcessHeap(), 0, lpEntryList);
			return FALSE;
		}
	}

	HeapFree(GetProcessHeap(), 0, lpEntryList);
	WSCEnumProtocols(NULL, NULL, &dwSize, &nError);
	lpEntryList = (LPWSAPROTOCOL_INFOW)HeapAlloc(GetProcessHeap(), 0, dwSize);
	nTotalEntryCount = WSCEnumProtocols(NULL, lpEntryList, &dwSize, &nError);

	lpdwEntryIdOrder = (LPDWORD)HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD) * nTotalEntryCount);

	nOrderCount = 0;
	for (i = 0; i < nTotalEntryCount; i++) {
		if (lpEntryList[i].ProtocolChain.ChainLen > 0 && lpEntryList[i].ProtocolChain.ChainEntries[0] == dwDummyEntryId)
			lpdwEntryIdOrder[nOrderCount++] = lpEntryList[i].dwCatalogEntryId;
	}

	for (i = 0; i < nTotalEntryCount; i++) {
		if (lpEntryList[i].ProtocolChain.ChainLen == 0 || lpEntryList[i].ProtocolChain.ChainEntries[0] != dwDummyEntryId)
			lpdwEntryIdOrder[nOrderCount++] = lpEntryList[i].dwCatalogEntryId;
	}

	nError = WSCWriteProviderOrder(lpdwEntryIdOrder, nTotalEntryCount);

	HeapFree(GetProcessHeap(), 0, lpdwEntryIdOrder);
	HeapFree(GetProcessHeap(), 0, lpNewEntryList);
	HeapFree(GetProcessHeap(), 0, lpEntryList);

	return nError == ERROR_SUCCESS;
}

InstallLspLegacyの主な処理内容は、 ダミーエントリのインストール、実際のエントリのインストール、 新しいオーダーの設定の3つになります。 各種処理を順に見ていきます。

CopyMemory(&dummyEntry, &lpBaseEntryList[0], sizeof(WSAPROTOCOL_INFOW));

dummyEntry.iSocketType            = 0;
dummyEntry.iProtocol              = 0;
dummyEntry.dwProviderFlags        |= PFL_HIDDEN;
dummyEntry.dwProviderFlags        &= (~PFL_MATCHES_PROTOCOL_ZERO);
dummyEntry.ProtocolChain.ChainLen = 0;
lstrcpyW(dummyEntry.szProtocol, lpszProtocolName);

if (WSCInstallProvider(&guidProvider, lpszProviderPath, &dummyEntry, 1, lpnError) == SOCKET_ERROR)
	return FALSE;

このコードは、ダミーエントリをインストールする部分です。 まず、dummyEntryのメンバを一通り初期化するという意味で、 lpBaseEntryList[0]をコピーしています。 lpBaseEntryListに指定するインデックスは、nEntryCountの範囲内ならばどれでも構いません。 コピーが終了したら、ダミーエントリとしての情報を維持するように、dummyEntryの一部のメンバを書き換えます。 ここで特に重要なのは、dwProviderFlagsにPFL_HIDDENを指定する点と、 ProtocolChain.ChainLenに0を指定する点です。 PFL_HIDDENを指定することで、WSAEnumProtocolsの列挙から除外され、 0を指定することでダミーエントリとして認識されるようになります。 なお、新しく作成されたエントリのエントリIDは、自動で決定されます。

WSCEnumProtocols(NULL, NULL, &dwSize, &nError);
lpEntryList = (LPWSAPROTOCOL_INFOW)HeapAlloc(GetProcessHeap(), 0, dwSize);
nTotalEntryCount = WSCEnumProtocols(NULL, lpEntryList, &dwSize, &nError);

for (i = 0; i < nTotalEntryCount; i++) {
	if (IsEqualGUID(lpEntryList[i].ProviderId, guidProvider)) {
		dwDummyEntryId = lpEntryList[i].dwCatalogEntryId;
		break;
	}
}

lpNewEntryList = (LPWSAPROTOCOL_INFOW)HeapAlloc(GetProcessHeap(), 0, sizeof(WSAPROTOCOL_INFOW) * nEntryCount);

for (i = 0; i < nEntryCount; i++) {
	CopyMemory(&lpNewEntryList[i], &lpBaseEntryList[i], sizeof(WSAPROTOCOL_INFOW));

	for (j = lpNewEntryList[i].ProtocolChain.ChainLen; j > 0; j--)
		lpNewEntryList[i].ProtocolChain.ChainEntries[j] = lpNewEntryList[i].ProtocolChain.ChainEntries[j - 1];

	lpNewEntryList[i].ProtocolChain.ChainEntries[0] = dwDummyEntryId;
	lpNewEntryList[i].ProtocolChain.ChainEntries[1] = lpBaseEntryList[i].dwCatalogEntryId;
	lpNewEntryList[i].ProtocolChain.ChainLen++;
	
	wsprintfW(szOverProtocolName, L"%s over [%s]", lpszProtocolName, lpBaseEntryList[i].szProtocol);
	lstrcpyW(lpNewEntryList[i].szProtocol, szOverProtocolName);
	
	CoCreateGuid(&lpNewEntryList[i].ProviderId);
	
	if (WSCInstallProvider(&lpNewEntryList[i].ProviderId, lpszProviderPath, &lpNewEntryList[i], 1, lpnError) == SOCKET_ERROR) {
		WSCDeinstallProvider(&guidProvider, &nError);
		HeapFree(GetProcessHeap(), 0, lpNewEntryList);
		HeapFree(GetProcessHeap(), 0, lpEntryList);
		return FALSE;
	}
}

このコードは、実際のエントリをインストールする部分です。 まず、WSCEnumProtocolsで全てのエントリのリストを取得し、 その中から先ほどインストールしたダミーエントリのエントリIDを取得します。 これは、実際のエントリをインストールするために必要です。 続いて、lpBaseEntryListを実際のエントリを示すlpNewEntryListにコピーし、 実際のエントリ用に適切に調整します。 まず、ChainEntriesに格納されている各エントリIDを1つ後方にずらします。 たとえば、ChainEntries[1]はChainEntries[2]に格納され、 ChainEntries[0]はChainEntries[1]に格納されます。 こうすることで、ChainEntries[0]に空きが生じるため、ここにダミーエントリのエントリIDを指定します。 また、ChainEntries[1]には、実際のエントリの下に存在することになるエントリのIDを指定します。 下に存在することになるエントリはlpBaseEntryListで表されますから、このエントリのIDを指定します。 プロトコルチェーンの階層が1つ増えたことになるため、ChainLenの増加を忘れずに行っておきます。 szProtocolには、szOverProtocolNameの書式通りの文字列を指定するようにします。 この書式のおかけで、このエントリがどのエントリの上に存在するかが分かるようになります。 ProviderIdは、CoCreateGuidで作成した新規のGUIDを指定します。 LSPのGUIDは、guidProviderに格納されていますが、これはダミーエントリのGUIDとして指定します。

HeapFree(GetProcessHeap(), 0, lpEntryList);
WSCEnumProtocols(NULL, NULL, &dwSize, &nError);
lpEntryList = (LPWSAPROTOCOL_INFOW)HeapAlloc(GetProcessHeap(), 0, dwSize);
nTotalEntryCount = WSCEnumProtocols(NULL, lpEntryList, &dwSize, &nError);

lpdwEntryIdOrder = (LPDWORD)HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD) * nTotalEntryCount);

nOrderCount = 0;
for (i = 0; i < nTotalEntryCount; i++) {
	if (lpEntryList[i].ProtocolChain.ChainLen > 0 && lpEntryList[i].ProtocolChain.ChainEntries[0] == dwDummyEntryId)
		lpdwEntryIdOrder[nOrderCount++] = lpEntryList[i].dwCatalogEntryId;
}

for (i = 0; i < nTotalEntryCount; i++) {
	if (lpEntryList[i].ProtocolChain.ChainLen == 0 || lpEntryList[i].ProtocolChain.ChainEntries[0] != dwDummyEntryId)
		lpdwEntryIdOrder[nOrderCount++] = lpEntryList[i].dwCatalogEntryId;
}

nError = WSCWriteProviderOrder(lpdwEntryIdOrder, nTotalEntryCount);

このコードは、新しくオーダーを設定する部分です。 まず、lpEntryListを一度開放して、新しく初期化し直します。 これは、lpEntryListに先ほどインストールしたエントリを含めるためです。 次に、全てのエントリのIDを格納できる配列を確保し、 1回目のループでオーダーの前方に移動させたいエントリのIDを配列に格納します。 前方に移動させるエントリというのは、先ほどインストールした実際のエントリの事であり、 このエントリにはChainEntries[0]がダミーエントリのIDであるという特徴があります。 よって、ChainEntries[0]がdwDummyEntryIdと一致したエントリのIDは、 いち早くlpdwEntryIdOrderに格納されることになります。 ChainLenが0より大きいかを確認しているのは、lpEntryList[i]がダミーエントリである場合に、 ChainEntries[0]への参照を防ぐためです。 2回目のループでは、1回目のループと反対の条件を記述し、 1回目のループでlpdwEntryIdOrderに格納されなかった残りのエントリのIDを格納します。


戻る