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

分享

手把手跟我學(xué)驅(qū)動(dòng)(2)

 創(chuàng)科之龍 2010-11-20

時(shí)間過(guò)得很快,離我的上一篇文章已經(jīng)又過(guò)了一個(gè)星期,本想隔上三兩天就開(kāi)始著手寫(xiě)第二篇的,無(wú)奈很忙,也有很多東西要去學(xué)習(xí)。一個(gè)星期沒(méi)寫(xiě)驅(qū)動(dòng)了,不過(guò)對(duì)自己上個(gè)星期學(xué)習(xí)的過(guò)程到現(xiàn)在的記憶還很清晰,在上一篇文章里面,我提供了一個(gè)最基本最基本的驅(qū)動(dòng)程序框架,無(wú)非就是一個(gè)“main”函數(shù)加上一些附屬例程,事實(shí)上,就算是我們只有一個(gè)DriverEntry函數(shù)也是可行的,只是如果這樣的話(huà),你對(duì)它的所有請(qǐng)求(或者說(shuō)是調(diào)用)都會(huì)失敗,事實(shí)上,上個(gè)驅(qū)動(dòng)本來(lái)就是什么也沒(méi)做!上一篇文章另外一個(gè)內(nèi)容就是,總結(jié)了使用WDK(DDK)與VS2008開(kāi)發(fā)驅(qū)動(dòng)的基本設(shè)置,想當(dāng)初我的第一個(gè)驅(qū)動(dòng)框架竟然會(huì)連接了kernel32.dll,這是一件多么可笑的事情,顯然這樣的“驅(qū)動(dòng)”是絕對(duì)不可能被正確加載的,所以,如果哪位新手再遇到我這樣的問(wèn)題(這個(gè)時(shí)候,你連DriverEntry都進(jìn)不去),請(qǐng)你檢查一下你鏈接得到的目標(biāo)文件使用了哪些輸入庫(kù)(姑且這么說(shuō)吧),目前為止,我的驅(qū)動(dòng)可能會(huì)用到ntoskrnl.exe和HAL.dll所提供的服務(wù)。

是啊,上個(gè)驅(qū)動(dòng)什么也不干,可以說(shuō)是一無(wú)是處。但是,據(jù)我最初聽(tīng)說(shuō)的驅(qū)動(dòng),它主要就是提供一個(gè)PC機(jī)上的加入的新硬件的功能調(diào)用的模塊,可以說(shuō)是主要為應(yīng)用程序提供基本的輸入輸出服務(wù),比如說(shuō)是一個(gè)打印機(jī)驅(qū)動(dòng),我現(xiàn)在在用WPS,那么我選擇打印,打印機(jī)的驅(qū)動(dòng)程序就負(fù)責(zé)把我WPS上的字傳給打印機(jī)輸出。我的概念最初也就僅限于這么多,再后來(lái),我知道了文件系統(tǒng)驅(qū)動(dòng),等等,我所知有限就不在這兒貽笑大方了,再申明一點(diǎn),我寫(xiě)這一系列的文章只是要把我學(xué)習(xí)的經(jīng)驗(yàn)?zāi)贸鰜?lái)和那些和我一樣初學(xué)的同志們交流交流,如此而已。好了,開(kāi)始今天的內(nèi)容吧。

1. IRP

