2011-03-30 21 views
33

Dado un archivo DLL, me gustaría poder encontrar todas las llamadas a un método dentro de ese archivo DLL. ¿Cómo puedo hacer esto?C# reflexión y búsqueda de todas las referencias

Esencialmente, ¿cómo puedo hacer programáticamente lo que Visual Studio ya hace?

No quiero usar una herramienta como .NET Reflector para hacer esto, pero la reflexión es correcta y probablemente necesaria.

+2

¿Necesita detectar solo llamadas estáticas al método? ¿Deben detectarse las llamadas con Reflection y Reflection Emit? –

+0

¿Y debería funcionar esto con un código no administrado? No todas las DLL admiten la reflexión. –

+1

@Darin: esos serían la guinda del pastel, pero no son necesarios. @James: No creo que tenga que preocuparme por el código no administrado. ¿Cómo podría hacer esto en dlls que no son compatibles con la reflexión? – user420667

Respuesta

43

Para saber dónde se usa un método MyClass.Foo(), debe analizar todas las clases de todos los ensamblados que tengan una referencia al ensamblaje que contiene MyClass. Escribí una simple prueba de concepto de cómo se puede ver este código.En mi ejemplo he usado this library (es sólo a single .cs file) escritos por John Broadus Evain:

me escribió una pequeña clase de prueba para analizar:

public class TestClass 
{ 
    public void Test() 
    { 
     Console.WriteLine("Test"); 
     Console.Write(10); 
     DateTime date = DateTime.Now; 
     Console.WriteLine(date); 
    } 
} 

Y escribí este código para imprimir todos los métodos utilizados en TestClass.Test() :

MethodBase methodBase = typeof(TestClass).GetMethod("Test"); 
var instructions = MethodBodyReader.GetInstructions(methodBase); 

foreach (Instruction instruction in instructions) 
{ 
    MethodInfo methodInfo = instruction.Operand as MethodInfo; 

    if(methodInfo != null) 
    { 
     Type type = methodInfo.DeclaringType; 
     ParameterInfo[] parameters = methodInfo.GetParameters(); 

     Console.WriteLine("{0}.{1}({2});", 
      type.FullName, 
      methodInfo.Name, 
      String.Join(", ", parameters.Select(p => p.ParameterType.FullName + " " + p.Name).ToArray()) 
     ); 
    } 
} 

me dio el siguiente resultado:

System.Console.WriteLine(System.String value); 
System.Console.Write(System.Int32 value); 
System.DateTime.get_Now(); 
System.Console.WriteLine(System.Object value); 

Este ejemplo obviamente no está completo, porque no maneja los parámetros de ref y out, y no maneja argumentos genéricos. Estoy seguro de que olvidó otros detalles también. Simplemente muestra que se puede hacer.

+0

Agradable. Esto también usa Linq para la declaración de selección. Es hora de construir algunos gráficos de dependencia. – user420667

+0

Recibo este mensaje de error: "El valor no está dentro del rango esperado". de este código: 'this.body = method.GetMethodBody(); if (this.body == null) lanza una nueva ArgumentException(); 'y me pregunto cómo podría haber métodos sin cuerpo (o qué más podría estar pasando). ¿Alguna idea? – ekkis

+1

@ekkis: un método abstracto (de una clase abstracta) no tiene cuerpo. Puede verificar si el método es abstracto al marcar 'MethodBase.IsAbstract'. –

-1

Ver la pregunta sobre el desbordamiento de la pila Get a list of functions for a DLL.

Rasgado de lo anterior (gracias Jon Skeet):

Para un montaje especial, puede utilizar Assembly.GetTypes para obtener los tipos, a continuación, para cada tipo de llamada Type.GetMethods(), Type.GetProperties() etc, o simplemente Type.GetMembers().

Sin embargo, para la funcionalidad de los complementos, generalmente es una buena idea tener una interfaz común que los complementos deben implementar, lo que reduce la cantidad de reflejos que necesita usar. Use Type.IsAssignableFrom() para verificar si un tipo es compatible con una interfaz en particular.

Es posible que también desee consultar el Marco de Extensibilidad Administrado que puede facilitar la implementación de un sistema de extensión.

+2

No veo cómo esta respuesta ayuda a encontrar llamadas a un método determinado. Ayuda a enumerar tipos, métodos, propiedades, etc. en un ensamblaje de destino, pero el OP está buscando una forma de detectar que un método determinado está llamando a otro método. –

+0

Esto no es lo que el OP está preguntando. –

+0

Esto no es lo que el OP está buscando. Esto enumera los métodos, pero no sus invocaciones. – jason

1

Consideraría reflejar los ensamblados de Visual Studio y ver si puede encontrarlo en la base de código de ingeniería inversa. Creo que VS realmente está navegando por el código en lugar de reflexionar. La reflexión, como ha publicado Michael, es excelente para determinar los bits de un conjunto, pero no los consumidores de esos bits. Sin embargo, no he reexaminado la reflexión para confirmar mis sospechas.

2

Puede echar un vistazo al artículo Determining .NET Assembly and Method References de la revista MSDN.

+0

+1: tenga en cuenta que tendrá que buscar alguna vez para el lector IL - Reflector de uno no está directamente disponible por lo que puedo decir ... –

+0

Buen artículo. Pero sugiere usar Reflector para las dependencias de método. Supongo que todos se reducen a analizar IL. – user420667

3

La reflexión sola no es suficiente para encontrar todas las referencias a un método en un ensamblaje dado. Reflection le proporciona una matriz de bytes para el cuerpo de cualquier método en particular (MethodInfo.GetMethodBody.GetILAsByteArray) y usted tiene que analizarlo usted mismo para referencias a otros métodos. Hay varias bibliotecas de "CIL reader" disponibles públicamente (no las he usado, espero que alguien publique más).

Añadiendo la opción FxCop - dependiendo de su situación, puede reutilizar la lógica de análisis CIL proporcionada por FxCop (análisis de código Visual Studio) y agregar sus reglas personalizadas si ejecutarla como parte del análisis de código está bien para usted.

Cuestiones relacionadas