/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #define _WIN32_DCOM #include "Host/SystemInfo.hpp" #include "Base/Logger.hpp" #include #include #include #include #include #include #include #include #include #include #include // NOTE(vb): Windows defines macros like GetUserName that are used to automatically select the appropriate function version (GetUserNameA for ANSI and GetUserNameW for Unicode) // based on whether the _UNICODE macro is defined, so we manually undefine these macros to avoid naming collisions. #undef GetUserName // link this for power settings #pragma comment(lib, "PowrProf.lib") #pragma comment(lib, "wbemuuid.lib") namespace OpenVulkano { namespace { enum class SYS_MEM_TYPE { TOTAL_PHYS, AVAIL_PHYS }; enum BatteryChargeStatus { MEDIUM = 0, // [low, high] HIGH = 1, // > 66% LOW = 2, // < 33% CRITICAL = 4, // <5% CHARGING = 8, NO_SYSTEM_BATTERY = 128, UNKNOWN = 255 }; size_t ReadSystemMemInfo(SYS_MEM_TYPE type) { MEMORYSTATUSEX status; status.dwLength = sizeof(status); if (GlobalMemoryStatusEx(&status)) { switch (type) { case SYS_MEM_TYPE::TOTAL_PHYS: return status.ullTotalPhys; case SYS_MEM_TYPE::AVAIL_PHYS: return status.ullAvailPhys; } } Logger::PERF->error("Failed to get system RAM info"); return 0; } enum class APP_MEM_TYPE { VM_MAX, USED }; size_t ReadAppMemInfo(APP_MEM_TYPE type) { PROCESS_MEMORY_COUNTERS_EX counters; if (GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast(&counters), sizeof(counters))) { switch(type) { case APP_MEM_TYPE::VM_MAX: return counters.PeakPagefileUsage; case APP_MEM_TYPE::USED: return counters.PrivateUsage; } } Logger::PERF->error("Failed to get process memory info"); return 0; } size_t GetCommitLimit() { MEMORYSTATUSEX status; status.dwLength = sizeof(status); if(GlobalMemoryStatusEx(&status)) { return status.ullTotalPageFile; } Logger::PERF->error("Failed to get system commit limit"); return 0; } std::optional GetWMIProperty(const std::string& propName) { std::optional res; // https://learn.microsoft.com/en-us/windows/win32/wmisdk/example-creating-a-wmi-application?redirectedfrom=MSDN HRESULT hres; // Initialize COM. hres = CoInitializeEx(0, COINIT_MULTITHREADED); if (FAILED(hres)) { Logger::APP->error("Failed to initialize COM library. Error code = 0x{}", hres); return {}; } // Initialize hres = CoInitializeSecurity(NULL, -1, // COM negotiates service NULL, // Authentication services NULL, // Reserved RPC_C_AUTHN_LEVEL_DEFAULT, // authentication RPC_C_IMP_LEVEL_IMPERSONATE, // Impersonation NULL, // Authentication info EOAC_NONE, // Additional capabilities NULL // Reserved ); if (FAILED(hres)) { Logger::APP->error("Failed to initialize security. Error code = 0x{}", hres); CoUninitialize(); return {}; } // Obtain the initial locator to Windows Management // on a particular host computer. IWbemLocator* pLoc = 0; hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*) &pLoc); if (FAILED(hres)) { Logger::APP->error("Failed to create IWbemLocator object. Error code = 0x{}", hres); CoUninitialize(); return {}; } IWbemServices* pSvc = 0; // Connect to the root\cimv2 namespace with the // current user and obtain pointer pSvc // to make IWbemServices calls. hres = pLoc->ConnectServer( _bstr_t(L"ROOT\\CIMV2"), // WMI namespace NULL, // User name NULL, // User password 0, // Locale NULL, // Security flags 0, // Authority 0, // Context object &pSvc // IWbemServices proxy ); if (FAILED(hres)) { Logger::APP->error("Could not connect. Error code = 0x{}", hres); pLoc->Release(); CoUninitialize(); return {}; } // Set the IWbemServices proxy so that impersonation // of the user (client) occurs. hres = CoSetProxyBlanket( pSvc, // the proxy to set RPC_C_AUTHN_WINNT, // authentication service RPC_C_AUTHZ_NONE, // authorization service NULL, // Server principal name RPC_C_AUTHN_LEVEL_CALL, // authentication level RPC_C_IMP_LEVEL_IMPERSONATE, // impersonation level NULL, // client identity EOAC_NONE // proxy capabilities ); if (FAILED(hres)) { Logger::APP->error("Could not set proxy blanket. Error code = 0x{}", hres); pSvc->Release(); pLoc->Release(); CoUninitialize(); return {}; } // Use the IWbemServices pointer to make requests of WMI. // Make requests here: // For example, query for all the running processes IEnumWbemClassObject* pEnumerator = NULL; hres = pSvc->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM Win32_ComputerSystem"), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); if (FAILED(hres)) { Logger::APP->error("Query for processes failed. Error code = 0x{}", hres); pSvc->Release(); pLoc->Release(); CoUninitialize(); return {}; } else { IWbemClassObject* pclsObj; ULONG uReturn = 0; while (pEnumerator) { hres = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); if (0 == uReturn) { break; } VARIANT vtProp; ZeroMemory(&vtProp, sizeof(vtProp)); std::wstring propNameUtf16; utf8::utf8to16(propName.begin(), propName.end(), std::back_inserter(propNameUtf16)); // Get the value of the specified property hres = pclsObj->Get(propNameUtf16.data(), 0, &vtProp, 0, 0); if (FAILED(hres)) { Logger::APP->error("Failed to get property {}. Error code = 0x{}", propName, hres); } else { // init optional res = ""; std::wstring propValue = vtProp.bstrVal; utf8::utf16to8(propValue.begin(), propValue.end(), std::back_inserter(*res)); } VariantClear(&vtProp); pclsObj->Release(); pclsObj = NULL; } } // Cleanup // ======== pSvc->Release(); pLoc->Release(); pEnumerator->Release(); CoUninitialize(); return res; } } size_t SystemInfo::GetSystemRam() { return ReadSystemMemInfo(SYS_MEM_TYPE::TOTAL_PHYS); } size_t SystemInfo::GetSystemRamAvailable() { return ReadSystemMemInfo(SYS_MEM_TYPE::AVAIL_PHYS); } size_t SystemInfo::GetAppRamMax() { return std::min(GetSystemRam(), GetCommitLimit()); } size_t SystemInfo::GetAppRamAvailable() { return std::min(GetSystemRamAvailable(), GetCommitLimit() - GetAppRamUsed()); } size_t SystemInfo::GetAppRamUsed() { return ReadAppMemInfo(APP_MEM_TYPE::USED); } size_t SystemInfo::GetAppVirtualMemoryMax() { return ReadAppMemInfo(APP_MEM_TYPE::VM_MAX); } std::string SystemInfo::GetUserName() { char username[UNLEN+1]; DWORD username_len = UNLEN+1; ::GetUserNameA(username, &username_len); return username; } std::string SystemInfo::GetHostName() { char hostname[UNLEN+1]; gethostname(hostname, UNLEN+1); return hostname; } std::string SystemInfo::GetDeviceName() { char computerName[UNLEN+1]; DWORD computerName_len = UNLEN+1; GetComputerNameA(computerName, &computerName_len); return computerName; } std::string SystemInfo::GetDeviceVendorName() { std::optional res = GetWMIProperty("Manufacturer"); return res ? *res : "Unknown"; } std::string SystemInfo::GetDeviceModelName() { std::optional res = GetWMIProperty("Model"); return res ? *res : "Unknown"; } std::string SystemInfo::GetOsName() { return "Windows"; } OsVersion SystemInfo::GetOsVersion() { NTSTATUS(WINAPI * RtlGetVersion)(LPOSVERSIONINFOEXW); OSVERSIONINFOEXW info; *(FARPROC*) &RtlGetVersion = GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion"); info.dwOSVersionInfoSize = sizeof(info); RtlGetVersion(&info); return { (int)info.dwMajorVersion, (int)info.dwMinorVersion, 0, (int)info.dwBuildNumber }; } std::string SystemInfo::GetOsNameHumanReadable() { NTSTATUS(WINAPI *RtlGetVersion)(LPOSVERSIONINFOEXW); OSVERSIONINFOEXW info; *(FARPROC*)&RtlGetVersion = GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion"); info.dwOSVersionInfoSize = sizeof(info); RtlGetVersion(&info); if (info.wProductType == VER_NT_WORKSTATION) { if (info.dwMajorVersion == 10) { if (info.dwBuildNumber >= 22000) return "Windows 11"; return "Windows 10"; } else if (info.dwMajorVersion == 6) { switch (info.dwMinorVersion) { case 0: return "Windows Vista"; case 1: return "Windows 7"; case 2: return "Windows 8"; case 3: return "Windows 8.1"; } } return "Windows " + std::to_string(info.dwMajorVersion) + "." + std::to_string(info.dwMinorVersion); } else { if (info.dwMajorVersion == 10) { switch (info.dwBuildNumber) { case 14393: return "Windows Server 2016"; case 16299: return "Windows Server 1709"; case 17134: return "Windows Server 1803"; case 17763: return "Windows Server 2019"; case 18362: return "Windows Server 1903"; case 18363: return "Windows Server 1909"; case 19041: return "Windows Server 2004"; case 19042: return "Windows Server 20H2"; case 20348: return "Windows Server 2022"; case 25398: return "Windows Server 23H2"; case 26100: return "Windows Server 2025"; default: return "Windows Server"; } } else if (info.dwMajorVersion == 6) { switch (info.dwMinorVersion) { case 0: return "Windows Server 2008"; case 1: return "Windows Server 2008 R2"; case 2: return "Windows Server 2012"; case 3: return "Windows Server 2012 R2"; } } return "Windows Server " + std::to_string(info.dwMajorVersion) + "." + std::to_string(info.dwMinorVersion); } } DeviceType SystemInfo::GetDeviceType() { return DeviceType::PC; } size_t SystemInfo::GetCpuCoreCount() { DWORD bufferSize = 0; GetLogicalProcessorInformationEx(LOGICAL_PROCESSOR_RELATIONSHIP::RelationProcessorCore, nullptr, &bufferSize); std::vector buf(bufferSize); SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* info = reinterpret_cast(buf.data()); GetLogicalProcessorInformationEx(LOGICAL_PROCESSOR_RELATIONSHIP::RelationProcessorCore, info, &bufferSize); size_t physProcessorCount = 0; BYTE* start = buf.data(); BYTE* end = buf.data() + bufferSize; while (start < end) { SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* current = reinterpret_cast(start); physProcessorCount++; start += current->Size; } return physProcessorCount; } size_t SystemInfo::GetCpuThreadCount() { return std::thread::hardware_concurrency(); } int32_t SystemInfo::GetCpuTemperature() { return 0; //TODO } CpuThermalState SystemInfo::GetCpuThermalState() { return CpuThermalState::Normal; // TODO } bool SystemInfo::IsDeviceInLowPowerMode() { GUID* activePowerMode; bool res = false; if(PowerGetActiveScheme(0, &activePowerMode) == 0) { res = IsEqualGUID(*activePowerMode, GUID_MAX_POWER_SAVINGS); } LocalFree(activePowerMode); return res; } BatteryState SystemInfo::GetDeviceBatteryState() { SYSTEM_POWER_STATUS info; GetSystemPowerStatus(&info); BYTE batteryFlag = info.BatteryFlag; if (batteryFlag & BatteryChargeStatus::CHARGING) { if (info.BatteryLifePercent == 100) return BatteryState::ChargingFull; return BatteryState::Charging; } else if (batteryFlag < BatteryChargeStatus::CHARGING) { return BatteryState::Unplugged; } // BatteryState::NotCharging is impossible to distinguish // NO_SYSTEM_BATTERY or UNKNOWN return BatteryState::Unavailable; } float SystemInfo::GetDeviceBatteryLevel() { SYSTEM_POWER_STATUS info; GetSystemPowerStatus(&info); return info.BatteryLifePercent != 255 ? (static_cast(info.BatteryLifePercent / 100.f)) : -1.f; } void SystemInfo::EnableEnergyEvents() { //TODO } bool SystemInfo::IsMultitaskingSupported() { return true; } DeviceOrientation SystemInfo::GetDeviceOrientation() { DEVMODEA devmode; ZeroMemory(&devmode, sizeof(devmode)); devmode.dmSize = sizeof(DEVMODE); if (EnumDisplaySettingsA(0, ENUM_CURRENT_SETTINGS, &devmode)) { DWORD width = devmode.dmPelsWidth; DWORD height = devmode.dmPelsHeight; if (width > height) { // landscape return devmode.dmDisplayOrientation == DMDO_180 ? DeviceOrientation::LandscapeLeft : DeviceOrientation::LandscapeRight; } // portrait return devmode.dmDisplayOrientation == DMDO_180 ? DeviceOrientation::PortraitUpsideDown : DeviceOrientation::Portrait; } return DeviceOrientation::Unknown; } void SystemInfo::EnableDeviceOrientationEvents() { //TODO } InterfaceOrientation SystemInfo::GetInterfaceOrientation() { DeviceOrientation devOrient = GetDeviceOrientation(); switch (devOrient) { case OpenVulkano::DeviceOrientation::Portrait: return InterfaceOrientation::Portrait; case OpenVulkano::DeviceOrientation::PortraitUpsideDown: return InterfaceOrientation::PortraitUpsideDown; case OpenVulkano::DeviceOrientation::LandscapeLeft: return InterfaceOrientation::LandscapeRight; case OpenVulkano::DeviceOrientation::LandscapeRight: return InterfaceOrientation::LandscapeLeft; default: return InterfaceOrientation::Landscape; } } }