首先我們得認(rèn)識(shí)的一個(gè)東西就是IRP,中文說(shuō)法是IO請(qǐng)求包,它的英文全稱(chēng)可能是I/O Request Package吧。一個(gè)說(shuō)法是,MS的驅(qū)動(dòng)框架是一個(gè)分層的驅(qū)動(dòng)模型,也就是說(shuō),當(dāng)一個(gè)用戶(hù)層的應(yīng)用程序發(fā)出一個(gè)IO請(qǐng)求的時(shí)候,由I/O管理器(這是一個(gè)內(nèi)核基本組件,在《NT文件系統(tǒng)內(nèi)幕》這本書(shū)里面有講,只是我感覺(jué)不怎么好理解,所以我也沒(méi)理解好,我只需要知道它給我們提供這些服務(wù)即可)將這個(gè)I/O請(qǐng)求按一個(gè)規(guī)定的模式打個(gè)包,里面有一些必須的參數(shù),就是一個(gè)IRP。原則上,一個(gè)I/O請(qǐng)求,I/O管理器只會(huì)為我們創(chuàng)建一個(gè)IRP包。每個(gè)IRP包包括一個(gè)IRP頭部和一個(gè)IO_STACK_LOCATION數(shù)組,這個(gè)數(shù)組的元素個(gè)數(shù)由這個(gè)IRP包即將經(jīng)歷的所有驅(qū)動(dòng)層次來(lái)決定,每個(gè)數(shù)組元素對(duì)應(yīng)一個(gè)可能(只所以說(shuō)是可能,是因?yàn)?,某個(gè)驅(qū)動(dòng)會(huì)選擇完成一個(gè)IRP,從而這個(gè)IRP便不便會(huì)結(jié)束向下一個(gè)驅(qū)動(dòng)的傳遞)會(huì)收到這個(gè)IRP包的驅(qū)動(dòng),另一方面,這是一個(gè)由數(shù)組實(shí)現(xiàn)的一個(gè)棧,這個(gè)棧元素個(gè)數(shù)在IRP頭部有記錄。關(guān)于IRP我不想再多說(shuō),太多了我也說(shuō)不清楚。另外一個(gè)值得一提的是,當(dāng)一個(gè)IRP傳遞到一個(gè)驅(qū)動(dòng)層次的時(shí)候,具體它是發(fā)給誰(shuí)了呢?它是發(fā)給了這層驅(qū)動(dòng)程序創(chuàng)建的DEVICE_OBJECT上來(lái)了,這就是我們的驅(qū)動(dòng)的每一個(gè)IO例程的兩個(gè)參數(shù)一個(gè)是DEVICE_OBJECT另一個(gè)是IRP的原因了,IRP是IO管理器提供的,這個(gè)DEVICE_OBJECT是這個(gè)IRP的目標(biāo)設(shè)備(如果你的驅(qū)動(dòng)創(chuàng)建了多于一個(gè)設(shè)備的話(huà)),目前,我們的驅(qū)動(dòng)程序里面只是創(chuàng)建了一個(gè)虛擬的設(shè)備,所以,暫時(shí)不必考慮這個(gè)問(wèn)題,不過(guò),我相信可能大家看過(guò)別人的驅(qū)動(dòng)里面都有一個(gè)IS_MY_DEVICEOBJECT這么一個(gè)宏,顧名思義就是判斷是否是我們創(chuàng)建的設(shè)備對(duì)象的意思,加上也無(wú)防。

2.基本數(shù)據(jù)傳輸

 在今天,我想要說(shuō)明的基本數(shù)據(jù)傳輸是使用ReadFile和WriteFile來(lái)進(jìn)行用戶(hù)程序于內(nèi)核驅(qū)動(dòng)之間的最最基本的數(shù)據(jù)傳輸問(wèn)題。眾所周知,32位系統(tǒng)中的可用地址空間最多可達(dá)4GB,不過(guò),默認(rèn)情況下,只有2GB是供應(yīng)用程序使用,而另外的2GB是供操作系統(tǒng)內(nèi)核使用的。對(duì)于供應(yīng)用程序使用的2GB的地址空間,對(duì)不同的應(yīng)用程序來(lái)說(shuō),盡管有相同的虛擬地址值,但實(shí)際對(duì)應(yīng)的物理地址可以不同,具體內(nèi)容就更可以不同了。但是,供操作系統(tǒng)內(nèi)核使用的2GB內(nèi)容對(duì)所有的應(yīng)用程序來(lái)說(shuō)都是相同的,只是對(duì)每個(gè)應(yīng)用程序都是拒絕訪問(wèn)的。一般情況下,為了保護(hù)系統(tǒng)安全,內(nèi)核驅(qū)動(dòng)與應(yīng)用程序中間不能直接進(jìn)行數(shù)據(jù)傳輸,如果需要,只有IO管理器和虛擬內(nèi)存管理器的介入才行(可能是這樣)。那么,操作系統(tǒng)內(nèi)核為我們的驅(qū)動(dòng)程序與應(yīng)用程序提供了三種數(shù)據(jù)傳輸方式,①是直接IO;②是緩沖IO;③兩者都不IO(即非①非②)。由于Read和Write請(qǐng)求中數(shù)據(jù)的傳輸都是單方面的,所以,對(duì)于一種IO模式來(lái)說(shuō),數(shù)據(jù)傳輸?shù)妮d體是用相同的方式使用的。

