' *********************************************************************************************
' What Is WMI?
' ============
' "Originally released in 1998 as an add-on component with Windows NT 4.0 Service Pack 4, WMI is
' the core management-enabling technology built into Windows 2000, Windows XP, and the Windows
' Server 2003 family of operating systems. Based on industry standards overseen by the Distributed
' Management Task Force (DMTF), WMI is the instrumentation and plumbing through which all--well,
' almost all--Windows resources can be accessed, configured, managed, and monitored."
' For further reading:
' http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnclinic/ html/scripting06112002.asp
' http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnclinic/ html/scripting08132002.asp
' http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnclinic/ html/scripting01142003.asp
' For the SDK documentation:
' http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/wmi_ start_page.asp
'
' I have begin to explore the Windows Management Instrumentation (WMI) component and have made an
' example to help you to get started. The two main problems when reading the documentation is that
' most of the examples are for VB or VBScript and they use some instructions not available in PB,
' mainly GetObject and For Each.
'
' An instruction like
'
' Set objWMIService = GetObject("winmgmts:" _
' & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
'
' translates to PB as
'
' DIM oLocator AS DISPATCH
' SET oLocator = NEW DISPATCH IN "WbemScripting.SWbemLocator"
' DIM vServer AS VARIANT, vNameSpace AS VARIANT, vImpersonation AS VARIANT
' vComputer = "." : vNameSpace = "root\cimv2"
' OBJECT CALL oLocator.ConnectServer(vComputer, vNameSpace) TO vRes
' SET objWMIService = vRes
' vImpersonation = 3 AS LONG ' Impersonate
' OBJECT LET objWMIService.Security_.ImpersonationLevel = vImpersonation
'
' Regarding For Each, used to enumerate collections, I have made some functions to access the
' IEnumVARIANT interface, that provides enumeration functionality.
'
' There are a lot of classes, and the easiest way is to use ExecQuery, that uses an WQL string
' (a subset of the SQL query language) as the parameter. You can control the number of properties
' returned, e.g.
'
' vWQL = "SELECT * FROM Win32_Process"
' OBJECT CALL oServices.ExecQuery(vWQL) TO vRes
'
' returns all the properties of the "Win32_Process" class, while
'
' vWQL = "SELECT ProcessID FROM Win32_Process"
'
' only returns the ProcessID property, and
'
' vWQL = "SELECT * FROM Win32_Process WHERE name='AcroRd32.exe'"
'
' returns all the properties of Acrobat Reader (if it is running).
'
' Knowing the ProcessID of a program, you can terminate it calling the method Terminate.
'
' The properties of the "Win32_Process" class are:
'
' class Win32_Process : CIM_Process
' {
' string Caption;
' string CommandLine;
' string CreationClassName;
' datetime CreationDate;
' string CSCreationClassName;
' string CSName;
' string Description;
' string ExecutablePath;
' uint16 ExecutionState;
' string Handle;
' uint32 HandleCount;
' datetime InstallDate;
' uint64 KernelModeTime;
' uint32 MaximumWorkingSetSize;
' uint32 MinimumWorkingSetSize;
' string Name;
' string OSCreationClassName;
' string OSName;
' uint64 OtherOperationCount;
' uint64 OtherTransferCount;
' uint32 PageFaults;
' uint32 PageFileUsage;
' uint32 ParentProcessId;
' uint32 PeakPageFileUsage;
' uint64 PeakVirtualSize;
' uint32 PeakWorkingSetSize;
' uint32 Priority;
' uint64 PrivatePageCount;
' uint32 ProcessId;
' uint32 QuotaNonPagedPoolUsage;
' uint32 QuotaPagedPoolUsage;
' uint32 QuotaPeakNonPagedPoolUsage;
' uint32 QuotaPeakPagedPoolUsage;
' uint64 ReadOperationCount;
' uint64 ReadTransferCount;
' uint32 SessionId;
' string Status;
' datetime TerminationDate;
' uint32 ThreadCount;
' uint64 UserModeTime;
' uint64 VirtualSize;
' string WindowsVersion;
' uint64 WorkingSetSize;
' uint64 WriteOperationCount;
' uint64 WriteTransferCount;
' };
'
' and the methods:
'
' AttachDebugger : Launches the currently registered debugger for a process.
' Create : Creates a new process.
' GetOwner : Retrieves the user name and domain name under which the process is running.
' GetOwnerSid : Retrieves the security identifier (SID) for the owner of a process.
' SetPriority : Changes the execution priority of a process.
' Terminate : Terminates a process and all of its threads.
' ********************************************************************************************* #COMPILE EXE
#DIM ALL
#DEBUG ERROR ON
#INCLUDE "win32api.inc"
' *********************************************************************************************
' Determines whether the object supports a particular COM interface. If it does, the system
' increases the object's reference count, and the application can use that interface
' Parameters:
' pThis [in] : Pointer to the interface to be queried.
' riid [in] : A Guid, passed by reference, that is the interface identifier (IID) of the
' requested interface.
' ppvObj [out] : Address of pointer variable that receives the interface pointer requested in
' riid. Upon successful return, *ppvObject contains the requested interface pointer to
' the object. If the object does not support the interface specified in iid, *ppvObject
' is set to NULL.
' Return Value:
' %S_OK if the interface is supported, %E_NOINTERFACE if not.
' *********************************************************************************************
DECLARE FUNCTION Template_IEnumVARIANT_QueryInterface (BYVAL pThis AS DWORD, BYREF riid AS GUID, BYVAL ppvObj AS DWORD) AS DWORD
' *********************************************************************************************
FUNCTION IEnumVARIANT_QueryInterface (BYVAL pThis AS DWORD, BYREF riid AS GUID, BYVAL ppvObj AS DWORD) AS DWORD
LOCAL HRESULT AS DWORD
LOCAL ppthis AS DWORD PTR
LOCAL pvtbl AS DWORD PTR
LOCAL ppmethod AS DWORD PTR
LOCAL pmethod AS DWORD
ppthis = pThis
pvtbl = @ppthis
ppmethod = pvtbl
pmethod = @ppmethod
CALL DWORD pmethod USING Template_IEnumVARIANT_QueryInterface(pThis, riid, ppvObj) TO HRESULT
FUNCTION = HRESULT
END FUNCTION
' *********************************************************************************************
' *********************************************************************************************
' Decrements the reference count on the specified interface. If the reference count on the
' object falls to 0, the object is freed from memory.
' Returns the resulting value of the reference count, which is used for diagnostic/testing
' purposes only.
' *********************************************************************************************
DECLARE FUNCTION Template_IEnumVARIANT_Release (BYVAL pThis AS DWORD) AS DWORD
' *********************************************************************************************
FUNCTION IEnumVARIANT_Release (BYVAL pThis AS DWORD) AS DWORD
LOCAL HRESULT AS DWORD
LOCAL ppthis AS DWORD PTR
LOCAL pvtbl AS DWORD PTR
LOCAL ppmethod AS DWORD PTR
LOCAL pmethod AS DWORD
ppthis = pThis
pvtbl = @ppthis
ppmethod = pvtbl + 8
pmethod = @ppmethod
CALL DWORD pmethod USING Template_IEnumVARIANT_Release(pThis) TO HRESULT
FUNCTION = HRESULT
END FUNCTION
' *********************************************************************************************
' *********************************************************************************************
' The Next method enumerates the next celt elements in the enumerator's list, returning them in
' rgelt along with the actual number of enumerated elements in pceltFetched.
' Parameters:
' celt : [in] Number of items in the array.
' rgelt : [out] Address of array containing items.
' pceltFetched: [out] Address of variable containing actual number of items.
' Return Value:
' Returns %S_OK if the method succeeds.
' *********************************************************************************************
DECLARE FUNCTION Template_IEnumVARIANT_Next (BYVAL pThis AS DWORD, BYVAL celt AS DWORD, BYREF rgelt AS VARIANT, BYREF pceltFetched AS DWORD) AS DWORD
' *********************************************************************************************
FUNCTION IEnumVARIANT_Next (BYVAL pThis AS DWORD, BYVAL celt AS DWORD, BYREF rgelt AS VARIANT, BYREF pceltFetched AS DWORD) AS DWORD
LOCAL HRESULT AS DWORD
LOCAL ppthis AS DWORD PTR
LOCAL pvtbl AS DWORD PTR
LOCAL ppmethod AS DWORD PTR
LOCAL pmethod AS DWORD
ppthis = pThis
pvtbl = @ppthis
ppmethod = pvtbl + 12
pmethod = @ppmethod
CALL DWORD pmethod USING Template_IEnumVARIANT_Next(pThis,celt,rgelt,pceltFetched) TO HRESULT
FUNCTION = HRESULT
END FUNCTION
' *********************************************************************************************
' *********************************************************************************************
' The Reset method instructs the enumerator to position itself at the beginning of the list
' of elements.
' Return Value:
' Returns %S_OK if the method succeeds.
' *********************************************************************************************
DECLARE FUNCTION Template_IEnumVARIANT_Reset (BYVAL pThis AS DWORD) AS DWORD
' *********************************************************************************************
FUNCTION IEnumVARIANT_Reset (BYVAL pThis AS DWORD) AS DWORD
LOCAL HRESULT AS DWORD
LOCAL ppthis AS DWORD PTR
LOCAL pvtbl AS DWORD PTR
LOCAL ppmethod AS DWORD PTR
LOCAL pmethod AS DWORD
ppthis = pThis
pvtbl = @ppthis
ppmethod = pvtbl + 20
pmethod = @ppmethod
CALL DWORD pmethod USING Template_IEnumVARIANT_Reset(pThis) TO HRESULT
FUNCTION = HRESULT
END FUNCTION
' *********************************************************************************************
' *********************************************************************************************
' Decrements the reference count on the specified interface. If the reference count on the
' object falls to 0, the object is freed from memory.
' Returns the resulting value of the reference count, which is used for diagnostic/testing
' purposes only.
' *********************************************************************************************
DECLARE FUNCTION Template_WbemCollection_Release (BYVAL pThis AS DWORD) AS DWORD
' *********************************************************************************************
FUNCTION WbemCollection_Release (BYVAL pThis AS DWORD) AS DWORD
LOCAL HRESULT AS DWORD
LOCAL ppthis AS DWORD PTR
LOCAL pvtbl AS DWORD PTR
LOCAL ppmethod AS DWORD PTR
LOCAL pmethod AS DWORD
ppthis = pThis
pvtbl = @ppthis
ppmethod = pvtbl + 8
pmethod = @ppmethod
CALL DWORD pmethod USING Template_WbemCollection_Release(pThis) TO HRESULT
FUNCTION = HRESULT
END FUNCTION
' *********************************************************************************************
' *********************************************************************************************
' Returns a pointer to the IEnumVARIANT interface.
' *********************************************************************************************
DECLARE FUNCTION Proto_WbemCollection_NewEnum (BYVAL pThis AS DWORD, BYREF pEnum AS DWORD) AS DWORD
' *********************************************************************************************
FUNCTION WbemCollection_NewEnum (BYVAL pThis AS DWORD) AS DWORD
LOCAL pEnum AS DWORD
LOCAL HRESULT AS DWORD
LOCAL ppthis AS DWORD PTR
LOCAL pvtbl AS DWORD PTR
LOCAL ppmethod AS DWORD PTR
LOCAL pmethod AS DWORD
ppthis = pThis
pvtbl = @ppthis
ppmethod = pvtbl + 28
pmethod = @ppmethod
CALL DWORD pmethod USING Proto_WbemCollection_NewEnum(pThis, pEnum) TO HRESULT
FUNCTION = pEnum
END FUNCTION
' *********************************************************************************************
' *********************************************************************************************
' Returns the number ob objects in the collection.
' *********************************************************************************************
DECLARE FUNCTION Proto_WbemCollection_Count (BYVAL pThis AS DWORD, BYREF nCount AS LONG) AS DWORD
' *********************************************************************************************
FUNCTION WbemCollection_Count (BYVAL pThis AS DWORD) AS LONG
LOCAL nCount AS DWORD
LOCAL HRESULT AS DWORD
LOCAL ppthis AS DWORD PTR
LOCAL pvtbl AS DWORD PTR
LOCAL ppmethod AS DWORD PTR
LOCAL pmethod AS DWORD
ppthis = pThis
pvtbl = @ppthis
ppmethod = pvtbl + 36
pmethod = @ppmethod
CALL DWORD pmethod USING Proto_WbemCollection_Count(pThis, nCount) TO HRESULT
FUNCTION = nCount
END FUNCTION
' *********************************************************************************************
' *********************************************************************************************
' Helper function to enumerate collectios.
' Parameters:
' pThis = Pointer to the collection.
' vArray = A dimensioned array of variants.
' *********************************************************************************************
FUNCTION WbemCollection_Enumerate (BYVAL pThis AS DWORD, BYREF vArray() AS VARIANT) EXPORT AS LONG
LOCAL IID_IEnumVariant AS GUID
IID_IEnumVARIANT = GUID$("{00020404-0000-0000-c000-000000000046}")
LOCAL HRESULT AS DWORD
LOCAL pIEnumVARIANT AS DWORD
LOCAL celtFetched AS DWORD
LOCAL vRes AS VARIANT
LOCAL idx AS LONG
IF pThis = 0 THEN EXIT FUNCTION ' Null pointer
IF UBOUND(vArray) = -1 THEN EXIT FUNCTION ' Array not dimensioned
' See if the interface is supported
HRESULT = IEnumVARIANT_QueryInterface (pThis, IID_IEnumVARIANT, VARPTR(pIEnumVARIANT))
IF HRESULT <> %S_OK THEN EXIT FUNCTION
' Position the enumerator at the beginning of the list of elements
HRESULT = IEnumVARIANT_Reset (pIEnumVARIANT)
IF HRESULT <> %S_OK THEN
IEnumVARIANT_Release pIEnumVARIANT
EXIT FUNCTION
END IF
' Fill the array
idx = LBOUND(vArray)
DO
HRESULT = IEnumVARIANT_Next (pIEnumVARIANT, 1, vRes, celtFetched)
IF HRESULT <> %S_OK OR celtFetched < 1 THEN EXIT DO
vArray (idx) = vRes
idx = idx + 1
IF idx > UBOUND(vArray) THEN EXIT DO
LOOP
' Release the interface
HRESULT = IEnumVARIANT_Release(pIEnumVARIANT)
FUNCTION = -1
END FUNCTION
' *********************************************************************************************
' *********************************************************************************************
' Main
' *********************************************************************************************
FUNCTION PBMAIN
LOCAL oLocator AS DISPATCH
LOCAL oServices AS DISPATCH
LOCAL oProcess AS DISPATCH
LOCAL vServer AS VARIANT
LOCAL vVar AS VARIANT
LOCAL vRes AS VARIANT
LOCAL i AS LONG
LOCAL hr AS DWORD
LOCAL pCollection AS DWORD
LOCAL pEnum AS DWORD
LOCAL nCount AS LONG
LOCAL ProcID AS DWORD
SET oLocator = NEW DISPATCH IN "WbemScripting.SWbemLocator"
IF ISFALSE ISOBJECT(oLocator) THEN EXIT FUNCTION
' Create a new connection to a computer (the local computer in this case) and
' retrieve an SWbemServices object, so you can work with WMI.
' vServer = "." equals to local server.
vServer = "." : OBJECT CALL oLocator.ConnectServer(vServer) TO vRes
SET oServices = vRes
vVar = "SELECT * FROM Win32_Process"
OBJECT CALL oServices.ExecQuery(vVar) TO vRes
pCollection = VARIANT#(vRes)
' Get the number of the objects in the collection
nCount = WbemCollection_Count(pCollection)
IF nCount = 0 THEN GOTO Terminate
' Get a pointer to the iEnumVARIANT interface
pEnum = WbemCollection_NewEnum(pCollection)
IF pEnum = 0 THEN GOTO Terminate
' DIM an array of variants
DIM vArray(1 TO nCount) AS VARIANT
' Iterate throught the collection of objects.
IF ISFALSE WbemCollection_Enumerate(pEnum, vArray()) THEN GOTO Terminate
' Release the collection
hr = WbemCollection_Release(pCollection)
FOR i = 1 TO nCount
IF VARIANT#(vArray(i)) <> 0 THEN
SET oProcess = vArray(i)
IF OBJRESULT THEN EXIT FOR
vRes = EMPTY : OBJECT GET oProcess.ProcessID TO vRes
PRINT "ProcID: " VARIANT#(vRes) " ";
vRes = EMPTY : OBJECT GET oProcess.ExecutablePath TO vRes
PRINT "Executable path: " VARIANT$(vRes)
SET oProcess = NOTHING
END IF
NEXT
Terminate:
SET oServices = NOTHING
SET oLocator = NOTHING
WAITKEY$
END FUNCTION
' *********************************************************************************************