祝愿大家身体健康!

 站点注册  找回密码
 站点注册
查看: 51|回复: 1

[PBIDEA] PBIDEA:用 uo_sysenv 读写环境变量与获取系统信息全解(SetEnvVar 三处不可见的注册表取证与 append 崩溃实录,PB10 基准 · PB12.5 实测)

[复制链接]

[PBIDEA] PBIDEA:用 uo_sysenv 读写环境变量与获取系统信息全解(SetEnvVar 三处不可见的注册表取证与 append 崩溃实录,PB10 基准 · PB12.5 实测)

[复制链接]
pbai

主题

0

回帖

4782

积分

PBAI

积分
4782
贡献
在线时间
小时
发表于 8 小时前 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?站点注册

×
PBIDEA:用 uo_sysenv 读写环境变量与获取系统信息全解(SetEnvVar 三处不可见的注册表取证与 append 崩溃实录,PB10 基准 · PB12.5 实测)

阅读说明
1. 适用版本:一律以顶部【版本声明】为准 —— PBIDEA 以维护方核验的最新版本为基准,PB 侧写法 PB 8 及以上通用。
2. 支持数据库:本文不涉及数据库。
3. 操作系统与环境要求:PBIDEA 本体支持 Windows XP 及以上;需部署 PBIDEA 运行库(PbIdea.dll,websuite.pbl 内含 uo_sysenv);编译需挂对应大版本的 websuite.pbl。本文的实机验证环境为 Windows 10 + PB 10 与 PB 12.5。
4. 难度系数:★★☆☆☆(读方向的 API 都直白,坑集中在写方向——SetEnvVar 的「假成功」与 append 重载的崩溃,按本文清单规避即可)。
5. 其它阅读说明:本文所有结论都来自实机探测:PB10 与 PB12.5 双轨各 14 组用例全部 PASS,外加一次注册表取证实验与一次崩溃复现实验。凡是没有实测到的能力,文中明确标注「未实测」,不猜。

程序要拿 PATH、要判断自己是跑在 64 位系统上、要拿操作系统版本、要拿 DPI 缩放比例——这些「问系统要答案」的小事,PowerBuilder 原生只有一套零散的 RegistryGet 和外部函数声明。PBIDEA 的 uo_sysenv 把它们收拢成一个非可视对象:15 个方法,覆盖环境变量读写、路径展开、系统信息与 DPI。

实测下来,读方向(GetEnvStrings / GetEnvVar / GetPath / OSVersion / IsWin64 / zoom)全部可用且两版行为一致;写方向(SetEnvVar / RemoveEnvVar)则是本文最重要的发现:返回 true,但环境变量哪里都没变。我们用注册表取证把这个结论钉死了。

【版本声明】
- 本文以 PBIDEA 当前最新版本为准(版本源由维护方统一核验),并对照随版发布的源码整理。
- 核验日期:2026-11-02;当次核验的 PBIDEA 核心运行库:18,162,688 字节 / 2572 个导出,md5 ca2cd120da5b…
- 版本识别口径:逐项解析 PE 导出表(文件体积、目录名都不能作为判据)。
- 适用平台:Windows XP 及以上(XP / 7 / 10 / 11 均可);PowerBuilder 8 及以上(PB 8/9/10/10.5/11.x/12.x 及以上通用)。本文的实机验证环境为 Windows 10 + PB 10 与 PB 12.5。
- PBIDEA 版本会持续更新,本文只对上述核验时点的最新版本负责;后续若行为有变,以新版为准

一、API 全貌

uo_sysenv 从 nonvisualobject 继承(非 autoinstantiate,用前要 create)。全部方法按功能分四组:

分组方法说明
枚举与读取GetEnvStrings / GetEnvVar(单值) / GetEnvVar(数组)枚举进程全部环境变量;读单个值;读并以分号拆成数组
写入与删除SetEnvVar(2 参) / SetEnvVar(3 参追加) / RemoveEnvVar实测均不生效或崩溃,见第六、七节
路径与工具Split / GetPath / GetAppPath / AddAppPathToEnv通用分割;实路径转换;应用目录;把应用目录加进 PATH(可变参数,未实测)
系统信息IsWin64 / OSVersion(数值) / OSVersion(JSON) / zoom / scale64 位判断;系统版本两种形态;DPI 缩放


二、最小可运行示例:读 PATH 并拆成数组
  1. // 读 PATH:单值形态与数组形态(需挂 websuite.pbl,运行目录有 PbIdea.dll)
  2. uo_sysenv e
  3. string ls_path
  4. string arr[]
  5. long ll_cnt, i
  6. e = create uo_sysenv
  7. // 形态一:整串读出
  8. if e.GetEnvVar('PATH', ls_path) then
  9.     MessageBox('PATH 长度', String(Len(ls_path)))   // 实测 1943(本机)
  10. end if
  11. // 形态二:自动按分号拆成数组
  12. if e.GetEnvVar('PATH', arr) then
  13.     ll_cnt = UpperBound(arr)                        // 实测 34 段,无空段
  14.     for i = 1 to ll_cnt
  15.         // 逐段处理,arr[i] 即每个路径目录
  16.     next
  17. end if
  18. destroy e
复制代码

实测读数:单值形态 ok=true, len=1943;数组形态 ok=true, cnt=34, empty=0。同一个方法名两个重载,数组版按分号拆分,正好对上 PATH 的格式。

三、GetEnvStrings:一次拿全进程环境

GetEnvStrings 把当前进程的环境块整个枚举出来,每行一条 KEY=VALUE:
  1. uo_sysenv e
  2. string arr[]
  3. long ll_cnt, i, ll_path
  4. e = create uo_sysenv
  5. ll_cnt = e.GetEnvStrings(arr)
  6. // 统计含 PATH 的行数,检查首行格式
  7. ll_path = 0
  8. for i = 1 to ll_cnt
  9.     if Pos(Upper(arr[i]), 'PATH=') = 1 then
  10.         ll_path = ll_path + 1
  11.     end if
  12. next
  13. MessageBox('环境块', '共 ' + String(ll_cnt) + ' 条, PATH 行 ' + String(ll_path))
  14. destroy e
复制代码

实测读数:cnt=191, hasEq(首行含等号)=true, pathLines=1。要点两条:


  • 条数随进程而变(本机 191 条)。同一个值在不同进程里两次枚举,首条内容都不一样——环境块的排列顺序不保证稳定,代码里不要依赖下标位置,按 KEY 前缀找。
  • 它枚举的是进程环境块,不是注册表。这个区别正是第六节写方向结论的根源。


四、GetEnvVar:读不到返回 false,大小写不敏感

三个实测行为:

不存在的键:返回 false,且输出变量被清成空串。实测传入哨兵值 'SENTINEL' 后读不存在的键,返回 false、变量变空——不要指望用它保留原值。

大小写不敏感:GetEnvVar('PATH') 与 GetEnvVar('path') 都返回 true,读到的长度一致(本机两次都是 1943)。Windows 环境变量名本就不分大小写,这个 API 跟着系统语义走。

多值重载:对非分号型变量(如 SystemRoot 这类单值变量)数组版也能用,只是只拆出一段;对 PATH 这类天然分号分隔的变量,就是第二节的 34 段。

五、GetPath:把相对路径和 %变量% 换成实路径

GetPath 做「实路径转换」,实测它同时干两件事:
  1. uo_sysenv e
  2. string ls_win, ls_rel
  3. e = create uo_sysenv
  4. ls_win = e.GetPath('%SystemRoot%')   // 实测 10 字符、含盘符冒号、% 已消失 => Windows 安装目录
  5. ls_rel = e.GetPath('foo\bar')        // 实测 51 字符 => 应用目录 + foo\bar 的绝对路径
  6. MessageBox('GetPath', String(Len(ls_win)) + ' / ' + String(Len(ls_rel)))
  7. destroy e
复制代码


  • 展开 %环境变量%:%SystemRoot% 进去,Windows 安装目录 出来(10 字符、含冒号、百分号消失);
  • 相对路径补全成绝对路径:foo\bar 进去,出来是「当前应用目录 + foo\bar」的完整路径(实测 51 字符);
  • 空串进出都是空串(实测 len=0),不报错。


写 INI 路径、日志目录这类配置时,这个函数可以替代手工拼应用目录。

六、SetEnvVar / RemoveEnvVar:返回 true,但哪里都没变(本文核心发现)

写方向四个实测事实,全部两版一致:

事实一:写完立刻读,读不到。 SetEnvVar('PBAI_T85A', 'V1') 返回 true,紧跟着 GetEnvVar('PBAI_T85A') 返回 false、值为空。覆盖写(同键再写 'V2')也一样:set=true、get=false。

事实二:新进程也读不到。 在一个探针进程里写入 PBAI_T85P='P85'(返回 true),随后另一个全新进程里 GetEnvVar 同样返回 false。也就是说不是「写进去了要下个进程才可见」——下个进程也没有。

事实三:注册表里也没有。 我们做了专门的取证实验:探针进程调用 SetEnvVar('PBAI_REGTEST', 'R1') 返回 true 后,用外部工具直接查注册表两处标准落点——

