2016年4月2日 星期六

DLL in Mingw

http://www.mingw.org/wiki/sampleDLL

HOWTO Create and Deploy a Sample DLL using MinGW

 

A sample DLL
The DLL we will build will consist of a single source file "example_dll.cpp":
#include 
#include "example_dll.h"

__stdcall void hello(const char *s)
{
        printf("Hello %s\n", s);
}
int Double(int x)
{
        return 2 * x;
}
void CppFunc(void)
{
        puts("CppFunc");
}
void MyClass::func(void)
{
        puts("MyClass.func()");
}
The following header "example_dll.h" declares the interface of the DLL, and is used both when building the DLL and when building an executable that uses the DLL:
#ifndef EXAMPLE_DLL_H
#define EXAMPLE_DLL_H

#ifdef __cplusplus
extern "C" {
#endif

#ifdef BUILDING_EXAMPLE_DLL
#define EXAMPLE_DLL __declspec(dllexport)
#else
#define EXAMPLE_DLL __declspec(dllimport)
#endif

void __stdcall EXAMPLE_DLL hello(const char *s);

int EXAMPLE_DLL Double(int x);

#ifdef __cplusplus
}
#endif

// NOTE: this function is not declared extern "C"
void EXAMPLE_DLL CppFunc(void);

// NOTE: this class must not be declared extern "C"
class EXAMPLE_DLL MyClass
{
public:
        MyClass() {};
        virtual ~MyClass() {};
        void func(void);
};

#endif  // EXAMPLE_DLL_H
Note that the three functions defined by the DLL have been declared slightly differently (for illustration purposes).

Building the DLL

To build the DLL use the following commands:
g++ -c -DBUILDING_EXAMPLE_DLL example_dll.cpp
g++ -shared -o example_dll.dll example_dll.o -Wl,--out-implib,libexample_dll.a

The -DBUILDING_EXAMPLE_DLL compiler option causes the DLL's functions to be declared as "dllexport", meaning that they will be "exported" from the DLL and available to client applications. The "-shared" option tells the linker to create a DLL instead of an .exe, and the "--out-implib" linker option causes an import library to be created, which is used later on.
Note:
The import library created by the "--out-implib" linker option is required iff (==if and only if) the DLL shall be interfaced from some C/C++ compiler other than the MinGW toolchain. The MinGW toolchain is perfectly happy to directly link against the created DLL. More details can be found in the ld.exe info files that are part of the binutils package (which is a part of the toolchain).

Building a client executable

The following source code "example_exe.cpp" demonstrates calling the DLL's functions:
#include 
#include "example_dll.h"

int main(void)
{
        hello("World");
        printf("%d\n", Double(333));
        CppFunc();

        MyClass a;
        a.func();

        return 0;
}
To build the above example program, use the following commands:
g++ -c example_exe.cpp
g++ -o example_exe.exe example_exe.o -L. -lexample_dll

The option -lexample_dll specifies that the executable be linked with the library libexample_dll.a. The option -L. instructs the linker to search for libraries in the current directory, which is necessary because "." is not in the default search path (this requirement can be avoided by placing libraries in the MinGW lib directory).
It is worth mentioning that the same executable may be built without an import library using the following command:
g++ -o example_exe.exe example_exe.o example_dll.dll
If this method works for your application then there is usually no need for an import library.

Building and using a DLL without the dllexport/dllimport attibutes

If you pass the -no-undefined and --enable-runtime-pseudo-reloc options to the linker, you don't have to add dllimport or dllexport attributes to the source code that the DLL is made with ; all functions are imported/exported automatically by default, just like in unices.
Is dllhelpers http://cygutils.fruitbat.org/dll-stuff/index.html still a useful extra reference, or is it just too outdated? -- GregChicares?
Caveat
There is an important thing to note with the above example functions:
Both hello(const char *) and Double(int) are surrounded by
#ifdef __cplusplus
extern "C" {
#endif
and

#ifdef __cplusplus
}
#endif
in the header file. This has the rather important consequence that their exported names use C-style name mangling (i.e. their names are "as is").
The function CppFunc( void ) is not inside an extern "C" {...} block and thus uses C++-style name mangling. This has the consequence that the exported name depends on the compiler used to generate the DLL. It is by design that such generated names are different from compiler to compiler.
The important and often overlooked consequence of this is that from the above DLL only the functions hello(const char *) and Double(int) are seamlessly callable from e.g. MS VC++ while CppFunc(void) is not (assuming the DLL is created by MinGW).
A similar statement goes for C++ classes exported in a DLL, i.e. for the class MyClass. For further reading search the Web for the keyword "ABI" and possible in conjunction with "C++". See also MixObjects.
Note: Someone else might want to provide more links here
A way to circumvent this problem is either using COM or create C-style wrapper functions to encapsulate the C++ ABI.

http://www.mingw.org/wiki/CreateImportLibraries

HOWTO Create an Import Library for a DLL using MinGW

