Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
Stefan-Schnell
Active Contributor
31,428
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.
51 Comments
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");
Stefan-Schnell
Active Contributor
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
 

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)”
Stefan-Schnell
Active Contributor
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
 

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.
Stefan-Schnell
Active Contributor
Thanks for sharing your code.
0 Kudos
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 Kudos
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.
Stefan-Schnell
Active Contributor
0 Kudos
Hello Mohamed,

no, unfortunately I have never worked with MDM, so I can't give you hints, sorry.

Best regards
Stefan
0 Kudos
Thanks ..
0 Kudos
Hi - Can we use name for identifying the object instead of FindByID always? Please advise.
Stefan-Schnell
Active Contributor
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
Former Member
0 Kudos
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
Former Member
0 Kudos
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

 
 

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.
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;
}
Stefan-Schnell
Active Contributor
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
Stefan-Schnell
Active Contributor
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 Kudos
Thanks Stefan, will check the code shared.
0 Kudos
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?

 

 
Stefan-Schnell
Active Contributor
0 Kudos
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 Kudos

Hi Stefan,

I tried running the code mentioned in the blog but I am getting following error. Could you please help me.

Exception in thread “main” com.jacob.com.ComFailException: Can’t get object clsid from progid
at com.jacob.com.Dispatch.createInstanceNative(Native Method)
at com.jacob.com.Dispatch.<init>(Dispatch.java:99)
at com.jacob.activeX.ActiveXComponent.<init>(ActiveXComponent.java:58)
at testSAP.LoginSAP.main(LoginSAP.java:25)

Stefan-Schnell
Active Contributor
0 Kudos
Hello Neha,

is the SAP GUI Scripting installed on your computer?

Best regards
Stefan

0 Kudos
Thanks Stefan.

Enabled the scripting in SAP GUI options and tried running the code mentioned in the blog. But it is failing at this line

SAPROTWr = new ActiveXComponent("SapROTWr.SapROTWrapper");

Error:

Exception in thread "main" com.jacob.com.ComFailException: Can't co-create object
at com.jacob.com.Dispatch.createInstanceNative(Native Method)
at com.jacob.com.Dispatch.<init>(Dispatch.java:99)
at com.jacob.activeX.ActiveXComponent.<init>(ActiveXComponent.java:58)
at testSAP.LoginSAP.main(LoginSAP.java:25)
Stefan-Schnell
Active Contributor
0 Kudos
Hello Neha,

do you use x86 version of Java? SAP Rot Wrapper is an x86 version and it is not possible to use it with x64 Java version.

Let us know your results.

Cheers
Stefan
0 Kudos
Thanks Stefan. I tried with x86 version of Java. It worked!
0 Kudos
Hi Stefan,

I have installed the Scripting Tracker from https://tracker.stschnell.de/

But I do not see the java option to select for the coding. Could you please help me here.

Stefan-Schnell
Active Contributor
Hello Neha,

I disable the Java support with release 4 of Scripting Tracker. Java is since version 9 available only as x64 version. The only way to get access to the Windows Running Object Table (ROT) is via an x86 library, as I described above. So nobody has a chance to use this method with an actual Java release. From this perspective I see no reason to support Java furthermore.

Best regards
Stefan

 
Okay Stefan. Thanks!

But is there any possibility of getting any of the versions of Scripting Tracker below release 4?

Because I wanted the Java Scripting Engine which was more helpful as we do the SAP Automation using Java and can be integrated with the Hybris web apps as well which are automated using Selenium.
former_member604305
Discoverer
In VB Language sap connection string is connection = GUIApp.OpenConnection("XYZ [PUBLIC]", true)

But if same I have to do it with Java  then What should be it.
former_member619341
Discoverer
Hello Stefan

 

I appreciate your work very much!

I'd like to ask you for the very same thing, just to share a Scripting tracker version that supports Java plotter.

Thank you.
Stefan-Schnell
Active Contributor

Hello kaclaw,

