Winsock カーネル関数での IRP の使用

Winsock カーネル (WSK) ネットワーク プログラミング インターフェイス (NPI) は、ネットワーク I/O 操作の非同期完了に IRP を使用します。 各 WSK 関数は、パラメーターとして IRP へのポインターを受け取ります。 WSK サブシステムは、WSK 関数によって実行される操作が完了した後、IRP を完了します。

WSK アプリケーションが WSK 関数に渡すために使用する IRP は、次のいずれかの方法で開始されます。

  • WSKアプリケーションは、IoAllocateIrp 関数を呼び出すことで、IRP を割り当てます。 この状況では、WSK アプリケーションは、少なくとも 1 つの I/O スタックの場所を持つ IRP を割り当てる必要があります。

  • WSK アプリケーションは、以前に割り当て、完了した IRP を再利用します この状況では、WSK は、IoReuseIrp 関数を呼び出し、IRP を再初期化する必要があります。

  • WSK アプリケーションは、上位レベルのドライバーまたは I/O マネージャーによって渡された IRP を使用します。 この場合、IRP には、WSK サブシステムが使用できる I/O スタックの場所を少なくとも 1 つ残しておく必要があります。

WSK アプリケーションが WSK 関数を呼び出すための IRP を取得した後、WSK サブシステムが IRP を完了したときに呼び出される IoCompletion ルーチンを設定できます。 WSK アプリケーションは、IoSetCompletionRoutine 関数を呼び出すことによって、IRP の IoCompletion ルーチンを設定します。 IRP がどのように生成されたかによって、IoCompletion ルーチンは、必要または任意となります。

  • WSK アプリケーションが IRP を割り当てた場合、または以前に割り当てた IRP を再利用する場合は、WSK 関数を呼び出す前に IRP の IoCompletion ルーチンを設定する必要があります。 このような場合、WSK アプリケーションは、IoSetCompletionRoutine 関数に渡される InvokeOnSuccessInvokeOnError、および InvokeOnCancel パラメーターに TRUE を指定して、IoCompletion ルーチンが常に呼び出されるようにする必要があります。 さらに、IRP に設定されている IoCompletion ルーチンは、IRP の完了処理を終了するために常に STATUS_MORE_PROCESSING_REQUIRED を返す必要があります。 IoCompletion ルーチンが呼び出された後、WSK アプリケーションが IRP を使用して行われる場合は、IoCompletion ルーチンから戻る前に、IRP を解放する IoFreeIrp 関数を呼び出す必要があります。 WSK アプリケーションが IRP を解放しない場合は、別の WSK 関数を呼び出すために、IRP を再利用できます。

  • 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 アプリケーションが WSK 関数に IRP を渡す場合、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;
}

IRPs の使用方法についての詳細は、 IRPsの処理をご覧ください。