Does VBScript Support Introspection for Objects? - vbscript

I am pulling results back from WMI using WQL via VBScript.
In examples, a For Each loop is used to iterate over the results, but in each example, it is assumed that the property names are known. Case in point:
Set colInstalledPrinters = objWMIService.ExecQuery ("Select * from Win32_Printer Where Default = True")
For Each objPrinter in colInstalledPrinters
Wscript.Echo objPrinter.Name
Next
Some of the WMI classes have a very long list of properties associated with them. As an additional complication, some properties cannot be expected to be present (according to various webpages I have read about WMI). Rather than researching each WMI class and hoping that the properties listed are present, I would like to obtain a list of the properties (or columns, if I am thinking in SQL/WQL) present for, say, an objPrinter or any other returned item.
Python is my usual language but I cannot install it on the target machines in this instance; I can perform remote querying of WMI via Python but I am trying to trigger on an local event, hence falling back to VBScript. Although I gather Powershell might be able to do this, I would rather not learn it just this instant.
So, does VBScript support that level of introspection which would allow me to enumerate a list of properties? Or is there something I can do involving a schema I can reference and examine in-script?

Use the .Properties_ collection of the item:
Option Explicit
Const wbemFlagReturnImmediately = &h10
Const wbemFlagForwardOnly = &h20
Dim objWMIService
Set objWMIService = GetObject("winmgmts:\\.\root\CIMV2")
Dim colItems
Set colItems = objWMIService.ExecQuery( _
"SELECT * FROM Win32_Printer" _
, "WQL" _
, wbemFlagReturnImmediately + wbemFlagForwardOnly _
)
Dim objItem
For Each objItem In colItems
Dim oProp
For Each oProp In objItem.Properties_
WScript.Echo oProp.Name, TypeName( oProp.Value ), ToString( oProp.Value )
Next
WScript.Echo
Next
Function ToString( vX )
ToString = "!! work to do !!"
On Error Resume Next
ToString = CStr( vX )
On Error GoTo 0
End Function
Output:
...
MimeTypesSupported Null !! work to do !!
Name String Auto HP LaserJet 5 on WINXP2
NaturalLanguagesSupported Null !! work to do !!
Network Boolean False
PaperSizesSupported Variant() !! work to do !!
...
Obviously, the ToString() function needs further work.

Related

Get running instance of application from WMI process?

