收藏 分享(赏)

VC++动态链接库编程.doc

上传人:buyk185 文档编号:3889317 上传时间:2018-11-25 格式:DOC 页数:63 大小:661KB
下载 相关 举报
VC++动态链接库编程.doc_第1页
第1页 / 共63页
VC++动态链接库编程.doc_第2页
第2页 / 共63页
VC++动态链接库编程.doc_第3页
第3页 / 共63页
VC++动态链接库编程.doc_第4页
第4页 / 共63页
VC++动态链接库编程.doc_第5页
第5页 / 共63页
点击查看更多>>
资源描述

1、1VC+动态链接库编程之基础慨念2005-10 作者:宋宝华 出处:天极网http:/ 先来阐述一下 DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量、函数或类。在仓库的发展史上经历了“无库静态链接库 动态链接库”的时代。静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib中的指令都被直接包含在最终生成的 EXE 文件中了。但是若使用 DLL,该DLL不必被包含在最终 EXE文件中,EXE 文件执行时可以“动态”地引用和卸载这个与 EXE独立的 DLL文件。静 态链接库和动态

2、链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。对动态链接库,我们还需建立如下概念:(1)DLL 的编制与具体的编程语言及编译器无关只要遵循约定的 DLL接口规范和调用方式,用各种语言编写的 DLL都可以相互调用。譬如 Windows提供的系统 DLL(其中包括了 Windows的 API),在任何开发环境中都能被调用,不在乎其是 Visual Basic、Visual C+还是Delphi。(2)动态链接库随处可见我们在 Windows目录下的 system32文件夹中会看到kernel32.dll、user32.

3、dll 和 gdi32.dll,windows 的 大多数 API都包含在这些 DLL中。kernel32.dll 中的函数主要处理内存管理和进程调度;user32.dll中的函数主要控制用户界面; gdi32.dll 中的函数则负责图形方面的操作。一般的程序员都用过类似 MessageBox的函数,其实它就包含在user32.dll这个动态链接库中。由此可见 DLL对我们来说其实并不陌生。(3)VC 动态链接库的分类Visual C+支持三种 DLL,它们分别是 Non-MFC DLL(非 MFC动态库)、MFC Regular DLL(MFC 规则 DLL)、MFC Extension D

4、LL(MFC 扩展 DLL)。非 MFC动态库不采用 MFC类库结构,其导出函数为标准的 C接口,能被非MFC或 MFC编写的应用程序所调用;MFC 规则 DLL 包含一个继承自 CWinApp的类,但其无消息循环;MFC 扩展 DLL采用 MFC的动态链接版本创建,它只能被用 MFC类库所编写的应用程序所调用。2由于本文篇幅较长,内容较多,势必需要先对阅读本文的有关事项进行说明,下面以问答形式给出。问:本文主要讲解什么内容?答:本文详细介绍了 DLL编程的方方面面,努力学完本文应可以对 DLL有较全面的掌握,并能编写大多数 DLL程序。问:如何看本文?答:本文每一个主题的讲解都附带了源代码例

5、程,可以随文下载(每个工程都经 WINRAR压缩)。所有这些例程都由笔者编写并在 VC+6.0中调试通过。当然看懂本文不是读者的最终目的,读者应亲自动手实践才能真正掌握DLL的奥妙。问:学习本文需要什么样的基础知识?答:如果你掌握了 C,并大致掌握了 C+,了解一点 MFC的知识,就可以轻松地看懂本文。2.静态链接库对静态链接库的讲解不是本文的重点,但是在具体讲解 DLL之前,通过一个静态链接库的例子可以快速地帮助我们建立“库”的概念。图 1 建立一个静态链接库如图 1,在 VC+6.0中 new一个名称为 libTest的 static library工程3(单击此处下载本工程),并新建 l

6、ib.h和 lib.cpp两个文件,lib.h 和lib.cpp的源代码如下:/文件:lib.h#ifndef LIB_H#define LIB_Hextern “C“ int add(int x,int y); /声明为 C编译、连接方式的外部函数#endif/文件:lib.cpp#include “lib.h“int add(int x,int y)return x + y;编译这个工程就得到了一个.lib 文件,这个文件就是一个函数库,它提供了 add的功能。将头文件和.lib 文件提交给用户后,用户就可以直接使用其中的 add函数了。标准 Turbo C2.0中的 C库函数(我们用来的

