AFX_MANAGE_STATE(AfxGetStaticModuleState())语句的作用

本文介绍了解决DLL资源冲突的方法,特别是在MFC应用程序中如何正确加载DLL中的对话框资源。通过调整资源模块句柄,确保应用程序能准确引用到DLL中的资源。

先看一个例子:

1、创建一个动态链接到MFC DLL的规则DLL,其内部包含一个对话框资源。指定该对话框ID如下:
              #define IDD_DLL_DIALOG 2000

2、创建一个基于对话框的mfc应用程序,它包含两个对话框资源,IDD_UI_DIALOG和IDD_EXE_DIALOG。并将后者的ID指定如下:
              #define IDD_EXE_DIALOG 2000
其中前者是这个应用程序的用户界面,单击上面的按钮,将弹出一个对话框。部分代码如下:
// in DLL
void CDLL::ShowDlg(void)
{
       CDialog dlg(IDD_DLL_DIALOG); //打开ID为2000的对话框
       dlg.DoModal();
}
// in EXE
void CEXE::OnButtonClick()
{
       ShowDlg();
}

3、单击按钮,弹出的不是期望的DLL中的对话框IDD_DLL_DIALOG,而是应用程序中的对话框IDD_EXE_DIALOG。

解释:

1、应用程序进程本身及其调用的每个DLL模块都具有一个全局唯一的HINSTANCE句柄,它们代表了EXE或DLL模块在进程虚拟空间中的起始地址。(进程本身的模块句柄一般为0x400000,而DLL模块的缺省句柄为0x10000000。如果程序同时加载了多个DLL,则每个DLL模块都会有不同的HINSTANCE。应用程序在加载DLL时对其进行了重定位)。
2、共享MFC DLL(或MFC扩展DLL)的规则DLL涉及到HINSTANCE句柄问题,HINSTANCE句柄对于加载资源特别重要。EXE和DLL都有其自己的资源,而且这些资源的ID可能重复,如果应用程序与规则DLL共享MFC DLL(或MFC扩展DLL),那么将总是默认使用EXE的资源
3、因此应用程序需要通过资源模块的切换来找到正确的资源。如果应用程序需要来自于DLL的资源,就应将资源模块句柄指定为DLL的模块句柄;如果需要EXE文件中包含的资源,就应将资源模块句柄指定为EXE的模块句柄。

解决办法:

1、在DLL中改进:

方法1。

// in DLL
void CDLL::ShowDlg(void)
{
       AFX_MANAGE_STATE(AfxGetStaticModuleState());
       CDialog dlg(IDD_DLL_DIALOG); //打开ID为2000的对话框
       dlg.DoModal();
}

注:AFX_MANAGE_STATE(AfxGetStaticModuleState());一定是作为接口函数的第一条语句。
       其功能是在栈上(这意味着其作用域是局部的)创建一个AFX_MODULE_STATE类的实例,并将其指   针pModuleState返回。
       AFX_MODULE_STATE类利用其构造函数和析构函数进行存储模块状态现场及恢复现场的工作。
       该宏用于将pModuleState设置为当前的有效模块状态。当离开该宏的作用域时(也就离开了pModuleState所指栈上对象的作用域),先前的模块状态将由类AFX_MODULE_STATE的析构函数恢复。(即自动恢复)

方法2。

// in DLL
void CDLL::ShowDlg(void)
{
       HINSTANCE save_hInstance = AfxGetResourceHandle();
       AfxSetResourceHandle(theApp.m_hInstance);
       CDialog dlg(IDD_DLL_DIALOG); //打开ID为2000的对话框
       dlg.DoModal();
       AfxSetResourceHandle(save_hInstance);    
}

注:AfxGetResourceHandle:获取当前资源模块句柄;AfxSetResourceHandle:设置程序目前要使用的资源模块句柄。
       同方法1比较,方法2能够灵活地设置程序的资源模块句柄,而方法1则只能在DLL接口函数退出的时候才会恢复模块句柄。

2、在应用程序中改进:

// in EXE
void CEXE::OnButtonClick()
{
       HINSTANCE exe_hInstance = GetModuleHandle(NULL);
      HINSTANCE dll_hInstance = GetModuleHandle("SharedDll.dll");
      AfxSetResourceHandle(dll_hInstance); //切换状态
       ShowDlg();
      AfxSetResourceHandle(exe_hInstance); //恢复状态
}

注:使用状态切换的情况:当DLL导出函数包含MFC资源、类或者需要创建窗口时。

track: http://tech.163.com/06/0316/10/2CB1FGNG0009159F_4.html

////////////////////////

AFX_MANAGE_STATE(AfxGetStaticModuleState());//用于模块切换时的状态保护,

1.AfxGetStaticModuleState()指向当前模块状态;

