見出し画像

UE5 EA Visual Studio 2019 ~動的リンクライブラリ(.dll)を作成してUEで使用する~

Visual Studio 2019で動的ライブラリ(.dll)を作成するプロジェクトを作成していきます。

DLLファイルとは、Windowsのプログラムファイルの種類の一つで、様々なプログラムから利用される汎用性の高い機能を収録した、部品化されたプログラムのこと。標準のファイル拡張子は「.dll」。

DLLファイル 【Dynamic Link Library】

DLLには2種類のリンク方式があります。

  • Explicit Linking(明示的リンク)

  • Implicit Linking(暗黙的リンク)

2種類のリンク方式のDLLを作成して、UE4で呼び出す方法についてまとめました。

Visual Studio 2019で動的ライブラリ(DLL)を作成する

Explicit Linking(明示的リンク)のDLLを作成する

Visual Studio 2019で新しいプロジェクトを作成します。

Visual Studio 2019 > Create a new project

Dynamic Link Library(DLL)を作成するテンプレートを選択します。

今回は一つのソリューションに2つのプロジェクトを含めるので、Project nameとSolution nameを別々に設定します。
・Project name:DLL_Explicit
・Solution name:DLLSample

Project nameとSolution nameを別々に設定

Module-Definition File(.def)とC++ファイル(.cpp)を追加します。
・DLL_Explicit.def
・DLL_Explicit.cpp

Module-Definition File(.def)とC++ファイル(.cpp)を追加

Module-Definition File(.def)は[Visual C++ > Code]を選択すると表示されます。

Module-Definition File(.def)は[Visual C++ > Code]を選択すると表示される

DLL_Explicit.def

LIBRARY DLL_Explicit
EXPORTS
    DLL_Explicit_AddTest @1
    DLL_Explicit_AddTest_Arg3 @2

DLL_ExplicitTest.cpp

#include "pch.h"

int DLL_Explicit_AddTest(int A, int B)
{
	return A + B;
}

int DLL_Explicit_AddTest_Arg3(int A, int B, int C)
{
	return A + B + C;
}

Module-Definition File[DLL_Explicit.def]が設定されていることを確認します。
プロジェクトファイルを右クリック > Properties

プロジェクトファイルを右クリック > Properties

Release(x64)の設定に作成したModule-Definition File[DLL_Explicit.def]が設定されていることを確認します。

Module-Definition File[DLL_Explicit.def]が設定されていることを確認する

Build ConfigurationをRelease(x64)に設定します。
Configuration Managerを開きます。

Configuration Managerを開く

Build ConfigurationをRelease(x64)に設定します。
・Active solution configuration:Release
・Active solution platform:x64

Build ConfigurationをRelease(x64)に設定

プロジェクトをビルドします。

プロジェクトを右クリック > Build

ソリューションのフォルダ > x64 > Release配下にDLLが作成されます。
Explicit Linging(明示的リンク)のDLLは.dllファイルのみを使用します。

Explicit Linging(明示的リンク)のDLLは.dllファイルのみを使用

Implicit Linking(暗黙的リンク)のDLLを作成する

ソリューションにImplicit Linking(暗黙的リンク)のDLLを作成するプロジェクトを追加します。

ソリューションを右クリック > Add

Dynamic Link Library(DLL)を作成するテンプレートを選択します。

Dynamic Link Library(DLL) > Next

Project nameを[DLL_Implicit]設定します。

Project name:DLL_Implicit > Create

ヘッダファイル(.h)とC++ファイル(.cpp)を追加します。
・DLL_Implicit.h
・DLL_Implicit.cpp

C++ファイル(.cpp)を追加します。

DLL_Implicit.h

#pragma once

#ifdef DLL_EX
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif

#ifdef __cplusplus
extern "C"
{
#endif

	DLL_API int DLL_Implicit_AddTest(int A, int B);
	DLL_API int DLL_Implicit_AddTest_Arg3(int A, int B, int C);

#ifdef __cplusplus
}
#endif

DLL_Implicit.cpp

#include "pch.h"
#include "DLL_Implicit.h"

int __stdcall DLL_Implicit_AddTest(int A, int B)
{
	return A + B;
}

int __stdcall DLL_Implicit_AddTest_Arg3(int A, int B, int C)
{
	return A + B + C;
}

この状態で一度Buildを行います。

プロジェクトファイルを右クリック > Build

Implicit Linking(暗黙的リンク)のDLLはDLLとLIB と、LIBから呼び出すのでヘッダーファイルが必要になります。

