6. Firing WMI Events
In the Featured Toaster sample driver, the ToasterFireArrivalEvent
function shows how to generate a WMI event that has a dynamic instance
name. The WMI event contains the name of the device model, which WMI
records to log the arrival of this device. The driver calls this
function from its EvtDevicePrepareHardware
callback to indicate that the device has been configured. Parts of this
function are standard WMI tasks and have little to do with KMDF. This
discussion focuses on the KMDF-specific code:
NTSTATUS
ToasterFireArrivalEvent(
IN WDFDEIVE Device
)
{
WDFMEMORY memory;
PWNODE_SINGLE_INSTANCE wnode;
ULONG wnodeSize;
ULONG wnodeDataBlockSize;
ULONG wnodeInstanceNameSize;
ULONG size;
ULONG length;
UNICODE_STRING deviceName;
UNICODE_STRING modelName;
NTSTATUS status;
//
// *NOTE*
// WdfWmiFireEvent only fires single instance events at
// the present, so continue to use this method of firing
// events
// *NOTE*
//
RtlInitUnicodeString(&modelName, L"Sonali\0\0");
//
// Get the device name.
//
status = GetDeviceFriendlyName(&Device, &memory);
if(!NT_SUCCESS(status)) {
return status;
}
RtlInitUnicodeString(&deviceName, (PWSTR)
WdfMemoryGetBuffer(memory, NULL));
//
//Determine the amount of wnode information we need.
//
wnodeSize = sizeof(WNODE_SINGLE_INSTANCE);
wnodeInstanceNameSize = deviceName.Length + sizeof(USHORT);
wnodeDataBlockSize = modelName.Length + sizeof(USHORT);
size = wnodeSize + wnodeInstanceNameSize +
wnodeDataBlockSize;
//
// Allocate memory for the WNODE from NonPagedPool
//
wnode = ExAllocatePoolWithTag(NoPagedPool, size,
TOASTER_POOL_TAG);
If(NULL != wnode) {
RtlZeroMemory(wnode, size);
wnode->WnodeHeader.BufferSize = size
wnode->WnodeHeader.ProviderId =
IoWMIDeviceObjectToProviderId(
WdfDeviceWdmGetDeviceObject(Device));
wnode->WnodeHeader.Version = 1;
KeQuerySystemTime(&wnode->WnodeHeader.TimeStamp);
RtlCopyMemory(&wnode->WnodeHeader.Guid,
&TOASTER_NOTIFY_DEVICE_ARRIVAL_EVENT,
Sizeof(GUID));
//
// Set flags to indicate that you are creating dynamic
// instance names. This driver supports dynamic
// instances because it can fire the events at any time.
// If it used static instance names instead, it could only
// fire events after WMI queries for IRP_MN_REGINFO,
// which happens after the device has been started.
// Note also that if you are firing an event after the
// device is started, you should
// check whether the event is enabled, because that
// indicates that someone is interested in receiving the
// event. Why waste system resources by firing an event
// when nobody is interested?
//
wnode->WnodeHeader.Flags = WNODE_FLAG_EVENT_ITEM |
WNODE_FLAG_SINGLE_INSTANCE;
wnode->OffsetInstanceName = wnodeSize;
wnode->DataBlockOffset= wnodeSize + wnodeInstanceNameSize;
wnode->SizeDataBlock = wnodeDataBlockSize;
//
// Write the instance name.
//
size -= wnodeSize;
status = WDF_WMI_BUFFER_APPEND_STRING(
WDF_PTR_ADD_OFFSET(wnode, wnod->OffsetInstance),
size,
&deviceName,
&length
);
//
// Size was precomputed, so this should never fail.
//
ASSERT(NT_SUCCESS(status));
//
// Write the data, which is the model name as a
string.
//
size = -= wnodeInstanceNameSize;
WDF_WMI_BUFFER_APPEND_STRING(
WDF_PTR_ADD_OFFSET(wnode, wnode->DataBlockOffset),
size,
&modelName,
&length
);
//
// Size was precomputed, so this should never fail.
//
ASSERT(NT_SUCCESS(status));
//
// Indicate the event to WMI. WMI will take care of
// freeing the WMI struct back to pool.
//
status = IoWMIWriteEvent(wnode);
if(!NT_SUCCESS(status)) {
KdPrint(("IoWMIWriteEvent failed %x\n", status));
ExFreePool(wnode);
}
}
else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
//
// Free the memory allocated by GetDeviceFriendlyName
// function.
//
WdfObjectDelete(memory);
return status;
}
ToasterfireArrivalEvent first defines a model name for its device and then retrieves the friendly name for the device by calling the internal routine GetDeviceFriendlyName. GetDeviceFriendlyName takes a handle to the device object and returns a handle to a WDFMEMORY object that contains a buffer that holds the name.
The driver passes the returned WDFMEMORY object handle to WdfGetMemoryBuffer to retrieve a pointer to the buffer itself and calls RtlUnicodeString to copy the contents of the buffer into the Unicode string variable deviceName, as follows:
RtlInitUnicodeString(&deviceName,
(PWSTR)WdfMemoryGetBuffer(memory, NULL));
The driver now constructs the WMI event by using standard WMI structures and routines (defined in wmistr.h) along with standard Windows device driver interfaces (DDIs), which are defined in wdm.h and ntddk.h. The driver allocates memory from the nonpaged pool for the WNODE_SINGLE_INSTANCE structure (wnode) by calling ExAllocatePoolWithTag. If memory allocation succeeds, the driver zero-initializes the wnode structure and then fills it in the usual way for WMI. To supply the WMI provider ID, the driver calls IoWmiDeviceObjectToProviderId, passing as a parameter the WDM device object returned by WdfDeviceWdmGetDeviceObject.
After the wnode structure is filled, the driver calls the WDF_WMI_BUFFER_APPEND_STRING function once to append the instance name to the WNODE_SINGLE_INSTANCE structure and a second time to append the model name.
Finally, the driver fires the event by calling IoWMIWriteEvent. WMI records the data and frees the memory allocated for the WNODE_SINGLE_INSTANCE structure.