Quantcast
Channel: Scripting Languages
Viewing all 68 articles
Browse latest View live

How to use PowerShell Script Language via dotNET Connector (NCo) in SAP Context

$
0
0

Hello community,

 

PowerShell is in standard available on any Windows 7 and higher and it bases on dotNET. You can find a lot of information here. PowerShell offers a good integrated scripting environment (ISE), so it is very easy to code and test scripts.

 

SAP offers with the dotNET Connector (NCo) an easy way to use the SAP backend in this environment. You can find NCo here. The NCo is primarly used with C# language and you can find a lot of examples.

 

I combine the possibilities of PowerShell and NCo, to show how easy and fast you can create a communication between your presentation server and the application server. The first example shows only the version number of the NCo:

 

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

 

  #-Function Get-NCoVersion---------------------------------------------

    Function Get-NCoVersion {

 

      #-Loads NCo libraries---------------------------------------------

        $rc = [Reflection.Assembly]::LoadFile("C:\NCo\sapnco.dll")

        $rc = [Reflection.Assembly]::LoadFile("C:\NCo\sapnco_utils.dll")

 

 

      #-Gets the version of NCo-----------------------------------------

        $Version =

          [SAP.Middleware.Connector.SAPConnectorInfo]::get_Version()

        $PatchLevel =

          [SAP.Middleware.Connector.SAPConnectorInfo]::get_KernelPatchLevel()

        $SAPRelease =

          [SAP.Middleware.Connector.SAPConnectorInfo]::get_SAPRelease()

 

      #-Shows the result------------------------------------------------

        Write-Host "`r`nNCo verion:" $Version

        Write-Host "Patch Level:" $PatchLevel

        Write-Host "SAP Release:" $SAPRelease

 

    }

 

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

    Get-NCoVersion

 

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

 

001.JPG

The second example shows how to call a function module, with import and export parameters:

 

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

 

  #-Function Invoke-SAPFunctionModule-----------------------------------

    Function Invoke-SAPFunctionModule {

 

      #-Loads NCo libraries---------------------------------------------

        $rc = [Reflection.Assembly]::LoadFile("C:\NCo\sapnco.dll")

        $rc = [Reflection.Assembly]::LoadFile("C:\NCo\sapnco_utils.dll")

 

 

      #-Sets connection parameters--------------------------------------

        $cfgParams = New-Object SAP.Middleware.Connector.RfcConfigParameters

        $cfgParams.Add("NAME", "TEST")

        $cfgParams.Add("ASHOST", "ABAP")

        $cfgParams.Add("SYSNR", "00")

        $cfgParams.Add("CLIENT", "001")

        $cfgParams.Add("USER", "BCUSER")

        $cfgParams.Add("PASSWD", "minisap")

 

      #-Destination-----------------------------------------------------

        $destination =

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

 

      #-Metadata--------------------------------------------------------

      [SAP.Middleware.Connector.IRfcFunction]$rfcFunction =

        $destination.Repository.CreateFunction("STFC_CONNECTION")

 

      #-Sets import parameter-------------------------------------------

        $rfcFunction.SetValue("REQUTEXT", "Hello World from PowerShell")

 

      #-Calls function module-------------------------------------------

        $rfcFunction.Invoke($destination)

 

      #-Shows export parameters-----------------------------------------

        Write-Host $rfcFunction.GetValue("ECHOTEXT")

        Write-Host $rfcFunction.GetValue("RESPTEXT")

 

    }

 

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

    Invoke-SAPFunctionModule

      

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

 

002.JPG

The PowerShell ISE offers a lot of additional things, e.g. like code completion.

 

003.jpg

 

With PowerShell and NCo you can easily create client functions. All you have to do is to copy the necessary NCo libraries on your presentation server. PowerShell is on Windows available and so you can start up quickly.

 

Enjoy it.

 

Cheers

Stefan


PowerShell Functions: Load NCo in Relation to Shell and Get Destination with Password Requester

$
0
0

Hello community,

 

I presented here the possibility to use PowerShell via dotNET Connector (NCo) with SAP.

 

Here now two functions to simplify the using of PowerShell with NCo.

 

The first function shows how to load NCo in relation to the bus width of the PowerShell. The function gets the directory of the script, set it as current directory, expects the NCo libraries in subdirectories x64 for the 64-bit version and x86 for the 32-bit version and loads it.

 

  #-Function Load-NCo---------------------------------------------------

  #-

  #- The loading of the NCo libraries is in dependence to the shell.

  #- The 64-bit version loads the libraries from the path x64, and the

  #- 32-bit version from the path x86.

  #-

  #---------------------------------------------------------------------

    Function Load-NCo {

 

      $CurrentDir = Get-Location

      [System.IO.Directory]::SetCurrentDirectory($CurrentDir.Path)

 

      $Size = [System.IntPtr]::Size

      if ($Size -eq 4) {

        $Path = $CurrentDir.Path + "\x86\"

      }

      elseif ($Size -eq 8) {

        $Path = $CurrentDir.Path + "\x64\"

      }

 

      $rc = [Reflection.Assembly]::LoadFile($Path + "sapnco.dll")

      $rc = [Reflection.Assembly]::LoadFile($Path + "sapnco_utils.dll")

 

    }

 

The second function shows how to get a destination with a password requester.

 

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

    Function Get-Destination {


      #-Verbindungsparamter---------------------------------------------

        $cfgParams = New-Object SAP.Middleware.Connector.RfcConfigParameters

        $cfgParams.Add("NAME", "TEST")

        $cfgParams.Add("ASHOST", "ABAP")

        $cfgParams.Add("SYSNR", "00")

        $cfgParams.Add("CLIENT", "001")

        $cfgParams.Add("USER", "BCUSER")


        $SecPasswd = Read-Host -Prompt "Passwort" -AsSecureString

        $ptrPasswd = [Runtime.InteropServices.Marshal]::SecureStringToBStr($SecPasswd)

        $Passwd = [Runtime.InteropServices.Marshal]::PtrToStringBStr($ptrPasswd)

        $cfgParams.Add("PASSWD", $Passwd)


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


    }

001.JPG

These two functions changes the examples a little bit.

 

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

 

  #-Function Load-NCo---------------------------------------------------

    Function Load-NCo {

 

      $CurrentDir = Get-Location

      [System.IO.Directory]::SetCurrentDirectory($CurrentDir.Path)

 

      $Size = [System.IntPtr]::Size

      if ($Size -eq 4) {

        $Path = $CurrentDir.Path + "\x86\"

      }

      elseif ($Size -eq 8) {

        $Path = $CurrentDir.Path + "\x64\"

      }

 

      $rc = [Reflection.Assembly]::LoadFile($Path + "sapnco.dll")

      $rc = [Reflection.Assembly]::LoadFile($Path + "sapnco_utils.dll")

 

    }

 

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

    Function Get-Destination {

 

      #-Connectionparamter----------------------------------------------

        $cfgParams = New-Object SAP.Middleware.Connector.RfcConfigParameters

        $cfgParams.Add("NAME", "TEST")

        $cfgParams.Add("ASHOST", "ABAP")

        $cfgParams.Add("SYSNR", "00")

        $cfgParams.Add("CLIENT", "001")

        $cfgParams.Add("USER", "BCUSER")

        $cfgParams.Add("PASSWD", "minisap")

 

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

 

    }

 

  #-Function Invoke-SAPFunctionModule-----------------------------------

    Function Invoke-SAPFunctionModule {

 

      $destination = Get-Destination

 

      #-Metadata--------------------------------------------------------

        [SAP.Middleware.Connector.IRfcFunction]$rfcFunction =

          $destination.Repository.CreateFunction("STFC_CONNECTION")

 

      #-Set importparameter---------------------------------------------

        $rfcFunction.SetValue("REQUTEXT", "Hello World from PowerShell")

 

      #-Call function module--------------------------------------------

        $rfcFunction.Invoke($destination)

 

      #-Show exportparameter--------------------------------------------

        Write-Host $rfcFunction.GetValue("ECHOTEXT")

        Write-Host $rfcFunction.GetValue("RESPTEXT")

 

    }

 

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

    Load-NCo

    Invoke-SAPFunctionModule

 

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

 

Enjoy it.

 

Cheers

