3. cdecl calling convention
Code: |
Function Test3( i: Integer; b: Boolean; d: Double ): Integer; cdecl; |
Cdecl calling convention passes parameters on the stack and pushes them from right to left in the parameter list. Each parameter occupies a multiple of 4 bytes. The resulting stack layout is
ebp + 16 value of d, 8 bytes
ebp + 12 value of b, 4 bytes, only lowbyte significant
ebp + 08 value of i, 4 bytes
ebp + 04 return address, 4 bytes
ebp + 00 old ebp value
The parameters are cleared off the stack by the calling function, so the function ends with a ret 0 and after the call instruction we find a add esp, $10 instruction ($10 = 16 is the total size of the parameters on stack).
4. Stdcall calling convention
Code: |
Function Test4( i: Integer; b: Boolean; d: Double ): Integer; stdcall; |
Sdtcall calling convention passes parameters on the stack and pushes them from right to left in the parameter list. Each parameter occupies a multiple of 4 bytes. The resulting stack layout is
ebp + 16 value of d, 8 bytes
ebp + 12 value of b, 4 bytes, only lowbyte significant
ebp + 08 value of i, 4 bytes
ebp + 04 return address, 4 bytes
ebp + 00 old ebp value
The parameters are cleared off the stack by the called function using a ret $10 instruction ($10 = 16 is the total size of the parameters on stack).
When writing DLLs that are only be meant to be used from Delphi programs you will usually use the register calling convention, since it is the most efficient one. But this really ties the DLL to Delphi, no program compiled in another language (with the exception of BC++ Builder perhaps) will be able to use the DLL unless it uses assembler to call the functions, since the Register calling convention (like MS VC _fastcall) is
compiler-specific. When writing DLLs that should be usable by other programs regardless of language you use the stdcall calling convention for exported routines. Any language that can call Windows API routines will be able to call routines from such a DLL, as long as you stay away from Delphi-specific data types, like String, Boolean, objects, real48. Pascal calling convention is Win16 heritage, it was the default for the Win16 API but is no longer used on Win32. A topic loosely tied to calling conventions is name decoration for exported names in DLLs. Delphi (5 at least) does not decorate names, regardless of calling convention used. The name
appears in the exported names table exactly as you cite it in the exports clause of the DLL, case and all. Case is
significant for exported functions in Win32! Other compilers may decorate names. Unless told to do otherwise a C compiler will prefix all cdecl functions with an underbar and will decorate stdcall functions in the format _name@x, where x is the total parameter size, e.g. _Test3@16. C++ is even worse, unless functions are declared as extern "C" it will export names in a decorated format that encodes parameter size and type, in a compiler-specific fashion. For routines exported with Pascal calling convention the names may be all uppercase,
but as said above you will not usually encouter this convention on Win32. Due to these naming issues it is often appropriate to sic TDUMP on an unknown DLL you want to interface to, to figure out the actual names of the exported functions. These can then be given in a name clause for the external statement if they are decorated.
Demo DLL:
Code: |
library DemoDLL; uses Windows;
function Test1(i: Integer; b: Boolean; d: Double): Integer; pascal; begin Result := Round(i * Ord(b) * d); end;
function Test2(i: Integer; b: Boolean; d: Double): Integer; register; begin Result := Round(i * Ord(b) * d); end;
function Test3(i: Integer; b: Boolean; d: Double): Integer; cdecl; begin Result := Round(i * Ord(b) * d); end;
function Test4(i: Integer; b: Boolean; d: Double): Integer; stdcall; begin Result := Round(i * Ord(b) * d); end;
exports Test1 index1, Test2 index2, Test3 index3, Test4 index4;
begin end. |
Code: |
// Example call from test project: implementation {$R *.DFM}
function Test1(i: Integer; b: Boolean; d: Double): Integer; pascal; external'DEMODLL.DLL'Index1;
function Test2(i: Integer; b: Boolean; d: Double): Integer; register; external'DEMODLL.DLL'Index2;
function Test3(i: Integer; b: Boolean; d: Double): Integer; cdecl; external'DEMODLL.DLL'Index3;
function Test4(i: Integer; b: Boolean; d: Double): Integer; stdcall; external'DEMODLL.DLL'Index4;
procedure TForm1.Button1Click(Sender: TObject); var i: Integer; begin i := Test1(16, True, 1.0); i := Test2(16, True, 1.0); i := Test3(16, True, 1.0); i := Test4(16, True, 1.0); end; |
Set breakpoints on the lines and step into the routines with the CPU window open to see the stack layout.
Взято с Vingrad
- << Назад
- Вперёд
Просьба писать ваши замечания, наблюдения и все остальное,
что поможет улучшить предоставляемую информацию на этом сайте.
ВСЕ КОММЕНТАРИИ МОДЕРИРУЮТСЯ ВРУЧНУЮ, ТАК ЧТО СПАМИТЬ БЕСПОЛЕЗНО!