项目要求
- 创建DLL
- 第一步:
- 第二步:
- 第三步:
- 第四步:
- 第五步:
- C++ 调用例子
- 易语言调用例子:
1.用VS2017编写一个C++动态链接库(DLL)供第三方调用
2.开发环境VS2017
创建DLL
第一步:
首先我们VS界面左上角:文件(F)-》新建项目-》visual C++ -》windows 桌面 -》动态链接库(DLL)
得到以下界面
第二步:
我们新建一个头文件:<Dll1.h>
第三步:
我们在Dll1.cpp里面编写代码
1.需要用到头文件<Dll1.h>和 <Windows.h>
2.在函数前面 返回值类型 后面加上 “__stdcall” 也就是宏“WINAPI”
- 微软解释:
调用约定用于调用Win32 API函数。被调用者会清除堆栈,因此编译器会生成vararg函数。使用这种调用约定的函数需要一个函数原型。下面的列表显示了这个调用约定的实现。 - 符合国人的解释:
几乎我们写的每一个WINDOWS API函数都是__stdcall类型的。
首先,需要了解两者之间的区别: WINDOWS的函数调用时需要用到栈(STACK,一种先入后出的存储结构)。
当函数调用完成后,栈需要清除,【这里就是问题的关键】,如何清除??
如果我们的函数使用了_cdecl,那么栈的清除工作是由调用者,用COM的术语来讲就是客户来完成的。这样带来了一个棘手的问题,不同的编译器产生栈的方式不尽相同,那么调用者能否正常的完成清除工作呢?答案是不能。 如果使用__stdcall,上面的问题就解决了,函数自己解决清除工作。所以,在跨(开发)平台的调用中,我们都使用__stdcall(虽然有时是以WINAPI的样子出现)。那么为什么还需要_cdecl呢?当我们遇到这样的函数如fprintf()它的参数是可变的,不定长的,被调用者事先无法知道参数的长度,事后的清除工作也无法正常的进行,因此,这种情况我们只能使用_cdecl。到这里我们有一个结论,如果你的程序中没有涉及可变参数,最好使用__stdcall关键字。
// Dll1.cpp : 定义 DLL 应用程序的导出函数。
//
#include "Dll1.h"
#include "stdafx.h"
#include <Windows.h>
//两数相加
int WINAPI add(int n1, int n2)
{
return n1 + n2;
}
第四步:
我们在 <Dll1.h> 头文件里:声明函数
#pragma once
int WINAPI add(int n1, int n2);
第五步:
导出函数:有 3种 导出方法,头文件和cpp都要改
1.第一种:在函数返回值类型前面加上 __declspec(dllexport)
__declspec(dllexport) int WINAPI add(int n1, int n2);
得到效果如图:
由于C++支持函数重载,国人称之为 “名称粉碎机制” 所以导出函数名会改变,第三方调用的时候需带如图名称,显然不是很适合咱
2.第二种:在函数返回值类型前面加上 extern “C” __declspec(dllexport) 指定该函数是个C语言的函数
extern "C" __declspec(dllexport) int WINAPI add(int n1, int n2);
得到效果如图:
第三方调用的时候需带如图名称了,显然也不是很合适
3.第三种:新建 “def” 文件实现函数导出
在 “Dll1.def ” 写入代码:
LIBRARY
EXPORTS
add
.def 文件中的第一条 LIBRARY 语句不是必须的,但LIBRARY 语句后面的 DLL 的名称必须正确,即与生成的动态链接库的名称必须匹配。此语句将 .def 文件标识为属于 DLL。链接器将此名称放到 DLL 的导入库中。
EXPORTS语句列出名称,可能的话还会列出 DLL 导出函数的序号值。通过在函数名的后面加上 @ 符和一个数字,给函数分配序号值。当指定序号值时,序号值的范围必须是从 1 到 N,其中 N 是 DLL 导出函数的个数。
得到效果如图:
最后编写好如下3个文件及内容:
此时我们就可以编译代码给第三方程序调用了;
C++ 调用例子
1.将DLL放到运行目录
// TEST.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include <iostream>
#include <Windows.h>
//定义一个函数指针返回值和参数必须和原函数一致,声明该函数是 WINAPI 自己释放栈的
typedef int(WINAPI *ADD)(int n1, int n2);
int main()
{
//LoadLibrary函数将指定的可执行模块映射到调用进程的地址空间。L表示宽字符的DLL名称
HMODULE hDll = LoadLibrary(L"Dll1.dll");
if (!hDll)
{
std::cout << "error LoadLibrary";
return -1;
}
//GetProcAddress函数从指定的动态链接库(DLL)中检索导出的函数或变量的地址。
ADD add = (ADD)GetProcAddress(hDll,"add");
//调用并输出返回值
std::cout << add(6,9);
//释放句柄
FreeLibrary(hDll);
}
易语言调用例子:
1.将DLL放到运行目录
.版本 2
.DLL命令 add, 整数型, "Dll1.dll", "add"
.参数 a, 整数型
.参数 b, 整数型
调试输出 (add (3, 4))