SCIENCE PARK

デバドラ講座

【 デバドラ講座28:「WindowsNTデバイスドライバプログラミングCD」CH9 】

Driverディレクトリ dispatch.cの解析

オープンクローズルーチン

NTSTATUS
XxDispatchOpenClose(
     IN PDEVICE_OBJECT pDO,
     IN PIRP Irp)
{    Irp->IoStatus.Status = STATUS_SUCCESS; //IRPの完了状態は成功
     Irp->IoStatus.Information = 0; //読み書きされたバイト長は0
     IoCompleteRequest( Irp, IO_NO_INCREMENT ); //IRPの完了を伝える
     return STATUS_SUCCESS;
}

書き込みルーチン

NTSTATUS
XxDispatchWrite(
     IN PDEVICE_OBJECT pDO,
     IN PIRP Irp)
{ PIO_STACK_LOCATION
IrpStack = IoGetCurrentIrpStackLocation( Irp ); //現在のスタックロケーションを取得
if( IrpStack->Parameters.Write.Length == 0 ) { //データ転送長が0の場合この処理を行う
     Irp->IoStatus.Status = STATUS_SUCCESS; //IRPの完了状態は成功
     Irp->IoStatus.Information = 0; //読み書きされたバイト長は0
     IoCompleteRequest( Irp, IO_NO_INCREMENT ); //IRPの完了を伝える
     return STATUS_SUCCESS;
}
IoMarkIrpPending( Irp ); //0でない時、IRPをペンディングとして印をつける
IoStartPacket( pDO, Irp, 0, NULL ); //StartI/OのキューにIRPを挿入
//StartI/Oルーチンが起動していない場合、起動させる
return STATUS_PENDING;
}

Testディレクトリ xxtest.cの解析

VOID __cdecl main (VOID)
{    HANDLE hDevice; //CreateFileによって取得したファイルのハンドル
     DWORD dwBytesWritten; //デバイスに書き込まれたデータのバイト数
     LPCTSTR szBuffer = "The owls are not what they seem.\n\0";
     DWORD dwErrorCode; //エラーコード値
     
     hDevice = CreateFile( //CreateFile関数を実行する
                   "\\\\.\\XX1", //ファイル名のポインタ
                   GENERIC_WRITE, //デバイスにデータを書き込むことができる
                   0, //オブジェクトは共有しない
                   NULL, //取得したハンドルは子プロセスに継承できない
                   OPEN_EXISTING, //指定したファイルを開く。
                   //ファイルが存在しない場合、関数は失敗する。
                   FILE_ATTRIBUTE_NORMAL, //ファイルの属性は設定しない
                   NULL );
     if( hDevice == INVALID_HANDLE_VALUE ) //CreateFileの実行に失敗したとき
     {
     dwErrorCode = GetLastError(); //最新のエラーコードの取得
     ErrorMessage( "CreateFile", dwErrorCode ); //エラーメッセージの表示
     ExitProcess( dwErrorCode ); //プロセスを終了
     }
     if( !WriteFile(
                   hDevice, //書き込みを行うファイルハンドル
                   szBuffer, //データバッファのポインタ
                   strlen( szBuffer ), //データのバイト数
                   &dwBytesWritten, //実際に書き込まれたデータのバイト数の変数アドレス
                   NULL ) )                            //オーバーラップI/O用の構造体ポインタ
     { //WriteFileの実行に失敗したとき
     dwErrorCode = GetLastError(); //最新のエラーコードの取得
     ErrorMessage( "WriteFile", dwErrorCode ); //エラーメッセージの表示
     ExitProcess( dwErrorCode );  //プロセスを終了
     }
     printf( "Success ! %d bytes written.\n",dwBytesWritten );
     //WriteFileに成功した際メッセージを表示させる
     CloseHandle( hDevice ); //ファイルハンドルを閉じる
     ExitProcess( ERROR_SUCCESS ); //プロセスを終了
}

エラーメッセージルーチン

#include 
#include  //プログラムに使用するヘッダファイル
 
static VOID
ErrorMessage(
     LPTSTR lpOrigin, //ローカルルーチンのプロトコール宣言
     DWORD dwMessageId);
 
