mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1723 lines
53 KiB
1723 lines
53 KiB
#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 "<attributePrefix>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 <property>[,<display name>]
|
|
// 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 <property>[,<display name>]
|
|
// 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);
|
|
}
|