查看 LIB 的工具






4.90/5 (52投票s)
2005年1月15日
5分钟阅读

295656

17185
一个查看库(.LIB)文件中函数并将其导出到头文件(.H)的有用工具
引言
你是否曾想知道库(.Lib)文件导出了哪些内容?有时,库可能会导出未在头文件中提供的隐藏函数,或者你是否曾想过在提供LIB文件的同时,根据头文件来验证函数的语法?可能会存在不匹配的情况。在这种情况下,这就是你的解决方案。
这个简单的工具可以遍历LIB文件并显示其导出的函数,并且可以根据发现的内容生成一个头文件(.h)。这对于Visual C++开发者来说非常有用。存在一些限制,请参阅“限制”标题下的内容。
背景
你可能会在C++函数的位置看到一些乱码字符。类似于Depends(Visual Studio附带的一个显示DLL内部函数的工具)。这被称为“名称修饰”。乱码格式的函数被称为“修饰名称”。C++编译器会对C++程序中的符号名称进行编码,以便在名称中包含类型信息。这样,链接器就可以检查确切的函数并确保类型安全的链接。C编译器不需要这个,因为在整个可执行链接中只能存在一个函数。然而,在C++中,可以存在多个同名但位于不同类中且参数不同的函数。因此,C++需要这个来解析函数名称。
在这里,我利用了编译器的这一特性。该工具收集库(.Lib)文件中的修饰名称,并将其解码为未修饰名称。未修饰形式是我们正常头文件中存在的形式。
名称修饰
Microsoft™ Visual C++ 编译器以这种方式编码C++程序中的符号名称。这并未由Microsoft™公开。这是他们的一项编译器秘密。然而,在某些情况下,你需要未修饰的格式。为此,Microsoft™提供了一些解码API。这些API包含在“Debug Help Library”(DbgHelp.DLL)中。
C++修饰符号以问号‘?
’开头。在处理LIB时,如果你收集以‘?
’开头并以NULL
字符结尾的符号,你就可以得到整个修饰名称。将此输入到“Debug Help Library”中的UnDecorateSymbolName
函数,你将获得未修饰的名称。就是这样。例如,如果你输入
?MyFunc@@YAHD@Z
你将得到
int __cdecl MyFunc(char)
C符号与其对应的C++不同。在导出的函数前面始终有一个“__imp__
”标记。它不提供参数类型规范。它仅在‘@
’标记后提供参数的字节数。例如,函数
int __stdcall func (int a, double b)
编码后看起来会像这样
__imp__func@12
因此,在C的LIB文件中,不可能获取头文件。这里只能检索函数名称,即通过收集从“__imp__
”标记到‘@
’字符的部分。这就是该工具所做的。
核心代码
在这里,我将LIB文件符号提取封装在一个名为CLibContent
的类中。当传递LIB文件路径时,它会提取并以字符串数组的形式存储修饰和未修饰的名称,这些名称稍后可以被检索。下面是展示核心提取的代码。
...
...
HANDLE hLibFile = CreateFile("C:\Libfiles\adme.lib",
GENERIC_READ,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
...
...
for(i=0;i<dwFileSize;i++)
{
SetFilePointer(hLibFile,i,0,FILE_BEGIN);
nRet = ReadFile(hLibFile,&szChar,32,&dwBytes,NULL);
if(!nRet)
return GetLastError();
if((szChar[0] == '?')||(bCollect))
{// the symbol is C++ type collect it
szBuff[dwBuffPos] = szChar[0];
dwBuffPos++;
bCollect = true;
}
else if(!memcmp(szChar,C_STYLE,7))
// check if it is C style (ie. "__imp__")
{
dwBytes = strlen(szChar);
nRet = ExtractCSymbol(szChar,dwBytes);
// if it is succ then 0 else non zero
if(!nRet) // if failed then pass on to next char
i += dwBytes; // if passed skip the string
}
if((szChar[0] == 0)&&(bCollect))
{ // finish and pass the buffer to decode
szBuff[dwBuffPos] = szChar[0];
dwBuffPos++;
nRet = ExtractCppSymbol(szBuff, dwBuffPos);
dwBuffPos = 0;
bCollect = false;
}
}
...
...
以下函数用于解码CPP风格的修饰名称
int CLibContent::ExtractCppSymbol(char *szDecoratedName, DWORD dwLen)
{
char szFunc[512];
int nRet;
nRet = UnDecorateSymbolName(szDecoratedName, szFunc, 512,
UNDNAME_COMPLETE|UNDNAME_32_BIT_DECODE);
if(!nRet)
return GetLastError(); // failure
if(!memcmp(szDecoratedName,szFunc,nRet))
return ERROR_INVALID_DATA;
// since it is not decoded, this cud b some junk
if(!memcmp("`string'",szFunc,nRet))
// this cud b imported functions and constants
return ERROR_INVALID_DATA; // so v don't need this
//storing strings in a array and returning nothing else
return m_cMemStore.Add(szDecoratedName,szFunc);
}
以下函数用于解码C风格的修饰名称
int CLibContent::ExtractCSymbol(char *szDecoratedName, DWORD dwLen)
{
char szFunc[512];
DWORD dwBuffPos;
int i = 0;
dwBuffPos = 0;
if(memcmp(szDecoratedName,C_STYLE,7))
return ERROR_INVALID_DATA;
i += strlen(C_STYLE);
for(;i<dwLen;i++)
{
szFunc[dwBuffPos] = szDecoratedName[i];
if(szFunc[dwBuffPos] == '@')
{
szFunc[dwBuffPos] = 0;
//storing strings in a array nothing else
m_cMemStore.Add(szDecoratedName, szFunc);
return ERROR_SUCCESS;
}
dwBuffPos++;
}
return ERROR_INVALID_DATA;;
}
关注点
在进行此操作时,我偶然试验了BSC SDK(Browser Toolkit)。这是一个有用的SDK,当你想要浏览VC++生成的BSC(browser information)文件时。VC++ IDE使用此文件,当你右键单击并选择“转到定义...”菜单项时,它会指向函数或变量的定义。使用此SDK,你可以获得项目中使用的符号的详细信息。此工具包中有一个函数可以从特定于BSC文件的修饰名称中获取未修饰的符号。有点像UnDecorateSymbolName
函数。:)
限制
当想到LIB文件时,它似乎非常简单,并且所有东西都属于同一类。并非如此。每个编译器和每个编译器版本都可能产生不同类型的LIB文件和名称修饰。从这个角度来看,此工具将/可能无法处理除VC++ 6.0以外的其他编译器生成的LIB文件。
在导出的头文件中,属于类的函数不会以类的形式出现。它们看起来更像一个完整的函数。另一件事是,C函数的参数无法检索或导出到头文件中。
结论
如果你想了解VC++的修饰格式,请查看此网站。它提供了VC++ 6.0的名称修饰格式。
需要注意的是,此格式可能随时更改,恕不另行通知。此工具也无法确定是否适用于VC++ 7.0的LIB文件。如果有人能测试并告知我,那将很有帮助。我在eVC++ 3.0、eVC++4.0和VC++ 6.0编译器生成的LIB文件上进行了测试。
希望你觉得有用。谢谢!
许可证
本文未明确附加任何许可证,但可能包含文章文本或下载文件本身的使用条款。如有疑问,请通过下方的讨论区联系作者。作者可能使用的许可证列表可以在此处找到。