MFC 如何使用动态链接库中的资源

近来在论坛上很有多帖子问到如何使用DLL中的资源(包括对话框,图标等)的问题,现在笔者就来就此问题谈谈,包含在DLL内部使用资源,DLL中使用其它DLL中的资源和在应用程序中使用资源。 
我们先以图标为例说起(其它的资源与此图标的加载原理大致相同),我们要加载图标,一般是调用AfxGetApp()->LoadIcon(…);下面是CWinApp::LoadIcon的实现(afxwin2.inl): 
_AFXWIN_INLINE HICON CWinApp::LoadIcon(LPCTSTR lpszResourceName) const 
{ return ::LoadIcon(AfxFindResourceHandle(lpszResourceName, 
RT_GROUP_ICON), lpszResourceName); } 
_AFXWIN_INLINE HICON CWinApp::LoadIcon(UINT nIDResource) const 
{ return ::LoadIcon(AfxFindResourceHandle(MAKEINTRESOURCE(nIDResource), 
RT_GROUP_ICON), MAKEINTRESOURCE(nIDResource)); } 
可以看到CWinApp::LoadIcon实际上调用了API .LoadIcon,下面是API LoadIcon的原型: 
HICON LoadIcon( 
HINSTANCE hInstance, LPCTSTR lpIconName); 
hInstance 
[in] Handle to an instance of the module whose executable file contains the icon to be loaded. This parameter must be NULL when a standard icon is being loaded. 
hInstance是我们要加载ICON的模块实例,这个实例从何来,当然我们可以直接传入DLL的实例,对于通过LoadLibrary动态加载的DLL我们可以很容易的得到其句柄,但对于我们直接链接的DLL得到其句柄则要费一番周折。可以看到CWinApp::LoadIcon是通过AfxFindResouceHandle找到此句柄的。下面是AfxFindResourceHandle的定义(afxwin.h): 
#ifndef _AFXDLL 
#define AfxFindResourceHandle(lpszResource, lpszType) AfxGetResourceHandle() 
#else 
HINSTANCE AFXAPI AfxFindResourceHandle(LPCTSTR lpszName, LPCTSTR lpszType); 
#endif 
我们先讨论在静态链接库中使用MFC DLL的情况。可以看到,我们如果在静态库中使用MFC DLL的话(#ifndef _AFXDLL),实际上就是调用的AfxGetResourceHandle,MSDN中的说明是 
AfxGetResourceHandle 
This function accesses the application’s resources directly by using the HINSTANCE handle returned, for example, in calls to the Windows function FindResource. 
HINSTANCE AfxGetResourceHandle( ); 
Return Value 
An HINSTANCE handle where the default resources of the application are loaded. 
函数返回的应用程序加载的缺省资源的HINSTANCE句柄,HINSTANCE相当于HMODULE,也就是资源所在的模块句柄。显然在此,我们使用的是DLL中的资源,那么我们就应该返回此DLL中的HINSTANCE了,如果让AfxGetResourceHandle返回DLL的HINSTANCE呢?答案是通过AfxSetResourceHandle设置。MSDN中AfxSetResouceHandle的说明如下: 
AfxSetResourceHandle 
This function sets the HINSTANCE handle that determines where the default resources of the application are loaded. 
void AfxSetResourceHandle( 
HINSTANCE hInstResource ); 
Parameters 
hInstResource 
Specifies the instance or module handle to an .EXE or DLL file from which the application’s resources are loaded. 
我们只需将DLL的HINSTANCE传入AfxSetResouceHanle就行了。如何得到DLL的HINSTANCE呢,我们可以通过DLL声明一个接口HINSTANCE GetInstance获得,也可以通过EnumProcessMoudules找到(详细的过程见MSDN中EnumProcessModules的说明和示例)。 
我们使用完DLL中的资源要使用EXE中的资源的资源怎么办呢?我们需要在使用完成后用AfxSetResource重新将资源模块的句柄设置为原来的值,如果来保证在资源使用完成后完成这一个工作呢,即使在使用过程中发生异常了,为此我们利C++类的构造和析构机制创建了这一类: 
class CLocalResource 

public: 
CLocalResource(HINSTANCE hInstance) 

m_hInstOld=AfxGetInstanceHandle(); 
AfxSetInstanceHandle(hInstance); 

virtual ~CLocalResource() 

AfxSetInstanceHandle(m_hInstOld); 

protected: 
HINSTANCE m_hInstOld; 
}; 
我们只需在使用DLL的资源之前构造一个CLocalInstance就行了。 
void CXXXX::LoadResouceFromDLL(HINSTANCE hInst,UINT nResID,…) 