2.当前函数调用结束后原模块的状态自动被恢复;

3.用于DLL中所调用MFC函数、类、资源时的模块状态切换

现在刚上班就看看源代码,看到这个玩意儿没见过,就到网上,搜了一下,就搜到了。

AFX_MANAGE_STATE(AfxGetStaticModuleState())

看看这个东东到底是干吗的呢?

 摘自MSDN:

By default, MFC uses the resource handle of the main application to load the resource template. If you have an exported function in a DLL, such as one that launches a dialog box in the DLL, this template is actually stored in the DLL module. You need to switch the module state for the correct handle to be used. You can do this by adding the following code to the beginning of the function:

AFX_MANAGE_STATE(AfxGetStaticModuleState( ));

This swaps the current module state with the state returned from AfxGetStaticModuleState until the end of the current scope.
也就是說,並不是每一个dll的输出函数前都要调用它,只有在要輸出對話框等用到資源時要調用!

dll中资源是共享的用了这个函数的防止不同的进程修改资源产生错误!

缺省情况下MFC使用主应用程序的资源句柄来载入资源模板,而DLL中的资源模板是存在于DLL模板中,因此要使用这一语句切换到由AfxGetStaticModuleState返回的正确的模块状态,得到正确的句柄。

----------------------------------------------------------------------------------------------------------------------------------

动态链接到MFC的规则DLL所有输出的函数应该以如下语句开始:


AFX_MANAGE_STATE(AfxGetStaticModuleState( )) //此语句用来正确地切换MFC模块状态。作用在MSDN的解释:
By default, MFC uses the resource handle of the main application to load the resource template. If you have an exported function in a DLL, such as one that launches a dialog box in the DLL, this template is actually stored in the DLL module. You need to switch the module state for the correct handle to be used. You can do this by adding the following code to the beginning of the function:
AFX_MANAGE_STATE(AfxGetStaticModuleState( ));


应用《在Dll中创建对话框并调用

1、第一步创建一“MFC AppWizard (dll)”工程,接下来选择“Regular Dll using shared MFC DLL”,点击“Finish”。
AFX_MANAGE_STATE(AfxGetStaticModuleState())语句的作用 - 思想有多远 - 思想有多远…… 2、添加一对话框资源到工程中,从菜单中选择Insert->Resource,添加一“Dialog”
选择“New”,至此对话框已添加到DLL工程中。
AFX_MANAGE_STATE(AfxGetStaticModuleState())语句的作用 - 思想有多远 - 思想有多远…… 3、为对话框添加一新类,如:CTest,基类为CDialog。
AFX_MANAGE_STATE(AfxGetStaticModuleState())语句的作用 - 思想有多远 - 思想有多远…… 4、在MFCDLL.cpp中(因创建的工程为MFCDLL)添加接口函数:

extern "C" __declspec(dllexport) void Show()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	CTest test;
	test.DoModal ();
}

别忘了在文件中添加: #include "Test.h":),大功告成,编译吧!
AFX_MANAGE_STATE(AfxGetStaticModuleState())语句的作用 - 思想有多远 - 思想有多远…… 5、用VC新建一对话框工程,在一按钮点击事件中添加如下代码:

typedef void (WINAPI * TESTDLL)();


HINSTANCE hmod;
hmod = ::LoadLibrary ("mfcdll.dll");
if(hmod==NULL)
{
	AfxMessageBox("Fail");
}
TESTDLL lpproc;
lpproc = (TESTDLL)GetProcAddress (hmod,"Show");
if(lpproc!=(TESTDLL)NULL)
	(*lpproc)();
