Jump to content
Tuts 4 You

[.NET SNIPPET] How to read PDB files


Recommended Posts

How to read PDB files

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 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 *** = Assembly.GetExecutingAssembly();
ISymbolReader symreader = SymUtil.GetSymbolReaderForFile(***.Location, null); MethodInfo m = ***.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++)
} #region test class public enum MyEnum
} 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
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.
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;
// 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);
if (importerPtr != IntPtr.Zero)
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.
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.
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

Thanks Mike.

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Create New...