啟用和停用事件回呼函式

Winsock Kernel (WSK) 應用程式可以實作 WSK 子系統非同步呼叫的事件回呼函式,以在通訊端發生特定 事件 時通知應用程式。 每當 WSK 應用程式建立通訊端或接受接聽通訊端上的通訊端時,就可以將用戶端 分派資料表 結構提供給 WSK 子系統。 此分派資料表包含新通訊端之 WSK 應用程式事件回呼函式的指標。 如果 WSK 應用程式未針對特定通訊端實作任何事件回呼函式,則不需要為該通訊端的 WSK 子系統提供用戶端分派資料表結構。

除了接聽通訊端的 WskInspectEventWskAbortEvent 事件回呼函式之外,所有通訊端的事件回呼函式都可以使用 SO_WSK_EVENT_CALLBACK 通訊端選項來啟用或停用。 WSK 應用程式可以同時在通訊端上啟用多個事件回呼函式。 不過,WSK 應用程式必須個別停用每個事件回呼函式。

下列程式碼範例示範 WSK 應用程式如何使用 SO_WSK_EVENT_CALLBACK 通訊端選項,在連線導向通訊端上啟用 WskDisconnectEventWskReceiveEvent 事件回呼函式。

// 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 應用程式在通訊端上啟用條件式接受模式時,才會啟用 WskInspectEventWskAbortEvent 事件回呼函式。 WSK 應用程式可在接聽通訊端上啟用條件式接受模式,方法是在將通訊端系結至本機傳輸位址之前,先設定通訊端 的 SO_CONDITIONAL_ACCEPT 通訊端選項。 如需如何設定通訊端選項的詳細資訊,請參閱 在通訊端上執行控制作業

在接聽通訊端上啟用條件式接受模式之後,就無法停用通訊端的 WskInspectEventWskAbortEvent 事件回呼函式。 如需在接聽通訊端上有條件地接受連入連線的詳細資訊,請參閱 接聽和接受連入連線

接聽通訊端可以在接聽通訊端的 WskAcceptEvent 事件回呼函式所接受的連接導向通訊端上自動啟用事件回呼函式。 WSK 應用程式會在接聽通訊端上啟用連線導向通訊端事件回呼函式,自動啟用這些回呼函式。 如需此程式的詳細資訊,請參閱 SO_WSK_EVENT_CALLBACK

如果 WSK 應用程式一律會在它建立的每個通訊端上啟用特定事件回呼函式,應用程式可以使用用戶端控制作業,將 WSK 子系統設定為使用 WSK_SET_STATIC_EVENT_CALLBACKS 用戶端控制作業自動啟用這些事件回呼函式。 以這種方式啟用的事件回呼函式一律會啟用,且稍後 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用戶端控制作業來自動啟用特定事件回呼函式,它必須先執行此動作,才能建立任何通訊端。