Windows驅動程式框架

epluguo發表於2013-08-09

在配置好想對應的開發環境後,我們就可以開發驅動程式了。注:下面的主要以NT式驅動為例,部分涉及到WDM驅動的差別會有特別說明。

在Console控制檯下,我們的有一個入口函式main;在Windows圖形介面平臺下,有另外一個入口函式Winmain。我們只要在這入口函式裡面呼叫其他相關的函式,程式就會按照我們的意願跑起來了。在我們用IDE開發的時候,也許你不會發現這些細微之處是如何配置出來的,一般來說我們也不用理會,因為在新建工程的時候,IDE已經幫我們把編譯器(Compiler)以及聯結器(Linker)的相關引數設定好,在正式程式設計的時候,我們只要按照規定的框架程式設計就行了。

同樣,在驅動程式也有一個入口函式DriverEntry,這並不是一定的,但這是微軟預設的、推薦使用的。在我們配置開發環境的時候我們有機會指定入口函式,這是連結器的引數/entry:"DriverEntry"。

入口函式的宣告

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING pRegistryPath)


DriverEntry主要是對驅動程式進行初始化工作,它由系統程式(System)建立,系統啟動的時候System系統程式就被建立了。


驅動載入的時候,系統程式將會建立新的執行緒,然後呼叫執行體元件中的物件管理器,建立一個驅動物件(DRIVER_OBJECT)。另外,系統程式還得呼叫執行體元件中的配置管理程式,查詢此驅動程式在登錄檔中對應項。系統程式在呼叫驅動程式的DriverEntry的時候就會將這兩個值傳到pDriverObject和pRegistryPath。


接下來,我們介紹下上面出現的幾個資料結構:

typedef LONG NTSTATUS

在驅動開發中,我們應習慣於用NTSTATUS返回資訊,NTSTATUS各個位有不同的含義,我們可以也應該用巨集NT_SUCCESS來判斷是否返回成功。

#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)

NTSTAUS的編碼意義


其中

Ser是Serviity的縮寫,代表嚴重程度。

00:成功      01:資訊     10:警告      11:錯誤

C是Customer的縮寫,代表自定義的位。

Facility:裝置位

Code:裝置的狀態程式碼。

根據這定義編碼,還有補碼的概念,那麼只要是錯誤的時候,最高位就是1,NTSTATUS的值就是負數,所以可以大於零來判斷,但無論如何都希望讀者用NT_SUCCESS巨集來判斷是否成功,因為這可能在以後會有所改動,即使這麼多年來都一直沿用著。

同樣的,微軟也為我們定義了其他幾個判斷巨集:

#define NT_INFORMATION(Status) ((((ULONG)(Status)) >> 30) == 1)
#define NT_WARNING(Status) ((((ULONG)(Status)) >> 30) == 2)
#define NT_ERROR(Status) ((((ULONG)(Status)) >> 30) == 3)

有了之前的介紹,這三個相信不說大家也能領會了。但最常用的還是NT_SUCCESS。

我們繼續說其他的兩個資料結構,先說PUNICODE_STRING吧,P代表這是一個指標型別,指向一個UNICODE_STRING結構。


