MFC在处理消息时,把消息应发到的类的实例的指针(this)作为一个参数传给这个全局函数。这样此函数就可以区别处理多个实例。窗口过程是全局函数,是因为在所有的此类的窗口实例存在时,都要求此函数有效,如果是类的成员函数,当实例销毁时,就不能访问此函数了。

解决方案 »

  1.   

    <<大家都知道,窗口过程中间应该有消息循环不是这样的。一般一个线程只有一个消息泵,它负责实现消息循环,并将消息发配到窗口,而窗口只是负责处理消息而已,并没有什么消息循环。<<它怎能知道哪个消息是给哪个实例对象的呢所谓一个窗口类的实例,只是Windows窗口的C++封装而已,真正表示窗口的是HWND,而不是窗口类的实例。比如,你用Detach就可以使一个窗口类不与任何窗口相联系。而HWND恰恰就是发给窗口过程的一个参数。事实上,即使有HWND,窗口过程一般也并不关心它。因为既然几个窗口共用一个窗口过程,那么这些窗口必然有着许多的共性,窗口过程实现的往往就是这些共性——管你是谁,只要你承认你受我管,你就得这么干。就比如说虽然系统中有各式各样的对话框,但它们总是能根据TAB键浏览子控件,这便是由对话框的窗口过程实现的所谓“共性”之一。<<那CDialog类的窗口过程是如何实现的呢?简直不可能!!!这是完全肯能的。事实上,最复杂的窗口过程要数EDIT的了,它要对几十种消息做出响应。值得庆幸的是,Windows的开发者们已经为我们写好了大量的这种公用(或叫缺省)窗口过程,我们写窗口过程所要做的事很简单:只处理自己关心的消息,而将不关心的消息丢给缺省窗口过程即可。事实上,像EDIT这样的东西,我们完全可以不写任何自己的窗口过程,它就已经工作得很精彩了。当然,你如果实在想定制EDIT控件的话,通过子类化甚至超类化,自己写窗口过程也是完全可以的。
      

  2.   

    MFC通过HWND找到对应的MFC类的实例指针(this),再用此指针作参数调类的静态函数。这一过程通过CMap来实现,不如ATL作得巧妙和快捷,但是ATL中用到了thunk和汇编。
      

  3.   

    我找到了微软的VC中的C++编绎器中的USER GUIDE,中非常清楚到提到了这一个关键的原因:
    编译器在进行原码的编绎时为每一个类的成员函数加上THIS指什,当然窗口过程也不例外,
    这正是为什么只有一个版本的成员函可以处理不同的对象的原因(This的值不一样)!!!!!!!!
      

  4.   

    to: cloudshadow1(云影) :但是静态成员函数的this指针必须是显式声明的参数,编译器不为静态成员函数生成隐含的this参数。
      

  5.   

    to: horris(僧推月下门) 
    我看atl用的只不过是一个庞大的switch罢了。  
    to:: WhiteWaterBlueSky(疯狂数码) 
     azuo_lee() is right,一个线程只有一个消息泵几乎每一个窗口都要用到defwindowproc(?),当然是根据参数里的HWND判断目的窗口了。
      

  6.   

    cyq_96你是不是有时间,看一下信箱。快点!!!
      

  7.   

    To  horris(僧推月下门) and everyone:
    可能是我打字太慢了,等我刚打完下面这段话竟然已有10多位朋友回答了,不知下面的问题是否已被解决,先发了再说,大家别怪我没长眼,别人的回复都不看。"把消息应发到的类的实例的指针(this)作为一个参数传给这个全局函数",这也是我想到的唯一解决办法,不过消息传递一样只有两个参数,this指针就用掉一个有点可惜。现在别人叫我做一个类,而且由于种种原因,摆明了要我从CWnd派生,摆明了要我创建一个窗口,摆明了要求发一个自定义的消息让我的类处理它,摆明了该消息有两个参数要传递。它显然希望用PostMessage(WM_OURMESSAGE,p1,p2)的方式向我发这个消息。我总不能跟它说,不行,你这个消息只能带一个参数过来,因为我需要一个参数传递this指针!当然逼急了我也只好这么说,马急了还咬人呢,不过不到万不得已我还不想这么说。况且我觉得如果我生成一个CDialog类的实例d1,想向它发消息时好象不用d1.PostMessage(WM_OURMESSAGE,&d1,p2);当然这我还没有试过,没有调查就没有发言权,等我先去试一下先。
    但话又说回来,如果的确如此,那就要请各位大哥再开动聪明的大脑,想想它是怎样实现的了。
      

  8.   

    你的大概意思就是要在窗口过程中处理每个窗口实例各不相同的相关数据。关于这个问题,解决的方案其实有很多:
    1。使用WndExtra字节。这在你注册窗口类时指定。窗口过程中可以根据HWND和GetWindowLong取得相应的数据,但这种方法不很方便;
    2。使用Window Properties。这与第一种方法类似,比它稍稍方便一些,还是不怎么好用;
    3。使用结构体存储数据,然后把其指针作为message的参数发给窗口过程。这是Windows中普遍使用的方法之一。不存在你说的“不行,你这个消息只能带一个参数过来”这种问题,因为这一个参数实际可以是几乎无限多的参数;
    4。利用MFC的封装,将每个窗口特定的数据作为窗口类的成员变量,然后传递this指针,窗口过程通过this指针访问特定数据。这其实是第3种方法的变通,或叫做第3种方法的MFC实现。同理,这个this指针实际代表的参数个数理论上几乎是无限,因此不必为其占用了一个message参数而耿耿于怀;
    5。根本就不要直接写什么窗口过程,而是直接在CWnd的派生类中处理你关心的消息。应用程序框架负责在接到消息时调用指定的message handler。这是最方便、简洁的办法,也是MFC推荐的做法——这些message handler都是非静态成员函数,存取成员变量更是得心应手。
    还有一点要注意的是,上面说的前两种方法虽然不方便,但可以实现跨进程的通信,也就是说,你用这种方法可以实现全局窗口类的窗口过程;后两种方法虽然好用,但由于进程间地址空间的独立,通过传递指针访问其他进程的数据是不行的,因此,这种方法只能是在一个进程内部使用(当然,也可以通过DLL注入的方法实现变通的“伪进程间”通信)。至于最后一种方法,自不必说,好用是非常的好用,但一旦脱离了MFC程序框架的支持,就寸步难行了。
      

  9.   

    To: azuo_lee() 
    最后一种方法我喜欢,可是试过了,好象不行,所以我才想写窗口过程。不过既然高手说可以,那肯定是我的方法错了,下面是我的详细做法,讲大侠指正!先从CWnd派生一个新类a,在Class View中单击右键选择Add Windows Message Handle,选择增加WM_TIMER消息处理代码,内容为显示一个MessageBox。然后测试能不能响应,方法如下:在一个对话框类中加一个数据成员a1类型为a,再加一个按钮,按下按钮时调a1.PostMessage(WM_TIMER,0,0);结果是编译通过,运行出错!!!好可怜啊,不知哪里做的不对???
      

  10.   

    你只是在对话框中添加了一个a类型的变量a1显然不行——就像我上面说的,此时的a1还只是一个C++的对象,而没有与任何实际的Windows窗口相关联,它自然响应不了任何消息——因为Windows消息是发给Windows窗口的,而决不是发给C++对象的!!!
    你必须在发消息之前,用某种办法使C++对象与窗口关联。这里最好的办法就是调用a的方法Create来创建一个窗口。你加上Create以后再试试,就应该没有问题了。
      

  11.   

    看看 mfc 的消息映射,每个消息对应的处理函数是一个类的成员函数(自然带有 this 指针)
    至于怎样使全局的窗口过程调用类的成员函数,侯捷的深入浅出mfc....讲的再清楚不过了DECLARE_MESSAGE_MAP() 声明了一个消息处理的大表(静态的)
    static const AFX_MSGMAP_ENTRY _messageEntries[];struct AFX_MSGMAP_ENTRY
    {
    UINT nMessage;   // windows message
    UINT nCode;      // control code or WM_NOTIFY code
    UINT nID;        // control ID (or 0 for windows messages)
    UINT nLastID;    // used for entries specifying a range of control id's
    UINT nSig;       // signature type (action) or pointer to message #
    AFX_PMSG pfn;    // routine to call (or special value)
    };在消息映射表中会为每一个消息的处理填充这样一个结构,pfn 指向你的对象的成员函数(消息处理函数)具体的太麻烦了,去看看《深入浅出 ...》 吧
      

  12.   

    你要是用SDK做过程序的话,就应该知道,窗口过程的第一参数是窗口句柄,MFC中不管是将其封装成CWnd,还是CDialog,都可以从此参数中知道是哪一个实例,不仅仅是this指针的问题。
      

  13.   

    MFC和ATL实现的窗口回调过程的流程:
    1.Windows消息(HWND,msg,wparam,lparam) -> 2.由HWND找到它对应的CWnd(对ATL是CWindowImpl)给pThis -> 3.调CWnd的静态成员函数CWnd::WndProc(可能是这个名字啊),这个函数的参数是:(pThis,msg,wparam,lparam) -> 4.WndProc解析msg,wparam,lparam,调用消息影射中定义的消息响应函数。消息影射的宏实际上就是在WndProc中引入一大堆case语句。to bighead(累了) :
    我想我们讨论的是第二步,如何由HWND找到CWnd.MFC在每个CWnd实例产生时,都填写一个CMap类型的相关表,填入CWnd类实例的指针和HWND。第二步就是由HWND查表得到CWnd类实例的指针。应当说查CMap表是相当快的,且与表的元素多少没太大关系,但是当表的元素多时很显然占用的空间比较多。我看过一本书《ATL Internals》,里面介绍ATL处理这个问题比较有技巧,几乎没用额外的空间,你有兴趣可以看ATL的源码。to CYQ_96()只是路过而已():
    我明白你是要干什么了,其实很简单,用不着自已编窗口过程,有现成的消息影射:
    ON_MESSAGE( <message>, <memberFxn> )
    其响应函数原型:
    afx_msg LRESULT memberFxn(WPARAM, LPARAM);
    你只要从CWnd派生,然后手工在头文件和实现文件中添加这个消息影射和响应函数,好象ClassWizard不支持ON_MESSAGE,具体步骤:
    在头文件的
    DECLARE_MESSAGE_MAP()
    前加入
    afx_msg LRESULT OnYourmsg(WPARAM, LPARAM);
    在实现文件的
    END_MESSAGE_MAP()
    前加入
    ON_MESSAGE( your msg id, OnYourmsg )
    最后,在实现文件中加入OnYourmsg成员函数的函数体。
    如果是自定义的消息,不是标准Windows消息,则要把ON_MESSAGE换成ON_REGISTERED_MESSAGE(your msg id, OnYourmsg)。如何你没有消息ID,只有消息的文字名称则要在实现文件中加入一个全局变量:
    const UINT mymsg = RegisterWindowMessage( "WM_YOURMSG" );
    而宏换成:
    ON_REGISTERED_MESSAGE(your msg id, OnYourmsg)
      

  14.   

    有点错,重写一遍:MFC和ATL实现的窗口回调过程的流程:
    1.Windows消息(HWND,msg,wparam,lparam) -> 2.由HWND找到它对应的CWnd(对ATL是CWindowImpl)给pThis -> 3.调CWnd的静态成员函数CWnd::WndProc(可能是这个名字啊),这个函数的参数是:(pThis,msg,wparam,lparam) -> 4.WndProc解析msg,wparam,lparam,调用pThis的消息影射中定义的消息响应函数。消息影射的宏实际上就是在WndProc中引入一大堆case语句。to bighead(累了) :
    我想我们讨论的是第二步,如何由HWND找到CWnd.MFC在每个CWnd实例产生时,都填写一个CMap类型的相关表,填入CWnd类实例的指针和HWND。第二步就是由HWND查表得到CWnd类实例的指针。应当说查CMap表是相当快的,且与表的元素多少没太大关系,但是当表的元素多时很显然占用的空间比较多。我看过一本书《ATL Internals》,里面介绍ATL处理这个问题比较有技巧,几乎没用额外的空间,你有兴趣可以看ATL的源码。to CYQ_96()只是路过而已():
    我明白你是要干什么了,其实很简单,用不着自已编窗口过程,有现成的消息影射:
    ON_MESSAGE( <message>, <memberFxn> )
    其响应函数原型:
    afx_msg LRESULT memberFxn(WPARAM, LPARAM);
    你只要从CWnd派生,然后手工在头文件和实现文件中添加这个消息影射和响应函数,好象ClassWizard不支持ON_MESSAGE,具体步骤:
    1.在头文件的
    DECLARE_MESSAGE_MAP()
    前加入
    afx_msg LRESULT OnYourmsg(WPARAM, LPARAM);
    2.在实现文件的
    END_MESSAGE_MAP()
    前加入
    ON_MESSAGE( your msg id, OnYourmsg )
    3.最后,在实现文件中加入OnYourmsg成员函数的函数体。
    如果没有自定义的消息的ID,只有消息的文字名称,则要在实现文件中加入一个全局变量:
    const UINT mymsgid = RegisterWindowMessage( "WM_YOURMSG" );把ON_MESSAGE换成ON_REGISTERED_MESSAGE(mymsgid, OnYourmsg)。
      

  15.   

    搞那么吓人的标题干嘛。小儿科的问题,自己多看看书不就明白了吗?kao!
      

  16.   

    : horris(僧推月下门) : azuo_lee() 
    现在终于知道,VC栏里谁是真正乐于助人的高手了,希望能和你们交个朋友。我的Email:[email protected]
      

  17.   

    其实大家讲的,几年前我几乎全在书上都看过,只是没能形成统一连续的观点。
    特别是近两年都是在Delphi,VB环境下编程,对这些细节渐渐淡忘了,现在各位的帮助下正逐渐恢复状态,再一次感谢大家!另:如能得到各位的Email地址,以后多多交流那就不胜荣幸了。