thanks for your post and your kindful words. I got many mails from users who wanted to have Java recording possibility back in the Tracker. This and the answer from jude.bradley here, with the hint to note 2689304, changes the the things. “…as of patchlevel 8 of SAP GUI for Windows 7.50 and as of SAP GUI for Windows 7.60, the installation of SAP GUI for Windows automatically writes the respective registry values so that a usage of saprotwr.dll from 64bit processes is possible.”

You can find here Tracker with Java support.

Good automation, also to neha.goje.

Best regards
Stefan

former_member622015
Discoverer
0 Kudos
Hi Stefan,

Thanks a lot for your continuous help in automating SAP GUI with selenium - java approach. I have got a blocker. If you could please share any code specific to drop down selection and the menu bar selection of GUI. Thanks so much!! Waiting for you response...
former_member622015
Discoverer
0 Kudos

Another question Stephan. If you could please help. How UFT gets hold of every object of SAP GUI? How can we get the same information about SAP GUI objects in java also? for example: SAP GUI table column names, menubar etc?

former_member622015
Discoverer
0 Kudos
And if you could please share all the methods, functions etc to be used in SAP GUI Java combination so that we can do a full end to end flow of SAP by java and selenium.
Stefan-Schnell
Active Contributor
0 Kudos
Hello Soumen,

welcome in the SAP Community.

To choose an entry in the menu bar you can try this:
Obj = new ActiveXComponent(Session.invoke("findById", "wnd[0]/mbar/menu[0]/menu[0]").toDispatch());
Obj.invoke("select");

Obj = new ActiveXComponent(Session.invoke("findById", "wnd[0]/mbar/menu[2]/menu[0]").toDispatch());
Obj.invoke("select");

With the number in the brackets you choose the menu, e.g. with "menu[0]" you choose the first menu.

To set an entry in a combo box (drop down selection) you can try this:
Obj = new ActiveXComponent(Session.invoke("findById", "wnd[0]/usr/cmbT005X-LAND").toDispatch());
Obj.setProperty("key", "DE");
Obj = new ActiveXComponent(Session.invoke("findById", "wnd[0]/usr/cmbT002-SPRAS").toDispatch());
Obj.setProperty("key", "DE");

Here you choose the combo box (cmb) and set the key, in my example both German, country and language.

Best regards
Stefan
Stefan-Schnell
Active Contributor
0 Kudos

Hello Soumen,

I don’t know UFT. But to get information about the controls of the SAP GUI for Windows you can use Scripting Tracker, as I described above. You can find here Tracker with Java support.

Best regards
Stefan

Stefan-Schnell
Active Contributor
0 Kudos
Hello Soumen,

what are you exactly looking for?

Best regards
Stefan
0 Kudos
Hi  stefan.schnell,

getting below mentioned exception, what would be the solution. please let me know.

"java.lang.IllegalStateException: Dispatch not hooked to windows memory"

 
Girish2000
Explorer
0 Kudos

Hi I'm not able to execute script tracker generate java scripting using 

ActiveXComponent sapRotWr = new ActiveXComponent("SapROTWr.SapROTWrapper");
Dispatch rotEntry = sapRotWr.invoke("GetROTEntry", "SAPGUI").toDispatch();
Variant scriptEngine = Dispatch.call(rotEntry, "GetScriptingEngine");

Getting can't create object error at 
ActiveXComponent sapRotWr = new ActiveXComponent("SapROTWr.SapROTWrapper");

I have 64 bit java and windows .
I copied jacob dll to system32 
I tried to provide separate path also .

Still I'm facing the error. Please @Stefan-Schnell  help me with this how to work with jacob . I need it 

Stefan-Schnell
Active Contributor

Hello @Girish2000,

I don't know exactly what happens in your case. I assume that your Java installation is correct, the JAVA_HOME environment variable is correct and you add %JAVA_HOME%/bin to your PATH environment variable. It is not necessary to copy jacob.dll into system32 folder, here you need administrative rights, add the path to your PATH environment variable. That are all preparations, with this it should work.

I tried that just this moment in a clean Windows 11 environment with SAP GUI for Windows 8 PL9 HF1 64-bit and BellSoft JDK 23. All works as expected. Try it on the same way in your JShell to see what happens in your case.

