-
c语言入门之C++Builder使用经验谈
C++Builder3.0是Borland公司(现已更名为Insprise)于1998年推出的新一代基于C语言的RAD开发工具。C++Builder3.0的问世,对广大爱好C语言的用户来说不啻是个福音。因为以往在Windows下,没有一种真正基于C语言的可视化编程语言。你如果想用VB或Delphi这一类可视化编程语言去编程,你就不得不去重温一遍Basic或Pascal语言,没有了像C语言一样可以灵活应用的指针,没有了"++"、"――"这样一类可爱的运算,总之一切使用起来都不如C语言一样得心应手。现在这一切烦恼都不复存在了。C++Builder3.0不仅支持传统的C语言,也支持Borland的OWL和Microsoft的MFC。可以这样说,C++Builder3.0是目前Windows下功能最为强大的C语言编译器。由于C++Builder3.0问世不久,有关资料不是很多,下面结合笔者的使用情况,谈谈几点经验、体会。
一、动态调用窗体Form
在缺省情况下,由File/NewForm生成添加入项目文件中的窗体都具有"AutoCreate"(自动创建)的特性。即只要程序运行,该窗体就存在于内存中了,不管当前它是否被调用。具有这种特性的窗体一般适用于窗体属性比较固定、经常被调用的情况。其优点是速度快,缺点是占用内存。在实际程序设计中,会遇见大量类似对话框功能的窗体,它们用于显示状态或输入信息,仅须在程序中调用一下,完成其功能就行了,无需常驻内存。这时可以通过选择Project/Options/Forms,将"Auto--Createforms"栏中相应的窗体,如Form1,用">"键移动到"Availableforms"栏中,并在程序需调用该窗体处,加入下列语句:
TForm1*myform=newTForm1(this);
myform->ShowModal();
deletemyform;
窗体Form1仅是在需要调用时才调入内存,调用完成后,即用delete清除出内存。这样可减少程序对内存资源的占用。
二、遍历窗体控件的方法
要访问或修改窗体上的控件,方法很简单,以TEdit为例子:
Edit1->Text="";
Edit2->Text="";
但如果窗体上有十来个像Edit1这样的控件,需要进行相同的初始化,用上面的方法一个一个地进行,岂不麻烦!所以有必要掌握遍历窗体控件的方法。在介绍该方法之前,让我们先了解一下窗体Form的Components和Controls属性。参见表一。
表一
属性类型说明
ComponentCountInt目前Form上各类
控件的总数
ComponentsTCompont*目前Form上指向
所有控件的数组
ControlCountInt目前Form上某一子
区域上各类控件的总数
ControlsTControl*目前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上,与不需要初始化的控件区分开来,这样便于编程。
AnsiStringnamestring="TEdit";
for(inti=1;iControlCount;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__fastcallTForm1::
FormKeyPress(TObject*Sender,char&Key)
{
if(Key==VK_RETURN)
{
SendMessage(this->Handle,WM_NEXTDLGCTL,0,0);
Key=0;
}
}
四、为TStringGrid的文字加上颜色
----TStringGrid是C++Builder提供给用户的一种字符网格控件。美中不足的是,它没有提供分别修改各单元字体颜色、大小的方法。其实要为TStringGrid实现这样功能,只需在程序中稍加处理就行了。方法是自定义一个二维数组cellbuf,它的下标与网格单元列行一一对应,用于存放各网格单元的颜色、文字等信息。
structCellStru
{
AnsiStringmsg;//文字信息
TColorcolor;//文字颜色
};
CellStrucellbuf[MAXCOL][MAXROW];
----初始化cellbuf后,再在字符网格控件StringGrid1的OnDrawCell响应事件中,加入如下的代码即可。
void__fastcallTForm1::StringGrid1DrawCell
(TObject*Sender,intCol,
intRow,TRect&Rect,TGridDrawStateState)
{
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"应用程序存在,若存在,则退出程序的运行。该功能可防止程序的再次运行。在某些场合这样设计是必须的。
WINAPIWinMain(HINSTANCE,HINSTANCE,LPSTR,int)
{
try
{
if(FindWindow(NULL,"Demo")!=0)
{
Application->MessageBox
("程序已经运行!","警告",MB_ICONSTOP);
return0;
}
TSplashForm*splash=newTSplashForm(Application);
splash->Show();
splash->Update();
Application->Initialize();
Application->CreateForm(__classid(TForm1),&Form1);
splash->Close();
deletesplash;
Application->Run();
}
catch(Exception&exception)
{
Application->ShowException(&exception);
}
return0;
}
六、如何永久清除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环境下,BorlandC++仅支持16位应用程序的端口操作,对32位应用程序的端口操作不再支持,而C++Builder开发出来的程序是32位的。我个人以为,这是C++Builder设计者的败笔。因为PC机中,I/O地址空间与内存地址空间从来都是各自独立的。看看Delphi,不就通过Port数组实现了对I/O端口的访问了吗?搞不清楚为什么C++Builder就没有提供类似的机制?下面这几个函数是笔者从网上淘下来的,经过验证,在Windows95环境下,的确可实现对I/O端口的读写。读者可以借鉴使用。
voidoutportb(unsignedshort
intport,unsignedcharvalue)
{
//movedx,*(&port);
__emit__(0x8b,0x95,&port);
//moval,*(&value);
__emit__(0x8a,0x85,&value);
//outdx,al;
__emit__(0x66,0xee);
}
voidoutportw(unsignedshort
intport,unsignedshortintvalue)
{
//movedx,*(&port);
__emit__(0x8b,0x95,&port);
//movax,*(&value);
__emit__(0x66,0x8b,0x85,&value);
//outdx,ax;
__emit__(0xef);
}
unsignedcharinportb(unsignedshortintport)
{
unsignedcharvalue;
//movedx,*(&port);
__emit__(0x8b,0x95,&port);
//inal,dx;
__emit__(0x66,0xec);
//mov*(&value),al;
__emit__(0x88,0x85,&value);
returnvalue;
}
unsignedshortintinportw(unsignedshortintport)
{
unsignedshortintvalue;
//movedx,*(&port);
__emit__(0x8b,0x95,&port);
//inax,dx
__emit__(0xed);
//mov*(&value),ax
__emit__(0x66,0x89,0x85,&value);
returnvalue;
}
八、软件的分发
在Windows下开发的应用程序一般都比较庞大,程序的运行往往离不开一大堆不知名的系统DLL文件。为了生成能脱离C++Builder环境、独立运行的应用程序,读者须对编译器进行一定的设置。方法是:置Project/Option/Packages/Runwithruntimepackages为Disable,置Project/Option/Linker/UsesdynamicRTL为Disable,重新编译一遍程序,这样生成的EXE文件就可以脱离C++Builder环境运行了。但如果你的程序中应用了数据库,仅有上述的操作是不够的--因为,你还得安装BDE(BorlandDatabaseEngineer)。BDE的安装比较麻烦,读者最好是用C++Builder3.0附带的InstallShieldExpress来制作安装盘,把应用程序和BDE打包在一起。如果找不到,也可用Delphi3.0附带的InstallShieldExpress来制作。InstallShield的使用方法,限于篇幅,不再介绍。有条件的读者可上网查到有关资料。
一、动态调用窗体Form
在缺省情况下,由File/NewForm生成添加入项目文件中的窗体都具有"AutoCreate"(自动创建)的特性。即只要程序运行,该窗体就存在于内存中了,不管当前它是否被调用。具有这种特性的窗体一般适用于窗体属性比较固定、经常被调用的情况。其优点是速度快,缺点是占用内存。在实际程序设计中,会遇见大量类似对话框功能的窗体,它们用于显示状态或输入信息,仅须在程序中调用一下,完成其功能就行了,无需常驻内存。这时可以通过选择Project/Options/Forms,将"Auto--Createforms"栏中相应的窗体,如Form1,用">"键移动到"Availableforms"栏中,并在程序需调用该窗体处,加入下列语句:
TForm1*myform=newTForm1(this);
myform->ShowModal();
deletemyform;
窗体Form1仅是在需要调用时才调入内存,调用完成后,即用delete清除出内存。这样可减少程序对内存资源的占用。
二、遍历窗体控件的方法
要访问或修改窗体上的控件,方法很简单,以TEdit为例子:
Edit1->Text="";
Edit2->Text="";
但如果窗体上有十来个像Edit1这样的控件,需要进行相同的初始化,用上面的方法一个一个地进行,岂不麻烦!所以有必要掌握遍历窗体控件的方法。在介绍该方法之前,让我们先了解一下窗体Form的Components和Controls属性。参见表一。
表一
属性类型说明
ComponentCountInt目前Form上各类
控件的总数
ComponentsTCompont*目前Form上指向
所有控件的数组
ControlCountInt目前Form上某一子
区域上各类控件的总数
ControlsTControl*目前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上,与不需要初始化的控件区分开来,这样便于编程。
AnsiStringnamestring="TEdit";
for(inti=1;iControlCount;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__fastcallTForm1::
FormKeyPress(TObject*Sender,char&Key)
{
if(Key==VK_RETURN)
{
SendMessage(this->Handle,WM_NEXTDLGCTL,0,0);
Key=0;
}
}
四、为TStringGrid的文字加上颜色
----TStringGrid是C++Builder提供给用户的一种字符网格控件。美中不足的是,它没有提供分别修改各单元字体颜色、大小的方法。其实要为TStringGrid实现这样功能,只需在程序中稍加处理就行了。方法是自定义一个二维数组cellbuf,它的下标与网格单元列行一一对应,用于存放各网格单元的颜色、文字等信息。
structCellStru
{
AnsiStringmsg;//文字信息
TColorcolor;//文字颜色
};
CellStrucellbuf[MAXCOL][MAXROW];
----初始化cellbuf后,再在字符网格控件StringGrid1的OnDrawCell响应事件中,加入如下的代码即可。
void__fastcallTForm1::StringGrid1DrawCell
(TObject*Sender,intCol,
intRow,TRect&Rect,TGridDrawStateState)
{
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"应用程序存在,若存在,则退出程序的运行。该功能可防止程序的再次运行。在某些场合这样设计是必须的。
WINAPIWinMain(HINSTANCE,HINSTANCE,LPSTR,int)
{
try
{
if(FindWindow(NULL,"Demo")!=0)
{
Application->MessageBox
("程序已经运行!","警告",MB_ICONSTOP);
return0;
}
TSplashForm*splash=newTSplashForm(Application);
splash->Show();
splash->Update();
Application->Initialize();
Application->CreateForm(__classid(TForm1),&Form1);
splash->Close();
deletesplash;
Application->Run();
}
catch(Exception&exception)
{
Application->ShowException(&exception);
}
return0;
}
六、如何永久清除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环境下,BorlandC++仅支持16位应用程序的端口操作,对32位应用程序的端口操作不再支持,而C++Builder开发出来的程序是32位的。我个人以为,这是C++Builder设计者的败笔。因为PC机中,I/O地址空间与内存地址空间从来都是各自独立的。看看Delphi,不就通过Port数组实现了对I/O端口的访问了吗?搞不清楚为什么C++Builder就没有提供类似的机制?下面这几个函数是笔者从网上淘下来的,经过验证,在Windows95环境下,的确可实现对I/O端口的读写。读者可以借鉴使用。
voidoutportb(unsignedshort
intport,unsignedcharvalue)
{
//movedx,*(&port);
__emit__(0x8b,0x95,&port);
//moval,*(&value);
__emit__(0x8a,0x85,&value);
//outdx,al;
__emit__(0x66,0xee);
}
voidoutportw(unsignedshort
intport,unsignedshortintvalue)
{
//movedx,*(&port);
__emit__(0x8b,0x95,&port);
//movax,*(&value);
__emit__(0x66,0x8b,0x85,&value);
//outdx,ax;
__emit__(0xef);
}
unsignedcharinportb(unsignedshortintport)
{
unsignedcharvalue;
//movedx,*(&port);
__emit__(0x8b,0x95,&port);
//inal,dx;
__emit__(0x66,0xec);
//mov*(&value),al;
__emit__(0x88,0x85,&value);
returnvalue;
}
unsignedshortintinportw(unsignedshortintport)
{
unsignedshortintvalue;
//movedx,*(&port);
__emit__(0x8b,0x95,&port);
//inax,dx
__emit__(0xed);
//mov*(&value),ax
__emit__(0x66,0x89,0x85,&value);
returnvalue;
}
八、软件的分发
在Windows下开发的应用程序一般都比较庞大,程序的运行往往离不开一大堆不知名的系统DLL文件。为了生成能脱离C++Builder环境、独立运行的应用程序,读者须对编译器进行一定的设置。方法是:置Project/Option/Packages/Runwithruntimepackages为Disable,置Project/Option/Linker/UsesdynamicRTL为Disable,重新编译一遍程序,这样生成的EXE文件就可以脱离C++Builder环境运行了。但如果你的程序中应用了数据库,仅有上述的操作是不够的--因为,你还得安装BDE(BorlandDatabaseEngineer)。BDE的安装比较麻烦,读者最好是用C++Builder3.0附带的InstallShieldExpress来制作安装盘,把应用程序和BDE打包在一起。如果找不到,也可用Delphi3.0附带的InstallShieldExpress来制作。InstallShield的使用方法,限于篇幅,不再介绍。有条件的读者可上网查到有关资料。
最新更新
Objective-C语法之代码块(block)的使用
VB.NET eBook
Add-in and Automation Development In VB.NET 2003 (F
Add-in and Automation Development In VB.NET 2003 (8
Add-in and Automation Development in VB.NET 2003 (6
Add-in and Automation Development In VB.NET 2003 (5
AddIn Automation Development In VB.NET 2003 (4)
AddIn And Automation Development In VB.NET 2003 (2)
Addin and Automation Development In VB.NET 2003 (3)
AddIn And Automation Development In VB.NET 2003 (1)
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
SQL Server -- 解决存储过程传入参数作为s
武装你的WEBAPI-OData入门
武装你的WEBAPI-OData便捷查询
武装你的WEBAPI-OData分页查询
武装你的WEBAPI-OData资源更新Delta
5. 武装你的WEBAPI-OData使用Endpoint 05-09
武装你的WEBAPI-OData之API版本管理
武装你的WEBAPI-OData常见问题
武装你的WEBAPI-OData聚合查询
OData WebAPI实践-OData与EDM
OData WebAPI实践-Non-EDM模式