UE4之整合第三方库

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"
            }
			);

至此,可以实现动态库的自动拷贝,以及实现加载不同路径的动态库,以及打包之后可以正常运行。

 

***********************************20200822******************************

今天写了一个UE4集成Mqtt的库,反复阅读了上面写的文章,又有了新的体会。

这里写点心得:

1、我们写的插件可以直接拷贝到其他工程,都是可以直接运行的。只需要重新生成一下vs工程即可。

2、我们这里插件采用懒加载,避免启动蓝图的时候直接失败。(遇到这个问题查了好久)

懒加载如下:在插件的xxxx.Build.cs文件里面

PublicDelayLoadDLLs.Add("PlugMqttLibrary.dll");

 3、我们加载动态库直接在启动module的时候加载,这里我贴一下我的示例代码。这里要特别注意,我们需要编辑器调用的dll与运行的时候调用的dll不一样,吃过这个亏。通过宏开关来判断  WITH_EDITOR

 

// Copyright Epic Games, Inc. All Rights Reserved.

#include "PlugMqtt.h"
#include "Core.h"
#include "Modules/ModuleManager.h"
#include "Interfaces/IPluginManager.h"

#define LOCTEXT_NAMESPACE "FPlugMqttModule"

void FPlugMqttModule::StartupModule()
{
	pahomqtt3aLibraryHandle = nullptr;
	pahomqttpp3LibraryHandle = nullptr;
	PlugMqttLibraryHandle = nullptr;
	// 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("PlugMqtt")->GetBaseDir();

	FString pahomqtt3aLibraryPath;
	FString pahomqttpp3LibraryPath;
	FString PlugMqttLibraryPath;

#if WITH_EDITOR   // WITH_EDITOR
	// Add on the relative location of the third party dll and load it
	pahomqtt3aLibraryPath = FPaths::Combine(*BaseDir, TEXT("Source/ThirdParty/PlugMqttLibrary/lib/paho-mqtt3a.dll"));
	pahomqttpp3LibraryPath = FPaths::Combine(*BaseDir, TEXT("Source/ThirdParty/PlugMqttLibrary/lib/paho-mqttpp3.dll"));
	PlugMqttLibraryPath = FPaths::Combine(*BaseDir, TEXT("Source/ThirdParty/PlugMqttLibrary/x64/Debug/PlugMqttLibrary.dll"));
#else
	pahomqtt3aLibraryPath = FPaths::Combine(*BaseDir, TEXT("../../Binaries/ThirdParty/Win64/paho-mqtt3a.dll"));
	pahomqttpp3LibraryPath = FPaths::Combine(*BaseDir, TEXT("../../Binaries/ThirdParty/Win64/paho-mqttpp3.dll"));
	PlugMqttLibraryPath = FPaths::Combine(*BaseDir, TEXT("../../Binaries/ThirdParty/Win64/PlugMqttLibrary.dll"));
#endif 




	pahomqtt3aLibraryHandle = !pahomqtt3aLibraryPath.IsEmpty() ? FPlatformProcess::GetDllHandle(*pahomqtt3aLibraryPath) : nullptr;
	pahomqttpp3LibraryHandle = !pahomqttpp3LibraryPath.IsEmpty() ? FPlatformProcess::GetDllHandle(*pahomqttpp3LibraryPath) : nullptr;
	PlugMqttLibraryHandle = !PlugMqttLibraryPath.IsEmpty() ? FPlatformProcess::GetDllHandle(*PlugMqttLibraryPath) : nullptr;

	if (nullptr == pahomqtt3aLibraryHandle)
	{
		UE_LOG(LogTemp, Error, TEXT("GetDllHandle = %s"), *pahomqtt3aLibraryPath);
	}
	if (nullptr == pahomqttpp3LibraryHandle)
	{
		UE_LOG(LogTemp, Error, TEXT("GetDllHandle = %s"), *pahomqttpp3LibraryPath);
	}
	if (nullptr == PlugMqttLibraryHandle)
	{
		UE_LOG(LogTemp, Error, TEXT("GetDllHandle = %s"), *PlugMqttLibraryPath);
	}
}

void FPlugMqttModule::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
	if (nullptr == pahomqtt3aLibraryHandle)
	{
		FPlatformProcess::FreeDllHandle(pahomqtt3aLibraryHandle);
		pahomqtt3aLibraryHandle = nullptr;
	}
	if (nullptr == pahomqttpp3LibraryHandle)
	{
		FPlatformProcess::FreeDllHandle(pahomqttpp3LibraryHandle);
		pahomqtt3aLibraryHandle = nullptr;
	}
	if (nullptr == PlugMqttLibraryHandle)
	{
		FPlatformProcess::FreeDllHandle(PlugMqttLibraryHandle);
		PlugMqttLibraryHandle = nullptr;
	}
}

#undef LOCTEXT_NAMESPACE
	
IMPLEMENT_MODULE(FPlugMqttModule, PlugMqtt)

4、需要把动态库拷贝到运行加载的目录中去,这样才能加载。这里直接把整个build.cs文件贴上来。

// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.IO;
using UnrealBuildTool;

public class PlugMqtt : ModuleRules
{
	private void CopyDllAndLibToProjectBinaries(string Filepath, ReadOnlyTargetRules Target)
	{
		string BinariesDirectory = Path.Combine(ModuleDirectory, "../../../../", "Binaries/ThirdParty", Target.Platform.ToString());

		Console.WriteLine(BinariesDirectory);

		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));
	}

	public PlugMqtt(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",
				"CoreUObject",
				"Engine",
				"Projects"
				// ... add other public dependencies that you statically link with here ...
			}
			);
			
		
		PrivateDependencyModuleNames.AddRange(
			new string[]
			{
				// ... add private dependencies that you statically link with here ...	
			}
			);
		
		
		DynamicallyLoadedModuleNames.AddRange(
			new string[]
			{
				// ... add any modules that your module loads dynamically here ...
			}
			);

		string LibPath = Path.Combine(ModuleDirectory, "../ThirdParty/PlugMqttLibrary/x64/Debug/");
		PublicAdditionalLibraries.Add(Path.Combine(LibPath, "PlugMqttLibrary.lib"));

		PublicDelayLoadDLLs.Add("PlugMqttLibrary.dll");

		CopyDllAndLibToProjectBinaries(Path.Combine(ModuleDirectory, "../ThirdParty/PlugMqttLibrary/", "x64", "Debug", "PlugMqttLibrary.dll"), Target);
		CopyDllAndLibToProjectBinaries(Path.Combine(ModuleDirectory, "../ThirdParty/PlugMqttLibrary/lib", "paho-mqtt3a.dll"), Target);
		CopyDllAndLibToProjectBinaries(Path.Combine(ModuleDirectory, "../ThirdParty/PlugMqttLibrary/lib", "paho-mqttpp3.dll"), Target);
	}
}

5、如果要在插件里面编写UE4的对象,需要添加模块依赖,主要是CoreUObject和Engine 如下:

		PublicDependencyModuleNames.AddRange(
			new string[]
			{
				"Core",
				"CoreUObject",
				"Engine",
				"Projects"
				// ... add other public dependencies that you statically link with here ...
			}
			);

 

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值