一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

DDK開發(fā)介紹-1

 李林俠圖書館 2011-11-29

DDK開發(fā)介紹

jackyhwei 發(fā)布于 2011-01-28 09:59 點(diǎn)擊:

本篇我們介紹開發(fā)之前的準(zhǔn)備工作,包括開發(fā)環(huán)境準(zhǔn)備、預(yù)備知識(shí)。
開發(fā)環(huán)境準(zhǔn)備
對(duì)于開發(fā)WDM驅(qū)動(dòng)程序來(lái)說(shuō),我們有以下三個(gè)常用組合:
1.直接使用Windows DDK
2.使用DriverStudio
3.使用Windriver
下面我們分別比較三種方式的優(yōu)缺點(diǎn)。
第一種:開發(fā)難度大一些,而且有很多煩瑣的工作要作,大部分都是通用的基礎(chǔ)性的工作。但如果選用這種方式的話你將對(duì)整個(gè)體系結(jié)構(gòu)會(huì)有很好的理解和把握。
第二種:難度低一些,工具軟件已經(jīng)幫你作了很多基礎(chǔ)性的工作。也封裝了一些細(xì)節(jié),你只要專心去作你需要的操作,但由于封裝的問題,可能會(huì)帶來(lái)一些bug。有可能導(dǎo)致項(xiàng)目的失敗。
第三種:幾乎沒有難度(從開發(fā)驅(qū)動(dòng)的角度)。很容易,但只能開發(fā)硬件相關(guān)的驅(qū)動(dòng),事實(shí)上你寫的只是定制和調(diào)用它提供的通用驅(qū)動(dòng)而已。效率上有問題。工作頻率不是很高。但開發(fā)花費(fèi)的時(shí)間很少。是上面的幾 乃至幾十分之一。 
建議: 
用windriver作驅(qū)動(dòng)程序的原型,用driverstudio作最終發(fā)行的驅(qū)動(dòng)程序,如果驅(qū)動(dòng)程序很復(fù)雜的話,建議直接使用ddk開發(fā)。 
上面的幾種情況都需要vc++作為輔助開發(fā)環(huán)境。(ddk也可以直接用命令行工具,但比較煩),前兩種情況都需要ddk。開發(fā)時(shí)間上,第一種最長(zhǎng),第三種最短,第二種可以認(rèn)為是前面兩種方案的折衷。    
如果更具體一點(diǎn)的話,我們可以把以上三種形式比作三種開發(fā)工具,那就是 ms c,vc++,Vb。
如果SDK沒bug的話,用ms c開發(fā)的純sdk程序的bug是最少的。Vc++由于對(duì)sdk進(jìn)行了封裝,必然會(huì)引出一些新的bug。Vb開發(fā)程序雖然快了一些,但運(yùn)行效率比前兩種方式差了很多。
這樣說(shuō)明這三種方式的話,大家一定會(huì)明白了。
我們?yōu)榱撕?jiǎn)便起見,使用ddk+VC的方式。
首 先,我們按正常方式安裝好vc++ 6.0。不過據(jù)微軟文檔說(shuō)ddk98只支持vc++ 5.0。我手里沒有vc++5.0,在vc++6.0下試了一 下,證明可以使用,不過設(shè)置很困難的。當(dāng)然,如果你不覺得煩的話,也可以直接用build工具即可。在安裝好vc++后再安裝ddk開發(fā)包,這樣不容易出 錯(cuò)。
如果你使用DriverStudio開發(fā)包,請(qǐng)先安裝好vc++6.0,然后再安裝它,在安裝softice時(shí)注意選擇通用顯卡驅(qū)動(dòng),這樣一般情況下都能正常使用。

預(yù)備知識(shí)
    在開發(fā)環(huán)境安裝完成后,我們將要步入開發(fā)過程。在實(shí)際動(dòng)手之前,我們先要學(xué)習(xí)一些預(yù)備知識(shí)。
    在設(shè)備驅(qū)動(dòng)程序中,要作很多工作,包括初始化,設(shè)備對(duì)象創(chuàng)建等等工作,其中一些是很重要的,必須實(shí)現(xiàn),一些是可選的,如果你的驅(qū)動(dòng)對(duì)這些功能的要求不是很高的話,可以不實(shí)現(xiàn)。
    要實(shí)現(xiàn)的功能主要有以下幾個(gè):
初始化
創(chuàng)建和刪除設(shè)備
I/O請(qǐng)求的超時(shí)處理
I/O請(qǐng)求的撤消
訪問硬件資源
處理Windows的輸入/輸出請(qǐng)求
串行化對(duì)設(shè)備的訪問
調(diào)用其它驅(qū)動(dòng)程序
處理一個(gè)可熱拔插的設(shè)備被加入或刪除的情況
處理電源管理請(qǐng)求
使用Windows管理診斷功能
處理Windows的打開和關(guān)閉文件句柄的請(qǐng)求

