What's in a PDB file? Use the Debug Interface Access SDK

It’s easy to use C# code and MSDia140.dll from the Debug Interface Access SDK to examine what’s inside a PDB.

A PDB is Program Database which is generated when an executable such as an EXE or DLL is built. It includes a lot of information about the file that is very useful for a debugger. This include names and addresses of symbols.

Managed code PDB contents are somewhat different from native code: a lot of the managed code information can be obtained from other sources. For example, the Type of a symbol can be obtained from the Metadata of the binary.

Below is some sample code that uses the DIA SDK to read a PDB and display its contents.

See also

Write your own Linq query viewer

Use DataTemplates and WPF in code to create a general purpose LINQ Query results display

<code>

 using Dia2Lib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
// File->New->Project->C# Windows WPF Application.
// Replace MainWindow.Xaml.cs with this content
// add a reference to c:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Packages\Debugger\msdia140.dll
namespace WpfApplication1
{
  public partial class MainWindow : Window
  {
    class SymbolInfo
    {
      public int Level { get; set; } //recursion level
      public string SymbolName { get; set; }
      public uint LocationType { get; set; }
      public ulong Length { get; set; }
      public uint AddressOffset { get; set; }
      public uint RelativeAddress { get; set; }
      public string SourceFileName { get; set; }
      public uint SourceLineNo { get; set; }
      public SymTagEnum SymTag { get; set; }
      public string SymbolType { get; set; }
      public override string ToString()
      {
        return $"{SymbolName} {SourceFileName}({SourceLineNo})  {SymbolType}";
      }
    }
    public MainWindow()
    {
      InitializeComponent();
      this.Loaded += (ol, el) =>
        {
          try
          {
            this.WindowState = WindowState.Maximized;
            var pdbName = System.IO.Path.ChangeExtension(
                      Assembly.GetExecutingAssembly().Location, "pdb");
            this.Title = pdbName;
            var lstSymInfo = new List<SymbolInfo>();
            using (var diaUtil = new DiaUtil(pdbName))
            {
              Action<IDiaEnumSymbols, int> lamEnum = null; // recursive lambda
                    lamEnum = (enumSym, lvl) =>
                    {
                      if (enumSym != null)
                      {
                        foreach (IDiaSymbol sym in enumSym)
                        {
                          var symbolInfo = new SymbolInfo()
                          {
                            Level = lvl,
                            SymbolName = sym.name,
                            Length = sym.length,
                            LocationType = sym.locationType,
                            SymTag = (SymTagEnum)sym.symTag,
                            AddressOffset = sym.addressOffset,
                            RelativeAddress = sym.relativeVirtualAddress
                          };
                          var symType = sym.type;
                          if (symType != null)
                          {
                            var symtypename = symType.name;
                            symbolInfo.SymbolType = symtypename;
                          }
                          lstSymInfo.Add(symbolInfo);
                          if (sym.addressOffset > 0 && sym.addressSection > 0 && sym.length > 0)
                          {
                            try
                            {
                              IDiaEnumLineNumbers enumLineNums;
                              diaUtil._IDiaSession.findLinesByAddr(
                                        sym.addressSection,
                                        sym.addressOffset,
                                        (uint)sym.length,
                                        out enumLineNums
                                        );
                              if (enumLineNums != null)
                              {
                                foreach (IDiaLineNumber line in enumLineNums)
                                {
                                  var linenumber = line.lineNumber;
                                  symbolInfo.SourceFileName = line.sourceFile.fileName;
                                  symbolInfo.SourceLineNo = line.lineNumber;
                                  break;
                                }
                              }
                            }
                            catch (Exception)
                            {
                            }
                          }
                          switch (symbolInfo.SymTag)
                          {
                            case SymTagEnum.SymTagFunction:
                            case SymTagEnum.SymTagBlock:
                            case SymTagEnum.SymTagCompiland:
                              IDiaEnumSymbols enumChildren;
                              sym.findChildren(SymTagEnum.SymTagNull, name: null, compareFlags: 0, ppResult: out enumChildren);
                              lamEnum.Invoke(enumChildren, lvl + 1);
                              break;
                          }
                        }
                      }
                    };
                    /* query by table of symbols
                    IDiaEnumTables enumTables;
                    diaUtil._IDiaSession.getEnumTables(out enumTables);
                    foreach (IDiaTable tabl in enumTables)
                    {
                        var tblName = tabl.name;
                        if (tblName == "Symbols")
                        {
                            IDiaEnumSymbols enumSyms = tabl as IDiaEnumSymbols;
                            lamEnum.Invoke(enumSyms, 0);
                        }
                    }
                    /*/ // query by global scope
                    var globalScope = diaUtil._IDiaSession.globalScope;
              IDiaEnumSymbols enumSymGlobal;
              globalScope.findChildrenEx(SymTagEnum.SymTagNull, name: null, compareFlags: 0, ppResult: out enumSymGlobal);
              lamEnum.Invoke(enumSymGlobal, 0);
                    //*/
                  }
            var gridvw = new GridView();
            foreach (var mem in typeof(SymbolInfo).GetMembers().
                        Where(m => m.MemberType == MemberTypes.Property)
                  )
            {
              var gridCol = new GridViewColumn();
              gridvw.Columns.Add(gridCol);
              gridCol.Header = new GridViewColumnHeader()
              {
                Content = mem.Name
              };
              var template = new DataTemplate(typeof(SymbolInfo));
              var factTblk = new FrameworkElementFactory(typeof(TextBlock));
              factTblk.SetBinding(TextBlock.TextProperty, new Binding(mem.Name));
                    // for wide columns let's set the tooltip too
                    factTblk.SetBinding(TextBlock.ToolTipProperty, new Binding(mem.Name));
              factTblk.SetValue(TextBlock.MaxWidthProperty, 300.0);
              var factSP = new FrameworkElementFactory(typeof(StackPanel));
              factSP.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);
              factSP.AppendChild(factTblk);
              template.VisualTree = factSP;
              gridCol.CellTemplate = template;
            }

            var lv = new ListView()
            {
              ItemsSource = lstSymInfo,
              View = gridvw
            };
            lv.DataContext = lstSymInfo;
            this.Content = lv;

          }
          catch (Exception ex)
          {
            this.Content = ex.ToString();
          }
        };
    }
  }
  public class DiaUtil : IDisposable
  {
    public IDiaDataSource _IDiaDataSource;
    public IDiaSession _IDiaSession;
    public DiaUtil(string pdbName)
    {
      _IDiaDataSource = new DiaSource();
      _IDiaDataSource.loadDataFromPdb(pdbName);
      _IDiaDataSource.openSession(out _IDiaSession);
    }

    public void Dispose()
    {
      Marshal.ReleaseComObject(_IDiaSession);
      Marshal.ReleaseComObject(_IDiaDataSource);
    }
  }
}

</code>