http://www.txsz.net/xs/delphi/3/Windows%20%E6%B6%88%E6%81%AF%E6%9C%BA%E5%88%B6.htm

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( , WM_USER, , ); // 首先强制生成消息队列
PeekMessage( Msg, , WM_USER, WM_USER, ); // 然后这里就是所谓的消息循环,只有当收到WM_QUIT的消息时,GetMessage()才会返回False
while GetMessage( Msg, , , ) 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 := ;
case MsgID of
WM_CLOSE :
begin // 关闭窗体所产生的消息
if MessageBox( Handle, '要关闭这个程序吗?', '例子程序-4', MB_ICONQUESTION or
MB_YESNO ) = IDYES then
DestroyWindow( hWnd )
else
Result := ;
Exit;
end;
WM_DESTROY :
begin // DestroyWindow()所产生的消息
PostQuitMessage( );
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( , PChar( IDI_APPLICATION ) );
wc.hCursor := LoadCursor( , IDC_ARROW ); // 图标
wc.hbrBackground := GetStockObject( WHITE_BRUSH ); // 背景画刷
wc.lpszClassName := ClassName; // 前面定义的常量
if RegisterClass( wc ) = then
Halt( );
hWnd := CreateWindowEx( , ClassName, // 刚才注册的类的名称
'Sample', // 窗体的标题
WS_OVERLAPPEDWINDOW, // 窗体有标题栏、系统菜单、最大小化菜单,以及拉伸边框
Integer( CW_USEDEFAULT ), Integer( CW_USEDEFAULT ),
Integer( CW_USEDEFAULT ), Integer( CW_USEDEFAULT ), , , hInstance, nil );
if hWnd = then
Halt( );
ShowWindow( hWnd, CmdShow );
UpdateWindow( hWnd );
while GetMessage( Msg, , , ) 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控件很多时候也离不开消息。

深入Delphi -- Windows 消息机制的更多相关文章

  1. 转:Windows消息机制要点

    Windows消息机制要点 1. 窗口过程     每个窗口会有一个称为窗口过程的回调函数(WndProc),它带有四个参数,分别为:窗口句柄(Window Handle),消息ID(Message ...

  2. Windows消息机制

    Windows的消息系统是由3个部分组成的: · 消息队列.Windows能够为所有的应用程序维护一个消息队列.应用程序必须从消息队列中获取消息,然后分派给某个窗口.· 消息循环.通过这个循环机制应用 ...

  3. 我对windows消息机制的理解(参考深入浅出MFC,欢迎批评指正!!)

    以消息为基础,以事件驱动之 程序的进行依靠外部消息来驱动,即:程序不断等待任何可能的输入,然后做判断,然后再做适当的处理. 消息输入:操作系统捕获,以消息形式进入程序.(操作系统通过其USERS模块中 ...

  4. <Win32_1>深入浅出windows消息机制[转自crocodile_]

    上学期学习了Java ,感觉Java写一个窗口真心简单,很易上手,也就难怪很多开发人员选择Java作为自己的开发编程语言.但是由于自身对windows的热爱,让我觉得c.c++语言才是我亲睐的编程语言 ...

  5. 收藏:Windows消息机制

    百度百科介绍的windows消息机制也不错:http://baike.baidu.com/view/672379.htm Windows的应用程序一般包含窗口(Window),它主要为用户提供一种可视 ...

  6. windows消息机制(转)

    1. 引言Windows 在操作系统平台占有绝对统治地位,基于Windows 的编程和开发越来越广泛.Dos 是过程驱动的,而Windows 是事件驱动的[6],这种差别的存在使得很多Dos 程序员不 ...

  7. 深入理解windows 消息机制

    深入理解Windows消息机制 今天我们来学一学Windows消息机制,我们知道在传统的C语音程序中,当我们需要打开一个文件时,我们可以调用fopen()函数,这个函数最后又会调用操作系统提供的函数以 ...

  8. windows消息机制框架原理【简单版本】

    windows消息机制框架原理 结合两张图理解 窗口和窗口类 Windows UI 应用程序 (e) 具有一个主线程 (g).一个或多个窗口 (a) 和一个或多个子线程 (k) [工作线程或 UI 线 ...

  9. Windows消息机制要点

    1. 窗口过程     每个窗口会有一个称为窗口过程的回调函数(WndProc),它带有四个参数,分别为:窗口句柄(Window Handle),消息ID(Message ID),和两个消息参数(wP ...

随机推荐

  1. python+selenium环境搭建

    这里主要基于windows平台. 下载python.http://python.org/getit/ 下载setuptools [python的基础包工具].http://pypi.python.or ...

  2. SQL跨数据库复制表数据

    SQL跨数据库复制表数据   不同服务器数据库之间的数据操作 不同数据库之间复制表的数据的方法: 当表目标表存在时: insert into 目的数据库..表 select * from 源数据库.. ...

  3. 1005acm罚时

    ACM国际大学生程序设计竞赛是由国际计算机学会主办的,一项旨在展示大学生创新能力.团队精神和在压力下编写程序.分析和解决问题能力的年度竞赛.参赛队伍最多由三名参赛队员组成,竞赛中一般命题10-13题, ...

  4. 阿里巴巴2013年实习生笔试题B

    阿里巴巴集团2013实习生招聘技术类笔试题(B) 一.单向选择题 1.在常用的网络协议中,___B__是面向连接的.有重传功能的协议. A. IP B. TCP C. UDP D. DXP 2.500 ...

  5. 在logopond中看到的优秀设计随想

    本随笔仅仅只是自己对于设计作品的想法,不喜勿喷~ 昨日看到关于大神配色的文章,决定在logopond网站中看看优秀的作品,以为自己的配色找找灵感,学习学习,对自己有很强的震撼力的有: 以女性高跟性的抽 ...

  6. linq性能剖析

    Orcas(VS2008&Framework3.5)给我们带来了很多令人兴奋的新特性,尤其是LINQ的引进,可以说方便了一大批开发 人员和框架设计人员.过去,当我们使用O/RMapping的一 ...

  7. 【boost】BOOST_LOCAL_FUNCTION体验

    c++11里支持使用lambda在函数内定义本地嵌套函数,将一些算法的判断式定义为本地函数可以使代码更加清晰,同时声明和调用靠近也使得更容易维护.遗憾的是公司开发平台任然停留在vs2008,使用boo ...

  8. 用C#.NET调用Java开发的WebService传递int,double问题

    用C#.NET调用Java开发的WebService时,先在客户端封装的带有int属性的对象,当将该对象传到服务器端时,服务器端可以得到string类型的属性值,却不能得到int类型.double和D ...

  9. 【原创】Docker容器及Spring Boot微服务应用

    Docker容器及Spring Boot微服务应用 1 什么是Docker 1.1 Docker的出现 问题一:项目实施环境复杂问题 传统项目实施过程中经常会出现“程序在我这跑得好好的,在你那怎么就不 ...

  10. Azure杂七杂八系列(二) - 如何在Azure上重新配置VM

    我们经常遇到这样的问题,  对于已经建立的VM进行性能提升, 比如需要更好的虚拟机或者需要迁移到其他的虚拟网络 那么我们可以使用以下的方法进行修改. 1. 如图所示, TESTVMXX位于North ...