老程序員做新方向,老樹發新芽,作為菜鳥的我,寫點心得,用以記錄並與同行交流
1對一些概念的理解:
KMDF與UMDF。兩者的框架,及使用VS生成的初始代碼基本相同,只有所包含的頭文件不同,鏈接的系統庫不同,最終生成的文件分別為.sys和.dll。因為框架完全相同,相互移植應該比較容易。
UMDF驅動運行在用戶空間,調試相對容易,程序崩潰時也不至於對系統影響太大。所以只要能實現,應該首選使用UMDF框架。
雖說UMDF框架運行在用戶空間,但也不是說所有WIN32應用程序可以使用的API都能正常工作。本人遇到過調用WMI組件在UMDF驅動中編譯失敗的問題,比較有趣的是使用C++調用時編譯失敗,但使用C文件調用時就正常。
上手
無論什么時候,微軟提供的例子都是最好的學習材料,而且某些例子本身就已經能夠工作的非常出色。本人曾經使用其中提供的鍵盤過濾驅動,只修改了安裝文件中的設備路徑,就可以正常安裝,穩定工作。所以當要開發一個驅動時,最快捷的方法是找到最接近其功能的例子,拿它來修改。以下所記錄的內容都基於一個例子,或者使用WDK的模板創建的驅動框架。
INF文件及安裝
INF文件中最重要的是硬件編號,即[Standard.NT$ARCH$]所指定的內容。這里設定的硬件編號與實際安裝時使用的硬件編號一定要相同。否則安裝會失敗。
如果是設備管理器能直接看到的設備,如果一個ACPI設備,在設備管理器中安裝就可以。如果我們開發的設備驅動沒有對應的硬件設備結點,就需要使用devcon來安裝,該程序會創建硬件設備結點,並安裝驅動。devcon可以在WDK的安裝目錄中找到。使用方法:
devcon.exe inf文件名稱 硬件設備結點(即inf文件中設置的結點)
例如,如果設備驅動的INF文件中有如下片段:
[Standard.NT$ARCH$]
%mydriver.DeviceDesc%=mydriver_Device, Root\MyDriver
則devcon的用法為:devcon.exe install xxx.inf Root\MyDriver
在測試階段,因為我們的驅動沒有獲得微軟提供的簽名,需要在系統中開啟測試簽名才能正常安裝,某些驅動甚至需要將驅動文件中的測試簽名證書安裝到系統信任的證書目錄中。開啟測試簽名的方法:bcdedit /set testsigining on
WHCK測試
如果只是單純的學習WDK驅動開發,可以不做WHCK測試,但如果是做產品,WHCK是必須要做的,需要注意的幾點:
1)server必須是 server2008 R2, server2012, server2016等的英文版。其它更新的server版本是否可用不清楚,但英文版本是必須的。
2)安裝server時注意不要安裝標准版,它沒有桌面,只有命令行,使用不便,至於基於它能否運行WHCK server,還真沒有試過。
3)如果一時找不到英文版本也沒有關系,安裝完,將界面改成英文資源。
並修改注冊表以將系統默認語言改為英文。
將HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\Language
下面InstallLanguage的值改為0409。如果還有問題,可以把同級的跟Language相關的注冊表值都改為:0409
4)最好使用真實機器安裝server,來運行WHCK服務器端。因為WHCK運行時間很長,可能需要幾天。放在開發機上一不小心關掉,就麻煩了。我試過在vmware中運行server, WHCK客戶端連不上。在服務端看不到測試機。
5)WHCK測試環境bug很多,需要問題不要心急,要多試。不行就重啟測試機,服務一般不需要重啟,實在沒辦法重啟它也是一種方法。
6)可以在開發機上安裝whck的studio,用來連接到服務器,執行測試用例,而不需要登錄到服務器,比較方便。
指定驅動的類型
在調用WdfDeviceCreate之前調用:WdfDeviceInitSetDeviceType來設置驅動自身的類型,如:
WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_ACPI); 將其設置為一個基於ACPI的驅動。
如果我們不依賴於其它總線,就可以不調用這個函數。如果設置的類型不對就不能正常安裝。如果是過濾驅動,在調用它之前還需要調用WdfFdoInitSetFilter來申明自己是過濾驅動。
驅動中使用事件
UMDF驅動中可以直接使用CreateEvent等用戶空間的API,這里不再贅述。KMDF中需要使用KeInitializeEvent來初始化一個Event, KeWaitForSingleObject來等待。KePulseEvent來釋放等待中的事件。
驅動中使用延時
KMDF驅動中使用KeDelayExecutionThread來做延時,需要注意的是:其時間單位是100納秒。如果延時1毫秒需要使用的值為:10000。而且有一個變態的設計:我們傳入的值如果為負數,即表示需要等待的時間,如果為正數,則表示需要等待到的時間,即如果使用正數,就需要讀出當前時間,加上要等待的時間以后再傳給KeDelayExecutionThread。相信還是使用負數比較方便。
驅動相互調用
兩個KMDF驅動調用比較方便,在A驅動中WdfDeviceCreate之前調用WdfDeviceInitAssignName函數來為自己申明一個名字。如:
DECLARE_CONST_UNICODE_STRING(devName, L"\\Device\\MyTestDevice");
status = WdfDeviceInitAssignName(DeviceInit, &devName);
這樣在B驅動中就可以使用IoGetDeviceObjectPointer來獲取驅動A的相關信息,進而調用IoCallDriver來發送Iocontrol進行數據交互。
而UMDF驅動訪問KMDF驅動就比較麻煩了。請參考:
https://docs.microsoft.com/en-us/windows-hardware/drivers/wdf/controlling-device-access#specifying-device-security-in-an-inf-file
中斷級別(IRQ LEVEL)
相比應用開發,KMDF驅動開發有一個比較麻煩的問題是中斷級別。同樣的函數在不同的地方調用效果不一定相同。當函數報錯,發生崩潰等問題時查看微軟的幫助文檔,了解中斷級別對該函數的影響,及當前代碼運行在什么樣的中斷級別上。