StefanSchnell_0-1730176938502.png

If the last line delivers com.jacab.activeX.ActiveXComponent@[hexnumber] it works correct. If not, it is assumed that no instance of the SAP ROT Wrapper can be created. Do you use the SAP GUI for Windows in the 64-bit version? The 64-bit version of saprotwr.dll can only be used by 64bit processes. If you are using the 32-bit version of SAP GUI for Windows, mixing it with Java 64-bit will not work. That would be the only reason I can see at the moment.

Best regards
Stefan

 

 

Girish2000
Explorer

Hi @Stefan-Schnell ,

I'm currently working with 32-bit SAP Windows and using 64-bit java. Are there any workaround to solve this problem?.

zfiori
Participant
0 Kudos

Hi Community,

" 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. "

Thanks for your sharing, it help us a lot.

🙂

 

Regards,

 

ZFiori.

Girish2000
Explorer


@Stefan-Schnell Is there a way to listen for events from SAP? Specifically, if I make changes in the SAP GUI, I want to capture those events in my app. For example, if I enter data into a field or click a button, I would like to be notified of those actions. If possible, could you please share a Python or PowerShell snippet to listen for events from SAP GUI scripting?

Stefan-Schnell
Active Contributor

Hello @Girish2000,

I remember we discussed the possibility to use 32-bit COM libraries from 64-bit programming languages here a long time ago. Unfortunately it is lost here, but a copy exists here. I don't have the slightest idea if this still works today, but you can give it a try. Let us know your result. However, I would recommend that you use the 64-bit version of the SAP GUI for Windows, because it simplifies many things and you are up-to-date. The way is also described here, but no solution, only that this should be possible.

I presented a SAP GUI Scripting Recorder in PowerShell here in the community. This allows you to listen for events. But please note, like the approach above, that this was nine years ago, but you can give it a try too. Let us know your result.

Best regards
Stefan

 

Stefan-Schnell
Active Contributor

Hello @zfiori,

the approach here is more than seven years old. I can't tell you how relevant this approach is today. If you work with 32-bit applications it is certainly not wrong. The approach bases on the SAP GUI for Windows 7.50, which was only available in 32-bit version. Today I would simply analyze it in detail and reevaluate.

Best regards
Stefan

Girish2000
Explorer

Hi @Stefan-Schnell ,

Thank you for providing the PowerShell recorder. Unfortunately, it's not working for me as expected. If possible, could you share the latest version or suggest any other implementation, such as in Java or Python? That would be very helpful.

I'm currently unable to register the "Change" event or listen to changes in elements. Your assistance with this would be greatly appreciated.

