dll动态库调用约定
1、动态链接库英文为DLL,是Dynamic Link Library 的缩写形式,DLL是一个包含可由多个程序同时使用的代码和数据的库,DLL不是可执行文件。动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL 还有助于共享数据和资源。多个应用程序可同时访问内存中单个DLL 副本的内容。DLL 是一个包含可由多个程序同时使用的代码和数据的库。
2、操作实例,C语言咧调用系统的kernel32.dll中的GlobalMemoryStatusEx函数
typedef void(WINAPI* FunctionGlobalMemoryStatusEx)(LPMEMORYSTATUS);//声明函数指针模型
HMODULE hModule;//Dll句柄
FunctionGlobalMemoryStatusEx GlobalMemoryStatusEx;//函数指针模型声明函数变量
MEMORYSTATUS status;
status.dwLength = sizeof(status);
//GlobalMemoryStatus(&status);
hModule = LoadLibrary("kernel32.dll");//调试时hModule为0x10000000,载入动态链接库dll,返回它的句柄
if(NULL==hModule)//判断载入是否成功
{
//error.
MessageBox(hwndDlg,TEXT("载入指定的动态链接库dll失败"),TEXT("error"),MB_OK);
return 0;
}
//调用GetProcAddress API根据dll句柄,和dll的声明的函数名获取函数指针
GlobalMemoryStatusEx =(FunctionGlobalMemoryStatusEx)GetProcAddress(hModule,"GlobalMemoryStatusEx");
if(NULL==GlobalMemoryStatusEx)//判断获取是否成功
{
//error
MessageBox(hwndDlg,TEXT("error2"),TEXT("error2"),MB_OK);
return 0;
}
//获取成功,然后可以直接用函数指针来调用函数,函数名就是函数指针,C语言应该都懂
GlobalMemoryStatusEx(&status);//调用函数
FreeLibrary(hModule);//用完了要释放dll
3、第二步已经说名了怎么动态调用DLL,我们还要注意一点,DLL的调用约定
dll有__cdecl __stdcall WINAPI 等不同的调用约定,也就是参数的压栈顺序等,暂时不用关心,只要保证调用的时候和dll中的调用约定一样就可以。//否则会报错:The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.
如上面的列子,如果我把typedef void(WINAPI* FunctionGlobalMemoryStatusEx)(LPMEMORYSTATUS);//声明函数指针模型,改成:
typedef void(__cdecl* FunctionGlobalMemoryStatusEx)(LPMEMORYSTATUS););//声明函数指针模型
运行时会报错误:
由此,在声明函数原型指针时要注意写对调用约定,如果不知道,那么换着调试看那个对。
4、说明一下调用约定(Calling Convention)相关的(其他地方拷贝来的)
调用约定用来处理决定函数参数传送时入栈和出栈的顺序(由调用者还是被调用者把参数弹出栈),以及编译器用来识别函数名称的名称修饰约定等问题。在Microsoft VC++ 6.0中定义了下面几种调用约定,我们将结合汇编语言来一一分析它们:
4.1、__cdecl
__cdecl是C/C++和MFC程序默认使用的调用约定,也可以在函数声明时加上__cdecl关键字来手工指定。采用__cdecl约定时,函数参数按照从右到左的顺序入栈,并且由调用函数者把参数弹出栈以清理堆栈。因此,实现可变参数的函数只能使用该调用约定。由于每一个使用__cdecl约定的函数都要包含清理堆栈的代码,所以产生的可执行文件大小会比较大。__cdecl可以写成_cdecl。
下面将通过一个具体实例来分析__cdecl约定:
在VC++中新建一个Win32 Console工程,命名为cdecl。其代码如下:
int __cdecl Add(int a, int b); //函数声明
void main()
{
Add(1,2); //函数调用
}
int __cdecl Add(int a, int b) //函数实现
{
return (a + b);
}
函数调用处反汇编代码如下:
;Add(1,2);
push 2 ;参数从右到左入栈,先压入2
push 1 ;压入1
call @ILT+0(Add) (00401005) ;调用函数实现
add esp,8 ;由函数调用清栈
4.2、__stdcall
__stdcall调用约定用于调用Win32 API函数。采用__stdcal约定时,函数参数按照从右到左的顺序入栈,被调用的函数在返回前清理传送参数的栈,函数参数个数固定。由于函数体本身知道传进来的参数个数,因此被调用的函数可以在返回前用一条ret n指令直接清理传递参数的堆栈。__stdcall可以写成_stdcall。
还是那个例子,将__cdecl约定换成__stdcall:
int __stdcall Add(int a, int b)
{
return (a + b);
}
函数调用处反汇编代码:
; Add(1,2);
push 2 ;参数从右到左入栈,先压入2
push 1 ;压入1
call @ILT+10(Add) (0040100f) ;调用函数实现
函数实现部分的反汇编代码:
;int __stdcall Add(int a, int b)
push ebp
mov ebp,esp
sub esp,40h
push ebx
push esi
push edi
lea edi,[ebp-40h]
mov ecx,10h
mov eax,0CCCCCCCCh
rep stos dword ptr [edi]
;return (a + b);
mov eax,dword ptr [ebp+8]
add eax,dword ptr [ebp+0Ch]
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret 8 ;清栈
4.3、__fastcall
__fastcall约定用于对性能要求非常高的场合。__fastcall约定将函数的从左边开始的两个大小不大于4个字节(DWORD)的参数分别放在ECX和EDX寄存器,其余的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的堆栈。__fastcall可以写成_fastcall。
依旧是相类似的例子,此时函数调用约定为__fastcall,函数参数个数增加2个:
int __fastcall Add(int a, double b, int c, int d)
{
return (a + b + c + d);
}
函数调用部分的汇编代码:
;Add(1, 2, 3, 4);
push 4 ;后两个参数从右到左入栈,先压入4
mov edx,3 ;将int类型的3放入edx
push 40000000h ;压入double类型的2
push 0
mov ecx,1 ;将int类型的1放入ecx
call @ILT+0(Add) (00401005) ;调用函数实现
函数实现部分的反汇编代码:
; int __fastcall Add(int a, double b, int c, int d)
push ebp
mov ebp,esp
sub esp,48h
push ebx
push esi
push edi
push ecx
lea edi,[ebp-48h]
mov ecx,12h
mov eax,0CCCCCCCCh
rep stos dword ptr [edi]
pop ecx
mov dword ptr [ebp-8],edx
mov dword ptr [ebp-4],ecx
;return (a + b + c + d);
fild dword ptr [ebp-4]
fadd qword ptr [ebp+8]
fiadd dword ptr [ebp-8]
fiadd dword ptr [ebp+10h]
call __ftol (004011b8)
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret 0Ch ;清栈
关键字__cdecl、__stdcall和__fastcall可以直接加在要输出的函数前,也可以在编译环境的Setting...->C/C++->Code Generation项选择。它们对应的命令行参数分别为/Gd、/Gz和/Gr。缺省状态为/Gd,即__cdecl。当加在输出函数前的关键字与编译环境中的选择不同时,直接加在输出函数前的关键字有效。
1.__stdcall
以“?”标识函数名的开始,后跟函数名; 函数名后面以“@@YG”标识参数表的开始,后跟参数表;
参数表以代号表示: X--void , D--char, E--unsigned char, F--short, H--int, I--unsigned int, J--long, K--unsigned long, M--float, N--double, _N--bool, .... PA--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以“0”代替,一个“0”代表一次重复;
参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前;
参数表后以“@Z”标识整个名字的结束,如果该函数无参数,则以“Z”标识结束。 其格式为“?functionname@@YG*****@Z”或“?functionname@@YG*XZ”, 例如 int Test1(char *var1,unsigned long)-----“?Test1@@YGHPADK@Z” void Test2() -----“?Test2@@YGXXZ”
2 __cdecl调用约定: 规则同上面的 _stdcall 调用约定,只是参数表的开始标识由上面的“@@YG”变为“@@YA”。
3 __fastcall调用约定: 规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的“@@YG”变为“@@YI”。
VC++对函数的省缺声明是"__cedcl",将只能被C/C++调用。
这是C++编译时的函数改名规则,c++函数改名主要是为了函数重载,而在C中不存在函数重载的问题,可以看出C++编译后函数的新名字很复杂。
所以只能通过extern来声明C函数编译命名规则来调用DLL中的导出函数
dll动态库调用约定相关推荐
- C++中.lib静态库、.dll动态库的生成及调用2
关于动.静态库的介绍及静态库的生成及调用参见:C++中.lib静态库..dll动态库的生成及调用<1>,本文主要介绍基于VS2015平台的动态库dll的生成及调用方法. 一.动态库的生成 ...
- C++中.lib静态库、.dll动态库的生成及调用1
一.前言 1.动态链接库(dll)与静态链接库(lib): 动态链接库(dll)是一个可以被其他应用程序共享的程序模块,其中封装了一些可以被共享的例程和资源.在链接步骤中,连接器将从库文件取得所需的代 ...
- Node.js 调用 dll动态库 以华旭身份证阅读器为例
需求来源 由于使用Electron使用开发桌面端,同时也需要连接硬件设备,单纯使用js方法无法完成,需要通过Node调用dll动态库方式完成. 版本说明: node v12.18.3 (32位) np ...
- JNA框架调用dll动态库(给你整得明明白白)
java调用dll动态库的方法,总的有三种:JNI.JNA.JNative.其中JNA调用DLL是最方便的.网上文章一大堆,我就不废话了. 使用JNA框架调用DLL动态库,步骤如下: 一.环境准备 1 ...
- Windows系统下通过JNI调用dll动态库的实现
目的:java代码使用jni获取数据 工具: eclipse + Microsoft visual studio (c++) 业务代码: Java代码业务实现: package com.weip.jn ...
- 【C 语言】动态库封装与设计 ( 动态库调用环境搭建 | 创建应用 | 拷贝动态库相关文件到源码路径 | 导入头文件 | 配置动态库引用 | 调用动态库中的函数 )
文章目录 一.在 Visual Studio 2019 中创建 " 控制台应用 " 程序 二.拷贝 xxx.lib.xxx.dll.xxx.h 到源码路径 三.导入 xxx.h 头 ...
- VS2019:C++程序lib静态库、dll动态库的生成和使用
一.静态库 静态库的使用需要(1).h头文件:(2).lib静态库文件: 1.静态库生成 (1)项目-右键属性-常规-配置类型-选静态库: (2)点击生成,Debug文件夹下出现lib静态库: 2.静 ...
- 易语言 标准c 动态库,易语言Dll动态库的开发
一.关于易语言DLL 易语言Dll动态库的开发是编程当中和其他语言开发进行交互必不可少的方法之一,从易语言3.6版开始,已经能够支持对DLL动态链接库的开发, 编译出的DLL是标准的DLL,和其他编程 ...
- C#中DLL动态库的使用
C#中DLL动态库的使用介绍 介绍 DLL依赖项 注意事项 DLL创建及调用 程序Demo 介绍 DLL中文名称为:动态链接库英文为DLL*,是Dynamic Link Library*的缩写.是一个 ...
最新文章
- 各大厂分布式链路跟踪系统架构对比
- FPGA之道(69)提高设计的综合性能(一)提高设计的鲁棒性
- 离散数学--二元关系总结
- 删除所有的.svn文件夹
- helm部署Loki
- 修改RAC VIP IP
- Spring+Mybatis多数据源配置(四)——AbstractRoutingDataSource实现数据源动态切换
- springboot忽略证书_SpringBoot获取resource下证书失败
- mac 用户 文件夹 权限_WIN7局域网文件分权限共享设置方法
- Filter过滤器拦截方式
- [原]部署kubernetes dashboard(二)
- System.Net.Http.Formatting的nuget版本冲突问题
- 小米6线刷包php文件格式,小米6官方固件刷机教程_线刷|救砖教程图解
- 【转载】学习可能用到的英语单词
- Xcode Message from debugger: Terminated due to memory issue
- 微信小程序中如何有效地改变app.js中的数据,并在其他页面进行动态响应
- 如何用计算机录视频,怎么用电脑录制视频
- 根据瘦子的日历,改装了一个月历控件
- 图像相似度比较算法总结
- GJB-150三防试验机构,国军标试验机构
热门文章
- Qt把已有工程添加到其他工程中作为子工程或新创建子工程
- android 蓝牙不停扫描,android – BluetoothAdapter不会停止扫描BLE设备
- pysql与mysql的区别_postgresql与mysql的区别是什么
- php面向对象代码_PHP面向对象之抽象类详解(代码实例)
- combotree 可以异步加载吗_Unity AssetBundle 资源打包,加载,本地缓存方式,安卓,PC本地加载路径问题...
- 考研计算机专业复试,计算机专业考研复试准备
- keil5安装_keil5,软件安装包及安装教程
- linux单个core的线程,正确使用Core Data多线程的3种方式
- 这是我的C语言入门笔记。
- 做计算机的小卫士教案,小卫士在行动小班教案