The NT Insider

WPP Tracing Part II -- Coexisting Peacefully with WMILIB
(By: The NT Insider, Vol 12, Issue 2, March-April 2005 | Published: 15-Mar-05| Modified: 22-Mar-05)

Welcome to the second and final chapter in our exploration of WPP tracing. If you haven't read Part I yet (front page, this issue), I highly suggest you do so to orient yourself, even if you already feel comfortable with WPP tracing. Also, this article assumes the following:

  • You are 100% comfortable with WMILIB.
  • The driver that you're trying to add WPP tracing to uses WMILIB to realize its WMI support. If you've built your own WMI handling code, the content of the article will still be useful but you'll need to figure out the integration details yourself.

Now that we're on the same page, let's look a bit more into the Verifier error that we stumbled upon with WPP tracing and how we subsequently developed our workaround. If all goes well, during the course of this discussion you'll become a bit more comfortable with some tracing details and be able to work out any problems you might face in the future.

Where Does That Verifier Error Come From?
We've mentioned a couple of times that running a driver with the WPP_SYSTEMCONTROL macro leads to a Verifier error on Server 2003. Here's a sample output from that error:

********************************************************************
* THIS VALIDATION BUG IS FATAL AND WILL CAUSE THE VERIFIER TO HALT  *
* WINDOWS (BUGCHECK) WHEN THE MACHINE IS NOT UNDER A KERNEL DEBUGGER!*
*********************************************************************

WDM DRIVER ERROR: [USBFX2LK.sys @ 0xF764D080] An IRP dispatch handler
                  has returned without passing down or completing 
                  this Irp or someone forgot to return
                  STATUS_PENDING. (Irp = 824B4ED8).
IRP_MJ_SYSTEM_CONTROL.IRP_MN_BOGUS
[ DevObj=81B3CE40, FileObject=00000000, Parameters=81D52568 00000000 00000000 00000000 ]
http://www.microsoft.com/hwdq/bc/default.asp?os=5.2.3790&major=0xc9&minor=0x226&lang=0x9
_Break, Ignore, Zap, Remove, Disable all (bizrd)?

The thing that threw us for a loop when we saw this was that we knew we had a proper IRP_MJ_SYSTEM_CONTROL function and that this driver worked when built with tracing disabled or when built for XP and later. Even worse, we weren't being called with any WMI IRPs at all, which didn't bode well for the PerfMon integration code that was in this driver.

After a good hard look, it turned out that the only thing that we were doing differently when things worked, was not calling WPP_SYSTEMCONTROL, so it was time to figure out what exactly that did.

Where Do These WPP Macros Come From?
Obviously, the first thing to look for was the definition of the WPP_SYSTEMCONTROL macro. We checked in the .H files in the DDK, which turned up nothing. We first attributed this to the XP Search Dog's inability to find anything, but in this one case it was right. No WPP macros at all are defined within the DDK's include files. However, we did find the WPP_SYSTEMCONTROL macro defined within the TMH files that were generated as part of the build. After doing a full search on the DDK, we hit upon where the true heart of WPP tracing lies within the DDK: the bin\wppconfig\rev1 directory.

Tracing Template Files
As you browse the wppconfig directory, you'll find a bunch of files with the .TPL extension, which indicates that they are tracing template files. These files actually contain all of the code necessary to support WPP tracing on the driver side of things, and are the basis for the TMH files that get generated and included within your driver. You might have figured this out after reading the previous article on WPP, when we added the gen:{km-w2k.tpl}*.tmh line in our SOURCES file to indicate that the Win2K template should be used to create the TMH files.

While perusing these TPL files, it's important to realize that WPP is actually just a set of macros and functions that exposes a subset of WMI's event tracing facilities to a driver. This is the reason why you will often see the terms WPP and ETW (Event Tracing For Windows) used interchangeably.

Because we're pretty interested in WPP_SYSTEMCONTROL and its side effects at this point, let's check out its definition within km-init.tpl (below).

