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.

1724 lines
55 KiB

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