HEX
Server: Apache
System: Windows NT MAGNETO-ARM 10.0 build 22000 (Windows 10) AMD64
User: Michel (0)
PHP: 7.4.7
Disabled: NONE
Upload Files
File: C:/Windows/OEM/Utility.vbs
Option Explicit

Const TemporaryFolder = 2
Const ForReading = 1

Const ERROR_NORESOURCEDRIVE   = 7
Const ERROR_PROTOCOLVIOLATION = 4

Dim ERROR_FILE_NOT_FOUND : ERROR_FILE_NOT_FOUND = &H80070002&

Const KVP_REGISTRY_KEY_PATH = "SOFTWARE\Microsoft\Virtual Machine\Guest"
Const CURRENTVERSION_REGISTRY_KEY_PATH = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion"
Const AZURE_PROVISIONING_KEY_PATH = "HKEY_LOCAL_MACHINE\SYSTEM\Setup\AzureProvisioning"

Dim WshShell : Set WshShell = CreateObject("WScript.Shell")

Function GetSystemRegistry(strValueName)
    On Error Resume Next

    Dim valueName : valueName =  CURRENTVERSION_REGISTRY_KEY_PATH & "\" & strValueName
    Dim strValue : strValue = WshShell.RegRead(valueName)
    GetSystemRegistry = strValue
End Function

Function GetOSCurrentBuildNumber
    GetOSCurrentBuildNumber = GetSystemRegistry("CurrentBuildNumber")
End Function

Function GetOSBuildLabEx
    GetOSBuildLabEx = GetSystemRegistry("BuildLabEx")
End Function

Function GetOSProductName
    GetOSProductName = GetSystemRegistry("ProductName")
End Function

Function GetOSCurrentVersion
    GetOSCurrentVersion = GetSystemRegistry("CurrentVersion")
End Function

Function GetOSCurrentMajorVersionNumber
    GetOSCurrentMajorVersionNumber = GetSystemRegistry("CurrentMajorVersionNumber")

    If Err.Number <> 0 Then
        Err.Clear
        Dim Version : Version = GetOSCurrentVersion
        If Err.Number = 0 Then
            Dim arr : arr = Split(Version, ".")
            GetOSCurrentMajorVersionNumber = arr(0)
        End If
    End If
End Function

Function GetOSCurrentMinorVersionNumber
    GetOSCurrentMinorVersionNumber = GetSystemRegistry("CurrentMinorVersionNumber")

    If Err.Number <> 0 Then
        Err.Clear
        Dim Version : Version = GetOSCurrentVersion
        If Err.Number = 0 Then
            Dim arr : arr = Split(Version, ".")
            If UBound(arr) > 0 Then
                GetOSCurrentMinorVersionNumber = arr(1)
            End If
        End If
    End If
End Function

Function GetOSVersion
    Dim majorVersion : majorVersion = GetOSCurrentMajorVersionNumber
    Dim minorVersion : minorVersion = GetOSCurrentMinorVersionNumber
    Dim buildNumber : buildNumber = GetOSCurrentBuildNumber
    GetOSVersion = majorVersion & "." & minorVersion & "." & buildNumber
End Function

' execute the given command, collecting the results to an object
Function ExecuteWithResults(strCommand)

    Dim oResults, oExec, exitLoop

    Set oResults = new ExecResults
    oResults.StdOut = ""
    oResults.StdErr = ""
    oResults.ExitCode = Null
    exitLoop = False

    Set oExec = WshShell.Exec(strCommand)

    Do

        oResults.StdOut = oResults.StdOut & oExec.StdOut.ReadAll()
        oResults.StdErr = oResults.StdErr & oExec.StdErr.ReadAll()

        WScript.Sleep 10
    Loop While oExec.Status = 0 _
        Or Not oExec.StdOut.AtEndOfStream _
        Or Not oExec.StdErr.AtEndOfStream

    oResults.ExitCode = oExec.ExitCode

    Set ExecuteWithResults = oResults
End Function

Function ExecuteAndTraceWithResults(strCommand, tracer)
    Set ExecuteAndTraceWithResults = ExecuteAndTraceWithResultsWithErrorSuppression(strCommand, tracer, False)
End Function

