深入MFC扩展DLL

来源:互联网 发布:hexo 知乎图标 编辑:程序博客网 时间:2024/05/16 12:35
 
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库,则无此问题。
 
原创粉丝点击