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.

610 lines
20 KiB

  1. //=================================================================
  2. //
  3. // Copyright (c) 2000-2001 Microsoft Corporation, All Rights Reserved
  4. //
  5. // binding.cpp -- Rule-based association class
  6. //
  7. // This class allows for the creation of a specific type of rule-based associations. Consider
  8. // this example:
  9. //
  10. // CBinding MyPhysicalDiskToLogicalDisk(
  11. // L"PhysicalDiskToLogicalDisk",
  12. // L"Root\\default",
  13. // L"PhysicalFixedDisk",
  14. // L"LogicalDisk",
  15. // L"Antecendent",
  16. // L"Dependent",
  17. // L"MappedDriveLetter",
  18. // L"DriveLetter"
  19. // );
  20. //
  21. // This declaration is saying that there is a class named "PhysicalDiskToLogicalDisk" which
  22. // resides in the "root\default" namespace. It is an association between the "PhysicalFixedDisk"
  23. // class, and the "LogicalDisk" class. The "PhysicalFixedDisk" value goes into the
  24. // "Antecendent" property of the "PhysicalDiskToLogicalDisk" class, and the
  25. // "LogicalDisk" value goes in the "Dependent" property of the "PhysicalDiskToLogicalDisk" class.
  26. // Only return instances where PhysicalFixedDisk.MappedDriveLetter = LogicalDisk.DriveLetter.
  27. //
  28. // Some notes:
  29. // - When choosing which of the two classes should be the left class, choose the class that is
  30. // likely to have fewer instances. This will result in less memory being used, and instances
  31. // being sent back to the client sooner.
  32. //
  33. // - CBinding supports ExecQuery, GetObject, and EnumerateInstances.
  34. //
  35. // - CBinding is designed to be derived from. For example, if your association needs to
  36. // support DeleteInstance, ExecMethod, or PutInstance, create a class that derives from
  37. // CBinding, and add the appropriate methods. Also, various methods such as
  38. // LoadPropertyValues and AreRelated may be useful for further customization.
  39. //
  40. // - The two endpoint classes can be dynamic, static, or abstract. CBinding will do a deep
  41. // enumeration (actually a query, which is always deep) to retrieve the instances.
  42. //
  43. // - When calling the endpoint classes, CBinding will use per property gets, and queries
  44. // with Select clauses and/or Where statements. If the endpoint classes support per-property
  45. // gets or queries, this will result in better performance for the associaton class.
  46. //
  47. // - The association class and both endpoints must all be in the same namespace.
  48. //
  49. // See also: CAssociation (assoc.cpp) for a different type of rule-based association.
  50. //
  51. //=================================================================
  52. #include "precomp.h"
  53. #include "Binding.h"
  54. #include <helper.h>
  55. /////////////////////////////////////////////////////////////////////
  56. //
  57. // Function: CBinding::CBinding
  58. //
  59. // Constructor.
  60. //
  61. /////////////////////////////////////////////////////////////////////
  62. CBinding::CBinding(
  63. LPCWSTR pwszClassName,
  64. LPCWSTR pwszNamespaceName,
  65. LPCWSTR pwszLeftClassName,
  66. LPCWSTR pwszRightClassName,
  67. LPCWSTR pwszLeftPropertyName,
  68. LPCWSTR pwszRightPropertyName,
  69. LPCWSTR pwszLeftBindingPropertyName,
  70. LPCWSTR pwszRightBindingPropertyName
  71. ) : CAssociation (
  72. pwszClassName,
  73. pwszNamespaceName,
  74. pwszLeftClassName,
  75. pwszRightClassName,
  76. pwszLeftPropertyName,
  77. pwszRightPropertyName
  78. )
  79. {
  80. // Save off the binding property names
  81. m_sLeftBindingPropertyName = pwszLeftBindingPropertyName;
  82. m_sRightBindingPropertyName = pwszRightBindingPropertyName;
  83. }
  84. /////////////////////////////////////////////////////////////////////
  85. //
  86. // Function: CBinding::~CBinding
  87. //
  88. // Destructor.
  89. //
  90. /////////////////////////////////////////////////////////////////////
  91. CBinding::~CBinding()
  92. {
  93. }
  94. /////////////////////////////////////////////////////////////////////
  95. //
  96. // Function: CBinding::AreRelated
  97. //
  98. // Determine whether the two instances are related. For
  99. // CBinding, this is done by comparing their BindingProperty values.
  100. //
  101. // Note that NULL properties values are not considered to be related
  102. // to anything, even another NULL.
  103. //
  104. /////////////////////////////////////////////////////////////////////
  105. bool CBinding::AreRelated(
  106. const CInstance *pLeft,
  107. const CInstance *pRight
  108. )
  109. {
  110. bool bRet = false;
  111. variant_t LeftBindingPropertyValue,
  112. RightBindingPropertyValue;
  113. if (pLeft->GetVariant(m_sLeftBindingPropertyName, LeftBindingPropertyValue) &&
  114. pRight->GetVariant(m_sRightBindingPropertyName, RightBindingPropertyValue) )
  115. {
  116. bRet = CompareVariantsNoCase(&LeftBindingPropertyValue, &RightBindingPropertyValue);
  117. }
  118. return bRet;
  119. }
  120. /////////////////////////////////////////////////////////////////////
  121. //
  122. // Function: CBinding::GetRightInstances
  123. //
  124. // Make an async call (well, sort of async) to retrieve all of the
  125. // instances of the righthand class. If possible use the sLeftWheres
  126. // to create a query to minimize the number of returned instances.
  127. //
  128. /////////////////////////////////////////////////////////////////////
  129. HRESULT CBinding::GetRightInstances(
  130. MethodContext *pMethodContext,
  131. TRefPointerCollection<CInstance> *lefts,
  132. const CHStringArray &sLeftWheres
  133. )
  134. {
  135. CHString sQuery;
  136. // Did we get any where clauses?
  137. if (sLeftWheres.GetSize() == 0)
  138. {
  139. // Nope, retrieve them all.
  140. sQuery.Format(L"SELECT __RELPATH, %s FROM %s WHERE %s<>NULL",
  141. (LPCWSTR)m_sRightBindingPropertyName,
  142. (LPCWSTR)m_sRightClassName,
  143. (LPCWSTR)m_sRightBindingPropertyName);
  144. }
  145. else
  146. {
  147. // Yup, build a query to only retrieve those instances.
  148. CHString sQuery2;
  149. sQuery.Format(L"SELECT __RELPATH, %s FROM %s WHERE (%s<>NULL) AND (%s=%s ",
  150. (LPCWSTR)m_sRightBindingPropertyName,
  151. (LPCWSTR)m_sRightClassName,
  152. (LPCWSTR)m_sRightBindingPropertyName,
  153. (LPCWSTR)m_sRightBindingPropertyName,
  154. (LPCWSTR)sLeftWheres[0]);
  155. // Usually, we should only have one (that what ASSOCIATORS and REFERENCES will
  156. // generate). However, if we have more than one, tack the rest on.
  157. for (DWORD x=1; x < sLeftWheres.GetSize(); x++)
  158. {
  159. sQuery2.Format(L"OR %s=%s ", (LPCWSTR)m_sRightBindingPropertyName, (LPCWSTR)sLeftWheres[x]);
  160. sQuery += sQuery2;
  161. }
  162. // put the final close parenthesis.
  163. sQuery.SetAt(sQuery.GetLength() - 1, L')');
  164. }
  165. // 'StaticEnumerationCallback' will get called once for each instance
  166. // returned from the query
  167. HRESULT hr = CWbemProviderGlue::GetInstancesByQueryAsynch(
  168. sQuery,
  169. this,
  170. StaticEnumerationCallback,
  171. GetNamespace(),
  172. pMethodContext,
  173. lefts);
  174. return hr;
  175. }
  176. /////////////////////////////////////////////////////////////////////
  177. //
  178. // Function: CBinding::GetLeftInstances
  179. //
  180. // Retrieve the lefthand instances, storing them in lefts
  181. //
  182. /////////////////////////////////////////////////////////////////////
  183. HRESULT CBinding::GetLeftInstances(
  184. MethodContext *pMethodContext,
  185. TRefPointerCollection<CInstance> &lefts,
  186. const CHStringArray &sRightWheres
  187. )
  188. {
  189. CHString sQuery;
  190. // Did we get any where clauses?
  191. if (sRightWheres.GetSize() == 0)
  192. {
  193. // Nope, retrieve them all.
  194. sQuery.Format(L"SELECT __RELPATH, %s FROM %s WHERE %s <> NULL",
  195. (LPCWSTR)m_sLeftBindingPropertyName,
  196. (LPCWSTR)m_sLeftClassName,
  197. (LPCWSTR)m_sLeftBindingPropertyName
  198. );
  199. }
  200. else
  201. {
  202. // Yup, build a query to only retrieve those instances.
  203. CHString sQuery2;
  204. sQuery.Format(L"SELECT __RELPATH, %s FROM %s WHERE (%s<>NULL) AND (%s=%s ",
  205. (LPCWSTR)m_sLeftBindingPropertyName,
  206. (LPCWSTR)m_sLeftClassName,
  207. (LPCWSTR)m_sLeftBindingPropertyName,
  208. (LPCWSTR)m_sLeftBindingPropertyName,
  209. (LPCWSTR)sRightWheres[0]);
  210. // Usually, we should only have one (that what ASSOCIATORS and REFERENCES will
  211. // generate). However, if we have more than one, tack the rest on.
  212. for (DWORD x=1; x < sRightWheres.GetSize(); x++)
  213. {
  214. sQuery2.Format(L"OR %s=%s ", (LPCWSTR)m_sLeftBindingPropertyName, (LPCWSTR)sRightWheres[x]);
  215. sQuery += sQuery2;
  216. }
  217. // put the final close parenthesis.
  218. sQuery.SetAt(sQuery.GetLength() - 1, L')');
  219. }
  220. return CWbemProviderGlue::GetInstancesByQuery(sQuery, &lefts, pMethodContext, GetNamespace());
  221. }
  222. /////////////////////////////////////////////////////////////////////
  223. //
  224. // Function: CBinding::RetrieveLeftInstance
  225. //
  226. // Retrieve a specific lefthand instance. Use per-property gets
  227. // to only request the required properties for best performance.
  228. //
  229. /////////////////////////////////////////////////////////////////////
  230. HRESULT CBinding::RetrieveLeftInstance(
  231. LPCWSTR lpwszObjPath,
  232. CInstance **ppInstance,
  233. MethodContext *pMethodContext
  234. )
  235. {
  236. CHStringArray csaProperties;
  237. csaProperties.Add(L"__Relpath");
  238. csaProperties.Add(m_sLeftBindingPropertyName);
  239. return CWbemProviderGlue::GetInstancePropertiesByPath(lpwszObjPath, ppInstance, pMethodContext, csaProperties);
  240. }
  241. /////////////////////////////////////////////////////////////////////
  242. //
  243. // Function: CBinding::RetrieveRightInstance
  244. //
  245. // Retrieve a specific lefthand instance. Use per-property gets
  246. // to only request the required properties for best performance.
  247. //
  248. /////////////////////////////////////////////////////////////////////
  249. HRESULT CBinding::RetrieveRightInstance(
  250. LPCWSTR lpwszObjPath,
  251. CInstance **ppInstance,
  252. MethodContext *pMethodContext
  253. )
  254. {
  255. CHStringArray csaProperties;
  256. csaProperties.Add(L"__Relpath");
  257. csaProperties.Add(m_sRightBindingPropertyName);
  258. return CWbemProviderGlue::GetInstancePropertiesByPath(lpwszObjPath, ppInstance, pMethodContext, csaProperties);
  259. }
  260. /////////////////////////////////////////////////////////////////////
  261. //
  262. // Function: CBinding::MakeWhere
  263. //
  264. // If the key property of the righthand class also happens to be
  265. // the binding property AND if we have a path for specific righthand
  266. // instances we need to return, then we can use the path for the
  267. // righthand instances to build a where clause for the lefthand
  268. // instances.
  269. //
  270. // Note that if we find invalid paths in sRightPaths, we remove
  271. // them from sRightPaths.
  272. //
  273. /////////////////////////////////////////////////////////////////////
  274. void CBinding::MakeWhere(
  275. CHStringArray &sRightPaths,
  276. CHStringArray &sRightWheres
  277. )
  278. {
  279. // See if we have any righthand instances
  280. if (sRightPaths.GetSize() > 0)
  281. {
  282. ParsedObjectPath *pParsedPath = NULL;
  283. CObjectPathParser objpathParser;
  284. CHString sTemp;
  285. for (DWORD x=0; x < sRightPaths.GetSize();) // Note that x++ is done inside the loop
  286. {
  287. // Parse the instance
  288. int nStatus = objpathParser.Parse( sRightPaths[x], &pParsedPath );
  289. if ( 0 == nStatus )
  290. {
  291. OnDeleteObj<ParsedObjectPath *,
  292. CObjectPathParser,
  293. void(CObjectPathParser::*)(ParsedObjectPath *),
  294. &CObjectPathParser::Free> ReleaseMe(&objpathParser,pParsedPath);
  295. // See if the property name in the key is the property name we are binding on
  296. if ( (pParsedPath->m_dwNumKeys == 1) && (pParsedPath->m_paKeys[0]->m_pName != NULL) )
  297. {
  298. if (_wcsicmp(pParsedPath->m_paKeys[0]->m_pName, m_sRightBindingPropertyName) == 0)
  299. {
  300. // Yes, it is. Make a where clause statement.
  301. HRESULT hr = MakeString(&pParsedPath->m_paKeys[0]->m_vValue, sTemp);
  302. // See if we already have that where clause
  303. if ( SUCCEEDED(hr) && IsInList(sRightWheres, sTemp) == -1)
  304. {
  305. // A query with 1000 where clauses isn't going
  306. // to be very efficient either. Pick a reasonable limit
  307. if (sRightWheres.GetSize() < MAX_ORS)
  308. {
  309. sRightWheres.Add(sTemp);
  310. }
  311. else
  312. {
  313. // Too many. Fall back on a complete enum
  314. sRightWheres.RemoveAll();
  315. break;
  316. }
  317. }
  318. }
  319. else
  320. {
  321. // Fall back on a complete enum
  322. sRightWheres.RemoveAll();
  323. break;
  324. }
  325. }
  326. else
  327. {
  328. // Fall back on a complete enum
  329. sRightWheres.RemoveAll();
  330. break;
  331. }
  332. // This was a valid path
  333. x++;
  334. }
  335. else
  336. {
  337. // This was an invalid path. Remove it
  338. sRightPaths.RemoveAt(x);
  339. }
  340. }
  341. }
  342. }
  343. /////////////////////////////////////////////////////////////////////
  344. //
  345. // Function: CBinding::FindWhere
  346. //
  347. // At this point, we have loaded all the lefthand instances. We
  348. // can use the binding property from these instances to build
  349. // a where clause to be used when retrieve the righthand instances.
  350. //
  351. /////////////////////////////////////////////////////////////////////
  352. HRESULT CBinding::FindWhere(
  353. TRefPointerCollection<CInstance> &lefts,
  354. CHStringArray &sLeftWheres
  355. )
  356. {
  357. REFPTRCOLLECTION_POSITION posLeft;
  358. CInstancePtr pLeft;
  359. HRESULT hr = WBEM_S_NO_ERROR;
  360. if (lefts.BeginEnum(posLeft))
  361. {
  362. variant_t vLeftBindingPropertyValue;
  363. CHString sTemp;
  364. // Walk the left instances
  365. for (pLeft.Attach(lefts.GetNext(posLeft)) ;
  366. (pLeft != NULL) ;
  367. pLeft.Attach(lefts.GetNext(posLeft)) )
  368. {
  369. // Get the binding property from the left
  370. if (pLeft->GetVariant(m_sLeftBindingPropertyName, vLeftBindingPropertyValue))
  371. {
  372. // Turn it into a where clause
  373. hr = MakeString(&vLeftBindingPropertyValue, sTemp);
  374. // See if we alread have this where clause
  375. if (SUCCEEDED(hr) && IsInList(sLeftWheres, sTemp) == -1)
  376. {
  377. // A query with 1000 where clauses isn't going
  378. // to be very efficient either. Pick a reasonable limit
  379. if (sLeftWheres.GetSize() < MAX_ORS)
  380. {
  381. sLeftWheres.Add(sTemp);
  382. }
  383. else
  384. {
  385. // Too many. Fall back to enum
  386. sLeftWheres.RemoveAll();
  387. break;
  388. }
  389. }
  390. vLeftBindingPropertyValue.Clear();
  391. }
  392. else
  393. {
  394. hr = WBEM_E_FAILED;
  395. break;
  396. }
  397. }
  398. lefts.EndEnum();
  399. }
  400. return hr;
  401. }
  402. /////////////////////////////////////////////////////////////////////
  403. //
  404. // Function: CBinding::MakeString
  405. //
  406. // Turn the bindingproperty value into a string suitable for using
  407. // in a wql where clause.
  408. //
  409. /////////////////////////////////////////////////////////////////////
  410. HRESULT CBinding::MakeString(VARIANT *pvValue, CHString &sTemp)
  411. {
  412. bool bIsString = V_VT(pvValue) == VT_BSTR;
  413. HRESULT hr = VariantChangeType(
  414. pvValue,
  415. pvValue,
  416. VARIANT_NOVALUEPROP,
  417. VT_BSTR
  418. );
  419. if (SUCCEEDED(hr))
  420. {
  421. // If the original type was string, we need to escape quotes
  422. // and backslashes, and put double quotes around it.
  423. if (bIsString)
  424. {
  425. CHString sTemp2;
  426. EscapeCharacters(V_BSTR(pvValue), sTemp2);
  427. sTemp.Format(L"\"%s\"", (LPCWSTR)sTemp2);
  428. }
  429. else
  430. {
  431. sTemp = V_BSTR(pvValue);
  432. }
  433. }
  434. return hr;
  435. }
  436. /////////////////////////////////////////////////////////////////////
  437. //
  438. // Function: CBinding::IsInList
  439. //
  440. // See whether a given string already exists in a chstring array.
  441. //
  442. /////////////////////////////////////////////////////////////////////
  443. DWORD CBinding::IsInList(
  444. const CHStringArray &csaArray,
  445. LPCWSTR pwszValue
  446. )
  447. {
  448. DWORD dwSize = csaArray.GetSize();
  449. for (DWORD x=0; x < dwSize; x++)
  450. {
  451. // Note this is a CASE INSENSITIVE compare
  452. if (_wcsicmp(csaArray[x], pwszValue) == 0)
  453. {
  454. return x;
  455. }
  456. }
  457. return -1;
  458. }
  459. /////////////////////////////////////////////////////////////////////
  460. //
  461. // Function: CBinding::CompareVariantsNoCase
  462. //
  463. // Compare two variants to see if they are the same.
  464. //
  465. /////////////////////////////////////////////////////////////////////
  466. bool CBinding::CompareVariantsNoCase(const VARIANT *v1, const VARIANT *v2)
  467. {
  468. if (v1->vt == v2->vt)
  469. {
  470. switch (v1->vt)
  471. {
  472. case VT_NULL: return false;
  473. case VT_BOOL: return (v1->boolVal == v2->boolVal);
  474. case VT_UI1: return (v1->bVal == v2->bVal);
  475. case VT_I2: return (v1->iVal == v2->iVal);
  476. case VT_I4: return (v1->lVal == v2->lVal);
  477. case VT_R4: return (v1->fltVal == v2->fltVal);
  478. case VT_R8: return (v1->dblVal == v2->dblVal);
  479. case VT_BSTR:
  480. {
  481. if ( (v1->bstrVal == v2->bstrVal) || // deal with both being NULL
  482. (0 == _wcsicmp(v1->bstrVal, v2->bstrVal)) )
  483. {
  484. return true;
  485. }
  486. else
  487. {
  488. return false;
  489. }
  490. }
  491. default:
  492. {
  493. // Should never get here
  494. }
  495. }
  496. }
  497. return false;
  498. }
  499. /////////////////////////////////////////////////////////////////////
  500. //
  501. // Function: CBinding::EscapeBackslashes
  502. //
  503. // Prefix " and \ characters with an additional \
  504. //
  505. /////////////////////////////////////////////////////////////////////
  506. VOID CBinding::EscapeCharacters(LPCWSTR wszIn,
  507. CHString& chstrOut)
  508. {
  509. CHString chstrCpyNormPathname(wszIn);
  510. LONG lNext = -1L;
  511. chstrOut.Empty();
  512. // Find the next character to escape
  513. while( (lNext = chstrCpyNormPathname.FindOneOf(L"\"\\") ) != -1)
  514. {
  515. // Add on to the new string we are building:
  516. chstrOut += chstrCpyNormPathname.Left(lNext + 1);
  517. // Add on the second backslash:
  518. chstrOut += _T('\\');
  519. // Hack off from the input string the portion we just copied
  520. chstrCpyNormPathname = chstrCpyNormPathname.Right(chstrCpyNormPathname.GetLength() - lNext - 1);
  521. }
  522. // If the last character wasn't a '\', there may still be leftovers, so
  523. // copy them here.
  524. if(chstrCpyNormPathname.GetLength() != 0)
  525. {
  526. chstrOut += chstrCpyNormPathname;
  527. }
  528. }