初始化和注册提供程序模块

提供程序模块必须先初始化多个数据结构,然后才能将自身注册到网络模块注册器 (NMR) 。 这些结构包括 NPI_MODULEID 结构、 NPI_PROVIDER_CHARACTERISTICS 结构、 包含在NPI_PROVIDER_CHARACTERISTICS 结构) 中的NPI_REGISTRATION_INSTANCE结构 (,以及提供程序模块定义的用于提供程序模块注册上下文的结构。

如果提供程序模块将自身注册到 NMR 作为 网络编程接口 (NPI) (用于定义 NPI 特定提供程序特征)的提供程序,则提供程序模块还必须初始化由 NPI 定义的提供程序特征结构的实例。

只要提供程序模块已注册到 NMR,所有这些数据结构都必须保持有效并驻留在内存中。

例如,假设“EXNPI”NPI 在头文件 Exnpi.h 中定义以下内容:

// EXNPI NPI identifier
const NPIID EXNPI_NPIID = { ... };

// EXNPI provider characteristics structure
typedef struct EXNPI_PROVIDER_CHARACTERISTICS_
{
  .
  . // NPI-specific members
  .
} EXNPI_PROVIDER_CHARACTERISTICS, *PEXNPI_PROVIDER_CHARACTERISTICS;

下面显示了将自身注册为 EXNPI NPI 的提供程序模块如何初始化所有这些数据结构:

// Include the NPI specific header file
#include "exnpi.h"

// Structure for the provider module's NPI-specific characteristics
const EXNPI_PROVIDER_CHARACTERISTICS NpiSpecificCharacteristics =
{
  .
  . // The NPI-specific characteristics of the provider module
  .
};

// Structure for the provider module's identification
const NPI_MODULEID ProviderModuleId =
{
  sizeof(NPI_MODULEID),
  MIT_GUID,
  { ... }  // A GUID that uniquely identifies the provider module
};

// Prototypes for the provider module's callback functions
NTSTATUS
  ProviderAttachClient(
    IN HANDLE NmrBindingHandle,
    IN PVOID ProviderContext,
    IN PNPI_REGISTRATION_INSTANCE ClientRegistrationInstance,
    IN PVOID ClientBindingContext,
    IN CONST VOID *ClientDispatch,
    OUT PVOID *ProviderBindingContext,
    OUT PVOID *ProviderDispatch
    );

NTSTATUS
  ProviderDetachClient(
    IN PVOID ProviderBindingContext
    );

VOID
  ProviderCleanupBindingContext(
    IN PVOID ProviderBindingContext
    );

// Structure for the provider module's characteristics
const NPI_PROVIDER_CHARACTERISTICS ProviderCharacteristics =
{
  0,
  sizeof(NPI_PROVIDER_CHARACTERISTICS),
  ProviderAttachClient,
  ProviderDetachClient,
  ProviderCleanupBindingContext,
  {
    0,
    sizeof(NPI_REGISTRATION_INSTANCE),
    &EXNPI_NPIID,
    &ProviderModuleId,
    0,
    &NpiSpecificCharacteristics
  }
};

// Context structure for the provider module's registration
typedef struct PROVIDER_REGISTRATION_CONTEXT_ {
  .
  . // Provider-specific members
  .
} PROVIDER_REGISTRATION_CONTEXT, *PPROVIDER_REGISTRATION_CONTEXT;

// Structure for the provider's registration context
PROVIDER_REGISTRATION_CONTEXT ProviderRegistrationContext =
{
  .
  . // Initial values for the registration context
  .
};

提供程序模块通常在其 DriverEntry 函数中初始化自身。 提供程序模块main初始化任务包括:

  • 指定 Unload 函数。 从系统卸载提供程序模块时,操作系统会调用此函数。 如果提供程序模块不提供 unload 函数,则无法从系统卸载该提供程序模块。

  • 调用 NmrRegisterProvider 函数以向 NMR 注册提供程序模块。

例如:

// Prototype for the provider module's unload function
VOID
  Unload(
    PDRIVER_OBJECT DriverObject
   );

// Variable to contain the handle for the registration
HANDLE ProviderHandle;

// DriverEntry function
NTSTATUS
  DriverEntry(
    PDRIVER_OBJECT DriverObject,
    PUNICODE_STRING RegistryPath
    )
{
  NTSTATUS Status;

  // Specify the unload function
  DriverObject->DriverUnload = Unload;

  .
  . // Other initialization tasks
  .

  // Register the provider module with the NMR
  Status = NmrRegisterProvider(
    &ProviderCharacteristics,
    &ProviderRegistrationContext,
    &ProviderHandle
    );

  // Return the result of the registration
  return Status;
}

如果提供程序模块是多个 NPI 的提供程序,则必须初始化一组独立的数据结构,并为它支持的每个 NPI 调用 NmrRegisterProvider 。 如果网络模块既是提供程序模块,又是客户端模块 (也就是说,它是一个 NPI 的提供程序,另一个 NPI) 的客户端,则它必须初始化两个独立的数据结构集,一个用于提供程序接口,另一个用于客户端接口,并同时调用 NmrRegisterProviderNmrRegisterClient

提供程序模块不需要从其 DriverEntry 函数中调用 NmrRegisterProvider。 例如,在提供程序模块是复杂驱动程序的子组件的情况下,仅当激活提供程序模块子组件时,才会注册提供程序模块。

有关实现提供程序模块的 Unload 函数的详细信息,请参阅 卸载提供程序模块