Êíèãà: Writing Windows WDM Device Drivers

A WMI Driver

A WMI Driver

A WDM driver can use WMI mechanisms to publish information, permit configuration of its device, and supply notifications of events. It should continue to report standard Windows 2000 events, as many system administrators will still be looking for this event information. However, WMI is the way forward, as it allows information to be made available on non-W2000 systems, and it allows remote inspection and control of the device.

WDM drivers, NT style drivers, and miniport drivers/minidriver can support WMI. In the latter case, you must fit in with the WMI reporting mechanism supported by the class driver. For example, a SCSI miniport must set the WmiDataProvider BOOLEAN in the PORT_CONFIGURATION_INFORMATION structure and handle SRB_FUNCTION_WMI requests.

The Wdm3 driver defines two custom WMI blocks. Listing 12.2 shows how the Wdm3Information data block and the Wdm3Event event block are defined in Wdm3.mof. The identifying GUID for each block is defined in the GUIDs.h header: WDM3_WMI_GUID and WDM3_WMI_EVENT_GUID.

The Wdm3Information data block contains two 32-bit unsigned properties followed by a counted wide string property. The first real property, BufferLen, is the length of the shared memory buffer. BufferFirstWord is the first 32 bits of the shared memory buffer, or zero if the buffer is not long enough. Finally, SymbolicLinkName is the symbolic link name of the Wdm3 device interface.

The Wdm3Inforination data block has a definition for a PowerDown function. I found that this did not compile, so I commented it out.

The Wdm3Event WMI event block must be derived from the standard WMI Event class. It simply defines a Message property.

Listing 12.2 Wdm3Iinformation and Wdm3Event block

/* WMI data block: Information about Wdm3 device Wdm3Information, identified by WDM3_WMI_GUID */
[WMI, Dynamic, Provider("WMIProv"), Description("Wdm3 information"), guid("{C0CF0643-5F6E-11d2-B677-0OC0DFE4C1F3}"), locale("MSx409")]
class Wdm3Information {
 [key, read]
 string InstanceName;
 [read]
 boolean Active;
 [WmiDataId(1), read, Description("Shared memory buffer length") ]
 uint32 BufferLen;
 [WmiDataId(2), read, Description("First ULONG of shared memory buffer") ]
 uint32 BufferFirstWord;
 [WmiDataId(3), read, Description("Symbolic link name") ]
 string SymbolicLinkName;
 /* Doesn't compile
 [Implemented]
 void PowerDown(); */
};
/* WMI event block: Wdm3 device event Wdm3Event, identified by WDM3_WMI_EVENT_GUID */
[WMI, Dynamic, Provider("WMIProv"), guid("{C0CF0644-5F6E-11d2-B677-00C0DFE4C1F3}"), locale("MSx409"), Description("Wdm3 event message")]
class Wdm3Event : WMIEvent {
 [key, read]
 string InstanceName;
 [read]
 boolean Active;
 [WmiDataId(1), read, Description("Message")]
 string Message;
};

This example MOF file shows how a driver defines static instance names when it registers a WMI block. Alternatively, if necessary, the driver can define dynamic instance names if the instances change frequently at run time. Handling dynamic instance names is harder and is not a common requirement, so it is not discussed here.

WMI Build Environment

Quite a few changes must be made to a project to support WMI.

1. All the WMI functions, including support for the Wdm3SystemControl WMI IRP, are in Wmi.cpp. Include this file in the SOURCES list of files to compile in the SOURCES file.

2. I found that this line had to be included in SOURCES to ensure that the WMI library file was found.

TARGETLIBS=C:NTDDKLIBFREI386WmiLib.Lib

3. The prebuild steps had to be altered to persuade build to run the mofcomp tool to compile Wdm3.mof. The SOURCES file NTTARGETFILE0 macro is changed to ensure that makefile.inc runs the following command. The complete makefile.inc is given in the next chapter.

mofcomp –B:Wdm3.bmf –WMI Wdm3.mof

This command line compiles MOF source file Wdm3.mof into the binary MOF Wdm3.bmf file. The options to mofcomp ensure that the source code is checked for WMI compatibility and that the output goes in the correct file.

