构件组成
Delphi 5构件板的FastNet页中,提供了TNMSmtp构件,它的功能就在于将邮件发送到指定的邮件服务器。其最常用的属性和方法如下:
Host:SMTP邮件服务器的地址,如SMTP.SINA.COM.CN
Port:SMTP邮件服务器的端口号,一般用其默认值25即可
UserID:用户名,当与邮件服务器建立连接时,需验证用户名
PostMessage.FromAddress:发件人地址
PostMessage.ToAddress:收件人地址
PostMessage.Attachments:邮件附件的文件列表
PostMessage.Body:邮件的正文
PostMessage.Subject:邮件的主题
Connect方法:建立与SMTP服务器的连接,连接前需指定Host和UserID
Disconnect方法:断开与服务器之间的连接
SendMail方法:将当前邮件发送到服务器
当然,TNMSmtp构件还有很多其它的属性、方法、事件,在这里我们不作介绍,需要时大家可以查阅相关资料或Delphi的帮助信息。
实现方式
首先在Form1中加入相应的TLabel、TEdit、TMemo、TButton、TListBox、TOpenDialog和TNMSmtp构件。其中,TMemo用于书写邮件内容,TListBox显示附件项目,TOpenDialog(文件打开对话框)用于选择附件文件,按钮Button1添加附件,按钮Button2连接服务器并发送邮件。程序的具体代码如下:
procedure TForm1.Button1Click(Sender: TObject);
begin
if OpenDialog1.Execute then ListBox1.Items.Add(OpenDialog1.FileName);
//将选择的文件项添加到ListBox1中,作为附件
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
NMSmtp1.Host:=Edit3.Text; //SMTP服务器地址
NMSmtp1.UserID:=Edit4.Text; //你在对应服务器上的注册用户名
NMSmtp1.Connect; //与服务器建立连接
NMSmtp1.PostMessage.FromAddress:=Edit5.Text; //发件人的邮件地址
NMSmtp1.PostMessage.Subject:=Edit2.Text; //邮件主题
NMSmtp1.PostMessage.ToAddress.Add(Edit1.Text); //收件人的邮件地址
NMSmtp1.PostMessage.Attachments.AddStrings(ListBox1.Items); //附件
NMSmtp1.PostMessage.Body.Assign(Memo1.Lines); //邮件的正文
NMSmtp1.SendMail; //发送邮件
NMSmtp1.Disconnect; //断开连接
ShowMessage('发送完毕!'); //发送完时显示提示信息
end;
以作者为例:我的一个邮箱为[email]dyhuyz@163.com[/email],利用SMTP.163.com邮件服务器向《中国电脑教育报》编辑部发信时,在Edit1中填入编辑部的地址[email]soft@cce.com.cn[/email],Edit2中写上主题,Edit3中填入SMTP.163.com,Edit4中填入用户名dyhuyz,Edit5中填入我的地址[email]dyhuyz@163.com[/email],添加附件后,单击发送按钮,一切OK了!
注意事项
需要特别说明的是:目前部分网站的SMTP邮件服务器要对发送邮件的用户进行身份验证,即要求给出用户名和密码信息,否则无法发送,包括163.net,263.net,sina.com,sohu.com等均在此列。而Delphi 5的TNMSmtp构件本身未提供“密码”属性,采用以上简单方法,不能利用这些服务器发送邮件,否则会出现错误信息;Delphi 6中用Indy构件组取代了Delphi 5中的FastNet构件组,其中的SMTP包含有身份验证的相关信息,可以很方便地解决这个问题。另一部分网站,如China.com,163.com等,则不要求身份验证,利用本程序,尽可畅通无阻。
如何在Delphi中使用资源文件
——————————————————————————–
资源也是数据,它相当于我们熟悉的只读数据。在应用程序的可执行代码中,它是单独存储的,当其被调用时才载入程序,在程序执行完后又退出。Delphi中的资源有很多类型,适用于不同的地方,大致有以下几类:
●图标资源:是一种小型位图,用户常常用不同的图标代替不同的应用程序。
●光标资源:也是小型的位图,不过它适用的颜色不多。Delphi已经给光标指定了一个光标图案集,就是我们操作中常用到的Cursor属性,同时用户也可自定义光标图案。
●位图资源:Delphi只是将位图资源存入资源文件中,在使用时从资源文件中调出。
●字符串资源:将字串符文件存储于资源文件中。
本文通过实例程序来说明资源在Delphi中的用法,所有示例程序均在Delphi 4中调试通过。
1、生成资源文件
在Delphi中提供了一个图形编辑器(Image Editor),通过该编辑器可以编辑生产Bitmap、Icon和Cursor三种资源文件,也可以直接绘制ICO、CUR和BMP文件。该编辑器不能进行文字处理,具体文字处理在后面介绍。编辑图形类资源文件时,首先启动Image Editor,选择:File->New->Resource File;在弹出的窗口中用鼠键右键单击“Contents”,再弹出的菜单中单击:New;选择需要编辑的资源文件的类型(Bitmap、Icon或Cursor)。
2、装载与使用资源文件
资源文件编辑生成后(文件名后缀为.res),要使用这些资源文件,首先要通过添加代在表单文件的implementation关键字中加入:
{ *.DFM}
{资源文件名.RES}
定义了资源文件并且在单元文件中包括了资源文件名,需要调用Windows的API函数调用资源文件里的内容,如:LoadIcon,LoadString,LoadBitmap,LoadResource等。
例如:下面的语句装入了一个名为mybmp.bmp的文件:
Bmp.Handle := LoadBitmap(Hinstance , 'mybmp.bmp');
下面示例程序说明了图标、光标和位图资源的使用方法,在资源文件TEST.RES中定义了两个光标(cur1和cur2)、两个位图(bmp1和bmp2)以及两个图标(in1和in2),在程序中对这些资源都进行了调用。并利用定时器使位图和图标的显示有类似动画的感觉。当鼠标移动到Ladel1上时,光标会变成你定义的第一个光标形状;当单击Button1后,再把光标移到Ladel1上时,光标会变成你定义的第二个光标形状。
unit testtes;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Label1: TLabel;
Button1: TButton;
Image1: TImage;
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
bmp12 : Integer;
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{ *.DFM}
{ TEST.RES}
const
crMycur1 = 1;
crMycur2 = 2;
procedure TForm1.FormCreate(Sender: TObject);
var
bmp : TBitmap;
ico : TIcon;
begin
Screen.Cursors[crMycur1] := LoadCursor(Hinstance,'CUR1');
Screen.Cursors[crMycur2] := LoadCursor(Hinstance,'CUR2');
Label1.Cursor := crMycur1;
bmp := TBitmap.Create ;
bmp.Handle := LoadBitmap(Hinstance,'BMP1');
Image1.Width := bmp.Width + 10;
Image1.Height := bmp.Height + 10;
Image1.Canvas.Draw(4,8,bmp);
bmp12 := 1;
ico := TIcon.Create ;
ico.Handle := LoadIcon(Hinstance,'IN1');
Icon := ico;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Label1.Cursor := crMycur2;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
bmp:TBitmap;
ico : TIcon;
begin
bmp := TBitmap.Create ;
ico := TIcon.Create ;
if bmp12=1 then bmp12 := 2 else bmp12 :=1;
bmp.Handle := LoadBitmap(Hinstance,PChar('BMP'+IntToStr(bmp12)));
ico.Handle := LoadIcon(Hinstance,PChar('IN'+IntToStr(bmp12)));
Image1.Width := bmp.Width + 10;
Image1.Height := bmp.Height + 10;
Image1.Canvas.Draw(4,6,bmp);
Icon := ico;
end;
end.
3、字符串资源的定义与使用
字符串的存储在应用程序中是独立的,应用程序只有在使用资源时载入,使用完之后清除,从而节省内存,同时字符串也可以用于翻译,一些汉化软件都利用了字符串。编辑的字符串放在一个文本文件中,可以使用Delphi中的:File->New->Text,编辑字符串文件,字符串文件的格式如下:
stringtable
begin
1,"book"
2,"apple"
3,"desk"
4,"pen"
5,"computer"
end
编辑完字符串文件后,选择Save as,注意要将文件类型改为资源编译文件(.RC),这还不是资源文件,它还必须经过编译才能成为资源文件(.RES)。编译命令为Dos提示符下的BRCC32,其路径为:D:Program FilesBorlandDelphi4Binrcc32.exe;例如上面的字符串资源编译文件名为:StrRes.rc,在DOS提示符下输入:brcc32 mydirStrRes.rc;则编译后会生成一个名为:StrRes.res的资源文件,使用该文件即可访问字符串资源。具体使用见下例:
unit teststr;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
count : integer;
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{ *.DFM}
{ StrRes.RES}
const
wordcount = 5;
procedure TForm1.Button1Click(Sender: TObject);
var
strword : string;
begin
if count>wordcount then count := 1;
strword := LoadStr(count);
label1.Caption := strword;
count := count + 1;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
label1.Caption := LoadStr(1);
count := 2;
end;
end.
程序中常量wordcount用来记录字符串资源文件中字符串的数量,变量count用来记录显示的字符串编号。程序运行后单击Button1按钮,则将循环显示字符串资源文件中的每一个字符串。
Delphi开发用DOA运行存储过程
——————————————————————————–
用Delphi开发C/S结构的Oracle数据库软件时,为提高效率,通常将大批量的数据处理交给后台存储过程来完成。由于Delphi需通过BDE才能操作和处理各种数据库文件,这样不仅效率低,而且存在一定局限性,所以考虑采用第三方工具DOA来提高交互效率,方便前后台信息的传递。
DOA(即Direct Oracle Access的缩写)是荷兰Allround Automations公司开发的访问O
racle的工具,运用DOA构件可以在Delphi或C++Builder开发环境下跳过BDE,而直接通过SQLNet访问Oracle。初次接触DOA,一些编程人员对怎样运用DOA调用存储过程感到困惑,笔者将结合电信综合管理系统中数据加工审核这一具体实例,详细阐述其具体的方法和步骤。
实现方法
1.用TOraclePackage的CallProcedure / CallXXXFunction
用TOraclePackage的CallProcedure方法,我们就可简单地调用Oracle存储过程,该方法中参数以数组的形式传递。当TOraclePackage的ParameterMode属性为pmNamed时要按照名称传递参数,每个参数前面必须有指定名称的字符串,其格式为:
CallProcedure(ProcedureName, [ParameterName1, Parameter1, ParameterName2, Parameter2…]);
当TOraclePackage的ParameterMode属性为pmPositional时,要按照位置传递参数:
CallProcedure(ProcedureName,[Parameter1, Parameter2,…]);
输出参数通过传递parString、parInteger、parFloat、parDate或parBoolean常数来定义,输出参数值在过程调用后用GetParameter方法获得,格式为:
PackageName.CallProcedure('ProcedureName',[parString]);
GetParameter(ParameterId);
如果没有参数,则用parNone获得:
PackageName.CallProcedure('ProcedureName', parNone);
与上面类似,我们用TOraclePackage的Call…Function方法也可简单地调用Oracle函数,只是根据返回值的不同,调用相应的方法CallBooleanFunction、CallDateFunction、CallFloatFunction、CallIntegerFunction或CallStringFunction。
2.用TOracleQuery或TOracleDataSet的Execute
用TOracleQuery或TOracleDataSet执行存储过程的步骤为:
(1)设置SQL属性
将TOracleQuery或TOracleDataSet的SQL属性设为:
begin
ProcedureName (:Parameter1, : Parameter2,…);
end;
(2)定义参数
定义参数的方式有两种,一是在对象查看器Properties选项卡的Variables属性中,单击省略号按钮,打开变量属性编辑器;二是用DeclareVariable方法。DOA支持PL/SQL表,它可以作为输入/输出参数传递给后台存储过程和函数,通过一次调用即可传递大量信息,使得在C/S结构中,网络通信量显著减少。在TOracleQuery或TOracleDataSet中定义PL/SQL表既可使用变量属性编辑器(选中PL/SQL Table复选框,然后定义表的大小,如果是字符串型的,还需定义字符串大小),也可在运行时定义PL/SQL表(先调用DeclareVariable方法定义变量,再调用DimPLSQLTable方法定义表大小)。
(3)参数赋值
给输入参数赋值用SetVariable方法,给PL/SQL表赋值首先要创建数组变量,分别赋值数组的各元素,再通过该数组用SetVariable方法给PL/SQL表赋值。
ArrayName := VarArrayCreate([LowBounds, UpBounds], VarType);
Table[LowBounds] := value1;
…
Table[UpBounds] := valuen;
OracleQueryName.SetVariable(':TableName', ArrayName);
(4)执行存储过程
调用Execute方法执行存储过程,代码如下:
OracleQueryName. Execute;
(5)获得输出参数
用GetVariable方法获得输出参数值,输入/输出参数为PL/SQL表时,返回的数组变量下限与输入相同;输出参数为PL/SQL表时,返回的数组变量下限从零开始。
应用实例
以下是笔者运用上述第二种方法在电信综合统计管理系统中调用加工审核存储过程的一段源代码:
create or replace package pk_sh is
type t_object is table of varchar2(15) index by binary_integer;
type t_formula is table of number index by binary_integer;
procedure sp_audit(sh_time in varchar2,sh_dx in t_object,sh_gs in t_formula);
end pk_sh;
我们首先在服务器端创建包pk_sh,包中定义了两种PL/SQL表,其中t_object存储审核对象,t_formula存储审核公式ID,存储过程sp_audit根据传递的参数(时间、对象、公式)对后台数据进行加工审核。
客户端用Delphi编写,即通过DOA访问sp_audit,具体源代码如下:
Var
OracleSession1: TOracleSession;
OracleQuery1: TOracleQuery;
Begin
//连接数据库
OracleSession1:= TOracleSession.Create(nil);
OracleSession1.LogonDatabase := 'chicago';
OracleSession1.LogonUsername := 'scott';
OracleSession1.LogonPassword := 'tiger';
OracleSession1.Connected:= True;
OracleQuery := TOracleQuery.Create(nil);
OracleQuery1.Session := OracleSession1;
//创建数组并赋值
sh_dx:=VarArrayCreate([0, LV_object.Items.Count -1], varVariant);
for i:=0 to LV_object.Items.Count -1 do
begin
sh_dx[i] :=LV_object.Items[i].caption;
end;
sh_gs:=VarArrayCreate([0, LV_formula.Items.Count -1], varVariant);
for i:=0 to LV_formula.Items.Count -1 do
begin
sh_gs[i] :=strtoint(LV_formula.Items[i].caption);
end;
sql_str :='pk_sh.sp_audit(:sh_time,:sh_dx,:sh_gs);';
with OracleQuery1 do
begin
//设置SQL属性
Clear;
SQL.Add('begin');
SQL.Add(' ' + sql_str );
SQL.Add('end;');
//定义参数
DeleteVariables;
DeclareVariable('sh_time', otString);
DeclareVariable('sh_dx', otString);
DeclareVariable('sh_gs', otInteger);
DimPLSQLTable('sh_dx', 2000, 15);
DimPLSQLTable('sh_gs', 500, 0);
//参数赋值
SetVariable(': sh_time ', sh_time);
SetVariable(':sh_dx', sh_dx);
SetVariable(':sh_gs', sh_gs);
//执行存储过程
Execute;
Free;
end;
OracleSession1.Connected:= False;
OracleSession1.Free;
End;
以上源代码采用Delphi 5、Oracle 8开发,在Windows 98/Windows2000系统平台下调试通过。
通过以上分析可知,利用BDE访问Oracle,由于它不支持PL/SQL表,参数只能分行传递,需反复多次调用存储过程,而用DOA则使问题圆满解决。此外,将TOracleQuery的Threaded属性设置为True,就可简单地编写多线程应用程序,而将Debug属性设置为True,可在运行时显示SQL语句和变量值,以方便调试。
用Delphi开发ASP分页组件
——————————————————————————–
随着网络技术的发展和Internet的普及,Browser/Server在软件开发中已成为主流,笔者在开发一个ERP系统时,就采用了B/S软件模式,具体架构为SQL Server+IIS+IE网页采用的是Active Server Page文件。由于系统涉及大量的数据操作和查询,若纯粹采用ASP脚本语言编写势必造成效率低下,为了提高系统的整体效率和安全性,笔者采用了ASP组件来代替ASP脚本语言。
由于Delphi在开发数据库应用系统中具有的强大的功能和极高的效率,所以笔者开发ASP组件较常用的是Delphi 5.0(当然也可采用Visual Basic或VC++开发ASP组件),Delphi本身在Internet和InternetExpress两个组件面板提供了众多的组件可以直接生成Web页面,但是这些组件都缺少网页中数据显示常见的分页功能。众所周知,ASP是通过建立ADO连接数据库后建立RecordSet对象,然后利用RecordSet的AbsolutePage进行页面定位,而在Delphi 5.0中,已提供了ADO组件封装了Microsoft的ADO库,所以同样具有页面定位功能。下面笔者将分步来开发一个通用的显示分页Web页面的ASP组件。
第一步:新建一个Activex Library,命名为PadoPage,然后再新建一个Active Server Object Class,命名为AdoPage,即建立了一个名为AdoPage的ASP组件,文件命名为Adopage.pas。
第二步:打开Type Library,新建一个方法Get_Page,然后在Get_Page加入一个参数Pconnandsgl,用于传递数据库连接语句和SQL语句,参数选择为BSTR类型。
第三步:新建一个DataModule,放入Adoconnection组件和AdoQuery组件,将Data Module命名为AdoDataModule。由于新建立的组件中的方法Get_Page要从DataModule中取得数据,所以需在Adopage.pas的Uses子句中加入AdoDataModule,然后声明一个数据模块的变量fadodm,同时加入Initialize和Destroy这两个方法,以便在ASP组作中生成数据模块。Adopage.pas具体代码如下所示:
unit Adopage;
interface
uses
ComObj, SysUtils, Classes, ActiveX, AspTlb, Pbasedata_TLB, StdVcl, AdoDataModule;
//将AdoDataModule加入USE子句
type
T Adopage = class(TASPObject, Ibasedata)
private
fadodm:TAdoDataModuleform;
protected
procedure OnEndPage; safecall;
procedure OnStartPage(const AScriptingContext: IUnknown); safecall;
procedure get_page(const pconnandsql: WideString); safecall;
public
procedure initialize;override;
destructor destroy;override;
end;
implementation
uses ComServ,forms;
destructor Tadopage.destroy;
begin
inherited;
fadodm.Destroy;
end;
procedure Tadopage.initialize;
begin
inherited;
fadodm:=tadodmform.Create(forms.application);
end;
第四步:建立通用的分页显示数据的方法get_page,具体代码如下:
procedure Tadopage.get_page(const pconnandsql: WideString);
var i,j,n:integer;
connstr,sqlstr:widestring;
rs:_recordset;
cur_url:widestring;
page_no:integer;
begin
//首先从传递过来的参数中分别取出连接串和SQL语句
pconnandsql:=uppercase(pconnandsql);
i:=pos('CONNSTR',pconnandsql);
j:=pos('SQLSTR',pconnandsql);
if i=0 or j=0 then
begin
response.write('数据库连接串或SQL语句错误!');
abort;
end;
for n:=I+8 to j-1 do
connstr:=connstr+pconnandsql[n];
for n:=j+7 to length(pconnandsql) do
sqlstr:=sqlstr+pconnandsql[n];
//将取得的连接串和SQL语句分别赋给ADOconnection和ADOQuery
fadodm.adoconnection1.connstring:=connstr;
fadodm.adoquery1.sql.add(sqlstr);
//以下为打开数据库并进行分页的过程
try
fadodm.adoquery1.open;
//打开数据库
rs:=fadodm.adoquery1.recordset;
//取得当前打开页面的URL和页码
try
if request.servervariable['url'].count>0 then
cur_url:= request.servervariable.item['url'];
if request.querystring['page_no'].count>0 then
page_no:=request.querystring.item['page_no']
else
page_no:=1;
except
end;
rs.pagesize:=20;
//每页设为20行
rs.AbsolutePage:=page_no;
//页面定位
response.write('共'+inttostr(rs.pagecount)+'页& ');
response.write('第'+inttostr(page_no)+'页& ');
//对每个页码建立超链接
for i:=1 to rs.pagecount do
response.write('<a href=https://www.cnblogs.com/IceKernel/articles/"'+cur_url+'?page_no='+inttostr(i)+'">'
+inttostr(i)+'</a>');
//数据记录按表格显示
response.write('<table>');
//取得表格标题
response.write('<tr>');
for I:=0 to fadodm.adoquery1.fields.count-1 do
response.write('<td>'+fadodm.adoquery1.fields[i].fieldname+'</td>');
response.write('</tr>');
j:=1
with fadodm.adoquery1 do
while (not eof) and j<=rs.pagesize do
begin
response.write('<tr>');
//取得表格内容
for i:=1 to fields.count do
response.write('<td>'+fields[i].asstring+'</td>');
response.write('</tr>');
next;
end;
response.write('</table>');
fadodm.adoquery1.close;
except
response.write('数据出错啦!');
end;
end;
以上即为取得通用分页数据的过程,需要注意的是编译时部分函数会出错,只需在USES子句中加入sysutils、classes和adodb单元即可。
第五步:编译并注册adopage组件,即可在ASP代码中调用,调用示例如下:
<%
dim webpageobj
set webpageobj=server.createobject("padopage.adopage")
webpageobj.get_page("conn=provider=SQLOLEDB.1;presist security info=false;
user id=sa;initical catalog=sale_data;data source=(local),
sqlstr=selectfrom customer")
%>
通过以上步骤,我们就顺利地利用Delphi开发出了具有分页功能的ASP组件了。
利用Delphi实现图像的淡入淡出
——————————————————————————–
我们在浏览网页时见过不少图像淡入淡出的特技,其实,用Delphi也可以实现这样的效果。
用Delphi显示图像,有两个不可缺少的步骤,一是将图像装入Delphi隐形控件TBitmap中,二是用Canvas(画布)的Draw(x,y,Bitmap)或StretchDraw(Rect,Bitmap)方法将图像显示出来。淡出的效果就是将图像上每一个像素的颜色值进行设置,使它逐渐减少到0(黑色),实
现图像的渐渐隐去。利用Canvas的Scanline属性可读取和设置图像每一行的像素颜色,我们就是利用它来实现特技的。淡入则是将一幅图像装入两个TBitmap对象,一个用来保存原始颜色,另一个用来处理,将像素的颜色从0逐渐递增到原始图的颜色,实现淡入的效果。
准备工作:新建一个窗体并加入一个Image控件(用来显示图像特技),两个Button控件(用来切换淡入淡出)。下面我们将两个Button的Click事件源码介绍如下:
unit drdc;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Image1: TImage;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
x,y,i:integer;
Bitmap:TBitmap;
pixcolo:PByteArray;
begin
Bitmap:=TBitmap.Create;
//创建TBitMap实例
try
Bitmap.LoadFromFile
('c:windows ouds.bmp');
Bitmap.PixelFormat:=pf24bit;
for i:=0 to 255 do
begin
for y:=0 to Bitmap.Height-1 do
begin
pixcolo:=Bitmap.Scanline[y];
//扫描每行像素颜色
for x:=0 to ((Bitmap.Width3)-1) do
if pixcolo[x]>0 then pixcolo[x]:=(pixcolo[x]-1);
//递减颜色值,不同的递减值可改变不同的速度
end;
Image1.Canvas.Draw(0,0,Bitmap);
//画出图像
Application.ProcessMessages;
//系统做其他工作
end;
finally
Bitmap.free; //释放位图
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
x,y,i,j:integer;
Bitmap1,Bitmap2:TBitmap;
pixcolo1,pixcolo2:PByteArray;
begin
Bitmap1:=TBitmap.Create;
Bitmap2:=TBitmap.Create;
try
Bitmap1.LoadFromFile('c:windows ouds.bmp');
//将同一幅图像装入两个TBitmap实例
Bitmap2.LoadFromFile('c:windows ouds.bmp');
Bitmap1.pixelFormat:=pf24bit;
Bitmap2.pixelFormat:=pf24bit;
for y:=0 to Bitmap2.height-1 do
begin
pixcolo2:=Bitmap2.Scanline[y];
for x:=0 to ((Bitmap2.Width3)-1) do
pixcolo2[x]:=0;
//先将要处理的图像的像素颜色值设为0
end;
for i:=0 to 255 do
begin
for y:=0 to Bitmap2.Height-1 do
begin
pixcolo2:=Bitmap2.Scanline[y];
pixcolo1:=Bitmap1.Scanline[y];
for x:=0 to ((Bitmap2.Width3)-1) do if pixcolo2[x]<pixcolo1[x] then pixcolo2[x]:=(pixcolo2[x]+1);
end;
//与原始图的像素颜色值比较,并递增其值直到与原始图相等
Image1.Canvas.Draw(0,0,Bitmap2);
Application.ProcessMessages;
end;
finally
Bitmap1.free
end;
end;
end.
利用上面的程序,我们就在Delphi中初步实现了图像的淡入淡出效果。
看实例学Delphi编程四例
——————————————————————————–
在编程爱好者中流传着这样一句话:“业余的程序员用VB,真正的程序员用VC,聪明的程序员用Delphi”。这当然不尽贴切,但从中不难体会到Delphi作为Windows下的主要编程工具之一,其功能完善、灵活多变且易学易用的特点。
下面,我们就通过四则具体的实例来学习Delphi编程的一般思路和技巧。
将程序加入启动
Windows自启动的信息存放于注册表HKEY_LOCAL_MACHINESoftwareMicrosoftWindowsCurrentVersionRun中,Delphi为程序员提供了专用于注册表操作的类型TRegistry:首先利用它声明一个变量,变量的RootKey属性表明当前是对哪一个根键进行操作;使用变量的OpenKey方法打开子键,使用ReadString方法读子键内容,使用WriteString方法写入子键内容。可以自定义一个过程AutoRun来实现此功能,格式为:AutoRun(任意名称,要自动运行的程序名)。实例如下:
implementation
uses Registry; //声明注册表单元
procedure AutoRun(sCaption,sExeName:string); //sCaption为项目名称,sExeName为程序名
var
RegF:Tregistry; //定义变量RegF
begin
RegF:=Tregistry.Create; //创建变量
RegF.RootKey:=HKEY_LOCAL_MACHINE; //指定要操作的根键
if RegF.Openkey('SoftwareMicrosoft
WindowsCurrentVersionRun',true) then
RegF.WriteString(sCaption,sExeName);
RegF.Free; //释放变量
end;
procedure TForm1.Button1Click(Sender:
TObject);
begin
AutoRun('MyAutoRun',Paramstr(0));
//调用过程,Paramstr(0)函数返回带路径的程序名
end;
实现文件拷贝
在本例中,我们使用数据压缩的API函数LZCopy来实现一个文件拷贝的过程,其调用格式为:CopyFile(源文件名,目标文件名)。
implementation
uses LZExpand;
procedure CopyFile(sFileName,dFileName:String);
var sFile,dFile:file;
begin
AssignFile(sFile,sFileName);
Reset(sFile);
AssignFile(dFile,dFileName);
Rewrite(dFile);
LZCopy(TFileRec(sFile).Handle,TFileRec
(dFile).Handle);
CloseFile(sFile);
CloseFile(dFile);
end;
避免多次打开同一程序
Windows是一个多任务的操作系统,允许用户同时打开多个实例,但这样做有可能会造成不良后果。例如:自己设计屏幕保护程序时,只要屏保程序已经被打开运行,就不能再运行其第二个实例;如果不加以限制,则可能出现每隔一定时间就运行一个屏保程序的不正常情况。
要使系统只能打开程序的一个实例,需要对工程文件(*.dpr)进行少许改动,使用API函数FindWindow,其格式为:FindWindow(窗体类名,窗体标题),返回一个窗体的句柄或零(如果没有找到窗体)。假设程序主窗体的标题为MainForm,则工程文件的具体代码如下:
program Project1;
uses Forms,Windows, Unit1 in 'Unit1.pas' {Form1};
var Hwnd:THandle;
begin
Hwnd:=FindWindow('TForm1','MainForm'); //查找是否已有窗体MainForm
if Hwnd<>0 then begin
SetForegroundWindow(Hwnd); //激活已运行的程序实例,但程序最小化时无效
Application.Terminate; //终止本次实例
end
else begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
//运行本次实例
end;
end.
限制窗体的大小
当编程者为窗体选择可变化的边框时,用户可以按自己的意愿拖动边框,改变窗体大小。Delphi为窗体和所有控件提供了一个特殊的属性:Constraints。只需为Constraints属性的子属性设置合适的最大值与最小值,就能建立一个大小不超过指定限制的窗体或控件。我们可以将这些语句放在窗体创建事件中(FormCreate):
procedure TForm1.FormCreate(Sender: TObject);
begin
form1.Height:=200;
form1.Width:=300;
form1.Constraints.MaxHeight:=400;
form1.Constraints.MaxWidth:=600;
form1.Constraints.MinHeight:=100;
form1.Constraints.MinWidth:=150;
end;
Delphi趣味编程实例三则
——————————————————————————–
隐藏任务栏屏蔽热键
在自己的程序中将任务栏隐藏起来,并屏蔽Ctrl+Alt+Del和Alt+Tab等系统热键,也是很有趣的。要实现它们比较容易,但千万别忘了恢复正常,另外,本例中使用了3个API函数:FindWindow、ShowWindow和SystemParametersInfo,在使用它们之前一定要进行申明。
Implementation
var Hwnd:THandle;
Tmp:integer;
procedure TForm1.Button1Click(Sender: TObject);
begin
Hwnd:=FindWindow('Shell_TrayWnd',nil);
if Hwnd<>0 then ShowWindow(Hwnd,SW_HIDE); //隐藏任务栏
SystemParametersInfo(SPI_SCREEN
SAVERRUNNING,1,@Tmp,0); //屏蔽系统热键
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Hwnd:=FindWindow('Shell_TrayWnd',nil);
ShowWindow(Hwnd,SW_SHOW); //恢复任务栏
SystemParametersInfo(SPI_SCR
EENSAVERRUNNING,0,@Tmp,0);
//恢复系统热键
end;
动态调整显示器分辨率
Delphi提供了可以动态改变屏幕分辨率的函数,分别是EnumDisplaySettings()和ChangeDisplaySettings()。有了它们,编程时可以随时改变分辨率以适应要求。下面的CRTReset函数能方便实现这一功能:
implementation
function CRTReset(X, Y: Word): Boolean;
var
lpDevMode: TDeviceMode;
begin
Result:= EnumDisplaySettings(nil, 0, lpDevMode); //获取显示模式
if Result then begin
lpDevMode.dmFields := DM_PELSWID
TH Or DM_PELSHEIGHT;
lpDevMode.dmPelsWidth := X;
lpDevMode.dmPelsHeight := Y; //设置屏幕的宽度和高度
Result:= ChangeDisplaySettings(lpDevMode, 0) = DISP_CHANGE_SUCCESSFUL;
//改变屏幕分辨率并返回成功与否
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if CRTReset(800, 600) then ShowMessage('Now is 800*600'); //调用函数,设置分辨率为800×600
end;
鼠标滚轮如何编程
我们使用的鼠标,很多都带有一个滚轮,方便用户的操作。但遗憾的是,平时编程多数只利用到鼠标的左、右两键,如果将滚轮操作功能也加进你的程序,定能使其增色不少。
当鼠标指针指向窗体中时,Delphi为滚轮滚动提供OnMouseWheel事件,我们可以对它进行相应的处理。本例中,在窗体内放置一标签Label1,用于显示滚轮滚动的效果。
implementation
var i:integer;
procedure TForm1.FormCreate(Sender: TObject);
begin
i:=0;
Label1.Caption:=inttostr(i);
end;
procedure TForm1.FormMouseWheel(Sender: TObject; Shift: TShiftState;
WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
begin
if WheelDelta>0 then i:=i+1 else i:=i-1;
//wheelDelta参数表示滚动一格的值,向上滚动为正数,向下滚动则为负数
Label1.Caption:=inttostr(i);
end;
用Delphi打造RealPlayer播放器
——————————————————————————–
如今RealPlayer的流式媒体文件以其强大的视频压缩比正逐步悄然兴起。RealPlayer Plus播放器也理所当然地成为大家播放RealPlayer格式文件的首选软件。不过RealPlayer Plus也不是十全十美的,比如启动时间慢,占用空间大,留有历史记录等等。其实我们可以利用Delphi打造一个完全符合自己要求的RealPlayer播放器。下面我将向大家介绍如何利用Delphi来帮助你实现这一愿望。但前提是:你的机器中必须装有RealPlayer Plus播放器,因为我们要用到其自带的ActiveX控件。
首先,我们要在Delphi中导入所需的ActiveX控件。点击菜单“Component→Import ActiveX Control…”打开“Import ActiveX”对话框,在“Import ActiveX”列表框中,我们可以看到Windows中所有注册的AxctiveX控件。选择其中的“RealPlayer ActiveX Control Library(Version 1.0)”控件。然后单击“Install”按钮。
回到Delphi主界面,你会发现在VCL面板中的ActiveX标签中增加了一个名为RealAudio的组件。我们先把它放入窗体中。不过在默认情况中RealAudio组件没有视频播放窗口,也就是只能播放声音。这是我们不愿看到的,这时我们可以在RealAudio组件的Controls属性中添加如下代码:
IMAGEWINDOW,CONTROLPANEL,STATUSBAR
其中:IMAGEWINDOW、CONTROLPANEL、STATUSBAR分别表示显示视频播放窗口、控制条状态条。
添加代码后,我们可以发现控件外观已经改变成了视频播放窗口形状。然后设置Align属性为alClient,使播放窗口可以随窗体的变化而变化,方便大家在观看影视动画时可以随意拉动播放窗口大小。
然后,在窗中加入TMainMenu组件、TOpenDialog组件各一个。用于选择并打开播放影视文件。双击TMainMenu组件,进入菜单设计器。在这里,我们要设计一个菜单项File,及其2个子菜单Open、Exit。设计完毕后:
双击Open添加如下代码:
if OpenDialog1.Execute then
begin
RealAudio1.source:=OpenDialog1.FileName;
RealAudio1.doplay; //打开并启动播放器
End;
双击Open添加如下代码:
Application.terminate; //终止程序运行
为了在打开一个文件时,便于用户选择文件,要将OpenDialog组件的Filter属性设置如下:所有媒体文件(*.rm,*.ram,*.ra,*.swf,*.mp3)|*.rm;*.ram;*.ra;*.swf;*.mp3|*.*|*.*。
最后就可以进行编译、运行。此程序在Windows Me,Delphi 5.0的环境中调试通过。如果朋友们有什么不解之处,可与我联系:[email]tjyihui@sohu.com[/email]。
用Delphi控制Windows的关闭功能
——————————————————————————–
我们经常会遇到在安装驱动程序或应用程序时,对系统配置进行了修改而必须重新启动Windows才能使设置生效,这时往往会弹出一个提示用户是否重新启动Windows的对话框。但有时又不希望关闭Windows,例如:一个程序正在工作,数据尚未保存,而另一个程序执行了“关闭Windows”的操作(如“网络蚂蚁”的定时关机功能),就会影响用户工作甚至造成数据丢失。此时,我们可在程序中设置防止关闭Windows的代码,只要本程序在运行中关闭Windows时,都将弹出对话框要求用户确认。
对于以上功能,我们完全可以通过编程来实现,下面就以Delphi编程来完成。
关闭(或重启)Windows
要关闭Windows,可利用API函数ExitWindowsEx(),它能够实现“注销当前用户”、“关闭Windows”以及“关闭Windows并重启”等功能,具体格式和用法如下:
ExitWindowsEx(关闭类型参数,系统保留参数);
其中,系统保留参数无特定意义,一般写0即可;关闭类型可以是以下几种:
EWX_FORCE:强制关闭,Windows不会发送任何消息给正运行的程序,这可能导致数据丢失;
EWX_LOGOFF:关闭所有正在运行的程序,注销当前用户并重新登录;
EWX_POWEROFF:关闭Windows并关机,当然,系统必须支持电源管理;
EWX_REBOOT:关闭Windows并重新启动;
EWX_SHUTDOWN:关闭Windows,缓冲区内的数据将被写入磁盘。
我们来看一个实例,首先新建一窗体,在上面放置一组单选钮,命名为rgExit,共三个选项:注销当前用户并重新登录;关闭Windows并重新启动;关闭Windows。再放置两个按钮,Botton1用于确认,Botton2用于取消。代码如下:
Implementation
{ *.dfm}
Procedure tform1.button1click(sender: tobject);
Begin
case rgexit.itemindex of
0: exitwindowsex(ewx_
logoff,0); //注销当前用户并重新登录
1: exitwindowsex(ewx_reboot,0); //关闭Windows并重新启动
2: exitwindowsex(ewx_shutdown,0); //关闭Windows
end;
End;
Procedure tform1.button2click(sender: tobject);
Begin
close;
End;
防止关闭Windows
要实现“防止关闭Windows”这一功能其实很简单,只要在程序主窗体的OnCloseQuery事件中加入以下代码即可:
Procedure tform1.formclosequery(Sender: tobject; var canclose: Boolean);
Begin
If messagedlg('是否允许关闭?', mtconfirmation, mbokcancel, 0) = mrok then
Canclose := True
Else
Canclose := False;
End;
如果把CanClose设为False,表示不允许关闭Windows;如果CanClose设为True,则允许关闭。
CORBA技术及在Delphi中的实现
——————————————————————————–
一、 引言
进入90年代以来,分布式组件对象标准极大地推动了以异构环境下协同工作为目标的虚拟环境研究。当今国际上已有三大分布式组件对象标准:一个是OMG组织推出的CORBA,即公共对象请求代理结构;一个是微软公司推出的DCOM,即分布式组件对象模型;还有一个是SUN公司推出的用JAVA语言开发开发的分布对象模型RMI,即远程方法激活。在三种分布式组件标准中,CORBA标准在结构标准实现的灵活性、跨语言能力、跨平台实现及安全性等方面的综合对比中优于DCOM和RMI,所以它往往成为真正的开放式结构应用程序的首选标准。本文主要讨论CORBA技术,并探讨了在Delphi中的实现方法。
二、 CORBA及相关技术
1、 CORBA的概念和用途
CORBA是OMG(Object Management Group)提出的一个分布式对象技术的规范,它是针对多种对象系统在分布式计算环境中如何以对象方式集成而提出的,它为对象管理定义了一个对象模型-OMG参考模型(OMG reference model)及其框架结构。该模型由ORG、对象服务、公共设施、领域接口及应用对象等5个主要部分组成。该模型及其框架结构将面向对象技术与客户/服务器计算模式结合起来,有效地解决了对象封装和分布式计算环境中资源共享、代码可重用、可移植及应用间地互操作性等问题。
2、 Com与Corba的比较
COM和CORBA都提供了一种创建分布式、面向对象的结构体系的方法,即它们都提供了从另一台机器中调用一个存在于二进制可执行文件中的对象方法。COM和CORBA都有许多用于支持它们的实用程序,例如MTS、ITS、调度程序、注册表。然而,每个结构体系都有各自独特的优点。
COM作为一项以Microsoft和Windows为基础的技术,具有归并到全世界90%的台式计算机的优势,它不受约束并且拥有广泛的厂家支持。
CORBA可能是目前可真正提供全方位支持的较好系统。如,它在处理故障及加载分布方面比COM有更好的支持。当然,CORBA还是比COM更好地支持广泛的操作系统。
3、 Org
ORG(Object Request Broker)常被称作一组服务的类名称,这组服务用于连接客户和服务器,并在客户和服务器之间来回传递方法调用和信息。
因为ORG是在DLL里实现的,所以,它们驻留在服务器和客户实现的处理中,它们必须安装在所有使用CORBA的客户及服务器上。
4、 Smart Agent?
Smart Agent是一项用来帮助客户自动对一个服务器进行定位的目录服务。其宗旨在于帮助客户机连接到服务器上,并执行其他一些类似负载均衡和重启崩溃对象这样的重要任务。即:Smart Agent具有目录服务,它能查出对象的位置,并把客户程序接到这些对象上。
5、 代理、存根和框架
在一个分布式的应用程序里,客户不能直接与服务器通信,同样一个服务器也不直接与客户交流。函数的调用及其参数都必须通过网络从一个应用程序中调度到另一个应用程序里。为了使该体系工作,客户和服务器两边都要建立一个代理。客户这边的代理称为存根(Stub),而服务器的代理则叫框架(Skeleton)。
存根实现方法如下:
function TcorbaTestObjectStub.GetName:WideString;
var
OutBuf:ImarshalOutBuffer;
InBuf:ImarshalInBuffer;
Begin
Fstub.createRequest('GetName',True,OutBuf);
Fstub.Invoke(OutBuf,InBuf);
Result:=UnmarshalWideText(InBuf);
End;
其中,Fstub数据成员来自于CorbaObj单元,在CorbaObj单元里它被声明为Istub类型。Fstub.createRequest方法用一个ImarshalOutBuffer类型的变量与想调用的函数的名字进行连接,这个变量可通过Internet传输。当该包建好后,就可以调用Invoke将信息传递到服务器并从同一个服务器中得到反馈信息。ImarshalInBuffer类型的InBuf变量包含了该反馈信息,可以调用UnmershalWideText。
框架的目的在于接收由存根对Invoke的调用而传来的消息。当它收到消息后,就调用真正的GetName方法,接着把结果包装起来迅速传给客户,实现方法如下:
Procedure
TcorbaTestSkeleton.GetName(constInBuf:ImarshalInBuffer;Cookie:Pointer);
Var
OutBuf: ImarshalOutBuffer;
Retval: WideString;
Begin
Retval:=Fintf.GetName;
Fskeleton.GetReplyBuffer(Cookie,OutBuf);
OutBuf.PutWideText(PwideChar(Pointer(Retval)));
End;
该程序分两步:
第一步:是从由Delphi自动设置的内部Fintf变量调用Get Name。Fintf仅仅是一个指向对象真实接口的指针。因为现在正处于服务器本身当中,因此,这是指向你所创建的实际对象的真实指针。
第二步:是将函数结果包装起并把它送回客户端,你将通过调用GetReplyBuffer和PutWideText完成该步骤。
6、 接口定义语言
接口定义语言(IDL)用于定义对象的接口,一个对象的接口指定该对象所支持的类型和操作,因而唯一定义了可用于该对象的请求形式。所有接口均隐式从CORBA模块中定义的Object接口中导出。
接口定义语言(IDL)的重要特征是其语言无关性,这使得对象可用不同的程序设计语言构造,且依然可相互通信,CORBA规范通过精确定义基本数据类型的长度,保证了在异构硬件平台上的互操作性。
三、 Delphi4中CORBA客户和服务器对象分布式工作机理
Delphi4工具支持CORBA标准,并为开发分布式应用程序提供了优良的环境。
图中客户和服务器两边都有一个代理。在Delphi4中,真实的存根与框架由Delphi4在创建CORBA对象时自动生成,并存放于CORBA对象_TLB.pas命名的文件中。存根与框架这对代理使客户感觉到好象真的与服务器直接通信,而它们的具体实现存在于服务器中。在Delphi4中ORB就是一组动态连接库,它们提供了网络传输层以下的通信机制。图中的OsAgent不是CORBA标准中的一部分,它的宗旨在于帮助客户机连接到服务器上去,并执行其它一些类似于负载均衡和重启崩溃等重要任务。当客户程序启动时,它使用ORB与OsAgent进行通信,使客户机自动连接到服务器上去,换句话说,OsAgent有目录服务功能,它能够自动找出对象的位置。应该指出的是,在实现分布式应用程度过程中,客户、服务器双方必须都安装有OsAgent程序,同时必须在通信之前启动了OsAgent服务。
四、 Delphi中两个重要的CORBA存储库
1、 Interface Reposity
Interface Reposity是一个用来存储有关对象的具体内容的地方。该资源库有三种用途:
1)、它为用户提供了一个场所,用户可以通过它来查看有关对象的详细信息。
2)、它提供了允许CORBA对方法调用进行类型检查的机制。
3)、它允许在运行期间对一个对象进行动态调用。
2、 Implementation Reposity
Implementation Reposity用来存储有关服务器的信息,这样服务器才能自动被Smart Agent启动,尤其是Implementation Reposity含有有关服务器的名字及用来找到它的可执行程序的路径信息。
当一项叫做OAD(即Object Activation Daemon)的CORBA服务器需要启动一个服务器时,OAD可以通过查看Implementation Reposity,并执行其中的文件来给服务器定位。换句话说,Implementation Reposity中存储着服务器的路径、可执行文件名及其他一些相关的信息,如果该服务器并未运行但却已在Implementation Reposity中注册过了,那么Smart Agent可以请求OAD来启动它。
五、 在Delphi中使用CORBA
为在Delphi中使用Corba技术,首先要创建服务器和客户,然后在此基础上创建各类实用程序。服务器和客户程序创建如下:
1、 服务器的创建
创建CORBA服务器所需的步骤如下:
1)、创建一个标准的应用程序并保存到自己的目录中。在该目录下建立两个附加子目录,一个叫Client,另一个叫Server。
2)、选择New|File,转到multitier页面,选择CORBA Object。当提示为对象取个名字的时候,应叫它CorbaTestObject,而让其他所有设置都处在默认的状态。
3)、打开Type Library Edit建立一个叫GetName的方法,它是IcorbaTestObject的一部分,小心别把它加到根上(Project Name).在此处编辑器是不允许一个方法加入的。
4)、该方法的返回类型应该是一个WideString。在Parameters页面的靠近顶端处设置它,这个方法的COM IDL是:HRESULT_Stdcall GetName([out,retval])BSTR *Name).而Delphi的声明是:Function GetName:WideString[dispid$00000001];safecall;这个方法自动说明为Safecall,但这在Type Library Editor中可能不太明显。
5)、单击Refresh按钮来产生方法的代码。完成这个方法的实现代码,让它返回一个对象名字的字符串。
6)、存盘,执行一次该程序。到这一阶段,该应用程序必须提前运行,否则客户就无法访问它。
2、 客户程序的创建
建立一个客户应用程序的步骤:
1)、启动一个正规的应用程序
2)、将由服务器创建的XXX_TLB.pas文件加到主窗体的uses子句中,然后再把CornInit也加到uses子句中。
3)、创建一个按钮响应方法。
4)、从XXX_TLB.pas文件中获得CreateInstance的调用,并将它复制到按钮响应方法中,去掉无关的存储单元,保存其返回的类型。
5)、声明一个叫FcorbaTest的局部变量,它是属于对象返回的类型,在此处便是ICorbaTestObject,将从TLB中复制来的函数的返回值赋给它,将你所选择的任何字符串输送给CreateInstance.
FcorbaTest:=TcorbaTestObjectCorbaFactory.CreateInstance('CorbaTest');
6)、写一段代码从对象实例中调用方法,该对象实例是从TLB中复制的函数中返回的。
7)、确定Smart Agent已启动,然后启动服务器与客户应用程序,接着调用服务器所定义的方法(注:除非正在使用OAD,否则要明确启动服务器与客户应用程序,因为它是不会自动启动的)。
四、结束语
CORBA通过ORG机制为远程对象的激活提供了基本机制,而不管实现这些对象所使用的平台和技术,而且CORBA还为对象管理提供了一组对象服务,如名字服务、事件服务、事物处理服务及永久对象服务等。因此该技术为分布式应用的开发提供了强有力的支持,具有广泛的应用基础。
DELPHI的中文版安装程序制作大揭密
——————————————————————————–
DELPHI是目前WINDOWS95/NT环境下优秀的软件开发系统。虽然它目前还不是中文版的,但仍可以开发出完全中文版的应用程序。DELPHI附带的应用程序安装工具ISEXPRESS也是让程序员们如虎填翼,快速生成最终的应用软件的安装程序。但许多从事DELPHI软件开发的程序员可能都觉得它有一个不足,就是用ISEXPRESS工具制作的安装程序的界面是英文的,这对最终用户来说是很难接受的。勉强应付的办法是在制作安装程序时尽量减少对话框,这也难免不出英文提示。
能不能将ISEXPRESS制作的安装程序的显示全部变为中文的呢?
回答是肯定的。目前大多数软件的操作界面和信息都是写在RESOURCE(资源)中,所以我们只要把EXE文件和DLL文件中的RESOURCE摘取出来,就可以轻易的把信息翻译修改成中文。而目前不少编程系统中的资源编辑器(RESOURCE EDIT)都可以将一个已经编辑完成的EXE文件和DLL文件中的RESOURCE摘取出来让我们编辑。
修改的目标
首先让我们来分析一下ISEXPRESS制作安装程序时需要的一些必要的安装文件都是哪些。知道了哪个文件内部包含着安装时需要显示的信息,修改起来就有针对性了。对于安装时显示信息有影响的文件有以下几个:
1._SETUP.DLL
安装程序SETUP.EXE刚刚运行时使用的资源文件,包含一个对话框窗体、三组字符串和两个图标。(汉化的对话框如右所示,这是在准备建立安装程序的临时文件时显示的提示窗体)
2._ISUSR32.DLL
进行安装过程中需要显示的一些字信息符串资源。如下面的两个提示信息对话框中显示的中文信息就是从这个修改后的资源文件中取得的。
3._ISRES32.DLL
包含安装程序运行过程中使用的大部分位图、对话框、字符串、图标等资源。是进行中文化的主要部分。如下面的一个安装时的显示用户名、单位名和序列号的对话框就是修改了这个文件后显示成中文的:
4.RESDLL.DLL
这个资源文件是ISEXPRESS系统自己使用的,如果修改这个资源文件,将其中的英文替换为中文,则ISEXPRESS自己运行时,显示的就已经全是中文了。以下是修改后的RESDLL.DLL在ISX.EXE运行时出现的中文效果。
但是要注意,对于DELPHI3和DELPHI4两个不同版本的ISEXPRESS工具中,RESDLL.DLL文件的版本是有比较大的区别,不能将一个版本的文件汉化后放到另一个系统中运行,否则有些对话框不一样(如右面一个对话框只是DELPHI4中的ISEXPRESS才有的,而DELPHI3中的没有这么复杂),可能会导致系统死机。当然了,由于这个文件只是为ISEXPRESS自己使用,对于制作好的安装盘来说,它是一点作用也不起的,所以不修改它可没有关系。再者由于版本之间的差异,弄不好还会出乱子,不改它也罢。
5.UNINST.EXE
这个文件用在系统进行卸载时使用。所有的资源信息都保存在文件本身内部,如果要进行汉化,则会显示出如下的两个对话框:
修改的方法
(使用Borland C++5.02将软件中文化)
软件要求
1. Borland C++5.02 ,只要基本安装就可以了。
2. Windows95或Windows NT。
3. 您要中文化软件,此软件必须为Uni-code格式。
中文化过程
1. 摘录Resource
首先,运行Borland C++ 5.02,然后开启要编辑的文件。要注意,Viewer必须选择为Edit Resource,文件类型选择.DLL(如有必要也可选择*.EXE类型)。如右图。打开文件后,将显示此资源文件中的所有资源类型,下面是一个动态链接库形式的资源文件_ISRES32.DLL的剖析,我们可以看到这个文件中包含了BITMAP位图信息,DIALOG对话框信息,STRINGTABLE字符串列表及ICON图标和VERSIONINFO版本信息。将它们左边的加号用鼠标点一下或按"+"号键,就可以展开其中的详细条目。
2. 编辑Resouce
编辑DIALOG对话框
有两种方法编辑DIALOG对话框,可视化编辑方法和按文本方式编辑。选择DIALOG项中的某一个对话窗体,按鼠标右键,就可看到弹出菜单中的前两个编辑方法:EDIT和EDIT as Text。前一种方式是逐个修改控件,特别是它们的相对位置,就象在VB和DELPHI的可视化的环境中调整控件一样方便,这对控件的大小位置调整很管用,如果控件的英文文本改为中文后大小不合适了,使用这种方法进行大小调整是比较方便的;后者是纯文本方式,对于大量的英文内容的控件信息的修改,使用这种编辑方法是速度比较合适的。两种编辑显示如右图所示。
编辑STRINGTABLE等其它资源
STRINGTABLE字符串资源中有一种编辑方式,其它的资源的编辑方式也大体上与上在提的方法差不多,相信只要是有经验的程序员,稍加指点就会轻松完成所有中文化工作的。
使用VC++也可将软件中文化,但在使用前要注意的是,只有在WINDOWS NT下才可以将编辑过的RESOURCE存回到原本的exe 文件 (或 dll 文件),且读取时要以Resource资源文件的形式打开,在此不做多述。
Delphi控件的使用经验
——————————————————————————–
一.Delphi中树型控件的使用技巧
我们都知道,开发者主要用Delphi来开发数据库管理软件,正因如此,树型控件的使用最好与数据库联系起来。Delphi提供了一个树型控件TTreeView,可以用来描述复杂的层次关系。
1.树节点信息的存储和加载
常用的方法是用树控件的 LoadFromFile和SavetoFile方法,来实现树控件和文件之间的交互;或用Assign方法实现树控件和DBMemo,也就是和数据库间的交互。该方法的优点是编程相对简单,缺点是树控件的实际节点数可能会很大,对于“大树”,每次加载和存储的数据量会加大,将降低速度,增大系统开销,造成数据冗余。另一种方法,就是只在树上产生“看得见”的节点,没有专门记录全部树节点结构的文件或数据库字段,而将树节点结构分散在数据库的每一个记录中。
具体方法是:创建一个数据库,字段根据实际业务而定,其中必然有一个字段的信息将在树型控件的节点上显示,另外还要一个字段来保存节点的惟一标识号,该标识号由长度相等的两部分组成,前段表示当前节点的父节点号,后段表示当前节点的节点号,此标识号相当于一个“链表”,记录了树上节点的结构。该方法的优点:用户操作“大树”时,一般不会展开所有的节点,而只用到有限的一部分,同时只能从树根一层一层地展开,该法只在树上产生“看得见”的节点,所以,存储和加载“大树”的速度快,数据量小,系统开销和数据冗余较小。缺点:编程较复杂,但可以结合该方法编成一个新的树控件,将大大提高编程效率。值得注意的是,ID号必须惟一,所以在编程中如何合理产生ID尤为重要。
2.数据库结构示例
创建一个数据库,为简化程序,我只创建两个数据库字段,定义如下:
字段名 类型 长度
Text C 10
LongID C 6
LongID字段实际上由两段组成,每一段3位,LongID只能表示1000条记录。将LongID定义为索引字段,存为c: esttree ree.dbf。编辑该DBF文件,新建一条记录,Text字段设为TOP,LongID字段设为“000”(3个“0”前为三个空格)。
3.创建演示程序
在Form1上放置TreeView1、Table1、PopupMenu1、Edit1、Edit2。TreeView1的PopupMenu属性设为PopupMenu1;Table1的DataBaseName属性设为c: esttree,TableName属性设为tree.dbf,IndexFieldNames属性设为LongID;为PopupMenu1加选单项Add1和Del1,Caption分别为Add和Del;Edit1用来输入新节点的Text属性值,Edit2用来输入新节点的3位ID号。存为c: esttree reeunit.pas和c: esttree esttree.dpr。
在treeunit.pas的Type关键字后加入一行:Pstr:^string;{Pstr为字符串指针}
为Form1的OnCreate事件添加代码:
procedure TForm1.FormCreate(Sender: TObject);
var p:Pstr;Node:TTreeNode;
begin
with Table1,Treeview1 do
begin
open;
first;
new(p);{为指针p分配内存}
p^:=FieldByName(′LongID′).AsString;
Node:=Items.AddChildObject(nil,FieldByName(′Text′).AsString,p);
if HasSubInDbf(Node) then Items.AddChildObject(Node,′ ′,nil);{有子节点则加一个空子节点}
end;
end;
HasSubInDbf为自定义函数,自变量为Node,检查节点Node有无子节点,有则返回True,反之返回False,并在TForm1的类定义里加入原型声明(其它自定义函数的原型也在TForm1的类定义里声明,不另作解释),函数代码如下:
function TForm1.HasSubInDbf(Node:TTreeNode):Boolean;
begin
with Table1 do
begin
Table1.FindNearest([copy(Pstr(Node.Data)^,4,3)+′000′]);
result:=copy(FieldByName(′LongID′).AsString,1,3)=copy(Pstr(Node.Data)^,4,3);{如数据库里当前记录的LongID字段内容的前3位和节点Node的Data的后3位相同,则Node应该有子节点}
end;
end;
为TreeView1控件的OnDeletion事件添加代码,需要指出的是,不仅调用Delete方法可以触发OnDeletion事件,而且当树控件本身被释放前,也触发OnDeletion事件,所以,在此处加入dispose(node.data)会很“安全”:
procedure TForm1.TreeView1Deletion(Sender: TObject; Node: TTreeNode);
begin
Dispose(Node.Data);{释放节点数据内存}
end;
为Add1选单项的OnClick事件添加代码如下:
procedure TForm1.Add1Click(Sender: TObject);
var p:pstr;Tmpstr:string;i:integer;
begin
try
StrToInt(Edit2.Text);
Tmpstr:=Edit2.Text;{注:在实用中,必须用更好的方法来产生ID}
except;
ShowMessage(′重新输入Edit2的内容′);
abort;
end;
with TreeView1 do
begin
new(p);
p^:=copy(Pstr(Selected.Data)^,4,3)+TmpStr;
Items.AddChildObject(Selected,Edit1.Text,p);
end;
with Table1 do{ 在数据库里添加记录 }
begin
Append;
FieldByName(′Text′).AsString:=Edit1.text;
FieldByName(′LongID′).AsString:=p^;
Post;
end;
TmpStr:=inttostr(strtoint(TmpStr)+1);
for i:=length(TmpStr) to 2 do TmpStr:=′0′+TmpStr;
Edit2.Text:=TmpStr;
end;
为Del1菜单项的OnClick事件添加代码如下:
procedure TForm1.Del1Click(Sender: TObject);
var DelList:TStringList;LongID,NSubLongID:string;
begin
DelList:=TStringList.create;
DelList.Sorted:=True;
DelList.Add(Pstr(TreeView1.Selected.Data)^);
while DelList.Count>0 do
begin
LongID:=DelList.Strings[0];
DelList.Delete(0);
Table1.SetKey;
Table1.FieldByName(′LongID′).AsString:=LongID;
if Table1.GotoKey then Table1.Delete;
if HasSubInDbf(TreeView1.Selected) then
begin
NSubLongID:=Table1.FieldByName(′LongID′).AsString;
while (copy(NSubLongID,1,3)=copy(LongID,4,3))and(not Table1.Eof) do
begin
dellist.Add(NSubLongId);
Table1.Next;
NSubLongId:=Table1.FieldByName(′LongID′).AsString;
end;
end;
end;
DelList.Free;
TreeView1.Items.Delete(TreeView1.Selected);
end;
为TreeView1的OnExpanding事件添加代码:
procedure TForm1.TreeView1Expanding(Sender: TObject; Node: TTreeNode;
var AllowExpansion: Boolean);
var TmpNode:TTreeNode;NSubLongID:String;p:Pstr;bm:TBookMark;
begin
with Table1,TreeView1 do
begin
Items.BeginUpdate;
SetKey;
FieldByName(′LongID′).AsString:=Pstr(Node.Data)^;
if not GotoKey then Items.Delete(Node)
else
begin
TmpNode:=Node.GetFirstChild;
if (TmpNode.Text=′ ′)and(TmpNode.Data=nil) then
begin
TmpNode.Delete;
if HasSubInDbf(Node) then
begin
NSubLongID:=FieldByName(′LongID′).AsString;
while (copy(NSubLongID,1,3)=copy(Pstr(Node.Data)^,4,3))and(not Eof) do
begin
new(p);
p^:=FieldByName(′LongID′).AsString;
bm:=GetBookMark;
TmpNode:=Items.AddChildObject(Node,FieldByName(′Text′).AsString,p);
if HasSubInDbf(TmpNode) then Items.AddChildObject(TmpNode,′ ′,nil);
GotoBookMark(bm);
FreeBookMark(bm);
Next;
NSubLongId:=FieldByName(′LongID′).AsString;
end; end; end;
end;
Items.EndUpdate;
end;
end;
以上简要谈了谈数据库的树状显示的基本方法,另外,编辑树上节点的Text属性的同时对数据库进行修改、同一数据库在多用户同时操作时数据库以及树的一致性、树上节点的拷贝与复制等就不再赘述,读者可自行完善。
二.IP控件的使用
在网络程序中,我们常常碰到需要用户输入IP地址的情况。然而Delphi并没有为我们提供可以用于输入IP串的控件,这样我们只好用Tedit控件(单行文本框)来接受用户输入的IP串。但是,使用Tedit来输入IP串并不是一个好主意,因为处理起来非常不便。事实上,在我们的身旁有一个专门用来输入IP串的Windows控件。IP控件会拒绝非法的IP串(在每个部分只能输入0..255之间的数字);它让你可以轻松地获取控件中的IP串所对应的IP值(32位整数),这省去了IP串和IP值之间相互转换的麻烦;此外,你还能限制IP控件中所能输入的IP的范围。本节向大家介绍如何在我们的Delphi程序中使用Windows的IP控件。
Windows中有两个非常重要的动态联结库:commctrl.dll和comctl32.dll,它们是Windows的自定义控制库(Windows Common Controls)。自定义控制库中包含了许多常用的Windows控件,如Statusbar,Coolbar,HotKey等;在Delphi中,这些控件大多数都已被包装成可视化控件了。在Microsoft推出Internet Explorer 3之后,自定义控制库中新增了一些控件,其中就包括Windows的IP控件(IP Address edit control)。
1. 初始化Windows自定义控制库
Windows提供了两个API函数,InitCommonControls和InitCommonControlsEx,用来初始化自定义控制库。从名字我们不难看出这两个API函数的关系:后者是前者的增强。如果你希望在程序中使用IP控件,你必须用InitCommonControlsEx来完成对自定义控制库以及类的初始化。函数InitCommonControlsEx的原型如下(Pascal语法):
… …
创建IP控件
… …
使用IP控件。 在程序中,我们通过向IP控件发送消息来与它通讯。IP控件可以响应的消息有以下6个,这些消息及它们的含义,见下表:
… …
若想要获取IP控件中IP串所对应的IP值,你应该向IP控件发送IPM_GETADDRESS消息,并且需要把一个32位整数的地址作为SendMessage的最后一个参数。
… …
2. IP控件的通知消息
当IP串被改动后或者输入焦点发生了转移,IP控件就会向它的父窗口发送通知消息IPN_FIELDCHANGED。在大多数情况下,我们都可以忽略此通知消息。以下是处理通知消息IPN_FIELDCHANGED的一个示例:
procedure Tform1.WndProc(var Msg: TMessage);
var p:PNMHDR;
begin
inherited;
if Msg.Msg=WM_NOTIFY
then begin
p:=Pointer(Msg.lParam);
if p^.code=IPN_FIELDCHANGED
then begin
{…
处理IP控件的IPN_FIELDCHANGED通知消息
…}
end;
end;
end;
三.动态生成控件的方法及应用
1.Delphi中生成控件的两种方法
(1). Form(表单)设计中生成控件
在进行Form设计时,直接在控件工具箱选择所需控件,再设置其属性与响应事件,这种方法比较常见。
(2).程序中动态生成控件
有时候,我们需要在程序运行时动态生成控件,这样做有两大优点:一是可以增加程序的灵活性;二是如果生成控件的多少与程序中间运行结果相关,显然方法一是无法的实现的,必须用程序中动态生成方法。
程序中动态生成控件的方法分为三步,首先,定义生成的控件类型,再用Create函数生成控件,最后对控件的相关属性赋值。以TButton控件为例,步骤如下:
a. 定义控件类型
var
Button1:TButton;
b.生成控件
Button1:=TButton. Create(self);
Button1.Parent:=Self;
//一般将其父控件设置为Self,如果不设置Parent的值,
则控件不会在屏幕
//显示出来
c.设置其它属性及定义相关事件响应函数,如Caption,Left,Top,Height,Width,Visible,Enabled,Hint和onClick事件响应函数等。
2.动态生成控件方法的应用
在开发生产调度与管理系统中,需要动态生成排产计划图,以甘特图表示,应用Shape控件来显示零件的加工状况(每道工序的加工开始时间与结束时间)是非常适合的。应用Chart控件,对加工设备利用率以三维直方图显示,非常直观。现分别将在程序中动态生成Shape控件和Chart控件的过程加以说明。
(1).动态生成Shape控件显示排产计划图(甘特图)
procedure TCreateMultiCharts.ProcCreateCharts;
var
i,j,Rows,Columns,RowSpace,ChartsHeight:Integer;
ShapeChart:array of array of TShape;
begin
Rows:=16; //Shape控件数组行数
Columns:=8; // Shape控件数组列数
RowSpace:=20; // Shape控件行间距
ChartsHeight:=20; // Shape控件高度
SetLength(ShapeChart,Rows,Columns);
//设置ShapeChart数组大小
for i:=0 to Rows do
for j:=0 to Columns do
begin
ShapeChart[i][j]:=TShape.Create(self);
with ShapeChart[i,j] do
begin
Parent:=Self; //此行必不可少,
否则Shape控件在屏幕显示不出
Shape:=stRectangle; // Shape控件形状为矩形
Top:=45+i*(RowSpace+ChartsHeight);
Left:=Round(180+Q[i,j].StartTime);
//因Q[i,j].StartTime为实数,故需进行四舍五入取整
Width:=Round(Q[i,j].Value)
Height:=ChartsHeight;
Brush.Color:=RandomColor;
//自定义函数,说明附后
Brush.Style:=bsSolid; //设置填充方式
Enabled:=True;
end;
end;
end;
注:
a.Q为一记录型二维数组,定义如下:
type
TempData=Record
Value:Real;
StartTime:Real;
end;
Q:array of array of TempData
并且在另一过程已对Q的分量进行赋值。
b.为了区分不同的零件,Shape以不同颜色显示,此时,调用了函数RandomColor。该函数为:
function TCreateMultiCharts.RandomColor;
var
red,green,blue:byte;
begin
red:=random(255);
green:=random(255);
blue:=random(255);
result:=red or (green shl 8) or (blue shl 16);
end;
(2).动态生成Charts控件的ChartSeries组件,显示设备利用率
procedure TFormMultiMachinesBurthen.
ShowMachineBurthenCharts;
var
i:Integer;
Burthen:Real;
SeriesClass:TChartSeriesClass;
NewSeries:array of TChartSeries;
begin
SetLength(NewSeries,CreateMultiCharts.Rows);
MachinesBurthenCharts.height:=200;
MachinesBurthenCharts.Width:=550;
for i:=0 to CreateMultiCharts.Rows do
begin
SeriesClass:=TBarSeries; //设置形状为三维条形图
NewSeries[i]:=SeriesClass.Create(Self);
NewSeries[i].ParentChart:=MachinesBurthenCharts;
NewSeries[i].Clear;
Burthen:=MachineBurthen[i];
Burthen:=Round(Burthen*100)/100; //只取小数点后两位数字
NewSeries[i].add(Burthen,'',NewSeries[i].SeriesColor);
end;
end;
注:
(a).MachineBurthen[i]为一实型数组,其值为对应设备的利用率,已在另一函数中计算得到;
(b). MachinesBurthenCharts为TChart控件,在type段说明。
3.程序运行结果显示
(1).动态生成Shape控件,显示零件排产计划图(略)
(2).动态生成Chart控件的ChartSeries组件,显示设备利用率(略)








