45

Is there a way in C# to get the Assembly of the calling method? (Not the current method.)

i.e. I want the executing assembly, one above in the call stack.

Stephen Canon
  • 103,815
  • 19
  • 183
  • 269
David Pfeffer
  • 38,869
  • 30
  • 127
  • 202

6 Answers6

62

Try this

Assembly.GetCallingAssembly();
Daniel
  • 8,655
  • 5
  • 60
  • 87
Stecya
  • 22,896
  • 10
  • 72
  • 102
  • I want the assembly of the calling method, not the current method. – David Pfeffer Mar 29 '11 at 20:57
  • 13
    err - this will give you the assembly of the calling method, not the current method. GetExecutingAssembly gives you the assembly of the current method. Stecya is correct. – Rob Levine Mar 29 '11 at 20:59
  • 7
    Inlining can make this unreliable - see Remarks section in http://msdn.microsoft.com/en-us/library/system.reflection.assembly.getcallingassembly.aspx – Joe Mar 30 '11 at 06:07
  • 9
    Then don't let it be inlined :) `[MethodImplAttribute(MethodImplOptions.NoInlining)]` – MadSkunk Jan 14 '13 at 17:09
  • 2
    Be also aware of tail calls. A **[tail call](https://en.wikipedia.org/wiki/Tail_call)** would be for example `return Assembly.GetCallingAssembly();` - which might be optimized by the compiler as well and hence return unexpected results. A tail call optimization cannot be prevented by an attribute, only by avoiding to call a function right "inline" with the return statement. – Matt Jul 29 '16 at 13:01
14

How about this:

StackTrace stackTrace = new StackTrace();           // get call stack

var assembly = stackTrace.GetFrame(0).GetMethod().DeclaringType.Assembly;

With help from http://www.csharp-examples.net/reflection-callstack/

RQDQ
  • 15,461
  • 2
  • 32
  • 59
  • 2
    This will break when the JIT inlines methods. – JaredPar Mar 29 '11 at 21:01
  • When does the JIT inline methods? – Piotr Kula Oct 15 '15 at 08:53
  • It's a JIT optimization, so it decides according to its internal implementation. Inlining can be prevented though with an attribute: http://stackoverflow.com/questions/5169219/preventing-jit-inlining-on-a-method – RQDQ Oct 15 '15 at 15:02
7

This is what I use:

        var stackFrames = new StackTrace().GetFrames();
        if(stackFrames==null) return null;
        var executingAssembly = Assembly.GetExecutingAssembly();
        foreach (var frame in stackFrames)
        {
            var assembly = frame.GetMethod().DeclaringType.Assembly;
            if (assembly != executingAssembly)
            {
                return assembly;
            }
        }
        return null;
Sam Mackrill
  • 4,004
  • 8
  • 35
  • 55
5

No it's not possible to reliably understand who is calling you. Some people will undoubtedly suggest a stack walk but that is unreliable due to JIT inlining. There is just no way to reliably get the method / assembly which is calling your method.

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
1

Wrote this to get the AssemblyInfo.cs attributes for the calling assembly -- so uses the "GetAssembly(int stackTraceLevel)" method that does what is needed...

All this is also up on my blog at http://lancelarsen.com/reading-values-from-assemblyinfo-cs/

Enjoy

/// <summary>
/// Gets the values from the AssemblyInfo.cs file for the previous assembly
/// </summary>
/// <example>
/// AssemblyInfoCalling assembly = new AssemblyInfoCalling();
/// string company1 = assembly.Company;
/// string product1 = assembly.Product;
/// string copyright1 = assembly.Copyright;
/// string trademark1 = assembly.Trademark;
/// string title1 = assembly.Title;
/// string description1 = assembly.Description;
/// string configuration1 = assembly.Configuration;
/// string fileversion1 = assembly.FileVersion;
/// Version version1 = assembly.Version;
/// string versionFull1 = assembly.VersionFull;
/// string versionMajor1 = assembly.VersionMajor;
/// string versionMinor1 = assembly.VersionMinor;
/// string versionBuild1 = assembly.VersionBuild;
/// string versionRevision1 = assembly.VersionRevision;
/// </example>
public class AssemblyInfoCalling
{
    /// <summary>
    /// Initializes a new instance of the <see cref="AssemblyInfoCalling"/> class.
    /// </summary>
    /// <param name="traceLevel">The trace level needed to get correct assembly 
    /// - will need to adjust based on where you put these classes in your project(s).</param>
    public AssemblyInfoCalling(int traceLevel = 4)
    {
        //----------------------------------------------------------------------
        // Default to "3" as the number of levels back in the stack trace to get the 
        //  correct assembly for "calling" assembly
        //----------------------------------------------------------------------
        StackTraceLevel = traceLevel;
    }

    //----------------------------------------------------------------------
    // Standard assembly attributes
    //----------------------------------------------------------------------
    public string Company { get { return GetCallingAssemblyAttribute<AssemblyCompanyAttribute>(a => a.Company); } }
    public string Product { get { return GetCallingAssemblyAttribute<AssemblyProductAttribute>(a => a.Product); } }
    public string Copyright { get { return GetCallingAssemblyAttribute<AssemblyCopyrightAttribute>(a => a.Copyright); } }
    public string Trademark { get { return GetCallingAssemblyAttribute<AssemblyTrademarkAttribute>(a => a.Trademark); } }
    public string Title { get { return GetCallingAssemblyAttribute<AssemblyTitleAttribute>(a => a.Title); } }
    public string Description { get { return GetCallingAssemblyAttribute<AssemblyDescriptionAttribute>(a => a.Description); } }
    public string Configuration { get { return GetCallingAssemblyAttribute<AssemblyDescriptionAttribute>(a => a.Description); } }
    public string FileVersion { get { return GetCallingAssemblyAttribute<AssemblyFileVersionAttribute>(a => a.Version); } }

    //----------------------------------------------------------------------
    // Version attributes
    //----------------------------------------------------------------------
    public static Version Version
    {
        get
        {
            //----------------------------------------------------------------------
            // Get the assembly, return empty if null
            //----------------------------------------------------------------------
            Assembly assembly = GetAssembly(StackTraceLevel);
            return assembly == null ? new Version() : assembly.GetName().Version;
        }
    }
    public string VersionFull { get { return Version.ToString(); } }
    public string VersionMajor { get { return Version.Major.ToString(); } }
    public string VersionMinor { get { return Version.Minor.ToString(); } }
    public string VersionBuild { get { return Version.Build.ToString(); } }
    public string VersionRevision { get { return Version.Revision.ToString(); } }

    //----------------------------------------------------------------------
    // Set how deep in the stack trace we're looking - allows for customized changes
    //----------------------------------------------------------------------
    public static int StackTraceLevel { get; set; }

    //----------------------------------------------------------------------
    // Custom Attributes
    //----------------------------------------------------------------------
    public string Location 
    { 
        get
        {
            try
            {
                return GetCallingAssemblyAttribute<AssemblyLocationAttribute>(a => a.Value);
            }
            catch (NullReferenceException)
            {
                return string.Empty;
            }

        } 
    }

    /// <summary>
    /// Gets the calling assembly attribute.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="value">The value.</param>
    /// <example>return GetCallingAssemblyAttribute&lt;AssemblyCompanyAttribute&gt;(a => a.Company);</example>
    /// <returns></returns>
    private string GetCallingAssemblyAttribute<T>(Func<T, string> value) where T : Attribute
    {
        //----------------------------------------------------------------------
        // Get the assembly, return empty if null
        //----------------------------------------------------------------------
        Assembly assembly = GetAssembly(StackTraceLevel);
        if (assembly == null) return string.Empty;

        //----------------------------------------------------------------------
        // Get the attribute value
        //----------------------------------------------------------------------
        T attribute = (T) Attribute.GetCustomAttribute(assembly, typeof (T));
        return value.Invoke(attribute);
    }

    /// <summary>
    /// Go through the stack and gets the assembly
    /// </summary>
    /// <param name="stackTraceLevel">The stack trace level.</param>
    /// <returns></returns>
    private static Assembly GetAssembly(int stackTraceLevel)
    {
        //----------------------------------------------------------------------
        // Get the stack frame, returning null if none
        //----------------------------------------------------------------------
        StackTrace stackTrace = new StackTrace();
        StackFrame[] stackFrames = stackTrace.GetFrames();
        if (stackFrames == null) return null;

        //----------------------------------------------------------------------
        // Get the declaring type from the associated stack frame, returning null if nonw
        //----------------------------------------------------------------------
        var declaringType = stackFrames[stackTraceLevel].GetMethod().DeclaringType;
        if (declaringType == null) return null;

        //----------------------------------------------------------------------
        // Return the assembly
        //----------------------------------------------------------------------
        var assembly = declaringType.Assembly;
        return assembly;
    }
}
0

Came across this looking for something similar. In my case, it was Assembly.GetEntryAssembly().

Use Case

Program A runs CodeDomProvider.CompileAssemblyFromSource to embed resources and compile Program B. DLL in Program B then reads resources, but doesn't know the Assembly (which the above solves).

Tyler
  • 31
  • 2