IoRegisterPlugPlayNotification函数的作用是为指定guid的设备注册一个回调函数,当进入设备驱动并调用IoSetDeviceInterfaceState使能设备访问接口后,让系统调用之前注册的回调函数,以实现通知的目的.wrk1.2源码中并没有IO管理器的实现,因此,只能退而求其次,参考ReactOS中关于IoRegisterPlugPlayNotification的实现.
ReactOS在系统初始化阶段调用PoInitSystem初始化电源管理器时,会以
IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
0, /* The registry has not been initialized yet */
(PVOID)&GUID_DEVICE_SYS_BUTTON,
IopRootDeviceNode->
PhysicalDeviceObject->DriverObject,
PopAddRemoveSysCapsCallback,
NULL,
&NotificationEntry);
的形式为集成在主板按键上的调用IoRegisterPlugPlayNotification.
参数1 EventCategoryDeviceInterfaceChange表明Pnp管理器在接到设备接口状态变化(Enable/Disable)时调用先前注册的回调函数;
参数3 GUID_DEVICE_SYS_BUTTON是指被IoSetDeviceInterfaceState操作的接口;
参数5和6是回调函数的入口地址和参数;
不得不说,系统中注册回调函数都有一定的相似性,步骤可归纳为2步:1).为一个Entry分配pool内存,并用回调函数入口地址和参数Context初始化Entry,并初始化其他域(如回调条件);2).将刚分配的Entry结构以互斥的方式加入到内核中相应的队列中.IoRegisterPlugPlayNotification也是如此:
{
//分配池内存
Entry = ExAllocatePoolWithTag(
NonPagedPool,
sizeof(PNP_NOTIFY_ENTRY),
TAG_PNP_NOTIFY);
//从interface获得设备的名字
Status = IoGetDeviceInterfaces(
(LPGUID)EventCategoryData,
NULL, /* PhysicalDeviceObject OPTIONAL */
0, /* Flags */
&SymbolicLinkList);
NotificationInfos.Version = 1;//以下是初始化Entry结构
NotificationInfos.Size = sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION);
RtlCopyMemory(&NotificationInfos.Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof(GUID));
RtlCopyMemory(&NotificationInfos.InterfaceClassGuid, EventCategoryData, sizeof(GUID));
Entry->PnpNotificationProc = CallbackRoutine;
Entry->EventCategory = EventCategory;
Entry->Context = Context;
//最后,获得互斥锁,并插入PnpNotifyListHead
KeAcquireGuardedMutex(&PnpNotifyListLock);
InsertHeadList(&PnpNotifyListHead,
&Entry->PnpNotifyList);
KeReleaseGuardedMutex(&PnpNotifyListLock);
}
回调函数注册后,将在什么情况下才会被调用?通过搜索对PnpNotifyListHead的引用,可以发现端倪:
Pnpnotify.c!IopNotifyPlugPlayNotification中遍历PnpNotifyListHead:
ListEntry = PnpNotifyListHead.Flink;
while (ListEntry != &PnpNotifyListHead)
{
ChangeEntry = CONTAINING_RECORD(ListEntry, PNP_NOTIFY_ENTRY,
PnpNotifyList);
ListEntry = ListEntry->Flink;
}
这是典型的遍历队列操作啊,有必要介绍一下IopNotifyPlugPlayNotification的流程:
一般IoSetDeviceInterfaceState会以下列方式调用IopNotifyPlugPlayNotification
EventGuid = Enable ? &GUID_DEVICE_INTERFACE_ARRIVAL : &GUID_DEVICE_INTERFACE_REMOVAL;
IopNotifyPlugPlayNotification(
PhysicalDeviceObject,
EventCategoryDeviceInterfaceChange,
EventGuid,
&GuidString,
(PVOID)SymbolicLinkName);
当IopNotifyPlugPlayNotification被调用时,它搜索PnpNotifyListHead链表,逐项比对已注册在PnpNotifyListHead队列中的PNP_NOTIFY_ENTRY各项,如果PNP_NOTIFY_ENTRY中接口GUID和产生设备插拔事件的接口GUID相同,则调用PNP_NOTIFY_ENTRY中的回调函数,去完成一些挂起的IRP请求.