4. The main resource file Wdm3.rc must now include the binary MOF file Wdm3.bmf. The following line in the resource script identifies the data using the name MofResource.

MOFRESOURCE MOFDATA MOVEABLE PURE "Wdm3.bmf"

5. The main header in Wdm3.h had to use standard headers wmilib.h and wmistr.h. It also included wdmguid.h to get the definition of the standard GUID, GUID_POWER_DEVICE_ENABLE.

6. The device extension has these extra fields added.

WMILIB_CONTEXT WmiLibInfo; // WMI Context
BOOLEAN IdlePowerDownEnable; // Enable power down option
BOOLEAN WMIEventEnabled; // Enable WMI events

WmiLibInfo is used to pass the Wdm3 WMI GUIDs and various callbacks, as described later.

IdlePowerDownEnable implements the MSPower_DeviceEnable WMI data block. If IdlePowerDownEnable is TRUE, the driver can power down when it is idle.

Finally, WMIEventEnabled is used to enable WMI event reporting.

7. The main WMI code needs to refer to the driver registry path that was passed to DriverEntry. Therefore, DriverEntry saves a copy in its Wdm3RegistryPath global variable.

// Save a copy of our RegistryPath for WMI
Wdm3RegistryPath.MaximumLength = RegistryPath->MaximumLength;
Wdm3RegistryPath.Length = 0;
Wdm3RegistryPath.Buffer = (PWSTR)ExAllocatePool(PagedPool, Wdm3RegistryPath.MaximumLength);
if (Wdm3RegistryPath.Buffer == NULL) return STATUS_INSUFFICIENT_RESOURCES;
RtlCopyUnicodeString(&Wdm3RegistryPath, RegistryPath);

The driver unload routine Wdm3Unload deletes the Wdm3RegistryPath buffer.

Registering as a WMI Data Provider

You must register as a WMI provider by calling IoWmiRegistrationControl with a WMIREG_ACTION_REGISTER command for each device when it is ready to handle WMI IRPs. The Wdm3 driver makes this call in its RegisterWmi routine. RegisterWmi is called at the end of Wdm3AddDevice. The corresponding DeregisterWmi routine deregisters the WMI support by calling IoWmiRegistrationControl with the WMIREG_ACTION_DEREGISTER command. DeregisterWmi is called when the device is removed.

Listing 12.3 shows the RegisterWmi and DeregisterWMI routines. RegisterWmi also sets up the WmiLibInfo structure in the device extension, ready for processing by the System Control WMI IRP. Table 12.1 shows the WmiLibInfo WMILIB_CONTEXT structure fields.

WmiLibInfo first contains the list of WMI block GUIDs that are handled by this driver. Wdm3GuidList is an array of these WMIGUIDREGINFO structures. Each specifies the GUID pointer, a count of instances, and optionally some flags. There is only one instance of each WMI block for this device. No flags are specified, as the appropriate flags are used later in the call to QueryWmiRegInfo.

The WMILIB_CONTEXT WmiLibInfo field also sets up several callback routines that we shall meet later. These are used to help the processing of the IRP_MJ_SYSTEM_CONTROL WMI IRP.

After calling IoWmiRegistrationControl, your driver will then be sent an IRP_MN_REGINFO System Control IRP to obtain the device's WMI MOF information, as described in the following.

Listing 12.3 RegisterWmi and DeregisterWMI code

