在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
最近到图书馆里借了一本Delphi好书-《*Delphi精要*》。说实在的,国内的 Delphi书有95%是垃圾, 大都是网上的技巧,代码堆积起来的,通常一本书30分 钟就可以搞定。但是这本技术书写得有点武侠 版的味道,而且都是作者切身的经验体会,可以和我先前看过的 台@@湾 的牛人陈宽达 的《Delphi深度历险》 相提并论。好书当然要做笔记了,以下就是俺的看书笔记。 1.集合 定义 TOneSet= set of (A,B,C);//全集 声明 partSet=[A,B]; 运算 +: partSet=partSet+[B];//等价于:Include(partSet,B); -: partSet=partSet-[B];//等价于:Exclude(partSet,B); 2.指针 除了pchar类型的指针变量,其他类型的指针变量是不允许进行加减运算的 @I等价于addr(I) 3.编译时函数: 编译时函数可以在声明中调用如下: var R1:Word=Trunc(12.6); 编译时函数列表: ord() chr() Trunc() Round() High() Low() SizeOf() 4.DLL 1)在DLL中编写的函数或过程都必须加上stdcall调用参数. library Delphi; uses SysUtils, Classes; function TestDll(i:integer):integer;stdcall; begin Result:=i; end; exports TestDll; begin end. 2)当使用了长字符串类型的参数、变量时要引用ShareMem。 Delphi中的string类型很强大,我们知道普通的字符串长度最大为256个字 符,但Delphi中string类型在默认情况下长度可以达到2G。(对,您没有看错,确 实是两兆。)这时,如果您坚持要使用string类型的参数、变量甚至是记录信息 时,就要引用ShareMem单元,而且必须是第一个引用的。既在uses语句后是第一个 引用的单元。如下例: uses ShareMem, SysUtils, Classes; 还有一点,在您的工程文件(*.dpr)中而不只是单元文件(*.pas)中也要做 同样的工作,这一点Delphi自带的帮助文件没有说清楚,造成了很多误会。不这样 做的话,您很有可能付出死机的代价。避免使用string类型的方法是将string类型 的参数、变量等声明为Pchar或ShortString(如:s:string[10])类型。同样的问 题会出现在当您使用了动态数组时,解决的方法同上所述。 3)静态调用例子: unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Edit1: TEdit; Button1: TButton; procedure Button1Click(Sender: TObject); private ...{ Private declarations } public ...{ Public declarations } end; var Form1: TForm1; implementation ...{$R *.DFM} //本行以下代码为我们真正动手写的代码 function TestDll(i:integer):integer;stdcall; external ’Delphi.dll’; procedure TForm1.Button1Click(Sender: TObject); begin Edit1.Text:=IntToStr(TestDll(1)); end; end. 4)动态调用: 这一点很重要,使用静态调用方法时要求所调用的DLL文件以及要调用的函数或过 程等等必须存在。如果不存在或指定的路径和文件名不正确的话,运行主程序时系 统会提示“启动程序时出错”或“找不到*.dll文件”等运行错误。 动态调用DLL相对复杂很多,但非常灵活。为了全面的说明该问题,这次我们 举一个调用由C++编写的DLL的例子。首先在C++中编译下面的DLL源程序。 #include extern ”C” _declspec(dllexport) int WINAPI TestC(int i) ...{ return i; } 编译后生成一个DLL文件,在这里我们称该文件为Cpp.dll,该DLL中只有一个 返回整数类型的函数TestC。为了方便说明,我们仍然引用上面的调用程序,只是 将原来的Button1Click过程中的语句用下面的代码替换掉了。 procedure TForm1.Button1Click(Sender: TObject); type TIntFunc=function(i:integer):integer;stdcall; var Th:Thandle; Tf:TIntFunc; Tp:TFarProc; begin Th:=LoadLibrary(’Cpp.dll’); ...{装载DLL} if Th>0 then try Tp:=GetProcAddress(Th,PChar(’TestC’)); if Tp<>nil then begin Tf:=TIntFunc(Tp); Edit1.Text:=IntToStr(Tf(1)); ...{调用TestC函数} end else ShowMessage(’TestC函数没有找到’); finally FreeLibrary(Th); ...{释放DLL} end else ShowMessage(’Cpp.dll没有找到’); end; 5) 一、编写技巧。 1 、为了保证DLL的正确性,可先编写成普通的应用程序的一部分,调试无误 后再从主程序中分离出来,编译成DLL。 2 、为了保证DLL的通用性,应该在自己编写的DLL中杜绝出现可视化控件的名 称,如:Edit1.Text中的Edit1名称;或者自定义非Windows定义的类型,如某种记 录。 3 、为便于调试,每个函数和过程应该尽可能短小精悍,并配合具体详细的注 释。 4 、应多利用try-finally来处理可能出现的错误和异常,注意这时要引用 SysUtils单元。 5 、尽可能少引用单元以减小DLL的大小,特别是不要引用可视化单元,如 Dialogs单元。例如一般情况下,我们可以不引用Classes单元,这样可使编译后的 DLL减小大约16Kb。 二、调用技巧。 1 、在用静态方法时,可以给被调用的函数或过程更名。在前面提到的C++编 写的DLL例子中,如果去掉extern ”C”语句,C++会编译出一些奇怪的函数名,原来 的TestC函数会被命名为@TestC$s等等可笑的怪名字,这是由于C++采用了C++ name mangling技术。这个函数名在Delphi中是非法的,我们可以这样解决这个问 题: 改写引用函数为 function TestC(i:integer):integer;stdcall; external ’Cpp.dll’;name ’@TestC$s’; 其中name的作用就是重命名。 2 、可把我们编写的DLL放到Windows目录下或者Windowssystem目录下。这样 做可以在external语句中或LoadLibrary语句中不写路径而只写DLL的名称。但这样 做有些不妥,这两个目录下有大量重要的系统DLL,如果您编的DLL与它们重名的话 其后果简直不堪设想,况且您的编程技术还不至于达到将自己编写的DLL放到系统 目录中的地步吧! 三、调试技巧。 1 、我们知道DLL在编写时是不能运行和单步调试的。有一个办法可以,那就 是在Run|parameters菜单中设置一个宿主程序。在Local页的Host Application栏 中添上宿主程序的名字就可进行单步调试、断点观察和运行了。 2 、添加DLL的版本信息。开场白中提到了版本信息对于DLL是很重要的,如果 包含了版本信息,DLL的大小会增加2Kb。增加这么一点空间是值得的。很不幸我们 如果直接使用Project|options菜单中Version选项是不行的,这一点Delphi的帮助 文件中没有提到,经笔者研究发现,只要加一行代码就可以了。如下例: library Delphi; uses SysUtils, Classes; ...{$R *.RES} //注意,上面这行代码必须加在这个位置 function TestDll(i:integer):integer;stdcall; begin Result:=i; end; exports TestDll; begin end. 3 、为了避免与别的DLL重名,在给自己编写的DLL起名字的时候最好采用字符 数字和下划线混合的方式。如:jl_try16.dll。 4 、如果您原来在Delphi 1或Delphi 2中已经编译了某些DLL的话,您原来编 译的DLL是16位的。只要将源代码在新的Delphi 3或Delphi 4环境下重新编译,就 可以得到32位的DLL了。 [后记]:除了上面介绍的DLL最常用的使用方法外,DLL还可以用于做资源的载体。 例如,在Windows中更改图标就是使用的DLL中的资源。另外,熟练掌握了DLL的设 计技术,对使用更为高级的OLE、COM以及ActiveX编程都有很多益处。 ======================================================== 14:36 2005-11-30 1.操作有序变量的函数: Ord() Pred() Succ() High() Low() 2.Boolean类型 Boolean//一个字节 ...{以下四个是用以兼容操作系统的} ByteBool//一个字节 WordBool//两个字节 LongBool//四个字节 3.枚举和子界 TColors=(Red,Blue=5,Green,Yellow,Orange,Purple); TMyColors=Green..Purple; 4.Real 声明变量时用Double和Single,在用做参数传递时用Extended. 5.过程类型和方法类型 Type ...{过程类型:} TMyFunction = function(参数列表):返回类型; TMyProcedure = procedure()(参数列表); ...{方法类型:} TNotifyEvent= procedure(Sender:TObject) of object; 6.Variant类型 Variant类型有三个状态:Unassigned,Null,非Null 7.类型别名 type DWORD =LongWord;//任何时刻DWORD和LongWord都是相容的 type HWND = type LongWord;//用于var和out参数等要求严格类型匹配的地方时,被 认为不兼容 ======================================================== 21:23 2005-12-8 1.变体记录 type TMessage= packed record//Using packed slows data access and, in the case of a character array, affects type compatibility (for more information, msg:Cardinal; case Integer of 0:( WParam:Longint; LParam:Longint; Result:LongInt; ); 1:( WParamLo:word; WParamHi:word; LParamLo:word; LParamHi:word; ResultLo:word; ResultHi:word; ); end; 以上是vcl消息定义 typedef struct ...{ HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; } MSG, *PMSG; 系统消息结构定义 2.vcl中两种消息派遣 vcl消息机制的整个流程如下: Windows->Delphi Application->(TWinControl)MainWndProc->WndProc->Dispatch->Handler (1)Application->dispatchMessage(const lpMsg:TMsg)根据参数lpMsg的接受者句 柄字段,将消息正文发给该句柄对应的窗口控件在windows中注册的窗口过程: Twincontrol.MainWndProc.所以到这里,消息就传递给Twincontrol了。这是在 Windows中派遣,是底层Api函数的调用。 (2)TObject.Dispatch(var Message); Dispatch首先在本类中查找相应的消息方法,如果没有找到,那么逐级上溯父 类,祖先类,直到找到对应的消息方法。 procedure Tobject.Dispatch(var Message) begin 搜索Handler; 如果没有找到就调用DefaultHandler(var Message); end; ======================================================== 19:40 2005-12-9 1.发送消息的多种方法 1).给指定窗口发送消息 给窗口发送消息: function SendMessage(hWnd:HWND;Msg:UINT;wParam:WParam;lParam:LParam):LRESULT;stdcall; //That is, SendMessage does not return until the target control has handled the message, even if it //is in another thread. function DispatchMesage(const lpMsg:TMsg):Longint;stdcall; ------------------------------------------------------------------------------ function PostMessage(hWnd:HWND;Msg:UINT;wParam:WPARAM;lParam:LParam):Bool;stdcall; 2) 给应用程序发送消息: function SendAppMessage(Msg:Cardinal;wParam,lParam:Longint); 3) 给线程发送消息 function PostThreadMessage(idThread:DWORD;Msg:UINT;wParam:WPARAM;lParam:LPARAM):BOOL;stdcall; 获取idThread可用Api函数得: function GetWindowThreadProcessId(hWnd:HWND;lpdwProcessId:Pointer=nil):DWORD;stdcall;overload; function GetWindowThreadProcessId(hWnd:HWND;var dwProcessId:DWORD):DWORD;stdcall;overload; 今天郁闷啊,去玩魔兽输了两盘,好没面子,以后不玩了,魔兽bye了!虽然 你很完美,但是与你无缘了,但是我盼望着在未来的某一天我可以开发一款像你这 样的游戏!加油,加油,继续加油! ======================================================== 2005-12-12 1.字符串资源 resourcestring AuthorName = ’Marco Cantù’; BookName = ’Essential Pascal’; procedure TForm1.Button1Click(Sender: TObject); begin ShowMessage (BookName + #13 + AuthorName); end; 以上代码中的两个字符串将分两行输出显示,因为字符串被分行符 #13 隔开。 有趣的是,当你用资源编辑器打开执行文件时,你会在程序资源中看到你所定义的 字符串。这意味着字符串并没有进入编译代码,而是保存在执行文件 (EXE文件) 的一个单独区域。 注意:简而言之,采用资源的好处一方面可让Windows 来完成有效的内存处理,另 一方面不用更改源代码就可实现程序的本地化 (把字符串翻译成不同的语言)。 2.位操作 type TIntBitPos=0..31; //取得一个Integer中的某位 function GetBitOfInt(I:integer;BitPos:TIntBitPos):Boolean; begin result:=(I shr BitPos) and $1=1; end; //替换一个Integer的某位 procedure ReplaceBitOfInt(var I:integer;BitPos:TIntBitPos;NewValue:Boolean); var newV:Integer; begin if GetBitOfInt(I,BitPos)<>NewValue then begin if BitPos=0 then NewV:=1; else NewV:=2 shl (BitPos-1); if NewValue then Inc(I,NewV) else Dec(I,NewV); end; end; ======================================================== 11:27 2005-12-13 1.自定义系统唯一消息 UINT RegisterWindowMessage(LPCTSTR lpString); DWORD GetWindowThreadProcessId(HWND hWnd, LPDWORD lpdwProcessId ); 例子: 接收端 AppReceive var Id:dword; begin mainform.newOneMessage(var Msg:TMsg;var Handled:Boolean); begin if Msg.message=Id then begin showMessage(’应用程序收到消息’); handled:=true; end; end; mainform.formcreate(); begin caption:=’接受者’; ID:=RegisterWindowMessage(PChar(’OneOfAppMessage’)); end; mainform.WndProc(var Message:TMessage) override; begin inherited; with Message do if Msg=Id then begin showMessage(’主窗体收到消息’); result:=0; end; end; end; 发送端 AppSend var Id:dword; begin mainform.formcreate(); begin caption:=’发送者’; ID:=RegisterWindowMessage(Pchar(’OneOfAppMessage’)); end; mainform.button1Click(sender.Tobject); begin H:=FindWindow(nil,pchar(’接受者’)); if H<>0 then begin sendMessage(H,ID,0,0); PostThreadMessage(GetWindowThreadProcessId(H),ID,0,0); end; end; end; 本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/pottos/archive/2007/03/13/1528397.aspx |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论