Visual C++支持三种DLL,它们分别是Non-MFC DLL(非MFC动态库)、MFC Regular DLL(MFC规则DLL)、MFC Extension DLL(MFC扩展DLL)。
非MFC动态库不采用MFC类库结构,其导出函数为标准的C接口,能被非MFC或MFC编写的应用程序所调用;MFC规则DLL 包含一个继承自CWinApp的类,但其无消息循环;MFC扩展DLL采用MFC的动态链接版本创建,它只能被用MFC类库所编写的应用程序所调用。
MFC DLL
MFC DLL有3种,分别为:
(1)使用共享MFC DLL的规则DLL
(2)带静态链接MFC的规则DLL
(3)MFC扩展DLL
下面重点解释一下这些DLL的含义区别:
1、规则DLL
首先谈谈所谓的"规则DLL":"规则DLL"是由"Regular DLL"翻译而来的。它实际上体现出来两方面的本质:
(1)该DLL是基于MFC的;
(2)该DLL是"规则"的,它不同于"MFC扩展DLL",在规则DLL中内部虽然是可以使用MFC,但是规则DLL的接口应该不能是基于MFC的。而MFC扩展DLL与应用程序接口可以是MFC,可以从MFC扩展dll中导出一个MFC的派生类。
一般情况下我们都会使用规则的dll,因为"规则DLL"能够提供给所有支持dll技术的语言的调用接口。在规则DLL中,有一个CWinApp继承下来的类,dll入口函数则是由MFC自动提供,被MFC封装。此类DLL程序从CWinApp派生,但是没有消息循环:
下面再详细说明"规则DLL"的两个分类:
(1)使用共享MFC DLL的规则DLL
"共享MFC DLL的规则DLL"是在编写基于MFC的DLL程序时,编译后该DLL中不包含MFC的库,比如MFC42.dll,而是由dll运行的时候动态链接到MFC的库。这种方式比"带静态链接MFC的规则DLL"编译的稍微大些。因此,当发布"共享MFC DLL的规则DLL"dll时,如果对方的机器上没有安装MFC的库,那么该dll是运行不了的,除非你将MFC的库也一块给他,"共享MFC DLL的规则DLL"和"带静态链接MFC的规则DLL"最大的区别就是在使用MFC的方法上。
正是由于"共享MFC DLL的规则DLL"的这些特点,导致在系统加载该类dll时,会涉及到多个dll的加载,那么如果当DLL和应用程序中存在相同ID的资源时,系统不能正确分辨程序员的意图,因此,使用"共享MFC DLL的规则DLL"我们需要通过模块切换来找到正确的资源模块,并进行对应的操作。
"共享MFC DLL的规则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的模块句柄。
为了完成模块切换,在所有从DLL输出的函数中都应该使用以下语句开头:
AFX_MANAGE_STATE(AfxGetStaticModuleState( ))
此句话用来正确切换MFC的模块状态。说明:其功能是在栈上(这意味着其作用域是局部的)创建一个AFX_MODULE_STATE类的实例,并将其指针pModuleState返回。AFX_MODULE_STATE类利用其构造函数和析构函数进行存储模块状态现场及恢复现场的工作。
该宏用于将pModuleState设置为当前的有效模块状态。当离开该宏的作用域时(也就离开了pModuleState所指栈上对象的作用域),先前的模块状态将由类AFX_MODULE_STATE的析构函数恢复。
(2)带静态链接MFC的规则DLL
这个不多讲,是将MFC dll编译到自身内部的DLL类型,对比"使用共享MFC DLL的规则DLL"不难理解;
(3)规则DLL的其他说明:
规则DLL应用程序使用了MFC的DllMain,它将调用DLL程序的应用程序对象(从CWinApp派生)的InitInstance函数和ExitInstance函数。扩展DLL必须实现自己的DllMain。 当然必须注意MFC dll已经隐藏了DllMain。它的初始化称许实在一个基于CWinApp类的InitInstance()函数。
2、MFC扩展DLL
MFC扩展DLL与MFC规则DLL的相同点在于在两种DLL的内部都可以使用MFC类库,其不同点在于MFC扩展DLL与应用程序的接口可以是MFC的。MFC扩展DLL的含义在于它是MFC的扩展,其主要功能是实现从现有MFC库类中派生出可重用的类。MFC扩展DLL使用MFC 动态链接库版本,因此只有用共享MFC版本生成的MFC可执行文件(应用程序或规则DLL)才能使用MFC扩展DLL。此类dll一般很少用,不多说。
(1) MFC规则DLL
MFC规则DLL可以在该dll内部使用MFC,但是与应用程序的接口不能是MFC的。能够被所有支持dll的编程语言所写的应用程序使用,当然也包括使用MFC创建的应用程序。在这种动态链接库中包含一个从CWinApp中继承而来的类,DllMain函数也被隐藏在其中了。
规则DLL包含俩类——静态链接到MFC上和动态连接到MFC上。静态链接到MFC上的规则DLL与MFC静态链接,将MFC的dll代码直接生成在该.dll中,在调用该dll时,使用的是该dll的资源句柄 ;动态链接到MFC上的规则DLL可以和使用它的应用程序同时动态链接到MFC的dll和MFC的扩展dll上,此时,MFC使用主应用程序的资源句柄加载资源模板,这样,如果主应用程序和dll中有相同的资源ID时,就出现了问题,此时需要进行模块转换,才能正确的家在资源。
1.使用AFX_MANAGE_STATE(AfxGetStaticModuleState())作为接口的第一条语句进行模块状态转换;AFX_MOUDLE_STATE * AFXAPI AfxGetStaticModuleState(),该函数返回当前模块状态,AFX_MANAGE_STATE(AFX_MOUDLE_STATE * pMoudleState),该宏用于将pMoudleState设置为当前模块状态,当宏的作用域结束后,也就是离开pMoudleState所指向栈上对象的作用域时,AFX_MOUDLE_STATE的析构函数完成模块状态的恢复;
2.AfxGetResourceHandle()//获取程序当前正在使用的模块句柄,AfxSetResourceHandle()//设置程序需要使用的模块句柄,在接口函数开始时进行模块状态转换,HINSTANCE old_hInstance=AfxGetResourceHandle(); AfxSetResourceHandle(当前dll的句柄,可以使用theApp.m_hInstance);后面是函数的其余部分,结尾处AfxSetResourceHandle(old_hInstance);将模块状态再次转换过来;该方法可以用在dll中,也可以用在应用程序调用该dll函数之前之后;
(2)MFC的扩展DLL
MFC的扩展DLL的内涵是MFC的扩展,用户使用MFC的扩展DLL就像使用MFC本身的DLL一样,除了可以在MFC的扩展DLL内部使用MFC外,MFC的扩展DLL与应用程序的接口也可以是MFC,一般使用MFC的扩展DLL来增强MFC的功能,使用vc++向导生成的MFC的扩展DLL会自动生成DllMain()函数。由MFC扩展的DLL导出的函数,变量和其他很相似,对于导出类,应该在声明类的前面加上AFX_EXT_CLASS。在 DLL 的头文件中,将 AFX_EXT_CLASS 关键字添加到类的声明中,
如下所示:
class AFX_EXT_CLASS CMyClass : public CDocument
{
// <body of class>
};
当定义了预处理 _AFXDLL 和 _AFXEXT 时,该宏被 MFC 定义为__declspec(dllexport)。
但当定义了 _AFXDLL 而未定义 _AFXEXT 时,该宏被定义为__declspec(dllimport),
预处理器符号 _AFXDLL 指示共享 MFC 版本正在由目标可执行文件(DLL 或应用程序)使用。
当 _AFXDLL 和 _AFXEXT 都定义了时,这指示目标可执行文件是扩展 DLL。