Function ExecuteAndTraceWithResultsWithErrorSuppression(strCommand, tracer, suppressError)
    Dim oResults, commandElem, outputElem, errOutputElem, eventType, oTraceEvent
    
    ' log before executing command to capture command execution duration in wall time
    Set oTraceEvent = tracer.CreateEvent("INFO")
    Set commandElem = oTraceEvent.ownerDocument.CreateElement("ExecutingCommand")
    commandElem.appendChild(oTraceEvent.ownerDocument.CreateTextNode(strCommand))
    With oTraceEvent.appendChild(oTraceEvent.ownerDocument.CreateElement("ExecuteAndTraceWithResults"))
        .appendChild(commandElem)
    End With
    tracer.TraceEvent oTraceEvent 
    
    Set oResults = ExecuteWithResults(strCommand)
    Set ExecuteAndTraceWithResultsWithErrorSuppression = oResults

    If oResults.ExitCode = 0 Or suppressError = True Then 
        eventType = "INFO" 
    Else 
        eventType = "ERROR"
    End If

    Set oTraceEvent = tracer.CreateEvent(eventType)

    Set outputElem = oTraceEvent.ownerDocument.CreateElement("Output")
    If Not IsNull(oResults.StdOut) Then outputElem.appendChild(oTraceEvent.ownerDocument.CreateTextNode(CStr(oResults.StdOut)))
    Set errOutputElem = oTraceEvent.ownerDocument.CreateElement("ErrorOutput")
    If Not IsNull(oResults.StdErr) Then errOutputElem.appendChild(oTraceEvent.ownerDocument.CreateTextNode(CStr(oResults.StdErr)))

    With oTraceEvent.appendChild(oTraceEvent.ownerDocument.CreateElement("ExecuteAndTraceWithResults"))
        .setAttribute "ExitCode", oResults.ExitCode
        .appendChild(outputElem)
        .appendChild(errOutputElem)
    End With

    tracer.TraceEvent oTraceEvent
End Function

Function GetWinPAVersion(WshShell, FSO)
    Dim waGuestExePath

    waGuestExePath = WshShell.ExpandEnvironmentStrings("%SystemRoot%")
    waGuestExePath = FSO.BuildPath(waGuestExePath, "\OEM\WaGuest.exe")

    If (FSO.FileExists(waGuestExePath)) Then
        GetWinPAVersion = FSO.GetFileVersion(waGuestExePath)
    Else
        GetWinPAVersion = "0.0.0"
    End If
End Function

Class ExecResults
    Dim StdOut
    Dim StdErr
    Dim ExitCode
End Class

Function CreateTempFile(FSO)
    Dim folder, file
    Set folder = FSO.GetSpecialFolder(TemporaryFolder)
    file = FSO.GetTempName    
    CreateTempFile = FSO.BuildPath(folder, file)
End Function

Private Function ReadTempFile(FSO, file)
    Dim stream
    Dim str
    str = Null
    Set stream = FSO.OpenTextFile(file, ForReading, False)
    If Not stream.AtEndOfStream Then
        str = stream.ReadAll()
    End If
    stream.Close
    FSO.DeleteFile file
    ReadTempFile = str
End Function

Function GetScriptObject(WScript, scriptPath, componentId)
    Dim FSO, scriptDir
    Set FSO = CreateObject("Scripting.FileSystemObject")
    scriptDir = FSO.GetParentFolderName(WScript.ScriptFullName)
    Set GetScriptObject = GetObject("script:" & FSO.BuildPath(scriptDir, scriptPath) & "#" & componentId)
End Function

Function TraceError(objTrace, message)
    TraceError = TraceErrorAs("ERROR", objTrace, message, True)
End Function

Function TraceErrorWithoutClear(objTrace, message)
    TraceErrorWithoutClear = TraceErrorAs("ERROR", objTrace, message, False)
End Function

Function TraceErrorAs(eventType, objTrace, message, clearError)
    Dim oTraceEvent
    TraceErrorAs = Err.number
    If Err.number <> 0 Then
        Set oTraceEvent = objTrace.CreateEvent(eventType)
        With oTraceEvent.appendChild(oTraceEvent.ownerDocument.createElement("UnhandledError"))
            With .appendChild(oTraceEvent.ownerDocument.createElement("Message"))
                .text = message
            End With
            With .appendChild(oTraceEvent.ownerDocument.createElement("Number"))
                .text = Err.number
            End With
            With .appendChild(oTraceEvent.ownerDocument.createElement("Description"))
                .text = Err.Description
            End With
            With .appendChild(oTraceEvent.ownerDocument.createElement("Source"))
                .text = Err.Source
            End With
        End With
        objTrace.TraceEvent oTraceEvent

        If clearError = True Then
            Err.Clear
        End If
    End If
