在上一篇文章裡我們寫了第一個驅動程式-HelloWorld, 今天我們來完善它,主要完成兩個功能:添加一個驅動裝置與給驅動添加預設派遣(IRP)。
首先我們來完成第一個功能:添加一個驅動裝置。
代碼如下:
NTSTATUS CreateTheDevice(IN PDRIVER_OBJECT pDeviceObject){NTSTATUS status;PDEVICE_OBJECT pDevObj;UNICODE_STRING devName;UNICODE_STRING symLinkName;RtlInitUnicodeString(&devName, L"\\Device\\first_Device");//create devicestatus = IoCreateDevice(pDeviceObject,\0,\&devName,\FILE_DEVICE_UNKNOWN,\0,\TRUE,\&pDevObj\);if (!NT_SUCCESS(status)){if (STATUS_INSUFFICIENT_RESOURCES == status){KdPrint(("資源不足"));}if (STATUS_OBJECT_NAME_EXISTS == status){KdPrint(("指定對象名存在"));}if (STATUS_OBJECT_NAME_COLLISION == status){KdPrint(("//對象名有衝突"));} KdPrint(("裝置建立失敗"));return status;}KdPrint(("建立裝置成功"));pDevObj->Flags |= DO_BUFFERED_IO;//建立符號連結RtlInitUnicodeString(&symLinkName,L"\\??\\firstSymDevice");status = IoCreateSymbolicLink( &symLinkName,&devName );if (!NT_SUCCESS(status)) /*status等於0*/{IoDeleteDevice( pDevObj );return status;}return STATUS_SUCCESS;}
這樣就可以給我們的驅動加入一個裝置,驅動開發是unicode的方式,所以不能直接用使用者態的API去處理字串,必須用Rtl開頭的核心功能,如上面RtlInitUnicodeString就是給第一個參數初始化。
第二個功能是關於IRP的,什麼叫IRP(I/O Request Package).使用者模式下所有對驅動的I/O請求,全部由作業系統轉化為一個叫著IRP的資料結構,不同的IRP請求會被“派遣”到不同的派遣函數中。
有五種常用IRP類型,分別是:
#define IRP_MJ_CREATE 0x00 //CreateFile()#define IRP_MJ_CLOSE 0x02 //CloseHandle() #define IRP_MJ_READ 0x03//ReadFile#define IRP_MJ_WRITE 0x04//WriteFile#define IRP_MJ_DEVICE_CONTROL 0x0e//DeviceIoControl
步驟是:
1.建立IRP處理函數
2.在驅動入口外註冊IRP處理髮函數
3.實現IRP處理函數
有兩種方式註冊IRP派遣函數,第一種是只有一個派遣函數,在該派遣函數內分別對根據IRP類型做不同的處理,這種方式代碼如下:
//註冊派遺函數pDriverObject->MajorFunction[IRP_MJ_CREATE]=ddk_DispatchRoutine;//註冊派遺函數pDriverObject->MajorFunction[IRP_MJ_CLOSE]=ddk_DispatchRoutine;//註冊派遺函數pDriverObject->MajorFunction[IRP_MJ_READ]=ddk_DispatchRoutine;//註冊派遺函數pDriverObject->MajorFunction[IRP_MJ_WRITE]=ddk_DispatchRoutine;//註冊派遺函數pDriverObject->MajorFunction[ IRP_MJ_DEVICE_CONTROL]=ddk_DispatchRoutine;NTSTATUS ddk_DispatchRoutine(IN PDEVICE_OBJECT pDevobj,IN PIRP pIrp){ PIO_STACK_LOCATION irpsp=IoGetCurrentIrpStackLocation(pIrp); switch (irpsp->MajorFunction) { case IRP_MJ_CREATE: break; case IRP_MJ_CLOSE: break; case IRP_MJ_READ: break; case IRP_MJ_WRITE: break; case IRP_MJ_DEVICE_CONTROL: break; default: KdPrint(("其它處理"));//指示完成此IRP } //成功返回return STATUS_SUCCESS;}
第二種是對每一種類型的IRP註冊一個派遣函數,代碼如下:
//註冊派遺函數pDriverObject->MajorFunction[IRP_MJ_CREATE]=ddk_DispatchRoutine_CREATE;//註冊派遺函數pDriverObject->MajorFunction[IRP_MJ_CLOSE]=ddk_DispatchRoutine_CLOSE;//註冊派遺函數pDriverObject->MajorFunction[IRP_MJ_READ]=ddk_DispatchRoutine_READ;//註冊派遺函數pDriverObject->MajorFunction[IRP_MJ_WRITE]=ddk_DispatchRoutine_WRITE;//註冊派遺函數pDriverObject->MajorFunction[ IRP_MJ_DEVICE_CONTROL]=ddk_DispatchRoutine_CONTROL;NTSTATUS ddk_DispatchRoutine_CONTROL(IN PDEVICE_OBJECT pDevobj,IN PIRP pIrp){ //對相應的IPR進行處理 pIrp->IoStatus.Information=0;//設定作業的位元組數為0,這裡無實際意義 pIrp->IoStatus.Status=STATUS_SUCCESS;//返回成功 IoCompleteRequest(pIrp,IO_NO_INCREMENT);//指示完成此IRP KdPrint(("離開派遣函數\n"));//調試資訊 return STATUS_SUCCESS; //返回成功}
到此代碼介紹完畢, 記得在卸載驅動的時候,要卸載裝置,不然下次載入驅動的時候,因為裝置沒有卸載而不能成功建立裝置。請參看完整的源碼 。