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.

1723 lines
53 KiB

  1. #include "pch.h"
  2. #include "stddef.h"
  3. #include "dsrole.h"
  4. #pragma hdrstop
  5. /*-----------------------------------------------------------------------------
  6. / Class cache
  7. /----------------------------------------------------------------------------*/
  8. //
  9. // Class cache state and functions
  10. //
  11. #define ALL_PREFIXED_ATTRIBUTES \
  12. (CLASSCACHE_PROPPAGES| \
  13. CLASSCACHE_CONTEXTMENUS)
  14. #define ALL_NONPREFIXED_ATTRIBUTES \
  15. (CLASSCACHE_ICONS| \
  16. CLASSCACHE_FRIENDLYNAME| \
  17. CLASSCACHE_TREATASLEAF| \
  18. CLASSCACHE_ATTRIBUTENAMES| \
  19. CLASSCACHE_CREATIONINFO)
  20. #define ALL_DISPLAY_SPEC_VALUES \
  21. (CLASSCACHE_PROPPAGES| \
  22. CLASSCACHE_CONTEXTMENUS| \
  23. CLASSCACHE_ICONS| \
  24. CLASSCACHE_FRIENDLYNAME| \
  25. CLASSCACHE_TREATASLEAF| \
  26. CLASSCACHE_ATTRIBUTENAMES| \
  27. CLASSCACHE_CREATIONINFO)
  28. CRITICAL_SECTION g_csCache; // critical section for managing lifetime of the cache
  29. BOOL g_fClassCacheSorted = FALSE;
  30. HDPA g_hdpaClassCache = NULL;
  31. INT _CompareCacheEntry(LPVOID p1, LPVOID p2, LPARAM lParam);
  32. VOID _FreeCacheEntry(LPCLASSCACHEENTRY* ppCacheEntry);
  33. //
  34. // Cache fillers
  35. //
  36. HRESULT GetPropertyPageList(LPCLASSCACHEENTRY pCacheEntry, LPWSTR pPrefix, IADs* pDisplaySpecifier);
  37. VOID FreePropertyPageList(HDSA* pHDSA);
  38. HRESULT GetMenuHandlerList(LPCLASSCACHEENTRY pCacheEntry, LPWSTR pPrefix, IADs* pDisplaySpecifier);
  39. VOID FreeMenuHandlerList(HDSA* pHDSA);
  40. HRESULT GetIconList(LPCLASSCACHEENTRY pCacheEntry, IADs* pDisplaySpecifier);
  41. VOID FreeIconList(LPCLASSCACHEENTRY pCacheEntry);
  42. HRESULT GetAttributeNames(LPCLASSCACHEENTRY pCacheEntry, LPCLASSCACHEGETINFO pccgi, IADs* pDisplaySpecifier);
  43. INT CALLBACK _FreeAttributeNameCB(LPVOID p, LPVOID pData);
  44. VOID FreeAttributeNames(HDPA* pHDPA);
  45. //
  46. // Constant strings for the properties we expect
  47. //
  48. #define DISPLAY_SPECIFICATION L"displaySpecification"
  49. #define PROPERTY_PAGES L"propertyPages"
  50. #define CONTEXT_MENU L"contextMenu"
  51. #define ICON_LOCATION L"iconPath"
  52. #define FRIENDLY_NAME L"classDisplayName"
  53. #define ATTRIBUTE_NAMES L"attributeDisplayNames"
  54. #define TREAT_AS_LEAF L"treatAsLeaf"
  55. #define CREATION_DIALOG L"createDialog"
  56. #define CREATION_WIZARD L"creationWizard"
  57. #define CREATION_WIZARD_EXTN L"createWizardExt"
  58. //
  59. // property cache is used to store the property name (with optional server) and the ADsType.
  60. //
  61. CRITICAL_SECTION g_csPropCache;
  62. HDPA g_hdpaPropCache = NULL;
  63. typedef struct
  64. {
  65. LPWSTR pName; // property name (inc server if needed)
  66. ADSTYPE dwADsType; // attribute type
  67. } PROPCACHEENTRY, * LPPROPCACHEENTRY;
  68. INT _ComparePropCacheEntry(LPVOID p1, LPVOID p2, LPARAM lParam);
  69. VOID _FreePropCacheEntry(LPPROPCACHEENTRY *ppCacheEntry);
  70. HRESULT _GetDsSchemaMgmt(LPCLASSCACHEGETINFO pccgi, IDirectorySchemaMgmt **ppdsm);
  71. HRESULT _AddPropToPropCache(LPCLASSCACHEGETINFO pccgi, IDirectorySchemaMgmt *pdsm, LPCWSTR pAttributeName, ADSTYPE *padt);
  72. /*-----------------------------------------------------------------------------
  73. / _FreeCacheEntry
  74. / ---------------
  75. / Cache entries are stored as a LocalAlloc pointed to be the DPA. Here
  76. / we tidy up such allocations.
  77. /
  78. / In:
  79. / ppCacheEntry = pointer to block to be free'd
  80. /
  81. / Out:
  82. / VOID
  83. /----------------------------------------------------------------------------*/
  84. VOID _FreeCacheEntry(LPCLASSCACHEENTRY* ppCacheEntry)
  85. {
  86. LPCLASSCACHEENTRY pCacheEntry;
  87. USES_CONVERSION;
  88. TraceEnter(TRACE_CACHE, "_FreeCacheEntry");
  89. TraceAssert(ppCacheEntry);
  90. pCacheEntry = *ppCacheEntry;
  91. if (pCacheEntry)
  92. {
  93. Trace(TEXT("About to wait for multiple object for cache entry: %s"), W2T(pCacheEntry->pObjectClass));
  94. EnterCriticalSection(&pCacheEntry->csLock);
  95. LocalFreeStringW(&pCacheEntry->pKey);
  96. LocalFreeStringW(&pCacheEntry->pObjectClass);
  97. LocalFreeStringW(&pCacheEntry->pServer);
  98. LocalFreeStringW(&pCacheEntry->pFriendlyClassName);
  99. FreePropertyPageList(&pCacheEntry->hdsaPropertyPages);
  100. FreeMenuHandlerList(&pCacheEntry->hdsaMenuHandlers);
  101. FreeIconList(pCacheEntry);
  102. FreeAttributeNames(&pCacheEntry->hdpaAttributeNames);
  103. if (pCacheEntry->hdsaWizardExtn)
  104. DSA_Destroy(pCacheEntry->hdsaWizardExtn);
  105. LeaveCriticalSection(&pCacheEntry->csLock);
  106. DeleteCriticalSection(&pCacheEntry->csLock);
  107. LocalFree((HLOCAL)pCacheEntry);
  108. *ppCacheEntry = NULL;
  109. }
  110. TraceLeave();
  111. }
  112. /*-----------------------------------------------------------------------------
  113. / ClassCache_Init
  114. / ---------------
  115. / Initialize the cache objects we are going to use, mostly the syncronization
  116. / things that we need.
  117. /
  118. / In:
  119. / Out:
  120. / -
  121. /----------------------------------------------------------------------------*/
  122. VOID ClassCache_Init(VOID)
  123. {
  124. TraceEnter(TRACE_CACHE, "ClassCache_Init");
  125. InitializeCriticalSection(&g_csCache);
  126. InitializeCriticalSection(&g_csPropCache);
  127. TraceLeave();
  128. }
  129. /*-----------------------------------------------------------------------------
  130. / ClassCache_GetClassInfo
  131. / -----------------------
  132. / Query cache code which selectivitly caches information based
  133. / on the given object and the flags.
  134. /
  135. / In:
  136. / pGetInfo -> structure containing parameters for object
  137. / pPath = ADS path for the object we are tyring to cache on
  138. / pObjectClass = objectClass to key the cache entry on
  139. / pAttributePrefix = prefix used when querying for properties (also used in cache key)
  140. / dwFlags = flags indicating which cache fields are required
  141. /
  142. / ppCacheEntry -> receieves pointer to the cache entry
  143. /
  144. / Out:
  145. / HRESULT
  146. /----------------------------------------------------------------------------*/
  147. HRESULT CALLBACK _AddWizardExtnGUID(DWORD dwIndex, BSTR pString, LPVOID pData)
  148. {
  149. HRESULT hr;
  150. HDSA hdsa = (HDSA)pData;
  151. GUID guid;
  152. USES_CONVERSION;
  153. TraceEnter(TRACE_CACHE, "_AddWizardExtnGUID");
  154. Trace(TEXT("dwIndex %08x, pString: %s"), dwIndex, W2T(pString));
  155. if (GetGUIDFromStringW(pString, &guid))
  156. {
  157. if (-1 == DSA_AppendItem(hdsa, &guid))
  158. ExitGracefully(hr, E_FAIL, "Failed to add wizard GUID");
  159. }
  160. hr = S_OK;
  161. exit_gracefully:
  162. TraceLeaveResult(hr);
  163. }
  164. INT _CompareCacheEntryCB(LPVOID p1, LPVOID p2, LPARAM lParam)
  165. {
  166. INT iResult = -1;
  167. LPCLASSCACHEENTRY pEntry1 = (LPCLASSCACHEENTRY)p1;
  168. LPCLASSCACHEENTRY pEntry2 = (LPCLASSCACHEENTRY)p2;
  169. if (pEntry1 && pEntry2)
  170. iResult = StrCmpIW(pEntry1->pKey, pEntry2->pKey);
  171. return iResult;
  172. }
  173. HRESULT ClassCache_GetClassInfo(LPCLASSCACHEGETINFO pInfo, LPCLASSCACHEENTRY* ppCacheEntry)
  174. {
  175. HRESULT hr;
  176. LPCLASSCACHEENTRY pCacheEntry = NULL;
  177. WCHAR szClassKey[MAX_PATH*2];
  178. INT index;
  179. IADs* pDisplaySpecifier = NULL;
  180. IADs* pDsObject = NULL;
  181. IADsClass* pDsClass = NULL;
  182. BSTR bstrSchemaObject = NULL;
  183. HICON hSmallIcon = NULL;
  184. HICON hLargeIcon = NULL;
  185. VARIANT variant;
  186. VARIANT_BOOL vbIsContainer;
  187. DWORD dwFlags;
  188. DWORD dwWaitRes;
  189. USES_CONVERSION;
  190. TraceEnter(TRACE_CACHE, "ClassCache_GetClassInfo");
  191. if (!pInfo || !pInfo->pObjectClass || !ppCacheEntry)
  192. ExitGracefully(hr, E_FAIL, "Bad parameters for ClassCache_GetClassInfo");
  193. dwFlags = pInfo->dwFlags;
  194. // Build the key string, this is "className[:attributePrefix]" that way the shell and the
  195. // admin tools can share the same cache structure
  196. VariantInit(&variant);
  197. StrCpyW(szClassKey, pInfo->pObjectClass);
  198. if (pInfo->pAttributePrefix)
  199. {
  200. StrCatW(szClassKey, L":");
  201. StrCatW(szClassKey, pInfo->pAttributePrefix);
  202. if (dwFlags & ALL_PREFIXED_ATTRIBUTES)
  203. dwFlags |= ALL_PREFIXED_ATTRIBUTES;
  204. }
  205. else
  206. {
  207. if (dwFlags & ALL_NONPREFIXED_ATTRIBUTES)
  208. dwFlags |= ALL_NONPREFIXED_ATTRIBUTES;
  209. }
  210. // add the server name to the class key
  211. if (pInfo->pServer)
  212. {
  213. StrCatW(szClassKey, L":");
  214. StrCatW(szClassKey, pInfo->pServer);
  215. }
  216. Trace(TEXT("Cache key is: %s"), W2T(szClassKey));
  217. // do we have a cache? if so then look in there to see if we have
  218. // already cached information about this class
  219. Trace(TEXT("About to wait for global cache lock when getting cache entry: %s"), W2T(pInfo->pObjectClass));
  220. EnterCriticalSection(&g_csCache);
  221. TraceMsg("Global cache lock aquired, so can now modify cache content");
  222. if (g_hdpaClassCache)
  223. {
  224. // sort it if its not already sorted, then do a sorted search for the
  225. // best performance so we can pick up the information.
  226. if (!g_fClassCacheSorted)
  227. {
  228. TraceMsg("!!! Cache not sorted, just about to call DPA_Sort !!!");
  229. DPA_Sort(g_hdpaClassCache, _CompareCacheEntryCB, NULL);
  230. g_fClassCacheSorted = TRUE;
  231. }
  232. CLASSCACHEENTRY cce;
  233. cce.pKey = szClassKey;
  234. Trace(TEXT("Searching the cache for entry %s"), W2T(szClassKey));
  235. index = DPA_Search(g_hdpaClassCache, &cce, 0, _CompareCacheEntryCB, NULL, DPAS_SORTED);
  236. if (index >= 0)
  237. {
  238. Trace(TEXT("Cache hit at location %d"), index);
  239. pCacheEntry = (LPCLASSCACHEENTRY)DPA_FastGetPtr(g_hdpaClassCache, index);
  240. Trace(TEXT("About to wait on cache entry for: %s"), W2T(pCacheEntry->pObjectClass));
  241. EnterCriticalSection(&pCacheEntry->csLock);
  242. TraceMsg("Got lock on cache entry");
  243. }
  244. }
  245. else
  246. {
  247. g_hdpaClassCache = DPA_Create(4); // create the new cache
  248. if (!g_hdpaClassCache)
  249. {
  250. LeaveCriticalSection(&g_csCache);
  251. ExitGracefully(hr, E_OUTOFMEMORY, "Failed to cache object info");
  252. }
  253. }
  254. // pCacheEntry == NULL if we haven't hit anything yet, therefore lets
  255. // create a new entry if that happens, or fall through!
  256. if (!pCacheEntry)
  257. {
  258. // allocate a new entry, initialize it and put it into the DSA, having done
  259. // this we can search it, fill in the gaps etc.
  260. pCacheEntry = (LPCLASSCACHEENTRY)LocalAlloc(LPTR, SIZEOF(CLASSCACHEENTRY));
  261. if (!pCacheEntry)
  262. {
  263. LeaveCriticalSection(&g_csCache);
  264. ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate new cache structure");
  265. }
  266. InitializeCriticalSection(&pCacheEntry->csLock);
  267. EnterCriticalSection(&pCacheEntry->csLock); // enter it, we need it locked
  268. // pCacheEntry->pKey = NULL;
  269. // pCacheEntry->dwFlags = 0x0;
  270. // pCacheEntry->dwCached = 0x0;
  271. // pCacheEntry->fHasWizardDailogCLSID = FALSE;
  272. // pCacheEntry->fHasWizardPrimaryPageCLSID = FALSE;
  273. // pCacheEntry->pObjectClass = NULL;
  274. // pCacheEntry->pServer = NULL;
  275. // pCacheEntry->pFriendlyClassName = NULL;
  276. // pCacheEntry->hdsaPropertyPages = NULL;
  277. // pCacheEntry->hdsaMenuHandlers = NULL;
  278. // ZeroMemory(pCacheEntry->pIconName, SIZEOF(pCacheEntry->pIconName));
  279. // ZeroMemory(pCacheEntry->iImage, SIZEOF(pCacheEntry->iImage));
  280. // pCacheEntry->fIsContainer = FALSE;
  281. // pCacheEntry->hdpaAttributeNames = NULL;
  282. // pCacheEntry->clsidWizardDialog = { 0 };
  283. // pCacheEntry->clsidWizardPrimary = { 0 };
  284. // pCacheEntry->hdsaWizardExtn = NULL;
  285. hr = LocalAllocStringW(&pCacheEntry->pKey, szClassKey);
  286. if (SUCCEEDED(hr))
  287. hr = LocalAllocStringW(&pCacheEntry->pObjectClass, pInfo->pObjectClass);
  288. if (SUCCEEDED(hr) && pInfo->pServer)
  289. hr = LocalAllocStringW(&pCacheEntry->pServer, pInfo->pServer);
  290. if (FAILED(hr) || (-1 == DPA_AppendPtr(g_hdpaClassCache, pCacheEntry)))
  291. {
  292. LeaveCriticalSection(&g_csCache);
  293. LeaveCriticalSection(&pCacheEntry->csLock);
  294. _FreeCacheEntry(&pCacheEntry);
  295. ExitGracefully(hr, E_UNEXPECTED, "Failed to add cache entry to DPA");
  296. }
  297. g_fClassCacheSorted = FALSE;
  298. }
  299. LeaveCriticalSection(&g_csCache);
  300. // ensure we have a display specifier if we need one, that boils down to be
  301. // dwFlags expresses fields we are interested in, so do we have those in
  302. // the cache record, if not then lets check to see if those bits match
  303. // ones which come from the specifier, if so then we better grab one.
  304. if (dwFlags & ALL_DISPLAY_SPEC_VALUES)
  305. {
  306. if ((pCacheEntry->dwFlags & dwFlags) != dwFlags)
  307. {
  308. Trace(TEXT("Binding to the display specifier %08x,%08x"), pCacheEntry->dwFlags & dwFlags, dwFlags);
  309. if (FAILED(GetDisplaySpecifier(pInfo, IID_IADs, (LPVOID*)&pDisplaySpecifier)))
  310. {
  311. TraceMsg("Failed to bind to display specifier, pDisplaySpecifier == NULL");
  312. TraceAssert(pDisplaySpecifier == NULL);
  313. // ensure that we don't try and cache display specifier information and
  314. // we mark the cache record as dirty.
  315. dwFlags &= ~(ALL_DISPLAY_SPEC_VALUES & ~CLASSCACHE_FRIENDLYNAME);
  316. }
  317. }
  318. }
  319. // container flag for the objects
  320. if (dwFlags & CLASSCACHE_CONTAINER)
  321. {
  322. if (!(pCacheEntry->dwFlags & CLASSCACHE_CONTAINER))
  323. {
  324. if (pInfo->pPath)
  325. {
  326. TraceMsg("!!! Binding to the object to get container flags !!!");
  327. if (SUCCEEDED(ADsOpenObject(pInfo->pPath,
  328. pInfo->pUserName, pInfo->pPassword,
  329. (pInfo->dwFlags & CLASSCACHE_SIMPLEAUTHENTICATE) ? 0:ADS_SECURE_AUTHENTICATION,
  330. IID_IADs, (LPVOID*)&pDsObject)))
  331. {
  332. // Try to deterimine if the object is a container by binding to the
  333. // schema object and getting its container property.
  334. hr = pDsObject->get_Schema(&bstrSchemaObject);
  335. FailGracefully(hr, "Failed to get the objects schema");
  336. Trace(TEXT("Path to schema object is %s"), W2T(bstrSchemaObject));
  337. if (SUCCEEDED(ADsOpenObject(bstrSchemaObject,
  338. pInfo->pUserName, pInfo->pPassword,
  339. (pInfo->dwFlags & CLASSCACHE_SIMPLEAUTHENTICATE) ? 0:ADS_SECURE_AUTHENTICATION,
  340. IID_IADsClass, (LPVOID*)&pDsClass)))
  341. {
  342. if (SUCCEEDED(pDsClass->get_Container(&vbIsContainer)))
  343. {
  344. TraceMsg("Cached container flag");
  345. pCacheEntry->fIsContainer = (vbIsContainer == -1);
  346. pCacheEntry->dwCached |= CLASSCACHE_CONTAINER;
  347. }
  348. }
  349. }
  350. }
  351. else
  352. {
  353. TraceMsg("**** No ADsPath, cannot get container flag from schema ****");
  354. }
  355. }
  356. }
  357. // all the following attributes require a pDisplaySpecifier
  358. if (pDisplaySpecifier)
  359. {
  360. // property pages?
  361. if (dwFlags & CLASSCACHE_PROPPAGES)
  362. {
  363. if (!(pCacheEntry->dwFlags & CLASSCACHE_PROPPAGES))
  364. {
  365. TraceMsg("Caching property page list");
  366. if (SUCCEEDED(GetPropertyPageList(pCacheEntry, pInfo->pAttributePrefix, pDisplaySpecifier)))
  367. {
  368. TraceMsg("Fetching property page list");
  369. pCacheEntry->dwCached |= CLASSCACHE_PROPPAGES;
  370. }
  371. }
  372. }
  373. // context menu handlers?
  374. if (dwFlags & CLASSCACHE_CONTEXTMENUS)
  375. {
  376. if (!(pCacheEntry->dwFlags & CLASSCACHE_CONTEXTMENUS))
  377. {
  378. TraceMsg("Caching menu handler list");
  379. if (SUCCEEDED(GetMenuHandlerList(pCacheEntry, pInfo->pAttributePrefix, pDisplaySpecifier)))
  380. {
  381. TraceMsg("Fetched context menu list");
  382. pCacheEntry->dwCached |= CLASSCACHE_CONTEXTMENUS;
  383. }
  384. }
  385. }
  386. // icon location?
  387. if (dwFlags & CLASSCACHE_ICONS)
  388. {
  389. if (!(pCacheEntry->dwFlags & CLASSCACHE_ICONS))
  390. {
  391. TraceMsg("Caching icon list");
  392. if (SUCCEEDED(GetIconList(pCacheEntry, pDisplaySpecifier)))
  393. {
  394. TraceMsg("Fetched icon list");
  395. pCacheEntry->dwCached |= CLASSCACHE_ICONS;
  396. }
  397. }
  398. }
  399. // attribute name caching?
  400. if (dwFlags & CLASSCACHE_ATTRIBUTENAMES)
  401. {
  402. if (!(pCacheEntry->dwFlags & CLASSCACHE_ATTRIBUTENAMES))
  403. {
  404. TraceMsg("Caching attribute list");
  405. if (SUCCEEDED(GetAttributeNames(pCacheEntry, pInfo, pDisplaySpecifier)))
  406. {
  407. TraceMsg("Fetched attribute names");
  408. pCacheEntry->dwCached |= CLASSCACHE_ATTRIBUTENAMES;
  409. }
  410. }
  411. }
  412. // get the treat as leaf
  413. if (dwFlags & CLASSCACHE_TREATASLEAF)
  414. {
  415. if (!(pCacheEntry->dwFlags & CLASSCACHE_TREATASLEAF))
  416. {
  417. TraceMsg("Caching the treat as leaf flag");
  418. // pick up the "treatAsLeaf" attribute from the display specifier, if
  419. // this is undefined then use the normal container flag from the
  420. // schema.
  421. VariantClear(&variant);
  422. if (SUCCEEDED(pDisplaySpecifier->Get(TREAT_AS_LEAF, &variant)) && (V_VT(&variant) == VT_BOOL))
  423. {
  424. TraceMsg("Caching fTreatAsLeaf");
  425. pCacheEntry->fTreatAsLeaf = V_BOOL(&variant) == 1;
  426. pCacheEntry->dwCached |= CLASSCACHE_TREATASLEAF;
  427. }
  428. }
  429. }
  430. // get the CLSID that implements the creation dialog
  431. if (dwFlags & CLASSCACHE_WIZARDDIALOG)
  432. {
  433. if (!(pCacheEntry->dwFlags & CLASSCACHE_WIZARDDIALOG))
  434. {
  435. TraceMsg("Caching the creation wizard dialog CLSID");
  436. VariantClear(&variant);
  437. if (SUCCEEDED(pDisplaySpecifier->Get(CREATION_DIALOG, &variant)))
  438. {
  439. if (V_VT(&variant) == VT_BSTR)
  440. {
  441. if (GetGUIDFromStringW(V_BSTR(&variant), &pCacheEntry->clsidWizardDialog))
  442. {
  443. TraceGUID("CLSID of wizard dialog: ", pCacheEntry->clsidWizardDialog);
  444. pCacheEntry->dwCached |= CLASSCACHE_WIZARDDIALOG;
  445. }
  446. else
  447. {
  448. Trace(TEXT("GUID string failed to parse: %s"), W2T(V_BSTR(&variant)));
  449. }
  450. }
  451. }
  452. }
  453. }
  454. // get the CLSID that implements the primary pages of the wizard
  455. if (dwFlags & CLASSCACHE_WIZARDPRIMARYPAGE)
  456. {
  457. if (!(pCacheEntry->dwFlags & CLASSCACHE_WIZARDPRIMARYPAGE))
  458. {
  459. TraceMsg("Caching the creation wizard's primary page");
  460. VariantClear(&variant);
  461. if (SUCCEEDED(pDisplaySpecifier->Get(CREATION_WIZARD, &variant)))
  462. {
  463. if (V_VT(&variant) == VT_BSTR)
  464. {
  465. if (GetGUIDFromStringW(V_BSTR(&variant), &pCacheEntry->clsidWizardPrimaryPage))
  466. {
  467. TraceGUID("CLSID of primary pages: ", pCacheEntry->clsidWizardPrimaryPage);
  468. pCacheEntry->dwCached |= CLASSCACHE_WIZARDPRIMARYPAGE;
  469. }
  470. else
  471. {
  472. Trace(TEXT("GUID string failed to parse: %s"), W2T(V_BSTR(&variant)));
  473. }
  474. }
  475. }
  476. }
  477. }
  478. // get the CLSID of the extensions for the wizard
  479. if (dwFlags & CLASSCACHE_WIZARDEXTN)
  480. {
  481. if (!(pCacheEntry->dwFlags & CLASSCACHE_WIZARDEXTN))
  482. {
  483. TraceMsg("Caching the list of extension pages for the wizard");
  484. VariantClear(&variant);
  485. if (SUCCEEDED(pDisplaySpecifier->Get(CREATION_WIZARD_EXTN, &variant)))
  486. {
  487. if (!pCacheEntry->hdsaWizardExtn)
  488. {
  489. TraceMsg("Creating DSA to store GUIDs in");
  490. pCacheEntry->hdsaWizardExtn = DSA_Create(SIZEOF(GUID), 4);
  491. TraceAssert(pCacheEntry->hdsaWizardExtn);
  492. }
  493. if (pCacheEntry->hdsaWizardExtn)
  494. {
  495. TraceMsg("Attempting to cache extention GUIDs into the DPA");
  496. GetArrayContents(&variant, _AddWizardExtnGUID, (LPVOID)pCacheEntry->hdsaWizardExtn);
  497. }
  498. }
  499. }
  500. }
  501. }
  502. // friendly class anme for the object
  503. if (dwFlags & CLASSCACHE_FRIENDLYNAME)
  504. {
  505. if (!(pCacheEntry->dwFlags & CLASSCACHE_FRIENDLYNAME))
  506. {
  507. TraceMsg("Checking for the friendly class name");
  508. VariantClear(&variant);
  509. // if there is a display specifier and a friendly name then lets
  510. // pick it up and store it in the cache entry.
  511. if (pDisplaySpecifier)
  512. {
  513. if (SUCCEEDED(pDisplaySpecifier->Get(FRIENDLY_NAME, &variant)))
  514. {
  515. if (V_VT(&variant) == VT_BSTR)
  516. {
  517. Trace(TEXT("Friendly name: %s"), W2T(V_BSTR(&variant)));
  518. hr = LocalAllocStringW(&pCacheEntry->pFriendlyClassName, V_BSTR(&variant));
  519. FailGracefully(hr, "Failed to copy the friendly name");
  520. pCacheEntry->dwCached |= CLASSCACHE_FRIENDLYNAME;
  521. }
  522. }
  523. }
  524. // the friendly name is a special case, if we haven't been able to get the display
  525. // specifier or the friendly name from it then populate the cache with the
  526. // existing class name to avoid hitting the wire repeatedly.
  527. if (!(pCacheEntry->dwCached & CLASSCACHE_FRIENDLYNAME))
  528. {
  529. TraceMsg("Defaulting to un-friendly class name");
  530. hr = LocalAllocStringW(&pCacheEntry->pFriendlyClassName, pCacheEntry->pObjectClass);
  531. FailGracefully(hr, "Failed to allocate friendly class name");
  532. }
  533. pCacheEntry->dwCached |= CLASSCACHE_FRIENDLYNAME;
  534. }
  535. }
  536. hr = S_OK; // success!
  537. exit_gracefully:
  538. DoRelease(pDisplaySpecifier);
  539. DoRelease(pDsObject);
  540. DoRelease(pDsClass);
  541. VariantClear(&variant);
  542. SysFreeString(bstrSchemaObject);
  543. if (hSmallIcon)
  544. DestroyIcon(hSmallIcon);
  545. if (hLargeIcon)
  546. DestroyIcon(hLargeIcon);
  547. if (pCacheEntry)
  548. {
  549. // make the attributes as cached now, and if we succeeded then we
  550. // can pass out the locked cache entry, otherwise we must
  551. // unlock it - otherwise others will not be ableto updated!
  552. pCacheEntry->dwFlags |= dwFlags;
  553. if (SUCCEEDED(hr))
  554. {
  555. *ppCacheEntry = pCacheEntry;
  556. }
  557. else
  558. {
  559. LeaveCriticalSection(&pCacheEntry->csLock);
  560. }
  561. }
  562. TraceLeaveResult(hr);
  563. }
  564. /*-----------------------------------------------------------------------------
  565. / ClassCache_ReleaseClassInfo
  566. / ---------------------------
  567. / Each cache entry has a lock, this releases the lock. If the lock is
  568. / non-zero then the record cannot be updated, or released.
  569. /
  570. / In:
  571. / ppCacheEntry -> cache entry, NULL'd on exit.
  572. /
  573. / Out:
  574. / VOID
  575. /----------------------------------------------------------------------------*/
  576. VOID ClassCache_ReleaseClassInfo(LPCLASSCACHEENTRY* ppCacheEntry)
  577. {
  578. TraceEnter(TRACE_CACHE, "ClassCache_ReleaseClassInfo");
  579. if (ppCacheEntry)
  580. {
  581. LPCLASSCACHEENTRY pCacheEntry = *ppCacheEntry;
  582. if (pCacheEntry)
  583. {
  584. TraceMsg("Releasing critical section on cache record");
  585. LeaveCriticalSection(&pCacheEntry->csLock);
  586. *ppCacheEntry = NULL;
  587. }
  588. }
  589. TraceLeave();
  590. }
  591. /*-----------------------------------------------------------------------------
  592. / ClassCache_Discard
  593. / ------------------
  594. / Discard the cached information we have for the DS classes we have
  595. / seen (including the cache DPA & the image lists)
  596. /
  597. / In:
  598. / -
  599. / Out:
  600. / VOID
  601. /----------------------------------------------------------------------------*/
  602. INT _FreePropCacheEntryCB(LPVOID pVoid, LPVOID pData)
  603. {
  604. LPPROPCACHEENTRY pCacheEntry = (LPPROPCACHEENTRY)pVoid;
  605. TraceEnter(TRACE_CACHE, "_FreePropCacheEntryCB");
  606. _FreePropCacheEntry(&pCacheEntry);
  607. TraceLeaveValue(TRUE);
  608. }
  609. INT _FreeCacheEntryCB(LPVOID pVoid, LPVOID pData)
  610. {
  611. LPCLASSCACHEENTRY pCacheEntry = (LPCLASSCACHEENTRY)pVoid;
  612. TraceEnter(TRACE_CACHE, "_FreeCacheEntryCB");
  613. _FreeCacheEntry(&pCacheEntry);
  614. TraceLeaveValue(TRUE);
  615. }
  616. VOID ClassCache_Discard(VOID)
  617. {
  618. HRESULT hr;
  619. DWORD dwWaitRes;
  620. TraceEnter(TRACE_CACHE, "ClassCache_Discard");
  621. // avoid destroying the cache whilst its being updated, ths is a simple
  622. // mutex.
  623. TraceMsg("About to wait for global cache lock");
  624. EnterCriticalSection(&g_csCache);
  625. TraceMsg("Global cache lock aquired, so can now modify cache content");
  626. if (g_hdpaClassCache)
  627. {
  628. DPA_DestroyCallback(g_hdpaClassCache, _FreeCacheEntryCB, NULL);
  629. g_hdpaClassCache = NULL;
  630. }
  631. // the property cache is protected also, so wait until we can get the
  632. // lock on it before partying on the structure.
  633. TraceMsg("About to wait for global property cache lock");
  634. EnterCriticalSection(&g_csPropCache);
  635. TraceMsg("Global property cache lock aquired, so can now modify cache content");
  636. if (g_hdpaPropCache)
  637. {
  638. DPA_DestroyCallback(g_hdpaPropCache, _FreePropCacheEntryCB, NULL);
  639. g_hdpaPropCache = NULL;
  640. }
  641. LeaveCriticalSection(&g_csCache);
  642. DeleteCriticalSection(&g_csCache);
  643. LeaveCriticalSection(&g_csPropCache);
  644. DeleteCriticalSection(&g_csPropCache);
  645. TraceLeave();
  646. }
  647. /*-----------------------------------------------------------------------------
  648. / Property page list
  649. /----------------------------------------------------------------------------*/
  650. /*-----------------------------------------------------------------------------
  651. / GetPropertyPageList
  652. / -------------------
  653. / Build the list of property pages that we are going to be displaying
  654. / the code builds the list from the display specifier lists.
  655. /
  656. / In:
  657. / pCacheEntry -> Cache entry to update
  658. / pAttributePrefix -> suitable prefix for getting Admin/Shell pages
  659. / pDataObject -> IDataObject for getting cached information from
  660. /
  661. / Out:
  662. / HRESULT
  663. /----------------------------------------------------------------------------*/
  664. HRESULT CALLBACK _AddPropertyPageItemCB(DWORD dwIndex, BSTR pString, LPVOID pData)
  665. {
  666. HRESULT hr;
  667. DSPROPERTYPAGE item;
  668. HDSA hdsa = (HDSA)pData;
  669. USES_CONVERSION;
  670. TraceEnter(TRACE_CACHE, "_AddPropertyPageItemCB");
  671. Trace(TEXT("dwIndex %08x, pString: %s"), dwIndex, W2T(pString));
  672. hr = LocalAllocStringW(&item.pPageReference, pString);
  673. FailGracefully(hr, "Failed to clone string");
  674. if (-1 == DSA_AppendItem(hdsa, &item))
  675. ExitGracefully(hr, E_FAIL, "Failed to property page reference to DSA");
  676. hr = S_OK;
  677. exit_gracefully:
  678. if (FAILED(hr))
  679. LocalFreeStringW(&item.pPageReference);
  680. TraceLeaveResult(hr);
  681. }
  682. HRESULT GetPropertyPageList(LPCLASSCACHEENTRY pCacheEntry, LPWSTR pAttributePrefix, IADs* pDisplaySpecifier)
  683. {
  684. HRESULT hr;
  685. VARIANT variant;
  686. WCHAR szProperty[MAX_PATH] = { TEXT('\0') };
  687. USES_CONVERSION;
  688. INT i;
  689. TraceEnter(TRACE_CACHE, "GetPropertyPageList");
  690. VariantInit(&variant);
  691. pCacheEntry->hdsaPropertyPages = DSA_Create(SIZEOF(DSPROPERTYPAGE), 4);
  692. TraceAssert(pCacheEntry->hdsaPropertyPages);
  693. if (!pCacheEntry->hdsaPropertyPages)
  694. ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate page DPA");
  695. // build the property we are going to key off and then lets start
  696. // to walk the list of diplay specifiers checking each one for
  697. // a list of property pages.
  698. if (pAttributePrefix)
  699. StrCatW(szProperty, pAttributePrefix);
  700. StrCatW(szProperty, PROPERTY_PAGES);
  701. Trace(TEXT("Enumerating property pages from: %s"), W2T(szProperty));
  702. if (SUCCEEDED(pDisplaySpecifier->Get(szProperty, &variant)))
  703. {
  704. hr = GetArrayContents(&variant, _AddPropertyPageItemCB, (LPVOID)pCacheEntry->hdsaPropertyPages);
  705. FailGracefully(hr, "Failed to add property pages to DSA");
  706. VariantClear(&variant);
  707. }
  708. if (SUCCEEDED(pDisplaySpecifier->Get(PROPERTY_PAGES, &variant)))
  709. {
  710. hr = GetArrayContents(&variant, _AddPropertyPageItemCB, (LPVOID)pCacheEntry->hdsaPropertyPages);
  711. FailGracefully(hr, "Failed to add property pages to DSA");
  712. VariantClear(&variant);
  713. }
  714. hr = S_OK;
  715. exit_gracefully:
  716. if (FAILED(hr))
  717. FreePropertyPageList(&pCacheEntry->hdsaPropertyPages);
  718. VariantClear(&variant);
  719. TraceLeaveResult(hr);
  720. }
  721. /*-----------------------------------------------------------------------------
  722. / FreePropertyPageList
  723. / --------------------
  724. / Free the property page list associated with a particular cache entry
  725. /
  726. / In:
  727. / pHDSA = pointer to a HDSA to be free'd, and NULL'd
  728. /
  729. / Out:
  730. / HRESULT
  731. /----------------------------------------------------------------------------*/
  732. INT _FreePropertyPageItemCB(LPVOID p, LPVOID pData)
  733. {
  734. LPDSPROPERTYPAGE pItem = (LPDSPROPERTYPAGE)p;
  735. TraceEnter(TRACE_CACHE, "_FreePropertyPageItemCB");
  736. TraceAssert(pItem);
  737. LocalFreeStringW(&pItem->pPageReference);
  738. TraceLeaveValue(1);
  739. }
  740. VOID FreePropertyPageList(HDSA* pHDSA)
  741. {
  742. TraceEnter(TRACE_CACHE, "FreePropertyPageList");
  743. if (*pHDSA)
  744. DSA_DestroyCallback(*pHDSA, _FreePropertyPageItemCB, 0L);
  745. *pHDSA = NULL;
  746. TraceLeave();
  747. }
  748. /*-----------------------------------------------------------------------------
  749. / Menu item lists
  750. /----------------------------------------------------------------------------*/
  751. /*-----------------------------------------------------------------------------
  752. / GetMenuHandlerList
  753. / ------------------
  754. / The "contextMenu" property on a DS object contains a list of
  755. / the menu handlers that we want to interact with.
  756. /
  757. / In:
  758. / pCacheEntry -> Cache entry to update
  759. / pAttributePrefix -> suitable prefix for getting Admin/Shell pages
  760. / pDataObject -> IDataObject for getting cached information from
  761. /
  762. / Out:
  763. / HRESULT
  764. /----------------------------------------------------------------------------*/
  765. HRESULT CALLBACK _AddMenuHandlerCB(DWORD dwIndex, BSTR pString, LPVOID pData)
  766. {
  767. HRESULT hr;
  768. DSMENUHANDLER item;
  769. HDSA hdsa = (HDSA)pData;
  770. USES_CONVERSION;
  771. TraceEnter(TRACE_CACHE, "_AddMenuHandlerCB");
  772. Trace(TEXT("dwIndex %08x, pString: %s"), dwIndex, W2T(pString));
  773. hr = LocalAllocStringW(&item.pMenuReference, pString);
  774. FailGracefully(hr, "Failed to clone string");
  775. if (-1 == DSA_AppendItem(hdsa, &item))
  776. ExitGracefully(hr, E_FAIL, "Failed to add menu reference to DSA");
  777. hr = S_OK;
  778. exit_gracefully:
  779. if (FAILED(hr))
  780. LocalFreeStringW(&item.pMenuReference);
  781. TraceLeaveResult(hr);
  782. }
  783. HRESULT GetMenuHandlerList(LPCLASSCACHEENTRY pCacheEntry, LPWSTR pAttributePrefix, IADs* pDisplaySpecifier)
  784. {
  785. HRESULT hr;
  786. WCHAR szProperty[MAX_PATH] = { TEXT('\0') };
  787. VARIANT variant;
  788. INT i;
  789. TraceEnter(TRACE_CACHE, "GetMenuHandlerList");
  790. VariantInit(&variant);
  791. pCacheEntry->hdsaMenuHandlers = DSA_Create(SIZEOF(DSPROPERTYPAGE), 4);
  792. TraceAssert(pCacheEntry->hdsaMenuHandlers);
  793. if (!pCacheEntry->hdsaMenuHandlers)
  794. ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate page DPA");
  795. // first try "<attributePrefix>ContextMenu" to pick up the provider specific menus
  796. if (pAttributePrefix)
  797. StrCatW(szProperty, pAttributePrefix);
  798. StrCatW(szProperty, CONTEXT_MENU);
  799. if (SUCCEEDED(pDisplaySpecifier->Get(szProperty, &variant)))
  800. {
  801. hr = GetArrayContents(&variant, _AddMenuHandlerCB, (LPVOID)pCacheEntry->hdsaMenuHandlers);
  802. FailGracefully(hr, "Failed to add property pages to DSA");
  803. VariantClear(&variant);
  804. }
  805. if (SUCCEEDED(pDisplaySpecifier->Get(CONTEXT_MENU, &variant)))
  806. {
  807. hr = GetArrayContents(&variant, _AddMenuHandlerCB, (LPVOID)pCacheEntry->hdsaMenuHandlers);
  808. FailGracefully(hr, "Failed to add property pages to DSA");
  809. VariantClear(&variant);
  810. }
  811. hr = S_OK; // success
  812. exit_gracefully:
  813. if (FAILED(hr))
  814. FreeMenuHandlerList(&pCacheEntry->hdsaMenuHandlers);
  815. VariantClear(&variant);
  816. TraceLeaveResult(hr);
  817. }
  818. /*-----------------------------------------------------------------------------
  819. / FreeMenuHandlerList
  820. / ------------=------
  821. / Free the list of menu items that are stored in the cache DSA.
  822. /
  823. / In:
  824. / pHDSA = pointer to a HDSA to be free'd, and NULL'd
  825. /
  826. / Out:
  827. / HRESULT
  828. /----------------------------------------------------------------------------*/
  829. INT _FreeMenuHandlerCB(LPVOID p, LPVOID pData)
  830. {
  831. LPDSMENUHANDLER pItem = (LPDSMENUHANDLER)p;
  832. TraceEnter(TRACE_CACHE, "_FreeMenuHandlerCB");
  833. TraceAssert(pItem);
  834. LocalFreeStringW(&pItem->pMenuReference);
  835. TraceLeaveValue(1);
  836. }
  837. VOID FreeMenuHandlerList(HDSA* pHDSA)
  838. {
  839. TraceEnter(TRACE_CACHE, "FreeMenuHandlerList");
  840. if (*pHDSA)
  841. DSA_DestroyCallback(*pHDSA, _FreeMenuHandlerCB, 0L);
  842. *pHDSA = NULL;
  843. TraceLeave();
  844. }
  845. /*-----------------------------------------------------------------------------
  846. / Property page list
  847. /----------------------------------------------------------------------------*/
  848. /*-----------------------------------------------------------------------------
  849. / GetIconList
  850. / -----------
  851. / Get the icon list from the class specifier. Bind to the class specifier
  852. / and then enumerate the icon property. We store an array which contains
  853. / the icon locations for multiple states, therefore as we are called to
  854. / add the entries we clear out that previous index.
  855. /
  856. / In:
  857. / pCacheEntry -> Cache entry to update
  858. / pDataObject -> pData object for extra information
  859. /
  860. / Out:
  861. / HRESULT
  862. /----------------------------------------------------------------------------*/
  863. HRESULT CALLBACK _AddIconToCacheEntryCB(DWORD dwIndex, BSTR pString, LPVOID pData)
  864. {
  865. HRESULT hr;
  866. LPCLASSCACHEENTRY pCacheEntry = (LPCLASSCACHEENTRY)pData;
  867. USES_CONVERSION;
  868. TraceEnter(TRACE_CACHE, "_AddIconToCacheEntryCB");
  869. Trace(TEXT("dwIndex %08x, pString: %s"), dwIndex, W2T(pString));
  870. if (dwIndex < ARRAYSIZE(pCacheEntry->pIconName))
  871. {
  872. LocalFreeStringW(&pCacheEntry->pIconName[dwIndex]);
  873. hr = LocalAllocStringW(&pCacheEntry->pIconName[dwIndex], pString);
  874. FailGracefully(hr, "Failed to copy icon location");
  875. }
  876. hr = S_OK;
  877. exit_gracefully:
  878. TraceLeaveResult(hr);
  879. }
  880. HRESULT GetIconList(LPCLASSCACHEENTRY pCacheEntry, IADs* pDisplaySpecifier)
  881. {
  882. HRESULT hr;
  883. VARIANT variant;
  884. USES_CONVERSION;
  885. TraceEnter(TRACE_CACHE, "GetIconList");
  886. VariantInit(&variant);
  887. if (SUCCEEDED(pDisplaySpecifier->Get(ICON_LOCATION, &variant)))
  888. {
  889. hr = GetArrayContents(&variant, _AddIconToCacheEntryCB, (LPVOID)pCacheEntry);
  890. FailGracefully(hr, "Failed to get the icon list into the cache entry");
  891. }
  892. hr = S_OK; // success
  893. exit_gracefully:
  894. if (FAILED(hr))
  895. FreeIconList(pCacheEntry);
  896. VariantClear(&variant);
  897. TraceLeaveResult(hr);
  898. }
  899. /*-----------------------------------------------------------------------------
  900. / FreeIconList
  901. / ------------
  902. / Clear out the icon list, this an array of string pointers allocated by
  903. / LocalAllocString.
  904. /
  905. / In:
  906. / pCacheEntry -> Cache entry to update
  907. /
  908. / Out:
  909. / HRESULT
  910. /----------------------------------------------------------------------------*/
  911. VOID FreeIconList(LPCLASSCACHEENTRY pCacheEntry)
  912. {
  913. TraceEnter(TRACE_CACHE, "FreeIconList");
  914. for (INT i = 0 ; i < ARRAYSIZE(pCacheEntry->pIconName); i++)
  915. LocalFreeStringW(&pCacheEntry->pIconName[i]);
  916. TraceLeave();
  917. }
  918. /*-----------------------------------------------------------------------------
  919. / Attribute Name helpers
  920. /----------------------------------------------------------------------------*/
  921. /*-----------------------------------------------------------------------------
  922. / GetAttributeNames
  923. / -----------------
  924. / Get the attribute names given the cache entry and the variant to store
  925. / them into.
  926. /
  927. / In:
  928. / pCacheEntry -> cache entry to be filled
  929. / pDataObject -> dataobject used for IDataObject caching
  930. /
  931. / Out:
  932. / HRESULT
  933. /----------------------------------------------------------------------------*/
  934. VOID _AddAttributeName(HDPA hdpaAttributeNames, LPWSTR pName, LPWSTR pDisplayName, DWORD dwFlags, HDPA hdpaNewAttributes)
  935. {
  936. HRESULT hr;
  937. LPATTRIBUTENAME pAttributeName = NULL;
  938. USES_CONVERSION;
  939. TraceEnter(TRACE_CACHE, "_AddAttributeName");
  940. Trace(TEXT("pName: %s"), W2T(pName));
  941. Trace(TEXT("pDisplayName: %s"), W2T(pDisplayName));
  942. pAttributeName = (LPATTRIBUTENAME)LocalAlloc(LPTR, SIZEOF(ATTRIBUTENAME));
  943. TraceAssert(pAttributeName);
  944. if (!pAttributeName)
  945. ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate ATTRIBUTENAME");
  946. //pAttributeName->pName = NULL;
  947. //pAttributeName->pDisplayName = NULL;
  948. pAttributeName->dwADsType = ADSTYPE_UNKNOWN;
  949. pAttributeName->dwFlags = dwFlags;
  950. hr = LocalAllocStringW(&pAttributeName->pName, pName);
  951. FailGracefully(hr, "Failed to allocate attribute name")
  952. hr = LocalAllocStringW(&pAttributeName->pDisplayName, pDisplayName);
  953. FailGracefully(hr, "Failed to allocate display name");
  954. if (-1 == DPA_AppendPtr(hdpaAttributeNames, pAttributeName))
  955. ExitGracefully(hr, E_OUTOFMEMORY, "Failed to add to the DPA");
  956. // do we need to add the attribute to the new "attribtes list"?
  957. Trace(TEXT("About to search cache for: %s"), W2T(pName));
  958. if (g_hdpaPropCache)
  959. {
  960. PROPCACHEENTRY pce = { 0 };
  961. pce.pName = pName;
  962. if (-1 == DPA_Search(g_hdpaPropCache, &pce, 0, _ComparePropCacheEntry, NULL, DPAS_SORTED))
  963. {
  964. hr = StringDPA_AppendStringW(hdpaNewAttributes, pName, NULL);
  965. FailGracefully(hr, "Failed to add the property to the new attribute list");
  966. }
  967. }
  968. else
  969. {
  970. hr = StringDPA_AppendStringW(hdpaNewAttributes, pName, NULL);
  971. FailGracefully(hr, "Failed to add the property to the new attribute list");
  972. }
  973. hr = S_OK;
  974. exit_gracefully:
  975. if (FAILED(hr))
  976. _FreeAttributeNameCB(pAttributeName, NULL);
  977. TraceLeave();
  978. }
  979. INT _CompareAttributeNameCB(LPVOID p1, LPVOID p2, LPARAM lParam)
  980. {
  981. LPATTRIBUTENAME pEntry1 = (LPATTRIBUTENAME)p1;
  982. LPATTRIBUTENAME pEntry2 = (LPATTRIBUTENAME)p2;
  983. return StrCmpIW(pEntry1->pName, pEntry2->pName);
  984. }
  985. HRESULT GetAttributeNames(LPCLASSCACHEENTRY pCacheEntry, LPCLASSCACHEGETINFO pccgi, IADs* pDisplaySpecifier)
  986. {
  987. HRESULT hr;
  988. LONG l, lower, upper;
  989. LPVARIANT pArray = NULL;
  990. HDPA hdpaNewAttributes = NULL;
  991. VARIANT variant;
  992. WCHAR szProperty[MAX_PATH], szDisplayName[MAX_PATH], szHide[10];
  993. DWORD dwFlags;
  994. IDirectorySchemaMgmt *pdsm = NULL;
  995. INT i;
  996. USES_CONVERSION;
  997. TraceEnter(TRACE_CACHE, "GetAttributeNames");
  998. // allocate a DPA for storing the new attribute list into
  999. hdpaNewAttributes = DPA_Create(16);
  1000. TraceAssert(hdpaNewAttributes);
  1001. if (!hdpaNewAttributes)
  1002. ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate new attribute DPA");
  1003. // get the property the user specified, this should be an array of
  1004. // property values that will be associated with the class
  1005. VariantInit(&variant);
  1006. if (!pCacheEntry->hdpaAttributeNames)
  1007. {
  1008. pCacheEntry->hdpaAttributeNames = DPA_Create(16);
  1009. TraceAssert(pCacheEntry->hdpaAttributeNames);
  1010. if (!pCacheEntry->hdpaAttributeNames)
  1011. ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate attribute name DSA");
  1012. }
  1013. if (SUCCEEDED(pDisplaySpecifier->Get(ATTRIBUTE_NAMES, &variant)))
  1014. {
  1015. if (V_VT(&variant) == VT_BSTR)
  1016. {
  1017. // Parse the name from the string format <property>[,<display name>]
  1018. // and add it to the property DPA we have been given.
  1019. GetStringElementW(V_BSTR(&variant), 0, szProperty, ARRAYSIZE(szProperty));
  1020. GetStringElementW(V_BSTR(&variant), 1, szDisplayName, ARRAYSIZE(szDisplayName));
  1021. dwFlags = 0x0;
  1022. if (SUCCEEDED(GetStringElementW(V_BSTR(&variant), 2, szHide, ARRAYSIZE(szHide))))
  1023. {
  1024. Trace(TEXT("Parsing hide flag: %s"), W2T(szHide));
  1025. dwFlags = StringToDWORD(szHide);
  1026. }
  1027. _AddAttributeName(pCacheEntry->hdpaAttributeNames, szProperty, szDisplayName, dwFlags, hdpaNewAttributes);
  1028. }
  1029. else
  1030. {
  1031. if (V_VT(&variant) != (VT_VARIANT|VT_ARRAY))
  1032. ExitGracefully(hr, E_FAIL, "Exported VARIANT array as result from property query");
  1033. hr = SafeArrayGetLBound(V_ARRAY(&variant), 1, (LONG*)&lower);
  1034. FailGracefully(hr, "Failed to get lower bounds of array");
  1035. hr = SafeArrayGetUBound(V_ARRAY(&variant), 1, (LONG*)&upper);
  1036. FailGracefully(hr, "Failed to get upper bounds of array");
  1037. hr = SafeArrayAccessData(V_ARRAY(&variant), (LPVOID*)&pArray);
  1038. FailGracefully(hr, "Failed to get 'safe' accessor to array");
  1039. for (l = lower; l <= upper ; l++)
  1040. {
  1041. LPVARIANT pVariant = &pArray[l];
  1042. TraceAssert(pVariant);
  1043. if (V_VT(pVariant) == VT_BSTR )
  1044. {
  1045. // Parse the name from the string format <property>[,<display name>]
  1046. // and add it to the property DPA we have been given.
  1047. GetStringElementW(V_BSTR(pVariant), 0, szProperty, ARRAYSIZE(szProperty));
  1048. GetStringElementW(V_BSTR(pVariant), 1, szDisplayName, ARRAYSIZE(szDisplayName));
  1049. if (SUCCEEDED(GetStringElementW(V_BSTR(pVariant), 2, szHide, ARRAYSIZE(szHide))))
  1050. {
  1051. Trace(TEXT("Parsing hide flag: %s"), W2T(szHide));
  1052. dwFlags = StringToDWORD(szHide);
  1053. }
  1054. _AddAttributeName(pCacheEntry->hdpaAttributeNames, szProperty, szDisplayName, dwFlags, hdpaNewAttributes);
  1055. }
  1056. }
  1057. DPA_Sort(pCacheEntry->hdpaAttributeNames, _CompareAttributeNameCB, NULL);
  1058. }
  1059. }
  1060. // walk the cache adding the entries
  1061. hr = _GetDsSchemaMgmt(pccgi, &pdsm);
  1062. FailGracefully(hr, "Failed to get schema management object");
  1063. for (i = 0 ; i < DPA_GetPtrCount(hdpaNewAttributes) ; i++)
  1064. {
  1065. LPCWSTR pAttributeName = StringDPA_GetStringW(hdpaNewAttributes, i);
  1066. TraceAssert(pAttributeName);
  1067. hr = _AddPropToPropCache(pccgi, pdsm, pAttributeName, NULL);
  1068. FailGracefully(hr, "Failed to add property to cache");
  1069. }
  1070. hr = S_OK;
  1071. exit_gracefully:
  1072. if (FAILED(hr))
  1073. FreeAttributeNames(&pCacheEntry->hdpaAttributeNames);
  1074. VariantClear(&variant);
  1075. DoRelease(pdsm);
  1076. if (g_hdpaPropCache)
  1077. {
  1078. TraceMsg("Sorting the property cache");
  1079. DPA_Sort(g_hdpaPropCache, _ComparePropCacheEntry, NULL);
  1080. }
  1081. StringDPA_Destroy(&hdpaNewAttributes);
  1082. TraceLeaveResult(hr);
  1083. }
  1084. /*-----------------------------------------------------------------------------
  1085. / FreeAttributeNames
  1086. / --------------------
  1087. / Free the DSA containiing the attribute names and their display
  1088. / name.
  1089. /
  1090. / In:
  1091. / pHDSA = pointer to a HDSA to be free'd, and NULL'd
  1092. /
  1093. / Out:
  1094. / HRESULT
  1095. /----------------------------------------------------------------------------*/
  1096. INT _FreeAttributeNameCB(LPVOID p, LPVOID pData)
  1097. {
  1098. LPATTRIBUTENAME pItem = (LPATTRIBUTENAME)p;
  1099. TraceEnter(TRACE_CACHE, "_FreeAttributeNameCB");
  1100. TraceAssert(pItem && pItem->pName && pItem->pDisplayName);
  1101. LocalFreeStringW(&pItem->pName);
  1102. LocalFreeStringW(&pItem->pDisplayName);
  1103. LocalFree(pItem);
  1104. TraceLeaveValue(1);
  1105. }
  1106. VOID FreeAttributeNames(HDPA* pHDPA)
  1107. {
  1108. TraceEnter(TRACE_CACHE, "FreeAttributeNames");
  1109. if (*pHDPA)
  1110. {
  1111. DPA_DestroyCallback(*pHDPA, _FreeAttributeNameCB, 0L);
  1112. *pHDPA = NULL;
  1113. }
  1114. TraceLeave();
  1115. }
  1116. /*-----------------------------------------------------------------------------
  1117. / Property cache helpers and fillers
  1118. /----------------------------------------------------------------------------*/
  1119. //
  1120. // cache house keeping functions (delete and compare)
  1121. //
  1122. INT _ComparePropCacheEntry(LPVOID p1, LPVOID p2, LPARAM lParam)
  1123. {
  1124. LPPROPCACHEENTRY pEntry1 = (LPPROPCACHEENTRY)p1;
  1125. LPPROPCACHEENTRY pEntry2 = (LPPROPCACHEENTRY)p2;
  1126. return StrCmpIW(pEntry1->pName, pEntry2->pName);
  1127. }
  1128. VOID _FreePropCacheEntry(LPPROPCACHEENTRY *ppCacheEntry)
  1129. {
  1130. if (*ppCacheEntry)
  1131. {
  1132. LPPROPCACHEENTRY pCacheEntry = *ppCacheEntry;
  1133. LocalFreeStringW(&pCacheEntry->pName);
  1134. LocalFree(pCacheEntry);
  1135. *ppCacheEntry = NULL;
  1136. }
  1137. }
  1138. //
  1139. // get the IDirectorySchemaManagement object for the server
  1140. //
  1141. HRESULT _GetDsSchemaMgmt(LPCLASSCACHEGETINFO pccgi, IDirectorySchemaMgmt **ppdsm)
  1142. {
  1143. HRESULT hres;
  1144. IADs *pRootDSE = NULL;
  1145. VARIANT variant;
  1146. LPWSTR pszPath = NULL;
  1147. LPWSTR pszServer = pccgi->pServer;
  1148. LPWSTR pszMachineServer = NULL;
  1149. INT cchPath;
  1150. USES_CONVERSION;
  1151. TraceEnter(TRACE_CACHE, "_GetDsSchemaMgmt");
  1152. *ppdsm = NULL;
  1153. VariantInit(&variant);
  1154. hres = GetCacheInfoRootDSE(pccgi, &pRootDSE);
  1155. if ((hres == HRESULT_FROM_WIN32(ERROR_NO_SUCH_DOMAIN)) && !pccgi->pServer)
  1156. {
  1157. TraceMsg("Failed to get the RootDSE from the server - not found");
  1158. DSROLE_PRIMARY_DOMAIN_INFO_BASIC *pInfo;
  1159. if (DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (BYTE**)&pInfo) == WN_SUCCESS)
  1160. {
  1161. if (pInfo->DomainNameDns)
  1162. {
  1163. Trace(TEXT("Machine domain is: %s"), W2T(pInfo->DomainNameDns));
  1164. CLASSCACHEGETINFO ccgi = *pccgi;
  1165. ccgi.pServer = pInfo->DomainNameDns;
  1166. hres = GetCacheInfoRootDSE(&ccgi, &pRootDSE);
  1167. if (SUCCEEDED(hres))
  1168. {
  1169. hres = LocalAllocStringW(&pszMachineServer, pInfo->DomainNameDns);
  1170. pszServer = pszMachineServer;
  1171. }
  1172. }
  1173. DsRoleFreeMemory(pInfo);
  1174. }
  1175. }
  1176. FailGracefully(hres, "Failed to get the RootDSE");
  1177. hres = pRootDSE->Get(L"defaultNamingContext", &variant);
  1178. FailGracefully(hres, "Failed to get default naming context for this object");
  1179. if (V_VT(&variant) != VT_BSTR)
  1180. ExitGracefully(hres, E_FAIL, "defaultNamingContext is not a BSTR");
  1181. cchPath = lstrlenW(L"LDAP://") + lstrlenW(V_BSTR(&variant));
  1182. if (pszServer)
  1183. cchPath += 1 + lstrlenW(pszServer);
  1184. hres = LocalAllocStringLenW(&pszPath, cchPath);
  1185. FailGracefully(hres, "Failed to allocate the buffer for the name");
  1186. StrCpyW(pszPath, L"LDAP://");
  1187. if (pszServer)
  1188. {
  1189. StrCatW(pszPath, pszServer);
  1190. StrCatW(pszPath, L"/");
  1191. }
  1192. StrCatW(pszPath, V_BSTR(&variant));
  1193. Trace(TEXT("Default naming context is (with prefix) %s"), W2T(pszPath));
  1194. hres = ADsOpenObject(pszPath,
  1195. pccgi->pUserName, pccgi->pPassword,
  1196. (pccgi->dwFlags & CLASSCACHE_SIMPLEAUTHENTICATE) ? 0:ADS_SECURE_AUTHENTICATION,
  1197. IID_IDirectorySchemaMgmt, (void **)ppdsm);
  1198. FailGracefully(hres, "Failed to open the default naming context object");
  1199. exit_gracefully:
  1200. LocalFreeStringW(&pszPath);
  1201. LocalFreeStringW(&pszMachineServer);
  1202. VariantClear(&variant);
  1203. DoRelease(pRootDSE);
  1204. TraceLeaveResult(hres);
  1205. }
  1206. //
  1207. // allocate the cache (if needed) and add a new entry to it, reading the schema to find out the type
  1208. // of attribute this is.
  1209. //
  1210. HRESULT _AddPropToPropCache(LPCLASSCACHEGETINFO pccgi, IDirectorySchemaMgmt *pdsm, LPCWSTR pAttributeName, ADSTYPE *padt)
  1211. {
  1212. HRESULT hres;
  1213. PADS_ATTR_DEF pad = NULL;
  1214. WCHAR szAttributeName[MAX_PATH];
  1215. DWORD dwReturned;
  1216. LPPROPCACHEENTRY pCacheEntry = NULL;
  1217. USES_CONVERSION;
  1218. TraceEnter(TRACE_CACHE, "_AddPropToPropCache");
  1219. // compute the property name
  1220. StrCpyW(szAttributeName, pAttributeName);
  1221. if (pccgi->pServer)
  1222. {
  1223. StrCatW(szAttributeName, L":");
  1224. StrCatW(szAttributeName, pccgi->pServer);
  1225. }
  1226. // check to see if we have a cache already
  1227. if (!g_hdpaPropCache)
  1228. {
  1229. g_hdpaPropCache = DPA_Create(16);
  1230. TraceAssert(g_hdpaPropCache);
  1231. if (!g_hdpaPropCache)
  1232. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate property cache");
  1233. }
  1234. // allocate a new cache entry, fill it and add it to the DPA.
  1235. pCacheEntry = (LPPROPCACHEENTRY)LocalAlloc(LPTR, SIZEOF(PROPCACHEENTRY));
  1236. TraceAssert(pCacheEntry);
  1237. if (!pCacheEntry)
  1238. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate new property cache entry");
  1239. // pCacheEntry->pName = NULL;
  1240. pCacheEntry->dwADsType = ADSTYPE_UNKNOWN;
  1241. // fill the record from the schema information we have
  1242. hres = LocalAllocStringW(&pCacheEntry->pName, szAttributeName);
  1243. FailGracefully(hres, "Failed to add name to entry");
  1244. hres = pdsm->EnumAttributes((LPWSTR *)&pAttributeName, 1, &pad, &dwReturned);
  1245. FailGracefully(hres, "Failed to read the property information");
  1246. if (dwReturned)
  1247. {
  1248. pCacheEntry->dwADsType = pad->dwADsType;
  1249. }
  1250. else
  1251. {
  1252. TraceMsg("*** Failed to read property type from schema, defaulting to ADSTYPE_UNKNOWN ***");
  1253. }
  1254. Trace(TEXT("Attribute: %s is %08x"), W2CT(pCacheEntry->pName), pCacheEntry->dwADsType);
  1255. if (-1 == DPA_AppendPtr(g_hdpaPropCache, pCacheEntry))
  1256. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to add the entry to the property cache DPA");
  1257. hres = S_OK;
  1258. exit_gracefully:
  1259. if (FAILED(hres))
  1260. _FreePropCacheEntry(&pCacheEntry);
  1261. if (pad)
  1262. FreeADsMem(pad);
  1263. if (SUCCEEDED(hres) && padt)
  1264. *padt = pCacheEntry->dwADsType;
  1265. TraceLeaveResult(hres);
  1266. }
  1267. /*-----------------------------------------------------------------------------
  1268. / ClassCache_GetADsTypeFromAttribute
  1269. / ----------------------------------
  1270. / Given a property name return the ADsType for it, this is based off the
  1271. / global property cache, not the display specifier information we have.
  1272. /
  1273. / In:
  1274. / pccgi -> CLASSCACHEGETINFO structure (credentials)
  1275. / pAttributeName -> attribute name to look up
  1276. /
  1277. / Out:
  1278. / ADSTYPE
  1279. /----------------------------------------------------------------------------*/
  1280. ADSTYPE ClassCache_GetADsTypeFromAttribute(LPCLASSCACHEGETINFO pccgi, LPCWSTR pAttributeName)
  1281. {
  1282. ADSTYPE dwResult = ADSTYPE_UNKNOWN;
  1283. WCHAR szAttributeName[MAX_PATH];
  1284. INT iFound = -1;
  1285. IDirectorySchemaMgmt *pdsm = NULL;
  1286. USES_CONVERSION;
  1287. TraceEnter(TRACE_CACHE, "ClassCache_GetADsTypeFromAttribute");
  1288. Trace(TEXT("Looking up property in cache: %s"), W2CT(pAttributeName));
  1289. // get the lock on the cache, then search it for the property we have been given
  1290. TraceMsg("Waiting to get cache lock for property cache");
  1291. EnterCriticalSection(&g_csPropCache);
  1292. Trace(TEXT("Lock aquired, building key for: %s"), W2CT(pAttributeName));
  1293. StrCpyW(szAttributeName, pAttributeName);
  1294. if (pccgi->pServer)
  1295. {
  1296. StrCatW(szAttributeName, L":");
  1297. StrCatW(szAttributeName, pccgi->pServer);
  1298. }
  1299. Trace(TEXT("Key for attribute in cache is: %s"), W2T(szAttributeName));
  1300. // and search for it...
  1301. if (g_hdpaPropCache)
  1302. {
  1303. PROPCACHEENTRY pce = { 0 };
  1304. pce.pName = (LPWSTR)szAttributeName;
  1305. iFound = DPA_Search(g_hdpaPropCache, &pce, 0, _ComparePropCacheEntry, NULL, DPAS_SORTED);
  1306. Trace(TEXT("Entry found in cache at %d"), iFound);
  1307. }
  1308. // iFound != -1 if we found something, otherwise we need to allocate a new entry
  1309. if (iFound != -1)
  1310. {
  1311. LPPROPCACHEENTRY pCacheEntry = (LPPROPCACHEENTRY)DPA_GetPtr(g_hdpaPropCache, iFound);
  1312. if (pCacheEntry)
  1313. {
  1314. dwResult = pCacheEntry->dwADsType;
  1315. Trace(TEXT("Property found in cache, result %d"), dwResult);
  1316. }
  1317. }
  1318. else if (SUCCEEDED(_GetDsSchemaMgmt(pccgi, &pdsm)))
  1319. {
  1320. if (SUCCEEDED(_AddPropToPropCache(pccgi, pdsm, pAttributeName, &dwResult)))
  1321. {
  1322. TraceMsg("Added the property to the cache, therefore sorting");
  1323. DPA_Sort(g_hdpaPropCache, _ComparePropCacheEntry, NULL);
  1324. }
  1325. }
  1326. LeaveCriticalSection(&g_csPropCache);
  1327. DoRelease(pdsm);
  1328. TraceLeaveValue(dwResult);
  1329. }