1. 程式人生 > >第二講:我的第一個驅動

第二講:我的第一個驅動

原文:http://blog.csdn.net/caperingrabbit/article/details/5285288

配置好了開發環境之後,下面就要通過具體的程式來了解驅動的開發了。下面我們以一個WDM驅動的框架來實現Windows驅動程式的HelloWorld

作為一個驅動程式,首先應該寫的是它的入口函式,這點跟MFCWinMain或者C++中的Main函式一樣,驅動的入口函式使用DriverEntry。在入口函式中主要實現的功能是一些分發例程的註冊以及其他的需要初始化的事務。

DriverEntry的原型是NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING pRegistryPath),

INOUT這只是一個巨集,用來表示引數的輸入輸出型別,並無實質意義,也不佔空間。pDriverObject是我們所要建立的驅動物件,pRegistryPath是驅動程式在登錄檔中的地址。至於它們的型別,PDRIVER_OBJECT是驅動開發中一個重要的資料結構,這是驅動物件,具體的我們以後再討論,而PUNICODE_STRING則是一個寬字元的指標,在核心開發中,一般使用的字串都是寬字元,即Unicode string。  下面是一個範例:

[cpp]  view plain  copy
  1. extern"C" //一般用C++進行Windows驅動開發,在入口函式前面要加這個關鍵詞,主要是為了在驅動程式中能夠呼叫C的函式  
  2. NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,  
  3.     IN PUNICODE_STRING pRegistryPath  
  4.     )  
  5.     {  
  6.         KdPrint(("Hello!welcome to the driver entry!/n"));//KdPrint是一個巨集,類似於MFC中的Trace  
  7.         //對於通用的例程,這裡也使用一個通用的處理函式進行註冊  
  8.         for(ULONG i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)  
  9.         {  
  10.             pDriverObject->MajorFunction[i] = DispatchRoutine;  
  11.         }         
  12.           
  13.         //驅動的功能例程的註冊,這是幾個比較特殊的例程,我們分別註冊,  
  14.         //而驅動開發的主要工作就是完成他們的這些例程的實現函式,為了簡單方便講解,這裡使用的和上邊一樣  
  15.         pDriverObject->DriverExtension->AddDevice = MyDeviceAdd;  
  16.         pDriverObject->DriverUnload = DriverUnload;  
  17.         pDriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchRoutine;  
  18.         pDriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchRoutine;  
  19.         pDriverObject->MajorFunction[IRP_MJ_READ] = DispatchRoutine;  
  20.         pDriverObject->MajorFunction[IRP_MJ_WRITE] = DispatchRoutine;  
  21.           
  22.         return STATUS_SUCCESS;  
  23. }  

 

完成了入口函式之後,接下來就要實現上面註冊的分發例程的功能函數了。作為一個WDM驅動程式,首先需要建立裝置物件,這個任務就交給AddDevice例程,通過上面的註冊,實現部分放在MyDeviceAdd。

[cpp]  view plain  copy
  1. NTSTATUS MyDeviceAdd(IN PDRIVER_OBJECT DriverObject,  
  2.                            IN PDEVICE_OBJECT PhysicalDeviceObject)  
  3. {   
  4.     PAGED_CODE();//DDK提供的一個巨集  
  5.     KdPrint(("Enter MyDrivers/n"));  
  6.   
  7.     NTSTATUS status;  
  8.     PDEVICE_OBJECT fdo;  
  9.     UNICODE_STRING devName;  
  10.     RtlInitUnicodeString(&devName,L"//Device//MyDevice");  
  11.     //建立裝置物件  
  12.     status = IoCreateDevice(  
  13.         DriverObject,  
  14.         sizeof(DEVICE_EXTENSION),  
  15.         &(UNICODE_STRING)devName,  
  16.         FILE_DEVICE_UNKNOWN,  
  17.         0,  
  18.         FALSE,  
  19.         &fdo);  
  20.     if( !NT_SUCCESS(status))  
  21.         return status;  
  22.     //獲得裝置擴充套件的資料結構。這個資料結構一般是由使用者自己定義,儲存一些裝置相關的資訊  
  23.     PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;  
  24.     //配置裝置擴充套件的內容  
  25.     pdx->fdo = fdo;  
  26.     //裝置的掛載  
  27.     pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);  
  28.     UNICODE_STRING symLinkName;  
  29.     RtlInitUnicodeString(&symLinkName,L"//DosDevices//MyDriver");  
  30.   
  31.     pdx->ustrDeviceName = devName;  
  32.     pdx->ustrSymLinkName = symLinkName;  
  33.     status = IoCreateSymbolicLink(&(UNICODE_STRING)symLinkName,&(UNICODE_STRING)devName);  
  34.   
  35.     if( !NT_SUCCESS(status))  
  36.     {  
  37.         IoDeleteSymbolicLink(&pdx->ustrSymLinkName);  
  38.         status = IoCreateSymbolicLink(&symLinkName,&devName);  
  39.         if( !NT_SUCCESS(status))  
  40.         {  
  41.             return status;  
  42.         }  
  43.     }  
  44.     //設定裝置的IO狀態  
  45.     fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;  
  46.     fdo->Flags &= ~DO_DEVICE_INITIALIZING;  
  47.   
  48.     KdPrint(("LeaveMyDrivers/n"));  
  49.     return STATUS_SUCCESS;  
  50. }  

 