const int GUID_COUNT = 3;
WMIGUIOREGINFO Wdm3GuidList[GUID_CÙUNT] = {
 { &WDM3_WMI_GUID, 1. 0 }, // Wdm3Information
 { &GUID_POWER_DEVICE_ENABLE, 1, 0}, // MSPower_DeviceEnable
 { &WDM3_WMI_EVENT_GUID, 1, 0}, // Wdm3Event
};
const ULONG WDM3_WMI_GUID_INDEX = 0;
const ULONG GUID_POWER_DEVICE_ENABLE_INDEX = 1;
const ULONG WDM3_WMI_EVENT_GUID_INDEX = 2;
void RegisterWmi(IN PDEVICE_OBJECT fdo) {
 PWDM3_DEVICE_EXTENSION dx=(PWDM3_DEVICE_EXTENSION)fdo->DeviceExtension;
 dx->WmiLibInfo.GuidCount = GUID_COUNT;
 dx->WmiLibInfo.GuidList = Wdm3GuidList;
 dx->WmiLibInfo.QueryWmiRegInfo = QueryWmiRegInfo;
 dx->WmiLibInfo.QueryWmiDataBlock = QueryWmiDataBlock;
 dx->WmiLibInfo.SetWmiDataBlock = SetWmiDataBlock;
 dx->WmiLibInfo.SetWmiDataItem = SetWmiDataItem;
 dx->WmiLibInfo.ExecuteWmiMethod = ExecuteWmiMethod;
 dx->WmiLibInfo.WmiFunctionControl = WmiFunctionControl;
 NTSTATUS status = IoWMIRegistrationControl(fdo, WMIREG_ACTION_REGISTER);
 DebugPrint("RegisterWmi %x", status);
}
void DeregisterWmi( IN PDEVICE_OBJECT fdo) {
 IoWMIRegistrationControl(fdo, WMIREG_ACTION_DEREGISTER);
 DebugPrintMsg("DeregisterWmi");
}

Table 12.1 WMILIB_CONTEXT structure

GuidCount ULONG Required Count of WMI blocks
GuidList  Required Array with the GUIDs of the WMI blocks supported, etc.
QueryWmiRegInfo Required Provide further information about the WMIblocks you are registering
QueryWmiDataBlock Callback Required Return a single instance or all instances of adata block
SetWmiDataBlock Callback Optional Set all data items in a single instance of adata block
SetWmiDataItem Callback Optional Set a single data item in a single instance of a data block
ExecuteWmiMethod Callback Optional Execute a method associated with a data block
WmiFunctionControl Callback Optional Enable and disable event notification andexpensive data block collection

Handling System Control IRPs

Your driver must then handle System Control IRPs in your SystemControl routine. Table 12.2 shows the various minor codes associated with this IRP.

Table 12.2 System control minor request codes

IRP_MN_REGINFO Query a driver's registration information
IRP_MN_QUERY_ALL_DATA Get all instances in a given data block
IRP_MN_QUERY_SINGLE_INSTANCE Get a single instance in a given data block
IRP_MN_CHANGE_SINGLE_INSTANCE Change all data items in a given data block
IRP_MN_CHANGE_SINGLE_ITEM Change a single data item in a given data block
IRP_MN_ENABLE_EVENTS Enable event notification
IRP_MN_DISABLE_EVENTS Disable event notification
IRP_MN_ENABLE_COLLECTION Start collection of data that is expensive to collect
IRP_MN_DISABLE_COLLECTION Stop collection of data that is expensive to collect
IRP_MN_EXECUTE_METHOD Execute a method in a data block

If you have dynamic instance names, you must process the IRP and call WmiCompleteRequest, or pass it onto the next driver if you do not recognize the GUID.

For static instances, as used by Wdm3, you simply have to call WmiSystemControl, passing a pointer to your WMILIBCONTEXT structure. WmiSystemControl processes the IRPs as much as possible. If the request is destined for your driver, it invokes the appropriate callback to let you process the IRP. On return, WmiSystemControl sets its IrpDisposition parameter to tell you how to continue processing the IRP, as shown in Table 12.3. For example, if the IRP was not destined for your driver, IrpDisposition is set to IrpForward and the IRP is sent down the stack.

Table 12.3 WmiSystemControl IrpDisposition handling

IrpProcessed Either call WmiCompleteRequest here or in your call-back routine.
IrpNotComplete IRP processed but an error detected, so just call IoCompleteRequest.
IrpNotWMI or IrpForward Forward to next driver: call IoSkipCurrentIrpStackLocation and IoCallDriver

Listing 12.4 shows how the Wdm3SystemControl routine processes System Control IRPs. The main processing is carried out by the system function WmiSystemControl. Wdm3SystemControl acts on the IrpDisposition parameter in the recommended way.

Listing 12.4 Wdm3SystemControl routine