static VOID
ErrorMessage(
     LPTSTR lpOrigin, // エラーの場所を示す
     DWORD dwMessageId)// ERROR_XXXコード値
{
     LPTSTR msgBuffer; // システムから返された文字列
     DWORD cBytes; // 返された文字列長

     cBytes = FormatMessage( //メッセージ文字列を書式化する
                   FORMAT_MESSAGE_FROM_SYSTEM |  
//システムメッセージテーブルリソースから検索するよう指定
     FORMAT_MESSAGE_ALLOCATE_BUFFER,
//バッファの割り当てを要求
     NULL, //メッセージ定義の位置を指定
     dwMessageId, //メッセージ識別子を指定
                   MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
                   //メッセージの言語識別子を指定
                   (TCHAR *)&msgBuffer, //バッファへのポインタを指定
                   500, //出力バッファに格納できる最大文字数
                   NULL );
     if( msgBuffer ) {
                   msgBuffer[ cBytes ] = TEXT('\0');
                   printf( "Error: %s -- %s\n", lpOrigin, msgBuffer );
                   LocalFree( msgBuffer );   
     //ローカルメモリオブジェクトを解放し、ハンドルを無効にする
     }
     else {
                   printf( "FormatMessage error: %d\n", GetLastError());
     }
}

「toaster\bus」の解析

Bus_FDO_Powerルーチン(power.c)

NTSTATUS
Bus_FDO_Power (
    PFDO_DEVICE_DATA Data,
    PIRP Irp)
{
    // このルーチンで使う変数を定義する
    NTSTATUS status; //完了ステータス
    POWER_STATE powerState;
    POWER_STATE_TYPE powerType;
    PIO_STACK_LOCATION stack; //スタックロケーションのポインタ
 
    stack = IoGetCurrentIrpStackLocation (Irp); //現在のスタックロケーションを取得
    powerType = stack->Parameters.Power.Type; //要求の種類を示す
    powerState = stack->Parameters.Power.State;
 
    if (Data->DevicePnPState == NotStarted) {
        // デバイスがまだ開始されていない場合、次の階層ドライバにIRPを渡す
        PoStartNextPowerIrp(Irp); //次のパワーIRP開始の準備をする
        IoSkipCurrentIrpStackLocation(Irp); //現在のスタックロケーションの内容を
        //次に低い階層のスタックロケーションにコピーする
        return PoCallDriver(Data->NextLowerDriver, Irp);
        //次に低い階層のスタックロケーションにパワーIRPを受け渡す
    }
    Bus_IncIoCount (Data); //デバイスの受け取った要求回数を1増やす 
    // 現在のスタックロケーションのマイナー機能関数が IRP_MN_SET_POWER の場合、メッセージを表示させる
    if(stack->MinorFunction == IRP_MN_SET_POWER) {
        Bus_KdPrint_Cont(Data, BUS_DBG_POWER_TRACE,
                     ("\tRequest to set %s state to %s\n",
                     ((powerType == SystemPowerState) ?  "System" : "Device"),
                     ((powerType == SystemPowerState) ? \
                        DbgSystemPowerString(powerState.SystemState) :\
                        DbgDevicePowerString(powerState.DeviceState))));
    }
    PoStartNextPowerIrp (Irp); //次のパワーIRP開始の準備をする
    IoSkipCurrentIrpStackLocation(Irp); //現在のスタックロケーションの内容を次に
    //低い階層のスタックロケーションにコピーする
    status =  PoCallDriver (Data->NextLowerDriver, Irp);
    //次に低い階層のスタックロケーションにパワーIRPを受け渡す
    Bus_DecIoCount (Data); //デバイスの受け取った要求回数を1減らす
    return status;
}

Bus_IoCtlルーチン(busenum.c)

