简 述: 获取 windows 下当前代码模块的 dll 的版本号信息;或者指定路径下的 exe / dll 的版本号。

[TOC]

本文初发于 “偕臧的小站“,同步转载于此。

💻: win10 21H2 📎 Visual Studio 2019

方法一 使用 GetFileVersionInfoSize、GetFileVersionInfo、VerQueryValue 三个函数来获取版本信息。通过参考其它思路,后整合手写一份自己所需要的代码,不过这方式缺陷是获取的为当前 exe 的安装路径,需要是已知,手动传入此参数。

获取EXE版本信息 GetFileVersionInfo

如何使用API函数GetFileVersionInfo,获得版本信息

#include

#include

#include

#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

#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

系列地址QtExamples

欢迎 star ⭐ 和 fork 🍴 这个系列的 C++ / QT / DTK 学习,附学习由浅入深的目录,这里你可以学到如何亲自编写这类软件的经验,这是一系列完整的教程,并且永久免费!”