Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
567 views
in Technique[技术] by (71.8m points)

c# - Retrieve Local variable name from PDB files

i'm currently trying to retrieve source code from IL bytecodes and PDB files, i'm arrived to the point where i can generate source code from IL and reflection i know the name of local variable names is included in the pdb file. My question is how can i find it back ? what libs should i use to handle the pdb files (if any) or should i write the code myself ? where can i find information about pdb file format ? currently in the generated sourcecode i'm using auto generated values for local variables but i want to change that as i believe it is possible to find that information back if you have pdb files at your disposal. I tried to look on google but i didnt find any usefull informations.

Thanks in advance for you replies ;)

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Here's how to read local variable names from a MethodInfo using the types in System.Diagnostics.SymbolStore:

public class LocalVariableNameReader
{
    Dictionary<int, string> _names = new Dictionary<int, string> ();

    public string this [int index]
    {
        get
        {
            if (!_names.ContainsKey (index)) return null;
            return _names [index];
        }
    }

    public LocalVariableNameReader (MethodInfo m)
    {
        ISymbolReader symReader = SymUtil.GetSymbolReaderForFile (m.DeclaringType.Assembly.Location, null);
        ISymbolMethod met = symReader.GetMethod (new SymbolToken (m.MetadataToken));
        VisitLocals (met.RootScope);
    }

    void VisitLocals (ISymbolScope iSymbolScope)
    {
        foreach (var s in iSymbolScope.GetLocals ()) _names [s.AddressField1] = s.Name;
        foreach (var c in iSymbolScope.GetChildren ()) VisitLocals (c);
    }
}

The SymUtil class comes from this example.

Edit: The above link is broken. From a google cache:

A long time ago I was looking on how to process pdb files in order to get additional information that reflection simply could not provide. Now I stumped over a 5 year old post of Mike Stall (here) and suddenly all became clear. I’ve put together a small example which uses .net 4.0 to read the method body code as it is in the source code from where the assembly was compiled. Mind you that in order for this to work you have to have the pdb of the assembly available. Also please make sure you add a refference to ISymWrapper and your project is targeting .Net 4.0 framework, not .Net 4.0 Client.

using System;
using System.Diagnostics.SymbolStore;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;

