ATL消息处理机制参考模板.docx

上传人:b****1 文档编号:13239983 上传时间:2023-06-12 格式:DOCX 页数:19 大小:21.63KB
下载 相关 举报
ATL消息处理机制参考模板.docx_第1页
第1页 / 共19页
ATL消息处理机制参考模板.docx_第2页
第2页 / 共19页
ATL消息处理机制参考模板.docx_第3页
第3页 / 共19页
ATL消息处理机制参考模板.docx_第4页
第4页 / 共19页
ATL消息处理机制参考模板.docx_第5页
第5页 / 共19页
ATL消息处理机制参考模板.docx_第6页
第6页 / 共19页
ATL消息处理机制参考模板.docx_第7页
第7页 / 共19页
ATL消息处理机制参考模板.docx_第8页
第8页 / 共19页
ATL消息处理机制参考模板.docx_第9页
第9页 / 共19页
ATL消息处理机制参考模板.docx_第10页
第10页 / 共19页
ATL消息处理机制参考模板.docx_第11页
第11页 / 共19页
ATL消息处理机制参考模板.docx_第12页
第12页 / 共19页
ATL消息处理机制参考模板.docx_第13页
第13页 / 共19页
ATL消息处理机制参考模板.docx_第14页
第14页 / 共19页
ATL消息处理机制参考模板.docx_第15页
第15页 / 共19页
ATL消息处理机制参考模板.docx_第16页
第16页 / 共19页
ATL消息处理机制参考模板.docx_第17页
第17页 / 共19页
ATL消息处理机制参考模板.docx_第18页
第18页 / 共19页
ATL消息处理机制参考模板.docx_第19页
第19页 / 共19页
亲,该文档总共19页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

ATL消息处理机制参考模板.docx

《ATL消息处理机制参考模板.docx》由会员分享,可在线阅读,更多相关《ATL消息处理机制参考模板.docx(19页珍藏版)》请在冰点文库上搜索。

ATL消息处理机制参考模板.docx

ATL消息处理机制参考模板

ATL消息机制的探究

作者:

后知后觉(307817387)

任何的框架,包括MFC或者ATL,创建并显示窗口,处理窗口消息都逃不过RegisterClass、CreateWindow和MessageLoop。

对于ATL也是一样的道理,下面就来细说一下ATL的消息处理机制。

重要的部分我会用红色标识出来。

1,注册窗口类

CWindowImpl类使用一个DECLARE_WND_CLASS(NULL)的宏来定义WNDCLASS的信息

#defineDECLARE_WND_CLASS(WndClassName)\

staticATL:

:

CWndClassInfo&GetWndClassInfo()\

