我需要一个替代`Assembly.GetEntryAssembly()`的方法,它永远不会返回null。

27

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

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

这似乎是合适的方法,但Assembly.GetEntryAssembly的MSDN参考页面指出,当从非托管代码中调用此方法时,该方法可能返回null。

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

是否有可靠的方法来做到这一点,即始终返回一个非空的Assembly引用?

3个回答

23

到目前为止,我能想到的最好的方法是以下内容,适用于单线程情况:

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

上述代码片段是为了易于理解而进行优化的,而不是为了执行速度或内存效率。


7
已经点赞了,但更好的做法可能是这样:Assembly.GetEntryAssembly() ?? new StackTrace().GetFrames().Last().GetMethod().Module.Assembly - Ohad Schneider
mscorelib... 我会避免使用它。 - HellBaby

12
我尝试了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.LastOrDefault();

Assembly entryAssembly = entryMethod?.Module?.Assembly;

基本上,我会遍历堆栈直到找到一个返回类型为voidint常规方法,该方法名为“Main”。 如果找不到这样的方法,则我会寻找通过反射调用的方法。例如,NUnit使用该调用来加载单元测试。
当然,只有在Assembly.GetEntryAssembly()返回null时,我才会执行上述操作。

主函数可能会返回一个 int,而其他类中可能会有其他的主函数,但这是一个好的开始。 - Rob Prouse
没错,我更新了我的答案以反映你关于返回类型(void、int、Task、Task<int>)的建议。至于同音异义词“Main”方法,我认为这是一个罕见的情况,上面的代码只是最大努力,而不是保证。另外,我考虑到的不仅是方法的名称,还有堆栈跟踪。因此,库中声明的其他方法“Main”并不足以欺骗该片段。 - Eric Boumendil

4
另一个(大部分未经测试的)可行解决方案的起点可能是这样的:
// 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 是否保证始终返回非空引用?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接