NTSTATUS Wdm3SystemControl (IN PDEVICE_OBJECT fdo, IN PIRP Irp) {
 DebugPrintMsg("Wdm3SystemControl");
 PWDM3_DEVICE_EXTENSION dx = (PWDM3_DEVICE_EXTENSION)fdo->DeviceExtension;
 SYSCTL_IRP_DISPOSITION disposition;
 NTSTATUS status =  WmiSystemControl(&dx->WmiLibInfo, fdo, Irp, &disposition);
 switch(disposition) {
 case IrpProcessed:
  // This irp has been processed and may be completed or pending.
  break;
 case IrpNotCompleted:
  // This irp has not been completed, but has been fully processed.
  // we will complete it now
  IoCompleteRequest(Irp, IO_NO_INCREMENT);
  break;
 case IrpForward: case IrpNotWmi:
  // This irp is either not a WMI irp or is a WMI irp targetted
  // at a device lower in the stack.
  IoSkipCurrentIrpStacktocation(Irp);
  status = IoCallDriver(dx->NextStackDevice, Irp);
  break;
 default:
  DebugPrint("Wdm3SystemControl bad disposition %d",disposition);
  // ASSERT(FALSE);
 }
 return status;
}

At a minimum, you should write QueryWmiRegInfo, QueryWmiDataBlock, SetWmiDataBlock, and SetWmiDataItem callbacks. Even though the last two callbacks are optional, it is best to implement them to ensure that WmiCompleteRequest is called with an appropriate error code.

QueryWmiRegInfo Handler

The Wdm3 QueryWmiRegInfo callback routine shown in Listing 12.5 provides further information about the WMI blocks you are registering, in addition to the GuidList provided in your WMILIB_CONTEXT.

Return your MOF resource name and your registry path.

If you are writing a WDM driver, you will usually set the WMIREG_FLAG_INSTANCE_PD0 flag and pass the device's PDO. See the DDK documentation for details of other flags that you can set.

Listing 12.5 QueryWmiRegInfo routine

#define MofResourceNameText L"MofResource"
NTSTATUS QueryWmiRegInfo(IN PDEVICE_OBJECT fdo, OUT PULONG PRegFlags, OUT PUNICODE_STRING PInstanceName, OUT PUNICODE_STRING *PRegistryPath, OUT PUNICODE_STRING MofResourceName, OUT PDEVICE_OBJECT *Pdo) {
 DebugPrintMsg("QueryWmiRegInfo");
 PWDM3_DEVICE_EXTENSION dx = (PWDM3_DEVICE_EXTENSION)fdo->DeviceExtension;
 *PRegFlags = WMIREG_FLAG_INSTANCE_PDO;
 *PRegistryPath = &Wdm3RegistryPath;
 RtlInitUnicodeString( MofResourceName, MofResourceNameText);
 *Pdo = dx->pdo;
 return STATUS_SUCCESS;
}

QueryWmiDataBlock Handler

A QueryWmiDataBlock callback routine returns one or more instances of a data block. When finished, it should call WmiCompleteRequest. A driver can return STATUS_PENDING if the IRP cannot be completed immediately.

QueryWmiDataBlock is passed GuidIndex, the relevant index into your GuidList array, the InstanceIndex, and the InstanceCount required. You have to fill the Buffer (that has a maximum size of BufferAvail). Fill in InstanceLengthArray, an array of ULONGs giving the length of each instance.

In the buffer, each instance's data should be aligned on an 8-byte boundary. The actual data should correspond to the class MOF definition. A MOF string should be returned as a counted Unicode, MOF Boolean returned as a BOOLEAN, MOF uint32 returned as a ULONG, MOF uint64 as a ULONGLONG, etc.

Listing 12.6 shows how the Wdm3 QueryWmiDataBlock routine handles requests for the Wdm3Information and MSPower_DeviceEnable WMI data blocks. For Wdm3Information, QueryWmiDataBlock first works out the size of buffer required and checks that the given output buffer is large enough. It then stores the BufferLen, BufferFirstWord, and SymbolicLinkName property values. The SymbolicLinkName string is stored in counted Unicode (i.e., a USHORT count followed by the wide char characters). Finally, the number of bytes set is stored. Only one instance of a Wdm3Information data block is ever processed, so this size is stored in the first ULONG pointed to by InstanceLengthArray.