寬字串結構體(UNICODE_STRING)
typedef struct _UNICODE_STRING {
  USHORT  Length;
  USHORT  MaximumLength;
  PWSTR  Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

其中,

Ø  Length:Unicode字串當前的字元長度。注意不是位元組數,每個Unicode字元佔用兩個位元組。

Ø  MaximumLength:該Unicode字串的最大容納長度。

Ø  Buffer:Unicode字串的緩衝地址。

 

UNICODE_STRING是Windows驅動開發裡面經常用到的一個結構,用Length來標記字串的長度而不再用\0來表示結束。可以用RtlInitUnicodeString來對其初始化,但這裡的pRegistryPath是直接由建立驅動程式的執行緒傳進來的引數,如果在接下來仍需要用到該值,最好是使用RtlCopyUnicodeString函式將其值另外儲存下來,因為這個字串並不是長期存在的,DriverEntry函式返回的時候可能就會被銷燬了。

 

PDRIVER_OBJECT,P代表這是一個指標型別,指向一個驅動物件(DRIVER_OBJECT),每個驅動程式都有一個驅動物件。這是一個半透明的資料結構,微軟沒有公開它的完全定義,只是有提到幾個成員,但我們依舊可以通過WinDbg看到它的定義,只是不同的系統可能會存在不同的結構。不過我另外在WDK的標頭檔案WDM.h裡面發現了它的定義:

 

驅動物件(DRIVER_OBJECT)

typedef struct _DRIVER_OBJECT {
    CSHORT Type;
    CSHORT Size;
    PDEVICE_OBJECT DeviceObject;
    ULONG Flags;
    PVOID DriverStart;
    ULONG DriverSize;
    PVOID DriverSection;
    PDRIVER_EXTENSION DriverExtension;
    UNICODE_STRING DriverName;
    PUNICODE_STRING HardwareDatabase;
    PFAST_IO_DISPATCH FastIoDispatch;
    PDRIVER_INITIALIZE DriverInit;
    PDRIVER_STARTIO DriverStartIo;
    PDRIVER_UNLOAD DriverUnload;
    PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
} DRIVER_OBJECT;

這裡提下幾個比較重要的欄位,

Ø  DeviceObject:指向由此驅動建立的裝置物件。每個驅動程式都會有一個或多個的裝置物件。其中,每個裝置物件都會有一個指標指向下一個裝置物件,這在我們介紹裝置物件的時候再繼續說。

Ø  DriverName:驅動的名字,該字串一般為\Driver\[驅動程式名稱]。

Ø  HardwareDatabase:記錄裝置的硬體資料庫鍵名。該字串一般為"\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services\[服務名]"。

Ø  FastIoDispatch:指向快速I/O函式入口,是檔案驅動中用到的排遣函式。

Ø  DriverStartIo:記錄StartIo例程的函式地址,用於序列化操作。

Ø  DriverUnload:指定驅動解除安裝時所用的回撥函式地址。

Ø  MajorFunction:這是一個函式指標陣列,每個指標指向的是一個函式,該函式就是處理相應IRP的排遣函式,陣列的索引值與IRP_MJ_XXX相對應。

 

我們已經瞭解了DriverEntry函式頭的那個資料結構了,但這還不夠,在DriverEntry裡,我們主要是對驅動程式進行初始化,這就涉及到其他的一些資料結構了,下面我們繼續逐一地介紹。

 

裝置物件(DEVICE_OBJECT)

typedef struct _DEVICE_OBJECT {
  CSHORT                      Type;
  USHORT                      Size;
  LONG                        ReferenceCount;
  struct _DRIVER_OBJECT  *DriverObject;
  struct _DEVICE_OBJECT  *NextDevice;
  struct _DEVICE_OBJECT  *AttachedDevice;
  struct _IRP  *CurrentIrp;
  PIO_TIMER                   Timer;
  ULONG                       Flags;
  ULONG                       Characteristics;
  __volatile PVPB             Vpb;
  PVOID                       DeviceExtension;
  DEVICE_TYPE                 DeviceType;
  CCHAR                       StackSize;
  union {
    LIST_ENTRY         ListEntry;
    WAIT_CONTEXT_BLOCK Wcb;
  } Queue;
  ULONG                       AlignmentRequirement;
  KDEVICE_QUEUE               DeviceQueue;
  KDPC                        Dpc;
  ULONG                       ActiveThreadCount;
  PSECURITY_DESCRIPTOR        SecurityDescriptor;
  KEVENT                      DeviceLock;
  USHORT                      SectorSize;
  USHORT                      Spare1;
  struct _DEVOBJ_EXTENSION  *  DeviceObjectExtension;
  PVOID                       Reserved;
} DEVICE_OBJECT, *PDEVICE_OBJECT;

這裡只對幾個比較重要的欄位進行說明:

Ø  DriverObject:指向建立此裝置物件的驅動程式物件。同屬於一個驅動程式的裝置物件指向的是同一個驅動物件。

Ø  NextObject:指向同一個驅動程式建立的下一個裝置物件。同一個驅動物件可以建立若干個裝置物件,每個裝置物件根據NextDevice來連成一個連結串列,最後一個裝置物件的NextDevice域為NULL。

Ø  AttachedDevice:指向附加到此裝置物件之上的最近裝置物件。這裡需要理解分層驅動程式的概念。

Ø  DeviceExtension:指向裝置的擴充套件物件。每個裝置都會指定一個裝置擴充套件物件,這個資料結構由驅動程式開發者自行定義,可以用來記錄一些與裝置相關的一些資訊,同時應儘量避免使用全域性變數,將資料存放在裝置擴充套件裡,具有很大的靈活性。

Ø  CurrentIrp:在使用StartIO例程的時候,該成員指向的是當前IRP結構。

Ø  Flags:指定了該裝置物件的標記。下面列出了常用的幾個標記:

flag值

含義

DO_BUFFERED_IO

讀寫操作使用緩衝方式(系統複製緩衝區)訪問使用者模式資料

DO_EXCLUSIVE

一次只允許一個執行緒開啟裝置控制程式碼

DO_DIRECT_IO

讀寫操作使用直接方式(記憶體描述符表)訪問使用者模式資料

DO_DEVICE_INITIALIZING

裝置物件正在初始化

DO_POWER_PAGABLE

必須在PASSIVE_LEVEL級上處理IRP_MJ_PNP請求

 

Ø  DeviceType:指定裝置的型別。一般在開發虛擬裝置時,選擇FILE_DEVICE_UNKNOW。其他的請自行參考WDK文件。

Ø  StackSize:在多層驅動情況下,驅動與驅動之間會形成類似堆疊的結構,稱之為裝置棧。IRP會依次從最高層傳遞到最底層。StackSize描述的就是該層數。最底層的裝置層數為1。

Ø  AlignmentRequirement:在進行大容量傳輸的時候,往往需要進行記憶體對齊,以保證傳輸速度。請使用類似FILE_XXX_ALIGNMENT的方式進行賦值。



下面給大家展示一下DriverEntry的最基本框架

#ifdef __cplusplus
extern "C"
{
#endif

#include <NTDDK.h>

#ifdef __cplusplus
};
#endif

#define PAGECODE code_seg("PAGE")
#define LOCKEDCODE code_seg()
#define INITCODE code_seg("INIT")

#define PAGEDATA data_seg("PAGE")
#define LOCKEDDATA data_seg()
#define INITDATA data_seg("INIT")


typedef struct _DEVICE_EXTENSION
{
	PDEVICE_OBJECT pDevice;
	UNICODE_STRING ustrDeviceName;
	UNICODE_STRING ustrSymLinkName;
} DEVICE_EXTENSION , *PDEVICE_EXTENSION;

//函式宣告
NTSTATUS DispatchRoutine(__in struct _DEVICE_OBJECT  *DeviceObject, __in struct _IRP  *Irp);
VOID UnloadRoutine(__in struct _DRIVER_OBJECT  *DriverObject);

///////////////////////
#pragma INITCODE
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,
								PUNICODE_STRING pRegistryPath)
{
	NTSTATUS status;
	PDEVICE_EXTENSION pDevExt;
	PDEVICE_OBJECT pDevObj;
	KdPrint(("\n--------------------------------------!\n"));
	KdPrint(("Enter DriverEntry!\n"));
	//註冊相關例程
	pDriverObject->DriverUnload = UnloadRoutine;
	pDriverObject->MajorFunction[IRP_MJ_CREATE]	=	DispatchRoutine;
	pDriverObject->MajorFunction[IRP_MJ_READ]	=	DispatchRoutine;
	pDriverObject->MajorFunction[IRP_MJ_WRITE]	=	DispatchRoutine;
	pDriverObject->MajorFunction[IRP_MJ_CLOSE]	=	DispatchRoutine;

	//初始化相關字串
	UNICODE_STRING ustrDeviceName;	//裝置名
	UNICODE_STRING ustrSymLinkName; //符號連結名

	RtlInitUnicodeString(&ustrDeviceName,L"\\Device\\MyDDKDevice1");
	RtlInitUnicodeString(&ustrSymLinkName,L"\\??\\MyDDKDriver1");

	//建立裝置物件
	
	status = IoCreateDevice(pDriverObject,sizeof(DEVICE_EXTENSION),&ustrDeviceName,FILE_DEVICE_UNKNOWN,0,TRUE,&pDevObj);
	if (!NT_SUCCESS(status))
	{
		KdPrint(("Create Device Failure!\n"));
		return status;	
	}
	pDevObj->Flags |= DO_BUFFERED_IO;
	pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
	pDevExt->ustrDeviceName = ustrDeviceName;
	pDevExt->pDevice = pDevObj;

	//建立符號連結
	pDevExt->ustrSymLinkName = ustrSymLinkName;
	status = IoCreateSymbolicLink(&ustrSymLinkName,&ustrDeviceName);
	if (!NT_SUCCESS(status))
	{
		IoDeleteDevice(pDevObj);
		return status;
	}
	KdPrint(("Leave DriverEntry! stauts=%d",status));
	return status;
}

這是用C++寫的,所以必要的地方加上了extern“C”,否則會引起一些錯誤,這是因為C++與C在進行名稱粉碎的時候處理得不一樣,C++這個改進主要是為了實現一些高階功能,比如多型。雖然加上extern “C”會有點麻煩,但可以用上C++那些功能,個人覺得也有所值。如果用C,直接忽略上面的extern “C”。

 

NTDDK.h是NT式驅動需要載入的標頭檔案,如果是WDM式驅動,那麼載入的是WDM.h

 

#define INITCODE code_seg("INIT")定義一個巨集,#prama INITCODE還原後就是#pramacode_seg(“INIT”),表示接下來的程式碼載入到INIT記憶體區域中,成功載入後,可以退出記憶體。對於DriverEntry這種一次性的函式而言,這是最適合的選擇,可以節省記憶體。函式結束後需要顯式地切換回來,如:#prama LOCKEDCODE。

同樣,PAGECODE表示分頁記憶體,作用是將此部分程式碼放入分頁記憶體中執行,在裡面的程式碼切換程式上下文時可能會被換回分頁檔案。LOCKEDCODE表示預設記憶體,也就是非分頁記憶體,裡面的程式碼常駐記憶體。IRQL處於DISPATCH_LEVEL或者以上的等級,必須處於非分頁記憶體裡面。

