65.9K
CodeProject 正在变化。 阅读更多。
Home

查看 LIB 的工具

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (52投票s)

2005年1月15日

5分钟阅读

viewsIcon

295656

downloadIcon

17185

一个查看库(.LIB)文件中函数并将其导出到头文件(.H)的有用工具

Sample Image - LibView.jpg

引言

你是否曾想知道库(.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.0eVC++4.0VC++ 6.0编译器生成的LIB文件上进行了测试。

希望你觉得有用。谢谢!

许可证

本文未明确附加任何许可证,但可能包含文章文本或下载文件本身的使用条款。如有疑问,请通过下方的讨论区联系作者。作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.