7、scanf、printf、memcpy、strcpy 等)就来自这种静态库。下面来看看怎么使用这个库,在 libTest工程所在的工作区内 new一个libCall工程。libCall 工程仅包含一个 main.cpp文件,它演示了静态链接库的调用方法,其源代码如下:#include #include “lib.h“#pragma comment( lib, “debuglibTest.lib“ ) /指定与静态库一起连接int main(int argc, char* argv)printf( “2 + 3 = %d“, add( 2, 3 ) );静态链接库的调用就是这么简单,或许我们每天

8、都在用,可是我们没有明白这个概念。代码中#pragma comment( lib , “debuglibTest.lib“ )的意思是指本文件生成的.obj 文件应与 libTest.lib一起连接。如果不用#pragma comment指定,则可以直接在 VC+中设置,如图 2,依次选择4tools、options、directories、library files 菜单或选项,填入库文件路径。图 2中加红圈的部分为我们添加的 libTest.lib文件的路径。图 2 在 VC中设置库文件路径这个静态链接库的例子至少让我们明白了库函数是怎么回事,它们是哪来的。我们现在有下列模糊认识了:(1)

9、库不是个怪物,编写库的程序和编写一般的程序区别不大,只是库不能单独执行;(2)库提供一些可以给别的程序调用的东东,别的程序要调用它必须以某种方式指明它要调用之。以上从静态链接库分析而得到的对库的懵懂概念可以直接引申到动态链接库中,动态链接库与静态链接库在编写和调用上的不同体现在库的外部接口定义及调用方式略有差异。 3.库的调试与查看在具体进入各类 DLL的详细阐述之前,有必要对库文件的调试与查看方法进行一下介绍,因为从下一节开始我们将面对大量的例子工程。由于库文件不能单独执行,因而在按下 F5(开始 debug模式执行)或CTRL+F5(运行)执行时,其弹出如图 3所示的对话框,要求用户输入可

10、执行文 件的路径来启动库函数的执行。这个时候我们输入要调用该库的 EXE文件的路径就可以对库进行调试了,其调试技巧与一般应用工程的调试一样。5图 3 库的调试与“运行”通常有比上述做法更好的调试途径,那就是将库工程和应用工程(调用库的工程)放置在同一 VC工作区,只对应用工程进行调试,在应用工程调用库中函数的语 句处设置断点,执行后按下 F11,这样就单步进入了库中的函数。第2节中的 libTest和 libCall工程就放在了同一工作区,其工程结构如图 4所 示。图 4 把库工程和调用库的工程放入同一工作区进行调试上述调试方法对静态链接库和动态链接库而言是一致的。所以本文提供下载的所有源代码

11、中都包含了库工程和调用库的工程,这二者都被包含在一个工作区内,这是笔者提供这种打包下载的用意所在。动态链接库中的导出接口可以使用 Visual C+的 Depends工具进行查看,让我们用 Depends打开系统目录中的 user32.dll,看到了吧?红圈内的就是几个版本的 MessageBox了!原来它真的在这里啊,原来它就在这里啊!6图 5 用 Depends查看 DLL当然 Depends工具也可以显示 DLL的层次结构,若用它打开一个可执行文件则可以看出这个可执行文件调用了哪些 DLL。好,让我们正式进入动态链接库的世界,先来看看最一般的 DLL,即非 MFC DLL。VC+动态链接

12、库编程之非 MFC DLL 4.1一个简单的 DLL 第 2节给出了以静态链接库方式提供 add函数接口的方法,接下来我们来看看怎样用动态链接库实现一个同样功能的 add函数。如图 6,在 VC+中 new一个 Win32 Dynamic-Link Library工程dllTest(单击此处下载本工程)。注意不要选择 MFC AppWizard(dll),因为用 MFC AppWizard(dll)建立的将是第 5、6 节要讲述的 MFC 动态链接库。7图 6 建立一个非 MFC DLL在建立的工程中添加 lib.h及 lib.cpp文件,源代码如下:/* 文件名:lib.h */#ifnde

