PowerShell mit ABAP nutzen
PowerShell ist eine sehr leistungsfähige Skriptsprache. Sie wird im Standard seit Windows 7 ausgeliefert. Aktuell ist Windows 10 mit PowerShell 5 ausgestattet. Der SAP GUI für Windows bietet eine COM-Schnittstelle die von ABAP aus verwendet werden kann. Mittels dieser Schnittstelle, und einer Bibliothek von SAPIEN, besteht die Möglichkeit das PowerShell auf dem Frontend-Server von ABAP genutzt werden kann. In diesem Beitrag soll kurz eine Realisierung mit einigen Anwendungsfällen umrissen werden.
Als erstes beschaffen wir uns die notwendigen Bibliotheken von SAPIEN. Einfach im Bereich My Account, nach dem Login, Download auswählen und die Bibliotheken herunter laden.

Nach der Installation und Registrierung können diese sofort mit ABAP verwendet werden. Für die Registrierung einfach eine Konsole im Administratormodus öffnen und die folgenden Befehle ausführen.
C:\Windows\Microsoft.NET\Framework\v4.0.30319\regasm.exe "C:\Program Files (x86)\SAPIEN Technologies, Inc\ActiveXPowerShell V3\\ActiveXPoshV3.dll" /codebase
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\regasm.exe "C:\Program Files\SAPIEN Technologies, Inc\ActiveXPowerShell V3\ActiveXPoshV3.dll" /codebase
Hier als Beispiel der Aufruf aller installierten Services mit ihrem Status.
"-Begin-----------------------------------------------------------------
Program zPSTest.
"-Constants-----------------------------------------------------------
Constants:
OUTPUT_CONSOLE Type i Value 0,
OUTPUT_WINDOW Type i Value 1,
OUTPUT_BUFFER Type i Value 2
.
"-Variables-----------------------------------------------------------
Data:
PS Type OLE2_OBJECT,
Result Type i,
strResult Type String,
tabResult Type Table Of String,
cmd Type String
.
"-Main----------------------------------------------------------------
Create Object PS 'SAPIEN.ActiveXPoSHV3'.
Check sy-subrc = 0 And PS-Handle <> 0 Or PS-Type = 'OLE2'.
Call Method Of PS 'Init' = Result Exporting #1 = 0.
If Result <> 0.
Free Object PS.
Exit.
EndIf.
Call Method Of PS 'IsPowerShellInstalled' = Result.
If Result = 0.
Free Object PS.
Exit.
EndIf.
Set Property Of PS 'OutputMode' = OUTPUT_BUFFER.
cmd = `Get-WmiObject -class Win32_Service | `.
cmd = cmd && `Format-Table -property Name,State`.
Call Method Of PS 'Execute' Exporting #1 = cmd.
Call Method Of PS 'OutputString' = strResult.
Split strResult At cl_abap_char_utilities=>cr_lf
Into Table tabResult.
Loop At tabResult Into strResult.
Write: / strResult.
EndLoop.
Free Object PS.
"-End-------------------------------------------------------------------
In der Variablen cmd werden die PowerShell-Befehle übergeben, in diesem Fall die Ausgabe einer formatierten Tabelle mit Name und Status aller Services über das Windows Management Instrumentarium (WMI):
Get-WmiObject -class Win32_Service | Format-Table -property Name,State

Sollte der Aufruf nicht funktionieren, kann es sein, dass eine Standard-Sicherheitsregel den Aufruf blockiert. Zum Experimentieren können diese über den SAP Logon deaktiviert werden.

