Skip to Content

More and more loses the SAP GUI for Windows his unique position as primary UI and also more and more are desktop applications replaced with web applications. The SAP GUI for Windows offers for automation approaches the SAP GUI Scripting, a consolidated and technical reliable way. For web browser wrotes Former Member in his great blog about START (Simple Test Automation for Regression Tests) an UI Automation Framework to automate the testing of application. START bases on Selenium WebDrivers. So I think it could be advantageous to combine SAP GUI Scripting and Selenium.

 

Java

So I advanced my Scripting Tracker to create also Java code, to offer the possibilty to combine SAP GUI Scripting on this language platform. To use SAP GUI Scripting with Java you need the Java COM Bridge (JaCoB).

For the web activities I use in my context the chrome web driver, an x86 application, because I use the Chrome browser. So it is necessary to use the x86 (i586) version of the JDK, also for the SAP GUI for Windows. To record the activities on the web browser I use Firefox with Selenium IDE. You see it is a very heterogeneous development environment. But after all, it works. Here an example code how to combine SAP GUI for Windows activities with any activities in a web browser. In the example I combine information from the TAC SE16 from the table DEMO_CR_CUSTOMRS with a web dynpro application DEMO_WD_CAR_RENTAL – this is not really useful but shows the horizons.

//-Begin----------------------------------------------------------------

package de.stschnell;

  //-Import-------------------------------------------------------------
  import com.jacob.activeX.*;
  import com.jacob.com.*;
  import java.util.HashMap;
  import java.util.Map;
  import org.openqa.selenium.*;
  import org.openqa.selenium.chrome.ChromeDriver;
  import org.openqa.selenium.chrome.ChromeOptions;
  import org.openqa.selenium.remote.*;
  
public class SAPGUIScriptingWithSelenium {

  public static void main(String[] args) {

    //-Variables--------------------------------------------------------
    ActiveXComponent SAPROTWr, GUIApp, Connection, Session, Obj;
    Dispatch ROTEntry;
    Variant Value, ScriptEngine;
    String cnt = "0";

    ComThread.InitSTA();

    //-Set SapGuiAuto = GetObject("SAPGUI")-----------------------------
    SAPROTWr = new ActiveXComponent("SapROTWr.SapROTWrapper");
    try {
      ROTEntry = SAPROTWr.invoke("GetROTEntry", "SAPGUI").toDispatch();
      //-Set application = SapGuiAuto.GetScriptingEngine----------------
      ScriptEngine = Dispatch.call(ROTEntry, "GetScriptingEngine");
      GUIApp = new ActiveXComponent(ScriptEngine.toDispatch());

      //-Set connection = application.Children(0)-----------------------
      Connection = new ActiveXComponent(
        GUIApp.invoke("Children", 0).toDispatch()
      );
      //-Set session = connection.Children(0)---------------------------
      Session = new ActiveXComponent(
        Connection.invoke("Children", 0).toDispatch()
      );

      //-Open SE16------------------------------------------------------
      Obj = new ActiveXComponent(Session.invoke("findById", 
        "wnd[0]/tbar[0]/okcd").toDispatch());
      Obj.setProperty("text", "/nse16");
      Obj = new ActiveXComponent(Session.invoke("findById", 
        "wnd[0]").toDispatch());
      Obj.invoke("sendVKey", 0);

      //-Open selection view for table DEMO_CR_CUSTOMRS-----------------
      Obj = new ActiveXComponent(Session.invoke("findById", 
        "wnd[0]/usr/ctxtDATABROWSE-TABLENAME").toDispatch());
      Obj.setProperty("text", "DEMO_CR_CUSTOMRS");
      Obj = new ActiveXComponent(Session.invoke("findById", 
        "wnd[0]/tbar[1]/btn[7]").toDispatch());
      Obj.invoke("press");

      //-Open dialog "Number of Entries"--------------------------------
      Obj = new ActiveXComponent(Session.invoke("findById", 
        "wnd[0]/tbar[1]/btn[31]").toDispatch());
      Obj.invoke("press");

      //-Get the number of entries--------------------------------------
      Obj = new ActiveXComponent(Session.invoke("findById", 
        "wnd[1]/usr/txtG_DBCOUNT").toDispatch());
      Value = Obj.getProperty("text");
      cnt = Value.toString();

    } catch (Exception e) {
      System.out.println(
        e.getMessage().toString()
      );
    } finally {
      ComThread.Release();
    }
          
          
    //-If number of entries > 0-----------------------------------------
    if (cnt != "0") {

      //-Set path to chromedriver---------------------------------------
      System.setProperty(
        "webdriver.chrome.driver",
        "C:/Program Files/Selenium/chromedriver.exe");

      //-Set path to chrome browser-------------------------------------
      Map<String, Object> chromeOptions = new HashMap<String, Object>();
      chromeOptions.put("binary", "C:/Program Files/Google/Chrome/Application/chrome.exe");
      DesiredCapabilities capabilities = DesiredCapabilities.chrome();
      capabilities.setCapability(ChromeOptions.CAPABILITY, chromeOptions);        
    
      //-Opens a web browser window-------------------------------------
      WebDriver driver = new ChromeDriver(capabilities);

      //-Do your activities in the browser------------------------------
      driver.get("http://nsp.stschnell.de:8630/sap/bc/webdynpro/sap/demo_wd_car_rental");
      driver.findElement(By.id("WD1C")).clear();
      driver.findElement(By.id("WD1C")).sendKeys("00000001");
      driver.findElement(By.cssSelector("span.urBtnCntTxt")).click();

      driver.close();
      driver.quit();

    }

    System.exit(0);
  
  }          

}

