The __stdcall calling convention has been there for a very long time. While older calling conventions like __pascal fell into oblivion, __stdcall became the standard calling convention of Win32 API functions. Unlike __cdecl (the native calling convention of C/C++), it is supported in C/C++, Visual Basic, Java, and other languages alike, which makes it the first choice when building a DLL for cross-language use.
The internal representations of both __cdecl and __stdcall
functions have decorations. In MSVC (Microsoft Visual C++) and MinGW
(Minimalistic GNU for Windows) GCC, __cdecl function will be prefixed
an underscore, and __stdcall functions will have the beginning underscore
as well as be appended by the at-sign (@
) followed by the number
of bytes in the argument list. So,
double __cdecl sin(double)
will be decorated as _sin
, and double __stdcall sin(double)
will be decorated as _sin@8
.
But things are not that simple. The decorations could change when they
appear in DLLs or when they are produced by different compilers. The following
table lists the names as are produced by MSVC, MinGW, Digital Mars C/C++
Compiler (DMC), Borland C++ Compiler/C++ Builder (BCC):
Calling Convention | Internal* | MSVC DLL (w/ DEF) | MSVC DLL (dllexport) | DMC DLL | MinGW DLL | BCC DLL |
__stdcall |
_Function@n |
Function |
_Function@n |
_Function@n |
Function@n |
Function |
__cdecl |
_Function |
Function |
Function |
Function |
Function |
_Function |
* For all but BCC, which has the same naming convention for symbols in code and exported name in DLL.
What a mess (especially when you notice that, for MSVC, whether a
name is exported by a DEF file or by the
This DEF file defines three exports for a testdll.dll: the first one is a __cdecl function, the second one a __stdcall function, and the third one an alias of the first function (the left side of the "=" sign is an exported name and the right side the internal name). The three functions are also assigned ordinals. A function can be called by its name or its ordinal.LIBRARY testdll.dll EXPORTS cdeclFunction @1 _stdcallFunction@8 @2 aliasName = cdeclFunction @3
will becomecl /LD testdll.obj testdll.def
link /out:testdll.dll /dll /implib:testdll.lib /def:testdll.def testdll.obj
_Function@n
; but if we use a DEF
file, the exported name could be either Function
or _Function@n
;
if both names appear, only the undecorated form is used. However, we
can force both forms of exports with the following lines in the EXPORTS
section:
TestFunction = _TestFunction@4 _TestFunction@4 = _TestFunction@4
Nota bene: 1) it seems LIB does not accept aliased forms (it will simply ignore the part after the equal-sign); 2) it assumes all functions in the DEF file __cdecl. The second point lies in the fact that the import library it produces will map each symbol in the DLL to an internal name with an underscore prefixed, i.e., the linker using the import library will try to resolve an undefined symbollib /def:DEF_file
_Function
to the symbol Function
in the DLL. It takes no special care of
the __stdcall calling convention. With some techniques we could
use LIB to produce import libraries for __stdcall functions,
but the caller could only call them by ordinal, not by name. The details
are left as an exercise -shared
option is specially designed to produce DLLs. We could also use the -Wl
option to pass special link options.
Either gcc or ld can accept a DEF file directly on the command line. When a function (say,--add-stdcall-alias Export symbols with and without @nn --kill-at Remove @nn from exported symbols --out-implib <file> Generate import library --output-def <file> Generate a .DEF file for the built DLL
TestFunction@4
) is marked as
__declspec(dllexport), and we have the following line in the
EXPORTS section,
both symbols will be exported to the DLL (LINK has similar behaviour too). This behaviour is different from dllwrap, which we shall talk of immediately.TestFunction = TestFunction@4
and dllwrap will transparently call gcc, ld, and dlltool to fulfil its task. If dllwrap is asked to produce an import librarydllwrap --def DEF_file -o DLL_file OBJ_files [--output-lib LIB_file]
--output-lib
),dlltool works like LIB, and similarly it will ignore the part after the equal-sign in a DEF file, but it has its special features that somehow compensate for this shortcoming:-l --output-lib <outname> Generate an interface library. -D --dllname <name> Name of input dll to put into interface lib. -d --input-def <deffile> Name of .def file to be read in. -U --add-underscore Add underscores to symbols in interface library. -k --kill-at Kill @<n> from exported names.
-U
option
makes the items in the DEF file map to symbols prefixed with an
underscore in the DLL, and
-k
option makes the
items in the DEF file map to symbols stripped of @n
in
the DLL.
/LD
command-line
option of CL:
The resulting DLL will have exported names likecl /LD OBJ_files
_MyFunction@8
,
as is shown in the `MSVC DLL (dllexport)' column
above. To create symbols with no decoration, we must use a DEF file. The
following is an automatic way to create a DEF file from the DLL if
__declspec(dllexport) is used to indicate which functions to
export:
At this step, you may also want to generate a DEF file to make the DLL usable with MinGW source.link /out:DLL_file /dll OBJ_files pexports DLL_file | sed "s/^_\([[:alnum:]_]\+\)@[[:digit:]]\+/\1/" > DEF_file
Once you have the object files and the DEF file, creating the DLL and the import library can be done in one step:pexports DLL_file | sed "s/^_\([[:alnum:]_]\+\)\(@[[:digit:]]\+\)/\1\2/" > DEF_for_gcc
And you are free to use the DLL and the import library now as you wish.link /out:DLL_file /dll /def:DEF_file /implib:LIB_file OBJ_files
If we want to use a DEF file to control which functions to export, we can start by (assuming __declspec(dllexport) is used to indicate which functions to export)gcc -shared -o DLL_file OBJ_files -Wl,--output-def,DEF_file gcc -shared -o DLL_file OBJ_files -Wl,--kill-at dlltool -d DEF_file --dllname DLL_file --output-lib LIB_file --kill-at
to produce a DEF file with exports like "gcc -shared -o DLL_file OBJ_files -Wl,--kill-at,--output-def,DEF_file
Function =
Function@n
@Ordinal
". After editing it to our will, the following commands
will finish the job:
And the import library is now at your hand to use.dllwrap --def DEF_file -o DLL_file OBJ_files sed "s/[[:alnum:]_]\+ *= *//" DEF_file > New_DEF_file dlltool -d New_DEF_file --dllname DLL_file --output-lib LIB_file --kill-at
I am not sure whether I have stated clearly, but I have listed all my findings when I struggled to find out how to use the DLL tools properly and how to deal with __stdcall functions in DLLs. Hope you find it useful.
ACKNOWLEDGEMENT: The MinGW mailing list provided much useful information; Luke Dunstan provided important suggestions and corrections.
2002-8-20, written by Wu Yongwei
2004-9-9, last updated by Wu Yongwei
This work is licensed under a Creative Commons Attribution-ShareAlike 2.5 Licence.