從 實(shí)際工作情況來(lái)看,只有初始化模塊是必不可少的。但是只有初始化模塊的驅(qū)動(dòng)程序什么工作也干不了,只能說(shuō)它僅僅是一個(gè)概念意義上的驅(qū)動(dòng)程序而已,好比失去 感覺的植物人(軀體存在,但已經(jīng)沒有了意志)。通常情況下,一個(gè)完整的驅(qū)動(dòng)程序至少要能響應(yīng)用戶態(tài)程序發(fā)出的I/O訪問請(qǐng)求。大多數(shù)情況下驅(qū)動(dòng)程序要訪問 它們所支持的硬件資源,并且要支持簡(jiǎn)單的電源管理功能和Windows管理診斷功能或能向系統(tǒng)日志寫入信息。
    WDM驅(qū)動(dòng)程序通常由PnP管理器載入內(nèi)存,然后調(diào)用它之中的AddDevice例程來(lái)創(chuàng)建設(shè)備。當(dāng)然,在此時(shí)還要需要一個(gè)inf安裝文件而來(lái)指明該驅(qū)動(dòng)程序需要的一些參數(shù)。
    系 統(tǒng)內(nèi)核通常通過向驅(qū)動(dòng)程序發(fā)送IRP包來(lái)運(yùn)行驅(qū)動(dòng)程序中的實(shí)現(xiàn)代碼。我們以Windows向設(shè)備發(fā)出的ReadFile調(diào)用為例:此時(shí)Windows向驅(qū) 動(dòng)程序發(fā)出一個(gè)“讀”請(qǐng)求的IRP包,讀取緩沖區(qū)的大小和位置作為IRP包中的參數(shù)指定(IRP實(shí)際上是一個(gè)數(shù)據(jù)結(jié)構(gòu),包含幾個(gè)域)。如果你作過 Windows的程序,特別是用 VC作過開發(fā)的話,你應(yīng)該知道,windows用戶態(tài)應(yīng)用程序是消息驅(qū)動(dòng)的,應(yīng)用程序中的代碼是通過消息機(jī)制的觸發(fā)而獲 得運(yùn)行的機(jī)會(huì)的,需要的參數(shù)是通過消息的域(wParam、lParam)傳給應(yīng)用程序。實(shí)際上驅(qū)動(dòng)程序的動(dòng)作還是可以看作是一種消息驅(qū)動(dòng)方式,只不過內(nèi) 核態(tài)的“消息”已經(jīng)不再稱作消息,而是被稱作I/O請(qǐng)求包(IRP)。
    驅(qū)動(dòng)程序通常使用DriverEntry作為入口點(diǎn),與我們?cè)赪indows應(yīng)用程序中定義的WinMain相似。通常情況下,它是驅(qū)動(dòng)程序的默認(rèn)入口點(diǎn)。
    注: 標(biāo)準(zhǔn)Build腳本將驅(qū)動(dòng)程序入口點(diǎn)定為DriverEntry,你最好遵守這個(gè)假設(shè),否則必須修改Build腳本。
    在 這個(gè)入口函數(shù)中,我們必須作必要的初始化設(shè)置,并設(shè)置必要的回調(diào)函數(shù)。我們可以這樣理解:我們用c++(特別是用VC++)時(shí),我們?cè)陬惖臉?gòu)造函數(shù)中要作 必要的初始化操作,并要在類中作消息處理方法的映射,這樣才能讓需要的消息得到適當(dāng)?shù)奶幚?。我們也要在DriverEntry例程中設(shè)置必要的IRP處理 函數(shù)。
    一般情況下,DriverEntry例程要設(shè)置以下幾個(gè)IRP處理函數(shù):
    DriverUnload 指向驅(qū)動(dòng)程序的清除例程。I/O管理器會(huì)在卸載驅(qū)動(dòng)程序前調(diào)用該例程。通常,WDM驅(qū)動(dòng)程序的DriverEntry例程一般不分配任何資源,所以DriverUnload例程也沒有什么清除工作要做。 
    DriverExtension->AddDevice 指向驅(qū)動(dòng)程序的AddDevice函數(shù)。PnP管理器將為每個(gè)硬件實(shí)例調(diào)用一次AddDevice例程。這樣將創(chuàng)建一個(gè)該設(shè)備對(duì)象。 
    DriverStartIo 如果驅(qū)動(dòng)程序使用標(biāo)準(zhǔn)的IRP排隊(duì)方式,應(yīng)該設(shè)置該成員,使其指向驅(qū)動(dòng)程序的StartIo例程。如果你不理解什么是“標(biāo)準(zhǔn)”排隊(duì)方式,不要著急,能后的教程中你就會(huì)完全明白,許多驅(qū)動(dòng)程序都使用這種方法。 
    MajorFunction 是一個(gè)指針數(shù)組,I/O管理器把每個(gè)數(shù)組元素都初始化成指向一個(gè)空函數(shù),這個(gè)空函數(shù)僅返回失敗。驅(qū)動(dòng)程序可能僅需要處理幾種類型的IRP,所以至少應(yīng)該設(shè)置與那幾種IRP類型相對(duì)應(yīng)的指針元素,使它們指向相應(yīng)的派遣函數(shù)。 
下面是一段DriverEntry例程的示例:

  1. extern "C" NTSTATUS  DriverEntry(IN PDRIVER_OBJECT DriverObject
  2. , IN PUNICODE_STRING RegistryPath)
  3.   DriverObject->DriverUnload = DriverUnload; <--1 
  4.   DriverObject->DriverExtension->AddDevice = AddDevice; 
  5.   DriverObject->DriverStartIo = StartIo; 
  6.   DriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnp;
  7.  //設(shè)置各個(gè)IRP的處理函數(shù)    ,這三個(gè)IRP是每一個(gè)WDM驅(qū)動(dòng)程序必須處理的。                 
  8.   DriverObject->MajorFunction[IRP_MJ_POWER] = DispatchPower; 
  9.   DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = DispatchWmi; 
  10.   ...  <--3 
  11.   servkey.Buffer = (PWSTR) ExAllocatePool(PagedPool
  12. , RegistryPath->Length + sizeof(WCHAR));    <--4 
  13.   if (!servkey.Buffer) 
  14.     return STATUS_INSUFFICIENT_RESOURCES; 
  15.   servkey.MaximumLength = RegistryPath->Length + sizeof(WCHAR); 
  16.   RtlCopyUnicodeString(&servkey, RegistryPath); 
  17.   return STATUS_SUCCESS;   <--5 

1.    前三條語(yǔ)句為驅(qū)動(dòng)程序的其它入口點(diǎn)設(shè)置了函數(shù)指針。在這里,我們用了能表達(dá)其功能的名字命名了這些函數(shù):DriverUnload、AddDevice、StartIo。 
2.    每 個(gè)WDM驅(qū)動(dòng)程序必須能處理PNP、POWER、SYSTEM_CONTROL這三種請(qǐng)求;應(yīng)該在這里為這些請(qǐng)求指定派遣函數(shù)。在早期的 Windows 2000 DDK中,IRP_MJ_SYSTEM_CONTROL曾被稱作IRP_MJ_WMI,所以我把系統(tǒng)控制派遣函數(shù)命名為 DispatchWmi。 
3.    在省略號(hào)處,你可以插入設(shè)置其它MajorFunction指針的代碼。 
4.    如果驅(qū)動(dòng)程序需要訪問設(shè)備的服務(wù)鍵,可以在這里備份RegistryPath串。例如,如果驅(qū)動(dòng)程序要作為WMI生產(chǎn)者,則需要備份這個(gè)串。這里我假設(shè)已經(jīng)在某處聲明了一個(gè)類型為UNICODE_STRING的全局變量servkey。 
5.    返回STATUS_SUCCESS指出函數(shù)成功。如果函數(shù)失敗,應(yīng)該返回NTSTATUS.H中的一個(gè)錯(cuò)誤代碼,或者返回用戶定義的錯(cuò)誤代碼。STATUS_SUCCESS的值為0。 
關(guān)于DriverUnload例程的補(bǔ)充說(shuō)明:
在WDM驅(qū)動(dòng)程序中,DriverUnload例程的作用就是釋放DriverEntry例程在全局初始化過程中申請(qǐng)的任何資源,但它幾乎沒什么可做。如果你在DriverEntry中備份了RegistryPath串,應(yīng)該在這里釋放備份所占用的內(nèi)存:
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
  RtlFreeUnicodeString(&servkey);//釋放先前申請(qǐng)的資源
}
如果DriverEntry例程返回一個(gè)失敗狀態(tài)代碼,系統(tǒng)將不再調(diào)用DriverUnload例程。所以,不能讓DriverEntry例程出錯(cuò)后產(chǎn)生任何副作用,必須在它返回錯(cuò)誤代碼前消除副作用(釋放掉申請(qǐng)的系統(tǒng)資源)。
一般情況下,一個(gè)驅(qū)動(dòng)程序可以被多個(gè)設(shè)備利用。WDM驅(qū)動(dòng)程序有一個(gè)特殊的AddDevice函數(shù),PnP管理器為每個(gè)設(shè)備實(shí)例調(diào)用該函數(shù)。以下為該函數(shù)的原型定義:
NTSTATUS AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT pdo)
{
}
DriverObject參數(shù)指向一個(gè)驅(qū)動(dòng)程序?qū)ο螅褪悄阍贒riverEntry入口例程中初始化的那個(gè)驅(qū)動(dòng)程序?qū)ο?。pdo參數(shù)指向設(shè)備堆棧底部的物理設(shè)備對(duì)象。
對(duì)于功能驅(qū)動(dòng)程序,其AddDevice函數(shù)的基本職責(zé)是創(chuàng)建一個(gè)設(shè)備對(duì)象并把它連接到以pdo為底的設(shè)備堆棧中。相關(guān)步驟如下: 
1.    調(diào)用IoCreateDevice創(chuàng)建設(shè)備對(duì)象,并建立一個(gè)私有的設(shè)備擴(kuò)展對(duì)象。 
2.    注冊(cè)一個(gè)或多個(gè)設(shè)備接口,以便應(yīng)用程序能夠發(fā)現(xiàn)設(shè)備的存在。另外,還可以給出設(shè)備名并創(chuàng)建符號(hào)連接。 
3.    初始化設(shè)備擴(kuò)展和設(shè)備對(duì)象的Flag成員。 
4.    調(diào)用IoAttachDeviceToDeviceStack函數(shù)把新設(shè)備對(duì)象放到堆棧上。 
下面我將詳細(xì)解釋這些步驟。
創(chuàng)建設(shè)備對(duì)象
調(diào)用IoCreateDevice函數(shù)創(chuàng)建設(shè)備對(duì)象,例如:

  1. PDEVICE_OBJECT fdo; 
  2. NTSTATUS status = IoCreateDevice(DriverObject, 
  3. sizeof(DEVICE_EXTENSION), 
  4. NULL, 
  5. FILE_DEVICE_UNKNOWN, 
  6. FILE_DEVICE_SECURE_OPEN, 
  7. FALSE, 
  8. &fdo); 

第一個(gè)參數(shù)(DriverObject) 就是AddDevice的第一個(gè)參數(shù)。該參數(shù)用于在驅(qū)動(dòng)程序和新設(shè)備對(duì)象之間建立連接,這樣I/O管理器就可以向設(shè)備發(fā)送指定的IRP。
第二個(gè)參數(shù)是設(shè)備擴(kuò)展結(jié)構(gòu)的大小。I/O管理器自動(dòng)分配這個(gè)內(nèi)存,并把設(shè)備對(duì)象中的DeviceExtension指針指向這塊內(nèi)存。
第三個(gè)參數(shù)在本例中為NULL。它可以是命名該設(shè)備對(duì)象的UNICODE_STRING串的地址。決定是否命名設(shè)備對(duì)象以及以什么名字命名還需要仔細(xì)考慮,我將在后面深入認(rèn)真地討論這個(gè)問題。
第 四個(gè)參數(shù)(FILE_DEVICE_UNKNOWN) 是設(shè)備類型。這個(gè)值可以被設(shè)備硬件鍵(注冊(cè)表中包含該硬件信息的鍵值)或類鍵(注冊(cè)表中包含該類驅(qū) 動(dòng)信息的鍵值)中的可替換值(overriding values)所替代,如果這兩個(gè)鍵都含有該參數(shù)的替換值,那么硬件鍵中的可替換值具有更高的優(yōu)先 權(quán)。對(duì)于屬于某個(gè)已存在類的設(shè)備,必須在這些地方指定正確的值,因?yàn)轵?qū)動(dòng)程序與外圍系統(tǒng)的交互需要依靠這個(gè)值。另外,設(shè)備對(duì)象的默認(rèn)安全設(shè)置也依靠這個(gè)設(shè) 備類型值。
第五個(gè)參數(shù)(FILE_DEVICE_SECURE_OPEN) 為設(shè)備對(duì)象提供Characteristics標(biāo)志。這些標(biāo)志主要關(guān) 系到塊存儲(chǔ)設(shè)備(如軟盤、CDROM、Jaz等等)。未公開標(biāo)志位FILE_AUTOGENERATED_DEVICE_NAME僅用于內(nèi)部使用,并不是 DDK文檔忘記提到該標(biāo)志。這個(gè)參數(shù)同樣也能被硬件鍵或類鍵中的對(duì)應(yīng)值替換,如果兩個(gè)值都存在,那么硬件鍵中的可替換值具有更高的優(yōu)先權(quán)。
第六個(gè)參數(shù)(FALSE) 指出設(shè)備是否是排斥的。通常,對(duì)于排斥設(shè)備,I/O管理器僅允許打開該設(shè)備的一個(gè)句柄。這個(gè)值同樣也能被注冊(cè)表中硬件鍵和類鍵中的值替換,如果兩個(gè)可替換值都存在,硬件鍵中的可替換值具有更高的優(yōu)先權(quán)。
注意 
排斥屬性僅關(guān)系到打開請(qǐng)求的目標(biāo)是命名設(shè)備對(duì)象。如果你遵守Microsoft推薦的WDM驅(qū)動(dòng)程序設(shè)計(jì)方針,沒有為設(shè)備對(duì)象命名,那么打開請(qǐng)求將直接指向 PDO(物理設(shè)備對(duì)象)。PDO通常不能被標(biāo)記為排斥,因?yàn)榭偩€驅(qū)動(dòng)程序沒有辦法知道設(shè)備是否需要排斥特征。把PDO標(biāo)為排斥的唯一的機(jī)會(huì)在注冊(cè)表中,即 設(shè)備硬件鍵或類鍵的Properties子鍵含有Exclusive可替換值。為了完全避免依賴排斥屬性,你應(yīng)該利用IRP_MJ_CREAT例程彈出任 何有違規(guī)行為的打開請(qǐng)求。 
第七個(gè)參數(shù)(&fdo) 是存放設(shè)備對(duì)象指針的地址,IoCreateDevice函數(shù)使用該變量保存剛創(chuàng)建的設(shè)備對(duì)象的地址。
如 果IoCreateDevice由于某種原因失敗,則它返回一個(gè)錯(cuò)誤代碼,不改變fdo中的值。如果IoCreateDevice函數(shù)返回成功代碼,那么 它同時(shí)也設(shè)置了fdo指針。然后我們進(jìn)行到下一步,初始化設(shè)備擴(kuò)展,做與創(chuàng)建新設(shè)備對(duì)象相關(guān)的其它工作,如果在這之后又發(fā)現(xiàn)了錯(cuò)誤,那么在返回前應(yīng)先釋放 剛創(chuàng)建的設(shè)備對(duì)象并返回狀態(tài)碼。見下面例子代碼:

  1. NTSTATUS status = IoCreateDevice(...); 
  2. if (!NT_SUCCESS(status)) 
  3.   return status; 
  4. ... 
  5. if (<some other error discovered>) 
  6.   IoDeleteDevice(fdo); 
  7.   return status; 

為設(shè)備命名
Windows 使 用對(duì)象管理器集中管理系統(tǒng)中的大量的內(nèi)部數(shù)據(jù)結(jié)構(gòu)(每個(gè)對(duì)象在系統(tǒng)中都表現(xiàn)為一個(gè)數(shù)據(jù)結(jié)構(gòu)),包括驅(qū)動(dòng)程序?qū)ο蠛驮O(shè)備對(duì)象。為了便于區(qū)別,每個(gè)對(duì)象都有名 稱,對(duì)象管理器用一個(gè)層次化的命名空間來(lái)管理這些名稱。圖中是DevView(一個(gè)設(shè)備觀察工具,在驅(qū)動(dòng)開發(fā)網(wǎng) 站<http://www.有下載>)顯示的頂層對(duì)象名。此工具以文件夾形式顯示的對(duì)象是目錄對(duì)象,它可 以包含子目錄或常規(guī)對(duì)象,其它圖標(biāo)則代表正常對(duì)象。
通常設(shè)備對(duì)象都把自己的名字放到\Device目錄中。在Windows 2000中,設(shè)備的 名稱有兩個(gè)用途。第一個(gè)用途,通過命名后,其它內(nèi)核模式部件可以通過調(diào)用IoGetDeviceObjectPointer函數(shù)找到該設(shè)備,找到設(shè)備對(duì)象 后,就可以向該設(shè)備的驅(qū)動(dòng)程序發(fā)送IRP(I/O請(qǐng)求包)。
另一個(gè)用途,允許用戶態(tài)的應(yīng)用程序打開命名設(shè)備的句柄,這樣它們就可以向驅(qū)動(dòng)程序發(fā)送 IRP。應(yīng)用程序可以使用標(biāo)準(zhǔn)的CreateFile API打開命名設(shè)備句柄,然后用ReadFile、WriteFile,和 DeviceIoControl向驅(qū)動(dòng)程序發(fā)出請(qǐng)求(關(guān)于這些API函數(shù)的詳細(xì)說(shuō)明和使用,我們將在后面的文章中詳述)。應(yīng)用程序打開設(shè)備句柄時(shí)使 用\\.\路徑前綴。在C/C++語(yǔ)言程序中使用時(shí),需要轉(zhuǎn)化為’\\\\.\\’,這是由語(yǔ)法規(guī)定的。在內(nèi)部,I/O管理器在執(zhí)行名稱搜索前自動(dòng)把 \\.\轉(zhuǎn)換成\??\。為了把\??目錄中的名字與名字在其它目錄(例如,在\Device目錄)中的對(duì)象相連接,對(duì)象管理器實(shí)現(xiàn)了一種稱為符號(hào)連接 (symbolic link)的對(duì)象。
符號(hào)連接
符號(hào)連接有點(diǎn)象WIDOWS桌面上的快捷方式,符號(hào)連接在Windows NT/2K中 的主要用途是把處于列表前面的DOS形式的名稱連接到設(shè)備上。符號(hào)連接可以使對(duì)象管理器在分析一個(gè)名稱時(shí)能跳到命名空間的某個(gè)地方。例如我們通常見到的C 盤,其實(shí)它是就是一個(gè)設(shè)備(磁盤)的符號(hào)鏈接。如果你用過unix/linux操作系統(tǒng)的話,你會(huì)對(duì)符號(hào)鏈接有所理解,這里的符號(hào)鏈接相當(dāng)于unix /linux系統(tǒng)中的軟鏈接。
 
術(shù)語(yǔ)
類鍵
所有設(shè)備類的類鍵都出現(xiàn)在HKLM\System\CurrentControlSet\Control\Class鍵中。它們的鍵名是由Microsoft賦予的GUID值。
硬件鍵 
硬件鍵包含單個(gè)設(shè)備的信息。

在上期中我們講了符號(hào)連接,在應(yīng)用層開發(fā)中我們可以調(diào)用以下函數(shù)來(lái)創(chuàng)建一個(gè)\??目錄下的符號(hào)鏈接:
BOOL okay = DefineDosDevice(DDD_RAW_TARGET_PATH, "barf", "\\Device\\SECTEST_0");
調(diào)用成功后,將會(huì)在設(shè)備命名空間的\??目錄下生成一個(gè)名為”barf“的符號(hào)鏈接,該鏈接指向”“\\Device\\SECTEST_0“這個(gè)對(duì)象。
在核心態(tài)的驅(qū)動(dòng)程序中,我們需要調(diào)用以下的函數(shù)來(lái)創(chuàng)建相應(yīng)的符號(hào)鏈接:
IoCreateSymbolicLink(linkname, targname);
Linkname是要?jiǎng)?chuàng)建的符號(hào)鏈接名,相當(dāng)于上面函數(shù)中的”barf”,targname是該鏈接指向的設(shè)備對(duì)象。
如 果你創(chuàng)建了一個(gè)指向不存在的設(shè)備對(duì)象的符號(hào)鏈接,系統(tǒng)并不會(huì)作任何檢查,當(dāng)你訪問這個(gè)符號(hào)鏈接時(shí)只會(huì)收到一個(gè)錯(cuò)誤報(bào)告。所以你必須要自己保證鏈接的目的對(duì) 象真正存在。如果你想允許用戶模式程序能超越這個(gè)連接而轉(zhuǎn)到其它地方,應(yīng)使用IoCreateUnprotectedSymbolicLink函數(shù)替代上 面的IoCreateSymbolicLink函數(shù)。
給設(shè)備命名后我們就可以很方便地打開該設(shè)備進(jìn)行訪問了。但在方便的同時(shí)你需要注意一個(gè)很嚴(yán)重 的問題:“安全性”。一旦為設(shè)備命名后,符何核心態(tài)的驅(qū)動(dòng)程序都可以打開該設(shè)備的句柄,從而訪問此設(shè)備。而且更糟的是,任何用戶態(tài)的應(yīng)用程序也可以通過建 立該設(shè)備名的符號(hào)鏈接而訪問到該設(shè)備。而這種情況可能是你不愿意看到的。
一旦你決定要為你的設(shè)備命名時(shí),你應(yīng)該將這個(gè)設(shè)備對(duì)象的名稱放到對(duì)象名空間的“\Device”目錄中,我們可以使用以下的核心態(tài)函數(shù)來(lái)創(chuàng)建設(shè)備,同時(shí)給設(shè)備命名:
  1. UNICODE_STRING devname; 
  2. RtlInitUnicodeString(&devname, L"\\Device\\Simple0"); 
  3. IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), &devname, ...); 
