Мне нужна альтернатива `Assembly.GetEntryAssembly()`, которая никогда не возвращает null
Мне нужно найти сборку, в которой запущено исполнение управляемого кода.
// using System.Reflection;
Assembly entryAssembly = Assembly.GetEntryAssembly();
Это похоже на способ, но страница MSDN для Assembly.GetEntryAssembly
утверждает, что этот метод "[c] возвращает null при вызове из неуправляемого кода."
В этом случае я хотел бы знать, какая сборка вызывается неуправляемым кодом.
Есть ли надежный способ сделать это, то есть тот, который всегда возвращает ссылку на ненулевое Assembly
?
Ответы
Ответ 1
Лучшее, о чем я мог догадаться, - это следующее, которое должно работать в однопоточном сценарии:
// using System.Diagnostics;
// using System.Linq;
Assembly entryAssembly = new StackTrace().GetFrames().Last().GetMethod().Module.Assembly;
(приведенный выше фрагмент оптимизирован для удобства понимания, а не для скорости выполнения или эффективности памяти.)
Ответ 2
Я пробовал оба метода 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
.
Если такой метод не найден, я ищу метод, вызываемый посредством отражения. Например, NUnit использует этот вызов для загрузки модульных тестов.
Конечно, я делаю это, только если Assembly.GetEntryAssembly()
возвращает null
.
Ответ 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
всегда возвращает ненулевую ссылку?