Windows平台可执行文件(execute文件)属性中会有版本信息,包含文件说明、文件版本、版权等信息。本文主要目的是将设置版本信息的方法公开化。
首先我们要清楚Windows下的可执行文件格式属于PE文件格式标准,PE文件标准支持内嵌资源,就是将一个外部文件内嵌到可执行文件中,这样程序启动时只需从自身内部找到这块资源加载就可以了,而不需依赖其他外部的磁盘文件。
PE文件支持的内嵌资源都有两个必须的标识:一个是资源类型,一个是资源名称。因此只要知道内嵌资源的这两个标识就能找到对应的资源。可执行文件的版本信息就是以内嵌资源的方式保存在文件中。
- 资源类型和资源名称可以是数字也可以是字符串。
- 系统已知资源都是以数字来命名的(比如图标资源、版本资源、Manifest资源)。
- 建议自定义资源都用字符串来命名,避免与系统资源冲突。
微软提供了一组通用API接口,用来获取和设置可执行文件的内嵌资源,请看下面详细介绍。
版本数据获取
获取PE文件的版本信息数据有两种获取方法:
- 通用内嵌资源获取方式
- 专用API获取方式
通用内嵌资源获取方式
通过下面接口可以获取Windows可执行文件中的任何通用资源。
#include <io.h>
#include <string>
std::string GetPEResource(const char* exepath, const char* type,
const char* name, int language = 0)
{
std::string r = "";
if (!exepath)
return r;
//判定文件是否存在
if (_access(exepath, 0) != 0)
return r;
//加载可执行文件
HMODULE hexe = LoadLibrary(exepath);
if (!hexe)
return r;
//查找资源
HRSRC src = FindResourceEx(hexe, type, name, language);
if (src)
{
HGLOBAL glb = LoadResource(hexe, src);
int sz = SizeofResource(hexe, src);
r = std::string((char *)LockResource(glb), sz);
UnlockResource(glb);
FreeResource(glb);
}
//释放可执行文件
FreeLibrary(hexe);
return r; //返回一个完整的、标准的版本信息数据
}
通过对 GetPEResource
接口进行简单调用即可获取PE文件中的版本数据。
文件的版本信息以内嵌资源的方式保存在可执行文件中,它的资源类型是RT_VERSION
(16),资源名称是 MAKEINTRESOURCE(VS_VERSION_INFO)
(1)。
std::string rc = GetPEResource( "C:\\测试文件.exe", RT_VERSION, MAKEINTRESOURCE(VS_VERSION_INFO), 0);
专用API获取方式
微软提供了另外一种专门用于获取文件版本信息的API接口,示例代码如下:
#include <io.h>
#include <string>
std::string GetPEVersionInfo(const char* exepath)
{
std::string r = "";
if (!exepath)
return r;
if (_access(exepath, 0) != 0)
return r;
DWORD sz = GetFileVersionInfoSize(exepath, 0);
r.resize(sz, 0);
return GetFileVersionInfo(exepath, 0, sz, (void*)r.c_str()) ? r : "";
}
通过调用此接口,也可正常获取文件中的版本信息数据。但此接口返回的缓冲区是自带备份的,并非标准版本数据。举个例子:假如某exe文件的版本数据大小是N字节,那么调用 GetPEVersionInfo
接口返回的缓冲区大小则是N * 2 + 4字节,前N字节数据为标准的版本数据,中间4字节用 ‘F’ ‘E’ ‘2’ 'X’字符填充,后N个字节用0填充。
版本数据解析
版本数据是格式是一个名叫
VS_VERSIONINFO
的结构体,在msdn上可搜到这个结构体的相关描述。该结构可以认为由三部分构成。
第一部分是一个VS_FIXEDFILEINFO
对象,包含简要版本信息。
第二部分是一个VarFileInfo
对象,该部分可能不存在。
第三部分是一个StringFileInfo
对象,该部分可能不存在。
VS_FIXEDFILEINFO
对象存放在VS_VERSIONINFO
结构中的Value
字段中,
VarFileInfo
对象和StringFileInfo
对象(若存在的话)存放在VS_VERSIONINFO
结构中的Children
字段中。
typedef struct {
WORD wLength; // VS_VERSIONINFO 结构的长度(以字节为单位)
WORD wValueLength; // Value成员的长度(以字节为单位)。 如果没有与当前版本结构关联的 Value 成员,则此值为零。
WORD wType; // 版本资源中的数据的类型。 如果版本资源为文本数据,则此成员为 1; 如果版本资源为二进制数据,则为0。
WCHAR szKey; // 标识字符串: “VS_VERSION_INFO”
WORD Padding1; // 包含在32位边界上 对齐 Value 成员所需的 0 , 使后面字段按4字节对齐。
VS_FIXEDFILEINFO Value; // 与此 VS_VERSIONINFO 结构关联的数据。 wValueLength 成员指定此成员的长度;如果 wValueLength 为零,则不存在此成员。
WORD Padding2; // 在32位边界上对齐 Children 成员所需的 0 。 这些字节不包含在 wValueLength 中。
WORD Children; // 零个或一个 StringFileInfo 结构的数组,以及零个或一个 VarFileInfo 结构。
} VS_VERSIONINFO;
typedef struct tagVS_FIXEDFILEINFO {
DWORD dwSignature; // 文件填充信息标识,固定值0xFEEF04BD
DWORD dwStrucVersion; // 该结构的32位二进制版本号,高16位是主版本号,低16位是副版本号
DWORD dwFileVersionMS; // 该文件二进制版本号的高32bits
DWORD