Building a Dynamic-Link Library using C++ MinGW

Home / Building a Dynamic-Link Library using C++ MinGW

Building a Dynamic-Link Library using C++ MinGW

December 9, 2015 | Article | No Comments

Dynamic-Link Library or DLL, is implementation of shared library concept in Microsoft Windows and OS/2 operating system. Usually it has file extension DLL. The file formats for DLL are the same as for executable file – that is Portable Executable (PE) for 32-bit and 64-bit Windows, and New Executable (NE) for 16-bit Windows.

In this article, we will discuss about how we can create a .dll file using C++ and compiler MinGW.

The Files

We will use three files: my_dll.h, my_dll.cpp, and client.cpp

The my_dll.h is a header file where we place declarations:

// my_dll.h
#ifndef _MY_OWN_DLL_H__
#define _MY_OWN_DLL_H__
#ifdef __cplusplus
extern "C" {
#endif

#ifdef BUILD_MY_DLL
#define MY_DLL __declspec(dllexport)
#else
#define MY_DLL __declspec(dllimport)
#endif

void __stdcall MY_DLL hello(const char*s);

int MY_DLL Double(int x);

#ifdef __cplusplus
}
#endif

// This function is not declared extern "C"
void MY_DLL CppFunc(void);

// NOTE: This class must not be declared extern "C"
class MY_DLL MyClass {
public:
   MyClass() {}
   virtual ~MyClass() {}
   void func(void);
};
#endif

This one for my_dll.cpp will hold the implementation detail:

// my_dll.cpp
#include <stdio.h>
#include "my_dll.h"

__stdcall void hello(const char *s) {
   printf("Hello %s\n", s);
}
int Double(int x) {
   return (2*x);
}
void CppFunc(void) {
   puts("We are calling CppFunc");
}

void MyClass :: func(void) {
   puts("We are calling func() from class MyClass");
}

Those two files will be compiled as a single .dll file. Another source file, client.cpp, will be used as a “client”. This file will be compiled as a single executable file which will depend on the .dll file.

// client.cpp
#include <stdio.h>
#include "my_dll.h"

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

   MyClass a;
   a.func();

   return 0;
}

Building DLL

To build the DLL, use following commands:

g++ -c -DBUILD_MY_DLL my_dll.cpp
g++ -shared -o my_dll.dll my_dll.o -Wl,--out-implib,libmy_dll.a

The -DBUILD_MY_DLL will be passed to compiler (see the .h file). This is because DLL’s functions need to be declared as “dllexport”, meaning that they will be “exported” from the DLL and available to client applications.

The “-shared” options 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 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.

Building a Client Executable

To build the client, use following commands:

g++ -c client.cpp
g++ -o client.cpp client.o -L. -lmy_dll

The option “-L.” will add . (current directory) to where library will be searched. As we use current directory to store all the example, we will use this option. Then the “-lmy_dll” will tell the linker to link this code with library “libmy_dll.a”. The linker will search directory in current directory (remember the -L.) first.

We may built the same executable without an import library using following command:

g++ -o client.cpp client.o my_dll.dll

If this method work for your application, then there is usually no need for an import library. In my case, the application which doesn’t use import library result in smaller size.

Buildign and Using a DLL without the dllexport / dllimport Attributes

If you pass the “-no-undefined” and “–enable-runtime-pseudo-reloc” options to the linker, then 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 unix;

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.

A way to circumvent this problem is either using COM or create C-style wrapper functions to encapsulate the C++ ABI.

,

About Author

about author

xathrya

A man who is obsessed to low level technology.

Leave a Reply

Your email address will not be published. Required fields are marked *

Social media & sharing icons powered by UltimatelySocial