MFC 正规DLL
2.5 MFC 正规DLL ——CWinApp 派生类
当用 AppWizard 产生正规 DLL 时, DllMain 函数将出现在框架内,并且我们会得到一个 CWinApp 的派生类 ( 和一个该类的全局对象 ) ,就像 EXE 程序的情形一样。我们可以通过重载 CWinApp::InitInstance 和 CWinApp::ExitInstance 函数获得控制。然而大多数情况下,我们不用重载这两函数。我们只要编写 C 函数,然后用 __declspec(dllexport) 修饰符导出这些函数 ( 或者在工程的 DEF 文件里加入函数入口 ) 即可。
2.6 使用AFX_MANAGE_STATE 宏
当 mfc42.dll 作为进程的一部分被装入时,它把数据存放在一些可靠的全局变量里。如果我们从一个 MFC 程序或扩展 DLL 中调用 MFC 函数,则 mfc42.dll 会知道如何代表调用进程去设置这些全局变量。然而,如果我们从一个正规 MFC DLL 中调用进入 mfc42.dll ,则全局变量并不同步,其后果不可预知。为了解决这个问题,请在正规 DLL 所有导出函数的开始处,插入下面的代码行:
AFX_MANAGE_STATE(AfxGetStaticModuleState());
如果 MFC 代码被静态连接,则该宏不会有任何影响。
2.7 如何使用MFC 正规DLL
u 创建 MFC 正规 DLL
a) 运行 AppWizard ,选择 MFC AppWizard(dll)->Regular DLL Using Shared MFC DLL ,工程名为 ex21c 。
b) 在 ex21c.cpp 文件中加入导入的函数代码:
+ expand sourceview plaincopy to clipboardprint?
// The one and only CEx21cApp object
CEx21cApp theApp;
extern "C" __declspec(dllexport) double Ex21cSquareRoot(double d)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState()); // Note!
TRACE("Entering EX21cSquareRoot/n");
if (d>=0.0)
{
return sqrt(d); // 添加math.h头文件
}
AfxMessageBox("Can't take square root of a negative number.");
return 0.0;
}
// The one and only CEx21cApp object
CEx21cApp theApp;
extern "C" __declspec(dllexport) double Ex21cSquareRoot(double d)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState()); // Note!
TRACE("Entering EX21cSquareRoot/n");
if (d>=0.0)
{
return sqrt(d); // 添加math.h头文件
}
AfxMessageBox("Can't take square root of a negative number.");
return 0.0;
}
c) 编译工程,得到 ex21c.dll 和 ex21c.lib 两个文件。
u 测试 DLL 的客户程序
a) 创建一个空白的 Win32 控制台程序,工程名为 client ,添加如下测试代码:
+ expand sourceview plaincopy to clipboardprint?
#include <stdio.h>
#include <iostream>
//声明Ex21cSquareRoot为一个导入函数
extern "C" __declspec(dllimport) double Ex21cSquareRoot(double d);
int main()
{
printf("input a number:");
double dInput,dOutput;
scanf("%lf",&dInput);
//测试使用导出的函数
dOutput=Ex21cSquareRoot(dInput);
printf("sqrt(%lf)=%lf/n",dInput,dOutput);
system("pause");
return 0;
}
#include <stdio.h>
#include <iostream>
//声明Ex21cSquareRoot为一个导入函数
extern "C" __declspec(dllimport) double Ex21cSquareRoot(double d);
int main()
{
printf("input a number:");
double dInput,dOutput;
scanf("%lf",&dInput);
//测试使用导出的函数
dOutput=Ex21cSquareRoot(dInput);
printf("sqrt(%lf)=%lf/n",dInput,dOutput);
system("pause");
return 0;
}
b) 将 ex21c.dll 和 ex21c.lib 这两个文件拷贝到 client 工程目录中,并且在 Project->Settings->Link ,在 Object/library modules 中添加 ex21c.lib( 多个 lib 用空格分开 ) 。
c) 编译并测试,输入 2 ,将输出如下结果,即可以成功地调用正规 DLL 导出的函数。
input a number:2
sqrt(2.000000)=1.414214
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/delphiwcdj/archive/2010/01/06/5144940.aspx
使用 MFC 可以生成两类 DLL : MFC 扩展 DLL 和常规 DLL 。常规 DLL 又分为两类:动态链接 (dynamically linked) 和静态链接 (statically linked) 。关于这两种 DLL 的区别和简单的用法已经在前面的博文中总结过,本文主要针对以下三个方面进一步地讨论关于这两类 DLL 的用法和注意事项。
构建 DLL
在客户程序中使用 DLL
DLL 与 MFC 和编译器的兼容相关的一些常见问题
From: CodeGuru Visual C++ 编程精粹(wcdj借于图书馆发现此书翻译的不是很好,将主要精髓整理如下)
--------------------------------------------------------------------------------
n 构建 DLL
通过 AppWizard 可以生成一个不完成任何实质性任务的 DLL 。新的 DLL 能编译,但由于它没有导出任何类或函数,所以仍然不能发挥作用。所以我们的任务就是:
² 给 DLL 添加功能,使之发挥作用;
² 修改客户应用程序来运用这个 DLL ;
我们可以完成下面的工作:
1) 导出类
一旦结束了 AppWizard ,然后就可以从另一个项目添加 .cpp 和 .h ,来给 DLL 添加类,或者在当前项目中从头开始创建它们。为了导出类,在类声明中添加“ __declspec(dllexport) ”,如下所示:
view plaincopy to clipboardprint?
class __declspec(dllexport) CMyClass
{
// 在此放置类声明
};
class __declspec(dllexport) CMyClass
{
// 在此放置类声明
};
如果创建的是 MFC 扩展 DLL ,可使用宏 AFX_EXT_CLASS :
view plaincopy to clipboardprint?
class AFX_EXT_CLASS CMyClass
{
// 在此放置类声明
};
class AFX_EXT_CLASS CMyClass
{
// 在此放置类声明
};
还有其他途径来导出类,但以上办法最容易。
2) 导出变量、导出常量和导出对象
如果不导出整个类,也可以让 DLL 导出一个变量、常量或对象。导出一个变量或常量,按如下方式声明:
view plaincopy to clipboardprint?
__declspec(dllexport) int MyInt;
__declspec(dllexport) extern const COLORREF MyColor=RGB(50,50,50);
__declspec(dllexport) int MyInt;
__declspec(dllexport) extern const COLORREF MyColor=RGB(50,50,50);
需要导出一个常量时,必须使用定义符 extern 。否则,会得到链接错误。
可以按相同的方式声明和导出一个类对象:
view plaincopy to clipboardprint?
__declspec(dllexport) CRect MyRect(30,30,300,300);
__declspec(dllexport) CRect MyRect(30,30,300,300);
注意: 如果客户程序识别这个类而且有自己的头文件,则只能导出一个类对象。如果在 DLL 中创建一个新类,客户程序不借助头文件,就不能识别它。
当你导出一个变量或对象时,载入此 DLL 的每个客户程序都将获得自己的拷贝。于是,如果两个不同的应用程序使用同一个 DLL ,一个应用程序所做的修改不会影响另一个应用程序。
务必记住: 只能导出处于 DLL 中具有全局作用域的对象和变量。当局部对象和变量越出作用域时,它们就会消亡。因此,如果 DLL 包含如下代码,就不会正常工作:
view plaincopy to clipboardprint?
MyFunction()
{
__declspec(dllexport) CSomeClass SomeObject;
__declspec(dllexport) int SomeInt;
}
MyFunction()
{
__declspec(dllexport) CSomeClass SomeObject;
__declspec(dllexport) int SomeInt;
}
一旦对象和变量越出作用域,它们就不复存在了。
3) 导出函数
导出函数与导出对象或变量相似。只要将“ __declspec(dllexport) ”放到函数原型开头:
view plaincopy to clipboardprint?
__declspec(dllexport) int SomeFunction(int);
__declspec(dllexport) int SomeFunction(int);
如果创建的是常规 DLL ,它将提供 C 写成的客户程序使用,则函数声明应如下所示:
view plaincopy to clipboardprint?
extern "C" __declspec(dllexport) int SomeFunction(int);
extern "C" __declspec(dllexport) int SomeFunction(int);
注意: 如果创建的是一个动态链接到 MFC 代码库 DLL 的常规 DLL ,则必须插入宏 AFX_MANAGE_STATE ,作为导出函数的首行。因此,函数定义应如下:
view plaincopy to clipboardprint?
extern "C" __declspec(dllexport) int AddFive(int x)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
return x+5;
}
extern "C" __declspec(dllexport) int AddFive(int x)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
return x+5;
}
说明: 在每个常规 DLL 中,这样做没有危害。如果将你的 DLL 切换到静态链接,其中的宏只是无效而已。
注意: 只有 MFC 扩展 DLL 导出的函数才可以让参数和返回值使用 MFC 数据类型。
4) 导出指针
导出指针与导出一个变量或对象的方式相同,比如:
view plaincopy to clipboardprint?
// 导出未初始化的指针
__declspec(dllexport) int* SomeInt;
// 导出未初始化的指针
__declspec(dllexport) int* SomeInt;
view plaincopy to clipboardprint?
// 导出一个初始化的指针
__declspec(dllexport) CSomeClass* SomePointer=new CSomeClass;
// 导出一个初始化的指针
__declspec(dllexport) CSomeClass* SomePointer=new CSomeClass;
当然,如果声明和初始化指针,需要找到删除它的地方。
u 在扩展 DLL 中,有一个 DllMain() 函数。当客户程序附加到 DLL 上,而且再次分离时,会调用这个函数。因此,这是一种在扩展 DLL 中处理指针的可能方式:
+ expand sourceview plaincopy to clipboardprint?
#include "SomeClass.h"
__declspec(dllexport) CSomeClass* SomePointer=new CSomeClass;
DllMain(HINSTANCE hInstance,DWORD dwReason,LPVOID lpReserved)
{
if (dwReason==DLL_PROCESS_ATTACH)
{
// 附加时
}
else if (dwReason==DLL_PROCESS_DETACH)
{
// 分离时
delete SomePointer;
}
}
#include "SomeClass.h"
__declspec(dllexport) CSomeClass* SomePointer=new CSomeClass;
DllMain(HINSTANCE hInstance,DWORD dwReason,LPVOID lpReserved)
{
if (dwReason==DLL_PROCESS_ATTACH)
{
// 附加时
}
else if (dwReason==DLL_PROCESS_DETACH)
{
// 分离时
delete SomePointer;
}
}
u 在常规 DLL 中,看起来和普通 MFC 可执行文件更像。它有一个从 CwinApp 派生的对象来处理 DLL 的开关。可以使用 Class Wizard 添加 InitInstance() 和 ExitInstance() 函数。
+ expand sourceview plaincopy to clipboardprint?
int CMyDllApp::ExitInstance()
{
delete SomePointer;
return CWinApp::ExitInstance();
}
int CMyDllApp::ExitInstance()
{
delete SomePointer;
return CWinApp::ExitInstance();
}
在客户程序中使用 DLL
DLL 不能在自身上运行。它需要有一个客户应用程序载入它,并使用它的接口。
编译 DLL 时,编译器创建两个重要文件: .dll 文件和 .lib 文件。客户应用程序需要这两个文件。必须将它们复制到客户应用程序的项目文件夹中。
注意: 在 debug 模式下通过生成创建的 .dll 文件和 .lib 文件与在 release 模式下创建的文件是不同的。在 debug 模式下生成的客户程序时,需要 .dll 和 .lib 的 debug 版本;而在 release 模式下生成的客户程序时,则需要 .dll 和 .lib 的 release 版本。
除了 .dll 和 .lib 文件外,客户程序还需要针对导出内容 ( 导出类、函数、对象和变量 ) 的头文件。导出时,在声明中添加“ __declspec(dllexport) ”,而在导入时,需要添加“ __declspec(dllimport) ”。
记住: 如果在 DLL 中使用定义符 extern “C” ,也必须在客户程序中使用它。
为了导入整个类,必须复制整个 .h 头文件到客户程序。 DLL 和客户程序于是具有导出类的相同的头文件,不同的是,在 DLL 中是“ class __declspec(dllexport) CMyClass ”,在客户程序文件中是“ class __declspec(dllimport) CMyClass ”。如果创建的是 MFC 扩展 DLL ,在两个位置均为“ class AFX_EXT_CLASS CMyClass ”。
说明: 一旦完成了客户程序的构建,就准备将它移交给实际用户,给予他们的应该是发行版本的可执行文件以及发行版本的 DLL ,不必给予用户 .lib 文件。 .dll 文件和 .exe 文件放在同一个目录中,或者将 .dll 放在 Windows 的 System 目录下。
警告: 由于 DLL 存在一些严重的缺陷,因此 COM 和 ATL 才应运而生。 DLL 存在的主要问题有两个: (1) 通过某种版本编译器构建的 DLL 可能与另外的编译器构建的客户程序不兼容。 (2) 修改 DLL 时,可能必须重新编译客户程序,即使没有更改客户程序中的代码时也是如此。可能仍然要拷入新的 .dll 和 .lib 文件,再进行重新的编译。
在某些情况下,可以通过一些途径来避免这个问题。下文将介绍两种方法。
n DLL 与 MFC 和编译器的兼容相关的一些常见问题
DLL 是任何 MFC 程序员有用的工具,但是它们存在许多重要的限制。
Ø MFC 问题 。 DLL 必须具备正确版本的 MFC 代码库。
Ø 编译器不兼容性问题 。用一种版本的编译器来构建 DLL ,但用另一种版本的编译器构建的 App 来调用它,这时就有可能会出现问题。
Ø 重新编译问题 。比如现在构建的 DLL 导出一个名叫 CMyClass 的类,为 CMyClass 提供一个头文件的拷贝,供客户应用程序使用,并假设 CMyClass 对象的大小为 30 字节。这时,假如修改 DLL 来更改 CMyClass ,为其增加一个 int 型的私有成员变量,因此,当创建 CMyClass 类型的对象时,大小变为 34 个字节。将这个新的 DLL 发送到用户,并通知他们替换掉旧的 DLL ,于是就产生了一个问题:客户 App 期望的是 30 字节大小的对象,但是新的
DLL 创建的是 34 个字节大小的对象,这时客户 App 就会出错。
解决方案是什么?
如果上述这些问题存在完美的解决方案的话,这也许就是 COM 。但是掌握 COM 或 ATL 需要花费大量的时间和精力,运用 DLL 相对容易得多。下面介绍两种通过修改 DLL 来解决上述问题的办法:
1) 使用接口类
接口类的目标是分离需要导出的类与该类的接口。完成的途径是创建第二个类,它将作为需要导出的类的接口。于是,即使在导出类改变时,也不必重新编译客户的 App ,因为接口类保持不变。
创建一个单独的接口类,只要接口类 ( 大小 ) 不改变,就不必重新编译。但是采用这种方法仍然存在两个相对较小的问题。 (1) 首先,对于 CMyClass 中的每个公有函数和成员变量,必须在 CMyInterface 中创建一个对应的函数或变量。如果 CMyClass 中有上百个函数和变量,创建过程就会非常冗长且很容易出错。 (2) 这样做导致必须完成的过程数量增加了,客户的 App 不再直接调用 CMyClass ,相反,它调用一个调用 CMyClass 的 CMyInterface 函数,如果这是一个被客户
App 经常调用的函数,那么额外的处理时间会激增。
2) 使用创建和销毁导出类的静态函数
避免必须重新编译的另一条途径是使用静态函数来创建和销毁导出类。在创建导出类时,添加两个公有静态函数 CreateMe() 和 DestoryMe() ,使用这种技术也可以修改 CMyClass 的大小而不必重新编译客户的 App 。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/delphiwcdj/archive/2010/01/12/5182966.aspx
分享到:
相关推荐
本例中的"Regular DLL"是指遵循MFC规范的动态链接库。VC++ 6.0是微软的老版集成开发环境,尽管现在有更新的版本如Visual Studio,但该例子仍然具有学习价值,因为MFC的基本原理和机制并未发生太大变化。 一个MFC ...
MFC 规则DLL 添加自定义的 DLLMAIN() 函数!(Vc6.0 与 Vc8.0 同样适用) 这也是我自己一直困扰了好久的问题,本来编程水平就不高,总想借助MFC的类库编写程序,方便不少啊!但是MFC规则DLL中却不提供DLLMAIN()函数...
本文将深入探讨MFC扩展DLL(Dynamic Link Library)中的导出类技术,帮助你理解如何创建和使用这类DLL。 MFC扩展DLL与常规DLL的主要区别在于它允许直接使用MFC类。这使得DLL可以包含MFC的成员函数,从而能够更方便...
MFC规则DLL详细介绍 MFC规则DLL是指在VC++环境下使用MFC框架编写的动态链接库。它不同于MFC扩展DLL,MFC规则DLL的概念体现在两方面:首先,它是MFC的,意味着可以在这种DLL的内部使用MFC;其次,它是规则的,意味着...
VC++动态链接库编程之MFC扩展DLL 本文将对VC++动态链接库编程之MFC扩展DLL进行详细的解释和分析。 一、MFC扩展DLL概论 MFC扩展DLL是指使用MFC类库的动态链接库,它可以与MFC规则DLL共存。在MFC扩展DLL中,可以...
一个简单的调用MFC规则DLL的实例,有详细的代码注释和文档说明。欢迎学习编写MFC规则DLL(共享非静态)的同学们下载使用。
本文主要介绍了如何使用MFC扩展DLL来导出类的方法,包括创建MFC扩展DLL的步骤、建立输出类、创建窗口类DLL、编译DLL、建立DLL测试程序等。 MFC扩展DLL是指使用MFC框架创建的动态链接库,可以提供公共接口供其他应用...
MFC扩展DLL是一种特殊的DLL类型,它允许你在DLL中使用MFC类,并且能够与MFC应用程序共享这些类。下面我们将深入探讨MFC扩展DLL的创建过程、用途及其相关知识点。 1. **MFC扩展DLL的创建** 创建MFC扩展DLL通常涉及...
本文将深入探讨如何创建一个遵循MFC规则的动态链接库(DLL),并展示如何在其他应用程序中调用这个DLL的功能。 首先,我们要理解动态链接库(DLL)的概念。DLL是一种可执行文件,它包含可被多个程序同时使用的代码...
MFC扩展DLL是Microsoft Foundation Class (MFC)库中的一个重要概念,主要用于创建可重用的类库,这些类库能够与MFC应用程序进行交互。MFC扩展DLL的主要特点是其接口可以是MFC类的,允许用户从现有的MFC类库中派生新...
在Microsoft Visual Studio 2015 (VS2015) 中,开发人员可以使用C++语言创建MFC(Microsoft Foundation Classes)DLL(Dynamic Link Library)动态库。MFC是微软提供的一套C++类库,它封装了Windows API,使得开发者...
- **规则 DLL (Regular DLL)** - **共享 MFC**:DLL 中不包含 MFC 库函数,需要额外安装 MFC 动态链接库才能使用。 - **静态 MFC**:DLL 内部包含了 MFC 库函数,可以脱离 MFC 动态链接库独立使用。 - **扩展 DLL ...
在MFC中,DLL有两种主要类型:MFC扩展DLL和MFC常规DLL。本例中我们关注的是MFC扩展DLL,因为它允许直接使用MFC类。 首先,创建MFC扩展DLL工程。在Visual Studio中,选择"文件" > "新建" > "项目",然后在模板中选择...
mfc70.dll
MFC扩展DLL允许我们在DLL中使用MFC的C++对象,而MFC常规DLL则将MFC类的实现封装在DLL中,调用者通过接口来使用。在这个例子中,"MFCLibrary2"可能是一个MFC扩展DLL,因为它提供了一个更灵活的方式,允许在DLL和调用...
这个名为"MFCDll.rar"的压缩包文件显然包含了关于MFC动态链接库(DLL)的实例和相关资源。动态链接库是一种共享代码的方式,使得多个程序可以同时使用同一份代码,从而节省内存并提高系统效率。 在Windows环境下,...
本文将详细讲解如何在MFC环境中创建一个扩展DLL,并导出一个对话框类,以便在其他应用程序中使用。我们将遵循以下步骤进行操作: 1. **创建MFC扩展DLL项目** 首先,我们需要在Visual Studio中创建一个新的MFC扩展...
在MFC开发中,有些功能是通过这些DLL实现的,比如常见的MFC核心库(mfcmfc.dll)、MFC ActiveX支持库(mfcautomation.dll)以及MFC的CRunTime类库(msvcrXX.dll,这里的XX代表版本号)等。 MFC的核心概念包括: 1. ...
综上所述,"VC++ MFC DLL"涉及到的是利用Visual C++的MFC库来创建和使用DLL的技能,包括DLL的创建、导出与导入、通信机制、调试、部署以及性能优化等多个方面。熟练掌握这些知识点,对于Windows平台的软件开发具有...
在本文中,我们将深入探讨如何在Visual Studio 2005环境下使用MFC(Microsoft Foundation Classes)库创建和调用DLL(Dynamic Link Library)文件。首先,MFC是微软为Windows应用程序开发提供的一种C++类库,它封装...