lldxhardware.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:18k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file lldxhardware.cpp
  3.  * @brief LLDXHardware implementation
  4.  *
  5.  * $LicenseInfo:firstyear=2001&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2001-2010, Linden Research, Inc.
  8.  * 
  9.  * Second Life Viewer Source Code
  10.  * The source code in this file ("Source Code") is provided by Linden Lab
  11.  * to you under the terms of the GNU General Public License, version 2.0
  12.  * ("GPL"), unless you have obtained a separate licensing agreement
  13.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  14.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16.  * 
  17.  * There are special exceptions to the terms and conditions of the GPL as
  18.  * it is applied to this Source Code. View the full text of the exception
  19.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  20.  * online at
  21.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22.  * 
  23.  * By copying, modifying or distributing this software, you acknowledge
  24.  * that you have read and understood your obligations described above,
  25.  * and agree to abide by those obligations.
  26.  * 
  27.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29.  * COMPLETENESS OR PERFORMANCE.
  30.  * $/LicenseInfo$
  31.  */
  32. #ifdef LL_WINDOWS
  33. // Culled from some Microsoft sample code
  34. #include "linden_common.h"
  35. #define INITGUID
  36. #include <dxdiag.h>
  37. #undef INITGUID
  38. #include <boost/tokenizer.hpp>
  39. #include "lldxhardware.h"
  40. #include "llerror.h"
  41. #include "llstring.h"
  42. #include "llstl.h"
  43. #include "lltimer.h"
  44. void (*gWriteDebug)(const char* msg) = NULL;
  45. LLDXHardware gDXHardware;
  46. //-----------------------------------------------------------------------------
  47. // Defines, and constants
  48. //-----------------------------------------------------------------------------
  49. #define SAFE_DELETE(p)       { if(p) { delete (p);     (p)=NULL; } }
  50. #define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p);   (p)=NULL; } }
  51. #define SAFE_RELEASE(p)      { if(p) { (p)->Release(); (p)=NULL; } }
  52. std::string get_string(IDxDiagContainer *containerp, WCHAR *wszPropName)
  53. {
  54.     HRESULT hr;
  55. VARIANT var;
  56. WCHAR wszPropValue[256];
  57. VariantInit( &var );
  58. hr = containerp->GetProp(wszPropName, &var );
  59. if( SUCCEEDED(hr) )
  60. {
  61. // Switch off the type.  There's 4 different types:
  62. switch( var.vt )
  63. {
  64. case VT_UI4:
  65. swprintf( wszPropValue, L"%d", var.ulVal ); /* Flawfinder: ignore */
  66. break;
  67. case VT_I4:
  68. swprintf( wszPropValue, L"%d", var.lVal ); /* Flawfinder: ignore */
  69. break;
  70. case VT_BOOL:
  71. wcscpy( wszPropValue, (var.boolVal) ? L"true" : L"false" ); /* Flawfinder: ignore */
  72. break;
  73. case VT_BSTR:
  74. wcsncpy( wszPropValue, var.bstrVal, 255 ); /* Flawfinder: ignore */
  75. wszPropValue[255] = 0;
  76. break;
  77. }
  78. }
  79. // Clear the variant (this is needed to free BSTR memory)
  80. VariantClear( &var );
  81. return utf16str_to_utf8str(wszPropValue);
  82. }
  83. LLVersion::LLVersion()
  84. {
  85. mValid = FALSE;
  86. S32 i;
  87. for (i = 0; i < 4; i++)
  88. {
  89. mFields[i] = 0;
  90. }
  91. }
  92. BOOL LLVersion::set(const std::string &version_string)
  93. {
  94. S32 i;
  95. for (i = 0; i < 4; i++)
  96. {
  97. mFields[i] = 0;
  98. }
  99. // Split the version string.
  100. std::string str(version_string);
  101. typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
  102. boost::char_separator<char> sep(".", "", boost::keep_empty_tokens);
  103. tokenizer tokens(str, sep);
  104. tokenizer::iterator iter = tokens.begin();
  105. S32 count = 0;
  106. for (;(iter != tokens.end()) && (count < 4);++iter)
  107. {
  108. mFields[count] = atoi(iter->c_str());
  109. count++;
  110. }
  111. if (count < 4)
  112. {
  113. //llwarns << "Potentially bogus version string!" << version_string << llendl;
  114. for (i = 0; i < 4; i++)
  115. {
  116. mFields[i] = 0;
  117. }
  118. mValid = FALSE;
  119. }
  120. else
  121. {
  122. mValid = TRUE;
  123. }
  124. return mValid;
  125. }
  126. S32 LLVersion::getField(const S32 field_num)
  127. {
  128. if (!mValid)
  129. {
  130. return -1;
  131. }
  132. else
  133. {
  134. return mFields[field_num];
  135. }
  136. }
  137. std::string LLDXDriverFile::dump()
  138. {
  139. if (gWriteDebug)
  140. {
  141. gWriteDebug("Filename:");
  142. gWriteDebug(mName.c_str());
  143. gWriteDebug("n");
  144. gWriteDebug("Ver:");
  145. gWriteDebug(mVersionString.c_str());
  146. gWriteDebug("n");
  147. gWriteDebug("Date:");
  148. gWriteDebug(mDateString.c_str());
  149. gWriteDebug("n");
  150. }
  151. llinfos << mFilepath << llendl;
  152. llinfos << mName << llendl;
  153. llinfos << mVersionString << llendl;
  154. llinfos << mDateString << llendl;
  155. return "";
  156. }
  157. LLDXDevice::~LLDXDevice()
  158. {
  159. for_each(mDriverFiles.begin(), mDriverFiles.end(), DeletePairedPointer());
  160. }
  161. std::string LLDXDevice::dump()
  162. {
  163. if (gWriteDebug)
  164. {
  165. gWriteDebug("StartDevicen");
  166. gWriteDebug("DeviceName:");
  167. gWriteDebug(mName.c_str());
  168. gWriteDebug("n");
  169. gWriteDebug("PCIString:");
  170. gWriteDebug(mPCIString.c_str());
  171. gWriteDebug("n");
  172. }
  173. llinfos << llendl;
  174. llinfos << "DeviceName:" << mName << llendl;
  175. llinfos << "PCIString:" << mPCIString << llendl;
  176. llinfos << "Drivers" << llendl;
  177. llinfos << "-------" << llendl;
  178. for (driver_file_map_t::iterator iter = mDriverFiles.begin(),
  179.  end = mDriverFiles.end();
  180.  iter != end; iter++)
  181. {
  182. LLDXDriverFile *filep = iter->second;
  183. filep->dump();
  184. }
  185. if (gWriteDebug)
  186. {
  187. gWriteDebug("EndDevicen");
  188. }
  189. return "";
  190. }
  191. LLDXDriverFile *LLDXDevice::findDriver(const std::string &driver)
  192. {
  193. for (driver_file_map_t::iterator iter = mDriverFiles.begin(),
  194.  end = mDriverFiles.end();
  195.  iter != end; iter++)
  196. {
  197. LLDXDriverFile *filep = iter->second;
  198. if (!utf8str_compare_insensitive(filep->mName,driver))
  199. {
  200. return filep;
  201. }
  202. }
  203. return NULL;
  204. }
  205. LLDXHardware::LLDXHardware()
  206. {
  207. mVRAM = 0;
  208. gWriteDebug = NULL;
  209. }
  210. void LLDXHardware::cleanup()
  211. {
  212.   // for_each(mDevices.begin(), mDevices.end(), DeletePairedPointer());
  213. }
  214. /*
  215. std::string LLDXHardware::dumpDevices()
  216. {
  217. if (gWriteDebug)
  218. {
  219. gWriteDebug("n");
  220. gWriteDebug("StartAllDevicesn");
  221. }
  222. for (device_map_t::iterator iter = mDevices.begin(),
  223.  end = mDevices.end();
  224.  iter != end; iter++)
  225. {
  226. LLDXDevice *devicep = iter->second;
  227. devicep->dump();
  228. }
  229. if (gWriteDebug)
  230. {
  231. gWriteDebug("EndAllDevicesnn");
  232. }
  233. return "";
  234. }
  235. LLDXDevice *LLDXHardware::findDevice(const std::string &vendor, const std::string &devices)
  236. {
  237. // Iterate through different devices tokenized in devices string
  238. std::string str(devices);
  239. typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
  240. boost::char_separator<char> sep("|", "", boost::keep_empty_tokens);
  241. tokenizer tokens(str, sep);
  242. tokenizer::iterator iter = tokens.begin();
  243. for (;iter != tokens.end();++iter)
  244. {
  245. std::string dev_str = *iter;
  246. for (device_map_t::iterator iter = mDevices.begin(),
  247.  end = mDevices.end();
  248.  iter != end; iter++)
  249. {
  250. LLDXDevice *devicep = iter->second;
  251. if ((devicep->mVendorID == vendor)
  252. && (devicep->mDeviceID == dev_str))
  253. {
  254. return devicep;
  255. }
  256. }
  257. }
  258. return NULL;
  259. }
  260. */
  261. BOOL LLDXHardware::getInfo(BOOL vram_only)
  262. {
  263. LLTimer hw_timer;
  264. BOOL ok = FALSE;
  265.     HRESULT       hr;
  266.     CoInitialize(NULL);
  267.     IDxDiagProvider *dx_diag_providerp = NULL;
  268.     IDxDiagContainer *dx_diag_rootp = NULL;
  269. IDxDiagContainer *devices_containerp = NULL;
  270. // IDxDiagContainer *system_device_containerp= NULL;
  271. IDxDiagContainer *device_containerp = NULL;
  272. IDxDiagContainer *file_containerp = NULL;
  273. IDxDiagContainer *driver_containerp = NULL;
  274.     // CoCreate a IDxDiagProvider*
  275. LL_DEBUGS("AppInit") << "CoCreateInstance IID_IDxDiagProvider" << LL_ENDL;
  276.     hr = CoCreateInstance(CLSID_DxDiagProvider,
  277.                           NULL,
  278.                           CLSCTX_INPROC_SERVER,
  279.                           IID_IDxDiagProvider,
  280.                           (LPVOID*) &dx_diag_providerp);
  281. if (FAILED(hr))
  282. {
  283. LL_WARNS("AppInit") << "No DXDiag provider found!  DirectX 9 not installed!" << LL_ENDL;
  284. gWriteDebug("No DXDiag provider found!  DirectX 9 not installed!n");
  285. goto LCleanup;
  286. }
  287.     if (SUCCEEDED(hr)) // if FAILED(hr) then dx9 is not installed
  288.     {
  289.         // Fill out a DXDIAG_INIT_PARAMS struct and pass it to IDxDiagContainer::Initialize
  290.         // Passing in TRUE for bAllowWHQLChecks, allows dxdiag to check if drivers are 
  291.         // digital signed as logo'd by WHQL which may connect via internet to update 
  292.         // WHQL certificates.    
  293.         DXDIAG_INIT_PARAMS dx_diag_init_params;
  294.         ZeroMemory(&dx_diag_init_params, sizeof(DXDIAG_INIT_PARAMS));
  295.         dx_diag_init_params.dwSize                  = sizeof(DXDIAG_INIT_PARAMS);
  296.         dx_diag_init_params.dwDxDiagHeaderVersion   = DXDIAG_DX9_SDK_VERSION;
  297.         dx_diag_init_params.bAllowWHQLChecks        = TRUE;
  298.         dx_diag_init_params.pReserved               = NULL;
  299. LL_DEBUGS("AppInit") << "dx_diag_providerp->Initialize" << LL_ENDL;
  300.         hr = dx_diag_providerp->Initialize(&dx_diag_init_params);
  301.         if(FAILED(hr))
  302. {
  303.             goto LCleanup;
  304. }
  305. LL_DEBUGS("AppInit") << "dx_diag_providerp->GetRootContainer" << LL_ENDL;
  306.         hr = dx_diag_providerp->GetRootContainer( &dx_diag_rootp );
  307.         if(FAILED(hr) || !dx_diag_rootp)
  308. {
  309.             goto LCleanup;
  310. }
  311. HRESULT hr;
  312. // Get display driver information
  313. LL_DEBUGS("AppInit") << "dx_diag_rootp->GetChildContainer" << LL_ENDL;
  314. hr = dx_diag_rootp->GetChildContainer(L"DxDiag_DisplayDevices", &devices_containerp);
  315. if(FAILED(hr) || !devices_containerp)
  316. {
  317.             goto LCleanup;
  318. }
  319. // Get device 0
  320. LL_DEBUGS("AppInit") << "devices_containerp->GetChildContainer" << LL_ENDL;
  321. hr = devices_containerp->GetChildContainer(L"0", &device_containerp);
  322. if(FAILED(hr) || !device_containerp)
  323. {
  324.             goto LCleanup;
  325. }
  326. // Get the English VRAM string
  327. {
  328.   std::string ram_str = get_string(device_containerp, L"szDisplayMemoryEnglish");
  329.   // We don't need the device any more
  330.   SAFE_RELEASE(device_containerp);
  331.   // Dump the string as an int into the structure
  332.   char *stopstring;
  333.   mVRAM = strtol(ram_str.c_str(), &stopstring, 10); 
  334.   LL_INFOS("AppInit") << "VRAM Detected: " << mVRAM << " DX9 string: " << ram_str << LL_ENDL;
  335. }
  336. if (vram_only)
  337. {
  338. ok = TRUE;
  339. goto LCleanup;
  340. }
  341. /* for now, we ONLY do vram_only the rest of this
  342.    is commented out, to ensure no-one is tempted
  343.    to use it
  344. // Now let's get device and driver information
  345. // Get the IDxDiagContainer object called "DxDiag_SystemDevices".
  346. // This call may take some time while dxdiag gathers the info.
  347. DWORD num_devices = 0;
  348.     WCHAR wszContainer[256];
  349. LL_DEBUGS("AppInit") << "dx_diag_rootp->GetChildContainer DxDiag_SystemDevices" << LL_ENDL;
  350. hr = dx_diag_rootp->GetChildContainer(L"DxDiag_SystemDevices", &system_device_containerp);
  351. if (FAILED(hr))
  352. {
  353. goto LCleanup;
  354. }
  355. hr = system_device_containerp->GetNumberOfChildContainers(&num_devices);
  356. if (FAILED(hr))
  357. {
  358. goto LCleanup;
  359. }
  360. LL_DEBUGS("AppInit") << "DX9 iterating over devices" << LL_ENDL;
  361. S32 device_num = 0;
  362. for (device_num = 0; device_num < (S32)num_devices; device_num++)
  363. {
  364. hr = system_device_containerp->EnumChildContainerNames(device_num, wszContainer, 256);
  365. if (FAILED(hr))
  366. {
  367. goto LCleanup;
  368. }
  369. hr = system_device_containerp->GetChildContainer(wszContainer, &device_containerp);
  370. if (FAILED(hr) || device_containerp == NULL)
  371. {
  372. goto LCleanup;
  373. }
  374. std::string device_name = get_string(device_containerp, L"szDescription");
  375. std::string device_id = get_string(device_containerp, L"szDeviceID");
  376. LLDXDevice *dxdevicep = new LLDXDevice;
  377. dxdevicep->mName = device_name;
  378. dxdevicep->mPCIString = device_id;
  379. mDevices[dxdevicep->mPCIString] = dxdevicep;
  380. // Split the PCI string based on vendor, device, subsys, rev.
  381. std::string str(device_id);
  382. typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
  383. boost::char_separator<char> sep("&\", "", boost::keep_empty_tokens);
  384. tokenizer tokens(str, sep);
  385. tokenizer::iterator iter = tokens.begin();
  386. S32 count = 0;
  387. BOOL valid = TRUE;
  388. for (;(iter != tokens.end()) && (count < 3);++iter)
  389. {
  390. switch (count)
  391. {
  392. case 0:
  393. if (strcmp(iter->c_str(), "PCI"))
  394. {
  395. valid = FALSE;
  396. }
  397. break;
  398. case 1:
  399. dxdevicep->mVendorID = iter->c_str();
  400. break;
  401. case 2:
  402. dxdevicep->mDeviceID = iter->c_str();
  403. break;
  404. default:
  405. // Ignore it
  406. break;
  407. }
  408. count++;
  409. }
  410. // Now, iterate through the related drivers
  411. hr = device_containerp->GetChildContainer(L"Drivers", &driver_containerp);
  412. if (FAILED(hr) || !driver_containerp)
  413. {
  414. goto LCleanup;
  415. }
  416. DWORD num_files = 0;
  417. hr = driver_containerp->GetNumberOfChildContainers(&num_files);
  418. if (FAILED(hr))
  419. {
  420. goto LCleanup;
  421. }
  422. S32 file_num = 0;
  423. for (file_num = 0; file_num < (S32)num_files; file_num++ )
  424. {
  425. hr = driver_containerp->EnumChildContainerNames(file_num, wszContainer, 256);
  426. if (FAILED(hr))
  427. {
  428. goto LCleanup;
  429. }
  430. hr = driver_containerp->GetChildContainer(wszContainer, &file_containerp);
  431. if (FAILED(hr) || file_containerp == NULL)
  432. {
  433. goto LCleanup;
  434. }
  435. std::string driver_path = get_string(file_containerp, L"szPath");
  436. std::string driver_name = get_string(file_containerp, L"szName");
  437. std::string driver_version = get_string(file_containerp, L"szVersion");
  438. std::string driver_date = get_string(file_containerp, L"szDatestampEnglish");
  439. LLDXDriverFile *dxdriverfilep = new LLDXDriverFile;
  440. dxdriverfilep->mName = driver_name;
  441. dxdriverfilep->mFilepath= driver_path;
  442. dxdriverfilep->mVersionString = driver_version;
  443. dxdriverfilep->mVersion.set(driver_version);
  444. dxdriverfilep->mDateString = driver_date;
  445. dxdevicep->mDriverFiles[driver_name] = dxdriverfilep;
  446. SAFE_RELEASE(file_containerp);
  447. }
  448. SAFE_RELEASE(device_containerp);
  449. }
  450. */
  451.     }
  452.     // dumpDevices();
  453.     ok = TRUE;
  454. LCleanup:
  455. if (!ok)
  456. {
  457. LL_WARNS("AppInit") << "DX9 probe failed" << LL_ENDL;
  458. gWriteDebug("DX9 probe failedn");
  459. }
  460. SAFE_RELEASE(file_containerp);
  461. SAFE_RELEASE(driver_containerp);
  462. SAFE_RELEASE(device_containerp);
  463. SAFE_RELEASE(devices_containerp);
  464.     SAFE_RELEASE(dx_diag_rootp);
  465.     SAFE_RELEASE(dx_diag_providerp);
  466.     
  467.     CoUninitialize();
  468.     
  469.     return ok;
  470.     }
  471. LLSD LLDXHardware::getDisplayInfo()
  472. {
  473. LLTimer hw_timer;
  474.     HRESULT       hr;
  475. LLSD ret;
  476.     CoInitialize(NULL);
  477.     IDxDiagProvider *dx_diag_providerp = NULL;
  478.     IDxDiagContainer *dx_diag_rootp = NULL;
  479. IDxDiagContainer *devices_containerp = NULL;
  480. IDxDiagContainer *device_containerp = NULL;
  481. IDxDiagContainer *file_containerp = NULL;
  482. IDxDiagContainer *driver_containerp = NULL;
  483.     // CoCreate a IDxDiagProvider*
  484. llinfos << "CoCreateInstance IID_IDxDiagProvider" << llendl;
  485.     hr = CoCreateInstance(CLSID_DxDiagProvider,
  486.                           NULL,
  487.                           CLSCTX_INPROC_SERVER,
  488.                           IID_IDxDiagProvider,
  489.                           (LPVOID*) &dx_diag_providerp);
  490. if (FAILED(hr))
  491. {
  492. llwarns << "No DXDiag provider found!  DirectX 9 not installed!" << llendl;
  493. gWriteDebug("No DXDiag provider found!  DirectX 9 not installed!n");
  494. goto LCleanup;
  495. }
  496.     if (SUCCEEDED(hr)) // if FAILED(hr) then dx9 is not installed
  497.     {
  498.         // Fill out a DXDIAG_INIT_PARAMS struct and pass it to IDxDiagContainer::Initialize
  499.         // Passing in TRUE for bAllowWHQLChecks, allows dxdiag to check if drivers are 
  500.         // digital signed as logo'd by WHQL which may connect via internet to update 
  501.         // WHQL certificates.    
  502.         DXDIAG_INIT_PARAMS dx_diag_init_params;
  503.         ZeroMemory(&dx_diag_init_params, sizeof(DXDIAG_INIT_PARAMS));
  504.         dx_diag_init_params.dwSize                  = sizeof(DXDIAG_INIT_PARAMS);
  505.         dx_diag_init_params.dwDxDiagHeaderVersion   = DXDIAG_DX9_SDK_VERSION;
  506.         dx_diag_init_params.bAllowWHQLChecks        = TRUE;
  507.         dx_diag_init_params.pReserved               = NULL;
  508. llinfos << "dx_diag_providerp->Initialize" << llendl;
  509.         hr = dx_diag_providerp->Initialize(&dx_diag_init_params);
  510.         if(FAILED(hr))
  511. {
  512.             goto LCleanup;
  513. }
  514. llinfos << "dx_diag_providerp->GetRootContainer" << llendl;
  515.         hr = dx_diag_providerp->GetRootContainer( &dx_diag_rootp );
  516.         if(FAILED(hr) || !dx_diag_rootp)
  517. {
  518.             goto LCleanup;
  519. }
  520. HRESULT hr;
  521. // Get display driver information
  522. llinfos << "dx_diag_rootp->GetChildContainer" << llendl;
  523. hr = dx_diag_rootp->GetChildContainer(L"DxDiag_DisplayDevices", &devices_containerp);
  524. if(FAILED(hr) || !devices_containerp)
  525. {
  526.             goto LCleanup;
  527. }
  528. // Get device 0
  529. llinfos << "devices_containerp->GetChildContainer" << llendl;
  530. hr = devices_containerp->GetChildContainer(L"0", &device_containerp);
  531. if(FAILED(hr) || !device_containerp)
  532. {
  533.             goto LCleanup;
  534. }
  535. // Get the English VRAM string
  536. std::string ram_str = get_string(device_containerp, L"szDisplayMemoryEnglish");
  537. // Dump the string as an int into the structure
  538. char *stopstring;
  539. ret["VRAM"] = strtol(ram_str.c_str(), &stopstring, 10);
  540. std::string device_name = get_string(device_containerp, L"szDescription");
  541. ret["DeviceName"] = device_name;
  542. std::string device_driver=  get_string(device_containerp, L"szDriverVersion");
  543. ret["DriverVersion"] = device_driver;
  544.         
  545.         // ATI has a slightly different version string
  546.         if(device_name.length() >= 4 && device_name.substr(0,4) == "ATI ")
  547.         {
  548.             // get the key
  549.             HKEY hKey;
  550.             const DWORD RV_SIZE = 100;
  551.             WCHAR release_version[RV_SIZE];
  552.             // Hard coded registry entry.  Using this since it's simpler for now.
  553.             // And using EnumDisplayDevices to get a registry key also requires
  554.             // a hard coded Query value.
  555.             if(ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\ATI Technologies\CBT"), &hKey))
  556.             {
  557.                 // get the value
  558.                 DWORD dwType = REG_SZ;
  559.                 DWORD dwSize = sizeof(WCHAR) * RV_SIZE;
  560.                 if(ERROR_SUCCESS == RegQueryValueEx(hKey, TEXT("ReleaseVersion"), 
  561.                     NULL, &dwType, (LPBYTE)release_version, &dwSize))
  562.                 {
  563.                     // print the value
  564.                     // windows doesn't guarantee to be null terminated
  565.                     release_version[RV_SIZE - 1] = NULL;
  566.                     ret["DriverVersion"] = utf16str_to_utf8str(release_version);
  567.                 }
  568.                 RegCloseKey(hKey);
  569.             }
  570.         }    
  571.     }
  572. LCleanup:
  573. SAFE_RELEASE(file_containerp);
  574. SAFE_RELEASE(driver_containerp);
  575. SAFE_RELEASE(device_containerp);
  576. SAFE_RELEASE(devices_containerp);
  577.     SAFE_RELEASE(dx_diag_rootp);
  578.     SAFE_RELEASE(dx_diag_providerp);
  579.     
  580.     CoUninitialize();
  581. return ret;
  582. }
  583. void LLDXHardware::setWriteDebugFunc(void (*func)(const char*))
  584. {
  585. gWriteDebug = func;
  586. }
  587. #endif