我需要一个永远不会返回null的`Assembly.GetEntryAssembly()`的替代品

时间:2013-01-04 22:08:34

标签: c# .net system.reflection entry-point

我需要找到托管代码执行开始的程序集。

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

这似乎是要走的路,但MSDN reference page for Assembly.GetEntryAssembly表示此方法“[c]从非托管代码调用时返回null。”

在这种情况下,我想知道哪个程序集是由非托管代码调用的。

是否有可靠的方法来执行此操作,即始终返回非空Assembly引用的方法?

3 个答案:

答案 0 :(得分:17)

到目前为止,我能想到的最好的是以下内容,它应该在单线程场景中起作用:

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

(上面的代码段经过优化,易于理解,而不是执行速度或内存效率。)

答案 1 :(得分:10)

我尝试了两种stakx方法。

Method based on MainModule在某些特殊情况下不起作用(例如动态程序集)。

Method based on 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;

基本上,我走了筹码,直到找到名为“Main”的conventional method voidint返回类型。 如果没有找到这样的方法,我会寻找通过反射调用的方法。例如,NUnit使用该调用来加载单元测试。

当然,只有当Assembly.GetEntryAssembly()返回null时才会这样做。

答案 2 :(得分:3)

工作解决方案的另一个(很大程度上未经测试的)起点可能是这样的:

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

仍存在一些不确定因素:

  • 模块和组件不是一回事。 ProcessModule甚至可能在概念上与Module不同。以上代码是否总是在存在多模块(即多文件)程序集的情况下工作,尤其是当程序集的入口点不在清单模块中时?

  • Process.MainModule是否保证始终返回非空引用?