如何从资源中获取版本信息?

3

我在资源中声明了版本信息:

100 VERSIONINFO
FILEVERSION 1,0,0,2
PRODUCTVERSION 1,0,0,2
FILEOS VOS_NT
FILETYPE VFT_APP
{
    BLOCK "StringFileInfo"
    {
        BLOCK "000004b0"
        {
            VALUE "FileDescription", "My application"
            VALUE "FileVersion", "1.0.0.2"
            VALUE "InternalName", "app.exe"
            VALUE "LegalCopyright", "Copyright ©  2012 by David."
            VALUE "OriginalFilename", "app.exe"
            VALUE "ProductName", "app"
            VALUE "ProductVersion", "1.0.0.2"
            VALUE "Assembly Version", "1.0.0.2"
        }
    }

    BLOCK "VarFileInfo"
    {
        VALUE "Translation", 0x0000 0x04B0
    }
}

我是这样获取版本信息的:

HRSRC hResInfo;
HGLOBAL hResData;
LPCVOID pRes;
UINT uLen;
VS_FIXEDFILEINFO *lpFfi;

hResInfo = FindResource(hInst, MAKEINTRESOURCE(100), RT_VERSION);
MessageBox(0, "FindResource", 0,0);

hResData = LoadResource(hInst, hResInfo);
MessageBox(0, "LoadResource", 0,0);

pRes = LockResource(hResData);
MessageBox(0, "LockResource", 0,0);

VerQueryValue(pRes, "\\" ,(LPVOID*)&lpFfi, &uLen);
MessageBox(0, "VerQueryValue", 0,0);

FreeResource(hResData);

DWORD dwFileVersionMS = lpFfi->dwFileVersionMS;
DWORD dwFileVersionLS = lpFfi->dwFileVersionLS;

DWORD dwLeftMost     = HIWORD(dwFileVersionMS);
DWORD dwSecondLeft   = LOWORD(dwFileVersionMS);
DWORD dwSecondRight  = HIWORD(dwFileVersionLS);
DWORD dwRightMost    = LOWORD(dwFileVersionLS);

在函数VerQueryValue中存在一个错误,因为程序中断(没有显示文本为“VerQueryValue”的MessageBox),Visual C++向我显示以下消息:
“ProxyCU.exe”中的0x77bf15a5处出现了第一次异常:0xC0000005:访问位置0x00483192时违规写入。
如何修复此代码?
敬礼,David

我通常会将版本信息定义的位置分离出来,就像这里所示,这样在代码中使用时,它就是一个编译器定义而不是对LoadResource()的调用。如果你无法让你的代码工作,你可以考虑这个选项。 - Sean Cline
我建议在LoadResource()之后检查错误代码(@ERR在调试器中观察,或者使用GetLastError)。另外,请检查地址0x00483192是否在pRes指向的内存范围内。 - Pavel Radzivilovsky
完全与问题无关,但是为什么你的版本资源ID是100?据我所知,它应该在ID 1处,即VS_VERSION_INFO。 - poizan42
2个回答

16
VerQueryValue()无法直接从原始资源访问版本信息。您必须在内存中制作副本,然后将该内存传递给VerQueryValue()。原因是VerQueryValue()旨在与GetFileVersionInfo()一起使用,后者需要一个用户分配的可写内存块,并在其中执行某些修复操作。访问VS_FIXEDFILEINFO结构不需要修复操作,但内存块仍必须是可写的。因为原始资源是只读内存,所以不能直接将其传递给VerQueryValue()

请尝试这个:

HRSRC hResInfo;
DWORD dwSize;
HGLOBAL hResData;
LPVOID pRes, pResCopy;
UINT uLen;
VS_FIXEDFILEINFO *lpFfi;

hResInfo = FindResource(hInst, MAKEINTRESOURCE(100), RT_VERSION);
dwSize = SizeofResource(hInst, hResInfo);
hResData = LoadResource(hInst, hResInfo);
pRes = LockResource(hResData);
pResCopy = LocalAlloc(LMEM_FIXED, dwSize);
CopyMemory(pResCopy, pRes, dwSize);
FreeResource(hResData);

VerQueryValue(pResCopy, TEXT("\\"), (LPVOID*)&lpFfi, &uLen);

DWORD dwFileVersionMS = lpFfi->dwFileVersionMS;
DWORD dwFileVersionLS = lpFfi->dwFileVersionLS;

DWORD dwLeftMost     = HIWORD(dwFileVersionMS);
DWORD dwSecondLeft   = LOWORD(dwFileVersionMS);
DWORD dwSecondRight  = HIWORD(dwFileVersionLS);
DWORD dwRightMost    = LOWORD(dwFileVersionLS);

LocalFree(pResCopy);

更新: 只有当您仅访问 VS_FIXEDFILEINFO 结构时,此方法才有效。如果您需要访问任何其他值,则必须使用 GetFileVersionInfo()。根据 Raymond Chen 的博客:

VerQueryValue 的第一个参数必须是从 GetFileVersionInfo 获取的缓冲区

文档之所以说 VerQueryValue 的第一个参数必须是由 GetFileVersionInfo 函数返回的缓冲区,是有原因的。GetFileVersionInfo 返回的缓冲区是一个不透明数据块,专门格式化以便 VerQueryValue 能够工作。您不应该查看该缓冲区内部,并且您绝不能尝试“以其他方式获取数据”。因为如果您这样做,VerQueryValue 将在未按函数所期望的方式格式化的缓冲区中查找某些内容。


+1 我刚刚需要并使用了这个。谢谢你。一个非常小的评论:根据文档,不需要调用 FreeResource 并且会返回 FALSE - David Heffernan
FreeResource的文档建议不要调用它。 - M.M
版本资源不一定是100,可以是任何数字。在我正在处理的传统项目中,它是“MAKEINTRESOURCE(1)” - Soonts
@Soonts 是的,但在OP给出的例子中,它是100。 - Remy Lebeau
显示剩余8条评论

-1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接