Wednesday 14 March 2018

How To Combine PowerShell Development in ABAP Context

If you work with PowerShell in ABAP context it could be profitably to integrate PowerShell development into your ABAP development process. In this example I will show how to use PowerShell ISE to read and store PowerShell scripts as includes on an SAP system.

At the SAP system I added two RFMs. The first reads an include and the second writes an include. In both cases it is necessary that the include exists on the SAP system and it must be activated.

Here the RFM to read an include. At first it checks in the table TADIR if the include exists. Then it reads the source and delivers it as string.

FUNCTION z_rfc_readincl
  IMPORTING
    VALUE(IV_INCLNAME) TYPE SOBJ_NAME
  EXPORTING
    VALUE(EV_STRINCL) TYPE STRING
  EXCEPTIONS
    NOT_AUTHORIZED.

  DATA:
    lt_tadir    TYPE tadir,
    lt_incl     TYPE TABLE OF string,
    lv_inclline TYPE string,
    lv_retincl  TYPE string
    .

  AUTHORITY-CHECK OBJECT 'S_DEVELOP'
    ID 'ACTVT'  FIELD '03'.
  IF sy-subrc <> 0.
    RAISE not_authorized.
  ENDIF.

  SELECT SINGLE * FROM tadir INTO lt_tadir WHERE obj_name = iv_inclname.
  CHECK sy-subrc = 0.
  READ REPORT iv_inclname 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.
  ev_strincl = lv_retincl.

ENDFUNCTION.

Here the RFM to write an include. At first it checks in the table TADIR if the include exists.

FUNCTION z_rfc_writeincl
  IMPORTING
    VALUE(iv_inclname) TYPE  sobj_name
    VALUE(iv_strincl) TYPE  string
  EXPORTING
    VALUE(ev_result) TYPE  integer
  EXCEPTIONS
    not_authorized.

  DATA:
    lt_tadir    TYPE tadir,
    lt_incl     TYPE TABLE OF string
    .

  AUTHORITY-CHECK OBJECT 'S_DEVELOP'
    ID 'ACTVT'  FIELD '02'.
  IF sy-subrc <> 0.
    RAISE not_authorized.
  ENDIF.

  ev_result = 0.
  CHECK iv_inclname+0(1) = 'Y' OR iv_inclname+0(1) = 'Z'.
  SELECT SINGLE * FROM tadir INTO lt_tadir WHERE obj_name = iv_inclname.
  CHECK sy-subrc = 0.
  SPLIT iv_strincl AT cl_abap_char_utilities=>cr_lf INTO TABLE lt_incl.
  INSERT REPORT iv_inclname FROM lt_incl PROGRAM TYPE 'I'.
  CHECK sy-subrc = 0.
  ev_result = 1.

ENDFUNCTION.

Here the PowerShell script to use this RFMs. The first function Read-SAPInclude uses z_rfc_readincl and the second function Write-SAPInclude uses z_rfc_writeincl. The basics to use NCo with PowerShell is described here and here.

Function Read-SAPInclude {
#-Begin-----------------------------------------------------------------

  #-Function Get-Destination--------------------------------------------
  Function Get-Destination {

    #-Verbindungsparamter-----------------------------------------------
    $cfgParams = `
      New-Object SAP.Middleware.Connector.RfcConfigParameters
    $cfgParams.Add($cfgParams::Name, "TEST")
    $cfgParams.Add($cfgParams::AppServerHost, "ABAP")
    $cfgParams.Add($cfgParams::SystemNumber, "00")
    $cfgParams.Add($cfgParams::Client, "001")
    $cfgParams.Add($cfgParams::User, "BCUSER")

    $SecPasswd = Read-Host -Prompt "Passwort" -AsSecureString
    $ptrPasswd = `
      [Runtime.InteropServices.Marshal]::SecureStringToBStr($SecPasswd)
    $Passwd = `
      [Runtime.InteropServices.Marshal]::PtrToStringBStr($ptrPasswd)
    $cfgParams.Add($cfgParams::Password, $Passwd)

    Return [SAP.Middleware.Connector.RfcDestinationManager]::GetDestination($cfgParams)

  }

  #-Sub ReadInclude-----------------------------------------------------
  Function ReadInclude([string]$InclName) {

    $destination = Get-Destination

    $rfcFunction = $destination.Repository.CreateFunction("Z_RFC_READINCL")
    $rfcFunction.SetValue("IV_INCLNAME", $InclName)
    $rfcFunction.Invoke($destination)
    $NewTab = $psISE.CurrentPowerShellTab.Files.Add()
    $NewTab.Editor.Text = $rfcFunction.GetValue("EV_STRINCL")
    $NewTab.Editor.SetCaretPosition(1,1)

  }

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

    [void][Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')

    $InclName = [Microsoft.VisualBasic.Interaction]::InputBox("Include Name", `
      "Request")
    If (-not ([string]::IsNullOrEmpty($InclName))) {
      ReadInclude $InclName.ToUpper()
    }

  }

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

  #-Error routine-------------------------------------------------------
  Trap {
    [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") > $Null
    [Void] [System.Windows.Forms.MessageBox]::Show( `
      $_.Exception.GetType().FullName + `
      [System.Environment]::NewLine + `
      "Error at line " + $_.InvocationInfo.ScriptLineNumber + `
      " in " + $_.InvocationInfo.ScriptName + `
      [System.Environment]::NewLine + [System.Environment]::NewLine + `
      $_.Exception.Message, "An Error Occurred", 0)
    Exit
  }

#-End-------------------------------------------------------------------
}

