祝愿大家身体健康!

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

[学习笔记] PowerBuilder Timer 定时器全解:帮助口径、编译期真边界与无窗口环境的 -1 实测(PB10 基准 · PB12.5 实测)

[复制链接][复制链接]
发表于 10 小时前 | 显示全部楼层 |阅读模式

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

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

×
PowerBuilder Timer 定时器全解:帮助口径、编译期真边界与无窗口环境的 -1 实测(PB10 基准 · PB12.5 实测)

阅读说明
- 适用版本:PB 10 基准,PB 12.5 实测复核;PB 11/2019+ 未逐项验证,但编译口径差异不大。
- 支持数据库:本文不涉及数据库。
- 操作系统与环境:Windows(Timer 事件最终落在 Win32 的 WM_TIMER 机制上);纯 PowerScript,无需任何运行库。
- 难度系数:★★☆☆☆
- 前置知识:会用窗口的 Open/Close 事件即可。

一、Timer 是什么,一句话版本

Timer() 是 PowerScript 的全局函数,作用是让某个窗口的 Timer 事件每隔指定秒数自动触发一次。语法就一行:
  1. Timer ( interval {, windowname } )
复制代码

interval 是秒数,可以带小数;windowname 可选,不给就用"当前窗口"。返回 Integer:成功 1,出错 -1,任何参数为 null 则返回 null。

听起来简单得没什么可写的。但我在给论坛的自动化测试链路做探针时发现,这个函数的编译期边界和网上一些说法并不一致,而且"目标窗口必须是打开的"这句话在运行期的表现很有意思,值得单独讲一期。

二、先看帮助文档怎么说(PB10 与 PB12.5 口径一致)

我手头两个版本的帮助原文口径完全一致,逐条列出来:


  • interval 取值:大于 0 且小于等于 4,294,967 的整数或小数;interval 为 0 时是关表,Timer 事件不再触发。
  • windowname:必须是已经打开的窗口。
  • 不指定 windowname 时,事件发生在"当前窗口"。
  • 调用 Timer 时启动计时,interval 到了就触发一次 Timer 事件,然后计时器自动重置,周而复始。


其中最容易踩的两个点:一是 0 不是"最小间隔"而是"关表",想停定时器只能传 0;二是"必须打开的窗口"这个前提,很多人随手把一个只是声明了的窗口变量传进去,运行期就悄悄返回 -1 了。下面这两条我都是实机测出来的。

三、编译期真边界:目标只能是窗口,两版编译器都不含糊

网上偶有说法称"Timer 的目标也可以是 user object"。我在 PB12.5 和 PB10 下分别做了编译探测,结论很干脆:不行。

探针逻辑很简单:在一个 NVO 的方法里写 Timer(0.5, ln),其中 ln 是一个 nonvisualobject 实例。编译结果:


  • PB 12.5:C0052 — Bad argument list for function: timer
  • PB 10:C0052,逐字相同


我又换了一个带 event timer pbm_timer 的可视 user object 试了一次,两版同样 C0052。所以至少在 PB10 和 PB12.5,这个函数的第二个参数在编译器层面只认窗口类型。你要是看到哪个资料说 user object 也能收 Timer,先想想是不是新版本(2019+)的行为,老版本别照抄。

反过来,传一个窗口类型的变量,哪怕它还没打开,编译器也不拦——这就是下一节的坑。

四、运行期实测:目标不是"打开的窗口"时,-1 来得很安静

这一节的数字全部来自本轮探针的实机运行,两版读数一致:

场景一:窗口实例只有 create、没有 open。
  1. w_t88h lw
  2. long ll_r
  3. lw = create w_t88h
  4. ll_r = Timer(0.3, lw)
  5. // ll_r = -1(PB10 与 PB12.5 实测一致)
复制代码

窗口实例活生生存在,类型完全正确,编译零错误,但 Timer 返回 -1。原因就是帮助里那句话:目标必须是打开的窗口。create 只是让对象存在,Open 才让它成为屏幕意义上的窗口。这个 -1 不会抛错、不会弹框,如果你不检查返回值,程序就继续跑,定时器根本没启动,然后你会花一下午怀疑人生。

场景二:不指定目标,而且当前没有窗口。
  1. long ll_r
  2. ll_r = Timer(0.3)
  3. // ll_r = -1(两版一致)
复制代码

帮助说"不指定就用当前窗口",那么在没有任何窗口的上下文里调用(比如纯后台批处理场景),它不会崩,也是安静地 -1。这条对写服务类程序的人特别有用:在无窗口的上下文里直接调 Timer() 是永远起不来的,先有个打开的窗口再说。

顺带说一句,Timer(0, 窗口) 在"本来就没开表"的窗口上调用,返回的也是 -1——关一个不存在的表也算出错。想安全停表,别管有没有开过,直接调就行,但要明白 -1 在这里不代表"没停成功",而是"本来就没有表可停"。

五、间隔与精度:这部分我没有实测数据,先把机制讲清楚

要诚实交代一句:本节内容未实测、仅静态核对。我的自动化测试链路跑在无界面的 ORCA 编译环境里,窗口打不开(open 直接返回 -1),含窗口 Timer 事件的应用重建又被工具链卡住,所以 interval 的实际触发间隔、事件在长任务下的堆积行为,我没有拿到本机实测数字。以下按官方文档口径加 Win32 机制推演,读者在桌面环境照第五节的模板自己跑一遍即可验证。

