版本信息资源,在这里指的是资源类型为RT_VERSION的资源信息。通过ResHacker看到的效果图如下:
版本信息资源存储编码格式是UNICODE,解析代码如下:
HRSRC hResrc = ::FindResourceEx((HMODULE)hModule, lpType, lpName, wLanguage);
DWORD dwSize = ::SizeofResource((HMODULE)hModule, hResrc);
HGLOBAL hGlobal = ::LoadResource((HMODULE)hModule, hResrc);
if (hGlobal == NULL)
return;
BYTE* lpResrc = (BYTE*)LockResource(hGlobal);
if (lpResrc == NULL)
return;
HGLOBAL hAllocMem = GlobalAlloc(GMEM_FIXED, dwSize);
char* pMem = (char*)GlobalLock(hAllocMem);
memcpy(pMem, lpResrc, dwSize);
std::wstring swVersion = GetVersionInfo((char*)pMem, dwSize);
GlobalUnlock(hAllocMem);
GlobalFree(hAllocMem);
//swVersion就是解析的最终结果
其中GetVersionInfo函数的实现代码如下:
#define DWORDUP(x) (((DWORD)(LPBYTE)(x)+3)&~03)
std::wstring GetVersionInfo(char* lpszVersinon, int size)
{
//第一部分是一个 VS_FIXEDFILEINFO 对象,包含简要版本信息。
//第二部分是一个 VarFileInfo 对象,该部分可能不存在。
//第三部分是一个 StringFileInfo 对象,该部分可能不存在。
VS_VERSIONINFO* pVersionInfo = (VS_VERSIONINFO*)lpszVersinon;
VS_FIXEDFILEINFO* pFileInfo = (VS_FIXEDFILEINFO*)((char*)lpszVersinon + 40);
std::wstring wsResult = L"1 VERSIONINFO\r\n";
CStringW wStr;
wStr.Format(L"FILEVERSION %d,%d,%d,%d",
(pFileInfo->dwFileVersionMS >> 16) & 0xFF,
(pFileInfo->dwFileVersionMS) & 0xFF,
(pFileInfo->dwFileVersionLS >> 16) & 0xFF,
(pFileInfo->dwFileVersionLS) & 0xFF);
wsResult += wStr.GetBuffer(0);
wsResult += L"\r\n";
wStr.Format(L"PRODUCTVERSION %d,%d,%d,%d",
(pFileInfo->dwProductVersionMS >> 16) & 0xFF,
(pFileInfo->dwProductVersionMS) & 0xFF,
(pFileInfo->dwProductVersionLS >> 16) & 0xFF,
(pFileInfo->dwProductVersionLS) & 0xFF);
wsResult += wStr.GetBuffer(0);
wsResult += L"\r\n";
wStr.Format(L"FILEOS 0x%X", pFileInfo->dwFileOS);
wsResult += wStr.GetBuffer(0);
wsResult += L"\r\n";
wStr.Format(L"FILETYPE 0x%X", pFileInfo->dwFileType);
wsResult += wStr.GetBuffer(0);
wsResult += L"\r\n{";
VS_VARSTRINGTABLE* pbVersionInfoStrings = (VS_VARSTRINGTABLE*)((char*)pFileInfo + sizeof(VS_FIXEDFILEINFO));
VS_VARBLOCK* pbVersionInfoString = (VS_VARBLOCK*)pbVersionInfoStrings;
int nRef = 1;
while ((DWORD_PTR)pbVersionInfoString < (DWORD_PTR)lpszVersinon + size)
{
VS_VARBLOCK* pbBlocks = (VS_VARBLOCK*)pbVersionInfoString;
std::wstring wsBlock = L"\r\nBLOCK \""+ std::wstring((WCHAR*)pbBlocks->szKey) + L"\"";
wsBlock += L"\r\n{\r\n";
if (nRef == 1) // StringFileInfo
{
VS_VARSTRING* pbBlock = (VS_VARSTRING*)DWORDUP(pbBlocks->szKey + lstrlenW(pbBlocks->szKey) + 1);
while ((DWORD_PTR)pbBlock < (DWORD_PTR)pbBlocks + pbBlocks->wLength)
{
wsBlock += L"\tBLOCK \"" + std::wstring((WCHAR*)pbBlock->szKey) + L"\"";
//
VS_VARSTRINGTABLE* pbStringTable = (VS_VARSTRINGTABLE*)pbBlock;
wsBlock += L"\r\n\t{";
BOOL bResult = FALSE;
VS_VARSTRING* pbString = (VS_VARSTRING*)DWORDUP(pbStringTable->szKey + lstrlenW(pbStringTable->szKey) + 1);
while ((DWORD_PTR)pbString < (DWORD_PTR)pbStringTable + pbStringTable->wLength)
{
if (pbString->wType == 1)
wsBlock += L"\r\n\t\tVALUE \"" + std::wstring((WCHAR*)pbString->szKey) + L"\"" + L", \"" + std::wstring((WCHAR*)DWORDUP(pbString->szKey + lstrlenW(pbString->szKey) + 1)) + L"\"";
else
{
VS_VARSTRING* pbStringValue = (VS_VARSTRING*)DWORDUP(pbString->szKey + lstrlenW(pbString->szKey) + 1);
WORD dw1 = *(WORD*)pbStringValue;
WORD dw2 = *(WORD*)((WORD*)pbStringValue + 1);
wStr.Format(L"0x%04X 0x%04X", dw1, dw2);
wsBlock += L"\r\n\t\tVALUE \"" + std::wstring((WCHAR*)pbString->szKey) + L"\"" + L", " + std::wstring((WCHAR*)wStr.GetBuffer(0));
}
pbString = (VS_VARSTRING*)DWORDUP((DWORD_PTR)pbString + pbString->wLength);
}
//
wsBlock += L"\t\r\n\t}";
pbBlock = (VS_VARSTRING*)DWORDUP((DWORD_PTR)pbBlock + pbBlock->wLength);
}
}
else if (nRef == 2) // VarFileInfo
{
wsBlock = L"\r\n" + wsBlock;
VS_VARSTRING* pbBlock = (VS_VARSTRING*)DWORDUP(pbBlocks->szKey + lstrlenW(pbBlocks->szKey) + 1);
wsBlock += L"\tBLOCK \"" + std::wstring((WCHAR*)pbBlock->szKey) + L"\"";
//
VS_VARSTRINGTABLE* pbStringTable = (VS_VARSTRINGTABLE*)pbBlock;
wsBlock += L"\r\n\t{";
BOOL bResult = FALSE;
VS_VARSTRING* pbString = (VS_VARSTRING*)pbStringTable;
if (pbString->wType == 1)
wsBlock += L"\r\n\t\tVALUE \"" + std::wstring((WCHAR*)pbString->szKey) + L"\"" + L", \"" + std::wstring((WCHAR*)DWORDUP(pbString->szKey + lstrlenW(pbString->szKey) + 1)) + L"\"";
else
{
VS_VARSTRING* pbStringValue = (VS_VARSTRING*)DWORDUP(pbString->szKey + lstrlenW(pbString->szKey) + 1);
WORD dw1 = *(WORD*)pbStringValue;
WORD dw2 = *(WORD*)((WORD*)pbStringValue + 1);
wStr.Format(L"0x%04X 0x%04X", dw1, dw2);
wsBlock += L"\r\n\t\tVALUE \"" + std::wstring((WCHAR*)pbStringTable->szKey) + L"\"" + L", " + std::wstring((WCHAR*)wStr.GetBuffer(0));
}
wsBlock += L"\t\r\n\t}";
pbBlock = (VS_VARSTRING*)DWORDUP((DWORD_PTR)pbBlock + pbBlock->wLength);
}
else
break; // 按道理这里不会进来
wsBlock += L"\r\n}";
wsResult += wsBlock;
pbVersionInfoString = (VS_VARSTRING*)DWORDUP((DWORD_PTR)pbVersionInfoString + pbVersionInfoString->wLength);
nRef++;
}
wsResult += L"\r\n}";
return wsResult;
}
解析结果一致。
文件所用结构定义如下:
typedef struct tagVS_FIXEDFILEINFO
{
DWORD dwSignature; // 文件填充信息标识,固定值0xFEEF04BD
DWORD dwStrucVersion; // 结构体版本号,当前值0x10000,高2字节代表主版本号,低2字节代表副版本号
DWORD dwFileVersionMS; // 4段文件版本号的前2段,每段2字节,不如3.8
DWORD dwFileVersionLS; // 4段文件版本号的后2段,每段2字节
DWORD dwProductVersionMS; // 4段产品版本号的前2段,每段2字节
DWORD dwProductVersionLS; // 4段产品版本号的后2段,每段2字节
DWORD dwFileFlagsMask; /* = 0x3F for version "0.42" */
DWORD dwFileFlags; /* e.g. VFF_DEBUG | VFF_PRERELEASE */
DWORD dwFileOS; /* e.g. VOS_DOS_WINDOWS16 */
DWORD dwFileType; /* e.g. VFT_DRIVER */
DWORD dwFileSubtype; /* e.g. VFT2_DRV_KEYBOARD */
DWORD dwFileDateMS; // 文件创建日期高4字节
DWORD dwFileDateLS; // 文件创建日期低4字节
} VS_FIXEDFILEINFO;
typedef struct tagVS_VERSIONINFO
{
WORD wLength; // 结构体总长度
WORD wValueLength; // 后面Value字段的长度
WORD wType; // 资源类型,0代表二进制数据,1代表字符串数据
WCHAR szKey[ANYSIZE_ARRAY]; // 标识字符串: “VS_VERSION_INFO”
WORD Padding1[ANYSIZE_ARRAY]; // 填充字节串(以0填充),使后面字段按4字节对齐
VS_FIXEDFILEINFO Value; // VS_FIXEDFILEINFO对象,存储简要版本信息 offset=40字节
WORD Padding2[ANYSIZE_ARRAY]; // 填充字节串(以0填充),使后面字段按4字节对齐
WORD Children[ANYSIZE_ARRAY]; // 里面最多存放两个对象,要么是 VarFileInfo 要么是 StringFileInfo
} VS_VERSIONINFO;
typedef struct tagVS_VAR
{
WORD wLength; // 该结构体长度
WORD wValueLength; // Value字段长度
WORD wType; // 0代表二进制数据,1代表字符串数据。此值为0
WCHAR szKey[ANYSIZE_ARRAY]; // 标识字符串: “Translation”
WORD Padding[ANYSIZE_ARRAY]; // 填充字节串(以0填充),使后面字段按4字节对齐
DWORD Value; // 区域语言值
}VS_VAR;
typedef struct tagVS_VARFILEINFO
{
WORD wLength; // 该结构体长度
WORD wValueLength; // Value字段长度
WORD wType; // 0代表二进制数据,1代表字符串数据。此值为0
WCHAR* szKey; // 标识字符串: “Translation”
WORD* Padding; // 填充字节串(以0填充),使后面字段按4字节对齐
VS_VAR Children; // 包含区域语言列表信息
}VS_VARFILEINFO;
typedef struct tagVS_STRING
{
WORD wLength; // 该结构体长度
WORD wValueLength; // 后面Value字段占用的WORD大小
WORD wType; // 0代表二进制数据,1代表字符串数据。此值为0
WCHAR szKey[ANYSIZE_ARRAY]; // “Comments” “CompanyName” “FileDescription” “FileVersion”等等
WORD Padding[ANYSIZE_ARRAY]; // 填充字节串(以0填充),使后面字段按4字节对齐
WORD Value[ANYSIZE_ARRAY]; // 值的内容,注意:填充Value的值要使得后续的对象按4字节对齐
}VS_STRING;
typedef struct tagVS_STRINGTABLE
{
WORD wLength; // 该结构体长度
WORD wValueLength; // Value字段长度
WORD wType; // 0代表二进制数据,1代表字符串数据。此值为0
TCHAR szKey[ANYSIZE_ARRAY]; // 语言和编码中文字符一般为Unicode “000004b0”
WORD Padding[ANYSIZE_ARRAY]; // 填充字节串(以0填充),使后面字段按4字节对齐
VS_STRING Children[ANYSIZE_ARRAY]; // String对象列表,版本字段全部存放在这个列表中
}VS_STRINGTABLE;
typedef struct tagVS_STRINGFILEINFO
{
WORD wLength; // 该结构体长度
WORD wValueLength; // Value字段长度
WORD wType; // 0代表二进制数据,1代表字符串数据。此值为0
WCHAR szKey[ANYSIZE_ARRAY]; // 标识字符串: “Translation”
WORD Padding[ANYSIZE_ARRAY]; // 填充字节串(以0填充),使后面字段按4字节对齐
VS_STRINGTABLE Children[ANYSIZE_ARRAY]; // 版本字段列表
}VS_STRINGFILEINFO;
#include <fstream>
typedef struct tagVS_VARBLOCK
{
WORD wLength; // 该结构体长度
WORD wValueLength; // Key字段长度
WORD wType; // 0代表二进制数据,1代表字符串数据。此值为0
WCHAR szKey[ANYSIZE_ARRAY];
WORD Padding[ANYSIZE_ARRAY];
}VS_VARBLOCK;
typedef struct tagVS_VARSTRING:
public VS_VARBLOCK
{
WORD Value[ANYSIZE_ARRAY];
}VS_VARSTRING;
typedef struct tagVS_VARSTRINGTABLE:
public VS_VARBLOCK
{
VS_VARSTRING Children[ANYSIZE_ARRAY];
}VS_VARSTRINGTABLE;
typedef struct tagVS_VARSTRINGFILEINFO:
public VS_VARBLOCK
{
VS_VARSTRINGTABLE Children[ANYSIZE_ARRAY];
}VS_VARSTRINGFILEINFO;