分享
 
 
 

深入Delphi (三)

王朝delphi·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

深入Delphi (三)

Windows 消息机制

by machine

大家是不是很奇怪为什么我还没说到Delphi的控件呢?不过不用着急,有关深入控件的内容,将会很快出现了,但在这之前,还得了解Windows图形界面程序的机制——Windows的消息机制。

使用过Delphi的朋友都知道,Delphi是一个真正面向对象的编程环境,但是不但如此,Delphi的这种面向对象的机制是单纯的建立在Windows的消息机制上的Delphi代码,而不是像VB、VFP之类的调用DLL、OCX,通过查看Delphi控件的源代码,你可以知道整个机制是怎样组织起来的,而且你可以完完全全地控制这些控件,因为它们只是用Delphi代码编写的,而不是存在在DLL中看不见的东西。

那么Windows所谓的消息(Message)机制到底是什么呢?还记得以前学Basic的时候,根本没有什么事件之类的东西,整个程序是用流程图来描绘的,在程序需要键盘输入的地方,整个程序就停下来等待输入,然后根据输入来做不同的事情。这样做本来是没什么问题的,但是到了图形界面的时候,情况不同了,鼠标输入成了一个很大的问题,而且在Windows这样的多任务系统下,不可能让一个程序不断的测试设备状态那样子来获得输入。

总之呢,Windows下的消息机制是完全不同的,即使与单纯的中断事件比起来,还是有很多不同的地方。

简单的说来,一个线程在创建窗体的时候,会自动生成一个消息队列,但窗体不是必要的,可以通过其他途径来创建消息队列。然后,其他的程序,或者Windows系统本身,可以向这个线程发送消息到它的消息队列中,通知这个线程有什么东西发生了,这就是所谓的事件。每个进程都可以使用GetMessage函数获得它的消息队列中最前面的一个消息,GetMessage同时会自动将此消息从消息队列中删除掉,当然也可以指定不删除消息,这个以后再说。光听可能还是不能想象出来,那就看以下的例子吧:

program Sample3;

uses

Windows,

Messages;

var Msg: TMsg;

begin

PostMessage(0, WM_USER, 0, 0); // 首先强制生成消息队列

PeekMessage(Msg, 0, WM_USER, WM_USER, 0);

// 然后这里就是所谓的消息循环,只有当收到WM_QUIT的消息时,GetMessage()才会返回False

while GetMessage(Msg, 0, 0, 0) do begin

end;

// 这里可以做程序结束前(收到WM_QUIT后)的工作

end.

这个程序运行后不会干任何事情,同时也会忽略一切Windows发给它的消息,除了WM_QUIT之外,因为GetMessage这个函数有一个特点,当收到其他消息的时候,GetMessage的返回值是TRUE,而在收到WM_QUIT的时候返回值则为FALSE,因此消息循环就被打破了。在Windows关闭的时候,Windows会自动发一个WM_QUIT的消息到这个程序的主线程,然后程序就退出了。

绝大多数的程序的主体就是这个样子,都有一个消息循环,也就是说,每一个程序都是不断的使用GetMessage尝试获得新的消息,然后处理,周而复始,直到收到WM_QUIT为止。而其中高明之处,就是GetMessage在被调用的时候,如果检查出消息队列中没有消息,则函数不会马上返回,而是使线程转入睡眠状态,因而线程不会因为不断的循环而浪费CPU时间。在有新的消息收到之后,线程会重新苏醒,GetMessage把收到的消息放到一个TMsg类型的参数里面返回,于是程序就可以处理这个消息了。

好了,这个消息机制是如何和窗体程序结合在一起的呢?换句话说,如果程序生成了窗体,那么程序又如何通过这个消息机制获取用户的输入消息呢?这就要从创建窗体的过程说起了。以下是一个比较复杂一点的例子:

program Sample4;

uses

Windows,

Messages;

var

Msg: TMsg;

wc: TWndClass; // RegisterClass()所需要的参数

hWnd: THandle; // 主窗体的句柄

const

ClassName = 'MainWClass';

function MainWndProc(Handle: THandle; MsgID: UINT; wParam, lParam: Integer): LRESULT; stdcall;

begin

Result := 1;

case MsgID of

WM_CLOSE: begin // 关闭窗体所产生的消息

if MessageBox(Handle,

'要关闭这个程序吗?',

'例子程序-4',

MB_ICONQUESTION or MB_YESNO

) = IDYES then

