尧图精选

MySapLogon:SAP 多系统自动登录与 DPAPI 加密

🕒 发布时间:2026/10/2 13:42:10 📁 来源:尧图网络
周一早上八点四十你端着咖啡坐到工位打开 SAP Logon双击 PRD等三秒敲工号Tab敲密码Tab语言选 ZH回车。如果手上还挂着 DEV、QAS 和一套老 ECC这一整套动作要重复四遍。要是哪天手抖选错了客户端号登录进去看到的是别的公司代码还得退出来重来。MySapLogon 就是在这种每天都要做、做一次烦一次的场景下长出来的小工具——它把选系统 → 输账号 → 输密码 → 选语言 → 回车这五步压缩成一次双击顺带把密码从明文里救出来。这篇是流星程序集的第十八篇我把这个自动登录 SAP 系统的工具从原理、选型、代码到踩坑完整地摊开讲一遍。它适合两类人一类是每天要在多个 SAP 系统之间来回切的业务顾问、财务、物料、开发同学另一类是要做批量测试、批量截图、批量跑报表的自动化同学。如果你只是偶尔登一次 SAP那这篇文章对你的收益有限但只要你一周有三天以上要开两个以上的客户端后面这些内容能省下的时间是很实在的。1. 每天重复三次的登录动作值得写一个 MySapLogon1.1 手动登录的时间账和出错账先算一笔账。一次完整的人工登录从点击到进入初始屏幕手快的人 12 秒手慢的人 20 秒取中位数 15 秒。一天按开 3 个客户端算45 秒一年 250 个工作日大约是 3.1 小时。这个数字听起来不算惊人但真正的成本不在时间上而在出错率和注意力消耗上。人工登录的错误模式非常固定客户端号敲成 200 而不是 100语言没注意进了一个德语界面然后对着Benutzer发懵密码输到一半被同事喊走回来按了回车结果报了三次失败账号被锁更常见的是——密码框不能复制粘贴密码又要求大小写加数字加符号于是很多人把密码设成了好记但不符合安全策略的弱口令或者干脆写在便签纸上贴在显示器边框。第三种情况才是最要命的为了效率人会自动降低安全标准。自动化登录解决的其实是两个问题一个是省动作一个是把密码从人的记忆和便签里挪到加密存储里。第二个问题比第一个重要得多。很多人做自动登录工具做着做着就变成了把明文密码写进 ini 文件这等于把贴在显示器上的便签变成了贴在共享盘上的便签风险反而放大了。1.2 MySapLogon 真正要啃下的三块骨头把这件事想清楚之后MySapLogon 的设计目标就明确了它必须同时解决三件事第一块是通道。SAP GUI 不是一个开放的窗口程序它对外提供的自动化入口只有SAP GUI Scripting这一条官方路径而这条路径在客户端和服务端各有开关默认状态下大概率是关着的。第二块是凭据。密码怎么存、存成什么格式、换台电脑还能不能用、换个人登录 Windows 还能不能用这几个问题决定了这个工具是能用还是敢用。第三块是登录之后的乱流。点完登录按钮不等于进了系统。多重登录提示、版权信息、系统公告、密码即将过期提醒任何一个弹窗卡在那里自动化就变成了打开了登录框然后发呆。这三块骨头里第一块是技术问题第二块是安全设计问题第三块是耐心问题。我见过很多同类工具第一块做得漂漂亮亮第二块直接躺平第三块压根没考虑结果就是演示的时候很好用真用两周就没人开了。1.3 先说清楚哪几种情况别用自动化登录在动手之前有几个场景我要明确劝退这不是技术做不到而是做了不划算或者不合规。共享账号不要用。如果一个账号是多人共用的虽然 SAP 的授权模型本来就不该这么搞把它加密存在某个人的电脑上本质上是把共享凭据私有化了一旦这个人离职或者换机器谁都不知道密码是什么。这种账号的正确解法是走正式的 SSO 或账号重分配流程。生产系统的敏感账号要谨慎。财务、人事、薪酬相关的账号很多公司的内部审计是明确要求登录动作必须有人工确认环节的。这种账号做自动登录要先跟内控确认别自己先做了再解释。已经上了正式单点登录的环境不要重复造轮子。如果贵司已经通过 SNC 加 Kerberos 那一套做了企业级单点登录登录环节本来就不需要敲密码你再搞一个存密码的小工具属于逆着架构走。排除掉这几种剩下的场景——开发机、测试机、培训机、个人专用的业务账号——就非常适合做自动化。下面讲的所有内容都建立在账号是你个人的、系统是可自动化访问的这个前提上。2. SAP GUI Scripting 是官方留的接口但开关藏在两个地方2.1 客户端开关GUI 选项与注册表两条路SAP GUI Scripting 的客户端开关在 SAP Logon / SAP GUI 的本地布局里。路径是打开 SAP GUI 或 SAP Logon点左上角那把小齿轮定制本地布局进辅助功能与脚本展开脚本勾上启用脚本。同一个页面上还有三个勾选项分别是脚本打开连接时通知脚本连接到已存在的会话时通知脚本重新连接时通知。做自动化的时候这三个全部要取消勾选否则每次脚本一动手屏幕上就弹一个确认框自动化立刻变成半自动。如果你要给十几台机器批量配置一个个点太慢可以走注册表。客户端脚本开关对应的位置在HKEY_CURRENT_USER\Software\SAP\SAPGUI Front\SAP Frontend Server\Security需要的值是值名称类型建议值作用UserScriptingDWORD1启用脚本WarnOnConnectionDWORD0打开新连接时不弹提示WarnOnAttachDWORD0附加到已有会话时不弹提示WarnOnReconnectDWORD0重新连接时不弹提示这四个值改完之后必须重启 SAP GUI 客户端才生效。这一步特别容易被忽略改完注册表直接跑脚本然后怀疑是不是注册表路径写错了实际上是老的进程还在用旧的配置。我踩过至少两次这个坑都是在帮别人远程排查的时候对方说改了没用我让他重启 GUI问题就没了。另外有些企业会在HKEY_LOCAL_MACHINE\Software\SAP\SAPGUI Front\SAP Frontend Server\Security下做更高级别的策略锁定管理员级别设成 0 之后用户级别怎么改都会被覆盖回来。这种情况下你改 HKCU 是白费力气得找 BASIS 或者桌面运维放开。2.2 服务端开关sapgui/user_scripting 与它的连带参数客户端的门开了服务端的门可能还关着。SAP 系统里有一个 Profile 参数叫sapgui/user_scripting默认值是 FALSE。也就是说即使你本地脚本环境全配好了连接上去之后调用脚本接口系统会直接拒绝。这个参数分动态和静态两部分。动态部分可以用事务码 RZ11 直接改改完立即生效不用重启实例静态部分需要改 Profile 文件再重启。做验证的时候先用 RZ11 临时打开最省事登录系统输入事务码RZ11。参数名填sapgui/user_scripting。进入后点击修改把新值设为 TRUE保存。退出重新登录一次这一步很关键脚本权限是在会话建立时确定的。这里有个经验RZ11 改的值在实例重启后会丢失。如果你只是自己做测试没问题如果是给团队用一定要走正式的 Profile 变更流程把这个参数写进实例的 Profile 文件里。否则某天夜里系统重启第二天所有人的自动登录工具集体失效你会被追着问是不是你的程序坏了。同一个参数族里还有几个值得关注的sapgui/user_scripting_disable_recording设成 TRUE 可以禁止录制脚本有些安全要求高的企业会打开它。sapgui/user_scripting_force_notification设成 TRUE 会强制弹出脚本通知和客户端那三个勾选项是配合关系。sapgui/user_scripting_per_user按用户粒度控制比全局开关更精细。如果你的环境里sapgui/user_scripting必须是 FALSE比如生产系统的安全基线不允许那我在 3.1 节讲的 sapshcut 命令行方案就是唯一的出路因为那个方案走的是登录参数传递不依赖脚本引擎。2.3 用五行脚本先验证通道在写整个工具之前先用最短的代码验证通道通不通这个习惯能帮你省下大量排查时间。找一台装好 SAP GUI 的机器新建一个.vbs文件内容如下Set obj CreateObject(Sapgui.ScriptingCtrl.1) Set app obj.GetScriptingEngine Set conn app.OpenConnection(PRD, True) Set sess conn.Children(0) WScript.Echo sess.Info.SystemName / sess.Info.Client / sess.Info.User双击运行。如果弹出一个窗口显示PRD / 100 / 你的用户名说明脚本通道完全打通可以继续往下做。如果报错对照下面这几种情况报ActiveX 部件不能创建对象客户端脚本开关没开或者宿主进程位数和 SAP GUI 不匹配。报OpenConnection失败PRD这个名字在你的 SAP Logon 里不存在名字必须和登录面板上显示的连接名完全一致。弹出脚本安全提示WarnOnConnection那三个值没设对。能连上但Info取不到值服务端sapgui/user_scripting没开。提示OpenConnection的第一个参数既可以是 SAP Logon 里那条连接的名字也可以是一段完整连接串。用连接串的好处是它不依赖你本地的登录面板配置换台机器也能跑。具体格式见 6.1 节。3. 三条实现路线sapshcut、Scripting、混合方案3.1 路线一sapshcut.exe 命令行直连SAP GUI 安装目录里有一个不太引人注意的小程序叫sapshcut.exe它接受一组命令行参数直接拉起一个已登录的会话。典型命令长这样C:\Program Files (x86)\SAP\FrontEnd\SAPgui\sapshcut.exe ^ -systemPRD -client100 -userJDOE -pwYourPassword -languageZH -maxgui几个参数的作用分别是-system指定系统标识-client指定客户端号-user和-pw是账号密码-language是登录语言-maxgui表示最大化窗口。你还可以加-command让它登录后直接跑某个事务码比如-commandSE38。这条路线的优点是极其简单不依赖脚本引擎服务端sapgui/user_scripting关着也能用一行命令解决战斗。缺点是密码写在命令行里同一台机器上任何能跑tasklist或者看进程命令行的人都能读到——Windows 的任务管理器默认不显示命令行但 PowerShell 一条Get-CimInstance Win32_Process就全露出来了。所以这条路线正确的用法是只在内存里拼命令行不要写进 bat 文件。由你的工具解密出密码用进程调用的方式直接把参数传给 sapshcut全程不落盘。3.2 路线二Scripting COM 填表登录第二条路线就是 2.3 节验证过的那套用脚本引擎模拟人工填表。完整一点的版本是这样Set obj CreateObject(Sapgui.ScriptingCtrl.1) Set app obj.GetScriptingEngine Set conn app.OpenConnection(PRD, True) Set sess conn.Children(0) sess.findById(wnd[0]/usr/txtRSYST-BNAME).Text JDOE sess.findById(wnd[0]/usr/pwdRSYST-BCODE).Text YourPassword sess.findById(wnd[0]/usr/txtRSYST-LANGU).Text ZH sess.findById(wnd[0]).sendVKey 0这里有几个细节值得展开。txtRSYST-BNAME是用户名字段pwdRSYST-BCODE是密码字段txtRSYST-LANGU是语言字段wnd[0]是主窗口。控件 ID 不是背下来的是用 GUI 自带的脚本录制和回放功能录出来的。操作方式是SAP GUI 定制本地布局 → 脚本 → 脚本录制和回放打开录制选一个存放目录然后你在系统里正常操作一遍登录停止录制目录里就会生成一个.vbs里面每一行都是准确的控件 ID。这个功能是 SAP GUI Scripting 里最被低估的东西。很多人靠猜 ID 前缀txt是文本框、ctxt是带搜索帮助的文本框、pwd是密码框、btn是按钮、rad是单选按钮去拼拼错了就报找不到元素然后浪费半小时。录制一遍五秒钟解决。这条路线的优点是精确、可控能处理登录后的各种弹窗能校验登录结果还能顺带做后续的自动化操作。缺点是服务端必须开脚本而且比 sapshcut 慢一点因为要等窗口渲染。3.3 路线三混合方案与它的取舍理由我在 MySapLogon 里最终用的是混合方案逻辑是这样的先用 sapshcut 把会话拉起来拿到一个已经填好账号密码的登录界面实际上 sapshcut 会自动提交所以是直接进入系统然后如果服务端允许脚本就用脚本引擎接管后续的弹窗处理和结果校验如果服务端不允许脚本就退化为纯 sapshcut 模式只保证开起来不做后续校验。为什么要这么绕因为纯 sapshcut 有两个硬伤一是它无法确认到底登录成功了没有如果密码错了它照样拉起一个界面然后停在那里报用户名或密码错误而你的工具以为一切正常二是它处理不了登录后的弹窗。而纯 scripting 的问题是它依赖服务端参数很多客户环境里这个参数是锁死的你没法要求 BASIS 为了你一个小工具去改生产系统的 Profile。混合方案的核心判断逻辑是On Error Resume Next Set app CreateObject(Sapgui.ScriptingCtrl.1).GetScriptingEngine If Err.Number 0 Then 脚本可用走完整流程 Else 脚本不可用退化为纯命令行拉起 End If On Error GoTo 0这个先探测、再降级的思路是这类小工具能不能在真实企业环境里活下来的关键。永远不要假设运行环境和你开发机的环境一样这是我做了十几个这类小工具之后最深的体会。3.4 三路线对照表维度sapshcut 命令行Scripting COM混合方案服务端脚本参数要求不要求必须为 TRUE有则用无则退化密码暴露面进程命令行可见仅在内存与脚本变量中取决于降级路径登录结果校验无可精确校验有脚本时可校验弹窗处理能力无强有脚本时强实现复杂度低中中高适用场景生产环境受限开发测试环境通用这张表我贴在自己的工具文档里每次有人问为什么不用 XX 方案直接甩表。4. 密码不能明文躺硬盘DPAPI 加密与配置结构设计4.1 为什么 Base64 等于没加密先做一个思想实验。你写了一个配置文件[PRD] userJDOE passwordSk9ITjIwMjUh这个Sk9ITjIwMjUh看起来像乱码但任何人把这段字符串丢进浏览器控制台跑一句atob(Sk9ITjIwMjUh)一秒钟就还原成明文。Base64 是编码不是加密它的设计目标是把二进制数据塞进文本协议从来就没打算保护任何东西。用 Base64 存密码安全性等于零只是让密码看起来不那么扎眼而已。再往上一层是自定义异或加密或者硬编码密钥的 AES。这两个稍微好一点但因为密钥就在程序里任何人反编译一下 exe 或者用strings命令扫一遍文件密钥就到手了。这类方案能挡住的是同事随手打开你的配置文件看一眼挡不住任何有意愿的人。4.2 DPAPI把密钥交给 Windows 账号Windows 提供了一个叫 DPAPI 的接口中文一般叫数据保护 API它能做到的事是用当前 Windows 用户的登录凭据派生密钥来加密数据。这意味着两件事——第一加密出来的密文只有同一个 Windows 用户能解开第二你不需要自己管理密钥密钥由系统托管。这个特性正好匹配我们的场景。同事拷走你的配置文件在他的电脑上解不开因为他不是同一个 Windows 用户你把电脑重装系统换了账号之前的密文也解不开这是设计上的取舍不是 bug。C# 版本最省事直接调ProtectedDatausing System.Security.Cryptography; using System.Text; public static string Protect(string plain) { byte[] raw Encoding.UTF8.GetBytes(plain); byte[] enc ProtectedData.Protect(raw, null, DataProtectionScope.CurrentUser); return Convert.ToBase64String(enc); } public static string Unprotect(string cipher) { byte[] enc Convert.FromBase64String(cipher); byte[] raw ProtectedData.Unprotect(enc, null, DataProtectionScope.CurrentUser); return Encoding.UTF8.GetString(raw); }C# 里DataProtectionScope有两个枚举值CurrentUser和LocalMachine。一定要用CurrentUserLocalMachine意味着这台机器上任何用户都能解密安全性差一大截。Python 版本需要pywin32import win32crypt blob win32crypt.CryptProtectData(bYourPassword, MySapLogon, None, None, None, 0) with open(prd_pw.bin, wb) as f: f.write(blob) with open(prd_pw.bin, rb) as f: data f.read() desc, plain win32crypt.CryptUnprotectData(data, None, None, None, 0) print(plain.decode())PowerShell 版本更短用ConvertFrom-SecureString就能实现同样效果它底层也是 DPAPI$sec ConvertTo-SecureString YourPassword -AsPlainText -Force $enc ConvertFrom-SecureString $sec $enc | Out-File .\prd_pw.txt # 解密 $sec2 Get-Content .\prd_pw.txt | ConvertTo-SecureString $bstr [Runtime.InteropServices.Marshal]::SecureStringToBSTR($sec2) [Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr)三种语言任选思路完全一致加密一次把密文写进配置文件运行时解密用完立刻把明文变量清掉。VBScript 里没有真正的内存擦除但至少可以做到password 减少它存活的时间窗口。4.3 配置文件长什么样我的配置文件用的是 JSON结构大致如下{ version: 1, systems: [ { name: PRD-生产, conn: /H/sap-prd.example.local/S/3200, client: 100, user: JDOE, lang: ZH, secret: AQAAANCMnd8BFdERjHoAwE/ClsBAAAA..., autoCommand: , order: 1 }, { name: QAS-测试, conn: /H/sap-qas.example.local/S/3200, client: 200, user: JDOE, lang: ZH, secret: AQAAANCMnd8BFdERjHoAwE/ClsBAAAA..., autoCommand: SE38, order: 2 } ] }几个设计说明。name是给人看的用中文加系统类型比纯 SID 好认conn是给程序用的连接串不依赖本地的 SAP Logon 面板secret是 DPAPI 加密后的 Base64 密文注意它和 Windows 账号绑定所以这个字段不应该进 Gitorder控制批量启动的顺序我一般把最耗时的生产系统放最后启动让它慢慢初始化。还有一个细节配置文件里不放密码只放加密后的密文。首次配置时运行一个单独的MySapLogon --setup交互式输入密码程序调 DPAPI 加密后写回配置。这样即便配置文件被人看到也拿不到任何东西。5. 登录框点完只是开始会打断自动化的几类弹窗5.1 多重登录提示最容易撞上的是多重登录提示。当同一个账号已经在系统里存在活动会话时SAP 会弹一个对话框问你是结束其他登录并继续还是取消本次登录。这个弹窗的控件 ID 里包含radMULTI_LOGON_OPT这样的前缀一般有两个单选项加一个确认按钮。处理逻辑是登录提交之后不要立刻认为成功而是轮询检查wnd[1]是否存在存在且里面能通过findById找到usr/radMULTI_LOGON_OPT2这类控件就选中它再按确认。If sess.Children.Count 1 Then Dim w1 Set w1 sess.Children(1) If Not IsNull(w1) Then sess.findById(wnd[1]/usr/radMULTI_LOGON_OPT2).Select sess.findById(wnd[1]/tbar[0]/btn[0]).press End If End If我个人的选择是选中继续而不是结束其他登录因为结束其他会话可能会导致你正在跑的后台作业报表被中断。这个取舍在批量启动多个客户端的时候尤其重要——如果你第二台的自动化把第一台的会话踢掉了工具看起来成功了实际上你前面的工作全白做了。5.2 版权信息与系统公告SAP GUI 在首次连接某个版本的补丁包时会弹一次版权信息点一下继续就没了之后因为版本没变所以不会再弹。这个弹窗本身不麻烦麻烦的是它会出现在第一次运行的时候而你正好在测自动化于是你以为是自己代码写坏了。比它更烦的是系统公告。管理员可以用 SM02 往系统里发全局消息用户一登录就弹出来。有些公司的公告是每天弹的内容还是纯文本自动化脚本如果不处理每次登录都要卡在那。好消息是这类公告弹窗的按钮 ID 通常也是wnd[1]/tbar[0]/btn[0]或者wnd[1]/usr/btnSPOP-OPTION1可以统一处理。我的做法是写一个通用的弹窗收割函数登录提交之后循环最多四次每次看看有没有wnd[1]有就按类型处理处理完再检查一次直到没有弹窗为止。这个循环的上限一定要设否则万一遇到一个点了按钮又弹出来的死循环工具就挂在那了。5.3 密码过期与首次登录改密这类弹窗绝对不能自动点掉。SAP 的密码策略通常设置了密码有效期快到期时系统会提示密码将在 N 天后过期是否现在修改。如果你写了一个自动点是的逻辑接着就是一个改密对话框需要输入旧密码、新密码、确认新密码——你的脚本没有新密码于是整个流程崩在半路更糟的是可能因为多次尝试被锁定。正确的处理方式是识别出来、停下来、告警。检测到这类弹窗标题或控件里带密码相关字样时直接把当前会话的状态标记为需要人工介入弹一个系统托盘通知然后退出自动化流程。用户看到通知手动处理改密下次用新密码重新跑一遍 setup。首次登录改密是同理。新开账号或者管理员重置密码之后SAP 会强制要求改密这个环节必须人工完成。工具要做的是别去解析这个界面因为它的控件结构在不同版本之间差异很大硬编码容易出事。5.4 弹窗处理的通用写法把上面几种情况归纳一下弹窗处理的结构大概是这样一个函数Function HandlePopups(sess) Dim i, wnd, txt HandlePopups OK For i 1 To 4 Set wnd Nothing On Error Resume Next Set wnd sess.findById(wnd[1]) On Error GoTo 0 If wnd Is Nothing Then Exit Function txt wnd.Text If InStr(txt, 多个登录) 0 Or InStr(txt, Multiple) 0 Then sess.findById(wnd[1]/usr/radMULTI_LOGON_OPT2).Select sess.findById(wnd[1]/tbar[0]/btn[0]).press ElseIf InStr(txt, 密码) 0 And InStr(txt, 过期) 0 Then HandlePopups NEED_MANUAL Exit Function Else On Error Resume Next sess.findById(wnd[1]/tbar[0]/btn[0]).press On Error GoTo 0 End If Next End Function这段代码里有两个坑我想单独提一下。第一Set wnd Nothing这一句不能省因为On Error Resume Next遇到 findById 失败时不会改变变量的值如果你不主动清空上一轮的 wnd 对象还在下一轮判断Is Nothing就会误判。第二wnd.Text取的是弹窗标题栏的文本不同语言版本这个文本不一样所以不要只匹配中文最好中英文关键字都覆盖或者干脆改用控件 ID 判断——ID 是稳定不变的文本不是。说到这里补一句SAP 的很多界面文本会跟着登录语言变化所以任何基于文本匹配的逻辑都是脆弱的。真正稳的做法是尽量用控件 ID 和控件类型来判断。6. 多系统多客户端连接参数从哪来、怎么管6.1 从 SAPUILandscape.xml 与 saplogon.ini 里取参数手敲连接串容易错尤其是带消息服务器的系统格式长这样/H/sap-prd.example.local/S/3200带消息服务器和登录组的格式更长/H/sap-prd.example.local/S/3600/M/sap-ms.example.local/S/3601/G/PUBLIC这两串东西不用自己拼它们就躺在你本地的配置文件里。老版本 SAP GUI 用的是saplogon.ini一般位于%APPDATA%\SAP\Common\目录下里面每个系统一段Conn那一行后面跟的就是完整的连接串。新版本7.60 之后改用了SAPUILandscape.xml同一个目录下也有一份全局的在C:\ProgramData\SAP\SAPGUILandscape\下里面用 XML 节点描述每个系统的Server、SystemId、MessageServer等信息。MySapLogon 里我做了个导入功能读取SAPUILandscape.xml把里面所有系统列出来让用户勾选要加进来的然后自动填充conn、system字段用户只需要补客户端号、账号和密码。这个功能看起来很小但实际省事很多因为手抄连接串特别容易把端口号抄错而端口号抄错的报错信息非常含糊排查起来很痛苦。SAP GUI 版本本地配置文件名典型路径7.40 及更早saplogon.ini%APPDATA%\SAP\Common\7.50 过渡期两者都有%APPDATA%\SAP\Common\7.60 及之后SAPUILandscape.xml%APPDATA%\SAP\Common\ 与 C:\ProgramData\SAP\SAPGUILandscape\6.2 客户端号、语言、登录组的变体管理同一个系统往往有多个变体。比如 PRD 系统里100 是集团总部、200 是某个子公司语言方面有人习惯中文有人习惯英文做报表的时候英文界面反而更好认字段名登录组方面大型系统会配置多个登录组做负载分发。我处理变体的方式是在配置里允许一个系统多条条目用name区分。比如PRD-总部-中文client100, langZHPRD-总部-英文client100, langENPRD-子公司-中文client200, langZH这样看起来配置项变多了但用起来很直观想开哪个双击哪个。比在工具里搞一堆下拉框去选客户端号和语言要简单得多而且配置本身就是文本复刻到另一台机器上只需要处理加密字段的不同。唯一需要注意的是语言字段只影响登录界面和菜单文本不影响数据本身。有人以为用 EN 登录看到的报表数字和 ZH 登录不一样这是误解。但语言确实影响日期格式和数字的小数点/千分位显示做数据比对的时候要留意。6.3 批量启动时的顺序与并发一键启动四个客户端的体验很爽但有两点要控制。第一是并发。同时拉起四个 SAP GUI 进程每个进程启动要占用不少内存老机器上可能会卡住一两秒甚至触发内存告急。我的做法是串行启动每个之间间隔 3 到 5 秒等上一个的登录界面稳定了再起下一个。这个间隔不是随便定的是测出来的SAP GUI 从进程启动到登录界面可交互冷启动大约 4 到 8 秒热启动 2 到 3 秒。间隔设成 3 秒在大多数机器上够用如果发现偶发失败就往上调到 5 秒。第二是顺序。生产系统通常最慢因为要做各种权限检查和数据初始化把它放最后启动可以和前面几个的启动时间重叠掉。反过来如果你把生产放第一个然后干等它启动完再启其他的总时间反而更长。串行启动的伪代码大概是这样foreach ($sys in $config.systems | Sort-Object order) { $pw Unprotect-Dpapi $sys.secret Start-Process $sapshcut -ArgumentList ( -system$($sys.name), -client$($sys.client), -user$($sys.user), -pw$pw, -language$($sys.lang) ) $pw $null Start-Sleep -Seconds 3 }注意$pw $null这一句用完立刻把明文变量清掉这是好习惯。7. 实机踩坑与排查清单7.1 CreateObject 失败 的四种成因这是最高频的报错至少有四种完全不同的成因需要分别排查。第一种客户端脚本开关没开。回到 SAP GUI 定制本地布局里的脚本页面看一眼别忘了改完注册表要重启 GUI。第二种宿主进程位数不匹配。SAP GUI for Windows 长期以来是 32 位程序它注册的 COM 组件也注册在 32 位视图下。如果你用 64 位的 PowerShell 或者 64 位的 Python 去CreateObject可能找不到这个类。解决办法是把脚本宿主换成 32 位版本。PowerShell 有%SystemRoot%\SysWOW64\WindowsPowerShell\v1.0\powershell.exe这个 32 位入口Python 则要装 32 位解释器。第三种SAP GUI 没有作为 Automation 服务器注册。正常情况下安装 SAP GUI 时会注册但如果用的是绿色版或者拷贝版可能没注册。可以用regsvr32手动注册安装目录下的sapgui.ocx之类的组件不过更稳妥的做法是重新跑一遍官方安装包。第四种用了GetObject(SAPGUI)但当前没有 GUI 实例在跑。GetObject是附加到已有实例如果 SAP GUI 压根没启动它必然失败。要创建新实例应该用CreateObject(Sapgui.ScriptingCtrl.1)。这两个函数经常被混用是新手最容易犯的错。7.2 元素 ID 找不到与控件类型写错排在第二位的坑是找不到元素 wnd[0]/usr/txtRSYST-BNAME这类报错。原因通常是三个。第一个是窗口层次错了。wnd[0]是主窗口如果有弹窗盖在上面你要找的控件可能还在wnd[0]里但输入焦点被wnd[1]抢走了这时候应该先处理wnd[1]。反过来如果你想找的控件本身就在弹窗里那前缀就得写wnd[1]。第二个是控件类型前缀写错。文本框是txt带搜索帮助的文本框是ctxt密码框是pwd按钮是btn单选按钮是rad复选框是chk。我之前把ctxtRSYST-MANDT写成txtRSYST-MANDT报错信息只说找不到元素完全不提类型不对硬是找了十分钟。记住一句话客户端号字段是 ctxt 不是 txt。第三个是界面还没加载完。脚本跑得比界面渲染快尤其是冷启动的第一次登录。解决办法是加轮询等待比如循环检查sess.findById(wnd[0]/usr/txtRSYST-BNAME)是否能取到取到了再往下走最多等 15 秒。不要用固定的WScript.Sleep 5000那个在快机器上浪费四秒在慢机器上又不够。7.3 安全软件、输入法和多显示器的干扰有几个干扰因素跟 SAP 没关系但会影响自动化。端点安全软件。很多企业的终端防护会拦截程序模拟键盘输入和程序操控其他程序窗口这类行为。如果你的脚本用的是脚本引擎内部接口sendVKey、控件的Text赋值一般不会被拦因为它走的是 SAP 自己的 COM 接口。但如果你用SendKeys或者 AutoIt 的键盘模拟去做补充操作被拦的概率就很高。能用 SAP 自己的接口就别用键盘模拟这是原则。中文输入法。输入法在密码框里可能会吞掉第一个字符或者把符号变成全角。手工登录的时候人能看到自动化的时候你只会收到用户名或密码错误。稳妥的做法是自动登录过程中不强依赖输入法状态因为脚本引擎是直接给控件属性赋值不经过输入法但如果你的流程里有SendKeys那就要先把输入法切到英文。多显示器和缩放。这个问题只影响基于屏幕坐标的点击方案。尽量别用坐标点击SAP GUI Scripting 提供的是控件级接口不需要坐标准确。如果你的方案里有坐标那大概率是绕路了回头看看能不能用控件接口替代。7.4 一张可以照着走的排查表把上面所有坑整理成一张表出问题的时候从上往下走现象优先排查方向快速验证方式CreateObject 失败脚本开关 / 宿主位数用 32 位宿主跑 2.3 节的五行脚本OpenConnection 失败连接名或连接串错误手动在 SAP Logon 里连一次同名连接找不到元素窗口层次 / 控件前缀 / 加载未完成用脚本录制功能录一遍完整登录登录后停在错误页密码错误 / 账号锁定手工登录验证账号状态弹窗卡住流程多重登录 / 公告加弹窗收割循环打日志记录每个弹窗标题换台机器跑不通DPAPI 账号绑定在新机器上重跑 setup 重新加密偶发失败重跑就好等待时间不足把固定 sleep 换成轮询等待隔夜全部失效实例重启导致 RZ11 动态参数丢失检查 sapgui/user_scripting 是否走了 Profile最后再提一个我在实际维护里养成的习惯让工具写日志。每次登录尝试记录时间、系统、客户端、耗时、结果、遇到的弹窗标题日志文件按天切分。平时你不看但一旦出问题翻日志比回忆当时屏幕上显示了什么要靠谱得多。我遇到过一次案例连续三天早上第一台客户端必失败第二台正常翻日志发现失败的那次总是多了一个 SM02 公告弹窗而这个公告是每天早上七点五十五分由某个后台作业发出来的——如果我当时没日志这个规律根本找不到。关于 MySapLogon 这个工具本身后续我打算再加两块东西一是把配置里的secret换成本地 Windows 凭据管理器Credential Manager存储走CredWrite/CredRead接口多一层系统级管理二是给每个系统加一个登录后自动执行事务码的开关让它顺带把每天早上要跑的 MD07、KO88 检查也一起做了。等这两个做完大概可以出第十九篇了。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →