在ATL中初始时给窗口指定的消息过程是StartWindowProc,它通过ExtractCreateWndData函数找到窗口的This指针,然后才能应用汇编指令偷天换日地把系统传递进来的窗口句柄和窗体对象的This指针对应起来,这些都是后话,问题是,ExtractCreateWndData函数是怎么找到属于当前窗口句柄的This指针的呢?一个窗口对象建立起来以后,通过Create函数创建窗体:template <class TBase, class TWinTraits>
HWND CWindowImplBaseT< TBase, TWinTraits >::Create(HWND hWndParent, _U_RECT rect, LPCTSTR szWindowName, DWORD dwStyle, DWORD dwExStyle, _U_MENUorID MenuOrID, ATOM atom, LPVOID lpCreateParam)
{
………… _AtlWinModule.AddCreateWndData(&m_thunk.cd, this);         ………… HWND hWnd = ::CreateWindowEx(…………); ATLASSERT(m_hWnd == hWnd); return hWnd;
}旁的不说,仅注意这两个语句就行了,AddCreateWndData函数,把窗口对象的This指针保存在_AtlWinModule的创建窗口对象列表(m_pCreateWndList)中,然后调用CreateWindowEx创建窗口时,系统第一次调用回调函数StartWindowProc,并将窗体句柄传递进来,StartWindowProc的工作就是把这个窗体句柄和相应的窗体对象This指针联系起来,使得后续消息到达的时候能找到该对象的消息处理函数。template <class TBase, class TWinTraits>
LRESULT CALLBACK CWindowImplBaseT< TBase, TWinTraits >::StartWindowProc(……)
{
CWindowImplBaseT< TBase, TWinTraits >* pThis = (CWindowImplBaseT< TBase, TWinTraits >*)_AtlWinModule.ExtractCreateWndData();          …………
}这里的ExtractCreateWndData取出的指针就是AddCreateWndData添加进去的。现在分别看看AddCreateWndData和ExtractCreateWndData的代码:ATLINLINE ATLAPI_(void) AtlWinModuleAddCreateWndData(_ATL_WIN_MODULE* pWinModule, _AtlCreateWndData* pData, void* pObject)
{
…………

pData->m_pThis = pObject;
pData->m_dwThreadID = ::GetCurrentThreadId(); CComCritSecLock<CComCriticalSection> lock(pWinModule->m_csWindowCreate, false);
if (FAILED(lock.Lock()))
{
…………
} pData->m_pNext = pWinModule->m_pCreateWndList;
pWinModule->m_pCreateWndList = pData;
}ATLINLINE ATLAPI_(void) AtlWinModuleAddCreateWndData(_ATL_WIN_MODULE* pWinModule, _AtlCreateWndData* pData, void* pObject)
{
if (pWinModule == NULL)
_AtlRaiseException(EXCEPTION_ACCESS_VIOLATION); ATLASSERT(pData != NULL && pObject != NULL);
if(pData == NULL || pObject == NULL)
_AtlRaiseException(EXCEPTION_ACCESS_VIOLATION);

pData->m_pThis = pObject;
pData->m_dwThreadID = ::GetCurrentThreadId();
CComCritSecLock<CComCriticalSection> lock(pWinModule->m_csWindowCreate, false);
if (FAILED(lock.Lock()))
{
ATLTRACE(atlTraceWindowing, 0, _T("ERROR : Unable to lock critical section in AtlWinModuleAddCreateWndData\n"));
ATLASSERT(0);
return;
}
pData->m_pNext = pWinModule->m_pCreateWndList;
pWinModule->m_pCreateWndList = pData;
}ATLINLINE ATLAPI_(void*) AtlWinModuleExtractCreateWndData(_ATL_WIN_MODULE* pWinModule)
{
………… void* pv = NULL;
CComCritSecLock<CComCriticalSection> lock(pWinModule->m_csWindowCreate, false);
if (FAILED(lock.Lock()))
{
…………
} _AtlCreateWndData* pEntry = pWinModule->m_pCreateWndList;
if(pEntry != NULL)
{
DWORD dwThreadID = ::GetCurrentThreadId();
_AtlCreateWndData* pPrev = NULL;
while(pEntry != NULL)
{
if(pEntry->m_dwThreadID == dwThreadID)
{
if(pPrev == NULL)
pWinModule->m_pCreateWndList = pEntry->m_pNext;
else
pPrev->m_pNext = pEntry->m_pNext;
pv = pEntry->m_pThis;
break;
}
pPrev = pEntry;
pEntry = pEntry->m_pNext;
}
}
return pv;
}这里面的AtlCreateWndData结构定义如下:struct AtlCreateWndData
{
void* m_pThis;
DWORD m_dwThreadID;
_AtlCreateWndData* m_pNext;
};也就是说,AddCreateWndData创建一个AtlCreateWndData结构,把This指针和线程ID保存进去,然后把这个结构挂接到全局模块对象的创建窗口链表的链首。ExtractCreateWndData顺序检索链表,找到第一个线程号匹配的AtlCreateWndData对象,并将这个对象中保存的This指针返回。按照我的理解,因为一个线程只可能顺序的创建多个窗口,所以这个是线程安全的。就是说,一个线程,它把窗口对象的This指针保存到模块的列表中以后,调用Create创建窗口,在创建过程中,由系统调用StartWindowProc函数从列表上取出This指针(按照线程号比配,能够保证取出的一定是该线程刚才插进去的This指针),然后Create函数返回。如果StartWindowProc函数的调用不是在Create过程中发生的话,那么有可能一个线程连续Create两个窗口,然后才接到系统调用StartWindowProc的消息,那么就会把属于后一个窗口的This指针和前一个窗口对象的句柄对应起来,从而发生错误。不知道我的这个理解是否正确,请大家帮助讨论,批评指正。另外,程序中的互斥对象是用于什么的?为什么只见Lock不见UnLock?UnLock的代码在哪里?
请高手指点!谢谢!