版权声明:本文为CSDN博主「蓬 蒿 人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
在 UE4 中使用 静态链接库 和 动态链接库 与普通程序中使用差不多,只是要在编译文件中设置库文件路径。下面创建一个 第三人称 模板的 C++ 工程 ThirdPerson ,使用 这篇文章中的静态链接库和动态链接库。
一、静态链接库
在工程目录新建一个文件夹 ThirdParty(与 Content 目录同一级),将 CaptureScreen.h 和CaptureScreen.lib 复制到这个文件夹,接下来要在编译文件 .build.cs 文件中将这个目录添加到包含目录,使 UE4 能搜索到头文件(如果没有添加包含目录,头文件的路径就是 ../../ThirdParty/CaptureScreen.h ),然后将 CaptureScreen.lib 添加到依赖库列表中。
ThirdPerson.build.cs 文件大致如下
ThirdPerson.Build.cs
using UnrealBuildTool;
using System.IO;
public class ThirdPerson : ModuleRules
{
public
    ThirdPerson(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
        PublicDependencyModuleNames.AddRange(new string[]{"Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "ImageWrapper"});
        PublicIncludePaths.Add(Path.Combine(ThirdPartyPath));                             ///< *
        PublicAdditionalLibraries.Add(Path.Combine(ThirdPartyPath, "CaptureScreen.lib")); ///< *
    }
private
    string ThirdPartyPath ///< *
    {
        get { return Path.GetFullPath(Path.Combine(ModuleDirectory, "../../ThirdParty/")); }
    }
}
在 AThirdPersonGameMode 类中实现一个蓝图可调用的函数 TestCapture 来调用静态库中的函数,在游戏运行期间都可以调用来获取屏幕截图,为了方便这里就在 StartPlay 中调用 TestCapture。.h 和 .cpp 代码如下
ThirdPersonGameMode.h
#pragma once
 
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "ThirdPersonGameMode.generated.h"
 
UCLASS(minimalapi)
class AThirdPersonGameMode : public AGameModeBase
{
	GENERATED_BODY()
 
public:
	AThirdPersonGameMode();
	void StartPlay();
	void EndPlay(const EEndPlayReason::Type EndPlayReason);
	UFUNCTION(BlueprintCallable)
	void TestCapture(int x,int y,int width,int height); ///< *
};
ThirdPersonGameMode.cpp
#include "ThirdPersonGameMode.h"
#include "ThirdPersonCharacter.h"
#include "UObject/ConstructorHelpers.h"
#include"IImageWrapperModule.h"
#include"ModuleManager.h"
#include"IImageWrapper.h"
#include"Misc/FileHelper.h"
#include"CaptureScreen.h" ///< 包含静态库的头文件
AThirdPersonGameMode::AThirdPersonGameMode()
{
	// set default pawn class to our Blueprinted character
	static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/ThirdPersonCPP/Blueprints/ThirdPersonCharacter"));
	if (PlayerPawnBPClass.Class != NULL)
	{
		DefaultPawnClass = PlayerPawnBPClass.Class;
	}
	
}
 
bool OnCaptureData(const char* InData, int InWidth, int InHeight, int InBytesPerColor)
{
	/*FString ScreenShotName = FPaths::ProjectSavedDir() + TEXT("ScreenShot.png");
	TArray<FColor> colors;
	colors.AddZeroed(InHeight*InWidth);
	for (int i = 0; i < InHeight; i++)
	{
		for (int j = 0; j < InWidth; j++)
		{
			int DestIndex = i * InWidth + j;
			//(InHeight-i-1):BMP数据是从左下角到右上角的顺序,也就是上下颠倒的,所以要倒回来
			int SrcIndex =((InHeight -i-1) * InWidth + j) * InBytesPerColor;
			colors[DestIndex].B = InData[SrcIndex];
			colors[DestIndex].G = InData[SrcIndex+1];
			colors[DestIndex].R = InData[SrcIndex+2];
			colors[DestIndex].A = 255;
		}
	}
	IImageWrapperModule& ImageWrapperModule = FModuleManager::Get().LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
	TSharedPtr<IImageWrapper>  ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);
	if (ImageWrapper->SetRaw(colors.GetData(), colors.Num()*sizeof(FColor),InWidth, InHeight, ERGBFormat::BGRA, 8))
	{
		FFileHelper::SaveArrayToFile(ImageWrapper->GetCompressed(), *ScreenShotName);
	}*/
	return true;
}
 
void AThirdPersonGameMode::StartPlay()
{
	Super::StartPlay();
	TestCapture(0,0,0,0); ///< *
}
 
void AThirdPersonGameMode::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	Super::EndPlay(EndPlayReason);
}
 
/// @note 调用静态库中的函数
void AThirdPersonGameMode::TestCapture(int x, int y, int width, int height)
{
	RunCapture(x, y, width, height, OnCaptureData);
}
二、动态链接库
动态链接库同时需要将 CaptureScreen.dll 文件放到 ThirdParty 目录,使用动态链接库有两种方式,
- 一种是通过 .h 和 .lib 文件直接调用,
 - 一种是只用 dll 文件通过查找函数地址来使用。
 
1. 通过 .lib 文件使用动态链接库
ThirdPerson.build.cs 文件内容跟上面的差不多,只是要将 dll 文件添加到 延迟加载列表 PublicDelayLoadDLLs 中,如果不添加就会在引擎启动的时候加载,导致出错(不知道还有没有其他的解决办法)。
ThirdPerson.Build.cs
using UnrealBuildTool;
using System.IO;
public class ThirdPerson : ModuleRules
{
private
   string ThirdPartyPath
   {
       get { return Path.GetFullPath(Path.Combine(ModuleDirectory, "../../ThirdParty/")); }
   }
public
   ThirdPerson(ReadOnlyTargetRules Target) : base(Target)
   {
       PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
       PublicDependencyModuleNames.AddRange(new string[]{"Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "ImageWrapper"});
       PublicIncludePaths.Add(Path.Combine(ThirdPartyPath));                             ///< *
       PublicAdditionalLibraries.Add(Path.Combine(ThirdPartyPath, "CaptureScreen.lib")); ///< *
       PublicDelayLoadDLLs.Add("CaptureScreen.dll");                                     ///*
   }
}
在使用 dll 中的函数之前(本例在 StartPlay 中)要通过调用 PushDllDirectory 函数将 Thirdparty 目录加入到 dll 搜索路径,否则找不到 dll 文件(如果将 dll 文件放到工程目录 /Binaries/Win64 下可以不调用)。
ThirdPersonGameMode.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "ThirdPersonGameMode.generated.h"
UCLASS(minimalapi)
class AThirdPersonGameMode : public AGameModeBase
{
   GENERATED_BODY()
public:
   AThirdPersonGameMode();
   void StartPlay();
   void EndPlay(const EEndPlayReason::Type EndPlayReason);
   UFUNCTION(BlueprintCallable)
   void TestCapture(int x,int y,int width,int height);
};
ThirdPersonGameMode.cpp
#include "ThirdPersonGameMode.h"
#include "ThirdPersonCharacter.h"
#include "UObject/ConstructorHelpers.h"
#include"IImageWrapperModule.h"
#include"ModuleManager.h"
#include"IImageWrapper.h"
#include"Misc/FileHelper.h"
#include"CaptureScreen.h" ///< 包含头文件
AThirdPersonGameMode::AThirdPersonGameMode()
{
	// set default pawn class to our Blueprinted character
	static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/ThirdPersonCPP/Blueprints/ThirdPersonCharacter"));
	if (PlayerPawnBPClass.Class != NULL)
	{
		DefaultPawnClass = PlayerPawnBPClass.Class;
	}
	
}
 
bool OnCaptureData(const char* InData, int InWidth, int InHeight, int InBytesPerColor)
{
	/*FString ScreenShotName = FPaths::ProjectSavedDir() + TEXT("ScreenShot.png");
	TArray<FColor> colors;
	colors.AddZeroed(InHeight*InWidth);
	for (int i = 0; i < InHeight; i++)
	{
		for (int j = 0; j < InWidth; j++)
		{
			int DestIndex = i * InWidth + j;
			// (InHeight-i-1):BMP数据是从左下角到右上角的顺序,也就是上下颠倒的,所以要倒回来
			int SrcIndex =((InHeight -i-1) * InWidth + j) * InBytesPerColor;
			colors[DestIndex].B = InData[SrcIndex];
			colors[DestIndex].G = InData[SrcIndex+1];
			colors[DestIndex].R = InData[SrcIndex+2];
			colors[DestIndex].A = 255;
		}
	}
	IImageWrapperModule& ImageWrapperModule = FModuleManager::Get().LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
	TSharedPtr<IImageWrapper>  ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);
	if (ImageWrapper->SetRaw(colors.GetData(), colors.Num()*sizeof(FColor),InWidth, InHeight, ERGBFormat::BGRA, 8))
	{
		FFileHelper::SaveArrayToFile(ImageWrapper->GetCompressed(), *ScreenShotName);
	}*/
	return true;
}
 
void AThirdPersonGameMode::StartPlay()
{
	Super::StartPlay();
	FPlatformProcess::PushDllDirectory(*(FPaths::ProjectDir() / TEXT("ThirdParty/")));///< 将 dll 存放目录加入搜索路径
	TestCapture(0,0,0,0); ///< *
}
 
void AThirdPersonGameMode::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	Super::EndPlay(EndPlayReason);
}
/// @note 调用函数
void AThirdPersonGameMode::TestCapture(int x, int y, int width, int height)
{
	RunCapture(x, y, width, height, OnCaptureData);
}
2. 直接使用 dll 文件
直接使用 dll 文件来调用库中的函数只需要一个 dll 文件,.build.cs 文件也不用改,可直接用 GetDllExport 通过函数名称从 dll 中取得需要调用的函数。
ThirdPersonGameMode.h
#pragma once
 
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "ThirdPersonGameMode.generated.h"
 
UCLASS(minimalapi)
class AThirdPersonGameMode : public AGameModeBase
{
	GENERATED_BODY()
 
public:
	AThirdPersonGameMode();
	void StartPlay();
	void EndPlay(const EEndPlayReason::Type EndPlayReason);
	UFUNCTION(BlueprintCallable)
	void TestCapture(int x,int y,int width,int height);
 