CLocalResouce localRes(hInst); 
… 

下面来讨论在动态库中使用MFC DLL的情况(也就是定义了_AFXDLL的情况) 
来看看AfxGetInstanceHandle ,AfxGetResourceHandle和AfxSetResouceHandle的实现(afxwin1.inl中): 
_AFXWIN_INLINE HINSTANCE AFXAPI AfxGetInstanceHandle() 
{ ASSERT(afxCurrentInstanceHandle != NULL); 
return afxCurrentInstanceHandle; } 
_AFXWIN_INLINE HINSTANCE AFXAPI AfxGetResourceHandle() 
{ ASSERT(afxCurrentResourceHandle != NULL); 
return afxCurrentResourceHandle; } 
_AFXWIN_INLINE void AFXAPI AfxSetResourceHandle(HINSTANCE hInstResource) 
{ ASSERT(hInstResource != NULL); afxCurrentResourceHandle = hInstResource; } 
实际上访问的就是afxCurrentInstanceHandle,afxCurrentResourceHandle。 

// Global functions for access to the one and only CWinApp 
… 
#define afxCurrentInstanceHandle AfxGetModuleState()->m_hCurrentInstanceHandle 
#define afxCurrentResourceHandle AfxGetModuleState()->m_hCurrentResourceHandle 
…. 
AFX_MODULE_STATE* AFXAPI 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在此我们就不讨论了,有兴趣的读者可以阅读(afxstat_.h和afxstate.cpp)。 
那AfxGetModuleState()->m_hCurrentResourceHandle又是在哪初始化的呢? 
我们可以在appinit.cpp中的BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow)看到 
// set resource handles 
AFX_MODULE_STATE* pModuleState = AfxGetModuleState(); 
pModuleState->m_hCurrentInstanceHandle = hInstance; 
pModuleState->m_hCurrentResourceHandle = hInstance; 
和appinit.cpp中的void CWinApp::SetCurrentHandles()中看到 
AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE(); 
pModuleState->m_hCurrentInstanceHandle = m_hInstance; 
pModuleState->m_hCurrentResourceHandle = m_hInstance; 
CWinApp::SetCurrentHandles()也是由AfxWinInit调用的,那AfxWinInit又由谁调用呢? 
我们可以在DllMain(dllinit.cpp和dllmodul.cpp,分别对应于MFC扩展DLL和MFC规则DLL)看到 
(dllinit.cpp,MFC扩展DLL) 
static AFX_EXTENSION_MODULE coreDLL; 
…. 
extern "C" 
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID) 

if (dwReason == DLL_PROCESS_ATTACH) 

……. 
// initialize this DLL's extension module 
VERIFY(AfxInitExtensionModule(coreDLL, hInstance)); 
#ifdef _AFX_OLE_IMPL 
AfxWinInit(hInstance, NULL, _T(""), 0); 

…. 
#endif 
…. 
// wire up this DLL into the resource chain 
CDynLinkLibrary* pDLL = new CDynLinkLibrary(coreDLL, TRUE); 
ASSERT(pDLL != NULL); 
pDLL->m_factoryList.m_pHead = NULL; 
…. 

else if (dwReason == DLL_PROCESS_DETACH) 

