最近到图书馆里借了一本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