次の方法で共有


チュートリアル: Hello World Windows ドライバー (Kernel-Mode Driver Framework) を記述する

この記事では、Kernel-Mode Driver Framework (KMDF) を使用して小さな ユニバーサル Windows ドライバー を作成し、別のコンピューターにドライバーを展開してインストールする方法について説明します。

[前提条件]

  • 手順に従って Windows Driver Kit (WDK) をインストールします。 WDK をインストールすると、Windows 用のデバッグ ツールが含まれます。

  • Visual Studio 2022 をインストールします。 Visual Studio 2022 をインストールするときに、 C++ によるデスクトップ開発 ワークロードを選択し、[ 個々のコンポーネント ] で次の追加を選択します。

    • MSVC v143 - VS 2022 C++ ARM64/ARM64EC Spectre対策済みライブラリ (最新)
    • MSVC v143 - VS 2022 C++ x64/x86 Spectre 軽減ライブラリ (最新)
    • 最新の v143 ビルド ツール (ARM64/ARM64EC) に対応した Spectre 軽減策付き C++ ATL
    • Spectre 軽減策を使用した最新の v143 ビルド ツール用 C++ ATL (x86 および x64)
    • Spectre 軽減策を使用した最新の v143 ビルド ツール用 C++ MFC (ARM64/ARM64EC)
    • Spectre 軽減策を使用した最新の v143 ビルド ツール用 C++ MFC (x86 および x64)
    • Windows ドライバー キット

ドライバーの作成とビルド

  1. Microsoft Visual Studio を開きます。 [ ファイル ] メニューの [ 新しい > プロジェクト] を選択します。

  2. [ 新しいプロジェクトの作成 ] ダイアログ ボックスで、左側のドロップダウンで [C++ ] を選択し、中央のドロップダウンで [Windows ] を選択し、右側のドロップダウンで [ ドライバー ] を選択します。

  3. プロジェクトの種類の一覧から カーネル モード ドライバー、空 (KMDF) を選択します。 [次へ] を選択します。

    カーネル モード ドライバー オプションが選択されている Visual Studio の [新しいプロジェクト] ダイアログ ボックスのスクリーンショット。

    ヒント

    Visual Studio でドライバー プロジェクト テンプレートが見つからない場合、WDK Visual Studio 拡張機能が正しくインストールされませんでした。 この問題を解決するには、Visual Studio インストーラーを起動し、[変更] を選択し、[個々のコンポーネント] タブで Windows ドライバー キットを追加して、[変更] を選択します。

  4. [ 新しいプロジェクトの構成 ] ダイアログ ボックスで、[ プロジェクト名 ] フィールドに「KmdfHelloWorld」と入力します。

    新しい KMDF ドライバーまたは UMDF ドライバーを作成するときは、32 文字以下のドライバー名を選択する必要があります。 この長さの制限は wdfglobals.h で定義されています。

  5. [ 場所 ] フィールドに、新しいプロジェクトを作成するディレクトリを入力します。

  6. [ ソリューションとプロジェクトを同じディレクトリに配置する ] をオンにして、[ 作成] を選択します。

    [作成] ボタンが強調表示されている Visual Studio の [新しいプロジェクトの構成] ダイアログ ボックスのスクリーンショット。

    Visual Studio では、1 つのプロジェクトとソリューションが作成されます。 ソリューション エクスプローラー ウィンドウで確認できます。 ([ソリューション エクスプローラー] ウィンドウが表示されない場合は、[表示] メニューから [ソリューション エクスプローラー] を選択します)。このソリューションには、KmdfHelloWorld という名前のドライバー プロジェクトがあります。

    ソリューションと KmdfHelloWorld という名前の空のドライバー プロジェクトが表示されている Visual Studio ソリューション エクスプローラー ウィンドウのスクリーンショット。

  7. ソリューション エクスプローラー ウィンドウで、ソリューション 'KmdfHelloWorld' (1 つのプロジェクトのうち 1 つ) を右選択し、Configuration Manager を選択します。 ドライバー プロジェクトの構成とプラットフォームを選択します。 たとえば、[ デバッグ ] と [ x64] を選択します。

  8. ソリューション エクスプローラー ウィンドウでKmdfHelloWorld プロジェクトを右クリックし、[追加] を選択し、[新しい項目] を選択します。

  9. [ 新しい項目の追加 ] ダイアログ ボックスで、「Driver.c」と入力します。

    ファイル名拡張子は .c であり、 .cppではありません。

    [] を選択し、[] を追加します。 次に示すように、 Driver.c ファイルが ソース ファイルの下に追加されます。

    ドライバー プロジェクトに追加された driver.c ファイルが表示されている Visual Studio ソリューション エクスプローラー ウィンドウのスクリーンショット。

