wmiVideoInfo.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #define _WIN32_DCOM
  23. //#include <comdef.h>
  24. #include <wbemidl.h>
  25. //#include <atlconv.h>
  26. #include <DXGI.h>
  27. #pragma comment(lib, "comsuppw.lib")
  28. #pragma comment(lib, "wbemuuid.lib")
  29. #include "platformWin32/videoInfo/wmiVideoInfo.h"
  30. #include "core/util/safeRelease.h"
  31. #include "console/console.h"
  32. // http://www.spectranaut.net/sourcecode/WMI.cpp
  33. // Add constructor to GUID.
  34. struct MYGUID : public GUID
  35. {
  36. MYGUID( DWORD a, SHORT b, SHORT c, BYTE d, BYTE e, BYTE f, BYTE g, BYTE h, BYTE i, BYTE j, BYTE k )
  37. {
  38. Data1 = a;
  39. Data2 = b;
  40. Data3 = c;
  41. Data4[ 0 ] = d;
  42. Data4[ 1 ] = e;
  43. Data4[ 2 ] = f;
  44. Data4[ 3 ] = g;
  45. Data4[ 4 ] = h;
  46. Data4[ 5 ] = i;
  47. Data4[ 6 ] = j;
  48. Data4[ 7 ] = k;
  49. }
  50. };
  51. //------------------------------------------------------------------------------
  52. // DXDIAG declarations.
  53. struct DXDIAG_INIT_PARAMS
  54. {
  55. DWORD dwSize;
  56. DWORD dwDxDiagHeaderVersion;
  57. BOOL bAllowWHQLChecks;
  58. LPVOID pReserved;
  59. };
  60. struct IDxDiagContainer : public IUnknown
  61. {
  62. virtual HRESULT STDMETHODCALLTYPE GetNumberOfChildContainers( DWORD* pdwCount ) = 0;
  63. virtual HRESULT STDMETHODCALLTYPE EnumChildContainerNames( DWORD dwIndex, LPWSTR pwszContainer, DWORD cchContainer ) = 0;
  64. virtual HRESULT STDMETHODCALLTYPE GetChildContainer( LPCWSTR pwszContainer, IDxDiagContainer** ppInstance ) = 0;
  65. virtual HRESULT STDMETHODCALLTYPE GetNumberOfProps( DWORD* pdwCount ) = 0;
  66. virtual HRESULT STDMETHODCALLTYPE EnumPropNames( DWORD dwIndex, LPWSTR pwszPropName, DWORD cchPropName ) = 0;
  67. virtual HRESULT STDMETHODCALLTYPE GetProp( LPCWSTR pwszPropName, VARIANT* pvarProp ) = 0;
  68. };
  69. struct IDxDiagProvider : public IUnknown
  70. {
  71. virtual HRESULT STDMETHODCALLTYPE Initialize( DXDIAG_INIT_PARAMS* pParams ) = 0;
  72. virtual HRESULT STDMETHODCALLTYPE GetRootContainer( IDxDiagContainer** ppInstance ) = 0;
  73. };
  74. static MYGUID CLSID_DxDiagProvider( 0xA65B8071, 0x3BFE, 0x4213, 0x9A, 0x5B, 0x49, 0x1D, 0xA4, 0x46, 0x1C, 0xA7 );
  75. static MYGUID IID_IDxDiagProvider( 0x9C6B4CB0, 0x23F8, 0x49CC, 0xA3, 0xED, 0x45, 0xA5, 0x50, 0x00, 0xA6, 0xD2 );
  76. static MYGUID IID_IDxDiagContainer( 0x7D0F462F, 0x4064, 0x4862, 0xBC, 0x7F, 0x93, 0x3E, 0x50, 0x58, 0xC1, 0x0F );
  77. //------------------------------------------------------------------------------
  78. const WCHAR *WMIVideoInfo::smPVIQueryTypeToWMIString [] =
  79. {
  80. L"MaxNumberControlled", //PVI_NumDevices
  81. L"Description", //PVI_Description
  82. L"Name", //PVI_Name
  83. L"VideoProcessor", //PVI_ChipSet
  84. L"DriverVersion", //PVI_DriverVersion
  85. L"AdapterRAM", //PVI_VRAM
  86. };
  87. //------------------------------------------------------------------------------
  88. WMIVideoInfo::WMIVideoInfo()
  89. : PlatformVideoInfo(),
  90. mLocator( NULL ),
  91. mServices( NULL ),
  92. mComInitialized( false ),
  93. mDXGIModule( NULL ),
  94. mDXGIFactory( NULL ),
  95. mDxDiagProvider( NULL )
  96. {
  97. }
  98. //------------------------------------------------------------------------------
  99. WMIVideoInfo::~WMIVideoInfo()
  100. {
  101. SAFE_RELEASE( mLocator );
  102. SAFE_RELEASE( mServices );
  103. if( mDxDiagProvider )
  104. SAFE_RELEASE( mDxDiagProvider );
  105. if( mDXGIFactory )
  106. SAFE_RELEASE( mDXGIFactory );
  107. if( mDXGIModule )
  108. FreeLibrary( ( HMODULE ) mDXGIModule );
  109. if( mComInitialized )
  110. CoUninitialize();
  111. }
  112. //------------------------------------------------------------------------------
  113. String WMIVideoInfo::_lookUpVendorId(U32 vendorId)
  114. {
  115. String vendor;
  116. switch (vendorId)
  117. {
  118. case 0x10DE:
  119. vendor = "NVIDIA";
  120. break;
  121. case 0x1002:
  122. vendor = "AMD";
  123. break;
  124. case 0x8086:
  125. vendor = "INTEL";
  126. break;
  127. }
  128. return vendor;
  129. }
  130. //------------------------------------------------------------------------------
  131. bool WMIVideoInfo::_initialize()
  132. {
  133. // Init COM
  134. HRESULT hr = CoInitialize( NULL );
  135. mComInitialized = SUCCEEDED( hr );
  136. if( !mComInitialized )
  137. return false;
  138. bool success = false;
  139. success |= _initializeDXGI();
  140. success |= _initializeDxDiag();
  141. success |= _initializeWMI();
  142. return success;
  143. }
  144. bool WMIVideoInfo::_initializeWMI()
  145. {
  146. //// Set security levels
  147. //hr = CoInitializeSecurity(
  148. // NULL,
  149. // -1, // COM authentication
  150. // NULL, // Authentication services
  151. // NULL, // Reserved
  152. // RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
  153. // RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
  154. // NULL, // Authentication info
  155. // EOAC_NONE, // Additional capabilities
  156. // NULL // Reserved
  157. // );
  158. //if( FAILED( hr ) )
  159. //{
  160. // Con::errorf( "WMIVideoInfo: Failed to initialize com security." );
  161. // return false;
  162. //}
  163. // Obtain the locator to WMI
  164. HRESULT hr = CoCreateInstance(
  165. CLSID_WbemLocator,
  166. 0,
  167. CLSCTX_INPROC_SERVER,
  168. IID_IWbemLocator,
  169. (void**)&mLocator
  170. );
  171. if( FAILED( hr ) )
  172. {
  173. Con::errorf( "WMIVideoInfo: Failed to create instance of IID_IWbemLocator." );
  174. return false;
  175. }
  176. // Connect to the root\cimv2 namespace with
  177. // the current user and obtain pointer pSvc
  178. // to make IWbemServices calls.
  179. hr = mLocator->ConnectServer(
  180. BSTR(L"ROOT\\CIMV2"), // Object path of WMI namespace
  181. NULL, // User name. NULL = current user
  182. NULL, // User password. NULL = current
  183. 0, // Locale. NULL indicates current
  184. NULL, // Security flags.
  185. 0, // Authority (e.g. Kerberos)
  186. 0, // Context object
  187. &mServices // pointer to IWbemServices proxy
  188. );
  189. if( FAILED( hr ) )
  190. {
  191. Con::errorf( "WMIVideoInfo: Connect server failed." );
  192. return false;
  193. }
  194. // Set security levels on the proxy
  195. hr = CoSetProxyBlanket(
  196. mServices, // Indicates the proxy to set
  197. RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
  198. RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
  199. NULL, // Server principal name
  200. RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
  201. RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
  202. NULL, // client identity
  203. EOAC_NONE // proxy capabilities
  204. );
  205. if( FAILED( hr ) )
  206. {
  207. Con::errorf( "WMIVideoInfo: CoSetProxyBlanket failed" );
  208. return false;
  209. }
  210. return true;
  211. }
  212. bool WMIVideoInfo::_initializeDXGI()
  213. {
  214. // Try using for DXGI 1.1, will only succeed on Windows 7+.
  215. mDXGIModule = ( HMODULE ) LoadLibrary( L"dxgi.dll" );
  216. if( mDXGIModule != 0 )
  217. {
  218. typedef HRESULT (WINAPI* CreateDXGIFactoryFuncType )( REFIID, void** );
  219. CreateDXGIFactoryFuncType factoryFunction =
  220. ( CreateDXGIFactoryFuncType ) GetProcAddress( ( HMODULE ) mDXGIModule, "CreateDXGIFactory1" );
  221. if( factoryFunction && factoryFunction( IID_IDXGIFactory1, ( void** ) &mDXGIFactory ) == S_OK )
  222. return true;
  223. else
  224. {
  225. FreeLibrary( ( HMODULE ) mDXGIModule );
  226. mDXGIModule = 0;
  227. }
  228. }
  229. return false;
  230. }
  231. bool WMIVideoInfo::_initializeDxDiag()
  232. {
  233. if( CoCreateInstance( CLSID_DxDiagProvider, NULL, CLSCTX_INPROC_SERVER, IID_IDxDiagProvider, ( void** ) &mDxDiagProvider ) == S_OK )
  234. {
  235. DXDIAG_INIT_PARAMS params;
  236. dMemset( &params, 0, sizeof( DXDIAG_INIT_PARAMS ) );
  237. params.dwSize = sizeof( DXDIAG_INIT_PARAMS );
  238. params.dwDxDiagHeaderVersion = 111;
  239. params.bAllowWHQLChecks = false;
  240. HRESULT result = mDxDiagProvider->Initialize( &params );
  241. if( result != S_OK )
  242. {
  243. Con::errorf( "WMIVideoInfo: DxDiag initialization failed (%i)", result );
  244. SAFE_RELEASE( mDxDiagProvider );
  245. return false;
  246. }
  247. else
  248. {
  249. Con::printf( "WMIVideoInfo: DxDiag initialized" );
  250. return true;
  251. }
  252. }
  253. return false;
  254. }
  255. //------------------------------------------------------------------------------
  256. // http://msdn2.microsoft.com/en-us/library/aa394512.aspx
  257. //
  258. // The Win32_VideoController WMI class represents the capabilities and management capacity of the
  259. // video controller on a computer system running Windows.
  260. //
  261. // Starting with Windows Vista, hardware that is not compatible with Windows Display Driver Model (WDDM)
  262. // returns inaccurate property values for instances of this class.
  263. //
  264. // Windows Server 2003, Windows XP, Windows 2000, and Windows NT 4.0: This class is supported.
  265. //------------------------------------------------------------------------------
  266. bool WMIVideoInfo::_queryProperty( const PVIQueryType queryType, const U32 adapterId, String *outValue )
  267. {
  268. if( _queryPropertyDXGI( queryType, adapterId, outValue ) )
  269. return true;
  270. else if( _queryPropertyDxDiag( queryType, adapterId, outValue ) )
  271. return true;
  272. else
  273. return _queryPropertyWMI( queryType, adapterId, outValue );
  274. }
  275. bool WMIVideoInfo::_queryPropertyDxDiag( const PVIQueryType queryType, const U32 adapterId, String *outValue )
  276. {
  277. if( mDxDiagProvider != 0 )
  278. {
  279. IDxDiagContainer* rootContainer = 0;
  280. IDxDiagContainer* displayDevicesContainer = 0;
  281. IDxDiagContainer* deviceContainer = 0;
  282. // Special case to deal with PVI_NumAdapters
  283. if(queryType == PVI_NumAdapters)
  284. {
  285. DWORD count = 0;
  286. String value;
  287. if( mDxDiagProvider->GetRootContainer( &rootContainer ) == S_OK
  288. && rootContainer->GetChildContainer( L"DxDiag_DisplayDevices", &displayDevicesContainer ) == S_OK
  289. && displayDevicesContainer->GetNumberOfChildContainers( &count ) == S_OK )
  290. {
  291. value = String::ToString("%d", count);
  292. }
  293. if( rootContainer )
  294. SAFE_RELEASE( rootContainer );
  295. if( displayDevicesContainer )
  296. SAFE_RELEASE( displayDevicesContainer );
  297. *outValue = value;
  298. return true;
  299. }
  300. WCHAR adapterIdString[ 2 ];
  301. adapterIdString[ 0 ] = L'0' + adapterId;
  302. adapterIdString[ 1 ] = L'\0';
  303. String value;
  304. if( mDxDiagProvider->GetRootContainer( &rootContainer ) == S_OK
  305. && rootContainer->GetChildContainer( L"DxDiag_DisplayDevices", &displayDevicesContainer ) == S_OK
  306. && displayDevicesContainer->GetChildContainer( adapterIdString, &deviceContainer ) == S_OK )
  307. {
  308. const WCHAR* propertyName = 0;
  309. switch( queryType )
  310. {
  311. case PVI_Description:
  312. propertyName = L"szDescription";
  313. break;
  314. case PVI_Name:
  315. propertyName = L"szDeviceName";
  316. break;
  317. case PVI_ChipSet:
  318. propertyName = L"szChipType";
  319. break;
  320. case PVI_DriverVersion:
  321. propertyName = L"szDriverVersion";
  322. break;
  323. // Don't get VRAM via DxDiag as that won't tell us about the actual amount of dedicated
  324. // video memory but rather some dedicated+shared RAM value.
  325. }
  326. if( propertyName )
  327. {
  328. VARIANT val;
  329. if( deviceContainer->GetProp( propertyName, &val ) == S_OK )
  330. switch( val.vt )
  331. {
  332. case VT_BSTR:
  333. value = String( val.bstrVal );
  334. break;
  335. default:
  336. AssertWarn( false, avar( "WMIVideoInfo: property type '%i' not implemented", val.vt ) );
  337. }
  338. }
  339. }
  340. if( rootContainer )
  341. SAFE_RELEASE( rootContainer );
  342. if( displayDevicesContainer )
  343. SAFE_RELEASE( displayDevicesContainer );
  344. if( deviceContainer )
  345. SAFE_RELEASE( deviceContainer );
  346. if( value.isNotEmpty() )
  347. {
  348. // Try to get the DxDiag data into some canonical form. Otherwise, we
  349. // won't be giving the card profiler much opportunity for matching up
  350. // its data with profile scripts.
  351. switch( queryType )
  352. {
  353. case PVI_ChipSet:
  354. if( value.compare( "ATI", 3, String::NoCase ) == 0 )
  355. value = "ATI Technologies Inc.";
  356. else if( value.compare( "NVIDIA", 6, String::NoCase ) == 0 )
  357. value = "NVIDIA";
  358. else if( value.compare( "INTEL", 5, String::NoCase ) == 0 )
  359. value = "INTEL";
  360. else if( value.compare( "MATROX", 6, String::NoCase ) == 0 )
  361. value = "MATROX";
  362. break;
  363. case PVI_Description:
  364. if( value.compare( "ATI ", 4, String::NoCase ) == 0 )
  365. {
  366. value = value.substr( 4, value.length() - 4 );
  367. if( value.compare( " Series", 7, String::NoCase | String::Right ) == 0 )
  368. value = value.substr( 0, value.length() - 7 );
  369. }
  370. else if( value.compare( "NVIDIA ", 7, String::NoCase ) == 0 )
  371. value = value.substr( 7, value.length() - 7 );
  372. else if( value.compare( "INTEL ", 6, String::NoCase ) == 0 )
  373. value = value.substr( 6, value.length() - 6 );
  374. else if( value.compare( "MATROX ", 7, String::NoCase ) == 0 )
  375. value = value.substr( 7, value.length() - 7 );
  376. break;
  377. }
  378. *outValue = value;
  379. return true;
  380. }
  381. }
  382. return false;
  383. }
  384. bool WMIVideoInfo::_queryPropertyDXGI( const PVIQueryType queryType, const U32 adapterId, String *outValue )
  385. {
  386. if( mDXGIFactory )
  387. {
  388. // Special case to deal with PVI_NumAdapters
  389. if (queryType == PVI_NumAdapters)
  390. {
  391. U32 count = 0;
  392. IDXGIAdapter1 *adapter;
  393. while (mDXGIFactory->EnumAdapters1(count, &adapter) != DXGI_ERROR_NOT_FOUND)
  394. {
  395. ++count;
  396. adapter->Release();
  397. }
  398. String value = String::ToString("%d", count);
  399. *outValue = value;
  400. return true;
  401. }
  402. IDXGIAdapter1* adapter;
  403. if( mDXGIFactory->EnumAdapters1( adapterId, &adapter ) != S_OK )
  404. return false;
  405. DXGI_ADAPTER_DESC1 desc;
  406. if( adapter->GetDesc1( &desc ) != S_OK )
  407. {
  408. adapter->Release();
  409. return false;
  410. }
  411. String value;
  412. switch( queryType )
  413. {
  414. case PVI_Description:
  415. value = String( desc.Description );
  416. break;
  417. case PVI_Name:
  418. value = String( avar( "%i", desc.DeviceId ) );
  419. break;
  420. case PVI_VRAM:
  421. value = String( avar( "%i", desc.DedicatedVideoMemory / 1048576 ) );
  422. break;
  423. case PVI_ChipSet:
  424. value = _lookUpVendorId(desc.VendorId);
  425. break;
  426. //TODO PVI_DriverVersion
  427. }
  428. adapter->Release();
  429. *outValue = value;
  430. return true;
  431. }
  432. return false;
  433. }
  434. bool WMIVideoInfo::_queryPropertyWMI( const PVIQueryType queryType, const U32 adapterId, String *outValue )
  435. {
  436. if( mServices == NULL )
  437. return false;
  438. BSTR bstrWQL = SysAllocString(L"WQL");
  439. BSTR bstrPath = SysAllocString(L"select * from Win32_VideoController");
  440. IEnumWbemClassObject* enumerator;
  441. // Use the IWbemServices pointer to make requests of WMI
  442. HRESULT hr = mServices->ExecQuery(bstrWQL, bstrPath, WBEM_FLAG_FORWARD_ONLY, NULL, &enumerator);
  443. if( FAILED( hr ) )
  444. return false;
  445. IWbemClassObject *adapter = NULL;
  446. ULONG uReturned;
  447. // Get the appropriate adapter.
  448. for ( S32 i = 0; i <= adapterId; i++ )
  449. {
  450. hr = enumerator->Next(WBEM_INFINITE, 1, &adapter, &uReturned );
  451. if ( FAILED( hr ) || uReturned == 0 )
  452. {
  453. enumerator->Release();
  454. return false;
  455. }
  456. }
  457. // Now get the property
  458. VARIANT v;
  459. hr = adapter->Get( smPVIQueryTypeToWMIString[queryType], 0, &v, NULL, NULL );
  460. bool result = SUCCEEDED( hr );
  461. if ( result )
  462. {
  463. switch( v.vt )
  464. {
  465. case VT_I4:
  466. {
  467. LONG longVal = v.lVal;
  468. if( queryType == PVI_VRAM )
  469. {
  470. longVal = longVal >> 20; // Convert to megabytes
  471. // While this value is reported as a signed integer, it is possible
  472. // for video cards to have 2GB or more. In those cases the signed
  473. // bit is set and will give us a negative number. Treating this
  474. // as unsigned will allows us to handle video cards with up to
  475. // 4GB of memory. After that we'll need a new solution from Microsoft.
  476. *outValue = String::ToString( (U32)longVal );
  477. }
  478. else
  479. {
  480. *outValue = String::ToString( (S32)longVal );
  481. }
  482. break;
  483. }
  484. case VT_UI4:
  485. {
  486. *outValue = String::ToString( (U32)v.ulVal );
  487. break;
  488. }
  489. case VT_BSTR:
  490. {
  491. *outValue = String( v.bstrVal );
  492. break;
  493. }
  494. case VT_LPSTR:
  495. case VT_LPWSTR:
  496. break;
  497. }
  498. }
  499. // Cleanup
  500. adapter->Release();
  501. enumerator->Release();
  502. return result;
  503. }