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>