Uso de IRP con funciones de kernel de Winsock

La interfaz de programación de red (NPI) de Winsock Kernel (WSK) usa IRP para completar de forma asincrónica las operaciones de E/S de red. Cada función WSK toma un puntero a un IRP como parámetro. El subsistema WSK completa el IRP una vez completada la operación realizada por la función WSK.

Un IRP que una aplicación WSK usa para pasar a una función de WSK puede originarse de una de las maneras siguientes.

  • La aplicación WSK asigna el IRP mediante una llamada a la función IoAllocateIrp . En esta situación, la aplicación WSK debe asignar el IRP con al menos una ubicación de pila de E/S.

  • La aplicación WSK reutiliza un IRP completado que asignó anteriormente. En esta situación, el WSK debe llamar a la función IoReuseIrp para reinicializar el IRP.

  • La aplicación WSK usa un IRP que se le pasó por un controlador de nivel superior o por el administrador de E/S. En esta situación, el IRP debe tener al menos una ubicación de pila de E/S restante disponible para su uso por parte del subsistema WSK.

Una vez que una aplicación WSK tiene un IRP que se va a usar para llamar a una función de WSK, puede establecer una rutina de IoCompletion para que se llame al IRP cuando el subsistema WSK complete el IRP. Una aplicación WSK establece una rutina de IoCompletion para un IRP mediante una llamada a la función IoSetCompletionRoutine . Dependiendo de cómo se originó el IRP, una rutina de IoCompletion es necesaria o opcional.

  • Si la aplicación WSK asignó el IRP o está reutilizando un IRP que asignó anteriormente, debe establecer una rutina de IoCompletion para irP antes de llamar a una función WSK. En esta situación, la aplicación WSK debe especificar TRUE para los parámetros InvokeOnSuccess, InvokeOnError e InvokeOnCancel que se pasan a la función IoSetCompletionRoutine para asegurarse de que siempre se llama a la rutina IoCompletion . Además, la rutina IoCompletion que se establece para irP siempre debe devolver STATUS_MORE_PROCESSING_REQUIRED para finalizar el procesamiento de finalización del IRP. Si la aplicación WSK se realiza mediante irP después de llamar a la rutina IoCompletion , debe llamar a la función IoFreeIrp para liberar el IRP antes de volver de la rutina ioCompletion . Si la aplicación WSK no libera el IRP, puede reutilizar el IRP para una llamada a otra función WSK.

  • Si la aplicación WSK usa un IRP que se le pasó por un controlador de nivel superior o por el administrador de E/S, debe establecer una rutina de IoCompletion para el IRP antes de llamar a la función WSK solo si se debe notificar cuando se haya completado la operación realizada por la función WSK. Si la aplicación WSK no establece una rutina de IoCompletion para irP, cuando se complete el IRP, el IRP se pasará de nuevo al controlador de nivel superior o al administrador de E/S según el procesamiento normal de finalización de IRP. Si la aplicación WSK establece una rutina de IoCompletion para irP, la rutina IoCompletion puede devolver STATUS_SUCCESS o STATUS_MORE_PROCESSING_REQUIRED. Si la rutina IoCompletion devuelve STATUS_SUCCESS, el procesamiento de finalización de IRP continuará normalmente. Si la rutina IoCompletion devuelve STATUS_MORE_PROCESSING_REQUIRED, la aplicación WSK debe completar el IRP llamando a IoCompleteRequest una vez que haya terminado de procesar los resultados de la operación que realizó la función WSK. Una aplicación WSK nunca debe liberar un IRP que se le haya pasado por un controlador de nivel superior o por el administrador de E/S.

Nota Si la aplicación WSK establece una rutina de IoCompletion para un IRP que se le pasó por un controlador de nivel superior o por el administrador de E/S, la rutina IoCompletion debe comprobar el miembro PendingReturned del IRP y llamar a la función IoMarkIrpPending si el miembro PendingReturned es TRUE. Para obtener más información, consulte Implementación de una rutina de IoCompletion.

Nota Una aplicación WSK no debe llamar a nuevas funciones de WSK en el contexto de la rutina IoCompletion . Si lo hace, puede provocar llamadas recursivas y agotar la pila del modo kernel. Al ejecutar en IRQL = DISPATCH_LEVEL, esto también puede provocar la inanición de otros subprocesos.

Una aplicación WSK no inicializa los IRP que pasa a las funciones de WSK que no sean establecer una rutina de IoCompletion . Cuando una aplicación WSK pasa un IRP a una función WSK, el subsistema WSK configura la siguiente ubicación de pila de E/S en nombre de la aplicación.

En el ejemplo de código siguiente se muestra cómo una aplicación WSK puede asignar y usar un IRP al realizar una operación de recepción en 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;
}

El modelo que se muestra en el ejemplo anterior, donde la aplicación WSK asigna un IRP y, a continuación, lo libera en la rutina de finalización, es el modelo que se usa en los ejemplos durante el resto de la documentación de WSK.

En el ejemplo de código siguiente se muestra cómo una aplicación WSK puede usar un IRP que se le ha pasado por un controlador de nivel superior o por el administrador de E/S al realizar una operación de recepción en 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;
}

Para obtener más información sobre el uso de IRP, consulte Control de IRP.