关闭

AFX_EXT_CLASS

937人阅读 评论(0) 收藏 举报

MFC扩展DLL是通常实现从现有Microsoft基础类库类派生的可重用类的DLL。
      MFC扩展DLL具有下列功能和要求:
          1。客户端可执行文件必须是用定义的_AFXDLL编译的MFC应用程序。
          2。扩展DLL也可由动态链接到MFC的规则DLL使用。
          3。扩展DLL应该用定义的_AFXEXT编译。这将强制同时定义_AFXDLL,并确保从MFC头文件中拉入正确的声明。它也确保了在生成DLL时将AFX_EXT_CLASS定义为__declspec(dllexport),这在使用此宏声明扩展DLL中的类时是必要的。
          4。扩展DLL不应实例化从CWinApp派生的类,而应依赖客户端应用程序(或DLL)提供此对象。
          5。但扩展DLL应提供DllMain函数,并在那里执行任何必需的初始化。
      扩展DLL是使用MFC动态链接库版本(也称作共享MFC版本)生成的。只有用共享MFC版本生成的MFC可执行文件(应用程序或规则DLL)才能使用扩展DLL。客户端应用程序和扩展DLL必须使用相同版本的MFCx0.dll。使用扩展DLL,可以从MFC派生新的自定义类,然后将此“扩展”版本的MFC提供给调用DLL的应用程序。
      扩展DLL也可用于在应用程序和DLL之间传递MFC派生的对象。与已传递的对象关联的成员函数存在于创建对象所在的模块中。由于在使用MFC的共享DLL版本时正确导出了这些函数,因此可以在应用程序和它加载的扩展DLL之间随意传递MFC或MFC派生的对象指针。
      MFC扩展DLL使用共享MFC版本的方式与应用程序使用MFC的共享DLL版本的方式相同,但有另外几点需要注意的事项:它没有CWinApp派生对象。它必须与客户端应用程序的CWinApp派生对象一起使用。这意味着客户端应用程序拥有主消息泵、空闲循环等。
      它在自己的DllMain函数中调用AfxInitExtensionModule。应检查此函数的返回值。如果从AfxInitExtensionModule返回的是零值,则从DllMain函数返回0。
      如果扩展DLL希望将CRuntimeClass对象或资源导出到应用程序中,它将在初始化期间创建一个CDynLinkLibrary对象。
   在MFC4.0版之前,这类DLL称作AFXDLL。AFXDLL引用生成DLL时定义的_AFXDLL预处理器符号。
      共享版本MFC的导入库的命名依据MFCDLL命名约定中描述的约定。VisualC++提供预生成的MFCDLL版本,以及若干可以使用并用应用程序发布的非MFCDLL。它们记录在安装到ProgramFiles\MicrosoftVisualStudio文件夹的Redist.txt中。
      如果要使用.def文件导出,请在头文件的开始和结尾处放置下列代码:
      #undef AFX_DATA
      #define AFX_DATAAFX_EXT_DATA
      //<body of your header file>
      #undef AFX_DATA
      #define AFX_DATA
      这四行确保为扩展DLL正确编译代码。省去这四行可能导致DLL不能正确地编译或链接。
      如果需要与MFCDLL来回传递MFC或MFC派生对象的指针,DLL应为扩展DLL。与已传递的对象关联的成员函数存在于创建对象所在的模块中。由于在使用MFC的共享DLL版本时正确导出了这些函数,因此可以在应用程序和它加载的扩展DLL之间随意传递MFC或MFC派生的对象指针。
      由于存在C++名称重整和导出问题,在同一DLL的调试版本和零售版本及用于不同平台的DLL之间,扩展DLL中的导出列表可能不同。零售版本的MFCx0.dll有大约2,000个导出入口点;调试版本的MFCx0D.dll有大约3,000个导出入口点。


内存管理
      MFCx0.dll和所有加载到客户端应用程序的地址空间中的扩展DLL使用相同的内存分配器、资源加载和其他MFC“全局”状态,就好像它们在同一个应用程序中一样。这一点意义重大,因为非MFCDLL库和规则DLL正好相反,它们让每个DLL在各自的内存池之外分配。
      如果扩展DLL分配内存,则该内存能够随意与任何其他应用程序分配的对象相混合。同时,如果动态链接到MFC的应用程序失败,操作系统的保护将维护共享DLL的任何其他MFC应用程序的完整性。
      同样,其他“全局”MFC状态(如从中加载资源的当前可执行文件)也在客户端应用程序与MFCx0.dll本身以及所有MFC扩展DLL之间共享。