直接IO:當(dāng)采用直接IO是使用一個(gè)系統(tǒng)內(nèi)核分配的一個(gè)MDL來(lái)完成的,所謂的MDL就是一個(gè)內(nèi)存描述符,它的細(xì)節(jié)我們可以參考相關(guān)資料,事實(shí)上我也解釋不清楚,但有一點(diǎn)我可以肯定的是,它可能是因?yàn)橹苯又赶驅(qū)?yīng)的物理內(nèi)存,所以相應(yīng)的IO類(lèi)型被稱(chēng)做直接IO。在驅(qū)動(dòng)里,我們可以使用MmGetSystemAddressForMdlSafe函數(shù)來(lái)返回一個(gè)和我們常用的線性地址一樣的地址來(lái)供我們使用,它實(shí)際上是一個(gè)使用了MmMapLockedPagesSpecifyCache宏,使用它鎖住了物理內(nèi)存從而使得在它有效期間不會(huì)被換出物理內(nèi)存從而保證了以后的訪問(wèn)不會(huì)引發(fā)缺頁(yè)異常而損失效率。而系統(tǒng)可用的像這樣的物理內(nèi)存是相當(dāng)有限的,所以對(duì)它的使用應(yīng)該采用保守的態(tài)度。而說(shuō)法說(shuō),一般情況下我們應(yīng)該避免使用直接IO,除非迫不得已。當(dāng)使用直接IO時(shí),在需要IO操作時(shí),IO管理器會(huì)分配一個(gè)相關(guān)的MDL放在IRP的MdlAddress成員中供我們的IRP例程使用。

緩沖IO:這種IO方式和名字很像哦,系統(tǒng)會(huì)分配一個(gè)與應(yīng)用程序傳入的地址不同的地址,當(dāng)寫(xiě)入(比如WriteFile)時(shí),系統(tǒng)自動(dòng)將用戶(hù)傳入的數(shù)據(jù)復(fù)制到這個(gè)緩沖區(qū)傳入IRP例程,當(dāng)是讀出(如ReadFile)時(shí),系統(tǒng)在這次IO操作完成并且在返回應(yīng)用程序之前,將這個(gè)緩沖區(qū)的數(shù)據(jù)復(fù)制到應(yīng)用程序的接收緩沖區(qū)中。這個(gè)緩沖區(qū)地址保存在IRP->AssociatedIrp->SystemBuffer成員中。

兩者都不IO:這是一種和以上兩種IO方式都不同的操作,IO管理器不會(huì)為我們創(chuàng)建任何緩沖區(qū)或MDL,而是,直接傳入應(yīng)用程序提供的接收或是寫(xiě)入地址。這里有點(diǎn)更應(yīng)該命名為直接IO,對(duì)不?但是,已經(jīng)這樣命名了,我們就應(yīng)該搞清楚它和真正的直接IO的區(qū)別,直接IO鎖定了實(shí)際的物理內(nèi)存而使之不會(huì)被換出物理內(nèi)存,而此處的兩者都不IO則沒(méi)有這個(gè)操作。這樣,這個(gè)用戶(hù)程序傳入的地址值保存在IRP結(jié)構(gòu)的UserBuffer域中。

