MonitorReportTool.cpp
1 #include "pch.h" 2 #include "MonitorReportTool.h" 3 4 #include <WbemCli.h> 5 #include <dwmapi.h> 6 #include <comutil.h> 7 8 #include <unordered_map> 9 10 #include "ErrorMessage.h" 11 #include "Logger.h" 12 13 namespace FancyZonesUtils 14 { 15 template<RECT MONITORINFO::* member> 16 std::vector<std::pair<HMONITOR, MONITORINFOEX>> GetAllMonitorInfo() 17 { 18 using result_t = std::vector<std::pair<HMONITOR, MONITORINFOEX>>; 19 result_t result; 20 21 auto enumMonitors = [](HMONITOR monitor, HDC, LPRECT, LPARAM param) -> BOOL { 22 MONITORINFOEX mi; 23 mi.cbSize = sizeof(mi); 24 result_t& result = *reinterpret_cast<result_t*>(param); 25 if (GetMonitorInfo(monitor, &mi)) 26 { 27 result.push_back({ monitor, mi }); 28 } 29 30 return TRUE; 31 }; 32 33 EnumDisplayMonitors(NULL, NULL, enumMonitors, reinterpret_cast<LPARAM>(&result)); 34 return result; 35 } 36 } 37 38 void LogEnumDisplayMonitors() 39 { 40 Logger::log(L" ---- EnumDisplayMonitors as in FancyZones ---- "); 41 42 auto allMonitors = FancyZonesUtils::GetAllMonitorInfo<&MONITORINFOEX::rcWork>(); 43 std::unordered_map<std::wstring, DWORD> displayDeviceIdxMap; 44 45 for (auto& monitorData : allMonitors) 46 { 47 auto monitorInfo = monitorData.second; 48 49 DISPLAY_DEVICE displayDevice{ .cb = sizeof(displayDevice) }; 50 std::wstring deviceId; 51 auto enumRes = EnumDisplayDevicesW(monitorInfo.szDevice, displayDeviceIdxMap[monitorInfo.szDevice], &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME); 52 53 if (enumRes == 0) 54 { 55 Logger::log(get_last_error_or_default(GetLastError())); 56 } 57 else 58 { 59 Logger::log(L"DeviceId: {}", std::wstring(displayDevice.DeviceID)); 60 Logger::log(L"DeviceKey: {}", std::wstring(displayDevice.DeviceKey)); 61 Logger::log(L"DeviceName: {}", std::wstring(displayDevice.DeviceName)); 62 Logger::log(L"DeviceString: {}", std::wstring(displayDevice.DeviceString)); 63 Logger::log(L""); 64 } 65 } 66 67 Logger::log(L""); 68 } 69 70 void LogPrintDisplayDevice(const DISPLAY_DEVICE& displayDevice, bool internal) 71 { 72 const bool active = displayDevice.StateFlags & DISPLAY_DEVICE_ACTIVE; 73 const bool mirroring = displayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER; 74 const bool modesPruned = displayDevice.StateFlags & DISPLAY_DEVICE_MODESPRUNED; 75 const bool primaryDevice = displayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE; 76 const bool removable = displayDevice.StateFlags & DISPLAY_DEVICE_REMOVABLE; 77 const bool VGA_Compatible = displayDevice.StateFlags & DISPLAY_DEVICE_VGA_COMPATIBLE; 78 79 Logger::log(L"{}DeviceId: {}", internal?L"--> ":L"", std::wstring(displayDevice.DeviceID)); 80 Logger::log(L"{}DeviceKey: {}", internal?L"--> ":L"", std::wstring(displayDevice.DeviceKey)); 81 Logger::log(L"{}DeviceName: {}", internal?L"--> ":L"", std::wstring(displayDevice.DeviceName)); 82 Logger::log(L"{}DeviceString: {}", internal?L"--> ":L"", std::wstring(displayDevice.DeviceString)); 83 Logger::log(L"{}StateFlags: {}", internal?L"--> ":L"", displayDevice.StateFlags); 84 Logger::log(L"{}active: {}", internal?L"--> ":L"", active); 85 Logger::log(L"{}mirroring: {}", internal?L"--> ":L"", mirroring); 86 Logger::log(L"{}modesPruned: {}", internal?L"--> ":L"", modesPruned); 87 Logger::log(L"{}primaryDevice: {}", internal?L"--> ":L"", primaryDevice); 88 Logger::log(L"{}removable: {}", internal?L"--> ":L"", removable); 89 Logger::log(L"{}VGA_Compatible: {}", internal?L"--> ":L"", VGA_Compatible); 90 Logger::log(L""); 91 } 92 93 void LogExhaustiveDisplayDevices(bool use_EDD_GET_DEVICE_INTERFACE_NAME) 94 { 95 Logger::log(L" ---- Exhaustive EnumDisplayDevicesW {} EDD_GET_DEVICE_INTERFACE_NAME ---- ", use_EDD_GET_DEVICE_INTERFACE_NAME?L"with":L"without"); 96 DISPLAY_DEVICE displayDevice{ .cb = sizeof(DISPLAY_DEVICE) }; 97 DWORD deviceIdx = 0; 98 while (EnumDisplayDevicesW(nullptr, deviceIdx, &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME)) 99 { 100 LogPrintDisplayDevice(displayDevice, false); 101 DISPLAY_DEVICE displayDeviceInternal{ .cb = sizeof(DISPLAY_DEVICE) }; 102 DWORD deviceIdxInternal = 0; 103 while (EnumDisplayDevicesW(displayDevice.DeviceName, deviceIdxInternal, &displayDeviceInternal, EDD_GET_DEVICE_INTERFACE_NAME)) { 104 Logger::log(L"Inside {} there's:", displayDevice.DeviceName); 105 LogPrintDisplayDevice(displayDeviceInternal, true); 106 deviceIdxInternal++; 107 } 108 deviceIdx++; 109 } 110 } 111 112 void LogEnumDisplayMonitorsProper() 113 { 114 115 auto allMonitors = FancyZonesUtils::GetAllMonitorInfo<&MONITORINFOEX::rcWork>(); 116 117 Logger::log(L" ---- FancyZonesUtils::GetAllMonitorInfo ---- "); 118 for (auto& monitorData : allMonitors) 119 { 120 auto monitorInfo = monitorData.second; 121 Logger::log(L"szDevice: {}", std::wstring(monitorInfo.szDevice)); 122 Logger::log(L"cbSize: {}", monitorInfo.cbSize); 123 Logger::log(L"dwFlags: {}", monitorInfo.dwFlags); 124 Logger::log(L""); 125 } 126 127 LogExhaustiveDisplayDevices(true); 128 LogExhaustiveDisplayDevices(false); 129 130 Logger::log(L""); 131 } 132 133 void LogWMIProp(IWbemClassObject* wbemClassObj, std::wstring_view prop) 134 { 135 if (!wbemClassObj) 136 { 137 return; 138 } 139 140 VARIANT vtProp{}; 141 142 // Get the value of the Name property 143 auto hres = wbemClassObj->Get(prop.data(), 0, &vtProp, 0, 0); 144 if (FAILED(hres)) 145 { 146 Logger::log(L"Get {} Error code = {} ", prop, get_last_error_or_default(hres)); 147 return; 148 } 149 150 switch (vtProp.vt) 151 { 152 case VT_I2: //short 153 { 154 Logger::log(L"{} : {}", prop, vtProp.iVal); 155 } 156 break; 157 case VT_I4: //int, long 158 { 159 Logger::log(L"{} : {}", prop, vtProp.lVal); 160 } 161 break; 162 case VT_BSTR: //BSTR 163 { 164 Logger::log(L"{} : {}", prop, vtProp.bstrVal); 165 } 166 break; 167 case VT_UI1: //BYTE (unsigned char) 168 { 169 Logger::log(L"{} : {}", prop, vtProp.bVal); 170 } 171 break; 172 case VT_ARRAY: // parray 173 case 8195: // also parray 174 { 175 std::u32string str(static_cast<const char32_t*>(vtProp.parray->pvData)); 176 std::wstring wstr; 177 for (const char32_t& c : str) 178 { 179 wstr += (wchar_t)c; 180 } 181 182 Logger::log(L"{} : {}", prop, wstr); 183 } 184 break; 185 default: 186 { 187 Logger::log(L"{} : value is empty", prop); 188 } 189 break; 190 } 191 192 VariantClear(&vtProp); 193 } 194 195 void LogWMI() 196 { 197 Logger::log(L" ---- WMI ---- "); 198 199 HRESULT hres; 200 201 // Initialize COM. 202 hres = CoInitializeEx(0, COINIT_MULTITHREADED); 203 if (FAILED(hres)) 204 { 205 Logger::log(L"Failed to initialize COM library. Error code = ", hres); 206 return; 207 } 208 209 // Initialize 210 hres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, 211 RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL); 212 213 if (FAILED(hres)) 214 { 215 Logger::log(L"Failed to initialize security. Error code = ", hres); 216 CoUninitialize(); 217 return; 218 } 219 220 // Obtain the initial locator to Windows Management 221 // on a particular host computer. 222 IWbemLocator* pLocator = 0; 223 224 hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, reinterpret_cast<LPVOID*>(&pLocator)); 225 if (FAILED(hres)) 226 { 227 Logger::log(L"Failed to create IWbemLocator object. Error code = ", hres); 228 CoUninitialize(); 229 return; 230 } 231 232 IWbemServices* pServices = 0; 233 hres = pLocator->ConnectServer(_bstr_t(L"ROOT\\WMI"), NULL, NULL, 0, NULL, 0, 0, &pServices); 234 235 if (FAILED(hres)) 236 { 237 Logger::log(L"Could not connect WMI server. Error code = ", hres); 238 pLocator->Release(); 239 CoUninitialize(); 240 return; 241 } 242 243 Logger::log(L"Connected to ROOT\\WMI WMI namespace"); 244 Logger::log(L""); 245 246 247 // Set the IWbemServices proxy so that impersonation 248 // of the user (client) occurs. 249 hres = CoSetProxyBlanket(pServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, 250 RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE); 251 252 if (FAILED(hres)) 253 { 254 Logger::log(L"Could not set proxy blanket. Error code = ", hres); 255 pServices->Release(); 256 pLocator->Release(); 257 CoUninitialize(); 258 return; 259 } 260 261 // Use the IWbemServices pointer to make requests of WMI. 262 // Make requests here: 263 IEnumWbemClassObject* pEnumerator = NULL; 264 265 hres = pServices->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM WmiMonitorID"), 266 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); 267 268 if (FAILED(hres)) 269 { 270 Logger::log(L"Query for monitors failed. Error code = ", hres); 271 pServices->Release(); 272 pLocator->Release(); 273 CoUninitialize(); 274 return; 275 } 276 277 IWbemClassObject* pClassObject; 278 ULONG uReturn = 0; 279 280 while (pEnumerator) 281 { 282 hres = pEnumerator->Next(WBEM_INFINITE, 1, &pClassObject, &uReturn); 283 284 if (0 == uReturn) 285 { 286 break; 287 } 288 289 LPSAFEARRAY pFieldArray = NULL; 290 hres = pClassObject->GetNames(NULL, WBEM_FLAG_ALWAYS, NULL, &pFieldArray); 291 if (FAILED(hres)) 292 { 293 Logger::log(L"Failed to get field names. Error code = {}", get_last_error_or_default(hres)); 294 break; 295 } 296 297 LogWMIProp(pClassObject, L"InstanceName"); 298 299 LogWMIProp(pClassObject, L"YearOfManufacture"); 300 LogWMIProp(pClassObject, L"WeekOfManufacture"); 301 302 LogWMIProp(pClassObject, L"UserFriendlyNameLength"); 303 LogWMIProp(pClassObject, L"UserFriendlyName"); 304 LogWMIProp(pClassObject, L"ManufacturerName"); 305 306 LogWMIProp(pClassObject, L"SerialNumberID"); 307 LogWMIProp(pClassObject, L"ProductCodeID"); 308 309 Logger::log(L""); 310 311 pClassObject->Release(); 312 pClassObject = NULL; 313 } 314 315 pServices->Release(); 316 pLocator->Release(); 317 pEnumerator->Release(); 318 319 CoUninitialize(); 320 } 321 322 void LogWMICIMV2() 323 { 324 Logger::log(L" ---- WMI ---- "); 325 326 HRESULT hres; 327 328 hres = CoInitializeEx(0, COINIT_MULTITHREADED); 329 if (FAILED(hres)) 330 { 331 Logger::log(L"Failed to initialize COM library. Error code = ", hres); 332 return; 333 } 334 335 hres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, 336 RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL); 337 338 if (FAILED(hres)) 339 { 340 Logger::log(L"Failed to initialize security. Error code = ", hres); 341 CoUninitialize(); 342 return; 343 } 344 345 // Obtain the initial locator to Windows Management 346 // on a particular host computer. 347 IWbemLocator* pLocator = 0; 348 349 hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, reinterpret_cast<LPVOID*>(&pLocator)); 350 if (FAILED(hres)) 351 { 352 Logger::log(L"Failed to create IWbemLocator object. Error code = ", hres); 353 CoUninitialize(); 354 return; 355 } 356 357 IWbemServices* pServices = 0; 358 359 // Connect to the root\cimv2 namespace with the 360 // current user and obtain pointer pSvc 361 // to make IWbemServices calls. 362 363 hres = pLocator->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &pServices); 364 365 if (FAILED(hres)) 366 { 367 Logger::log(L"Could not connect WMI server. Error code = ", hres); 368 pLocator->Release(); 369 CoUninitialize(); 370 return; 371 } 372 373 Logger::log(L"Connected to ROOT\\CIMV2 WMI namespace"); 374 Logger::log(L""); 375 376 // Set the IWbemServices proxy so that impersonation 377 // of the user (client) occurs. 378 hres = CoSetProxyBlanket(pServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, 379 RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE); 380 381 if (FAILED(hres)) 382 { 383 Logger::log(L"Could not set proxy blanket. Error code = ", hres); 384 pServices->Release(); 385 pLocator->Release(); 386 CoUninitialize(); 387 return; 388 } 389 390 // Use the IWbemServices pointer to make requests of WMI. 391 // Make requests here: 392 IEnumWbemClassObject* pEnumerator = NULL; 393 hres = pServices->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM Win32_DesktopMonitor"), 394 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); 395 396 if (FAILED(hres)) 397 { 398 Logger::log(L"Query for monitors failed. Error code = ", hres); 399 pServices->Release(); 400 pLocator->Release(); 401 CoUninitialize(); 402 return; 403 } 404 405 IWbemClassObject* pClassObject; 406 ULONG uReturn = 0; 407 408 while (pEnumerator) 409 { 410 hres = pEnumerator->Next(WBEM_INFINITE, 1, &pClassObject, &uReturn); 411 412 if (0 == uReturn) 413 { 414 break; 415 } 416 417 LogWMIProp(pClassObject, L"DeviceID"); 418 LogWMIProp(pClassObject, L"Caption"); 419 LogWMIProp(pClassObject, L"Description"); 420 LogWMIProp(pClassObject, L"MonitorManufacturer"); 421 LogWMIProp(pClassObject, L"MonitorType"); 422 LogWMIProp(pClassObject, L"Name"); 423 LogWMIProp(pClassObject, L"PNPDeviceID"); 424 LogWMIProp(pClassObject, L"Status"); 425 426 LogWMIProp(pClassObject, L"Availability"); 427 428 Logger::log(L""); 429 430 pClassObject->Release(); 431 pClassObject = NULL; 432 } 433 434 pServices->Release(); 435 pLocator->Release(); 436 pEnumerator->Release(); 437 438 CoUninitialize(); 439 } 440 441 void LogCCD() 442 { 443 Logger::log(L" ---- CCD ---- "); 444 445 LONG result = ERROR_SUCCESS; 446 std::vector<DISPLAYCONFIG_PATH_INFO> paths; 447 std::vector<DISPLAYCONFIG_MODE_INFO> modes; 448 449 do 450 { 451 UINT32 pathCount{}, modeCount{}; 452 auto sizesResult = GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS | QDC_INCLUDE_HMD | QDC_VIRTUAL_MODE_AWARE, &pathCount, &modeCount); 453 454 if (sizesResult != ERROR_SUCCESS) 455 { 456 Logger::log(L"GetDisplayConfigBufferSizes error {}", get_last_error_or_default(sizesResult)); 457 return; 458 } 459 460 paths.resize(pathCount); 461 paths.resize(modeCount); 462 463 auto result = QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS | QDC_INCLUDE_HMD | QDC_VIRTUAL_MODE_AWARE, &pathCount, paths.data() 464 , &modeCount, modes.data(), nullptr); 465 466 // The function may have returned fewer paths/modes than estimated 467 paths.resize(pathCount); 468 modes.resize(modeCount); 469 } while (result == ERROR_INSUFFICIENT_BUFFER); 470 471 if (result != ERROR_SUCCESS) 472 { 473 Logger::log(L"QueryDisplayConfig error {}", get_last_error_or_default(result)); 474 return; 475 } 476 477 // For each active path 478 for (auto& path : paths) 479 { 480 // Find the target (monitor) friendly name 481 DISPLAYCONFIG_TARGET_DEVICE_NAME targetName = {}; 482 targetName.header.adapterId = path.targetInfo.adapterId; 483 targetName.header.id = path.targetInfo.id; 484 targetName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME; 485 targetName.header.size = sizeof(targetName); 486 result = DisplayConfigGetDeviceInfo(&targetName.header); 487 488 if (result != ERROR_SUCCESS) 489 { 490 Logger::log(L"DisplayConfigGetDeviceInfo error {}", get_last_error_or_default(result)); 491 } 492 493 // Find the adapter device name 494 DISPLAYCONFIG_ADAPTER_NAME adapterName = {}; 495 adapterName.header.adapterId = path.targetInfo.adapterId; 496 adapterName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME; 497 adapterName.header.size = sizeof(adapterName); 498 499 result = DisplayConfigGetDeviceInfo(&adapterName.header); 500 501 if (result != ERROR_SUCCESS) 502 { 503 Logger::log(L"DisplayConfigGetDeviceInfo error {}", get_last_error_or_default(result)); 504 continue; 505 } 506 507 Logger::log(L"Monitor: {} connected to adapter {}" 508 , (targetName.flags.friendlyNameFromEdid ? targetName.monitorFriendlyDeviceName : L"Unknown") 509 , adapterName.adapterDevicePath); 510 } 511 } 512 513 void LogInfo() 514 { 515 Logger::log(L"Timestamp: {}", std::chrono::system_clock::now()); 516 Logger::log(L""); 517 518 LogEnumDisplayMonitors(); 519 LogEnumDisplayMonitorsProper(); 520 LogWMICIMV2(); 521 LogWMI(); 522 LogCCD(); 523 524 Logger::log(L"======================================="); 525 Logger::log(L""); 526 } 527 528 int APIENTRY wWinMain(_In_ HINSTANCE hInstance, 529 _In_opt_ HINSTANCE hPrevInstance, 530 _In_ LPWSTR lpCmdLine, 531 _In_ int nCmdShow) 532 { 533 UNREFERENCED_PARAMETER(hPrevInstance); 534 UNREFERENCED_PARAMETER(lpCmdLine); 535 536 Logger::init("MonitorReportTool"); 537 538 LogInfo(); 539 540 return 0; 541 }