共用方式為


撰寫 Hello World Windows 驅動程式 (KMDF)

本文說明如何使用 Kernel-Mode Driver Framework (KMDF) 撰寫小型 通用 Windows 驅動程式 ,然後將驅動程式部署並安裝在個別計算機上。

繼續之前,請先完成 下載 Windows 驅動程式套件 (WDK) 中列出的安裝步驟。

當您安裝 WDK 時,會包含適用於 Windows 的偵錯工具。

建立和建置驅動程式

  1. 開啟 Microsoft Visual Studio。 在 [ 檔案] 功能表上,選擇 [ 新增 > 專案]。

  2. 在 [ 建立新專案 ] 對話框中,選取左下拉式清單中的 [C++ ]、選擇中間下拉式清單中的 [Windows ],然後在右下拉式清單中選擇 [ 驅動程式 ]。

  3. 從項目類型清單中選取 [核心模式驅動程式]、[空白 (KMDF) ]。 選取 [下一步] 。

    Visual Studio 新項目對話框的螢幕快照,其中已選取核心模式驅動程序選項。

  4. 在 [ 設定新專案 ] 對話框中,於 [ 項目名稱 ] 字段中輸入 “KmdfHelloWorld”。

    注意

    當您建立新的 KMDF 或 UMDF 驅動程式時,您必須選取具有 32 個字元或更少字元的驅動程式名稱。 此長度限制定義於 wdfglobals.h 中。

  5. 在 [ 位置] 欄位中,輸入您要在其中建立新項目的目錄。

  6. 勾選 [將方案和專案放在相同的目錄中 ],然後選取 [ 建立]。

    Visual Studio 設定新項目對話框的螢幕快照,其中已醒目提示 [建立] 按鈕。

    Visual Studio 會建立一個專案和方案。 您可以在 [方案總管] 視窗中查看這些檔案。 (如果看不到 方案總管 視窗,請從 [檢視] 功能表選擇 [方案總管]。) 解決方案有名為 KmdfHelloWorld 的驅動程序專案。

    Visual Studio 方案總管視窗的螢幕快照,其中顯示解決方案和名為 KmdfHelloWorld 的空白驅動程序專案。

  7. [方案總管] 視窗中,選取並保留 (或) 以滑鼠右鍵按兩下 KmdfHelloWorld 解決方案,然後選擇 [Configuration Manager]。 選擇驅動程式專案的組態和平臺。 例如,選擇 [ 偵錯 ] 和 [x64]。

  8. [方案總管] 視窗中,再次選取並按住 (或) 以滑鼠右鍵按兩下 KmdfHelloWorld 專案,選擇 [新增],然後選取 [新增專案]。

  9. 在 [ 新增專案 ] 對話框中,選取 [C++ 檔案]。 針對 [名稱],輸入 “Driver.c”。

    注意

    擴展名為 .c,而不是 .cpp

    選取 [新增]。 Driver.c 檔案會在 [原始程序檔] 底下新增,如下所示。

    Visual Studio 方案總管視窗的螢幕快照,其中顯示已新增至驅動程序專案的 driver.c 檔案。

撰寫您的第一個驅動程式程式代碼

既然您已建立空的 Hello World 專案並新增 Driver.c 原始程式檔,您將會藉由實作兩個基本事件回呼函式來撰寫驅動程式執行所需的最基本程序代碼。

  1. 在 Driver.c 中,從包含下列標頭開始:

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

    提示

    如果您無法新增 Ntddk.h,請開啟 Configuration -> C/C++ -> 一般 -> 其他包含目錄 並新增 C:\Program Files (x86)\Windows Kits\10\Include\<build#>\km,並以 <build#> WDK 安裝中的適當目錄取代 。

    Ntddk.h 包含所有驅動程式的核心 Windows 核心定義,而 Wdf.h 則包含以 Windows Driver Framework (WDF) 為基礎的驅動程式定義。

  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 佇列、定時器、微調鎖定等等。 如需架構對象的詳細資訊,請參閱 Framework 物件簡介

    提示

    針對 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) 個專案],然後選擇 [Configuration Manager]。 選擇驅動程式專案的組態和平臺。 在此練習中,我們選擇 [ 偵錯 ] 和 [x64]。

  2. [方案總管] 視窗中,選取並保留 (或以滑鼠右鍵按兩下 [kmdfHelloWorld]) ,然後選擇 [屬性]。 在 [Wpp 追蹤 > 所有選項] 中,將 [執行 Wpp 追蹤 ] 設定為 [否]。 依序選取 [套用] 和 [確定] 。

  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>>> 當地時間。 現在 Stampinf 和 Inf2Cat 都會使用當地時間。

部署驅動程式

一般而言,當您測試和偵錯驅動程式時,調試程式和驅動程式會在不同的計算機上執行。 執行調試程式的計算機稱為 主計算機,而執行驅動程式的計算機稱為 目標計算機。 目標計算機也稱為 測試計算機