//-End------------------------------------------------------------------

Here Scripting Tracker which  records Java code from the SAP GUI for Windows activities:

Here Firefox with Selenium IDE to record web activities:

Here the SAP GUI for Windows with SAP GUI Scripting and Eclipse in debug mode side by side:

Here Chrome with Eclipse in debug mode side by side:

 

PowerShell

A longer time ago I wrote here about the possibility to use PowerShell with SAP GUI Scripting. Selenium offers, besides Java, also dotNET libraries. On this way it is possible to combine SAP GUI Scripting with the WebDriver also on the base of PowerShell.

Here the same example in PowerShell.

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

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

  #-Set SapGuiAuto = GetObject("SAPGUI")--------------------------------
  $SapGuiAuto = Get-Object( , "SAPGUI")
  If ($SapGuiAuto -isnot [__ComObject]) {
    Exit
  }

  #-Set application = SapGuiAuto.GetScriptingEngine---------------------
  $application = Invoke-Method $SapGuiAuto "GetScriptingEngine"
  If ($application -isnot [__ComObject]) {
    Free-Object $SapGuiAuto
    Exit
  }

  #-Set connection = application.Children(0)----------------------------
  $connection = Get-Property $application "Children" @(0)
  If ($connection -eq $Null) {
    Free-Object $SapGuiAuto
    Exit
  }

  #-Set session = connection.Children(0)--------------------------------
  $session = Get-Property $connection "Children" @(0)
  If ($session -eq $Null) {
    Free-Object $SapGuiAuto
    Exit
  }

  #-Open SE16-----------------------------------------------------------
  $ID = Invoke-Method $session "findById" @("wnd[0]/tbar[0]/okcd")
  Set-Property $ID "text" @("/nse16")
  $ID = Invoke-Method $session "findById" @("wnd[0]")
  Invoke-Method $ID "sendVKey" @(0)

  #-Open selection view for table DEMO_CR_CUSTOMRS----------------------
  $ID = Invoke-Method $session "findById" @("wnd[0]/usr/ctxtDATABROWSE-TABLENAME")
  Set-Property $ID "text" @("DEMO_CR_CUSTOMRS")
  $ID = Invoke-Method $session "findById" @("wnd[0]/tbar[1]/btn[7]")
  Invoke-Method $ID "press"

  #-Open dialog "Number of Entries"-------------------------------------
  $ID = Invoke-Method $session "findById" @("wnd[0]/tbar[1]/btn[31]")
  Invoke-Method $ID "press"

  #-Get the number of entries-------------------------------------------
    $ID = Invoke-Method $session "findById" @("wnd[1]/usr/txtG_DBCOUNT")
    $Value = Get-Property $ID "text"

  #-If number of entries > 0--------------------------------------------
  If ($Value -ne 0) {
  
    #-Load libraries----------------------------------------------------
    [System.Reflection.Assembly]::LoadFrom("C:\Program Files\Selenium\Selenium.WebDriverBackedSelenium.dll")
    [System.Reflection.Assembly]::LoadFrom("C:\Program Files\Selenium\WebDriver.dll")
    [System.Reflection.Assembly]::LoadFrom("C:\Program Files\Selenium\WebDriver.Support.dll")

    #-Set path to chrome browser----------------------------------------
    $Options = New-Object OpenQA.Selenium.Chrome.ChromeOptions
    $Options.BinaryLocation = "C:/Program Files/Google/Chrome/Application/chrome.exe"

    #-Opens a web browser window----------------------------------------
    $WebDriver = New-Object OpenQA.Selenium.Chrome.ChromeDriver("C:\Program Files\Selenium", $Options)
    $WebDriver.Url = "http://nsp.stschnell.de:8630/sap/bc/webdynpro/sap/demo_wd_car_rental"

    #-Do your activities in the browser---------------------------------
    $WebDriver.FindElementById("WD1C").Clear()
    $WebDriver.FindElementById("WD1C").SendKeys("00000001")
    $WebDriver.FindElementByCssSelector("span.urBtnCntTxt").click()

    $WebDriver.Close()
    $WebDriver.Quit()

  }

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

 

