DLL延迟加载

*.build.cs文件里设定PublicDelayLoadDLLs字段,参考fragment:519
module.cpp里加载dlls,参考fragment:520

递归搜索指定后缀集合的文件

// Fill out your copyright notice in the Description page of Project Settings.

using System.IO;
using System.Collections.Generic;
using UnrealBuildTool;

namespace UnrealBuildTool.Rules
{
#if WITH_FORWARDED_MODULE_RULES_CTOR
    using zTargetInfo = ReadOnlyTargetRules;
#else
    using zTargetInfo = TargetInfo;
#endif

    public class HeXrTransceiverLib : ModuleRules
    {
        public HeXrTransceiverLib(zTargetInfo Target)
#if WITH_FORWARDED_MODULE_RULES_CTOR
            : base(Target)
#endif
        {
            Type = ModuleType.External;

            PublicIncludePaths.AddRange(
                    new string[] {
                        // ... add public include paths required here ...
                        Path.Combine(ModuleDirectory, "include"),
                    }
                );

            // Add the import library
            //PublicLibraryPaths.Add(Path.Combine(ModuleDirectory, "lib"));
            PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "lib", "HeXrTransceiver.lib"));

            // Delay-load the DLL, so we can load it from the right place first
            PublicDelayLoadDLLs.Add("HeXrTransceiver.dll");

            string dll_folder_path = Path.Combine(ModuleDirectory, "dll");
            string[] dlls_fullpath = DFS_GetFilesRecursively(dll_folder_path, new string[] { ".dll" });
            // Runtime Dependencies for Project Package
            foreach(string dll in dlls_fullpath)
	    {
                 string target_output_path = "$(TargetOutputDir)/dll".Replace("dll", Path.GetFileName(dll));
                 RuntimeDependencies.Add(target_output_path, dll); // copy
                 // PublicDelayLoadDLLs.Add(Path.GetFileName(dll));
		 RuntimeDependencies.Add(target_output_path, StagedFileType.NonUFS);
	    }
        }

        /*
         * Root: folder full path
         * FileExts: dll,jpg,txt,etc.
         * Return full path of files.
         */
        public string[] DFS_GetFilesRecursively(string Root, string[] FileExts)
        {
            List<string> Ret = new List<string>();

            DirectoryInfo DI = new DirectoryInfo(Root);
            foreach(FileInfo FI in DI.GetFiles())
            {
                //System.Console.WriteLine("FI.Extension={0} FI.FullName={1}", FI.Extension, FI.FullName);
                foreach(string Ext in FileExts)
                {
                    if (Ext.Equals(FI.Extension, System.StringComparison.OrdinalIgnoreCase))
                    {
                        Ret.Add(FI.FullName);
                        break;
                    }
                }
            }

            foreach(DirectoryInfo SubDI in DI.GetDirectories())
            {
                Ret.AddRange(DFS_GetFilesRecursively(SubDI.FullName, FileExts));
            }

            return Ret.ToArray();
        }

        /**
         * Root: folder full path
         * FileExts: dll,jpg,txt,etc.
         * Return full path of files.
         */
        public string[] GetFiles_NoRecur(string Root, string[] FileExts)
        {
            List<string> Ret = new List<string>();
    
            DirectoryInfo DI = new DirectoryInfo(Root);
            foreach (FileInfo FI in DI.GetFiles())
            {
                //System.Console.WriteLine("FI.Extension={0} FI.FullName={1}", FI.Extension, FI.FullName);
                foreach (string Ext in FileExts)
                {
                    if (Ext.Equals(FI.Extension, System.StringComparison.OrdinalIgnoreCase))
                    {
                        Ret.Add(FI.FullName);
                        break;
                    }
                }
            }
    
            return Ret.ToArray();
        }
    }
}

UE4插件简单正确加载插件的方法

#include "Interfaces/IPluginManager.h"
#include "HAL/FileManager.h"

