這個例子是從《windows驅動開發技術詳解》的CD光牒上copy的,我隻是自己稍微改了一下。
入口函數DriverEntry
#pragma INITCODE
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING pRegistryPath)
{
KdPrint(("Enter DriverEntry\n"));
pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;
pDriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
pDriverObject->MajorFunction[IRP_MJ_CREATE] =
pDriverObject->MajorFunction[IRP_MJ_READ] =
pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloWDMDispatchRoutine;
pDriverObject->DriverUnload = HelloWDMUnload;
KdPrint(("Leave DriverEntry\n"));
return STATUS_SUCCESS;
}
提供4個派遣函數:
HelloWDMAddDevice
HelloWDMPnp
HelloWDMDispatchRoutine
HelloWDMUnload
在DriverEntry裡面簡單的設定一下。
AddDevice函數
AddDevice函數是WDM驅動特有的,NT驅動沒有,這也是主要的差別之一。先給出代碼:
#pragma PAGEDCODE
NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject)
//DriverObject就是指向本驅動程式的一個對象,是由PNP管理器傳遞進來的。
//PhysicalDeviceObject是PNP管理器傳遞進來的底層驅動裝置對象,這個東西在NT驅動中是沒有的。通常稱之為PDO,确切的說是由總線驅動建立的。
{
PAGED_CODE();
KdPrint(("Enter HelloWDMAddDevice\n"));
NTSTATUS status;
PDEVICE_OBJECT fdo;
UNICODE_STRING devName;
RtlInitUnicodeString(&devName,L"\\Device\\MyWDMDevice");//裝置名稱,裝置名稱隻能被核心模式下的其他驅動所識别。
//建立FDO(Function Device Object)
status = IoCreateDevice(
DriverObject,
sizeof(DEVICE_EXTENSION),
&(UNICODE_STRING)devName,
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&fdo);
if( !NT_SUCCESS(status))
return status;
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
pdx->fdo = fdo;
//将FDO附加在PDO上面,并且将Extension中的NextStackDevice指向FDO的下層裝置。如果PDO上面有過濾驅動的話,NextStackDevice就是過濾驅動,如果沒有就是PDO。
pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);
UNICODE_STRING symLinkName;
//建立連結符号,這樣使用者模式的應用就可以通路此裝置。核心模式下,符号連結是以\??\開頭的(或者\DosDevices\)。使用者模式下則是\\.\開頭。
//這裡就可以在使用者模式下用\\.\HelloWDM來通路本裝置。
RtlInitUnicodeString(&symLinkName,L"\\DosDevices\\HelloWDM");
pdx->ustrDeviceName = devName;
pdx->ustrSymLinkName = symLinkName;
status = IoCreateSymbolicLink(&(UNICODE_STRING)symLinkName,&(UNICODE_STRING)devName);
if( !NT_SUCCESS(status))
{
IoDeleteSymbolicLink(&pdx->ustrSymLinkName);
status = IoCreateSymbolicLink(&symLinkName,&devName);
if( !NT_SUCCESS(status))
{
return status;
}
}
fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;//DO_BUFFERED_IO,定義為“緩沖記憶體裝置”
fdo->Flags &= ~DO_DEVICE_INITIALIZING;//将Flag上的DO_DEVICE_INITIALIZING位清零,保證裝置初始化完畢,必須的。
KdPrint(("Leave HelloWDMAddDevice\n"));
return STATUS_SUCCESS;
}
IoCreateDevice有個裝置類型參數,這裡使用FILE_DEVICE_UNKNOWN。Windows已經預先定義了一些常見的裝置類型,如果驅動裝置并不在這些類型裡面,有兩種辦法:
1. 使用FILE_DEVICE_UNKNOWN;
2. 使用>=0x8000(32768 - 65535)的值
詳見:http://msdn.microsoft.com/en-us/library/windows/hardware/ff563821(v=vs.85).aspx
在DriverEntry函數裡面通過pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;設定AddDevice的例程函數。我畫了一個簡單的圖來描述AddDevice基本流程:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLxgTN48VN2ADO0QTN1MTMvwFNx8CXyEjMxAjMvw1ckF2bsBXdvwFdl5mLuR2cj5Set1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
AddDevice函數有2個參數:DriverObject和PDO。
DriverObject就是目前驅動程式的一個執行個體,PDO是實體裝置對象(由總線驅動建立)。
AddDevice函數主要工作就是建立一個功能裝置對象FDO,然後附加在傳遞進來的PDO上面。
PNP IRP處理函數
WDM支援PNP(即插即用),這也是不同于NT驅動的一個主要特征。所有WDM驅動都需要設定PNP IRP的派遣函數。如:
pDriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;
這個是在入口函數DriverEntry裡面設定的。
看具體代碼:
#pragma PAGEDCODE
NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo,
IN PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMPnp\n"));
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;//DEVICE_EXTENSION是在AddDevice裡面建立的。
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);//擷取irp資訊
switch (stack->MinorFunction)//根據PNP irp的minor function code來調用相應的處理函數。
{
case IRP_MN_REMOVE_DEVICE:
status = HandleRemoveDevice(pdx, Irp);
break;
default:
status = DefaultPnpHandler(pdx, Irp);
break;
}
KdPrint(("Leave HelloWDMPnp\n"));
return status;
}
Pnp派遣函數有2個參數:fdo和Irp。
fdo就是由AddDevice函數建立的那個功能裝置對象,Irp是I/O管理器傳進來的一個包(I/O Request Packet)。
這個例子裡面簡單處理了IRP_MN_REMOVE_DEVICE這個minor function code,其他所有的minor function code統統用一個函數來處理DefaultPnpHandler。
先看一下IRP_MN_REMOVE_DEVICE的處理函數:
#pragma PAGEDCODE
NTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION pdx, PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter HandleRemoveDevice\n"));
//設定IRP的完成狀态。
Irp->IoStatus.Status = STATUS_SUCCESS;
//将IRP請求向底層驅動轉發。
NTSTATUS status = DefaultPnpHandler(pdx, Irp);
//删除符号連結
IoDeleteSymbolicLink(&(UNICODE_STRING)pdx->ustrSymLinkName);
//調用IoDetachDevice()把fdo從裝置棧中脫開:
if (pdx->NextStackDevice)
IoDetachDevice(pdx->NextStackDevice);
//删除fdo:
IoDeleteDevice(pdx->fdo);
KdPrint(("Leave HandleRemoveDevice\n"));
return status;
}
裡面做了一些簡單的删除工作: 設定IRP完成狀态 -> 将IRP請求向下一層驅動轉發 -> 删除符号連結 -> 删除功能裝置對象(FDO)。
再看一些DefaultPnpHandler函數:
#pragma PAGEDCODE
NTSTATUS DefaultPnpHandler(PDEVICE_EXTENSION pdx, PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter DefaultPnpHandler\n"));
IoSkipCurrentIrpStackLocation(Irp);
KdPrint(("Leave DefaultPnpHandler\n"));
return IoCallDriver(pdx->NextStackDevice, Irp);//将irp傳遞給下層驅動
}
啥也沒做,就是跳過本層堆棧,直接将irp傳遞到下層驅動。
IoSkipCurrentIrpStackLocation:修改IRP的IO_STACK_LOCATION,使下層驅動可以獲得跟本層驅動一樣的IRP。
MSDN解釋:
The IoSkipCurrentIrpStackLocation macro modifies the system'sIO_STACK_LOCATION array pointer, so that when the current driver calls the next-lower driver, that driver receives the sameIO_STACK_LOCATION structure that the current driver received.
IoCallDriver: 将irp傳遞給下層驅動裝置對象。
現在可以看到,這個驅動程式例子隻是處理了Remove Device,對于其他所有PNP請求,隻是簡單的傳遞給下層驅動處理。
DriverUnload函數
因為IRP_MN_REMOVE_DEVICE的函數裡面已經處理了裝置删除,DriverUnload函數裡面不需要處理什麼事情了,隻是簡單列印一些log。
DriverEntry函數裡面的代碼,簡單設定一下派遣函數,如:
pDriverObject->DriverUnload = HelloWDMUnload;
HelloWDMUnload的實作:
#pragma PAGEDCODE
void HelloWDMUnload(IN PDRIVER_OBJECT DriverObject)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMUnload\n"));
KdPrint(("Leave HelloWDMUnload\n"));
}
啥都沒做,就是列印log。
最後剩下一個函數是一個派遣函數,處理CREATE,READ, WRITE等irp。
DispatchRoutine
看DriverEntry裡面的幾行代碼:
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
pDriverObject->MajorFunction[IRP_MJ_CREATE] =
pDriverObject->MajorFunction[IRP_MJ_READ] =
pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloWDMDispatchRoutine;
這個驅動例子裡面,将DEVICE_CONTROL, CREATE, READ 和WRITE的IRP用同一個派遣函數來處理。
#pragma PAGEDCODE
NTSTATUS HelloWDMDispatchRoutine(IN PDEVICE_OBJECT fdo,
IN PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMDispatchRoutine\n"));
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
//列印IRP的major function code和minor function code。
ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
KdPrint(("DispatchRoutine, Major function code: %x, Minor function code: %x, control code: %x\n",
stack->MajorFunction, stack->MinorFunction, code));
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0; // no bytes xfered
IoCompleteRequest( Irp, IO_NO_INCREMENT );
KdPrint(("Leave HelloWDMDispatchRoutine\n"));
return STATUS_SUCCESS;
}
HelloWDMDispatchRoutine是一個派遣函數,它有2個參數:fdo和irp(跟PNP處理函數一樣)
這裡隻做了2個事情:
1. 列印一些log
2. 設定irp完成狀态,調用IoCompleteRequest函數來完成這個irp。不需要往下層驅動傳irp了,因為這個irp已經完成了。
好了,代碼基本介紹完畢,看一下完整代碼:
/************************************************************************
* 檔案名稱:HelloWDM.cpp
* 作 者:張帆
* 完成日期:2007-11-1
*************************************************************************/
#include "HelloWDM.h"
/************************************************************************
* 函數名稱:DriverEntry
* 功能描述:初始化驅動程式,定位和申請硬體資源,建立核心對象
* 參數清單:
pDriverObject:從I/O管理器中傳進來的驅動對象
pRegistryPath:驅動程式在系統資料庫的中的路徑
* 傳回 值:傳回初始化驅動狀态
*************************************************************************/
#pragma INITCODE
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING pRegistryPath)
{
KdPrint(("Enter DriverEntry\n"));
pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;
pDriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
pDriverObject->MajorFunction[IRP_MJ_CREATE] =
pDriverObject->MajorFunction[IRP_MJ_READ] =
pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloWDMDispatchRoutine;
pDriverObject->DriverUnload = HelloWDMUnload;
KdPrint(("Leave DriverEntry\n"));
return STATUS_SUCCESS;
}
/************************************************************************
* 函數名稱:HelloWDMAddDevice
* 功能描述:添加新裝置
* 參數清單:
DriverObject:從I/O管理器中傳進來的驅動對象
PhysicalDeviceObject:從I/O管理器中傳進來的實體裝置對象
* 傳回 值:傳回添加新裝置狀态
*************************************************************************/
#pragma PAGEDCODE
NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject)
//DriverObject就是指向本驅動程式的一個對象,是由PNP管理器傳遞進來的。
//PhysicalDeviceObject是PNP管理器傳遞進來的底層驅動裝置對象,這個東西在NT驅動中是沒有的。通常稱之為PDO,确切的說是由總線驅動建立的。
{
PAGED_CODE();
KdPrint(("Enter HelloWDMAddDevice\n"));
NTSTATUS status;
PDEVICE_OBJECT fdo;
UNICODE_STRING devName;
RtlInitUnicodeString(&devName,L"\\Device\\MyWDMDevice");//裝置名稱,裝置名稱隻能被核心模式下的其他驅動所識别。
//建立FDO(Function Device Object)
status = IoCreateDevice(
DriverObject,
sizeof(DEVICE_EXTENSION),
&(UNICODE_STRING)devName,
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&fdo);
if( !NT_SUCCESS(status))
return status;
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
pdx->fdo = fdo;
//将FDO附加在PDO上面,并且将Extension中的NextStackDevice指向FDO的下層裝置。如果PDO上面有過濾驅動的話,NextStackDevice就是過濾驅動,如果沒有就是PDO。
pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);
UNICODE_STRING symLinkName;
//建立連結符号,這樣使用者模式的應用就可以通路此裝置。核心模式下,符号連結是以\??\開頭的(或者\DosDevices\)。使用者模式下則是\\.\開頭。
//這裡就可以在使用者模式下用\\.\HelloWDM來通路本裝置。
RtlInitUnicodeString(&symLinkName,L"\\DosDevices\\HelloWDM");
pdx->ustrDeviceName = devName;
pdx->ustrSymLinkName = symLinkName;
status = IoCreateSymbolicLink(&(UNICODE_STRING)symLinkName,&(UNICODE_STRING)devName);
if( !NT_SUCCESS(status))
{
IoDeleteSymbolicLink(&pdx->ustrSymLinkName);
status = IoCreateSymbolicLink(&symLinkName,&devName);
if( !NT_SUCCESS(status))
{
return status;
}
}
fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;//DO_BUFFERED_IO,定義為“緩沖記憶體裝置”
fdo->Flags &= ~DO_DEVICE_INITIALIZING;//将Flag上的DO_DEVICE_INITIALIZING位清零,保證裝置初始化完畢,必須的。
KdPrint(("Leave HelloWDMAddDevice\n"));
return STATUS_SUCCESS;
}
/************************************************************************
* 函數名稱:DefaultPnpHandler
* 功能描述:對PNP IRP進行預設處理
* 參數清單:
pdx:裝置對象的擴充
Irp:從IO請求包
* 傳回 值:傳回狀态
*************************************************************************/
#pragma PAGEDCODE
NTSTATUS DefaultPnpHandler(PDEVICE_EXTENSION pdx, PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter DefaultPnpHandler\n"));
IoSkipCurrentIrpStackLocation(Irp);
KdPrint(("Leave DefaultPnpHandler\n"));
return IoCallDriver(pdx->NextStackDevice, Irp);//将irp傳遞給下層驅動
}
/************************************************************************
* 函數名稱:HandleRemoveDevice
* 功能描述:對IRP_MN_REMOVE_DEVICE IRP進行處理
* 參數清單:
fdo:功能裝置對象
Irp:從IO請求包
* 傳回 值:傳回狀态
*************************************************************************/
#pragma PAGEDCODE
NTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION pdx, PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter HandleRemoveDevice\n"));
//設定IRP的完成狀态。
Irp->IoStatus.Status = STATUS_SUCCESS;
//将IRP請求向底層驅動轉發。
NTSTATUS status = DefaultPnpHandler(pdx, Irp);
//删除符号連結
IoDeleteSymbolicLink(&(UNICODE_STRING)pdx->ustrSymLinkName);
//調用IoDetachDevice()把fdo從裝置棧中脫開:
if (pdx->NextStackDevice)
IoDetachDevice(pdx->NextStackDevice);
//删除fdo:
IoDeleteDevice(pdx->fdo);
KdPrint(("Leave HandleRemoveDevice\n"));
return status;
}
/************************************************************************
* 函數名稱:HelloWDMPnp
* 功能描述:對即插即用IRP進行處理
* 參數清單:
fdo:功能裝置對象
Irp:從IO請求包
* 傳回 值:傳回狀态
*************************************************************************/
#pragma PAGEDCODE
NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo,
IN PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMPnp\n"));
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;//DEVICE_EXTENSION是在AddDevice裡面建立的。
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);//擷取irp資訊
switch (stack->MinorFunction)//根據PNP irp的minor function code來調用相應的處理函數。
{
case IRP_MN_REMOVE_DEVICE:
status = HandleRemoveDevice(pdx, Irp);
break;
default:
status = DefaultPnpHandler(pdx, Irp);
break;
}
KdPrint(("Leave HelloWDMPnp\n"));
return status;
}
/************************************************************************
* 函數名稱:HelloWDMDispatchRoutine
* 功能描述:對預設IRP進行處理
* 參數清單:
fdo:功能裝置對象
Irp:從IO請求包
* 傳回 值:傳回狀态
*************************************************************************/
#pragma PAGEDCODE
NTSTATUS HelloWDMDispatchRoutine(IN PDEVICE_OBJECT fdo,
IN PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMDispatchRoutine\n"));
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
//列印IRP的major function code和minor function code。
ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
KdPrint(("DispatchRoutine, Major function code: %x, Minor function code: %x, control code: %x\n",
stack->MajorFunction, stack->MinorFunction, code));
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0; // no bytes xfered
IoCompleteRequest( Irp, IO_NO_INCREMENT );
KdPrint(("Leave HelloWDMDispatchRoutine\n"));
return STATUS_SUCCESS;
}
/************************************************************************
* 函數名稱:HelloWDMUnload
* 功能描述:負責驅動程式的解除安裝操作
* 參數清單:
DriverObject:驅動對象
* 傳回 值:傳回狀态
*************************************************************************/
#pragma PAGEDCODE
void HelloWDMUnload(IN PDRIVER_OBJECT DriverObject)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMUnload\n"));
KdPrint(("Leave HelloWDMUnload\n"));
}
接下來就是如何安裝了。
WDM驅動安裝
WDM驅動安裝需要一個inf檔案,這裡就直接引用《windows驅動開發技術詳解》裡面的,沒有做任何改動,直接貼出來:
;; The Win2K DDK documentation contains an excellent INF reference.
;--------- Version Section ---------------------------------------------------
[Version]
Signature="$CHICAGO$"
Provider=Zhangfan_Device
DriverVer=11/1/2007,3.0.0.3
; If device fits one of the standard classes, use the name and GUID here,
; otherwise create your own device class and GUID as this example shows.
Class=ZhangfanDevice
ClassGUID={EF2962F0-0D55-4bff-B8AA-2221EE8A79B0}
;--------- SourceDiskNames and SourceDiskFiles Section -----------------------
; These sections identify source disks and files for installation. They are
; shown here as an example, but commented out.
[SourceDisksNames]
1 = "HelloWDM",Disk1,,
[SourceDisksFiles]
HelloWDM.sys = 1,MyDriver_Check,
;--------- ClassInstall/ClassInstall32 Section -------------------------------
; Not necessary if using a standard class
; 9X Style
[ClassInstall]
Addreg=Class_AddReg
; NT Style
[ClassInstall32]
Addreg=Class_AddReg
[Class_AddReg]
HKR,,,,%DeviceClassName%
HKR,,Icon,,"-5"
;--------- DestinationDirs Section -------------------------------------------
[DestinationDirs]
YouMark_Files_Driver = 10,System32\Drivers
;--------- Manufacturer and Models Sections ----------------------------------
[Manufacturer]
%MfgName%=Mfg0
[Mfg0]
; PCI hardware Ids use the form
; PCI\VEN_aaaa&DEV_bbbb&SUBSYS_cccccccc&REV_dd
;改成你自己的ID
%DeviceDesc%=YouMark_DDI, PCI\VEN_9999&DEV_9999
;---------- DDInstall Sections -----------------------------------------------
; --------- Windows 9X -----------------
; Experimentation has shown that DDInstall root names greater than 19 characters
; cause problems in Windows 98
[YouMark_DDI]
CopyFiles=YouMark_Files_Driver
AddReg=YouMark_9X_AddReg
[YouMark_9X_AddReg]
HKR,,DevLoader,,*ntkern
HKR,,NTMPDriver,,HelloWDM.sys
HKR, "Parameters", "BreakOnEntry", 0x00010001, 0
; --------- Windows NT -----------------
[YouMark_DDI.NT]
CopyFiles=YouMark_Files_Driver
AddReg=YouMark_NT_AddReg
[YouMark_DDI.NT.Services]
Addservice = HelloWDM, 0x00000002, YouMark_AddService
[YouMark_AddService]
DisplayName = %SvcDesc%
ServiceType = 1 ; SERVICE_KERNEL_DRIVER
StartType = 3 ; SERVICE_DEMAND_START
ErrorControl = 1 ; SERVICE_ERROR_NORMAL
ServiceBinary = %10%\System32\Drivers\HelloWDM.sys
[YouMark_NT_AddReg]
HKLM, "System\CurrentControlSet\Services\HelloWDM\Parameters",\
"BreakOnEntry", 0x00010001, 0
; --------- Files (common) -------------
[YouMark_Files_Driver]
HelloWDM.sys
;--------- Strings Section ---------------------------------------------------
[Strings]
ProviderName="Zhangfan."
MfgName="Zhangfan Soft"
DeviceDesc="Hello World WDM!"
DeviceClassName="Zhangfan_Device"
SvcDesc="Zhangfan"
有關裡面的說明,以後再講。
有了inf後,就可以通過控制台->安裝硬體來安裝這個驅動。給出最後一個截圖,其他略。
安裝成功後,可以在裝置管理裡面看到:
這樣就安裝成功了。
搞了半天,這個驅動有啥用呢?當然我們可以寫一個使用者模式的測試程式。
調用驅動(使用者模式和核心模式通信)
寫個很簡單的測試例子:
// TestWDMDriver.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
#define DEVICE_NAME L"\\\\.\\HelloWDM"
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hDevice = CreateFile(DEVICE_NAME,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if (hDevice != INVALID_HANDLE_VALUE)
{
for(int i = 0; i < 10; i++)
{
DWORD RetBytes = 0;
BOOL b = DeviceIoControl(hDevice, 0xAB, NULL, 0, NULL, 0, &RetBytes, NULL);
printf("Test number %d, DeviceIoControl result: %d, byte: %d\n", i + 1, b, RetBytes);
Sleep(1000);
}
CloseHandle(hDevice);
}
else
printf("CreateFile failed, err: %x\n", GetLastError());
return 0;
}
相當的簡單,通過CreateFile函數來打開HelloWDM驅動,通過裝置名字\\.\HelloWDM。這個名字對應驅動的AddDevice函數裡面設定的符号連結\\DosDevices\\HelloWDM。
這裡有必要講一下驅動的幾個名字概念:
1. 首先是裝置名稱,就是IoCreateDevice裡面用到的那個名字。這個名字隻能被核心模式的程式(如其他驅動)所識别。使用者模式的程式是不知道這個名字的。
2. 符号連結,驅動程式裡面可以為某個裝置設定符号連結,以\??\開頭(或者\DosDevices\),這樣使用者模式的程式就可以通過這個符号連結來通路這個裝置。
3. 使用者模式程式裡面的裝置名稱,以\\.\開頭
比如這個例子裡面核心模式的裝置名稱是"\Device\MyWDMDevice",符号連結是"\DosDevices\HelloWDM",然後使用者模式程式通過"\\.\HelloWDM"通路這個裝置。
當CreateFile成功打開這個裝置後,就可以操作這個裝置了。測試例子裡面簡單往這個裝置調用了10次DeviceIoControl函數(也就是發送一個IRP_MJ_DEVICE_CONTROL類型的IRP)。
運作測試程式,在debugview裡面可以看到:
哈哈,驅動程式成功地收到了來自測試程式(使用者模式)的資訊。
這裡隻是簡單列印一下資訊,
Major function code 是e(16進制),看一下WDM.H
#define IRP_MJ_CREATE 0x00
#define IRP_MJ_CREATE_NAMED_PIPE 0x01
#define IRP_MJ_CLOSE 0x02
#define IRP_MJ_READ 0x03
#define IRP_MJ_WRITE 0x04
#define IRP_MJ_QUERY_INFORMATION 0x05
#define IRP_MJ_SET_INFORMATION 0x06
#define IRP_MJ_QUERY_EA 0x07
#define IRP_MJ_SET_EA 0x08
#define IRP_MJ_FLUSH_BUFFERS 0x09
#define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0a
#define IRP_MJ_SET_VOLUME_INFORMATION 0x0b
#define IRP_MJ_DIRECTORY_CONTROL 0x0c
#define IRP_MJ_FILE_SYSTEM_CONTROL 0x0d
#define IRP_MJ_DEVICE_CONTROL 0x0e
确實0x0e對應的是IRP_MJ_DEVICE_CONTROL。
然後測試例子裡面發送了一個0xAB的控制碼給驅動,那麼驅動裡面通過stack->Parameters.DeviceIoControl.IoControlCode得到的控制碼也是0xab,看上面的debugview資訊。
這樣就大功告成了。首先我們寫了一個簡單的WDM驅動,然後安裝,之後在使用者模式裡面通路這個驅動。這也就是驅動的一般流程。
當然真正的驅動程式遠沒有這個例子簡單。我個人對于驅動大概也就是國小生的水準,需要繼續學習,研究。
我将完整代碼打包上傳了,有興趣可以下載下傳:http://download.csdn.net/detail/zj510/4794275
(用DDK編譯驅動,簡單調用build指令即可。用VS2008編譯使用者模式的測試程式)