DLL中调用约定和名称修饰(一)

DLL中调用约定和名称修饰(一)
调用约定(Calling Convention)是指在程序设计语言中为了实现函数调用而建立的一种协议。这种协议规定了该语言的函数中的参数传送方式、参数是否可变和由谁来处理堆栈等问题。不同的语言定义了不同的调用约定。
在C++中,为了允许操作符重载和函数重载,C++编译器往往按照某种规则改写每一个入口点的符号名,以便允许同一个名字(具有不同的参数类型或者是不同的作用域)有多个用法,而不会打破现有的基于C的链接器。这项技术通常被称为名称改编(Name Mangling)或者名称修饰(Name Decoration)。许多C++编译器厂商选择了自己的名称修饰方案。
因此,为了使其它语言编写的模块(如Visual Basic应用程序、Pascal或Fortran的应用程序等)可以调用C/C++编写的DLL的函数,必须使用正确的调用约定来导出函数,并且不要让编译器对要导出的函数进行任何名称修饰。
1.调用约定(Calling Convention)
调用约定用来处理决定函数参数传送时入栈和出栈的顺序(由调用者还是被调用者把参数弹出栈),以及编译器用来识别函数名称的名称修饰约定等问题。在Microsoft VC++ 6.0中定义了下面几种调用约定,我们将结合汇编语言来一一分析它们:
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 ;由函数调用清栈
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 ;清栈
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。当加在输出函数前的关键字与编译环境中的选择不同时,直接加在输出函数前的关键字有效。

展开阅读全文

VC++、DLL、虚函数、调用约定

08-23

n关于“VC++、DLL、虚函数、调用约定”的一个问题折腾我很久了,希望CSDN的朋友可以助我解决问题。nnDLL里,接口类的虚函数(非纯虚函数),应该怎样调用约定?nn---------------------------------------------------nn背景描述:n一个DLL工程A(接口),有个接口类AI,里面全是纯虚函数。n一个EXE工程B(平台),有个成员变量mc类型是类AI。n一个DLL工程C(游戏),有个类是类AI的子类,即实现类,对类AI的所有接口(纯虚函数)进行了实现。nn原理是:n平台只有一个在跑,而游戏是N个,工程B(平台)的mc被赋值成工程C(游戏)的对象,通过工程A的接口,mc可以调用工程C(游戏)里的函数(接口的实现)。nn问题:n我在开发一个新游戏时,发现工程A(接口)类AI的接口不够用,想新增一个接口,于是加入了一个”虚函数“,注意不是“纯虚函数”,为的是不影响之前所有已有的游戏(不然之前的游戏不作任何改动而编译 会报error C2259: 不能实例化抽象类),即之前的游戏不用实现这个接口,修改工程B(平台)调用这个函数时是工程A(接口)新增的接口,若游戏实现这个接口,就调用游戏里的实现函数;若游戏没有定义这个接口(之前的游戏),则调用工程A(接口)类AI里的虚函数(里面只有一句调试打印)。nn但发现,有的游戏是正常的,有的游戏不正常。不正常的游戏,调试工程B(平台)在调用新接口时提示:nRun-Time Check Failure #0 - 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.nn百度了一下,很多人说是调名约定的问题。n工程A、工程B、所有的游戏工程C,工程选项里都是 __cdecl (/Gd)。nn于是我在工程A、工程C里下面几种尝试:nvirtual void OnEventABC(int a, bool b); //没写调用约定,有的游戏正常,有的游戏出错nvirtual void __stdcall OnEventABC(int a, bool b); //所有的游戏都出错nvirtual void __cdecl OnEventABC(int a, bool b); //所有的游戏都出错nvirtual void WINAPI OnEventABC(int a, bool b); //所有的游戏都出错nn---------------------------------------------------nn请问大家,怎么解决这个问题,是调名约定的问题,还是其它什么问题?n 问答

VB的DLL调用怎么“DLL调用约定错误”

10-06