同理,對於資料段也有同樣的機制,於是有了PAGEDATA、LOCKEDDATA、INITDATA。

 

KdPrint是一個巨集,在除錯版本里面(具備DBG巨集定義),有

#define KdPrint(_x_) DbgPrint _x_

而在正式版本里面,KdPrint被定義為空。所以可以用來作為除錯輸出。但注意Kdprint後面是兩層括號,用法與C語言執行庫的printf差不多。


pDriverObject->DriverUnload = UnloadRoutine;將解除安裝例程函式告訴驅動物件,驅動物件在前面已經有定義,這裡不做深入討論。


pDriverObject->MajorFunction[IRP_MJ_CREATE]  =       DispatchRoutine;註冊排遣例程。Windows是訊息驅動,而驅動程式是IRP驅動的,I/O管理器將傳送到驅動的“訊息”封裝在IRP裡面,驅動程式也將結果告訴IRP。類似windows的訊息機制,對於不同的“訊息”,驅動程式需要註冊不同的處理例程來區別對待,當然也可以放在同一個例程裡面,然後用switch語句來區別對待,但當處理過程比較長的時候,會比較凌亂。

IRP_MJ_CREATE是當RING3應用程式在使用CreateFile函式建立與驅動程式的通訊通道時所啟用的。IRP_MJ_READ是ReadFile,IRP_MJ_WRITE是WriteFile,而IRP_MJ_CLOSE是CloseHandle關閉檔案控制程式碼的時候產生的。

小知識:對於WDM式驅動,仍需要註冊AddDevice例程,pDriverObject->DriverExtension->AddDevice = WDMAddDeviceRoutine,裝置物件的初始化將在AddDevice裡面進行而不是DriverEntry。另外還需要註冊IRP_MJ_PNP排遣函式。

前面有講到UNICODE_STRING結構,那麼這裡就可以很好的瞭解初始化的這兩個結構了,忘記的看回前面的,這裡只列出RtlInitUnicodeString函式定義。

VOID RtlInitUnicodeString(PUNICODE_STRING  DestinationString,PCWSTR  SourceString);

IoCreateDevice是註冊裝置物件,一個驅動必須對應這一個或多個裝置物件。

NTSTATUS 
  IoCreateDevice(
    IN PDRIVER_OBJECT  DriverObject,
    IN ULONG  DeviceExtensionSize,
    IN PUNICODE_STRING  DeviceName  OPTIONAL,
    IN DEVICE_TYPE  DeviceType,
    IN ULONG  DeviceCharacteristics,
    IN BOOLEAN  Exclusive,
    OUT PDEVICE_OBJECT  *DeviceObject
    );

Ø  DriverObject:驅動物件的指標,這裡用的是入口函式傳進來的驅動物件。每個驅動有若干個裝置物件,每個裝置物件只有一個驅動函式。

Ø  DeviceExtensionSize:自定義的裝置擴充套件的大小。

Ø  DeviceName:裝置物件名稱,前面有用RtlInitUnicodeString對其進行過初始化。裝置物件是暴露在核心層面上的名稱,對於RING3層桌面程式是不可見的。格式需為:\Device\[裝置名]。如果不指定裝置名,I/O管理器將會自動分配一個數字作為裝置名,如:\Device\00000001

Ø  DeviceType:裝置型別,這裡用FILE_DEVICE_UNKNOWN。