13、f LIB_H#define LIB_Hextern “C“ int _declspec(dllexport)add(int x, int y);#endif/* 文件名:lib.cpp */#include “lib.h“int add(int x, int y)return x + y;与第 2节对静态链接库的调用相似,我们也建立一个与 DLL工程处于同一工作区的应用工程 dllCall,它调用 DLL中的函数 add,其源代码如下:#include #include typedef int(*lpAddFun)(int, int); /宏定义函数指针类型int main(int argc

14、, char *argv)HINSTANCE hDll; /DLL 句柄 lpAddFun addFun; /函数指针hDll = LoadLibrary(“DebugdllTest.dll“);8if (hDll != NULL)addFun = (lpAddFun)GetProcAddress(hDll, “add“);if (addFun != NULL)int result = addFun(2, 3);printf(“%d“, result);FreeLibrary(hDll);return 0;分析上述代码,dllTest 工程中的 lib.cpp文件与第 2节静态链接库版本完全相

15、同,不同在于 lib.h对函数 add的声明前面添加了 _declspec(dllexport)语句。这个语句的含义是声明函数 add为 DLL的导出函数。DLL 内的函数分为两种:(1)DLL 导出函数,可供应用程序调用;(2) DLL 内部函数,只能在 DLL程序使用,应用程序无法调用它们。而应用程序对本 DLL的调用和对第 2节静态链接库的调用却有较大差异,下面我们来逐一分析。首先,语句 typedef int ( * lpAddFun)(int,int)定义了一个与 add函数接受参数类型和返回值均相同的函数指针类型。随后,在 main函数中定义了lpAddFun的实例 addFun;

16、其次,在函数 main中定义了一个 DLL HINSTANCE句柄实例 hDll,通过Win32 Api函数 LoadLibrary动态加载了 DLL模块并将 DLL模块句柄赋给了hDll;再次,在函数 main中通过 Win32 Api函数 GetProcAddress得到了所加载DLL模块中函数 add的地址并赋给了 addFun。经由函数指针 addFun进行了对DLL中 add函数的调用;最后,应用工程使用完 DLL后,在函数 main中通过 Win32 Api函数FreeLibrary释放了已经加载的 DLL模块。通过这个简单的例子,我们获知 DLL定义和调用的一般概念:(1)DLL

17、 中需以某种特定的方式声明导出函数(或变量、类);9(2)应用工程需以某种特定的方式调用 DLL的导出函数(或变量、类)。下面我们来对“特定的方式进行”阐述。4.2 声明导出函数DLL 中导出函数的声明有两种方式:一种为 4.1节例子中给出的在函数声明中加上_declspec(dllexport),这里不再举例说明;另外 一种方式是采用模块定义(.def) 文件声明,.def 文件为链接器提供了有关被链接程序的导出、属性及其他方面的信息。下面的代码演示了怎样同.def 文件将函数 add声明为 DLL导出函数(需在dllTest工程中添加 lib.def文件):; lib.def : 导出 D

18、LL函数LIBRARY dllTestEXPORTSadd 1.def 文件的规则为:(1)LIBRARY 语句说明.def 文件相应的 DLL;(2)EXPORTS 语句后列出要导出函数的名称。可以在.def 文件中的导出函数名后加n,表示要导出函数的序号为 n(在进行函数调用时,这个序号将发挥其作用);(3).def 文件中的注释由每个注释行开始处的分号 (;) 指定,且注释不能与语句共享一行。由此可以看出,例子中 lib.def文件的含义为生成名为“dllTest”的动态链接库,导出其中的 add函数,并指定 add函数的序号为 1。4.3 DLL 的调用方式在 4.1节的例子中我们看到

19、了由“LoadLibrary-GetProcAddress-FreeLibrary”系统 Api提供的三位一体“DLL 加载-DLL 函数地址获取-DLL 释放”方式,这种调用方式称为 DLL的动态调用。动态调用方式的特点是完全由编程者用 API 函数加载和卸载 DLL,程序员可以决定 DLL 文件何时加载或不加载,显式链接在运行时决定加载哪个 DLL 10文件。与动态调用方式相对应的就是静态调用方式,“有动必有静”,这来源于物质世界的对立统一。“动与静”,其对立与统一竟无数次在技术领域里得到验证,譬如 静态 IP与 DHCP、静态路由与动态路由等。从前文我们已经知道,库也分为静态库与动态库