這里所說(shuō)的IO方式僅限于IRP_MJ_READ和IRP_MJ_WRITE例程,并且,只能使用上面的三者之一。并且,這個(gè)IO方式是對(duì)應(yīng)于DEVICE_OBJECT的,很容易理解,每個(gè)IRP例程都只接收PDEVICE_OBJECT和PIRP兩個(gè)參數(shù),我們創(chuàng)建的設(shè)備使用哪一種IO方式在設(shè)備創(chuàng)建后初始化中指定,當(dāng)一個(gè)IRP包發(fā)送給設(shè)備時(shí),IO管理器會(huì)檢查對(duì)應(yīng)設(shè)備需要的IO方式而采用符合相應(yīng)IO方式的操作。這個(gè)IO方式在DEVICE_OBJECT的Flags域中指定。不管是讀寫(xiě),都有讀寫(xiě)的字節(jié)長(zhǎng)度,這個(gè)長(zhǎng)度保存在當(dāng)前對(duì)應(yīng)的驅(qū)動(dòng)棧中,在代碼中會(huì)有注釋。好了,不多說(shuō)了,我們來(lái)試一試上面的說(shuō)法(有一點(diǎn)小問(wèn)題,注意看清楚了)。

3. Example2驅(qū)動(dòng)實(shí)現(xiàn)

我們還是使用和上一例中相同的方式,我只是在(1)的基礎(chǔ)上做出擴(kuò)充,先復(fù)制相關(guān)的代碼吧。主框架代碼和Example1沒(méi)有太大區(qū)別,只是添加了我們這次將要加入的IRP_MJ_READ例程,另外,在我們的設(shè)備創(chuàng)建成功之后,還應(yīng)該為它設(shè)定READ和WRITE使用的IO方式,其它的都在主程序了.當(dāng)然,和Example1一樣,我沒(méi)有將這些代碼分開(kāi)在若干文件中,這是為了方便我往這里貼(嘿嘿),以后,代碼逐漸增多的時(shí)候,會(huì)分開(kāi)的。下面是代碼部分:

#include <wdm.h>

//因?yàn)橹恢С秩NIO方式,所以,下面兩個(gè)都不定義的話(huà),就是使用NEITHER_IO了

//#define EXAMPLE2_BUFFERED_IO

#define EXAMPLE2_DIRECT_IO

//定義IO方式的選取及相及的例程

#ifdef EXAMPLE2_DIRECT_IO

#define EXAMPLE2_IO_TYPE DO_DIRECT_IO

#define Example2Read Example2DirectRead

#elif defined(EXAMPLE2_BUFFERED_IO)

#define EXAMPLE2_IO_TYPE DO_BUFFERED_IO

#define Example2Read Example2BufferedRead

#else //defined(EXAPME2_NEITHER_IO)

#define EXAMPLE2_IO_TYPE 0

#define Example2Read Example2NeitherRead

#endif

NTSTATUS Example2DirectRead(PDEVICE_OBJECT pDevObj,PIRP pIrp);

NTSTATUS Example2BufferedRead(PDEVICE_OBJECT pDevObj,PIRP pIrp);

NTSTATUS Example2NeitherRead(PDEVICE_OBJECT pDevObj,PIRP pIrp);

//驅(qū)動(dòng)入口函數(shù)

NTSTATUS DriverEntry(PDRIVER_OBJECT pDrvObj,PUNICODE_STRING pUsRegPath)

