Unity製DLLを外部で動かす時のエラー対処法


はじめに

Unityに依存しているManaged DLLを外部の.NETプロジェクトで動かそうとしたときに、以下のようなエラーが出ることがあります。

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.Security.SecurityException: ECall methods must be packaged into a system module.
at UnityEngine.DebugLogHandler.Internal_Log(LogType level, LogOption options, String msg, Object obj)
at UnityEngine.DebugLogHandler.LogFormat(LogType logType, Object context, String format, Object[] args)
at UnityEngine.Logger.Log(LogType logType, Object message)
at UnityEngine.Debug.Log(Object message)
...

このエラーは、Unityのネイティブコードと連携する関数(例えばUnityEngine.Debug.Logなど)を、Unityのランタイム環境外で呼び出そうとした場合に発生します。

UnityのLog関数などはUnityのネイティブコードと連携して動作するため、Unityのランタイム環境外でこれらの関数を呼び出すと、System.Security.SecurityExceptionがスローされます。

対処法

一般的な対処法としては、Harmonyなどのライブラリを使用して、UnityのLog関数を無効化するパッチを当てる方法があります。

UnityPatch.cs
static void PatchUnityDebug(Harmony harmony)
{
// Get UnityEngine.Debug type
var debugType = Type.GetType("UnityEngine.Debug, UnityEngine.CoreModule")
?? Type.GetType("UnityEngine.Debug, UnityEngine")
?? AppDomain.CurrentDomain
.GetAssemblies()
.Select(a => a.GetType("UnityEngine.Debug"))
.FirstOrDefault(t => t != null);
if (debugType == null)
throw new Exception("UnityEngine.Debug not found!");
// Patch all methods starting with Log
var methods = debugType.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(m => m.Name.StartsWith("Log",
StringComparison.Ordinal));
var prefix = new HarmonyMethod(typeof(Program).GetMethod(nameof(Prefix),
BindingFlags.Static | BindingFlags.Public));
foreach (var m in methods)
{
harmony.Patch(m, prefix: prefix);
}
}
public static bool Prefix(MethodBase __originalMethod, object[] __args)
{
try
{
var args = (__args == null || __args.Length == 0)
? ""
: string.Join(", ",
__args.Select(a =>
a is Array ar ? $"[{string.Join(", ", ar.Cast<object>())}]" : a?.ToString()));
Console.WriteLine($"[Unity LOG] {__originalMethod.Name}({args})");
} catch {}
return false; // Skip original method
}