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 可以帮助区分对象未注册、对象未运行、访问被拒绝以及普通的脚本方法调用失败。
建议的验证顺序
先用附加模式进行最小测试:
- 手动启动 SAP GUI。
- 手动登录目标系统。
- 确保只有一个连接和一个 session。
- 将 Delphi 工程编译为与 SAP GUI 兼容的目标平台。
- 使用
GetActiveOleObject('SAPGUI')获取入口。 - 检查两级
Children.Count。 - 先读取简单属性,再调用
StartTransaction('MMBE')。 - 确认最小测试可以运行后,再添加多连接选择、页面导航、字段填写和报表导出逻辑。
最小测试可以正常运行,说明 COM 通信已经建立,Scripting 权限也可用。之后遇到的问题通常与 session 选择、界面加载时序或控件 ID 变化有关,而不是 ActiveX 对象创建失败。
备注:内容仅供参考。
