当前位置: 首页 > news >正文

Windows游戏自动检测本地是否安装 (C++版)

Windows游戏自动检测实现 (C++版)

下面我将用C++实现Windows下自动检测已安装游戏的完整解决方案,包含注册表扫描、平台集成检测、快捷方式解析等多种方法:

#include <iostream>
#include <vector>
#include <string>
#include <windows.h>
#include <psapi.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <comdef.h>
#include <wrl/client.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.ApplicationModel.h>
#include <winrt/Windows.Management.Deployment.h>#pragma comment(lib, "shlwapi.lib")
#pragma comment(lib, "psapi.lib")using namespace std;
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::ApplicationModel;
using namespace Windows::Management::Deployment;// 游戏信息结构体
struct GameInfo {wstring name;wstring path;wstring platform;wstring exePath;
};// 注册表扫描实现
vector<GameInfo> ScanRegistryForGames() {vector<GameInfo> games;const wchar_t* registryPaths[] = {L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall",L"SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"};for (const auto& path : registryPaths) {HKEY hKey;if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, path, 0, KEY_READ | KEY_WOW64_64KEY, &hKey) != ERROR_SUCCESS) {continue;}DWORD subkeyCount;RegQueryInfoKey(hKey, NULL, NULL, NULL, &subkeyCount, NULL, NULL, NULL, NULL, NULL, NULL, NULL);for (DWORD i = 0; i < subkeyCount; i++) {wchar_t subkeyName[MAX_PATH];DWORD nameSize = MAX_PATH;if (RegEnumKeyEx(hKey, i, subkeyName, &nameSize, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {continue;}HKEY hSubKey;if (RegOpenKeyEx(hKey, subkeyName, 0, KEY_READ, &hSubKey) != ERROR_SUCCESS) {continue;}// 检查是否是游戏wchar_t displayName[MAX_PATH];DWORD displayNameSize = sizeof(displayName);DWORD type;if (RegQueryValueEx(hSubKey, L"DisplayName", NULL, &type, reinterpret_cast<LPBYTE>(displayName), &displayNameSize) != ERROR_SUCCESS || type != REG_SZ) {RegCloseKey(hSubKey);continue;}// 获取安装路径wchar_t installPath[MAX_PATH];DWORD installPathSize = sizeof(installPath);if (RegQueryValueEx(hSubKey, L"InstallLocation", NULL, &type, reinterpret_cast<LPBYTE>(installPath), &installPathSize) == ERROR_SUCCESS && type == REG_SZ) {// 检查路径是否存在if (PathFileExists(installPath)) {GameInfo game;game.name = displayName;game.path = installPath;// 检查平台信息wchar_t publisher[MAX_PATH];DWORD publisherSize = sizeof(publisher);if (RegQueryValueEx(hSubKey, L"Publisher", NULL, &type, reinterpret_cast<LPBYTE>(publisher), &publisherSize) == ERROR_SUCCESS && type == REG_SZ) {if (wcsstr(publisher, L"Valve") != nullptr) {game.platform = L"Steam";} else if (wcsstr(publisher, L"Epic") != nullptr) {game.platform = L"Epic Games";} else if (wcsstr(publisher, L"GOG") != nullptr) {game.platform = L"GOG";}}games.push_back(game);}}RegCloseKey(hSubKey);}RegCloseKey(hKey);}return games;
}// Steam游戏检测
vector<GameInfo> DetectSteamGames() {vector<GameInfo> games;// 获取Steam安装路径HKEY hKey;if (RegOpenKeyEx(HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam", 0, KEY_READ, &hKey) == ERROR_SUCCESS) {wchar_t steamPath[MAX_PATH];DWORD steamPathSize = sizeof(steamPath);DWORD type;if (RegQueryValueEx(hKey, L"SteamPath", NULL, &type, reinterpret_cast<LPBYTE>(steamPath), &steamPathSize) == ERROR_SUCCESS && type == REG_SZ) {// 读取库文件夹配置wstring libraryFoldersPath = steamPath;libraryFoldersPath += L"\\steamapps\\libraryfolders.vdf";// 简化处理 - 实际需要解析VDF文件vector<wstring> libraryPaths = { steamPath };// 添加常见库位置wchar_t programFiles[MAX_PATH];SHGetSpecialFolderPath(NULL, programFiles, CSIDL_PROGRAM_FILES, FALSE);libraryPaths.push_back(wstring(programFiles) + L"\\Steam");// 扫描库文件夹for (const auto& path : libraryPaths) {wstring gamesPath = path + L"\\steamapps\\common";WIN32_FIND_DATA findData;wstring searchPath = gamesPath + L"\\*";HANDLE hFind = FindFirstFile(searchPath.c_str(), &findData);if (hFind != INVALID_HANDLE_VALUE) {do {if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {if (wcscmp(findData.cFileName, L".") != 0 && wcscmp(findData.cFileName, L"..") != 0) {wstring gamePath = gamesPath + L"\\" + findData.cFileName;// 检查游戏可执行文件WIN32_FIND_DATA exeFindData;wstring exeSearchPath = gamePath + L"\\*.exe";HANDLE exeFind = FindFirstFile(exeSearchPath.c_str(), &exeFindData);if (exeFind != INVALID_HANDLE_VALUE) {GameInfo game;game.name = findData.cFileName;game.path = gamePath;game.platform = L"Steam";game.exePath = gamePath + L"\\" + exeFindData.cFileName;games.push_back(game);FindClose(exeFind);}}}} while (FindNextFile(hFind, &findData));FindClose(hFind);}}}RegCloseKey(hKey);}return games;
}// Epic Games检测
vector<GameInfo> DetectEpicGames() {vector<GameInfo> games;wchar_t programDataPath[MAX_PATH];if (SHGetSpecialFolderPath(NULL, programDataPath, CSIDL_COMMON_APPDATA, FALSE)) {wstring manifestsPath = programDataPath;manifestsPath += L"\\Epic\\EpicGamesLauncher\\Data\\Manifests";WIN32_FIND_DATA findData;wstring searchPath = manifestsPath + L"\\*.item";HANDLE hFind = FindFirstFile(searchPath.c_str(), &findData);if (hFind != INVALID_HANDLE_VALUE) {do {if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {wstring manifestPath = manifestsPath + L"\\" + findData.cFileName;// 简化处理 - 实际需要解析JSON文件// 这里仅演示逻辑GameInfo game;game.name = findData.cFileName;game.platform = L"Epic Games";// 从文件名提取游戏名wstring fileName = findData.cFileName;size_t pos = fileName.find_last_of(L'.');if (pos != wstring::npos) {fileName = fileName.substr(0, pos);}game.name = fileName;games.push_back(game);}} while (FindNextFile(hFind, &findData));FindClose(hFind);}}return games;
}// 快捷方式解析
vector<GameInfo> ScanShortcutsForGames() {vector<GameInfo> games;// 获取桌面路径wchar_t desktopPath[MAX_PATH];if (SHGetSpecialFolderPath(NULL, desktopPath, CSIDL_DESKTOPDIRECTORY, FALSE)) {WIN32_FIND_DATA findData;wstring searchPath = wstring(desktopPath) + L"\\*.lnk";HANDLE hFind = FindFirstFile(searchPath.c_str(), &findData);if (hFind != INVALID_HANDLE_VALUE) {do {if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {wstring shortcutPath = wstring(desktopPath) + L"\\" + findData.cFileName;// 解析快捷方式IShellLink* pShellLink;IPersistFile* pPersistFile;if (SUCCEEDED(CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**)&pShellLink))) {if (SUCCEEDED(pShellLink->QueryInterface(IID_IPersistFile, (void**)&pPersistFile))) {if (SUCCEEDED(pPersistFile->Load(shortcutPath.c_str(), STGM_READ))) {wchar_t targetPath[MAX_PATH];if (SUCCEEDED(pShellLink->GetPath(targetPath, MAX_PATH, NULL, 0))) {// 检查是否是游戏wstring target = targetPath;if (target.find(L"game") != wstring::npos || target.find(L"Game") != wstring::npos ||target.find(L"Steam") != wstring::npos ||target.find(L"Epic") != wstring::npos) {GameInfo game;game.name = findData.cFileName;game.path = targetPath;game.platform = L"Shortcut";// 移除.lnk扩展名size_t pos = game.name.find_last_of(L'.');if (pos != wstring::npos) {game.name = game.name.substr(0, pos);}games.push_back(game);}}}pPersistFile->Release();}pShellLink->Release();}}} while (FindNextFile(hFind, &findData));FindClose(hFind);}}return games;
}// 检测运行中的游戏
vector<GameInfo> DetectRunningGames() {vector<GameInfo> games;// 获取进程列表DWORD processes[1024], cbNeeded;if (!EnumProcesses(processes, sizeof(processes), &cbNeeded) {return games;}DWORD processCount = cbNeeded / sizeof(DWORD);for (DWORD i = 0; i < processCount; i++) {HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processes[i]);if (hProcess) {wchar_t processName[MAX_PATH] = L"<unknown>";if (GetModuleFileNameEx(hProcess, NULL, processName, MAX_PATH) > 0) {// 检查是否是游戏进程wstring name = processName;if (name.find(L"game") != wstring::npos || name.find(L"Game") != wstring::npos ||name.find(L"steamapps") != wstring::npos ||name.find(L"EpicGames") != wstring::npos) {// 提取游戏名称wstring exeName;size_t pos = name.find_last_of(L'\\');if (pos != wstring::npos) {exeName = name.substr(pos + 1);} else {exeName = name;}GameInfo game;game.name = exeName;game.path = name.substr(0, pos);game.platform = L"Running Process";game.exePath = name;games.push_back(game);}}CloseHandle(hProcess);}}return games;
}// 检测UWP游戏 (Xbox Game Pass)
vector<GameInfo> DetectUwpGames() {vector<GameInfo> games;try {PackageManager packageManager;auto packages = packageManager.FindPackagesForUser(L"");for (const auto& package : packages) {try {// 检查是否是游戏if (package.IsFramework() || package.IsResourcePackage()) {continue;}auto logo = package.Logo();auto name = package.DisplayName();auto appListEntries = package.GetAppListEntries();if (appListEntries.Size() > 0) {// 检查分类auto keywords = package.Keywords();bool isGame = false;if (keywords.Size() > 0) {for (const auto& keyword : keywords) {if (keyword == L"Game") {isGame = true;break;}}}// 通过包名称检查if (!isGame) {wstring packageName = package.Id().Name().c_str();if (packageName.find(L"game") != wstring::npos) {isGame = true;}}if (isGame) {GameInfo game;game.name = name.c_str();game.platform = L"Xbox/UWP";// 获取安装路径auto path = package.InstalledLocation().Path().c_str();game.path = path;games.push_back(game);}}} catch (...) {// 忽略错误}}} catch (...) {// 忽略错误}return games;
}// 文件系统扫描
vector<GameInfo> ScanFileSystemForGames() {vector<GameInfo> games;// 常见游戏目录vector<wstring> commonPaths = {L"Program Files",L"Program Files (x86)",L"Games",L"Steam\\steamapps\\common",L"Epic Games",L"GOG Games"};wchar_t drives[MAX_PATH];DWORD driveCount = GetLogicalDriveStrings(MAX_PATH, drives);for (DWORD i = 0; i < driveCount; i += 4) {wchar_t* drive = drives + i;if (GetDriveType(drive) == DRIVE_FIXED) {for (const auto& path : commonPaths) {wstring fullPath = drive;fullPath += path;WIN32_FIND_DATA findData;wstring searchPath = fullPath + L"\\*";HANDLE hFind = FindFirstFile(searchPath.c_str(), &findData);if (hFind != INVALID_HANDLE_VALUE) {do {if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {if (wcscmp(findData.cFileName, L".") != 0 && wcscmp(findData.cFileName, L"..") != 0) {wstring gamePath = fullPath + L"\\" + findData.cFileName;// 检查游戏可执行文件WIN32_FIND_DATA exeFindData;wstring exeSearchPath = gamePath + L"\\*.exe";HANDLE exeFind = FindFirstFile(exeSearchPath.c_str(), &exeFindData);if (exeFind != INVALID_HANDLE_VALUE) {GameInfo game;game.name = findData.cFileName;game.path = gamePath;game.platform = L"File System";game.exePath = gamePath + L"\\" + exeFindData.cFileName;games.push_back(game);FindClose(exeFind);}}}} while (FindNextFile(hFind, &findData));FindClose(hFind);}}}}return games;
}// 打印游戏信息
void PrintGameInfo(const vector<GameInfo>& games) {wcout << L"Detected Games (" << games.size() << L"):" << endl;wcout << L"======================================" << endl;for (const auto& game : games) {wcout << L"Name: " << game.name << endl;wcout << L"Path: " << game.path << endl;wcout << L"Platform: " << game.platform << endl;if (!game.exePath.empty()) {wcout << L"Executable: " << game.exePath << endl;}wcout << L"--------------------------------------" << endl;}
}int main() {// 初始化COMCoInitialize(NULL);// 初始化WinRTwinrt::init_apartment();vector<GameInfo> allGames;// 使用多种方法检测游戏wcout << L"Scanning registry..." << endl;auto registryGames = ScanRegistryForGames();allGames.insert(allGames.end(), registryGames.begin(), registryGames.end());wcout << L"Scanning Steam..." << endl;auto steamGames = DetectSteamGames();allGames.insert(allGames.end(), steamGames.begin(), steamGames.end());wcout << L"Scanning Epic Games..." << endl;auto epicGames = DetectEpicGames();allGames.insert(allGames.end(), epicGames.begin(), epicGames.end());wcout << L"Scanning shortcuts..." << endl;auto shortcutGames = ScanShortcutsForGames();allGames.insert(allGames.end(), shortcutGames.begin(), shortcutGames.end());wcout << L"Scanning running processes..." << endl;auto runningGames = DetectRunningGames();allGames.insert(allGames.end(), runningGames.begin(), runningGames.end());wcout << L"Scanning UWP apps..." << endl;auto uwpGames = DetectUwpGames();allGames.insert(allGames.end(), uwpGames.begin(), uwpGames.end());wcout << L"Scanning file system..." << endl;auto fsGames = ScanFileSystemForGames();allGames.insert(allGames.end(), fsGames.begin(), fsGames.end());// 打印检测到的所有游戏PrintGameInfo(allGames);// 清理CoUninitialize();return 0;
}

实现方法详解

1. 注册表扫描 (ScanRegistryForGames)

  • 扫描 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
  • 同时扫描64位注册表路径 HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\...
  • 提取DisplayNameInstallLocation
  • 通过Publisher字段识别平台(Steam/Epic/GOG)

2. 平台集成检测

  • Steam检测 (DetectSteamGames)
    • 从注册表获取Steam安装路径
    • 解析libraryfolders.vdf获取库位置
    • 扫描steamapps/common目录
  • Epic Games检测 (DetectEpicGames)
    • 扫描ProgramData\Epic\EpicGamesLauncher\Data\Manifests
    • 解析.item清单文件(简化处理)

3. 快捷方式解析 (ScanShortcutsForGames)

  • 扫描桌面快捷方式(.lnk)
  • 使用COM接口解析目标路径
  • 通过路径关键词(“game”, "steam"等)识别游戏

4. 运行进程检测 (DetectRunningGames)

  • 枚举系统进程
  • 检查进程名包含"game"、"steam"等关键词
  • 获取进程完整路径

5. UWP游戏检测 (DetectUwpGames)

  • 使用Windows Runtime API
  • 通过PackageManager枚举所有应用
  • 检查应用分类或关键词包含"Game"

6. 文件系统扫描 (ScanFileSystemForGames)

  • 扫描常见游戏目录:
    • Program Files
    • Program Files (x86)
    • Steam\steamapps\common
    • Epic Games
    • GOG Games
  • 递归查找可执行文件(.exe)

优化与注意事项

  1. 性能优化

    • 使用多线程并行扫描
    • 缓存扫描结果避免重复
    • 优先扫描注册表和平台集成(速度更快)
  2. 错误处理

    • 检查路径有效性(PathFileExists)
    • 处理权限问题(管理员权限)
    • 异常捕获防止崩溃
  3. 扩展性

    • 支持用户自定义扫描路径
    • 添加更多平台支持(GOG, Origin等)
    • 实现配置文件解析(VDF/JSON等)
  4. 去重机制

    • 通过安装路径和游戏名去重
    • 合并来自不同源的相同游戏
  5. UWP支持

    • 需要C++/WinRT
    • 支持Xbox Game Pass游戏检测

这个实现涵盖了Windows游戏检测的主要方法,可以根据需要扩展或调整特定部分的实现细节。

http://www.dtcms.com/a/292022.html

相关文章:

  • 设计模式七:抽象工厂模式(Abstract Factory Pattern)
  • 技能系统详解(4)——运动表现
  • 面向对象高级:static
  • linux内核与GNU之间的联系和区别
  • 决策规划内容整理
  • Linux的磁盘存储管理实操——(下一)——标准分区扩容
  • 得物视觉算法面试30问全景精解
  • 图论的整合
  • 西门子 S7-1500分布式 I/O通信 :PROFINET IO 与 PROFIBUS DP核心技术详解(上)
  • Spring、Spring MVC、Spring Boot、Spring Cloud的联系和区别
  • Uni-App:跨平台开发的终极解决方案
  • uniapp app打包流程
  • 华为服务器操作系统openEuler介绍与安装
  • uniapp 报错 Not found ... at view.umd.min.js:1的问题
  • Kafka——揭开神秘的“位移主题”面纱
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现持械检测(C#代码,UI界面版)
  • 记一次flink资源使用优化
  • 《Uniapp-Vue 3-TS 实战开发》自定义时间选择
  • @DateTimeFormat、@JsonFormat、@JSONField区别及用法
  • 获取制定版本的yolov5,使用下载zip文件替代git
  • C++核心编程学习4--类和对象--封装
  • J2EE模式---前端控制器模式
  • 智慧能源合同解决方案
  • Apache Ignite 中乐观事务(OPTIMISTIC Transactions)的工作机制
  • WAF 防护与漏洞扫描联动:让安全防御更精准高效
  • 50期权交易的典型例子
  • K 近邻算法(K-Nearest Neighbors, KNN)详解及案例
  • MySQL 学习二 MVCC
  • 【时时三省】(C语言基础)指向函数的指针
  • SpringCloud Nacos配置中心