一.Visual Studio 编写C++动态链接库(2017版本);
-
创建动态链接库项目
单击左上角“文件”菜单,选择“新建”->“项目”->“动态链接库(DLL);
配置项目名称及解决方案名称后,点击“确定”。
成功创建项目。
-
生成动态链接库
添加头文件“rovdll.h”和源文件“rovdll.cpp”。
头文件中声明一个导出函数。
#pragma once
#include <cstdio>
#include <cstdint>
#ifdef BUILD_MYDLL //判断宏有没有定义,定义了则执行后面的语句,没有定义则报错
#define API_SYMBOL __declspec(dllexport) //声明导出函数,类,对象供外面调用,
#else
#define API_SYMBOL __declspec(dllimport) //从别的动态库中导入函数,类,对象
#endif
#define PidParamFileName "pidParam.json"
//导出函数,由于是c的接口所以得加extern "C"
extern "C" API_SYMBOL int convert_arry(int* data, int len);
按住“Alt”+“Enter”,单击函数名“convert_arry”,创建函数定义如下:
//为使dllexport有效,需在头文件前定义该宏
#define BUILD_MYDLL
#include "pch.h"
#include "rovdll.h"
#include <fstream>
#include <iostream>
using namespace std;
int convert_arry(int * data, int len)
{
for (int i = 0; i < len; i++)
{
cout << data[i] << ",";
}
cout << endl;
int nTotal = 0;
for (int i = 0; i < len; i++)
{
nTotal += data[i];
--data[i];
}
return nTotal;
}
编译项目,右键“项目”点击“生成” 。
生成成功。
项目目录下生成对应文件。
二.创建应用程序隐式调用动态库;
右键单击“解决方案”->“添加”->“新建项目”->“控制台应用”
隐式调用需要三个文件:“.lib”、“.dll”、“.h”文件。
1.先加入头文件,会发现include下面出现红色波浪号,提示“无法打开 源文件”。
此时,需要为它提供一个搜索路径。
右键点击“项目DemoInvokeDll”->"属性"。
在“C/C++”、“常规”中附加“附加包含目录”。
添加DemoDll的文件路径。
点击“应用”、“确定”后,波浪线消失。
2. 引入lib符号文件
右键点击“项目DemoInvokeDll”->“属性”->“链接器”->“输入”;
添加“附加依赖项”->“编辑”;
输入“DemoDll.lib”->“确定”“应用”“”。
再给lib添加一个搜索路径:“链接器”->“常规” ->“附加库目录”->“编辑”
加入链接库所在的目录,“确定”->“应用”->“确定”。
3.在“DemoInvokeDll”中,编写调用代码。
#include <iostream>
#include "rovdll.h"
int main()
{
int arr[] = { 3,6,7,8 };
int nLen = sizeof(arr) / sizeof(int);
int nTotal = convert_arry(arr, nLen);
std::cout << "数组元素求和结果:" << nTotal << std::endl;
std::cout << "数组元素新值:" << std::endl;
for (int i = 0; i < nLen; i++) {
std::cout << arr[i] << ",";
}
}
4.运行程序
将“DemoInvokeDll”设为启动项目。
运行项目,得到调试结果。结果正确,按空格退出。
三.创建应用程序显式调用动态库;
显式调用只需要一个文件:“.dll”的动态库文件。
右键单击“解决方案”->“添加”->“新建项目”->“控制台应用”
在生成的“xsInvokeDll.cpp”中,按下列五步:
第一步,创建一个别名,它引用的函数类型需要和dll中导出函数一致;
第二步,加载DemoDll.dll 动态库文件;
第三步,获取dll中的函数地址;
第四步,调用函数;
第五步,释放函数句柄。
代码如下:
#include <iostream>
#include <Windows.h>
//第一步,创建一个别名,它引用的函数类型需要和dll中导出函数一致。
typedef int(*LPDO_ARRAY)(int* pArr, int nlen);
int main()
{
HINSTANCE hDll; //定义一个实例句柄,引用动态库,需引入windows头文件
LPDO_ARRAY IpDo_array; //定义一个函数指针,用来指向导出函数
//第二步,加载DemoDll.dll 动态库文件
hDll = LoadLibrary(L"DemoDll.dll");
if (hDll != NULL) {
//第三步,获取dll中的函数地址
IpDo_array = (LPDO_ARRAY)GetProcAddress(hDll, "convert_arry");
if (IpDo_array != NULL) {
//第四步,调用函数
int nArr[] = { 1,2,3,5 };
int nlen = sizeof(nArr) / sizeof(int);
int nRet;
//通过函数指针IpDo_array调用函数
nRet = IpDo_array(nArr, nlen);
std::cout << "返回值:" << nRet << std::endl;
std::cout << "数组新值:" ;
for (int i = 0; i < nlen; ++i) {
std::cout << nArr[i] << ",";
}
}
//第五步,释放动态库句柄
FreeLibrary(hDll);
}
}
调用方法1:将“DemoInvokeDll”设为启动项目,运行程序,得到结果。
调用方法2:
将“xsInvokeDll.exe”“DemoDll.dll”文件复制到D盘。
用命令行方式执行 “xsInvokeDll.exe”,得到结果。
四.动态库和静态库的区别。
静态库:当“应用程序代码”在编译的时候,把“静态库代码”一起打包为一个“可执行程序”。对于不同的可执行程序,无论是储存到硬盘,还是运行时加载到内存,它们都拥有各自静态库代码的拷贝。
动态库:在编译的时候只有少量的符号信息,会打包到可执行程序里,主要的程序代码会被独立编译成一个dll文件,同时,多个应用程序可以共享这个dll文件。并且,动态库可以实现运行时的动态加载,还可以被第三方程序调用。
参考:
Visual Studio 2019-编写C++动态链接库(老蓝的程序实践)