关于消息的问题!(50分)

H

hhsj

Unregistered / Unconfirmed
GUEST, unregistred user!
1、消息发出后先被传入系统队列,然后被传入应用程序队列!<br>&nbsp; &nbsp;请问:传入系统队列后,将根据什么找到相应的应用程序队列呢?<br>2、消息在经过这上面所说的的传递后,由应用程序的消息循环进行处理。<br>&nbsp; &nbsp;请问:消息怎么找到处理该消息的窗口呢?<br>3、消息--系统队列--应用程序队列--应用程序消息循环--处理该消息的窗口。<br>&nbsp; &nbsp;在这个路线中,我搞不懂消息是根据什么找到应用程序的,并且找到具体<br>&nbsp; &nbsp;处理该消息的窗口。消息在传入系统队列时候,应该只有一个hWndle,这个<br>&nbsp; &nbsp;hWndle应该是处理该消息的窗口。系统应该是根据这个hWndle来找到应用<br>&nbsp; &nbsp;程序的吗?怎么找呢?
 
发布到你进程的消息都由Windows放入进程的消息队列中,由进程中处理消息的过程(WinMain过程, 在Delphi中就是Application)通过PeekMessage或GetMessage从消息队列中取出。 然后通过调用DispatchMessage分别交给相应的窗口的WindowProc执行。<br>上面这点就可以回答你的三个问题了。<br>由你的程序调用DispatchMessage这个API由操作系统去找目的窗口的WindowProc入口来执行相关消息。
 
问题是发送的消息怎么找到我的进程队列呢?
 
