How to create a DLL in C++ using run time dynamic linking

How would you like to have an easily update-able program? If your program was using DLLs, your users would not have to redownload your entire program once a new version is out. You could provide them with an updater that downloads the new DLLs and they'll be set and ready to go.

How would you like to have a plugin interface for your program? What would Firefox or Winamp be without plugins? They'd be pretty lame. Programs can implement a plugin system using DLLs.

toc_collapse=0;
Contents 

Why DLLs?

DLLs make your program modular and if this is what you're aiming for, you should keep reading this tutorial. In this tutorial I will show you how to write DLLs that can be loaded explicitly at run-time (aka run-time dynamic linking). I will not explain how to write DLLs that use load-time dynamic linking because they're not that suitable for writing modular programs.

For simplicity's sake, we will not be using an IDE. We'll only be using MinGW's g++. So you only need the command line and MinGW to compile and run the programs in this tutorial. You can also use the g++ compiler included in your DevCpp or Code::Blocks installation. Just "cd" to their bin directory if you haven't added it to the PATH environment variable and go from there.

How do I write DLLs?

It's quite easy. Our sample modular program will have a...

  • main.cpp file, which will load the DLL and use the functions provided in it.
  • Dll.cpp file, where we will write the actual code for the functions in the DLL.
  • Dll.h file, which will be included by main.cpp so that our program will know what are the functions in the DLL that it can use (We'll be basically declaring function pointers there).

Suppose you are doing an encryption toolkit that uses a lot of encryption algorithms. You decide you have to add another encryption algorithm. You start writing it, add it to your code, recompile the whole program and you're done. You then proceed to updating everyone with your new version of the program. That will take a lot of time.

But what if your program was modular and it was able to load algorithms dynamically from DLLs. Each DLL would implement an encrypt() and decrypt() function. Your program would load an encryption DLL (we'll see how soon) call the functions in it and do the job. When you want to add a new algorithm, just write it in a DLL and send it to the user. When you want to update an algorithm you can just update its DLL. Easy huh?

Code please?

Our sample program will implement the ROT13 encryption algorithm in a DLL, load it dynamically, and use it to encrypt and decrypt strings.

Let's do a sort of top-down approach. Here's how we would like main.cpp to look like (simplified version)...

// -- main.cpp --

#include <string>
#include <iostream>
#include <windows.h>
#include "Rot13Dll.h"   //      I lied, it's Rot13Dll.h not Dll.h :P

using namespace std;

//      Don't worry about CryptFuncPtr for now. It's declared in Rot13Dll.h
CryptFuncPtr LoadEncryptionFunction(const std::string& dll);

//      Gets the input (string to encrypt) from somewhere. Most probably the keyboard.
std::string getInput() { return std::string("sample input"); }

int main() {
        std::string dllPath = "rot13.dll";
       
        //      Load the functions from the DLL
        cout << "Attempting to load the Rot13 DLL...\n";
        CryptFuncPtr encryptFunction = LoadEncryptionFunction(dllPath);
       
        //      Do some error checking....
        if(!encryptFunction) {
                cout << "Error loading the encryption function from "<< dllPath << endl;
                return 0;
        }
       
        //      Read the input...
        std::string input = getInput(), output;
       
        //      Do the encryption and decryption, then print the output...
        cout << "Encrypting...\n";
        output = encryptFunction(input, true);
        cout << "Encrypted [" << input << "] to [" << output << "]\n\n";
       
        cout << "Decrypting...\n";
        input = encryptFunction(output, false);
        cout << "Decrypted [" << output << "] back to [" << input << "]\n\n";
       
        system("PAUSE");
        return 0;
}

So that would be main.cpp... Loads the encryption function from the DLL, reads in some string, encrypts it and decrypts it back. Nothing complicated except for the LoadEncryptionFunction and the function pointer which we'll cover next.

Let's see how Rot13Dll.h would look like. Rot13Dll.h only declares a function pointer (you should brush up on them if you don't know what they are) to the encryption function in DLL.

// -- Rot13Dll.h --

#ifndef __ROT_13_DLL_H_INCLUDED__
#define __ROT_13_DLL_H_INCLUDED__

#include <string>       //      Or do a forward declaration...

//      Pointer to the encryption function. The last parameter is boolean and
//      indicates whether the function should encrypt or decrypt the input (true for enc. false for dec.)
typedef std::string (/*__cdecl*/ *CryptFuncPtr)(const std::string&, bool);

#endif

The only thing main.cpp needs to know about the DLL is that it exports a std::string CryptFuncPtr(const std::string&, bool) function. It "acquires" that knowledge by #include-ing Rot13Dll.h which declares that function pointer (as we've seen above). Main.cpp will declare such a pointer and will retrieve the function from the DLL and store it in that pointer. LoadEncryptionFunction takes care of that...

// -- main.cpp --

//      Loads the encryption function from the DLL returning a pointer to it, so that it can be called.
CryptFuncPtr LoadEncryptionFunction(const std::string& dll) {
        //      Load the DLL in which the function is stored
        //      This call should have a matching ::FreeLibrary() call
        //      But we'll take care of that later...
        HINSTANCE dllInstance = ::LoadLibrary(dll.c_str());
       
        //      We use ::GetProcAddresss() to retrieve a pointer to the function.
        //      We need to cast this pointer to the right type (CryptFuncPtr in this case)
        CryptFuncPtr func = reinterpret_cast<CryptFuncPtr>(::GetProcAddress(dllInstance, "encrypt"));
        return func;
}

Loading a function from a DLL is as simple as loading the DLL using LoadLibrary() and then loading the function using GetProcAddress(). Notice that we actually give the name of the function, as a string literal, to GetProcAddress(), that's how it identifies the function in the DLL: by name. This means that in your Rot13Dll.cpp file you need to have a function named "encrypt". Here's a simplified version of this file :P

// -- Rot13Dll.cpp --

#include <windows.h>
#include <string>

extern "C" _declspec(dllexport) std::string encrypt(const std::string& data, bool encrypt) {
        //      The funny part is that ROT13 is a symmetric cipher, so the if shouldn't really be there...
        if(encrypt) {
                return std::string("return the encrypted version here lol");
        } else {
                return std::string("return the decrypted version here lol");
        }
}

The extern "C" _declspec(dllexport) might seem overly complicated for you. Just know that it needs to be there.
Okay, so we've got ourselves the source code, but how do we get the EXE and how do we get the DLL? The EXE you just get by compiling your program normally like this:

g++ -o myProgramThatLoadsDLLs.exe main.cpp

There are actually two commands to compile the DLL. The first one builds the object code and the second one builds the DLL.

g++ -c Rot13Dll.cpp
g++ -shared -o rot13.dll Rot13Dll.o

Personally I use batch files and put the commands in them with a pause command at the end so I can see what happens.
So go ahead, copy and paste the code in the three files, compile them and see if it works. I'm testing them as I write this sentence :P

Wow, that was easy, tell me more about DLLs!

Well, we did a couple of (intentional) mistakes there because we kind of forgot to free the loaded DLL. You can do a lot more with DLLs. You can store variables in DLLs and provide accessors and mutators for the variables. You can also have objects (class instances) "returned" from your DLL if you follow a certain design pattern. Next, I'll show you how to write a small library that will help you load DLLs and use the functions provided by them.

Actually, scratch that, just download the attached source code.

AttachmentSize
Runtime dynamic linking DLLs.zip5.4 KB
delta's picture

this tutorial is very very

this tutorial is very very util, thanks!!! Good Work

Anonymous's picture

Really a good topic discussed

Really a good topic discussed in a simple way.. Thanks Alin..

Anonymous's picture

Well done, this made my day!

Well done, this made my day!

Post new comment

The content of this field is kept private and will not be shown publicly. If you have a Gravatar account associated with the e-mail address you provide, it will be used to display your avatar.