WTL 窗口创建消息队列

ATLAPP.H包含了消息循环类、接口类、和产生应用程序所必需的一些基础类定义。

       类定义如下:

              CmessageFilter类---用于消息过滤的

        CidleHandler 类---用于空闲消息处理的

        CmessageLoop类---用于消息循环的

              CappModule 类---应用程序基础类

              CserverAppModule类---用于Com服务构架的应用程序类

       另外还有3个全局函数:

              AtlGetDefaultGuiFont()获得默认的显示字体

              AtlCreateBoldFont()   产生一个粗体字体

              AtlInitCommonControls()初始化一些控件所需共同的DLL

      WTL程序的结构

       一个窗口程序的创建到销毁过程主要经过如下几个阶段

1. 注册窗口类

2. 创建窗口

3. 进入消息循环

如果用C写过Win32窗口程序的人一定会记得如下的结构:

//窗口过程处理函数

LRESULT CALLBACK WndProc(HWND hwnd,UINT Message,WPARAM wParam,LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR szCmdLine,int iCmdShow)

{

          HWND hwnd = NULL;

          MSG msg;

          

          WNDCLASS wndclass;

          wndclass.style       = CS_HREDRAW | CS_VREDRAW;

          wndclass.lpfnWndProc = WndProc;

     …

     //注册窗口

     if(!RegisterClass(&wndclass))

     {

          MessageBox(NULL,TEXT("Porgram requires Windows NT!"),szAppName,MB_ICONERROR);

          return 0;

          }

     //创建窗口

     hwnd = CreateWindow(szAppName,TEXT("My Application"),

     WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,

     CW_USEDEFAULT,CW_USEDEFAULT,

     CW_USEDEFAULT,CW_USEDEFAULT,

     NULL,NULL,hInstance,NULL);

 

          ShowWindow(hwnd,iCmdShow);

          UpdateWindow(hwnd);

        

         //进入消息循环

          while(GetMessage(&msg,NULL,0,0))

          {

               TranslateMessage(&msg);

               DispatchMessage(&msg);

     }

 

     return msg.wParam;

}

那么你可能会问WTL的WinMain函数再哪里?如果你通过WTL/ATL导向生成一个应用程序,那么你会在跟工程名字同名的.cpp文件中发现如下的代码:

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)

{

     HRESULT hRes = ::CoInitialize(NULL);

// If you are running on NT 4.0 or higher you can use the following call instead to

// make the EXE free threaded. This means that calls come in on a random RPC thread.

//     HRESULT hRes = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);

          ATLASSERT(SUCCEEDED(hRes));

 

// this resolves ATL window thunking problem when Microsoft Layer for Unicode (MSLU) is used

          ::DefWindowProc(NULL, 0, 0, 0L);

 

        AtlInitCommonControls(ICC_COOL_CLASSES | ICC_BAR_CLASSES); // add flags to support other controls

 

     hRes = _Module.Init(NULL, hInstance); //等下分析它的实现      ATLASSERT(SUCCEEDED(hRes));

 

     int nRet = Run(lpstrCmdLine, nCmdShow);//程序的关键分

 

     _Module.Term();

     ::CoUninitialize();

 

     return nRet;

}   

从这个_tWinMain函数的定义,你可以发现程序的关键部分是我紫色标记出来的Run()函数。这个函数是一个自定义的函数,不过如果通过ATL/WTL导向程序,那么会自动生成这样一个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;

}

通过这个Run函数我们可以看到在函数中完成了如下几个过程:

1. 生成一个消息循环对象(theLoop)

2. 在全局的_Module中加入这个消息循环

3. 生成一个应用程序框架对象

4. 显示应用程序框架

5. 开始消息循环

6. 结束消息循环

7. 返回WinMain函数,结束程序

实现分析

在这篇文章我不想过多的分析应用程序框架和窗口的细节,这些内容将放在以后的几篇文章中详细分析,本文主要对ATLAPP.H头文件中实现的一些过程进行详细分析。

首先从全局变量_Module开始。

_Module维持着生成应用程序的主线程,控制着程序的消息循环队列,是一个CAppModule的对象。该CAppModule从ATL::CcomModule继承。

在WTL::CappModule中定义了8个公有成员函数,分别为:

AddMessageLoop()添加一个消息循环,进入消息循环队列里。

RemoveMessageLoop()移除消息循环队列。

GetMessageLoop()获得消息循环。

InitSettingChangeNotify()初始化环境

AddSettingChangeNotify()添加一个窗口句柄。

RemoveSettingChangeNotify()清理环境

除了8个公有成员函数外,该类还定义了3个公有成员变量

m_dwMainThreadID负责保存该应用程序的主线程ID

m_pMsgLoopMap负责存储消息循环

m_pSettingChangeNotify负责存放窗口句柄

下面分别来分析几个主要成员函数的实现:

BOOL AddMessageLoop(CMessageLoop* pMsgLoop)