Stefan

How to Call BAPI Function Modules with PowerShell via NCo

$
0
0

Hello community,

 

I presented here the possibility to use PowerShell via dotNET Connector (NCo) with SAP. Also I presented here two auxiliary functions for an easier using of PowerShell via dotNET Connector (NCo) with SAP.

 

Here now a short description with two examples how to call BAPI function modules (FM) with PowerShell and NCo. I use the FM BAPI_USER_CREATE1 and BAPI_USER_DELETE. In comparisaon to a normal function call we use also the FM BAPI_TRANSACTION_COMMIT to do an external commit. Also we use an additional context to start a stateful call sequence, to bind the destination’s connection pool with the current thread of execution, with BeginContext and EndContext.

 

Here the first example how to create the user MYUSER:

 

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

 

  #-Function Load-NCo---------------------------------------------------

    ...

 

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

    ...

 

  #-Function Invoke-SAPFunctionModule-----------------------------------

    Function Invoke-SAPFunctionModule {

 

      $destination = Get-Destination

 

      #-Metadata--------------------------------------------------------

        try {

          [SAP.Middleware.Connector.IRfcFunction]$bapiCreateUser =

            $destination.Repository.CreateFunction("BAPI_USER_CREATE1")

          [SAP.Middleware.Connector.IRfcFunction]$bapiTransactionCommit =

            $destination.Repository.CreateFunction("BAPI_TRANSACTION_COMMIT")

        }

        catch [SAP.Middleware.Connector.RfcBaseException] {

          Write-Host "Metadata error"

          break

        }

 

      #-Set importparameter---------------------------------------------

        $bapiCreateUser.SetValue("USERNAME", "MYUSER")

        [SAP.Middleware.Connector.IRfcStructure]$imPassword =

          $bapiCreateUser.GetStructure("PASSWORD")

        $imPassword.SetValue("BAPIPWD", "initial")

        [SAP.Middleware.Connector.IRfcStructure]$imAddress =

          $bapiCreateUser.GetStructure("ADDRESS")

        $imAddress.SetValue("FIRSTNAME", "My")

        $imAddress.SetValue("LASTNAME", "User")

        $imAddress.SetValue("FULLNAME", "MyUser")

 

      #-Open context----------------------------------------------------

        $rc =

          [SAP.Middleware.Connector.RfcSessionManager]::BeginContext($destination)

 

      #-Call function modules-------------------------------------------

        try {

          #-Create user-------------------------------------------------

            $bapiCreateUser.Invoke($destination)

          [SAP.Middleware.Connector.IRfcTable]$return =

            $bapiCreateUser.GetTable("RETURN")

          foreach ($line in $return) {

            Write-Host $line.GetValue("TYPE") "-" $line.GetValue("MESSAGE")

          }

          #-Commit------------------------------------------------------

            $bapiTransactionCommit.Invoke($destination)

        }

        finally {

          #-Close context-----------------------------------------------

            $rc =

              [SAP.Middleware.Connector.RfcSessionManager]::EndContext($destination)

        }

 

    }

 

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

    Load-NCo

    Invoke-SAPFunctionModule

 

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

 

Here now the second example how to delete the user MYUSER:

 

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

 

  #-Function Load-NCo---------------------------------------------------

    ...

 

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

    ....

 

  #-Function Invoke-SAPFunctionModule-----------------------------------

    Function Invoke-SAPFunctionModule {

 

      $destination = Get-Destination

 

      #-Metadata--------------------------------------------------------

        try {

          [SAP.Middleware.Connector.IRfcFunction]$bapiDeleteUser =

            $destination.Repository.CreateFunction("BAPI_USER_DELETE")

          [SAP.Middleware.Connector.IRfcFunction]$bapiTransactionCommit =

            $destination.Repository.CreateFunction("BAPI_TRANSACTION_COMMIT")

        }

        catch [SAP.Middleware.Connector.RfcBaseException] {

          Write-Host "Metadaten-Fehler"

          break

        }

 

      #-Set importparameter---------------------------------------------

        $bapiDeleteUser.SetValue("USERNAME", "MYUSER")

 

      #-Open context----------------------------------------------------

        $rc =

          [SAP.Middleware.Connector.RfcSessionManager]::BeginContext($destination)

 

 

      #-Call function modules-------------------------------------------

        try {

          #-Delete user-------------------------------------------------

            $bapiDeleteUser.Invoke($destination)

          [SAP.Middleware.Connector.IRfcTable]$return =

            $bapiDeleteUser.GetTable("RETURN")

          foreach ($line in $return) {

            Write-Host $line.GetValue("TYPE") "-" $line.GetValue("MESSAGE")

          }

          #-Commit------------------------------------------------------

            $bapiTransactionCommit.Invoke($destination)

        }

        finally {

          #-Close context-----------------------------------------------

            $rc =

              [SAP.Middleware.Connector.RfcSessionManager]::EndContext($destination)

        }

 

    }

 

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

    Load-NCo

    Invoke-SAPFunctionModule

 

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

 

This examples shows also how to use structures and tables in the import and export parameters.

 

PowerShell offers a lot of possibilities and among the shadow of the circumstance of the end of support of VBScript in a shorter distant future - first signs look here - seems it better to look for an alternative. In my case offers PowerShell all the things to replace VBScript.

 

Enjoy it.

 

Cheers

Stefan

Copy ABAP User using BAPIs

$
0
0

A handy script for copying ABAP user to one or more new users. It works just like SU01 User Copy function, only programatically, by calling BAPIs of SU_USER ABAP Function Group, via Remote Function Calls (RFCs). No new ABAP code or dialog access needed in backend system, only the source ABAP User and the list of new usernames, to be created.

 

Using the transaction SU01 in this example, the source user UNAMEFROM is selected and copied to new user UNAMETO1:

Copy.png

The end result is a new created ABAP User, with Address, Logon Data, Defaults, Parameters, Roles, Profiles and Groups.

UnameTo.png

We achieve the same, using BAPIs from SU_USER ABAP Function Group and Python script:

SU_USER.png

Using the script, you can do these repetitive SU01 task automatically, from your notebook. The Python RFC Connector shall be installed on a notebook and the data to be copied you can customise for your project needs.

Log.png

Here the code (clone from Github). Error checks and optional parameters removed, for the sake of readability.


# -*- coding: utf-8 -*-

 

from pyrfc import *

import datetime

 

# BAPI calls log

def print_log(bapi_return):

    if len(bapi_return) > 0:

        for line in bapi_return:

            print '%s: %s' % (line['TYPE'], line['MESSAGE'])

 

# Connect to ABAP system

SAPROUTER = '/H/123.12.123.12/E/yt6ntx/H/123.14.131.111/H/'

 

EC4 = {

    'user'      : 'abapuser',

    'passwd'    : 'abappass',

    'ashost'    : '10.11.12.13',

    'saprouter' : SAPROUTER,

    'sysnr'     : '00',

    'client'    : '300',

    'trace'     : '3',

    'lang'      : 'EN' }

 

c = Connection(**EC4)

 

# The sourse user, to be copied

uname_from = 'UNAMEFROM'

# Defaults if source user validity not maintained (undefined)

valid_from = datetime.date(2015,1,19)

valid_to   = datetime.date(2015,12,31)

# New users' password. For automatic generation check CREATE BAPI

initpwd = 'InitPa$$21'

 

# Users to be created

users= ['UNAMETO1', 'UNAMETO2']

 

# Get source user details

r = c.call('BAPI_USER_GET_DETAIL', USERNAME = uname_from, CACHE_RESULTS  = ' ')

 

# Set new users' defaults

if r['LOGONDATA']['GLTGV'] is None:

    r['LOGONDATA']['GLTGV'] = valid_from

if r['LOGONDATA']['GLTGB'] is None:

    r['LOGONDATA']['GLTGB'] = valid_to

password = {'BAPIPWD' : initpwd}

 

# Create new users