WPP_SYSTEMCONTROL Defined
Here's the code from km-init.tpl that deals with the WPP_SYSTEMCONTROL macro:

#if !defined(WPP_TRACE_W2K_COMPATABILITY)
#define WPP_SYSTEMCONTROL(PDO)
#define WPP_SYSTEMCONTROL2(PDO, offset)
#else  // #if !defined(WPP_TRACE_W2K_COMPATABILITY)

ULONG_PTR WPP_Global_NextDeviceOffsetInDeviceExtension = -1;

#define WPP_SYSTEMCONTROL(PDO) \
        PDO->MajorFunction[ IRP_MJ_SYSTEM_CONTROL ] = WPPSystemControlDispatch;
#define WPP_SYSTEMCONTROL2(PDO, offset) \
        WPP_SYSTEMCONTROL(PDO); WPP_Global_NextDeviceOffsetInDeviceExtension = (ULONG_PTR)offset;

// Routine to handle the System Control in W2K
NTSTATUS
WPPSystemControlDispatch(
    IN PDEVICE_OBJECT pDO,
    IN PIRP Irp
    );

#ifdef ALLOC_PRAGMA
    #pragma alloc_text( PAGE, WPPSystemControlDispatch)
#endif // ALLOC_PRAGMA

We can see from this that WPP_SYSTEMCONTROL does absolutely nothing when built for non-Win2K platforms, as does this obviously twice as good version WPP_SYSTEMCONTROL2, which we'll get to later.

However, in the Win2K case we get a brand new variable WPP_Global_NextDeviceOffsetInDeviceExtension and a mildly disturbing side effect of calling WPP_SYSTEMCONTROL: our IRP_MJ_SYSTEM_CONTROL entry point is completely obliterated and replaced with WPPSystemControlDispatch. This explains why our driver wasn't being called with WMI requests when built for Win2K, but it's also looking like we're not going to be able to support WPP tracing and WMI in the same driver if we use this macro.

 

We can also see that the WPP_SYSTEMCONTROL2 macro is almost identical to WPP_SYSTEMCONTROL. However, it also fills in WPP_Global_NextDeviceOffsetInDeviceExtension with whatever value we pass as the second parameter. We'll get to what the side effect of this is in a moment.

WppSystemControlDispatch Defined
Now that we know that calling WPP_SYSTEMCONTROL replaces our IRP_MJ_SYSTEM_CONTROL handler with WppSystemControlDispatch, the next step is to check out the implementation of that routine and see if we can determine the cause of the Verifier error. Conveniently, this routine is also defined within km-init.tpl and directly follows the previous listing from this file:

// Routine to handle the System Control in W2K
NTSTATUS
WPPSystemControlDispatch(
    IN PDEVICE_OBJECT pDO,
    IN PIRP Irp
    )
{

    PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
    ULONG BufferSize = irpSp->Parameters.WMI.BufferSize;
    PVOID Buffer = irpSp->Parameters.WMI.Buffer;
    ULONG ReturnSize = 0;
    NTSTATUS Status = STATUS_SUCCESS;

    WppDebug(0,("WPPSYSTEMCONTROL\n"));

    if (pDO == (PDEVICE_OBJECT)irpSp->Parameters.WMI.ProviderId) {

   CASE 1:

#if defined(WPP_TRACE_W2K_COMPATABILITY)
   //To differentiate between the case where wmilib has
        //already filled in parts of the buffer
   if (irpSp->MinorFunction == IRP_MN_REGINFO)
            RtlZeroMemory(Buffer, BufferSize);
#endif
        Status = WppTraceCallback((UCHAR)(irpSp->MinorFunction),
                                          NULL,
                                          BufferSize,
                                          Buffer,
                                          &WPP_CB[0],
                                          &ReturnSize);

        WppDebug(0,("WPPSYSTEMCONTROL Status 0x%08X\n",Status));

        Irp->IoStatus.Status = Status;
        Irp->IoStatus.Information = ReturnSize;
        IoCompleteRequest( Irp, IO_NO_INCREMENT );
        return Status;

    } else if (WPP_Global_NextDeviceOffsetInDeviceExtension != -1) {

   CASE 2:

        ULONG_PTR t;

        WppDebug(0,("WPPSYSTEMCONTROL - not for us\n"));

        //
        // Set current stack back one.
        //
        IoSkipCurrentIrpStackLocation( Irp );
        //
        // Pass the call to the next driver.
        //
        t = (ULONG_PTR)pDO->DeviceExtension;
        t += WPP_Global_NextDeviceOffsetInDeviceExtension;
        return IoCallDriver((PDEVICE_OBJECT)t,Irp);

    } else {
  
   CASE 3:

        //unable to pass down -- what to do?
        //don't change irp status - IO defaults to failure
        return Irp->IoStatus.Status;
    }
}