QueryWmiDataBlock handles a MSPower_DeviceEnable WMI data block request in a similar way.

Listing 12.6 QueryWmiDataBlock routine

NTSTATUS QueryWmiDataBlock(IN PDEVICE_OBJECT fdo, IN PIRP Irp, IN ULONG GuidIndex, IN ULONG InstanceIndex, IN ULONG InstanceCount, IN OUT PULONG InstanceLengthArray, IN ULONG OutBufferSize, OUT PUCHAR PBuffer) {
 DebugPrint("QueryWmiDataBlock: GuidIndex %d, InstanceIndex %d, "
  "InstanceCount %d, OutBufferSize %d",
  GuidIndex,InstanceIndex, InstanceCount, OutBufferSize);
 PWDM3_DEVICE_EXTENSI0N dx = (PWDK3_DEVICE_EXTENSION)fdo->DeviceExtension;
 NTSTATUS status;
 ULONG size =0;
 switch( GuidIndex) {
 case WDM3_WMI_GUID_INDEX: // Wdm3Information
  {
   ULONG SymLinkNameLen = dx->ifSymLinkName.Length;
   size = sizeof(ULONG)+sizeof(ULONG)+SymLinkNameLen+sizeof(USHORT);
   // Check output buffer size
   if (OutBufferSize<size) {
    status = STATUS_BUFFER_TOO_SMALL;
    break;
   }
   // Store uint32 BufferLen
   *(ULONG *)PBuffer = BufferSize;
   PBuffer +-= sizeof (ULONG);
   // Store uint32 BufferFirstWord
   ULONG FirstWord = 0;
   if (Buffer!=NULL && BufferSize>=4) FirstWord = *(ULONG*)Buffer;
   *(ULONG *)PBuffer = FirstWord;
   PBuffer += sizeof(ULONG);
   // Store string SymbolicLinkName as counted Unicode
   *(USHORT *)PBuffer = (USHORT)SymLinkNameLen;
   PBuffer += sizeof(USHORT);
   RtlCopyMemory(PBuffer, dx->ifSymLinkName.Buffer, SymLinkNameLen);
   // Store total size
   *InstanceLengthArray = size;
   status = STATUS_SUCCESS;
   break;
  }
 case GUID_POWER_DEVICE_ENABLE_INDEX: // MSPower_DeviceEnable
  {
   size = sizeof(BOOLEAN);
   // Check output buffer size
   if (OutBufferSize<size) {
    status = STATUS_BUFFER_TOO_SMALL;
    break;
   }
   // Store boolean IdlePowerDownEnable in Enable property
   *(BOOLEAN*)PBuffer = dx->IdlePowerDownEnable;
   // Store total size
   *InstanceLengthArray = size;
   status = STATUS_SUCCESS;
   break;
  }
 default:
  DebugPrintMsg("QueryWmiDataBlock: Bad GUID index");
  status = STATUS_WMI_GUID_NOT_FOUND;
  break;
 }
 return WmiCompleteRequest(fdo, Irp, status, size, IO_NO_INCREMENT);
}

SetWmiDataBlock Handler

The Wdm3 SetWmiDataBlock routine shown in Listing 12.7 lets a user change the device extension IdlePowerDownEnable settings. If the MSPower_DeviceEnable GUID index is given and the input buffer size is large enough, the BOOLEAN at the start of Pbuffer is stored in IdlePowerDownEnable.

SetWmiDataBlock then acts on the new power down enable setting. If powering down is now enabled, PoRegisterDeviceForIdleDetection is called, if necessary. If now disabled, PoRegisterDeviceForIdleDetection is called to turn off idle detection; if the Wdm3 device is powered down, SendDeviceSetPower is called to power the device up.

Listing 12.7 SetWmiDataBlock routine