最初のドライバー コードを記述する

空の Hello World プロジェクトを作成し、Driver.c ソース ファイルを追加したら、2 つの基本的なイベント コールバック関数を実装して、ドライバーの実行に必要な最も基本的なコードを記述します。

  1. Driver.c では、まず次のヘッダーを含めます。

    #include <ntddk.h>
    #include <wdf.h>
    

    ヒント

    Ntddk.hを追加できない場合は、Configuration -> C/C++ -> General -> Additional Include Directory を開き、C:\Program Files (x86)\Windows Kits\10\Include\<build#>\kmを追加します。<build#>を WDK インストールの適切なディレクトリに置き換えます。

    Ntddk.h にはすべてのドライバーの主要な Windows カーネル定義が含まれていますが、 Wdf.h には Windows Driver Framework (WDF) に基づくドライバーの定義が含まれています。

  2. 次に、2 つのコールバックの宣言を指定します。

    DRIVER_INITIALIZE DriverEntry;
    EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd;
    
  3. DriverEntry を記述するには、次のコードを使用します。

    NTSTATUS 
    DriverEntry(
        _In_ PDRIVER_OBJECT     DriverObject, 
        _In_ PUNICODE_STRING    RegistryPath
    )
    {
        // NTSTATUS variable to record success or failure
        NTSTATUS status = STATUS_SUCCESS;
    
        // Allocate the driver configuration object
        WDF_DRIVER_CONFIG config;
    
        // Print "Hello World" for DriverEntry
        KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: DriverEntry\n" ));
    
        // Initialize the driver configuration object to register the
        // entry point for the EvtDeviceAdd callback, KmdfHelloWorldEvtDeviceAdd
        WDF_DRIVER_CONFIG_INIT(&config, 
                               KmdfHelloWorldEvtDeviceAdd
                               );
    
        // Finally, create the driver object
        status = WdfDriverCreate(DriverObject, 
                                 RegistryPath, 
                                 WDF_NO_OBJECT_ATTRIBUTES, 
                                 &config, 
                                 WDF_NO_HANDLE
                                 );
        return status;
    }
    

    DriverEntry は、多くのユーザー モード アプリケーション向けの Main() など、すべてのドライバーのエントリ ポイントです。 DriverEntry のジョブは、ドライバー全体の構造体とリソースを初期化することです。 この例では、 DriverEntry の "Hello World" を印刷し、 EvtDeviceAdd コールバックのエントリ ポイントを登録するようにドライバー オブジェクトを構成した後、ドライバー オブジェクトを作成して返しました。

    ドライバー オブジェクトは、デバイス オブジェクト、I/O キュー、タイマー、スピンロックなどを含む、ドライバーで作成する可能性のある他のすべてのフレームワーク オブジェクトの親オブジェクトとして機能します。 フレームワーク オブジェクトの詳細については、「フレームワーク オブジェクト の概要」を参照してください。

    ヒント

    DriverEntry の場合は、コード分析とデバッグに役立つ名前を "DriverEntry" として保持することを強くお勧めします。

  4. 次に、次のコードを使用して 、KmdfHelloWorldEvtDeviceAdd を記述します。

    NTSTATUS 
    KmdfHelloWorldEvtDeviceAdd(
        _In_    WDFDRIVER       Driver, 
        _Inout_ PWDFDEVICE_INIT DeviceInit
    )
    {
        // We're not using the driver object,
        // so we need to mark it as unreferenced
        UNREFERENCED_PARAMETER(Driver);
    
        NTSTATUS status;
    
        // Allocate the device object
        WDFDEVICE hDevice;    
    
        // Print "Hello World"
        KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: KmdfHelloWorldEvtDeviceAdd\n" ));
    
        // Create the device object
        status = WdfDeviceCreate(&DeviceInit, 
                                 WDF_NO_OBJECT_ATTRIBUTES,
                                 &hDevice
                                 );
        return status;
    }
    

    EvtDeviceAdd は、デバイスが到着したことを検出すると、システムによって呼び出されます。 そのジョブは、そのデバイスの構造体とリソースを初期化することです。 この例では、 EvtDeviceAdd の "Hello World" メッセージを出力し、デバイス オブジェクトを作成して返しました。 作成する他のドライバーでは、ハードウェアの I/O キューを作成したり、デバイス固有の情報用に デバイス コンテキスト ストレージ領域を設定したり、デバイスの準備に必要な他のタスクを実行したりできます。

    ヒント

    デバイスの追加コールバックの場合は、ドライバーの名前をプレフィックス (KmdfHelloWorldEvtDeviceAdd) として指定した方法に注目してください。 一般に、他のドライバーの関数と区別するために、この方法でドライバーの関数に名前を付けてお勧めします。 DriverEntry は、正確に名前を付けるべき唯一の名前です。

  5. 完全な Driver.c は次のようになります。

    #include <ntddk.h>
    #include <wdf.h>
    DRIVER_INITIALIZE DriverEntry;
    EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd;
    
    NTSTATUS 
    DriverEntry(
        _In_ PDRIVER_OBJECT     DriverObject, 
        _In_ PUNICODE_STRING    RegistryPath
    )
    {
        // NTSTATUS variable to record success or failure
        NTSTATUS status = STATUS_SUCCESS;
    
        // Allocate the driver configuration object
        WDF_DRIVER_CONFIG config;
    
        // Print "Hello World" for DriverEntry
        KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: DriverEntry\n" ));
    
        // Initialize the driver configuration object to register the
        // entry point for the EvtDeviceAdd callback, KmdfHelloWorldEvtDeviceAdd
        WDF_DRIVER_CONFIG_INIT(&config, 
                               KmdfHelloWorldEvtDeviceAdd
                               );
    
        // Finally, create the driver object
        status = WdfDriverCreate(DriverObject, 
                                 RegistryPath, 
                                 WDF_NO_OBJECT_ATTRIBUTES, 
                                 &config, 
                                 WDF_NO_HANDLE
                                 );
        return status;
    }
    
    NTSTATUS 
    KmdfHelloWorldEvtDeviceAdd(
        _In_    WDFDRIVER       Driver, 
        _Inout_ PWDFDEVICE_INIT DeviceInit
    )
    {
        // We're not using the driver object,
        // so we need to mark it as unreferenced
        UNREFERENCED_PARAMETER(Driver);
    
        NTSTATUS status;
    
        // Allocate the device object
        WDFDEVICE hDevice;    
    
        // Print "Hello World"
        KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: KmdfHelloWorldEvtDeviceAdd\n" ));
    
        // Create the device object
        status = WdfDeviceCreate(&DeviceInit, 
                                 WDF_NO_OBJECT_ATTRIBUTES,
                                 &hDevice
                                 );
        return status;
    }
    
  6. Driver.c を保存します。