I tried with the same code I modified the dll path 

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

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

      [Reflection.Assembly]::LoadWithPartialName("Microsoft.VisualBasic") > $Null
      [Reflection.Assembly]::LoadFile($PSScriptRoot + "\Interop.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--------------------------------------------------------------------

Girish2000_0-1730223135205.png

 

Thank you!

Stefan-Schnell
Active Contributor

Hello @Girish2000,

here the modified code:

 

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

# Includes
."$PSScriptRoot\COM.ps1"

# Sub Main
function Main() {

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

  $SapGuiAuto = Get-Object( , "SAPGUI")
  if ($SapGuiAuto -isnot [System.__ComObject]) {
    return
  }

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

  $Connection = $Application.Children.Item(0)
  if ($Connection -eq $Null) {
    return
  } else {
    [Interop.SAPFEWSELib.GuiConnectionClass]$Connection = `
      [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType( `
        $Connection, `
        [Interop.SAPFEWSELib.GuiConnectionClass] `
      )
  }

  $Session = $Connection.Children.Item(0)
  if ($Session -eq $Null) {
    return
  } else {
    [Interop.SAPFEWSELib.GuiSession]$Session =
      [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType( `
        $Session, `
        [Interop.SAPFEWSELib.GuiSessionClass]
      )
  }

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

  while ($true) {

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

    [Interop.SAPFEWSELib.GuiSession]$RecSession =
      [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType( `
        $event.SourceArgs[0], `
        [Interop.SAPFEWSELib.GuiSessionClass] `
      )
    #Without the following line delivers PowerShell an error
    $Dummy = $RecSession | Format-List | Out-String

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

    Remove-Event -SourceIdentifier "Action"

  }

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

}

# Main
Main

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

 

StefanSchnell_0-1730260792069.png

I tried it on Win 11, with SAP GUI for Windows release 8 PL9 HF1 32-bit (x86) with Windows PowerShell x86, and it works as expected. In the image you can see I press only enter and the event I received is send virtual key event with button 0 and the opcode method (M). I used for this test the PowerShell  32-bit version from the path c:\Windows\SysWOW64\WindowsPowerShell\v1.0\. But it works also with PowerShell 64-bit.

I tried it with PowerShell Core release 7.4.6 64-bit and it works also.

StefanSchnell_0-1730261832204.png

Best regards
Stefan

Girish2000
Explorer

@Stefan-Schnell   Thank you so much for your help—it’s working now!

There are still a few issues, like some events not being captured properly, but I truly appreciate your support. Thanks again!

Girish2000
Explorer

Hi @Stefan-Schnell 

I have set ElementVisualizationMode to true, and the GUI elements are highlighting with a red boundary as expected. However, the Hit event is not being triggered. Below is the code I am using. Could you please review it and let me know if I am missing something?

Thank you in advance for your assistance.

Best regards,
K Girish Kumar

 

using System;
using System.Runtime.InteropServices;
using Interop.SAPFEWSELib;

class SAPGuiRecorder
{
    static void Main(string[] args)
    {
        try
        {
            // Initialize SAP GUI scripting engine
            Console.WriteLine("Initializing SAP GUI scripting...");
            var sapGui = InitializeSAPGui();
            if (sapGui == null)
            {
                Console.WriteLine("SAP GUI scripting engine not found.");
                return;
            }

            // Get the first connection and session
            var session = GetSAPSession(sapGui);
            if (session == null)
            {
                Console.WriteLine("No active session found.");
                return;
            }

            // Enable visualization mode and subscribe to the Hit event
            session.ActiveWindow.ElementVisualizationMode = true;
            Console.WriteLine("Visualization Mode Enabled.");
            session.Hit += OnHitEvent;

            Console.WriteLine("SAP GUI recorder is running. Interact with the GUI to trigger events. Press Enter to exit.");
            Console.ReadLine();

            // Clean up resources
            FreeCOMObject(session);
            FreeCOMObject(sapGui);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }

    // Initialize SAP GUI scripting engine
    static GuiApplication InitializeSAPGui()
    {
        object sapGuiAuto = Marshal.BindToMoniker("SAPGUI");
        return sapGuiAuto != null ? (GuiApplication)InvokeMethod(sapGuiAuto, "GetScriptingEngine") : null;
    }

    // Get the active SAP session
    static GuiSession GetSAPSession(GuiApplication app)
    {
        try
        {
            var connection = (GuiConnection)app.Connections.Item(0);
            return (GuiSession)connection.Children.Item(0);
        }
        catch
        {
            return null;
        }
    }

    // Event handler for the Hit event
    private static void OnHitEvent(GuiSession session, GuiComponent component, string innerObject)
    {
        Console.WriteLine("\n--- Hit Event Detected ---");
        Console.WriteLine($"Session ID: {session.Id}");
        Console.WriteLine($"Component Name: {component.Name}");
        Console.WriteLine($"Inner Object: {innerObject}");
    }

    // Helper: Invoke a method via reflection
    static dynamic InvokeMethod(object obj, string methodName, object[] methodParams = null)
    {
        return obj.GetType().InvokeMember(methodName, System.Reflection.BindingFlags.InvokeMethod, null, obj, methodParams);
    }

    // Helper: Free COM object
    static void FreeCOMObject(object obj)
    {
        if (obj != null)
        {
            Marshal.ReleaseComObject(obj);
        }
    }
}

 


 

Labels in this area