mirror of
https://github.com/wbenny/pdbex
synced 2026-06-08 18:13:37 +00:00
be7a72fe8f
* [ADD] direct export call msdia140.dll * [MDF] Copy msdia140.dll cmd * [ADD] Support binary file path
696 lines
16 KiB
C++
696 lines
16 KiB
C++
#include "PDBExtractor.h"
|
|
#include "PDBHeaderReconstructor.h"
|
|
#include "PDBSymbolVisitor.h"
|
|
#include "PDBSymbolSorter.h"
|
|
#include "PDBSymbolSorterAlphabetical.h"
|
|
#include "UdtFieldDefinition.h"
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <stdexcept>
|
|
|
|
namespace
|
|
{
|
|
//
|
|
// Headers & footers for test file and reconstructed header.
|
|
//
|
|
|
|
static const char TEST_FILE_HEADER[] =
|
|
"#include <stdio.h>\n"
|
|
"#include <stddef.h>\n"
|
|
"#include <stdint.h>\n"
|
|
"\n"
|
|
"typedef long HRESULT;\n"
|
|
"\n"
|
|
"#include \"%s\"\n"
|
|
"\n"
|
|
"int main()\n"
|
|
"{\n";
|
|
|
|
static const char TEST_FILE_FOOTER[] =
|
|
"\n"
|
|
"\treturn 0;\n"
|
|
"}\n"
|
|
"\n";
|
|
|
|
static const char HEADER_FILE_HEADER[] =
|
|
"/*\n"
|
|
" * PDB file: %s\n"
|
|
" * Image architecture: %s (0x%04x)\n"
|
|
" *\n"
|
|
" * Dumped by pdbex tool v" PDBEX_VERSION_STRING ", by wbenny\n"
|
|
" */\n\n";
|
|
|
|
static const char DEFINITIONS_PRAGMA_PACK_BEGIN[] =
|
|
"#include <pshpack1.h>";
|
|
|
|
static const char DEFINITIONS_PRAGMA_PACK_END[] =
|
|
"#include <poppack.h>";
|
|
|
|
//
|
|
// Error messages.
|
|
//
|
|
|
|
static const char* MESSAGE_INVALID_PARAMETERS =
|
|
"Invalid parameters";
|
|
|
|
static const char* MESSAGE_FILE_NOT_FOUND =
|
|
"File not found";
|
|
|
|
static const char* MESSAGE_SYMBOL_NOT_FOUND =
|
|
"Symbol not found";
|
|
|
|
//
|
|
// Our exception class.
|
|
//
|
|
|
|
class PDBDumperException
|
|
: public std::runtime_error
|
|
{
|
|
public:
|
|
PDBDumperException(const char* Message)
|
|
: std::runtime_error(Message)
|
|
{
|
|
|
|
}
|
|
};
|
|
}
|
|
|
|
int
|
|
PDBExtractor::Run(
|
|
int argc,
|
|
char** argv
|
|
)
|
|
{
|
|
int Result = ERROR_SUCCESS;
|
|
|
|
try
|
|
{
|
|
ParseParameters(argc, argv);
|
|
OpenPDBFile();
|
|
|
|
PrintTestHeader();
|
|
|
|
if (m_Settings.SymbolName == "*")
|
|
{
|
|
DumpAllSymbols();
|
|
}
|
|
else if (m_Settings.SymbolName == "%")
|
|
{
|
|
DumpAllSymbolsOneByOne();
|
|
}
|
|
else
|
|
{
|
|
DumpOneSymbol();
|
|
}
|
|
|
|
PrintTestFooter();
|
|
}
|
|
catch (const PDBDumperException& e)
|
|
{
|
|
std::cerr << e.what() << std::endl;
|
|
Result = EXIT_FAILURE;
|
|
}
|
|
|
|
CloseOpenedFiles();
|
|
|
|
return Result;
|
|
}
|
|
|
|
void
|
|
PDBExtractor::PrintUsage()
|
|
{
|
|
printf("Extracts types and structures from PDB (Program database).\n");
|
|
printf("Version v%s\n", PDBEX_VERSION_STRING);
|
|
printf("\n");
|
|
printf("pdbex <symbol> <path> [-o <filename>] [-t <filename>] [-e <type>]\n");
|
|
printf(" [-u <prefix>] [-s prefix] [-r prefix] [-g suffix]\n");
|
|
printf(" [-p] [-x] [-m] [-b] [-d] [-i] [-l]\n");
|
|
printf("\n");
|
|
printf("<symbol> Symbol name to extract\n");
|
|
printf(" Use '*' if all symbols should be extracted.\n");
|
|
printf(" Use '%%' if all symbols should be extracted separately.\n");
|
|
printf("<path> Path to the PDB file.\n");
|
|
printf(" -o filename Specifies the output file. (stdout)\n");
|
|
printf(" -t filename Specifies the output test file. (off)\n");
|
|
printf(" -e [n,i,a] Specifies expansion of nested structures/unions. (i)\n");
|
|
printf(" n = none Only top-most type is printed.\n");
|
|
printf(" i = inline unnamed Unnamed types are nested.\n");
|
|
printf(" a = inline all All types are nested.\n");
|
|
printf(" -u prefix Unnamed union prefix (in combination with -d).\n");
|
|
printf(" -s prefix Unnamed struct prefix (in combination with -d).\n");
|
|
printf(" -r prefix Prefix for all symbols.\n");
|
|
printf(" -g suffix Suffix for all symbols.\n");
|
|
printf("\n");
|
|
printf("Following options can be explicitly turned off by adding trailing '-'.\n");
|
|
printf("Example: -p-\n");
|
|
printf(" -p Create padding members. (T)\n");
|
|
printf(" -x Show offsets. (T)\n");
|
|
printf(" -m Create Microsoft typedefs. (T)\n");
|
|
printf(" -b Allow bitfields in union. (F)\n");
|
|
printf(" -d Allow unnamed data types. (T)\n");
|
|
printf(" -i Use types from stdint.h instead of native types. (F)\n");
|
|
printf(" -j Print definitions of referenced types. (T)\n");
|
|
printf(" -k Print header. (T)\n");
|
|
printf(" -n Print declarations. (T)\n");
|
|
printf(" -l Print definitions. (T)\n");
|
|
printf(" -f Print functions. (F)\n");
|
|
printf(" -z Print #pragma pack directives. (T)\n");
|
|
printf(" -y Sort declarations and definitions. (F)\n");
|
|
printf("\n");
|
|
}
|
|
|
|
void
|
|
PDBExtractor::ParseParameters(
|
|
int argc,
|
|
char** argv
|
|
)
|
|
{
|
|
//
|
|
// Early check for help parameter.
|
|
//
|
|
|
|
if ( argc == 1 ||
|
|
(argc == 2 && strcmp(argv[1], "-h") == 0) ||
|
|
(argc == 2 && strcmp(argv[1], "--help") == 0))
|
|
{
|
|
PrintUsage();
|
|
|
|
//
|
|
// Kitten died when I wrote this.
|
|
//
|
|
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
int ArgumentPointer = 0;
|
|
|
|
m_Settings.SymbolName = argv[++ArgumentPointer];
|
|
m_Settings.PdbPath = argv[++ArgumentPointer];
|
|
|
|
while (++ArgumentPointer < argc)
|
|
{
|
|
const char* CurrentArgument = argv[ArgumentPointer];
|
|
size_t CurrentArgumentLength = strlen(CurrentArgument);
|
|
|
|
const char* NextArgument = ArgumentPointer < argc
|
|
? argv[ArgumentPointer + 1]
|
|
: nullptr;
|
|
|
|
size_t NextArgumentLength = NextArgument
|
|
? strlen(CurrentArgument)
|
|
: 0;
|
|
|
|
//
|
|
// Handling of -X- switches.
|
|
//
|
|
|
|
if ((CurrentArgumentLength != 2 && CurrentArgumentLength != 3) ||
|
|
(CurrentArgumentLength == 2 && CurrentArgument[0] != '-') ||
|
|
(CurrentArgumentLength == 3 && CurrentArgument[0] != '-' && CurrentArgument[2] != '-'))
|
|
{
|
|
throw PDBDumperException(MESSAGE_INVALID_PARAMETERS);
|
|
}
|
|
|
|
bool OffSwitch = CurrentArgumentLength == 3 && CurrentArgument[2] == '-';
|
|
|
|
//
|
|
// Handling of options.
|
|
//
|
|
|
|
switch (CurrentArgument[1])
|
|
{
|
|
case 'o':
|
|
if (!NextArgument)
|
|
{
|
|
throw PDBDumperException(MESSAGE_INVALID_PARAMETERS);
|
|
}
|
|
|
|
++ArgumentPointer;
|
|
m_Settings.OutputFilename = NextArgument;
|
|
|
|
if (m_Settings.SymbolName != "%")
|
|
{
|
|
m_Settings.PdbHeaderReconstructorSettings.OutputFile = new std::ofstream(
|
|
NextArgument,
|
|
std::ios::out
|
|
);
|
|
}
|
|
else
|
|
{
|
|
m_Settings.PdbHeaderReconstructorSettings.OutputFile = nullptr;
|
|
}
|
|
break;
|
|
|
|
case 't':
|
|
if (!NextArgument)
|
|
{
|
|
throw PDBDumperException(MESSAGE_INVALID_PARAMETERS);
|
|
}
|
|
|
|
++ArgumentPointer;
|
|
m_Settings.TestFilename = NextArgument;
|
|
m_Settings.PdbHeaderReconstructorSettings.TestFile = new std::ofstream(
|
|
m_Settings.TestFilename,
|
|
std::ios::out
|
|
);
|
|
|
|
break;
|
|
|
|
case 'e':
|
|
if (!NextArgument)
|
|
{
|
|
throw PDBDumperException(MESSAGE_INVALID_PARAMETERS);
|
|
}
|
|
|
|
++ArgumentPointer;
|
|
switch (NextArgument[0])
|
|
{
|
|
case 'n':
|
|
m_Settings.PdbHeaderReconstructorSettings.MemberStructExpansion =
|
|
PDBHeaderReconstructor::MemberStructExpansionType::None;
|
|
break;
|
|
|
|
case 'i':
|
|
m_Settings.PdbHeaderReconstructorSettings.MemberStructExpansion =
|
|
PDBHeaderReconstructor::MemberStructExpansionType::InlineUnnamed;
|
|
break;
|
|
|
|
case 'a':
|
|
m_Settings.PdbHeaderReconstructorSettings.MemberStructExpansion =
|
|
PDBHeaderReconstructor::MemberStructExpansionType::InlineAll;
|
|
break;
|
|
|
|
default:
|
|
m_Settings.PdbHeaderReconstructorSettings.MemberStructExpansion =
|
|
PDBHeaderReconstructor::MemberStructExpansionType::InlineUnnamed;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 'u':
|
|
if (!NextArgument)
|
|
{
|
|
throw PDBDumperException(MESSAGE_INVALID_PARAMETERS);
|
|
}
|
|
|
|
++ArgumentPointer;
|
|
m_Settings.PdbHeaderReconstructorSettings.AnonymousUnionPrefix = NextArgument;
|
|
break;
|
|
|
|
case 's':
|
|
if (!NextArgument)
|
|
{
|
|
throw PDBDumperException(MESSAGE_INVALID_PARAMETERS);
|
|
}
|
|
|
|
++ArgumentPointer;
|
|
m_Settings.PdbHeaderReconstructorSettings.AnonymousStructPrefix = NextArgument;
|
|
break;
|
|
|
|
case 'r':
|
|
if (!NextArgument)
|
|
{
|
|
throw PDBDumperException(MESSAGE_INVALID_PARAMETERS);
|
|
}
|
|
|
|
++ArgumentPointer;
|
|
m_Settings.PdbHeaderReconstructorSettings.SymbolPrefix = NextArgument;
|
|
break;
|
|
|
|
case 'g':
|
|
if (!NextArgument)
|
|
{
|
|
throw PDBDumperException(MESSAGE_INVALID_PARAMETERS);
|
|
}
|
|
|
|
++ArgumentPointer;
|
|
m_Settings.PdbHeaderReconstructorSettings.SymbolSuffix = NextArgument;
|
|
break;
|
|
|
|
case 'p':
|
|
m_Settings.PdbHeaderReconstructorSettings.CreatePaddingMembers = !OffSwitch;
|
|
break;
|
|
|
|
case 'x':
|
|
m_Settings.PdbHeaderReconstructorSettings.ShowOffsets = !OffSwitch;
|
|
break;
|
|
|
|
case 'm':
|
|
m_Settings.PdbHeaderReconstructorSettings.MicrosoftTypedefs = !OffSwitch;
|
|
break;
|
|
|
|
case 'b':
|
|
m_Settings.PdbHeaderReconstructorSettings.AllowBitFieldsInUnion = !OffSwitch;
|
|
break;
|
|
|
|
case 'd':
|
|
m_Settings.PdbHeaderReconstructorSettings.AllowAnonymousDataTypes = !OffSwitch;
|
|
break;
|
|
|
|
case 'i':
|
|
m_Settings.UdtFieldDefinitionSettings.UseStdInt = !OffSwitch;
|
|
break;
|
|
|
|
case 'j':
|
|
m_Settings.PrintReferencedTypes = !OffSwitch;
|
|
break;
|
|
|
|
case 'k':
|
|
m_Settings.PrintHeader = !OffSwitch;
|
|
break;
|
|
|
|
case 'n':
|
|
m_Settings.PrintDeclarations = !OffSwitch;
|
|
break;
|
|
|
|
case 'l':
|
|
m_Settings.PrintDefinitions = !OffSwitch;
|
|
break;
|
|
|
|
case 'f':
|
|
m_Settings.PrintFunctions = !OffSwitch;
|
|
break;
|
|
|
|
case 'z':
|
|
m_Settings.PrintPragmaPack = !OffSwitch;
|
|
break;
|
|
|
|
case 'y':
|
|
m_Settings.Sort = !OffSwitch;
|
|
break;
|
|
|
|
default:
|
|
throw PDBDumperException(MESSAGE_INVALID_PARAMETERS);
|
|
}
|
|
}
|
|
|
|
m_HeaderReconstructor = std::make_unique<PDBHeaderReconstructor>(
|
|
&m_Settings.PdbHeaderReconstructorSettings
|
|
);
|
|
|
|
m_SymbolVisitor = std::make_unique<PDBSymbolVisitor<UdtFieldDefinition>>(
|
|
m_HeaderReconstructor.get(),
|
|
&m_Settings.UdtFieldDefinitionSettings
|
|
);
|
|
|
|
if (m_Settings.Sort)
|
|
{
|
|
m_SymbolSorter = std::make_unique<PDBSymbolSorterAlphabetical>();
|
|
}
|
|
else
|
|
{
|
|
m_SymbolSorter = std::make_unique<PDBSymbolSorter>();
|
|
}
|
|
}
|
|
|
|
void
|
|
PDBExtractor::OpenPDBFile()
|
|
{
|
|
if (m_PDB.Open(m_Settings.PdbPath.c_str()) == FALSE)
|
|
{
|
|
throw PDBDumperException(MESSAGE_FILE_NOT_FOUND);
|
|
}
|
|
}
|
|
|
|
void
|
|
PDBExtractor::PrintTestHeader()
|
|
{
|
|
if (m_Settings.PdbHeaderReconstructorSettings.TestFile != nullptr)
|
|
{
|
|
static char TEST_FILE_HEADER_FORMATTED[16 * 1024];
|
|
sprintf_s(
|
|
TEST_FILE_HEADER_FORMATTED, TEST_FILE_HEADER,
|
|
m_Settings.OutputFilename
|
|
);
|
|
|
|
(*m_Settings.PdbHeaderReconstructorSettings.TestFile) << TEST_FILE_HEADER_FORMATTED;
|
|
}
|
|
}
|
|
|
|
void
|
|
PDBExtractor::PrintTestFooter()
|
|
{
|
|
if (m_Settings.PdbHeaderReconstructorSettings.TestFile != nullptr)
|
|
{
|
|
(*m_Settings.PdbHeaderReconstructorSettings.TestFile) << TEST_FILE_FOOTER;
|
|
}
|
|
}
|
|
|
|
void
|
|
PDBExtractor::PrintPDBHeader()
|
|
{
|
|
if (m_Settings.PrintHeader)
|
|
{
|
|
static const char* const ArchitectureString =
|
|
m_PDB.GetMachineType() == IMAGE_FILE_MACHINE_I386 ? "i386" :
|
|
m_PDB.GetMachineType() == IMAGE_FILE_MACHINE_AMD64 ? "AMD64" :
|
|
m_PDB.GetMachineType() == IMAGE_FILE_MACHINE_IA64 ? "IA64" :
|
|
m_PDB.GetMachineType() == IMAGE_FILE_MACHINE_ARMNT ? "ArmNT" :
|
|
"Unknown";
|
|
|
|
static char HEADER_FILE_HEADER_FORMATTED[16 * 1024];
|
|
|
|
sprintf_s(
|
|
HEADER_FILE_HEADER_FORMATTED, HEADER_FILE_HEADER,
|
|
m_Settings.PdbPath.c_str(),
|
|
ArchitectureString,
|
|
m_PDB.GetMachineType()
|
|
);
|
|
|
|
*m_Settings.PdbHeaderReconstructorSettings.OutputFile << HEADER_FILE_HEADER_FORMATTED;
|
|
}
|
|
}
|
|
|
|
void
|
|
PDBExtractor::PrintPDBDeclarations()
|
|
{
|
|
//
|
|
// Write declarations.
|
|
//
|
|
|
|
if (m_Settings.PrintDeclarations)
|
|
{
|
|
for (auto&& e : m_SymbolSorter->GetSortedSymbols())
|
|
{
|
|
if (e->Tag == SymTagUDT && !PDB::IsUnnamedSymbol(e))
|
|
{
|
|
*m_Settings.PdbHeaderReconstructorSettings.OutputFile
|
|
<< PDB::GetUdtKindString(e->u.Udt.Kind)
|
|
<< " " << m_HeaderReconstructor->GetCorrectedSymbolName(e) << ";"
|
|
<< std::endl;
|
|
}
|
|
else if (e->Tag == SymTagEnum)
|
|
{
|
|
*m_Settings.PdbHeaderReconstructorSettings.OutputFile
|
|
<< "enum"
|
|
<< " " << m_HeaderReconstructor->GetCorrectedSymbolName(e) << ";"
|
|
<< std::endl;
|
|
}
|
|
}
|
|
|
|
*m_Settings.PdbHeaderReconstructorSettings.OutputFile << std::endl;
|
|
}
|
|
}
|
|
|
|
void
|
|
PDBExtractor::PrintPDBDefinitions()
|
|
{
|
|
//
|
|
// Write definitions.
|
|
//
|
|
|
|
if (m_Settings.PrintDefinitions)
|
|
{
|
|
if (m_Settings.PrintPragmaPack)
|
|
{
|
|
*m_Settings.PdbHeaderReconstructorSettings.OutputFile
|
|
<< DEFINITIONS_PRAGMA_PACK_BEGIN
|
|
<< std::endl;
|
|
}
|
|
|
|
for (auto&& e : m_SymbolSorter->GetSortedSymbols())
|
|
{
|
|
bool Expand = true;
|
|
|
|
//
|
|
// Do not expand unnamed types, if they will be inlined.
|
|
//
|
|
|
|
if (m_Settings.PdbHeaderReconstructorSettings.MemberStructExpansion == PDBHeaderReconstructor::MemberStructExpansionType::InlineUnnamed &&
|
|
e->Tag == SymTagUDT &&
|
|
PDB::IsUnnamedSymbol(e))
|
|
{
|
|
Expand = false;
|
|
}
|
|
|
|
if (Expand)
|
|
{
|
|
m_SymbolVisitor->Run(e);
|
|
}
|
|
}
|
|
|
|
if (m_Settings.PrintPragmaPack)
|
|
{
|
|
*m_Settings.PdbHeaderReconstructorSettings.OutputFile
|
|
<< DEFINITIONS_PRAGMA_PACK_END
|
|
<< std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
PDBExtractor::PrintPDBFunctions()
|
|
{
|
|
//
|
|
// Write definitions.
|
|
//
|
|
|
|
if (m_Settings.PrintFunctions)
|
|
{
|
|
*m_Settings.PdbHeaderReconstructorSettings.OutputFile
|
|
<< "/*"
|
|
<< std::endl;
|
|
|
|
for (auto&& e : m_PDB.GetFunctionSet())
|
|
{
|
|
*m_Settings.PdbHeaderReconstructorSettings.OutputFile
|
|
<< e
|
|
<< std::endl;
|
|
}
|
|
|
|
*m_Settings.PdbHeaderReconstructorSettings.OutputFile
|
|
<< "*/"
|
|
<< std::endl;
|
|
}
|
|
}
|
|
|
|
void
|
|
PDBExtractor::DumpAllSymbols()
|
|
{
|
|
//
|
|
// We are going to print all symbols.
|
|
//
|
|
|
|
PrintPDBHeader();
|
|
|
|
for (auto&& e : m_PDB.GetSymbolMap())
|
|
{
|
|
m_SymbolSorter->Visit(e.second);
|
|
}
|
|
|
|
PrintPDBDeclarations();
|
|
PrintPDBDefinitions();
|
|
PrintPDBFunctions();
|
|
}
|
|
|
|
void
|
|
PDBExtractor::DumpOneSymbol()
|
|
{
|
|
const SYMBOL* Symbol = m_PDB.GetSymbolByName(m_Settings.SymbolName.c_str());
|
|
|
|
if (Symbol == nullptr)
|
|
{
|
|
throw PDBDumperException(MESSAGE_SYMBOL_NOT_FOUND);
|
|
}
|
|
|
|
PrintPDBHeader();
|
|
|
|
//
|
|
// InlineAll supresses PrintReferencedTypes.
|
|
//
|
|
|
|
if (m_Settings.PrintReferencedTypes &&
|
|
m_Settings.PdbHeaderReconstructorSettings.MemberStructExpansion != PDBHeaderReconstructor::MemberStructExpansionType::InlineAll)
|
|
{
|
|
m_SymbolSorter->Visit(Symbol);
|
|
|
|
//
|
|
// Print header only when PrintReferencedTypes == true.
|
|
//
|
|
|
|
PrintPDBDefinitions();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Print only the specified symbol.
|
|
//
|
|
|
|
m_SymbolVisitor->Run(Symbol);
|
|
}
|
|
}
|
|
|
|
void
|
|
PDBExtractor::DumpAllSymbolsOneByOne()
|
|
{
|
|
//
|
|
// Copy all symbols locally.
|
|
//
|
|
for (auto&& e : m_PDB.GetSymbolMap())
|
|
{
|
|
m_SymbolSorter->Visit(e.second);
|
|
}
|
|
|
|
auto Symbols = m_SymbolSorter->GetSortedSymbols();
|
|
|
|
m_SymbolSorter->Clear();
|
|
|
|
//
|
|
// Create output directory.
|
|
//
|
|
std::string OutputDirectory = m_Settings.OutputFilename
|
|
? m_Settings.OutputFilename
|
|
: ".";
|
|
|
|
if (OutputDirectory[OutputDirectory.length() - 1] != '\\')
|
|
{
|
|
OutputDirectory += '\\';
|
|
}
|
|
|
|
BOOL Result = CreateDirectory(OutputDirectory.c_str(), NULL);
|
|
if (!Result && GetLastError() != ERROR_ALREADY_EXISTS)
|
|
{
|
|
throw PDBDumperException("Cannot create directory");
|
|
}
|
|
|
|
for (auto&& e : Symbols)
|
|
{
|
|
if (!PDB::IsUnnamedSymbol(e))
|
|
{
|
|
m_Settings.PdbHeaderReconstructorSettings.OutputFile = new std::ofstream(
|
|
OutputDirectory + std::string(e->Name) + ".h",
|
|
std::ios::out
|
|
);
|
|
|
|
m_Settings.SymbolName = e->Name;
|
|
DumpOneSymbol();
|
|
|
|
delete m_Settings.PdbHeaderReconstructorSettings.OutputFile;
|
|
|
|
m_SymbolSorter->Clear();
|
|
}
|
|
}
|
|
|
|
m_Settings.PdbHeaderReconstructorSettings.OutputFile = nullptr;
|
|
}
|
|
|
|
void
|
|
PDBExtractor::CloseOpenedFiles()
|
|
{
|
|
//
|
|
// We want to free the memory only if the filename was specified,
|
|
// because OutputFile or TestFile may be std::cout.
|
|
//
|
|
|
|
if (m_Settings.TestFilename)
|
|
{
|
|
delete m_Settings.PdbHeaderReconstructorSettings.TestFile;
|
|
}
|
|
|
|
if (m_Settings.OutputFilename)
|
|
{
|
|
delete m_Settings.PdbHeaderReconstructorSettings.OutputFile;
|
|
}
|
|
}
|