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 <stdio.h>
#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 <stdio.h>
#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.

See Also

This page: DLL.

Site Status

Site maintenance completed May 25th, 2012 at 12:38 UTC