/ tools / MonitorReportTool / MonitorReportTool.cpp
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  }