标 题: 【成果3.4】WDM驱动中使用DeviceIoControl,CreateFile
作 者: 火影
时 间: 2008-01-08,23:49
链 接: http://bbs.pediy.com/showthread.php?t=57948
同样使用网上流传的WDM驱动Demo,自己添加一些注释,好像不算是自己的成果
驱动部分:
/*************************************************************************
/*
/* This file contains the implementation for mandatory part for
/* Pseudo Driver to work with the support of I/O Control Code
/* handling.
/*
/*************************************************************************/
#include <wdm.h>
#include "DrvMain.h"
#include "..\ShareFiles\Basic\WDMDefault.h"
#include "..\ShareFiles\PnP\PnP.h"
#include "..\ShareFiles\PM\PM.h"
UNICODE_STRING Global_sz_Drv_RegInfo;
UNICODE_STRING Global_sz_DeviceName;
PDEVICE_POWER_INFORMATION Global_PowerInfo_Ptr;
NTSTATUS
DriverEntry(
//DriverEntry的第一个参数是一个指针
//指向一个刚被初始化的驱动程序对象, 该对象就代表你的驱动程序。
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
DbgPrint("In DriverEntry : Begin\r\n");
//保存设备服务键键名
RtlInitUnicodeString(
&Global_sz_Drv_RegInfo,
RegistryPath->Buffer);
// Initialize function pointers
DriverObject->DriverUnload = DriverUnload;
//注意看这里
DriverObject->DriverExtension->AddDevice = AddDevice;
DriverObject->MajorFunction[IRP_MJ_CREATE] = PsdoDispatchCreate;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = PsdoDispatchClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = PsdoDispatchDeviceControl;
DriverObject->MajorFunction[IRP_MJ_POWER] = PsdoDispatchPower;
DriverObject->MajorFunction[IRP_MJ_PNP] = PsdoDispatchPnP;
DbgPrint("In DriverEntry : End\r\n");
return STATUS_SUCCESS;
}
NTSTATUS
AddDevice(
//DriverObject 参数指向一个驱动程序对象
//就是你在DriverEntry 例程中初始化的那个驱动程序对象。PhysicalDeviceObject参
//数指向设备堆栈底部的物理设备对象
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject
)
{
ULONG DeviceExtensionSize;
PDEVICE_EXTENSION p_DVCEXT;
PDEVICE_OBJECT ptr_PDO;
NTSTATUS status;
ULONG IdxPwrState;
DbgPrint("In AddDevice : Begin\r\n");
//设备名称
RtlInitUnicodeString(
&Global_sz_DeviceName,
L"\\DosDevices\\PSEUDODEVICE");
//Get DEVICE_EXTENSION required memory space
DeviceExtensionSize = sizeof(DEVICE_EXTENSION);
//创建设备,WDM驱动在这里创建设备
status = IoCreateDevice(
DriverObject,
DeviceExtensionSize,
&Global_sz_DeviceName,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
FALSE,
//存放设备对象指针
&ptr_PDO
);
if (NT_SUCCESS(status)) {
//Set Device Object Flags
//清除DO_DEVICE_INITIALIZING标志
//当这个标志设置时,I/O 管理器将拒绝任何打开该设备句柄的请求或向该设备对象上附着其它设备对象的请求。
//在驱动程序完成初始化后,必须清除这个标志
ptr_PDO->Flags &= ~DO_DEVICE_INITIALIZING;
//设备对象中有两个标志位需要在 AddDevice 中初始化,并且它们在以后也不会改变,它们是
//DO_BUFFERED_IO 和 DO_DIRECT_IO 标志。你只能设置并使用其中一个标志,它将决定你以何种方式处理
//来自用户模式的内存缓冲区
//在 buffered 方式中,I/O管理器先创建一个与用户模式数据缓冲区大小相等的系统缓冲区。而你的驱动程序将使用这个
//系统缓冲区工作。I/O 管理器负责在系统缓冲区和用户模式缓冲区之间复制数据。
//在 direct 方式中,I/O 管理器锁定了包含用户模式缓冲区的物理内存页,并创建一个称为 MDL(内存描述符表)的辅助数
//据结构来描述锁定页。因此你的驱动程序将使用 MDL 工作。
ptr_PDO->Flags |= DO_DIRECT_IO;
//Device Extension memory maps
//初始化设备扩展
p_DVCEXT = ptr_PDO->DeviceExtension;
//保存新设备对象
p_DVCEXT->DeviceObject = ptr_PDO;
//Initialize driver description string
RtlInitUnicodeString(
&p_DVCEXT->Device_Description,
L"This is a Pseudo Device Driver\r\n"
L"Created by mjtsai 2003/1/25\r\n");
//初始化自旋锁
IoInitializeRemoveLock(
&p_DVCEXT->RemoveLock,
'KCOL',
0,
0
);
//Initialize driver power state
p_DVCEXT->SysPwrState = PowerSystemWorking;
//全供电状态
p_DVCEXT->DevPwrState = PowerDeviceD0;
//Initialize device power information
//可暂时跳过
Global_PowerInfo_Ptr = ExAllocatePool(
NonPagedPool, sizeof(DEVICE_POWER_INFORMATION));
RtlZeroMemory(
Global_PowerInfo_Ptr,
sizeof(DEVICE_POWER_INFORMATION));
Global_PowerInfo_Ptr->SupportQueryCapability = FALSE;
Global_PowerInfo_Ptr->DeviceD1 = 0;
Global_PowerInfo_Ptr->DeviceD2 = 0;
Global_PowerInfo_Ptr->WakeFromD0 = 0;
Global_PowerInfo_Ptr->WakeFromD1 = 0;
Global_PowerInfo_Ptr->WakeFromD2 = 0;
Global_PowerInfo_Ptr->WakeFromD3 = 0;
Global_PowerInfo_Ptr->DeviceWake = 0;
Global_PowerInfo_Ptr->SystemWake = 0;
for (IdxPwrState = 0;
IdxPwrState < PowerSystemMaximum;
IdxPwrState++)
{
Global_PowerInfo_Ptr->DeviceState[IdxPwrState] = 0;
}
//Store next-layered device object
//Attach device object to device stack
//IoAttachDeviceToDeviceStack 的第一个参数是新创建的设备对象的地址
//第二个参数是设备对象指针。 AddDevice的第二个参数也是这个地址。
//返回值是紧接着你下面的任何设备对象的地址,它可以是设备对象,也可以是其它低
//级过滤器设备对象
//本例把新设备对象链接到底层物理设备对象之上
p_DVCEXT->NextDeviceObject =
IoAttachDeviceToDeviceStack(ptr_PDO, PhysicalDeviceObject);
}
DbgPrint("In AddDevice : End\r\n");
return status;
}
VOID
DriverUnload(
IN PDRIVER_OBJECT DriverObject
)
{
PDEVICE_EXTENSION p_DVCEXT;
DbgPrint("In DriverUnload : Begin\r\n");
p_DVCEXT = DriverObject->DeviceObject->DeviceExtension;
ExFreePool(Global_PowerInfo_Ptr);
RtlFreeUnicodeString(&Global_sz_Drv_RegInfo);
RtlFreeUnicodeString(
&p_DVCEXT->Device_Description);
//减少调用者设备对象和底层驱动的设备对象的连接
//减少对p_DVCEXT->DeviceObject的引用计数
//如果计数为0且底层驱动已经被标记为卸载操作,则底层驱动被卸载
IoDetachDevice(
p_DVCEXT->DeviceObject);
//从系统中移除一个设备对象,当p_DVCEXT->NextDeviceObject引用计数为0时。
//如果不为0,则标记为待删除。
IoDeleteDevice(
p_DVCEXT->NextDeviceObject);
DbgPrint("In DriverUnload : End\r\n");
return;
}
NTSTATUS
PsdoDispatchCreate(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PIO_STACK_LOCATION p_IO_STK;
PDEVICE_EXTENSION p_DVCEXT;
NTSTATUS status;
DbgPrint("IRP_MJ_CREATE Received : Begin\r\n");
//获得当前IRP堆栈位置
p_IO_STK = IoGetCurrentIrpStackLocation(Irp);
//DeviceExtension该结构可用于保存每个设备实例的信息
p_DVCEXT = DeviceObject->DeviceExtension;
//增加计数
status = IoAcquireRemoveLock(&p_DVCEXT->RemoveLock, p_IO_STK->FileObject);
if (NT_SUCCESS(status)) {
CompleteRequest(Irp, STATUS_SUCCESS, 0);
DbgPrint("IRP_MJ_CREATE Received : End\r\n");
return STATUS_SUCCESS;
} else {
IoReleaseRemoveLock(&p_DVCEXT->RemoveLock, p_IO_STK->FileObject);
CompleteRequest(Irp, status, 0);
DbgPrint("IRP_MJ_CREATE Received : End\r\n");
return status;
}
}
NTSTATUS
PsdoDispatchClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PIO_STACK_LOCATION p_IO_STK;
PDEVICE_EXTENSION p_DVCEXT;
DbgPrint("IRP_MJ_CLOSE Received : Begin\r\n");
p_IO_STK = IoGetCurrentIrpStackLocation(Irp);
p_DVCEXT = DeviceObject->DeviceExtension;
IoReleaseRemoveLock(&p_DVCEXT->RemoveLock,
p_IO_STK->FileObject);
CompleteRequest(Irp, STATUS_SUCCESS, 0);
DbgPrint("IRP_MJ_CLOSE Received : Begin\r\n");
return STATUS_SUCCESS;
}
NTSTATUS
PsdoDispatchDeviceControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
ULONG code, cbin, cbout, info, pwrinf_size;
PIO_STACK_LOCATION p_IO_STK;
PDEVICE_EXTENSION p_DVCEXT;
PDEVICE_POWER_INFORMATION pValue;
ULONG IdxPwrState;
NTSTATUS status;
p_IO_STK = IoGetCurrentIrpStackLocation(Irp);
p_DVCEXT = DeviceObject->DeviceExtension;
//DeviceIoControl访问码
code = p_IO_STK->Parameters.DeviceIoControl.IoControlCode;
//输入缓冲区长度
cbin = p_IO_STK->Parameters.DeviceIoControl.InputBufferLength;
//输出缓冲区长度
cbout = p_IO_STK->Parameters.DeviceIoControl.OutputBufferLength;
IoAcquireRemoveLock(&p_DVCEXT->RemoveLock, Irp);
switch(code)
{
case IOCTL_READ_DEVICE_INFO:
if (p_DVCEXT->Device_Description.Length > cbout)
{
cbout = p_DVCEXT->Device_Description.Length;
info = cbout;
} else {
info = p_DVCEXT->Device_Description.Length;
}
//将数据复制到系统缓冲区,I/O管理器负责将数据复制到用户缓冲区内
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,
p_DVCEXT->Device_Description.Buffer,
info);
status = STATUS_SUCCESS;
break;
//可跳过不看
case IOCTL_READ_POWER_INFO:
pwrinf_size = sizeof(DEVICE_POWER_INFORMATION);
if (pwrinf_size > cbout)
{
cbout = pwrinf_size;
info = cbout;
} else {
info = pwrinf_size;
}
//Display Related Device Power State
DbgPrint("Support Query Device Capability : %$r\n",
Global_PowerInfo_Ptr->SupportQueryCapability ? "Yes" : "No");
DbgPrint("DeviceD1 : %d\r\n", Global_PowerInfo_Ptr->DeviceD1);
DbgPrint("DeviceD2 : %d\r\n", Global_PowerInfo_Ptr->DeviceD2);
DbgPrint("WakeFromD0 : %d\r\n", Global_PowerInfo_Ptr->WakeFromD0);
DbgPrint("WakeFromD1 : %d\r\n", Global_PowerInfo_Ptr->WakeFromD1);
DbgPrint("WakeFromD2 : %d\r\n", Global_PowerInfo_Ptr->WakeFromD2);
DbgPrint("WakeFromD3 : %d\r\n", Global_PowerInfo_Ptr->WakeFromD3);
DbgPrint("SystemWake : %d\r\n", Global_PowerInfo_Ptr->SystemWake);
DbgPrint("DeviceWake : %d\r\n", Global_PowerInfo_Ptr->DeviceWake);
for (IdxPwrState = 0;
IdxPwrState < PowerSystemMaximum;
IdxPwrState++)
{
DbgPrint("DeviceState[%d] : %d\r\n",
IdxPwrState,
Global_PowerInfo_Ptr->DeviceState[IdxPwrState]);
}
#ifdef _DEF_HANDLE_BY_POWER_INFO_STRUCTURE
pValue = (PDEVICE_POWER_INFORMATION)
Irp->AssociatedIrp.SystemBuffer;
pValue->SupportQueryCapability = Global_PowerInfo_Ptr->SupportQueryCapability;
pValue->DeviceD1 = Global_PowerInfo_Ptr->DeviceD1;
pValue->DeviceD2 = Global_PowerInfo_Ptr->DeviceD2;
pValue->DeviceWake = Global_PowerInfo_Ptr->DeviceWake;
pValue->SystemWake = Global_PowerInfo_Ptr->SystemWake;
pValue->WakeFromD0 = Global_PowerInfo_Ptr->WakeFromD0;
pValue->WakeFromD1 = Global_PowerInfo_Ptr->WakeFromD1;
pValue->WakeFromD2 = Global_PowerInfo_Ptr->WakeFromD2;
pValue->WakeFromD3 = Global_PowerInfo_Ptr->WakeFromD3;
for (IdxPwrState = 0;
IdxPwrState < PowerSystemMaximum;
IdxPwrState++)
{
pValue->DeviceState[IdxPwrState] =
Global_PowerInfo_Ptr->DeviceState[IdxPwrState];
}
#else
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,
Global_PowerInfo_Ptr,
info);
#endif
status = STATUS_SUCCESS;
break;
default:
info = 0;
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
IoReleaseRemoveLock(&p_DVCEXT->RemoveLock, Irp);
CompleteRequest(Irp, STATUS_SUCCESS, info);
return status;
}
/*
NTSTATUS CompleteRequest(
IN PIRP Irp,
IN NTSTATUS status,
IN ULONG_PTR info)
{
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = info;
//驱动程序已经完成对IRP的处理,并把IRP交给I/O管理器。
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
*/
用户层部分:
#include "stdafx.h"
#include <winioctl.h>
/*
Self-Defined I/O Control Code
*/
//I/O Control Code for Device Information retrieval
//注意下面的缓冲模式与驱动程序中adddevice函数中初始的缓冲模式有些不同
//解释如下:
//那时我指出, 当读写请求到来时, 你必须记住在 AddDevice
//中指出的访问用户模式缓冲区所使用的模式,是 buffered模式还是 direct 模式(或者两者都不是)。控制请求也
//利用这些寻址方式,但有一些差异。不是用设备对象中的标志来指定全局寻址方式,而是用 IOCTL中的功能码
//的低两位来为每个IOCTL指定寻址方式
//此外,IOCTL中指定的缓冲方式并不影响普通读写 IRP的寻址缓冲区。
//#define CTL_CODE( DeviceType, Function, Method, Access ) ( \
((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) )
//#define METHOD_BUFFERED 0
//#define METHOD_IN_DIRECT 1
//#define METHOD_OUT_DIRECT 2
//#define METHOD_NEITHER 3
#define IOCTL_READ_DEVICE_INFO \
CTL_CODE( \
FILE_DEVICE_UNKNOWN, \
0x800, \
METHOD_BUFFERED, \
FILE_ANY_ACCESS)
int main(int argc, char* argv[])
{
//倒数第二个参数:我们以不指定FILE_FLAG_OVERLAPPED标志的情况下打开设备句柄。因此,在这之后对
//DeviceIoControl的调用将不返回,直到驱动程序对我们的请求做出回答。
//用来设置同步或异步
HANDLE hdevice = CreateFile("\\\\.\\PSEUDODEVICE", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hdevice == INVALID_HANDLE_VALUE)
{
printf("Unable to open PSEUDODEVICE device - error %d\n", GetLastError());
return 1;
}
wchar_t answer[512] = {'\0'};
DWORD junk;
//只需要输出缓冲区,注意answer为unicode
if (DeviceIoControl(hdevice, IOCTL_READ_DEVICE_INFO, NULL, 0, answer, sizeof(answer), &junk, NULL))
{
answer[junk] = 0;
MessageBoxW(NULL,answer,L"look me", 0);
wprintf(L"%s",answer);
}
else
printf("Error %d in call to DeviceIoControl\n", GetLastError());
CloseHandle(hdevice);
return 0;
}
如何编译代码:
进入ShareFiles文件夹,分别Build "Basic","PnP","PM",然后Build "IOCTL_PW"
如何安装驱动:
这里使用“控制面板”中的“添加硬件”的方式,选中从“磁盘安装”,选择“install"文件夹内的inf文件进行安装即可(普通的动态加载驱动程序的方法好像不适用于WDM驱动,每次都蓝屏)