【发布时间】:2020-08-22 19:34:17
【问题描述】:
我正在尝试使用 WinAPI 函数 VerQueryValue 获取文件的各种属性,如其“详细信息”选项卡中所示。我已成功使用此函数通过VS_FIXEDFILEINFO 获取非字符串版本信息,但无法使函数文档底部显示的example 正常工作。
该示例实际上并不完整,因为它省略了其他相关函数的使用以及使用该函数所需的一些缓冲区的构造,因此我已尽我所能填补空白并更改了一些使用 C++ SL 的中间步骤,因为这最终是我需要使用的语言:
#include <iostream>
#include <iomanip>
#include "sstream"
#include "Windows.h"
#pragma comment(lib, "Version.lib")
int ReadOutFileDescriptions(std::wstring filename)
{
LPBYTE lpBuffer = NULL;
DWORD verHandle, verSize = GetFileVersionInfoSize(filename.c_str(), &verHandle);
if (verSize != NULL)
{
LPSTR verData = new char[verSize];
if (GetFileVersionInfo(filename.c_str(), verHandle, verSize, verData))
{
UINT cbTranslate;
// Structure used to store enumerated languages and code pages.
struct LANGANDCODEPAGE {
WORD wLanguage;
WORD wCodePage;
} *lpTranslate;
// Read the list of languages and code pages.
VerQueryValue(verData, TEXT("\\VarFileInfo\\Translation"), (LPVOID*)&lpTranslate, &cbTranslate);
// Read the file description for each language and code page.
for (ULONGLONG i = 0; i < (cbTranslate / sizeof(struct LANGANDCODEPAGE)); i++)
{
std::wostringstream ss; ss << std::setfill(L'0') << std::hex;
ss << std::setw(4) << lpTranslate[i].wLanguage;
std::wstring langS = ss.str();
ss.str(std::wstring());
ss << std::setw(4) << lpTranslate[i].wCodePage;
std::wstring codeS = ss.str();
std::wstring subBlock = L"\\StringFileInfo\\" + langS + codeS + L"\\FileDescription";
// Retrieve file description for language and code page "i".
WCHAR descBuffer[50];
LPVOID lpBuffer = &descBuffer;
UINT bufferSize;
VerQueryValue(verData, subBlock.c_str(), &lpBuffer, &bufferSize);
std::cout << bufferSize << '\n' << descBuffer;
}
}
delete[] verData;
}
return 0;
}
int main(int argc, char* argv[])
{
ReadOutFileDescriptions(L"MyFile.exe");
return 0;
}
我只对 WinAPI 及其 typedef 有一点经验,而且我的 C 有点生疏,所以我确定我只是设置/使用不正确的缓冲区等。
打印的缓冲区大小是正确的(MyFile.exe 的描述长度 + 1 表示空字符)所以我知道函数正在获取正确的值,但实际打印的值只是一系列十六进制字符,很可能是地址。
我做错了什么?
编辑(答案): 感谢@dxiv,我意识到我并不完全理解如何在内部和函数返回后使用 VerQueryValue 的“结果”参数 (lplpBuffer)。
将循环的结尾更改为以下即可达到我想要的结果:
//WCHAR descBuffer[50] Not required, the function doesn't utilize a user made buffer
LPVOID lpBuffer;
UINT bufferSize;
VerQueryValue(verData, subBlock.c_str(), &lpBuffer, &bufferSize); // lpBuffer is reassigned here
std::wstring fileDescription((const TCHAR*)lpBuffer); // Create std::string from C style string (char*) that lpBuffer now points to
std::wcout << bufferSize << '\n' << fileDescription;
【问题讨论】:
-
std::cout 不知道有关 WCHAR[] 的 bean,请使用 std::wcout。支持调试器。
-
lpBuffernull 终止了吗? -
@Mayur 是的,虽然在调用返回之前字符串的长度也被放入“puLen”(此处为bufferSize)缓冲区参数中
-
文档中没有提到它,但实际上它是空终止的,至少在我的带有 SDK 10.0.18362 的 Windows 10 机器上(证明:i.imgur.com/Lznaziv.png)。虽然通常您不应该依赖于一种实现,但对这种行为进行更改无疑会破坏许多应用程序,并且非常重要,我怀疑它会被更改。如果您仍然希望安全,在 C++ 中您可以使用构造函数 std::wstring((const TCHAR*)lpBuffer, *puLen) 来指定 str 长度,或者在 C 中您可以分配一个 *puLen+1 的缓冲区, strcpy lpBuffer 进去,然后做 buffer[*puLen+1] = '\0'
-
@Mayur 不是 stackoverflow 最正式的事情,但我意识到我在上次回复时忘记联系你了。