(ソリューションフォルダ)/x64/Release
・DLL_Implicit.dll
・DLL_Implicit.lib
(ソリューションフォルダ)/DLL_Implicit
・DLL_Implicit.h

Implicit Linking(暗黙的リンク)を使用するのに必要になるファイル

UE5 EAでDLLを使用する

DLLを使用するテスト用のC++プロジェクトを作成する

UE5 EAでDLLを使用する準備をします。
BlankのC++プロジェクトを作成します。

BlankのC++プロジェクトを作成

Explicit Linking(明示的リンク)のDLL(DLL_Explicit.dll)を使用する

DLL_Explicit.dllをUE5プロジェクトにコピーします。

【コピー元】
(ソリューションファイル)/x64/Release
・DLL_Explicit.dll
【コピー先】
(UE5プロジェクトフォルダ)/Binaries/Win64

DLL_Explicit.dllをUE5プロジェクトにコピー

Explicit Linking(明示的リンク)のDLLを呼び出すためのActorクラスを追加します。

C++Classes配下のフォルダを右クリック > New C++ Class…

親クラスにActorを選択します。

ACTOR > NEXT >

名前を[DLLExplicitActor](Private)に設定します。

Private > Name:DLLExplicitActor > CREATE CLASS

追加したソースコードを修正します。

DLLExplicitActor.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "DLLExplicitActor.generated.h"

UCLASS()
class ADLLExplicitActor : public AActor
{
	GENERATED_BODY()

public:	
 	UFUNCTION(BlueprintCallable, Category="Call DLL Explicit Test")
	static int32 CallExplicitAddTest(const int32 A, const int32 B, bool& success);

	UFUNCTION(BlueprintCallable, Category = "Call DLL Explicit Test")
	static int32 CallExplicitAddTest_Arg3(const int32 A, const int32 B, const int32 C, bool& success);

};

DLLExplicitActor.cpp

#include "DLLExplicitActor.h"

int32 ADLLExplicitActor::CallExplicitAddTest(const int32 A, const int32 B, bool& success)
{
	typedef int(*_dll_function)(int _A, int _B);            // DLLAddTestのポインタ型を宣言する
	static void* dll_ptr = nullptr;                         // 読み込むDLL(DynamicLibTest.dll)を格納する変数
	static _dll_function func_ptr = nullptr;                // DLLに含まれる関数へのポインタを格納する変数

	const static FString dllname = "DLL_Explicit.dll";      // DLL名
	const static FString funcname = "DLL_Explicit_AddTest"; // 関数名

	// DLLを読み込み、ポインタを取得
	dll_ptr = FPlatformProcess::GetDllHandle(*dllname);
	if (!dll_ptr)
	{
		success = false;
		return 0;
	}
	// DLLから関数の位置を取得
	func_ptr = (_dll_function)FPlatformProcess::GetDllExport(dll_ptr, *funcname);
	if (!func_ptr)
	{
		success = false;
		return 0;
	}

	// 関数の呼び出しが成功したので、関数を呼び出す
	success = true;
	return (*func_ptr)(A, B);
}

int32 ADLLExplicitActor::CallExplicitAddTest_Arg3(const int32 A, const int32 B, const int32 C, bool& success)
{
	typedef int(*_dll_function)(int _A, int _B, int _C);
	static void* dll_ptr = nullptr;
	static _dll_function func_ptr = nullptr;

	const static FString dllname = "DLL_Explicit.dll";
	const static FString funcname = "DLL_Explicit_AddTest_Arg3";

	// DLLを読み込み、ポインタを取得
	dll_ptr = FPlatformProcess::GetDllHandle(*dllname);
	if (!dll_ptr)
	{
		success = false;
		return 0;
	}

	func_ptr = (_dll_function)FPlatformProcess::GetDllExport(dll_ptr, *funcname);
	if (!func_ptr)
	{
		success = false;
		return 0;
	}

	success = true;
	return (*func_ptr)(A, B, C);
}

ビューポートに作成したActor[DLLExplicitActor]を追加します。
DLLの関数を呼び出して、PrintStringで出力する処理を書いていきます。

ビューポートに[DLLExplicitActor]を追加する

レベルブループリントを開きます。

Blurprints >Open Level Blueprint

BeginPlay時に関数[CallExplicitAddTest]と[CallExplicitAddTest_Arg3]を呼び出して、PrintStringで出力する処理を実装します。
関数ごとに色を別々にすると確認しやすいです。

