Source code of Windows XP (NT5)
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.

796 lines
24 KiB

  1. /*++
  2. Copyright (C) 1997-2001 Microsoft Corporation
  3. Module Name:
  4. Abstract:
  5. History:
  6. --*/
  7. //***************************************************************************
  8. //
  9. // NTPERF.CPP
  10. //
  11. // Sample NT5 Perf Counter Provider
  12. //
  13. // raymcc 02-Dec-97 Created
  14. // raymcc 20-Feb-98 Updated to use new initializer
  15. //
  16. //***************************************************************************
  17. #include "precomp.h"
  18. #include <stdio.h>
  19. #include <wbemidl.h>
  20. #include <wbemint.h>
  21. #include "ntperf.h"
  22. //***************************************************************************
  23. //
  24. // CNt5Refresher constructor
  25. //
  26. //***************************************************************************
  27. // ok
  28. CNt5Refresher::CNt5Refresher()
  29. {
  30. m_lRef = 0; // COM Ref Count
  31. // Set the instance cache to all zeros.
  32. // As objects are added to the refresher
  33. // we simply put them in unused slots in the array.
  34. // ================================================
  35. for (int i = 0; i < NUM_SAMPLE_INSTANCES; i++)
  36. {
  37. m_aInstances[i] = 0;
  38. }
  39. // Set the values of the property handles to zero.
  40. // ===============================================
  41. m_hName = 0;
  42. m_hCounter1 = 0;
  43. m_hCounter2 = 0;
  44. m_hCounter3 = 0;
  45. }
  46. //***************************************************************************
  47. //
  48. // CNt5Refresher destructor
  49. //
  50. //***************************************************************************
  51. // ok
  52. CNt5Refresher::~CNt5Refresher()
  53. {
  54. // Release the cached IWbemObjectAccess instances.
  55. // ===============================================
  56. for (DWORD i = 0; i < NUM_SAMPLE_INSTANCES; i++)
  57. {
  58. if (m_aInstances[i])
  59. m_aInstances[i]->Release();
  60. }
  61. }
  62. //***************************************************************************
  63. //
  64. // CNt5Refresher::Refresh
  65. //
  66. // Executed to refresh a set of instances bound to the particular
  67. // refresher.
  68. //
  69. //***************************************************************************
  70. // ok
  71. HRESULT CNt5Refresher::Refresh(/* [in] */ long lFlags)
  72. {
  73. // Zip through all the objects and increment the values.
  74. // =====================================================
  75. for (DWORD i = 0; i < NUM_SAMPLE_INSTANCES; i++)
  76. {
  77. // Get the object at this location.
  78. // ================================
  79. IWbemObjectAccess *pAccess = m_aInstances[i];
  80. // If there is no object in this array slot (a NULL pointer)
  81. // there is nothing to refresh.
  82. // =========================================================
  83. if (pAccess == 0)
  84. continue;
  85. // Increment all the counter values to simulate an update.
  86. // The client already has a pointer to this object, so
  87. // all we have to do is update the values.
  88. // =======================================================
  89. DWORD dwVal;
  90. pAccess->ReadDWORD(m_hCounter1, &dwVal);
  91. dwVal++;
  92. pAccess->WriteDWORD(m_hCounter1, dwVal);
  93. pAccess->ReadDWORD(m_hCounter3, &dwVal);
  94. dwVal++;
  95. pAccess->WriteDWORD(m_hCounter3, dwVal);
  96. unsigned __int64 qwVal;
  97. pAccess->ReadQWORD(m_hCounter2, &qwVal);
  98. qwVal++;
  99. pAccess->WriteQWORD(m_hCounter2, qwVal);
  100. }
  101. return NO_ERROR;
  102. }
  103. //***************************************************************************
  104. //
  105. // CNt5Refresher::TransferPropHandles
  106. //
  107. // This is a private mechanism used by CNt5PerfProvider.
  108. // It is used to copy the property handles from the
  109. // hi-perf provider object to the refresher. We need these handles to
  110. // quickly access the properties in each instance. The same handles are
  111. // used for all instances.
  112. //
  113. //***************************************************************************
  114. // ok
  115. void CNt5Refresher::TransferPropHandles(CNt5PerfProvider *pSrc)
  116. {
  117. m_hName = pSrc->m_hName;
  118. m_hCounter1 = pSrc->m_hCounter1;
  119. m_hCounter2 = pSrc->m_hCounter2;
  120. m_hCounter3 = pSrc->m_hCounter3;
  121. }
  122. //***************************************************************************
  123. //
  124. // CNt5Refresher::AddRef
  125. //
  126. // Standard COM AddRef().
  127. //
  128. //***************************************************************************
  129. // ok
  130. ULONG CNt5Refresher::AddRef()
  131. {
  132. return InterlockedIncrement(&m_lRef);
  133. }
  134. //***************************************************************************
  135. //
  136. // CNt5Refresher::Release
  137. //
  138. // Standard COM Release().
  139. //
  140. //***************************************************************************
  141. // ok
  142. ULONG CNt5Refresher::Release()
  143. {
  144. long lRef = InterlockedDecrement(&m_lRef);
  145. if(lRef == 0)
  146. delete this;
  147. return lRef;
  148. }
  149. //***************************************************************************
  150. //
  151. // CNt5Refresher::QueryInterface
  152. //
  153. // Standard COM QueryInterface().
  154. //
  155. //***************************************************************************
  156. // ok
  157. HRESULT CNt5Refresher::QueryInterface(REFIID riid, void** ppv)
  158. {
  159. if (riid == IID_IUnknown || riid == IID_IWbemRefresher)
  160. {
  161. *ppv = (IWbemRefresher *) this;
  162. AddRef();
  163. return S_OK;
  164. }
  165. else return E_NOINTERFACE;
  166. }
  167. //***************************************************************************
  168. //
  169. // CNt5Refresher::AddObject
  170. //
  171. // Adds an object to the refresher. This is a private mechanism
  172. // used by CNt5PerfProvider and not part of the COM interface.
  173. //
  174. // The ID we return for future identification is simply
  175. // the array index.
  176. //
  177. //***************************************************************************
  178. // ok
  179. BOOL CNt5Refresher::AddObject(
  180. IWbemObjectAccess *pObj,
  181. LONG *plId
  182. )
  183. {
  184. for (DWORD i = 0; i < NUM_SAMPLE_INSTANCES; i++)
  185. {
  186. if (m_aInstances[i] == 0)
  187. {
  188. pObj->AddRef();
  189. m_aInstances[i] = pObj;
  190. // The ID we return for future identification is simply
  191. // the array index.
  192. // ====================================================
  193. *plId = i;
  194. return TRUE;
  195. }
  196. }
  197. return FALSE;
  198. }
  199. //***************************************************************************
  200. //
  201. // CNt5Refresher::RemoveObject
  202. //
  203. // This is a private mechanism used by CNt5PerfProvider and not
  204. // part of the COM interface.
  205. //
  206. // Removes an object from the refresher by ID. In our case, the ID
  207. // is actually the array index we used internally, so it is simple
  208. // to locate and remove the object.
  209. //
  210. //***************************************************************************
  211. BOOL CNt5Refresher::RemoveObject(LONG lId)
  212. {
  213. if (m_aInstances[lId] == 0)
  214. return FALSE;
  215. m_aInstances[lId]->Release();
  216. m_aInstances[lId] = 0;
  217. return TRUE;
  218. }
  219. //***************************************************************************
  220. //
  221. // CNt5PerfProvider constructor
  222. //
  223. //***************************************************************************
  224. // ok
  225. CNt5PerfProvider::CNt5PerfProvider()
  226. {
  227. m_lRef = 0;
  228. m_pSampleClass = 0;
  229. // All the instances we work with are cached internally.
  230. // =====================================================
  231. for (int i = 0; i < NUM_SAMPLE_INSTANCES; i++)
  232. m_aInstances[i] = 0;
  233. // Property value handles.
  234. // =======================
  235. m_hName = 0; // "Name" property in the MOF
  236. m_hCounter1 = 0; // "Counter1" in the MOF
  237. m_hCounter2 = 0; // "Counter2" in the MOF
  238. m_hCounter3 = 0; // "Counter3" in the MOF
  239. }
  240. //***************************************************************************
  241. //
  242. // CNt5PerfProvider destructor
  243. //
  244. //***************************************************************************
  245. // ok
  246. CNt5PerfProvider::~CNt5PerfProvider()
  247. {
  248. // Release all the objects which have been added to the array.
  249. // ===========================================================
  250. for (int i = 0; i < NUM_SAMPLE_INSTANCES; i++)
  251. if (m_aInstances[i])
  252. m_aInstances[i]->Release();
  253. if (m_pSampleClass)
  254. m_pSampleClass->Release();
  255. }
  256. //***************************************************************************
  257. //
  258. // CNt5Refresher::AddRef
  259. //
  260. // Standard COM AddRef().
  261. //
  262. //***************************************************************************
  263. // ok
  264. ULONG CNt5PerfProvider::AddRef()
  265. {
  266. return InterlockedIncrement(&m_lRef);
  267. }
  268. //***************************************************************************
  269. //
  270. // CNt5Refresher::Release
  271. //
  272. // Standard COM Release().
  273. //
  274. //***************************************************************************
  275. // ok
  276. ULONG CNt5PerfProvider::Release()
  277. {
  278. long lRef = InterlockedDecrement(&m_lRef);
  279. if(lRef == 0)
  280. delete this;
  281. return lRef;
  282. }
  283. //***************************************************************************
  284. //
  285. // CNt5Refresher::QueryInterface
  286. //
  287. // Standard COM QueryInterface(). We have to support two interfaces,
  288. // the IWbemHiPerfProvider interface itself to provide the objects and
  289. // the IWbemProviderInit interface to initialize the provider.
  290. //
  291. //***************************************************************************
  292. // ok
  293. HRESULT CNt5PerfProvider::QueryInterface(REFIID riid, void** ppv)
  294. {
  295. if(riid == IID_IUnknown || riid == IID_IWbemHiPerfProvider)
  296. {
  297. *ppv = (IWbemHiPerfProvider*) this;
  298. AddRef();
  299. return S_OK;
  300. }
  301. else if (riid == IID_IWbemProviderInit)
  302. {
  303. *ppv = (IWbemProviderInit *) this;
  304. AddRef();
  305. return S_OK;
  306. }
  307. else return E_NOINTERFACE;
  308. }
  309. //***************************************************************************
  310. //
  311. // CNt5Refresher::Initialize
  312. //
  313. // Called once during startup. Insdicates to the provider which
  314. // namespace it is being invoked for and which User. It also supplies
  315. // a back pointer to WINMGMT so that class definitions can be retrieved.
  316. //
  317. // We perform any one-time initialization in this routine. The
  318. // final call to Release() is for any cleanup.
  319. //
  320. // <wszUser> The current user.
  321. // <lFlags> Reserved.
  322. // <wszNamespace> The namespace for which we are being activated.
  323. // <wszLocale> The locale under which we are to be running.
  324. // <pNamespace> An active pointer back into the current namespace
  325. // from which we can retrieve schema objects.
  326. // <pCtx> The user's context object. We simply reuse this
  327. // during any reentrant operations into WINMGMT.
  328. // <pInitSink> The sink to which we indicate our readiness.
  329. //
  330. //***************************************************************************
  331. // ok
  332. HRESULT CNt5PerfProvider::Initialize(
  333. /* [unique][in] */ LPWSTR wszUser,
  334. /* [in] */ LONG lFlags,
  335. /* [in] */ LPWSTR wszNamespace,
  336. /* [unique][in] */ LPWSTR wszLocale,
  337. /* [in] */ IWbemServices __RPC_FAR *pNamespace,
  338. /* [in] */ IWbemContext __RPC_FAR *pCtx,
  339. /* [in] */ IWbemProviderInitSink __RPC_FAR *pInitSink
  340. )
  341. {
  342. BSTR PropName = 0;
  343. IWbemObjectAccess *pAccess = 0;
  344. // Get a copy of our sample class def so that we can create & maintain
  345. // instances of it.
  346. // ===================================================================
  347. HRESULT hRes = pNamespace->GetObject(BSTR(L"Win32_Nt5PerfTest"),
  348. 0, pCtx, &m_pSampleClass, 0
  349. );
  350. if (hRes)
  351. return hRes;
  352. // Precreate 10 instances, and set them up in an array which
  353. // is a member of this C++ class.
  354. //
  355. // We only store the IWbemObjectAccess pointers, since
  356. // we are updating 'well-known' properties and already
  357. // know their names.
  358. // ==========================================================
  359. for (int i = 0; i < NUM_SAMPLE_INSTANCES; i++)
  360. {
  361. IWbemClassObject *pInst = 0;
  362. m_pSampleClass->SpawnInstance(0, &pInst);
  363. // Write out the instance name.
  364. // ============================
  365. wchar_t buf[128];
  366. swprintf(buf, L"Inst_%d", i);
  367. VARIANT vName;
  368. VariantInit(&vName);
  369. V_BSTR(&vName) = SysAllocString(buf);
  370. V_VT(&vName) = VT_BSTR;
  371. BSTR PropName = SysAllocString(L"Name");
  372. pInst->Put(PropName, 0, &vName, 0);
  373. SysFreeString(PropName);
  374. VariantClear(&vName);
  375. pInst->QueryInterface(IID_IWbemObjectAccess, (LPVOID *) &pAccess);
  376. m_aInstances[i] = pAccess;
  377. pInst->Release();
  378. }
  379. // Get the property handles for the well-known properties in
  380. // this counter type. We cache the property handles
  381. // for each property so that we can transfer them to the
  382. // refresher later on.
  383. // =========================================================
  384. m_pSampleClass->QueryInterface(IID_IWbemObjectAccess,
  385. (LPVOID *) &pAccess);
  386. PropName = SysAllocString(L"Name");
  387. hRes = pAccess->GetPropertyHandle(PropName, 0, &m_hName);
  388. SysFreeString(PropName);
  389. PropName = SysAllocString(L"Counter1");
  390. hRes = pAccess->GetPropertyHandle(PropName, 0, &m_hCounter1);
  391. SysFreeString(PropName);
  392. PropName = SysAllocString(L"Counter2");
  393. hRes = pAccess->GetPropertyHandle(PropName, 0, &m_hCounter2);
  394. SysFreeString(PropName);
  395. PropName = SysAllocString(L"Counter3");
  396. hRes = pAccess->GetPropertyHandle(PropName, 0, &m_hCounter3);
  397. SysFreeString(PropName);
  398. pAccess->Release();
  399. // Now let's set all the instance to some default values.
  400. // ======================================================
  401. for (i = 0; i < NUM_SAMPLE_INSTANCES; i++)
  402. {
  403. IWbemObjectAccess *pAccess = m_aInstances[i];
  404. hRes = pAccess->WriteDWORD(m_hCounter1, DWORD(i));
  405. hRes = pAccess->WriteQWORD(m_hCounter2, (_int64) + 100 + i);
  406. hRes = pAccess->WriteDWORD(m_hCounter3, DWORD(i + 1000));
  407. }
  408. // We now have all the instances ready to go and all the
  409. // property handles cached. Tell WINMGMT that we're
  410. // ready to start 'providing'.
  411. // =====================================================
  412. pInitSink->SetStatus(0, WBEM_S_INITIALIZED);
  413. return NO_ERROR;
  414. }
  415. //***************************************************************************
  416. //
  417. // CNt5Refresher::QueryInstances
  418. //
  419. // Called whenever a complete, fresh list of instances for a given
  420. // class is required. The objects are constructed and sent back to the
  421. // caller through the sink. The sink can be used in-line as here, or
  422. // the call can return and a separate thread could be used to deliver
  423. // the instances to the sink.
  424. //
  425. // Parameters:
  426. // <pNamespace> A pointer to the relevant namespace. This
  427. // should not be AddRef'ed.
  428. // <wszClass> The class name for which instances are required.
  429. // <lFlags> Reserved.
  430. // <pCtx> The user-supplied context (not used here).
  431. // <pSink> The sink to which to deliver the objects. The objects
  432. // can be delivered synchronously through the duration
  433. // of this call or asynchronously (assuming we
  434. // had a separate thread). A IWbemObjectSink::SetStatus
  435. // call is required at the end of the sequence.
  436. //
  437. //***************************************************************************
  438. // ok
  439. HRESULT CNt5PerfProvider::QueryInstances(
  440. /* [in] */ IWbemServices __RPC_FAR *pNamespace,
  441. /* [string][in] */ WCHAR __RPC_FAR *wszClass,
  442. /* [in] */ long lFlags,
  443. /* [in] */ IWbemContext __RPC_FAR *pCtx,
  444. /* [in] */ IWbemObjectSink __RPC_FAR *pSink
  445. )
  446. {
  447. if (pNamespace == 0 || wszClass == 0 || pSink == 0)
  448. return WBEM_E_INVALID_PARAMETER;
  449. // Quickly zip through the instances and update the values before
  450. // returning them. This is just a dummy operation to make it
  451. // look like the instances are continually changing like real
  452. // perf counters.
  453. // ==============================================================
  454. for (int i = 0; i < NUM_SAMPLE_INSTANCES; i++)
  455. {
  456. IWbemObjectAccess *pAccess = m_aInstances[i];
  457. // Every object can be access one of two ways. In this case
  458. // we get the 'other' (primary) interface to this same object.
  459. // ===========================================================
  460. IWbemClassObject *pOtherFormat = 0;
  461. pAccess->QueryInterface(IID_IWbemClassObject, (LPVOID *) &pOtherFormat);
  462. // Send a copy back to the caller.
  463. // ===============================
  464. pSink->Indicate(1, &pOtherFormat);
  465. pOtherFormat->Release(); // Don't need this any more
  466. }
  467. // Tell WINMGMT we are all finished supplying objects.
  468. // =================================================
  469. pSink->SetStatus(0, WBEM_NO_ERROR, 0, 0);
  470. return NO_ERROR;
  471. }
  472. //***************************************************************************
  473. //
  474. // CNt5Refresher::CreateRefresher
  475. //
  476. // Called whenever a new refresher is needed by the client.
  477. //
  478. // Parameters:
  479. // <pNamespace> A pointer to the relevant namespace. Not used.
  480. // <lFlags> Not used.
  481. // <ppRefresher> Receives the requested refresher.
  482. //
  483. //***************************************************************************
  484. // ok
  485. HRESULT CNt5PerfProvider::CreateRefresher(
  486. /* [in] */ IWbemServices __RPC_FAR *pNamespace,
  487. /* [in] */ long lFlags,
  488. /* [out] */ IWbemRefresher __RPC_FAR *__RPC_FAR *ppRefresher
  489. )
  490. {
  491. if (pNamespace == 0 || ppRefresher == 0)
  492. return WBEM_E_INVALID_PARAMETER;
  493. // Construct a new empty refresher.
  494. // ================================
  495. CNt5Refresher *pNewRefresher = new CNt5Refresher();
  496. // Move copies of the property handles to the refresher
  497. // so that it can quickly update property values during
  498. // a refresh operation.
  499. // ====================================================
  500. pNewRefresher->TransferPropHandles(this);
  501. // Follow COM rules and AddRef() the thing before sending it back.
  502. // ===============================================================
  503. pNewRefresher->AddRef();
  504. *ppRefresher = pNewRefresher;
  505. return NO_ERROR;
  506. }
  507. //***************************************************************************
  508. //
  509. // CNt5Refresher::CreateRefreshableObject
  510. //
  511. // Called whenever a user wants to include an object in a refresher.
  512. //
  513. // Parameters:
  514. // <pNamespace> A pointer to the relevant namespace in WINMGMT.
  515. // <pTemplate> A pointer to a copy of the object which is to be
  516. // added. This object itself cannot be used, as
  517. // it not owned locally.
  518. // <pRefresher> The refresher to which to add the object.
  519. // <lFlags> Not used.
  520. // <pContext> Not used here.
  521. // <ppRefreshable> A pointer to the internal object which was added
  522. // to the refresher.
  523. // <plId> The Object Id (for identification during removal).
  524. //
  525. //***************************************************************************
  526. // ok
  527. HRESULT CNt5PerfProvider::CreateRefreshableObject(
  528. /* [in] */ IWbemServices __RPC_FAR *pNamespace,
  529. /* [in] */ IWbemObjectAccess __RPC_FAR *pTemplate,
  530. /* [in] */ IWbemRefresher __RPC_FAR *pRefresher,
  531. /* [in] */ long lFlags,
  532. /* [in] */ IWbemContext __RPC_FAR *pContext,
  533. /* [out] */ IWbemObjectAccess __RPC_FAR *__RPC_FAR *ppRefreshable,
  534. /* [out] */ long __RPC_FAR *plId
  535. )
  536. {
  537. // The object supplied by <pTemplate> must not be copied.
  538. // Instead, we want to find out which object the caller is after
  539. // and return a pointer to *our* own private instance which is
  540. // already set up internally. This value will be sent back to the
  541. // caller so that everyone is sharing the same exact instance
  542. // in memory.
  543. // ===============================================================
  544. // Find out which object is being requested for addition.
  545. // ======================================================
  546. wchar_t buf[128];
  547. *buf = 0;
  548. LONG lNameLength = 0;
  549. pTemplate->ReadPropertyValue(m_hName, 128, &lNameLength, LPBYTE(buf));
  550. // Scan out the index from the instance name. We only do this
  551. // because the instance name is a string.
  552. // ===========================================================
  553. DWORD dwIndex = 0;
  554. swscanf(buf, L"Inst_%u", &dwIndex);
  555. // Now we know which object is desired.
  556. // ====================================
  557. IWbemObjectAccess *pOurCopy = m_aInstances[dwIndex];
  558. // The refresher being supplied by the caller is actually
  559. // one of our own refreshers, so a simple cast is convenient
  560. // so that we can access private members.
  561. // =========================================================
  562. CNt5Refresher *pOurRefresher = (CNt5Refresher *) pRefresher;
  563. pOurRefresher->AddObject(pOurCopy, plId);
  564. // Return a copy of the internal object.
  565. // =====================================
  566. pOurCopy->AddRef();
  567. *ppRefreshable = pOurCopy;
  568. *plId = LONG(dwIndex);
  569. return NO_ERROR;
  570. }
  571. //***************************************************************************
  572. //
  573. // CNt5Refresher::StopRefreshing
  574. //
  575. // Called whenever a user wants to remove an object from a refresher.
  576. //
  577. // Parameters:
  578. // <pRefresher> The refresher object from which we are to
  579. // remove the perf object.
  580. // <lId> The ID of the object.
  581. // <lFlags> Not used.
  582. //
  583. //***************************************************************************
  584. // ok
  585. HRESULT CNt5PerfProvider::StopRefreshing(
  586. /* [in] */ IWbemRefresher __RPC_FAR *pRefresher,
  587. /* [in] */ long lId,
  588. /* [in] */ long lFlags
  589. )
  590. {
  591. // The refresher being supplied by the caller is actually
  592. // one of our own refreshers, so a simple cast is convenient
  593. // so that we can access private members.
  594. // =========================================================
  595. CNt5Refresher *pOurRefresher = (CNt5Refresher *) pRefresher;
  596. pOurRefresher->RemoveObject(lId);
  597. return NO_ERROR;
  598. }
  599. //***************************************************************************
  600. //
  601. // CNt5Refresher::CreateRefreshableEnum
  602. //
  603. // Called whenever a user wants to create an enumeration in a refresher.
  604. //
  605. // Parameters:
  606. // <pNamespace> The namespace this is for
  607. // <wszClass> Name of the class we are enumerating
  608. // <pRefresher> The refresher object from which we are to
  609. // remove the perf object.
  610. // <lFlags> Not used.
  611. // <pContext> Wbem Context object
  612. // <pHiPerfEnum> Enumerator object into which refresher should place
  613. // its results
  614. // <plId> The enum id (for identification during removal)
  615. //
  616. //***************************************************************************
  617. // ok
  618. HRESULT CNt5PerfProvider::CreateRefreshableEnum(
  619. /* [in] */ IWbemServices* pNamespace,
  620. /* [in, string] */ LPCWSTR wszClass,
  621. /* [in] */ IWbemRefresher* pRefresher,
  622. /* [in] */ long lFlags,
  623. /* [in] */ IWbemContext* pContext,
  624. /* [in] */ IWbemHiPerfEnum* pHiPerfEnum,
  625. /* [out] */ long* plId )
  626. {
  627. // Just a placeholder for now
  628. return E_NOTIMPL;
  629. }
  630. //***************************************************************************
  631. //
  632. // CNt5Refresher::CreateRefreshableEnum
  633. //
  634. // Called whenever a user wants to create an enumeration in a refresher.
  635. //
  636. // Parameters:
  637. // <pNamespace> The namespace this is for
  638. // <lNumObjects> Number of objects in the array
  639. // <apObj> Objects to retrieve (keys are set)
  640. // <lFlags> Not used.
  641. // <pContext> Wbem Context object
  642. // <pHiPerfEnum> Enumerator object into which refresher should place
  643. // its results
  644. // <plId> The enum id (for identification during removal)
  645. //
  646. //***************************************************************************
  647. // ok
  648. HRESULT CNt5PerfProvider::GetObjects(
  649. /* [in] */ IWbemServices* pNamespace,
  650. /* [in] */ long lNumObjects,
  651. /* [in,size_is(lNumObjects)] */ IWbemObjectAccess** apObj,
  652. /* [in] */ long lFlags,
  653. /* [in] */ IWbemContext* pContext)
  654. {
  655. // Just a placeholder for now
  656. return E_NOTIMPL;
  657. }