2013-01-04 97 views
15

我需要找到開始託管代碼執行的程序集。我需要一個`Assembly.GetEntryAssembly()`的替代方法,它永遠不會返回null

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

這似乎是要走的路,但MSDN reference page for Assembly.GetEntryAssembly指出,這種方法「[C]從非託管代碼調用時返回NULL。」

在這種情況下,我想知道哪個程序集是由非託管代碼調用的。

有沒有一種可靠的方法來做到這一點,即總是返回一個非空的Assembly參考?

回答

14

我能想到的迄今最好的是下面的,應在單線程的情況下工作:

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

(上面的代碼是爲了便於理解優化,而不是執行速度或內存效率)

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保證始終返回一個非空引用?

6

我試過兩種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.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; 

基本上,我走棧,直到我找到一個名爲「主」與void返回類型的常規方法。 如果找不到這樣的方法,我查找通過反射調用的方法。例如,NUnit使用該調用來加載單元測試。

當然,我這樣做只有當Assembly.GetEntryAssembly()返回null

相關問題