可以看看这篇研究<br><br>VCL窗口函数注册机制研究手记,兼与MFC比较<br><br>By &nbsp;王捷 &nbsp;cheka@yeah.net &nbsp; &nbsp; (转载请保留此信息)<br><br>&nbsp; &nbsp; &nbsp;这个名字起的有些耸人听闻,无他意,只为吸引眼球而已,如果您对下列关键词有兴<br>趣,希望不要错过本文:<br><br>1. &nbsp; &nbsp; &nbsp; &nbsp;VCL可视组件在内存中的分页式管理;<br><br>2. &nbsp; &nbsp; &nbsp; &nbsp;让系统回调类的成员方法<br><br>3. &nbsp; &nbsp; &nbsp; &nbsp;Delphi 中汇编指令的使用<br><br>4. &nbsp; &nbsp; &nbsp; &nbsp;Hardcore <br><br>5. &nbsp; &nbsp; &nbsp; &nbsp;第4条是骗你的<br><br>&nbsp;<br><br>&nbsp; &nbsp; 我们知道Windows平台上的GUI程序都必须遵循Windows的消息响应机制,可以简单概括<br>如下,所有的窗口控件都向系统注册自身的窗口函数,运行期间消息可被指派至特定窗口<br>控件的窗口函数处理。对消息相应机制做这样的概括有失严密,请各位见谅,我想赶紧转<br>向本文重点,即在利用Object Pascali或是C++这样的面向对象语言编程中,如何把一个类<br>的成员方法向系统注册以供回调。<br><br>&nbsp;<br>&nbsp; &nbsp; 在注册窗口类即调用RegisterClass函数时,我们向系统传递的是一个WindowProc 类<br>型的函数指针<br><br>WindowProc 的定义如下<br><br>LRESULT CALLBACK WindowProc(<br><br>&nbsp; HWND hwnd, &nbsp; &nbsp; &nbsp;// handle to window<br><br>&nbsp; UINT uMsg, &nbsp; &nbsp; &nbsp;// message identifier<br><br>&nbsp; WPARAM wParam, &nbsp;// first message parameter<br><br>&nbsp; LPARAM lParam &nbsp; // second message parameter<br><br>);<br><br>&nbsp; &nbsp; 如果我们有一个控件类,它拥有看似具有相同定义的成员方法TMyControl.WindowProc,<br>可是却不能够将它的首地址作为lpfnWndProc参数传给RegisterClass,道理很简单,因为<br>Delphi中所有类成员方法都有一个隐含的参数,也就是Self,因此无法符合标准<br>&nbsp;WindowProc 的定义。<br><br>&nbsp;<br>&nbsp; &nbsp; 那么,在VCL中,控件向系统注册时究竟传递了一个什么样的窗口指针,同时通过这个<br>指针又是如何调到各个类的事件响应方法呢?我先卖个关子,先看看MFC是怎么做的。<br><br>&nbsp; &nbsp; 在调查MFC代码之前,我作过两种猜想: <br><br>一,作注册用的函数指针指向的是一个类的静态方法,<br><br>静态方法同样不需要隐含参数 this (对应 Delphi中的 Self ,不过Object Pascal不支持<br>静态方法)<br><br>二,作注册用的函数指针指向的是一个全局函数,这当然最传统,没什么好说的。<br><br><br>&nbsp; &nbsp; 经过简单的跟踪,我发现MFC中,全局函数AfxWndProc是整个MFC程序处理消息的“根<br>节点”,也就是说,所有的消息都由它指派给不同控件的消息响应函数,也就是说,所有<br>的窗口控件向系统注册的窗口函数很可能就是 AfxWndProc (抱歉没做深入研究,如果不<br>对请指正)。而AfxWndProc 是如何调用各个窗口类的WndProc呢?<br><br>&nbsp; &nbsp; 哈哈,MFC用了一种很朴素的机制,相比它那么多稀奇古怪的宏来说,这种机制相当好<br>理解:使用一个全局的Map数据结构来维护所有的窗口对象和Handle(其中Handle为键值),<br>然后AfxWndProc根据Handle来找出唯一对应的窗口对象<br>(使用静态函数CWnd::FromHandlePermanent(HWND hWnd) ),然后调用其WndProc,注意<br>WndProc可是虚拟方法,因此消息能够正确到达所指定窗口类的消息响应函数并被处理。<br><br>&nbsp;<br><br>&nbsp; &nbsp; 于是我们有理由猜想VCL也可能采用相同的机制,毕竟这种方式实现起来很简单。我确<br>实是这么猜的,不过结论是我错了......<br><br>&nbsp; &nbsp; 开场秀结束,好戏正式上演。<br><br>&nbsp; &nbsp; 在Form1上放一个Button(缺省名为Button1),在其OnClick事件中写些代码,加上断点,<br>F9运行,当停留在断点上时,打开Call Stack窗口(View-&gt;Debug Window-&gt;Call Stack,<br>或者按Ctrl-Alt-S )可看到调用顺序如下(从底往上看,stack嘛)<br><br>( 如果你看到的 Stack 和这个不一致,请打开DCU 调试开关<br>&nbsp;Project-&gt;Options-&gt;Compiler-&gt;Use Debug DCUs, 这个开关如果不打开,是没法调试VCL<br>源码的 )<br><br><br>TForm1.Button1Click(???)<br><br>TControl.Click<br><br>TButton.Click<br><br>TButton.CNCommand ((48401, 3880, 0, 3880, 0))<br><br>TControl.WndProc ((48401, 3880, 3880, 0, 3880, 0, 3880, 0, 0, 0))<br><br>TWinControl.WndProc ((48401, 3880, 3880, 0, 3880, 0, 3880, 0, 0, 0))<br><br>TButtonControl.WndProc ((48401, 3880, 3880, 0, 3880, 0, 3880, 0, 0, 0))<br><br>TControl.Perform (48401,3880,3880)<br><br>DoControlMsg (3880,(no value))<br><br>TWinControl.WMComman d((273, 3880, 0, 3880, 0))<br><br>TCustomForm.WMCommand ((273, 3880, 0, 3880, 0))<br><br>TControl.WndProc ((273, 3880, 3880, 0, 3880, 0, 3880, 0, 0, 0))<br><br>TWinControl.WndProc((273, 3880, 3880, 0, 3880, 0, 3880, 0, 0, 0))<br><br>TCustomForm.WndProc ((273, 3880, 3880, 0, 3880, 0, 3880, 0, 0, 0))<br><br>TWinControl.MainWndProc ((273, 3880, 3880, 0, 3880, 0, 3880, 0, 0, 0))<br><br>StdWndProc (3792,273,3880,3880)<br><br><br>&nbsp; &nbsp; &nbsp;可见 StdWndProc 看上去象是扮演了MFC中 AfxWndProc 的角色,不过我们先不谈它,<br>如果你抑制不住好奇心,可以提前去看它的源码,在Forms.pas中,看到了么? 是不是<br>特~~~~别有趣阿。<br><br>&nbsp;<br><br>&nbsp; &nbsp; &nbsp;实际上,VCL在RegisterClass时传递的窗口函数指针并非指向StdWndProc。那是什么<br>呢? <br><br>&nbsp; &nbsp; &nbsp;我跟,我跟,我跟跟跟,终于在Controls.pas的TWindowControl的实现代码中<br><br>(procedure TWinControl.CreateWnd;) 看到了RegisterClass的调用,hoho,终于找到组<br>织了......别忙,发现了没,这时候注册的窗口函数是InitWndProc,看看它的定义,嗯,<br>符合标准,再去瞧瞧代码都干了些什么。<br><br>&nbsp; &nbsp; 发现这句:<br><br>SetWindowLong(HWindow, GWL_WNDPROC,Longint(CreationControl.FObjectInstance));<br><br>&nbsp; &nbsp; 我Faint,搞了半天InitWndProc初次被调用(对每一个Wincontrol来说)就把自个儿<br>给换了,新上岗的是FObjectInstance。下面还有一小段汇编,是紧接着调用<br>FObjectInstance的,调用的理由不奇怪,因为以后调用FObjectInstace都由系统CallBack<br>了,但现在还得劳InitWndProc的大驾去call。调用的方式有些讲究,不过留给您看完这篇<br>文章后自个儿琢磨去吧。<br><br>&nbsp; &nbsp; 接下来只能继续看FObjectInstance是什么东东,它定义在 TWinControl 的 Private <br>段,是个Pointer也就是个普通指针,当什么使都行,你跟Windows说它就是 WndProc 型指<br>针 Windows 拿你也没辙。<br><br>&nbsp; &nbsp; &nbsp;FObjectInstance究竟指向何处呢,镜头移向 TWincontrol 的构造函数,这是<br>FObjectInstance初次被赋值的地方。 多余的代码不用看,焦点放在这句上<br>&nbsp;<br>&nbsp; &nbsp; &nbsp;FObjectInstance := MakeObjectInstance(MainWndProc);<br><br>&nbsp;<br><br>&nbsp; &nbsp; &nbsp;可以先告诉您,MakeObjectInstance是本主题最精彩之处,但是您现在只需知道<br>FObjectInstance“指向了”MainWndProc,也就是说通过某种途径VCL把每个MainWndProc<br>作为窗口函数注册了,先证明容易的,即 MainWndProc 具备窗口函数的功能,来看代码:<br><br>( 省去异常处理 )<br><br>procedure TWinControl.MainWndProc(var Message: TMessage);<br><br>begin<br><br>&nbsp; &nbsp; &nbsp; WindowProc(Message);<br><br>&nbsp; &nbsp; &nbsp; FreeDeviceContexts;<br><br>&nbsp; &nbsp; &nbsp; FreeMemoryContexts;<br><br>end;<br><br>&nbsp;<br><br>FreeDeviceContexts; 和 &nbsp;FreeMemoryContexts 是保证VCL线程安全的,不在本文讨论之列<br>,只看WindowProc(Message); 原来 MainWndProc 把消息委托给了方法 WindowProc处理,<br>注意到 MainWndProc 不是虚拟方法,而 WindowProc 则是虚拟的,了解 Design Pattern <br>的朋友应该点头了,嗯,是个 Template Method , 很自然也很经典的用法,这样一来所有<br>的消息都能准确到达目的地,也就是说从功能上看 MainWndProc 确实可以充作窗口函数。<br>您现在可以回顾一下MFC的 AfxWindowProc 的做法,同样是利用对象的多态性,但是两种方<br>式有所区别。<br><br>&nbsp; &nbsp; 是不是有点乱了呢,让我们总结一下,VCL 注册窗口函数分三步: <br><br>1. &nbsp;[ TWinControl.Create ]<br><br>&nbsp; &nbsp; FObjectInstance 指向了 MainWndProc<br><br>2. &nbsp;[ TWinControl.CreateWnd ] <br><br>&nbsp; &nbsp; WindowClass.lpfnWndProc 值为 @InitWndProc; <br><br>&nbsp; &nbsp; 调用Windows.RegisterClass(WindowClass)向系统注册<br><br>3. &nbsp;[ InitWndProc 初次被Callback时 ]<br><br>&nbsp; &nbsp; SetWindowLong(HWindow, GWL_WNDPROC, Longint(CreationControl.FObjectInstance)) <br><br>&nbsp; &nbsp; 窗口函数被偷梁换柱,从此 InitWndProc 退隐江湖<br><br>&nbsp; &nbsp; (注意是对每个TWinControl控件来说,InitWndProc 只被调用一次)<br>&nbsp;<br><br>&nbsp; &nbsp; 前面说过,非静态的类方法是不能注册成为窗口函数的,特别是Delphi中<br>根本没有静态类方法,那么MainWndProc 也不能有特权(当然宝兰可以为此在编译器上动点<br>手脚,如果他们不怕成为呕像的话)。<br><br>&nbsp; &nbsp; 那么,那么,您应该意识到了,在幕后操纵一切的,正是...... <br><br>&nbsp; &nbsp; 背景打出字幕<br><br>&nbsp; &nbsp; 超级巨星:麦克奥布吉特因斯坦斯 <br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(MakeObjectInstance)<br><br>&nbsp; &nbsp; 天空出现闪电,哦耶,主角才刚刚亮相。<br><br><br>&nbsp; &nbsp; 废话不说,代码伺候:<br><br>&nbsp;<br><br>( 原始码在 Form.pas 中,“{}”中是原始的注释,而“ //” 后的是我所加,您可以直<br>接就注释代码,也可以先看我下面的评论,再回头啃code )<br><br>// 共占 13 Bytes,变体纪录以最大值为准<br><br>type<br><br>&nbsp; PObjectInstance = ^TObjectInstance;<br><br>&nbsp; TObjectInstance = packed record<br><br>&nbsp; &nbsp; Code: Byte; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // 1 Bytes<br><br>&nbsp; &nbsp; Offset: Integer; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 4 Bytes<br><br>&nbsp; &nbsp; case Integer of &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br><br>&nbsp; &nbsp; &nbsp; 0: (Next: PObjectInstance); &nbsp;// 4 Bytes<br><br>&nbsp; &nbsp; &nbsp; 1: (Method: TWndMethod); &nbsp; &nbsp; // 8 Bytes &nbsp;<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// TWndMethod 是一个指向对象方法的指针,<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 事实上是一个指针对,包含方法指针以<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 及一个对象的指针(即Self )<br><br>&nbsp; end;<br><br>&nbsp;<br><br>// 313是满足整个TInstanceBlock的大小不超过4096的最大值<br><br>InstanceCount = 313;<br><br>&nbsp;<br>// 共占 4079 Bytes<br><br>type<br><br>&nbsp; PInstanceBlock = ^TInstanceBlock;<br><br>&nbsp; TInstanceBlock = packed record<br><br>&nbsp; &nbsp; Next: PInstanceBlock; &nbsp; &nbsp; &nbsp; &nbsp;// 4 Bytes<br><br>&nbsp; &nbsp; Code: array[1..2] of Byte; &nbsp; // 2 Bytes<br><br>&nbsp; &nbsp; WndProcPtr: Pointer; &nbsp; &nbsp; &nbsp; &nbsp; // 4 Bytes<br><br>&nbsp; &nbsp; Instances: array[0..InstanceCount] of TObjectInstance; 313 * 13 = 4069<br><br>&nbsp; end;<br><br>&nbsp;<br><br>function CalcJmpOffset(Src, Dest: Pointer): Longint;<br><br>begin<br><br>&nbsp; Result := Longint(Dest) - (Longint(Src) + 5);<br><br>end;<br><br>&nbsp;<br><br>function MakeObjectInstance(Method: TWndMethod): Pointer;<br><br>const<br><br>&nbsp; BlockCode: array[1..2] of Byte = (<br><br>&nbsp; &nbsp; $59, &nbsp; &nbsp; &nbsp; { POP ECX }<br><br>&nbsp; &nbsp; $E9); &nbsp; &nbsp; &nbsp;{ JMP StdWndProc } &nbsp;// 实际上只有一个JMP<br><br>&nbsp; PageSize = 4096;<br><br>var<br><br>&nbsp; Block: PInstanceBlock;<br><br>&nbsp; Instance: PObjectInstance;<br><br>begin<br><br>&nbsp; // InstFreeList = nil 表明一个Instance block已被占满,于是需要为一个新<br><br>&nbsp; // Instance block分配空间,一个个Instance block通过PinstanceBlock中的<br><br>&nbsp; // Next 指针相连,形成一个链表,其头指针为InstBlockList<br><br>&nbsp; <br><br>&nbsp; if InstFreeList = nil then<br><br>&nbsp; begin<br><br>&nbsp; &nbsp; // 为Instance block分配虚拟内存,并指定这块内存为可读写并可执行<br><br>&nbsp; &nbsp; // PageSize 为4096。<br><br>&nbsp; &nbsp; Block := VirtualAlloc(nil, PageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);<br><br>&nbsp; &nbsp; Block^.Next := InstBlockList;<br><br>&nbsp; &nbsp; Move(BlockCode, Block^.Code, SizeOf(BlockCode));<br><br>&nbsp; &nbsp; Block^.WndProcPtr := Pointer(CalcJmpOffset(@Block^.Code[2], @StdWndProc));<br><br>&nbsp; &nbsp; <br><br>&nbsp; &nbsp; // 以下代码建立一个Instance的链表<br><br>&nbsp; &nbsp; Instance := @Block^.Instances;<br><br>&nbsp; &nbsp; repeat<br><br>&nbsp; &nbsp; &nbsp; Instance^.Code := $E8; &nbsp;{ CALL NEAR PTR Offset }<br><br>&nbsp; &nbsp; &nbsp; //算出相对 jmp StdWndProc指令的偏移量,放在$E8的后面<br><br>&nbsp; &nbsp; &nbsp; Instance^.Offset := CalcJmpOffset(Instance, @Block^.Code);<br><br>&nbsp; &nbsp; &nbsp; Instance^.Next := InstFreeList;<br><br>&nbsp; &nbsp; &nbsp; InstFreeList := Instance;<br><br>&nbsp; &nbsp; &nbsp; // 必须有这步,让Instance指针移至当前instance子块的底部<br><br>&nbsp; &nbsp; &nbsp; Inc(Longint(Instance), SizeOf(TObjectInstance));<br><br>&nbsp; &nbsp; &nbsp; // 判断一个Instance block是否已被构造完毕<br><br>&nbsp; &nbsp; until Longint(Instance) - Longint(Block) &gt;= SizeOf(TInstanceBlock);<br><br>&nbsp; &nbsp; InstBlockList := Block;<br><br>&nbsp; end;<br><br>&nbsp; Result := InstFreeList; <br><br>&nbsp; Instance := InstFreeList;<br><br>&nbsp; InstFreeList := Instance^.Next;<br><br>&nbsp; Instance^.Method := Method;<br><br>end;<br><br>&nbsp;<br><br>&nbsp; &nbsp; 不要小看这区区几十行代码的能量,就是它们对 VCL 的可视组件进行了分页式管理,<br>(代码中对两个链表进行操作,InstanceBlock 中有 ObjectInstance 的链表,而一个个<br>InstanceBlock &nbsp;又构成一个链表 )一个 InstanceBlock 为一页,有4096 字节,虽然 <br>InstanceBlock 实际使用的只有 4079 字节,不过为了 Alignment ,就加了些 padding <br>凑满 4096 。从代码可见每一页中可容纳 313 个所谓的ObjectInstance,如果望文生义<br>很容易将这个 ObjectInstance 误解为对象实例,其实不然,每个ObjectInstance 其实是<br>一小段可执行代码,而这些可执行代码不是编译期间生成的,也不是象虚拟函数那样滞后<br>联编,而根本就是MakeObjectInstance 在运行期间“创作”的(天哪)! 也就是说,<br>MakeObjectInstance 将所有的可视VCL组件 改造成了一页页的可执行代码区域,是不是<br>很了不起呢。<br><br>&nbsp; &nbsp; 不明白ObjectInstance所对应的代码是做什么的么?没关系,一起来看<br><br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; call &nbsp;- - - - - - - - - - - &gt; &nbsp;pop ECX &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // 在call 之前,下一个指令的地址会被压栈<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; @MainWndProc &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // 紧接着执行pop ECX, 为何这么做呢?<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; @Object(即Self) &nbsp;// 前面注释中提过<br>&nbsp;<br><br>&nbsp; &nbsp; 答案在 StdWndProc 的代码中,要命哦,全是汇编,可是无限风光在险峰,硬着头皮闯<br>一回吧。<br><br>&nbsp; &nbsp; &nbsp;果不其然,我们发现其中用到了ECX<br><br>function StdWndProc(Window: HWND; Message, WParam: Longint;<br><br>&nbsp; LParam: Longint): Longint; stdcall; assembler;<br><br>asm<br><br>&nbsp; &nbsp; &nbsp; &nbsp; XOR &nbsp; &nbsp; EAX,EAX &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br><br>&nbsp; &nbsp; &nbsp; &nbsp; PUSH &nbsp; &nbsp;EAX<br><br>&nbsp; &nbsp; &nbsp; &nbsp; PUSH &nbsp; &nbsp;LParam<br><br>&nbsp; &nbsp; &nbsp; &nbsp; PUSH &nbsp; &nbsp;WParam<br><br>&nbsp; &nbsp; &nbsp; &nbsp; PUSH &nbsp; &nbsp;Message<br><br>&nbsp; &nbsp; &nbsp; &nbsp; MOV &nbsp; &nbsp; EDX,ESP<br><br>&nbsp; &nbsp; &nbsp; &nbsp; MOV &nbsp; &nbsp; EAX,[ECX].Longint[4] // &nbsp; 相当于 MOV EAX, [ECX+4] ( [ECX+4] 是什么?就是Self )<br><br>&nbsp; &nbsp; &nbsp; &nbsp; CALL &nbsp; &nbsp;[ECX].Pointer &nbsp; &nbsp; &nbsp; &nbsp;// &nbsp; 相当于 CALL &nbsp; &nbsp;[ECX] , 也就是调用 MainWndProc<br><br>&nbsp; &nbsp; &nbsp; &nbsp; ADD &nbsp; &nbsp; ESP,12<br><br>&nbsp; &nbsp; &nbsp; &nbsp; POP &nbsp; &nbsp; EAX<br><br>end;<br><br>&nbsp;<br><br>&nbsp;<br>&nbsp; &nbsp; 这段汇编中在调用MainWndProc前作了些参数传递的工作,由于MainWndProc 的定义如<br>下:<br><br>procedure TwinControl..MainWndProc(var Message: TMessage);<br><br><br>&nbsp; &nbsp; 根据Delphi 的约定,这种情况下隐函数Self 作为第一个参数,放入EAX 中,<br>TMessage 结构的指针作为第二个参数,放入EDX中,而Message的指针从哪儿来呢?我们看<br>到在连续几个 Push 之后,程序已经在堆栈中构造了一个TMessage 结构,而这时的ESP <br>当然就是这个结构的指针,于是将它赋给EDX 。如果您不熟悉这方面的约定,可以参考<br>Delphi 的帮助Object Pascal Refrence -&gt; Program Control。<br><br>&nbsp; &nbsp; 现在真相大白,Windows 消息百转千折终于传进MainWndProc , 不过这一路也可谓相<br>当精彩,MakeObject这一函数自然是居功至伟, StdWndProc 也同样是幕后英雄,让我们<br>把 MakeObjectInstance 作出的代码和StdWndProc 连接起来,哦,堪称鬼斧神工.<br><br>&nbsp; &nbsp;( 大富翁没法显示图像,可以去<br>&nbsp; &nbsp; &nbsp;http://jp.njuct.edu.cn/crystal/article/vcl%20hardcore.htm<br>&nbsp; &nbsp; &nbsp;看完整全文,感谢房客支持)<br><br>&nbsp; &nbsp; 就此在总结一下, FobjectInstance 被VCL 注册为窗口函数,而实际上 <br>FObjectInstance 并不实际指向某个函数,而是指向一个ObjectInstance, 而后者我们已<br>经知道是一系列相接的可执行代码段当中的一块,当系统需要将 FObjectInstance 当做<br>窗口函数作为回调时,实际进入了ObjectInstance 所在的代码段,然后几番跳跃腾挪(<br>一个call 加一个 jump )来到StdWndProc ,StdWndProc 的主要功用在于将Self 指针<br>压栈,并把Windows的消息包装成Delphi的TMessage 结构,如此才能成功调用到<br>TWinControl类的成员方法 MainWndProc, 消息一旦进入MainWndProc 便可以轻车熟路一路<br>高唱小曲来到各个对象转属的WndProc , 从此功德圆满。<br><br><br>&nbsp; &nbsp; 后记: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br><br>&nbsp; &nbsp; 个人感觉在这一技术上VCL 要比MFC 效率高出不少,后者每次根据窗口句柄来检索相<br>对应的窗口对象指针颇为费时,同时MakeObject &nbsp;的代码也相当具有参考价值,有没有想<br>过让你自己的程序在内存中再开一堆可执行代码?<br>&nbsp; &nbsp; &nbsp; &nbsp;<br>&nbsp; &nbsp; 所有的代码是基于Delphi5的,可能与其余版本有所出入,但相信不会很大。<br><br>&nbsp; &nbsp; 整个星期六和星期天我都花在写作此文上了(连调试带写字), 不过水平所限,难免<br>有所错误与表达不周,但愿不至以己昏昏令人昏昏,欢迎来信探讨指教 &nbsp;<br><br>cheka@yeah.net , thanx
 
消息结构中有窗口句柄handle,操作系统会由这个handle找到创建这个<br>窗口的线程,然后把把这个消息附加到这个线程的消息队列中,线程中再作相应的处理。<br>要记住:消息属于某个线程.
 
学习了。
 
C(WINMAIN函数)中有一个GETMESSAGE函数,他的大体功能按我现在的理解是从系统队列<br>中取出消息并将消息从系统队列中移除,APPLICATON类中没有这个函数。那什么函数<br>能将消息从系统队列中移除掉?<br>
 
GetMessage 是 API 函数,Delphi中照样能用.
 
DELPHI中没有对应该函数的函数吗?
 
根据消息里的句柄找到相应的消息处理队列啊。
 
顶部