What's Wrong With This Picture?
There are two bugs within WppSystemControlDispatch, take a few minutes to try and pick them out. Here are a couple of hints: the first bug will cause a system crash, and the second bug is the cause of the Verifier error that we are receiving.

We'll discuss the Verifier error first, because it will lead to a discussion of the second error. In our case we used WPP_SYSTEMCONTROL, so WPP_Global_NextDevice OffsetInDeviceExtension is -1. When Verifier sends us the bogus WMI IRP, the request is not targeted at our device and we end up in CASE 3. When this routine reaches CASE 3, it simply returns whatever status was previously set in the IRP. The IRP is not passed down, nor is it even completed, which is definitely an error and is correctly flagged by Verifier.

So, looking at the rest of the code, it seems pretty clear that we should be using WPP_SYSTEMCONTROL2, if anything, because that will lead us to CASE 2. CASE 2 attempts to pass the IRP on to the next driver and it does so by taking the device extension of the passed-in device object, adding WPP_Global_NextDeviceOffsetInDeviceExtension to it, and treating that address as the address of a device object. Well, that certainly isn't going to work. From the definition of the variable, I'd expect to call WPP_SYSTEMCONTROL2 in this fashion:

    WPP_SYSTEMCONTROL2(DriverObject,
   FIELD_OFFSET(MY_DEVICE_EXT, DeviceToSendIrpsTo);

What the calculation in CASE 2 will result in then will be a PDEVICE_OBJECT* and not a PDEVICE_OBJECT. This is the other bug in the code, and the one that will result in a system crash.

But, we've gone off on a tangent here because all of this is moot, anyway. If you use WPP_SYSTEMCONTROL or WPP_SYSTEMCONTROL2 in your driver, you lose your ability to support WMI, which isn't good at all. This leads to our first step in supporting WPP and WMI in the same driver:

STEP 1: Remove any calls to WPP_SYSTEMCONTROL or WPP_SYSTEMCONTROL2 from your driver.

So What's A Brother To Do?
All is not lost, and hopefully it's becoming clear to you what the trick is to getting WPP and WMI to coexist. The code within CASE 1 of WppSystemControlDispatch is what needs to be executed to support WPP tracing in a driver, so we just need to incorporate that code into our IRP_MJ_SYSTEM_ CONTROL handler instead of completely replacing it with the WPP IRP_MJ_SYSTEM_CONTROL handler.

WPP Registration vs. WMI Registration
We need to determine what exactly it is that WppTraceCallback does before we can integrate calls to it from within our WMI code. Once again, we don't need to leave the comfort of km-init.tpl to get full source for this routine. Below is a heavily edited version of the function that only shows the parts necessary for our discussion:

NTSTATUS
WppTraceCallback(
    IN UCHAR minorFunction,
    IN PVOID DataPath,
    IN ULONG BufferLength,
    IN PVOID Buffer,
    IN PVOID Context,
    OUT PULONG Size
    )
{
    WPP_PROJECT_CONTROL_BLOCK *cb = (WPP_PROJECT_CONTROL_BLOCK*)Context;

    *Size = 0;
    switch(minorFunction)
    {
        case IRP_MN_REGINFO:
        {
            CASE 1:
            PWMIREGINFOW wmiRegInfo;

#if defined(WPP_TRACE_W2K_COMPATABILITY)

            wmiRegInfo = (PWMIREGINFO)Buffer;
           
            if (wmiRegInfo->GuidCount >= 1) {
                // Replace the null trace GUID with the driver's
                // trace control GUID
                wmiRegInfo->WmiRegGuid[wmiRegInfo->GuidCount-1].Guid  =
                                                 *cb->Registration.ControlGuid;
                wmiRegInfo->WmiRegGuid[wmiRegInfo->GuidCount-1].Flags =
                      WMIREG_FLAG_TRACE_CONTROL_GUID | WMIREG_FLAG_TRACED_GUID;
                *Size= wmiRegInfo->BufferSize;
                status = STATUS_SUCCESS;

#ifdef WPP_GLOBALLOGGER
                //
                // Code to check if Global logger is active removed
                // ...
                //

#endif  //#ifdef WPP_GLOBALLOGGER
             break;
         }
#endif
            //
            // Code removed from here because we don't really care...
            // ...
            //

            break;
        }

        case IRP_MN_ENABLE_EVENTS:
        case IRP_MN_DISABLE_EVENTS:
        {
            //
            // Code to handle enabling/disabling tracing removed
            // ...
            //
            break;
        }
        default:
        {
            CASE 2:
            status = STATUS_INVALID_DEVICE_REQUEST;
            break;
        }

    }
    return(status);
}

Let's first handle what's going on in CASE 1 above, which, it turns out, contains a couple of critical hints about how we're going to get this working.

NOTE: When using WMILIB, remember that you must fill in a WMIREGINFO structure that indicates the number of WMI data items that you support, via the GuidCount, and the GUIDs associated with them. On Windows 2000, the actual registration of these GUIDs with the system happens during IRP_MJ_SYSTEM_CONTROL/IRP_MN_REGINFO processing, and on XP and later the registration happens during IRP_MJ_SYSTEM_CONTROL/IRP_MN_REGINFO_EX processing. This little detail about the minor function code used for WMI registration isn't really important when using WMILIB, but it will be extremely important when trying to get WPP and WMI to work together. 

WppTraceCallback and IRP_MN_REGINFO
During IRP_MN_REGINFO processing, the WppTraceCallback code looks to see if a list of GUIDs has been registered with WMILIB. It does this by checking to see if the GuidCount member of the WMIREGINFO structure is greater than one. If there are GUIDs, WppTraceCallback replaces the null trace GUID with the driver's trace control GUID. A quick read of that code indicates that it expects this "null trace GUID" to be at the end of the GUID list in the WMIREGINFO structure, so now it's just a matter of figuring out what the GUID is. The best chance of us finding that would be to search the TPL files for instances of the DEFINE_GUID macro (the macro that is used to, well, define GUIDs). This yields one hit in control.tpl:

#if defined(WPP_TRACE_W2K_COMPATABILITY)
DEFINE_GUID(WPP_TRACE_CONTROL_NULL_GUID, 0x00000000L, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
#endif

So this really is just a NULL GUID and from the code in CASE 1 it appears to just be a placeholder for your trace control GUID in the WMIGUIDREGINFO structure that you pass to WMILIB. This then leads us to step two in the peaceful coexistence of WPP and WMI:

Step 2: Add the WPP_TRACE_CONTROL_GUID to the end of the WMIGUIDREGINFO structure that you pass to WMILIB.

One quick thing to keep in mind before we move on is that the WppTraceCallback routine only handles IRP_MN_REGINFO and IRP_MN_ENABLE/DISABLE_ EVENT requests. It simply ignores everything else.

IRP_MJ_SYSTEM_CONTROL Changes
So far, you've removed any calls to WPP_ SYSTEMCONTROL from your driver and added the WPP_TRACE_CONTROL_NULL_GUID to the end of your WMIGUIDREGINFO structure. Let's see what else we'll need to modify in order to achieve WMI harmony.

Here's an IRP_MJ_SYSTEM_CONTROL handler for a WDM driver that uses WMILIB for its WMI processing:

NTSTATUS
NothingSystemControl(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    ) {

    PNOTHING_DEVICE_EXT    devExt;
    SYSCTL_IRP_DISPOSITION disposition;
    NTSTATUS               status;

    devExt = (PNOTHING_DEVICE_EXT)DeviceObject

->DeviceExtension;
    //
    // Pass the request to WMILIB. We will be called at one
    // of our registered callbacks if this is a request that
    // we need to handle.
    //
    status = WmiSystemControl(&devExt
-
>WmiLibInfo,
                              DeviceObject, 
                              Irp,
                              &disposition);
    //
    // Check the disposition of the request so that we can determine
    // what to do.
    //
    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
            // targeted at a device lower in the stack.
            //
            IoSkipCurrentIrpStackLocation(Irp);
            status = IoCallDriver(devExt
-
>DeviceToSendIrpsTo, Irp);
            break;
        }

        default:
        {
            //
            // We really should never get here, 
            // but if we do just forward?
            //
           ASSERTMSG("Unkown SYSCTL_IRP_DISPOSITION from WmiSystemControl", FALSE);
            IoSkipCurrentIrpStackLocation(Irp);
            status = IoCallDriver(devExt
->DeviceToSendIrpsTo, Irp);
          
break;
        
}       
   
}

 

    return(status);
}

