Two third-party DLL directories

I have two plugins using third-party libraries in my project. Both work fine separately, if the other plugin is disabled. If they both are enabled, the second loaded plugin would crashe with error which means it can’t find its dlls:

__delayLoadHelper2() [D:\a_work\1\s\src\vctools\delayimp\delayhlp.cpp:312]

In the StartupModule() I add dlls path for delay loaded dll like this:

FPlatformProcess::PushDllDirectory(*PluginBinariesDir); // “MyPlugin1/Source/ThirdParty/Binaries/Win64”

and for another pluign

FPlatformProcess::PushDllDirectory(*PluginBinariesDir); // “MyPlugin2/Source/ThirdParty/Binaries/Win64”

But it looks like PushDllDirectory(…) overrides previous path rather than push new path to the array. For example, if I load MyPlugin1 first and then load MyPlugin2, and if I copy all dlls to the MyPlugin2’s binaries directory, the project would load without crash.

So, do I do something wrong and need to use another function, or is it a bug which should be reported?

In case you’re still interested in this, it might help if you put the full *.build.cs file here…

Hmm, let’s try.

using UnrealBuildTool;
using System.IO;

public class LlamaCppLight : ModuleRules
{
    private string ThirdPartyLlamaCpp
    {
        get { return Path.GetFullPath(Path.Combine(ModuleDirectory, "../ThirdParty/llama.cpp")); }
    }

    public LlamaCppLight(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
		
		PublicIncludePaths.AddRange(
			new string[] {
			}
		);
				
		
		PrivateIncludePaths.AddRange(
			new string[] {
                Path.Combine(ThirdPartyLlamaCpp, "Include")
            }
		);
			
		
		PublicDependencyModuleNames.AddRange(
			new string[]
			{
				"Core",
                "Projects"
            }
		);
			
		
		PrivateDependencyModuleNames.AddRange(
			new string[]
			{
				"CoreUObject",
				"Engine",
				"Slate",
				"SlateCore",
				"GameplayTags"
			}
		);
		
		
		DynamicallyLoadedModuleNames.AddRange(
			new string[]
			{
			}
		);

        if (Target.Platform == UnrealTargetPlatform.Win64)
        {
            string BinariesPath = Path.Combine(ThirdPartyLlamaCpp, "Binaries/Win64");
            // Add static libraries
            PublicAdditionalLibraries.AddRange
            (
                new string[]
                {
                    Path.Combine(BinariesPath, "llama.lib"),
                    Path.Combine(BinariesPath, "ggml.lib")
                }
            );

			// Add dynamic libraries
            PublicDelayLoadDLLs.AddRange(
                new string[]
				{
                    "llama.dll",
                    "ggml.dll"
                }
			);

            if (!Target.bBuildEditor)
            {
                string[] DLLs = { "ggml.dll", "ggml-base.dll", "llama.dll" };

                // Target directory in a packaged project
                string DllDestinationDir = "$(ProjectDir)/Binaries/ThirdParty/llama.cpp";

				// Copy DLLs to the target project's executable directory
				foreach (string FileName in DLLs)
				{
                    RuntimeDependencies.Add(Path.Combine(DllDestinationDir, FileName), Path.Combine(BinariesPath, FileName));
                }
            }
        }
    }
}

and (consider bUseEspeak = true)

using UnrealBuildTool;
using System.IO;

public class LocalTTS : ModuleRules
{
	private string ThirdPartyEspeak
    {
        get { return Path.GetFullPath(Path.Combine(ModuleDirectory, "../ThirdParty/espeak")); }
    }
    private string ThirdPartyUni
    {
        get { return Path.GetFullPath(Path.Combine(ModuleDirectory, "../ThirdParty/uni_algo")); }
    }

	public LocalTTS(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;

		// **************************************************
		// ESPEAK-NG LIBRARY
		bool bUseEspeak = false;
        // ESPEAK-NG LIBRARY
        // **************************************************

        PublicIncludePaths.AddRange(
			new string[] {
				// ... add public include paths required here ...
				Path.Combine(ThirdPartyUni, "Include"),
                Path.Combine(ModuleDirectory, "../ThirdParty/miniz")
            }
		);
        if (Target.Platform == UnrealTargetPlatform.Android)
		{
			PublicIncludePaths.Add(Path.Combine(ThirdPartyEspeak, "Include"));
        }

		PublicDependencyModuleNames.AddRange(
			new string[]
			{
				"Core",
				"Projects",
				"NNE"
			}
			);

		PrivateDependencyModuleNames.AddRange(
			new string[]
			{
				"CoreUObject",
				"Engine",
				"Slate",
				"SlateCore",
				"Json",
				"AudioPlatformConfiguration",
                "AudioExtensions"
			}
			);


		DynamicallyLoadedModuleNames.AddRange(
			new string[]
			{
			}
			);

		if (bUseEspeak)
		{
			if (Target.Platform == UnrealTargetPlatform.Win64)
			{
				PublicDefinitions.Add("ESPEAK_NG=1");
			}
			else
			{
                PublicDefinitions.Add("ESPEAK_NG=0");
            }
            if (Target.Platform == UnrealTargetPlatform.Win64)
			{
				// copy all DLLs to the packaged build
                if (!Target.bBuildEditor && Target.Type == TargetType.Game)
				{
					string BinariesPath = Path.Combine(ThirdPartyEspeak, "Binaries", "Win64");
					string DllDestinationDir = "$(ProjectDir)/Binaries/ThirdParty/espeak";

					string[] DLLs = { "libespeak-ng.dll" };

					// Copy DLLs to the target project's executable directory
					foreach (string FileName in DLLs)
					{
						RuntimeDependencies.Add(Path.Combine(DllDestinationDir, FileName), Path.Combine(BinariesPath, FileName));
					}
				}

				if (!Target.bBuildEditor)
				{
					string PluginContentPath = Path.GetFullPath(Path.Combine(ModuleDirectory, "../../Content/NonUFS/espeak-ng-data/*"));
					string ProjectContentPath = "$(ProjectDir)/Content/NonUFS/espeak-ng-data/*";
					RuntimeDependencies.Add(ProjectContentPath, PluginContentPath, StagedFileType.NonUFS);
				}
			}
		}
		else
		{
            PublicDefinitions.Add("ESPEAK_NG=0");
        }
    }
}