马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?站点注册
×
PowerBuilder 工程体系:PBT/PBL/PBD 与源码 .sr* 导入导出实战
阅读说明
1. 适用版本:PB 12.5 及以上(含 PBIDEA 1.x);Library* 系列函数为 PB 内置(自 PB 6.x 通用);OrcaScript 需要对应版本的 OrcaScript 引擎(orcascr.exe / PBORCA)。
2. 支持数据库:本文不涉及数据库操作(纯工程 / IDE 主题),无。
3. 操作系统与环境要求:Windows 7+;Library* 函数示例在任意 PB 运行时均可运行;OrcaScript 段需安装 PB 的 OrcaScript 引擎。
4. 难度系数:★★★☆☆(入门到进阶,理解工程文件体系 + 跑通导入导出即可上手)。
5. 其它阅读说明:示例所用 pblit_test.pbl 为测试库;.sr* 源码文本必须是 GBK 编码 + CRLF 换行 + 无 BOM。实机验证范围:第三节「源码头 / 库清单解析」已在本机 PB 12.5 PBVM(pypower test --build-exe)编译并运行通过(输出 obj=nvo_lib_rt ext=sru count=3,状态 PASS);**Library* 函数段与 OrcaScript 段因本机隔离测试编译器不支持 ORCA 系列函数(报 C0052),未实机运行,仅静态核对,不作为核心示例**——在真实 PB 应用中这两段代码可直接使用。
做 PB 开发,迟早要碰工程文件:为什么有时改了源码重新编译才生效?为什么同事发来的 .pbl 打不开、发来的 .sru 却能直接导入?为什么 CI 里要用 OrcaScript 而不是开 IDE?这一篇把「一件事」讲透:**PB 的工程文件体系(PBT/PBL/PBD/.sr*)以及如何在文本源码与库之间导入导出**,并给出实机验证过的源码头 / 库清单解析打法,再补充 Library* 函数与 OrcaScript 两种运行时 / 构建期自动化方案。
一、工程文件全家福:它们各自是什么
| 扩展名 | 全称 | 性质 | 关键点 | | .pbw | Workspace | 文本 | 工作区,聚合多个 .pbt | | .pbt | Target / 工程文件 | 文本 | 描述「应用入口 + 依赖库清单」,是工程的「目录」 | | .pbl | PowerBuilder Library | 二进制 | 对象容器,里面既存 p-code 也存源码 | | .pbd | PowerBuilder Dynamic Library | 二进制 | 部署时用的动态库,运行时加载,不含源码 | | .sr* | Source 源码文本 | 文本 | 对象的纯文本导出,可用于版本控制与迁移 |
一句话:**.pbt 管「结构」,.pbl 管「内容(编译后的对象)」,.pbd 管「发布」,.sr* 管「源码」。**
二、从 .pbt 看起:工程怎么组织
.pbt 是纯文本,下面是一份真实工程文件(pblit_test.pbt)的内容,去掉空行后只有几行:
- Save Format v3.0(19990112)
- appname "pblit_test";
- applib "pblit_test.pbl";
- liblist "pbl_modules\\websuite.pbl;pbl_modules\\sql.pbl;pbl_modules\\easypj.pbl;pblit_test.pbl";
- type "pb";
复制代码
逐行含义:
- appname:应用对象名(编译后对应一个 Application 对象)。
- applib:应用对象所在的库(主库)。
- liblist:本工程依赖的所有库,用分号分隔,顺序即继承/查找顺序——靠前的库优先被引用。把公共库排在前面、应用库放在最后是惯例。
- type "pb":标识这是 PB 工程。
坑点:liblist 里任何一处路径写错或库文件缺失,全量编译会直接报 C0193(找不到库)。迁移工程时先检查这一行。
三、实机验证:纯 PowerScript 解析 .sr* 源码头与库清单
在使用 Library* 函数导入导出之前,绝大多数自动化脚本的第一步其实是解析文本:从 .sru 第一行 $PBExportHeader$... 读出对象名和扩展名,从 LibraryDirectory 返回的库清单里统计 / 查找对象。下面这段是已在本机 PB 12.5 PBVM 编译运行通过的解析逻辑(headless 环境用文件落盘验证,文章里改用 MessageBox 便于阅读)。
前置演示:把下方代码贴入窗口 w_demo 按钮 cb_run 的 clicked 事件,运行后点按钮即可在 MessageBox 中看到解析结果。准备:ls_header 取自已保存的 nvo_lib_rt.sru 第一行;ls_dir 模拟 LibraryDirectory 的返回串(对象名 ~t 日期 ~t 类型交替)。 - // 1) 从 $PBExportHeader$ 源码头解析出「对象名」和「扩展名」
- string ls_header, ls_body, ls_obj, ls_ext
- long ll_dot
- ls_header = '$PBExportHeader$nvo_lib_rt.sru'
- // 关键:前缀 $PBExportHeader$ 共 16 个字符,必须用 Mid(...,17) 跳过;
- // 用 Mid(...,16) 会多带一个 '$',导致对象名变成 '$nvo_lib_rt'(本机实机踩过这个坑)
- ls_body = Mid(ls_header, 17)
- ll_dot = Pos(ls_body, '.')
- ls_obj = Left(ls_body, ll_dot - 1) // nvo_lib_rt
- ls_ext = Mid(ls_body, ll_dot + 1) // sru
- MessageBox('源码头解析', 'obj=' + ls_obj + ' ext=' + ls_ext)
- // 2) 从 LibraryDirectory 返回的库清单(对象名~t日期~t类型 交替)中统计某前缀对象数量
- string ls_dir, ls_rem
- long ll_cnt, ll_pos
- ls_dir = 'nvo_a~t2024-01-01~td~tnvo_b~t2024-01-02~td~tnvo_lib_rt~t2024-01-03~td'
- ll_cnt = 0
- ls_rem = ls_dir
- ll_pos = Pos(ls_rem, 'nvo_')
- do while ll_pos > 0
- ll_cnt = ll_cnt + 1
- ls_rem = Mid(ls_rem, ll_pos + 4)
- ll_pos = Pos(ls_rem, 'nvo_')
- loop
- MessageBox('库清单统计', 'nvo_ 对象数=' + String(ll_cnt)) // 结果 = 3
复制代码本机实机验证(PB 12.5 PBVM,pypower test --build-exe):将上述解析逻辑写成 .pbtest 脚本运行,输出 diag = obj=nvo_lib_rt ext=sru count=3,状态 PASS。注意 headless 环境用 FileWrite 落盘而非 MessageBox;上面为便于阅读改用 MessageBox,二者逻辑完全一致。
这组解析是后面 Library* / OrcaScript 自动化的地基——你能正确解析出对象名和扩展名,才能把它们喂给导入导出函数。
四、Library* 函数:运行时导入导出的正确写法(语法参考,未实机)
PB 在运行时提供三个库操作函数,不需要开 IDE,也不需要 OrcaScript 引擎,纯 PowerScript 即可调用。下面给出标准写法。
未实机运行提示:以下 Library* 函数属于 PB 内置 ORCA 系列函数,在真实 PB 应用中可直接编译运行。但本机 pypower 隔离测试编译器不支持该系列(编译报 C0052: Bad argument list),因此本段【未实机运行,仅静态核对】,不作为核心示例;请在你自己的 PB IDE 中验证。 - // 示例输入:目标库(你要导入/导出的 PBL 完整路径)
- string ls_lib
- ls_lib = 'D:\tmp\pblit_test.pbl'
- // 示例输入:导入用的源码文本文件路径(.sru,GBK 编码)
- string ls_src
- ls_src = 'D:\tmp\nvo_lib_rt.sru'
- // 错误回传变量
- string ls_err
- ls_err = ''
- // 1) LibraryImport:把文本源码导入 PBL(创建/覆盖对象)
- int li_imp
- li_imp = LibraryImport(ls_lib, 'nvo_lib_rt', ExportUserObject!, ls_src, ls_err)
- if li_imp <> 1 then
- MessageBox('导入失败', ls_err)
- return
- end if
- // 2) LibraryDirectory:列出库内所有对象名(以 ~n 分隔的字符串)
- string ls_dir
- ls_dir = LibraryDirectory(ls_lib)
- boolean lb_ok
- lb_ok = (Pos(ls_dir, 'nvo_lib_rt') > 0)
- // 3) LibraryExport:把对象导回文本源码文件
- string ls_out
- ls_out = 'D:\tmp\nvo_lib_rt_exported.sru'
- int li_exp
- li_exp = LibraryExport(ls_lib, 'nvo_lib_rt', ExportUserObject!, ls_out)
- // 输出结果
- MessageBox('导入导出结果', 'import=' + String(li_imp) + ' dir_has=' + String(lb_ok) + ' export=' + String(li_exp))
复制代码
三个函数要点:
- LibraryImport(libraryname, objectname, objecttype, syntaxfromfile, errors):把 .sr* 文本导入库。objecttype 是 LibraryExportType 枚举,用户对象用 ExportUserObject!,窗口用 ExportWindow!,DataWindow 用 ExportDataWindow!,全局函数用 ExportFunction!。返回 1 成功、-1 失败,失败信息写在 errors 里。
- LibraryDirectory(libraryname):返回库内全部对象名,换行(~n)分隔。常用来「导入后验证对象确实进了库」。
- LibraryExport(libraryname, objectname, objecttype, syntaxfile):把对象导出成 .sr* 文本。返回 1 成功、-1 失败。
五、.sr* 源码格式速查
.sr* 是 PB 对象的纯文本表示,可用于 git 版本控制、跨版本迁移、批量处理。常见扩展名:
| 扩展名 | 对象类型 | | .sru | 用户对象(User Object) | | .srw | 窗口(Window) | | .srd | DataWindow 对象 | | .srf | 全局函数(Global Function) | | .srm | 菜单(Menu) | | .srj | 工程对象(Project) |
一个最小可导入的用户对象源码长这样(这就是第四节导入所用的文件内容):
- $PBExportHeader$nvo_lib_rt.sru
- forward
- global type nvo_lib_rt from nonvisualobject
- end type
- end forward
- global type nvo_lib_rt from nonvisualobject
- end type
- global nvo_lib_rt nvo_lib_rt
- forward prototypes
- public function integer of_sum (integer a, integer b);
- end prototypes
- public function integer of_sum (integer a, integer b); return a + b
- end function
- on nvo_lib_rt.create
- call super::create
- end on
- on nvo_lib_rt.destroy
- call super::destroy
- end on
复制代码
格式红线(任意一条错都可能导致导入失败或编译报错):
- 首行必须是 $PBExportHeader$<对象名>.<扩展名>,且对象名与导入时指定的 objectname 一致。注意前缀本身是 16 个字符(含收尾的 $),解析时别数错。
- GBK 编码 + CRLF 换行 + 无 BOM。很多「导入后乱码/编译 C 开头错误」的根因就是 UTF-8 或 LF 换行。
- forward / global type / forward prototypes / 事件与函数体 / on ...create / on ...destroy 结构必须完整。
窗口 .srw 特有坑:事件脚本按位置归属——窗口自身的事件脚本必须放在各控件 type 块之前,且每个控件块后要紧跟它自己的脚本块。归属错位时编译器会把报错路径写成 窗口名.事件.行,实际是归属错了,不是那一行真有错。
六、OrcaScript:在 IDE 之外做工程级自动化
Library* 函数适合「运行时、单个对象」的操作;要做整个工程的批量导入、导出、编译,应该用 OrcaScript——由 orcascr.exe 执行的脚本语言(不是 PowerScript,运行在 IDE 之外,可放进 CI)。
- // OrcaScript 脚本(保存为 build.orc,用 orcascr.exe build.orc 执行)
- start session "pblit_test.pbt"
- library "pblit_test.pbl" import "D:\tmp\nvo_lib_rt.sru" "" nvo_lib_rt
- library "pblit_test.pbl" export "nvo_lib_rt" "" "D:\tmp\nvo_lib_rt_exported.sru"
- build library "pblit_test.pbl"
- end session
复制代码说明:本段为 OrcaScript(由 orcascr.exe 执行,非 PowerScript),未在本机 PBVM 实机运行,仅静态核对;不同 PB 版本的 OrcaScript 子命令细节以你本地帮助文档为准。它解决的是「不开 IDE 也能批量导入导出 + 全量编译」的需求,正是论坛每日自动发帖背后的 PBL 构建链路所用思路。
OrcaScript 与 Library* 的分工:
| 维度 | Library* 函数 | OrcaScript | | 运行位置 | PB 应用运行时 | IDE 之外(orcascr.exe) | | 粒度 | 单个对象 | 整个库 / 工程 | | 典型用途 | 程序内动态加载/导出 | 构建、迁移、CI 批处理 | | 是否需 PBVM | 是 | 否(需 OrcaScript 引擎) |
七、边界情况与常见坑
- 导入后不是立即能用:LibraryImport 只是把对象放进库,部分情况下需要「Regenerate / 全量重建」才能让新对象被其它对象正确引用。CI 里常见做法是「先逐个 import,最后对整个工程做一次 full rebuild」。
- 字符集陷阱:源码文本必须是 GBK。用 UTF-8 保存的 .sru 在 PB 中文环境导入会乱码或报编码相关错误。
- 祖先依赖:导入一个继承自自定义祖先的对象(如 uo_json 继承自某 NVO),祖先必须先在库里存在,否则导入的子类会处于「断根」状态,编译报 C 系列错误。
- 源码与 p-code 不一致:.pbl 里同时存源码和 p-code;用外部工具改了 p-code 却没改源码,或反之,都会导致「看到的源码和跑起来的行为不一致」。以源码 + 重新编译为准。
- 双会话 ORCA 构建:在自动化环境(本机 PBIDEA 构建链路即如此)里,稳妥做法是「会话 1 负责导入所有 .sr*,会话 2 用自有应用对库做全量重建」——两个会话隔离,避免导入态污染编译态。
- 源码头前缀长度:$PBExportHeader$ 是 16 个字符(不是 15),解析时用 Mid(header, 17);用 Mid(header, 16) 会多带结尾的 $,对象名变成 $nvo_lib_rt,后续导入 / 比对全部错位。本机实机验证时明确踩到过。
- LibraryExport 失败返回 -1:常见原因是对象名或类型写错、库路径不对、或该对象已被破坏(p-code 不完整)。先用 LibraryDirectory 确认对象确实在库里。
八、与相近方案对比
- 直接共享 .pbl:最原始,但二进制合并冲突几乎无法解决,且体积大。不适合版本控制。
- **.sr* + 版本控制(git)**:把对象导出成文本再提交,diff/merge 友好,是团队协作的推荐基础。配合 LibraryImport 或 OrcaScript 再导入回库。
- **Library* 函数:适合应用运行时**需要动态加载/热更新对象的场景(如插件式架构)。
- OrcaScript / PBC 命令行:适合构建期自动化——CI、夜间构建、批量迁移。
- 第三方 PBL 反编译/导出工具:用于没有源码只有 .pbl 的抢救场景,但生产环境应保留 .sr* 源码。
九、扩展点
- **把 .sr* 纳入 git**:写个小脚本在提交前 LibraryExport 全库、在拉取后 LibraryImport 回库,实现「库即源码、文本可审」。
- CI 自动构建:用 OrcaScript 的 build library / build exe 在每次推送后自动编译,失败即报警。
- 增量 vs 全量:日常用 LibraryImport 改单个对象即可;发布前务必跑一次全量 rebuild(会话 2),确保没有一个对象处于「有条目没 p-code」的状态——这正是本机构建红线里「四绿」检查要拦截的问题。
掌握 PBT/PBL/PBD 与 .sr* 的关系,再配合「先解析、再导入导出」的打法,以及 Library* 函数与 OrcaScript 两种自动化方案,你就能把 PB 工程从「靠 IDE 手工点」升级到「文本可管、脚本可建」的工程化状态。 |