Concept

There are two main types of libraries: Static and Dynamic (also called Shared).

  • A static library is statically linked to a program and is available at compile time.
  • A dynamic (or shared) library, on the other hand, is dynamically linked and available at runtime.

Static linking assumes that the library code is built into the final block of code, unlike dynamic linking. But the process of dynamic linking takes some time.
It follows that the static library is faster, but the dynamic library takes up less memory.

Dynamic library can also contain an import library which is intended to be statically linked to resolve external references to exported dynamic library functions.

Unreal Engine has built-in tool called UBT (UnrealBuildTool) to manage the build process. All build related code must be in the appropriate build.cs file of your module (in your project or plugin source code).

Library extensions and prefixes

To briefly understand what your library suits for, you can use the following table.

PlatformStatic libraryDynamic libraryLibrary prefix
Windows.lib.dlln/a
UNIX-like (Linux, Android, iOS).a.solib
OS X (Mac).a.dyliblib

Also keep in mind that the library depends on your architecture. For example, a library built for x32 architecture will not be compatible with x64 architecture

Before the implementation

The examples below use a plugin named PLUGIN_EXAMPLE and a third party library named lib_[PLATFORM_NAME]_example (where [PLATFORM_NAME] is the name of the platform, e.g. win64, linux, ios, etc) located in the PLUGIN_EXAMPLE/Source/ThirdParty/ directory.

How to integrate static library

Let’s say you have a Windows x64 static library named lib_win64_example.lib. To integrate this library, you need to add its path to PublicAdditionalLibraries in your build.cs:

PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "..", "ThirdParty", "lib_win64_example.lib"));

Click to see full example
using System;
using System.IO;
using UnrealBuildTool;

public class PLUGIN_EXAMPLE : ModuleRules
{
	public PLUGIN_EXAMPLE(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;

		PublicDependencyModuleNames.AddRange(
			new string[]
			{
				"Core"
			}
		);

		PrivateDependencyModuleNames.AddRange(
			new string[]
			{
				"CoreUObject"
			}
		);

		// Include the library
		PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "..", "ThirdParty", "lib_win64_example.lib"));
	}
}

You then need to include the header files in the file you want to call the library functions from. To remove the various warnings/errors that might cause the build to fail, you can wrap your include with the THIRD_PARTY_INCLUDES_START and THIRD_PARTY_INCLUDES_END macros:

THIRD_PARTY_INCLUDES_START
#include "ThirdParty/header_to_include.h"
THIRD_PARTY_INCLUDES_END

How to integrate dynamic library

If the dynamic library comes with import library

If you have an import library, then you do not need to separately import the required functions from the dynamic library.

Let’s say you have a Windows x64 dynamic library named lib_win64_example.dll and a corresponding import library named lib_win64_import_example.lib.

To include your import library, you need to add its path to PublicAdditionalLibraries in your build.cs:

PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "..", "ThirdParty", "lib_win64_import_example.lib"));

You then need to add your dynamic library as a runtime dependency to put it alongside the executable:

RuntimeDependencies.Add("$(PluginDir)/ThirdParty/lib_win64_example.dll");

Afterwards, on Windows, you will likely need to specify the DLL to be delay-loaded. This is done by adding the following line to your build.cs:

PublicDelayLoadDLLs.Add("lib_win64_example.dll");

Click to see full example
using System;
using System.IO;
using UnrealBuildTool;

public class PLUGIN_EXAMPLE : ModuleRules
{
	public PLUGIN_EXAMPLE(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;

		PublicDependencyModuleNames.AddRange(
			new string[]
			{
				"Core"
			}
		);

		PrivateDependencyModuleNames.AddRange(
			new string[]
			{
				"CoreUObject",
				"Projects"
			}
		);

		// Include the import library
		PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "..", "ThirdParty", "lib_win64_import_example.lib"));

		// Put the library along with the executable
		RuntimeDependencies.Add("$(PluginDir)/ThirdParty/lib_win64_example.dll");
		
		// Load library
		PublicDelayLoadDLLs.Add("lib_win64_example.dll");
	}
}

The next step is to specify when to load and free your dynamic library. In the following example, the lifetime of the library will match the lifetime of the module. That is, the library will be loaded on module startup and freed on module shutdown.

You need to open your module’s interface file PLUGIN_EXAMPLE.h and add a field that will contain your dynamic library handler:

void* DynamicLibExampleHandle;

Click to see full example
class FPLUGIN_EXAMPLEModule : public IModuleInterface
{
public:
	virtual void StartupModule() override;
	virtual void ShutdownModule() override;
	
	void* DynamicLibExampleHandle;
};

Then in your PLUGIN_EXAMPLE.cpp add the following code to StartupModule to load your library:

const FString BasePluginDir = IPluginManager::Get().FindPlugin("PLUGIN_EXAMPLE")->GetBaseDir();

const FString LibExamplePath = FPaths::Combine(*BasePluginDir, TEXT("Source/ThirdParty/lib_win64_example.dll"));

DynamicLibExampleHandle = FPlatformProcess::GetDllHandle(*LibExamplePath);

if (DynamicLibExampleHandle != nullptr)
{
	UE_LOG(LogTemp, Log, TEXT("lib_win64_example.dll loaded successfully!"));
}
else
{
	UE_LOG(LogTemp, Fatal, TEXT("lib_win64_example.dll failed to load!"));
}

And the following code to ShutdownModule to unload your library:

FPlatformProcess::FreeDllHandle(DynamicLibExampleHandle);
DynamicLibExampleHandle = nullptr;

Click to see full example
void FPLUGIN_EXAMPLEModule::StartupModule()
{
	const FString BasePluginDir = IPluginManager::Get().FindPlugin("PLUGIN_EXAMPLE")->GetBaseDir();

	const FString LibExamplePath = FPaths::Combine(*BasePluginDir, TEXT("Source/ThirdParty/lib_win64_example.dll"));

	DynamicLibExampleHandle = FPlatformProcess::GetDllHandle(*LibExamplePath);

	if (DynamicLibExampleHandle != nullptr)
	{
		UE_LOG(LogTemp, Log, TEXT("lib_win64_example.dll loaded successfully!"));
	}
	else
	{
		UE_LOG(LogTemp, Fatal, TEXT("lib_win64_example.dll failed to load!"));
	}
}

void FPLUGIN_EXAMPLEModule::ShutdownModule()
{
	FPlatformProcess::FreeDllHandle(DynamicLibExampleHandle);
	DynamicLibExampleHandle = nullptr;
}

You then need to include the header files in the file you want to call the library functions from. To remove the various warnings/errors that might cause the build to fail, you can wrap your include with the THIRD_PARTY_INCLUDES_START and THIRD_PARTY_INCLUDES_END macros:

THIRD_PARTY_INCLUDES_START
#include "ThirdParty/header_to_include.h"
THIRD_PARTY_INCLUDES_END

If there is only a dynamic library

If you only have a dynamic library, you will need to manually import the required functions.

In your build.cs file, you need to add your dynamic library as a runtime dependency to put it along with the executable:

RuntimeDependencies.Add("$(PluginDir)/ThirdParty/lib_win64_example.dll");

Click to see full example
using UnrealBuildTool;

public class PLUGIN_EXAMPLE : ModuleRules
{
	public PLUGIN_EXAMPLE(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;

		PublicDependencyModuleNames.AddRange(
			new string[]
			{
				"Core"
			}
		);

		PrivateDependencyModuleNames.AddRange(
			new string[]
			{
				"CoreUObject",
				"Projects"
			}
		);

		// Put the library along with the executable
		RuntimeDependencies.Add("$(PluginDir)/ThirdParty/lib_win64_example.dll");
	}
}

The next step is to specify when to load and free your dynamic library. In the following example, the lifetime of the library will match the lifetime of the module. That is, the library will be loaded on module startup and freed on module shutdown.

You need to open your module’s interface file PLUGIN_EXAMPLE.h and add a field that will contain your dynamic library handler:

void* DynamicLibExampleHandle;

Click to see full example
class FPLUGIN_EXAMPLEModule : public IModuleInterface
{
public:
	virtual void StartupModule() override;
	virtual void ShutdownModule() override;
	
	void* DynamicLibExampleHandle;
};

Then in your PLUGIN_EXAMPLE.cpp add the following code to StartupModule to load your library:

const FString BasePluginDir = IPluginManager::Get().FindPlugin("PLUGIN_EXAMPLE")->GetBaseDir();

const FString LibExamplePath = FPaths::Combine(*BasePluginDir, TEXT("Source/ThirdParty/lib_win64_example.dll"));