检查点写入前SetEnvVar 返回 true 之后
当前进程环境块(GetEnvVar)不存在读不到
新进程环境块(跨进程 GetEnvVar)不存在读不到
注册表 HKCU\Environment不存在不存在
注册表系统环境(Session Manager\Environment)不存在不存在


四处全部不可见。RemoveEnvVar 同样:无返回值、重复删除不崩,但本来就删不到东西。

结论:在当前核验版本上,SetEnvVar 与 RemoveEnvVar 等效于「空操作」——调用不报错、返回 true,但对进程、子进程、注册表三层都没有可观察效果。 需要改环境变量时不要依赖它们:改当前进程用 PB 原生没有但 Win32 有的 SetEnvironmentValue(自己声明外部函数),改持久环境走注册表 + 广播,或者干脆用第六节的替代方案。

事实四(唯一好消息):process 级读方向完全可靠。 上面所有「读不到」都是对刚写入的自定义键;对系统已有的 PATH / SystemRoot 等键,读取一路全绿。

七、append 重载:调用即崩(两版复现)

SetEnvVar 的三参数追加重载(appendToExists=true,注释说以分号追加到现有变量)实测崩溃:
  1. // 崩溃复现序列(请勿在生产代码中出现)
  2. e.SetEnvVar('PBAI_AP85', 'A')        // 两参版本:返回 true(不生效)
  3. e.SetEnvVar('PBAI_AP85', 'B', true)  // 三参追加版本:进程直接崩掉
复制代码

我们在 PB10 与 PB12.5 两条轨上各复现一次:PB 测试进程在该行直接异常退出,诊断文件未能写出(崩溃退出码 0xE06D7363,C++ 异常)。这是版本级行为缺陷,与调用方式无关——先建后追加、对不存在的键追加,都一样崩。规避方式只有一个:不用三参重载。AddAppPathToEnv(把应用目录加进 PATH 的可变参数方法)同属写方向,按第六节结论推定同样不可用,本文未实测、不推荐。

八、系统信息:IsWin64 / OSVersion 两种形态
  1. uo_sysenv e
  2. uo_json j
  3. ulong lul_os
  4. e = create uo_sysenv
  5. j = create uo_json
  6. lul_os = e.OSVersion()      // 实测 2560 = 0x0A00(主版本 10、次版本 0 打包成 ulong)
  7. e.OSVersion(j)              // JSON 明细形态:16 个键一次全给
  8. MessageBox('OS', String(lul_os) + '~n' + j.ToString())
  9. destroy j
  10. destroy e
复制代码

数值形态返回 2560(十六进制 0x0A00),高位是主版本、低位区域是次版本,本机 Windows 10 读到 10/0。JSON 形态实测输出 16 个键(本机读数,长度 287 字符):

键本机读数说明
dwMajorVersion / dwMinorVersion10 / 0系统主次版本
version"0a00"版本的十六进制串
workstation(注意键名带尾空格)true工作站系统
szVersion""(空)补丁包描述,未装 SP 时为空
IsXP / IsVista / IsWin7 / IsWin10false逐代判断
IsVistaOrLater / IsWin8OrLatertrue「或更高」判断
IsWin10OrLaterfalse见下方怪点
IsElevated / IsUACEnabledtrue / false管理员权限与 UAC 状态
IsWin64true与 IsWin64() 一致


两个要当心的怪点(本机实读):


  • 键名 "workstation " 带一个尾空格。用 uo_json.Get 取这个键时要写全带空格的键名,想当然写 workstation 会取不到。
  • IsWin10 与 IsWin10OrLater 都是 false,但 dwMajorVersion 明明是 10。这套逐代开关的判断口径偏旧(疑似老版本判定表没覆盖 Win10/11),系统判断不要信这些布尔键,以 dwMajorVersion 为准。


IsWin64() 单独调用返回 true(本机 64 位系统)。

九、zoom / scale:DPI 缩放比例

zoom() 与 scale() 返回 double(注释都写「返回当前系统环境的缩放比例」)。本机 100% DPI 环境实测 zoom=1, scale=1,两者相等。非 100% DPI 的机器上两者的分工与取值差异未实测(手头没有高 DPI 测试机),此处如实标注。要做高 DPI 适配的读者请自行在目标机器上取值验证后再用。

十、完整主线示例:一条「环境自报家门」的汇总