Ø  DeviceCharacteristics:裝置物件的特徵。

Ø  Exclusive:設定裝置物件是否為專用的。也即是否允許有第二個驅動、程式訪問這個裝置物件。

Ø  DeviceObject:I/O管理器將會負責建立這個裝置物件,可以用這個引數來接收該物件的地址。

建立了裝置物件,在程式臨近結束之際需要用IoDeleteDevice刪除裝置物件。

 

pDevObj->Flags |= DO_BUFFERED_IO;是設定訪問標誌為緩衝模式。

 

pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;獲取裝置擴充套件。

 

pDevExt->ustrDeviceName = ustrDeviceName;將相關資訊儲存在裝置擴充套件裡面。

 

裝置物件名稱只暴露在核心層面,想要在RING3使用者層訪問驅動程式則需要建立符號連結。符號連結是暴露在使用者層面的。註冊符號連結用的函式是IoCreateSymbolicLink。

NTSTATUS 
  IoCreateSymbolicLink(
    IN PUNICODE_STRING  SymbolicLinkName,
    IN PUNICODE_STRING  DeviceName
    );

Ø  SymbolicLinkName:符號連結的字串,前面有對其初始化。符號連結名需要以\??\開頭,或者\DosDevice\(未證實)。而在使用者模式下需要以\\.\開頭才能找到對應的符號連結。

Ø  DeviceName:裝置名的字串。

注意:建立了符號連結,在程式臨近結束之際需要用IoDeleteSymbolicLink刪除符號連結。並且先刪除符號連結在刪除裝置物件。

 

在上面的DriverEntry函式裡面,已經完成了基本的初始化工作,接下來,我們看一下解除安裝回撥例程。

 

回撥例程的宣告為:

VOID UnloadRoutine(__in struct _DRIVER_OBJECT  *DriverObject);

除了函式名字外,請不要改動其他引數。

 

在回撥函式裡面,我們主要進行一些清理工作。

#pragma PAGECODE
VOID UnloadRoutine(__in struct _DRIVER_OBJECT  *pDriverObject)
{
	PDEVICE_OBJECT pNextObj;
	PDEVICE_EXTENSION pDevExt;
	pNextObj = pDriverObject->DeviceObject;
	KdPrint(("Enter unload routine!\n"));
	while(pNextObj != NULL)
	{
		pDevExt = (PDEVICE_EXTENSION)pNextObj->DeviceExtension;

		//刪除符號連結
		IoDeleteSymbolicLink(&pDevExt->ustrSymLinkName);
		pNextObj = pNextObj->NextDevice;
		//刪除裝置
		IoDeleteDevice(pDevExt->pDevice);
	}
	KdPrint(("Leave unload routine!\n"));
	KdPrint(("--------------------------------------!\n"));
}

基本在上面都已經有所描述了。這裡還強調一點,一個驅動程式可以有一個或者多個裝置物件,在驅動程式完全解除安裝之前需要刪除對應的符號連結、裝置物件。所以這裡用到了while迴圈來完成這項工作,不明白的回去上面繼續熟悉下裝置物件的結構。

 

這個驅動程式沒有做什麼工作,所以在排遣函式裡面我們只是單純的設定狀態為成功,操作的位元組為0,設定IRP狀態為完成,就返回了。


#pragma PAGECODE
NTSTATUS DispatchRoutine(__in struct _DEVICE_OBJECT  *pDeviceObject,
						 __in struct _IRP  *pIrp)
{
	KdPrint(("Enter dispatch routine!\n"));
	NTSTATUS status = STATUS_SUCCESS;
	//完成IRP
	pIrp->IoStatus.Status = status;
	pIrp->IoStatus.Information = 0;
	IoCompleteRequest(pIrp,IO_NO_INCREMENT);
	KdPrint(("Leave dispatch routine!\n"));
	return status;
}

pIrp->IoStatus.Status = status;設定IO狀態。

pIrp->IoStatus.Information = 0;設定實際操作的位元組數。使用者層函式ReadFile、WriteFile的第四個引數lpNumberOfBytesRead用於接收實際操作的位元組數,這個結果就是這樣產生的。

IoCompleteRequest設定完成IRP的處理,否則會繼續往下層傳遞。

 

整一個框架都已基本介紹完畢了,下面貼上完整的程式碼吧。

#ifdef __cplusplus
extern "C"
{
#endif

#include <NTDDK.h>

#ifdef __cplusplus
};
#endif