DynamicLibExampleHandle = FPlatformProcess::GetDllHandle(*LibExamplePath);

if (DynamicLibExampleHandle != nullptr)
{
	UE_LOG(LogTemp, Log, TEXT("lib_win64_example.dll loaded successfully!"));
}
else
{
	UE_LOG(LogTemp, Fatal, TEXT("lib_win64_example.dll failed to load!"));
}

And the following code to ShutdownModule to unload your library:

FPlatformProcess::FreeDllHandle(DynamicLibExampleHandle);
DynamicLibExampleHandle = nullptr;

Click to see full example
void FPLUGIN_EXAMPLEModule::StartupModule()
{
	const FString BasePluginDir = IPluginManager::Get().FindPlugin("PLUGIN_EXAMPLE")->GetBaseDir();

	const FString LibExamplePath = FPaths::Combine(*BasePluginDir, TEXT("Source/ThirdParty/lib_win64_example.dll"));

	DynamicLibExampleHandle = FPlatformProcess::GetDllHandle(*LibExamplePath);

	if (DynamicLibExampleHandle != nullptr)
	{
		UE_LOG(LogTemp, Log, TEXT("lib_win64_example.dll loaded successfully!"));
	}
	else
	{
		UE_LOG(LogTemp, Fatal, TEXT("lib_win64_example.dll failed to load!"));
	}
}

void FPLUGIN_EXAMPLEModule::ShutdownModule()
{
	FPlatformProcess::FreeDllHandle(DynamicLibExampleHandle);
	DynamicLibExampleHandle = nullptr;
}

You then need to include the header files in the file you want to call the library functions from. To remove the various warnings/errors that might cause the build to fail, you can wrap your include with the THIRD_PARTY_INCLUDES_START and THIRD_PARTY_INCLUDES_END macros:

THIRD_PARTY_INCLUDES_START
#include "ThirdParty/header_to_include.h"
THIRD_PARTY_INCLUDES_END

To properly get the required functions to call from your dynamic library, you need to use FPlatformProcess::GetDllExport. To make it easier to get functions, you can create the following macro:

 CALL_LIBEXAMPLE_FUNC(FunctionName)                                                                                                                            \
    []() {                                                                                                                                                     \
        using FunctionType = decltype(&FunctionName);                                                                                                          \
        const FName ModuleName = FName(TEXT("PLUGIN_EXAMPLE"));                                                                                                \
        const FPLUGIN_EXAMPLEModule& CurrentModule = FModuleManager::GetModuleChecked<FPLUGIN_EXAMPLEModule>(ModuleName);                                      \
        static FunctionType FunctionPtr = (FunctionType)(FPlatformProcess::GetDllExport(CurrentModule.DynamicLibExampleHandle, TEXT(#FunctionName)));          \
        return FunctionPtr;                                                                                                                                    \
    }()

And then use it as follows:

// This is an example of an argument to pass to a function
bool ArgumentToPass = true;

// Let's say there is a function "getInvertedBool(bool)" in the dynamic library
CALL_LIBEXAMPLE_FUNC(getInvertedBool)(ArgumentToPass);

Support different platforms

Here is the example of static library integration with support for Win64, Linux and Mac platforms. You can easily modify it to suit your needs to support the required platforms.

using System;
using System.IO;
using UnrealBuildTool;

public class PLUGIN_EXAMPLE : ModuleRules
{
	private string GetExampleLibraryPath(ReadOnlyTargetRules Target)
	{
		if (Target.Platform == UnrealTargetPlatform.Win64)
		{
			return Path.Combine(ModuleDirectory, "..", "ThirdParty", "lib_win64_example.lib");
		}

		if (Target.Platform == UnrealTargetPlatform.Linux)
		{
			return Path.Combine(ModuleDirectory, "..", "ThirdParty", "lib_linux_example.a");
		}

		if (Target.Platform == UnrealTargetPlatform.Mac)
		{
			return Path.Combine(ModuleDirectory, "..", "ThirdParty", "lib_mac_example.a");
		}

		return null;
	}

	public PLUGIN_EXAMPLE(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;

		PrivateDependencyModuleNames.AddRange(
			new string[]
			{
				"CoreUObject",
				"Engine",
				"Core"
			}
		);

		string LibraryPath = GetExampleLibraryPath(Target);

		if (LibraryPath != null)
		{
			PublicAdditionalLibraries.Add(LibraryPath);
		}
	}
}