Other Configuration Functionality

Other Configuration Functionality

Up to this point, I ve talked about the important concepts you need to know to write a hardware device driver. I ll discuss two less important minor function codes IRP_MN_FILTER_RESOURCE_REQUIREMENTS and IRP_MN_DEVICE_ USAGE_NOTIFICATION that you might need to process in a practical driver. Finally I ll mention how you can register to receive notifications about PnP events that affect devices other than your own.

Filtering Resource Requirements

Sometimes the PnP Manager is misinformed about the resource requirements of your driver. This can occur because of hardware and firmware bugs, mistakes in the INF file for a legacy device, or other reasons. The system provides an escape valve in the form of the IRP_MN_FILTER_RESOURCE_REQUIREMENTS request, which affords you a chance to examine and possibly alter the list of resources before the PnP Manager embarks on the arbitration and assignment process that culminates in your receiving a start device IRP.

When you receive a filter request, the FilterResourceRequirements substructure of the Parameters union in your stack location points to an IO_RESOURCE_REQUIREMENTS_LIST data structure that lists the resource requirements for your device. In addition, if any of the drivers above you have processed the IRP and modified the resource requirements, the IoStatus.Information field of the IRP will point to a second IO_RESOURCE_REQUIRE MENTS_LIST, which is the one from which you should work. Your overall strategy will be as follows: If you want to add a resource to the current list of requirements, you do so in your dispatch routine. Then you pass the IRP down the stack synchronously that is, by using the ForwardAndWait method you use with a start device request. When you regain control, you can modify or delete any of the resource descriptions that appear in the list.

Here s a brief and not very useful example that illustrates the mechanics of the filtering process:

NTSTATUS HandleFilterResources(PDEVICE_OBJECT fdo, PIRP Irp) { PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension; PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp); 

PIO_RESOURCE_REQUIREMENTS_LIST original = stack->Parameters .FilterResourceRequirements.IoResourceRequirementList;

PIO_RESOURCE_REQUIREMENTS_LIST filtered = (PIO_RESOURCE_REQUIREMENTS_LIST) Irp->IoStatus.Information;

PIO_RESOURCE_REQUIREMENTS_LIST source = filtered ? filtered : original;

if (source->AlternativeLists != 1) return DefaultPnpHandler(fdo, Irp);

ULONG sizelist = source->ListSize; PIO_RESOURCE_REQUIREMENTS_LIST newlist = (PIO_RESOURCE_REQUIREMENTS_LIST) ExAllocatePool(PagedPool, sizelist + sizeof(IO_RESOURCE_DESCRIPTOR)); if (!newlist) return DefaultPnpHandler(fdo, Irp); RtlCopyMemory(newlist, source, sizelist);

newlist->ListSize += sizeof(IO_RESOURCE_DESCRIPTOR); PIO_RESOURCE_DESCRIPTOR resource = &newlist->List[0].Descriptors[newlist->List[0].Count++]; RtlZeroMemory(resource, sizeof(IO_RESOURCE_DESCRIPTOR)); resource->Type = CmResourceTypeDevicePrivate; resource->ShareDisposition = CmResourceShareDeviceExclusive; resource->u.DevicePrivate.Data[0] = 42;

Irp->IoStatus.Information = (ULONG_PTR) newlist; if (filtered && filtered != original) ExFreePool(filtered);