…. 
// cleanup module state for this process 
AfxTermExtensionModule(coreDLL); 
…. 
// cleanup module state in OLE private module state 
AfxTermExtensionModule(coreDLL, TRUE); 
…. 

… 

可以看到在提供自动化支持时,将调用AfxWinInit(但MFC的DLL向导,对于扩展DLL却不允许添加自动化支持)。 
(在dllmodul.cpp中,MFC常规DLL) 
#ifdef _AFXDLL 
static AFX_EXTENSION_MODULE controlDLL; 
…. 
#endif 

extern "C" 
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) 

if (dwReason == DLL_PROCESS_ATTACH) 

…. 
_AFX_THREAD_STATE* pState = AfxGetThreadState(); 
AFX_MODULE_STATE* pPrevModState = pState->m_pPrevModuleState; 

// Initialize DLL's instance(/module) not the app's 
if (!AfxWinInit(hInstance, NULL, _T(""), 0)) 

AfxWinTerm(); 
goto Cleanup; // Init Failed 


…. 
#ifdef _AFXDLL 
// wire up this DLL into the resource chain 
VERIFY(AfxInitExtensionModule(controlDLL, hInstance)); 
CDynLinkLibrary* pDLL; pDLL = new CDynLinkLibrary(controlDLL); 
ASSERT(pDLL != NULL); 
#else 
AfxInitLocalData(hInstance); 
#endif 
… 

else if (dwReason == DLL_PROCESS_DETACH) 

…. 
#ifdef _AFXDLL 
AfxTermExtensionModule(controlDLL, TRUE); 
#else 
AfxTermLocalData(hInstance, TRUE); 
#endif 

… 

看到上面的代码,其实与MFC扩展DLL的代码大同小异,只过是MFC常规DLL可以支持在静态链接库全用MFC DLL,相应的初始化/反初始化函数就变为了AfxInitLocalData/ AfxTermLocalData,在此我们不对在静态链接库中使用MFC DLL的情况作更多讨论。 
以及在winmain.cpp中的int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow),在appmodul.cpp中我们可以看到入口函数的实现: 
extern "C" int WINAPI 
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
LPTSTR lpCmdLine, int nCmdShow) 

// call shared/exported WinMain 
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); 

看看AfxInitExtensionModule , AfxTermExtensionModule CDynLinkLibrary的定义(afxdll_.h)和实现(在dllinit.cpp中)就明白了DllMain做了什么: 

BOOL AFXAPI AfxInitExtensionModule(AFX_EXTENSION_MODULE& state, HMODULE hModule) 

// only initialize once 
if (state.bInitialized) 

AfxInitLocalData(hModule); 
return TRUE; 

state.bInitialized = TRUE; 
// save the current HMODULE information for resource loading 
ASSERT(hModule != NULL); 
state.hModule = hModule; 
state.hResource = hModule; 
// save the start of the runtime class list 
AFX_MODULE_STATE* pModuleState = AfxGetModuleState(); 
state.pFirstSharedClass = pModuleState->m_classList.GetHead(); 
pModuleState->m_classList.m_pHead = pModuleState->m_pClassInit; 

#ifndef _AFX_NO_OLE_SUPPORT 
// save the start of the class factory list 
state.pFirstSharedFactory = pModuleState->m_factoryList.GetHead(); 
pModuleState->m_factoryList.m_pHead = pModuleState->m_pFactoryInit; 
#endif 
return TRUE; 

… 
void AFXAPI AfxTermExtensionModule(AFX_EXTENSION_MODULE& state, BOOL bAll) 

// make sure initialized 
if (!state.bInitialized) 
return; 

// search for CDynLinkLibrary matching state.hModule and delete it 
ASSERT(state.hModule != NULL); 
AFX_MODULE_STATE* pModuleState = AfxGetModuleState(); 
AfxLockGlobals(CRIT_DYNLINKLIST); 
for (CDynLinkLibrary* pDLL = pModuleState->m_libraryList; pDLL != NULL; ) 