There's really not much to grok here as you're already familiar with WMILIB, so let's look at what it takes to add the appropriate calls to WPP.

WPP Don't Know EX
We've seen that in the WppTraceCallback routine, WPP registration is done during IRP_MN_REGINFO processing. That's all fine and good when running on Win2K, but on XP and later WMI registration is done with IRP_MN_REGINFO_EX. WMILIB is happy to properly process IRP_MN_REGINFO_EX requests, but WppTrace Callback will simply ignore them. This means that if you add calls to WppTraceCallback in your IRP_MJ_SYSTEM_ CONTROL handler and run the driver on Windows XP, your WMI code will work, but tracing will not. Luckily for us, drivers built for Win2K, where IRP_MN_REGINFO doesn't exist, are guaranteed to work on XP and later.

The way that compatibility is achieved in this particular scenario is that if an IRP_MN_REGINFO_EX request is sent to a driver and fails, the system falls back and sends an IRP_MN_REGINFO request. Because WMILIB can handle either IRP_MN_REGINFO or IRP_MN_REGINFO_EX and WPP can only handle IRP_MN_REGINFO, by failing the EX version we end up with the common denominator and will be able to get WMILIB and WPP properly initialized.

Step 3: Fail all IRP_MJ_SYSTEM_CONTROL/IRP_MN_REGINFO_EX requests.