for uname_to in users:

    print uname_to

    r['ADDRESS']['LASTNAME'] = uname_to

    r['ADDRESS']['FULLNAME'] = uname_to

 

    x = c.call('BAPI_USER_CREATE1',

        USERNAME    = uname_to,

        LOGONDATA   = r['LOGONDATA'],

        PASSWORD    = password,

        DEFAULTS    = r['DEFAULTS'],

        ADDRESS     = r['ADDRESS'],

        COMPANY     = r['COMPANY'],

        REF_USER    = r['REF_USER'],

        PARAMETER   = r['PARAMETER'],

        GROUPS      = r['GROUPS']

    )

 

    print_log(x['RETURN'])

 

    x = c.call('BAPI_USER_PROFILES_ASSIGN',

        USERNAME  = uname_to,

        PROFILES  = r['PROFILES']

    )

 

    print_log(x['RETURN'])

 

    x = c.call('BAPI_USER_ACTGROUPS_ASSIGN',

        USERNAME       = uname_to,

        ACTIVITYGROUPS = r['ACTIVITYGROUPS']

    )

 

    print_log(x['RETURN'])

 

# Finished

print ("%s copied to %d new users\nBye!") % (uname_from, len(users))

 

 

You can do the same from Java, .NET, nodejs, GO, Ruby, using respective RFC connectors (here PyRFC used) and of course in ABAP (clone from Github):


REPORT zsbucopy1.

 

DATA:

  uname_from        LIKE bapibname-bapibname VALUE 'UNAMEFROM',

 

  ls_logondata      TYPE bapilogond,

  ls_defaults      TYPE bapidefaul,

  ls_address        TYPE bapiaddr3,

  ls_company        TYPE  bapiuscomp,

 

  lt_parameter      TYPE STANDARD TABLE OF bapiparam,

  lt_profiles      TYPE STANDARD TABLE OF bapiprof,

  lt_activitygroups TYPE STANDARD TABLE OF bapiagr,

  lt_return        TYPE STANDARD TABLE OF bapiret2,

  lt_parameter1    TYPE STANDARD TABLE OF bapiparam1,

  lt_groups        TYPE STANDARD TABLE OF bapigroups,

 

  uname_to          LIKE bapibname-bapibname VALUE 'UNAMETO',

  is_password      TYPE bapipwd.

 

is_password-bapipwd = 'Init2014'.

 

CALL FUNCTION 'BAPI_USER_GET_DETAIL'

  EXPORTING

    username      = uname_from

    cache_results  = ' '

  IMPORTING

    logondata      = ls_logondata

    defaults      = ls_defaults

    address        = ls_address

    company        = ls_company

  TABLES

    parameter      = lt_parameter

    profiles      = lt_profiles

    activitygroups = lt_activitygroups

    return        = lt_return

    parameter1    = lt_parameter1

    groups        = lt_groups.

 

MOVE uname_to TO: ls_address-lastname, ls_address-fullname.

 

CALL FUNCTION 'BAPI_USER_CREATE1'

  EXPORTING

    username  = uname_to

    logondata  = ls_logondata

    password  = is_password

    defaults  = ls_defaults

    address    = ls_address

    company    = ls_company

    ref_user  = uname_from

  TABLES

    parameter  = lt_parameter

    return    = lt_return

    groups    = lt_groups

    parameter1 = lt_parameter1.

 

CALL FUNCTION 'BAPI_USER_PROFILES_ASSIGN'

  EXPORTING

    username = uname_to

  TABLES

    profiles = lt_profiles

    return  = lt_return.

 

CALL FUNCTION 'BAPI_USER_ACTGROUPS_ASSIGN'

  EXPORTING

    username      = uname_to

  TABLES

    activitygroups = lt_activitygroups

    return        = lt_return.


Hope you enjoy creating new ABAP users programatically.

How to make file-dialogues scriptable (vba)

$
0
0

As Holger Kohn stated here, file dialogues are GUI-modal and recordable in SAP-Basis Release 740 only. Meaning, if you're up to date, you won't have troubles to control Save-As or Open-dialogues within your scripts. But also for those of us still on earlier releases, there's no need to despair!

 

This is how you get it done:

 

The key to accessing external file dialogues while executing your SAPGui Script, is Windows API functions. Basically, it's about finding the right window and bringing it to the foreground, so you can manipulate it via sendkey commands. It's first of all these two functions which you have to make use of:

