PAYATHON 2026

Delphi 中 SAP GUI Scripting 对象访问失败

排障赵师傅

结论

代码中的对象层级没有明显问题,错误在于获取入口时混用了两种连接方式。

要附加到已经运行并登录的 SAP GUI,应使用:

SAPGuiAuto := GetActiveOleObject('SAPGUI');
Application := SAPGuiAuto.GetScriptingEngine;

SAPGUI.ScriptingCtrl.1 用于创建独立的 Scripting Control。通过 CreateOleObject 创建后,该对象就是脚本应用入口。此时无需调用 GetScriptingEngine,应直接通过 OpenConnection 打开 SAP Logon 中的连接。

当前代码最可能出错的是这一行:

SAPGuiAuto := GetActiveOleObject('SAPGUI.ScriptingCtrl.1');

这里混淆了“附加到正在运行的 SAP GUI”和“创建 Scripting Control”两种用法。

附加到已经登录的 SAP GUI

SAP Logon 已经打开,并且用户已经登录到某个系统时,可以这样写:

uses
  System.SysUtils,
  Vcl.Dialogs,
  ComObj,
  Variants;

procedure TForm1.Button1Click(Sender: TObject);
var
  SAPGuiAuto: OleVariant;
  SAPApplication: OleVariant;
  Connection: OleVariant;
  Session: OleVariant;
begin
  try
    // 获取正在运行的 SAP GUI
    SAPGuiAuto := GetActiveOleObject('SAPGUI');

    // 获取 SAP GUI Scripting 应用对象
    SAPApplication := SAPGuiAuto.GetScriptingEngine;

    if SAPApplication.Children.Count = 0 then
      raise Exception.Create('当前没有可用的 SAP GUI 连接');

    Connection := SAPApplication.Children(0);

    if Connection.Children.Count = 0 then
      raise Exception.Create('当前连接中没有可用的 SAP GUI 会话');

    Session := Connection.Children(0);

    if Session.Busy then
      raise Exception.Create('SAP GUI 会话正在处理其他请求');

    Session.StartTransaction('MMBE');
  except
    on E: Exception do
      ShowMessage('SAP GUI Scripting 调用失败:' + E.Message);
  end;
end;

运行这段代码需要满足以下条件:

  • SAP GUI 已经启动。
  • 至少有一个已登录的连接。
  • 该连接中至少有一个可用的 session。
  • Delphi 程序与 SAP GUI 运行在同一个 Windows 用户会话中。

访问 Children(0) 前,需要先检查 Children.Count。即使已经成功取得 SAPApplication,如果当前没有登录连接,或者 session 尚未建立,直接访问也会引发 COM 调用错误。

使用 Scripting Control 主动打开连接

应用程序需要根据 SAP Logon 中的连接配置主动打开系统时,可以使用 SAPGUI.ScriptingCtrl.1:

uses
  System.SysUtils,
  Vcl.Dialogs,
  ComObj,
  Variants;

procedure TForm1.Button1Click(Sender: TObject);
var
  SAPApplication: OleVariant;
  Connection: OleVariant;
  Session: OleVariant;
begin
  try
    SAPApplication := CreateOleObject('SAPGUI.ScriptingCtrl.1');

    // 参数应为 SAP Logon 中显示的连接名称
    Connection := SAPApplication.OpenConnection(
      'SAP Logon 中的连接名称',
      True
    );

    if Connection.Children.Count = 0 then
      raise Exception.Create('连接已经打开,但尚未建立可用会话');

    Session := Connection.Children(0);
    Session.StartTransaction('MMBE');
  except
    on E: Exception do
      ShowMessage('SAP GUI Scripting 调用失败:' + E.Message);
  end;
end;

这种方式下不要再写:

SAPApplication := SAPGuiAuto.GetScriptingEngine;

OpenConnection 的第一个参数通常是 SAP Logon 配置项中显示的连接名称,不一定与系统 SID 相同,应以当前机器上的 SAP Logon 配置为准。

连接打开后,界面可能会停留在登录页。此时虽然已经取得 SAP GUI 会话,但身份认证尚未完成。程序需要等待并处理登录界面,也可以改用已经登录的会话。

VBA 和 VBS 可以运行,而 Delphi 失败的原因

VBA 和 VBS 示例通常使用以下对象链:

Set SapGuiAuto = GetObject("SAPGUI")
Set Application = SapGuiAuto.GetScriptingEngine
Set Connection = Application.Children(0)
Set Session = Connection.Children(0)

在 Delphi 中,GetObject("SAPGUI") 对应的是:

SAPGuiAuto := GetActiveOleObject('SAPGUI');

不应写成:

GetActiveOleObject('SAPGUI.ScriptingCtrl.1');

VBA 中的 Set 是对象赋值语法。Delphi 使用 OleVariant 时不需要对应的关键字,方法名和属性名仍可通过后期绑定调用。

排查 ActiveX component can't create object

这个错误通常出现在创建或查找 COM 对象时,可以按以下顺序检查。

1. 确认调用方式与 ProgID 匹配

附加到正在运行的 SAP GUI:

GetActiveOleObject('SAPGUI');

创建 Scripting Control:

CreateOleObject('SAPGUI.ScriptingCtrl.1');

GetActiveOleObject 不会启动 SAP GUI。如果当前桌面会话中没有正在运行的 SAP GUI,程序将无法取得 SAPGUI 对象。

2. 检查程序位数

检查 Delphi 工程的目标平台是 Win32 还是 Win64,并确认它与已安装的 SAP GUI 和 Scripting Control 兼容。