到目前為止,您已使用 Visual Studio 在主電腦上建置驅動程式。 現在您需要設定目標計算機。

  1. 請遵循佈建 電腦以進行驅動程式部署和測試中的指示, (WDK 10)

    提示

    當您依照步驟使用網路纜線自動佈建目標計算機時,請記下埠和密鑰。 您稍後會在偵錯步驟中使用它們。 在此範例中,我們將使用 50000 作為埠,並使用 1.2.3.4 作為密鑰。

    在實際的驅動程序偵錯案例中,我們建議使用 KDNET 產生的密鑰。 如需如何使用 KDNET 產生隨機金鑰的詳細資訊,請參閱 偵錯驅動程式 - 逐步實驗室 (Sysvad 核心模式) 主題。

  2. 在主計算機上,在 Visual Studio 中開啟您的方案。 您可以在 KmdfHelloWorld 資料夾中按兩下方案檔案KmdfHelloWorld.sln。

  3. [方案總管] 視窗中,選取並按住 (或以滑鼠右鍵按兩下 KmdfHelloWorld 專案) ,然後選擇 [屬性]。

  4. 在 [ KmdfHelloWorld 屬性頁 ] 視窗中,移至 [ 設定屬性 > 驅動程序安裝 > 部署],如下所示。

  5. 部署之前,請檢查移除先前的驅動程式版本

  6. 針對 [目標裝置名稱],選取您為測試和偵錯設定的計算機名稱。 在此練習中,我們使用名為 MyTestComputer 的計算機。

  7. 選取 [硬體標識符驅動程式更新],然後輸入驅動程式的硬體識別碼。 在此練習中,硬體標識碼為 Root\KmdfHelloWorld。 選取 [確定]。

    已選取部署驅動程式安裝選項的 kmdfhelloworld 屬性頁視窗螢幕快照。

    注意

    在此練習中,硬體標識碼不會識別實際的硬體片段。 它會識別一個虛構的裝置,該 裝置會在裝置樹狀結構 中指定為根節點的子系。 針對實際硬體,請勿選取 [硬體標識符驅動程式更新] ;請改為選取 [安裝並驗證]。 您會在驅動程式的資訊中看到硬體識別碼, (INF) 檔案。 在 [方案總管] 視窗中,移至 [KmdfHelloWorld 驅動程序檔案],然後按兩下 [KmdfHelloWorld.inf>]。 硬體標識碼位於 [Standard.NT$ARCH$]。

    [Standard.NT$ARCH$]
    %KmdfHelloWorld.DeviceDesc%=KmdfHelloWorld_Device, Root\KmdfHelloWorld
    
  8. 在 [ 建置] 功能表上,選擇 [部署解決方案]。 Visual Studio 會自動將安裝及執行驅動程式所需的檔案複製到目標計算機。 部署可能需要一或兩分鐘的時間。

    當您部署驅動程式時,驅動程式檔案會複製到測試計算機上的 %Systemdrive%\drivertest\drivers資料夾。 如果在部署期間發生錯誤,您可以檢查檔案是否複製到測試計算機。 確認 .inf、.cat、測試憑證和 .sys 檔案,以及任何其他必要檔案都存在於 %systemdrive%\drivertest\drivers 資料夾中。

    如需部署驅動程式的詳細資訊,請參閱 將驅動程式部署至測試計算機

安裝驅動程式

將 Hello World 驅動程式部署至目標計算機後,現在您將安裝驅動程式。 當您先前使用 [自動 ] 選項布建目標計算機時,Visual Studio 會設定目標計算機,以在布建過程中執行測試已簽署的驅動程式。 現在,您只需要使用 DevCon 工具來安裝驅動程式。

  1. 在主計算機上,流覽至 WDK 安裝中的 [工具] 資料夾,並找出 DevCon 工具。 例如,查看下列資料夾:

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

    將 DevCon 工具複製到遠端電腦。

  2. 在目標電腦上,流覽至包含驅動程式檔案的資料夾,然後執行DevCon工具,以安裝驅動程式。

    1. 以下是您將用來安裝驅動程式之 devcon 工具的一般語法:

      devcon 安裝 <INF 檔案><硬體識別碼>

      安裝此驅動程式所需的 INF 檔案為 KmdfHelloWorld.inf。 INF 檔案包含用來安裝驅動程式二進位檔的硬體識別碼, KmdfHelloWorld.sys。 回想一下,位於 INF 檔案中的硬體識別碼是 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 驅動程式套件的 x64version WinDbg.exe, (已安裝為 Windows 套件安裝的一部分的 WDK) 。 以下是 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. 若要讓目標計算機再次執行,請從 [錯] 功能表選擇 [移至],或按 “g”,然後按 [enter]。

  6. 若要停止偵錯會話,請選擇 [偵錯] 功能表中的 [中斷鏈接調試程式]。

    重要

    請務必使用 「go」 命令讓目標計算機在結束調試程式之前再次執行,或者目標計算機仍不會回應滑鼠和鍵盤輸入,因為它仍在與調試程式交談。

如需驅動程式偵錯程式的詳細逐步解說,請參閱 偵錯通用驅動程式 - 逐步執行實驗室 (Echo Kernel-Mode)

如需遠端偵錯的詳細資訊,請參閱 使用 WinDbg 進行遠端偵錯

Windows 的偵錯工具

偵錯通用驅動程式 - 逐步實驗室 (Echo 核心模式)

撰寫您的第一個驅動程式