20、DLL,而想不到,深入到 DLL内部,其调用方式也分为静态与动 态。“动与静”,无处不在。周易已认识到有动必有静的动静平衡观,易系辞曰:“动静有常,刚柔断矣”。哲学意味着一种普遍的真理,因此,我们经 常可以在枯燥的技术领域看到哲学的影子。静态调用方式的特点是由编译系统完成对 DLL的加载和应用程序结束时 DLL 的卸载。当调用某 DLL的应用程序结束时,若系统中还有其它程序使用该 DLL,则 Windows对 DLL的应用记录减 1,直到所有使用该 DLL的程序都结束时才释放它。静态调用方式简单实用,但不如动态调用方式灵活。下面我们来看看静态调用的例子(单击此处下载本工程),将编译dllTes

21、t工程所生成的.lib 和.dll 文件拷入 dllCall工程所在的路径,dllCall执行下列代码:#pragma comment(lib,“dllTest.lib“) /.lib文件中仅仅是关于其对应 DLL文件中函数的重定位信息extern “C“ _declspec(dllimport) add(int x,int y); int main(int argc, char* argv)int result = add(2,3); printf(“%d“,result);return 0;由上述代码可以看出,静态调用方式的顺利进行需要完成两个动作:(1)告诉编译器与 DLL相对应的.li

22、b 文件所在的路径及文件名,#pragma comment(lib,“dllTest.lib“)就是起这个作用。程序员在建立一个 DLL文件时,连接器会自动为其生成一个对应的.lib 文件,该文件包含了 DLL 导出函数的符号名及序号(并不含有实际的代码)。在应用程序里,.lib 文件将作为 DLL的替代文件参与编译。(2)声明导入函数,extern “C“ _declspec(dllimport) add(int x,int y)语句中的_declspec(dllimport)发挥这个作用。11静态调用方式不再需要使用系统 API来加载、卸载 DLL以及获取 DLL中导出函数的地址。这是因为

23、,当程序员通过静态链接方式编译生成应用程序时,应用 程序中调用的与.lib 文件中导出符号相匹配的函数符号将进入到生成的EXE 文件中,.lib 文件中所包含的与之对应的 DLL文件的文件名也被编译器存储在 EXE 文件内部。当应用程序运行过程中需要加载 DLL文件时,Windows 将根据这些信息发现并加载 DLL,然后通过符号名实现对 DLL 函数的动态链接。这样,EXE 将能直接通过函数名调用 DLL的输出函数,就象调用程序内部的其他函数一样。4.4 DllMain 函数Windows 在加载 DLL的时候,需要一个入口函数,就如同控制台或 DOS程序需要 main函数、 WIN32 程

24、序需要 WinMain函数一样。在前面的例子中,DLL并没有提供 DllMain函数,应用工程也能成功引用 DLL,这是因为 Windows 在找不到 DllMain的时候,系统会从其它运行库中引入一个不做任何操作的缺省DllMain函数版本,并不意味着 DLL可以放弃 DllMain函数。根据编写规范,Windows 必须查找并执行 DLL里的 DllMain函数作为加载DLL的依据,它使得 DLL得以保留在内存里。这个函数并不属于导出函数,而是 DLL的内部函数。这意味着不能直接在应用工程中引用 DllMain函数,DllMain是自动被调用的。我们来看一个 DllMain函数的例子(单击

25、此处下载本工程)。BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)switch (ul_reason_for_call)case DLL_PROCESS_ATTACH:printf(“nprocess attach of dll“);break;case DLL_THREAD_ATTACH:printf(“nthread attach of dll“);break;case DLL_THREAD_DETACH:printf(“nthread detach of dll“);br

26、eak;case DLL_PROCESS_DETACH:printf(“nprocess detach of dll“);break;return TRUE;12DllMain 函数在 DLL被加载和卸载时被调用,在单个线程启动和终止时,DLLMain函数也被调用,ul_reason_for_call 指明了 被调用的原因。原因共有4种,即 PROCESS_ATTACH、PROCESS_DETACH、THREAD_ATTACH 和 THREAD_DETACH,以 switch语句列出。来仔细解读一下 DllMain的函数头 BOOL APIENTRY DllMain( HANDLE hModu

27、le, WORD ul_reason_for_call, LPVOID lpReserved )。APIENTRY 被定义为_stdcall,它意味着这个函数以标准 Pascal的方式进行调用,也就是 WINAPI方式;进程中的每个 DLL模块被全局唯一的 32字节的 HINSTANCE句柄标识,只有在特定的进程内部有效,句柄代表了 DLL模块在进程虚拟空间中的起始地 址。在 Win32中,HINSTANCE 和 HMODULE的值是相同的,这两种类型可以替换使用,这就是函数参数 hModule的来历。执行下列代码:hDll = LoadLibrary(“DebugdllTest.dll“);