BeginPlay時に関数[CallExplicitAddTest]と[CallExplicitAddTest_Arg3]を呼び出して、
PrintStringで出力する処理を実装

Playして足し算の結果を確認します。
足し算結果が合っているので、DLLの関数を呼び出すことが出来ました。

Explicit Linking(明示的リンク)のDLLの関数を呼び出すことが出来た

ShippingBuild時にアプリのパッケージ内にDLLをコピーする(Build.cs)

DLLを特定の場所に置くことで実行できますが、DLLはアプリのパッケージ内に含める必要があります。DLLをパッケージ内に含める設定を行います。

UEアプリ開発中にはDLLを[(プロジェクトdirectory)/Binaries/Win64」に配置すれば動作するのですが、Shippingビルド時にはアプリのパッケージ内にDLLをコピーする必要があります。

C++でつくるUnreal Engineアプリ開発 for Windows & mac OS

「(UE5 EAプロジェクト名).Build.cs」を開きます。

「(UE5 EAプロジェクト名).Build.cs」を開く

CPP_DLLTest.Build.cs

using UnrealBuildTool;

public class CPP_DLLTest : ModuleRules
{
	public CPP_DLLTest(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
	
		PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });

		PrivateDependencyModuleNames.AddRange(new string[] {  });

		var DLLExplicitPath = ModuleDirectory + "../../../Binaries/Win64/DLL_Explicit.dll";
		PublicDelayLoadDLLs.Add(DLLExplicitPath);
		RuntimeDependencies.Add(new RuntimeDependency(DLLExplicitPath));
	}
}

DLLを読み込み、パッケージ内にDLLをコピーする処理

var DLLExplicitPath = ModuleDirectory + "../../../Binaries/Win64/DLL_Explicit.dll";
PublicDelayLoadDLLs.Add(DLLExplicitPath);
RuntimeDependencies.Add(new RuntimeDependency(DLLExplicitPath));

Shippingビルドをして、DLLがアプリ内に含まれるか確認します。
Binary Configurationを[Shipping]に設定します。

Platforms > Windows > Shipping

プロジェクトをパッケージ化します。

Platform > Windows > Package Project 

DLL_Explicit.dllがアプリと同じフォルダにコピーされました。

DLL_Explicit.dllがアプリと同じフォルダにコピーされる

ここまでがExplicit Linging(明示的リンク)のDLLを使用するまでの設定になります。

Implicit Linking(暗黙的リンク)のDLL(DLL_Implicit.dll)を使用する

Implicit Linking(暗黙的リンク)のDLLはDLLとLIB と、LIBから呼び出すのでヘッダーファイルが必要になります。

(ソリューションフォルダ)/x64/Release
・DLL_Implicit.dll
・DLL_Implicit.lib
(ソリューションフォルダ)/DLL_Implicit
・DLL_Implicit.h

Implicit Linking(暗黙的リンク)のDLLを使用するのに必要となるファイル

必要なファイルを以下の構成になるようにコピーします

(UE5 EA Projectフォルダ)
  ├ Binaries
  |   └ Win64
  |     └ DLL_Implicit.dll
  └ Source/
    └ (UE5 EA Project名)/
      ├ Library/
      |  ├ header/
      |  |  └ DLL_Implicit.h
      |  └ lib/
      |    └ DLL_Implicit.lib
      ├ Private 
      | 

静的ライブラリの時と同様にBuild.csの修正を行います。

Build.csファイルを修正する

CPP_DLLTestBuild.cs

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

using UnrealBuildTool;

public class CPP_DLLTest : ModuleRules
{
    public CPP_DLLTest(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

        PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });

        PrivateDependencyModuleNames.AddRange(new string[] { });

        var DLLExplicitPath = ModuleDirectory + "../../../Binaries/Win64/DLL_Explicit.dll";
        PublicDelayLoadDLLs.Add(DLLExplicitPath);
        RuntimeDependencies.Add(new RuntimeDependency(DLLExplicitPath));

        PublicIncludePaths.Add(ModuleDirectory + "/Library/header");
        if (Target.Platform == UnrealTargetPlatform.Win64)
        {
            PublicAdditionalLibraries.Add(ModuleDirectory + "/Library/lib/DLL_Implicit.lib");
        }
        var DLLImplicitPath = ModuleDirectory + "../../../Binaries/Win64/DLL_Implicit.dll";
        PublicDelayLoadDLLs.Add(DLLImplicitPath);
        RuntimeDependencies.Add(new RuntimeDependency(DLLImplicitPath));
    }
}

