【问题标题】:I need an alternative to `Assembly.GetEntryAssembly()` that never returns null我需要一个永远不会返回 null 的 `Assembly.GetEntryAssembly()` 的替代品
【发布时间】:2012-12-19 09:38:31
【问题描述】:

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

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

这似乎是可行的方法,但MSDN reference page for Assembly.GetEntryAssembly 声明此方法“[c]从非托管代码调用时返回 null。”

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

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

【问题讨论】:

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


    【解决方案1】:

    目前我能想到的最好的是以下,它应该在单线程场景中工作:

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

    (上面的sn-p是为了便于理解而优化的,而不是为了执行速度或内存效率。)

    【讨论】:

    • 赞成,但可能会更好地做类似Assembly.GetEntryAssembly() ?? new StackTrace().GetFrames().Last().GetMethod().Module.Assembly
    【解决方案2】:

    我尝试了两种 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.LastOrDefault();
    
    Assembly entryAssembly = entryMethod?.Module?.Assembly;
    

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

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

    【讨论】:

    • Main 可能会返回一个 int 并且其他类中可能还有其他 Main 方法,但这是一个好的开始。
    • 正确,我更新了我的答案以反映您对返回类型(void、int、Task、Task)的建议。至于谐音“Main”方法,我想这是一种罕见的情况,上面的代码只是尽力而为,并非保证。此外,我不仅考虑了方法的名称,还考虑了堆栈跟踪。因此,在库中声明的其他方法“Main”不足以欺骗 sn-p。
    【解决方案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 是否保证始终返回非空引用?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-22
      • 2018-08-21
      • 1970-01-01
      • 1970-01-01
      • 2019-02-07
      • 2012-08-19
      相关资源
      最近更新 更多