mirror of
https://github.com/microsoft/PowerToys
synced 2025-08-23 10:37:43 +00:00
478 lines
13 KiB
C++
478 lines
13 KiB
C++
|
#include "pch.h"
|
||
|
#include "MonitorReportTool.h"
|
||
|
|
||
|
#include <WbemCli.h>
|
||
|
#include <dwmapi.h>
|
||
|
#include <comutil.h>
|
||
|
|
||
|
#include <unordered_map>
|
||
|
|
||
|
#include "ErrorMessage.h"
|
||
|
#include "Logger.h"
|
||
|
|
||
|
namespace FancyZonesUtils
|
||
|
{
|
||
|
template<RECT MONITORINFO::* member>
|
||
|
std::vector<std::pair<HMONITOR, MONITORINFOEX>> GetAllMonitorInfo()
|
||
|
{
|
||
|
using result_t = std::vector<std::pair<HMONITOR, MONITORINFOEX>>;
|
||
|
result_t result;
|
||
|
|
||
|
auto enumMonitors = [](HMONITOR monitor, HDC, LPRECT, LPARAM param) -> BOOL {
|
||
|
MONITORINFOEX mi;
|
||
|
mi.cbSize = sizeof(mi);
|
||
|
result_t& result = *reinterpret_cast<result_t*>(param);
|
||
|
if (GetMonitorInfo(monitor, &mi))
|
||
|
{
|
||
|
result.push_back({ monitor, mi });
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
};
|
||
|
|
||
|
EnumDisplayMonitors(NULL, NULL, enumMonitors, reinterpret_cast<LPARAM>(&result));
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void LogEnumDisplayMonitors()
|
||
|
{
|
||
|
Logger::log(L" ---- EnumDisplayMonitors ---- ");
|
||
|
|
||
|
auto allMonitors = FancyZonesUtils::GetAllMonitorInfo<&MONITORINFOEX::rcWork>();
|
||
|
std::unordered_map<std::wstring, DWORD> displayDeviceIdxMap;
|
||
|
|
||
|
for (auto& monitorData : allMonitors)
|
||
|
{
|
||
|
auto monitorInfo = monitorData.second;
|
||
|
|
||
|
DISPLAY_DEVICE displayDevice{ .cb = sizeof(displayDevice) };
|
||
|
std::wstring deviceId;
|
||
|
auto enumRes = EnumDisplayDevicesW(monitorInfo.szDevice, displayDeviceIdxMap[monitorInfo.szDevice], &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME);
|
||
|
|
||
|
if (enumRes == 0)
|
||
|
{
|
||
|
Logger::log(get_last_error_or_default(GetLastError()));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Logger::log(L"DeviceId: {}", std::wstring(displayDevice.DeviceID));
|
||
|
Logger::log(L"DeviceKey: {}", std::wstring(displayDevice.DeviceKey));
|
||
|
Logger::log(L"DeviceName: {}", std::wstring(displayDevice.DeviceName));
|
||
|
Logger::log(L"DeviceString: {}", std::wstring(displayDevice.DeviceString));
|
||
|
Logger::log(L"");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Logger::log(L"");
|
||
|
}
|
||
|
|
||
|
void LogWMIProp(IWbemClassObject* wbemClassObj, std::wstring_view prop)
|
||
|
{
|
||
|
if (!wbemClassObj)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
VARIANT vtProp{};
|
||
|
|
||
|
// Get the value of the Name property
|
||
|
auto hres = wbemClassObj->Get(prop.data(), 0, &vtProp, 0, 0);
|
||
|
if (FAILED(hres))
|
||
|
{
|
||
|
Logger::log(L"Get {} Error code = {} ", prop, get_last_error_or_default(hres));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
switch (vtProp.vt)
|
||
|
{
|
||
|
case VT_I2: //short
|
||
|
{
|
||
|
Logger::log(L"{} : {}", prop, vtProp.iVal);
|
||
|
}
|
||
|
break;
|
||
|
case VT_I4: //int, long
|
||
|
{
|
||
|
Logger::log(L"{} : {}", prop, vtProp.lVal);
|
||
|
}
|
||
|
break;
|
||
|
case VT_BSTR: //BSTR
|
||
|
{
|
||
|
Logger::log(L"{} : {}", prop, vtProp.bstrVal);
|
||
|
}
|
||
|
break;
|
||
|
case VT_UI1: //BYTE (unsigned char)
|
||
|
{
|
||
|
Logger::log(L"{} : {}", prop, vtProp.bVal);
|
||
|
}
|
||
|
break;
|
||
|
case VT_ARRAY: // parray
|
||
|
case 8195: // also parray
|
||
|
{
|
||
|
std::u32string str(static_cast<const char32_t*>(vtProp.parray->pvData));
|
||
|
std::wstring wstr;
|
||
|
for (const char32_t& c : str)
|
||
|
{
|
||
|
wstr += (wchar_t)c;
|
||
|
}
|
||
|
|
||
|
Logger::log(L"{} : {}", prop, wstr);
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
{
|
||
|
Logger::log(L"{} : value is empty", prop);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
VariantClear(&vtProp);
|
||
|
}
|
||
|
|
||
|
void LogWMI()
|
||
|
{
|
||
|
Logger::log(L" ---- WMI ---- ");
|
||
|
|
||
|
HRESULT hres;
|
||
|
|
||
|
// Initialize COM.
|
||
|
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
|
||
|
if (FAILED(hres))
|
||
|
{
|
||
|
Logger::log(L"Failed to initialize COM library. Error code = ", hres);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Initialize
|
||
|
hres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT,
|
||
|
RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
|
||
|
|
||
|
if (FAILED(hres))
|
||
|
{
|
||
|
Logger::log(L"Failed to initialize security. Error code = ", hres);
|
||
|
CoUninitialize();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Obtain the initial locator to Windows Management
|
||
|
// on a particular host computer.
|
||
|
IWbemLocator* pLocator = 0;
|
||
|
|
||
|
hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLocator);
|
||
|
if (FAILED(hres))
|
||
|
{
|
||
|
Logger::log(L"Failed to create IWbemLocator object. Error code = ", hres);
|
||
|
CoUninitialize();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
IWbemServices* pServices = 0;
|
||
|
hres = pLocator->ConnectServer(_bstr_t(L"ROOT\\WMI"), NULL, NULL, 0, NULL, 0, 0, &pServices);
|
||
|
|
||
|
if (FAILED(hres))
|
||
|
{
|
||
|
Logger::log(L"Could not connect WMI server. Error code = ", hres);
|
||
|
pLocator->Release();
|
||
|
CoUninitialize();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Logger::log(L"Connected to ROOT\\WMI WMI namespace");
|
||
|
Logger::log(L"");
|
||
|
|
||
|
|
||
|
// Set the IWbemServices proxy so that impersonation
|
||
|
// of the user (client) occurs.
|
||
|
hres = CoSetProxyBlanket(pServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
|
||
|
RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
|
||
|
|
||
|
if (FAILED(hres))
|
||
|
{
|
||
|
Logger::log(L"Could not set proxy blanket. Error code = ", hres);
|
||
|
pServices->Release();
|
||
|
pLocator->Release();
|
||
|
CoUninitialize();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Use the IWbemServices pointer to make requests of WMI.
|
||
|
// Make requests here:
|
||
|
IEnumWbemClassObject* pEnumerator = NULL;
|
||
|
|
||
|
hres = pServices->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM WmiMonitorID"),
|
||
|
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);
|
||
|
|
||
|
if (FAILED(hres))
|
||
|
{
|
||
|
Logger::log(L"Query for monitors failed. Error code = ", hres);
|
||
|
pServices->Release();
|
||
|
pLocator->Release();
|
||
|
CoUninitialize();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
IWbemClassObject* pClassObject;
|
||
|
ULONG uReturn = 0;
|
||
|
|
||
|
while (pEnumerator)
|
||
|
{
|
||
|
hres = pEnumerator->Next(WBEM_INFINITE, 1, &pClassObject, &uReturn);
|
||
|
|
||
|
if (0 == uReturn)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
LPSAFEARRAY pFieldArray = NULL;
|
||
|
hres = pClassObject->GetNames(NULL, WBEM_FLAG_ALWAYS, NULL, &pFieldArray);
|
||
|
if (FAILED(hres))
|
||
|
{
|
||
|
Logger::log(L"Failed to get field names. Error code = {}", get_last_error_or_default(hres));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
LogWMIProp(pClassObject, L"InstanceName");
|
||
|
|
||
|
LogWMIProp(pClassObject, L"YearOfManufacture");
|
||
|
LogWMIProp(pClassObject, L"WeekOfManufacture");
|
||
|
|
||
|
LogWMIProp(pClassObject, L"UserFriendlyNameLength");
|
||
|
LogWMIProp(pClassObject, L"UserFriendlyName");
|
||
|
LogWMIProp(pClassObject, L"ManufacturerName");
|
||
|
|
||
|
LogWMIProp(pClassObject, L"SerialNumberID");
|
||
|
LogWMIProp(pClassObject, L"ProductCodeID");
|
||
|
|
||
|
Logger::log(L"");
|
||
|
|
||
|
pClassObject->Release();
|
||
|
pClassObject = NULL;
|
||
|
}
|
||
|
|
||
|
pServices->Release();
|
||
|
pLocator->Release();
|
||
|
pEnumerator->Release();
|
||
|
|
||
|
CoUninitialize();
|
||
|
}
|
||
|
|
||
|
void LogWMICIMV2()
|
||
|
{
|
||
|
Logger::log(L" ---- WMI ---- ");
|
||
|
|
||
|
HRESULT hres;
|
||
|
|
||
|
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
|
||
|
if (FAILED(hres))
|
||
|
{
|
||
|
Logger::log(L"Failed to initialize COM library. Error code = ", hres);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
hres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT,
|
||
|
RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
|
||
|
|
||
|
if (FAILED(hres))
|
||
|
{
|
||
|
Logger::log(L"Failed to initialize security. Error code = ", hres);
|
||
|
CoUninitialize();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Obtain the initial locator to Windows Management
|
||
|
// on a particular host computer.
|
||
|
IWbemLocator* pLocator = 0;
|
||
|
|
||
|
hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLocator);
|
||
|
if (FAILED(hres))
|
||
|
{
|
||
|
Logger::log(L"Failed to create IWbemLocator object. Error code = ", hres);
|
||
|
CoUninitialize();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
IWbemServices* pServices = 0;
|
||
|
|
||
|
// Connect to the root\cimv2 namespace with the
|
||
|
// current user and obtain pointer pSvc
|
||
|
// to make IWbemServices calls.
|
||
|
|
||
|
hres = pLocator->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &pServices);
|
||
|
|
||
|
if (FAILED(hres))
|
||
|
{
|
||
|
Logger::log(L"Could not connect WMI server. Error code = ", hres);
|
||
|
pLocator->Release();
|
||
|
CoUninitialize();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Logger::log(L"Connected to ROOT\\CIMV2 WMI namespace");
|
||
|
Logger::log(L"");
|
||
|
|
||
|
// Set the IWbemServices proxy so that impersonation
|
||
|
// of the user (client) occurs.
|
||
|
hres = CoSetProxyBlanket(pServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
|
||
|
RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
|
||
|
|
||
|
if (FAILED(hres))
|
||
|
{
|
||
|
Logger::log(L"Could not set proxy blanket. Error code = ", hres);
|
||
|
pServices->Release();
|
||
|
pLocator->Release();
|
||
|
CoUninitialize();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Use the IWbemServices pointer to make requests of WMI.
|
||
|
// Make requests here:
|
||
|
IEnumWbemClassObject* pEnumerator = NULL;
|
||
|
hres = pServices->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM Win32_DesktopMonitor"),
|
||
|
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);
|
||
|
|
||
|
if (FAILED(hres))
|
||
|
{
|
||
|
Logger::log(L"Query for monitors failed. Error code = ", hres);
|
||
|
pServices->Release();
|
||
|
pLocator->Release();
|
||
|
CoUninitialize();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
IWbemClassObject* pClassObject;
|
||
|
ULONG uReturn = 0;
|
||
|
|
||
|
while (pEnumerator)
|
||
|
{
|
||
|
hres = pEnumerator->Next(WBEM_INFINITE, 1, &pClassObject, &uReturn);
|
||
|
|
||
|
if (0 == uReturn)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
LogWMIProp(pClassObject, L"DeviceID");
|
||
|
LogWMIProp(pClassObject, L"Caption");
|
||
|
LogWMIProp(pClassObject, L"Description");
|
||
|
LogWMIProp(pClassObject, L"MonitorManufacturer");
|
||
|
LogWMIProp(pClassObject, L"MonitorType");
|
||
|
LogWMIProp(pClassObject, L"Name");
|
||
|
LogWMIProp(pClassObject, L"PNPDeviceID");
|
||
|
LogWMIProp(pClassObject, L"Status");
|
||
|
|
||
|
LogWMIProp(pClassObject, L"Availability");
|
||
|
|
||
|
Logger::log(L"");
|
||
|
|
||
|
pClassObject->Release();
|
||
|
pClassObject = NULL;
|
||
|
}
|
||
|
|
||
|
pServices->Release();
|
||
|
pLocator->Release();
|
||
|
pEnumerator->Release();
|
||
|
|
||
|
CoUninitialize();
|
||
|
}
|
||
|
|
||
|
void LogCCD()
|
||
|
{
|
||
|
Logger::log(L" ---- CCD ---- ");
|
||
|
|
||
|
LONG result = ERROR_SUCCESS;
|
||
|
std::vector<DISPLAYCONFIG_PATH_INFO> paths;
|
||
|
std::vector<DISPLAYCONFIG_MODE_INFO> modes;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
UINT32 pathCount{}, modeCount{};
|
||
|
auto sizesResult = GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS | QDC_INCLUDE_HMD | QDC_VIRTUAL_MODE_AWARE, &pathCount, &modeCount);
|
||
|
|
||
|
if (sizesResult != ERROR_SUCCESS)
|
||
|
{
|
||
|
Logger::log(L"GetDisplayConfigBufferSizes error {}", get_last_error_or_default(sizesResult));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
paths.resize(pathCount);
|
||
|
paths.resize(modeCount);
|
||
|
|
||
|
auto result = QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS | QDC_INCLUDE_HMD | QDC_VIRTUAL_MODE_AWARE, &pathCount, paths.data()
|
||
|
, &modeCount, modes.data(), nullptr);
|
||
|
|
||
|
// The function may have returned fewer paths/modes than estimated
|
||
|
paths.resize(pathCount);
|
||
|
modes.resize(modeCount);
|
||
|
} while (result == ERROR_INSUFFICIENT_BUFFER);
|
||
|
|
||
|
if (result != ERROR_SUCCESS)
|
||
|
{
|
||
|
Logger::log(L"QueryDisplayConfig error {}", get_last_error_or_default(result));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// For each active path
|
||
|
for (auto& path : paths)
|
||
|
{
|
||
|
// Find the target (monitor) friendly name
|
||
|
DISPLAYCONFIG_TARGET_DEVICE_NAME targetName = {};
|
||
|
targetName.header.adapterId = path.targetInfo.adapterId;
|
||
|
targetName.header.id = path.targetInfo.id;
|
||
|
targetName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
|
||
|
targetName.header.size = sizeof(targetName);
|
||
|
result = DisplayConfigGetDeviceInfo(&targetName.header);
|
||
|
|
||
|
if (result != ERROR_SUCCESS)
|
||
|
{
|
||
|
Logger::log(L"DisplayConfigGetDeviceInfo error {}", get_last_error_or_default(result));
|
||
|
}
|
||
|
|
||
|
// Find the adapter device name
|
||
|
DISPLAYCONFIG_ADAPTER_NAME adapterName = {};
|
||
|
adapterName.header.adapterId = path.targetInfo.adapterId;
|
||
|
adapterName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME;
|
||
|
adapterName.header.size = sizeof(adapterName);
|
||
|
|
||
|
result = DisplayConfigGetDeviceInfo(&adapterName.header);
|
||
|
|
||
|
if (result != ERROR_SUCCESS)
|
||
|
{
|
||
|
Logger::log(L"DisplayConfigGetDeviceInfo error {}", get_last_error_or_default(result));
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
Logger::log(L"Monitor: {} connected to adapter {}"
|
||
|
, (targetName.flags.friendlyNameFromEdid ? targetName.monitorFriendlyDeviceName : L"Unknown")
|
||
|
, adapterName.adapterDevicePath);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void LogInfo()
|
||
|
{
|
||
|
Logger::log(L"Timestamp: {}", std::chrono::system_clock::now());
|
||
|
Logger::log(L"");
|
||
|
|
||
|
LogEnumDisplayMonitors();
|
||
|
LogWMICIMV2();
|
||
|
LogWMI();
|
||
|
LogCCD();
|
||
|
|
||
|
Logger::log(L"=======================================");
|
||
|
Logger::log(L"");
|
||
|
}
|
||
|
|
||
|
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
|
||
|
_In_opt_ HINSTANCE hPrevInstance,
|
||
|
_In_ LPWSTR lpCmdLine,
|
||
|
_In_ int nCmdShow)
|
||
|
{
|
||
|
UNREFERENCED_PARAMETER(hPrevInstance);
|
||
|
UNREFERENCED_PARAMETER(lpCmdLine);
|
||
|
|
||
|
Logger::init("MonitorReportTool");
|
||
|
|
||
|
LogInfo();
|
||
|
|
||
|
return 0;
|
||
|
}
|