FreeLibrary(hmod);
转自:http://blog.163.com/ljm1113@126/blog/static/579844522010818104635766/
http://hi.baidu.com/jk_cau/blog/item/619858355e41048da71e120d.html
<think>我们正在讨论MFC中的AFX_MANAGE_STATEAfxGetStaticModuleState。根据引用,这些用于模块状态切换,特别是在DLL中。用户要求解析其用途和代码工作原理。首先,AFX_MANAGE_STATE是一个宏,用于管理MFC模块的全局状态。MFC使用模块状态来跟踪资源句柄、当前应用程序实例等。当在DLL中调用MFC函数时,需要确保使用正确的模块状态,否则可能访问错误的资源(例如,使用应用程序的资源而不是DLL的资源)。AfxGetStaticModuleState函数返回指向当前模块状态的指针。在DLL中,通常使用它来获取DLL的模块状态。AFX_MANAGE_STATE宏接受一个指向AFX_MODULE_STATE的指针,通常通过AfxGetStaticModuleState()获取。这个宏的作用是在进入函数时设置当前模块状态为指定的状态,并在退出函数时恢复之前的模块状态。这样,在函数执行期间,所有MFC操作(如加载资源)都会使用正确的模块状态。根据引用[5],在函数堆栈中,只需要在最外层入口使用一次即可,避免嵌套使用。现在,我们来看代码工作原理:1.AfxGetStaticModuleState()的实现:```cppAFX_MODULE_STATE*AFXAPIAfxGetStaticModuleState(){AFX_MODULE_STATE*pModuleState=&afxModuleState;returnpModuleState;}```这个函数返回一个指向全局变量`afxModuleState`的指针,该变量代表DLL的模块状态。2.AFX_MANAGE_STATE宏的定义:```cpp#defineAFX_MANAGE_STATE(p)\AFX_MAINTAIN_STATE2_ctlState(p)```这个宏实际上声明了一个名为`_ctlState`的AFX_MAINTAIN_STATE2类型的局部变量。该类的构造函数保存当前的模块状态并设置新的状态,析构函数恢复原来的状态。因此,当我们在函数开始时写:```cppAFX_MANAGE_STATE(AfxGetStaticModuleState());```相当于:```cppAFX_MAINTAIN_STATE2_ctlState(AfxGetStaticModuleState());```这样,在函数执行期间,模块状态被设置为DLL的模块状态,函数结束后自动恢复。用途:在DLL导出的函数中,如果该函数会使用MFC资源(如对话框、字符串等)或调用MFC函数,必须使用此宏来确保资源查找在DLL的模块中进行,而不是在调用者(应用程序)的模块中。如果不使用,可能会出现资源加载错误(如对话框显示为空白或错误字符串),因为MFC默认使用主应用程序的资源句柄。引用[4]中的例子说明,如果不加,可能会使用应用程序的资源而不是DLL的资源,导致问题。总结:-AFX_MANAGE_STATE(AfxGetStaticModuleState())用于在DLL导出函数中切换模块状态,确保使用DLL自己的资源。-它通过一个局部变量(AFX_MAINTAIN_STATE2)在构造函数中设置新状态,析构函数恢复旧状态,实现自动状态管理。-只需在DLL导出函数的入口点使用一次。相关问题:1.在什么情况下必须使用AFX_MANAGE_STATE宏?2.如果不使用AFX_MANAGE_STATE宏,可能会遇到什么问题?3.AFX_MANAGE_STATE宏如何实现模块状态的自动恢复?4.在MFCDLL中,除了AFX_MANAGE_STATE,还有哪些方法管理模块状态?</think>### AFX_MANAGE_STATEAfxGetStaticModuleState解析 #### 功能用途 在MFC模块化开发中(特别是DLL开发),`AFX_MANAGE_STATE(AfxGetStaticModuleState())` 用于**模块状态切换**,核心解决资源定位问题: 1. **资源隔离**:确保DLL使用自己的资源(对话框/字符串等),而非调用方EXE的资源[^3][^4] 2. **状态管理**:自动保存和恢复模块状态,避免资源句柄冲突[^1][^5] 3. **多模块支持**:使DLL能独立使用MFC类库和资源[^2] #### 代码工作原理 ```cpp // 1. 获取当前模块状态 AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState() { AFX_MODULE_STATE* pModuleState = &afxModuleState; return pModuleState; // 返回DLL的全局状态对象 } // 2. 状态切换宏(简化版) #define AFX_MANAGE_STATE(pState) \ AFX_MAINTAIN_STATE2 _state(pState); // 3. 实际应用场景 extern "C" __declspec(dllexport) void ShowDlg() { AFX_MANAGE_STATE(AfxGetStaticModuleState()); // 状态切换 CMyDlg dlg; // 此时使用的资源来自DLL dlg.DoModal(); // 而非调用方EXE } ``` #### 执行机制 1. **进入函数时**: - 保存当前模块状态(通常为EXE状态) - 将新状态设置为`AfxGetStaticModuleState()`返回的DLL状态[^1][^3] 2. **资源访问**: - `CDialog::DoModal()`等操作自动使用DLL的资源句柄[^4] 3. **退出函数时**: - 通过`AFX_MAINTAIN_STATE2`析构函数自动恢复原始状态[^5] > ⚠️ **关键设计**:使用RAII(资源获取即初始化)技术,通过局部对象`_state`的构造/析构实现状态自动切换和恢复[^3][^5]。 #### 使用规范 1. **作用域**:只需在DLL导出函数**最外层**使用一次[^5] 2. **嵌套禁止**:避免在已使用宏的函数内再次调用其他使用宏的函数 3. **典型场景**: - DLL中创建对话框/窗口 - 加载DLL专属资源(图标/字符串) - 调用MFC类库函数 > 示例:未使用宏时,`AfxMessageBox("hello")`可能显示EXE的资源导致乱码;使用后正确加载DLL资源[^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值