WTL 消息流

原创 2007年09月27日 13:51:00

在_tWinMain里做了一些初始化函数之后,就进入了Run函数,显然Run函数就是消息循环。下面是Run函数的代码:
int Run(LPTSTR /*lpstrCmdLine*/ = NULL, int nCmdShow = SW_SHOWDEFAULT)

{

         CMessageLoop theLoop;

         _Module.AddMessageLoop(&theLoop);

         CMainFrame wndMain;

         if(wndMain.CreateEx() == NULL)

         {

                   ATLTRACE(_T("Main window creation failed!/n"));

                   return 0;

         }

         wndMain.ShowWindow(nCmdShow);

         int nRet = theLoop.Run();

         _Module.RemoveMessageLoop();

         return nRet;

}
 

不出所料,CmessageLoop就是用作消息循环的类,_Module.AddMessageLoop不过是保存一个全局引用,其它地方可以方便的取得CmessageLoop的实例theLoop。

CmessageLoop::Run函数无疑就是消息循环函数了:
int Run()

         {

                   BOOL bDoIdle = TRUE;

                   int nIdleCount = 0;

                   BOOL bRet;

                   for(;;)

                   {

                            while(bDoIdle && !::PeekMessage(&m_msg, NULL, 0, 0, PM_NOREMOVE))

                            {

                                     if(!OnIdle(nIdleCount++))

                                               bDoIdle = FALSE;

                            }

                            bRet = ::GetMessage(&m_msg, NULL, 0, 0);

                            if(bRet == -1)

                            {

                                     ATLTRACE2(atlTraceUI, 0, _T("::GetMessage returned -1 (error)/n"));

                                     continue;   // error, don't process

                            }

                            else if(!bRet)

                            {

                                     ATLTRACE2(atlTraceUI, 0, _T("CMessageLoop::Run - exiting/n"));

                                     break;   // WM_QUIT, exit message loop

                            }

                            if(!PreTranslateMessage(&m_msg))

                            {

                                     ::TranslateMessage(&m_msg);

                                     ::DispatchMessage(&m_msg);

                            }

                            if(IsIdleMessage(&m_msg))

                            {

                                     bDoIdle = TRUE;

                                     nIdleCount = 0;

                            }

                   }

                   return (int)m_msg.wParam;

         }

CmessageLoop::Run函数,不断的从消息队列里取消息,然后分发给对应的窗口。当消息分发到窗口时,自然就是调用窗口的WINPROC函数了。那么WINPROC是在哪里注册,在哪里实现的呢?

         HWND CwindowImpl::Create(HWND hWndParent, RECT& rcPos, LPCTSTR szWindowName = NULL,

                            DWORD dwStyle = 0, DWORD dwExStyle = 0,

                            UINT nID = 0, LPVOID lpCreateParam = NULL)

         {

                   if (T::GetWndClassInfo().m_lpszOrigName == NULL)

                            T::GetWndClassInfo().m_lpszOrigName = GetWndClassName();

                   ATOM atom = T::GetWndClassInfo().Register(&m_pfnSuperWindowProc);

                   dwStyle = T::GetWndStyle(dwStyle);

                   dwExStyle = T::GetWndExStyle(dwExStyle);

                   return CWindowImplBaseT< TBase, TWinTraits >::Create(hWndParent, rcPos, szWindowName,

                            dwStyle, dwExStyle, nID, atom, lpCreateParam);

         } 

原来在窗口创建函数里注册WinClass,因为这是在基类里实现的,它需要从子类窗口中获得WinClass信息。很明显,这里调用GetWndClassInfo获得WinClass信息。但是我们并没有看到子类里实现该函数,应该是宏展开的吧,DECLARE_XXXX_WND_CLASS之类最为可疑:
#define DECLARE_FRAME_WND_CLASS(WndClassName, uCommonResourceID) /

static CFrameWndClassInfo& GetWndClassInfo() /

{ /

         static CFrameWndClassInfo wc = /

         { /

                   { 0, StartWindowProc, /

                     0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, WndClassName }, /

                   NULL, NULL, IDC_ARROW, TRUE, 0, _T(""), uCommonResourceID /

         }; /

         return wc; /

}