{

 NTSTATUS status = STATUS_UNSUCCESSFUL;//初始化為不成功

 UNICODE_STRING usDevName;//我們的設(shè)備名

 UNICODE_STRING usDosDevName;//Dos符號(hào)鏈接

 PDEVICE_OBJECT pDevObj = NULL;//設(shè)備對(duì)象

 unsigned int nIndex;//一個(gè)計(jì)數(shù)器,循環(huán)的時(shí)候可以用到

 DbgPrint("Example2: Driver entry is called.\n");

 //初始化兩個(gè)Unicodestring

 RtlInitUnicodeString(&usDevName,L"\\Device\\Example2");

 RtlInitUnicodeString(&usDosDevName,L"\\DosDevices\\Example2");

 //現(xiàn)在我們創(chuàng)建設(shè)備

 status = IoCreateDevice(pDrvObj,0,&usDevName,FILE_DEVICE_UNKNOWN,

  FILE_DEVICE_SECURE_OPEN,FALSE,&pDevObj);

 //只有成功創(chuàng)建設(shè)備我們才有必要繼續(xù)

 if(NT_SUCCESS(status)){//測(cè)試成功與否一般用這個(gè)宏,而不是讓它直接與STATUS_SUCCESS比

//較,查看一下這個(gè)宏定義就知道了

  //成功創(chuàng)建設(shè)備之后,我們要作的一件事就是初使化驅(qū)動(dòng)的IRP例程,現(xiàn)在,我們的驅(qū)動(dòng)什么都不   //干,所以,每個(gè)例程也還是什么都不做

  //每個(gè)設(shè)備最多有IRP_MJ_MAXIMUM_FUNCTION個(gè)IRP例程,現(xiàn)在,我們都把它們初始//化成   //一個(gè)相同的入口

  for(nIndex=0;nIndex<IRP_MJ_MAXIMUM_FUNCTION;++nIndex)

   pDrvObj->MajorFunction[nIndex] = Example2IrpRoutine;

  //======現(xiàn)在我們已經(jīng)提供了實(shí)質(zhì)性的Read例程,所以,不使用默認(rèn)例程了

  pDrvObj->MajorFunction[IRP_MJ_READ] = Example2Read;

  //接下來(lái),我再安裝一個(gè)卸載例程

  pDrvObj->DriverUnload = Example2Unload;

  //把創(chuàng)建的設(shè)備保存起來(lái)吧,否則以后便不能引用啦

  pDrvObj->DeviceObject = pDevObj;

  //====下面這一行代碼是在Example1的基礎(chǔ)上添加的,指定我們的讀寫(xiě)例程使用的IO方式

  pDevObj->Flags |= EXAMPLE2_IO_TYPE;

  //好了,創(chuàng)建符號(hào)鏈接,

  status = IoCreateSymbolicLink(&usDosDevName,&usDevName);

  if(!NT_SUCCESS(status)){

   IoDeleteDevice(pDevObj);//刪除創(chuàng)建的設(shè)備對(duì)象

  }

 }

 return status;//

}

NTSTATUS Example2DirectRead(PDEVICE_OBJECT pDevObj,PIRP pIrp)

{

 NTSTATUS status = STATUS_UNSUCCESSFUL;

 PIO_STACK_LOCATION pIoStack = NULL;

 unsigned int nBytesRead = 0;

 charpUserBuffer = NULL;//用來(lái)

 charszFromDriver = "This is the string data from Example2 driver:DirectIO.";//用戶(hù)程序的讀請(qǐng)求將從此      //串中填充

 DbgPrint("This is calling of Example2 driver using directIo.\n");

 pIoStack = IoGetCurrentIrpStackLocation(pIrp);

 if(pIoStack!=NULL&&pIrp->MdlAddress!=NULL){//因?yàn)橹苯覫O使用系統(tǒng)提供的Mdl數(shù)據(jù),所以,必須           //檢查這個(gè)值,

  //如果對(duì)一個(gè)空的MdlAddress地址使用以下操作,而我們又沒(méi)采取其它防范措施,將會(huì)導(dǎo)致一次   //藍(lán)屏

  pUserBuffer = MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority);

  //如果以上操作成功執(zhí)行,pUserBuffer應(yīng)該是一個(gè)可用的線性虛擬地址

  if(pUserBuffer!=NULL){

  //現(xiàn)在是時(shí)候檢查用戶(hù)接收緩沖區(qū)長(zhǎng)度了

  nBytesRead = pIoStack->Parameters.Read.Length;//可見(jiàn),如果是寫(xiě)的話(huà),此處應(yīng)該是       //Parameters.Write.Length

  if(nBytesRead>56)//因?yàn)槲覀兊膬?nèi)核數(shù)據(jù)源只有56個(gè)字節(jié)數(shù)據(jù)

   nBytesRead = 56;

  //好了,可以復(fù)制了

  RtlCopyMemory(pUserBuffer,szFromDriver,nBytesRead);

  status = STATUS_SUCCESS;//表示操作成功完成

  }//end if pUserBuffer!=NULL

 }

 //好了,該返回給用戶(hù)程序一些相關(guān)的信息了,這些信息保存在IRP的IoStatus域中

 pIrp->IoStatus.Status = status;//操作成功還是失敗

 pIrp->IoStatus.Information = nBytesRead;//讀寫(xiě)操作完成長(zhǎng)度

 //現(xiàn)在,還應(yīng)該通知IO管理器,這個(gè)IRP已經(jīng)完成,不再繼續(xù)處理

 IoCompleteRequest(pIrp,IO_NO_INCREMENT);

 return status;

}

