MFC DLL—Regular DLL and Extension DLL

来源:互联网 发布:php require默认路径 编辑:程序博客网 时间:2024/05/17 00:12

2  MFC DLL ——扩展的和正规的

MFC 的 AppWizard 可以让我们创建 MFC 库支持的两种 DLL :扩展的 DLL 和正规的 DLL 。这两种类型的区别是什么呢?

说明: 当然, Developer Studio 也让我们创建纯的、与 MFC 库无关的 Win32 DLL ,就像它让我们创建与 MFC 库无关的 Windows 程序一样。

²      扩展 DLL 支持 C++ 接口,换句话说,该 DLL 可以导出整个类,客户可以构造这些类的对象或从这些类进行派生。扩展 DLL 动态连接到 MFC 库的 DLL 版本的代码,因此,扩展 DLL 要求客户程序被动态连接到 MFC 库 (AppWizard 默认设置 ) ,并且客户程序和扩展 DLL 要一致连接到 MFC DLL 的相同版本 (mfc42.dll 、 mfcd42.dll 等 ) 。扩展 DLL 很小,我们可以创建一个简单的扩展 DLL ,大约 10KB 左右,它的装载会很快。

²      如果我们需要一个 DLL ,并希望它可以被任何 Win32 编程环境 ( 包括 Visual Basic 5.0) 装载,那么我们必须使用正规 DLL 。这里有一个很大的限制就是,正规 DLL 可以导出 C 风格的函数,但不能导出 C++ 类、成员函数或重载函数,因为每一个 C++ 编译器都有自己修饰名字的方法。不过,我们可以在正规 DLL 内部使用 C++ 类 ( 特别是 MFC 库的类 ) 。

当我们创建 MFC 正规 DLL 时,我们可以选择静态连接或动态连接到 MFC 库。如果选择了静态连接, DLL 将包括所有它需要的 MFC 库代码的拷贝,因此它可以独立于 MFC 库。一个典型的 Release 版本静态连接的正规 DLL 大约为 144KB 左右。如果选择动态连接,大小可降到 17KB 左右,但必须保证适当的 MFC DLL 在目标机器上存在。

当我们指定 AppWizard 创建 DLL 或 EXE 的类型时,编译器 #define 常量将按下表设置:

 
 动态连接到共享的 MFC 库
 静态连接到 MFC 库
 
正规 DLL
 _AFXDLL 、 _USRDLL
 _USRDLL
 
扩展 DLL
 _AFXEXT 、 _AFXDLL
                  不支持                 
 
客户 EXE
 _AFXDLL
 没有定义常量
 

如果我们查看 MFC 源代码和头文件,将会看到大量的这些常量的 #ifdef 语句。这表明库代码的编译也是非常不同的,具体取决于我们产生工程时指定的类型。

2.1  共享的MFC DLL 和Windows DLL

²      如果我们使用共享 MFC DLL 选项创建一个 Windows 的 Debug 目标的程序,则该程序将动态连接到下列一个或多个 (ANSI)MFC DLL :

mfc42d.dll
 核心 MFC 类
 
mfco42d.dll
 ActiveX(OLE) 类
 
mfcd42d.dll
 数据库类 (ODBC 和 DAO)
 
Mfcn42.dll
 Winsock 和 WinInet 类
 

²      当创建 Release 目标时,程序只是动态连接到 mfc42.dll 。对这些 MFC DLL 的连接都是通过导入库隐式连接。我们也可以假定隐式连接到 Windows 里的 ActiveX 和 ODBC 的 DLL ,这种情况下,我们可以认为当装入用 Release 目标创建的客户时,所有这些 DLL 都被连接到了程序中,而不管客户是否使用了 ActiveX 和 ODBC 特性。然而,实际情况并不是这样。通过一些创造性的手段,当这些函数中的某一个首先被调用时, MFC 会显示装入 ActiveX 和 ODBC DLL( 通过调用 LoadLibrary) 。这样客户程序只是装入自己所需要的 DLL 。

2.2  MFC 扩展DLL ——导出类