この例は、ドライバーの基本的な概念を示しています。これは、初期化されると、システムが何かを必要なときにそれらを呼び出すのを待つ"コールバックのコレクション" です。 システム呼び出しには、新しいデバイス到着イベント、ユーザー モード アプリケーションからの I/O 要求、システム電源シャットダウン イベント、別のドライバーからの要求、またはユーザーが予期せずデバイスを取り外したときの突然の削除イベントが考えられます。 幸い、「Hello World」と言うには、ドライバーとデバイスの作成について心配するだけで済んだのです。

次に、ドライバーをビルドします。

ドライバーをビルドする

  1. ソリューション エクスプローラー ウィンドウで、ソリューション 'KmdfHelloWorld' (1 つのプロジェクトのうち 1 つ) を右選択し、Configuration Manager を選択します。 ドライバー プロジェクトの構成とプラットフォームを選択します。 この演習では、[ デバッグ ] と [ x64] を選択します。

  2. ソリューション エクスプローラー ウィンドウでKmdfHelloWorld を右クリックし、[プロパティ] を選択します[Wpp トレース] > [すべてのオプション] で、[Wpp トレースの実行] を [いいえ] に設定します。 [ 適用] を選択し、[ OK] を選択します

  3. ドライバーをビルドするには、[ビルド] メニューから [ソリューションビルド ] を選択します。 Visual Studio の [出力] ウィンドウにビルドの進行状況が表示されます。 ([出力] ウィンドウが表示されない場合は、[表示] メニューから [出力] を選択します)。ソリューションが正常にビルドされたことを確認したら、Visual Studio を閉じます。

  4. ビルドされたドライバーを表示するには、エクスプローラーで KmdfHelloWorld フォルダーに移動し、 x64\Debug\KmdfHelloWorld に移動します。 フォルダーには次のものが含まれます。

    • KmdfHelloWorld.sys -- カーネル モード ドライバー ファイル
    • KmdfHelloWorld.inf -- ドライバーのインストール時に Windows が使用する情報ファイル
    • KmdfHelloWorld.cat -- インストーラーがドライバーのテスト署名を検証するために使用するカタログ ファイル