End Function

Sub LogDebug(objTrace, name, message)
    Log objTrace, "DEBUG", name, message
End Sub

Sub LogInfo(objTrace, name, message)
    Log objTrace, "INFO", name, message
End Sub

Sub LogWarn(objTrace, name, message)
    Log objTrace, "WARN", name, message
End Sub

Sub LogError(objTrace, name, message)
    Log objTrace, "ERROR", name, message
End Sub

Sub Log(objTrace, level, name, message)
    Dim oTraceEvent : Set oTraceEvent = objTrace.CreateEvent(level)
    With oTraceEvent.appendChild(oTraceEvent.ownerDocument.createElement(name))
        .text = message
    End With
    objTrace.TraceEvent oTraceEvent
End Sub

Function LeftPad(strText, intLen, chrPad)
    'LeftPad( "1234", 7, "x" ) = "xxx1234"
    'LeftPad( "1234", 3, "x" ) = "234"
    LeftPad = Right( String( intLen, chrPad ) & strText, intLen )
End Function 

' by first trying to get the drive letter by device path and then trying to get 
' the drive letter by volume label (to support GPT)
Function GetResourceDrive(tracer)
    Dim oResults, cachedDrive

    cachedDrive = GetRegistryValue(tracer, AZURE_PROVISIONING_KEY_PATH, "ResourceDrive", "REG_SZ", True)
    If IsNull(cachedDrive) Then
        Set oResults = ExecuteAndTraceWithResults("%SystemRoot%\OEM\FindVolume.exe /path \device\Harddisk1\Partition1", tracer)
        If oResults.ExitCode <> 0 Then
            Set oResults = ExecuteAndTraceWithResults("%SystemRoot%\OEM\FindVolume.exe /label ""Temporary Storage"" ", tracer)
            If oResults.ExitCode <> 0 Then
                'unknown failure
                Err.Raise vbObjectError + ERROR_NORESOURCEDRIVE, "Utility.vbs", "Failed to locate the resource drive as expected"
            End If
        End If
        cachedDrive = Trim(Replace(oResults.StdOut, vbCrLf, ""))

        SetRegistryValue tracer, AZURE_PROVISIONING_KEY_PATH, "ResourceDrive", "REG_SZ", cachedDrive, True
    End If

    GetResourceDrive = cachedDrive
End Function

Sub SetCacheValue(tracer, name, regType, value)
    SetRegistryValue tracer, AZURE_PROVISIONING_KEY_PATH, name, regType, value, True
End Sub

Function GetCacheValue(tracer, name, regType)
    GetCacheValue = GetRegistryValue(tracer, AZURE_PROVISIONING_KEY_PATH, name, regType, True)
End Function

' Get Kvp registry value
Function GetKvpRegistry(strValueName)
	On Error Resume Next

	Dim valueName : valueName =  "HKEY_LOCAL_MACHINE\" & KVP_REGISTRY_KEY_PATH & "\" & strValueName
   	Dim strValue : strValue = WshShell.RegRead(valueName) 
	
	GetKvpRegistry = strValue
End Function

' Set Kvp registry
Sub SetKvpRegistry(strValueName, strValue, tracer)
	On Error Resume Next
	
	Dim valueName : valueName =  "HKEY_LOCAL_MACHINE\" & KVP_REGISTRY_KEY_PATH & "\" & strValueName
   	WshShell.RegWrite valueName, strValue, "REG_SZ"
    
    If TraceError(tracer, "SetKvpRegistry: Set registry " & strValueName & " failed. Registry value should be: " & strValue) = 0 Then
        TraceKvpStatus tracer, strValueName, "INFO", strValue, "SetKvpRegistry"
    End If
End Sub

