handmade.network » Wiki » 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");