#define PAGECODE code_seg("PAGE")
#define LOCKEDCODE code_seg()
#define INITCODE code_seg("INIT")

#define PAGEDATA data_seg("PAGE")
#define LOCKEDDATA data_seg()
#define INITDATA data_seg("INIT")

#define arrarysize(arr) (sizeof(arr)/sizeof(arr)[0])

typedef struct _DEVICE_EXTENSION
{
	PDEVICE_OBJECT pDevice;
	UNICODE_STRING ustrDeviceName;
	UNICODE_STRING ustrSymLinkName;
} DEVICE_EXTENSION , *PDEVICE_EXTENSION;


//函式宣告
NTSTATUS DispatchRoutine(__in struct _DEVICE_OBJECT  *DeviceObject, __in struct _IRP  *Irp);
VOID UnloadRoutine(__in struct _DRIVER_OBJECT  *DriverObject);
	  

///////////////////////
#pragma INITCODE
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,
								PUNICODE_STRING pRegistryPath)
{
	NTSTATUS status;
	PDEVICE_EXTENSION pDevExt;
	PDEVICE_OBJECT pDevObj;
	KdPrint(("\n--------------------------------------!\n"));
	KdPrint(("pRegistryPath value:%ws",pRegistryPath));
	KdPrint(("Enter DriverEntry!\n"));
	//註冊相關例程
	pDriverObject->DriverUnload = UnloadRoutine;
	pDriverObject->MajorFunction[IRP_MJ_CREATE]	=	DispatchRoutine;
	pDriverObject->MajorFunction[IRP_MJ_READ]	=	DispatchRoutine;
	pDriverObject->MajorFunction[IRP_MJ_WRITE]	=	DispatchRoutine;
	pDriverObject->MajorFunction[IRP_MJ_CLOSE]	=	DispatchRoutine;

	//初始化相關字串
	UNICODE_STRING ustrDeviceName;	//裝置名
	UNICODE_STRING ustrSymLinkName; //符號連結名

	RtlInitUnicodeString(&ustrDeviceName,L"\\Device\\MyDDKDevice1");
	RtlInitUnicodeString(&ustrSymLinkName,L"\\??\\MyDDKDriver1");

	//建立裝置物件
	
	status = IoCreateDevice(pDriverObject,sizeof(DEVICE_EXTENSION),&ustrDeviceName,FILE_DEVICE_UNKNOWN,0,TRUE,&pDevObj);
	if (!NT_SUCCESS(status))
	{
		KdPrint(("Create Device Failure!\n"));
		return status;	
	}
	pDevObj->Flags |= DO_BUFFERED_IO;
	pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
	pDevExt->ustrDeviceName = ustrDeviceName;
	pDevExt->pDevice = pDevObj;

	//建立符號連結
	pDevExt->ustrSymLinkName = ustrSymLinkName;
	status = IoCreateSymbolicLink(&ustrSymLinkName,&ustrDeviceName);
	if (!NT_SUCCESS(status))
	{
		IoDeleteDevice(pDevObj);
		return status;
	}
	KdPrint(("Leave DriverEntry! stauts=%d",status));
	return status;
}
#pragma PAGECODE
NTSTATUS DispatchRoutine(__in struct _DEVICE_OBJECT  *pDeviceObject,
						 __in struct _IRP  *pIrp)
{
	KdPrint(("Enter dispatch routine!\n"));
	NTSTATUS status = STATUS_SUCCESS;
	//完成IRP
	pIrp->IoStatus.Status = status;
	pIrp->IoStatus.Information = 0;
	IoCompleteRequest(pIrp,IO_NO_INCREMENT);
	KdPrint(("Leave dispatch routine!\n"));
	return status;
	
}

#pragma PAGECODE
VOID UnloadRoutine(__in struct _DRIVER_OBJECT  *pDriverObject)
{
	PDEVICE_OBJECT pNextObj;
	PDEVICE_EXTENSION pDevExt;
	pNextObj = pDriverObject->DeviceObject;
	KdPrint(("Enter unload routine!\n"));
	while(pNextObj != NULL)
	{
		pDevExt = (PDEVICE_EXTENSION)pNextObj->DeviceExtension;

		//刪除符號連結
		IoDeleteSymbolicLink(&pDevExt->ustrSymLinkName);
		pNextObj = pNextObj->NextDevice;
		//刪除裝置
		IoDeleteDevice(pDevExt->pDevice);
	}
	KdPrint(("Leave unload routine!\n"));
	KdPrint(("--------------------------------------!\n"));
}

相關文章