NTSTATUS status = ForwardAndWait(fdo, Irp); if (NT_SUCCESS(status)) { // stuff }

Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; }

  1. The parameters for this request include a list of I/O resource requirements. These are derived from the device s configuration space, the registry, or wherever the bus driver happens to find them.

  2. Higher-level drivers might have already filtered the resources by adding requirements to the original list. If so, they set the IoStatus.Information field to point to the expanded requirements list structure.

  3. If there s no filtered list, we ll extend the original list. If there s a filtered list, we ll extend that.

  4. Theoretically, several alternative lists of requirements could exist, but dealing with that situation is beyond the scope of this simple example.

  5. We need to add any resources before we pass the request down the stack. First we allocate a new requirements list and copy the old requirements into it.

  6. Taking care to preserve the preexisting order of the descriptors, we add our own resource description. In this example, we re adding a resource that s private to the driver.

  7. We store the address of the expanded list of requirements in the IRP s IoStatus.Information field, which is where lower-level drivers and the PnP system will be looking for it. If we just extended an already filtered list, we need to release the memory occupied by the old list.

  8. We pass the request down using the same ForwardAndWait helper function that we used for IRP_MN_START_DEVICE. If we weren t going to modify any resource descriptors on the IRP s way back up the stack, we could just call DefaultPnpHandler here and propagate the returned status.

  9. When we complete this IRP, whether we indicate success or failure, we must take care not to modify the Information field of the I/O status block: it might hold a pointer to a resource requirements list that some driver maybe even ours! installed on the way down. The PnP Manager will release the memory occupied by that structure when it s no longer needed.

Device Usage Notifications

Disk drivers (and the drivers for disk controllers) in particular sometimes need to know extrinsic facts about how they re being used by the operating system, and the IRP_MN_DEVICE_USAGE_NOTIFICATION request provides a means to gain that knowledge. The I/O stack location for the IRP contains two parameters in the Parameters.UsageNotification substructure. See Table 6-4. The InPath value (a Boolean) indicates whether the device is in the device path required to support that usage, and the Type value indicates one of several possible special usages.

Table 6-4. Fields in the Parameters.UsageNotification Substructure of an I/O Stack Location

Parameter

Description

InPath

TRUE if device is in the path of the Type usage; FALSE if not

Type

Type of usage to which the IRP applies

In the subdispatch routine for the notification, you should have a switch statement (or other logic) that differentiates among the notifications you know about. In most cases, you ll pass the IRP down the stack. Consequently, a skeleton for the subdispatch function is as follows:

NTSTATUS HandleUsageNotification(PDEVICE_OBJECT fdo, PIRP Irp) { PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension; PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp); DEVICE_USAGE_NOTIFICATION_TYPE type = stack->Parameters.UsageNotification.Type; BOOLEAN inpath = stack->Parameters.UsageNotification.InPath; switch (type) { case DeviceUsageTypeHibernation:  Irp->IoStatus.Status = STATUS_SUCCESS; break; case DeviceUsageTypeDumpFile:  Irp->IoStatus.Status = STATUS_SUCCESS; break; case DeviceUsageTypePaging:  Irp->IoStatus.Status = STATUS_SUCCESS; break; default: break; } return DefaultPnpHandler(fdo, Irp); }

Set the Status field of the IRP to STATUS_SUCCESS for only the notifications that you explicitly recognize, as a signal to the bus driver that you ve processed them. The bus driver will assume that you didn t know about and therefore didn t process a notification for which you don t set STATUS_SUCCESS.

You might know that your device can t support a certain kind of usage. Suppose, for example, that some fact that only you know prevents your disk device from being used to store a hibernation file. In such a case, you should have the IRP fail if it specifies the InPath value:

 case DeviceUsageTypeHibernation: if (inpath) return CompleteRequest(Irp, STATUS_UNSUCCESSFUL, 0);

In the remainder of this section, I ll briefly describe each of the current usage types.

DeviceUsageTypePaging

The InPathTRUE notification indicates that a paging file will be opened on the device. The InPathFALSE notification indicates that a paging file has been closed. Generally, you should maintain a counter of the number of paging files you ve been notified about. While any paging file remains active, you ll cause queries for STOP and REMOVE functions to fail. In addition, when you receive the first paging notification, make sure that your dispatch routines for READ, WRITE, DEVICE_CONTROL, PNP, and POWER requests are locked into memory. (Refer to the information on driver paging in User-Mode and Kernel-Mode Address Spaces in Chapter 3 for more information.) You should also clear the DO_POWER_PAGABLE flag in your device object to force the Power Manager to send you power IRPs at DISPATCH_LEVEL. To be safe, I d also suggest nullifying any idle-notification registration you might have made. (See Chapter 8 for a discussion of idle detection.)