28、if (hDll != NULL)addFun = (lpAddFun)GetProcAddress(hDll, MAKEINTRESOURCE(1);/MAKEINTRESOURCE 直接使用导出文件中的序号if (addFun != NULL)int result = addFun(2, 3);printf(“ncall add in dll:%d“, result);FreeLibrary(hDll);我们看到输出顺序为:process attach of dllcall add in dll:5process detach of dll这一输出顺序验证了 DllMain被调用的时机。代

29、码中的 GetProcAddress ( hDll, MAKEINTRESOURCE ( 1 ) )值得留意,它直接通过.def 文件中为 add函数指定的顺序号访问 add函数,具体体现在MAKEINTRESOURCE ( 1 ),MAKEINTRESOURCE 是一个通过序号获取函数名的宏,定义为(节选自 winuser.h):13#define MAKEINTRESOURCEA(i) (LPSTR)(DWORD)(WORD)(i)#define MAKEINTRESOURCEW(i) (LPWSTR)(DWORD)(WORD)(i)#ifdef UNICODE#define MAKEIN

30、TRESOURCE MAKEINTRESOURCEW#else#define MAKEINTRESOURCE MAKEINTRESOURCEA4.5 _stdcall 约定如果通过 VC+编写的 DLL欲被其他语言编写的程序调用,应将函数的调用方式声明为_stdcall 方式,WINAPI 都采用这种方式,而 C/C+ +缺省的调用方式却为_cdecl。_stdcall 方式与_cdecl 对函数名最终生成符号的方式不同。若采用 C编译方式(在 C+中需将函数声 明为 extern “C“),_stdcall 调用约定在输出函数名前面加下划线,后面加“”符号和参数的字节数,形如_functio

31、nnamenumber;而 _cdecl 调用约定仅在输出函数名前面加下划线,形如_functionname。Windows 编程中常见的几种函数类型声明宏都是与_stdcall 和_cdecl 有关的(节选自 windef.h):#define CALLBACK _stdcall /这就是传说中的回调函数#define WINAPI _stdcall /这就是传说中的 WINAPI#define WINAPIV _cdecl#define APIENTRY WINAPI /DllMain的入口就在这里#define APIPRIVATE _stdcall#define PASCAL _std

32、call 在 lib.h中,应这样声明 add函数:int _stdcall add(int x, int y);在应用工程中函数指针类型应定义为:typedef int(_stdcall *lpAddFun)(int, int);若在 lib.h中将函数声明为_stdcall 调用,而应用工程中仍使用 typedef int (* lpAddFun)(int,int),运行时将发生错误(因为类型不匹配,在应用工程中仍然是缺省的_cdecl 调用),弹出如图 7所示的对话框。 14图 7 调用约定不匹配时的运行错误图 8中的那段话实际上已经给出了错误的原因,即“This is usually

33、a result of ”。单击此处下载_stdcall 调用例子工程源代码。4.6 DLL导出变量DLL 定义的全局变量可以被调用进程访问;DLL 也可以访问调用进程的全局数据,我们来看看在应用工程中引用 DLL中变量的例子(单击此处下载本工程)。/* 文件名:lib.h */#ifndef LIB_H#define LIB_Hextern int dllGlobalVar;#endif/* 文件名:lib.cpp */#include “lib.h“#include int dllGlobalVar;BOOL APIENTRY DllMain(HANDLE hModule, DWORD u

34、l_reason_for_call, LPVOID lpReserved)switch (ul_reason_for_call)case DLL_PROCESS_ATTACH:dllGlobalVar = 100; /在 dll被加载时,赋全局变量为 100break;15case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break;return TRUE;文件名:lib.def;在 DLL中导出变量LIBRARY “dllTest“EXPORTSdllGlobalVar CONSTANT;或 dllGl

