启用和禁用事件回调函数
Winsock 内核 (WSK) 应用程序可以实现 WSK 子系统异步调用的事件回调函数,以便在套接字上发生某些 事件 时通知应用程序。 每当 WSK 应用程序创建套接字或在侦听套接字上接受套接字时,它都可以向 WSK 子系统提供客户端 调度表 结构。 此调度表包含指向新套接字的 WSK 应用程序事件回调函数的指针。 如果 WSK 应用程序未为特定套接字实现任何事件回调函数,则无需为该套接字的 WSK 子系统提供客户端调度表结构。
可以使用 SO_WSK_EVENT_CALLBACK 套接字选项启用或禁用套接字的所有事件回调函数(侦听套接字的 WskInspectEvent 和 WskAbortEvent 事件回调函数除外)。 WSK 应用程序可以同时在套接字上启用多个事件回调函数。 但是,WSK 应用程序必须单独禁用每个事件回调函数。
下面的代码示例演示 WSK 应用程序如何使用 SO_WSK_EVENT_CALLBACK 套接字选项在面向连接的套接字上启用 WskDisconnectEvent 和 WskReceiveEvent 事件回调函数。
// Function to enable the WskDisconnectEvent and WskReceiveEvent
// event callback functions on a connection-oriented socket
NTSTATUS
EnableDisconnectAndRecieveCallbacks(
PWSK_SOCKET Socket
)
{
PWSK_PROVIDER_CONNECTION_DISPATCH Dispatch;
WSK_EVENT_CALLBACK_CONTROL EventCallbackControl;
NTSTATUS Status;
// Get pointer to the socket's provider dispatch structure
Dispatch =
(PWSK_PROVIDER_CONNECTION_DISPATCH)(Socket->Dispatch);
// Specify the WSK NPI identifier
EventCallbackControl.NpiId = &NPI_WSK_INTERFACE_ID;
// Set the event flags for the event callback functions that
// are to be enabled on the socket
EventCallbackControl.EventMask =
WSK_EVENT_DISCONNECT | WSK_EVENT_RECEIVE;
// Initiate the control operation on the socket
Status =
Dispatch->WskControlSocket(
Socket,
WskSetOption,
SO_WSK_EVENT_CALLBACK,
SOL_SOCKET,
sizeof(WSK_EVENT_CALLBACK_CONTROL),
&EventCallbackControl,
0,
NULL,
NULL,
NULL // No IRP for this control operation
);
// Return the status of the call to WskControlSocket()
return Status;
}
下面的代码示例演示 WSK 应用程序如何使用 SO_WSK_EVENT_CALLBACK 套接字选项在面向连接的套接字上禁用 WskReceiveEvent 事件回调函数。
// Prototype for the disable disconnect IoCompletion routine
NTSTATUS
DisableDisconnectComplete(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context
);
// Function to disable the WskDisconnectEvent event
// callback functions on a connection-oriented socket
NTSTATUS
DisableDisconnectCallback(
PWSK_SOCKET Socket
)
{
PWSK_PROVIDER_CONNECTION_DISPATCH Dispatch;
PIRP Irp;
WSK_EVENT_CALLBACK_CONTROL EventCallbackControl;
NTSTATUS Status;
// Get pointer to the socket's provider dispatch structure
Dispatch =
(PWSK_PROVIDER_CONNECTION_DISPATCH)(Socket->Dispatch);
// Allocate an IRP
Irp =
IoAllocateIrp(
1,
FALSE
);
// Check result
if (!Irp)
{
// Return error
return STATUS_INSUFFICIENT_RESOURCES;
}
// Set the completion routine for the IRP
IoSetCompletionRoutine(
Irp,
DisableDisconnectComplete,
Socket, // Use the socket object for the context
TRUE,
TRUE,
TRUE
);
// Specify the WSK NPI identifier
EventCallbackControl.NpiId = &NPI_WSK_INTERFACE_ID;
// Set the event flag for the event callback function that
// is to be disabled on the socket along with the disable flag
EventCallbackControl.EventMask =
WSK_EVENT_DISCONNECT | WSK_EVENT_DISABLE;
// Initiate the control operation on the socket
Status =
Dispatch->WskControlSocket(
Socket,
WskSetOption,
SO_WSK_EVENT_CALLBACK,
SOL_SOCKET,
sizeof(WSK_EVENT_CALLBACK_CONTROL),
&EventCallbackControl,
0,
NULL,
NULL,
Irp
);
// Return the status of the call to WskControlSocket()
return Status;
}
// Disable disconnect IoCompletion routine
NTSTATUS
DisableDisconnectComplete(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context
)
{
UNREFERENCED_PARAMETER(DeviceObject);
PWSK_SOCKET Socket;
// Check the result of the control operation
if (Irp->IoStatus.Status == STATUS_SUCCESS)
{
// The WskDisconnectEvent event callback
// function is now disabled
// Get the socket object from the context
Socket = (PWSK_SOCKET)Context;
// Perform the next operation on the socket
...
}
// Error status
else
{
// Handle error
...
}
// Free the IRP
IoFreeIrp(Irp);
// Always return STATUS_MORE_PROCESSING_REQUIRED to
// terminate the completion processing of the IRP.
return STATUS_MORE_PROCESSING_REQUIRED;
}
对于侦听套接字,仅当 WSK 应用程序在套接字上启用条件接受模式时,才启用 WskInspectEvent 和 WskAbortEvent 事件回调函数。 WSK 应用程序通过在将套接字绑定到本地传输地址之前为套接字设置 SO_CONDITIONAL_ACCEPT 套接字选项,在侦听套接字上启用条件接受模式。 有关如何设置套接字选项的详细信息,请参阅 对套接字执行控制操作。
在侦听套接字上启用条件接受模式后,无法禁用套接字的 WskInspectEvent 和 WskAbortEvent 事件回调函数。 有关在侦听套接字上有条件地接受传入连接的详细信息,请参阅 侦听和接受传入连接。
侦听套接字可以在侦听套接字的 WskAcceptEvent 事件回调函数接受的面向连接的套接字上自动启用事件回调函数。 WSK 应用程序通过在侦听套接字上启用面向连接的套接字事件回调函数,自动启用这些回调函数。 有关此过程的详细信息,请参阅 SO_WSK_EVENT_CALLBACK。
如果 WSK 应用程序始终在创建的每个套接字上启用某些事件回调函数,则应用程序可以使用 WSK_SET_STATIC_EVENT_CALLBACKS 客户端控制操作将 WSK 子系统配置为自动启用这些事件回调函数。 以这种方式启用的事件回调函数始终处于启用状态,WSK 应用程序以后不能禁用或重新启用。 如果 WSK 应用程序始终在它创建的每个套接字上启用某些事件回调函数,则应用程序应使用此方法自动启用这些事件回调函数,因为它将产生更好的性能。
下面的代码示例演示 WSK 应用程序如何使用 WSK_SET_STATIC_EVENT_CALLBACKS 客户端控制操作在数据报套接字上自动启用 WskReceiveFromEvent 事件回调函数,并在面向连接的套接字上启用 WskReceiveEvent 事件回调函数。
// Function to set static event callbacks
NTSTATUS
SetStaticEventCallbacks(
PWSK_APP_BINDING_CONTEXT BindingContext,
)
{
WSK_EVENT_CALLBACK_CONTROL EventCallbackControl;
NTSTATUS Status;
// Specify the WSK NPI identifier
EventCallbackControl.NpiId = &NPI_WSK_INTERFACE_ID;
// Set the event flags for the event callback functions that
// are to be automatically enabled on every new socket
EventCallbackControl.EventMask =
WSK_EVENT_RECEIVE_FROM | WSK_EVENT_RECEIVE;
// Perform client control operation
Status =
BindingContext->
WskProviderDispatch->
WskControlClient(
BindingContext->WskClient,
WSK_SET_STATIC_EVENT_CALLBACKS,
sizeof(WSK_EVENT_CALLBACK_CONTROL),
&EventCallbackControl,
0,
NULL,
NULL,
NULL // No IRP for this control operation
);
// Return status of client control operation
return Status;
}
如果 WSK 应用程序使用 WSK_SET_STATIC_EVENT_CALLBACKS 客户端控制操作自动启用某些事件回调函数,则必须在创建任何套接字之前执行此操作。