继WDM后微软出了WDF,封装了WDM中的一些基本代码逻辑。本人菜鸟,也不知道本质上有何区别,只觉得是多了Wdf开头的函数,基本的编程框架上有点出入。
KMDF是WDF的内核级部分,为了理清KMDF的结构,又觉得内核编程很复杂,HelloWorld类型的程序实在说明不了什么 修改一下《windows设备驱动WDF开发》的CharSample,查了WDK帮助文档加上注释以帮助自己理解KMDF的大致运作过程。
CharSample原本是应用层输入数字字符,驱动读取输入缓冲区返还相应的中文,自己修改为返还英文(调试过程出现过数据类型的错误,Char CHAR int INT size_t 注意ANSI C的函数)
另外,KMDF的IO处理例程中Create Close Cleanup要自己处理 Read Write DeviceControl可由IO队列管理,所以自行添加一个Create例程
驱动:
//基本KMDF,全部内容写入同一源文件
#pragma warning(disable:4200) //
#pragma warning(disable:4201) // nameless struct/union
#pragma warning(disable:4214) // bit field types other than int
#include <ntddk.h>
#include <wdf.h>
#include <initguid.h>
#ifndef DEBUGGING
#define DEBUGGING 1
#endif
//全局标识符
DEFINE_GUID(CharSample_DEVINTERFACE_GUID, \
0xbd083159, 0xeb56, 0x437e, 0xbb, 0x98, 0x17, 0x65, 0xe4, 0x40, 0x81, 0xe);
//控制命令
#define CharSample_IOCTL_800 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
//全局变量
CHAR szEngNum[10][8]
={"zero",
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine"
};
//入口函数
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath);
//CharSample设备添加例程
NTSTATUS CharSample_EvtDeviceAdd(IN WDFDRIVER Driver,
IN PWDFDEVICE_INIT DeviceInit);
//DeviceIoControl例程
VOID CharSample_EvtIoDeviceControl
(IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN size_t OutputBufferLength,
IN size_t InputBufferLength,
IN ULONG IoControlCode
);
//Create例程(无操作)
VOID CharSample_EvtDeviceFileCreate(
IN WDFDEVICE Device,
IN WDFREQUEST Request,
IN WDFFILEOBJECT FileObject
);
//入口函数
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, //入口参数
IN PUNICODE_STRING RegistryPath) //入口参数
{
WDF_DRIVER_CONFIG config; //驱动对象配置结构
NTSTATUS status;
//_asm int 3;
//对象配置、指定设备添加例程入口
WDF_DRIVER_CONFIG_INIT(&config,CharSample_EvtDeviceAdd);
//对象配置、指定设备添加例程入口
status = WdfDriverCreate(
DriverObject,
RegistryPath,
WDF_NO_OBJECT_ATTRIBUTES, // Driver Attributes
&config, // Driver Config Info
WDF_NO_HANDLE // hDriver
);
return status;
}
//CharSample设备添加例程
NTSTATUS
CharSample_EvtDeviceAdd(
IN WDFDRIVER Driver,
IN PWDFDEVICE_INIT DeviceInit
)
{
NTSTATUS status;
WDFDEVICE device;
WDF_IO_QUEUE_CONFIG ioQueueConfig;
WDF_FILEOBJECT_CONFIG fileConfig; //文件配置信息
//例程的首句PAGED_CODE,表示该例程的代码占用分页内存。
//只能在PASSIVE_LEVEL中断级别调用该例程,否则会蓝屏。
//如不说明,则占用系统的非分页内存,要珍惜使用。
PAGED_CODE();
//设置Create例程
WDF_FILEOBJECT_CONFIG_INIT(
&fileConfig,
CharSample_EvtDeviceFileCreate,
WDF_NO_EVENT_CALLBACK,
WDF_NO_EVENT_CALLBACK
);
WdfDeviceInitSetFileObjectConfig(DeviceInit,&fileConfig,WDF_NO_OBJECT_ATTRIBUTES);
//创建设备,没有对象属性和设备对象环境变量结构
status = WdfDeviceCreate(&DeviceInit, WDF_NO_OBJECT_ATTRIBUTES, &device);
if (!NT_SUCCESS(status))
{
return status;
}
//初始化缺省队列配置,设置I/O请求分发处理方式为串行。
//对这个实例而言,选择串行或并行都可以,但不能选手工。
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, WdfIoQueueDispatchSequential);
//设置EvtIoDeviceControl例程,处理应用程序的DeviceIoControl()函数调用
ioQueueConfig.EvtIoDeviceControl = CharSample_EvtIoDeviceControl;
//创建队列
status = WdfIoQueueCreate(device, &ioQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, NULL);
if (!NT_SUCCESS(status)) {
return status;
}
//创建设备GUID接口
status = WdfDeviceCreateDeviceInterface(device, (LPGUID) &CharSample_DEVINTERFACE_GUID, NULL);
if (!NT_SUCCESS(status)) {
}
return status;
}
//DeviceIoControl例程
VOID
CharSample_EvtIoDeviceControl(
IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN size_t OutputBufferLength,
IN size_t InputBufferLength,
IN ULONG IoControlCode
)
{
NTSTATUS status;
PVOID buffer;
CHAR n;
INT len;
PAGED_CODE();
switch(IoControlCode) {
case CharSample_IOCTL_800:
if (InputBufferLength == 0 || OutputBufferLength < 2)
{ //检查输入、输出参数有效性
WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
}
else
{
//输入缓冲区地址可通过调用WdfRequestRetrieveInputBuffer函数获得
//输出缓冲区地址可通过调用WdfRequestRetrieveOutputBuffer函数获得
//获取输入缓冲区地址buffer
//要求1字节空间
status = WdfRequestRetrieveInputBuffer(Request, 1, &buffer, NULL);
if (!NT_SUCCESS(status)) {
WdfRequestComplete(Request, STATUS_UNSUCCESSFUL);
break;
}
//这里buffer表示输入缓冲区地址
//输入n=应用程序传给驱动程序的数字ASCII码
n = *(CHAR *)buffer;
// #if DEBUGGING
// _asm int 3
// #endif
if ((n>=‘0‘) && (n<=‘9‘))
{ //若为数字,则处理
n-=‘0‘; //n=数字(0-9)
len=strlen(szEngNum[n])+1;
//获取输出缓冲区地址buffer
status = WdfRequestRetrieveOutputBuffer(Request, (size_t)len, &buffer, NULL);
if (!NT_SUCCESS(status)) {
WdfRequestComplete(Request, STATUS_UNSUCCESSFUL);
break;
}
//这里buffer表示输出缓冲区地址
//输出:E文数组szEngNum[]中取出对应的数字的中文码,拷贝到输出缓冲区
strncpy((PCHAR)buffer,szEngNum[n],len);
//完成I/O请求,驱动程序传给应用程序的数据长度为len
WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, len);
}
else //否则返回无效参数
WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
}
break;
default :
status = STATUS_INVALID_DEVICE_REQUEST;
WdfRequestCompleteWithInformation(Request, status, 0);
break;
}
return;
}
//Create例程(无操作)
VOID CharSample_EvtDeviceFileCreate(
IN WDFDEVICE Device,
IN WDFREQUEST Request,
IN WDFFILEOBJECT FileObject)
{
NTSTATUS status=STATUS_SUCCESS;
WdfRequestComplete(Request,status);
}
应用层:
// Test_CharSample.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
#include <setupapi.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <winioctl.h>
#include "public.h"
PCHAR
GetDevicePath(
IN LPGUID InterfaceGuid
);
int main(int argc, char* argv[])
{
PCHAR DevicePath;
HANDLE hDevice = INVALID_HANDLE_VALUE;
printf("Application Test_CharSample starting...\n");
DevicePath = GetDevicePath((LPGUID)&CharSample_DEVINTERFACE_GUID);
hDevice = CreateFile(DevicePath,
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL );
if (hDevice == INVALID_HANDLE_VALUE) {
printf("ERROR opening device: (%0x) returned from CreateFile\n", GetLastError());
return 0;
}
printf("OK.\n");
CHAR bufInput[1]; // Input to device
CHAR bufOutput[10]; // Output from device
ULONG nOutput; // Count written to bufOutput
printf("请输入数字(0-9)\n");
l0: bufInput[0] = _getch();
if ((bufInput[0]<‘0‘) || (bufInput[0]>‘9‘)) goto l0;
_putch(bufInput[0]);
// Call device IO Control interface (CharSample_IOCTL_800) in driver
if (!DeviceIoControl(hDevice,
CharSample_IOCTL_800,
bufInput,
1,
bufOutput,
10,
&nOutput,
NULL)
)
{
printf("ERROR: DeviceIoControl returns %0x.", GetLastError());
goto exit;
}
printf("\n%s",bufOutput);
printf("\n");
exit:
if (hDevice != INVALID_HANDLE_VALUE) {
CloseHandle(hDevice);
}
return 0;
}
//根据全局ID获取设备路径
PCHAR
GetDevicePath(
IN LPGUID InterfaceGuid
)
{
HDEVINFO HardwareDeviceInfo;
SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
PSP_DEVICE_INTERFACE_DETAIL_DATA pDeviceInterfaceDetailData = NULL;
ULONG Length, RequiredLength = 0;
BOOL bResult;
//获取设备信息设置
HardwareDeviceInfo = SetupDiGetClassDevs(
InterfaceGuid,
NULL,
NULL,
(DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));
if (HardwareDeviceInfo == INVALID_HANDLE_VALUE)
{
printf("SetupDiGetClassDevs failed!\n");
exit(1);
}
DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
//设备存在 枚举接口
bResult = SetupDiEnumDeviceInterfaces(HardwareDeviceInfo,
0,
InterfaceGuid,
0,
&DeviceInterfaceData);
if (bResult == FALSE)
{
LPVOID lpMsgBuf;
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR) &lpMsgBuf,
0,
NULL
)) {
printf("Error: %s", (LPSTR)lpMsgBuf);
LocalFree(lpMsgBuf);
}
printf("SetupDiEnumDeviceInterfaces failed.\n");
SetupDiDestroyDeviceInfoList(HardwareDeviceInfo);
exit(1);
}
//获取设备接口的详细信息结构的大小
//通过两次调用 SetupDiGetDeviceInterfaceDetail
SetupDiGetDeviceInterfaceDetail(
HardwareDeviceInfo,
&DeviceInterfaceData,
NULL,
0,
&RequiredLength,
NULL
);
//详细信息结构初始化
pDeviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA) LocalAlloc(LMEM_FIXED, RequiredLength);
if (pDeviceInterfaceDetailData == NULL)
{
SetupDiDestroyDeviceInfoList(HardwareDeviceInfo);
printf("Failed to allocate memory.\n");
exit(1);
}
pDeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
Length = RequiredLength;
bResult = SetupDiGetDeviceInterfaceDetail(
HardwareDeviceInfo,
&DeviceInterfaceData,
pDeviceInterfaceDetailData,//成功调用此函数后pDeviceInterfaceDetailData指向有效的SP_DEVICE_INTERFACE_DETAIL_DATA
Length,
&RequiredLength,
NULL);
if (bResult == FALSE)
{
LPVOID lpMsgBuf;
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR) &lpMsgBuf,
0,
NULL
))
{
MessageBox(NULL, (LPCTSTR) lpMsgBuf, "Error", MB_OK);
LocalFree(lpMsgBuf);
}
printf("Error in SetupDiGetDeviceInterfaceDetail\n");
SetupDiDestroyDeviceInfoList(HardwareDeviceInfo);
LocalFree(pDeviceInterfaceDetailData);
exit(1);
}
return pDeviceInterfaceDetailData->DevicePath;
}
结果: