文書番号: 320275 - 最終更新日: 2005年12月19日 - リビジョン: 5.1

IRP のさまざまな処理方法 (パート 1/2)

お知らせお使いのオペレーティング システムには適用しない情報が含まれている場合があります。

目次

すべて展開する | すべて折りたたむ

概要

はじめに

Windows Driver Model (WDM) ドライバで最もよく行われるタスクの 1 つは、あるドライバから別のドライバに I/O 要求パケット (IRP) を送信することです。ドライバは、IRP を独自に作成して下位のドライバに送信するか、別の上位のドライバから受け取った IRP を転送します。

この資料では、ドライバが下位のドライバに IRP を送信するためのあらゆる方法について、コメント付きのサンプル コードを使用して解説します。ドライバの作成者は古い IRP 処理規則に影響されずに、必要に応じて、この資料で説明するテンプレートのいずれかを使用することができます。

パート 1 では、ディスパッチ ルーチンで別のドライバに IRP を転送する 5 つのシナリオを示しています。残りの 7 つのシナリオ (パート 2 に記載しています) では、IRP を作成して別のドライバに送信する方法について説明します。パート 2 については、「サポート技術情報」 (Microsoft Knowledge Base) の次の資料を参照してください。
326315? (http://support.microsoft.com/kb/326315/ ) [INFO] IRP のさまざまな処理方法 (パート 2/2)
さまざまなシナリオを検証する前に、完了ルーチンによって返される STATUS について以下の事柄に注意してください。
IRP 完了ルーチンは STATUS_MORE_PROCESSING_REQUIRED または STATUS_SUCCESS のいずれかを返します。
I/O マネージャは状態を確認するとき、次の規則に従います。
  • 状態が STATUS_MORE_PROCESSING_REQUIRED の場合、IRP の完了を停止し、スタックの場所を変更せずに状態を返します。
  • 状態が STATUS_MORE_PROCESSING_REQUIRED 以外の場合、IRP の完了を続行します。
I/O マネージャは STATUS_MORE_PROCESSING_REQUIRED 以外のどの値が使用されているかを知る必要はないため、STATUS_SUCCESS (値 0 は多くのプロセッサ アーキテクチャで効率的に読み込まれます) を使用します。

コードの読みやすさを高めるために、Windows XP SP1 と Windows XP .NET Driver Development Kit Ntddk.h および Wdm.h ヘッダー ファイルには、以下のコードのように、STATUS_CONTINUE_COMPLETION と名前が指定される新しい #define が含まれます。このエイリアスは STATUS_SUCCESS です。
// 
// This value should be returned from completion routines to continue
// completing the IRP upwards. Otherwise, STATUS_MORE_PROCESSING_REQUIRED
// should be returned.
// 
#define STATUS_CONTINUE_COMPLETION      STATUS_SUCCESS
// 
// Completion routines can also use this enumeration instead of status codes.
// 
typedef enum _IO_COMPLETION_ROUTINE_RESULT {
    
    ContinueCompletion = STATUS_CONTINUE_COMPLETION,
    StopCompletion = STATUS_MORE_PROCESSING_REQUIRED

} IO_COMPLETION_ROUTINE_RESULT, *PIO_COMPLETION_ROUTINE_RESULT;
				

詳細

シナリオ 1 : 転送して終了する

ドライバが IRP を下方向に転送するだけで追加アクションを行わない場合は、以下のコードを使用します。この場合、ドライバは完了ルーチンを設定する必要はありません。ドライバが最上位のドライバの場合、下位のドライバによって返される状態に応じて、IRP は同期的または非同期的に完了することができます。
NTSTATUS
DispatchRoutine_1(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    // 
    // You are not setting a completion routine, so just skip the stack
    // location because it provides better performance.
    // 
    IoSkipCurrentIrpStackLocation (Irp);
    return IoCallDriver(TopOfDeviceStack, Irp);
} 

シナリオ 2 : 転送して IRP が完了するまで同期的に待つ

ドライバが下位のドライバに IRP を転送し、IRP を処理するために状態が返されるまで待つ場合は、以下のコードを使用します。これは PNP IRP の処理でよく行われます。たとえば、IRP_MN_START_DEVICE IRP を受け取る場合、IRP をバス ドライバに転送し、IRP が完了するまで待ってからデバイスを起動する必要があります。Windows XP システムには IoForwardIrpSynchronously という名前の新しい関数があり、この操作を簡単に行うことができます。
NTSTATUS
DispatchRoutine_2(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    KEVENT   event;
    NTSTATUS status;

    KeInitializeEvent(&event, NotificationEvent, FALSE);

    // 
    // You are setting completion routine, so you must copy
    // current stack location to the next. You cannot skip a location
    // here.
    // 
    IoCopyCurrentIrpStackLocationToNext(Irp);

    IoSetCompletionRoutine(Irp,
                           CompletionRoutine_2,
                           &event,
                           TRUE,
                           TRUE,
                           TRUE
                           );

    status = IoCallDriver(TopOfDeviceStack, Irp);

    if (status == STATUS_PENDING) {
        
       KeWaitForSingleObject(&event,
                             Executive, // WaitReason
                             KernelMode, // must be Kernelmode to prevent the stack getting paged out
                             FALSE,
                             NULL // indefinite wait
                             );
       status = Irp->IoStatus.Status;
    }
    
    // <---- Do your own work here.


    // 
    // Because you stopped the completion of the IRP in the CompletionRoutine
    // by returning STATUS_MORE_PROCESSING_REQUIRED, you must call
    // IoCompleteRequest here.
    // 
    IoCompleteRequest (Irp, IO_NO_INCREMENT);
    return status;

}
NTSTATUS
CompletionRoutine_2(
    IN PDEVICE_OBJECT   DeviceObject,
    IN PIRP             Irp,
    IN PVOID            Context
    )
{ 
  if (Irp->PendingReturned == TRUE) {
    // 
    // You will set the event only if the lower driver has returned
    // STATUS_PENDING earlier. This optimization removes the need to
    // call KeSetEvent unnecessarily and improves performance because the
    // system does not have to acquire an internal lock.  
    // 
    KeSetEvent ((PKEVENT) Context, IO_NO_INCREMENT, FALSE);
  }
  // This is the only status you can return. 
  return STATUS_MORE_PROCESSING_REQUIRED;  
} 
				

シナリオ 3 : 完了ルーチンを設定して転送するが、IRP が完了するまで待たない

このシナリオでは、ドライバは完了ルーチンを設定して IRP を転送し、下位のドライバの現在の状態をそのまま返します。完了ルーチンを設定する目的は、戻る途中で IRP の内容を変更するためです。
NTSTATUS
DispathRoutine_3(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    NTSTATUS status;

    // 
    // Because you are setting completion routine, you must copy the
    // current stack location to the next. You cannot skip a location
    // here.
    // 
    IoCopyCurrentIrpStackLocationToNext(Irp); 

    IoSetCompletionRoutine(Irp,
                           CompletionRoutine_31,// or CompletionRoutine_32
                           NULL,
                           TRUE,
                           TRUE,
                           TRUE
                           );
    
    return IoCallDriver(TopOfDeviceStack, Irp);

} 
ディスパッチ ルーチンで下位のドライバの状態を返す場合、以下の点に留意します。
  • 完了ルーチン内の IRP の状態を変更しないようにします。これは、IRP の IoStatus ブロック (Irp->IoStatus.Status) に設定された状態値が、下位のドライバのリターン状態と同じであることを確認するためです。
  • Irp->PendingReturned で示される IRP の待ち状態を伝達する必要があります。
  • IRP の同期を変更しないようにします。
結果的に、このシナリオで有効な完了ルーチンのバージョンは 2 つだけです (31 と 32)。
 
NTSTATUS
CompletionRoutine_31 (
    IN PDEVICE_OBJECT   DeviceObject,
    IN PIRP             Irp,
    IN PVOID            Context
    )
{   

    // 
    // Because the dispatch routine is returning the status of lower driver
    // as is, you must do the following:
    // 
    if (Irp->PendingReturned) {
        
        IoMarkIrpPending( Irp );
    }
    
    return STATUS_CONTINUE_COMPLETION ; // Make sure of same synchronicity 
}

NTSTATUS
CompletionRoutine_32 (
    IN PDEVICE_OBJECT   DeviceObject,
    IN PIRP             Irp,
    IN PVOID            Context
    )
{   
    // 
    // Because the dispatch routine is returning the status of lower driver
    // as is, you must do the following:
    // 
    if (Irp->PendingReturned) {
        
        IoMarkIrpPending( Irp );
    }
    
    //    
    // To make sure of the same synchronicity, complete the IRP here.
    // You cannot complete the IRP later in another thread because the 
    // the dispatch routine is returning the status returned by the lower
    // driver as is.
    // 
    IoCompleteRequest( Irp,  IO_NO_INCREMENT);  

    // 
    // Although this is an unusual completion routine that you rarely see,
    // it is discussed here to address all possible ways to handle IRPs.  
    // 
    return STATUS_MORE_PROCESSING_REQUIRED; 
} 

				

シナリオ 4 : キューに入れておいて後で処理する、または転送して完了前に IRP を再使用する

ドライバが IRP をキューに入れておいて後で処理する場合、または IRP を下位のドライバに転送して IRP の完了前に指定の回数だけ再使用する場合は、以下のコードを使用します。ディスパッチ ルーチンは IRP を保留中としてマークし、IRP が後で別のスレッドによって完了されるため STATUS_PENDING を返します。ここで、完了ルーチンは必要に応じて、IRP の状態を変更できます (前のシナリオとは異なります)。
NTSTATUS
DispathRoutine_4(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    NTSTATUS status;

    // 
    // You mark the IRP pending if you are intending to queue the IRP
    // and process it later. If you are intending to forward the IRP 
    // directly, use one of the methods discussed earlier in this article.
    // 
    IoMarkIrpPending( Irp );    

    // 
    // For demonstration purposes: this IRP is forwarded to the lower driver.
    // 
    IoCopyCurrentIrpStackLocationToNext(Irp); 

    IoSetCompletionRoutine(Irp,
                           CompletionRoutine_41, // or CompletionRoutine_42
                           NULL,
                           TRUE,
                           TRUE,
                           TRUE
                           ); 
    IoCallDriver(TopOfDeviceStack, Irp);

    // 
    // Because you marked the IRP pending, you must return pending,
    // regardless of the status of returned by IoCallDriver.
    // 
    return STATUS_PENDING ;

}
完了ルーチンは STATUS_CONTINUE_COMPLETION または STATUS_MORE_PROCESSING_REQUIRED のいずれかを返します。別のスレッドからの IRP を再使用して後で完了する場合のみ、STATUS_MORE_PROCESSING_REQUIRED を返します。
NTSTATUS
CompletionRoutine_41(
    IN PDEVICE_OBJECT   DeviceObject,
    IN PIRP             Irp,
    IN PVOID            Context
    )
{ 
    // 
    // By returning STATUS_CONTINUE_COMPLETION , you are relinquishing the 
    // ownership of the IRP. You cannot touch the IRP after this.
    // 
    return STATUS_CONTINUE_COMPLETION ; 
} 


NTSTATUS
CompletionRoutine_42 (
    IN PDEVICE_OBJECT   DeviceObject,
    IN PIRP             Irp,
    IN PVOID            Context
    )
{  
    // 
    // Because you are stopping the completion of the IRP by returning the
    // following status, you must complete the IRP later.
    // 
    return STATUS_MORE_PROCESSING_REQUIRED ; 
} 
				

シナリオ 5 : ディスパッチ ルーチンで IRP を完了する

このシナリオでは、ディスパッチ ルーチンで IRP を完了する方法を示します。

重要 : ディスパッチ ルーチンで IRP を完了する場合、ディスパッチ ルーチンのリターン状態は IRP の IoStatus ブロック (Irp->IoStatus.Status) に設定された状態値と一致する必要があります。
NTSTATUS
DispatchRoutine_5(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    // 
    // <-- Process the IRP here.
    // 
    Irp->IoStatus.Status = STATUS_XXX;
    Irp->IoStatus.Information = YYY;
    IoCompletRequest(Irp, IO_NO_INCREMENT);
    return STATUS_XXX;
} 

関連情報

パート 2 については、「サポート技術情報」 (Microsoft Knowledge Base) の次の資料を参照してください。
326315? (http://support.microsoft.com/kb/326315/ ) [INFO] IRP のさまざまな処理方法 (パート 2/2)
  • Walter Oney 著『Programming Windows Driver Model』 Second Edition の Chapter 5

関連情報

なお、この資料は英語版の翻訳であり、日本語環境での確認は行っておりません。

この資料は以下の製品について記述したものです。
  • Microsoft Win32 Device Driver Kit for Windows 2000
  • Microsoft Windows XP Driver Development Kit (DDK)
  • Microsoft Windows Server 2003 Driver Development Kit (DDK)
  • Microsoft Windows NT 4.0
  • Microsoft Windows 2000 Standard Edition
  • Microsoft Windows XP Professional
キーワード:?
kbinfo kbkmode kbwdm KB320275
"Microsoft Knowledge Baseに含まれている情報は、いかなる保証もない現状ベースで提供されるものです。Microsoft Corporation及びその関連会社は、市場性および特定の目的への適合性を含めて、明示的にも黙示的にも、一切の保証をいたしません。さらに、Microsoft Corporation及びその関連会社は、本文書に含まれている情報の使用及び使用結果につき、正確性、真実性等、いかなる表明・保証も行ないません。Microsoft Corporation、その関連会社及びこれらの権限ある代理人による口頭または書面による一切の情報提供またはアドバイスは、保証を意味するものではなく、かつ上記免責条項の範囲を狭めるものではありません。Microsoft Corporation、その関連会社 及びこれらの者の供給者は、直接的、間接的、偶発的、結果的損害、逸失利益、懲罰的損害、または特別損害を含む全ての損害に対して、状況のいかんを問わず一切責任を負いません。(Microsoft Corporation、その関連会社 またはこれらの者の供給者がかかる損害の発生可能性を了知している場合を含みます。) 結果的損害または偶発的損害に対する責任の免除または制限を認めていない地域においては、上記制限が適用されない場合があります。なお、本文書においては、文書の体裁上の都合により製品名の表記において商標登録表示、その他の商標表示を省略している場合がありますので、予めご了解ください。"