Winsock 커널 함수와 함께 IRP 사용

WSK(Winsock 커널) NPI(네트워크 프로그래밍 인터페이스) 는 네트워크 I/O 작업의 비동기 완료를 위해 IRP를 사용합니다. 각 WSK 함수는 IRP에 대한 포인터를 매개 변수로 사용합니다. WSK 하위 시스템은 WSK 함수에서 수행하는 작업이 완료된 후 IRP를 완료합니다.

WSK 애플리케이션이 WSK 함수에 전달하는 데 사용하는 IRP는 다음 방법 중 하나로 시작됩니다.

  • WSK 애플리케이션은 IoAllocateIrp 함수를 호출하여 IRP를 할당합니다. 이 경우 WSK 애플리케이션은 하나 이상의 I/O 스택 위치를 사용하여 IRP를 할당해야 합니다.

  • WSK 애플리케이션은 이전에 할당한 완료된 IRP를 다시 사용합니다. 이 경우 WSK는 IRP를 다시 초기화하기 위해 IoReuseIrp 함수를 호출해야 합니다.

  • WSK 애플리케이션은 더 높은 수준의 드라이버 또는 I/O 관리자에 의해 전달된 IRP를 사용합니다. 이 경우 IRP에는 WSK 하위 시스템에 사용할 수 있는 하나 이상의 남은 I/O 스택 위치가 있어야 합니다.

WSK 애플리케이션에 WSK 함수를 호출하는 데 사용할 IRP가 있으면 WSK 하위 시스템에 의해 IRP가 완료될 때 IRP가 호출되도록 IoCompletion 루틴을 설정할 수 있습니다. WSK 애플리케이션은 IoSetCompletionRoutine 함수를 호출하여 IRP에 대한 IoCompletion 루틴을 설정합니다. IRP가 시작된 방식에 따라 IoCompletion 루틴이 필요하거나 선택 사항입니다.

  • WSK 애플리케이션이 IRP를 할당했거나 이전에 할당한 IRP를 다시 사용하는 경우 WSK 함수를 호출하기 전에 IRP에 대한 IoCompletion 루틴을 설정해야 합니다. 이 경우 WSK 애플리케이션은 IoCompletion 루틴이 항상 호출되도록 IoSetCompletionRoutine 함수에 전달되는 InvokeOnSuccess, InvokeOnErrorInvokeOnCancel 매개 변수에 대해 TRUE를 지정해야 합니다. 또한 IRP에 대해 설정된 IoCompletion 루틴은 항상 STATUS_MORE_PROCESSING_REQUIRED 반환하여 IRP의 완료 처리를 종료해야 합니다. IoCompletion 루틴이 호출된 후 WSK 애플리케이션이 IRP를 사용하여 수행되는 경우 IoCompletion 루틴에서 반환하기 전에 IRP를 해제하기 위해 IoFreeIrp 함수를 호출해야 합니다. WSK 애플리케이션이 IRP를 해제하지 않으면 IRP를 다른 WSK 함수 호출에 다시 사용할 수 있습니다.

  • WSK 애플리케이션이 더 높은 수준의 드라이버 또는 I/O 관리자에 의해 전달된 IRP를 사용하는 경우 WSK 함수에서 수행한 작업이 완료되었을 때 알림을 받아야 하는 경우에만 WSK 함수를 호출하기 전에 IRP에 대한 IoCompletion 루틴을 설정해야 합니다. WSK 애플리케이션이 IRP에 대한 IoCompletion 루틴을 설정하지 않은 경우 IRP가 완료되면 IRP가 일반 IRP 완료 처리에 따라 상위 수준 드라이버 또는 I/O 관리자에게 다시 전달됩니다. WSK 애플리케이션이 IRP에 대한 IoCompletion 루틴을 설정하는 경우 IoCompletion 루틴은 STATUS_SUCCESS 반환하거나 STATUS_MORE_PROCESSING_REQUIRED 수 있습니다. IoCompletion 루틴이 STATUS_SUCCESS 반환하는 경우 IRP 완료 처리는 정상적으로 계속됩니다. IoCompletion 루틴이 STATUS_MORE_PROCESSING_REQUIRED 반환하는 경우 WSK 애플리케이션은 WSK 함수에서 수행한 작업의 결과 처리를 완료한 후 IoCompleteRequest를 호출하여 IRP를 완료해야 합니다. WSK 애플리케이션은 더 높은 수준의 드라이버 또는 I/O 관리자에 의해 전달된 IRP를 해제해서는 안 됩니다.

참고 WSK 애플리케이션이 상위 수준 드라이버 또는 I/O 관리자에 의해 전달된 IRP에 대한 IoCompletion 루틴을 설정하는 경우 IoCompletion 루틴은 IRP의 PendingReturned 멤버를 검사 PendingReturned 멤버가 TRUE인 경우 IoMarkIrpPending 함수 호출해야 합니다. 자세한 내용은 IoCompletion 루틴 구현을 참조하세요.

참고 WSK 애플리케이션은 IoCompletion 루틴의 컨텍스트에서 새 WSK 함수를 호출해서는 안 됩니다. 이렇게 하면 재귀 호출이 발생하고 커널 모드 스택이 소진될 수 있습니다. IRQL = DISPATCH_LEVEL 실행할 때 다른 스레드가 부족해질 수도 있습니다.

WSK 애플리케이션은 IoCompletion 루틴을 설정하는 것 외에 WSK 함수에 전달하는 IRP를 초기화하지 않습니다. WSK 애플리케이션이 IRP를 WSK 함수에 전달하면 WSK 하위 시스템은 애플리케이션을 대신하여 다음 I/O 스택 위치를 설정합니다.

다음 코드 예제에서는 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 애플리케이션이 소켓에서 수신 작업을 수행할 때 상위 수준 드라이버 또는 I/O 관리자에 의해 전달된 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 처리를 참조하세요.