DLL (Dynamic Link Libraries)

Para usar una DLL primero tiene que ser importada en el proceso y ya se puede usar sus funciones

Crear DLL

Visual Studio: nuevo proyecto de tipo Dynamic-Link Library (DLL)
Visual Studio: nuevo proyecto de tipo Dynamic-Link Library (DLL)
El dllmain.cpp que genera la plantilla
El dllmain.cpp que genera la plantilla
Podemos renombrar dllmain.cpp a dllmain.c

Quitar Header Precompilados

Puede existir framework.h pch.h pch.cpp al crear el proyecto pero no son necesarios y se pueden borrar
Los archivos de la plantilla que se pueden borrar
Los archivos de la plantilla que se pueden borrar
Después de borrar ajustamos las propiedades
Menú Project, opción Properties del proyecto
Menú Project, opción Properties del proyecto
Propiedades del proyecto: Precompiled Header en Use (/Yu)
Propiedades del proyecto: Precompiled Header en Use (/Yu)
Se cambia a Not Using Precompiled Headers
Se cambia a Not Using Precompiled Headers

Exportar función

c
#include <Windows.h>

// Exported function
extern "C" __declspec(dllexport) VOID WINAPI HelloWorld(VOID)
{
    MessageBoxA(NULL, "Hello, World!", "DLL Message", MB_ICONINFORMATION);
}

// Entry point for the DLL
BOOL WINAPI DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call) {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}

Ver funciones exportadas

terminal
dumpbin /exports sampleDLL.dll

Load-Time Linking vs Run-Time Linking

Load-time: Windows carga la DLL al arrancar el programa, antes de main. Tú llamas a la función directamente (MessageBoxA(...)) y el compilador lo apunta en la IAT del .exe. Si la DLL falta, el programa ni arranca.
Run-time: tú cargas la DLL cuando quieras con LoadLibraryA, buscas la función con GetProcAddress y la llamas mediante un puntero a función (typedef + cast). Si la DLL falta, LoadLibraryA devuelve NULL y tú decides qué hacer. Al terminar, FreeLibrary.
Método usado Run-Time
La DLL generada junto al ejecutable que la carga
La DLL generada junto al ejecutable que la carga
c
#include <stdio.h>
#include <Windows.h>

// Casteamos la funcion de la DLL ya que se exporto como VOID WINAPI HelloWorld(VOID);
typedef VOID(WINAPI* fnHelloWorld)(VOID);

int main() {

    // Cargamos la DLL
    HMODULE hModule = GetModuleHandleA("DllGenerador.dll");
    BOOL cargadoManualmente = FALSE;

    if (hModule == NULL) {
        hModule = LoadLibraryA("DllGenerador.dll");
        if (hModule == NULL) {
            printf("[!] LoadLibraryA Failed With Error: %lu\n", GetLastError());
            return -1;
        }

        cargadoManualmente = TRUE;
    }

    printf("[+] Se ha cargado correctamente la dll Handle: 0x%p\n", hModule);

    // Obtenemos puntero hacia la dir de Memoria de HelloWorld de la DLL y lo almacenamos en pHelloWorld
    PVOID pHelloWorld = GetProcAddress(hModule, "HelloWorld");

    if (pHelloWorld == NULL) {
        printf("[!] GetProcAddress Failed With Error: %lu\n", GetLastError());
        if (cargadoManualmente) FreeLibrary(hModule);
        return -1;
    }

    // Type-Casteamos la direccion de memoria para hacer que matchee con el casteo de arriba
    fnHelloWorld HelloWorld = (fnHelloWorld)pHelloWorld;

    // Ejecutamos la función
    HelloWorld();

    if (cargadoManualmente)
        FreeLibrary(hModule);

    return 0;
}

RunDLL32

terminal
rundll32.exe <dllname>, <function exported to run>
terminal
rundll32 .\DllGenerador.dll, HelloWorld