实现组件存储 WinSxS 文件夹解析

目录

背景

目录名的组成

解析目录结构

更新&总结


文章出处链接:[https://blog.csdn.net/qq_59075481/article/details/140385969].

背景

 WinSxS 文件夹位于 Windows 文件夹中,例如 C: \Windows\WinSxS。它是 Windows 组件存储文件的位置。 Windows 组件存储用于支持自定义和更新 Windows 所需的函数。 Windows 组件存储是首先在 Windows XP 中引入的,用于支持并行程序集。从 Windows Vista 开始,组件存储得到了增强,可以跟踪构成操作系统的所有组件并为其提供服务。

这些不同的操作系统组件可以跟踪对象,例如文件、目录、注册表项和服务。然后,系统会将特定版本的组件收集到包中。

 Windows 更新和 DISM 使用包来更新 Windows。 Windows 组件存储将处理 Windows 安装中使用的组件和包。确定 Windows 组件存储大小的过程会很复杂,因为 Windows 通过名为硬链接的技术使用的许多文件都来自 Windows 组件存储外的目录。在这种情况下,某个组件版本中的文件会同时出现在 Windows 组件存储的内部和外部。使用硬链接,Windows 就可以在看起来并不实际占用为多个副本提供的额外空间的情况下,保留同一文件的多个副本。

组件存储文件夹中的文件目录比较复杂,有些时候需要枚举或者检索目录中某个组件的特定版本的副本会很麻烦。在这篇文章中,我将提供一个解析组件存储文件夹的初步程序。包括相关测试示例。

目录名的组成

组件存储的目录名包含多个字段的信息,每个信息之间使用下划线("_")间隔开来,按照顺序包括:程序运行系统架构、组件名称、组件签名、更新时文件系统版本号、语言、文件存储签名。

系统架构有两种:amd64 、 x86,分别代表:适用于 x64 、 x32 的操作系统。

组件名称是该组件的描述名称(当目录名称过长时,采用.. 缩略名称的中间内容,如果是存储的组件的纯资源文件,则后缀 ".resources")

组件签名和文件存储签名都是一段校验码。

版本号包含 4 个部分,结构为:

// 版本信息结构体
struct VersionInfo {
    int major;
    int minor;
    int build;
    int revision;
};

语言可以是:zh-cn、en-us 等等,混合多个目录文件的,一般语言字段填写 none。

下面是一个标准的样例: 

amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.22621.3672_none_2713b9d173822955

解析目录结构

由于我目前还没有找到相关的 API 来解析目录结构,所以,使用正则表达式编写了一个程序用于解析目录。

提供三个主要函数:

  • listWinSxSAssemblies:解析 WinSxS 整个目录结构,并使用文件流存储解析结果。
  • getAssembliesByComponentName:根据组件名称,检索所有匹配到的存储条目信息。
  • getLatestAssemblyByArchitecture:根据组件名称和适用的系统架构,查找符合条件且版本号最高的存储目录名称。

代码如下:

// EnumWinSxS.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <windows.h>
#include <iostream>
#include <fstream>
#include <string>
#include <regex>
#include <vector>
#include <tuple>
#include <algorithm>
#include <sstream>

// 版本信息结构体
struct VersionInfo {
    int major;
    int minor;
    int build;
    int revision;
};

// 解析特征字符串
struct AssemblyInfo {
    std::wstring architecture;
    std::wstring componentName;
    std::wstring componentSign;
    VersionInfo version;
    std::wstring language;
    std::wstring folderSign;
    bool isResource;
};

// 解析版本信息字符串
VersionInfo parseVersionInfo(const std::wstring& versionStr) {
    VersionInfo version = { 0, 0, 0, 0 };
    std::wregex versionPattern(LR"((\d+)\.(\d+)\.(\d+)\.(\d+))");
    std::wsmatch match;
    if (std::regex_match(versionStr, match, versionPattern)) {
        version.major = std::stoi(match[1]);
        version.minor = std::stoi(match[2]);
        version.build = std::stoi(match[3]);
        version.revision = std::stoi(match[4]);
    }
    return version;
}

// 解析 WinSxS 目录名称并返回 AssemblyInfo 结构体
AssemblyInfo parseAssemblyInfo(const std::wstring& dirName) {
    AssemblyInfo info;
    //std::wregex regexPattern(LR"((amd64|x86)_([a-zA-Z0-9.-]+)_(\w+)_((\d+\.\d+\.\d+\.\d+))_((?:[a-z]{2}-[a-z]{2}|none))_(\w+))");
    std::wregex regexPattern(LR"((amd64|x86)_([a-zA-Z0-9.-]+)_(\w+)_(\d+\.\d+\.\d+\.\d+)_((?:[a-z]{2}-[a-z]{2}|none))_(\w+))");
    std::wsmatch match;
    if (std::regex_match(dirName, match, regexPattern)) {
        info.architecture = match[1];
        info.componentName = match[2];
        info.componentSign = match[3];
        info.version = parseVersionInfo(match[4]); // 解析版本信息
        info.language = match[5];
        info.folderSign = match[6];
        info.isResource = (dirName.find(L".resources") != std::wstring::npos);
        if (info.isResource) {
            size_t pos = info.componentName.find(L".resources");
            if (pos != std::wstring::npos) {
                info.componentName.erase(pos, std::wstring::npos);
            }
        }
    }
    return info;
}

// 比较版本信息的函数
bool compareVersions(const VersionInfo& v1, const VersionInfo& v2) {
    return std::tie(v1.major, v1.minor, v1.build, v1.revision) < std::tie(v2.major, v2.minor, v2.build, v2.revision);
}

// 将版本信息合并为一个完整字符串
std::wstring versionInfoToString(const VersionInfo& version) {
    std::wostringstream oss;
    oss << version.major << L"." << version.minor << L"." << version.build << L"." << version.revision;
    return oss.str();
}

//std::vector<std::wstring> listFilesInDirectory(const std::wstring& directory) {
//    std::vector<std::wstring> files;
//    WIN32_FIND_DATAW findFileData;
//    std::wstring searchPath = directory + L"\\*";
//    HANDLE hFind = FindFirstFileW(searchPath.c_str(), &findFileData);
//
//    if (hFind == INVALID_HANDLE_VALUE) {
//        return files;
//    }
//
//    do {
//        const std::wstring fileName = findFileData.cFileName;
//        if (!(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
//            files.push_back(directory + L"\\" + fileName);
//        }
//    } while (FindNextFileW(hFind, &findFileData) != 0);
//
//    FindClose(hFind);
//    return files;
//}

void listFilesRecursively(const std::wstring& directory, std::wofstream& outFile, const std::wstring& baseDir) {
    WIN32_FIND_DATAW findFileData;
    std::wstring searchPath = directory + L"\\*";
    HANDLE hFind = FindFirstFileW(searchPath.c_str(), &findFileData);

    if (hFind == INVALID_HANDLE_VALUE) {
        return;
    }

    do {
        const std::wstring fileName = findFileData.cFileName;
        if (fileName == L"." || fileName == L"..") {
            continue;
        }

        std::wstring fullPath = directory + L"\\" + fileName;
        std::wstring relPath = fullPath.substr(baseDir.length() + 1);

        if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            listFilesRecursively(fullPath, outFile, baseDir);
        }
        else {
            std::wcout << L"  File: " << relPath << std::endl;
            outFile << L"  File: " << relPath << std::endl;
        }
    } while (FindNextFileW(hFind, &findFileData) != 0);

    FindClose(hFind);
}


