UMDF驱动开发入门:创建虚拟设备,从安装到I/O交互全解析

news/2025/10/21 18:24:08/文章来源:https://www.cnblogs.com/duwenlong/p/19156137

这篇博客详细介绍了UMDF驱动的基本概念、生命周期、代码实现和应用交互,适合初学者入门

• 目的:创建一个“虚拟设备”(软件模拟的设备),让用户模式应用程序(比如你的C#或C++程序)能与之“对话”。它不控制真实硬件(如USB设备),而是演示驱动的基本流程:加载、创建设备、处理请求。
• 为什么用UMDF? UMDF让驱动运行在用户模式(非内核),更安全稳定。内核驱动(如KMDF)风险高,UMDF适合简单任务。
• 示例功能:驱动加载后,注册一个设备接口。应用能打开设备、发送“控制请求”(如自定义命令),驱动简单响应(目前只返回成功)。你可以扩展它做监控、数据处理等。
• 文件结构(从ReadMe.txt):
• Device.c & Device.h:设备创建和上下文。

• Driver.c:驱动入口和全局回调。
• Queue.c:I/O请求队列处理。
• Public.h:共享定义(如设备接口GUID)。
• UMDFTest.inf:安装文件(定义硬件ID、服务等)。
• 其他:跟踪日志、项目配置。

驱动的生命周期(从加载到运行)

驱动像“服务”一样工作。安装后,它不直接运行,而是等系统调用。
• 入口点在哪里?
• 全局入口:DriverEntry 函数(在Driver.c)。这是驱动的“起点”,系统加载DLL时第一个调用它。
• 它初始化WDF框架、注册回调(如设备添加事件)、设置跟踪日志。
• 如果失败,驱动不加载。
• 设备入口:UMDFTestEvtDeviceAdd(也在Driver.c),PnP(即插即用)系统添加设备时调用。它调用UMDFTestCreateDevice(在Device.c)创建设备对象。
• 安装后:驱动作为UMDF服务运行在WUDFHost.exe进程中(用户模式主机)。系统启动它时,调用DriverEntry,然后创建设备。
• 安装过程(手动做的):
• 用pnputil /add-driver UMDFExample.inf /install安装INF文件。 //安装后出现在system devices下面名字叫做UMDFExampledevices
• 系统注册设备(硬件ID Root\UMDFExample),加载DLL到WUDFHost。
• 设备出现在设备管理器(“UMDFExample Device”),状态“正常”。 //没找到

  1. 其他程序怎么用这个驱动?入口点在哪里?
    驱动不是“程序”,它是系统服务。其他程序(应用)通过设备接口与之交互,像打开文件一样。
    • 入口点main(应用侧):
    • 设备接口GUID:GUID_DEVINTERFACE_UMDFExample(在Public.h定义,值是{8d385c4d-4fa8-413c-9307-a8fa3e768390})。这是“门牌号”,应用用它找到设备。
    • 打开设备:用CreateFile API(Windows函数),路径是\.\GUID字符串(如\.{8d385c4d-4fa8-413c-9307-a8fa3e768390})。成功返回句柄(handle),像文件句柄。
    • 发送消息: 使用DeviceIoControl API(windows函数),成功的话会返回DeviceIoControl succeeded 返回的实际内容如下:bytesReturned,

    BOOL result = DeviceIoControl(hDevice, // 1. 设备句柄
    IOCTL_UMDFTEST_HELLO,  // 2. 控制码
    (LPVOID)inputBuffer,   // 3. 输入缓冲区指针
    inputSize,             // 4. 输入缓冲区大小
    outputBuffer,          // 5. 输出缓冲区指针
    outputSize,            // 6. 输出缓冲区大小
    &bytesReturned,        // 7. 接收实际返回字节数的变量指针
    nullptr);              // 8. 用于异步操作,此处为同步
    

    3.驱动层先不细讲,在驱动里面UMDFTestEvtIoDeviceControl是一个回调函数,用于处理来自用户模式应用程序的 设备控制请求(IOCTL)。当你的应用调用 DeviceIoControl 时,UMDF 框架会自动调用这个函数来处理请求。再博客的最后会写个例子讲解这个函数详细的代码内容

    VOID UMDFTestEvtIoDeviceControl( _In_ WDFQUEUE Queue, // 队列句柄(框架管理的 I/O 队列) 
    _In_ WDFREQUEST Request, // 请求句柄(包含输入/输出缓冲区和请求详情) 
    _In_ size_t OutputBufferLength, // 输出缓冲区大小(字节) 
    _In_ size_t InputBufferLength, // 输入缓冲区大小(字节) 
    _In_ ULONG IoControlCode // IOCTL 控制码(唯一标识请求类型,如 IOCTL_UMDFTEST_HELLO)
    )
    

• 返回类型:VOID(无返回值,因为处理结果通过 Request 对象传递)。
• 参数:
• Queue:指向队列对象的句柄(你不需要直接操作它)。
• Request:核心对象,封装了请求的所有数据(输入缓冲区、输出缓冲区等)。这是你处理数据的入口。
• OutputBufferLength / InputBufferLength:缓冲区大小,用于安全检查(防止缓冲区溢出)。
• IoControlCode:一个数字,标识具体的 IOCTL 类型(例如,你的代码中定义的 IOCTL_UMDFTEST_HELLO)。
• In 注解:SAL (Source Annotation Language) 注解,表示这些参数是输入的(只读)。

检查是否还有 pnputil /enum-drivers | findstr /i UMDFTest

我们讲解一下exe要加载驱动的时候传入的参数和驱动的inf的对应关系

SW_DEVICE_CREATE_INFO createInfo = { 0 };
createInfo.cbSize = sizeof(SW_DEVICE_CREATE_INFO);
createInfo.pszInstanceId = L"umdftest";  // 实例ID   
createInfo.pszzHardwareIds = L"Root\\UMDFTest\0\0";    // 硬件ID,与INF文件匹配
createInfo.pszzCompatibleIds = nullptr;  // 兼容ID,无需设置
createInfo.pContainerId = nullptr;               // 容器ID
createInfo.CapabilityFlags = SWDeviceCapabilitiesDriverRequired;  // 能力标志,需要驱动程序
createInfo.pszDeviceDescription = L"UMDF Test Device";   // 设备描述
createInfo.pszDeviceLocation = nullptr;          // 设备位置
createInfo.pSecurityDescriptor = nullptr;        // 安全描述符HRESULT hr = SwDeviceCreate(L"umdftest", L"HTREE\\ROOT\\0", &createInfo, 0, nullptr, SwDeviceCreatedCallback, nullptr, &g_SwDevice);

• createInfo.pszInstanceId(实例ID,如L"umdfexample"):这不是直接对应INF中的某个字段,而是软件设备的唯一标识符,用于区分多个实例。它与INF中的硬件ID(Root\UMDFExample)配合使用,但INF本身不定义实例ID。你可以自定义,只要在代码中保持一致即可。
• SwDeviceCreate的第一个参数(pszEnumeratorName,如L"umdfexample"):这是枚举器名称,通常设置为与pszInstanceId相同的值,用于标识创建设备的枚举器。它也不直接对应INF中的特定字段,而是系统内部使用的标识符。

每次编译并修改代码后重复安装INF时,系统会将每个INF作为独立的驱动包添加,导致多个oemXX.inf文件(例如oem40.inf和oem54.inf)。即使硬件ID相同,pnputil也会创建新包,而不是覆盖旧的。这是因为驱动包是基于INF内容的哈希值管理的,如果内容不同(即使硬件ID相同),会被视为新包。
对于软件设备(如SWD\umdfexample\umdfexample),可能会出现多个设备实例或冲突。
正常更新驱动的处理步骤:

  1. 停止相关应用程序和服务:确保没有进程使用驱动。
  2. 删除旧驱动包:
    • 运行 pnputil /enum-drivers 查看所有驱动包。
    • 删除旧的:pnputil /delete-driver oem40.inf(替换为实际名称)。
  3. 卸载设备实例(如果存在):
    • 使用设备管理器:找到设备(例如“UMDF Test Device”),右键卸载。
    • 或使用命令:pnputil /remove-device "SWD\umdfexample\umdfexample"(替换为实际设备ID)。
  4. 安装新驱动:
    • 复制新编译的INF和DLL到目录。
    • 运行 pnputil /add-driver UMDFExample.inf /install。
  5. 重启系统(如果需要):有时软件设备需要重启才能生效。
  6. 测试:运行应用程序验证新代码。
    如果只想强制更新而不删除,可以尝试 pnputil /add-driver UMDFExample.inf /install /force,但这可能不总是可靠。建议始终删除旧包以避免冲突。

驱动层代码详细分析

1.INF安装阶段(驱动注册)

我们从编译完成之后的安装开始:

•     首先命令执行:pnputil /add-driver UMDFExample.inf /install 将INF文件添加到系统驱动存储中,生成一个oemXX.inf文件(例如oem54.inf)。

• 驱动代码经历:此时驱动代码(UMDFExample.dll)尚未加载。INF只是注册了驱动包,包括硬件ID(Root\UMDFExample)、服务名(UMDFExample)和DLL路径。系统知道“如果有匹配的硬件ID,就加载这个驱动”。
• 结果:驱动包已安装,但没有设备实例。驱动代码静止。

2.设备枚举与发现阶段(PnP触发)

• 触发条件:当应用程序运行 SwDeviceCreate(在UMDFConsole.cpp中)时,系统创建软件设备实例(SWD\umdfexample\umdfexample),硬件ID为Root\UMDFExample。
• PnP管理器动作:PnP检测到新设备,匹配INF中的硬件ID,决定加载对应的驱动。
• 驱动代码经历:WUDFHost进程启动(如果未运行)。系统启动WUDFHost.exe(用户模式主机进程),并加载UMDFExample.dll到该进程中。
• DriverEntry调用:这是驱动的入口点(在Driver.c中)。DriverEntry 初始化WDF框架,注册设备创建回调(UMDFExampleCreateDevice)。
• 此时,驱动开始“活起来”,但设备对象尚未创建。

3. 设备创建与初始化阶段

• UMDFExampleCreateDevice(在Device.c中):创建WDFDEVICE对象,初始化设备上下文(deviceContext->PrivateDeviceData = 0),并创建设备接口(WdfDeviceCreateDeviceInterface,使用GUID_DEVINTERFACE_UMDFExample)。这允许应用程序通过SetupAPI找到设备路径。
• UMDFExampleQueueInitialize(在Queue.c中):创建I/O队列(WDFQUEUE),配置并行处理,注册事件回调(如EvtIoDeviceControl和EvtIoStop)。
• 设备现在“可用”,应用程序可以打开设备句柄(CreateFile)。

4. I/O请求处理阶段(运行时)

• 触发条件:应用程序调用 DeviceIoControl 发送IOCTL请求(例如IOCTL_UMDFTEST_HELLO)。
• 驱动代码经历:
• 请求进入队列,调用 UMDFExampleEvtIoDeviceControl(在Queue.c中)。
• 检查IoControlCode(如果是IOCTL_UMDFTEST_HELLO)。
• 获取输入/输出缓冲区。
• 处理逻辑:拼接字符串("hi client this as drive,i received message as " + 输入),设置bytesReturned。
• 调用 WdfRequestCompleteWithInformation 完成请求,返回数据给应用程序。
• 如果不支持的IOCTL,调用 WdfRequestComplete 返回STATUS_INVALID_DEVICE_REQUEST。
• 其他事件如EvtIoStop在电源管理时调用(目前返回,无额外处理)。

5.设备移除与卸载阶段

• 触发条件:应用程序调用 SwDeviceClose,或系统重启/卸载驱动。
• 驱动代码经历:
• PnP调用设备移除回调(如果有),清理资源。
• WUDFHost进程卸载DLL,驱动对象销毁。
• 如果删除驱动包(pnputil /delete-driver),INF被移除,下次需要重新安装。

关键注意事项

• 用户模式特性:UMDF运行在WUDFHost中,崩溃不会蓝屏,但权限受限(不能直接访问硬件)。
• 调试:使用WDF Verifier或ETW跟踪(TraceEvents)查看日志。
• 错误处理:如果SwDeviceCreate失败(权限问题),驱动不会加载。确保管理员运行应用程序。
• 生命周期总结:从安装到卸载,驱动代码只在设备存在时活跃。软件设备依赖应用程序创建/销毁。

UMDFExampleEvtIoDeviceControl 函数的详细讲解

当应用程序(UMDFConsole.cpp)调用 DeviceIoControl 发送字符串 "Hello from App" 时,驱动端(UMDFExample.dll,在WUDFHost进程中运行)会经历以下步骤处理请求。假设IOCTL码为IOCTL_UMDFTEST_HELLO(匹配驱动中的检查):###

我自己的代码如下:

VOID
UMDFExampleEvtIoDeviceControl(_In_ WDFQUEUE Queue,_In_ WDFREQUEST Request,_In_ size_t OutputBufferLength,_In_ size_t InputBufferLength,_In_ ULONG IoControlCode)
/*++Routine Description:This event is invoked when the framework receives IRP_MJ_DEVICE_CONTROL request.Arguments:Queue -  Handle to the framework queue object that is associated with theI/O request.Request - Handle to a framework request object.OutputBufferLength - Size of the output buffer in bytesInputBufferLength - Size of the input buffer in bytesIoControlCode - I/O control code.Return Value:VOID--*/
{if (IoControlCode == IOCTL_UMDFTEST_HELLO) {  // 检查控制码PVOID inputBuffer = NULL;PVOID outputBuffer = NULL;size_t bytesReturned = 0;// 获取缓冲区if (NT_SUCCESS(WdfRequestRetrieveInputBuffer(Request, 0, &inputBuffer, NULL)) &&NT_SUCCESS(WdfRequestRetrieveOutputBuffer(Request, 0, &outputBuffer, NULL))) {// 处理:拼接字符串并返回const char* prefix = "hi client this as drive,i received message as ";size_t prefixLen = strlen(prefix);size_t inputLen = InputBufferLength > 0 ? InputBufferLength - 1 : 0; // 假设输入是null-terminated字符串size_t totalLen = prefixLen + inputLen + 1; // +1 for null terminatorif (OutputBufferLength >= totalLen) {memcpy(outputBuffer, prefix, prefixLen);if (inputLen > 0) {memcpy((char*)outputBuffer + prefixLen, inputBuffer, inputLen);}((char*)outputBuffer)[totalLen - 1] = '\0'; // null terminatebytesReturned = totalLen;} else {// 输出缓冲区太小,返回错误WdfRequestComplete(Request, STATUS_BUFFER_TOO_SMALL);return;}}TraceEvents(TRACE_LEVEL_INFORMATION,TRACE_QUEUE,"%!FUNC! Queue 0x%p, Request 0x%p OutputBufferLength %d InputBufferLength %d IoControlCode %d",Queue, Request, (int)OutputBufferLength, (int)InputBufferLength, IoControlCode);// 完成请求并指定返回字节数WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, bytesReturned);}else {WdfRequestComplete(Request, STATUS_INVALID_DEVICE_REQUEST);  // 不支持的 IOCTL}return;
}

1.请求到达驱动

• DeviceIoControl 通过设备句柄发送IRP(I/O Request Packet)到内核,内核转发给UMDF框架。
• UMDF将请求分发到I/O队列(在Queue.c的UMDFExampleQueueInitialize中创建)。
• 调用 UMDFExampleEvtIoDeviceControl 回调函数(在Queue.c中)。

2.检查IOCTL码

• 驱动检查 IoControlCode 是否等于 IOCTL_UMDFTEST_HELLO(定义在Public.h中)。
• 如果匹配,继续处理;否则,返回 STATUS_INVALID_DEVICE_REQUEST。

3.获取输入/输出缓冲区

• 调用 WdfRequestRetrieveInputBuffer 获取输入缓冲区指针(包含 "Hello from App",长度为 InputBufferLength,包括null终止符)。
• 调用 WdfRequestRetrieveOutputBuffer 获取输出缓冲区指针(应用程序提供的256字节缓冲区)。

4.处理逻辑(拼接字符串)

• 计算前缀:"hi client this as drive,i received message as "(长度约50字节)。
• 输入长度:InputBufferLength - 1(假设null-terminated,去掉null)。
• 总长度:前缀 + 输入 + 1(null终止符)。
• 检查输出缓冲区是否足够大(OutputBufferLength >= totalLen)。
• 如果足够:
• 使用 memcpy 复制前缀到输出缓冲区。
• 复制输入字符串到输出缓冲区后面。
• 添加null终止符。
• 设置 bytesReturned = totalLen。
• 如果不够,返回 STATUS_BUFFER_TOO_SMALL。

5.完成请求

• 调用 WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, bytesReturned),将拼接后的字符串(例如 "hi client this as drive,i received message as Hello from App")返回给应用程序。
• 应用程序在 outputBuffer 中接收数据,bytesReturned 指示实际返回字节数。

关键点

• 输入:"Hello from App"(字符串,驱动假设null-terminated)。
• 输出:拼接后的字符串,长度取决于输入。
• 错误处理:如果缓冲区小,返回错误;不支持IOCTL也返回错误。
• 日志:TraceEvents 记录调试信息(队列、请求、缓冲区大小等)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/942557.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从零开始,搭建自己的AI平台写小说

一、安装模型 1.1 安装modelscope 1.2 使用脚本下载 from modelscope import snapshot_download model_dir = snapshot_download(Qwen/Qwen3-14B-GGUF)二、安装llama 1.1 下载 打开 llama.cpp GitHub Releases 页面 ht…

2025年AI优化公司电话推荐:十家可验证服务商沟通备忘

您想快速锁定具备真实技术积累、可电话核实、且愿意提供案例背调的GEO优化服务商,而非被营销话术包围。为此,我交叉比对了公开工商信息、主流平台备案案例与第三方招聘网站的技术岗位披露,筛掉成立不足两年、专利与…

2025深圳离婚律所电话推荐:家理律所福田诺德中心25楼

您此刻检索“深圳离婚律所电话”,核心诉求是快速锁定一家在本地法院系统有长期沉淀、流程透明、可立即联系的婚姻家事团队,并验证其真实办公地址与既往案例密度。为此,我交叉比对了深圳市律师协会2024年度行业报告、…

1242. 多线程网页爬虫

1242. 多线程网页爬虫 题目描述给你一个初始地址 startUrl 和一个 HTML 解析器接口 HtmlParser,请你实现一个 多线程的网页爬虫,用于获取与 startUrl 有 相同主机名 的所有链接。 以 任意 顺序返回爬虫获取的路径。…

使用SpringBoot + Thymeleaf + MyBatisPlus实现一个简单的书籍管理系统-demo2

一 准备环境 1 数据库环境搭建 CREATE DATABASE IF NOT EXISTS db_book; USE db_book;CREATE TABLE book (id INT PRIMARY KEY AUTO_INCREMENT COMMENT 书籍唯一标识,name VARCHAR(255) NOT NULL COMMENT 书籍名称,aut…

2025年深圳离婚律所电话推荐:家理福田诺德中心婚姻家事专线

您正在寻找能够在深圳本地高效、合规地处理离婚事务的律师事务所,核心诉求是“可靠、可验证、可直接联系”。为此,我交叉比对了深圳市律师协会官网公示信息、中国裁判文书网近三年的婚姻家事案例数据,以及公开工商登…

生日

RT。十六岁了。常有欣喜,常有感叹;岁岁更进,年年生辉。别的话也不多说,继续加油,即使孤身一人,即使前路茫然。又有何妨?问路在何处?路在脚下。

2025年润滑油厂家权威推荐榜:工业润滑油,汽车润滑油,发动机润滑油,甲醇发动机润滑油,全合成润滑油,长效发动机润滑油品牌深度解析

2025年润滑油厂家权威推荐榜:工业润滑油,汽车润滑油,发动机润滑油,甲醇发动机润滑油,全合成润滑油,长效发动机润滑油品牌深度解析 行业背景与发展趋势 随着全球工业化和汽车产业的快速发展,润滑油行业正经历着深…

2025固定资产管理系统电话推荐:公贝资产全周期管理方案

您正在寻找一套能够把采购、折旧、盘点、报废串成一条线的固定资产管理平台,希望先通过电话快速验证产品边界、实施周期和后续服务。为了节省您首轮筛选时间,我依据公开招投标信息、工信部备案数据以及企业官网披露资…

如果使用 vxe-table 实现全键盘操作,按键切换复选框单选框的选中状态

如果使用 vxe-table 实现全键盘操作,按键切换复选框单选框的选中状态,当遇到特点需求,比如需要通过按键来选中复选框或单选框时,该功能就非常有用了。 查看官网:https://vxetable.cn gitbub:https://github.com/…

2025年超声波清洗机厂家电话推荐:广东洁泰设备选型与联系指引

您正在寻找一家在超声波清洗领域有真实量产经验、能提供非标定制且售后响应可验证的国内厂家。为了避免信息碎片化带来的比对偏差,我调取了工商注册信息、专利公示库、环保备案号及第三方行业报告,把能交叉验证的线索…

2025年超声波清洗机厂家电话推荐:广东洁泰超声设备有限公司

您正在寻找一家在超声波清洗领域拥有长期沉淀、能够提供非标定制且售后响应迅速的国内厂家,核心诉求集中在技术可靠性、行业案例与后续服务保障。为确保信息可交叉验证,我仅以公开工商登记、专利数据库、第三方行业报…

2025年上海装修公司电话推荐:极家与俞润本土双选参考

您正在寻找在上海本地具备规模、资质与口碑的装修服务商,希望先通过电话沟通快速了解设计能力、施工标准与售后政策,再决定是否深入面谈。以下两家企业均持有国家二级及以上施工资质,门店与展厅可公开参观,联系方式…

2025年激光切割机厂家电话推荐:济南邦德激光4009917771技术对接通道.

您正在寻找可直接沟通的激光切割设备源头厂家,核心诉求是快速验证企业真实性与技术实力,并获取官方售前支持。为此,我交叉比对了海关总署出口名录、全国工商联装备业分会年报、以及上交所科创板公开问询回复,筛选出…

2025年岗亭定制厂家电话推荐:法利莱集团连锁服务网络覆盖多省市

您正在寻找一家能把图纸变成实物、把工期写进合同、把售后落到网点的岗亭定制厂家,核心诉求无非三点:方案可靠、价格透明、响应及时。为了把“听起来不错”变成“看得见摸得着”,我调取了行业协会近三年的会员名录、…

各项任务完成时间统计

计划:大约2天左右调查背景和构思 开发:约1天整 记录用时:总计3天左右 事后总结:完成代码时注释十分必要,有助于他人理解和自己回顾,同时软件文档的撰写也是很重要的

AI转型困境:仅13%企业实现技术落地

最新研究报告显示,尽管87%企业高管认为AI将在未来一年彻底改变组织架构,但仅13%企业成功落实AI转型。调查发现企业普遍存在技术基础设施不完善、员工技能不足等实施障碍,揭示了AI愿景与现实落地之间的巨大差距。多方…

软件工程结对项目-小学四则运算题目生成与判题程序

软件工程结对项目-小学四则运算题目生成与判题程序软件工程结对项目 一、项目参与成员 计算机科学与技术3班 许晓喆 3223004302这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScie…

哑演算基础理论

the-umbral-calculus前言: 这篇文章基本是关于 Steven Roman, The Umbral Calculus 1~3 章的中文翻译(其实可能不太算翻译了,改的原文结构还是挺多的),原文可从 Z-Library 下载 . 有问题请在评论区反馈!\[\newco…

2025年激光切割机厂家电话推荐:济南邦德激光股份有限公司4009917771

您正在寻找一台性能稳定、售后响应及时的激光切割机,并希望直接对接具备规模与出口经验的厂家。为了降低信息噪音,我调阅了海关总署、山东省工信厅公开数据,以及近五年《中国激光产业发展报告》中“金属成形激光设备…