Condividi tramite


Uso dei runtime di integrazione con funzioni kernel Winsock

Winsock Kernel (WSK) Network Programming Interface (NPI) usa i provider di integrazione per il completamento asincrono delle operazioni di I/O di rete. Ogni funzione WSK accetta un puntatore a un IRP come parametro. Il sottosistema WSK completa l'IRP dopo il completamento dell'operazione eseguita dalla funzione WSK.

Un IRP usato da un'applicazione WSK per passare a una funzione WSK può avere origine in uno dei modi seguenti.

  • L'applicazione WSK alloca l'IRP chiamando la funzione IoAllocateIrp . In questo caso, l'applicazione WSK deve allocare l'IRP con almeno un percorso dello stack di I/O.

  • L'applicazione WSK riutilizza un IRP completato allocato in precedenza. In questo caso, WSK deve chiamare la funzione IoReuseIrp per reinizializzare l'IRP.

  • L'applicazione WSK usa un IRP che è stato passato a esso da un driver di livello superiore o dal gestore di I/O. In questo caso, l'IRP deve avere almeno un percorso dello stack di I/O rimanente disponibile per l'uso dal sottosistema WSK.

Dopo che un'applicazione WSK ha un IRP da usare per chiamare una funzione WSK, può impostare una routine IoCompletion da chiamare quando l'IRP viene completato dal sottosistema WSK. Un'applicazione WSK imposta una routine IoCompletion per un IRP chiamando la funzione IoSetCompletionRoutine . A seconda della modalità di origine dell'IRP, è necessaria o facoltativa una routine IoCompletion .

  • Se l'applicazione WSK ha allocato l'IRP o riutilizza un IRP allocato in precedenza, deve impostare una routine IoCompletion per L'IRP prima di chiamare una funzione WSK. In questo caso, l'applicazione WSK deve specificare TRUE per i parametri InvokeOnSuccess, InvokeOnError e InvokeOnCancel passati alla funzione IoSetCompletionRoutine per garantire che la routine IoCompletion venga sempre chiamata. Inoltre, la routine IoCompletion impostata per l'IRP deve restituire sempre STATUS_MORE_PROCESSING_REQUIRED per terminare l'elaborazione del completamento di IRP. Se l'applicazione WSK viene eseguita usando l'IRP dopo la chiamata alla routine IoCompletion , deve chiamare la funzione IoFreeIrp per liberare l'IRP prima di tornare dalla routine IoCompletion . Se l'applicazione WSK non libera l'IRP, può riutilizzare l'IRP per una chiamata a un'altra funzione WSK.

  • Se l'applicazione WSK usa un IRP passato da un driver di livello superiore o dal gestore di I/O, deve impostare una routine IoCompletion per LRP prima di chiamare la funzione WSK solo se deve ricevere una notifica quando l'operazione eseguita dalla funzione WSK è stata completata. Se l'applicazione WSK non imposta una routine IoCompletion per L'IRP, quando l'IRP viene completata, il provider di risorse di configurazione verrà passato al driver di livello superiore o al gestore di I/O in base alla normale elaborazione del completamento di IRP. Se l'applicazione WSK imposta una routine IoCompletion per IRP, la routine IoCompletion può restituire STATUS_SUCCESS o STATUS_MORE_PROCESSING_REQUIRED. Se la routine IoCompletion restituisce STATUS_SUCCESS, l'elaborazione del completamento di IRP continuerà normalmente. Se la routine IoCompletion restituisce STATUS_MORE_PROCESSING_REQUIRED, l'applicazione WSK deve completare l'IRP chiamando IoCompleteRequest dopo aver completato l'elaborazione dei risultati dell'operazione eseguita dalla funzione WSK. Un'applicazione WSK non deve mai liberare un IRP passato da un driver di livello superiore o dal gestore di I/O.

Nota Se l'applicazione WSK imposta una routine IoCompletion per un IRP passato da un driver di livello superiore o dal gestore di I/O, la routine IoCompletion deve controllare il membro PendingReturned di IRP e chiamare la funzione IoMarkIrpPending se il membro PendingReturned è TRUE. Per altre informazioni, vedere Implementazione di una routine IoCompletion.

Nota Un'applicazione WSK non deve chiamare nuove funzioni WSK nel contesto della routine IoCompletion . In questo modo possono verificarsi chiamate ricorsive ed esaurire lo stack della modalità kernel. Durante l'esecuzione in IRQL = DISPATCH_LEVEL, ciò può causare anche la fame di altri thread.

