About eText Wizard

eTextWizard

目录：

一、关于eText Wizard（电子文档处理器）:

  1. 软件开发目的及软件用途
  2. 软件的主要功能
  3. 软件的安装及卸载
  4. 软件的注册
  5. 软件的下载地址

二、“国华软件工作室”的其他软件：

三、我的主页及联系方式：
一、关于eText Wizard: Top
1.软件开发目的及软件用途 Top
相信各位在上网一段时间后，硬盘里一定积攒了大量的电子文本，如：
技巧集锦、教程、技术文章、编程指南、小说、散文、传记等等，这些
精彩文件平时大量占用你的硬盘空间，一到用的时候，怎么也找不到；
读起来也很费力，一部《三国》你至少要打开一百二十个文件；而且
第二天也找不到昨天读到那里了，你说急人不急人！
   终于有一天想把他们作成帮助文件，但是，面对某个目录下的几千个
文本文件，才发现所谓的“所见即所得”的帮助文件制作软件竟然要
一个又一个地“手工重复劳作”........
  我曾经用Help&Manual手工作了一个包含两千多条Delphi技巧的帮助文件，
历时两三周终于完成，可是...手抽筋，眼发晕，食欲不振.......
  有时，真的希望有人把我想要的文本全处理好再放到网上，可是...
  面对网上成兆亿的文本难道只有手工处理吗？
  由此，萌发了我的软件开发方向：电子文档处理。
  “电子文档处理器”同时也是一个强大的HELP和CHM格式电子书批量制作工具。

“把繁重而又重复乏味的工作全部交给电脑去做吧！
    人的智慧在于告诉它应该怎么做！”
                                —xxx如是说。

2.软件的主要功能 Top

                功能
                 | 简介

                Txt->Help
                 | 文本文件转帮助文件，支持UNIX格式的TXT，自动添加标题、目录、索引。

                Txt->Html
                 | 文本文件转网页，自动处理URL&EMAIL

                Txt->Chm
                 | 文本文件转网页帮助文件，自动添加目录、索引、
                前页、后页等

                Html->Txt
                 | 网页文件转文本，转换完成比较干净彻底
                （可处理&nbsp;等），支持部分ASP文件。

                Html->Chm
                 | 网页文件转网页帮助文件，自动添加目录、索引。

                VBcode->Html
                 | VB源程序转为网页文件便于浏览。

                FileRenamer
                 | 批量文件改名，可预览结果，众多实用选项。

                Index Maker
                 | 对一个或多个目录下的所有或部分文件作索引，
                自动提取网页的<Title>作为标题，自动提取
                TXT第一行作为标题，多种输出格式。

                Dir Maker
                 | 批量目录建造。 *

                Title Editor
                 | 文本文件的标题编辑器及文件预览器。*

                Find&Replace
                 | 批量文件字符串查找替换器
                *

                Big5->GB

       注解：带*的项目尚未最终完成

3.软件的安装及卸载 Top

运行环境： MS win95;win98;win2000. win98以前的版本运行需要VB5.0
          的链接库文件MSVBVM50.DLL。

安装：

直接运行完全安装版即可（建议初次安装使用完全安装版）；
如果你以前安装过eTextWizard，直接下载新版本的主执行文件
eTextWizard.exe覆盖原来的即可。

卸载：

运行“控制面版”的“添加和删除程序”，找到“电子文档处理器”，
  选“卸载”，再依屏幕操作即可。

4.软件的注册 Top

本软件为“共享软件”，即“先试后买”的软件。

由于目前正处于测试阶段，故软件价格待定。

5.软件的下载地址:

本软件将会不定期更新版本，请留意！

http://www.ebook007.com/softdown.htm

二、“国华软件工作室”的其他软件： Top

TextVampire(网络即时贴) v1.0：Top
浏览文本或网页时，随时把你喜欢的文字很方便地存成.Txt文件。
你要做的只是选好文本，再按一下CTRL-C，这时，点击“网络即时贴”的
粘贴并生成文本文件”钮，TextVampire会自动截取所选文本的第一行（非空）
作为文件名保存起来，方便你将来收集整理、查找；同时也简化了你的击键次数。

http://www.ebook007.com/softdown.htm

三、我的主页及联系方式： Top

我的主要站点：

http://www.ebook007.com/
 (北极星书库)

http://go.163.com/~guohua99/(电子文档处理器)

镜像站点：

http://chmbook.363.net/

eMail:

fengguohua@hotmail.com

警告：Top

对重要文件请先作好备份，以免造成不必要的损失；
用户试用此软件的同时由用户个人承担试用此软件的一切风险，如用户不接受
此项要求请马上把本程序从你的硬盘删除； TXT转HELP结束后，程序会自动删除
已处理过的目录下的所有RTF文件，请在运行前
检查该目录下是否有重要的RTF文件，请作好备份。
请不要使用本软件制作非法电子出版物，否则一切后果由制作人自行承担，与本软件作者无关。
请不要修改本软件的版权信息（包括随软件发布的一切文档），否则作者将保留追究其法律责任的权利。
在不修改作者版权信息及保证软件完整性的前提下，任何个人或组织可随意发布或试用本软件的免费共享版，
但不得对本软件做任何逆向工程（指反汇编、反编译，公布注册码、散发正式版等）。
本文档中所指“程序”或“本程序”均指eTextWizard(中文名：电子文档处理器)。

注：MS,Windows Help(hcw.exe,hcrtf.exe),Html Help(hhw.exe,hhc.exe)的版权均归美国微软公司所有。

All Rights Reserved, Fengguohua of North Star, 2000-05-03

Top


北极星书库-->PowerBuilder系列讲座(张健姿)

北极星书库

PowerBuilder系列讲座
(177KB">

华天新技术开发公司 张健姿
北京友谊宾馆30538 室 100873
Email: sjhzyz@public.bta.net.cn

1. 数据库的事务管理 | 2. 调用Windows的动态链接库
3. PowerBuilder面向对象的程序设计 | 4. 并发控制
5. DataWindow的打印输出 | 6. DataWindow的打印输出（续）
7. 利用SetActionCode函数控制DataWindow | 8. 分布式PowerBuilder设计
9. DataWindow的数据缓冲区 | 10. 用DataWindow实现对多表的修改
11. 游标的使用 | 12. 预装入对象
13. 资源管理 | 14. PowerBuilder的事件(续)
15. PowerBuilder的事件(续) | 16. 生成可重用的数据窗口
17. 在数据窗口的祖先对象中编写行选择函数 | 18. 对数据库查询的优化
19. 使用PowerBuilder另外的500个函数(上) | 20.21. 使用PowerBuilder另外的500个函数（中、下）
22. 使用树型视图控件对数据库查询的优化 | 23. 函数与事件
24. Install Builder的使用方法 | 25.26. 一个代码维护的应用程序（上、下）
27. 在PowerScript脚本中访问数据窗口中的数据 | 28. PowerBuilder系列讲座
29. PowerBuilder系列讲座(29) | 30. 在数据窗口中使用滚动条
31. 在数据窗口中使用滚动条（续） | 32. 为下拉式数据窗口建立缓冲区
33. 为下拉式数据窗口建立缓冲区 | 34. 分布式事务处理
35. 分布式应用开发 | 36. FUNCky 的使用
37. PFC 介绍 | 38. PowerBuilder 6.0 简介
40. 图形用户界面设计（续） |
北极星书库


Get Update News

Update Link:
http://www.ebook007.com/softdown.htm

“电子文档处理器”会经常更新，请登陆“北极星书库”以得到最新版本。如有任何意见和建议也请给我发Email。

我的Email地址： fengguohua@hotmail.com。


Goto eTextWizard's Homepage

My Homepage:
http://www.ebook007.com/

“北极星书库”有海量的已经经过我的“电子文档处理器”处理过的Windows帮助文件或Windows HtmlHelp格式的小说、传记等作为电子书范本供大家参考。欢迎大家试用“电子文档处理器”--他不仅是一个强大的批量文本转换工具、文档制作工具，同时也是一款少见的具有强大功能的电子书批量制作工具，“北极星书库”的所有HELP和CHM格式电子书全部用“电子文档处理器”批量制作完成！！


张健姿-->PowerBuilder系列讲座-->1. 数据库的事务管理

 后页
前页
目录
北极星书库
1. 数据库的事务管理

---- 在 数 据 库 中, 所 谓 事 务 是 指 一 组 逻 辑 操 作 单 元, 使 数 据 从 一 种 状 态 变 换 到 另 一 种 状 态。 为 确 保 数 据 库 中 数 据 的 一 致 性, 数 据 的 操 纵 应 当 是 离 散 的 成 组 的 逻 辑 单 元: 当 它 全 部 完 成 时, 数 据 的 一 致 性 可 以 保 持, 而 当 这 个 单 元 中 的 一 部 分 操 作 失 败, 整 个 事 务 应 全 部 视 为 错 误, 所 有 从 起 始 点 以 后 的 操 作 应 全 部 回 退 到 开 始 状 态。

---- 对 事 务 的 操 作 是 这 样 进 行 的: 先 定 义 开 始 一 个 事 务, 然 后 对 数 据 作 修 改 操 作, 这 时 如 果 提 交(COMMIT), 这 些 修 改 就 永 久 地 保 存 下 来, 如 果 回 退(ROLLBACK), 数 据 库 管 理 系 统 将 放 弃 您 所 作 的 所 有 修 改 而 回 到 开 始 事 务 时 的 状 态。 此 外 有 些 数 据 库 支 持 事 务 的" 存 储 点(savepoint) 这 一 概 念: 即 在 一 个 事 务 进 程 中 任 意 一 点 您 都 可 以 进 行 当 前 状 态 的 存 储, 回 退 时 只 是 回 到 你 所 设 定 的 存 储 点, 而 不 必 退 回 全 部 的 事 务。 如 果 您 的 事 务 可 以 分 成 几 组 对 数 据 库 的 修 改, 那 就 可 以 设 置 多 个 存 储 点, 根 据 需 要 您 可 以 回 退 到 任 意 一 个 存 储 点, 而 不 使 所 有 事 务 的 修 改 数 据 全 部 丢 失。

---- 正 确 地 管 理 事 务 可 以 保 证 数 据 的 完 整 性, 当 您 所 做 的 工 作 全 部 完 成 和 得 到 确 认 之 前, 没 有 任 何 数 据 物 理 地 写 进 数 据 库。 让 我 们 来 看 这 样 一 个 实 例, 我 们 有 这 样 一 个 银 行 应 用 系 统, 前 台 使 用 者 作 出 将 储 户 甲 的 一 百 元 存 款 划 归 储 户 乙 帐 下 的 操 作; 在 后 台 的 数 据 库 中, 这 两 个 客 户 的 记 录 分 储 在 两 张 表 中, 当 使 用 者 在 屏 幕 上 作 出 如 上 操 作 时, 在 后 台 需 要 对 两 张 表 进 行 修 改。 如 果 在 数 据 库 中 对 甲 用 户 存 款 余 款 作 减 去 一 百 元 修 改 后, 对 乙 用 户 加 一 百 元 的 操 作 修 改 却 失 败 时, 前 一 张 表 也 必 须 回 到 修 改 前 的 状 态, 否 则 数 据 库 的 内 容 不 统 一, 甲 储 户 白 白 损 失 一 百 元, 信 息 必 然 是 不 正 确 的。 因 此 进 行 事 务 管 理 是 必 须 的。

---- 传 统 地, 我 们 认 为 一 个 事 务 包 括 了 对 一 个 或 多 个 表 的 修 改, 而 随 着 分 布 式 数 据 库 和 数 据 仓 库 的 发 展, 事 务 可 能 包 括 了 对 一 个 或 多 个 数 据 库 的 修 改。 在 上 例 中 甲 乙 两 用 户 就 可 能 是 异 地 用 户, 信 息 分 储 在 不 同 地 域 的 不 同 数 据 库 中, 上 述 的 一 个 事 务 就 涉 及 到 了 对 不 同 数 据 库 的 操 作 。

PowerBuilder 中 的 事 务 管 理

---- 作 为 数 据 库 的 前 台 开 发 工 具Power-Builder 支 持 事 务 管 理 的 操 作。 在Power-Builder 中 有 一 种 称 作 事 务(transaction) 的 对 象, 这 个 对 象 是PowerBuilder 应 用 与 数 据 库 的 通 讯 区 域。P owerBuilder 在 应 用 开 始 时 建 立 一 个 全 局 的 事 务 对 象SQLCA。 由 于 大 多 数 的 应 用 只 用 到 一 个 数 据 库, 所 以 一 般 开 发 者 主 要 也 只 用SQLCA 作 为 与 唯 一 数 据 库 连 接 的 事 务 对 象。

----PowerScript 中 常 用 的 事 务 管 理 的 语 句 有 四 个:COMMIT,ROLLBACK,CONNECT,DISCONNECT。

---- 当 您 需 要 应 用 与 数 据 库 建 立 连 接 时 使 用CONNECT 这 一 操 作 命 令, 取 消 连 接 时 执 行DISCONN ECT, 这 两 个 命 令 一 般 分 别 用 在 应 用 的 开 始 和 结 束, 也 就 是Appli-cation 的Open 和Close 事 件 中 。

---- 当 一 个 事 务 的 数 据 库 修 改 都 成 功 地 完 成 后, 修 改 须 提 交 给 数 据 库,COM-MIT 语 句 是 一 个 旧 事 务 结 束 和 一 个 新 事 务 开 始 的 界 线。 在 修 改 被 提 交 前, 数 据 库 的 数 据 并 没 有 被 真 正 地 修 改, 这 些 修 改 被 保 留 在 某 个 工 作 区, 只 有 作 修 改 的 用 户 才 能 看 到 这 些 被 修 改 后 的 值, 提 交 之 后, 则 所 有 的 用 户 就 都 可 以 看 到 新 值 了。

---- 在 事 务 的 进 程 中 发 生 某 些 错 误, 或 者 在 操 作 中 出 于 种 种 原 因 打 算 中 止 事 务, 须 用ROLLBAC K 命 令 回 退 事 务, 如 果 已 作 的 操 作 不 用ROLLBACK 命 令 取 消, 这 些 操 作 必 将 错 误 地 作 为 下 一 个 事 务 的 一 部 分 而 导 致 数 据 库 的 混 乱。

---- 如 果 您 使 用 的 是 多 窗 口 的 应 用, 却 只 用 一 个 事 务 对 象, 就 应 格 外 注 意ROLL-BACK 和COMMIT 会 影 响 事 务 的 逻 辑 一 致 性。 在 某 个 窗 口 执 行 的 这 两 个 指 令 会 使 其 他 窗 口 应 用 中 所 进 行 到 一 半 的 工 作 提 交 或 回 退。

---- 在 多 用 户 系 统 中, 修 改 和 提 交 的 时 间 越 接 近, 提 交 成 功 的 可 能 性 就 越 高。 因 为 一 个 事 务 中 所 有 的SQL 语 句 全 部 执 行 成 功 而 提 交 却 失 败 是 完 全 可 能 发 生 的, 例 如 在 您 的 事 务 过 程 中, 另 一 个 用 户 修 改 了 数 据 并 提 交, 这 很 可 能 使 您 作 出 的 修 改 无 效, 这 时COMMIT 将 失 败, 您 必 须 回 退 这 一 事 务 的 全 部。

事 务 对 象 的AutoCommit 属 性

---- 事 务 对 象 有 一 个AutoCom-mit 的 属 性 可 以 使 开 发 者 简 化 对 事 务 管 理 的 操 作, 这 一 布 尔 型 的 属 性 可 以 用TRUE 或FALSE 来 对 其 赋 值。 当 其 为 真 时,PowerBuilder 不 通 过 其 他 额 外 的 交 互 就 将 您 的SQL 语 句 传 输 给 后 台 数 据 库, 而 且 执 行 完 毕 自 动 提 交。

---- 当 然, 您 可 以 设 置AutoCommit 属 性 为 假( 缺 省 值), 使 用COMMIT 或ROLLBACK 这 样 的 关 键 词 提 交 或 回 退 事 务。 在 大 多 数 应 用 中, 一 部 分 的 数 据 库 操 作 是 要 成 组 提 交 的, 而 另 一 些 则 不 用。 因 此 我 们 可 以 利 用AutoCommit 的 特 性 来 确 定 事 务 的 起 点, 当 我 们 把AutoCommit 的 属 性 设 为Fals e 时, 系 统 设 定 此 时 为 事 务 的 起 点。 当AutoCommit 设 为 真 时, 系 统 自 动 消 取 这 一 事 务。 因 此 你 可 以 先 把AutoCommit 设 为 真, 当 您 需 要 开 始 一 个 事 务 时, 将 其 置 为false, 此 刻 即 为 事 务 起 始 点 。

----PowerBuilder 内 部 这 种 事 务 管 理 的 最 大 优 点 是 方 便。 您 不 去 考 虑 整 个 事 务, 而 只 需 把 您 所 作 的 修 改 提 交 或 滚 回 即 可。 但 是 方 便 与 可 控 性 总 是 矛 盾 的, 在Power-Builder 中 没 有 存 储 点 和 嵌 套 事 务 管 理 的 机 制, 即 使 您 所 使 用 的 数 据 库 支 持 这 些 特 性, 在PowerBuilder 中 却 无 法 得 以 体 现。 不 过 在 普 通 的 应 用 中, 存 储 点 和 嵌 套 事 务 管 理 并 不 是 必 须 的, 一 般 的 事 务 管 理 足 以 够 用 。

用 数 据 库 的 事 务 管 理 指 令 实 现 完 全 控 制

---- 上 述 的 事 务 管 理 方 式 尽 管 简 单 方 便, 但 是 在 某 些 应 用 中, 我 们 也 的 确 需 要 利 用 所 用 的 数 据 库 系 统 的 嵌 套 事 务 和 存 储 点 的 特 性, 而PowerBuilder 内 部 的 事 务 管 理 没 有 提 供 这 样 的 功 能, 您 必 须 自 己 设 计。

---- 自 己 进 行 事 务 管 理 的 方 式 是 直 接 使 用 数 据 库 本 身 的 事 务 指 令。 当 您 使 用 自 己 的 管 理 方 式 时, 就 应 使Power-Builder 停 止 管 理 事 务, 即 设 置Auto-Commit 为TRUE, 系 统 内 部 就 不 会 自 动 建 构 事 务 处 理 的 命 令 了。 实 现 人 工 事 务 管 理 的 方 式 是 采 用EXECUTE IMMEDIATE 这 条PowerBuild er 指 令 来 执 行 任 意 的 数 据 库 操 作。 你 所 需 做 的 是 将 数 据 库 指 令 编 辑 成 一 个 字 符 串, 您 可 以 执 行 任 何 的 数 据 定 义 语 句 如 建 表、 建 主 键、 存 储 过 程 等, 例 如 您 可 以 用

----EXECUTE IMMEDIATE BEGIN TRANSACTION trans-name

---- 这 样 的 指 令 开 始 一 个 事 务。 采 用 这 种 方 法, 只 要 您 所 用 的 数 据 库 支 持 嵌 套 事 务 和 存 储 点 等 事 务 管 理, 我 们 通 过PowerBuilder 开 发 出 的 应 用 也 就 同 样 可 以 实 现。

---- 在PowerBuilder 中 提 供 的 事 务 管 理 的 方 法 是 多 种 多 样 的, 只 要 您 灵 活 运 用, 就 一 定 能 设 计 出 优 秀 的 数 据 库 应 用 来。

后页
前页
目录


张健姿-->PowerBuilder系列讲座-->2. 调用Windows的动态链接库

2. 调用Windows的动态链接库

---- 许 多 熟 练 使 用C 的 程 序 员 在 使 用PowerBuilder 时 都 希 望 自 己 以 前 在C 上 做 的 工 作 可 以 被 PowerBuilder 引 用, 这 是 完 全 可 以 的。 在PowerBuilder 中 你 可 以 通 过 外 部 引 用 函 数 的 形 式 来 调 用 动 态 连 接 库 中 的 函 数。

----

PowerBuilder 调 用DLL 程 序 使 用 规 则

----PowerBuilder 可 以 支 持 任 何 一 种 非PowerScript 编 写, 并 存 储 在 动 态 链 接 库 中 的 外 部 函 数 或 过 程 的 调 用。 但 外 部 函 数 的 参 数 必 须 是 符 合Pascal 规 则 的( 即 参 数 压 栈 顺 序 从 前 至 后)。

---- 在 函 数 调 用 前, 因 先 作 函 数 声 明,PowerBuilder 支 持 以 下 两 种 外 部 函 数 类 型:

---- · 全 局 函 数: 可 以 在 应 用 的 任 意 位 置 调 用;

---- · 局 部 外 部 函 数: 在window,menu,user object 或 用 户 自 定 义 函 数 等 对 象 中 定 义。

---- 外 部 函 数 声 明 的 语 法 是:

----{Access}FUNCTION ReturnDataType Function-Name({REF}{DataTypel Arg1,...,Data TypeN ArgN})LIBRARY LibName

---- 外 部 过 程 声 明 的 语 法 是:

----{Access}SUBROUTION Subroutine({REF}{DataType1 Arg1,...,DataTypeN ArgN})

---- 如 果 您 使 用 的 是 局 部 外 部 函 数 的 声 明, 您 还 可 以 指 定 对 象 的 访 问 权 限:Public,Private, Protected。 对 局 部 函 数 权 限 访 问 的 限 制 同 对 对 象 的 实 例 变 量 的 限 制 相 同。 您 可 以 指 定 对 象 名 加 函 数 名 的 方 式 调 用 外 部 函 数:

----object.function(arguments)

---- 如 在window 的w_emp 上 调 用 局 部 外 部 函 数Recog(), 就 可 这 样 使 用:

----w_emp.Recog()

----

如 何 在PowerBuilder 与DLL 之 间 传 递 参 数

---- 在PowerBuilder 的script 中 调 用DLL 中 的 函 数, 缺 省 情 况 下 是 通 过 传 值 法 来 传 递 参 数(pa ssed by value), 也 就 是 说PowerBuilder 将 对 要 传 递 的 参 数 做 一 份 拷 贝, 然 后 通 过 堆 栈 将 这 份 拷 贝 传 递 给 函 数。 如 果 你 希 望DLL 中 的 函 数 可 以 改 变 调 用 参 数 的 原 值, 就 可 以 通 过 参 考 传 值 法 (passed by reference) 来 传 递 参 数, 即 在 参 数 类 型 前 面 加REF 关 键 字 来 声 明 该 参 数 将 要 用 参 考 传 值 法。

---- 在 使 用DLL 时 有 一 些 基 本 规 则

---- 在MS Windows 中, 一 个DLL 在 被 装 入 内 存 后, 只 会 有 一 个 实 例, 不 会 因 为 多 个 程 序 使 用 同 一 个DLL 而 在 内 存 中 产 生 多 个DLL 拷 贝。 每 个DLL 只 有 一 个 最 大 为64K 的 数 据 段。 缺 省 情 况 下,Po werBuilder 都 是 使 用 传 值 法 来 传 递 参 数。 当 你 在 函 数 应 用 说 明 时 使 用 了REF 关 键 字,PowerBu ilder 将 传 递 一 个32 位 的 地 址 指 针( 段 地 址+ 偏 移 量) 给 被 调 用 的 函 数, 而 不 是 只 传 递 偏 移 量, 这 才 能 保 证DLL 中 的 函 数 能 得 到PowerBuilder 中 数 据 的 正 确 地 址。 在PowerBuilder 中 使 用 的 数 据 类 型 与C 语 言 支 持 的 数 据 类 型 不 尽 相 同,C 中 不 支 持 的 数 据 类 型 应 在 调 用 前 先 进 行 转 换。

---- 对 于 结 构, 要 在C 和PowerBuilder 中 做 相 等 的 说 明。

----PowerBuilder 不 支 持 函 数 指 针 的 传 递, 因 此 在PowerBuilder 中 不 能 使 用 回 调 函 数(callb ack funcion)。 如 果DLL 的 参 数 需 要 空 指 针(NULL), 你 可 以 向 函 数 传 递 一 个 值 为0 的 长 整 型。

----Windows 中 使 用 的 有 些 数 据 类 型C 中 并 不 支 持, 但 一 般 在C 的 预 编 译 器 中 用TYPEDEF 作 预 定 义, 同 时PowerBuilder 接 口 也 应 当 作 适 当 转 换。

----

使 用DLL 的 常 见 错 误 和 需 要 注 意 的 地 方

----1. 导 致 保 护 性 错(general protection fault)

---- 在Windows 中, 如 果 你 企 图 访 问 不 是 属 于 你 的 应 用 程 序 的 内 存 将 导 致 保 护 性 错。 导 致 保 护 性 错 的 原 因 可 能 有 以 下 几 点:

----a. 向DLL 中 的 函 数 传 递 了 不 正 确 的 参 数。 这 种 错 误 是 比 较 难 调 试 的, 因 为PowerBuilder 的 调 试 器 不 能 跟 踪 到C 程 序 中。 你 可 以 通 过 在C 中 使 用MessageBox 函 数 显 示 调 用 参 数 的 方 法 来 检 查 参 数 传 递 的 正 确 性。 更 全 面 的 方 法 是 使 用Windows 的 调 试 版 本( 带 有 调 试 信 息 的Windows 环 境) 和 功 能 更 强 的 调 试 器(Soft-ice for windows 或CodeView 等);

----b.C 中 对 数 组 的 访 问 超 出 了PowerBuilder 中 申 请 的 边 界。 在C 中 是 不 作 数 组 边 界 检 查 的, 这 可 能 是 导 致 保 护 性 错 的 最 常 见 的 原 因;

----c. 使 用 了 已 经 释 放 的 内 存 指 针。 你 最 好 把 已 经 释 放 的 内 存 指 针 置 为NULL, 以 便 在 使 用 前 进 行 判 断。

----2. 使 用 远 指 针

---- 在C 中, 所 有 的 静 态 变 量 和 全 局 变 量 都 是 在 程 序 的 数 据 堆 中 分 配 的, 其 他 变 量 都 是 在 栈 中 分 配 的。DLL 可 以 有 自 己 的 数 据 段, 但 是 它 没 有 堆 栈 段, 使 用 的 是 调 用 程 序 的 堆 栈。 这 就 意 味 着 寄 存 器DS 指 向 的 是DLL 数 据 段,SS 指 向PowerBuilder 应 用 程 序 的 堆 栈。 而 一 般 的Windows 应 用 程 序 中,DS 和SS 是 相 同 的, 你 可 以 使 用 近 指 针, 但 在 调 用DLL 中 引 用 远 堆 的 变 量 必 须 使 用32 位 的 远 指 针。 如 果 使 用 任 何 与 内 存 寻 址 有 关 的C 函 数, 都 要 使 用C 中 的far 版 本。 例 如 字 符 串 拷 贝 函 数, 应 该 用_fstrcpy 而 不 要 用strcpy。

----3. 注 意 静 态 变 量 的 使 用

---- 无 论 有 多 少 实 例 调 用 同 一 个DLL, 在 内 存 中 只 有 一 份DLL 代 码。 由 于Windows 是 多 任 务 的 环 境, 因 此DLL 中 的 静 态 变 量 可 能 由 于 其 他 实 例 对 此DLL 的 调 用 而 改 变。

----4. 不 要 试 图 共 享 文 件 句 柄

---- 在Windows 环 境 下, 不 可 能 在 应 用 程 序 和DLL 间 共 享 文 件 句 柄。 每 个 应 用 有 各 自 的 文 件 句 柄 表, 如 果 两 个 应 用 通 过 一 个DLL 来 访 问 同 一 个 文 件, 它 们 必 须 分 别 打 开 这 个 文 件。

----5. 及 时 释 放 使 用 过 的 资 源

---- 如 果 你 的DLL 中 使 用 了GDI 对 象, 一 定 要 及 时 释 放 它 们, 否 则 会 使Windows 因 申 请GDI 资 源 失 败 而 死 机; 例 如 你 建 立 了 一 个 逻 辑 字 体 或 逻 辑 笔, 在 使 用 完 后, 要 用DeleteObject 来 删 除 它。

----6. 为 使PowerBuilder 应 用 在Windows 环 境 下 正 常 运 行,DLL 应 放 在 下 列 目 录 之 中:

  • 当 前 目 录
  • 目 录
  • Windows System 目 录
  • 在DOS 的 路 径 中 包 括 的 目 录


张健姿-->PowerBuilder系列讲座-->3. PowerBuilder面向对象的程序设计

3. PowerBuilder面向对象的程序设计

---- 在PowerBuilder 的 应 用 开 发 中, 运 用 面 向 对 象 技 术 不 是 必 须 的, 但 是 对 于 一 个 大 型 复 杂 应 用 系 统 的 开 发, 如 果 采 用 了 面 向 对 象 技 术, 则 开 发 效 率 会 大 大 提 高。

---- 用PowerBuilder 开 发 的 好 处 是, 即 使 您 不 懂 得 什 么 是 面 向 对 象 技 术, 您 也 可 能 会 下 意 识 地 用 到 面 向 对 象 技 术 的 一 些 特 性, 但 是 如 果 您 对 这 一 技 术 有 了 充 分 的 了 解, 那 对 您 所 设 计 程 序 的 可 重 用 性, 可 维 护 性 和 其 他 各 方 面 的 质 量 都 会 有 大 幅 度 提 高。

----

PowerBuilder 的 对 象

----PowerBuilder 对 象 有 三 个 部 分 元 素: 属 性、 函 数 和 事 件。

----(1) 属 性: 也 就 是 数 据。 包 括 系 统 属 性 和 用 户 定 义 属 性 两 类, 描 述 该 对 象 的 各 种 特 性。 如 在 窗 口 对 象 中 的 系 统 属 性 包 括 标 题、 高 度、 宽 度 等, 用 户 定 义 的 属 性 可 以 是 实 例 变 量 或 共 享 变 量, 对 这 种 属 性 的 访 问 同 系 统 属 性 相 同, 只 是 在 封 装 性 上 有 所 不 同。

----(2) 事 件:PowerBuilder 中 的 事 件 与Windows 的 事 件 存 在 着 映 射 关 系。 当 用 户 的 操 作 或 系 统 本 身 产 生 了Windows 的 标 准 事 件, 就 转 化 成PowerBuilder 中 的 事 件。 例 如 命 令 按 钮 有 一 个 叫 Clicked 事 件, 当 用 户 按 按 钮 时 触 发, 操 作 系 统 首 先 检 测 到 鼠 标 被 点 击, 并 把Clicked 这 一 消 息 转 给 该 按 钮,PowerBuilder 运 行 时 的 工 作 引 擎 翻 译 了 这 一 事 件, 并 执 行 适 当 的PowerScript 语 句。PowerBuilder 中 大 部 分 的 事 件 都 是 可 以 映 射 成 由 用 户 激 发 的Windows 事 件。 另 一 类 事 件 是 用 户 定 义 事 件, 这 种 事 件 是 用 户 声 明 并 可 以 在 任 何 时 刻 由 用 户 触 发 的,Power-Builder 中 保 留 了75 个 用 户 事 件 可 由 程 序 员 使 用。

----(3) 函 数: 用 户 通 过 函 数 来 使 对 象 完 成 某 些 操 作。Pow-erSoft 公 司 建 议 用 户 通 过 调 用 对 象 事 件 和 函 数 来 修 改 对 象 而 不 要 直 接 修 改 对 象 属 性, 以 满 足 对 象 封 装 的 要 求, 例 如 采 用Window. hide() 而 不 要 用Window.visi-ble=false 来 使 窗 口 不 可 见。

----

PowerBuilder 面 向 对 象 的 特 性

---- 提 到 一 门 语 言 的 面 向 对 象 特 性, 它 应 当 具 有 继 承 性、 多 态 性 和 封 装 性, 如 缺 少 其 中 的 一 个 则 只 能 称 其 为 基 于 对 象 的 系 统, 而 不 是 面 向 对 象 的 系 统, 而PowerBuilder 则 很 好 地 具 备 了 全 部 三 个 特 性。

---- · 继 承:

----PowerBuilder 中, 窗 口、 菜 单 和 用 户 对 象 是 可 以 继 承 的, 而 其 他 对 象 则 都 不 能。 当 您 继 承 了 一 个 对 象, 那 您 得 到 的 子 类 将 具 有 父 类 的 属 性、 实 例 变 量、 共 享 变 量、 控 件、 用 户 自 定 义 事 件、 对 象 级 函 数、 事 件 和 代 码(script)。 也 就 是 说 当 您 继 承 了 一 个 类, 您 几 乎 得 到 了 这 个 类 的 全 部, 不 过 有 一 点 值 得 注 意, 您 不 能 在 子 类 中 删 除 任 何 一 个 继 承 到 的 特 性。

---- 在 继 承 了 祖 先 类 后, 您 可 以 在 子 类 中 扩 展 或 覆 盖 祖 先 的 元 素。 例 如 代 码, 在PowerBuilde r 中 缺 省 的 是 采 用 扩 展 方 式, 先 执 行 继 承 到 的 祖 先 代 码, 后 执 行 子 类 的 扩 展 代 码。 您 也 可 以 选 择 菜 单Compiler|Override Ancestor Script 选 项, 只 执 行 子 类 的 代 码, 并 可 在 代 码 中 的 任 意 位 置 调 动 祖 先 代 码 如CALL w_l ∷open。

---- 重 载 函 数 是 面 向 对 象 程 序 设 计 的 重 要 特 性, 即 同 一 函 数 名 却 有 着 不 同 的 参 数 和 返 回 值。 在 运 行 时, 系 统 自 动 寻 找 执 行 参 数 相 匹 配 的 那 个 函 数, 例 如 我 们 常 用 的MessageBox() 这 个 函 数 就 有12 个 不 同 参 数 的 重 载 函 数。 遗 憾 的 是, 对Power-Builder 我 们 不 能 在 同 一 个 继 承 对 象 中 定 义2 个 不 同 参 数 的 重 载 函 数, 只 能 在 后 继 的 类 中 定 义 重 载 函 数, 这 使 我 们 对 重 载 函 数 的 定 义 带 来 了 很 大 的 不 便, 这 不 能 不 说 是PowerBuilder 的 缺 陷。( 不 过 在 即 将 发 布 的PowerBuilder5.0 中, 正 是 支 持 了 函 数 重 载 这 一 特 性。)

---- 对 于 对 象 的 继 承,PowerBuilder 中 所 有 对 象 都 有 其 共 同 的 基 类PowerObject, 从 这 一 个 基 类 下 面 分 成 继 承 类Graph-icObject 和NonVisualObject 等。 它 们 的 继 承 关 系 和 扩 展 函 数 及 属 性 如 下 表:

---- · 多 态 性:

---- 在PowerBuilder 中 有 大 量 的 多 态 函 数 如print()、TriggerEvent() 等, 在 运 行 过 程 中, 您 只 需 要 指 出 对 象 和 函 数 名 即 可。 在 有 些 函 数 中, 即 使 不 知 道 对 象 类, 也 可 以 用Class Name() 函 数 得 到 对 象 类, 或 得 到 实 例 名, 将 对 象 名 作 为 函 数 参 数 调 用 该 函 数。

---- · 封 装 性:

---- 封 装 的 目 的 是 为 了 实 现 数 据 隐 藏 和 数 据 保 护, 封 装 的 目 标 是 为 对 象 提 供 一 个 对 外 操 作 的 接 口, 使 其 他 对 象 通 过 函 数 来 访 问, 而 不 允 许 直 接 操 纵 对 象 的 属 性。 在PowerBuilder 中 有 三 种 访 问 类 型Public、Protect、Private, 这 三 种 访 问 控 制 类 型 可 以 用 在 对 象 的 变 量 和 函 数 上, 缺 省 的 实 例 变 量 和 对 象 函 数 都 是public 类 型 的。 为 了 保 护 数 据, 应 尽 可 能 多 地 使 用private 和p rotect 类 型, 前 者 只 允 许 对 象 内 部 的 元 素 来 访 问, 后 者 可 以 接 受 对 象 内 部 和 继 承 类 的 元 素 访 问 。

----

PowerBuilder 面 向 对 象 程 序 设 计 的 过 程

---- 事 实 上 用 户 在 运 用Power-Builder 进 行 程 序 设 计 的 一 开 始, 就 已 经 开 始 进 行 对 象 类 的 设 计 : 例 如 在 建 立 窗 口 时 可 以 用 窗 口 画 笔(painter) 建 立 一 个 新 窗 口, 命 名 为w_emp。 建 立 的 窗 口 就 是 从PowerBuilder 的 窗 口 对 象 继 承 下 来 的, 所 以 窗 口 会 自 动 具 备 一 些 属 性, 例 如X 和Y 的 坐 标、 高 度、 宽 度、 窗 口 类 型、 标 题 等。 当 用 窗 口 画 笔 建 立 窗 口 时, 您 给 这 些 属 性 赋 了 值, 你 还 可 以 增 加 窗 口 的 函 数 和 事 件。 当 窗 口 被 保 存 时, 你 就 建 立 了 一 个 新 的 窗 口 继 承 类, 称 为:w_emp。 输 出 对 象 的 句 法 显 示 的 部 分 程 序 如 下:

----global type w_emp from Window
----int X=200
----int Y=233
----int Width=2405
----int Height=1285
----boolean TitleBar=true
----string Title="Employee Mainte-nance"
----string MenuName="m_emp"
----long BackColor=12632256
----boolean ControlMenu=true
----boolean MinBox=true
----boolean MaxBox=true
----boolean Resizable=true
----.
----.
----.
----end type
----global w_emp w_emp

---- 请 注 意 系 统 在 这 里 做 了 什 么: 首 先 定 义 了w_emp 窗 口 类 是 从window 类 继 承 而 来, 接 着 定 义 这 一 对 象 类 的 各 属 性 值, 在 句 法 的 最 后 一 行 声 明 了 一 个 全 局 变 量, 变 量 的 类 型( 对 象 类) 是w_e mp, 变 量 名( 对 象 实 例) 也 是w_emp。 也 就 是 说 在 您 建 立 了w_emp 这 一 窗 口 对 象 后, 系 统 就 具 有 了 w_emp 这 一 对 象 类 和w_emp 这 一 全 局 变 量。 这 个 变 量 是 引 用 变 量, 也 就 是 在 有 些 语 言 称 之 为 指 针 的 变 量 类 型。 引 用 变 量 并 不 真 正 含 有 窗 口 实 例 的 属 性, 而 是 包 含 了 一 个 位 置 或 是 内 存 中 的 一 个 地 址, 在 那 儿 你 可 以 找 到 实 例 的 实 际 属 性 和 程 序。 但 在 应 用 的 开 始 时 这 一 指 针 为 空, 因 为 在 内 存 中 并 没 有 一 个 这 样 的 窗 口 实 例。 当 系 统 执 行 了Open(w_emp) 后, 系 统 在 内 存 中 复 制 了 一 份w_emp 对 象 类 的 拷 贝 或 实 例,w_emp 这 一 参 照 变 量 指 向 了 这 一 地 址。 由 于 窗 口 对 象 的visibl e 等 属 性 都 是Public 类 型, 所 以 继 承 类 也 都 具 有 了 这 些 属 性。


张健姿-->PowerBuilder系列讲座-->4. 并发控制

4. 并发控制

---- 并 发 能 力 是 指 多 用 户 在 同 一 时 间 对 相 同 数 据 同 时 访 问 的 能 力。 一 般 的 关 系 型 数 据 库 都 具 有 并 发 控 制 的 能 力, 但 是 这 种 并 发 功 能 也 会 对 数 据 的 一 致 性 带 来 危 险。 试 想 若 有 两 个 用 户 都 试 图 访 问 某 个 银 行 用 户 的 记 录 并 同 时 要 求 修 改 该 用 户 的 存 款 余 额 时, 情 况 将 会 怎 样 呢? 我 们 可 以 对PowerBuilder 中 的DataWindow 进 行 设 置 来 进 行 并 发 控 制。 所 谓 并 发 控 制 就 是 指 在 用 户 数 据 修 改 的 过 程 中 保 证 该 数 据 不 被 覆 盖 或 改 变 的 方 式, 在 下 面 的 例 子 中 我 们 将 看 到 如 何 设 置Da taWindow 来 控 制 开 发 访 问。 为 了 说 明 问 题, 我 们 举 这 样 一 个 简 单 的 银 行 系 统 中 的 例 子, 某 用 户 的 存 款 状 况 如 右:

---- 我 们 假 设 事 情 的 经 过 是 这 样 的: 公 司 的 某 员 工 在 银 行 前 台 取 款2,000 元, 银 行 出 纳 查 询 用 户 的 存 款 信 息 显 示 银 行 存 款 余 额20,000 元; 正 在 这 时, 另 一 银 行 帐 户 转 帐 支 票 支 付 该 帐 户5,0 00 元, 机 器 查 询 也 得 到 当 前 用 户 存 款20,000 元, 这 时 银 行 的 出 纳 员 看 到 用 户 存 款 超 过 了 取 款 额 , 就 支 付 了 客 户2,000 元 并 将 用 户 存 款 改 为18,000 元, 然 后 银 行 的 另 一 名 操 作 员 根 据 支 票, 将 汇 入 的5,000 元 加 上, 把 用 户 的 余 额 改 为25,000 元, 那 么 数 据 库 管 理 系 统 是 否 可 以 接 受 这 些 修 改 呢?

---- 在DataWindows 的 设 计 中, 我 们 选 择 菜 单Rows|Update …, 会 出 现Specify Update Charac teristics 的 设 置 窗 口, 在 这 个 窗 口 中 我 们 设 置Update 语 句 中Where 子 句 的 生 成, 以 此 来 进 行 开 发 控 制。 在 这 里 有 三 个 选 项, 我 们 分 别 看 一 看 在 本 例 中 这 三 个 选 项 的 结 果:

----(1)Key Columns: 生 成 的Where 子 句 中 只 比 较 表 中 的 主 键 列 的 值 与 最 初 查 询 时 是 否 相 同 来 确 定 要 修 改 的 记 录。 在 上 述 的 例 子 中, 转 帐 支 票 的 操 作 将 覆 盖 出 纳 员 作 出 的 修 改, 这 样 银 行 损 失 两 千 元。

----(2)Key and Updateable Columns: 生 成 的Where 子 句 比 较 表 中 主 键 列 和 可 修 改 列 的 值 与 最 初 查 询 时 否 是 相 同。 在 上 例 中 两 次 查 询 出 的 结 果 都 是 有 两 万 余 额, 当 第 一 个 人 修 改 余 额 时 , 余 额 仍 是 二 万 元, 所 以 修 改 成 立, 而 支 票 转 帐 操 作 时 余 额 已 不 是 二 万, 所 以 该 列 不 匹 配, 修 改 失 败。

----(3)Key and Modified Columns:Where 子 句 比 较 主 键 和 将 要 修 改 的 列, 在 本 例 中, 结 果 与 Key and Updateable Columns 的 选 择 相 同, 因 为 余 额 已 改 变, 不 再 与 最 初 的 查 询 相 同, 因 此 仍 然 不 能 修 改。

---- 让 我 们 作 另 外 一 个 假 设, 我 们 把 银 行 后 台 作 支 票 转 帐 操 作 改 为 冻 结 用 户 存 款, 即 把 状 态 字 段 的 值 改 为 冻 结, 而 且 事 件 发 生 的 次 序 如 下 表, 那 么 表 中 的 次 序4 … 前 台 出 纳 的 修 改 能 不 能 成 立 呢:

----1.Key Columns:Where 子 句 只 比 较 主 键 值, 显 然 出 纳 员 的 修 改 是 允 许 的。

----2.Key and Updateable Columns: 生 成 的Where 子 句 包 括 比 较 所 有 可 修 改 的 列, 因 此 出 纳 修 改 时Statue 字 段 为 冻 结 与 出 纳 查 询 时 的tive 不 符, 修 改 失 败, 同 时 显 示 错 误 信 息。

----3.Key and Modified Columns:Where 子 句 的 比 较 包 括 主 键 和 要 修 改 的 列, 由 于 本 列 中 修 改 列 仍 为20,000 元 没 有 变 化, 所 以 出 纳 的 修 改 可 以 成 立。

---- 在 本 例 中, 我 们 可 以 看 到Key and Updateable Columns 的 选 项 最 严 格, 可 以 避 免 出 现 状 态 列 发 生 改 变 时 余 额 作 修 改 的 错 误, 但 是 这 也 会 禁 止 我 们 作 一 些 本 当 允 许 的 并 发 修 改, 如 出 纳 修 改 存 款 余 额, 而 业 务 员 修 改 用 户 的 联 系 地 址 等。 因 此 我 们 应 当 根 据 实 际 情 况, 选 择 适 当 的Upd ate 设 置。

---- 根 据 我 们 使 用 数 据 库 的 不 同, 我 们 还 有 一 些 其 他 的 控 制 并 发 访 问 和 修 改 的 选 择 方 案, 如 对 数 据 加 锁。 锁 是 一 个 用 户 避 免 其 他 用 户 对 指 定 行 作 修 改 的 操 作。 在 结 束 一 个 事 务 如 执 行com mit,rollback,disconnect 等 语 句 时 自 动 将 锁 释 放。 如 果 您 使 用 的DBMS 支 持 锁 的 操 作, 在Pow er-Builder 的DataWindow 设 计 时,Select 语 句 可 在from 子 句 中 加 上with holdlock: 即 在data Window 的SQL Window 中, 在 表 窗 口 的 标 题 处 点 击 右 鼠 标, 弹 出 菜 单 的 最 后 一 个 选 项 即 为Hold lock。 选 择 该 项, 生 成 的SQL 语 句 将 在re-trievel() 函 数 执 行 后 将 所 查 询 的 数 据 加 锁, 以 避 免 其 他 用 户 的 修 改 访 问, 直 至commit,rollback 等 事 件 发 生 后 解 锁。 这 种 方 式 带 来 的 问 题 是, 当 用 户 查 询 完 数 据 后 可 能 离 开 计 算 机 长 时 间 不 用, 这 段 时 间 内 其 他 用 户 均 无 法 修 改 数 据。 此 外 有 些DBMS 如Sybase 等 不 支 持 行 级 锁, 也 就 是 说 当 你 对 某 一 行 查 询 时 更 多 的 行 都 被 上 了 锁, 这 就 更 增 加 了 并 发 处 理 的 局 限 性。 另 一 个 值 得 注 意 的 问 题 是 在 多 窗 口 应 用 中 某 一 个 窗 口 的 事 务 提 交 将 会 导 致 使 用 一 事 务 中 其 他 数 据 窗 口 的 查 询 行 解 锁, 这 时 修 改 将 可 能 发 生 错 误。 某 些DBMS 系 统 支 持 一 个 称 作" 时 间 戳(timestamp)" 的 数 据 项 来 控 制 并 发 性。 每 张 表 中 都 有 一 个 时 间 戳 的 数 据 列, 当Insert 语 句 或Update 语 句 对 数 据 行 作 修 改 时 该 列 自 动 被 修 改 为 当 前 时 间。 当 你 要 作 修 改 时,where 子 句 可 检 查 时 间 戳 列 在 查 询 时 和 修 改 时 两 个 值 是 否 相 符, 以 此 来 确 保 您 作 出 的 修 改 不 会 覆 盖 别 人 的 修 改, 因 此 这 种 确 认 方 式 与key and Updateable Columns 选 项 相 同。 即 使 两 个 用 户 对 同 一 行 的 不 同 列 作 修 改, 后 一 个 修 改 者 也 将 失 败。 在 常 用 的 关 系 型 数 据 库 中Sybase 和Microsoft 的SQL Server 支 持 时 间 戳 的 使 用。 而 在PowerBuilder 中, 不 管 用 户 后 台 连 接 何 种 数 据 库, 只 要 表 中 带 有timestamp 的 列 名 且 数 据 类 型 为datetime,PB 将 自 动 忽 略Upda te characteristics 的 选 项, 而 在where 子 句 中 生 成 主 键 和 时 间 戳 列 的 比 较。

---- 如 果 您 所 用 的 数 据 库 不 支 持 时 间 戳 但 支 持 触 发 器, 您 也 可 以 在 表 中 增 加 一 列 整 数 型 的 列。 当 有 对 表 中 某 种 记 录 作 修 改 时, 该 列 自 动 加1。 下 列 使 用 的 是Watcom 数 据 库, 对Shipper 表 增 加Updcnt 字 段 并 作 两 个 触 发 器, 这 样 任 何 用 户 或 进 程 试 图 修 改 某 行 记 录 时, 该 字 段 均 可 发 生 变 化。

---- 对INSERT 触 发 器 的 编 写 如 下:

----DROP TRIGGER INS —SHIPPER’

----CREATE TRIGGER SHIPPER BEFORE INSERT ON SHIPPER

----REFERENCING NEW AS Newvalue

----FOR EACH ROW

---- BEGIN

---- SET newvalue.UpdCnt=newvalue.UpdCnt+1;

---- END'

---- 同 理 可 编 写UPDATE 触 发 器。

---- 在 您 的PowerBuilder 应 用 之 中, 除 表 的 主 键 外, 必 须 再 加 上 这 一 列 作 为 检 测 列 加 入Updat e 语 句 中 的Where 子 句 中, 这 样 再 作Update 操 作 时, 后 台 数 据 库 会 比 较 修 改 时 与 用 户 作Retriev e 操 作 时 数 据 是 否 相 等, 以 确 认 是 否 能 作 修 改。 在DataWindows 中 在Specify Update Charact eris-tics 的 对 话 框 的 右 下 角 的Unique key column(s) 中 加 上Updcnt 一 项, 同 时 注 意where c lause 中 选 择Key columns, 这 样PowerBuilder 在 构 造where 子 句 时 就 会 认 为Updcnt 亦 是 表 的 主 键, 而 成 为 检 测 项。

---- 当 数 据 窗 口 的Update 函 数 被 调 用 后, 触 发 器 将 修 改 过 记 录 中 的Updcnt 列 表 为 新 值, 为 保 证 下 一 次 修 改 能 够 有 效, 您 应 当 立 即 作Retrieve() 以 使DataWindow 缓 冲 区 中Updcnt 的 值 与 数 据 库 相 同。 显 然 修 改 后 立 即 查 询 的 代 价 要 比 其 他 任 何 一 种 并

---- 发 控 制 的 代 价 要 小 得 多。


张健姿-->PowerBuilder系列讲座-->5. DataWindow的打印输出

5. DataWindow的打印输出

---- 在PowerBuilder 应 用 程 序 的 开 发 中, 各 种 报 表 的 打 印 功 能 是 必 不 可 少 的。Power-Builde r4.0 提 供 了 大 量 与 打 印 机 进 行 通 信 的 内 部 函 数, 用 于 各 种 打 印 输 出。 运 用 这 些 函 数, 您 不 仅 可 以 打 印 常 见 的DataWindow 对 象, 还 可 以 打 印 字 符 串、 位 图、 图 形 对 象, 甚 至 是 全 屏。

----DataWindow 的 两 种 打 印 方 法:

---- 在PowerBuilder 中 我 们 可 以 有 两 种 方 法 打 印DataW-indows。 一 种 最 简 单 的 方 法 就 是 直 接 地 将DataWindow 提 交 给 打 印 机, 另 一 种 方 法 是 将 大 量 的DataWindow( 或 混 合 其 他 的 对 象) 放 在 一 起 作 为 一 个 单 一 的 打 印 作 业 提 交 输 出。

---- 法 一: 不 指 定 打 印 作 业, 而 发 送 一 个 单 一 的DataW-indow。 将 报 表 传 给 打 印 机 输 出 的 最 简 单 的 方 式 是 使 用DataWindow 对 象 的Print() 函 数, 您 只 需 简 单 指 明 您 打 印 的DataWindow 控 件 的 名 字 就 可 以 得 到 该DataWindow 生 成 的 报 表 了。 例:

----dw_data.print({cancel dialog})

----Print() 函 数 不 需 要 任 何 参 数, 但 是 它 有 一 个 可 选 参 数, 可 以 控 制PowerBuilder 是 否 显 示 Print Cancel 对 话 框 窗 口( 缺 省 为 自 动 出 现, 如 图1 所 示)。Cancel dialog 参 数 是 一 个 布 尔 型 变 量, 其 缺 省 值 为 真。

---- 一 Print:HP LaserJet HP on LPT1:

----Preparing page 1 of 2

---- Cancel

---- 图1: 除 非 特 别 指 定, 否 则 将 在 打 印 时 缺 省 出 现Print Cancel 对 话 框。Print() 函 数 还 有 一 些 格 式 可 控 制 打 印 正 文 字 符 串, 但 是 它 们 要 求 首 先 打 开 一 个 打 印 作 业。 这 些 格 式 将 在 后 面 解 释。

---- 法 二: 集 成 多 个DataWindow 作 为 一 个 打 印 作 业。 您 可 以 将 提 交 打 印 的 多 个DataWindow 作 为 一 个 单 一 的 打 印 指 令。 为 实 现 这 一 功 能 必 须 打 开 一 个 所 谓 打 印 作 业(Print job), 即 发 送 给 打 印 机 一 个 单 一 工 作 单 元, 其 中 可 以 包 含 多 个 子 任 务。 在PowerBuilder 中 一 个 打 印 作 业 由 它 的 作 业 代 号 唯 一 指 定。 我 们 使 用PrintOpen({job name}) 函 数 打 开 一 个 作 业, 并 得 到 打 印 作 业 号。PrintOpen() 函 数 有 一 个 可 选 参 数, 就 是 打 印 作 业 名 称, 这 个 作 业 名 字 将 作 为Windows 3. x 的Print Manager 作 业 集 中 的 名 字。 当 您 使 用PrintOpen() 函 数 时, 系 统 将 另 起 一 页 开 始 打 印 。 当 然 在 打 印 作 业 中, 您 还 可 以 使 用 其 他 函 数 换 页, 比 如 将 在 后 面 讲 述 的PrintPage() 函 数。

---- 在 您 打 开 了 一 个 作 业 号 后, 就 可 以 在 这 个 作 业 内 使 用 下 列 函 数 继 续 其 他 各 种 各 样 的 打 印 项 目。

----1) 在 打 印 作 业 中 加 入 字 符 串:

---- 我 们 也 可 以 使 用 另 外 一 个 与Print() 函 数 略 微 不 同 的 函 数 来 打 印 字 符 串:

----PrintText(print_job_number,string,x,y{,font_number})

---- 这 个 函 数 将 包 含 在string 参 数 中 的 正 文 字 符 串 当 作 一 个 对 象 进 行 打 印。 这 个 打 印 的 字 符 串 在 由X,Y 参 数( 以 千 分 之 一 寸 为 单 位) 指 定 的 坐 标 处 开 始。font_number 参 数 是 一 个 可 选 参 数 , 缺 省 使 用 当 前 字 型, 否 则font_number 的 值 可 以 从0 到8,0 是 打 印 机 缺 省 的 字 型,1-8 是 使 用 后 面 讲 述 的PrintDefineFont() 函 数 定 义 的 字 型。 这 个 函 数 不 像 其 他 指 定 坐 标 的 函 数, 它 将 改 变 打 印 光 标 的 位 置。 这 个 函 数 的 返 回 值 是 打 印 光 标 的 新X 坐 标, 但 它 不 改 变Y 坐 标。

在 打 印 作 业 中 打 印 对 象

---- 下 面 的 这 些 函 数 可 以 使 您 在 打 开 的 打 印 作 业 中 打 印 不 同 的 对 象。 这 些 函 数 中 的 任 一 个 指 定X、Y 坐 标 的 函 数 都 不 会 改 变 当 前 打 印 光 标 位 置。

----objectname.Print(print_job_number,x,y {,width,height})

---- 这 个 函 数 可 以 将 任 何 一 个object_name 指 定 的 对 象 放 在print_job_number 定 义 的 打 印 作 业 中 打 印, 这 个 对 象 可 以 是 一 个 窗 口 也 可 以 是 一 个DragObject 类 的 继 承 类( 包 括 了 所 有 的 窗 口 控 件)。X、Y 参 数 指 定 了 一 页 中 的 坐 标, 您 可 以 指 出 所 打 印 对 象 出 现 的 位 置( 以 千 分 之 一 寸 为 单 位)。Width 和Height 参 数 是 可 选 的 参 数, 用 来 指 明 你 所 需 的 打 印 对 象 的 宽 和 高( 仍 以 千 分 之 一 寸 为 单 位)。 如 果 缺 省,PowerBuilder 将 使 用 对 象 本 身 的 尺 寸 打 印。

----PrintBitmap(print_job_number,bitmap,x,y,width,height)

---- 这 个 函 数 将 在 由X,Y 参 数( 千 分 之 一 寸 为 单 位) 指 定 的 打 印 区 域 上 打 印 一 个 位 图。bitmap 参 数 是 一 个 包 含 要 打 印 位 图 的 文 件 名 的 字 符 串( 例:BRUSH.BMP)。Width 和Height 参 数 指 定 位 图 显 示 的 宽 和 高, 这 个 参 数 并 不 是 可 选 的。 输 入0 值, 意 味 着 位 图 按 它 本 身 的 值 打 印。

----PrintDataWindow(print_job_number,datawindowcontrol)

---- 这 个 函 数 将 一 个DataWindow 控 件 的 内 容 作 为 这 个 指 定 的 打 印 作 业 的 一 部 分 来 打 印。 由 于 Power-Builder 使 用 在DataWindow 对 象 中 定 义 的 字 型,Print-DefineFont() 和PrintSet-Font () 函 数 不 对 其 产 生 影 响。Powersoft 建 议 如 用PrintDataWindow, 那 么 在 这 个 打 印 作 业 中 就 不 需 要 使 用 其 它 函 数。 因 为 这 个 函 数 将 使 用 整 张 纸, 每 一 次 调 用PrintDataWidnow, 您 打 印 的 下 一 项 都 将 另 起 一 页。

----PrintLine(Print_job_number,X1,Y1,X2,Y2,thickness)

---- 这 个 函 数 将 打 印 一 指 定 宽 度 的 线。 这 条 线 将 在 由X1,Y1 参 数( 以 千 分 之 一 寸 为 单 位) 指 定 的 坐 标 处 开 始, 在 由X2,Y2 参 数( 也 以 千 分 之 一 寸 为 单 位) 指 定 的 坐 标 处 结 束。thickness 参 数 是 一 个 整 型 变 量, 它 指 定 了 这 条 线 的 宽 度, 以 千 分 之 一 寸 为 单 位。

----PrintOval(print_job_number,x,y,width,height,thickness)

---- 这 个 函 数 打 印 一 个 椭 圆( 如 果 宽 度 和 高 度 相 同 则 形 成 一 个 圆) 它 的 轮 廓 线 的 宽 度 由thick ness 指 定。 椭 圆 都 由 一 个 想 象 的 边 框 包 围,X,Y 参 数 指 定 了 这 个 界 框 的 左 上 角 的 位 置( 千 分 之 一 寸 为 单 位),width,heigh 为 这 个 边 框 的 高 和 宽。

----PrintRect(print_job_number,x,y,width,height,thickness)

---- 除 了 它 是 打 印 一 个 矩 形 外, 这 个 函 数 与PrintOval() 函 数 完 全 一 样。

----PrintRoundRect(print_job_number,x,y,width,height,thickness)

---- 除 了 它 是 打 印 一 个 圆 角 矩 形 外, 这 个 函 数 与PrintO-val() 函 数 完 全 一 样。

----( 未 完 待 续)


张健姿-->PowerBuilder系列讲座-->6. DataWindow的打印输出（续）

6. DataWindow的打印输出（续）

----PowerBuilder 提 供 了 一 组 打 印 控 制 函 数 来 控 制 要 交 给 打 印 机 打 印 的 对 象。 第 一 个 函 数P rintOpen() 在 这 章 的 前 面 我 们 已 介 绍 过 了, 其 它 还 有:

----PrintCancel(print_job_number)

---- 这 个 函 数 将 中 止 打 印 作 业 号 所 打 印 的 作 业, 不 发 送 任 何 内 容 给 打 印 机。

----datawindowcontrol.PrintCancel()

---- 这 个 函 数 与 上 一 函 数 的 区 别 是: 这 个 函 数 取 消 的 是 用Datawindowcontrol.Print() 提 交 的 作 业。

----PrintClose(print_job_number)

---- 这 个 函 数 将 关 闭 指 定 的 打 印 作 业 并 且 把 它 发 给 打 印 机( 或 者 假 脱 机 程 序)。 为 了 避 免 挂 起 打 印 作 业, 所 有 打 开 的 打 印 作 业 都 应 在 应 用 的 结 束 前 被 关 闭 或 取 消。

----PrintDefineFont(print_job_number,fontnumber,facename,height,weight,font-pit ch,fontfamily,italic,underline)

---- 你 可 以 用 这 个 函 数 定 义 字 型。Power-Builder 允 许 在 一 个 打 印 作 业 中 使 用 八 种 字 型, 用 这 个 函 数 可 定 义 其 中 的 任 一 个。 这 个 字 型 所 属 的 打 印 作 业 号 由Print_job_number 参 数 指 定,fo nt_number 参 数 是 这 个 打 印 作 业 所 采 用 的 字 型 号(1-8)。 其 余 的 参 数 描 述 了 字 型 如 何 显 示, 是 否 斜 体 字、 粗 体 和 下 划 线 等。facename 参 数 是 一 个 包 含 了 你 所 定 义 的 字 体 名 字 的 字 符 串( 例 :courier 100CPI)。height 参 数 是 你 所 需 的 以 千 分 之 一 寸 为 单 位 的 字 体 的 高 度( 例:250 将 是 一 个18 点 阵courier 10 CPI 字 体),height 参 数 也 可 使 用 一 负 值 来 表 示 点 阵 数 指 定 点 尺 寸( 例 :-18 是18 点 阵)。weight 参 数 值 是 您 所 要 的 字 型 的 笔 画 权 值( 例:400 是 平 常 正 文,700 是 粗 体) 。fontpitch 参 数 是 一 个 枚 举 数 据 类 型, 它 指 定 了 字 体 的 间 距(Default!,Fixed! 或variable! );fontfamily 参 数 也 是 一 个 枚 举 类 型, 指 定 了 字 体 系 列(AnyFont!,Decorative!,Mod-ern!,R oman!,Script!,Swiss!)。 这 两 种 参 数 只 在 基 于Windows 的 操 作 系 统 中 使 用, 在Macintosh 等 机 器 中 不 能 使 用。italic 参 数 是 一 个 布 尔 型 变 量, 指 定 字 体 是 斜 体(True) 或 非 斜 体(False),un derline 参 数 也 是 一 个 布 尔 值, 用 法 同 上。

----PrintOpen()

---- 这 个 函 数 已 在 这 章 的 开 始 讲 述 过 了, 它 定 义 一 个 打 印 作 业 以 便 将 所 有 的 打 印 项 成 组 发 送 给 打 印 机。 它 的 返 回 值 是 唯 一 的 打 印 作 业ID, 如 果 返 回 值 为 负, 则 意 味 着 产 生 错 误。

----PrintPage(print_job_number)

---- 这 个 函 数 将 指 定 的 打 印 作 业 的 当 前 页 传 送 给 打 印 机( 或 假 脱 机 程 序), 并 另 起 一 页 用 于 之 后 的 打 印 集。

----PrintSend(print_job_number,string{,zero_character})

---- 这 个 函 数 在 打 印 作 业 中 发 送 一 个 特 定 的 字 符 串 给 打 印 机。 这 个 字 符 串 一 般 是 一 个 包 含 转 义 字 符 的 命 令 串 用 来 启 动 或 操 作 打 印 机。 这 些 打 印 机 的 控 制 代 码 因 打 印 机 的 不 同 而 不 同, 您 应 查 询 您 的 打 印 机 手 册 来 得 到 正 确 的 代 码。

---- 这 个 与 打 印 机 进 行 通 信 的 字 符 串 应 是 一 个ASCII 码 集, 并 以ASCII 码0 来 结 束。 如 果 你 需 要 发 送ASCII 码0, 你 可 以 用 另 外 的 一 个 字 符 来 替 代0, 给 定 的 这 个 替 代 字 符 的ASCII 值 由 可 选 参 数 zero-character 来 指 定。 这 个 函 数 一 般 用 于 改 变 纸 的 定 向(Landscape 或por-trait) 或 者 改 变 打 印 用 的 纸 盘 等。

----PrintSetFont(print_job_number,fontnumber)

---- 这 个 函 数 设 置 当 前 打 印 机 字 体, 它 是 你 使 用PrintDefineFont() 函 数 中 定 义 的 八 个 字 体 中 的 一 个。

----PrintSetSpacing(print_job_number,spacing)

---- 这 个 函 数 使 用Spacing 参 数 来 决 定 正 文 行 之 间 的 行 距。 这 个 值 乘 以 当 前 字 体 高 得 到 行 距 , 缺 省 值 是1.2。

----PrintSetup()

---- 这 个 函 数 调 用Windows 的Print Setup 对 话 框, 实 际 显 示 的Setup 窗 口 依 赖 于 你 所 安 装 的 打 印 机 驱 动 程 序。

----PrintWidth(print_job_number,string)

---- 这 个 函 数 的sting 参 数 返 回 字 符 串 的 宽 度( 以 千 分 之 一 寸 为 单 位)。 返 回 值 的 大 小 是 依 赖 于 当 前 选 择 的 字 体。

----PrintX(print_job_number)

---- 这 个 函 数 返 回 当 前 打 印 光 标 的X 坐 标( 水 平) 值。

----PrintY(print_job_number)

---- 这 个 函 数 返 回 当 前 打 印 光 标 的Y 坐 标( 垂 直) 值。

---- 除 非 特 别 说 明, 上 述 函 数 的 返 回 值1 意 味 着 成 功,-1 意 味 着 失 败。

DataWindow 控 件 的 打 印 属 性

---- 使 用modify 函 数, 我 们 可 以 改 变DataWindow 的 打 印 属 性。 在 打 印DataW-indow 之 前, 我 们 可 以 调 用 如 图 所 示 的 窗 口, 从 而 得 到 我 们 想 让 用 户 设 置 的 参 数。 这 个 窗 口 在PowerBuilder E nterprise 的 实 例 库 中, 您 也 可 以 在 直 接 调 用 它 前 对 其 显 示 界 面 进 行 汉 化 和 作 适 当 修 改。

---- 例 如, 我 们 使 用 下 面 语 句 来 改 变 我 们 想 打 印 的 份 数。

----dw_data..Modify("DataWindow.Print.Copies=3")

---- 你 可 以 使 用PowerBuilder Enterprise 打 包 软 件DWSYN40.EXE----DataWindow 语 法 生 成 器 来 建 立Modify 语 句。

---- 下 面 所 列 的 可 以 修 改 的 参 数 全 部 以DataWindow.print 为 前 缀, 后 接 这 个 属 性。

----Collate=Yes or No( 缺 省 为no, 不 作 校 对)

---- 这 个 属 性 用 来 指 示 是 否 要 对 打 印 进 行 校 对。 校 对 通 常 较 慢, 因 为 整 个 打 印 过 程 必 须 反 复 几 次 以 得 到 一 个 校 对 后 的 结 果。

----Color=1( 彩 色)or 2( 单 色)

---- 这 个 属 性 指 示 传 送 给 打 印 输 出 的 是 彩 色( 如 果 你 有 一 台 彩 色 打 印 机) 还 是 单 色。

----Columns= ＜an integer ＞( 缺 省 为1)

---- 这 个 属 性 用 来 指 示DataWindow 以 报 纸 风 格 打 印 在 一 页 上 的 栏 的 数 量。

----Columns.Width= ＜an integer ＞

---- 这 个 属 性 指 示 以 报 纸 风 格 打 印 的 栏 的 宽 度( 基 于 为DataWindow 指 定 的 单 位)。

----Copies= ＜an integer ＞

---- 这 个 属 性 指 示 你 所 需 的 打 印 的 数 量。

----DocumentName= ＜a string ＞

---- 这 个 属 性 可 以 让 你 为 文 件 设 置 一 个 名 字。 这 个 名 字 在DataWindow 传 送 给 打 印 机 时 在 打 印 序 列 中 出 现。

----Duplex=1( 单 一)or 2( 水 平)or 3( 垂 直)

---- 这 个 属 性 指 示 打 印 输 出 的 方 向。

----Filename= ＜filename string ＞

---- 这 个 属 性 仅 当 把DataWindow 输 出 为 一 个 文 件 时 才 有 用。 这 个 属 性 包 含 的 字 符 串 是 将 在 磁 盘 中 存 储 的 文 件 的 名 字。

----Margin.Bottom= ＜an integer ＞

---- 这 个 属 性 为 一 整 型 指 示 页 面 下 部 空 白 的 宽 度( 用DataWindow 指 定 的 单 位)

----Margin.Left= ＜an integer ＞

---- 功 能 与Margin.Botton 相 同, 但 指 示 的 是 左 侧 空 白 宽 度。

----Margin.Right= ＜an integer ＞

---- 功 能 与Mangin.Bottom 相 同, 但 指 示 的 是 右 侧 空 白 宽 度。

----Margin.Top= ＜an integer ＞

---- 功 能 与Mangin.Bottom 相 同, 但 指 示 的 是 顶 端 空 白 宽 度。

----Orientation=0( 打 印 机 的 缺 省 设 置) 或2( 横 向 打 印) 或3( 纵 向 打 印) 这 个 属 性 用 来 指 示 打 印 定 向。 你 可 以 使 用 打 印 机 缺 省 设 置, 或 者 设 置 成Landscape 或Portrait 模 式, 覆 盖 缺 省 的 打 印 设 置。

----Page.Range= ＜page range string ＞

---- 这 个 属 性 为 一 字 符 串 指 明 你 要 打 印 的 页 数。 在 字 符 串 中 的 数 字 用 逗 号 分 隔, 或 者 数 字 之 间 用 由 一 横 线 分 隔 表 示 一 范 围 或 两 者 同 时 使 用, 例 如:"1,2,3,6-12", 空 字 符 串 表 示 要 打 印 所 有 的 页。

----Page.RangeInclude=0( 全 部 打 印) 或1( 打 印 奇 数 页) 或2( 打 印 偶 数 页)

---- 这 个 属 性 用 来 指 示 在Page.Range 指 示 的 范 围 中 哪 些 页 要 打 印。 你 可 以 先 选 择 打 印 偶 数 页 , 然 后 将 纸 放 入 打 印 机 中, 再 选 择 奇 数 页 打 印。 这 一 功 能 像 在 打 印 书 时 那 样, 需 在 纸 的 两 面 打 印 时 非 常 有 用。

----Paper.Size= ＜an integer ＞

---- 这 一 属 性 用 来 指 示 打 印 纸 的 尺 寸。 可 接 受 的 值 如 下:

----0- 缺 省

----1-Letter 8 1/2 x 11 in

----2-LetterSmall 8 1/2 x 11 in

----3-Tabloid 17 x 11 inches

----4-Ledger 17 x 11 in

----5-Legal 8 1/2 x 14 in

----6-Statement 5 1/2 x 8 1/2 in

----7-Executive 7 1/4 x 10 1/2 in

----8-A3 297 x 420 mm

----9-A4 210 x 297 mm

----10-A4 Small 210 x 297 mm

----11-A5 148 x 210 mm

----12-B4 250 x 354

----13-B5 182 x 257 mm

----14-Folio 8 1/2 x 13 in

----15-Quarto 215 x 275 mm

----16-10 x 14 in

----17-11 x 17 in

----18-Note 8 1/2 x 11 in

----19-Envelope #9 3 7/8 x 8 7/8

----20-Envelope #10 4 1/8 x 9 1/2

----21-Envelope #11 4 1/2 x 10 3/8

----22-Envelope #12 4 x 11 1/276

----23-Envelope #14 5 x 11 1/2

----24-C size sheet

----25-D size sheet

----26-E size sheet

----27-Envelope DL 110 x 220 mm

----28-Envelope C5 162 x 229 mm

----29-Envelope C3 324 x 458 mm

----30-Envelope C4 229 x 324 mm

----31-Envelope C6 114 x 162 mm

----32-Envelope C65 114 x 229 mm

----33-Envelope B4 250 x 353 mm

----34-Envelope B5 176 x 250 mm

----35-Envelope B6 176 x 125 mm

----36-Envelope 110 x 230 mm

----37-Envelope Monarch 3.875 x 7.5 in

----38-6 3/4 Envelope 3 5/8 x 6 1/2 in

----39-US Std Fanfold 14 7/8 x 11 in

----40-German Std Fanfold 8 1/2 x 12 in

----41-German Legal Fanfold 8 1/2 x 13 in

----Paper.Source= ＜an integer ＞

---- 这 一 属 性 是 一 个 整 型 值, 指 明 纸 的 来 源。 可 接 受 的 值 如 下:

----0-Default

----1-Upper

----2-Lower

----3-Middle

----4-Manual

----5-Envelope

----6-Envelope manual

----7-Auto

----8-Tractor

----9-Smallfmt

----10-Largefmt

----11-Large capacity

----14-Cassette

----Preview=Yes( 进 入 打 印 预 览 状 态) 或No( 缺 省- 退 出 打 印 预 览 状 态)

---- 这 个 属 性 允 许 您 的DataWindow 进 入 和 退 出 打 印 预 览 状 态。 这 种 状 态 可 以 所 见 即 所 得 地 看 到 用 户 即 将 打 印 的 结 果。

----Preview.Rulers=Yes( 显 示 标 尺) 或No( 缺 省- 不 显 示 标 尺)

---- 这 个 属 性 是 指Print Preview 状 态 下, 是 否 要 在DataWindow 对 象 上 显 示 标 尺。

----Preview.Zoom= ＜an integer ＞

---- 这 个 属 性 是Print Preview 状 态 下, 为 预 览 的DataWindow 指 定 一 个 放 缩 因 子。 缺 省 值 为1 00%(Preview.zoom=100%)

----Prompt=Yes( 缺 省- 显 示 提 示) 或No( 无 提 示)

---- 这 个 属 性 可 以 控 制PowerBuilder 显 示 一 个 允 许 用 户 在 作 业 打 印 前 取 消 打 印 作 业 的 提 示。

----Quality=0( 缺 省)or 1( 高)or 2( 适 中)or 3( 低)or 4( 草 稿)

---- 这 个 属 性 可 以 选 择 打 印 输 出 的 质 量。

----Scale= ＜an integer ＞

---- 这 个 属 性 指 定 打 印 输 出 放 大 或 缩 小 的 比 例。

制 作 一 个 屏 幕 打 印: 如 何 打 印 全 屏

----PowerBuilder 4.0 的 一 个 新 特 征 是 可 以 把 屏 幕 图 像 提 交 打 印 作 业, 在 纸 上 的 任 一 点 打 印 屏 幕。 我 们 使 用PrintScreen() 函 数 实 现 这 一 功 能。

----PrintScreen() 的 语 法 如 下:

----PrintScreen(print_job_number,x,y{,width,height})

---- 执 行 这 一 函 数, 在 指 定 的 打 印 作 业 中 将 当 前 屏 在 由X,Y 参 数( 千 分 之 一 寸 为 单 位) 指 定 的 坐 标 处 打 印。width 和height 参 数 指 示 你 所 要 的 打 印 屏 幕 的 宽 和 高。 最 后 这 两 个 参 数 是 可 选 的 , 如 果 缺 省, 屏 幕 将 按 初 始 的 宽 度 和 高 度 打 印。


张健姿-->PowerBuilder系列讲座-->7. 利用SetActionCode函数控制DataWindow

7. 利用SetActionCode函数控制DataWindow

----DataWindow 控 件 的 一 些 事 件 有 一 个 动 作 码 操 纵 这 个 事 件 之 后 的 缺 省 动 作。 在Pow-erBui lder 4.0 中 我 们 可 以 使 用SetAction-Code 函 数, 来 设 置 这 个 动 作 码 的 值 以 控 制 在 这 些 事 件 发 生 后 的 处 理 过 程。( 在PowerBuilder 5.0 中, 由 于 事 件 可 以 有 返 回 数 值, 所 以 采 用 返 回 一 个 整 型 数 值 来 取 代SetActionCode 函 数, 比 如 使 用return 1 取 代SetActionCode(1), 但 基 本 的 使 用 规 则 两 者 是 相 同 的。)DataWindow 控 件 中 下 列 事 件 使 用 动 作 码:

----CLICKED

----DBERROR

----ITEMCHANGED

----ITEMERROR

----PRINTPAGE

----RETRIEVEROW

----RETRIEVESTART

----UPDATESTART

---- 有 效 的 动 作 码 值 和 应 当 的 处 理 过 程 随 事 件 的 不 同 而 不 同。

Clicked Event

---- 无 论 何 时, 当 用 户 在DataWindow 控 件 上 点 击 时,CLICKED 事 件 被 触 发。 如 果 点 击 在 一 个 有 效 的 行 上, 那 么DataWindow 将 自 动 把 此 行 作 为 当 前 行。 如 果 你 不 想 换 行, 就 可 以 使 用SetActi onCode 来 停 止。

----0 进 行 换 行 和CLICKED 事 件。( 缺 省)

----1 停 止 处 理CLICKED 事 件。

---- 例 如: 下 面 一 段 代 码 只 允 许 用 户 点 击 在 自 己 的 用 户 号 上。

----//Clicked Event

----//We'll assume there is an instance variable with the current user's

----//User ID:string is_user_id

----long ll_row

----string ls_user_id

----ll_row =GetClickedRow()

----//No need to continue if the user didn't click on a valid row

----if ll_row ＜1 then

----return

----end if

----ls_user_id=GetItemString(11_row,"user_id")

----//If the user_id is not the current user then disallow row change

----if ls-user-id ＜ ＞is-user-id then

----beep(1)

----SetActionCode(1)

----return

----end if

ItemChanged Event

----DataWindow ITEMCHANGED 事 件 可 以 有 几 种 不 同 的 操 作: 接 受 前 一 字 段 的 新 值, 因 有 错 误 而 拒 绝 接 受 新 值, 拒 绝 新 值 但 是 继 续 其 它 的 处 理 过 程。 这 些 值 如 下:

----0 接 受 新 的 数 据 值。( 缺 省)

----1 拒 绝 新 的 数 据 值。( 启 动ItemEr-ror 事 件)

----2 拒 绝 新 的 数 据 值 但 是 焦 点 改 变。

---- 在ITEMCHANGED 事 件 中 使 用SetAction-Code 函 数 可 以 进 行 多 字 段 的 交 叉 确 认。 例 如, 银 行 系 统 中 为 确 认Account Status 是 否 可 以 转 为Inactive, 就 需 检 验Balance 字 段 是 否 为 零:

----//ItemChanged Event

----Decimal (2) ld_balance //Customer Account Balance

----Long ll_currow // Current Row Number

----String ls_column_name //The name of the column that changed

----String ls_status //Customer Account Status

----ll_currow=this.GetRow()

----ls_column_name=this.GetColumnName()

----CHOOSE CASE ls_column_name

----...

----...

----CASE "STATUS"

----ls_status=this.GetText()

----//If STATUS is Inactive

----IF ls_status= "I" THEN

---- ld_balance=this.GetItemDecimal (ll_currow,"BALANCE")

----IF ld_balance ＜ ＞0 THEN

----//SET AN ERROR

----this.SetActionCode(1)

----RETURN

----ELSE

----//ACCERT THE VALUE

----this.SetActionCode(0) /* not required since 0 is default */

----RETURN

----END IF

----END IF

----END CHOOSE

---- 在 程 序 中,SetActionCode 函 数 不 一 定 要 在 最 后 一 行, 但 是 由 于 其 他DataWindow 函 数 可 能 会 重 置 动 作 码。 为 了 避 免 这 个 问 题, 一 般 在SetActionCode 后 面 立 即 执 行Re-turn 结 束 这 个 程 序 段。

---- 在ITEMCHANGED 事 件 中 使 用SetAc-tionCode 函 数 用 途 是 可 以 给 该 字 段 一 个 新 值, 而 不 是 用 户 输 入 的 那 样。 例 如: 用 户 将 日 期 输 入 为 星 期 日, 但 是 我 们 希 望 将 其 改 为 在 此 之 后 的 第 一 个 非 休 息 日。 实 现 这 一 功 能 并 不 像 想 象 的 那 样 简 单:

----//ItemChanged Event

----date ldt_process //process date

----long ll_currow // Current Row Number

----string ls_column_name // The name of the column that changed

----ll_currow=this.GetRow()

----ls_column_name=this.GetColumnName()

----CHOOSE CASE ls_column_name

----...

----...

----CASE "process_date"

----ldt_process=f_get_next_bus_date(date(this.GetText()))

---- 错 误 this.SetText(ldt_process)

----this.AcceptText()

----...

---- 执 行 上 述 代 码, 系 统 将 进 入 死 循 环。 因 为 用AcceptText 函 数 改 变 日 期 的 同 时, 也 触 发ITE MCHANGED 事 件, 只 是 当 前 列 仍 在process-data 列 上, 这 样 就 导 致 堆 栈 溢 出。 因 此 在ITEMCHANG ED 事 件 中 不 能 使 用Ac-ceptText 函 数, 应 使 用 我 们 这 里 介 绍 的Se-tActionCode 这 一 函 数 来 完 成 这 一 功 能:

----CASE "process_date"

---- ldt_process=f_get_next_bus_date(date(this.GetText()))

---- 正 确 this.SetItemText(ll_currow,"process_date",ldt_process)//set value i n buffer

---- this.SetActionCode(2) // reject edit control value

----RETURN

---- 在Primary!Buffer 中 将process_date 的 值 置 为ldt_process,SetActionCode(2) 摒 弃 用 户 在edit 控 件 中 输 入 的 值( 星 期 日), 并 允 许 改 变 焦 点( 没 有 错 误 发 生)。

ItemError Event

---- 在 任 何 时 候, 当 一 个DateWindow 列 没 有 通 过 有 效 性 检 验 或 者 这 个 值 在ITEM-CHANGED 事 件 中 被 拒 绝 时,ITEMER-ROR 事 件 启 动。 如 同ITEMCHANGED 事 件 一 样, 它 的 动 作 码 也 可 以 设 置 为 接 受 或 拒 绝 这 个 字 段 的 新 输 入 值。 它 还 可 以 在 拒 绝 新 值 时, 决 定 是 否 取 消 错 误 信 息 框 的 显 示。 ITEMERROR 事 件 的 动 作 码 可 以 是:

----0 拒 绝 新 的 数 据 值 并 且 显 示 错 误 信 息;( 缺 省)

----1 拒 绝 新 的 数 据 值 而 不 显 示 错 误 信 息;

----2 接 受 新 的 数 据 值;

----3 拒 绝 新 的 数 据 值 但 是 允 许 改 变 焦 点。

---- 如 果 我 们 想 要 在 一 特 定 区 域 显 示 一 个 用 户 自 定 义 的 错 误 信 息 来 代 替Power-Builder 本 身 错 误 信 息 框, 我 们 可 以 使 用SetActionCode 来 取 消 标 准 的 信 息 框。 例 如, 在 前 面 例 子 中, 当 收 支 差 额 不 是0 是0 时, 我 们 就 可 以 用 这 一 方 法 显 示 一 个 错 误 信 息:

----//ItemError Event

----Long 11_Currow /* Current Row Number */

----String ls_column_name /* The name of the column that changed */

----ll_currow=this.GetRow()

----ls_column_name=this.GetColumnName

----CHOOSE CASE s_column_name

----CASE "status"

----MessageBox("Error","Account cannot be changed to Inactive"+ "Balance is no t zero.")

----this.SetActionCode(1)

----RETURN

----...

----END CHOOSE

---- 在ITEMERROR 事 件 中 使 用SetAc-tionCode, 我 们 就 可 以 有 选 择 地 忽 略DataWindow 对 象 的 一 个 列 中 输 入 的 有 效 性 规 则。 例 如, 在 收 支 差 额 中, 我 们 有 下 面 这 个 有 效 性 规 则:

----Real(GetText()) ＜=10000

---- 客 户 收 支 差 额 不 应 超 过10,000 元, 如 果 我 们 允 许 使 用 公 司 帐 户 的 客 户 可 以 超 过10,000, 我 们 可 以 使 用 如 下 方 式:

----//ItemError Event

----CHOOSE CASE ls_column_name

----CASE "balance"

----// Allow balance over ＄10,000 on Corportate accounts

----IF ld_balance not ＜=10000 AND ls_type="C"

----this.SetActionCode(2)

----RETURN

----END IF

----...

---- 我 们 也 可 以 像 前 面ITEMCHANGED 事 件 那 样 在Prinary!Buffer 中 拒 绝 新 输 入 的 值 并 填 入 新 值, 只 是 在 这 里 将 动 作 码 置 为3。

DBError Event

---- 在 执 行 了dw.Retrieve,dw.Update() 函 数 或 嵌 入 式SQL 语 句 并 发 生 了 一 个 数 据 库 错 误(SQ LCode 等 于-1) 时, 触 发DBError 事 件。 许 多Power-Builder 的 开 发 商 都 在 为 这 种 情 况 设 计 了 标 准 的 数 据 库 错 误 信 息 显 示。 为 了 使PowerBuilder 不 显 示 缺 省 的 数 据 库 错 误 信 息, 我 们 可 以 使 用SetActioncode。DBError 事 件 的 动 作 码 值 如 下:

----0 显 示 错 误 信 息。( 缺 省)

----1 不 显 示 错 误 信 息。

---- 例 如:

----//DBError event

----MessageBox(" 数 据 库 错 误"," 错 误 值"+string(this.DBErrorCode)+&

----" 错 误 信 息 为:"+this.DBErrorMessage(),StopSign!)

----//Supress PB generated DB Error Message.

----this.Set ActionCode(1)

----return

PrintPage

----PRINTPAGE 事 件 是 在 执 行dw.Print() 函 数 之 后, 数 据 传 送 给 打 印 机 之 前 触 发。 当 打 印 一 个 DataWindow 时, 你 可 在 打 印 之 前 设 置 动 作 码 来 跳 过 一 页。PRINT-PAGE 事 件 的 动 作 码 如 下:

----0 不 跳 过 一 页;( 缺 省)

----1 跳 过 一 页。

---- 如 您 打 算 打 印 时 跳 过 一 页, 你 可 以 在 中PRINTPAGE 编 码 如 下:

----//Printpage event

----this.SetActionCode(1)

RetrieveRow

---- 从 数 据 库 服 务 器 中 每 次 接 受 了 一 行 记 录 均 启 动REIRIEVEROW 事 件。 在 这 个 事 件 中, 你 可 以 设 置 一 动 作 码 来 停 止 检 索。 下 面 是RE-TRIEVEROW 事 件 的 有 效 动 作 码:

----0 继 续。( 缺 省)

----1 停 止 检 索。

---- 如 果 一 个DataWindow 将 命 中 很 多 行, 并 且 你 希 望 在 检 索 到 一 定 量 后 停 止。 你 可 以 在RETR IEVEROW 事 件 中 使 用SetActionCode:

----//RetrieveRow event

----//Instance variable Long il_count

----...

----IF il_count++ ＞ 100 THEN

----// Maximum rows retrieved,stip retrieval

----this.SetActionCode(1)

----RETURN

----END IF

---- 当 用 来 给 被 检 索 行 计 数 的 临 时 变 量il_count 的 值 超 过100 时, 检 索 将 停 止。

---- 注 意: 在RETRIEVEROW 事 件 中 存 在 代 码, 那 么 检 索 每 一 行 都 会 触 发 事 件, 这 将 降 低 检 索 的 速 度。

----RetrieveStart

----RETRIEVESTART 事 件 在dw.Retrieve() 函 数 之 后, 产 生SQL 传 送 给 服 务 器 之 前 触 发。

---- 在 一 些 特 定 场 合 可 能 需 要 在 开 始 一 个 检 索 之 前 停 止 它。 在RetrieveStart 事 件 中 的 动 作 码:

----0 继 续。( 缺 省)

----1 不 检 索。

---- 例 如: 我 们 让 一 个 用 户 输 入 检 索 标 准 的 窗 口, 在RETRIEVESTART 中 判 断 返 回 行 数 是 否 太 多 , 以 决 定 停 止 检 索 并 且 让 用 户 缩 小 检 索 范 围。

----//Retrieve Start event

----Int li_count /*Expected Retrieve Count */

----...

----//Get count of Rows to be retrieved

----...

----IF li_count ＞1000 THEN

----MessageBox("Stop","Please narrow your search",stop!)

----This.SetActionCode(1)

----RETURN

----END IF

----...

UpdateStart

---- 这 一 在 执 行Update() 函 数 之 后, 产 生 的 修 改SQL 语 句 传 送 给 服 务 器 之 前 触 发。

---- 通 过 设 置 这 一 动 作 码, 你 可 以 阻 止 修 改 传 送 给 服 务 器。UPDATESTART 事 件 的 动 作 码 如 下:

----0 继 续。( 缺 省)

----1 不 修 改。

---- 如 果 你 要 阻 止 执 行 修 改 语 句, 在UP-DATESTART 事 件 中 使 用 下 列 代 码:

----//UpdateStart event

----...

----this.SetActionCode(1)

----RETURN

----...

综 　 述

---- 在 很 多 情 况 下, 设 置 动 作 码 是 非 常 有 用 的。 这 里 的 例 子, 让 你 对 其 中 几 种 情 况 有 一 个 了 解 。 当 你 对 使 用PowerScript 编 码 有 了 更 多 的 经 验 后, 你 会 发 现SetActionCode 是 非 常 有 用 的。


张健姿-->PowerBuilder系列讲座-->8. 分布式PowerBuilder设计

8. 分布式PowerBuilder设计

----1996 年6 月 推 出 的Power-Builder 5.0, 增 加 了 许 多 新 特 性, 其 中 较 为 引 人 注 目 的 是 分 布 式 PowerBuilder 的 计 算 方 式(Distributing PowerBuilder)。

---- 所 谓 分 布 式 计 算 方 式 和 三 级 系 统 结 构, 是 近 年 来 提 出 的 一 种 新 的 计 算 方 式, 这 就 是 在 传 统 的 客 户 机/ 服 务 器 两 级 结 构 中 再 增 加 一 个 称 作 应 用 服 务 器 的 中 间 级, 用 以 执 行 复 杂 的 商 业 逻 辑 计 算。 传 统 的 两 级 结 构 中 商 业 逻 辑 一 般 是 放 在 客 户 端, 少 量 的 则 以 存 储 过 程 的 形 式 放 在 数 据 库 服 务 器 中。 将 商 业 逻 辑 从 其 他 两 级 中 独 立 出 来, 将 更 适 应 于 日 益 复 杂 化 和 日 益 变 化 的 商 业 逻 辑 的 需 要, 从 而 带 来 了 开 发、 维 护 和 使 用 时 的 便 利。 虽 然 三 级 结 构 在 传 统 的 数 据 库 领 域 还 没 有 被 人 们 广 泛 接 受, 但 在Internet&Intranet 领 域, 人 们 已 开 始 大 量 使 用 这 种 分 布 式 的 计 算 方 式 了: 在Intranet 中, 系 统 至 少 就 是 由 浏 览 器( 客 户 机)、Web 服 务 器( 应 用 服 务 器)、 数 据 库 这 样 的 三 级 结 构 组 成。 而PowerBuilder 支 持 这 种 分 布 式 的 计 算 方 式, 这 使 得 我 们 使 用PowerB uilder 开 发Internet 应 用 成 为 了 可 能。

---- 所 谓 分 布 式PowerBuilder 的 实 现 就 是 在 客 户 端 远 程 地 初 始 化 一 个 在 服 务 器 端 的 用 户 自 定 义 对 象, 并 且 调 用 这 一 对 象 的 属 性 和 函 数。

分 布 式PowerBuilder 中 的 新 概 念

---- 服 务 器 应 用: 在 以 往 的PowerBuilder 版 本 中,Power-Builder 应 用 是 作 为 客 户 机/ 服 务 器 体 系 结 构 中 的 客 户 端 应 用 提 交 的, 在 分 布 式PowerBuilder 中 引 入 了 服 务 器 应 用 这 个 概 念。 这 个 应 用 包 括 了 不 可 视 的 用 户 对 象, 并 被 客 户 端 应 用 调 用, 这 些 用 户 对 象 被 称 作 远 过 程 对 象。 这 种 调 用 可 以 是 跨 进 程 或 跨 硬 件 平 台 的。 分 布 式PowerBuilder 在 服 务 器 端 有 一 个PowerScript 对 象Transport 来 监 听 客 户 端 对 过 程 调 用 的 需 求, 而 这 个 服 务 器 端 的 应 用 也 可 以 同 一 般 传 统 的 客 户 机/ 服 务 器 应 用 的 客 户 端 或 远 程 的 客 户 端 应 用 一 样 来 调 用 另 外 的PowerBuilder 服 务 器 应 用 。

---- 客 户 应 用:PowerBuilder 客 户 应 用 是 用 以 调 用 远 端 对 象 的 部 分。 分 布 式PowerBuilder 应 用 包 括 了 一 个 新 的 对 象, 称 作Connection, 用 以 连 接PowerBuilder 服 务 器 应 用。 当 同 服 务 器 建 立 连 接 后, 客 户 应 用 将 像 调 用 本 地 的 不 可 视 用 户 对 象 一 样 调 用 远 程 对 象。

---- 远 程 对 象: 与 其 他 的 不 可 视 用 户 对 象 类 同。 一 个 远 程 对 象 包 含 了 用PowerScript 编 写 的 商 业 逻 辑, 放 在 服 务 器 端, 作 为 不 可 视 用 户 对 象 的 特 例, 远 程 对 象 可 以 引 用 各 种 不 可 视 的 函 数 和 数 据 库 的 指 令, 如 使 用DataWindow 的 不 可 视 形 式DataStore 来 实 现 对 数 据 库 访 问 的 封 装 等。 远 程 对 象 像 是 一 个 过 程 或 函 数 的 调 用, 并 支 持 参 数 传 递 和 返 回 结 果, 支 持 除 对 象 数 据 类 型 以 外 的 其 他 所 有 类 型。

---- 传 输 对 象: 是 服 务 器 端 用 以 接 收 客 户 请 求 的 特 殊 连 接 对 象。 当 使 用create transport 命 令 创 建 一 个 传 输 实 例 后, 该 对 象 以 对 象 的 属 性 中 所 定 义 的 协 议 监 听 接 收 客 户 的 请 求。 这 个 对 象 的 属 性 包 括 通 讯 驱 动 名 称、 通 讯 协 议 类 型、 超 时 协 议 等, 得 到 和 设 定 这 些 属 性 的 方 法 同Po werBuilder 的 其 他 对 象 如transaction 等 一 样。

---- 连 接 对 象: 是 客 户 端 发 送 请 求 的 客 户 端 的 对 象。 当 使 用create transport 创 建 一 个 连 接 对 象 时, 本 对 象 的Connect To Server 函 数 执 行 同 服 务 器 的 连 接, 通 讯 方 式 如 通 讯 名 称、 协 议 等 由 连 接 对 象 的 属 性 来 决 定。

---- 对 象 存 储 代 理: 一 种 存 储 用 户 对 象 的 新 选 择。 存 储 代 理 保 存 对 象 的 远 程 名 称 并 生 成 内 部 代 码 以 供 远 程 调 用。 当connec-tion 对 象 与 之 建 立 连 接 后, 该 对 象 就 能 够 用 以 远 程 调 用。

---- 新 的PowerBuilder 对 象 是nonvisualobject 和structure 的 继 承 类, 它 们 的 关 系 如 下:

----nonvisualobject

---- connectobject( 新)

---- connection( 新)

---- transport( 新)

---- remoteobject( 新)

----structure

---- connectioninfo( 新)

设 计 一 个 分 布 式PowerBuilder 应 用 举 例

----

一、 服 务 器 端 应 用

---- 首 先 您 需 要 创 建 一 个 服 务 器 端 的 应 用。 目 前 所 有 的 分 布 式PowerBuilder 服 务 器 应 用 都 应 当 运 行 在32 位 平 台 上(Windows NT 或Windows 95)。 这 个 应 用 需 有 三 个 组 成 部 分: 一、 用 户 自 定 义 的 对 象 类; 二、 这 个 对 象 类 在 服 务 器 端 的 代 理; 三、 一 个 传 输 对 象, 这 个 传 输 对 象 并 不 被 调 用, 它 只 是 用 以 监 听 从 网 络 上 的 客 户 一 端 的 调 用。

---- · 创 建 用 户 对 象

---- 我 们 首 先 定 义 一 个 服 务 器 端 的 对 象 类, 它 的 步 骤 如 下:

----1. 点 击 用 户 对 象 图 标。

----2. 点 击new 按 钮。

----3. 在NewUserObject 对 话 框 中 选 择Class 组 中 的Custom 类 型。

----4. 在 进 入 用 户 对 象 的 设 计 中, 选 择Declare|User Object function 菜 单 项。

----5. 点 击new 按 钮。

----6. 函 数 起 名 为"of-my-message"。

----7. 函 数 的 返 回 值 为string 型, 无 参 数 传 递。

----8. 在 函 数 的Script 中 写 入:Return "Hello World"

----9. 关 闭 函 数 画 笔, 将 这 一 用 户 对 象 存 储 为uo-my-object"

---- · 设 置 代 理 对 象

---- 这 个 代 理 对 象 在 客 户 端 当 作 一 个 类 被 引 用, 但 它 首 先 应 在 服 务 器 端 创 建, 创 建 代 理 对 象 的 步 骤 如 下:

----1. 打 开 刚 创 建 的 用 户 对 象uo-my-object。

----2. 在 用 户 区 点 击 鼠 标 右 键。

----3. 在 弹 出 菜 单 中 选 择"set proxy name"。

----4. 在 代 理 名 称 的 输 入 域 输 入"uo-proxy-my-object", 点 击OK 按 钮。

----5. 存 储"uo-my-object" 对 象。

----* 这 时 如 果 您 在Library 画 笔 中 看 当 前 的PBL 库, 您 将 看 到 新 创 建 的 代 理 对 象。

---- · 设 置 传 输 对 象

---- 这 个 对 象 的 工 作 方 式 与PowerBuilder 中 的 事 务 对 象(transaction) 很 相 似, 它 是 同 客 户 端 建 立 连 接 的 结 构。 设 置 过 程 如 下:

----1. 声 明 一 个 全 局 的transport 变 量。 如:

----transport gt-transport

----2. 在application 的Open 事 件 中 写 下:

----gt-transport=Create transport

----gt-transport.driver="namedpipes"

----gt-transport.location="."

----gt-transport.application="my-dpbserv"

----gt-transport.options=""

----gt-transport.Listen()

----

二、 客 户 端 的 应 用

---- 在 客 户 端 的 应 用 有 四 个 组 成 部 分: 一、 得 到 在Server 端 创 建 的 代 理 对 象, 将 其 放 入 客 户 端 的 对 象 库 中; 二、 连 接 服 务 器 端 的 应 用, 这 一 步 骤 即 是 创 建 一 个 连 接 对 象(connect) 的 实 例; 三 、 创 建 代 理 对 象, 设 置 它 的 连 接( 用SetConnec-tions() 连 接 到Connect 对 象 上); 四、 调 用 远 过 程 对 象, 测 试 这 一 连 接。

---- · 将 代 理 对 象 放 入 客 户 端 的 对 象 库 中

---- 这 里 您 有 以 下 两 种 方 法 实 现: 一 是 拷 贝"uo-proxy-my-object" 这 一 代 理 对 象 到 客 户 端 的 应 用 对 象 库 中; 另 一 个 办 法 是 将 服 务 器 端"uo-proxy-my-object" 这 个 对 象 所 在 的 对 象 库 也 放 入 客 户 端 的 搜 索 库 中。

---- · 将 客 户 端 连 接 在 服 务 器 应 用 上: 1. 声 明 一 个 全 局 连 接 对 象:

----connection gc-connection 2. 在 应 用 的Open 事 件 中 创 建 连 接 对 象, 定 义 其 部 分 属 性 并 建 立 同 服 务 器 的 连 接:

----gc-connection=Create connection

----gc-connection.driver="namedpipes"

----gc-connection.location="SERVERNAME"

----gc-connection.application="my-dpbserv"

----gc-connection.options=""

----gc-connection.ConnectToServer()

----* 其 中location 属 性 是 指 服 务 器 应 用 运 行 所 在 的 那 个 计 算 机 的 名 字:application 属 性 是 传 输 对 象 所 在 的 服 务 器 端 应 用 的 名 字。

---- · 创 建 代 理 对 象 实 例, 将 其 连 到 连 接 对 象 上。 1. 声 明 一 个"uo-proxy-my-object" 的 全 局 变 量。( 这 也 正 是 要 将 这 一 代 理 对 象 置 于 客 户 端 应 用 所 搜 索 到 的 路 径 中 的 原 因)

----uo-proxy-my-object gp-uo-proxy-my-object 2. 在 应 用 对 象 的Open 事 件 中 键 入 下 列 代 码:

----gp-uo-proxy-my-object=Create uo-proxy-my-object 3. 调 用 代 理 对 象 的SetConnect() 函 数 传 递 连 接 对 象 的 参 数, 将 代 理 对 象 实 例 连 到 连 接 对 象 上:( 这 一 端 代 码 也 可 在 应 用 的Open 事 件 中 执 行)

----gp-uo-proxy-my-object.SetConnect(gc-connection)

---- · 调 用 远 端 对 象

---- 远 端 对 象 的of-my-message 函 数 返 回 一 个 字 符 串, 所 以 我 们 可 以 在 下 列 程 序 中 调 用 这 一 函 数, 将 其 返 回 值 显 示 出 来。

---- 1. 在 窗 口 中, 设 计 一 个 按 钮, 它 的clicked 事 件 如 下 编 写:

----string ls-retcode

----ls-retcode=iuo-proxy-my-object.of-my-message()

----MessageBox("Message From Remote Object",ls-retcode)

---- 2. 首 先 在 服 务 器 端 运 行 服 务 器 端 的 应 用;

---- 3. 在 客 户 机 上 运 行 客 户 端 的 应 用;

---- 4. 点 击 窗 口 按 钮, 我 们 即 可 得 到 结 果, 显 示 出 一 个 标 有"hello World" 的 消 息 框。


张健姿-->PowerBuilder系列讲座-->9. DataWindow的数据缓冲区

9. DataWindow的数据缓冲区

----

DATAWINDOW 的 四 个 缓 站 区

---- 在PowerBuilder 中,DataWindow 是 用 户 前 端 用 来 存 储、 操 纵 数 据 的 对 象。 在 每 一 个Data Window 对 象 中 有4 个 二 维 表 作 为 数 据 缓 冲 区, 用 来 存 储 查 询 到 的 数 据。 用 户 在DataWindow 中 对 数 据 处 理 系 统 内 部 的 操 作 实 际 上 都 是 将 数 据 在 这 几 个 缓 冲 区 中 进 行 的 修 改 和 移 动, 最 后 在 用 户 提 交 数 据 库 时, 系 统 根 据 这 四 个 缓 冲 区 中 的 信 息 形 成SQL 的IN-SERT,UPDATE,DELETE 等 语 句 。 这 四 个 缓 冲 区 是:

----

Primary Buffer

---- 这 个 缓 冲 区 是 存 放 填 充 窗 口 中DataWindow 控 件 中 数 据 的, 调 用DataWindow 的Retrieve() 函 数 和InsertRow() 函 数 可 以 将 数 据 填 入 这 个 缓 冲 区 中。 当 使 用 有 关DataWindow 删 除 和 过 滤 函 数 时, 相 应 记 录 将 从 这 一 缓 冲 区 中 删 除。 而 在 执 行DataWindow 的Update() 函 数 时,PowerBu ilder 将 查 看 这 一 缓 冲 区 中 的 记 录, 以 形 成SQL INSERT 和UPDATE 语 句。

----

Delete Buffer

---- 这 个 缓 冲 区 保 存 的 是 用DeleteRow() 函 数 从Primary Buffer 中 删 除 的 记 录, 执 行Update( ) 函 数 时, 系 统 根 据 这 一 缓 冲 区 的 记 录 形 成DELETE 语 句。

----

Filter Buffer

---- 这 个 缓 冲 区 存 储 的 是 从Original Buffer 使 用Filter() 函 数 过 滤 到Primary Buffer 中 后 剩 余 的 记 录。

----

Original Buffer

---- 这 一 缓 冲 区 存 储 的 是DataWindow 最 初 执 行retrieve() 函 数 时 得 到 的 全 部 记 录。 当 提 交 数 据 库 时, 根 据Primary Buffer 生 成 的UPDATE 语 句 和 根 据Delete Buffer 生 成 的DELETE 语 句 都 要 依 据 这 一 缓 冲 区 来 构 造 这 些SQL 语 句 中 的Where 子 句。

----Original Buffer 由PowerBuilder 内 部 维 护,Power-Builder 所 提 供 的 任 何 函 数 都 无 法 改 变 它 的 值, 不 过 通 过PowerBuilder 所 提 供 的GetItem … 系 列 的 函 数 可 以 读 出DataWindow 最 初 从 数 据 库 中 查 到 的 原 始 值。 通 过 这 些 函 数 我 们 可 以 编 程 实 现 所 谓 的"Undo" 功 能, 并 且 得 到 在 使 用 乐 观 锁 时 形 成 提 交 数 据 库 的WHERE 子 句。

---- 如 果 您 当 前 使 用 的DataWindow 没 有 设 置 修 改 的 权 力, 您 将 不 能 对Delete 缓 冲 区 和Origin al 缓 冲 进 行 操 作, 而 且 当 调 用Update() 时 也 将 引 起 系 统 错 误。

----

数 据 缓 冲 区 的 状 态 值

----Primary Buffer 和Delete Buffer 都 有 行 级 和 列 级 的 状 态 值, 这 个 状 态 值 是 一 个 枚 举 类 型 。 在 提 交 时 由 该 行 的 状 态 值 来 决 定 是 否 要 产 生SQL 语 句, 其 中Primary Buffer 产 生 的 是IN-SE RT 和UPDATE 语 句, 而Delete Buffer 产 生 的 是DELETE 语 句。 我 们 用GetItemStatus() 函 数 和Se tItemStatus() 函 数 可 以 对 这 一 状 态 值 作 操 纵。 这 一 枚 举 状 态 有 以 下 四 种:

---- ·NotModified! — — — 该 行 或 行 的 值 为 查 询 所 得, 没 有 发 生 改 变。

---- ·DataModified! — — — 该 行 或 列 的 值 为 查 询 所 得, 发 生 了 改 变。

---- ·New! — — — 该 行 或 列 为 一 插 入 的 新 行, 数 据 没 有 发 生 改 变( 数 据 为 空 或 缺 省 值)。

---- ·NewModified! — — — 该 行 或 列 为 一 插 入 的 新 行, 数 据 发 生 改 变。 改 变 是 通 过 用 户 键 盘 输 入 或 调 用 了SetItem() 函 数。

---- 让 我 们 来 看 下 面 这 一 实 例:

---- 我 们 有 这 样 一 张 表, 表 中 有 三 个 字 段, 其 中ITEM 是 主 键。

----ITEM CHAR(5);

----NAME CHAR(20);

----QUANTITY INT。

---- 在Script 中 我 们 查 询 这 张 表 的 记 录, 得 到 以 下 这 些 信 息 存 储 了 在Primary 和Origianal B uffer 中, 其 中 的 行 号 是 缓 冲 区 加 上 的。

---- 在 窗 口 中, 我 们 编 程 过 滤 掉 数 量 为0 的 行, 并 且 加 上 一 个 空 行:

----dw_1.SetFilter("quantity=0")

----dw_1.Filter()

----dw_1.InsertRow()

---- 这 时Primary Buffer 的 状 态 为:

---- 在Filter Buffer 中 的 记 录 为:

---- 用 户 在 新 插 入 行 中 输 入 数 据, 删 除 了 第3 行 数 据, 并 修 改 了 第2 行 数 据。 当 他 离 开 这 个Dat aWindow 时,Primary 和Delete 缓 冲 区 的 状 态 如 下:

---- 这 时 执 行dw_1.update() 函 数, 系 统 将 基 于 这 两 个 缓 冲 区 生 成SQL 语 句。

---- 在Primary Buffer 中, 状 态 为NotModified 和New! 的 行 将 被 忽 略 而 不 产 生SQL 语 句。 状 态 为DataModified 的 行 将 产 生UPDATE 语 句, 状 态 为NewModified 的 行 将 产 生INSERT 语 句, 在Dele te 缓 冲 区 中 的 行 将 产 生DELETE 语 句。

----

四 个 缓 冲 区 在 编 程 中 的 运 用

---- 某 些DataWindow 控 件 的 函 数 有 指 定DataWindow 缓 冲 区 的 功 能。 如 果 缺 省, 则 表 示Primar y 缓 冲 区。 下 列 是 可 以 指 定 缓 冲 区 的 函 数:

---- ·GetItemStatus()

---- ·GetNextModified()

---- ·GetUpdateStatus()

---- ·SetItemStatus()

---- 此 外 还 有GetItem … 系 列 的 函 数, 用 以 查 询DataWindow 中 的 值。 这 些 函 数 有:

---- ·GetItemDate()

---- ·GetItemDataTime()

---- ·GetItemDecimal()

---- ·GetItemNumber()

---- ·GetItemString()

---- ·GetItemTime()

----GetItem … 系 列 函 数 也 可 以 指 定 缓 冲 区, 而 且 可 以 允 许 您 指 定 查 询 的 是 当 前 值 还 是 原 始 值 ( 最 初 从 数 据 库 中 查 到 的 值)。 如:

----dw_1.GetItemString(1,"name",Primary!,TRUE)

---- 使 用DataWindow 的Reset() 函 数 和Retrieve() 函 数 以 及 改 变DataObject 属 性 时, 系 统 将 重 置 这 几 个 缓 冲 区。

---- 当 用 户 插 入 一 个 新 行 时, 编 程 者 往 往 要 自 动 在 新 插 入 行 中 的 某 些 列 中 插 入 一 些 缺 省 的 数 据 值。 这 时, 用 户 并 没 有 对 数 据 作 任 何 修 改。 可 是 在 用 户 要 关 闭 这 一 窗 口 时, 如 果 在CloseQu ery 事 件 中 用ModifiedCount() 函 数 查 看 数 据 是 否 修 改, 得 到 的 结 果 显 然 是 有 所 改 变。 用 户 将 得 到 一 个 提 示 信 息 框:" 是 否 打 算 保 存 所 作 的 修 改? 是/ 否/ 取 消" — — — 这 显 然 是 不 适 合 的: 而 且 如 果 用 户 选 择 的 是 保 存 时, 这 些 原 应 是 空 记 录 的 行 也 成 为 了 有 内 容 的 行, 系 统 将 其 形 成SQL 的INSERT 语 句 提 交 数 据 库, 这 样 的 结 果 更 为 恶 劣。 为 改 变 这 一 状 况, 我 们 可 以 用SetItemStat us() 函 数 将 该 行 的 状 态 改 为New!。 即 当 有 新 行 插 入 并 且 设 置 了 缺 省 值 之 后, 该 行 的 状 态 值 为 New!。 下 面 的 程 序 就 可 以 得 到 如 上 的 功 能。

---- ∥Insert a new row in dw_1

----long 1_Row

----1_Row=dw_1,InsertRow(dw_1,GetRow())

----dw_1,SetItem(1_Row,"discount_pct",0,10)

---- ∥Set th row status to New! so that the CloseQuery check will only detect u ser entries.

----dw_1.SetItemStatus(1_Row,0,Primary!,New!)

---- 使 用SetItemStatus 函 数 时 有 一 些 状 态 转 换 的 限 制, 下 表 中 显 示 的 是 将 原 来 的 状 态 改 变 为 另 一 状 态 时, 会 产 生 的 影 响。 若 表 中 为No, 则 说 明 用 这 一 函 数 进 行 这 样 的 设 置 无 效; 若 表 中 给 出 了 不 同 的 状 态, 则 该 状 态 是 对 您 指 定 的 状 态 的 替 代。

---- 当 一 个 状 态 的 改 变 不 能 允 许 时, 可 以 多 次 调 用SetItemStatus 以 得 到 期 望 的 状 态。 例 如, 从New! 状 态 不 能 改 成NotModified! 状 态, 可 将 其 改 成DataModified! 状 态, 然 后 再 改 成NotMod ified! 状 态。


张健姿-->PowerBuilder系列讲座-->10. 用DataWindow实现对多表的修改

10. 用DataWindow实现对多表的修改

---- 在 开 发 中, 我 们 有 时 会 遇 到 用 一 个DataWindow 来 表 现 数 据 库 中 多 个 表 中 数 据 的 情 形, 这 时DataWindow 用 到 的SQLSelect 语 句 是 一 个 多 张 表 连 接 的 视 图。 当 我 们 对 这 样 一 个DataWindow 中 的 数 据 进 行 修 改 并 提 交 数 据 库 时, 我 们 将 会 发 现 我 们 并 不 能 简 单 地 使 用 一 条dw.update() 来 实 现 同 时 对 数 据 库 中 多 张 表 的 修 改。

---- 事 实 上, 系 统 在DataWindow 中 提 交 数 据 库 并 生 成UPDATE 语 句 时 所 根 据 的 仍 然 是 图 一 这 个 窗 口。 在 以 前 的 章 节 中 我 们 曾 介 绍 过 利 用 这 个 窗 口 中 一 些 不 同 的 选 项 实 现 数 据 库 的 并 发 控 制, 这 里 我 们 再 讲 一 下 这 个 窗 口 中 的 选 项 对 生 成UPDATE 语 句 的 其 它 影 响。

---- 当 我 们 调 用UPDATE 函 数 时, 这 个 窗 口 中 有 四 个 域 将 被 涉 及:

  • Where Clause for Update/Delete where 子 句 的 生 成 依 据。 这 个 选 项 主 要 是 用 来 进 行 并 发 控 制 的, 这 里 不 再 赘 述。
  • UpdateableColumns 可 修 改 的 列。 这 是 一 个 我 们 将 遇 到 麻 烦 的 地 方。PowerBuilder 只 能 同 时 对 一 张 要 修 改 的 表 进 行 管 理, 所 以 我 们 不 能 一 下 子 指 出 所 有 表 中 所 有 要 修 改 的 列。 在 这 里 我 们 只 能 指 出 某 一 张 表 中 要 修 改 的 列, 以 后 在 程 序 中 用Modify 语 句 依 次 指 定 其 它 每 一 张 表 中 要 修 改 的 列。
  • Table to Update 将 要 修 改 的 表。PowerBuilder 只 支 持 一 张 表 的 修 改, 所 以 我 们 也 只 能 在 这 里 只 指 定 其 中 的 某 一 张 表, 其 余 的 表 也 将 要 利 用Modify 语 句 修 改。
  • Unique Key Columns 唯 一 键 的 列。 这 也 是 我 们 要 注 意 的 地 方, 因 为PowerBuilder 生 成 的UPDATE 语 句 只 是 在 要 修 改 的 表 名 中 使 用DATABASE.OWNER.TABLE 这 样 的 全 称, 而 引 用 某 列 时 只 是 简 单 指 出 列 名, 而 不 用DATABASE.OWNER.TABLE.COLUMN 这 样 的 全 称。 所 以, 如 果 我 们 这 里 用 到 的 唯 一 列 标 识 不 属 于 上 面"Table to Update" 中 定 义 的 表 的 列, 这 一 条SQL 语 句 将 出 现 语 法 错 误。

---- PowerBuilder 在DataWindow 中 产 生 的UPDATE 语 句 和DELETE 语 句 如 下:
---- ●UPDATE database.owner.table
---- SET column=somevalue,
---- WHERE UniqueKeyColumn=somevalue;

---- ●DELETE FROM database.owner.table
---- WHERE UniqueKeyColumn=somevalue;

---- 在 一 条SQLUPDATE 和DELETE 语 句 中, 所 有 引 用 的 列 名 均 不 使 用 列 的 全 称, 因 此 对 列 名 的 确 定 完 全 由update table 来 决 定, 在UPDATE 语 句 中 所 有 修 改 属 性 均 设 为TRUE, 而 且 用 户 进 行 过 修 改 的 列 都 将 加 到SET 子 句 中。PowerBuilder 根 据 用 户 对"Where Clause for Update/Delete" 的 选 择 形 成WHERE 子 句, 由 于 系 统 自 动 地 将 多 个 表 中 所 有 的 列 都 加 到SET 子 句 和WHERE 子 句 中, 而PowerBuilder 又 是 根 据 用 户 指 定 的update table 来 确 定 列, 因 此 我 们 显 然 是 无 法 得 到 一 个 准 确 的SQL 语 句 的。 我 们 来 看 这 样 一 个 实 例( 本 表 可 以 在PowerBuilderDEOM 数 据 库 中 得 到, 注 意: 如 果 我 们 希 望 某 一 列 不 被 修 改, 我 们 可 以 在DataWindow 画 笔 中 将TABorder 设 置 为0):

CUSTOMER.Customer-ID=101
CUSTOMER.FirstName=Michaels
CUSTOMER.LastName=Devlin
CUSTOMER.Address=3114 Pioneer Avenue
SALES-ORDER.ID=2001
SALES-ORDER.order-date=09/14/94
我 们 将 其 改 为:
CUSTOMER.Address=1905 Maple Avenue
SALES-ORDER.order-date=12/11/94

---- 如 果 我 们 只 是 用dw.update() 语 句 来 形 成UPDATE 语 句,PowerBuilder 将 自 动 构 造 成 这 样 的 一 条 语 句:

---- 上 述 三 条SQL 语 句 在 提 交 数 据 库 时 都 将 产 生"Invalid column name ‘id' " 的 错 误, 很 明 显,"id" 这 一 个 字 段 并 不 在custumer 这 一 张 表 中。 此 外SALES-ORDER 这 张 表 的 内 容 并 没 有 进 行 修 改。

---- 为 真 正 实 现 对 多 表 数 据 的 修 改, 我 们 可 以 编 写 这 样 一 个 函 数f-MUpdate()。

integer f-MUpdate(a-dw,as-update-table)
a-dw-DataWindow 名 称;as-update-table- 将 要 修 改 的 表 名 的 全 称。
Long ll-ipos,ll-maxcol,i
String ls-colnam,ls-mod,ls-table-name,ls-err,ls-updatekeyll,ls-qualifier=""
// 从 传 递 的 参 数 中 分 离 出 表 名 和 域 名
ll-ipos=Pos(as-update-table," ·",l)
If(ll-ipos ＞0)Then
ls-qualifier =Left(as-update-table,ll-ipos)
as-update-table=Mid(as-update-table,& ll-ipos+1)
ll-ipos=Pos(as-update-table," ·",l)
If(ll-ipos ＞0)Then
ls-qualifier=ls-qualifier+&
Left(as-update-table,ll-ipos)
as-update-table=Mid(as-update-table,& ll-ipos+1)
end If
End If
// 得 到 这 个DataWindow 包 含 列 的 数 量

ls-err=a-dw.object.DataWindow.Column.Count
ll-maxcol=Integer(Is-err)
If(ll-maxcol ＜1)Then
Return 1

End IF

---- // 确 认DataWindow 中 的 每 一 列 是 否 属 于 要 修 改 的 表 中 的 列, 如 是 将 其update 属 性 设 为 Yes, 否 则 为No.

FOR i=1 To ll-maxcol

ls-mod="#"+String(i)+".Name"
ls-colnam=a-dw.Descibe(ls-mod)
ls-table-name=a-dw.Descibe(ls-colnam+&".dbName")
ls-table-name=Left(ls-table-name,&)
(Pos(ls-table-name," ·",l)-l))

If(Upper(as-update-table)=Upper(ls-table-name))Then
ls-mod=ls-colnam+".Update=Yes"
Else
// 如 果 该 列 不 在 可 修 改 的 表 中,
// 而 其 为 主 键 的 属 性 为TRUE
// 将 其 列 名 存 在ls-updatekey[ ] 中
// 并 将 其 主 键 的 属 性 设 为FALSE。
// 由 于PB 不 保 留 列 的 全 名, 我 们 不 能。
// 保 留 非 修 改 表 的 键 的 名 称。
// 所 以 我 们 将 其 保 留 在 数 组 之 中, 以 便 其 恢 复
If(Upper(a-dw.Descibe(ls-colnam+".Key"))=" YES")Then
ls-updatekey[UpperBound(ls-updatekey[ ])+1]=ls-colnam
ls-mod=ls-colnam+".Key=No"
If(a-dw.Modify(ls-mod) ＜ ＞"")Then
Return-2
End If
End If
ls-mod=ls-colnam+".Update=No"
End IF

// 确 认 修 改 是 否 成 功
If(a-dw.Modify(ls-mod) ＜ ＞"")Then
Return-2
End IF

NEXT

// 将 修 改 表 名 设 置 为 全 称
a-dw.object.Data Window.Table.UpdateTable=ls-qualifier+as-update-table

// 修 改DataWindow

If(a-dw.Update(TRUE,FALSE) ＜ ＞1)Then
Return-5
Else
// 将DataWindow 恢 复 为 其 原 来 的 状 态
ll-maxcol=UpperBound(ls-updatekey)
FOR i=l to ll-maxcol
ls-mod=ls-updatekey[i]+".key=Yes"
If(a-dw.Modify(ls-mod) ＜ ＞"")Then
Return-6
End If
Return 0
End If

---- 调 用 这 一 函 数 时 应 遵 守 以 下 规 则:

  • 保 证 数 据 的 参 照 完 整 性, 应 当 先 修 改 父 表 后 修 改 子 表;
  • 在DataWindow 画 笔 中 为 每 张 表 建 立 唯 一 主 键;
  • 在 图 一 窗 口 中, 将 该DataWindow 设 为 允 许 修 改;
  • 指 定 所 涉 及 的 任 意 一 张 表 为 修 改 表, 表 中 的 任 一 列 为 可 修 改 列。 以 保 证DataWindow 有 修 改 权, 这 样 我 们 调 用 的 函 数 就 可 以 覆 盖 这 一 设 置。
  • 不 要 将 并 发 控 制 选 为key and modified columns, 这 个 函 数 无 法 在 这 一 条 件 下 工 作。
  • 将 表 名 的 全 称DATABASE.OWNER.TABLE 传 递 给 这 个 函 数, 特 别 是 在 这 个DataWindows 是 对 多 个 数 据 库 进 行 操 作 时。

---- 在 上 例 中, 我 们 可 以 这 样 调 用 这 个 函 数

f-MUpdate(dw-l," dbo.sales-order")
f-MUpdate(dw-l," dbo.customer")


张健姿-->PowerBuilder系列讲座-->11. 游标的使用

11. 游标的使用

---- 提 到 游 标 这 个 词, 人 们 想 到 的 是 在 屏 幕 上 一 个 闪 动 的 方 框, 用 以 指 示 用 户 将 要 输 入 字 符 的 位 置。 而 在 关 系 型 数 据 库 的SQL 语 言 中, 游 标 却 有 另 外 的 含 义, 它 是 存 放 结 果 集 的 数 据 对 象。

为 什 么 要 用 到 游 标

---- 在 某 些PowerBuilder 应 用 程 序 的 开 发 中, 您 可 能 根 本 用 不 到 游 标 这 样 一 个 对 象。 因 为 在 其 它 工 具 开 发 中 很 多 需 用 游 标 实 现 的 工 作, 在PowerBuilder 中 却 已 有DataWin-dow 来 代 劳 了。 事 实 上,DataWindow 不 仅 可 以 替 代 游 标 进 行 从 后 台 数 据 库 查 询 多 条 记 录 的 复 杂 操 作, 而 且 还 远 不 止 这 些。 但 是 同DataWindow 和DataStore 相 比, 游 标 也 有 其 自 身 的 优 点, 比 如 系 统 资 源 占 用 少, 操 作 灵 活, 可 根 据 需 要 定 义 变 量 类 型 如 全 局、 实 例 或 局 部 类 型 和 访 问 类 型 如 私 有 或 公 共 等 。

游 标 的 操 作

---- 使 用 游 标 有 四 种 基 本 的 步 骤: 声 明 游 标、 打 开 游 标、 提 取 数 据、 关 闭 游 标。

----

声 明 游 标

---- 象 使 用 其 它 类 型 的 变 量 一 样, 使 用 一 个 游 标 之 前, 首 先 应 当 声 明 它。 游 标 的 声 明 包 括 两 个 部 分: 游 标 的 名 称; 这 个 游 标 所 用 到 的SQL 语 句。 如 要 声 明 一 个 叫 作Cus-tomerCursor 的 游 标 用 以 查 询 地 址 在 北 京 的 客 户 的 姓 名、 帐 号 及 其 余 额, 您 可 以 编 写 如 下 代 码:

----DECLARE CustomerCursor CURSOR FOR

----SELECT acct_no,name,balance

----FROM customer

----WHERE province=" 北 京";

---- 在 游 标 的 声 明 中 有 一 点 值 得 注 意 的 是, 如 同 其 它 变 量 的 声 明 一 样, 声 明 游 标 的 这 一 段 代 码 行 是 不 执 行 的, 您 不 能 将debug 时 的 断 点 设 在 这 一 代 码 行 上, 也 不 能 用IF...END IF 语 句 来 声 明 两 个 同 名 的 游 标, 如 下 列 的 代 码 就 是 错 误 的。

----IF Is_prov=" 北 京"THEN

----DECLARE CustomerCursor CURSOR FOR

----SELECT acct_no,name,balance

----FROM customer

----WHERE province=" 北 京";

----ELSE

----DECLARE CustomerCursor CURSOR FOR

----SELECT acct_no,name,balance

----FROM customer

----WHERE province〈〉" 北 京";

----END IF

----

打 开 游 标

---- 声 明 了 游 标 后 在 作 其 它 操 作 之 前, 必 须 打 开 它。 打 开 游 标 是 执 行 与 其 相 关 的 一 段SQL 语 句 , 例 如 打 开 上 例 声 明 的 一 个 游 标, 我 们 只 需 键 入:

----OPEN CustomerCursor;

---- 由 于 打 开 游 标 是 对 数 据 库 进 行 一 些SQL SELECT 的 操 作, 它 将 耗 费 一 段 时 间, 主 要 取 决 于 您 使 用 的 系 统 性 能 和 这 条 语 句 的 复 杂 程 度。 如 果 执 行 的 时 间 较 长, 可 以 考 虑 将 屏 幕 上 显 示 的 鼠 标 改 为hourglass。

---- 提 取 数 据

---- 当 用OPEN 语 句 打 开 了 游 标 并 在 数 据 库 中 执 行 了 查 询 后, 您 不 能 立 即 利 用 在 查 询 结 果 集 中 的 数 据。 您 必 须 用FETCH 语 句 来 取 得 数 据。 一 条FETCH 语 句 一 次 可 以 将 一 条 记 录 放 入 程 序 员 指 定 的 变 量 中。 事 实 上,FETCH 语 句 是 游 标 使 用 的 核 心。 在DataWindow 和DataStore 中, 执 行 了R etrieve() 函 数 以 后, 查 询 的 所 有 结 果 全 部 可 以 得 到; 而 使 用 游 标, 我 们 只 能 逐 条 记 录 地 得 到 查 询 结 果。

---- 已 经 声 明 并 打 开 一 个 游 标 后, 我 们 就 可 以 将 数 据 放 入 任 意 的 变 量 中。 在FETCH 语 句 中 您 可 以 指 定 游 标 的 名 称 和 目 标 变 量 的 名 称。 如 下 例:

----FETCH CustmerCur-sor

----INTO:ls_acct_no,

----:ls_name,

----:ll_balance;

---- 从 语 法 上 讲, 上 面 所 述 的 就 是 一 条 合 法 的 取 数 据 的 语 句, 但 是 一 般 我 们 使 用 游 标 却 还 应 当 包 括 其 它 的 部 分。 正 如 我 们 前 面 所 谈 到 的, 游 标 只 能 一 次 从 后 台 数 据 库 中 取 一 条 记 录, 而 在 多 数 情 况 下, 我 们 所 想 要 作 的 是 在 数 据 库 中 从 第 一 条 记 录 开 始 提 取, 一 直 到 结 束。 所 以 我 们 一 般 要 将 游 标 提 取 数 据 的 语 句 放 在 一 个 循 环 体 内, 直 至 将 结 果 集 中 的 全 部 数 据 提 取 后, 跳 出 循 环 圈 。 通 过 检 测SQLCA.SQL-CODE 的 值, 可 以 得 知 最 后 一 条FETCH 语 句 是 否 成 功。 一 般, 当SQLCODE 值 为0 时 表 明 一 切 正 常,100 表 示 已 经 取 到 了 结 果 集 的 末 尾, 而 其 它 值 均 表 明 操 作 出 了 问 题, 这 样 我 们 可 以 编 写 以 下 的 代 码:

----lb_continue=True

----ll_total=0

----DO WHILE lb_continue

----FETCH CustomerCur-sor

----INTO:ls_acct_no,

----:ls_name,

----:ll_balance;

----If sqlca.sqlcode=0 Then

----ll_total+=ll_balance

----Else

----lb_continue=False

----End If

----LOOP

---- 循 环 体 的 结 构 有 多 种, 这 里 提 到 的 是 最 常 见 的 一 种。 也 有 的 程 序 员 喜 爱 将 一 条FETCH 语 句 放 在 循 环 体 的 前 面, 循 环 体 内 再 放 置 另 外 一 条FETCH 语 句, 并 检 测SQLCA.SQLCODE 是 否 为100。 但 是 这 样 做, 维 护 时 需 同 时 修 改 两 条FETCH 语 句, 稍 麻 烦 了 些。

----

关 闭 游 标

---- 在 游 标 操 作 的 最 后 请 不 要 忘 记 关 闭 游 标, 这 是 一 个 好 的 编 程 习 惯, 以 使 系 统 释 放 游 标 占 用 的 资 源。 关 闭 游 标 的 语 句 很 简 单:

----CLOSE CustomerCursor;

----

使 用Where 子 句 子

---- 我 们 可 以 动 态 地 定 义 游 标 中 的Where 子 句 的 参 数, 例 如 在 本 例 中 我 们 是 直 接 定 义 了 查 询 省 份 是 北 京 的 记 录, 但 也 许 在 应 用 中 我 们 要 使 用 一 个 下 拉 式 列 表 框, 由 用 户 来 选 择 要 查 询 的 省 份 , 我 们 该 怎 样 做 呢?

---- 我 们 在 前 面 曾 经 提 到 过,DECLARE 语 句 的 作 用 只 是 定 义 一 个 游 标, 在OPEN 语 句 中 这 个 游 标 才 会 真 正 地 被 执 行。 了 解 了 这 些, 我 们 就 可 以 很 方 便 地 实 现 这 样 的 功 能, 在DECLARE 的Where 子 句 中 加 入 变 量 作 参 数, 如 下 所 示:

----DECLARE CustomerCursor CURSOR FOR

----SELCECT acct_no,name,balance

----FROM customer

----WHERE province=:ls_province;

---- ∥ 定 义ls_province 的 值

----OPEN CustomerCursor;

----

游 标 的 类 型

---- 同 其 它 变 量 一 样, 我 们 也 可 以 定 义 游 标 的 访 问 类 型: 全 局、 共 享、 实 例 或 局 部, 游 标 变 量 的 命 名 规 范 建 议 也 同 其 它 变 量 一 样。

游 标 的 高 级 技 巧

---- 尽 管 目 前 基 于SQL 语 句 的 后 台 数 据 库 所 支 持 的 语 言 都 大 致 相 当, 但 对 游 标 的 支 持 却 有 着 一 些 差 异, 例 如 对 滚 动 游 标 支 持。 所 谓 滚 动 游 标, 就 是 程 序 员 可 以 指 定 游 标 向 前 后 任 意 一 个 方 向 滚 动。 如 在Informix 中, 您 甚 至 还 可 以 将 游 标 滚 向 结 果 集 开 头 或 末 尾, 使 用 的 语 句 分 别 是FET CH FIRST,FETCH LAST、FETCH PRIOR 和FETCH NEXT。 当 程 序 员 用FETCH 语 句, 其 缺 省 是 指FET CH NEXT。 由 于 滚 动 是 在 数 据 库 后 台 实 现 的, 所 以 滚 动 游 标 为 用 户 编 程 提 供 了 极 大 的 方 便。

---- 对 游 标 支 持 的 另 一 个 不 同 是 可 修 改 游 标。 上 述 游 标 的 使 用 都 是 指 只 读 游 标, 而 象Oracle 、Sybase 等 数 据 库 却 另 外 支 持 可 作 修 改 的 游 标。 使 用 这 样 的 数 据 库, 您 可 以 修 改 或 删 除 当 前 游 标 所 在 的 行。 例 如 修 改 当 前 游 标 所 在 行 的 用 户 的 余 额, 我 们 可 以 如 下 操 作:

----UPDATE customer

----SET balance=1000

----WHERE CURRENT of customerCursor;

---- 删 除 当 前 行 的 操 作 如 下:

----DELETE FROM Customer

----WHERE CURRENT OF CustomerCursor;

---- 但 是 如 果 您 当 前 使 用 的 数 据 库 是Sybase, 您 需 要 修 改 数 据 库 的 参 数, 将 游 标 可 修 改 的 值 定 为1, 才 能 执 行 上 述 操 作。 这 一 赋 值 在 连 接 数 据 库 的 前 后 进 行 均 可。

----SQLCA.DBParm="Cursor Update=1"

---- 另 外 一 个 内 容 是 动 态 游 标, 也 就 是 说 您 可 以 运 行 过 程 中 动 态 地 形 成 游 标 的SELECT 语 句。 这 同 在PowerBuilder 中 动 态 地 使 用 嵌 入 式SQL 一 样, 需 要 用 到DynamicStagin-gArea 等 数 据 类 型, 这 已 超 出 了 本 节 的 范 围。


张健姿-->PowerBuilder系列讲座-->12. 预装入对象

12. 预装入对象

---- 在PowerBuilder 5.0 中 支 持 全 编 译 代 码, 但 用 于 采 用 这 种 方 法 存 在 编 译 时 间 长、 编 译 后 可 执 行 文 件 量 大 等 缺 点, 所 以 在 不 少 场 合, 我 们 仍 采 用 伪 编 译 方 式, 就 是 将 装 载 对 象 和 源 代 码 的PBL 文 件 编 译 成PowerBuilder 的 动 态 链 接 库(.PBD)。PowerBuilder 的 动 态 链 接 库 中 装 载 的 是 与 源 程 序 库 中 源 代 码 相 匹 配 的 二 进 制 表 示。 在 运 行 时, 对 象( 包 括 函 数) 依 照" 需 要 时 调 用" 的 原 则, 从.PBD 中 装 入 内 存, 这 就 使 可 执 行 程 序 的 字 节 数 大 大 缩 小, 执 行 效 率 会 提 高, 而 且 由 于 只 有 那 些 要 用 到 的 对 象 才 被 即 时 装 入 内 存, 使 系 统 需 要 进 行 内 存 交 换 的 机 会 就 少 得 多, 因 而 应 用 的 运 行 也 就 更 快。

---- 但 有 时 我 们 也 会 发 现 用 户 在 进 行 打 开 窗 口 等 操 作 时 系 统 的 响 应 速 度 较 慢, 特 别 是 在 客 户 机 的 配 置 较 低 的 情 况 下, 这 种 情 形 尤 为 突 出。 我 们 知 道, 当 程 序 调 用 一 个 新 的 对 象 时, 系 统 要 到 各 个.PBD 文 件 中 查 找 这 个 对 象, 如 果 这 个 对 象 是 由 其 它 对 象 继 承 而 来, 那 么 其 所 有 的 祖 先 对 象 都 需 要 装 入 内 存。 如 应 用 软 件 十 分 庞 大, 这 种 查 找 和 装 入 显 然 是 十 分 消 耗 时 间 的。

---- 这 里 我 们 介 绍 一 种 预 装 入 对 象 的 办 法, 可 以 在 一 定 程 度 上 解 决 这 个 问 题。 预 装 入 对 象 就 是 改 变 对 象 初 始 装 入 的 时 间, 也 就 是 说 在 用 户 对 应 用 并 没 有 反 应 速 度 的 要 求 时 装 入 了 对 象, 而 不 是 在 用 户 需 要 该 对 象 时 才 装 入( 如 打 开 一 个 窗 口 时)。 预 装 入 对 象 可 以 明 显 地 提 高 性 能, 这 种 技 术 给 用 户 的 印 象 是 在 用 户 真 正 要 求 系 统 的 响 应 速 度 时 所 有 对 象 的 装 入 都 加 快 了。 最 适 合 做 这 些 预 装 入 的 地 方 是 在APPLICATION 的OPEN 事 件 中。 在Pow-erBuilder 5.0 开 发 工 具 中 并 没 有 直 接 提 供 这 样 的 功 能, 但 我 们 可 以 用 一 些 简 单 的 技 巧 来 实 现 它, 这 就 是 使 用 一 个 非 可 视 化 对 象。

---- 我 们 首 先 应 当 知 道: 一 些 对 象, 如 非 可 视 化 对 象 等, 被 调 用 时 全 部 装 入 内 存, 而 另 一 些 对 象 , 如 函 数, 只 装 入 需 要 的 部 分。 因 此 使 用 一 个 非 可 视 化 对 象 可 能 比 使 用 一 个 全 局 函 数 更 快, 当 然 还 与 它 的 大 小 和 功 能 有 关。

---- 您 不 必 在 应 用 的OPEN 事 件 中 预 装 入 所 有 的 对 象, 而 是 预 装 入 那 些 最 经 常 被 用 作 为 祖 先 的 对 象。 具 体 的 做 法 是 将 这 个 非 可 视 化 对 象 定 义 成 一 个 变 量。 这 个 非 可 视 化 对 象 一 直 保 留 在 内 存 中 直 到 应 用 结 束, 这 可 使 应 用 更 加 紧 凑。 这 样 做 的 另 一 个 好 处 是, 一 旦 您 定 义 了 一 个 对 象 的 指 针 作 为 全 局 变 量, 就 可 以 在 软 件 　 的 任 何 地 方 利 用 这 个 指 针, 引 用 这 个 对 象 的 常 量、 函 数 和 其 它 的 特 性。

---- 具 体 的 步 骤 可 以 这 样 进 行:

步 骤 一: 创 建 一 个 预 装 入 的 对 象

---- 创 建 一 个Non_Visual_Object 类 型 的 用 户 对 象, 将 该 对 象 以 您 选 定 的 名 字 存 盘。 在 本 例 中 采 用NVO_Object_Pre_Loader.

---- 在 该 对 象 中, 建 立 一 个Powerobject 的 对 象 数 组 叫 做IPO_Pre_Loaded_objects[]。Power object 对 象 是Pow-erBuilder 对 象 层 次 中 最 高 层 次 的 对 象( 见 前 文《PowerBuilder 面 向 对 象 的 程 序 设 计》), 因 此 它 能 被 分 配 给 任 何 一 个PowerBuilder 标 准 的 或 自 定 义 对 象。 另 外 建 立 一 个 整 型 的 变 量 做 为 数 组 的 索 引, 我 们 叫 它 为ii_Idx, 并 且 将 它 初 始 化 为0。 这 两 个 变 量 均 为 实 例 变 量。

----PRIVATE:/* 限 制 对 这 两 个 变 量 的 访 问 权 限*/

----/* 我 们 假 设 预 装 入 的 对 象 数 最 多 不 超 过10, 当 然 开 发 者 也 可 根 据 实 际 情 况 调 整*/

----PowerObject IPO_Pre_Loaded_objects[10]

----Integer ii_Idx=0

---- 注 意, 我 们 应 当 在 这 里 就 预 先 定 义 数 组 的 大 小, 这 样 可 以 使 这 个 对 象 预 先 保 留 内 存 而 且 在 自 身 预 装 入 时 也 能 运 行 得 更 快。

步 骤 二: 创 建 预 装 入 函 数

---- 现 在 创 建 一 个 用 户 对 象 函 数 叫 做NVOF_Pre_Load_Object。 这 个 函 数 有 一 个 参 数:APO_Ob ject, 它 也 是Powerobject 类 型。 函 数 的 代 码 如 下。

----/*

---- 函 数:NVOF_Pre_Load_Object

---- 功 能:To pre_load often used ancestor objects

---- 参 数:Power Object APO_Object

---- 返 回 值:integer

----1: 成 功,-1: 失 败

----*/

----ii_Idx++

----/* 将 要 预 装 入 的 对 象 赋 值 给 这 个 数 组*/

----IPO_Pre_Loaded_Objects[ii_Idx]=APO_Object

----/* 检 验 赋 值 是 否 成 功*/

----If isvalid(IPO_Pre_Loaded_objects[ii_Idx])then

----Return 1

----else

----Return -1

----end if

步 骤 三: 创 建 可 以 放 入 调 用 用 户 对 象 代 码 的 用 户 事 件

---- 在 这 个 对 象 上 为 开 发 者 建 立 一 个 可 放 入 对 预 装 入 对 象 调 用 的 代 码。 我 们 定 义 了 使 用 一 个 " 声 明" 的 事 件, 该 事 件 将 在 对 象 的CONSTRUCTOR 事 件 中 被 触 发。 我 们 可 以 将 这 个 用 户 事 件 命 名 为NVO_UE_DECLARATIONS, 并 且 将 下 面 的 代 码 放 入 对 象 的CONSTRUCTOR 事 件 中。

----This.Post Event("nvo_ue_declarations")

---- 在NVO_UE_DECLARATION 事 件 中 您 可 以 放 入 对 预 装 入 对 象 的 调 用。

步 骤 四: 预 装 入 对 象

---- 这 段 代 码 存 在NVO_UE_DECLARATION 事 件 中。 可 能 您 的 应 用 需 要 更 多 或 更 少 的 预 装 入 对 象 , 我 们 只 假 设 有 这 样 几 个 常 用 的 祖 先 对 象 类w_WindowBase、udw_DataWindow、uo_UserObjec tBase 和m_MenuBase。 这 些 对 象 分 别 代 表 我 们 的 窗 口 类、 用 户 对 象 数 据 窗 口 类、 用 户 对 象 类 和 菜 单 类 的 祖 先 对 象。

----NVO_UE_DECLARATON 事 件 中 的 代 码 如 下:

----/* 声 明 指 向 这 些 对 象 的 局 部 变 量*/

----Window lWindow

----UserObject lUO

----DataWindow lDW

----Menu lMenu

----/* 对 每 一 个 需 要 预 装 入 的 对 象, 使 用Create 语 句 创 建 一 个 该 对 象 的 实 例, 并 调 用 预 装 入 函 数 将 这 个 实 例 保 存 在 内 存 中*/

----/* 创 建 一 个 窗 口 基 类 的 实 例*/

----lWindow=Create w_WindowsBase

----NVOF_Pre_Load_Object(lWindow)

----/* 创 建 一 个DataW-indow 的 用 户 对 象 基 类 的 实 例*/

----lDW=Create udw_DataWindowBase

----NVOF_Pre_Load_object(lDW)

----/* 创 建 一 个 用 户 对 象 的 基 类 实 例*/

----lUO=uo_UserObjectBase

----NVOF_Pre_Load_Object(lUO)

----/* 创 建 一 个 菜 单 实 例*/

----lMenu=Create m_MenuBase

----NVOF_Pre_Load_Object(lMenu)

---- 一 般 来 讲, 动 态 调 用 的 对 象, 都 是 那 些 用 字 符 串 变 量 调 用 的 对 象, 将 不 包 含 在.EXE 文 件 中 。 例 如, 如 果 您 用Open(mywin,"My_Window") 打 开 了 一 个 窗 口,My_Window 窗 口 并 不 自 动 地 包 含 在 您 的EXE 文 件 中。 如 果 编 译 成.PBD 文 件, 则 会 包 含 在.PBD 当 中。 但 是 如 果 您 用 这 种 方 法 预 装 入 这 个 对 象, 该 窗 口 在.PBD 和.EXE 编 译 技 术 下 都 将 被 包 含。

步 骤 五: 初 始 化 预 装 入 对 象

---- 为 了 使 对 象 进 入 内 存, 还 剩 下 最 后 的 一 步。 我 们 必 须 将 预 装 入 对 象 本 身 装 入 内 存。 您 可 以 选 择 在 应 用 频 繁 使 用 部 分 开 始 工 作 前 的 任 何 地 方 做 这 件 事。 一 般 常 用 的 是 在APPLICA-TIO N 的OPEN 事 件 中。 需 要 的 代 码 如 下:

----anvo_object_pre_loader=Create NVO_Object_Pre_loader.

---- 这 段 程 序 将 建 立 这 个 非 可 视 化 对 象, 该 可 视 化 对 象 就 会 激 发 对 象 的 声 明 事 件, 从 而 预 装 入 各 个 对 象。

---- 最 后 一 点 需 提 请 注 意 的 是, 不 要 忘 记 在 您 的APPLI-CATION 的CLOSE 事 件 中 加 上DESTROY a nvo_object_pre_loader 这 样 一 条 语 句, 否 则 这 个 应 用 将 造 成 所 谓 的 内 存 漏 洞(Memory Leak) 。


张健姿-->PowerBuilder系列讲座-->13. 资源管理

13. 资源管理

内 存 漏 洞

我 们 在 上 期 的《 预 装 入 对 象》 一 文 中 提 到 了 关 于 内 存 漏 洞（MemoryLeak） 的 问 题。 内 存 漏 洞 是 程 序 员 和 用 户 都 很 关 心 的 问 题。 一 个 对 象 被 装 入 并 且 分 配 了 内 存， 而 在 对 象 被 关 闭 却 没 有 释 放 分 配 的 内 存， 这 时 一 个 内 存 漏 洞 就 产 生 了。 在 开 发 工 具 中 也 会 有 内 存 漏 洞， 这 是 我 们 无 法 控 制 的， 但 是 我 们 必 须 注 意 我 们 自 己 的 代 码 所 造 成 的 内 存 漏 洞。

最 有 可 能 造 成 内 存 漏 洞 的 是 使 用CREATE 语 句 创 建 一 个 对 象 后 没 有 对 应 的DESTROY 语 句 消 除。 无 论 何 时， 您 用CREATE 函 数 创 建 了 一 个 对 象， 您 就 必 须 负 责 在 该 对 象 执 行 完 成 之 后 释 放 分 配 的 内 存。

例： 我 创 建 了 我 自 己 的 一 个transaction 对 象：
TransactionMy_Transaction
My_transaction=CREATETransaction
当 我 用 完 这 个 对 象 后， 我 应 该 清 除 该 对 象：
DESTROYMyTransaction
（ 注：SQLCA 是 由 您 的 应 用 自 动 创 建 同 时 也 自 动 关 闭。）

内 存 漏 洞 的 出 现 经 常 是 与 开 发 者 使 用 了 非 可 视 化 用 户 对 象 有 关。 因 为 这 种 对 象 只 能 用CREATE 语 句 创 建 它 的 一 个 实 例， 因 此 如 不 对 其 使 用DESTROY 语 句 消 除， 则 必 然 导 致 错 误。 另 外 一 个 有 关 的 是 用OpenUserObject 或OpenUserObjectWithParm 函 数 创 建 的 动 态 用 户 对 象， 这 些 对 象 同 样 要 求 您 在 结 束 使 用 它 们 时 用 相 应 的CloseUserObject 函 数。

我 们 知 道，PowerBuilder 能 够 自 动 清 除 放 在 一 个 窗 口 中 的 常 规 对 象， 但 它 为 什 么 不 能 在 应 用 结 束 时 自 动 清 除 用 户 自 己 创 建 的 对 象 呢 ？ 因 为PowerBuilder 仅 能 清 除 控 件 列 表 中 的 对 象， 而 且 只 有 一 个 对 象（ 如 窗 口） 及 其 表 面 的 那 些 对 象 才 会 列 在 控 件 列 表 当 中（ 包 括 不 可 见 的 对 象）。 而 动 态 的 用 户 对 象 和 非 可 视 化 对 象， 与PowerBuilder 全 局 对 象 的 实 例（transactions、error、message 等 等） 一 样， 是 在 对 象 的 控 制 列 表 已 经 创 建 后 加 到 对 象 上 的。 关 闭 父 对 象 时， 系 统 并 不 知 道 要 清 除 这 些 动 态 加 入 的 对 象。 如 果 您 没 有 手 工 消 除 它 们， 它 们 将 一 直 保 持 打 开 状 态， 并 常 驻 在 内 存 中， 直 到 使 用 工 具 来 清 除， 或 者 关 闭Windows 系 统。 使 用 象Windows3.1ResourceKit 中 提 供 的 内 存 资 源 监 控 器 这 样 的 工 具， 能 使 您 在 测 试 过 程 中 检 查 资 源 以 确 定 资 源 按 照 预 想 的 那 样 被 释 放。

内 存 管 理

在Windows3.x 平 台 上， 开 发 人 员 编 译 时 会 遇 到 这 样 的 问 题： 机 器 里 有32M 内 存， 而 且 只 有 两 个 应 用 在 运 行， 可 是PowerBuilder 却 总 是 提 示 内 存 不 足（OutofMemory）。 而 用 户 在 使 用 某 应 用 软 件 时， 也 会 同 样 出 现 内 存 不 足 的 问 题， 于 是 用 户 只 得 关 闭 其 它 的 应 用， 直 到 发 现 关 闭 了 某 一 程 序 释 放 了 足 够 的 内 存 空 间 可 供PowerBuilder 运 行 起 来 为 止。 于 是 用 户 开 始 抱 怨 开 发 者， 而 开 发 者 开 始 抱 怨PowerBuilder。

其 实 在 多 数 情 况 下， 他 们 都 不 应 该 受 责 备， 问 题 的 根 源 出 在Windows 本 身。 我 们 知 道， 从 严 格 意 义 上 讲，Windows 并 不 是 一 个 操 作 系 统， 它 只 是 一 个DOS 应 用 程 序， 它 仍 然 要 求 固 定 的 程 序 空 间。 这 些 空 间 分 布 在1M 以 下 的 上 位 内 存 中。 如 果 您 使 用 带 参 数/C 的DOS 命 令MEM， 您 将 会 看 到 类 似 以 下 的 显 示。

Modulesusingmemorybelow1MB:

Name	Total	=Conventional	+UpperMemory
--------	----------	---------	--------
MSDOS	17,533(17K)	17,533(17K)	0(0K)
HIMEM	1,120(1K)	1,120(1K)	0(0K)
EMM386	4,144(4K)	4,144(4K)	0(0K)
POWER	80(0K)	80(0K)	0(0K)
COMMAND	3,888(4K)	3,888(4K)	0(0K)
win386	44,816(44K)	2,384(2K)	42,432(41K)
NAVTSR	7,984(8K)	7,984(8K)	0(0K)
MOUSE	25,328(25K)	272(0K)	25,056(24K)
SHARE	26,368(26K)	26,368(26K)	0(0K)
DOSKEY	4,144(4K)	4,144(4K)	0(0K)
WIN	1,760(2K)	1,760(2K)	0(0K)
COMMAND	4,048(4K)	4,048(4K)	0(0K)
POWER	4,672(5K)	0(0K)	4,672(5K)
SMARTDRV	29,024(28K)	0(0K)	29,024(28K)
Free	581,312(568K)	581,312(568K)	0(0K)

MemorySummary:

TypeofMemory	Total	=Used	+Free
-----------	----------	----------	----------
Conventional	655,360	74,048	581,312
Upper	101,184	101,184	0
Reserved	393,216	393,216	0
Extended(XMS)	19,821,760	18,773,184	1,048,576
----------	----------	----------	----------
Totalmemory	20,971,520	19,341,632	1,629,888
Totalunder1MB	756,544	175,232	581,312

Largestexecutableprogramsize581,296(568K)
Largestfreeuppermemoryblock0(0K)
MS-DOSisresidentinthehighmemoryarea.

尽 管 在DOS 内 存 限 制640K 和1M 上 位 内 存 块 之 间 有360K（ 约384,000 字 节） 的 可 用 内 存， 但 在 显 示 中，“ 上 位” 内 存 和“ 保 留” 内 存 之 间 却 有 近500K 可 用。“ 保 留” 内 存 一 般 用 来 装 载 网 络 软 件 和 其 它 的 驱 动， 在"Win386" 一 行 也 显 示 还 有42K 可 用， 这 是 被 那 些 需 要 固 定 地 址 空 间 的 应 用 使 用 的。 当Windows 创 建 一 项 新 任 务 时，Windows 的 装 入 模 块 为 该 任 务 创 建 一 个 任 务 数 据 库(TDB)。 这 个 任 务 数 据 库 必 须 被 装 入 到1MB 以 下 内 存， 而 且 最 小 长 度 为200 字 节。 原 因 是 任 务 数 据 库 项 的 第 二 部 分 是 一 个 程 序 段 前 缀（PSP）， 这 是Windows1.0、2.0 和3.0 实 模 式 创 造 的， 使 用 它 的 唯 一 原 因 是 更 加 容 易 地 调 度 应 用 内 置 的MS-DOS 扩 展 器。 在 保 护 模 式 的Windows 并 不 是 必 需 的， 不 过 在Windows3.1 保 护 模 式 中 仍 保 留 了 这 一 结 构。

无 论 您 的 机 器 是 多 少 兆 内 存， 您 的 程 序 必 须 去 争 取 这 段 可 用 空 间， 没 有 什 么 方 法 可 以 扩 充 它。 这 一 限 制， 以 及 下 面 将 提 到 的64KGDI 和USER 堆， 是Windows3.X 中 最 受 限 制 的 代 码。

所 有 的Windows 程 序 或 多 或 少 需 要 一 些 这 种1M 以 下 的 内 存 才 能 正 常 工 作。 一 般 的 程 序 除 分 配 了 一 些 固 定 的 内 存 外， 有 些 程 序 还 要 另 外 申 请 一 些 内 存 空 间， 而 有 些 有 错 误 的 程 序 会 很 快 地 消 耗 掉 这 段 有 限 的 内 存 空 间， 这 就 会 出 现 前 面 我 们 所 提 到 的 内 存 不 足 问 题。 不 过PowerBuilder 并 不 让 我 们 自 己 去 分 配 这 些 稀 有 的 资 源， 这 也 使 我 们 无 法 解 决 这 种 原 因 引 起 的 内 存 不 足 问 题。

C 程 序 员 会 很 熟 悉 用GMEM_FIXED 或 是 用GlobalDosAlloc 函 数 分 配 内 存。 这 两 个 函 数 都 将 试 图 分 配 低 于1M 的 内 存。HEAPWALK.EXE（ 包 含 在WindowsSDK 或C++ 编 译 器 中） 这 样 的 应 用 程 序 能 查 看1M 以 下 分 配 的 内 存 块， 从 而 更 准 确 地 确 定 哪 些 应 用 吃 掉 了 您 宝 贵 的 内 存 资 源。 可 以 用MS-DOS 中 的LoadHigh 功 能， 将 一 些 驻 留 程 序 装 入 高 端 而 释 放 常 规 内 存， 但 这 样 做 也 减 少 了 可 用 的 上 位 内 存， 最 好 办 法 只 能 是 折 衷。

其 它Windows 资 源

还 有 两 种Windows 中 常 常 引 起 问 题 的 资 源， 它 们 是USER 和GDI 资 源。 这 些 资 源 就 象 上 面 描 述 的 任 务 数 据 库 区 一 样， 也 是 被Windows 而 不 是 被 机 器 内 存 限 制 的， 它 们 都 有 不 能 超 过64K 的 限 制。 这 意 味 着 有 时 并 不 是 机 器 内 存 总 数 引 起 了 内 存 不 足。

GDI 资 源 就 是 您 的 应 用 中 用 到 的 资 源 句 柄 和 设 备 上 下 文。 每 个 位 图、 图 标、 光 标、 数 据 窗 口、 用 户 对 象 和 窗 口 都 需 要 这 种 资 源。 大 的 自 定 义 工 具 条 对GDI 资 源 的 需 求 最 大， 但 它 一 般 不 会 引 起 麻 烦； 窗 口、 数 据 窗 口 和 按 钮 将 最 终 用 光GDI 资 源。GDI 资 源 几 乎 无 一 例 外 地 最 先 减 少 到20% 以 下。

USER 资 源 也 是 每 个 对 象 都 需 要 的 资 源。 如 果 一 个 用 户 对 象 由 一 组 对 象 组 成， 其 中 每 个 对 象 都 需 要USER 资 源（ 句 柄、 任 务 管 理 等 等）。 这 种 情 况 下 调 用 动 态 打 开 或 关 闭 的 对 象 显 然 是 有 益 的。 因 为 数 据 窗 口 是 单 一 对 象， 所 以 使 用 数 据 窗 口 作 为 对 象 集 合 也 是 给 这 些 资 源 减 少 负 担 的 一 个 好 方 法，

通 过 上 述 介 绍， 我 们 可 以 看 到Windows3.x 中 可 用 的 内 存 远 比 您 最 开 始 想 象 的 要 少。Windows95 也 许 将 会 缓 解 这 一 问 题， 但 仍 需 观 察。

动 态 监 控 资 源 例 程

最 后， 我 将 介 绍 一 个 能 帮 您 在 运 行 时 跟 踪 您 的 系 统 资 源 的 例 程。 我 们 曾 提 到 过， 使 用PowerBuilder， 我 们 不 能 控 制 系 统 对 资 源 的 使 用， 但 是 我 们 可 以 用 一 些 简 单 的SDK 函 数 调 来 监 控 资 源 的 使 用。 我 们 将 使 用 一 个 非 可 视 化 对 象 做 为SDK 的 界 面 对 象。 当 然 您 也 可 以 将SDK 函 数 声 明 为 全 局 外 部 函 数 并 通 过 全 局 函 数 来 访 问 它 们。

步 骤 一：

创 建API 访 问 对 象NVO_API_ACCESS.；

创 建 一 个 用 于 声 明 的 应 用 事 件， 并 从CONSTRUCTOR 事 件 中 触 发 该 事 件。

创 建 一 个 用 于 初 始 化 的 用 户 事 件NVO_UE_SETUP， 并 从CONSTRUSTOR 中 触 发。

步 骤 二：

声 明 以 下 的 局 部 外 部 函 数：

FUNCTION uint GetFreeSystemResources(uint SysResource) LIBRARY 'user.dll'

步 骤 三：
声 明 以 下 的 变 量 实 例：

Private:
/* 最 小 的 资 源 限 制*/
Long	il_usermem_limit
Long	il_gdimem_limit
Long	il_memory_limit
Long	il_standard_threshhold
/* 资 源 访 问 常 量*/
Integer	sdkUser=0,sdkGDI=1,sdkResources=2

步 骤 四：

创 建 以 下 的 用 户 对 象 函 数：

//*************************************************
//	 函 数:	NVOF_Check_Resources
//	 功 能:	To check system resources
//	 参 数:( 无)
// 返 回 值:integer
//	1- 成 功,<0- 失 败（ 绝 对 数 值 为 资 源 访 问 常 量）。 //************************************************* if GetFreeSystemResources(sdkUser) < il_usermem_limit then return(sdkUser*-1) end if if GetFreeSystemResources(sdkGDI) < il_gdimem_limit then return(sdkgdi*-1) end if if GetFreeSystemResources(sdkResources) < il_memory_limit then return(sdkMemory*-1) end if Return1

步 骤 五：
我 们 需 要 创 建 一 个 函 数 用 于 建 立 我 们 的 最 小 资 源 限 制。 创 建 函 数NVOF_Set_Resource_Limit。 输 入 参 数 是 整 型 变 量ai_ResourceType， 它 指 代 可 用 资 源 常 量 种 类(GDI,User 或 Memory) 和ai_threshhold， 指 代 这 种 资 源 类 型 的 临 界 限 制 值。

//***********************************************
//	 函 数:	NVOF_Set_Resource_Limit
//	 作 用:	Set resource threshhold limits
//	 参 数:   integer	ai_ResourceType
//					( 指 代 可 用 资 源 常 量 种 类)
//			integer   ai_Threshhold
//					( 这 种 资 源 类 型 的 临 界 限 制 值)
//   返 回 值:	integer
//			1- 成 功,-1- 失 败.
//
//***********************************************

/* 设 置 资 源 极 限*/
Choose Case ai_ResourceType
    Case sdkGDI
        il_gdimem_limit=ai_Threshhold
    Case sdkUser
        il_usermem_limit=ai_Threshhold
    Case sdkSystem
        il_Memory_Limit=ai_Threshhold
End Choose
Return1

步 骤 六：
在 声 明 事 件 中， 将il_Default_Threshold 设 为 稍 大 一 些 的 数 值。 例 如，30% 可 能 是 您 能 接 受 的 最 低 的 不 用 警 告 用 户 的 资 源 量， 那 么 我 们 在 声 明 事 件 中 写 下 如 下 代 码：
il_Default_Threshold=30
在 初 始 化 事 件 中， 建 立 缺 省 函 数 以 设 定 临 界 限 制， 函 数 可 以 在 运 行 时 重 置 这 些 限 制。 开 发 过 程 中 将 限 制 设 的 高 一 点， 这 样 确 保 在 最 后 产 品 运 行 时 资 源 不 会 出 问 题。
NVOF_Set_Resource_Limit(sdkUser,il_Default_Threshold)
NVOF_Set_Resource_Limit(sdkGDI,il_Default_Threshold)
NVOF_Set_Resource_Limit(sdkMemory,il_Default_Threshold)

小 结

建 立 上 述 对 象 的 一 个 实 例， 在 打 开 一 个 窗 口、 动 态 打 开 一 个 用 户 对 象 或 创 建 一 个 对 象 之 前， 通 过 调 用 对 象 的NVOF_Check_Resources() 函 数， 您 可 以 确 定 是 否 有 足 够 的 资 源 用 来 继 续 正 常 打 开。 这 些 函 数 也 可 以 在 应 用 的 任 何 地 方 调 用 以 重 置 限 制 或 检 查 资 源。


张健姿-->PowerBuilder系列讲座-->14. PowerBuilder的事件(续)

14. PowerBuilder的事件(续)

熟 悉 Windows 编 程 的 人 员 都 知 道， Windows 的 API 为 程 序 员 提 供 了 大 量 的 消 息， 约 有 两 百 多 个 的 消 息。 在 PowerBuilder 中， 系 统 将 每 个 Windows 消 息 做 为 事 件 传 递 给 程 序 员 处 理， 并 且 为 大 多 数 普 通 的 事 件 提 供 了 缺 省 的 处 理。

PowerBuilder 在 语 句 描 绘 器 中 为 每 一 个 标 准 的 控 件 设 定 了 一 些 常 用 的 事 件，一 般 的 编 程 人 员 基 于 这 些 事 件 就 基 本 可 以 完 成 常 见 的 操 作。 如 果 我 们 会 遇 到 事 件 的 处 理 是 需 要 另 外 的 截 获 Windows 的 消 息， 我 们 可 以 在 语 句 描 绘 器 的 菜 单 的 Declare | User Event... 中 加 入 一 个 新 的 事 件， 同 时 指 出 您 所 希 望 对 应 的 Windows 消 息。

Windows API 的 消 息 一 般 以 两 到 三 个 字 符 的 标 识 开 始， 这 个 标 识 标 明 了 该 消 息 的 来 源 或 对 象。 例 如：pbm_active实 际 上 对 应 的 是 WM_ACTIVATE,WM 说 明 该 消 息 是 一 窗 口 消 息。 其 它 的 消 息 前 缀 包 括：

*BM         按 钮 消 息
*BN         按 钮 通 知 消 息
*CB         通 用 对 话 框 消 息
*CBN        通 用 对 话 框 通 知 消 息
*DM         对 话 框 消 息
*EM         编 辑 器 控 件 消 息
*EN         编 辑 器 控 件 通 知 消 息
*LB         列 表 框 消 息
*LBN        列 表 框 通 知 消 息
*MM         多 媒 体 消 息
*SBN        卷 滚 条 通 知 消 息

此 外 还 有 一 个 DDE 消 息，DDE 消 息 从 技 术 上 来 说 属 于 窗 口 消 息， 但 在 这 里 我 们 把 它 们 分 离 出 来， 因 为 PowerBuilder 并 不 完 全 支 持 全 部 的 DDE 消 息。

消 息 一 般 告 诉 某 个 对 象（如 按 钮） 去 执 行 一 些 动 作。 通 知 消 息 一 般 由 某 个 控 件 发 出， 它 通 知 应 用 程 序 某 些 事 件 在 其 上 发 生 了。Waite Group 的 《Windows API 编 程 宝 典》 详 细 描 述 了 这 200 多 个 消 息 的 具 体 含 义， 这 些 消 息， 我 们 在 PowerBuilder 中 也 可 以 用 Send(handle, message_no, word, long) 函 数 发 出。

所 有 的 PowerBuilder 自 动 映 射 的 事 件 名 都 以"Pbm"前 缀， 例 如，BM_GETCHECK变 成 了

pbm_bmgetcheck。 通 过 向 定 义 用 户 事 件 对 话 框 中 增 加 事 件， 程 序 员 可 以 在 需 要 的 时 候 截 获 这 些 消 息。

PowerBuilder 还 有 另 一 类 消 息 是 标 准 的 Windows API 所 不 提 供 的 消 息。 这 类 消 息 以"dw"或"dwn"开 头， 如 pbm_dwnretrieveend。 这 些 是 与 数 据 窗 口 有 关 的 消 息 和 通 知 消 息， 因 为 数 据 窗 口 不 是 标 准 窗 口 对 象， 它 们 有 它 们 自 己 的 事 件。

下 面 我 们 解 释 一 下 PowerBuilder 中 以 pbm 开 头 的 内 部 映 射 事 件 的 含 义。 限 于 篇 幅， 我 们 不 能 全 部 介 绍， 只 能 将 每 一 类 中 举 一 些 主 要 的 例 子， 其 余 的 事 件 类 推。

单 选 或 多 选 按 钮 消 息（前 缀：pbm_bm)

getcheck               单选按钮或多选按钮是否被选？
getstate               按钮是否加亮
setcheck               将无线按钮或确认框的选中状态改为未选中状态，反之亦然
setstate               加亮或不加亮按钮
change                 改变按钮的风格；例如，改为单选按钮或组合框

单选或多选按钮通知消息（前缀：pbm_bn）
clicked                按钮控件被点中
disable                使按钮控件无效
double-clicked         按钮控件被双点
dragdrop               一个对象被放到按钮控件
dragenter              一个对象被拖到按钮控件
dragleave              一个对象被拖离按钮控件
dragover               一个对象被拖经按钮控件
hilite                 按钮控件将被加亮
paint                  按钮控件将被绘制
setfocus               按钮控件将获得聚焦
unhilite               按钮控件将被不加亮
通用对话框消息（前缀：pbm_cb）
addstring              将字符串加到通用对话框
deletestring           从通用对话框删除一个字符串
dir                    加一个目录列表到通用对话框
findstring             搜索以一组字符开头的字符串
findstringexact        搜索与所提供的字符完全匹配的字符串
getcount               列表框中的项数
getcursel              当前被选项的数目
getdroppedcontrolset   列表框在屏幕上的坐标位置
geteditsel             编辑器控件中被选字符的范围
getextendedui          缺省或扩展的用户界面
getitemdata            重画的列表框中的 4字节的项
getitemheight          列表框中某一项的高度
getlbtext              列表框中的文本
getlbtextlen           列表框中文本的长度
insertstring           向列表框中增加新的字符串或4字节项
limittext              限制能被输入到列表框中的字符数
resetcontent           删除列表框中所有元素
selectstring           搜索匹配的字符串并显示
setcursel              在列表框中选择并显示一项
seteditsel             在列表框的编辑区域中选择一块文本
setextendedui          设置缺省或扩展用户界面
setitemdata            在列表框中设定 4字节（32位）项
setitemheight          设置列表框中项的高度
showdropdown           转换（显示或隐藏列表框中拖放区域）

通用对话框通知消息（前缀：pbm_cbn）
dblclk                 用户在列表中某一项上双点
dragdrop               一个对象被放到通用对话框控件上
dragententer           一个对象被拖到通用对话框控件上
dragleave              一个对象被拖离通用对话框控件
dragover               一个对象被拖经通用对话框控件
dropdown               列表框的下放区域即将被显示
editchange             编辑器控件中的文本发生变化
editupdate             列表框编辑器控件中的文本即将被改变
errspace               列表框满，不能再向其中加入项
killfocus              通用列表框失去聚焦
selchange              列表框中被选文本被改变
selendcancel           用户按下了"取消"按钮
selendok               用户按下了"确认"按钮
setfocus               通用对话控件拥有聚焦

数据窗口消息（前缀：pbm_dw）
closedropdown          关闭下拉式数据窗口
scrollend              在数据窗口中卷滚到最后一行
scrdlhome              在数据窗口中卷滚到第一行
scrolllineend          卷滚到当前行的行尾（水平方向）
scrolllinehome         卷滚到当前行的行首（水平方向）

数据窗口通知消息（前缀：pbm_dwn）
backtabout             即将通过 Shift+Tab组合键离开该控件
changing               控件即将被改变
dropdown               下拉式列表框的下拉部分即将可见
graphcreate            即将创建图形
itemchangefocus        数据窗口控件中当前项的聚焦改变
itemvalidationerror    对当前项的修改引起了一个合法性检查错误
key                    有键被按下。使用KeyDown()处理键盘值
lbuttondown            鼠标左键被按下
lbuttonup              鼠标左键被松开
mbuttonclk             鼠标中键点击
mbuttondbclk           鼠标中键双点
mousemove              鼠标移动
printmarginchange      打印边界被改变
processenter           回车键被按下
rowchange              数据窗口中聚焦从一行转向另一行
tabdownout             用户在数据窗口最后一行按下了下箭头键
tabout                 用户在数据窗口的最后一行／列中按下了tab键
tabupout               用户在数据窗口第一行中按下了上箭头键

动态数据交换(DDE)消息（前缀：pbm_dde）
ddeack                 收到一个DDE消息
ddeinitiate            开始一个DDE会话
ddeterminate           终止一个DDE会话

编辑器控件消息（前缀：pbm_em）
canundo                编辑器控件是否能撤消上一次修改？
emptyundobuffer        清空由 Windows管理的取消操作的缓冲区
fmtlines               在多行编辑器控件的行尾增加或删除回车换行
getfirstvisibleline    返回编辑器控件中可见的第一行的行号
gethandle              获得编辑器控件使用的内存句柄
getline                从编辑器控件中复制一行到内存中的一个缓冲区
getlinecount           返回多行编辑器控件的行数
getmodify              文本是否被用户修改
getrect                返回控件的长方形域
getsel                 返回被选文本的起始位置
limittext              限制用户键入的文本长度
linefromchar           返回被选文本的行号
lineindex              返回编辑器控件中被选行第一个字符在编辑串中的位置
linelength             返回编辑器控件中被选行中的字符数
linescroll             水平或垂直卷滚编辑器控件
replacesel             从剪贴板或从键盘用新文本替换被选文本
sethandle              设置编辑器控件的句柄指向内存中的一个缓冲区
setmodify              设置编辑器控件的modified标志
setpasswordchar        设置用户输入任何文本时显示的字符。用来在输入密码时的显示
setrect                设置／重置编辑器控件所在的长方形区域。
编辑器控件中的文本被重画。
setrectnp              与Setrect大致相同，除了不重画文本
setsel                 选择字符
settabstops            在多行编辑器控件中设置tab stops
setwordbreak           设置新的词拆分函数
setwordbreakproc       设置新的词拆分过程
undo                   撤消最近的编辑操作

编辑控件通知消息（前缀：pbm_en）
change                 编辑器控件中的文本发生改变
errspace               编辑器控件内存缓冲区溢出
hscroll                用户点中上水平卷滚条
maxtext                用户试图输入比所允许的更多的文本
update                 编辑器控件即将显示用户的修改
vscroll                用户点中了垂直卷滚条


张健姿-->PowerBuilder系列讲座-->15. PowerBuilder的事件(续)

15. PowerBuilder的事件(续)
列表框消息（前缀：pbm_lb）
addstring              向列表框控件中增加一项或一个字符串
deletestring           从列表框中删除一项
dir                    用目录列表填充列表框
findstring             在列表框中搜索部分与所给字符串相匹配的第一项
findstringexact        在列表框中搜索与所给字符串精确匹配的第一项。
getcaretindex          在列表框中搜索拥有聚焦的项
getcount               确定列表框中的项数
getcursel              确定所选项的是第几项
gethorizontalextent    获得列表框的宽度、计算水平卷滚
getitemheight          确定列表框控件中一项的高度
getitemrect            确定列表框的尺寸
getsel                 获得列表框中当前所选的项
getselcount            在多选列表框中获得所选项的数目
getselitems            用列表框中所表框各自的项号填充一个给定的整数数组
gettext                获得列表框中当前所选的项的文本
gettextlen             获得列表框中当前所选的项的文本中的字符数
gettopindex            确定列表框中可见的最上面一项的项号
insertstring           向列表框中加入一个新字符串
resetcontent           重置（消除）列表框中的内容
selectstring           搜索并加亮与所给字符匹配的字符串
selitemrange           选择／取消列表框中某一范围中的项
setcaretindex          设置列表框中的某一项拥有聚焦
setcolumnwidth         设置列表框中列的宽度
setcursel              在列表框中选择并加亮一项。如果需要，卷滚到列表框
sethorizontaltext      设置可以列表框中被水平卷滚的单元数。
setitemdata            设置与列表框相关的32位／4字节值
setitemheight          设置与列表框中一项的高度。
setsel                 在列表框中选择一个字符串
settabstops            设置列表框控件中tab stops的位置
settopindex            卷滚列表框使特定的项成为可见的最上面的一项。
列表框通知消息（前缀：pbm_en）
dblclk                 用户在列表框控件中的某一项上双点
errspace               用户试图超越可在列表框中输入字符的最大限制
selcancel              当前选取文本被取消
selchange              用户在列表框中选择或取消了一项窗口消息（前缀：pbm_）
activateapp            被激活的窗口属于另外一个应用
askcbformatname        要求剪贴板中的内容被复制到一个使用自定义格式的文本缓
冲区中
char                   传送键盘上按下的键
chartoitem             通过转换键盘来的字符，帮助列表框定位其中的项
childactivate          一个子窗口被移动或激活
clear                  用户要删除当前编辑器控件中的内容
command                用户选择了一个菜单项，控件，或使用了加速键
compacting             系统内存资源不足。当windows占用了多于 1/8的 CPU时间
用于紧缩内存时，产生这条消息
compareitem            当新的一项被加入列表框或组合框时产生此消息。Window
用这条消息方便项之间的比较
ctlcolor               一个控件即将被画出。可在此时改变控件的颜色
deadchar               用户选择了一种非英语字符集或其它特殊字符集，这将改变下
面将要输入的字符
deleteitem             一项被从列表框或通用对话框中移去
destroyclipboard       剪贴板内容被清除
devmodechanged          WIN.INI中的一个设备名被修改
drawclipboard          剪贴板内容发生改变
drawitem               列表框或通用对话框的一项内容被改变
dropfiles              当鼠标左键在一个注册为拖放文件接受器的应用上释放时，发
出该消息
erasebkgnd             窗口的客户区需要重画
fontchange             应用可用的字体数改变
getdlgcode             通知消息，说明当前使用哪种类型的键盘
getfont                获取当前激活的字体
getminmaxinto           Windows正在检查最大化或最大化窗口的尺寸
gettext                从一个控件（如按钮或编辑器控件）中复制文本到一个内存缓冲
区
gettextlength          用来确定一个控件中的字符数
hscrollclipboard       剪贴板的水平卷滚条被使用
iconerasebkgnd         一个最小化窗口需要重化背景
initdialog             一个对话框即将被显示
initmenu               一个菜单即将被显示
initmenupopup          一个弹出式窗口即将将显示
keydown                键盘上的一个键被按下
keyup                  键盘上的一个键被释放
mdiactive              一个MDI子窗口（表单）被激活
mdicascade             以重叠的形式重排所有的表单
mdicreate              创建一个MDI子窗口（表单）
mdidestroy             从MDI框架中移去一个表单
mdigetactive           获得当前活动的MDI表单的句柄
mdiiconrange           在一个MDI框架中重排最小化表单的图标
mdimaximize            最大化一个MDI子表单
mdinext                激活下一个MDI表单（紧接着活动表单的表单）
mdirestore             恢复MDI表单到它原来的大小
mdisetmenu             将一个菜单与一个MDI表单联系起来
mdifitle               平铺所有的MDI表单
measureitem            这个消息被送给即将创建的、内有按钮或其它控件的窗口
menuchar               用户使用了一个快捷键，但该快捷键系统不支持
menuselect             用户选择了一个菜单项
mouseactivate          用户在一个非活动窗口中点击了鼠标
mousemove              用户移动了鼠标
ncactivate             窗口的非客户区即将被激活
nccalcsize             窗口的尺寸需要重新计算
nccreate               窗口即将创建它的非客户区
ncdestroy              窗口的非共享区被析构
nchittest              每次非客户区被移动时都发送该消息
nclbuttondblclk        用户在非客户区双点了鼠标左键
ncpaint                非客户需要画出
nextdlgctl             在对话框中将聚焦转给另一个控件
paint                  窗口的客户区需要被画出
paintclipboard         剪贴板应用有剪贴操作，剪贴板查看器需重画
palettechanged         系统调色板被改变
paletteischanging      系统调色板即将被改变
parentnotify           通知父窗口一个子窗口即将被创建
querydragicon          用户要拖一个最小化窗口
queryendsession        通知消息，说明窗口即将被关闭
querynewpalette        应用即将收到输入聚焦，应该执行任何必要的颜色调整
queryopen              一个最小化窗口即将被恢复
quit                   应用处理的最后一个消息
renderallformats       通知一个剪贴板格式的拥有者，应用将失去所有的格式
renderformats          通知消息，说明放在剪贴板中的数据应该用一种特殊格式传送
setcursor              通知消息，说明鼠标指针在一个窗口中移动
setfont                用来在对话框中改变字体
setredraw              在加一个新项之前发送给列表框或通用对话框
settext                用来改变窗口的标题或文本
sizeclipboard          剪贴板查看器应用改变尺寸
spoolerstatus          一个打印管理器任务被添加或删除
syschar                 键和其它某键同时被按下
syscolorchange         一种或多种系统颜色被改变
syscommand             用户选择了一个系统菜单命令
sysdeadchar            通知消息，说明一种非英语字符集被选定
syskeydown             用户按下某键的同时按下了键
syskeyup               用户释放了ALT组合键
timechange             系统时钟被修改
undo                   从undo缓冲区复制文本到编辑器控件
vkeytoitem             当一个列表框拥有聚焦时，用户按下了一个键
vscroll                用户点击了垂直卷滚条
vscrollclipboard       剪贴板查看器的垂直卷滚条被点击
windowposchanged       窗口位置发生改变
windowposchanging      窗口位置即将发生改变
wininichange            WIN.INI文件被修改

例 如 许 多 最 终 用 户 曾 长 期 使 用 基 于 DOS 平 台 或 基 于 字 符 终 端 的 软 件 应 用， 在 这 些 软 件 中， 同 一 个 窗 口 下 的 多 个 输 入 列 之 间 聚 焦 的 转 换 一 般 是 靠 键 入 回 车 驱 动 的， 因 此 大 多 数 的 录 入 员 都 习 惯 于 使 用 键 盘 右 侧 的 数 字 键 盘 录 入 数 据， 每 项 数 据 录 入 后 敲 击 回 车， 转 到 下 一 个 输 入 项。 在 Windows 环 境 下， 同 一 个 窗 口 下 的 不 同 控 件 之 间 的 转 换 是 用 tab 键 实 现 的， 在 一 个 DataWindow 中 无 论 是 FreeForm 形 式 还 是 Tabular 或 Grid 形 式， 系 统 缺 省 都 是 使 用 tab 键 或 Shift-tab 键 转 换 到 下 一 个 数 据 列 或 上 一 个 数 据 列， 而 用 户 敲 击 回 车 键， 系 统 会 将 聚 焦 转 到 下 一 条 记 录 的 第 一 个 输 入 列 上。 这 样 有 时 用 户 就 会 向 开 发 者 提 出 这 样 的 需 求： 希 望 用 回 车 键 来 实 现 列 之 间 的 切 换， 以 符 合 录 入 员 固 有 录 入 习 惯。

如 果 我 们 熟 悉 了 DataWindow 的 事 件 定 义， 就 可 以 非 常 轻 易 地 实 现 上 述 功 能。 我 们 在 DataWindow 中 定 义 一 个 EnterKey 的 事 件， 以 捕 捉 用 户 在 DataWindow 中 敲 击 了 回 车 键， 对 其 应 实 现 的 功 能 进 行 转 换。

在 User Events... 的 对 话 框 中 加 入 Event Name 为 EnterKey， Event ID 为 pbm_dwnprocessenter 的 事 件， 对 其 事 件 编 程 如 下：

int li_CurCol
int li_cnt
//得到当前光标所在的列数
li_CurCol = getcolumn()
//假设这个 DataWindow共有 6列
if li_CurCol < 6 then
//如果当前列不是最后一列，将聚焦转到下一列
    setcolumn(li_CurCol + 1)
elseif rowcount() <= getrow() then
//当前列是最后一列，而且当前行是最后一行，
//则增加一行，并将聚焦转至该行的第一列
    li_cnt = insertrow(0)
    scrolltorow(li_cnt)
    setcolumn(1)
else
//当前列是最后一列，当前行不是最后一行，
//则将聚焦转至下一行的第一列
    li_cnt = getrow() + 1
    setrow( li_cnt )
    scrolltorow( li_cnt )
    setcolumn(1)
end if
//将系统固有的操作屏蔽，每一个返回值
//的不同含义，以前的文章曾有叙述。


张健姿-->PowerBuilder系列讲座-->16. 生成可重用的数据窗口

16. 生成可重用的数据窗口

许 多 人 在 学 习 了 窗 口 的 继 承 以 后， 就 认 为 算 是 掌 握 了PowerBuilder 的 继 承 技 术 了。 实 际 上，PowerBuilder 中 继 承 的 作 用 远 不 止 这 些。 这 里 我 们 介 绍 利 用 继 承 的 方 法， 设 计 一 个 可 重 用 的 数 据 窗 口 祖 先。 通 过 本 节 的 介 绍 您 将 看 到 继 承 技 术 在 数 据 窗 口 上 的 运 用， 体 会 到 它 在 这 一 方 面 发 挥 出 灵 活 性 和 强 大 功 能。

数 据 窗 口 的 继 承 是 通 过 自 定 义 用 户 对 象 的 方 式 扩 展 数 据 窗 口 控 件， 使 该 控 件 功 能 更 为 强 大。 在 本 节 后 面 的 部 分 中， 我 们 把 数 据 窗 口 控 件 的 用 户 对 象 祖 先 叫 作dwa_main。 您 也 可 以 把 您 的 控 件 命 名 为 任 何 您 想 要 的 名 字。

我 们 曾 在 以 前 的 文 章 中 介 绍 过， 数 据 窗 口 对 象 是 不 具 有 继 承 性 的， 但 是 我 们 可 以 利 用 具 有 可 继 承 特 性 的 数 据 窗 口 用 户 对 象 来 拓 展PowerBuilder 所 提 供 的 数 据 窗 口 的 基 本 属 性。 这 里 我 们 要 注 意 的 是 我 们 所 继 承 的 是 数 据 窗 口 控 件， 而 不 是 数 据 窗 口 对 象。

为 什 么 应 该 使 用 用 户 对 象 数 据 窗 口， 而 不 用PowerBuilder 提 供 的 呢 ？ 当 您 将 一 个 数 据 窗 口 控 件 放 到 窗 口 上 时， 您 所 做 的 是 继 承 一 个 基 于 系 统 事 先 定 义 好 的 数 据 窗 口 类 来 创 建 一 个 新 的 数 据 窗 口。 这 意 味 着， 给 您 的 数 据 窗 口 增 加 功 能 方 法 只 有 一 个， 就 是 在 这 个 数 据 窗 口 控 件 的 事 件 语 句 中 增 加 代 码。 您 不 能 为 这 个 数 据 窗 口 定 义 您 自 己 的 函 数， 您 也 不 能 给 这 个 数 据 窗 口 增 加 另 外 的 属 性。 如 果 您 认 为 有 一 些 常 用 的 对 数 据 窗 口 的 操 作 需 要 封 装， 您 只 能 将 这 些 加 入 数 据 窗 口 所 在 的 窗 口。 如 果 您 希 望 两 个 不 同 的 数 据 窗 口 上 有 相 同 的 行 为， 唯 一 的 方 法 就 是 重 写 代 码。 这 是 显 然 不 是 好 的 面 向 对 象 编 程 风 格。 解 决 上 述 问 题 的 好 方 法 就 是 定 义 一 个 数 据 窗 口 用 户 对 象， 这 非 常 容 易 做 到。 您 只 要 打 开 用 户 对 象 绘 制 器， 选 择“ 新 建” 和“ 标 准 可 视 化 用 户 对 象”。 然 后 选 择 数 据 窗 口 作 为 类 型。 您 就 可 以 看 见 一 个 小 白 框。 这 就 是 您 的 数 据 窗 口 控 制， 将 它 以dwa_main 的 名 字 存 盘。dwa_main 将 是 您 的 最 底 层 的 数 据 窗 口 祖 先。

现 在， 当 您 在 窗 口 上 需 使 用 数 据 窗 口 时， 您 不 要 将PowerBuilder 内 部 的 数 据 窗 口 放 到 窗 口 上 了， 而 是 放 上 您 新 创 建 的 数 据 窗 口 用 户 对 象。 这 个 数 据 窗 口 的 名 字 应 该dw_1。 使 用 用 户 对 象 来 替 代 系 统 本 身 的 数 据 窗 口 以 后， 您 仍 可 以 象 以 前 那 样 使 用 数 据 窗 口 的 所 有 的 函 数， 属 性 和 事 件。 不 同 的 是， 您 现 在 可 以 为 您 的 数 据 窗 口 祖 先dwa_main 中 加 入 实 例 或 共 享 的 变 量， 事 件 和 函 数。 因 为 您 在 窗 口 中 用 到 的 数 据 窗 口 都 是 继 承 了 祖 先 数 据 窗 口dwa_main， 所 以 您 所 增 加 的 这 一 切 都 可 以 在 每 一 个 数 据 窗 口 控 件 中 存 在。

如 果 您 认 为 这 种 方 法 可 行， 决 定 以 后 的 设 计 都 采 用 用 户 对 象 来 替 代PowerBuilder 本 身 的 数 据 窗 口， 那 么 您 可 能 还 会 遇 到 这 样 一 个 问 题， 如 何 在 已 编 完 的 代 码 中 用 上 这 个 对 象。 这 里 我 们 介 绍 采 用 这 样 一 种 办 法， 将 您 的 数 据 窗 体 控 件 的 祖 先 对 象 由Datawindow 改 变 成 为dwa_main。 在library 画 笔 中 输 出 含 有 数 据 窗 口 的 窗 口 或 用 户 对 象， 修 改 几 个 引 用， 然 后 将 它 再 输 入 到 您 的 库 中。

注 意 在 做 这 些 操 作 之 前， 先 做 一 下 备 份。

当 您 输 出 的 窗 口 或 自 定 义 可 视 化 用 户 对 象 中， 您 可 以 看 到 类 似 下 面 的 代 码： type dw_1 from datawindow within w_anc

我 们 曾 在 以 前 的 文 章 中 介 绍 过， 这 行 代 码 的 作 用 是 告 诉PowerBuilder 编 译 器 您 定 义 了 一 个 从 数 据 窗 口 继 承 下 来 的 叫 做dw_1 的 数 据 窗 口， 因 此 我 们 只 要 在 这 里 将datawindow 改 为dwa_main， 就 可 以 使dw_1 继 承 的 对 象 改 为 有 系 统 内 部 的 数 据 窗 口 控 件 改 为dwa_main。

如 果 您 打 算 将 所 有 在 您 的 机 器 上 开 发 的 其 他 人 也 使 用dwa_main 对 象， 您 还 可 以 自 定 义 窗 口 工 具 条， 在 工 具 条 中 加 入 一 个 新 图 标 来 代 表 数 据 窗 口 用 户 对 象 的 控 件。 在 定 义 工 具 条 的 对 话 框 中， 要 求 填 入dwa_main 这 一 用 户 对 象， 并 设 置 您 希 望 得 到 的 工 具 条 文 本 和 帮 助 信 息。

数 据 窗 口 的 构 造 事 件

一 般 的， 如 果 某 些 代 码 的 初 始 化 操 作 在 应 用 中 首 次 运 行 时 被 执 行， 您 应 该 将 它 们 放 在 应 用 的open 事 件 中。 如 果 某 些 代 码 在 窗 口 被 打 开 时 执 行 其 初 始 化 操 作， 您 应 该 将 它 们 放 在 窗 口 的open 事 件 中。 同 样 如 果 想 让 代 码 在 数 据 窗 口 被“ 打 开” 时 执 行 初 始 化 操 作， 您 应 该 使 用 数 据 窗 口 的 构 造 事 件， 因 为 当 数 据 窗 口 创 建 时 构 造 事 件 被 激 发。 事 实 上， 窗 口 上 的 每 一 个 控 件 在 窗 口 打 开 之 前 都 会 激 发 自 身 的 构 造 事 件。 假 设 在 您 的 窗 口 上 有 三 个 控 件， 它 们 的 顺 序 为cb_ok，sle_1 和dw_1。 如 果 您 使 用OpenSheet() 函 数 将 该 窗 口 作 为 表 单 打 开， 您 将 得 到 以 下 的 事 件 序 列：

cb_ok::constructor
sle_1::constructor
dw_1::constructor
window::open
window::show
window::resize
window::activate
cb_ok::getfocus

如 果 您 打 开 一 个SDI 窗 口， 您 将 得 到 以 下 的 事 件 序 列：

cb_ok::constructor
sle_1::constructor
dw_1::constructor
window::open
window::activate
cb_ok::getfocus
window::show
window::resize

有 一 点 需 要 说 明 的 是， 窗 体 本 身open 事 件 中 的 所 作 的 初 始 化 工 作 是 在 所 有 控 件 的 构 造 事 件 被 激 发 后 才 执 行 的。 如 果 您 用OpenSheetWithParm() 或OpenWithParm() 函 数 向 您 的 窗 口 传 递 数 据， 那 么 您 在 构 造 事 件 中 使 用 消 息 对 象 时 就 要 特 别 小 心。 如 果 其 中 的 任 何 一 个 构 造 事 件 修 改 了 消 息 对 象 的StringParm，DoubleParm 或PowerObjectParm 属 性，open 事 件 中 使 用 的 消 息 对 象 就 不 能 得 到 正 确 的 信 息。

我 们 对 每 一 个 数 据 窗 口 的 构 造 事 件 编 程 时， 总 是 要 反 复 地 键 入

dw_1.SetTransObject(SQLCA)

事 实 上， 我 们 的 应 用 中 有90 ％ 以 上 是 使 用SQLCA 这 个 事 务 对 象， 如 果 我 们 采 用 用 户 对 象 的 设 计 方 法， 我 们 就 可 以 把 这 一 行 代 码 写 在 用 户 对 象 之 中， 而 可 以 省 去 在 每 次 用 数 据 窗 口 时 都 要 键 入。

Event:Constructor
Object:dwa_main
SetTransObject(SQLCA)

每 一 次 调 用 这 个 用 户 对 象， 我 们 缺 省 的 就 是 使 用SQLCA 这 个 事 务。 我 们 完 全 不 必 担 心 这 样 作 会 出 现 什 么 问 题。 因 为 如 果 您 使 用 外 部 数 据 窗 口，PowerBuilder 是 根 本 不 会 理 会 您 曾 经 执 行 过 的 任 何 事 务 对 象， 如 果 您 使 用 的 事 务 不 是SQLCA， 那 么 您 只 需 在 继 承 后 的 数 据 窗 口 中， 再 次 调 用SetTransObject() 这 个 函 数， 将 您 用 到 的 事 务 对 象 引 入， 系 统 就 会 将SQLCA 这 个 对 象 覆 盖。


张健姿-->PowerBuilder系列讲座-->17. 在数据窗口的祖先对象中编写行选择函数

17. 在数据窗口的祖先对象中编写行选择函数

上 期 我 们 介 绍 了 使 用 自 定 义 用 户 对 象 取 代 系 统 自 身 的 数 据 窗 口 的 技 术， 采 用 这 种 办 法 我 们 可 以 把 对 数 据 窗 口 经 常 作 的 操 作 编 写 成 为 对 象 函 数， 在 数 据 窗 口 的 祖 先 对 象 中 进 行 定 义， 并 将 其 封 装 起 来， 而 不 必 在 每 一 个 使 用 数 据 窗 口 的 窗 口 之 中 重 复 编 程。 在 数 据 窗 口 中 我 们 经 常 要 使 用 对 各 条 记 录 的 选 择 操 作， 本 期 我 们 介 绍 如 何 将 这 样 的 功 能 在 对 象 函 数 中 实 现， 以 拓 展 数 据 窗 口 的 基 本 功 能。

我 们 在 数 据 窗 口 中 需 要 进 行 行 选 择 操 作。 如 用 户 为 了 修 改 数 据 或 看 到 更 详 细 的 内 容 而 选 择 了 某 一 行； 在 另 一 些 情 况 下， 用 户 可 能 要 同 时 删 除 或 修 改 多 行， 这 时， 我 们 需 要 有 使 用 户 在 一 个 数 据 窗 口 中 同 时 选 择 多 行 的 功 能。 这 在PowerBuilder 中 是 相 当 容 易 做 到 的， 但 是， 如 果 您 在 一 个 数 据 窗 口 祖 先 中 没 有 标 准 的 函 数 来 处 理 这 些， 而 在 每 个 窗 口 中 重 复 的 编 程， 这 当 然 就 要 麻 烦 多 了。

我 们 首 先 来 看 一 看 进 行 行 选 择 的 不 同 方 法。

所 有 行 都 不 加 亮

数 据 窗 口 的 标 准 行 为 是 不 加 亮 任 何 行。 这 当 然 实 现 起 来 很 简 单 了， 您 什 么 也 不 需 要 做。 这 种 做 法 对 于 那 些 只 允 许 用 户 进 行 滚 动 和 查 看 的 数 据 列 表 或 那 些 单 行 的 数 据 窗 口 是 合 适 的。

单 行 选 择

单 行 选 择 意 味 着 用 户 在 同 一 时 间 只 能 选 择 一 行。 在 用 户 选 择 一 行 来 执 行 一 些 动 作， 如 删 除 或 是 在 主 从 关 系 的 列 表 中 显 示 详 细 信 息， 这 是 非 常 有 用 的。 下 面 是 实 现 允 许 单 行 选 择 功 能 所 应 编 写 的 代 码：

Event: RowFocusChanged
if GetRow() > 0 then
    SelectRow( 0, FALSE )
    SelectRow( GetRow(), TRUE )
end if

多 行 的 自 动 选 择

多 行 的 自 动 选 择 时， 当 用 户 点 击 一 个 未 加 亮 的 行 时， 该 行 将 变 亮。 如 果 用 户 点 击 一 个 加 亮 行， 该 行 将 不 加 亮。

为 了 实 现 上 述 行 为， 您 可 以 将 以 下 的 代 码 放 入RowFocusChanged 事 件 中：

Event:RowFocusChanged
Event: RowFocusChanged
Object: Any DataWindow
if GetRow() > 0 then
    if IsSelected( GetRow() ) then
        SelectRow( GetRow(), FALSE )
    else
        SelectRow( GetRow(), TRUE )
    end if
end if

或者简化成为下面的一行代码：
if GetRow() > 0 then SelectRow( GetRow(), NOT IsSelected( GetRow()))
按住shift，control或control+shift点击鼠标的使用
windows 的 文 件 管 理 器 或 其 它windows 程 序 中， 您 可 以 使 用 按 住shift，control 或control+shift 这 种 键 盘 与 鼠 标 的 组 合 来 改 变 选 择 行 为。PowerBuilder 在 数 据 窗 口 中 没 有 提 供 这 样 的 能 力， 我 们 必 须 自 己 实 现。
我 们 应 当 实 现 的 组 合 功 能 是：
按 下 键
动 作
无
只 加 亮 当 前 行， 将 其 它 行 的 显 示 设 为 正 常
Control
保 持 其 它 行 的 状 态， 并 将 当 前 行 也 加 亮
Shift
加 亮 起 始 行 至 当 前 行 之 间 的 所 有 行， 将 其 它 行 的 显 示 设 为 正 常
Control-Shift
保 持 其 它 行 的 状 态， 并 加 亮 起 始 行 至 当 前 行 之 间 的 所 有 行，
建 立 这 样 的 功 能， 我 们 首 先 需 要 对 起 始 行 进 行 跟 踪。 在 用 户 使 用shift 或control-shift 键 时， 系 统 应 当 把 当 前 用 户 用 鼠 标 点 击 或 键 盘 选 中 的 行 与 在 此 之 前 进 行 操 作 的 起 始 行 之 间 的 所 有 行 全 部 加 亮。 因 此， 数 据 窗 口 必 须 记 录 当 前 的 起 始 行。 我 们 声 明 这 样 一 个 实 例 变 量：
protected long il_anchor_row
当 用 户 点 击 鼠 标 或 进 行 了 键 盘 操 作 时， 我 们 要 测 试 用 户 是 否 同 时 按 下 了shift 或control 键， 使 用 的 是KeyDown() 函 数， 检 查 有 没 有KeyShift! 和KeyControl!， 如 有 这 样 的 键 按 下 则 记 录 起 始 行。

编 写 行 选 择 函 数

为 了 在 用 户 点 击 鼠 标 或 进 行 键 盘 操 作 时 使 上 述 行 为 发 生 作 用， 我 们 必 须 在Clicked 事 件 和 一 个 映 射 到Pbm_dwnkey 事 件 的 自 定 义 用 户 事 件 中 调 用 我 们 自 己 的 行 选 择 函 数。 这 样， 不 管 用 户 是 点 击 鼠 标 还 是 击 中 键 盘， 选 择 行 为 都 会 发 生。 您 也 可 以 将 这 个 功 能 放 入RowFocusChanged 事 件 中。

为 了 使 这 个 行 选 择 功 能 可 重 用， 这 里 我 们 建 立 两 个 函 数。 一 个 函 数 设 置 我 们 希 望 的 选 择 行 为 类 型， 另 外 一 个 真 正 执 行 该 选 择 行 为。 首 先 我 们 使 用 一 个 实 例 变 量 来 设 定 选 择 行 为 的 值：
protected integer ii_select_behavior
这 个 变 量 中 将 存 放 下 面 的 一 些 值。
可 能 发 生 的 选 择 行 为
值
行 为 0
不 允 许 选 择 行 为 1
只 允 许 有 一 行 选 中 2
自 动 实 现 多 行 选 择 3
允 许 使 用 鼠 标 和 键 盘 组 合 选 择 99
不 允 许 选 择， 将 鼠 标 变 成 手 型 如 果 您 使 用 的 是 保 护 变 量 或 私 有 变 量， 这 就 意 味 着 本 对 象 以 外 的 程 序 无 法 访 问， 因 此 您 必 须 为 其 他 程 序 员 建 立 对 这 些 变 量 赋 值 和 获 取 这 些 变 量 值 的 函 数。 另 外， 您 还 需 要 一 些 函 数 来 执 行 基 于 该 变 量 的 过 程。
函 数：uf_SetSelect(Select_behavior)
我 们 要 写 的 第 一 个 函 数 将 允 许 程 序 员 设 置 选 择 行 为。
函 数 : public integer uf_SetSelect( integer ai_select_behavior)
/* 本 函 数 设 置 数 据 窗 口 的 选 择 行 为 值
下 列 为 有 效 的 选 择 行 为 值 */

CHOOSE CASE ai_select_behavior
    CASE 0, 1, 2, 3, 99
        ii_select_behavior = ai_select_behavior
        //  至 少 一 行 将 被 选 中
        if ai_select_behavior = 1 then
            uf_process_select( GetRow(), "Keyboard" )
        end if
        if ai_select_behavior = 99 then
            SetRowFocusIndicator(Hand!)
        else
            SetRowFocusIndicator(OFF!)
        end if
        return 0
    CASE ELSE
        return -1
END CHOOSE

函 数：uf_ProcessSelect(long al_row, string as_input_type)
一 旦 选 择 类 型 被 设 置， 所 有 的 行 都 必 须 被 处 理。 我 把 这 部 分 代 码 放 入 一 个 叫 作uf_ProcessSelect 的 函 数 中。 这 个 函 数 处 理 选 择 行 为。 您 需 要 告 诉 该 函 数 您 想 要 处 理 的 行 和 该 请 求 是 通 过 鼠 标 还 是 键 盘 发 出 的。 下 面 是 该 函 数：

long		l_row
boolean		b_reset_anchor
boolean		b_keyboard, b_mouse
//  鼠 标 动 作 还 是 键 盘 动 作 ？
if Upper(left(as_input_type,1)) = "K" then
        b_keyboard = TRUE
else
        b_mouse = TRUE
end if
/*	 确 认 鼠 标 点 在 了 数 据 窗 口 的 记 录 上   */
IF al_row <1 THEN Return 1 /* 是 否 要 确 定 起 始 行 */ b_reset_anchor="TRUE" SetRedraw(FALSE) /* 有 效 的 操 作 值 0="不" 允 许 选 择 行 为 1="只" 允 许 有 一 行 选 中 2="自" 动 实 现 多 行 选 择 3="允" 许 使 用 鼠 标 和 键 盘 组 合 选 择 99="不" 允 许 选 择， 将 鼠 标 变 成 手 型*/ CHOOSE CASE ii_select_behavior CASE 0, 99 // 无 CASE 1 // 单 行 选 中 SelectRow(0,FALSE) SelectRow(al_row,TRUE) CASE 2 // 多 行 选 中 if b_mouse then SelectRow(al_row, NOT IsSelected( al_row )) end if CASE 3 IF keydown(KeyShift!) AND KeyDown(KeyControl!) THEN IF il_anchor_row> al_row then
                FOR l_row = il_anchor_row TO al_row STEP -1
                    this.selectrow(l_row,TRUE)
                NEXT
            ELSE
                FOR l_row = il_anchor_row TO al_row
                    this.selectrow(l_row,TRUE)
                NEXT
            END IF
        ELSEIF KeyDown(KeyShift!) Then
            SelectRow(0,FALSE)
            IF il_anchor_row > al_row then
                FOR l_row = il_anchor_row TO al_row STEP -1
                    this.selectrow(l_row,TRUE)
                NEXT
            ELSE
                FOR l_row = il_anchor_row TO al_row
                    this.selectrow(l_row,TRUE)
                NEXT
            END IF
            b_reset_anchor = FALSE
        ELSEIF Keydown(KeyControl!) THEN
            SelectRow( al_row, NOT IsSelected( al_row ) )
        ELSE
            SelectRow(0,FALSE)
            SelectRow(al_row,TRUE)
        END IF
END CHOOSE
SetRedraw(TRUE)
if b_reset_anchor then il_anchor_Row = al_row
return 0

现 在， 当 我 们 希 望 执 行 行 选 择 时， 所 要 做 的 全 部 操 作 就 是 调 用uf_ProcessSelect() 函 数。 一 般 当 用 户 在 一 个 数 据 窗 口 中 点 击 了 鼠 标 或 是 按 下 了 上、 下 箭 头 键 时， 调 用 这 个 函 数。 另 外 还 有 捕 获home 和end 键 的 代 码。 下 面 是we_keydow 用 户 事 件（ 映 射 到Pbm_dwnkey 事 件） 中 的 代 码：

Event: we_keydown (pbm_dwnkey)
Object: Any DataWindow
if KeyDown(KeyDownArrow!) and GetRow() <> RowCount() then
    uf_processSelect( GetRow() + 1 , "Keyboard")
elseif KeyDown(KeyUpArrow!) and GetRow() <> 1 then
    uf_processSelect( GetRow() - 1 , "Keyboard")
elseif KeyDown(KeyHome!) and RowCount() > 0 then
    uf_processSelect( 1, "KeyBoard")
elseif KeyDown(KeyEnd!) and RowCount() > 0 then
    uf_processSelect( RowCount(), "Keyboard")
end if
最 后， 我 们 需 要 在clicked 事 件 中 加 入：
uf_processSelect(GetClickedRow(), "Mouse")

在 这 个 祖 先 函 数 中 编 写 这 样 一 个 行 选 择 的 函 数 只 是 作 为 一 个 简 单 的 实 例， 相 信 您 一 定 能 因 此 收 到 启 发， 编 写 出 更 多 的 函 数， 以 拓 展 数 据 窗 口 的 基 本 功 能。


张健姿-->PowerBuilder系列讲座-->18. 对数据库查询的优化

18. 对数据库查询的优化

我 们 使 用PowerBuilder 编 程 时 总 是 会 大 量 的 用 到 数 据 库 的 查 询 语 句， 对 于 一 条 复 杂 的 查 询 语 句 来 说， 对 相 同 的 查 询 条 件 的 实 现 一 般 总 可 以 有 多 种 不 同 的 表 达 方 法， 而 不 同 的 表 达 会 使 数 据 库 的 响 应 速 度 大 相 径 庭。 据 统 计， 约 有90% 性 能 问 题 是 由 于 应 用 开 发 的 程 序 员 或 用 户 使 用 了 不 恰 当 的 查 询 语 句 而 使 系 统 的 响 应 速 度 减 慢， 因 此 提 高 书 写SQL 语 句 的 质 量 对 软 件 性 能 的 提 高 有 很 大 关 系， 然 而 查 询 语 句 的 好 坏 往 往 是 同 实 际 运 行 系 统 的 数 据 库 结 构、 记 录 的 数 量 等 具 体 情 况 有 关 的， 我 们 无 法 只 用 几 条 简 单 的 普 遍 适 用 的 规 律 来 总 结 应 当 怎 样 优 化 查 询 语 句， 以 得 到 更 高 的 性 能。 不 过 我 们 首 先 应 当 对 数 据 库 管 理 系 统 最 基 本 的 工 作 规 律 有 一 些 了 解， 这 样 才 能 使 我 们 在 对 查 询 进 行 时 优 化 有 所 根 据。

由 于SQL 语 言 是 面 向 结 果 而 不 是 面 向 过 程 的 查 询 语 言， 所 以 一 般 支 持SQL 语 言 的 大 型 关 系 型 数 据 库 都 需 要 使 用 一 个 基 于 成 本 的 优 化 器， 为 一 个 即 时 查 询 提 供 一 个 最 佳 的 执 行 策 略。 对 于 优 化 器， 输 入 是 一 条 查 询 语 句， 输 出 是 一 个 执 行 策 略。 这 个 执 行 策 略 是 执 行 这 个 查 询 所 需 要 的 一 系 列 步 骤。 数 据 库 的 反 应 速 度 经 常 就 体 现 在 这 一 个 优 化 算 法 上。 不 同 的 查 询 策 略 和 查 询 步 骤 可 使 得 服 务 器 的 反 应 不 同， 因 此 采 用 适 当 的 查 询 策 略 可 使 得 系 统 性 能 大 大 的 提 高。

优 化 器 的 优 化 是 基 于 用 户 对 所 查 询 表 的 内 容 和 其 他 一 些 服 务 器 的 有 关 因 素 如Cache 大 小、Cache 策 略、I/O 大 小 等。 一 般 来 说 硬 盘 访 问 是 成 本 最 高 的 操 作， 因 此 对 用 户 来 讲， 使 优 化 器 能 对 字 段 的 索 引 进 行 操 作 是 优 化 查 询 的 关 键。

SQL 查 询 语 句 都 可 以 有 很 多 种 执 行 策 略， 优 化 器 将 估 计 出 全 部 的 执 行 方 法 中 所 需 时 间 最 少 的 也 就 是 所 谓 成 本 最 低 的 那 一 种 方 法。 一 般 来 讲， 最 为 重 要 的 选 择 就 是 使 用 什 么 索 引 和 采 用 何 种 表 的 连 接 手 段， 而 所 有 优 化 的 进 行 都 是 基 于 用 户 所 使 用 的 查 询 语 句 中 的where 子 句。

优 化 器 对where 子 句 中 的 优 化 分 为 以 下 几 类：
搜 索 参 数(searchargument)

搜 索 参 数 的 核 心 就 是 数 据 库 能 否 使 用 表 中 字 段 的 索 引 来 查 询 数 据， 而 不 必 直 接 查 询 记 录 中 的 数 据。 如 带 有=,<,>,>=,<= 等 操 作 符 的 条 件 系 统 就 可 以 直 接 使 用 索 引。

如 下 列 条 件 是 搜 索 参 数：

id = " T0001”
salary  >  30000
a = 1 and c = 7
而下列则不是搜索参数：
salary = commission
dept != 10
salary *12  >=  30000
a = 1 or c = 7
当然优化器有时可以将非搜索参数转化为搜索参数，如：
SELECT  name FROM employee   WHERE  salary   BETWEEN   $10000  AND   $15000
SELECT  name  FROM employee   WHERE  salary  >=  $10000   AND   salary  <= $15000 SELECT name FROM employee WHERE name like "a%" SELECT name FROM employee WHERE name>= "a"  AND   name<= "b" SELECT name FROM employee WHERE salary>  $3000 * 12
SELECT  name  FROM  employee  WHERE  salary  >  $36000

因 此 我 们 在 查 询 中 应 当 提 供 能 一 些 冗 余 的 搜 索 参 数， 使 优 化 器 有 更 多 的 选 择 余 地， 如title 和titleauthor 两 张 表 是 一 对 多 的 关 系， 同 样 的 查 询 条 件 我 们 有 以 下 三 种 表 现 方 法：

SELECT  title_id, title   FROM  titles,  titleauthor
WHERE  title.title_id = titleauthor.title_id
AND  titleauthor.title_id = 'T81002'
SELECT  title_id, title   FROM  titles,  titleauthor
WHERE  title.title_id = titleauthor.title_id
AND  title.title_id = 'T81002'
SELECT  title_id, title   FROM  titles,  titleauthor
WHERE  title.title_id = titleauthor.title_id
AND  title.title_id = 'T81002'
AND  titleauthor.title_id  = 'T81002'

显 然 三 种 方 法 一 种 比 一 种 要 好， 因 为 后 者 为 优 化 器 提 供 了 更 多 的 选 择 机 会。

连 接 条 件

接 连 时 优 化 器 将 所 有 连 接 的 方 法 全 部 列 举 出 来， 计 算 每 一 种 连 接 的 成 本， 选 择 成 本 最 低 的 一 种。 如 连 接 时 用 到 的 数 据 无 法 获 得， 一 般 系 统 会 使 用 平 均 密 度 作 为 依 据， 估 算 可 能 的 命 中 率。 ' 或' 运 算 条 件

当 查 询 语 句 中 有IN 这 样 的 关 键 词 时， 优 化 器 将 转 化 其 中 的 内 容 以OR 并 列 条 件。 例 如：

SELECT * FROM author
WHERE au_lname in ('Berry','Densham')
将转化为：
SELECT * FROM author
WHERE au_lname = 'Berry' or au_lname = 'Densham'

数 据 库 管 理 系 统 将 对 每 一 个OR 从 句 进 行 查 询， 将 所 有 的 结 果 合 并 后 去 除 重 复 项 作 为 最 终 结 果。

基 于 对 上 述 数 据 库 优 化 器 的 了 解， 为 确 保 对 我 们 将 要 执 行 的 查 询 语 句 得 以 进 行 准 确 的 优 化， 我 们 应 注 意 以 下 几 点：

1. 避 免 使 用 不 兼 容 的 数 据 类 型。 例 如float 和int，char 和varchar，binary 和varbinary 是 不 兼 容 的。 数 据 类 型 的 不 兼 容 可 能 使 得 优 化 器 无 法 执 行 一 些 本 来 可 以 进 行 的 优 化 操 作。 例 如：

SELECT  name  FROM  employee
WHERE  salary  >  60000

在 这 条 语 句 中， 如salary 字 段 是money 型 的， 则 优 化 器 很 难 对 其 进 行 优 化， 因 为60000 是 个 整 型 数。 我 们 应 当 在 编 程 是 使 用 一 个 函 数 将 整 型 数 转 化 成 为 钱 币 型 的 数， 而 不 要 等 到 运 行 时 转 化。

2. 如 果 在 一 个 存 储 过 程 或 触 发 器 中， 有 表 达 式 的 值 在 编 译 时 无 法 得 到， 优 化 器 就 只 能 使 用 它 的 平 均 密 度 来 估 计 命 中 的 记 录 数。 例 如：

DECLARE  @value  money
SELECT  name
FROM     employee
WHERE  salary  =  @value

这 样 的 命 令 是 可 优 化 的。 只 是 由 于@value 的 值 在 执 行 前 不 知 道， 它 只 能 使 用 其 平 均 密 度， 来 估 计 这 条 命 令 将 要 命 中 的 记 录 数。

3. 避 免 对 搜 索 参 数 使 用 其 他 数 学 操 作 符。 如 要 将

SELECT  name  FROM  employee
WHERE  SUBSTRING(id, 1, 1) = 'B'
SELECT  name  FROM  emplyee
WHERE  salary  * 12  >  30000
写成为：
SELECT  name  FROM  employee
WHERE  id   like 'B%'
SELECT  name  FROM  emplyee
WHERE  salary   >  3000

4. 避 免 使 用!= 或<> 等 这 样 的 操 作 符， 因 为 这 会 使 系 统 无 法 使 用 索 引， 而 只 能 直 接 搜 索 表 中 的 数 据。 例 如：
SELECT id FROM employee
WHERE id != 'B%'

优 化 器 将 无 法 通 过 索 引 来 确 定 将 要 命 中 的 行 数。
上 述 我 们 提 到 的 是 一 些 基 本 的 提 高 查 询 速 度 的 注 意 事 项， 但 是 在 更 多 的 情 况 下， 程 序 员 往 往 需 要 反 复 地 试 验 比 较 不 同 的 语 句 以 得 到 最 佳 的 方 案。 此 外 更 为 重 要 的 是 需 要 数 据 库 管 理 员 在 数 据 库 的 服 务 器 一 端 调 整 数 据 库 管 理 系 统 的 参 数， 以 得 到 更 快 的 响 应 性 能， 这 就 超 出 了 本 文 的 讨 论 范 围。


张健姿-->PowerBuilder系列讲座-->19. 使用PowerBuilder另外的500个函数(上)

19. 使用PowerBuilder另外的500个函数(上)

乍 一 看 到 这 个 题 目， 我 们 可 能 会 觉 得 很 玄。PowerBuilder 系 统 自 身 提 供 的 函 数 大 约 只 有200 多 个（ 不 考 虑 多 态 函 数）， 我 们 何 从 得 到 更 多 另 外 的500 多 个 函 数 呢 ？

我 们 在 以 前 的 专 题 中 讨 论 过 如 何 使 用WindowsAPI 标 准， 调 用 外 部 函 数 的 问 题。 而 使 用 过WindowsSDK 编 程 的 开 发 人 员 都 知 道， 在USER.EXE、GDI.EXE 等 可 执 行 文 件 和 动 态 连 接 库 中，Windows 本 身 提 供 了 大 量 的 库 函 数， 这 些 函 数 在PowerBuilder 中， 我 们 都 是 可 以 直 接 调 用 的。 这 样 我 们 的 函 数 库 就 一 下 子 又 扩 充 了500 多 个。

在 这 些API 函 数 中， 相 当 一 部 分 对 我 们 是 非 常 有 用 的， 这 里 我 们 可 以 分 类 向 大 家 介 绍 一 些：
窗 口 函 数
自 动 卷 滚 条

缺 省 情 况 下，MDI 表 单 是 可 以 重 新 设 定 大 小 的。 这 样 就 可 以 使 用 户 将 窗 口 设 成 比 您 原 先 的 设 定 更 小。 这 种 情 况 下， 除 非 您 对 窗 口 设 置 了 卷 滚 条 的 属 性， 窗 口 上 的 一 些 控 件 用 户 将 不 能 访 问 到。 而 另 一 方 面， 除 非 窗 口 确 实 小 到 不 足 以 显 示 所 有 的 控 件， 否 则 没 有 必 要 提 供 卷 滚 条。

许 多Windows 的MDI 应 用 在 处 理 这 一 问 题 时 是 这 样 做 的； 当 窗 口 小 到 天 法 显 示 所 有 控 制 时 打 开 卷 滚 条； 而 窗 口 大 小 合 适 时， 关 闭 卷 滚 条。PowerBuilder 系 统 本 身 并 没 有 提 供 自 动 处 理 上 述 操 作 的 特 性， 但 是 我 们 自 己 加 上 这 一 特 性 是 很 容 易 的， 而 且 实 现 上 述 自 动 卷 滚 条 的 功 能 也 用 不 到WindowsAPI 调 用， 只 需 在MDI 表 单 窗 口 的resize 事 件 中 触 发 另 一 个 事 件 以 判 断 在 当 前 窗 口 尺 寸 小 于 某 一 阈 值 时 显 示 卷 滚 条（ 后 面 您 将 了 解 为 什 么 我 们 要 另 外 的 触 发 一 个 事 件， 而 不 直 接 在resize 事 件 中 直 接 做）。 此 后 用 户 在 滚 动 卷 滚 条 时， 窗 口 是 会 自 动 随 之 滚 动 的， 当 用 户 再 次 改 变 窗 口 大 小 并 大 于 该 阈 值 时， 我 们 应 该 关 闭 卷 滚 条， 我 们 仍 然 可 以 不 调 用WindowsAPI 而 做 到 这 一 点。 然 而 在 整 个 例 子 中， 真 正 有 难 度 的 是 在 关 闭 卷 滚 条 之 前， 我 们 需 要 检 查 用 户 是 否 滚 动 了 卷 滚 条。 如 果 用 户 滚 动 了 卷 滚 条， 我 们 则 应 当 首 先 将 窗 口 移 回 原 始 状 态 这 就 要 用 到WindowsAPI 函 数 了。 因 为 我 们 需 要 确 定：(1) 卷 滚 条 的 滚 动 范 围(2) 滚 动 块 在 卷 滚 条 上 的 当 前 位 置。 为 了 做 到 这 一 点， 我 们 需 要 使 用 两 个WindowsAPI 调 用：GetScrollpos 和Getscrollrange。

为 了 使 用 这 两 个API 函 数， 首 先 我 们 在 需 要 使 用 它 们 的 窗 口 中 将 它 们 声 明 为localexternal 函 数。 在Localextemal 函 数 对 话 框 中， 加 入 如 下 语 句：

FunctionintGetScrollPos(uintHandle,intBar)Library"USER.EXE"
SubRoutineGetScrollRange(uintHandle,intBar,RefintMinPos,&
RefintMaxPos)Library"USER.EXE"
同时在该窗口中声明如下的实例变量：
//判断用户是否要最小化窗口
Booleanib_minimized=FALSE
//判断是否显示卷滚框
intii_desired_height=1000
intii_desired_width=2000

其 中 的 两 个 整 形 变 量ii_desired_height 和ii_desired_width 是 用 来 决 定 在 窗 口 的 什 么 时 候 打 开 或 移 去 卷 滚 条 的 阈 值。 当 然， 每 个 窗 口 中 都 必 须 设 置 这 两 个 变 量。 为 了 使 窗 口 更 为 通 用， 我 们 也 可 以 定 义 一 个 窗 口 级 的 函 数， 该 函 数 在 窗 口 的open 事 件 中 能 分 析 窗 口 的control[] 属 性 也 就 是 窗 口 内 所 有 控 件 的 最 大 值， 以 决 定 应 将 那 两 个 整 形 变 量 设 置 为 多 少。

那 个 布 尔 型 的 实 例 变 量ib_minimized 是 用 来 确 定 用 户 是 否 正 要 将 窗 口 最 小 化。 如 果 是， 则 我 们 处 理 卷 滚 条 时 会 有 些 不 同， 稍 后 我 们 将 讨 论 这 种 情 况。

我 们 在open 事 件 中， 加 入 如 下 的 语 句：

this.LinesPerPage=10
this.ColumnsPerPage=10
this.UnitsPerLine=this.ii_desired_height*.009
this.UnitsPerColumn=this.ii_desired_width*.009

接 下 来 我 们 要 做 的 是 监 视resize 事 件， 根 据 用 户 改 变 窗 口 大 小 的 不 同 方 式， 改 变 卷 滚 条 的 状 态。 这 里 的 问 题 是， 在 移 去 或 加 上 卷 滚 条 时 会 引 起 另 一 个resize 事 件。 因 此， 在resize 事 件 中 直 接 改 变 卷 滚 条 的 状 态 会 引 起 递 归 调 用resize 事 件， 这 是 我 们 不 希 望 发 生 的。

为 了 防 止 递 归 调 用， 我 们 要 定 义 一 些 的 用 户 自 定 义 事 件， 在 这 些 事 件 中 处 理 卷 滚 条 的 移 去 或 加 入。 在 用 户 事 件 对 话 框 中， 输 入 如 下 的 值：

EventName
EventID

addhscroll
pbm_custom01

addvscroll
pbm_custom02

removehscroll
pbm_custom03

removevscroll
pbm_custom04

syscommand
pbm_syscommand加入前四个自定义用户事件的语句为：
ADDHSCROLL事件
//加入卷滚条
this.hscrollbar=TRUE

ADDVSCROLL事件
//加入卷滚条
this.vscrollbar=TRUE

REMOVEHSCROLL事件
uint		WinHandle
integer	ScrollPos,MinPos,MaxPos
//得到窗口句柄
WinHandle=Handle(this)
//得到滚动块在卷滚条上的当前位置
ScrollPos=GetScrollPos(WinHandle,0)
//得到卷滚条的滚动范围
GetScrollRange(WinHandle,0,MinPos,MaxPos)
//滚动窗口
IF ScrollPos > MinPos THEN 	Send ( WinHandle, 276, 5, 0 )
ScrollPos = GetScrollPos ( WinHandle, 0 )
//消除滚动条
this.hscrollbar=FALSE

REMOVEVSCROLLuserevent
uint		WinHandle
integer	ScrollPos,MinPos,MaxPos
WinHandle = Handle ( this )
ScrollPos = GetScrollPos ( WinHandle, 1 )
GetScrollRange ( WinHandle, 1, MinPos, MaxPos )
IF ScrollPos > MinPos THEN	Send ( WinHandle, 277, 6, 0 )
this.vscrollbar = FALSE
我们还需要在resize事件中增加下列代码以决定
什么时候调用这些自定义用户事件。
RESIZEevent
//如用户进行的是最小化窗口的操作，则返回。
IF ib_minimized THEN
    Return
ELSE
    //否则考察是否需要卷滚条
    IF this.width  ii_desired_width AND this.hscrollbar THEN PostEvent
( this, "RemoveHScroll" )
    IF this.height > ii_desired_height AND this.vscrollbar THEN PostEvent
( this, "RemoveVScroll" )
END IF

在SYSCOMMAND 事件中有下列代码：
SYSCOMMANDevent
//如用户试图最小化窗口，则立即取消卷滚条
IF message.wordparm = 61472 THEN
    ib_minimized = TRUE
    this.vscrollbar = FALSE
    this.hscrollbar = FALSE
ELSE
    ib_minimized = FALSE
END IF

工 作 原 理

OPEN 事 件 中 的 代 码 是 为 了 调 整 卷 滚 条 的 滚 动 速 度。AddHScroll 和AddVScroll 中 的 代 码 分 别 加 入 水 平 和 垂 直 卷 滚 条。RemoveHScroll 和RemoveVScroll 除 分 别 移 去 水 平 和 垂 直 卷 滚 条 外， 还 要 使 用WindowsAPI 调 用。

GetScrollPos 函 数 可 返 回 滚 动 块 在 卷 滚 条 上 的 当 前 位 置。 我 们 必 须 告 诉 该 函 数 我 们 需 要 的 是 哪 个 窗 口 上 的 哪 个 卷 滚 条 的 信 息。 在WindowsAPI 中，“ 指 向” 一 个 窗 口 的 方 法 是 通 过 引 用 它 的“ 句 柄”。PowerBuilder 提 供 了 获 得 应 用 中 的 指 定 窗 口 句 柄 的 函 数， 这 个 函 数 叫 作Handle（）。 因 为 这 些 自 定 义 用 户 事 件 是 在 窗 口 自 身 当 中 定 义 的， 所 以 我 们 只 需 要 将“this” 作 为 参 数 传 递 给Handle 函 数。0 做 为 第 二 个 参 数， 告 诉Handle 函 数 我 们 需 要 知 道 水 平 卷 滚 条 的 值； 而 传 递1 则 表 示 需 要 知 道 垂 直 卷 滚 条 的 值。

通 过 将 当 前 卷 滚 位 置 与 最 小 位 置 相 比 较， 我 们 就 会 知 道 用 户 是 否 滚 动 过 窗 口。 如 果 滚 动 过 窗 口， 我 们 就 向 窗 口 发 送 一 个 消 息 告 诉 它 将 窗 口 恢 复 原 状。 与WM_VSCROLL 和WM_HSCROLL 消 息 相 对 应 的 消 息 号 分 别 是227 和226。 在WM_VSCROLL 中， 我 们 发 送SB_TOP(6)， 它 使 窗 口 滚 动 到 顶 部。 在WM_HSCROLL 中， 我 们 发 送5 使 窗 口 滚 动 到 左 边。

当 用 户 试 图 最 小 化 或 最 大 化 窗 口 时， 卷 滚 条 的 行 为 会 有 不 同。 我 们 知 道，PowerBuilder 的resize 事 件 所 映 射 的Windows 事 件 —WM_SIZE 是 在 一 个 窗 口 被 改 变 大 小 以 后 触 发 的。 而 当 用 户 试 图 最 小 化 一 个 窗 口 时， 我 们 需 要 在 最 小 化 发 生 之 前 关 闭 卷 滚 条。 因 此， 在 这 种 情 况 下resize 事 件 中 的 代 码 是 来 不 及 起 作 用 了。 在 这 里， 我 们 采 取 的 方 法 是 定 义 了 一 个 映 射 到WM_COMMAND 消 息 的 事 件。 当 用 户 试 图 最 小 化 窗 口 时， 随 该 消 息 一 起 发 送 的 值 是61472。 因 此， 如 果 该 事 件 的 值 是61472， 我 们 只 需 简 单 地 移 去 卷 滚 条。 问 题 是 完 成 这 些 操 作 后， 系 统 还 将 激 发 一 个resize 事 件，resize 事 件 中 是 引 入 实 例 变 量ib_minimized 的 地 方。 在syscommand 事 件 中 我 们 设 定 实 例 变 量， 在resize 事 件 中， 检 查 该 变 量。 该 变 量 告 诉resize 事 件 用 户 正 在 最 小 化 窗 口， 于 是resize 事 件 就 不 会 处 理 卷 滚 条。 如 果 用 户 将 窗 口 复 原，syscommand 事 件 将 再 次 被 激 发， 但 这 次 传 递 给 消 息 的 值 就 不 是61472 了。 因 此syscommand 事 件 中 仅 仅 将 实 例 变 量 标 识 复 位。 于 是resize 事 件 就 又 能 处 理 卷 滚 条 了。


张健姿-->PowerBuilder系列讲座-->20.21. 使用PowerBuilder另外的500个函数（中、下）

20.21. 使用PowerBuilder另外的500个函数（中、下）

上 期 我 们 介 绍 了 调 用WindowsAPI 函 数 的 方 法， 本 期 我 们 继 续 介 绍 几 个 常 用 的API 函 数。

一 个 简 单 的 文 件 拷 贝 例 程

Windows 操 作 系 统 在 象 文 件 操 纵 一 类 的 低 级 函 数 调 用 方 面 是 相 当 灵 活 的。 而 在PowerBuilder 中 为 了 实 现 这 一 些 低 级 操 作， 开 发 者 们 必 须 用 第 三 方 开 发 库（ 如：FUNCkyforPowerBuilder 库） 或 用C 语 言 之 类 的 语 言 开 发 自 己 的 库， 这 种 方 法 我 们 将 在 后 面 的 章 节 中 讨 论。 在 这 里， 我 们 首 先 展 示 如 何 通 过 调 用WindowsAPI 函 数 实 现 简 单 而 又 常 用 的 低 级 操 作： 文 件 拷 贝。

使 用WindowsAPI 而 不 用 第 三 方 开 发 库 的 主 要 好 处 是， 如 果 这 是 您 所 要 的 唯 一 的 低 级 操 作， 在 发 布 您 的 应 用 时 就 不 需 要 同 时 将 额 外 的 库 打 包 进 您 的 应 用 中。 但 如 果 您 还 需 使 用 了 第 三 方 开 发 库 提 供 的 其 它 服 务， 为 简 单 起 见， 您 使 用 第 三 方 开 发 库 所 提 供 的 例 程 可 能 要 更 方 便 些。

实 现： 首 先 声 明 下 列localexternal 函 数

Function  long  LZOpenFile(string  FileName,  REF ws_ofstruct  FileStructure,
uint  style) Library "lzexpand.dll"
Function  long  CopyLZFile(uint  SourceHandle, uint  DestHandle)
Library  "lzexpand.dll"
Function  long  LZClose (uint  FileHandle)  Library  "lzexpand.dll"
ws_ofstruct的窗口级结构如下：
Variable Name
Data Type

byte[1]
char

fixed_disk[1]
char

errorcode
uint

reserverd[4]
char

pathname[128]
char键入下面的一个窗口级函数：
wf_copy
// 返 回:	boolean
// 参 数:
    string	as_source
    string	as_dest
long		ll_SourceFileHandle, ll_DestFileHandle, ll_result
ws_ofstruct	   lstr_SourceFileStructure, lstr_DestFileStructure
// 打 开 源 文 件
ll_SourceFileHandle = LZOpenFile ( as_source, lstr_SourceFileStructure, 0 )
IF ll_SourceFileHandle = -1 THEN
    MessageBox ( " 程 序 错 误", " 无 法 打 开 文 件: " + as_source )
    Return FALSE
END IF
// 打 开 目 标 文 件
ll_DestFileHandle = LZOpenFile ( as_dest, lstr_DestFileStructure, 4096 )
IF ll_DestFileHandle = -1 THEN
    MessageBox ( " 程 序 错 误", " 无 法 创 建 文 件: " + as_dest )
    LZClose ( ll_SourceFileHandle )
    Return FALSE
END IF
// 拷 贝
ll_result = CopyLZFile ( ll_SourceFileHandle, ll_DestFileHandle )
IF li_result <0 THEN MessageBox (" 程 序 错 误"," 无 法 拷 贝 文 件: " + as_source & + " to file: " + as_dest ) LZClose ( ll_SourceFileHandle ) LZClose ( ll_DestFileHandle ) Return FALSE END IF // 关 闭 文 件 LZClose ( ll_SourceFileHandle ) LZClose ( ll_DestFileHandle ) Return TRUE

工 作 原 理

Windows 所 带 的COMPRESS.EXE 是 一 个 处 理 采 用Lempel-ziv 算 法 压 缩 的 文 件 的 可 执 行 文 件。 此 外Windows 内 部 还 包 含 了 几 个 处 理 这 一 算 法 的 函 数， 其 中 之 一 是LzCopy 函 数， 该 函 数 可 以 用 来 生 成 解 压 缩 文 件。 同 时LzCopy 函 数 的 文 档 中 还 说 明 了：“ 如 果 原 文 件 未 被 压 缩， 该 函 数 复 制 原 文 件。” 于 是LzCopy 函 数 也 可 以 用 作 一 个 快 速 的 文 件 拷 贝 函 数。

LzCopy 函 数 有 两 个 参 数， 原 文 件 和 目 标 文 件 的 句 柄。 但 是 不 幸 的 是，PowerBuilder 提 供 的FileOpen 函 数 返 回 的 文 件 句 柄 并 不 是LzCopy 所 需 要 的 那 个 句 柄。 因 此， 如 果 调 用WindowsAPI 中 的LzCopy 函 数， 我 们 必 须 同 时 使 用LzOpenFile 和LzClose 函 数， 以 确 保 获 得 所 需 要 的 文 件 句 柄。

LzFileOpen 函 数 有 三 个 参 数。 第 一 个 参 数 是 将 要 打 开 的 文 件 名， 第 二 个 参 数 是 一 个 结 构， 这 个 结 构 将 在 函 数 调 用 时 填 入 有 关 这 个 文 件 的 信 息， 与PowerBuilder 的FileOpen 函 数 一 样，LzFileOpen 函 数 可 以 创 建 或 打 开 一 个 文 件。 该 函 数 的 第 三 个 参 数 指 明 该 函 数 是 被 用 来 以 只 读 方 式 打 开 文 件(0) 还 是 以 只 写 方 式 创 建 文 件(4096)。 当 我 们 打 开 了 源 文 件 和 目 标 文 件 后， 我 们 只 要 简 单 地 将 返 回 的 这 两 个 文 件 的 句 柄 传 递 给LzCopy 函 数， 就 可 实 现 文 件 拷 贝。 最 后， 在 退 出 之 前 调 用LzClose 函 数 来 关 闭 这 两 个 文 件。

判 断 另 一 应 用 是 否 正 在 运 行

实 现： 首 先 声 明 下 列localexternal 函 数

Function uint  GetModuleHandle(string ModuleName)  Library "kernel"
Function uint  GetModuleUsage(uint  ModuleName)  Library "kernel"
Function uint  FindExecutable(string  FileName, &
    REF string Directory,  REF string Result)  Library "shell"
键入下面的一个窗口级函数：
fw_check_app_status
// 返 回:	boolean
// 参 数:
//	string  as_filename
//	string  as_directory
integer	li_size = 144, li_result
string	ls_executable = Space ( li_size )
integer	li_module_usage
uint		li_module_handle
li_result = FindExecutable ( as_filename, as_directory, ls_executable )
IF li_result <32 THEN Return FALSE //如可执行文件已载入，获取这个执行文件的句柄 li_module_handle="GetModuleHandle" ( ls_executable ) IF li_module_handle < 1 THEN // 没 有 载 入 Return FALSE ELSE // 获 取 有 多 少 个 实 例 被 载 入 li_module_usage="GetModuleUsage" ( li_module_handle ) Return TRUE END IF

工 作 原 理

这 段 程 序 可 以 获 取 某 个 应 用 是 否 正 在 运 行。

我 们 用windowsAPI 中 的GetModueHandle 函 数 来 判 定 是 否 另 一 个 应 用 正 在 运 行。 为 了 做 到 这 一 点， 我 们 需 要 知 道 文 件 的 全 路 径 名( 包 括 驱 动 器 名 和 路 径)。 我 们 可 以 将 它 包 含 在 应 用 的INI 文 件 中， 也 可 以 通 过 写 入 源 代 码 将 它 嵌 入 可 执 行 程 序。 这 里 向 大 家 推 荐 的 是 采 用 前 者 的 方 法， 这 样 作 可 使 应 用 具 有 更 多 的 灵 活 性。 在 上 面 的 窗 口 级 函 数 中， 我 们 首 先 在 用 户 的Windows 目 录 中 查 找win.ini 文 件。 因 为 对 于 我 们 需 要 进 行 查 询 的 应 用 与 特 定 扩 展 名 的 文 档 之 间 的 关 联 被 记 录 在WIN.INI 文 件 中。 于 是 我 们 所 要 做 的 第 一 件 事 就 是 在WIN.INI 文 档 中 查 找 这 一 关 联， 以 知 道 我 们 所 要 寻 找 应 用 是 从 通 过 哪 个 软 件 装 入 的。 我 们 可 以 自 己 搜 索WIN.INI 文 件， 或 者 调 用WindowsAPI 中 的FindExecutable 函 数 来 得 到 这 一 信 息。 这 里， 我 们 使 用 了 第 二 种 方 法。 我 们 只 要 将 与 我 们 所 希 望 寻 找 的 可 执 行 程 序 相 关 联 的 一 个 文 件 的 文 件 名 与 路 径 传 递 给FindExecutable 函 数， 就 会 得 到 该 可 执 行 程 序 的 全 路 径 名。 这 在 我 们 判 断 出 该 可 执 行 程 序 没 有 在 运 行 以 后 启 动 该 程 序 时 是 很 有 用 的。

一 旦 获 得 了 可 执 行 程 序 的 全 路 径 名, 我 们 将 它 传 递 给GefModuleHandle 函 数。 如 果 应 用 正 在 运 行， 则 该 函 数 返 回 应 用 的 句 柄。 如 果 返 回0， 说 明 应 用 并 没 有 装 入， 此 时 退 出 函 数。 如 果 应 用 正 在 运 行， 我 们 将 获 得 的 应 用 的 句 柄 传 递 给GetModuleUsage 函 数。 这 个 函 数 的 返 回 值 会 告 诉 我 们 这 个 应 用 有 几 个 不 同 的 实 例。 在 我 们 的 例 子 中， 如 查 询 的 应 用 是MicrosoftWord， 该 应 用 是 不 允 许 多 个 实 例 同 时 运 行， 但 象NotePad 这 样 的 应 用， 这 些 信 息 对 还 是 非 常 有 用 的。

激 活 另 一 个 应 用

实 现： 首 先 声 明 下 列localexternal 函 数

Subroutine  BringWindowToTop(int hWnd)  Library "User"
Function      uint  GetNextWindow(uint hWnd, int Flag)  Library "user"
Function      uint  GetWindowText(uint hWnd, REF string Title, uint Size )
Library  "user"
Function      uint  ShowWindow(uint  hWnd, int  CmdShow)  Library "user"
Function    integer  IsIconic(uint  hWnd)  Library "user"
Function    integer  OPenIcon(uint hWnd)  Library  "user"
Function    integer  FindWindow(string  ClassName,  string  WindowName)
Library  "user"
Function    integer  FindWindow(long    ClassName,  string  WindowName)
Library  "user"
Function    integer  FindWindow(string  ClassName,  long    WindowName)
Library  "user"
键入下面的一个窗口级函数：
fw_make_app_active
// 返 回：boolean
// 参 数：
//string   as_title
boolean	lb_cont = TRUE
integer	li_result, li_size
string		ls_title
uint		li_handle
li_handle = Handle ( this )
li_size = Len ( at_title ) + 1

//li_handle = FindWindow ( 0, "Control Panel" )

DO WHILE lb_cont
    li_handle = GetNextWindow ( li_handle, 1 )
    If li_handle = Handle ( this ) THEN Return FALSE
    li_result = GetWindowText ( li_handle, ls_title, li_size )
    IF ls_title = as_title THEN lb_cont = FALSE
LOOP

//ShowWindow ( li_handle, 3 )

IF IsIconic ( li_handle ) > 0 THEN
    OpenIcon ( li_handle )
ELSE
    BringWindowToTop ( li_handle )
END IF
Return TRUE

工 作 原 理

获 得 另 一 个 应 用 最 顶 层 窗 口 引 用 的 最 简 单 的 方 法 就 是 用FindWindow 函 数 来 查 找。FindWindow 函 数 有 两 个 参 数： 您 要 找 的 窗 口 的 类 名 和 窗 口 的 标 题。 注 意， 我 们 在localexternal 函 数 中 用 三 种 方 式 声 明 了 这 一 函 数。 这 是 因 为 您 可 以 用null 作 为“ 通 配 符” 分 别 传 递 两 个 参 数。 为 了 传 递null， 您 必 须 将 参 数 类 型 定 义 成long 型， 其 值 为0。 所 以 我 们 需 要 为 每 一 种 可 能 的 调 用 形 式 定 义 一 个localexternal 函 数。 当 我 们 真 正 使 用 这 个 函 数 时，PowerBuilder 自 动 判 断 我 们 要 用 哪 一 种 形 式。

例 如 在 窗 口 函 数fw_make_app_active 中， 我 们 可 以 用 这 种 手 段 使Findwindow 函 数 搜 索ControlPanel 窗 口。 接 下 来 只 要 将Findwindow 函 数 返 回 的 窗 口 句 柄 传 递 给BringWindowToTop 函 数 就 可 以 激 活ControlPanel 窗 口 了。

但 是 这 样 的 做 法 有 这 样 两 点 缺 陷：

您 并 不 总 是 知 道 某 个 应 用 的 顶 层 窗 口 的 全 名。 例 如 当 您 使 用MicosotWord， 当 前 打 开 的 文 档 的 名 字 也 将 出 现 在 窗 口 的 标 题 上。 因 此， 这 种 情 况 下， 寻 找 顶 层 窗 口 需 要 另 一 种 方 法。

如 果 您 希 望 激 活 的 窗 口 正 处 于 最 小 化 状 态，BringWindowToTop 函 数 将 不 能 正 常 工 作。

实 际 上， 上 面 提 到 的 第 二 个 问 题 比 较 容 易 解 决。 我 们 加 入IsIconic 函 数 判 断 我 们 所 要 激 活 的 窗 口 是 否 最 小 化。 如 果 不 是 最 小 化 状 态， 就 使 用BringWindowsToTop 函 数。 如 果 是 最 小 化 状 态， 就 用OpenIcon 函 数 来 激 活 该 窗 口。 这 种 方 法 只 假 定 我 们 想 以 窗 口 最 小 化 前 的 大 小 及 位 置 显 示 该 窗 口。 我 们 也 可 以 用ShoWindow 函 数 来 对 要 显 示 的 其 它 应 用 的 窗 口 施 以 更 多 的 控 制。

本 例 中， 我 们 传 送 一 个 参 数 使 被 激 活 窗 口 以 最 大 化 方 式 显 示。 如 果 您 想 以 不 同 的 方 式 激 活 窗 口， 请 参 照 下 表 的 参 数 对 应：

0
隐 藏 窗 口

1
以 窗 口 原 来 的 大 小 和 位 置( 如 果 当 前 窗 口 处 于 最 大 化 或 最 小 化 状 态) 并 激 活 该 窗 口( 与 值 相 同)

2
以 最 小 化 方 式 显 示 并 激 活 窗 口

3
以 最 大 化 方 式 显 示 并 激 活 窗 口

4
以 窗 口 最 近 一 次 的 大 小 和 位 置 显 示 但 不 激 活 窗 口

5
以 窗 口 最 近 一 次 的 大 小 和 位 置 显 示 并 激 活 窗 口

6
最 小 化 窗 口 并 在 系 统 列 表 中 激 活 顶 层 窗 口

7
最 小 化 窗 口 但 不 激 活 窗 口( 保 持 当 前 窗 口 激 活)

8
以 窗 口 的 当 前 状 态 显 示 但 并 不 激 活 它( 保 持 当 前 窗 口 激 活)

9
恢 复 窗 口 原 先 的 大 小 和 位 置( 如 果 窗 口 处 于 最 小 化 或 最 大 化 状 态) 并 激 活 它 解 决 第 二 个 问 题 时， 我 们 声 明 了 另 外 两 个 函 数，GetNextWindow 和GetWindowsText。 如 果 我 们 将 一 个 窗 口 句 柄 传 递 给GetNextWindow 函 数， 它 将 在 父 窗 口 所 属 的 所 有 子 窗 口 之 间 循 环。 如 果 将 一 个 顶 层 窗 口 句 柄 传 递 给 该 函 数， 该 函 数 将 在 所 有 顶 层 窗 口 之 间 循 环。 我 们 希 望 得 到 的 是 后 一 种 方 式， 所 以 我 们 首 先 要 获 取 自 己 应 用 的 顶 层 窗 口 句 柄。

当 我 们 在 顶 层 窗 口 之 间 循 环 时， 用GetWindowText 函 数 返 回 它 们 的 标 题。 这 个 函 数 有 三 个 参 数， 窗 口 句 柄， 一 个 用 以 返 回 标 题 的 字 符 串 以 及 返 回 字 符 串 的 最 大 长 度。 我 们 要 好 好 利 用 最 后 一 个 参 数， 因 为 我 们 并 不 想 要 匹 配 整 个 窗 口 标 题， 而 是 要 匹 配 能 保 证 我 们 发 现 所 要 寻 找 的 窗 口 的 长 度。 例 如， 如 果 我 们 将“MicrosoftWord” 传 递 给 这 个 窗 口 函 数 时， 这 个 函 数 判 断 文 本 的 长 度 并 将 它 加1， 这 样 只 将 我 们 将 要 比 较 的 串 的 长 度 传 递 给 了GetWindowText 函 数， 该 函 数 在 顶 层 窗 口 之 间 循 环， 寻 找 以 该 字 符 串 开 头 的 窗 口。 如 果 我 们 发 现 了 一 个 匹 配， 就 设 置 结 束 循 环 标 志， 退 出 循 环 并 继 续 执 行。 如 果 我 们 没 有 发 现 匹 配， 将 在 循 环 到 我 们 自 己 的 应 用 窗 口 时 退 出 循 环。 这 时， 我 们 简 单 地 退 出 函 数。


张健姿-->PowerBuilder系列讲座-->22. 使用树型视图控件对数据库查询的优化

22. 使用树型视图控件对数据库查询的优化

PowerBuilder5.0 自 去 年5 月 发 布 以 来， 至 今 已 有 一 年 多 了。 但 是 笔 者 却 时 常 看 到 不 少 用 户 对5.0 的 新 特 性 缺 乏 了 解， 在 开 发 中 不 能 充 分 的 利 于5.0 的 新 特 性, 或 是 向 笔 者 提 出 一 些 属 于 是 不 同 版 本 间 差 异 的 问 题， 其 主 要 原 因 一 方 面 由 于 许 多 有 经 验 的， 较 早 从 事 开 发 的 程 序 员 是 从4.0 以 下 开 始 的， 他 们 向 初 学 者 传 授 的 编 程 经 验 较 多 的 以4.0 为 主， 而 且 市 场 上 大 多 数 的PowerBuilder 的 技 术 书 籍 也 是 以4.0 为 基 础， 而 另 一 方 面5.0 版 本 的 升 级 同 老 版 本 相 比 有 了 许 多 根 本 性 的 改 变， 它 所 新 增 及 扩 充 的 功 能 要 较 以 往 的 版 本 升 级 为 多。 从 本 期 起， 我 们 将 陆 续 介 绍 一 些PowerBuilder5.0 的 新 特 性。 5.0 在 窗 口 的 控 件 中 增 加 了 几 个 在Windows95 中 新 增 的 控 件， 如 标 签 形 式（Tab Folder）、 树 型 视 图 （TreeView）、 列 表 视 图（ListView）， 而 且 这 些 控 件 在Windows3.x 的 版 本 中 也 能 正 常 使 用， 这 样 就 大 大 丰 富 了 窗 口 的 表 现 形 式， 特 别 是 针 对Windows3.x 平 台。

我 们 首 先 来 介 绍 树 型 视 图 控 件。

树 视 图 控 件 最 适 合 显 示 具 有 层 次 关 系 的 数 据， 在Windows95 中 文 件 和 文 件 夹（ 子 目 录） 之 间 的 关 系 就 是 用 树 型 视 图 来 表 现 的。 在 数 据 库 中 树 型 视 图 是 一 个 管 理 大 量 数 据 的 一 个 好 方 法， 因 为 用 户 只 需 简 单 地 点 击 鼠 标 就 可 以 选 择 自 己 需 要 的 数 据。

当 我 们 对 数 据 的 表 现 有 以 下 要 求 时， 我 们 可 首 先 考 虑 使 用 树 型 视 图 控 件：

* 显 示 层 次 结 构 中 元 素 之 间 的 关 系
* 元 素 在 层 次 结 构 之 间 漫 游（ 元 素 间 的 拷 贝、 移 动 等）
* 描 述 每 个 元 素 相 关 信 息
* 将 大 量 数 据 检 索 的 过 程 划 分 为 若 干 步 骤， 只 表 现 其 中 需 要 的 部 分

例 如 在PowerBuilder5.0 所 包 含 的 例 子Example50 中， 就 有 适 用 于 树 型 列 表 的 形 式 来 表 现 的 实 例： 在 公 司 的 业 务 部 门 中 有 若 干 名 业 务 员， 他 们 每 个 人 分 别 掌 握 着 一 定 数 量 的 客 户， 这 些 客 户 都 曾 经 同 本 公 司 签 订 了 一 些 合 同， 每 份 合 同 中 又 包 括 有 向 公 司 订 购 的 不 同 的 产 品。 因 此 我 们 需 要 表 现 这 些 信 息 中 这 样 的 层 次 关 系： 业 务 员---> 客 户---> 合 同---> 产 品。 又 如 一 个 新 闻 单 位 下 属 了 几 个 不 同 的 部 门， 每 个 部 门 有 若 干 记 者， 每 个 记 者 在 不 同 时 期 又 完 成 了 一 定 数 量 的 稿 件。 当 这 个 新 闻 单 位 的 主 编 审 稿 或 统 计 记 者 工 作 业 绩 时， 也 可 以 采 用 树 型 视 图 的 形 式 表 现 部 门---> 记 者---> 稿 件 的 层 次 信 息。

对 树 型 视 图 的 编 程 是 窗 口 中 较 为 复 杂 的 部 分， 它 无 法 象 数 据 窗 口 那 样 使 用 一 条Retrieve() 函 数 就 可 以 将 数 据 表 现 出 来。 在PowerBuilder 中 树 型 视 图 控 件 包 括 了 树 型 视 图(TreeView) 和 树 型 视 图 项(TreeViewItem) 两 个 对 象。 其 中 树 型 视 图 对 象 可 以 在 窗 口 画 笔 中 通 过 点 击 鼠 标 的 方 式 创 建， 而 树 型 视 图 项 则 必 须 通 过 编 程 来 声 明 及 定 义 它 们 的 属 性。

当 您 在 窗 口 画 笔 中 用 鼠 标 在 窗 口 上 放 置 了 一 个 树 型 视 图 的 控 件， 这 表 明 您 将

认 可 将 在 窗 口 的 这 个 位 置 采 用 树 型 视 图 来 表 现 一 些 信 息；

您 可 以 通 过 设 置 这 个 控 件 的 属 性 确 定 这 个 控 件 是 否 可 见， 是 否 显 示 连 线 等 基 本 属 性。

而 这 时 运 行 这 个 窗 口， 您 看 到 的 只 是 这 个 树 型 控 件 的 空 架 子， 其 中 没 有 任 何 数 据。

树 型 控 件 的 每 一 个 数 据 项 都 是 一 个TreeViewItem， 而 这 些 树 型 视 图 项 您 只 能 在 程 序 中 逐 项 加 入。 一 般 来 讲， 当 数 据 量 很 小 而 且 数 据 间 没 有 复 杂 的 关 系 时， 可 以 在 树 型 视 图 创 建 时 就 使 用 预 先 直 接 把 各 项 加 入 到 树 视 图 中 。 而 数 据 较 多 时， 则 根 据 需 要 来 制 定 树 视 图， 不 要 制 定 整 个 树 视 图 控 件 的 每 一 层 的 每 一 项， 而 是 将 用 户 检 索 数 据 的 过 程 划 分 为 若 干 步 骤。 只 是 在 用 户 打 开 一 个 具 体 的 树 视 图 项 时， 才 根 据 用 户 的 需 要 检 索 数 据。

在 根 据 需 要 制 定 时， 你 可 以 在 某 一 项 被 选 中 时 制 定 该 项 的 下 一 层， 也 可 以 在 某 一 项 被 选 中 时 制 定 该 项 的 下 两 层。 第 二 种 方 法 可 以 保 证 打 开 按 钮 与 数 据 同 步。

将 树 型 视 图 项 加 入 到 树 视 图 中 的 方 法 是 使 用InsertItem() 系 列 函 数 中 的 一 种， 其 中：
InsertItemFirst() 将 加 入 项 作 为 第 一 个 子 项；
InsertItemLast() 将 加 入 项 作 为 最 后 一 个 子 项；
InsertItem() 将 加 入 项 放 在 指 定 的 子 项 后 面；
InsertItemSort() 按 排 序 的 顺 序 放 置 加 入 项。
* 记 住 表 示 层 次 结 构 中 的 根 节 点 的 句 柄 为0。
在 制 定 树 型 视 图 控 件 时， 主 要 使 用 的 事 件 有：
*ItemPopulate（ 只 有 在 选 中 的 项 第 一 次 被 打 开 时 才 被 激 活； 然 后 再 打 开 该 项 时 这 个 事 件 不 再 被 激 活 ）
* ItemExpanding
* ItemExpanded

在 关 闭 树 型 视 图 的 下 一 层 数 据 时， 下 列 事 件 发 生：

* ItemCollapsing
* ItemCollapsed

请 注 意ItemPopulate 这 一 事 件 发 生 的 特 点。 如 果 数 据 是 静 态 的， 我 们 只 须 使 用ItemPopulate 事 件， 对 树 型 视 图 控 件 中 的 每 一 项 制 定 一 次 就 可 以 了。 如 果 数 据 是 动 态 的， 那 么 在 每 一 个 数 据 项 被 打 开 时 我 们 都 要 刷 新 它。 仅 使 用ItemPopulate 事 件 就 不 能 满 足 需 要 了， 还 必 须 使 用 其 它 四 个 事 件 的 组 合： 用 一 个 打 开 事 件 制 定 被 选 择 项 的 下 层 数 据。 用 一 个 关 闭 事 件 删 除 被 选 择 项 的 下 层 数 据。 下 次 再 打 开 这 项 时， 还 要 重 新 制 定 它。

例1： 下 面 的 例 子 制 定 一 个 简 单 树 视 图 控 件 的 前 两 层， 该 树 视 图 控 件 用 于 显 示 一 个CD 集 的 类 别， 歌 手 以 及 唱 片。 在Constructor 事 件 中 编 写 脚 本。 用 一 个DataStore 搜 集 数 据。 代 码 在DataStore 中 循 环， 每 个 循 环 一 行， 用 类 别 号 和 类 别 名 字 制 定 树 视 图 的 第 一 层。 在 显 示 歌 手 号 时 ， 制 定 第 二 层。

Integer  li_rowcount, li_row, li_current_categoryid,
li_last_categoryid, li_current_artistid, li_last_artistid
TreeViewItem	ltvi_level_one, ltvi_level_two
DataStore	lds_data
Long  ll_handle_level_one, ll_handle_level_two
lds_data = CREATE datastore
lds_data.DataObject = "d_category_artist_albums"
lds_data.SetTransObject(SQLCA)
li_rowcount = lds_data.Retrieve()

//在DataStore循环以定制TreeView的数据项
FOR li_row=1 TO li_rowcount
li_current_categoryid = Integer(lds_data.Object.category_id[li_row])
li_current_artistid = Integer(lds_data.Object.artist_id[li_row])
IF IsNull(li_current_artistid) THEN li_current_artist = 0
IF li_current_categoryid <> li_last_categoryid THEN
//定制树型视图的第一级，当歌手的编号有效时设置其下一层的属性
ltvi_level_one.label = String(lds_data.Object.category_name[li_row])
ltvi_level_one.data = li_current_categoryid
ltvi_level_one.pictureindex = 1
ltvi_level_one.selectedpictureindex = 2
ltvi_level_one.children = (li_current_artistid>0)
ll_handle_level_one = This.InsertItemLast(0,ltvi_level_one)
END IF
IF li_current_artistid <> li_last_artistid THEN
IF li_current_artistid>0 THEN
    ltvi_level_two.label = String(li_current_artistid)
    ltvi_level_two.pictureindex = 3
    ltvi_level_two.selectedpictureindex = 3
    ltvi_level_two.data = li_current_artistid
    ltvi_level_two.children = true
    ll_handle_level_two = This.InsertItemLast(ll_handle_level_one,ltvi_level_two)
END IF
END IF
li_last_categoryid = li_current_categoryid
li_last_artistid = li_current_artistid
NEXT


张健姿-->PowerBuilder系列讲座-->23. 函数与事件

23. 函数与事件

在PowerBuilder5.0 的PowerScript 语 言 中， 对 于 函 数 和 事 件 的 处 理 有 了 新 的 拓 展。 记 得 我 们 曾 在 关 于PowerBuilder 的 面 向 对 象 特 性 中 介 绍 过，PowerBuilder 的 对 象 由3 部 分 内 容 组 成： 属 性、 函 数 和 事 件。 而 在5.0 中， 对 于 这 部 分 理 论 进 行 了 调 整： 将 函 数 和 事 件 进 行 了 同 化， 统 称 为 方 法（Method ）。 在Script 中 调 用 函 数 和 调 用 事 件 的 方 法 进 一 步 接 近， 事 件 也 可 带 有 参 数， 并 具 有 返 回 值。

函 数 和 事 件 即 统 称 方 法 调 用 的 语 法 为：
{ objectname.} { type } { calltype } { when } functionname ( { argumentlist } )
这 些 参 数 的 说 明 如 下：

type       所调用方法的类型    可以是EVENT或FUNCTION，其中FUNCTION为缺省。
calltype   所调用的类型        可以是动态（关键词为DYNAMIC）或静态
（关键词为STATIC），其中STATIC为缺省。
when       调用的时间          POST或TRIGGER，其中TRIGGER为缺省。

当 然， 并 不 是 所 有 的 选 项 都 是 有 意 义 的。 比 如 我 们 不 会 去 调 用 一 个 动 态 的PowerScript 对 象 函 数。 只 有 在 用 户 自 定 义 函 数 中， 我 们 才 可 能 动 态 地 调 用 一 个 函 数。 允 许 对 函 数 的 调 用 采 用Post 方 式 是5.0 的 新 特 点。 当 函 数 以 这 一 方 式 被 调 用 时， 系 统 将 这 一 函 数 放 在 了 这 个 对 象 的 消 息 队 列 的 末 尾， 当 前 面 的 所 有 消 息 都 被 执 行 了 以 后， 这 一 函 数 才 被 调 用。 采 用 这 种 方 式 调 用 函 数， 您 仍 可 以 传 递 参 数， 但 是 却 无 法 得 到 返 回 值。 如：

//  合 法 语 句
POST wf_calculate(x,y)
// 非 法 语 句
Statue = Woo.cb_1.POST EVENT Clicked()

关 键 词STATIC 是 表 示 您 现 在 所 调 用 的 方 法 和 您 所 使 用 的 参 数 在 编 译 时 是 存 在 的。 如：
STATIC EVENT ue_validates(ls_name, li_id)

如 果 关 键 词DYNAMIC 出 现 在 这 一 行 调 用 的 函 数 中，PowerBuilder 在 编 译 这 一 行 代 码 时 将 不 检 查 这 个 方 法 和 所 用 参 数 的 有 效 性， 只 是 到 了 运 行 时， 系 统 才 会 到PBD 的 库 中 去 搜 索 这 个 方 法。

当 我 们 在 运 行 时 需 要 根 据 当 前 不 同 情 况 的 上 下 文 调 用 不 同 的 对 象 函 数 时， 我 们 会 用 到 这 一 关 键 词。 例 如：

PowerObject   lpo
lpo = dw_1
lpo.dynamic show()

在 运 行 前， 也 许 我 们 还 无 法 了 解 到 我 们 要 对 哪 个 对 象 进 行show() 的 操 作， 只 能 等 到 运 行 时 才 能 确 定 操 作 对 象。 如 果 在 上 述 的 语 句 中 我 们 去 掉 关 键 词dynamic， 系 统 编 译 将 出 现 错 误， 因 为PowerObject 对 象 类 并 没 有show 这 个 对 象 函 数。 一 般 来 说，Dynamic 关 键 词 只 有 在 调 用 多 态 方 法 时 才 可 能 用 到， 这 实 际 上 也 是 进 一 步 拓 展 了PowerBuilder 的 面 向 对 象 的 特 性。

不 过 我 们 在 调 用 方 法 时 使 用STATIC 这 一 关 键 词， 一 般 也 只 是 为 了 系 统 在 编 译 时 对 您 所 引 用 的 方 法 进 行 一 下 语 法 检 查， 以 确 保 引 用 的 正 确 性。 如 果 我 们 在 运 行 时 改 变 了 方 法 的 对 象 名 称， 系 统 将 调 用 改 变 后 的 对 象 的 方 法。

我 们 来 作 这 样 一 个 实 验：
在 窗 口 中 我 们 声 明 一 个 实 例 变 量：
CommandButton icb
在 窗 口 中 有 两 个 按 钮， 在 第 一 个 按 钮cb_1 的constructor 事 件 中 键 入：
icb = this
clicked 事 件 中 键 入：
icb.resize(200,100)
窗 口 的 第 二 个 按 钮cb_2 的clicked 事 件 中 编 码 如 下：
icb = this

当 这 个 窗 口 运 行 时， 在cb_2 的 构 造 事 件 中 变 量icb 引 用 定 为cb_1， 如 果 这 时 我 们 首 先 点 击 第 一 个 按 钮， 这 个 按 钮 的clicked 事 件 被 触 发， 调 用icb.resize() 函 数， 这 个 按 钮 的 尺 寸 将 随 之 改 变。 我 们 点 击 一 下cb_2 后， 再 按 动 第 一 个 按 钮， 这 时 您 会 发 现，cb_2 的 尺 寸 也 改 变 了。 因 为 在 我 们 按 下 第 二 个 按 钮 后icb 的 引 用 赋 值 已 经 改 变 为 了cb_2。

在PowerBuilder5.0 所 包 括 的Anchor Bay Nut Company sample application 这 一 实 例 中 就 使 用 了 一 个 动 态 调 用 函 数 的 实 例。m_datareview_framemenu 是 一 个 祖 先 菜 单 类， 它 的 后 继 菜 单 类 有m_datareview_custsheet，m_datareview_ordsheet 等， 分 别 为w_datareview_custsheet，w_datareview_ordsheet 等 窗 口 所 拥 有， 这 些 窗 口 是MDI 下 的 不 同 表 单， 对 这 些 表 现 不 同 信 息 的 表 单 进 行 输 出(export) 操 作 是 各 不 相 同 的， 因 此 各 自 的 窗 口 分 别 有 不 同 的 多 态 函 数wf_export 处 理 各 自 的 输 出 操 作。 可 是 在 对m_datareview_framemenu 菜 单 的m_export 项 编 码 时， 我 们 无 法 知 道 当 前 激 活 的 表 单 是 哪 一 个， 所 以 我 们 只 能 动 态 地 调 用 这 个 窗 口 函 数：

guo_global_vars.ish_currentsheet.DYNAMIC wf_export()

其 中guo_global_vars.ish_currentsheet 的 值 是 当 前 活 动 表 单 的 引 用， 而 这 个 值 在 声 明 时 却 只 能 声 明 作 这 些 表 单 共 同 的 祖 先 类w_datareview_frame 的 引 用， 因 为 这 个 祖 先 窗 口 是 没 有wf_export 这 个 窗 口 函 数 的， 所 以 我 们 必 须 使 用DYNAMIC 这 个 关 键 词。 这 个 例 子 在abnc_rev.pbl 库 中 可 以 找 到， 有 兴 趣 的 人 员 可 以 好 好 理 解 一 下。 如 果PowerBuilder 没 有 这 个 调 用 动 态 函 数 的 功 能， 那 么 我 们 只 能 采 用 其 它 方 法 替 代 解 决， 如：
1. 在 祖 先 窗 口w_datareview_frame 中 定 义 一 个 空 白 的wf_export() 函 数；
2. 使 用SWITCH CASE 语 句 来 决 定 调 用 哪 个 窗 口 函 数。
显 然 第 二 种 方 法 不 是 一 个 好 的 面 向 对 象 的 程 序 设 计。

在 目 前 为 止， 函 数 和 事 件 仍 存 在 着 一 些 差 异， 它 们 表 现 在 事 件 只 与 对 象 有 关， 而 函 数 既 包 括 对 象 函 数， 也 有 一 部 分 是 全 局 的。
在PowerBuilder 中， 事 件 和 函 数 的 搜 索 顺 序 是 不 同 的。
在 运 行 时， 用 户 调 用 了 一 个 未 定 义 的 事 件， 系 统 将 不 会 产 生 任 何 错 误， 但 是 调 用 了 未 定 义 函 数， 系 统 将 触 发 错 误。
函 数 可 以 重 载， 而 事 件 不 能。
对 自 定 义 的 函 数， 您 可 以 指 定 其 访 问 范 围 如public，private，protect 等， 但 是 事 件 不 能， 其 访 问 范 围 全 部 为public。

您 可 以 很 容 易 地 在 继 承 对 象 中 扩 展 或 覆 盖 祖 先 对 象 事 件 中 的 代 码， 但 是 如 您 将 在 后 继 对 象 中 对 函 数 进 行 覆 盖 就 很 困 难 了， 您 必 须 要 重 新 定 义 这 个 函 数。

限 于 篇 幅， 我 们 只 能 对 第 二 条 进 一 步 引 申 说 明：
由 于 函 数 是 支 持 重 载 的， 当 您 的 代 码 调 用 一 个 函 数 时， 系 统 将 本 着 兼 容 匹 配 的 原 则， 搜 索 您 将 要 调 用 的 函 数， 因 此 当 您 调 用 重 载 函 数 时， 一 定 要 指 明 参 数 的 类 型。 例 如 数 据 窗 口 表 达 式 的 返 回 值 是ANY 型 的， 您 应 当 这 样 明 确 指 出：

wf_process_search(string(dw_1.object.name[3]))
而 不 要 笼 统 写 成：
wf_process_search(dw_1.object.name[3])

而 另 一 方 面，PowerBuilder 的 事 件 是 不 支 持 重 载 的， 但 是 后 继 对 象 的 事 件 却 可 以 扩 展 祖 先 对 象 的 同 一 事 件 的。 系 统 搜 索 到 您 将 要 调 用 的 对 象 事 件 后， 还 要 继 续 上 溯 寻 找 它 的 祖 先， 直 到 祖 先 事 件 的 代 码 为 空 或 被 覆 盖 了 为 止， 并 要 首 先 执 行 其 祖 先 代 码。

究 竟 是 在 函 数 中 还 是 在 事 件 里 编 写 您 的 应 用 中 的 复 杂 的 商 业 逻 辑， 这 只 是 一 个 编 程 风 格 问 题， 一 般 对 系 统 的 运 行 效 率 将 不 产 生 任 何 影 响。

此 外 另 一 点 要 说 明 的 是 以 上 所 谈 到 的 在5.0 中 新 增 加 的 语 法 功 能 是4.0 以 下 版 本 的 超 集， 也 就 是 说， 这 种 方 法 兼 容 了 以 往 调 用 函 数 和 触 发 事 件 的 语 法， 以 保 证PowerBuilder 新 版 本 的 向 上 兼 容 性。PowerBuilder5.0 中 仍 然 支 持 以 往 的TriggerEvent() 函 数、PostEvent() 函 数 和 关 键 词CALL 语 句。 只 是 以 前 的 触 发 事 件 的 方 法 无 法 在 其 中 加 入 参 数。


张健姿-->PowerBuilder系列讲座-->24. Install Builder的使用方法

24. Install Builder的使用方法

当 您 的PowerBuilder 应 用 编 写 完 毕， 将 其 制 成 安 装 盘 发 放 给 用 户， 往 往 是 您 的 软 件 开 发 过 程 的 最 后 的 一 步， 也 是 十 分 重 要 的 一 步。 PowerBuilder 附 带 的Install Builder 为 这 一 开 发 周 期 的 最 后 一 步 提 供 了 较 为 全 面 制 作 功 能。 使 用 这 个 软 件 制 作 出 的 安 装 软 件 将 同PowerBuilder 的 安 装 过 程 相 类 似， 在 安 装 过 程 中 给 予 了 用 户 较 多 的 选 择 安 装 的 机 会。 因 此 我 们 需 要 在 使 用Install Builder 时 设 定 这 些 可 以 由 用 户 选 择 的 配 置 选 项， 其 中 有 些 部 分 较 为 复 杂， 不 少 用 户 不 甚 了 解。 而Sybase 所 提 供 的 英 文 资 料 中 却 没 有 包 括 这 一 软 件 的 使 用 说 明， 所 以 在 这 里 简 单 介 绍 一 下 这 一 软 件 的 使 用。

一 Install Builder 的 原 理

针 对 每 一 应 用， 用 户 通 过Install Builder 的 交 互 界 面 定 义 一 个 安 装 的 配 置 信 息， 这 个 配 置 信 息 也 可 以 存 储 成 为 文 件 形 式(*.CFG)， 以 备 下 一 次 调 用。Install Builder 根 据 配 置 文 件， 将 用 户 开 发 的 应 用 的 文 件 和 选 用 的 可 重 用 部 件 如 运 行 库 等 生 成 磁 盘 压 缩 文 件。 同 时，Install Builder 根 据 配 置 文 件 生 成 描 述 安 装 过 程 和 界 面 的INF 文 件。 在 交 付 用 户 使 用 的 介 质 中，PowerBuilder 的 安 装 程 序 根 据INF 文 件 中 的 设 置 进 行 安 装。

二 Install Builder 的 功 能

如 果 您 安 装 过PowerBuilder 或 其 它PowerSoft 的 产 品， 您 一 定 会 对 它 的 安 装 程 序 有 所 了 解。Install Builder 可 以 帮 助 开 发 人 员 自 动 实 现Powersoft 的 安 装 程 序 的 许 多 特 性， 如： 将 安 装 的 内 容 分 成 组 件 和 子 组 件 、 更 新INI 文 件 和 注 册 文 件（ 在Windows95 和Windows NT 中）、 设 定 组 件 和 子 组 件 的 缺 省 路 径、 设 定 典 型/ 最 小 化/ 自 定 义 安 装 的 内 容、 设 定 安 装 时 的 图 标 等。 此 外，Install Builder 还 可 以 实 现 以 下 的 功 能：

将 用 户 的 应 用 划 分 的 不 同 组 件 联 系 在 一 起， 以 使 它 们 可 以 被 缺 省 地 安 装 于 同 一 目 录 下；

创 建 隐 藏 组 件， 使PowerBuilder 安 装 程 序 在 无 论 用 户 选 择 了 任 何 组 件 安 装 的 情 况 下 均 安 装 这 些 组 件， 以 使 其 适 用 于 不 同 应 用 的 安 装 过 程。

发 放PowerBuilder 的 运 行 模 块 及 与 您 所 使 用 的 关 系 型 数 据 库 相 关 的 客 户 端 模 块。Install Builder 包 含 一 个 预 先 定 义 好 的 可 重 用 组 件 清 单， 清 单 中 包 含 了PowerBuiler 运 行 库、SQL Anywhere 及 其 它 常 见 关 系 数 据 库 的 客 户 端 所 需 的 动 态 连 接 库。

产 生 包 含 应 用 组 件 和 可 重 用 组 件 的 磁 盘 压 缩 文 件
产 生 安 装 盘
产 生OLE 自 动 化 对 象 的 注 册 文 件
复 制 和 更 新ODBC 设 置
复 制 和 更 新 注 册 入 口

三 Install Builder 使 用 要 点

通 过Install Builder 发 布 您 的 应 用， 您 应 当 在 软 件 中 完 成 这 样 几 件 事 情：
创 建 配 置 文 件
创 建 可 重 用 组 件
产 生 磁 盘 压 缩 文 件
产 生 安 装 盘
除 此 以 外， 您 还 可 以 定 义 或 修 改Install Builder 的INI 文 件。

限 于 篇 幅， 我 们 不 能 对Install Builder 的 使 用 做 全 面 和 详 细 的 介 绍， 我 们 只 能 将 针 对 一 些 不 易 理 解 的 一 些 问 题 进 行 介 绍。

3.1 创 建 配 置 文 件

在 创 建 配 置 文 件 是 使 用 这 个 软 件 中 最 重 要 的 步 骤， 用 户 主 要 完 成 以 下 任 务：
定 义 组 件 和 子 组 件 的 内 容
设 定 安 装 路 径
设 定 应 用 和ODBC 的INI 文 件
设 定 应 用 和ODBC 及OLE 的 注 册 信 息
设 定 安 装 程 序 的 标 题 和 图 案

创 建 配 置 文 件 的 全 部 工 作 都 是 通 过Install Builder 的 主 窗 口 实 现 的。

窗 口 上 部 的 组 件 项 用 来 定 义 应 用 组 件/ 子 组 件 的 概 要 信 息。 它 由 三 个 单 行 编 辑 框 分 别 定 义 名 称、 描 述 和 缺 省 安 装 路 径 和 两 个 多 选 框 用 以 定 义 是 否 加 入 路 径 和 该 组 件 项 是 否 对 用 户 隐 藏 组 成。 增 加 和 删 除 组 件/ 子 组 件 的 操 作 可 通 过 三 个 工 具 条 按 钮 实 现。 下 面 的6 个 标 签 选 择 分 别 用 来 定 义 当 前 组 件 的 详 细 信 息。 它 们 包 括： 该 组 件 所 应 当 包 括 的 文 件、INI 文 件 设 定、 注 册 入 口、 扩 展、 补 充 选 项 和 图 标。

设 定 应 包 括 文 件 的 办 法 可 以 直 接 在Edit|Select File... 中 选 择， 也 可 以 指 定 一 个PBL 中 的 工 程 文 件， 系 统 自 动 地 将 其 中 的EXE、PBD 或DLL 文 件 加 入 其 中。INI 文 件 可 以 在 不 同 文 件 中 逐 项 地 加 入 在 您 的 应 用 中 将 要 使 用 的 信 息， 其 中 加 入ODBC 的 信 息 较 为 简 单， 您 只 要 在 通 过 菜 单 的Edit|ODBC Entries 中 选 定 需 要 加 入 哪 个 数 据 库 的 信 息 即 可。

当 您 的 应 用 较 为 复 杂， 由 用 户 的 任 选 项 较 多 时， 配 置 文 件 比 较 繁 琐， 但 是 大 部 分 内 容 并 不 难 于 理 解， 我 们 只 对 这 样 一 些 概 念 进 行 重 点 说 明：

应 用 组 件

指 通 过Install Builder 主 窗 口 可 以 定 义 一 个 基 本 组 件 和 若 干 个 组 件/ 子 组 件。 定 义 它 们 的 目 的 是 为 了 使 最 终 用 户 可 以 自 由 地 选 择 安 装 您 的 应 用 中 的 任 何 一 个 部 分。

路 径 设 置 （ 系 统 路 径 和 应 用 路 径）

我 们 知 道，Windows 3.1 对 路 径 的 处 理 与Windows 95 和Windows NT 不 同： 在Windows3.1 中， 用 户 将 一 个 目 录 加 入 系 统 路 径 即autoexec.bat 的PATH 中， 操 作 系 统 将 在 搜 索 文 件 的 过 程 中 用 到 这 一 目 录。 而 在Windows 95 和Windows NT 中， 每 个 应 用 有 自 己 的 应 用 路 径（application path）。 应 用 路 径 中 包 含 了 一 系 列 目 录（ 文 件 夹）， 当 操 作 系 统 需 要 为 某 个 应 用 搜 索 文 件 时 将 在 这 些 目 录 中 查 找。

对 这 两 种 操 作 系 统 的 路 径 设 置 是 不 同 的。
16 位 安 装 的 路 径 设 置： 选 中Add to Path 的 多 选 框
32 位 安 装 的 路 径 的 设 置 要 按 以 下 步 骤 进 行：
1 为 所 有 组 选 取 文 件
2 选 中Add to Path 多 选 框
3 点 击 位 于Add to Path 多 选 框 右 侧 的 按 钮
弹 出App Path 对 话 框， 该 对 话 框 列 出 了 当 前 组 件 中 的 所 有 可 执 行 文 件。
4 在App Path 对 话 框 中 选 取 需 要 加 入 路 径 的 文 件， 然 后 点 击OK 按 钮。
5 对 所 有 组 件 重 复2 至4 步
使 用 可 变 路 径 变 量
不 同 用 户 机 器 的 目 录 结 构 是 各 不 相 同 的， 使 用Install Builder 的 可 变 路 径 变 量（ 也 称 符 号 变 量 —Symbolic Variables）， 可 以 将 您 的 应 用 安 装 在 不 同 目 录 结 构 的 机 器 上。
Install Builder 使 用 以 下 几 个 可 变 路 径：
@(base) 基 本 组 件 的 路 径
@(windows)windows 的 路 径
@(system)windows\system 的 路 径
@(componentname) 某 一 名 称 为componentname 组 件 的 路 径
在Install Builder 中， 用 户 可 以 在 任 何 需 要 路 径 名 的 地 方 使 用 可 变 路 径 变 量，Powersoft 安 装 程 序 在 安 装 时， 将 可 变 路 径 变 量 替 换 为 相 应 的 路 径 名。

3.2 创 建 可 重 用 组 件

可 重 用 组 件 指 的 是PowerBuilder 的 运 行 库 和 具 体 关 系 数 据 库 客 户 端 的 运 行 模 块， 它 们 可 被 用 于 不 同 应 用 的 安 装。 创 建 可 重 用 组 件 的 工 作 可 以 通 过 可 重 用 组 件 窗 口 完 成。 用 户 可 以 通 过 点 及 菜 单 项Actions|Define Reusable components...， 进 入 可 重 用 组 件 窗 口。 可 重 用 组 件 窗 口 与Install Builder 主 窗 口 类 似， 对 可 它 的 操 作 在 许 多 方 面 也 与 定 义 应 用 组 件 相 似。Install Builder 将 重 用 组 件 的 定 义 存 在 INSTBLDR.INI 中。

3.3 修 改Install Builder 的INI 文 件 设 置

Install Builder 用INSTBLDR.INI 来 维 护 以 下 内 容：
Install Builder 文 件 的 位 置
Power Builder 和SQL Anywhere 可 重 用 组 件 的 位 置
压 缩 后 的 可 重 用 组 件 的 位 置
可 重 用 组 件 的 定 义
对INSTBLDR.INI 得 修 改 可 以 通 过 点 击Options|INI 后 弹 出 的INI File Settings for Install Builder 对 话 框 实 现， 这 样 下 一 使 用 该 软 件 时， 我 们 将 得 到 新 的 缺 省 信 息。

[ 结 束 语]

这 里 我 们 重 点 要 指 出 的 是， 以 笔 者 目 前 手 中 的 最 新 版 本5.0.02 看，Install Builder 中 仍 有 一 些 小 错 误， 因 此 每 一 次 安 装 盘 制 作 完 成， 请 务 必 在 一 台 未 安 装 有PowerBuilder 和 关 系 数 据 库 客 户 端 软 件 的 计 算 机 上 进 行 安 装 测 试， 以 免 等 到 软 件 分 发 至 最 终 用 户 无 法 安 装 使 用。


张健姿-->PowerBuilder系列讲座-->25.26. 一个代码维护的应用程序（上、下）

25.26. 一个代码维护的应用程序（上、下）

在 使 用PowerBuilder 编 程 的 过 程 中， 您 一 定 遇 到 过 一 个 全 局 变 量 或 设 计 的 对 象 需 要 更 换 名 称 的 情 况， 这 个 变 量 可 能 会 在 您 所 编 写 的 多 个PowerScript 中 引 用， 为 了 实 现 更 名， 您 可 能 要 打 开 所 有 的 对 象， 对 其 中 所 有 进 行 过 编 码 的 事 件 进 行 搜 索。 显 然， 这 是 一 个 非 常 费 时 费 力 的 工 作， 而 且 一 旦 搜 寻 有 所 遗 漏， 这 点 差 错 就 可 能 成 为 您 编 写 的 应 用 中 的 错 误 潜 伏 下 来， 在 测 试 或 直 到 用 户 使 用 时 才 被 发 现。

事 实 上PowerBuilder 系 统 本 身 提 供 了 一 系 列 对 其 本 身 对 象 库pbl 进 行 操 纵 的 函 数， 如LibraryDirectory() 等。 这 些 函 数 可 以 用 于 您 在 编 程 时 对 自 身 代 码 的 维 护， 还 可 以 自 动 创 建 用 于 打 印 报 表 的 数 据 窗 口 等。

这 里 我 们 介 绍 给 您 一 个 小 型 的 应 用 软 件， 用 于 自 动 检 索PowerBuilder 的pbl 库， 搜 索 和 替 换 您 指 定 的 对 象 或 代 码。 通 过 这 个 软 件， 您 将 进 一 步 了 解PowerBuilder 对 用 户 所 设 计 的 对 象 的 管 理 模 式。

我 们 首 先 设 计 一 个 用 于 浏 览 的 不 可 视 用 户 自 定 义 对 象 名 叫u_objbrowser。 这 个 对 象 的 作 用 是 在PowerBuilder 的 对 象 库 中 搜 索 和 浏 览 对 象。 浏 览 的 过 程 是 先 查 询 当 前 应 用 包 含 的 所 有PowerBuilder 对 象 库， 搜 索 这 些 库 中 指 定 的 对 象 类 型， 将 命 中 的 结 果 返 回。 在 这 个 对 象 中， 我 们 重 点 要 用 到 的 就 是LibraryDirectory() 这 个 函 数。

您 一 定 都 用 过 函 数ImportString() 为 数 据 窗 口 填 充 数 据。 在 这 里 使 用 的LibraryDirectory() 函 数 所 得 到 的 返 回 信 息 同ImportString() 的 表 达 方 式 是 基 本 相 似： 结 果 为 一 个 字 符 串， 字 符 之 间 表 达 不 同 的 列 以~t 分 隔， 不 同 的 行 以~n 分 隔。

在u_browser 对 象 中， 我 们 声 明 实 例 变 量 如 下：

string	uis_ApplicationName	// 当 前 应 用 名
string	uis_ApplicationLibrary	// 包 含 应 用 对 象 的pbl 库 名
string	uis_ApplicationDirectory	// ApplicationLibrary 所 在 的 路 径
string	uis_PBiniFile	// pb.ini 的 文 件 名 称

这 个 对 象 中 包 含 了 这 样 几 个 函 数：
u_browser.uf_GetCurrentApplication()， 获 取 当 前 应 用 名

这 个 函 数 是 一 个 初 始 化 函 数， 它 首 先 在WIN.INI 中 寻 找PB.INI 的 路 径， 读 取PB.INI 文 件 中 的[APPLICATION] 这 一 段， 来 获 取 当 前 这 个 应 用 的 操 作 环 境。 然 后 对 两 个 变 量 赋 值：uis_ApplicationName 是 当 前 应 用 的 名 称，uis_ApplicationLibrary 是 这 个 应 用 的 应 用 对 象 所 存 储 的pbl 库 名。

u_browser.uf_GetCurrentApplication()
long         ll_pos1, ll_pos2
string        ls_PBInitPath
// 获 取PB.INI 的 路 径
ls_PBInitPath = ProfileString (“WIN.INI”,POWERBUILDER”,
“INITPATH”,“”)
uis_PBiniFile = “PB.INI*
IF ls_PBInitPath <> “”THEN
IF Right(ls_PBInitPath, 1) <> *\” THEN
    ls_PBInitPath = ls_PBInitPath + *\*
END IF
uis_PBiniFile = ls_PBInitPath + uis_PBiniFile
END IF
//从PB.INI文件中获取应用名称和所在的PBL库名
uis_ApplicationName = ProfileString(PBiniFile, "APPLICATION”,
“APPNAME”, “”)
uis_ApplicationLibrary = ProfileString(PBiniFile,“APPLICATION”,
“APPLIB”,“”)
uis_ApplicationDirectory = “”
ll_pos1 = 0
ll_pos2 = Pos(uis_ApplicationLibrary, *\”)
DO WHILE ll_pos2 > 0
ll_pos1 = ll_pos2
ll_pos2 = Pos(uis_ApplicationLibrary, *\”, ll_pos1 + 1)
LOOP
IF ll_pos1 > 0 THEN uis_ApplicationDirectory = Left
(uis_ApplicationLibrary, ll_pos1)

u_browser.uf_GetLibraryPath(): 获 取 本 应 用 所 包 含 的pbl 库 名

一 个 应 用 包 含 了 哪 几 个pbl 库， 这 样 的 信 息 并 不 是 作 为application 对 象 的 一 个 属 性 存 储 在pbl 库 中 的， 而 是 包 含 在 了pb.ini 文 件 中[application] 段 的$AppLib 项 等 号 后 面 的 字 符 串 中， 库 名 与 库 名 之 间 以 分 号 分 隔。 在 这 里 我 们 使 用 了uf_GetLibraryPath() 函 数 读 取 这 些 信 息， 并 将 库 名 字 符 串 改 变 为 以 标 准 的~t 分 隔， 作 为 函 数 的 返 回 值。

long     ll_pos1
string   ls_LibraryList
// 从pb.ini 中 读 取 本 应 用pbl 库 的 路 径
ls_LibraryList = ProfileString ( uis_PBiniFile,&
“Application”,“$” + uis_ApplicationLibrary +&
“(” + uis_ApplicationName + “)”, “”)
// 以~t 符 号 取 代 分 号 作 为 分 隔 符
ls_pos1 = Pos(ls_LibraryList, “;”)
DO WHILE ls_pos1 > 0
ls_LibraryList = Replace(ls_LibraryList, ll_pos1, 1,“~t~n”)
ll_pos1 = Pos(ls_LibraryList, “;”, ll_pos1 + 2)
LOOP
RETURN ls_LibraryList

当 得 到 了 这 些 库 名 的 列 表 后， 我 们 就 可 以 在 库 中 搜 索 我 们 所 须 的 任 何 一 个 特 定 的 对 象 了 ( 如Application, Window, DataWindow, Menu, Function, Structure 或UserObject 等)。
uf_GetLibraryObjects()： 获 取pbl 库 中 的 对 象

这 个 函 数 有 三 个 参 量： 一 个 由 库 名 组 成 的 字 串， 一 个LibDirType 对 象 类 型， 一 个 布 尔 型 的 标 志 参 数。 布 尔 参 数 用 以 指 示 是 否 只 返 回 对 象 名 称。

其 中LibDirType 是 一 个 枚 举 数 据 类 型， 包 括 了 下 列 的 几 个 值DirAll!, DirApplication!, DirDataWindow!, DirFunction!, DirMenu!, DirStructure!, DirUserObject! 和DirWindow! 这 个 函 数 的 返 回 值 是 所 有 与 您 指 定 的 对 象 类 型 相 匹 配 的 存 储 在 这 些 库 中 的 对 象 名。 事 实 上 使 用LibraryDirectory() 函 数 还 将 包 含 这 些 对 象 的 注 释 和 最 后 修 改 的 时 间 等 信 息， 而 在uf_GetLibraryObjects() 函 数 中， 我 们 可 以 根 据 布 尔 参 数， 来 决 定 是 否 调 用uf_ParseDirectory() 函 数 将 这 些 内 容 剔 除 掉。

uf_GetLibraryObjects()
// 参 数
//string  	as_LibraryName
//LibDirType  a_ObjectType
//boolean	ab_ParseList
string   ls_result
ls_result = LibraryDirectory (as_LibraryName, a_ObjectType)
IF ab_ParseList THEN
ls_result = This.uf_ParseDirectory (ls_result)
END IF
RETURN ls_result

使 用LibraryDirectory() 函 数 得 到 的 对 象 信 息 有 三 个 数 据 段： 对 象 名 称、 修 改 时 间 和 注 释， 这 三 个 字 段 以~t 相 隔， 不 同 的 对 象 间 以~n 分 隔。uf_ParseDirectory() 函 数 将 删 除 每 一 条 记 录 的 后 两 个 字 段， 只 保 留 对 象 名 称 的 信 息。uf_ParseDirectory() 函 数 的 代 码 如 下：

uf_ParseDirectory()
// 参 数：string   as_LibDirectory
// 返 回：string
string ls_ObjList = “”
long  ll_pos1, ll_pos2, ll_DirLen
ll_DirLen = Len(as_LibDirectory)
ll_pos1 = 1
DO WHILE ll_pos1

最 后 一 个 函 数 是uf_ConvertListToArray()， 它 接 受 一 个 由 对 象 名 组 成 的 字 符 串， 将 其 分 隔 为 数 组 形 式。

uf_ConvertListToArray()
// 参 数：string		as_ObjectString
//      string		as_ObjectOjbect
//	 string		as_OjbectArray	reference
// 返 回： 分 隔 符 的 个 数
long ll_pos1, ll_pos2, ll_count
int  li_seplen
IF as_ObjectString = “” THEN RETURN 0
li_SepLen = Len(as_ObjectSeparator)
IF Right (as_ObjectString, SepLen) <> as_ObjectSeparator &
THEN as_ObjectString = as_ObjectString + as_ObjectSeparator
//确定分隔符在字串中的位置
ll_count = 0
ll_pos1 = 1
ll_pos2 = Pos (as_ObjectString, as_ObjectSeparator)
DO UNTIL ll_pos2 = 0
ll_Count ++
// 将 分 隔 符 间 的 内 容 拷 贝 在 数 组 中
as_ObjectArray[ll_count] = Mid (as_ObjectString, ll_pos1, ll_pos2 - ll_pos1)
ll_pos1 = ll_pos2 + li_seplen
ll_pos2 = Pos (as_ObjectString, as_ObjectSeparator, ll_pos1)
LOOP
// 返 回 分 隔 符 的 个 数
RETURN ll_count

---------在此处分隔为上下两篇---------

我 们 要 写 的 这 个 搜 索 及 替 换 的 应 用 程 序 使 用 了u_browser 这 个 用 户 对 象。 它 将 根 据 用 户 指 定 的 选 择 条 件， 搜 索 对 象 的 输 出 文 本。 但 是 用 于PowerBuilder 并 不 支 持 除 数 据 窗 口 外 对 象 的 动 态 输 入， 所 以 如 果 我 们 对 输 出 的 文 本 进 行 修 改 后 是 无 法 存 入pbl 库 中 的， 我 们 只 能 将 其 保 存 成 为 一 个 文 本 文 件， 搜 索 完 成 后， 在Library 画 笔 中 手 工 将 这 些 修 改 后 的 对 象 输 入 库 中。

我 们 要 注 意 的 问 题 是， 如 果 在 不 同 的 对 象 之 间 都 引 用 了 修 改 的 文 字 时， 输 入 这 些 对 象 的 顺 序 是 十 分 重 要 的， 例 如 您 修 改 了 全 局 变 量 的 名 称， 您 就 应 当 首 先 输 入application 对 象， 也 就 是 说 您 应 当 首 先 输 入 定 义 文 字 的 那 个 对 象。 另 一 个 注 意 事 项 是， 您 不 能 修 改PowerBuilder 保 留 的 关 键 字， 如type，within 等。 当 您 使 用 了 这 个 搜 索 工 具 以 后， 它 会 自 动 生 成 一 个 文 件 指 示 您 需 要 输 入 对 象 的 名 称、 这 个 对 象 的 库 名 和 对 象 输 出 的 文 件 名。

在 这 个 应 用 的Open 事 件 中 我 们 写 入 这 样 一 段 代 码：

Browser = CREATE u_objbrowser
Browser.ff_GetCurrentApplication()
Open(w_search_criteria)

其 中 将 要 打 开 的w_search_criteria 窗 口 如 图 所 示：

在 上 面 的ListBox 中 显 示 了 当 前 应 用 所 包 含 的pbl 库， 由 用 户 指 定 搜 索 的 范 围， 左 下 角 为 十 个 多 选 框， 指 定 要 选 择 的 对 象 名 称， 右 下 角 由 用 户 输 入 查 找 和 替 换 的 字 符 串 内 容。

在 这 个 窗 口 中 声 明 一 个wstr_SearchCriteria 结 构 的 实 例iwstr_SearchCriteria， 将 用 户 输 入 的 信 息 填 写 入 这 个 结 构 中。 这 个 结 构 包 括：

boolean	object_type[]  //用户在窗口右下角选中的对象类型
integer		lib_count	 //用户选择对象库的个数
string		lib_list[]	 //用户选择了的pbl库名
boolean	match_case	 //用户是否选择了“区分大小写”的选项
此外还要对以下几个实例变量赋值：
string is_SearchFor   //用户指定搜索的字符串
string is_ReplaceText //用户指定替换的字符串
integer ii_ReplaceLen = len(is_SearchFor)
integer ii_SecharLen = len(is_ReplaceText)

当 用 户 按 下“ 开 始 查 找” 的 按 钮 以 后， 出 现 一 个 显 示 进 度 的 子 窗 口。 这 个 窗 口 的 设 计 过 程 限 于 篇 幅 从 略。 在PowerBuilder4.0 的 类 库 中 有 一 个 可 以 利 于 的 用 于 显 示 进 度 的 窗 口， 通 过 调 用 该 窗 口 的f_progress() 函 数 可 以 显 示 正 在 进 行 的 进 度， 本 文 也 正 是 将 这 个 窗 口 进 行 改 进 后 得 到 的， 不 过 这 个 窗 口 在PowerBuilder5.0 的 类 库 中 被 取 消 了， 如 果 您 也 想 使 用 它， 就 只 能 将 其 从4.0 中 升 级 得 到 了。 在 这 个 窗 口 中 我 们 使 用 了 两 个 用 户 自 定 义 事 件ue_object 和ue_search 来 分 别 控 制 搜 索 的 对 象 和 完 成 查 找 工 作。

当 有 文 本 被 替 换 时， 我 们 调 用 窗 口 函 数wf_SaveExportObject()， 将 这 个 对 象 的 输 出 文 件 冠 以$PBExportHeader$objectnam e.ext~r~n 的 文 字， 并 将 其 保 存 成 为 一 个 文 件。 这 个 对 象 名 称 的 前8 个 字 符 作 为 文 件 名 称， 如 果 出 现 重 名， 将 提 示 用 户， 由 用 户 进 行 确 认。

在 这 个 进 度 窗 口 打 开 时， 我 们 声 明 一 个wstr_object 结 构 的 实 例 数 组iwstr_object[]， 用 以 记 录 用 户 选 中 了 的 对 象 类 型。 这 个 结 构 包 括 以 下 几 个 元 素：

string	libraryname  	//该对象将要在哪个pbl库中搜索
integer	objecttype	//这个对象类型
integer	objectcount	//在这个库中共命中了多少个该对象类型
string	objectlist[]	//命中对象的名称
在ue_object事件中我们要作以下初始化的工作：
int li_MaxIndex,li_LibCnt, k, j
string	ls_List
libdirtype libdir[] = {dirapplication!,dirdatawindow!, &
    dirfunction!,dirmenu!,dirpipeline!,dirproject!,dirquery!,&
    dirstructure!,diruserobject!,dirwindow!}
long ProgressMax, ProgressCur = 0
int li_curobj, li_curobjcount
libexporttype libexport[]={exportapplication!,exportdatawindow!, &
    exportfunction!,exportmenu!,exportpipeline!,exportproject!,
exportquery!,&
    exportstructure!,exportuserobject!,exportwindow!}
li_MaxIndex = 0
liLibCnt = iwstr_SearchCriteria.lib_count
//对每一个用户选中的pbl库
FOR j = 1 to li_LibCnt
//对于每一个可能选择的对象类型
FOR k = 1 to 10
//如果用户选择了该对象类型
IF iwstr_searchcriterial.object_types[k] THEN
li_MaxIndex ++
//保存库名
lwstr_object[li_MaxIndex].LibraryName =&
iwstr_searchcriterial.Lib_List[j]
    //保存对象类型
lwstr_object[li_MaxIndex].ObjectType = k
//获取该对象类型在库中的全部名称列表
ls_List = Browser.uf_GetLibraryObjects(&
lwstr_object[li_MaxIndex].LibraryName,&
LibDir[k], TRUE)
//将名称列表转换为数组形式
lwstr_object[li_MaxIndex].ObjectCount = &
Browser.uf_ConvertListToArray(ls_List,&
lwstr_object[li_MaxIndex].ObjectList, "~t~n")
ProgressMax = ProgressMax + &
lwstr_object[li_MaxIndex].ObjectCount
END IF
NEXT
NEXT

// 现 在 要 搜 索 的 对 象 名 称 都 已 经 确 定， 接 下 来 我 们 要 作 的 事 情 就 是 循 环 输 出 每 一 个

// 对 象 的 文 本， 并 调 用ue_search() 事 件， 搜 索 出 指 定 的 文 字。

FOR li_CurIndex = 1 to li_MaxIndex
FOR li_CurObj = 1 to lwstr_object[k].OjbectCount
    // 显 示 对 象 信 息
    st_library.Text = lwstr_object[li_CurIndex].LibraryName
    st_object.Text = lwstr_object[li_CurIndex].ObjectList[li_CurObj]
    // 将 对 象 输 出 到mle_text 中
    mle_text.Text = LibraryExport(lwstr_object[li_CurIndex].LibraryName, &
      lwstr_object[li_CurIndex].ObjectList[li_CurObj], &
    LibExport[lwstr_object[li_CurIndex].ObjectType])
    THIS.Event ue_search()
    ProgressCur ++
    f_Progress( ProgressCur / ProgressMax )
NEXT
NEXT

UE_SEARCH事件将搜索与之匹配的文本，其代码如下：
int	  li_SearchPos = 0
boolean  lb_Changed = False
DO
IF iwstr_searchcriterial.match_case THEN
    li_SearchPos = Pos(mle_text.Text, is_SearchFor, li_SearchPos )
ELSE
li_SearchPos = Pos(UPPER(mle_text.Text),is_SearchFor, li_SearchPos )
END IF
IF li_SearchPos > 0 THEN
    mle_text.text = Replace(mle_text.text, li_SearchPos, ii_SearchLen, &
    is_ReplaceText )
    changed = true
    li_SearchPos += ii_ReplaceLen
END IF
LOOP WHILE li_SearchPos <> 0
//如果没有发现匹配的文字则返回，否则将保存该输出文件
IF Changed THEN
wf_SaveExportObject()
END IF

这 里 我 们 只 是 列 出 了 这 个 软 件 中 部 分 代 码， 其 余 部 分 您 可 自 行 补 齐。 事 实 上， 这 个 应 用 小 软 件 的 用 途 是 非 常 广 的。 我 们 在 以 前 的 专 题 中 曾 经 谈 到 过 使 用 可 重 用 的 数 据 窗 口 的 问 题， 将 数 据 窗 口 定 义 为 由 用 户 对 象dw_main 继 承 而 来 的 用 户 对 象， 并 且 提 到 了 要 改 变 已 设 计 好 的 数 据 窗 口 所 应 继 承 的 祖 先 对 象。 此 外 您 在 编 程 中 可 能 经 历 过 这 样 的 事 情， 在 最 开 始 编 程 曾 经 尝 试 使 用 了 一 些 简 单 地 窗 口 继 承， 而 当 您 完 成 了 该 软 件 的 开 发， 而 且 具 有 了 一 定 的 开 发 经 验 后， 在 整 理 这 些 代 码 时， 您 会 发 现 有 更 好 的 继 承 关 系 来 取 代 现 有 的 层 次， 当 您 设 计 好 新 的 基 类 后， 就 可 以 使 用 这 个 软 件 来 替 换 原 有 的 基 类 对 象 了。

由 于PowerBuilder 没 有 为 我 们 提 供 将 对 象 输 入pbl 库 的 函 数， 我 们 只 能 将 其 保 存 成 为 文 件， 然 后 手 工 输 入 对 象 库 中。 如 果 您 认 为 这 样 太 麻 烦， 也 可 以 使 用C 语 言 调 用 其ORCA 的API 函 数， 用 程 序 来 完 成 输 入 工 作， 不 过 这 个 内 容 就 超 出 了 本 文 的 范 围 了。


张健姿-->PowerBuilder系列讲座-->27. 在PowerScript脚本中访问数据窗口中的数据

27. 在PowerScript脚本中访问数据窗口中的数据

在4.0 以 前 的 版 本， 如 果 您 想 在PowerScript 脚 本 中 访 问 数 据 窗 口 中 的 数 据， 方 法 只 有 一 种， 那 就 是 使 用SetItem 和GetItem 系 列 的 函 数， 指 定 您 所 要 访 问 的 某 一 行 列 的 值， 这 一 方 法 的 局 限 性 在 于 您 一 次 只 能 访 问 到 一 个 数 值。 而 在5.0 版 中，PowerBuilder 拓 展 了 数 据 窗 口 的 属 性， 使 数 据 窗 口 中 的 数 据 成 为 了 该 对 象 的 一 个 属 性， 而 使 用 户 可 以 象 访 问 其 它 对 象 属 性 一 样 直 接 访 问 数 据 窗 口 的 数 据 了。 这 样 我 们 就 有 了 两 种 访 问 数 据 的 方 法 了。

1. 传 统 的 使 用SetItem 和GetItem 系 列 函 数， 如：

dw_1.SetItem(1, "empname", "Phillips")
ls_name = dw_1.GetItemString(1, "empname")

2. 表 达 式 的 方 法， 这 种 方 法 可 以 指 定 某 一 列、 某 一 行、 某 一 块、 或 是 用 户 选 中 的 行 列、 甚 至 整 个 数 据 窗 口 控 件 的 全 部 数 据， 如：

dw_1.Object.empname[1] = "Phillips"
dw_1.Object.Data[1,1] = "Phillips"

这 两 种 方 法 都 可 以 允 许 用 户 指 定 特 定 的 数 据 缓 冲 区， 也 就 是 说 您 采 用 任 何 一 种 方 法 都 可 以 访 问 到 数 据 被 修 改 前 的 原 始 值、 被 过 滤 掉 的 值、 被 删 去 的 值 及 当 前 值 等。

在 大 多 数 情 况 下， 您 可 以 采 用 这 两 种 方 法 中 的 任 一 种， 但 这 两 种 方 法 在 使 用 时 也 是 各 有 利 弊 的。

如 果 您 只 是 要 访 问 某 行 某 列 的 一 个 数 值， 两 种 方 法 在 执 行 效 率 上 是 基 本 相 同 的 的。

如 果 您 打 算 采 用 数 据 窗 口 的 列 名 而 不 是 数 值 来 表 示 某 一 列， 而 这 一 列 的 列 名 在 运 行 前 又 不 知 道， 或 是 要 根 据 用 户 的 不 同 输 入 来 决 定， 那 您 只 能 采 用 函 数 法， 因 为 这 一 方 法 可 以 动 态 地 改 变 列 名。

如 果 您 需 要 访 问 的 数 据 不 止 一 列， 而 是 确 定 范 围 的 多 行 多 列， 那 您 采 用 表 达 式 法 将 获 得 更 高 的 效 率。

表 达 式 法 是PowerBuilder5.0 新 增 加 的 功 能， 虽 说 这 种 方 法 可 使 得 数 据 窗 口 的 面 向 对 象 的 特 性 更 为 明 显， 语 言 的 表 达 也 更 为 直 观， 易 于 使 旁 观 者 阅 读 他 人 的 程 序 时 容 易 看 懂， 但 是 这 种 方 法 的 语 法 形 式 却 是 很 复 杂 多 样， 使 用 起 来 却 不 太 容 易。

总 体 上 讲， 表 达 式 的 语 法 可 以 分 成3 组：
直 接 指 定 列 名 称 法： 如 果 您 事 先 知 道 您 需 要 访 问 数 据 的 列 名， 而 且 仅 访 问 这 一 列， 可 以 采 用 这 种 方 法。
选 择 这 一 控 件 中 一 条 记 录 或 多 条 记 录

dwcontrol.object.columnname {.buffer } {.datasource } { [ startrownum{, endrownum} ] }

我 们 在 前 面 的 专 题 中 曾 经 介 绍 过 关 于 数 据 窗 口 的 四 个 缓 冲 区， 在 这 条 命 令 的 语 法 中buffer 选 项 就 可 以 选 择Primary，Filter 或Delete 这 三 个 缓 冲 区， 缺 省 为Primary；datasource 选 项 为Current 或Original， 缺 省 为Current。 如 果 您 选 择Original， 则 系 统 将 到 数 据 窗 口 的Original 缓 冲 区 中 去 读 取 数 据， 显 然Original 缓 冲 区 是 只 读 的， 您 不 能 对 其 赋 值。 在 后 面 的 方 括 号 中， 您 可 以 指 定 您 打 算 访 问 的 这 一 列 中 的 起 止 记 录 数， 如 果 只 是 一 条 记 录， 您 在StartRownum 中 指 定 行 号 就 可 以 了； 如 果 方 括 号 中 两 个 参 数 全 部 缺 省， 则 指 这 一 列 的 全 部 记 录。

例 如：
string ls_name
ls_name = dw_1.Object.name[1]
又 如：
dw_1.Object.salary[8,12] = id_salary

id_salary 是 一 个 数 组， 如 果 其 中 只 有 四 个 元 素， 则 第12 行salary 的 值 为 空。
选 择 这 个 数 据 窗 口 控 件 中 加 亮 的 记 录
dwcontrol.Object.columnname {.Primary }{.datasource } .Selected
在 数 据 窗 口 中 加 亮 的 记 录 就 是 您 曾 经 使 用 过SelectRow 函 数 对 其 进 行 加 亮 表 示 这 一 行 选 中 的 记 录。 显 然， 对 这 一 特 性 数 据 的 访 问 只 能 是Primary 缓 冲 区 中 的， 不 过 数 据 源 仍 然 可 以 选 择 是 当 前 还 是 原 始 的。 如 果 选 择 原 始， 也 就 是 说 您 得 到 的 数 据 是 从 数 据 库 中 查 到 的， 修 改 前 的 数 据。

例 如：
string ls_name
ls_name = dw_1.Object.name.Selected
使 用 数 值 来 表 示 列：
dwcontrol.Object.Data {.buffer } {.datasource } [ startrownum, startcolnum,
endrownum, endcolnum ]

我 们 知 道 在SetItem 和GetItem 系 列 的 函 数 中， 对 列 的 表 达 既 可 以 用 列 名 也 可 以 采 用 数 值 指 示， 在 这 里 也 是 同 样。 如 果 我 们 采 用 数 值 指 代 列 名 的 方 法， 同 上 一 种 比 较， 我 们 要 将 列 名 换 成 了 关 键 词Data， 在 后 面 的 方 括 号 中， 参 数 变 成 了 四 个。 这 一 表 达 式 的 返 回 值 将 是 一 个 结 构 数 组， 或 是 一 个 用 户 对 象。 必 须 注 意， 您 所 定 义 的 结 构 或 用 户 对 象 必 须 与 数 据 窗 口 的 这 几 列 的 数 据 类 型 相 匹 配， 否 则PowerBuilder 将 出 现 错 误。

对 整 行 记 录 的 操 纵：
dwcontrol.Object.Data {.buffer } {.datasource } { [ rownum ] }

这 一 种 方 法 是 上 一 种 方 法 的 特 例， 如 果rownum 中 的 数 值 缺 省， 将 表 示 整 个 数 据 窗 口 的 全 部 数 据。
例 如 数 据 窗 口 中 的 列 名 及 数 据 类 型 是 这 样 的：

ID (number)
name (string)
retired_status (boolean)
birth_date (date)

首 先 我 们 要 在 结 构 画 笔 中 定 义 一 个str_empdata 的 数 据 结 构。 它 包 括 四 个 元 素， 类 型 分 别 是：
integer, string, boolean 和date
执 行 下 列 代 码：
str_empdata lstr_currdata[]
lstr_currdata = dw_1.Object.Data

这 样lstr_currdata 结 构 中 数 组 的 上 界 将 用 数 据 窗 口 控 件 中 记 录 的 行 数 相 等， 并 完 成 了 赋 值。

我 们 还 可 以 用 这 种 方 法 指 定 这 一 控 件 中 高 亮 度 的 行
dwcontrol.Object.Data {.Primary } {.datasource } .Selected
这 三 种 形 式 的 功 能 比 较 如 下：

访 问 范 围
语 法
访 问 控 件 中 高 亮 度 的 行

1
一 列
dwcontrol.object.columnname{.buffer } {.datasource}
能

2
多 列
dwcontrol.object.data{.buffer } {.datasource}
不 能

3
一 条 记 录 的 全 部 列
dwcontrol.object.data{.buffer } {.datasource}
能 在 使 用 这 些 语 句 时 我 们 要 注 意 以 下 几 点：

同 使 用SetItem 和GetItem 系 列 函 数 一 样， 在 编 译 时PowerBuilder 将 不 检 查 您 所 指 示 的 列 的 有 效 性， 您 必 须 自 行 检 查 并 确 保 引 用 的 有 效 性。

在 第 一 种 方 法 中 我 们 必 须 注 意 在 列 名 后 面 一 定 要 有 所 后 缀， 例 如：

ld_salary[] = dw_1.object.emp_salary.primary
ld_salray = dw_1.object.emp_salary[5]
上 述 表 达 式 都 是 合 法 的， 但 是
ld_salary[] = dw_1.object.emp_salary
就 是 非 法 表 达 了。

因 为dw_1.object.emp_salary 指 代 的 是 这 个emp_salary 列 的DWObject 对 象， 而 不 是 在emp_salary 列 中 的 数 据。 我 们 可 以 用dw_1.object.emp_salary 来 指 代 数 据 窗 口 中 的 对 象， 如：

integer li_data
DWObject dwo_empsalary
dwo_empsalary = dw_1.Object.emp_salary
FOR li_cnt = 1 to 100
    li_data = dwo_empsalary[li_cnt]
    .........
NEXT

这 种 表 达 式 法 返 回 的 数 据 类 型 是ANY 型 的，PowerBuilder 将 根 据“ 兼 容” 原 则 转 换 数 据 库 与PowerBuilder 的 数 据 类 型， 因 此 如 果 您 调 用 的 重 载 函 数 要 使 用 表 达 式 法 得 到 数 据 窗 口 的 数 据 作 为 参 数， 建 议 您 一 定 使 用 一 个 强 制 转 换 数 据 类 型 的 函 数。 例 如：

    wf_overload(real(dw_1.object.dept_id[1]))


张健姿-->PowerBuilder系列讲座-->28. PowerBuilder系列讲座

28. PowerBuilder系列讲座

笔 者 经 常 遇 到 有 用 户 问： 使 用PowerBuilder 开 发 的 应 用 程 序 交 付 运 行 时 需 要 怎 样 的 运 行 环 境。 当 我 告 诉 他 是pb5dk 下 的 所 有 文 件 时， 他 又 会 抱 怨 说， 一 个 原 本 不 大 的 应 用 程 序， 运 行 环 境 却 占 了 打 包 文 件 的 大 半， 释 放 后 在 用 户 的 机 器 中 更 是 要 占7M 之 多。 事 实 上 这 些 动 态 连 接 库 是 用 以 解 释 不 同 的 对 象， 或 是 被 不 同 对 象 的 函 数 所 调 用， 如 果 在 您 的 小 应 用 中 某 些 对 象 根 本 没 有 用 到， 则 与 其 相 关 的 动 态 连 接 库 也 就 不 必 须 了。

此 外 如 果 您 的 开 发 环 境 的 硬 盘 空 间 也 十 分 局 促， 有 些 开 发 环 境 中 的 动 态 连 接 库 似 乎 也 可 以 临 时 删 去。 下 面 列 出 的 是 开 发 和 运 行 环 境 中 的 所 有 动 态 连 接 库 的 作 用， 也 许 在 您 安 装 和 打 包 时 会 有 所 作 用。
Powerbuilder 5.0 DLL | DLL 作 用 描 述 | 运 行 环 境 | 平 台
pbapl050.dll | 应 用 画 笔 | 无 | 全 部
pbbgr050.dll | 商 业 图 形 引 擎 | 有 | 全 部
pbcgc050.dll | 代 码 生 成 － 编 译 | 无 | Win32
pbcgl050.dll | 代 码 生 成 － 链 接 | 无 | Win32
pbcgr050.dll | 代 码 生 成 － 资 源 编 译 | 无 | Win32
pbcgs050.dll | 代 码 生 成 －Win16 编 译 | 无 | Win32
pbch050.dll | 代 码 生 成 － 头 文 件 | 无 | Win32
pbcmp050.dll | Powerscript 编 译 器 | 无 | Win32
pbctl050.dll | 窗 口， 报 表， 用 户 自 定 义 对 象 画 笔 | 无 | 全 部
pbdbl050.dll | 数 据 库 接 口 － 与pbsyb 的 消 息 句 柄 | 有 ( 可 选) | Win16, Win32
pbdbt050.dll | 数 据 库 接 口 － 与pbsyb 的 消 息 句 柄 | 有 ( 可 选) | Win16, Win32
pbdpb050.dll | 分 布 式PB - 本 地 驱 动 | 有 ( 可 选) | Win16, Win32
pbdpp050.dll | Pipeline 画 笔 | 无 | 全 部
pbdse050.dll | 分 布 式PB | 有 ( 可 选) | Win16, Win32
pbdtp050.dll | 数 据 库 画 笔 | 无 | 全 部
pbdts050.dll | 数 据 库 画 笔 工 具 | 无 | 全 部
pbdwd050.dll | 数 据 窗 口 绘 画 画 笔 | 无 | 全 部
pbdwe050.dll | 数 据 窗 口 引 擎 | 有 | 全 部
pbdwo050.dll | 数 据 窗 口 用 对 话 框 | 无 | 全 部
pbdwp050.dll | 数 据 窗 口 画 笔 | 无 | 全 部
pbdwt050.dll | 数 据 窗 口 预 览 | 无 | 全 部
pbeol050.dll | PB 库 接 口 - Object Cycle | 无 | Win16, Win32
pbfrm050.dll | 报 表 画 笔 | 无 | Win16, Win32
pbgen050.dll | 应 用 模 板 | 无 | 全 部
pbhlp050.dll | 帮 助 文 件 工 具 | 无 | Win16, Win32
pbibm050.dll | 数 据 库 接 口 －IBM | 有 ( 可 选) | Win16
pbidbf50.dll | 数 据 窗 口 输 入 Dbase 文 件 | 有 ( 可 选) | Win16, Win32
pbin5050.dll | 数 据 库 接 口 －Informix 5.0 | 有 ( 可 选) | Win16, Win32
pbitxt50.dll | 数 据 窗 口 输 入 Dbase 文 件 | 有 ( 可 选) | 全 部
pblbm050.dll | PB 库 接 口- LBMS | 无 | Win16
pblib050.dll | PB 库 画 笔 和 浏 览 器 | 无 | 全 部
pbmdi050.dll | 数 据 库 接 口 - MDI | 有 ( 可 选) | Win16, Win32
pbmnu050.dll | 菜 单 画 笔 | 无 | 全 部
pbmss050.dll | 数 据 库 接 口 - MS SQL Server 6.0 | 有 ( 可 选) | Win16, Win32
pbndv050.dll | PB 库 画 笔- Endeavor | 无 | Win16
pbnet050.dll | 数 据 库 接 口 - net gateway 有 ( 可 选) | Win16
pbnpc050.dll | 分 布 式PB - 命 名 管 道 客 户 端 | 有 ( 可 选) | Win32
pbnps050.dll | 分 布 式PB - 命 名 管 道 服 务 器 端 | 有 ( 可 选) | Win32
pbo71050.dll | 数 据 库 接 口 - Oracle 7.1 | 有 ( 可 选) | Win16, Win32
pbo72050.dll | 数 据 库 接 口 - Oracle 7.2 | 有 ( 可 选) | Win32
pbodb050.dll | 数 据 库 接 口 - ODBC | 有 ( 可 选) | Win16, Win32
pbor7050.dll | 数 据 库 接 口 - Oracle 7.0 | 有 ( 可 选) | Win16, Win32
pborc050.dll | ORCA | 无 | Win16, Win32
pbosc050.dll | 分 布 式PB - open server 客 户 端 | 有 ( 可 选) | Win16, Win32
pboss050.dll | 分 布 式PB - open server 服 务 器 端 | 有 ( 可 选) | Win32
pboui050.dll | OLE 2 用 户 接 口 | 有 | Win16
pbpvc050.dll | PB 库 接 口 - PVCS | 无 | Win16, Win32
pbroi050.dll | OLE 2 支 持 软 件 | 有 | Win16, Win32
pbrtc050.dll | RTF 支 持 软 件 | 有 ( 可 选) | Win16, Win32
pbrte050.dll | 运 行 环 境 引 擎 | 有 | 全 部
pbrtf050.dll | 运 行 环 境 函 数 | 有 | 全 部
pbscr050.dll | Script 画 笔 | 无 | 全 部
pbshr050.dll | 打 印 | 有 | 全 部
pbsmi050.dll | 分 布 式PB | 有 ( 可 选) | Win16, Win32
pbsql050.dll | SQL 画 笔 | 无 | 全 部
pbstr050.dll | 结 构painter | 无 | 全 部
pbsyb050.dll | 数 据 库 接 口 - Sybase dblib 接 口（ 与 微 软 库 相 连） | 有 ( 可 选) | Win16, Win32
pbsyc050.dll | 数 据 库 接 口 - Sybase ctlib 接 口 | 有 ( 可 选) | Win16, Win32
pbsys050.dll | 开 发 系 统 | 无 | 全 部
pbsyt050.dll | 数 据 库 接 口 - Sybase dblib 接 口( 与Sybase 库 向 连） | 有 ( 可 选) | Win32
pbtbl050.dll | 数 据 库 表 画 笔 | 无 | 全 部
pbtra050.dll | 数 据 库 接 口 - 跟 踪 数 据 库 | 无 | Win16, Win32
pbtyp050.dll | 系 统 对 象 和 函 数 的 定 义 | 有 | 全 部
pbudo050.dll | 用 户 对 象 画 笔 | 无 | 全 部
pbvbx050.dll | VBX | 有 | Win16
pbwed050.dll | 编 辑 器 | 无 | Win16, Win32
pbwei050.dll | 编 辑 器 - 自 动 缩 进 | 无 | Win16, Win32
pbwep050.dll | 编 辑 器 - Powerscript 颜 色 | 无 | Win16, Win32
pbwes050.dll | 编 辑 器 - SQL 颜 色 | 无 | Win16, Win32
pbwin050.dll | 窗 口 画 笔 | 无 | 全 部
pbwsc050.dll | 分 布 式PB - winsock 客 户 端 | 有 ( 可 选) | Win16, Win32
pbwss050.dll | Dist PB - winsock 服 务 器 端 | 有 ( 可 选) | Win32

SQL / SCRIPTS
SQLCA.SCR          用于定义SQLCA的Script
DB2SYSPB.SQL       创建和初始化存储表
PBOR7CAT.SQL       在Oracle的Master数据库中创建内部的存储过程
PBSYB.BAT          使用W3DBLIB在Sybase的Master数据库中创建由
PowerBulder使用的内部的存储过程
PBSYB.SQL          使用W3DBLIB在Sybase的Master数据库中创建由
PowerBulder使用的内部的存储过程
PBSYBRT.SQL        创建用于运行环境的Sybase存储过程
PBSYC.BAT          使用CTLIB在Sybase系统10中的Master数据库中创建
由PowerBulder使用的内部的存储过程
PBSYC.SQL          使用CTLIB在Sybase系统10中的Master数据库中创建
由PowerBulder使用的内部的存储过程
SYBSYSPB.SQL       创建和初始化Sybase存储表
ORCA
PBORCA.H           当使用ORCA编写接口时需要
PBORC.LIB          当使用ORCA编写接口时需要
Sybase SQL Anywhere
数据库引擎：在SQLANY50\WIN目录下。
DB32W    EXE        2,476 04-01-96  12:00p
DBBACKW.EXE         数据库备份（windows）
DBCOLLW.EXE         数据库字符集顺序信息
DBENG50S EXE        16位数据库引擎
DBENG50W HLP        32位数据库引擎帮助文件
DBENG50W EXE        32位数据库引擎776,608 04-01-96  12:00p
DBERASEW EXE        删除一个数据库（Windows）
DBEXPANW EXE        扩展数据库
DBEXTW50 DLL        数据库引擎扩展
DBINFOW  EXE        显示数据库文件的数据库信息（Windows）
DBINITW  EXE        创建数据库
DBL50T   DLL	    Windows NT数据库引擎的嵌入C程序的接口
DBL50W   DLL        Windows 3.1数据库引擎的嵌入C程序的接口
DBLOGW   EXE        用于改变数据库日志名或删除日志用
DBSHRINW EXE        压缩数据库
DBSTARTW EXE        数据库16位引擎
DBSTOPW  EXE        停止16位数据库引擎
DBTL50W  DLL        SQL Anywhere数据库工具(Windows)
DBTRANW  EXE        将LOG文件转换成SQL命令集
DBUNLOAW EXE        恢复(unload)数据库(Windows)
DBUPGRDW EXE        升级早期版本的数据库
DBVALIDW EXE        检验数据库有效性(Windows)
DBWE50   DLL        数据库引擎扩展
DBWRITEW EXE        创建写文件来更新被压缩的数据库(Windows)
ISQLW    EXE        交互式SQL用户界面
RT32W    EXE        旧32位Windows运行版的引擎
RTDSK50S EXE        16位Windows数据库引擎的运行版
RTDSK50W EXE        32位Windows数据库引擎的运行版
RTSTARTW EXE        旧32位Windows运行版的引擎
WL50EN.DLL          英语动态连接库
WL50FR.DLL          法语动态连接库
WL50GE.DLL          德语动态连接库
WOD50W   DLL        SQL Anywhere ODBC引擎的动态连接库
WODBC    HLP        ODBC帮助文件
INFORMIX
LDLLSQLW.DLL        API接口

有关ODBC的文件：
CTL3DV2.DLL         3维动态连接库版本2
ODBC.INI            ODBC驱动的管理器(安装在windows目录下)
ODBC.DLL            ODBC驱动的管理器(安装在windows\system目录下)
ODBCINST.DLL        安装的ODBC驱动 (安装在windows\system目录下)
ODBCINST.INI        安装的ODBC驱动(安装在windows目录下)

* 说 明SQL Anywhere 数 据 库 引 擎 的 运 行 版 是 数 据 库 引 擎DBENG50W.EXE 的 子 集， 其 中 减 少 了 创 建 数 据 库 表， 动 态 截 断 日 志 等 功 能。

PowerBuilder 的 高 级 工 具：

对PowerBuilder 的 高 级 工 具 的 处 理，4.0 与5.0 有 一 些 差 别， 在4.0 中 每 一 个 小 工 具 都 是 一 个 单 独 的.EXE 文 件， 而 在5.0 中 所 有 的 工 具 都 集 成 在 了 一 起， 形 成 一 个 可 执 行 文 件， 而 每 一 个 工 具 都 由 一 个PBD 文 件 组 成。

APU. EXE，APU50. PBD，APU50.INI，APUPFC.PBD 组 成 了 一 个 集 成 环 境。 其 中 包 括 的 工 具 有：DWCHECK.PBD，DWCHECK.INI 用 于 数 据 窗 口 中SQL 语 句 的 确 认；DWEAS.PBD，DWEAS.INI 数 据 窗 口 扩 展 属 性 定 义；OBJSRCH.PBD，OBJSRCH.INI 用 于 对 象 的 搜 索；PEAR.PBD，PEAR.INI 可 以 生 成PowerBuilder 在 数 据 库 中 创 建 的 扩 展 属 性 的 文 档；SPUD.PBD，SPUD.INI ，SPUDERR.TXT 是 存 储 过 程 更 新 的 工 具；XREF.PBD，XREF.INI 用 于 交 叉 引 用； 交 叉 引 用 所 用 到 的 数 据 库 为XREF.DB， 如 果 建 在 后 台 的 大 型 数 据 库 上，XREFINF.SQL，XREFOR7.SQL，XREFSYB.SQL 这 三 个 文 件 分 别 是 用 于 在 您 所 使 用 的Informix，Oracle 和Sybase 上 创 建 对 象 表 的SQL 语 句。


张健姿-->PowerBuilder系列讲座-->29. PowerBuilder系列讲座(29)

29. PowerBuilder系列讲座(29)

我 们 在 使 用PowerBuilder 编 程 的 过 程 中， 总 是 不 可 避 免 地 要 用 到BLOB（Binary Large Objects ， 大 型 二 进 制 对 象) 字 段 及 其 相 关 函 数。 这 里 我 们 来 介 绍 一 下 使 用BLOB 的 方 法

何 时 使 用BLOB 数 据 类 型

在 下 列 情 况 下， 我 们 可 能 要 在PowerScript 语 言 中 使 用 到BLOB 数 据 类 型：

  • 您 要 将OLE 对 象（ 如 图 形、 声 音 等） 存 入 您 的 数 据 库 中；
  • 您 要 将 大 型 的 二 进 制 对 象 存 入 您 的 数 据 库 中；
  • 您 所 要 操 纵 的 文 本 对 象 过 大， 以 致 于 一 般 的 字 符 串 函 数 无 法 对 其 操 作；
  • 您 所 使 用 数 据 库 的 数 据 类 型PowerBuilder 不 能 支 持， 如SQLBase 中longvarchar 这 一 数 据 类 型， 所 以 您 只 能 使 用blob 函 数 对 其 进 行 操 纵。

您 所 使 用 的DBMS 中， 怎 样 的 数 据 类 型 可 以 在PowerScript 中 与BLOB 数 据 类 型 相 对 应 ？

这 里 罗 列 的 一 些 常 见 数 据 库 的 数 据 类 型， 其 它 类 型 的 数 据 库 请 查 询 该 数 据 库 的 相 关 文 档， 及PowerBuilder 的 用 户 手 册。
DBMS | 数 据 类 型
Watcom SQL | long binary, long varchar
Oracle | longraw
SQL Server | Image, Text
Informix | Text
MDI Gateway DB/2 | Long varchar
SQLBase | longvarchar
AllBase | longvarchar
DB2/2 | N/A

二、 在 数 据 窗 口 中 使 用OLE 列

在 数 据 窗 口 中 使 用OLE 列， 必 须 满 足 以 下 要 求：

  • 包 括OLE 列 的 表 必 须：
    具 有 一 个 可 以 对 应 成 为BLOB 的 数 据 类 型；
    一 个OLE 列 必 须 允 许 为 空；
    必 须 有 主 键；
  • 如 果 您 使 用 的 开 发 环 境 是SQL Server， 您 必 须 将AUTOCOMMIT 设 置 为TRUE；
  • 在 数 据 窗 口 画 笔 中 指 定 数 据 源 的 列 名 时， 存 储BLOB 数 据 的 列 不 能 被 包 括；

在 数 据 窗 口 设 计 时， 选 择 菜 单Ojbect|OLE Database Blob， 然 后 在 设 计 窗 口 上 空 点 一 下， 这 时 会 出 现 这 样 的 一 个 屏 幕：

在 这 个 定 义 属 性 的 窗 口 中， 大 部 分 的 内 容 与 定 义 其 它 数 据 窗 口 列 属 性 的 窗 口 大 致 相 当， 只 有 这 个definition 项 与 其 它 列 不 同， 这 里 特 别 说 明 一 下：

上 面 几 项 内 容 的 填 写 较 易 理 解， 事 实 上 只 要 保 留 其 缺 省 值 就 可 以 了。

Key Clause: 这 一 项 的 内 容 是PowerBuilder 用 以 确 定 在select 和update 语 句 中 如 何 使 用WHERE 子 句 的。 一 般 我 们 使 用 这 一 个 表 的 主 键 就 可 以 了。 在 这 个 例 子 里， 就 相 当 于where 子 句 是"... where id =: id"。 请 注 意 理 解 这 两 个“id” 不 同 的 含 义 和 各 自 的 指 代。

File Template: 用 以 指 定 您 所 希 望 的 这 个OLE 对 象 使 用 的 公 共 模 板。 在 本 列 中， 我 们 保 留 其 为 空。 但 是 如 果 您 存 储 的 是word 文 件， 您 就 应 当 指 定 一 个DOC 文 件， 或 者 是normal.dot 文 件 等。

OLE Class: Description (Only Class is used) : 如 果 您 没 有 使 用 文 件 模 板， 您 就 应 当 在 这 里 指 定 一 个 应 用 类， 本 例 中 我 们 使 用PaintBrush。 如 果 您 打 算 使 用 的 应 用 类 并 没 有 包 括 在 这 个 下 拉 式 列 表 框 中， 您 就 应 当 在Windows 的 注 册 文 件 中 加 入： 在Windows3.1 中 您 应 当 修 改win.ini 文 件， 而 在Windows95 和Windows NT 中 您 应 当 调 用REGEDIT 应 用 程 序。

Client Name Expression: 这 个 表 达 式 是 用 以 得 到blob 列 中 每 行 的 不 同 值。 这 可 以 使OLE 对 不 同 的blob 行 引 用 不 同 的 对 象。

完 成 了 数 据 窗 口 的 设 计 后， 您 可 以 在 其 设 计 预 览 方 式 下 测 试 这 个OLE 列。 只 要 在 这 个 对 象 上 双 击，PowerBuilder 就 会 自 动 启 动 服 务 器 应 用， 进 入 一 个 新 文 件 的 编 辑 和 一 个 模 板 拷 贝 的 编 辑， 这 当 然 要 根 据 您 设 定 的 是 对 象 类 还 是 文 件 模 板。 为 了 使 这 个OLE 列 更 醒 目， 您 可 以 在 其 背 后 加 入 一 幅 图， 或 加 上 边 框。

当 您 确 实 地 在 这 一 列 中 存 储 了 数 据， 在 这 一 列 的 位 置 上 就 会 出 现 一 个 与 您 所 使 用 的OLE 服 务 器 应 用 相 对 应 的 图 标。

对OLE 列 的 更 新
OLE 列 只 能 够 在 数 据 窗 口 中 得 以 更 新。OLE 对 象 存 储 在OLE 对 象 容 器(container) 之 中， 并 为OLE 服 务 器 提 供 信 息 和 它 们 的 类 名。 当 其 中 的 内 容 更 新 时， 这 个 容 器 必 须 保 留。 这 个 功 能 是 设 计 在 数 据 窗 口 和OLE 列 对 象 之 内 的， 可 是 如 果 您 打 算 在 数 据 窗 口 之 外 更 新 这 个Blob 对 象， 您 就 将 破 坏 这 个 容 器。

使 用OLEActivate 函 数
如 果 您 打 算 使 用 程 序 激 活 一 个OLE 列， 而 不 是 等 用 户 双 击 这 个 列， 您 就 可 以 使 用OLEActivate 函 数。 这 个 函 数 的 语 法 为：

datawindowcontrol.OLEActivate ( row, column, verb )

此 外 您 打 算 以 非 零 的“verb” 值 激 活OLE 列， 也 需 要 用 到 这 个 函 数。 关 于“verb” 值 的 定 义， 是 根 据 不 同 的OLE 服 务 器 而 定 的， 您 必 须 查 询 所 使 用 的OLE 服 务 器 文 档。 使 用windows95 的 用 户 可 以 用REGEDIT 查 询 注 册 窗 口 的 数 据 库。 键 入REGEDIT /V 可 以 看 到 整 个 注 册 窗 口 的 树 型 列 表。

例 如 我 们 有 一 个 名 为dw_sound 的 数 据 窗 口， 其 中 有 一 个OLE 列 为“sound_blob” 包 含 的 是.wav 文 件， 我 们 希 望 使 用 微 软 的Quick Recorder 作 为OLE 服 务 器， 我 们 可 以 这 样 调 用 这 个 函 数

dw_sound.OLEActivate(5, "sound_blob", 1)
在Quick Recorder 中“verb” 值 为1 表 示 编 辑。
又 如：
dw_sound.OLEActivate(2, 4, 0)

表 示 激 活OLE 服 务 器 调 用 数 据 窗 口 中 第 二 行 第 四 列 的 数 据，“verb” 为0 表 示 演 奏 这 个.wav 文 件。

使 用Sybase 用 户 应 注 意 的 问 题：

Sybase 对 于OLE 列 的 控 制 同 其 它 数 据 库 比 有 一 些 小 小 的 限 制， 这 体 现 在Sybase 的text 数 据 类 型 上。 由 于Sybase 的 字 符 串 函 数 的 作 用， 作 为blob 字 段 的text 数 据 类 型 最 大 只 能 到4K。

三、 不 使 用OLE 方 式 对Blob 进 行 操 作：

在PowerScript 中 提 供 给 您 许 多 不 使 用OLE 而 处 理 大 型 二 进 制 大 型 的 方 法。 例 如 下 例 将 数 据 读 入Blob 列：

/* 本 段 代 码 将 读 取 一 个 超 过32766 字 节 的 文 件， 这 个 代 码 可 以 用 以 读 取Text 文 本 和Bitmap 图 象。*/

SetPointer(HOURGLASS!)
int li_filenum, li_loops, li_counter long ll_filelen, ll_ll_bytes_read, ll_new_pos
blob lb_ourblob, lb_tot_b
ll_filelen = FileLength(sle_filename.text)
// 获 取 文 件 的 大 小
li_filenum = FileOpen(sle_filename.text,STREAMMODE!,
READ!,LOCKREAD!)
// 指 定 该 文 件 为 只 读，li_filenum 为 这 个 文 件 的 句 柄
IF ll_filelen > 32766 THEN
    li_loops = ((ll_filelen-1)/32766) + 1
ELSE
li_loops = 1
END IF
/*FileRead() 函 数 不 支 持 读 取 大 于32K 的 文 本，
计 算 将 使 用 FileRead 函 数 的 次 数*/
// 读 文 件...
FOR li_counter = 1 to li_loops
    ll_bytes_read = FileRead(li_filenum,lb_our_blob )
    lb_tot_b = lb_tot_b + lb_our_blob
    ll_new_pos = ll_new_pos + ll_bytes_read
    FileSeek(li_filenum,ll_new_pos,FROMBEGINNING!)
NEXT
FileClose(li_filenum)

// 现 在lb_tot_b 已 包 括 了 文 件 的 内 容， 如 果 这 个 文 件 是 位 图 文 件， 可 对 其 使 用SetPicture 函 数， 如 果 是 文 本 文 件， 可 使 用string() 函 数 将 其 转 成text 文 件。
SetPicture(p_my_picture, lb_tot_b)

现 在 您 已 有 了 一 个 存 在blob 变 量 中 的BLOB 数 据， 您 可 以 将 其 写 入 数 据 库 中。 如 果 您 不 打 算 使 用OLE， 您 可 以 使 用 在PowerScript 中 使 用SELECTBLOB，UPDATEBLOB 语 句。 同 时， 必 须 满 足 以 下 条 件：

1. 您 必 须 在 数 据 库 中 定 义 有 与blob 数 据 类 型 相 等 的 数 据 类 型（ 如 在Watcom 中 的Long Varchar 和Long Binary， 在SQL Server 中 的Image， 在Oracle 中 的long raw 等）。
2. 每 一 个 定 义 有blob 字 段 的 表 必 须 有 主 键。
3. blob 列 必 须 允 许 为 空。
4. 您 打 算 写 入blob 数 据 的 这 一 条 记 录 必 须 已 经 输 入 数 据， 也 就 是 说 您 只 能 使 用UPDATE 的 方 式 写 入blob 数 据。
5. SQL SERVER 的 用 户 必 须 在 使 用BLOB 功 能 前 将AUTOCOMMIT 设 置 为TRUE， 在BLOB 功 能 结 束 后 可 再 恢 复 回FALSE 状 态。 可 是 有 关BLOB 的 功 能 却 不 能 同 其 它 操 作 在 同 一 个 事 物 之 中。

例 如 我 们 在Watcom 数 据 库 中 有 一 个 称 作blob_table 的 表 其 中 有 两 个 字 段 分 别 为blob_id， 整 型， 主 键；blob_col 数 据 类 型 为long binary 并 且 将 其 设 置 为 允 许 为 空。 在 我 们 将blob 写 入 数 据 库 之 前， 我 们 必 须 将 主 键 的 值 写 入 数 据 库。 我 们 在 表 中 加 入 一 个 新 记 录，blob_id 的 值 为373， 而blob_col 设 置 为NULL。 然 后 我 们 执 行 下 面 的 代 码：

UPDATEBLOB blob_table SET blob_col = :lb_tot_b
WHERE blob_id = 373 USING SQLCA;
IF sqlca.sqlcode <> 0 then
    messagebox("UPDATEBLOB 失 败", sqlca.sqlerrtext)
END IF

与UPDATEBLOB 相 对 应 的 是SELECTBLOB， 使 用SELECTBLOB 同 使 用SELECT INTO 语 句 基 本 相 同， 只 是 这 时 我 们 操 纵 的 是BLOB 数 据 类 型 而 已。 例 如：

blob lb_blob_var
SELECTBLOB blob_col INTO :lb_blob_var FROM blob_test
WHERE blob_id = 423  USING SQLCA;

现 在 您 就 可 以 通 过Powerscript 操 纵 得 到 的blob 变 量 了。

注 意： 您 可 以 使 用UPDATEBLOB 语 句 修 改 多 条 记 录， 使 这 些 记 录 具 有 相 同 的 值， 但 是 您 却 不 能 使 用SELECTBLOB 语 句 得 到 多 于 一 条 的 记 录。 您 必 须 使 用WHERE 条 件 确 保SELECTBLOB 查 询 到 的 是 一 行 记 录。
您 也 可 以 将 从 数 据 库 读 出 的blob 数 据 写 入 本 地 的 文 件 中， 方 法 与 读 文 件 相 似。

四 其 它Blob 字 段 的 函 数

此 外PowerBuilder 还 提 供 了 其 它 三 个 有 关 对Blob 函 数 的 操 作： Blob(), BlobEdit() and BlobMid()。 这 几 个 函 数 的 使 用 您 可 以 查 询 有 关 的 文 档。


张健姿-->PowerBuilder系列讲座-->30. 在数据窗口中使用滚动条

30. 在数据窗口中使用滚动条

在 数 据 窗 口 中， 我 们 总 不 可 避 免 要 使 用 到 滚 动 条。 在 缺 省 状 态 下， 当 用 户 点 击 垂 直 滚 动 条 时， 当 前 的 数 据 窗 口 滚 动 显 示 一 页 或 一 行 新 内 容， 可 是 有 时 我 们 希 望 在 用 户 点 击 滚 动 条 时， 系 统 有 另 外 的 响 应。 在 数 据 窗 口 的 事 件 中， 全 局 变 量message 中wordparm 属 性 指 示 了 是 何 种 滚 动 类 型 导 致 了 这 个 事 件 的 发 生。

WordParm 的 值 | 垂 直 滚 动 类 型
0 | 行 向 上 滚 动
1 | 行 向 下 滚 动
2 | 页 向 上 滚 动
3 | 页 向 下 滚 动
4 | 竖 直 移 动

通 过 判 断 这 些 滚 动 类 型， 我 们 就 可 以 改 变 它 的 缺 省 动 作， 手 工 对 滚 动 方 式 进 行 控 制。 例 如 在 某 些 情 况 下， 我 们 希 望 用 户 点 击 滚 动 条 时， 记 录 能 够 一 行 一 行 的 滚 动。 下 列 的 代 码 可 以 在tabular、freeform 和grid 表 现 形 式 的 数 据 窗 口 中， 实 现 这 样 的 功 能。 在数据窗口的OTHER事件中： integer li_wordparm li_wordparm = Message.WordParm if Message.Number = 277 then if li_wordparm = 1 or li_wordparm = 3 then //捕捉行向下滚动和页向下滚动的事件， 使屏幕向下滚动一条记录，并将数据窗口 //的聚焦向下移动一行 dw_1.ScrollToRow( dw_1.GetRow() + 1) elseif li_wordparm = 0 or li_wordparm = 2 then //捕捉行向下滚动和页向下滚动的事件， 使屏幕向下滚动一条记录，并将数据窗口 //的聚焦向上移动一行 dw_1.ScrollToRow(dw_1.GetRow() - 1) else RETURN end if //避免缺省的数据窗口的滚屏行为 Message.Processed=TRUE end if 在 开 发 过 程 中， 我 们 可 能 会 大 量 的 使 用Master/Detail 形 式 的 数 据 窗 口 来 表 现 数 据。 所 谓Master/Detail 风 格 就 是 在 一 个 窗 口 中 有 两 个 数 据 窗 口 组 成 主 表 和 细 目 表， 分 别 显 示 数 据 库 中 的 两 张 相 关 联 的 表， 这 一 格 式 可 用 于 展 现 给 定 事 物 的 两 套 数 据 的 关 系。

使 用Master/Detail 形 式 的 一 种 可 能 性 是 使 两 个 数 据 窗 口 显 示 同 一 套 数 据， 其 中Master 的 数 据 用 于 浏 览， 不 能 修 改， 而Detail 的 数 据 窗 口 显 示 的 是 与Master 窗 口 的 相 关 的 更 为 详 细 的 信 息， 是 可 以 修 改 的。 这 时 您 希 望 用 户 只 能 够 滚 动Master 窗 口， 同 时 使Detail 窗 口 显 示 响 应 的 信 息， 而 不 希 望 用 户 能 够 使 用 滚 动 条 来 滚 动Detail 数 据 窗 口， 翻 看 无 关 的 信 息。

下 面 的 代 码 就 是 用 于 避 免 用 户 滚 动Detail 数 据 窗 口， 翻 看 到 其 它 行 的 记 录。

事 件 名 称：Keypressed

描 述：Keypressed 为 一 个 用 户 自 定 义 事 件， 在 数 据 窗 口 控 件 的Script 画 笔 中 定 义， 它 的 事 件 编 号 为pbm_dwnkey， 当 用 户 有 按 键 操 作 时 触 发。

Script：
IF (KeyDown(keytab!)) OR (KeyDown(keyEnter!)) OR
(KeyDown(keyDownArrow!))  &
OR	(KeyDown(KeyUpArrow!)) OR (KeyDown(KeyPageDown!))
OR &
    KeyDown(KeyPageUp!)) &
THEN
    This.SetRedraw(False)

// 当 用 户 按 下 上 述 键 时， 系 统 将 不 与 响 应， 避 免 数 据 窗 口 自 动 进 行 相 应 的 操 作
END IF

在 上 面 的 的 事 件 中， 我 们 使 用 了SetReDraw() 函 数， 这 将 阻 塞 用 户 对 数 据 窗 口 诸 如 更 新 等 操 作， 我 们 必 须 在 下 列 事 件 中 取 消 用 户 对 数 据 窗 口 进 行 正 常 操 作 的 阻 塞。

事 件 名 称：	RowFocusChanged
Script：
This.ScrollToRow(myrow)
//myrow是一个实例变量，它的值是这个数据窗口当前应当显示的记录数
This.SetRedraw(True)
// 允 许 对 数 据 窗 口 进 行 操 作
事 件 名 称：ItemFocusChanged
Script：
This.SetRedraw(True)
//允许用户在同一条记录内进行水平滚动

在 有 些 情 况 下， 我 们 会 使Master 和Detail 两 个 数 据 窗 口 显 示 相 同 的 内 容， 我 们 可 以 使 用ShareData() 函 数 令Detail 数 据 窗 口 共 享Master 窗 口 的 数 据， 如 何 在 使 用 滚 动 条 滚 动 其 中 的 一 个 窗 口 时， 另 一 个 窗 口 能 够 同 步 进 行 滚 动 呢 ？ 如 果 您 没 有 对Master 数 据 窗 口 使 用"Retrieve as Needed" 选 项， 这 一 个 功 能 的 实 现 是 十 分 简 单 的。

假 设Master 数 据 窗 口 名 称 为dw_1， 对 其SCROLLVERTICAL 事 件 编 程 如 下：

integer vmax_1, vpos_1,vmax_2, vpos_2
string r_code
decimal vmax_1_percent
vmax_1 = integer(dw_1.describe("datawindow.verticalscrollmaximum"))
vpos_1  =integer(dw_1.describe("datawindow.verticalscrollposition"))
vmax_2 = integer(dw_2.describe("datawindow.verticalscrollmaximum"))
vmax_1_percent = vpos_1 / vmax_1
vpos_2 = vmax_1_percent * vmax_2
r_code = dw_2.modify("datawindow.verticalscrollposition="+string(vpos_2))
// 检 验 是 否 修 改 成 功
if r_code <> "" then
    beep(6)
    mle_1.text = "dw_1scroll = "+r_code+" vpos_2 = "+string(vpos_2)
// 在scrollvertical 事 件 中 无 法 使 用MessageBox 弹 出 错 误 信 息 框
end if
Detail数据窗口名称[qu1]为dw_2，对其SCROLLVERTICAL事件编程如下：
integer vmax_1, vpos_1,vmax_2, vpos_2
string r_code
decimal vmax_2_percent
vmax_2 = integer(dw_2.describe("datawindow.verticalscrollmaximum"))
vpos_2  = integer(dw_2.describe("datawindow.verticalscrollposition"))
vmax_1 =integer(dw_1.describe("datawindow.verticalscrollmaximum"))
vmax_2_percent = vpos_2 / vmax_2
vpos_1 = vmax_2_percent * vmax_1
r_code = dw_1.modify("datawindow.verticalscrollposition="+string(vpos_1))
// 检 验 是 否 修 改 成 功
if r_code <> ""then
    beep(6)
    mle_1.text = "dw_1 mod "+r_code
end if

谈 到 这 里， 我 们 又 要 引 入 一 个 新 话 题， 这 就 是 在 某 些 事 件 中 避 免 使 用MessageBox 的 问 题：

当 用 户 进 行 的 错 误 操 作 时， 我 们 应 当 在 屏 幕 上 弹 出 一 个 提 示 框， 警 告 发 生 的 错 误 或 提 示 将 要 发 生 的 事 情， 以 引 起 用 户 的 注 意； 有 些 程 序 员 也 喜 欢 在 调 试 程 序 时， 使 用MessageBox 函 数 显 示 当 前 的 系 统 状 态。 可 是 在PowerBuilder 的 某 些 改 变 控 件 聚 焦 的 事 件 中， 系 统 是 无 法 显 示 信 息 框 的， 我 们 必 须 使 用response 类 型 的 窗 口 来 取 代 信 息 框： 在 这 些 事 件 中 使 用POST 方 式 调 用 一 个 新 事 件， 在 新 事 件 中 打 开 这 个response 窗 口。

某 些 窗 口 控 件（ 包 括 按 钮 在 内） 是 由 于 聚 焦 的 改 变 而 捕 获 鼠 标 的， 在 这 些 控 件 的 某 些 事 件 中 应 避 免 使 用MessageBox 函 数 的。 这 些 事 件 包 括 这 样 几 类：

1. 事 件 名 称：

Modified
GetFocus
LoseFocus
ItemFocusChanged
Activate
Deactivate

不 能 使 用 的 原 因： 由 于 聚 焦 的 改 变 而 导 致 循 环。

2. 事 件 名 称：

ScrollVertical
ScrollHorizontal
ScrollBar 对 象

不 能 使 用 的 原 因：MessageBox 将 导 致 消 息 队 列 的 过 载， 不 要 在 任 何 卷 滚 的 事 件 中 使 用MessageBox 函 数。

3. 事 件 名 称：
ReSize

不 能 使 用 的 原 因： 当 用 户 点 击MessageBox 的OK 框 后， 父 窗 口 将 重 新 获 得 聚 焦， 并 再 次 触 发resize 事 件， 弹 出 另 外 的 一 个MessageBox， 这 将 导 致 无 穷 循 环。

4. 事 件 名 称：
Open (Response 窗 口)

不 能 使 用 的 原 因： 这 将 在 打 开 窗 口 时 同 时 有 多 个 窗 口 模 板， 从 而 导 致 了 不 可 预 料 的 结 果。

此 外，MessageBox 还 将 触 发Activate 或Deactivate 事 件。 因 此 有 些 情 况 下， 您 可 以 将 信 息 写 在 窗 口 的 标 题、 微 帮 助 上， 或 是 使 用 单 行 编 辑 器， 使 用Beep() 函 数 有 时 也 可 以 达 到 指 示 作 用， 而 不 必 须 非 使 用MessageBox 不 可。

我 们 言 归 正 传， 当Master 数 据 窗 口 没 有 使 用"Retrieve as Needed" 选 项 时， 上 面 的 代 码 就 可 以 得 到 满 意 的 结 果， 而 如 果 您 为 了 得 到 更 快 的 响 应 速 度 而 使 用 了"Retrieve as Needed" 时， 结 果 就 没 有 这 么 简 单 了。 在 用 户 点 击dw_2 的 滚 动 条 使 之 滚 动 到 最 下 部 时， 因 数 据 窗 口 已 将 数 据 显 示 到 了 最 后 一 条， 这 就 触 发dw_1 从 后 台 数 据 库 中 攫 取 新 的 数 据， 这 样 当dw_1 数 据 窗 口 中 有 了 新 数 据， 系 统 会 自 动 发 出 消 息， 使 共 享 数 据 的dw_2 复 位， 滚 回 到 最 初 的 位 置。 这 样 将 触 发 了dw_2 的scrollvertical 事 件， 使dw_2 和dw_1 均 滚 回 到 初 始 的 位 置， 因 而 无 法 得 到 正 确 的 结 果。 如 何 解 决 这 个 问 题 呢 ？ 有 兴 趣 的 读 者 可 以 简 单 思 考 一 下， 我 们 将 在 下 期 对 这 一 问 题 进 行 进 一 步 的 讨 论。


张健姿-->PowerBuilder系列讲座-->31. 在数据窗口中使用滚动条（续）

31. 在数据窗口中使用滚动条（续）

在 上 期 中 我 们 讨 论 了 在Master/Detail 数 据 窗 口 中， 当Master 数 据 窗 口 没 有 使 用"Retrieve as Needed" 选 项 时， 两 个 数 据 窗 口 同 时 滚 动 的 问 题。 可 是 当Master 数 据 窗 口 使 用 了"Retrieve as Needed" 选 项 时， 我 们 却 遇 到 了 麻 烦。 在 点 击dw_2 的 滚 动 条 时， 如 果dw_1 从 数 据 库 中 取 得 了 新 数 据， 将 导 致dw_2 的 复 位，dw_2 就 不 能 显 示 正 确 的 结 果 了。 解 决 这 个 问 题， 我 们 最 容 易 想 到 的 解 决 方 法 就 是 利 用dw_2 在scrollvertical 事 件 中 获 得 的dw_2 当 前 滚 动 条 的 位 置， 对dw_2 进 行 重 新 赋 值。
即 对dw_1 的SCROLLVERTICAL 事 件 编 程 如 下：

integer vmax_1, vpos_1,vmax_2, vpos_2
string r_code
decimal vmax_1_percent
vmax_1 = integer(dw_1.describe("datawindow.verticalscrollmaximum"))
vpos_1  =integer(dw_1.describe("datawindow.verticalscrollposition"))
vmax_2 = integer(dw_2.describe("datawindow.verticalscrollmaximum"))
vmax_1_percent = vpos_1 / vmax_1
vpos_2 = vmax_1_percent * vmax_2
r_code = dw_2.modify("datawindow.verticalscrollposition="+string(vpos_2))
// 检 验 是 否 修 改 成 功
if r_code <> "" then
    beep(6)
    mle_1.text = "dw_1scroll = "+r_code+" vpos_2 = "+string(vpos_2)
//在scrollvertical事件中无法使用MessageBox弹出错误信息框
end if
对dw_2 的SCROLLVERTICAL 事 件 编 程 如 下：
integer vmax_1, vpos_1,vmax_2, vpos_2
string r_code
decimal vmax_2_percent
vmax_2 = integer(dw_2.describe("datawindow.verticalscrollmaximum"))
vpos_2  = integer(dw_2.describe("datawindow.verticalscrollposition"))
vmax_1 =integer(dw_1.describe("datawindow.verticalscrollmaximum"))
vmax_2_percent = vpos_2 / vmax_2
vpos_1 = vmax_2_percent * vmax_1
r_code = dw_1.modify("datawindow.verticalscrollposition="+string(vpos_1))
// 检 验 是 否 修 改 成 功
if r_code <> ""then
    beep(6)
    mle_1.text = "dw_1 mod "+r_code
end if
r_code = dw_2.modify("datawindow.verticalscrollposition="+string(vpos_2))
// 检 验 是 否 修 改 成 功
if r_code <> "" then
    beep(6)
    sle_1.text = "dw_2 mod" +  r_code
end if

键 入 上 述 代 码 后， 运 行 时 你 就 会 发 现 点 击dw_2 时 运 行 的 结 果 是 正 常 的， 然 而 点 击dw_1 的 滚 动 条 却 发 生 了 错 误。 这 是 为 什 么 呢 ？

首 先 我 们 还 是 对 用 户 点 击dw_1 的 滚 动 条 向 下 滚 动 时， 系 统 进 行 了 哪 些 操 作 进 行 一 下 仔 细 地 分 析： 当 用 户 点 击 鼠 标 使dw_1 滚 动 到 了 最 后 一 行 时， 这 时 系 统 没 有 立 即 进 行 显 示 滚 动 的 操 作， 它 首 先 发 现 数 据 窗 口 的Primary! 缓 冲 区 的 数 据 已 经 告 罄， 于 是 就 向 后 台 的 数 据 库 中 攫 取 数 据， 取 得 新 的 数 据 后， 系 统 会 将 与dw_1 共 享 数 据 的dw_2 复 位， 使 之 回 到 显 示 第 一 条 记 录 的 位 置。 这 就 触 发 了dw_2 的scrollvertical 事 件（ 我 们 称 之 为 事 件 一）。 在 这 个 事 件 中， 由 于 此 时dw_2 显 示 的 是 第 一 条 记 录， 它 将 修 改dw_1 的 滚 动 条 位 置， 使 之 也 显 示 第 一 条 记 录。 并 立 即 触 发dw_1 的scrollvertical 事 件 二。 在 事 件 二 中，dw_1 又 根 据 自 己 的 位 置 修 改dw_2， 但 是 由 于dw_2 修 改 前 后 的 位 置 相 同， 所 以 没 有 对 系 统 造 成 什 么 影 响， 又 回 到 了 事 件 一。 在 事 件 执 行 完 毕 后， 系 统 开 始 响 应 用 户 的 下 滚dw_1 的 操 作， 首 先 将dw_1 向 下 滚 动 一 条 记 录， 这 样dw_1 显 示 的 是 第 二 条 记 录， 并 触 发 了dw_1 的scrollvertical 事 件 三。 当 事 件 三 执 行 时， 将dw_2 也 滚 动 到 第 二 条 记 录， 并 触 发 了dw_2 的scrollvertical 事 件 四， 不 过 这 一 事 件 四 也 不 会 对 任 何 一 个 数 据 窗 口 造 成 影 响， 最 后 返 回 执 行 完 事 件 三。 这 就 完 成 了 用 户 点 击 鼠 标 后 的 一 连 串 响 应。 但 是 最 后 两 个 数 据 窗 口 却 显 示 的 是 第 二 条 记 录。 有 兴 趣 的 读 者 可 以 仔 细 分 析 这 一 系 列 事 件 发 生 的 顺 序。

用 户 点 击 滚 动 条 而 使 得 上 述 代 码 对 各 个 事 件 触 发 及 其 执 行 过 程 虽 然 十 分 复 杂， 并 不 是 每 一 个 人 都 有 兴 趣 对 其 分 析 清 楚， 但 是 这 个 代 码 的 错 误 却 是 一 目 了 然 的， 只 须 稍 加 测 试 就 可 发 现。 可 是 令 人 不 解 的 是 这 段 代 码 在 美 国 却 几 乎 被 视 作 经 典， 而 且 向 美 国PowerSoft 技 术 支 持 中 心 提 出 这 个 问 题， 那 里 的 工 作 人 员 也 会 引 用 这 段 代 码 作 为 回 答。

事 实 上 解 决 这 个 问 题 的 方 法 并 不 十 分 复 杂， 我 们 只 需 将 修 改 滚 动 条 位 置 的 时 间 稍 加 改 变 就 可 以 得 到 正 确 的 结 果。 首 先 我 们 在 这 两 个 数 据 窗 口 中 各 声 明 一 个ue_scrollvertical 的 用 户 自 定 义 事 件 事 件。 传 递 一 个 参 数， 参 数 名 为vpos，integer 型， 通 过 传 值 法 传 递。 然 后 键 入 下 列 代 码：

对dw_1 的SCROLLVERTICAL 事 件 编 程 如 下：

integer li_vmax_1, li_vpos_1,li_vmax_2, li_vpos_2
decimal ld_vmax_1_percent
li_vmax_1 = integer(dw_1.describe("datawindow.verticalscrollmaximum"))
li_vpos_1 = integer(dw_1.describe("datawindow.verticalscrollposition"))
li_vmax_2 = integer(dw_2.describe("datawindow.verticalscrollmaximum"))
ld_vmax_1_percent = li_vpos_1 / li_vmax_1
li_vpos_2 = ld_vmax_1_percent * li_vmax_2
if abs( li_vpos_2 - integer(dw_1.describe
("datawindow.verticalscrollposition")) ) > 1 then
//如dw_2应显示的位置与dw_2的当前位置不符，产生修改dw_2的事件
    event post ue_scrollvertical( li_vpos_2 )
end if
对dw_1 的UE_SCROLLVERTICAL 事 件 编 程 如 下：
事 件： 自 定 义ue_scrollvertical
参 数：vpos   integer 型
返 回 值：integer
string ls_ret_code
dw_2.modify("datawindow.verticalscrollposition="+string(vpos))
if ls_ret_code <> "" then
    beep(6)
    sle_1.text = "dw_1scroll = " + ls_ret_code + " vpos = " + string(vpos)
// 在scrollvertical 事 件 中 将 不 能 弹 出 错 误 信 息 框
end if
return 1
对dw_2 的SCROLLVERTICAL 事 件 编 程 如 下：
integer li_vmax_1, li_vpos_1,li_vmax_2, li_vpos_2
string ls_ret_code
decimal ld_vmax_2_percent
li_vmax_2 = integer(dw_2.describe("datawindow.verticalscrollmaximum"))
li_vpos_2 = integer(dw_2.describe("datawindow.verticalscrollposition"))
li_vmax_1 =integer(dw_1.describe("datawindow.verticalscrollmaximum"))
ld_vmax_2_percent = li_vpos_2 / li_vmax_2
li_vpos_1 = ld_vmax_2_percent * li_vmax_1
event post ue_scrollvertical(li_vpos_1)
//由于dw_1使用了retreive-as-needed，可能会自动将dw_2
滚回第一行，表现重新设置dw_2
ls_ret_code = dw_2.modify
("datawindow.verticalscrollposition="+string(li_vpos_2))
// 检 验 是 否 修 改 成 功
if ls_ret_code <> "" then
    beep(6)
    sle_1.text = "dw_2 mod" +  ls_ret_code
end if
对dw_2 的UE_SCROLLVERTICAL 事 件 编 程 如 下：
事 件： 自 定 义ue_scrollvertical
参 数：vpos   integer 型
返 回 值：integer
string  ls_ret_code
ls_ret_code = dw_1.modify("datawindow.verticalscrollposition="+string(vpos))
// 检 验 是 否 修 改 成 功
if ls_ret_code <> ""then
    beep(6)
    sle_1.text = "dw_1 mod " + ls_ret_code
end if

我 们 再 分 析 一 下 上 述 代 码 执 行 的 过 程： 用 户 点 击 了dw_1 的 滚 动 条， 使dw_1 将 滚 动 到 最 后 一 行， 产 生 了dw_1 的scrollvertical 事 件 一， 但 并 没 有 立 即 执 行， 只 放 在 了 事 件 队 列 的 后 面。 系 统 首 先 发 现 数 据 窗 口 的Primary! 缓 冲 区 中 数 据 为 空， 向 后 台 数 据 库 取 数 据， 并 使dw_2 复 位， 回 到 显 示 第 一 条 记 录 的 位 置。 这 触 发 了dw_2 的scrollvertical 事 件 二， 在 首 先 执 行 的 这 个 事 件 二 中，PowerBuilder 根 据dw_2 当 前 显 示 的 是 第 一 条 记 录 这 一 条 件， 产 生 一 个ue_scrollvertical 事 件 三， 将dw_1 也 滚 回 显 示 第 一 条 记 录， 但 是 这 个 事 件 三 并 没 有 立 即 触 发 执 行， 而 是 放 在 了 事 件 队 列 的 最 后。 当 事 件 二 执 行 完 毕 后， 系 统 响 应 用 户 对dw_1 进 行 的 向 下 翻 页 的 操 作， 并 触 发dw_1 事 件 一 的 执 行。 这 时dw_1 显 示 的 是 用 户 希 望 看 到 的 正 确 的 记 录，PowerBuilder 根 据dw_1 的 当 前 位 置， 计 算 出dw_2 与 之 相 对 应 的 位 置， 将 修 改dw_2 位 置 的 操 作ue_scrollvertical 事 件 四 放 在 了 事 件 队 列 的 最 后。 这 时 该 执 行 事 件 三 了， 事 件 三 修 改dw_1 滚 动 条 的 位 置 至 第 一 条 记 录， 这 一 操 作 触 发 了dw_1 的scrollvertical 事 件 五， 此 时 两 个 数 据 窗 口 均 显 示 的 是 第 一 条 记 录。 在 这 一 事 件 中，PowerBuilder 经 过 计 算， 得 到 了dw_2 应 显 示 的 位 置 与 当 前 位 置 相 等 的 结 论， 因 此 不 产 生ue_scrollvertical 事 件， 事 件 五 没 有 对 系 统 产 生 任 何 影 响。 接 下 来 执 行 的 是 事 件 队 列 中 仅 存 的 事 件 四， 在 这 个 事 件 里， 系 统 将dw_2 滚 动 至 正 确 的 显 示 位 置 上， 并 触 发 了dw_2 的scrollvertical 事 件 六。 在 事 件 六 中， 系 统 又 产 生dw_2 的ue_scrollvertical 事 件 七， 但 是 这 两 个 事 件 均 没 有 对 显 示 造 成 任 何 影 响， 这 两 个 事 件 执 行 过 程 中 也 没 有 再 产 生 新 的 事 件， 这 一 系 列 的 响 应 便 最 终 结 束 了。 而 最 终 的 结 果 是 两 个 数 据 窗 口 都 是 停 在 了 正 确 的 结 果 上 了。

同 理， 我 们 对 用 户 滚 动dw_2 后 系 统 的 响 应 进 行 细 致 地 分 析， 也 可 以 看 到 上 述 代 码 的 正 确 性， 这 里 就 不 再 赘 述 了。

我 们 在 本 篇 中 编 写 的 代 码 可 能 并 不 一 定 在 您 开 发 的 软 件 中 使 用 得 上， 但 是 如 果 您 能 够 对 这 一 代 码 执 行 的 过 程 全 部 自 行 分 析 清 楚， 或 者 有 足 够 的 耐 性 看 明 白 了 上 面 文 章 的 分 析， 那 么 您 对PowerBuilder 的 事 件 一 定 会 有 更 深 的 理 解。


张健姿-->PowerBuilder系列讲座-->32. 为下拉式数据窗口建立缓冲区

32. 为下拉式数据窗口建立缓冲区

在 上 两 期 文 章 中， 我 们 提 到 了 在Master/Detail 风 格 的 数 据 窗 口 中 使 用ShareData() 函 数， 可 以 保 持 两 个 数 据 窗 口 中 数 据 的 一 致 性。 其 实 这 个 函 数 还 有 着 其 他 很 多 不 同 的 用 途。 这 里 我 们 就 另 举 一 例。

许 多 最 终 用 户 在 使 用PowerBuilder 应 用 程 序 时 抱 怨 运 行 速 度 太 慢。 一 般 来 讲， 一 个 企 业 级 的 客 户/ 服 务 器 应 用 程 序 执 行 速 度 的 瓶 颈 并 不 在 于 代 码 执 行 的 效 率， 而 在 于 应 用 向 后 台 数 据 库 查 询 数 据 时 等 待 后 台 响 应 的 时 间。 改 进 应 用 软 件 速 度 的 关 键 在 于 减 少 应 用 向 后 台 数 据 库 查 询 的 数 据 量， 减 少 网 络 的 数 据 流 量， 其 中 减 少 下 拉 式 数 据 窗 口 的 查 询 量 就 是 一 个 有 效 的 方 法。

一 般 的， 为 了 用 户 输 入 方 便 和 避 免 废 键， 在 数 据 窗 口 某 些 数 据 列 使 用 下 拉 式 数 据 窗 口 是 程 序 员 经 常 采 用 的 方 法。 运 行 一 个 应 用， 某 一 段 时 间 所 处 理 一 般 都 是 相 同 或 相 近 的 事 务， 这 样 同 时 打 开 的 不 同 窗 口 和 数 据 窗 口 却 很 可 能 同 时 采 用 相 同 的 下 拉 式 数 据 窗 口。 在 显 示 这 些 窗 口 前， 当 系 统 执 行 主 数 据 窗 口 的Retrieve() 命 令 时， 总 是 要 首 先 到 数 据 库 中 查 询 这 些 下 拉 式 数 据 窗 口 的 数 据。 如 果 我 们 能 够 减 少 这 些 查 询， 进 而 减 少 网 络 流 量， 和 后 台 数 据 库 服 务 器 进 行 语 法 分 析， 设 计 查 询 算 法 和 执 行 查 询 的 时 间， 这 将 能 使 应 用 的 性 能 有 较 大 的 提 高。 这 里 我 们 采 用 的 方 法 是 在 客 户 端 建 立 一 个 保 存 结 果 集 的 缓 冲 区， 并 使 用ShareData 函 数 使 缓 冲 区 的 数 据 同 下 拉 式 数 据 窗 口 的 主 缓 冲 区 建 立 共 享。

我 们 将 这 个 数 据 缓 冲 区 建 立 在 一 个 不 可 视 的 窗 口 上， 在 这 个 窗 口 为 每 一 个 不 同 的 结 果 集 创 建 一 个 不 同 的 用 户 对 象。 首 先 我 们 创 建 一 个 标 准 的 不 可 视 的 用 户 对 象， 在 可 选 的 对 象 类 型 中 选 择DataStore， 我 们 并 不 需 要 为 这 个 用 户 对 象 编 写 任 何 代 码， 只 要 将 其 保 存 为u_dsa 即 可。

我 们 创 建 一 个 窗 口w_resultset_server 作 为 结 果 集 服 务 器， 在 这 个 不 可 视 的 窗 口 中 声 明 这 样 一 些 实 例 变 量：
// 缓 冲 区 大 小
PROTECTED integer ii_cache_size
// 缓 冲 区
PROTECTED u_dsa ids_cache[]
// 我 们 用 以 检 索 结 果 集 的 事 务 对 象
PROTECTED transaction i_trans
// 更 新 结 果 集 的 时 间（ 设 为30 秒）
PROTECTED long il_refresh_interval = 1800
// 更 新 的 起 始 时 刻
PROTECTED time itm__last_refresh

使 用dddw 的 窗 口 将 通 过fw_share_dddw() 函 数 来 访 问w_resultset_server 窗 口， 这 个 函 数 将 接 受 一 个 数 据 窗 口 和 一 个 列 名 作 为 参 数。

//public function integer fw_share_dddw
// 参 数： DataWindow  adw_parent
//		string  as_column_name  表 现 形 式 为dddw 列 名
integer  li_cache_idx
DataWindowchild dwc
string   ls_dataobject_name
//dddw 所 使 用dataobject 的 名 称
ls_dataobject_name = adw_parent.Describe( as_column_name + “.dddw.name” )
IF ls_dataobject_name = “”THEN RETURN 0
// 该dataobject 是 否 存 在 缓 冲 区 中
li_cache_idx = fw_find_dataobject (ls_dataobject_name)
IF li_cache_idx < 1 THEN RETURN 0
// 同 子 数 据 窗 口 共 享 数 据
adw_parent.GetChild ( as_column_name, dwc )
RETURN ids_cache[li_cache_idx].ShareData (dwc)

在 这 个 函 数 中， 为 确 定 下 拉 式 数 据 窗 口 所 使 用 的 数 据 窗 口 的 名 称， 首 先 使 用 了Describe() 函 数 来 获 得 该 列 的dddw.name 属 性。 如 果 该 列 名 没 有 找 到， 或 该 列 的 表 现 形 式 不 是 下 拉 式 数 据 窗 口， 这 个 函 数 的 返 回 值 为“”； 接 下 来 调 用fw_find_dataobject() 函 数， 以 确 定 这 个 数 据 结 果 集 是 否 在 缓 冲 区 中， 如 不 存 在， 就 新 创 建 一 个； 最 后 使 用ShareDate() 函 数 将 缓 冲 区 中 结 果 集 同 下 拉 式 数 据 窗 口 共 享 数 据。

这 个 函 数 中 调 用 的fw_find_dataobject() 函 数 如 下， 它 接 受 一 个as_dataobject 参 数， 并 使 用DO...WHILE 循 环 在 缓 冲 区 中 查 找 该 对 象 的 结 果 集， 如 找 到 将lb_found 设 为True， 否 则 设 为False。

//protected function integer fw_find_dataobject
// 参 数： string  as_dataobject
integer   li_cache_idx = 1
boolean  lb_found = FALSE
u_dsa    lds_new_entry
// 定 位dataobject 所 在 的 缓 冲 区
DO WHILE (NOT lb_found) and (li_cache_idx <= ii_cache_size)
IF ids_cache[li_cache_idx].dataobject = as_dataobject THEN
lb_found = TRUE
ELSE
li_cache_idx ++
END IF
LOOP
// 如 果 该dataobject 不 存 在 与 缓 冲 区 中， 则 创 建 一 个 新 入 口
IF NOT lb_found THEN
IF OpenUserObject (lds_new_entry) < 1 THEN
RETURN 0
END IF
lds_new_entry.dataobject = as_dataobject
IF lds_new_entry.SetTransObject (i_trans) < 1 THEN
RETURN 0
END IF
IF lds_new_entry.Retrieve () < 0 THEN
RETURN 0
END IF
ii_cache_size ++
ids_cache[ii_cache_size] = lds_new_entry
li_cache_idx = ii_cache_size
END IF
RETURN li_cache_idx

如 果 该 数 据 对 象 不 存 在 于 缓 冲 区 中， 我 们 将 使 用OpenUserObject() 函 数 新 创 建 一 个 用 户 对 象， 并 将 这 个DataStore 的DataObject 属 性 赋 值 为as_dataobject， 然 后 调 用SetTransObject() 函 数 和Retrieve() 函 数 查 询 结 果。

在 这 个 隐 含 窗 口 的Open 事 件 中 键 入 下 列 代 码：

i_trans = sqlca
itm_last_refresh = Now ()
// 设 置 起 始 时 间
Timer ( 60 )

在 窗 口 函 数fw_find_dataobject() 中 调 用SetTransObject() 时 使 用 的 事 务 变 量 是i_trans， 这 样 做 的 目 的 是 为 了 软 件 更 为 通 用。 我 们 在Open 事 件 中 将 这 个 实 例 变 量 赋 值 为SQLCA， 您 也 可 以 根 据 需 要 给 予 不 同 的 赋 值。 我 们 使 用 了 一 个 实 例 变 量itm_last_refresh 作 为 计 时 器， 并 且 设 定 每 一 分 钟 中 断 一 次， 以 使 窗 口 根 据 不 同 的 需 要 更 新 缓 冲 区 中 的 数 据。

下 面 的 一 个 窗 口 函 数fw_refresh_all 用 以 更 新 缓 冲 区 数 据：

//public  subroutine  fw_refresh_all ()
integer  li_cache_idx
// 更 新 所 以 缓 冲 区
FOR li_cache_idx = 1 TO ii_cache_size
ids_cache[li_cache_idx].Retrieve ()
NEXT
// 重 新 设 置 更 新 缓 冲 区 的 时 间
i_tm_last_refresh = Now ()

我 们 在w_resultset_server 的Open 事 件 中 对 一 个 计 时 器 进 行 了 初 始 化， 同 时 还 初 始 化 了 一 个 计 时 变 量itm_last_refresh， 我 们 剩 下 的 工 作 就 是 在Timer 事 件 中， 编 写 代 码 根 据 设 定 的 时 间 来 调 用fw_refresh_all()。

time  ltm_current_time
integer  li_cache_idx
// 当 前 时 间
current_time = Now ()
// 考 虑 过 午 夜 的 特 殊 情 况
IF ltm_current_time < itm_last_refresh THEN
fw_refresh_all ()
// 检 测 已 过 的 时 间 间 隔
ELSEIF RelativeTime ( itm_last_refresh, ii_refresh_interval )
<= ltm_current_time THEN
fw_refresh_all ()
END IF

一 般 在 一 个 大 型 应 用 中， 这 个 对 象 中 往 往 要 有 几 十 个 结 果 集。 您 可 能 并 不 希 望 系 统 同 时 更 新 所 有 的 数 据， 您 可 以 修 改Timer 事 件 中 的 代 码， 使 系 统 根 据 一 定 的 算 法， 轮 流 更 新 缓 冲 区 中 的 数 据， 而 不 使 用 户 感 觉 到 延 迟。 值 得 指 出 的 是， 在 一 般 的 数 据 窗 口 中 的dddw， 除 了 在 创 建 时 系 统 会 自 动 对 该 数 据 窗 口 中 的 所 有dddw 进 行Retrieve() 以 外， 在 运 行 过 程 中 如 果 没 有 程 序 明 确 指 明 某 个dddw 执 行Retrieve() 函 数， 系 统 是 不 会 自 动 更 新 其 数 据 的。 因 此 采 用 本 文 推 荐 的 方 法 还 可 以 保 持 下 拉 式 数 据 窗 口 中 的 数 据 接 近 后 台 服 务 器 中 的 最 新 数 据。 如 果 您 在 应 用 中 必 须 使 下 拉 式 数 据 窗 口 中 的 数 据 为 后 台 的 最 新 数 据， 也 可 以 调 用wf_refresh_resultset() 强 制 缓 冲 区 更 新 数 据， 使 用 的 参 数 有 两 个， 数 据 窗 口 名 称 和 列 名：

//public  function  boolean  fw_refresh_resultset()
// 参 数： DataWindow  adw_parent
//		string  as_column_name  数 据 窗 口 中 为dddw 的 列 名
integer  li_cache_idx
string   ls_dataobject_name
//dddw 所 使 用dataobject 的 名 称
ls_dataobject_name = adw_parent.Describe( as_column_name + “.dddw.name” )
IF ls_dataobject_name = “”THEN RETURN False
// 该dataobject 是 否 存 在 缓 冲 区 中
li_cache_idx = fw_find_dataobject (ls_dataobject_name)
IF li_cache_idx < 1 THEN RETURN False
// 更 新 缓 冲 区 内 的 数 据
ids_cache[li_cache_idx].Retrieve()
RETURN True

在 这 段 代 码 中， 我 们 首 先 使 用fw_find_dataobject 函 数 找 到 要 更 新 数 据 的dddw 所 使 用 的 缓 冲 区， 然 后 更 新 该 缓 冲 区 的 数 据 即 可。


张健姿-->PowerBuilder系列讲座-->33. 为下拉式数据窗口建立缓冲区

33. 为下拉式数据窗口建立缓冲区

同 其 他4GL 工 具 如Delphi，Visual Basic 的 早 期 版 本 一 样，PowerBuilder4.0 以 前 所 生 成 的 可 执 行 文 件 使 用 是 伪 代 码， 也 称P-Code（pseudo-code）。P-Code 在 运 行 时 是 被 解 释 执 行 的， 更 准 确 地 说，P-Code 使 用 的 是 虚 拟 机 指 令， 为 不 同 的 宿 主 机 提 供 共 享 的 操 作 命 令。 在 运 行 时， 操 作 系 统 根 据 不 同 平 台 解 释 实 现P-Code 指 令。 一 条P-Code 指 令 可 以 替 代 几 条 机 器 代 码 指 令， 这 样 就 减 少 了 执 行 代 码 的 大 小， 不 过 却 以 应 用 的 执 行 效 率 为 代 价。 一 般 的，P-Code 的 执 行 速 度 要 慢 于 编 译 代 码， 但 文 件 的 大 小 却 小 于 编 译 代 码。

PowerBuilder 5.0 引 入 了 生 成 全 编 译 的 可 执 行 代 码 的 功 能， 以 提 高 运 行 效 率， 但 同 时 它 也 保 留 了 使 用P-Code 为 可 执 行 文 件 的 功 能。 使 用P-Code 的 应 用 程 序 包 括 执 行 文 件.EXE 和PowerBuilder 的 动 态 连 接 库.PBD， 而 编 译 代 码 所 产 生 的 执 行 文 件 为.EXE 文 件 和 动 态 连 接 库DLL。

什 么 是 编 译 代 码

编 译 代 码 首 先 以P-Code 为 基 础，PowerBuilder 首 先 根 据P-Code 创 建C 代 码， 然 后 将C 代 码 编 译 成 为 机 器 代 码 的 执 行 文 件。 编 译 后 的 执 行 程 序 中， 所 有 指 令 都 是 实 际 的 机 器 代 码。 这 就 意 味 着 这 样 的 执 行 文 件 的 大 小 将 远 大 于P-Code。

使 用 编 译 代 码 仍 需 要PowerBuilder 的 运 行 库，PowerBuilder 在 执 行 机 器 代 码 时， 仍 使 用 共 享 的 函 数 库， 这 样 做 的 目 的 主 要 是 为 了 节 省 内 存。 使 用 动 态 连 接 库 可 以 避 免 将 所 有 的 功 能 函 数 都 写 在 一 个 可 执 行 文 件 中， 以 减 小 可 执 行 文 件 的 体 积。 而 在Windows 环 境 中， 内 存 中 留 有 一 份DLL 的 拷 贝 可 以 为 多 个 应 用 程 序 使 用。 此 外PowerBuilder 5.0 还 压 缩 了 一 些DLL 中 函 数 的 大 小， 现 在32 位 平 台 上 运 行 时 只 需7 个 动 态 连 接 库， 解 压 缩 后 为4.5M。 应 用 所 需 要 的 其 它DLL 如 用 于 数 据 库 访 问、 分 布 式PowerBuilder， 及RTF 编 辑 器 等 可 在 用 到 时 动 态 地 装 入。

编 译 代 码 创 建

编 译 代 码 的 创 建 有 以 下 几 个 步 骤：
在 开 发 的 过 程 中，PowerScript 首 先 被 翻 译 成 了P-Code， 当 您 在Script 编 辑 器 中 编 写 完 一 个 事 件 或 函 数 的 代 码 返 回 窗 口 时， 系 统 就 会 自 动 地 翻 译 这 一 段Script 代 码。 当 您 在 工 程 画 笔 中 创 建P-Code 可 执 行 文 件 时，PowerBuilder 将 为 不 同 的PBL 中 不 同 对 象 之 间 的 引 用 建 立 起 连 接， 并 将 这 些 对 象 拷 贝 到 指 定 的EXE 或PBD 文 件 中。 这 是 一 个 相 对 较 快 的 过 程。

接 下 来P-Code 将 被 翻 译 成 为C 语 言 的 语 句。 在 这 一 过 程 中， 工 程 画 笔 窗 口 下 面 的 微 帮 助 中 将 显 示"compiling ... " 的 字 样， 这 一 过 程 约 占 全 部 编 译 时 间 的10 ％。 然 后 是 将C 代 码 编 译 和 链 接 成 为 机 器 代 码， 形 成 可 执 行 文 件 和 动 态 连 接 库， 在 这 一 步 骤 中， 微 帮 助 显 示 的 是"generating..."， 这 一 编 译 过 程 最 费 时 间， 将 占 约90 ％ 的 编 译 时 间。

通 过 上 面 的 介 绍， 我 们 看 到 创 建 机 器 代 码 所 用 的 时 间 比 产 生P-Code 的 用 时 要 多 得 多， 因 为 后 者 只 是 一 个 二 进 制 代 码 搬 运 的 过 程。 因 此 在 开 发 过 程 中， 我 们 一 般 使 用 的 主 要 是P-Code， 只 是 在 最 终 交 付 用 户 使 用 时， 才 根 据 实 际 情 况 选 择 是 否 创 建 机 器 代 码。

编 译 代 码 的 优 势：
在 以 下 这 几 个 方 面， 将PowerScript 代 码 编 译 成 为 机 器 代 码 将 得 到 较 快 的 运 行 速 度：

  • 循 环 结 构
  • 浮 点 运 算
  • 整 数 运 算
  • 函 数 调 用

但 是 在 另 外 的 一 些 方 面， 编 译 成 为 机 器 代 码 并 不 能 使 运 行 速 度 明 显 加 快， 如 涉 及 图 形 用 户 界 面， 数 据 库 访 问， 小 数 运 算， 字 符 串 操 作 等。 在 有 些 情 况 下， 两 种 代 码 的 速 度 是 相 同 的， 如 对 资 源 的 调 用 和 对 数 据 窗 口 的 执 行。 在 这 些 情 况 下， 您 就 无 法 通 过 使 用 编 译 代 码 来 提 高 运 行 的 速 度 了。

如 果 您 的 应 用 是 以 执 行 自 身 的 代 码 为 主， 例 如 应 用 中 较 多 的 是 同 一 条 代 码 被 反 复 执 行（ 如 循 环） 或 是 编 写 的 代 码 主 要 用 来 进 行 数 学 运 算， 这 样 的 应 用 应 当 使 用 编 译 代 码。 在 函 数 调 用 方 面， 使 用 机 器 码 也 要 比P-Code 快 得 多。 因 此 如 果 您 的Script 大 量 的 调 用 函 数 或 是 采 用 新 的 语 法 形 式 调 用 事 件， 使 用 机 器 码 也 将 是 个 好 的 选 择。 不 过 您 应 当 记 住， 编 译 代 码 的 执 行 文 件 大 小 要 比P-Code 大 得 多， 因 为 一 条PowerScript 代 码 将 有 可 能 编 译 成 为5 条 甚 至10 条C 代 码。 当 这 些C 代 码 被 编 译 后， 可 执 行 的 代 码 将 远 远 大 于 相 应 的P-Code， 因 此 如 果 您 的 应 用 程 序 的 文 件 大 小 是 您 应 用 的 首 要 考 虑 因 素， 或 者 您 的 应 用 较 多 的 是 数 据 库 访 问， 那 您 应 当 选 择 使 用P-Code。

提 高 应 用 的 性 能

事 实 上， 一 个 好 的 数 据 库 的 物 理 设 计 和 应 用 程 序 的 模 块 设 计 可 以 大 大 地 提 高 应 用 的 性 能。 如 果 应 用 设 计 不 好， 即 使 使 用 了 编 译 代 码， 也 不 会 得 到 很 好 的 系 统 性 能。 此 外 调 整 系 统 参 数 设 置 和 合 理 的 物 理 带 宽， 可 以 使PowerBuilder 5.0 的 应 用 运 行 得 十 分 快 捷。

在PowerBuilder 5.0 中， 系 统 内 部 已 经 在 一 些 功 能 上 进 行 了 增 强， 对 这 些 功 能 加 以 利 用， 本 身 就 可 以 实 现 性 能 的 优 化。

  • 使 用PowerBuilder 5.0 的Tab 控 件 时， 如 果 在TabPage 的 属 性 中 选 中"create on demand" 这 个 复 选 框， 在 运 行 时， 只 有 当 用 户 选 择 这 个TabPage，PowerBuilder 才 去 初 始 化 该TabPage 对 象 的 实 例。
  • 使 用DataStore 而 不 要 使 用 不 可 视 的DataWindow 作 为 数 据 的 缓 冲 区。
  • 传 递string、datetime 等 数 据 类 型 的 参 数 尽 可 能 使 用read-only 方 式， 因 为 同 采 用reference 方 式 相 比，PowerBuilder 无 需 为 这 些 参 数 再 于 内 存 中 复 制 一 份 拷 贝。 同 理，reference 方 式 要 比value 法 传 参 更 快 一 些。
  • 使 用 局 部 变 量 要 比 全 局 和 共 享 变 量 要 快 一 些， 因 为 局 部 变 量 存 在 栈 中， 而 全 局 变 量 则 存 储 于 远 堆。 这 个 速 度 的 差 异 在 使 用 机 器 代 码 时 比P-Code 更 为 明 显。
  • 使 用 数 组 对DataWindow 的 数 据 进 行 赋 值， 而 不 要 使 用SetItem( ) 和GetItem( ) 系 列 的 函 数， 因 为 前 者 只 使 用 了 一 次 函 数， 就 调 用 了 所 有 的 行 和 列。
  • 为 加 快 编 译 速 度， 不 要 在 一 个 事 件 或 函 数 中 编 写 过 长 的 代 码， 将 它 们 分 成 几 个 函 数 或 其 他 可 重 用 的 代 码 对 象。
  • 调 整 您 的 数 据 库。 一 般 来 说， 数 据 库 访 问 在 客 户/ 服 务 器 结 构 的 应 用 中 要 占 用 大 量 的 时 间， 尽 可 能 利 用PowerBuilder 的 特 性 来 减 少 数 据 库 访 问 的 时 间， 包 括：
    1. 在5.0 中DBParm 缺 省 设 置StaticBind = 1， 也 就 是 说 在 检 索 数 据 之 前，PowerBuilder 并 不 首 先 获 取 查 询 数 据 结 果 集 结 构 的 描 述， 这 样 作 可 以 提 高 数 据 的 检 索 速 度， 但 是 如 果 数 据 窗 口 同 数 据 库 的 结 果 集 不 匹 配 时， 将 发 生 错 误。 因 此 如 果 您 需 要 动 态 改 变 数 据 窗 口 所 查 询 的 表， 而 且 这 些 表 结 构 不 同 时， 您 应 当 将StaticBind 的 值 设 置 为0。
    2. DBParm 缺 省 设 置 允 许DataWindow 放 置BLOB 字 段， 使 用Data Pipeline 可 以 对 数 据 库 插 入BLOB 字 段 的 数 据。
    3. 通 过DBParm 的 设 置 可 以 使 数 据 库 支 持 游 标。 设 置DBParm SQLCache = n 将 告 诉PowerBuilder 将 为 多 少 个 使 用 不 同SELECT 语 句 的 游 标 建 立 缓 冲。 如 果 您 多 次 重 复 使 用 同 一 个SELECT 语 句， 当 缓 冲 区 内 有 数 据 时， 检 索 的 速 度 将 大 大 加 快。SQLCache 的 缺 省 值 为0。
    4. PowerBuilder 5.0 中 对 数 据 窗 口 有 了 这 样 的 一 个 选 项， 可 以 将 数 据 窗 口 的 结 果 集 保 存 在 本 地 磁 盘 上。 这 样 作 可 以 减 少 内 存 的 使 用， 使 您 创 建 更 大 的 报 表， 不 过 存 取 速 度 比 将 结 果 集 放 在 内 存 中 要 慢 得 多。 如 果 您 的 数 据 窗 口 将 显 示 的 不 是 一 个 很 大 的 报 表， 一 般 还 是 将 结 果 集 放 在 内 存 中 为 佳。

其 它 的 注 意 事 项

对 于 用 户 使 用PowerBuilder 编 译 代 码， 还 有 以 下 几 点 值 得 说 明：

  • PowerBuilder 产 生 编 译 代 码 的 速 度 很 慢， 应 尽 可 能 使 用 主 频 较 快 的 计 算 机， 最 多 至64M 内 存， 不 过 在5.0 的 版 本 中， 多CPU 对 提 高 编 译 速 度 没 有 任 何 帮 助。
  • PowerBuilder 所 使 用 的Watcom 的 技 术 主 要 是32 位 的 技 术， 尽 管 在32 位 平 台 上 有 生 成16 位 代 码 的 选 项， 但 是 速 度 却 并 不 见 佳。 另 外 在16 位 平 台 上， 我 们 不 能 产 生32 位 的 应 用 程 序。
  • 编 译16 位 应 用 程 序 有 一 个 限 制 就 是 代 码 和 数 据 都 不 能 超 过64K， 如 果 函 数 或 事 件 中 的 代 码 过 长，PowerBuilder 在 创 建EXE 时 将 产 生 错 误。 我 们 推 荐 的 解 决 办 法 就 是 将 长 代 码 分 解 成 多 个 小 函 数， 这 样 做 也 可 以 提 高 运 行 效 率 并 有 利 于 您 对 应 用 代 码 的 维 护。
  • 如 果 您 在Windows NT 或Windows 95 上 运 行16 位 应 用 程 序， 您 所 使 用 的 运 行 库 也 应 当 是16 位 的。
  • 在 一 个PowerBuilder 应 用 中 我 们 只 能 使 用P-Code 的PBD 或 编 译 代 码DLL 中 的 一 种， 而 不 能 将 其 混 合 使 用， 不 过 唯 一 的 例 外 是 使 用PowerBuilder 创 建OLE 服 务 器。
  • 如 果 您 只 是 想 改 变 一 个DLL， 而 并 不 想 重 新 编 译 全 部 的 应 用， 您 可 以 在Library 画 笔 中 选 择Library|Build Runtime Library... 的 菜 单 项。
  • 在 编 译 时， 系 统 将 首 先 剔 除 注 释 语 句， 所 以Script 中 注 释 语 句 的 多 少 对 编 译 速 度 没 有 影 响。
  • 在 您 使 用PowerBuilder 的Plug-In 技 术 时， 您 不 能 使 用 编 译 代 码。


张健姿-->PowerBuilder系列讲座-->34. 分布式事务处理

34. 分布式事务处理

美 国Sybase 公 司 于 今 年 七 月 发 布 了PowerBuilder 6.0 的Beta 版， 正 式 的 版 本 也 将 于 不 久 的 将 来 推 出， 其 中 对 分 布 式 事 务 处 理 的 支 持 是 新 版 本 中 增 强 得 最 多 的 功 能。 早 在1995 年，PowerSoft 公 司 就 提 出 了 在“ 分 布 式 事 务” 方 面 的 发 展 战 略， 并 在1996 年 发 布 的PowerBuilder 5.0 中 支 持 了 分 布 式 事 务 处 理 的 功 能， 笔 者 在 以 前 的 讲 座 中 也 曾 经 讨 论 过 有 关 如 何 创 建 分 布 式PowerBuilder 应 用 的 问 题， 但 是 得 到 读 者 反 馈 意 见 却 很 少。 以 往， 一 个 使 用PowerBuilder 开 发 客 户/ 服 务 器 应 用 软 件 的 程 序 员 是 较 少 会 想 到 使 用 分 布 式 事 务 来 分 割 应 用 的， 因 此 一 般 的 用 户 对 分 布 式 事 务 的 概 念 和 意 义 也 不 甚 了 解， 这 里 我 们 来 进 一 步 讨 论 一 下 分 布 式 事 务 处 理 及 其 应 用。

三 级 体 系 结 构

首 先 我 们 介 绍 三 级 体 系 结 构 这 一 概 念。 所 谓 级 是 指 一 种 功 能 划 分， 我 们 以 往 所 开 发 的 数 据 库 应 用 软 件 一 般 是 基 于 客 户/ 服 务 器 结 构 的， 我 们 称 之 为 两 级 体 系 结 构。 也 就 是 说 整 个 系 统 可 以 分 成 两 个 功 能 块， 第 一 级 包 括 了 软 件 的 应 用 层 和 表 现 层， 驻 留 于 客 户 端。 我 们 使 用PowerBuilder 开 发 出 的 应 用 主 要 用 于 第 一 级， 运 行 于 客 户 端。 第 二 级 包 含 数 据 库 和 服 务 器 的 组 件。 一 个 基 于SQL 的 数 据 库 管 理 系 统 一 般 安 装 在 服 务 器 端， 应 用 软 件 在 服 务 器 端 进 行 的 操 作 主 要 是 数 据 存 储 和 检 索。 在 两 级 模 式 中 会 有 一 些 应 用 逻 辑 以 存 储 过 程 和 触 发 器 的 形 式 存 储 在 服 务 器 端， 以 优 化 服 务 器 的 性 能， 但 绝 大 多 数 的 应 用 逻 辑 是 放 在 客 户 端 的。

三 级 模 式 是 将 系 统 分 为 有 三 个 不 同 的“ 级”： 表 现 级， 商 业 逻 辑 级 和 数 据 访 问 级。 表 现 级 是 处 理 用 户 界 面 的 功 能； 数 据 访 问 级 是 数 据 源， 在 通 常 状 况 下 指 数 据 库； 商 业 逻 辑 级 是 新 增 加 的 一 级， 指 程 序 中 作 出 智 能 决 策 的 那 一 部 分 功 能。 在 早 期 的 应 用 中， 这 一 部 分 的 功 能 并 不 十 分 复 杂， 一 般 将 其 放 在 表 现 级 即 可， 另 有 少 量 以 存 储 过 程 或 触 发 器 的 形 式 放 在 数 据 访 问 一 级， 而 随 着 软 件 工 程 的 发 展， 软 件 的 日 益 复 杂， 软 件 中 功 能 增 加 最 多 的 就 是 在 这 一 级。 一 个MIS 系 统 的 功 能 由 早 先 的 对 某 一 个 表 的 简 单 查 询， 发 展 到 涉 及 多 个 表 的 分 类 统 计 求 和， 根 据 复 杂 的 公 式 分 析 计 算， 进 行 决 策 支 持 等， 如 将 这 些 增 强 的 功 能 仍 全 部 放 置 在 表 现 级， 会 使 得 客 户 机 越 来 越 不 堪 重 负， 因 此 就 有 人 提 出 在 系 统 中 将 商 业 逻 辑 分 离 出 来， 单 独 形 成 了 一 级， 这 就 形 成 了 三 级 结 构。 随 着 三 级 结 构 的 进 一 步 发 展， 一 般 总 是 把 运 行 在 商 业 逻 辑 级 的 软 件 编 写 成 为 了 一 个 为 客 户 机 所 调 用， 能 够 完 成 一 定 的 逻 辑 功 能 的 专 用 软 件， 同 数 据 库 服 务 器 相 区 别， 我 们 称 之 为 应 用 服 务 器。 在 一 个 网 络 中， 可 以 有 着 多 个 不 同 功 能 的 应 用 服 务 器， 为 客 户 机 或 其 它 的 应 用 服 务 器 提 供 专 业 服 务， 这 样， 三 级 结 构 就 发 展 成 为 了N 级， 这 就 是 所 谓 的 分 布 式 计 算 方 式。

分 布 式 计 算 的 优 势：
采 用 分 布 式 计 算 有 着 多 方 面 的 技 术 优 势， 包 括：

逻 辑 封 装 性： 这 是 分 布 式 模 式 中 最 具 诱 惑 力 的 特 征， 这 种 模 式 的 根 基 在 于 将 以 往 全 部 由 客 户 机 完 成 的 事 务 逻 辑 中 的 一 部 分 从 客 户 端 分 开。 当 使 公 司 需 要 动 态 改 变 一 个 应 用 软 件 的 商 业 逻 辑 规 则 时， 只 要 改 变 一 个 应 用 服 务 器 的 程 序 即 可， 而 不 需 要 更 改 客 户 端 用 户 界 面， 这 样 就 无 需 中 断 用 户， 为 最 终 用 户 重 新 发 放 新 的 界 面 软 件 或 亲 自 上 门 为 其 安 装 调 试 并 重 新 培 训 用 户， 提 高 了 工 作 效 率。 这 种 多 级 模 式 对 于 需 经 常、 快 速 改 变 应 用 程 序 的 行 业 很 有 帮 助。 瘦 客 户 机： 这 种 类 型 的 应 用 在 运 行 时 最 显 著 的 特 点 就 是 减 少 甚 至 消 除 了 传 统 的 两 级 体 系 结 构 中， 以 客 户 机 为 中 心 或 称 为“ 肥 客 户” 的 模 式， 减 轻 了 客 户 机 的 功 能 负 担， 使 其 消 肿 成 为 了“ 瘦 客 户”。“ 肥 客 户” 是 用 户 感 到 十 分 苦 恼 的 事 情， 用 户 为 使 用 更 强 功 能 的 软 件， 就 必 须 付 出 高 昂 的 维 护 费 用， 不 断 地 为 个 人 电 脑 的 软 硬 件 设 备 升 级。 近 日 流 行 的NC 也 正 是 看 到 一 般 用 户 在 维 护PC 机 运 行 时 负 担 过 重， 而 提 出 通 过 网 络 将 一 部 分 的 任 务 交 给 了 服 务 器 完 成。 这 两 种 方 法 有 着 相 通 之 处。

性 能： 性 能 的 提 高 是 三 级 模 式 最 终 被 用 户 采 用 的 主 要 原 因。 将 复 杂 的 应 用 和 商 业 逻 辑 分 离 出 来 由 专 门 的 一 台 应 用 服 务 器 来 处 理， 既 可 以 提 高 应 用 的 执 行 速 度， 也 可 以 减 少 网 络 调 用 的 通 讯 量。 不 过 这 种 性 能 提 高 是 有 一 定 代 价 的。 这 就 是 开 发 时 要 将 应 用 逻 辑 分 割 为 客 户 端 逻 辑 和 服 务 器 端 逻 辑， 这 就 增 加 了 设 计 的 复 杂 性。

安 全 性 管 理： 在 分 布 式 计 算 模 式 中， 由 于 所 有 的 商 业 逻 辑 都 驻 留 在 服 务 器 端， 信 息 管 理 部 门 就 可 以 十 分 方 便 地 监 控 服 务 器 的 运 行 情 况， 很 容 易 地 控 制 访 问 服 务 器 以 及 与 服 务 器 应 用 打 交 道 人 员 的 数 量。 这 可 以 大 大 简 化 管 理 员 对 系 统 的 管 理， 减 轻 系 统 维 护 的 工 作 量， 并 确 保 系 统 的 可 靠 运 行。

分 布 式 事 务 处 理 最 广 泛 和 最 成 功 的 应 用 当 数Internet/Intranet 技 术 了， 尽 管 许 多 人 并 不 意 识 到Internet 就 是 一 个 三 级 体 系 结 构 的 代 表。 随 着Internet/Intranet 使 用 的 不 断 深 化， 用 户 对Web 服 务 器 所 查 询 的 信 息 就 不 只 局 限 于 以 文 件 方 式 存 放 在 服 务 器 端 的 静 态 的 超 文 本 文 件 了。 这 时 我 们 需 要 借 助 关 系 型 数 据 库 来 存 放 变 化 的 数 据， 并 在Web 服 务 器 与 数 据 库 服 务 器 之 间 以JDBC、CGI 等 协 议 建 立 起 两 者 的 连 接， 使Web 服 务 器 能 够 实 现 对 数 据 库 的 即 席 查 询， 并 将 结 果 返 回 浏 览 器。 我 们 可 以 发 现， 在 这 样 一 个Intranet 结 构 中， 浏 览 器--Web 服 务 器-- 数 据 库 服 务 器 就 分 别 对 应 于 客 户 端-- 应 用 服 务 器-- 数 据 库 服 务 器 三 级 体 系 结 构 中 的 三 个 环 节，Intranet 就 是 一 个 典 型 的 三 级 体 系 结 构 的 应 用。 因 此， 我 们 可 以 认 为，Internet 模 式 的 技 术 优 势 如 用 户 界 面 简 单， 管 理 人 员 易 于 维 护 等 也 正 是 多 级 计 算 方 式 的 优 势 所 在。 此 外， 由 于PowerBuilder 支 持 分 布 式 事 务 处 理， 这 就 使 得PowerBuilder 很 容 易 地 支 持 了 作 为 分 布 式 事 务 的 特 例---Internet 的 开 发 了。

分 布 式 应 用 的 开 发

尽 管 分 布 式 计 算 是 一 个 较 新 的 概 念， 但 是 使 用PowerBuilder 开 发 分 布 式 应 用 却 十 分 简 单， 只 要 您 会 使 用PowerBuilder 中 的 用 户 自 定 义 的 不 可 视 对 象， 您 就 会 很 快 地 掌 握 分 布 式PowerBuilder 的 开 发。 在PowerBuilder 中， 应 用 服 务 器 一 端 的 功 能 都 是 通 过 不 可 视 用 户 对 象 实 现 的， 开 发 人 员 可 以 将PowerBuilder 的 自 定 义 用 户 的 对 象 放 于 应 用 服 务 器 一 侧， 被 称 作 远 端 对 象， 在 客 户 端 放 置 该 用 户 对 象 的 代 理 对 象。 此 外， 在 服 务 器 一 侧 有 一 个 新 的transport 对 象 用 于 监 听 任 何 一 个 用 户 或 其 它 应 用 服 务 器 的 请 求， 在 客 户 端 有 一 个connection 对 象 用 以 建 立 同 远 端 对 象 的 连 接。 客 户 端 的 应 用 程 序 通 过connection 对 象 连 接 应 用 服 务 器， 连 接 建 立 后， 客 户 端 的 应 用 就 可 以 象 调 用 本 地 对 象 一 样 调 用 应 用 服 务 器 上 的 对 象 函 数 了。 当 然， 我 们 这 里 指 的 用 户 可 以 是 一 般 意 义 上 的 客 户， 也 可 以 指 应 用 服 务 器 以 一 个 客 户 的 身 份 调 用 其 它 应 用 服 务 器。

远 端 的 用 户 对 象 中 的 商 业 逻 辑 可 以 是 采 用PowerScript 编 写 的， 作 为 一 个 不 可 视 用 户 对 象 的 特 例， 这 个 对 象 可 以 调 用 任 何 函 数 或 使 用 数 据 库 命 令， 它 也 可 以 使 用 不 可 视 的DataStore 来 封 装 数 据 库 访 问，PowerBuilder 5.0 可 以 支 持 除 可 视 对 象 以 外 的 所 有 的 数 据 类 型。 在 客 户 端 使 用 这 种 形 式 调 用 就 象 在 使 用 存 储 过 程 一 样 将 参 数 通 过 网 络 传 递 给 服 务 器， 服 务 器 的 运 行 结 果 返 回 客 户 端。


张健姿-->PowerBuilder系列讲座-->35. 分布式应用开发

35. 分布式应用开发

在 第8 期 中， 我 们 曾 经 介 绍 了 开 发 一 个 简 单 分 布 式 应 用 的 步 骤， 这 里 我 们 不 再 赘 述 开 发 的 全 过 程， 只 是 就 其 中 的 主 要 内 容 再 重 点 说 明 一 下。

不 可 视 对 象（NVO） 及 其 代 理 对 象（proxy）

不 可 视 对 象： 不 可 视 对 象 最 早 在3.0 中 引 入， 功 能 在 以 后 的 每 一 个 版 本 中 都 得 到 一 定 的 加 强。 简 单 的 说， 不 可 视 对 象 是 一 个 进 程 对 象。 在 这 个 对 象 中， 没 有 任 何 可 视 的 成 份， 因 此 而 得 名。 不 可 视 对 象 可 以 是 一 个 事 务 处 理 的 对 象。 由 一 些 事 务 处 理 函 数 组 成， 并 有 一 个 方 法 集 作 为 外 界 访 问 接 口。 用 户 通 过 这 个 由 函 数 和 事 件 组 成 的 方 法 集 调 用 事 务 处 理 函 数。 在 分 布 式PowerBuilder 中， 我 们 将 这 样 的 不 可 视 对 象 放 在 了 远 端。 使 事 务 处 理 的 函 数 在 应 用 服 务 器 中 或 者 说 在 另 外 的 一 台 计 算 机 中 运 行。

代 理 对 象： 我 们 在 不 可 视 对 象 的 外 部 增 加 了 一 层 包 装， 把 这 一 层 作 为 编 程 的 接 口 层（API）。 这 就 是 该 不 可 视 对 象 的 代 理 对 象(proxy)， 代 理 对 象 作 为 不 可 视 对 象 的 最 外 层 包 装， 有 与 该 对 象 相 同 的public 访 问 特 性 的 方 法 和 属 性。 将 实 际 的 对 象 与 代 理 对 象 分 离 开， 这 就 形 成 了 分 布 式 的 结 构。 不 可 视 对 象 所 进 行 的 事 务 处 理 放 在 了 应 用 服 务 器 上， 而 它 的 代 理 对 象 放 在 了 客 户 端。

在 客 户 端， 用 户 对 代 理 对 象 进 行 操 作， 但 这 只 是 不 可 视 对 象 的 一 个API 接 口， 它 并 不 包 含 真 正 的 方 法。 代 理 在 内 部 重 新 调 用 运 行 在 远 端 的 对 象 的 方 法。 在 服 务 器 一 端， 运 行 着 的 是 一 个 真 正 的 不 可 视 对 象， 它 同 普 通 的 不 可 视 对 象 没 有 任 何 不 同 之 处， 只 是 对 这 个 不 可 视 对 象 的 调 用 是 在 客 户 端 通 过 它 的 代 理 进 行 的。 每 一 个 运 行 在 服 务 器 端 的 对 象 在 客 户 端 都 必 须 有 它 的 代 理， 这 个 对 象 才 能 够 被 该 客 户 端 的 应 用 程 序 所 调 用。 一 个 应 用 服 务 器 可 能 会 有 多 个 客 户 机， 这 样 在 一 个 服 务 器 上 的 不 可 视 对 象 就 可 能 在 客 户 机 上 有 多 个 不 同 的 代 理。

连 接 对 象（connection） 及 传 输 对 象（transport）

介 绍 了 服 务 器 端 的 不 可 视 对 象 和 客 户 端 的 代 理 对 象 这 两 个 在 分 布 式 应 用 中 的 对 等 部 分， 接 下 来 我 们 要 考 虑 的 是 如 何 将 这 两 个 部 分 建 立 连 接。 在 连 接 的 过 程 中， 我 们 就 要 涉 及 另 两 个 对 等 的 对 象， 这 就 是 在PowerBuilder5.0 中 新 增 加 的 两 个 对 象 类 型: 连 接（connection） 及 传 输（transport） 对 象。connection 对 象 是 在 客 户 端 用 以 同 服 务 器 进 行 通 讯 的 对 象， 反 过 来，transport 对 象 是 驻 留 在 服 务 器 端 用 以 同 客 户 端 应 用 进 行 通 讯 的 对 象。

connection 对 象： 我 们 可 以 将 这 一 对 象 同 用 在 数 据 库 事 务 的transaction 对 象 进 行 类 比。 我 们 通 过 定 义transaction 对 象 的 属 性 连 接 某 个 数 据 库 服 务 器。 在 客 户 端 的 应 用 中， 用 数 据 窗 口 或 直 接 使 用SQL 访 问 数 据 库 服 务 器 时， 我 们 须 使 用SetTransObject() 函 数 或USING 关 键 词 来 指 定 这 个 数 据 窗 口 所 使 用 的transaction 对 象。 同 理， 分 布 式 事 务 中， 客 户 端 的 代 理 对 象 就 如 同 一 个 数 据 窗 口， 我 们 也 应 当 指 定 代 理 对 象 同 哪 个 服 务 器 进 行 通 讯。 因 为 正 如 一 个 数 据 窗 口 可 以 访 问 不 同 的 数 据 库 服 务 器 一 样， 一 个 代 理 对 象 也 可 以 代 理 不 同 应 用 服 务 器 上 的 不 可 视 对 象， 而 其 中 的connection 对 象 就 象transaction 对 象 一 样， 它 的 属 性 就 是 用 以 区 别 不 同 的 服 务 器。 在connection 对 象 中 最 重 要 的 三 个 属 性 是application、 driver、 location。driver 属 性 是 用 以 指 定 我 们 所 使 用 的 通 讯 协 议， 目 前 支 持 的 是TCP/IP 和Named Pipes；location 属 性 是 应 用 服 务 器 的 名 称。

象 事 务 对 象 中 使 用CONNECT USING SQLCA 一 样， 接 下 来 我 们 调 用 一 个connection 的 对 象 函 数ConnectToServer()， 建 立 客 户 和 应 用 服 务 器 的 连 接。 也 正 如 事 务 对 象 一 样， 在 调 用 这 个 函 数 后， 我 们 应 检 验connection 的 返 回 值， 以 确 认 连 接 是 否 成 功。connection 对 象 同 服 务 器 建 立 连 接 后， 客 户 端 即 可 同 服 务 器 进 行 通 话。 下 一 步 我 们 指 定 代 理 对 象 使 用 哪 一 个 连 接 对 象。 这 就 好 比 是 在 数 据 窗 口 中 使 用SetTransObject 函 数 将 数 据 窗 口 和 事 务 对 象 建 立 联 系， 代 理 对 象 使 用 的 的 是SetConnect() 函 数。 一 个PowerBuilder 的 应 用 可 以 同 时 连 接 多 个 数 据 库 服 务 器， 同 样 在 分 布 式PowerBuilder 中， 您 也 可 以 同 时 建 立 多 个 连 接 对 象， 每 个 连 接 对 象 同 不 同 的 应 用 服 务 器 相 连， 使 一 个 客 户 端 应 用 可 以 同 时 访 问 多 个 应 用 服 务 器。

传 输 对 象： 在 客 户 端 我 们 使 用 的 是connection 对 象 同 服 务 器 进 行 联 系， 而 在 服 务 器 端 我 们 使 用 的 是transport 对 象 同 客 户 端 进 行 连 接。transport 对 象 用 以 指 示 本 应 用 服 务 器 的 一 些 属 性 值， 并 且 将 这 个 应 用 服 务 器 启 动 起 来。transport 也 有 着 两 个 重 要 的 属 性application 和driver。 同 样driver 是 指TCP/IP 或Named Pipe 协 议， 而application 是 指 正 在 监 听 的 应 用 名 称。 启 动 服 务 器 的 函 数 是listen()， 执 行 了 这 个 函 数 后， 在 服 务 器 端 就 增 加 了 一 个 进 程 用 于 监 听 任 何 客 户 端 的 请 求。

分 布 式PowerBuilder 的 工 作 原 理

当 一 个connection 对 象 调 用 了ConnectToServer 函 数 成 功 后， 服 务 器 端 将 在 内 存 中 开 辟 一 个 用 于 该 客 户 端 应 用 的 工 作 区， 而 在proxy 对 象 调 用 了SetConnect 函 数 后， 系 统 将 在 服 务 器 端 初 始 化 一 个 不 可 视 对 象 的 实 例， 注 意 系 统 并 不 是 在 用 户 初 始 化 代 理 对 象 时 初 始 化 不 可 视 用 户 对 象 的， 而 是 在 调 用SetConnect 函 数 后， 在 服 务 器 端 对NVO 进 行 初 始 化 的。

分 布 式PowerBuilder 在 服 务 器 一 端 实 现 的 是 一 个 虚 拟 机 模 型。 每 当 一 个 客 户 请 求 连 接 时， 服 务 器 就 会 在 内 存 中 建 立 一 个 新 的 区 域 用 于 该 客 户 的 请 求， 每 一 个 内 存 空 间 都 是 相 互 独 立。 在PowerBuilder 5.0 中 分 布 式 应 用 只 支 持 同 步 方 式， 也 就 是 说， 当 您 调 用 代 理 对 象 的 一 个 函 数 时， 系 统 实 际 将 调 用 服 务 器 端 的 不 可 视 对 象 的 函 数。 这 时 客 户 端 应 用 将 被 挂 起， 直 至 服 务 器 函 数 运 行 完 毕， 客 户 端 才 能 继 续 工 作。 而 在 即 将 发 布 的6.0 中 将 支 持 异 步 运 算。

当 您 调 用 一 个 客 户 端 函 数 时， 您 一 定 需 要 传 递 参 数 给 这 个 函 数。 目 前 的 分 布 式PowerBuilder 版 本 可 以 支 持 多 种 数 据 类 型 的 传 递： 所 有 简 单 的 数 据 类 型， 结 构 和 数 组， 使 用 传 值 法 和 传 参 法。 还 支 持 传 递 不 可 视 对 象。 不 过 不 能 以 引 用 方 式 传 递 对 象， 例 如 您 不 能 在 分 布 式 中 传 递 一 个 窗 口 或 一 个 数 据 窗 口 的 句 柄。

当 我 们 在 使 用 完 一 个 代 理 对 象 后 应 注 意 在 程 序 的 结 束 前 用destroy 语 句 析 构 这 个 对 象 实 例， 否 则 将 出 现 我 们 在 第13 期 曾 经 讨 论 过 的 内 存 漏 洞（memory leak） 的 问 题。 而 这 一 点 在 分 布 式 中 尤 为 重 要。 因 为 对 代 理 的 析 构 将 使 远 端 的 不 可 视 对 象 同 时 析 构。 如 果 您 忘 记 了 对 代 理 对 象 的 析 构， 不 可 视 对 象 将 不 会 在 服 务 器 端 自 动 清 除， 这 样 您 将 在 客 户 和 服 务 器 两 端 都 造 成 了 内 存 漏 洞。 此 外connection 对 象 还 有 一 个DisconnectServer 函 数， 这 个 函 数 用 以 取 消 客 户 与 服 务 器 的 连 接。 服 务 器 端 将 回 收 分 配 给 该 用 户 的 内 存， 在 程 序 结 束 时 不 使 用 这 个 函 数 也 将 造 成 服 务 器 器 端 的 内 存 漏 洞。 在 客 户 端 产 生 了 内 存 漏 洞 也 许 并 不 会 造 成 多 么 恶 劣 的 影 响， 在 服 务 器 端 有 一 个10K 左 右 的 漏 洞 可 能 也 算 不 得 什 么， 但 是 如 果 每 一 次 的 客 户 调 用NVO 函 数 都 造 成10K 的 漏 洞， 一 个 客 户 机 每 天 有100 次 的 调 用， 这 个 服 务 器 如 连 接 了100 个 客 户 机， 那 么 一 天 之 内 就 会 造 成102.4M 的 内 存 漏 洞， 这 足 以 使 任 何 强 壮 的 服 务 器 崩 溃。

开 发 分 布 式 应 注 意 的 问 题

  • 在 本 地 进 行 分 布 式 设 计

    我 们 在 做 数 据 库 模 型 设 计 时， 首 先 设 计 的 是 实 体 与 实 体 之 间 关 系， 我 们 将 不 考 虑 每 一 个 表、 每 一 个 字 段 的 具 体 实 现， 这 时 我 们 称 之 为 概 念 设 计 阶 段， 然 后 再 考 虑 其 物 理 实 现。 同 样 我 们 在 设 计 分 布 式 应 用 时 也 应 首 先 考 虑 在 本 地 上 的 实 现， 如 过 早 地 考 虑 网 络 的 拓 扑 模 型 将 会 使 得 您 的 分 布 式 应 用 失 去 未 来 重 新 分 割 应 用 的 灵 活 性。 应 用 在 分 布 运 行 之 前 首 先 应 当 在 本 地 成 功 运 行， 使 用Debug 调 试 必 须 使 用 本 地 的 接 口。

  • 同 产 品 相 同 的 开 发 环 境

    当 开 发 环 境 与 实 际 交 付 用 户 运 行 的 环 境 有 显 著 差 别 时， 会 使 得 这 个 应 用 在 成 为 产 品 时， 分 布 式 应 用 部 分 根 本 不 可 用。 例 如， 一 个 在 小 型 的 高 速 局 域 网 环 境 中 开 发 出 来 的 应 用， 如 工 作 在 一 个 拨 号 网 络 或 一 个 广 域 网 中。 也 许 就 不 会 有 象 期 望 的 那 样 有 理 想 的 运 行 效 果。

  • 使 网 络 通 讯 量 最 小 化

    在 物 理 对 象 设 计 和 实 现 过 程 中 最 基 本 的 目 标 是 减 少 分 布 式 应 用 中 代 理 之 间 的 消 息 和 代 理 对 象 的 传 递。 函 数 调 用， 对 远 过 程 对 象 中 实 例 变 量 的 引 用， 触 发 远 过 程 对 象 的 事 件 等 操 作 都 要 比 引 用 本 地 对 象 花 费 更 多 的 时 间。

  • PowerBuilder 对 象 库 的 分 布

    一 般 建 议 在 开 发 时， 将 不 可 视 对 象 及 其 代 理 对 象 放 在 同 一 个pbl 库 中， 这 个 库 都 包 括 在 客 户 端 应 用 和 服 务 器 端 应 用 可 搜 索 到 路 径 中。 这 样 在 对 象 被 修 改 后， 运 行 时 的 两 端 的 应 用 就 可 以 使 用 同 一 个DLL 或PBD。

  • 同 时 检 入（check in） 和 检 出（check out）NVO 及 其 代 理 对 象

    在 团 队 开 发 中， 一 个 程 序 员 检 入NVO 时， 应 当 同 时 检 入 它 的 代 理， 即 使 您 不 打 算 修 改 这 个 对 象 的 接 口 函 数，PowerBuilder 在 保 存NVO 对 象 时， 会 同 时 重 新 修 改 保 存 在 同 一 个pbl 库 中 的 这 个 对 象 的 代 理， 因 为 代 理 对 象 不 仅 带 有 接 口 函 数 的 信 息， 还 包 括 了 对 象 的 实 例 变 量 等 其 它 信 息 在 内。

  • 建 立 一 个 远 程 控 制 机 制

    在 一 般 情 况 下， 应 开 发 一 个 应 用 服 务 器 的 远 程 设 置， 控 制 新 用 户 连 接， 启 动， 关 闭 和 其 它 日 常 任 务 维 护 的 控 制 功 能， 以 便 于 管 理 员 对 应 用 服 务 器 的 管 理。

上 期 错 误 更 正

在8 月25 日 所 刊 出 的 第32 期 中， 我 们 介 绍 了 为 下 拉 式 数 据 窗 口 建 立 数 据 缓 冲 服 务 器， 我 们 也 可 以 使 用 分 布 式PowerBuilder 将 这 个 小 应 用 分 成 两 部 分， 把 缓 冲 区 建 在 应 用 服 务 器 上。 只 要 对 原 文 中 的 部 分 代 码 稍 加 修 改 就 可 以 完 成 这 个 分 布 式 应 用。 不 过 在 该 文 中 的wf_find_dataobject 函 数 却 有 一 个 严 重 的 错 误， 笔 者 误 将 应 当 用 在datawindow 中 的 代 码 用 在 了 这 里。 准 确 的 代 码 应 当 是 将 原 程 序 中 第14 行 至 第16 行 该 为：

    lds_new_entry = create u_dsa
IF IsNull(lds_new_entry) THEN
RETURN 0
END IF

衷 心 地 感 谢 向 我 指 出 这 一 错 误 深 圳 现 代 计 算 机 公 司 的 刘 伟 青 先 生， 同 时， 对 该 文 的 读 者 表 示 深 深 的 歉 意。


张健姿-->PowerBuilder系列讲座-->36. FUNCky 的使用

36. FUNCky 的使用

FUNCky 是 一 个PowerBuilder 应 用 程 序 库， 它 提 供 了16 位 和32 位 两 种 应 用 库， 其 中 包 含 了 大 量 的 底 层 函 数， 免 去 了 开 发 人 员 亲 自 动 手 使 用C 语 言 编 写 动 态 链 接 库 来 处 理 很 多 常 用 的 诸 如 文 件 操 作、 字 符 串 操 作、 位 操 作 等 功 能。 使 用Funcky 能 大 大 提 高 开 发 效 率。 本 篇 简 单 讲 述 一 下 如 何 在PowerBuilder 中 使 用Funcky。

将FUNCky 加 到 您 的 应 用 中

FUNCky 提 供 了16 位 和32 位 的 运 行 库Funcky.dll、Funcky16.pbl 和 Funcky32.dll、Funcky32.pbl， 应 用 程 序 调 用 的 函 数 或 过 程 都 在 该 动 态 链 接 库 和PBL 文 件 中。 安 装 完FUNCky 后， 两 个 动 态 链 接 库 被 拷 贝 到Windows 的System 目 录 下， 两 个PBL 则 在 安 装 的 目 录 下。 使 用 时 将 用 到 的 函 数 在 应 用 的Global External Function 或Local External Function 中 作 相 应 的 声 明 即 可， 如 需 使 用 全 部 函 数 则 可 以 将 整 个PBL 文 件 加 到 您 应 用 的 搜 索 库 中。 函 数 原 型 定 义 可 以 从 相 应 函 数 帮 助 中 获 得。

除 帮 助 文 件 中 提 供 的 函 数 以 外， 在funcky.dll 中 还 有 其 它 的 一 些 未 公 开 的 函 数 在Funcky.pbl 中 被 调 用， 这 些 函 数 用 户 也 可 以 使 用。

使 用Funcky 要 注 意 的 几 点

  • 在 应 用 中， 只 将 使 到 的 函 数 加 到 函 数 原 型 声 明 中 去， 不 要 声 明 过 多 的 函 数 原 形， 否 则 运 行 时 系 统 可 能 会 报 函 数 原 型 不 匹 配 错 误；
  • 如 只 需 使 用 少 量 函 数， 用 户 尽 量 不 要 将FUNCky.pbl 加 到 应 用 中 使 用， 因 为 那 将 需 要 大 量 的 函 数 原 型 声 明 才 能 编 译 通 过；
  • 某 些 函 数 原 型 是 以"_" 开 头 的 定 义， 这 些 函 数 都 在Funcky.pbl 中 有 定 义， 使 用 者 可 以 直 接 使 用 该 函 数， 将Funcky.pbl 中 的 代 码 直 接 拷 贝 到 自 己 的pbl 中， 或 自 己 编 写 相 关 的PowerBuilder 函 数。

Funcky 函 数 分 类

Funcky 一 共 提 供 了12 类 的 函 数：

  • 位 操 作 函 数
  • 块 和 内 存 操 作 函 数
  • 数 据 转 换 函 数
  • DBF 文 件 访 问 函 数
  • 磁 盘 访 问 函 数
  • DOS 环 境 函 数
  • 日 期 函 数
  • 低 级 文 件 操 作 函 数
  • 转 换 函 数
  • 字 符 串 操 作 函 数
  • 系 统 资 源 函 数
  • 时 间 操 作 函 数

举 例 说 明

下 面 我 们 举 几 个 例 子， 说 明 一 下 这 些 函 数 的 使 用。 我 们 着 重 介 绍 的 是DBF 文 件 访 问， 块 操 作 和 内 存 操 作、 低 级 文 件 操 作 等 这 几 类 函 数， 这 是 大 家 在 应 用 中 经 常 要 用 到 的， 而PowerBuilder 本 身 却 不 提 供 的 操 作。

  1. 我 们 经 常 在PowerBuilder 应 用 中 遇 到 有 关 图 象 存 储 和 显 示 的 问 题， 而 在 这 方 面，PowerBuilder 本 身 的 处 理 功 能 较 弱， 在 实 际 应 用 中 经 常 无 法 达 到 用 户 的 要 求。 我 们 可 以 借 助Funcky 的 函 数 来 处 理 图 象 文 件。

    例 如 在 应 用 中 需 要 存 储、 查 询、 显 示 和 打 印 照 片， 一 般 开 发 人 员 使 用OLE 嵌 入 的 方 法 来 做， 但 是 如 果 获 得 的 每 一 幅 照 片 的 规 格 不 完 全 一 样， 这 样 显 示 和 打 印 的 效 果 就 很 差， 采 用 这 种 方 法 制 作 通 行 证 或 身 份 证 上 的 照 片 几 乎 是 不 可 用 的。 解 决 这 一 问 题， 所 使 用 的 较 简 单 的 方 法 是， 以bitmap 格 式 在 数 据 库 中 存 放 图 象 数 据， 显 示/ 打 印 时， 将 数 据 取 出 在 本 地 保 存 成 为 临 时 文 件， 根 据 图 象 的 尺 寸 进 行 等 比 例 缩 放 显 示 在 图 象 控 件 上， 这 样 既 能 达 到 界 面 的 要 求， 又 能 获 得 高 质 量 的 图 象。 我 们 可 以 借 助Funcky 函 数 来 解 决 这 样 几 个 问 题：

    • bitmap 图 象 文 件 存 储 效 率 不 高， 在 数 据 库 中 占 据 的 空 间 过 大， 可 在 存 入 前 使 用fcompress 函 数 对 图 象 文 件 进 行 压 缩：

    函 数 声 明：

FUNCTION long fcompress(string s,string d)
LIBRARY  "FUNCky32.DLL"
//定义压缩函数原型
SCRIPT：
retcode = fcompress(ls_path, "_tmp.tmp")
//压缩文件
    choose case retcode
...//检查返回值
    End choose
...//将压缩后的文件保存到数据库中

    fdcompress 的 压 缩 效 率 不 高， 但 速 度 很 快， 还 是 值 得 使 用 的。

    • bitmap 图 象 的 宽 和 高 保 存 在 该 文 件 的 第18 和 第22 个 字 节 中， 不 用Funcky 函 数 很 难 读 出 这 两 个 字 节 的 信 息。 下 面 一 段 程 序 是 解 压 缩 和 计 算BMP 图 象 的 宽 和 高

    函 数 声 明：

//定义从二进制数据中读取一个长整形函数原型
FUNCTION long _blob2long(ref blob b, uint o)
LIBRARY "FUNCky32.DLL"
//定义解压缩函数原型
FUNCTION long fdcompress(string s,string d)
LIBRARY "FUNCky32.DLL"
SCRIPT：
... //从数据库中将数据取出保存在文件$fgd.bmp中
    status = fdcompress("$fgd.bmp","_fgd.bmp")
//解压缩
choose case status
    ...//检查返回值
    End choose
...//将_fgd.bmp读入blob变量pic
ll_width = _blob2long(pic,18)   //读图象宽度
ll_height = _blob2long(pic,22)  //读图象高度
//根据指定的宽和高计算缩放比例
bili_width = integer(sle_width.text) / ll_width
bili_height = integer(sle_height.text) / ll_height
//得到显示宽度和高度
p_width = ll_width * min(bili_width, bili_height)
p_height = ll_height * min(bili_width, bili_height)

    我 们 可 以 看 到Funcky 函 数 的 使 用 非 常 简 单， 能 够 轻 松 做 到 在PowerBuilder 中 很 难 实 现 的 操 作。

  2. 在 应 用 中 您 可 能 会 遇 到 直 接 读 取DBF 文 件 的 情 况， 处 理 这 类 问 题 的 方 法 一 般 有 两 种 经 典 的 方 法： 一 是 使 用 数 据 窗 口 读DBF 文 件， 二 是 指 定 一 个 磁 盘 目 录 作 为 数 据 区， 用ODBC 连 接 这 个 目 录， 使 用 时 规 定 用 户 必 须 将DBF 文 件 拷 贝 到 这 个 目 录 下。 但 是 这 两 种 方 法 都 缺 乏 灵 活 性， 而 在Funcky 中 却 提 供 了 直 接 读DBF 文 件 的 函 数。 在Funcky32.pbl 中 有 一 个dbffieldget() 函 数 可 以 获 取 该DBF 文 件 的 字 段 名 称。 不 过 根 据 我 们“ 尽 量 不 使 用Funcky32.pbl” 的 原 则， 自 己 手 工 编 写 一 个 实 现 该 功 能 的 代 码。

    定 义 外 部 函 数：

FUNCTION long dbfOpen(string f, int o)
LIBRARY "FUNCky32.DLL"
FUNCTION boolean dbfEof(long d)
LIBRARY "FUNCky32.DLL"
FUNCTION int dbfClose(long d)
LIBRARY "FUNCky32.DLL"
FUNCTION long dbfSkip(long d, long c)
LIBRARY "FUNCky32.DLL"
FUNCTION uint dbfFieldSize(long d, int p)
LIBRARY "FUNCky32.DLL"
SUBROUTINE _dbfFieldGet(long d,int p,ref string b)
LIBRARY "FUNCky32.DLL"
FUNCTION int dbfFieldPos(long d,string n)
LIBRARY "FUNCky32.DLL"

SCRIPT：
//以读写和拒绝其他用户写的方式打开dbf数据库
do while 1 = 1
    dbf = dbfopen(ls_file, 2 + 32)
    if dbf <> 0 then
        exit
    end if
loop

//读dbf数据
//ii_xx_count是需要读的dbf字段的总数
//is_dbf[]是字段名称数组
do while not dbfeof(dbf)
for i = 1 to ii_xx_count
p = dbffieldpos(dbf, upper(is_dbf[i]))
//获取字段的位置
ls_tmp = space(dbffieldsize(dbf, p) + 1)
//根据字段的大小初始化存放数据的变量
_dbffieldget(dbf, p, ls_tmp)
//将数据读入到ls_tmp中
    next
        dbfskip(dbf, 1)
//使dbf操作指向下一条记录
loop
dbfclose(dbf)
//关闭打开的dbf文件

    这 样 直 接 读 取DBF 信 息， 给 应 用 带 来 的 好 处 是 显 而 易 见 的。

  3. 对 于 数 据 库 的 用 户 管 理， 我 们 一 般 会 采 用 在 数 据 库 中 建 立 一 个 表， 记 录 用 户 名 称、 口 令 等 信 息， 在 用 户 登 录 时， 应 用 检 索 这 个 表 的 信 息， 同 用 户 输 入 的 内 容 核 对。 按 照 一 般 系 统 管 理 的 规 定， 每 一 个 用 户 的 口 令， 其 他 人 是 无 法 在 系 统 中 查 到 的。 即 使 是 系 统 管 理 员 也 只 有 删 除 登 录 用 户 的 权 利， 而 无 法 获 取 用 户 的 口 令。 可 是 由 于 系 统 管 理 员 可 以 读 取 数 据 库 中 的 每 一 张 表， 因 此 他 是 就 有 机 会 看 到 用 户 的 登 录 口 令 的， 因 此 我 们 应 当 在 口 令 字 段 存 入 数 据 库 前 对 其 进 行 加 密。Funcky 提 供 了encrypt() 函 数 和decrypt() 函 数 就 可 以 用 来 完 成 这 一 工 作， 这 就 避 免 了 程 序 员 手 工 编 写 加 密 算 法 的 麻 烦 了。

    此 外，Funcky 在16 位Windows 下 还 有 许 多 底 层 的 操 作， 包 括 中 断 的 调 用 和 直 接 操 作 磁 盘 的 函 数， 可 以 用 以 软 件 加 密 的 工 作， 这 里 就 无 法 详 述 了。 总 之，Funcky 提 供 了 较 多 的 函 数 功 能， 有 条 件 的 读 者 可 以 在 以 后 的 应 用 中 尝 试 使 用， 相 信 会 给PowerBuilder 的 开 发 带 来 一 定 的 方 便。


张健姿-->PowerBuilder系列讲座-->37. PFC 介绍

37. PFC 介绍

PFC 是PowerBuilder 高 级 开 发 工 具 集 中 的 一 个 基 本 类 库， 提 供 了 进 行 一 般 应 用 开 发 时 会 经 常 用 到 的 对 象 和 函 数 代 码。PowerBuilder 5.0 的 基 本 类 库 中 的 内 容 十 分 丰 富， 比 以 前 版 本 中 所 提 供 的 功 能 要 增 加 了 许 多， 而 且 对 象 之 间 的 继 承 和 引 用 关 系 也 比 以 前 更 为 清 晰 合 理。 因 此 编 写 一 个 完 全 建 构 在PFC 类 库 之 上 的 应 用 可 大 大 提 高 软 件 质 量， 而 且 利 用 基 类 的 已 有 代 码 也 会 使 得 许 多 复 杂 的 功 能 实 现 起 来 要 轻 松 得 多。 可 是 如 何 编 写 这 样 的 应 用， 以 合 理 地 继 承、 利 用 和 改 造PFC 的 基 本 框 架， 以 及 使 用PFC 时 应 注 意 的 事 项， 却 是 十 分 复 杂 的 问 题， 在 这 里 无 法 用 几 千 字 就 能 够 讲 清 楚。 不 过 我 们 仍 然 能 够 像 使 用 以 往 版 本 的 类 库 一 样， 只 是 借 用PFC 中 一 些 对 象 的 基 本 功 能 以 省 去 自 己 编 写 代 码 的 麻 烦。

本 文 将 主 要 介 绍 如 何 利 用PFC 中 几 个 数 据 窗 口 功 能， 为 我 们 的 编 程 带 来 便 利。

我 们 在 应 用 中 会 遇 到 层 次 型 关 系 的 数 据， 例 如 部 门 和 雇 员 实 体 之 间 的 关 系。 在 对 一 个 部 门 下 雇 员 的 数 据 进 行 维 护 时， 可 以 使 用Master/Detail 的 表 现 形 式， 在 数 据 窗 口dw_1 表 现 已 有 部 门 的 数 据， 并 根 据 用 户 选 择 的 某 一 条 记 录， 在dw_2 中 变 换 所 选 定 部 门 雇 员 的 信 息， 并 对 雇 员 信 息 进 行 维 护。

首 先 声 明 一 个 全 局 变 量：
n_cst_appmanager gnv_app
在 应 用 的Open 事 件 中 加 入：
gnv_app = CREATE n_cst_appmanager
gnv_app.Event pfc_Open(commandline)
在 应 用 的Close 事 件 中 键 入：
gnv_app.Event pfc_Close( )
DESTROY n_cst_appmanager

在 窗 口 中 我 们 定 义dw_1 和dw_2 为PFC 中u_dw 的 实 例 对 象， 在 窗 口 的Open 事 件 中 我 们 编 程 如 下：

// 设 置dw_1 和dw_2 的 级 联 方 式
dw_1.of_setlinkage(true)
dw_2.of_setlinkage(true)
dw_1.of_settransobject(sqlca)
dw_2.of_settransobject(sqlca)
// 定 义dw_1 和dw_2 的 级 联 关 系
dw_2.inv_linkage.of_linkto(dw_1)
dw_2.inv_linkage.of_setarguments("dept","dept")
dw_2.inv_linkage.of_setusecollinks(1) // 设 置 连 接 方 式 为filter
// 触 发dw_2 的pfc_retrieve() 事 件
dw_2.event pfc_retrieve()
if dw_1.event pfc_retrieve() = -1 then
    messagebox(" 错 误"," 提 取 数 据 错 误-- 类 别")
else
    dw_1.setfocus()
end if
在dw_1 和dw_2 的pfc_retrieve
事 件 中 将 祖 先 的 代 码 覆 盖， 并 分 别 写 下 如 下 语 句：
return this.retrieve()

在dw_1 的rowfocuschanged 事 件 代 码 中 将 对 该 数 据 窗 口 的 数 据 进 行 过 滤， 而 执 行retrieve() 函 数 将 触 发 这 一 事 件。

使 用PFC 中 这 个 对 象 的 优 点 还 可 以 体 现 在 以 下 两 点： 当 需 要 更 新 级 联 的 所 有 数 据 窗 口 时， 只 需 执 行dw_1.inv_linkage.of_update() 一 条 命 令 即 可 完 成； 如 果 想 要 在dw_2 中 增 加 一 行 新 数 据， 可 使 用dw_2.pfc_addrow(0) 或dw_2.pfc_insertrow() 函 数， 这 个 函 数 会 将dw_2 中dept 字 段 的 值 自 动 设 置 为dw_1 中dept 的 当 前 值， 从 而 避 免 了 手 工 使 用GetItem 和SetItem 函 数 来 设 置。

在 上 述 简 单 的 关 系 中， 并 未 充 分 体 现 出PFC 优 越 性。 但 在 一 个 工 厂 中 对 零 件 的 管 理 或 是 大 型 商 场 中 对 商 品 的 管 理， 往 往 是 要 通 过 多 次 的 级 联 才 能 定 位 到 某 一 类 产 品 的 子 目， 在 这 样 的 系 统 中， 拥 有 一 个 像u_dw 这 样 的 基 类， 编 程 的 效 率 就 可 以 提 高 许 多。

在 一 个 数 据 窗 口 中 如 果 有 的 列 含 有 下 拉 式 数 据 窗 口 时， 在 这 些 列 中 使 用 下 拉 搜 索 功 能 会 给 用 户 带 来 很 多 方 便， 也 就 是 说 在 这 些 列 中 当 用 户 使 用 键 盘 敲 入 数 据 时， 应 用 程 序 自 动 地 搜 索 下 拉 式 数 据 窗 口 中 对 应 的 值， 使 其 跳 转 到 相 应 的 记 录 上。 使 用PFC， 我 们 只 需 指 定 这 个 数 据 窗 口 继 承n_cst_dwsrv_dropdownsearch 对 象， 并 在 这 个 数 据 窗 口 中 键 入 下 列 代 码：

constructor 事 件：
dw_1.of_setdropdownsearch(true)
dw_1.inv_dropdownsearch.of_addcolumn()
editchanged 事 件：
inv_dropdownsearch.event pfc_editchanged (row,dwo,data)

这 个 数 据 窗 口 中 所 有 的 下 拉 式 数 据 窗 口 就 都 具 备 了 下 拉 搜 索 功 能。 不 过 我 们 要 对 这 个PFC 对 象 进 行 改 进， 以 适 应 中 国 国 情。 原 有 的 搜 索 功 能 是 要 求 用 户 在 该 列 中 输 入 下 拉 式 数 据 窗 口 的 显 示 值(display column) 才 能 查 询， 这 对 于 使 用 拼 音 文 字 的 用 户 是 十 分 方 便 的， 应 用 将 随 着 用 户 输 入 字 符 的 增 多， 逐 步 递 进 地 显 示 相 应 的 记 录。 可 是 在 中 文 应 用 软 件 中， 该 列 的 显 示 值 一 般 都 为 中 文， 输 入 并 不 方 便， 所 以 有 经 验 的 操 作 员 往 往 习 惯 于 使 用 他 们 熟 悉 的 编 号， 即 下 拉 式 数 据 窗 口 的 实 际 存 储 值(data column)， 因 此 将 应 用 程 序 做 成 对 实 际 值 进 行 查 询 也 许 更 符 合 中 国 用 户 的 习 惯。 我 们 就 需 要 在n_cst_dwsrv_dropdownsearch 中 的pfc_editchanged 事 件 中 作 如 下 修 改：

覆 盖 祖 先 类 的 代 码， 键 入 新 代 码。 新 代 码 是 对 它 的 祖 先 对 象pfc_ n_cst_dwsrv_dropdownsearch 中pfc_editchanged 事 件 的 代 码 作 部 分 修 改， 将 其 中 第72 行

    ls_dddw_displaycol = adwo_obj.dddw.displaycolumn
改 为：
    ls_dddw_displaycol = adwo_obj.dddw.datacolumn

即 可。（ 因 代 码 过 长， 我 们 无 法 在 这 里 全 部 引 述。）

3. 使 用PFC 中 有 关 数 据 窗 口 打 印 的 功 能。 在u_dw 对 象 中 还 提 供 了 一 些 有 关 打 印 的 事 件 和 函 数， 我 们 首 先 在 数 据 窗 口 的 构 造 事 件 或 窗 口 的Open 事 件 中， 将 数 据 窗 口 设 置 允 许 打 印 预 览： dw_1.of_setprintpreview(true)。

我 们 可 以 在 程 序 中 调 用dw_1.event pfc_print()、dw_1.event pfc_printpreview()、dw_1.event pfc_pagesetup()、dw_1.event pfc_zoom() 分 别 用 来 实 现 打 印、 打 印 预 览、 页 面 设 置、 预 览 窗 口 的 放 大 缩 小 等 功 能。 在 这 些 功 能 中，PowerBuilder 会 自 动 弹 出 相 应 的 窗 口， 由 用 户 设 置 打 印 的 份 数， 页 面 纸 张 的 大 小， 预 览 窗 口 的 显 示 比 例 等 参 数。

此 外 在 窗 口 对 象 中PFC 提 供 了response 风 格 窗 口 的 祖 先 类w_response， 其 中 加 入 一 些 用 户 可 能 经 常 用 到 的 功 能。 例 如 只 需 在 继 承 窗 口 的open 事 件 中 加 入 如 下 语 句：this.inv_base.of_center()， 可 实 现 该 窗 口 在 打 开 时 窗 口 在 屏 幕 的 正 中 位 置 显 示； 又 如 在open 事 件 中 加 入： this.of_setbase(true)， 每 个 继 承 窗 口 缺 省 都 有 数 据 数 据 窗 口 存 储 功 能 等 等。


张健姿-->PowerBuilder系列讲座-->38. PowerBuilder 6.0 简介

38. PowerBuilder 6.0 简介

PowerBuilder 6.0 不 久 将 在 中 国 市 场 发 布， 这 里 我 们 简 单 介 绍 一 下 这 一 版 本 的 新 功 能。

对 常 规 应 用 开 发 功 能 的 增 强

对 于 使 用PowerBuilder 开 发 客 户/ 服 务 器 前 端 应 用 的 用 户 来 说， 新 版 本 增 加 的 功 能 并 不 算 多， 主 要 有 这 样 一 些：

新 的 调 试 器

在 以 往 版 本 中，PowerBuilder 所 提 供 的 调 试 功 能 同 那 些 以 生 产 开 发 工 具 闻 名 的 微 软、Borland 等 厂 商 出 产 的Visual Basic、Delphi 相 比， 功 能 要 弱 得 多。 在 这 一 版 本 中，PowerBuilder 为 用 户 提 供 的 调 试 手 段 和 能 够 显 示 的 信 息 都 大 为 增 强， 新 的 调 试 窗 口 以Tab Folder 的 形 式 表 现， 而 且 还 允 许 用 户 同 时 打 开 多 个MDI 调 试 窗 口。 这 样 就 大 大 增 加 了 在 窗 口 所 能 向 用 户 展 现 的 信 息 量， 新 的 调 试 功 能 有：

  • 实 时 调 试： 开 发 人 员 可 在 运 行 状 态 中 断 进 入 调 试 器。
  • 剖 析 与 跟 踪： 应 用 的 跟 踪 与 剖 析 能 力 使 开 发 人 员 能 够 收 集、 跟 踪 和 分 析PowerBuilder 应 用 执 行 的 信 息， 以 得 到 各 个 部 分 代 码 的 执 行 效 率。
  • 定 制 例 程： 开 发 人 员 能 够 使 用 新 的PowerBuilder 对 象 与PowerScript 函 数 创 建 和 定 制 分 析 例 程。

在 新 的 调 试 窗 口 中， 我 们 可 以 看 到 以 下 调 试 信 息：

  • 设 置 条 件 断 点
  • 堆 栈 的 数 据
  • 内 存 中 的 对 象， 即 在 应 用 中 所 有 对 象 的 实 例
  • 源 代 码
  • 源 代 码 浏 览 器： 所 有 对 象 中 的 代 码 都 以Tree View 形 式 组 织， 这 样 可 以 使 用 户 更 快、 更 方 便 地 搜 索 代 码
  • 历 史 源 代 码
  • 变 量
  • 工 具 条

PowerBuilder 6.0 使 用 的 是 新 的Microsoft Office 97 风 格 的 平 面 工 具 条。 此 外， 在6.0 中 增 加 了 一 个 工 具 栏 按 钮 可 以 生 成Windows 注 册 项 并 激 活windows 的 同 步 工 具。

数 据 窗 口 的 增 强：

数 据 窗 口 的 功 能 得 到 了 进 一 步 的 加 强。 在 以 往 的 讲 座 中 我 们 经 常 谈 到 如 何 增 强 数 据 窗 口 的 功 能， 并 将 其 封 装 在 一 起， 定 制 成 为 新 的 用 户 对 象。 在6.0 中 数 据 窗 口 支 持 按 钮 对 象， 可 任 意 放 置 在Header、Detail 或Footer 等 任 意 位 置， 并 增 加 了 与 按 钮 相 关 的Clicking 和Clicked 事 件。 这 样 开 发 人 员 可 以 将 对 数 据 的 增、 删、 改、 查 询 等 功 能 的 按 钮 全 部 放 置 在 数 据 窗 口 的 内 部， 不 需 使 用 用 户 自 定 义 对 象 就 可 以 得 到 具 有 这 些 功 能 的 封 装 对 象。 另 外， 增 加 了Group Box 对 象 可 以 用 来 框 起 和 标 记 数 据 窗 口 上 的 一 系 列 对 象， 不 过 在 数 据 窗 口 中 这 个 对 象 的 功 能 是 以 装 饰 功 能 为 主， 这 可 能 是 以 往 版 本 中 数 据 窗 口 没 有 这 个 对 象 的 原 因。 所 以 以 前 为 了 实 现 这 样 的 一 个 效 果， 我 们 要 用 两 个 不 同 颜 色 的 矩 形 框 叠 加， 才 能 在 数 据 窗 口 上 马 马 虎 虎 地 作 出 一 个 有 三 维 效 果 的 分 组 框（ 实 际 上 仔 细 看， 这 个 立 体 效 果 也 是 有 问 题 的）， 或 是 在 窗 口 设 计 时 在 数 据 窗 口 控 件 上 再 叠 加 一 个 分 组 框。 现 在 就 方 便 了 些。

远 程DataStore：

客 户 端 的 数 据 窗 口 与DataStore 现 在 可 以 与 应 用 服 务 器 端 的DataStore 交 换 数 据。

对Internet/intranet 的 支 持

支 持Web

为 支 持 开 发 人 员 在Web 上 分 发 的 应 用 的 需 要，PowerBuilder 6.0 将 原 来5.0 中Internet 开 发 工 具IDT 集 成 到 了 新 的 开 发 环 境 中。 包 括 的 组 件 有：

  • Web.PB 的 动 态 连 接 库： 用 于 支 持Web 服 务 器 的CGI、ISAPI 和NSAPI 协 议
  • Web.PB 类 库： 用 于 生 成HTML 以 及 管 理 浏 览 器 的 连 接 状 态
  • Web.PB 向 导： 生 成 用 于 激 活 分 布 式 对 象 服 务 的HTML 页 面
  • PowerBuilder Window Plug-in 的 动 态 连 接 库： 支 持 标 准 模 式 和 安 全 模 式 两 个 版 本
  • PowerBuilder Data Window Plug-in 的 动 态 连 接 库： 仅 有 支 持 标 准 模 式
  • Window ActiveX： 这 是6.0 新 增，IDT 中 没 有 的 内 容， 支 持 标 准 模 式 和 安 全 模 式 两 个 版 本， 与Plug-in 性 质 类 似， 但 允 许 调 用 方 法。
  • O'Reilly WebSite Web 服 务 器

生 成HTML 的 增 强

增 强 了 数 据 窗 口 生 成HTML 的 能 力， 使 应 用 能 很 好 地 保 留 了 数 据 窗 口 的 表 现 风 格， 更 为 灵 活 轻 松 的 生 成 动 态Web 页 面。

  • 对 于FreeForm 风 格 或Tabular 风 格 的 数 据 窗 口， 可 以 指 定 一 定 范 围 的 行 和 列 生 成 相 应 的HTML 文 本。
  • Tab Order 不 为 零 的 列 可 成 为 输 入 域。
  • HTML 文 本 输 入 域 名 将 从 列 名 派 生。
  • 文 本、 计 算 列 等 成 为HTML 的 文 本。
  • PowerBuilder 中 的 按 钮 也 对 应HTML 中 的 按 钮， 其 它 的 控 件( 如 单 选 钮、 复 选 框、 列 表 框 等) 映 射 为HTML 中 相 应 的 控 件.
  • 嵌 套 的 数 据 窗 口 可 生 成 的HTML 语 法 中 的HTML 表 格。

对 事 务 的 支 持

应 用 服 务 器 的 异 步 处 理 功 能

我 们 曾 经 在 以 前 的 文 章 中 提 到 过，PowerBuilder 5.0 的 分 布 式 事 务 只 支 持 同 步 方 式， 而 在6.0 中， 加 入 了 支 持 异 步 处 理 的 功 能。 在 应 用 服 务 器 上 提 供 了 客 户 请 求 的 排 队 机 制， 使 客 户 端 在 发 出 一 个 对 应 用 服 务 器 上 方 法 的 调 用 请 求 后 继 续 它 的 程 序， 而 且 不 需 要 停 下 等 待 这 个 方 法 执 行 完 毕。

服 务 器 的 推 送

服 务 器 推 送 使 得 分 布 式PowerBuilder 的 应 用 服 务 器 可 以 向 应 用 的 客 户 端 回 送 消 息。 这 种 推 送 技 术 既 可 以 将 被 异 步 方 式 调 用 的 结 果 传 送 给 客 户， 也 可 向 客 户 端 发 送 其 它 更 一 般 的 信 号。

应 用 服 务 器 共 享 对 象

在6.0 中 可 以 允 许 多 个 客 户 共 享 一 个 应 用 服 务 器 实 例 对 象， 而 不 必 象 在5.0 中 那 样 对 每 一 个 客 户 的 连 接， 服 务 器 都 要 初 始 化 一 个 实 例。 对 象 的 命 名 实 例 可 以 作 为 共 享 对 象 注 册。 一 旦 注 册， 共 享 对 象 就 可 被 应 用 服 务 器 会 话 里 的 任 一 客 户 会 话 所 访 问。

名 称 服 务 器 实 用 工 具

名 称 服 务 器 的 实 用 工 具 可 以 实 现 物 理 的 应 用 服 务 器 及 其 逻 辑 名 称 之 间 的 重 新 命 名， 这 个 工 具 的 作 用 主 要 是 为 了 在 服 务 器 之 间 均 衡 客 户 的 连 接 负 载， 也 可 以 为 分 布 式PowerBuilder 应 用 服 务 器 提 供 连 接 信 息。

PowerBuilder 虚 拟 机

将PowerBuilder 执 行 所 需 的 有 关 文 件 和 动 态 连 接 库 形 成 一 个 统 一 的PowerBuilder 运 行 环 境 称 作PowerBuilder 虚 拟 机， 以 简 化PowerBuilder 执 行 环 境 的 分 发。

同 步 工 具

这 个 新 增 加 的 功 能 可 以 检 验 任 意 一 组 文 件 以 确 定 其 中 所 有 的DLL 是 否 为 最 新 版 本。 如 果 不 是， 当 这 些DLL 被 使 用 时 将 会 被 自 动 更 新。 这 个 同 步 工 具 对 于 检 验PowerBuilder 虚 拟 机 的 本 地 拷 贝 尤 其 有 用。 开 发 人 员 可 以 设 置 这 个 工 具， 使 其 在 应 用 启 动 时 自 动 运 行。PB 的 同 步 工 具 可 以 支 持16 位 和32 位 的Windows， 也 可 以Active X 的 方 式 运 行。

开 放 的 技 术

PowerBuilder 6.0 在 开 发 技 术 方 面 的 增 强， 主 要 有：

多 平 台 的 支 持

6.0 增 加 的UNIX 平 台 还 包 括 了IBM AIX 与HP-UX 系 列， 不 同 平 台 之 间 的 兼 容 是 源 码 级 的， 除 非 您 在 代 码 中 使 用 了OLE 等Windows 特 有 的 协 议， 您 开 发 出 的 一 套 源 码 就 可 以 在 各 种UNIX 工 作 站 上 使 用。

扩 展 的 数 据 库 连 接 能 力

PowerBuilder 6.0 增 加 了 用 于Informix 7.2 的 专 有 数 据 库 连 接 驱 动， 不 过 只 限 于32 位Windows 平 台； 增 加 了 对Sybase SQL 11.1 的 支 持， 包 括Open Client 11.1 安 全 与 目 录 的 服 务； 提 供 了 微 软ODBC 3.0 协 议 的 的 接 口。

扩 展 的 语 言 支 持

在Windows NT 4.X 平 台 上， 有PowerBuilder Enterprise 的Unicode 版 本。PowerBuilder 的Unicode 版 本 支 持 完 全 的Unicode， 即 所 有 的 字 符 都 是 双 字 节 的。 这 与UTF8 采 用 不 同 数 目 的 字 节 来 存 储 不 同 的 字 符 不 同。 所 有 的 对 话 框 和 内 部 显 示 都 是 使 用Unicode 字 符 集 的 英 语 表 示 的， 不 过 这 样 的 后 果 是 软 件 的 大 小 要 比 使 用ASCII 码 的 版 本 大。 用PowerBuilder 的Unicode 版 本 建 立 的 应 用， 必 须 分 发 到 具 有 下 述 条 件 的 机 器 上： 这 些 机 器 上 运 行 支 持Unicode 的 操 作 系 统(Windows NT 4.X)， 在 该 操 作 系 统 上 运 行PowerBuilder Unicode VM。 只 要 您 所 使 用 的 字 符 为ANSI 所 支 持， 应 用 就 可 以 在ANSI 与Unicode 版 本 之 间 迁 移。 支 持 使 用Unicode 对 中 国 用 户 是 一 件 好 事， 用 户 可 以 多 了 一 个 选 择 的 机 会， 例 如PowerBuilder 窗 口 的RTF 控 件 和 组 件 产 品Visual Component 的Visual Writer 均 不 是Windows 的 标 准 控 件， 所 以 都 不 能 支 持 中 文， 因 此 在 删 除 和 滚 屏 时 都 可 能 有“ 半 汉 字” 的 问 题 而 导 致 屏 幕 显 示 乱 码， 使 用Unicode 就 可 以 避 免 这 一 问 题。

扩 展 了 对 第 三 方 应 用 接 口 的 支 持

在PowerBuilder6.0 中 增 加 了 类 定 义 的 接 口， 用 户 可 以 通 过PowerScript 得 到 关 于PowerBuilder 类 定 义 的 信 息。 此 外 还 增 加 了 一 些 对 象 类 和 函 数 用 以 获 得PowerBuilder 对 象 及 其 属 性 的 信 息， 所 有 的 属 性 都 是 只 读 的。

PowerBuilder 6.0 支 持 生 成 多 种 标 准 组 件， 包 括ActiveX 组 件、CORBA 组 件、C++ 类 和Java 组 件。


张健姿-->PowerBuilder系列讲座-->40. 图形用户界面设计（续）

40. 图形用户界面设计（续）

规 范 性 和 一 致 性

我 们 经 常 使 用 的 图 形 用 户 界 面， 如Windows、Mac、Xwindow 等， 都 提 供 了 一 套 标 准 的 控 件， 经 常 使 用 计 算 机 的 用 户 可 能 已 十 分 了 解 这 些 标 准 控 件 的 用 途， 例 如 一 个 多 选 框 在 用 户 用 鼠 标 点 击 后 会 改 变 其 状 态， 当 按 动 滚 动 条 上 的 箭 头 时， 屏 幕 将 发 生 滚 动； 当 点 击 单 选 框 时 一 般 系 统 不 会 弹 出 一 个 对 话 框， 如 果 不 做 诸 如 点 击 按 钮 或 选 择 菜 单 等 类 似 的 操 作， 系 统 也 不 会 进 入 到 一 个 什 么 新 的 操 作 进 程 等 等。 在 用 户 用 过 了 大 量 应 用 软 件 后， 用 户 就 会 对 常 见 窗 口 的 操 作 建 立 起 一 些 基 本 概 念， 当 他 进 入 一 个 从 未 用 过 的 软 件 时， 不 会 束 手 无 策， 而 是 会 因 该 新 软 件 与 已 用 过 的 某 些 应 用 相 似， 从 而 对 它 的 使 用 有 一 定 认 识。 这 种 认 识 程 度 越 高， 说 明 这 一 软 件 越 直 观， 也 就 是 软 件 的 用 户 界 面 设 计 得 越 成 功。

因 此 每 一 个 程 序 员 都 应 当 意 识 到 他 自 己 所 开 发 的 应 用 与 其 他 程 序 员 开 发 的 应 用 都 是 在 同 一 平 台 上， 使 用 的 也 是 相 同 的 窗 口 控 件， 用 户 在 其 他 应 用 上 学 到 的 对 软 件 的 操 作 方 法 越 多， 可 运 用 在 新 开 发 的 应 用 上 的 也 就 越 多。 如 果 使 用 标 准 的 方 法， 用 户 会 感 到 越 方 便， 尽 管 他 们 并 不 一 定 能 意 识 到 方 便 的 原 因 所 在。

作 为 一 个 程 序 员 首 先 应 当 了 解 窗 口 中 不 同 控 件 的 作 用， 不 少PowerBuilder 的 程 序 员 都 在DOS 或Mainframe 的 字 符 终 端 上 开 发 过 应 用 软 件， 而 对Windows 的 图 形 界 面 却 比 较 陌 生。 因 此 作 为 程 序 员 熟 悉 使 用 怎 样 的 控 件 来 完 成 怎 样 的 功 能 是 至 关 重 要 的。 当 一 个 程 序 员 用 错 了 所 应 使 用 的 控 件 时， 应 用 也 不 可 能 是 很 直 观 的。

保 持 一 致 是 使 应 用 直 观 的 重 要 环 关。 一 致 性 既 包 括 使 用 标 准 的 控 件， 也 是 指 使 用 相 同 的 信 息 表 现 方 法， 如 在 字 体、 标 签 风 格、 颜 色、 术 语、 显 示 错 误 信 息 等 方 面 确 保 一 致。 下 面 是 显 示 信 息 时 应 当 遵 循 的 一 致 性 标 准：

  • 标 签 提 示： 字 体 为 加 重、 宋 体、 黑 色、 灰 底 或 透 明、 无 边 框。 右 对 齐 并 带 有 冒 号 结 束。 如“ 姓 名：”；
  • 日 期： 正 常 字 体、 宋 体、 白 底 黑 字，3-D lowered；
  • 对 齐 方 法：
    1. 左 对 齐： 一 般 文 字、 单 个 数 字、 日 期 等。
    2. 右 对 齐： 数 字、 时 间、 日 期 加 时 间。

对 于 这 些 信 息 的 排 列 大 部 分 都 是 由 程 序 员 来 控 制 的， 上 述 显 示 风 格 也 仅 供 参 考， 更 为 重 要 的 是 程 序 员 应 当 注 意 在 同 一 软 件 中 表 现 形 式 的 一 致 性。 如 果 在 同 一 个 应 用 中， 这 些 信 息 的 表 现 方 式 不 一 致， 会 使 得 用 户 分 散 注 意 力， 因 而 影 响 这 一 软 件 的 使 用。

遵 循 了 上 述 一 致 性 原 则 后， 还 应 注 意 在 一 个 窗 口 内 部 所 有 控 件 的 布 局 和 信 息 组 织 的 艺 术 性， 使 得 用 户 界 面 美 观。 在 一 个 窗 口 中 按tab 键， 移 动 聚 焦 的 顺 序 不 能 杂 乱 无 章， 在 字 符 界 面 下， 如DOS 中，tab order 的 顺 序 是 先 从 左 至 右， 后 从 上 至 下， 而 在Windows 中 的 顺 序 则 一 般 应 先 从 上 至 下 再 从 左 至 右。 一 屏 中 首 先 应 输 入 的 和 重 要 信 息 的 控 件 在tab 顺 序 中 应 当 靠 前， 位 置 也 应 放 在 窗 口 上 部 较 醒 目 的 位 置。

MS Windows 应 用 中 应 当 遵 循 的 规 则 还 有 一 条 是 可 不 用 鼠 标 的 原 则： 应 用 中 的 每 一 个 功 能 只 用 键 盘 也 应 当 可 以 完 成， 也 就 是 说 在 设 计 的 应 用 中 还 应 加 入 一 些 必 要 的 按 钮 和 菜 单 项。 但 是， 许 多 鼠 标 的 操 作， 如 双 击、 拖 动 对 象 等， 并 不 能 简 单 地 用 键 盘 来 模 拟 即 可 实 现， 例 如 在 一 个 列 表 框 中 用 鼠 标 双 击 其 中 一 项 可 以 表 示 选 中 该 项 内 容； 为 了 用 键 盘 也 能 实 现 这 一 功 能， 必 须 在 窗 口 中 定 义 一 个 表 示 选 中 的 按 钮， 以 作 为 实 现 双 击 功 能 的 替 代。 又 如 在 一 个 窗 口 中 有 两 个 数 据 窗 口， 可 以 用 鼠 标 从 一 个 数 据 窗 口 中 将 一 项 拖 出 然 后 放 到 另 一 个 中； 如 果 只 用 键 盘， 就 应 当 在 菜 单 中 设 置 拷 贝 或 移 动 的 菜 单 项。

在 菜 单 项 中 使 用 快 捷 键 可 以 让 使 用 键 盘 的 用 户 操 作 更 快 一 些， 在 西 文Windows 及 其 应 用 软 件 中 快 捷 键 的 使 用 大 多 是 一 致 的。

面 向 事 务 的：

  • Ctrl-D 删 除
  • Ctrl-F 寻 找
  • Ctrl-I 插 入
  • Ctrl-N 新 记 录
  • Ctrl-S 保 存

查 询/ 列 表：

  • Ctrl-O
  • Ctrl-R

其 它：

  • Ctrl-C 拷 贝
  • Ctrl-H 帮 助
  • Ctrl-P 打 印
  • Ctrl-V 粘 贴
  • Ctrl-W 关 闭
  • Ctrl-X 剪 切

MS Windows 保 留 键：

  • Ctrl-Tab 下 一 窗 口
  • Ctrl-Esc 任 务 列 表
  • Ctrl-F4 关 闭 窗 口
  • Alt-F4 结 束 应 用
  • Alt-Tab 下 一 应 用
  • Enter 缺 省 按 钮/ 确 认 操 作
  • Esc 取 消 按 钮/ 取 消 操 作
  • Shift-F1 上 下 文 相 关 帮 助

这 些 快 捷 键 也 可 以 作 为 开 发 中 文 应 用 软 件 的 标 准， 但 使 用 汉 语 拼 音 的 开 头 字 母 亦 可。

安 全 的 应 用 软 件

在 应 用 软 件 的 开 发 中， 重 要 的 一 个 因 素 是 要 给 用 户 提 供 一 个 安 全 的 工 作 环 境， 在 软 件 中 不 应 藏 有 任 何 地 雷， 在 用 户 将 要 作 破 坏 性 的 且 难 以 恢 复 的 操 作 时， 系 统 应 当 给 以 应 有 的 提 示。 用 户 只 有 充 分 相 信 软 件 是 安 全 的， 才 会 乐 于 使 用 它， 才 会 放 心 地 去 探 索 这 一 软 件 所 提 供 的 新 功 能， 才 能 更 高 效 地 使 用 它。

什 么 是 安 全 的 应 用
首 先 最 重 要 的， 但 也 是 最 不 容 易 实 现 的 是 排 除 可 能 会 使 应 用 非 正 常 中 止 的 错 误。 在 任 何 情 况 下， 最 终 用 户 都 不 应 看 到 应 用 出 现 保 护 性 错 误 而 退 出 系 统， 这 种 错 误 最 容 易 使 用 户 对 软 件 失 去 信 心。 因 为 这 意 味 着 用 户 要 中 断 思 路， 并 费 时 费 力 地 重 新 登 录， 而 且 已 进 行 的 操 作 也 会 因 没 有 存 盘 而 全 部 丢 失。 当 然 造 成 系 统 中 止 的 原 因 很 多， 可 能 是 网 络、 硬 件、 系 统 软 件 或 是PowerBuilder 本 身 的 问 题， 但 是 开 发 者 应 当 尽 量 周 全 地 考 虑 到 各 种 可 能 发 生 的 问 题， 使 出 错 的 可 能 降 至 最 小。 特 别 是 使 用PowerBuilder 开 发 的 软 件 的 确 经 常 会 出 现 保 护 性 错 误， 但 是 开 发 人 员 却 不 能 因 此 怨 天 尤 人， 而 是 要 多 做 测 试， 选 择 最 为 保 险 的 方 法 实 现 相 同 的 功 能。

此 外， 在 使 用PowerBuilder 编 程 时， 还 有 一 种 常 见 的 导 致 非 正 常 中 止 的 情 况 是 由 于 程 序 员 自 己 的 原 因 造 成 的， 这 就 是 变 量 联 接 滞 后 问 题， 即 在 编 译 时 定 义 一 个 有 效 的 对 象 类， 但 是 在 运 行 时 却 没 有 创 建 它 的 实 例 或 这 个 实 例 已 释 放（destroy）； 这 时 如 有 一 段 代 码 引 用 到 这 一 对 象， 系 统 却 无 法 在 内 存 中 找 到 它 的 实 例， 而 导 致 中 止。 因 此， 在 引 用 变 量 时， 应 当 首 先 检 查 它 是 否 存 在 实 例， 例 如 在 一 个 窗 口 中 引 用 了 另 外 一 个 窗 口 的 对 象， 就 应 首 先 检 测 它 在 执 行 代 码 之 前 是 否 已 经 创 建 了 一 个 实 例。

在 下 面 的 例 子 中，win_A 这 一 窗 口 对 象 可 能 在 代 码 执 行 时 并 没 有 创 建， 因 此 我 们 就 应 当 在 执 行 前 用IsValid 函 数 来 检 查 其 是 否 有 效：

IF IsValid(win_A) THEN
        Close(win_A)
END IF

使 用 数 据 窗 口 中 应 注 意 的 问 题
PowerBuilder 的 代 码 将 引 用 很 多 数 据 窗 口 中 的 列， 指 代 这 一 列 的 方 法 可 以 是 列 名 也 可 以 是 列 号。 列 号 是 指 这 一 个 数 据 窗 口 在 执 行Select 语 句 中 排 列 的 顺 序 号， 如 果 习 惯 于 使 用 列 号 引 用 某 列， 要 注 意 可 能 在 后 来 会 在 数 据 窗 口 的select 语 句 中 移 动 或 删 除 某 些 列， 如 果 不 能 想 起 所 有 引 用 过 这 个 数 据 窗 口 的 代 码， 这 时 就 有 可 能 出 现 数 据 类 型 不 匹 配 的 错 误。 使 用 列 名 来 引 用 则 可 以 避 免 这 类 错 误， 但 是 这 里 使 用 的 字 符 串， 在 编 译 时PowerBuilder 并 不 验 证 引 用 得 是 否 正 确， 只 有 在 运 行 时， 系 统 才 可 能 发 现 使 用 的 字 符 串 在 数 据 窗 口 中 不 存 在， 并 提 示 错 误， 退 出 应 用。 这 种 错 误 一 般 比 较 难 以 发 现， 减 小 这 种 出 错 的 方 法 是 尽 可 能 使 用Object Browses 来 剪 贴， 以 减 小 录 入 错 误。

安 全 的 环 境 还 应 当 注 意 尽 可 能 避 免 用 户 无 意 录 入 无 效 的 数 据。 在GUI 下， 有 许 多 控 件 可 以 以 十 分 友 好 的 方 式 限 制 用 户 输 入 值 的 种 类。 例 如 一 个 多 选 框 只 有 两 种 值 的 选 择， 所 以 当 用 户 作 出 选 择 的 可 能 性 只 有 两 个 时， 可 以 采 用 这 一 控 件， 免 去 了 另 外 编 程 进 行 输 入 有 效 性 验 证 的 麻 烦。 当 选 择 的 可 能 再 多 一 些 时， 可 以 采 用 单 选 钮， 每 一 种 选 择 都 是 有 效 的， 用 户 不 可 能 输 入 任 何 一 种 无 效 的 选 择。 同 理 采 用ListBox、DropDownDataWindow、DropDownListBox 等 标 准 控 件 都 可 以 有 效 地 避 免 用 户 无 效 输 入， 而 不 必 另 外 编 程 来 进 行 有 效 性 验 证， 同 时 可 避 免 用 户 在 某 一 个 输 入 点 反 复 作 输 入 尝 试 而 耽 误 很 多 时 间。

在 一 个 应 用 系 统 中， 开 发 者 应 当 避 免 用 户 作 出 未 经 授 权 或 没 有 意 义 的 操 作， 在PowerBuilder 中 菜 单 和 按 钮 等 对 象 有Enabled 和Visible 的 属 性， 因 此 对 用 户 未 授 权 或 无 法 进 行 的 操 作 应 将 相 应 对 象 的 属 性 改 为Disabled 和Invisible。 例 如 在 一 个 窗 口 中 有 一 个 数 据 输 入 窗 口 和 一 个 删 除 按 钮。 在 编 辑 一 条 新 记 录 时， 删 除 按 钮 是 无 效 的， 因 此， 这 个 按 钮 的 属 性 应 为Disable。 当 窗 口 的 状 态 发 生 变 化 时， 应 当 随 时 改 变 控 件 或 菜 单 项 的 这 两 个 属 性。

当 对 象 无 效 时， 使 用 该 控 件 的Disable 属 性 还 是Invisible 属 性 是 个 人 习 惯 问 题。 一 般 地， 在 一 个 菜 单 项 或 控 件 永 远 是 无 效 时 应 当 使 用Invisible 属 性， 例 如 一 个 用 户 打 开 一 个 有 删 除 键 的 窗 口， 但 是 这 个 用 户 没 有 删 除 权 限， 那 么 这 个 按 钮 就 应 当 是 不 可 见 的； 而 如 果 用 户 有 权 删 除， 只 是 在 当 前 状 态 下， 无 法 执 行 删 除 功 能， 如 编 辑 新 记 录， 这 时 该 按 钮 应 当 设 置 成Disable。

数 据 窗 口 是 一 个 单 一 的 封 装 的 对 象， 当 不 希 望 用 户 输 入 某 一 列 时， 不 能 将 整 个 数 据 窗 口 的 属 性 设 成Disable， 而 是 应 当 采 用 其 它 的 方 法， 一 般 来 讲， 有 以 下 三 种 方 式：

  1. 将 该 列 的Tab Order 设 为0， 这 样 用 户 将 无 法 按tab 键 或 用 鼠 标 聚 焦 在 该 列 上。 如 果 采 用 这 一 方 法， 应 当 将 其 原 有 的Tab Order 值 存 储 在 一 个 实 例 变 量 中， 以 便 在 需 要 时 恢 复。
  2. 将 列 的Display Only 或Protect 属 性 设 为 真。 这 两 个 属 性 设 为 真 时， 可 以 使 该 列 得 以 聚 焦( 只 要tab order 不 为0)， 用 户 还 可 以 在 该 列 上 选 中 一 些 文 字 及 水 平、 垂 直 滚 屏， 只 是 这 一 列 的 值 不 能 作 修 改。 如 果 数 据 库 中 的 某 列 大 篇 幅 的 多 行 文 字 只 能 显 示 而 不 能 修 改 时， 可 以 采 用 这 一 方 法， 但 应 注 意 该 列 的tab order 不 能 为0， 这 样 保 留 用 户 在 必 要 时 对 其 中 的 部 分 内 容 进 行 拷 贝 的 权 利。
  3. 将 该 列 的Visible 属 性 设 为 假。 这 样 该 列 自 动 将tab order 设 为0 而 无 法 聚 焦。 当 这 一 值 再 改 为 真 时，tab order 的 值 仍 能 恢 复。 这 一 方 法 的 缺 点 是 在 改 变 属 性 时 屏 幕 会 出 现 闪 烁， 因 此 这 一 方 法 不 能 滥 用。

将 系 统 出 现 的 错 误 提 示 信 息 尽 可 能 淡 化 处 理 是 使 得 用 户 有 安 全 感 的 重 要 因 素。 开 发 者 应 当 预 见 到 某 些 可 能 出 现 的 错 误， 尽 可 能 避 免 系 统 缺 省 的 错 误 信 息 出 现， 一 方 面 由 于 这 些 信 息 本 身 没 有 汉 化， 而 且 过 于 专 业 化， 用 户 不 易 理 解， 另 一 方 面 也 容 易 使 用 户 以 为 自 己 操 作 错 误， 造 成 不 安 全 感。 例 如 用 户 可 能 会 在 输 入 数 据 时 违 反 了 数 据 库 的 约 束 条 件， 开 发 者 应 当 在PowerBuilder 发 出SQL 语 句 向 数 据 库 提 交 前 对 此 进 行 必 要 的 有 效 性 检 验。 当 然 使 用 下 拉 列 表 框 等 方 法 可 以 实 现 外 键 约 束。 如 果 用 户 直 接 从 键 盘 输 入， 须 在 对 数 据 作update 之 前 先 验 证 其 有 效 性。 数 据 库 中 有 些 列 有 非 空 的 约 束， 如 果 用 户 对 某 些 列 还 没 有 填 入 值 时 就 提 交 数 据 库 会 也 返 回 错 误， 因 此 在 开 发 者 设 计 窗 口 时， 应 对 数 据 库 的 非 空 约 束 等 条 件 先 作 一 预 览， 不 要 让 从DBMS 发 出 的 错 误 信 息 直 接 显 示 给 用 户。 另 外 在 使 用 数 据 窗 口 前， 应 确 保 每 一 列 都 在update 选 项 中 被 选 中（ 在 数 据 窗 口 画 笔 菜 单 的Rows|Update 中）， 否 则 系 统 在DataWindow 做Update 和Insert 操 作 时， 将 不 包 括 该 列。

PowerBuilder 中， 数 据 窗 口 画 笔 里 列 的Required Field 属 性 是 一 个 界 面 很 不 友 好 的 属 性。 一 旦 聚 焦 在 该 列 上， 没 有 输 入 任 何 东 西 就 离 开 或 者 提 交 数 据 窗 口 时 该 列 没 有 值， 系 统 都 不 允 许。 即 使 不 知 该 输 入 什 么， 想 查 询 一 下 其 他 窗 口 时， 也 要 先 在 该 列 中 写 入 一 些 东 西， 才 能 关 团 这 个 窗 口， 因 此 建 议 一 般 不 采 用 这 一 属 性， 而 是 要 通 过 编 程 来 做 检 查， 可 以 把 这 段 代 码 放 在UpdateStart 事 件 中， 这 一 事 件 在 执 行update() 函 数 时 触 发。

在 用 户 要 作 出 不 易 恢 复 的 操 作 前， 应 对 用 户 作 适 当 的 提 示， 这 样 在 用 户 打 算 探 索 系 统 新 功 能 时 没 有 后 顾 之 忧。 例 如 用 户 不 经 意 下 删 除 一 些 记 录 要 恢 复 它 们 是 困 难 的， 因 此 在 用 户 删 除 前， 出 一 个 提 示 窗 口“ 您 将 删 除 下 列 记 录， 继 续 ？” 等。 在 系 统 修 改 了 记 录 却 没 有 存 盘 就 退 出 之 前， 应 在CloseQuery 事 件 提 示“ 是 否 要 保 存” 等 信 息。

在 应 用 程 序 设 计 的 全 过 程 中， 必 须 记 住 要 编 写 安 全 的 应 用 这 一 重 要 因 素， 这 样 才 能 在 该 应 用 使 用 时 少 产 生 错 误， 减 小 维 护 的 压 力， 这 些 注 意 事 项 有：

  1. 统 一 的 外 观；
  2. 引 用 对 象 前 的 有 效 性 检 验；
  3. 使 用 正 确 的 数 据 窗 口 列 名；
  4. 使 用 适 当 的GUI 控 件 来 使 得 用 户 输 入 有 效；
  5. 需 要 时 对 数 据 窗 口 的 列 作 保 护；
  6. 提 交 前 对 数 据 窗 口 列 作 约 束 检 验；
  7. 避 免 用 户 进 入 危 险 地 带；
  8. 对 用 户 不 经 意 的 操 作 作 出 提 示。

小 结

《 宋 史》 载， 岳 飞 曾 言：“ 兵 法 之 常， 运 用 之 妙， 存 乎 一 心”。 我 们 在 这 两 期 讨 论 的 界 面 设 计 的 一 些 注 意 事 项， 以 及 以 前 各 期 谈 到 的 其 他 一 些PowerBuilder 的 开 发 技 巧， 并 不 是 万 能 的 和 一 成 不 变 的， 也 并 没 有 涵 盖 开 发 的 全 部 内 容。 只 是 希 望 能 够 为 读 者 提 供 解 决 问 题 的 思 路， 使 读 者 能 够 建 立 感 觉， 演 绎 出 同 适 合 于 应 用 的、 实 用 的 技 巧 来。

本 讲 座 到 此 就 暂 告 一 段 落。 笔 者 在 历 时 一 年 的 撰 文 过 程 中， 曾 收 到 了 读 者 大 量 的 热 情 的 来 信（Email） 和 电 话， 给 予 我 鼓 励 与 支 持， 对 文 章 中 的 错 误 给 予 批 评 与 指 正， 这 些 都 成 为 了 我 坚 持 完 成 这 一 讲 座 的 最 根 本 的 动 力。 遗 憾 的 是， 限 于 时 间 和 精 力， 对 于 这 些 来 信， 我 未 能 一 一 回 复， 特 此 至 歉。 更 为 遗 憾 的 是， 限 于 版 面， 我 不 得 不 在 讲 座 中 放 弃 了 一 些 虽 然 重 要、 却 需 要 较 大 篇 幅 才 能 说 清 楚 的 专 题， 如 应 用 分 析 设 计、 团 队 开 发、 图 形 数 据 窗 口 设 计、Debug 技 巧 等 等， 今 后 将 陆 续 为 大 家 补 上。

最 后， 感 谢 原Sybase 公 司 市 场 部 总 监 檀 林 先 生 促 成 了 这 一 讲 座 的 开 办， 感 谢《 计 算 机 世 界》 李 福 顺 主 任 和 林 江 艳 小 姐 悉 心 地 安 排 和 对 文 章 精 益 求 精 地 修 改 和 校 正， 感 谢 读 者 对 本 讲 座 的 关 注 和 支 持 ！

编 后 话：
40 期 的《Power Builser 应 用 开 发 系 列 讲 座》 到 本 期 为 止 告 一 段 落， 感 谢 广 大 读 者 对 本 讲 座 的 关 注 和 支 持， 感 谢 本 讲 座 的 作 者 张 健 姿 先 生 的 辛 勤 工 作。 正 如 张 健 姿 先 生 所 言， 今 后 本 报 还 将 继 续 发 表 一 些 由 于 篇 幅 所 限 而 未 谈 透 的 问 题， 对 此， 我 们 欢 迎 读 者 提 出 意 见、 建 议 也 欢 迎 广 大 读 者 就 相 关 问 题 继 续 投 稿。

另 外， 本 讲 座 的 系 列 文 章 以 及 今 后 将 要 刊 登 的 有 关 文 章 将 在 适 当 的 时 候 汇 集 成 册 出 版， 我 们 希 望 得 到 读 者 对 此 书 的 需 求 信 息， 欢 迎 大 家 来 信 联 系。 来 信 请 寄： 北 京 万 寿 路 翠 微 中 里16 号3 号 楼4 层 计 算 机 世 界 软 件 部“PB 讲 座” 收（100036）。