"PrinterUsb.dll"里的数据类型及函数如下:rnconst int DEVNUM = 10;rnconst int PATHLEN = 200;rnrntypedef structrnrn char strDevPath[PATHLEN];rnDEVPATH;rnrntypedef structrnrn DEVPATH DevPath[DEVNUM];rn int DevNum;rnDEVINFO;rnrnint GetDevicePath(DEVINFO&);rnbool Open(int PortNum);rnbool Write(char *pBuf,DWORD nNumberOfBytesToWrite,DWORD &NumberOfBytesWritten);rnvoid Close();rnrnrn我在USB里调用DLL,作如下声明:rnrnrnPublic Const DevNum = 10rnPublic Const PATHLEN = 200rnPublic Type DEVPATHrn strDevPath(0 To PATHLEN) As StringrnEnd TypernrnPublic Type DEVINFOrn iDevPath(0 To DevNum) As DEVPATHrn DevNum As IntegerrnEnd TypernrnPublic Declare Function GetDevicePath _rn Lib "PrinterUsb.dll" _rn (ByRef sDevInfo As DEVINFO) _rnAs LongrnrnPublic Declare Function OpenUSB _rn Lib "PrinterUsb.dll" Alias "Open" _rn (ByVal PortNum As Integer) _rnAs BooleanrnrnPublic Declare Function WriteUSB _rn Lib "PrinterUsb.dll" Alias "Write" _rn (ByVal pBuf As String, _rn ByVal nNumberOfBytesToWrite As Long, _rn ByVal nNumberOfBytesWritten As Long) _rnAs BooleanrnrnPublic Declare Sub CloseUSB _rn Lib "PrinterUsb.dll" Alias "Close" ()rnrnrn程序如下:rnPrivate Sub cmdUSBTest_Click()rn Dim MyDevInfo As DEVINFOrn Dim ESC As String: ESC = Chr(27)rn Dim strCmd As Stringrn Dim nNumberOfBytesToWrite As Long: nNumberOfBytesToWrite = 0rn Dim nNumberOfBytesWritten As Long: nNumberOfBytesWritten = 10rn rn strCmd = "hello"rn rn rn GetDevicePath MyDevInforn If MyDevInfo.DevNum = 0 Thenrn MsgBox "未检测到USB设备!"rn Endrn End Ifrn rn If OpenUSB(0) = False Thenrn MsgBox "打开USB设备失败!"rn Endrn End Ifrn rn WriteUSB strCmd, nNumberOfBytesToWrite, nNumberOfBytesWrittenrn rn CloseUSBrn rn rnEnd Subrnrnrnrn执行GetDevicePath MyDevInfo时就出现“实时错误49,DLL调用约定错误”rn我把rnPublic Declare Function GetDevicePath _rn Lib "PrinterUsb.dll" _rn (ByRef sDevInfo As DEVINFO) _rnAs Longrn改成rnPublic Declare Function GetDevicePath _rn Lib "PrinterUsb.dll" _rn (ByVal sDevInfo As DEVINFO) _rnAs Longrn就出现“编译错误:用户类型不能用”。rnrn现在怎么办,哪儿错了,要怎么改,原因是什么呢,谢谢!rn 论坛

请教高手:VB dll 调用约定错误!!

02-29

