99体育 C ++动态链接库,静态链接库

日期:2021-02-18 21:05:36 浏览量: 153

一、概述

1、动态库和静态库之间的异同

动态可链接库(DLL)提供一些可以直接使用的变量,类和函数。在经历了“无库-静态链接库-动态链接库”过程之后,dll被广泛使用。

静态链接库和动态链接库是共享代码。

如果使用静态链接库(.lib),则lib中的指令最终将被编译到链接该静态库的exe(或dll)文件中。发行软件时,仅需要发行exe(或dll)文件。 .lib文件是必需的。但是,如果您使用动态链接库(.dll),则dll中的指令将不会编译到exe文件中,但是在exe文件执行期间,动态加载和卸载了独立的dll文件凤凰彩票 ,这需要与exe文件一起发布。

静态链接库和动态链接库之间的另一个区别是,静态链接库不再可以包含其他动态链接库或静态链接库,但是动态链接库不受此限制,并且动态链接库可以包括其他动态链接库。链接库。还有静态链接库。

2、相关常识

(1)只要遵循dll接口规范和调用方法,以各种语言编写的dll都可以相互调用。

(2) dll具有三种分类,非MFC DLL(非MFC DLL),MFC常规DLL(MFC常规DLL)和MFC扩展DLL(MFC扩展DLL)。非MFC DLL不使用MFC库rules,其导出的函数是标准C接口,可由非MFC或MFC编写的应用程序调用; MFC规则DLL包含CWinApp类,但没有消息循环; MFC扩展DLL由MFC创建动态链接库版本,仅适用于使用MFC库编写的应用程序。

二、静态库

静态链接库的后缀是.lib。下面的示例将介绍如何生成.lib文件以及如何调用.Lib

1、生成.lib文件

1),创建一个空解决方案,解决方案名称为StaticLibrary。

c++ 动态链接库

2)c++ 动态链接库,添加一个win32项目,类型为静态库,项目名称为StaticLib,项目为空。

选择win32项目项目类型

c++ 动态链接库

选择静态库并取消预编译头文件

c++ 动态链接库

3),将lib.h和Lib.cpp文件添加到项目中YOBET体育 ,两个文件代码如下图所示

c++ 动态链接库

lib.h文件代码如下:

#ifndef __LIB_H__
#define __LIB_H__
int add(int a,int b);
#endif

lib.cpp文件提供了一个将两个整数相加并返回两个数字之和的函数。代码如下:

#include "lib.h"
int add(int a,int b)
{
    return a+b;
}

4),该库项目无法单独运行,您需要右键单击以生成它

c++ 动态链接库

成功生成后,将在解决方案目录的调试文件夹(即静态库文件)中生成StaticLib.lib文件。

2、如何调用.lib文件

1),在解决方案中添加另一个项目,项目名称为StaticLibCall,类型为Win32,在控制台程序中选择空项目。

c++ 动态链接库

选择控制台应用程序并清空项目

2),将main.cpp文件添加到项目源文件中

#include 
#include "lib.h"
#pragma comment (lib,"StaticLib.lib")//指定与静态库一起连接
int main()
{
    printf("2+3=%d",add(2,3));
}

3)。将刚刚生成的两个文件StaticLib.lib和lib.h复制到项目目录。 (通常,使用静态库时必须提供这两个文件。.h文件提供预定义的功能,.lib文件提供该功能的实现)

4)。生成的exe文件是可以独立运行的正在运行的程序。 lib文件中的函数实现链接到exe文件,并且不再需要lib。结果如下

c++ 动态链接库

三、动态链接库

仅介绍创建和调用DLL(非MFC DLL)的方法。该Dll实现的功能与第2节介绍的静态库实现的功能相同。

1、如何生成dll文件

1),创建dll项目的步骤与上述创建lib的步骤相同,选择类型时只需要选择dll。构建项目后,添加DLib.h和DLib.cpp文件

c++ 动态链接库

2),DLib.h和DLib.cpp代码如下:

DLib.h文件

1 #ifndef __DLIB_H__
2 #define __DLIB_H__
3 
4 extern "C" int __declspec(dllexport) add(int a,int b); 
5 
6 
7 #endif

DLib.cpp文件

1 #include "Dlib.h"
2 
3 int add(int a,int b)
4 {
5     return a+b;
6 }

分析代码。该项目的.cpp文件中的代码与.cpp第2节中的代码完全相同。.h文件不同。在项目的.h文件中的add函数中添加extern“ C”告诉编译器该函数是使用C调用方法编译的,而__declspec(impoet)是将函数add声明为dll的导出函数, DLL函数分为两种类型:

