在Windows 2000和Windows XP中,用户可以通过文件属性的"版本"标签页查看文件的版本信息。然而在Windows 7中,这个功能被"详细信息"标签页所取代,它不仅没有显示所有以前的信息,而且不支持复制粘贴显示的信息。
幸运的是,有了VersInfoEx Shell扩展,这个问题不再是问题。VersInfoEx Shell扩展将丢失的"版本"标签页功能带回了Windows7。
在Michael Dunn的优秀文章系列"The Complete Idiot's Guide to Writing Shell Extensions"(特别是系列的第五部分)中,能够构建一个简单的属性页Shell扩展,它显示文件的VS_VERSIONINFO文件版本资源信息。
代码使用了一个手工制作的C++类来解析所有的VS_VERSIONINFO文件版本资源信息,包括所有可能存在的StringTables中的所有版本字符串,并将它们封装到一个简单的类的几个变量中,以便程序更容易访问。
然后,这个类的信息被Shell扩展本身简单地显示给用户。
CVersionInfo类完成了所有的解析工作,解析VS_VERSIONINFO文件版本资源信息。是从头开始编写它的,使用了SDK提供的信息。
CodeProject上确实有其他一些文章展示了如何解析版本信息,但它们在某些方面都有所欠缺。大多数文章没有解析所有可用的版本信息字符串,而且它们的整体解析逻辑在看来过于复杂和笨重。
CVersionInfo::Init()函数中的简单解析逻辑克服了这两个缺点,它提供了一个非常简短和简单的算法来解析所有的版本字符串:
C++
// Point to the VS_VERSIONINFO block passed to us (key = "VS_VERSION_INFO")
BLOCK* pVersionInfo = (BLOCK*) pVI;
// The root VS_VERSIONINFO block's value data is a VS_FIXEDFILEINFO structure
if (pVersionInfo->wValueLength)
memcpy(&m_FFInfo, BlockValue(pVersionInfo), min(sizeof(m_FFInfo), pVersionInfo->wValueLength));
// Process all of the root block's child blocks...
BLOCK* pBlock = ChildBlock(pVersionInfo);
BLOCK* pEndVersInfo = EndBlock(pVersionInfo);
for (; pBlock < pEndVersInfo; pBlock = NextBlock(pBlock)) {
if (_wcsicmp(pBlock->szKeyW, L"VarFileInfo") == 0) {
// "VarFileInfo" child BLOCKs are "Var" BLOCKS...
BLOCK* pVar = (BLOCK*) ChildBlock(pBlock);
BLOCK* pEndVars = EndBlock(pBlock);
for (; pVar < pEndVars; pVar = NextBlock(pVar)) {
if (_wcsicmp(pVar->szKeyW, L"Translation") == 0) {
DWORD* pLangDword = BlockValue(pVar);
WORD nNumDwords = pVar->wValueLength / sizeof(DWORD);
...(process language/codepage array)...
}
}
} else if (_wcsicmp(pBlock->szKeyW, L"StringFileInfo") == 0) {
// "StringFileInfo" child BLOCKs are "StringTable" BLOCKS...
BLOCK* pStrTab = (BLOCK*) ChildBlock(pBlock);
BLOCK* pEndStrTab = EndBlock(pBlock);
for (; pStrTab < pEndStrTab; pStrTab = NextBlock(pStrTab)) {
// "StringTable" child BLOCKs are "String" BLOCKS...
BLOCK* pString = (BLOCK*) ChildBlock(pStrTab);
BLOCK* pEndStrings = EndBlock(pStrTab);
for (; pString < pEndStrings; pString = NextBlock(pString)) {
CStringW strNameW = pString->szKeyW;
CStringW strValueW = (LPCWSTR) BlockValue(pString);
...(process versinfo String)...
}
}
}
}
简单性来自于使用非常小的"辅助"函数,这些函数适当地调整传递的BLOCK结构指针。文件版本信息资源BLOCK结构如下所示:
C++
struct BLOCK
// (always aligned on 32-bit (DWORD) boundary)
{
WORD wLength; // Length of this block (doesn't include padding)
WORD wValueLength; // Value length (if any)
WORD wType; // Value type (0 = binary, 1 = text)
WCHAR szKeyW[]; // Value name (block key) (always NULL terminated)
//WORD padding1[]; // Padding, if any (ALIGNMENT)
//xxxxx Value[]; // Value data, if any (*ALIGNED*)
//WORD padding2[]; // Padding, if any (ALIGNMENT)
//xxxxx Child[]; // Child block(s), if any (*ALIGNED*)
};
每个块总是有一个键,但可能有也可能没有值成员,它本身可能是一个或多个子块等。所有块都在DWORD(32位)对齐边界上开始,它们的值数据和子块也是如此(如果存在的话,除了值数据之外)。
块长度或值长度字段不包括块的末尾和下一个块的开始之间或键的末尾和值数据的开始之间的任何填充。
"辅助"函数本身如下:
C++
// Helper functions for navigating through VERSIONINFO data...
BLOCK* RoundUp32(BLOCK* p, size_t n)
{
return (BLOCK*) (((ptrdiff_t) p + n + 3) & ~3);
}
BLOCK* AlignBlock(BLOCK* pBlk, BLOCK* p)
{
return RoundUp32(pBlk, ((BYTE*)p - (BYTE*)pBlk));
}
BLOCK* BlockValue(BLOCK* pBlk)
{
return AlignBlock(pBlk, (BLOCK*) ((BYTE*)pBlk + sizeof(BLOCK) + ((wcslen(pBlk->szKeyW) + 1) * sizeof(WCHAR))));
}
BLOCK* EndBlock(BLOCK* pBlk)
{
return (BLOCK*) ((BYTE*) pBlk + pBlk->wLength);
}
// (NOTE: must NOT be rounded)
BLOCK* ChildBlock(BLOCK* pBlk)
{
return AlignBlock(pBlk, (BLOCK*) ((BYTE*) BlockValue(pBlk) + pBlk->wValueLength));
}
BLOCK* NextBlock(BLOCK* pBlk)
{
return AlignBlock(pBlk, EndBlock(pBlk));
}
利用这些信息(上述函数),如果愿意,可以很容易地编写自己的"HasChild()"函数,如下所示:
C++
BOOL HasChild(BLOCK* pBlk) {
return ChildBlock(pBlk) < EndBlock(pBlk);
}
不提供安装程序,因为安装Shell扩展非常简单:
将DLL复制到您的%SystemRoot%\system32文件夹中。
打开管理员命令提示符窗口,并输入命令:
regsvr32 "C:\Windows\system32\VersInfoEx.dll"
注销然后重新登录,以强制Shell(explorer.exe)刷新。
注意,必须在regsvr32命令上输入完整路径,因为那是实际写入注册表的值。
如果想卸载它,只需"注销"Shell扩展,输入相同的命令,但指定"/u"(注销)选项。然后只需从Windowssystem32目录中删除DLL即可。
只在Windows7 x64上测试了这个Shell扩展,但它应该可以在任何32位或64位版本的Windows上工作。