Back to WPPSystemControlDispatch
There's a line within WppSystemControlDispatch that we didn't mention earlier, but now becomes important:

#if defined(WPP_TRACE_W2K_COMPATABILITY)
        //To differentiate between the case where wmilib has
        //already filled in parts of the buffer
        if (irpSp->MinorFunction == IRP_MN_REGINFO)
            RtlZeroMemory(Buffer, BufferSize);
#endif

Before WMILIB has called you back at your QueryWmiRegInfo callback, you might get a few calls for IRP_MN_REGINFO with an uninitialized buffer. This is not a good thing at all, because the WppTraceCallback code does no sanity checking on the buffer whatsoever, and this can lead to an invalid memory access. This means that you need to include some logic in your code to zero this buffer every time you are called up until the point that your QueryWmiRegInfo callback is called.

Step 4: Add a BOOLEAN to your device extension that is set to TRUE in your QueryWmiRegInfo callback. Until this flag is set to TRUE, zero the data buffer sent with all IRP_MN_REGINFO requests.

Calling WppTraceCallback
It's finally time to add the final step: adding the call to WppTraceCallback within your driver. If you search the TPL files once more to find the definition of this routine, you'll find this in control.tpl:

NTSTATUS
WppTraceCallback(
    IN UCHAR minorFunction,
    IN PVOID DataPath,
    IN ULONG BufferLength,
    IN PVOID Buffer,
    IN PVOID Context,
    OUT PULONG Size
    ) ;

