Verwenden von IRPs mit Winsock-Kernelfunktionen

Die Netzwerkprogrammierschnittstelle (NPI) des Winsock-Kernels (WSK) verwendet IRPs zum asynchronen Abschluss von Netzwerk-E/A-Vorgängen. Jede WSK-Funktion verwendet einen Zeiger auf einen IRP als Parameter. Das WSK-Subsystem schließt das IRP ab, nachdem der von der WSK-Funktion ausgeführte Vorgang abgeschlossen ist.

Ein IRP, das von einer WSK-Anwendung zum Übergeben an eine WSK-Funktion verwendet wird, kann auf eine der folgenden Arten stammen.

  • Die WSK-Anwendung ordnet das IRP zu, indem die IoAllocateIrp-Funktion aufgerufen wird . In diesem Fall muss die WSK-Anwendung den IRP mit mindestens einem E/A-Stapelspeicherort zuordnen.

  • Die WSK-Anwendung verwendet einen abgeschlossenen IRP wieder, den sie zuvor zugewiesen hat. In diesem Fall muss der WSK die IoReuseIrp-Funktion aufrufen, um das IRP erneut zu initialisieren.

  • Die WSK-Anwendung verwendet einen IRP, der entweder von einem Treiber auf höherer Ebene oder vom E/A-Manager an sie übergeben wurde. In diesem Fall muss das IRP über mindestens einen verbleibenden E/A-Stapelspeicherort verfügen, der vom WSK-Subsystem verwendet werden kann.

Nachdem eine WSK-Anwendung über ein IRP verfügt, das zum Aufrufen einer WSK-Funktion verwendet werden soll, kann sie eine IoCompletion-Routine für die IRP festlegen, die aufgerufen wird, wenn das IRP vom WSK-Subsystem abgeschlossen wird. Eine WSK-Anwendung legt eine IoCompletion-Routine für ein IRP fest, indem die IoSetCompletionRoutine-Funktion aufgerufen wird. Je nachdem, wie das IRP entstanden ist, ist eine IoCompletion-Routine entweder erforderlich oder optional.

  • Wenn die WSK-Anwendung den IRP zugewiesen hat oder eine zuvor zugewiesene IRP wiederverwendet, muss sie vor dem Aufrufen einer WSK-Funktion eine IoCompletion-Routine für das IRP festlegen. In diesem Fall muss die WSK-Anwendung TRUE für die Parameter InvokeOnSuccess, InvokeOnError und InvokeOnCancel angeben, die an die IoSetCompletionRoutine-Funktion übergeben werden, um sicherzustellen, dass die IoCompletion-Routine immer aufgerufen wird. Darüber hinaus muss die für das IRP festgelegte IoCompletion-Routine immer STATUS_MORE_PROCESSING_REQUIRED zurückgeben, um die Abschlussverarbeitung des IRP zu beenden. Wenn die WSK-Anwendung das IRP verwendet, nachdem die IoCompletion-Routine aufgerufen wurde, sollte sie die IoFreeIrp-Funktion aufrufen, um die IRP freizugeben, bevor sie von der IoCompletion-Routine zurückkehrt. Wenn die WSK-Anwendung den IRP nicht freigibt, kann sie das IRP für einen Aufruf einer anderen WSK-Funktion wiederverwenden.

  • Wenn die WSK-Anwendung einen IRP verwendet, der von einem Treiber auf höherer Ebene oder vom E/A-Manager an sie übergeben wurde, sollte sie vor dem Aufrufen der WSK-Funktion nur dann eine IoCompletion-Routine für das IRP festlegen, wenn sie benachrichtigt werden muss, wenn der von der WSK-Funktion ausgeführte Vorgang abgeschlossen ist. Wenn die WSK-Anwendung keine IoCompletion-Routine für das IRP festsetzt, wird die IRP nach Abschluss des IRP wieder an den Treiber der höheren Ebene oder an den E/A-Manager gemäß der normalen IRP-Abschlussverarbeitung übergeben. Wenn die WSK-Anwendung eine IoCompletion-Routine für das IRP festlegt, kann die IoCompletion-Routine entweder STATUS_SUCCESS oder STATUS_MORE_PROCESSING_REQUIRED zurückgeben. Wenn die IoCompletion-Routine STATUS_SUCCESS zurückgibt, wird die IRP-Abschlussverarbeitung normal fortgesetzt. Wenn die IoCompletion-Routine STATUS_MORE_PROCESSING_REQUIRED zurückgibt, muss die WSK-Anwendung das IRP abschließen, indem sie IoCompleteRequest aufruft , nachdem sie die Verarbeitung der Ergebnisse des Vorgangs abgeschlossen hat, der von der WSK-Funktion ausgeführt wurde. Eine WSK-Anwendung sollte niemals einen IRP freigeben, der von einem Treiber höherer Ebene oder vom E/A-Manager an sie übergeben wurde.

Hinweis Wenn die WSK-Anwendung eine IoCompletion-Routine für ein IRP festlegt, das von einem Treiber auf höherer Ebene oder vom E/A-Manager an sie übergeben wurde, muss die IoCompletion-Routine den PendingReturned-Member des IRP überprüfen und die IoMarkIrpPending-Funktion aufrufen, wenn der PendingReturned-MemberTRUE ist. Weitere Informationen finden Sie unter Implementieren einer IoCompletion-Routine.

Hinweis Eine WSK-Anwendung sollte keine neuen WSK-Funktionen im Kontext der IoCompletion-Routine aufrufen. Dies kann zu rekursiven Aufrufen führen und den Kernelmodusstapel erschöpfen. Bei der Ausführung unter IRQL = DISPATCH_LEVEL kann dies auch zum Verhungern anderer Threads führen.

Eine WSK-Anwendung initialisiert nicht die IRPs, die sie an die WSK-Funktionen übergibt, außer dem Festlegen einer IoCompletion-Routine . Wenn eine WSK-Anwendung ein IRP an eine WSK-Funktion übergibt, richtet das WSK-Subsystem den nächsten E/A-Stapelspeicherort im Namen der Anwendung ein.

Das folgende Codebeispiel zeigt, wie eine WSK-Anwendung ein IRP zuordnen und verwenden kann, wenn ein Empfangsvorgang für einen Socket ausgeführt wird.

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

Das im vorherigen Beispiel gezeigte Modell, bei dem die WSK-Anwendung einen IRP zuordnet und ihn dann in der Vervollständigungsroutine freigibt, ist das Modell, das in den Beispielen im rest der WSK-Dokumentation verwendet wird.

Das folgende Codebeispiel zeigt, wie eine WSK-Anwendung ein IRP verwenden kann, das von einem Treiber auf höherer Ebene oder vom E/A-Manager an sie übergeben wurde, wenn ein Empfangsvorgang für einen Socket ausgeführt wird.

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

Weitere Informationen zur Verwendung von IRPs finden Sie unter Behandeln von IRPs.