Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

853 lines
23 KiB

  1. /*++
  2. Copyright (C) 1995-2001 Microsoft Corporation
  3. Module Name:
  4. PERFCACH.CPP
  5. Abstract:
  6. Containes some classes which are used to cache NT performance data.
  7. History:
  8. a-davj 15-DEC-95 Created.
  9. --*/
  10. #include "precomp.h"
  11. #include <wbemidl.h>
  12. #include "perfcach.h"
  13. #include <winperf.h>
  14. //***************************************************************************
  15. //
  16. // BOOL CIndicyList::SetUse
  17. //
  18. // DESCRIPTION:
  19. //
  20. // Indicates that an object type has just been used. If the object
  21. // is already on the list, then its last accessed time is updated. New
  22. // object types are added to the list
  23. //
  24. // PARAMETERS:
  25. //
  26. // iObj Number. The acutally translates to the object number
  27. // that perf monitor uses to identify objects.
  28. //
  29. // RETURN VALUE:
  30. //
  31. // always TRUE unless it was a new entry and there isnt enough memory
  32. // to add.
  33. //***************************************************************************
  34. BOOL CIndicyList::SetUse(
  35. IN int iObj)
  36. {
  37. int iNumEntries, iCnt;
  38. // Go Through list and determine if there is an entry
  39. Entry * pCurr;
  40. iNumEntries = Entries.Size();
  41. for(iCnt = 0; iCnt < iNumEntries; iCnt++)
  42. {
  43. pCurr = (Entry *)Entries.GetAt(iCnt);
  44. if(iObj == pCurr->iObject) // found it!
  45. break;
  46. }
  47. if(iCnt < iNumEntries)
  48. {
  49. // Found the entry. Set its last used to to the
  50. // present unless it is a permanent entry
  51. if(pCurr->dwLastUsed != PERMANENT)
  52. pCurr->dwLastUsed = GetCurrentTime();
  53. return TRUE;
  54. }
  55. else
  56. // Entry not found, add to list
  57. return bAdd(iObj,GetCurrentTime());
  58. }
  59. //***************************************************************************
  60. //
  61. // BOOL CIndicyList::bItemInList
  62. //
  63. // DESCRIPTION:
  64. //
  65. // Checks if an item is in the list.
  66. //
  67. // PARAMETERS:
  68. //
  69. // iObj Number. The acutally translates to the object number
  70. // that perf monitor uses to identify objects.
  71. //
  72. // RETURN VALUE:
  73. //
  74. // TRUE if the item is in the list
  75. //
  76. //***************************************************************************
  77. BOOL CIndicyList::bItemInList(
  78. IN int iObj)
  79. {
  80. int iNumEntries, iCnt;
  81. // Go Through list and determine if the entry is there
  82. Entry * pCurr;
  83. iNumEntries = Entries.Size();
  84. for(iCnt = 0; iCnt < iNumEntries; iCnt++)
  85. {
  86. pCurr = (Entry *)Entries.GetAt(iCnt);
  87. if(iObj == pCurr->iObject) // found it!
  88. return TRUE;
  89. }
  90. return FALSE;
  91. }
  92. //***************************************************************************
  93. //
  94. // BOOL CIndicyList::bAdd
  95. //
  96. // DESCRIPTION:
  97. //
  98. // Adds an object type to the list
  99. //
  100. // PARAMETERS:
  101. //
  102. // iObj Number. The acutally translates to the object number
  103. // that perf monitor uses to identify objects.
  104. // dwTime Current system time
  105. //
  106. // RETURN VALUE:
  107. //
  108. // Returns TRUE if OK.
  109. //
  110. //***************************************************************************
  111. BOOL CIndicyList::bAdd(
  112. IN int iObj,
  113. IN DWORD dwTime)
  114. {
  115. Entry * pNew = new Entry;
  116. if(pNew == NULL)
  117. return FALSE;
  118. pNew->iObject = iObj;
  119. pNew->dwLastUsed = dwTime;
  120. int iRet = Entries.Add(pNew);
  121. if(iRet != CFlexArray::no_error)
  122. {
  123. delete pNew;
  124. return FALSE;
  125. }
  126. return TRUE;
  127. }
  128. //***************************************************************************
  129. //
  130. // void CIndicyList::PruneOld
  131. //
  132. // DESCRIPTION:
  133. //
  134. // Looks at the entries in the list and removes any that have
  135. // not been used in a long time.
  136. //
  137. //***************************************************************************
  138. void CIndicyList::PruneOld(void)
  139. {
  140. Entry * pCurr;
  141. int iNumEntries, iCnt;
  142. DWORD dwCurr = GetCurrentTime();
  143. iNumEntries = Entries.Size();
  144. for(iCnt = iNumEntries-1; iCnt >= 0; iCnt--)
  145. {
  146. pCurr = (Entry *)Entries.GetAt(iCnt);
  147. if(pCurr->dwLastUsed != PERMANENT)
  148. if((dwCurr - pCurr->dwLastUsed) > MAX_UNUSED_KEEP)
  149. {
  150. Entries.RemoveAt(iCnt);
  151. delete pCurr;
  152. }
  153. }
  154. // Entries.FreeExtra();
  155. }
  156. //***************************************************************************
  157. //
  158. // LPCTSTR CIndicyList::pGetAll
  159. //
  160. // DESCRIPTION:
  161. //
  162. // Returns a pointer to a string containing the numbers of all the objects
  163. // on the list. For example, if the list had objects 2,4, and 8; then
  164. // the string "2 4 8" would be retrieved. Null is returned if there
  165. // isnt enough memory.
  166. //
  167. // RETURN VALUE:
  168. //
  169. // see description
  170. //
  171. //***************************************************************************
  172. LPCTSTR CIndicyList::pGetAll(void)
  173. {
  174. int iNumEntries, iCnt;
  175. Entry * pCurr;
  176. // Go Through list and add each object number to the string
  177. sAll.Empty();
  178. iNumEntries = Entries.Size();
  179. for(iCnt = 0; iCnt < iNumEntries; iCnt++)
  180. {
  181. TCHAR pTemp[20];
  182. pCurr = (Entry *)Entries.GetAt(iCnt);
  183. sAll += _itow(pCurr->iObject,pTemp,10);
  184. if(iCnt < iNumEntries-1)
  185. sAll += TEXT(" ");
  186. }
  187. return sAll;
  188. }
  189. //***************************************************************************
  190. //
  191. // CIndicyList & CIndicyList::operator =
  192. //
  193. // DESCRIPTION:
  194. //
  195. // Supports the assignment of one CIndicyList object to another
  196. //
  197. // PARAMETERS:
  198. //
  199. // from Value to copy
  200. //
  201. // RETURN VALUE:
  202. //
  203. // reterence the "this" object
  204. //***************************************************************************
  205. CIndicyList & CIndicyList::operator = (
  206. CIndicyList & from)
  207. {
  208. int iNumEntries, iCnt;
  209. Entry * pCurr;
  210. // Free existing list
  211. FreeAll();
  212. iNumEntries = from.Entries.Size();
  213. for(iCnt = 0; iCnt < iNumEntries; iCnt++)
  214. {
  215. pCurr = (Entry *)from.Entries.GetAt(iCnt);
  216. bAdd(pCurr->iObject, pCurr->dwLastUsed);
  217. }
  218. return *this;
  219. }
  220. //***************************************************************************
  221. //
  222. // void CIndicyList::FreeAll
  223. //
  224. // DESCRIPTION:
  225. //
  226. // Purpose: Clears out the list and frees memory.
  227. //
  228. //***************************************************************************
  229. void CIndicyList::FreeAll(void)
  230. {
  231. int iNumEntries, iCnt;
  232. // Go Through list and determine if there is an entry
  233. Entry * pCurr;
  234. // delete each object in the list.
  235. iNumEntries = Entries.Size();
  236. for(iCnt = 0; iCnt < iNumEntries; iCnt++)
  237. {
  238. pCurr = (Entry *)Entries.GetAt(iCnt);
  239. delete pCurr;
  240. }
  241. Entries.Empty();
  242. }
  243. //***************************************************************************
  244. //
  245. // DWORD PerfBuff::Read
  246. //
  247. // DESCRIPTION:
  248. //
  249. // Read the perf monitor data.
  250. //
  251. // PARAMETERS:
  252. //
  253. // hKey Registry key for perf mon data
  254. // iObj Number. The acutally translates to the object number
  255. // that perf monitor uses to identify objects.
  256. // bInitial Set to TRUE for first call
  257. //
  258. // RETURN VALUE:
  259. //
  260. // 0 All is well
  261. // WBEM_E_OUT_OF_MEMORY
  262. //
  263. //***************************************************************************
  264. DWORD PerfBuff::Read(
  265. IN HKEY hKey,
  266. IN int iObj,
  267. IN BOOL bInitial)
  268. {
  269. DWORD dwRet;
  270. LPCTSTR pRequest;
  271. // Make sure there is a data buffer
  272. if(dwSize == 0)
  273. {
  274. pData = new char[INITIAL_ALLOCATION];
  275. if(pData == NULL)
  276. return WBEM_E_OUT_OF_MEMORY;
  277. dwSize = INITIAL_ALLOCATION;
  278. }
  279. hKeyLastRead = hKey; // record the key that was used
  280. // Make sure that the desired object is in the list of
  281. // objects to be retrieved. Also set pRequest to the string that will
  282. // be passed to retrieve the perf counter block. An initial read is done
  283. // in order to establish the list of permanent object types which are
  284. // always to be retrived and that includes the standard "global" types
  285. // such as memory, processor, disk, etc.
  286. if(!bInitial)
  287. {
  288. if(!List.SetUse(iObj))
  289. return WBEM_E_OUT_OF_MEMORY;
  290. List.PruneOld();
  291. pRequest = List.pGetAll();
  292. if(pRequest == NULL)
  293. return WBEM_E_OUT_OF_MEMORY;
  294. }
  295. else
  296. pRequest = TEXT("Global");
  297. // Read the data. Note that the read may be retried if the data
  298. // block needs to be expanded
  299. do
  300. {
  301. DWORD dwTempSize, dwType;
  302. dwTempSize = dwSize;
  303. try
  304. {
  305. dwRet = RegQueryValueEx (hKey,pRequest,NULL,&dwType,
  306. (BYTE *)pData,&dwTempSize);
  307. }
  308. catch(...)
  309. {
  310. delete pData;
  311. return WBEM_E_FAILED;
  312. }
  313. if(dwRet == ERROR_MORE_DATA)
  314. {
  315. delete pData;
  316. dwSize += 5000;
  317. pData = new char[dwSize];
  318. if(pData == NULL)
  319. {
  320. dwSize = 0;
  321. return WBEM_E_OUT_OF_MEMORY;
  322. }
  323. }
  324. } while (dwRet == ERROR_MORE_DATA);
  325. // Set the age of the data
  326. if(dwRet == ERROR_SUCCESS)
  327. {
  328. PERF_DATA_BLOCK * pBlock = (PERF_DATA_BLOCK *)pData;
  329. PerfTime = *(LONGLONG UNALIGNED *)(&pBlock->PerfTime);
  330. PerfTime100nSec = *(LONGLONG UNALIGNED *)(&pBlock->PerfTime100nSec);
  331. PerfFreq = *(LONGLONG UNALIGNED *)(&pBlock->PerfFreq);
  332. dwBuffLastRead = GetCurrentTime();
  333. }
  334. else
  335. dwBuffLastRead = 0;
  336. // If this was an initial read of the default objects, add all the
  337. // default objects to the list as permanent entries
  338. if(bInitial && dwRet == ERROR_SUCCESS)
  339. {
  340. int iIndex;
  341. PERF_DATA_BLOCK * pBlock = (PERF_DATA_BLOCK * )pData;
  342. PPERF_OBJECT_TYPE pObj;
  343. pObj = (PPERF_OBJECT_TYPE)((PBYTE)pBlock + pBlock->HeaderLength);
  344. for(iIndex = 0; iIndex < (int)pBlock->NumObjectTypes; iIndex++)
  345. {
  346. //todo, check for errors on add.
  347. if(!List.bAdd((int)pObj->ObjectNameTitleIndex,PERMANENT))
  348. return WBEM_E_OUT_OF_MEMORY;
  349. pObj = (PPERF_OBJECT_TYPE)((PBYTE)pObj + pObj->TotalByteLength);
  350. }
  351. }
  352. return dwRet;
  353. }
  354. //***************************************************************************
  355. //
  356. // LPSTR PerfBuff::Get
  357. //
  358. // DESCRIPTION:
  359. //
  360. // Returns a pointer to the data and also indicates that the particular type
  361. // was just used.
  362. //
  363. // PARAMETERS:
  364. //
  365. // iObj Number. The acutally translates to the object number
  366. // that perf monitor uses to identify objects.
  367. //
  368. // RETURN VALUE:
  369. //
  370. // see description.
  371. //***************************************************************************
  372. LPSTR PerfBuff::Get(
  373. int iObj)
  374. {
  375. List.SetUse(iObj);
  376. return pData;
  377. }
  378. //***************************************************************************
  379. //
  380. // void PerfBuff::Free
  381. //
  382. // DESCRIPTION:
  383. //
  384. // Frees up the memory
  385. //
  386. //***************************************************************************
  387. void PerfBuff::Free()
  388. {
  389. if(pData)
  390. delete pData;
  391. pData = NULL;
  392. dwSize = 0;
  393. hKeyLastRead = NULL;
  394. dwBuffLastRead = 0;
  395. List.FreeAll();
  396. }
  397. //***************************************************************************
  398. //
  399. // PerfBuff::PerfBuff
  400. //
  401. // DESCRIPTION:
  402. //
  403. // Constructor.
  404. //
  405. //***************************************************************************
  406. PerfBuff::PerfBuff()
  407. {
  408. dwSize = 0;
  409. pData = NULL;
  410. hKeyLastRead = NULL;
  411. dwBuffLastRead = 0;
  412. }
  413. //***************************************************************************
  414. //
  415. // BOOL PerfBuff::bOK
  416. //
  417. // DESCRIPTION:
  418. //
  419. // Returns TRUE, if and only if the same registry key was used to read
  420. // the data, the data isnt too old, and the particular object type is
  421. // in the data block.
  422. //
  423. // PARAMETERS:
  424. //
  425. // hKey Registry key for reading data
  426. // dwMaxAge Maximum acceptable age
  427. // iObj Number. The acutally translates to the object number
  428. // that perf monitor uses to identify objects.
  429. //
  430. // RETURN VALUE:
  431. //
  432. // see desription
  433. //***************************************************************************
  434. BOOL PerfBuff::bOK(
  435. IN HKEY hKey,
  436. IN DWORD dwMaxAge,
  437. IN int iObj)
  438. {
  439. if(dwSize ==0)
  440. return FALSE;
  441. if(hKey != hKeyLastRead)
  442. return FALSE;
  443. if((GetCurrentTime() - dwBuffLastRead) > dwMaxAge)
  444. return FALSE;
  445. return List.bItemInList(iObj);
  446. }
  447. //***************************************************************************
  448. //
  449. // PerfBuff & PerfBuff::operator =
  450. //
  451. // DESCRIPTION:
  452. //
  453. // Allows assignment.
  454. //
  455. // PARAMETERS:
  456. //
  457. // from Assignment source
  458. //
  459. // RETURN VALUE:
  460. //
  461. // reference to "this" object.
  462. //***************************************************************************
  463. PerfBuff & PerfBuff::operator = (
  464. IN PerfBuff & from)
  465. {
  466. // if the objects have different buffer sizes, free up the destinations
  467. // buffer and reallocate on of the same size as the source.
  468. if(from.dwSize != dwSize)
  469. {
  470. Free();
  471. pData = new char[from.dwSize];
  472. if(pData == NULL)
  473. {
  474. // failure in assignment isnt too serious since the buffer
  475. // will just return null when asked for the data.
  476. dwSize = 0;
  477. dwBuffLastRead = 0;
  478. return *this;
  479. }
  480. dwSize = from.dwSize;
  481. }
  482. // Copy the list of objects and times etc.
  483. memcpy(pData,from.pData,dwSize);
  484. List = from.List;
  485. hKeyLastRead = from.hKeyLastRead;
  486. dwBuffLastRead = from.dwBuffLastRead;
  487. PerfTime = from.PerfTime;
  488. PerfTime100nSec = from.PerfTime100nSec;
  489. PerfFreq = from.PerfFreq;
  490. return *this;
  491. }
  492. //***************************************************************************
  493. //
  494. // void PerfCache::FreeOldBuffers
  495. //
  496. // DESCRIPTION:
  497. //
  498. // Called by the house keeping thread to free up any buffers tool old to
  499. // be of any use.
  500. //
  501. //***************************************************************************
  502. void PerfCache::FreeOldBuffers(void)
  503. {
  504. if(Old.dwSize != 0 &&
  505. (GetCurrentTime() - Old.dwBuffLastRead) > MAX_OLD_AGE)
  506. Old.Free();
  507. if(New.dwSize != 0 &&
  508. (GetCurrentTime() - New.dwBuffLastRead) > MAX_OLD_AGE)
  509. New.Free();
  510. }
  511. //***************************************************************************
  512. //
  513. // DWORD PerfCache::dwGetNew
  514. //
  515. // DESCRIPTION:
  516. //
  517. // Sets a pointer to the most recently read data and will actually do a read
  518. // if the data in the new buffer isnt fresh enough. The PLINESTRUCT data is
  519. // also set.
  520. //
  521. // PARAMETERS:
  522. //
  523. // pName Machine name
  524. // iObj Number. The acutally translates to the object number
  525. // that perf monitor uses to identify objects.
  526. // pData Set to the object name
  527. // pls Set to info used to do calculations.
  528. //
  529. // RETURN VALUE:
  530. //
  531. // 0 all is well
  532. // WBEM_E_OUT_OF_MEMORY
  533. // otherwise error from dwGetHandle, or Read.
  534. //***************************************************************************
  535. DWORD PerfCache::dwGetNew(
  536. IN LPCTSTR pName,
  537. IN int iObj,
  538. OUT IN LPSTR * pData,
  539. OUT IN PLINESTRUCT pls)
  540. {
  541. DWORD dwRet;
  542. // Get the handle
  543. dwRet = dwGetHandle(pName);
  544. if(hHandle == NULL || dwRet != 0)
  545. return dwRet;
  546. // If the new data is acceptable, then use it
  547. if(New.bOK(hHandle,MAX_NEW_AGE, iObj))
  548. {
  549. // OutputDebugString(TEXT("\r\nCurrent New is OK"));
  550. }
  551. else
  552. {
  553. // If the new data has the correct type, AND either the old data
  554. // is junk, or the new data has aged enough to be old, copy the
  555. // new into the old.
  556. if(New.bOK(hHandle,MAX_OLD_AGE, iObj) &&
  557. (!Old.bOK(hHandle,MAX_OLD_AGE, iObj) ||
  558. (GetCurrentTime() - New.dwBuffLastRead >= MIN_TIME_DIFF)))
  559. {
  560. // OutputDebugString("\r\nMoving New into Old in dwGetNew");
  561. Old = New;
  562. if(Old.dwSize == 0) // could happen in low memory
  563. return WBEM_E_OUT_OF_MEMORY;
  564. }
  565. // Read the latest data.
  566. dwRet = New.Read(hHandle, iObj, FALSE);
  567. // OutputDebugString(TEXT("\r\nRead in New"));
  568. if(dwRet != ERROR_SUCCESS)
  569. return dwRet;
  570. }
  571. *pData = New.Get(iObj);
  572. pls->lnNewTime = New.PerfTime;
  573. pls->lnNewTime100Ns = New.PerfTime100nSec;
  574. pls->lnPerfFreq = New.PerfFreq;
  575. return ERROR_SUCCESS;
  576. }
  577. //***************************************************************************
  578. //
  579. // DWORD PerfCache::dwGetPair
  580. //
  581. // DESCRIPTION:
  582. //
  583. // Sets a pointer to the most recently read data and to the old data so that
  584. // time averaging can be done. This routine will ensure that the time
  585. // difference between the old and new is sufficient. The dwGetNew
  586. // routine should always be called first. The PLINESTRUCT data is
  587. // also set.
  588. //
  589. // PARAMETERS:
  590. //
  591. // pName Object Name
  592. // iObj Number. The acutally translates to the object number
  593. // that perf monitor uses to identify objects.
  594. //
  595. // pOldData Older data sample
  596. // pNewData Newer data sample
  597. // pls line struct data with things like frequency, age etc.
  598. //
  599. // RETURN VALUE:
  600. //
  601. // 0 if OK, otherwise retuns an error code.
  602. //
  603. //***************************************************************************
  604. DWORD PerfCache::dwGetPair(
  605. IN LPCTSTR pName,
  606. IN int iObj,
  607. OUT IN LPSTR * pOldData,
  608. OUT IN LPSTR * pNewData,
  609. OUT IN PLINESTRUCT pls)
  610. {
  611. DWORD dwRet;
  612. BOOL bOldOK;
  613. // Check to see if the old buffer is OK.
  614. bOldOK = Old.bOK(hHandle,MAX_OLD_AGE, iObj);
  615. // If both buffers are ok, then we are done
  616. if(bOldOK)
  617. {
  618. *pOldData = Old.Get(iObj);
  619. pls->lnOldTime = Old.PerfTime;
  620. pls->lnOldTime100Ns = Old.PerfTime100nSec;
  621. // OutputDebugString(TEXT("\r\nOld is OK"));
  622. return ERROR_SUCCESS;
  623. }
  624. // Since the new buffer has already been read, use it as the old buffer
  625. Old = New;
  626. if(Old.dwSize == 0) // could happen in low memory
  627. return WBEM_E_OUT_OF_MEMORY;
  628. // OutputDebugString(TEXT("\r\nCopying New into Old in dwGetPair"));
  629. // Possibly delay long enough so that there is a decent interval
  630. DWORD dwAge = GetCurrentTime() - Old.dwBuffLastRead;
  631. if(dwAge < MIN_TIME_DIFF)
  632. {
  633. DWORD dwSleep = MIN_TIME_DIFF - dwAge;
  634. Sleep(dwSleep);
  635. }
  636. // Read in the new buffer
  637. dwRet = New.Read(hHandle, iObj, FALSE);
  638. // OutputDebugString(TEXT("\r\ndoing raw read of new after delay"));
  639. if(dwRet != ERROR_SUCCESS)
  640. return dwRet;
  641. *pNewData = New.Get(iObj);
  642. *pOldData = Old.Get(iObj);
  643. pls->lnOldTime = Old.PerfTime;
  644. pls->lnOldTime100Ns = Old.PerfTime100nSec;
  645. pls->lnNewTime = New.PerfTime;
  646. pls->lnNewTime100Ns = New.PerfTime100nSec;
  647. pls->lnPerfFreq = New.PerfFreq;
  648. return ERROR_SUCCESS;
  649. }
  650. //***************************************************************************
  651. //
  652. // PerfCache::PerfCache
  653. //
  654. // DESCRIPTION:
  655. //
  656. // Constructor.
  657. //
  658. //***************************************************************************
  659. PerfCache::PerfCache()
  660. {
  661. // Read in the standard counters. This builds a list containing
  662. // those standards as well as providing immediate data for any
  663. // request to come in the near future.
  664. hHandle = HKEY_PERFORMANCE_DATA;
  665. /// New.Read(hHandle, 0, TRUE);
  666. }
  667. //***************************************************************************
  668. //
  669. // PerfCache::~PerfCache
  670. //
  671. // DESCRIPTION:
  672. //
  673. // Destructor.
  674. //
  675. //***************************************************************************
  676. PerfCache::~PerfCache()
  677. {
  678. // If the handle is to a remote machine, close it.
  679. if(hHandle != NULL && hHandle != HKEY_PERFORMANCE_DATA)
  680. RegCloseKey(hHandle);
  681. }
  682. //***************************************************************************
  683. //
  684. // DWORD PerfCache::dwGetHandle
  685. //
  686. // DESCRIPTION:
  687. //
  688. // Makes sure that hHandle is set correctly.
  689. //
  690. // PARAMETERS:
  691. //
  692. // pMachine Machine name.
  693. //
  694. // RETURN VALUE:
  695. //
  696. // 0 all is well
  697. // WBEM_E_OUT_OF_MEMORY
  698. // WBEM_E_INVALID_PARAMETER bad argument
  699. // otherwise error from RegConnectRegistry
  700. //
  701. //***************************************************************************
  702. DWORD PerfCache::dwGetHandle(
  703. LPCTSTR pMachine)
  704. {
  705. DWORD dwRet;
  706. // if the machines are the same, the just use the existing handle
  707. if(pMachine == NULL)
  708. return WBEM_E_INVALID_PARAMETER; // bad mapping string
  709. if(!lstrcmpi(sMachine,pMachine) && hHandle != NULL)
  710. return 0; // already got it!
  711. // handle is needed for machine other that the local. Start
  712. // by freeing the existing handle if it too is non local
  713. if(hHandle != NULL && hHandle != HKEY_PERFORMANCE_DATA)
  714. RegCloseKey(hHandle);
  715. // save the machine name so that we dont reopen this
  716. sMachine = pMachine;
  717. if(lstrcmpi(pMachine,TEXT("local")))
  718. {
  719. LPTSTR pTemp = NULL;
  720. int iLen = sMachine.Length() +1;
  721. dwRet = RegConnectRegistry(sMachine,HKEY_PERFORMANCE_DATA,
  722. &hHandle);
  723. if(dwRet != ERROR_SUCCESS)
  724. { // could not remote connect
  725. hHandle = NULL;
  726. sMachine.Empty();
  727. }
  728. }
  729. else
  730. { // local machine, use standard handle.
  731. sMachine = TEXT("Local");
  732. hHandle = HKEY_PERFORMANCE_DATA;
  733. dwRet = 0;
  734. }
  735. return dwRet;
  736. }