ヒント

ドライバーのビルド時に DriverVer set to a date in the future が表示される場合は、Inf2Cat が /uselocaltime設定するようにドライバー プロジェクトの設定を変更します。 そのためには、 Configuration Properties->Inf2Cat->General->Use Local Time を使用します。 Stampinf と Inf2Cat の両方でローカル時刻が使用されるようになりました。

ドライバーを展開する

通常、ドライバーをテストしてデバッグする場合、デバッガーとドライバーは別のコンピューターで実行されます。 デバッガーを実行するコンピューターは ホスト コンピューターと呼ばれ、ドライバーを実行するコンピューターは ターゲット コンピューターと呼ばれます。 ターゲット コンピューターは 、テスト コンピューターとも呼ばれます。

これまでは、Visual Studio を使用してホスト コンピューター上にドライバーをビルドしました。 次に、ターゲット コンピューターを構成する必要があります。

  1. 「ドライバーの展開とテスト用にコンピューターをプロビジョニングする (WDK 10)」の手順に従います。

    ヒント

    ネットワーク ケーブルを使用してターゲット コンピューターを自動的にプロビジョニングする手順に従う場合は、ポートとキーをメモします。 デバッグ手順の後半で使用します。 この例では、ポートとして 50000 を使用し、キーとして 1.2.3.4 を使用します。

    実際のドライバー デバッグ シナリオでは、KDNET で生成されたキーを使用することをお勧めします。 KDNET を使用してランダム キーを生成する方法の詳細については、「ドライバーの デバッグ - ステップ バイ ステップ ラボ (Sysvad カーネル モード)」 トピックを参照してください。

  2. ホスト コンピューターで、Visual Studio でソリューションを開きます。 KmdfHelloWorld フォルダー内のソリューション ファイル (KmdfHelloWorld.sln) をダブルクリックできます。

  3. ソリューション エクスプローラー ウィンドウで、KmdfHelloWorld プロジェクトを右クリックし、[プロパティ] を選択します

  4. ドライバーインストールのデプロイメント>に移動します。

  5. [ ターゲット デバイス名] で、テストとデバッグ用に構成したコンピューターの名前を選択します。 この演習では、MyTestComputer という名前のコンピューターを使用します。

  6. 最新バージョンのドライバーをテストしていることを確認するには、展開前に [ 以前のバージョンのドライバーを削除する] をオンにします。

  7. [ ハードウェア ID ドライバーの更新] を選択し、ドライバーのハードウェア ID を入力します。 この演習では、ハードウェア ID は Root\KmdfHelloWorld です。 [OK] を選択.

    この演習では、ハードウェア ID は実際のハードウェアを識別しません。 これは、ルート ノードの子として デバイス ツリー 内の場所が与えられる架空のデバイスを識別します。 実際のハードウェアの場合は、[ ハードウェア ID ドライバーの更新] を選択しないでください。代わりに、[ インストールして確認] を選択します。 ドライバーの情報 (INF) ファイルにハードウェア ID が表示されます。 ソリューション エクスプローラー ウィンドウで、KmdfHelloWorld > Driver Files に移動し、KmdfHelloWorld.inf をダブルクリックします。 ハードウェア ID は [Standard.NT$ARCH$] にあります。

    [Standard.NT$ARCH$]
    %KmdfHelloWorld.DeviceDesc%=KmdfHelloWorld_Device, Root\KmdfHelloWorld
    
  8. [ ビルド ] メニューの [ ソリューションのデプロイ] を選択します。 Visual Studio は、ドライバーのインストールと実行に必要なファイルをターゲット コンピューターに自動的にコピーします。 デプロイには 1 ~ 2 分かかる場合があります。

    ドライバーを展開すると、ドライバー ファイルがテスト コンピューターの %Systemdrive%\drivertest\drivers フォルダーにコピーされます。 展開中に問題が発生した場合は、ファイルがテスト コンピューターにコピーされているかどうかを確認できます。 .inf、.cat、test cert、.sys ファイル、およびその他の必要なファイルが、%systemdrive%\drivertest\drivers フォルダーに存在することを確認します。

    ドライバーの展開の詳細については、「 テスト コンピューターへのドライバーの展開」を参照してください。

ドライバーをインストールする

