初めての USB クライアント ドライバーの記述方法 (UMDF)
この記事では、Microsoft Visual Studio 2022 で提供されているユーザー モード ドライバー、USB (UMDF V2) テンプレートを使用して、ユーザー モード ドライバー フレームワーク (UMDF) ベースのクライアント ドライバーを作成します。 クライアント ドライバーを作成してインストールした後、デバイス マネージャーでクライアント ドライバーを表示し、デバッガーでドライバーの出力を確認します。
UMDF (この記事ではフレームワークと呼ばれます) は、コンポーネント オブジェクト モデル (COM) に基づいています。 すべてのフレームワーク オブジェクトは、既定で IUnknown とそのメソッド、QueryInterface、AddRef、および Release を実装する必要があります。 AddRef メソッドと Release メソッドはオブジェクトの有効期間を管理するため、クライアント ドライバーは参照カウントをメインする必要はありません。 QueryInterface メソッドを使用すると、クライアント ドライバーは、Windows Driver Frameworks (WDF) オブジェクト モデルの他のフレームワーク オブジェクトへのインターフェイス ポインターを取得できます。 フレームワーク オブジェクトは、複雑なドライバー タスクを実行し、Windows とやりとりします。 特定のフレームワーク オブジェクトは、クライアント ドライバーがフレームワークとやりとりできるようにするインターフェイスを公開します。
UMDF ベースのクライアント ドライバーはインプロセス COM サーバー (DLL) として実装されており、USB デバイス用のクライアント ドライバーを作成する場合は C++ が推奨される言語です。 通常、クライアント ドライバーは、フレームワークによって公開される複数のインターフェイスを実装します。 この記事では、フレームワーク インターフェイスをコールバック クラスとして実装するクライアント ドライバー定義クラスについて説明します。 これらのクラスがインスタンス化されると、結果のコールバック オブジェクトは特定のフレームワーク オブジェクトと連携します。 このパートナーシップにより、クライアント ドライバーは、フレームワークによって報告されたデバイスまたはシステム関連のイベントに応答できます。 Windows が特定のイベントについてフレームワークに通知するたびに、フレームワークはクライアント ドライバーのコールバック (利用可能な場合) を呼び出します。 それ以外の場合、フレームワークはイベントの既定の処理を続行します。 テンプレート コードは、ドライバー、デバイス、キューのコールバック クラスを定義します。
テンプレートによって生成されるソース コードの詳細については、「USB クライアント ドライバーの UMDF テンプレート コードについて」を参照してください。
開始する前に
ユーザー モード ドライバーの開発、デバッグ、インストールには、次の 2 つのコンピューターが必要です。
- Windows 10 以降のバージョンの Windows オペレーティング システムを実行しているホスト コンピューター。 ホスト コンピューターは開発環境であり、ドライバーを記述してデバッグします。
- ドライバーをテストするオペレーティング システムのバージョン (Windows 11 バージョン 22H2 など) を実行しているターゲット コンピューター。 ターゲット コンピューターには、デバッグするユーザー モード ドライバーとデバッガーの 1 つがあります。
場合によっては、ホスト コンピューターとターゲット コンピューターが同じバージョンの Windows を実行している場合、Windows 10 以降のバージョンの Windows を実行しているコンピューターを 1 台だけ使用できます。 この記事では、ユーザー モード ドライバーの開発、デバッグ、インストールに 2 台のコンピューターを使用していることを前提としています。
始める前に、次の要件を満たしていることを確認します。
ソフトウェア要件
ホスト コンピューターに Visual Studio 2022 がある。
ホスト コンピューターに、Windows 11 バージョン 22H2 用の最新の Windows Driver Kit (WDK) がある。
このキットには、USB クライアント ドライバーの開発、ビルド、デバッグに必要なヘッダー、ライブラリ、ツール、ドキュメント、デバッグ ツールが含まれています。 WDK の最新バージョンは、「WDK を取得する方法」から入手できます。
ホスト コンピューターに、Windows 用のデバッグ ツールの最新バージョンがある。 WDK から最新バージョンを取得することも、 Windows 用デバッグ ツールをダウンロードしてインストールすることもできます。
2 台のコンピューターを使用している場合は、ユーザー モード デバッグ用にホスト コンピューターとターゲット コンピューターを構成する必要があります。 詳細については、「 Visual Studio でのユーザー モード デバッグの設定」を参照してください。
ハードウェア要件
クライアント ドライバーを記述する USB デバイスを取得します。 ほとんどの場合、USB デバイスとそのハードウェア仕様が提供されています。 この仕様では、デバイスの機能とサポートされているベンダー コマンドについて説明します。 この仕様を使用して、USB ドライバーの機能を確定し、関連する設計上の意思決定を行います。
USB ドライバーの開発を初めて使用する場合は、OSR USB FX2 学習キットを使用して、WDK に含まれる USB サンプルを調査します。 USB FX2 デバイスと、クライアント ドライバーを実装するために必要なすべてのハードウェア仕様が含まれています。
推奨資料
- すべてのドライバー開発者のための概念
- デバイス ノードとデバイス スタック
- Windows 上のドライバーの概要
- ユーザー モード ドライバー フレームワーク
- Windows Driver Foundation を使用したドライバーの開発、Penny Orwick および Guy Smith で作成。 詳細については、「WDF を使用したドライバーの開発」を参照してください。
手順 1: ドライバー コードを生成する
UMDF ドライバー コードの記述の詳細については、「テンプレートに基づく UMDF ドライバーの記述」を参照してください。
USB 固有のコードの場合は、Visual Studio 2022 で次のオプションを選択します
- [新しいプロジェクト] ダイアログ ボックスの上部にある検索ボックスに、もう一度「USB」と入力します。
- 中央のペインで、[ユーザー モード ドライバー、USB (UMDF V2)] を選択します。
- [次へ] を選択します。
- プロジェクト名を入力し、保存場所を選択して、[作成] を選択します。
次のスクリーンショットは、USB ユーザー モード ドライバー テンプレートの [新しいプロジェクト] ダイアログ ボックスを示しています。
この記事では、プロジェクトの名前が MyUSBDriver_UMDF_ であることを前提としています。 次のファイルが含まれます。
ファイル | 説明 |
---|---|
Driver.h; Driver.c | ドライバー モジュールのエントリ ポイントの実装が含まれています。 DriverEntry と WDFDRIVER に関連する機能とコールバック。 |
Device.h; Device.c | WDFDEVICE と WDFUSBDEVICE に関連する機能とコールバック。 |
Queue.h; Queue.c | WDFQUEUE 関連の機能とコールバック。 |
Trace.h | デバイス インターフェイス GUID を定義します。 また、トレース関数とマクロも宣言します。 |
<Project name>.inf | ターゲット コンピューターにクライアント ドライバーをインストールするために必要な INF ファイル。 |
手順 2: デバイスに関する情報を追加する
ドライバーをビルドする前に、デバイス (具体的にはハードウェア ID) に関する情報を追加する必要があります。 ハードウェア ID を指定するには:
- [ソリューション エクスプローラー] ウィンドウで、[MyUSBDriver_UMDF_] を右クリックし、[プロパティ] を選択します。
- 次に示すように、[MyUSBDriver_UMDF_ のプロパティ ページ] ウィンドウで、[構成プロパティ] [>ドライバーのインストール] [>デプロイ] の順に移動します。
- デプロイ前に以前のドライバーバージョンを削除にチェックを入れます。
- [Target Device Name] (ターゲット デバイス名) で、テストとデバッグ用に構成したコンピューターの名前を選択します。
- [Hardware ID Driver Update] (ハードウェア ID のドライバーの更新) を選択し、ドライバーのハードウェア ID を入力します。 この演習では、ハードウェア ID は Root\MyUSBDriver_UMDF_ です。 [OK] を選択します。
Note
この演習のハードウェア ID は実際のハードウェアを識別するものではありません。 これは、ルート ノードの子としてデバイス ツリー内の場所が与えられる架空のデバイスを識別します。 実際のハードウェアの場合は、[ハードウェア ID ドライバーの更新] を選択しないでください。 代わりに、[インストールして確認] を選択します。 ハードウェア ID は、ドライバーの情報 (INF) ファイルで確認できます。 [ソリューション エクスプローラー] ウィンドウで、[MyUSBDriver_UMDF_>] ドライバー ファイルに移動し、[MyUSBDriver_UMDF_.inf] をダブルクリックします。 ハードウェア ID は [Standard.NT$ARCH$] の下に記載されています。
すべての UMDF ベースの USB クライアント ドライバーには、マイクロソフトが提供する 2 つのドライバー (リフレクターと WinUSB) が必要です。
リフレクター: ドライバーが正常に読み込まれた場合、リフレクターはカーネル モード スタックで最上位のドライバーとして読み込まれます。 リフレクターは、カーネル モード スタックの最上位ドライバーである必要があります。 この要件を満たすために、テンプレートの INF ファイルは、サービスとしてリフレクターを指定し、WinUSB を INF の下位フィルター ドライバーとして指定します。
[MyDevice_Install.NT.Services] AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall ; flag 0x2 sets this as the service for the device AddService=WinUsb,0x000001f8,WinUsb_ServiceInstall ; this service is installed because its a filter.
WinUSB: クライアント ドライバーの場合、WinUSB はカーネル モード USB ドライバー スタックへのゲートウェイであるため、インストール パッケージには Winusb.sys 用のコインストーラーが含まれている必要があります。 読み込まれるもう 1 つのコンポーネントは、クライアント ドライバーのホスト プロセス (Wudfhost.exe) で、WinUsb.dll という名前のユーザー モード DLL です。 Winusb.dll は、クライアント ドライバーと WinUSB の間の通信プロセスを簡略化する WinUSB 関数を公開します。
手順 3: USB クライアント ドライバー コードをビルドする
ドライバーをビルドするには:
- Visual Studio 2022 でドライバー プロジェクトまたはソリューションを開きます。
- [ソリューション エクスプローラー] でソリューションを右クリックして、[構成マネージャー] を選択します。
- [構成マネージャー] から、関心のあるビルドの種類に対応する [アクティブ ソリューション構成 ](たとえば、[デバッグ] または [リリース]) と [アクティブ ソリューション プラットフォーム] (たとえば、[Win32]) を選択します。
- デバイス インターフェイスの GUID がプロジェクト全体で正確であることを確認します。
- デバイス インターフェイス GUID は Trace.h で定義され、Device.c の
MyUSBDriverUMDFCreateDevice
から参照されます。 MyUSBDriver_UMDF_ という名前でプロジェクトを作成すると、Visual Studio 2022 はデバイス インターフェイス GUID をGUID_DEVINTERFACE_MyUSBDriver_UMDF_
という名前で定義しますが、不正なパラメーター&GUID_DEVINTERFACE_MyUSBDriverUMDF
を使用してWdfDeviceCreateDeviceInterface
を呼び出します。 ドライバーが正しくビルドされるように、間違ったパラメーターを Trace.h で定義された名前に置き換えます。
- デバイス インターフェイス GUID は Trace.h で定義され、Device.c の
- [ビルド] メニューの [ソリューションのビルド] を選択します。
詳しくは、「ドライバーのビルド」をご覧ください。
手順 4: テストとデバッグ用にコンピューターを構成する
ドライバーをテストおよびデバッグするには、ホスト コンピューターでデバッガーを実行し、ターゲット コンピューターでドライバーを実行します。 ここまでは、ホスト コンピューター上で Visual Studio を使用してドライバーをビルドしてきました。 次に、ターゲット コンピューターを構成する必要があります。 ターゲット コンピューターを構成するには、「ドライバーのデプロイとテスト用にコンピューターをプロビジョニングする」の手順に従います。
手順 5: カーネル デバッグのトレースを有効にする
テンプレート コードには、関数呼び出しのトレースに役立つトレース メッセージ (TraceEvents) がいくつか含まれています。 ソース コード内のすべての関数には、ルーチンの開始と終了をマークするトレース メッセージが含まれています。 エラーの場合、トレース メッセージにはエラー コードと意味のある文字列が含まれます。 ドライバー プロジェクトでは WPP トレースが有効になっているため、ビルド プロセス中に作成される PDB シンボル ファイルには、トレース メッセージのフォーマット指示が含まれています。 WPP トレース用にホスト コンピューターとターゲット コンピューターを構成すると、ドライバーはトレース メッセージをファイルまたはデバッガーに送信できます。
WPP トレース用にホスト コンピューターを構成するには
トレース メッセージ フォーマット命令を PDB シンボル ファイルから抽出して、トレース メッセージ フォーマット (TMF) ファイルを作成します。
Tracepdb.exe を使用して TMF ファイルを作成できます。 このツールは、WDK の<install folder>Windows Kits\10\bin\<architecture> フォルダーにあります。 次のコマンドは、ドライバー プロジェクトの TMF ファイルを作成します。
tracepdb -f <PDBFiles> -p <TMFDirectory>
-f オプションは、PDB シンボル ファイルの場所と名前を指定します。 -p オプションは、Tracepdb によって作成される TMF ファイルの場所を指定します。 詳しくは、「Tracepdb コマンド」を参照してください。
指定した場所には 3 つのファイルがあり、プロジェクト内の C コード ファイルごとに 1 つずつあります。 GUID ファイル名が与えられます。
デバッガーで、次のコマンドを入力します。
.load Wmitrace .chain !wmitrace.searchpath + <TMF file location>
これらのコマンドでは次のことが行われます。
- Wmitrace.dll 拡張機能を読み込みます。
- デバッガー拡張機能が読み込まれたことを確認します。
- デバッガー拡張機能の検索パスに TMF ファイルの場所を追加します。
次のような内容が出力されます。
Trace Format search path is: 'C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE;c:\drivers\tmf
WPP トレース用にターゲット コンピューターを構成するには
ターゲット コンピューターに Tracelog ツールがあることを確認します。 このツールは、WDK の <install_folder>Windows Kits\10\Tools\<arch> フォルダーにあります。 詳細については、「Tracelog コマンド構文」を参照してください。
[コマンド ウィンドウ] を管理者として開きます。
次のコマンドを入力します。
tracelog -start MyTrace -guid \#c918ee71-68c7-4140-8f7d-c907abbcb05d -flag 0xFFFF -level 7-rt -kd
このコマンドは、MyTrace という名前のトレース セッションを開始します。
guid 引数は、クライアント ドライバーであるトレース プロバイダーの GUID を指定します。 Visual Studio 2022 プロジェクトの Trace.h から GUID を取得できます。 別のオプションとして、次のコマンドを入力して、.guid ファイルに GUID を指定できます。 ファイルには、ハイフン形式の GUID が含まれています。
tracelog -start MyTrace -guid c:\\drivers\\Provider.guid -flag 0xFFFF -level 7-rt -kd
トレース セッションを停止するには、次のコマンドを入力します。
tracelog -stop MyTrace
手順 6: ターゲット コンピューターにドライバーをデプロイする
- [ソリューション エクスプローラー] ウィンドウで、プロジェクト名 [MyUSBDriver_UMDF_] を右クリックし、[プロパティ] を選択します。
- 左側のウィンドウで、[構成プロパティ > ドライバーのインストール > デプロイ] に移動します。
- [ターゲット デバイス名] に、ターゲット コンピューターの名前を指定します。
- [インストール/再インストールと確認] を選択します。
- OK を選択します。
- [デバッグ] メニューの [デバッグの開始] を選択するか、キーボードの F5 キーを押します。
Note
[ハードウェア ID ドライバーの更新] で、デバイスのハードウェア ID を指定しないでください。 ハードウェア ID は、ドライバーの情報 (INF) ファイルでのみ指定する必要があります。
手順 7: デバイス マネージャーでドライバーを表示する
次のコマンドを入力して、[デバイス マネージャー] を開きます。
devmgmt
[デバイス マネージャー] に、次のノードが表示されていることを確認します。
USB デバイス
MyUSBDriver_UMDF_Device
手順 8: デバッガーで出力を表示する
ホスト コンピューターの [デバッガー イミディエイト ウィンドウ] に、トレース メッセージが表示されることを確認します。
出力は次のようになります。
[0]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::OnPrepareHardware Entry
[0]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::OnPrepareHardware Exit
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::CreateInstanceAndInitialize Entry
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::Initialize Entry
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::Initialize Exit
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::CreateInstanceAndInitialize Exit
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::Configure Entry
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyIoQueue::CreateInstanceAndInitialize Entry
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyIoQueue::Initialize Entry
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyIoQueue::Initialize Exit
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyIoQueue::CreateInstanceAndInitialize Exit
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::Configure Exit
解説
フレームワークとクライアント ドライバーが連携して Windows とやりとりし、USB デバイスに送信された要求を処理する方法を見てみましょう。 この図は、UMDF ベースの USB クライアント ドライバー用にシステムに読み込まれたモジュールを示しています。
各モジュールの目的を次に示します。
- アプリケーション — USB デバイスとコミュニケーションするための I/O 要求を発行するユーザー モード プロセス。
- I/O マネージャー — 受信したアプリケーション要求を表す I/O 要求パケット (IRP) を作成して、ターゲット デバイスのカーネル モード デバイス スタックの最上位に転送する Windows コンポーネント。
- リフレクター — カーネル モード デバイス スタック (WUDFRd.sys) の上部にインストールされている、マイクロソフトが提供するカーネル モード ドライバー。 リフレクターは、I/O マネージャーから受信した IRP をクライアント ドライバー ホスト プロセスにリダイレクトします。 要求を受信すると、フレームワークとクライアント ドライバーが要求を処理します。
- ホスト プロセス — ユーザー モード ドライバーが実行されるプロセス (Wudfhost.exe)。 また、フレームワークと I/O ディスパッチャーもホストします。
- クライアント ドライバー: USB デバイスのユーザー モード関数ドライバー。
- UMDF — クライアント ドライバーの代わりに Windows とのほとんどのやりとりを処理するフレームワーク モジュール。 クライアント ドライバーが一般的なドライバー タスクの実行に使用できるユーザー モード デバイス ドライバー インターフェイス (DDI) を公開します。
- ディスパッチャー— ホストプロセスで実行されるメカニズム。 要求がユーザー モード ドライバーによって処理され、ユーザー モード スタックの一番下に到達した後、要求をカーネル モードに転送する方法を決定します。 この図では、ディスパッチャーは要求をユーザー モード DLL (Winusb.dll) に転送します。
- Winusb.dll — クライアント ドライバーと WinUSB (Winusb.sys、カーネル モードで読み込まれる) 間の通信プロセスを簡略化する WinUSB 関数を公開する、マイクロソフトが提供するユーザー モード DLL。
- Winusb.sys — USB デバイスのすべての UMDF クライアント ドライバーに必要な、マイクロソフトが提供するドライバー。 ドライバーはリフレクターの下にインストールする必要があり、カーネル モードで USB ドライバー スタックへのゲートウェイとして機能します。 詳細については、「開発者向け WinUSB の概要」を参照してください。
- USB ドライバー スタック — マイクロソフトが提供するドライバーのセット。USB デバイスとのプロトコル レベルの通信を処理します。 詳細については、「Windows の USB ホスト側ドライバー」を参照してください。
アプリケーションが USB ドライバー スタックに対して要求を行うたびに、Windows I/O マネージャーはその要求をリフレクターに送信し、リフレクターはその要求をユーザー モードのクライアント ドライバーに送ります。 クライアント ドライバーは、WinUSB に要求を送信する WinUSB 関数を内部的に呼び出す特定の UMDF メソッドを呼び出すことによって、要求を処理します。 要求を受信すると、WinUSB は要求を処理するか、USB ドライバー スタックに転送します。