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 的 使 用 方 法, 限 于 篇 幅, 不 再 介 绍。 有 条 件 的 读 者 可 上 网 查 到 有 关 资 料。

返回