如果扩展 DLL 只包含被导出的 C++ 类,那么我们可以很方便地创建和使用该 DLL 。创建 EX21A 示例程序 (P433) 的步骤显示了如何让 AppWizard 创建一个扩展 DLL 的框架。该框架只有 DllMain 函数,我们只要简单地把 C++ 类加到工程里。但有一件特殊的事情我们必须要做,即我们必须把宏 AFX_EXT_CLASS 加到类声明中,如下所示:

view plaincopy to clipboardprint?
class AFX_EXT_CLASS Cstudent:public Cobject 
class AFX_EXT_CLASS Cstudent:public Cobject

不仅对 DLL 工程中的 H 文件要作这样的修改,对客户程序使用的 H 文件同样也要作修改。换句话说, H 文件对于客户和 DLL 是一样的。该宏根据相应条件会产生不同的代码——在 DLL 里导出类,在客户程序里导入类。

2.3  MFC 扩展DLL 资源搜索的顺序

如果我们创建一个动态连接到 MFC 的客户程序,许多 MFC 库的标准资源 ( 错误信息字符串和打印预览对话框模板等 ) 都被保存在 MFC DLL 里 (mfc42.dll 、 mfcd42.dll 等 ) ,但应用程序也有自己的资源。当程序调用一个 MFC 函数 ( 如 Cstring::LoadString 或 Cbitmap::LoadBitmap) 时,框架首先搜索 EXE 的资源,然后搜索 MFC DLL 的资源 。

如果程序包含一个扩展的 DLL ,并且 EXE 需要一个资源,则搜索顺序为:首先是 EXE 文件,然后是扩展 DLL ,再是 MFC DLL 。例如我们有一个字符串资源 ID ,在所有资源中它是唯一的,则 MFC 库将会找到该资源。如果在 EXE 文件和扩展 DLL 文件里有重复的字符串 ID ,则 MFC 库会装入 EXE 文件里的字符串。

如果扩展 DLL 装入一个资源,则搜索序列为:首先是扩展 DLL ,然后是 MFC DLL ,再是 EXE 。

说明: 如果需要的话,我们可以改变搜索序列。假定我们希望 EXE 代码首先搜索扩展 DLL 的资源,则可以使用下面的代码:

+ expand sourceview plaincopy to clipboardprint?
HINSTANCE hInstResourceClient=AfxGetResourceHandle();  
//Use DLL's instance handle  
AfxSetResourceHandle(::GetModuleHandle("mydllname.dll"));  
CString strRes;  
strRes.LoadString(IDS_MYSTRING);  
//Restore client's instance handle  
AfxSetResourceHandle(hInstResourceClient); 
HINSTANCE hInstResourceClient=AfxGetResourceHandle();
//Use DLL's instance handle
AfxSetResourceHandle(::GetModuleHandle("mydllname.dll"));
CString strRes;
strRes.LoadString(IDS_MYSTRING);
//Restore client's instance handle
AfxSetResourceHandle(hInstResourceClient);

 


我们不能用 AfxGetInstanceHandle 代替 ::GetModuleHandle 。在一个扩展 DLL 里, AfxGetInstanceHandle 返回的是 EXE 的实例句柄,而不是 DLL 的句柄。

2.4  如何使用MFC 扩展DLL

u      将一个独立的类 MyClass ,做成一个扩展的 DLL

a)         首先制作一个单独的功能简单的类,内容如下:

+ expand sourceview plaincopy to clipboardprint?
// MyClass.h  
class CMyClass  
{     
public:   
    CMyClass();   
    void SetValue(int n);     
    void GetValue();      
private:      
    int _a;   
};  
// MyClass.cpp  
#include "StdAfx.h"  
#include "MyClass.h"  
CMyClass::CMyClass()  
{     
    printf("CMyClass::CMyClass()/n");     
}  
void CMyClass::SetValue(int n)  
{     
    printf("SetValue(int n)/n");      
    _a=n;     
}  
void CMyClass::GetValue()  
{     
    printf("GetValue()/n");   
    printf("_a=%d/n",_a);     

// MyClass.h
class CMyClass

public: 
 CMyClass(); 
 void SetValue(int n); 
 void GetValue(); 
private: 
 int _a; 
};
// MyClass.cpp
#include "StdAfx.h"
#include "MyClass.h"
CMyClass::CMyClass()

 printf("CMyClass::CMyClass()/n"); 
}
void CMyClass::SetValue(int n)

 printf("SetValue(int n)/n"); 
 _a=n; 
}
void CMyClass::GetValue()

 printf("GetValue()/n"); 
 printf("_a=%d/n",_a); 
}