NOTE
In Chapter 8, I ll discuss how to set the DO_POWER_PAGABLE flag in a device object. You need to be sure that you never clear this flag while a device object under yours has the flag set. You ll want to clear the flag only in a completion routine, after the lower-level drivers have cleared their own flags. You need a completion routine anyway because you must undo anything you did in your dispatch routine if the IRP fails in the lower layers.

DeviceUsageTypeDumpFile

The InPathTRUE notification indicates that the device has been chosen as the repository for a crash dump file should one be necessary. The InPathFALSE notification cancels that. Maintain a counter of TRUE minus FALSE notifications. While the counter is nonzero:

  • Make sure that your power management code see Chapter 8 will never take the device out of the D0, or fully on, state. You can optimize your power behavior by inspecting the ShutdownType specified in system power IRPs in light of other usages of which you ve been notified. Explaining this advanced topic is beyond the scope of this book.

  • Avoid registering the device for idle detection, and nullify any outstanding registration.

  • Make sure that your driver causes stop and remove queries to fail.

DeviceUsageTypeHibernation

The InPathTRUE notification indicates that the device has been chosen to hold the hibernation state file should one be written. The InPathFALSE notification cancels that. You should maintain a counter of TRUE minus FALSE notifications. Your response to system power IRPs that specify the PowerSystemHibernate state will be different than normal because your device will be used momentarily to record the hibernate file. Elaboration of this particular feature of disk drivers is beyond the scope of this book.

PnP Notifications

Windows XP and Windows 98/Me provide a way to notify both user-mode and kernel-mode components of particular PnP events. Windows 95 has a WM_DEVICECHANGE message that user-mode programs can use to monitor, and sometimes control, hardware and power changes in the system. The newer operating systems build on WM_DEVICECHANGE to allow user-mode programs to easily detect when a driver enables or disables a registered device interface. Kernel-mode drivers can also register for similar notifications.

NOTE
Refer to the documentation for WM_DEVICECHANGE, RegisterDeviceNotification, and UnregisterDeviceNotification in the Platform SDK. I ll give you examples of using this message and these APIs, but I won t explain all possible uses of them. Some of the illustrations that follow also assume you re comfortable programming with Microsoft Foundation Classes.

Using WM_DEVICECHANGE

An application with a window can subscribe for WM_DEVICECHANGE messages related to a specific interface GUID (globally unique identifier). For example:

DEV_BROADCAST_DEVICEINTERFACE filter; filter.dbcc_size = sizeof(filter); filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; filter.dbcc_classguid = GUID_DEVINTERFACE_PNPEVENT; m_hInterfaceNotification = RegisterDeviceNotification(m_hWnd, &filter, 0);

NOTE
The PNPEVENT sample shows how to monitor WM_DEVICE CHANGE in order to monitor device insertion and removal events. The examples in this section are drawn from the TEST program accompanying that sample. The PNPEVENT driver itself is actually not very interesting.

The key statement here is the call to RegisterDeviceNotification, which asks the PnP Manager to send our window a WM_DEVICECHANGE message whenever anyone enables or disables a GUID_DEVINTERFACE_PNPEVENT interface. So suppose a device driver calls IoRegisterDeviceInterface with this interface GUID during its AddDevice function. We re asking to be notified when that driver calls IoSetDeviceInterfaceState to either enable or disable that registered interface.

When an application has a handle to a device, it can register for notifications concerning that specific handle:

DEV_BROADCAST_HANDLE filter = {0}; filter.dbch_size = sizeof(filter); filter.dbch_devicetype = DBT_DEVTYP_HANDLE; filter.dbch_handle = m_hDevice; m_hHandleNotification = RegisterDeviceNotification(m_hWnd, &filter, 0);

