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>
Comments
- Anonymous
November 14, 2016
Hi Calvin Hsia,"add a reference to c:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Packages\Debugger\msdia140.dll" you mentioned above is something wrong on my machine. The msdia140.dll is built by vctoolsrel, and it can not be referenced directly through add reference dialogue on wpf appliction. Please show more detail, thanks!