	void* CaptureScreenHandle=nullptr;
};
ThirdPersonGameMode.cpp
#include "ThirdPersonGameMode.h"
#include "ThirdPersonCharacter.h"
#include "UObject/ConstructorHelpers.h"
#include"IImageWrapperModule.h"
#include"ModuleManager.h"
#include"IImageWrapper.h"
#include"Misc/FileHelper.h"
AThirdPersonGameMode::AThirdPersonGameMode()
{
	// set default pawn class to our Blueprinted character
	static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/ThirdPersonCPP/Blueprints/ThirdPersonCharacter"));
	if (PlayerPawnBPClass.Class != NULL)
	{
		DefaultPawnClass = PlayerPawnBPClass.Class;
	}
	
}
 
bool OnCaptureData(const char* InData, int InWidth, int InHeight, int InBytesPerColor)
{
	/*FString ScreenShotName = FPaths::ProjectSavedDir() + TEXT("ScreenShot.png");
	TArray<FColor> colors;
	colors.AddZeroed(InHeight*InWidth);
	for (int i = 0; i < InHeight; i++)
	{
		for (int j = 0; j < InWidth; j++)
		{
			int DestIndex = i * InWidth + j;
			//(InHeight-i-1):BMP数据是从左下角到右上角的顺序,也就是上下颠倒的,所以要倒回来
			int SrcIndex =((InHeight -i-1) * InWidth + j) * InBytesPerColor;
			colors[DestIndex].B = InData[SrcIndex];
			colors[DestIndex].G = InData[SrcIndex+1];
			colors[DestIndex].R = InData[SrcIndex+2];
			colors[DestIndex].A = 255;
		}
	}
	IImageWrapperModule& ImageWrapperModule = FModuleManager::Get().LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
	TSharedPtr<IImageWrapper>  ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);
	if (ImageWrapper->SetRaw(colors.GetData(), colors.Num()*sizeof(FColor),InWidth, InHeight, ERGBFormat::BGRA, 8))
	{
		FFileHelper::SaveArrayToFile(ImageWrapper->GetCompressed(), *ScreenShotName);
	}*/
	return true;
}
 
void AThirdPersonGameMode::StartPlay()
{
	Super::StartPlay();
	FPlatformProcess::PushDllDirectory(*(FPaths::ProjectDir() / TEXT("ThirdParty/")));///< 将 dll 存放目录加入搜索路径
	CaptureScreenHandle = FPlatformProcess::GetDllHandle(TEXT("CaptureScreen.dll"));///< 或者全路径 FPaths::ProjectDir()/TEXT("ThirdParty/CaptureScreen.dll")
	TestCapture(0,0,0,0); ///< *
}
 
void AThirdPersonGameMode::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	if (CaptureScreenHandle)
	{
		FPlatformProcess::FreeDllHandle(CaptureScreenHandle);
		CaptureScreenHandle = nullptr;
	}
	Super::EndPlay(EndPlayReason);
}
/// @note *
typedef bool(*OnCaptureComplete)(const char*, int, int, int);
typedef bool(*RunCaptureProc)(int, int, int, int, OnCaptureComplete);
/// @note 调用动态库函数
void AThirdPersonGameMode::TestCapture(int x, int y, int width, int height)
{
	if (!CaptureScreenHandle)
	{
		return;
	}
	RunCaptureProc RunCapture = (RunCaptureProc)FPlatformProcess::GetDllExport(CaptureScreenHandle, TEXT("RunCapture"));
	if (RunCapture)
	{
		RunCapture(x, y, width, height, OnCaptureData);
	}
}
如果 dll 文件不依赖其他 dll 文件,GetDllHandle 可传入 dll 文件的完整路径而不需要 PushDllDirectory,但是如果 dll 还依赖于其他 dll 文件并且也在 Thirdparty 目录下,就必须调用PushDllDirectory,否则找不到依赖的 dll 文件。