DLL not found/linked(?) when packaging for Windows
I have this fatal error below I’m running into that does not appear during PIE sessions. My editor is also compiled from source and rebuilds/grabs the dll’s the sdk plugin we’re developing uses. This error appears on .exe launch. My build target is Windows (Win64)
.
Note: I have replaced project details with curly brackets, vague names, and matched the casing of the original.
The Fatal Error:
Fatal error!
Unhandled Exception: 0xc06d007e
0x00007fff4a81fe4c KERNELBASE.dll!UnknownFunction []
0x00007ff69bdd1614 {Project_Name}-Win64-DebugGame.exe!__delayLoadHelper2() [D:\a\_work\1\s\src\vctools\delayimp\delayhlp.cpp:312]
0x00007ff69bbab828 {Project_Name}-Win64-DebugGame.exe!_tailMerge_{core_dll}_dll() []
0x00007ff698975289 {Project_Name}-Win64-DebugGame.exe!UPanelContainer::UPanelContainer() [C:\path\to\project\{Project_Name}\Plugins\{Company}\{SDK_Plugin}\Source\Platform\UI\Private\Framework\PanelContainer.cpp:457]
0x00007ff698951bf7 {Project_Name}-Win64-DebugGame.exe!InternalConstructor<UPanelContainer>() [C:\Program Files\Epic Games\UE_5.4\Engine\Source\Runtime\CoreUObject\Public\UObject\Class.h:3559]
0x00007ff68fa039fe {Project_Name}-Win64-DebugGame.exe!UClass::CreateDefaultObject() []
0x00007ff68fdafefb {Project_Name}-Win64-DebugGame.exe!UObjectInitialized() []
0x00007ff68fd93312 {Project_Name}-Win64-DebugGame.exe!ProcessNewlyLoadedUObjects() []
0x00007ff696a3a607 {Project_Name}-Win64-DebugGame.exe!FEngineLoop::PreInitPostStartupScreen() []
0x00007ff696a343d8 {Project_Name}-Win64-DebugGame.exe!GuardedMain() []
0x00007ff696a3465a {Project_Name}-Win64-DebugGame.exe!GuardedMainWrapper() []
0x00007ff696a37225 {Project_Name}-Win64-DebugGame.exe!LaunchWindowsStartup() []
0x00007ff696a47504 {Project_Name}-Win64-DebugGame.exe!WinMain() []
0x00007ff69bdd53ba {Project_Name}-Win64-DebugGame.exe!__scrt_common_main_seh() [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288]
0x00007fff4c00257d KERNEL32.DLL!UnknownFunction []
My understanding of this is that on the first runtime usage of the {projectcore}.dll
done by UPanelContainer’s constructor crashes out because the link is broken.
The Problem
I thought I’d setup the project appropriately to package for windows. But somehow my dll’s linking is broken.
From what I’ve understood reading around the forums and documenation, the pattern for including a ThirdParty (non-windows standard dll location like where vcpkg installs to) dll’s is to do the following:
Within the ThirdParty plugin module {ProjectCoreLibrary}:
PublicDelayLoadDLLs.Add("{projectcore}.dll");
RuntimeDependencies.Add(Path.Combine(binFolder, "{projectcore}.dll"));
3. Cannot doRuntimeDependencies.Add(string inPath, string inSourcePath);
because editor and project is compiled (from source?) via VS. Packaging for Windows is only possible through editor and this setup causes the compiled dll’s to be locked by the editor.
Then within the plugin module:
- `PublicDependencyModuleNames.AddRange( new string {“{ProjectCoreLibrary}”});
- Inside
{ProjectCoreLibrary}.cpp
FPlatformProcess::GetDllHandle(*LibraryPath)
- LibraryPath pointing to the
Plugin\...\{ProjectCore}\Binaries\Win64
which contains the dll’s.
Relevant code
The plugins within my project uses a thirdparty {projectcore}.dll
, this core project sits outside the unreal project and we use the UE4CMake
(with minor tune ups for UE5 paths) to build this external dll and drop it into the unreal project.
ThirdParty/{ProjectCoreLibrary}/{ProjectCoreLibrary}.Build.cs
Contains:
public class ProjectCoreLibrary : ModuleRules
{
public ProjectCoreLibrary(ReadOnlyTargetRules Target) : base(Target)
{
Type = ModuleType.External;
var projectCoreSdkDevFolder = System.Environment.GetEnvironmentVariable("PROJECT_CORE_SDK");
if (Target.Platform == UnrealTargetPlatform.Win64 && !string.IsNullOrEmpty(projectCoreSdkDevFolder))
{
// Uses the UE4CMAKE project plugin to build the core dll and adds it as a dependency into this ThirdParty project
CMakeTarget.add(Target, this, "projectcore", projectCoreSdkDevFolder, " --trace");
}
bool isDebug = Target.Configuration == UnrealTargetConfiguration.Debug ||
Target.Configuration == UnrealTargetConfiguration.DebugGame;
var targetBuildConfig = isDebug ? "Debug" : "Release";
if (Target.Platform == UnrealTargetPlatform.Win64)
{
var binFolder = !string.IsNullOrEmpty(projectCoreSdkDevFolder) ?
Path.Combine(PluginDirectory, "..", "..", "..", "Intermediate", "CMakeTarget", "{projectcore}", "build", "{projectcore}", "{Project.Core}", targetBuildConfig) :
Path.Combine(PluginDirectory, "Redist", "Win64");
// binFolder = C:/Path\To\Project\Build\Windows\{project_name}\Intermediate\CMakeTarget\{projectcore}\build\{projectcore}\{Project.Core}}\Debug
// Delay-load the DLL, so we can load it from the right place first
PublicDelayLoadDLLs.Add("{projectcore}.dll");
RuntimeDependencies.Add(Path.Combine(binFolder, "{projectcore}.dll"));
// Initially used this and works when compiling the editor and then PIE.
// However I cannot package to windows from the compiled editor because the editor has locked the files used that this method wants to overwrite.
// PluginDirectory = C:/Path/To/{project_name}/Plugins/{Company}/{ProjectCore}/
// RuntimeDependencies.Add(
// Path.Combine(PluginDirectory, "Binaries", "Win64", "{projectcore}.dll"),
// Path.Combine(binFolder, "{projectcore}.dll"));
}
else
{
throw new System.NotSupportedException();
}
}
}
{ProjectCore}/{ProjectCore}.Build.cs
Contains:
public class ProjectCore : ModuleRules
{
public ProjectCore(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"{ProjectCore}Library",
"Projects"
// ... add other public dependencies that you statically link with here ...
}
);
}
}
{ProjectCore}/public/{ProjectCore}.cpp
Contains:
#define LOCTEXT_NAMESPACE "FProjectCoreModule"
void FProjectCoreModule::StartupModule()
{
std::shared_ptr<int> p;
UE_LOG(LogTemp, Display, TEXT("{ProjectCore} module started"));
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
// Get the base directory of this plugin
FString BaseDir = IPluginManager::Get().FindPlugin("{ProjectCore}")->GetBaseDir();
FString FullPath = FPaths::ConvertRelativePathToFull(BaseDir);
UE_LOG(LogTemp, Display, TEXT("{ProjectCore} using base dir=%s"), *BaseDir);
UE_LOG(LogTemp, Display, TEXT("{ProjectCore} using full path=%s"), *FullPath);
// Add on the relative location of the third party dll and load it
FString LibraryPath;
#if PLATFORM_WINDOWS
LibraryPath = FPaths::Combine(*BaseDir, TEXT("Binaries/Win64/{projectcore}.dll"));
// Tried using same path that the ThirdParty {ProjectCoreLibrary} has for it's binFolder
//LibraryPath = FPaths::Combine(
// FPaths::ProjectIntermediateDir(),
// TEXT("CMakeTarget/{projectcore}/build/{projectcore}/{Project.Core}/"),
// TEXT("Debug"),
// TEXT("{projectcore}.dll"));
#endif // PLATFORM_WINDOWS
ProjectCoreLibraryHandle = !LibraryPath.IsEmpty() ? FPlatformProcess::GetDllHandle(*LibraryPath) : nullptr;
if (ProjectCoreLibraryHandle)
{
UE_LOG(LogTemp, Display, TEXT("{ProjectCore} loaded library %s"), *LibraryPath);
SetupMemory();
}
else
{
UE_LOG(LogTemp, Error, TEXT("{ProjectCore} failed to load library %s"), *LibraryPath);
}
}
void FProjectCoreModule::ShutdownModule()
{
// Free the dll handle
if (ProjectCoreLibraryHandle)
{
FPlatformProcess::FreeDllHandle(ProjectCoreLibraryHandle);
ProjectCoreLibraryHandle = nullptr;
}
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FProjectCoreModule, ProjectCore)