How does this work, to make a .DLL?

Hi, I am currently reading about how to make DLLs and I am seriously struggling to understand what's going on. So I will ask each question I have and hope that any of you can help me. I could just accept everything and copy-paste but that will just build up frustration where I keep asking what's going on like now.

The code:

dllFile.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#ifndef _DLL_TUTORIAL_H_
#define _DLL_TUTORIAL_H_
#include <iostream>
 
#if defined DLL_EXPORT
#define DECLDIR __declspec(dllexport)
#else
#define DECLDIR __declspec(dllimport)
#endif
 
extern "C"
{
   DECLDIR int Add( int a, int b );
   DECLDIR void Function( void );
}
 
#endif 


dllFile.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include "DLL_Tutorial.h"
 
#define DLL_EXPORT
 
extern "C"
{
   DECLDIR int Add( int a, int b )
   {
      return( a + b );
   }
 
   DECLDIR void Function( void )
   {
      std::cout << "DLL Called!" << std::endl;
   }
}


I will make bullet points for each question.
*1: What does "#if defined DLL_EXPORT" do? Why is it needed?
*2: When will DECLDIR be replaced with "__declspec(dllexport)"?
*3: When will DECLDIR be replaced with "__declspec(dllimport)"?
*4: What does "#define DLL_EXPORT" do? Why is it needed?
*5: I still do not understand the #define directive. When I read about it, they say that #define will replace the identifier with a replacement.
But how does it work in a header guard? There you only have for
example: "#define FILE_H (replacement missing?)" - the identifier FILE_H, but we do NOT give it a replacement.

I am sorry if I give you too many questions. But as a beginner it felt very hard ot understand all of this at once.

Thanks for all help!
Best regards, Zerpent :).
1. In your project which builds the DLL, you should define DLL_EXPORT. Assuming that we define this BEFORE we include the header, it will put __declspec(dllexport) in front of the functions that you want to export. These functions will now be available to the outside world from your DLL. If you are importing the DLL, you won't define DLL_EXPORT and so now we have __declspec(dllimport) in front of each function. That tells our linker to not bother linking because that will be done at runtime when we load the DLL.

2. DECLDIR should be replaced with __declspec(dllexport) when we compile the DLL project. This adds that function to our index of functions in the DLL which can be used externally.

3. DECLDIR should be replaced with __declspec(dllimport) when we use this header to compile the project which has imported this DLL. This tells the linker to skip this symbol for now, we'll find it during runtime when we have a .dll around.

4. #define DLL_EXPORT is just a flag that helps us to flag whether we are compiling the DLL or someone is using our DLL. It's defined in dllFile.cpp because that file isn't available to the users and so DLL_EXPORT will not be defined when they use it. That's what selects between __declspec(dllexport) and __declspec(dllimport). Note there is a bug in your code. It should be defined BEFORE including the header. I always do this in the project settings themselves instead of source files.

5. #define can replace a symbol with some text like: #define INT int . Now I can write INT and before the code gets compiled, the compiler will read it as int. It can also be used to just say "this exists". #define DLL_EXPORT will replace that text with nothing... so no big deal right? But it also causes #ifdef DLL_EXPORT or #if defined DLL_EXPORT to be a true statement. If DLL_EXPORT was not previously defined, the compiler will completely ignore everything between that and the #else or #endif
Example... If we run this through the pre-processor (before it gets actually compiled)
1
2
3
4
5
6
7
8
9
#define DLL_EXPORT

#if defined DLL_EXPORT
#define DECLDIR __declspec(dllexport)
#else
#define DECLDIR __declspec(dllimport)
#endif

DECLDIR int Add( int a, int b );

turns into:
1
2
3
4
5
6
7
8
9
//#defined DLL_EXPORT -- removed because it gets replaced with nothing

//#if defined DLL_EXPORT
#define DECLDIR __declspec(dllexport) // This line remains because DLL_EXPORT was defined
//#else
//#define DECLDIR __declspec(dllimport)
//#endif

DECLDIR int Add( int a, int b );

turns into this for the compiler:
__declspec(dllexport) int Add( int a, int b );


Here's the same code without the DLL_EXPORT defined:
1
2
3
4
5
6
7
#if defined DLL_EXPORT  // This hasn't been defined!
#define DECLDIR __declspec(dllexport)
#else
#define DECLDIR __declspec(dllimport)
#endif

DECLDIR int Add( int a, int b );

Turns into
1
2
3
4
5
6
7
//#if defined DLL_EXPORT  // This hasn't been defined!
//#define DECLDIR __declspec(dllexport)
//#else
#define DECLDIR __declspec(dllimport)
//#endif

DECLDIR int Add( int a, int b );

Turns into:
__declspec(dllimport) int Add( int a, int b );
And this is what the compiler will actually read now.
Last edited on
I understand it much more now. But it raised a few more questions.

Why can't the linker link the functions that are in the DLL before runtime? Isn't the DLL an already compiled file with machine code that can already be used? Does the linker link the functions during runtime or is there a special "DLL-linker" doing it?

The #define directives with only an identifier without a replacement, are only always used to decide what the ifs and ifns should do then?

The ifs and the ifns are the directives that control what will be excluded in the current .h-file then?

Thanks for taking the time Stewbond to help me. It helps and means a lot.
Best regards, Zerpent :).
Last edited on
Linking a library at compile time is known as "static" linking. It's possible and common.

One reason why you may use a DLL instead is that when you change a DLL you don't need to re-compile the entire program. You can just build that DLL. This lets you swap in and out components of your program.

Zerpent wrote:
The #define directives with only an identifier without a replacement, are only always used to decide what the ifs and ifns should do then?

that's right
Ok, I can even write the DLLs all by my self now and understand what's going on :).

Thanks so much Stewbond.
Topic archived. No new replies allowed.