注册 Winsock 内核应用程序

WSK 客户端对象注册

Winsock 内核 (WSK) 应用程序必须通过调用 WskRegister 函数注册为 WSK 客户端。 WskRegister 要求 WSK 应用程序初始化并传递指向其 WSK 客户端 的网络编程接口 (NPI ) (WSK_CLIENT_NPI 结构) ,WSK 注册对象 (WSK_REGISTRATION 结构) 成功返回时由 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,则函数将始终立即返回而不等待。

  • 如果 WSK_INFINITE_WAIT WaitTimeout ,则函数将等到 WSK 子系统准备就绪。

  • 如果 WaitTimeout 是任何其他值,则当 WSK 子系统准备就绪或等待时间(以毫秒为单位)达到 WaitTimeout 值时,函数将返回,以先发生者为准。

重要为了避免对其他驱动程序和服务的启动产生不利影响,从其 DriverEntry 函数调用 WskCaptureProviderNPI 的 WSK 应用程序不应将 WaitTimeout 参数设置为WSK_INFINITE_WAIT或等待时间过长。 此外,如果 WSK 应用程序在系统启动阶段很早就启动,它应该等待 WSK 子系统在与 运行 DriverEntry 的工作线程不同的工作线程中准备就绪。

如果调用 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 应用程序使用完 WSK_PROVIDER_DISPATCH 函数后,它必须通过调用 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 中的函数。