深入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问题的引出
在创建一个使用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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
系统根据B/S,即所谓的电脑浏览器/网络服务器方式,运用Java技术性,挑选MySQL作为后台系统。系统主要包含对客服聊天管理、字典表管理、公告信息管理、金融工具管理、金融工具收藏管理、金融工具银行卡管理、借款管理、理财产品管理、理财产品收藏管理、理财产品银行卡管理、理财银行卡信息管理、银行卡管理、存款管理、银行卡记录管理、取款管理、转账管理、用户管理、员工管理等功能模块。 文中重点介绍了银行管理的专业技术发展背景和发展状况,随后遵照软件传统式研发流程,最先挑选适用思维和语言软件开发平台,依据需求分析报告模块和设计数据库结构,再根据系统功能模块的设计制作系统功能模块图、流程表和E-R图。随后设计架构以及编写代码,并实现系统能模块。最终基本完成系统检测和功能测试。结果显示,该系统能够实现所需要的作用,工作状态没有明显缺陷。 系统登录功能是程序必不可少的功能,在登录页面必填的数据有两项,一项就是账号,另一项数据就是密码,当管理员正确填写并提交这二者数据之后,管理员就可以进入系统后台功能操作区。进入银行卡列表,管理员可以进行查看列表、模糊搜索以及相关维护等操作。用户进入系统可以查看公告和模糊搜索公告信息、也可以进行公告维护操作。理财产品管理页面,管理员可以进行查看列表、模糊搜索以及相关维护等操作。产品类型管理页面,此页面提供给管理员的功能有:新增产品类型,修改产品类型,删除产品类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值