使用VS API开发一个PDB Dumper并且可以在没装VS2010的计算机上运行
GacUI到了撰写文档的时候了。虽然GacUI本身的功能还没有全部完成,但是发布一个alpha版还是可以的。因此GacUI需要一份文档。自从.net语言支持XML注释生成文档之后,Visual Studio的本地C++也支持使用XML注释了。只要打开了[工程属性 -> C/C++ -> Output Files -> Generate XML Documentation Files]之后,Visual Studio会在编译本地C++工程之后,将所有的XML注释收集起来,放在和可执行文件同一个目录下的<ProjectName.xml>里面。然后我就尝试bing了一下有没有从C++的XML文档生成可读文档的工具,结果发现只有.net才支持。
后来我稍微研究了一下(详细内容将会在下一篇博客透露),发现之所以没人写这个工具,是因为只有.net的可执行文件才包含足够多的元数据,而且这些元数据是必须的,否则无法生成一个完整的文档。举个例子,虽然<ProjectName.xml>包含了xml注释和该注释所在的符号,但是却没有包含该符号的结构信息。结果你试图生成一个函数的文档的时候,发现你获取不到它的返回类型!不过这也是情有可原的,因为本地C++程序根本就没有元数据。
由此我联想到了之前写程序读pdb的时候的一些内容,我想到pdb生成的那份xml显然是可以当成元数据的。而且我找到了一个方法,让你在使用Visual Studio2010的PDB API msdia100.dll的时候,可以不需要安装Visual Studio 2010了。下面就来介绍PDB Dumper的代码。
首先是main函数。main函数做的工作跟之前的这篇博客http://www.zzzyk.com/kf/201203/122573.html 说的一样,当然还是要创建一个IDiaSymbol的COM对象。一般来说,COM对象是需要被注册到windows里面(基本上都在注册表里)才能使用CoCreateInstance来创建。但是后来我发现msdia100.dll十分良心,还提供了一个NoRegCoCreate函数,可以在你只有msdia100.dll但却没有注册它的COM对象的情况下创建该对象: #include <Windows.h>
#include <iostream>
#include <string>
#include "dia2.h"
#include "diacreate.h"
#pragma comment(lib, "diaguids.lib")
namespace dumppdb
{
extern void DumpPdbToXml(IDiaSymbol* exeSymbol, const wchar_t* xml);
}
IDiaSymbol* CreateDiaSymbol(const wchar_t* pdbPath)
{
IDiaDataSource* pSource=0;
IDiaSession* pSession=0;
IDiaSymbol* pSymbol=0;
CoInitialize(NULL);
//HRESULT hr = CoCreateInstance(
// CLSID_DiaSource,
// NULL,
// CLSCTX_INPROC_SERVER,
// IID_IDiaDataSource,
// (void**) &pSource
// );
HRESULT hr = NoRegCoCreate(
L"msdia100.dll",
CLSID_DiaSource,
IID_IDiaDataSource,
(void**) &pSource
);
if(SUCCEEDED(hr))
if(SUCCEEDED(pSource->loadDataFromPdb(pdbPath)))
if(SUCCEEDED(pSource->openSession(&pSession)))
if(SUCCEEDED(pSession->get_globalScope(&pSymbol)))
{
return pSymbol;
}
return 0;
}
int wmain(int argc, wchar_t* argv[])
{
if(argc==3)
{
std::wcout<<L"importing "<<argv[1]<<std::endl;
IDiaSymbol* exeSymbol=CreateDiaSymbol(argv[1]);
if(exeSymbol)
{
std::wcout<<L"exporting "<<argv[2]<<std::endl;
dumppdb::DumpPdbToXml(exeSymbol, argv[2]);
std::wcout<<L"exported "<<argv[2]<<std::endl;
}
else
{
std::wcout<<L"Failed to read pdb("<<argv[1]<<L")"<<std::endl;
}
}
else
{
std::wcout<<L"Pdb2Xml.exe <pdb-path> <xml-path>"<<std::endl;
}
return 0;
}
这里的dia2.h、diacreate.h、diaguids.lib和msdia100.dll都可以在C:\Program Files (x86)\Microsoft Visual Studio 10.0\DIA SDK下找到。我们需要做的就是将这些文件都复制到我们的工程目录下面。至于如何读取IDiaSymbol的内容,各位就自己查MSDN了。下面贴出我使用IDiaSymbol将PDB的关键内容输出成xml的函数,也就是上面的代码提到的DumpPdbToXml函数了:
#include "Dia2.h"
#include "..\..\..\..\..\Library\Stream\Accessor.h"
#include "..\..\..\..\..\Library\Stream\CharFormat.h"
#include "..\..\..\..\..\Library\Stream\FileStream.h"
#include "..\..\..\..\..\Library\Stream\CacheStream.h"
#include "..\..\..\..\..\Library\Collections\Dictionary.h"
using namespace vl;
using namespace vl::collections;
using namespace vl::stream;
namespace dumppdb
{
//--------------------------------------------------------------------
void PrintString(TextWriter& file, const wchar_t* text, int len=-1)
{
if(len==-1) len=(int)wcslen(text);
file.WriteString(text, len);
}
void PrintSpaces(TextWriter& file, int level)
{
for(int i=0;i<level;i++) PrintString(file, L" ");
}
void PrintEscapedName(TextWriter& file, const wchar_t* name)
{
const wchar_t* head=name;
const wchar_t* reading=head;
while(*reading)
{
switch(*reading)
{
case L'<':
PrintString(file, head, reading-head);
PrintString(file, L"<");
head=reading+1;
reading=head;
break;
case L'>':
补充:软件开发 , C++ ,