简体   繁体   中英

How to find out the project directory of an assembly?

To enable an elaborate debugging scenario, I need my assembly to figure out where the source files are at run-time.

The Assembly object doesn't know this information, and it probably shouldn't. The .pdb file obviously knows, but I have no idea how to parse it. So I thought maybe I can tag my assembly at build time with an attribute, something to the effect of:

[assembly: AssemblyMetadata("ProjectDirectory", $(ProjectDir))]

I can't use the current directory, since it's set by IIS to some temporary directory during debugging. Nor do I want to hard-code the directory.

The closest I've come so far in solving this, is using the Assembly.CodeBase property. It points to the directory where the IIS solution was built ( Solution/IISProject/bin/Debug/ , rather than /Solution/source/MyLibrary/ ), but not my project directory. A hacky solution is going up a few levels from there, then go down a few levels back to the project folder. I would very much like to avoid this hack if possible.

To enable these types of scenarios usually the best practice is to place these directories in a setting in your web.config. So you can say based on a given situation, use this directory instead of this directory. Often people use if (IsDebugging) use this dir, else use this dir.

After a lot of soul searching, I managed to collect enough inspiration to compose the following snippet. The idea is to use the .pdb file to discover the location of the source code. You need to reference ISymWrapper.dll and compile in 32 bits mode. If there's a simpler method around, I'd be glad to hear it.

using System;
using System.Diagnostics.SymbolStore;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Linq;
public static class AssemblyDirectoryInfo
{
    private const string
        ImporterId = "7DAC8207-D3AE-4c75-9B67-92801A497D44",
        DispenserId = "809c652e-7396-11d2-9771-00a0c9b4d50c",
        DispenserClassId = "e5cb7a31-7512-11d2-89ce-0080c792e5d8";

    [Guid(ImporterId), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), ComVisible(true)]
    public interface IMetaDataImport{}

    [Guid(DispenserId), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), ComVisible(true)]
    interface IMetaDataDispenser
    {
        void DefineScope();

        void OpenScope([In, MarshalAs(UnmanagedType.LPWStr)] String szScope, [In] Int32 dwOpenFlags,
                       [In] ref Guid riid, [Out, MarshalAs(UnmanagedType.IUnknown)] out Object punk);
    }

    [DllImport("ole32.dll")]
    private static extern int CoCreateInstance([In] ref Guid guid,
        [In, MarshalAs(UnmanagedType.IUnknown)] Object pUnkOuter,
        [In] uint dwClsContext,
        [In] ref Guid riid,
        [Out, MarshalAs(UnmanagedType.Interface)] out Object ppv);

    public static DirectoryInfo GetAssemblyCodeBase(Assembly assembly)
    {
        var file = new FileInfo(assembly.Location);

        Guid dispenserClassId = new Guid(DispenserClassId),
             importerIid = new Guid(ImporterId),
             dispenserIid = new Guid(DispenserId);

        object dispenser, importer;

        CoCreateInstance(ref dispenserClassId, null, 1, ref dispenserIid, out dispenser);
        ((IMetaDataDispenser)dispenser).OpenScope(file.FullName, 0, ref importerIid, out importer);

        var ptr = Marshal.GetComInterfaceForObject(importer, typeof(IMetaDataImport));
        var reader = new SymBinder().GetReader(ptr, file.FullName, file.DirectoryName);
        return reader.GetDocuments()
                     .Select(d => d.URL)
                     .Where(v => !string.IsNullOrEmpty(v))
                     .OrderBy(v => v.Length)
                     .Select(v => new FileInfo(v).Directory)
                     .First();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM