?? atlapp.h文件剖析.txt
字號:
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 + -