Merge pull request 'Extend SystemInfo implementation for Windows' (#173) from misc into master

Reviewed-on: https://git.madvoxel.net/OpenVulkano/OpenVulkano/pulls/173
Reviewed-by: Georg Hagen <georg.hagen@madvoxel.com>
This commit is contained in:
Oleksii_Hyzha
2024-12-16 15:36:10 +01:00

View File

@@ -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 <iostream>
#include <utf8.h>
#include <Windows.h>
#include <Psapi.h>
#include <Lmcons.h>
#include <Winsock.h>
#include <Winbase.h>
#include <powersetting.h>
#include <guiddef.h>
#include <comdef.h>
#include <Wbemidl.h>
// 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<std::string> GetWMIProperty(const std::string& propName)
{
std::optional<std::string> 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<std::string> res = GetWMIProperty("Manufacturer");
return res ? *res : "Unknown";
}
std::string SystemInfo::GetDeviceModelName()
{
return "Unknown"; //TODO
{
std::optional<std::string> 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<BYTE> buf(bufferSize);
SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* info = reinterpret_cast<SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*>(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<SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*>(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<float>(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;
}
}
}