Um den kompletten Umfang der Bibliothek einfach nutzen zu können, hier eine entsprechende Klasse.
"-Begin-----------------------------------------------------------------
class Z_CL_ACTIVEXPOSHV3 definition
public
create public .
public section.
constants MC_OUTPUTCONSOLE type I value 0 ##NO_TEXT.
constants MC_OUTPUTWINDOW type I value 1 ##NO_TEXT.
constants MC_OUTPUTBUFFER type I value 2 ##NO_TEXT.
constants MC_TRUE type I value 1 ##NO_TEXT.
constants MC_FALSE type I value 0 ##NO_TEXT.
"! Loads the ActiveXPoshV3 library
"!
"! @parameter rv_result | 1 for success, otherwise 0
methods LOAD_LIB
returning
value(RV_RESULT) type I .
"! Frees the ActiveXPoshV3 library
methods FREE_LIB .
"! Executes stored OLE activities
methods FLUSH .
"! Clears the internal output buffer
"! when the OutputMode property is set to mc_OutputBuffer
methods CLEAR_OUTPUT .
"! Evaluates a PowerShell expression
"! If the expression returns an object this function returns -1,
"! otherwise 0. Output, if any, is not captured or redirected.
"!
"! @parameter iv_expression | PowerShell command
"!
"! @parameter rv_result | Result of the command
methods EVAL
importing
value(IV_EXPRESSION) type STRING
returning
value(RV_RESULT) type I .
"! Executes a PowerShell command or script
"! Output is directed according to the OutputMode property.
"! Variable assignments persist between calls.
"!
"! @parameter iv_command | PowerShell command or script
methods EXECUTE
importing
value(IV_COMMAND) type STRING .
"! Evaluates a PowerShell expression
"!
"! @parameter iv_expression | PowerShell command
"!
"! @parameter rv_result | Value as string
methods GET_VALUE
importing
value(IV_EXPRESSION) type STRING
returning
value(RV_RESULT) type STRING .
"! Initial call to instantiate a PowerShell engine
"! Required for any of the methods of this object to succeed.
"!
"! @parameter iv_load_profiles | Determines if your PowerShell profiles, if they exist, are executed
"!
"! @parameter rv_result | Returns 0 if successful, otherwise <> 0
methods INIT
importing
value(IV_LOAD_PROFILES) type I
returning
value(RV_RESULT) type I .
"! Checks if PowerShell is installed
"!
"! @parameter rv_result | Returns 1 if PowerShell is installed, otherwise 0
methods GET_IS_POWERSHELL_INSTALLED
returning
value(RV_RESULT) type I .
"! Gets the current output mode
"!
"! @parameter rv_result | 0 = OutputConsole, 1 = OutputWindow, 2 = OutputBuffer
methods GET_OUTPUTMODE
returning
value(RV_RESULT) type I .
"! Sets the current output mode
"!
"! @parameter iv_mode | 0 = OutputConsole, 1 = OutputWindow, 2 = OutputBuffer
methods SET_OUTPUTMODE
importing
value(IV_MODE) type I .
"! Delivers the content of the output buffer as a single string
"!
"! @parameter rv_result | Output buffer as string
methods GET_OUTPUTSTRING
returning
value(RV_RESULT) type STRING .
"! Gets the desired output width in characters
"! PowerShell output often gets truncated, wrapped or adjusted
"! corresponding to the width of a console window. Since there
"! is not necessarily a console window available, the default
"! is set to 80 characters width.
"!
"! @parameter rv_result | Width in characters
methods GET_OUTPUTWIDTH
returning
value(RV_RESULT) type I .
"! Sets the desired output width in characters
"! PowerShell output often gets truncated, wrapped or adjusted
"! corresponding to the width of a console window. Since there
"! is not necessarily a console window available, the default
"! is set to 80 characters width.
"!
"! @parameter iv_width | Width in characters
methods SET_OUTPUTWIDTH
importing
value(IV_WIDTH) type I .
"! Reads an include as string
"!
"! @parameter iv_incl_name | Name of the include
"!
"! @parameter rv_str_incl | Include as string
methods READ_INCL_AS_STRING
importing
value(IV_INCL_NAME) type SOBJ_NAME
returning
value(RV_STR_INCL) type STRING .
"! Loads a file from the MIME repository
"! and copies it to the SAP GUI work directory
"!
"! @parameter iv_uri_file | URI path of the file in the MIME repository
methods LOAD_FILE_FROM_MIME
importing
!IV_URI_FILE type CSEQUENCE .
"! Converts an outputstring to a string table
"!
"! @parameter iv_outputstring | String from get_outputstring
"!
"! @parameter rv_stringtable | String in table
methods OUTPUTSTRING_TO_TABLE
importing
value(IV_OUTPUTSTRING) type STRING
returning
value(RT_STRINGTABLE) type Z_TAB_STRING .
PRIVATE SECTION.
METHODS isactivex
EXPORTING ev_result TYPE i.
DATA olib TYPE ole2_object.
ENDCLASS.
CLASS Z_CL_ACTIVEXPOSHV3 IMPLEMENTATION.
METHOD clear_output."-------------------------------------------------
CALL METHOD OF olib 'ClearOutput'.
ENDMETHOD.
METHOD eval."---------------------------------------------------------
CALL METHOD OF olib 'Eval' = rv_result
EXPORTING #1 = iv_expression.
ENDMETHOD.
METHOD execute."------------------------------------------------------
CALL METHOD OF olib 'Execute'
EXPORTING
#1 = iv_command.
ENDMETHOD.
METHOD flush."--------------------------------------------------------
CALL METHOD cl_gui_cfw=>flush.
ENDMETHOD.
METHOD free_lib."-----------------------------------------------------
FREE OBJECT olib.
ENDMETHOD.
METHOD get_is_powershell_installed."----------------------------------
GET PROPERTY OF olib 'IsPowerShellInstalled' = rv_result.
ENDMETHOD.
METHOD get_outputmode."-----------------------------------------------
GET PROPERTY OF olib 'OutputMode' = rv_result.
ENDMETHOD.
METHOD get_outputstring."---------------------------------------------
GET PROPERTY OF olib 'OutputString' = rv_result.
ENDMETHOD.
METHOD get_outputwidth."----------------------------------------------
GET PROPERTY OF olib 'OutputWidth' = rv_result.
ENDMETHOD.
METHOD get_value."-----------------------------------------------------
CALL METHOD OF olib 'GetValue' = rv_result
EXPORTING #1 = iv_expression.
ENDMETHOD.
METHOD init."----------------------------------------------------------
CALL METHOD OF olib 'Init' = rv_result
EXPORTING #1 = iv_load_profiles.
ENDMETHOD.
METHOD isactivex."----------------------------------------------------
DATA hasactivex(32) TYPE c.
ev_result = 0.
CALL FUNCTION 'GUI_HAS_OBJECTS'
EXPORTING
object_model = 'ACTX'
IMPORTING
return = hasactivex
EXCEPTIONS
invalid_object_model = 1
OTHERS = 2.
CHECK sy-subrc = 0 AND hasactivex = 'X'.
ev_result = 1.
ENDMETHOD.
METHOD load_file_from_mime."------------------------------------------
DATA:
lr_mr_api TYPE REF TO if_mr_api,
lv_filedata TYPE xstring,
lv_workdir TYPE string,
lv_filepath TYPE string,
lt_filename TYPE STANDARD TABLE OF string,
lv_filename TYPE string,
lt_dtab TYPE TABLE OF x255,
lv_len TYPE i,
lv_fileexists TYPE abap_bool
.
SPLIT iv_uri_file AT '/' INTO TABLE lt_filename.
READ TABLE lt_filename INDEX lines( lt_filename ) INTO lv_filename.
CALL METHOD cl_gui_frontend_services=>get_sapgui_workdir
CHANGING
sapworkdir = lv_workdir
EXCEPTIONS
OTHERS = 1.
lv_filepath = lv_workdir && '\' && lv_filename.
CALL METHOD cl_gui_frontend_services=>file_exist
EXPORTING
file = lv_filepath
RECEIVING
result = lv_fileexists
EXCEPTIONS
OTHERS = 1.
CHECK lv_fileexists = abap_false.
IF lr_mr_api IS INITIAL.
lr_mr_api = cl_mime_repository_api=>if_mr_api~get_api( ).
ENDIF.
CALL METHOD lr_mr_api->get
EXPORTING
i_url = iv_uri_file
IMPORTING
e_content = lv_filedata
EXCEPTIONS
OTHERS = 1.
CHECK sy-subrc = 0.
CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
EXPORTING
buffer = lv_filedata
IMPORTING
output_length = lv_len
TABLES
binary_tab = lt_dtab.
CALL FUNCTION 'GUI_DOWNLOAD'
EXPORTING
bin_filesize = lv_len
filename = lv_filepath
filetype = 'BIN'
TABLES
data_tab = lt_dtab
EXCEPTIONS
OTHERS = 1.
ENDMETHOD.
METHOD load_lib."-----------------------------------------------------
DATA rc TYPE i VALUE 0.
rv_result = 0.
CALL METHOD me->isactivex IMPORTING ev_result = rc.
CHECK rc = 1.
CREATE OBJECT olib 'SAPIEN.ActiveXPoSHV3'.
CHECK sy-subrc = 0 AND olib-handle <> 0 AND olib-type = 'OLE2'.
rv_result = 1.
ENDMETHOD.
METHOD outputstring_to_table."----------------------------------------
FIELD-SYMBOLS:
<lv_string> TYPE string
.
SPLIT iv_outputstring
AT cl_abap_char_utilities=>cr_lf
INTO TABLE rt_stringtable.
"-Delete empty lines------------------------------------------------
LOOP AT rt_stringtable ASSIGNING <lv_string>.
CHECK <lv_string> IS INITIAL.
DELETE rt_stringtable.
ENDLOOP.
ENDMETHOD.
METHOD read_incl_as_string."------------------------------------------
DATA:
lt_tadir TYPE tadir,
lt_incl TYPE TABLE OF string,
lv_inclline TYPE string,
lv_retincl TYPE string
.
SELECT SINGLE * FROM tadir INTO lt_tadir
WHERE obj_name = iv_incl_name.
CHECK sy-subrc = 0.
READ REPORT iv_incl_name INTO lt_incl.
CHECK sy-subrc = 0.
LOOP AT lt_incl INTO lv_inclline.
lv_retincl = lv_retincl && lv_inclline &&
cl_abap_char_utilities=>cr_lf.
CLEAR lv_inclline.
ENDLOOP.
rv_str_incl = lv_retincl.
ENDMETHOD.
METHOD set_outputmode."-----------------------------------------------
SET PROPERTY OF olib 'OutputMode' = iv_mode.
ENDMETHOD.
METHOD set_outputwidth."----------------------------------------------
SET PROPERTY OF olib 'OutputWidth' = iv_width.
ENDMETHOD.
ENDCLASS.
"-End-------------------------------------------------------------------
Hier ein Beispielprogramm zum Anzeigen einer Tabelle im TableGrid von PowerShell. Die Daten werden einfach mittels eines Select gelesen und mit dem JSON-Serialisierer umgewandelt. Dann wird PowerShell geladen und initialisiert. Die JSON-Daten werden dem GridView nach einer Konvertierung über die Pipeline übergeben.
"-Begin-----------------------------------------------------------------
REPORT zposh_example2.
DATA:
lt_sflight TYPE STANDARD TABLE OF sflight,
lv_sflight_json TYPE string,
lo_posh TYPE REF TO z_cl_activexposhv3,
lv_pscode TYPE string,
lv_result TYPE string
.
SELECT * INTO CORRESPONDING FIELDS OF TABLE lt_sflight FROM SFLIGHT.
lv_sflight_json = /ui2/cl_json=>serialize( data = lt_sflight ).
CREATE OBJECT lo_posh.
CHECK lo_posh->load_lib( ) = lo_posh->mc_true.
CHECK lo_posh->get_is_powershell_installed( ) = lo_posh->mc_true.
CHECK lo_posh->init( iv_load_profiles = lo_posh->mc_false ) = 0.
lo_posh->set_outputmode( lo_posh->mc_outputbuffer ).
lo_posh->set_outputwidth( 132 ).
lo_posh->clear_output( ).
lv_pscode =
'(''' && lv_sflight_json && ''' | ConvertFrom-Json) | Out-GridView'
.
lo_posh->execute( lv_pscode ).
lv_result = lo_posh->get_outputstring( ).
lo_posh->free_lib( ).
"-End-------------------------------------------------------------------
Und so sieht es dann aus.

Auf diese Art und Weise lassen sich nun sehr einfach Integrationsszenarien zwischen PowerShell und ABAP realisieren.
Hier ein Beispiel zum Anzeigen von .NET-Dialogen. Das Beispiel 1 öffnet einen Dialog, im Beispiel 2 wird das Ergebnis des 1. Dialoges angezeigt und im Beispiel 3 wird ein Datei-Öffnen-Dialog angezeigt und mit Auswahl eines Bildes wird dieses in einem eigenen Dialog angezeigt.
"-Begin-----------------------------------------------------------------
REPORT zposh_example1.
DATA:
lo_posh TYPE REF TO z_cl_activexposhv3,
lv_pscode TYPE string,
lv_result TYPE string
.
CREATE OBJECT lo_posh.
CHECK lo_posh->load_lib( ) = lo_posh->mc_true.
CHECK lo_posh->get_is_powershell_installed( ) = lo_posh->mc_true.
CHECK lo_posh->init( iv_load_profiles = lo_posh->mc_false ) = 0.
lo_posh->set_outputmode( lo_posh->mc_outputbuffer ).
lo_posh->set_outputwidth( 132 ).
lo_posh->clear_output( ).
"-Example 1-------------------------------------------------------------
lv_pscode =
'[void][System.Reflection.Assembly]::LoadWithPartialName(`' &&
' "Microsoft.VisualBasic");' &&
'[Microsoft.VisualBasic.Interaction]::MsgBox("Hello World", `' &&
' "YesNoCancel,SystemModal,Information", "Message");'.
lo_posh->execute( lv_pscode ).
lv_result = lo_posh->get_outputstring( ).
lo_posh->clear_output( ).
"-Example 2-------------------------------------------------------------
lv_pscode =
'[System.Windows.Forms.MessageBox]::Show("' && lv_result && '","Button",0);'.
lo_posh->execute( lv_pscode ).
lv_result = lo_posh->get_outputstring( ).
lo_posh->clear_output( ).
"-Example 3-------------------------------------------------------------
lv_pscode =
'Function GetFileName() {' &&
' $dlgOpen = New-Object System.Windows.Forms.OpenFileDialog;' &&
' $dlgOpen.Title = "Please select a file";' &&
' $dlgOpen.InitialDirectory = `' &&
' "C:\Users\Public\Pictures\Sample Pictures\";' &&
' $dlgOpen.Filter = "All Files (*.*)|*.*";' &&
' $dlgOpen.ShowHelp = $True;' &&
' $result = $dlgOpen.ShowDialog();' &&
' If($result -eq "OK") {' &&
' [System.Diagnostics.Debug]::WriteLine($dlgOpen.FileName);' &&
' Return $dlgOpen.FileName;' &&
' }' &&
'}' &&
'[void][reflection.assembly]::LoadWithPartialName(`' &&
' "System.Windows.Forms");' &&
'$fileName = GetFileName;' &&
'If ([string]::IsNullOrEmpty($fileName) -eq $True) { Exit }' &&
'$file = (get-item $fileName);' &&
'$img = [System.Drawing.Image]::Fromfile($file);' &&
'[System.Windows.Forms.Application]::EnableVisualStyles();' &&
'$form = new-object Windows.Forms.Form;' &&
'$form.Text = "Image Viewer";' &&
'$form.Width = $img.Size.Width;' &&
'$form.Height = $img.Size.Height;' &&
'$pictureBox = new-object Windows.Forms.PictureBox;' &&
'$pictureBox.Width = $img.Size.Width;' &&
'$pictureBox.Height = $img.Size.Height;' &&
'$pictureBox.Image = $img;' &&
'$form.controls.add($pictureBox);' &&
'$form.Add_Shown( { $form.Activate() } );' &&
'$form.ShowDialog()'.
lo_posh->execute( lv_pscode ).
lv_result = lo_posh->get_outputstring( ).
lo_posh->free_lib( ).
"-End-------------------------------------------------------------------
In diesem Fall sind die PowerShell Sourcen direkt in den ABAP-Quellcode eingebettet. Diese können jedoch auch als Include-Entwicklungsobjekt gespeichert und mit der Methode READ_INCL_AS_STRING eingelesen und der Methode EXECUTE übergeben werden. Mit diesem Ansatz kann nun die gesamte .NET-Welt in ABAP integriert werden. Jede .NET-Bibliothek kann so genutzt werden und darüber hinaus können auch VB# resp. VB.NET und C# direkt integriert werden.
- PowerShell mit ABAP nutzen - 17. März 2018
- SAP GUI Scripting Rekorder mit Windows PowerShell - 22. November 2016
- 64-bit Programme mit dem SAP GUI für Windows ausführen - 20. November 2016


