In the previous post, we created a simple plugin in C++ that we exported into a DLL. This DLL plugin takes 2 integers and adds them together and returns the sum back. In this post, we will import the DLL from the plugin project into a new UE4 project and access its functionality.
Project Set-Up:
We start by creating a new UE4 (engine version 4.20.2) project. Let’s create a C++ project with “Basic Code” and call it “DataStorageSample“. This is shown in the image below.
Once the project is created UE4 should open both the Visual Studio project with the source code as well as the editor window with the project loaded up with a simple sample scene.
Create Game Instance Class:
The first thing we will start with will be creating and setting up the Game Instance class for the project. Later we will access the functions of the DLL in the game instance class.
Let’s create a Game Instance class in C++. There are several ways you can do this. Check out the “Player Pawn C++ Class” section of the post from a previous series. The difference is you will need to choose “GameInstance” parent class instead of the pawn class. To do this, turn on “Show all Classes” on the top right of the window and type in “game instance” in the search text box and select the GameInstance class. Let’s call this class “cDataStorageGameInstance“.
Now that the base c++ game instance class is created we will set this as the project’s game instance in the project settings. Open the project settings page and search for “GameInstance” in the search box. Select “cDataStorageGameInstance” from the drop-down box and close the settings page. Let’s Compile the source code now just to make sure that we have set everything up correctly. The project should compile without any errors at this point.
Implement C++ object class (UObject) that imports the DLL functions:
Next, we will need a class that will import the C++ DLL and provides an interface for calling the functions that are implemented in the DLL. For this, we create a class of Object type. This is done the same as the previous section except we select “Object” as the parent class. Let’s call this new class “cDataStorageWrapper“. Once the class is created we can start by adding the importing functionality.
First, we start by declaring the functions that have been implemented in the C++ library. The function that was implemented in the post where the C++ DLL was created we implemented and exposed the function “int SumOf(int a, int b)“. Let’s start by defining this function in the “cDataStorageWrapper” header file as follows.
typedef int(*__SumOf)(int a, int b);
we need to make sure that the return type and the arguments match the function that we are trying to access. Next, we create a pointer to this function so that it can be called later. We will use the typedef that we created for this.
__SumOf m_funcSumOf;
We will need a handle to the library which. We use a void pointer to hold this.
void *v_dllHandle
Next, we will need to declare 3 functions. One would be used to import the DLL, the second to import the SumOf function and the third for calling the SumOf function. Here is the code snippet for the declaration of the functions.
UFUNCTION(BlueprintCallable, Category = "CPP Utilitites") bool ImportDLL(FString a_strFolderName, FString a_strDLLName); UFUNCTION(BlueprintCallable, Category = "CPP Utilitites") bool ImportMethod_SumOf(); UFUNCTION(BlueprintCallable, Category = "CPP Utilitites") int CallSumOf(int a_nNum1, int a_nNum2);
Since the implementation of these functions is really self-explanatory, I will just post the code below. Please put any questions in the comments and I will get back with an explanation.
Here is the complete header file:
#pragma once #include "CoreMinimal.h" #include "UObject/NoExportTypes.h" #include "cDataStorageWrapper.generated.h" /** * */ typedef int(*__SumOf)(int a, int b); UCLASS() class DATASTORAGESAMPLE_API UcDataStorageWrapper : public UObject { GENERATED_BODY() private: void *v_dllHandle; __SumOf m_funcSumOf; public: UFUNCTION(BlueprintCallable, Category = "CPP Utilitites") bool ImportDLL(FString a_strFolderName, FString a_strDLLName); UFUNCTION(BlueprintCallable, Category = "CPP Utilitites") bool ImportMethod_SumOf(); UFUNCTION(BlueprintCallable, Category = "CPP Utilitites") int CallSumOf(int a_nNum1, int a_nNum2); };
Here is the complete source file:
#include "cDataStorageWrapper.h" #include "Paths.h" bool UcDataStorageWrapper::ImportDLL(FString a_strFolderName, FString a_strDLLName) { FString filePath = *FPaths::GamePluginsDir() + a_strFolderName + "/" + a_strDLLName; if (FPaths::FileExists(filePath)) { v_dllHandle = FPlatformProcess::GetDllHandle(*filePath); // Retrieve the DLL. if (v_dllHandle != NULL) { return true; } } return false;// Return an error. } bool UcDataStorageWrapper::ImportMethod_SumOf() { if (v_dllHandle != NULL) { m_funcSumOf = NULL; FString procName = "SumOf";// Needs to be the exact name of the DLL method. m_funcSumOf = (__SumOf)FPlatformProcess::GetDllExport(v_dllHandle, *procName); if (m_funcSumOf != NULL) { return true; } } return false;// Return an error. } int UcDataStorageWrapper::CallSumOf(int a_nNum1, int a_nNum2) { if (m_funcSumOf == NULL) { UE_LOG(LogTemp, Error, TEXT("OPS Client Function was null")); return INT_MIN; } int sum = m_funcSumOf(a_nNum1, a_nNum2); UE_LOG(LogTemp, Error, TEXT("The sum of %d and %d is: %d"), a_nNum1, a_nNum2, sum); return sum; }
Create Wrapper UObject Instance in Game Instance:
Next, we will need to create a private object of the wrapper object and a function that imports the library along with the functionality that is exposed through it.
GameInstance Header file:
private: UPROPERTY() UcDataStorageWrapper* m_refDataStorageUtil; bool ImportDataStorageLibrary();
GameInstance Source file:
bool UcDataStorageGameInstance::ImportDataStorageLibrary() { //create the wrapper object m_refDataStorageUtil = NewObject<UcDataStorageWrapper>(this); if (m_refDataStorageUtil == NULL) { UE_LOG(LogTemp, Error, TEXT("Could not create the Data Storage Object")); return false; } //Import the DLL if (!m_refDataStorageUtil->ImportDLL("DataStorage", "SQLite3NativeDataStorage.dll")) { UE_LOG(LogTemp, Error, TEXT("Could not import the Data Storage DLL")); return false; } if (!m_refDataStorageUtil->ImportMethod_SumOf()) { UE_LOG(LogTemp, Error, TEXT("Could not import the SumOf Method from the Data Storage Library")); return false; } return true; }
As you can see in the code above, the function imports the DLL and the method that we have created in the library. You can see that the folder name is “DataStorage” and the DLL name is “SQLite3NativeDataStorage.dll“. We need to paste the DLL in the right folder for the game to access it. We will see this in the next section.
Next, we will need to call the function that we just created. I have called this in the Init function of the game instance class so that the library and the exposed functions are loaded as soon as the game is run.
All that is left to do now is to call the function in the wrapper object that calls the SumOf function in the library. You could expose this function to other classes and blueprints by making a function in the game instance class. For now, we will call it in the init function of game instance after the import is completed. Here is the completed init function in the game instance class:
Header file:
public: virtual void Init() override;
</pre> <div>void UcDataStorageGameInstance::Init()</div> <div>{</div> <div>Super::Init();</div> <div>if (ImportDataStorageLibrary())</div> <div>{</div> <div>int n1 = 1570;</div> <div>int n2 = 9220;</div> <div>int nSum = m_refDataStorageUtil->CallSumOf(n1, n2);</div> <div>UE_LOG(LogTemp, Log, TEXT("Sum of %d and %d is %d"), n1, n2, nSum);</div> <div>}</div> <div>}</div> <pre>
Adding the DLL to the Project Folder:
As mentioned in the previous post, we will need to add the DLL that was compiled for Unreal in the previous post to the project’s directory. We first need to create a “Plugins” folder in the project’s directory next to the “Content” folder.
Inside the”Plugins” folder, we create the “DataStorage” folder exactly as we mentioned in the import function of the game instance class source code (previous section). We will now paste the “SQLite3NativeDataStorage.dll” in the “DataStorage” folder.
Thanks for the tutorial. How would one go about importing DLL function inside UE4 as a plugin, i.e. creating third party plugin using UE4 plugin menu & then importing DLL function into it? The process seems quite involved & any guidance there would be much appreciated.
Yes. That is a different way of getting plugins to work with UE4. I might do a future series on that.
Hey DarkRyder!
The repository is private
Thanks for the series; well written!
Some syntax coloring wouldn’t hurt though 😀
paths.h not working in UE4