The 2024 Wheel Reinvention Jam is in 4 days. September 23-29, 2024. More info

Cross platform opening dynamic library and loading functions from it

To load dynamic libraries at runtime, the code for each platform is different. The below shows examples of loading a library at runtime and extracting a function pointer out of the library.

Windows version

1
2
HMODULE handle = LoadLibraryA("myLibrary.dll");       
function_signature *pointerToFunction = (function_signature *)GetProcAddress(handle, "functionName");

Now that you get the basic idea, let's see a working example with comments explaining each step. This is one common way that you might choose to do it in practice.

In any non-trivial application, you could be loading many different libraries, and functions. Some of those libraries and functions may be optional. We will do this in small steps:

  1. We will #define a macro for creating the function signature and stub.
  2. We will use the macro to typedef a non-pointer version of the function
  3. We will use the macro to make a stub function that can be used in place of a failed lookup.
  4. At some arbitrary point (in main, in another function, or in a loader function that you create) we will load the library, testing to various versions.
  5. If the library has loaded, we will then try to load a function pointer with the signature we defined.
  6. If that function pointer is null, we will set it to the stub function.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <windows.h>
#include <xinput.h>
#include <stdio.h>
// First, we define a macro that
// prints out the "signature"
#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE* pState)
// Now we create a typedef for this function pointer
typedef X_INPUT_GET_STATE(x_input_get_state);
// The above will expand to this:
// typedef DWORD WINAPI x_input_get_state(DWORD dwUserIndex, XINPUT_STATE* pState);
// Now we create a "stub" in case the function couldn't be loaded
X_INPUT_GET_STATE(XInputGetStateStub) { 
    return(ERROR_DEVICE_NOT_CONNECTED);
}
int main(int argc, char* argv[]) {
    // First we try to load the "latest" version
    HMODULE x_input_lib = LoadLibraryA("xinput1_4.dll");
    if ( ! x_input_lib ) {
        // If that version wasn't found, we try to load
        // the next latest, and so on.
        printf("Failed to load xinput 1.4\n");
        x_input_lib = LoadLibraryA("xinput1_3.dll");
    }

    if ( ! x_input_lib ) {
        // If all else fails, well, we handle this
        // whatever way we need to, here I quit out.
        printf("Failed to load xinputlib\n");
        return(-1);
    }
    printf("XInput lib loaded\n");
    x_input_get_state* functionPointer = (x_input_get_state*)GetProcAddress(
        x_input_lib,
        "XInputGetState"
    );  
    // Now we check the functionPointer to see if it
    // is null.
    if ( ! functionPointer ) {
        printf("Could not load XInputGetState function, using stub\n");
        // If it's null, we set it to the stub
        functionPointer = XInputGetStateStub;
    }

    XINPUT_STATE ControllerState;

    if ( functionPointer(0,&ControllerState) == ERROR_SUCCESS) {
        printf("A controller is plugged in!\n");
    } else {
        printf("No 0th controller found!\n");
    }

    return(0);
}

Mac/Linux version

1
2
3
4
#include <dlfcn.h>

void *handle = dlopen ("myLibrary.dylib", RTLD_LAZY);
function_signature *pointerToFunction = (function_signature *)dlsym(handle, "functionName");

Edited by Jason Martin on