C++版clover支持安装net3.5,仅限于本地,因为下载站不会放那么大的包
大石头 authored at 2025-07-07 20:34:42
5.84 KiB
LuckyClover
#include "CGetOSVersion.h"

/// <summary>读取注册表中的字符串值</summary>
static bool ReadRegString(HKEY root, const char* path, const char* key, std::string& value)
{
    HKEY hKey = NULL;
    if (RegOpenKeyExA(root, path, 0, KEY_READ, &hKey) != ERROR_SUCCESS) return false;

    char buf[512] = { 0 };
    DWORD size = sizeof(buf);
    DWORD type = 0;
    auto rs = RegQueryValueExA(hKey, key, NULL, &type, (LPBYTE)buf, &size);
    RegCloseKey(hKey);

    if (rs != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ)) return false;

    value = buf;
    return true;
}

/// <summary>通过注册表判断是否服务器版操作系统</summary>
static bool IsServerOS()
{
    std::string type;
    if (!ReadRegString(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\ProductOptions", "ProductType", type)) return false;

    return !type.empty() && _stricmp(type.c_str(), "WinNT") != 0;
}

CGetOSVersion::CGetOSVersion()
{
    m_Info.Major = 0;
    m_Info.Minor = 0;
    m_Info.Build = 0;
    m_Info.IsServer = false;
    m_Info.Is64Bit = false;
    m_Info.ServicePack = 0;
}

CGetOSVersion::~CGetOSVersion()
{

}

/// <summary>宽字符串转ANSI字符串</summary>
static std::string WideToAnsi(const wchar_t* str)
{
    if (str == NULL || str[0] == 0) return "";

    auto len = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
    if (len <= 0) return "";

    std::string buf(len - 1, '\0');
    WideCharToMultiByte(CP_ACP, 0, str, -1, &buf[0], len, NULL, NULL);

    return buf;
}

/// <summary>检测本地操作系统版本信息</summary>
/// <returns>操作系统版本信息</returns>
OSVersionInfo CGetOSVersion::GetVersion()
{
    CheckOSVer();

    return m_Info;
}

/// <summary>内部调用接口,检测本地操作系统版本信息</summary>
/// <returns>bool,值为真则表示检测成功</returns>
bool CGetOSVersion::CheckOSVer()
{
    m_Info.Major = 0;
    m_Info.Minor = 0;
    m_Info.Build = 0;
    m_Info.ServicePack = 0;
    m_Info.IsServer = IsServerOS();
    m_Info.Desc = "未知的系统版本";

    auto bExcute = true;
    std::string csd;

    // Windows 8.1 起 GetVersionEx 受清单影响会撒谎,优先使用 ntdll!RtlGetVersion
    typedef LONG(WINAPI* RTLGETVERSION)(OSVERSIONINFOW*);
    auto hinst = LoadLibraryA("ntdll.dll");
    if (hinst != NULL)
    {
        auto pfn = (RTLGETVERSION)GetProcAddress(hinst, "RtlGetVersion");
        if (pfn != NULL)
        {
            OSVERSIONINFOW vi;
            ZeroMemory(&vi, sizeof(vi));
            vi.dwOSVersionInfoSize = sizeof(vi);
            if (pfn(&vi) == 0)
            {
                m_Info.Major = vi.dwMajorVersion;
                m_Info.Minor = vi.dwMinorVersion;
                m_Info.Build = vi.dwBuildNumber;
                csd = WideToAnsi(vi.szCSDVersion);
            }
        }
        FreeLibrary(hinst);
    }

    if (m_Info.Major == 0)
    {
        OSVERSIONINFOA vi;
        ZeroMemory(&vi, sizeof(vi));
        vi.dwOSVersionInfoSize = sizeof(vi);

        // GetVersionEx 在新系统中已标记弃用,这里仅作为 RtlGetVersion 不可用时的兜底
#pragma warning(push)
#pragma warning(disable: 4996)
        auto success = GetVersionExA(&vi) != FALSE;
#pragma warning(pop)
        if (success)
        {
            m_Info.Major = vi.dwMajorVersion;
            m_Info.Minor = vi.dwMinorVersion;
            m_Info.Build = vi.dwBuildNumber;
            csd = vi.szCSDVersion;
        }
        else
            bExcute = false;
    }

    // 判断64位系统。32位进程中 GetSystemInfo 只返回当前进程架构,必须使用 GetNativeSystemInfo
    SYSTEM_INFO si;
    ZeroMemory(&si, sizeof(si));
    GetNativeSystemInfo(&si);
    m_Info.Is64Bit = si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ||
        si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64 ||
        si.wProcessorArchitecture == (WORD)12;  // ARM64

    if (bExcute) m_Info.Desc = FormatDesc(m_Info.Major, m_Info.Minor, m_Info.Build, m_Info.IsServer, csd);

    // 从 CSDVersion(如 "Service Pack 3")解析 Service Pack 序号
    auto ps = csd.find("Service Pack");
    if (ps != std::string::npos) m_Info.ServicePack = (DWORD)atoi(csd.c_str() + ps + 12);

    return bExcute;
}

/// <summary>根据版本号生成系统描述</summary>
/// <param name="major">主版本号</param>
/// <param name="minor">次版本号</param>
/// <param name="build">构建号</param>
/// <param name="isServer">是否服务器版</param>
/// <param name="csd">服务包描述,如 Service Pack 1</param>
/// <returns>系统描述,如 Windows 7 SP1</returns>
std::string CGetOSVersion::FormatDesc(DWORD major, DWORD minor, DWORD build, bool isServer, const std::string& csd)
{
    std::string desc = "未知的系统版本";

    if (major == 5)
    {
        if (minor == 0) desc = "Windows 2000";
        else if (minor == 1) desc = "Windows XP";
        else if (minor == 2) desc = isServer ? "Windows Server 2003" : "Windows XP x64";
    }
    else if (major == 6)
    {
        if (minor == 0) desc = isServer ? "Windows Server 2008" : "Windows Vista";
        else if (minor == 1) desc = isServer ? "Windows Server 2008 R2" : "Windows 7";
        else if (minor == 2) desc = isServer ? "Windows Server 2012" : "Windows 8";
        else if (minor == 3) desc = isServer ? "Windows Server 2012 R2" : "Windows 8.1";
    }
    else if (major == 10)
    {
        // Windows 11 与 Server 2022/2025 的主次版本号同样是 10.0,只能靠构建号区分
        if (build >= 22000) desc = isServer ? "Windows Server 2022 或更高" : "Windows 11";
        else desc = isServer ? "Windows Server 2016/2019" : "Windows 10";
    }

    if (!csd.empty() && csd != "Service Pack 0")
    {
        // 描述统一为 Win7 SP1 风格
        auto ps = csd.find("Service Pack");
        if (ps != std::string::npos) desc += " SP" + csd.substr(ps + 13);
        else desc += " " + csd;
    }

    return desc;
}