b)         然后,运行 AppWizard 产生一个扩展的 DLL ,通过选择 MFC AppWizard(dll)->MFC Extension DLL->Finish ,工程名为 ex21a 。  

c)         将 MyClass.h 和 MyClass.cpp 这两个文件拷贝到刚才创建的工程中,通过 Project->Add to Project->Files 。添加后,编辑 MyClass.h 文件如下:

+ expand sourceview plaincopy to clipboardprint?
// MyClass.h  
// 扩展DLL使用AFX_EXT_CLASS宏导出类,  
// 在_AFXDLL和_AFXEXT被定义时,AFX_EXT_CLASS宏被定义为:  
// #define AFX_EXT_CLASS       AFX_CLASS_EXPORT  
// #define AFX_CLASS_EXPORT __declspec(dllexport)  
//class CMyClass  
class AFX_EXT_CLASS CMyClass // 修改此处  
{     
public:   
    CMyClass();   
    void SetValue(int n);     
    void GetValue();      
private:      
    int _a;   
}; 
// MyClass.h
// 扩展DLL使用AFX_EXT_CLASS宏导出类,
// 在_AFXDLL和_AFXEXT被定义时,AFX_EXT_CLASS宏被定义为:
// #define AFX_EXT_CLASS       AFX_CLASS_EXPORT
// #define AFX_CLASS_EXPORT __declspec(dllexport)
//class CMyClass
class AFX_EXT_CLASS CMyClass // 修改此处

public: 
 CMyClass(); 
 void SetValue(int n); 
 void GetValue(); 
private: 
 int _a;
};

d)         最后编译工程,得到了我们需要的两个文件 ex21a.dll 和 ex21a.lib 。

u          DLL 测试客户程序

a)         运行 AppWizard 产生一个 Win32 控制台程序,通过选择 Win32 Console Application-> 工程名为 client-> 一个空白工程,然后再新建一个 C++ Source File ,文件名为 client 。完成后,在 client.cpp 文件中添加如下测试代码:

+ expand sourceview plaincopy to clipboardprint?
// client.cpp  
#include <stdio.h>  
#include <iostream>  
#include "MyClass.h"  
int main()  
{  
    printf("main()/n");   
    int nVal;     
    nVal=100;     
      
    //测试使用我们导入的类      
    CMyClass mc;      
    mc.SetValue(nVal);    
    mc.GetValue();    
      
    system("pause");      
    return 0;  

// client.cpp
#include <stdio.h>
#include <iostream>
#include "MyClass.h"
int main()
{
 printf("main()/n"); 
 int nVal; 
 nVal=100; 
 
 //测试使用我们导入的类 
 CMyClass mc; 
 mc.SetValue(nVal); 
 mc.GetValue(); 
 
 system("pause"); 
 return 0;
}

b)         修改设置,在 Project->Settings->General-> 在 Microsoft Foundation Classes 中选择 Use MFC in a Shared DLL 。

c)         将 ex21a.dll 、 ex21a.lib 和 MyClass.h 三个文件拷贝到 client 工程目录下,类似上述相同的方法,将 MyClass.h 添加到工程中,并且在 Project->Settings->Link ,在 Object/library modules 中添加 ex21a.lib( 多个 lib 用空格分开 ) 。

d)         修改 MyClass.h 文件的内容。当时我在调试的时候,出现了错误,需要小心。我们在扩展 DLL 工程中的 MyClass.h 文件中使用宏 AFX_EXT_CLASS 来声明导出类,而同时要在 client 工程中的 MyClass.h 文件中使用宏 AFX_EXT_CLASS 来声明导入类。但是在我编译 client 工程的时候问题出现了:

