如果前面的編譯過程沒有錯誤的話,現(xiàn)在我們應(yīng)該已經(jīng)得到了一個HelloWDM.sys文件,假設(shè)它是放在D:\HelloWDM\objfre\i386中。
安裝WDM驅(qū)動程序可以用兩種方法,一種是利用注冊表,還有一種是利用INF文件。我們一般是采用INF文件(這是微軟推薦的)。INF文件可以在 WINNT\INF 目錄中找到很多。為了順利安裝,我在這里先給出 HelloWDM 所需要的 HelloWDM.INF 文件:
;; The Win2K DDK documentation contains an excellent INF reference. ;--------- Version Section --------------------------------------------------- [Version] Signature="$CHICAGO$" Provider=LC_Device DriverVer=8/21/2002,3.0.0.3 ; If device fits one of the standard classes, use the name and GUID here, ; otherwise create your own device class and GUID as this example shows. Class=Unknown ClassGUID={ff646f80-8def-11d2-9449-00105a075f6b} ;--------- SourceDiskNames and SourceDiskFiles Section ----------------------- ; These sections identify source disks and files for installation. They are ; shown here as an example, but commented out. [SourceDisksNames] 1 = "HelloWDM",Disk1,, [SourceDisksFiles] HelloWDM.sys = 1,objfre\i386, ;--------- ClassInstall/ClassInstall32 Section ------------------------------- ; Not necessary if using a standard class ; 9X Style [ClassInstall] Addreg=Class_AddReg ; NT Style [ClassInstall32] Addreg=Class_AddReg [Class_AddReg] HKR,,,,%DeviceClassName% HKR,,Icon,,"-5" ;--------- DestinationDirs Section ------------------------------------------- [DestinationDirs] YouMark_Files_Driver = 10,System32\Drivers ;--------- Manufacturer and Models Sections ---------------------------------- [Manufacturer] %MfgName%=Mfg0 [Mfg0] ; PCI hardware Ids use the form ; PCI\VEN_aaaa&DEV_bbbb&SUBSYS_cccccccc&REV_dd ;改成你自己的ID %DeviceDesc%=YouMark_DDI, PCI\VEN_9999&DEV_9999 ;---------- DDInstall Sections ----------------------------------------------- ; --------- Windows 9X ----------------- ; Experimentation has shown that DDInstall root names greater than 19 characters ; cause problems in Windows 98 [YouMark_DDI] CopyFiles=YouMark_Files_Driver AddReg=YouMark_9X_AddReg [YouMark_9X_AddReg] HKR,,DevLoader,,*ntkern HKR,,NTMPDriver,,HelloWDM.sys HKR, "Parameters", "BreakOnEntry", 0x00010001, 0 ; --------- Windows NT ----------------- [YouMark_DDI.NT] CopyFiles=YouMark_Files_Driver AddReg=YouMark_NT_AddReg [YouMark_DDI.NT.Services] Addservice = HelloWDM, 0x00000002, YouMark_AddService [YouMark_AddService] DisplayName = %SvcDesc% ServiceType = 1 ; SERVICE_KERNEL_DRIVER StartType = 3 ; SERVICE_DEMAND_START ErrorControl = 1 ; SERVICE_ERROR_NORMAL ServiceBinary = %10%\System32\Drivers\HelloWDM.sys [YouMark_NT_AddReg] HKLM, "System\CurrentControlSet\Services\HelloWDM\Parameters",\ "BreakOnEntry", 0x00010001, 0 ; --------- Files (common) ------------- [YouMark_Files_Driver] HelloWDM.sys ;--------- Strings Section --------------------------------------------------- [Strings] ProviderName="Flying L Co.,Ltd." MfgName="LC Soft" DeviceDesc="Hello World WDM!" DeviceClassName="LC_Device" SvcDesc="???" |
注意它可以同時在Win98或者Win2000中使用(系統(tǒng)會通過這個INF文件里面的字段名稱,自動選擇適合當前系統(tǒng)的安裝方法的)。關(guān)于INF文件的各個字段含義現(xiàn)在我也不知道,所以也沒有辦法說清楚,如果誰看到這篇文章,而又知道的話,不妨為我一份。
準備好這個 HelloWDM.INF 文件后,讓我們打開控制面板,雙擊“添加/刪除硬件”,選擇“添加/排除設(shè)備故障”->“添加新設(shè)備”->“否,我想從列表選擇硬件”->“其它設(shè)備”->“從磁盤安裝”,選擇 HelloWDM.INF 所在的路徑,然后安裝。
當安裝完成后,系統(tǒng)就會添加上你寫好的驅(qū)動程序了。(可以在“設(shè)備管理器”中查看到)。然后重啟電腦,這個驅(qū)動程序就投入使用啦。
關(guān)于安裝,我也只知道這么多,到底安裝驅(qū)動程序時,操作系統(tǒng)都作了些什么,我也不是很清楚,等我弄明白了我再貼上。
DDK分為98 DDK和2000 DDK兩種,它們工作起來是大同小異的,不過有些驅(qū)動程序只能在2000 DDK中使用。由于Win98注定是一種即將被淘汰的操作系統(tǒng)了,所以我學(xué)習的時候也沒有過多的關(guān)注,我用的是2000的DDK,所以以下的所有內(nèi)容都是針對2000 DDK的。
·準備工作
1、確定你已經(jīng)安裝了Visual C++
2、安裝2000 DDK
3、安裝2000 DDK成功后,在“開始”->“程序”里應(yīng)該有“Development Kits”->“Windows 2000 DDK”的項目。
(注意一定要先安裝好VC,然后才安裝DDK,這個順序決不能顛倒!!)
4、保證DDKROOT環(huán)境變量設(shè)置為Windows 2000 DDK的基目錄,如果不是的話,請在控制面板“系統(tǒng)”屬性的“高級”標簽環(huán)境變量編輯器中設(shè)置好這個環(huán)境變量。
·編寫必需的文件
編譯WDM程序的時候,有兩個文件是必須要有的,它們是:
1、makefile
(這個是什么啊?你可能會問。)對于比較年輕的程序員來說,有可能沒有見過這個文件吧。其實在VC這些IDE出現(xiàn)之前,我們都必須使用makefile來確定項目中哪些文件需要重新編譯,現(xiàn)在的IDE都把這個工作自動做好了
我們要做的工作很簡單,就是提供這樣一個文件,它的內(nèi)容是:
# # DO NOT EDIT THIS FILE!!! Edit .\sources. If you want to add a new source # file to this component. This file merely indirects to the real make file # that is shared by all the driver components of the Windows NT DDK # !INCLUDE $(NTMAKEENV)\makefile.def |
TARGETNAME=HelloWDM //編譯出來的驅(qū)動程序的名稱 TARGETTYPE=DRIVER //編譯的類型是驅(qū)動程序編譯 DRIVERTYPE=WDM //驅(qū)動程序的類型是WDM驅(qū)動程序 TARGETPATH=OBJ //生成的文件存放在OBJ目錄中 INCLUDES=$(BASEDIR)\inc;\ //這是需要引入的頭文件 $(BASEDIR)\inc\ddk;\ TARGETLIBS=$(BASEDIR)\lib\*\free\usbd.lib\ //這是需要引入的庫文件 SOURCES=HelloWDM.cpp\ //這是源碼文件 |
New or updated MSVC detected. Updating DDK environment…. Setting environment for using Microsoft Visual C++ tools. Starting dirs creation…Completed. D:\NTDDK>cd\HelloWDM (回車) D:\HelloWDM>build (回車) |
首先祭出Delphi里附帶的tdump.exe程序。讓我們鍵入:
C:\WINNT\System32\Drivers>tdump ccport.sys -em -ee
參數(shù)-em是列出Import table,-ee是列出Export table。回車之后,屏幕列出一大堆東西:
C:\WINNT\SYSTEM32\DRIVERS>tdump ccport.sys -em -ee Turbo Dump Version 5.0.16.12 Copyright ? 1988, 2000 Inprise Corporation Display of File CCPORT.SYS IMPORT: NTOSKRNL.EXE={hint:011Fh}.’memcpy’ IMPORT: NTOSKRNL.EXE={hint:003Dh}.’IoDeleteDevice’ IMPORT: NTOSKRNL.EXE={hint:0030h}.’IoAttachDeviceToDeviceStack’ IMPORT: NTOSKRNL.EXE={hint:008Eh}.’KeSetEvent’ IMPORT: NTOSKRNL.EXE={hint:0068h}.’IofCallDriver’ IMPORT: NTOSKRNL.EXE={hint:0095h}.’KeWaitForSingleObject’ IMPORT: NTOSKRNL.EXE={hint:0074h}.’KeInitializeEvent’ IMPORT: NTOSKRNL.EXE={hint:003Fh}.’IoDetachDevice’ IMPORT: NTOSKRNL.EXE={hint:00D3h}.’RtlFreeUnicodeString’ IMPORT: NTOSKRNL.EXE={hint:0077h}.’KeInitializeSpinLock’ IMPORT: NTOSKRNL.EXE={hint:0129h}.’strcpy’ IMPORT: NTOSKRNL.EXE={hint:0121h}.’memset’ IMPORT: NTOSKRNL.EXE={hint:003Ch}.’IoCreateUnprotectedSymbolicLink’ IMPORT: NTOSKRNL.EXE={hint:0038h}.’IoCreateDevice’ IMPORT: NTOSKRNL.EXE={hint:00C2h}.’RtlAnsiStringToUnicodeString’ IMPORT: NTOSKRNL.EXE={hint:0069h}.’IofCompleteRequest’ IMPORT: NTOSKRNL.EXE={hint:0124h}.’sprintf’ IMPORT: NTOSKRNL.EXE={hint:003Eh}.’IoDeleteSymbolicLink’ IMPORT: NTOSKRNL.EXE={hint:0042h}.’IoFreeIrp’ IMPORT: NTOSKRNL.EXE={hint:004Dh}.’IoInitializeIrp’ IMPORT: NTOSKRNL.EXE={hint:002Dh}.’IoAllocateIrp’ IMPORT: NTOSKRNL.EXE={hint:0027h}.’InterlockedExchange’ IMPORT: NTOSKRNL.EXE={hint:0025h}.’InterlockedCompareExchange’ IMPORT: NTOSKRNL.EXE={hint:0035h}.’IoCancelIrp’ IMPORT: NTOSKRNL.EXE={hint:012Ah}.’strlen’ IMPORT: NTOSKRNL.EXE={hint:0126h}.’strcat’ IMPORT: NTOSKRNL.EXE={hint:0114h}.’atoi’ IMPORT: NTOSKRNL.EXE={hint:0128h}.’strcmp’ IMPORT: NTOSKRNL.EXE={hint:0034h}.’IoBuildSynchronousFsdRequest’ IMPORT: NTOSKRNL.EXE={hint:00D5h}.’RtlInitAnsiString’ IMPORT: HAL.DLL={hint:0006h}.’KfAcquireSpinLock’ IMPORT: HAL.DLL={hint:0009h}.’KfReleaseSpinLock’ EXPORT ord:0001=’Vcomm_DriverControl’ |
看到了嗎?它主要調(diào)用了NTOSKRNL.EXE和HAL.DLL文件(實際上你會發(fā)現(xiàn),幾乎所有的WDM驅(qū)動程序都會調(diào)用NTOSKRNL.EXE文件,從它的名字你可以看出為什么了吧?),并且輸出了一個函數(shù)“Vcomm_DriverControl”。這表明,其實.sys跟.exe文件一樣,都是一種PE文件來的。不同的是,.sys文件Import的通常是NTOSKRNL.EXE,而.exe文件Import的通常是KERNEL32.DLL和USER32.DLL。
知道了這些有什么用呢?實際上,由于.sys通常不調(diào)用KERNEL32.DLL和USER32.DLL,所以你是不能在設(shè)備驅(qū)動程序里面調(diào)用任何C、C++和Win32函數(shù)的,而且也不能用C++關(guān)鍵字new和delete等(可以用malloc和free來代替),而必須使用大量的內(nèi)核函數(shù)。另外,你應(yīng)該也能看到她調(diào)用了像IoDeleteDevice、IoAttachDeviceToDeviceStack等等函數(shù),這些你以前可能沒有見過的函數(shù)都是些內(nèi)核函數(shù)。為了讀者的方便,下面我列出一些常見的驅(qū)動程序可用的內(nèi)核函數(shù):
Ex… 執(zhí)行支持 Hal… 硬件抽象層(僅NT/Windows 2000) Io… I/O管理器(包括即插即用函數(shù)) Ke… 內(nèi)核 Ks… 內(nèi)核流IRP管理函數(shù) Mm… 內(nèi)存管理器 Ob… 對象管理器 Po… 電源管理 Ps… 進程結(jié)構(gòu) Rtl… 運行時庫 Se… 安全引用監(jiān)視 Zw… 其他函數(shù) |
最后讓我們再來看看,寫設(shè)備驅(qū)動程序時必須注意的一些問題:
1、內(nèi)核宏
如果查看DDK頭文件,會發(fā)現(xiàn)有幾個內(nèi)核函數(shù)是以宏的方式實現(xiàn)的。這種宏中有幾個宏的定義是相當糟糕的。例如,我們看到RemoveHeadList的定義如下:
#define RemoveHeadList(ListHead) (ListHead)->Flink; {RemoveEntryList((ListHead)->Flink)} |
如果以以下方式調(diào)用RemoveHeadList,則將編譯錯誤的代碼:
if(SomethingInList) Entry = RemoveHeadList(list); |
使這個調(diào)用安全的唯一方法是使用花括號:
if(SomethingInList) { Entry = RemoveHeadList(list); } |
所以我們切勿為了貪圖一時的方便,而使用不太規(guī)范的寫法,最好是在所有的if、for和while等語句中使用花括號。
2、驅(qū)動程序函數(shù)名稱
跟C/C++的main()函數(shù)一樣,設(shè)備驅(qū)動程序也有一個必須存在,而且只能以DriverEntry()為名稱的入口函數(shù)。然而,除此之外,我們可以使用任何名字來給其他函數(shù)命名——只要你自己記得就行了,當然,最好符合某些特定的規(guī)范啦,例如匈牙利命名法……
3、安裝時的問題
·在Windows98中驅(qū)動程序可執(zhí)行文件必須是8.3文件名。(別問我為什么,我也不知道,我只能建議你去問比爾該死)
·如果INF文件中含有非法節(jié)的詳細資料,Windows將不使用這個INF文件。
本節(jié)羅羅嗦嗦講了一大堆,跟實際的編程卻并沒有太大的關(guān)系,前傳嘛!就是這樣的啦!
看了好多天的書!特別到書店買了《Windows 200/xp wdm 設(shè)備驅(qū)動開發(fā)》這本書,在這里我不想怎么評論它!對于高手來說,我覺得她一定不能滿足,但是對于像我這樣想入門的人來說,仿佛看了半天,還是不知道從何下手。什么原理、模型、分層等等講不講,講!絕對應(yīng)該講!但是你得快點告訴我怎么先弄一個像“Hello Word!”的什么簡單來不能再簡單的完整的例子給我呀!到網(wǎng)上找阿找啊!那些高手啊!也不為我們新手寫點圖文并茂的上手資料。沒辦法!結(jié)合自己的需要再參考一些別人的東東,算是自己的一點不成熟的想法吧!
我覺得下面這個介紹非常不錯!我能看懂,所以貼了出來。
我道為什么找不到“Hello Word!”呢?原來在驅(qū)動開發(fā)的例子里是沒有所謂的“Hello World”程序的。這主要還是因為網(wǎng)絡(luò)上的WDM資料太少造成的。但是程序的入口點呢?c語言有Main(),用Vc的常看見的是WinMain(),Delphi開發(fā)的是Program里的Begin,但是驅(qū)動開發(fā)呢?那也是應(yīng)該有程序的入口點啊。后來我才明白了,那就是DriverEntry()函數(shù)。還有一個問題讓我懷疑了老半天,那就是驅(qū)動開發(fā)的源程序中需不需要include頭文件呀?為什么會懷疑呢?那是因為我看了半天的書都沒有看到一個完整的驅(qū)動程序結(jié)構(gòu)。真的是郁悶。下面是我看到的一個完整的結(jié)構(gòu),我先放上來,讓大家看看驅(qū)動開發(fā)的結(jié)構(gòu)吧。
/***************************************************************
程序名稱:Hello World for WDM
文件名稱:HelloWDM.cpp
日期:2002-8-16
***************************************************************/
//一定要的頭文件,聲明了函數(shù)模塊和變量:
#include "HelloWDM.h"
/***************************************************************
函數(shù)名稱:DriverEntry()
功能描述:WDM程序入口(原來的WinMain被換成了DriverEntry,也是驅(qū)動程序的大門)
***************************************************************/
//extern "C"是必須的,表示“用C鏈接”。如果你的文件名是HelloWDM.c的話,這句可以省略。
extern "C"
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, //IN 是一個關(guān)鍵字表示這是一個輸 入?yún)?shù),PDRIVER_OBJECT是一個數(shù)據(jù)結(jié)構(gòu)的指針,就像PCHAR一樣,這個數(shù)據(jù)結(jié)構(gòu)是什么樣子的,后面我會列出來。她描述了一個驅(qū)動設(shè)備對象。
IN PUNICODE_STRING RegistryPath)//參數(shù)RegistryPath指定了驅(qū)動程序注冊表健的路徑,因為驅(qū)動程序安裝后總會在系統(tǒng)注冊表里留下一點東西的。
{
//指定“添加設(shè)備”消息由函數(shù)“HelloWDMAddDevice()”來處理:
DriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;
//指定“即插即用”消息由函數(shù)“HelloWDMPnp()”來處理:
DriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;
//返回一個NTSTATUS值STATUS_SUCCESS。幾乎所有的驅(qū)動程序例程都必須返回一個NTSTATUS值,這些值在NTSTATUS.H DDK頭文件中有詳細的定義。
return STATUS_SUCCESS;
}
//NTSTATUS也是一個數(shù)據(jù)類型,上面我所說的消息有點不準確的,準確地說是“I/O請求包”,不過如果像我們以前理解消息那樣來理解也無不可,我覺得兩者太想了。無非就是上層的應(yīng)用程序通過它來告訴驅(qū)動程序,你要給我什么服務(wù)吧!IRP_MJ_PNP就是即插即用處理的請求。你發(fā)沒發(fā)覺上面其實是在制造進入各個房間的“小門”
/***************************************************************
函數(shù)名稱:HelloWDMAddDevice()
功能描述:處理“添加設(shè)備”消息
***************************************************************/
NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject)
{
//定義一個NTSTATUS類型的返回值:
NTSTATUS status;
//定義一個功能設(shè)備對象(Functional Device Object):
PDEVICE_OBJECT fdo;
//創(chuàng)建我們的功能設(shè)備對象,并儲存到fdo中:
status = IoCreateDevice(
DriverObject, //驅(qū)動程序?qū)ο?/FONT>
sizeof(DEVICE_EXTENSION), //要求的設(shè)備擴展的大小
NULL, //設(shè)備名稱,這里為NULL
FILE_DEVICE_UNKNOWN, //設(shè)備的類型,在標準頭文件WDM.H或NTDDK.H中列出的FILE_DEVICE_xxx值之一
0, //各種常量用OR組合在一起,指示可刪除介質(zhì)、只讀等。
FALSE, //如果一次只有一個線程可以訪問該設(shè)備,為TRUE,否則為FALSE
&fdo); //返回的設(shè)備對象
//NT_SUCCESS宏用于測試IoCreateDevice內(nèi)核是否成功完成。不要忘記檢查對內(nèi)核的所有調(diào)用是否成功。NT_ERROR宏不等同于!NT_SUCCESS,最好使用!NT_SUCCESS,因為除了錯誤外,它還截獲警告信息。
if( !NT_SUCCESS(status))
return status;
//創(chuàng)建一個設(shè)備擴展對象dx,用于存儲指向fdo的指針:
PDEVICE_EXTENSION dx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
dx->fdo = fdo;
//用IoAttachDeviceToDeviceStack函數(shù)把HelloWDM設(shè)備掛接到設(shè)備棧:
dx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);
//設(shè)置fdo的flags。有兩個“位”是必須改變的,一個是必須清除DO_DEVICE_INITIALIZING標志,如果在DriverEntry例程中調(diào)用IoCreateDevice(),就不需要清除這個標志位。還有一個是必須設(shè)置DO_BUFFER_IO標志位:
fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
fdo->Flags &= ~DO_DEVICE_INITIALIZING;
//返回值:
return STATUS_SUCCESS;
}
/***************************************************************
函數(shù)名稱:HelloWDMPnp()
功能描述:處理“即插即用”消息
***************************************************************/
NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo,
IN PIRP Irp)
{
//創(chuàng)建一個設(shè)備擴展對象dx,用于存儲指向fdo的指針:
PDEVICE_EXTENSION dx=(PDEVICE_EXTENSION)fdo->DeviceExtension;
//首先要通過函數(shù)IoGetCurrentIrpStackLocation()得到當前的IRP,并由此得到Minor Function:
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
ULONG MinorFunction = IrpStack->MinorFunction;
//然后把這個Minor Function傳遞給下一個設(shè)備棧:
IoSkipCurrentIrpStackLocation(Irp);
NTSTATUS status = IoCallDriver( dx->NextStackDevice, Irp);
//處理“即插即用”次功能代碼:
//當Minor Function等于IRP_MN_REMOVE_DEVICE時,說明有設(shè)備被拔出或卸下,這時要取消資源分配并刪除設(shè)備:
if( MinorFunction==IRP_MN_REMOVE_DEVICE)
{
//取消設(shè)備接口:
IoSetDeviceInterfaceState(&dx->ifSymLinkName, FALSE);
RtlFreeUnicodeString(&dx->ifSymLinkName);
//調(diào)用IoDetachDevice()把fdo從設(shè)備棧中脫開:
if (dx->NextStackDevice)
IoDetachDevice(dx->NextStackDevice);
//刪除fdo:
IoDeleteDevice(fdo);
}
//返回值:
return status;
}
/***************************************************************
程序名稱:Hello World for WDM
文件名稱:HelloWDM.h
作者:羅聰
日期:2002-8-16
***************************************************************/
//頭文件,只是聲明一些函數(shù)和變量,比較簡單就不多說了,請讀者自行研究:
#ifdef __cplusplus
extern "C"
{
#endif
#include "ntddk.h"
#ifdef __cplusplus
}
#endif
typedef struct _DEVICE_EXTENSION
{
PDEVICE_OBJECT fdo;
PDEVICE_OBJECT NextStackDevice;
UNICODE_STRING ifSymLinkName;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject);
NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo,
IN PIRP Irp);
好了,第一個WDM版的“Hello World”就介紹到這里,雖然實際上它什么都沒有做,但是由于它包含了完整的框架,所以對于向我這樣的新手來說還是很有參考價值的。至于怎么編譯及安裝只有下次再說了
第一:安裝win2000操作系統(tǒng),我安裝是win2000高級服務(wù)器版本。
第二:安裝Vc++6.0,我裝的是英文版。
第三:安裝win2000DDK;
通常驅(qū)動程序的調(diào)試都是用ddk在cmd中完成的。這部分我暫時略過。下面先介紹如何設(shè)置vc++6.0在Visual Studio 6.0集成環(huán)境中開發(fā)設(shè)備驅(qū)動程序的方法。
在Windows上,Windows DDK提供的開發(fā)環(huán)境是基于命令行的,操作起來極為不便,而Visual Studio 6.0給我們提供了非常友好易用的集成環(huán)境,讓我們有如虎添翼之感。
那么,能否利用Visual Studio的集成環(huán)境來開發(fā)驅(qū)動程序呢?答案是可以的。通過對Visual Studio集成環(huán)境的簡單設(shè)置,創(chuàng)建好自己的驅(qū)動開發(fā)集成環(huán)境就可以了。
首先要求系統(tǒng)已安裝DDK和Visual C++6.0(安裝時選上所有工具),
1、接下來需要改造ddk\bin\setenv.bat 把要求mstools的有關(guān)語句注釋掉(若想在命令行環(huán)境開發(fā)驅(qū)動則還需加入call VC_DIR\VC98\Bin\Vcvars32. bat),以便能在命令行使用vc的相關(guān)工具;若只想在IDE環(huán)境開發(fā)就不必調(diào)用Vcvars32.bat,因為相關(guān)工具的路徑信息可以在vc環(huán)境中設(shè)置.)
2、創(chuàng)建一個目錄DriverEnv(目錄名隨意),作為你開發(fā)驅(qū)動的大本營
3、在該目錄下創(chuàng)建一個批處理文件MakeDrvr.bat,內(nèi)容如下:
@echo off
if "%1"=="" goto usage
if "%3"=="" goto usage
if not exist %1\bin\setenv.bat goto usage
call %1\bin\setenv %1 %4
%2
cd %3
build -b -w %5 %6 %7 %8 %9
goto exit
:usage
echo usage MakeDrvr DDK_dir Driver_Drive Driver_Dir free/checked [build_options]echo eg MakeDrvr %%DDKROOT%% C: %%WDMBOOK%% free -cef
:exit
該批處理首先對傳遞的參數(shù)作一些檢查,然后調(diào)用ddk的setenv命令設(shè)置環(huán)境變量,然后改變目錄為源程序所在驅(qū)動器和目錄,并最后調(diào)用build,-b保證顯示完全的錯誤信息,-w保證在屏幕上輸出警告,在vc ide里的output窗口中可以看到這些錯誤和警告。
4.建立一個空白工程
選File的new菜單項,然后選project欄的makefile,然后輸入路徑,一路next下去即可,visual studio提供兩種配置win32 debug和win32 release.
5.修改這兩種配置
選project的settings菜單項win32 debug:
在Build Command Line一欄填入MakeDrvr DDK_DIR SOURCE_DRIVE SOURCE_DIR checked [build options]
在Rebuild all options一欄填入 -nmake /a
在output file一欄填入與sources文件中的TARGETNAME相同的文件名
在Browse info file name一欄填入obj\i386\checked\(與TARGETNAME相同的文件名,見下述).bsc
win32 release:
在Build Command Line一欄填入MakeDrvr DDK_DIR SOURCE_DRIVE SOURCE_DIR free [build options]
在Rebuild all options一欄填入 -nmake /a
在output file一欄填入與sources文件中的TARGETNAME相同的文件名
在Browse info file name一欄填入obj\i386\free\(與TARGETNAME相同的文件名).bsc
注:DDK_DIR一般可以寫成%BASEDIR%,build options一般為-cef即已足夠
6.添加源文件到工程
可以新建,也可以添加,這和普通的win32開發(fā)一樣。
7.添加資源文件
選INSERT的RESOURCE菜單項即可
8.把文件makefile放入源程序目錄,其內(nèi)容總是
#
# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
# file to this component. This file merely indirects to the real make file
# that is shared by all the driver components of the Windows NT DDK
#
!INCLUDE $(NTMAKEENV)\makefile.def
9.把文件Sources放入源程序目錄,內(nèi)容為
TARGETNAME=RamDrive//這是要生成的驅(qū)動程序.sys文件的名字
TARGETPATH=obj //.sys文件所在目錄的上層目錄,(由于ddk的bug)應(yīng)手工在obj目錄下創(chuàng)建checked和free目錄,以作為.sys的最終存放目錄
TARGETTYPE=DRIVER //驅(qū)動程序的類型,一般不變
INCLUDES=$(BASEDIR)\inc //ddk包含文件路徑,一般不變
SOURCES=RamDrive.cpp RamDrive.rc //源文件(不要頭文件),資源文件
BROWSER_INFO = 1 //若想要瀏覽信息,則要有本行;否則可無
10.因為MakeDrvr.bat在DriverEnv目錄,所以應(yīng)把該目錄添加到vc的Executable files里面
選tools的options菜單項,然后選directories頁,在show directories for一欄選擇Executable files,然后添加即可.
至此,環(huán)境設(shè)置完畢,你可以按F7, build你的驅(qū)動程序了。
用戶模式與內(nèi)核模式
從Intel80386開始,出于安全性和穩(wěn)定性的考慮,該系列的CPU可以運行于ring0~ring3從高到低四個不同的權(quán)限級,對數(shù)據(jù)也提供相應(yīng)的四個保護級別。運行于較低級別的代碼不能隨意調(diào)用高級別的代碼和訪問較高級別的數(shù)據(jù),而且也只有運行在ring0層的代碼可以直接對物理硬件進行訪問。由于WindowsNT是一個支持多平臺的操作系統(tǒng),為了與其他平臺兼容,它只利用了CPU的兩個運行級別。一個被稱為內(nèi)核模式,對應(yīng)80x86的ring0層,是操作系統(tǒng)的核心部分,設(shè)備驅(qū)動程序就是運行在該模式下;另一個被稱為用戶模式,對應(yīng)80x86的ring3層,操作系統(tǒng)的用戶接口部分(就是我們通常所說的win32 API)以及所有的用戶應(yīng)用程序都運行在該級別。操作系統(tǒng)對運行在內(nèi)核模式下的代碼是不設(shè)防的,所以不管是建設(shè)還是破壞內(nèi)核模式下的編程都是值得去研究的。
圖1-WIN2000系統(tǒng)的分層結(jié)構(gòu)
在物理硬件與系統(tǒng)核心之間有一個硬件抽象層(HardwareAbstractionLayer),它屏蔽了不同平臺硬件的差異,向操作系統(tǒng)的上層提供了一套統(tǒng)一的接口。從圖中我們還可以看到,設(shè)備驅(qū)動程序(DeviceDriver)是被I/O管理器(I/OManager)包圍起來的,即驅(qū)動程序與操作系統(tǒng)上層的通信全部都要通過I/O管理器。這給驅(qū)動程序的編寫帶來了很大的便利,因為很多諸如接收用戶的請求、與用戶程序交換數(shù)據(jù)、內(nèi)存映射、掛接中斷、同步等等麻煩的工作都由I/O管理器代勞了。
驅(qū)動程序的分類
驅(qū)動程序并不像所有人想的那樣一定要和硬件打交道,我粗略的把他分為兩類:硬驅(qū)動和軟驅(qū)動。硬驅(qū)動就是對硬件直接編程進行控制,這類驅(qū)動通常必須遵守硬件的通信協(xié)議,直接對硬件進行端口訪問、中斷響應(yīng)、DMA傳輸。它包括:串、并行口,鍵盤,文件系統(tǒng),SCSI,網(wǎng)絡(luò)等驅(qū)動程序;另外一種軟驅(qū)動呢?不需要直接對硬件就行操作。我認為他可以理解為它是在硬驅(qū)動之上的一層更為高級的驅(qū)動。我想學(xué)習的主要是軟驅(qū)動。
一般來說,設(shè)備驅(qū)動程序的任務(wù)主要有兩個:第一,接受來自用戶程序的讀寫請求,把用戶的數(shù)據(jù)傳送給設(shè)備,或把從設(shè)備接收到的數(shù)據(jù)傳送給用戶;第二,輪詢設(shè)備或處理來自設(shè)備的中斷請求,完成數(shù)據(jù)傳輸。
驅(qū)動程序的結(jié)構(gòu)
在這里,我主要介紹WDM的結(jié)構(gòu)。WDM(Windows driver module)是什么東西呢?在Windows98\95下面,也許你聽得最多的是VXD,我只知道VXD是一種驅(qū)動程序,和WDM差不多的東西。只是因為Windows2000是WindowsNT那條線過來的東西,要加上兩個主要的新功能:即插即用(Plug and Play)和電源管理(Power Menage),又不能用Windows98\95那一套,所以就搞出一個叫WDM這么個東西,來支持PNP和PM.。其實想想,現(xiàn)在的技術(shù)名詞還不是一般的多啊!總之wdm大家都叫它windows驅(qū)動程序模型。
Windows2000里有叫即插即用管理器和I\O(此I\O非彼I\O端口)管理器的兩個東西。比如說我在機器上插了一張符合PCI規(guī)范的PCI卡。即插即用管理器會發(fā)現(xiàn)這張卡插在第N個插槽上,然后即插即用管理器會說它找到了這樣一張卡,它就去找有沒有現(xiàn)成的驅(qū)動程序,如果沒有找到,它會告訴我們,我找到了這樣一張卡,請你插入這張卡的驅(qū)動程序盤。好,我們就把驅(qū)動程序盤給它,即插即用管理器會去找驅(qū)動程序盤上的INF文件,找到后它會比較PCI卡上的標志和INF文件里的標志是否相同,如果相同,它就會依照INF文件里提供的路徑去找驅(qū)動程序,找到之后就可以交給I\O管理器,I\O管理器會裝載這個驅(qū)動程序。I\O管理器在做了一些接口的工作后,即插即用管理器會先分配好相關(guān)的資源給PCI卡,比如說I\O端口空間、內(nèi)存空間和中斷向量,然后告訴這張卡的驅(qū)動程序,我給你分配了這些資源,你看怎么的。如果你沒有怎么的或不敢怎么的,那就趕快記下這些資源,以備后用。
下面說I\O管理器這個東西。上面我們講到I\O管理器裝載這個驅(qū)動程序,驅(qū)動程序有一個大門,還有N多的小門。I\O管理器先從大門進去(因為I\O管理器只找得到大門,I\O管理器是不是很傻,NO,當然有它的道理,你別問我:I\O管理器怎么找到大門的?驅(qū)動程序無非就是一些文件,I\O管理器把這么些文件加載到系統(tǒng)中去),去找一樣?xùn)|西:進小門的地圖。我們要在大門進去的房間里放這張地圖(驅(qū)動程序都是我們造的,我們當然有驅(qū)動程序的地圖啦)。I\O管理器找到了地圖,就可以自由進出大小門了。———這些大小門說白了就是函數(shù)(不要問我函數(shù)是什么東東),小門的地圖就是函數(shù)的地址。I\O管理器知道了這些函數(shù)的地址,當然就可以調(diào)用這些函數(shù)啦。還有一個叫IRP的東西,中文名叫I\O請求包。我們這樣來理解它:在用戶的應(yīng)用程序這一端,要和驅(qū)動程序?qū)υ挘鼈冎g不是簡單的調(diào)用函數(shù)(至于為什么,我現(xiàn)在也不知道),應(yīng)用程序和驅(qū)動程序之間有I\O管理器隔著,應(yīng)用程序?qū)︱?qū)動程序的操作,首先由I\O管理器處理成一個包,這個包里面有應(yīng)用程序請求的操作內(nèi)容、傳送的數(shù)據(jù)等等一些東西,然后I\O管理器把這個包扔給驅(qū)動程序,驅(qū)動程序依照包里的請求,完成操作,把該回傳的數(shù)據(jù)放進包里,再把包扔還給I\O管理器,I\O管理器再把數(shù)據(jù)返回給應(yīng)用程序。——這里所說的包,就是IRP。
這里說的只是WDM結(jié)構(gòu)的一部分,但是有了這一部分知識,其它部分就不難懂了。通過上面的介紹你看見了什么。你可以想象得出驅(qū)動程序是什么樣子的嗎?我是這樣想的。驅(qū)動程序好像就是一個函數(shù)庫,只不過在大門的地方放了一張地圖。而這個大門與地圖我們見到過嗎?好像有點像dll文件呢。在早些時候我學(xué)dll的時候,我就只會用dll來存放函數(shù)。