//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1997 - 1999
//
//  File:       schema.cpp
//
//  This file contains the implementation of the Schema Cache
//
//--------------------------------------------------------------------------

#include "pch.h"
#include "sddl.h"
#include "sddlp.h"

//
// CSchemaCache object definition
//
#include "schemap.h"

PSCHEMACACHE g_pSchemaCache = NULL;


//
// Page size used for paging query result sets (better performance)
//
#define PAGE_SIZE       16

//
// The following array defines the permission names for DS objects.
//
SI_ACCESS g_siDSAccesses[] =
{
    { &GUID_NULL, DS_GENERIC_ALL,           MAKEINTRESOURCE(IDS_DS_GENERIC_ALL),        SI_ACCESS_GENERAL | SI_ACCESS_SPECIFIC },
    { &GUID_NULL, DS_GENERIC_READ,          MAKEINTRESOURCE(IDS_DS_GENERIC_READ),       SI_ACCESS_GENERAL },
    { &GUID_NULL, DS_GENERIC_WRITE,         MAKEINTRESOURCE(IDS_DS_GENERIC_WRITE),      SI_ACCESS_GENERAL },
    { &GUID_NULL, ACTRL_DS_LIST,            MAKEINTRESOURCE(IDS_ACTRL_DS_LIST),         SI_ACCESS_SPECIFIC },
    { &GUID_NULL, ACTRL_DS_LIST_OBJECT,     MAKEINTRESOURCE(IDS_ACTRL_DS_LIST_OBJECT),  SI_ACCESS_SPECIFIC },
    { &GUID_NULL, ACTRL_DS_READ_PROP,       MAKEINTRESOURCE(IDS_ACTRL_DS_READ_PROP),    SI_ACCESS_SPECIFIC | SI_ACCESS_PROPERTY },
    { &GUID_NULL, ACTRL_DS_WRITE_PROP,      MAKEINTRESOURCE(IDS_ACTRL_DS_WRITE_PROP),   SI_ACCESS_SPECIFIC | SI_ACCESS_PROPERTY },
    { &GUID_NULL, ACTRL_DS_WRITE_PROP|ACTRL_DS_READ_PROP, MAKEINTRESOURCE(IDS_ACTRL_DS_READ_WRITE_PROP),   },
    { &GUID_NULL, DELETE,                   MAKEINTRESOURCE(IDS_ACTRL_DELETE),          SI_ACCESS_SPECIFIC },
    { &GUID_NULL, ACTRL_DS_DELETE_TREE,     MAKEINTRESOURCE(IDS_ACTRL_DS_DELETE_TREE),  SI_ACCESS_SPECIFIC },
    { &GUID_NULL, READ_CONTROL,             MAKEINTRESOURCE(IDS_ACTRL_READ_CONTROL),    SI_ACCESS_SPECIFIC },
    { &GUID_NULL, WRITE_DAC,                MAKEINTRESOURCE(IDS_ACTRL_CHANGE_ACCESS),   SI_ACCESS_SPECIFIC },
    { &GUID_NULL, WRITE_OWNER,              MAKEINTRESOURCE(IDS_ACTRL_CHANGE_OWNER),    SI_ACCESS_SPECIFIC },
    { &GUID_NULL, 0,                        MAKEINTRESOURCE(IDS_NO_ACCESS),             0 },
    { &GUID_NULL, ACTRL_DS_SELF,            MAKEINTRESOURCE(IDS_ACTRL_DS_SELF),         SI_ACCESS_SPECIFIC },
    { &GUID_NULL, ACTRL_DS_CONTROL_ACCESS,  MAKEINTRESOURCE(IDS_ACTRL_DS_CONTROL_ACCESS),SI_ACCESS_SPECIFIC },
    { &GUID_NULL, ACTRL_DS_CREATE_CHILD,    MAKEINTRESOURCE(IDS_ACTRL_DS_CREATE_CHILD), SI_ACCESS_CONTAINER | SI_ACCESS_SPECIFIC },
    { &GUID_NULL, ACTRL_DS_DELETE_CHILD,    MAKEINTRESOURCE(IDS_ACTRL_DS_DELETE_CHILD), SI_ACCESS_CONTAINER | SI_ACCESS_SPECIFIC },
    { &GUID_NULL, ACTRL_DS_DELETE_CHILD|ACTRL_DS_CREATE_CHILD,    MAKEINTRESOURCE(IDS_ACTRL_DS_CREATE_DELETE_CHILD), 0 },  //This won't show up as checkbox but used to display in advanced page.
};
#define g_iDSRead       1   // DS_GENERIC_READ
#define g_iDSListObject 4   // ACTRL_DS_LIST_OBJECT
#define g_iDSProperties 5   // Read/Write properties
#define g_iDSDefAccess  g_iDSRead
#define g_iDSAllExtRights 15
#define g_iDSAllValRights 14
#define g_iDSDeleteTree	  9


//
// The following array defines the inheritance types common to all DS containers.
//
SI_INHERIT_TYPE g_siDSInheritTypes[] =
{
    { &GUID_NULL, 0,                                        MAKEINTRESOURCE(IDS_DS_CONTAINER_ONLY)     },
    { &GUID_NULL, CONTAINER_INHERIT_ACE,                    MAKEINTRESOURCE(IDS_DS_CONTAINER_SUBITEMS) },
    { &GUID_NULL, CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE, MAKEINTRESOURCE(IDS_DS_SUBITEMS_ONLY)      },
};

//
//Used to store some temp info
//
typedef struct _temp_info
{
    LPCGUID pguid;
    DWORD   dwFilter;
    LPCWSTR pszLdapName;
    WCHAR   szDisplayName[ANYSIZE_ARRAY];
} TEMP_INFO, *PTEMP_INFO;


//
// Helper functions for cleaning up DPA lists
//
int CALLBACK
_LocalFreeCB(LPVOID pVoid, LPVOID /*pData*/)
{
    LocalFree(pVoid);
    return 1;
}

void
DestroyDPA(HDPA hList)
{
    if (hList != NULL)
        DPA_DestroyCallback(hList, _LocalFreeCB, 0);
}
//
// Callback function for merging. Passed to DPA_Merge
//
LPVOID CALLBACK _Merge(UINT , LPVOID pvDest, LPVOID , LPARAM )
{
    return pvDest;
}

BSTR
GetFilterFilePath(void)
{
    WCHAR szFilterFile[MAX_PATH];
    UINT cch = GetSystemDirectory(szFilterFile, ARRAYSIZE(szFilterFile));
    if (0 == cch || cch >= ARRAYSIZE(szFilterFile))
        return NULL;
    if (szFilterFile[cch-1] != L'\\')
        szFilterFile[cch++] = L'\\';
    lstrcpynW(szFilterFile + cch, c_szFilterFile, ARRAYSIZE(szFilterFile) - cch);
    return SysAllocString(szFilterFile);
}


//
// Local prototypes
//
HRESULT
Schema_Search(LPWSTR pszSchemaSearchPath,
              LPCWSTR pszFilter,
              HDPA *phCache,
              BOOL bProperty);

//
// C wrappers for the schema cache object
//
HRESULT
SchemaCache_Create(LPCWSTR pszServer)
{
    HRESULT hr = S_OK;

    if (g_pSchemaCache == NULL)
    {
        g_pSchemaCache = new CSchemaCache(pszServer);

        if (g_pSchemaCache  == NULL)
            hr = E_OUTOFMEMORY;
    }

    return hr;
}


void
SchemaCache_Destroy(void)
{
    delete g_pSchemaCache;
    g_pSchemaCache = NULL;
}


HRESULT
SchemaCache_GetInheritTypes(LPCGUID pguidObjectType,
                            DWORD dwFlags,
                            PSI_INHERIT_TYPE *ppInheritTypes,
                            ULONG *pcInheritTypes)
{
    HRESULT hr = E_UNEXPECTED;

    if (g_pSchemaCache)
        hr = g_pSchemaCache->GetInheritTypes(pguidObjectType, dwFlags, ppInheritTypes, pcInheritTypes);

    return hr;
}


HRESULT
SchemaCache_GetAccessRights(LPCGUID pguidObjectType,
                            LPCWSTR pszClassName,
                            HDPA     hAuxList,
                            LPCWSTR pszSchemaPath,
                            DWORD dwFlags,
                            PACCESS_INFO* ppAccesInfo)
{
    HRESULT hr = E_UNEXPECTED;

    if (g_pSchemaCache)
        hr = g_pSchemaCache->GetAccessRights(pguidObjectType,
                                             pszClassName,
                                             hAuxList,
                                             pszSchemaPath,
                                             dwFlags,
                                             ppAccesInfo);
    return hr;
}


HRESULT
Schema_GetDefaultSD( GUID *pSchemaIdGuid,
                     PSID pDomainSid,
					 PSID pRootDomainSid,
                     PSECURITY_DESCRIPTOR *ppSD )
{
    HRESULT hr = E_UNEXPECTED;
    if( g_pSchemaCache )
        hr = g_pSchemaCache->GetDefaultSD(pSchemaIdGuid, 
										  pDomainSid, 
										  pRootDomainSid, 
										  ppSD);

    return hr;

}


HRESULT Schema_GetObjectTypeList(GUID *pSchamaGuid,
                                 HDPA hAuxList,
                                 LPCWSTR pszSchemaPath,
                                 DWORD dwFlags,
                                 POBJECT_TYPE_LIST *ppObjectTypeList, 
                                 DWORD * pObjectTypeListCount)
{
    HRESULT hr = E_UNEXPECTED;
    if( g_pSchemaCache )
        hr = g_pSchemaCache->GetObjectTypeList( pSchamaGuid,
                                                hAuxList,
                                                pszSchemaPath,
                                                dwFlags,
                                                ppObjectTypeList, 
                                                pObjectTypeListCount);

    return hr;

}


HRESULT Schema_GetObjectTypeGuid(LPCWSTR pszClassName, LPGUID pGuid)
{
   
    if( g_pSchemaCache )
        return g_pSchemaCache->LookupClassID(pszClassName, pGuid);
    else
        return E_UNEXPECTED;
}

AUTHZ_RESOURCE_MANAGER_HANDLE Schema_GetAUTHZ_RM()
{
    if( g_pSchemaCache )
        return g_pSchemaCache->GetAuthzRM();

    return NULL;
}

//
// DPA comparison functions used for sorting and searching the cache lists
//
int CALLBACK
Schema_CompareLdapName(LPVOID p1, LPVOID p2, LPARAM lParam)
{
    int nResult = 0;
    PID_CACHE_ENTRY pEntry1 = (PID_CACHE_ENTRY)p1;
    PID_CACHE_ENTRY pEntry2 = (PID_CACHE_ENTRY)p2;
    LPCWSTR pszFind = (LPCWSTR)lParam;

    if (pEntry1)
        pszFind = pEntry1->szLdapName;

    if (pszFind && pEntry2)
    {
        nResult = CompareStringW(LOCALE_USER_DEFAULT,
                                 0,
                                 pszFind,
                                 -1,
                                 pEntry2->szLdapName,
                                 -1) - CSTR_EQUAL;
    }

    return nResult;
}


//
// Callback function used to sort based on display name
//
int CALLBACK
Schema_CompareTempDisplayName(LPVOID p1, LPVOID p2, LPARAM )
{
    int nResult = 0;
    PTEMP_INFO pti1 = (PTEMP_INFO)p1;
    PTEMP_INFO pti2 = (PTEMP_INFO)p2;

    if (pti1 && pti2)
    {
        LPCWSTR psz1 = pti1->szDisplayName;
        LPCWSTR psz2 = pti2->szDisplayName;

        if (!*psz1)
            psz1 = pti1->pszLdapName;
        if (!*psz2)
            psz2 = pti2->pszLdapName;

        // Note that we are sorting backwards
        nResult = CompareStringW(LOCALE_USER_DEFAULT,
                                 0,
                                 (LPCWSTR)psz2,
                                 -1,
                                 (LPCWSTR)psz1,
                                 -1) - CSTR_EQUAL;
    }

    return nResult;
}

//
// Callback function used to sort based on display name
//
int CALLBACK
Schema_ComparePropDisplayName(LPVOID p1, LPVOID p2, LPARAM )
{
    int nResult = 0;
    PPROP_ENTRY pti1 = (PPROP_ENTRY)p1;
    PPROP_ENTRY pti2 = (PPROP_ENTRY)p2;

    if (pti1 && pti2)
    {
        LPCWSTR psz1 = pti1->szName;
        LPCWSTR psz2 = pti2->szName;

        nResult = CompareStringW(LOCALE_USER_DEFAULT,
                                 0,
                                 (LPCWSTR)psz1,
                                 -1,
                                 (LPCWSTR)psz2,
                                 -1) - CSTR_EQUAL;
    }

    return nResult;
}

//
// DPA comparison function used for sorting the Extended Rights list
//
int CALLBACK
Schema_CompareER(LPVOID p1, LPVOID p2, LPARAM /*lParam*/)
{
    int nResult = 0;
    PER_ENTRY pEntry1 = (PER_ENTRY)p1;
    PER_ENTRY pEntry2 = (PER_ENTRY)p2;

    if (pEntry1 && pEntry2)
    {
        nResult = CompareStringW(LOCALE_USER_DEFAULT,
                                 0,
                                 pEntry1->szName,
                                 -1,
                                 pEntry2->szName,
                                 -1) - CSTR_EQUAL;

    }

    return nResult;
}



//
// CSchemaCache object implementation
//
CSchemaCache::CSchemaCache(LPCWSTR pszServer)
{
    HRESULT hr;
    IADsPathname *pPath = NULL;
    BSTR strRootDSEPath = NULL;
    IADs *pRootDSE = NULL;
    VARIANT var = {0};
    DWORD dwThreadID;
    HANDLE ahWait[2];
    TraceEnter(TRACE_SCHEMA, "CSchemaCache::CSchemaCache");

    // Initialize everything
    ZeroMemory(this, sizeof(CSchemaCache));
    m_hrClassResult = E_UNEXPECTED;
    m_hrPropertyResult = E_UNEXPECTED;
    m_nDsListObjectEnforced = -1;        
    m_hLoadLibPropWaitEvent = NULL;
    m_hLoadLibClassWaitEvent = NULL;

    m_AICommon.pAccess = g_siDSAccesses;    
    m_AICommon.cAccesses = ARRAYSIZE(g_siDSAccesses);
    m_AICommon.iDefaultAccess = g_iDSDefAccess; 
    m_AICommon.bLocalFree = FALSE;


    m_hClassCache = NULL;
    m_hPropertyCache = NULL;    
    m_pInheritTypeArray = NULL;
    m_hObjectTypeCache = NULL;
    m_hAccessInfoCache = NULL;

    ExceptionPropagatingInitializeCriticalSection(&m_ObjectTypeCacheCritSec);

    
    if (pszServer && !*pszServer)
        pszServer = NULL;

    // Create a path object for manipulating ADS paths
    hr = CoCreateInstance(CLSID_Pathname,
                          NULL,
                          CLSCTX_INPROC_SERVER,
                          IID_IADsPathname,
                          (LPVOID*)&pPath);
    FailGracefully(hr, "Unable to create ADsPathname object");

    // Build RootDSE path with server
    hr = pPath->Set((LPWSTR)c_szRootDsePath, ADS_SETTYPE_FULL);
    FailGracefully(hr, "Unable to initialize path object");
    if (pszServer)
    {
        hr = pPath->Set((LPWSTR)pszServer, ADS_SETTYPE_SERVER);
        FailGracefully(hr, "Unable to initialize path object");
    }
    hr = pPath->Retrieve(ADS_FORMAT_WINDOWS, &strRootDSEPath);
    FailGracefully(hr, "Unable to retrieve RootDSE path from path object");

    // Bind to the RootDSE object
    hr = OpenDSObject(strRootDSEPath,
                       NULL,
                       NULL,
                       ADS_SECURE_AUTHENTICATION,
                       IID_IADs,
                       (LPVOID*)&pRootDSE);
    if (FAILED(hr) && pszServer)
    {
        // Try again with no server
        SysFreeString(strRootDSEPath);

        hr = pPath->Retrieve(ADS_FORMAT_WINDOWS_NO_SERVER, &strRootDSEPath);
        FailGracefully(hr, "Unable to retrieve RootDSE path from path object");

        hr = OpenDSObject(strRootDSEPath,
                           NULL,
                           NULL,
                           ADS_SECURE_AUTHENTICATION,
                           IID_IADs,
                           (LPVOID*)&pRootDSE);
    }
    FailGracefully(hr, "Failed to bind to root DSE");

    // Build the schema root path
    hr = pRootDSE->Get((LPWSTR)c_szSchemaContext, &var);
    FailGracefully(hr, "Unable to get schema naming context");

    TraceAssert(V_VT(&var) == VT_BSTR);
    hr = pPath->Set(V_BSTR(&var), ADS_SETTYPE_DN);
    FailGracefully(hr, "Unable to initialize path object");

    hr = pPath->Retrieve(ADS_FORMAT_WINDOWS, &m_strSchemaSearchPath);
    FailGracefully(hr, "Unable to retrieve schema search path from path object");

    // Build the Extended Rights container path
    VariantClear(&var);
    hr = pRootDSE->Get((LPWSTR)c_szConfigContext, &var);
    FailGracefully(hr, "Unable to get configuration naming context");

    TraceAssert(V_VT(&var) == VT_BSTR);
    hr = pPath->Set(V_BSTR(&var), ADS_SETTYPE_DN);
    FailGracefully(hr, "Unable to initialize path object");

    hr = pPath->AddLeafElement((LPWSTR)c_szERContainer);
    FailGracefully(hr, "Unable to build Extended Rights path");

    hr = pPath->Retrieve(ADS_FORMAT_WINDOWS, &m_strERSearchPath);
    FailGracefully(hr, "Unable to retrieve Extended Rights search path from path object");
    
    //Create the Events
    m_hLoadLibPropWaitEvent = CreateEvent(NULL,
                                          TRUE,
                                          FALSE,
                                          NULL );
    m_hLoadLibClassWaitEvent = CreateEvent(NULL,
                                          TRUE,
                                          FALSE,
                                          NULL );


    if( m_hLoadLibPropWaitEvent && m_hLoadLibClassWaitEvent )
    {
        // Start a thread to enumerate the schema classes
        m_hClassThread = CreateThread(NULL,
                                      0,
                                      SchemaClassThread,
                                      this,
                                      0,
                                      &dwThreadID);

        // Start a thread to enumerate the schema properties
        m_hPropertyThread = CreateThread(NULL,
                                         0,
                                         SchemaPropertyThread,
                                         this,
                                         0,
                                         &dwThreadID);



        ahWait[0] = m_hClassThread;
        ahWait[1] = m_hPropertyThread;

        WaitForMultipleObjects(2,
                               ahWait,
                               TRUE,
                               INFINITE);
    }

exit_gracefully:

    VariantClear(&var);
    DoRelease(pRootDSE);
    DoRelease(pPath);
    SysFreeString(strRootDSEPath);
    if( m_hLoadLibPropWaitEvent )
        CloseHandle( m_hLoadLibPropWaitEvent );
    if( m_hLoadLibClassWaitEvent )
        CloseHandle( m_hLoadLibClassWaitEvent );


    TraceLeaveVoid();
}


CSchemaCache::~CSchemaCache()
{

    TraceEnter(TRACE_SCHEMA, "CSchemaCache::~CSchemaCache");

    SysFreeString(m_strSchemaSearchPath);
    SysFreeString(m_strERSearchPath);
    SysFreeString(m_strFilterFile);
    DeleteCriticalSection(&m_ObjectTypeCacheCritSec);

    DestroyDPA(m_hClassCache);
    DestroyDPA(m_hPropertyCache);
    if(m_hObjectTypeCache)
    {
        POBJECT_TYPE_CACHE pOTC = NULL;
        UINT cCount = DPA_GetPtrCount(m_hObjectTypeCache);
        for(UINT i = 0; i < cCount; ++i)
        {
            pOTC = (POBJECT_TYPE_CACHE)DPA_FastGetPtr(m_hObjectTypeCache, i);
            if(pOTC)
            {
                DestroyDPA(pOTC->hListChildObject);
                DestroyDPA(pOTC->hListExtRights);
                DestroyDPA(pOTC->hListProperty);
                DestroyDPA(pOTC->hListPropertySet);    
            }
        }
    }
    DestroyDPA(m_hObjectTypeCache);    

    if (m_hAccessInfoCache != NULL)
    {
        UINT cItems = DPA_GetPtrCount(m_hAccessInfoCache);
        PACCESS_INFO pAI = NULL;
        while (cItems > 0)
        {
            pAI = (PACCESS_INFO)DPA_FastGetPtr(m_hAccessInfoCache, --cItems);
            if(pAI && pAI->pAccess)
                LocalFree(pAI->pAccess);            
        }
    }
    DestroyDPA(m_hAccessInfoCache);        
        
    if (m_pInheritTypeArray != NULL)
        LocalFree(m_pInheritTypeArray);

    TraceMsg("CSchemaCache::~CSchemaCache exiting");
    TraceLeaveVoid();
}


LPCWSTR
CSchemaCache::GetClassName(LPCGUID pguidObjectType)
{
    LPCWSTR pszLdapName = NULL;
    PID_CACHE_ENTRY pCacheEntry;

    TraceEnter(TRACE_SCHEMACLASS, "CSchemaCache::GetClassName");

    pCacheEntry = LookupClass(pguidObjectType);

    if (pCacheEntry != NULL)
        pszLdapName = pCacheEntry->szLdapName;

    TraceLeaveValue(pszLdapName);
}


HRESULT
CSchemaCache::GetInheritTypes(LPCGUID ,
                              DWORD dwFlags,
                              PSI_INHERIT_TYPE *ppInheritTypes,
                              ULONG *pcInheritTypes)
{
    // We're going to find the inherit type array corresponding to the passed-in
    // object type - pInheritTypeArray will point to it!
    TraceEnter(TRACE_SCHEMACLASS, "CSchemaCache::GetInheritTypes");
    TraceAssert(ppInheritTypes != NULL);
    TraceAssert(pcInheritTypes != NULL);

    *pcInheritTypes = 0;
    *ppInheritTypes = NULL;

    // If the filter state is changing, free everything
    if (m_pInheritTypeArray &&
        (m_pInheritTypeArray->dwFlags & SCHEMA_NO_FILTER) != (dwFlags & SCHEMA_NO_FILTER))
    {
        LocalFree(m_pInheritTypeArray);
        m_pInheritTypeArray = NULL;
    }

    // Build m_pInheritTypeArray if necessary
    if (m_pInheritTypeArray == NULL)
    {
        BuildInheritTypeArray(dwFlags);
    }

    // Return m_pInheritTypeArray if we have it, otherwise
    // fall back on the static types
    if (m_pInheritTypeArray)
    {
        *pcInheritTypes = m_pInheritTypeArray->cInheritTypes;
        *ppInheritTypes = m_pInheritTypeArray->aInheritType;
    }
    else
    {
        TraceMsg("Returning default inherit information");
        *ppInheritTypes = g_siDSInheritTypes;
        *pcInheritTypes = ARRAYSIZE(g_siDSInheritTypes);
    }

    TraceLeaveResult(S_OK); // always succeed
}


HRESULT
CSchemaCache::GetAccessRights(LPCGUID pguidObjectType,
                              LPCWSTR pszClassName,
                              HDPA hAuxList,
                              LPCWSTR pszSchemaPath,
                              DWORD dwFlags,
                              PACCESS_INFO *ppAccessInfo)
{
    HRESULT hr = S_OK;
    HCURSOR hcur;

    TraceEnter(TRACE_SCHEMA, "CSchemaCache::GetAccessRights");
    TraceAssert(ppAccessInfo);


    BOOL bAddToCache = FALSE;
    PACCESS_INFO pAI = NULL;
    //
    // If the SCHEMA_COMMON_PERM flag is on, just return the permissions
    // that are common to all DS objects (including containers).
    //
    if (dwFlags & SCHEMA_COMMON_PERM)
    {        
        *ppAccessInfo = &m_AICommon;
        TraceLeaveResult(S_OK);
    }

    TraceAssert(pguidObjectType);
        
    EnterCriticalSection(&m_ObjectTypeCacheCritSec);


    //
    //If AuxList is null, we can return the item from cache
    //
    if(hAuxList == NULL)
    {
        //There is no Aux Class. Check the m_hAccessInfoCache if we have access right
        //for the pguidObjectType;
        if (m_hAccessInfoCache != NULL)
        {
            UINT cItems = DPA_GetPtrCount(m_hAccessInfoCache);

            while (cItems > 0)
            {
                pAI = (PACCESS_INFO)DPA_FastGetPtr(m_hAccessInfoCache, --cItems);
                //
                //Found A match.
                //
                if(pAI && 
                   IsEqualGUID(pAI->ObjectTypeGuid, *pguidObjectType) &&
                   ((pAI->dwFlags & (SI_EDIT_PROPERTIES | SI_EDIT_EFFECTIVE)) == 
                    (dwFlags & (SI_EDIT_PROPERTIES | SI_EDIT_EFFECTIVE))))    

                    break;

                pAI = NULL;
            }
            
            if(pAI)
            {
                goto exit_gracefully;    
            }
        }
        bAddToCache = TRUE;
    }
    
    pAI = (PACCESS_INFO)LocalAlloc(LPTR,sizeof(ACCESS_INFO));
    if(!pAI)
        ExitGracefully(hr, E_OUTOFMEMORY, "LocalAlloc failed");
    
    hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));

    hr = BuildAccessArray(pguidObjectType,
                          pszClassName,
                          pszSchemaPath,
                          hAuxList,
                          dwFlags,
                          &pAI->pAccess,
                          &pAI->cAccesses,
                          &pAI->iDefaultAccess);
    FailGracefully(hr, "BuildAccessArray Failed");

    if(bAddToCache)
    {
        if(!m_hAccessInfoCache)
            m_hAccessInfoCache = DPA_Create(4);
    
        if(!m_hAccessInfoCache)
            ExitGracefully(hr, E_OUTOFMEMORY, "DPA_Create Failed");

        pAI->dwFlags = dwFlags;
        pAI->ObjectTypeGuid = *pguidObjectType;
        DPA_AppendPtr(m_hAccessInfoCache, pAI);
    }
    
    //
    //If item is added to cache, don't localfree it. It will be free when 
    //DLL is unloaded
    //
    pAI->bLocalFree = !bAddToCache;
    
    SetCursor(hcur);

exit_gracefully:

    if(FAILED(hr))
    {
        if(pAI)
        {
            LocalFree(pAI);
            pAI = NULL;
        }
    }

    *ppAccessInfo = pAI;

    LeaveCriticalSection(&m_ObjectTypeCacheCritSec);

    TraceLeaveResult(hr);
}

 

HRESULT 
CSchemaCache::GetDefaultSD(GUID *pSchemaIDGuid, 
						   PSID pDomainSid, 
						   PSID pRootDomainSid, 
						   PSECURITY_DESCRIPTOR *ppSD)
{
    TraceEnter(TRACE_SCHEMA, "CSchemaCache::GetDefaultSD");
    TraceAssert( pSchemaIDGuid != NULL);
    TraceAssert( ppSD != NULL );

    HRESULT hr = S_OK;

	if( (pDomainSid && !IsValidSid(pDomainSid)) ||
		 (pRootDomainSid && !IsValidSid(pRootDomainSid)) )
		 return E_INVALIDARG;

    LPWSTR pszDestData = NULL;
    IDirectorySearch * IDs = NULL;
    ADS_SEARCH_HANDLE hSearchHandle=NULL;  
    LPWSTR lpszSchemaGuidFilter = L"(schemaIdGuid=%s)";
    LPWSTR pszAttr[] = {L"defaultSecurityDescriptor"};
    ADS_SEARCH_COLUMN col;
    WCHAR szSearchBuffer[MAX_PATH];

    hr = ADsEncodeBinaryData( (PBYTE)pSchemaIDGuid,
                          sizeof(GUID),
                          &pszDestData  );
    FailGracefully(hr, "ADsEncodeBinaryData Failed");
    
    wsprintf(szSearchBuffer, lpszSchemaGuidFilter,pszDestData);
    
   
   //We have Filter Now

   //Search in Configuration Contianer
    hr = OpenDSObject(  m_strSchemaSearchPath,
                         NULL,
                         NULL,
                         ADS_SECURE_AUTHENTICATION,
                         IID_IDirectorySearch,
                         (void **)&IDs );
    FailGracefully(hr, "OpenDSObject Failed");

    hr = IDs->ExecuteSearch(szSearchBuffer,
                           pszAttr,
                           1,
                           &hSearchHandle );

    FailGracefully(hr, "Search in Schema  Failed");


    hr = IDs->GetFirstRow(hSearchHandle);
    if( hr == S_OK )
    {  
        //Get Guid
        hr = IDs->GetColumn( hSearchHandle, pszAttr[0], &col );
        FailGracefully(hr, "Failed to get column from search result");

        if(pDomainSid && pRootDomainSid)
        {
            if(!ConvertStringSDToSDDomain(pDomainSid,
                                          pRootDomainSid,
                                          (LPCWSTR)(LPWSTR)col.pADsValues->CaseIgnoreString,
                                          SDDL_REVISION_1,
                                          ppSD,
                                          NULL )) 
            {
                hr = GetLastError();
                IDs->FreeColumn( &col );
                ExitGracefully(hr, E_FAIL, "Unable to convert String SD to SD");
            }
        }
        else
        {
            if ( !ConvertStringSecurityDescriptorToSecurityDescriptor( (LPCWSTR)(LPWSTR)col.pADsValues->CaseIgnoreString,
                                                                        SDDL_REVISION_1,
                                                                        ppSD,
                                                                        NULL ) ) 
            {
                hr = GetLastError();
                IDs->FreeColumn( &col );
                ExitGracefully(hr, E_FAIL, "Unable to convert String SD to SD");
            }
        }
        IDs->FreeColumn( &col );         
    }
    else
        ExitGracefully(hr, E_FAIL, "Schema search resulted in zero rows");
    
    

exit_gracefully:

    if( IDs )
    {
      if( hSearchHandle )
         IDs->CloseSearchHandle( hSearchHandle );
      IDs->Release();
    }
    FreeADsMem(pszDestData);
    TraceLeaveResult(hr);
}



VOID AddOTLToList( POBJECT_TYPE_LIST pOTL, WORD Level, LPGUID pGuidObject )
{
    (pOTL)->Level = Level;
    (pOTL)->ObjectType = pGuidObject;
}

//Get the ObjectTypeList for pSchemaGuid class

OBJECT_TYPE_LIST g_DefaultOTL[] = {
                                    {0, 0, (LPGUID)&GUID_NULL},
                                  };