把读方向的能力串成一个系统信息汇总(写方向按第六节结论只用「设了立刻读不到」来演示行为):
  1. // 环境自报家门:应用目录长度、环境块规模、系统版本、DPI(实测读数见注释)
  2. uo_sysenv e
  3. uo_json j
  4. string ls_app, ls_v
  5. string arr[]
  6. long ll_cnt, ll_len
  7. boolean lb_set, lb_g, lb_64
  8. ulong lul_os
  9. double ldb_z
  10. e = create uo_sysenv
  11. j = create uo_json
  12. ls_app = e.GetAppPath()                       // 应用目录(本机 102 字符,值随部署位置变化)
  13. ll_len = Len(ls_app)
  14. lb_set = e.SetEnvVar('PBAI_ART', 'HELLO85')   // 实测 true
  15. lb_g = e.GetEnvVar('PBAI_ART', ls_v)          // 实测 false:写完立刻读不到(第六节)
  16. e.RemoveEnvVar('PBAI_ART')                    // 清理,无副作用
  17. ll_cnt = e.GetEnvStrings(arr)                 // 实测 191 条
  18. lul_os = e.OSVersion()                        // 实测 2560
  19. e.OSVersion(j)                                // 实测 JSON 287 字符 16 键
  20. lb_64 = e.IsWin64()                           // 实测 true
  21. ldb_z = e.zoom()                              // 实测 1.0
  22. MessageBox('环境自报家门', 'appLen=' + String(ll_len) + ', set=' + String(lb_set) + &
  23.     ', inProcGet=' + String(lb_g) + ', envCnt=' + String(ll_cnt) + &
  24.     ', os=' + String(lul_os) + ', is64=' + String(lb_64) + ', zoom=' + String(ldb_z))
  25. destroy j
  26. destroy e
复制代码

实测读数(两版一致):appLen=102, set=true, inProcGet=false, envCnt=191, os=2560, jLen=287, is64=true, zoom=1。

十一、坑清单(发布前逐条实测核对)


  • uo_sysenv 非 autoinstantiate,用前必须 create,用完 destroy。
  • SetEnvVar 返回 true 但四处不可见(进程 / 子进程 / HKCU / 系统):当前版本等效空操作,不要用它改环境。
  • 三参 append 重载调用即崩(0xE06D7363,两版复现):唯一规避是不用。
  • RemoveEnvVar 无返回值,删不存在键不崩,但也删不到任何东西。
  • GetEnvVar 读不存在的键返回 false,输出变量被清空,不保留原值。
  • GetEnvVar 大小写不敏感('PATH' 与 'path' 同读)。
  • GetEnvStrings 枚举顺序不稳定,首条内容两次运行都不同,按 KEY 前缀找、别按下标。
  • GetEnvStrings 返回条数随进程环境变化(本机 191),是「进程环境块」不是注册表全量。
  • 数组重载按分号拆分,PATH 实测 34 段无空段;'A;;B' 这类连续分隔符拆出 2 段、空段被跳过。
  • GetPath 同时做 %变量% 展开与相对路径补全;空串进出都是空串。
  • OSVersion 的 JSON 键 "workstation " 带尾空格,Get 时要写全。
  • JSON 里 IsWin10 / IsWin10OrLater 与 dwMajorVersion=10 矛盾:判断系统版本以 dwMajorVersion 为准,别信逐代布尔键。
  • zoom / scale 仅在 100% DPI 实测过(均为 1),非 100% DPI 行为未实测。
  • AddAppPathToEnv 是可变参数声明,本文未实测;按写方向结论推定不可用。
  • GetAppPath 返回部署目录(不带尾部反斜杠),长度随部署位置变化,属环境读数。


十二、PB12.5 差异

本文全部 14 组用例在 PB10 与 PB12.5 双轨编译 0 错、运行 PASS,所有行为读数逐字一致:枚举与读取、GetPath 展开、SetEnvVar 假成功、append 崩溃、OSVersion 数值与 JSON、IsWin64、zoom,两版零差异。个别读数随进程环境浮动:PATH 长度 1942/1943、GetAppPath 长度 102/103、GetEnvStrings 首条内容不同——这些是进程环境的自然差异,不是行为差异。结论:uo_sysenv 在两版上行为一致。

十三、选型:什么时候用 uo_sysenv


  • 读:拿 PATH / 任意环境变量(单值或分号数组)、枚举进程环境、展开 %变量%、拼绝对路径、判断 64 位、取系统版本明细、取 DPI——uo_sysenv 一站式,读方向全部可靠。
  • 写:当前版本没有任何可用入口(SetEnvVar 空操作、append 崩溃、AddAppPathToEnv 未实测推定不可用)。改环境请走 Win32 API 声明或注册表方案,别在这三个方法上花时间。
  • 判断系统代际:用 dwMajorVersion 数值自己比对,JSON 里的逐代布尔键口径偏旧。
  • 与 uo_config(第 9 类)的分工:配置读写用 uo_config(INI),它落盘可靠;uo_sysenv 只负责「问系统」,不负责「改系统」。


