您好,用户模式?即插即用调用

您可以为从内核模式驱动程序到用户模式应用程序的简单单向通信使用即插即用通知。为此,使用描述事件的 TARGET_DEVICE_CUSTOM_NOTIFICATION 结构来调用 IoReportTargetDeviceChangeAsynchronous。这个例程通知即插即用管理器设备上发生了事件。

系统定义几种类型的即插即用事件来向设备接口、硬件配置文件或目标设备通知发生的改变。这些事件都是自动发送的,驱动程序方面不需要任何操作。驱动程序可以定义自定义事件,这些事件并非一定要与硬件相关。需要做的事情就是为事件发布一个 GUID 并使用 GUID 和关于事件的其他信息来填充 TARGET_DEVICE_CUSTOM_NOTIFICATION 结构。

任何数目的用户模式应用程序都可以从您的驱动程序注册 PnP 通知(通过调用 RegisterDeviceNotification 并传递一个包含设备句柄的 DEV_BROADCAST_HANDLE 通知筛选器)。(内核模式驱动程序通过调用 IoRegisterPlugPlayNotification 为特定的事件类别注册一个回调例程来注册系统定义的即插即用事件。)

当事件发生时,系统通过标准的 Windows 消息机制来通知所有注册的用户模式应用程序:每个注册的应用程序都接收到一条 WM_DEVICECHANGE 消息,其中包含一个 DBT_CUSTOMEVENT 的 WPARAM 和指向包含事件特定数据结构的 LPARAM)。

如果系统内存不足,那么即插即用通知无法保证正常工作。IoReportTargetDeviceChangeAsynchronous 在被调用时立即分配内存。如果分配失败,那么通知不会被发送。在用户模式中,系统必须为消息的 LPARAM 分配内存;如果分配失败,那么不会接收到通知。

而且,通知是短暂的,没有任何存储历史。假设内存分配成功,那么只接收到经过注册接收的通知。应用程序注册之前发送的通知不再可用。

最后,因为即插即用通知是单向通信,所以如果您的驱动程序必须与用户模式应用程序同步,您将需要使用不同的机制。共享事件和驱动程序定义的 IOCTL 是完成这项工作的两种方法。请参见 Windows DDK 中的 Event 例子来获取阐述这些技术的示例代码。

您应该做什么?

要使用即插即用通知与用户模式应用程序通信:

如果您正在定义一个自定义事件,那么使用 guidgen 或 uuidgen 为事件创建一个全局唯一标识符 (GUID),并在头文件中发布这个 GUID,以便需要通知的组件使用。

在您的内核模式驱动程序中,在事件发生时调用 IoReportTargetDeviceChangeAsynchronous,传递一个包含 GUID 和其他事件信息的 TARGET_DEVICE_CUSTOM_NOTIFICATION 结构。

要使用 IoReportTargetDeviceChangeAsynchronous,请包含 Ntddk.h 并链接 Wdm.lib 和 Ntoskrnl.lib。

在您的用户模式应用程序中,获取设备的句柄并通过调用 RegisterDeviceNotification 来注册通知,传递一个包含设备句柄和事件 GUID 的 DEV_BROADCAST_HANDLE 通知筛选器。

在您的用户模式应用程序中,处理带有 DBT_CUSTOMEVENT WPARAM 的 WM_DEVICECHANGE 消息。

代码示例

例子 1:广播事件的通知。下列代码示例显示内核模式驱动程序将如何使用 IoReportTargetDeviceChangeAsynchronous 来广播事件的通知(在这个例子中为 GUID_SOME_PNP_EVENT,包含消息 "Hello"。

typedef struct _EVENT_INFO { ULONG Count; WCHAR Message[32]; } EVENT_INFO;
 
UCHAR buffer[sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION) + sizeof(EVENT_INFO)]; PTARGET_DEVICE_CUSTOM_NOTIFICATION pNotify; UNALIGNED EVENT_INFO * pInfo;
 
RtlZeroMemory(buffer, sizeof(buffer));
 
pNotify = (PTARGET_DEVICE_CUSTOM_NOTIFICATION) buffer;
 
RtlCopyMemory(&pNotify->Event, &GUID_SOME_PNP_EVENT, sizeof(GUID));
 
pNotify->NameBufferOffset = -1; pNotify->Version = 1; pNotify->Size = sizeof(*pNotify) - sizeof(pNotify->CustomDataBuffer) + sizeof(*pInfo);
 
pInfo = (EVENT_INFO UNALIGNED *) &pNotify->CustomDataBuffer[0];
 
pInfo->Count = pdx->Count;
 
status = RtlStringCchCopyW(pInfo->Message, sizeof(pInfo->Message)/sizeof(pInfo->Message[0]), L"Hello!");
 
if (NT_SUCCESS(status)) { IoReportTargetDeviceChangeAsynchronous(pdx->Pdo, pNotify, NULL, NULL); }

例子 2:注册通知。下列代码示例显示用户模式应用程序将如何为一个接口类 GUID 注册设备接口中的更改通知。

BOOL DoRegisterDeviceInterface( GUID InterfaceClassGuid, HDEVNOTIFY *hDevNotify ) { DEV_BROADCAST_DEVICEINTERFACE NotificationFilter; DWORD Err;

ZeroMemory( &NotificationFilter, sizeof(NotificationFilter) ); NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; NotificationFilter.dbcc_classguid = InterfaceClassGuid;

*hDevNotify = RegisterDeviceNotification( hWnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE );

if(!*hDevNotify) { Err = GetLastError(); printf( "RegisterDeviceNotification failed:%lx.\n", Err); return FALSE; }

return TRUE; }

例子 3:处理自定义事件。下列代码示例显示用户模式应用程序将如何为自定义事件处理 WM_DEVICECHANGE 消息。请参见 Windows DDK 中的 Notify 例子以获取演示如何注册和处理即插即用通知的更多代码。

case WM_DEVICECHANGE:OnDeviceChange(Wparam, (_DEV_BROADCAST_HEADER *) Lparam); break;

void OnDeviceChange( ULONG EventCode, _DEV_BROADCAST_HEADER *Hdr ) { PDEV_BROADCAST_HANDLE pHdrHandle;

pHdrHandle = (PDEV_BROADCAST_HANDLE) Hdr;

if (EventCode == DBT_CUSTOMEVENT) { if (memcmp(&pHdrHandle->dbch_eventguid, &GUID_PNPEVENT_EVENT, sizeof(GUID)) == 0) { PEVENT_INFO pEventInfo = (PEVENT_INFO) pHdrHandle->dbch_data;

... } else { // handle other event guids here } } else { // handle other EventCodes here } }

		

更多信息:

用户模式交互:内核模式驱动程序指南

Windows DDK:
使用 PnP 通知
定义和导出新的 GUID
IoReportTargetDeviceChangeAsynchronous
TARGET_DEVICE_CUSTOM_NOTIFICATION
事件例子 - %winddk%\src\general\event\
通知例子 - %winddk%\src\general\toaster\exe\notify\

Platform SDK:
RegisterDeviceNotification
WM_DEVICECHANGE



此信息有用吗?