本主題介紹瞭如何使用內核模式驅動程序框架 (KMDF) 編寫很是小的通用 Windows 驅動程序。node
若要開始操做,請確保你已安裝 Microsoft Visual Studio 2015 和 Windows 驅動程序工具包 (WDK) 10。windows
當安裝 WDK 時,須要包括 Windows 調試工具。網絡
在名稱字段中,輸入「KmdfHelloWorld」做爲項目名稱。框架
備註函數
在建立新的 KMDF 或 UMDF 驅動程序時,必須選擇一個很少於 32 個字符的驅動程序名稱。 此長度限制在 wdfglobals.h 中定義。工具
在位置字段中,輸入要在其中建立新項目的目錄。測試
選中建立解決方案的目錄。 單擊肯定。spa
Visual Studio 建立了一個項目和一個解決方案。 你能夠在解決方案資源管理器窗口中看到它們,如此處所示。 (若是「解決方案資源管理器」窗口不可見,則從視圖菜單中選擇解決方案資源管理器。)該解決方案包含名爲 KmdfHelloWorld 的驅動程序項目。debug
在解決方案資源管理器窗口中,右鍵單擊 KmdfHelloWorld,而後選擇屬性。 導航到配置屬性 > 驅動程序設置 > 常規,請注意,目標平臺默認爲通用。3d
在解決方案資源管理器窗口中,右鍵單擊 KmdfHelloWorld,而後選擇添加 > 新建項目。
在添加新項目對話框中,選擇 C++ 文件。 對於名稱,輸入「Driver.c」。
備註
文件擴展名爲 .c,不是 .cpp。
單擊添加。 Driver.c 文件添加在源文件下,以下所示。
如今,你已經建立了空的 Hello World 項目並添加了 Driver.c 源文件,你將經過實現兩個基本事件回調函數來編寫驅動程序運行所需的最基本的代碼。
在 Driver.c 中,首先包括如下標頭:
C++
#include <ntddk.h> #include <wdf.h>
Ntddk.h 包含全部驅動程序的核心 Windows 內核定義,而 Wdf.h 包含基於 Windows 驅動程序框架 (WDF) 的驅動程序的定義。
接下來,爲你將使用的兩個回調提供聲明:
C++
DRIVER_INITIALIZE DriverEntry; EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd;
使用如下代碼編寫 DriverEntry:
C++
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」來幫助進行代碼分析和調試。
接下來,使用如下代碼編寫 KmdfHelloWorldEvtDeviceAdd:
C++
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 是徹底應該這樣命名的惟一一項。
如今,你的整個 Driver.c 以下所示:
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; }
保存 Driver.c。
此示例說明了驅動程序的基本概念:驅動程序是一個「回調集合」,經初始化後,會在系統有須要時等待系統調用。 這多是新設備到達事件、用戶模式應用程序的 I/O 請求、系統電源關閉事件、另外一個驅動程序的請求,或用戶意外拔出設備時的意外刪除事件。 幸運的是,就「Hello World」而言,只需操心驅動程序和設備的建立。
接下來,你將生成驅動程序。
在解決方案資源管理器窗口中,右鍵單擊解決方案「KmdfHelloWorld」(1 個項目),而後選擇配置管理器。 爲驅動程序項目和程序包項目選擇配置和平臺。 在本練習中,咱們選擇調試和 x64。
在解決方案資源管理器窗口中,右鍵單擊 KmdfHelloWorld,而後選擇屬性。 在 Wpp 跟蹤 > 全部選項中,將運行 Wpp 跟蹤設置爲否。 單擊應用,而後單擊肯定。
若要查看生成的驅動程序,則在「文件資源管理器」中,依次轉到你的 KmdfHelloWorld 文件夾和 C:\KmdfHelloWorld\x64\Debug。 該文件夾包括:
一般,當你測試和調試驅動程序時,調試器和驅動程序會在不一樣的計算機上運行。 運行調試器的計算機稱爲主計算機,運行驅動程序的計算機稱爲目標計算機。 目標計算機也稱爲測試計算機。
到目前爲止,你已在主計算機上使用 Visual Studio 生成了驅動程序。 如今,你須要配置目標計算機。
按照預配計算機以便進行驅動程序部署和測試 (WDK 10) 中的說明進行操做。
提示
按照步驟使用網絡電纜自動預配目標計算機時,請記下端口和密鑰。 之後,你將在調試步驟中使用它們。 在此示例中,咱們將使用 50000 做爲端口,使用 1.2.3.4 做爲密鑰。
在實際驅動程序調試方案中,咱們建議使用 KDNET 生成的密鑰。 有關如何使用 KDNET 生成一個隨機密鑰的詳細信息,請參閱調試驅動程序 - 分步實驗室(Sysvad 內核模式)主題。
在主計算機上,在 Visual Studio 中打開你的解決方案。 你能夠在 KmdfHelloWorld 文件夾中雙擊解決方案文件 KmdfHelloWorld.sln。
選擇硬件 ID 驅動程序更新,而後輸入驅動程序的硬件 ID。 在本練習中,硬件 ID 爲 Root\KmdfHelloWorld。 單擊肯定。
備註
在本練習中,硬件 ID 未標識硬件的真實部分。 它標識了虛構設備,該設備位於設備樹中,做爲根節點的子節點。 對於真實的硬件,不選擇硬件 ID 驅動程序更新,選擇安裝和驗證。 你將在驅動程序的信息 (INF) 文件中看到硬件 ID。 在解決方案資源管理器窗口中,轉到 KmdfHelloWorld > 驅動程序文件,而後雙擊 KmdfHelloWorld.inf。 硬件 ID 位於 [Standard.NT$ARCH$] 之下。
C++
[Standard.NT$ARCH$] %KmdfHelloWorld.DeviceDesc%=KmdfHelloWorld_Device, Root\KmdfHelloWorld
在生成菜單上,選擇部署解決方案。 Visual Studio 會自動將安裝和運行驅動程序所需的文件複製到目標計算機。 此操做可能會花費一兩分鐘的時間。
在部署驅動程序時,驅動程序文件將複製到測試計算機上的 %Systemdrive%\drivertest\drivers 文件夾。 若是部署期間發生錯誤,你能夠查看這些文件是否被複制到了測試計算機。 請確認 .inf、.cat、測試證書和 .sys 文件以及其餘任何須要的文件均位於 %systemdrive%\drivertest\drivers 文件夾中。
有關部署驅動程序的詳細信息,請參閱將驅動程序部署到測試計算機。
將你的 Hello World 驅動程序部署到目標計算機後,如今你將安裝該驅動程序。 若是你以前使用自動選項經過 Visual Studio 預配了目標計算機,則在預配過程當中,Visual Studio 會將目標計算機設置爲運行測試簽名驅動程序。 如今,你只需使用 DevCon 工具安裝驅動程序便可。
在主計算機上,導航到 WDK 安裝中的「Tools」文件夾,而後找到 DevCon 工具。 例如,在如下文件夾中查看:
C:\Program Files (x86)\Windows Kits\10\Tools\x64\devcon.exe
將 DevCon 工具複製到遠程計算機。
在目標計算機上,導航到包含驅動程序文件的文件夾,而後運行 DevCon 工具,以安裝驅動程序。
如下是將用於安裝驅動程序的 devcon 工具的常規語法:
devcon install <INF file> <hardware ID>
安裝此驅動程序所需的 INF 文件是 KmdfHelloWorld.inf。 INF 文件包含用於安裝驅動程序二進制文件 KmdfHelloWorld.sys 的硬件 ID。 回想一下,位於 INF 文件中的硬件 ID 是 Root\KmdfHelloWorld。
以管理員身份打開命令提示符窗口。 導航到你的驅動程序包文件夾,而後輸入如下命令:
devcon install kmdfhelloworld.inf root\kmdfhelloworld
若是你收到一條關於 devcon 未被識別的錯誤消息,請嘗試添加 devcon 工具的路徑。 例如,若是已將其複製到目標計算機上稱爲 C:\Tools 的文件夾,則嘗試使用如下命令:
c:\tools\devcon install kmdfhelloworld.inf root\kmdfhelloworld
此時將顯示一個對話框,指示測試驅動程序是未簽名驅動程序。 單擊仍然安裝此驅動程序以繼續。
如今,你已在目標計算機上安裝了 KmdfHelloWorld 驅動程序,你將從主計算機遠程鏈接調試器。
在主計算機上,以管理員身份打開命令提示符窗口。 轉到 WinDbg.exe 目錄。 咱們將使用安裝 Windows 工具包過程當中安裝的 Windows 驅動程序工具包 (WDK) 中 x64 版本的 WinDbg.exe。 下面是 WinDbg.exe 的默認路徑:
C:\Program Files (x86)\Windows Kits\10\Debuggers\x64
使用如下命令啓動 WinDbg 以鏈接到目標計算機上的內核調試會話。 端口和密鑰的值應該與你預配目標計算機所使用的值相同。 咱們將使用 50000 做爲端口,使用 1.2.3.4 做爲密鑰,咱們在部署步驟期間使用過這些值。 K 標誌指示這是內核調試會話。
WinDbg -k net:port=50000,key=1.2.3.4
在調試菜單上,選擇中斷。 主計算機上的調試器將中斷目標計算機。 在調試器命令窗口中,你能夠看到內核調試命令提示符:kd>。
此時,能夠試驗調試器,方法是在 kd> 提示符處輸入命令。 例如,能夠嘗試使用如下命令:
若要讓目標計算機再次運行,請從調試菜單中選擇執行,或者按「g」,而後按「Enter」。
若要中止調試會話,請從調試菜單中選擇分離調試器。
重要
請確保在退出調試器以前使用「執行」命令讓目標計算機再次運行,不然目標計算機將仍然對你的鼠標和鍵盤輸入無響應,由於它仍在與調試器通話。
有關驅動程序調試過程的詳細分步演練,請參閱調試通用驅動程序 - 分步實驗室(回顯內核模式)。
有關遠程調試的詳細信息,請參閱使用 WinDbg 遠程調試。