Yesterday I spent a long time tracking down a problem with PDB symbol access. I figured I could spare some poor soul from going through the same pain as I did by documenting what I found out.
The synopsis: If you’re using DBGHELP to read PDB symbols, your code will likely break when reading PDBs created with Visual Studio 2005 (Whidbey) or later. To understand the problem, you need to know the relevant players and their versions:
- DBGHELP.DLL v5, which is in the System32 directory, and DBGHELP.DLL v6, a redistributable DLL most often obtained via a WinDBG install.
- The MSDIA code, the SDK of which comes with Visual Studio.
- The Microsoft PDB format, which usually changes from version to version of Visual Studio. In this case, PDBs created by Visual Studio 2005 are different than those created with Visual Studio 2003.
DBGHELP is a fantastic DLL for people writing debuggers and similar tools. It encapsulates a ton of knowledge about all sorts of things, including symbol files. Under the hood, more recent versions of DBGHELP.DLL know how to use the MSDIA interfaces to access PDB files.
Here’s my scenario: I was writing code that used DBGHELP for symbol access, and trying to read symbols from a VS 2005 created DLL. I noticed weird behavior in that various symbol routines returned success, but the data returned wasn’t correct. After much effort, I realized that my program was loading DBGHELP.DLL from the \WINDOWS\SYSTEM32 directory, and that this DLL is version 5.x, last updated about three years ago. Pay no attention to the file date on DBGHELP, the version info is what tells you what you’re playing with.
So what’s wrong with the v5 DBGHELP? It was released long before the VS 2005 PDB format arrived. As such, it has no idea how to use the latest MSDIA version to read the Visual Studio 2005 symbols. Luckily, v6 of DBGHELP (again, it’s redistributable and comes with WinDBG) knows about the newer MSDIA interface. Aha! There’s my answer (or so I thought.) I’ll just drop the v6 DBGHELP.DLL over my v5 version in my \WiNDOWS\SYSTEM32 directory.
Foiled! Windows System File Protection won’t let me just overwrite DBGHELP.DLL with a newer version. I even played the tricks with the DLLCACHE directory which used to work, to no avail. Ultimately, the solution that worked for me was to copy a v6 version of DBGHELP.DLL into the same directory as my application. This version takes precedence over the one in \WINDOWS\SYSTEM32.
Also, beware if you’re using IMAGEHLP at the same time, since it’s a “KnownDLL”, and it also exports many of the same routines as DBGHELP. In this case, no matter what I didn’t I couldn’t get IMAGEHLP & DBGHELP to load from anywhere other than \WINDOWS\SYSTEM32. To fix this, I had to specify DBGHELP.LIB *before* IMAGEHLP.LIB in the linker input line, thus forcing the linker to resolve against the DBGHELP version of the APIs, rather than the IMAGEHLP version, which really just pass through to DBGHELP.DLL.
I bounced this topic of DBGHELP v5 and VS 2005 symbols around with some internal stakeholders. The main response was: If you want correct behavior with the latest PDB files, the MSDIA interfaces are the way to go, rather than DBGHELP. You’ll still have to make sure you’re using the latest and greatest version of MSDIA, but at least your not trying to work through an older DBGHELP.DLL that’s not aware of the newer PDBs. Personally, I find the DBGHELP APIs simpler to use, but MSDIA is the official way of accessing PDBs these days, with DBGHELP support being just a nice side benefit if it works.