CDynLinkLibrary* pNextDLL = pDLL->m_pNextDLL; 
if (bAll || pDLL->m_hModule == state.hModule) 
delete pDLL; // will unwire itself 
pDLL = pNextDLL; 

AfxUnlockGlobals(CRIT_DYNLINKLIST); 
// delete any local storage attached to this module 
AfxTermLocalData(state.hModule, TRUE); 
// remove any entries from the CWnd message map cache 
AfxResetMsgCache(); 


class CDynLinkLibrary : public CCmdTarget 

DECLARE_DYNAMIC(CDynLinkLibrary) 
public: 

// Constructor 
explicit CDynLinkLibrary(AFX_EXTENSION_MODULE& state, BOOL bSystem = FALSE); 
CDynLinkLibrary(HINSTANCE hModule, HINSTANCE hResource); 

// Attributes 
HMODULE m_hModule; 
HMODULE m_hResource; // for shared resources 
…. 
BOOL m_bSystem; // TRUE only for MFC DLLs 
// Implementation 
public: 
CDynLinkLibrary* m_pNextDLL; // simple singly linked list 
virtual ~CDynLinkLibrary(); 
…. 
}; 

CDynLinkLibrary::CDynLinkLibrary(AFX_EXTENSION_MODULE& state, BOOL bSystem) 

… 
// copy info from AFX_EXTENSION_MODULE struct 
ASSERT(state.hModule != NULL); 
m_hModule = state.hModule; 
m_hResource = state.hResource; 
m_classList.m_pHead = state.pFirstSharedClass; 
…. 
m_bSystem = bSystem; 

// insert at the head of the list (extensions will go in front of core DLL) 
DEBUG_ONLY(m_pNextDLL = NULL); 
AfxLockGlobals(CRIT_DYNLINKLIST); 
m_pModuleState->m_libraryList.AddHead(this); 
AfxUnlockGlobals(CRIT_DYNLINKLIST); 


CDynLinkLibrary::CDynLinkLibrary(HINSTANCE hModule, HINSTANCE hResource) 

… 
m_hModule = hModule; 
m_hResource = hResource; 
m_classList.m_pHead = NULL; 
… 
m_bSystem = FALSE; 
// insert at the head of the list (extensions will go in front of core DLL) 
DEBUG_ONLY(m_pNextDLL = NULL); 
AfxLockGlobals(CRIT_DYNLINKLIST); 
m_pModuleState->m_libraryList.AddHead(this); 
AfxUnlockGlobals(CRIT_DYNLINKLIST); 

… 
CDynLinkLibrary::~CDynLinkLibrary() 

// remove this frame window from the list of frame windows 
AfxLockGlobals(CRIT_DYNLINKLIST); 
m_pModuleState->m_libraryList.Remove(this); 
AfxUnlockGlobals(CRIT_DYNLINKLIST); 

由此我们可以看出DLL初始化时先调AfxInitExtensionModule是为了构造一个AFX_EXTENSION_MODULE,然后将它插入插入一个AFX_MODULE_STATE链表中,同时将当前DLL的HINSTANCE插入此AFX_MODULE_STATE的CDynLinkLibrary链表中,以便稍后讲到的AfxFindResourceHandle找到。DLL从内存移出时就从此链表中删除自己。 
让我们来看看AfxFindResourceHandle的实现(dllinit.cpp): 
HINSTANCE AFXAPI AfxFindResourceHandle(LPCTSTR lpszName, LPCTSTR lpszType) 

ASSERT(lpszName != NULL); 
ASSERT(lpszType != NULL); 

HINSTANCE hInst; 

// first check the main module state 
AFX_MODULE_STATE* pModuleState = AfxGetModuleState(); 
if (!pModuleState->m_bSystem) 

hInst = AfxGetResourceHandle(); 
if (::FindResource(hInst, lpszName, lpszType) != NULL) 
return hInst; 


// check for non-system DLLs in proper order 
AfxLockGlobals(CRIT_DYNLINKLIST); 
CDynLinkLibrary* pDLL; 
for (pDLL = pModuleState->m_libraryList; pDLL != NULL; 
pDLL = pDLL->m_pNextDLL) 

if (!pDLL->m_bSystem && pDLL->m_hResource != NULL && 
::FindResource(pDLL->m_hResource, lpszName, lpszType) != NULL) 

// found it in a DLL 
AfxUnlockGlobals(CRIT_DYNLINKLIST); 
return pDLL->m_hResource; 


AfxUnlockGlobals(CRIT_DYNLINKLIST); 

// check language specific resource next 
hInst = pModuleState->m_appLangDLL; 
if (hInst != NULL && ::FindResource(hInst, lpszName, lpszType) != NULL) 
return hInst; 

// check the main system module state 
if (pModuleState->m_bSystem) 

hInst = AfxGetResourceHandle(); 
if (::FindResource(hInst, lpszName, lpszType) != NULL) 
return hInst; 


// check for system DLLs in proper order 
AfxLockGlobals(CRIT_DYNLINKLIST); 
for (pDLL = pModuleState->m_libraryList; pDLL != NULL; pDLL = pDLL->m_pNextDLL) 

if (pDLL->m_bSystem && pDLL->m_hResource != NULL && 
::FindResource(pDLL->m_hResource, lpszName, lpszType) != NULL) 

// found it in a DLL 
AfxUnlockGlobals(CRIT_DYNLINKLIST); 
return pDLL->m_hResource; 


AfxUnlockGlobals(CRIT_DYNLINKLIST); 

// if failed to find resource, return application resource 
return AfxGetResourceHandle(); 

上面的代码首先检查主模块的状态,通过返回的是个AFX_MODULE_STATE的类对象指针. AFX_MODULE_STATE又是个什么呢?下面看看AFX_MODULE_STATE的定义(afxstat_.h中,它的实现在afxstate.cpp中,有兴趣的读者可以读读): 
// AFX_MODULE_STATE (global data for a module) 
class AFX_MODULE_STATE : public CNoTrackObject 

public: 
#ifdef _AFXDLL 
AFX_MODULE_STATE(BOOL bDLL, WNDPROC pfnAfxWndProc, DWORD dwVersion, 
BOOL bSystem = FALSE); 
#else 
explicit AFX_MODULE_STATE(BOOL bDLL); 
#endif 
~AFX_MODULE_STATE(); 

CWinApp* m_pCurrentWinApp; 
HINSTANCE m_hCurrentInstanceHandle; 
HINSTANCE m_hCurrentResourceHandle; 
LPCTSTR m_lpszCurrentAppName; 
BYTE m_bDLL; // TRUE if module is a DLL, FALSE if it is an EXE 
BYTE m_bSystem; // TRUE if module is a "system" module, FALSE if not 
… 

#ifdef _AFXDLL 
// CDynLinkLibrary objects (for resource chain) 
CTypedSimpleList<CDynLinkLibrary*> m_libraryList; 

// special case for MFC71XXX.DLL (localized MFC resources) 
HINSTANCE m_appLangDLL; 
#endif 
…. 
}; 
我们来看看我们关心的属性 
CWinApp* m_pCurrentWinApp; 
HINSTANCE m_hCurrentInstanceHandle; 
HINSTANCE m_hCurrentResourceHandle; 
LPCTSTR m_lpszCurrentAppName; 

BYTE m_bDLL; // TRUE if module is a DLL, FALSE if it is an EXE 
BYTE m_bSystem; // TRUE if module is a "system" module, FALSE if not 
… 
// CDynLinkLibrary objects (for resource chain) 
CTypedSimpleList<CDynLinkLibrary*> m_libraryList; 

// special case for MFC71XXX.DLL (localized MFC resources) 
HINSTANCE m_appLangDLL; 
看了相关注释,也就明白什么意思了,在此就不多解释了。 

