深入MFC扩展DLL

 
1            问题的引出
在创建一个使用MFC的DLL时,VS向导自动添加了一个从CwinApp继承而来的类,并重载了InitInstance和ExitInstance两个函数。在这个文件的前面还有一段注释:
//
//TODO: 如果此DLL 相对于MFC DLL 是动态链接的,
//       则从此DLL 导出的任何调入
//       MFC 的函数必须将AFX_MANAGE_STATE 宏添加到
//       该函数的最前面。
//
//       例如:
//
//       extern "C" BOOL PASCAL EXPORT ExportedFunction()
//       {
//            AFX_MANAGE_STATE(AfxGetStaticModuleState());
//            // 此处为普通函数体
//       }
//
//       此宏先于任何MFC 调用
//       出现在每个函数中十分重要。这意味着
//       它必须作为函数中的第一个语句
//       出现,甚至先于所有对象变量声明,
//       这是因为它们的构造函数可能生成MFC
//       DLL 调用。
//
那么, AFX_MANAGE_STATE(AfxGetStaticModuleState());究竟是什么东西,为什么出现在每个函数中十分重要呢?
2            问题研究
我们知道,在MFC代码中,如果要访问一些模块中的资源或信息,通常要用 AfxGetModuleState函数取得模块信息的指针,那么MFC又如何知道用户扩展的DLL中会有哪些资源呢?
看看 AfxGetModuleState的实现:
PROCESS_LOCAL(_AFX_BASE_MODULE_STATE, _afxBaseModuleState)
AFX_MODULE_STATE* AfxGetModuleState()
{
     _AFX_THREAD_STATE* pState = _afxThreadState;
     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();
     }
     ASSERT(pResult != NULL);
     return pResult;
}
从这里可以看出,如果我们不修改 _AFX_THREAD_STATE 中的m_pModuleState指针,那么它将指向MFC DLL中的 _afxBaseModuleState这个全局变量。但是在MFC运行时,它有时又必须访问MFC DLL中的资源,因此我们只能在每次要使用本模块资源之前修改此指针,在使用完成后又将此指针恢复。这就是AFX_MANAGE_STATE这个宏完成的工作。
再看看 AfxGetStaticModuleState()做了什么。
LRESULT CALLBACK AfxWndProcDllStatic(HWND, UINT, WPARAM, LPARAM);
class _AFX_DLL_MODULE_STATE :public AFX_MODULE_STATE
{
public :
     _AFX_DLL_MODULE_STATE() : AFX_MODULE_STATE(TRUE, AfxWndProcDllStatic, 7)
         { }
};
static _AFX_DLL_MODULE_STATE afxModuleState;
AFX_MODULE_STATE* AfxGetStaticModuleState()
{
     AFX_MODULE_STATE* pModuleState = &afxModuleState;
     return pModuleState;
}
进一步的分析发现 AfxGetStaticModuleState()这个函数是和 DllMain函数一样静态链接到DLL中的,也就是说每个DLL中都有一个独立的 afxModuleState 我们知道 AFX_MODULE_STATE中保存了每个模块的独立信息,如此模块中实现的类信息,此模块运行时使用的资源等等。据此我们就可以猜测 AFX_MANAGE_STATE是用于将我们保存DLL信息的指针传递给MFC的。看看:
#define AFX_MANAGE_STATE(p) AFX_MAINTAIN_STATE2 _ctlState(p);
AFX_MODULE_STATE* AfxSetModuleState(AFX_MODULE_STATE* pNewState)
{
     _AFX_THREAD_STATE* pState = _afxThreadState;
     AFX_MODULE_STATE* pPrevState = pState->m_pModuleState;
     pState->m_pModuleState = pNewState;
     return pPrevState;
}
 
AFX_MAINTAIN_STATE::~AFX_MAINTAIN_STATE()
{
     _AFX_THREAD_STATE* pState = _afxThreadState;
     pState->m_pModuleState = m_pPrevModuleState;
}
 
AFX_MAINTAIN_STATE2::AFX_MAINTAIN_STATE2(AFX_MODULE_STATE* pNewState)
{
     m_pThreadState = _afxThreadState;
     m_pPrevModuleState = m_pThreadState->m_pModuleState;
     m_pThreadState->m_pModuleState = pNewState;
}
从以上代码可以看出, AFX_MANAGE_STATE可以保证在退出函数时恢复原来的ModuleState指针。
3            优缺点分析
毫无疑问的是,使用这种方式对于模块的独立性是很有帮助的,比如它可以保证在读取或者创建资源时优先使用本模块中的资源,而不用担心与其它模块中的资源ID相冲突。
但是缺点也是明显的,在每次调用MFC中的函数之前都必须使用AFX_MANAGE_STATE,非常的麻烦,而且有时候会出现一些比较难以发现的错误。比如将CImageList这种类型的变量放在自己的类中的时候,当类实例调用析构函数时,CImageList的析构函数也将释放它读取到的资源(句柄),但是这个时候往往没有使用AFX_MANAGE_STATE(因为CImageList是在析构函数调用完成后释放的),这样的话,这个ImageList句柄到CImageList指针的映射将无法正确释放,可能造成以后的隐患。
4            结论
1、只有当自己写的库是对MFC类库的扩展时才使用这种类型的DLL,否则使用普通DLL就行了。
2、如果扩展DLL没有使用独立的资源也不需要使用MFC扩展的DLL类型。
3、可以考虑将本DLL中的资源放在EXE或者其它类型的主程序中,从而避免使用此种类型的DLL。
4、如果是使用静态链接的DLL库,则无此问题。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值