這 里的UNICODE_STRING devname就是用來(lái)存放設(shè)備名的地方。RtlInitUnicodeString是unicode串初始化函數(shù), 第一個(gè)參數(shù)是要初始化的變量地址,第二個(gè)為設(shè)備名常量。第二個(gè)參數(shù)前的大寫L是將這個(gè)常量轉(zhuǎn)換成此函數(shù)需要的寬字符串。一般我們使用如下的格式為設(shè)備命 名:
設(shè)備名0
其中的0為設(shè)備的實(shí)例號(hào)(即產(chǎn)生實(shí)例的順序)。
說(shuō)到這里我要提醒一下大家,在驅(qū)動(dòng)程序中一般不使用Ansi字符串,取而代之的是UniCode字符串,它以16位表示一個(gè)字符。這點(diǎn)和在WinCE下開發(fā)軟件很相似。
在 以前的老式驅(qū)動(dòng)程序中(Win 3.2 or Win95)中大量使用設(shè)備命名(包括直接用名字和名字的符號(hào)鏈接)的方式來(lái)訪問設(shè)備。這樣做有兩個(gè)很主要 的問題。一是安全性問題。在上面我們已經(jīng)講了這樣做有潛在的安全性問題,符何程序只要知道該設(shè)備的名字就可以訪問它。第二個(gè)問題是你的應(yīng)用程序要訪問該設(shè) 備必須事先知道它的名字,否則不能訪問。這在測(cè)試用的設(shè)備或私有設(shè)備(只為你的應(yīng)用程序服務(wù)而不向第三方提供接口)的情況下是可以的。但是如果你的硬件設(shè) 備還要為第三方的程序服務(wù)或者有可能有第三方的公司為你的設(shè)備寫驅(qū)動(dòng)時(shí)就會(huì)有很多問題??赡苣銓?duì)該設(shè)備的命名會(huì)和其它的設(shè)備相重復(fù)。而且這樣的命名很依賴 程序員本身所使用的自然語(yǔ)言。
為了解決這個(gè)問題,微軟在設(shè)計(jì)WDM框架時(shí)引入了一個(gè)新的命名方案。該方案與任何自然語(yǔ)言無(wú)關(guān),且易于擴(kuò)展,可廣泛 地用于軟件用硬件,并且易于歸檔。該方案依靠一個(gè)設(shè)備接口的概念。它基本上是軟件如何訪問硬件的一個(gè)說(shuō)明。一個(gè)設(shè)備接口由一個(gè)唯一的128位的GUID標(biāo) 識(shí)。一般情況下我們可以使用GUIDGEN工具生成這個(gè)標(biāo)識(shí)(GUIDGEN工具可以在VC++企業(yè)版的可執(zhí)行程序目錄下找到)。由于采用了獨(dú)特的生成算 法,你永遠(yuǎn)也不用擔(dān)心重復(fù)出現(xiàn)GUID的情況。這樣一個(gè)GUID就唯一標(biāo)識(shí)了一種設(shè)備接口。
 
生成的代碼如下所示
  1. // {CAF53C68-A94C-11D2-BB4A-00C04FA330A6} 
  2. DEFINE_GUID(<<name>>,//將此處改成你的名字  
  3. 0xCAF53C68, 0xA94C, 0x11D2, 0xBB, 0x4A, 0x00, 0xC0, 0x4F, 0xA3, 0x30, 0xA6); 

此為GUIDGEN程序工作時(shí)的截屏??梢赃x擇四種格式輸出。一般情況下我們選擇第二種。并且為了便于管理,我們把要用于的GUID聲明集中放到一個(gè)頭文件中。

 

你可以把設(shè)備接口想象成鎖和鑰匙。這樣應(yīng)用程序就可以準(zhǔn)確地訪問需要訪問的設(shè)備。
我們可以在功能驅(qū)動(dòng)程序的AddDevice例程序中注冊(cè)一個(gè)或多個(gè)設(shè)備接口,程序如下:
  1. #include <initguid.h> 
  2. #include "guids.h" 
  3. //其它聲明 
  4. NTSTATUS AddDevice(...) 
  5. //其它代碼 
  6. IoRegisterDeviceInterface(pdo, &GUID_SIMPLE, NULL, &pdx->ifname); 
  7. //其它代碼 
