转述高手教的消息的基本知识,通俗易懂,---请高手进来指出错误所在

94120144 2003-09-30 09:13:35
(
注:名字定义不准确,请按感觉看我写的东东,由于写得含糊,不会误导谁,高手不用骂,只需要用平和的语气指出我说错在哪就可以了;
另: 我的习惯把函数说成方法
要有C++的知识才能看懂
如果想看到下列描述,从学习书本上打开一个最简单的HELLO WORLD实例, 用F11键单步调,就可以看到下面我的描述的执行过程
)


一个消息是一个结构的实例对象,是什么样的结构可以在帮助里面找到,这里暂时不关心.

很多个消息排成队,成了消息队列.

任何一个WINDWOS进程(即一个可执行文件)有一个主线程,和可能有的多个工作线程以及多个用户线程(一个用户线程直观地看,就是一个窗口);

主线程或者用户线程里面有一个RUN()方法,里面有一个死循环,这个死循环不停地去查上述的那个消息队列中是否有消息.如果有,就PEEK这个消息(PEEK意思是把这个消息拿过来,准备处理它)

PEEK完后,这个死循环如果觉得这个消息没有什么疑意义,它就PUMP(处理)这个消息

PUMP的过程:
1. 先GET这个消息.(GET与PEEK相比是这样的,PEEK得到消息后,没有从上述的消息队列中把消息删除,但是GET得到消息后,把这个消息从队列中删除了.)

2. GET这个消息之后,先试着用快速处理的方法看是否可以把这个消息搞定(用PreTranslateMessage()方法来快速处理),如果搞不定------

3. 把消息中的快捷键解释成普通的字各符;

4. 把消息分发:就是去找相应的回调函数来响应(比如用户定义的ON_LEFT_DOWN,ON_KEY_DOWN之类的东西)

<什么是回调函数? 大家最好发贴问一下, 或者看下述: 回调函数是一些函数, 这些函数在主线程或者应用线程建立一个窗口的时候, 每个函数把指向自己的一个指针在这个窗口中注册, 这个窗口是归WINDOWS管理的,我们暂时把管理这个窗口的那些WINDOWS函数称为管理员. 当一个消息被传给WINDOWS的管理员的时候, 这个管理员到上述指针列表里面去找合适的响应函数的指针,某个指针被找到后,该指针去调用所指向的函数,该函数用传来的消息作为参数,称为回调>

**********************************************

谢谢观看,请顶一下,确保高手可以随时看到并进来指教,

要UP哦
...全文
41 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
会思考的草 2003-09-30
  • 打赏
  • 举报
回复
第一个问题的回答是我自己写的,第二个问题是参照前人的书和自己的理解写的。
会思考的草 2003-09-30
  • 打赏
  • 举报
回复
1.每个GUI线程都有一个消息队列。
2.消息的产生:
鼠标单击->鼠标驱动截获->生成消息放入系统队列->GetMessage/PeekMessage扫描系统队列->获取当前任务应该处理的消息->放入线程消息队列。
94120144 2003-09-30
  • 打赏
  • 举报
回复
会思考的草所贴的内容,

可能由于原作者著时缺乏耐心, 后面内容不连续,看不下去了,唉
94120144 2003-09-30
  • 打赏
  • 举报
回复
按照会思考的草的说法,似乎是每个窗口有一个自己的消息队列,这个消息队列是其它软件发来的.
所有的窗口共用一个系统队列,这个系统队列的消息是由硬件传递来的.

对不对?????

见下:
2.对于消息的处理,Windows中其实有两个队列用于此目的,它们分别是系统队列和消息队列。每个要处理消息的窗口都有自己的一个消息队列。在系统队列中的事件仅仅是硬件事件的记录,GetMessage/PeekMessage会从这些事件中创建消息,并确定哪一个窗体将接收这个消息。
3.鼠标单击事件的过程:
94120144 2003-09-30
  • 打赏
  • 举报
回复
可是我已经搜索过历史贴子了,没有找到合适的贴子
gully 2003-09-30
  • 打赏
  • 举报
回复
请问如何将消息传入系统队列?
比如将WM_DEVICECHANGE传给windows 强制使其刷新,这种方法行得通吗?
会思考的草 2003-09-30
  • 打赏
  • 举报