{\

staticATL:

:

CWndClassInfowc=\

{\

{sizeof(WNDCLASSEX),CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,StartWindowProc,\

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

NULL,NULL,IDC_ARROW,TRUE,0,_T("")\

};\

returnwc;\

}

这里有一个很重要的信息,那就是StartWindowProc,这个是定义的默认的窗口处理函数。

先提醒一下,后面有具体说明。

CWndClassInfo的定义如下:

struct_ATL_WNDCLASSINFOW

{

WNDCLASSEXWm_wc;

LPCWSTRm_lpszOrigName;

WNDPROCpWndProc;

LPCWSTRm_lpszCursorID;

BOOLm_bSystemCursor;

ATOMm_atom;

WCHARm_szAutoName[5+sizeof(void*)*CHAR_BIT];

ATOMRegister(WNDPROC*p)

{

returnAtlWinModuleRegisterWndClassInfoW(&_AtlWinModule,&_AtlBaseModule,this,p);

}

};

其中的Register方法会注册一个窗口类。

在CWindowImpl的Create方法中:

HWNDCreate(HWNDhWndParent,_U_RECTrect=NULL,LPCTSTRszWindowName=NULL,

DWORDdwStyle=0,DWORDdwExStyle=0,

_U_MENUorIDMenuOrID=0U,LPVOIDlpCreateParam=NULL)

{

if(T:

:

GetWndClassInfo().m_lpszOrigName==NULL)

T:

:

GetWndClassInfo().m_lpszOrigName=GetWndClassName();

ATOMatom=T:

:

GetWndClassInfo().Register(&m_pfnSuperWindowProc);

dwStyle=T:

:

GetWndStyle(dwStyle);

dwExStyle=T:

:

GetWndExStyle(dwExStyle);

//setcaption

if(szWindowName==NULL)

szWindowName=T:

:

GetWndCaption();

returnCWindowImplBaseT:

:

Create(hWndParent,rect,szWindowName,

dwStyle,dwExStyle,MenuOrID,atom,lpCreateParam);

}

这里Register的里面的具体实现很复杂,不过其本质就是注册窗口类,具体里面的实现的作用就是根据GetWndClassInfo里面定义的结构体里面的内容生成一个WNDCLASSEX的信息,窗口过程函数也是同理,然后注册成窗口类。

同时对传入的m_pfnSuperWindowProc赋值,其值就是定义的StartWindowProc。

自此,可以的出,创建窗口生成消息之后第一步会到StartWindowProc中去执行。

具体StartWindowProc是什么呢?

其实它是CWindowImplBaseT里面定义的一个静态函数:

staticLRESULTCALLBACKStartWindowProc(HWNDhWnd,UINTuMsg,WPARAMwParam,LPARAMlParam);

T:

:

GetWndClassInfo函数就是调用DECLARE_WND_CLASS宏定义的方法,其本质就是返回一个定义好的CWndClassInfo对象。

这里在创建窗口之前先调用Register方法注册一个窗口类,上面也说到了过。

这里的T:

:

GetWndClassInfo和T:

:

GetWndStyle这些方法的调用很巧妙,它有利的避开了虚函数机制同时能够实现动态调用的功效。

T就是我们自定义的类如CMainDlg,CMainDlg继承自CWindowImpl,CWindowImpl继承自CWindowImplBaseT,CWindowImplBaseT继承自CWindowImplRoot,最终CWindowImplRoot继承自TBase也就是后面的类中传递进来的CWindow,同事也继承了CMessage。

CWindow用来封装HWND,CMessage用来封装消息循环。

总之T就是我们定义的最终的子类CMainDlg,所以继承链中任何一个类定义了GetWndClassInfo或者GetWndStyle方法,T:

:

GetWndClassInfo或者T:

:

GetWndStyle就可以调用到该方法,这样的话,如果子类要重写该方法,即使在父类中调用T:

:

GetWndClassInfo或T:

:

GetWndStyle也是调用子类重写过的版本,实现“多态”的效果。

2,创建窗口

窗口类注册好了之后调用CWindowImplBaseT的Create方法:

template

HWNDCWindowImplBaseT:

:

Create(HWNDhWndParent,_U_RECTrect,LPCTSTRszWindowName,DWORDdwStyle,DWORDdwExStyle,_U_MENUorIDMenuOrID,ATOMatom,LPVOIDlpCreateParam)

{

BOOLresult;

ATLASSUME(m_hWnd==NULL);

//Allocatethethunkstructurehere,wherewecanfailgracefully.

result=m_thunk.Init(NULL,NULL);

if(result==FALSE){

SetLastError(ERROR_OUTOFMEMORY);

returnNULL;

}

if(atom==0)

returnNULL;

_AtlWinModule.AddCreateWndData(&m_thunk.cd,this);

if(MenuOrID.m_hMenu==NULL&&(dwStyle&WS_CHILD))

MenuOrID.m_hMenu=(HMENU)(UINT_PTR)this;

if(rect.m_lpRect==NULL)

rect.m_lpRect=&TBase:

:

rcDefault;

HWNDhWnd=:

:

CreateWindowEx(dwExStyle,MAKEINTATOM(atom),szWindowName,

dwStyle,rect.m_lpRect->left,rect.m_lpRect->top,rect.m_lpRect->right-rect.m_lpRect->left,

rect.m_lpRect->bottom-rect.m_lpRect->top,hWndParent,MenuOrID.m_hMenu,

_AtlBaseModule.GetModuleInstance(),lpCreateParam);

ATLASSUME(m_hWnd==hWnd);

returnhWnd;

}

A,_AtlWinModule.AddCreateWndData(&m_thunk.cd,this)的说明:

这里有几个重要的地方,第一点就是每一个CWindowImpl保存了一个变量m_thunk,这个变量很重要,它的类型是CWndProcThunk,定义如下:

classCWndProcThunk

{

public:

_AtlCreateWndDatacd;

CStdCallThunkthunk;

BOOLInit(WNDPROCproc,void*pThis)

{

returnthunk.Init((DWORD_PTR)proc,pThis);

}

WNDPROCGetWNDPROC()

{

return(WNDPROC)thunk.GetCodeAddress();

}

};

其中cd的定义如下:

struct_AtlCreateWndData

{

void*m_pThis;

DWORDm_dwThreadID;

_AtlCreateWndData*m_pNext;

};

_AtlCreateWndData的本质就是链表的一个节点,有一个指向后面节点的指针m_pNext。

m_pThis用来保存当前对象也就是生成的CMainDlg对象。

m_dwThreadID用来保存当前的线程ID。

下面就来说明一下保存这两个值的具体作用。

这里有一句很重要的代码_AtlWinModule.AddCreateWndData(&m_thunk.cd,this);具体是做什么的呢?

_AtlWinModule是一个CAtlWinModule的对象,CAtlWindModule继承自_ATL_WIN_MODULE,_ATL_WIN_MODULE的定义如下:

struct_ATL_WIN_MODULE70

{

UINTcbSize;

CComCriticalSectionm_csWindowCreate;

_AtlCreateWndData*m_pCreateWndList;

CSimpleArraym_rgWindowClassAtoms;

};

typedef_ATL_WIN_MODULE70_ATL_WIN_MODULE;

这里可以看出,_AtlWinModlue其实保存了一个_AtlCreateWndData的指针,这个指针就是用来构建一个窗口类链表的头结点。

_AtlWinModule.AddCreateWndData(&m_thunk.cd,this);的作用就是将刚刚新建的this(就是CMainDlg的对象的指针)保存到链表的头部。

这句话的具体实现是这样的:

voidAddCreateWndData(_AtlCreateWndData*pData,void*pObject)

{

AtlWinModuleAddCreateWndData(this,pData,pObject);

}

ATLINLINEATLAPI_(void)AtlWinModuleAddCreateWndData(_ATL_WIN_MODULE*pWinModule,_AtlCreateWndData*pData,void*pObject)

{

if(pWinModule==NULL)

_AtlRaiseException((DWORD)EXCEPTION_ACCESS_VIOLATION);

ATLASSERT(pData!

=NULL&&pObject!

=NULL);

if(pData==NULL||pObject==NULL)

_AtlRaiseException((DWORD)EXCEPTION_ACCESS_VIOLATION);

pData->m_pThis=pObject;

pData->m_dwThreadID=:

:

GetCurrentThreadId();

CComCritSecLocklock(pWinModule->m_csWindowCreate,false);

if(FAILED(lock.Lock()))

{

ATLTRACE(atlTraceWindowing,0,_T("ERROR:

UnabletolockcriticalsectioninAtlWinModuleAddCreateWndData\n"));

ATLASSERT(0);

return;

}

pData->m_pNext=pWinModule->m_pCreateWndList;

pWinModule->m_pCreateWndList=pData;

}

这里的最后两句话可以很清楚的看到,已经将刚刚生成的_AtlCreateWndData节点也就是m_thunk里面的cd值(这里保存的就是当前对象CMainDlg)添加到了全局的_AtlWinModlue的链表的头部。

对于_AtlWinModule.AddCreateWndData的说明就先到这里,下文将说明保存保存这个指针的意义之所在。

回到上面创建的过程里面,现在已经将目前的CMainDlg对象的信息保存在了全局链表的头部,之后就使用CreateWindowEx方法来创建真正的窗口了。

HWNDhWnd=:

:

CreateWindowEx(dwExStyle,MAKEINTATOM(atom),szWindowName,

dwStyle,rect.m_lpRect->left,rect.m_lpRect->top,rect.m_lpRect->right-rect.m_lpRect->left,

rect.m_lpRect->bottom-rect.m_lpRect->top,hWndParent,MenuOrID.m_hMenu,

_AtlBaseModule.GetModuleInstance(),lpCreateParam);

在创建的时候会触发Windows消息如WM_NCCREATE,WM_CREATE等消息,很自然的,这些消息肯定会由窗口过程函数来处理。

在最初的窗口类定义中可以看到入口函数为StartWindowProc,它是一个静态的成员函数,定义在CWindowImplBaseT中。

此时消息会先由这个函数处理,内部逻辑如下:

template

LRESULTCALLBACKCWindowImplBaseT:

:

StartWindowProc(HWNDhWnd,UINTuMsg,WPARAMwParam,LPARAMlParam)

{

CWindowImplBaseT*pThis=(CWindowImplBaseT*)_AtlWinModule.ExtractCreateWndData();

ATLASSERT(pThis!

=NULL);

if(!

pThis)

{

return0;

}

pThis->m_hWnd=hWnd;

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

WNDPROCpProc=pThis->m_thunk.GetWNDPROC();

WNDPROCpOldProc=(WNDPROC):

:

SetWindowLongPtr(hWnd,GWLP_WNDPROC,(LONG_PTR)pProc);

#ifdef_DEBUG

if(pOldProc!

=StartWindowProc)

ATLTRACE(atlTraceWindowing,0,_T("Subclassingthroughahookdiscarded.\n"));

#else

(pOldProc);//avoidunusedwarning

#endif

returnpProc(hWnd,uMsg,wParam,lParam);

}

第一句_AtlWinModule.ExtractCreateWndData();先将刚刚保存在链表头部的节点取出来,其结果就是得到刚刚添加到头部的对象指针。

B,_AtlWinModule.ExtractCreateWndData()的说明

void*ExtractCreateWndData()

{

returnAtlWinModuleExtractCreateWndData(this);

}

ATLINLINEATLAPI_(void*)AtlWinModuleExtractCreateWndData(_ATL_WIN_MODULE*pWinModule)

{

if(pWinModule==NULL)

returnNULL;

void*pv=NULL;

CComCritSecLocklock(pWinModule->m_csWindowCreate,false);

if(FAILED(lock.Lock()))

{

ATLTRACE(atlTraceWindowing,0,_T("ERROR:

UnabletolockcriticalsectioninAtlWinModuleExtractCreateWndData\n"));

ATLASSERT(0);

returnpv;

}

_AtlCreateWndData*pEntry=pWinModule->m_pCreateWndList;

if(pEntry!

=NULL)

{

DWORDdwThreadID=:

:

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;

}

}

returnpv;

}

这里可以看出,先将头部节点取出来,查看线程ID是否是和当前ID一致。

这个很重要。

下面就分析一下原因。

每一个线程都是按一条指令一条指令去执行,这里的_AtlWinModule保存的可能会有很多线程创建的类似CMainDlg的对象,也依次添加到了头部。

所以如果取出来的不是当前线程的ID对应的对象的话,就继续往下找。

如果找到了是当前线程的对象,按照指令一条一条往下执行的原则,这个对象绝对是在Create方法中调用_AtlWinModule.AddCreateWndData(&m_thunk.cd,this);这一句话来添加的。

因为线程里面的指令都是按条往下执行的,所以按照顺序,Create之后马上就会触发Windows消息,在这中间过程中,全局的链表中的对象顺序是没有被库中的其他地方改变(从本质上来说肯定是可以改变的)。

最终获得的效果就是在Create方法中将对象添加到全局链表的头部,在StartWindowProc中取出来,然后将该对象的m_hWnd和刚刚创建的hWnd关联起来。

(其实封装HWND的窗口类本质就是要把hWnd的值保存到自己的m_hWnd变量中,同时还要将任何与hWnd相关的消息流入到窗口类中去执行)。

pThis->m_hWnd=hWnd;(这句话就是关联之处)。

C,下面来讲讲thunk技术

回到上面的StartWindowProc中去,关联好了之后还有以下几句关键代码:

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

WNDPROCpProc=pThis->m_thunk.GetWNDPROC();

WNDPROCpOldProc=(WNDPROC):

:

SetWindowLongPtr(hWnd,GWLP_WNDPROC,(LONG_PTR)pProc);

returnpProc(hWnd,uMsg,wParam,lParam);

Thunk代码定义如下:

classCWndProcThunk

{

public:

_AtlCreateWndDatacd;

CStdCallThunkthunk;

BOOLInit(WNDPROCproc,void*pThis)

{

returnthunk.Init((DWORD_PTR)proc,pThis);

}

WNDPROCGetWNDPROC()

{

return(WNDPROC)thunk.GetCodeAddress();

}

};

#pragmapack(push,1)

struct_stdcallthunk

{

DWORDm_mov;//movdwordptr[esp+0x4],pThis(esp+0x4ishWnd)

DWORDm_this;//

BYTEm_jmp;//jmpWndProc

DWORDm_relproc;//relativejmp

BOOLInit(DWORD_PTRproc,void*pThis)

{

m_mov=0x042444C7;//C744240C

m_this=PtrToUlong(pThis);

m_jmp=0xe9;

m_rel

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 经管营销 > 生产经营管理

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2