其中的GUID_SIPMLE就是我們要注冊(cè)的接口的GUID的定義。對(duì)此段代碼,我們作如下說(shuō)明:
我 們包含了GUIDS.H頭文件,那里定義了DEFINE_GUID宏。DEFINE_GUID通常聲明一個(gè)外部變量。在驅(qū)動(dòng)程序的某些地方,我們不得不為 將要引用的每個(gè)GUID保留初始化的存儲(chǔ)空間。系統(tǒng)頭文件INITGUID.H利用某些預(yù)編譯指令使DEFINE_GUID宏在已經(jīng)定義的情況下仍能保留 該存儲(chǔ)空間。 
我使用單獨(dú)的頭文件來(lái)保存我要引用的GUID定義。這是一個(gè)好的想法,因?yàn)橛脩裟J降拇a也需要包含這些定義,但它們不需要那些僅與內(nèi)核模式驅(qū)動(dòng)程序有關(guān)的聲明。 
IoRegisterDeviceInterface 的第一個(gè)參數(shù)必須是設(shè)備PDO的地址。第二個(gè)參數(shù)指出與接口關(guān)聯(lián)的GUID,第三個(gè)參數(shù)指出額外的接口細(xì)分類名。只有Microsoft的代碼才使用名稱 細(xì)分類方案。第四個(gè)參數(shù)是一個(gè)UNICODE_STRING串的地址,該串用于接收設(shè)備對(duì)象的符號(hào)連接名。 
IoRegisterDeviceInterface 的返回值是一個(gè)Unicode 字符串,這樣可以在不知道驅(qū)動(dòng)程序的具體編碼的情況下(也就是說(shuō)沒看過你的驅(qū)動(dòng)程序的具體代碼),應(yīng)用程序可以確定并打開 該設(shè)備的句柄。這個(gè)返回值是很奇怪的,形如以下情形:\DosDevices\0000000000000007#{CAF53C68-A94C- 11d2-BB4A-00C04FA330A6}.
即它的名字是0000000000000007#{CAF53C68-A94C-11d2-BB4A-00C04FA330A6}
注冊(cè)過程實(shí)際上是先創(chuàng)建一個(gè)符號(hào)鏈接,然后把它記入注冊(cè)表。當(dāng)驅(qū)動(dòng)程序在響應(yīng)PnP請(qǐng)求IRP+MN_START_DEVICE時(shí),驅(qū)動(dòng)程序?qū)⒄{(diào)用IoSetDeviceInterfaceState函數(shù)”使能”該接口:
IoSetDeviceInterfaceState(&pdx->ifname, TRUE);
所謂使能也就是使此符號(hào)鏈接指向具體的PDO對(duì)象。
在 響應(yīng)這個(gè)調(diào)用過程中,I/O管理器將創(chuàng)建一個(gè)指向設(shè)備PDO的符號(hào)連接對(duì)象。以后,驅(qū)動(dòng)程序會(huì)執(zhí)行一個(gè)功能相反的調(diào)用禁止該接口(用FALSE做參數(shù)調(diào)用 IoSetDeviceInterfaceState)。最后,I/O管理器刪除符號(hào)連接對(duì)象,但它保留了注冊(cè)表項(xiàng),即這個(gè)名字將總與設(shè)備的這個(gè)實(shí)例關(guān) 聯(lián);但符號(hào)連接對(duì)象與硬件一同到來(lái)或消失。
枚舉設(shè)備接口
 內(nèi)核模式代碼和用戶模式代碼都能定位含有它們感興趣接口的設(shè)備。下面我將解釋如 何在用戶模式中枚舉所有含有特定接口的設(shè)備。枚舉代碼寫起來(lái)十分冗長(zhǎng),最后我不得不寫一個(gè)C++類來(lái)實(shí)現(xiàn)。你可以在DEVICELIST.CPP和 DEVICELIST.H文件中找到這些代碼。它們聲明并實(shí)現(xiàn)了一個(gè)CDeviceList類,該類包含一個(gè)CDeviceListEntry對(duì)象數(shù)組。
一些聲明代碼去掉了,詳細(xì)的文章請(qǐng)看驅(qū)動(dòng)開發(fā)網(wǎng)上的志寧專欄(http://www./column.php?sortid=3)。
所有實(shí)際的工作都發(fā)生在CDeviceList::Initialize函數(shù)中。其執(zhí)行過程大致是這樣:先枚舉所有接口GUID與構(gòu)造函數(shù)得到的GUID相同的設(shè)備,然后確定一個(gè)“友好”名,我們希望向最終用戶顯示這個(gè)名字。最后返回找到的設(shè)備號(hào)。下面是這個(gè)函數(shù)的代碼:
  1. int CDeviceList::Initialize()//枚舉設(shè)備的接口的具體實(shí)現(xiàn)代碼 
  2.   HDEVINFO info = SetupDiGetClassDevs(&m_guid, NULL, NULL
  3. , DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);     
  4.   if (info == INVALID_HANDLE_VALUE) 
  5.     return 0; 
  6.   SP_INTERFACE_DEVICE_DATA ifdata; 
  7.   ifdata.cbSize = sizeof(ifdata); 
  8.   DWORD devindex; 
  9.   for (devindex = 0; SetupDiEnumDeviceInterfaces(info
  10. , NULL, &m_guid, devindex, &ifdata); ++devindex)     
  11.   { 
  12.     DWORD needed; 
  13.     SetupDiGetDeviceInterfaceDetail(info, &ifdata, NULL, 0, &needed, NULL);                 
  14.  
  15.     PSP_INTERFACE_DEVICE_DETAIL_DATA detail =
  16.  (PSP_INTERFACE_DEVICE_DETAIL_DATA) malloc(needed);  
  17.     detail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA); 
  18.     SP_DEVINFO_DATA did = {sizeof(SP_DEVINFO_DATA)}; 
  19.     SetupDiGetDeviceInterfaceDetail(info, &ifdata, detail, needed, NULL, &did)); 
  20.     TCHAR fname[256];                                             
  21.     if (!SetupDiGetDeviceRegistryProperty(info, 
  22. &did, 
  23. SPDRP_FRIENDLYNAME,
  24. NULL, 
  25. (PBYTE) fname, 
  26. izeof(fname), 
  27. NULL) 
  28.       && !SetupDiGetDeviceRegistryProperty(info, 
  29. &did, 
  30. SPDRP_DEVICEDESC, 
  31. NULL, 
  32. (PBYTE) fname, 
  33. sizeof(fname), 
  34. NULL) 
  35.     ) 
  36.       _tcsncpy(fname, detail->DevicePath, 256); 
  37.     CDeviceListEntry e(detail->DevicePath, fname);                             
  38.     free((PVOID) detail); 
  39.     m_list.Add(e); 
  40.   } 
  41.   SetupDiDestroyDeviceInfoList(info); 
  42.   return m_list.GetSize(); 
該語(yǔ)句打開一個(gè)枚舉句柄,我們用它尋找包含了指定GUID接口的所有設(shè)備。 
循環(huán)調(diào)用SetupDiEnumDeviceInterfaces以尋找每個(gè)與此相匹配的設(shè)備。 
有兩項(xiàng)信息是我們需要的,接口的“細(xì)節(jié)”信息和設(shè)備實(shí)例信息。這個(gè)“細(xì)節(jié)”信息就是設(shè)備的符號(hào)名。因?yàn)樗拈L(zhǎng)度可變,所以我們兩次調(diào)用了SetupDiGetDeviceInterfaceDetail。第一次調(diào)用確定了長(zhǎng)度,第二次調(diào)用獲得了名字。 
通過詢問注冊(cè)表中的FriendlyName鍵或DeviceDesc鍵,我們獲得了設(shè)備的“友好”名稱。 
我們用設(shè)備符號(hào)名同時(shí)作為連接名和友好名創(chuàng)建了類CDeviceListEntry的一個(gè)臨時(shí)實(shí)例e。 
友好名 
你可能會(huì)疑惑,注冊(cè)表怎么會(huì)有設(shè)備的FriendlyName名。安裝設(shè)備驅(qū)動(dòng)程序的INF文件中有一個(gè)指定設(shè)備參數(shù)的段,這些參數(shù)將被添加到注冊(cè)表中。通常我們可以在這里為設(shè)備提供一個(gè)FriendlyName名。
注:在windows 2k下和Windows 98下的inf文件有少許的不同,即使是同一個(gè)設(shè)備的inf文件,也要作過適當(dāng)修改后才能同時(shí)用于兩個(gè)平臺(tái)下。
作好以上工作后,我們還要初始化一些其它的數(shù)據(jù)結(jié)構(gòu)才能完成設(shè)備加載工作。
在AddDevice中還需要加入其它一些步驟來(lái)初始化設(shè)備對(duì)象,下面我將按順序描述這些步驟。
設(shè)備擴(kuò)展的內(nèi)容和管理全部由用戶決定。該結(jié)構(gòu)中的數(shù)據(jù)成員應(yīng)直接反映硬件的專有細(xì)節(jié)以及對(duì)設(shè)備的編程方式。大多數(shù)驅(qū)動(dòng)程序都會(huì)在這里放入一些數(shù)據(jù)項(xiàng),下面代碼聲明了一個(gè)設(shè)備擴(kuò)展結(jié)構(gòu):
  1. typedef struct _DEVICE_EXTENSION { 
  2.   PDEVICE_OBJECT DeviceObject; 
  3.   PDEVICE_OBJECT LowerDeviceObject; 
  4.   PDEVICE_OBJECT Pdo; 
  5.   UNICODE_STRING ifname; 
  6.   IO_REMOVE_LOCK RemoveLock; 
  7.   DEVSTATE devstate; 
  8.   DEVSTATE prevstate; 
  9.   DEVICE_POWER_STATE devpower; 
  10.   SYSTEM_POWER_STATE syspower; 
  11.   DEVICE_CAPABILITIES devcaps; 
  12.  ...//其它一些結(jié)構(gòu) 
  13. } DEVICE_EXTENSION, *PDEVICE_EXTENSION; 
我模仿DDK中官方的結(jié)構(gòu)聲明模式聲明了這個(gè)結(jié)構(gòu)。 
我 們可以用設(shè)備對(duì)象中的DeviceExtension指針定位自己的設(shè)備擴(kuò)展。同樣,我們有時(shí)也需要在給定設(shè)備擴(kuò)展時(shí)能定位設(shè)備對(duì)象。因?yàn)槟承┖瘮?shù)的邏輯 參數(shù)就是設(shè)備擴(kuò)展本身(這里有設(shè)備每個(gè)實(shí)例的全部信息)。所以,我認(rèn)為這里應(yīng)該有一個(gè)DeviceObject指針。 
我在一些地方曾提到過,在調(diào)用IoAttachDeviceToDeviceStack函數(shù)時(shí),應(yīng)該把緊接著你下面的設(shè)備對(duì)象的地址保存起來(lái)。LowerDeviceObject成員用于保存這個(gè)地址。 
有一些服務(wù)例程需要PDO的地址,而不是堆棧中某個(gè)高層設(shè)備對(duì)象的地址。由于定位PDO非常困難,所以最好的辦法是在AddDevice執(zhí)行時(shí)在設(shè)備擴(kuò)展中保存一個(gè)PDO地址。 
無(wú)論你用什么方法(符號(hào)連接或設(shè)備接口)命名你的設(shè)備,都希望能容易地獲得這個(gè)名字。所以,這里我用一個(gè)Unicode串成員ifname來(lái)保存設(shè)備接口名。如果你使用一個(gè)符號(hào)連接名而不是設(shè)備接口,應(yīng)該使用一個(gè)有相關(guān)含義的成員名,例如“l(fā)inkname”。 
當(dāng)你調(diào)用IoDeleteDevice刪除這個(gè)設(shè)備對(duì)象時(shí),需要使用一個(gè)自旋鎖來(lái)解決同步安全問題,我將在第六章中討論同步問題。因此,需要在設(shè)備擴(kuò)展中分配一個(gè)IO_REMOVE_LOCK對(duì)象。AddDevice有責(zé)任初始化這個(gè)對(duì)象。 
你可能需要一個(gè)成員來(lái)記錄設(shè)備當(dāng)前的PnP狀態(tài)和電源狀態(tài)。DEVSTATE和POWERSTATE是枚舉類型變量,我假設(shè)事先已經(jīng)在頭文件中聲明了這些變量類型。我將在后面章節(jié)中討論這些狀態(tài)變量的用途。 
電源管理的另一個(gè)部分涉及電源能力設(shè)置的恢復(fù),設(shè)備擴(kuò)展中的devcaps結(jié)構(gòu)用于保存這些設(shè)置。 
下面是AddDevice中的初始化語(yǔ)句(著重設(shè)備擴(kuò)展部分的初始化):
  1. NTSTATUS AddDevice(...) 
  2.   PDEVICE_OBJECT fdo; 
  3.   IoCreateDevice(..., sizeof(DEVICE_EXTENSION), ..., &fdo); 
  4.   PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension; 
  5.   pdx->DeviceObject = fdo; 
  6.   pdx->Pdo = pdo; 
  7.   IoInitializeRemoveLock(&pdx->RemoveLock, ...); 
  8.   pdx->devstate = STOPPED; 
  9.   pdx->devpower = PowerDeviceD0; 
  10.   pdx->syspower = PowerSystemWorking; 
  11.   IoRegisterDeviceInterface(..., &pdx->ifname); 
  12.   pdx->LowerDeviceObject = IoAttachDeviceToDeviceStack(...); 
初始化默認(rèn)的DPC對(duì)象
許 多設(shè)備使用中斷來(lái)完成操作。中斷服務(wù)例程(ISR)不能調(diào)用用于報(bào)告IRP完成的函數(shù)(IoCompleteRequest)。利用DPC(延遲過程調(diào) 用)可以避開這個(gè)限制。你的設(shè)備對(duì)象中應(yīng)包含一個(gè)輔助DPC對(duì)象,它可以調(diào)度你的DPC例程,該對(duì)象應(yīng)該在設(shè)備對(duì)象創(chuàng)建后不久被初始化。DPC例程還有其 它的作用,比如你需要在中斷中處理很多很耗時(shí)的操作,這在通常情況下是不可以的,這樣會(huì)影響操作系統(tǒng)的響應(yīng)速度,為此我們把這些處理操作移到DPC例程中去。
NTSTATUS AddDevice(...)
{
  IoCreateDevice(...);
  IoInitializeDpcRequest(fdo, DpcForIsr);
}
設(shè)置緩沖區(qū)對(duì)齊掩碼
執(zhí) 行DMA傳輸?shù)脑O(shè)備直接使用內(nèi)存中的數(shù)據(jù)緩沖區(qū)工作。HAL(硬件抽象層)要求DMA傳輸中使用的緩沖區(qū)必須按某個(gè)特定界限對(duì)齊,而且設(shè)備也可能有更嚴(yán)格 的對(duì)齊需求。設(shè)備對(duì)象中的AlignmentRequirement域表達(dá)了這個(gè)約束,它是一個(gè)位掩碼,等于要求的地址邊界減一。下面語(yǔ)句可以把任何地址 圈入這個(gè)界限:
PVOID address = ...;
SIZE_T ar = fdo->AlignmentRequirement;
address = (PVOID) ((SIZE_T) address & ~ar);
還可以把任意地址圈入下一個(gè)對(duì)齊邊界:
PVOID address = ...;
SIZE_T ar = fdo->AlignmentRequirement;
address = (PVOID) (((SIZE_T) address + ar) & ~ar);
在這兩段代碼中,我使用了SIZE_T把指針類型(它可以是32位也可以是64位,這取決于編譯的目標(biāo)平臺(tái))轉(zhuǎn)化成一個(gè)整型,該整型與原指針有同樣的跨度范圍。
IoCreateDevice 把新設(shè)備對(duì)象中的AlignmentRequirement域設(shè)置成HAL要求的值。例如,Intel的x86芯片沒有對(duì)齊需求,所以 AlignmentRequirement的默認(rèn)值為0。如果設(shè)備需要更嚴(yán)格的緩沖區(qū)對(duì)齊(例如設(shè)備有總線主控的DMA能力,要求對(duì)齊數(shù)據(jù)緩沖區(qū)),應(yīng)該 修改這個(gè)默認(rèn)值,如下:
if (MYDEVICE_ALIGNMENT - 1 > fdo->AlignmentRequirement)
  fdo->AlignmentRequirement = MYDEVICE_ALIGNMENT - 1;
我假設(shè)你在驅(qū)動(dòng)程序某處已定義了一個(gè)名為MYDEVICE_ALIGNMENT的常量,它是2的冪,代表設(shè)備的數(shù)據(jù)緩沖區(qū)對(duì)齊需求。
其它對(duì)象
設(shè)備可能還有其它一些需要在AddDevice中初始化的對(duì)象。這些對(duì)象可能包括各種同步對(duì)象,各種隊(duì)列頭(queue anchors),聚集/分散列表緩沖區(qū),等等。
初始化設(shè)備標(biāo)志
設(shè) 備對(duì)象中有兩個(gè)標(biāo)志位需要在AddDevice中初始化,并且它們?cè)谝院笠膊粫?huì)改變,它們是DO_BUFFERED_IO和DO_DIRECT_IO標(biāo) 志。你只能設(shè)置并使用其中一個(gè)標(biāo)志,它將決定你以何種方式處理來(lái)自用戶模式的內(nèi)存緩沖區(qū)。(我將在第七章中討論這兩種緩沖模式的不同,以及你如何選 擇) 由于任何在后面裝入的上層過濾器驅(qū)動(dòng)程序?qū)?fù)制你的標(biāo)志設(shè)置,所以在AddDevice中做這個(gè)選擇十分重要。如果你在過濾器驅(qū)動(dòng)程序裝入后改變了 設(shè)置,它們可能會(huì)不知道。
設(shè)備對(duì)象中有兩個(gè)標(biāo)志位屬于電源管理范疇。與前兩個(gè)緩沖區(qū)標(biāo)志不同,這兩個(gè)標(biāo)志在任何時(shí)間都可以被改變。我將在第八章中 詳細(xì)討論它們,但這里我先介紹一下。DO_POWER_PAGABLE意味著電源管理器將在PASSIVE_LEVEL級(jí)上向你發(fā)送 IRP_MJ_POWER請(qǐng)求。DO_POWER_INRUSH意味著你的設(shè)備在上電時(shí)將汲取大量電流,因此,電源管理器將確保沒有其它INRUSH設(shè)備 同時(shí)上電。
設(shè)置初始電源狀態(tài)
大部分設(shè)備一開始就進(jìn)入全供電狀態(tài)。如果你知道你的設(shè)備的初始電源狀態(tài),應(yīng)該告訴電源管理器:
POWER_STATE state;
state.DeviceState = PowerDeviceD0;
PoSetPowerState(fdo, DevicePowerState, state)
建立設(shè)備堆
每個(gè)過濾器驅(qū)動(dòng)程序和功能驅(qū)動(dòng)程序都有責(zé)任把自己的設(shè)備對(duì)象放到設(shè)備堆棧上,從PDO開始一直向上。你可以調(diào)用IoAttachDeviceToDeviceStack完成你那部分工作:
NTSTATUS AddDevice(..., PDEVICE_OBJECT pdo)
{
  PDEVICE_OBJECT fdo;
  IoCreateDevice(..., &fdo);
  pdx->LowerDeviceObject = IoAttachDeviceToDeviceStack(fdo, pdo);
}
IoAttachDeviceToDeviceStack 的第一個(gè)參數(shù)是新創(chuàng)建的設(shè)備對(duì)象的地址。第二個(gè)參數(shù)是PDO地址。AddDevice的第二個(gè)參數(shù)也是這個(gè)地址。返回值是緊接著你下面的任何設(shè)備對(duì)象的地 址,它可以是PDO,也可以是其它低級(jí)過濾器設(shè)備對(duì)象。如果該函數(shù)失敗則返回一個(gè)NULL指針,因此你的AddDevice函數(shù)也是失敗的,應(yīng)返回 STATUS_DEVICE_REMOVED。
在AddDevice中最后一件需要做的事是清除設(shè)備對(duì)象中的DO_DEVICE_INITIALIZING標(biāo)志:
fdo->Flags &= ~DO_DEVICE_INITIALIZING;
當(dāng) 這個(gè)標(biāo)志設(shè)置時(shí),I/O管理器將拒絕任何打開該設(shè)備句柄的請(qǐng)求或向該設(shè)備對(duì)象上附著其它設(shè)備對(duì)象的請(qǐng)求。在驅(qū)動(dòng)程序完成初始化后,必須清除這個(gè)標(biāo)志。在以 前版本的Windows NT中,大部分驅(qū)動(dòng)程序在DriverEntry中創(chuàng)建所有需要的設(shè)備對(duì)象。當(dāng)DriverEntry返回時(shí),I/O管理器自動(dòng) 遍歷設(shè)備對(duì)象列表并清除該標(biāo)志。但在WDM驅(qū)動(dòng)程序中,設(shè)備對(duì)象在DriverEntry返回后才創(chuàng)建,所以I/O管理器不會(huì)自動(dòng)清除這個(gè)標(biāo)志,驅(qū)動(dòng)程序 必須自己清除它。
這是本節(jié)的內(nèi)容。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    99久久精品午夜一区二| 开心五月激情综合婷婷色| 男人大臿蕉香蕉大视频| 色综合伊人天天综合网中文| 国语对白刺激高潮在线视频| 福利在线午夜绝顶三级| 97人妻精品一区二区三区男同 | 中文字幕人妻综合一区二区| 熟女乱一区二区三区四区| 国产成人一区二区三区久久| 91插插插外国一区二区| 国产精品午夜一区二区三区| 日韩精品一区二区不卡| 一个人的久久精彩视频| 中文字幕一区二区三区大片| 欧美大黄片在线免费观看| 午夜精品黄片在线播放| 精品丝袜一区二区三区性色| 69老司机精品视频在线观看| 国产午夜福利在线免费观看| 中文字幕精品一区二区年下载| 黄片免费在线观看日韩| 富婆又大又白又丰满又紧又硬| 91偷拍与自偷拍精品| 国产亚洲欧美另类久久久| 国产不卡的视频在线观看| 亚洲男人的天堂久久a| 国产级别精品一区二区视频| 日本加勒比在线观看不卡| 色播五月激情五月婷婷| 欧美大粗爽一区二区三区 | 欧美日韩在线视频一区| 亚洲做性视频在线播放| 日韩人妻少妇一区二区| 国产乱人伦精品一区二区三区四区 | 欧美91精品国产自产| 精品一区二区三区乱码中文| 日本高清不卡在线一区| 国产日产欧美精品视频| 久久经典一区二区三区| 日韩精品视频一二三区|