web-dev-qa-db-ja.com

nullを返さない「Assembly.GetEntryAssembly()」の代替手段が必要です

マネージコードの実行が開始されたアセンブリを見つける必要があります。

// using System.Reflection;
Assembly entryAssembly = Assembly.GetEntryAssembly();

これは進むべき道のように思えますが、 Assembly.GetEntryAssemblyのMSDNリファレンスページ は、このメソッド "[c]がアンマネージから呼び出されたときにnullを返すと述べていますコード。 "

その場合、どのアセンブリがアンマネージコードによって呼び出されたかを知りたいです。

これを行うための信頼できる方法、つまり常にnull以外のAssembly参照を返す方法はありますか?

これまでに考えられた最善の方法は次のとおりです。これは、シングルスレッドのシナリオで機能するはずです。

// using System.Diagnostics;
// using System.Linq; 
Assembly entryAssembly = new StackTrace().GetFrames().Last().GetMethod().Module.Assembly;

(上記のスニペットは、実行速度やメモリ効率ではなく、理解を容易にするために最適化されています。)

18
stakx

Stakxの両方の方法を試しました。

MainModuleに基づくメソッド 一部の特殊なケース(動的アセンブリなど)では機能しません。

StackTraceに基づくメソッド は、mscorlibのように、階層内で高すぎる(または低すぎる)アセンブリを返す可能性があります。

私は自分のユースケースでうまく機能する小さなバリアントを作成しました:

_// using System.Diagnostics;
// using System.Linq;
var methodFrames = new StackTrace().GetFrames().Select(t => t.GetMethod()).ToArray();
MethodBase entryMethod = null;
int firstInvokeMethod = 0;
for (int i = 0; i < methodFrames.Length; i++)
{
    var method = methodFrames[i] as MethodInfo;
    if (method == null)
        continue;
    if (method.IsStatic &&
        method.Name == "Main" &&
        (
            method.ReturnType == typeof(void) || 
            method.ReturnType == typeof(int) ||
            method.ReturnType == typeof(Task) ||
            method.ReturnType == typeof(Task<int>)
        ))
    {
        entryMethod = method;
    }
    else if (firstInvokeMethod == 0 &&
        method.IsStatic &&
        method.Name == "InvokeMethod" &&
        method.DeclaringType == typeof(RuntimeMethodHandle))
    {
        firstInvokeMethod = i;
    }
}

if (entryMethod == null)
    entryMethod = firstInvokeMethod != 0 ? methodFrames[firstInvokeMethod - 1] : methodFrames.Last();

Assembly entryAssembly = entryMethod.Module.Assembly;
_

基本的に、voidまたはintの戻り値の型を持つ "Main"という名前の 従来のメソッド が見つかるまでスタックを上に移動します。そのようなメソッドが見つからない場合は、リフレクションを介して呼び出されるメソッドを探します。たとえば、NUnitはその呼び出しを使用して単体テストをロードします。

もちろん、私はAssembly.GetEntryAssembly()nullを返す場合にのみそれを行います。

10
Eric Boumendil

実用的なソリューションのもう1つの(ほとんどテストされていない)開始点は、次のようなものです。

// using System;
// using System.Diagnostics;
// using System.Linq;
ProcessModule mainModule = Process.GetCurrentProcess().MainModule;
Assembly entryAssembly = AppDomain.CurrentDomain.GetAssemblies()
                         .Single(Assembly => Assembly.Location == mainModule.FileName);

いくつかの不確実性が残っています:

  • モジュールとアセンブリは同じものではありません。 ProcessModuleModuleとは概念的に異なる場合もあります。上記のコードは、特にアセンブリのエントリポイントがマニフェストモジュールにない場合、マルチモジュール(つまりマルチファイル)アセンブリが存在する場合に常に機能しますか?

  • Process.MainModule常にnull以外の参照を返すことが保証されていますか?

3
stakx