While our game may be running without any issues in the editor or even in…
Consuming lib and dll files
In this post I’m going to show you how you can consume .lib and .dll files in your ue4 project. In case you don’t have a library or a dll file hanging around I’m going to show you how to create one and provide some example files that I have created for this post.
This post is going to be divided in the following categories:
- Creating a lib file
- Consuming the file in your ue4 project
- Creating a dll file
- Consuming the dll file in your ue4 project
1. Creating a lib file
In order to create a lib file in visual studio search for the expression “Static Libray” and create a new project
In the project that I have created, I added a new header and source file named OrfeasMathLibrary.h and OrfeasMathLibrary.cpp respectively. Here’s the code for them:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
//OrfeasMathLibrary.h namespace OrfeasMathLibrary { class Arithmetic { public: // Returns a + b static double Add(double a, double b); // Returns a - b static double Subtract(double a, double b); // Returns a * b static double Multiply(double a, double b); // Returns a / b static double Divide(double a, double b); }; } |
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 |
// OrfeasStaticLibrary.cpp : Defines the functions for the static library. #include "pch.h" #include "OrfeasMathLibrary.h" namespace OrfeasMathLibrary { double Arithmetic::Add(double a, double b) { return a + b; } double Arithmetic::Subtract(double a, double b) { return a - b; } double Arithmetic::Multiply(double a, double b) { return a * b; } double Arithmetic::Divide(double a, double b) { return a / b; } } |
The pch header file is a precompiled header generated from Visual studio. So once you have typed in the code above, compile your project in Release and x64 version. Ideally, you should compile your project in all the different scenarios that you will be building your ue4 project in order to maintain compatibility. This means maybe including debug and x32 versions as well
Once you compile your project in x64 version, the following file will be generated in your visual studio’s project directory: [Visual_Studio_Project_Directory] -> x64 -> Release -> NameOfSolution.lib
You should keep in mind the location of that file since we will need to reference in the ue4 project in the next step
2. Consuming the .lib file
In order to consume the lib file we have created above, first of all, navigate to your ue4 project in the .Build.cs file and add the following line:
1 2 |
//Change this to match your lib file's path PublicAdditionalLibraries.Add(@"C:/Users/Orfeas/source/repos/OrfeasStaticLibrary/x64/Release/OrfeasStaticLibrary.lib"); |
Then, in order to call the “Add” function from your .lib file, create a new blueprint function library and type in the following code in the header file:
1 2 3 4 |
public: UFUNCTION(BlueprintCallable) static void PrintSumFromLib(float a, float b); |
Afterwards, in its source file, include the header file “OrfeasMathLibrary.h” (or make sure to match the name of C++ class in the lib file).
Then, type in the following implementation for the PrintSumFromLib function:
1 2 3 4 5 6 7 8 |
void UOrfeasBlueprintFunctionLibrary::PrintSumFromLib(float a, float b) { //At this point make sure to //1) Include the "OrfeasMathLibrary.h" //2) Include the lib file complete path in PublicAdditionalLibraries in your [Project].Build.cs file double Sum = OrfeasMathLibrary::Arithmetic::Add(a, b); GLog->Log("Sum from static lib:" + FString::SanitizeFloat(Sum)); } |
At this point, you can call the function from anywhere in your Blueprints and see the result coming from the .lib file! Moreover, intellisense should be able to pick up these functions and provide helpful tips.
3. Creating a .dll file
In order to create a dll file, from visual studio select either the dynamic-link library with exports or dynamic-link library.
In case you select the first one, visual studio is going to generate some template code for you. You can either build on top of that or type in your own code.
In my case, here is the header file for my project:
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 |
// ---- Code generated by VS // The following ifdef block is the standard way of creating macros which make exporting // from a DLL simpler. All files within this DLL are compiled with the MATHISFUNDLL_EXPORTS // symbol defined on the command line. This symbol should not be defined on any project // that uses this DLL. This way any other project whose source files include this file see // MATHISFUNDLL_API functions as being imported from a DLL, whereas this DLL sees symbols // defined with this macro as being exported. #ifdef MATHISFUNDLL_EXPORTS #define MATHISFUNDLL_API __declspec(dllexport) #else #define MATHISFUNDLL_API __declspec(dllimport) #endif // ---- end of code generated by VS //By default C++ is performing name mangling of types. //This means that if we have a function named "Sum" the compiler may internally generate a different name for it eg __Sum_ or something different //By including the following ifdef we're telling the compiler "In case you're compiling C++ language, make sure the method names have C linkage (ie don't change their names) //For more information: https://stackoverflow.com/questions/1041866/what-is-the-effect-of-extern-c-in-c #ifdef __cplusplus extern "C" { #endif MATHISFUNDLL_API float Sum(float a, float b); MATHISFUNDLL_API int GetFibonacciNTerm(int Term); #ifdef __cplusplus } #endif |
The reason we’re typing MATHISFUNDLL_API before the exposed functions is to mark them as __declspec(dllexport) meaning that we want to make them visible to the application that is consuming our dll. For more information regarding the use of extern C please take a look at the comments above and follow the provided link.
At this point we’re done with the header file so let’s proceed on typing in the logic for the mentioned functions above:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
MATHISFUNDLL_API float Sum(float a, float b) { return a+b; } MATHISFUNDLL_API int GetFibonacciNTerm(int Term) { //https://www.mathsisfun.com/numbers/fibonacci-sequence.html //Recursive way of calculating fibonacci term. Not the best algorithm in terms of efficiency but it works in our case :) if (Term == 0) { return 0; } else if (Term == 1) { return 1; } else return GetFibonacciNTerm(Term - 1) + GetFibonacciNTerm(Term - 2); } |
At this point, set your solution configuration to Release and your platform to x64 and you can close visual studio after compiling. Once we’re done with that it’s time to consume the produced dll from the engine
4. Consuming dll files
The process of consuming dll files requires the following three steps:
- Creating a dll handle that points to the dll we want to consume
- Creating a dll export that is pointing to the function of the dll we would like to use
- Calling the pointing function
Heading over to the same Blueprint Function Library that I created before, I’ve added the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
private: /** * Reference of the dll handle */ static void* DllHandle; /** * Attempts to point the dll handle to the dll location */ static bool LoadDllHandle(); public: UFUNCTION(BlueprintCallable) static void PrintSumFromLib(float a, float b); UFUNCTION(BlueprintCallable) static void PrintSumFromDll(float a, float b); /* Term >=0 */ UFUNCTION(BlueprintCallable) static void PrintFibonacciTerm(int32 Term); |
Then, on the source file:
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 |
void* UOrfeasBlueprintFunctionLibrary::DllHandle=nullptr; bool UOrfeasBlueprintFunctionLibrary::LoadDllHandle() { FString DllFilePath = FPaths::ProjectDir() + "/Binaries/Win64/MATHISFUNDLL.dll"; if (FPaths::FileExists(DllFilePath)) { DllHandle = FPlatformProcess::GetDllHandle(*DllFilePath); } return DllHandle!=nullptr; } void UOrfeasBlueprintFunctionLibrary::PrintSumFromDll(float a, float b) { if (DllHandle || LoadDllHandle()) //We have a valid dll handle { //We will try to store the Sum function that exists in our loaded dll file in the DllExport //void* DllExport = FPlatformProcess::GetDllExport(DllHandle,*FString("AddNumbers")); void* DllExport = FPlatformProcess::GetDllExport(DllHandle,*FString("Sum")); if (DllExport) { //Declare a type definition for a function that accepts 2 float params and has float return type in order to store the Sum function from the dll typedef float(*GetSum)(float a, float b); //Type cast the valid dll export to GetSum type GetSum SumFunc = (GetSum)(DllExport); //Call the function & print the result float Result = (float)SumFunc(a,b); GLog->Log(FString::SanitizeFloat(a)+" + "+ FString::SanitizeFloat(b)+"="+FString::SanitizeFloat(Result)); } } } void UOrfeasBlueprintFunctionLibrary::PrintFibonacciTerm(int32 Term) { if (DllHandle || LoadDllHandle()) { //Same approach as PrintSumFromDll void* DllExport = FPlatformProcess::GetDllExport(DllHandle,*FString("GetFibonacciNTerm")); if (DllExport) { typedef int32 (*GetFibonacciTerm)(int32 Term); GetFibonacciTerm FibonacciFunc = (GetFibonacciTerm)(DllExport); int32 FibonacciTerm = (int32)FibonacciFunc(Term); GLog->Log("Fibonacci Term #"+FString::FromInt(Term) +":"+FString::FromInt(FibonacciTerm)); } } } |
At this point, once we compile we can call the functions Sum and GetFibonacciNTerm from our dll file via either C++ or Blueprint code:
Of course you can write up more complicated functions and logic but this post showcases the basic setup for consuming lib and dll files. At this point I would like to thank Rama and ZKarmaKun for their invaluable help. Thanks for reading!
Hi,
thanks for this tutorial. Very helpful and easy to follow. 🙂
I checked your Github repo and noticed that it is pointing to this site.
Would you mind putting this tutorial into the README.md?
That’d be much more handy for later use when I want to use the cloned repo from my hard disk.
Cheers
Apparently I missed that, thanks for pointing it out!
Have a good one & stay safe,
-Orfeas