5、Windows驱动开发技术详解笔记(1) 入门基础驱动程序结构
NT式
1、Driver.h頭文件中包含了開發(fā)NT式驅(qū)動(dòng)所需要的NTDDK.h,此外還定義了幾個(gè)標(biāo)志來(lái)指明函數(shù)和變量分配在分頁(yè)內(nèi)存還是非分頁(yè)內(nèi)存中。Windows驅(qū)動(dòng)程序的入口函數(shù)是DriverEntry函數(shù)。WDM式的驅(qū)動(dòng)程序要導(dǎo)入的頭文件是WDM.h。
代碼
1 #ifdef __cplusplus
2
3 extern "C"
4
5 {
6
7 #endif
8
9 #include <NTDDK.h>
10
11 #ifdef __cplusplus
12
13 }
14
15 #endif
16
17 #define PAGEDCODE code_seg("PAGE")
18
19 #define LOCKEDCODE code_seg()
20
21 #define INITCODE code_seg("INIT")
22
23 #define PAGEDDATA data_seg("PAGE")
24
25 #define LOCKEDDATA data_seg()
26
27 #define INITDATA data_seg("INIT")
28
29 #define arraysize(p) (sizeof(p)/sizeof((p)[0]))
30
31 typedef struct _DEVICE_EXTENSION {
32
33 PDEVICE_OBJECT pDevice;
34
35 UNICODE_STRING ustrDeviceName; //設(shè)備名稱
36
37 UNICODE_STRING ustrSymLinkName; //符號(hào)鏈接名
38
39 } DEVICE_EXTENSION, *PDEVICE_EXTENSION;
40
41 // 函數(shù)聲明
42
43 NTSTATUS CreateDevice (IN PDRIVER_OBJECT pDriverObject);
44
45 VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject);
46
47 NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,
48
49 IN PIRP pIrp);
說(shuō)明:
1)采用C++編程,所以需要用extern "C",因?yàn)槲覀儗?dǎo)入的是C的函數(shù)的符號(hào)表。
2)在驅(qū)動(dòng)中用到的變量或函數(shù)都需要指定分配在分頁(yè)或非分頁(yè)內(nèi)存中,分頁(yè)內(nèi)存在物理內(nèi)存不夠的情況下可能會(huì)被交換出去,對(duì)于一些需要高IRQL的例程絕對(duì)不能被交換出頁(yè)面,因此它們必須被定義為非分頁(yè)內(nèi)存。
3)DriverEntry需要放在INIT標(biāo)志的內(nèi)存中。
2、驅(qū)動(dòng)程序的入口函數(shù)
代碼
1 /************************************************************************
2
3 * 函數(shù)名稱:DriverEntry
4
5 * 功能描述:初始化驅(qū)動(dòng)程序,定位和申請(qǐng)硬件資源,創(chuàng)建內(nèi)核對(duì)象
6
7 * 參數(shù)列表:
8
9 pDriverObject:從I/O管理器中傳進(jìn)來(lái)的驅(qū)動(dòng)對(duì)象
10
11 pRegistryPath:驅(qū)動(dòng)程序在注冊(cè)表的中的路徑
12
13 * 返回值:返回初始化驅(qū)動(dòng)狀態(tài)
14
15 *************************************************************************/
16
17 #pragma INITCODE
18
19 extern "C" NTSTATUS DriverEntry (
20
21 IN PDRIVER_OBJECT pDriverObject,
22
23 IN PUNICODE_STRING pRegistryPath )
24
25 {
26
27 NTSTATUS status;
28
29 KdPrint(("Enter DriverEntry\n"));
30
31 //注冊(cè)其他驅(qū)動(dòng)調(diào)用函數(shù)入口
32
33 pDriverObject->DriverUnload = HelloDDKUnload;
34
35 pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutine;
36
37 pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutine;
38
39 pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine;
40
41 pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKDispatchRoutine;
42
43 //創(chuàng)建驅(qū)動(dòng)設(shè)備對(duì)象
44
45 status = CreateDevice(pDriverObject);
46
47 KdPrint(("DriverEntry end\n"));
48
49 return status;
50
51 }
1)猶如控制臺(tái)程序需要main、Win32 程序需要WinMain、DLL 程序需要DllMain 一樣,驅(qū)動(dòng)程序也有自己的入口點(diǎn),即DriverEntry。DriverEntry 需要被加載到INIT 內(nèi)存區(qū)域中,這樣當(dāng)驅(qū)動(dòng)被卸載后它可以退出內(nèi)存。
2)DriverEntry 是由內(nèi)核中的I/O 管理器負(fù)責(zé)調(diào)用的,它有兩個(gè)參數(shù)DriverObject 和RegistryPath(當(dāng)然形參的名字可以自己改變)。其中DriverObject 是由I/O管理器傳遞進(jìn)來(lái)的驅(qū)動(dòng)對(duì)象,RegistryPath 則指向此驅(qū)動(dòng)負(fù)責(zé)的注冊(cè)表。
3)我們可以看到DriverEntry 首先是定義了一些變量,然后調(diào)用IoCreateDevice 創(chuàng)建設(shè)備對(duì)象,緊接著調(diào)用IoCreateSymbolicLink 創(chuàng)建符號(hào)鏈接。HelloDDKDispatchRoutine等是驅(qū)動(dòng)程序在向Windows 的I/O 管理器注冊(cè)一些回調(diào)函數(shù)。上面代碼的含義是:當(dāng)驅(qū)動(dòng)程序?qū)⒈恍遁d時(shí)自動(dòng)調(diào)用HelloDDKUnload例程;當(dāng)驅(qū)動(dòng)程序接收到 IRP_MJ_CREATE 時(shí)自動(dòng)調(diào)用HelloDDKDispatchRoutine。
現(xiàn)在可以把IRP_MJ_CREATE理解成類似ring3的“消息”,當(dāng)我們的驅(qū)動(dòng)程序接收到不同的IRP就表明發(fā)生了不同的事件,然后我們及時(shí)給予處理。
4)KdPrint是宏,用來(lái)輸出。類似于MFC中的TRACE。
5)#pragma INITCODE來(lái)指明此函數(shù)加載到INIT內(nèi)存函數(shù)中。
6)在驅(qū)動(dòng)對(duì)象DriverObject 中,有個(gè)函數(shù)指針數(shù)組MajorFunction,它里面的每一個(gè)元素都記錄著一個(gè)函數(shù)的地址對(duì)應(yīng)著相應(yīng)的IRP,我們可以通過(guò)簡(jiǎn)單地設(shè)置這個(gè)數(shù)組將IRP 與相應(yīng)的派遣函數(shù)關(guān)聯(lián)起來(lái)。諸如IRP_MJ_CREATE 其實(shí)是使用#define 定義的一個(gè)宏,比如IRP_MJ_CREATE 實(shí)際上就是0x00,而IRP_MJ_CLOSE 則是0x02 等。由于在進(jìn)入DriverEntry 之前,I/O 管理器會(huì)將_IopInvalidDeviceRequest 的地址填滿整個(gè)MajorFunction 數(shù)組,因此除了我們自行設(shè)置過(guò)的IRP 之外,其他的IRP 都與系統(tǒng)默認(rèn)的_IopInvalidDeviceRequest 函數(shù)關(guān)聯(lián)。
3、創(chuàng)建設(shè)備例程(函數(shù))
代碼
1 /************************************************************************
2
3 * 函數(shù)名稱:CreateDevice
4
5 * 功能描述:初始化設(shè)備對(duì)象
6
7 * 參數(shù)列表:
8
9 pDriverObject:從I/O管理器中傳進(jìn)來(lái)的驅(qū)動(dòng)對(duì)象
10
11 * 返回 值:返回初始化狀態(tài)
12
13 *************************************************************************/
14
15 #pragma INITCODE
16
17 NTSTATUS CreateDevice (
18
19 IN PDRIVER_OBJECT pDriverObject)
20
21 {
22
23 NTSTATUS status;
24
25 PDEVICE_OBJECT pDevObj;
26
27 PDEVICE_EXTENSION pDevExt;
28
29 //創(chuàng)建設(shè)備名稱
30
31 UNICODE_STRING devName;
32
33 RtlInitUnicodeString(&devName,L"\\Device\\MyDDKDevice");
34
35 //創(chuàng)建設(shè)備
36
37 status = IoCreateDevice( pDriverObject,
38
39 sizeof(DEVICE_EXTENSION),
40
41 &(UNICODE_STRING)devName,
42
43 FILE_DEVICE_UNKNOWN,
44
45 0, TRUE,
46
47 &pDevObj );
48
49 if (!NT_SUCCESS(status))
50
51 return status;
52
53 pDevObj->Flags |= DO_BUFFERED_IO;
54
55 pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
56
57 pDevExt->pDevice = pDevObj;
58
59 pDevExt->ustrDeviceName = devName;
60
61 //創(chuàng)建符號(hào)鏈接
62
63 UNICODE_STRING symLinkName;
64
65 RtlInitUnicodeString(&symLinkName,L"\\??\\HelloDDK");
66
67 pDevExt->ustrSymLinkName = symLinkName;
68
69 status = IoCreateSymbolicLink( &symLinkName,&devName );
70
71 if (!NT_SUCCESS(status))
72
73 {
74
75 IoDeleteDevice( pDevObj );
76
77 return status;
78
79 }
80
81 return STATUS_SUCCESS;
82
83 }
******
RtlInitUnicodeString
http://msdn.microsoft.com/en-us/library/ms648420%28VS.85%29.aspx
IoCreateDevice
http://msdn.microsoft.com/en-us/library/ff548397%28VS.85%29.aspx
1),前面我們創(chuàng)建的設(shè)備對(duì)象雖然有個(gè)參數(shù)指定了設(shè)備名稱,但是這個(gè)設(shè)備名稱只能在內(nèi)核態(tài)可見,也就說(shuō)ring3 的應(yīng)用層程序是看不見它的,因此驅(qū)動(dòng)程序需要向ring3 公布一個(gè)符號(hào)鏈接,這個(gè)鏈接指向真正的設(shè)備名稱,而ring3 的應(yīng)用程序可以通過(guò)該符號(hào)鏈接找到驅(qū)動(dòng)程序進(jìn)行通信。實(shí)際上我們經(jīng)常所說(shuō)的C 盤、D 盤就是一個(gè)符號(hào)鏈接,它們?cè)趦?nèi)核中的真正設(shè)備對(duì)象是“\Device\HarddiskVolume1”和“\Device \HarddiskVolume2”。在內(nèi)核模式下,符號(hào)鏈接是以“\??\”( 或“\DosDevices\”)開頭的,如C 盤就是“\??\C:”,
而在用戶模式下,則是以“\\.\”開頭的,如C 盤就是“\\.\C:”。
4、卸載驅(qū)動(dòng)例程
代碼
1 #pragma PAGEDCODE
2
3 VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject)
4
5 {
6
7 PDEVICE_OBJECT pNextObj;
8
9 KdPrint(("Enter DriverUnload\n"));
10
11 pNextObj = pDriverObject->DeviceObject;//由驅(qū)動(dòng)對(duì)象得到設(shè)備對(duì)象
12
13 while (pNextObj != NULL)
14
15 {
16
17 PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
18
19 pNextObj->DeviceExtension;
20
21 //刪除符號(hào)鏈接
22
23 UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;
24
25 IoDeleteSymbolicLink(&pLinkName);
26
27 //刪除設(shè)備對(duì)象
28
29 pNextObj = pNextObj->NextDevice;
30
31 IoDeleteDevice( pDevExt->pDevice );
32
33 }
34
35 }
卸載驅(qū)動(dòng)例程是我們?cè)贒riverEntry 中自己定義的,當(dāng)驅(qū)動(dòng)被卸載時(shí)I/O管理器負(fù)責(zé)調(diào)用該例程,它主要做一些掃尾處理的工作。
KdPrint
由于驅(qū)動(dòng)程序工作于內(nèi)核態(tài),不像控制臺(tái)的程序一樣可以使用printf 輸出一些信息,也不像Win32 程序可以通過(guò)MessageBox 來(lái)彈出一個(gè)對(duì)話框,它要想輸出一些信息,就需要調(diào)用DbgPrint 函數(shù),不過(guò)這個(gè)函數(shù)輸出的信息我們無(wú)法直接看到,需要使用一些專門的工具,比如DbgView (KmdManager)等。
有些內(nèi)容我們只想在調(diào)試版輸出,在發(fā)行版忽略,因此DDK 中定義了一個(gè)宏KdPrint,它在發(fā)行版不被編譯,只在調(diào)試版才會(huì)運(yùn)行。KdPrint是這樣定義的:
#define KdPrint(_x_) DbgPrint _x_,在使用時(shí)最外層要有兩個(gè)連續(xù)的括號(hào)。
5、派遣例程
代碼
1 /************************************************************************
2
3 * 函數(shù)名稱:HelloDDKDispatchRoutine
4
5 * 功能描述:對(duì)讀IRP進(jìn)行處理
6
7 * 參數(shù)列表:
8
9 pDevObj:功能設(shè)備對(duì)象
10
11 pIrp:從IO請(qǐng)求包
12
13 * 返回 值:返回狀態(tài)
14
15 *************************************************************************/
16
17 #pragma PAGEDCODE
18
19 NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,
20
21 IN PIRP pIrp)
22
23 {
24
25 KdPrint(("Enter HelloDDKDispatchRoutine\n"));
26
27 NTSTATUS status = STATUS_SUCCESS;
28
29 // 完成IRP
30
31 pIrp->IoStatus.Status = status;
32
33 pIrp->IoStatus.Information = 0; // bytes xfered
34
35 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
36
37 KdPrint(("Leave HelloDDKDispatchRoutine\n"));
38
39 return status;
40
41 }
派遣例程是處理IRP的。
編譯
DDK方式
進(jìn)入相應(yīng)目錄,Build
Source如下:
TARGETNAME=HelloDDK
TARGETTYPE=DRIVER
TARGETPATH=OBJ //編譯輸出目錄
INCLUDES=$(BASEDIR)\inc;\
$(BASEDIR)\inc\ddk;\
SOURCES=Driver.cpp\
VC方式:參見[1]第一章。
驅(qū)動(dòng)安裝
用DriverStudio中的工具:DriverMonitor。
WDM式驅(qū)動(dòng)
/************************************************************************
* 函數(shù)名稱:DriverEntry
* 功能描述:初始化驅(qū)動(dòng)程序,定位和申請(qǐng)硬件資源,創(chuàng)建內(nèi)核對(duì)象
* 參數(shù)列表:
pDriverObject:從I/O管理器中傳進(jìn)來(lái)的驅(qū)動(dòng)對(duì)象
pRegistryPath:驅(qū)動(dòng)程序在注冊(cè)表的中的路徑
* 返回 值:返回初始化驅(qū)動(dòng)狀態(tài)
*************************************************************************/
#pragma INITCODE
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING pRegistryPath)
{
KdPrint(("Enter DriverEntry\n"));
pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice; //相比NT,此回調(diào)函數(shù)的作用是創(chuàng)建設(shè)備對(duì)象并由PNP管理器調(diào)用。
...
/************************************************************************
* 函數(shù)名稱:HelloWDMAddDevice
* 功能描述:添加新設(shè)備
* 參數(shù)列表:
DriverObject:從I/O管理器中傳進(jìn)來(lái)的驅(qū)動(dòng)對(duì)象
PhysicalDeviceObject:從I/O管理器中傳進(jìn)來(lái)的物理設(shè)備對(duì)象
* 返回 值:返回添加新設(shè)備狀態(tài)
*************************************************************************/
#pragma PAGEDCODE
NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMAddDevice\n"));
...
PAGED_CODE();//宏,只有check版本中有效,當(dāng)此例程所在的中斷請(qǐng)求超過(guò)APC_LEVEL時(shí),會(huì)產(chǎn)生一個(gè)斷言。
***
對(duì)IRP_MN_REMOVE_DEVICE的處理,類似于NT式驅(qū)動(dòng)中的卸載例程,而在WDM式驅(qū)動(dòng)中,卸載例程幾乎不做處理。
******
Source文件:
TARGETNAME=HelloWDM
TARGETTYPE=DRIVER
DRIVERTYPE=WDM
TARGETPATH=OBJ
INCLUDES=$(BASEDIR)\inc;\
$(BASEDIR)\inc\ddk;\
SOURCES=HelloWDM.cpp\
編譯:
同NT
安裝:
用EzDriverInstall安裝或控制面版中添加硬件。
實(shí)際上,常見的Windows 驅(qū)動(dòng)程序是可以分成兩類的:一類是不支持即插即用功能的NT 式驅(qū)動(dòng)程序,另一類是支持即插即用的WDM 式驅(qū)動(dòng)程序。NT 式驅(qū)動(dòng)的安裝是基于服務(wù)的,可以通過(guò)修改注冊(cè)表進(jìn)行,也可以直接通過(guò)服務(wù)函數(shù)如CreateService 進(jìn)行安裝;但WDM 式驅(qū)動(dòng)不同,它安裝的時(shí)候需要通過(guò)編寫一個(gè)inf 文件進(jìn)行控制。除此之外,它們所使用的頭文件也不大相同,例如NT 式驅(qū)動(dòng)往往需要導(dǎo)入一個(gè)名為“ntddk.h”的頭文件,而WDM 式驅(qū)動(dòng)需要的卻是“wdm.h”頭文件。我們?cè)趯W(xué)習(xí)的過(guò)程中所編寫的大多屬于NT 式驅(qū)動(dòng),除非我們需要自己的設(shè)備支持即插即用,才需要考慮編寫
WDM 式驅(qū)動(dòng)程序。
參考
【1】Windows 驅(qū)動(dòng)開發(fā)技術(shù)詳解
【2】http://msdn.microsoft.com/en-us/library/ff565757%28VS.85%29.aspx
總結(jié)
以上是生活随笔為你收集整理的5、Windows驱动开发技术详解笔记(1) 入门基础驱动程序结构的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 吃莲子吃多少合适?
- 下一篇: 切菜的木制砧板,在南方,比如福建,用一段