c# .net system.reflection entry-point

c# - Necesito una alternativa a `Assembly.GetEntryAssembly()` que nunca devuelve null



.net system.reflection (3)

Lo mejor que se me ha ocurrido es lo siguiente, que debería funcionar en un escenario de un solo hilo:

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

(El fragmento de código anterior está optimizado para facilitar la comprensión, no para la velocidad de ejecución o la eficiencia de la memoria).

Necesito encontrar el ensamblaje en el que se inició la ejecución del código administrado.

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

Esto parece ser el camino a seguir, pero la página de referencia de MSDN para Assembly.GetEntryAssembly indica que este método "[c] devuelve un valor nulo cuando se llama desde un código no administrado".

En ese caso, me gustaría saber qué ensamblado fue llamado por un código no administrado.

¿Existe una forma confiable de hacer esto, es decir, una que siempre devuelva una referencia de Assembly no nula?


Otro punto de partida (en gran parte no probado) para una solución de trabajo podría ser algo como esto:

// using System; // using System.Diagnostics; // using System.Linq; ProcessModule mainModule = Process.GetCurrentProcess().MainModule; Assembly entryAssembly = AppDomain.CurrentDomain.GetAssemblies() .Single(assembly => assembly.Location == mainModule.FileName);

Quedan algunas incertidumbres:

  • Los módulos y conjuntos no son lo mismo. ProcessModule puede incluso ser conceptualmente diferente de Module . ¿El código anterior siempre funcionará en presencia de conjuntos de múltiples módulos (es decir, de múltiples archivos), especialmente cuando el punto de entrada de un conjunto no se encuentra en el módulo de manifiesto?

  • ¿Se garantiza que Process.MainModule siempre devuelve una referencia no nula?


Probé ambos métodos de stakx.

El método basado en MainModule no funciona en algunos casos especiales (por ejemplo, ensamblajes dinámicos).

El método basado en StackTrace puede devolver un conjunto demasiado alto (o bajo) en la jerarquía, como mscorlib.

Hice una pequeña variante que funciona bien en mis casos de uso:

// 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.Last(); Assembly entryAssembly = entryMethod.Module.Assembly;

Básicamente, subo la pila hasta que encuentro un método convencional llamado "Main" con el tipo void o int return. Si no se encuentra tal método, busco un método invocado a través de la reflexión. Por ejemplo, NUnit usa esa invocación para cargar pruebas unitarias.

Por supuesto, lo hago solo si Assembly.GetEntryAssembly() devuelve null .