UE4的第三方库,大家都建议使用插件进行整合,那么我也尝试一下插件整合。
首先先写一个C++的动态库:
在AddSdk.h的头文件里面,定义一个函数:
__declspec(dllimport) int Test_Add(int a, int b);
实现函数如下:
__declspec(dllexport) int Test_Add(int a, int b)
{
return a + b;
}
编译之后,会生成一个动态库
然后在UE4里面添加一个空的插件SimplePlug
在Source文件夹下面新建一个文件夹ThirdParty
在include里面放置头文件的定义,在lib里面放置lib文件和dll文件
然后需要在SimplePlug.Build.cs里面设置一下
整个代码如下:
// Copyright Epic Games, Inc. All Rights Reserved.
using System.IO;
using UnrealBuildTool;
public class SimplePlug : ModuleRules
{
string ThirdPartyPath
{
get
{
return Path.GetFullPath(Path.Combine(ModuleDirectory, "../ThirdParty/"));
}
}
public SimplePlug(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
// ... add other private include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
// ... add other public dependencies that you statically link with here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
// ... add private dependencies that you statically link with here ...
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
string includePath = Path.Combine(ThirdPartyPath, "include");
PublicIncludePaths.Add(includePath);
string LibPath = Path.Combine(ThirdPartyPath, "lib");
PublicAdditionalLibraries.Add(Path.Combine(LibPath,"X64", "AddDll.lib"));
PublicDelayLoadDLLs.Add(Path.Combine(LibPath, "X64", "AddDll.dll"));
}
}
上述代码主要实现头文件的引入,lib文件的依赖 以及动态库的依赖。
最后新建一个AMyActor
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyActor.h"
#include "AddSdk.h"
// Sets default values
AMyActor::AMyActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
Super::BeginPlay();
UE_LOG(LogTemp, Log, TEXT("Your message =%i"),Test_Add(1,2));
}
// Called every frame
void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
最后发现效果不错:符合预期要求
*********************************************2020-07-25*************************************
上面的操作存在两个问题:
1、打包之后,无法调用动态库
2、需要手动拷贝动态库到程序当前路径下。
为了解决这两个问题,有研究了一下
增加拷贝函数,实现把动态库拷贝到需要运行的路径下:
输入参数FIlepath 为动态库的全路径
private void CopyDllAndLibToProjectBinaries(string Filepath, ReadOnlyTargetRules Target)
{
string BinariesDirectory = Path.Combine(ModuleDirectory,"../../", "Binaries/ThirdParty", Target.Platform.ToString());
string Filename = Path.GetFileName(Filepath);
if (!Directory.Exists(BinariesDirectory))
{
Directory.CreateDirectory(BinariesDirectory);
}
File.Copy(Filepath, Path.Combine(BinariesDirectory, Filename), true);
RuntimeDependencies.Add(Path.Combine(BinariesDirectory, Filename));
}
另外,把动态库的引入修改为延迟加载,把加载放置到启动module里面,这样可以选择合适的路径来加载动态库
string includePath = Path.Combine(ThirdPartyPath, "include");
PublicIncludePaths.Add(includePath);
string LibPath = Path.Combine(ThirdPartyPath, "lib");
PublicAdditionalLibraries.Add(Path.Combine(LibPath,"X64", "AddDll.lib"));
PublicDelayLoadDLLs.Add("AddDll.dll");
CopyDllAndLibToProjectBinaries(Path.Combine(ThirdPartyPath, "lib", "X64", "AddDll.dll"), Target);
加载和释放动态库
// Copyright Epic Games, Inc. All Rights Reserved.
#include "SimplePlug.h"
#include "Core.h"
#include "Modules/ModuleManager.h"
#include "Interfaces/IPluginManager.h"
#define LOCTEXT_NAMESPACE "FSimplePlugModule"
void FSimplePlugModule::StartupModule()
{
// 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("SimplePlug")->GetBaseDir();
// Add on the relative location of the third party dll and load it
FString LibraryPath;
LibraryPath = FPaths::Combine(*BaseDir, TEXT("Binaries/ThirdParty/Win64/AddDll.dll"));
ExampleLibraryHandle = !LibraryPath.IsEmpty() ? FPlatformProcess::GetDllHandle(*LibraryPath) : nullptr;
if (ExampleLibraryHandle)
{
// Call the test function in the third party library that opens a message box
}
else
{
FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("ThirdPartyLibraryError", "Failed to load example third party library"));
}
}
void FSimplePlugModule::ShutdownModule()
{
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
// we call this function before unloading the module.
// Free the dll handle
FPlatformProcess::FreeDllHandle(ExampleLibraryHandle);
ExampleLibraryHandle = nullptr;
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FSimplePlugModule, SimplePlug)
最后发现一直无法编译通过,需要在下面增加“Projects”
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
// ... add other public dependencies that you statically link with here ...
"Projects"
}
);
至此,可以实现动态库的自动拷贝,以及实现加载不同路径的动态库,以及打包之后可以正常运行。