使用以下代码时尤其需要注意进程位数:

CreateOleObject('SAPGUI.ScriptingCtrl.1');

如果进程位数与 ActiveX 控件的注册位数不一致,可能会直接出现“ActiveX component can’t create object”。

本机安装的是 32 位 SAP GUI 时,可以先把 Delphi 工程编译为 Win32 进行验证。SAP GUI 的发行版和安装方式可能不同,最终应以本机实际安装的组件为准。

3. 确认 Scripting 组件已安装

SAP GUI Scripting 已启用,不代表相关 ActiveX 组件一定已经完整安装并正确注册。如果无法创建 SAPGUI.ScriptingCtrl.1,应通过 SAP GUI 安装程序检查或修复 Scripting 组件。

不建议从其他电脑复制 OCX 或 DLL 后手工注册,因为组件的版本、依赖项和位数可能不匹配。

4. 保持相同的权限级别

SAP GUI 和 Delphi 程序应使用同一个 Windows 用户,并在同一个桌面会话中以相同的权限级别运行。

例如,SAP GUI 以普通权限运行,而 Delphi 程序以管理员身份运行,程序可能无法发现已有的自动化对象。反过来也可能出现同样的问题。调试时,可以先让 Delphi IDE、生成的 EXE 和 SAP GUI 全部以普通权限运行。

5. 检查 SAP GUI 和服务器设置

通常需要满足以下条件:

  • SAP 服务器允许 GUI Scripting。
  • SAP GUI 客户端已经启用 Scripting。
  • 服务器策略允许当前用户使用 Scripting。
  • 本机安全设置没有阻止脚本连接。
  • 如果客户端设置为连接时弹出提示,需要由用户确认,或由管理员按照组织的安全要求配置。

服务器参数和客户端选项的具体名称可能因 SAP 环境、权限策略和 GUI 版本而异,应由 SAP Basis 或桌面运维人员核对。

Object required 的常见原因

这个错误一般说明对象链中的某一级没有取得有效对象。需要检查以下层级:

SAPGUI
  └─ GuiApplication
      └─ GuiConnection
          └─ GuiSession

常见原因有:

  • SAP GUI 尚未启动。
  • SAP Logon 已经启动,但没有登录任何系统。
  • Application.Children.Count 为 0。
  • 连接已经存在,但 Connection.Children.Count 为 0。
  • 会话正处于关闭、登录或切换状态。
  • 代码固定使用索引 0,而目标是另一个连接或会话。
  • SAP GUI 和 Delphi 程序位于不同的用户会话、远程桌面或 Citrix 会话中。

如果用户可能同时登录多个 SAP 系统,不宜长期依赖 Children(0)。可以枚举连接和 session,再根据 Connection.Description、Session.Info.SystemName、Session.Info.Client 或其他实际可用属性选择目标。具体可用属性以当前安装的 SAP GUI Scripting API 为准。

Delphi 中的注意事项

COM 初始化

在 VCL 主线程中调用时,通常不需要额外处理。如果把 SAP 操作放到自建工作线程中,该线程必须初始化 COM,并在结束时释放:

uses
  Winapi.ActiveX;

procedure ExecuteSapTask;
begin
  OleCheck(CoInitializeEx(nil, COINIT_APARTMENTTHREADED));
  try
    // SAP GUI Scripting 调用
  finally
    CoUninitialize;
  end;
end;

不要在工作线程中直接操作 VCL 控件。错误信息应通过 TThread.Synchronize 或 TThread.Queue 返回界面线程。

后期绑定与类型库

使用 OleVariant 属于后期绑定,不需要导入 SAP GUI 类型库,适合快速验证,也能减少不同类型库版本造成的编译依赖。

Delphi 也可以导入 SAP GUI Scripting API 类型库,并使用明确的接口类型。这样能获得代码提示和编译期类型检查,但接口名称和生成的单元会受到本机类型库版本影响。排查连接问题时,先使用 OleVariant 通常更直接。

记录完整的 COM 错误码

只显示 E.Message 能获得的信息有限,可以单独捕获 EOleSysError:

uses
  System.SysUtils,
  Vcl.Dialogs,
  ComObj;

try
  // SAP GUI Scripting 调用
except
  on E: EOleSysError do
    ShowMessage(Format(
      'COM 调用失败,HRESULT=0x%.8x,错误信息:%s',
      [Cardinal(E.ErrorCode), E.Message]
    ));
  on E: Exception do
    ShowMessage(E.ClassName + ': ' + E.Message);
end;

HRESULT 可以帮助区分对象未注册、对象未运行、访问被拒绝以及普通的脚本方法调用失败。

建议的验证顺序

先用附加模式进行最小测试:

  1. 手动启动 SAP GUI。
  2. 手动登录目标系统。
  3. 确保只有一个连接和一个 session。
  4. 将 Delphi 工程编译为与 SAP GUI 兼容的目标平台。
  5. 使用 GetActiveOleObject('SAPGUI') 获取入口。
  6. 检查两级 Children.Count。
  7. 先读取简单属性,再调用 StartTransaction('MMBE')。
  8. 确认最小测试可以运行后,再添加多连接选择、页面导航、字段填写和报表导出逻辑。

最小测试可以正常运行,说明 COM 通信已经建立,Scripting 权限也可用。之后遇到的问题通常与 session 选择、界面加载时序或控件 ID 变化有关,而不是 ActiveX 对象创建失败。

备注:内容仅供参考。