Power Shell
Zuletzt geändert: 14.06.2023 11:29

PowerShell aus einem Menüpunkt starten #

Menüpunkte lassen sich innerhalb EULANDAs sehr einfach als Actions in der SQL-Registry zufügen. Dort wiederum lässt sich als CommandText ein VbScript hinterlegen, welches wiederum ein externes PowerShell-Skript aufrufen kann.

Hierzu legt man in der SQL-Registry einen Ordner an unter:

ROOT\OBJECTS\DATAOBJECTS\Eulanda.Adresse\Actions

Über die Rechte Mausoption auf dem Actions-Ordner dann auf Neu einen neuen Schlüssel anlegen. In der Auswahl wählt man iScript-Menübefehl aus, geben Sie einen Namen ein, z.B. user.MeinPowerShellAufruf. Auf der rechten Seite werden dann alle Felder angelegt. Das VbScript wird in den CommandText gelegt.

Das eigentliche Skript, welches an Ihre Funktion angepasst werden muss, ist nur der Abschnitt Main. Aber sie fügen immer das Komplette Skript wie angegeben ein und ändern dann den Main-Abschnitt nach Ihren Bedürfnissen ab.

Hinweis

Bei Nutzung des Befehls Clear-Host gibt es eine Exception, wenn das PowerShell-Skript von EULANDA gestartet wird. Dies hängt damit zusammen, dass es, wenn die Ausgabe über eine Pipe umgeleitet wird, kein Konsolen-Host verfügbar ist. Notwendig ist die Umleitung, um die Meldungen und Fehler in EULANDA anzeigen zu können.

sub main(id)
    Dim command, strOutput, strError
    
    command = CommandScript("C:\users\johndoe\desktop\pwtest.ps1")
    command = Append(command, "-udl", """X:\Eulanda_1 MyCompany.udl""")
    command = Append(command, "-deliveryId", id)

    call Powershell(command, true, 30, strOutput, strError)
end sub

Überschreiben Sie den kompletten Inhalt und fügen Sie folgendes Skript ein:

option explicit


Sub Popup(sec)
    Dim WshShell, fso, tempFolder, tempFilePath, file
    Dim vbscriptCode

    if sec > 0 then
        vbscriptCode = "Dim message, WshShell" & vbCrLf & _                        
            "Set WshShell = CreateObject(""WScript.Shell"")" & vbCrLf & _
            "message = ""Bitte warten, die Verarbeitung kann bis zu '" & sec & "' Sekunden dauern...""" & vbCrLf & _
            "firstRun = False" & vbCrLf & _
            "Do" & vbCrLf & _
            "    If Not firstRun Then WshShell.Popup message, " & sec & " , ""Info-Popup"", 0" & vbCrLf & _
            "    WScript.Sleep 10" & vbCrLf & _
            "    firstRun = True" & vbCrLf & _
            "Loop While WshShell.AppActivate(""ShowPopup.vbs"")"

        Set fso = CreateObject("Scripting.FileSystemObject")
        tempFolder = fso.GetSpecialFolder(2)  ' 2 stands for the temporary folder
        If Not fso.FolderExists(tempFolder) Then
            fso.CreateFolder(tempFolder)
        End If
        tempFilePath = fso.BuildPath(tempFolder, "ShowPopup.vbs")

        Set file = fso.CreateTextFile(tempFilePath, True)
        file.Write vbscriptCode
        file.Close
        Set WshShell = CreateObject("WScript.Shell")
        WshShell.Run "wscript.exe " & tempFilePath, 0, false
    end if
End Sub



Function ConvertUmlaute(strInput)
    Dim strOutput
    strOutput = ""

    Dim i
    For i = 1 To Len(strInput)
        Dim character
        character = Mid(strInput, i, 1)

        Dim asciiValue
        asciiValue = AscW(character)

        Select Case asciiValue
            Case 8482
                character = "Ö"
            Case 381
                character = "Ä"
            Case 353
                character = "Ü"
            Case 8221
                character = "ö"
            Case 8222
                character = "ä"
            Case 129
                character = "ü"
            Case 225
                character = "ß"
            case 184
                character = "©"    
            case 169
                character = "®"    
        End Select        
        strOutput = strOutput & character
    Next

    ConvertUmlaute = strOutput
End Function


Function RemoveAnsiCodes(str)
    Dim regex

    Set regex = New RegExp
    regex.Pattern = "\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]"
    regex.Global = True
    RemoveAnsiCodes = regex.Replace(str, "")
End Function

