39

Given a DLL file, I'd like to be able to find all the calls to a method within that DLL file. How can I do this?

Essentially, how can I do programmatically what Visual Studio already does?

I don't want to use a tool like .NET Reflector to do this, but reflection is fine and probably necessary.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
user420667
  • 6,022
  • 14
  • 47
  • 81
  • 2
    Do you need to detect only static calls to the method? Should calls with Reflection and Reflection Emit also be detected? – Darin Dimitrov Mar 30 '11 at 17:48
  • And should this work with non-managed code? Not all DLLs support reflection. – James Keesey Mar 30 '11 at 17:50
  • 1
    @Darin: those would be icing on the cake, but aren't necessary. @James: I don't think I have to worry about non-managed code. How could I do this on dlls that don't support reflection? – user420667 Mar 30 '11 at 18:18

6 Answers6

52

To find out where a method MyClass.Foo() is used, you have to analyse all classes of all assemblies that have a reference to the assembly that contains MyClass. I wrote a simple proof of concept of how this code can look like. In my example I used this library (it's just a single .cs file) written by Jb Evain:

I wrote a little test class to analyse:

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

And I wrote this code to print out all the methods used within 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())
        );
    }
}

It gave me the following output:

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

This example is obviously far from complete, because it doesn't handle ref and out parameters, and it doesn't handle generic arguments. I am sure that forgot about other details as well. It just shows that it can be done.

Jb Evain
  • 16,611
  • 2
  • 63
  • 66
Elian Ebbing
  • 17,427
  • 5
  • 43
  • 54
  • Nice. This also uses Linq for the select statement. Time to build me some dependency graphs. – user420667 Mar 30 '11 at 20:47
  • I'm getting this error message: "Value does not fall within the expected range." from this bit of code: `this.body = method.GetMethodBody(); if (this.body == null) throw new ArgumentException();` and I'm wondering how there could be bodyless methods (or what else could be going on). any thoughts? – ekkis Sep 14 '13 at 00:22
  • 1
    @ekkis: an abstract method (of an abstract class) is bodyless. You can check if the method is abstract by checking `MethodBase.IsAbstract`. – Elian Ebbing Sep 14 '13 at 01:00
  • 1
    @ElianEbbing, apparently there are other kinds of bodiless methods. Take a look at `System.GetType()` - I'm not sure what's special about this one but the `.GetMethodBody()` also comes up null. I wonder whether we should be throwing ArgumentExceptions in these cases or just fail silently – ekkis Sep 18 '13 at 19:10
  • @ekkis - You are right. It seems that the core .net library contains some methods that have no body. These methods are decorated with the `[MethodImpl(MethodImplOptions.InternalCall)]` attribute and are implemented within the CL runtime itself. I think you are right that the `GetInstructions()` method should return an empty list in this case instead of an exception. – Elian Ebbing Sep 18 '13 at 20:51
  • MethodBodyReader - I learn something new everyday, this is liquid gold! – mhand Jan 22 '16 at 02:29
  • 1
    Nuget alternative to `MethodBodyReader`: nuget package **mono.reflection** with `var instructions = Disassembler.GetInstructions(methodBase);` – Joep Geevers Mar 22 '21 at 21:05
4

You may take a look at the MSDN Magazine article Determining .NET Assembly and Method References.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Darin Dimitrov
  • 960,118
  • 257
  • 3,196
  • 2,876
  • +1: note that you'll have to search somewher for IL reader - Reflector's one is not directly available as far as I can tell... – Alexei Levenkov Mar 30 '11 at 18:00
  • Good article. But it suggests using Reflector for the method dependencies. I guess they all boil down to parsing IL. – user420667 Mar 30 '11 at 20:46
3

Reflection alone is not enough to find all references to a method in a given assembly. Reflection gives you a byte array for the body of any particular method (MethodInfo.GetMethodBody.GetILAsByteArray) and you have to parse it yourself for references to other methods. There are several publicly available "CIL reader" libraries (I have not used them - hopefully someone will post more on it).

Adding FxCop option - depending on your scenario, you may be able to reuse CIL parsing logic provided by FxCop (Visual Studio code analysis) and add your custom rules if running it as part of code analysis is OK for you.

BenMorel
  • 30,280
  • 40
  • 163
  • 285
Alexei Levenkov
  • 94,391
  • 12
  • 114
  • 159
1

I would consider reflecting the Visual Studio assemblies and see if you can find it in the reverse engineered code base. I believe VS is actually navigating code rather than reflecting. Reflection, as Michael has posted, is great for determining the bits of an assembly, but not the consumers of those bits. I have not re-examined reflection to confirm my suspicions, however.

Gregory A Beamer
  • 16,342
  • 3
  • 23
  • 29
0

Hey this is an example assuming you want to search for all the calls in the current assembly. I coded this trying to get a parameter value in order to set some constraints for a method with some default values. But I couldn't manage to get the parameter values, I got only types and defaults.

var currentAssembly = Assembly.GetExecutingAssembly();

foreach (var method in currentAssembly.GetTypes().Where(type => type.IsClass)
                                      .SelectMany(cl => cl.GetMethods())
                                      .OfType<MethodBase>()
                                      .SelectMany(mb => mb.GetInstructions())
                                      .Select(inst => inst.Operand).OfType<MethodInfo>()
                                      .Where(mi => mi.Name == "<YourMethodName>"))
{
    //here are your calls.
}

Hope it helps.

-1

See Stack Overflow question Get a list of functions for a DLL.

Ripped from the above (thanks Jon Skeet):

For a particular assembly, you can use Assembly.GetTypes to get the types, then for each type call Type.GetMethods(), Type.GetProperties() etc, or just Type.GetMembers().

However, for plugin functionality it's usually a good idea to have a common interface which the plugins have to implement - that reduces the amount of reflection you need to use. Use Type.IsAssignableFrom() to check whether a type is compatible with a particular interface.

You might also want to look at the Managed Extensibility Framework which can make implementing an extension system easier.

Community
  • 1
  • 1
Michael
  • 1,578
  • 5
  • 22
  • 40
  • 2
    I don't see how this answer helps in finding calls to a given method. It helps list types, methods, properties, etc in a target assembly, but the OP is looking for a way to detect that a given method is calling another method. – Darin Dimitrov Mar 30 '11 at 17:51
  • This is not what the OP is asking about. – Gregory A Beamer Mar 30 '11 at 17:52
  • This is not what the OP is looking for. This lists the methods, but not their invocations. – jason Mar 30 '11 at 17:52