(a)DLL导出功能,可以由调用dll的应用程序调用

(b)DLL内部函数只能在DLL程序中使用,并且不能调用调用DLL的应用程序

3),右键单击生成;成功生成后,将在debug文件夹中生成DynamicLib.dll文件。同时,将在此路径下生成DynamicLib.lib文件,该文件与第一节不同。静态库文件,此lib文件只是dll文件中导出函数的声明和定位信息,并且不包含该函数的实现(并且第一部分中的静态库文件包含该函数的实现),因此此lib文件仅位于。它在编译调用相应dll库的项目时使用,不需要与exe一起发布。

2、如何调用.dll文件

调用dll文件有两种方法,一种是动态调用,另一种是静态调用。

动态调用意味着程序员调用系统API函数来加载和卸载dll。程序员可以决定何时加载dll文件,何时卸载,加载哪个dll文件以及完全有权使用dll文件。

静态调用由编译系统完成,以加载dll文件并在应用程序末尾卸载dll。当调用某个dll的应用程序结束时,Windows系统将dll的应用程序记录减少1,直到被使用为止。该dll的所有应用程序都结束了,即dll应用程序记录为0,并且操作系统将卸载dll。静态调用方法很简单,但不如动态调用适用。

([1),动态调用dll

1),创建一个新的控制台项目,添加main.cpp文件,将刚生成的dynamicLib.dll文件复制到项目目录中,main.cpp代码如下

// Test0629.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "windows.h"

typedef int (*lpAddFun)();//宏定义函数指针类型

int main()
{
    HINSTANCE hDll;//DLL 句柄
    lpAddFun Fun;//函数指针
    hDll = LoadLibrary("MsgDLL0629.dll");//动态获取dll文件的路径
    if (hDll!=NULL)
    {
        Fun =(lpAddFun)GetProcAddress(hDll,"Destroy");//根据函数名在dll文件中获取该函数的地址
        if (Fun!=NULL)
        {
            Fun();       
        }        
        FreeLibrary(hDll);
    }
    return 0;
}

Main.cpp代码分析:

语句typedef int(* lpAddFun)(int,int)定义了具有与add函数相同的参数类型和返回值的函数指针类型,然后在主函数中定义了lpAddFun addFun的实例;

在函数main中定义了DLL HISTANCE句柄实例hDll,并通过Win32API函数LoadLibrary动态加载DLL模块,并将DLL模块句柄分配给hDll;

在主函数中,通过Win32API函数GetProcAddress获取加载的DLL模块中的add函数的地址,并将其分配给addFun亚博电竞 ,然后通过函数指针addFun调用DLL中的add函数;

完成对dll的调用后,通过主函数中的Win32API函数FreeLibrary释放已加载的DLL模块。

通过以上分析,我们可以看到:

(a)动态调用仅需要dll文件,而不需要相应的.h头文件和.lib文件。通常,只要有一个dll,就可以在此dll中调用导出的函数。

(b)在dll中调用函数时,您需要知道导出函数的函数签名。如果您具有与dll对应的头文件,则可以引用该头文件。如果没有头文件,则可以使用特定的工具来获取dll。导出函数的函数签名

(c)DLL需要以特定方式声明导出的函数(或变量,类)

(d)应用程序需要以特定方式调用DLL淡出功能(或变量,类)

([2),静态调用dll

1)c++ 动态链接库,创建一个新项目,将其命名为DllStaticCall,添加main.cpp文件,并将刚刚生成的两个文件DynamicLib.dll和DynamicLib.lib复制到项目目录。

main.cpp代码如下

#include "stdafx.h"
 //.lib文件中仅仅是关于其对应DLL文件中函数的定位信息
#pragma comment(lib,".lib文件路径")
extern "C" __declspec(dllimport) 函数名(参数);
int main()
{
     正常调用方法
     return 0;
}

从上面的代码可以看出,静态调用需要完成两个操作:

a)#pragma comment(lib,“ DynamicLib.lib”)告诉编译器与dll相对应的.lib文件的路径和文件名。

生成dll文件时,链接器将自动为其生成相应的.lib文件,其中包含dll导出函数的符号名称和序列号(没有实际代码)。在应用程序中,.lib文件将用作dll替换文件来参与编译。编译完成后,不需要.lib文件。

b)extern“ C” int __declspec(dllimport)add(int a,int b)声明导入函数,该函数必须与dll项目.h文件中的函数声明一致。

