Build and use C DLL file in VSCode - GitMasterNikanjam/C_WiKi GitHub Wiki
A DLL (Dynamic Link Library) file is a type of file that contains code, data, and resources that can be used by multiple programs simultaneously in a Windows environment. DLL files have the file extension .dll
. Here are some key points about DLL files:
-
Purpose: DLLs help promote code reuse and modular programming. They allow programs to use shared functions without the need to embed this code within the executable file itself, reducing redundancy and saving memory.
-
Usage: DLLs are used by applications to perform common tasks. For instance, a DLL might contain functions for handling file operations, displaying dialog boxes, or interacting with hardware.
-
Benefits:
- Code Sharing: Multiple applications can use the same DLL, reducing the amount of duplicated code.
- Memory Efficiency: Since the DLL code is loaded into memory only once, it conserves memory usage.
- Modularity: Developers can update or patch a DLL independently of the applications that use it.
- Smaller Executables: Applications can be smaller because they do not need to include all the code they might need, relying instead on shared DLLs.
-
Structure: A DLL file contains:
- Code: Executable code that can be called by applications.
- Data: Variables and data structures that can be accessed by the functions within the DLL.
- Resources: Icons, images, and other resource files that can be used by applications.
-
Linking: DLLs can be linked to an application in two ways:
- Static Linking: The linking is done at compile time.
- Dynamic Linking: The linking is done at runtime. This allows for updates to the DLL without the need to recompile the application.
-
Common Issues:
- Missing DLLs: If an application tries to use a DLL that is not present or cannot be found, it will fail to run.
- Version Conflicts: Different applications might require different versions of the same DLL, leading to "DLL Hell," where managing these dependencies becomes complex.
-
Example: A common DLL in Windows is
kernel32.dll
, which contains core functions for memory management, input/output operations, and interrupt handling. -
Development: Developers create DLLs using languages like C, C++, and C#. They are then compiled into binary form. Tools like Visual Studio provide support for creating and managing DLL projects.
-
Security: DLL files can be a vector for malware, as they contain executable code. Ensuring DLLs are from trusted sources is essential to maintain system security.
DLL files play a crucial role in the Windows operating system and many applications, offering a flexible and efficient way to manage shared resources and functionality.
Creating a DLL in C++ involves several steps, from writing the C++ code to exporting the functions and building the DLL. Here’s a step-by-step guide:
Create a new project for your DLL. The structure of the DLL will involve specifying which functions and classes should be accessible from outside the DLL.
-
Define Functions and Classes: Write the functions and classes that will be part of the DLL.
-
Export Functions and Classes: Use the
__declspec(dllexport)
directive to specify which functions and classes should be exported.
Here is an example of a simple C++ DLL:
// mylibrary.h
#ifndef MYLIBRARY_H
#define MYLIBRARY_H
#ifdef MYLIBRARY_EXPORTS
#define MYLIBRARY_API __declspec(dllexport)
#else
#define MYLIBRARY_API __declspec(dllimport)
#endif
extern "C" {
MYLIBRARY_API int add(int a, int b);
MYLIBRARY_API int subtract(int a, int b);
}
#endif // MYLIBRARY_H
// mylibrary.cpp
#include "mylibrary.h"
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
Creating a DLL project in Visual Studio Code (VS Code) involves several steps, including setting up the environment, writing the code, and configuring the build system. Here's a detailed guide to help you through the process:
-
Install VS Code: Download and install Visual Studio Code from here.
-
Install C++ Compiler: You need a C++ compiler like GCC (MinGW for Windows) or MSVC.
- Windows: Install MinGW from mingw-w64.
-
Linux: Install GCC using the package manager (
sudo apt install gcc g++
). -
macOS: Install Xcode command-line tools (
xcode-select --install
).
-
Install CMake: CMake is a build system generator that you can download from here.
-
Install VS Code Extensions:
- C/C++ by Microsoft
- CMake Tools by Microsoft
-
Create a Folder for Your Project: Create a directory for your project, e.g.,
MyLibrary
. -
Create the Source Files: Inside the project directory, create your header (
.h
) and source (.cpp
) files.
Example structure:
MyLibrary/
|-- include/
| |-- mylibrary.h
|-- src/
| |-- mylibrary.cpp
|-- CMakeLists.txt
#ifndef MYLIBRARY_H
#define MYLIBRARY_H
#ifdef _WIN32
#ifdef MYLIBRARY_EXPORTS
#define MYLIBRARY_API __declspec(dllexport)
#else
#define MYLIBRARY_API __declspec(dllimport)
#endif
#else
#define MYLIBRARY_API
#endif
extern "C" {
MYLIBRARY_API int add(int a, int b);
MYLIBRARY_API int subtract(int a, int b);
}
#endif // MYLIBRARY_H
#include "mylibrary.h"
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
cmake_minimum_required(VERSION 3.10)
project(MyLibrary)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
add_library(MyLibrary SHARED src/mylibrary.cpp)
target_include_directories(MyLibrary PUBLIC "${PROJECT_SOURCE_DIR}/include")
if(WIN32)
target_compile_definitions(MyLibrary PRIVATE MYLIBRARY_EXPORTS)
endif()
-
Open Folder: Open the
MyLibrary
folder in VS Code. - Configure CMake Tools: Click on the CMake Tools extension icon and select "Configure Project" to generate the necessary build files.
- Build the Project: Use the CMake Tools extension to build your project. This will generate the DLL file in the build directory.
Create a separate folder for your application, e.g., MyApp
, and set it up to use the DLL.
- Project Structure:
MyApp/
|-- main.cpp
|-- CMakeLists.txt
#include <iostream>
#include "mylibrary.h"
int main() {
int result1 = add(3, 4);
int result2 = subtract(7, 2);
std::cout << "Addition: " << result1 << std::endl;
std::cout << "Subtraction: " << result2 << std::endl;
return 0;
}
cmake_minimum_required(VERSION 3.10)
project(MyApp)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
add_executable(MyApp main.cpp)
target_include_directories(MyApp PRIVATE "../MyLibrary/include")
target_link_libraries(MyApp PRIVATE "../MyLibrary/build/MyLibrary.dll")
-
Open the Application Folder in VS Code: Open the
MyApp
folder in VS Code. - Configure and Build the Application: Use CMake Tools to configure and build your application.
Ensure that the DLL (MyLibrary.dll
) is in the same directory as your executable or in a directory included in the system PATH.
By following these steps, you can create a DLL project in VS Code and use it in an application. This setup allows for modular and reusable code in C++.
Here’s how you can create a DLL in Visual Studio Code (VS Code) and use it in an application without using CMake or JSON files, purely using terminal commands.
- Install VS Code: Download and install Visual Studio Code from here.
-
Install MinGW (for Windows users):
- Download MinGW from mingw-w64.
- Add the
bin
directory of MinGW to your system PATH.
-
Create a Folder for Your Project: Create a directory for your project, e.g.,
MyLibrary
. -
Create the Source Files: Inside the project directory, create your header (
.h
) and source (.cpp
) files.
Example structure:
MyLibrary/
|-- include/
| |-- mylibrary.h
|-- src/
| |-- mylibrary.cpp
Using LoadLibrary
, GetProcAddress
, and FreeLibrary
is another approach to working with DLLs, which allows dynamic loading of DLLs at runtime. This method is useful when you need to load a DLL dynamically based on certain conditions or when the DLL might not be present at compile time. Here’s how these functions work and how you can use them in a C++ project:
LoadLibrary
is used to load a DLL at runtime. It returns a handle to the loaded DLL module.
GetProcAddress
is used to get the address of an exported function from the DLL. This function takes the handle to the DLL module and the name of the function as parameters.
FreeLibrary
is used to unload the DLL when it is no longer needed. This helps to free up resources.
Here’s an example of how you can use these functions to load a DLL and call its functions at runtime:
#ifndef MYLIBRARY_H
#define MYLIBRARY_H
#ifdef _WIN32
#define MYLIBRARY_API __declspec(dllexport)
#else
#define MYLIBRARY_API
#endif
extern "C" {
MYLIBRARY_API int add(int a, int b);
MYLIBRARY_API int subtract(int a, int b);
}
#endif // MYLIBRARY_H
#include "mylibrary.h"
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
Compile the DLL using the following command:
g++ -shared -o MyLibrary.dll -I include src/mylibrary.cpp
#include <iostream>
#include <windows.h>
typedef int (*AddFunc)(int, int);
typedef int (*SubtractFunc)(int, int);
int main() {
// Load the DLL
HMODULE hModule = LoadLibrary("MyLibrary.dll");
if (!hModule) {
std::cerr << "Could not load the DLL" << std::endl;
return 1;
}
// Get function addresses
AddFunc add = (AddFunc)GetProcAddress(hModule, "add");
SubtractFunc subtract = (SubtractFunc)GetProcAddress(hModule, "subtract");
if (!add || !subtract) {
std::cerr << "Could not locate the functions" << std::endl;
FreeLibrary(hModule);
return 1;
}
// Use the functions
int result1 = add(3, 4);
int result2 = subtract(7, 2);
std::cout << "Addition: " << result1 << std::endl;
std::cout << "Subtraction: " << result2 << std::endl;
// Free the DLL
FreeLibrary(hModule);
return 0;
}
- Compile the Application:
g++ -o MyApp.exe main.cpp
-
Ensure the DLL is in the Same Directory as the Executable.
-
Run the Application:
./MyApp.exe
- Flexibility: You can load different versions of the DLL or load DLLs based on runtime conditions.
- Optional Components: If a DLL is optional, you can check for its existence and load it if available, or provide fallback functionality.
- Delayed Loading: DLLs are loaded only when needed, which can reduce the initial load time of the application.
Using LoadLibrary
, GetProcAddress
, and FreeLibrary
provides greater flexibility in managing DLLs, especially when the DLLs might not be available at compile time or when you need to load DLLs dynamically based on certain conditions. This method is particularly useful for large applications with optional components or plugins.
Let's go through an example where we define a global variable in a DLL and then use that global variable in another C++ application.
#ifndef MYLIBRARY_H
#define MYLIBRARY_H
#ifdef _WIN32
#define MYLIBRARY_API __declspec(dllexport)
#else
#define MYLIBRARY_API
#endif
extern "C" {
MYLIBRARY_API int add(int a, int b);
MYLIBRARY_API int subtract(int a, int b);
}
// Declare the global variable
extern MYLIBRARY_API int globalVar;
#endif // MYLIBRARY_H
#include "mylibrary.h"
// Define the global variable
int globalVar = 100;
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
g++ -shared -o MyLibrary.dll -I include src/mylibrary.cpp
#include <iostream>
#include <windows.h>
// Define function pointers
typedef int (*AddFunc)(int, int);
typedef int (*SubtractFunc)(int, int);
int main() {
// Load the DLL
HMODULE hModule = LoadLibrary("MyLibrary.dll");
if (!hModule) {
std::cerr << "Could not load the DLL" << std::endl;
return 1;
}
// Get function addresses
AddFunc add = (AddFunc)GetProcAddress(hModule, "add");
SubtractFunc subtract = (SubtractFunc)GetProcAddress(hModule, "subtract");
// Access the global variable directly
int* globalVarPtr = (int*)GetProcAddress(hModule, "globalVar");
if (!add || !subtract || !globalVarPtr) {
std::cerr << "Could not locate the functions or global variable" << std::endl;
FreeLibrary(hModule);
return 1;
}
// Use the functions
int result1 = add(3, 4);
int result2 = subtract(7, 2);
std::cout << "Addition: " << result1 << std::endl;
std::cout << "Subtraction: " << result2 << std::endl;
// Access and modify the global variable
std::cout << "Initial Global Variable: " << *globalVarPtr << std::endl;
*globalVarPtr = 200;
std::cout << "Modified Global Variable: " << *globalVarPtr << std::endl;
// Free the DLL
FreeLibrary(hModule);
return 0;
}
- Compile the Application:
g++ -o MyApp.exe main.cpp -I include
-
Ensure the DLL is in the Same Directory as the Executable.
-
Run the Application:
./MyApp.exe
-
Loading the DLL:
-
HMODULE hModule = LoadLibrary("MyLibrary.dll");
- This function loads the DLL and returns a handle to it. If the DLL is not found, it returns
NULL
.
- This function loads the DLL and returns a handle to it. If the DLL is not found, it returns
-
-
Getting Function Addresses:
-
AddFunc add = (AddFunc)GetProcAddress(hModule, "add");
-
GetProcAddress
retrieves the address of theadd
function from the DLL. The function name must match the name used in the DLL.
-
-
-
Accessing the Global Variable:
-
int* globalVarPtr = (int*)GetProcAddress(hModule, "globalVar");
-
GetProcAddress
is used to retrieve the address of theglobalVar
. The name must match the name used in the DLL.
-
-
-
Using the Functions and Global Variable:
- The functions are called through the function pointers
add
andsubtract
. - The global variable is accessed and modified through the pointer
globalVarPtr
.
- The functions are called through the function pointers
-
Freeing the DLL:
-
FreeLibrary(hModule);
- This function unloads the DLL and frees the associated resources.
-
This example demonstrates how to create a DLL with global variables and use them in another C++ application. By using dynamic loading (LoadLibrary
, GetProcAddress
, FreeLibrary
), you can load the DLL at runtime and access its functions and global variables. This approach is useful for modular applications, plugin systems, and scenarios where the DLL might not be available at compile time.
The performance of a DLL (Dynamic Link Library) versus a statically linked library (a normal C++ library) can depend on several factors. Here are some considerations:
-
Loading and Linking:
- DLL: DLLs are loaded dynamically at runtime, which means there is an overhead associated with loading and resolving symbols (functions and variables) during runtime. This overhead typically includes additional memory and CPU cycles.
- Static Library: Static libraries are linked directly into the executable at compile time. This means there is no runtime overhead for loading or resolving symbols.
-
Execution Speed:
- Once loaded, the performance difference between a DLL and a static library for executing functions and accessing data is generally negligible. Both are accessed in memory similarly once they are loaded.
-
Startup Time:
- DLL: Dynamic loading of DLLs can result in slower startup times compared to statically linked libraries because of the time required to load and link the DLLs during program initialization.
- Static Library: Statically linked libraries are typically faster to start because all required code is already present in the executable.
-
Memory Usage:
- DLL: DLLs can lead to more efficient memory usage in certain scenarios because they can be shared across multiple applications. This is particularly beneficial when multiple applications use the same DLL.
- Static Library: Static libraries may result in slightly higher memory usage since each application that uses the library will have its own copy of the library code.
- Modularity and Updates: DLLs allow for modular code that can be updated independently of the main application.
- Plugin Architectures: DLLs are commonly used for plugin architectures where additional functionality can be dynamically loaded at runtime.
- Versioning: DLLs can support versioning mechanisms, allowing different versions of the same DLL to be used by different applications.
- CPU Overhead: The overhead associated with loading and resolving symbols in DLLs can impact performance, especially in applications that require frequent dynamic loading and unloading of DLLs.
- Startup Time: If startup time is critical, statically linking libraries might be more suitable to avoid the runtime loading overhead of DLLs.
In general, the performance difference between using a DLL and a statically linked library in terms of execution speed is minimal once the DLL is loaded. However, DLLs may introduce additional startup overhead and slightly higher memory usage compared to static libraries. The choice between using a DLL or a static library often depends on factors such as modularity requirements, memory constraints, startup time sensitivity, and the need for runtime flexibility. Each has its strengths and is chosen based on the specific needs and constraints of the application or system.