⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 atlapp.h文件剖析.txt

📁 WTL源码剖析 --- ATLAPP.H
💻 TXT
📖 第 1 页 / 共 2 页
字号:
WTL源码剖析 --- ATLAPP.H  

作者:姜江
QQ:457283
E-mail:jznsmail@163.net

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));

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -