How do you link to a DLL from a plugin?

So I have some code I didn’t write in front of me and it mostly makes sense. To build it however it requires the addition of an SDK in the form of a DLL.

What I’ve worked out so far is to add everything to “Thirdparty/MySDK” with the *.h files under “Includes” and the .lib file under “Libraries”. This path is in relation to the game project, not the plugin. Is that correct?

I also have a .dll file, where does that go? How do I connect it up, or does it load it based on the *.lib name?

The code uses AddThirdPartyPrivateStaticDependencies() in the Module rules/build file and calls something I don’t think I can find in the project. This third party static dependency - what is it expecting? If the line is AddThirdPartyPrivateStaticDependencies(Target, “MyThing”), what is “MyThing”?

I found an example that led me to do this inside the module build file:

PublicDelayLoadDLLs.Add("ThirdParty/MyThing/Libraries/MyDll.dll");
PublicIncludePaths.AddRange(new string[] { "ThirdParty/MyThing/Includes" });
PublicAdditionalLibraries.Add("ThirdParty/MyThing/Libraries/MyLib.lib");

Is this the right way to do it?

As you can see it’s all new to me, so some insight would be appreciated. Feel free to comment on other things you think I need to know as well.

1, How you structure your .h files and libraries is up to you, but I do recommend having them separated logically like that.

.dll files will not be loaded automagically. It would go into the third party folder as well. For example: “Thirdparty/MySDK/DLL”. Linking and importing external assets like this is all done via the build.rc file. If you know how to include external libraries and such normally, outside of UE4, then you know the steps required inside UE4. You need to explicitly add the Header folders, the library folders and the dll folders, and you must also tell UE4 what library or DLL files to use.

2, I cant remember at the top of my head what it does, but I also do not think you need it.
PublicAdditionalLibraries.Add(“Path/to/lib/file.lib”); will add one .lib file. Write one for every file you want included.
PublicIncludePaths.Add(“String/to/include/folder”) for including the .h files.
The tricky part might be the mixing you might have to do with your compilation of the .lib files themselves. You must compile them as .dll files. Have a look.

3, These are correct.

PublicDelayLoadDLLs.Add(“ThirdParty/MyThing/Libraries/MyDll.dll”);
PublicIncludePaths.AddRange(new string[] { “ThirdParty/MyThing/Includes” });
PublicAdditionalLibraries.Add(“ThirdParty/MyThing/Libraries/MyLib.lib”);

Take a look at what I did for the VlcMedia plug-in.

The key ingredients for keeping everything self-contained in the plug-in are:

  • RuntimeDependencies: you need to add your DLLs and other required files to this array in your plug-in’s Build.cs file, so that UBT will include them when you package and deploy your game
  • FPlatformProcess::GetDllHandle: It is best to explicitly load your DLLs on startup from the directory within your plug-in folder. That way the OS won’t try to find them in the standard locations (where they cannot be found).

I used /MyPlugin/ThirdParty/ to store my third-party dependencies. My DLL initialization is in Vlc.cpp

Thanks! That was enormously helpful.

I have a further question: the build script is C#, while your DLL loader is C++.

What’s the difference between what C# does when you add a public include path and libraries or load a DLL and how you’ve done it in C++? What are the actual results of what the C# methods do?

The C# code is only used by Unreal Build Tool (UBT) when compiling your plug-in. UBT is currently written in C#, so the build configuration files are in C# as well.

The C++ code is what actually executes at run-time in your plug-in. In VlcMediaModule.cpp you can see that I am initializing my VLC stuff in StartupModule(), which is a function that is automatically called by the Engine’s module manager when the plug-in is loaded on startup.

I suppose a better way to phrase it is: what’s C# doing when those methods are called? Is it just moving files into places that UE expects them?

Yes, it will make sure that UBT knows about the existence of those files, so that they can be included in packaged builds of your game. Otherwise they would not be copied out, and your game wouldn’t work on other people’s computers. I hope that makes sense.

So I need all of the above, not one or the other. That answers my vaguely worded question, thank you! :slight_smile:

Yes, you want all of the above. You need the C# portion in order to package your plug-in correctly. Otherwise the third-party DLLs (and any other required files) will be missing from your game.

You also need to call GetDllHandle on the DLL(s) in your plug-in directory, or otherwise the OS will not find your DLLs. It will only search standard locations, such as the .exe directory and C:/Windows/System32, but it doesn’t know that your DLLs are actually tucked away inside your plug-in directory. Once GetDllHandle is called, the DLL is already loaded into memory, and subsequent attempts to access stuff in the third-party DLLs will not require the OS to search for them.

If you didn’t do this second step at run-time, then you would be forced to have all your third-party DLLs to be copied into the /Engine/Binaries/Win64/ directory, for example, which is not desirable.

As this thread was the best to help about DLL linking, I want to add more information as I got issues too.

So, to load DLL, you indeed need to use PublicDelayLoadDLLs but you can only provide the filename on it. If your DLL is in directory that is not [YourProject]/Binaries or [YourPlugin]/Binaries, you will need to add the directory path with PublicLibraryPaths.

PublicLibraryPaths.Add("../SDK/bin");
PublicDelayLoadDLLs.Add("myLib.dll");

In your software, you can use :

const FString dllDirectory = TEXT("../SDK/bin");
FPlatformProcess::PushDllDirectory(*dllDirectory);
{
 // Get DLL handle
 void* dllHandle = FPlatformProcess::GetDllHandle("myLib.dll");

 // ... do stuff with DLL

 // Release DLL
 FPlatformProcess::FreeDllHandle(dllHandle);
}
FPlatformProcess::PopDllDirectory(*dllDirectory);

Hope it will help.

1 Like