After creating the script code and the description.xml file, we can test whether our custom object functions properly. To test the extension, we need to change the TestComplete settings to specify the extension folder and call some methods from our scripts. Below is the steps that demonstrate how to do this.
One note before we proceed: the names of the extension and object we created in this tutorial (WMI Object and WMI) coincides with the name of the extension and objects that are installed in TestComplete. Before installing our extension, disable the TestComplete extension:
-
Select File | Install Script Extensions from the main menu to invoke the Script Extensions dialog.
-
In the dialog, uncheck the WMI Object extension.
-
Press OK to close the dialog and save the changes.
Now we can install our script extension into TestComplete:
-
Select the Tools | Options from the TestComplete main menu. This will invoke the Options dialog.
-
In the dialog, select the Engines | Script Extensions node from the tree on the left of the dialog. This will open the Script Extensions Options page. Here you can specify the folders that contain script extensions.
-
In the dialog, click Add Folder. This will invoke the standard Browse for Folder dialog.
-
In the dialog, choose the C:\My Extension Files folder and click OK. The folder will be added to the script extensions list:
We would like to note that we specified C:\My Extension Files, rather than C:\My Extension Files\Extension1. So far our script extension files are not packed to the .tcx package. When specifying the path to the unpacked extensions, you should not specify the folder that contains the description.xml file, but the folder that is one level up. This happens because TestComplete treats each item of the Script Extensions Folders list like the folder that may contain several extensions, but not just one extension. So, unpacked files cannot reside in the root of these folders, they must be in subfolders. That is why we specified C:\My Extension Files, but not C:\My Extension Files\Extension1. If we had the packed extension (the .tcx file) in the Extension1 folder, we would have specified C:\My Extension Files\Extension1. -
Make sure that the check box of the C:\My Extension Files item is selected in the list.
-
Click OK to save the changes and to close the Options dialog.
After you added the our extension’s path to the Script Extension Options dialog and saved the changes, TestComplete scans the folder and automatically installs the extension. To check whether the extension is installed successfully:
-
Open the Script Extensions dialog (select File | Install Script Extension from the main menu). The extension should be listed in the dialog:
-
The object should appear in the Code Completion window. Open any script unit for editing and check this:
To test whether our WMI
object functions properly, create script code that call the object’s methods and properties. Below is sample code that calls some of the object’s methods:
JavaScript, JScript
function Test()
{
WMI.PostProcessorInfo();
Log.Message("Processor architecture: " + WMI.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE"));
WMI.PostDrivesInfo();
WMI.PostInstalledAppsInfo();
WMI.CreateProcess("notepad");
}
Python
def Test():
WMI.PostProcessorInfo()
Log.Message("Processor architecture: " + WMI.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE"))
WMI.PostDrivesInfo()
WMI.PostInstalledAppsInfo()
WMI.CreateProcess("notepad")
VBScript
Sub Test
WMI.PostProcessorInfo
Log.Message "Processor architecture: " & WMI.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE")
WMI.PostDrivesInfo
WMI.PostInstalledAppsInfo
WMI.CreateProcess "notepad"
End Sub
DelphiScript
procedure Test;
begin
WMI.PostProcessorInfo;
Log.Message('Processor architecture: ' + WMI.GetEnvironmentVariable('PROCESSOR_ARCHITECTURE'));
WMI.PostDrivesInfo;
WMI.PostInstalledAppsInfo;
WMI.CreateProcess('notepad');
end;
C++Script, C#Script
function Test()
{
WMI["PostProcessorInfo"]();
Log["Message"]("Processor architecture: " + WMI["GetEnvironmentVariable"]("PROCESSOR_ARCHITECTURE"));
WMI["PostDrivesInfo"]();
WMI["PostInstalledAppsInfo"]();
WMI["CreateProcess"]("notepad");
}
If there are any errors, make sure the contents of the description.xml and wmiCode.vbs files are correct and that the script file has the ANSI or UTF-8 character encoding. The full contents of these files is provided below:
description.xml
[description.xml]
<?xml version="1.0" encoding="UTF-8"?>
<ScriptExtensionGroup>
<Category Name="Runtime Objects">
<ScriptExtension Name="WMI Object" Author="SmartBear Software" Version="1.1 test" HomePage="smartbear.com">
<Script Name="wmiCode.vbs" InitRoutine="Initialize">
<RuntimeObject Name="WMI">
<Method Name="ConnectToComputer" Routine="ConnectToComputer">
Connects to WMI on the specified computer using the specified user name and password.
</Method>
<Method Name="WaitForComputer" Routine="WaitForComputer">
Waits until the WMI connection to the specified computer is established during a timeout period.
</Method>
<Method Name="RestartComputer" Routine="RestartComputer">
Restarts the computer.
</Method>
<Method Name="CreateProcess" Routine="CreateProcess">
Launches the specified process.
</Method>
<Method Name="ProcessExists" Routine="ProcessExists">
Checks if the specified process is running.
</Method>
<Method Name="WaitForProcessExit" Routine="WaitForProcessExit">
Waits for a process to end during a timeout period.
</Method>
<Method Name="GetServiceState" Routine="GetServiceState">
Returns the current state of the specified service.
</Method>
<Method Name="WaitForServiceState" Routine="WaitForServiceState">
Waits for a service state to change during a timeout period.
</Method>
<Method Name="GetEnvironmentVariable" Routine="GetEnvironmentVariable">
Returns the value of the specified environment variable.
</Method>
<Method Name="PostEnvironmentVariableInfo" Routine="PostEnvironmentVariableInfo">
Logs information about environment variables.
</Method>
<Method Name="PostProcessorInfo" Routine="PostProcessorInfo">
Logs information about the processor(s) installed on the computer.
</Method>
<Method Name="PostDrivesInfo" Routine="PostDrivesInfo">
Logs information about disk drives available on the computer.
</Method>
<Method Name="PostInstalledAppsInfo" Routine="PostInstalledAppsInfo">
Logs information about all the applications installed on the computer.
</Method>
<Method Name="PostEventsInfo" Routine="PostEventsInfo">
Posts information from the system event log to the test log.
</Method>
<Property Name="ComputerName" GetRoutine="GetComputerName" SetRoutine="SetComputerName">
The name of the computer that the WMI object works with. A dot (".") stands for the local computer.
</Property>
<Property Name="Service" GetRoutine="GetWMIService">
Returns the WMI service object.
</Property>
<Property Name="MaxEventCount" GetRoutine="GetMaxEventCount" SetRoutine="SetMaxEventCount">
Specifies the maximum number of entries to be retrieved from the system event log.
</Property>
<Description>
Provides access to the WMI service.
</Description>
</RuntimeObject>
</Script>
<Description>
Provides scripting interface to WMI objects.
</Description>
</ScriptExtension>
</Category>
</ScriptExtensionGroup>
wmiCode.vbs
' ============================== Global Variables ==============================
Dim WMIService
Dim ComputerName
Dim MaxEventCount
' ========== SWbemLocator.ConnectServer() Default Constant Definitions =========
Const CLocalComputer = "."
Const CDefaultNamespace = "Root\CIMv2"
Const CDefaultLocale = Null
Const CDefaultAuthority = Null
Const CZeroConnectionTimeout = &H0 ' = 0
Const ConnectFlag_UseMaxWait = &H80 ' = 2 Min
' ========================= Common Constant Definitions ========================
Const Flags_RtnImmedFwdOnly = &H30 ' The iFlags value for the SWbemServices.ExecQuery method
Const CPollInterval = 0.1 ' = 100 ms; Polling interval for event queries
Const CDefaultEventCount = 50 ' The default number of the latest system events to be logged by PostEventsInfo
' ===================== User Messages Constant Definitions =====================
Const UMSG_ComputerConnected = "A connection to the ""%s"" computer has been established successfully."
Const UMSG_UnableToConnect = "Unable to connect to the ""%s"" computer."
Const UMSG_ConnectionTimeout = "Failed to connect to the ""%s"" computer during the timeout period."
Const USMG_WaitingForConnection = "Connecting to the ""%s"" computer..."
Const UMSG_ProcessWasCreated = "The ""%s"" process has been created successfully."
Const UMSG_UnableToCreateProcess = "Unable to create the ""%s"" process."
Const UMSG_ErrorWhileCreatingProcess = "An error occurred while creating the ""%s"" process. The error code is %u."
Const UMSG_ProcessNotFound = "The ""%s"" process is not running."
Const UMSG_ProcessExited = "The ""%s"" process has closed."
Const UMSG_ProcessHasntExited = "The ""%s"" process has not closed during the timeout period."
Const UMSG_WaitingForProcessExit = "Waiting for the ""%s"" process to close..."
Const UMSG_ServiceNotFound = "The ""%s"" service is not registered."
Const UMSG_ServiceStateChanged = "The state of the ""%s"" service has changed to ""%s""."
Const UMSG_ServiceStateTimeout = "The state of the ""%s"" service has not changed to ""%s"" during the timeout period."
Const UMSG_WaitingForServiceStateChange = "Waiting until the state of the ""%s"" service changes..."
' ========================== WMI Constant Definitions ==========================
' SWbemLocator.Security_.AuthenticationLevel Values
Const AuthenticationLevel_Default = 0
' SWbemLocator.Security_.ImpersonationLevel Values
Const ImpersonationLevel_Impersonate = 3
' SWbemLocator.Locator.Security_.Privileges Values
Const Privilege_ChangeNotify = "SeChangeNotifyPrivilege"
Const Privilege_RemoteShutdown = "SeRemoteShutdownPrivilege"
Const Privilege_Restore = "SeRestorePrivilege"
Const Privilege_Shutdown = "SeShutdownPrivilege"
' ================ The Script Extension's Initialization Routine ===============
Sub Initialize
MaxEventCount = CDefaultEventCount
ComputerName = CLocalComputer
ConnectToComputerInternal Null, Null
End Sub
' ====================== WMI Object Methods and Properties =====================
Sub ConnectToComputer(Computer, User, Password)
ComputerName = Computer
' Attempt to connect to WMI on the specified computer
On Error Resume Next
Indicator.PushText(aqString.Format(USMG_WaitingForConnection, GetComputerDisplayName))
ConnectToComputerInternal User, Password
Indicator.PopText
' Check if connection was successful
If Err.Number = 0 Then
Log.Message(aqString.Format(UMSG_ComputerConnected, GetComputerDisplayName))
Else
Log.Error(aqString.Format(UMSG_UnableToConnect, GetComputerDisplayName))
End If
End Sub
Sub WaitForComputer(Computer, User, Password, Timeout)
ComputerName = Computer
' Convert the timeout value from milliseconds to seconds (round to larger)
Dim TimeoutSeconds
TimeoutSeconds = Timeout \ 1000
If Timeout Mod 1000 <> 0 Then
TimeoutSeconds = TimeoutSeconds + 1
End If
' Perform multiple attempts to connect to the specified computer during the timeout period
On Error Resume Next
Indicator.PushText(aqString.Format(USMG_WaitingForConnection, GetComputerDisplayName))
Dim StartTime
StartTime = Now
Do
Err.Clear
ConnectToComputerInternal User, Password
Loop While (Err.Number <> 0) And (DateDiff("s", StartTime, Now) < TimeoutSeconds)
Indicator.PopText
' Check if connection succeeded
If Err.Number = 0 Then
Log.Message(aqString.Format(UMSG_ComputerConnected, GetComputerDisplayName))
Else
Log.Error(aqString.Format(UMSG_ConnectionTimeout, GetComputerDisplayName))
End If
End Sub
Sub RestartComputer
Dim OSColl, OSItem
Set OSColl = WMIService.ExecQuery("SELECT * FROM Win32_OperatingSystem")
For Each OSItem In OSColl
OSItem.Reboot
Next
End Sub
Function CreateProcess(CommandLine)
Dim Process, InParams, OutParams
Set Process = WMIService.Get("Win32_Process")
' Specify the command line to execute
Set InParams = Process.Methods_("Create").InParameters.SpawnInstance_
InParams.CommandLine = CommandLine
' Launch the process and get its ID
Set OutParams = WMIService.ExecMethod("Win32_Process", "Create", InParams)
CreateProcess = OutParams.ProcessId
' Check if the process was launched successfully
If OutParams.ReturnValue = 0 Then
Log.Message(aqString.Format(UMSG_ProcessWasCreated, CommandLine))
ElseIf IsNull(OutParams.ReturnValue) Then
Log.Error(aqString.Format(UMSG_UnableToCreate, CommandLine))
Else
Log.Error(aqString.Format(UMSG_ErrorWhileCreatingProcess, CommandLine, OutParams.ReturnValue))
End If
End Function
Function ProcessExists(ProcessName)
ProcessExists = False
Dim ProcessColl, ProcessItem
' Search for a process with the specified name
Set ProcessColl = WMIService.ExecQuery _
("SELECT * FROM Win32_Process WHERE Name='" & ProcessName & "'", , Flags_RtnImmedFwdOnly)
' Check if the found object is valid
For Each ProcessItem In ProcessColl
ProcessExists = Not IsNull(ProcessItem.ProcessId)
Exit For
Next
End Function
Sub WaitForProcessExit(ProcessName, Timeout)
' Verify that the specified process is running
If Not ProcessExists(ProcessName) Then
Log.Message(aqString.Format(UMSG_ProcessNotFound, ProcessName))
Exit Sub
End If
Dim EventSource, EventItem
' Subscribe to the process termination event
Indicator.PushText(aqString.Format(UMSG_WaitingForProcessExit, ProcessName))
Set EventSource = oWMIService.ExecNotificationQuery _
("SELECT * FROM __InstanceDeletionEvent WITHIN " & CPollInterval _
& " WHERE TargetInstance ISA 'Win32_Process'" _
& " AND TargetInstance.Name='" & ProcessName & "'")
' Wait for the process to exit during the timeout period
On Error Resume Next
Set EventItem = EventSource.NextEvent(Timeout)
Set EventSource = Nothing
Indicator.PopText
' Check if the process has exited
If IsEmpty(EventItem) Then
Log.Error(aqString.Format(UMSG_ProcessHasntExited, ProcessName))
Else
Log.Message(aqString.Format(UMSG_ProcessExited, ProcessName))
End If
End Sub
Function GetServiceState(ServiceName)
Dim ServiceColl, ServiceItem
' Search for a service with the specified display name
Set ServiceColl = WMIService.ExecQuery _
("SELECT * FROM Win32_Service WHERE DisplayName='" & ServiceName & "'", , Flags_RtnImmedFwdOnly)
' Get the state of the service
For Each ServiceItem In ServiceColl
GetServiceState = ServiceItem.State
Exit For
Next
End Function
Sub WaitForServiceState(ServiceName, State, Timeout)
' Verify that the specified service is registered
If IsEmpty(GetServiceState(ServiceName)) Then
Log.Error(aqString.Format(UMSG_ServiceNotFound, ServiceName))
Exit Sub
End If
Dim EventSource, EventItem
' Subscribe to the service state change event
Indicator.PushText(aqString.Format(UMSG_WaitingForServiceStateChange, ServiceName))
Set Events = WMIService.ExecNotificationQuery _
("SELECT * FROM __InstanceModificationEvent WITHIN " & CPollInterval _
& " WHERE TargetInstance ISA 'Win32_Service'" _
& " AND TargetInstance.DisplayName='" & ServiceName & "'" _
& " AND TargetInstance.State='" & State & "'")
' Wait for the service state to change during the timeout period
On Error Resume Next
Set EventItem = Events.NextEvent(Timeout)
Set Events = Nothing
Indicator.PopText
' Check if the service state has changed
If IsEmpty(EventItem) Then
Log.Error(aqString.Format(UMSG_ServiceStateTimeout, ServiceName, State))
Else
Log.Message(aqString.Format(UMSG_ServiceStateChanged, ServiceName, CurrentState))
End If
End Sub
' The get method for the WMI.ComputerName property
Function GetComputerName
GetComputerName = ComputerName
End Function
' The set method for the WMI.ComputerName property
Sub SetComputerName(Name)
ComputerName = Name
ConnectToComputerInternal Null, Null
End Sub
' The get method for the WMI.MaxEventCount property
Function GetMaxEventCount
GetMaxEventCount = MaxEventCount
End Function
' The set method for the WMI.MaxEventCount property
Sub SetMaxEventCount(Count)
MaxEventCount = Count
End Sub
' The get method for the WMI.Service property
Function GetWMIService
Set GetWMIService = WMIService
End Function
Function GetEnvironmentVariable(Name)
Dim EnvVarColl, EnvVarItem
' Search for an environment variable with the specified name
Set EnvVarColl = WMIService.ExecQuery _
("SELECT * FROM Win32_Environment WHERE Name='" & Name & "'", , Flags_RtnImmedFwdOnly)
' Get the variable's value
For Each EnvVarItem In EnvVarColl
GetEnvironmentVariable = EnvVarItem.VariableValue
Exit For
Next
End Function
Sub PostEnvironmentVariableInfo
Dim EnvVarColl, EnvVarItem
Log.AppendFolder("Environment Variables")
' Obtain all environment variables
Set EnvVarColl = WMIService.ExecQuery("SELECT * FROM Win32_Environment", , Flags_RtnImmedFwdOnly)
' Log the variables' properties
For Each EnvVarItem In EnvVarColl
Log.AppendFolder EnvVarItem.Name
Log.Message "Value: " & EnvVarItem.VariableValue
Log.Message "User name: " & EnvVarItem.UserName
Log.Message "Description: " & EnvVarItem.Description
Log.Message "System variable: " & EnvVarItem.SystemVariable
Log.PopLogFolder
Next
Log.PopLogFolder
End Sub
Sub PostProcessorInfo
Dim ObjectsList, CurrentObject, ID
Log.AppendFolder "Processors"
' Obtain all the processors
Set ObjectsList = WMIService.ExecQuery("SELECT * FROM Win32_Processor", , Flags_RtnImmedFwdOnly)
' Log the processors' properties
ID = 1
For Each CurrentObject In ObjectsList
Log.AppendFolder "Processor #" & ID
Log.Message "Manufacturer: " & CurrentObject.Manufacturer
Log.Message "Description: " & CurrentObject.Description
Log.Message "Frequency: " & CurrentObject.CurrentClockSpeed
Log.PopLogFolder
ID = ID + 1
Next
Log.PopLogFolder
End Sub
Sub PostDrivesInfo
Dim ObjectsList, CurrentObject
Log.AppendFolder "Logical Disks"
' Obtain all the fixed hard drives
Set ObjectsList = WMIService.ExecQuery("SELECT * FROM Win32_LogicalDisk WHERE MediaType=12", , Flags_RtnImmedFwdOnly)
' Log the hard drives' properties
For Each CurrentObject In ObjectsList
Log.AppendFolder CurrentObject.Name
Log.Message "Label: " & CurrentObject.VolumeName
Log.Message "Size (MB): " & CurrentObject.Size / 1024^2
Log.Message "Free space (MB): " & CurrentObject.FreeSpace / 1024^2
Log.PopLogFolder
Next
Log.PopLogFolder
End Sub
Sub PostInstalledAppsInfo
Dim ObjectsList, CurrentObject
Log.AppendFolder "Installed Applications"
' Obtain all the installed products
Set ObjectsList = WMIService.ExecQuery("SELECT * FROM Win32_Product", , Flags_RtnImmedFwdOnly)
' Log the products' properties
For Each CurrentObject In ObjectsList
Log.AppendFolder CurrentObject.Caption
Log.Message "Vendor: " & CurrentObject.Vendor
Log.Message "Version: " & CurrentObject.Version
Log.Message "Description: " & CurrentObject.Description
Log.Message "Location: " & CurrentObject.InstallLocation
Log.PopLogFolder
Next
Log.PopLogFolder
End Sub
Sub PostEventsInfo
Dim ObjectsList, CurrentObject, Count
Log.AppendFolder("System Events")
' Retrieve entries from the system event log
Set ObjectsList = WMIService.ExecQuery("SELECT * FROM Win32_NTLogEvent WHERE LogFile='System'")
' Log the latest MaxEventCount events
Count = 1
For Each CurrentObject in ObjectsList
Log.AppendFolder(CurrentObject.SourceName)
Log.Message "User: " & CurrentObject.User
Log.Message "Category: " & CurrentObject.CategoryString
Select Case CurrentObject.EventType
Case 1
Log.Error "Message", CurrentObject.Message
Case 2
Log.Warning "Message", CurrentObject.Message
Case 3
Log.Message "Message", CurrentObject.Message
End Select
Log.PopLogFolder
Count = Count + 1
If Count > MaxEventCount Then
Exit For
End If
Next
Log.PopLogFolder
End Sub
' ============================== Internal Routines =============================
' Connects to WMI on a computer using the specified credentials and security settings
Sub ConnectToComputerInternal(User, Password)
' Specify the local computer's name by default
If IsNull(ComputerName) Then
ComputerName = CLocalComputer
End If
' Specify security settings for the WMI connection
Dim Locator
Set Locator = CreateObject("WbemScripting.SWbemLocator")
Locator.Security_.AuthenticationLevel = AuthenticationLevel_Default ' Use default server-side settings
Locator.Security_.ImpersonationLevel = ImpersonationLevel_Impersonate ' Use the client's security credentials
Locator.Security_.Privileges.AddAsString(Privilege_ChangeNotify)
Locator.Security_.Privileges.AddAsString(Privilege_Shutdown)
Locator.Security_.Privileges.AddAsString(Privilege_RemoteShutdown)
Locator.Security_.Privileges.AddAsString(Privilege_Restore)
' Connect to WMI on the specified computer
Set WMIService = Nothing
Set WMIService = Locator.ConnectServer _
(ComputerName, CDefaultNamespace, User, Password, CDefaultLocale, CDefaultAuthority, CZeroConnectionTimeout)
Set Locator = Nothing
End Sub
' Returns the display name of the computer to be used in indicator and log messages
Function GetComputerDisplayName
If ComputerName = CLocalComputer Then
GetComputerDisplayName = Sys.HostName
Else
GetComputerDisplayName = ComputerName
End If
End Function
See Also
Debugging Script Extensions
Installing and Uninstalling Script Extensions