NTSTATUS
Bus_IoCtl (
    IN  PDEVICE_OBJECT  DeviceObject,
    IN  PIRP Irp)
{
    // このルーチンで使う変数を定義する
    PIO_STACK_LOCATION irpStack; //スタックロケーションのポインタ
    NTSTATUS status; //完了ステータス
    ULONG inlen, outlen; //バイト長
    PFDO_DEVICE_DATA fdoData; //デバイスエクステンションのポインタ
    PVOID //バッファのポインタ
 
    PAGED_CODE (); //このルーチンはページプールメモリで実行します
   
    fdoData = (PFDO_DEVICE_DATA) DeviceObject->DeviceExtension;
    // デバイスオブジェクトからデバイスエクステンションのポインタの取得
    // 取得したデバイスオブジェクトがFDOでないとき、以下の処理を行う
    if (!fdoData->IsFDO) {
 
        status = STATUS_INVALID_DEVICE_REQUEST; //IRPの完了状態はデバイスが無効
        Irp->IoStatus.Status = status; //IRPに状態を設定
        IoCompleteRequest (Irp, IO_NO_INCREMENT); //IRPの完了を伝える
        return status;}
 
    if (fdoData->DevicePnPState == Deleted) {
        // デバイスがすでに抜かれている際、以下の処理を行う
        Irp->IoStatus.Status = status = STATUS_DELETE_PENDING; //状態の設定
        IoCompleteRequest (Irp, IO_NO_INCREMENT); //IRPの完了
        return status;
    }
    Bus_IncIoCount (fdoData); //デバイスの受け取った要求回数を1増やす
    irpStack = IoGetCurrentIrpStackLocation (Irp); //現在のスタックロケーションを取得
    buffer = Irp->AssociatedIrp.SystemBuffer; //システムバッファの取得
    inlen = irpStack->Parameters.DeviceIoControl.InputBufferLength;
    //入力バッファのバイト長を取得
    outlen = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
    //出力バッファのバイト長を取得
    status = STATUS_INVALID_PARAMETER; //定義していないコントロールコード

    // I/Oコントロールコードの分岐
    switch (irpStack->Parameters.DeviceIoControl.IoControlCode)
{
 
    case IOCTL_BUSENUM_PLUGIN_HARDWARE:
        if ((inlen == outlen) &&
            // 次の条件の場合、メッセージの表示、状態の設定、読み書きしたバイト数の設定を行う
            ((sizeof (BUSENUM_PLUGIN_HARDWARE) + sizeof(UNICODE_NULL) * 2) <=
             inlen) &&
            (sizeof (BUSENUM_PLUGIN_HARDWARE) ==
             ((PBUSENUM_PLUGIN_HARDWARE) buffer)->Size))
{
            Bus_KdPrint(fdoData, BUS_DBG_IOCTL_TRACE, ("PlugIn called\n"));
            //完了状態を設定
            status= Bus_PlugInDevice((PBUSENUM_PLUGIN_HARDWARE)buffer,
inlen, fdoData); //メッセージの表示
            Irp->IoStatus.Information = outlen; //読み書きしたバイト長の設定
        }
        break;
    case IOCTL_BUSENUM_UNPLUG_HARDWARE:
        if ((sizeof (BUSENUM_UNPLUG_HARDWARE) == inlen) &&
            (inlen == outlen) &&
            (((PBUSENUM_UNPLUG_HARDWARE)buffer)->Size == inlen))
{
            Bus_KdPrint(fdoData, BUS_DBG_IOCTL_TRACE, ("UnPlug called\n"));
//メッセージの表示
            status= Bus_UnPlugDevice((PBUSENUM_UNPLUG_HARDWARE)buffer, fdoData);
            //完了状態を設定
            Irp->IoStatus.Information = outlen; //読み書きしたバイト長の設定
        }
        break;
    case IOCTL_BUSENUM_EJECT_HARDWARE:
        if ((sizeof (BUSENUM_EJECT_HARDWARE) == inlen) &&
            (inlen == outlen) &&
            (((PBUSENUM_EJECT_HARDWARE)buffer)->Size == inlen))
{
            Bus_KdPrint(fdoData, BUS_DBG_IOCTL_TRACE, ("Eject called\n"));
//メッセージの表示
            status= Bus_EjectDevice((PBUSENUM_EJECT_HARDWARE)buffer, fdoData);
            //完了状態を設定
            Irp->IoStatus.Information = outlen; //読み書きしたバイト長の設定
        }
        break;  
    default:
        // I/Oコントロールコードがデフォルトの場合、処理は行わず、STATUS_INVALID_PARAMETER を戻す
        break;
    }
 
    Irp->IoStatus.Status = status; //完了状態を設定
    IoCompleteRequest (Irp, IO_NO_INCREMENT); //IRPの完了
    Bus_DecIoCount (fdoData); //デバイスの受け取った要求回数を1減少させる
    return status;
}

参考資料

「WDMデバイスドライバプログラミング完全ガイド」下
Edword N. Dekker, Jpseph M. Newcomer著 株式会社クイック訳
「Windows NTデバイスドライバプログラミング」
アート・ベーカー著 WinProgDDK ML和訳プロジェクト訳
page up