共享资源和类
      导出资源的操作是通过资源列表完成的。每个应用程序均包含CDynLinkLibrary对象的单向链接表。查找资源时,大部分加载资源的标准MFC实现首先查看当前资源模块(AfxGetResourceHandle),如果未找到资源,则浏览CDynLinkLibrary对象的列表,并尝试加载请求的资源。
      浏览列表的做法有一些缺点,即速度稍慢且需要管理资源ID范围。它的优点在于链接到几个扩展DLL的客户端应用程序可以使用DLL提供的任何资源,而无需指定DLL实例句柄。AfxFindResourceHandle是用于浏览资源列表以查找给定匹配的API。它采用资源的名称和类型,并从最先找到匹配项的位置返回资源句柄(或NULL)。
      如果不想浏览列表,而是仅从特定的位置加载资源,请使用函数AfxGetResourceHandle和AfxSetResourceHandle保存旧句柄和设置新句柄。返回到客户端应用程序之前,请务必还原旧资源句柄。有关使用此方法显式加载菜单的示例,请参见MFC示例DLLHUSK中的Testdll2.cpp。
      动态创建给定了MFC名称的MFC对象与此类似。MFC对象反序列化机制需要注册所有的CRuntimeClass对象,以便可以通过基于以前存储的内容动态创建所需类型的C++对象来重新构造。
      在MFC示例DLLHUSK中,此列表的形式如下:
      head ->     DLLHUSK.EXE     - or -     DLLHUSK.EXE
                     |                        |
                TESTDLL2.DLL             TESTDLL2.DLL
                     |                        |
                TESTDLL1.DLL             TESTDLL1.DLL
                     |                        |
                MFCOxxD.DLL                   |
                     |                        |
                MFCDxxD.DLL                   |
                     |                        |
                MFCxxD.DLL               MFCxx.DLL
      其中xx是版本号;例如42表示4.2版。
      MFCxx.dll通常排在资源和类列表的最后。MFCxx.dll包含所有的标准MFC资源,其中包括所有标准命令ID的提示字符串。将MFCxx.dll放在列表的最后使得DLL和客户端应用程序本身不必有自己的标准MFC资源副本,而是可以依赖MFCxx.dll中的共享资源。
      将所有DLL的资源名和类名合并到客户端应用程序的命名空间中,这种做法的缺点是需要小心选取ID或名称。
      DLLHUSK示例通过使用多个头文件来管理共享资源命名空间。
      如果MFC扩展DLL需要为每个应用程序维护额外的数据,可从CDynLinkLibrary派生一个新类并在DllMain中创建此类。运行时,DLL会检查当前应用程序的CDynLinkLibrary对象列表,以查找用于特定扩展DLL的对象。

 

使用 AFX_EXT_CLASS 导出和导入

扩展DLL使用 AFX_EXT_CLASS 宏导出类;链接到扩展 DLL 的可执行文件使用该宏导入类。使用 AFX_EXT_CLASS 宏,用于生成扩展 DLL 的相同头文件可以与链接到 DLL 的可执行文件一起使用。

在 DLL 的头文件中,将 AFX_EXT_CLASS 关键字添加到类的声明中,如下所示:

class AFX_EXT_CLASS CMyClass : public Cdocument{// };

当定义了预处理 _AFXDLL 和 _AFXEXT 时,该宏被 MFC 定义为 __declsp(dllexport).但当定义了 _AFXDLL 而未定义 _AFXEXT 时,该宏被定义为 __declspec(dllimport)。定义后,预处理器符号 _AFXDLL 指示共享 MFC 版本正在由目标可执行文件(DLL 或应用程序)使用。当 _AFXDLL 和 _AFXEXT 都定义了时,这指示目标可执行文件是扩展 DLL。

由于从扩展 DLL 导出时,AFX_EXT_CLASS 被定义为 __declspec(dllexport),因此可以导出整个类,而不必将该类的所有符号的修饰名放到 .DEF 文件中。此方法由 MFC 示例DLLHUSK使用。

虽然使用此方法可以避免创建 .DEF 文件和类的所有修饰名,但由于名称可以按序号导出,创建 .DEF 文件的效率更高。若要使用 .DEF 文件导出方法,请将下列代码放在头文件的开头和结尾处:

#undef AFX_DATA

#define AFX_DATA AFX_EXT_DATA

//

#undef AFX_DATA

#define AFX_DATA

警告导出内联函数时要小心,因为它们有可能导致版本冲突。内联函数扩展到应用程序代码中;因此,如果以后重写内联函数,除非重新编译应用程序本身,否则内联函数不会被更新。(通常,不用重新生成使用 DLL 函数的应用程序就可以更新 DLL 函数。)

导出类中的个别成员

有时,您可能希望导出类中的个别成员。例如,如果导出 CDialog 派生类,可能只需要导出构造函数和 DoModal 调用。可以对需要导出的个别成员使用 AFX_EXT_CLASS。

例如:

class CExampleDialog : public CDialog

{

public:

AFX_EXT_CLASS CExampleDialog();

AFX_EXT_CLASS int DoModal();

...

// rest of class definition

...

};

您不再导出类的所有成员,但由于 MFC 宏的工作方式,您可能会遇到其他问题。几个 MFC 的 Helper 宏实际声明或定义数据成员。因此,还必须从 DLL 导出这些数据成员。

例如,当生成扩展 DLL 时,DECLARE_DYNAMIC 宏的定义如下:

#define DECLARE_DYNAMIC(class_name)

protected:

static CRuntimeClass* PASCAL _GetBaseClass();

public:

static AFX_DATA CRuntimeClass class##class_name;

virtual CRuntimeClass* GetRuntimeClass() const;

以 static AFX_DATA 打头的行声明类的内部静态对象。若要正确导出该类并从客户端可执行文件访问运行时信息,必须导出此静态对象。由于静态对象是用 AFX_DATA 修饰符声明的,因此只需在生成 DLL 时将 AFX_DATA 定义为 __declspec(dllexport),并在生成客户端可执行文件时将 AFX_DATA 定义为 __declspec(dllimport)。由于已经以此方式定义了 AFX_EXT_CLASS,因此只需参考类定义,将 AFX_DATA 重定义为与 AFX_EXT_CLASS 相同。

例如:

#undef AFX_DATA

#define AFX_DATA AFX_EXT_CLASS

class CExampleView : public CView

{

DECLARE_DYNAMIC()

// ... class definition ...

};

#undef AFX_DATA

#define AFX_DATA

MFC 总是在其宏的内部定义的数据项上使用 AFX_DATA 符号,因此该技术适用于所有这类情况。例如,它适用于 DECLARE_MESSAGE_MAP。

注意如果导出整个类而非选定的类成员,静态数据成员将自动导出。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:11460557次
    • 积分:62238
    • 等级:
    • 排名:第42名
    • 原创:675篇
    • 转载:2224篇
    • 译文:0篇
    • 评论:543条
    文章分类
    文章存档
    最新评论
    计算流量