HRESULT
CSchemaCache::GetObjectTypeList( GUID *pguidObjectType,
                                 HDPA hAuxList,
                                 LPCWSTR pszSchemaPath,
                                 DWORD dwFlags,
                                 POBJECT_TYPE_LIST *ppObjectTypeList,                                  
                                 DWORD * pObjectTypeListCount)
{
    TraceEnter(TRACE_SCHEMA, "CSchemaCache::GetObjectTypeList");
    TraceAssert( pguidObjectType != NULL);
    TraceAssert( ppObjectTypeList != NULL );
    TraceAssert( pObjectTypeListCount != NULL );
    TraceAssert(pszSchemaPath != NULL);


    HRESULT hr = S_OK;
    //
    //Lists
    //
    HDPA hExtRightList = NULL;
    HDPA hPropSetList = NULL;
    HDPA hPropertyList = NULL;
    HDPA hClassList = NULL;
    //
    //List counts
    //
    ULONG cExtendedRights = 0;
    ULONG cPropertySets = 0;
    UINT cProperties = 0;
    UINT cChildClasses = 0;

    POBJECT_TYPE_LIST pOTL = NULL;
    POBJECT_TYPE_LIST pTempOTL = NULL;

    LPCWSTR pszClassName = NULL;
    UINT cGuidIndex = 0;


    if( dwFlags & SCHEMA_COMMON_PERM )
    {
        *ppObjectTypeList = 
            (POBJECT_TYPE_LIST)LocalAlloc(LPTR,sizeof(OBJECT_TYPE_LIST)*ARRAYSIZE(g_DefaultOTL));
        if(!*ppObjectTypeList)
            TraceLeaveResult(E_OUTOFMEMORY);
        //
        //Note that default OTL is entry with all zero,thatz what LPTR does.
        //so there is no need to copy
        //
        *pObjectTypeListCount = ARRAYSIZE(g_DefaultOTL);        
        TraceLeaveResult(S_OK);
    }

    EnterCriticalSection(&m_ObjectTypeCacheCritSec);

    //
    // Lookup the name of this class
    //
    if (pszClassName == NULL)
        pszClassName = GetClassName(pguidObjectType);

    if(!pszClassName)
         ExitGracefully(hr, E_UNEXPECTED, "Unknown child object GUID");
    
    //
    // Get the list of Extended Rights for this page
    //
    if (pguidObjectType &&
        SUCCEEDED(GetExtendedRightsForNClasses(m_strERSearchPath,
                                               pguidObjectType,
                                               hAuxList,
                                               &hExtRightList,
                                               &hPropSetList)))

    {
        if(hPropSetList)
            cPropertySets = DPA_GetPtrCount(hPropSetList);
        if(hExtRightList)
            cExtendedRights = DPA_GetPtrCount(hExtRightList);
    }

    //
    //Get the child classes
    //
    if( pguidObjectType &&
        SUCCEEDED(GetChildClassesForNClasses(pguidObjectType,
                                             pszClassName,
                                             hAuxList,
                                             pszSchemaPath,
                                             &hClassList)))
    {

        if(hClassList)
            cChildClasses = DPA_GetPtrCount(hClassList);        
    }
     
    //
    //Get the properties for the class
    //        
    if (pguidObjectType &&
        SUCCEEDED(GetPropertiesForNClasses(pguidObjectType,
                                           pszClassName,
                                           hAuxList,
                                           pszSchemaPath,
                                           &hPropertyList)))
    {
        if(hPropertyList)
            cProperties = DPA_GetPtrCount(hPropertyList);
        
    }
    
    pOTL = (POBJECT_TYPE_LIST)LocalAlloc(LPTR, 
                                         (cPropertySets + 
                                          cExtendedRights + 
                                          cChildClasses + 
                                          cProperties +
                                          1)* sizeof(OBJECT_TYPE_LIST)); 
                                                   
    if(!pOTL)
        ExitGracefully(hr, E_OUTOFMEMORY, "Unable to create POBJECT_TYPE_LIST");

    pTempOTL = pOTL;

    //
    //First Add the entry corresponding to Object
    //
    AddOTLToList(pTempOTL, 
                 ACCESS_OBJECT_GUID, 
                 pguidObjectType);
    pTempOTL++;
    cGuidIndex++;    
    
    UINT i, j;
    for (i = 0; i < cExtendedRights; i++)
    {
        PER_ENTRY pER = (PER_ENTRY)DPA_FastGetPtr(hExtRightList, i);
        AddOTLToList(pTempOTL, 
                     ACCESS_PROPERTY_SET_GUID, 
                     &(pER->guid));
        pTempOTL++;    
        cGuidIndex++;
    }
    
    //
    //Add Property Set
    //

    for(i = 0; i < cPropertySets; ++i)
    {
        PER_ENTRY pER = (PER_ENTRY)DPA_FastGetPtr(hPropSetList, i);
        
        AddOTLToList(pTempOTL,
                     ACCESS_PROPERTY_SET_GUID, 
                     &pER->guid); 
        cGuidIndex++;
        pTempOTL++;    
        
        //
        //Add all the properties which are member of this property set
        //
        for(j = 0; j < cProperties; ++j)
        {
            PPROP_ENTRY pProp = (PPROP_ENTRY)DPA_FastGetPtr(hPropertyList, j);
            if(IsEqualGUID(pER->guid, *pProp->pasguid))
            {
                AddOTLToList(pTempOTL,
                             ACCESS_PROPERTY_GUID, 
                             pProp->pguid); 
                cGuidIndex++;
                pTempOTL++;    
                pProp->dwFlags|= OTL_ADDED_TO_LIST;
            }
        }                
    }               

    //Add all remaining properties
    for( j =0; j < cProperties; ++j )
    {
        PPROP_ENTRY pProp = (PPROP_ENTRY)DPA_FastGetPtr(hPropertyList, j);
        if( !(pProp->dwFlags & OTL_ADDED_TO_LIST) )
        {
            AddOTLToList(pTempOTL, 
                         ACCESS_PROPERTY_SET_GUID, 
                         pProp->pguid); 
            pTempOTL++;    
            cGuidIndex++;
        }
        pProp->dwFlags &= ~OTL_ADDED_TO_LIST;
    }
    
    //All all child clasess
    for( j = 0; j < cChildClasses; ++j )
    {
        PPROP_ENTRY pClass= (PPROP_ENTRY)DPA_FastGetPtr(hClassList, j);
        AddOTLToList(pTempOTL, 
                     ACCESS_PROPERTY_SET_GUID, 
                     pClass->pguid); 
        pTempOTL++;
        cGuidIndex++;
    }

exit_gracefully:

    DPA_Destroy(hExtRightList);
    DPA_Destroy(hClassList);
    DPA_Destroy(hPropertyList);
    DPA_Destroy(hPropSetList);

    LeaveCriticalSection(&m_ObjectTypeCacheCritSec);

    if (FAILED(hr))
    {
        *ppObjectTypeList = NULL;
        *pObjectTypeListCount = 0;
    }
    else
    {
        *ppObjectTypeList = pOTL;
        *pObjectTypeListCount = cGuidIndex;
    }

    TraceLeaveResult(hr);
}


PID_CACHE_ENTRY
CSchemaCache::LookupID(HDPA hCache, LPCWSTR pszLdapName)
{
    PID_CACHE_ENTRY pCacheEntry = NULL;
    int iEntry;

    TraceEnter(TRACE_SCHEMA, "CSchemaCache::LookupID");
    TraceAssert(hCache != NULL);
    TraceAssert(pszLdapName != NULL && *pszLdapName);

    iEntry = DPA_Search(hCache,
                        NULL,
                        0,
                        Schema_CompareLdapName,
                        (LPARAM)pszLdapName,
                        DPAS_SORTED);

    if (iEntry != -1)
        pCacheEntry = (PID_CACHE_ENTRY)DPA_FastGetPtr(hCache, iEntry);

    TraceLeaveValue(pCacheEntry);
}

BOOL
CSchemaCache::IsAuxClass(LPCGUID pguidObjectType)
{
    PID_CACHE_ENTRY pCacheEntry = NULL;
    HRESULT hr = S_OK;
    UINT cItems;

    TraceEnter(TRACE_SCHEMACLASS, "CSchemaCache::LookupClass");
    TraceAssert(pguidObjectType != NULL);
    
    if(IsEqualGUID(*pguidObjectType, GUID_NULL))
        return FALSE;

    hr = WaitOnClassThread();
    FailGracefully(hr, "Class cache unavailable");

    TraceAssert(m_hClassCache != NULL);

    cItems = DPA_GetPtrCount(m_hClassCache);

    while (cItems > 0)
    {
        PID_CACHE_ENTRY pTemp = (PID_CACHE_ENTRY)DPA_FastGetPtr(m_hClassCache, --cItems);

        if (IsEqualGUID(*pguidObjectType, pTemp->guid))
        {
            pCacheEntry = pTemp;
            break;
        }
    }

exit_gracefully:

    if(pCacheEntry)
        return pCacheEntry->bAuxClass;
    else
        return FALSE;       
}

PID_CACHE_ENTRY
CSchemaCache::LookupClass(LPCGUID pguidObjectType)
{
    PID_CACHE_ENTRY pCacheEntry = NULL;
    HRESULT hr = S_OK;
    UINT cItems;

    TraceEnter(TRACE_SCHEMACLASS, "CSchemaCache::LookupClass");
    TraceAssert(pguidObjectType != NULL);
    TraceAssert(!IsEqualGUID(*pguidObjectType, GUID_NULL));

    hr = WaitOnClassThread();
    FailGracefully(hr, "Class cache unavailable");

    TraceAssert(m_hClassCache != NULL);

    cItems = DPA_GetPtrCount(m_hClassCache);

    while (cItems > 0)
    {
        PID_CACHE_ENTRY pTemp = (PID_CACHE_ENTRY)DPA_FastGetPtr(m_hClassCache, --cItems);

        if (IsEqualGUID(*pguidObjectType, pTemp->guid))
        {
            pCacheEntry = pTemp;
            break;
        }
    }

exit_gracefully:

    TraceLeaveValue(pCacheEntry);
}


HRESULT
CSchemaCache::LookupClassID(LPCWSTR pszClass, LPGUID pGuid)
{
    TraceEnter(TRACE_SCHEMACLASS, "CSchemaCache::LookupClassID");
    TraceAssert(pszClass != NULL && pGuid != NULL);
	

    HRESULT hr = WaitOnClassThread();
	if(SUCCEEDED(hr))
    {
        TraceAssert(m_hClassCache != NULL);
        PID_CACHE_ENTRY pCacheEntry = LookupID(m_hClassCache, pszClass);
        if (pCacheEntry)
            *pGuid = pCacheEntry->guid;
    }

    return hr;
}


LPCGUID
CSchemaCache::LookupPropertyID(LPCWSTR pszProperty)
{
    LPCGUID pID = NULL;

    TraceEnter(TRACE_SCHEMAPROP, "CSchemaCache::LookupPropertyID");
    TraceAssert(pszProperty != NULL);

    if (SUCCEEDED(WaitOnPropertyThread()))
    {
        TraceAssert(m_hPropertyCache != NULL);
        PID_CACHE_ENTRY pCacheEntry = LookupID(m_hPropertyCache, pszProperty);
        if (pCacheEntry)
            pID = &pCacheEntry->guid;
    }

    TraceLeaveValue(pID);
}


WCHAR const c_szDsHeuristics[] = L"dSHeuristics";

int
CSchemaCache::GetListObjectEnforced(void)
{
    int nListObjectEnforced = 0;    // Assume "not enforced"
    HRESULT hr;
    IADsPathname *pPath = NULL;
    const LPWSTR aszServicePath[] =
    {
        L"CN=Services",
        L"CN=Windows NT",
        L"CN=Directory Service",
    };
    BSTR strServicePath = NULL;
    IDirectoryObject *pDirectoryService = NULL;
    LPWSTR pszDsHeuristics = (LPWSTR)c_szDsHeuristics;
    PADS_ATTR_INFO pAttributeInfo = NULL;
    DWORD dwAttributesReturned;
    LPWSTR pszHeuristicString;
    int i;

    TraceEnter(TRACE_SCHEMA, "CSchemaCache::GetListObjectEnforced");

    // Create a path object for manipulating ADS paths
    hr = CoCreateInstance(CLSID_Pathname,
                          NULL,
                          CLSCTX_INPROC_SERVER,
                          IID_IADsPathname,
                          (LPVOID*)&pPath);
    FailGracefully(hr, "Unable to create ADsPathname object");

    hr = pPath->Set(m_strERSearchPath, ADS_SETTYPE_FULL);
    FailGracefully(hr, "Unable to initialize ADsPathname object");

    hr = pPath->RemoveLeafElement();
    for (i = 0; i < ARRAYSIZE(aszServicePath); i++)
    {
        hr = pPath->AddLeafElement(aszServicePath[i]);
        FailGracefully(hr, "Unable to build path to 'Directory Service' object");
    }

    hr = pPath->Retrieve(ADS_FORMAT_WINDOWS, &strServicePath);
    FailGracefully(hr, "Unable to build path to 'Directory Service' object");

    hr = ADsGetObject(strServicePath,
                      IID_IDirectoryObject,
                      (LPVOID*)&pDirectoryService);
    FailGracefully(hr, "Unable to bind to 'Directory Service' object for heuristics");

    hr = pDirectoryService->GetObjectAttributes(&pszDsHeuristics,
                                                1,
                                                &pAttributeInfo,
                                                &dwAttributesReturned);
    if (!pAttributeInfo)
        ExitGracefully(hr, hr, "GetObjectAttributes failed to read dSHeuristics property");

    TraceAssert(ADSTYPE_DN_STRING <= pAttributeInfo->dwADsType);
    TraceAssert(ADSTYPE_NUMERIC_STRING >= pAttributeInfo->dwADsType);
    TraceAssert(1 == pAttributeInfo->dwNumValues);

    pszHeuristicString = pAttributeInfo->pADsValues->NumericString;
    if (pszHeuristicString &&
        lstrlenW(pszHeuristicString) > 2 &&
        L'0' != pszHeuristicString[2])
    {
        nListObjectEnforced = 1;
    }

exit_gracefully:

    if (pAttributeInfo)
        FreeADsMem(pAttributeInfo);

    DoRelease(pDirectoryService);
    DoRelease(pPath);

    SysFreeString(strServicePath);

    TraceLeaveValue(nListObjectEnforced);
}

BOOL
CSchemaCache::HideListObjectAccess(void)
{
    TraceEnter(TRACE_SCHEMA, "CSchemaCache::HideListObjectAccess");

    if (-1 == m_nDsListObjectEnforced)
    {
        m_nDsListObjectEnforced = GetListObjectEnforced();
    }

    TraceLeaveValue(0 == m_nDsListObjectEnforced);
}


#define ACCESS_LENGTH_0 (sizeof(SI_ACCESS) + MAX_TYPENAME_LENGTH * sizeof(WCHAR))
#define ACCESS_LENGTH_1 (sizeof(SI_ACCESS) + MAX_TYPENAME_LENGTH * sizeof(WCHAR))
#define ACCESS_LENGTH_2 (3 * sizeof(SI_ACCESS) + 3 * MAX_TYPENAME_LENGTH * sizeof(WCHAR))

