You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1866 lines
53 KiB
1866 lines
53 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
wmisamp.c
|
|
|
|
Abstract:
|
|
|
|
Sample device driver whose purpose is to show various mechanisms for
|
|
using WMI in a kernel mode driver. Specific things shown are
|
|
|
|
Events
|
|
Event references
|
|
Queries, Sets
|
|
Methods
|
|
Updating guid registration
|
|
|
|
Environment:
|
|
|
|
WDM, NT and Windows 98
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include <WDM.H>
|
|
|
|
#include "filter.h"
|
|
|
|
#include <wmistr.h>
|
|
#include <wmiguid.h>
|
|
|
|
//
|
|
// default Date/Time structure
|
|
#define FilterDateTime L"19940525133015.000000-300"
|
|
|
|
NTSTATUS
|
|
FilterFunctionControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG GuidIndex,
|
|
IN WMIENABLEDISABLECONTROL Function,
|
|
IN BOOLEAN Enable
|
|
);
|
|
|
|
NTSTATUS
|
|
FilterExecuteWmiMethod(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG GuidIndex,
|
|
IN ULONG InstanceIndex,
|
|
IN ULONG MethodId,
|
|
IN ULONG InBufferSize,
|
|
IN ULONG OutBufferSize,
|
|
IN PUCHAR Buffer
|
|
);
|
|
|
|
NTSTATUS
|
|
FilterSetWmiDataItem(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG InstanceIndex,
|
|
IN ULONG GuidIndex,
|
|
IN ULONG DataItemId,
|
|
IN ULONG BufferSize,
|
|
IN PUCHAR Buffer
|
|
);
|
|
|
|
NTSTATUS
|
|
FilterSetWmiDataBlock(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG GuidIndex,
|
|
IN ULONG InstanceIndex,
|
|
IN ULONG BufferSize,
|
|
IN PUCHAR Buffer
|
|
);
|
|
|
|
NTSTATUS
|
|
FilterQueryWmiDataBlock(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG GuidIndex,
|
|
IN ULONG InstanceIndex,
|
|
IN ULONG InstanceCount,
|
|
IN OUT PULONG InstanceLengthArray,
|
|
IN ULONG BufferAvail,
|
|
OUT PUCHAR Buffer
|
|
);
|
|
|
|
NTSTATUS
|
|
FilterQueryWmiRegInfo(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
OUT ULONG *RegFlags,
|
|
OUT PUNICODE_STRING InstanceName,
|
|
OUT PUNICODE_STRING *RegistryPath,
|
|
OUT PUNICODE_STRING MofResourceName,
|
|
OUT PDEVICE_OBJECT *Pdo
|
|
);
|
|
|
|
void FilterSetEc1(
|
|
struct DEVICE_EXTENSION * devExt,
|
|
PUCHAR Buffer,
|
|
ULONG Length,
|
|
ULONG Index
|
|
);
|
|
|
|
void FilterSetEc2(
|
|
struct DEVICE_EXTENSION * devExt,
|
|
PUCHAR Buffer,
|
|
ULONG Length,
|
|
ULONG Index
|
|
);
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE,FilterQueryWmiRegInfo)
|
|
#pragma alloc_text(PAGE,FilterQueryWmiDataBlock)
|
|
#pragma alloc_text(PAGE,FilterSetWmiDataBlock)
|
|
#pragma alloc_text(PAGE,FilterSetWmiDataItem)
|
|
#pragma alloc_text(PAGE,FilterExecuteWmiMethod)
|
|
#pragma alloc_text(PAGE,FilterFunctionControl)
|
|
#endif
|
|
|
|
|
|
#ifdef USE_BINARY_MOF_QUERY
|
|
//
|
|
// MOF data can be reported by a device driver via a resource attached to
|
|
// the device drivers image file or in response to a query on the binary
|
|
// mof data guid. Here we define global variables containing the binary mof
|
|
// data to return in response to a binary mof guid query. Note that this
|
|
// data is defined to be in a PAGED data segment since it does not need to
|
|
// be in nonpaged memory. Note that instead of a single large mof file
|
|
// we could have broken it into multiple individual files. Each file would
|
|
// have its own binary mof data buffer and get reported via a different
|
|
// instance of the binary mof guid. By mixing and matching the different
|
|
// sets of binary mof data buffers a "dynamic" composite mof would be created.
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma data_seg("PAGED")
|
|
#endif
|
|
|
|
UCHAR FilterBinaryMofData[] =
|
|
{
|
|
#include "filter.x"
|
|
};
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma data_seg()
|
|
#endif
|
|
#endif
|
|
|
|
|
|
//
|
|
// Create data structures for identifying the guids and reporting them to
|
|
// WMI. Since the WMILIB callbacks pass an index into the guid list we make
|
|
// definitions for the various guids indicies.
|
|
//
|
|
#define FilterClass1 0
|
|
#define FilterClass2 1
|
|
#define FilterClass3 2
|
|
#define FilterClass4 3
|
|
#define FilterClass5 4
|
|
#define FilterClass6 5
|
|
#define FilterClass7 6
|
|
#define FilterGetSetData 7
|
|
#define FilterFireEvent 8
|
|
#define FilterEventClass1 9
|
|
#define FilterEventClass2 10
|
|
#define FilterEventClass3 11
|
|
#define FilterEventClass4 12
|
|
#define FilterEventClass5 13
|
|
#define FilterEventClass6 14
|
|
#define FilterEventClass7 15
|
|
#define FilterEventReferenceClass 16
|
|
#define FilterIrpCount 17
|
|
#define BinaryMofGuid 18
|
|
|
|
GUID FilterClass1Guid = Vendor_SampleClass1Guid;
|
|
GUID FilterClass2Guid = Vendor_SampleClass2Guid;
|
|
GUID FilterClass3Guid = Vendor_SampleClass3Guid;
|
|
GUID FilterClass4Guid = Vendor_SampleClass4Guid;
|
|
GUID FilterClass5Guid = Vendor_SampleClass5Guid;
|
|
GUID FilterClass6Guid = Vendor_SampleClass6Guid;
|
|
GUID FilterClass7Guid = Vendor_SampleClass7Guid;
|
|
GUID FilterGetSetDataGuid = Vendor_GetSetDataGuid;
|
|
GUID FilterFireEventGuid = Vendor_FireEventGuid;
|
|
GUID FilterEventClass1Guid = Vendor_EventClass1Guid;
|
|
GUID FilterEventClass2Guid = Vendor_EventClass2Guid;
|
|
GUID FilterEventClass3Guid = Vendor_EventClass3Guid;
|
|
GUID FilterEventClass4Guid = Vendor_EventClass4Guid;
|
|
GUID FilterEventClass5Guid = Vendor_EventClass5Guid;
|
|
GUID FilterEventClass6Guid = Vendor_EventClass6Guid;
|
|
GUID FilterEventClass7Guid = Vendor_EventClass7Guid;
|
|
GUID FilterEventReferenceClassGuid = Vendor_EventReferenceClassGuid;
|
|
GUID FilterIrpCountGuid = Vendor_IrpCounterGuid;
|
|
GUID FilterBinaryMofGuid = BINARY_MOF_GUID;
|
|
|
|
WMIGUIDREGINFO FilterGuidList[] =
|
|
{
|
|
{
|
|
&FilterClass1Guid, // Guid
|
|
1, // # of instances in each device
|
|
WMIREG_FLAG_EXPENSIVE // Flag as expensive to collect
|
|
},
|
|
|
|
{
|
|
&FilterClass2Guid,
|
|
1,
|
|
0
|
|
},
|
|
|
|
{
|
|
&FilterClass3Guid,
|
|
1,
|
|
0
|
|
},
|
|
|
|
{
|
|
&FilterClass4Guid,
|
|
1,
|
|
0
|
|
},
|
|
|
|
{
|
|
&FilterClass5Guid,
|
|
1,
|
|
0
|
|
},
|
|
|
|
{
|
|
&FilterClass6Guid,
|
|
1,
|
|
0
|
|
},
|
|
|
|
{
|
|
&FilterClass7Guid,
|
|
1,
|
|
0
|
|
},
|
|
|
|
{
|
|
&FilterGetSetDataGuid,
|
|
1,
|
|
0
|
|
},
|
|
|
|
{
|
|
&FilterFireEventGuid,
|
|
1,
|
|
0
|
|
},
|
|
|
|
{
|
|
&FilterEventClass1Guid,
|
|
1,
|
|
WMIREG_FLAG_EVENT_ONLY_GUID // Flag as an event
|
|
},
|
|
|
|
{
|
|
&FilterEventClass2Guid,
|
|
1,
|
|
WMIREG_FLAG_EVENT_ONLY_GUID
|
|
},
|
|
|
|
{
|
|
&FilterEventClass3Guid,
|
|
1,
|
|
WMIREG_FLAG_EVENT_ONLY_GUID
|
|
},
|
|
|
|
{
|
|
&FilterEventClass4Guid,
|
|
1,
|
|
WMIREG_FLAG_EVENT_ONLY_GUID
|
|
},
|
|
|
|
{
|
|
&FilterEventClass5Guid,
|
|
1,
|
|
WMIREG_FLAG_EVENT_ONLY_GUID
|
|
},
|
|
|
|
{
|
|
&FilterEventClass6Guid,
|
|
1,
|
|
WMIREG_FLAG_EVENT_ONLY_GUID
|
|
},
|
|
|
|
{
|
|
&FilterEventClass7Guid,
|
|
1,
|
|
WMIREG_FLAG_EVENT_ONLY_GUID
|
|
},
|
|
|
|
{
|
|
&FilterEventReferenceClassGuid,
|
|
1,
|
|
WMIREG_FLAG_EVENT_ONLY_GUID
|
|
},
|
|
|
|
{
|
|
&FilterIrpCountGuid,
|
|
1,
|
|
0
|
|
},
|
|
|
|
{
|
|
&FilterBinaryMofGuid,
|
|
1,
|
|
#ifdef USE_BINARY_MOF_QUERY
|
|
0
|
|
#else
|
|
WMIREG_FLAG_REMOVE_GUID
|
|
#endif
|
|
}
|
|
|
|
};
|
|
|
|
#define FilterGuidCount (sizeof(FilterGuidList) / sizeof(WMIGUIDREGINFO))
|
|
|
|
//
|
|
// We need to hang onto the registry path passed to our driver entry so that
|
|
// we can return it in the QueryWmiRegInfo callback.
|
|
//
|
|
UNICODE_STRING FilterRegistryPath;
|
|
|
|
NTSTATUS VA_SystemControl(
|
|
struct DEVICE_EXTENSION *devExt,
|
|
PIRP irp,
|
|
PBOOLEAN passIrpDown
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispatch routine for System Control IRPs (MajorFunction == IRP_MJ_SYSTEM_CONTROL)
|
|
|
|
Arguments:
|
|
|
|
devExt - device extension for targetted device object
|
|
irp - Io Request Packet
|
|
*passIrpDown - returns with whether to pass irp down stack
|
|
|
|
Return Value:
|
|
|
|
NT status code
|
|
|
|
--*/
|
|
{
|
|
PWMILIB_CONTEXT wmilibContext;
|
|
NTSTATUS status;
|
|
SYSCTL_IRP_DISPOSITION disposition;
|
|
|
|
InterlockedIncrement(&devExt->WmiIrpCount);
|
|
|
|
wmilibContext = &devExt->WmiLib;
|
|
|
|
//
|
|
// Call Wmilib helper function to crack the irp. If this is a wmi irp
|
|
// that is targetted for this device then WmiSystemControl will callback
|
|
// at the appropriate callback routine.
|
|
//
|
|
status = WmiSystemControl(wmilibContext,
|
|
devExt->filterDevObj,
|
|
irp,
|
|
&disposition);
|
|
|
|
switch(disposition)
|
|
{
|
|
case IrpProcessed:
|
|
{
|
|
//
|
|
// This irp has been processed and may be completed or pending.
|
|
*passIrpDown = FALSE;
|
|
break;
|
|
}
|
|
|
|
case IrpNotCompleted:
|
|
{
|
|
//
|
|
// This irp has not been completed, but has been fully processed.
|
|
// we will complete it now.
|
|
*passIrpDown = FALSE;
|
|
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.
|
|
*passIrpDown = TRUE;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
//
|
|
// We really should never get here, but if we do just forward....
|
|
ASSERT(FALSE);
|
|
*passIrpDown = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
NTSTATUS
|
|
FilterInitializeWmiDataBlocks(
|
|
IN struct DEVICE_EXTENSION *devExt
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine is called to create a new instance of the device
|
|
|
|
Arguments:
|
|
devExt is device extension
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PWMILIB_CONTEXT wmilibInfo;
|
|
ULONG i;
|
|
PEC1 Ec1;
|
|
PEC2 Ec2;
|
|
UCHAR Ec[sizeof(EC2)];
|
|
|
|
//
|
|
// Fill in the WMILIB_CONTEXT structure with a pointer to the
|
|
// callback routines and a pointer to the list of guids
|
|
// supported by the driver
|
|
//
|
|
wmilibInfo = &devExt->WmiLib;
|
|
wmilibInfo->GuidCount = FilterGuidCount;
|
|
wmilibInfo->GuidList = FilterGuidList;
|
|
wmilibInfo->QueryWmiRegInfo = FilterQueryWmiRegInfo;
|
|
wmilibInfo->QueryWmiDataBlock = FilterQueryWmiDataBlock;
|
|
wmilibInfo->SetWmiDataBlock = FilterSetWmiDataBlock;
|
|
wmilibInfo->SetWmiDataItem = FilterSetWmiDataItem;
|
|
wmilibInfo->ExecuteWmiMethod = FilterExecuteWmiMethod;
|
|
wmilibInfo->WmiFunctionControl = FilterFunctionControl;
|
|
|
|
//
|
|
// Initialize the wmi data blocks with specific data
|
|
//
|
|
devExt->Ec1Count = 3;
|
|
devExt->Ec2Count = 3;
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
Ec1 = (PEC1)Ec;
|
|
memset(Ec1, i, sizeof(EC1));
|
|
memcpy(Ec1->Xdatetime, FilterDateTime, 25*sizeof(WCHAR));
|
|
|
|
ASSERT(devExt->Ec1[i] == NULL);
|
|
FilterSetEc1(devExt,
|
|
(PUCHAR)Ec1,
|
|
sizeof(EC1),
|
|
i);
|
|
|
|
|
|
Ec2 = (PEC2)Ec;
|
|
memset(Ec2, i, sizeof(EC2));
|
|
memcpy(Ec2->Xdatetime, FilterDateTime, 25*sizeof(WCHAR));
|
|
|
|
ASSERT(devExt->Ec2[i] == NULL);
|
|
FilterSetEc2(devExt,
|
|
(PUCHAR)Ec2,
|
|
sizeof(EC2),
|
|
i);
|
|
}
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
void FilterWmiCleanup(
|
|
struct DEVICE_EXTENSION *devExt
|
|
)
|
|
{
|
|
ULONG i;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
if (devExt->Ec1[i] != NULL)
|
|
{
|
|
ExFreePool(devExt->Ec1[i]);
|
|
devExt->Ec1[i] = NULL;
|
|
}
|
|
|
|
if (devExt->Ec2[i] != NULL)
|
|
{
|
|
ExFreePool(devExt->Ec2[i]);
|
|
devExt->Ec2[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
FilterQueryWmiRegInfo(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
OUT ULONG *RegFlags,
|
|
OUT PUNICODE_STRING InstanceName,
|
|
OUT PUNICODE_STRING *RegistryPath,
|
|
OUT PUNICODE_STRING MofResourceName,
|
|
OUT PDEVICE_OBJECT *Pdo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is a callback into the driver to retrieve the list of
|
|
guids or data blocks that the driver wants to register with WMI. This
|
|
routine may not pend or block. Driver should NOT call
|
|
WmiCompleteRequest.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject is the device whose registration info is being queried
|
|
|
|
*RegFlags returns with a set of flags that describe the guids being
|
|
registered for this device. If the device wants enable and disable
|
|
collection callbacks before receiving queries for the registered
|
|
guids then it should return the WMIREG_FLAG_EXPENSIVE flag. Also the
|
|
returned flags may specify WMIREG_FLAG_INSTANCE_PDO in which case
|
|
the instance name is determined from the PDO associated with the
|
|
device object. Note that the PDO must have an associated devnode. If
|
|
WMIREG_FLAG_INSTANCE_PDO is not set then Name must return a unique
|
|
name for the device.
|
|
|
|
InstanceName returns with the instance name for the guids if
|
|
WMIREG_FLAG_INSTANCE_PDO is not set in the returned *RegFlags. The
|
|
caller will call ExFreePool with the buffer returned.
|
|
|
|
*RegistryPath returns with the registry path of the driver. The caller
|
|
does NOT free this buffer.
|
|
|
|
*MofResourceName returns with the name of the MOF resource attached to
|
|
the binary file. If the driver does not have a mof resource attached
|
|
then this can be returned as NULL. The caller does NOT free this
|
|
buffer.
|
|
|
|
*Pdo returns with the device object for the PDO associated with this
|
|
device if the WMIREG_FLAG_INSTANCE_PDO flag is retured in
|
|
*RegFlags.
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
struct DEVICE_EXTENSION * devExt = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// Return the registry path for this driver. This is required so WMI
|
|
// can find your driver image and can attribute any eventlog messages to
|
|
// your driver.
|
|
*RegistryPath = &FilterRegistryPath;
|
|
|
|
#ifndef USE_BINARY_MOF_QUERY
|
|
//
|
|
// Return the name specified in the .rc file of the resource which
|
|
// contains the bianry mof data. By default WMI will look for this
|
|
// resource in the driver image (.sys) file, however if the value
|
|
// MofImagePath is specified in the driver's registry key
|
|
// then WMI will look for the resource in the file specified there.
|
|
RtlInitUnicodeString(MofResourceName, L"MofResourceName");
|
|
#endif
|
|
|
|
//
|
|
// Specify that the driver wants WMI to automatically generate instance
|
|
// names for all of the data blocks based upon the device stack's
|
|
// device instance id. Doing this is STRONGLY recommended since additional
|
|
// information about the device would then be available to callers.
|
|
*RegFlags = WMIREG_FLAG_INSTANCE_PDO;
|
|
*Pdo = devExt->physicalDevObj;
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
ULONG FilterGetEc1(
|
|
struct DEVICE_EXTENSION * devExt,
|
|
PUCHAR Buffer,
|
|
ULONG Index
|
|
)
|
|
{
|
|
RtlCopyMemory(Buffer,
|
|
devExt->Ec1[Index],
|
|
devExt->Ec1Length[Index]);
|
|
|
|
return(devExt->Ec1Length[Index]);
|
|
}
|
|
|
|
ULONG FilterGetActualEc1(
|
|
struct DEVICE_EXTENSION * devExt,
|
|
PUCHAR Buffer,
|
|
ULONG Index
|
|
)
|
|
{
|
|
RtlCopyMemory(Buffer,
|
|
devExt->Ec1[Index],
|
|
devExt->Ec1ActualLength[Index]);
|
|
|
|
return(devExt->Ec1ActualLength[Index]);
|
|
}
|
|
|
|
void FilterSetEc1(
|
|
struct DEVICE_EXTENSION * devExt,
|
|
PUCHAR Buffer,
|
|
ULONG Length,
|
|
ULONG Index
|
|
)
|
|
{
|
|
PEC1 New;
|
|
ULONG NewLength;
|
|
|
|
NewLength = (Length + 7) & ~7;
|
|
|
|
New = ExAllocatePoolWithTag(PagedPool, NewLength, FILTER_TAG);
|
|
if (New != NULL)
|
|
{
|
|
if (devExt->Ec1[Index] != NULL)
|
|
{
|
|
ExFreePool(devExt->Ec1[Index]);
|
|
}
|
|
devExt->Ec1[Index] = New;
|
|
devExt->Ec1Length[Index] = NewLength;
|
|
devExt->Ec1ActualLength[Index] = Length;
|
|
RtlCopyMemory(New,
|
|
Buffer,
|
|
Length);
|
|
}
|
|
}
|
|
|
|
|
|
ULONG FilterGetEc2(
|
|
struct DEVICE_EXTENSION * devExt,
|
|
PUCHAR Buffer,
|
|
ULONG Index
|
|
)
|
|
{
|
|
RtlCopyMemory(Buffer,
|
|
devExt->Ec2[Index],
|
|
devExt->Ec2Length[Index]);
|
|
|
|
return(devExt->Ec2Length[Index]);
|
|
}
|
|
|
|
ULONG FilterGetActualEc2(
|
|
struct DEVICE_EXTENSION * devExt,
|
|
PUCHAR Buffer,
|
|
ULONG Index
|
|
)
|
|
{
|
|
RtlCopyMemory(Buffer,
|
|
devExt->Ec2[Index],
|
|
devExt->Ec2ActualLength[Index]);
|
|
|
|
return(devExt->Ec2ActualLength[Index]);
|
|
}
|
|
|
|
void FilterSetEc2(
|
|
struct DEVICE_EXTENSION * devExt,
|
|
PUCHAR Buffer,
|
|
ULONG Length,
|
|
ULONG Index
|
|
)
|
|
{
|
|
PEC2 New;
|
|
ULONG NewLength;
|
|
|
|
NewLength = (Length + 7) & ~7;
|
|
|
|
New = ExAllocatePoolWithTag(PagedPool, NewLength, FILTER_TAG);
|
|
if (New != NULL)
|
|
{
|
|
if (devExt->Ec2[Index] != NULL)
|
|
{
|
|
ExFreePool(devExt->Ec2[Index]);
|
|
}
|
|
devExt->Ec2[Index] = New;
|
|
devExt->Ec2Length[Index] = NewLength;
|
|
devExt->Ec2ActualLength[Index] = Length;
|
|
RtlCopyMemory(New,
|
|
Buffer,
|
|
Length);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
FilterQueryWmiDataBlock(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG GuidIndex,
|
|
IN ULONG InstanceIndex,
|
|
IN ULONG InstanceCount,
|
|
IN OUT PULONG InstanceLengthArray,
|
|
IN ULONG BufferAvail,
|
|
OUT PUCHAR Buffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is a callback into the driver to query for the contents of
|
|
all instances of a data block. If the driver can satisfy the query within
|
|
the callback it should call WmiCompleteRequest to complete the irp before
|
|
returning to the caller. Or the driver can return STATUS_PENDING if the
|
|
irp cannot be completed immediately and must then call WmiCompleteRequest
|
|
once the query is satisfied.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject is the device whose data block is being queried
|
|
|
|
Irp is the Irp that makes this request
|
|
|
|
GuidIndex is the index into the list of guids provided when the
|
|
device registered
|
|
|
|
InstanceCount is the number of instnaces expected to be returned for
|
|
the data block.
|
|
|
|
InstanceLengthArray is a pointer to an array of ULONG that returns the
|
|
lengths of each instance of the data block. If this is NULL then
|
|
there was not enough space in the output buffer to fufill the request
|
|
so the irp should be completed with the buffer needed.
|
|
|
|
BufferAvail on entry has the maximum size available to write the data
|
|
blocks.
|
|
|
|
Buffer on return is filled with the returned data blocks. Note that each
|
|
instance of the data block must be aligned on a 8 byte boundry.
|
|
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
struct DEVICE_EXTENSION * devExt = DeviceObject->DeviceExtension;
|
|
ULONG sizeNeeded;
|
|
ULONG i;
|
|
ULONG LastInstanceIndex;
|
|
ULONG sizeUsed, vlSize;
|
|
|
|
switch(GuidIndex)
|
|
{
|
|
case FilterEventReferenceClass:
|
|
case FilterClass1:
|
|
case FilterClass2:
|
|
{
|
|
// plain EC1
|
|
sizeNeeded = devExt->Ec1Length[0];
|
|
if (BufferAvail < sizeNeeded)
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
*InstanceLengthArray = sizeNeeded;
|
|
FilterGetEc1(devExt, Buffer, 0);
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case FilterClass3:
|
|
{
|
|
// fixed array of EC1
|
|
sizeNeeded = 0;
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
//
|
|
// Each embedded class in an array of embedded classes
|
|
// must be naturally aligned, and any padding between
|
|
// the embedded classes must be included in the calculation
|
|
// of the size of buffer needed to fufill the request.
|
|
// Since the largest element in the embedded structure is
|
|
// 8 bytes we pad the structure size out to 8 bytes.
|
|
sizeNeeded += (devExt->Ec1Length[i] + 7) & ~7;
|
|
}
|
|
|
|
if (BufferAvail < sizeNeeded)
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
*InstanceLengthArray = sizeNeeded;
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
//
|
|
// Copy each embedded class from storage into the
|
|
// output buffer. Note that we make sure that each
|
|
// embedded class begins on a natural alignment, in
|
|
// this case an 8 byte boundry
|
|
sizeUsed = FilterGetEc1(devExt, Buffer, i);
|
|
Buffer += (sizeUsed+7) & ~7;
|
|
}
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case FilterClass4:
|
|
{
|
|
// variable array of EC1
|
|
|
|
//
|
|
// Account for the size of the ULONG plus padding so that the
|
|
// embedded classes start on an 8 byte boundry
|
|
sizeNeeded = (sizeof(ULONG) + 7) & ~7;
|
|
|
|
vlSize = devExt->Ec1Count;
|
|
|
|
for (i = 0; i < vlSize; i++)
|
|
{
|
|
sizeNeeded += (devExt->Ec1Length[i] + 7) &~7;
|
|
}
|
|
|
|
if (BufferAvail < sizeNeeded)
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
*InstanceLengthArray = sizeNeeded;
|
|
*((PULONG)Buffer) = vlSize;
|
|
Buffer += (sizeof(ULONG) + 7) & ~7;
|
|
for (i = 0; i < vlSize; i++)
|
|
{
|
|
sizeUsed = FilterGetEc1(devExt, Buffer, i);
|
|
Buffer += (sizeUsed+7) & ~7;
|
|
}
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case FilterClass5:
|
|
{
|
|
// plain EC2
|
|
sizeNeeded = devExt->Ec2Length[0];
|
|
if (BufferAvail < sizeNeeded)
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
*InstanceLengthArray = sizeNeeded;
|
|
FilterGetEc2(devExt, Buffer, 0);
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case FilterClass6:
|
|
{
|
|
// fixed array EC2
|
|
sizeNeeded = 0;
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
sizeNeeded += (devExt->Ec2Length[i] + 7) & ~7;
|
|
}
|
|
|
|
if (BufferAvail < sizeNeeded)
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
*InstanceLengthArray = sizeNeeded;
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
sizeUsed = FilterGetEc2(devExt, Buffer, i);
|
|
Buffer += (sizeUsed + 7) & ~7;
|
|
}
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case FilterClass7:
|
|
{
|
|
// VL array EC2
|
|
|
|
|
|
sizeNeeded = (sizeof(ULONG) + 7) & ~7;
|
|
|
|
vlSize = devExt->Ec2Count;
|
|
for (i = 0; i < vlSize; i++)
|
|
{
|
|
sizeNeeded += devExt->Ec2Length[i];
|
|
}
|
|
|
|
if (BufferAvail < sizeNeeded)
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
*InstanceLengthArray = sizeNeeded;
|
|
*((PULONG)Buffer) = vlSize;
|
|
Buffer += (sizeof(ULONG)+7) & ~7;
|
|
for (i = 0; i < vlSize; i++)
|
|
{
|
|
sizeUsed = FilterGetEc2(devExt, Buffer, i);
|
|
Buffer += (sizeUsed + 7) & ~7;
|
|
}
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case FilterIrpCount:
|
|
{
|
|
sizeNeeded = sizeof(Vendor_IrpCounter);
|
|
if (BufferAvail >= sizeNeeded)
|
|
{
|
|
PVendor_IrpCounter IrpCounter = (PVendor_IrpCounter)Buffer;
|
|
|
|
IrpCounter->TotalIrpCount = devExt->TotalIrpCount;
|
|
IrpCounter->TotalIrpRate = devExt->TotalIrpCount;
|
|
IrpCounter->WmiIrpCount = devExt->WmiIrpCount;
|
|
*InstanceLengthArray = sizeNeeded;
|
|
status = STATUS_SUCCESS;
|
|
} else {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case FilterFireEvent:
|
|
case FilterGetSetData:
|
|
{
|
|
//
|
|
// Method classes do not have any data within them, but must
|
|
// repond successfully to queries so that WMI method operation
|
|
// work successfully.
|
|
sizeNeeded = sizeof(USHORT);
|
|
if (BufferAvail < sizeNeeded)
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
*InstanceLengthArray = sizeNeeded;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
}
|
|
|
|
#ifdef USE_BINARY_MOF_QUERY
|
|
case BinaryMofGuid:
|
|
{
|
|
sizeNeeded = sizeof(FilterBinaryMofData);
|
|
|
|
if (BufferAvail < sizeNeeded)
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
RtlCopyMemory(Buffer, FilterBinaryMofData, sizeNeeded);
|
|
*InstanceLengthArray = sizeNeeded;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
default:
|
|
{
|
|
status = STATUS_WMI_GUID_NOT_FOUND;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Complete the irp. If there was not enough room in the output buffer
|
|
// then status is STATUS_BUFFER_TOO_SMALL and sizeNeeded has the size
|
|
// needed to return all of the data. If there was enough room then
|
|
// status is STATUS_SUCCESS and sizeNeeded is the actual number of bytes
|
|
// being returned.
|
|
status = WmiCompleteRequest(
|
|
DeviceObject,
|
|
Irp,
|
|
status,
|
|
sizeNeeded,
|
|
IO_NO_INCREMENT);
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
//
|
|
// Use this size when checking that the input data block is the correct
|
|
// size. The compiler will add padding to the end of the structure if
|
|
// you use sizeof(EC1), but WMI may pass a data block that is the exact
|
|
// size of the data without padding.
|
|
//
|
|
#define EC1Size (FIELD_OFFSET(EC1, Xdatetime) + 25*sizeof(WCHAR))
|
|
|
|
NTSTATUS FilterSetEc1Worker(
|
|
struct DEVICE_EXTENSION * devExt,
|
|
ULONG BufferSize,
|
|
ULONG Index,
|
|
PUCHAR Buffer,
|
|
PULONG BufferUsed
|
|
)
|
|
{
|
|
ULONG blockLen;
|
|
NTSTATUS status;
|
|
PEC1 Ec1;
|
|
|
|
Ec1 = (PEC1)Buffer;
|
|
if (BufferSize >= EC1Size)
|
|
{
|
|
blockLen = sizeof(EC1);
|
|
|
|
FilterSetEc1(devExt,
|
|
Buffer,
|
|
blockLen,
|
|
Index);
|
|
*BufferUsed = (blockLen+7) & ~7;
|
|
status = STATUS_SUCCESS;
|
|
} else {
|
|
status = STATUS_INVALID_PARAMETER_MIX;
|
|
}
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// Use this size when checking that the input data block is the correct
|
|
// size. The compiler will add padding to the end of the structure if
|
|
// you use sizeof(EC2), but WMI may pass a data block that is the exact
|
|
// size of the data without padding.
|
|
//
|
|
#define EC2Size (FIELD_OFFSET(EC2, Xdatetime) + 25*sizeof(WCHAR))
|
|
|
|
NTSTATUS FilterSetEc2Worker(
|
|
struct DEVICE_EXTENSION * devExt,
|
|
ULONG BufferSize,
|
|
ULONG Index,
|
|
PUCHAR Buffer,
|
|
PULONG BufferUsed
|
|
)
|
|
{
|
|
ULONG blockLen;
|
|
NTSTATUS status;
|
|
PUSHORT wPtr;
|
|
PEC2 Ec2;
|
|
|
|
Ec2 = (PEC2)Buffer;
|
|
if (BufferSize >= EC2Size)
|
|
{
|
|
blockLen = sizeof(EC2);
|
|
|
|
FilterSetEc2(devExt,
|
|
Buffer,
|
|
blockLen,
|
|
Index);
|
|
*BufferUsed = (blockLen+7) & ~7;
|
|
status = STATUS_SUCCESS;
|
|
} else {
|
|
status = STATUS_INVALID_PARAMETER_MIX;
|
|
}
|
|
return(status);
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
FilterSetWmiDataBlock(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG GuidIndex,
|
|
IN ULONG InstanceIndex,
|
|
IN ULONG BufferSize,
|
|
IN PUCHAR Buffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is a callback into the driver to change the contents of
|
|
a data block. If the driver can change the data block within
|
|
the callback it should call WmiCompleteRequest to complete the irp before
|
|
returning to the caller. Or the driver can return STATUS_PENDING if the
|
|
irp cannot be completed immediately and must then call WmiCompleteRequest
|
|
once the data is changed.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject is the device whose data block is being queried
|
|
|
|
Irp is the Irp that makes this request
|
|
|
|
GuidIndex is the index into the list of guids provided when the
|
|
device registered
|
|
|
|
BufferSize has the size of the data block passed
|
|
|
|
Buffer has the new values for the data block
|
|
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
ULONG bufferUsed;
|
|
struct DEVICE_EXTENSION * devExt = DeviceObject->DeviceExtension;
|
|
ULONG i;
|
|
ULONG vlSize;
|
|
|
|
|
|
switch(GuidIndex)
|
|
{
|
|
case FilterClass1:
|
|
case FilterClass2:
|
|
{
|
|
// plain EC1
|
|
status = FilterSetEc1Worker(devExt,
|
|
BufferSize,
|
|
0,
|
|
Buffer,
|
|
&bufferUsed);
|
|
break;
|
|
}
|
|
|
|
case FilterClass3:
|
|
{
|
|
// fixed array of EC1
|
|
|
|
for (i = 0, status = STATUS_SUCCESS;
|
|
(i < 4) && NT_SUCCESS(status); i++)
|
|
{
|
|
status = FilterSetEc1Worker(devExt,
|
|
BufferSize,
|
|
i,
|
|
Buffer,
|
|
&bufferUsed);
|
|
Buffer += bufferUsed;
|
|
BufferSize -= bufferUsed;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case FilterClass4:
|
|
{
|
|
// variable array of EC1
|
|
|
|
if (BufferSize >= ((sizeof(ULONG) +7) & ~7))
|
|
{
|
|
vlSize = *((PULONG)Buffer);
|
|
Buffer += ((sizeof(ULONG) +7) & ~7);
|
|
|
|
if ((vlSize >= 1) && (vlSize <= 4))
|
|
{
|
|
for (i = 0, status = STATUS_SUCCESS;
|
|
(i < vlSize) && NT_SUCCESS(status); i++)
|
|
{
|
|
status = FilterSetEc1Worker(devExt,
|
|
BufferSize,
|
|
i,
|
|
Buffer,
|
|
&bufferUsed);
|
|
Buffer += bufferUsed;
|
|
BufferSize -= bufferUsed;
|
|
}
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
devExt->Ec1Count = vlSize;
|
|
}
|
|
} else {
|
|
KdPrint(("SetEc1 only up to [4] allowed, not %d\n",
|
|
vlSize));
|
|
status = STATUS_INVALID_PARAMETER_MIX;
|
|
}
|
|
} else {
|
|
KdPrint(("SetEc1 size too small\n"));
|
|
status = STATUS_INVALID_PARAMETER_MIX;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case FilterClass5:
|
|
{
|
|
// plain EC2
|
|
status = FilterSetEc2Worker(devExt,
|
|
BufferSize,
|
|
0,
|
|
Buffer,
|
|
&bufferUsed);
|
|
break;
|
|
}
|
|
|
|
case FilterClass6:
|
|
{
|
|
// fixed array EC2
|
|
for (i = 0, status = STATUS_SUCCESS;
|
|
(i < 4) && NT_SUCCESS(status); i++)
|
|
{
|
|
status = FilterSetEc2Worker(devExt,
|
|
BufferSize,
|
|
i,
|
|
Buffer,
|
|
&bufferUsed);
|
|
Buffer += bufferUsed;
|
|
BufferSize -= bufferUsed;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case FilterClass7:
|
|
{
|
|
// VL array EC2
|
|
if (BufferSize >= sizeof(ULONG))
|
|
{
|
|
vlSize = *((PULONG)Buffer);
|
|
Buffer += (sizeof(ULONG) +7) & ~7;
|
|
if ((vlSize >= 1) && (vlSize <= 4))
|
|
{
|
|
for (i = 0, status = STATUS_SUCCESS;
|
|
(i < vlSize) && NT_SUCCESS(status); i++)
|
|
{
|
|
status = FilterSetEc2Worker(devExt,
|
|
BufferSize,
|
|
i,
|
|
Buffer,
|
|
&bufferUsed);
|
|
Buffer += bufferUsed;
|
|
BufferSize -= bufferUsed;
|
|
}
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
devExt->Ec1Count = vlSize;
|
|
}
|
|
} else {
|
|
KdPrint(("SetEc2 only up to [4] allowed, not %d\n",
|
|
vlSize));
|
|
status = STATUS_INVALID_PARAMETER_MIX;
|
|
}
|
|
} else {
|
|
KdPrint(("SetEc2 size too small\n"));
|
|
status = STATUS_INVALID_PARAMETER_MIX;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
status = STATUS_WMI_GUID_NOT_FOUND;
|
|
break;
|
|
}
|
|
}
|
|
|
|
status = WmiCompleteRequest(
|
|
DeviceObject,
|
|
Irp,
|
|
status,
|
|
0,
|
|
IO_NO_INCREMENT);
|
|
|
|
return(status);
|
|
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
FilterSetWmiDataItem(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG GuidIndex,
|
|
IN ULONG InstanceIndex,
|
|
IN ULONG DataItemId,
|
|
IN ULONG BufferSize,
|
|
IN PUCHAR Buffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is a callback into the driver to change the contents of
|
|
a data block. If the driver can change the data block within
|
|
the callback it should call WmiCompleteRequest to complete the irp before
|
|
returning to the caller. Or the driver can return STATUS_PENDING if the
|
|
irp cannot be completed immediately and must then call WmiCompleteRequest
|
|
once the data is changed.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject is the device whose data block is being changed
|
|
|
|
Irp is the Irp that makes this request
|
|
|
|
GuidIndex is the index into the list of guids provided when the
|
|
device registered
|
|
|
|
DataItemId has the id of the data item being set
|
|
|
|
BufferSize has the size of the data item passed
|
|
|
|
Buffer has the new values for the data item
|
|
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
struct DEVICE_EXTENSION * devExt = DeviceObject->DeviceExtension;
|
|
ULONG sizeNeeded = 0;
|
|
|
|
switch(GuidIndex)
|
|
{
|
|
case FilterClass1:
|
|
case FilterClass2:
|
|
case FilterClass3:
|
|
case FilterClass4:
|
|
case FilterClass5:
|
|
case FilterClass6:
|
|
case FilterClass7:
|
|
{
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
|
|
case FilterFireEvent:
|
|
case FilterGetSetData:
|
|
{
|
|
status = STATUS_WMI_READ_ONLY;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
status = STATUS_WMI_GUID_NOT_FOUND;
|
|
break;
|
|
}
|
|
}
|
|
|
|
status = WmiCompleteRequest(
|
|
DeviceObject,
|
|
Irp,
|
|
status,
|
|
sizeNeeded,
|
|
IO_NO_INCREMENT);
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
NTSTATUS FilterDoFireEvent(
|
|
struct DEVICE_EXTENSION * devExt,
|
|
ULONG WnodeType, // 0 - AllData, 1 - Single Instance
|
|
ULONG DataType, // 1,2,5,8 is guid id
|
|
ULONG BlockIndex // 0 - 3 is block index containing data
|
|
)
|
|
{
|
|
PWNODE_HEADER Wnode;
|
|
PWNODE_ALL_DATA WnodeAD;
|
|
PWNODE_SINGLE_INSTANCE WnodeSI;
|
|
PUCHAR dataPtr;
|
|
PUCHAR WnodeDataPtr;
|
|
ULONG dataSize;
|
|
LPGUID Guid;
|
|
NTSTATUS status;
|
|
ULONG sizeNeeded;
|
|
BOOLEAN isEventRef = FALSE;
|
|
|
|
if (BlockIndex > 3)
|
|
{
|
|
return(STATUS_INVALID_PARAMETER_MIX);
|
|
}
|
|
|
|
switch(DataType)
|
|
{
|
|
case 1:
|
|
{
|
|
// EC1
|
|
dataSize = devExt->Ec1Length[BlockIndex];
|
|
dataPtr = (PUCHAR)devExt->Ec1[BlockIndex];
|
|
Guid = &FilterEventClass1Guid;
|
|
break;
|
|
}
|
|
|
|
case 2:
|
|
{
|
|
// EC1 (embedded)
|
|
dataSize = devExt->Ec1Length[BlockIndex];
|
|
dataPtr = (PUCHAR)devExt->Ec1[BlockIndex];
|
|
Guid = &FilterEventClass2Guid;
|
|
break;
|
|
}
|
|
|
|
case 5:
|
|
{
|
|
// EC2 (embedded)
|
|
dataSize = devExt->Ec2Length[BlockIndex];
|
|
dataPtr = (PUCHAR)devExt->Ec2[BlockIndex];
|
|
Guid = &FilterEventClass5Guid;
|
|
break;
|
|
}
|
|
|
|
case 8:
|
|
{
|
|
isEventRef = TRUE;
|
|
Guid = &FilterEventReferenceClassGuid;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
return(STATUS_INVALID_PARAMETER_MIX);
|
|
}
|
|
}
|
|
|
|
if (isEventRef) {
|
|
Wnode = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(WNODE_EVENT_REFERENCE),
|
|
FILTER_TAG);
|
|
if (Wnode != NULL)
|
|
{
|
|
PWNODE_EVENT_REFERENCE WnodeER;
|
|
|
|
sizeNeeded = sizeof(WNODE_EVENT_REFERENCE);
|
|
|
|
|
|
//
|
|
// Create a WNODE_EVENT_REFERENCE. First set the flags in the
|
|
// header to specify an event reference with static instance
|
|
// names.
|
|
//
|
|
Wnode->Flags = WNODE_FLAG_EVENT_REFERENCE |
|
|
WNODE_FLAG_STATIC_INSTANCE_NAMES;
|
|
|
|
WnodeER = (PWNODE_EVENT_REFERENCE)Wnode;
|
|
|
|
//
|
|
// The target guid is almose always the same guid as the event
|
|
// guid. To be most efficient we can set the size of the target
|
|
// data block, but in any case if it is too small then WMI will
|
|
// retry with a larger buffer.
|
|
//
|
|
WnodeER->TargetGuid = *Guid;
|
|
WnodeER->TargetDataBlockSize = 0;
|
|
|
|
//
|
|
// Since the target guid is a static instance name guid we fill
|
|
// in the instance index. If the target guid was dynamic instance
|
|
// names we would have to setup the instance name as text.
|
|
WnodeER->TargetInstanceIndex = 0;
|
|
dataPtr = NULL;
|
|
} else {
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
} else if (WnodeType == 0)
|
|
{
|
|
sizeNeeded = FIELD_OFFSET(WNODE_ALL_DATA,
|
|
OffsetInstanceDataAndLength) + dataSize;
|
|
Wnode = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeNeeded,
|
|
FILTER_TAG);
|
|
|
|
if (Wnode == NULL)
|
|
{
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
Wnode->Flags = WNODE_FLAG_ALL_DATA |
|
|
WNODE_FLAG_FIXED_INSTANCE_SIZE |
|
|
WNODE_FLAG_EVENT_ITEM |
|
|
WNODE_FLAG_STATIC_INSTANCE_NAMES;
|
|
WnodeAD = (PWNODE_ALL_DATA)Wnode;
|
|
WnodeAD->DataBlockOffset = FIELD_OFFSET(WNODE_ALL_DATA,
|
|
OffsetInstanceDataAndLength);
|
|
WnodeAD->InstanceCount = 1;
|
|
WnodeAD->FixedInstanceSize = dataSize;
|
|
WnodeDataPtr = (PUCHAR)Wnode + WnodeAD->DataBlockOffset;
|
|
|
|
} else if (WnodeType == 1) {
|
|
sizeNeeded = FIELD_OFFSET(WNODE_SINGLE_INSTANCE,
|
|
VariableData) + dataSize;
|
|
Wnode = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeNeeded,
|
|
FILTER_TAG);
|
|
|
|
if (Wnode == NULL)
|
|
{
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
Wnode->Flags = WNODE_FLAG_SINGLE_INSTANCE |
|
|
WNODE_FLAG_EVENT_ITEM |
|
|
WNODE_FLAG_STATIC_INSTANCE_NAMES;
|
|
WnodeSI = (PWNODE_SINGLE_INSTANCE)Wnode;
|
|
WnodeSI->DataBlockOffset = FIELD_OFFSET(WNODE_SINGLE_INSTANCE,
|
|
VariableData);
|
|
WnodeSI->InstanceIndex = 0;
|
|
WnodeSI->SizeDataBlock = dataSize;
|
|
WnodeDataPtr = (PUCHAR)Wnode + WnodeSI->DataBlockOffset;
|
|
} else {
|
|
return(STATUS_INVALID_PARAMETER_MIX);
|
|
}
|
|
|
|
|
|
Wnode->Guid = *Guid;
|
|
Wnode->ProviderId = IoWMIDeviceObjectToProviderId(devExt->filterDevObj);
|
|
Wnode->BufferSize = sizeNeeded;
|
|
KeQuerySystemTime(&Wnode->TimeStamp);
|
|
|
|
if (dataPtr != NULL)
|
|
{
|
|
RtlCopyMemory(WnodeDataPtr, dataPtr, dataSize);
|
|
}
|
|
|
|
status = IoWMIWriteEvent(Wnode);
|
|
|
|
//
|
|
// If IoWMIWriteEvent succeeds then WMI will free the event buffer. If
|
|
// it fails then the caller frees the event buffer.
|
|
//
|
|
if (! NT_SUCCESS(status))
|
|
{
|
|
ExFreePool(Wnode);
|
|
}
|
|
return(status);
|
|
}
|
|
|
|
NTSTATUS
|
|
FilterExecuteWmiMethod(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG GuidIndex,
|
|
IN ULONG InstanceIndex,
|
|
IN ULONG MethodId,
|
|
IN ULONG InBufferSize,
|
|
IN ULONG OutBufferSize,
|
|
IN PUCHAR Buffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is a callback into the driver to execute a method. If
|
|
the driver can complete the method within the callback it should
|
|
call WmiCompleteRequest to complete the irp before returning to the
|
|
caller. Or the driver can return STATUS_PENDING if the irp cannot be
|
|
completed immediately and must then call WmiCompleteRequest once the
|
|
data is changed.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject is the device whose method is being executed
|
|
|
|
Irp is the Irp that makes this request
|
|
|
|
GuidIndex is the index into the list of guids provided when the
|
|
device registered
|
|
|
|
MethodId has the id of the method being called
|
|
|
|
InBufferSize has the size of the data block passed in as the input to
|
|
the method.
|
|
|
|
OutBufferSize on entry has the maximum size available to write the
|
|
returned data block.
|
|
|
|
Buffer is filled with the input buffer on entry and returns with
|
|
the output data block
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
ULONG sizeNeeded = 0;
|
|
NTSTATUS status;
|
|
struct DEVICE_EXTENSION * devExt = DeviceObject->DeviceExtension;
|
|
ULONG bufferUsed;
|
|
ULONG blockIndex;
|
|
ULONG UlongPadSize = (sizeof(ULONG) + 7) & ~7;
|
|
|
|
|
|
if (GuidIndex == FilterGetSetData)
|
|
{
|
|
switch(MethodId)
|
|
{
|
|
case SetEC1:
|
|
case SetEC1AsEc:
|
|
{
|
|
// SetEc1
|
|
|
|
if (InBufferSize < UlongPadSize)
|
|
{
|
|
status = STATUS_INVALID_PARAMETER_MIX;
|
|
sizeNeeded = 0;
|
|
break;
|
|
} else {
|
|
blockIndex = *((PULONG)Buffer);
|
|
if (blockIndex > 3)
|
|
{
|
|
status = STATUS_INVALID_PARAMETER_MIX;
|
|
break;
|
|
}
|
|
Buffer += UlongPadSize;
|
|
InBufferSize -= UlongPadSize;
|
|
}
|
|
|
|
status = FilterSetEc1Worker(devExt,
|
|
InBufferSize,
|
|
blockIndex,
|
|
Buffer,
|
|
&bufferUsed);
|
|
sizeNeeded = 0;
|
|
break;
|
|
}
|
|
|
|
case SetEC2:
|
|
case SetEC2AsEc:
|
|
{
|
|
// SetEc2
|
|
|
|
if (InBufferSize < UlongPadSize)
|
|
{
|
|
status = STATUS_INVALID_PARAMETER_MIX;
|
|
sizeNeeded = 0;
|
|
break;
|
|
} else {
|
|
blockIndex = *((PULONG)Buffer);
|
|
if (blockIndex > 3)
|
|
{
|
|
status = STATUS_INVALID_PARAMETER_MIX;
|
|
break;
|
|
}
|
|
Buffer += UlongPadSize;
|
|
InBufferSize -= UlongPadSize;
|
|
}
|
|
|
|
status = FilterSetEc2Worker(devExt,
|
|
InBufferSize,
|
|
blockIndex,
|
|
Buffer,
|
|
&bufferUsed);
|
|
sizeNeeded = 0;
|
|
break;
|
|
}
|
|
|
|
case GetEC1:
|
|
case GetEC1AsEc:
|
|
{
|
|
// GetEc1
|
|
|
|
if (InBufferSize != sizeof(ULONG))
|
|
{
|
|
status = STATUS_INVALID_PARAMETER_MIX;
|
|
sizeNeeded = 0;
|
|
break;
|
|
} else {
|
|
blockIndex = *((PULONG)Buffer);
|
|
if (blockIndex > 3)
|
|
{
|
|
status = STATUS_INVALID_PARAMETER_MIX;
|
|
break;
|
|
}
|
|
}
|
|
|
|
sizeNeeded = devExt->Ec1ActualLength[blockIndex];
|
|
if (OutBufferSize < sizeNeeded)
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
FilterGetActualEc1(devExt, Buffer, blockIndex);
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case GetEC2:
|
|
case GetEC2AsEc:
|
|
{
|
|
// GetEc2
|
|
if (InBufferSize != sizeof(ULONG))
|
|
{
|
|
status = STATUS_INVALID_PARAMETER_MIX;
|
|
sizeNeeded = 0;
|
|
break;
|
|
} else {
|
|
blockIndex = *((PULONG)Buffer);
|
|
if (blockIndex > 3)
|
|
{
|
|
status = STATUS_INVALID_PARAMETER_MIX;
|
|
break;
|
|
}
|
|
}
|
|
|
|
sizeNeeded = devExt->Ec2ActualLength[blockIndex];
|
|
if (OutBufferSize < sizeNeeded)
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
FilterGetActualEc2(devExt, Buffer, blockIndex);
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case DisableSampleClass7:
|
|
{
|
|
//
|
|
// Mark the guid for FilterClass7 as not available and then
|
|
// call WMI to inform it that the guid list has changed. WMI
|
|
// will send a new IRP_MN_REGINFO which would cause the
|
|
// QueryWmiRegInfo callback to be called and the new
|
|
// guid list would be returned and the registration updated.
|
|
FilterGuidList[FilterClass7].Flags |= WMIREG_FLAG_REMOVE_GUID;
|
|
status = IoWMIRegistrationControl(devExt->filterDevObj,
|
|
WMIREG_ACTION_UPDATE_GUIDS);
|
|
sizeNeeded = 0;
|
|
break;
|
|
}
|
|
|
|
case UnregisterFromWmi:
|
|
{
|
|
//
|
|
// We must wait until AFTER the irp is completed before
|
|
// calling IoWMIRegistrationControl to unregister. Since
|
|
// that api will block until all WMI irps sent to the
|
|
// device are completed we would become deadlocked.
|
|
|
|
IoWMIRegistrationControl(devExt->filterDevObj,
|
|
WMIREG_ACTION_BLOCK_IRPS);
|
|
|
|
status = WmiCompleteRequest(
|
|
DeviceObject,
|
|
Irp,
|
|
STATUS_SUCCESS,
|
|
0,
|
|
IO_NO_INCREMENT);
|
|
|
|
IoWMIRegistrationControl(devExt->filterDevObj,
|
|
WMIREG_ACTION_DEREGISTER);
|
|
return(status);
|
|
}
|
|
|
|
case EnableSampleClass7:
|
|
{
|
|
//
|
|
// Mark the guid for FilterClass7 as available and then
|
|
// call WMI to inform it that the guid list has changed. WMI
|
|
// will send a new IRP_MN_REGINFO which would cause the
|
|
// QueryWmiRegInfo callback to be called and the new
|
|
// guid list would be returned and the registration updated.
|
|
FilterGuidList[FilterClass7].Flags &= ~WMIREG_FLAG_REMOVE_GUID;
|
|
status = IoWMIRegistrationControl(devExt->filterDevObj,
|
|
WMIREG_ACTION_UPDATE_GUIDS);
|
|
sizeNeeded = 0;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
status = STATUS_WMI_ITEMID_NOT_FOUND;
|
|
}
|
|
}
|
|
} else if (GuidIndex == FilterFireEvent) {
|
|
if (MethodId == FireEvent)
|
|
{
|
|
if (InBufferSize == 3*sizeof(ULONG))
|
|
{
|
|
ULONG wnodeType;
|
|
ULONG dataType;
|
|
ULONG blockIndexA;
|
|
|
|
wnodeType = *(PULONG)Buffer;
|
|
Buffer += sizeof(ULONG);
|
|
|
|
dataType = *(PULONG)Buffer;
|
|
Buffer += sizeof(ULONG);
|
|
|
|
blockIndexA = *(PULONG)Buffer;
|
|
Buffer += sizeof(ULONG);
|
|
|
|
status = FilterDoFireEvent(devExt,
|
|
wnodeType,
|
|
dataType,
|
|
blockIndexA);
|
|
|
|
sizeNeeded = 0;
|
|
|
|
} else {
|
|
status = STATUS_INVALID_PARAMETER_MIX;
|
|
}
|
|
} else {
|
|
status = STATUS_WMI_ITEMID_NOT_FOUND;
|
|
}
|
|
} else {
|
|
status = STATUS_WMI_GUID_NOT_FOUND;
|
|
}
|
|
|
|
status = WmiCompleteRequest(
|
|
DeviceObject,
|
|
Irp,
|
|
status,
|
|
sizeNeeded,
|
|
IO_NO_INCREMENT);
|
|
|
|
return(status);
|
|
}
|
|
|
|
NTSTATUS
|
|
FilterFunctionControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG GuidIndex,
|
|
IN WMIENABLEDISABLECONTROL Function,
|
|
IN BOOLEAN Enable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is a callback into the driver to enabled or disable event
|
|
generation or data block collection. A device should only expect a
|
|
single enable when the first event or data consumer enables events or
|
|
data collection and a single disable when the last event or data
|
|
consumer disables events or data collection. Data blocks will only
|
|
receive collection enable/disable if they were registered as requiring
|
|
it. If the driver can complete enabling/disabling within the callback it
|
|
should call WmiCompleteRequest to complete the irp before returning to
|
|
the caller. Or the driver can return STATUS_PENDING if the irp cannot be
|
|
completed immediately and must then call WmiCompleteRequest once the
|
|
data is changed.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject is the device object
|
|
|
|
GuidIndex is the index into the list of guids provided when the
|
|
device registered
|
|
|
|
Function specifies which functionality is being enabled or disabled
|
|
|
|
Enable is TRUE then the function is being enabled else disabled
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
status = WmiCompleteRequest(
|
|
DeviceObject,
|
|
Irp,
|
|
STATUS_SUCCESS,
|
|
0,
|
|
IO_NO_INCREMENT);
|
|
return(status);
|
|
}
|
|
|
|
|