简 述: 获取 windows
下当前代码模块的 dll
的版本号信息;或者指定路径下的 exe
/ dll
的版本号。
[TOC]
本文初发于 “偕臧的小站“,同步转载于此。
💻: win10 21H2
📎 Visual Studio 2019
方法一
使用 GetFileVersionInfoSize
、GetFileVersionInfo
、VerQueryValue
三个函数来获取版本信息。通过参考其它思路,后整合手写一份自己所需要的代码,不过这方式缺陷是获取的为当前 exe 的安装路径,需要是已知,手动传入此参数。
#include <string>
#include <tchar.h>
#include <iostream>
#pragma comment(lib, "version.lib")
// 获取当前项目的版本号
inline std::string getVersion()
{
std::string ret;
// 获取程序的自身路径
TCHAR productPath[MAX_PATH] = { 0 };
if (GetModuleFileName(nullptr, productPath, sizeof(productPath) / sizeof(char)) == 0)
return ret;
// 能否获取版本号
uint32_t size = 0;
size = GetFileVersionInfoSize(productPath, NULL);
if (size == 0)
return ret;
// 加载版本信息
BYTE* pData = new BYTE[size];
if (!GetFileVersionInfo(productPath, NULL, size, (LPVOID)pData)) {
//TRACE("GetFileVersionInfo failed with error %d\n", GetLastError());
return ret;
}
// 获取产品名和版本
LPVOID lpBuffer;
UINT uLength;
DWORD dwVerMS;
DWORD dwVerLS;
// "040904b0" 是对应 rc 中语言的 ID
if (VerQueryValue((LPVOID)pData, _T("\\"), &lpBuffer, &uLength)) {
dwVerMS = ((VS_FIXEDFILEINFO*)lpBuffer)->dwProductVersionMS;
dwVerLS = ((VS_FIXEDFILEINFO*)lpBuffer)->dwProductVersionLS;
} else {
//TRACE("Can't obtain ProductName and ProductVersion from resources\n");
return ret;
}
::VerQueryValue((LPCVOID)pData, _T("\\StringFileInfo\\040904b0\\ProductName"), &lpBuffer, &uLength);
std::cout << "================>" << (LPCTSTR)lpBuffer << " " << (int)uLength << std::endl;
ret = std::to_string(dwVerMS >> 16) + "." + std::to_string(dwVerMS & 0xFFFF) + "."
+ std::to_string(dwVerLS >> 16) + "." + std::to_string(dwVerLS & 0xFFFF);
return ret;
}
关于最后打印本版信息两行代码,进行位运算可见下图,最后打印为 10.1.0.2001
,基础知识遗忘可回顾 位操作 。
方法二『荐』
考虑到安装程序时可能为自定义安装路径,那么路径就未知了;方式一就不可用了。
此时采用 VirtualQuery(GetSelfModuleHandle, &mbi, sizeof(mbi))
则可以很确保获取当前代码运行的 dll
的相关句柄,然后进而获取期路径、模块名等信息;故而更推荐此方法;最后可添加 _WIN32
宏判断确保仅在 windows 下运行。
//-------------------------.h 声明--------------------------
// 获取当前调用 dll 版本号
#include <mutex>
#ifdef _WIN32
inline std::once_flag g_dllFlag;
inline bool LogGetModVer(std::wstring& sv, std::wstring& error);
#endif
//-------------------------.cpp 定义--------------------------
#ifdef _WIN32
inline HMODULE GetSelfModuleHandle() // 获取自身模块句柄
{
MEMORY_BASIC_INFORMATION mbi;
return ((::VirtualQuery(GetSelfModuleHandle, &mbi, sizeof(mbi)) != 0) ? (HMODULE)mbi.AllocationBase : NULL); // 取当前函数的函数地址,用地址找模块
}
bool LogGetModVer(std::wstring& sv, std::wstring& error)
{
wchar_t DllDir[512];
HMODULE hModule = GetSelfModuleHandle();
if (hModule == NULL) {
error = L"GetSelfModuleHandle() failed! Not found self module handle.";
return false;
}
// 获取模块名称和路径
GetModuleFileName(hModule, DllDir, 512);
std::wstring sFilePath(DllDir);
if (sFilePath.empty())
{
error = L"sFilePath is empty.";
return false;
}
... // dllPtr = 加载的 dll 模块指针
if (nullptr != dllPtr)
{
...
if (!dllPtr->OpenFile(sFilePath.c_str(), true)) {
error = L"dllPtr open failed.";
return false;
}
// 通过自己封装的 dllPtr 类型来获取当前具体版本
}
error = L"__GetModVer faild.";
return false;
}
#endif
//-------------------------调用示例--------------------------
#ifdef _WIN32
std::call_once(g_dllFlag, [=]() { // 背景可能是多线程下,仅运行一次即可
std::wstring sv;
std::wstring error;
if (LogGetModVer(sv, error))
{
std::wstring sVer;
if (!sv.empty()) {
sVer = std::wstring(L"模块版本号: ") + sv.c_str();
} else {
sVer = error;
}
// ... sVer 输出到日志
}
});
#endif
系列地址
欢迎 star
⭐ 和 fork
🍴 这个系列的 C++ / QT / DTK
学习,附学习由浅入深的目录,这里你可以学到如何亲自编写这类软件的经验,这是一系列完整的教程,并且永久免费!”