error C2079: 'CMyClass' uses undefined class 'AFX_EXT_CLASS'

提示 AFX_EXT_CLASS 没有定义,应该是少加头文件了。在网上 g 一下找到了一些相同的问题贴:

http://www.eggheadcafe.com/software/aspnet/29755654/problem-using-self-built.aspx

http://hi.baidu.com/qingdi963/blog/item/c75fa8501ffada878c5430b7.html/cmtid/24b28d832df3729af703a6c7#24b28d832df3729af703a6c7

http://msdn.microsoft.com/en-us/library/h5f7ck28.aspx (Extension DLLs)

http://msdn.microsoft.com/en-us/library/4fezhh3d.aspx (DLLHUSK Sample: Dynamically Links the MFC Library)

http://www.codeguru.com/Cpp/W-P/dll/article.php/c119 (Using one extension DLL in another)

 

解决方法如下,修改 MyClass.h 文件内容为:

方法 1 :

+ expand sourceview plaincopy to clipboardprint?
// MyClass.h  
// 客户程序使用AFX_EXT_CLASS宏导入类,  
// 在只定义了_AFXDLL时,AFX_EXT_CLASS宏被定义为:  
// #define AFX_EXT_CLASS       AFX_CLASS_IMPORT  
// #define AFX_CLASS_IMPORT __declspec(dllimport)  
#include <afxext.h> // 缺少头文件MFC extensions (可以在StdAfx.h中找到)  
//class CMyClass  
class AFX_EXT_CLASS CMyClass  
{     
public:   
    CMyClass();   
    void SetValue(int n);     
    void GetValue();      
private:  
    int _a;      
}; 
// MyClass.h
// 客户程序使用AFX_EXT_CLASS宏导入类,
// 在只定义了_AFXDLL时,AFX_EXT_CLASS宏被定义为:
// #define AFX_EXT_CLASS       AFX_CLASS_IMPORT
// #define AFX_CLASS_IMPORT __declspec(dllimport)
#include <afxext.h> // 缺少头文件MFC extensions (可以在StdAfx.h中找到)
//class CMyClass
class AFX_EXT_CLASS CMyClass

public: 
 CMyClass(); 
 void SetValue(int n); 
 void GetValue(); 
private:
 int _a;   
};

方法 2 :

+ expand sourceview plaincopy to clipboardprint?
// MyClass.h  
// 客户程序使用AFX_EXT_CLASS宏导入类,  
// 在只定义了_AFXDLL时,AFX_EXT_CLASS宏被定义为:  
// #define AFX_EXT_CLASS       AFX_CLASS_IMPORT  
// #define AFX_CLASS_IMPORT __declspec(dllimport)  
//class CMyClass  
//class AFX_EXT_CLASS CMyClass  
class __declspec(dllimport) CMyClass //通过上述分析,直接使用__declspec(dllimport)  
{     
public:   
    CMyClass();   
    void SetValue(int n);     
    void GetValue();      
private:      
    int _a;       
}; 
// MyClass.h
// 客户程序使用AFX_EXT_CLASS宏导入类,
// 在只定义了_AFXDLL时,AFX_EXT_CLASS宏被定义为:
// #define AFX_EXT_CLASS       AFX_CLASS_IMPORT
// #define AFX_CLASS_IMPORT __declspec(dllimport)
//class CMyClass
//class AFX_EXT_CLASS CMyClass
class __declspec(dllimport) CMyClass //通过上述分析,直接使用__declspec(dllimport)

public: 
 CMyClass(); 
 void SetValue(int n); 
 void GetValue(); 
private: 
 int _a;     
};

e)         现在重新编译没问题了,可以得到如下的输出结果,即可以在 client 程序中成功地调用我们在扩展 DLL 中导出的类了。

main()

CMyClass::CMyClass()

SetValue(int n)

GetValue()

_a=100

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/delphiwcdj/archive/2010/01/06/5142937.aspx

原创粉丝点击