' Trace Kvp status
Sub TraceKvpStatus(tracer, kvpRegistry, strEvent, strStatus, strElementName)
    Dim oTraceEvent : Set oTraceEvent = tracer.CreateEvent(strEvent)
    With oTraceEvent.appendChild(oTraceEvent.ownerDocument.createElement(strElementName))
        With .appendChild(oTraceEvent.ownerDocument.createElement(kvpRegistry))
            .setAttribute "Message", strStatus
        End With
    End With
    tracer.TraceEvent oTraceEvent
End Sub

Class HttpRequest
    Public verb
    Public url
    Public headers
    
    ' request timeout values, in milliseconds
    Public resolveTimeout
    Public connectTimeout 
    Public sendTimeout
    Public receiveTimeout
    
    Private Sub Class_Initialize
        verb = Null
        url = Null
        Set headers = Nothing
        resolveTimeout = 0
        connectTimeout = 10*1000
        sendTimeout = 30*1000
        receiveTimeout = 30*1000
    End Sub
    
    Private Sub Class_Terminate
    
    End Sub
End Class

' Documentation for Msxml2.ServerXMLHTTP.3.0 timeouts: https://msdn.microsoft.com/en-us/library/ms760403(v=vs.85).aspx
'
' timeout argument values are in seconds
Function CreateHttpRequest(verb, url, headers, resolveTimeout, connectTimeout, sendTimeout, receiveTimeout, tracer)
    Dim http

    Set http = New HttpRequest
    http.verb = verb
    http.url = url
    http.resolveTimeout = resolveTimeout*1000
    http.connectTimeout = connectTimeout*1000
    http.sendTimeout = sendTimeout*1000
    http.receiveTimeout = receiveTimeout*1000

    If Not (IsNull(headers) Or IsEmpty(headers) Or (headers Is Nothing)) Then
        Set http.headers = headers
    End If

    Set oTraceEvent = tracer.CreateEvent("INFO")
    With oTraceEvent.appendChild(oTraceEvent.ownerDocument.createElement("HttpRequest"))
        .setAttribute "verb", verb
        .setAttribute "url", url
    End With
    tracer.TraceEvent oTraceEvent

    Set CreateHttpRequest = http
End Function

Function IsRetriableHr(hr)
    ' Client HTTP library error codes that should be retried.
    Const ERROR_INTERNET_CONNECTION_ABORTED = &H80072EFE&
    Const ERROR_TIMEOUT = &H800705B4&
    Const ERROR_INTERNET_TIMEOUT = &H80072EE2&
    Const ERROR_INTERNET_CANNOT_CONNECT = &H80072EFD&
    Const ERROR_INTERNET_NAME_NOT_RESOLVED = &H80072EE7&
    Const ERROR_INTERNET_CONNECTION_RESET = &H80032EFF&
    Const ERROR_WINHTTP_INVALID_SERVER_RESPONSE = &H80072F78&

    If hr >= 0 Then
        IsRetriableHr = False 'SUCCESS HRESULTs do not need retrying
    Else
        Select Case hr
            Case ERROR_INTERNET_CONNECTION_ABORTED, _
                 ERROR_TIMEOUT, _
                 ERROR_INTERNET_TIMEOUT, _
                 ERROR_INTERNET_CANNOT_CONNECT, _
                 ERROR_INTERNET_NAME_NOT_RESOLVED, _
                 ERROR_INTERNET_CONNECTION_RESET, _
                 ERROR_WINHTTP_INVALID_SERVER_RESPONSE
                IsRetriableHr = True
            Case Else
                IsRetriableHr = False
        End Select
    End If
End Function

Sub TraceResponse(http, tracer, isXml)
    Dim oTraceEvent
    Set oTraceEvent = tracer.CreateEvent("INFO")
    
    Dim httpResponse : Set httpResponse = oTraceEvent.ownerDocument.createElement("HttpResponse")
    httpResponse.setAttribute "statusCode", http.status
    
    If http.status = 200 Then
        Dim responseBody
        Set responseBody = oTraceEvent.ownerDocument.createElement("ResponseBody")
        
        If isXml Then
            Dim node, xml
            Set xml = http.responseXml
            
            If (Not xml Is Nothing) And (Not xml.documentElement Is Nothing) Then
                Set node = xml.documentElement.cloneNode(true)
                responseBody.appendChild(node)
            End If
        Else
            responseBody.Text = http.responseText
        End If
        
        httpResponse.appendChild(responseBody)
    End If
    
    oTraceEvent.appendChild(httpResponse)
    
    tracer.TraceEvent oTraceEvent