35、obalVar DATAGetGlobalVar从 lib.h和 lib.cpp中可以看出,全局变量在 DLL中的定义和使用方法与一般的程序设计是一样的。若要导出某全局变量,我们需要在.def 文件的EXPORTS后添加:变量名 CONSTANT /过时的方法或变量名 DATA /VC+提示的新方法在主函数中引用 DLL中定义的全局变量:#include #pragma comment(lib,“dllTest.lib“)extern int dllGlobalVar;int main(int argc, char *argv)printf(“%d “, *(int*)dllGlobalVar

36、);*(int*)dllGlobalVar = 1;printf(“%d “, *(int*)dllGlobalVar);16return 0;特别要注意的是用 extern int dllGlobalVar声明所导入的并不是 DLL中全局变量本身,而是其地址,应用程序必须通过强制指针转换来使用 DLL中的全局变量。这一点,从* (int*)dllGlobalVar 可以看出。因此在采用这种方式引用 DLL全局变量时,千万不要进行这样的赋值操作:dllGlobalVar = 1;其结果是 dllGlobalVar指针的内容发生变化,程序中以后再也引用不到DLL中的全局变量了。在应用工程中引用

37、DLL中全局变量的一个更好方法是:#include #pragma comment(lib,“dllTest.lib“)extern int _declspec(dllimport) dllGlobalVar; /用_declspec(dllimport)导入int main(int argc, char *argv)printf(“%d “, dllGlobalVar);dllGlobalVar = 1; /这里就可以直接使用, 无须进行强制指针转换printf(“%d “, dllGlobalVar);return 0;通过_declspec(dllimport)方式导入的就是 DLL中全

38、局变量本身而不再是其地址了,笔者建议在一切可能的情况下都使用这种方式。4.7 DLL 导出类DLL 中定义的类可以在应用工程中使用。下面的例子里,我们在 DLL中定义了 point和 circle两个类,并在应用工程中引用了它们(单击此处下载本工程)。/文件名:point.h,point 类的声明#ifndef POINT_H#define POINT_H#ifdef DLL_FILEclass _declspec(dllexport) point /导出类 point17#elseclass _declspec(dllimport) point /导入类 point#endifpublic:

39、float y;float x;point();point(float x_coordinate, float y_coordinate);#endif/文件名:point.cpp,point 类的实现#ifndef DLL_FILE#define DLL_FILE#endif#include “point.h“/类 point的缺省构造函数point:point()x = 0.0;y = 0.0;/类 point的构造函数point:point(float x_coordinate, float y_coordinate)x = x_coordinate;y = y_coordinate;/

40、文件名:circle.h,circle 类的声明#ifndef CIRCLE_H#define CIRCLE_H#include “point.h“ #ifdef DLL_FILEclass _declspec(dllexport)circle /导出类 circle18#elseclass _declspec(dllimport)circle /导入类 circle#endifpublic:void SetCentre(const point void SetRadius(float r);float GetGirth();float GetArea();circle();private:f

41、loat radius;point centre;#endif/文件名:circle.cpp,circle 类的实现#ifndef DLL_FILE#define DLL_FILE#endif#include “circle.h“#define PI 3.1415926/circle类的构造函数circle:circle()centre = point(0, 0);radius = 0;/得到圆的面积float circle:GetArea()return PI *radius * radius;/得到圆的周长float circle:GetGirth()19return 2 *PI * ra

42、dius;/设置圆心坐标void circle:SetCentre(const point /设置圆的半径void circle:SetRadius(float r)radius = r;类的引用:#include “circle.h“ /包含类声明头文件#pragma comment(lib,“dllTest.lib“);int main(int argc, char *argv)circle c;point p(2.0, 2.0);c.SetCentre(p);c.SetRadius(1.0);printf(“area:%f girth:%f“, c.GetArea(), c.GetGir

43、th();return 0;从上述源代码可以看出,由于在 DLL的类实现代码中定义了宏 DLL_FILE,故在 DLL的实现中所包含的类声明实际上为:class _declspec(dllexport) point /导出类 point和class _declspec(dllexport) circle /导出类 circle20而在应用工程中没有定义 DLL_FILE,故其包含 point.h和 circle.h后引入的类声明为:class _declspec(dllimport) point /导入类 point和class _declspec(dllimport) circle /导入类

44、 circle不错,正是通过 DLL中的class _declspec(dllexport) class_name /导出类 circle 与应用程序中的class _declspec(dllimport) class_name /导入类匹对来完成类的导出和导入的!我们往往通过在类的声明头文件中用一个宏来决定使其编译为 class _declspec(dllexport) class_name还是 class _declspec(dllimport) class_name版本,这样就不再需要两个头文件。本程序中使用的是:#ifdef DLL_FILEclass _declspec(dllexp

45、ort) class_name /导出类#elseclass _declspec(dllimport) class_name /导入类#endif21实际上,在 MFC DLL的讲解中,您将看到比这更简便的方法,而此处仅仅是为了说明_declspec(dllexport)与_declspec(dllimport)匹对的问题。由此可见,应用工程中几乎可以看到 DLL中的一切,包括函数、变量以及类,这就是 DLL所要提供的强大能力。只要 DLL释放这些接口,应用程序使用它就将如同使用本工程中的程序一样!本章虽以 VC+为平台讲解非 MFC DLL,但是这些普遍的概念在其它语言及开发环境中也是相同的

46、,其思维方式可以直接过渡。 接下来,我们将要研究MFC规则 DLL。VC+动态链接库编程之 MFC规则 DLL第 4节我们对非 MFC DLL进行了介绍,这一节将详细地讲述 MFC规则 DLL的创建与使用技巧。 另外,自从本文开始连载后,收到 了一些读者的 e-mail。有的读者提出了一些问题,笔者将在本文的最后一次连载中选取其中的典型问题进行解答。由于时间的关系,对于读者朋友的来信,笔 者暂时不能一一回复,还望海涵!由于笔者的水平有限,文中难免有错误和纰漏,也热诚欢迎读者朋友不吝指正!5. MFC 规则 DLL5.1 概述MFC 规则 DLL的概念体现在两方面:(1) 它是 MFC的“是 M

47、FC的”意味着可以在这种 DLL的内部使用 MFC;(2) 它是规则的“是规则的”意味着它不同于 MFC扩展 DLL,在 MFC规则 DLL的内部虽然可以使用 MFC,但是其与应用程序的接口不能是 MFC。而 MFC扩展 DLL与应用程序的接口可以是 MFC,可以从 MFC扩展 DLL中导出一个 MFC类的派生类。Regular DLL 能够被所有支持 DLL技术的语言所编写的应用程序调用,当然也包括使用 MFC的应用程序。在这种动态连接库中,包含一个从 CWinApp继承下来的类,DllMain 函数则由 MFC自动提供。Regular DLL 分为两类:22(1)静态链接到 MFC 的规则

48、 DLL静态链接到 MFC的规则 DLL与 MFC库(包括 MFC扩展 DLL)静态链接,将MFC库的代码直接生成在.dll 文件中。在调用这种 DLL的接口时,MFC 使用 DLL的资源。因此,在静态链接到 MFC 的规则 DLL中不需要进行模块状态的切换。使用这种方法生成的规则 DLL其程序较大,也可能包含重复的代码。(2)动态链接到 MFC 的规则 DLL动态链接到 MFC 的规则 DLL 可以和使用它的可执行文件同时动态链接到 MFC DLL 和任何 MFC扩展 DLL。在使用了 MFC共享库的时候,默认情况下,MFC使用主应用程序的资源句柄来加载资源模板。这样,当 DLL和应用程序中

49、存在相同 ID的资源时(即 所谓的资源重复问题),系统可能不能获得正确的资源。因此,对于共享 MFC DLL的规则 DLL,我们必须进行模块切换以使得 MFC能够找到正确的资源模板。我们可以在 Visual C+中设置 MFC规则 DLL是静态链接到 MFC DLL还是动态链接到 MFC DLL。如图 8,依次选择 Visual C+的 project - Settings - General菜单或选项,在 Microsoft Foundation Classes中进行设置。图 8 设置动态/静态链接 MFC DLL5.2 MFC 规则 DLL的创建我们来一步步讲述使用 MFC向导创建 MFC规则 DLL的过程,首先新建一个project,如图 9,选择 p

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 网络科技 > C/C++资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报