各位高手,我的VB调用DLL,在运行时出现了 dll 调用约定错误! 程序如下,请各位大虾帮忙解决一下!!感恩。。rnrnVB程序:rnrnrnrnOption Explicitrnrn'USB User InterfacernPublic Declare Function Read_USB Lib "DS5000USB_UI.dll" (ByVal mType As Long, ByVal wLength As Long, pBuffer As Any) As BooleanrnPublic Declare Function Write_USB Lib "DS5000USB_UI.dll" (ByVal cChar As Long) As BooleanrnrnGlobal Const USB_READ_CMD_REDAY = 0rnGlobal Const USB_READ_CMD_DATA = 1rnGlobal Const ARRAYSIZE = 1024 ' Size of read bufferrnGlobal DSO_IOInitOK As BooleanrnGlobal USB_TimeOutTimer As Long 'USB´«Êä½áÊøʱ¼ärnrnrnSub USB_Init()rn' ========================================================================rn'rn' INITIALIZATION SECTIONrn'rn' ========================================================================rnrn ' The application brings the oscilloscope online using ildev. Arn ' device handle, Dev, is returned and is used in all subsequentrn ' calls to the device.rn Dim retcode As Booleanrn Dim ValueStr As String * ARRAYSIZErn Dim buf(255) As Bytern ' The application reads the ASCII string from the oscilloscopern ' into the ValueStr variable.rn retcode = Read_USB(USB_READ_CMD_REDAY, 1, buf(0))rn rn If retcode = False Thenrn MsgBox "Cann't Connect to USB Device", 48, "USB Init"rn DSO_IOInitOK = Falsern Elsern DSO_IOInitOK = Truern End Ifrn rnEnd SubrnSub SendToUSB(cmd As String)rn Dim retcode As Booleanrn Dim buf As Stringrn Dim temp As Longrn Dim i As Integerrn rn buf = cmd + Chr$(13)rnrn ' The application writes the text in ScopeCommand to thern ' oscilloscope.rn For i = 1 To Len(cmd) + 1rn temp = Asc(buf)rn buf = Right(buf, Len(cmd) + 1 - i)rn retcode = Write_USB(temp)rn If retcode = False Thenrn MsgBox "Cann't Connect to USB Device", 48, "USB Send Message"rn Exit Forrn End Ifrn Next irnrnEnd SubrnPublic Function ReadUSB() As Stringrnrn Dim retcode As Booleanrn Dim tmpStr As Stringrn Dim buffer(256) As Bytern Dim i, size As Longrn ' The application reads the ASCII string from the oscilloscopern ' into the ValueStr variable.rn USB_TimeOutTimer = 0rn buffer(0) = 0rn While (buffer(0) = 0 And USB_TimeOutTimer < 20)rn retcode = Read_USB(USB_READ_CMD_REDAY, 1, buffer(0))rn If retcode = False Thenrn MsgBox "Can't Connect to USB Device", 48, "USB Read Message"rn ReadUSB = ""rn Exit Functionrn End Ifrn DoEventsrn Wendrn If USB_TimeOutTimer > 19 Thenrn MsgBox "Read USB time out", 48, "USB Read Message"rn tmpStr = ""rn Elsern size = buffer(0)rn retcode = Read_USB(USB_READ_CMD_DATA, size, buffer(0))rn If retcode = False Thenrn MsgBox "Cann't Connect to USB Device", 48, "USB Read Message"rn ReadUSB = ""rn Exit Functionrn End Ifrn tmpStr = ""rn For i = 0 To size - 1rn If (buffer(i) = 10) Thenrn Exit Forrn End Ifrn tmpStr = tmpStr + Chr(buffer(i))rn Next irn End Ifrn ReadUSB = tmpStrrn rnEnd FunctionrnrnrnrnrnDLL函数定义:rnrn(DLL头文件)rnrn// DS5000USB_UI.h : main header file for the DS5000USB_UI DLLrn//rnrn#if !defined(AFX_DS5000USB_UI_H__A004624B_F508_4C1D_B0A9_5ABF16885DC8__INCLUDED_)rn#define AFX_DS5000USB_UI_H__A004624B_F508_4C1D_B0A9_5ABF16885DC8__INCLUDED_rnrn#if _MSC_VER > 1000rn#pragma oncern#endif // _MSC_VER > 1000rnrn#ifndef __AFXWIN_H__rn #error include 'stdafx.h' before including this file for PCHrn#endifrnrn#include "resource.h" // main symbolsrnrn#define USB_CTRL_TYPE_LEN 0//¶Á´«ÊäµÄÊý¾ÝµÄ³¤¶Èrn#define USB_CTRL_TYPE_DATA 1//¶Á´«ÊäµÄÊý¾Ýrnrnextern "C" BOOL EXPORT __stdcall Control_Command_Send(INT devIndex,INT mRequest,INT wValue,INT wIndex,INT wLength);rnextern "C" BOOL EXPORT __stdcall Control_Command_Receive(INT devIndex,INT mRequest,INT wValue,INT wIndex,INT wLength,PUCHAR mBuffer);rnextern "C" BOOL EXPORT __stdcall Bulk_DataTransfer_Read(INT devIndex,INT wLength,PUCHAR mBuffer);rnextern "C" BOOL EXPORT __stdcall Bulk_DataTransfer_Write(INT devIndex,INT wLength,PUCHAR mBuffer);rnextern "C" INT EXPORT __stdcall GetDeviceNum();//»ñÈ¡µ±Ç°ÏµÍ³ÖеÄRIGOL ·ÇTMCÉ豸×ÜÊýrn/*******************************************************************************rnrn ** º¯ÊýÃû³Æ: Read_USB rn ** ÊäÈë²ÎÊýrn ** devIndex --- É豸±àºÅ rn ** type --- ¶ÁÈ¡ÀàÐÍΪÊý¾Ý³¤¶È(USB_CTRL_TYPE_LEN)»òÊý¾ÝÄÚÈÝ(USB_CTRL_TYPE_DATA)rn ** wLength --- ±¾´Î¶ÁÈ¡µÄ³¤¶Èrn ** mBuffer --- ´æ´¢¶ÁÈ¡Êý¾ÝµÄ¿Õ¼äÖ¸Õërn **rn ** Êä³ö²ÎÊý:rn ** ·µ»ØÀàÐÍ --- BOOLÀàÐÍrn **rn ** Ãè Êö: ¶ÁÈ¡USBÊý¾Ýrn********************************************************************************/rnextern "C" BOOL EXPORT __stdcall Read_USB(INT devIndex,INT type,INT wLength,PUCHAR mBuffer);rn/*******************************************************************************rnrn ** º¯ÊýÃû³Æ: Write_USB rn ** ÊäÈë²ÎÊýrn ** devIndex --- É豸±àºÅ rn ** chr --- Ҫͨ¹ýUSB·¢Ë͵ÄÊý¾Ýrn **rn ** Êä³ö²ÎÊý:rn ** ·µ»ØÀàÐÍ --- BOOLÀàÐÍrn **rn ** Ãè Êö: ·¢ËÍUSBÊý¾Ýrn********************************************************************************/rnextern "C" BOOL EXPORT __stdcall Write_USB(INT devIndex,UCHAR chr);rnrnHANDLE OpenDeviceByIndex(INT index);rnBOOL CloseDeviceByIndex(INT index);rn//BOOL Close_Device_Handle();rnrnrn#define MAX_BULK_INBUF_SIZE 4096rn#define MAX_BULK_OUTBUF_SIZE 4096rnrn#define MAX_CONTROL_INBUF_SIZE 512rn#define MAX_CONTROL_OUTBUF_SIZE 512rnrnrnBOOL CloseIfOpen(VOID);rnBOOL Exit(INT res);rnrnextern HANDLE OpenByInterface(rn GUID* pClassGuid, // points to the GUID that identifies the interface classrn DWORD instance, // specifies which instance of the enumerated devices to openrn PDWORD pError // address of variable to receive error statusrn );rnrn/////////////////////////////////////////////////////////////////////////////rn// CDS5000USB_UIApprn// See DS5000USB_UI.cpp for the implementation of this classrn//rnrnrnclass CDS5000USB_UIApp : public CWinApprnrnpublic:rn CDS5000USB_UIApp();rnrn// Overridesrn // ClassWizard generated virtual function overridesrn //AFX_VIRTUAL(CDS5000USB_UIApp)rn public:rn virtual void OnFinalRelease();rn //AFX_VIRTUALrnrn //AFX_MSG(CDS5000USB_UIApp)rn // NOTE - the ClassWizard will add and remove member functions here.rn // DO NOT EDIT what you see in these blocks of generated code !rn //AFX_MSGrn DECLARE_MESSAGE_MAP()rn;rnrnrn/////////////////////////////////////////////////////////////////////////////rnrn//AFX_INSERT_LOCATIONrn// Microsoft Visual C++ will insert additional declarations immediately before the previous line.rnrn#endif // !defined(AFX_DS5000USB_UI_H__A004624B_F508_4C1D_B0A9_5ABF16885DC8__INCLUDED_)rnrn 论坛

没有更多推荐了,返回首页