I'm trying to get hold of running instances of MS Access 2010+ (on Win10) but the usual tip; GetObject(, "Access.Application") ... for works only for hidden instances started by myself with script, but not any instances started from GUI by the user.
And yes, I've read perhaps ten or more google hits on the subject, both on WMI and GetObject, but I seem to have missed something important.
However, I've tried the code below and I can get hold of any process of running Access instances in this way, and I can even .terminate() them, but, that's not what I want to do. Instead I want to grab the process and assign it to a usable (correct type) Access variable (see "OutInstance" in the code below) :
[Edit: Using WHERE clause, and skipped Exit as to retrieve the last instance]
Public Function GetRunningInstance(sAppName sComputer, ByRef OutInstance)
Dim oWMIService
Dim wProcesses
Dim oPrc
GetRunningInstance = False
Set OutInstance = Nothing
if sComputer = "" then sComputer = "."
Set oWMIService = GetObject("winmgmts:" & "{impersonationLevel=" & _
"impersonate}!\\" & sComputer & "\root\cimv2")
Set wProcesses = oWMIService.ExecQuery ("SELECT * FROM Win32_Process " & _
"WHERE Name = '" & sAppName & "'")
For Each oPrc in wProcesses
''' oPrc.Terminate() ''' Works, I can shut down Access...
Set OutInstance = oPrc
GetRunningInstance = True
''' By not exiting we get the last instance.
Next
End Function
Now, after trying to get hold of an instance, how do I "cast" the process to a usable Access application variable in this VBScript?
Sub Test_DoStuff()
Dim InstProc
Dim AccessApp
If GetRunningInstance("msaccess.exe", "127.0.0.1", InstProc) Then
Set AccessApp = ''' cast 'InstProc' to my "Access.Application" somehow?
Else
Set AccessApp = CreateObject("Access.Application")
End If
'''
''' Doing my stuff
'''
AccessApp.CloseCurrentDatabase
AccessApp.DoCmd.Quit
End Sub
Test
I (also) don't understand why GetObject(, "Access.Application") doesn't work in all cases. Permissions? (I understand that it's 'unsafe' to close a database currently being used by a user, but also that can be dealt with).
// Rolf

Return volume name in Do loop

I'm trying to create a script for HP UFT to automatically create/mount/unmount VHDs as needed. The problem is that diskpart takes quite a while to create, mount, format and label the VHD. I need a method to pause the script while the VHD is being created/mounted, after which it will continue on with the script.
The variable uftDrive is currently returning a drive letter, not a volume name, so the loop just runs indefinitely. Any thoughts as to how to pass the volume names as a variable? The VHD script is automatically assigning the first-available drive letter to the drive, as we have multiple machines that UFT will be run on, and they don't have identical network drive mappings, forcing us to dynamically detect the drive by volume name.
'===========================================================================
'This will check to see if the Virtual Hard Disk (VHD) exists, and if not,
'create it
'===========================================================================
Dim makevhdExists, vhdExists
Set makevhdExists = CreateObject("Scripting.FileSystemObject")
Set vhdExists = CreateObject("Scripting.FileSystemObject")
If Not makevhdExists.FileExists("c:\UFT\mountVHD.bat") Then
makevhdExists.CopyFile "\\companyADfolders\Users\UFT\VHD\*.*", "c:\UFT\"
End If
If Not makevhdExists.FileExists("c:\UFT\unmountVHD.bat") Then
wait 2
ElseIf Not vhdExists.FileExists("C:\UFT\UFT.vhd") Then
SystemUtil.Run "cmd","/c""C:\UFT\createVHD.bat"""
End If
Dim uftExists, uftDrive
uftExists = "False"
Do
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery _
("Select * From Win32_LogicalDisk Where VolumeName = 'UFT'")
For Each objItem in colItems
uftDrive = objItem.Name 'This is currently returning a drive letter,
'not a volume name
If uftDrive.Name = "UFT" Then
uftExists = "True"**
End If
Next
Loop Until uftExists = "True"
For the wait part
WScript.Sleep 5000
where the value indicated is milliseconds
For the second part, maybe i'm missing something, so, instead of an answer I have one silly question:
If you query wmi for win32_logicaldisk instances where their VolumeName is UFT, WHY your if command checks the Name property instead of the VolumeName property?
In any case, you don't need that if. If colItems.Count is greater than 0, there is at least one instance that matches the indicated condition
Something like this should work
Dim uftExists
uftExists = False
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Do
Set colItems = objWMIService.ExecQuery _
("Select * From Win32_LogicalDisk Where VolumeName = 'UFT'")
If colItems.Count > 0 Then
utfExists = True
Else
Sleep 500
End If
Loop Until uftExists
Changing ObjItem.Name to ObjItem.VolumeName worked!
For Each objItem in colItems
uftDrive = objItem.VolumeName 'This is currently returning a drive letter, not a volume name
If uftDrive = "UFT" Then
uftExists = "True"**
End If
Next
For the first question: To delay the script, you can use this function:
WScript.Sleep 1000
The number is in Milliseconds, so 1000 is 1 second.

Query to Select All Non-Essential Temporary Files

I am trying to write a script to delete unneeded temporary files. I am wanting to specifically target .tmp's, though. At least for now. So I am trying to write a WQL query to return a collection with which I can use a FOR EACH statement to delete all of the .tmp's in C:\Users\\AppData\Local\Temp. I've only recently started learning VBScript. But I have experience writing programs in C/C++ (mainly "math-y" programs).
Cscript seems to have no problem with the query itself. But when I try to use the Count method on the resulting collection, cscript returns an error: (17,1) Microsoft VBVScript runtime error: Object doesn't support this property or method: 'colTempFiles.Count'.
I've read up on WQL a little bit, thinking that maybe I'm not getting a collection returned for some reason. But I can't seem to find anything wrong with the query. I'm thinking that maybe I shouldn't be selecting from FileSystemObject. But I've read what I can find about it, and it seems to be the right thing to do (although there really isn't a lot of helpful info on MSDN).
Anyway, here's the script I currently have, without comments. The second line is something I am not currently using, but am going to try to use later, so that I can define a variable as the local computer's username and not have to point to the local Temp folder's path specifically. Any help would be greatly appreciated:
strComputer = "."
strUser="adam"
Set objFSO=CreateObject("Scripting.FileSystemObject")
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colTempFiles = objWMIService.ExecQuery _
("SELECT * FROM FileSystemObject WHERE Name = '*.tmp' AND "_
& "NOT Name LIKE 'Prf%' AND Path LIKE 'C:\Users\adam\AppData\Local\Temp\%'")
colTempFiles.Count
For Each objFile in colTempFiles
Wscript.Echo objFile.Name
'Set objF=objFSO.GetFile("objFile.Path")
'objF.Delete(True)
Next
I think you're confusing two different technologies. A FileSystemObject is a COM class that needs to be instantiated using CreateObject() in VBScript. For WQL, you need to use a WMI class in your query. Here is a core list of WMI classes. For your purposes, you'll want to use the CIM_DataFile class to work with files.
You can use either technology. The FileSystemObject is the preferred method if you're working with the local file system. If you need to work with files on a remote machine, use WMI and WQL.
Here's an example using a FileSystemObject:
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder("C:\Users\adam\AppData\Local\Temp")
For Each objFile In objFolder.Files
If StrComp(objFSO.GetExtensionName(objFile.Path), "tmp", vbTextCompare) = 0 Then
objFile.Delete ' This is the Delete() method of the FSO's "File" class
End If
Next
And here's an example using WQL:
strComputer = "."
' Connect to the WMI service on the specified computer...
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
' Build our WQL query...
strQuery = "select * from CIM_DataFile "
strQuery = strQuery & "where Drive='C:' "
strQuery = strQuery & "and Path='\\Users\\adam\\AppData\\Local\\Temp\\' "
strQuery = strQuery & "and Name like '%.tmp'"
' Run the query...
Set colTempFiles = objWMIService.ExecQuery(strQuery)
' Delete each file...
For Each objFile In colTempFiles
objFile.Delete ' This is the Delete() method of the WMI "CIM_DataFile" class
Next

Need a VB Script to check if service exist

I want to write a VBS script which will check if specific service is installed/exist or not locally.
If it is not installed/exist, script will display message (any text) and disabled the network interface i.e. NIC.
If service exist and running, NO Action. Just exit.
If service exist but not running, same action, script will display message (any text) and disabled the network interface i.e. NIC.
i have below given code which is displaying a message in case one service is stop but it is not -
Checking if service exist or not
Disabling the NIC
strComputer = "."
Set objWMIService = Getobject("winmgmts:"_
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colRunningServices = onjWMIService.ExecQuery _
("select State from Win32_Service where Name = 'dhcp'")
For Each objService in colRunningServices
If objService.State <> "Running" Then
errReturn = msgbox ("Stopped")
End If
Next
Please help. Thanks in advance.
To know if service is installed, check colRunningServices.Count . It will be 0 if no service match the wmi query.
To disable NIC, use a wmi query SELECT * FROM Win32_NetworkAdapter, iterate de returned collection of NICs, find the one you are interested and use the Disable/Enable method of it. BUT this only will work if OS is Vista or later.
It would look something like this but I cannot test this!
Option Explicit
Const TITLE = "Title"
Const SERVICE = "dhcp"
Dim wmi
Dim svcs,svc
Set wmi = GetObject("winmgmts:\\.\root\cimv2")
Set svcs = wmi.ExecQuery("Select * from Win32_Service where Name = '" & SERVICE & "'")
If svcs.Count = 0 Then
Call MsgBox(SERVICE & " service does not exist",vbCritical,TITLE)
Call disableNIC(wmi)
Else
For Each svc In svcs
If svc.State <> "Running" Then
Call MsgBox(SERVICE & " service is not running",vbCritical,TITLE)
Call disableNIC(wmi)
End If
Next
End If
Set wmi = Nothing
WScript.Quit
Sub disableNIC(ByRef wmi)
Dim nics,nic
Set nics = wmi.ExecQuery("Select * from Win32_NetworkAdapter")
For Each nic In nics
nic.Disable
Next
End Sub
Beware that this disables every NIC. If you want to specify one you'd have to add a where clause in disableNIC. Also I imagine you would have to run this as an administrator.
Seems like a weird thing to want to do though...

VBS - Get Default Printer

Using the Wscript.Network object shown below, is there an easy way to retrieve the default printer on a machine? I know how to set the default printer, but I'm looking to get the current default printer name. I have a mixture of Windows 2000, XP, and 7 clients and don't want to use WMI for that reason.
Set objNetwork = CreateObject("WScript.Network")
Set objLocalPrinters = objNetwork.EnumPrinterConnections
The WshNetwork.EnumPrinterConnections collection doesn't provide any information about the default printer. You can try retrieving the default printer name from the registry instead, though I'm not sure if it's reliable:
Set oShell = CreateObject("WScript.Shell")
strValue = "HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows\Device"
strPrinter = oShell.RegRead(strValue)
strPrinter = Split(strPrinter, ",")(0)
WScript.Echo strPrinter
As for WMI, it's true that some WMI classes and class members aren't available on older Windows versions. For example, the Win32_Printer.Default property that indicates whether the printer is the default one, doesn't exist on Windows 2000/NT. Nevertheless, there's a simple workaround for finding the default printer on those Windows versions, which consists in checking for the PRINTER_ATTRIBUTE_DEFAULT attribute in each printer's Attribute bitmask:
Const ATTR_DEFAULT = 4
strComputer = "."
Set oWMI = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colPrinters = oWMI.ExecQuery("SELECT * FROM Win32_Printer")
For Each oPrinter in colPrinters
If oPrinter.Attributes And ATTR_DEFAULT Then
Wscript.Echo oPrinter.ShareName
End If
Next
This code works on later Windows versions as well.
For details, check out this Hey, Scripting Guy! article: Is There Any Way to Determine the Default Printer On a Computer?
From (MSDN):
The EnumPrinterConnections method returns a collection. This collection is an array that associates pairs of items — network printer local names and their associated UNC names. Even-numbered items in the collection represent printer ports. Odd-numbered items represent the networked printer UNC names. The first item in the collection is at index zero (0).
So there is little chance to get the default printer from this collection. Sorry
Greetz,
GHad
for MS ACCESS oPrinter.ShareName is null but oPrinter.Caption works well!
Dim strComputer As String
Dim oWMI As Object
Dim colPrinters
Dim oPrinter
Const ATTR_DEFAULT = 4
strComputer = "."
Set oWMI = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colPrinters = oWMI.ExecQuery("SELECT * FROM Win32_Printer")
For Each oPrinter In colPrinters
If oPrinter.Attributes And ATTR_DEFAULT Then
Debug.Print oPrinter.Caption
End If
Next

Resources