#include "pch.h" #include "stddef.h" #include "dsrole.h" #pragma hdrstop /*----------------------------------------------------------------------------- / Class cache /----------------------------------------------------------------------------*/ // // Class cache state and functions // #define ALL_PREFIXED_ATTRIBUTES \ (CLASSCACHE_PROPPAGES| \ CLASSCACHE_CONTEXTMENUS) #define ALL_NONPREFIXED_ATTRIBUTES \ (CLASSCACHE_ICONS| \ CLASSCACHE_FRIENDLYNAME| \ CLASSCACHE_TREATASLEAF| \ CLASSCACHE_ATTRIBUTENAMES| \ CLASSCACHE_CREATIONINFO) #define ALL_DISPLAY_SPEC_VALUES \ (CLASSCACHE_PROPPAGES| \ CLASSCACHE_CONTEXTMENUS| \ CLASSCACHE_ICONS| \ CLASSCACHE_FRIENDLYNAME| \ CLASSCACHE_TREATASLEAF| \ CLASSCACHE_ATTRIBUTENAMES| \ CLASSCACHE_CREATIONINFO) CRITICAL_SECTION g_csCache; // critical section for managing lifetime of the cache BOOL g_fClassCacheSorted = FALSE; HDPA g_hdpaClassCache = NULL; INT _CompareCacheEntry(LPVOID p1, LPVOID p2, LPARAM lParam); VOID _FreeCacheEntry(LPCLASSCACHEENTRY* ppCacheEntry); // // Cache fillers // HRESULT GetPropertyPageList(LPCLASSCACHEENTRY pCacheEntry, LPWSTR pPrefix, IADs* pDisplaySpecifier); VOID FreePropertyPageList(HDSA* pHDSA); HRESULT GetMenuHandlerList(LPCLASSCACHEENTRY pCacheEntry, LPWSTR pPrefix, IADs* pDisplaySpecifier); VOID FreeMenuHandlerList(HDSA* pHDSA); HRESULT GetIconList(LPCLASSCACHEENTRY pCacheEntry, IADs* pDisplaySpecifier); VOID FreeIconList(LPCLASSCACHEENTRY pCacheEntry); HRESULT GetAttributeNames(LPCLASSCACHEENTRY pCacheEntry, LPCLASSCACHEGETINFO pccgi, IADs* pDisplaySpecifier); INT CALLBACK _FreeAttributeNameCB(LPVOID p, LPVOID pData); VOID FreeAttributeNames(HDPA* pHDPA); // // Constant strings for the properties we expect // #define DISPLAY_SPECIFICATION L"displaySpecification" #define PROPERTY_PAGES L"propertyPages" #define CONTEXT_MENU L"contextMenu" #define ICON_LOCATION L"iconPath" #define FRIENDLY_NAME L"classDisplayName" #define ATTRIBUTE_NAMES L"attributeDisplayNames" #define TREAT_AS_LEAF L"treatAsLeaf" #define CREATION_DIALOG L"createDialog" #define CREATION_WIZARD L"creationWizard" #define CREATION_WIZARD_EXTN L"createWizardExt" // // property cache is used to store the property name (with optional server) and the ADsType. // CRITICAL_SECTION g_csPropCache; HDPA g_hdpaPropCache = NULL; typedef struct { LPWSTR pName; // property name (inc server if needed) ADSTYPE dwADsType; // attribute type } PROPCACHEENTRY, * LPPROPCACHEENTRY; INT _ComparePropCacheEntry(LPVOID p1, LPVOID p2, LPARAM lParam); VOID _FreePropCacheEntry(LPPROPCACHEENTRY *ppCacheEntry); HRESULT _GetDsSchemaMgmt(LPCLASSCACHEGETINFO pccgi, IDirectorySchemaMgmt **ppdsm); HRESULT _AddPropToPropCache(LPCLASSCACHEGETINFO pccgi, IDirectorySchemaMgmt *pdsm, LPCWSTR pAttributeName, ADSTYPE *padt); /*----------------------------------------------------------------------------- / _FreeCacheEntry / --------------- / Cache entries are stored as a LocalAlloc pointed to be the DPA. Here / we tidy up such allocations. / / In: / ppCacheEntry = pointer to block to be free'd / / Out: / VOID /----------------------------------------------------------------------------*/ VOID _FreeCacheEntry(LPCLASSCACHEENTRY* ppCacheEntry) { LPCLASSCACHEENTRY pCacheEntry; USES_CONVERSION; TraceEnter(TRACE_CACHE, "_FreeCacheEntry"); TraceAssert(ppCacheEntry); pCacheEntry = *ppCacheEntry; if (pCacheEntry) { Trace(TEXT("About to wait for multiple object for cache entry: %s"), W2T(pCacheEntry->pObjectClass)); EnterCriticalSection(&pCacheEntry->csLock); LocalFreeStringW(&pCacheEntry->pKey); LocalFreeStringW(&pCacheEntry->pObjectClass); LocalFreeStringW(&pCacheEntry->pServer); LocalFreeStringW(&pCacheEntry->pFriendlyClassName); FreePropertyPageList(&pCacheEntry->hdsaPropertyPages); FreeMenuHandlerList(&pCacheEntry->hdsaMenuHandlers); FreeIconList(pCacheEntry); FreeAttributeNames(&pCacheEntry->hdpaAttributeNames); if (pCacheEntry->hdsaWizardExtn) DSA_Destroy(pCacheEntry->hdsaWizardExtn); LeaveCriticalSection(&pCacheEntry->csLock); DeleteCriticalSection(&pCacheEntry->csLock); LocalFree((HLOCAL)pCacheEntry); *ppCacheEntry = NULL; } TraceLeave(); } /*----------------------------------------------------------------------------- / ClassCache_Init / --------------- / Initialize the cache objects we are going to use, mostly the syncronization / things that we need. / / In: / Out: / - /----------------------------------------------------------------------------*/ VOID ClassCache_Init(VOID) { TraceEnter(TRACE_CACHE, "ClassCache_Init"); InitializeCriticalSection(&g_csCache); InitializeCriticalSection(&g_csPropCache); TraceLeave(); } /*----------------------------------------------------------------------------- / ClassCache_GetClassInfo / ----------------------- / Query cache code which selectivitly caches information based / on the given object and the flags. / / In: / pGetInfo -> structure containing parameters for object / pPath = ADS path for the object we are tyring to cache on / pObjectClass = objectClass to key the cache entry on / pAttributePrefix = prefix used when querying for properties (also used in cache key) / dwFlags = flags indicating which cache fields are required / / ppCacheEntry -> receieves pointer to the cache entry / / Out: / HRESULT /----------------------------------------------------------------------------*/ HRESULT CALLBACK _AddWizardExtnGUID(DWORD dwIndex, BSTR pString, LPVOID pData) { HRESULT hr; HDSA hdsa = (HDSA)pData; GUID guid; USES_CONVERSION; TraceEnter(TRACE_CACHE, "_AddWizardExtnGUID"); Trace(TEXT("dwIndex %08x, pString: %s"), dwIndex, W2T(pString)); if (GetGUIDFromStringW(pString, &guid)) { if (-1 == DSA_AppendItem(hdsa, &guid)) ExitGracefully(hr, E_FAIL, "Failed to add wizard GUID"); } hr = S_OK; exit_gracefully: TraceLeaveResult(hr); } INT _CompareCacheEntryCB(LPVOID p1, LPVOID p2, LPARAM lParam) { INT iResult = -1; LPCLASSCACHEENTRY pEntry1 = (LPCLASSCACHEENTRY)p1; LPCLASSCACHEENTRY pEntry2 = (LPCLASSCACHEENTRY)p2; if (pEntry1 && pEntry2) iResult = StrCmpIW(pEntry1->pKey, pEntry2->pKey); return iResult; } HRESULT ClassCache_GetClassInfo(LPCLASSCACHEGETINFO pInfo, LPCLASSCACHEENTRY* ppCacheEntry) { HRESULT hr; LPCLASSCACHEENTRY pCacheEntry = NULL; WCHAR szClassKey[MAX_PATH*2]; INT index; IADs* pDisplaySpecifier = NULL; IADs* pDsObject = NULL; IADsClass* pDsClass = NULL; BSTR bstrSchemaObject = NULL; HICON hSmallIcon = NULL; HICON hLargeIcon = NULL; VARIANT variant; VARIANT_BOOL vbIsContainer; DWORD dwFlags; DWORD dwWaitRes; USES_CONVERSION; TraceEnter(TRACE_CACHE, "ClassCache_GetClassInfo"); if (!pInfo || !pInfo->pObjectClass || !ppCacheEntry) ExitGracefully(hr, E_FAIL, "Bad parameters for ClassCache_GetClassInfo"); dwFlags = pInfo->dwFlags; // Build the key string, this is "className[:attributePrefix]" that way the shell and the // admin tools can share the same cache structure VariantInit(&variant); StrCpyW(szClassKey, pInfo->pObjectClass); if (pInfo->pAttributePrefix) { StrCatW(szClassKey, L":"); StrCatW(szClassKey, pInfo->pAttributePrefix); if (dwFlags & ALL_PREFIXED_ATTRIBUTES) dwFlags |= ALL_PREFIXED_ATTRIBUTES; } else { if (dwFlags & ALL_NONPREFIXED_ATTRIBUTES) dwFlags |= ALL_NONPREFIXED_ATTRIBUTES; } // add the server name to the class key if (pInfo->pServer) { StrCatW(szClassKey, L":"); StrCatW(szClassKey, pInfo->pServer); } Trace(TEXT("Cache key is: %s"), W2T(szClassKey)); // do we have a cache? if so then look in there to see if we have // already cached information about this class Trace(TEXT("About to wait for global cache lock when getting cache entry: %s"), W2T(pInfo->pObjectClass)); EnterCriticalSection(&g_csCache); TraceMsg("Global cache lock aquired, so can now modify cache content"); if (g_hdpaClassCache) { // sort it if its not already sorted, then do a sorted search for the // best performance so we can pick up the information. if (!g_fClassCacheSorted) { TraceMsg("!!! Cache not sorted, just about to call DPA_Sort !!!"); DPA_Sort(g_hdpaClassCache, _CompareCacheEntryCB, NULL); g_fClassCacheSorted = TRUE; } CLASSCACHEENTRY cce; cce.pKey = szClassKey; Trace(TEXT("Searching the cache for entry %s"), W2T(szClassKey)); index = DPA_Search(g_hdpaClassCache, &cce, 0, _CompareCacheEntryCB, NULL, DPAS_SORTED); if (index >= 0) { Trace(TEXT("Cache hit at location %d"), index); pCacheEntry = (LPCLASSCACHEENTRY)DPA_FastGetPtr(g_hdpaClassCache, index); Trace(TEXT("About to wait on cache entry for: %s"), W2T(pCacheEntry->pObjectClass)); EnterCriticalSection(&pCacheEntry->csLock); TraceMsg("Got lock on cache entry"); } } else { g_hdpaClassCache = DPA_Create(4); // create the new cache if (!g_hdpaClassCache) { LeaveCriticalSection(&g_csCache); ExitGracefully(hr, E_OUTOFMEMORY, "Failed to cache object info"); } } // pCacheEntry == NULL if we haven't hit anything yet, therefore lets // create a new entry if that happens, or fall through! if (!pCacheEntry) { // allocate a new entry, initialize it and put it into the DSA, having done // this we can search it, fill in the gaps etc. pCacheEntry = (LPCLASSCACHEENTRY)LocalAlloc(LPTR, SIZEOF(CLASSCACHEENTRY)); if (!pCacheEntry) { LeaveCriticalSection(&g_csCache); ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate new cache structure"); } InitializeCriticalSection(&pCacheEntry->csLock); EnterCriticalSection(&pCacheEntry->csLock); // enter it, we need it locked // pCacheEntry->pKey = NULL; // pCacheEntry->dwFlags = 0x0; // pCacheEntry->dwCached = 0x0; // pCacheEntry->fHasWizardDailogCLSID = FALSE; // pCacheEntry->fHasWizardPrimaryPageCLSID = FALSE; // pCacheEntry->pObjectClass = NULL; // pCacheEntry->pServer = NULL; // pCacheEntry->pFriendlyClassName = NULL; // pCacheEntry->hdsaPropertyPages = NULL; // pCacheEntry->hdsaMenuHandlers = NULL; // ZeroMemory(pCacheEntry->pIconName, SIZEOF(pCacheEntry->pIconName)); // ZeroMemory(pCacheEntry->iImage, SIZEOF(pCacheEntry->iImage)); // pCacheEntry->fIsContainer = FALSE; // pCacheEntry->hdpaAttributeNames = NULL; // pCacheEntry->clsidWizardDialog = { 0 }; // pCacheEntry->clsidWizardPrimary = { 0 }; // pCacheEntry->hdsaWizardExtn = NULL; hr = LocalAllocStringW(&pCacheEntry->pKey, szClassKey); if (SUCCEEDED(hr)) hr = LocalAllocStringW(&pCacheEntry->pObjectClass, pInfo->pObjectClass); if (SUCCEEDED(hr) && pInfo->pServer) hr = LocalAllocStringW(&pCacheEntry->pServer, pInfo->pServer); if (FAILED(hr) || (-1 == DPA_AppendPtr(g_hdpaClassCache, pCacheEntry))) { LeaveCriticalSection(&g_csCache); LeaveCriticalSection(&pCacheEntry->csLock); _FreeCacheEntry(&pCacheEntry); ExitGracefully(hr, E_UNEXPECTED, "Failed to add cache entry to DPA"); } g_fClassCacheSorted = FALSE; } LeaveCriticalSection(&g_csCache); // ensure we have a display specifier if we need one, that boils down to be // dwFlags expresses fields we are interested in, so do we have those in // the cache record, if not then lets check to see if those bits match // ones which come from the specifier, if so then we better grab one. if (dwFlags & ALL_DISPLAY_SPEC_VALUES) { if ((pCacheEntry->dwFlags & dwFlags) != dwFlags) { Trace(TEXT("Binding to the display specifier %08x,%08x"), pCacheEntry->dwFlags & dwFlags, dwFlags); if (FAILED(GetDisplaySpecifier(pInfo, IID_IADs, (LPVOID*)&pDisplaySpecifier))) { TraceMsg("Failed to bind to display specifier, pDisplaySpecifier == NULL"); TraceAssert(pDisplaySpecifier == NULL); // ensure that we don't try and cache display specifier information and // we mark the cache record as dirty. dwFlags &= ~(ALL_DISPLAY_SPEC_VALUES & ~CLASSCACHE_FRIENDLYNAME); } } } // container flag for the objects if (dwFlags & CLASSCACHE_CONTAINER) { if (!(pCacheEntry->dwFlags & CLASSCACHE_CONTAINER)) { if (pInfo->pPath) { TraceMsg("!!! Binding to the object to get container flags !!!"); if (SUCCEEDED(ADsOpenObject(pInfo->pPath, pInfo->pUserName, pInfo->pPassword, (pInfo->dwFlags & CLASSCACHE_SIMPLEAUTHENTICATE) ? 0:ADS_SECURE_AUTHENTICATION, IID_IADs, (LPVOID*)&pDsObject))) { // Try to deterimine if the object is a container by binding to the // schema object and getting its container property. hr = pDsObject->get_Schema(&bstrSchemaObject); FailGracefully(hr, "Failed to get the objects schema"); Trace(TEXT("Path to schema object is %s"), W2T(bstrSchemaObject)); if (SUCCEEDED(ADsOpenObject(bstrSchemaObject, pInfo->pUserName, pInfo->pPassword, (pInfo->dwFlags & CLASSCACHE_SIMPLEAUTHENTICATE) ? 0:ADS_SECURE_AUTHENTICATION, IID_IADsClass, (LPVOID*)&pDsClass))) { if (SUCCEEDED(pDsClass->get_Container(&vbIsContainer))) { TraceMsg("Cached container flag"); pCacheEntry->fIsContainer = (vbIsContainer == -1); pCacheEntry->dwCached |= CLASSCACHE_CONTAINER; } } } } else { TraceMsg("**** No ADsPath, cannot get container flag from schema ****"); } } } // all the following attributes require a pDisplaySpecifier if (pDisplaySpecifier) { // property pages? if (dwFlags & CLASSCACHE_PROPPAGES) { if (!(pCacheEntry->dwFlags & CLASSCACHE_PROPPAGES)) { TraceMsg("Caching property page list"); if (SUCCEEDED(GetPropertyPageList(pCacheEntry, pInfo->pAttributePrefix, pDisplaySpecifier))) { TraceMsg("Fetching property page list"); pCacheEntry->dwCached |= CLASSCACHE_PROPPAGES; } } } // context menu handlers? if (dwFlags & CLASSCACHE_CONTEXTMENUS) { if (!(pCacheEntry->dwFlags & CLASSCACHE_CONTEXTMENUS)) { TraceMsg("Caching menu handler list"); if (SUCCEEDED(GetMenuHandlerList(pCacheEntry, pInfo->pAttributePrefix, pDisplaySpecifier))) { TraceMsg("Fetched context menu list"); pCacheEntry->dwCached |= CLASSCACHE_CONTEXTMENUS; } } } // icon location? if (dwFlags & CLASSCACHE_ICONS) { if (!(pCacheEntry->dwFlags & CLASSCACHE_ICONS)) { TraceMsg("Caching icon list"); if (SUCCEEDED(GetIconList(pCacheEntry, pDisplaySpecifier))) { TraceMsg("Fetched icon list"); pCacheEntry->dwCached |= CLASSCACHE_ICONS; } } } // attribute name caching? if (dwFlags & CLASSCACHE_ATTRIBUTENAMES) { if (!(pCacheEntry->dwFlags & CLASSCACHE_ATTRIBUTENAMES)) { TraceMsg("Caching attribute list"); if (SUCCEEDED(GetAttributeNames(pCacheEntry, pInfo, pDisplaySpecifier))) { TraceMsg("Fetched attribute names"); pCacheEntry->dwCached |= CLASSCACHE_ATTRIBUTENAMES; } } } // get the treat as leaf if (dwFlags & CLASSCACHE_TREATASLEAF) { if (!(pCacheEntry->dwFlags & CLASSCACHE_TREATASLEAF)) { TraceMsg("Caching the treat as leaf flag"); // pick up the "treatAsLeaf" attribute from the display specifier, if // this is undefined then use the normal container flag from the // schema. VariantClear(&variant); if (SUCCEEDED(pDisplaySpecifier->Get(TREAT_AS_LEAF, &variant)) && (V_VT(&variant) == VT_BOOL)) { TraceMsg("Caching fTreatAsLeaf"); pCacheEntry->fTreatAsLeaf = V_BOOL(&variant) == 1; pCacheEntry->dwCached |= CLASSCACHE_TREATASLEAF; } } } // get the CLSID that implements the creation dialog if (dwFlags & CLASSCACHE_WIZARDDIALOG) { if (!(pCacheEntry->dwFlags & CLASSCACHE_WIZARDDIALOG)) { TraceMsg("Caching the creation wizard dialog CLSID"); VariantClear(&variant); if (SUCCEEDED(pDisplaySpecifier->Get(CREATION_DIALOG, &variant))) { if (V_VT(&variant) == VT_BSTR) { if (GetGUIDFromStringW(V_BSTR(&variant), &pCacheEntry->clsidWizardDialog)) { TraceGUID("CLSID of wizard dialog: ", pCacheEntry->clsidWizardDialog); pCacheEntry->dwCached |= CLASSCACHE_WIZARDDIALOG; } else { Trace(TEXT("GUID string failed to parse: %s"), W2T(V_BSTR(&variant))); } } } } } // get the CLSID that implements the primary pages of the wizard if (dwFlags & CLASSCACHE_WIZARDPRIMARYPAGE) { if (!(pCacheEntry->dwFlags & CLASSCACHE_WIZARDPRIMARYPAGE)) { TraceMsg("Caching the creation wizard's primary page"); VariantClear(&variant); if (SUCCEEDED(pDisplaySpecifier->Get(CREATION_WIZARD, &variant))) { if (V_VT(&variant) == VT_BSTR) { if (GetGUIDFromStringW(V_BSTR(&variant), &pCacheEntry->clsidWizardPrimaryPage)) { TraceGUID("CLSID of primary pages: ", pCacheEntry->clsidWizardPrimaryPage); pCacheEntry->dwCached |= CLASSCACHE_WIZARDPRIMARYPAGE; } else { Trace(TEXT("GUID string failed to parse: %s"), W2T(V_BSTR(&variant))); } } } } } // get the CLSID of the extensions for the wizard if (dwFlags & CLASSCACHE_WIZARDEXTN) { if (!(pCacheEntry->dwFlags & CLASSCACHE_WIZARDEXTN)) { TraceMsg("Caching the list of extension pages for the wizard"); VariantClear(&variant); if (SUCCEEDED(pDisplaySpecifier->Get(CREATION_WIZARD_EXTN, &variant))) { if (!pCacheEntry->hdsaWizardExtn) { TraceMsg("Creating DSA to store GUIDs in"); pCacheEntry->hdsaWizardExtn = DSA_Create(SIZEOF(GUID), 4); TraceAssert(pCacheEntry->hdsaWizardExtn); } if (pCacheEntry->hdsaWizardExtn) { TraceMsg("Attempting to cache extention GUIDs into the DPA"); GetArrayContents(&variant, _AddWizardExtnGUID, (LPVOID)pCacheEntry->hdsaWizardExtn); } } } } } // friendly class anme for the object if (dwFlags & CLASSCACHE_FRIENDLYNAME) { if (!(pCacheEntry->dwFlags & CLASSCACHE_FRIENDLYNAME)) { TraceMsg("Checking for the friendly class name"); VariantClear(&variant); // if there is a display specifier and a friendly name then lets // pick it up and store it in the cache entry. if (pDisplaySpecifier) { if (SUCCEEDED(pDisplaySpecifier->Get(FRIENDLY_NAME, &variant))) { if (V_VT(&variant) == VT_BSTR) { Trace(TEXT("Friendly name: %s"), W2T(V_BSTR(&variant))); hr = LocalAllocStringW(&pCacheEntry->pFriendlyClassName, V_BSTR(&variant)); FailGracefully(hr, "Failed to copy the friendly name"); pCacheEntry->dwCached |= CLASSCACHE_FRIENDLYNAME; } } } // the friendly name is a special case, if we haven't been able to get the display // specifier or the friendly name from it then populate the cache with the // existing class name to avoid hitting the wire repeatedly. if (!(pCacheEntry->dwCached & CLASSCACHE_FRIENDLYNAME)) { TraceMsg("Defaulting to un-friendly class name"); hr = LocalAllocStringW(&pCacheEntry->pFriendlyClassName, pCacheEntry->pObjectClass); FailGracefully(hr, "Failed to allocate friendly class name"); } pCacheEntry->dwCached |= CLASSCACHE_FRIENDLYNAME; } } hr = S_OK; // success! exit_gracefully: DoRelease(pDisplaySpecifier); DoRelease(pDsObject); DoRelease(pDsClass); VariantClear(&variant); SysFreeString(bstrSchemaObject); if (hSmallIcon) DestroyIcon(hSmallIcon); if (hLargeIcon) DestroyIcon(hLargeIcon); if (pCacheEntry) { // make the attributes as cached now, and if we succeeded then we // can pass out the locked cache entry, otherwise we must // unlock it - otherwise others will not be ableto updated! pCacheEntry->dwFlags |= dwFlags; if (SUCCEEDED(hr)) { *ppCacheEntry = pCacheEntry; } else { LeaveCriticalSection(&pCacheEntry->csLock); } } TraceLeaveResult(hr); } /*----------------------------------------------------------------------------- / ClassCache_ReleaseClassInfo / --------------------------- / Each cache entry has a lock, this releases the lock. If the lock is / non-zero then the record cannot be updated, or released. / / In: / ppCacheEntry -> cache entry, NULL'd on exit. / / Out: / VOID /----------------------------------------------------------------------------*/ VOID ClassCache_ReleaseClassInfo(LPCLASSCACHEENTRY* ppCacheEntry) { TraceEnter(TRACE_CACHE, "ClassCache_ReleaseClassInfo"); if (ppCacheEntry) { LPCLASSCACHEENTRY pCacheEntry = *ppCacheEntry; if (pCacheEntry) { TraceMsg("Releasing critical section on cache record"); LeaveCriticalSection(&pCacheEntry->csLock); *ppCacheEntry = NULL; } } TraceLeave(); } /*----------------------------------------------------------------------------- / ClassCache_Discard / ------------------ / Discard the cached information we have for the DS classes we have / seen (including the cache DPA & the image lists) / / In: / - / Out: / VOID /----------------------------------------------------------------------------*/ INT _FreePropCacheEntryCB(LPVOID pVoid, LPVOID pData) { LPPROPCACHEENTRY pCacheEntry = (LPPROPCACHEENTRY)pVoid; TraceEnter(TRACE_CACHE, "_FreePropCacheEntryCB"); _FreePropCacheEntry(&pCacheEntry); TraceLeaveValue(TRUE); } INT _FreeCacheEntryCB(LPVOID pVoid, LPVOID pData) { LPCLASSCACHEENTRY pCacheEntry = (LPCLASSCACHEENTRY)pVoid; TraceEnter(TRACE_CACHE, "_FreeCacheEntryCB"); _FreeCacheEntry(&pCacheEntry); TraceLeaveValue(TRUE); } VOID ClassCache_Discard(VOID) { HRESULT hr; DWORD dwWaitRes; TraceEnter(TRACE_CACHE, "ClassCache_Discard"); // avoid destroying the cache whilst its being updated, ths is a simple // mutex. TraceMsg("About to wait for global cache lock"); EnterCriticalSection(&g_csCache); TraceMsg("Global cache lock aquired, so can now modify cache content"); if (g_hdpaClassCache) { DPA_DestroyCallback(g_hdpaClassCache, _FreeCacheEntryCB, NULL); g_hdpaClassCache = NULL; } // the property cache is protected also, so wait until we can get the // lock on it before partying on the structure. TraceMsg("About to wait for global property cache lock"); EnterCriticalSection(&g_csPropCache); TraceMsg("Global property cache lock aquired, so can now modify cache content"); if (g_hdpaPropCache) { DPA_DestroyCallback(g_hdpaPropCache, _FreePropCacheEntryCB, NULL); g_hdpaPropCache = NULL; } LeaveCriticalSection(&g_csCache); DeleteCriticalSection(&g_csCache); LeaveCriticalSection(&g_csPropCache); DeleteCriticalSection(&g_csPropCache); TraceLeave(); } /*----------------------------------------------------------------------------- / Property page list /----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------- / GetPropertyPageList / ------------------- / Build the list of property pages that we are going to be displaying / the code builds the list from the display specifier lists. / / In: / pCacheEntry -> Cache entry to update / pAttributePrefix -> suitable prefix for getting Admin/Shell pages / pDataObject -> IDataObject for getting cached information from / / Out: / HRESULT /----------------------------------------------------------------------------*/ HRESULT CALLBACK _AddPropertyPageItemCB(DWORD dwIndex, BSTR pString, LPVOID pData) { HRESULT hr; DSPROPERTYPAGE item; HDSA hdsa = (HDSA)pData; USES_CONVERSION; TraceEnter(TRACE_CACHE, "_AddPropertyPageItemCB"); Trace(TEXT("dwIndex %08x, pString: %s"), dwIndex, W2T(pString)); hr = LocalAllocStringW(&item.pPageReference, pString); FailGracefully(hr, "Failed to clone string"); if (-1 == DSA_AppendItem(hdsa, &item)) ExitGracefully(hr, E_FAIL, "Failed to property page reference to DSA"); hr = S_OK; exit_gracefully: if (FAILED(hr)) LocalFreeStringW(&item.pPageReference); TraceLeaveResult(hr); } HRESULT GetPropertyPageList(LPCLASSCACHEENTRY pCacheEntry, LPWSTR pAttributePrefix, IADs* pDisplaySpecifier) { HRESULT hr; VARIANT variant; WCHAR szProperty[MAX_PATH] = { TEXT('\0') }; USES_CONVERSION; INT i; TraceEnter(TRACE_CACHE, "GetPropertyPageList"); VariantInit(&variant); pCacheEntry->hdsaPropertyPages = DSA_Create(SIZEOF(DSPROPERTYPAGE), 4); TraceAssert(pCacheEntry->hdsaPropertyPages); if (!pCacheEntry->hdsaPropertyPages) ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate page DPA"); // build the property we are going to key off and then lets start // to walk the list of diplay specifiers checking each one for // a list of property pages. if (pAttributePrefix) StrCatW(szProperty, pAttributePrefix); StrCatW(szProperty, PROPERTY_PAGES); Trace(TEXT("Enumerating property pages from: %s"), W2T(szProperty)); if (SUCCEEDED(pDisplaySpecifier->Get(szProperty, &variant))) { hr = GetArrayContents(&variant, _AddPropertyPageItemCB, (LPVOID)pCacheEntry->hdsaPropertyPages); FailGracefully(hr, "Failed to add property pages to DSA"); VariantClear(&variant); } if (SUCCEEDED(pDisplaySpecifier->Get(PROPERTY_PAGES, &variant))) { hr = GetArrayContents(&variant, _AddPropertyPageItemCB, (LPVOID)pCacheEntry->hdsaPropertyPages); FailGracefully(hr, "Failed to add property pages to DSA"); VariantClear(&variant); } hr = S_OK; exit_gracefully: if (FAILED(hr)) FreePropertyPageList(&pCacheEntry->hdsaPropertyPages); VariantClear(&variant); TraceLeaveResult(hr); } /*----------------------------------------------------------------------------- / FreePropertyPageList / -------------------- / Free the property page list associated with a particular cache entry / / In: / pHDSA = pointer to a HDSA to be free'd, and NULL'd / / Out: / HRESULT /----------------------------------------------------------------------------*/ INT _FreePropertyPageItemCB(LPVOID p, LPVOID pData) { LPDSPROPERTYPAGE pItem = (LPDSPROPERTYPAGE)p; TraceEnter(TRACE_CACHE, "_FreePropertyPageItemCB"); TraceAssert(pItem); LocalFreeStringW(&pItem->pPageReference); TraceLeaveValue(1); } VOID FreePropertyPageList(HDSA* pHDSA) { TraceEnter(TRACE_CACHE, "FreePropertyPageList"); if (*pHDSA) DSA_DestroyCallback(*pHDSA, _FreePropertyPageItemCB, 0L); *pHDSA = NULL; TraceLeave(); } /*----------------------------------------------------------------------------- / Menu item lists /----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------- / GetMenuHandlerList / ------------------ / The "contextMenu" property on a DS object contains a list of / the menu handlers that we want to interact with. / / In: / pCacheEntry -> Cache entry to update / pAttributePrefix -> suitable prefix for getting Admin/Shell pages / pDataObject -> IDataObject for getting cached information from / / Out: / HRESULT /----------------------------------------------------------------------------*/ HRESULT CALLBACK _AddMenuHandlerCB(DWORD dwIndex, BSTR pString, LPVOID pData) { HRESULT hr; DSMENUHANDLER item; HDSA hdsa = (HDSA)pData; USES_CONVERSION; TraceEnter(TRACE_CACHE, "_AddMenuHandlerCB"); Trace(TEXT("dwIndex %08x, pString: %s"), dwIndex, W2T(pString)); hr = LocalAllocStringW(&item.pMenuReference, pString); FailGracefully(hr, "Failed to clone string"); if (-1 == DSA_AppendItem(hdsa, &item)) ExitGracefully(hr, E_FAIL, "Failed to add menu reference to DSA"); hr = S_OK; exit_gracefully: if (FAILED(hr)) LocalFreeStringW(&item.pMenuReference); TraceLeaveResult(hr); } HRESULT GetMenuHandlerList(LPCLASSCACHEENTRY pCacheEntry, LPWSTR pAttributePrefix, IADs* pDisplaySpecifier) { HRESULT hr; WCHAR szProperty[MAX_PATH] = { TEXT('\0') }; VARIANT variant; INT i; TraceEnter(TRACE_CACHE, "GetMenuHandlerList"); VariantInit(&variant); pCacheEntry->hdsaMenuHandlers = DSA_Create(SIZEOF(DSPROPERTYPAGE), 4); TraceAssert(pCacheEntry->hdsaMenuHandlers); if (!pCacheEntry->hdsaMenuHandlers) ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate page DPA"); // first try "ContextMenu" to pick up the provider specific menus if (pAttributePrefix) StrCatW(szProperty, pAttributePrefix); StrCatW(szProperty, CONTEXT_MENU); if (SUCCEEDED(pDisplaySpecifier->Get(szProperty, &variant))) { hr = GetArrayContents(&variant, _AddMenuHandlerCB, (LPVOID)pCacheEntry->hdsaMenuHandlers); FailGracefully(hr, "Failed to add property pages to DSA"); VariantClear(&variant); } if (SUCCEEDED(pDisplaySpecifier->Get(CONTEXT_MENU, &variant))) { hr = GetArrayContents(&variant, _AddMenuHandlerCB, (LPVOID)pCacheEntry->hdsaMenuHandlers); FailGracefully(hr, "Failed to add property pages to DSA"); VariantClear(&variant); } hr = S_OK; // success exit_gracefully: if (FAILED(hr)) FreeMenuHandlerList(&pCacheEntry->hdsaMenuHandlers); VariantClear(&variant); TraceLeaveResult(hr); } /*----------------------------------------------------------------------------- / FreeMenuHandlerList / ------------=------ / Free the list of menu items that are stored in the cache DSA. / / In: / pHDSA = pointer to a HDSA to be free'd, and NULL'd / / Out: / HRESULT /----------------------------------------------------------------------------*/ INT _FreeMenuHandlerCB(LPVOID p, LPVOID pData) { LPDSMENUHANDLER pItem = (LPDSMENUHANDLER)p; TraceEnter(TRACE_CACHE, "_FreeMenuHandlerCB"); TraceAssert(pItem); LocalFreeStringW(&pItem->pMenuReference); TraceLeaveValue(1); } VOID FreeMenuHandlerList(HDSA* pHDSA) { TraceEnter(TRACE_CACHE, "FreeMenuHandlerList"); if (*pHDSA) DSA_DestroyCallback(*pHDSA, _FreeMenuHandlerCB, 0L); *pHDSA = NULL; TraceLeave(); } /*----------------------------------------------------------------------------- / Property page list /----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------- / GetIconList / ----------- / Get the icon list from the class specifier. Bind to the class specifier / and then enumerate the icon property. We store an array which contains / the icon locations for multiple states, therefore as we are called to / add the entries we clear out that previous index. / / In: / pCacheEntry -> Cache entry to update / pDataObject -> pData object for extra information / / Out: / HRESULT /----------------------------------------------------------------------------*/ HRESULT CALLBACK _AddIconToCacheEntryCB(DWORD dwIndex, BSTR pString, LPVOID pData) { HRESULT hr; LPCLASSCACHEENTRY pCacheEntry = (LPCLASSCACHEENTRY)pData; USES_CONVERSION; TraceEnter(TRACE_CACHE, "_AddIconToCacheEntryCB"); Trace(TEXT("dwIndex %08x, pString: %s"), dwIndex, W2T(pString)); if (dwIndex < ARRAYSIZE(pCacheEntry->pIconName)) { LocalFreeStringW(&pCacheEntry->pIconName[dwIndex]); hr = LocalAllocStringW(&pCacheEntry->pIconName[dwIndex], pString); FailGracefully(hr, "Failed to copy icon location"); } hr = S_OK; exit_gracefully: TraceLeaveResult(hr); } HRESULT GetIconList(LPCLASSCACHEENTRY pCacheEntry, IADs* pDisplaySpecifier) { HRESULT hr; VARIANT variant; USES_CONVERSION; TraceEnter(TRACE_CACHE, "GetIconList"); VariantInit(&variant); if (SUCCEEDED(pDisplaySpecifier->Get(ICON_LOCATION, &variant))) { hr = GetArrayContents(&variant, _AddIconToCacheEntryCB, (LPVOID)pCacheEntry); FailGracefully(hr, "Failed to get the icon list into the cache entry"); } hr = S_OK; // success exit_gracefully: if (FAILED(hr)) FreeIconList(pCacheEntry); VariantClear(&variant); TraceLeaveResult(hr); } /*----------------------------------------------------------------------------- / FreeIconList / ------------ / Clear out the icon list, this an array of string pointers allocated by / LocalAllocString. / / In: / pCacheEntry -> Cache entry to update / / Out: / HRESULT /----------------------------------------------------------------------------*/ VOID FreeIconList(LPCLASSCACHEENTRY pCacheEntry) { TraceEnter(TRACE_CACHE, "FreeIconList"); for (INT i = 0 ; i < ARRAYSIZE(pCacheEntry->pIconName); i++) LocalFreeStringW(&pCacheEntry->pIconName[i]); TraceLeave(); } /*----------------------------------------------------------------------------- / Attribute Name helpers /----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------- / GetAttributeNames / ----------------- / Get the attribute names given the cache entry and the variant to store / them into. / / In: / pCacheEntry -> cache entry to be filled / pDataObject -> dataobject used for IDataObject caching / / Out: / HRESULT /----------------------------------------------------------------------------*/ VOID _AddAttributeName(HDPA hdpaAttributeNames, LPWSTR pName, LPWSTR pDisplayName, DWORD dwFlags, HDPA hdpaNewAttributes) { HRESULT hr; LPATTRIBUTENAME pAttributeName = NULL; USES_CONVERSION; TraceEnter(TRACE_CACHE, "_AddAttributeName"); Trace(TEXT("pName: %s"), W2T(pName)); Trace(TEXT("pDisplayName: %s"), W2T(pDisplayName)); pAttributeName = (LPATTRIBUTENAME)LocalAlloc(LPTR, SIZEOF(ATTRIBUTENAME)); TraceAssert(pAttributeName); if (!pAttributeName) ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate ATTRIBUTENAME"); //pAttributeName->pName = NULL; //pAttributeName->pDisplayName = NULL; pAttributeName->dwADsType = ADSTYPE_UNKNOWN; pAttributeName->dwFlags = dwFlags; hr = LocalAllocStringW(&pAttributeName->pName, pName); FailGracefully(hr, "Failed to allocate attribute name") hr = LocalAllocStringW(&pAttributeName->pDisplayName, pDisplayName); FailGracefully(hr, "Failed to allocate display name"); if (-1 == DPA_AppendPtr(hdpaAttributeNames, pAttributeName)) ExitGracefully(hr, E_OUTOFMEMORY, "Failed to add to the DPA"); // do we need to add the attribute to the new "attribtes list"? Trace(TEXT("About to search cache for: %s"), W2T(pName)); if (g_hdpaPropCache) { PROPCACHEENTRY pce = { 0 }; pce.pName = pName; if (-1 == DPA_Search(g_hdpaPropCache, &pce, 0, _ComparePropCacheEntry, NULL, DPAS_SORTED)) { hr = StringDPA_AppendStringW(hdpaNewAttributes, pName, NULL); FailGracefully(hr, "Failed to add the property to the new attribute list"); } } else { hr = StringDPA_AppendStringW(hdpaNewAttributes, pName, NULL); FailGracefully(hr, "Failed to add the property to the new attribute list"); } hr = S_OK; exit_gracefully: if (FAILED(hr)) _FreeAttributeNameCB(pAttributeName, NULL); TraceLeave(); } INT _CompareAttributeNameCB(LPVOID p1, LPVOID p2, LPARAM lParam) { LPATTRIBUTENAME pEntry1 = (LPATTRIBUTENAME)p1; LPATTRIBUTENAME pEntry2 = (LPATTRIBUTENAME)p2; return StrCmpIW(pEntry1->pName, pEntry2->pName); } HRESULT GetAttributeNames(LPCLASSCACHEENTRY pCacheEntry, LPCLASSCACHEGETINFO pccgi, IADs* pDisplaySpecifier) { HRESULT hr; LONG l, lower, upper; LPVARIANT pArray = NULL; HDPA hdpaNewAttributes = NULL; VARIANT variant; WCHAR szProperty[MAX_PATH], szDisplayName[MAX_PATH], szHide[10]; DWORD dwFlags; IDirectorySchemaMgmt *pdsm = NULL; INT i; USES_CONVERSION; TraceEnter(TRACE_CACHE, "GetAttributeNames"); // allocate a DPA for storing the new attribute list into hdpaNewAttributes = DPA_Create(16); TraceAssert(hdpaNewAttributes); if (!hdpaNewAttributes) ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate new attribute DPA"); // get the property the user specified, this should be an array of // property values that will be associated with the class VariantInit(&variant); if (!pCacheEntry->hdpaAttributeNames) { pCacheEntry->hdpaAttributeNames = DPA_Create(16); TraceAssert(pCacheEntry->hdpaAttributeNames); if (!pCacheEntry->hdpaAttributeNames) ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate attribute name DSA"); } if (SUCCEEDED(pDisplaySpecifier->Get(ATTRIBUTE_NAMES, &variant))) { if (V_VT(&variant) == VT_BSTR) { // Parse the name from the string format [,] // and add it to the property DPA we have been given. GetStringElementW(V_BSTR(&variant), 0, szProperty, ARRAYSIZE(szProperty)); GetStringElementW(V_BSTR(&variant), 1, szDisplayName, ARRAYSIZE(szDisplayName)); dwFlags = 0x0; if (SUCCEEDED(GetStringElementW(V_BSTR(&variant), 2, szHide, ARRAYSIZE(szHide)))) { Trace(TEXT("Parsing hide flag: %s"), W2T(szHide)); dwFlags = StringToDWORD(szHide); } _AddAttributeName(pCacheEntry->hdpaAttributeNames, szProperty, szDisplayName, dwFlags, hdpaNewAttributes); } else { if (V_VT(&variant) != (VT_VARIANT|VT_ARRAY)) ExitGracefully(hr, E_FAIL, "Exported VARIANT array as result from property query"); hr = SafeArrayGetLBound(V_ARRAY(&variant), 1, (LONG*)&lower); FailGracefully(hr, "Failed to get lower bounds of array"); hr = SafeArrayGetUBound(V_ARRAY(&variant), 1, (LONG*)&upper); FailGracefully(hr, "Failed to get upper bounds of array"); hr = SafeArrayAccessData(V_ARRAY(&variant), (LPVOID*)&pArray); FailGracefully(hr, "Failed to get 'safe' accessor to array"); for (l = lower; l <= upper ; l++) { LPVARIANT pVariant = &pArray[l]; TraceAssert(pVariant); if (V_VT(pVariant) == VT_BSTR ) { // Parse the name from the string format [,] // and add it to the property DPA we have been given. GetStringElementW(V_BSTR(pVariant), 0, szProperty, ARRAYSIZE(szProperty)); GetStringElementW(V_BSTR(pVariant), 1, szDisplayName, ARRAYSIZE(szDisplayName)); if (SUCCEEDED(GetStringElementW(V_BSTR(pVariant), 2, szHide, ARRAYSIZE(szHide)))) { Trace(TEXT("Parsing hide flag: %s"), W2T(szHide)); dwFlags = StringToDWORD(szHide); } _AddAttributeName(pCacheEntry->hdpaAttributeNames, szProperty, szDisplayName, dwFlags, hdpaNewAttributes); } } DPA_Sort(pCacheEntry->hdpaAttributeNames, _CompareAttributeNameCB, NULL); } } // walk the cache adding the entries hr = _GetDsSchemaMgmt(pccgi, &pdsm); FailGracefully(hr, "Failed to get schema management object"); for (i = 0 ; i < DPA_GetPtrCount(hdpaNewAttributes) ; i++) { LPCWSTR pAttributeName = StringDPA_GetStringW(hdpaNewAttributes, i); TraceAssert(pAttributeName); hr = _AddPropToPropCache(pccgi, pdsm, pAttributeName, NULL); FailGracefully(hr, "Failed to add property to cache"); } hr = S_OK; exit_gracefully: if (FAILED(hr)) FreeAttributeNames(&pCacheEntry->hdpaAttributeNames); VariantClear(&variant); DoRelease(pdsm); if (g_hdpaPropCache) { TraceMsg("Sorting the property cache"); DPA_Sort(g_hdpaPropCache, _ComparePropCacheEntry, NULL); } StringDPA_Destroy(&hdpaNewAttributes); TraceLeaveResult(hr); } /*----------------------------------------------------------------------------- / FreeAttributeNames / -------------------- / Free the DSA containiing the attribute names and their display / name. / / In: / pHDSA = pointer to a HDSA to be free'd, and NULL'd / / Out: / HRESULT /----------------------------------------------------------------------------*/ INT _FreeAttributeNameCB(LPVOID p, LPVOID pData) { LPATTRIBUTENAME pItem = (LPATTRIBUTENAME)p; TraceEnter(TRACE_CACHE, "_FreeAttributeNameCB"); TraceAssert(pItem && pItem->pName && pItem->pDisplayName); LocalFreeStringW(&pItem->pName); LocalFreeStringW(&pItem->pDisplayName); LocalFree(pItem); TraceLeaveValue(1); } VOID FreeAttributeNames(HDPA* pHDPA) { TraceEnter(TRACE_CACHE, "FreeAttributeNames"); if (*pHDPA) { DPA_DestroyCallback(*pHDPA, _FreeAttributeNameCB, 0L); *pHDPA = NULL; } TraceLeave(); } /*----------------------------------------------------------------------------- / Property cache helpers and fillers /----------------------------------------------------------------------------*/ // // cache house keeping functions (delete and compare) // INT _ComparePropCacheEntry(LPVOID p1, LPVOID p2, LPARAM lParam) { LPPROPCACHEENTRY pEntry1 = (LPPROPCACHEENTRY)p1; LPPROPCACHEENTRY pEntry2 = (LPPROPCACHEENTRY)p2; return StrCmpIW(pEntry1->pName, pEntry2->pName); } VOID _FreePropCacheEntry(LPPROPCACHEENTRY *ppCacheEntry) { if (*ppCacheEntry) { LPPROPCACHEENTRY pCacheEntry = *ppCacheEntry; LocalFreeStringW(&pCacheEntry->pName); LocalFree(pCacheEntry); *ppCacheEntry = NULL; } } // // get the IDirectorySchemaManagement object for the server // HRESULT _GetDsSchemaMgmt(LPCLASSCACHEGETINFO pccgi, IDirectorySchemaMgmt **ppdsm) { HRESULT hres; IADs *pRootDSE = NULL; VARIANT variant; LPWSTR pszPath = NULL; LPWSTR pszServer = pccgi->pServer; LPWSTR pszMachineServer = NULL; INT cchPath; USES_CONVERSION; TraceEnter(TRACE_CACHE, "_GetDsSchemaMgmt"); *ppdsm = NULL; VariantInit(&variant); hres = GetCacheInfoRootDSE(pccgi, &pRootDSE); if ((hres == HRESULT_FROM_WIN32(ERROR_NO_SUCH_DOMAIN)) && !pccgi->pServer) { TraceMsg("Failed to get the RootDSE from the server - not found"); DSROLE_PRIMARY_DOMAIN_INFO_BASIC *pInfo; if (DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (BYTE**)&pInfo) == WN_SUCCESS) { if (pInfo->DomainNameDns) { Trace(TEXT("Machine domain is: %s"), W2T(pInfo->DomainNameDns)); CLASSCACHEGETINFO ccgi = *pccgi; ccgi.pServer = pInfo->DomainNameDns; hres = GetCacheInfoRootDSE(&ccgi, &pRootDSE); if (SUCCEEDED(hres)) { hres = LocalAllocStringW(&pszMachineServer, pInfo->DomainNameDns); pszServer = pszMachineServer; } } DsRoleFreeMemory(pInfo); } } FailGracefully(hres, "Failed to get the RootDSE"); hres = pRootDSE->Get(L"defaultNamingContext", &variant); FailGracefully(hres, "Failed to get default naming context for this object"); if (V_VT(&variant) != VT_BSTR) ExitGracefully(hres, E_FAIL, "defaultNamingContext is not a BSTR"); cchPath = lstrlenW(L"LDAP://") + lstrlenW(V_BSTR(&variant)); if (pszServer) cchPath += 1 + lstrlenW(pszServer); hres = LocalAllocStringLenW(&pszPath, cchPath); FailGracefully(hres, "Failed to allocate the buffer for the name"); StrCpyW(pszPath, L"LDAP://"); if (pszServer) { StrCatW(pszPath, pszServer); StrCatW(pszPath, L"/"); } StrCatW(pszPath, V_BSTR(&variant)); Trace(TEXT("Default naming context is (with prefix) %s"), W2T(pszPath)); hres = ADsOpenObject(pszPath, pccgi->pUserName, pccgi->pPassword, (pccgi->dwFlags & CLASSCACHE_SIMPLEAUTHENTICATE) ? 0:ADS_SECURE_AUTHENTICATION, IID_IDirectorySchemaMgmt, (void **)ppdsm); FailGracefully(hres, "Failed to open the default naming context object"); exit_gracefully: LocalFreeStringW(&pszPath); LocalFreeStringW(&pszMachineServer); VariantClear(&variant); DoRelease(pRootDSE); TraceLeaveResult(hres); } // // allocate the cache (if needed) and add a new entry to it, reading the schema to find out the type // of attribute this is. // HRESULT _AddPropToPropCache(LPCLASSCACHEGETINFO pccgi, IDirectorySchemaMgmt *pdsm, LPCWSTR pAttributeName, ADSTYPE *padt) { HRESULT hres; PADS_ATTR_DEF pad = NULL; WCHAR szAttributeName[MAX_PATH]; DWORD dwReturned; LPPROPCACHEENTRY pCacheEntry = NULL; USES_CONVERSION; TraceEnter(TRACE_CACHE, "_AddPropToPropCache"); // compute the property name StrCpyW(szAttributeName, pAttributeName); if (pccgi->pServer) { StrCatW(szAttributeName, L":"); StrCatW(szAttributeName, pccgi->pServer); } // check to see if we have a cache already if (!g_hdpaPropCache) { g_hdpaPropCache = DPA_Create(16); TraceAssert(g_hdpaPropCache); if (!g_hdpaPropCache) ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate property cache"); } // allocate a new cache entry, fill it and add it to the DPA. pCacheEntry = (LPPROPCACHEENTRY)LocalAlloc(LPTR, SIZEOF(PROPCACHEENTRY)); TraceAssert(pCacheEntry); if (!pCacheEntry) ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate new property cache entry"); // pCacheEntry->pName = NULL; pCacheEntry->dwADsType = ADSTYPE_UNKNOWN; // fill the record from the schema information we have hres = LocalAllocStringW(&pCacheEntry->pName, szAttributeName); FailGracefully(hres, "Failed to add name to entry"); hres = pdsm->EnumAttributes((LPWSTR *)&pAttributeName, 1, &pad, &dwReturned); FailGracefully(hres, "Failed to read the property information"); if (dwReturned) { pCacheEntry->dwADsType = pad->dwADsType; } else { TraceMsg("*** Failed to read property type from schema, defaulting to ADSTYPE_UNKNOWN ***"); } Trace(TEXT("Attribute: %s is %08x"), W2CT(pCacheEntry->pName), pCacheEntry->dwADsType); if (-1 == DPA_AppendPtr(g_hdpaPropCache, pCacheEntry)) ExitGracefully(hres, E_OUTOFMEMORY, "Failed to add the entry to the property cache DPA"); hres = S_OK; exit_gracefully: if (FAILED(hres)) _FreePropCacheEntry(&pCacheEntry); if (pad) FreeADsMem(pad); if (SUCCEEDED(hres) && padt) *padt = pCacheEntry->dwADsType; TraceLeaveResult(hres); } /*----------------------------------------------------------------------------- / ClassCache_GetADsTypeFromAttribute / ---------------------------------- / Given a property name return the ADsType for it, this is based off the / global property cache, not the display specifier information we have. / / In: / pccgi -> CLASSCACHEGETINFO structure (credentials) / pAttributeName -> attribute name to look up / / Out: / ADSTYPE /----------------------------------------------------------------------------*/ ADSTYPE ClassCache_GetADsTypeFromAttribute(LPCLASSCACHEGETINFO pccgi, LPCWSTR pAttributeName) { ADSTYPE dwResult = ADSTYPE_UNKNOWN; WCHAR szAttributeName[MAX_PATH]; INT iFound = -1; IDirectorySchemaMgmt *pdsm = NULL; USES_CONVERSION; TraceEnter(TRACE_CACHE, "ClassCache_GetADsTypeFromAttribute"); Trace(TEXT("Looking up property in cache: %s"), W2CT(pAttributeName)); // get the lock on the cache, then search it for the property we have been given TraceMsg("Waiting to get cache lock for property cache"); EnterCriticalSection(&g_csPropCache); Trace(TEXT("Lock aquired, building key for: %s"), W2CT(pAttributeName)); StrCpyW(szAttributeName, pAttributeName); if (pccgi->pServer) { StrCatW(szAttributeName, L":"); StrCatW(szAttributeName, pccgi->pServer); } Trace(TEXT("Key for attribute in cache is: %s"), W2T(szAttributeName)); // and search for it... if (g_hdpaPropCache) { PROPCACHEENTRY pce = { 0 }; pce.pName = (LPWSTR)szAttributeName; iFound = DPA_Search(g_hdpaPropCache, &pce, 0, _ComparePropCacheEntry, NULL, DPAS_SORTED); Trace(TEXT("Entry found in cache at %d"), iFound); } // iFound != -1 if we found something, otherwise we need to allocate a new entry if (iFound != -1) { LPPROPCACHEENTRY pCacheEntry = (LPPROPCACHEENTRY)DPA_GetPtr(g_hdpaPropCache, iFound); if (pCacheEntry) { dwResult = pCacheEntry->dwADsType; Trace(TEXT("Property found in cache, result %d"), dwResult); } } else if (SUCCEEDED(_GetDsSchemaMgmt(pccgi, &pdsm))) { if (SUCCEEDED(_AddPropToPropCache(pccgi, pdsm, pAttributeName, &dwResult))) { TraceMsg("Added the property to the cache, therefore sorting"); DPA_Sort(g_hdpaPropCache, _ComparePropCacheEntry, NULL); } } LeaveCriticalSection(&g_csPropCache); DoRelease(pdsm); TraceLeaveValue(dwResult); }