使用Python的win32com.client创建SAP GUI连接失败,寻求排查帮助
修复SAP GUI自定义连接创建的Python脚本问题
先梳理原代码中的核心问题:
- 尝试打开不存在的连接:
application.OpenConnection(connection_name, True)在连接未创建前调用会直接报错 - 连接添加对象错误:将新创建的连接添加到已打开连接的
Children集合,而非SAP Logon的全局连接列表 - 脚本引擎调用混淆:错误使用
GetScriptingEngineControl(),创建连接需操作SAP Logon的主应用对象 - 固定等待时间不可靠:
time.sleep(5)无法保证SAP GUI完全加载,易导致后续COM调用失败 - 登录逻辑冗余:初始化中硬编码登录信息,与单独的
login方法冲突
修正后的完整代码
import win32com.client import sys import subprocess import time import logging from dotenv import load_dotenv import os # 配置日志 logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') load_dotenv() class Sap(): def __init__(self, connection_name, application_server, instance_number, system_id, saprouter_string, client): ''' 初始化SAP GUI并创建自定义应用服务器连接 Args: connection_name (str): 新建连接的名称 application_server (str): SAP应用服务器地址 instance_number (str): 实例编号(如"00") system_id (str): 系统ID(如"PRD") saprouter_string (str): SAProuter字符串(无则留空) client (str): 客户端编号(如"100") ''' logging.info('启动SAP GUI') self.path = r"C:\Program Files (x86)\SAP\FrontEnd\SAPgui\saplogon.exe" # 启动SAP Logon并等待就绪 self.sap_process = subprocess.Popen(self.path) self.wait_for_sap_gui() # 获取SAP GUI自动化对象 self.SapGuiAuto = win32com.client.GetObject("SAPGUI") if not isinstance(self.SapGuiAuto, win32com.client.CDispatch): logging.error('无法获取SAP GUI自动化对象') return # 获取SAP Logon应用对象(用于管理连接列表) self.logon_app = self.SapGuiAuto.GetScriptingEngine() # 检查连接是否已存在,避免重复创建 if self.connection_exists(connection_name): logging.info(f'连接"{connection_name}"已存在,直接使用') self.open_existing_connection(connection_name) return # 创建新的自定义应用服务器连接 logging.info(f'创建新连接: {connection_name}') new_conn = self.logon_app.CreateConnection() new_conn.Name = connection_name new_conn.ConnectionType = "CustomApplicationServer" # 注意:正确的枚举值是驼峰式,不是空格分隔 new_conn.ApplicationServer = application_server new_conn.SystemID = system_id new_conn.InstanceNumber = instance_number if saprouter_string: new_conn.SAProuterString = saprouter_string new_conn.Client = client new_conn.Language = "EN" # 将新连接添加到SAP Logon的连接列表 self.logon_app.Connections.Add(new_conn) # 保存连接配置 self.logon_app.SaveConnectionList() logging.info('连接创建并保存成功') # 打开新创建的连接 self.open_existing_connection(connection_name) def wait_for_sap_gui(self, timeout=15): '''等待SAP GUI完全加载,避免COM调用失败''' start_time = time.time() while time.time() - start_time < timeout: try: win32com.client.GetObject("SAPGUI") logging.info('SAP GUI已就绪') return except: time.sleep(1) logging.error('SAP GUI加载超时') sys.exit(1) def connection_exists(self, conn_name): '''检查指定名称的连接是否已存在''' for conn in self.logon_app.Connections: if conn.Name == conn_name: return True return False def open_existing_connection(self, conn_name): '''打开已存在的SAP连接并获取会话''' try: # 打开连接(False表示不显示登录对话框,后续通过login方法手动登录) self.connection = self.logon_app.OpenConnection(conn_name, False) self.session = self.connection.Children(0) self.session.findById("wnd[0]").maximize() logging.info(f'成功打开连接: {conn_name}') except Exception as e: logging.error(f'打开连接失败: {str(e)}') sys.exit(1) def login(self, user, password, language=None): '''执行SAP登录操作''' try: logging.info('开始登录SAP') # 若未指定语言,使用连接预设的语言 lang = language or self.session.findById("wnd[0]/usr/txtRSYST-LANGU").text self.session.findById("wnd[0]/usr/txtRSYST-MANDT").text = self.connection.Client self.session.findById("wnd[0]/usr/txtRSYST-BNAME").text = user self.session.findById("wnd[0]/usr/pwdRSYST-BCODE").text = password self.session.findById("wnd[0]/usr/txtRSYST-LANGU").text = lang # 发送回车登录 self.session.findById("wnd[0]").sendVKey(0) # 处理可能的登录提示(如密码过期) if self.session.findById("wnd[0]/sbar").text: logging.warning(f'登录提示: {self.session.findById("wnd[0]/sbar").text}') logging.info('登录成功') except Exception as e: logging.error(f'登录失败: {str(e)}') raise # 使用示例 if __name__ == "__main__": sap = Sap( connection_name="自定义SAP连接", application_server="sap.example.com", instance_number="00", system_id="DEV", saprouter_string="/H/saprouter.example.com/S/3299", client="100" ) sap.login( user=os.getenv("SAP_USER"), password=os.getenv("SAP_PASSWORD"), language="EN" )
关键修复说明
- 连接创建逻辑修正:
- 使用
logon_app.Connections.Add(new_conn)将新连接添加到全局连接列表,而非错误的connection.Children.Add - 连接类型值更正为
CustomApplicationServer(SAP COM对象要求的枚举值,而非界面显示的"Custom Application Server")
- 使用
- 可靠性提升:
- 新增
wait_for_sap_gui方法,循环等待SAP GUI就绪,替代固定sleep - 添加
connection_exists检查,避免重复创建相同名称的连接
- 新增
- 逻辑优化:
- 分离连接创建与登录逻辑,初始化仅负责连接配置和打开,登录通过单独方法完成
- 移除硬编码的用户名密码,改用环境变量存储敏感信息
- 细化异常处理,捕获具体错误并输出详细日志
- API调用修正:
- 使用
GetScriptingEngine()获取正确的SAP Logon应用对象,而非GetScriptingEngineControl()
- 使用
内容的提问来源于stack exchange,提问作者Jerónimo Pimentel
相关产品推荐
相关产品推荐