DestroyWindow(hWnd)

else

Result := 0;

Exit;

end;

WM_DESTROY: begin // DestroyWindow()所产生的消息

PostQuitMessage(0);

end;

end;

// 剩下的消息交给Windows预设的处理函数就可以了,比如画窗体的WM_NCPAINT消息等

Result := DefWindowProc(Handle, MsgID, wParam, lParam);

end;

begin

// 首先使用RegisterClass()注册窗体的类,这可不是Delphi数据类型中的类哦!

wc.style := CS_HREDRAW or CS_VREDRAW;

wc.lpfnWndProc := @MainWndProc; // 消息处理函数的地址

wc.hInstance := hInstance; // 程序的句柄,同时也是基地址

wc.hIcon := LoadIcon(0, PChar(IDI_APPLICATION));

wc.hCursor := LoadCursor(0, IDC_ARROW); // 图标

wc.hbrBackground := GetStockObject(WHITE_BRUSH); // 背景画刷

wc.lpszClassName := ClassName; // 前面定义的常量

if RegisterClass(wc) = 0 then Halt(0);

hWnd := CreateWindowEx(0,

ClassName, // 刚才注册的类的名称

'Sample', // 窗体的标题

WS_OVERLAPPEDWINDOW, // 窗体有标题栏、系统菜单、最大小化菜单,以及拉伸边框

Integer(CW_USEDEFAULT),

Integer(CW_USEDEFAULT),

Integer(CW_USEDEFAULT),

Integer(CW_USEDEFAULT),

0,

0,

hInstance,

nil

);

if hWnd = 0 then Halt(0);

ShowWindow(hWnd, CmdShow);

UpdateWindow(hWnd);

while GetMessage(Msg, 0, 0, 0) do begin

TranslateMessage(Msg);

DispatchMessage(Msg); // 该API将消息分派到相应的窗体消息处理函数

end;

ExitCode := Msg.wParam;

end.

由于要说的东西比较多,其中的API说明、定义就请各位自己查看SDK了。在创建窗体之前,首先需要向Windows注册窗体的类。所谓的注册窗体类,就是要填充一个TWndClass结构的数据,设定这个类的属性,然后传递给RegisterWindowClass()。在这些属性当中,就包括这个类的窗体消息处理函数的指针,然后还有这个类的名称。在用CreateWindowEx创建主窗体的时候,就可以根据类的名称创建这个类的窗体了。那么窗体消息处理函数是用来干什么的呢?

需要注意的是,一个线程可以创建多个窗体,这些窗体可以是你的程序创建的,也可能是Windows在你的程序运行过程当中创建的,比如用户点击窗体左上角的系统图标时Windows会生成一个系统菜单——在Windows里,所有你能看到的东西,包括编辑框、按钮这些东西都叫做窗体,并非程序主窗体、子窗体才叫做窗体的哦!那么问题也就随之而来了,一个线程中消息队列只有一个,但窗体有这么多,如果所有消息都在主程序的消息循环中处理,那么编写大的程序将非常困难,而且结构混乱的程序维护起来也很麻烦。于是这个窗体消息处理函数(Window Procedure)就起作用了,由于可以为每一个类编写消息处理函数,因此只需要在收到相应窗体的消息的时候,把消息传递给相应类的消息处理函数处理就可以了,整个程序就变得十分结构化。程序中甚至不需要记录每一个窗体对应的类消息处理函数是哪一个,直接调用DispatchMessage() Windows就会自动使用相应的消息处理函数了。

另一个函数DefWindowPro()也是一个关键之处,试一下把这一行省略,看看程序运行后有什么效果?事实上,这样做之后,甚至连窗体你都不会看得到,Windows只是为你所创建的窗体在屏幕上保留了一个位置。为什么呢?你的程序并没有画窗体啊!别以为创建了窗体之后Windows就会为你完成一切,其实是DefWindowProc()处理了WM_PAINT和WM_NCPAINT的消息,完成了画窗体的工作,DefWindowProc为一般的程序主窗体做了很多这样的幕后工作哦!也由此可见,消息并不只是用户鼠标、键盘的输入消息,而是程序和Windows系统的联系工具,以后就会知道,消息还有很多用处呢,而编写Delphi控件很多时候也离不开消息。好了,这一章就讲到这里吧。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有