Published on

UE4使用第三方静态库和动态库

前言

本文介绍一下UE4使用第三方库的简单用法, 即DLL动态库和Lib静态库的使用

我们用插件的形式封装起来, 便于以后移植到其他项目中去;

所以在开始之前先新建一个空或者蓝图库插件

导出动态/静态库

新建控制台应用程序ThirdPartyDLL

然后创建一个同名头文件,写入一下内容

#pragma once

#define DLL_EXPORT __declspec(dllexport)	//shortens __declspec(dllexport) to DLL_EXPORT
#ifdef __cplusplus		//if C++ is used convert it to C to prevent C++'s name mangling of method names
extern "C"
{
#endif

	int DLL_EXPORT GetMac(char * mac);

#ifdef __cplusplus
}
#endif

因为要确保函数名称我们用C的方式编译即extern "C"

实现

#pragma once

#include "string.h"
#include "ThirdPartyDLL.h"


#include <windows.h>
#include <wincon.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <Nb30.h>
#pragma comment(lib,"netapi32.lib")


int  GetMac(char * mac)
{
	NCB ncb;
	typedef struct _ASTAT_
	{
		ADAPTER_STATUS   adapt;
		NAME_BUFFER   NameBuff[30];
	}ASTAT, *PASTAT;

	ASTAT Adapter;

	typedef struct _LANA_ENUM
	{
		UCHAR   length;
		UCHAR   lana[MAX_LANA];
	}LANA_ENUM;

	LANA_ENUM lana_enum;
	UCHAR uRetCode;
	memset(&ncb, 0, sizeof(ncb));
	memset(&lana_enum, 0, sizeof(lana_enum));
	ncb.ncb_command = NCBENUM;
	ncb.ncb_buffer = (unsigned char *)&lana_enum;
	ncb.ncb_length = sizeof(LANA_ENUM);
	uRetCode = Netbios(&ncb);

	if (uRetCode != NRC_GOODRET)
		return uRetCode;

	for (int lana = 0; lana < lana_enum.length; lana++)
	{
		ncb.ncb_command = NCBRESET;
		ncb.ncb_lana_num = lana_enum.lana[lana];
		uRetCode = Netbios(&ncb);
		if (uRetCode == NRC_GOODRET)
			break;
	}

	if (uRetCode != NRC_GOODRET)
		return uRetCode;

	memset(&ncb, 0, sizeof(ncb));
	ncb.ncb_command = NCBASTAT;
	ncb.ncb_lana_num = lana_enum.lana[0];
	strcpy((char*)ncb.ncb_callname, "*");
	ncb.ncb_buffer = (unsigned char *)&Adapter;
	ncb.ncb_length = sizeof(Adapter);
	uRetCode = Netbios(&ncb);

	if (uRetCode != NRC_GOODRET)
		return uRetCode;

	sprintf(mac, "%02X-%02X-%02X-%02X-%02X-%02X",
		Adapter.adapt.adapter_address[0],
		Adapter.adapt.adapter_address[1],
		Adapter.adapt.adapter_address[2],
		Adapter.adapt.adapter_address[3],
		Adapter.adapt.adapter_address[4],
		Adapter.adapt.adapter_address[5]);

	return 0;
}




int main()
{

	char   mac[200];
	GetMac(mac);
	printf("The Mac Address is : %s   \n", mac);

	system("pause");


	return 0;
}

上述内容目的是获取本地的mac地址, 不用理会具体实现方法, 我们主要是拿来当作第三方库使用, 毕竟UE4现在自己的API没有可以拿到Mac地址的;

设置模式为 Release+x64

在vs中简单运行一下得到结果

image-20210111164720144

运行正常, 然后是导出我们所需要的动态库

在项目属性里设置配置类型为DLL,如下

image-20210111164824509

生成以后就得到了我们需要的DLL文件


静态库只需要在项目配置里修改成Lib,然后再生成一次即可

UE4封装函数库

静态方法

先到插件目录下,在source同级目录下新建文件夹ThirdParty,然后分别创建子文件夹DLL,Include,Lib,如下

image-20210111165251214

分别放入的文件为ThirdPartyDLL.dll,ThirdPartyDLL.h,ThirdPartyDLL.lib

然后来到我们的插件模块,找到*.build.cs文件,添加必要路径

//using System.IO; 

private string ModulePath
    {
        get { return ModuleDirectory; }//return 
    }
    private string ThirdPartyPath
    {
        get { return Path.GetFullPath(Path.Combine(ModulePath, "../../ThirdParty/")); }
    }


 PublicIncludePaths.Add(Path.Combine(ThirdPartyPath, "Include"));
        PublicAdditionalLibraries.Add(Path.Combine(ThirdPartyPath, "Lib", "ThirdPartyDLL.lib"));

这样我们就可以在自己的cpp中包含静态库的头文件了

找到/创建蓝图函数库

头文件下声明如下函数

UFUNCTION(BlueprintCallable, BlueprintPure ,Category = "Flib|DllHelper", meta = (DisplayName = "GetMac"))
		static FString GetMacFromLib();

然后实现

#include "ThirdPartyDLL.h"
FString UFlib_DllHelper::GetMacFromLib()
{
	char   mac[200];
	GetMac(mac);
	FString out = FString(mac);
	return out;
	
}

运行测试

image-20210111165817017

image-20210111165829392

运行正常,打包以后也正常

动态方法

动态获取DLL, 我们可以直接在函数库中使用

UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Flib|DllHelper", meta = (DisplayName = "GetMacDLL"))
		static FString GetMacFromDLL();
typedef int(*_getMac)(char * mac);
_getMac funcGetMac; //函数指针

void *v_dllHandle; //DLL句柄

FString UFlib_DllHelper::GetMacFromDLL()
{
    //我们基于插件目录找到DLL文件
	FString folder ="DllImporter//ThirdParty//DLL";
	FString file = "ThirdPartyDLL.dll";
	FString filePath = *FPaths::ProjectPluginsDir() + folder + "/" + file;

	if (FPaths::FileExists(filePath))
	{
		v_dllHandle = FPlatformProcess::GetDllHandle(*filePath);

		if (v_dllHandle != nullptr)
		{
			funcGetMac = nullptr;
			FString procName = "GetMac";
			funcGetMac = (_getMac)FPlatformProcess::GetDllExport(v_dllHandle, *procName);
			if (funcGetMac != nullptr)
			{
				int idx = 0;
				char   mac[200];
				idx = int(funcGetMac(mac));
				return FString(mac);
			}
		}
	}
	return TEXT("");
}

image-20210111171736080

但是如果只是这样的话, 编辑器模式下没有问题, 打包以后会找不到DLL文件

解决方法是在*.build.cs内对动态库添加包含

PublicLibraryPaths.Add(Path.Combine(ThirdPartyPath, "DLL"));
RuntimeDependencies.Add(new RuntimeDependency(Path.Combine(ThirdPartyPath, "DLL","ThirdPartyDLL.dll")));

成功

另外还有一种情况, DLL导出库是Lib的链接, 在编辑器下没有问题, 但是打包以后会提示招不到DLL

但是DLL是在你所在模块插件的目录里, 你需要手动复制到项目Binaries里去,或者用如下的方式自动拷贝并添加动态依赖

string BinariesDirectory = Path.Combine(ProjectDirectory, "Binaries", PlatformString);
			string SourceFile = Path.Combine(ThirdPartyPath, "assimp/bin", PlatformString, "assimp-vc140-mt.dll");
			string TargetFile = Path.Combine(BinariesDirectory, "assimp-vc140-mt.dll");
			if (!Directory.Exists(BinariesDirectory))
            {
				Directory.CreateDirectory(BinariesDirectory);
            }
			if(File.Exists(SourceFile) && !File.Exists(TargetFile))
            {
				File.Copy(SourceFile, TargetFile, false);
            }
			
			RuntimeDependencies.Add(Path.Combine(BinariesDirectory, "assimp-vc140-mt.dll"));

image-20210324113946789

如上图打包以后就放到了正确的目录