Reputation: 453
I have a symbol cache directory set to D:\symbols
in Visual Studio options:
Within this directory Visual Studio creates a hierarchy with top-level directories matching PDB file names (e.g. ole32.pdb
), on the next level are one or more directories like D0C3BDDD4ADD4E87B2B5E803303B8D772
(looking like 33-digit hexadecimal numbers) and inside them are PDB files themselves, presumably, downloaded from Microsoft Symbol Servers.
I suppose that these hexadecimal numbers represent versions of PDB files. I wonder, whether these numbers have any structure or meaning, and how they can be extracted from PDB files (ideally, using C#)?
Given a PDB file in some other folder, is it possible to find a directory in the symbol cache where Visual Studio debugger would look for it?
Upvotes: 14
Views: 699
Reputation: 9407
This value is a GUID that's embedded in both the assembly and the symbol file so they can be synced up.
https://www.atmosera.com/blog/pdb-files-what-every-developer-must-know/
You can run dumpbin /headers
on your assembly to see the embedded GUID.
Upvotes: 4
Reputation: 11932
The first 32 digits is just a GUID that is baked both into PE file (DLL, EXE, ...) and a corresponding PDB, next digits are so-called age in hexadecimal representation without leading zeros (it might be incremented during a build process by linking, signing, etc). In most cases an age fits into a single hex digit, hence 33 digits in total, sometimes called signature. You can extract a signature from a PDB file using the Debug Interface Access SDK. An example in C#:
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
static class Program
{
// Pass a PDB file name as a command-line parameter
static void Main(string[] args)
{
var pdbFile = args.FirstOrDefault();
if (!File.Exists(pdbFile))
return;
try
{
var dataSource = (IDiaDataSource)Activator.CreateInstance(Marshal.GetTypeFromCLSID(new Guid("83AB22C8-993A-4D14-A0E0-37BC0AAEA793")));
dataSource.LoadDataFromPdb(pdbFile);
IDiaSession session;
dataSource.OpenSession(out session);
var globalScope = session.GlobalScope;
Console.WriteLine(globalScope.Guid.ToString("N").ToUpperInvariant() + globalScope.Age.ToString("X"));
}
catch (COMException) { } // May happen for corrupted PDB files
}
}
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("79F1BB5F-B66E-48E5-B6A9-1545C323CA3D")]
interface IDiaDataSource
{
void _VtblGap_1();
void LoadDataFromPdb(string pdbFile);
void _VtblGap_3();
void OpenSession(out IDiaSession session);
}
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("6FC5D63F-011E-40C2-8DD2-E6486E9D6B68")]
interface IDiaSession
{
void _VtblGap_2();
IDiaSymbol GlobalScope { get; }
}
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("CB787B2F-BD6C-4635-BA52-933126BD2DCD")]
interface IDiaSymbol
{
void _VtblGap_43();
Guid Guid { get; }
void _VtblGap_28();
uint Age { get; }
}
Upvotes: 9