HRESULT
CSchemaCache::BuildAccessArray(LPCGUID pguidObjectType,
                               LPCWSTR pszClassName,
                               LPCWSTR pszSchemaPath,    
                               HDPA hAuxList,
                               DWORD dwFlags,
                               PSI_ACCESS *ppAccesses,
                               ULONG *pcAccesses,
                               ULONG *piDefaultAccess)
{
    HRESULT hr = S_OK;
    
    DWORD dwBufferLength = 0;
    UINT cMaxAccesses;
    LPWSTR pszData = NULL;
    //
    //Lists
    //
    HDPA hExtRightList = NULL;
    HDPA hPropSetList = NULL;
    HDPA hPropertyList = NULL;
    HDPA hClassList = NULL;
    //
    //List counts
    //
    ULONG cExtendedRights = 0;
    ULONG cPropertySets = 0;
    UINT cProperties = 0;
    UINT cChildClasses = 0;

    ULONG cBaseRights = 0;
    
    
    PSI_ACCESS pAccesses = NULL;
    PSI_ACCESS pTempAccesses = NULL;
    ULONG cAccesses = 0;

    TraceEnter(TRACE_SCHEMA, "CSchemaCache::BuildAccessArray");
    TraceAssert(pguidObjectType != NULL);
    TraceAssert(ppAccesses);
    TraceAssert(pcAccesses);
    TraceAssert(piDefaultAccess);

    *ppAccesses = NULL;
    *pcAccesses = 0;
    *piDefaultAccess = 0;
    //
    //Decide what all we need
    //
    BOOL bBasicRight = FALSE;
    BOOL bExtRight = FALSE;
    BOOL bChildClass = FALSE;
    BOOL bProp = FALSE;


    //
    // Lookup the name of this class
    //
    if (pszClassName == NULL)
        pszClassName = GetClassName(pguidObjectType);
    
    if(pszClassName == NULL)
        ExitGracefully(hr, E_UNEXPECTED, "Unknown child object GUID");

    
    if(dwFlags & SI_EDIT_PROPERTIES)
    {
        bProp = TRUE;
    }
    else if(dwFlags & SI_EDIT_EFFECTIVE)
    {
        bExtRight = TRUE;
        bChildClass = TRUE;
        bProp = TRUE;
    }
    else
    {
        bExtRight = TRUE;
        bChildClass = TRUE;
    }
    //
    //We don't show basicRights for Auxillary Classes.
    //This happens when user selects Aux Class in Applyonto combo
    //
    bBasicRight = !IsAuxClass(pguidObjectType);
    //
    // Get the list of Extended Rights for this page
    //
    if (pguidObjectType &&
        SUCCEEDED(GetExtendedRightsForNClasses(m_strERSearchPath,
                                               pguidObjectType,
                                               hAuxList,
                                               bExtRight ? &hExtRightList : NULL,
                                               &hPropSetList)))

    {
        if(hPropSetList)
            cPropertySets = DPA_GetPtrCount(hPropSetList);
        if(hExtRightList)
            cExtendedRights = DPA_GetPtrCount(hExtRightList);
    }


    if( bChildClass &&
        pguidObjectType &&
        SUCCEEDED(GetChildClassesForNClasses(pguidObjectType,
                                             pszClassName,
                                             hAuxList,
                                             pszSchemaPath,
                                             &hClassList)))
    {

        if(hClassList)
            cChildClasses = DPA_GetPtrCount(hClassList);        
    }
     

    //
    //Get the properties for the class
    //        
    if (bProp &&
        pguidObjectType &&
        SUCCEEDED(GetPropertiesForNClasses(pguidObjectType,
                                           pszClassName,
                                           hAuxList,
                                           pszSchemaPath,
                                           &hPropertyList)))
    {
        if(hPropertyList)
            cProperties = DPA_GetPtrCount(hPropertyList);
        
    }
    
    if(bBasicRight)
    {
        //
        //Only Read Property and write Property
        //
        if(dwFlags & SI_EDIT_PROPERTIES)
        {
            cBaseRights = 2;
        }
        else
        {
            cBaseRights = ARRAYSIZE(g_siDSAccesses);
                if (!cChildClasses)
                    cBaseRights -= 3; // skip DS_CREATE_CHILD and DS_DELETE_CHILD and both

        }
    }

    //
    //Three Entries per Child Class 1)Create 2)Delete 3) Create/Delete 
    //Three Entries per Prop Class 1) Read 2)Write 3)Read/Write
    //
    cMaxAccesses =  cBaseRights +
                    cExtendedRights +  
                    3 * cChildClasses +  
                    3 * cPropertySets +
                    3 * cProperties;
    //
    //This can happen for Aux Class Object Right page
    //As we don't show general rights for it.
    //
    if(cMaxAccesses == 0)
        goto exit_gracefully;
    
    //
    // Allocate a buffer for the access array
    //
    dwBufferLength =  cBaseRights * sizeof(SI_ACCESS)
                      + cExtendedRights * ACCESS_LENGTH_1
                      + cChildClasses * ACCESS_LENGTH_2
                      + cPropertySets * ACCESS_LENGTH_2
                      + cProperties * ACCESS_LENGTH_2;
    
    pAccesses = (PSI_ACCESS)LocalAlloc(LPTR, dwBufferLength);
    if (pAccesses == NULL)
        ExitGracefully(hr, E_OUTOFMEMORY, "LocalAlloc failed");

    pTempAccesses = pAccesses;
    pszData = (LPWSTR)(pTempAccesses + cMaxAccesses);

    //
    //Copy the basic right
    //
    if(bBasicRight)
    {
        if(dwFlags & SI_EDIT_PROPERTIES)
        {    
            //
            // Add "Read All Properties" and "Write All Properties"
            //
            CopyMemory(pTempAccesses, &g_siDSAccesses[g_iDSProperties], 2 * sizeof(SI_ACCESS));
            pTempAccesses += 2;
            cAccesses += 2;
        }
        else
        {
            //
            // Add normal entries 
            //
            CopyMemory(pTempAccesses, g_siDSAccesses, cBaseRights * sizeof(SI_ACCESS));
            pTempAccesses += cBaseRights;
            cAccesses += cBaseRights;

            if (HideListObjectAccess())
            {
                pAccesses[g_iDSRead].mask &= ~ACTRL_DS_LIST_OBJECT;
                pAccesses[g_iDSListObject].dwFlags = 0;
            }
			
			//
			//If there are no child objects, don't show create/delete child objects
			//
			if(cChildClasses == 0)
				pAccesses[g_iDSDeleteTree].dwFlags = 0;

        }
    }

    //
    // Add entries for creating & deleting child objects
    //
    if (bChildClass && cChildClasses)
    {
        TraceAssert(NULL != hClassList);

        cAccesses += AddTempListToAccessList(hClassList,
                                             &pTempAccesses,
                                             &pszData,
                                             SI_ACCESS_SPECIFIC,
                                             SCHEMA_CLASS | (dwFlags & SCHEMA_NO_FILTER),
                                             FALSE);
    }



    if(bExtRight)
    {
        //
        //Decide if to show "All Extended Rights" entry 
        //and "All Validated Right Entry
        //
        BOOL bAllExtRights = FALSE;
        BOOL bAllValRights = FALSE;
        if(cExtendedRights)
        {
            //
            // Add entries for Extended Rights
            //
            UINT i;
            for (i = 0; i < cExtendedRights; i++)
            {
                PER_ENTRY pER = (PER_ENTRY)DPA_FastGetPtr(hExtRightList, i);

                //
                //Show All Validated Right entry only if atleast one 
                //individual Validated Right is present
                //
                if(pER->mask & ACTRL_DS_SELF)
                    bAllValRights = TRUE;

                //
                //Show All Validated Right entry only if atleast one 
                //individual Validated Right is present
                //
                if(pER->mask & ACTRL_DS_CONTROL_ACCESS)
                    bAllExtRights = TRUE;

                pTempAccesses->mask = pER->mask;
                //
                //Extended Rights Are shown on both first page and advanced page
                //
                pTempAccesses->dwFlags = SI_ACCESS_GENERAL | SI_ACCESS_SPECIFIC;
                pTempAccesses->pguid = &pER->guid;
                pTempAccesses->pszName = pszData;
                lstrcpynW(pszData, pER->szName, MAX_TYPENAME_LENGTH);
                pszData += (lstrlen(pTempAccesses->pszName) + 1);
                pTempAccesses++;
                cAccesses++;
            }
        }
        if(!bAllExtRights && bBasicRight)
            pAccesses[g_iDSAllExtRights].dwFlags = 0;

        if(!bAllValRights && bBasicRight)
            pAccesses[g_iDSAllValRights].dwFlags = 0;

    }

    //
    //Add PropertySet Entries
    //
    if (cPropertySets > 0)
    {
        cAccesses += AddTempListToAccessList(hPropSetList,
                                             &pTempAccesses,
                                             &pszData,
                                             SI_ACCESS_GENERAL|SI_ACCESS_PROPERTY,
                                             (dwFlags & SCHEMA_NO_FILTER),
                                             TRUE);
    }

    //
    // Add property entries
    //
    if (bProp && cProperties > 0)
    {
        cAccesses += AddTempListToAccessList(hPropertyList,
                                             &pTempAccesses,
                                             &pszData,
                                             SI_ACCESS_PROPERTY,
                                             (dwFlags & SCHEMA_NO_FILTER),
                                             FALSE);
    }


    *ppAccesses = pAccesses;
    *pcAccesses = cAccesses;
    *piDefaultAccess = bBasicRight ? g_iDSDefAccess : 0;


exit_gracefully:

    if(hExtRightList)
        DPA_Destroy(hExtRightList);
    if(hClassList)
        DPA_Destroy(hClassList);
    if(hPropertyList)
        DPA_Destroy(hPropertyList);
    if(hPropSetList)
        DPA_Destroy(hPropSetList);

    TraceLeaveResult(hr);
}




HRESULT
CSchemaCache::EnumVariantList(LPVARIANT pvarList,
                              HDPA hTempList,
                              DWORD dwFlags,
                              IDsDisplaySpecifier *pDisplaySpec,
                              LPCWSTR pszPropertyClass,
                              BOOL )
{
    HRESULT hr = S_OK;
    LPVARIANT pvarItem = NULL;
    int cItems;
    BOOL bSafeArrayLocked = FALSE;

    TraceEnter(TRACE_SCHEMA, "CSchemaCache::EnumVariantList");
    TraceAssert(pvarList != NULL);
    TraceAssert(hTempList != NULL);

	if (dwFlags & SCHEMA_CLASS)
        hr = WaitOnClassThread();
    else
		hr = WaitOnPropertyThread();

	FailGracefully(hr, "Required Cache Not Available");


    if (V_VT(pvarList) == (VT_ARRAY | VT_VARIANT))
    {
        hr = SafeArrayAccessData(V_ARRAY(pvarList), (LPVOID*)&pvarItem);
        FailGracefully(hr, "Unable to access SafeArray");
        bSafeArrayLocked = TRUE;
        cItems = V_ARRAY(pvarList)->rgsabound[0].cElements;
    }
    else if (V_VT(pvarList) == VT_BSTR) // Single entry in list
    {
        pvarItem = pvarList;
        cItems = 1;
    }
    else
    {
        // Unknown format
        ExitGracefully(hr, E_INVALIDARG, "Unexpected VARIANT type");
    }

    if (NULL == m_strFilterFile)
        m_strFilterFile = GetFilterFilePath();

    //
    // Enumerate the variant list and get information about each
    // (filter, guid, display name)
    //
    for ( ; cItems > 0; pvarItem++, cItems--)
    {
        LPWSTR pszItem;
        DWORD dwFilter = 0;
        WCHAR wszDisplayName[MAX_PATH];
        PPROP_ENTRY pti;
        PID_CACHE_ENTRY pid ;


        //
        //Get the ldapDisplayName
        //            
        TraceAssert(V_VT(pvarItem) == VT_BSTR);
        pszItem = V_BSTR(pvarItem);

        //
        // Check for nonexistent or empty strings
        //
        if (!pszItem || !*pszItem)
            continue;

        //
        //Check if the string is filtered by dssec.dat file
        //
        if(m_strFilterFile)
        {
            if (dwFlags & SCHEMA_CLASS)
            {
                dwFilter = GetPrivateProfileIntW(pszItem,
                                                 c_szClassKey,
                                                 0,
                                                 m_strFilterFile);
                if(pszPropertyClass)
                    dwFilter |= GetPrivateProfileIntW(pszPropertyClass,
                                                      pszItem,
                                                      0,
                                                      m_strFilterFile);


            }
            else if (pszPropertyClass)
            {
                dwFilter = GetPrivateProfileIntW(pszPropertyClass,
                                                 pszItem,
                                                 0,
                                                 m_strFilterFile);
            }
        }
        
        //
        // Note that IDC_CLASS_NO_CREATE == IDC_PROP_NO_READ
        // and IDC_CLASS_NO_DELETE == IDC_PROP_NO_WRITE
        //
        dwFilter &= (IDC_CLASS_NO_CREATE | IDC_CLASS_NO_DELETE);

        //
        //Get the schema or property cache entry
        //
        if (dwFlags & SCHEMA_CLASS)
            pid = LookupID(m_hClassCache, pszItem);
        else
            pid = LookupID(m_hPropertyCache, pszItem);
            
        if(pid == NULL)
            continue;

        
        //
        //Get the Display Name
        //
        wszDisplayName[0] = L'\0';

        if (pDisplaySpec)
        {
            if (dwFlags & SCHEMA_CLASS)
            {
                pDisplaySpec->GetFriendlyClassName(pszItem,
                                                   wszDisplayName,
                                                   ARRAYSIZE(wszDisplayName));
            }
            else if (pszPropertyClass)
            {
                pDisplaySpec->GetFriendlyAttributeName(pszPropertyClass,
                                                       pszItem,
                                                       wszDisplayName,
                                                       ARRAYSIZE(wszDisplayName));
            }
        }

        LPWSTR pszDisplay;
        pszDisplay = (wszDisplayName[0] != L'\0') ? wszDisplayName : pszItem;
        //
        // Remember what we've got so far
        //
        pti = (PPROP_ENTRY)LocalAlloc(LPTR, sizeof(PROP_ENTRY) + StringByteSize(pszDisplay));
        if (pti)
        {
            pti->pguid = &pid->guid;
            pti->pasguid = &pid->asGuid;   
            pti->dwFlags |= dwFilter;
            lstrcpyW(pti->szName, pszDisplay);
            DPA_AppendPtr(hTempList, pti);
        }            
    }


exit_gracefully:

    if (bSafeArrayLocked)
        SafeArrayUnaccessData(V_ARRAY(pvarList));

    TraceLeaveResult(hr);
}


