使用visual studio 2019创建与使用静态库与动态库

在cpp文件加入#include<WinSock2.h>后编译错误,显示cin,cout,endl以及自己定义的一些变量都显示未声名的标识符,还有下面一些错误
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.17763.0\um\WinSock2.h(2606,1): error C2375: “WSAAsyncGetHostByName”: 重定义;不同的链接,原因未知。
加入#include<WinSock2.h>是想实现socket通信的功能,但是编译不成功。
解决方法:将socket通信的功能放到静态库或者动态库中去实现
注意:静态库的头文件(假设为静态库.h)中不要写#include<WinSock2.h>,#include<WinSock2.h>要写在静态库的源文件(假设为静态库.cpp)中,否则会报同样的错误,因为你在源程序(假设为源.app)中写 #include“标头.h”,相当与把静态库.h中的#include<>都写在了这里。

下面是静态库和动态库的介绍和使用

一.静态库和动态库的区别

exe程序开始运行时会把静态库整合进来,而动态库则是程序需要时才调用。lib就是静态库文件,dll就是动态库文件。
lib可以实现程序不同的功能。
dll可以实现程序不同的功能,程序升级时替换或添加dll,就可以实现功能的升级或添加。

二.静态库的使用(visual studio 2019)

1.打开visual studio 2019,选择创建新项目
在这里插入图片描述2.选择静态库,下一步。然后给你的项目命名,创建就行了。
在这里插入图片描述3.静态库代码的编写。这样创建静态库,visual studio 2019会帮你建立两个头文件framework.h和pch.h和两个源文件pch.cpp和StaticLib1.cpp(根据你项目名来取名),将你的代码写到相应的地方就行了。
注意:将函数声明写在头文件中,比如这里的pch.h。这样在需要使用的静态库的源文件中通过#include“…/pch.h”(绝对路径或相对路径),就可以将函数声明包含进来。否则编译器无法识别你在静态库中写的函数
在这里插入图片描述4.静态库的生成。写好代码后,右键项目-生成,编译器会在项目目录下生成debug文件夹,其包含以下文件。
在这里插入图片描述**5.静态库的使用。**新建一个项目,比方说一个控制台程序。首先通过#include把静态库的头文件加进来(注意路径)。然后右键项目-属性-配置属性-链接器-常规-附加库目录。把你lib文件所在的目录加进来,可以相对路径也可以绝对路径。然后通过#pragma comment(lib, “你的静态库名.lib”),把静态库加进来,就可以使用静态库的函数了。

3.动态库的使用

1.动态库项目的创建。选择动态连接库(DLL)。
在这里插入图片描述2.DLL应用程序的入口。
在这里插入图片描述在编译器帮你创建的项目中,有一个dllmain.cpp文件,它定义DLL应用程序的入口,如图。
APIENTRY被定义为__stdcall,它意味着这个函数以标准Pascal的方式进行调用,也就是WINAPI方式;
函数参数hModule是进程中的每个DLL模块被全局唯一的32字节的HINSTANCE句柄标识(句柄代表了DLL模块在进程虚拟空间中的起始地址,只有在特定的进程内部有效);
参数ul_reason_for_call指明了被调用的原因。共有4种,即PROCESS_ATTACH、PROCESS_DETACH、THREAD_ATTACH和THREAD_DETACH,以switch语句列出;
lpReserved 表示一个保留参数,目前已经很少使用。