End Sub

Class RoleCertificate
    Public name
    Public tmpFile
    Public thumbprint
    Public storeName
    Public storeLocation
    
    Private Sub Class_Initialize
        tmpFile = Null
        thumbprint = Null
        storeName = Null
        storeLocation = Null
    End Sub
    
    Private Sub Class_Terminate
    
    End Sub
End Class

Class OfflinedFeatures
    Private Sub Class_Initialize
        Tag = ""
        Flags = 0
    End Sub

    Private Sub Class_Terminate
    End Sub

    Public Tag
    Public Flags
End Class

Function BuildExecuteString(callerName, procName, procArgStr, isFunction, returnValueIsObj)
    Dim executeString : executeString = procName & "(" & procArgStr & ")"
    If isFunction = True Then
        executeString = callerName & " = " & executeString
        If returnValueIsObj = True Then
            executeString = "Set " & executeString
        End If
    Else
        executeString = "Call " & executeString
    End If
    BuildExecuteString = executeString
End Function

Function InstrumentProcedure(procName, procArgStr, isFunction, returnValueIsObj, kvpKeyName, tracer)
    Dim offlinedFlags
    Dim executeString, executeArgs
    
    Set offlinedFlags = New OfflinedFeatures

    executeArgs = "procName, procArgStr, isFunction, returnValueIsObj, kvpKeyName, tracer, 0, offlinedFlags"
    executeString = BuildExecuteString("InstrumentProcedure", "InstrumentProcedureWithOfflinedFeatures", executeArgs, isFunction, returnValueIsObj)
    Execute executeString
End Function

' Profiles a procedure's execution (subroutine or function), capturing its execution time and error code 
' via the KVP channel under the specified registry KVP key name
' Value of the KVP entry is "Called=<call-time>;Returned=<return-time>;ErrorCode=<error-code>"
Function InstrumentProcedureWithOfflinedFeatures(procName, procArgStr, isFunction, returnValueIsObj, kvpKeyName, tracer, procFlag, offlinedFlags)
    Dim procRef, executeString
    Dim procErrorCode, procErrorMsg, procErrorSrc
    Dim callTime, returnTime
    Dim kvpValue, oTraceEvent
    Dim isOfflined, origProcName

    On Error Resume Next

    origProcName = procName

    isOfflined = (procFlag <> 0) And ((procFlag And offlinedFlags.Flags) = procFlag)
    If isOfflined = True And isFunction = True Then
        procName = procName & "Offlined"
    End If

    ' Verify that the referred-to procedure exists
    Set procRef = GetRef(procName)
    If TraceError(tracer, "InstrumentProcedureWithOfflinedFeatures: Failed to obtain reference to procedure '" & procName & "'") <> 0 Then
        Exit Function
    End If

    ' Build-up execution string
    executeString = BuildExecuteString("InstrumentProcedureWithOfflinedFeatures", procName, procArgStr, isFunction, returnValueIsObj)

    ' Capture current time in UTC before procedure is executed
    callTime = tracer.GetCurrentTime()

    procErrorCode = 0
    If isFunction = True Or isOfflined <> True Then
        Execute executeString

        ' Capture any error that occurs during execution so it can be returned to the caller
        procErrorCode = Err.Number
    End If

    If isOfflined = True Then
        Set oTraceEvent = tracer.CreateEvent("INFO")
        With oTraceEvent.appendChild(oTraceEvent.ownerDocument.createElement("ProcedureOfflined"))
            .setAttribute "Procedure", origProcName
            .setAttribute "Flag", procFlag
        End With
        tracer.TraceEvent oTraceEvent
    End If

    If procErrorCode <> 0 Then
        procErrorMsg = Err.Description
        procErrorSrc = Err.Source
        TraceError tracer, "InstrumentProcedureWithOfflinedFeatures: Failed to execute '" & executeString & "'. Will raise error to caller"
    End If

    ' Capture current time in UTC now that procedure has returned
    returnTime = tracer.GetCurrentTime()

    ' Generate and capture value in KVP channel
    kvpValue = "Called=" & callTime & ";Returned=" & returnTime & ";ErrorCode=" & procErrorCode
    
    SetKvpRegistry kvpKeyName, kvpValue, tracer

    ' If an error occurred during procedure execution, raise it for the caller
    If procErrorCode <> 0 Then
        Err.Raise procErrorCode, procErrorSrc, procErrorMsg
    End If
