You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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"
    )

关键修复说明

  1. 连接创建逻辑修正:
    • 使用logon_app.Connections.Add(new_conn)将新连接添加到全局连接列表,而非错误的connection.Children.Add
    • 连接类型值更正为CustomApplicationServer(SAP COM对象要求的枚举值,而非界面显示的"Custom Application Server")
  2. 可靠性提升:
    • 新增wait_for_sap_gui方法,循环等待SAP GUI就绪,替代固定sleep
    • 添加connection_exists检查,避免重复创建相同名称的连接
  3. 逻辑优化:
    • 分离连接创建与登录逻辑,初始化仅负责连接配置和打开,登录通过单独方法完成
    • 移除硬编码的用户名密码,改用环境变量存储敏感信息
    • 细化异常处理,捕获具体错误并输出详细日志
  4. API调用修正:
    • 使用GetScriptingEngine()获取正确的SAP Logon应用对象,而非GetScriptingEngineControl()

内容的提问来源于stack exchange,提问作者Jerónimo Pimentel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 16:25:02