回复
2.对于消息的处理,Windows中其实有两个队列用于此目的,它们分别是系统队列和消息队列。每个要处理消息的窗口都有自己的一个消息队列。在系统队列中的事件仅仅是硬件事件的记录,GetMessage/PeekMessage会从这些事件中创建消息,并确定哪一个窗体将接收这个消息。
3.鼠标单击事件的过程:
首先,将计算该事件屏幕坐标的相应窗体。此计算(调用窗体点击测试)以桌面窗体开始,从头至尾的扫描细统中的每一个窗体(包括子窗体),直到找到一个包含这个鼠标坐标点的窗体,并且这个窗体没有任何同样包含这个坐标点的子窗体。
如果一个窗体使用SetCapture捕获鼠标,那么“系统队列扫描”代码将通过普通的点击测试,并将所有的鼠标消息返回到捕获的窗体。
如果这个被处理的事件是一个“鼠标键按下”事件(任何一个鼠标键),代码会检测这个事件是否会转化为双击事件。如果在两次鼠标键按下事件中,时间和距离的增量在允许的范围之中,该事件将会生成一个双击消息,否则它将生成一个标准的“按下”事件。所有的鼠标事件都将生成标准的鼠标消息,而双击测试只在鼠标事件指定的,包含CS_DBLCLKS类型的窗体中进行。
一个消息从鼠标事件中构造出来。
如果鼠标点击测试确定该事件发生在一个窗体的非客户区,如边框或标题栏,那么该构造出的消息映射到它相应的非客户区消息中。例如:一个WM_MOUSEMOVE事件会被映谢为WM_NCMOUSEMOVE消息。
与所有指定的消息过滤器进行对照,核查此消息,如果该消息不匹配过滤器,则重新从头开始“系统队列扫描”代码,查看队列中的下一个消息。
如果鼠标消息需要前往与当前任务不同的另一个任务的相关窗体,事件会被留在系统队列中,并且如果那个将会处理这个消息的任务在休眠之中,会被唤醒。这个新近被唤醒的任务不会在此刻立即运行,只会标记为准备运行,然后插入就绪队列中等待系统调度。如果消息前往了其它任务,并且在系统队列中没有要处理的事件被发现,“系统队列扫描”会代码返回到GetMessage/PeekMessage主代码。
如果安装了鼠标钩子,它将在此刻被调用。如果鼠标钩子返回了一个非零值,那么该鼠标事件被忽略,并从系统队列中被删除,然后重新从头开始“系统队列扫描”代码。如果钩子返回零,则继续处理。
如果消息是一个“鼠标键按下”消息,“系统队列扫描”则会在返回此消息之前,按照下面的方法激活窗体:沿着父链一直向上寻找该窗体的“最终顶层父窗体”,直到相遇,然后用SendMessage向该窗体的“最终顶层父窗体”发送一个WM_MOUSEACTIVATE消息。检查WM_MOUSEACTVATE的返回值,如果返回的值为空、MA_ACTIVATE或者MA_ACTIVATEANDEAT,ActivateWindow函数将被调用去激活那个“最终顶层父窗体”;如果返回的值是MA_NOACTIVATE或者MA_NOACTIVATEANDEAT,窗体则不被激活(注意:MA_ACTIVATEANDEAT和MA_NOACTIVATEANDEAT会导致“鼠标键按下”事件从系统队列中被删除,而不会生成一个鼠标按下消息);如果都不是以上的值,则一个WM_SETCURSOR消息被发送到窗体,充许窗体设置指针的轮廓。
如果鼠标钩子被调用,并且当前的鼠标事件从系统队列中被删除了,则检查“基于计算机训练”(CBT)的钩子。如果安装有有一个CBT钩子,将会携带HCBT_CLICKSKIPPED钩子码代调用它。
按键状态表包含了三个用于跟踪鼠标按键状态的入口。这些按键被分配予虚拟键代码(VK_LBUTTON,VK_RUTTON和VC_MBUTTON),它们和GetKeyState一起始用去确事实上鼠标键是弹起还是按下。在返回鼠标消息之前,“系统队列扫描”代码会(为弹起或按下消息)设置按键状态表并且从系统队列中删除消息。如果PeekMessage被调用时携带PM_NOREMOVE,则按键状态表不会被修改。
会思考的草 2003-09-30
  • 打赏
  • 举报
回复
1.确切讲,__stdcall不是ANSI C的关键字,它是Windows扩充的,(教材上讲的都是ANSI C,以DOS为平台的,所以你看不到这些奇怪的符号),这里面牵涉到一个调用规则和堆栈管理的问题。我们知道,在发生函数调用的时候,函数的参数会先压栈(别告诉我你不知道什么叫做“栈”——什么?你不知道?那还是回去拿本数据结构看看再来吧)。一直以来,存在两种压栈规则:PASCAL和C规则,C规则是,参数按照从右到左的顺序依次入栈,函数返回时,由调用者负责清理栈;PASCAL约定和C约定正好相反,它规定参数是从左向右传递,由被调用者恢复堆栈。这两种约定在Win16中都存在的,所以有时候还能看到PASCAL的修饰符。举个例子:
假如有函数:foo(int arg1,int arg2,int arg3),
用C规则的话就是:
push [arg1]
push [arg2]
push [arg3]
call foo
add esp, 3 * 4 ;调用者自己恢复堆栈指针
但是在Win32中的__stdcall和两者都不完全相同,它采用的是C的压栈顺序(即从右到左),但是由被调用者清理栈。这就有个好处,即,在编译的时候,被调用函数中无须生成清理栈的代码,所以编译产生的目标代码小一些。Win32只用__stdcall约定,它是Windows API函数中默认的调用约定,VB、VFP等也采用这个约定,但除了一个特例,即wsprintf。
但是C调用规则(__cdecl)还是相当有用的,譬如我们都知道,printf函数的参数是可变的,这个时候用__stdcall规则显然不可能,因为被调用的函数怎么能事先知道有多少个参数呢?所以这个时候采用C规则就方便得多。
除了__stdcall和__cdecl,还有几种规则:
__fastcall调用约定是“人”如其名,它的主要特点就是快,因为它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈),在函数名修饰约定方面,它和前两者均不同。
__thiscall仅仅应用于“C++”成员函数。this指针存放于CX寄存器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。
说到现在,其实这些修饰符就是编译器开关,指定编译器如何产生代码,相当于汇编中的伪指令它们可以直接加在要输出的函数前,也可以在编译环境的Setting...\C/C++ \Code Generation项选择。当加在输出函数前的关键字与编译环境中的选择不同时,直接加在输出函数前的关键字有效。它们对应的命令行参数分别为/Gz、/Gd和/Gr。缺省状态为/Gd,即__cdecl。
会思考的草 2003-09-30
  • 打赏
  • 举报
回复
这种问题到底要问多少次???

16,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