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 do- RuntimeDependencies.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\Win64which 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)