1回顶部 联系作者:e-mail:21cnbao@21cn.com 相关链接:VC++动态链接库(DLL)编程深入浅出(二) (一) 第4节我们对非MFC DLL进行了介绍,这一节将详细地讲述MFC规则DLL的创建与使用技巧。 2回顶部 我们可以在Visual C++中设置MFC规则DLL是静态链接到MFC DLL还是动态链接到MFC DLL。8,依次选择Visual C++的project -> Settings -> General菜单或选项,在Microsoft Foundation Classes中进行设置。 图8 设置动态/静态链接MFC DLL 5.2 MFC规则DLL的创建 图9 MFC DLL工程的创建 图10所示对话框中的1区选择MFC DLL的类别。 3回顶部 添加socket通讯支持后的InitInstance成员函数如下: BOOL CRegularDllSocketApp::InitInstance() 4区选择是否由MFC向导自动在源代码中添加注释,一般我们选择“Yes,please”。 图10 MFC DLL的创建选项 5.3 一个简单的MFC规则DLL 图11 MFC规则DLL例子 在DLL中添加对话框的方式与在MFC应用程序中是一样的。 4回顶部 第一组文件:CWinApp继承类的声明与实现 // RegularDll.h : main header file for the REGULARDLL DLL 分析: 在这一组文件中定义了一个继承自CWinApp的类CRegularDllApp,并同时定义了其的一个实例theApp。乍一看,您会以为它是一个MFC应用程序,因为MFC应用程序也包含这样的在工程名后添加“App”组成类名的类(并继承自CWinApp类),也定义了这个类的一个全局实例theApp。 5回顶部 我们知道,在MFC应用程序中CWinApp取代了SDK程序中WinMain的地位,SDK程序WinMain所完成的工作由CWinApp的三个函数完成: virtual BOOL InitApplication( ); 但是MFC规则DLL并不是MFC应用程序,它所继承自CWinApp的类不包含消息循环。这是因为,MFC规则DLL不包含CWinApp::Run机制,主消息泵仍然由应用程序拥有。如果DLL 生成无模式对话框或有自己的主框架窗口,则应用程序的主消息泵必须调用从DLL 导出的函数来调用PreTranslateMessage成员函数。 另外,MFC规则DLL与MFC 应用程序中一样,需要将所有 DLL中元素的初始化放到InitInstance 成员函数中。 第二组文件 自定义对话框类声明及实现(点击查看附件) 分析: 这一部分的编程与一般的应用程序根本没有什么不同,我们照样可以利用MFC类向导来自动为对话框上的控件添加事件。MFC类向导照样会生成类似ON_BN_CLICKED(IDC_HELLO_BUTTON, OnHelloButton)的消息映射宏。 第三组文件 DLL中的资源文件 //{{NO_DEPENDENCIES}} 分析: 在MFC规则DLL中使用资源也与在MFC应用程序中使用资源没有什么不同,我们照样可以用Visual C++的资源编辑工具进行资源的添加、删除和属性的更改。 第四组文件 MFC规则DLL接口函数 #include "StdAfx.h" 分析: 这个接口并不使用MFC,但是在其中却可以调用MFC扩展类CdllDialog的函数,这体现了“规则”的概类。 与非MFC DLL完全相同,我们可以使用__declspec(dllexport)声明或在.def中引出的方式导出MFC规则DLL中的接口。 6回顶部 5.4 MFC规则DLL的调用 笔者编写了12的对话框MFC程序(下载本工程附件)来调用5.3节的MFC规则DLL,在这个程序的对话框上点击“调用DLL”按钮时弹出5.3节MFC规则DLL中的对话框。 图12 MFC规则DLL的调用例子 下面是“调用DLL”按钮单击事件的消息处理函数: void CRegularDllCallDlg::OnCalldllButton() 上述例子中给出的是显示调用的方式,可以看出,其调用方式与第4节中非MFC DLL的调用方式没有什么不同。 7回顶部 我们照样可以在EXE程序中隐式调用MFC规则DLL,只需要将DLL工程生成的.lib文件和.dll文件拷入当前工程所在的目录,并在RegularDllCallDlg.cpp文件(图12所示对话框类的实现文件)的顶部添加: #pragma comment(lib,"RegularDll.lib") 并将void CRegularDllCallDlg::OnCalldllButton() 改为: void CRegularDllCallDlg::OnCalldllButton() 5.5 共享MFC DLL的规则DLL的模块切换 应用程序进程本身及其调用的每个DLL模块都具有一个全局唯一的HINSTANCE句柄,它们代表了DLL或EXE模块在进程虚拟空间中的起始地址。进程本身的模块句柄一般为0x400000,而DLL模块的缺省句柄为0x10000000。如果程序同时加载了多个DLL,则每个DLL模块都会有不同的HINSTANCE。应用程序在加载DLL时对其进行了重定位。 共享MFC DLL(或MFC扩展DLL)的规则DLL涉及到HINSTANCE句柄问题,HINSTANCE句柄对于加载资源特别重要。EXE和DLL都有其自己的资源,而且这些资源的ID可能重复,应用程序需要通过资源模块的切换来找到正确的资源。如果应用程序需要来自于DLL的资源,就应将资源模块句柄指定为DLL的模块句柄;如果需要EXE文件中包含的资源,就应将资源模块句柄指定为EXE的模块句柄。 这次我们创建一个动态链接到MFC DLL的规则DLL(下载本工程附件),在其中包含13的对话框。 图13 DLL中的对话框 另外,在与这个DLL相同的工作区中生成一个基于对话框的MFC程序,其对话框与图12完全一样。但是在此工程中我们另外添加了一个14的对话框。 图14 EXE中的对话框 图13和图14中的对话框除了caption不同(以示区别)以外,其它的都相同。 8回顶部 尤其值得特别注意,在DLL和EXE中我们对图13和图14的对话框使用了相同的资源ID=2000,在DLL和EXE工程的resource.h中分别有如下的宏: //DLL中对话框的ID 与5.3节静态链接MFC DLL的规则DLL相同,我们还是在规则DLL中定义接口函数ShowDlg,原型如下: #include "StdAfx.h" 而为应用工程主对话框的“调用DLL”的单击事件添加如下消息处理函数: void CSharedDllCallDlg::OnCalldllButton() 我们以为单击“调用DLL”会弹出13所示DLL中的对话框,可是可怕的事情发生了,我们看到是图14所示EXE中的对话框! 惊讶? 产生这个问题的根源在于应用程序与MFC规则DLL共享MFC DLL(或MFC扩展DLL)的程序总是默认使用EXE的资源,我们必须进行资源模块句柄的切换,其实现方法有三: 方法一 在DLL接口函数中使用: AFX_MANAGE_STATE(AfxGetStaticModuleState()); 9回顶部 我们将DLL中的接口函数ShowDlg改为: void ShowDlg(void) 这次我们再点击EXE程序中的“调用DLL”按钮,弹出的是DLL中的13的对话框!嘿嘿,弹出了正确的对话框资源。 AfxGetStaticModuleState是一个函数,其原型为: AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState( ); 该函数的功能是在栈上(这意味着其作用域是局部的)创建一个AFX_MODULE_STATE类(模块全局数据也就是模块状态)的实例,对其进行设置,并将其指针pModuleState返回。 AFX_MODULE_STATE类的原型如下: // AFX_MODULE_STATE (global data for a module) AFX_MODULE_STATE类利用其构造函数和析构函数进行存储模块状态现场及恢复现场的工作,类似汇编中call指令对pc指针和sp寄存器的保存与恢复、中断服务程序的中断现场压栈与恢复以及操作系统线程调度的任务控制块保存与恢复。 许多看似不着边际的知识点居然有惊人的相似! 10回顶部 AFX_MANAGE_STATE是一个宏,其原型为: AFX_MANAGE_STATE( AFX_MODULE_STATE* pModuleState ) 该宏用于将pModuleState设置为当前的有效模块状态。当离开该宏的作用域时(也就离开了pModuleState所指向栈上对象的作用域),先前的模块状态将由AFX_MODULE_STATE的析构函数恢复。 方法二 在DLL接口函数中使用: AfxGetResourceHandle(); AfxGetResourceHandle用于获取当前资源模块句柄,而AfxSetResourceHandle则用于设置程序目前要使用的资源模块句柄。 我们将DLL中的接口函数ShowDlg改为: void ShowDlg(void) 通过AfxGetResourceHandle和AfxSetResourceHandle的合理变更,我们能够灵活地设置程序的资源模块句柄,而方法一则只能在DLL接口函数退出的时候才会恢复模块句柄。方法二则不同,如果将ShowDlg改为: extern CSharedDllApp theApp; //需要声明theApp外部全局变量 在应用程序主对话框的“调用DLL”按钮上点击,将看到两个对话框,相继为DLL中的对话框(图13)和EXE中的对话框(图14)。 11回顶部 方法三 由应用程序自身切换 资源模块的切换除了可以由DLL接口函数完成以外,由应用程序自身也能完成(下载本工程附件)。 现在我们把DLL中的接口函数改为最简单的: void ShowDlg(void) 而将应用程序的OnCalldllButton函数改为: void CSharedDllCallDlg::OnCalldllButton() 方法三中的Win32函数GetModuleHandle可以根据DLL的文件名获取DLL的模块句柄。如果需要得到EXE模块的句柄,则应调用带有Null参数的GetModuleHandle。 方法三与方法二的不同在于方法三是在应用程序中利用AfxGetResourceHandle和AfxSetResourceHandle进行资源模块句柄切换的。同样地,在应用程序主对话框的“调用DLL”按钮上点击,也将看到两个对话框,相继为DLL中的对话框(图13)和EXE中的对话框(图14)。 在下一节我们将对MFC扩展DLL进行详细分析和实例讲解,欢迎您继续关注本系列连载。 |
鏀惰棌鎴愬姛鏌ョ湅鏀惰棌>>
正在阅读:VC++动态链接库(DLL)编程深入浅出(三)VC++动态链接库(DLL)编程深入浅出(三)
2005-09-23 09:57
出处:PConline
责任编辑:moningfeng