For each of the notification handles you register, you should eventually call UnregisterDeviceNotification. In preparing the first edition of this book, I found that this function was destabilizing Windows 98. A reader figured out these undocumented rules about how to call this function safely:

  • Call UnregisterDeviceNotification while the window whose handle you specified in the registration call still exists.

  • In Windows 98 (and, presumably, Windows Me), don t call UnregisterDeviceNotification from within the message handler for a notification relating to the same notification handle. Doing so is perfectly safe in Windows 2000 and Windows XP, though.

After your application registers for notifications, the system will send you WM_DEVICECHANGE window messages to alert you to various events of possible interest. I ll discuss here the DBT_DEVICEQUERYREMOVE and DBT_DEVICEREMOVECOMPLETE notifications, which are of particular interest to applications. Unless an application processes these notifications correctly, the PnP Manager can t successfully handle the two most common device removal scenarios.

The QUERYREMOVE and REMOVECOMPLETE notifications relate to a specific handle. The TEST program for PNPEVENT handles them this way:

BEGIN_MESSAGE_MAP(CTestDlg, CDialog) //{{AFX_MSG_MAP(CTestDlg)  //}}AFX_MSG_MAP ON_WM_DEVICECHANGE()END_MESSAGE_MAP() BOOL CTestDlg::OnDeviceChange(UINT nEventType, DWORD dwData) { if (!dwData) return TRUE; _DEV_BROADCAST_HEADER* p = (_DEV_BROADCAST_HEADER*) dwData; if (p->dbcd_devicetype == DBT_DEVTYP_DEVICEINTERFACE) return HandleDeviceChange(nEventType, (PDEV_BROADCAST_DEVICEINTERFACE) p); else if (p->dbcd_devicetype == DBT_DEVTYP_HANDLE) return HandleDeviceChange(nEventType, (PDEV_BROADCAST_HANDLE) p); else return TRUE; } BOOL CTestDlg::HandleDeviceChange(DWORD evtype, PDEV_BROADCAST_HANDLE dhp) { if (dhp->dbch_handle != m_hDevice) return TRUE; switch (evtype) { case DBT_DEVICEQUERYREMOVE:

if (!<okay to remove device>) return BROADCAST_QUERY_DENY; case DBT_DEVICEREMOVECOMPLETE: case DBT_DEVICEREMOVEPENDING:

if (m_hHandleNotification && !win98) { UnregisterDeviceNotification(m_hHandleNotification); m_hHandleNotification = NULL; }

CloseHandle(m_hDevice); break; } return TRUE; }

  1. TEST actually displays a message box at this point to ask you whether it will be OK to remove the device. In a real application, you might have some reason to demur. If you decide it s OK to remove the device, the code falls through into the next case. I found that it was necessary in Windows 98/Me to close my handle now rather than wait for another notification.

  2. As I mentioned earlier, you can close a notification handle while handling a notification in Windows 2000 or XP but not in Windows 98/Me.

  3. This is the point of all the machinery: we want to close our handle to the device when it s about to disappear or has already disappeared.