生成了裝置物件之後,接下來就要處理髮向裝置的各種請求了。這裡就用一個通用的處理例程來示範一下。其實這個處理函式只是將這些請求轉交給真實的裝置而已,沒有實際的功能。

[cpp]  view plain  copy
  1. NTSTATUS HelloWDMDispatchRoutine(IN PDEVICE_OBJECT fdo,  
  2.                                  IN PIRP Irp)  
  3. {  
  4.     PAGED_CODE();  
  5.     KdPrint(("Enter HelloWDMDispatchRoutine/n"));  
  6.     Irp->IoStatus.Status = STATUS_SUCCESS;  
  7.     Irp->IoStatus.Information = 0;   //   
  8.     IoCompleteRequest( Irp, IO_NO_INCREMENT );  
  9.     KdPrint(("Leave HelloWDMDispatchRoutine/n"));  
  10.     return STATUS_SUCCESS;  
  11. }  
  12. 執行完所有的請求之後,在最後解除安裝驅動。對於WDM驅動,標準的程式其實解除安裝例程沒什麼實質內容,一般都是有Pnp裡邊的RemoveDevice來處理的,這裡只是一個演示,演示一下這個例程的功能和內容。  
  13. VOID DriverUnload(IN PDRIVER_OBJECT pDriverObject)   
  14. {  
  15.     PDEVICE_OBJECT  pNextObj;  
  16.     KdPrint(("Enter DriverUnload/n"));  
  17.     pNextObj = pDriverObject->DeviceObject;  
  18.     while (pNextObj != NULL)   
  19.     {  
  20.         PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)  
  21.             pNextObj->DeviceExtension;  
  22.   
  23.         //刪除符號連結  
  24.         UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;  
  25.         IoDeleteSymbolicLink(&pLinkName);  
  26.         pNextObj = pNextObj->NextDevice;  
  27.         IoDeleteDevice( pDevExt->pDevice );  
  28.     }  
  29. }  

 

這樣,一個驅動的主要部分就完成了。接下來完成一些細碎的工作,編譯一下,一個驅動就出現了。

標頭檔案

[cpp]  view plain  copy
  1. #ifdef __cplusplus  
  2. extern "C"  
  3. {  
  4. #endif  
  5. #include <wdm.h>  
  6. #ifdef __cplusplus  
  7. }  
  8. #endif   
  9.   
  10. typedef struct _DEVICE_EXTENSION  
  11. {  
  12.     PDEVICE_OBJECT fdo;  
  13.     PDEVICE_OBJECT NextStackDevice;  
  14.     UNICODE_STRING ustrDeviceName;  // 裝置名  
  15.     UNICODE_STRING ustrSymLinkName; // 符號連結名  
  16. } DEVICE_EXTENSION, *PDEVICE_EXTENSION;  
  17.   
  18. #define PAGEDCODE code_seg("PAGE")  
  19. #define LOCKEDCODE code_seg()  
  20. #define INITCODE code_seg("INIT")  
  21.   
  22. #define PAGEDDATA data_seg("PAGE")  
  23. #define LOCKEDDATA data_seg()  
  24. #define INITDATA data_seg("INIT")  
  25.   
  26. #define arraysize(p) (sizeof(p)/sizeof((p)[0]))  
  27.   
  28. NTSTATUS MyDeviceAdd(IN PDRIVER_OBJECT DriverObject,  
  29.                            IN PDEVICE_OBJECT PhysicalDeviceObject);  
  30. NTSTATUS DispatchRoutine(IN PDEVICE_OBJECT fdo,  
  31.                                  IN PIRP Irp);  
  32. void DriverUnload(IN PDRIVER_OBJECT DriverObject);  
  33.   
  34. extern "C"  
  35. NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,  
  36.                      IN PUNICODE_STRING RegistryPath);  

 

新建一個文字檔案,內容如下:

[cpp]  view plain  copy
  1. #  
  2. # DO NOT EDIT THIS FILE!!!  Edit ./sources. If you want to add a new source  
  3. # file to this component.  This file merely indirects to the real make file  
  4. # that is shared by all the driver components of the Windows NT DDK  
  5. #  
  6.   
  7. !INCLUDE $(NTMAKEENV)/makefile.def  

 

重新命名檔案,改為Makefile,字尾沒有。然後重新建立一個文字檔案,寫入如下內容:

[cpp]  view plain  copy
  1. TARGETNAME=HellOWORLD  
  2. TARGETTYPE=DRIVER  
  3. DRIVERTYPE=WDM  
  4. TARGETPATH=OBJ  
  5.   
  6. INCLUDES=$(BASEDIR)/inc;/  
  7.          $(BASEDIR)/inc/ddk;/  
  8.   
  9. SOURCES=MyDrivers.cpp/  

 

儲存,更改檔名為Sources,字尾沒有,這樣進入DDK的編譯環境,已編譯,一個字尾為.sys的驅動檔案就出現了。

好了,一個簡單的驅動就誕生了。