C++ ときどき ごはん、わりとてぃーぶれいく☆

Wonder Rabbit Projectのなかのひとのブログ。主にC++。

.net./C#/UE4: UE4 製のアプリを C# から起動したり終了したりさせたい場合のメモ、あるいは閉じても閉じても殺しても死なないアプリを殺すメモ。

// Process
using System.Diagnostics;
// C# アプリから UE4 製のアプリ(に限らず)を起動してプロセスをハンドルする
var p = Process.Start( "your-executable-path.exe" );
// 既に動作している UE4 製のアプリ(に限らず)を探してプロセスをハンドルする
// note: hoge.uproject で作った hoge.exe なら hoge で探せる。
var ps = Process.GetProcessesByName( "your-ue4-project-name" );
if ( ps.Length > 0 )
  var p = ps.First();
// ハンドルしている UE4 製のアプリ(に限らず)のプロセスを殺す(つもりだが死なない)
p.Close(); // <-- 残念でした。死にません。
// メインウィンドウを殺す(つもりだが死なない)
p.CloseMainWindow(); // <-- 残念でした。死にません。
// お前を…殺す(つもりだが死なない)
p.Kill(); // <-- 残念でした。死にません。

実は死んでいるのだが死んでいない。UE4製のアプリは元気に何事も無く動作している上に、 GetProcessByName に発見されないステルス状態になっている…かのような挙動が観測されるようになる。

ここで UE4 製のアプリのプロセスがどうなっているのか Process Monitor とかそういう何かでプロセスツリーを確認しよう。(WindowsPowerShell でも psエイリアスとして動作するようだけど、ツリー表示はできないらしい。 WSL からでは Windows のプロセスは見えないし。不便じゃのぅ…😅)

f:id:USAGI-WRP:20181201152347p:plain

f:id:USAGI-WRP:20181201152411p:plain

今回は G5 という UE4 製のアプリです。 UE4 のパッケージ機能を使い Windows 向けにプロダクトを出力すると、出力ディレクトリー直下に <project-name>.exe が生成され、一般的には配布先のユーザーはこれを起動してアプリを使います。

しかし、実際にはこの出力ディレクトリー直下の <project-name>.exeBinaries/Win64/<project-name>-Win64-Shipping.exe などを子プロセスとして起動させる事で UE4 製のアプリの"本体"(というか"実体")を間接的に起動させるプロセスのラッパー。だから C# の実装で <project-name> で起動したり探したりしたプロセスを Close しても CloseMainWindow しても Kill してもユーザーの目に映る動作中の UE4 製のアプリの "本体" は死なずに動作し続けます。プロセス名も尾びれが付いていて異なるため、目に見える UE4 製のアプリは動き続けているのに GetProcessesByName で探せない一見すると謎のステルス状態のように観測されるようになります。

// UE4 製のアプリを起動してプロセスをハンドル
var p = Process.Start( "your-executable-path.exe" );
// ぶち殺す
using ( var killer = new Process() )
{
  killer.StartInfo.FileName = System.IO.Path.Combine( Environment.GetFolderPath( Environment.SpecialFolder.System ), "taskkill.exe" );
  killer.StartInfo.Arguments = string.Format( "/PID {0} /T /F", p.Id );
  killer.StartInfo.CreateNoWindow = true;
  killer.StartInfo.UseShellExecute = false;
  killer.Start();
  killer.WaitForExit();
}
// ↑ ref. https://qiita.com/yohhoy/items/b6e32e17c9d568f927d8
// @yohhoy さんありがとう♥(2014年の記事なので最新のピカピカマイクロソフトAPIではこんなことしなくて済むのか知らんけど。)

率直に言ってウケル…笑っていいと思います。でも、 Windows ってたまにこーゆーことした方が楽だったり高速だったりって事あるのよね…。例えば真面目に WMI を使うよりコマンド呼んで文字列の結果をパースした方が早かったり速かったり、なんてこともあった。

UE4 アプリの場合は子プロセスで起動される事に気づいてしまえば子プロセスを確定する方法はもうちょっと丁寧だったり、あるいはファイルから泥臭く拾ってくるにせよ Close を呼んであげる事もできるけれど、汎用性もあるのでブログにこの方法もメモしておく事にしました。

そもそも終了に限らず別プロセスからにゃんにゃんするなら適当なプロセス間通信の手段を用意した方がいいけどね😅

References