NTSTATUS Example2BufferedRead(PDEVICE_OBJECT pDevObj,PIRP pIrp)

{

 NTSTATUS status = STATUS_UNSUCCESSFUL;

 PIO_STACK_LOCATION pIoStack = NULL;

 unsigned int nBytesRead = 0;

 charpUserBuffer = NULL;//用來(lái)

 charszFromDriver = "This is the string data from Example2 driver:BufferedIo.";//用戶(hù)程序的讀請(qǐng)求將從      //此串中填充

 DbgPrint("This is calling of Example2 driver using BufferedIo.\n");

 pIoStack = IoGetCurrentIrpStackLocation(pIrp);

 pUserBuffer = pIrp->AssociatedIrp.SystemBuffer;//這是系統(tǒng)為我們準(zhǔn)備的緩沖區(qū),我們?cè)谒厦娌僮骷?/font>    //,.此IO完成后,系統(tǒng)會(huì)自動(dòng)將其中的數(shù)據(jù)復(fù)制給用戶(hù)程序

 if(pIoStack!=NULL&&pUserBuffer!=NULL){//我們必須確保不在空地址上操作,

  //現(xiàn)在是時(shí)候檢查用戶(hù)接收緩沖區(qū)長(zhǎng)度了

  nBytesRead = pIoStack->Parameters.Read.Length;//可見(jiàn),如果是寫(xiě)的話(huà),此處應(yīng)該是       //Parameters.Write.Length

  if(nBytesRead>58)//因?yàn)槲覀兊膬?nèi)核數(shù)據(jù)源只有個(gè)字節(jié)數(shù)據(jù)

   nBytesRead = 58;

   //好了,可以復(fù)制了

   RtlCopyMemory(pUserBuffer,szFromDriver,nBytesRead);

   status = STATUS_SUCCESS;//表示操作成功完成  

 }

 //好了,該返回給用戶(hù)程序一些相關(guān)的信息了,這些信息保存在IRP的IoStatus域中

 pIrp->IoStatus.Status = status;//操作成功還是失敗

 pIrp->IoStatus.Information = nBytesRead;//讀寫(xiě)操作完成長(zhǎng)度

 //現(xiàn)在,還應(yīng)該通知IO管理器,這個(gè)IRP已經(jīng)完成,不再繼續(xù)處理

 IoCompleteRequest(pIrp,IO_NO_INCREMENT);

 return status;

}

NTSTATUS Example2NeitherRead(PDEVICE_OBJECT pDevObj,PIRP pIrp)

