PaperSloth’s diary

主にゲーム開発関連についての記事を書きます。

UE4 Editorから外部アプリケーションの実行

この記事はUnreal Engine 4 (UE4) その2 Advent Calendar 2017の17日目の記事です。
Unreal Engine 4 (UE4) Advent Calendar 2017 - Qiita

環境

Visual Studio Community 2015
・Unreal Engine4.17.2
・Windows10

コードの記述

Blueprint Function Libraryから呼び出せる形にしています。
以下コード

header

// Copyright(c) 2016-2017 PaperSloth

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "ExampleFunctionLibrary.generated.h"

UCLASS()
class PROJECT_API UExampleFunctionLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()
public:
	UFUNCTION(BlueprintCallable, Category = "Editor")
	static void CreateProcess(const FString& path, const FString& args = TEXT(""));
};

cpp

// Copyright(c) 2016-2017 PaperSloth

#include "ExampleFunctionLibrary.h"

void UExampleFunctionLibrary::CreateProcess(const FString& path, const FString& args)
{
	FPlatformProcess::CreateProc(*path, *args, true, false, false, nullptr, 0, nullptr, nullptr);
}

実装としてはFPlatformProcess::CreateProcを呼び出すだけのシンプルな実装です。
WindowsPCの場合はFWindowsPlatformProcess::CreateProcを呼んでいるのと同じです。

他にもExecProcessがあり、引数がシンプルなのですがこちらは使用しません。
関数の中を見るとどちらもWinAPIのCreateProcess関数を呼び出していることがわかります。
MSDN - Create Process関数
https://msdn.microsoft.com/ja-jp/library/cc429066.aspx?f=255&MSPPError=-2147217396

詳細が知りたい方はぜひとも内部実装を覗いてみてください。
Engine\Source\Runtime\Core\Private\Windows\WindowsPlatformProcess.cpp

ExecProcessを使用しない理由は簡単です。
関数内でWaitForSingleObjectが呼び出されており
起動したプロセスが終了するまでUE4 Editorが編集できなくなるからです。

::WaitForSingleObject(ProcInfo.hProcess, INFINITE);

Editorからの実行

簡単にEditor拡張を行うために、Blutilityを使用します。

先ずはBlutilityを使用できるように設定を有効にします。
エディターを再起動する必要はありません。
f:id:PaperSloth:20171125142636p:plain

Blutilityが有効になっていれば、下図のようにBlueprintタブから作成ができます。
f:id:PaperSloth:20171125142857p:plain

Blutilityを使用する場合は基底クラスに「GlobalEditorUtilityBase」を選択します。
f:id:PaperSloth:20171125143000p:plain

続いてBlueprintの編集です。
適当なCustomEventを作成し、Call In Editorにチェックを入れましょう。
f:id:PaperSloth:20171125143250p:plain

先程作成した関数を呼び出します。
Path:実行したいアプリケーション(exe)のパス
Args:アプリケーション起動時の引数
f:id:PaperSloth:20171125144407p:plain

PathとArgsに変数を渡します。
CustomEventの引数にした場合はBlutilityから呼び出せないため
変数を作成し、Instance Editableにチェックを入れます。
これで実行時に変数を編集できるようになります。
f:id:PaperSloth:20171125202711p:plain

実行画面は下記のようになります。
今回はDirectX診断ツールを起動してみました。
こちらは環境変数に既にパスが通っているため、これだけで呼び出しが可能です。
f:id:PaperSloth:20171125203021p:plain

問題点

さて、このBlutilty拡張なのですが幾つか問題点があります。
・batが起動できない
 これはCreateProcessの仕様です。
 ドキュメントを見てみると
 "To run a batch file, you must start the command interpreter;
  set lpApplicationName to cmd.exe and set lpCommandLine to the following arguments: /c plus the name of the batch file."
 とあります。コマンドプロンプトを立ち上げて引数にパスを指定することで起動できるようです。
CreateProcess function (Windows)

・幾つか起動しないアプリケーションがある
 例:cmd.exe等
 さて、先程のbatが起動できない問題はcmdに引数を渡すだけで実行できそうです。
 しかし、ここで問題発生。cmd.exeを起動できませんでした。
 これについては原因不明で特定に至っていません。

 この問題で何か分かる方がいればコメントかTwitterからアドバイスをいただけると幸いです。

まとめ

・Blutilityでのエディタ拡張は簡単
 SlateやPlugin、エンジン拡張に比べると影響範囲も少なく簡単に機能の追加、削除ができます。

・外部のアプリケーションの呼び出しも簡単
 関数1つを呼び出すだけなので、直ぐに実装できる。
 但し、上記で挙げたような問題に直面しています。

・FPathsとの組み合わせでより使いやすく出来る
 FPathsを使用すれば、エンジンのディレクトリやプロジェクトのディレクトリを簡単に取得できます。
 そのため、Plugin特有の機能やProject特有の機能をVCSにアップしておいてプロジェクトメンバー全員が同じパスで利用することができます。
 エンジン内での利用方法もFPathsとの組み合わせでパスを結合して利用しています。
 FPaths - API Reference
 FPaths | Unreal Engine