Поделиться через


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

Ядро Winsock (WSK) , интерфейс сетевого программирования (NPI), использует 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 при его завершении подсистемой 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. Если приложение 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;
}

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