Public Declare Function FindWindow Lib "user32" _

          (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

 

Private Declare Function SetForegroundWindow Lib "user32" _

         (ByVal hwnd As Long) As Long

 

If you can't find out the ipClassName (e.g. using tools like Spy++) and the FindWindow function won't find and return the window handle only by it's name, don't worry. There is a ready-to-use solution that's more complex but also more reliable: Access/VBA Tutorials - Bring an external application window to the foreground. (You can simply copy and paste it ...)

 

But be careful: If you refer to Gui-object methods (e.g. .pressToolbarButton "XYZ") to open the file-dialogue, your mission is already bound to fail: That's because your client has to wait for the server to answer until it can proceed. Meaning, the code that follows (bringing the save-dialogue to the foreground, sendkeys etc...) will have to wait in the queue.

 

That's why I am using sendkeys not only to fill-in and confirm the file-dialogue, but already to open it. This is how I got it solved, hope it will help some of you, too....

 

' ----------------------- Import Document -------------------------

  

Session.findById("wnd[0]/shellcont[0]/shell/shellcont[1]/shell").doubleClickItem "Key11", "Column1"

 

Call PressButton("Dokument importieren")         ' Finding the button turned out tricky for me, so I put it into a separate procedure, for better readability...
Application.Wait (Now + TimeSerial(0, 0, 3))      ' This pause makes sure the window is already open when you look for it....

 

bln = FnSetForegroundWindow("Öffnen")

  

SendKeys ThisWorkbook.Path & "\testmail.msg", True
SendKeys "{ENTER}", True

  

Application.Wait (Now + TimeSerial(0, 0, 1))

  

Session.findById("wnd[1]/usr/cmb/WSV/CA_DOC_IMPORT-LOB").Key = "ACC"
Session.findById("wnd[1]/usr/ctxt/WSV/CA_DOC_IMPORT-DOCCLASS").Text = "YOTH"
Session.findById("wnd[1]/usr/cmb/WSV/CA_DOC_IMPORT-VISIBILITY_LEVEL").Key = "D"

  

Review: Use Limits of SAP ActiveX Libraries in .NET Environment PowerShell

$
0
0

Hello community,

 

in a normal case you can use the COM libraries, like the SAP ActiveX, very easily in PowerShell scripting language.

 

$SAPFunc = New-Object -ComObject "SAP.Functions"

 

This should be sufficient to use the SAP ActiveX libraries. But in this case you don't have access to the members of the library.

001.JPG

At this point I use the way I described here, I use the helper functions. In this case you can use the ActiveX libraries, but you are missing the advantages of the Integrated Script Environment (ISE) of PowerShell, e.g. code completion.

 

To compensate this disadvantage I create with the type library importer (tlbimp) of the Microsoft .NET SDK an interoperability library.

 

[Reflection.Assembly]::LoadFile("C:\Dummy\Interop.wdtfuncs.dll")

$SAPFunc = New-Object Interop.wdtfuncs.SAPFunctionsClass

 

Now it works all as expected.

002.JPG

So far so good, but the next problem is in front of the door.

 

In a normal case you use the Connection property in the next step.

 

$Connection = $SAPFunc.Connection

 

It works, you get the object, but not all members.

003.JPG

Now I do the same steps with wdtlog.ocx and want to cast the object variable on different ways into the connection class type, but without success.

 

[Interop.wdtlog.ConnectionClass]$Connection = $SAPFunc.Connection

$Connection = [Interop.wdtlog.ConnectionClass]$SAPFunc.Connection

$Connection -As [Interop.wdtlog.ConnectionClass]

$Connection -Is [Interop.wdtlog.ConnectionClass]

 

As far as I can see it is not possible to cast a System.__ComObject to another in PowerShell.

 

Maybe someone has a solution for this problem.

 

Another insight: It is not in any case possible to use reference variables with COM libraries. If you work with an interoperability library it is as well not possible.

 

From these points it seems reasonable to use NCo, which I described here and here.

 

 

2015/09.20 - Addendum: The casting method that I described here doesn't work also. It crashes PowerShell with the errors System.AccessViolationException and System.RuntimeType.ForwardCallToInvokeMember at the point where the first property is assigned..

 

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

 

  #-Sub Main------------------------------------------------------------

    Function Main() {

 

      $Path = $PSScriptRoot

      [Reflection.Assembly]::LoadFile($Path + "\wdtlog.dll")

      [Reflection.Assembly]::LoadFile($Path + "\wdtfuncs.dll")

      [Reflection.Assembly]::LoadFile($Path + "\wdtaocx.dll")

 

      $SAPFunc = New-Object wdtfuncs.SAPFunctionsClass

      If ($SAPFunc -eq $Null) {

        Break

      }

 

      $SAPFunc.AboutBox()

 

      $Connection = $SAPFunc.Connection

      If ($Connection -eq $Null) {

        Break

      }

 

      [wdtlog.Connection]$Connection =

        [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Connection,

        [wdtlog.ConnectionClass])

 

      $Connection.Client = "001"

      $Connection.User = "BCUSER"

      $Connection.Password = "minisap"

      $Connection.Language = "EN"

      $Connection.System = "NSP"

      $Connection.HostName = "ABAP702"

      $Connection.SystemNumber = 0

    

      $SAPConnection = $Connection.Logon(0, -1)

      If ($SAPConnection -ne 0) {

        Write-Host "Check connection in SAP system with transactin SMGW"

 

      }

 

    }

 

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

    Main

 

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

 

Cheers

Stefan

How To Use NCo With PowerShell: Client Apps (PowerShell uses .NET Connector)

$
0
0

Hello community,

 

at the beginning of this year I presented here, here and here different possibilities to use SAPs .NET Connector (NCo) in the context of the scripting language PowerShell. Now with new experience and knowledge I can say that the use of this combination is very profitably. You can try different programming situations fast, without a special IDE. Also you can use it to automate a lot of things, e.g. to download and compare data (customizing on different systems). The PowerShell ISE (Integrated Script Environment) with the integrated debugger offers a good range of functions, e.g. like syntax highlighting and code completion. All in all I can recommend PowerShell unlimited. As a further outlook for the future, a combination of SAP GUI Scripting, NCo and PowerShell seems very interesting. We will see...

 

Here now two more examples. The first shows how to ping an SAP system via RFC_PING and the build-in ping from NCo. The second shows how to get system via the function module RFC_SYSTEM_INFO. The code is commented and I think easy to understand. It shows, in conjunction with the links above, all variants of parameters.

 

 

1st example - Ping

 

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


  <#

    .Synopsis

      Calls PING of an SAP system.

    .Description

      Calls the function module RFC_PING and the build-in function

      Ping of the .NET connector.

  #>

 

  #-Sub Load-NCo--------------------------------------------------------

    Function Load-NCo {

 

      $ScriptDir = $PSScriptRoot

 

      $Size = [System.IntPtr]::Size

      If ($Size -eq 4) {

        $Path = $ScriptDir + "\x86\"

      }

      ElseIf ($Size -eq 8) {

        $Path = $ScriptDir + "\x64\"

      }

 

      [Reflection.Assembly]::LoadFile($Path + "sapnco.dll") > $Null

      [Reflection.Assembly]::LoadFile($Path + "sapnco_utils.dll") > $Null

 

    }

 

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

    Function Get-Destination {

 

      #-Connection parameters-------------------------------------------

        $cfgParams = New-Object SAP.Middleware.Connector.RfcConfigParameters

        $cfgParams.Add("NAME", "TEST")

        $cfgParams.Add("ASHOST", "ABAP")

        $cfgParams.Add("SYSNR", "00")

        $cfgParams.Add("CLIENT", "001")

        $cfgParams.Add("USER", "BCUSER")

 

        $SecPasswd = Read-Host -Prompt "Passwort" -AsSecureString

        $ptrPasswd = [Runtime.InteropServices.Marshal]::SecureStringToBStr($SecPasswd)

        $Passwd = [Runtime.InteropServices.Marshal]::PtrToStringBStr($ptrPasswd)

        $cfgParams.Add("PASSWD", $Passwd)

 

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

 

    }

 

  #-Sub Execute-Ping----------------------------------------------------

    Function Execute-Ping () {

 

      $destination = Get-Destination

 

      #-Metadata--------------------------------------------------------

        [SAP.Middleware.Connector.IRfcFunction]$rfcFunction =

          $destination.Repository.CreateFunction("RFC_PING")

 

      #-Variant 1: Call function module---------------------------------

        Try {

          $rfcFunction.Invoke($destination)

          Write-Host "Ping successful"

        }

        Catch {

          Write-Host "Exception" $_.Exception.Message "occured"

        }

 

      #-Variant 2: Call build-in function-------------------------------

        Try {

          $destination.Ping()

          Write-Host "Ping successful"

        }

        Catch {

          Write-Host "Exception" $_.Exception.Message "occured"

        }

 

    }

 

  #-Sub Main------------------------------------------------------------

    Function Main () {

      If ($PSVersionTable.PSVersion.Major -ge 3) {

        Load-NCo

        Execute-Ping

      }

    }

 

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

    Main

 

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

 

 

2nd example - RFC_SYSTEM_INFO

 

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

 

  <#

    .Synopsis

      Calls RFC_SYSTEM_INFO of an SAP system.

    .Description

      Calls the function module RFC_SYSTEM_INFO and writes the result

      on the screen.

  #>

 

  #-Sub Load-NCo--------------------------------------------------------

    Function Load-NCo {

 

      $ScriptDir = $PSScriptRoot

 

      $Size = [System.IntPtr]::Size

      If ($Size -eq 4) {

        $Path = $ScriptDir + "\x86\"

      }

      ElseIf ($Size -eq 8) {

        $Path = $ScriptDir + "\x64\"

      }

 

      [Reflection.Assembly]::LoadFile($Path + "sapnco.dll") > $Null

      [Reflection.Assembly]::LoadFile($Path + "sapnco_utils.dll") > $Null

 

    }

 

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

    Function Get-Destination {

 

      #-Connection parameters-------------------------------------------

        $cfgParams = New-Object SAP.Middleware.Connector.RfcConfigParameters

        $cfgParams.Add("NAME", "TEST")

        $cfgParams.Add("ASHOST", "ABAP")

        $cfgParams.Add("SYSNR", "00")

        $cfgParams.Add("CLIENT", "001")

        $cfgParams.Add("USER", "BCUSER")

 

        $SecPasswd = Read-Host -Prompt "Passwort" -AsSecureString

        $ptrPasswd = [Runtime.InteropServices.Marshal]::SecureStringToBStr($SecPasswd)

        $Passwd = [Runtime.InteropServices.Marshal]::PtrToStringBStr($ptrPasswd)

        $cfgParams.Add("PASSWD", $Passwd)

 

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

 

    }

 

  #-Sub Get-SystemInfo--------------------------------------------------

    Function Get-SystemInfo () {

 

      $destination = Get-Destination

 

      #-Metadata--------------------------------------------------------

        [SAP.Middleware.Connector.IRfcFunction]$rfcFunction =

          $destination.Repository.CreateFunction("RFC_SYSTEM_INFO")

 

      #-Call function module--------------------------------------------

        Try {

          $rfcFunction.Invoke($destination)

 

          [SAP.Middleware.Connector.IRfcStructure]$Export =

            $rfcFunction.GetStructure("RFCSI_EXPORT")

 

          #-Get information---------------------------------------------

            Write-Host $Export.GetValue("RFCHOST")

            Write-Host $Export.GetValue("RFCSYSID")

            Write-Host $Export.GetValue("RFCDBHOST")

            Write-Host $Export.GetValue("RFCDBSYS")

 

        }

        Catch {

          Write-Host "Exception" $_.Exception.Message "occured"

        }

 

    }

 

  #-Sub Main------------------------------------------------------------

    Function Main () {

      If ($PSVersionTable.PSVersion.Major -ge 3) {

        Load-NCo

        Get-SystemInfo

      }

    }

 

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

    Main

 

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

 

If you like you can find the same functionalityhere, with SAP NetWeaver RFC SDK and FreeBASIC, to compare it.

 

Enjoy it.

 

Cheers

Stefan

From PowerShell Zero To SAP GUI Scripting Hero

$
0
0

Hello community,

 

I wrote here about the possibility to use SAP GUI Scripting inside PowerShell. The basis of this approach is Visual Basic Script (VBS) resp. the Microsoft Script Control. On this way it is very easy to use the existing scripts furthermore. Here I have compared the use of .NET invoke methods with the use of MSScriptControl. This was a step forward to .NET, but the disadvantage is that you can't use the advantages of the Integrated Script Environment (ISE) like code completion.

 

To walk around this shortcoming I searched for a method to cast the ___COMObject types to correct data types in a namespace of a .NET assembly. I found the method CreateWrapperOfType. All I have to do is to build with the type library importer (TLBIMP) a .NET assembly from the SAPFEWSE.OCX library - thats all. If you don't habe the possibility to build it at your own, look in your NWBC or SapDtsCOMFramework directory, here you find the assembly Interop.SAPFEWSELib.dll, it is also possible to use this assembly.

 

Hint: The assembly of the NWBC use the namespace Interop.SAPFEWSELib, the assembly of the Dynamic Test Script framework use only SAPFEWSELib. I find that very awkward. If you use the assembly of the NWBC you must modfy the types below and add Interop. in front of the type names.

 

A code line with a ___COMObject casting to another type of a SAP GUI Scripting object looks like this:

[SAPFEWSELib.GuiApplication]$Application =

    [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Application,

    [SAPFEWSELib.GuiApplicationClass])

Here I cast the variable $Application from the type System.___COMObject to GuiApplication - our base friend of the SAP GUI Scripting object hierarchy. I think it is easy to understand what happens.

 

Here now the complete code:

 

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

 

  [Reflection.Assembly]::LoadWithPartialName("Microsoft.VisualBasic")

 

  [Reflection.Assembly]::LoadFile("SAPFEWSELib.dll")

 

  $SapGuiAuto = [Microsoft.VisualBasic.Interaction]::GetObject("SAPGUI")

  If ($SapGuiAuto -eq $Null) {

    Break

  }

 

  $Application = $SapGuiAuto.GetType().InvokeMember("GetScriptingEngine",

        [System.Reflection.Bindingflags]::InvokeMethod,

        $null, $SapGuiAuto, $null, $null, $null, $null)

  [SAPFEWSELib.GuiApplication]$Application =

    [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Application,

    [SAPFEWSELib.GuiApplicationClass])

  If ($Application -eq $Null) {

    Break

  }

 

  $Connection = $Application.Children.Item(0)

  [SAPFEWSELib.GuiConnectionClass]$Connection =

    [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Connection,

    [SAPFEWSELib.GuiConnectionClass])

  If ($Connection -eq $Null) {

    Break

  }

 

  $Session = $Connection.Children.Item(0)

  [SAPFEWSELib.GuiSession]$Session =

    [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Session,

    [SAPFEWSELib.GuiSessionClass])

  If ($Session -eq $Null) {

    Break

  }

 

  $Mandt = $Session.FindById("wnd[0]/usr/txtRSYST-MANDT")

  [SAPFEWSELib.GuiTextField]$Mandt =

    [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Mandt,

    [SAPFEWSELib.GuiTextFieldClass])

  $BName = $Session.FindById("wnd[0]/usr/txtRSYST-BNAME")

  [SAPFEWSELib.GuiTextField]$BName =

    [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($BName,

    [SAPFEWSELib.GuiTextFieldClass])

  $Langu = $Session.FindById("wnd[0]/usr/txtRSYST-LANGU")

  [SAPFEWSELib.GuiTextField]$Langu =

    [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Langu,

    [SAPFEWSELib.GuiTextFieldClass])

  $BCode = $Session.FindById("wnd[0]/usr/pwdRSYST-BCODE")

  [SAPFEWSELib.GuiPasswordField]$BCode =

    [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($BCode,

    [SAPFEWSELib.GuiPasswordFieldClass])

 

  $Mandt.Text = "001"

  $BName.Text = "BCUSER"

  $Langu.Text = "EN"

  $Bcode.Text = "minisap"

 

  $MainWin = $Session.FindById("wnd[0]")

  [SAPFEWSELib.GuiMainWindow]$MainWin =

    [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($MainWin,

    [SAPFEWSELib.GuiMainWindowClass])

  $MainWin.SendVKey(0)

 

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

 

This example is equivalent to the examples in the links above. Sure it is more complex as VBScript, but it offers all adavantages of the integration in the .NET environment.

 

001.jpg

 

This possibility opens now the gate wide to use SAP GUI Scripting seamlessly in PowerShell and in any other language ot the .NET environment.

 

Hint after: As far as I know uses the NetWeaver Business Client (NWBC), version 4 and 5, as well as the Dynamic Test Scripts (DTS) framework the same method. SAP dts is a test framework which enables automation of testing SAP applications. Look at US patent US 20120023485 A1 for more information about DTS.

 

Enjoy it.

 

Cheers

Stefan


Use Named References of SAP ActiveX Libraries in VBA for Easy Programming

$
0
0

Hello community,

 

many Microsoft Office programmers uses the SAP ActiveX libraries of the SAP GUI for Windows installation. The reason for this descision is the availability of the libraries. To use the libraries it is necessary to reference it.

 

Menu Tools > References

101.jpg

Browse > Add Reference

102.jpg

It is necessary to add wdtfuncs.ocx, wdttaocx.ocx, wdtlog.ocx and optional wdobapi.ocx. You can find the files in the directory of the SAP GUI, C:\Program Files (x86)\SAP\FrontEnd\sapgui.

 

103.JPG

Hint: The wdtlog.ocx is in the directory C:\Program Files (x86)\Common Files\SAP Shared.

 

Hint: You can also use the unicode versions of the libraries, the filenames ends with an u.

 

104.jpg

So far the prelude. You can find a very good equivalent description here.

 

But many Office programmers declare their object variables in VBA only as Object. If they choose this method is it at first not necessary to reference the libraries and on second they lose the very good code completion functionality.


'-Begin-----------------------------------------------------------------

  Sub Test()

 

    '-Variables---------------------------------------------------------

      Dim oFunc As Object

      Dim oConn As Object

      Dim SAPConn As Integer

    

    Set oFunc = CreateObject("SAP.Functions.Unicode")

    If Not IsObject(oFunc) Then

       MsgBox "CreateObject(SAP.Functions.Unicode) failed", vbOKOnly, _

         "Error"

       Exit Sub

    End If

 

    Set oConn = oFunc.Connection()

    If Not IsObject(oConn) Then

      MsgBox "SAPFunc.Connection failed", vbOKOnly, "Error"

      Exit Sub

    End If

 

    SAPConn = oConn.Logon(0, vbFalse)

    If SAPConn <> 0 Then

 

      oConn.Logoff

    Else

      MsgBox "Connection.Logon failed", vbOKOnly, "Error"

    End If

 

  End Sub

'-End-------------------------------------------------------------------


If you want to benefit from the advantages, is it necessary to change the source like this:


'-Begin-----------------------------------------------------------------

  Sub Test()

 

    '-Variables---------------------------------------------------------

      Dim oFunc As SAPFunctionsOCX.SAPFunctions

      Dim oConn As SAPLogonCtrl.Connection

      Dim SAPConn As Integer

    

    Set oFunc = CreateObject("SAP.Functions.Unicode")

    If Not IsObject(oFunc) Then

       MsgBox "CreateObject(SAP.Functions.Unicode) failed", vbOKOnly, _

         "Error"

       Exit Sub

    End If

 

    Set oConn = oFunc.Connection()

    If Not IsObject(oConn) Then

      MsgBox "SAPFunc.Connection failed", vbOKOnly, "Error"

      Exit Sub

    End If

 

    SAPConn = oConn.Logon(0, vbFalse)

    If SAPConn <> 0 Then

 

      oConn.Logoff

    Else

      MsgBox "Connection.Logon failed", vbOKOnly, "Error"

    End If

 

  End Sub

'-End-------------------------------------------------------------------

 

As you can see is the source equivalent but the declarations of the variables oFunc and oConn are now not longer Objects, they are now definitive types from the library. And now the VBA IDE offers the code completion.

 

105.jpg

If you reference the SAP ActiveX libraries you should also declare your variables with the correct types, otherwise it makes no sense.

 

Hint: You can find information how to use SAP ActiveX libraries without SAP GUI for Windows here.

 

Cheers

Stefan

How to Use PowerShell for Automated Login to SAP

$
0
0

Hello community,

 

here is an interesting discussion how to automated logon to an SAP system via VBScript. But VBScript offers not really a function to detect the moment if the session is available. Here now an implementation how to do same with PowerShell. PowerShell offers with its interface to the Windows API an easy way to detect the availability of the new session.

 

Here now the code how to detect the availability of the session:

 

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

 

$Sig = @'

[DllImport("user32.dll", CharSet = CharSet.Auto)]

public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

'@

 

  #-Add FindWindow function---------------------------------------------

    $Win32 = Add-Type -Namespace Win32 -Name Funcs -MemberDefinition $Sig -PassThru

 

  #-Set the path to the SAP GUI directory-------------------------------

    $SAPGUIPath = "C:\Program Files (x86)\SAP\FrontEnd\SAPgui\"

 

  #-Set the SAP system ID or the IP address-----------------------------

    $SID = "NSP"

 

  #-Set the instance number of the SAP system---------------------------

    $InstanceNo = "00"

 

  #-Starts the SAP GUI--------------------------------------------------

    $SAPGUI = $SAPGUIPath + "sapgui.exe"

    & $SAPGUI $SID $InstanceNo

 

  While ($Win32::FindWindow("SAP_FRONTEND_SESSION", "SAP") -eq 0) {

    Start-Sleep -Milliseconds 250

  }

 

  Write-Host "Here now your script..."

 

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

 

Here now the code how to logon automatically:

 

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

 

  #-Includes------------------------------------------------------------

    ."COM.ps1"

 

  #-Signatures----------------------------------------------------------

    $Sig = @'

[DllImport("user32.dll", CharSet = CharSet.Auto)]

public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

'@

 

  #-Add FindWindow function---------------------------------------------

    $Win32 = Add-Type -Namespace Win32 -Name Funcs -MemberDefinition $Sig -PassThru

 

  #-Set the path to the SAP GUI directory-------------------------------

    $SAPGUIPath = "C:\Program Files (x86)\SAP\FrontEnd\SAPgui\"

 

  #-Set the SAP system ID-----------------------------------------------

    $SID = "localhost"

 

  #-Set the instance number of the SAP system---------------------------

    $InstanceNo = "00"

 

  #-Start the SAP GUI---------------------------------------------------

    $SAPGUI = $SAPGUIPath + "sapgui.exe"

    & $SAPGUI $SID $InstanceNo

 

  #-Wait until the session is available---------------------------------

    While ($Win32::FindWindow("SAP_FRONTEND_SESSION", "SAP") -eq 0) {

      Start-Sleep -Milliseconds 250

    }

 

  #-Logon to SAP GUI session--------------------------------------------

    $SapGuiAuto = Get-Object("SAPGUI")

    $application = Invoke-Method $SapGuiAuto "GetScriptingEngine"

    $connection = Get-Property $application "Children" @(0)

    $session = Get-Property $connection "Children" @(0)

 

    $ID = Invoke-Method $session "findById" @("wnd[0]/usr/txtRSYST-MANDT")

    Set-Property $ID "Text" @("001")

    $ID = Invoke-Method $session "findById" @("wnd[0]/usr/txtRSYST-BNAME")

    Set-Property $ID "Text" @("BCUSER")

    $ID = Invoke-Method $session "findById" @("wnd[0]/usr/pwdRSYST-BCODE")

    Set-Property $ID "Text" @("minisap")

    $ID = Invoke-Method $session "findById" @("wnd[0]/usr/txtRSYST-LANGU")

    Set-Property $ID "Text" @("EN")

 

    $ID = Invoke-Method $session "findById" @("wnd[0]")

    Invoke-Method $ID "sendVKey" @(0)

 

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

 

You can find the COM include file here.

 

Enjoy it.

 

Cheers

Stefan

How to Find the Next Logon Screen of a SID with SAP GUI Scripting

$
0
0

Hello community,

 

based on the discussion here I developed a function how to find the next logon screen of a given SID. The function delivers the connection number.

 

'-Begin--------------------------------------------------------------

 

  '-Directives-------------------------------------------------------

    Option Explicit

 

  '-Function GetConnectionNo-----------------------------------------

  '-

  '- Function to get connection number of the given SID with

  '- logon screen

  '-

  '------------------------------------------------------------------

    Function GetConnectionNo(Application, SID)

 

      '-Variables----------------------------------------------------

     Dim i, Connection, SessionInfo, ConnectionNo

 

      If Application.Children.Count > 0 Then

        For i = 0 To Application.Children.Count - 1

          Set Connection = Application.Children(CInt(i))

          Set SessionInfo = Connection.Children(0).Info

          If SessionInfo.SystemName = SID And _

            SessionInfo.Program = "SAPMSYST" Then

            ConnectionNo = Mid(Connection.Id, InStr(Connection.Id, "[") + 1)

            ConnectionNo = Left(ConnectionNo, Len(ConnectionNo) - 1)

            GetConnectionNo = CStr(ConnectionNo)

          End If

        Next

      Else

        GetConnectionNo = -1

      End If

 

    End Function

 

  '-Sub Main---------------------------------------------------------

    Sub Main()

 

      '-Variables----------------------------------------------------

        Dim SapGuiAuto, Application, ConnNo

 

      If Not IsObject(application) Then

        Set SapGuiAuto  = GetObject("SAPGUI")

        Set Application = SapGuiAuto.GetScriptingEngine

      End If

 

      ConnNo = GetConnectionNo(Application, "NSP")

 

      MsgBox ConnNo

 

    End Sub

 

  '-Main-------------------------------------------------------------

    Main

 

'-End----------------------------------------------------------------

 

Enjoy it.

 

Cheers

Stefan

How to Find the Next Not Busy Session of a SID in AutoIt

$
0
0

Hello community,

 

via e-mail I received a question how to get the next input-ready session of a SID in AutoIt scripting language. Here a solution.

 

It should now not be difficult to combine this approach with this approach to find a special session, e.g. the logon screen. It is only necessary to add the SessionInfo.Program attribute.

 

;-Begin-----------------------------------------------------------------

 

  ;-Directives----------------------------------------------------------

    AutoItSetOption("MustDeclareVars", 1)

 

  ;-Function GetSessionOfSID--------------------------------------------

  ;-

  ;- Delivers the first non busy session of a SID

  ;-

  ;---------------------------------------------------------------------

    Func GetSessionOfSID($Application, $SID)

 

      ;-Local Variables-------------------------------------------------

        Local $i, $Connection, $j, $Session, $SessionInfo

   

      If $Application.Children.Count > 0 Then

        For $i = 0 To $Application.Children.Count - 1

          $Connection = $Application.Children($i)

          For $j = 0 To $Connection.Children.Count - 1

            If $Connection.Children($j).Busy = False Then

              $Session = $Connection.Children($j)

              $SessionInfo = $Session.Info

              If $SessionInfo.SystemName = $SID Then

                Return $Session

              EndIf

            EndIf

          Next

        Next

      EndIf

   

    EndFunc

 

  ;-Sub Main------------------------------------------------------------

    Func Main()

 

      ;-Local Variables-------------------------------------------------

        Local $SAPROT, $SapGuiAuto, $Application, $Session

   

      $SAPROT = ObjCreate("SapROTWr.SAPROTWrapper")

      If Not IsObj($SAPROT) Then

        Return

      EndIf

 

      $SapGuiAuto = $SAPROT.GetROTEntry("SAPGUI")

      If Not IsObj($SapGuiAuto) Then

        Return

      EndIf

 

      $Application = $SapGuiAuto.GetScriptingEngine()

      If Not IsObj($Application) Then

        Return

      EndIf

 

      $Session = GetSessionOfSID($Application, "NSP")

      If IsObj($Session) Then

 

        ;Your script code here

        MsgBox(0, "", $Session.Id)

 

      EndIf

     

      $Application = 0

      $SapGuiAuto = 0

      $SAPROT = 0

     

    EndFunc

 

  ;-Main----------------------------------------------------------------

    Main()

 

;-End-------------------------------------------------------------------

 

Enjoy it.

 

Cheers

Stefan

Tip: How to Restart the SAPLogon via AutoIt

$
0
0

Hello community,

 

AutoIt is an incredible scripting language, it offers so much possibilities, e.g. to control processes and windows. Here now an example how to restart the SAPLogon on the fly:

 

;-Begin-----------------------------------------------------------------

 

  ;-Sub RestartProcess--------------------------------------------------

    Func RestartProcess($ProcessName)

 

      $PID = ProcessExists($ProcessName)

      If $PID Then

 

        $oWMI = ObjGet("winmgmts:\\.\root\CIMV2")

        If IsObj($oWMI) Then

          $Process = $oWMI.ExecQuery("Select * From win32_process " & _

            "Where Name = '" & $ProcessName & "'")

          If IsObj($Process) Then

            $ProcessPath = $Process.ItemIndex(0).ExecutablePath

          EndIf

        EndIf

 

        ProcessClose($PID)

        ProcessWait($PID)

 

        Run($ProcessPath)

 

      EndIf

 

    EndFunc

 

  ;-Sub Main------------------------------------------------------------

    Func Main()

 

      RestartProcess("saplogon.exe")

      WinWait("SAP Logon ")

      Sleep(4096)

 

    EndFunc

 

  ;-Main----------------------------------------------------------------

    Main()

 

;-End-------------------------------------------------------------------

 

As you can see it is absolut easy to get the process ID (PID), to close the process and to restart the process - here with the aid of Windows Management Instrumentarium (WMI).

 

Important hint: This script closes and restarts the saplogon process immediately, without any requests - all changes will be lost.

 

This script is excellent for stray processes on the frontend server.

 

Enjoy it.

 

Cheers

Stefan

SAP GUI Scripting Recorder with PowerShell

$
0
0

Hello community,

 

here a SAP GUI Scripting Recorder in PowerShell scripting language.

 

#-Begin-----------------------------------------------------------------  #-Sub Main------------------------------------------------------------    Function Main() {      [Reflection.Assembly]::LoadWithPartialName("Microsoft.VisualBasic") > $Null      [Reflection.Assembly]::LoadFile($PSScriptRoot + "\SAPFEWSELib.dll") > $Null      $SapGuiAuto = [Microsoft.VisualBasic.Interaction]::GetObject("SAPGUI")      If ($SapGuiAuto -eq $Null) {        Break      }      $Application = $SapGuiAuto.GetType().InvokeMember("GetScriptingEngine",        [System.Reflection.Bindingflags]::InvokeMethod,        $null, $SapGuiAuto, $null, $null, $null, $null)      [SAPFEWSELib.GuiApplication]$Application =        [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Application,        [SAPFEWSELib.GuiApplicationClass])      If ($Application -eq $Null) {        Break      }      $Connection = $Application.Children.Item(1)      [SAPFEWSELib.GuiConnectionClass]$Connection =        [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Connection,        [SAPFEWSELib.GuiConnectionClass])      If ($Connection -eq $Null) {        Break      }      $Session = $Connection.Children.Item(0)      [SAPFEWSELib.GuiSession]$Session =        [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Session,        [SAPFEWSELib.GuiSessionClass])      If ($Session -eq $Null) {        Break      }      $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        }        #$event.SourceArgs        Write-Host "ID: " $event.SourceArgs[1].Id        Write-Host "Type / Method / Parameter: " $event.SourceArgs[2].SyncRoot        Remove-Event -SourceIdentifier "Action"      }      Unregister-Event -SourceIdentifier "Action"      $Session.Record = $False    }  #-Main----------------------------------------------------------------    Main
#-End--------------------------------------------------------------------

 

As you can see I use the normal SAP GUI Scripting commands to get the application, connection and session. I set the property record of the session object to true and register the change event to record my activities. Now in the while loop, which exits after 10 seconds if no event is fired, I catch the parameters via $event.SourceArgs. Last but not least I unregister the event and set record to false.

 

Here the result of one activity:

 

Waiting for event...

 

Name                   : ses[0]

Type                   : GuiSession

TypeAsNumber           : 12

ContainerType          : True

Id                     : /app/con[1]/ses[0]

Parent                 : System.__ComObject

Children               : System.__ComObject

ActiveWindow           : System.__ComObject

Info                   : System.__ComObject

Record                 : True

TestToolMode           : 0

RecordFile             :

Busy                   : False

IsActive               : True

SaveAsUnicode          : True

ShowDropdownKeys       : False

PassportTransactionId  : 0A64C954920015101318093545917000

PassportPreSystemId    : CRS/crs_CRS_92

PassportSystemId       : CRS/crs_CRS_92

ErrorList              : System.__ComObject

AccEnhancedTabChain    : False

AccSymbolReplacement   : False

IsListBoxActive        : False

ListBoxTop             : 0

ListBoxLeft            : 0

ListBoxWidth           : 0

ListBoxHeight          : 0

ListBoxCurrEntryTop    : 0

ListBoxCurrEntryLeft   : 0

ListBoxCurrEntryWidth  : 0

ListBoxCurrEntryHeight : 0

ListBoxCurrEntry       : -1

ProgressPercent        : 0

ProgressText           :

SuppressBackendPopups  :

 

Name          : shell

Type          : GuiShell

TypeAsNumber  : 122

ContainerType : True

Id            : /shell

Parent        :

 

Length         : 3

LongLength     : 3

Rank           : 1

SyncRoot       : {SP, selectedNode, F00002}

IsReadOnly     : False

IsFixedSize    : True

IsSynchronized : False

Count          : 3

 

Waiting for event...

No event received for 10 seconds.

 

As you can see you get a lot of information. Important is the ID and the SyncRoot. With these information you can build a scripting recorder of your mind.

 

Enjoy it.

 

Cheers

Stefan

New NWBC Property at GUIConnectionClass of SAP GUI Scripting API

$
0
0

Hello community,

 

in the actual version 7400.2.5.255 offers the SAP GUI Scripting API a new property in the GUIConnectionClass, called ChildrenForNWBC.

It is not documented yet. It delivers an IDispatch of GUIComponentenCollection, like Children property.

The difference between ChildrenForNWBC and Children is at the moment not clear, because ChildrenForNWBC delivers also a result if NWBC doesn't run. This means it delivers the same result when it is called only with SAP GUI for Windows and the result is also the same as Children property.

 

Here a picture of the methods and properties of ChildrenForNWBC.

001.JPG

 

Cheers

Stefan


Parallel Using of Identical DisplayNames in the Running Object Table (ROT)

$
0
0

Hello community,

 

a few month ago I wrote here about the non uniqueness of IDs of the parallel using of SAP GUI for Windows and NWBC with SAP GUI Scripting. Also I wrote here about polyvalence entries from SAP GUI for Windows and NWBC in the Running Object Table (ROT). Here is now a new variant:

If you use the SAP GUI for Windows and NWBC parallel and you call in a script the method GetScriptingEngine, after GetObject with SAPGUISERVER - the DisplayName of the NWBC - , the entry of SAPGUISERVER is doubled on a SAPGUI entry. Thus you have the problem, if you run SAP GUI for Windows and NWBC at the same time, that you can't differentiate clearly between the SAPGUI objects from the ROT, because it exists twice by name. The DisplayName is in this case not unique. You have two entries with an equal DisplayName at the same time. I try it with the SAP GUI for Windows 7.40 PL 5 and the NWBC 5 PL 9.

 

Hint: The problem which I described here is also present.

 

All in all it is better, from the perspective of the SAP GUI Scripting, to use SAP GUI for Windows and NWBC not parallel and to open only one instance of the NWBC at the same time.

 

Cheers

Stefan

Recorded Script in SAP to open more than the first session.

$
0
0

Hi I am totally new to SAP. So please bear with me and my missing skills. I have tried to record a session to log into SAP. And open the first session i need.

 

It works good. But i normally work with more than one session. I tried to record this but it only create 2 new sessions. Below is my recorded script.

 

I need to open 2 more with the name fbl5n and udm_bp. My script below open the first session and then it just create 2 blank sessions.

 

Please try help with this. I have of course in below script removed my password.

 

Sincerely

 

Abjac

 

If Not IsObject(application) Then

   Set SapGuiAuto  = GetObject("SAPGUI")

   Set application = SapGuiAuto.GetScriptingEngine

End If

If Not IsObject(connection) Then

   Set connection = application.Children(0)

End If

If Not IsObject(session) Then

   Set session    = connection.Children(0)

End If

If IsObject(WScript) Then

   WScript.ConnectObject session,     "on"

   WScript.ConnectObject application, "on"

End If

session.findById("wnd[0]").maximize

session.findById("wnd[0]/usr/txtRSYST-BNAME").text = "Y99DEW838"

session.findById("wnd[0]/usr/pwdRSYST-BCODE").text = "***********"

session.findById("wnd[0]/usr/pwdRSYST-BCODE").setFocus

session.findById("wnd[0]/usr/pwdRSYST-BCODE").caretPosition = 11

session.findById("wnd[0]").sendVKey 0

session.findById("wnd[0]/tbar[0]/okcd").text = "/n/hoag/akon "

session.findById("wnd[0]/tbar[0]/btn[0]").press

session.findById("wnd[0]/tbar[1]/btn[17]").press

session.findById("wnd[1]/usr/cntlALV_CONTAINER_1/shellcont/shell").selectedRows = "0"

session.findById("wnd[1]/usr/cntlALV_CONTAINER_1/shellcont/shell").doubleClickCurrentCell

session.findById("wnd[0]/tbar[1]/btn[8]").press

session.createSession

session.createSession

Function FindAllByType and FindAllByTypeEx in SAP GUI Scripting

$
0
0

Hello community,

 

here are two functions to detect all controls of one type of a collection, called FindAllByType and FindAllByTypeEx. The function FindAllByType has the parameters obj which describes a collection of objects and strType which describes the type of the control as string, e.g. GuiLabel. The function FindAllByTypeEx has the parameters obj which describes a collection of objects and lngType which describes the type of the control as long, e.g. 30 for GuiLabel. Both delivers an array of objects of the required type of the control.

 

'-Begin-----------------------------------------------------------------

 

  '-Directives----------------------------------------------------------

    Option Explicit

 

  '-Global Variables----------------------------------------------------

    Dim gColl()

 

  '-Function FindAllByType----------------------------------------------

    Function FindAllByType(Obj, strType)

 

      '-Variables-------------------------------------------------------   

        Dim cntObj, i, j, Child

 

      On Error Resume Next    

      cntObj = Obj.Children.Count()    

      If cntObj > 0 Then    

        For i = 0 To cntObj - 1    

          Set Child = Obj.Children.Item(CLng(i))    

          FindAllByType Child, strType

          If UCase(Child.Type()) = UCase(strType) Then

            ReDim Preserve gColl(j)

            Set gColl(j) = Child

            j = j + 1

          End If

        Next    

      End If    

      On Error Goto 0  

      FindAllByType = gColl

 

    End Function

 

  '-Function FindAllByType----------------------------------------------

    Function FindAllByTypeEx(Obj, lngType)

 

      '-Variables-------------------------------------------------------   

        Dim cntObj, i, j, Child

 

      On Error Resume Next    

      cntObj = Obj.Children.Count()    

      If cntObj > 0 Then    

        For i = 0 To cntObj - 1    

          Set Child = Obj.Children.Item(CLng(i))    

          FindAllByTypeEx Child, lngType

          If Child.TypeAsNumber() = lngType Then

            ReDim Preserve gColl(j)

            Set gColl(j) = Child

            j = j + 1

          End If

        Next    

      End If    

      On Error Goto 0  

      FindAllByTypeEx = gColl

 

    End Function

 

  '-Sub Main------------------------------------------------------------

    Sub Main()

 

      '-Variables-------------------------------------------------------

        Dim SapGuiAuto, application, connection, session, Coll, i

        Dim OutText

 

      If Not IsObject(application) Then

        Set SapGuiAuto = GetObject("SAPGUI")

        Set application = SapGuiAuto.GetScriptingEngine

      End If

 

      If Not IsObject(connection) Then

        Set connection = application.Children(0)

      End If

 

      If Not IsObject(session) Then

        Set session = connection.Children(0)

      End If 

 

      Erase gColl

      Coll = FindAllByType(session, "GuiLabel")

      For i = 0 To UBound(Coll)

        OutText = OutText & Coll(i).ID() & vbCrLf

      Next

 

      Erase gColl

      Coll = FindAllByTypeEx(session, 31) 'GuiTextField

      For i = 0 To UBound(Coll)

        OutText = OutText & Coll(i).ID() & vbCrLf

      Next

 

      MsgBox OutText

   

    End Sub

 

  '-Main----------------------------------------------------------------

    Main

 

'-End-------------------------------------------------------------------

 

Enjoy it.

 

Cheers

Stefan

New SAP GUI Scripting API help

$
0
0

Hello community,

 

the actual patch level 5 of the SAP GUI for Windows 7.40 offers a facelifted SAP GUI Scripting API help.

 

The font styles has been changed.

001.jpg

The node structure has been changed - the nodes Overview and Members does not exists anymore and for the single methods and properties there are also no longer own nodes.

002.jpg

The syntax description is now in VB.NET.

003.jpg

The file size is significantly reduced.

 

All in all the new help file looks much smarter - thank you for that

 

Cheers

Stefan

How to make SAP ROT Wrapper library available in a 64-bit environment

$
0
0

Hello community,

 

the SAP ROT (Running Object Table) Wrapper library offers the possibility to get access to the ROT. But the library is only for x86 - 32-bit. With the standard installation is it not possible to use the library with x64 programming languages. Here is a description how to make the SAP ROT Wrapper library available for this kind of environments:

 

  1. Add in the registry the entry DllSurrogate in the key HKCR\Wow6432Node\AppID\{E2779C61-F87E-4038-98A0-1D9E71334706} without a value.
    999.JPG
  2. Create a new key in HKLM\Software\Classes\AppID with the GUID {E2779C61-F87E-4038-98A0-1D9E71334706} without any values.

 

That is all, now you can enjoy the the SAP GUI ROT Wrapper in an x64 environment.

 

Here a test script in PowerShell:

 

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

 

  #-Sub Main------------------------------------------------------------

    Function Main() {

 

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

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

 

      $Size = [System.IntPtr]::Size

      If ($Size -eq 4) {

        Write-Host "`r`nVersion x86`r`n"

      }

      ElseIf ($Size -eq 8) {

        Write-Host "`r`nVersion x64`r`n"

      }

 

      $Wrapper = New-Object -ComObject "SapROTWr.SapROTWrapper"

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

      #$Wrapper = [Microsoft.VisualBasic.Interaction]::CreateObject("SapROTWr.SapROTWrapper")

      If ($Wrapper -eq $Null) {

          Break

      }

 

      $RotSAPGUI = $Wrapper.GetROTEntry("SAPGUI")

      If ($RotSAPGUI -eq $Null) {

        Break

      }

 

      $Application = $RotSAPGUI.GetType().InvokeMember("GetScriptingEngine",

        [System.Reflection.Bindingflags]::InvokeMethod,

        $null, $RotSAPGUI, $null, $null, $null, $null)

      [sapfewselib.GuiApplication]$Application =

        [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Application,

        [sapfewselib.GuiApplicationClass])

      If ($Application -eq $Null) {

        Break

      }

 

      $Connection = $Application.Children.Item(0)

      [sapfewselib.GuiConnectionClass]$Connection =

        [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Connection,

        [sapfewselib.GuiConnectionClass])

      If ($Connection -eq $Null) {

        Break

      }

 

      $Session = $Connection.Children.Item(0)

      [sapfewselib.GuiSession]$Session =

        [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($Session,

        [sapfewselib.GuiSessionClass])

      If ($Session -eq $Null) {

        Break

      }

 

      $SessionInfo = $Session.Info

      [sapfewselib.GuiSessionInfo]$SessionInfo =

        [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($SessionInfo,

        [sapfewselib.GuiSessionInfoClass])

      If ($SessionInfo -eq $Null) {

        Break

      }

 

      Write-Host $SessionInfo.SystemName

      Write-Host $SessionInfo.Client

      Write-Host $SessionInfo.Transaction

 

    }

 

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

    Main

 

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

 

998.jpg

 

Hint:It could be possible that the SAP GUI ROT wrapper has another GUID. You can find it in the GuidAttribute of the manifest of the assembly.

 

Enjoy it.

 

Cheers

Stefan

Viewing all 68 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>