{

     CStaticDataInitCriticalSectionLock lock;

     //锁住关键片断,由于进程同步的关系!!!

     if(FAILED(lock.Lock()))

     {

          ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::AddMessageLoop.\n"));

          ATLASSERT(FALSE);

     return FALSE;

     }

        

     ATLASSERT(pMsgLoop != NULL);

     ATLASSERT(m_pMsgLoopMap->Lookup(::GetCurrentThreadId()) == NULL);   // not in map yet

 

     BOOL bRet = m_pMsgLoopMap->Add(::GetCurrentThreadId(), pMsgLoop);

 

     lock.Unlock();

 

     return bRet;

}

     关键部分我用红色的字体标记出来了,意思是什么?通过当前线程的Id来标示一个消息循环,存储在m_pMsgLoopMap中。

 

BOOL RemoveMessageLoop()

     {

          CStaticDataInitCriticalSectionLock lock;

          if(FAILED(lock.Lock()))

         {

              ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::RemoveMessageLoop.\n"));

              ATLASSERT(FALSE);

              return FALSE;

         }

 

         BOOL bRet = m_pMsgLoopMap->Remove(::GetCurrentThreadId());

 

          lock.Unlock();

 

         return bRet;

     }

       关键部分同样通过红色字体标记出来,嗯,没错正如AddMessageLoop函数一样,该函数也是通过线程Id来寻找消息循环移除对象的。

 

CMessageLoop* GetMessageLoop(DWORD dwThreadID = ::GetCurrentThreadId()) const

     {

          CStaticDataInitCriticalSectionLock lock;

          if(FAILED(lock.Lock()))

         {

              ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::GetMessageLoop.\n"));

              ATLASSERT(FALSE);

              return NULL;

         }

 

          CMessageLoop* pLoop = m_pMsgLoopMap->Lookup(dwThreadID);

 

          lock.Unlock();

 

         return pLoop;

     }

该函数通过线程Id在m_pMsgLoopMap消息队列中寻找对应的消息循环,找到后返回。

 

     BOOL InitSettingChangeNotify(DLGPROC pfnDlgProc = _SettingChangeDlgProc)

     {

          CStaticDataInitCriticalSectionLock lock;

          if(FAILED(lock.Lock()))

         {

              ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::InitSettingChangeNotify.\n"));

              ATLASSERT(FALSE);

              return FALSE;

         }

 

          if(m_pSettingChangeNotify == NULL)

         {

              typedef ATL::CSimpleArray<HWND>   _notifyClass;

              ATLTRY(m_pSettingChangeNotify = new _notifyClass);

              ATLASSERT(m_pSettingChangeNotify != NULL);

         }

 

         BOOL bRet = (m_pSettingChangeNotify != NULL);

          if(bRet && m_pSettingChangeNotify->GetSize() == 0)

         {

              // init everything

              _ATL_EMPTY_DLGTEMPLATE templ;

              //增加一个无模式对话框

              HWND hNtfWnd = ::CreateDialogIndirect(GetModuleInstance(), &templ, NULL, pfnDlgProc);

              ATLASSERT(::IsWindow(hNtfWnd));

              if(::IsWindow(hNtfWnd))

              {

// need conditional code because types don't match in winuser.h

#ifdef _WIN64

                   ::SetWindowLongPtr(hNtfWnd, GWLP_USERDATA, (LONG_PTR)this);

#else

                   ::SetWindowLongPtr(hNtfWnd, GWLP_USERDATA, PtrToLong(this));

#endif

                   //加入该窗口句柄

                   bRet = m_pSettingChangeNotify->Add(hNtfWnd);

              }

              else

              {

                   bRet = FALSE;

              }

         }

 

          lock.Unlock();

 

         return bRet;

     }

该函数用来初始化一个存放窗口句柄的对象

 

     BOOL InitSettingChangeNotify(DLGPROC pfnDlgProc = _SettingChangeDlgProc)

     {

          CStaticDataInitCriticalSectionLock lock;

          if(FAILED(lock.Lock()))

         {

              ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::InitSettingChangeNotify.\n"));

              ATLASSERT(FALSE);

              return FALSE;

         }

 

          if(m_pSettingChangeNotify == NULL)

         {

              typedef ATL::CSimpleArray<HWND>   _notifyClass;

              ATLTRY(m_pSettingChangeNotify = new _notifyClass);

              ATLASSERT(m_pSettingChangeNotify != NULL);

         }

 

         BOOL bRet = (m_pSettingChangeNotify != NULL);

          if(bRet && m_pSettingChangeNotify->GetSize() == 0)

         {

              // init everything

              //??空的ATL Dialog Template吗?

              _ATL_EMPTY_DLGTEMPLATE templ;

              //增加一个无模式对话框

              HWND hNtfWnd = ::CreateDialogIndirect(GetModuleInstance(), &templ, NULL, pfnDlgProc);

              ATLASSERT(::IsWindow(hNtfWnd));

              if(::IsWindow(hNtfWnd))

              {

// need conditional code because types don't match in winuser.h

#ifdef _WIN64

                   ::SetWindowLongPtr(hNtfWnd, GWLP_USERDATA, (LONG_PTR)this);

#else

                   ::SetWindowLongPtr(hNtfWnd, GWLP_USERDATA, PtrToLong(this));

#endif

                   bRet = m_pSettingChangeNotify->Add(hNtfWnd);

              }

              else

              {

                   bRet = FALSE;

              }

         }

 

          lock.Unlock();

 

         return bRet;

     }

 

     //清理消息

     void TermSettingChangeNotify()

     {

          CStaticDataInitCriticalSectionLock lock;

          if(FAILED(lock.Lock()))

         {

              ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::TermSettingChangeNotify.\n"));

              ATLASSERT(FALSE);

              return;

         }

 

          if(m_pSettingChangeNotify != NULL && m_pSettingChangeNotify->GetSize() > 0)

              //销毁窗口

              ::DestroyWindow((*m_pSettingChangeNotify)[0]);

         delete m_pSettingChangeNotify;

          m_pSettingChangeNotify = NULL;

 

          lock.Unlock();

     }

 

