diff --git a/openVulkanoCpp/Host/Windows/SystemInfo.cpp b/openVulkanoCpp/Host/Windows/SystemInfo.cpp index 6457544..4d61fef 100644 --- a/openVulkanoCpp/Host/Windows/SystemInfo.cpp +++ b/openVulkanoCpp/Host/Windows/SystemInfo.cpp @@ -4,24 +4,46 @@ * 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) { @@ -67,6 +89,170 @@ namespace OpenVulkano 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() @@ -124,12 +310,14 @@ namespace OpenVulkano std::string SystemInfo::GetDeviceVendorName() { - return "Microsoft"; //TODO + std::optional res = GetWMIProperty("Manufacturer"); + return res ? *res : "Unknown"; } std::string SystemInfo::GetDeviceModelName() - { - return "Unknown"; //TODO + { + std::optional res = GetWMIProperty("Model"); + return res ? *res : "Unknown"; } std::string SystemInfo::GetOsName() @@ -137,25 +325,31 @@ namespace OpenVulkano return "Windows"; } - OsVersion SystemInfo::GetOsVersion() + OsVersion SystemInfo::GetOsVersion() { - OSVERSIONINFOA info; - ZeroMemory(&info, sizeof(OSVERSIONINFOA)); - info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); - GetVersionExA(&info); + 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() { - OSVERSIONINFOEXA info; - ZeroMemory(&info, sizeof(OSVERSIONINFOEXA)); - info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); - GetVersionEx((OSVERSIONINFOA *)&info); + 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) @@ -172,7 +366,35 @@ namespace OpenVulkano else { if (info.dwMajorVersion == 10) - return "Windows Server 2016"; + { + 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) @@ -188,17 +410,33 @@ namespace OpenVulkano } } - DeviceType SystemInfo::GetDeviceType() + DeviceType SystemInfo::GetDeviceType() { return DeviceType::PC; } - size_t SystemInfo::GetCpuCoreCount() + size_t SystemInfo::GetCpuCoreCount() { - return 0; //TODO + 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() + size_t SystemInfo::GetCpuThreadCount() { return std::thread::hardware_concurrency(); } @@ -210,22 +448,46 @@ namespace OpenVulkano CpuThermalState SystemInfo::GetCpuThermalState() { - return CpuThermalState::Normal; + return CpuThermalState::Normal; // TODO } bool SystemInfo::IsDeviceInLowPowerMode() { - return false; //TODO + GUID* activePowerMode; + bool res = false; + if(PowerGetActiveScheme(0, &activePowerMode) == 0) + { + res = IsEqualGUID(*activePowerMode, GUID_MAX_POWER_SAVINGS); + } + LocalFree(activePowerMode); + return res; } BatteryState SystemInfo::GetDeviceBatteryState() { - return BatteryState::Unavailable; //TODO + 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() { - return 0; //TODO + SYSTEM_POWER_STATUS info; + GetSystemPowerStatus(&info); + return info.BatteryLifePercent != 255 ? (static_cast(info.BatteryLifePercent / 100.f)) : -1.f; } void SystemInfo::EnableEnergyEvents() @@ -240,7 +502,24 @@ namespace OpenVulkano DeviceOrientation SystemInfo::GetDeviceOrientation() { - return DeviceOrientation::LandscapeRight; //TODO + 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() @@ -250,6 +529,19 @@ namespace OpenVulkano InterfaceOrientation SystemInfo::GetInterfaceOrientation() { - return InterfaceOrientation::Landscape; //TODO + 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; + } } } \ No newline at end of file