1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 // Functions to enumerate the Dx Diagnostic Tool hierarchy and build up
6 // a tree of nodes with name / value properties.
7 
8 #define INITGUID
9 #include <dxdiag.h>
10 #include <windows.h>
11 
12 #include "base/stl_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/win/com_init_util.h"
16 #include "gpu/config/gpu_info_collector.h"
17 
18 namespace gpu {
19 
20 namespace {
21 
22 // Traverses the IDxDiagContainer tree and populates a tree of DxDiagNode
23 // structures that contains property name / value pairs and subtrees of DirectX
24 // diagnostic information.
RecurseDiagnosticTree(DxDiagNode * output,IDxDiagContainer * container,int depth)25 void RecurseDiagnosticTree(DxDiagNode* output,
26                            IDxDiagContainer* container,
27                            int depth) {
28   HRESULT hr;
29 
30   VARIANT variant;
31   VariantInit(&variant);
32 
33   DWORD prop_count;
34   hr = container->GetNumberOfProps(&prop_count);
35   if (SUCCEEDED(hr)) {
36     for (DWORD i = 0; i < prop_count; i++) {
37       WCHAR prop_name16[256];
38       hr = container->EnumPropNames(i, prop_name16, base::size(prop_name16));
39       if (SUCCEEDED(hr)) {
40         std::string prop_name8 = base::WideToUTF8(prop_name16);
41 
42         hr = container->GetProp(prop_name16, &variant);
43         if (SUCCEEDED(hr)) {
44           switch (variant.vt) {
45             case VT_UI4:
46               output->values[prop_name8] = base::NumberToString(variant.ulVal);
47               break;
48             case VT_I4:
49               output->values[prop_name8] = base::NumberToString(variant.lVal);
50               break;
51             case VT_BOOL:
52               output->values[prop_name8] = variant.boolVal ? "true" : "false";
53               break;
54             case VT_BSTR:
55               output->values[prop_name8] = base::WideToUTF8(variant.bstrVal);
56               break;
57             default:
58               break;
59           }
60 
61           // Clear the variant (this is needed to free BSTR memory).
62           VariantClear(&variant);
63         }
64       }
65     }
66   }
67 
68   if (depth > 0) {
69     DWORD child_count;
70     hr = container->GetNumberOfChildContainers(&child_count);
71     if (SUCCEEDED(hr)) {
72       for (DWORD i = 0; i < child_count; i++) {
73         WCHAR child_name16[256];
74         hr = container->EnumChildContainerNames(i, child_name16,
75                                                 base::size(child_name16));
76         if (SUCCEEDED(hr)) {
77           std::string child_name8 = base::WideToUTF8(child_name16);
78           DxDiagNode* output_child = &output->children[child_name8];
79 
80           IDxDiagContainer* child_container = nullptr;
81           hr = container->GetChildContainer(child_name16, &child_container);
82           if (SUCCEEDED(hr)) {
83             RecurseDiagnosticTree(output_child, child_container, depth - 1);
84 
85             child_container->Release();
86           }
87         }
88       }
89     }
90   }
91 }
92 }  // namespace anonymous
93 
GetDxDiagnostics(DxDiagNode * output)94 bool GetDxDiagnostics(DxDiagNode* output) {
95   // CLSID_DxDiagProvider is configured as an STA only object.
96   base::win::AssertComApartmentType(base::win::ComApartmentType::STA);
97 
98   HRESULT hr;
99   bool success = false;
100   IDxDiagProvider* provider = nullptr;
101   hr = CoCreateInstance(CLSID_DxDiagProvider, nullptr, CLSCTX_INPROC_SERVER,
102                         IID_IDxDiagProvider,
103                         reinterpret_cast<void**>(&provider));
104   if (SUCCEEDED(hr)) {
105     DXDIAG_INIT_PARAMS params = { sizeof(params) };
106     params.dwDxDiagHeaderVersion = DXDIAG_DX9_SDK_VERSION;
107     params.bAllowWHQLChecks = FALSE;
108     params.pReserved = nullptr;
109 
110     hr = provider->Initialize(&params);
111     if (SUCCEEDED(hr)) {
112       IDxDiagContainer* root = nullptr;
113       hr = provider->GetRootContainer(&root);
114       if (SUCCEEDED(hr)) {
115         // Limit to the DisplayDevices subtree. The tree in its entirity is
116         // enormous and only this branch contains useful information.
117         IDxDiagContainer* display_devices = nullptr;
118         hr = root->GetChildContainer(L"DxDiag_DisplayDevices",
119                                      &display_devices);
120         if (SUCCEEDED(hr)) {
121           RecurseDiagnosticTree(output, display_devices, 1);
122           success = true;
123           display_devices->Release();
124         }
125 
126         root->Release();
127       }
128     }
129     provider->Release();
130   }
131 
132   return success;
133 }
134 }  // namespace gpu
135