在多语言软件开发中,资源的本地化是一个重要的环节。本文将介绍一种在MFC应用程序中实现资源回退机制的方法,该方法允许动态地从DLL加载资源,如果DLL中不存在该资源,则从主EXE模块中加载。
理想的本地化模式是将主EXE模块嵌入本地语言资源,并可选地使用资源专用DLL来包含那些需要本地化的资源。如果DLL中缺少资源,则必须从EXE模块中加载。一个重要的要求是资源必须由默认的MFC函数加载。
在MFC中实现这种行为时,会遇到一个问题:虽然很容易用DLL覆盖默认应用程序资源处理程序,但如果DLL中缺少资源,则加载时会失败。MFC提供了一种机制,如果EXE中缺少资源,则从附加的DLL加载资源,使用全局链接列表(DLL链)。想法是将EXE附加到这个DLL链上,这样资源加载过程将首先在DLL中搜索资源,如果不在DLL中,则在EXE模块中搜索。
要实现这个提示,需要在应用程序类中添加两个私有变量,m_hResDll和m_pExeModule,并覆盖ExitInstance:
class CResFallbackApp : public CWinApp {
public:
CResFallbackApp();
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CResFallbackApp)
public:
virtual BOOL InitInstance();
virtual int ExitInstance();
//}}AFX_VIRTUAL
// Implementation
//{{AFX_MSG(CResFallbackApp)
// NOTE - the ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
HMODULE m_hResDll;
CDynLinkLibrary* m_pExeModule;
};
然后在InitInstance的开始处添加以下代码:
BOOL CResFallbackApp::InitInstance() {
// Search resources dll
m_hResDll = LoadLibrary(_T("ResDll.dll"));
// put here better way to find the right dll
if (m_hResDll != NULL) {
// if found:
// 1 - puts EXE module in extension DLL chain
// (using an instance of CDynLinkLibrary)
m_pExeModule = new CDynLinkLibrary(AfxGetInstanceHandle(), AfxGetResourceHandle());
// 2 - puts DLL as principal resources supplier
AfxSetResourceHandle(m_hResDll);
// now a resource is searched starting from resdll
// and if not found, is searched in exe module
}
}
然后在ExitInstance中添加一些清理代码:
int CResFallbackApp::ExitInstance() {
// cleaning
if (m_hResDll != NULL) {
delete m_pExeModule;
FreeLibrary(m_hResDll);
}
return CWinApp::ExitInstance();
}
关键在于创建一个CDynLinkLibrary类型的对象,使用EXE模块实例和资源处理程序。CDynLinkLibrary的构造函数将自己附加到模块状态中的DLL链上(深入跟踪以获取更多细节)。现在,如果或MFC尝试加载任何类型的资源,它首先在DLL中搜索,如果没有找到,则在EXE中搜索。例如:
void CResFallbackDlg::OnTest() {
CString sMsg;
sMsg.LoadString(IDS_TEST);
AfxMessageBox(sMsg);
sMsg.LoadString(IDS_TEST2);
AfxMessageBox(sMsg);
}
如果想要更多细节,可以跟踪LoadString,将到达mfc\src\dllinit.cpp中的以下代码片段。在这里,可以看到资源搜索是如何工作的。
这个提示在以下本地化场景中非常有用: