DLL入门浅析(1)——如何建立DLL
转载自:http://www.cppblog.com/suiaiguo/archive/2009/07/20/90619.html
初学DLL,结合教程,总结一下自己的所得,希望对DLL初学者们有所帮助。
动态链接库(DLL)是从C语言函数库和Pascal库单元的概念发展而来的。所有的C语言标准库函数都存放在某一函数库中。在链接应用程序的过程中,链接器从库文件中拷贝程序调用的函数代码,并把这些函数代码添加到可执行文件中。这种方法同只把函数储存在已编译的OBJ文件中相比更有利于代码的重用。但随着Windows这样的多任务环境的出现,函数库的方法显得过于累赘。如果为了完成屏幕输出、消息处理、内存管理、对话框等操作,每个程序都不得不拥有自己的函数,那么Windows程序将变得非常庞大。Windows的发展要求允许同时运行的几个程序共享一组函数的单一拷贝。动态链接库就是在这种情况下出现的。动态链接库不用重复编译或链接,一旦装入内存,DLL函数可以被系统中的任何正在运行的应用程序软件所使用,而不必再将DLL函数的另一拷贝装入内存。
下面我们一步一步来建立一个DLL。
一、建立一个DLL工程
新建一个工程,选择Win32
控制台项目(Win32 Console Application),并且在应用程序设置标签(the advanced
tab)上,选择DLL和空项目选项。
二、声明导出函数
这里有两种方法声明导出函数:一种是通过使用__declspec(dllexport),添加到需要导出的函数前,进行声明;另外一种就是通过模块定义文件(Module-Definition
File即.DEF)来进行声明。
第一种方法,建立头文件DLLSample.h,在头文件中,对需要导出的函数进行声明。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#ifndef _DLL_SAMPLE_H #define _DLL_SAMPLE_H // 如果定义了C++编译器,那么声明为C链接方式 #ifdef __cplusplus extern "C" { #endif // 通过宏来控制是导入还是导出 #ifdef _DLL_SAMPLE #define DLL_SAMPLE_API __declspec(dllexport) #else #define DLL_SAMPLE_API __declspec(dllimport) #endif // 导出/导入函数声明 DLL_SAMPLE_API void TestDLL(int); #undef DLL_SAMPLE_API #ifdef __cplusplus } #endif #endif
这个头文件会分别被DLL和调用DLL的应用程序引入,当被DLL引入时,在DLL中定义_DLL_SAMPLE宏,这样就会在DLL模块中声明函数为导出函数;当被调用DLL的应用程序引入时,就没有定义_DLL_SAMPLE,这样就会声明头文件中的函数为从DLL中的导入函数。
第二种方法:模块定义文件是一个有着.def文件扩展名的文本文件。它被用于导出一个DLL的函数,和__declspec(dllexport)很相似,但是.def文件并不是Microsoft定义的。一个.def文件中只有两个必需的部分:LIBRARY
和 EXPORTS。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
LIBRARY DLLSample DESCRIPTION "my simple DLL" EXPORTS TestDLL @1 ;@1表示这是第一个导出函数
第一行,''LIBRARY''是一个必需的部分。它告诉链接器(linker)如何命名你的DLL。下面被标识为''DESCRIPTION''的部分并不是必需的。该语句将字符串写入
.rdata
节,它告诉人们谁可能使用这个DLL,这个DLL做什么或它为了什么(存在)。再下面的部分标识为''EXPORTS''是另一个必需的部分;这个部分使得该函数可以被其它应用程序访问到并且它创建一个导入库。当你生成这个项目时,不仅是一个.dll文件被创建,而且一个文件扩展名为.lib的导出库也被创建了。除了前面的部分以外,这里还有其它四个部分标识为:NAME,
STACKSIZE, SECTIONS, 和
VERSION。另外,一个分号(;)开始一个注解,如同''//''在C++中一样。定义了这个文件之后,头文件中的__declspec(dllexport)就不需要声明了。
三、编写DllMain函数和导出函数
DllMain函数是DLL模块的默认入口点。当Windows加载DLL模块时调用这一函数。系统首先调用全局对象的构造函数,然后调用全局函数DLLMain。DLLMain函数不仅在将DLL链接加载到进程时被调用,在DLL模块与进程分离时(以及其它时候)也被调用。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include "stdafx.h" #define _DLL_SAMPLE #ifndef _DLL_SAMPLE_H #include "DLLSample.h" #endif #include "stdio.h" //APIENTRY声明DLL函数入口点 BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } void TestDLL(int arg) { printf("DLL output arg %d\n", arg); }
如果程序员没有为DLL模块编写一个DLLMain函数,系统会从其它运行库中引入一个不做任何操作的缺省DLLMain函数版本。在单个线程启动和终止时,DLLMain函数也被调用。
然后,F7编译,就得到一个DLL了。