Function TrimCrLf(ByVal str)
    ' Remove leading line breaks and spaces
    Do While Left(str, 2) = vbCrLf Or Left(str, 1) = vbLf Or Left(str, 1) = " "
        If Left(str, 2) = vbCrLf Then
            str = Mid(str, 3)
        ElseIf Left(str, 1) = vbLf Then
            str = Mid(str, 2)
        Else
            str = Trim(Mid(str, 2))
        End If
    Loop

    ' Remove trailing line breaks and spaces
    Do While Right(str, 2) = vbCrLf Or Right(str, 1) = vbLf Or Right(str, 1) = " "
        If Right(str, 2) = vbCrLf Then
            str = Left(str, Len(str) - 2)
        ElseIf Right(str, 1) = vbLf Then
            str = Left(str, Len(str) - 1)
        Else
            str = Trim(Left(str, Len(str) - 1))
        End If
    Loop

    TrimCrLf = str
End Function


Function GetOutput(Message)
    Dim output
    output = ""

    Dim line, lines
    lines = Split(Message, vbCrLf)

    For Each line In lines
        ' Check that the line does not start with "Write-Error:"
        If (Left(line, 12) <> "Write-Error:") and  (Left(line, 14) <> "ERROR Message:") Then
            output = output & vbCrLf & Trim(line)            
        End If
    Next

    output = TrimCrLf(output)
    GetOutput = output
End Function


Function GetError(Message)
    Dim errors
    errors = ""

    Dim line, lines
    lines = Split(Message, vbCrLf)

    For Each line In lines
        If Left(line, 12) = "Write-Error:" Then
            errors = errors & vbCrLf & Trim(Mid(line, 13))
        elseif Left(line, 14) = "ERROR Message:" Then 
            errors = errors & vbCrLf & Trim(Mid(line, 15))
        End If
    Next

    errors = TrimCrLf(errors)
    GetError = errors
End Function



Function Append(strBase, strAppend1, strAppend2)
    If (strBase <> "") And (Right(strBase, 1) <> " ") Then
        strBase = strBase & " "
    End If
    strBase = strBase & strAppend1
    If strAppend2 <> "" Then
        strBase = strBase & " " & strAppend2
    End If
    Append = strBase
End Function