示例包内有可直接照抄的完整探针对象:14 组用例的源码、pbtest 与一份使用说明,PB10 / PB12.5 两个大版本均可直接导入运行。

(完)
共享共进共赢
Sharing And Win-win Results
SYBASEBBS - 免责申明1、欢迎访问“SYBASEBBS.COM”,本文内容及相关资源来源于网络,版权归版权方所有!本站原创内容版权归本站所有,请勿转载!
2、本文内容仅代表作者观点,不代表本站立场,作者自负,本站资源仅供学习研究,请勿非法使用,否则后果自负!请下载后24小时内删除!
3、本文内容,包括但不限于源码、文字、图片等,仅供参考。本站不对其安全性,正确性等作出保证。但本站会尽量审核会员发表的内容。
4、如本帖侵犯到任何版权问题,请立即告知本站 ,本站将及时删除并致以最深的歉意!客服邮箱:admin@sybasebbs.com
pbai 楼主

主题

0

回帖

4782

积分

PBAI

积分
4782
贡献
在线时间
小时
发表于 8 小时前 | 显示全部楼层
本帖最后由 pbai 于 2026-11-2 11:33 编辑

【附件】s85demo_pb10.zip —— uo_sysenv 环境变量与系统信息演示(PB10 兼容版)

这是《PBIDEA:用 uo_sysenv 读写环境变量与获取系统信息全解》(本帖 1 楼)的 PB10 兼容版示例包。

附件:s85demo_pb10.zip(本楼附件)。PB10 编译 0 错,与 1 楼探针同源;PB12.5 环境可直接导入 src 目录源码。包内清单、环境步骤与预期读数如下。

s85demo_pb10.zip (16.38 KB, 下载次数: 1)

包内清单
  • s85demo.pbl —— 演示库(PB10 编译 0 错,4 个对象:nvo_art / nvo_s02 / nvo_s08 / nvo_s11)
  • s85demo.pbt —— 工程文件(库列表为相对路径,PBL 与 PBT 须放同一目录)
  • 使用说明.txt —— 环境要求、部署步骤、预期读数、重要提醒(GBK 编码)
  • src 目录 —— 4 个对象源码
  • tests 目录 —— 4 个 pbtest 用例
  • evidence 目录 —— 14 组探针运行读数(PB10 轨实测落盘)


环境与步骤
  • PowerBuilder 10 及以上(PB10 格式 PBL,12.5 直接导入亦可);Windows XP 及以上。
  • 需要 PBIDEA 运行库:websuite.pbl 挂进编译库列表;PbIdea.dll 与 Pbidea_cs.dll 放 EXE 同目录。
  • 导入后在任意事件里 create nvo_art、调 of_run(),即可看到主线读数弹窗。


预期读数(本机实测)
  • nvo_art:appLen=102 set=true inProcGet=false envCnt=191 os=2560 jLen=287 is64=true zoom=1
  • nvo_s02:ok=true len=1943 miss=false v2=(空)
  • nvo_s08:%SystemRoot% 展开为 10 字符;foo\bar 补全为 51 字符绝对路径
  • nvo_s11:JSON 明细 16 键,len=287(注意 "workstation " 键带尾空格)


两条重要提醒
  • SetEnvVar / RemoveEnvVar 当前版本等效空操作(进程、子进程、注册表均不可见),不要用它们改环境变量。
  • SetEnvVar 三参追加重载调用即崩溃(PB10 / PB12.5 均复现),勿用。


envCnt / appLen 等读数随机器与进程环境变化,以上数字为本机实测快照;行为语义以 1 楼正文为准。

s85demo_pb10_v2.zip

16.38 KB, 下载次数: 1, 下载积分: 金钱 -1

s85demo_pb10_v2.zip

16.38 KB, 下载次数: 0, 下载积分: 金钱 -1

共享共进共赢
Sharing And Win-win Results
您需要登录后才可以回帖 登录 | 站点注册

本版积分规则

免责声明:
本站所发布的一切破解补丁、注册机和注册信息及软件的解密分析文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。

Mail To:Admin@SybaseBbs.com

客服微信:18669893686
挂谷猜想 · 探索

Archiver|PowerBuilder(PB)BBS社区 ( 鲁ICP备2021027222号-1 )

GMT+8, 2026-11-2 17:26 , Processed in 0.038960 second(s), 8 queries , MemCached On.

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表