void listWinSxSAssemblies(const std::wstring& winSxsPath, std::wofstream& outFile) {
    WIN32_FIND_DATAW findFileData;
    std::wstring searchPath = winSxsPath + L"\\*";
    HANDLE hFind = FindFirstFileW(searchPath.c_str(), &findFileData);

    if (hFind == INVALID_HANDLE_VALUE) {
        std::wcerr << L"Error: Unable to open directory " << winSxsPath << std::endl;
        return;
    }

    do {
        const std::wstring dirName = findFileData.cFileName;
        if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            // Skip "." and ".." directories
            if (dirName == L"." || dirName == L"..") {
                continue;
            }

            AssemblyInfo info = parseAssemblyInfo(dirName);
            if (!info.architecture.empty()) {
                std::wcout << L"Directory: " << dirName << std::endl;
                outFile << L"Directory: " << dirName << std::endl;

                std::wcout << L"Architecture: " << info.architecture << std::endl;
                outFile << L"Architecture: " << info.architecture << std::endl;

                std::wcout << L"Component Name: " << info.componentName << std::endl;
                outFile << L"Component Name: " << info.componentName << std::endl;

                std::wcout << L"Component Signature: " << info.componentSign << std::endl;
                outFile << L"Component Signature: " << info.componentSign << std::endl;

                std::wcout << L"Version: " << versionInfoToString(info.version) << std::endl;
                outFile << L"Version: " << versionInfoToString(info.version) << std::endl;

                //std::wcout << L"Version: " << info.version.major << L"." << info.version.minor << L"." 
                //    << info.version.build << L"." << info.version.revision << std::endl;
                //outFile << L"Version: " << info.version.major << L"." << info.version.minor << L"." 
                //    << info.version.build << L"." << info.version.revision << std::endl;

                std::wcout << L"Language: " << info.language << std::endl;
                outFile << L"Language: " << info.language << std::endl;

                std::wcout << L"Folder Signature: " << info.folderSign << std::endl;
                outFile << L"Folder Signature: " << info.folderSign << std::endl;

                std::wcout << L"Is Only Resource: " << (info.isResource ? L"Yes" : L"No") << std::endl;
                outFile << L"Is Only Resource: " << (info.isResource ? L"Yes" : L"No") << std::endl;

                 List files in the directory
                //std::wstring subDirPath = winSxsPath + L"\\" + dirName;
                //std::vector<std::wstring> files = listFilesInDirectory(subDirPath);
                //for (const std::wstring& file : files) {
                //    std::wcout << L"  File: " << file << std::endl;
                //    outFile << L"  File: " << file << std::endl;
                //}

                // List files and subdirectories in the directory
                std::wstring subDirPath = winSxsPath + L"\\" + dirName;
                listFilesRecursively(subDirPath, outFile, winSxsPath);

                std::wcout << L"--------------------------------" << std::endl;
                outFile << L"--------------------------------" << std::endl;
            }
        }
    } while (FindNextFileW(hFind, &findFileData) != 0);

    FindClose(hFind);
}

std::vector<AssemblyInfo> getAssembliesByComponentName(const std::wstring& winSxsPath, const std::wstring& componentName) {
    std::vector<AssemblyInfo> matchingAssemblies;
    WIN32_FIND_DATAW findFileData;
    std::wstring searchPath = winSxsPath + L"\\*";
    HANDLE hFind = FindFirstFileW(searchPath.c_str(), &findFileData);

    if (hFind == INVALID_HANDLE_VALUE) {
        std::wcerr << L"Error: Unable to open directory " << winSxsPath << std::endl;
        return matchingAssemblies;
    }

    do {
        const std::wstring dirName = findFileData.cFileName;
        if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            if (dirName == L"." || dirName == L"..") {
                continue;
            }

            AssemblyInfo info = parseAssemblyInfo(dirName);
            if (!info.architecture.empty() && info.componentName == componentName) {
                matchingAssemblies.push_back(info);
            }
        }
    } while (FindNextFileW(hFind, &findFileData) != 0);

    FindClose(hFind);
    return matchingAssemblies;
}

std::wstring getLatestAssemblyByArchitecture(const std::wstring& winSxsPath, const std::wstring& componentName, const std::wstring& architecture) {
    std::wstring latestDir;
    VersionInfo latestVersion = { 0, 0, 0, 0 };

    WIN32_FIND_DATAW findFileData;
    std::wstring searchPath = winSxsPath + L"\\*";
    HANDLE hFind = FindFirstFileW(searchPath.c_str(), &findFileData);

    if (hFind == INVALID_HANDLE_VALUE) {
        std::wcerr << L"Error: Unable to open directory " << winSxsPath << std::endl;
        return latestDir;
    }

    do {
        const std::wstring dirName = findFileData.cFileName;
        if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            if (dirName == L"." || dirName == L"..") {
                continue;
            }

            AssemblyInfo info = parseAssemblyInfo(dirName);
            if (!info.architecture.empty() && info.architecture == architecture && info.componentName == componentName) {
                if (compareVersions(latestVersion, info.version)) {
                    latestVersion = info.version;
                    latestDir = dirName;
                }
            }
        }
    } while (FindNextFileW(hFind, &findFileData) != 0);

    FindClose(hFind);
    return latestDir;
}

int wmain() {
    
    const std::wstring winSxsPath = L"\\\\?\\C:\\Windows\\WinSxS"; // Using the long path prefix
    const std::wstring outputPath = L"output.txt";

    // Testing API 0
    std::wofstream outFile(outputPath);
    if (!outFile.is_open()) {
        std::wcerr << L"Error: Unable to open output file " << outputPath << std::endl;
        return 1;
    }

    listWinSxSAssemblies(winSxsPath, outFile);
    outFile.close();

    // Testing API 1
    const std::wstring componentNameToSearch = L"microsoft.windows.common-controls";
    std::vector<AssemblyInfo> foundAssemblies = 
        getAssembliesByComponentName(winSxsPath, componentNameToSearch);

    std::wcout << L"\nAssemblies for component name: " << componentNameToSearch << L"\n";
    for (const auto& assembly : foundAssemblies) {
        std::wcout << L"Architecture: " << assembly.architecture << std::endl;
        std::wcout << L"Component Name: " << assembly.componentName << std::endl;
        std::wcout << L"Component Signature: " << assembly.componentSign << std::endl;
        std::wcout << L"Version: " << versionInfoToString(assembly.version) << std::endl;
        //std::wcout << L"Version: " << assembly.version.major << L"." << assembly.version.minor 
        //    << L"." << assembly.version.build << L"." << assembly.version.revision << std::endl;
        std::wcout << L"Language: " << assembly.language << std::endl;
        std::wcout << L"Folder Signature: " << assembly.folderSign << std::endl;
        std::wcout << L"Is Only Resource: " << (assembly.isResource ? L"Yes" : L"No") << std::endl;
        std::wcout << L"--------------------------------" << std::endl;
    }

    // Testing API 2
    std::wstring architectureToSearch = L"amd64";
    std::wstring latestAssemblyDir = getLatestAssemblyByArchitecture(winSxsPath, 
        componentNameToSearch, architectureToSearch);

    if (!latestAssemblyDir.empty()) {
        std::wcout << L"Latest Assembly Directory for Architecture " 
            << architectureToSearch << L": "
            << latestAssemblyDir << std::endl;
    }
    else {
        std::wcout << L"No assemblies found for architecture " <<
            architectureToSearch << std::endl;
    }

    return 0;
}

测试截图:

WinSxS 解析效果截图

更新&总结

(暂无)


文章出处链接:[https://blog.csdn.net/qq_59075481/article/details/140385969].

本文发布于:2024.07.12,更新于:2024.07.12.

  • 17
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

涟幽516

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值