I suggest that you now try the following experiment to exercise both of these code paths. Launch the test program for PNPEVENT and install the PNP EVENT device. (See PNPEVENT.HTM for details of how to do this.) If you re running DbgView (see http://www.sysinternals.com), you ll observe the debug trace shown in lines 0 through 12 of Figure 6-6. The window for TEST will show an arrival message for the device (the first line in Figure 6-7). Now follow the instructions for installing and launching the PNPDTEST applet from the DDK Tools directory, and locate the device entry for PNPEVENT. (See Figure 6-8.) You ll find it indented below the Root node in the device list. Click the Test Surprise Remove button in PNPDTEST.

figure 6-6 debug trace from the pnpevent experiment.

Figure 6-6. Debug trace from the PNPEVENT experiment.

figure 6-7 test s event trace.

Figure 6-7. TEST s event trace.

figure 6-8 pnpdtest window.

Figure 6-8. PNPDTEST window.

Because of the way PNPDTEST works internally, the first thing that happens is a DBT_QUERYREMOVEDEVICE notification so that PNPDTEST can disable the device in order to install a filter driver. TEST presents a dialog asking whether it s OK to remove the device (Figure 6-9). You should answer yes, whereupon TEST will close its handle (Figure 6-10). Thereafter, the PnP Manager will send an IRP_MN_QUERY_REMOVE_DEVICE followed by an IRP_MN_REMOVE_DEVICE to the PNPEVENT driver. This sequence corresponds to lines 13 through 20 of the debug trace and the second event message in the TEST window.

figure 6-9 test asks whether it s ok to remove the device.

Figure 6-9. TEST asks whether it s OK to remove the device.

figure 6-10 test closes its handle.

Figure 6-10. TEST closes its handle.

PNPDTEST will now restart the device, generating lines 21 through 32 of the debug trace and the third notification in the TEST window. Note that TEST opens a new handle in response to the arrival notification.

Finally PNPDTEST causes an IRP_MN_SURPRISE_REMOVAL request to be sent to the PNPEVENT device. (Actually, it causes its associated filter driver to call IoInvalidateDeviceState, which eventually triggers the surprise removal IRP in the way I discussed earlier. You can see the trace of this internal magic in lines 33 through 39 of the debug trace.) PNPEVENT processes this in the way we ve discussed in this chapter. See lines 40 through 42 of the debug trace.

At this point, PNPEVENT will be in the SURPRISEREMOVED state. The driver can t be unloaded because a handle is still open. The test application will receive a WM_DEVICECHANGE with the DBT_DEVICEREMOVECOMPLETE code. The application closes its handle (debug trace line 43), whereupon the PnP Manager finishes up by sending an IRP_MN_REMOVE_DEVICE to PNP EVENT (debug trace lines 44 through 49).

You should notice that the application never receives a query in the surprise removal case.

I want to mention one fine point in connection with the DBT_QUERY REMOVEDEVICE notification. According to the Platform SDK documentation for this query, an application might return the special value BROADCAST_QUERY_DENY to decline permission to remove the device. This works as expected in Windows XP. That is, if you go to the Device Manager and attempt to remove the device, and if the application declines permission, the device will not be removed. In fact, the PnP Manager won t even send the IRP_MN_QUERY_REMOVE_DEVICE request to the driver in this situation.

In other versions of the operating system, however, BROAD CAST_QUERY_DENY doesn t work as expected. The Device Manager will appear to ignore the return code and proceed to remove the device from its window and to mark the device for deletion. It will realize that the device can t yet be removed, however, so it will post a dialog to the effect that you must restart the system for the removal to take effect. The driver remains in memory.

Notifications to Windows XP Services

Windows XP service programs can also subscribe for PnP notifications. The service should call RegisterServiceCtrlHandlerEx to register an extended control handler function. Then it can register for service control notifications about device interface changes. For example, take a look at the following code (and see the AUTOLAUNCH sample in Chapter 15):

DEV_BROADCAST_DEVICEINTERFACE filter = {0}; filter.dbcc_size = sizeof(filter); filter.dbcc_devicetype = DBT_DEVTYPE_DEVICEINTERFACE; filter.dbcc_classguid = GUID_AUTOLAUNCH_NOTIFY; m_hNotification = RegisterDeviceNotification(m_hService, (PVOID) &filter, DEVICE_NOTIFY_SERVICE_HANDLE);

Here m_hService is a service handle provided by the service manager when it starts your service, and DEVICE_NOTIFY_SERVICE_HANDLE indicates that you re registering for service control notifications instead of window messages. After receiving a SERVICE_CONTROL_STOP command, you want to deregister the notification handle:

UnregisterDeviceNotification(m_hNotification);

When a PnP event involving the interface GUID occurs, the system calls your extended service control handler function:

DWORD __stdcall HandlerEx(DWORD ctlcode, DWORD evtype, PVOID evdata, PVOID context) { }

where ctlcode will equal SERVICE_CONTROL_DEVICEEVENT, evtype will equal DBT_DEVICEARRIVAL or one of the other DBT_Xxx codes, evdata will be the address of a Unicode version of the DEV_BROADCAST_DEVICEINTERFACE structure, and context will be whatever context value you specified in your call to the RegisterServiceCtrlHandlerEx function.

Kernel-Mode Notifications

WDM drivers can use IoRegisterPlugPlayNotification to subscribe for interface and handle notifications. Here s an exemplary statement from the PNPMON sample driver that registers for notifications about the arrival and departure of an interface GUID designated by an application PNPMON s TEST.EXE in this case via an I/O control (IOCTL) operation:

status = IoRegisterPlugPlayNotification (EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, &p->guid, pdx->DriverObject, (PDRIVER_NOTIFICATION_CALLBACK_ROUTINE) OnPnpNotify, reg, &reg->InterfaceNotificationEntry);

The first argument indicates that we want to receive notifications whenever someone enables or disables a specific interface GUID. The second argument is a flag indicating that we want to receive callbacks right away for all instances of the interface GUID that are already enabled. This flag allows our driver to start after some or all of the drivers that export the interface in question and still receive notification callbacks about those interfaces. The third argument is the interface GUID in question. In this case, it comes to us via an IOCTL from an application. The fourth argument is the address of our driver object. The PnP Manager adds a reference to the object so that we can t be unloaded while we have any notification handles outstanding. The fifth argument is the address of a notification callback routine. The sixth argument is a context parameter for the callback routine. In this case, I specified the address of a structure (reg) that contains information relative to this registration call. The seventh and final argument gives the address of a variable where the PnP Manager should record a notification handle. We ll eventually call IoUnregisterPlugPlayNotification with the notification handle.

You need to call IoUnregisterPlugPlayNotification to close the registration handle. Because IoRegisterPlugPlayNotification adds a reference to your driver object, it won t do you any particular good to put this call in your DriverUnload routine. DriverUnload won t be called until the reference count drops to 0, which will never happen if DriverUnload itself has the deregistration calls. This problem isn t hard to solve you just need to pick an appropriate time to deregister, such as when you notice the last interface of a particular type being removed or in response to an IOCTL request from an application.

Given a symbolic link name for an enabled interface, you can also request notifications about changes to the device named by the link. For example:

PUNICODE_STRING SymbolicLinkName; // <== input to this process PDEVICE_OBJECT DeviceObject; // <== an output PFILE_OBJECT FileObject; // <== another output IoGetDeviceObjectPointer(&SymbolicLinkName, 0, &FileObject, &DeviceObject); IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange, 0, FileObject, pdx->DriverObject, (PDRIVER_NOTIFICATION_CALLBACK_ROUTINE) OnPnpNotify, reg, &reg->HandleNotificationEntry);

You shouldn t put this code inside your PnP event handler, by the way. IoGetDeviceObjectPointer internally performs an open operation for the named device object. A deadlock might occur if the target device were to perform certain kinds of PnP operations. You should instead schedule a work item by calling IoQueueWorkItem. Chapter 14 has more information about work items. The PNPMON sample driver illustrates how to use a work item in this particular situation.

The notifications that result from these registration calls take the form of a call to the callback routine you specified:

NTSTATUS OnPnpNotify(PPLUGPLAY_NOTIFICATION_HEADER hdr, PVOID Context) {  return STATUS_SUCCESS; }

The PLUGPLAY_NOTIFICATION_HEADER structure is the common header for several different structures that the PnP Manager uses for notifications:

typedef struct _PLUGPLAY_NOTIFICATION_HEADER { USHORT Version; USHORT Size; GUID Event; } PLUGPLAY_NOTIFICATION_HEADER, *PPLUGPLAY_NOTIFICATION_HEADER;

The Event GUID indicates what sort of event is being reported to you. See Table 6 5. The DDK header file WDMGUID.H contains the definitions of these GUIDs.

Table 6-5. PnP Notification GUIDs

GUID Name

Purpose of Notification

GUID_HWPROFILE_QUERY_CHANGE

OK to change to a new hardware profile?

GUID_HWPROFILE_CHANGE_CANCELLED

Change previously queried about has been cancelled.

GUID_HWPROFILE_CHANGE_COMPLETE

Change previously queried about has been accomplished.

GUID_DEVICE_INTERFACE_ARRIVAL

A device interface has just been enabled.

GUID_DEVICE_INTERFACE_REMOVAL

A device interface has just been disabled.

GUID_TARGET_DEVICE_QUERY_REMOVE

OK to remove a device object?

GUID_TARGET_DEVICE_REMOVE_CANCELLED

Removal previously queried about has been cancelled.

GUID_TARGET_DEVICE_REMOVE_COMPLETE

Removal previously queried about has been accomplished.

If you receive either of the DEVICE_INTERFACE notifications, you can cast the hdr argument to the callback function as a pointer to the following structure:

typedef struct _DEVICE_INTERFACE_CHANGE_NOTIFICATION { USHORT Version; USHORT Size; GUID Event; GUID InterfaceClassGuid; PUNICODE_STRING SymbolicLinkName; } DEVICE_INTERFACE_CHANGE_NOTIFICATION, *PDEVICE_INTERFACE_CHANGE_NOTIFICATION;

In the interface change notification structure, InterfaceClassGuid is the interface GUID, and SymbolicLinkName is the name of an instance of the interface that s just been enabled or disabled.

If you receive any of the TARGET_DEVICE notifications, you can cast the hdr argument as a pointer to this structure instead:

typedef struct _TARGET_DEVICE_REMOVAL_NOTIFICATION { USHORT Version; USHORT Size; GUID Event; PFILE_OBJECT FileObject; } TARGET_DEVICE_REMOVAL_NOTIFICATION, *PTARGET_DEVICE_REMOVAL_NOTIFICATION;

where FileObject is the file object for which you requested notifications.

Finally, if you receive any of the HWPROFILE_CHANGE notifications, hdr will really be a pointer to this structure:

typedef struct _HWPROFILE_CHANGE_NOTIFICATION { USHORT Version; USHORT Size; GUID Event; } HWPROFILE_CHANGE_NOTIFICATION, *PHWPROFILE_CHANGE_NOTIFICATION;

This doesn t have any more information than the header structure itself just a different typedef name.

One way to use these notifications is to implement a filter driver for an entire class of device interfaces. (There is a standard way to implement filter drivers, either for a single driver or for a class of devices, based on setting entries in the registry. I ll discuss that subject in Chapter 16. Here I m talking about filtering all devices that register a particular interface, for which there s no other mechanism.) In your driver s DriverEntry routine, you register for PnP notifications about one or more interface GUIDs. When you receive the arrival notification, you use IoGetDeviceObjectPointer to open a file object and then register for target device notifications about the associated device. You also get a device object pointer from IoGetDeviceObjectPointer, and you can send IRPs to that device by calling IoCallDriver. Be on the lookout for the GUID_TARGET_DEVICE_QUERY_REMOVE notification because you have to dereference the file object before the removal can continue.

The PNPMON Sample

The PNPMON sample illustrates how to register for and process PnP notifications in kernel mode. To give you something you can run on your computer and actually see working, I designed PNPMON to simply pass notifications back to a user-mode application (named TEST what else?). This is pretty silly in that a user-mode application can get these notifications on its own by calling RegisterDeviceNotification.

PNPMON is different from the other driver samples in this book. It s intended to be dynamically loaded as a helper for a user-mode application. The other drivers we look at are intended to manage hardware, real or imagined. The user-mode application uses service manager API calls to load PNPMON, which creates exactly one device object in its DriverEntry routine so that the application can use DeviceIoControl to get things done in kernel mode. When the application exits, it closes its handle and calls the service manager to terminate the driver.

PNPMON also includes a Windows 98/Me virtual device driver (VxD) that the test application can dynamically load. It s possible to dynamically load a WDM driver in Windows 98/Me by using an undocumented function (_NtKernLoadDriver, if you care), but there s no way to unload a driver that you ve loaded in this way. You don t need to resort to undocumented functions, though, because VxDs can call most of the WDM support routines directly by means of the WDMVXD import library in the Windows 98 DDK. (This library is missing from the Windows Me portion of the Windows XP DDK.) Just about the only extra things you need to do in your VxD project are include WDM.H ahead of the VxD header files and add WDMVXD.CLB to the list of inputs to the linker. So PNPMON.VXD simply registers for PnP notifications as if it were a WDM driver and supports the same IOCTL interface that PNPMON.SYS supports.

Custom Notifications

I ll close this section by explaining how a WDM driver can generate custom PnP notifications. To signal a custom PnP event, create an instance of the custom notification structure and call one of IoReportTargetDeviceChange or IoReportTargetDeviceChangeAsynchronous. The asynchronous flavor returns immediately. The synchronous flavor waits a long time, in my experience until the notification has been sent. The notification structure has this declaration:

typedef struct _TARGET_DEVICE_CUSTOM_NOTIFICATION { USHORT Version; USHORT Size; GUID Event; PFILE_OBJECT FileObject; LONG NameBufferOffset; UCHAR CustomDataBuffer[1]; } TARGET_DEVICE_CUSTOM_NOTIFICATION, *PTARGET_DEVICE_CUSTOM_NOTIFICATION;

Event is the custom GUID you ve defined for the notification. FileObject is NULL the PnP Manager will be sending notifications to drivers who opened file objects for the same PDO as you specify in the IoReportXxx call. CustomDataBuffer contains whatever binary data you elect followed by Unicode string data. NameBufferOffset is -1 if you don t have any string data; otherwise, it s the length of the binary data that precedes the strings. You can tell how big the total data payload is by subtracting the field offset of CustomDataBuffer from the Size value.

Here s how PNPEVENT generates a custom notification when you press the Send Event button in the associated test dialog:

struct _RANDOM_NOTIFICATION : public _TARGET_DEVICE_CUSTOM_NOTIFICATION { WCHAR text[14]; };  _RANDOM_NOTIFICATION notify; notify.Version = 1; notify.Size = sizeof(notify); notify.Event = GUID_PNPEVENT_EVENT; notify.FileObject = NULL; notify.NameBufferOffset = FIELD_OFFSET(RANDOM_NOTIFICATION, text) - FIELD_OFFSET(RANDOM_NOTIFICATION, CustomDataBuffer); *(PULONG)(notify.CustomDataBuffer) = 42; wcscpy(notify.text, L"Hello, world!"); IoReportTargetDeviceChangeAsynchronous(pdx->Pdo, &notify, NULL, NULL);

That is, PNPEVENT generates a custom notification whose data payload contains the number 42 followed by the string Hello, world!.

The notification shows up in any driver that registered for target device notifications pertaining to a file object for the same PDO. If your notification callback routine gets a notification structure with a nonstandard GUID in the Event field, you can expect that it s somebody s custom notification GUID. You need to understand what the GUID means before you go mucking about in the CustomDataBuffer!

User-mode applications are supposed to be able to receive custom event notifications too, but I ve not been able to get that to work.



Programming the Microsoft Windows Driver Model
Programming the Microsoft Windows Driver Model
ISBN: 0735618038
EAN: 2147483647
Year: 2003
Pages: 119
Authors: Walter Oney

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net