Calling conventions
With some Magic version 8 the list of available functions in eDevloper suitable to call external functions residing in dynamic link libraries (DLLs) was extended by the functions CallDLLS(), CallDLLF(), UDFS() and UDFF(). Before there were only the functions UDF() and CallDLL(). Also the CALL UDP operation became a new option "Cnv:" with the available options "C", "Standard" and "Fast".
Some more information regarding this evolution you can find on pages about
"Magic DLLs and
"Standard DLLs.
All these functions and the CALL UDP operation basically do the same thing, they call external functions residing in DLLs (Windows dynamic link library). They do however differ in the "calling convention" used to invoke this external function.
This, the method of calling a function, passing parameters to it and receiving its return value, is what in 3-GL world usually is referred to as a "calling convention". Calling conventions is an issue tightly connected to assembly (machine) language. Its therefore not easy to understand that these things are an issue in Magic development and that there's differences.
Unfortunately it is basically the author of a DLL who decides which calling convention is mandatory for the usage of the functions he provides. Therfore it was a requirement for Magic to support more, different, calling conventions in external DLLs as well. A requirement which has been adressed by putting these new functions in the tool.
When staying in Magic, you do not need to be aware of this issue. As soon as you use external APIs however you need to know few things about this. This document does not try to give you an in-depth explanation about different calling conventions (as noted above its tightly related to assembly language). It maybe does however give you sufficient information about so that you are able to choose the correct function to use when confronted with the need to call an external function residing in a DLL. For further reading on calling conventions and in-depth information about them please refer to the links given in the reference section.
All informations given here are valid for Magic (eDeveloper) versions <= 10 on MS Windows x86 (32-bit) platform only.
Important note!
Implemented calling conventions on Magic site and behaviour of Magic's/eDeveloper functionality required to call external functions residing in DLLs changed over the versions. The information on this page is about things for which not very much (partly wrong) documentation is available. To be as accurate as possible with the information given here, I did relative extensive testing. This included looking into the assembly code Magic produces to find out which calling convention actually is used where it was unclear or not explicitely specified. All tests have been done however with eDeveloper Version 9.4 SP7. Later versions should behave the same. Nothing changed anymore. Things may be different for other versions.
A brief history of "calling conventions" support in Magic/eDeveloper
(has to be added. Some information regarding the evolution of Magic & DLLs you'll find in the page about
"Magic DLLs")
Typical calling conventions and the correct Magic (eDeveloper) function to use
I'll start with a table which is showing which Magic/eDeveloper function has to be used for which calling convention. That might be already the information you need. Afterwards I'll explain the differences between these calling conventions to some more detail, explain few of the terms used and give some more information about how to identify calling conventions.
Also have a look into the
sample page regarding this which gives more insight regarding the correct function to use for invocation of external functions residing in DLLs.
| calling convention |
required funtion/operation to call |
Notes |
| _stdcall / STDCALL / WINAPI |
UDFS(), CallDLLS(), CALL UDP (with "cnv:" set to "Standard") |
This is the calling convention which you'll find most often implemented in Windows DLLs. All Windows APIs have "Standard" calling conventions. A habit which almost all vendors adopted to minimize interoperability issues.
A notable exception are "Magic DLLs" (extension) which use cdecl. See notes on cdecl/"C" below and information available on "Magic DLLs" |
| _cdecl / CDECL |
UDF(), CallDLL(), Call UDP (with "cnv:" set to "C") |
|
| _fastcall / FASTCALL |
UDFF(), CallDLLF(), CALL UDP (with "cnv:" set to "Fast") |
|
| _thiscall / THISCALL |
(not supported in current eDeveloper versions) |
|
What is a "calling convention" ?
A calling convention is a method for a program to pass parameters to a function and receive a result value back from it. Calling conventions can differ. Which calling convention actually is used is something the developer of the DLL defines in the code or the compiler settings. Every programming language has its default calling convention. Some programming languages (particularly C and C++) allow the programmer to change this default and specify something different.
It is the compiler which then adds the required assembly (machine) prolog- and epilog-code automatically to reflect these settings. Many programmers therefore are not familiar with calling conventions. When staying in their own world (language) it is always the same calling convention which is used and there's no need to know about. For functions within external DLLs (Windows API f.i.) they receive "header files" where these functions are declared and from which the compiler can deduce the correct calling convention to use.
When interoperability becomes an issue because external functions in libraries written in a different programming language have to be called - calling conventions sometimes DO become an issue however and the developer needs to use the correct one to be able to achieve this interoperability. In eDeveloper you basically have this requirement as well. You need to know about the calling convention used in external APIs if you want to call functions from there.
~- Basically calling conventions differ in:
- in which order parameters are put on the stack
- in who is it who cleans up (maintains) the stack when the function is finished (the caller or the callee)
- in naming conventions (symbol decoration)
- in receiving the return value
To show how calling conventions exactly differ, some basic know-how of assembler language and sample assembly code is required. This (and an explanation of this) is out of scope for this document. See the links in reference section to see some of this sample code and more detailled information if interested. As mentioned above you basically are fine when you know which calling convention is used. Once you identified it and told eDeveloper about by using the correct function, eDeveloper will do the rest.
What calling conventions are there and which of the available eDeveloper functions do I use to invoke them ?
Below is explanation for the four most important calling conventions. It is unlikely that you ever encounter something different.
Information about which calling convention is used you'll sometimes find in the accompanying documents for your DLL. In particular then when a non-standard calling convention is used. Since _stdcall (STDCALL) is strictly defined by Microsoft and used for ALL Windows API, many vendors adopted this habit and use _stdcall (STDCALL) for functions exported from there DLLs as well.
Often you do get however only C/C++ "header files" together with a DLL. These header files contain information about all functions exported from a DLL (declarations). The function declaration will contain information about the calling convention used for this function.
STDCALL ("Standard", _stdcall, WINAPI)
This is the calling convention used for Win32 API functions and it is strictly defined by Microsoft. STDCALL is what you most often will encounter when confronted with external DLLs.
Note: If you get documentation for external DLLs (or just C/C++ "header" files) you most often will not see STDCALL or _stdcall there. Instead you most often will see a prefix "WINAPI" or "FAR PASCAL" there. This is so called preprocessor definitions and defined to be _stcall. Calling convention _stdcall (or better its behaviour) is the standard originally used in Microsoft Pascal (therefore the PASCAL in "FAR PASCAL"). "FAR PASCAL" became obsolete with 32-Bit versions of Windows, you'll however still find it from time to time in older documentation and header files.
Windows compilers define WINAPI simply to be _stcall. _stcall and WINAPI therefore are interchangable. If you see WINAPI in front of a function name, you know that its _stcall calling convention.
Note: In existing documentation, e.g. most documentation available on MSDN website for the Windows APIs, functions usually are NOT prefixed with a calling convention specifier like WINAPI. This then does not allow you to deduce which calling convention actually is required for this function. Since Windows API functions however do ALWAYS use _stdcall (WINAPI) you can be quite sure about the calling convention. Its simply left away for the sake of brevity.
To give an example ...
If you search for a API which allows you to modify .ini files, you may stumble over following declaration from MSDN:
UINT GetProfileInt(LPCTSTR lpAppName, LPCTSTR lpKeyName, INT nDefault);
This would be the declaration of a function named GetProfileInt() which is exported from dynamic link library kernel32.dll.
As mentioned above, you can be sure about the fact that you need to use _stcall convention to invoke this function since its a Windows API. If you want to be really sure however, you need to lookup GetProfileInt() in WinBase.h which is a header file from Platform SDK and which contains the actual declaration for this function. There you then will find following:
WINBASEAPI UINT WINAPI GetProfileIntA(__in LPCSTR lpAppName, __in LPCSTR lpKeyName, __in INT nDefault);
This declaration now DOES contain WINAPI. WINAPI is declared to be _stdcall. You know that calling convention "Standard" has to be used from within eDeveloper if you want to use this function.
From the declaration given in WinBase.h you might also have noticed that the name of this function (and therefore the name which you have to use when you invoke this function from eDeveloper) actually is GetProfileIntA (and not - as stated in MSDN documentation just GetProfileInt). This is because this function deals with character data. If this is the case the Windows API does most often provide you with two functions. An ANSI and a Unicode (wide character) version. GetProfileIntA would be the ANSI version; GetProfileIntW the Unicode version.
Magic (eDeveloper) versions < 10 do not have Unicode so that you can only use the ANSI version. eDeveloper 10 would have Unicode but does not support Unicode parameters for external functions in DLLs or ActiveX. You still need to use GetProfileIntA with eDeveloper 10; If you pass Unicode data to these functions eDeveloper will convert it to ANSI before actually invoking this function.
If you forget about this "A" and "W" appended to API functions dealing with character data and simply use the name GetProfileInt you'll receive an error from magic "unknown function in module" when it tries to invoke this function. GetProfileInt simply does not exist in module kernel32.dll.
To actually call this function from within eDeveloper you have several options. This document is only concerned about calling conventions and (to some extend) naming of these functions. For a more detailled description about calling Windows functions, passing parameters and samples please see the wiki page about calling Windows API functions.
CallDLLS ('kernel32.GetProfileIntA', ...)
CALL UDP '@kernel32.GetProfileIntA' (cnv: "Standard")
In calling convention _stdcall it is the callee (called function itself) which cleans up the stack. Arguments are passed right to left on the stack.
CDECL (_cdecl)
This is the standard calling convention used in the "C" programming language.
_cdecl is also the calling convention which has to be used for "Magic DLLs" (or Magic extensions).
If you have a DLL which is written in C and you only get a C header file (*.h) with it which contains function declarations without specification of a calling convention, it is _cdecl which is used. See following sample from a C header file for a function which uses _cdecl calling convention (by default) and which gets two long integer values as parameters and which returns a long integer value:
long Add (long a, long b);
Sometimes the DLL will be written in C++, export however functions for usage in C. In the accompanying header file you'll see following then (which you also have to interpret as calling convention _cdecl):
extern "C" { long Add (long a, long b); }
If the function Add would reside in a "Standard DLL" named MyMath.dll you have following options to call this DLL. The mechanism of parameter passing and receiving return values in eDeveloper is identical with those used for _stdcall. See the wiki page about calling Windows API functions for more detailled information and samples.
// invoke function in "Standard DLL" with calling convention "C"
CallDLL ('MyMath.Add', ...)
CALL UDP '@MyMath.Add' (cnv: "C")
If the function Add would reside in a "Magic DLL" named MyMath.dll you have following options to call this DLL:
// invoke function in "Magic DLL" with calling convention "C" (Note: Magic DLLs do ALWAYS have calling convention "C"
UDF ('MyMath.Add', ...)
CALL UDP 'MyMath.Add' (cnv: "C")
Above differences show you that you need to know if its a "Magic DLL" or if its not. To find out you have following options:
- Use a DLL inspection tool like Dependency Walker to see if DLL exports a function named MAGIC_BIND. If it does => "Magic DLL".
- Simply "try" CALL UDP 'MyMath.Add' (cnv: "C") and check the error message you get.
If you use a module specifier for CALL UDP with a "@" prefix, CALL UDP tries to load a "Standard DLL". It it is none, Invocation fails with error message "User function/procedure not found loaded". => its a "Magic DLL"
If you use a module specifier for CALL UDP without a "@" prefix, CALL UDP tries to load a "Magic DLL". It it is none, Invocation fails with error message "Failed to load driver: MYMATH.DLL" => its a "Standard DLL"
On the
Samples page there's all kind of valid and invalid invocations of DLL functions so that basically all error messages Mgaic can produce when trying to invoke an external fucntion actually HAVE been produced. From the error message you get, you usually can decide whats going wrong and what to change. I'll summarize the results of these tests soon.
In C (_cdecl) parameters are passed in right to left order on the stack; it is the caller which is responsible to cleanup the stack.
Having the caller being responsible for cleanup the stack gives slightly bigger programs since the stack maintenance code is now at many places whereas it would be only in one place when the callee would cleanup the stack. By doing it that way, _cdecl however allows functions with a variable parameter list (vararg). This is a quite powerful feature which you find often in C code. E.g. C standard library function printf() does this.
FASTCALL (_fastcall)
If you have a FASTCALL function "Add" exported from a DLL named SpeedMath.dll you have following options to call this function. The mechanism of parameter passing and receiving return values in eDeveloper is identical with those used for _stdcall. See the wiki page about calling Windows API functions for more detailled information and samples.
CallDLLF ('SpeedMath.Add', ...)
UDFF ('SpeedMath.Add', ...)
CALL UDP 'SpeedMath.Add' (cnv: "Fast")
In calling convention _fastcall the first two DWORD or similar parameters (32-bit values, pointers, ...) are passed in CPU registers (ECX and EDX). All other parameters are passed in right to left order on the stack. It is the called function which is (most frequently) responsible for maintaining the stack.
Due to its nature with passing parameters in registers FASTCALL is the fastest way to call a function. It is however not completely standardized across all compilers and operating systems. FASTCALL should only be used where speed is essential and if there's 1 or two 32-bit arguments. If you are a person who writes DLL for usage within eDeveloper you need to know how eDeveloper implements FASTCALL. Most likely, since MSE uses MS compilers for Windows platforms, it will be a Microsoft _fastcall. (AS: might be wrong. I'll try to find out). Your compiler needs to produce compatible prolog/epilog code if you want to use FACTCALL for functions residing in your DLL.
THISCALL (_thiscall)
This is the standard calling convention used to invoke member functions of C++ classes. Its currently not supported by eDeveloper. If you have a DLL exporting functions with THISCALL calling convention you need to wrap this in an own DLL.
In _thiscall arguments are passed right to left on the stack; it is the callee which is responsible for maintaining the stack. The pointer to the class object itself is passed in register EAX.
See also:
- Sample invocations of the various functions on different DLL types
- eDeveloper online help on topics CallDLL∞, CallDLLS∞, CallDLLF∞, UDF∞, UDFS∞, UDFF∞ and CALL (UDP) operation∞
- Calling conventions on MSDN∞
- Calling conventions on Wikipedia∞
- calling Windows API functions
- "Magic DLLs"
There are no comments on this page. [Add comment]