NTSTATUS SetWmiDataBlock(IN PDEVICE_OBJECT fdo, IN PIRP Irp, IN ULONG GuidIndex, IN ULONG InstanceIndex, IN ULONG BufferSize, IN PUCHAR PBuffer) {
 DebugPrint("SetWmiDataBlock: GuidIndex %d, InstanceIndex %d, BufferSize %d",
  GuidIndex,Instancelndex,BufferSize);
 PWDM3_DEVICE_EXTENSION dx = (PWDM3_DEVICE_EXTENSION)fdo->DeviceExterision;
 if (GuidIndex==GUID_POWER_DEVICE_ENABLE_INDEX) // MSPower_DeviceEnable
 {
  if (BufferSize<sizeof(BOOLEAN))
   return WmiCompleteRequest(fdo, Irp, STATUS_BUFFER_TOO_SMALL, 0, IO_NO_INCREMENT);
  // Get Enable property into IdlePowerDownEnable
  dx->IdlePowerDownEnable = *(BOOLEAN*)PBuffer;
  // Action IdlePowerDownEnable
  if (dx->IdlePowerDownEnable) {
   DebugPrintMsg("SetWmiDataBlock: Enabling power down");
   // Enable power down idling
   if (dx->PowerIdleCounter==NULL) dx->PowerIdleCounter = PoRegisterDeviceForIdleDetection(dx->pdo, 30, 60, PowerDeviceD3);
  } else {
   DebugPrintMsg("SetWmiDataBlock: Disabling power down");
   // Disable power down idling
   if (dx->PowerIdleCounter!=NULL) dx->PowerIdleCounter = PoRegisterDeviceForIdleDetection(dx->pdo, 0, 0, PowerDeviceD3);
   if (dx->PowerState>PowerDeviceD0) {
    DebugPrintMsg("SetWmiDataBlock: Disabling power down: power up");
    SendDeviceSetPower(dx, PowerDeviceD0);
   }
  }
  return WmiCompleteRequest( fdo, Irp, STATUS_SUCCESS, 0, IO_NO_INCREMENT);
 }
 return FailWMIRequest(fdo, Irp, GuidIndex);
}

SetWmiDataItem Handler

A SetWmiDataItem callback handles the setting of one data item in an instance. This routine is not called in Wdm3. Listing 12.8 shows how SetWmiDataItem calls FailWMIRequest to reject the WMI IRP. FailWMIRequest fails the IRP with STATUS_WMI_GUID_NOT_FOUND if an invalid GUID was given, or STATUS_INVALID_DEVICE_REQUEST otherwise.

Listing 12.8 SetWmiDataItem routine

NTSTATUS SetWmiDataItem(IN PDEVICE_OBJECT fdo, IN PIRP Irp, IN ULONG GuidIndex, IN ULONG InstanceIndex, IN ULONG DataltemId, IN ULONG BufferSize, IN PUCHAR PBuffer) {
 DebugPrint("SetWmiDataItem: GuidIndex %d, InstanceIndex %d, DataItemId %d, BufferSize %d",
  GuidIndex.InstanceIndex, DataItemId, BufferSize);
 return FailWMIRequest(fdo, Irp, GuidIndex);
}
NTSTATUS FailWMIRequest(IN PDEVICE_OBJECT fdo, IN PIRP Irp, IN ULONG GuidIndex) {
 DebugPrint("FailWMIRequest: GuidIndex %d",GuidIndex);
 NTSTATUS status;
 if (GuidIndex<0 || GuidIndex>=GUID_COUNT) status = STATUS_WMI_GUID_NOT_FOUND;
 else status = STATUS_INVALID_DEVICE_REQUEST;
 status = WmiCompleteRequest(fdo, Irp, status, 0, IO_NO_INCREMENT);
 return status;
}

ExecuteWmiMethod Handler

Listing 12.9 shows how I think the optional ExecuteWmiMethod routine should be implemented. As I stated earlier, I could not get mofcomp to compile the PowerDown method in the Wdm3Information WMI data block. Therefore, I have not so far been able to test this method.

Listing 12.9 ExecuteWmiMethod routine