{

 NTSTATUS status = STATUS_UNSUCCESSFUL;

 PIO_STACK_LOCATION pIoStack = NULL;

 unsigned int nBytesRead = 0;

 charpUserBuffer = NULL;//用來(lái)

 charszFromDriver = "This is the string data from Example2 driver:NeitherIo.";//用戶(hù)程序的讀請(qǐng)求將從      //此串中填充

 DbgPrint("This is calling of Example2 driver using NeitherIo.\n");

 pIoStack = IoGetCurrentIrpStackLocation(pIrp);

 pUserBuffer = pIrp->UserBuffer;//這是應(yīng)用程序提供的接收緩沖區(qū)

 __try{

  if(pIoStack!=NULL&&pUserBuffer!=NULL){//我們必須確保不在空地址上操作,

   //緩沖區(qū)長(zhǎng)度

   nBytesRead = pIoStack->Parameters.Read.Length;

   //因?yàn)檫@個(gè)pUserBuffer是用戶(hù)程序傳來(lái)的,我們得檢查它的讀寫(xiě)屬性,用ProbeForWrite檢查是   //否可寫(xiě),如果不可寫(xiě),將引發(fā)一個(gè)異常,注意,如果你使用用戶(hù)程序提供的地址讀寫(xiě),所以你一   //定得記住使用異常機(jī)制,否則,你志遭遇的將不是一個(gè)遇到問(wèn)題需要關(guān)閉的錯(cuò)誤,而是一個(gè)   //恐怖的藍(lán)屏!

   ProbeForWrite(pUserBuffer,nBytesRead,TYPE_ALIGNMENT(char));

   //現(xiàn)在是時(shí)候檢查用戶(hù)接收緩沖區(qū)長(zhǎng)度了

   if(nBytesRead>57)//因?yàn)槲覀兊膬?nèi)核數(shù)據(jù)源只有57個(gè)字節(jié)數(shù)據(jù)

    nBytesRead = 57;

   //好了,可以復(fù)制了

   RtlCopyMemory(pUserBuffer,szFromDriver,nBytesRead);

   status = STATUS_SUCCESS;//表示操作成功完成

  }

 }__except(EXCEPTION_EXECUTE_HANDLER){

  status = STATUS_UNSUCCESSFUL;

  DbgPrint("An exception occurred:Example2NeitherRead.\n");//打印異常發(fā)生這個(gè)事實(shí)

 }

 //好了,該返回給用戶(hù)程序一些相關(guān)的信息了,這些信息保存在IRP的IoStatus域中

 pIrp->IoStatus.Status = status;//操作成功還是失敗

 pIrp->IoStatus.Information = nBytesRead;//讀寫(xiě)操作完成長(zhǎng)度

 //現(xiàn)在,還應(yīng)該通知IO管理器,這個(gè)IRP已經(jīng)完成,不再繼續(xù)處理

 IoCompleteRequest(pIrp,IO_NO_INCREMENT);

 return status;

}

在上面的代碼中,和Example1幾乎完全相同的例程我就沒(méi)有重復(fù)了。OK,現(xiàn)在可以寫(xiě)用戶(hù)態(tài)的測(cè)試代碼了,下面貼出我的一個(gè)很簡(jiǎn)單的測(cè)試代碼,

int main(int argcchar *argv[])

{

 HANDLE hFile = INVALID_HANDLE_VALUE;

 DWORD dwRet = 0;

 hFileCreateFile(_T("\\\\.\\example2"),GENERIC_READ|GENERIC_WRITE,

FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);

 if(hFile!=INVALID_HANDLE_VALUE){

  char readData[128];

  DWORD dwReadBytes = 0;

  if(ReadFile(hFile,readData,128,&dwReadBytes,NULL)){

   readData[dwReadBytes] = '\0';

   printf("%s\n",readData);

  }

  CloseHandle(hFile);

 }

 return 0;

}

4. Example2驅(qū)動(dòng)調(diào)試