End Function

' This function executes a RegWrite and sets the data of the specified key and value
' When the RegType is REG_MULTI_SZ, it uses REG ADD to set the value
' For REG_MULTI_SZ, the data input is an array of values
' For REG_DWORD and REG_QWORD, the data input is a long
' For all else the data input is a string (no support for BINARY yet)
Sub SetRegistryValue(objTrace, key, value, regType, data, suppressError)
    On Error Resume Next

    Dim oTraceEvent, registryValueString
    Dim lError, traceCategory, displayString

    Err.Clear

    If regType = "REG_MULTI_SZ" Then
        Dim strCommand, line, oResults, escapedData, strData

        strData = Join(data, "\0")
        escapedData = EscapeBatch(strData)

        strCommand = "REG ADD " & QuoteString(key) _
            & " /v " & QuoteString(value) _
            & " /t " & regType _
            & " /d " & QuoteString(escapedData) _
            & " /f"

        Set oResults = ExecuteAndTraceWithResultsWithErrorSuppression(strCommand, objTrace, suppressError)
        lError = oResults.ExitCode

        displayString = Join(data, "\0")
    Else
        WshShell.RegWrite key & "\" & value, data, regType
        lError = Err.Number

        displayString = data
    End If

    If suppressError = True Or lError = 0 Then
        traceCategory = "INFO"
    Else
        traceCategory = "ERROR"
    End If

    registryValueString = "key: " & key & ", value: " & value & ", type: " & regType & ", data: " & displayString
    Set oTraceEvent = objTrace.CreateEvent(traceCategory)
    With oTraceEvent.appendChild(oTraceEvent.ownerDocument.createElement("SetRegistryValue"))
        .Text = registryValueString
        If lError <> 0 Then
            .setAttribute "Error", lError
        End If
    End With
    objTrace.TraceEvent oTraceEvent
End Sub