UINT
CSchemaCache::AddTempListToAccessList(HDPA hTempList,
                                      PSI_ACCESS *ppAccess,
                                      LPWSTR *ppszData,
                                      DWORD dwAccessFlags,
                                      DWORD dwFlags,
                                      BOOL bPropSet)
{
    UINT cTotalEntries = 0;
    int cItems;
    DWORD dwAccess1;
    DWORD dwAccess2;
    WCHAR szFmt1[MAX_TYPENAME_LENGTH];
    WCHAR szFmt2[MAX_TYPENAME_LENGTH];
    WCHAR szFmt3[MAX_TYPENAME_LENGTH];

    TraceEnter(TRACE_SCHEMA, "CSchemaCache::AddTempListToAccessList");
    TraceAssert(ppAccess != NULL);
    TraceAssert(ppszData != NULL);

    cItems = DPA_GetPtrCount(hTempList);
    if (0 == cItems)
        ExitGracefully(cTotalEntries, 0, "empty list");

    if (dwFlags & SCHEMA_CLASS)
    {
        dwAccess1 = ACTRL_DS_CREATE_CHILD;
        dwAccess2 = ACTRL_DS_DELETE_CHILD;
        LoadStringW(GLOBAL_HINSTANCE, IDS_DS_CREATE_CHILD_TYPE, szFmt1, ARRAYSIZE(szFmt1));
        LoadStringW(GLOBAL_HINSTANCE, IDS_DS_DELETE_CHILD_TYPE, szFmt2, ARRAYSIZE(szFmt2));
        LoadStringW(GLOBAL_HINSTANCE, IDS_DS_CREATEDELETE_TYPE, szFmt3, ARRAYSIZE(szFmt3));
    }
    else
    {
        dwAccess1 = ACTRL_DS_READ_PROP;
        dwAccess2 = ACTRL_DS_WRITE_PROP;
        LoadStringW(GLOBAL_HINSTANCE, IDS_DS_READ_PROP_TYPE,  szFmt1, ARRAYSIZE(szFmt1));
        LoadStringW(GLOBAL_HINSTANCE, IDS_DS_WRITE_PROP_TYPE, szFmt2, ARRAYSIZE(szFmt2));
        LoadStringW(GLOBAL_HINSTANCE, IDS_DS_READWRITE_TYPE,  szFmt3, ARRAYSIZE(szFmt3));
    }

    // Enumerate the list and make up to 2 entries for each
    for(int i = 0; i < cItems; ++i)
    {
        PER_ENTRY pER = NULL;
        PPROP_ENTRY pProp = NULL;
        LPWSTR pszData;
        LPGUID pGuid = NULL;
        PSI_ACCESS pNewAccess;
        LPCWSTR pszName;
        int cch;
        DWORD dwAccess3;
        DWORD dwFilter = 0;
        
        if(bPropSet)
        {
            pER = (PER_ENTRY)DPA_FastGetPtr(hTempList, i);
            if (!pER)
                continue;
            pGuid = &pER->guid;
            pszName = pER->szName;
            dwFilter = 0;
        }
        else
        {
            pProp = (PPROP_ENTRY)DPA_FastGetPtr(hTempList, i);
            if (!pProp)
                continue;
            pGuid = pProp->pguid;
            pszName = pProp->szName;
            dwFilter = pProp->dwFlags;
        }
    
        pszData = *ppszData;
        pNewAccess = *ppAccess;

        dwAccess3 = 0;
        if ((dwFlags & SCHEMA_NO_FILTER) ||
            !(dwFilter & IDC_CLASS_NO_CREATE))
        {
            pNewAccess->mask = dwAccess1;
            pNewAccess->dwFlags = dwAccessFlags;
            pNewAccess->pguid = pGuid;

            pNewAccess->pszName = (LPCWSTR)pszData;
            cch = wsprintfW((LPWSTR)pszData, szFmt1, pszName);
            pszData += (cch + 1);

            cTotalEntries++;
            pNewAccess++;

            dwAccess3 |= dwAccess1;
        }

        if ((dwFlags & SCHEMA_NO_FILTER) ||
            !(dwFilter & IDC_CLASS_NO_DELETE))
        {
            pNewAccess->mask = dwAccess2;
            pNewAccess->dwFlags = dwAccessFlags;
            pNewAccess->pguid = pGuid;

            pNewAccess->pszName = (LPCWSTR)pszData;
            cch = wsprintfW((LPWSTR)pszData, szFmt2, pszName);
            pszData += (cch + 1);

            cTotalEntries++;
            pNewAccess++;

            dwAccess3 |= dwAccess2;
        }

        if (dwAccess3 == (dwAccess1 | dwAccess2))
        {
            // Add a hidden entry for
            //     "Read/write <prop>"
            // or
            //     "Create/delete <child>"
            pNewAccess->mask = dwAccess3;
            // dwFlags = 0 means it will never show as a checkbox, but it
            // may be used for the name displayed on the Advanced page.
            pNewAccess->dwFlags = 0;
            pNewAccess->pguid = pGuid;

            pNewAccess->pszName = (LPCWSTR)pszData;
            cch = wsprintfW((LPWSTR)pszData, szFmt3, pszName);
            pszData += (cch + 1);

            cTotalEntries++;
            pNewAccess++;
        }

        if (*ppAccess != pNewAccess)
        {
            *ppAccess = pNewAccess; // move past new entries
            *ppszData = pszData;
        }
    }

exit_gracefully:

    TraceLeaveValue(cTotalEntries);
}


DWORD WINAPI
CSchemaCache::SchemaClassThread(LPVOID pvThreadData)
{
    PSCHEMACACHE pCache;

    HINSTANCE hInstThisDll = LoadLibrary(c_szDllName);
    InterlockedIncrement(&GLOBAL_REFCOUNT);

    pCache = (PSCHEMACACHE)pvThreadData;
    SetEvent(pCache->m_hLoadLibClassWaitEvent);
    TraceEnter(TRACE_SCHEMACLASS, "CSchemaCache::SchemaClassThread");
    TraceAssert(pCache != NULL);
    TraceAssert(pCache->m_strSchemaSearchPath != NULL);

#if DBG
    DWORD dwTime = GetTickCount();
#endif

    ThreadCoInitialize();

    pCache->m_hrClassResult = Schema_Search(pCache->m_strSchemaSearchPath,
                                            c_szClassFilter,
                                            &pCache->m_hClassCache,
                                            FALSE);

    ThreadCoUninitialize();

#if DBG
    Trace((TEXT("SchemaClassThread complete, elapsed time: %d ms"), GetTickCount() - dwTime));
#endif

    TraceLeave();

    InterlockedDecrement(&GLOBAL_REFCOUNT);
    FreeLibraryAndExitThread(hInstThisDll, 0);
}


DWORD WINAPI
CSchemaCache::SchemaPropertyThread(LPVOID pvThreadData)
{
    PSCHEMACACHE pCache = NULL;

    HINSTANCE hInstThisDll = LoadLibrary(c_szDllName);
    InterlockedIncrement(&GLOBAL_REFCOUNT);

    pCache = (PSCHEMACACHE)pvThreadData;
    SetEvent(pCache->m_hLoadLibPropWaitEvent);
    TraceEnter(TRACE_SCHEMAPROP, "CSchemaCache::SchemaPropertyThread");
    TraceAssert(pCache != NULL);
    TraceAssert(pCache->m_strSchemaSearchPath != NULL);

#if DBG
    DWORD dwTime = GetTickCount();
#endif

    ThreadCoInitialize();

    pCache->m_hrPropertyResult = Schema_Search(pCache->m_strSchemaSearchPath,
                                               c_szPropertyFilter,
                                               &pCache->m_hPropertyCache,
                                               TRUE);

    ThreadCoUninitialize();

#if DBG
    Trace((TEXT("SchemaPropertyThread complete, elapsed time: %d ms"), GetTickCount() - dwTime));
#endif

    TraceLeave();

    InterlockedDecrement(&GLOBAL_REFCOUNT);
    FreeLibraryAndExitThread(hInstThisDll, 0);

}


HRESULT
CSchemaCache::BuildInheritTypeArray(DWORD dwFlags)
{
    HRESULT hr = S_OK;
    int cItems = 0;
    DWORD cbNames = 0;
    DWORD dwBufferLength;
    PINHERIT_TYPE_ARRAY pInheritTypeArray = NULL;
    PSI_INHERIT_TYPE pNewInheritType;
    LPGUID pGuidData = NULL;
    LPWSTR pszData = NULL;
    WCHAR szFormat[MAX_TYPENAME_LENGTH];
    HDPA hTempList = NULL;
    PTEMP_INFO pti;
    IDsDisplaySpecifier *pDisplaySpec = NULL;

    TraceEnter(TRACE_SCHEMACLASS, "CSchemaCache::BuildInheritTypeArray");
    TraceAssert(m_pInheritTypeArray == NULL);   // Don't want to build this twice

    if (NULL == m_strFilterFile)
        m_strFilterFile = GetFilterFilePath();

    hr = WaitOnClassThread();
    FailGracefully(hr, "Class cache unavailable");

    cItems = DPA_GetPtrCount(m_hClassCache);
    if (cItems == 0)
        ExitGracefully(hr, E_FAIL, "No schema classes available");

    hTempList = DPA_Create(cItems);
    if (!hTempList)
        ExitGracefully(hr, E_OUTOFMEMORY, "Unable to create DPA");

    // Get the display specifier object
    CoCreateInstance(CLSID_DsDisplaySpecifier,
                     NULL,
                     CLSCTX_INPROC_SERVER,
                     IID_IDsDisplaySpecifier,
                     (void**)&pDisplaySpec);

    // Enumerate child types, apply filtering, and get display names
    while (cItems > 0)
    {
        PID_CACHE_ENTRY pCacheEntry;
        WCHAR wszDisplayName[MAX_PATH];

        pCacheEntry = (PID_CACHE_ENTRY)DPA_FastGetPtr(m_hClassCache, --cItems);

        if (!pCacheEntry)
            continue;
        
        if (m_strFilterFile && !(dwFlags & SCHEMA_NO_FILTER))
        {
            DWORD dwFilter = GetPrivateProfileIntW(pCacheEntry->szLdapName,
                                                   c_szClassKey,
                                                   0,
                                                   m_strFilterFile);
            if (dwFilter & IDC_CLASS_NO_INHERIT)
                continue;
        }

        wszDisplayName[0] = L'\0';

        if (pDisplaySpec)
        {
            pDisplaySpec->GetFriendlyClassName(pCacheEntry->szLdapName,
                                               wszDisplayName,
                                               ARRAYSIZE(wszDisplayName));
        }

        if (L'\0' != wszDisplayName[0])
            cbNames += StringByteSize(wszDisplayName);
        else
            cbNames += StringByteSize(pCacheEntry->szLdapName);

        pti = (PTEMP_INFO)LocalAlloc(LPTR, sizeof(TEMP_INFO) + sizeof(WCHAR)*lstrlenW(wszDisplayName));
        if (pti)
        {
            pti->pguid = &pCacheEntry->guid;
            pti->pszLdapName = pCacheEntry->szLdapName;
            lstrcpyW(pti->szDisplayName, wszDisplayName);
            DPA_AppendPtr(hTempList, pti);
        }
    }

    // Sort by display name
    DPA_Sort(hTempList, Schema_CompareTempDisplayName, 0);

    // Get an accurate count
    cItems = DPA_GetPtrCount(hTempList);

    //
    // Allocate a buffer for the inherit type array
    //
    dwBufferLength = sizeof(INHERIT_TYPE_ARRAY) - sizeof(SI_INHERIT_TYPE)
        + sizeof(g_siDSInheritTypes)
        + cItems * (sizeof(SI_INHERIT_TYPE) + sizeof(GUID) + MAX_TYPENAME_LENGTH*sizeof(WCHAR))
        + cbNames;

    pInheritTypeArray = (PINHERIT_TYPE_ARRAY)LocalAlloc(LPTR, dwBufferLength);
    if (pInheritTypeArray == NULL)
        ExitGracefully(hr, E_OUTOFMEMORY, "LocalAlloc failed");

    pInheritTypeArray->cInheritTypes = ARRAYSIZE(g_siDSInheritTypes);

    pNewInheritType = pInheritTypeArray->aInheritType;
    pGuidData = (LPGUID)(pNewInheritType + pInheritTypeArray->cInheritTypes + cItems);
    pszData = (LPWSTR)(pGuidData + cItems);


    // Copy static entries
    CopyMemory(pNewInheritType, g_siDSInheritTypes, sizeof(g_siDSInheritTypes));
    pNewInheritType += ARRAYSIZE(g_siDSInheritTypes);

    // Load format string
    LoadString(GLOBAL_HINSTANCE,
               IDS_DS_INHERIT_TYPE,
               szFormat,
               ARRAYSIZE(szFormat));

    // Enumerate child types and make an entry for each
    while (cItems > 0)
    {
        int cch;
        LPCWSTR pszDisplayName;

        pti = (PTEMP_INFO)DPA_FastGetPtr(hTempList, --cItems);
        if (!pti)
            continue;

        if (pti->szDisplayName[0])
            pszDisplayName = pti->szDisplayName;
        else
            pszDisplayName = pti->pszLdapName;

        pNewInheritType->dwFlags = CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;

        // The class entry name is the child class name, e.g. "Domain" or "User"
        pNewInheritType->pszName = pszData;
        cch = wsprintfW(pszData, szFormat, pszDisplayName);
        pszData += (cch + 1);

        pNewInheritType->pguid = pGuidData;
        *pGuidData = *pti->pguid;
        pGuidData++;

        pNewInheritType++;
        pInheritTypeArray->cInheritTypes++;
    }

exit_gracefully:

    DoRelease(pDisplaySpec);

    if (SUCCEEDED(hr))
    {
        m_pInheritTypeArray = pInheritTypeArray;
        // Set this master inherit type array's GUID to null
        m_pInheritTypeArray->guidObjectType = GUID_NULL;
        m_pInheritTypeArray->dwFlags = (dwFlags & SCHEMA_NO_FILTER);
    }
    else if (pInheritTypeArray != NULL)
    {
        LocalFree(pInheritTypeArray);
    }

    DestroyDPA(hTempList);

    TraceLeaveResult(hr);
}


HRESULT
Schema_BindToObject(LPCWSTR pszSchemaPath,
                    LPCWSTR pszName,
                    REFIID riid,
                    LPVOID *ppv)
{
    HRESULT hr;
    WCHAR szPath[MAX_PATH];
    UINT nSchemaRootLen;
    WCHAR chTemp;

    TraceEnter(TRACE_SCHEMA, "Schema_BindToObject");
    TraceAssert(pszSchemaPath != NULL);
    TraceAssert(pszName == NULL || *pszName);
    TraceAssert(ppv != NULL);

    if (pszSchemaPath == NULL)
    {
        ExitGracefully(hr, E_INVALIDARG, "No schema path provided");
    }

    nSchemaRootLen = lstrlenW(pszSchemaPath);

    //
    // Build the schema path to this object
    //
    lstrcpynW(szPath, pszSchemaPath, nSchemaRootLen + 1);
    chTemp = szPath[nSchemaRootLen-1];
    if (pszName != NULL)
    {
        // If there is no trailing slash, add it
        if (chTemp != TEXT('/'))
        {
            szPath[nSchemaRootLen] = TEXT('/');
            nSchemaRootLen++;
        }

        // Add the class or property name onto the end
        lstrcpynW(szPath + nSchemaRootLen,
                 pszName,
                 ARRAYSIZE(szPath) - nSchemaRootLen);
    }
    else if (nSchemaRootLen > 0)
    {
        // If there is a trailing slash, remove it
        if (chTemp == TEXT('/'))
            szPath[nSchemaRootLen-1] = TEXT('\0');
    }
    else
    {
        ExitGracefully(hr, E_INVALIDARG, "Empty schema path");
    }

    //
    // Instantiate the schema object
    //
    ThreadCoInitialize();
    hr = OpenDSObject(szPath,
                       NULL,
                       NULL,
                       ADS_SECURE_AUTHENTICATION,
                       riid,
                       ppv);

exit_gracefully:

    TraceLeaveResult(hr);
}


HRESULT
Schema_GetObjectID(IADs *pObj, LPGUID pGUID)
{
    HRESULT hr;
    VARIANT varID = {0};

    TraceEnter(TRACE_SCHEMA, "Schema_GetObjectID(IADs*)");
    TraceAssert(pObj != NULL);
    TraceAssert(pGUID != NULL && !IsBadWritePtr(pGUID, sizeof(GUID)));

    // Get the "schemaIDGUID" property
    hr = pObj->Get((LPWSTR)c_szSchemaIDGUID, &varID);

    if (SUCCEEDED(hr))
    {
        LPGUID pID = NULL;

        TraceAssert(V_VT(&varID) == (VT_ARRAY | VT_UI1));
        TraceAssert(V_ARRAY(&varID) && varID.parray->cDims == 1);
        TraceAssert(V_ARRAY(&varID)->rgsabound[0].cElements >= sizeof(GUID));

        hr = SafeArrayAccessData(V_ARRAY(&varID), (LPVOID*)&pID);
        if (SUCCEEDED(hr))
        {
            *pGUID = *pID;
            SafeArrayUnaccessData(V_ARRAY(&varID));
        }
        VariantClear(&varID);
    }

    TraceLeaveResult(hr);
}