Function Write-SAPInclude {
#-Begin-----------------------------------------------------------------

  #-Function Get-Destination--------------------------------------------
  Function Get-Destination {

    #-Verbindungsparamter-----------------------------------------------
    $cfgParams = `
      New-Object SAP.Middleware.Connector.RfcConfigParameters
    $cfgParams.Add($cfgParams::Name, "TEST")
    $cfgParams.Add($cfgParams::AppServerHost, "ABAP")
    $cfgParams.Add($cfgParams::SystemNumber, "00")
    $cfgParams.Add($cfgParams::Client, "001")
    $cfgParams.Add($cfgParams::User, "BCUSER")

    $SecPasswd = Read-Host -Prompt "Passwort" -AsSecureString
    $ptrPasswd = `
      [Runtime.InteropServices.Marshal]::SecureStringToBStr($SecPasswd)
    $Passwd = `
      [Runtime.InteropServices.Marshal]::PtrToStringBStr($ptrPasswd)
    $cfgParams.Add($cfgParams::Password, $Passwd)

    Return [SAP.Middleware.Connector.RfcDestinationManager]::GetDestination($cfgParams)

  }

  #-Sub WriteInclude----------------------------------------------------
  Function WriteInclude([string]$InclName) {

    $destination = Get-Destination

    $rfcFunction = $destination.Repository.CreateFunction("Z_RFC_WRITEINCL")
    $rfcFunction.SetValue("IV_INCLNAME", $InclName)
    $rfcFunction.SetValue("IV_STRINCL", $psISE.CurrentFile.Editor.Text)
    $rfcFunction.Invoke($destination)
    $Result = $rfcFunction.GetValue("EV_RESULT")
If ($Result = 0) {
  Write-Host "An Error Occurred"
}

  }

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

    [void][Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')

    $InclName = [Microsoft.VisualBasic.Interaction]::InputBox("Include Name", `
      "Request")
    If (-not ([string]::IsNullOrEmpty($InclName))) {
      WriteInclude $InclName.ToUpper()
    }

  }

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

  #-Error routine-------------------------------------------------------
  Trap {
    [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") > $Null
    [Void] [System.Windows.Forms.MessageBox]::Show( `
      $_.Exception.GetType().FullName + `
      [System.Environment]::NewLine + `
      "Error at line " + $_.InvocationInfo.ScriptLineNumber + `
      " in " + $_.InvocationInfo.ScriptName + `
      [System.Environment]::NewLine + [System.Environment]::NewLine + `
      $_.Exception.Message, "An Error Occurred", 0)
    Exit
  }

#-End-------------------------------------------------------------------
}

#-Sub Load-NCo----------------------------------------------------------
Function Load-NCo {

  [String]$ScriptDir = "C:\Dummy"

  If ([Environment]::Is64BitProcess) {
    [String]$Path = $ScriptDir + "\x64\"
  } Else {
    [String]$Path = $ScriptDir + "\x86\"
  }

  [String]$File = $Path + "sapnco.dll"; Add-Type -Path $File
  $File = $Path + "sapnco_utils.dll"; Add-Type -Path $File

}

#-Main------------------------------------------------------------------
Load-NCo
$null = $psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add("Read SAP Include", { Read-SAPInclude }, $null)
$null = $psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add("Write SAP Include", { Write-SAPInclude }, $null)

Store this script into your profile file. Type into your PS console $profile.CurrentUserCurrentHost to find the location of the file. If the file exists add the code, otherwise store the code into this file.

SAP ABAP Development, SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Learning

If you restart your ISE you have now two new menu items.

SAP ABAP Development, SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Learning

The first menu item reads an include from an SAP system, opens a new tab at PowerShell ISE and writes the include from the SAP system to the PowerShell ISE window. The second menu item writes the code from the open PowerShell ISE window as include into an SAP system. In both cases you must define the include name and the password of the system. The system, client and user are hard coded, you can add an own login screen if you like.

SAP ABAP Development, SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Learning

SAP ABAP Development, SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Learning

After that you have on the one hand a new tab with the source of the include file.

SAP ABAP Development, SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Learning

SAP ABAP Development, SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Learning

And on the other hand you have new content in the include.

SAP ABAP Development, SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Learning

SAP ABAP Development, SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Learning

This example shows how easy it is to integrate PowerShell and ABAP development.

No comments:

Post a Comment