bool FSURAPluginRuntimeModule::LoadHeXrDependency( )
{
	// 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( "SURAPlugin" )->GetBaseDir( );

	// Add on the relative location of the third party dll and load it
	FString DllFolder(FPaths::Combine(*BaseDir, TEXT("Source/ThirdParty/HeXrTransceiverLib/dll")));
	FPlatformProcess::AddDllDirectory(*DllFolder);

	IFileManager::Get().IterateDirectoryRecursively(*DllFolder, [&](const TCHAR* Node, bool IsDir) -> bool
	{
		if (IsDir)
		{
			FPlatformProcess::AddDllDirectory(Node);
		}
		return true;
	});

	FString LibraryPath;
#if PLATFORM_WINDOWS
	LibraryPath = FPaths::Combine(*DllFolder, TEXT("HeXrTransceiver.dll"));
#endif // PLATFORM_WINDOWS

	HeXr_LIB_HANDLE = !LibraryPath.IsEmpty( ) ? FPlatformProcess::GetDllHandle( *LibraryPath ) : nullptr;

	return HeXr_LIB_HANDLE != nullptr;
}

RuntimeDependencies

If you want to have the DLL copied to the same output directory as the executable at build time, you do so via an overload of the RuntimeDependencies.Add method:
 RuntimeDependencies.Add("$(TargetOutputDir)/Foo.dll", Path.Combine(PluginDirectory, "Source/ThirdParty/bin/Foo.dll"));
Other variables can be used for output paths of the DLL:
$(EngineDir) The engine directory
$(ProjectDir) Directory containing the project file
$(ModuleDir) Directory containing the .build.cs file
$(PluginDir) Directory containing the .uplugin file
$(BinaryOutputDir) Directory containing the binary that this module is compiled into (for example, the path to the DLL for editor builds, and path to the executable (EXE) for packaged builds)
$(TargetOutputDir) Directory containing the executable (including in editor builds)

module.build.cs文件常见字段

string path = Path.Combile(p1, p2, p3);
string absolutePath = Path.GetFullPath(path);
//https://docs.unrealengine.com/4.27/en-US/ProductionPipelines/BuildTools/UnrealBuildTool/ThirdPartyLibraries/
RuntimeDependencies.Add(Path.GetFullPath(absolutePath), StagedFileType.NonUFS);
if (Target.bBuildEditor)判断当前build配置是否包含Editor

Public/PrivateDependencyModuleNames属于静态链接,通常其Public/PrivateIncludePaths会自行处理正确。
Public/PrivateIncludePathModuleNames与DynamicallyLoadedModuleNames配合属于动态链接,因此如果你使用了非抽象函数,则会提示无法找到链接符号。这一对配置的典型用法参考 #474,一般就是LoadModuleChecked,然后调用接口的抽象函数。

关于PublicSystemLibraryPaths,PublicAdditionalLibraries,PublicLibraryPaths的问题

在(<)UE4.24的版本中,指定lib文件的方法跟visual studio里使用的方式一致:
  1. PublicLibraryPaths用于添加lib文件所在的目录
  2. PublicAdditionalLibraries用于添加lib文件,不包括路径信息
引擎寻找lib文件的方式就是将上面这两个选项指定的字符串进行组合拼接,验证拼接后字符串是否为有效路径。这个过程是很慢的,因此从UE4.24版本开始,引擎推荐如下使用方式解决这个问题:
  • 忽略PublicLibraryPaths字段,且PublicAdditionalLibraries字段直接添加lib文件绝对路径。
如果仍坚持老的使用方式,则会报Warning,用以下方式可以消除这个Warning,但并不能消除老的方式的效率低下的问题:
  1. PublicSystemLibraryPaths取代PublicLibraryPaths,用于添加lib文件所在的目录
  2. PublicSystemLibraries来添加库文件,但只需要库的名字即可,不需要后缀。或者直接在cpp中用#pragma comment(lib,"avcodec.lib")这种写法,会从PublicSystemLibraryPaths指定的目录下去寻找

定义宏macro

PublicDefinitions.Add("MacroName=MacroBody");