Wednesday, August 29, 2007

Assembly.GetCallingAssembly()

I intend to become a serious tech blogger from now on.
[Oh yeah, it's something we that hold day jobs and family (wife & kid) to boot promise ourselves daily :)] I ran into Assembly.GetCallingAssembly() while working on a larger problem [which again I hope to blog in a series of posts succeeding this one].

  1. Fireup your notepad, copy/paste or type (oh, look out for Tenosynovitis) the below code.
  2. Save as CalledAsm.cs
  3. In VS.Net command prompt compile using, csc /t:library CalledAsm.cs
    1 using System;
    2 using System.Reflection;
    3 using System.Runtime.CompilerServices;
    4 
    5 public class CalledType
    6 {
    7     protected static void InternallyCalledMethod()
    8     {
    9         Console.WriteLine("Called Assembly in InternallyCalledMethod is {0}", Assembly.GetCallingAssembly().FullName);
   10 
   11     }
   12 
   13     public static void CalledMethod()
   14     {
   15         Console.WriteLine("Called Assembly in CalledMethod is {0}", Assembly.GetCallingAssembly().FullName);
   16         InternallyCalledMethod();
   17     }
   18 }
  1. In a new file, type the below code
  2. Save as CallingAsm.cs
  3. Compile using csc /t:exe CallingAsm.cs /r:CalledAsm.dll
    1 public class CallingAsm
    2 {
    3 
    4     public static void Main()
    5     {
    6         CalledType.CalledMethod();
    7     }
    8 }


getexecasm-1


Oops... CallingAsm in CalledMethod() is okay. But even in InternallyCalledMethod()?
The answer is inlining... C# compiler is inlining InternallyCalledMethod!

    1 using System;
    2 using System.Reflection;
    3 using System.Runtime.CompilerServices;
    4 
    5 public class CalledType
    6 {
    7     [MethodImpl(MethodImplOptions.NoInlining)]
    8     protected static void InternallyCalledMethod()
    9     {
   10         Console.WriteLine("Called Assembly in InternallyCalledMethod is {0}", Assembly.GetCallingAssembly().FullName);
   11 
   12     }
   13 
   14     public static void CalledMethod()
   15     {
   16         Console.WriteLine("Called Assembly in CalledMethod is {0}", Assembly.GetCallingAssembly().FullName);
   17         InternallyCalledMethod();
   18     }
   19 }



getexecasm-2

So, I trust GetCallingAssembly() to work only in frontline methods i.e. methods I know that would only be called from a different assembly. For example, a public method on my framework class that I do NOT call internally.

But for this to consistently work, you may need to know when does C# compiler inlines method calls?  Here's what MSDN says:

If the method that calls the GetCallingAssembly method is expanded inline by the compiler (that is, if the compiler inserts the function body into the emitted Microsoft intermediate language (MSIL), rather than emitting a function call), then the assembly returned by the GetCallingAssembly method is the assembly containing the inline code. This might be different from the assembly that contains the original method. To ensure that a method that calls the GetCallingAssembly method is not inlined by the compiler, you can apply the MethodImplAttribute attribute with MethodImplOptions.NoInlining.


Which I infer that inlining may happen across assemblies. So, even though the above code works, I feel safer with:

   14 [MethodImpl(MethodImplOptions.NoInlining)]
   15 public static void CalledMethod()
   16 {
   17     Console.WriteLine("Called Assembly in CalledMethod is {0}", Assembly.GetCallingAssembly().FullName);
   18     InternallyCalledMethod();
   19 }
 
 
Update:  How come my blog suddenly starting to look professional?  Thanks to CSAH