没错,就是它,原来WINPROC函数名为StartWindowProc。问题又来了:StartWindowProc在哪里实现了,子类没有,自然是基类里了:
template <class TBase, class TWinTraits>

LRESULT CALLBACK CWindowImplBaseT< TBase, TWinTraits >::StartWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

         CWindowImplBaseT< TBase, TWinTraits >* pThis = (CWindowImplBaseT< TBase, TWinTraits >*)_Module.ExtractCreateWndData();

         ATLASSERT(pThis != NULL);

         pThis->m_hWnd = hWnd;

         pThis->m_thunk.Init(pThis->GetWindowProc(), pThis);

         WNDPROC pProc = (WNDPROC)&(pThis->m_thunk.thunk);

         WNDPROC pOldProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pProc);

#ifdef _DEBUG

         // check if somebody has subclassed us already since we discard it

         if(pOldProc != StartWindowProc)

                   ATLTRACE2(atlTraceWindowing, 0, _T("Subclassing through a hook discarded./n"));

#else

         pOldProc; // avoid unused warning

#endif

         return pProc(hWnd, uMsg, wParam, lParam);

}
 

这个函数的实现有点晦涩,主要是不太明白thunk指的是什么。基本功能倒是很明显,只是重新设置了一下WINPROC,也就是说WINPROC被换成新的了,下次再也不会调用StartWindowProc了。很快找到了真正的WINPROC:
template <class TBase, class TWinTraits>

LRESULT CALLBACK CWindowImplBaseT< TBase, TWinTraits >::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

         CWindowImplBaseT< TBase, TWinTraits >* pThis = (CWindowImplBaseT< TBase, TWinTraits >*)hWnd;

         // set a ptr to this message and save the old value

         MSG msg = { pThis->m_hWnd, uMsg, wParam, lParam, 0, { 0, 0 } };

         const MSG* pOldMsg = pThis->m_pCurrentMsg;

         pThis->m_pCurrentMsg = &msg;

         // pass to the message map to process

         LRESULT lRes;

         BOOL bRet = pThis->ProcessWindowMessage(pThis->m_hWnd, uMsg, wParam, lParam, lRes, 0);

         // restore saved value for the current message

         ATLASSERT(pThis->m_pCurrentMsg == &msg);

         pThis->m_pCurrentMsg = pOldMsg;

         // do the default processing if message was not handled

         if(!bRet)

         {

                  if(uMsg != WM_NCDESTROY)

                            lRes = pThis->DefWindowProc(uMsg, wParam, lParam);

                   else

                   {

                            // unsubclass, if needed

                            LONG pfnWndProc = ::GetWindowLong(pThis->m_hWnd, GWL_WNDPROC);

                            lRes = pThis->DefWindowProc(uMsg, wParam, lParam);

                            if(pThis->m_pfnSuperWindowProc != ::DefWindowProc && ::GetWindowLong(pThis->m_hWnd, GWL_WNDPROC) == pfnWndProc)

                                     ::SetWindowLong(pThis->m_hWnd, GWL_WNDPROC, (LONG)pThis->m_pfnSuperWindowProc);

                            // clear out window handle

                            HWND hWnd = pThis->m_hWnd;

                            pThis->m_hWnd = NULL;

                            // clean up after window is destroyed

                            pThis->OnFinalMessage(hWnd);

                   }

         }

         return lRes;

}

在这个函数里,调用ProcessWindowMessage去分发消息,ProcessWindowMessage是在哪里实现的呢?没有找到自然是宏展开的:
#define BEGIN_MSG_MAP(theClass) /

public: /

         BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) /

         { /

                   BOOL bHandled = TRUE; /

                   hWnd; /

                   uMsg; /

                   wParam; /

                   lParam; /

                   lResult; /

                   bHandled; /

                   switch(dwMsgMapID) /

                   { /

#define MESSAGE_HANDLER(msg, func) /

         if(uMsg == msg) /

         { /

                   bHandled = TRUE; /

                   lResult = func(uMsg, wParam, lParam, bHandled); /

                   if(bHandled) /

                            return TRUE; /

         }

#define END_MSG_MAP() /

                            break; /

                   default: /

                            ATLTRACE2(atlTraceWindowing, 0, _T("Invalid message map ID (%i)/n"), dwMsgMapID); /

                            ATLASSERT(FALSE); /

                            break; /

                   } /

                   return FALSE; /

         }
