SAP GUI Scripting & Automation
Related Skills
sap-build-process-automation— Desktop Agent uses GUI scripting under the hoodsap-testing-quality— Automated GUI testing with eCATT/CBTAsap-s4hana-extensibility— When to use API vs. GUI scripting
Quick Start
Prerequisites:
- Enable scripting on SAP server:
RZ11→ parametersapgui/user_scripting=TRUE - Enable on client: SAP GUI → Options → Accessibility & Scripting → Enable scripting
- Disable notification popups: uncheck "Notify when script attaches" and "Notify when script opens connection"
Record your first script:
- Open SAP GUI → transaction you want to automate
- Menu: Customize Local Layout (Alt+F12) → Script Recording and Playback
- Click Record, perform actions, click Stop
- Save as
.vbsfile — this is your starting template
Minimal VBScript — Run Transaction:
If Not IsObject(application) Then
Set SapGuiAuto = GetObject("SAPGUI")
Set application = SapGuiAuto.GetScriptingEngine
End If
Set connection = application.Children(0)
Set session = connection.Children(0)
session.findById("wnd[0]/tbar[0]/okcd").text = "/nMM03"
session.findById("wnd[0]").sendVKey 0
session.findById("wnd[0]/usr/ctxtRMMG1-MATNR").text = "MAT-001"
session.findById("wnd[0]").sendVKey 0
Core Concepts
Object Hierarchy
GuiApplication
└── GuiConnection (SAP system connection)
└── GuiSession (user session / window)
└── GuiFrameWindow (wnd[0], wnd[1]...)
├── GuiToolbar (tbar[0] = command, tbar[1] = app)
├── GuiUserArea (usr/)
│ ├── GuiTextField, GuiCTextField
│ ├── GuiComboBox, GuiCheckBox, GuiRadioButton
│ ├── GuiTableControl, GuiGridView (ALV)
│ ├── GuiTab, GuiTabStrip
│ └── GuiTree
├── GuiStatusbar (sbar/)
└── GuiTitlebar (titl/)
Element ID Pattern
Format: wnd[N]/area/type[row,col]
wnd[0]/tbar[0]/okcd— Command field (OKCode)wnd[0]/usr/ctxtFIELD-NAME— Context field (F4 help available)wnd[0]/usr/txtFIELD-NAME— Text input fieldwnd[0]/usr/chkFIELD-NAME— Checkboxwnd[0]/usr/radFIELD-NAME— Radio buttonwnd[0]/usr/btnBUTTON-NAME— Push buttonwnd[0]/usr/tblTABLE/ctxtFIELD[row,col]— Table cellwnd[0]/usr/cntlGRID/shellcont/shell— ALV Grid controlwnd[0]/sbar— Status bar (messages)
Virtual Keys (sendVKey)
| Key | Code | Description |
|---|---|---|
| Enter | 0 |
Confirm/execute |
| F2 | 2 |
Choose/display |
| F3 | 3 |
Back |
| F5 | 5 |
Refresh |
| F8 | 8 |
Execute report |
| F12 | 12 |
Cancel |
| Shift+F4 | 16 |
Save as variant |
| Ctrl+S | 11 |
Save |
| Ctrl+Shift+F3 | 43 |
Delete |
Common Patterns
Pattern 1: Python Automation with win32com
import win32com.client
import time
def get_sap_session():
"""Connect to running SAP GUI session."""
sap_gui = win32com.client.GetObject("SAPGUI")
app = sap_gui.GetScriptingEngine
connection = app.Children(0)
session = connection.Children(0)
return session
def run_transaction(session, tcode):
"""Navigate to transaction."""
session.findById("wnd[0]/tbar[0]/okcd").text = f"/n{tcode}"
session.findById("wnd[0]").sendVKey(0)
def get_status_message(session):
"""Read status bar message."""
sbar = session.findById("wnd[0]/sbar")
return {
"type": sbar.MessageType, # S=Success, E=Error, W=Warning, I=Info
"text": sbar.Text
}
Pattern 2: Table Data Extraction
def extract_alv_grid(session, grid_id="wnd[0]/usr/cntlGRID/shellcont/shell"):
"""Extract all data from ALV grid."""
grid = session.findById(grid_id)
columns = grid.ColumnCount
rows = grid.RowCount
col_names = [grid.GetColumnTitles(i) for i in range(columns)]
data = []
for row in range(rows):
grid.SetCurrentCell(row, grid.GetColumnName(0))
record = {}
for col in range(columns):
col_name = grid.GetColumnName(col)
record[col_name] = grid.GetCellValue(row, col_name)
data.append(record)
return data
def extract_table_control(session, table_id):
"""Extract data from classic table control (scroll-aware)."""
table = session.findById(table_id)
all_rows = []
visible = table.VisibleRowCount
total = table.RowCount
for offset in range(0, total, visible):
table.VerticalScrollbar.Position = offset
for row in range(min(visible, total - offset)):
row_data = {}
for col in range(table.Columns.Count):
cell = table.GetCell(row, col)
row_data[table.Columns(col).Name] = cell.Text
all_rows.append(row_data)
return all_rows
Pattern 3: Batch Data Entry (e.g., Mass Material Change)
def mass_material_change(session, materials):
"""Change multiple materials via MM02."""
results = []
for mat in materials:
run_transaction(session, "MM02")
session.findById("wnd[0]/usr/ctxtRMMG1-MATNR").text = mat["matnr"]
session.findById("wnd[0]").sendVKey(0)
# Select views
for view in session.findById("wnd[0]/usr/tabsTABSPR1").Children:
if hasattr(view, "Selected"):
view.Selected = view.Text in mat.get("views", ["Basic Data 1"])
session.findById("wnd[0]").sendVKey(0)
# Update fields
for field_id, value in mat.get("fields", {}).items():
try:
session.findById(field_id).text = value
except Exception as e:
results.append({"matnr": mat["matnr"], "status": "ERROR", "msg": str(e)})
session.findById("wnd[0]").sendVKey(12) # Cancel
continue
session.findById("wnd[0]").sendVKey(11) # Ctrl+S
msg = get_status_message(session)
results.append({"matnr": mat["matnr"], "status": msg["type"], "msg": msg["text"]})
return results
Pattern 4: Handle Popups and Modal Dialogs
def handle_popup(session, action="confirm"):
"""Handle unexpected popup windows."""
try:
popup = session.findById("wnd[1]")
if action == "confirm":
popup.findById("wnd[1]/usr/btnBUTTON_1").press() # Yes/OK
elif action == "cancel":
popup.findById("wnd[1]/usr/btnBUTTON_2").press() # No/Cancel
elif action == "close":
popup.Close()
return True
except Exception:
return False # No popup present
def safe_action(session, action_func, max_popups=3):
"""Execute action and handle any resulting popups."""
action_func()
for _ in range(max_popups):
time.sleep(0.3)
if not handle_popup(session, "confirm"):
break
Pattern 5: Login Automation
def login_sap(system, client, user, password, language="EN"):
"""Open new SAP GUI connection and login."""
import subprocess
sap_logon = r"C:\Program Files (x86)\SAP\FrontEnd\SAPgui\saplogon.exe"
subprocess.Popen([sap_logon])
time.sleep(3)
sap_gui = win32com.client.GetObject("SAPGUI")
app = sap_gui.GetScriptingEngine
connection = app.OpenConnection(system, True)
session = connection.Children(0)
session.findById("wnd[0]/usr/txtRSYST-MANDT").text = client
session.findById("wnd[0]/usr/txtRSYST-BNAME").text = user
session.findById("wnd[0]/usr/pwdRSYST-BCODE").text = password
session.findById("wnd[0]/usr/txtRSYST-LANGU").text = language
session.findById("wnd[0]").sendVKey(0)
# Handle multiple logon popup
handle_popup(session, "confirm")
return session
Pattern 6: Error-Resilient Script Template
import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger("sap_script")
def run_with_retry(session, func, max_retries=3, *args, **kwargs):
"""Run GUI action with retry on transient errors."""
for attempt in range(max_retries):
try:
result = func(session, *args, **kwargs)
msg = get_status_message(session)
if msg["type"] == "E":
log.warning(f"Attempt {attempt+1}: SAP error: {msg['text']}")
if attempt < max_retries - 1:
session.findById("wnd[0]").sendVKey(3) # Back
continue
return result
except Exception as e:
log.error(f"Attempt {attempt+1}: Exception: {e}")
if attempt == max_retries - 1:
raise
return None
Error Catalog
| Error | Message | Root Cause | Fix |
|---|---|---|---|
Runtime error: -2147352567 |
Object not found | Element ID wrong or screen changed | Use Script Recorder to get correct ID |
Scripting disabled |
Server rejected script | sapgui/user_scripting = FALSE |
Set via RZ11 or profile parameter |
No SAP GUI instance |
GetObject failed | SAP GUI not running | Start SAP GUI before script |
Session busy |
Cannot attach | Transaction is processing | Add time.sleep() before action |
Modal dialog open |
Cannot access main window | Popup blocking | Handle wnd[1] before wnd[0] |
ALV: Invalid cell |
GetCellValue error | Row index out of range | Check RowCount before access |
Multiple logon |
Popup at login | User already logged in | Handle popup or close other session |
Authorization |
No authorization for scripting | Missing S_SCR auth object |
Assign auth via SU01/role |
Performance Tips
- Disable screen updates — Not natively supported; minimize visual impact by running in background session
- Batch via BDC/BAPI first — GUI scripting is last resort; prefer RFC/BAPI/OData for bulk operations
- Reuse sessions — Don't login/logout per record; reuse connection for batch processing
- Minimize findById calls — Cache element references:
field = session.findById(...)then reusefield - Handle scrolling — For table controls, read
VisibleRowCountand scroll incrementally - Parallel sessions — SAP allows up to 6 sessions per user; parallelize with
connection.Children(N) - Avoid hardcoded waits — Use status bar checks instead of fixed
time.sleep()where possible
Gotchas
- Screen variants: Same transaction can show different screens based on user settings — always verify screen number
- Language dependency: Element IDs can differ by logon language for some older transactions
- ALV vs. Table Control:
GuiGridView(ALV) andGuiTableControlhave completely different APIs - SAP GUI version: Scripting API changes between versions; test on target version
- Security policy: Many organizations disable scripting in production — always check policy
- 64-bit Python + 32-bit SAP GUI: COM interop may fail; use matching architecture