如果当前主模块是系统模块(通过m_bSystem标识),并且从当前模块中找到了相应的资源就返回此模块的HINSTANCE。接下来当前模块所载的DLL链表中的所有用户模块中查找,本地化的MFC资源DLL中查找,系统DLL中查找。如果都找不到则从EXE(或者通过AfxSetResouceHandle中设置的HINSTANCE)中查找。 
如果不考虑DLL链表,则需要我们每次使用DLL中的资源前调用AfxSetResouceHandle,使用后再调用AfxSetResourceHandle,就同前面讲的在静态链接库中使用MFC DLL一样。麻不麻烦? 
怎么解决呢?解决方法是用资源DLL的HINSTANCE构造一CDynLinkLibrary对象插入主模块(EXE)的AFX_MODULE_STATE的m_libraryList中。怎么做? 
看了上面DllMain的实现代码,我们可以照着做。 
static AFX_EXTENSION_MODULE ResouceDLL = { NULL, NULL } 
BOOL CXApp::InitInstance() 

// Get the resource DLL instance 
if (!AfxInitExtensionModule(ResouceDLL, hResourceModule)) 
return 0; 
new CDynLinkLibrary(ResouceDLL); 
… 

int CGenericMFCApp::ExitInstance() 

AfxTermExtensionModule(ResouceDLL); 
return CWinApp::ExitInstance(); 

这样我们就可以在应用程序中安全的使用DLL中的资源了,就好比EXE中的一样。对于我们自己编写入口函数的DLL,如果其中涉及到资源的话,我们可以参考上述的MFC常规DLL的实现。 
对于我们在ActiveX和一些DLL中的AFX_MANAGE_STATE(AfxGetStaticModuleState());我们又怎么理解呢?看看afxstat_.h: 
#ifdef _AFXDLL 
… 
class _AFX_THREAD_STATE; 
struct AFX_MAINTAIN_STATE2 

explicit AFX_MAINTAIN_STATE2(AFX_MODULE_STATE* pModuleState); 
~AFX_MAINTAIN_STATE2(); 
protected: 
AFX_MODULE_STATE* m_pPrevModuleState; 
_AFX_THREAD_STATE* m_pThreadState; 
}; 
#define AFX_MANAGE_STATE(p) AFX_MAINTAIN_STATE2 _ctlState(p); 
#else // _AFXDLL 
#define AFX_MANAGE_STATE(p) 
#endif //!_AFXDLL 

对于在静态库中使用MFC DLL时它就相当于不存在,反之,它构造一个AFX_MAINTAIN_STATE2对象,在afxstate.cpp中 
#ifdef _AFXDLL 
… 

AFX_MAINTAIN_STATE2::AFX_MAINTAIN_STATE2(AFX_MODULE_STATE* pNewState) 

m_pThreadState = _afxThreadState; 
m_pPrevModuleState = m_pThreadState->m_pModuleState; 
m_pThreadState->m_pModuleState = pNewState; 

#endif //_AFXDLL 

再看dllmodul.cpp中有关AfxGetStaticModuleState的实现: 

static _AFX_DLL_MODULE_STATE afxModuleState; 

… 

AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState() 

AFX_MODULE_STATE* pModuleState = &afxModuleState; 
return pModuleState; 


(MFC中太多的全局变量的确不雅!) 
可以看到通过构造一个AFX_MAINTAIN_STATE2对象(再结合AfxGetResouceHandle的实现),就使AfxFindResourceHandle返回的是ActiveX控件(实际上也是一个常规DLL)或DLL的HINSTANE了,这样AfxFindResourceHandle就不必须在DLL链表中查找了,但缺点是每次使用资源(或响应消息)时都要构造一个AFX_MAINTAIN_STATE2对象,并且,而且使用这种方法在此DLL中无法使用其它DLL中的资源或在EXE中使用DLL中的资源(在不使用AfxSetResourceHandle/AfxGetResourceHandle的前提下)。当然,我们这儿关心的只是资源的使用,如果涉及到其它,包括事件响应或多个UI线程时,AFX_MODULE_STATE还是不能少的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值