第十三章 编写及调用动态链接库(dll)
什么是动态连接库:以前在dos环境下开发软件,你必须利用链接器把所开发的应用程序模块结全起来,产生所谓的exe文件。这样你就可以在dos下直接运行这个可执行文件。而以有的那些模块就可以不要了,原因在于这些模块已经也exe文件结合在一起去了。这种在编译时就把应用程序所需的模块的全部注入到exe文件的方法叫静态链接。
你会发现静态链接会有一些缺点:
A:产生的exe文件会随便着应用程序的规模而越来越大。因为应用程序所需要的模块会变多。而这些模快被加入到exe文件中,相应的应用程序的运行效率也会越来越差,因为exe文件需要很多内存。
B:如果有多个exe应用程序运行,则在内存中会有多个相同的数据模块,很显然非常浪费内存空间。
为了解决上述的问题。windows就提出了dll的概念。所谓dll是把一些经常要共享的数据制作成dll文件,
当exe文件调用到dll文件内包含的数据时,则会把该dll文件调入到内存中去,不用时就从内存中放出来。特别的如果说你的windows系统内同时运行好几个exe文件,而这引exe文件都要用到dll中的数据,则windows不会在内存中发多个dll实例。它只会保留一份在内存中,也就是说多个exe文件共享同一个dll中的数据。很显然dll文件并不是在编译时与exe文件结合的。而是在exe文件在运行时根据程序需要来与exe结合的。这就是为什么我们开发的windows软件如果要发布必须打包的原因。因为如果你仅仅复制exe文件到另外一台计算机中的话,如果这个exe文件在运行时要用到某个dll,而你要没有把dll复制到这台计算机上,则exe文件会出错。
如何来编写:dll
基本上dll文件分成有窗体和无窗体的。其实编写dll与编写exe没有什么区别。只是要导出一些函数而已
A:新建一个无窗体的dll。
B:新建一个有窗体的dll.
******创建dll文件的方法:
1:利用向导dll winard
2:声明dll 文件的格式
function sum(a,b:integer):integer;exports;
bengin
result:=a b;
end;
exports --这里表示要输出函数的值,一定不能少
sum;
3:保存dll文件
4:新建一个应用程序,并在implementation的前面声明如下
function sum(a,b:integer):integer far;external'Project2.dll'
在这里Project2.dll是保存后的dll文件名
5:把做好的dll文件复制到该应用程序的工程文件下面,并编译应用程序
6:打包的时候.dll文件会自动加载在包中
第十四章 编写线程应用程序:
A:什么是线程:
每一个应用程序都对应一个进程。而一个进程可以分成很多线程,在一个进程里面有一个主要的线程称为主线程。当然一个进程里面也可以有多个线程。通过线程技术可以使不同的代码在同一时间运行。比如当你去做一个查询时,如果查询的数据量非常大,则你必须要等查询做完之后才可以做下面的操作,如果你用线程来实现就可以达到让查询在后台运行,而你可以做其它的事件。delphi把有关线程的对像封装在类ttheread里面。它是一个抽像类.综合上所述如果你要实现多任务的话,你必须要使用线程。
c:线程中的方法:
A:execute方法。是指定当线程运行时,去执行的代码。
B:create方法。用来创建一个线程实例,当它创建时就立即会去执行线程的execute方法。
C:freeonteminted:用来保证进程做完之后delphi会自动放进程.放在execute方法里面设置
D:Terminate:强制终止线程.
E:可以通过调用OnTerminate:=过程名。可以控制当线程终止之后的操作。
而过程名通常是一有对像的方法名。比如:procedure TForm1.wc(Sender: TObject);
B:与vcl同步:
vcl中大多数组件都没有提供多线程共访的机制,这是意味着,如果你要在多个线程中去访问同一个vcl组件,则必须要把它放到主线程中。这就是线程同步。我们只需要在线程的exec方法通过过Synchronize方法则可以实现与主线程同步
线程的应用
1:在图形中的应用
2: 在数据库的应用
第十五章 windows的消息
什么是消息:消息就是指windows发出一个通知告诉应用程序某个事件发生了。例如:单击鼠标,按下键盘,移动窗体,都会使用windows向应用程序发一个消息。消息本身是作为一条记录给应用程序的.这个记录中需要指有四个参数:
A:第一个参数是窗口或控制的句柄.指明这个消息是向谁发出的。
B:第二个参数就是一些消息常量。事实在messages单元中定义了很多消息常量。每一个常量都代表一个消息。比如:wm_mousemove代表鼠标移动。wm_keydown:代表键被按下等。
C:消息的第一个附加参数。有些消息仅凭消息常量是不够的,还需要一些附加信息作说明。如果不需要则为0
例如: 对于wm_keydown这个消息而言,则知道键被按下是不够的。还要知道是什么键被按下,这个时候就是 指明第一个附加参数
D:消息的第二个附加参数。同上。一般为零。
delphi中消息的机制:
一个消息从发生到被窗口或组件响应经历了如下几个阶段:
1:系统中发生了某个事件:如用户单击了按钮,或鼠标或键盘被按下了
2:由键盘或鼠标的驱动程序将用户的操作翻译成消息,由于消息很多,所以windows会为每一个运行在其中的应 用程序创建一个消息队列
3:接着application通过ProcessMessages方法从消息队列中取得一个消息。发送给某个窗口,
窗口可以是任何类型的屏幕对象,因为Win32能够维护大多数可视对象的句柄(窗口、对话框、按钮、编辑框等)。
4:在类tcontrl中有一个函数:WndProc;它就是窗口处理过程函数。由它来接受消息,并进行相应的处理。
5:在wndproc中会去调用dispatch这个函数去把消息分配给具体的窗口。
6:窗口(控件)在接受到消息后,会根据消息编号去调用自己的消息句柄处理方法,如果在该处理方法中有inherited关键字,则还会处理父类所对应的消息处理句柄。如果自己和父类没有对应的消息处理句柄则会交给Defaulthandler方法去执行默认的处理过程.Defaulthandler方法是定义于Tobject中的虚拟方法,其声明如下:procedure Defaulthandler(var Message);virtual;Tobject类中的Defaulthandler方法只是实现简单的返回而不对消息进行任何的处理.我们可以通过对此虚拟方法的重载,在子类中实现对消息的缺省处理.
总结:
事件--windows把事件翻译成消息放入消息对列----ProcessMessages-------WndProc----Dispatch----Handle(消息处理句柄)或Defaulthandler(没有消息处理句柄)
消息处理:
消息处理是指当应用程序收到消息时,做出相应的操作。vcl里面封装了很多消息处理函数。并把它做成供用户编写的事件。所以我们可以把事件看成是当组件收到消息时的处理过程。当然用户也可以自己定义消息处理过程.要定义消息处理过程(也叫消息句柄)必须用如下的格式:
procedure 过程名(var message:tmessage);message 消息;
习惯上把过程名写成消息名并省略"_ "线 比如:
procedure wmmove(var message:tmessage);message wm_move;
这样当窗口移动时,应用程序就能接收到一个wm_move消息。然后就会去处理这个消息过程。
注意:
1:消息类型不一定都是tmessage,还可以是别的消息类型比如:TWMCommand,TWMClose等。但是 tmessage类型是通用的消息类型。相当于变量里面的variant类型一样。
2:消息句柄方法可以看成是一个当某个事件发生时,接到某个消息时,自动执行的过程
消息句柄方法需要注意几点:
1:消息句柄方法不能重载,它一般声明在构件的protected或private区
2:如果子类与父类都有同一个消息句柄。当子类的某个事件发生时,只会执行子类的消息句柄不会执行父类的消息句柄,除非子类中有inherited指令字。如果子类中没有对应的句柄时,才会去执行父类的句柄。也就是说:如果父类有那个方法则使用自己的,没有则使用父类的。
向窗体或组件发送消息。
窗体或组件的某个事件一般而言都是由用户进行某个操需要人为的给某个组件发送一个消息。让这些事件提前触发比如:不需要单击按钮就可以去执行onclik事件。通过
perform方法向组件发一个消息。格式是:
组件名.Perform(消息,附加参数1, 附加参数二)
举列:
for i:=0 to form1.ComponentCount-1 do
if form1.Components[i] is tbutton then
(form1.Components[i] as tbutton).Perform(BM_CLICK,0,0);
再如:
self.Perform(WM_CLOSE,0,0);向窗体发一个关闭消息,则窗体会自动关闭.
vcl中消息的分类:
1:windows消息。是以wm开头的。是windows发给应用程序的消息。比如:
WM_MOVE:当一个窗口被移动时,windows会向应用程序发送些消息
WM_SIZE:改变大小
WM_CLOSE:窗口关闭时
WM_KEYDOWN:按下一个键
WM_KEYUP:松开一个键
WM_MOUSemOVE:鼠标移动
wm_WM_SYSCOMMAND:
当用户选择窗口菜单的一条命令或当用户选择最大化或最小化时那个窗口会收到此消息
2:组件消息。是应用程序发给vcl的组件的。
A:公共的组件的消息:是以cm开头的。定义于constrls单元中的
CM_LOSTFOCUS,CM_FONTCHANGED,CM_MOUSEENTER,CM_MOUSELEAVE
B:独有的:如按钮,列表框,组合框,编辑框等。在messsage中的声明
3:通知消息:
通知消息(Notification message)是指这样一种消息,一个窗口内的子控件发生了一些事情,需要通
知父窗口。通知消息只适用于标准的窗口控件如按钮、列表框、组合框、编辑框等.相当于下级部门 在遇到突发事件时要通知上级部门一样.单击或双击一个控件、在控件中选择部分文本、操作控件的
滚动条都会产生通知消息。
A:公共的组件的通知消息:是以CN开头的定义于constrls单元中的
B:独有的:如按钮,列表框,组合框,编辑框等。在messsage中的声明.
注意:当一个窗口内的子控件发生了一些事件,需要通知父窗口。这样父类就会收到一条 WM_COMMAND消息.所以如果父类有对应的句柄则会去执行该句柄.如果用户有特殊需要可以override祖先类tconstrl中定义的procedure WMCommand(var Message: TWMCommand); message WM_COMMAND的方法。而且在里面有一个消息类型twmcommand它是tmessage的一种形式。它里面有一个NotifyCode字段,返回子控件通知给父窗口的通知消息的索引号
举列:所有的按钮的单击事件都不可用。
自己定义消息:
我们也可以根据需要自己定义消息:自己定义消息要分成以下几个步骤:
A:定义一个消息常量:一般要是WM_USER或之后的。因为从这之后是delphi留给用户使用的
比如: const wm_chancolor=WM_USER 120
B:定义一个消息处理函数并实现细节
比如:
procedure wm_chancolor(var message:tmessage);message wm_chancolor;
C:通过perform方法向该组件发送这个消息。
消息句柄的在子类的重定义
为什么要重定义子类的消息句柄:
1:首先要明白,如果有一个事件在子控件中发生了,则子控件会收到对应的消息。此时,如果子控件没有对应的消息句柄,delphi就会去执行父类中对应的消息句柄。但是用户可能觉得父类的消息处理不太满意,这个时候,用户就可以在子控件中定义对应的消息处理句柄,来让子控件按照自己的需要进行处理。
2:虽然用户可以在子控件中定义自己的消息处理句柄,但是并不是北完全放弃父类的处理。用户可以在子控件的消息句柄中加上inherited关键字,来先让子控件处理,之后再交给父类处理。如果子控件没有加上inherited,则相当于只执行子控件的处理方法,这就是我们所说的消息屏蔽.
打个比方:员工张三接到一个任务,以前张三总是交给上级部门来完成,不过,上级部门的结果总让他不太满意,所以张三有两种选择:
1:先自己做让它不满意的那个部分,之后再交给上级部门来做,相当于在子控件中用inhreited
2:完全自己做,相当于在子控件不用inherited;
怎么样在子控件中重定义父类的消息句柄
直接在子控件中protected或private区中声明一个种父类一样的消息句柄就行了(不能使用override)
举例:
1:在用户单击了最大化或最小化或关闭按钮时来执行某个代码。
2:当用户单击窗体的任何按钮时,去执行某个代码
3:让窗口的颜色改变时,做某个事件。CMColorChanged
过滤消息
在一定情况下,用户可能需要屏蔽某些消息.或者截获某些消息进行处理被称为消息的过滤
在delphi中要截获一个消息的方法有三种.第一种
1:通过重载wndproc窗口处理过程。根据以上知识可知,该过程是在窗口收到该消息时,还没有级时分配和处理时发生的,也就是说,可以在任何消息还没有分发之前截获
2:通过定义消息句柄。[在消息经过处理将到达控件之前截获]
A:通过windproc来屏蔽 [屏蔽鼠标在窗口上的任何操作和系统按钮]
if (Message.Msg>=WM_MOUSEFIRST) and (Message.Msg <= WM_MOUSELAST) or (message.Msg=wm_syscommand) then
self.Caption:='wc'
else
inherited;
B:通过消息句柄:
application的onmessage
应用程序的onmessage事件会在应用程序收到消息时触会此事件。我们可以编写一个实验来说明app是否真会随着用户的不同操作会收到不同的消息。
举列:
1:在附加选项卡上选择:ApplicationEvents组件。在其中的onmessage事件过程中写入下面的代码:
self.Caption:='$' inttohex(msg.message,32);
消息与事件的关系:
A:事件过程指针:
1:这个指针指向特定的一个过程。
2:当消息产生时,会去执行对应的消息句柄。在消息句柄里去执行对应的事件过程指针
3:由过程指针找到它所指向的过程,并执行它
比如:
fmove:tnotifyevent;定义一个事件过程指针
procedure wmmousemove(var message:tmessage);message wm_lbuttondown;
procedure wc(sender:tobject);
procedure TForm1.FormCreate(Sender: TObject);[在窗体创建时,把fmove指向过程wc]
begin
self.fmove:=wc;
end;
procedure TForm1.wmmousemove(var message: tmessage);
[当事件发生了,form1收到此消息,会去执行消息句柄。在里面执行fmove事件过程指针]
begin
self.fmove(self);
end;
说明:
用户单击
1:用户单击:产生消息CN_COMMAND给父类tbutton
2:tbutton去执行对应的消息句柄。
3:在该句柄中去调用click过程
4:click过程中去执行FOnClick事件过程指针,而该指针指向用户编写的事件过程.
5:执行用户编写的事件过程.