#define WPP_TRACE_CONTROL(Function,Buffer,BufferSize,ReturnSize) \
      WppTraceCallback(Function,NULL,BufferSize,Buffer,&WPP_CB[0],&ReturnSize);

We'll use the provided macro to call the routine, because it has fewer parameters and maybe that means we can leave early today.

What we want is for WMILIB to register itself and then for WPP to steal the last entry in the GUID table. Also, we want the WPP code to be able to handle the IRP_MN_ENABLE/DISABLE_EVENT requests that it needs in order to support turning the tracing on and off. But we also don't want to prevent WMILIB from handling any requests for custom events that we have created.

The only way to achieve this is by adding our final step:

Step 5: Add a call to WPP_TRACE_CONTROL in the IrpNotProcessed case of your IRP_MJ_SYSTEM_CONTROL routine. If WMILIB failed the request but WPP_TRACE_CONTROL completed with success, change the status and information fields of the IRP.

After following all of these steps you should end up with an IRP_MJ_SYSTEM_CONTROL routine that looks something like this:

NTSTATUS
NothingSystemControl(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    ) {

    PNOTHING_DEVICE_EXT    devExt;
    SYSCTL_IRP_DISPOSITION disposition;
    NTSTATUS               status;

    devExt = (PNOTHING_DEVICE_EXT)DeviceObject->DeviceExtension;

#ifdef WPP_TRACING
#ifdef W2K


    //
    // One of the mythical things that people tell you that
    //  you need to do to get WPP tracing to work on Windows
    //  2000 is to call the WPP_SYSTEMCONTROL macro in your
    //  DriverEntry entry point. The problem with this is that
    //  what is does is actually trash your IRP_MJ_SYSTEM_CONTROL
    //  entry point, meaning that you can no longer expose your
    //  driver through WMI. What we will do to get around this
    //  is to incorporate a call to the function that the WPP
    //  IRP_MJ_SYSTEM_CONTROL handler calls in order to handle
    //  WPP data. By doing this, we can satisfy WPP while
    //  also supporting our own WMI data.
    //
    //  You can find the code for the WPP SYSTEM_CONTROL
    //  handler (WPPSystemControlDispatch) in the km-init.tpl
    //  file located in the DDK's bin\wppconfig directory.
    //
    if (DeviceObject == (PDEVICE_OBJECT)stack->Parameters.WMI.ProviderId) {

        //
        // If this is a REG_INFO request and we have not registered
        //  with WMI yet, we must zero the buffer. The reason for this
        //  is that the WPP code will just blindly use it without
        //  verifying any of the members of the structure. We learned
        //  this trick from the WPPSystemControl handler code in
        //  km-init.tpl
        //
        if (stack->MinorFunction == IRP_MN_REGINFO &&
            !devExt->WmiRegistered) {

            RtlZeroMemory(stack->Parameters.WMI.Buffer,
                          stack->Parameters.WMI.BufferSize);

        }

        //
        // Yet another nice thing about trying to get tracing and WMI
        //  support working is that we can NOT allow an IRP_MN_REGINFO_EX
        //  request to get to the WMI library, we must do the registering
        //  of WMI through an IRP_MN_REGINFO request.
        //
        // Why? Because the W2K tracing code has no idea what an
        //  IRP_MN_REGINFO_EX is, so it will refuse to setup the
        //  parts necessary for WPP tracing support. So, if
        //  we see an IRP_MN_REGINFO_EX request come in and we're
        //  built for Windows 2000, that means that we need to
        //  reject it to force the code to send us an IRP_MN_REGINFO
        //  request instead.
        //
        // IRP_MN_REGINFO_EX isn't defined for Windows 2000, so we use
        //  its constant value of 0xb here.
        //
        if (stack->MinorFunction == 0xb) {

            //
            // Fail the request
            //
            Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
            Irp->IoStatus.Information = 0;
            IoCompleteRequest(Irp, IO_NO_INCREMENT);
            OsrDecrementOutstandingIoCount(devExt,__FILE__,__LINE__);
            return STATUS_INVALID_DEVICE_REQUEST;

        }

    }

#endif // W2K
#endif //WPP_TRACING

    //
    // Call WMILIB to process the request.
    //

    status = WmiSystemControl(&devExt->WmiLibInfo,
                                 DeviceObject,
                                 Irp,
                                 &disposition);

    //
    // Check the disposition of the request, so that we can determine
    // what to do.
    //

    switch(disposition) {

        case IrpProcessed:
        {
            //
            // This IRP has been processed and may be completed or pending.
      //
            break;
        }
       
        case IrpNotCompleted:
        {

#ifdef WPP_TRACING
#ifdef W2K

            //
            // If the WMILIB didn't complete the IRP, then let's
            //  see if WPP will.
            //
            NTSTATUS wppStatus;
            wppStatus = WPP_TRACE_CONTROL(stack->MinorFunction,
                                          stack->Parameters.WMI.Buffer,
                                          stack->Parameters.WMI.BufferSize,
                                          bytesReturned);

            if (!NT_SUCCESS(status) &&
                NT_SUCCESS(wppStatus)) {
               

                //
                // WMILIB failed the IRP, but WPP completed it
                //  with success. Therefore, we'll change the IRP's
                //  status and bytes returned count to what WPP
                //  would like them to be.
                //
                Irp->IoStatus.Status = wppStatus;
                Irp->IoStatus.Information = bytesReturned;
                status = wppStatus;

            }

#endif // W2K
#endif //WPP_TRACING
 
            //
            // 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 targeted
            // at a device lower in the stack.

            IoSkipCurrentIrpStackLocation (Irp);
            status = IoCallDriver (devExt->DeviceToSendIrpsTo, Irp);
            break;
        }
                                   
        default:
        {
             //
            // We really should never get here, but if we do just forward.... 
            //
            ASSERTMSG("Unkown SYSCTL_IRP_DISPOSITION from WmiSystemControl",
                      FALSE);
            IoSkipCurrentIrpStackLocation(Irp);
            status = IoCallDriver(devExt->DeviceToSendIrpsTo, Irp);
            break;
       }       
    }

    return(status);

}

