SAP GUI Scripting Rekorder mit Windows PowerShell

Das SAP GUI Scripting API ist eine Schnittstelle zur Automatisierung von Benutzerinteraktionen mit dem SAP GUI für Windows. Das SAP GUI Scripting kann das Leben der Anwender erheblich vereinfachen in dem sich immer wiederholende Arbeiten und Aufgaben einfach automatisiert werden können. Hinter dem SAP GUI Scripting steht ein Objektmodell das den SAP GUI abbildet. Über dieses Objektmodell kann auf fast alle Objekte des SAP GUI zugegriffen werden. Eine sehr gute Einführung in das SAP GUI Scripting ist hier zu finden.

Die im SAP-Standard verankerten Möglichkeiten nutzen VBScript als Scripting-Sprache. Und im SAP-Hinweis 592685 wird demonstriert wie mit Hilfe der angehängten Visual Basic Anwendung die Aktionen des Anwenders in SAP GUI aufgezeichnet werden können. Die Visual Basic Plattform ist jedoch veraltet und Ansätze mit anderen (aktuellen) Scripting-Sprachen sind nicht vorhanden, mit Ausnahme des Scripting Tracker. Aus diesem Grund habe ich einen Ansatz eines SAP GUI Scripting Rekorders in PowerShell, der aktuellen Windows Script-Sprache, erstellt. Mit dieser Basis ist es nun sehr einfach einen eigenen Rekorder zu erstellen. Hier der Code:

#-Begin-----------------------------------------------------------------

  #-Includes------------------------------------------------------------
    ."$PSScriptRoot\COM.ps1"

  #-Sub Main------------------------------------------------------------
    Function Main() {

      [Reflection.Assembly]::LoadFile("$PSScriptRoot\SAPFEWSELib.dll") > $Null

      $SapGuiAuto = Get-Object "SAPGUI"
      If ($SapGuiAuto -isnot [System.__ComObject]) {
        Break
      }

      $Application = Invoke-Method $SapGuiAuto "GetScriptingEngine"
      [SAPFEWSELib.GuiApplication]$Application =
        [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Application,
        [SAPFEWSELib.GuiApplicationClass])
      If ($Application -isnot [System.__ComObject]) {
        Break
      }

      $Connection = $Application.Children.Item(0)
      If ($Connection -eq $Null) {
        Break
      }
      Else {
        [SAPFEWSELib.GuiConnectionClass]$Connection =
          [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Connection,
          [SAPFEWSELib.GuiConnectionClass])
      }

      $Session = $Connection.Children.Item(0)
      If ($Session -eq $Null) {
        Break
      }
      Else {
        [SAPFEWSELib.GuiSession]$Session =
          [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Session,
          [SAPFEWSELib.GuiSessionClass])
      }

      $Session.Record = $True
      Register-ObjectEvent -InputObject $Session -EventName "Change" -SourceIdentifier "Action" > $Null

      While ($true) {
        Write-Host "Waiting for event..."
        $Event = Wait-Event -SourceIdentifier "Action" -Timeout 10
        If ($Event -eq $Null) {
          Write-Host "No event received for 10 seconds."
          Break
        }

        [SAPFEWSELib.GuiSession]$RecSession =
          [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($event.SourceArgs[0],
          [SAPFEWSELib.GuiSessionClass])
        #Ohne die folgende Zeile liefer PowerShell im Folgenden einen Fehler
        $Dummy = $RecSession | Format-List | Out-String

        [SAPFEWSELib.GuiComponent]$RecComponent =
          [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($event.SourceArgs[1],
          [SAPFEWSELib.GuiComponentClass])
        Write-Host ( $RecComponent | Format-List | Out-String )
        #Write-Host ( $RecComponent | Select -ExpandProperty "ID" )
        Write-Host "Type / Method / Parameter: " $event.SourceArgs[2]

        Remove-Event -SourceIdentifier "Action"
      }

      Unregister-Event -SourceIdentifier "Action"
      $Session.Record = $False

    }

  #-Main----------------------------------------------------------------
    Main

#-End-------------------------------------------------------------------

Zur einfacheren Nutzung des Component Object Models (COM) habe ich ein Include erstellt, mit dem dies möglich ist.

#-Begin-----------------------------------------------------------------

  #-Load assembly-------------------------------------------------------
    [Reflection.Assembly]::LoadWithPartialName("Microsoft.VisualBasic") > $Null

  #-Function Create-Object----------------------------------------------
    Function Create-Object {
      param([String] $objectName)
      try {
        New-Object -ComObject $objectName
      }
      catch {
        [Void] [System.Windows.Forms.MessageBox]::Show(
          "Can't create object", "Important hint", 0)
      }  
    }

  #-Function Get-Object-------------------------------------------------
    Function Get-Object {
      param([String] $objectName)
      [Microsoft.VisualBasic.Interaction]::GetObject($objectName)
    }

  #-Sub Free-Object-----------------------------------------------------
    Function Free-Object {
      param([__ComObject] $object)
      [Void] [System.Runtime.Interopservices.Marshal]::ReleaseComObject($object)
    }

  #-Function Get-Property-----------------------------------------------
    Function Get-Property {
      param([__ComObject] $object, [String] $propertyName)
      $objectType = [System.Type]::GetType($object)
      $objectType.InvokeMember($propertyName,
        [System.Reflection.Bindingflags]::GetProperty,
        $null, $object, $null)
    }

  #-Sub Set-Property----------------------------------------------------
    Function Set-Property {
      param([__ComObject] $object, [String] $propertyName, 
        $propertyValue)
      $objectType = [System.Type]::GetType($object)
      [Void] $objectType.InvokeMember($propertyName,
        [System.Reflection.Bindingflags]::SetProperty,
        $null, $object, $propertyValue)
    }

  #-Function Invoke-Method----------------------------------------------
    Function Invoke-Method {
      param([__ComObject] $object, [String] $methodName,
        $methodParameters)
      $objectType = [System.Type]::GetType($object)
      $output = $objectType.InvokeMember($methodName,
        "InvokeMethod", $NULL, $object, $methodParameters)
      if ( $output ) { $output }
    }

#-End-------------------------------------------------------------------

Weiterhin benötigt der Rekorder eine Interoperationsbibliothek für das SAP GUI Scripting. Mit Hilfe der Interop-Technik lassen sich alle klassischen, binär kompilierten Windows-Bibliotheken mit Wrappern versehen, so dass deren Programmfunktionen wie normale .NET-Programmfunktionen aufgerufen werden können. Entweder man erzeugt sich diesen Wrapper selbst mit TypLibImp, einem Tool aus dem .NET-Framework, oder man nutzt die Interoperationsbibliothek des NWBC.

Vom Ablauf her ist der Rekorder sehr einfach:

  • Es wird eine Verbindung zu einer SAP-Session hergestellt
  • Dann wird das Ereignis Change registriert
  • In einer Schleife, die nach 10 Sekunden ohne Ereignis beendet wird, werden die Informationen eines Ereignisses abgefangen
  • Im Objekt GuiComponent sind viele Informationen zu finden
  • Im dritten Argument des Ereignisses $event.SourceArgs[2] ist die Aktivität zu finden
SAP GUI Scripting Recorder in Windows PowerShell
SAP GUI Scripting Recorder in Windows PowerShell

Mit diesem Ansatz ist es nun sehr einfach möglich einen eigenen SAP GUI Scripting Rekorder zu implementieren.

Letzte Artikel von Stefan Schnell (Alle anzeigen)