namespace PdbTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Assembly ass = Assembly.GetExecutingAssembly();
            ISymbolReader symreader = SymUtil.GetSymbolReaderForFile(ass.Location, null);

            MethodInfo m = ass.GetType("PdbTest.TestClass").GetMethod("GetStringRepresentation");
            ISymbolMethod met = symreader.GetMethod(new SymbolToken(m.MetadataToken));

            int count = met.SequencePointCount;

            ISymbolDocument[] docs = new ISymbolDocument[count];
            int[] offsets = new int[count];
            int[] lines = new int[count];
            int[] columns = new int[count];
            int[] endlines = new int[count];
            int[] endcolumns = new int[count];

            met.GetSequencePoints(offsets, docs, lines, columns, endlines, endcolumns);

            StreamReader reader = new StreamReader(docs[0].URL);
            string[] linesOfCode = reader.ReadToEnd().Split('n');
            reader.Close();

            Console.WriteLine("The content of method PdbTest.TestClass.GetStringRepresentation");
            for (int i = lines[0]; i < endlines[count - 1] - 1; i++)
            {
                Console.WriteLine(linesOfCode[i]);
            }
        }
    }


    #region test class

    public enum MyEnum
    {
        Apples,
        Oranges
    }

    public partial class TestClass
    {
        public string GetStringRepresentation(MyEnum e)
        {
            MyEnum e2 = MyEnum.Apples;
            return e.ToString() + e2.ToString();
        }
    }

    #endregion test class

    #region Get a symbol reader for the given module

    // Encapsulate a set of helper classes to get a symbol reader from a file.
    // The symbol interfaces require an unmanaged metadata interface.
    static class SymUtil
    {
        static class NativeMethods
        {
            [DllImport("ole32.dll")]
            public static extern int CoCreateInstance(
                [In] ref Guid rclsid,
                [In, MarshalAs(UnmanagedType.IUnknown)] Object pUnkOuter,
                [In] uint dwClsContext,
                [In] ref Guid riid,
                [Out, MarshalAs(UnmanagedType.Interface)] out Object ppv);
        }

        // Wrapper.
        public static ISymbolReader GetSymbolReaderForFile(string pathModule, string searchPath)
        {
            return SymUtil.GetSymbolReaderForFile(
                new System.Diagnostics.SymbolStore.SymBinder(), pathModule, searchPath);
        }

        // We demand Unmanaged code permissions because we're reading from the file 
        // system and calling out to the Symbol Reader
        // @TODO - make this more specific.
        [System.Security.Permissions.SecurityPermission(
            System.Security.Permissions.SecurityAction.Demand,
            Flags = System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)]
        public static ISymbolReader GetSymbolReaderForFile(
           System.Diagnostics.SymbolStore.SymBinder binder, string pathModule, string searchPath)
        {
            // Guids for imported metadata interfaces.
            Guid dispenserClassID = new Guid(0xe5cb7a31, 0x7512, 0x11d2, 0x89, 
                0xce, 0x00, 0x80, 0xc7, 0x92, 0xe5, 0xd8); // CLSID_CorMetaDataDispenser
            Guid dispenserIID = new Guid(0x809c652e, 0x7396, 0x11d2, 0x97, 0x71, 
                0x00, 0xa0, 0xc9, 0xb4, 0xd5, 0x0c); // IID_IMetaDataDispenser
            Guid importerIID = new Guid(0x7dac8207, 0xd3ae, 0x4c75, 0x9b, 0x67, 
                0x92, 0x80, 0x1a, 0x49, 0x7d, 0x44); // IID_IMetaDataImport

            // First create the Metadata dispenser.
            object objDispenser;
            NativeMethods.CoCreateInstance(ref dispenserClassID, null, 1, 
                ref dispenserIID, out objDispenser);

            // Now open an Importer on the given filename. We'll end up passing this importer 
            // straight through to the Binder.
            object objImporter;
            IMetaDataDispenser dispenser = (IMetaDataDispenser)objDispenser;
            dispenser.OpenScope(pathModule, 0, ref importerIID, out objImporter);

            IntPtr importerPtr = IntPtr.Zero;
            ISymbolReader reader;
            try
            {
                // This will manually AddRef the underlying object, so we need to 
                // be very careful to Release it.
                importerPtr = Marshal.GetComInterfaceForObject(objImporter, 
                    typeof(IMetadataImport));

                reader = binder.GetReader(importerPtr, pathModule, searchPath);
            }
            finally
            {
                if (importerPtr != IntPtr.Zero)
                {
                    Marshal.Release(importerPtr);
                }
            }
            return reader;
        }
    }
    #region Metadata Imports

    // We can use reflection-only load context to use reflection to query for 
    // metadata information rather
    // than painfully import the com-classic metadata interfaces.
    [Guid("809c652e-7396-11d2-9771-00a0c9b4d50c"), 
        InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [ComVisible(true)]
    interface IMetaDataDispenser
    {
        // We need to be able to call OpenScope, which is the 2nd vtable slot.
        // Thus we need this one placeholder here to occupy the first slot..
        void DefineScope_Placeholder();

        //STDMETHOD(OpenScope)(                   // Return code.
        //LPCWSTR     szScope,                // [in] The scope to open.
        //  DWORD       dwOpenFlags,            // [in] Open mode flags.
        //  REFIID      riid,                   // [in] The interface desired.
        //  IUnknown    **ppIUnk) PURE;         // [out] Return interface on success.
        void OpenScope([In, MarshalAs(UnmanagedType.LPWStr)] String szScope, 
            [In] Int32 dwOpenFlags, [In] ref Guid riid, 
            [Out, MarshalAs(UnmanagedType.IUnknown)] out Object punk);

        // Don't need any other methods.
    }

    // Since we're just blindly passing this interface through managed code to the Symbinder, 
    // we don't care about actually importing the specific methods.
    // This needs to be public so that we can call Marshal.GetComInterfaceForObject() on 
    // it to get the underlying metadata pointer.
    [Guid("7DAC8207-D3AE-4c75-9B67-92801A497D44"), 
        InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [ComVisible(true)]
    [CLSCompliant(true)]
    public interface IMetadataImport
    {
        // Just need a single placeholder method so that it doesn't complain
        // about an empty interface.
        void Placeholder();
    }
    #endregion

    #endregion Get a symbol reader for the given module
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...