A. 谁能给我讲一讲windows消息机制qq:524730955
Windows操作系统最大的特点就是其图形化的操作界面,其图形化界面是建立在其消息处理机物州制这个基础之上的。如果不理解Windows消息处理机制,肯定无法深入的理解Windows编程。可惜很多程序员对Windows消息只是略有所闻,对其使用知之甚少,更不了解其内部实现原理,本文试着一步一步向大家披露我理解的Windows消息机制。可以说,掌握了这一部分知识,就是掌握了Windows编程中的神兵利器,灵活运用它,将会极大的提高我们的编程能力。
一、 消息概述
Windows窗体是怎样塌蚂纤展现在屏幕上的呢?众所周知,是通过API绘制实现的。Windows操作系统提供了一系列的API函数来实现界面的绘制功能,例如:
DrawText绘制文字
DrawEdge绘制边框
DrawIcon绘制图标
BitBlt 绘制位图
Rectangle绘制矩形
…
再复杂的程序界面都是通过这个函数来实现的。
那什么时候调用这些函数呢?显然我们需要一个控制中心,用来进行“发号施令”,我们还需要一个命令传达机团仿制,将命令即时的传达到目的地。这个控制中心,就是一个动力源,就像一颗心脏,源源不断地将血液送往各处。这个命令传达机制就是Windows消息机制,Windows消息就好比是身体中的血液,它是命令传达的使者。
Windows消息控制中心一般是三层结构,其顶端就是Windows内核。Windows内核维护着一个消息队列,第二级控制中心从这个消息队列中获取属于自己管辖的消息,后做出处理,有些消息直接处理掉,有些还要发送给下一级窗体(Window)或控件(Control)。第二级控制中心一般是各Windows应用程序的Application对象。第三级控制中心就是Windows窗体对象,每一个窗体都有一个默认的窗体过程,这个过程负责处理各种接收到的消息。
消息是以固定的结构传送给应用程序的,结构如下:
Public Type MSG
hwnd As Long
message As Long
wParam As Long
lParam As Long
time As Long
pt As POINTAPI
End Type
其中hwnd是窗体的句柄,message是一个消息常量,用来表示消息的类型,wParam和lParam都是32位的附加信息,具体表示什么内容,要视消息的类型而定,time是消息发送的时间,pt是消息发送时鼠标所在的位置。
Windows操作系统中包括以下几种消息:
1、标准Windows消息:
这种消息以WM_打头。
2、通知消息
通知消息是针对标准Windows控件的消息。这些控个包括:按钮(Button)、组合框(ComboBox)、编辑框(TextBox)、列表框(ListBox)、ListView控件、Treeview控件、工具条(Toolbar)、菜单(Menu)等。每种消息以不同的字符串打头。
3、自定义消息
编程人员还可以自定义消息。
二、 关于Windows句柄
不是每个控件都能接收消息,转发消息和绘制自身,只有具有句柄(handle)的控件才能做到。有句柄的控件本质上都是一个窗体(window),它们可以独立存在,可以作为其它控件的容器,而没有句柄的控件,如Label,是不能独立存在的,只能作为窗口控件的子控件,它不能绘制自身,只能依靠父窗体将它绘制来。
句柄的本质是一个系统自动维护的32位的数值,在整个操作系统的任一时刻,这个数值是唯一的。但该句柄代表的窗体释放后,句柄也会被释放,这个数值又可能被其它窗体使用。也就是说,句柄的数值是动态的,它本身只是一个唯一性标识,操作系统通过句柄来识别和查找它所代表的对象。
然而,并非所有的句柄都是窗体的句柄,Windows系统中还中很多其它类型的句柄,如画布(hdc)句柄,画笔句柄,画刷句柄,应用程序句柄(hInstance)等。这种句柄是不能接收消息的。但不管是哪种句柄,都是系统中对象的唯一标识。本文只讨论窗体句柄。
那为什么句柄使窗口具有了如此独特的特性呢?实际是都是由于消息的原因。由于有了句柄,窗体能够接收消息,也就知道了该什么时候绘制自己,绘制子控件,知道了鼠标在什么时候点击了窗口的哪个部分,从而作出相应的处理。句柄就好像是一个人的身份证,有了它,你就可以从事各种社会活动;否则的话,你要么是一个社会看不到的黑户,要么跟在别人后面,通过别人来证明你的存在。
三、 消息的传送
1、从消息队列获取消息:
可以通过PeekMessage或GetMessage函数从Windows消息队列中获取消息。Windows保存的消息队列是以线程(Thread)来分组的,也就是说每个线程都有自己的消息队列。
2、发送消息
发送消息到指定窗体一般通过以下两个函数完成:SendMessage和PostMessage。两个函数的区别在于:PostMessage函数只是向线程消息队列中添加消息,如果添加成功,则返回True,否则返回False,消息是否被处理,或处理的结果,就不知道了。而SendMessage则有些不同,它并不是把消息加入到队列里,而是直接翻译消息和调用消息处理,直到消息处理完成后才返回。所以,如果我们希望发送的消息立即被执行,就应该调用SendMessage。
还有一点,就是SendMessage发送的消息由于不会被加入到消息队列中,所以通过PeekMessage或GetMessage是不能获取到由SendMessage发送的消息。
另外,有些消息用PostMessage不会成功,比如wm_settext。所以不是所有的消息都能够用PostMessage的。
还有一些其它的发送消息API函数,如PostThreadMessage,SendMessageCallback,SendMessageTimeout,SendNotifyMessage等。
四、 消息循环与窗体过程
消息循环是应用程序能够持续存在的根本原因。如果循环退出,则应用程序就结束了。
我们来看一看Delphi中封装的消息循环是怎样的:
第一步:程序开始运行(Run)
Application.Initialize;//初始化
Application.CreateForm(TForm1, Form1);//创建主窗体
Application.Run;//开始运行,准备进行消息循环
如果不创建主窗体,应用程序同样可以存在和运行。
第二步:开始调用消息循环(HandleMessage)
procere TApplication.Run;
begin
FRunning := True;
try
AddExitProc(DoneApplication);
if FMainForm <> nil then
begin
case CmdShow of
SW_SHOWMINNOACTIVE: FMainForm.FWindowState := wsMinimized;
SW_SHOWMAXIMIZED: MainForm.WindowState := wsMaximized;
end;
if FShowMainForm then
if FMainForm.FWindowState = wsMinimized then
Minimize else
FMainForm.Visible := True;
Repeat //注:循环开始
try
HandleMessage;
except
HandleException(Self);
end;
until Terminated;//循环结束条件
end;
finally
FRunning := False;
end;
end;
第三步:消息循环中对消息的处理。
procere TApplication.HandleMessage;
var
Msg: TMsg;
begin
if not ProcessMessage(Msg) then Idle(Msg);
end;
function TApplication.ProcessMessage(var Msg: TMsg): Boolean;
var
Handled: Boolean;
begin
Result := False;
if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
begin
Result := True;
if Msg.Message <> WM_QUIT then
begin
Handled := False;
if Assigned(FOnMessage) then FOnMessage(Msg, Handled);
if not IsHintMsg(Msg) and not Handled and not IsMDIMsg(Msg) and
not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end
else
FTerminate := True;
end;
end;
窗体过程实际上是一个回调函数。所谓的回调函数,实际上就是由Windows操作系统或外部程序调用的函数。回调函数一般都有规定的参数格式,以地址方式传递给调用者。窗口过程中是Windows操作系统调用了,在一个窗口创建的时候,在分配窗体句柄的时候就需要传入回调函数地址。那为什么我们平时编程看不到这个回调函数呢?这是由于我们的编程工具已经为我们生成了默认的窗体过程,这个过程的要做的事情就是判断不同的消息类型,然后做出不同的处理。例如可以为键盘或鼠标输入生成事件等。
五、 消息与事件
事件本质上是对消息的封装,是IDE编程环境为了简化编程而提供的有用的工具。这个封装是在窗体过程中实现的。每种IDE封装了许多Windows的消息,例如:
事件
消息
OnActivate
WM_ACTIVATE
OnClick
WM_XBUTTONDOWN
OnCreate
WM_CREATE
OnDblClick
WM_XBUTTONDBLCLICK
OnKeyDown
WM_KEYDOWN
OnKeyPress
WM_CHAR
OnKeyUp
WIN_KEYUP
OnPaint
WM_PAINT
OnResize
WM_SIZE
OnTimer
WM_TIMER
了解了这一点后,我们完成可以封装自己的事件。
通过上面的介绍,相信各位已经对Windows消息机制有了一定的理解了。通过Windows消息编程,我们不但可以实现很多常规功能,而且可以实现很多IDE类库没有提供的功能;另外,我们还可以通过消息钩子,对消息进行截获,改变其默认的处理函数,从而突破平台或软件功能的限制,极大的扩展程序的功能;我们还可以修改默认的窗体过程,按自己的要求来响应消息;或者自定义消息,实现程序之间的即时通讯等等。通过更加深入的学习,我们还会接触到更多与Windows消息机制相关其它Windows相对比较底层的知识,如果能够这样,蓦然回首,你会发现自己原来离“高手”不远了。
B. windows消息机制是怎么一回事谢谢!
Windows的消息系统是由3个部分组成的:
· 消息队列。Windows能够为所有的应用程序维护一个消息队列。应用程序必须从消息队列中获取
消息,然后分派给某个窗口。
· 消息循环。通过这个循环机制应用程序从消息队列中检索消息,再把它分派给适当的窗口,然
后继续从消息队列中检索下一条消息,再分派给适当的窗口,依次进行。
· 窗口过程。每个窗口都有一个窗口过程来接收传递给窗口的消息,它的任务就是获取消息然后
响应它。窗口过程是一个回调函数;处理了一个消息后,它通常要返回一个值给Windows。
注意回调函数是程序中的一种函数,它是友败由Windows或外部模块调用的。
一个消息从产生到被一个窗口响应,其中有5个步骤:
1) 系统中发生了某个事件。
2) Windows把这个事件翻译为消息,然后把它放到消息队列中。
3) 应用程序从消息队列中接收到这个消息,把它存放在TMsg记录中。
4) 应用程序把消息传递给一个适当的窗口的窗口过程。
5) 窗口过程响应这个消息并进行处理。
步骤3和4构成了应用程序的消息循环。消息循环往往是Windows应用程序的核心,因为消息循环
使一个应用程序能够响应外部的事件。消息循环的任务就是从消息队列中检索消息,然后把消息传递给适当的窗口。如果消息队列中没有消息,Windows就允许其他应用程序处理它们的消息。
Windows操作系统最大的特点就是其图形化的操作界面,其图形化界面是建立在其消息处理机制这个基础之上的。如果不理李悄解Windows消息处理机制,肯定无法深入的理解Windows编程。可惜很多程序员对Windows消息只是略有所闻,对其使用知之甚少,更不了解其内部实现原理,本文试着一步一步向大家披露我理解的Windows消息机制。可以说,掌握了这一部分知识,就是掌握了Windows编程中的神兵利器,灵活哪告渣运用它,将会极大的提高我们的编程能力。
C. 请问如何简述windows消息机制
Windows的应用程序一般包含窗口(Window),它主要为用户提供一种可视化的交互方式,窗口是总是在某个线程(Thread)内创建团信的。Windows系统通过消息机制来管理交互,消息(Message)被发送,保存,处理,一个线程会维护自己的一套消息队列(Message Queue),以保持线程间的独占性。队列的特点无非是先进先出,这种机制可以实现一种异步的需求响应过程。
如何发送消息?
消息的发送终归通过函数调用,比较常用的有PostMessage(),SendMessage(),另外还有一些Post*或Send*的函数。函数的调用者即发送消息的人。
谁来发送消息?硬件输入是如何被响应的?
消息可以由Windows系统发送,也可以由应用程序本身;可以向线程内发送,也可以夸线程。主要是看发送函数的调用者。
对于硬件消息,Windows系统启动时会运行一个叫Raw Input Thread的线程,简称RIT。这个线程负责处理System Hardware Input Queue(SHIQ)里面的消息,这些消息由硬件驱动发送。RIT负责把SHIQ里的消息分发到线程的消息队列里面,那RIT是如何知道该发给谁呢?如果是鼠标事件,那就看鼠标指针所指的塌雀轮窗口属于哪个线程,如果是键盘那就看哪个窗口当前是激活的。一些特殊的按键会有所不同,比如 Alt+Tab,Ctrl+Alt+Del等,RIT能保证他们不受当前线程的影响而死锁。RIT只能同时和一个岁袭线程关联起来。
D. windows消息机制流程
(1)操作系统接收到应用程序的窗口消息,将消息投递到该应用程序的消息队列中。 (2)应用程序在消息循环中调用GetMessage函数从消息队列中取出一条一条的消息。取出消息后,应用程序可以对消息进行一些预处理,例如,放弃对某些消息的响应,或者调用TranslateMessage产生新的消息。 (3)应用程序调用DispatchMessage,将消息回传给操作系统。消息是由MSG结构体对象来表示的,其中就包含了接收消息的窗口的句柄。因此,DispatchMessage函数总能进行正确的传递。 (4)系统利用WNDCLASS结构体的lpfnWndProc成员保液手存的窗口过程函数的指针调用窗口过稿慧程,对消息进行处理(即“系统给应用程序发送了消息”)。 注意:消息映射为什么不是虚函数呢? 答:C++有一个名为vtable的虚函数分发表。如果用虚函数发送消息CWnd将为超过100个消息来申明虚函数,对于没个虚函数,vtable中对应有4个字节,那么应用程序将需要400多个字节的表来支持虚拟消息处理函数。所以为了避免大型键埋答的vtable,MFC使用宏来把WINDOWS消息连接到C++成员函数。MFC消息处理程序需要函数原型,函数体和在消息映射中的输入项(宏调用),ClassWizard帮助我们将消息处理程序添加到类中。
E. 理解windows消息机制
消息机制是Windows应用程序的核心。在Windows中发生的一切都可以用消息来表示,消息用于告诉操作系统发生了什么,所有的Windows应用程序都是消息驱动的,
在Windows中,不同的消息由应用程序的不同部分进行处理。MFC库将很多底层的消息都屏蔽了,使用户更加方便、简易地处理消息。例如,用户接收到诸如移动鼠标键(WM_MOUSEMOVE)消息或单击鼠标键(WM_LRBUTTONDOWN)消息时不必处理窗口和鼠标的重画工作,MFC及应用程序框架会替用户做这些工作。在使用MFC进行编程时,用户只需处理一些高层的消息,例如,“用户在单击窗口中的OK按扭”,“用户现在选中了下拉列表框中的第五项”等等,这样就大大减轻了程序员的负担。
一个消息是由消息的名称(UINT)和两个参数(WPARAM, LPARAM)组成。消息的参数中镇笑敬包含有重要的信息。例如对鼠标消息而言,LPARAM中一般包含鼠标的位置信息,而WPARAM参数中包含了发生该消息时,SHIFT、CTRL等键的状态信息,对于不同的消息类型来说,两个参数也都相应地具有明确意义。
消息与输入焦点
Windows是一个以消息为导向的系统,应用程序只能被动地等待用户按键的消息,不能主动地去读键盘的状态,也就是说,每当键盘上有个键被按下,系统就会发出一个按键消息给窗口,告诉它某个键被按下去了,只要鼠标移动一下,系统也会发出相应的消息,并把鼠标的坐标信息传给窗口。
Windows可以同时执行许多程序,但键盘只有一个,怎么判断由哪个窗口接收键盘及鼠标的消息呢?采用“输入焦点”(inpuut focus)技术可以解决这个问题。只要某个窗口取得输入焦点,它不但会被提升到屏幕的最前面,颜色也会有所不同,所有的键盘消息就会导向该窗口,该窗口也成为“活动窗口”。
窗口如何取得输入焦点?通常被鼠标单击的窗口会得到输入焦点,除此之外,程序本身也可以利用SetFocus()来指定哪个窗口拥有输入焦点。
CWnd* CWnd::SetFocus();
如果调用某窗口的SetFocus()成员函数,该窗口就可以取得输入焦点,该函数返回前一个拥有输入焦点的窗口。
如果某个窗口的输入焦点被抢走,Windows系统就会发出WM_KILLFOCUS消息御慎给这个失去输入焦点的窗口,同时还会告诉该窗口下一个取得输入焦点的窗口的指针。而获得输入焦点的窗口则会收到WM_SETFOCUS消息。
消息响应函数分别为:
afx_msg void OnKillFocus(CWnd* pNewWnd);
其中的参数为得到输入焦点的窗口的指针。
Afx_msg void OnSetFocus(CWnd* pOldWnd);
其中的参数为失去输入焦点的窗口的指升闭针。
消息的分类
Windows系统预定义了许多消息,每个消息都拥有一个宏定义,即用形象的字符串来标识消息,一系列#define 语句将消息与特定数值联系起来,可以在头文件WinUser.h中找到这些宏定义,例如
#define WM_PAINT 120
可以在程序中通过消息名“WM_PAINT”来访问它。其他消息如:
#define WM_MOUSEMOVE 0x0200
#define WM_LBUTTONDOWN 0x0201
#define WM_LBUTTONUP 0x0202
#define WM_LBUTTONDBLCLK 0x0203
#define WM_RBUTTONDOWN 0x0204
#define WM_RBUTTONUP 0x0205
#define WM_RBUTTONDBLCLK 0x0206
#define WM_MBUTTONDOWN 0x0207
#define WM_MBUTTONUP 0x0208
#define WM_MBUTTONDBLCLK 0x0209
系统定义的消息有不同的前缀,不同的前缀有不同的含义。