HRESULT
Schema_Search(LPWSTR pszSchemaSearchPath,
              LPCWSTR pszFilter,
              HDPA *phCache,
              BOOL bProperty)
{
    HRESULT hr = S_OK;
    HDPA hCache = NULL;
    IDirectorySearch *pSchemaSearch = NULL;
    ADS_SEARCH_HANDLE hSearch = NULL;
    ADS_SEARCHPREF_INFO prefInfo[3];
    const LPCWSTR pProperties1[] =
    {
        c_szLDAPDisplayName,            // "lDAPDisplayName"
        c_szSchemaIDGUID,               // "schemaIDGUID"
        c_szObjectClassCategory,
    };
    const LPCWSTR pProperties2[] =
    {
        c_szLDAPDisplayName,
        c_szSchemaIDGUID,
        c_szAttributeSecurityGuid,
    };

    TraceEnter(lstrcmp(pszFilter, c_szPropertyFilter) ? TRACE_SCHEMACLASS : TRACE_SCHEMAPROP, "Schema_Search");
    TraceAssert(pszSchemaSearchPath != NULL);
    TraceAssert(phCache != NULL);

    LPCWSTR * pProperties = (LPCWSTR *)( bProperty ? pProperties2 : pProperties1 );
    DWORD dwSize = (DWORD)(bProperty ? ARRAYSIZE(pProperties2) : ARRAYSIZE(pProperties1));
    //
    // Create DPA if necessary
    //
    if (*phCache == NULL)
        *phCache = DPA_Create(100);

    if (*phCache == NULL)
        ExitGracefully(hr, E_OUTOFMEMORY, "DPA_Create failed");

    hCache = *phCache;

    // Get the schema search object
    hr = OpenDSObject(pszSchemaSearchPath,
                       NULL,
                       NULL,
                       ADS_SECURE_AUTHENTICATION,
                       IID_IDirectorySearch,
                       (LPVOID*)&pSchemaSearch);
    FailGracefully(hr, "Failed to get schema search object");

    // Set preferences to Asynchronous, Deep search, Paged results
    prefInfo[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
    prefInfo[0].vValue.dwType = ADSTYPE_INTEGER;
    prefInfo[0].vValue.Integer = ADS_SCOPE_SUBTREE;

    prefInfo[1].dwSearchPref = ADS_SEARCHPREF_ASYNCHRONOUS;
    prefInfo[1].vValue.dwType = ADSTYPE_BOOLEAN;
    prefInfo[1].vValue.Boolean = TRUE;

    prefInfo[2].dwSearchPref = ADS_SEARCHPREF_PAGESIZE;
    prefInfo[2].vValue.dwType = ADSTYPE_INTEGER;
    prefInfo[2].vValue.Integer = PAGE_SIZE;

    hr = pSchemaSearch->SetSearchPreference(prefInfo, ARRAYSIZE(prefInfo));

    // Do the search
    hr = pSchemaSearch->ExecuteSearch((LPWSTR)pszFilter,
                                      (LPWSTR*)pProperties,
                                      dwSize,
                                      &hSearch);
    FailGracefully(hr, "IDirectorySearch::ExecuteSearch failed");

    // Loop through the rows, getting the name and ID of each property or class
    for (;;)
    {
        ADS_SEARCH_COLUMN colLdapName;
        ADS_SEARCH_COLUMN colGuid;
        ADS_SEARCH_COLUMN colASGuid;
        LPWSTR pszLdapName;
        LPGUID pID;
        LPGUID pASID;
        INT iObjectClassCategory = 0;
        PID_CACHE_ENTRY pCacheEntry;

        hr = pSchemaSearch->GetNextRow(hSearch);

        if (FAILED(hr) || hr == S_ADS_NOMORE_ROWS)
            break;

        // Get class/property internal name
        hr = pSchemaSearch->GetColumn(hSearch, (LPWSTR)c_szLDAPDisplayName, &colLdapName);
        if (FAILED(hr))
        {
            TraceMsg("lDAPDisplayName not found for class/property");
            continue;
        }

        TraceAssert(colLdapName.dwADsType >= ADSTYPE_DN_STRING
                    && colLdapName.dwADsType <= ADSTYPE_NUMERIC_STRING);
        TraceAssert(colLdapName.dwNumValues == 1);

        pszLdapName = colLdapName.pADsValues->CaseIgnoreString;

        // Get the GUID column
        hr = pSchemaSearch->GetColumn(hSearch, (LPWSTR)c_szSchemaIDGUID, &colGuid);
        if (FAILED(hr))
        {
            Trace((TEXT("GUID not found for \"%s\""), pszLdapName));
            pSchemaSearch->FreeColumn(&colLdapName);
            continue;
        }

        // Get GUID from column
        TraceAssert(colGuid.dwADsType == ADSTYPE_OCTET_STRING);
        TraceAssert(colGuid.dwNumValues == 1);
        TraceAssert(colGuid.pADsValues->OctetString.dwLength == sizeof(GUID));

        pID = (LPGUID)(colGuid.pADsValues->OctetString.lpValue);


        pASID = (LPGUID)&GUID_NULL;
        if( bProperty )
        {
            // Get the AttrbiuteSecurityGUID column
            hr = pSchemaSearch->GetColumn(hSearch, (LPWSTR)c_szAttributeSecurityGuid, &colASGuid);
            
            if (hr != E_ADS_COLUMN_NOT_SET && FAILED(hr))
            {
                Trace((TEXT("AttributeSecurityGUID not found for \"%s\""), pszLdapName));
                pSchemaSearch->FreeColumn(&colLdapName);
                pSchemaSearch->FreeColumn(&colGuid);
                continue;
            }

            if( hr != E_ADS_COLUMN_NOT_SET )
            {
                // Get GUID from column
                TraceAssert(colASGuid.dwADsType == ADSTYPE_OCTET_STRING);
                TraceAssert(colASGuid.dwNumValues == 1);
                TraceAssert(colASGuid.pADsValues->OctetString.dwLength == sizeof(GUID));

                pASID = (LPGUID)(colASGuid.pADsValues->OctetString.lpValue);
            }
        }
        else
        {
            // Get the c_szObjectClassCategory column
            hr = pSchemaSearch->GetColumn(hSearch, (LPWSTR)c_szObjectClassCategory, &colASGuid);
            
            if (FAILED(hr))
            {
                Trace((TEXT("ObjectClassCategory not found for \"%s\""), pszLdapName));
                pSchemaSearch->FreeColumn(&colLdapName);
                pSchemaSearch->FreeColumn(&colGuid);
                continue;
            }

            // Get GUID from column
            TraceAssert(colASGuid.dwADsType == ADSTYPE_INTEGER);
            TraceAssert(colASGuid.dwNumValues == 1);
            
            iObjectClassCategory = colASGuid.pADsValues->Integer;
        }

        pCacheEntry = (PID_CACHE_ENTRY)LocalAlloc(LPTR,
                                  sizeof(ID_CACHE_ENTRY)
                                  + sizeof(WCHAR)*lstrlenW(pszLdapName));
        if (pCacheEntry != NULL)
        {
            // Copy the item name and ID
            pCacheEntry->guid = *pID;
            pCacheEntry->asGuid = *pASID;
            pCacheEntry->bAuxClass = (iObjectClassCategory == 3);
            lstrcpyW(pCacheEntry->szLdapName, pszLdapName);

            // Insert into cache
            DPA_AppendPtr(hCache, pCacheEntry);
        }
    
        pSchemaSearch->FreeColumn(&colLdapName);
        pSchemaSearch->FreeColumn(&colGuid);
        if(!bProperty || hr != E_ADS_COLUMN_NOT_SET)
        pSchemaSearch->FreeColumn(&colASGuid);
    }

    DPA_Sort(hCache, Schema_CompareLdapName, 0);

exit_gracefully:

    if (hSearch != NULL)
        pSchemaSearch->CloseSearchHandle(hSearch);

    DoRelease(pSchemaSearch);

    if (FAILED(hr))
    {
        DestroyDPA(hCache);
        *phCache = NULL;
    }

    TraceLeaveResult(hr);
}



//+--------------------------------------------------------------------------
//
//  Function:   Schema_GetExtendedRightsForOneClass
//
//  Synopsis:   This Function Gets the Extended Rights for One Class.
//              It Adds all the control rights, validated rights to 
//              phERList. It Adds all the PropertySets to phPropSetList.
//
//  Arguments:  [pszSchemaSearchPath - IN] : Path to schema
//              [pguidClass - In] : Guid Of the class
//              [phERList - OUT] : Get the output Extended Right List
//              [phPropSetList - OUT]: Gets the output PropertySet List
//
//  Returns:    HRESULT : S_OK if everything succeeded
//                        E_INVALIDARG if the object entry wasn't found
//
//  History:    3-Nov 2000  hiteshr   Created
//
//---------------------------------------------------------------------------
HRESULT
CSchemaCache::GetExtendedRightsForOneClass(IN LPWSTR pszSchemaSearchPath,
                                    IN LPCGUID pguidClass,
                                    OUT HDPA *phERList,
                                    OUT HDPA *phPropSetList)
{
    TraceEnter(TRACE_SCHEMA, "CSchemaCache::GetExtendedRightsForOneClass");
    
    if(!pszSchemaSearchPath 
       || !pguidClass 
       || IsEqualGUID(*pguidClass, GUID_NULL)
       || !phERList
       || !phPropSetList)
    {
        Trace((L"Invalid Arguments Passed to Schema_GetExtendedRightsForOneClass"));          
        return E_INVALIDARG; 
    }

    HRESULT hr = S_OK;
    IDirectorySearch *pSearch = NULL;
    ADS_SEARCH_HANDLE hSearch = NULL;
    ADS_SEARCHPREF_INFO prefInfo[3];
    WCHAR szFilter[100];
    HDPA hExtRightList = NULL;
    HDPA hPropSetList = NULL;
    POBJECT_TYPE_CACHE pOTC = NULL;

    //
    //Search in the cache
    //
    if (m_hObjectTypeCache != NULL)
    {
        UINT cItems = DPA_GetPtrCount(m_hObjectTypeCache);

        while (cItems > 0)
        {
            pOTC = (POBJECT_TYPE_CACHE)DPA_FastGetPtr(m_hObjectTypeCache, --cItems);
            //
            //Found A match.
            //
            if(IsEqualGUID(pOTC->guidObject, *pguidClass))    
                break;
            
            pOTC = NULL;                
        }
        //
        //Have we already got the properties
        //
        if(pOTC && pOTC->flags & OTC_EXTR)
        {
            *phERList = pOTC->hListExtRights;
            *phPropSetList = pOTC->hListPropertySet;
            return S_OK;
        }
    }


    //
    //Attributes to fetch
    //
    const LPCWSTR pProperties[] =
    {
        c_szDisplayName,                // "displayName"
        c_szDisplayID,                  // "localizationDisplayId"
        c_szRightsGuid,                 // "rightsGuid"
        c_szValidAccesses,              // "validAccesses"
    };


    //
    // Build the filter string
    //
    wsprintfW(szFilter, c_szERFilterFormat,
              pguidClass->Data1, pguidClass->Data2, pguidClass->Data3,
              pguidClass->Data4[0], pguidClass->Data4[1],
              pguidClass->Data4[2], pguidClass->Data4[3],
              pguidClass->Data4[4], pguidClass->Data4[5],
              pguidClass->Data4[6], pguidClass->Data4[7]);
    Trace((TEXT("Filter \"%s\""), szFilter));

    //
    // Create DPA to hold results
    //
    hExtRightList = DPA_Create(8);
    

    if (hExtRightList == NULL)
        ExitGracefully(hr, E_OUTOFMEMORY, "DPA_Create failed");

    hPropSetList = DPA_Create(8);

    if( hPropSetList == NULL )
        ExitGracefully(hr, E_OUTOFMEMORY, "DPA_Create failed");

    //
    // Get the schema search object
    //
    hr = OpenDSObject(pszSchemaSearchPath,
                       NULL,
                       NULL,
                       ADS_SECURE_AUTHENTICATION,
                       IID_IDirectorySearch,
                       (LPVOID*)&pSearch);
    FailGracefully(hr, "Failed to get schema search object");
    
    //
    // Set preferences to Asynchronous, OneLevel search, Paged results
    //
    prefInfo[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
    prefInfo[0].vValue.dwType = ADSTYPE_INTEGER;
    prefInfo[0].vValue.Integer = ADS_SCOPE_ONELEVEL;

    prefInfo[1].dwSearchPref = ADS_SEARCHPREF_ASYNCHRONOUS;
    prefInfo[1].vValue.dwType = ADSTYPE_BOOLEAN;
    prefInfo[1].vValue.Boolean = TRUE;

    prefInfo[2].dwSearchPref = ADS_SEARCHPREF_PAGESIZE;
    prefInfo[2].vValue.dwType = ADSTYPE_INTEGER;
    prefInfo[2].vValue.Integer = PAGE_SIZE;

    hr = pSearch->SetSearchPreference(prefInfo, ARRAYSIZE(prefInfo));
    FailGracefully(hr, "IDirectorySearch::SetSearchPreference failed");
    
    //
    // Do the search
    //
    hr = pSearch->ExecuteSearch(szFilter,
                                (LPWSTR*)pProperties,
                                ARRAYSIZE(pProperties),
                                &hSearch);
    FailGracefully(hr, "IDirectorySearch::ExecuteSearch failed");
    
    //
    // Loop through the rows, getting the name and ID of each property or class
    //
    for (;;)
    {
        ADS_SEARCH_COLUMN col;
        GUID guid;
        DWORD dwValidAccesses;
        LPWSTR pszName = NULL;
        WCHAR szDisplayName[MAX_PATH];

        hr = pSearch->GetNextRow(hSearch);

        if (FAILED(hr) || hr == S_ADS_NOMORE_ROWS)
            break;
        
        //
        // Get the GUID
        //
        if (FAILED(pSearch->GetColumn(hSearch, (LPWSTR)c_szRightsGuid, &col)))
        {
            TraceMsg("GUID not found for extended right");
            continue;
        }
        TraceAssert(col.dwADsType >= ADSTYPE_DN_STRING
                    && col.dwADsType <= ADSTYPE_NUMERIC_STRING);
        wsprintfW(szFilter, c_szGUIDFormat, col.pADsValues->CaseIgnoreString);
        CLSIDFromString(szFilter, &guid);
        pSearch->FreeColumn(&col);

        //
        // Get the valid accesses mask
        //
        if (FAILED(pSearch->GetColumn(hSearch, (LPWSTR)c_szValidAccesses, &col)))
        {
            TraceMsg("validAccesses not found for Extended Right");
            continue;
        }
        TraceAssert(col.dwADsType == ADSTYPE_INTEGER);
        TraceAssert(col.dwNumValues == 1);
        dwValidAccesses = (DWORD)(DS_GENERIC_ALL & col.pADsValues->Integer);
        pSearch->FreeColumn(&col);
        
        //
        // Get the display name
        //
        szDisplayName[0] = L'\0';
        if (SUCCEEDED(pSearch->GetColumn(hSearch, (LPWSTR)c_szDisplayID, &col)))
        {
            TraceAssert(col.dwADsType == ADSTYPE_INTEGER);
            TraceAssert(col.dwNumValues == 1);
            if (FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
                              g_hInstance,
                              col.pADsValues->Integer,
                              0,
                              szDisplayName,
                              ARRAYSIZE(szDisplayName),
                              NULL))
            {
                pszName = szDisplayName;
            }
            pSearch->FreeColumn(&col);
        }

        if (NULL == pszName &&
            SUCCEEDED(pSearch->GetColumn(hSearch, (LPWSTR)c_szDisplayName, &col)))
        {
            TraceAssert(col.dwADsType >= ADSTYPE_DN_STRING
                        && col.dwADsType <= ADSTYPE_NUMERIC_STRING);
            lstrcpynW(szDisplayName, col.pADsValues->CaseIgnoreString, ARRAYSIZE(szDisplayName));
            pszName = szDisplayName;
            pSearch->FreeColumn(&col);
        }

        if (NULL == pszName)
        {
            TraceMsg("displayName not found for Extended Right");
            continue;
        }

        //
        //Create A new Cache Entry
        //
        PER_ENTRY pER = (PER_ENTRY)LocalAlloc(LPTR, sizeof(ER_ENTRY) + StringByteSize(pszName));
        if( pER == NULL )
            ExitGracefully(hr, E_OUTOFMEMORY, "LocalAlloc failed");

        pER->guid = guid;
        pER->mask = dwValidAccesses;
        pER->dwFlags = 0;
        lstrcpyW(pER->szName, pszName);
        
        //
        // Is it a Property Set?
        //
        if (dwValidAccesses & (ACTRL_DS_READ_PROP | ACTRL_DS_WRITE_PROP))
        {   
            //         
            // Insert into list
            //
            Trace((TEXT("Adding PropertySet\"%s\""), pszName));
            DPA_AppendPtr(hPropSetList, pER);
            dwValidAccesses &= ~(ACTRL_DS_READ_PROP | ACTRL_DS_WRITE_PROP);            
        }    
        else if (dwValidAccesses)
        {
            //
            // Must be a Control Right, Validated Write, etc.
            //
            Trace((TEXT("Adding Extended Right \"%s\""), pszName));
            DPA_AppendPtr(hExtRightList, pER);
        }
    }

    UINT cCount;
    //
    //Get the count of Extended Rights
    //
    cCount = DPA_GetPtrCount(hExtRightList);    
    if(!cCount)
    {
        DPA_Destroy(hExtRightList);
        hExtRightList = NULL;
    }
    else
    {
        DPA_Sort(hExtRightList, Schema_CompareER, 0);
    }
    //
    //Get the Count of PropertySets
    //
    cCount = DPA_GetPtrCount(hPropSetList);    
    if(!cCount)
    {
        DPA_Destroy(hPropSetList);
        hPropSetList = NULL;
    }
    else
    {
        DPA_Sort(hPropSetList,Schema_CompareER, 0 );
    }

    //
    //Add entry to the cache
    //
    if(!m_hObjectTypeCache)
        m_hObjectTypeCache = DPA_Create(4);
    
    if(!m_hObjectTypeCache)
        ExitGracefully(hr, E_OUTOFMEMORY, "DPA_Create Failed");

    if(!pOTC)
    {
        pOTC = (POBJECT_TYPE_CACHE)LocalAlloc(LPTR,sizeof(OBJECT_TYPE_CACHE));
        if(!pOTC)
            ExitGracefully(hr, E_OUTOFMEMORY, "LocalAlloc Failed");
        pOTC->guidObject = *pguidClass;
        DPA_AppendPtr(m_hObjectTypeCache, pOTC);
    }

    pOTC->hListExtRights = hExtRightList;
    pOTC->hListPropertySet = hPropSetList;
    pOTC->flags |= OTC_EXTR;


exit_gracefully:

    if (hSearch != NULL)
        pSearch->CloseSearchHandle(hSearch);

    DoRelease(pSearch);

    if (FAILED(hr))
    {
        if(hExtRightList)
        {
            DestroyDPA(hExtRightList);
            hExtRightList = NULL;
        }
        if(hPropSetList)
        {
            DestroyDPA(hPropSetList);
            hPropSetList = NULL;
        }                        
    }

    //
    //Set The Output
    //
    *phERList = hExtRightList;
    *phPropSetList = hPropSetList;

    TraceLeaveResult(hr);
}

//+--------------------------------------------------------------------------
//
//  Function:   GetChildClassesForOneClass
//
//  Synopsis:   This Function Gets the List of child classes for a class.
//
//  Arguments:  [pguidObjectType - IN] : ObjectGuidType of the class
//              [pszClassName - IN] : Class Name
//              [pszSchemaPath - IN] : Schema Search Path
//              [phChildList - OUT]: Output childclass List
//
//  Returns:    HRESULT : S_OK if everything succeeded
//                        E_INVALIDARG if the object entry wasn't found
//
//  History:    3-Nov 2000  hiteshr   Created
//
//---------------------------------------------------------------------------

HRESULT
CSchemaCache::GetChildClassesForOneClass(IN LPCGUID pguidObjectType,
                                         IN LPCWSTR pszClassName,
                                         IN LPCWSTR pszSchemaPath,
                                         OUT HDPA *phChildList)
{
    TraceEnter(TRACE_SCHEMA, "CSchemaCache::GetChildClassesForOneClass");

    HRESULT hr = S_OK;
    BOOL bContainer = FALSE;
    VARIANT varContainment = {0};
    HDPA hClassList = NULL;
    UINT cChildClass = 0;
    POBJECT_TYPE_CACHE pOTC = NULL;

    if(!pguidObjectType || 
       !pszSchemaPath ||
       !phChildList)
    {
        Trace((L"Invalid Input Arguments Passed to Schema_GetExtendedRightsForOneClass"));
        return E_INVALIDARG;
    }

    //
    //Search in the cache
    //
    if (m_hObjectTypeCache != NULL)
    {
        UINT cItems = DPA_GetPtrCount(m_hObjectTypeCache);

        while (cItems > 0)
        {
            pOTC = (POBJECT_TYPE_CACHE)DPA_FastGetPtr(m_hObjectTypeCache, --cItems);
            //
            //Found A match.
            //
            if(IsEqualGUID(pOTC->guidObject, *pguidObjectType))    
                break;

            pOTC = NULL;
        }
        //
        //Have we already got the child classes
        //
        if(pOTC && pOTC->flags & OTC_COBJ)
        {
            *phChildList = pOTC->hListChildObject;
            return S_OK;
        }
    }

    //
    // Lookup the name of this class
    //
    if (pszClassName == NULL)
        pszClassName = GetClassName(pguidObjectType);

    if (pszClassName == NULL)
        ExitGracefully(hr, E_UNEXPECTED, "Unknown child object GUID");

    //
    // Figure out if the object is a container by getting the list of child
    // classes. 
    //
    IADsClass *pDsClass;

    //
    // Get the schema object for this class
    //
    hr = Schema_BindToObject(pszSchemaPath,
                             pszClassName,
                             IID_IADsClass,
                             (LPVOID*)&pDsClass);
    FailGracefully(hr, "Schema_BindToObjectFailed");

    //
    // Get the list of possible child classes
    //
    if (SUCCEEDED(pDsClass->get_Containment(&varContainment)))
    {
        if (V_VT(&varContainment) == (VT_ARRAY | VT_VARIANT))
        {
            LPSAFEARRAY psa = V_ARRAY(&varContainment);
            TraceAssert(psa && psa->cDims == 1);
            if (psa->rgsabound[0].cElements > 0)
            bContainer = TRUE;
        }
        else if (V_VT(&varContainment) == VT_BSTR) // single entry
        {
            TraceAssert(V_BSTR(&varContainment));
            bContainer = TRUE;
        }
                
        //
        // (Requires the schema class enumeration thread to complete first,
        // and it's usually not done yet the first time we get here.)
        //
        if(bContainer)
        {
            hClassList = DPA_Create(8);
            if (hClassList)
            {
                IDsDisplaySpecifier *pDisplaySpec = NULL;
                //
                // Get the display specifier object
                //
                CoCreateInstance(CLSID_DsDisplaySpecifier,
                                 NULL,
                                 CLSCTX_INPROC_SERVER,
                                 IID_IDsDisplaySpecifier,
                                 (void**)&pDisplaySpec);
                //
                // Filter the list & get display names
                //
                EnumVariantList(&varContainment,
                                hClassList,
                                SCHEMA_CLASS,
                                pDisplaySpec,
                                pszClassName,
                                FALSE);

                DoRelease(pDisplaySpec);

                //
                //Get the count of properties
                //
                cChildClass = DPA_GetPtrCount(hClassList);    
                if(!cChildClass)
                {
                    DPA_Destroy(hClassList);
                    hClassList = NULL;
                }
                else
                {   
                    //
                    //Sort The list
                    //
                    DPA_Sort(hClassList,Schema_ComparePropDisplayName, 0 );
                }
            }
        }
    }
    DoRelease(pDsClass);

    //
    //Add entry to the cache
    //
    if(!m_hObjectTypeCache)
        m_hObjectTypeCache = DPA_Create(4);
    
    if(!m_hObjectTypeCache)
        ExitGracefully(hr, E_OUTOFMEMORY, "DPA_Create Failed");

    if(!pOTC)
    {
        pOTC = (POBJECT_TYPE_CACHE)LocalAlloc(LPTR,sizeof(OBJECT_TYPE_CACHE));
        if(!pOTC)
            ExitGracefully(hr, E_OUTOFMEMORY, "LocalAlloc Failed");
        pOTC->guidObject = *pguidObjectType;
        DPA_AppendPtr(m_hObjectTypeCache, pOTC);
    }

    pOTC->hListChildObject = hClassList;
    pOTC->flags |= OTC_COBJ;

    


exit_gracefully:

    VariantClear(&varContainment);

    if(FAILED(hr))
    {
        DestroyDPA(hClassList);
        hClassList = NULL;
    }

    //
    //Set the Output
    //
    *phChildList = hClassList;

    TraceLeaveResult(hr);
}


//+--------------------------------------------------------------------------
//
//  Function:   GetPropertiesForOneClass
//
//  Synopsis:   This Function Gets the List of properties for a class.
//
//  Arguments:  [pguidObjectType - IN] : ObjectGuidType of the class
//              [pszClassName - IN] : Class Name
//              [pszSchemaPath - IN] : Schema Search Path
//              [phPropertyList - OUT]: Output Property List
//
//  Returns:    HRESULT : S_OK if everything succeeded
//                        E_INVALIDARG if the object entry wasn't found
//
//  History:    3-Nov 2000  hiteshr   Created
//
//---------------------------------------------------------------------------
HRESULT
CSchemaCache::GetPropertiesForOneClass(IN LPCGUID pguidObjectType,
                                       IN LPCWSTR pszClassName,
                                       IN LPCWSTR pszSchemaPath,
                                       OUT HDPA *phPropertyList)
{
    HRESULT hr;
    IADsClass *pDsClass = NULL;
    VARIANT varMandatoryProperties = {0};
    VARIANT varOptionalProperties = {0};
    UINT cProperties = 0;
    IDsDisplaySpecifier *pDisplaySpec = NULL;
    HDPA hPropertyList = NULL;
    POBJECT_TYPE_CACHE pOTC = NULL;

    TraceEnter(TRACE_SCHEMA, "CSchemaCache::GetPropertiesForOneClass");

    if(!pguidObjectType || 
       !pszSchemaPath ||
       !phPropertyList)
    {
        Trace((L"Invalid Input Arguments Passed to CSchemaCache::GetPropertiesForOneClass"));
        return E_INVALIDARG;
    }

    //
    //Search in the cache
    //
    if (m_hObjectTypeCache != NULL)
    {
        UINT cItems = DPA_GetPtrCount(m_hObjectTypeCache);

        while (cItems > 0)
        {
            pOTC = (POBJECT_TYPE_CACHE)DPA_FastGetPtr(m_hObjectTypeCache, --cItems);
            //
            //Found A match.
            //
            if(IsEqualGUID(pOTC->guidObject, *pguidObjectType))    
                break;
            
            pOTC = NULL;
        }
        //
        //Have we already got the properties
        //
        if(pOTC && pOTC->flags & OTC_PROP)
        {
            *phPropertyList = pOTC->hListProperty;
            return S_OK;
        }
    }


    //
    // Get the schema object for this class
    //
    if (pszClassName == NULL)
        pszClassName = GetClassName(pguidObjectType);

    if (pszClassName == NULL)
        ExitGracefully(hr, E_UNEXPECTED, "Unknown child object GUID");

    hr = Schema_BindToObject(pszSchemaPath,
                             pszClassName,
                             IID_IADsClass,
                             (LPVOID*)&pDsClass);
    FailGracefully(hr, "Unable to create schema object");

    //
    // Get the display specifier object
    //
    CoCreateInstance(CLSID_DsDisplaySpecifier,
                     NULL,
                     CLSCTX_INPROC_SERVER,
                     IID_IDsDisplaySpecifier,
                     (void**)&pDisplaySpec);

    hPropertyList = DPA_Create(8);
    if (!hPropertyList)
        ExitGracefully(hr, E_OUTOFMEMORY, "Unable to create DPA");

    //
    // Get mandatory and optional property lists
    //
    if (SUCCEEDED(pDsClass->get_MandatoryProperties(&varMandatoryProperties)))
    {
        EnumVariantList(&varMandatoryProperties,
                        hPropertyList,
                        0,
                        pDisplaySpec,
                        pszClassName,
                        FALSE);
    }
    if (SUCCEEDED(pDsClass->get_OptionalProperties(&varOptionalProperties)))
    {
        EnumVariantList(&varOptionalProperties,
                        hPropertyList,
                        0,
                        pDisplaySpec,
                        pszClassName,
                        FALSE);
    }

    //
    //Get the Number of properties
    //
    cProperties = DPA_GetPtrCount(hPropertyList);    
    if(!cProperties)
    {
        DPA_Destroy(hPropertyList);
        hPropertyList = NULL;
    }
    else
    {
        //
        //Sort The list
        //
        DPA_Sort(hPropertyList,Schema_ComparePropDisplayName, 0 );
    }

    //
    //Add entry to the cache
    //
    if(!m_hObjectTypeCache)
        m_hObjectTypeCache = DPA_Create(4);
    
    if(!m_hObjectTypeCache)
        ExitGracefully(hr, E_OUTOFMEMORY, "DPA_Create Failed");

    if(!pOTC)
    {
        pOTC = (POBJECT_TYPE_CACHE)LocalAlloc(LPTR,sizeof(OBJECT_TYPE_CACHE));
        if(!pOTC)
            ExitGracefully(hr, E_OUTOFMEMORY, "LocalAlloc Failed");
        pOTC->guidObject = *pguidObjectType;
        DPA_AppendPtr(m_hObjectTypeCache, pOTC);
    }

    pOTC->hListProperty = hPropertyList;
    pOTC->flags |= OTC_PROP;

exit_gracefully:

    VariantClear(&varMandatoryProperties);
    VariantClear(&varOptionalProperties);
    
    DoRelease(pDsClass);
    DoRelease(pDisplaySpec);

    if(FAILED(hr) && hPropertyList)
    {
        DestroyDPA(hPropertyList);
        hPropertyList = NULL;
    }
    //
    //Set the Output
    //
    *phPropertyList = hPropertyList;

    TraceLeaveResult(hr);
}
//+--------------------------------------------------------------------------
//
//  Function:   GetExtendedRightsForNClasses
//
//  Synopsis:   
//              This function gets the ExtendedRigts(control rights,
//              validated rights) and PropertySets for pszClassName and
//              all the classes in AuxTypeList. 
//              Function Merges Extended Rights of all the classes in to a 
//              signle list form which duplicates are removed and list 
//              is sorted.
//              Function Merges PropertySets of all the classes in to a 
//              signle list form which duplicates are removed and list 
//              is sorted.
//
//  Arguments:  [pguidClass - IN] : ObjectGuidType of the class
//              [hAuxList - IN]:List of Auxillary Classes
//              [pszSchemaSearchPath - IN] : Schema Search Path
//              [phERList - OUT]: Output Extended Rights list
//              [phPropSetList - OUT]: Output Propset List
//
//  Returns:    HRESULT : S_OK if everything succeeded
//                        E_INVALIDARG if the object entry wasn't found
//  Note:       Calling function must call DPA_Destroy on *phERList and *phPropSetList
//              to Free the memory
//
//  History:    3-Nov 2000  hiteshr   Created
//
//---------------------------------------------------------------------------
HRESULT
CSchemaCache::GetExtendedRightsForNClasses(IN LPWSTR pszSchemaSearchPath,
                                           IN LPCGUID pguidClass,
                                           IN HDPA    hAuxList,
                                           OUT HDPA *phERList,
                                           OUT HDPA *phPropSetList)
{
    TraceEnter(TRACE_SCHEMA, "CSchemaCache::GetExtendedRightsForNClasses");

    if(!pguidClass || 
       !pszSchemaSearchPath)
    {
        Trace((L"Invalid Input Arguments Passed to CSchemaCache::GetPropertiesForNClasses"));
        return E_INVALIDARG;
    }

    HRESULT hr = S_OK;
    HDPA hERList = NULL;
    HDPA hPropSetList = NULL;
    HDPA hFinalErList = NULL;
    HDPA hFinalPropSetList = NULL;

    //
    //Get the extended rights for pguidClass
    //
    hr = GetExtendedRightsForOneClass(pszSchemaSearchPath,
                                      pguidClass,
                                      &hERList,
                                      &hPropSetList);
    FailGracefully(hr,"GetExtendedRightsForOneClasses failed");

    if(hERList && phERList)
    {
        UINT cCount = DPA_GetPtrCount(hERList);
        hFinalErList = DPA_Create(cCount);
        if(!hFinalErList)
            ExitGracefully(hr, ERROR_NOT_ENOUGH_MEMORY,"DPA_Create Failed");
        //
        //Copy hERList to hFinalErList
        //
        DPA_Merge(hFinalErList,             //Destination
                  hERList,                  //Source
                  DPAM_SORTED|DPAM_UNION,   //Already Sorted And give me union
                  Schema_CompareER,
                  _Merge,
                  0);        
    }                    

    if(hPropSetList && phPropSetList)
    {
        UINT cCount = DPA_GetPtrCount(hPropSetList);
        hFinalPropSetList = DPA_Create(cCount);
        if(!hFinalPropSetList)
            ExitGracefully(hr, ERROR_NOT_ENOUGH_MEMORY,"DPA_Create Failed");

        //
        //Copy hPropSetList to hFinalPropSetList
        //
        DPA_Merge(hFinalPropSetList,             //Destination
                  hPropSetList,                  //Source
                  DPAM_SORTED|DPAM_UNION,   //Already Sorted And give me union
                  Schema_CompareER,
                  _Merge,
                  0);        
    }                    
    //
    //For each auxclass get the extended rights
    //and property sets
    //
    if (hAuxList != NULL)
    {

        UINT cItems = DPA_GetPtrCount(hAuxList);

        while (cItems > 0)
        {
            PAUX_INFO pAI;
            pAI = (PAUX_INFO)DPA_FastGetPtr(hAuxList, --cItems);
            if(IsEqualGUID(pAI->guid, GUID_NULL))
            {
                hr = LookupClassID(pAI->pszClassName, &pAI->guid);
				FailGracefully(hr,"Cache Not available");
            }
            
            hERList = NULL;
            hPropSetList = NULL;
            //
            //Get the ER and PropSet for AuxClass
            //
            hr = GetExtendedRightsForOneClass(pszSchemaSearchPath,
                                              &pAI->guid,
                                              &hERList,
                                              &hPropSetList);
            FailGracefully(hr,"GetExtendedRightsForOneClasses failed");
                        
            if(hERList && phERList)
            {
                if(!hFinalErList)
                {
                    UINT cCount = DPA_GetPtrCount(hERList);
                    hFinalErList = DPA_Create(cCount);

                    if(!hFinalErList)
                        ExitGracefully(hr, ERROR_NOT_ENOUGH_MEMORY,"DPA_Create Failed");
                }
        
                //
                //Merge hERList into hFinalErList
                //
                DPA_Merge(hFinalErList,             //Destination
                          hERList,                  //Source
                          DPAM_SORTED|DPAM_UNION,   //Already Sorted And give me union
                          Schema_CompareER,
                          _Merge,
                          0);        
            }                    
            
            if(hPropSetList && phPropSetList)
            {
                if(!hFinalPropSetList)
                {
                    UINT cCount = DPA_GetPtrCount(hPropSetList);
                    hFinalPropSetList = DPA_Create(cCount);

                    if(!hFinalPropSetList)
                        ExitGracefully(hr, ERROR_NOT_ENOUGH_MEMORY,"DPA_Create Failed");
                }

                //
                //Merge hPropSetList into hFinalPropSetList
                //        
                DPA_Merge(hFinalPropSetList,             //Destination
                          hPropSetList,                  //Source
                          DPAM_SORTED|DPAM_UNION,   //Already Sorted And give me union
                          Schema_CompareER,
                          _Merge,
                          0);        
            }                    

        }
    }

exit_gracefully:
    if(FAILED(hr))
    {
        if(hFinalPropSetList)
            DPA_Destroy(hFinalPropSetList);    

        if(hFinalErList)
            DPA_Destroy(hFinalErList);    

        hFinalErList = NULL;
        hFinalPropSetList = NULL;
    }
    
    if(phERList)
        *phERList = hFinalErList;
    if(phPropSetList)
        *phPropSetList = hFinalPropSetList;                     
    
    TraceLeaveResult(hr);
}

//+--------------------------------------------------------------------------
//
//  Function:   GetChildClassesForNClasses
//
//  Synopsis:   
//              This function gets the childclasses for pszClassName and
//              all the classes in AuxTypeList. Function Merges child clasess
//              of all the classes in to a signle list form which duplicates 
//              are removed and list is sorted
//
//  Arguments:  [pguidObjectType - IN] : ObjectGuidType of the class
//              [pszClassName - IN] : Class Name
//              [hAuxList - IN]:List of Auxillary Classes
//              [pszSchemaPath - IN] : Schema Search Path
//              [phChildList - OUT]: Output Child Classes List
//
//  Returns:    HRESULT : S_OK if everything succeeded
//                        E_INVALIDARG if the object entry wasn't found
//  Note:       Calling function must call DPA_Destroy on *phPropertyList to
//              Free the memory
//
//  History:    3-Nov 2000  hiteshr   Created
//
//---------------------------------------------------------------------------
HRESULT
CSchemaCache::GetChildClassesForNClasses(IN LPCGUID pguidObjectType,
                                             IN LPCWSTR pszClassName,
                                             IN HDPA hAuxList,
                                             IN LPCWSTR pszSchemaPath,
                                             OUT HDPA *phChildList)
{
    TraceEnter(TRACE_SCHEMA, "CSchemaCache::GetChildClassesForNClasses");

    if(!pguidObjectType || 
       !pszSchemaPath ||
       !phChildList)
    {
        Trace((L"Invalid Input Arguments Passed to CSchemaCache::GetPropertiesForNClasses"));
        return E_INVALIDARG;
    }

    HRESULT hr = S_OK;
    HDPA hChildList = NULL;
    HDPA hFinalChildList = NULL;

    //
    //Get the Child Classes for pszClassName
    //
    hr = GetChildClassesForOneClass(pguidObjectType,
                                    pszClassName,
                                    pszSchemaPath,
                                    &hChildList);
    FailGracefully(hr,"GetExtendedRightsForOneClasses failed");

    if(hChildList)
    {
        if(!hFinalChildList)
        {
            UINT cCount = DPA_GetPtrCount(hChildList);
            hFinalChildList = DPA_Create(cCount);
            if(!hFinalChildList)
                ExitGracefully(hr, ERROR_NOT_ENOUGH_MEMORY,"DPA_Create Failed");
        }
        //
        //Copy hChildList to hFinalChildList
        //
        DPA_Merge(hFinalChildList,             //Destination
                  hChildList,                  //Source
                  DPAM_SORTED|DPAM_UNION,      //Already Sorted And give me union
                  Schema_ComparePropDisplayName,
                  _Merge,
                  0);        
    }                    

    //
    //For each class in hAuxList get the child classes
    //
    if (hAuxList != NULL)
    {

        UINT cItems = DPA_GetPtrCount(hAuxList);

        while (cItems > 0)
        {
            PAUX_INFO pAI;
            pAI = (PAUX_INFO)DPA_FastGetPtr(hAuxList, --cItems);
            if(IsEqualGUID(pAI->guid, GUID_NULL))
            {
                hr = LookupClassID(pAI->pszClassName, &pAI->guid);
				FailGracefully(hr,"Cache Not available");
            }
            
            //
            //GetPropertiesForOneClass returns the list of handles
            //from cache so don't delete them. Simply set them to NULL
            //
            hChildList = NULL;
            
            hr = GetChildClassesForOneClass(&pAI->guid,
                                            pAI->pszClassName,
                                            pszSchemaPath,
                                            &hChildList);
            FailGracefully(hr,"GetExtendedRightsForOneClasses failed");
                        
            if(hChildList)
            {
                if(!hFinalChildList)
                {
                    UINT cCount = DPA_GetPtrCount(hChildList);
                    hFinalChildList = DPA_Create(cCount);
                    if(!hFinalChildList)
                        ExitGracefully(hr, ERROR_NOT_ENOUGH_MEMORY,"DPA_Create Failed");
                }
        
                //
                //Merge hChildList into hFinalChildList
                //
                DPA_Merge(hFinalChildList,             //Destination
                          hChildList,                  //Source
                          DPAM_SORTED|DPAM_UNION,      //Already Sorted And give me union
                          Schema_ComparePropDisplayName,
                          _Merge,
                          0);        
            }                    

        }
    }

exit_gracefully:
    if(FAILED(hr))
    {
        if(hFinalChildList)
            DPA_Destroy(hFinalChildList);    

        hFinalChildList = NULL;
    }
    
    //
    //Set the output
    //
    *phChildList = hFinalChildList;                     

    TraceLeaveResult(hr);
}

//+--------------------------------------------------------------------------
//
//  Function:   GetPropertiesForNClasses
//
//  Synopsis:   
//              This function gets the properties for pszClassName and
//              all the classes in AuxTypeList. Function Merges properties
//              of all the classes in to a signle list form which duplicates 
//
//  Arguments:  [pguidObjectType - IN] : ObjectGuidType of the class
//              [pszClassName - IN] : Class Name
//              [hAuxList - IN]:List of Auxillary Classes
//              [pszSchemaPath - IN] : Schema Search Path
//              [phPropertyList - OUT]: Output Property List
//
//  Returns:    HRESULT : S_OK if everything succeeded
//                        E_INVALIDARG if the object entry wasn't found
//  Note:       Calling function must call DPA_Destroy on *phPropertyList to
//              Free the memory
//
//  History:    3-Nov 2000  hiteshr   Created
//
//---------------------------------------------------------------------------
HRESULT
CSchemaCache::
GetPropertiesForNClasses(IN LPCGUID pguidObjectType,
                         IN LPCWSTR pszClassName,
                         IN HDPA hAuxList,
                         IN LPCWSTR pszSchemaPath,
                         OUT HDPA *phPropertyList)
{
    TraceEnter(TRACE_SCHEMA, "CSchemaCache::GetPropertiesForOneClass");

    if(!pguidObjectType || 
       !pszSchemaPath ||
       !phPropertyList)
    {
        Trace((L"Invalid Input Arguments Passed to CSchemaCache::GetPropertiesForNClasses"));
        return E_INVALIDARG;
    }

    HRESULT hr = S_OK;
    HDPA hPropertyList = NULL;
    HDPA hFinalPropertyList = NULL;

    //
    //Get The properties for pszClassName
    //
    hr = GetPropertiesForOneClass(pguidObjectType,
                                  pszClassName,
                                  pszSchemaPath,
                                  &hPropertyList);
    FailGracefully(hr,"GetPropertiesForOneClass failed");

    if(hPropertyList)
    {
        UINT cCount = DPA_GetPtrCount(hPropertyList);
        hFinalPropertyList = DPA_Create(cCount);
        if(!hFinalPropertyList)
            ExitGracefully(hr, ERROR_NOT_ENOUGH_MEMORY,"DPA_Create Failed");

        //
        //Copy hPropertyList to hFinalPropertyList. 
        //
        DPA_Merge(hFinalPropertyList,             //Destination
                  hPropertyList,                  //Source
                  DPAM_SORTED|DPAM_UNION,         //Already Sorted And give me union
                  Schema_ComparePropDisplayName,
                  _Merge,
                  0);        
    }                    

    //
    //Get the properties for each class in hAuxList 
    //and add them to hFinalPropertyList
    //
    if (hAuxList != NULL)
    {

        UINT cItems = DPA_GetPtrCount(hAuxList);

        while (cItems > 0)
        {
            PAUX_INFO pAI;
            pAI = (PAUX_INFO)DPA_FastGetPtr(hAuxList, --cItems);
            if(IsEqualGUID(pAI->guid, GUID_NULL))
            {
                hr = LookupClassID(pAI->pszClassName, &pAI->guid);
				FailGracefully(hr,"Cache Not available");
            }
           
            //
            //GetPropertiesForOneClass returns the list of handles
            //from cache so don't delete them. Simply set them to NULL
            //
            hPropertyList = NULL;
            
            //
            //Get properties for Aux Class
            //
            hr = GetPropertiesForOneClass(&pAI->guid,
                                          pAI->pszClassName,
                                          pszSchemaPath,
                                          &hPropertyList);
            FailGracefully(hr,"GetExtendedRightsForOneClasses failed");
                        
            if(hPropertyList)
            {
                if(!hFinalPropertyList)
                {
                    UINT cCount = DPA_GetPtrCount(hPropertyList);
                    hFinalPropertyList = DPA_Create(cCount);
                    if(!hFinalPropertyList)
                        ExitGracefully(hr, ERROR_NOT_ENOUGH_MEMORY,"DPA_Create Failed");
                }
                //
                //Merge hPropertyList with hFinalPropertyList
                //
                DPA_Merge(hFinalPropertyList,             //Destination
                          hPropertyList,                  //Source
                          DPAM_SORTED|DPAM_UNION,         //Already Sorted And give me union
                          Schema_ComparePropDisplayName,
                          _Merge,
                          0);        
            }                    

        }
    }

exit_gracefully:
    if(FAILED(hr))
    {
        if(hFinalPropertyList)
            DPA_Destroy(hFinalPropertyList);    

        hFinalPropertyList = NULL;
    }
    //
    //Set the Output
    //
    *phPropertyList = hFinalPropertyList;                     

    TraceLeaveResult(hr);
}


//+--------------------------------------------------------------------------
//
//  Function:   DoesPathContainServer
//
//  Synopsis:   
//              Checks if the path contain server name in begining
//  Arguments:  [pszPath - IN] : Path to DS object
//
//  Returns:    BOOL: true if path contain server name
//                    false if not or error occurs    
//
//  History:    27 March 2000  hiteshr   Created
//
//---------------------------------------------------------------------------

bool DoesPathContainServer(LPCWSTR pszPath)
{

	IADsPathname *pPath = NULL;
	BSTR strServerName = NULL;
	bool bReturn = false;
    
	BSTR strObjectPath = SysAllocString(pszPath);
	if(!strObjectPath)
		return false;


	//
	// Create an ADsPathname object to parse the path and get the
	// server name 
	//
	HRESULT hr = CoCreateInstance(CLSID_Pathname,
								  NULL,
								  CLSCTX_INPROC_SERVER,
								  IID_IADsPathname,
								  (LPVOID*)&pPath);
	if (pPath)
	{
		//
		//Set Full Path
		//
		if (SUCCEEDED(pPath->Set(strObjectPath, ADS_SETTYPE_FULL)))
		{
			//
			//Retrieve servername
			//
			hr = pPath->Retrieve(ADS_FORMAT_SERVER, &strServerName);
			if(SUCCEEDED(hr) && strServerName)
			{
				bReturn = true;
			}
		}
	}
	
	DoRelease(pPath);
	if(strServerName)
		SysFreeString(strServerName);
	SysFreeString(strObjectPath);

	return bReturn;
}

//*************************************************************
//
//  OpenDSObject()
//
//  Purpose:    Calls AdsOpenObject with ADS_SERVER_BIND
//  Return:		Same as AdsOpenObject
//
//*************************************************************

HRESULT OpenDSObject (LPTSTR lpPath, LPTSTR lpUserName, LPTSTR lpPassword, DWORD dwFlags, REFIID riid, void FAR * FAR * ppObject)
{
    if (DoesPathContainServer(lpPath))
    {
        dwFlags |= ADS_SERVER_BIND;
    }

    return (ADsOpenObject(lpPath, lpUserName, lpPassword, dwFlags,
                          riid, ppObject));
}