Loading DLLs using C++ in Windows
This standalone tutorial will guide you through writing your own DLL library from scratch and loading it into C++ and calling the exported functions in a separate project.
Hello, friends 😀! Today I will guide you through libraries and how you have implemented other's libraries in your C++ code in the Windows operating system. Hold your pace, we will walk together and explore libraries. But before moving forward, let me give short details on what this post is all about.
What are Libraries?
Like you use the information from the books kept in the library (the real one). In programming, you can use the knowledge (functions/routines) of implementation from some other author or community. Reusing others' code to avoid time in the re-implementation is really cool thing. It let you focus on the logic for your application rather than the utility
There are two parts for variables and functions: prototyping and definition.
In the case of prototyping, you basically create the signature of the identifier and register its name, return type and parameters (When you pass the value, it's called arguments and while accepting in functions they are called parameters)
Defining is when you actually implement the code for the function or variable (aka initializing). In this phase, you must use the same syntax of the identifier that you have chosen while prototyping.
Some veteran C++ developers, also sometimes called prototypes as an identifier signature.
So, the library contains definitions of these identifiers prototyped in the respective headers file. For example, you use functions from the
cmath header file, which are defined in the
libm library. In GCC you can do this with,
-lm means to include
libm.so file and provide the definitions for the functions. Well, we won't be using GCC in the Windows environment.
Since the code of the libraries rarely changes, they won't recompile every time you hit that Build button and provide a fast compilation.
Different types of Libraries
There are two types of libraries that can be used in the program.
- Static libraries
- Shared libraries
Static libraries (
.lib) contains definitions that are linked and embedded in the executable. So they make your executables portable and also bulky. If you call the executable n number of times, the library will be loaded n times along with the code in memory. Also if you have to update the code in the static library, you will require to compile the source of the dependents.
Shared libraries are also called Dynamic Link Libraries (aka DLL) also contain the same code definitions but unlike static libraries, they are not embedded in the code. It is loaded once in the memory when first called, after that, it is reused by the code. This opposes all the points that we have discussed for static libraries.
There are two methods of calling a function from DLL
- Load time linking
- Run time linking
So basically in run-time linking, you first load the library and then later call the function to get the address of exported functions and call it like a normal function. In this post, I will focus on Run-time linking. You can read about others from this article
NOTE – Though load time linking is faster than run time as it optimizes calls of GetProcAddress, but runtime dynamic linking is preferable when the startup performance of the application is important.
But where is my DLL?
Like you, I also had this question in my mind. How do programs look for the dynamic library while loading and what if they couldn't find it anywhere? When you will perform the
LoadLibrary call (I will explain it). It will search the name of the DLL file in the following order as described here.
Notice one thing, Safemode? If you do disable the DLL search safe mode, an attacker can do DLL search order hijacking and the program will load their malicious DLL, eventually taking over control of the system.
In case the dll is not found in the above order, LoadLibrary will return
NULL. So you can add safeguard.
Writing a DLL in C++
In this demonstration, I will be creating a simple arithmetic Dll that perform addition, subtraction, division and multiplication. Yes, so naive. This will not only help in learning how to load libraries but also how they are created.
DLL Entrypoint (
Like a normal C++ program, DLLs also have an entry point known as
DllMain It will be executed whenever a Dll file is loaded or unloaded to the running program/thread.
But unlike the
main function in your C++ code, this function is optional. If present, the system calls the entry-point function whenever a process or thread loads or unloads the DLL. In this project, I used the
DllMain function to print the text on the console, you can check it here
It is now time to add our functions in the DLL source file here. You can check the content below.
It is always intended to use the libraries in both C and C++, kind of backward compatibility you can say.
EXTERN_C here is a macro that gets resolved to
extern "C" in the compile-time (source code from wine) only if the library code is compiled from a C++ compiler.
Let's now create a signature for the functions we are going to use for typecast in the C++ code where we will be using this DLL. Here in this example, I will create 4 functions to perform arithmetic operations.
([variables...]) to execute that instruction set. These functions signatures will be used to typecast an address that you can call a normal function while dereferencing the pointer got from the
Note:– Since all the functions have the same signature with two arguments of
DWORD type and return type is also
DWORD. You can actually have only one function (shown below) and call it any name. These names are only for your understanding. In the reality, it will execute the instructions from the address of the function. But for learning purposes, I will stick to the above syntax
Exporting DLL Functions
Once you have written the functions in your library source code, it needs to be exported using a module definition file that tells the linker about the functions that can be used in the applications source code. This is supposed to be done by creating a
Source.def file in the project and configuring it in the Linker Settings as shown below
There is another way to export the functions using (shown below). I prefer to use module definitions as they are a cleaner way of configuration.
Finally, everything is done for creating the DLL in the visual studio 🤩! All you need to do now is to compile. Now let's jump on to using it in the C++ code.
Using a DLL in C++ code using
Now is the climax where I will show you how to load the DLL and call the exported function from it. The source code is pretty simple and can be found in the
The code starts with the
_tmain function which is replaced with
wmain in the compile-time and is used for handling UNICODE strings in the application. Then I added the check for handling invalid CLI arguments passed to the application and casting the
DWORD as shown here. So a basic template of application source code looks like below
Now you need to first load the library using the
LoadLibrary function which is defined in
libloaderapi.h header and aliased to
#define macros. It accepts only one argument: a long pointer to wide string as the file name of the library.
In return, it will give you a handle of the DLL module mapped into the virtual address of the current process. If this handle value is either
INVALID_HANDLE_VALUE which you can then as safe-guard for further processing.
Once the library is loaded successfully, you can have its address using the following code
Finally, it's now time to get the reference of function in the DLL and execute it using the normal function call. The address of function can be obtained using the
GetProcAddress function from
libloaderapi.h header file which accepts two parameters: DLL handle and the name of the exported function respectively. If the function succeeds, it will return the address of the exported function.
After you have got the address all you need to this call the function and as it is defined in the current project
Before building the project it is required to add the DLL into the reference. You can do it by selecting References in the solution explorer and selecting the
Lastly, when everything is completed and DLL is no longer required, you need to release the memory using the
FreeLibrary function from
libloaderapi.h. It accepts only one parameter: a handle to the loaded DLL module and returns
TRUE on success otherwise
You can find it implemented in the last of the main function here