《第10章其他编程》由会员分享,可在线阅读,更多相关《第10章其他编程(110页珍藏版)》请在金锄头文库上搜索。
1、第第10章章 其他编程其他编程Delphi实用教程实用教程第10章其他编程10.1 图形图像处理图形图像处理10.1.1画布的通用属性和方法画布的通用属性和方法 1.TCanvas Object(画布对象)(画布对象)常用方法及功能:(1)Arc方法 语法:语法:Arc(x1,y1,x2,y2,x3,y3,x4,y4:Integer)Arc方法在椭圆上画一段弧,椭圆由(x1,y1)、(x2,y2)两点所确定的椭圆所决定。弧的起点是椭圆圆周和椭圆中心与(x3,y3)连线的交点。弧矩形终点是椭圆周和椭圆中心与(x4,y4)连线的交点,以逆时针方向画弧。例如,以下的程序将在窗口中将在窗口中绘制一个圆
2、弧:procedureTForm1.FormPaint(Sender:TObject);varR:TRect;beginR:=GetClientRect;Canvas.Arc(R.Left,R.Top,R.Right,R.Bottom,R.Right,R.Top,R.Left,R.Top);end;第10章其他编程10.1.1画布的通用属性和方法画布的通用属性和方法(2)Chord方法语法:语法:Chord(x1,y1,x2,y2,x3,y3,x4,y4:Integer)Chord方法连接椭圆上的两点,椭圆由(x1,y1)、(x2,y2)两点所确定的矩形决定,(x3,y3)是始点,(x4,y4
3、)是终点。例如,在Form1窗体上放置一个按钮“连接椭圆上的两点”,双击该按钮,输入下面的代码:procedureTform1.Button1Click(Sender:TObject);varR:TRect;beginR:=GetClientRect; Get coordinates of the currentwindow.Canvas.Chord(R.Left,R.Top,R.Right,R.Bottom,R.Right,R.Top,R.Left,R.Top);end;第10章其他编程10.1.1画布的通用属性和方法画布的通用属性和方法Canvas类的Chord方法示例第10章其他编程10
4、.1.1画布的通用属性和方法画布的通用属性和方法(3)Brushcopy方法 语法:语法:Brushcopy(const Dest: TRect;Bitmap: Tbitmap;constSourceTRect;Color:Tcolor)Brushcopy方法把位图的一部分复制到画布的某个矩形区域,并用画笔的当前颜色替换位图的颜色。Dest定义画布的一个矩形区域,该矩形用以填充位图,Bitmap定义位图;Source定义位图中的矩形区域,该区域上的位图将被复制;Color定义画笔中,用以替换位图的颜色。第10章其他编程10.1.1画布的通用属性和方法画布的通用属性和方法(4)CopyRect方
5、法 语法:语法:CopyRect(Dest:TRect;Canvas:TCanvas;SourceTRect)此方法从另一个画布对象上复制部分图像到该画布。Canvas表示复制画布,Source是源画布上要复制的图像区域,Dest表示目标画布上将接受复制图像的矩形区域。第10章其他编程10.1.1画布的通用属性和方法画布的通用属性和方法(5)Draw方法 语法:语法:Draw(x,y:Integer;Graphic:Tgraphic)此方法在画布给定的像素点坐标(x,y)处画由参数Graphic所给的图像,该图像可以是位图、图标或元位图。Tcanvas的其他方法列于表中。第10章其他编程10.
6、1.1画布的通用属性和方法画布的通用属性和方法2. Tpen Object(画笔对象)画笔对象)用于在画布上绘制各种线段,该对象常用的属性有:lColor:定义笔的颜色;lWidth:定义线段宽度;lStyle:定义线段的各种类型,如表所示;第10章其他编程10.1.1画布的通用属性和方法画布的通用属性和方法Mode:定义线段的颜色,取值如表所示。第10章其他编程10.1.1画布的通用属性和方法画布的通用属性和方法3. Tbrush Object(画刷对象)(画刷对象)画刷对象用以填充图形,如用画刷颜色或图案对矩形或椭圆进行填充。Tbrush拥有一个画刷句柄(Hbrush)。画刷的颜色定义在C
7、olor属性中,画刷还有一个Bitmap属性,该属性只能在运行时得到,画刷可使用位图填充图形以产生特殊效果。 第10章其他编程10.1.1画布的通用属性和方法画布的通用属性和方法4. TColor类型类型TColor类型用于定义一个对象的颜色。很多部件的颜色属性就是TColor类型,在Graphics单元中TColor定义如下:TColor=-(COLOR_ENDCOLORS+1)$02FFFFF;第10章其他编程10.1.2 图形图像组件图形图像组件 1.Image组件组件Image组件是一个容器组件,它在应用程序窗体窗口中提供了一个矩形区域,用于显示和输出位图、图标、图元文件或用户自定义的
8、图形文件。Image组件的常用属性表。第10章其他编程10.1.2 图形图像组件图形图像组件 2. Shape组件组件Shape组件用于在窗体中绘制几何图形,如椭圆、矩形和圆角矩形等。该组件的常用属性有:lShape:指明需要绘制的几何图形,它可能的取值和含义如表所示; lBrush:指明在几何图形中填充的样式; lPen:指定几何图形所使用的线型。第10章其他编程10.1.2 图形图像组件图形图像组件 3. PaintBox组件组件该组件是一个简单的画板,它为应用程序提供了可在窗体的特定矩形区域内画图的方法。PaintBox组件的主要属性有:(1)Anchors属性该属性包含4个子属性(ak
9、Left、akTop、akRight和akBotton),用来指定在运行过程中,当父组件是可调时,它相对于父组件边沿的位置。(2)BoundsRect属性该属性用来指定组件的矩形边界,用其父组件的坐标系来表示。第10章其他编程10.1.2 图形图像组件图形图像组件 例如,以下代码用于绘制矩形:procedureTform1.Button1Click(Sender:TObject);varMyRect:TRect;beginMyRect:=ActiveControl.BoundsRect;MyRect.right=MyRect.Left+2*(MyRect.Right-MyRect.Left);
10、MyRect.Botton:=MyRect.Top+(MyRect.Botton-MyRect.Top)div2;ActiveControl.BoundsRect:=MyRect;end;(3)Canvas属性该属性仅在运行时有效,使用该属性可以在组件表面的特定区域进行绘图。(4)Color属性该属性可用于除“颜色”对话框以外的所有组件或对象。(5)Constaints属性该属性强制指定组件的大小。第10章其他编程10.1.3图形(图形(Picture)对象)对象 1.读取图像读取图像读取图像需要使用LoadFromFile方法来调用,同时配合OpenDialog函数来选择相应的图像文件。下面
11、的代码实现对图像文件的读取:procedureTform1.Open1Click(Sender:Tobject);begin ifOpenDialog1.Executethen beginCurrentFile:=OpenDialog1.Filename;Image.Picture.LoadFromFile(CurrentFile); end;end;第10章其他编程10.1.3图形(图形(Picture)对象对象 2. 保存图像保存图像为将Image组件中的图像保存为文件,可调用Image组件的Picture属性的SaveToFile方法。SaveToFile方法需要一个保存文件的文件名。下
12、面的代码是一般程序常用的“Save”和“SaveAs”菜单的处理程序:procedureTform1.Save1Click(Sender:Tobject)beginifCurrentFilethenImage.Picture.SaveToFile(CurrentFile)elseSaveAsClick(Sender);end;procedureTform.Saveas1Click(Sender:TObject);beginifSaveDialog1.ExecutethenbeginCurrentFile:=SaveDialog1.FileName;Save1Click(Sender);end;
13、end;第10章其他编程10.1.3图形图形(Picture)对象)对象 3. 替换图像替换图像用户可以在任何进修替换掉Image组件中的图像,只要将一新的图像对象赋给Picture就可以了。下面的代码实现用一个新的位图对象代替原有的Image组件中的Picture中的图像对象的功能:Bitmap:Tbitmap:beginBitmap:=Tbitmap.Create;Bitmap.Width:=NewWidth;Bitmap.Height:=NewHeight;Image.Picture.Graphic:=Bitmap;CurrentFile:=;end;第10章其他编程10.1.3图形(图
14、形(Picture)对象)对象 【例例】屏幕复制,将当前屏幕的图像抓下来,显示到窗口中;还可以屏幕复制,将当前屏幕的图像抓下来,显示到窗口中;还可以文件的形式保存起来。步骤如下:文件的形式保存起来。步骤如下:(1)建立用户界面:建立窗体Form1,添加一个Panel、一个Timer、一个SavePictureDialog、四个Button控件并在Panel控件上添加一个Image控件。(2)再创建窗体Form2,用于窗口抓图的显示窗口;在该窗体中添加一个Image控件和Timer控件,其中Image控件用于保存图像。(3)设置控件属性:对添加的控件按下表设置其属性。第10章其他编程10.1.3
15、图形图形(Picture)对象)对象 控件的属性设置第10章其他编程10.1.3图形(图形(Picture)对象对象(4)激活第一个单元文件,单击Delphi7的File菜单下的UseUnit项,出现窗口,选择Unit2,单击OK。(5)编写程序代码如下:procedureTForm1.Timer1Timer(Sender:TObject);/完成对屏幕的复制,并粘帖到Image控件中varFullscreen:Tbitmap;Fullscreencanvas:tcanvas;dc:HDC;begintimer1.Enabled:=false;fullscreen:=tbitmap.Creat
16、e;/建立一个BITMAP来存放图像fullscreen.Width:=screen.Width;dc:=getdc(0);/API函数,获取屏幕fullscreencanvas:=tcanvas.Create;/建立一个CANVAS对象fullscreencanvas.Handle:=dc;fullscreen.Canvas.CopyRect(rect(0,0,screen.Width,screen.Height),fullscreencanvas,rect(0,0,screen.Width,screen.Height);第10章其他编程10.1.3图形图形(Picture)对象)对象ful
17、lscreencanvas.Free; releasedc(0,dc); image1.Picture.Bitmap:=fullscreen;/将拷贝下的图像赋给IMAGE对象image1.Width:=fullscreen.Width;image1.Height:=fullscreen.Height;fullscreen.Free;form1.WindowState:=wsnormal;form1.Show;messagebeep(1);end;procedureTForm1.Button4Click(Sender:TObject);beginclose;end;第10章其他编程10.1.3
18、图形(图形(Picture)对象)对象procedureTForm1.Button1Click(Sender:TObject);beginform1.WindowState:=wsminimized;form1.Hide;timer1.Enabled:=true;end;procedureTForm1.Button3Click(Sender:TObject);beginifform1.SavePictureDialog1.Executethenbeginform2.Image1.Picture.SaveToFile(savepicturedialog1.FileName);end;end;第1
19、0章其他编程10.1.3图形图形(Picture)对象)对象procedureTForm1.Button2Click(Sender:TObject);beginform1.Hide;form2.Hide;form2.Timer1.Enabled:=true;end;end.varForm2:TForm2;foldx,x1,y1,x2,y2,oldx,oldy,foldy:integer;flag,trace:boolean;implementationusesunit3;$R*.dfm第10章其他编程10.1.3图形(图形(Picture)对象)对象procedureTForm2.Timer1
20、Timer(Sender:TObject);varfullscreen:TBitmap;fullscreencanvas:TCanvas;dc:HDC;beginform2.timer1.enabled:=false;fullscreen:=tbitmap.create;fullscreen.width:=screen.Width;fullscreen.Height:=screen.Height;dc:=getdc(0);fullscreencanvas:=tcanvas.Create;fullscreencanvas.Handle:=dc;fullscreen.Canvas.CopyRect
21、(rect(0,0,screen.Width,screen.Height),fullscreencanvas,rect(0,0,screen.Width,screen.Height);fullscreencanvas.Free;releasedc(0,dc);第10章其他编程10.1.3图形(图形(Picture)对象)对象image1.Picture.Bitmap:=fullscreen;image1.Width:=fullscreen.Width;image1.Height:=fullscreen.Height;fullscreen.Free;form2.WindowState:=wsno
22、rmal;form2.Show;messagebeep(1);foldx:=-1;foldy:=-1;image1.Canvas.Pen.Mode:=pmnot;/笔的模式取反image1.Canvas.Pen.Color:=clblack;/笔的颜色image1.Canvas.Brush.Style:=bsclear;/刷子的格式flag:=true;end;第10章其他编程10.1.3图形(图形(Picture)对象)对象procedureTForm2.Image1MouseMove(Sender:Tobject;Shift:TShiftState;X,Y:Integer);beginif
23、trace=truethen/追踪鼠标begin/擦除旧的图形并画上新的图形withimage1.Canvasdobeginrectangle(x1,y1,oldx,oldy);rectangle(x1,y1,x,y);oldx:=x;oldy:=y;end;endelseifflag=truethen第10章其他编程10.1.3图形(图形(Picture)对象)对象beginwithimage1.Canvasdobeginmoveto(foldx,0);lineto(foldx,screen.Height);moveto(0,foldy);lineto(screen.Width,foldy)
24、;moveto(x,0);lineto(x,screen.Height);moveto(0,y);lineto(screen.Width,y);foldx:=x;foldy:=y;end;end;end;第10章其他编程10.1.3图形(图形(Picture)对象)对象procedureTForm2.Image1MouseDown(Sender:Tobject;Button:TMouseButton;Shift:TShiftState;X,Y:Integer);/绘制区域图像varwidth,height:integer;newbitmap:TBitmap;beginif(trace=fals
25、e)thenbegin/首次点击鼠标左键,开始追踪鼠标flag:=false;withimage1.Canvasdobeginmoveto(foldx,0);lineto(foldx,screen.Height);moveto(0,foldy);lineto(screen.Width,foldy);第10章其他编程10.1.3图形(图形(Picture)对象)对象end;x1:=x;y1:=y;oldx:=x;oldy:=y;trace:=true;image1.Canvas.Pen.Color:=clblack;image1.Canvas.Brush.Style:=bsclear;endel
26、sebegin/第2次点击鼠标,得到矩形,并将它拷贝到FORM1的IMAGE上x2:=x;y2:=y;trace:=false;第10章其他编程10.1.3图形(图形(Picture)对象)对象image1.Canvas.Rectangle(x1,y1,oldx,oldy);width:=abs(x2-x1);height:=abs(y2-y1);orm1.Image1.Width:=width;form1.Image1.Height:=height;newbitmap:=Tbitmap.Create;/生成Bitmap对象newbitmap.Width:=width;newbitmap.He
27、ight:=height;newbitmap.Canvas.CopyRect(rect(0,0,width,height),form2.Image1.Canvas,rect(x1,y1,x2,y2);form1.Image1.Picture.Bitmap:=newbitmap;/放到FORM的IMAGE上newbitmap.Free;form2.Hide;form1.Show;end;end;end.第10章其他编程10.1.3图形(图形(Picture)对象)对象(6)运行得到如图的结果。第10章其他编程10.2 多媒体编程多媒体编程 10.2.1 简易媒体播放器实例简易媒体播放器实例 制作
28、一个媒体播放器,需要在窗体上设置一个TmediaPlayer(该组件位于组件面板的System选项卡上,图标为)、十个Tbutton、一个TopenDialog(位于组件面板的Dialogs选项卡上,图标为)等组件)、一个Edit、一个ComboBox、一个Timer如图所示。各个组件的名字(Name属性)采用默认值。第10章其他编程10.2.1 简易媒体播放器实例简易媒体播放器实例该媒体播放器的工作过程是:单击“打开”按钮,就会打开OpenDialog对话框,让用户从中选择一个文件。单击“播放”按钮,就会播放用户所选择的文件。下面的代码是用于处理Button8的OnClick事件,它将打开媒
29、体播放器。procedureTform1.Button8Click(Sender:Tobjict);beginifopendialog1.Executethenbeginmediaplayer1.FileName:=opendialog1.FileName;mediaplayer1.openend;end.第10章其他编程10.2.1 简易媒体播放器实例简易媒体播放器实例为了OpenDialog对话框只列出多媒体文件,需要在属性查看器中设置OpenDialog1的Filter属性,单击打开Filter属性设置旁的按钮,将打开FilterEditor对话框,如图所示,在该对话框中键入所要打开文件
30、的类型,如*.WAV、*.AVI、*.MID等,单击OK。设置好上述内容后,保存该工程,这样一个简单的媒体播放器就制作好了。运行这个程序,打开一个多媒体文件即可。第10章其他编程10.2.2 媒体播放器组件媒体播放器组件 1. MediaPlayer组件组件媒体播放器组件媒体播放器组件 媒体播放器组件属性媒体播放器组件属性第10章其他编程10.2.2 媒体播放器组件媒体播放器组件2. MediaPlayer组件的属性组件的属性(1)AutoEnable属性 该属性值是Boolean类型。如果这个属性设为True,媒体播放器在运行过程中能够自动控制哪些按钮当前可用,哪些按钮当前不可用(不可用的按
31、钮以灰色显示)。 (2)AutoOpen属性该属性值是Boolean类型。如果这个属性设为True,媒体播放器自动打开DeveceType属性指定的多媒体设备(如果DeviceType属性设为dtAutoSelect,媒体播放器将自动打开FileName属性中指定的文件)。(3)AutoRewind属性该属性值是Boolean类型,用于设置是否具有自动重绕功能。第10章其他编程10.2.2 媒体播放器组件媒体播放器组件(4)Capabilities属性该属性值是只读TMPDevCapsSet类型,返回当前设备可以进行 的操作,它的值及值的含义如表所示。第10章其他编程10.2.2 媒体播放器组
32、件媒体播放器组件(5)ColoredButton属性该属性值是TbuttonSet集合类型,用于设置哪些按钮彩色显示,哪些按钮用黑白显示,默认时全部按钮均用彩色显示。第10章其他编程10.2.2 媒体播放器组件媒体播放器组件MediaPlayer组件的其他属性列于表中。第10章其他编程10.2.2 媒体播放器组件媒体播放器组件上表Mode属性的取值及含义见表第10章其他编程10.2.2 媒体播放器组件媒体播放器组件3. MediaPlayer组件的过程和函数组件的过程和函数(1)AutoButtonSet过程该过程用来指定媒体播放器的按钮是否可用。(2)Back过程该过程使媒体播放器退回数个帧
33、。(3)Click过程这是一个动态过程,该过程决定当OnClick事件被触发时执行什么动作。在默认状态下,当OnClick事件触发时,该过程不招待任何动作,仅仅是调用一个事件处理程序,连接到媒体播放器的OnClick事件处理程序中。该过程可以根据需要重新加载事件处理程序。(4)Close过程该过程关闭已打开的多媒体播放设备。当应用程序中止时,自动调用该过程。第10章其他编程10.2.2 媒体播放器组件媒体播放器组件(5)Create函数这是一个动态的构造函数,该函数可在运行时建立一个MediaPlayer对象,并对其进行初始化,使得媒体播放器既可以在设计时静态建立,也可以在运行时通过调用Cre
34、ate方法动态建立。(6)Destroy函数这是一个动态的析构函数,该函数用于撤销媒体播放器对象。应用程序不能直接调用该函数,应先调用Free过程检查媒体播放器对象是否空闲,如果空闲就释放其占用的内存空间。该函数执行前,必须确保媒体设备已经关闭。第10章其他编程10.2.2 媒体播放器组件媒体播放器组件MediaPlayer媒体播放器的其他过程表。第10章其他编程10.2.2 媒体播放器组件媒体播放器组件MediaPlayer媒体播放器的其他过程表。第10章其他编程10.2.2 媒体播放器组件媒体播放器组件4. MediaPlayer组件的事件组件的事件(1)OnClick事件当用户移动光标到
35、媒体播放器控制按钮组的任一按钮上,单击鼠标左键或者当媒体播放器控制按钮获得焦点时,单击空格键都将会触发该事件。(2)OnNotify事件如果Notify属性设置为True,当一个媒体控制方法(如Back、Close、Eject、Next、Open、Pause、PauseOnly、Play.Previous、Resume、StartRecording、Step、Stop等)执行结束时,触发该事件。OnNotify事件触发后,必须重新设置Notify属性为True,以便触发下一个OnNotify事件。第10章其他编程10.2.2 媒体播放器组件媒体播放器组件(3)OnPostClick事件当OnC
36、lick事件处理程序被调用以后,触发该事件。(4)OnEnter事件当一个控件接收到键入焦点时,触发该事件。第10章其他编程10.3 线程线程 10.3.1 线程概念线程概念 所谓多线程,是指操作系统同时运行多个执行体的能力。线程概念与并发性是紧密相关的,多线程的出发点就是为了提高系统的并发度。线程与进程的差别主要体现在两个方面:线程和进程虽然都是系统的基本执行单元,线程的划分单位比进程小,因此支持多线程的系统比只支持多进程的系统并发度高;进程将内存作为自己独享的资源,每个进程都有自己的内存空间;而线程共享内存空间,并通过共享内存交换信息,因此有利于提高系统效率。第10章其他编程10.3.2
37、TThread类类 1.TThread类类TThread类是直接从Tobject继承下来的,它是抽象类,即不能创建TThread类的实例,而只能创建TThread派生类的实例即创建线程对象。创建线程对象的方法是,选择Delphi7主菜单下的FileNewOther,在弹出的“NewItems”对话框的“New”选项卡中选择ThreadObject图标,如图所示。第10章其他编程10.3.2 TThread类类当双击ThreadObject图标后,将出现如图所示的对话框,提示输入线程对象的名称,线程对象名称是一个标识符,如TestThread。输入线程对象名后,Delphi7会自动创建一个包括该
38、线程对象的单元,该单元给出了线程的框架,其最初的内容如下所示:第10章其他编程10.3.2 TThread类类unitUnit1;interfaceusesClasses;typeTestThread=class(TThread)privatePrivatedeclarationsprotectedprocedureExecute;override;end;implementationTestThreadprocedureTestThread.Execute;beginPlacethreadcodehereend;end.第10章其他编程10.3.2 TThread类类TThread派生类中唯
39、一必须覆盖的方法是Excute(),即需要定义线程所执行的功能。当Create()需要参数为False,则当调用Create()后,线程对象的Execute()方法将会自动被调用,即自动地执行Execute()的代码。当Create()需要参数为False,则不自动执行Execute()的代码,而是要通过TThread的Resume()方法来唤醒线程。第10章其他编程10.3.2 TThread类类2. 与与VCL同步同步大多数VCL都要求在任一时刻只允许一个线程访问它,因此在Delphi中使用多线程,还有一个重要的注意事项,即对VCL访问只能在主线程中进行,这表明所有与用户交互的代码只能在主
40、线程中。同时,Delphi为了使线程中的方法也能在主线程中执行,在TThread中设置了一个Synchronize()方法,该方法声明如下:procedureSynchronize(Method:TThreadMethod);Synchronize()方法的参数就是用来传递需要在主线程中执行的线程中的方法。第10章其他编程10.3.3 线程的终止线程的终止 当线程对象的Execute()方法执行完毕后,就认为该线程终止了,此时Delphi会自动调用一个标准例程EndThread(),该例程将再调用API函数ExitThread(),由ExitThread()来清除线程所占用的栈。线程终止时,将
41、触发OnTerminate事件,可以利用在退出Execute()之前将FreeOnTerminate属性置为True的方法来及时清除线程对象。procedureTestThread.Execute;vari:integer;Begin FreeOnTerminate:=True;/设置FreeOnTerminate为true以便及时清除线程对象 Fori:=1to10000dobeginIfTerminatedthenbreak;Value:=Value+1; end;End;第10章其他编程10.3.3 线程的终止线程的终止 【例例】创建如图所示的窗体,单击“开始”按钮后,将创建一个附属线程
42、,该线程执行一个计算任务,与此同时主线程并发执行,用户可在文本区域输入任意的字符。示例中所使用的窗体和组件的属性列于表中,未列的属性均取默认值。主窗体(设计时)主窗体(设计时)第10章其他编程10.3.3 线程的终止线程的终止 窗体与组件的属性设置第10章其他编程10.3.3 线程的终止线程的终止保存主窗体单元为Main.pas,源程序如下:unitMain;interfaceusesWindows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,Dialogs,StdCtrls,ThrdU;typeTMainForm=cl
43、ass(TForm)Label1:TLabel;Button1:TButton;Label2:TLabel;Edit1:TEdit;Memo1:TMemo;procedureButton1Click(Sender:TObject);end;第10章其他编程10.3.3 线程的终止线程的终止varMainForm:TMainForm;implementation$R*.dfmprocedureTMainForm.Button1Click(Sender:TObject);beginTTestThread.Create(False);创建线程,使其立即执行end;end.第10章其他编程10.3.3
44、 线程的终止线程的终止创建线程单元ThrdU.pas,选择主菜单的FileNewUnit,输入线程对象名为TtestThread,再输入以下源程序:unitThrdU;interfaceusesClasses;typeTTestThread=class(TThread)线程TTestThread类定义privateAnswer:integer;protectedprocedureGiveAnswer;procedureExecute;override;end;implementationusesSysUtils,Main;procedureTTestThread.GiveAnswer;begi
45、nMainForm.Edit1.Text:=InttoStr(Answer);end;第10章其他编程10.3.3 线程的终止线程的终止TTestThreadprocedureTTestThread.Execute;执行一个较长的计算任务Vari:integer;beginFreeOnTerminate:=True;fori:=1to2000000dobeginifTerminatedthenbreak;Inc(Answer,Round(abs(sin(sqrt(i);Synchronize(GiveAnswer);使GiveAnswer能在主线程中执行end;end;end.第10章其他编程
46、10.3.3 线程的终止线程的终止单击F9运行程序,结果如图所示。第10章其他编程10.3.4 线程同步线程同步 编写多线程应用程序时,需要控制好线程间的同步资源访问,以保证线程的安全运行。所谓线程同步,简单说就是协调线程间的执行时序。Win32API提供了一组同步对象,包括信号量、互斥、临界区和事件等,来解决线程的同步问题。Delphi分别将事件对象和临界区对象封装为TEvent和TCriticalSection对象,使得事件和临界区更易使用。第10章其他编程10.3.4 线程同步线程同步1.临界区临界区临界区是指一次只能由一个线程执行的一段代码。在使用临界区之前,要使用Initialize
47、CriticalSection()过程来初始化它,其声明如下:procedureInitializeCriticalSection(varlpCriticalSection:TRTLCriticalSection);stdcall;lpCriticalSection是一个TRTLCriticalSection类型的记录,在该参数中传递未初始化的记录,InitializeCriticalSection()过程就会填充这个记录。TRTLCriticalSection是一个与临界区资源相关的类型,。第10章其他编程10.3.4 线程同步线程同步创建临界区,使用EnterCriticalSection
48、()和LeaveCriticalSection()过程来封装代码块。这两个过程的声明如下:procedureEnterCriticalSection(varlpCriticalSection:TRTLCriticalSection);stdcall;procedureLeaveCriticalSection(varlpCriticalSection:TRTLCriticalSection);stdcall;当不再需要TRTLCriticalSection记录时,要调用DeleteCriticalSection()过程将其删除。第10章其他编程10.3.4 线程同步线程同步【例例】设有一个全局数
49、组,分别用两个线程对其进行两次初始化,需要对这两个线程同步,否则将出现数据不确定的情形。用临界区方式进行两线程的同步,将对数组各元素赋值的代码设为临界区。程序界面如图所示,当单击“初始化”按钮后,将生成两个线程并执行,在列表框中显示数组元素值。(1)创建应用程序界面,新建如图所示的窗体,并放置一个label、一个button和一个listbox组件,各组件及其属性设置列于表中。 第10章其他编程10.3.4 线程同步线程同步窗体与组件的属性设置第10章其他编程10.3.4 线程同步线程同步(2)在Form1对应的Unit1单元文件中输入程序代码:unitUnit1;interfaceusesW
50、indows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms, Dialogs,StdCtrls,unit2;TypeTForm1=class(TForm)Button1:TButton;ListBox1:TListBox;Label1:TLabel;procedureButton1Click(Sender:TObject);end;第10章其他编程10.3.4 线程同步线程同步var Form1:TForm1;CS:TRTLCriticalSection;/声明临界区资源记录变量implementation$R*.dfmp
51、rocedureTForm1.Button1Click(Sender:TObject);beginInitializeCriticalSection(CS);/初始化临界区TInitThread.Create(False);/创建TinitThread线程对象并执行TInitThread.Create(False);end;end.第10章其他编程10.3.4 线程同步线程同步(3)创建线程对象TinitThread,选择主菜单的FileNewOther,在NewItems的New选项卡中双击 “ThreadObject”图标,在弹出的对话框中输入线程名“TinitThread”。(4)在系统
52、自动新建的Unit2单元中输入程序代码:unitUnit2;interfaceusesWindows,SysUtils,Classes;/在此要添加Windows和SysUtilsType以下是TinitThread类定义TInitThread=class(TThread)protectedprocedureShowResult;/显示结果procedureExecute;override;end;第10章其他编程10.3.4 线程同步线程同步implementationusesUnit1;constSIZE=10;/数组大小varnext:integer=0;/全局变量定义 tag:inte
53、ger=0; A:array1.SIZEofinteger;procedureTInitThread.ShowResult;vari:integer;begininc(tag); iftag=2then begin/两个线程均执行完毕fori:=1toSIZEdo/在列表框中显示数组各元素值Form1.ListBox1.Items.Add(intToStr(Ai);DeleteCriticalSection(CS);/移去临界区 end;end;第10章其他编程10.3.4 线程同步线程同步procedureTInitThread.Execute;vari:integer;begin Ente
54、rCriticalSection(CS);/进入临界区 fori:=1toSIZEdo/为数组各元素赋值beginAi:=next;Inc(next);Sleep(5); end; LeaveCriticalSection(CS);/临界区终止 Synchronize(ShowResult);/使ShowResult能在主线程中执行end;end.第10章其他编程10.3.4 线程同步线程同步(5)单击F9运行程序,结果如图所示。第10章其他编程10.3.4 线程同步线程同步说明:说明:在第一个线程调用了EnterCriticalSection()之后,其他的线程就不能进入临界区代码,它们将睡
55、眠;直到第一个线程调用LeaveCriticalSection()后,下一个需要进入临界区的线程才被唤醒。第10章其他编程10.3.4 线程同步线程同步2. 互斥互斥(1)互斥(mutex)与临界区非常相似,但有两个主要区别:互斥可用于不同进程的线程间同步,而临界区不可;互斥可被赋予一个字符串名,通过该名字可创建该互斥对象的附加句柄。(2)创建互斥对象的函数是CreateMutex(),其原型为functionCreateMutex(lpMutexAttibutes:PsecurityAttributes;bInitialOwner:BOOL;lpName:Pchar):Thandle;std
56、call;lpMutexAttibutes参数为一个指向TsecurityAttributes记录的指针,通常置为0,表示使用默认的安全属性。BInitialOwner参数用于指出创建互斥对象的线程是否要成为此互斥对象的拥有者,该参数为false表示互斥对象没有拥有者。LpName参数指出互斥对象的名称。第10章其他编程10.3.4 线程同步线程同步(3)互斥对象不再使用时,应调用CloseHandle()过程来关闭它。当一个线程不需再拥有互斥对象时,应调用ReleaseMutex()过程。(4)为了防止其他线程进入同步区代码,需使用aitForSingleObject()函数,原型为:fun
57、ctionWaitForSingleObject(hHandle:Thandle;dwMilliseconds:DWORD):DWAORD;stdcall;DwMilliseconds参数可设为两个值:当值为0时,函数将只检查由hHandle参数指定的互斥对象是否处于发信号状态,检查完毕后函数立即返回当值为INFINITE时,则函数将一直等待,直到信号出现为止hHandle参数指定的互斥对象返回值有3种:WAIT_ABANDONED,表明指定对象是互斥对象,并且拥有该互斥对象的线程在未释放该对象之前就已终止WAIT_OBJECT_0,表明指定的对象处于发信号状态WAIT_TIMEOUT,表明等
58、待的时间已过,对象仍是非发信号状态第10章其他编程10.3.4 线程同步线程同步【例例】对于临界区的例题,使用互斥方式进行两线程的同步,该应用程对于临界区的例题,使用互斥方式进行两线程的同步,该应用程序与临界区的例题的差别仅在于线程同步方式不同,其余均相同。程序序与临界区的例题的差别仅在于线程同步方式不同,其余均相同。程序两个单元文件的内容分别为:两个单元文件的内容分别为:/文件Unit1.pasunitUnit1;interfaceusesWindows,Messages, SysUtils,Variants,Classes,Graphics,Controls,Forms,Dialogs,S
59、tdCtrls,Unit2;typeTForm1=class(TForm)Button1:TButton;Label1:TLabel;ListBox1:TListBox;procedureButton1Click(Sender:TObject);end;第10章其他编程10.3.4 线程同步线程同步varForm1:TForm1;hMutex:THandle=0;/声明互斥量implementation$R*.dfmprocedureTForm1.Button1Click(Sender:TObject);beginhMutex:=CreateMutex(nil,False,nil);/创建互斥
60、量TInitThread.Create(False);/创建TinitThread线程对象并执行TInitThread.Create(False);end;end.第10章其他编程10.3.4 线程同步线程同步/文件Unit2.pasunitUnit2;interfaceuses Windows,SysUtils,Classes;typeTInitThread=class(TThread)protectedprocedureShowResult;/显示结果procedureExecute;override;end;第10章其他编程10.3.4 线程同步线程同步implementationuse
61、sUnit1;constSIZE=10;/数组大小varnext:integer=0;/全局变量定义tag:integer=0;A:array1.SIZEofinteger;procedureTInitThread.ShowResult;var i:integer;begin inc(tag); iftag=2then begin两个线程均执行完毕fori:=1toSIZEdo/在列表框中显示数组各元素值Form1.ListBox1.Items.Add(intToStr(Ai);CloseHandle(hMutex);/关闭互斥对象 end;end;第10章其他编程10.3.4 线程同步线程同
62、步procedureTInitThread.Execute;vari:integer;beginifWaitForSingleObject(hMutex,INFINITE)=WAIT_OBJECT_0then此时,若要拥有hMutex互斥对象的线程将睡眠,用来禁止其他线程进入此同步区域的代码beginfori:=1toSIZEdo/为数组各元素赋值beginAi:=next;Inc(next);Sleep(5);end;end;ReleaseMutex(hMutex);/释放互斥量Synchronize(ShowResult);/使ShowResult能在主线程中执行end;end.第10章其
63、他编程10.4 DLL应用和开发应用和开发DLL DLL(DynamicLinkLibrary),即动态链接库,它就是程序模块,包括编译过的可执行代码、数据或资源,能够被其他的Windows应用程序共享。大部分DLL文件的扩展名为.dll,也有的可能是.drv(设备驱动程序)、.sys(系统文件)或.fon(字体文件)。Windows系统文件Kernel32.dll、User32.dll、GDI32.dll就是核心Win32系统的动态链接库Kernel.dll负责内存、进程和线程的管理User32.DLL负责创建窗口和处理Win32消息GDI32.DLL负责处理图形。DLL和可执行文件(exe
64、)非常类似,但DLL中包含了可执行代码却不能单独执行,而应由Windows应用程序直接或间接调用。第10章其他编程10.4.1 使用使用DLL的优点的优点 1.节省资源节省资源动态链接不将所调用的函数代码拷贝到应用程序的可执行文件中,而是应用程序运行时,才动态地装载DLL,其代码将被映射到进程的地址空间中,当要执行所调用DLL中的函数时,根据链接产生的重定位信息,转去执行DLL中相应的函数代码。2. 共享代码、资源和数据共享代码、资源和数据把资源组织到DLL中,就可以让许多应用程序使用,而不必在内存中重复装入这些内容。线程是相互独立的所有线程可以共享DLL的数据。3. 实现模块化实现模块化可将
65、程序划分为多个DLL模块,既可以提高模块化程度,又可以隐蔽代码实现细节。第10章其他编程10.4.1 使用使用DLL的优点的优点4. 独立于编程语言独立于编程语言在一种开发环境下设计的DLL程序,可以在其他的环境中使用。5. 便于系统升级便于系统升级如果需对系统升级,只要将所涉及的DLL进行即可,而不必将整个系统重新编译、链接,减少了工作量。第10章其他编程10.4.2 创建创建DLL创建创建DLL的步骤是:的步骤是:首先创建一个新的DLL项目(如project1),文件头部为:LibraryProject1;/Project1为创建的项目名在所创建的项目的Uses语句中加入Exports语句
66、,指明使用DLL的应用程序要调用的函数或过程名。形式为:exports/引出函数名或过程名函数名name函数引用名;/函数名与函数引用名通常取为相同函数引用名是使用该DLL文件的应用程序声明引用DLL函数或过程时的名字。在DLL单元文件(.Pas文件)中加入DLL的函数或过程的声明,形式如下:函数名(参数表):类型名;stdcall;/过程与之类似如在另一个pas文件中说明和定义的,则要在DLL项目文件中加入以下语句:UsesDllEXPinDllEXP.pasForm1;对项目进行编译即形成DLL文件,可被其它项目调用。第10章其他编程10.4.2 创建创建DLL 【例例】本例创建一个本例创
67、建一个DLL,文件名为,文件名为Max.dll,其中包含一个求两其中包含一个求两个数中的大者的函数个数中的大者的函数MaxNum。再设计一个主程序调用。再设计一个主程序调用DLL中的中的函数在主程序中输入两个整数,通过调用该函数在主程序中输入两个整数,通过调用该DLL,即可求出较大的,即可求出较大的数。数。1)创建)创建Max.dll文件文件 (1)新建一个DLL项目。选择主菜单的FileNewOthers菜单项,在“NewItems”对话框中双击“DLLWizard”图标,单击OK按钮。 (2)在所弹出的代码编辑窗口中输入DLL文件的程序代码,该 DLL的程序代码如下:第10章其他编程10.
68、4.2 创建创建DLLlibraryMax;/DLL项目名为MaxusesSysUtils,Classes;$R*.resfunctionMaxNum(Num1,Num2:integer):integer;stdcall;/定义求最大值函数beginifNum1Num2thenresult:=Num1elseresult:=Num2;end;exportsMaxNumnameMaxNum;/引出求最大值函数end.第10章其他编程10.4.2 创建创建DLL(3)选择主菜单下的FileSave命令项,保存该文件名为Max.dpr。(4)选择主菜单下的ProjectCompileMax(或用Ct
69、rl+F9快捷键)菜单项,对Max.dpr进行编译,即生成Max.dll文件。2)调用)调用DLL创建一个应用程序调用Max.dll文件中的MaxNum函数。该应用程序的界面如图所示,所使用的组件及其属性见下一页表中。 第10章其他编程10.4.2 创建创建DLL窗体与组件的属性设置第10章其他编程10.4.2 创建创建DLL主程序对应的单元文件为Unit1.pas文件,其内容如下:unitUnit1;interfaceusesWindows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms, Dialogs,StdCtrls
70、;type第10章其他编程10.4.2 创建创建DLLTForm1=class(TForm)Label1:TLabel;Label2:TLabel;Label3:TLabel;Edit1:TEdit;Edit2:TEdit;Label4:TLabel;Edit3:TEdit;Button1:TButton;procedureButton1Click(Sender:TObject);end;var Form1:TForm1;implementation$R*.dfm第10章其他编程10.4.2 创建创建DLLfunctionMaxNum(Num1,Num2:integer):integer;st
71、dcall;externalMax.dllnameMaxNum;/对DLL函数进行外部声明procedureTForm1.Button1Click(Sender:TObject);varN1,N2:integer; MX:integer;beginN1:=strtoint(Edit1.Text);N2:=strtoint(Edit2.Text);MX:=MaxNum(N1,N2);/调用DLL函数Edit3.Text:=inttostr(MX);end;end.第10章其他编程10.4.2 创建创建DLL程序的运行结果如图所示。第10章其他编程10.4.3 调用调用DLL 1. 装载时调用装载
72、时调用(Load-time Dynamic Linking)在应用程序编译之前已经明确知道要调用DLL的哪些函数或过程,在编译时目标文件中只保留必要的链接信息;应用程序执行时,利用链接信息加载DLL函数或过程代码,并在内存中将DLL代码链接到调用程序的执行空间中。在调用DLL的应用程序单元文件中,需要在Implementation之后对DLL函数进行外部声明,形式如下:Implementationfunction函数名(参数表):类型名;stdcall;ExternalDLLName;装载时调用DLL程序执行速度快,效率高。缺点是如果加载的DLL不存在或DLL中没有要调用的函数或过程,应用程序
73、就会自动终止运行。DLL程序一旦加载就一直驻留在应用程序地址空间。第10章其他编程10.4.3 调用调用DLL2. 运行时调用运行时调用(Run-time Dynamic Linking)编译时并不知道将会调用DLL的哪些函数或过程。在运行时用Windows的API函数LoadLibary和GetProcAddress函数动态地获得DLL函数或过程的入口地址,再使用FreeLibary进行释放。设要调用的DLL中包含一个名为test的函数:functiontest(参数表):类型名;stdcall;运行时调用DLL的方法是:(1)在调用该DLL的应用程序的Type类型声明处加入如下 定义:ty
74、peTtest=function(参数表):类型名;(2)在Var变量定义部分定义如下变量:VARAptr:Ttest; Ihnd:Thandle; RetVal:类型名;其中Aptr、Ihnd两个变量必须定义,RetVal是DLL函数的返回值,视具体情况而定。第10章其他编程10.4.3 调用调用DLL(3)在调用DLL处加入以下的语句进行DLL装载:Ihnd:=LoadLibrary(路径:DLL文件名);Aptr:=GetProcAddress(Ihnd,函数名);(4)在应用程序需要之处调用DLL函数:RetVal:=Test(Aptr)(实参表);(5)调用完后,要用FreeLibr
75、ary释放DLL占用的内存:FreeLibary(Ihnd);第10章其他编程10.4.3 调用调用DLL 【例例】本例创建一个本例创建一个DLL,文件名,文件名为为MyCalendar.dll,其中包含其中包含一个显示星期的函数一个显示星期的函数ShowCalendar,再设计一个主程序以运行,再设计一个主程序以运行时调用方式调用该时调用方式调用该DLL中的此函数。中的此函数。1)创建)创建MyCalendar.dll (1)新建一个DLL项目。(2)在所弹出的代码编辑窗口中输入 如下DLL文件的程序代码: /文件名为MyCalender.dpr;编译此文件生成MyCalendar.dlll
76、ibraryMyCalendar;usesSysUtils,Classes,Unit1inUnit1.pasForm1;$R*.resExportsShowCalendarnameShowCalendar;/声明DLL函数beginend.第10章其他编程10.4.3 调用调用DLL(3)选择主菜单下的FileNewForm,新建一个窗体,在其中放 置一个Tcalendar组件(该组件位于组件面板的Samples选项 卡上,图标为 )和一个Bevel组件,所设计的窗体如图 所示,所使用的组件及其属性列于下一页表中。第10章其他编程10.4.3 调用调用DLL窗体与组件的属性设置第10章其他编程
77、10.4.3 调用调用DLL(4)在第3步所建窗体所对应的单元文件中输入程序代码。以下是Unit1.pas文件的源程序代码:unitUnit1;interfaceusesWindows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,Dialogs,Buttons,StdCtrls,ComCtrls,Grids,Calendar,Spin,ExtCtrls;typeTCalForm=class(TForm)/窗体定义Calendar1:TCalendar;Bevel1:TBevel;Label1:TLabel;EditYea
78、r:TEdit;CMBMonth:TComboBox;Label2:TLabel;第10章其他编程10.4.3 调用调用DLLbtnOK:TBitBtn;btnCancel:TBitBtn;procedureFormCreate(Sender:TObject);procedureCMBMonthChange(Sender:TObject);procedureEditYearChange(Sender:TObject);procedureEditYearKeyPress(Sender:TObject;varKey:Char);procedurebtnOKClick(Sender:TObject)
79、;procedurebtnCancelClick(Sender:TObject);end;第10章其他编程10.4.3 调用调用DLL/声明要引出的函数functionShowCalendar(AHandle:THandle;ACaption:String):TDateTime;stdCall;implementation$R*.dfmprocedureTCalForm.FormCreate(Sender:TObject);/窗体创建事件vari:integer;beginfori:=Low(LongMonthNames)toHigh(LongMonthNames)doCMBMonth.Ite
80、ms.Add(LongMonthNamesi); CMBMonth.ItemIndex:=Calendar1.Month-1; EditYear.Text:=IntToStr(Calendar1.Year);end;第10章其他编程10.4.3 调用调用DLLprocedureTCalForm.CMBMonthChange(Sender:TObject);/ComboBox事件beginCalendar1.Month:=(SenderasTComboBox).ItemIndex+1;end;procedureTCalForm.EditYearChange(Sender:TObject);/Ed
81、it事件beginCalendar1.Year:=StrToInt(SenderasTEdit).Text);end;procedureTCalForm.EditYearKeyPress(Sender:TObject;varKey:Char);beginif(not(Keyin0.9)and(ord(Key)VK_BACK)then/若输入的非数字键或键beginKey:=#0;Beep;/振铃响end;end;第10章其他编程10.4.3 调用调用DLLprocedureTCalForm.btnOKClick(Sender:TObject);beginclose;end;procedureT
82、CalForm.btnCancelClick(Sender:TObject);beginCMBMonth.ItemIndex:=Calendar1.Month-1;EditYear.Text:=IntToStr(Calendar1.Year);end;第10章其他编程10.4.3 调用调用DLLfunctionShowCalendar(AHandle:THandle;ACaption:String):TDateTime;/引出函数定义varCalForm:TCalForm;/DLL窗体Begin/复制应用程序的句柄给DLL的TApplication对象Application.Handle:=A
83、Handle;CalForm:=TCalForm.Create(Application);/创建并显示窗体tryCalForm.Caption:=ACaption;/窗体标题CalForm.ShowModal;/显示方式为模式化Result:=CalForm.Calendar1.CalendarDate;/返回设定的日期finallyCalForm.Free;/卸载窗体end;end;end.第10章其他编程10.4.3 调用调用DLL(5)选择主菜单下的Save命令项,保存该文件名为Unit1.dpr。(6)选择主菜单下的ProjectCompileMyCalendar菜单项,编译生成MyC
84、alendar.dll。2)调用)调用DLL创建一个应用程序调用MyCalendar.dll文件中的ShowCalendar函数,程序的功能是,当用户设置某一日期后,该日期将被返回给本程序显示,界面如图。第10章其他编程10.4.3 调用调用DLL程序对应的单元文件为Test.pas文件,其内容如下:unittest;interfaceusesWindows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,Dialogs,StdCtrls,Buttons;Type /定义一个过程数据类型TShowCalendarTShowC
85、alendar=function(AHandle:THandle;ACaption:String):TDateTime;stdcall; TForm1=class(TForm) Edit1:TEdit; Label1:TLabel; BitBtn1:TBitBtn; procedureBitBtn1Click(Sender:TObject);end;var Form1:TForm1;implementation$R*.dfm第10章其他编程10.4.3 调用调用DLLprocedureTForm1.BitBtn1Click(Sender:TObject);varAHandle:THandle;
86、ShowCalendar:TShowCalendar;beginAHandle:=LoadLibrary(MyCalendar.dll);/动态载入DLL,返回其句柄tryifAHandle0then /载入成功/获得函数SHowCalendar的地址,为取址运算ShowCalendar:=GetProcAddress(AHandle,ShowCalendar);ifnot(ShowCalendar=nil)then/找到ShowCalendar函数/ 在Edit1上显示设定的日期Edit1.Text:=DateToStr(ShowCalendar(Application.Handle,调用DLL显示)elseRaiseLastWin32Error; finallyFreeLibrary(AHandle); end;end;end.第10章其他编程10.4.3 调用调用DLL单击F9运行程序,程序主窗体如图10.15所示,单击其中的“加载”按钮,将弹出DLL窗体,如下页图所示,第10章其他编程10.4.3 调用调用DLL在该窗体中设置年和月份值后,单击“设置”按钮,该窗口将被释放,而所设置的年、月和日将在主窗体的Edit框中显示,如下页图所示。第10章其他编程10.4.3 调用调用DLL第10章其他编程