・ヘッダーファイルのIncludePathを追加
・静的ライブラリを追加する(プラットフォームがWin64)
・DLLのコピー処理も追加します。

PublicIncludePaths.Add(ModuleDirectory + "/Library/header");
if (Target.Platform == UnrealTargetPlatform.Win64)
{
    PublicAdditionalLibraries.Add(ModuleDirectory + "/Library/lib/DLL_Implicit.lib");
}
var DLLImplicitPath = ModuleDirectory + "../../../Binaries/Win64/DLL_Implicit.dll";
PublicDelayLoadDLLs.Add(DLLImplicitPath);
RuntimeDependencies.Add(new RuntimeDependency(DLLImplicitPath));

静的ライブラリ(.lib)とヘッダーファイルの配置、Build.csの修正が出来たら、Visual Studio projectを更新します。

.uprojectを右クリック > Generate Visual Studio project files

静的ライブラリ(.lib)とヘッダーファイルがプロジェクトに追加されました。

静的ライブラリ(.lib)とヘッダーファイルがプロジェクトに追加される

ビューポートに作成したActor[DLLImplicitActor]を追加します。
DLLの関数を呼び出して、PrintStringで出力する処理を書いていきます。

C++Classes配下のフォルダを右クリック > New C++ Class…

親クラスにActorを選択します。

ACTOR > NEXT >

名前をDLLImplicitActor(Private)に設定します。

Private > Name:DLLImplicitActor > CREATE CLASS

追加したソースコードを修正します。

DLL_Implicit.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "DLLImplicitActor.generated.h"

UCLASS()
class ADLLImplicitActor : public AActor
{
	GENERATED_BODY()
	
public:	
	UFUNCTION(BlueprintCallable, Category = "Call DLL Implict Test")
		static int32 CallImplicitAddTest(const int32 A, const int32 B);

	UFUNCTION(BlueprintCallable, Category = "Call DLL Implict Test")
		static int32 CallImplicitAddTest_Arg3(const int32 A, const int32 B, const int32 C);
};

DLL_Implicit.cpp

// Fill out your copyright notice in the Description page of Project Settings.
#include "DLLImplicitActor.h"
#include "DLL_Implicit.h"


int32 ADLLImplicitActor::CallImplicitAddTest(const int32 A, const int32 B)
{
	return DLL_Implicit_AddTest(A, B);
}

int32 ADLLImplicitActor::CallImplicitAddTest_Arg3(const int32 A, const int32 B, const int32 C)
{
	return DLL_Implicit_AddTest_Arg3(A, B, C);
}

ビューポートに作成したActor[DLLImplicitActor]を追加します。
DLLの関数を呼び出して、PrintStringで出力する処理を書いていきます。

ビューポートに「DLLImplicitActor」を追加する

レベルブループリントを開きます。

BeginPlay時に関数[CallImplicitAddTest]と[CallImplicitAddTest_Arg3]を呼び出して、PrintStringで出力する処理を実装します。
関数ごとに色を別々にすると確認しやすいです。Explicitの結果と切り分けるために数値を10倍しています。

BeginPlay時に関数[CallImplicitAddTest]と[CallImplicitAddTest_Arg3]を呼び出して、
PrintStringで出力する処理を実装

Playして足し算の結果を確認します。
足し算結果が合っているので、DLLの関数を呼び出すことが出来ました。

DLLの関数を呼び出すことが出来た

Shippingビルド時にDLLがパッケージ内にコピーされるか確認します。
プロジェクトをパッケージングします。

Platforms > Windows > Package Project

DLL_Implicit.dllも同様にパッケージにコピーされました。

DLL_Implicit.dllも同様にパッケージにコピーされる

ここまでがImplicit Linging(暗黙的リンク)のDLLを使用するまでの設定になります。

まとめ

静的ライブラリとそんなに変わらなそうだから直ぐに書けると始めましたが、かなりのボリュームになりました。
これで外部ライブラリを使用出来るようになったのでUnreal Engine以外の機能を取り込むことが出来るようになりました。
DLLは奥が深いし、今後も使っていくので随時更新していきます。
いよいよプラグイン開発の入口に立つことが出来たのでワクワクします。

【参考資料】

【参照URL(VisualStudio側)】

【参照URL(Unreal Engine側)】




この記事が気に入ったらサポートをしてみませんか?