NTSTATUS ExecuteWmiMethod(IN PDEVICE_OBJECT fdo, IN PIRP Irp, IN ULONG GuidIndex, IN ULONG InstanceIndex, IN ULONG MethodId, IN ULONG InBufferSize, IN ULONG OutBufferSize, IN OUT PUCHAR Buffer) {
 DebugPrint("ExecuteWmiMethod: GuidIndex %d, InstanceIndex %d, "
  "MethodId %d, InBufferSize %d OutBufferSize %d",
  GuidIndex, InstanceIndex, MethodId, InBufferSize, OutBufferSize);
 PWOM3_DEVICE_EXTENSION dx = (PWDM3_DEVICE_EXTENSION)fdo->DeviceExtension;
 if (GuidIndex==WDM3_WMI_GUID_INDEX && MethodId==0) {
  DebugPrintMsg("ExecuteWmiMethod: PowerDown method");
  // Power Down
  if (dx->PowerState<PowerDeviceD3) SendDeviceSetPower(dx, PowerDeviceD3);
  return WmiCompleteRequest(fdo, Irp, STATUS_SUCCESS, 0, IO_NO_INCREMENT);
 }
 return FailWMIRequest(fdo, Irp, GuidIndex);
}

Firing WMI Events

To fire a WMI event, simply call WmiFireEvent, passing the relevant WMI event block GUID, the InstanceIndex, and any event data. WmiFireEvent makes the appropriate call to IoWmiWriteEvent.

The Wdm3 driver provides a helper function Wdm3FireEvent, shown in Listing 12.10. This takes a NULL-terminated wide string, formats it into a Wdm3Event message, and calls WmiFireEvent.

A user application must ask for events first by setting an Enable flag. This request arrives at the driver in its WmiFunctionControl optional callback routine. If the Function parameter is WmiEventControl, the new Enable setting is stored in the WMIEventEnabled flag in the device extension. WmiFunctionControl is also called to ask a driver to collect WMI data blocks that were marked as being expensive to collect.

The WMIEventEnabled flag is initially FALSE, which means that no events are generated. As I have found no way of registering for events, the Wdm3FireEvent and WmiFunctionControl functions have not been tested.

Listing 12.10 Wdm3FireEvent and WmiFunctionControl routines

void Wdm3FireEvent(IN PDEVICE_OBJECT fdo, wchar_t* Msg) {
 DebugPrint("Wdm3FireEvent: Msg %S", Msg);
 PWDM3_DEVICE_EXTENSION dx = (PWDM3_DEVICE_EXTENSION)fdo->DeviceExtension;
 if (!dx->WMIEventEnabled) return;
 // Get MsgLen in bytes
 int MsgLen = 0;
 wchar_t* Msg2 = Msg;
 while (*Msg2++!=0) MsgLen += sizeof(wchar_t);
 // Allocate event memory
 PUSHORT pData = (PUSHORT)ExAllocatePool(NonPagedPool, MsgLen+2);
 if (pData==NULL) return;
 PUSHORT pData2 = pData;
 *pData2++ = MsgLen;
 RtlMoveMemory(pData2, Msg, MsgLen);
 WmiFireEvent(fdo, (LPGUID)&WDM3_WMI_EVENT_GUID, 0, MsgLen+2, pData);
}
NTSTATUS WmiFunctionControl(IN PDEVICE_OBJECT fdo, IN PIRP Irp, IN ULONG GuidIndex, IN WMIENABLEDISABLECONTROL Function, IN BOOLEAN Enable) {
 DebugPrint("WmiFunctionControl: GuidIndex %d, Function %d, Enable %d",
  GuidIndex, Function, Enable);
 PWDM3_DEVICE_EXTENSION dx = (PWDM3_DEVICE_EXTENSION)fdo->DeviceExtension;
 if (GuidIndex==WDM3_WMI_EVENT_GUID_INDEX && Function==WmiEventControl) {
  DebugPrint("WmiFunctionControl: Event enable %d", Enable);
  dx->WMIEventEnabled = Enable;
  return WmiCompleteRequest(fdo, Irp, STATUS_SUCCESS, 0, IO_NO_INCREMENT);
 }
 return FailWMIRequest(fdo, Irp, GuidIndex);
}

Îãëàâëåíèå êíèãè

Îãëàâëåíèå ñòàòüè/êíèãè

Ãåíåðàöèÿ: 1.058. Çàïðîñîâ Ê ÁÄ/Cache: 3 / 0
ïîäåëèòüñÿ
Ââåðõ Âíèç