Yes, It Is That Complicated!
Unfortunately, it takes quite a bit of work to get WPP and WMILIB to coexist, but luckily it's mostly just a cut and paste job once you've seen how it's done.

For those of you that fell asleep during the first act, here's a summary of the steps required:

  • Remove any calls to WPP_SYSTEMCONTOL or WPP_ SYSTEMCONTOL2 from your driver (they're evil).
  • Add the WPP_TRACE_CONTROL_GUID to the end of the WMIGUIDREGINFO structure that you pass to WMILIB.
  • Fail all IRP_MJ_SYSTEM_CONTROL/IRP_MN_ REGINFO_EX requests.
  • Add a BOOLEAN to your device extension that is set to TRUE in your QueryWmiRegInfo callback. Until this flag is set to TRUE, zero the data buffer sent with all IRP_MN_ REGINFO requests.
  • Add a call to WPP_TRACE_CONTROL in the IrpNotProcessed case of your IRP_MJ_SYSTEM_ CONTROL routine. If WMILIB failed the request but WPP_TRACE_CONTROL completed with success, change the status and information fields of the IRP.

Good luck out there in the WPP battlefield! If you have any war stories or other tips that you've picked up along the way let us know at NTInsider@osr.com and maybe we'll get working on Part III....

This article was printed from OSR Online http://www.osronline.com

Copyright 2017 OSR Open Systems Resources, Inc.