Hello World ドライバーをターゲット コンピューターに展開したら、ドライバーをインストールします。 自動オプションを使用してターゲット コンピューターを Visual Studio で事前にプロビジョニングした場合、Visual Studio は、プロビジョニング プロセスの一環としてテスト署名済みドライバーを実行するようにターゲット コンピューターを設定します。 DevCon ツールを使用してドライバーをインストールするだけで済みます。

  1. ホスト コンピューターで、WDK インストールの [ツール] フォルダーに移動し、DevCon ツールを見つけます。 たとえば、次のフォルダーを探します。

    C:\Program Files (x86)\Windows Kits\10\Tools\<version number>\x64\devcon.exe

    DevCon ツールをリモート コンピューターにコピーします。

  2. ターゲット コンピューターで、ドライバー ファイルを含むフォルダーに移動し、DevCon ツールを実行してドライバーをインストールします。

    1. ドライバーのインストールに使用する devcon ツールの一般的な構文を次に示します。

      devcon install <INF ファイル><ハードウェア ID>

      このドライバーをインストールするために必要な INF ファイルは、KmdfHelloWorld.inf です。 INF ファイルには、ドライバー バイナリをインストールするためのハードウェア ID KmdfHelloWorld.sys含まれています 。 INF ファイルにあるハードウェア ID が Root\KmdfHelloWorld であることを思い出してください。

    2. 管理者としてコマンド プロンプト ウィンドウを開きます。 ビルドされたドライバー .sys ファイルを含むフォルダーに移動し、次のコマンドを入力します。

      devcon install kmdfhelloworld.inf root\kmdfhelloworld

      devcon が認識されないというエラー メッセージが表示される場合は、devcon ツールへのパスを追加してみてください。 たとえば、 C:\Tools という名前のターゲット コンピューター上のフォルダーにコピーした場合は、次のコマンドを使用してみてください。

      c:\tools\devcon install kmdfhelloworld.inf root\kmdfhelloworld

      テスト ドライバーが署名されていないドライバーであることを示すダイアログ ボックスが表示されます。 続行するには、[このドライバをインストールする] を選択します。

      ドライバーのインストール プロセス中に表示されるセキュリティ警告のスクリーンショット。

ドライバーをデバッグする

ターゲット コンピューターに KmdfHelloWorld ドライバーをインストールしたら、ホスト コンピューターからリモートでデバッガーをアタッチします。

  1. ホスト コンピューターで、管理者としてコマンド プロンプト ウィンドウを開きます。 WinDbg.exe ディレクトリに移動します。 Windows キットのインストールの一部としてインストールされた Windows Driver Kit (WDK) から WinDbg.exe の x64version を使用します。 WinDbg.exeの既定のパスを次に示します。

    C:\Program Files (x86)\Windows Kits\10\Debuggers\x64

  2. WinDbg を起動し、次のコマンドを使用して、ターゲット コンピューター上のカーネル デバッグ セッションに接続します。 ポートとキーの値は、ターゲット コンピューターのプロビジョニングに使用した値と同じである必要があります。 ポートには 50000 を使用し、キーには 1.2.3.4 (デプロイ手順で使用した値) を使用します。 k フラグは、これがカーネル デバッグ セッションであることを示します。

    WinDbg -k net:port=50000,key=1.2.3.4

  3. [ デバッグ ] メニューの [ 中断] を選択します。 ホストコンピューターのデバッガーがターゲットコンピューターに侵入します。 [デバッガー コマンド] ウィンドウに、カーネル デバッグコマンド プロンプト kd> が表示されます。

  4. この時点で、 kd> プロンプトにコマンドを入力して、デバッガーを試すことができます。 たとえば、次のコマンドを試すことができます。

  5. ターゲット コンピューターをもう一度実行するには、[デバッグ] メニューから [Go] を選択するか、"g" キーを押してから "Enter" キーを押します。

  6. デバッグ セッションを停止するには、[デバッグ] メニューから [デバッガーのデタッチ] を選択します。

    Important

    "go" コマンドを使用して、デバッガーを終了する前にターゲット コンピューターを再度実行することを確認してください。または、ターゲット コンピューターは、まだデバッガーと通信しているため、マウスとキーボードの入力に応答しなくなります。

ドライバーのデバッグ プロセスの詳細なチュートリアルについては、「 ユニバーサル ドライバーのデバッグ - ステップ バイ ステップ ラボ (Echo Kernel-Mode)」を参照してください。

リモート デバッグの詳細については、「 WinDbg を使用したリモート デバッグ」を参照してください。