原来BEGIN_MSG_MAP等函数就是用展开ProcessWindowMessage函数的,而ProcessWindowMessage又是为用来实现WinProc函数的,这下消息的流动过程就很清楚了。

另外与消息处理函数的两个类是CmessageFilter和CidleHandler,两者都是接口类,各定义了一个纯虚函数。在子类里实现它们,然后注册到CMessageLoop中去,CMessageLoop会在适当的时候调用它们。前者一般来转换快捷键,后者一般用来更新界面。

WTL 基础: 消息与消息反射

首先需要知道,消息是怎么来的。windows操作系统
  • xianlaowu
  • xianlaowu
  • 2014年04月12日 05:56
  • 745

WTL 窗口创建消息队列

ATLAPP.H包含了消息循环类、接口类、和产生应用程序所必需的一些基础类定义。        类定义如下:               CmessageFilter类---用于消息过滤的   ...
  • suhuaiqiang_janlay
  • suhuaiqiang_janlay
  • 2015年03月29日 11:09
  • 1331

WTL 学习笔记 -- 消息流

 正如刚从DOS转到Win32下写程序时,总是为找不到main函数而感到不爽,学习时WTL时,第一反应是找GetMessage和DispatchMessage,想知道消息是如何分发到窗口的。 在_tW...
  • lsm307742191
  • lsm307742191
  • 2009年03月08日 15:33
  • 1052

WTL入门(4)--- 对话框和控件

[源代码下载:http://download.csdn.net/source/3522801] MFC中,对话框和控件的封装节省了我们大量的时间和成本,否则我们需要编写大量的消息处理来管理各个控件。...
  • wcyoot
  • wcyoot
  • 2011年08月10日 10:28
  • 6321

VisualFC使用 - 处理WTL窗口消息

       WTL窗口消息一般使用ATL形式的消息,也可以使用WTL atlcrack.h中定义的WTL新型消息,VisualFC对这两种消息都支持。       新建或打开一个WTL项目,运行VF...
  • visualfc
  • visualfc
  • 2007年11月16日 10:39
  • 2356

WTL学习笔记之"Enter"和"ESC"的一点小心得

最近在玩WTL的时候,需要处理到回车和ESC键的问题,于是和以前在MFC的时候,老样子,直接写PreTranslateMessage来抓键盘消息(其实我比较喜欢在OnOK或者OnCancel中处理,毕...
  • blz_wowar
  • blz_wowar
  • 2008年03月28日 17:30
  • 3440

ATL和WTL中的消息反射

转载地址:  http://www.cppblog.com/qinqing1984/archive/2010/06/14/117872.html WTL是窗口模板库(Windows Librar...
  • szq2k08
  • szq2k08
  • 2015年07月16日 16:03
  • 476

WTL : create CtreeViewCtrl On CPaneContainerImpl

窗口切分后, 每个窗口都作为一个CPaneContain
  • LostSpeed
  • LostSpeed
  • 2014年06月08日 23:53
  • 1578

[ATL/WTL]_[初级]_[转发消息让CStatic支持点击消息操作]

场景: 1. CStatic支持不定长的字符串长度,还可以支持图片背景,当然要父控件响应 WM_CTLCOLORSTATIC 消息. 2. 可以响应点击事件的Cstatic可以当作简单按钮来使用,不需...
  • infoworld
  • infoworld
  • 2015年06月14日 20:45
  • 1671

WTL中消息映射初级

WTL 的消息映射机制和ATL的消息映射机制基本相同,WTL的消息机制的功能是在AtL的基础上加工细化了。都是通过宏定义实现 1:基本结构BEGIN_MSG_MAP(CMainFrame) ...
  • BlueCY
  • BlueCY
  • 2017年06月23日 11:34
  • 363
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:WTL 消息流
举报原因:
原因补充:

(最多只允许输入30个字)