📄 lc_bcb_19.txt
字号:
C++Builder3.0使用经验谈
C++Builder3.0使用经验谈
作者
钟伟
工作单位 成都电子研究所
- C++ Builder 3.0 是Borland 公 司( 现 已 更 名
为Insprise) 于1998 年 推 出 的 新 一 代 基 于C 语 言 的RAD 开 发 工
具。C++ Builder 3.0 的 问 世, 对 广 大 爱 好C 语 言 的 用 户 来 说 不
啻 是 个 福 音。 因 为 以 往 在Windows 下, 没 有 一 种 真 正 基 于C
语 言 的 可 视 化 编 程 语 言。 你 如 果 想 用VB 或Delphi 这 一 类 可
视 化 编 程 语 言 去 编 程, 你 就 不 得 不 去 重 温 一 遍Basic 或Pascal
语 言, 没 有 了 像C 语 言 一 样 可 以 灵 活 应 用 的 指 针, 没 有
了" + +"、"――" 这 样 一 类 可 爱 的 运 算, 总
之 一 切 使 用 起 来 都 不 如C 语 言 一 样 得 心 应 手。 现 在 这
一 切 烦 恼 都 不 复 存 在 了。C++ Builder 3.0 不 仅 支 持 传 统 的C 语
言, 也 支 持Borland 的OWL 和Microsoft 的MFC。 可 以 这 样 说,C++
Builder3.0 是 目 前Windows 下 功 能 最 为 强 大 的C 语 言 编 译 器。 由
于C++ Builder 3.0 问 世 不 久, 有 关 资 料 不 是 很 多, 下 面 结 合
笔 者 的 使 用 情 况, 谈 谈 几 点 经 验、 体 会。
---- 一、 动 态 调 用 窗 体Form
---- 在 缺 省 情 况 下, 由File/New Form 生 成
添 加 入 项 目 文 件 中 的 窗 体 都 具 有"Auto Create"( 自 动
创 建) 的 特 性。 即 只 要 程 序 运 行, 该 窗 体 就 存 在 于 内
存 中 了, 不 管 当 前 它 是 否 被 调 用。 具 有 这 种 特 性 的 窗
体 一 般 适 用 于 窗 体 属 性 比 较 固 定、 经 常 被 调 用 的 情
况。 其 优 点 是 速 度 快, 缺 点 是 占 用 内 存。 在 实 际 程 序
设 计 中, 会 遇 见 大 量 类 似 对 话 框 功 能 的 窗 体, 它 们 用
于 显 示 状 态 或 输 入 信 息, 仅 须 在 程 序 中 调 用 一 下, 完
成 其 功 能 就 行 了, 无 需 常 驻 内 存。 这 时 可 以 通 过 选 择Project/Options/Forms,
将"Auto--Create forms " 栏 中 相 应 的 窗 体, 如Form1, 用"
>" 键 移 动 到 "Available forms" 栏 中, 并 在 程 序 需 调
用 该 窗 体 处, 加 入 下 列 语 句:
TForm1 *myform=new TForm1(this);
myform- >ShowModal();
delete myform;
---- 窗 体Form1 仅 是 在 需 要 调 用 时 才 调
入 内 存, 调 用 完 成 后, 即 用delete 清 除 出 内 存。 这 样 可 减
少 程 序 对 内 存 资 源 的 占 用。
---- 二、 遍 历 窗 体 控 件 的 方 法
---- 要 访 问 或 修 改 窗 体 上 的 控 件,
方 法 很 简 单, 以TEdit 为 例 子:
---- Edit1- >Text="";
---- Edit2- >Text="";
---- 但 如 果 窗 体 上 有 十 来 个 像Edit1 这
样 的 控 件, 需 要 进 行 相 同 的 初 始 化, 用 上 面 的 方 法 一
个 一 个 地 进 行, 岂 不 麻 烦 ! 所 以 有 必 要 掌 握 遍 历 窗 体
控 件 的 方 法。 在 介 绍 该 方 法 之 前, 让 我 们 先 了 解 一 下
窗 体Form 的Components 和Controls 属 性。 参 见 表 一。
表 一
属性
类型
说明
ComponentCount
Int
目前Form上各类
控件的总数
Components
TCompont*
目前Form上指向
所有控件的数组
ControlCount
Int
目前Form上某一子
区域上各类控件的总数
Controls TControl*
目前Form上指向某一子
区域上所有控件的数组
---- 以 图 一 为 例( 图 略) 说 明,Form1 的ComponentCount=6,
而Panel1 的ControlCount=4.,
---- 其 中: 数 组 对 象
Components[0] Panel1
Components[1] Label1
Components[2] Edit1
Components[3] Label2
Components[4] Edit2
Components[5] Button1
数 组 对 象
Controls[0] Label1
Controls[1] Edit1
Controls[2] Label2
Controls[3] Edit2
---- 下 面 这 段 代 码 完 成 了 对Panel1 上 所
有TEdit 控 件 的 遍 历 初 始 化。 读 者 稍 加 修 改, 即 可 对 其 它
控 件 进 行 遍 历。 这 里 有 一 个 小 技 巧, 我 们 把 需 要 进 行
初 始 化 的 控 件 放 置 在 了 一Panel1 上, 与 不 需 要 初 始 化 的
控 件 区 分 开 来, 这 样 便 于 编 程。
AnsiString namestring="TEdit";
for(int i=1;i ControlCount;i++)
{
if(Panel1- > Controls[i]- >
ClassNameIs(namestring))
{
TEdit *p=dynamic_cast
(Panel1- >Controls[i]);
P- >Text="";
}
}
---- 三、 用Enter 键 控 制 焦 点 切 换 的
方 法
---- 在Windows 环 境 下, 要 使 一 个 控 件 取
得 焦 点, 可 在 该 控 件 上 用 鼠 标 单 击 一 下, 或 按Tab 键 将
焦 点 移 至 该 控 件 上。 这 种 控 制 焦 点 切 换 的 方 法 有 时 不
符 合 用 户 的 习 惯。 就 图 一 而 言, 用 户 就 希 望 用Enter 键,
控 制 焦 点 由Edit1 切 换 到Edit2。 要 实 现 这 样 的 功 能 需 借 助WinAPI
函 数SendMessage 来 完 成。 方 法 是: 先 设Form1 的KeyPreview 属 性 为true,
然 后 在Form1 的OnKeyPress 事 件 中 加 入 如 下 的 代 码。 这 样, 用
户 就 可 以 通 过 按Enter, 键 控 制 焦 点 按 定 义 好 的Taborder 顺
序 来 移 动 了 !
void __fastcall TForm1::
FormKeyPress(TObject *Sender, char &Key)
{
if(Key==VK_RETURN)
{
SendMessage(this- >Handle,WM_NEXTDLGCTL,0,0);
Key=0;
}
}
---- 四、 为TStringGrid 的 文 字 加 上 颜 色
---- TStringGrid 是C++ Builder 提 供 给 用 户 的
一 种 字 符 网 格 控 件。 美 中 不 足 的 是, 它 没 有 提 供 分 别
修 改 各 单 元 字 体 颜 色、 大 小 的 方 法。 其 实 要 为TStringGrid
实 现 这 样 功 能, 只 需 在 程 序 中 稍 加 处 理 就 行 了。 方 法
是 自 定 义 一 个 二 维 数 组cellbuf, 它 的 下 标 与 网 格 单 元 列
行 一 一 对 应, 用 于 存 放 各 网 格 单 元 的 颜 色、 文 字 等 信
息。
struct CellStru
{
AnsiString msg; // 文 字 信 息
TColor color; // 文 字 颜 色
};
CellStru cellbuf[MAXCOL][MAXROW];
---- 初 始 化cellbuf 后, 再 在 字 符 网 格 控
件StringGrid1 的OnDrawCell 响 应 事 件 中, 加 入 如 下 的 代 码 即 可。
void __fastcall TForm1::StringGrid1DrawCell
(TObject *Sender, int Col,
int Row, TRect &Rect, TGridDrawState State)
{
StringGrid1- >Canvas- >Font- >
Color=cellbuf[Col][Row].color;
StringGrid1- >Canvas- >TextOut(Rect.Left+3,
Rect.Top+3,cellbuf[Col][Row].msg);
}
---- 五、 软 件 封 面 的 实 现
---- 现 代 软 件 设 计 的 流 行 做 法 是,
在 程 序 运 行 完 成 初 始 化 之 前, 先 调 用 一 幅 画 面 做 为 封
面, 通 常 是1/4 屏 幕 大 小, 显 示 一 下 软 件 的 名 称、 作 者、
版 本 等 信 息。 要 用C++ Builder 实 现 这 样 的 功 能, 方 法 很 简
单: ① 自 定 义 一 窗 体 类TSplashForm, 将 其 设 置 成" 透 明 窗
口", 即BorderIcons 下 的 所 有 选 项 均 置 成false,BorderStyle=bsNone,FormStyle=fsStayOnTop,Position=poScreenCenter;
② 在TSplashForm 窗 体 上 放 置 一TPanel( 相 当 于 图 形 的 镜 框);
③ 在TPanel 上 放 置 一TImage 控 件, 调 入 所 需 要 的 图 形; ④ 对WinMain
函 数 稍 加 修 改, 加 入 如 下 所 示 代 码 即 可。 需 要 指 出 的
是, 这 段 代 码 通 过 函 数FindWindow, 搜 索 内 存 中 是 否 有 窗
口 标 题 为"Demo" 应 用 程 序 存 在, 若 存 在, 则 退 出 程
序 的 运 行。 该 功 能 可 防 止 程 序 的 再 次 运 行。 在 某 些 场
合 这 样 设 计 是 必 须 的。
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
try
{
if(FindWindow(NULL,"Demo")!=0)
{
Application- >MessageBox
(" 程 序 已 经 运 行!"," 警 告",MB_ICONSTOP);
return 0;
}
TSplashForm *splash=new TSplashForm(Application);
splash- >Show();
splash- >Update();
Application- >Initialize();
Application- >CreateForm(__classid(TForm1), &Form1);
splash- >Close();
delete splash;
Application- >Run();
}
catch (Exception &exception)
{
Application- >ShowException(&exception);
}
return 0;
}
---- 六、 如 何 永 久 清 除DBF 中 的 已 被
删 除 的 记 录
---- 用table- >Delete() 删 除 的DBF 记 录, 并
没 有 真 正 从DBF 数 据 库 中 被 删 除, 而 仅 仅 是 做 上 了 一 个
删 除 标 记。 如 何 实 现 类 似dBase 中 的Pack 命 令 的 功 能 呢 ?
请 看 下 面 的 代 码。
table- >Close();
for(;;)
try
{
table- >Exclusive=true;
table- >Open();
break;
}
catch(...)
{
}
if(DbiPackTable(table- >DBHandle,table- >
Handle,NULL,szDBASE,true)!=DBIERR_NONE)
Application- >MessageBox(" 不 能 删 除 记 录",
" 错 误",
MB_ICONSTOP);
---- 七、I/O 端 口 读 写 的 实 现
---- 细 心 的 读 者 会 发 现,C++ Builder 不 再
支 持 如inportb()、outportb() 一 类I/O 端 口 读 写 指 令 了。 准 确 地
说, 在Windows 环 境 下,Borland C++ 仅 支 持16 位 应 用 程 序 的 端 口
操 作, 对32 位 应 用 程 序 的 端 口 操 作 不 再 支 持, 而C++ Builder
开 发 出 来 的 程 序 是32 位 的。 我 个 人 以 为, 这 是C++ Builder 设
计 者 的 败 笔。 因 为PC 机 中,I/O 地 址 空 间 与 内 存 地 址 空 间
从 来 都 是 各 自 独 立 的。 看 看Delphi, 不 就 通 过Port 数 组 实
现 了 对I/O 端 口 的 访 问 了 吗 ? 搞 不 清 楚 为 什 么C++ Builder 就
没 有 提 供 类 似 的 机 制 ? 下 面 这 几 个 函 数 是 笔 者 从 网
上 淘 下 来 的, 经 过 验 证, 在Windows95 环 境 下, 的 确 可 实 现
对I/O 端 口 的 读 写。 读 者 可 以 借 鉴 使 用。
void outportb(unsigned short
int port, unsigned char value)
{
// mov edx, *(&port);
__emit__(0x8b, 0x95, &port);
// mov al, *(&value);
__emit__(0x8a, 0x85, &value);
// out dx, al;
__emit__(0x66, 0xee);
}
void outportw(unsigned short
int port, unsigned short int value)
{
// mov edx, *(&port);
__emit__(0x8b, 0x95, &port);
// mov ax, *(&value);
__emit__(0x66, 0x8b, 0x85, &value);
// out dx, ax;
__emit__(0xef);
}
unsigned char inportb(unsigned short int port)
{
unsigned char value;
// mov edx, *(&port);
__emit__(0x8b, 0x95, &port);
// in al, dx;
__emit__(0x66, 0xec);
// mov *(&value), al;
__emit__(0x88, 0x85, &value);
return value;
}
unsigned short int inportw(unsigned short int port)
{
unsigned short int value;
// mov edx, *(&port);
__emit__(0x8b, 0x95, &port);
// in ax, dx
__emit__(0xed);
// mov *(&value), ax
__emit__(0x66, 0x89, 0x85, &value);
return value;
}
---- 八、 软 件 的 分 发
---- 在Windows 下 开 发 的 应 用 程 序 一 般
都 比 较 庞 大, 程 序 的 运 行 往 往 离 不 开 一 大 堆 不 知 名 的
系 统DLL 文 件。 为 了 生 成 能 脱 离C++ Builder 环 境、 独 立 运 行
的 应 用 程 序, 读 者 须 对 编 译 器 进 行 一 定 的 设 置。 方 法
是: 置Project/Option/Packages/Run with runtime packages 为Disable, 置Project/Option/Linker/Uses
dynamic RTL 为Disable, 重 新 编 译 一 遍 程 序, 这 样 生 成 的EXE 文 件
就 可 以 脱 离C++ Builder 环 境 运 行 了。 但 如 果 你 的 程 序 中 应
用 了 数 据 库, 仅 有 上 述 的 操 作 是 不 够 的-- 因 为, 你 还
得 安 装BDE(Borland Database Engineer)。BDE 的 安 装 比 较 麻 烦, 读 者 最
好 是 用C++ Builder3.0 附 带 的InstallShield Express 来 制 作 安 装 盘, 把
应 用 程 序 和BDE 打 包 在 一 起。 如 果 找 不 到, 也 可 用Delphi3.0
附 带 的InstallShield Express 来 制 作。InstallShield 的 使 用 方 法, 限
于 篇 幅, 不 再 介 绍。 有 条 件 的 读 者 可 上 网 查 到 有 关 资
料。
返回