Использование IRP с функциями ядра Winsock

Сетевой программный интерфейс (NPI) Winsock Kernel (WSK) использует irP для асинхронного завершения операций сетевого ввода-вывода. Каждая функция WSK принимает указатель на IRP в качестве параметра. Подсистема WSK завершает IRP после завершения операции, выполняемой функцией WSK.

IRP, который приложение WSK использует для передачи в функцию WSK, может возникать одним из следующих способов.

  • Приложение WSK выделяет IRP путем вызова функции IoAllocateIrp . В этом случае приложение WSK должно выделить IRP по крайней мере с одним расположением стека ввода-вывода.

  • Приложение WSK повторно использует завершенный IRP, выделенный ранее. В этом случае WSK должен вызвать функцию IoReuseIrp для повторной инициализации IRP.

  • Приложение WSK использует IRP, переданное ему драйвером более высокого уровня или диспетчером ввода-вывода. В этом случае IRP должно иметь по крайней мере одно оставшееся расположение стека ввода-вывода, доступное для использования подсистемой WSK.

После того как приложение WSK имеет IRP для вызова функции WSK, оно может задать подпрограмму IoCompletion для IRP, которая будет вызываться при завершении IRP подсистемой WSK. Приложение WSK задает подпрограмму IoCompletion для IRP путем вызова функции IoSetCompletionRoutine . В зависимости от того, как возникла функция IRP, подпрограмма IoCompletion является обязательной или необязательной.

  • Если приложение WSK выделило IRP или повторно использует IRP, которое было выделено ранее, оно должно задать подпрограмму IoCompletion для IRP перед вызовом функции WSK. В этом случае приложение WSK должно указать значение TRUE для параметров InvokeOnSuccess, InvokeOnError и InvokeOnCancel , передаваемых в функцию IoSetCompletionRoutine , чтобы гарантировать, что подпрограмма IoCompletion всегда вызывается. Кроме того, подпрограмма IoCompletion , заданная для IRP, всегда должна возвращать STATUS_MORE_PROCESSING_REQUIRED для завершения обработки IRP. Если приложение WSK выполняется с помощью IRP после вызова подпрограммы IoCompletion , оно должно вызвать функцию IoFreeIrp , чтобы освободить IRP перед возвратом из подпрограммы IoCompletion . Если приложение WSK не освобождает IRP, оно может повторно использовать IRP для вызова другой функции WSK.

  • Если приложение WSK использует IRP, переданное ему драйвером более высокого уровня или диспетчером ввода-вывода, оно должно задать подпрограмму IoCompletion для IRP перед вызовом функции WSK, только если она должна получать уведомления о завершении операции, выполняемой функцией WSK. Если приложение WSK не задает подпрограмму IoCompletion для IRP, то после завершения IRP IRP будет передана обратно в драйвер более высокого уровня или в диспетчер ввода-вывода в рамках обычной обработки завершения IRP. Если приложение WSK задает подпрограмму IoCompletion для IRP, процедура IoCompletion может возвращать STATUS_SUCCESS или STATUS_MORE_PROCESSING_REQUIRED. Если подпрограмма IoCompletion возвращает STATUS_SUCCESS, обработка завершения IRP будет продолжаться в обычном режиме. Если подпрограмма IoCompletion возвращает STATUS_MORE_PROCESSING_REQUIRED, приложение WSK должно завершить IRP, вызвав IoCompleteRequest после завершения обработки результатов операции, выполненной функцией WSK. Приложение WSK никогда не должно освобождать IRP, переданное ему драйвером более высокого уровня или диспетчером ввода-вывода.

Примечание Если приложение WSK задает подпрограмму IoCompletion для IRP, переданного ему драйвером более высокого уровня или диспетчером ввода-вывода, то подпрограмма IoCompletion должна проверка элемент PendingReturned IRP и вызвать функцию IoMarkIrpPending, если член PendingReturned имеет значение TRUE. Дополнительные сведения см. в разделе Реализация процедуры IoCompletion.

Примечание Приложение WSK не должно вызывать новые функции WSK в контексте подпрограммы IoCompletion . Это может привести к рекурсивным вызовам и исчерпанию стека режима ядра. При выполнении в IRQL = DISPATCH_LEVEL это также может привести к нехватке других потоков.

Приложение WSK не инициализирует irp, которые оно передает функциям WSK, кроме задания подпрограммы IoCompletion . Когда приложение WSK передает IRP функции WSK, подсистема WSK настраивает следующее расположение стека ввода-вывода от имени приложения.

В следующем примере кода показано, как приложение WSK может выделять и использовать IRP при выполнении операции получения в сокете.

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

Модель, показанная в предыдущем примере, где приложение WSK выделяет IRP, а затем освобождает его в процедуре завершения, является моделью, которая используется в примерах в оставшейся части документации по WSK.

В следующем примере кода показано, как приложение WSK может использовать IRP, переданный ему драйвером более высокого уровня или диспетчером ввода-вывода, при выполнении операции получения в сокете.

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

Дополнительные сведения об использовании IRP см. в разделе Обработка IRP.