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

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

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

这似乎是要走的路,但Assembly.GetEntryAssembly的MSDN参考页指出此方法“[c]从非托管代码调用时返回null。”

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

有没有一种可靠的方法,即总是返回非空的Assembly引用?

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

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

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

我尝试了两种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.Name == "Main" && method.ReturnType == typeof(void)) entryMethod = method; else if (firstInvokeMethod == 0 && method.Name == "InvokeMethod" && method.IsStatic && method.DeclaringType == typeof(RuntimeMethodHandle)) firstInvokeMethod = i; } if (entryMethod == null) entryMethod = firstInvokeMethod != 0 ? methodFrames[firstInvokeMethod - 1] : methodFrames.Last(); Assembly entryAssembly = entryMethod.Module.Assembly; 

基本上,我走了堆栈,直到找到一个名为“Main”的传统方法,返回类型为void 。 如果没有找到这样的方法,我会寻找通过reflection调用的方法。 例如,NUnit使用该调用来加载unit testing。

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

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

 // 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是否保证始终返回非空引用?