当提供程序模块通过调用 NmrDeregisterProvider 函数与网络模块注册器 (NMR) 取消注册时,NMR 会针对它所附加到的每个客户端模块调用一次提供程序模块的 ProviderDetachClient 回调函数,以便提供程序模块可以在提供程序模块的取消注册过程中从所有客户端模块中分离自身。
此外,每当将提供程序模块附加到的客户端模块通过调用 NmrDeregisterClient 函数来注销 NMR 时,NMR 还会调用提供程序模块的 ProviderDetachClient 回调函数,以便提供程序模块可以在客户端模块的取消注册过程中从客户端模块中分离自身。
调用其 ProviderDetachClient 回调函数后,提供程序模块不得进一步调用客户端模块的任何 网络编程接口 (NPI) 回调函数。 如果在调用提供程序模块的 ProviderDetachClient 回调函数时,没有正在进行的对客户端模块的任何 NPI 回调函数的调用,则 ProviderDetachClient 回调函数应返回STATUS_SUCCESS。
如果在调用提供程序模块的 ProviderDetachClient 回调函数时正在进行对客户端模块的一个或多个 NPI 回调函数的调用,则 ProviderDetachClient 回调函数应返回STATUS_PENDING。 在这种情况下,在完成对客户端模块的 NPI 回调函数的所有正在进行的调用后,提供程序模块必须调用 NmrProviderDetachClientComplete 函数。 对 NmrProviderDetachClientComplete 的调用通知 NMR 从客户端模块分离提供程序模块已完成。
有关如何跟踪对客户端模块 NPI 回调函数的正在进行的调用数的详细信息,请参阅 编程注意事项。
如果提供程序模块实现 ProviderCleanupBindingContext 回调函数,则 NMR 会在提供程序模块和客户端模块完成彼此分离后调用提供程序模块的 ProviderCleanupBindingContext 回调函数。 提供程序模块的 ProviderCleanupBindingContext 回调函数应对提供程序模块的绑定上下文结构中包含的数据执行任何必要的清理。 如果提供程序模块动态分配了该结构的内存,则它应释放绑定上下文结构的内存。
例如:
// ProviderDetachClient callback function
NTSTATUS
ProviderDetachClient(
IN PVOID ProviderBindingContext
)
{
PPROVIDER_BINDING_CONTEXT BindingContext;
// Get a pointer to the binding context
BindingContext = (PPROVIDER_BINDING_CONTEXT)ProviderBindingContext;
// Set a flag indicating that the provider module is detaching
// from the client module so that no more calls are made to
// the client module's NPI callback functions.
...
// Check if there are no in-progress NPI callback function calls
// to the client module
if (...)
{
// Return success status indicating detachment is complete
return STATUS_SUCCESS;
}
// There are one or more in-progress NPI callback function
// calls to the client module
else
{
// Return pending status indicating detachment is pending
// completion of the in-progress NPI callback function calls
return STATUS_PENDING;
// When the last in-progress call to the client module's
// NPI callback functions completes, the provider module
// must call NmrProviderDetachClientComplete() with the
// binding handle for the attachment to the client module.
}
}
// ProviderCleanupBindingContext callback function
VOID
ProviderCleanupBindingContext(
IN PVOID ProviderBindingContext
)
{
PPROVIDER_BINDING_CONTEXT BindingContext;
// Get a pointer to the binding context
BindingContext = (PPROVIDER_BINDING_CONTEXT)ProviderBindingContext;
// Clean up the provider binding context structure
...
// Free the memory for provider's binding context structure
ExFreePoolWithTag(
BindingContext,
BINDING_CONTEXT_POOL_TAG
);
}