I need an alternative to `Assembly.GetEntryAssembly()` that never returns null

心不动则不痛 提交于 2019-11-28 10:44:31

The best I could think of so far is the following, which should work in a single-threaded scenario:

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

(The above snippet is optimized for ease of understanding, not for execution speed or memory efficiency.)

I tried both methods of stakx.

Method based on MainModule does not work in some special cases (dynamic assemblies for example).

Method based on StackTrace can return an assembly too high (or low) in the hierarchy, like mscorlib.

I made a little variant which works well in my use cases :

// 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;

Basically, I walk the stack up until I find a conventional method named "Main" with void or int return type. If no such method is found, I look for a method invoked via reflection. For example, NUnit uses that invocation to load unit tests.

Of course, I do that only if Assembly.GetEntryAssembly() returns null.

Another (largely untested) starting point for a working solution might be something like this:

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

Some uncertainties remain:

  • Modules and assemblies are not the same thing. ProcessModule might even be conceptually different from Module. Would the above code always work in the presence of multi-module (i.e. multi-file) assemblies, especially when an assembly's entry point is not in the manifest module?

  • Is Process.MainModule guaranteed to always returns a non-null reference?

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!