我們用一段簡(jiǎn)單的代碼,測(cè)試了Example2的IRP_MJ_READ例程的正確性。很好,如果代碼什么地方出現(xiàn)問(wèn)題,我們還需要調(diào)試,那,接下來(lái)再介紹一下我所使用的最基本的調(diào)試,我建議在虛擬機(jī)里面調(diào)試,這樣比較安全可靠,我使用的VMware,調(diào)試器肯定就用WinDbg了啊。至于驅(qū)動(dòng)環(huán)境的設(shè)置,我也不說(shuō)了,百度上隨便一搜就是一大把。這里,我要說(shuō)的是,我們生成的sys文件,用調(diào)試版,因?yàn)檫@樣WinDbg才能根據(jù)調(diào)試符號(hào)。當(dāng)在虛擬系統(tǒng)里面加載驅(qū)動(dòng)之后,就在WinDbg里面暫停虛擬系統(tǒng),使用!drvobj example2 2來(lái)查看這里的example2加載后的信息,現(xiàn)在要調(diào)試的是Read例程,所以,找到IRP_MJ_READ例程的地址,使用bp命令就在這個(gè)地方設(shè)下了斷點(diǎn),當(dāng)然,也可以用合函數(shù)名下斷,得靠你對(duì)WinDbg的學(xué)習(xí)了。更多的東西吧,還需要查看更多的文檔,我這里所介紹的,只是一些最基本最基本的東西。

5.實(shí)驗(yàn)

在我的代碼里面,我沒(méi)有加入IRP_MJ_WRITE的實(shí)現(xiàn),有興趣的自己實(shí)現(xiàn)一個(gè)試試吧!

6.總結(jié)

在本例中,我主要介紹了驅(qū)動(dòng)在Read和Write處理時(shí)采用的三種不同的IO方式,并給出了Read例程使用這三種方式的樣例代碼,不過(guò),我們還知道,用戶(hù)層的應(yīng)用程序不僅僅可以使用ReadFile和WriteFile同內(nèi)核驅(qū)動(dòng)進(jìn)行數(shù)據(jù)交換,并且這兩個(gè)函數(shù)的數(shù)據(jù)流向還局限于單方向的,如果用過(guò)DeviceIoControl我們會(huì)發(fā)現(xiàn),DevicdIoControl同樣也能與內(nèi)核驅(qū)動(dòng)進(jìn)行數(shù)據(jù)交換,更重要的是,它的數(shù)據(jù)流向還可以是雙向的!那么,在下一篇中,我們將學(xué)習(xí)DeviceIoControl的響應(yīng)實(shí)現(xiàn)?,F(xiàn)在,可以放松一下了。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(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)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多

    国产成人人人97超碰熟女| 国产精品欧美日韩中文字幕| 国产精品成人一区二区在线 | 欧美精品一区二区水蜜桃| 国产主播精品福利午夜二区| 国产毛片不卡视频在线| 麻豆蜜桃星空传媒在线观看 | 精品一区二区三区中文字幕| 亚洲一区二区三区精选| 日本午夜一本久久久综合| 欧美加勒比一区二区三区 | 久热人妻中文字幕一区二区| 我想看亚洲一级黄色录像| 午夜亚洲精品理论片在线观看| 亚洲精品成人福利在线| 五月婷婷六月丁香亚洲| 天堂av一区一区一区| 欧美熟妇喷浆一区二区| 一区二区三区国产日韩| 免费一级欧美大片免费看| 视频一区中文字幕日韩| 欧美亚洲国产日韩一区二区| 午夜视频成人在线观看| 亚洲一区二区三区四区| 亚洲av日韩av高潮无打码| 亚洲欧美精品伊人久久| 最近最新中文字幕免费| 国产欧美一区二区三区精品视| 国产精品伦一区二区三区四季| 黄片在线免费观看全集| 午夜精品国产一区在线观看| 精品精品国产自在久久高清| 激情五月激情婷婷丁香| 日本福利写真在线观看| 亚洲精品中文字幕欧美| 黄色激情视频中文字幕| 亚洲高清一区二区高清| 欧美精品日韩精品一区| 亚洲精品国产福利在线| 欧美一级黄片免费视频| 久热在线视频这里只有精品|