Windows驱动开发入门
對于初學者,DbgView.exe和SRVINSTW.EXE是非常簡單有用的兩個工具,一定要裝上。前者用于查看日志信息,后者用于加載驅動。
一、驅動程序設計的必要性
??? 在傳統DOS系統下,每個應用程序都有權利讀寫硬件,讀寫I/O端口,控制系統中斷,然而到了Windows系統中,為了保持良好的系統安全性,對應用程序的權限作出了限制,因為不適當的硬件讀寫會引發整個系統的崩潰。在Windows系統中,將整個程序設計為分層結構,其中,應用程序位于ring3,驅動程序位于ring0,應用程序不能讀寫底層硬件,對于硬件操作必須借助于底層驅動程序,所以,只要是與硬件系統打交道的Windows程序,必然會涉及到驅動程序的開發和設計。
二、驅動程序的分類和設計工具
??? 驅動程序是Windows系統的內核,驅動程序的分類與Windows相關,在Windows 9X下,驅動程序的類型為VXD(虛擬設備驅動程序),在Windows 2000/XP,驅動程序的類型為WDM(Windows驅動程序設計模型),生成的驅動程序設計文件為.sys格式。
??? 在Windows9X下,設計驅動程序的工具稱為VTOOLSD,而在Windows 2000/xp下,設計驅動程序的工具為DriverStudio中的DriverWorks,另外的設計驅動程序的工具還有WinDriver,微軟提供的開發工具為Windows DDK。由于所有的驅動設計工具均以DDK作為基本的類或者參照,加上DDK是一個免費軟件,所以在下面主要以DDK為例進行講解,掌握了DDK工具,其他工具也就變得簡單了。
三、Window DDK軟件的安裝與環境設置
??? 每個Windows系統都有各自的DDK開發工具,在安裝DDK前,請先根據Windows系統的不同,安裝不同的DDK工具。在安裝DDK之前,請先安裝相應的編譯器如Visual C++6.0,本文以Windows 2000下的DDK為例,說明安裝過程,安裝DDK完成后,選擇菜單“開始”->“Development Kit”->“Check Build environment”將自動進行各項環境的設置,當然用戶也可選擇“Free Build environment”,二者的區別在于“check”帶有調試信息,“Free”則不帶有調試信息,一般情況下,在軟件開發階段,選用“check”,而在發布階段,選用“Free”。
??? 選擇上述命令后,將進入編譯環境,該環境為“DOS”界面,進入驅動程序所在的目錄,一般情況下,該目錄包含了以下文件:
makefile 編譯文件,一般不作更改
sources 規定了其中的源文件,驅動程序的類型
源文件 為.c或者.h文件
??? 為了編寫方便,我們可以進入DDK提供的例子下面,將makefile和sources拷貝到我們程序所在的目錄下,將sources進行簡單的修改,編譯時,進入相應的目錄,在該目錄下輸入“Build”,系統編譯完成后,將在objchk\i386目錄下生成相應的.sys文件。
四、驅動程序的編寫
??? 尋找一個合適的編譯器如EditPlus,當然也可以用VC,只不過需要手動編譯,第一步,找到驅動程序的入口函數DriverEntry(),相當于Main()或者WinMain(),所有的驅動函數入口均從DriverEntry()開始,下面以端口驅動程序為例,說明驅動的編寫。該文件位于NTDDK\src\general\portio下。
1.創建設備
??? 對于設備驅動程序,首先要創建該設備,這段代碼可以放在DriverEntry中,也可放在AddDevice中。
首先,調用IoCreateDevice()創建自己的設備,該函數用法可以參考DDK或者相關示例。在端口操作中,可以將函數寫為:
status = IoCreateDevice (DriverObject,
??????????????????? sizeof (LOCAL_DEVICE_INFO),
??????????????????? &7,
??????????????????? GPD_TYPE,
??????????????????? 0,
??????????????????? FALSE,
????????????????? &deviceObject);
??? 其次,調用函數IoCreateSymbolicLink()創建兩個設備之間的連接
??? status = IoCreateSymbolicLink( &win32DeviceName, &ntDeviceName );
NTSTATUS DiskFilterAddDevice(IN PDRIVER_OBJECT DriverObject,IN PDEVICE_OBJECT PhysicalDeviceObject) {NTSTATUS status;IO_STATUS_BLOCK ioStatus;PDEVICE_OBJECT filterDeviceObject;PDEVICE_EXTENSION deviceExtension;PIRP irp;HANDLE thread_handle;static UCHAR firstdisk = 0;ULONG diskOrderFalg = 0;PAGED_CODE();DbgPrint("ntdisk:add device.\n");DbgPrint("DriverName = %ws\n", DriverObject->HardwareDatabase->Buffer);if (PhysicalDeviceObject->DeviceType == FILE_DEVICE_MASS_STORAGE && PhysicalDeviceObject->Characteristics == 0x181)//U盤{return STATUS_SUCCESS;}status = IoCreateDevice(DriverObject,DEVICE_EXTENSION_SIZE,NULL,FILE_DEVICE_DISK,0,FALSE,&filterDeviceObject);if (!NT_SUCCESS(status)){return status;}filterDeviceObject->Flags |= DO_DIRECT_IO;deviceExtension = (PDEVICE_EXTENSION)filterDeviceObject->DeviceExtension;RtlZeroMemory(deviceExtension, DEVICE_EXTENSION_SIZE);deviceExtension->TargetPhysicalDeviceObject = PhysicalDeviceObject;deviceExtension->TargetDeviceObject = IoAttachDeviceToDeviceStack(filterDeviceObject, PhysicalDeviceObject);if (deviceExtension->TargetDeviceObject == NULL){IoDeleteDevice(filterDeviceObject);return STATUS_NO_SUCH_DEVICE;}deviceExtension->DiskNumber = firstdisk++;DbgPrint("ntdisk: Attach to device %x,my device %x,return %x,disknumber %d \n", PhysicalDeviceObject, filterDeviceObject, deviceExtension->TargetDeviceObject, deviceExtension->DiskNumber);deviceExtension->DeviceObject = filterDeviceObject;KeInitializeEvent(&deviceExtension->PagingPathCountEvent, NotificationEvent, TRUE);filterDeviceObject->Flags |= DO_POWER_PAGABLE;filterDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;g_pDeviceExten[deviceExtension->DiskNumber] = deviceExtension;//向scsi發信號,打開開關。MakeSynchronusIoctl(deviceExtension->TargetDeviceObject, IOCTL_FROM_NTDISK_RW_SWITCH_ON, NULL, 0, NULL, 0);return STATUS_SUCCESS; }
2.初始化所用的資源
??? 在驅動程序中,總需要訪問I/O端口、系統中斷、內存地址以及DMA,使用這些資源之前,需要獲取資源并且初始化,一種簡單的方法是直接指定,如中斷10,DMA3等,這種方法雖然簡單,但靈活性差,任何硬件資源的改變均需在驅動程序中作出修改;另一種較為科學的方法就是讓驅動程序訪問注冊表,從注冊表中訪問硬件資源,然后進行初始化。
在驅動程序中,訪問注冊表的函數為RtlQueryRegistryValues(),該函數的用法較為復雜,可參考相關資料,建議在驅動程序開始開發時,直接給資源賦值,等驅動程序調試成功后再加入訪問注冊表代碼。
3.注冊驅動程序的各個處理函數
??? DriverObject->MajorFunction[IRP_MJ_CREATE]????????? = GpdDispatch;
??? DriverObject->MajorFunction[IRP_MJ_CLOSE]????????? = GpdDispatch;
??? DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = GpdDispatch;
??? DriverObject->DriverUnload????????????????????????? = GpdUnload;
??? DriverObject->MajorFunction[IRP_MJ_PNP]??????????? = GpdDispatchPnp;
??? DriverObject->MajorFunction[IRP_MJ_POWER]????????? = GpdDispatchPower;
??? DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL]= SystemControl;
??? DriverObject->DriverExtension->AddDevice????????? = GpdAddDevice;
??? 處理函數的注冊方法有點類似Windows下應用程序設計的消息處理函數,注冊完成后,當處理相應的IRP時,自動調用相應的函數模塊。
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath) {ULONG i;HANDLE hThread;NTSTATUS ntStatus;for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++){//if (i != IRP_MJ_POWER){DriverObject->MajorFunction[i] =DiskFilterPassThrough;}}DriverObject->MajorFunction[IRP_MJ_CREATE] = DiskFilterCreate;DriverObject->MajorFunction[IRP_MJ_READ] = DiskFilterReadWrite4;DriverObject->MajorFunction[IRP_MJ_WRITE] = DiskFilterReadWrite4;DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DiskFilterDeviceControl;DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = DiskFilterShutdownFlush;//DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = DiskFilterShutdownFlush;//DriverObject->MajorFunction[IRP_MJ_POWER] = DiskFilterDispatchPower;//DriverObject->MajorFunction[IRP_MJ_PNP] = DiskFilterDispatchPnp;DriverObject->DriverExtension->AddDevice = DiskFilterAddDevice;DriverObject->DriverUnload = DiskFilterUnload;return(STATUS_SUCCESS);} // DriverEntry
五、驅動程序與應用程序間的信息交互
??? 驅動程序用以訪問底層硬件,應用程序實現人機交互,驅動程序和應用程序之間需要實現相應的信息交互,一方面,應用程序通過對驅動程序發送相應的指令,實現硬件控制的動作指令,另一方面,驅動程序將硬件讀寫的狀態、從硬件上獲得的數據傳送給驅動程序,實現應用程序與驅動程序間的交互函數包括以下API函數;相應的API函數能夠激發驅動程序的消息。
接口API函數????????? 驅動程序的中IRP
CreateFile??????????? IRP_MJ_CREATE
CloseHandle???????? IRP_MJ_CLOSE
ReadFile????????????? IRP_MJ_READ
WriteFile????????????? IRP_MJ_WRITE
DeviceIoControl??? IRP_MJ_DEVICE_CONTROL
??? 在應用程序中,用戶可以調用上述函數操作驅動程序,其中CreateFile( )用于打開驅動程序,在使用完驅動程序之后,可以用CloseHandle()關閉驅動程序,ReadFile( )用于從驅動程序中讀取數據,WriteFile()用以往驅動程序中寫入數據,在函數中,最重要的是DeviceIoControl(),通過定義各種ITL_CODE來實現應用程序與驅動程序間的通訊函數,并可以傳遞各種參數和數據。
六、驅動程序的安裝
使用SRVINSTW.EXE--->
1.手動安裝方法
??? 生成的驅動程序為sys后綴,一般放于Windows\System32\Drivers目錄下,如果進行手動安裝,可以將生成好的驅動程序拷貝到該目錄中,然后修改注冊表,對于注冊表的修改,可以進入注冊表修改程序進行修改,也可編寫注冊表程序進行修改,以下為一注冊驅動程序的注冊表文件示例。
REGEDIT4
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Device]
"Type"=dword:00000001
"Start"=dword:00000001
"ErrorControl"=dword:00000001
"DisplayName"="Device"
"Group"="port"
"Tag"=dword:00000001
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Device\Parameters]
"IRQ Line"=dword:00000003
??? 直接在資源管理器下雙擊reg文件,在彈出的窗口上選擇“是”將直接修改注冊表,完成后,重新啟動Windows系統,將調用驅動程序。
2.編寫安裝文件INF
??? INF文件含有安裝一個WDM設備驅動程序需要的所有必需的信息,包括賦值的文件列表,要創建的注冊表項等,Windows為大多數類型的設備提供了一個標準的安裝程序。INF文件是一個文本文件,由節組成,每一節從括在方括號中的節的名稱開始,后面是節的內容,每一行可以是簡單的一項,或者設置一個一個值。具體的INF文件編寫可以參考現成的示例。
DDK安裝完成后,其中存在工具GenINF,可以按照該向導進行INF文件的編寫。
3.利用API函數編程實現驅動程序的安裝
??? 利用API函數實現注冊表的安裝,其實是利用訪問注冊表的API函數訪問修改注冊表,實現驅動程序的安裝。這種方法完全可以嵌入到我們的應用程序中,以下提供了安裝驅動程序的API代碼。主要的API函數包括RegCreateKeyEx(),RegSetValueEx(),RegQueryValueEx(),RegCloseKey() 。
七、驅動程序的調試
由于驅動程序的所有信息不能直接輸出到屏幕上,所以驅動程序的調試較一般應用程序要難得多,在調試時,可以利用應用程序中的DeviceIoControl()獲取驅動程序的狀態,也可借助調試工具SoftIce,比較方便的工具是SysInternals公司的DebugView,如果驅動程序中帶有調試語句信息DbgPrint(),可以直接將該函數提供的信息顯示到屏幕上。
使用windbg+虛擬機調試 || windbg+真機 ? 通過串口調試。
總結
以上是生活随笔為你收集整理的Windows驱动开发入门的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux安装PHPwind
- 下一篇: 生产企业智能制造执行系统解决方案(MES