下载本文档
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、DLL中调用约定和名称修饰调用约定(Calling Convention )是指在程序设计语言中为了实现函数调用 而建立的一种协议。这种协议规定了该语言的函数中的参数传送方式、参数是否可变和由谁来处理堆栈等问题。不同的语言定义了不同的调用约定。在C+中,为了允许操作符重载和函数重载,C+编译器往往按照某种规则 改写每一个入口点的符号名,以便允许同一个名字(具有不同的参数类型或者是 不同的作用域)有多个用法,而不会打破现有的基于C的链接器。这项技术通常被称为名称改编(Name Mangling )或者名称修饰(Name Decoration )。许 多C+编译器厂商选择了自己的名称修饰方案。因此
2、,为了使其它语言编写的模块(如 Visual Basic应用程序、Pascal或 Fortran的应用程序等)可以调用C/C+编写的DLL的函数,必须使用正确的调 用约定来导出函数,并且不要让编译器对要导出的函数进行任何名称修饰。一.调用约定(Calling Convention )调用约定用来处理决定函数参数传送时入栈和出栈的顺序(由调用者还是被调用者把参数弹出栈),以及编译器用来识别函数名称的名称修饰约定等问题。在Microsoft VC+ 6.0中定义了下面几种调用约定,我们将结合汇编语言来一一 分析它们:1、_cdecl_cdecl是C/C+和MFC程序默认使用的调用约定,也可以在函数
3、声明时 加上_cdecl关键字来手工指定。采用_cdecl约定时,函数参数按照从右到左 的顺序入栈,并且由调用函数者把参数弹出栈以清理堆栈。因此,实现可变参数的函数只能使用该调用约定。由于每一个使用 _cdecl约定的函数都要包含清理 堆栈的代码,所以产生的可执行文件大小会比较大。_cdecl可以写成_cdecl。下面将通过一个具体实例来分析_cdecl约定:在VC+中新建一个 Win32 Console 工程,命名为cdecl。其代码如下:int _cdecl Add(i nt a, i nt b);/ 函数声明void main()Add(1,2);/函数调用int _cdecl Add(
4、i nt a, i nt b)/ 函数实现return (a + b);函数调用处反汇编代码如下:;Add(1,2);push22push 1call ILT+0(Add) (00401005)addesp,8;参数从右到左入栈,先压入;压入1;调用函数实现;由函数调用清栈2、_stdcall_stdcall调用约定用于调用 Win32 API函数。采用_stdcal约定时,函数 参数按照从右到左的顺序入栈,被调用的函数在返回前清理传送参数的栈,函数参数个数固定。由于函数体本身知道传进来的参数个数,因此被调用的函数可以在返回前用一条ret n指令直接清理传递参数的堆栈。stdcall可以写成
5、_stdcall。还是那个例子,将_cdecl约定换成_stdcall :int _stdcall Add(i nt a, i nt b) return (a + b);函数调用处反汇编代码:;Add(1,2);push2;参数从右到左入栈,先压入2push1;压入1callILT+10(Add) (0040100f);调用函数实现函数实现部分的反汇编代码:;int _stdcall Add(i nt a, i nt b)pushebpmovebp,espsubesp,40hpushebxpushesipushedileaedi,ebp-40hmovecx,10hmoveax,0CCCCCCC
6、Chrep stosdword ptr edi;return (a+ b);moveax,dword ptr ebp+8addeax,dword ptr ebp+0Chpopedipopesipopebxmovesp,ebppopebpret8;清栈3、 fastcall_fastcall约定用于对性能要求非常高的场合。_fastcall约定将函数的从左 边开始的两个大小不大于4个字节(DWORD)的参数分别放在ECX和EDX寄 存器,其余的参数仍旧自右向左压栈传送, 被调用的函数在返回前清理传送参数 的堆栈。_fastcall可以写成_fastcall。依旧是相类似的例子,此时函数调用约定为
7、 _fastcall,函数参数个数增加2个:int _fastcall Add(i nt a, double b, int c, int d) return (a + b + c + d);函数调用部分的汇编代码:;Add(1,2, 3, 4);push4movedx,3push 40000000h;后两个参数从右到左入栈,先压入 4;将int类型的3放入edx;压入double类型的2push mov call0ecx,1;将int类型的1放入ecxILT+0(Add) (00401005);调用函数实现函数实现部分的反汇编代码:;int _fastcall Add(i nt a, doub
8、le b, int c, int d)push mov sub push push pushebpebp,esp esp,48h ebxesiedipushecxleaedi,ebp-48hmovecx,12hmoveax,0CCCCCCCChrep stosdword ptr edipopecxmovdword ptr ebp-8,edxmovdword ptr ebp-4,ecx;return (a + b + c + d);filddword ptr ebp-4faddqword ptr ebp+8fiadddword ptr ebp-8fiadddword ptr ebp+10hcal
9、l_ftol (004011b8)popedipopesipopebxmovesp,ebppopretebp0Ch;清栈关键字_cdecl、_stdcall和_fastcall可以直接加在要输出的函数前,也可以在编译环境的Setti ng.->C/C+->Code Gen eratio n 项选择。它们对应的命令 行参数分别为/Gd、/Gz和/Gr。缺省状态为/Gd,即_cdecl。当加在输出函数前 的关键字与编译环境中的选择不同时,直接加在输出函数前的关键字有效。4、thiscallthiscall调用约定是C+中的非静态类成员函数的默认调用约定。thiscall 只能被编译器使
10、用,没有相应的关键字,因此不能被程序员指定。采用thiscall约定时,函数参数按照从右到左的顺序入栈, 被调用的函数在返回前清理传送参 数的栈,只是另外通过ECX寄存器传送一个额外的参数:this指针。这次的例子中将定义一个类,并在类中定义一个成员函数,代码如下:class CSumpublic:int Add(i nt a, i nt b) return (a + b);void mai n()CSum sum; sum.Add(1,2);函数调用部分汇编代码:;CSum sum;sum.Add(1,2);push2;参数从右到左入栈,先压入2push1;压入1leaecx,ebp-4;e
11、cx存放了 this扌曰针callILT+5(CSum:Add) (0040100a);调用函数实现函数实现部分汇编代码:;int Add(int a, int b)pushebpmovebp,espsubesp,44h空间用于存放this指针pushebxpushesipushedipushecxleaedi,ebp-44hmovecx,11hmoveax,OCCCCCCCChrep stosdword ptr edipopecxmovdword ptr ebp-4,ecx;return (a+ b);moveax,dword ptr ebp+8addeax,dword ptr ebp+0C
12、hpopedipopesipopebxmovesp,ebppopebpret8;清栈;多用了一个4bytes 的5、naked 属性采用上面所述的四种调用约定的函数在进入函数时,编译器会产生代码来保存ESI、EDI、EBX、EBP寄存器中的值,退出函数时则产生代码恢复这些寄存 器的内容。对于定义了 naked属性的函数,编译器不会自动产生这样的代码, 需要你手工使用内嵌汇编来控制函数实现中的堆栈管理。由于naked属性并不是类型修饰符,故必须和_declspec共同使用。下面的这段代码定义了一个使 用了 naked属性的函数及其实现:_declspec ( n aked ) func() in
13、t i;int j;_asmebppushmov subebp, espesp, _LOCAL_SIZE_asmmovesp, ebppopebpretnaked属性与本节关系不大,具体请参考 MSDN。6、WINAPI还有一个值得一提的是 WINAPI宏,它可以被翻译成适当的调用约定以供 函数使用。该宏定义于 windef.h 之中。下面是在windef.h中的部分内容:#defi ne CDECL#defi ne WINAPI#defi ne CALLBACK#defi ne WINAPI#defi ne APIENTRY_cdeclCDECL_stdcall_stdcallWINAPI由
14、此可见,WINAPI、CALLBACK、APIENTRY 等宏的作用.名称修饰(Name Decoration )C或C+函数在内部(编译和链接)通过修饰名(Decoration Name )识 别。函数的修饰名是编译器在编译函数定义或者原型时生成的字符串。编译器在创建.obj文件时对函数名称进行修饰。有些情况下使用函数的修饰名是必要的, 如在模块定义文件里头指定输出 C+重载函数、构造函数、析构函数,又如在 汇编代码里调用C或C+函数等。在VC+中,函数修饰名由编译类型(C或C+ )、函数名、类名、调用 约定、返回类型、参数等多种因素共同决定。下面分 C编译、C+编译(非类 成员函数)和C+
15、类及其成员函数编译三种情况说明:1、C编译时函数名称修饰当函数使用_cdecl调用约定时,编译器仅在原函数名前加上一个下划线前 缀,格式为 _functionname 。例如:函数 int _cdecl Add(int a, int b),输出后为:_Add。当函数使用_stdcall调用约定时,编译器在原函数名前加上一个下划线前 缀,后面加上一个符号和函数参数的字节数,格式为_functionnamenumber。例如:函数 int _stdcall Add(int a, int b)输出后为:_Add8。当函数是用_fastcall调用约定时,编译器在原函数名前加上一个 符号, 后面是加一
16、个符号和函数参数的字节数,格式为functionnamenumber。例如:函数 int _fastcall Add(int a, int b)输出后为:Add8。以上改变均不会改变原函数名中的字符大小写。2、C+h编译时函数(非类成员函数)名称修饰当函数使用_cdecl调用约定时,编译器进行以下工作:1.以?标识函数名的开始,后跟函数名;2 .函数名后面以YA标识开始,后跟返回值和参数表;3 .当函数的返回值或者参数与C+类无关的时候,返回值和参数表以下列代号 表示:B: constD: charE: unsigned charF: shortG: unsigned shortH: intI
17、: unsigned intJ: longK: unsigned longM: floatN: double_N : boolPA:指针(*,后面的代号表明指针类型,如果相同类型的指针连续出现,以0代替,一个0代表一次重复)PB : const 指针AA :引用(&)AB : const 引用U :类或结构体V: In terface (接口)W4 : enumX: void4、YA标识之后紧跟的是该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前。当函数的返回值或者参数与C+类无关的时候,其处理符合本条规则,否则按照 5、6规则处理;5、当函数返回值为某个类或带
18、有 const性质的类的时候,返回值的命名为:?A/?B+V+类名+ (不带加号)。当函数返回值为某个类的指针 /引用或 者带有const性质的类的指针/引用的时候,返回值的命名为:PA/AA或者PB/AB+V+类名+ (不带加号);6、函数参数为某个类的时候,并且该参数所使用的类曾经出现过的话(也就是与函数返回值所使用的类相同或者与前一个参数使用的类相同),则该参数类型格式为:V+1+ (不带加号)。如果该参数所使用的类没有出现过的话,则该 参数类型格式为:V+类名+ (不带加号)。函数参数为某个类的指针/引用 或者带有const性质指针/引用的时候,则该参数类型格式是在上述格式的基础 上在
19、V前面加上代表指针/引用类型或者带有const性质指针/引用类型的标识符(PA/AA 或 PB/AB);7、参数表后以Z标识整个名字的结束,如果该函数无参数,则以Z标识结束。当函数使用_stdcall调用约定时,编译器所做工作的规则同上面的 _cdecl 调用约定,只是参数表的开始标识由上面的 YA变为YG。当函数使用_fastcall调用约定时,编译器所做工作的规则同上面的 _cdecl 调用约定,只是参数表的开始标识由上面的 YA变为丫1。3、C+编译类及其成员函数时名称修饰对于导出的C+类,仅能使用_cdecl调用约定。在编译器编译过程中,编 译器会对C+类进行处理。如:class _d
20、eclspec(dllexport) MyClass 会被处理 为 class MyClass & MyClass:operator=(class MyClass con st &)。在 C+ 编译器对C+类进行名称修饰的时候,编译器进行以下工作:1 以?标识函数名的开始,后跟?4+类名;2 类名后面跟QAE标识,对于导出类来说这是固定的;3. QAE后面跟AAV0ABV0,即引用类型标识符 AA+V+0 (重复的类 的标识符)+ (不带加号)和con st性质的引用AB+V+ 0 (重复的类的标识符) + (不带加号);4 .最后以Z标识整个名字的结束。对于导出的C+类中的成
21、员函数(非构造函数和析构函数),可以使用不同 的调用约定。当导出的C+类中的成员函数使用_cdecl调用约定时,编译器进 行以下工作:1.以?标识函数名的开始,后跟函数名+类名(不带加号);2 .之后以QAE标识开始,后跟返回值和参数表;3 当函数的返回值或者参数与C+类无关的时候,返回值和参数表以下列代号 表示:B: constD: charE: unsigned charF: shortG: unsigned shortH: intI: unsigned intJ: longK: unsigned longM: floatN: double_N : boolPA:指针(*,后面的代号表明指
22、针类型,如果相同类型的指针连续出现,以0代替,一个0代表一次重复)PB : const 指针AA :引用(&)AB : const 引用U :类或结构体V: In terface (接口)W4 : enumX: void4、 QAE标识之后紧跟的是该函数的返回值类型,其后依次为参数的数据类 型,指针标识在其所指数据类型前。当函数的返回值或者参数与 C+类无关的 时候,其处理符合本条规则,否则按照 5、6规则处理;5、当函数返回值为当前类或带有 const性质的当前类的时候,返回值的命名为:?A或?B+V+1+ (不带加号)。当函数返回值为当前类的指针/引用或者 带有con st性质的当
23、前类的指针/引用的时候,返回值的命名为:PA/AA或PB/AB+V+1+ (不带加号);6、当函数返回值为某个类或带有 const性质的类的时候,返回值的命名为:?A/?B+V+类名+ (不带加号)。当函数返回值为某个类的指针 /引用或 者带有const性质的类的指针/引用的时候,返回值的命名为:PA/AA或者 PB/AB+V+类名+ (不带加号);7、函数参数为某个类的时候,并且该参数所使用的类曾经出现过的话(也就是当前要导出的类、与函数返回值所使用的类相同或者与前一个参数使用的类相同 的类),则该参数类型格式为:V+1+ (不带加号)。如果该参数所使用的类 不是当前要导出的类的话,则该参数类型格式为:V+类名+ (不带加号)。函数参数为某个类的指针/引用或者带有const性质指针/引用的时候,则该参数 类型格式是在上述格式的基础上在 V前面加上代表指针/
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025-2026学年八年级三角形全等说课稿
- 2025-2026学年初中作文课保护鸟类说课稿
- 2025-2026学年24点游戏作文说课稿
- 2025-2026学年动物大迁徙说课稿
- 2025-2026学年大班《沉浮》的说课稿
- 2026下半年下半年高中语文教资面试名著导读真题及解析
- 《桂枝香金陵怀古》课件
- 《动力学全章》课件
- 《声波的基本特性》课件
- 2026年新版假发品牌测试题及答案
- 客户服务热线接听规范手册
- 起重指挥Q1培训课件
- 2024-2025学年广东省广州市荔湾一中高一(上)期中英语试卷
- 人才池管理办法
- DB32/T 3576-2019农村产权交易场所建设与管理
- 2025年少先队辅导员技能大赛考试题库(含答案)
- 门诊危重病人处置流程
- 冷却塔填料更换及安全措施
- T-CACM 1411-2022 糖尿病基层中医防治管理指南
- 彩砂环氧防滑地坪施工方案
- DB23-T 1167-2024 装配式聚苯模块保温系统技术规程
评论
0/150
提交评论