AFX_MANAGE_STATE

MFC对于跨模块之间的调用破费心机,设计了一条STATE机制.而且设计的颇为麻烦.费了老鼻子劲终于探究了一二而已.

转一篇分析的比较好的文章.

http://hi.baidu.com/rootlife/blog/item/2f37e354ad8cdc5bd10906be.html

这里补充几点:

1._afxthreadstate 是全局的,至于每个线程中维护一个_afxthreadstate变量,还是进程中维护一个_afxthreadstate变量.这个有待验证.

2. 每个模块会维护一个模块状态.如果是动态链接库的话,模块状态的定义在dllmodule.cpp中,static _AFX_DLL_MODULE_STATE afxModuleState;如果是可执行文件的话,模块状态的定义在afxstate.cpp中,PROCESS_LOCAL(_AFX_BASE_MODULE_STATE, _afxBaseModuleState)可以看到是进程唯一的.

3. AfxGetModuleThreadState(),去当前模块状态中的m_thread.可以看一下源代码是

AFX_MODULE_THREAD_STATE* AFXAPI AfxGetModuleThreadState()

{

AFX_MODULE_THREAD_STATE* pResult=AfxGetModuleState()->m_thread.GetData();

ENSURE(pResult != NULL);

return pResult;

}

而AfxGetModuleState()的源代码

AFX_MODULE_STATE* AFXAPI AfxGetModuleState()

{

_AFX_THREAD_STATE* pState = _afxThreadState;

ENSURE(pState);

AFX_MODULE_STATE* pResult;

if (pState->m_pModuleState != NULL)

{

// thread state's module state serves as override

pResult = pState->m_pModuleState;

}

else

{

// otherwise, use global app state

pResult = _afxBaseModuleState.GetData();

}

ENSURE(pResult != NULL);

return pResult;

}

一般是取当前模块的模块状态,如果模块状态为空的话,就取可执行文件的模块状态.一般情况下当前模块的状态是NULL的.也就是说一般取的都是可执行文件的模块状态.

4. AFX_MODULE_THREAD_STATE这个结构体又是什么呢? 从字面上看,是模块线程状态,理解起来从字面看不出啥意思,个人觉得命名命的不好.这个结构体是跟模块状态相关的,里面主要保存了当前的WinThread和各个HANDELMAP,有窗口句柄MAP,资源句柄MAP.截取部分代码如下:

  CWinThread* m_pCurrentWinThread;

// list of CFrameWnd objects for thread

CTypedSimpleList<CFrameWnd*> m_frameList;

// temporary/permanent map state

DWORD m_nTempMapLock;           // if not 0, temp maps locked

CHandleMap* m_pmapHWND;

CHandleMap* m_pmapHMENU;

CHandleMap* m_pmapHDC;

CHandleMap* m_pmapHGDIOBJ;

CHandleMap* m_pmapHIMAGELIST;

AFX_MODULE_THREAD_STATE在继承自CWnd的窗口创建时有重要作用.下面就讲一下CWnd的创建过程.

5. CCmdTarget保存有m_pModuleState成员变量,负责保存其指向的模块状态,这个模块状态的指针在CWnd初始化时有用.

m_pModuleState的初始化是在CCmdTarget构造时.m_pModuleState = AfxGetModuleState();可见,其赋值一般就是_afxBaseModuleState.如果没有特别处理模块状态的操作的情况下.

6. CWnd的创建,CWnd将句柄和WndProc Attatch的过程主要是通过hook的方式.在_AfxCbtFilterHook中完成的.贴一段重要的代码:

      AFX_MANAGE_STATE(pWndInit->m_pModuleState);

// the window should not be in the permanent map at this time

ASSERT(CWnd::FromHandlePermanent(hWnd) == NULL);

// connect the HWND to pWndInit...

pWndInit->Attach(hWnd);

// allow other subclassing to occur first

pWndInit->PreSubclassWindow();

WNDPROC *pOldWndProc = pWndInit->GetSuperWndProcAddr();

ASSERT(pOldWndProc != NULL);

// subclass the window with standard AfxWndProc

WNDPROC afxWndProc = AfxGetAfxWndProc();

oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC,

(DWORD_PTR)afxWndProc);

ASSERT(oldWndProc != NULL);

if (oldWndProc != afxWndProc)

*pOldWndProc = oldWndProc;

pThreadState->m_pWndInit = NULL;

可见,在attatch的过程中,首先会改变当前的模块状态,改变为CCmdTarget初始化时的模块状态,也就是AfxGetModuleState().

Attch上去,并且改变WndProc为AfxGetAfxWndProc()的返回值.

下面继续分析Attach和AfxGetAfxWndProc()这两个过程.

Attach 的过程主要就是

  CHandleMap* pMap = afxMapHWND(TRUE); // create map if not exist

ASSERT(pMap != NULL);

pMap->SetPermanent(m_hWnd = hWndNew, this);

在HWNDMap中注册句柄和类之间的映射.而afxMapHWND这个过程,又要见到上面讲过的AFX_MODUEL_THREAD_STATE了.

CHandleMap* PASCAL afxMapHWND(BOOL bCreate)

{

AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();

if (pState->m_pmapHWND == NULL && bCreate)

{

BOOL bEnable = AfxEnableMemoryTracking(FALSE);

#ifndef _AFX_PORTABLE

_PNH pnhOldHandler = AfxSetNewHandler(&AfxCriticalNewHandler);

#endif

pState->m_pmapHWND = new CHandleMap(RUNTIME_CLASS(CWnd),

ConstructDestruct<CWnd>::Construct, ConstructDestruct<CWnd>::Destruct, 

offsetof(CWnd, m_hWnd));

#ifndef _AFX_PORTABLE

AfxSetNewHandler(pnhOldHandler);

#endif

AfxEnableMemoryTracking(bEnable);

}

return pState->m_pmapHWND;

}

返回的是当前的MODULE_THREAD_STATE.而由于在Attach之前改变过当前的模块状态 AFX_MANAGE_STATE(pWndInit->m_pModuleState);所以AfxGetModuleThreadState()返回的就是pWndInit->m_pModuleState.也就是CCmdTarget在初始化时的注册的模块状态.如果HWNDMAP 没有的话,会创建.如果有的话,就直接返回.

Attach讲完了,AfxGetAfxWndProc()呢? 看一下AfxGetAfxWndProc()的代码

WNDPROC AFXAPI AfxGetAfxWndProc()

{

#ifdef _AFXDLL

return AfxGetModuleState()->m_pfnAfxWndProc;

#else

return &AfxWndProc;

#endif

}

如果是dll的话,会返回当前模块状态的WndProc.如果是exe的话,就返回全局的AfxWndProc

一般情况下,当前模块状态一般就是_afxBaseModuleState.而_afxBaseModuleState对应的WndProc是AfxWndProcBase.

LRESULT CALLBACK

AfxWndProcBase(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)

{

AFX_MANAGE_STATE(_afxBaseModuleState.GetData());

return AfxWndProc(hWnd, nMsg, wParam, lParam);

}

AfxWndProcBase的作用很简单,只是在调用全局的AfxWndProc改变一下当前的模块状态.

什么情况下,不是_afxBaseModuleState呢? 在你上层的调用中,当你改变过当前的模块状态的话,返回的就是你改变的模块状态了.如果你是在dll中的话,而对应模块状态的WndProc就变成了AfxWndProcDllStatic

LRESULT CALLBACK

AfxWndProcDllStatic(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)

{

AFX_MANAGE_STATE(&afxModuleState);

return AfxWndProc(hWnd, nMsg, wParam, lParam);

}

类似, AfxWndProcDllStatic也是改变一下当前的模块状态,为本模块的模块状态,然后调用AfxWndProc

7.分析完上面的一推,是不是有点晕了,我在跟进MFC的时候,差不多也快晕了.MFC的东西确实有点乱和难懂了.不过看着这么功能完毕的库,也就是几百个文件,1M而已,个人觉得MFC还是有很多地方值得可取的.

继续分析,从窗口的创建过程来看,HWND和CWnd的映射是保存在CCmdTarget成员变量m_pModuleState中的模块状态的MODULE_THREAD_STATE中的hwndmap中的.

下面分析一下一个比较常见的宏,在参考的链接的那篇文章中有比较详细的描述.

AFX_MANAGE_STATE

AFX_MANAGE_STATE的作用切换到指定的Module State,当出了作用域的时候将Module State恢复到原来的值。是在不同的Module State之中切换

8. 一些常见的诡异问题

dll中,我在一个Cwnd中包含一个模态的对话框成员变量m_modalDlg.

创建m_modalDlg的函数

viod CXXXWnd::CreateModalDlg()

{

  AFX_MANAGE_STATE(AfxGetStaticModuleState());

  m_modalDlg.DoModal()

}


这时候,如果实现m_domalDlg的类,比如CXXXDlg.重载了PreTranslateMessage()这个函数,会发现所有的消息都不会进这个函数.

啥原因呢? 待我来慢慢分析

当初始化m_modalDlg时,当前的模块状态一般应该是_afxBaseModuleState.在DoModal时,也就是Attach时,你改变了模块状态为AfxGetStaticModuleState(),所以你的HWND-CWND的映射是保存在_afxBaseModuleState的模块线程状态中的.

而我们看看模态对话框DoModal的过程,RunModalLoop() - > AfxPumpMessage() 而AfxPumpMessage() 取到消息之后,消息PreTranslateMessage的过程是在当前模块状态下进行的,也就是AfxGetStaticModuleState().

BOOL AfxInternalPreTranslateMessage(MSG* pMsg)

{

// ASSERT_VALID(this);

CWinThread *pThread = AfxGetThread();

if( pThread )

{

// if this is a thread-message, short-circuit this function

if (pMsg->hwnd == NULL && pThread->DispatchThreadMessageEx(pMsg))

return TRUE;

}

// walk from target to main window

CWnd* pMainWnd = AfxGetMainWnd();

if (CWnd::WalkPreTranslateTree(pMainWnd->GetSafeHwnd(), pMsg))

return TRUE;

// in case of modeless dialogs, last chance route through main

//   window's accelerator table

if (pMainWnd != NULL)

{

 CWnd* pWnd = CWnd::FromHandle(pMsg->hwnd);

 if (pWnd->GetTopLevelParent() != pMainWnd)

return pMainWnd->PreTranslateMessage(pMsg);

}

return FALSE;   // no special processing

}

可以看一下 CWnd::FromHandle(pMsg->hwnd)这个过程是在当然模块状态下的MODULE_THREAD_STATE下保存的HWNDMAP中查找的,而你HWND-CWND的注册又是在_afxBaseModuleState中.所以当然会查找不到,所以也就不会调用PreTranslateMessage()了,显然你也截获不到任何消息.


终于分析完了.

--by johny

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值