初始化和注册客户端模块
客户端模块必须先初始化多个数据结构,然后才能向网络模块注册器 (NMR) 。 这些结构包括 NPI_MODULEID 结构、 NPI_CLIENT_CHARACTERISTICS 结构、 NPI_CLIENT_CHARACTERISTICS 结构) 中包含的NPI_REGISTRATION_INSTANCE结构 (,以及客户端模块定义的用于客户端模块注册上下文的结构。
如果客户端模块将自己注册到 NMR 作为 网络编程接口 (NPI) (定义 NPI 特定客户端特征)的客户端,则客户端模块还必须初始化 NPI 定义的客户端特征结构的实例。
只要客户端模块注册到 NMR,所有这些数据结构都必须保持有效并驻留在内存中。
例如,假设“EXNPI”NPI 在头文件 Exnpi.h 中定义以下内容:
// EXNPI NPI identifier
const NPIID EXNPI_NPIID = { ... };
// EXNPI client characteristics structure
typedef struct EXNPI_CLIENT_CHARACTERISTICS_
{
.
. // NPI-specific members
.
} EXNPI_CLIENT_CHARACTERISTICS, *PEXNPI_CLIENT_CHARACTERISTICS;
下面显示了将自身注册为 EXNPI NPI 客户端的客户端模块如何初始化所有这些数据结构:
// Include the NPI specific header file
#include "exnpi.h"
// Structure for the client module's NPI-specific characteristics
const EXNPI_CLIENT_CHARACTERISTICS NpiSpecificCharacteristics =
{
.
. // The NPI-specific characteristics of the client module
.
};
// Structure for the client module's identification
const NPI_MODULEID ClientModuleId =
{
sizeof(NPI_MODULEID),
MIT_GUID,
{ ... } // A GUID that uniquely identifies the client module
};
// Prototypes for the client module's callback functions
NTSTATUS
ClientAttachProvider(
IN HANDLE NmrBindingHandle,
IN PVOID ClientContext,
IN PNPI_REGISTRATION_INSTANCE ProviderRegistrationInstance
);
NTSTATUS
ClientDetachProvider(
IN PVOID ClientBindingContext
);
VOID
ClientCleanupBindingContext(
IN PVOID ClientBindingContext
);
// Structure for the client module's characteristics
const NPI_CLIENT_CHARACTERISTICS ClientCharacteristics =
{
0,
sizeof(NPI_CLIENT_CHARACTERISTICS),
ClientAttachProvider,
ClientDetachProvider,
ClientCleanupBindingContext,
{
0,
sizeof(NPI_REGISTRATION_INSTANCE),
&EXNPI_NPIID,
&ClientModuleId,
0,
&NpiSpecificCharacteristics
}
};
// Context structure for the client module's registration
typedef struct CLIENT_REGISTRATION_CONTEXT_ {
.
. // Client-specific members
.
} CLIENT_REGISTRATION_CONTEXT, *PCLIENT_REGISTRATION_CONTEXT;
// Structure for the client's registration context
CLIENT_REGISTRATION_CONTEXT ClientRegistrationContext =
{
.
. // Initial values for the registration context
.
};
客户端模块通常在其 DriverEntry 函数中初始化自身。 客户端模块main初始化任务包括:
指定 Unload 函数。 从系统卸载客户端模块时,操作系统会调用此函数。 如果客户端模块不提供卸载函数,则无法从系统卸载客户端模块。
调用 NmrRegisterClient 函数以向 NMR 注册客户端模块。
例如:
// Prototype for the client module's unload function
VOID
Unload(
PDRIVER_OBJECT DriverObject
);
// Variable to contain the handle for the registration
HANDLE ClientHandle;
// DriverEntry function
NTSTATUS
DriverEntry(
PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath
)
{
NTSTATUS Status;
// Specify the unload function
DriverObject->DriverUnload = Unload;
.
. // Other initialization tasks
.
// Register the client module with the NMR
Status = NmrRegisterClient(
&ClientCharacteristics,
&ClientRegistrationContext,
&ClientHandle
);
// Return the result of the registration
return Status;
}
如果客户端模块是多个 NPI 的客户端,则必须初始化一组独立的数据结构,并为其支持的每个 NPI 调用 NmrRegisterClient 。 如果网络模块既是客户端模块又是提供程序模块 (也就是说,它是一个 NPI 的客户端,另一个 NPI) 的提供程序,则它必须初始化两个独立的数据结构集,一个用于客户端接口,另一个用于提供程序接口,并调用 NmrRegisterClient 和 NmrRegisterProvider。
客户端模块不需要从其 DriverEntry 函数中调用 NmrRegisterClient。 例如,在客户端模块是复杂驱动程序的子组件的情况下,仅当激活客户端模块子组件时,才会注册客户端模块。