Un'applicazione WSK non inizializza i runtime di integrazione passati alle funzioni WSK diverse dall'impostazione di una routine IoCompletion . Quando un'applicazione WSK passa un IRP a una funzione WSK, il sottosistema WSK configura la successiva posizione dello stack di I/O per conto dell'applicazione.

Nell'esempio di codice seguente viene illustrato come un'applicazione WSK può allocare e usare un IRP durante l'esecuzione di un'operazione di ricezione su un socket.

// Prototype for the receive IoCompletion routine
NTSTATUS
  ReceiveComplete(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PVOID Context
    );

// Function to receive data
NTSTATUS
  ReceiveData(
    PWSK_SOCKET Socket,
    PWSK_BUF DataBuffer
    )
{
  PWSK_PROVIDER_CONNECTION_DISPATCH Dispatch;
  PIRP Irp;
  NTSTATUS Status;

  // Get pointer to the 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,
    ReceiveComplete,
    DataBuffer,  // Use the data buffer for the context
    TRUE,
    TRUE,
    TRUE
    );

  // Initiate the receive operation on the socket
  Status =
    Dispatch->WskReceive(
      Socket,
      DataBuffer,
      0,  // No flags are specified
      Irp
      );

  // Return the status of the call to WskReceive()
  return Status;
}

// Receive IoCompletion routine
NTSTATUS
  ReceiveComplete(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PVOID Context
    )
{
  UNREFERENCED_PARAMETER(DeviceObject);

  PWSK_BUF DataBuffer;
  ULONG ByteCount;

  // Check the result of the receive operation
  if (Irp->IoStatus.Status == STATUS_SUCCESS)
  {
    // Get the pointer to the data buffer
    DataBuffer = (PWSK_BUF)Context;
 
    // Get the number of bytes received
    ByteCount = (ULONG)(Irp->IoStatus.Information);

    // Process the received data
    ...
  }

  // 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;
}

Il modello illustrato nell'esempio precedente, in cui l'applicazione WSK alloca un IRP e la libera nella routine di completamento, è il modello usato negli esempi nel resto della documentazione di WSK.

Nell'esempio di codice seguente viene illustrato come un'applicazione WSK può usare un IRP passato da un driver di livello superiore o dal gestore di I/O durante l'esecuzione di un'operazione di ricezione su un socket.

// Prototype for the receive IoCompletion routine
NTSTATUS
  ReceiveComplete(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PVOID Context
    );

// Function to receive data
NTSTATUS
  ReceiveData(
    PWSK_SOCKET Socket,
    PWSK_BUF DataBuffer,
    PIRP Irp;  // IRP from a higher level driver or the I/O manager
    )
{
  PWSK_PROVIDER_CONNECTION_DISPATCH Dispatch;
  NTSTATUS Status;

  // Get pointer to the provider dispatch structure
  Dispatch =
    (PWSK_PROVIDER_CONNECTION_DISPATCH)(Socket->Dispatch);

  // Set the completion routine for the IRP such that it is
  // only called if the receive operation succeeds.
  IoSetCompletionRoutine(
    Irp,
    ReceiveComplete,
    DataBuffer,  // Use the data buffer for the context
    TRUE,
    FALSE,
    FALSE
    );

  // Initiate the receive operation on the socket
  Status =
    Dispatch->WskReceive(
      Socket,
      DataBuffer,
      0,  // No flags are specified
      Irp
      );

  // Return the status of the call to WskReceive()
  return Status;
}

// Receive IoCompletion routine
NTSTATUS
  ReceiveComplete(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PVOID Context
    )
{
  UNREFERENCED_PARAMETER(DeviceObject);

  PWSK_BUF DataBuffer;
  ULONG ByteCount;

  // Since the completion routine was only specified to
  // be called if the operation succeeds, this should
  // always be true.
  ASSERT(Irp->IoStatus.Status == STATUS_SUCCESS);

  // Check the pending status of the IRP
  if (Irp->PendingReturned == TRUE)
  {
    // Mark the IRP as pending
    IoMarkIrpPending(Irp);
  }

  // Get the pointer to the data buffer
  DataBuffer = (PWSK_BUF)Context;
 
  // Get the number of bytes received
  ByteCount = (ULONG)(Irp->IoStatus.Information);

  // Process the received data
  ...

  // Return STATUS_SUCCESS to continue the
  // completion processing of the IRP.
  return STATUS_SUCCESS;
}

Per altre informazioni sull'uso dei runtime di integrazione, vedere Gestione dei runtime di integrazione.