一个程序要调用Dll里的函数,首先要先把DLL文件映射到进程的地址空间。要把一个DLL文件映射到进程的地址空间,有两种方法:静态链接和动态链接的LoadLibrary或者LoadLibraryEx。
当一个DLL文件被映射到进程的地址空间时,系统调用该DLL的DllMain函数,传递的ul_reason_for_call参数为DLL_PROCESS_ATTACH。
其它调用方式会传递不同的ul_reason_for_call参数。如: 当DLL被从进程的地址空间解除映射时,系统调用了它的DllMain,传递的ul_reason_for_call值是DLL_PROCESS_DETACH。单个线程启动,用值DLL_THREAD_ATTACH调用DLL的DllMain函数。单个线程终止,用DLL_THREAD_DETACH来调用DllMain函数。
3.动态库代码的编写。
在动态库中写一个简单的函数function()。头文件中定义这个函数,源文件实现函数。右键项目属性-生成。编译器在项目目录下生成debug文件夹,其中有dll文件。
在这里插入图片描述
在这里插入图片描述4.动态链接库的使用(隐式调用)
先介绍一个工具,Dependency Walker。它 是 Microsoft Visual C++ 中提供的非常有用的 PE 模块依赖性分析工具。主要功能如下:
查看 PE 模块的导入模块。
查看 PE 模块的导入和导出函数。
动态剖析 PE 模块的模块依赖性。
解析 C++ 函数名称。
就是可以分析程序使用了那些dll,以及查看程序或者dll中的函数。
用Dependency Walker打开你创建的dll文件,发现没有函数,原因是没有导出函数。
在这里插入图片描述用__declspec(dllexport)声明导出函数。重新生成一下。可以看到生成了一个lib文件和一个exp文件
在这里插入图片描述在这里插入图片描述
重新用Dependency Walker打开你创建的dll文件,在其中会发现你导出的函数,当然编译器给它改名了。(这是C语言和C++不同的语法导 致的。下面介绍不改函数名的方法,因为调用函数要用函数名,这个函数名不方便)
在这里插入图片描述在需要使用动态链接库的程序的app文件中声明导入函数(即动态链接库导出函数的一部分或全部)。
首先在你的app文件中包含进动态链接库的头文件。#include"…/Dll1/pch.h"
在这里插入图片描述
通过条件编译指令声明导入导出函数。修改一下dll的头文件。
在这里插入图片描述
右键dll项目属性-C/C+±预处理器-预处理器定义,将_DLLAPI预定义。
在这里插入图片描述这样在dll中DLLAPI代表的是__declspec(dllexport),而在调用dll的app文件中DLLAPI代表的是__declspec(dllimport)。因为在调用dll的app文件中你没有预定义_DLLAPI。
在你调用dll的程序中,右键项目属性-配置属性-链接器-常规-附加库目录,添加dll项目生成的lib文件的目录。调用dll的app文件中导入dll的lib文件。通过语句#pragma comment(lib,“DLL1.lib”)
在这里插入图片描述将你的dll放到调用dll的exe程序所在的目录下,运行调用dll的程序。
在这里插入图片描述5.动态链接库的使用(显示调用)
动态加载动态库,不要语句#pragma comment(lib,“DLL1.lib”),调用dll的app文件代码如下图:
在这里插入图片描述注意其中的函数名字,?function@@YAXXZ,为我们在Dependency Walker中看到的函数名字
在这里插入图片描述 开始执行调用dll的app。
在这里插入图片描述6.如何让导出的函数不改名
第一种方式
编译器编译cpp文件时使用的是C++语法,会更改导出的动态库函数的名称,如上将function改成了?function@@YAXXZ。
编译器编译c文件时使用的是C语言语法,不会更改导出的动态库函数的名称。
如果是要编译cpp,又要在编译时使导出函数不改名,可以在函数声明前加上 extern “C”,告诉编译器这里使用C语言的编译方式去编译。
在这里插入图片描述在这里插入图片描述这里函数就没有改名。
第二种方式
使用模块定义文件。右键dll项目下的源文件-添加-新建项,选择模块定义文件(.def)。新建一个def文件。
在这里插入图片描述将你要导出的dll模块名称,dll模块中的函数名称写在这个文件中。
在这里插入图片描述注意:有了这个def文件后就不需要在dll的头文件中导出函数了。
在这里插入图片描述
重新生成dll项目。在Dependency Walker查看一下导出的函数名。
在这里插入图片描述

发布了3 篇原创文章 · 获赞 0 · 访问量 234

猜你喜欢

转载自blog.csdn.net/weixin_34007256/article/details/105346608