' This function executes a RegRead and returns the data of the specified key and value
' For REG_MULTI_SZ, the output is an array of values
' For REG_DWORD and REG_QWORD, the output is a long
' For all else the output is a string (no support for BINARY yet)
Function GetRegistryValue(objTrace, key, value, regType, suppressError)

    On Error Resume Next

    Dim oTraceEvent, registryValueString
    Dim lError, traceCategory, displayString
    Dim arr

    GetRegistryValue = Null
    Err.Clear

    If regType = "REG_MULTI_SZ" Then
        arr = WshShell.RegRead(key & "\" & value)
        lError = Err.Number
        If Not IsNull(arr) Then
            displayString = Join(arr, "\0")
        End If
        GetRegistryValue = arr
    Else
        GetRegistryValue = WshShell.RegRead(key & "\" & value)
        lError = Err.Number
        displayString = CStr(GetRegistryValue)
    End If

    ' Clear errors for non-existent registries
    Err.Clear

    If suppressError = True Or lError = 0 Then
        traceCategory = "INFO"
    Else
        traceCategory = "ERROR"
    End If

    Set oTraceEvent = objTrace.CreateEvent(traceCategory)
    With oTraceEvent.appendChild(oTraceEvent.ownerDocument.createElement("GetRegistryValue"))
        If lError = ERROR_FILE_NOT_FOUND Then
            .Text = "Failed to find registry value " & key & "\" & value
        ElseIf lError <> 0 Then
            .Text = "Failed to read registry value " & key & "\" & value
        Else
            registryValueString = "key: " & key & ", value: " & value & ", data: " & displayString
            .Text = registryValueString
        End If
    End With
    objTrace.TraceEvent oTraceEvent
End Function

' This function executes a RegDelete command for the specified registry key.
Sub DeleteRegistry(objTrace, registryPath, registryKeyName, suppressError)

    Dim oTraceEvent, traceType

    On Error Resume Next

    If IsNull(registryKeyName) Or Len(registryKeyName) = 0 Then
        WshShell.RegDelete registryPath & "\"
    Else
        WshShell.RegDelete registryPath & "\" & registryKeyName
    End If 
   
    If Err.Number = 0 Then
        Set oTraceEvent = objTrace.CreateEvent("INFO")
        With oTraceEvent.appendChild(oTraceEvent.ownerDocument.createElement("DeleteRegistry"))
            .Text = "path: " & registryPath & ", key name: " & registryKeyName
        End With
        objTrace.TraceEvent oTraceEvent
    Else
        If suppressError = True Then
            traceType = "INFO"
        Else
            traceType = "ERROR"
        End If
        TraceErrorAs traceType, objTrace, "DeleteRegistry: Failed to delete key '" & registryPath & "\" & registryKeyName & "'.", True
    End If
End Sub

Function EscapeBatch(str)
    EscapeBatch = Replace(str, "%", "%%")
    EscapeBatch = Replace(EscapeBatch, "^", "^^")
    EscapeBatch = Replace(EscapeBatch, "&", "^&")
    EscapeBatch = Replace(EscapeBatch, "<", "^<")
    EscapeBatch = Replace(EscapeBatch, ">", "^>")
    EscapeBatch = Replace(EscapeBatch, "|", "^|")
End Function

Function EscapeXml(str)
    EscapeXml = Replace(str, "&", "&amp;")
    EscapeXml = Replace(EscapeXml, """", "&quot;")
    EscapeXml = Replace(EscapeXml, "'", "&apos;")
    EscapeXml = Replace(EscapeXml, "<", "&lt;")
    EscapeXml = Replace(EscapeXml, ">", "&gt;")
End Function

Function QuoteString(str)
    QuoteString = """" & CStr(str) & """"
End Function

Function BoolToString(bool)
    If bool = True Then
        BoolToString = "True"
    Else
        BoolToString = "False"
    End If
End Function

Function GetNumberOfLogicalProcessors
    On Error Resume Next

    GetNumberOfLogicalProcessors = WshShell.Environment("PROCESS").Item("NUMBER_OF_PROCESSORS")
End Function

Function IsValidFileName(str)
    Dim oRegExp

    If IsNull(str) Or IsEmpty(str) Then
        IsValidFileName = False
    Else
        Set oRegExp = New RegExp
        oRegExp.Pattern = "^[\x20\x21\x23\x24\x26-\x29\x2B-\x2E\x30-\x39\x3B\x3D\x40-\x5B\x5D-\x7B\x7D-\xFF]{1,255}$"
        oRegExp.IgnoreCase = True
        IsValidFileName = oRegExp.Test(str)
    End If
End Function

' Writes networking data (ipconfig, route, arp) to log via paramterized Tracing object
Sub LogNetworkingData(tracer)
    Dim configurationPass : configurationPass = Me.WScript.Arguments.Named("ConfigurationPass")

    Dim kvp_cmd_ipconfig           : kvp_cmd_ipconfig           = configurationPass & "_PA_cmd_ipconfig"
    Dim kvp_cmd_route              : kvp_cmd_route              = configurationPass & "_PA_cmd_route"
    Dim kvp_cmd_arp                : kvp_cmd_arp                = configurationPass & "_PA_cmd_arp"
    Dim kvp_cmd_sc_dhcp            : kvp_cmd_sc_dhcp            = configurationPass & "_PA_cmd_sc_dhcp"

    ExecuteAndTraceWithResults "ipconfig.exe /all", tracer
    SetKvpRegistry kvp_cmd_ipconfig, "[" & g_Trace.GetCurrentTime() & "] ipconfig.exe /all", g_Trace

    ExecuteAndTraceWithResults "route.exe print", tracer
    SetKvpRegistry kvp_cmd_route, "[" & g_Trace.GetCurrentTime() & "] route.exe print", g_Trace

    ExecuteAndTraceWithResults "arp.exe /a", tracer
    SetKvpRegistry kvp_cmd_arp, "[" & g_Trace.GetCurrentTime() & "] arp.exe /a", g_Trace

    ExecuteAndTraceWithResults "sc.exe query dhcp", tracer
    SetKvpRegistry kvp_cmd_sc_dhcp, "[" & g_Trace.GetCurrentTime() & "] sc.exe query dhcp", g_Trace
End Sub