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.

748 lines
22 KiB

  1. /*++
  2. Copyright (C) 1999-2001 Microsoft Corporation
  3. Module Name:
  4. PROTOQ.CPP
  5. Abstract:
  6. Prototype query support for WinMgmt Query Engine.
  7. This was split out from QENGINE.CPP for better source
  8. organization.
  9. History:
  10. raymcc 04-Jul-99 Created.
  11. --*/
  12. #include "precomp.h"
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <wbemcore.h>
  16. int SelectedClass::SetAll(int & nPos)
  17. {
  18. m_bAll = TRUE;
  19. // For each property, add an entry
  20. CWbemClass *pCls = (CWbemClass *)m_pClassDef;
  21. pCls->BeginEnumeration(WBEM_FLAG_NONSYSTEM_ONLY);
  22. OnDeleteObj0<IWbemClassObject,
  23. HRESULT(__stdcall IWbemClassObject:: *)(void),
  24. IWbemClassObject::EndEnumeration> EndMe(pCls);
  25. BSTR PropName = 0;
  26. while (S_OK == pCls->Next(0, &PropName, NULL, NULL, NULL))
  27. {
  28. CSysFreeMe smf(PropName);
  29. int nRes = SetNamed(PropName, nPos);
  30. if (CFlexArray::no_error != nRes)
  31. return nRes;
  32. }
  33. return CFlexArray::no_error;
  34. };
  35. HRESULT ReleaseClassDefs(IN CFlexArray *pDefs)
  36. {
  37. for (int i = pDefs->Size()-1; i >= 0 ; i--)
  38. {
  39. SelectedClass *pSelClass = (SelectedClass *) pDefs->GetAt(i);
  40. delete pSelClass;
  41. }
  42. return WBEM_NO_ERROR;
  43. }
  44. //***************************************************************************
  45. //
  46. // ExecPrototypeQuery
  47. //
  48. // Called by CQueryEngine::ExecQuery for SMS-style prototypes.
  49. //
  50. // Executes the query and returns only the class definition implied
  51. // by the query, whether a join or a simple class def.
  52. //
  53. //***************************************************************************
  54. HRESULT ExecPrototypeQuery(
  55. IN CWbemNamespace *pNs,
  56. IN LPWSTR pszQuery,
  57. IN IWbemContext* pContext,
  58. IN CBasicObjectSink *pSink
  59. )
  60. {
  61. HRESULT hRes;
  62. int nRes;
  63. if (pSink == NULL) return WBEM_E_INVALID_PARAMETER;
  64. if (pNs == NULL )
  65. return pSink->Return(WBEM_E_INVALID_PARAMETER);
  66. // Parse the query and determine if it is a single class.
  67. // ======================================================
  68. CTextLexSource src(pszQuery);
  69. CWQLScanner Parser(&src);
  70. nRes = Parser.Parse();
  71. if (nRes != CWQLScanner::SUCCESS)
  72. return pSink->Return(WBEM_E_INVALID_QUERY);
  73. // If a single class definition, branch, since we don't
  74. // want to create a __GENERIC object.
  75. // ====================================================
  76. CWStringArray aAliases;
  77. Parser.GetReferencedAliases(aAliases);
  78. if (aAliases.Size() == 1)
  79. {
  80. LPWSTR pszClass = Parser.AliasToTable(aAliases[0]);
  81. return GetUnaryPrototype(Parser, pszClass, aAliases[0], pNs, pContext, pSink);
  82. }
  83. // If here, a join must have occurred.
  84. // ===================================
  85. CFlexArray aClassDefs;
  86. OnDelete<CFlexArray *,HRESULT(*)( CFlexArray *), ReleaseClassDefs > FreeMe(&aClassDefs);
  87. hRes = RetrieveClassDefs(Parser,pNs, pContext,aAliases,&aClassDefs); // throw
  88. if (FAILED(hRes)) return pSink->Return(WBEM_E_INVALID_QUERY);
  89. // Iterate through all the properties selected.
  90. // ============================================
  91. const CFlexArray *pSelCols = Parser.GetSelectedColumns();
  92. int nPosSoFar = 0;
  93. for (int i = 0; i < pSelCols->Size(); i++)
  94. {
  95. SWQLColRef *pColRef = (SWQLColRef *) pSelCols->GetAt(i);
  96. hRes = SelectColForClass(Parser, &aClassDefs, pColRef, nPosSoFar);
  97. if (hRes) return pSink->Return(hRes);
  98. }
  99. // If here, we have the class definitions.
  100. // =======================================
  101. IWbemClassObject *pProtoInst = 0;
  102. hRes = AdjustClassDefs(&aClassDefs, &pProtoInst);
  103. CReleaseMe rmProto(pProtoInst);
  104. if (hRes) return pSink->Return(hRes);
  105. if (FAILED(hRes = pSink->Add(pProtoInst))) return pSink->Return(hRes);
  106. return pSink->Return(WBEM_NO_ERROR);
  107. }
  108. //***************************************************************************
  109. //
  110. //***************************************************************************
  111. HRESULT RetrieveClassDefs(
  112. IN CWQLScanner & Parser,
  113. IN CWbemNamespace *pNs,
  114. IN IWbemContext *pContext,
  115. IN CWStringArray & aAliasNames,
  116. OUT CFlexArray *pDefs
  117. )
  118. {
  119. for (int i = 0; i < aAliasNames.Size(); i++)
  120. {
  121. // Retrieve the class definition.
  122. LPWSTR pszClass = Parser.AliasToTable(aAliasNames[i]);
  123. if (pszClass == 0)
  124. continue;
  125. IWbemClassObject *pClassDef = 0;
  126. HRESULT hRes = pNs->Exec_GetObjectByPath(pszClass, 0, pContext,&pClassDef, 0);
  127. CReleaseMe rmClassDef(pClassDef);
  128. if (FAILED(hRes)) return hRes;
  129. wmilib::auto_ptr<SelectedClass> pSelClass( new SelectedClass);
  130. if (NULL == pSelClass.get())
  131. return WBEM_E_OUT_OF_MEMORY;
  132. pSelClass->m_wsClass = pszClass; // throw
  133. pSelClass->m_wsAlias = aAliasNames[i]; // throw
  134. pClassDef->AddRef();
  135. pSelClass->m_pClassDef = pClassDef;
  136. if (CFlexArray::no_error != pDefs->Add(pSelClass.get())) return WBEM_E_OUT_OF_MEMORY;
  137. pSelClass.release();
  138. }
  139. return WBEM_NO_ERROR;
  140. }
  141. //***************************************************************************
  142. //
  143. //***************************************************************************
  144. //***************************************************************************
  145. //
  146. //***************************************************************************
  147. HRESULT SelectColForClass(
  148. IN CWQLScanner & Parser,
  149. IN CFlexArray *pClassDefs,
  150. IN SWQLColRef *pColRef,
  151. IN int & nPosition
  152. )
  153. {
  154. int i;
  155. HRESULT hRes;
  156. if (!pColRef)
  157. return WBEM_E_FAILED;
  158. // If the column reference contains the class referenced
  159. // via an alias and there is no asterisk, we are all set.
  160. // ======================================================
  161. if (pColRef->m_pTableRef)
  162. {
  163. // We now have the class name. Let's find it and add
  164. // the referenced column for that class!
  165. // =================================================
  166. for (i = 0; i < pClassDefs->Size(); i++)
  167. {
  168. SelectedClass *pSelClass = (SelectedClass *) pClassDefs->GetAt(i);
  169. if (wbem_wcsicmp(LPWSTR(pSelClass->m_wsAlias), pColRef->m_pTableRef) != 0)
  170. continue;
  171. CWbemClass *pCls = (CWbemClass *) pSelClass->m_pClassDef;
  172. // See if the asterisk was used for this class.
  173. // =============================================
  174. if (pColRef->m_pColName[0] == L'*' && pColRef->m_pColName[1] == 0)
  175. {
  176. pSelClass->SetAll(nPosition);
  177. return WBEM_NO_ERROR;
  178. }
  179. // If here, a property was mentioned by name.
  180. // Verify that it exists.
  181. // ==========================================
  182. CVar Prop;
  183. hRes = pCls->GetProperty(pColRef->m_pColName, &Prop);
  184. if (FAILED(hRes))
  185. return WBEM_E_INVALID_QUERY;
  186. // Mark it as seleted.
  187. // ===================
  188. if (CFlexArray::no_error != pSelClass->SetNamed(pColRef->m_pColName, nPosition))
  189. return WBEM_E_OUT_OF_MEMORY;
  190. return WBEM_NO_ERROR;
  191. }
  192. // If here, we couldn't locate the property in any class.
  193. // ======================================================
  194. return WBEM_E_INVALID_QUERY;
  195. }
  196. // Did we select * from all tables?
  197. // ================================
  198. if (pColRef->m_dwFlags & WQL_FLAG_ASTERISK)
  199. {
  200. for (i = 0; i < pClassDefs->Size(); i++)
  201. {
  202. SelectedClass *pSelClass = (SelectedClass *) pClassDefs->GetAt(i);
  203. if (CFlexArray::no_error != pSelClass->SetAll(nPosition))
  204. return WBEM_E_OUT_OF_MEMORY;
  205. }
  206. return WBEM_NO_ERROR;
  207. }
  208. // If here, we have an uncorrelated property and we have to find out
  209. // which class it belongs to. If it belongs to more than one, we have
  210. // an ambiguous query.
  211. // ===================================================================
  212. DWORD dwTotalMatches = 0;
  213. for (i = 0; i < pClassDefs->Size(); i++)
  214. {
  215. SelectedClass *pSelClass = (SelectedClass *) pClassDefs->GetAt(i);
  216. CWbemClass *pCls = (CWbemClass *) pSelClass->m_pClassDef;
  217. // Try to locate the property in this class.
  218. // =========================================
  219. CVar Prop;
  220. hRes = pCls->GetProperty(pColRef->m_pColName, &Prop);
  221. if (hRes == 0)
  222. {
  223. if (CFlexArray::no_error != pSelClass->SetNamed(pColRef->m_pColName, nPosition))
  224. return WBEM_E_OUT_OF_MEMORY;
  225. dwTotalMatches++;
  226. }
  227. }
  228. // If more than one match occurred, we have an ambiguous query.
  229. // ============================================================
  230. if (dwTotalMatches != 1)
  231. return WBEM_E_INVALID_QUERY;
  232. return WBEM_NO_ERROR;
  233. }
  234. //***************************************************************************
  235. //
  236. //***************************************************************************
  237. HRESULT AddOrderQualifiers(
  238. CWbemClass *pCls,
  239. BSTR PropName,
  240. CFlexArray Matches
  241. )
  242. {
  243. IWbemQualifierSet * pQual;
  244. SCODE sc = pCls->GetPropertyQualifierSet(PropName, &pQual);
  245. if(sc != S_OK)
  246. return sc;
  247. CReleaseMe rm(pQual);
  248. // Create a safe array
  249. SAFEARRAYBOUND aBounds[1];
  250. aBounds[0].lLbound = 0;
  251. aBounds[0].cElements = Matches.Size();
  252. SAFEARRAY* pArray = SafeArrayCreate(VT_I4, 1, aBounds);
  253. if (NULL == pArray) return WBEM_E_OUT_OF_MEMORY;
  254. // Stuff the individual data pieces
  255. // ================================
  256. for(int nIndex = 0; nIndex < Matches.Size(); nIndex++)
  257. {
  258. long lPos = PtrToLong(Matches.GetAt(nIndex));
  259. sc = SafeArrayPutElement(pArray, (long*)&nIndex, &lPos);
  260. }
  261. VARIANT var;
  262. var.vt = VT_ARRAY | VT_I4;
  263. var.parray = pArray;
  264. sc = pQual->Put(L"Order", &var, 0);
  265. VariantClear(&var);
  266. return sc;
  267. }
  268. //***************************************************************************
  269. //
  270. //***************************************************************************
  271. HRESULT SetPropertyOrderQualifiers(SelectedClass *pSelClass)
  272. {
  273. HRESULT hRes = S_OK;
  274. CWbemClass *pCls = (CWbemClass *) pSelClass->m_pClassDef;
  275. // Go through each property
  276. pCls->BeginEnumeration(WBEM_FLAG_NONSYSTEM_ONLY);
  277. OnDeleteObj0<IWbemClassObject,
  278. HRESULT(__stdcall IWbemClassObject:: *)(void),
  279. IWbemClassObject::EndEnumeration> EndMe(pCls);
  280. BSTR PropName = 0;
  281. while (S_OK == pCls->Next(0, &PropName, NULL, NULL, NULL))
  282. {
  283. CSysFreeMe sfm(PropName);
  284. // Build up a list of properties that Match
  285. CFlexArray Matches;
  286. bool bAtLeastOne = false;
  287. for(int iCnt = 0; iCnt < pSelClass->m_aSelectedCols.Size(); iCnt++)
  288. {
  289. if(!wbem_wcsicmp(pSelClass->m_aSelectedCols.GetAt(iCnt), PropName))
  290. {
  291. if (CFlexArray::no_error == Matches.Add(pSelClass->m_aSelectedColsPos.GetAt(iCnt)))
  292. {
  293. bAtLeastOne = true;
  294. }
  295. }
  296. }
  297. if(bAtLeastOne)
  298. {
  299. hRes = AddOrderQualifiers(pCls, PropName, Matches);
  300. if (FAILED(hRes)) return hRes;
  301. }
  302. }
  303. return hRes;
  304. }
  305. //***************************************************************************
  306. //
  307. // AdjustClassDefs
  308. //
  309. // After all class definitions have been retrieved, they are adjusted
  310. // to only have the properties required and combined into a __GENERIC
  311. // instance.
  312. //
  313. //***************************************************************************
  314. HRESULT AdjustClassDefs(
  315. IN CFlexArray *pClassDefs,
  316. OUT IWbemClassObject **pRetNewClass
  317. )
  318. {
  319. int i;
  320. HRESULT hRes;
  321. for (i = 0; i < pClassDefs->Size(); i++)
  322. {
  323. SelectedClass *pSelClass = (SelectedClass *) pClassDefs->GetAt(i);
  324. CWbemClass *pCls = (CWbemClass *) pSelClass->m_pClassDef;
  325. if (pSelClass->m_bAll)
  326. {
  327. hRes = SetPropertyOrderQualifiers(pSelClass);
  328. if (FAILED(hRes)) return hRes;
  329. continue;
  330. }
  331. WString wsError = pCls->FindLimitationError(0, &pSelClass->m_aSelectedCols);
  332. if (wsError.Length() > 0)
  333. return WBEM_E_FAILED;
  334. // Map the limitaiton
  335. // ==================
  336. CLimitationMapping Map;
  337. BOOL bValid = pCls->MapLimitation(0, &pSelClass->m_aSelectedCols, &Map);
  338. if (!bValid)
  339. return WBEM_E_FAILED;
  340. CWbemClass* pStrippedClass = 0;
  341. hRes = pCls->GetLimitedVersion(&Map, &pStrippedClass);
  342. if(SUCCEEDED(hRes))
  343. {
  344. pSelClass->m_pClassDef = pStrippedClass;
  345. pCls->Release();
  346. if (FAILED(hRes = SetPropertyOrderQualifiers(pSelClass))) return hRes;
  347. }
  348. }
  349. // Count the number of objects that actually have properties
  350. int iNumObj = 0;
  351. for (i = 0; i < pClassDefs->Size(); i++)
  352. {
  353. SelectedClass *pSelClass = (SelectedClass *) pClassDefs->GetAt(i);
  354. CWbemObject *pObj = (CWbemObject *) pSelClass->m_pClassDef;
  355. if (pObj->GetNumProperties() > 0)
  356. iNumObj++;
  357. }
  358. // If there is just one object with properties, return it rather than the generic object
  359. if(iNumObj == 1)
  360. {
  361. for (i = 0; i < pClassDefs->Size(); i++)
  362. {
  363. SelectedClass *pSelClass = (SelectedClass *) pClassDefs->GetAt(i);
  364. CWbemObject *pObj = (CWbemObject *) pSelClass->m_pClassDef;
  365. if (pObj->GetNumProperties() == 0)
  366. continue;
  367. // Return it.
  368. // ==========
  369. *pRetNewClass = pObj;
  370. pObj->AddRef();
  371. return WBEM_NO_ERROR;
  372. }
  373. }
  374. // Prepare a __GENERIC class def. We construct a dummy definition which
  375. // has properties named for each of the aliases used in the query.
  376. // =====================================================================
  377. CGenericClass *pNewClass = new CGenericClass;
  378. if (pNewClass == 0) return WBEM_E_OUT_OF_MEMORY;
  379. CReleaseMe rmNewCls((IWbemClassObject *)pNewClass);
  380. pNewClass->Init(); // throw
  381. for (i = 0; i < pClassDefs->Size(); i++)
  382. {
  383. SelectedClass *pSelClass = (SelectedClass *) pClassDefs->GetAt(i);
  384. CWbemObject *pObj = (CWbemObject *) pSelClass->m_pClassDef;
  385. if (pObj->GetNumProperties() == 0)
  386. continue;
  387. CVar vEmbeddedClass;
  388. vEmbeddedClass.SetAsNull();
  389. if (FAILED( hRes = pNewClass->SetPropValue(pSelClass->m_wsAlias, &vEmbeddedClass, CIM_OBJECT))) return hRes;
  390. CVar vClassName;
  391. if (FAILED(hRes = pObj->GetClassName(&vClassName))) return hRes;
  392. WString wsCimType = L"object:";
  393. wsCimType += vClassName.GetLPWSTR();
  394. CVar vCimType(VT_BSTR, wsCimType);
  395. if (FAILED( hRes = pNewClass->SetPropQualifier(pSelClass->m_wsAlias, L"cimtype", 0,&vCimType))) return hRes;
  396. };
  397. // Spawn an instance of this class.
  398. // ================================
  399. CWbemInstance* pProtoInst = 0;
  400. if (FAILED( hRes = pNewClass->SpawnInstance(0, (IWbemClassObject **) &pProtoInst))) return hRes;
  401. CReleaseMe rmProtInst((IWbemClassObject *)pProtoInst);
  402. rmNewCls.release();
  403. // Now assign the properties to the embedded instances.
  404. // ====================================================
  405. for (i = 0; i < pClassDefs->Size(); i++)
  406. {
  407. SelectedClass *pSelClass = (SelectedClass *) pClassDefs->GetAt(i);
  408. CWbemClass *pCls = (CWbemClass *) pSelClass->m_pClassDef;
  409. if (pCls->GetNumProperties() == 0)
  410. continue;
  411. CVar vEmbedded;
  412. vEmbedded.SetEmbeddedObject((IWbemClassObject *) pCls);
  413. if (FAILED( hRes = pProtoInst->SetPropValue(pSelClass->m_wsAlias, &vEmbedded, 0))) return hRes;
  414. };
  415. // Return it.
  416. // ==========
  417. rmProtInst.dismiss();
  418. *pRetNewClass = pProtoInst;
  419. return WBEM_NO_ERROR;
  420. }
  421. //***************************************************************************
  422. //
  423. //***************************************************************************
  424. HRESULT GetUnaryPrototype(
  425. IN CWQLScanner & Parser,
  426. IN LPWSTR pszClass,
  427. IN LPWSTR pszAlias,
  428. IN CWbemNamespace *pNs,
  429. IN IWbemContext *pContext,
  430. IN CBasicObjectSink *pSink
  431. )
  432. {
  433. int i;
  434. // Retrieve the class definition.
  435. // ==============================
  436. IWbemClassObject *pClassDef = 0;
  437. IWbemClassObject *pErrorObj = 0;
  438. HRESULT hRes = pNs->Exec_GetObjectByPath(pszClass, 0, pContext,&pClassDef, &pErrorObj);
  439. CReleaseMeRef<IWbemClassObject *> rmObj(pClassDef);
  440. CReleaseMe rmErrObj(pErrorObj);
  441. if (FAILED(hRes))
  442. {
  443. pSink->SetStatus(0, hRes, NULL, pErrorObj);
  444. return S_OK;
  445. }
  446. rmErrObj.release();
  447. CWbemClass *pCls = (CWbemClass *) pClassDef;
  448. BOOL bKeepAll = FALSE;
  449. // This keeps track of the order in which columns are selected
  450. SelectedClass sel;
  451. sel.m_wsClass = pszClass; // throw
  452. sel.m_pClassDef = pClassDef;
  453. pClassDef->AddRef();
  454. // Go through all the columns and make sure that the properties are valid
  455. // ======================================================================
  456. const CFlexArray *pSelCols = Parser.GetSelectedColumns();
  457. int nPosition = 0;
  458. for (i = 0; i < pSelCols->Size(); i++)
  459. {
  460. SWQLColRef *pColRef = (SWQLColRef *) pSelCols->GetAt(i);
  461. if (pColRef->m_dwFlags & WQL_FLAG_ASTERISK)
  462. {
  463. bKeepAll = TRUE;
  464. if (CFlexArray::no_error == sel.SetAll(nPosition))
  465. continue;
  466. else
  467. return pSink->Return(WBEM_E_FAILED);
  468. }
  469. if (pColRef->m_pColName)
  470. {
  471. // check for the "select x.* from x" case
  472. if(pColRef->m_pColName[0] == L'*' && pColRef->m_pColName[1] == 0)
  473. {
  474. if (!wbem_wcsicmp(pColRef->m_pTableRef, pszAlias)) // SEC:REVIEWED 2002-03-22 : OK, prior guarantee of NULL terminators
  475. {
  476. bKeepAll = TRUE;
  477. if (CFlexArray::no_error == sel.SetAll(nPosition))
  478. continue;
  479. else
  480. return pSink->Return(WBEM_E_FAILED);
  481. continue;
  482. }
  483. else
  484. {
  485. return pSink->Return(WBEM_E_INVALID_QUERY);
  486. }
  487. }
  488. // Verify that the class has it
  489. // ============================
  490. CIMTYPE ct;
  491. if(FAILED(pCls->GetPropertyType(pColRef->m_pColName, &ct)))
  492. {
  493. // No such property
  494. // ================
  495. return pSink->Return(WBEM_E_INVALID_QUERY);
  496. }
  497. if (CFlexArray::no_error != sel.SetNamed(pColRef->m_pColName, nPosition))
  498. return pSink->Return(WBEM_E_FAILED);
  499. }
  500. }
  501. // Eliminate unreferenced columns from the query.
  502. // ==============================================
  503. CWStringArray aPropsToKeep; // SEC:REVIEWED 2002-03-22 : May throw
  504. if(!bKeepAll)
  505. {
  506. // Move through each property in the class and
  507. // see if it is referenced. If not, remove it.
  508. // ============================================
  509. int nNumProps = pCls->GetNumProperties();
  510. for (i = 0; i < nNumProps; i++)
  511. {
  512. CVar Prop;
  513. HRESULT hrInner;
  514. hrInner = pCls->GetPropName(i, &Prop);
  515. if (FAILED(hrInner)) return pSink->Return(hrInner);
  516. // See if this name is used in the query.
  517. // ======================================
  518. for (int i2 = 0; i2 < pSelCols->Size(); i2++)
  519. {
  520. SWQLColRef *pColRef = (SWQLColRef *) pSelCols->GetAt(i2);
  521. if (pColRef->m_pColName && wbem_wcsicmp(Prop, pColRef->m_pColName) == 0)
  522. {
  523. if (CFlexArray::no_error != aPropsToKeep.Add((LPWSTR) Prop))
  524. return pSink->Return(WBEM_E_FAILED);
  525. break;
  526. }
  527. }
  528. }
  529. }
  530. // Now we have a list of properties to remove.
  531. // ===========================================
  532. if (!bKeepAll && aPropsToKeep.Size())
  533. {
  534. WString wsError = pCls->FindLimitationError(0, &aPropsToKeep); // throw
  535. if (wsError.Length() > 0)
  536. {
  537. return pSink->Return(WBEM_E_FAILED);
  538. }
  539. // Map the limitaiton
  540. // ==================
  541. CLimitationMapping Map;
  542. BOOL bValid = pCls->MapLimitation(0, &aPropsToKeep, &Map);
  543. if (!bValid)
  544. {
  545. return pSink->Return(WBEM_E_FAILED);
  546. }
  547. CWbemClass* pNewStrippedClass = 0;
  548. hRes = pCls->GetLimitedVersion(&Map, &pNewStrippedClass);
  549. if(SUCCEEDED(hRes))
  550. {
  551. // this is for the on-stack object
  552. pClassDef->Release();
  553. // this is for the copy given to the SelectClass object
  554. sel.m_pClassDef->Release();
  555. sel.m_pClassDef = pNewStrippedClass;
  556. pNewStrippedClass->AddRef();
  557. pClassDef = pNewStrippedClass; // give ownership to the oure scope
  558. }
  559. }
  560. // Add the Order qualifier
  561. hRes= SetPropertyOrderQualifiers(&sel);
  562. if (FAILED(hRes)) return pSink->Return(hRes);
  563. // Return it.
  564. // ==========
  565. hRes = pSink->Add(pClassDef);
  566. if (FAILED(hRes)) return pSink->Return(hRes);
  567. return pSink->Return(WBEM_NO_ERROR);
  568. }