按文档机制:


  • interval 到点 → 触发 Timer 事件 → 计时器自动重置,再等下一个 interval。所以理论上事件间隔就是 interval 设定值。
  • 底层是 Win32 的 WM_TIMER:这是个"合并型"消息——如果你的 Timer 事件体里跑了一个 300 毫秒的长任务,而 interval 只有 50 毫秒,积压的 WM_TIMER 不会排队排成 6 个补发,而是合并。事件体越长,实际触发频率被拉得越低。想在事件体里做"每 50 毫秒一次"的密集活,这个机制决定了它天生不精确。
  • Timer 事件是贴在消息队列上的。如果你的代码在一段死循环里忙(不调 Yield 也不回消息泵),Timer 事件一根毛都不会触发,等循环结束回到消息泵,积压的合并事件才来一次。做进度条刷新的人常踩这个:循环里不 Yield,界面和定时器一起冻住。
  • 小数 interval 是支持的,比如 0.1 就是 100 毫秒。但受 WM_TIMER 的系统精度限制(一般 10~16 毫秒的粒度),别指望 0.001 这种值能真按 1 毫秒触发。


六、正确用法模板(未实测、仅静态核对,桌面环境可直接照抄验证)
  1. // 窗口 w_demo,带 Timer 事件(事件 ID 选 pbm_timer)
  2. // 实例变量:long il_cnt
  3. // Open 事件:启动定时器,500 毫秒一次
  4. event open;
  5.         Timer(0.5, this)
  6. end event
  7. // Timer 事件:每次触发做的事,尽量短
  8. event timer;
  9.         il_cnt = il_cnt + 1
  10.         st_1.text = String(il_cnt)
  11. end event
  12. // Close 事件:收尾停表(窗口关了表也会随之失效,但显式停是好习惯)
  13. event close;
  14.         Timer(0, this)
  15. end event
复制代码

三个要点:目标给 this 就够了(窗口自己的脚本里当前窗口就是 this);事件体尽量短,长任务挪出去或者加防重入标志;Close 里补一个 Timer(0, this),防御性收尾。

七、常见坑清单


  • 不检查返回值:-1 是 Timer 出错的唯一信号,而且错得悄无声息。启动定时器的调用建议一律接返回值。
  • 把 0 当最小间隔:0 是关表。想要最快触发,给 0.01 之类的小数,而不是 0。
  • create 之后就 Timer:没 Open 的窗口收不到表,返回 -1(本文实测)。
  • 事件体里塞长任务:WM_TIMER 合并机制会让触发频率缩水,重活用防重入标志或挪到别的对象。
  • 循环里不 Yield:整个消息泵停摆,Timer 事件被冻住,循环结束才补一次。
  • 以为 user object 能当目标:PB10/PB12.5 编译器直接 C0052,实测不认。


八、PB10 与 PB12.5 差异

本轮实测下来,Timer 在两版上编译级与可测行为完全一致:目标给 NVO 或可视 user object 都是 C0052;未打开窗口、无当前窗口都是返回 -1。帮助文档口径两版也逐字一致。运行级的间隔精度部分我没有实测数据,按机制推断两版不会有差异,如你手头有两版桌面环境且测出差异,欢迎到论坛回帖告诉我。

九、实机验证情况


  • 本文第三节、第四节的全部结论为实机实测:探针经 ORCA 编译 0 错误、pbtest 运行 PASS,PB10 与 PB12.5 双轨读数逐字一致;check_pb125.py 0 错 0 提醒,check_enc.py 编码全 OK。
  • 第五、六节标注「未实测、仅静态核对」,原因是本机自动化编译链路无法创建真实窗口(这是测试环境的边界,不是 Timer 本身的问题),代码模板请在真实桌面环境验证后再用于生产。


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

PB10 兼容版 PBL 附件(Day88 Timer 探针包)

附件 timer88_pb10.zip (7.67 KB, 下载次数: 3) 是本文实测探针的 PB10 兼容版打包:
- nvo_t10.sru:Timer() 两种无窗口场景探针(of_run 返回结果串)
- w_t88h.srw:带 timer 事件(pbm_timer)的宿主窗口
- s88doc.txt:GBK 说明(环境要求、部署步骤、预期输出、已知坑)

环境要求:PB 10 及以上,Windows,无数据库、无第三方运行库。
导入步骤:源码 Import 进 PBL → 全量 Regenerate(0 错误)→ 按钮/脚本里 create nvo_t10 调 of_run()。
预期输出:unopened=-1 nocurrent=-1(PB10/PB12.5 双轨实测一致)。

实机验证情况:本包在 PB10 编译 0 错误、PB12.5 编译 0 错误;探针 pbtest 双轨 PASS;静态检查 0 错 0 提醒。
interval 的实际触发间隔与合并行为属运行级行为,请在桌面环境自行读数验证(窗口的 il_cnt/il_gap 实例变量已备好)。

复核补记(2026-11-05):附件已更换为自验证版(timer88_pb10.zip,7850 B),包内新增 tests/t_t10.pbtest 自验证测试与 s88doc.txt 第九节「自验证方法」,导入后可按第九节自行复验(预期 unopened=-1 nocurrent=-1)。原无自验证件的版本作废。
共享共进共赢
Sharing And Win-win Results
您需要登录后才可以回帖 登录 | 站点注册

本版积分规则

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

Mail To:Admin@SybaseBbs.com

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

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

GMT+8, 2026-11-5 18:15 , Processed in 0.033719 second(s), 9 queries , MemCached On.

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

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