This page is about how to "call into" an existing DLL, using your mingw program. for information on creating your own DLL library, see DLL.
Usually (read: for all DLLs created with MinGW and also a few others) MinGW links fine against a DLL. No special import library is necessary (see sampleDLL).
However there are situations when it won't. Then one needs a so-called import library to help the linker. The rest of this page gives hints as to how to create such import libraries when there is only the DLL available.
MinGW comes with a handy tool which does most of the work for you:
dlltool.exe (or i586-mingw32-dlltool or something similar in linux cross compile). Issue 'dlltool --help' to get a brief description of the available options.
Sometimes dlltool can create an import library from the DLL. But then you wouldn't need an import library since the linker wouldn't need one. So you probably are reading this because you've got a DLL against which MinGW can't link without further help. You will provide this help by creating a custom def-File. There are several ways to create such a file. In the following some of these will be described:
run 'dlltool -z somedll.def --export-all-symbol somedll.dll' (without 's)
chances are high that you'll get something like 'dlltool: somedll.dll: no symbols'. However, the resulting def-File is a good starting point and probably will need only little additional tweaking.
use your favorite texteditor and create a file somedll.def with the following lines:
LIBRARY SOMEDLL.DLL
EXPORT

Here is at least the list of unsatisfied external references the linker did complain about and that are supposed to be in somedll.dll. The list is given one name per line.
Once you've created the def-File (see above) you'll issue

    dlltool -d somedll.def -l libsomedll.a
Depending on the DLL you may want to try one or more of the following:
add option -U
This will prefix all symbols in the import library with an underscore '_'
add option -k
This removes @ from the exported names but their calling convention remains stdcall.
add option -A
This adds an alias for all stdcall entries but without the @, i.e. you get both the undecorated names (as with -k) and the decorated names.
For a detailed description of these options and dlltool take a look at the corresponding documentation.
That's it. The resulting file libsomedll.a should satisfy the linker.
Some Examples
def-file for an external lib created with Delphi
LIBRARY Diverg01.dll
EXPORTS
Divergences
LTDivergences
STDivergences
Excerpt from a def-file for IBM's db2cli.dll
LIBRARY DB2CLI.dll
EXPORTS
SQLAllocConnect@8
SQLAllocEnv@4
SQLAllocHandle@12
SQLAllocStmt@8
SQLBindCol@24
SQLBindFileToCol@32
SQLBindFileToParam@32
SQLBindParam@32
SQLBindParameter@40
SQLBrowseConnect@24
SQLBrowseConnectW@24
SQLBuildDataLink@40
SQLBulkOperations@8
...
Note that you can also use the pexports command to help (see the Python example in the FAQ).
Note also that some dll's may just *not be compatible* -- for example if size differences occur between long long in the different compilers, and you attempt to pass out structs that have long longs. MSVC is known to have different lengths than gcc for long double, some of its versions of it have a difference for size_t, as well (other discrepancies may be lurking, as well).

 

  http://www.mingw.org/wiki/MSVC_and_MinGW_DLLs

MSVC and MinGW DLLs

Assume we have a testdll.h, testdll.c, and testmain.c. In the first case, we will compile testdll.c with MinGW, and let the MSVC-compiled testmain call it. You should use
gcc -shared -o testdll.dll testdll.c -Wl,--output-def,testdll.def,--out-implib,libtestdll.a
to produce the DLL and DEF files. MSVC cannot use the MinGW library, but since you have already the DEF file you may easily produce one by the Microsoft LIB tool:
lib /machine:i386 /def:testdll.def
Once you have testdll.lib, it is trivial to produce the executable with MSVC:
cl testmain.c testdll.lib
Now for MinGW programs calling an MSVC DLL. We have two methods. One way is to specify the LIB files directly on the command line after the main program. For example, after
cl /LD testdll.c
use
gcc -o testmain testmain.c testdll.lib
The other way is to produce the .a files for GCC. For __cdecl functions (in most cases), it is simple: you only need to apply the reimp tool from Anders Norlander (since his web site is no longer available, you may choose to download
[this version|http://wyw.dcweb.cn/download.asp?path=&file=reimp_new.zip]
enhanced by Jose Fonseca):
reimp testdll.lib
gcc -o testmain testmain.c -L. -ltestdll
However, for __stdcall functions, the above method does not work. For MSVC will prefix an underscore to __stdcall functions while MinGW will not. The right way is to produce the DEF file using the pexports tool included in the mingw-utils package and filter off the first underscore by sed:
pexports testdll.dll | sed "s/^_//" > testdll.def
Then, when using dlltool to produce the import library, add `-U' to the command line:
dlltool -U -d testdll.def -l libtestdll.a
And now, you can proceed in the usual way:
gcc -o testmain testmain.c -L. -ltestdll
Hooray, we got it.
This guide may also be helpful.
Here is an example of creating a .lib file from a mingw dll, in order to be able to use it from MSVC.

http://mirrors.zoreil.com/webclub.kcom.ne.jp/ma/colinp/win32/dll/make.html

Creating Dynamic Link Libraries

.def Files and Export Lists

When you want to create a DLL you must generate an export list, which lists all of the functions which you will allow programs to call directly. It is a good idea to keep the export list for any one DLL to a managable size. You create an export list by writing a .def file, which you can use as input to the dlltool program to create import libraries for your DLL and to create the special export table that needs to be linked into your DLL.
A .def file looks like this:
EXPORTS
Foo
Bar
That simply says that there are two functions, Foo and Bar, which are exported by the DLL in question. Generally you won't have to get much more sophisticated than that.

Generating an Import Library

For other programs to be able to use your DLL you need to provide an import library which can be linked to those programs (or other DLLs for that matter). The tool for generating import libraries from .def files is called dlltool and is discussed in its own section in the introduction to GNU programming tools.

DLL Initialization in DllMain

When using Mingw32 to build DLLs you have the option to include a function called DllMain to perform initialization or cleanup. In general there will be an entry point function of some sort for any DLL (whether you use Mingw32 or not). In Mingw32 this entry point function is supplied by the DLL startup code automatically linked to a DLL, and that startup code calls DllMain. There is a default DllMain which will be called if you don't supply one in your own code.
DllMain might look something like this:
BOOL WINAPI
DllMain (HANDLE hDll, DWORD dwReason, LPVOID lpReserved)
{
    switch (dwReason)
    {
        case DLL_PROCESS_ATTACH:
            // Code to run when the DLL is loaded
            break;

        case DLL_PROCESS_DETACH:
            // Code to run when the DLL is freed
            break;

        case DLL_THREAD_ATTACH:
            // Code to run when a thread is created during the DLL's lifetime
            break;

        case DLL_THREAD_DETACH:
            // Code to run when a thread ends normally.
            break;
    }
    return TRUE;
}
You put your code where the comments are. Notice that the function always returns TRUE. This is really only important when dwReason is DLL_PROCESS_ATTACH. If your code returns FALSE here that means that the DLL failed to initialize properly (for example, it couldn't allocate some memory it needed). The DLL will not be loaded and DllMain will be called again with DLL_PROCESS_DETACH immediately. If this DLL is being loaded automatically (instead of with the LoadLibrary function) that will make the loading of the entire program fail.

DllMain in C++ Modules

If you are writing code in C++ you should note that DllMain must not be "name-mangled" like normal C++ functions are (name mangling is a process that makes it possible to have two functions with the same name in C++ as long as they have different arguments). You can prevent the mangling of the DllMain function by adding
extern "C"
Before the name DllMain in the code above. You could also put DllMain in a separate .c source code file (but then you would have to worry about any calls made from inside DllMain to C++ code).
If you do not force DllMain to be a C function it will not be found by the call in the Mingw32 start up code, and the default DllMain in libmingw32.a will get called instead. If your initialization code doesn't seem to be called, and you are programming in C++, check that your DllMain function is either in a separate C source file or is forced to be a C function with extern "C".

Linking a DLL

Now, if you have created your .def file and have your object files ready, you can create a DLL. I have to warn you though, it's not pretty:
gcc -mdll -o junk.tmp -Wl,--base-file,base.tmp bar.o
del junk.tmp
dlltool --dllname bar.dll --base-file base.tmp --output-exp temp.exp --def bar.def
del base.tmp
gcc -mdll -o bar.dll bar.o -Wl,temp.exp
del temp.exp
This creates a relocatable DLL called bar.dll from the object file bar.o and the .def file bar.def. Relocatable DLLs are better, because they can be placed anywhere in memory and you don't have to worry about whether two DLLs in your program will be fixed at the same base address (see below).
The first line generates an output file which we will discard, called junk.tmp, as well as a base file called base.tmp. A base file gives the information necessary to generate relocation information for the DLL. This step would not be strictly necessary if you didn't want a relocatable DLL, but it is better this way, really.
After removing the junk.tmp file the third line invokes dlltool to build the temp.exp file, which contains the export table to be linked with the DLL, as well as putting the relocation information in a form which can be linked in and used by the operating system to relocate the DLL (and also putting it in the temp.exp file). If you wanted to be really ambitious I think you could output the import library at the same time, but why make things more complex then they already are?
If you are exporting PASCAL functions without the @nn extensions remember that -k doesn't seem to work in that line. See the below section for what you have to put in the .def file to get this to work.
Since the base file is no longer needed the fourth line deletes it, and then the fifth line performs the final linking of the object files containing the functions in the DLL and the export table into the form of a complete DLL file. Then we delete the export table file since we no longer need it. If all goes well you should have a DLL file ready for use.
Probably you will want to automate this process using a make file or some such method. With the Mingw32 version of Jam the above process could be done from compiling bar.c to finished DLL and import library with the following line in a Jamfile:
Dll bar.dll : bar.c : bar.def ;

Standard Call or PASCAL Functions

I talked a little about standard call or PASCAL calling convention functions on the last page, but now I'll talk about them a bit more. A calling convention is a way of arranging information in memory, that is, on the "stack", before calling a function, as well as a standard for who gets rid of the information on the stack after it has been used. The C language uses a convention where after a function returns the caller cleans up the mess.
The PASCAL convention works sort of the other way around. The function called cleans up the mess before it returns. The C convention has the advantage that the called function doesn't need to know how many arguments were passed (which makes the magic of printf, and other variable argument functions, possible). The PASCAL convention makes for smaller code, because the code to clean up the arguments is only in one place (the function), instead of in every function that calls the given function.
GCC can generate function calls, and functions, that use the PASCAL calling convention using the special __attribute__((stdcall)) keyword sequence. In fact, the GNU Win32 API headers define PASCAL, WINAPI and STDCALL constants to all be that magic keyword sequence.
The problem for DLL writers is that internally GCC mangles PASCAL functions with an at symbol (@) plus a number indicating the number of bytes in the argument list. This prevents you from making a call to such a function with the wrong number of arguments, which would have that function destroying part of your stack, causing crashes and mysterious errors. Instead you get a link time error if you don't use the right prototype. However, many times DLLs will export names without that @nn addition, and there is no way to figure out what the number should be without looking at the prototype of the function and counting bytes, or trying to link a program which uses the function and seeing the number in the linker error. Both of these methods are tedious if you have a lot of functions to deal with.
If you want to create an import library where the DLL gives names without the @nn but those functions are PASCAL then you need to add the @nn to the function names in the .def file. For example if the above Foo and Bar functions were PASCAL functions that took single integer arguments the .def file might look like this:
EXPORTS
Foo@4
Bar@4
Then you need to use the -k option for dlltool telling it to kill the @nn for the exported function names (but keep it for internal use). E.g.:
dlltool --dllname bar.dll --def bar.def --output-lib libbar.a -k
That takes care of the program's side of the problem. The program linked with the produced library will look for the names without the associated @nn extension, and if the library exports the names without @nn they will be found.
Unfortunately, when building a DLL the -k option doesn't work! This line (see the section on linking a DLL above)
dlltool --dllname bar.dll --base-file base.tmp --output-exp temp.exp --def bar.def -k
doesn't remove the @nn from the exported names in the created DLL (this is a bug in my opinion). Fortunately you can use the aliasing feature of dlltool to get this to work. Change your .def file to look like this:
EXPORTS
Foo@4
Foo=Foo@4
Bar@4
Bar=Bar@4
What the extra lines say are "export a symbol Foo (or Bar) pointing at the internal symbol Foo@4 (or Bar@4)." The import library produced by this .def file will have four internal symbols (available for use by your programs): Foo, Foo@4, Bar and Bar@4, but if -k is used Foo and Foo@4 both link to the exported symbol Foo, and similarly for Bar and Bar@4. The DLL produced using this .def file will export four names, Foo, Foo@4, Bar and Bar@4. The exported symbols all point to the internal @nn names.
The program will generate internal calls to Foo@4 and Bar@4, which will be resolved in the import library. Those symbols in the import library are, in turn, linked to exported names Foo and Bar. The DLL exports Foo and Bar symbols which point to the internal symbols Foo@4 and Bar@4, so everything works out in the end.
If you've really been paying attention closely you may notice that the above process is a little risky. If your program does not properly prototype the functions as STDCALL (or PASCAL) then it will try to link with Foo and Bar, and it will find them! However, they will be connected to the same STDCALL functions in the DLL as Foo@4 and Bar@4. Your program will corrupt the stack every time it calls Foo or Bar, and probably crash shortly afterwards.
To be completely correct and safe you have to use two .def files. One .def file for creating the import library and one for building the DLL. The import library .def file (call it bar_i.def) looks like this:
EXPORTS
Foo@4
Bar@4
same as before, and the DLL building .def file (call it bar_e.def) looks like this:
EXPORTS
Foo=Foo@4
Bar=Bar@4
Now when you build the import library using the first .def file like so:
dlltool --dllname bar.dll --def bar_i.def --output-lib libbar.a -k
it will contain only the symbols Foo@4 and Bar@4, which are linked to the external names Foo and Bar.
Then when you build the DLL with bar_e.def like this:
dlltool --dllname bar.dll --base-file base.tmp --output-exp temp.exp --def bar_e.def
the DLL exports the internal symbols Foo@4 and Bar@4 with only the names Foo and Bar. The DLL and the import library now match up exactly, so the only errors you should get will be linking errors... unless you forget the -k when building an import library, or use the wrong .def file somewhere...

http://mirrors.zoreil.com/webclub.kcom.ne.jp/ma/colinp/win32/dll/use.html

Using Dynamic Link Libraries


Using DLLs In Your Programs

In order to use a DLL you must have the appropriate header files and/or prototypes for the functions you want to call, plus the DLL of course, and a special library called an import library. An import library is a lot like a normal library except that instead of normal functions it contains an import table like I described in the introduction, and a short function with the name of the function you really want to call which looks up the correct address in the import table and calls the real DLL function. By linking your program with an import library you gain access to the functions exported by the DLL for which the import library was created.
Using an import library is just like linking with an ordinary library.
I talk about creating import libraries for your own DLLs in the section on creating DLLs. But sometimes you don't have an import library for a DLL that someone else gave you, which is what the next section is about.

Using Other People's DLLs

What if you have a DLL but you don't have an import library for it? At the moment this is the case for a lot of commercial DLL libraries which provide import libraries for Microsoft compatible compilers. At this time gcc does not understand Microsoft .lib files correctly (although that will be fixed in the future). To use the DLL you have to generate an import library that GCC will understand. You do that using dlltool in the same way as you generate import libraries for your own programs. But before you can do that you need a .def file. Fortunately there is a tool for automatically generating .def files called impdef.exe which is discussed in the section for DLL import library tools in the introduction to GNU programming tools.
Note, however, that impdef isn't perfect. Some newer DLLs confuse it so that it may not be possible to automatically generate the .def file, or the file won't work without changes. Also, gcc requires that functions with PASCAL calling convention (also called STDCALL or WINAPI functions) to have @nn added to the end of the function name, where nn is the number of bytes passed on the stack to the function in question (if you didn't understand that don't worry, just think of it as a magic number). Sometimes the functions will be exported with such "mangled" names, in which case things should be fine (as long as you don't use the -k option for dlltool). But in most cases, including almot all of the Win32 API functions, the mangled version is not exported. In this case you have to add the @nn on to each function name manually. You can generally determine the correct number by writing a program that uses the function and trying to link it. gcc will report the correct number in its linker error message (if the prototype is done correctly). You also have to use the -k option when creating the import library with dlltool, which I talk about a bit more in the section on creating DLLs.
There are other tools that will show you the exports for a DLL, including TDUMP from Borland and DUMPBIN (I think) from Microsoft. Also, using Explorer in Windows 95 you can Quick View a DLL, and that will also give you a list of its exports. Unfortunately you can't save it to a file or copy it to the clipboard, so it's very inconvenient for creating import libraries.

Global Variables in DLLs

With Win32 Microsoft introduced the idea that DLLs could contain global variables which you could access the same way you can access a function. The variables are also listed in the import/export tables with the functions, but instead of pointing to a function's code the variable entries point to the actual spot in memory where the variable is stored.
Microsoft made a special keyword for Visual C++ which makes the compiler generate the code to automatically follow the pointer in the import table and get or set the real value of such a variable whenever you manipulate it in your program. EGCS 1.1 and above should allow you to use the __declspec (dllexport) and __declspec (dllimport) syntax to use such variables. You can also access such variables by performing a little magic in your header files, if you are using another version of gcc for example. Say there was a variable foobar exported by a DLL you use. Here's how you could use it in your programs (assuming foobar is an int):
extern int* _imp__foobar;
#define foobar (*_imp__foobar)
Then you can go ahead and use foobar just like it was the same variable all through your program, and the DLL will see the changes you make in the variable as well. But, before you start doing this all over the place a few words of warning:
  • Older versions of dlltool used __imp_ as the prefix instead of _imp__. The new prefix is more compatible with Microsoft, and dlltool includes both for backward compatibility, but these type of changes show just how dangerous such a kludge can be.
  • Using defines like that can cause strange errors, because define has no concept of scope. So, for example, a C++ member function called foobar might get inappropriately mangled by the define.
  • You have to include the header file to access the variable, unlike normal global variables which you can declare inside individual source files or even functions.
  • You can't use the same header file for making the DLL itself, since it must deal directly with the foobar variable.
All things considered, global variables should be avoided in any case, and even more so when you are dealing with dynamic linking. Still, it is possible to work with global variables in a DLL, but just because you can do something doesn't mean you should do it.
 

http://www.willus.com/mingw/yongweiwu_stdcall.html

Stdcall and DLL tools of MSVC and MinGW

The __stdcall calling convention has been there for a very long time. When the older calling conventions like __pascal fell into oblivion, __stdcall became the standard calling conventions 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 __declspec(dllexport) attribute affects its naming decoration)! And although the ending decoration clearly shows how many bytes the called function pops up from stack beforereturning, it is not necessarily the most frequently used form. E.g., the Win32 API functions in the system DLLs have no decoration at all, as in the case ones uses a DEF file when exporting functions with MSVC. Many DLLs intended for multi-language usage also follow this practice and use no decoration with __stdcall functions (although the Java Native Interface, on Win32 platforms, will accept functions either undecorated or decorated in the Microsoft way, the latter being the preferred form). The remaining part of this article is thus devoted to the creation and use of such DLLs with MSVC and MinGW, as well as the introduction and comparison of related tools (there are good articles at http://www.bcbdev.com/articles.htm explaining the complexities of DLLs with Borland C++ Builder, so I need not bother to say anything more about it).

Tools working with DEF files

First, I will talk about the DEF file format and the relevant tools used with MSVC and MinGW. Many intricacies lie here.

DEF file format

We care about only two sections of the DEF file: the LIBRARY section and the EXPORTS section. The LIBRARY section specifies the internal name of the DLL; and the EXPORTS section specifies the function or data items to export. A short example follows:
LIBRARY    testdll.dll
EXPORTS
    cdeclFunction                       @1
    _stdcallFunction@8                  @2
    aliasName = cdeclFunction           @3
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 three functions are also assigned ordinals. A function can be called by its name or its ordinal.

CL

CL can accept a DEF file on the command line, and it simply passes the file name to LINK. E.g.,
cl /LD testdll.obj testdll.def
will become
link /out:testdll.dll /dll /implib:testdll.lib /def:testdll.def testdll.obj

LINK

LINK is our most important tool when treating DLL and DEF files with MSVC. The command line mentioned in CL already shows the options commonly used when creating a DLL with a DEF file. The main point is: if we do not use a DEF file when creating a DLL, the exported name of a __stdcall function will be _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. But it is possible to get both forms of exports (similar to the --add-stdcall-alias option of GNU ld and dllwrap), when we have the following line in the EXPORTS section (notice that the order cannot be reversed):
TestFunction = _TestFunction@4
Another thing to notice when we use the above line: LINK will export both TestFunction (or any alias name) and _TestFunction@4 from the DLL, but only _TestFunction@4 (the internal name) will be output to the import library: in no case will a program linked with the import library call the function by TestFunction!

LIB

If we have the DLL from somebody else (no source available), and we have the DEF file, the easiest way to create an import library is to use the LIB tool. The following syntax is often enough (check MSDN for more details):
lib /def:DEF_file
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 symbol _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 :-).

gcc

Here we use gcc to call ld. The reason why we do not use ld directly is that using gcc is generally more convenient. The -shared option is specially designed to produce DLLs. We could also use the -Wl option to pass special link options.

ld

GNU ld has many options regarding DLLs, but we shall only focus on four (help information follows):
--add-stdcall-alias                Export symbols with and without @nn
--kill-at                          Remove @nn from exported symbols
--out-implib                 Generate import library
--output-def                 Generate a .DEF file for the built DLL
Either gcc or ld can accept a DEF file directly on the command line. When we have the following line in the EXPORTS section,
TestFunction = TestFunction@4
both symbols will be exported to the DLL. This behaviour is different from dllwrap, which we shall talk of immediately.

dllwrap

GNU dllwrap could produce a DLL by a DEF file. We generally use dllwrap in the following syntax,
dllwrap --def DEF_file -o DLL_file OBJ_files [--output-lib LIB_file]
and dllwrap will transparently call gcc, ld, and dlltool to fulfil its task. If dllwrap is asked to produce an import library (--output-lib), it will let dlltool do it. Unlike LINK or ld, when dllwrap encounters a line in the EXPORTS section like
TestFunction = TestFunction@4
it will export TestFunction but not TestFunction@4, and only TestFunction is output to the import library (somehow similar to LIB).

dlltool

GNU dlltool may be used to create the files needed to build and use dynamic link libraries (DLLs). The following options are of interest to us currently:
-l --output-lib  Generate an interface library.
-D --dllname        Name of input dll to put into interface lib.
-d --input-def   Name of .def file to be read in.
-U --add-underscore       Add underscores to symbols in interface library.
-k --kill-at              Kill @ from exported names.
dlltool works like LIB, but it has its special feature: the -U option makes the items in the DEF file map to symbols prefixed with an underscore in the DLL, and the -k option makes the items in the DEF file map to symbols stripped of @n in the DLL.

pexports

This is a stand-alone open-source tool to produce a DEF file from a given DLL. It is not distributed with MSVC or MinGW, and you may choose to download here if you do not find it elsewhere.

The __stdcall DLL and the import library

Having learnt so much about the tools, now we are ready to do what we wanted. We still need sed (search on the Internet if you do not already have this useful tool), and a knowledge of regular expression is required to understand thoroughly how it works.

Microsoft Visual C++

The simplest way to produce a DLL is to use the /LD command-line option of CL:
cl /LD OBJ_files
The resulting DLL will have exported names like _MyFunction@8, as is shown in the `MSVC DLL (no DEF)' 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:
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:
link /out:DLL_file /dll /def:DEF_file /implib:LIB_file OBJ_files
And you are free to use the DLL and the import library now as you wish.

MinGW GCC

If we do not need to control which functions to export except by __declspec(dllexport), we can type:
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
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,--kill-at,--output-def,DEF_file
to produce a DEF file with exports like "Function = Function@n @Ordinal". After editing it to our will, the following commands will finish the job:
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
And the import library is now at your hand to use.
  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.

http://www.transmissionzero.co.uk/computing/building-dlls-with-mingw/

Building Windows DLLs with Mingw


Introduction

One question I often get asked is how to create Windows DLLs using MinGW. Under MSVC it’s pretty simple, as Visual Studio will create a barebones project with everything you need. With MinGW a bit of manual work is needed, but it’s not that difficult when you know how it’s done.

This article will show you the basics of creating DLLs with MinGW, and then linking to them in your applications. The code we produce here will be usable in MSVC as well, so you or your end users can target multiple compilers.
DLL Basics

A DLL is a type of shared library used on Microsoft Windows and OS/2, containing functions which can be reused in any application which wants to make use of them (actually, DLLs can contain a lot more than just functions, but this article is about creating libraries of functions). If you have a library foo.dll which contains a function DoWork(), the DLL must export that function in order for it to be used by applications. Likewise, if an application bar.exe wants to make use of the function DoWork(), it must import that function from foo.dll (it is also possible to write code which loads a DLL once an application has started running, calls functions from that DLL, and then unloads the DLL when it’s no longer useful—this is how plugin / addon systems usually work). The exporting and importing of functions is fairly straightforward, and just involves a little bit of code accompanied by the correct linker command line.
The Inflexible and Inelegant Way

First we will create a DLL exporting only a very basic function which adds two integers and returns the result. Notice the “__declspec(dllexport)” attribute in the code below—this is the key to exporting functions from the DLL, and every function you would like exported from the DLL should be marked with this (any internal functions which don’t make up part of your API should be left as they are). Also notice the “__cdecl” before the function name, which declares the calling convention for the function (see the Wikipedia article on x86 calling conventions if you’re not familiar with them). The default calling convention for C functions in MinGW is cdecl, but it’s a good idea to always explicitly state the calling convention in case some other compiler has a different default—if this happens, your application will likely misbehave or crash as a result of calling one of these functions. Note that this first example is deliberately inflexible and inelegant in order to show exactly what’s going on behind the scenes. In the next section we will be hiding these details using preprocessor definitions.

/* add_basic.c

   Demonstrates creating a DLL with an exported function, the inflexible way.
*/

__declspec(dllexport) int __cdecl Add(int a, int b)
{
  return (a + b);
}

To compile this, it’s just necessary to pass the option “-shared” to the usual linker command line:

z:\Users\mpayne\Documents\MinGWDLL>gcc -c -o add_basic.o add_basic.c

z:\Users\mpayne\Documents\MinGWDLL>gcc -o add_basic.dll -s -shared add_basic.o -Wl,--subsystem,windows

No errors should be reported, and you should now have a usable DLL. I’ve separated the compiler and link step in this example, although for such a small project it could have been done simply with “gcc -o add_basic.dll -s -shared add_basic.c -Wl,--subsystem,windows”. The “-Wl,--subsystem,windows” isn’t really necessary, but it’s just conventional that DLLs have the Windows GUI subsystem specified in their PE header. Also note the “-s” switch is used to strip symbols from the DLL—you’ll probably want to do this only for release builds.

You can now build an application which uses this DLL. All that is needed in order to use the function Add(a, b) is to declare the function with the “__declspec(dllimport)” attribute before using it (note that it’s dllimport for the client application, as opposed to the dllexport used in our DLL).

/* addtest_basic.c

   Demonstrates using the function imported from the DLL, the inelegant way.
*/

#include
#include

/* Declare imported function so that we can actually use it. */
__declspec(dllimport) int __cdecl Add(int a, int b);

int main(int argc, char** argv)
{
  printf("%d\n", Add(6, 23));

  return EXIT_SUCCESS;
}

You can now compile and link this application, simply by referencing our DLL on the linker command line.

z:\Users\mpayne\Documents\MinGWDLL>gcc -c -o addtest_basic.o addtest_basic.c

z:\Users\mpayne\Documents\MinGWDLL>gcc -o addtest_basic.exe -s addtest_basic.o -L. -ladd_basic

z:\Users\mpayne\Documents\MinGWDLL>addtest_basic.exe
29

The “-L” option specifies an additional folder that the linker should search for the DLL. In this case we want the linker to search the current directory, so we can just use a period, but for a real DLL which we have deployed to a system we would use the full path to its directory. The “-l” option specifies a DLL we want to import functions from—this should be the DLL filename without the file extension. Again, for such a small project the application could have been compiled and linked by doing a “gcc -o addtest_basic.exe -s addtest_basic.c -L. -ladd_basic”
The Flexible and Elegant Way

The above demonstrates very well what’s going on, but it’s less than ideal—in a real application you wouldn’t want to declare every single imported function in every single source code file where it’s used. Instead you would place the declarations in a header file and #include the header where needed. The only problem with this is that the client application requires the functions be declared “__declspec(dllimport)”, whereas when building the DLL you must declare them “__declspec(dllexport)”. Although you could use two separate headers, this can be a bit of a maintenance headache, so instead we use some preprocessor definitions.

/* add.h

   Declares the functions to be imported by our application, and exported by our
   DLL, in a flexible and elegant way.
*/

/* You should define ADD_EXPORTS *only* when building the DLL. */
#ifdef ADD_EXPORTS
  #define ADDAPI __declspec(dllexport)
#else
  #define ADDAPI __declspec(dllimport)
#endif

/* Define calling convention in one place, for convenience. */
#define ADDCALL __cdecl

/* Make sure functions are exported with C linkage under C++ compilers. */

#ifdef __cplusplus
extern "C"
{
#endif

/* Declare our Add function using the above definitions. */
ADDAPI int ADDCALL Add(int a, int b);

#ifdef __cplusplus
} // __cplusplus defined.
#endif

Notice that we have defined “ADDAPI” as “__declspec(dllexport)” if “ADD_EXPORTS” is defined, or “__declspec(dllimport)” otherwise. This is what allows us to use the same header for the applications and the DLL. Also notice we have defined “ADDCALL” as “__cdecl”, which allows easy redefinition of the calling convention we are using for our API. Now, anywhere in our code where we need to export a function from our DLL, we specify “ADDAPI” instead of a “__declspec(dllexport)” attribute, and “ADDCALL” instead of specifying, i.e. “__cdecl”. It’s conventional to name these preprocessor definitions “[library name]_EXPORTS”, “[library name]API”, and “[library name]CALL”, so it’s a good idea to stick to this so your code is readable by yourself and others.

Finally, note that the imported / exported functions should all be wrapped in an “#ifdef __cplusplus” block containing an “extern "C"” statement. This allows the header to be used by both C and C++ applications—without it a C++ compiler will use C++ function name mangling, which would cause the linker step to fail.

Bear in mind that this code isn’t portable, because it’s using Microsoft specific attributes in the code. That’s ok here because this is a tutorial about building Windows DLLs, but if you need cross-platform compatibility then ADDAPI and ADDCALL can be defined conditionally. Typically it would be done like this:

#ifdef _WIN32

  /* You should define ADD_EXPORTS *only* when building the DLL. */
  #ifdef ADD_EXPORTS
    #define ADDAPI __declspec(dllexport)
  #else
    #define ADDAPI __declspec(dllimport)
  #endif

  /* Define calling convention in one place, for convenience. */
  #define ADDCALL __cdecl

#else /* _WIN32 not defined. */

  /* Define with no value on non-Windows OSes. */
  #define ADDAPI
  #define ADDCALL

#endif

The code for the DLL can now include the header we just created, and the only change we need to make is to include “ADDCALL” before the function name (it’s necessary to specify the calling convention here, to prevent possible conflicts between the declaration and definition of the function).

/* add.c

   Demonstrates creating a DLL with an exported function in a flexible and
   elegant way.
*/

#include "add.h"

int ADDCALL Add(int a, int b)
{
  return (a + b);
}

To compile this DLL, it’s necessary to define “ADD_EXPORTS” when compiling the object code, to ensure “ADDAPI” is correctly defined in the header. This is done most easily by passing a “-D ADD_EXPORTS” on the command line:

z:\Users\mpayne\Documents\MinGWDLL>gcc -c -o add.o add.c -D ADD_EXPORTS

z:\Users\mpayne\Documents\MinGWDLL>gcc -o add.dll add.o -s -shared -Wl,--subsystem,windows

The client application code is much the same as before, except that we now #include the header file we created rather than declare the function in the source file.

/* addtest.c

   Demonstrates using the function imported from the DLL, in a flexible and
   elegant way.
*/

#include
#include
#include

int main(int argc, char** argv)
{
  printf("%d\n", Add(6, 23));

  return EXIT_SUCCESS;
}

To compile the application, no change is needed to the command line we use:

z:\Users\mpayne\Documents\MinGWDLL>gcc -c -o addtest.o addtest.c

z:\Users\mpayne\Documents\MinGWDLL>gcc -o addtest.exe -s addtest.o -L. -ladd

z:\Users\mpayne\Documents\MinGWDLL>addtest.exe
29

This is the build process that many real world libraries use. If it’s not clear what’s going on with the preprocessor definitions, pass “-save-temps” on the gcc command line during the compilation stage and look at the files with a “.i” extension—you’ll notice that ultimately the generated code in both elegant and inelegant examples is almost identical.
Exporting and Importing Variables

In addition to functions, it’s also possible to export and import variables. These variables must be declared “extern __declspec(dllexport)” or “extern __declspec(dllimport)”, depending on whether we are building the DLL or a client application using these variables. Similarly to functions, we can use our preprocessor definitions and simply delcare the variables “extern ADDAPI”. A calling convention should not be specified for variables.

Here we have added two exported variables to our header file, foo and bar.

/* add_var.h

   Declares a function and variables to be imported by our application, and
   exported by our DLL.
*/

/* You should define ADD_EXPORTS *only* when building the DLL. */
#ifdef ADD_EXPORTS
  #define ADDAPI __declspec(dllexport)
#else
  #define ADDAPI __declspec(dllimport)
#endif

/* Define calling convention in one place, for convenience. */
#define ADDCALL __cdecl

/* Make sure functions are exported with C linkage under C++ compilers. */
#ifdef __cplusplus
extern "C"
{
#endif

/* Declare our Add function using the above definitions. */
ADDAPI int ADDCALL Add(int a, int b);

/* Exported variables. */
extern ADDAPI int foo;
extern ADDAPI int bar;

#ifdef __cplusplus
} // __cplusplus defined.
#endif

The code for the DLL now includes the assignment of values to our exported variables:

/* add_var.c

   Demonstrates creating a DLL with an exported function and imported variables.
*/

#include "add_var.h"

int ADDCALL Add(int a, int b)
{
  return (a + b);
}

/* Assign value to exported variables. */
int foo = 7;
int bar = 41;

The application has been modified so that now it adds the imported foo and bar variables together, printing the result:

/* add_vartest.c

   Demonstrates using the function and variables exported by our DLL.
*/

#include
#include

/* Don't forget to change this to #include for real applications where
   the header has been deployed to a standard include folder!
*/
#include "add_var.h"

int main(int argc, char** argv)
{
  /* foo + bar = Add(foo, bar) */
  printf("%d + %d = %d\n", foo, bar, Add(foo, bar));

  return EXIT_SUCCESS;
}

Compilation is the same as before:

z:\Users\mpayne\Documents\MinGWDLL>gcc -c -o add_var.o add_var.c -D ADD_EXPORTS

z:\Users\mpayne\Documents\MinGWDLL>gcc -o add_var.dll add_var.o -s -shared -Wl,--subsystem,windows

z:\Users\mpayne\Documents\MinGWDLL>gcc -c -o add_vartest.o add_vartest.c

z:\Users\mpayne\Documents\MinGWDLL>gcc -o add_vartest.exe -s add_vartest.o -L. -ladd_var

z:\Users\mpayne\Documents\MinGWDLL>add_vartest.exe
7 + 41 = 48

If you change the values of foo and bar, recompile the DLL, and run the application again, you can see that the variables are indeed imported from the DLL.
Import Libraries

Although it’s usually possible to link to a DLL simply by having the DLL present on your system and adding some linker command line options, it may not always be desirable. In this case, an import library should be used instead. An import library contains no code, but does contain all of the necessary information needed for your application to locate the functions exported by the DLL. When creating libraries for third parties to use, I would recommend always creating an import library and distributing it with your DLL and header files, as your users may have a requirement to build their applications using import libraries.

If you have modified the function names that the DLL exports (see “Warning About Exporting stdcall Functions” below), an import library is the only option. This is the case for the Windows API, where you must link your applications using import libraries, i.e. passing “-lgdi32 -luser32” etc. on the linker command line when compiling Windows GUI programs.
Creating and Using an Import Library

If you haven’t modified any of the function names exported by the DLL, then creating an import library is just a case of passing an extra parameter “-Wl,--out-implib,lib[library name].a” to the linker command line.

z:\Users\mpayne\Documents\MinGWDLL>gcc -c -o add.o add.c -D ADD_EXPORTS

z:\Users\mpayne\Documents\MinGWDLL>gcc -o add.dll add.o -s -shared -Wl,--subsystem,windows,--out-implib,libadd.a
Creating library file: libadd.a

It’s conventional to use the library name in the import library name. It’s mandatory that the filename begins with “lib” and ends with a “.a” extension in order for it to be usable (actually there are a few supported naming conventions you can use which are covered in the manual, but anything else won’t work).

Building the application is the same as before, but instead of passing the directory containing the DLL to the linker, you pass the directory containing the import library instead (actually we’re still doing everything from the current directory, but this likely wouldn’t be the case for a real application). To link to the DLL, you pass the name of the import library without the lib prefix and file extension.

z:\Users\mpayne\Documents\MinGWDLL>gcc -c -o addtest.o addtest.c

z:\Users\mpayne\Documents\MinGWDLL>gcc -o addtest.exe -s addtest.o -L. -ladd

z:\Users\mpayne\Documents\MinGWDLL>addtest.exe
29

Typically DLLs go in a folder named “bin”, and import libraries go in a folder named “lib”, for example “C:\Program Files\Add\bin\” and “C:\Program Files\Add\lib\”.
Warning About Exporting stdcall Functions

One thing you should be aware of when declaring functions “__stdcall”, is that MinGW and MSVC export their functions with slightly different names (function decorations). For our add example above, MSVC would export the “Add(int a, int b)” function with the name “_Add@8” (underscore, function name, at sign, size of arguments in bytes), whereas MinGW will export it as “Add@8” (same as MSVC, but without the underscore). Therefore, if you intend to have binary compatibility between MinGW and MSVC builds of your DLL, it’s necessary to change the names of the exported functions. This is a bit more advanced than I intend to cover in this article, but I have written an Advanced MinGW DLL Topics article which explains how to do this, along with some of the pitfalls you should watch out for.
Adding Version Information and Comments to your DLL

If you view the properties for a DLL using Windows explorer and go to the “details” tab, you’ll likely see information about the DLL such as the version, author, copyright, and a description of the library. It’s a nice touch to add to a DLL (particularly helpful when you want to know which version of a DLL you have installed on your PC), and is fairly straightforward to add to your DLL. All you need to do is create a version resource such as the following:

#include

// DLL version information.
VS_VERSION_INFO    VERSIONINFO
FILEVERSION        1,0,0,0
PRODUCTVERSION     1,0,0,0
FILEFLAGSMASK      VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
  FILEFLAGS        VS_FF_DEBUG | VS_FF_PRERELEASE
#else
  FILEFLAGS        0
#endif
FILEOS             VOS_NT_WINDOWS32
FILETYPE           VFT_DLL
FILESUBTYPE        VFT2_UNKNOWN
BEGIN
  BLOCK "StringFileInfo"
  BEGIN
    BLOCK "080904b0"
    BEGIN
      VALUE "CompanyName", "Transmission Zero"
      VALUE "FileDescription", "A library to perform addition."
      VALUE "FileVersion", "1.0.0.0"
      VALUE "InternalName", "AddLib"
      VALUE "LegalCopyright", "©2013 Transmission Zero"
      VALUE "OriginalFilename", "AddLib.dll"
      VALUE "ProductName", "Addition Library"
      VALUE "ProductVersion", "1.0.0.0"
    END
  END
  BLOCK "VarFileInfo"
  BEGIN
    VALUE "Translation", 0x809, 1200
  END
END

I won’t go into detail about what each of these means, as it’s covered very well in the MSDN, but most of it is self-explanatory (I’d suggest reading the MSDN article anyway, particularly if the language is American English or anything other than British English).

The idea is to compile the resource script using windres.exe, and then pass it to the linker when linking your DLL. For example if your resource script is named “resource.rc”:

z:\Users\mpayne\Documents\MinGWDLL>windres -i resource.rc -o resource.o

z:\Users\mpayne\Documents\MinGWDLL>gcc -o add.dll add.o resource.o -s -shared -Wl,--subsystem,windows,--out-implib,libadd.a

That’s all there is to adding version information to a DLL. It’s worth taking the time to do this for any DLLs you release, as it’s both helpful and gives your application a professional look and feel.
Putting It All Together

Using the information in this article, you should now be able to put it all together and create DLLs using MinGW, as well as executables which use the functions exported from the DLLs. To help you out, I’ve created a small project which builds a DLL with the following functionality:

    Exported “int Add(int a, int b);” function, using cdecl calling convention.
    Exported variables “foo” and “bar”.
    Version Information resource embedded into the DLL.

Additionally an executable will be built which prints the result of an addition, using the exported function and exported variables. The project can be built with the mingw32-make utility.

Download the MinGW DLL Example
Things To Be Aware Of

There are a few things to be aware of when exposing a C API from a DLL. Firstly, your code can’t throw any C++ exceptions inside the DLL and expect the client application to catch these exceptions. This shouldn’t be a problem if you are using pure C code because C doesn’t support C++ exceptions, but if you’re coding in C++ and are specifying C linkage for your exported functions, it’s something you need to be careful about. It’s best to catch all exceptions where there is a possibility of an exception being thrown, and return some error code to the caller of the API you’re exposing. If you do throw exceptions across a DLL boundary, you might get lucky and it will work for you, but it will more than likely fail if someone is using your library with a different programming language, a different compiler, or even a different version of the compiler you are using (just because you wrote the library in C, doesn’t mean that someone won’t use it, for example, in an application written in Visual Basic, assembly, D, or C#).

Another thing to be aware of is that if you’re passing structs into a function exported by a DLL, or returning a struct from that function, you can’t later change the definition of that struct. Doing such changes would likely result in older applications either crashing or behaving in unintended ways, because the memory layout of the struct has changed since the application was compiled. It’s not impossible to change the definition of a struct without breaking backwards compatibility, but it’s not elegant either. For example, it’s common in the Windows API that structs have a (seemingly pointless) member which indicates the size of the struct, have new members added at the end of the struct definition, and are passed into functions by address and not value (for example the OPENFILENAME struct). These are the kind of things that allow the Windows API to change between releases of Windows without breaking backwards compatibility (I know people might say that each version of Windows causes many backwards compatibility issues, but the fact is that a well written application created for Windows 95 has a good chance of running on recent versions of Windows, which in my opinion is pretty impressive).

Although it’s possible to export C++ variables and C++ functions from a DLL, you should avoid doing so unless you really understand what you are doing. C++ does not have the well defined ABI that C does, and you’ll likely find that compiled C++ functions created with a compiler from one vendor are not compatible with those from another vendor. In fact, you may find that compatibility is broken between different versions of a C++ compiler from the same vendor. For this reason, each compiler has it’s own way of mangling the names of C++ functions, which prevents functions created with different C++ compilers from being linked together. As disappointing as this is, it prevents potential mayhem which could occur as a result of differences in ABI. I cover C++ DLLs in my Advanced MinGW DLL Topics article.


http://www.transmissionzero.co.uk/computing/advanced-mingw-dll-topics/

Advanced MinGW DLL Topics

 Introduction

This article is a follow-on from my Building Windows DLLs with MinGW article, which gave an introduction into building DLLs with MinGW and using them within an executable. In this article I will cover some more advanced topics which weren’t appropriate for the introductory article:

    Displaying functions exported from a DLL.
    The DllMain function.
    Using a module definition file.
    Exporting undecorated stdcall functions.
    Exporting C++ functions and variables from a DLL.
    Creating JNI DLLs.
    P/Invoking MinGW DLLs in .NET
    Setting the DLL base address.
    Loading and unloading DLLs at runtime.

Displaying Functions Exported From A DLL

If you’d like to view the functions exported by a DLL, you can use the GNU binutils objdump tool. This is useful if you want to see the exact symbols which are being exported from a DLL, including any name mangling. It is also useful if you have a third party DLL and would like to see exactly what it’s exporting. The following is an example of dumping the exports of the “AddLib.dll” from my previous article:

z:\Users\mpayne\Documents\MinGWDLL>objdump -p AddLib.dll

The output will contain something like the following:

There is an export table in .edata at 0x6da46000

The Export Tables (interpreted .edata section contents)

Export Flags                    0
Time/Date stamp                 4da9a500
Major/Minor                     0/0
Name                            00006046 AddLib.dll
Ordinal Base                    1
Number in:
        Export Address Table            00000003
        [Name Pointer/Ordinal] Table    00000003
Table Addresses
        Export Address Table            00006028
        Name Pointer Table              00006034
        Ordinal Table                   00006040

Export Address Table -- Ordinal Base 1
        [   0] +base[   1] 1280 Export RVA
        [   1] +base[   2] 2004 Export RVA
        [   2] +base[   3] 2000 Export RVA

[Ordinal/Name Pointer] Table
        [   0] Add
        [   1] bar
        [   2] foo

You can see the function “Add” is being exported, along with the variables “foo” and “bar”. You don’t get any more information than this, for example the number of parameters and their types. In fact, you don’t even know if a name represents a function or variable. That’s because each name represents an address in memory, which could be a pointer to anything.

If you have the Microsoft Windows SDK, you can also use the dumpbin utility to display exported functions:

z:\Users\mpayne\Documents\MinGWDLL>dumpbin -exports AddLib.dll
Microsoft (R) COFF/PE Dumper Version 9.00.30729.01
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file AddLib.dll

File Type: DLL

  Section contains the following exports for AddLib.dll

    00000000 characteristics
    4DA9A500 time date stamp Sat Apr 16 15:17:36 2011
        0.00 version
           1 ordinal base
           3 number of functions
           3 number of names

    ordinal hint RVA      name

          1    0 00001280 Add
          2    1 00002004 bar
          3    2 00002000 foo

  Summary

        1000 .CRT
        1000 .bss
        1000 .data
        1000 .edata
        1000 .eh_fram
        1000 .idata
        1000 .rdata
        1000 .reloc
        1000 .rsrc
        1000 .text
        1000 .tls

As you can see, it’s the same information about the exported functions, but presented in a different format.
The DllMain Function

DLLs can have an entry point named “DllMain”, which is called by the operating system when the DLL is loaded into or unloaded from memory, or when a thread is created or has exited. The format of the functions is as follows:

#include

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
  switch (fdwReason)
  {
    case DLL_PROCESS_ATTACH:
      /* Code path executed when DLL is loaded into a process's address space. */
      break;

    case DLL_THREAD_ATTACH:
      /* Code path executed when a new thread is created within the process. */
      break;

    case DLL_THREAD_DETACH:
      /* Code path executed when a thread within the process has exited *cleanly*. */
      break;

    case DLL_PROCESS_DETACH:
      /* Code path executed when DLL is unloaded from a process's address space. */
      break;
  }

  return TRUE;
}

Note that the function signature should match exactly, as getting the function name or case incorrect will result in the function never being called, and getting the calling convention incorrect can corrupt the stack—no matter what calling convention your other functions are using, always mark the DllMain function WINAPI, which maps to stdcall on Win32. The return value is always required (as per the C standard), but is ignored for all messages except for “DLL_PROCESS_ATTACH”. When handling the process attach message, you should return TRUE if your initialisation was successful, or FALSE if initialisation was unsuccessful. In the case that initialisation failed, the application will fail to start up and issue an error if the DLL is statically loaded, and in the case of dynamic loading, it will cause “LoadLibrary” to return a NULL value, and the DLL will not be mapped into the address space of the process.

The DLL entry point should perform only basic initialisation procedures for your DLL. If you need to perform other tasks, this should be handled elsewhere with a function called from the user code. This is because there is no guarantee about the order in which DLLs are loaded, so if your entry point for “Foo.dll” calls a function in “Bar.dll”, it’s quite possible that Bar.dll has not yet been loaded into your process’s address space, and would result in a general protection fault. The only functions which are safe to call are those located in “kernel32.dll”, which is guaranteed to be loaded before your DLL is loaded. See the DllMain Entry Point MSDN article for full details of how it works, and what you should and shouldn’t do in this function.
Using A Module Definition File

An alternative to using the “__declspec(dllexport)” attribute on functions you want exported from a DLL, is to use a module definition file. This is simply a file which defines (among other things), the functions and variables you want exported from a DLL. An example for our “AddLib.dll” looks like the following:

LIBRARY AddLib.dll
EXPORTS
  Add
  foo
  bar

This specifies that we want function “Add” being exported from the DLL, along with variables “foo” and “bar”. Our C header file looks like the following:

/* Define calling convention in one place, for convenience. */
#define ADDCALL __cdecl

/* Make sure functions are exported with C linkage under C++ compilers. */
#ifdef __cplusplus
extern "C"
{
#endif

/* Declare our Add function using the above definitions. */
int ADDCALL Add(int a, int b);

/* Exported variables. */
extern int foo;
extern int bar;

#ifdef __cplusplus
} // __cplusplus defined.
#endif

You can see that we have not exported the function or the variables. However, we still need to specify the calling convention, and the variables still to be marked “extern” to ensure they are linked correctly. The function body is implemented as follows:

#include "add.h"

int ADDCALL Add(int a, int b)
{
  return (a + b);
}

/* Assign value to exported variables. */
int foo = 7;
int bar = 41;

To use the module definition file, you just have to pass the filename during the link step:

z:\Users\mpayne\Documents\MinGWDLL>gcc -O3 -std=c99 -Wall -c add.c -o add.o

z:\Users\mpayne\Documents\MinGWDLL>gcc -o AddLib.dll add.o AddLib.def -shared -s -Wl,--subsystem,windows,--out-implib,libaddlib.a

One thing to be aware of, is that the header file above does not declare the functions “__declspec(dllimport)” when they are being imported. Whilst this does still work, it results in a slight overhead when calling these functions, as the compiler is not able to make an optimisation that it would otherwise be able to make if it knew the function was coming from a DLL. You could add a conditionally defined “__declspec(dllimport)” to the header file for the exported functions, but this adds another layer of complexity.
Exporting Undecorated stdcall Functions

If you are exporting functions from a DLL and you’re using the stdcall calling convention, you’ll find that MinGW decorates the functions exported from the DLL. For example if you have a function with the signature “int Add(int, int)”, the exported name will be “Add@8”. The linker has added an ‘@’ sign after the function name, followed by the size of the function arguments in bytes. However, if you’re using Microsoft’s Visual C++ you’ll find that it additionally prefixes the name with an underscore, for example “_Add@8”. This may not seem to be a cause for concern, but consider the following scenario. You create an image handling library, and distribute it as source code only. Company A builds an application using your image library, and builds them using MSVC. It deploys the DLL to the “system32” folder of your system, and all works fine. Company B then builds an application using your image library, and builds them using MinGW. It also deploys the DLL to the “system32” folder, overwriting the version of the DLL that Company A deployed. Company B’s application works fine, but Company A’s application is now broken, because the MSVC and MinGW DLLs have differently named exports which are incompatible with each other. The solution is to export the functions without decorations.

To export functions without decorations, it’s simply necessary to pass “--kill-at” to the linker. You must also create an import library when modifying the names exported by a DLL, otherwise the link step will fail when building applications using your DLL. Unfortunately, passing “--out-implib” to the linker will result in an unusable import library, due to the way that the functions are exported. To create the import library, you will need to use dlltool.exe along with a module definition file which contains the decorated versions of all of the exported functions. If this all sounds like a real pain, the following is a much simpler (if slightly inelegant) way of doing this:

z:\Users\mpayne\Documents\MinGWDLL>gcc -o AddLib.dll add.o -shared -s -Wl,--subsystem,windows,--output-def,AddLib.def

z:\Users\mpayne\Documents\MinGWDLL>gcc -o AddLib.dll add.o -shared -s -Wl,--subsystem,windows,--kill-at

z:\Users\mpayne\Documents\MinGWDLL>dlltool --kill-at -d AddLib.def -D AddLib.dll -l libaddlib.a

The first step creates the DLL with decorated functions. Although this is not what we want, passing “--output-def,AddLib.def” to the linker generates a module definition file for us which contains decorated function names. The second step creates the DLL again, but this time we pass “--kill-at” to export the functions without decorations. Note that we can’t create the module definition file in this step, because it would contain undecorated function names. The third and final step creates the import library, based on the module definition file and the DLL name. A little more complicated, but worth it if you need consistency between compilers. In fact, the Win32 API functions are exported this way, without any decorations.
Exporting C++ Functions & Variables

Exporting C++ functions and variables from a DLL is not particularly difficult, but there is one thing which can catch you out if you’re not aware. If you build a C++ DLL with one compiler, it probably won’t work with another compiler. In fact, C++ DLLs built with a certain version of a compiler may not be compatible with DLLs built with another version of the same compiler. This is because C++ is a complex language, and doesn’t have the well defined ABI which C has. For example, different compilers might have different ways of handling C++ exceptions, different ways of implementing virtual functions, or different memory layouts for STL classes. To avoid the issues which would result from this, C++ implementations are intentionally incompatible, and use different methods of name mangling for their functions.

Exporting global variables and global functions from a DLL is done the same way in C++ as it is done in C. The only difference from C, is that global variables can be instances of C++ objects, and the functions can be overloaded if needed. You can also export a C++ class, which will export all of its static and instance member functions which have implementations, whether they are public, protected, or private. The following is an example of a (very minimal) class representing a 2D point:

#ifndef POINT_HPP
#define POINT_HPP

#ifdef POINT_EXPORTS
  #define POINTAPI __declspec(dllexport)
#else
  #define POINTAPI __declspec(dllimport)
#endif

#include

using std::ostream;

class POINTAPI Point
{
  public:
    // Constructors.
    Point();
    Point(int x, int y);

    // Getters and setters.
    int getX() const;
    int getY() const;
    void setX(int x);
    void setY(int y);

    // Friend the overloaded operators, so they can access private Point data.
    friend Point operator+(const Point& lhs, const Point& rhs);
    friend ostream& operator<<(ostream& os, const Point& pt);

  private:
    int x, y;
};

// Overloaded operators.
POINTAPI Point operator+(const Point& lhs, const Point& rhs);
POINTAPI ostream& operator<<(ostream& os, const Point& pt);

extern POINTAPI Point foo;
extern POINTAPI Point bar;

#endif

The point class has been exported, which means that the two constructors, the getters, and the setters are exported. Additionally, the global function to add two points together, the global function to write the point to a stream, and two instances of points have been exported from the DLL. The following is the implementation of these functions:

#include "point.hpp"

Point::Point()
  : x(0), y(0)
{ }

Point::Point(int x, int y)
  : x(x), y(y)
{ }

int Point::getX() const { return this->x; }

int Point::getY() const { return this->y; }

void Point::setX(int x) { this->x = x; }

void Point::setY(int y) { this->y = y; }

Point operator+(const Point& lhs, const Point& rhs)
{
  return Point(lhs.x + rhs.x, lhs.y + rhs.y);
}

ostream& operator<<(ostream& os, const Point& pt)
{
  return os << "(" << pt.x << ", " << pt.y << ")";
}

Point foo(9, 6);
Point bar(3, 19);

Typically such simple functions would be declared inline, but we haven’t done so here because it would defeat the purpose of housing the functions in a DLL. Building the DLL is much the same as building a C DLL, except that we are using the C++ compiler in this case:

z:\Users\mpayne\Documents\MinGWDLL>g++ -c -o point.o point.cpp -D POINT_EXPORTS

z:\Users\mpayne\Documents\MinGWDLL>g++ -o point.dll point.o -s -shared -Wl,--subsystem,windows,--out-implib,libpoint.a
Creating library file: libpoint.a

Creating the import library is optional, as MinGW can alternatively use the DLL in the link step instead of the import library. If you perform an “objdump -p” on the DLL, you will notice that the functions have names such as “_ZN5Point4setXEi” and “_ZlsRSoRK5Point”. If it isn’t clear what these mangled functions refer to, you can use the “c++filt” tool to unmangle them. If you pass a function name on the command line, it will unmangle the name, and if you pass nothing on the command line, it will wait for names to be written to the standard input:

z:\Users\mpayne\Documents\MinGWDLL>c++filt -n _ZlsRSoRK5Point
operator<<(std::basic_ostream >&, Point const&)

z:\Users\mpayne\Documents\MinGWDLL>c++filt -n
_ZN5Point4setXEi
Point::setX(int)
_ZN5Point4setYEi
Point::setY(int)
_ZN5PointC1Eii
Point::Point(int, int)
_ZN5PointC1Ev
Point::Point()
_ZN5PointC2Eii
Point::Point(int, int)
_ZN5PointC2Ev
Point::Point()
_ZNK5Point4getXEv
Point::getX() const
_ZNK5Point4getYEv
Point::getY() const
_ZlsRSoRK5Point
operator<<(std::basic_ostream >&, Point const&)
_ZplRK5PointS1_
operator+(Point const&, Point const&)

Now that the DLL is created, you can write an application which makes use of the DLL. The following example makes use of all of the exported functions, and makes use of the overloaded addition operator along with the stream operator to add two points, and write them to the screen:

#include
#include "point.hpp"

using namespace std;

int main(int argc, char** argv)
{
  Point a;
  Point b(2, 7);
  Point c;
 
  c.setX(85);
  c.setY(24);
 
  cout << "a = " << a << endl;
  cout << "b = " << b << endl;
  cout << "c = (" << c.getX() << ", " << c.getY() << ")\n";

  cout << "foo + bar = " << foo << " + " << bar << " = " << (foo + bar) << endl;

  return 0;
}

The application can then be compiled, linked, and executed:

z:\Users\mpayne\Documents\MinGWDLL>g++ -c -o pointtest.o pointtest.cpp

z:\Users\mpayne\Documents\MinGWDLL>g++ -o pointtest.exe -s pointtest.o -L. -lpoint

z:\Users\mpayne\Documents\MinGWDLL>pointtest.exe
a = (0, 0)
b = (2, 7)
c = (85, 24)
foo + bar = (9, 6) + (3, 19) = (12, 25)

In this case we have exported all of the members from a class, but you don’t have to export them all if you don’t want to. Instead, you can mark individual member functions as being exported. Again, it doesn’t matter whether they are static functions or instance member functions, and it doesn’t matter whether they are public, protected, or private. The only thing you cannot export is a pure virtual function, which by definition has no implementation, so there is nothing to export. Regular virtual functions can be exported, but it’s only necessary to export them if you need to call them for stack based variables which are not accessed through a pointer or reference—for variables which are accessed via a pointer or reference, the functions are instead looked up in the object’s vtable, so a DLL import is not used.

When deploying C++ DLLs, you need to be very careful to avoid breaking other applications by putting an incompatible DLL in another application’s path. It’s best to either place the DLL in the same directory as the application you are deploying, or if you need to put the DLL in a shared folder, give the DLL a name which is unique to the compiler and version. An example would be “point-mingw-4.5.2.dll", or “point-msvc-2010.dll”.
Creating JNI DLLs

Creating MinGW DLLs for use with the Java Native Interface is quite easy, except for one tiny gotcha. The JNI calling convention on Win32 is stdcall, and the JVM expects the exported functions to either have undecorated names, or have names decorated in the format “_[function name]@[size of arguments]”. MinGW does not prefix the names with an underscore, so trying to call MinGW DLLs from Java might result in a stack trace like the following:

z:\Users\mpayne\Documents\MinGWDLL>java Hello
Exception in thread "main" java.lang.UnsatisfiedLinkError: Hello.add(II)I
        at Hello.add(Native Method)
        at Hello.main(Hello.java:5)

The solution is to ensure that the functions are exported with undecorated names. As we only require a DLL to be created in this case (no import library is needed), you can simply pass “--kill-at” to the linker and you’re done. Here is a quick example of using JNI with MinGW. Firstly, create the Java file with a native function declaration:

public class Hello
{
  public static void main(String[] args)
  {
    System.out.println("8 + 5 = " + Hello.add(8, 5));
  }
 
  static
  {
    System.loadLibrary("Hello");
  }
 
  public static native int add(int a, int b);
}

Remember to call “System.loadLibrary()” for the DLL, as I have done in the static constructor above. You can then compile the Java source code and create the C / C++ header file:

z:\Users\mpayne\Documents\MinGWDLL>javac Hello.java

z:\Users\mpayne\Documents\MinGWDLL>javah Hello

The second command will create a C / C++ header file which looks like the following:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class Hello */

#ifndef _Included_Hello
#define _Included_Hello
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Hello
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_Hello_add
  (JNIEnv *, jclass, jint, jint);

#ifdef __cplusplus
}
#endif
#endif

Next, create a C file which contains the body of this function:

#include "Hello.h"

jint JNICALL Java_Hello_add(JNIEnv* env, jclass clazz, jint a, jint b)
{
  return (a + b);
}

Now you can compile the DLL, remembering to pass “--kill-at” to the linker, and run the application:

z:\Users\mpayne\Documents\MinGWDLL>gcc -c -o Hello.o Hello.c -I "c:\Program Files (x86)\Java\jdk\include\win32" ^
                                   -I "c:\Program Files (x86)\Java\jdk\include"

z:\Users\mpayne\Documents\MinGWDLL>gcc -o Hello.dll -s -shared Hello.o -Wl,--subsystem,windows,--kill-at

z:\Users\mpayne\Documents\MinGWDLL>java Hello
8 + 5 = 13

Notice that I have passed two include paths to the compiler, as they contain headers which contain data types and function prototypes needed by JNI. You might need to adjust the paths to match those of your Java installation. Also be aware that like any Windows process, a 32 bit JVM can only load 32 bit DLLs, and a 64 bit JVM can only load 64 bit DLLs. If you try to load a 32 bit DLL in a 64 bit JVM, you’ll get an error such as “Can't load IA 32-bit .dll on a AMD 64-bit platform”.
P/Invoking MinGW DLLs in .NET

Calling functions from a MinGW DLL from .NET code is even easier than working with JNI. This is because in JNI the DLL has to be designed to work with JNI, and must follow certain rules. With P/Invoke, it doesn’t really matter how the DLL has been designed, as you can adjust the DLL import attribute in your .NET code to match the DLL.

To import a function from a DLL, you must declare the function as “extern”, and mark it with the “DllImport” attribute. Here is an example of importing a cdecl function:

using System;
using System.Runtime.InteropServices;

public class Hello
{
  public static void Main(string[] args)
  {
    Console.WriteLine("8 + 5 = {0}", Hello.Add(8, 5));
  }
 
  [DllImport("AddLib.dll", CallingConvention = CallingConvention.Cdecl)]
  extern public static int Add(int a, int b);
}

You can also import stdcall functions, by changing the DLL import attribute to specify “CallingConvention = CallingConvention.StdCall”. The .NET CLR will attempt to import the function without decorations, and if you have not specified “ExactSpelling = true”, it will also attempt to import the function decorated in the MSVC way, i.e. “_Add@8”. If you have a stdcall function decorated in the MinGW way, i.e. “Add@8”, it won’t work. However, you can still import the function by explicitly specifying the entry point:

using System;
using System.Runtime.InteropServices;

public class Hello
{
  public static void Main(string[] args)
  {
    Console.WriteLine("8 + 5 = {0}", Hello.Add(8, 5));
  }
 
  [DllImport("AddLib.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "Add@8", ExactSpelling = true)]
  extern public static int Add(int a, int b);
}

Remember that 64 bit Windows processes can only load 64 bit DLLs, and 32 bit Windows processes can only load 32 bit DLLs. Therefore if your DLL is a 32 bit DLL, and your end user is running the .NET application on a 64 bit version of Windows, your application will fail with an error similar to the following:

z:\Users\mpayne\Documents\MinGWDLL>Hello.exe

Unhandled Exception: System.BadImageFormatException: An attempt was made to load a program with an incorrect format.
(Exception from HRESULT: 0x8007000B)
   at Hello.Add(Int32 a, Int32 b)
   at Hello.Main(String[] args)

To work around this, you can specify the platform the application will run on by passing the “platform” linker option:

z:\Users\mpayne\Documents\MinGWDLL>csc /platform:x86 /out:Hello.exe Hello.cs
Microsoft (R) Visual C# 2005 Compiler version 8.00.50727.4927
for Microsoft (R) Windows (R) 2005 Framework version 2.0.50727
Copyright (C) Microsoft Corporation 2001-2005. All rights reserved.

The platform target option can also be set in Visual Studio, in the “build” section of the project’s properties. By setting this option, your application will run correctly on both 32 bit versions of Windows and 64 bit versions. The same can be done for 64 bit DLLs, by specifying the platform as “x64”, but of course your application won’t run on 32 bit version of Windows in that case.
Using MinGW DLLs with VB6 and VBA

It’s fairly straightforward to use MinGW DLLs with Visual Basic 6 and VBA. The only requirement is that the calling convention for exported functions is stdcall (cdecl and other calling conventions are not supported), and the functions are exported without decorations by using the “--kill-at” command line option:

z:\Users\mpayne\Documents\MinGWDLL>gcc -o AddLib.dll add.o -shared -s -Wl,--subsystem,windows,--kill-at

The function needs to be declared in your code before you are able to call it:

Private Declare Function MyAddFunction Lib "AddLib.dll" Alias "Add" (ByVal a As Long, ByVal b As Long) As Long

Sub Test()
    Call MsgBox(MyAddFunction(4, 5))
End Sub

I have used the “Alias” keyword here to import the function with a different name. I have done this just to demonstrate its use, and normally you would just leave this keyword out completely and use the name exported from the DLL. It does come in useful either if you would end up with name clashes in your code, or if you are calling a Windows API function which comes in Unicode and ANSI variants, and you don’t want to suffix the ‘A’ on every single function call (note that Visual Basic only supports ANSI strings, not Unicode).

If you are using VBA, you should additionally mark your function with the “PtrSafe” keyword. This will ensure you are able to run the code on 64 bit versions of Microsoft Office. However, this is not backwards compatible with versions before Office 2010, so you will need to do it conditionally:

#If VBA7 Then
    Private Declare PtrSafe Function MyAddFunction Lib "AddLib.dll" Alias "Add" (ByVal a As Long, ByVal b As Long) As Long
#Else
    Private Declare Function MyAddFunction Lib "AddLib.dll" Alias "Add" (ByVal a As Long, ByVal b As Long) As Long
#End If

Sub Test()
    Call MsgBox(MyAddFunction(4, 5))
End Sub

It’s a bit of a pain, but well worth doing to ensure your code works across many versions of Office on different architectures.
Setting The DLL Base Address

The base address of a DLL is the preferred location within a process’s virtual address space where the DLL should be loaded. If two DLLs which are loaded by a process have the same base address, or if the regions of memory that the DLLs should be mapped into overlap, it is not possible load both DLLs at their preferred load address. Therefore, one of the DLLs needs to be relocated and loaded at a different memory address. This involves the loader patching hard coded memory addresses within the application’s address space, which can involve significant overhead. By default, the MinGW linker chooses a base address based on a hash of the DLL name, which seems to work very well. However, if you do experience issues with DLLs being unable to be loaded at their preferred address, you can specify the base address of a DLL manually with the “--image-base” linker option. For example, to set the base address to “0x10000000” you pass this option as follows:

z:\Users\mpayne\Documents\MinGWDLL>gcc -o AddLib.dll obj/add.o -shared -s ^
                                   -Wl,--subsystem,windows,--out-implib,libaddlib.a,--image-base,0x10000000

You can verify the base address using “objdump -p AddLib.dll”, which should include the “ImageBase” in an output similar to the following:

AddLib.dll:     file format pei-i386

Characteristics 0x230e
        executable
        line numbers stripped
        symbols stripped
        32 bit words
        debugging information removed
        DLL

Time/Date               Tue Apr 19 16:32:45 2011
Magic                   010b    (PE32)
MajorLinkerVersion      2
MinorLinkerVersion      21
SizeOfCode              00000c00
SizeOfInitializedData   00002200
SizeOfUninitializedData 00000200
AddressOfEntryPoint     000010c0
BaseOfCode              00001000
BaseOfData              00002000
ImageBase               10000000
SectionAlignment        00001000
FileAlignment           00000200
MajorOSystemVersion     4
MinorOSystemVersion     0
MajorImageVersion       1
MinorImageVersion       0
MajorSubsystemVersion   4
MinorSubsystemVersion   0
Win32Version            00000000
SizeOfImage             0000c000
SizeOfHeaders           00000400
CheckSum                0000383c
Subsystem               00000002        (Windows GUI)
DllCharacteristics      00000000
SizeOfStackReserve      00200000
SizeOfStackCommit       00001000
SizeOfHeapReserve       00100000
SizeOfHeapCommit        00001000
LoaderFlags             00000000
NumberOfRvaAndSizes     00000010

Loading / Unloading DLLs At Runtime

Loading a DLL at runtime is useful when you have a plugin architecture where developers can extend the functionality of your application. You would likely provide some header files which define the data structures the application uses, some prototypes for supporting functions, and some prototypes for function(s) which the plugin must implement. To take a very simple example, you might require a developer to provide a DLL with a single exported function, with a signature “void __cdecl DoPlugin();”. Your application then just needs to call the “DoPlugin” function exported by that DLL, in order to expose its functionality.

Of course, you don’t know the name of this DLL in advance, so you need to load the DLL at runtime. This is done using the “LoadLibrary” Windows API function, located in “kernel32.dll”. Calling LoadLibrary loads the DLL into the process’s address space (if it is not already present), and increments the usage count for this DLL. If the DLL is successfully loaded, the function returns an HMODULE whose value is the address in memory where the DLL was loaded (ideally the DLL base address). Once the DLL is loaded into memory, you can call the “GetProcAddress” function to get the address of the function you’d like to call. The following example attempts to load the “AddLib.dll” library from my previous article, and call the “Add” function which the DLL exports:

#include
#include
#include

/* Function signature of the function exported from the DLL. */
typedef int (__cdecl *AddFunc)(int a, int b);

int main(int argc, char** argv)
{
  HMODULE hAddLib;
  AddFunc Add;

  /* Attempt to load the DLL into the process's address space. */
  if (! (hAddLib = LoadLibrary(TEXT("AddLib.dll"))))
  {
    fprintf(stderr, "Error loading \"AddLib.dll\".\n");
    return EXIT_FAILURE;
  }

  /* Print the address that the DLL was loaded at. */
  printf("Library is loaded at address %p.\n", hAddLib);

  /* Attempt to get the memory address of the "Add()" function. */
  if (! (Add = (AddFunc) GetProcAddress(hAddLib, "Add")))
  {
    fprintf(stderr, "Error locating \"Add\" function.\n");
    return EXIT_FAILURE;
  }

  /* Print the address of the "Add()" function. */
  printf("Add function is located at address %p.\n", Add);

  /* Call the function and display the results. */
  printf("7 + 41 = %d\n", Add(7, 41));

  /* Unload the DLL. */
  FreeLibrary(hAddLib);

  return EXIT_SUCCESS;
}

There are a few things here which should be noted. Firstly, the “LoadLibrary” and “GetProcAddress” functions return a NULL pointer on failure, and you should always check for a failure in either of these function calls. Secondly, the “LoadLibrary” function has both ANSI and Unicode versions, whereas “GetProcAddress” always takes an ANSI string for the function name—don’t let this catch you out in Unicode builds. Thirdly, we are using a C “typedef” to declare a function pointer type matching the signature of the function exported from the DLL. This signature should match exactly, including the calling convention, otherwise you can corrupt the stack when calling the function. Finally, the “FreeLibrary” function should be called when you are finished using functions exported from the DLL. The “FreeLibrary” function decrements the usage count of the DLL, and additionally unmaps the DLL from the process’s address space if the usage count reaches zero.

Running the application should result in an output such as the following:

z:\Users\mpayne\Documents\MinGWDLL>DynamicLoad.exe
Library is loaded at address 6DA40000.
Add function is located at address 6DA41280.
7 + 41 = 48

Of course in a real example you would not hard code the name of the DLL into the application. Instead, you would probably expose a user interface within the application which allows managing of plugins—exactly how this is done is left as an exercise for the reader.


 http://c.biancheng.net/cpp/html/2847.html

__stdcall,__cdecl,__pascal,__fastcall的区别

 

__cdecl

__cdecl 是 C Declaration  的缩写,表示 C 语言默认的函数调用方法:所有参数从右到左依次入栈,这些参数由调用者清除,称为手动清栈。被调用函数不会要求调用者传递多少参数,调用者传递过多或者过 少的参数,甚至完全不同的参数都不会产生编译阶段的错误。

__stdcall

__stdcall 是 Standard Call 的缩写,是 C++ 的标准调用方式:所有参数从右到左依次入栈,如果是调用类成员的话,最后一个入栈的是 this 指针。这些堆栈中的参数由被调用的函数在返回后清除,使用的指令是 retnX,X 表示参数占用的字节数,CPU 在 ret 之后自动弹出 X 个字节的堆栈空间,称为自动清栈。函数在编译的时候就必须确定参数个数,并且调用者必须严格的控制参数的生成,不能多,不能少,否则返回后会出错。

__pascal

__pascal 是 Pascal 语言(Delphi)的函数调用方式,也可以在 C/C++ 中使用,参数压栈顺序与前两者相反。返回时的清栈方式与 __stdcall 相同。

__fastcall

__fastcall 是编译器指定的快速调用方式。由于大多数的函数参数个数很少,使用堆栈传递比较费时。因此 __fastcall 通常规定将前两个(或若干个)参数由寄存器传递,其余参数还是通过堆栈传递。不同编译器编译的程序规定的寄存器不同,返回方式和 __stdcall 相当。

__thiscall

__thiscall 是为了解决类成员调用中 this 指针传递而规定的。__thiscall 要求把 this 指针放在特定寄存器中,该寄存器由编译器决定。VC 使用 ecx,Borland 的 C++ 编译器使用 eax。返回方式和 __stdcall 相当。

__fastcall 和 __thiscall 涉及的寄存器由编译器决定,因此不能用作跨编译器的接口。所以 Windows 上的 COM 对象接口都定义为 __stdcall 调用方式。

C 语言中不加说明默认函数为 __cdecl 方式(C中也只能用这种方式),C++ 也一样,但是默认的调用方式可以在 IDE 环境中设置。

例子:test1(par1, par2, par3);
__cdecl调用约定 PASCAL调用约定 __stdcall调用约定
push par3; 参数按右到左传递
push par2
push par1
call test1
add esp, OC; 平衡堆找
push par1; 参数按左到右传递
push par2
push par3
call test1; 函数内平衡堆找
push par3; 参数按右到左传递
push par2
push par1
call testl; 函数内平衡堆栈




沒有留言:

張貼留言

追蹤者