On this ways is it possible to combine SAP GUI Scripting with e.g. WebDynpro or UI5 applications. So you can reach a higher integration level in your automation approaches.

Enjoy it.

Cheers
Stefan

 

To report this post you need to login first.

21 Comments

You must be Logged on to comment or reply to a post.

  1. Former Member

    Thanks for this.I was able to login and search for some objects in SAP UI.Now i am stucked in reading data from a grid.After searching system generate a grid.Now i want to read the data from the grid .

    I can click on grid cells and rows but i can not read the data.How to read the data from grid.

    sample code of clicking a row and cell

    Obj = new ActiveXComponent(Session.invoke("findById", "wnd[0]/usr/cntlGRID1/shellcont/shell/shellcont[1]/shell").toDispatch());
    Obj.setProperty("selectedRows", "0");
    Obj = new ActiveXComponent(Session.invoke("findById", "wnd[0]/usr/cntlGRID1/shellcont/shell/shellcont[1]/shell").toDispatch());
    Obj.invoke("doubleClickCurrentCell");
    (1) 
    1. Stefan Schnell Post author

      Hello Mohamed,

      thanks for your reply.

      Sorry but I have no Java sample code to read an ALV grid, but you can find here and here examples in VBScript. I assume that it should looks like this:

      Table = new ActiveXComponent(Session.invoke("findById", "wnd[0]/usr/cntlGRID1/shellcont/shell/shellcont[1]/shell").toDispatch());
      Rows = Table.getProperty("RowCount") - 1;
      Cols = Table.getProperty("ColumnCount") - 1;
      for (int i = 0; i < Rows; i++) {
        for (int j = 0; j < Cols; j++) {
          string Value = Table.invoke("GetCellValue", i, j);
        }
      }

      Let us know your results.

      Best regards
      Stefan

      (1) 
      1. Former Member

         

        Thanks for the response.

           Rows = Table.getProperty(“RowCount”) – 1;

           Cols = Table.getProperty(“ColumnCount”) – 1;

        above returns the no of column and rows.But

            Table.invoke(“GetCellValue”, 1, 1);

        this returns an exception.I have tried with different  row and column id’s as well.

        Exception – Method threw ‘com.jacob.com.ComFailException’ exception.

        Detailed message -com.jacob.com.ComFailException: Invoke of: GetCellValue
        Source: SAP Frontend Server
        Description:

         

        Stack trace –

        0 = {StackTraceElement@1577} “com.jacob.com.Dispatch.invokev(Native Method)”
        1 = {StackTraceElement@1578} “com.jacob.com.Dispatch.invokev(Dispatch.java:625)”
        2 = {StackTraceElement@1579} “com.jacob.com.Dispatch.callN(Dispatch.java:453)”
        3 = {StackTraceElement@1580} “com.jacob.com.Dispatch.call(Dispatch.java:541)”
        4 = {StackTraceElement@1581} “com.jacob.activeX.ActiveXComponent.invoke(ActiveXComponent.java:453)”

        (0) 
        1. Stefan Schnell Post author

          Hello Mohamed,

          try this:

          arg = new Variant[2];
          Table = new ActiveXComponent(Session.invoke("findById", "wnd[0]/usr/cntlGRID1/shellcont/shell/shellcont[1]/shell").toDispatch());
          Rows = Table.getProperty("RowCount") - 1;
          Cols = Table.getProperty("ColumnCount") - 1;
          for (int i = 0; i < Rows; i++) {
            for (int j = 0; j < Cols; j++) {
              arg[0] = new Variant(i);
              arg[1] = new Variant(j);
              string Value = Table.invoke("GetCellValue", arg);
            }
          }
          

          Let us know your results.

          Cheers
          Stefan

          (0) 
          1. Former Member

             

            Thanks for the reply.Appreciate it.The above code also returns the same error.But I am lucky enough to come across a code which worked for me.

            Dispatch dispatch = Session.invoke("findById", "wnd[0]/usr/cntlGRID1/shellcont/shell/shellcont[1]/shell").toDispatch();
            Dispatch.call(dispatch,"GetCellValue",1,"objectid").toString();
            
            
            
            The above code returns the row 1's objectid columns value.
            (1) 
              1. Former Member

                Hi,

                Sorry for interrupting again. Have you ever done automation for  SAP MDM -SAP Master Data Management.

                Would appreciate you thoughts and Ideas on it.

                (0) 
    2. Former Member

      Hi ,

      I wanted to automate SAP GUI transactions. this blog gave me many useful info.

      I used the same Logon script shared on this blog but its not working.

      Could you please help me on this?

      thanks in Advance.

      –Jaya

      (0) 
    3. Former Member

      Hi ,

      I wanted to automate SAP GUI transactions. this blog gave me many useful info.

      I used the same Logon script shared on this blog but its not working. Any specific changes to be done? the script does not throw any error after resolving dependencies.

      Could you please help me on this?

      thanks in Advance.

      –Jaya

       

      (0) 
  2. Former Member

    Hi,

    Sorry for interrupting again. Have you ever done automation for  SAP MDM -SAP Master Data Management.

    Would appreciate you thoughts and Ideas on it.

    (0) 
    1. Stefan Schnell Post author

      Hello Manoranjan,

      welcome in the SAP Community.

      Sure is that possible, here examples of different login variant:

      'Here the standard with findById
      'session.findById("wnd[0]/usr/txtRSYST-MANDT").text = "001"
      'session.findById("wnd[0]/usr/txtRSYST-BNAME").text = "BCUSER"
      'session.findById("wnd[0]/usr/pwdRSYST-BCODE").text = "minisap"
      'session.findById("wnd[0]/usr/txtRSYST-LANGU").text = "DE"
      'session.findById("wnd[0]").sendVKey 0
      
      'Here a variant with findByNameEx
      'Set win = session.ActiveWindow()
      'win.findByNameEx("RSYST-MANDT", 31).text = "001"
      'win.findByNameEx("RSYST-BNAME", 31).text = "BCUSER"
      'win.findByNameEx("RSYST-BCODE", 33).text = "minisap"
      'win.findByNameEx("RSYST-LANGU", 31).text = "DE"
      'win.findByNameEx("wnd[0]", 21).sendVKey 0
      
      'Here a variant with findByName
      Set win = session.ActiveWindow()
      win.findByName("RSYST-MANDT", "GuiTextField").text = "001"
      win.findByName("RSYST-BNAME", "GuiTextField").text = "BCUSER"
      win.findByName("RSYST-BCODE", "GuiPasswordField").text = "minisap"
      win.findByName("RSYST-LANGU", "GuiTextField").text = "DE"
      win.findByName("wnd[0]", "GuiMainWindow").sendVKey 0

      Best regards
      Stefan

      (0) 
  3. Former Member

     

    Thanks a lot Stefan.

    The examples shared are VB, any reference how that would be in Java?

    One more help.. Is there any reference link which I can refer to?

    Currently, I’m seeing issues for SAP grid where the ID would end at shell and post that, i need to write functions/methods to access the content.

    (1) 
    1. Stefan Schnell Post author

      Hello Manoranjan,

      here a full example in Java which uses findByName method:

      package de.stschnell;
      
      import com.jacob.activeX.*;
      import com.jacob.com.*;
      
      public class Test {
      
        public static void main(String[] args) {
      
          ActiveXComponent SAPROTWr, GUIApp, Connection, Session, Obj, win;
      	Dispatch ROTEntry;
      	Variant ScriptEngine;
      	Variant[] arg;
      
      	ComThread.InitSTA();
      
      	//-Set SapGuiAuto = GetObject("SAPGUI")-----------------------------
      	SAPROTWr = new ActiveXComponent("SapROTWr.SapROTWrapper");
      	try {
      	  ROTEntry = SAPROTWr.invoke("GetROTEntry", "SAPGUI").toDispatch();
      	  //-Set application = SapGuiAuto.GetScriptingEngine----------------
      	  ScriptEngine = Dispatch.call(ROTEntry, "GetScriptingEngine");
      	  GUIApp = new ActiveXComponent(ScriptEngine.toDispatch());
      
      	  //-Set connection = application.Children(0)-----------------------
      	  Connection = new ActiveXComponent(
      	    GUIApp.invoke("Children", 0).toDispatch()
      	  );
      	  //-Set session = connection.Children(0)---------------------------
      	  Session = new ActiveXComponent(
      	    Connection.invoke("Children", 0).toDispatch()
      	  );
      	  //-Set win = session.ActiveWindow()-------------------------------
      	  win = new ActiveXComponent(
      	    Session.invoke("ActiveWindow").toDispatch()
      	  );
      
      arg = new Variant[2];
      arg[0] = new Variant("RSYST-MANDT");
      arg[1] = new Variant("GuiTextField");
      Obj = new ActiveXComponent(win.invoke("findByName", arg).toDispatch());
      Obj.setProperty("text", "099");
      
      arg = new Variant[2];
      arg[0] = new Variant("RSYST-BNAME");
      arg[1] = new Variant("GuiTextField");
      Obj = new ActiveXComponent(win.invoke("findByName", arg).toDispatch());
      Obj.setProperty("text", "YI00159");
      
      arg = new Variant[2];
      arg[0] = new Variant("RSYST-BCODE");
      arg[1] = new Variant("GuiPasswordField");
      Obj = new ActiveXComponent(win.invoke("findByName", arg).toDispatch());
      Obj.setProperty("text", "Baum456");
      
      arg = new Variant[2];
      arg[0] = new Variant("RSYST-LANGU");
      arg[1] = new Variant("GuiTextField");
      Obj = new ActiveXComponent(win.invoke("findByName", arg).toDispatch());
      Obj.setProperty("text", "DE");
      
      arg = new Variant[2];
      arg[0] = new Variant("wnd[0]");
      arg[1] = new Variant("GuiMainWindow");
      Obj = new ActiveXComponent(win.invoke("findByName", arg).toDispatch());
      Obj.invoke("sendVKey", 0);
      
      	} catch (Exception e) {
      	  System.out.println(
      	    e.getMessage().toString()
      	  );
      	} finally {
      	  ComThread.Release();
      	  System.exit(0);
      	}
      	
        }
      
      }

      Best regards
      Stefan

      (0) 
  4. Former Member
    Here is what I'm using to get the ID based on name or Text
    	public ActiveXComponent getXpath(ActiveXComponent Session, String name, String type, boolean editable, Hashtable<String,String> data) {
    		int count=0;
    		
    		//boolean recFlag = true;
    		ActiveXComponent childObject = null, currentObj = null;
    		Dispatch disp = Session.invoke("Children").toDispatch();
    		count = disp.call(disp, "count").toInt();
    		for(int i = 0;i<count;i++)
    		{
    			currentObj = new ActiveXComponent(Session.invoke("Children", i).toDispatch());
    			Variant tempXpath = currentObj.getProperty(type);
    			if(tempXpath.toString().contains(name) && (currentObj.getProperty("Changeable").toBoolean() == editable)) {
    				xpath =  currentObj.getProperty("ID").toString();
    				xpath = xpath.substring("/app/con[0]/ses[0]/".length());
    				data.put("finalXpath", xpath);
    				System.out.println(data.get("finalXpath"));
    				break;
    			}
    			if(currentObj.getProperty("ContainerType").toBoolean()) 
    			{
    				childObject = getXpath(currentObj,name,type,editable, data);
    			}
    		}
    		
    		return currentObj;
    	}
    (0) 
    1. Stefan Schnell Post author

      Hello Manoranjan,

      I am no Java programmer, but as far as I understand your code correct it can’t work. You are looping over the sessions but your xpath variable is set fix to ses[0]. Here my version of your code:

      public ActiveXComponent getXpath(ActiveXComponent Session, String name, String type, boolean editable, Hashtable<String,String> data) {
      	int count=0;
      		
      	//boolean recFlag = true;
      	ActiveXComponent childObject = null, currentObj = null;
      	Dispatch disp = Session.invoke("Children").toDispatch();
      	count = disp.call(disp, "count").toInt();
      	for(int i = 0;i<count;i++) {
      		currentObj = new ActiveXComponent(Session.invoke("Children", i).toDispatch());
      		Variant tempXpath = currentObj.getProperty(type);
      		if(tempXpath.toString().contains(name) && (currentObj.getProperty("Changeable").toBoolean() == editable)) {
      			xpath =  currentObj.getProperty("ID").toString();
      			xpath = xpath.substring("/app/con[0]/ses[" + i.toString() + "]/".length());
      			data.put("finalXpath", xpath);
      			System.out.println(data.get("finalXpath"));
      			break;
      		}
      		if(currentObj.getProperty("ContainerType").toBoolean()) {
      			childObject = getXpath(currentObj,name,type,editable, data);
      		}
      	}
      	return currentObj;
      }

      Best regards
      Stefan

      (0) 
  5. Former Member

    Hi Stefan – I’m trying to scroll down on MM43 but sendVKey 82 is not active/configured on this screen.

    Basically I’m trying to take a screenshot on the attributes which are not visible.

    Could you please suggest if there is any alternate method to scroll down to a specific field on MM43?

     

     

    (0) 
    1. Stefan Schnell Post author

      Hello Manoranjan,

      it would be great if you ask this as a new question in the forum and not as comment of this blog.

      Thanks and best regards
      Stefan

      (0) 

Leave a Reply