亲手尝试了一下创建并引用DLL一种方式,记录一下过程。
使用的语言为C++,IDE使用的为VS2010
说一下我自己的理解。程序编译是指编译当前程序中写的代码,当然应用程序中有可能还会包含着已经其他的开发人员开发的代码,这时不需要编译他们的代码,因为有可能他们就没给你源代码,这个时候要把他们提供的代码链接到应用程序中,怎么用呢。一般来说是会提供一个lib文件,或dll文件。简单说一下认识。
有两种方式把这些lib文件或者dll文件引进过来,一种是静态链接,一种是动态链接。
静态链接是把这些lib和dll直接加入到应用程序生成的exe文件中,这样的话会导致exe比较大,占用比较大的存储空间。lib文件中直接就是把obj文件直接压进lib文件中,在使用的过程中把lib和.h加入到工程中,直接编译就可以了。
而开发静态库时, 你只需要在头文件声明函数或者类,然后再实现文件里实现函数或类就可以了。
使用的时候,.h头文件加到工程里,然后再指定要链接的.lib文件,就可以了。
可用#pragma comment(lib, "libfilepath")来指定要链接的静态库。或者在project->dependency里设置。
libfilepath可以设定为相对路径使用.\ ..\等来指定路径。
动态链接使用dll文件,这个文件实际上里面都是函数的描述和定义,其实和exe文件很相似,但是它不能单独的执行。这个文件不能单独使用,一般要和lib文件一起配合使用,这个lib文件就和静态的不是一个意思,这个lib文件里面不是包括obj文件,而是对dll中的函数的位置,参数等的描述,作用类似于h文件,对dll中函数进行说明。这个dll文件不会加入到exe文件中,所以在程序执行的时候必须有exe和dll文件共同存在。这种动态链接的好处是
1、扩展了应用程序的特性;
2、可以用许多种编程语言来编写;
3、简化了软件项目的管理;
4、有助于节省内存;
5、有助于资源共享;
6、有助于应用程序的本地化;
7、有助于解决平台差异;
静态链接库和动态库(DLL)的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。
本文主要是针对于动态链接进行介绍
先说一下创建DLL
1.先创建工程,导出符号要勾选上
2.代码如下
3.编译后生成的了dll和lib文件
动态链接的使用方式又分为两种,一种称为静态的,一种称为动态的。
开发和使用dll需注意三种文件:
1)dll头文件
它是指dll中说明输出的类或符号原型或数据结构的.h文件。当其它应用程序调用dll时,需要将该文件包含入应用程序的源文件中。
2)dll的引入库文件(.lib)
它是dll在编译、链接成功后生成的文件。主要作用是当其它应用程序调用dll时,需要将该文件引入应用程序。否则,dll无法引入。
3)dll文件(.dll)
它是应用程序调用dll运行时,真正的可执行文件。dll应用在编译、链接成功后,.dll文件即存在。开发成功后的应用程序在发布时,只需要有.exe文件和.dll文件,不必有.lib文件和dll头文件。
从使用的角度上来看,如何静态调用,则要用到lib文件,而动态调用则不用,只需要DLL文件就够了。
DLL的调用分为动态和静态两种:动态调用和静态调用。动态调用方式的特点是完全由编程者用 API 函数加载和卸载 DLL,程序员可以决定 DLL 文件何时加载或不加载,显式链接在运行时决定加载哪个 DLL 文件。
动态调用,即显式调用方式,是由编程者用API函数加载和卸载DLL来达到调用DLL的目的,比较复杂,但能更加有效地使用内存,是编制大型应用程序时的重要方式。在Windows系统中,与动态库调用有关的函数包括:
①LoadLibrary(或MFC 的AfxLoadLibrary),装载动态库。
②GetProcAddress,获取要引入的函数,将符号名或标识号转换为DLL内部地址。
③FreeLibrary(或MFC的AfxFreeLibrary),释放动态链接库。
静态调用,也称为隐式调用,由编译系统完成对DLL的加载和应用程序结束时DLL卸载的编码(Windows系统负责对DLL调用次数的计数),调用方式简单,能够满足通常的要求。通常采用的调用方式是把产生动态连接库时产生的.LIB文件加入到应用程序的工程中,想使用DLL中的函数时,只须在源文件中声明一下。
LIB文件包含了每一个DLL导出函数的符号名和可选择的标识号以及DLL文件名,不含有实际的代码。Lib文件包含的信息进入到生成的应用程序中,被调用的DLL文件会在应用程序加载时同时加载在到内存中。
静态调用方式的特点是由编译系统完成对DLL的加载和应用程序结束时 DLL 的卸载。当调用某DLL的应用程序结束时,若系统中还有其它程序使用该 DLL,则Windows对DLL的应用记录减1,直到所有使用该DLL的程序都结束时才释放它。静态调用方式简单实用,但不如动态调用方式灵活。
静态加载过程
1.先建一个工程
2.现在要设置工程了,这个环节很重要。
先把MyDLL.dll中的声明文件MyDLL.h加入到T_MyDLL工程中,如果这里不这么设置的话,则要MyDLL.h引入到工程中。实际上这个文件只是在编译中用到,最终的exe不需要这个文件,所以这里建议通过在工程中设定一下。在工程上点右键,属性,进行设置,在“附加包含目录”中把路径设成刚刚MyDLL的目录,注意路径要到MyDLL.h这个路径下。
这里介绍这里设置的“附加包含目录”。
这里简单说一下和上面的“VC++目录”的区别
“VC++ Directories -> Include Directories” : Directory settings displayed in the window are the directories that Visual Studio will search for include files referred to in your source code files. Corresponds to environment variable INCLUDE. More information : http://msdn.microsoft.com/en-us/library/t9az1d21(v=vs.80).aspx
“C/C++ -> General -> Additional Include Directories”: The directory to be added to the list of directories searched for include files. More information : http://msdn.microsoft.com/en-us/library/73f9s62w(v=vs.80).aspx
下面那个链接讲的比较清楚了:
The compiler searches for directories in the following order:
1.Directories containing the source file.
2.Directories specified with the /I option, in the order that CL encounters them.
3.Directories specified in the INCLUDE environment variable.
order2中的/I是由C/C++ -> General -> Additional Include Directories设置的。
order3中的INCLUDE是由VC++ Directories -> Include Directories设置的。
设置完了MyDLL.h的路径后还要设置依赖文件MyDLL.lib的路径,因为MyDLL.lib是对DLL文件的说明,所以没有设置这个文件的路径的话,在程序链接的时候会找不到对应的MyDLL.dll中定义的函数。
这时不用设置MyDLL.dll文件,你也可以把这个dll文件拷到和T_MyDLLDlg.cpp文件同一个路径下。
设计的界面如下。
代码如下
效果就是实现一个简单的加法动作。
编译完生成一个exe文件
这时候把.exe文件和.dll文件拷到一个文件夹里,就可以执行了。如果不放.dll文件的话单执行exe的话是不能执行的。
动态链接的例子,用到上面的3个函数就可以了,这里不过多介绍。
动态调用要知道DLL的文件路径,并且知道接口函数的类型及参数,并不需要依赖到.h文件、lib文件等内容。
但是,要动态调用DLL里面的函数的话,那么在该函数的前面必须要添加一个extern "C"(声明为C编译、连接方式的外部函数),不然动态调用会找不到这个函数地址的。
如下,在MyDLL.h中添加下列代码:
extern "C" MYDLL_API int Add(int a, int b);
在MyDLL.cpp中添加下列代码:
MYDLL_API int Add(int a, int b)
{
return a + b;
}
创建一个MFC对话框应用程序,然后在初始化函数中加入下列代码:
{
CDialog::OnInitDialog();
// ....省略其他代码
//加载MyDLL.dll
HINSTANCE hDllInst = AfxLoadLibrary(_T("MyDLL.dll"));
//判断是否加载成功
if(hDllInst)
{
//根据DLL里面的封装函数来定义函数指定
typedef int(*pAdd)(int ,int);
pAdd myAdd;
//根据函数名来从已加载的DLL中获取函数地址并赋值
myAdd = (pAdd)GetProcAddress(hDllInst, "Add");
//判断是否获取成功
if(myAdd != NULL)
{
//调用该函数
int a = myAdd(10, 20);
CString str;
str.Format(_T("%d"), a);
TRACE(str);
}
//释放加载的DLL
AfxFreeLibrary(hDllInst);
}
return TRUE;
}