c)dll项目中的头文件可以包含在项目中,因此上面的代码不需要编写extern“ C” int __declspec(dllimport)add(int a,int b)语句,但是如果未提供相应的头文件在本文中仅以这种方式声明函数。

静态调用不需要使用Win32API函数来加载和卸载Dll并获取Dll中导出的函数的地址。这是因为通过静态链接编译和生成程序时,编译器将使用.lib文件中导出的函数的功能符号链接到生成的exe文件,即.lib中包含的相应dll文件的文件名。文件也会被编译并存储在exe文件中。当需要在应用程序运行期间加载dll文件时,Windows会根据此信息找到该文件并加载dll,然后通过符号名称实现对dll函数的动态链接,以便exe可以直接调用通过函数名称输出dll的输出函数,就像在程序中调用其他函数一样。

动态链接库的四、 def文件

dll导出函数是在前面添加__declspec(impoet)语句,以将该函数声明为dll的导出函数。还有另一种通过def文件将函数声明为导出函数的方法

1、如何使用def文件

([1)创建一个新的解决方案,添加两个项目钱柜体育 ,DllLib是生成dll文件的项目,Dllcall是调用dll的项目。

([2)在dllLib中添加两个文件lib.cpp和dlllib.def(不带.h头文件),代码如下

Lib.cpp代码:声明两个函数,加法和减法

int __stdcall Add(int numa,int numb)

{

返回(数字+麻木);

}

int __stdcall Sub(int numa,int numb)

{

返回(numa-numb);

}

dllLib.def的代码如下:

LIBRARY DllLib
EXPORTS
Add @ 1
Sub @ 2

.def文件的规则是:

([1) LIBRARY语句指示相对于.def文件的dll(不需要后缀dll)

(2) EXPORTS语句列出了要在任何地方使用的函数名称,您可以在.def文件中的导出函数名称后添加@n,这意味着导出函数的编号为n。函数调用,您可以使用此号码来调用函数(请参见下面的调用过程)

(3) .def文件中的注释在每行的开头用分号(;)指定,并且注释不能与该语句所在的行。

由此可见,示例中的.def文件的含义是生成一个名为DllLib的动态链接库,导出其中的add和sub函数,并将add函数号指定为1,然后将子编号为2。

2、调用dll

调用方法与上述方法相同,此示例使用动态调用。将刚刚生成的dll复制到项目目录。

main.cpp代码如下:

 1 #include 
 2 #include 
 3 
 4 typedef int (__stdcall *FUN)(int, int);
 5 HINSTANCE hInstance;
 6 
 7 FUN   fun;
 8 
 9 int main()
10 {
11     hInstance = LoadLibrary(L"DllLib.dll");
12     if(hInstance!=NULL)
13     {
14         fun = (FUN)GetProcAddress(hInstance, "Add");
15         //当在Def文件中指定函数序号时,可以通过序号导出,否则只能通过函数名称导出
16         //fun = (FUN)GetProcAddress(hInstance, MAKEINTRESOURCE(1));
17         if (fun!=NULL)
18         {
19                 printf("1+2=%d",fun(1, 2));
20         }
21     }
22     FreeLibrary(hInstance);
23     return 0;
24 }

注意:功能编号在def文件中定义。动态加载dll时,可以根据此数字加载函数。这样做的好处是,当dll项目的导出函数的函数名称发生更改万狗体育 ,但是该函数没有更改时,只要def中定义的函数号没有更改,调用dll的代码就不需要被改变。

五、 DllMain函数

Windows加载dll时,它需要一个入口函数,就像控制台需要main函数,而win32程序则需要WinMain函数。在前面的示例中,该dll不提供DllMain函数,并且该应用程序可以成功调用该dll。这是因为,当Windows无法找到DllMain函数时,系统将引用默认的DllMain函数版本,而其他运行时库则不执行任何操作。这并不意味着dll可以放弃DllMain函数。

根据编写规范,Windows必须在dll中找到并执行DllMain函数,作为加载dll的基础,这使dll可以驻留在内存中。 DllMain函数不是导出函数,而是dll的内部函数,这意味着DllMain函数不能在应用程序中直接引用,DllMain函数会自动调用。 DLLMain函数的功能是执行一些初始化操作,等效于该类的构造函数。关于DLLMain函数和派生类,在这里我不会做太多解释,以后再做进一步研究。

1、将DllMain函数添加到dll

([1)创建一个新的解决方案并添加两个项目。一个是生成dll,另一个是调用dll。

c++ 动态链接库

Lib.h代码

1 #ifndef __LIB_H__
2 #define __LIB_H__
3     extern "C" int __declspec(dllexport) add(int a,int b); 
4 #endif

Lib.cpp代码

 1 #include "lib.h"
 2 #include "stdio.h"
 3 #include "windows.h"
 4 
 5 BOOL APIENTRY DllMain(HANDLE hModule,
 6     DWORD ul_reason_for_call,
 7     LPVOID lpReserved)
 8 {
 9     switch (ul_reason_for_call)
10     {
11     case DLL_PROCESS_ATTACH:
12         printf("\nprocess attach of dll");
13         break;
14     case DLL_THREAD_ATTACH:
15         printf("\nthread attach of dll");
16         break;
17     case DLL_THREAD_DETACH:
18         printf("\nprocess detach of dll");
19         break;
20     case DLL_PROCESS_DETACH:
21         printf("\nprocess detach of dll");
22         break;
23     }
24     return TRUE;
25 }
26 
27 int add(int a,int b)
28 {
29     return a+b;
30 }

Main.cpp代码

 1 #include "stdio.h"
 2 #include "windows.h"
 3 
 4 typedef int (*lpAddFun)(int ,int );//宏定义函数指针类型
 5 
 6 int main()
 7 {
 8     HINSTANCE hDll;//DLL 句柄
 9     lpAddFun addFun;//函数指针
10     hDll = LoadLibrary(L"DllLib.dll");//动态获取dll文件的路径
11     if (hDll!=NULL)
12     {
13         addFun =(lpAddFun)GetProcAddress(hDll,"add");
14         if (addFun!=NULL)
15         {
16             int result =addFun(2,3);
17             printf("\ncall add in dll %d",result);
18         }
19 
20         FreeLibrary(hDll);
21     }
22     return 0;
23 }

六、导出类

point.h

 1 #ifndef __POINT_H__
 2 #define __POINT_H__
 3 
 4 #ifdef DLL_FILE
 5 class _declspec (dllexport) point //导出类point
 6 #else 
 7 class _declspec(dllimport) point //导入类point
 8 #endif
 9 {
10 public: 
11     float y;
12     float x;
13     point();
14     point(float x_coordinate,float y_coordinate);
15 };
16 
17 #endif

point.cpp

 1 #ifndef DLL_FILE
 2 #define DLL_FILE
 3 #endif
 4 #include"point.h"
 5 //类point的缺省构造函数
 6 point::point()
 7       {
 8           x=0.0;
 9           y=0.0;
10       }
11 point::point(float x_coordinate,float y_coordinate)
12       {
13           x=x_coordinate ;
14           y=y_coordinate;
15       }

circle.h

 1 #ifndef __CIRCLE_H__
 2 #define __CIRCLE_H__
 3 #include "point.h"
 4 #ifdef DLL_FILE
 5 class _declspec (dllexport) circle //导出类circle
 6 #else 
 7 class _declspec(dllimport) circle //导入类circle
 8 #endif
 9 {
10 public: 
11     void SetCenter(const point crePoint);
12     void SetRadius(float r);
13     float GetGirth();
14     float GetArea();
15     circle();
16 private:
17     float radius;
18     point center;
19 };
20 
21 #endif

circle.cpp

 1 #ifndef DLL_FILE
 2 #define DLL_FILE
 3 #endif
 4 #include"circle.h"
 5 #define  PI 3.1415926
 6 //类circle的缺省构造函数
 7 circle::circle()
 8 {
 9     center =point(0,0);
10     radius =0;
11 }
12 //得到圆的面积
13 float circle::GetArea()
14 {
15     return PI*radius*radius;
16 }
17 //得到圆从周长
18 float circle::GetGirth()
19 {
20     return 2*PI*radius;
21 }
22 //设置圆心坐标
23 void circle::SetCenter(const point crePoint)
24 {
25     center =crePoint;
26 }
27 //设置圆的半径
28 void circle::SetRadius(float r)
29 {
30     radius     =r;
31 }

将circle.h,point.h和生成的.lib文件放在同一级别目录中

main.cpp

 1 #include "circle.h"
 2 #include "stdio.h"
 3 
 4 //.lib文件中仅仅是关于其对应DLL文件中函数的定位信息
 5 #pragma comment(lib,"DllExportClass.lib")
 6 
 7 int main()
 8 {
 9     circle c;
10     point p(2.0,2.0);
11     c.SetCenter(p);
12     c.SetRadius(1.0);
13     printf("area:%f,girth:%f",c.GetArea(),c.GetGirth());
14     scanf("%d");
15     return 0;
16 }

结果是

c++ 动态链接库