註冊 Winsock 核心應用程式
WSK 用戶端物件註冊
Winsock Kernel (WSK) 應用程式必須藉由呼叫 WskRegister 函式,註冊為 WSK 用戶端。 WskRegister 需要 WSK 應用程式初始化並傳遞指標給其 WSK 用戶端的 Network Programming Interface (NPI)(WSK_CLIENT_NPI 結構)和 WSK 註冊物件 (WSK_REGISTRATION 結構),而 WSK 註冊物件會在成功傳回時由 WskRegister 初始化。
下列程式代碼範例示範 WSK 應用程式如何註冊為 WSK 用戶端。
// Include the WSK header file
#include "wsk.h"
// WSK Client Dispatch table that denotes the WSK version
// that the WSK application wants to use and optionally a pointer
// to the WskClientEvent callback function
const WSK_CLIENT_DISPATCH WskAppDispatch = {
MAKE_WSK_VERSION(1,0), // Use WSK version 1.0
0, // Reserved
NULL // WskClientEvent callback not required for WSK version 1.0
};
// WSK Registration object
WSK_REGISTRATION WskRegistration;
// DriverEntry function
NTSTATUS
DriverEntry(
PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath
)
{
NTSTATUS Status;
WSK_CLIENT_NPI wskClientNpi;
.
.
.
// Register the WSK application
wskClientNpi.ClientContext = NULL;
wskClientNpi.Dispatch = &WskAppDispatch;
Status = WskRegister(&wskClientNpi, &WskRegistration);
if(!NT_SUCCESS(Status)) {
.
.
.
return Status;
}
.
.
.
}
WSK 應用程式不需要從其 DriverEntry 函式內呼叫 WskRegister。 例如,如果 WSK 應用程式是複雜驅動程式的子元件,則只有在啟用 WSK 應用程式子元件時,才會註冊應用程式。
WSK 應用程式必須保留傳遞至 WskRegister 的 WSK_CLIENT_DISPATCH 結構在記憶體中且保持其有效狀態,直到呼叫 WskDeregister 後註冊不再有效為止。 WSK_REGISTRATION 結構也必須保持有效且保留在記憶體中,直到 WSK 應用程式停止呼叫其他 WSK 註冊函式為止。 上述程式代碼範例會將這兩個結構保留在驅動程式的全域數據區段中,藉此將結構數據保留在記憶體中,直到卸除驅動程序為止。
WSK 提供者 NPI 擷取
在 WSK 應用程式向 WskRegister註冊為 WSK 用戶端之後,它必須使用 WskCaptureProviderNPI 函式,從 WSK 子系統擷取 WSK 提供者 NPI,才能開始使用 WSK 介面。
由於 WSK 子系統在 WSK 應用程式嘗試擷取 WSK 提供者 NPI 時可能尚未就緒,因此 WskCaptureProviderNPI 函式可讓 WSK 應用程式輪詢或等候 WSK 子系統準備就緒,如下所示:
如果 WaitTimeout 參數是WSK_NO_WAIT,則函式一律會立即傳回,而不需要等候。
如果 WaitTimeout 等於 WSK_INFINITE_WAIT,函式會等到 WSK 子系統準備就緒。
如果 WaitTimeout 是其他值,則當 WSK 子系統準備就緒或等候時間(以毫秒計算)達到 WaitTimeout的值時,函數會傳回,兩者以先發生者為準。
重要 為了避免對其他驅動程式和服務啟動產生負面影響,從其 DriverEntry 函式呼叫 WskCaptureProviderNPI 的 WSK 應用程式不應將 WaitTimeout 參數設定為WSK_INFINITE_WAIT或過度等候時間。 此外,如果 WSK 應用程式在系統啟動階段非常早啟動,應該在不同於執行 DriverEntry 的工作執行緒中,等待 WSK 子系統準備就緒。
如果呼叫 WskCaptureProviderNPI 失敗並返回 STATUS_NOINTERFACE,則 WSK 應用程式可以使用 WskQueryProviderCharacteristics 函式來查找 WSK 子系統所支援的 WSK NPI 版本範圍。 WSK 應用程式可以呼叫 WskDeregister 來取消註冊其目前的註冊實例,然後使用使用支援的 WSK NPI 版本的不同 WSK_CLIENT_DISPATCH 實例再次註冊。
WskCaptureProviderNPI 傳回成功時,其 WskProviderNpi 參數會指向 WSK 提供者 NPI (WSK_PROVIDER_NPI)可供 WSK 應用程式使用。 WSK_PROVIDER_NPI結構包含指向WSK客戶端物件的指標(WSK_CLIENT),以及WSK應用程式可用來建立WSK套接字和在WSK客戶端物件上執行其他操作的WSK函數調度表 (WSK_PROVIDER_DISPATCH)。 使用 WSK_PROVIDER_DISPATCH 函式完成 WSK 應用程式之後,它必須呼叫 WskReleaseProviderNPI來釋放 WSK 提供者 NPI。
下列程式代碼範例示範 WSK 應用程式如何擷取 WSK 提供者 NPI、使用它來建立套接字,然後釋放它。
// WSK application routine that waits for WSK subsystem
// to become ready and captures the WSK Provider NPI
NTSTATUS
WskAppWorkerRoutine(
)
{
NTSTATUS Status;
WSK_PROVIDER_NPI wskProviderNpi;
// Capture the WSK Provider NPI. If WSK subsystem is not ready yet,
// wait until it becomes ready.
Status = WskCaptureProviderNPI(
&WskRegistration, // must have been initialized with WskRegister
WSK_INFINITE_WAIT,
&wskProviderNpi
);
if(!NT_SUCCESS(Status))
{
// The WSK Provider NPI could not be captured.
if( Status == STATUS_NOINTERFACE ) {
// WSK application's requested version is not supported
}
else if( status == STATUS_DEVICE_NOT_READY ) {
// WskDeregister was invoked in another thread thereby causing
// WskCaptureProviderNPI to be canceled.
}
else {
// Some other unexpected failure has occurred
}
return Status;
}
// The WSK Provider NPI has been captured.
// Create and set up a listening socket that accepts
// incoming connections.
Status = CreateListeningSocket(&wskProviderNpi, ...);
// The WSK Provider NPI will not be used any more.
// So, release it here immediately.
WskReleaseProviderNPI(&WskRegistration);
// Return result of socket creation routine
return Status;
}
WSK 應用程式可以多次呼叫 WskCaptureProviderNPI。 對於成功傳回之 WskCaptureProviderNPI 的每個 呼叫,都必須有對應呼叫 WskReleaseProviderNPI。 在 呼叫 WskReleaseProviderNPI之後,WSK 應用程式不得對 WSK_PROVIDER_DISPATCH 中的函式進行任何進一步呼叫。