#include "pch.h" #include #include "tools.h" #include #include #include "StringHelper.h" #ifdef _AFXDLL #include "afxpriv.h" #endif #include #pragma comment(lib,"version.lib") #include #include #pragma comment(lib,"SetupAPI.lib") #include #include #include #include #include #pragma comment(lib, "Psapi.lib") #include // 调试打印函数 void OutputDebugPrintf(const char* strOutputString, ...) { #define PUT_PUT_DEBUG_BUF_LEN 1024 char strBuffer[PUT_PUT_DEBUG_BUF_LEN] = { 0 }; va_list vlArgs; va_start(vlArgs, strOutputString); _vsnprintf_s(strBuffer, sizeof(strBuffer) - 1, strOutputString, vlArgs); //_vsnprintf_s _vsnprintf va_end(vlArgs); OutputDebugStringA(strBuffer); //OutputDebugString // OutputDebugStringW } int64_t getTimeMsTick() { struct __timeb64 t; _ftime64_s(&t); return t.time * 1000 + t.millitm; } bool GetFileVersion(const wchar_t * sTargetFileName, std::wstring &verstr) { DWORD nInfoSize = 0, dwHandle = 0; // 获取版本信息表大小 nInfoSize = GetFileVersionInfoSize(sTargetFileName, &dwHandle); if (nInfoSize == 0) { return _T(""); } // 分配缓冲区,读版本信息 char* pInfoBuf = new char[nInfoSize]; GetFileVersionInfo(sTargetFileName, 0, nInfoSize, pInfoBuf); // 信息表中语言信息结构体 struct LANGANDCODEPAGE { WORD wLanguage; WORD wCodePage; }*pTranslate; UINT cbTranslate = 0; VerQueryValue(pInfoBuf, TEXT("\\VarFileInfo\\Translation"), (LPVOID*)&pTranslate, &cbTranslate); // Read the file description for each language and code page. for (int i = 0; i < (cbTranslate / sizeof(struct LANGANDCODEPAGE)); i++) { WCHAR SubBlock[256] = { 0 }; wsprintf(SubBlock, TEXT("\\StringFileInfo\\%04x%04x\\FileVersion"), pTranslate[i].wLanguage, pTranslate[i].wCodePage); WCHAR* pVersion = NULL; UINT nBytes = 0; VerQueryValue(pInfoBuf, SubBlock, (LPVOID*)&pVersion, &nBytes); verstr = pVersion; break; } delete[] pInfoBuf; return cbTranslate>0; } bool GetFileVersion(const wchar_t* sTargetFileName, sVersion& ver) { DWORD nInfoSize = 0, dwHandle = 0; // 获取版本信息表大小 nInfoSize = GetFileVersionInfoSize(sTargetFileName, &dwHandle); if (nInfoSize == 0) { return _T(""); } // 分配缓冲区,读版本信息 char* pInfoBuf = new char[nInfoSize]; GetFileVersionInfo(sTargetFileName, 0, nInfoSize, pInfoBuf); // 信息表中语言信息结构体 struct LANGANDCODEPAGE { WORD wLanguage; WORD wCodePage; }*pTranslate; UINT cbTranslate = 0; VerQueryValue(pInfoBuf, TEXT("\\VarFileInfo\\Translation"), (LPVOID*)&pTranslate, &cbTranslate); // Read the file description for each language and code page. for (int i = 0; i < (cbTranslate / sizeof(struct LANGANDCODEPAGE)); i++) { WCHAR SubBlock[256] = { 0 }; wsprintf(SubBlock, TEXT("\\StringFileInfo\\%04x%04x\\FileVersion"), pTranslate[i].wLanguage, pTranslate[i].wCodePage); WCHAR* pVersion = NULL; UINT nBytes = 0; VerQueryValue(pInfoBuf, SubBlock, (LPVOID*)&pVersion, &nBytes); ver.fromStr(pVersion); break; } delete[] pInfoBuf; return cbTranslate > 0; } bool GetFileVersion(const wchar_t* sTargetFileName, sVersion2& ver) { DWORD nInfoSize = 0, dwHandle = 0; // 获取版本信息表大小 nInfoSize = GetFileVersionInfoSize(sTargetFileName, &dwHandle); if (nInfoSize == 0) { return _T(""); } // 分配缓冲区,读版本信息 char* pInfoBuf = new char[nInfoSize]; GetFileVersionInfo(sTargetFileName, 0, nInfoSize, pInfoBuf); // 信息表中语言信息结构体 struct LANGANDCODEPAGE { WORD wLanguage; WORD wCodePage; }*pTranslate; UINT cbTranslate = 0; VerQueryValue(pInfoBuf, TEXT("\\VarFileInfo\\Translation"), (LPVOID*)&pTranslate, &cbTranslate); // Read the file description for each language and code page. for (int i = 0; i < (cbTranslate / sizeof(struct LANGANDCODEPAGE)); i++) { WCHAR SubBlock[256] = { 0 }; wsprintf(SubBlock, TEXT("\\StringFileInfo\\%04x%04x\\FileVersion"), pTranslate[i].wLanguage, pTranslate[i].wCodePage); WCHAR* pVersion = NULL; UINT nBytes = 0; VerQueryValue(pInfoBuf, SubBlock, (LPVOID*)&pVersion, &nBytes); ver.fromStr(pVersion); break; } delete[] pInfoBuf; return cbTranslate > 0; } bool GetProductVersion(const wchar_t* sTargetFileName, std::wstring& verstr) { DWORD nInfoSize = 0, dwHandle = 0; // 获取版本信息表大小 nInfoSize = GetFileVersionInfoSize(sTargetFileName, &dwHandle); if (nInfoSize == 0) { return _T(""); } // 分配缓冲区,读版本信息 char* pInfoBuf = new char[nInfoSize]; GetFileVersionInfo(sTargetFileName, 0, nInfoSize, pInfoBuf); // 信息表中语言信息结构体 struct LANGANDCODEPAGE { WORD wLanguage; WORD wCodePage; }*pTranslate; UINT cbTranslate = 0; VerQueryValue(pInfoBuf, TEXT("\\VarFileInfo\\Translation"), (LPVOID*)&pTranslate, &cbTranslate); // Read the file description for each language and code page. for (int i = 0; i < (cbTranslate / sizeof(struct LANGANDCODEPAGE)); i++) { WCHAR SubBlock[256] = { 0 }; wsprintf(SubBlock, TEXT("\\StringFileInfo\\%04x%04x\\ProductVersion"), pTranslate[i].wLanguage, pTranslate[i].wCodePage); WCHAR* pVersion = NULL; UINT nBytes = 0; VerQueryValue(pInfoBuf, SubBlock, (LPVOID*)&pVersion, &nBytes); verstr = pVersion; break; } delete[] pInfoBuf; return cbTranslate > 0; } bool GetProductVersion(const wchar_t* sTargetFileName, sVersion& ver) { DWORD nInfoSize = 0, dwHandle = 0; // 获取版本信息表大小 nInfoSize = GetFileVersionInfoSize(sTargetFileName, &dwHandle); if (nInfoSize == 0) { return _T(""); } // 分配缓冲区,读版本信息 char* pInfoBuf = new char[nInfoSize]; GetFileVersionInfo(sTargetFileName, 0, nInfoSize, pInfoBuf); // 信息表中语言信息结构体 struct LANGANDCODEPAGE { WORD wLanguage; WORD wCodePage; }*pTranslate; UINT cbTranslate = 0; VerQueryValue(pInfoBuf, TEXT("\\VarFileInfo\\Translation"), (LPVOID*)&pTranslate, &cbTranslate); // Read the file description for each language and code page. for (int i = 0; i < (cbTranslate / sizeof(struct LANGANDCODEPAGE)); i++) { WCHAR SubBlock[256] = { 0 }; wsprintf(SubBlock, TEXT("\\StringFileInfo\\%04x%04x\\ProductVersion"), pTranslate[i].wLanguage, pTranslate[i].wCodePage); WCHAR* pVersion = NULL; UINT nBytes = 0; VerQueryValue(pInfoBuf, SubBlock, (LPVOID*)&pVersion, &nBytes); ver.fromStr(pVersion); break; } delete[] pInfoBuf; return cbTranslate > 0; } bool GetProductVersion(const wchar_t* sTargetFileName, sVersion2& ver) { DWORD nInfoSize = 0, dwHandle = 0; // 获取版本信息表大小 nInfoSize = GetFileVersionInfoSize(sTargetFileName, &dwHandle); if (nInfoSize == 0) { return _T(""); } // 分配缓冲区,读版本信息 char* pInfoBuf = new char[nInfoSize]; GetFileVersionInfo(sTargetFileName, 0, nInfoSize, pInfoBuf); // 信息表中语言信息结构体 struct LANGANDCODEPAGE { WORD wLanguage; WORD wCodePage; }*pTranslate; UINT cbTranslate = 0; VerQueryValue(pInfoBuf, TEXT("\\VarFileInfo\\Translation"), (LPVOID*)&pTranslate, &cbTranslate); // Read the file description for each language and code page. for (int i = 0; i < (cbTranslate / sizeof(struct LANGANDCODEPAGE)); i++) { WCHAR SubBlock[256] = { 0 }; wsprintf(SubBlock, TEXT("\\StringFileInfo\\%04x%04x\\ProductVersion"), pTranslate[i].wLanguage, pTranslate[i].wCodePage); WCHAR* pVersion = NULL; UINT nBytes = 0; VerQueryValue(pInfoBuf, SubBlock, (LPVOID*)&pVersion, &nBytes); ver.fromStr(pVersion); break; } delete[] pInfoBuf; return cbTranslate > 0; } static bool GetModuleFileVersion(const wchar_t* modulePath, DWORD& major, DWORD& minor, DWORD& build, DWORD& revision) { DWORD handle = 0; DWORD verSize = GetFileVersionInfoSizeW(modulePath, &handle); if (verSize == 0) { return false; } std::vector verData(verSize); if (!GetFileVersionInfoW(modulePath, 0, verSize, verData.data())) { return false; } VS_FIXEDFILEINFO* pFileInfo = nullptr; UINT len = 0; if (!VerQueryValueW(verData.data(), L"\\", reinterpret_cast(&pFileInfo), &len)) { return false; } if (len == 0 || pFileInfo == nullptr) { return false; } major = HIWORD(pFileInfo->dwFileVersionMS); minor = LOWORD(pFileInfo->dwFileVersionMS); build = HIWORD(pFileInfo->dwFileVersionLS); revision = LOWORD(pFileInfo->dwFileVersionLS); return true; } // 检查当前进程所使用的 VC 运行库版本 int CheckVcRuntimeVersion(sVersion& Ver) { // 1. 枚举当前进程模块,找 VC 运行库 DLL HMODULE hMods[1024]; DWORD cbNeeded = 0; if (!EnumProcessModules(GetCurrentProcess(), hMods, sizeof(hMods), &cbNeeded)) { return -1; } #ifdef _DEBUG const wchar_t* targetNames[] = { L"vcruntime140d.dll", L"vcruntime140_1d.dll", L"msvcp140d.dll", L"vcruntime140_2d.dll" // 覆盖更多可能 }; #else const wchar_t* targetNames[] = { L"vcruntime140.dll", L"vcruntime140_1.dll", L"msvcp140.dll", L"vcruntime140_2.dll" // 覆盖更多可能 }; #endif wchar_t path[MAX_PATH] = {}; DWORD major = 0, minor = 0, build = 0, rev = 0; bool found = false; const UINT moduleCount = cbNeeded / sizeof(HMODULE); for (UINT i = 0; i < moduleCount && !found; ++i) { if (GetModuleFileNameW(hMods[i], path, MAX_PATH) == 0) { continue; } const wchar_t* fileName = wcsrchr(path, L'\\'); fileName = fileName ? fileName + 1 : path; for (auto name : targetNames) { if (_wcsicmp(fileName, name) == 0) { if (GetModuleFileVersion(path, major, minor, build, rev)) { found = true; } break; } } } if (!found) { // 没找到运行库 DLL return -1; } Ver.Major = static_cast(major); Ver.Mid = static_cast(minor); Ver.Minor = static_cast(build); return 1; } //SHBrowseForFolder可以用来得到一个用户选择的目录。 //可是有时候会有需要去指定一个初始目录, 比如希望上次用户选择的目录可以保存下来。这该如何去做? //在BROWSEINFO结构体中提供了一个成员,这是一个指向函数的指针,通过这个回调函数,可以处理初始化的时候需要做的一些事情。 static TCHAR g_szLastSelDir[MAX_PATH]; static int CALLBACK BrowseCallbackProc(HWND hwnd, UINT msg, LPARAM lp, LPARAM pData) { if (msg == BFFM_INITIALIZED) { ::SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)g_szLastSelDir); } return 0; } BOOL BrowseDirectory(HWND hwnd, LPTSTR lpszDir) { lstrcpyn(g_szLastSelDir, lpszDir, sizeof(g_szLastSelDir)/sizeof(TCHAR)); BROWSEINFO bi; bi.hwndOwner = hwnd; bi.pidlRoot = 0; bi.pszDisplayName = 0; bi.lpszTitle = L"Browse Directory"; bi.lpfn = BrowseCallbackProc; bi.lParam = 0; bi.ulFlags = BIF_STATUSTEXT | BIF_USENEWUI | BIF_RETURNONLYFSDIRS; LPITEMIDLIST pidl; if (pidl = SHBrowseForFolder(&bi)) { SHGetPathFromIDList(pidl, lpszDir); _tcscpy_s(g_szLastSelDir, MAX_PATH, lpszDir); return TRUE; } return FALSE; } #define STM32_CRC_DEF 0x04c11db7 //STM32硬件CRC计数等式 uint32_t crc32update_f4soft(uint32_t crc, uint32_t* pBuffer, uint32_t len) { uint32_t xbit = 0; //CRC计算式计算 扫描变量 uint32_t data = 0; //当前用于CRC的数据缓存 uint32_t bits = 0; //位计数变量 len /= 4; while (len--) { xbit = 0x80000000; data = *pBuffer++; for (bits = 0; bits < 32; bits++) { if (crc & 0x80000000) { //CRC计算式计算 crc <<= 1; crc ^= STM32_CRC_DEF; } else { crc <<= 1; } if (data & xbit) { //CRC计算式计算 crc ^= STM32_CRC_DEF; } xbit >>= 1; } } return crc; } uint32_t crc32calc_f4soft(uint32_t* pBuffer, uint32_t len) { return crc32update_f4soft(0xFFFFFFFF, pBuffer, len); } #include #include #include #pragma comment(lib, "wbemuuid.lib") int GetMotherboardAndBIOSInfo(std::wstring& Manufacturer, std::wstring& Model, std::wstring& BisoVer, bool needCoInit) { HRESULT hres; if (needCoInit) { // MFC程序默认已经初始化了COM,所以这里可以选择不初始化。 // 初始化COM. hres = CoInitializeEx(0, COINIT_MULTITHREADED); if (FAILED(hres)) { switch (hres) { case S_FALSE: std::wcout << L"CoInitializeEx again. " << std::endl; break; case RPC_E_CHANGED_MODE: std::wcout << L"CoInitializeEx failed for changed mode " << std::endl; break; default: std::wcout << L"CoInitializeEx failed with HRESULT: " << std::hex << hres << std::endl; break; } return -1; // 如果失败,直接返回 } } hres = CoInitializeSecurity( NULL, -1, // COM认证的用户级别 NULL, // 授权服务的ID列表 NULL, // 保留 RPC_C_AUTHN_LEVEL_DEFAULT, // 默认的认证级别 RPC_C_IMP_LEVEL_IMPERSONATE, // 模拟级别的认证 NULL, // 身份验证服务的权限 EOAC_NONE, // 额外的客户端权限 NULL // 保留 ); if (FAILED(hres)) { if (needCoInit) CoUninitialize(); return -2; } IWbemLocator* pLoc = NULL; hres = CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLoc); if (FAILED(hres)) { if (needCoInit) CoUninitialize(); return -3; } IWbemServices* pSvc = NULL; hres = pLoc->ConnectServer( _bstr_t(L"ROOT\\CIMV2"), // WMI命名空间 NULL, // 用户名 NULL, // 密码 0, // 其他选项 NULL, // 上下文 0, // 启动标志 0, // 保留 &pSvc // IWbemServices proxy ); if (FAILED(hres)) { pLoc->Release(); if (needCoInit) CoUninitialize(); return -4; } hres = CoSetProxyBlanket( pSvc, // WMI代理 RPC_C_AUTHN_WINNT, // 认证服务 RPC_C_AUTHZ_NONE, // 授权服务 NULL, // 服务器原则名称 RPC_C_AUTHN_LEVEL_CALL, // 认证级别 RPC_C_IMP_LEVEL_IMPERSONATE, // 模拟级别 NULL, // 客户端身份 EOAC_NONE // 代理能力 ); if (FAILED(hres)) { pSvc->Release(); pLoc->Release(); if (needCoInit) CoUninitialize(); return -5; } IEnumWbemClassObject* pEnumerator = NULL; hres = pSvc->ExecQuery( bstr_t("WQL"), bstr_t("SELECT * FROM Win32_BaseBoard"), // 查询主板信息 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); if (SUCCEEDED(hres)) { IWbemClassObject* pclsObj = NULL; ULONG uReturn = 0; while (pEnumerator) { HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); if (0 == uReturn) break; VARIANT vtProp; hr = pclsObj->Get(L"Manufacturer", 0, &vtProp, 0, 0); Manufacturer = vtProp.bstrVal; // 获取主板制造商 VariantClear(&vtProp); hr = pclsObj->Get(L"Product", 0, &vtProp, 0, 0); Model = vtProp.bstrVal; // 获取主板型号 VariantClear(&vtProp); pclsObj->Release(); } } else { pSvc->Release(); pLoc->Release(); if (needCoInit) CoUninitialize(); return -6; } hres = pSvc->ExecQuery( bstr_t("WQL"), bstr_t("SELECT * FROM Win32_BIOS"), // 查询BIOS信息 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); if (SUCCEEDED(hres)) { IWbemClassObject* pclsObj = NULL; ULONG uReturn = 0; while (pEnumerator) { HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); if (0 == uReturn) break; VARIANT vtProp; hr = pclsObj->Get(L"SMBIOSBIOSVersion", 0, &vtProp, 0, 0); BisoVer = vtProp.bstrVal; // 获取BIOS版本 VariantClear(&vtProp); pclsObj->Release(); } } else { pSvc->Release(); pLoc->Release(); if (needCoInit) CoUninitialize(); return -7; } // 清理 pSvc->Release(); pLoc->Release(); pEnumerator->Release(); if (needCoInit)CoUninitialize(); return 0; // 成功 } // 在binPath目录下找vc_redist.x64*.exe,获取版本最高的那个 bool installVcRuntime(const fs::path& binPath, const sVersion& minVer) { fs::path highestRedist; sVersion highestVer = minVer; // 初始为编译版本 if (fs::exists(binPath) && fs::is_directory(binPath)) { for (const auto& entry : fs::directory_iterator(binPath)) { if (entry.is_regular_file()) { std::wstring filename = entry.path().filename().wstring(); // 匹配 vc_redist.x64 开头的 .exe 文件 if (filename.find(L"vc_redist.x64") == 0 && entry.path().extension() == L".exe") { sVersion fileVer; if (GetFileVersion(entry.path().wstring().c_str(), fileVer)) { if (fileVer > highestVer) { highestVer = fileVer; highestRedist = entry.path(); } } } } } } std::wstring message = L"检测到VC++运行库版本过低,建议更新到14.44及以上版本,以保证软件稳定运行。\n"; if (!highestRedist.empty()) { message += fmt::format(L"\n在软件目录下找到运行库安装包(版本{}.{}.{}):\n{}\n\n是否立即安装?\n\n", highestVer.Major, highestVer.Mid, highestVer.Minor, highestRedist.filename().wstring()); message += L"点击【是】立即安装,点击【否】退出程序手动下载安装。"; int result = MessageBox(nullptr, message.c_str(), L"运行库版本过低", MB_ICONWARNING | MB_YESNO); if (result == IDYES) { // 启动安装程序并等待其执行完成 SHELLEXECUTEINFOW sei = {}; sei.cbSize = sizeof(sei); sei.fMask = SEE_MASK_NOCLOSEPROCESS; sei.hwnd = nullptr; sei.lpVerb = L"open"; sei.lpFile = highestRedist.c_str(); sei.lpParameters = L"/install /quiet /norestart"; sei.lpDirectory = nullptr; sei.nShow = SW_SHOWNORMAL; if (ShellExecuteExW(&sei)) { if (sei.hProcess != nullptr) { // 等待安装程序执行完 WaitForSingleObject(sei.hProcess, INFINITE); CloseHandle(sei.hProcess); } MessageBox(nullptr, L"VC++ 运行库安装完成,为保证软件稳定运行,建议重启电脑后再使用本软件。", L"安装完成", MB_ICONINFORMATION | MB_OK); return true; } else { MessageBox(nullptr, L"无法启动 VC++ 运行库安装程序,请手动在 bin 目录或公司钉盘中运行安装包。", L"安装失败", MB_ICONERROR | MB_OK); } } else { } } else { message += L"请在公司钉盘\"软件发布 / 运行库\"目录下载安装最新的VC运行库。\n" L"或微软下载:https://aka.ms/vc14/vc_redist.x64.exe"; MessageBox(nullptr, message.c_str(), L"运行库版本过低", MB_ICONWARNING); } return false; }