BOOL AddSettingChangeNotify(HWND hWnd)

     {

          CStaticDataInitCriticalSectionLock lock;

          if(FAILED(lock.Lock()))

         {

              ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::AddSettingChangeNotify.\n"));

              ATLASSERT(FALSE);

              return FALSE;

         }

 

          ATLASSERT(::IsWindow(hWnd));

         BOOL bRet = FALSE;

          if(InitSettingChangeNotify() != FALSE)

              bRet = m_pSettingChangeNotify->Add(hWnd);

 

          lock.Unlock();

 

         return bRet;

     }

 

BOOL RemoveSettingChangeNotify(HWND hWnd)

     {

          CStaticDataInitCriticalSectionLock lock;

          if(FAILED(lock.Lock()))

         {

              ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::RemoveSettingChangeNotify.\n"));

              ATLASSERT(FALSE);

              return FALSE;

         }

 

         BOOL bRet = FALSE;

          if(m_pSettingChangeNotify != NULL)

              bRet = m_pSettingChangeNotify->Remove(hWnd);

 

          lock.Unlock();

 

         return bRet;

     }

 

     现 在回到刚才提到的Run()函数,里面最开始就定义了一个CmessageLoop循环对象,然后通过_Module对象的AddMessageLoop 成员函数加入到循环队列里面,直到_Module调用了RemoveMessageLoop移除循环队列,程序才结束循环,返回到WinMain函数。

     在这里还有一个比较重要的类,那就是CMessageLoop,是他维持了系统的消息,维持了程序的生命周期。那么下面我们来看看这个类的定义和具体的实现方法。

CmessageLoop包含了如下一些成员函数和成员变量

成员变量

//处理消息

     ATL::CSimpleArray<CMessageFilter*> m_aMsgFilter;

     //处理空闲句柄

     ATL::CSimpleArray<CIdleHandler*> m_aIdleHandler;

     //Win32API消息结构

     MSG m_msg;

 

     成员函数(用红色标记的函数是虚函数)

AddMessageFilter         加入一条消息过滤

RemoveMessageFilter      移除一条消息过滤

AddIdleHandler       加入一个空闲句柄

RemoveIdleHandler         移出一个空闲句柄

AddUpdateUI              为了兼容老的ATL而设计的

RemoveUpdateUI           为了兼容老的ATL而设计的

IsIdleMessage            过滤一些比如WM_MOUSEMOVE之类的消息

Run                      消息循环。关键部分!!!

PreTranslateMessage      消息过滤

OnIdle                   空闲处理

 

再这里我不准备对每个函数都进行详细的分析,主要分析核心的函数Run,CmessageLoop由它来维持着系统的消息循环。

函数如下:

int Run()

     {

         //空闲?

         BOOL bDoIdle = TRUE;

         //空闲计数器

         int nIdleCount = 0;

         //返回标志

         BOOL bRet;

 

         //开始消息循环了哦!!!

          for(;;)

         {

 

              //当bDoIdle为TRUE,并且不能从消息队列里面取出消息了,那么开始空闲操作了!

              //PM_NOREMOVE:再PeekMessage函数处理后不将消息从队列里移除

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

              {

                   if(!OnIdle(nIdleCount++))

                        bDoIdle = FALSE;

              }

             

              //从当前线程获取一个消息

              //返回-1表示出现一个错误

              //返回 0表示提交了一个WM_QUIT,程序将要退出

              //成功获得一个消息,返回不等于0的值

              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

              }

 

              //如果熟悉使用c语言来写Win32的程序员会发现,原来WinMain中的哪个处理消息循环的语句放到这里来了!!!

              if(!PreTranslateMessage(&m_msg))

              {

                   //translates virtual-key messages into character messages.

                   ::TranslateMessage(&m_msg);

                   //dispatches a message to a window procedure

                   ::DispatchMessage(&m_msg);

              }

             

              //判断是否为空闲消息?

              //排除WM_MOUSEMOVE WM_NCMOUSEMOVE WM_SYSTIMER消息

              if(IsIdleMessage(&m_msg))

              {

                   bDoIdle = TRUE;

                   nIdleCount = 0;

              }

         }

 

         return (int)m_msg.wParam;

     }

以上就是对ATLAPP.H中的几个比较重要的类的分析,还有其他几个类的分析我将放在以后的文章中

(待续。。。)

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值