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:

  1. 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.

  2. 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.

  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. Example: A common DLL in Windows is kernel32.dll, which contains core functions for memory management, input/output operations, and interrupt handling.

  8. 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.

  9. 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:

Step 1: Write the C++ Code

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.

  1. Define Functions and Classes: Write the functions and classes that will be part of the DLL.

  2. 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:

Step 1: Install Required Tools

  1. Install VS Code: Download and install Visual Studio Code from here.

  2. 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).
  3. Install CMake: CMake is a build system generator that you can download from here.

  4. Install VS Code Extensions:

    • C/C++ by Microsoft
    • CMake Tools by Microsoft

Step 2: Create the Project Structure

  1. Create a Folder for Your Project: Create a directory for your project, e.g., MyLibrary.
  2. 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

Step 3: Write the C++ Code

mylibrary.h

#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

mylibrary.cpp

#include "mylibrary.h"

int add(int a, int b) {
    return a + b;
}

int subtract(int a, int b) {
    return a - b;
}

Step 4: Configure CMake

CMakeLists.txt

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()

Step 5: Open the Project in VS Code

  1. Open Folder: Open the MyLibrary folder in VS Code.
  2. Configure CMake Tools: Click on the CMake Tools extension icon and select "Configure Project" to generate the necessary build files.

Step 6: Build the DLL

  1. Build the Project: Use the CMake Tools extension to build your project. This will generate the DLL file in the build directory.

Step 7: Use the DLL in an Application

Create a separate folder for your application, e.g., MyApp, and set it up to use the DLL.

  1. Project Structure:
MyApp/
|-- main.cpp
|-- CMakeLists.txt

main.cpp

#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;
}

CMakeLists.txt

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")
  1. Open the Application Folder in VS Code: Open the MyApp folder in VS Code.
  2. Configure and Build the Application: Use CMake Tools to configure and build your application.

Step 8: Run the 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.

Step 1: Install Required Tools

  1. Install VS Code: Download and install Visual Studio Code from here.
  2. Install MinGW (for Windows users):
    • Download MinGW from mingw-w64.
    • Add the bin directory of MinGW to your system PATH.

Step 2: Create the Project Structure

  1. Create a Folder for Your Project: Create a directory for your project, e.g., MyLibrary.
  2. 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

LoadLibrary is used to load a DLL at runtime. It returns a handle to the loaded DLL module.

GetProcAddress

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

FreeLibrary is used to unload the DLL when it is no longer needed. This helps to free up resources.

Example

Here’s an example of how you can use these functions to load a DLL and call its functions at runtime:

1. Create the DLL

mylibrary.h
#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
mylibrary.cpp
#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

2. Use the DLL in an Application

main.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 and Run the Application

  1. Compile the Application:
g++ -o MyApp.exe main.cpp
  1. Ensure the DLL is in the Same Directory as the Executable.

  2. Run the Application:

./MyApp.exe

Why Use This Approach?

  • 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.

Summary

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.

Step 1: Create the DLL

1.1 Header File: mylibrary.h

#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

1.2 Source File: mylibrary.cpp

#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;
}

1.3 Compile the DLL

g++ -shared -o MyLibrary.dll -I include src/mylibrary.cpp

Step 2: Create the Application that Uses the DLL

2.1 Application Source File: main.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;
}

Steps to Compile and Run the Application

  1. Compile the Application:
g++ -o MyApp.exe main.cpp -I include
  1. Ensure the DLL is in the Same Directory as the Executable.

  2. Run the Application:

./MyApp.exe

Explanation

  1. 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.
  2. Getting Function Addresses:

    • AddFunc add = (AddFunc)GetProcAddress(hModule, "add");
      • GetProcAddress retrieves the address of the add function from the DLL. The function name must match the name used in the DLL.
  3. Accessing the Global Variable:

    • int* globalVarPtr = (int*)GetProcAddress(hModule, "globalVar");
      • GetProcAddress is used to retrieve the address of the globalVar. The name must match the name used in the DLL.
  4. Using the Functions and Global Variable:

    • The functions are called through the function pointers add and subtract.
    • The global variable is accessed and modified through the pointer globalVarPtr.
  5. Freeing the DLL:

    • FreeLibrary(hModule);
      • This function unloads the DLL and frees the associated resources.

Summary

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:

DLLs vs. Static Libraries

  1. 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.
  2. 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.
  3. 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.
  4. 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.

Use Cases for DLLs

  • 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.

Performance Considerations

  • 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.

Conclusion

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.

⚠️ **GitHub.com Fallback** ⚠️