function CommandScript(strScript)
    Const quotes = """"
    dim strBase
    strBase = "pwsh.exe -windowstyle hidden -nologo -noprofile -ExecutionPolicy Bypass -File"
    CommandScript = Append("", strBase, quotes & strScript & quotes)
end function


function CommandInline(str)
    dim strBase
    strBase = "pwsh.exe -windowstyle hidden -nologo -noprofile -ExecutionPolicy Bypass --Command"
    Command = strBase & " " & str
    ' sample to call this sub procedure
    ' CommandInline("try {throw 'This is an exception, ut with Umlaut ÖÄÜ!'} catch {Write-Output $_.Exception.Message; exit 99}")
end function


Function IsPwshInstalled()
    Dim WshShell, strPwsh

    Set WshShell = CreateObject("WScript.Shell")
    On Error Resume Next
    strPwsh = WshShell.RegRead("HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\pwsh.exe\")
    On Error GoTo 0
    Dim fso: Set fso = CreateObject("Scripting.FileSystemObject")
    If fso.FileExists(strPwsh) Then
        IsPwshInstalled = True
    Else
        IsPwshInstalled = False
    End If
End Function


dim top

sub PowerShell(CommandLine, MsgOnSuccess, sec, ByRef strOutput, ByRef strError)
    ' 1. paramater = call with pwsh, standard switches and script as well as script parameters
    ' 2. parameter = show no message if successful
    ' 3. parameter = Wait seconds for the popup. Draws attention to the fact that something is still happening. At 0 no popup comes

    ' Popup is called in backgroud to inform the slow loading process of powershell
    Popup(sec)

    Dim fso, WshShell, tempFileName, file, line, strCommon, result
    Const TemporaryFolder = 2
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set WshShell = CreateObject("WScript.Shell")

    tempFileName = fso.GetSpecialFolder(TemporaryFolder) & "\" & fso.GetTempName                       
    CommandLine = "%comspec% /c """ & CommandLine & " > " & tempFileName & " 2>&1" & """"
    ' Use RUN, because it hides the window, side effect it did not support stdErr
    Top = top & "Now22 = " & Now
    result = WshShell.Run(CommandLine, 0, true)


    ' Read the output from the temp file
    Set file = fso.OpenTextFile(tempFileName, 1)
    strOutput = ""
    strError = ""
    Do Until file.AtEndOfStream
    strOutput = strOutput & file.ReadLine() & vbCrLf
    Loop
    file.Close()
    fso.DeleteFile(tempFileName)
    strOutput = ConvertUmlaute(strOutput)
    strOutput = RemoveAnsiCodes(strOutput)
    strError = GetError(strOutput)
    strOutput = GetOutput(strOutput)
    if strOutput <> "" then strCommon = "Output:" & vbCrLf & strOutput else strCommon = ""         
        if strError <> "" then strCommon = Trim(strCommon & vbCrLf & vbCrlf & "Error(s):" & vbCrlf & strError)
     
    if result <> 0 Then         
                ellib.showMessage(strCommon & vbCrLf & vbCrLf & "Errorcode: " & result )
    Else
        If MsgOnSuccess Then
            ellib.showMessage(strOutput)
        End if
    end if
end sub


sub main(id)
    Dim command, strOutput, strError
    
    command = CommandScript("C:\users\johndoe\desktop\pwtest.ps1")
    command = Append(command, "-udl", """X:\Eulanda_1 MyCompany.udl""")
    command = Append(command, "-deliveryId", id)

    call Powershell(command, true, 30, strOutput, strError)
end sub


Main(1)

Die Sub-Routine main, ist diejenige, die man nur anpassen muss. Legen Sie das PowerShell-Skript auf den Desktop und passen Sie dessen Namen an. Hier im Beispiel heißt es psTest.ps1. Mit Append lassen sich an den String command jeweils Parameter Pärchen anfügen. Diese hängen von Ihrem Skript ab. Wird nur ein Switch benötigt, dann lassen sie in Append den zweiten Wert leer (zwei Anführungszeichen).

Der Powershell-Aufruf erfolgt über einen Call-Aufruf. Der erste Parameter ist die vollständige Kommandozeile, der zweite besagt, dass bei Erfolg eine Messagebox angezeigt werden soll, und der dritte Parameter gibt ein Timeout an. Da der Aufruf einige Sekunden in Anspruch nehmen kann, wird eine Splash-Box im Hintergrund angezeigt, die innerhalb des PowerShell-Skripts mit Stop-Popup jederzeit abgeschaltet werden kann, spätestens eben nach dem Timeout.

Bei einem Fehler mit Fehlercode wird eine Message-Box angezeigt. Treten ansonsten Fehler oder Meldungen auf, erhält man diese nach dem Aufruf in den Variablen strOutput und strError.

Am Ende des Dokuments finden Sie eine entsprechende PowerShell-Datei zum Testen.

[cmdletbinding()]
Param(
    [string]$udl,
    [int]$deliveryId
)

function Start-Popup {
    param (
        [Parameter(Mandatory=$true)]
        [int]$sec
    )

    if ($sec -gt 0) {
        $vbscriptCode = @'
        Dim message, WshShell
        Set WshShell = CreateObject("WScript.Shell")
        message = "Bitte warten, die Verarbeitung kann bis zu '{0}' Sekunden dauern..."
        firstRun = False
        Do
            If Not firstRun Then WshShell.Popup message, {0} , "Info-Popup", 0
            WScript.Sleep 10
            firstRun = True
        Loop While WshShell.AppActivate("ShowPopup.vbs")

'@ -f $sec

        $tempFolder = [System.IO.Path]::GetTempPath()
        $tempFilePath = Join-Path -Path $tempFolder -ChildPath "ShowPopup.vbs"

        try {
            $vbscriptCode | Out-File -FilePath $tempFilePath -Force
            Start-Process -FilePath "wscript.exe" -ArgumentList $tempFilePath -WindowStyle Hidden
        } catch {
            Write-Error "Failed to create and run VBScript: $_"
        }

    }
}


function Stop-Popup {
    param ()

    $windowTitle = "Info-Popup"
    $process = Get-Process | Where-Object { $_.MainWindowTitle -eq $windowTitle }
    if ($process) {
        Stop-Process -Id $process.Id
    }
}


try {
    Clear-Host -ErrorAction SilentContinue
}
catch {
    <#
        When Clear-Host is called but the script is piped, Clear-Host
        returns an exception like 'Cannot bind argument to parameter 'Path'
        because it is an empty string'. This happens especially when the ps1
        script is called from Eulanda, because here the output is always
        read back via pipe. Clear-Host should be in a separate
        try/catch block in any case.
    #>
}

# Alternatively, an identical function for displaying a popup with start popup is available
# Start-Popup -sec 5

write-error "First error"

Write-Host "Udl: $udl"
Write-Host "DeliveryId: $deliveryId"
Write-Verbose "Verbose is on"

start-sleep -Seconds 3

try {
    write-error "This is umlaut test: ÖÄÜ"
}
catch {
    $ErrorMessage = $_.Exception.Message
    # Try to write error messages
    # Write-Error $ErrorMessage
}
finally {
	<#
  		It can't hurt to call the command Stop-Popup in the finallly block, 
  		especially in case of an exception the popup will be hidden immediately. 
  		In addition, call the command before the first dialog, so that the 
  		popup is then also hidden.
	#>
	Stop-Popup
}

# If an exit code > 0 is set, the error is always displayed within EULANDA
# Exit 99