Condividi tramite


Recupero di oggetti eliminati

Gli oggetti eliminati vengono archiviati nel contenitore Oggetti eliminati. Il contenitore Oggetti eliminati non è in genere visibile, ma il contenitore Oggetti eliminati può essere associato da un membro del gruppo administrators. È possibile enumerare il contenuto del contenitore Oggetti eliminati e ottenere singoli attributi di oggetto eliminati usando l'interfacciaIDirectorySearchcon la preferenza di ricerca ADS_SEARCHPREF_TOMBSTONE.

Il contenitore Oggetti eliminati può essere ottenuto tramite l'associazione al noto GUID, GUID_DELETED_OBJECTS_CONTAINER, definito in Ntdsapi.h. Per ulteriori informazioni sull'associazione a GUID noti, vedere la sezione "Associazione di a oggetti Well-Known utilizzando WKGUID".

Specificare l'opzione ADS_FAST_BIND quando si esegue l'associazione al contenitore Oggetti eliminati. Ciò significa che le interfacce ADSI usate per lavorare con un oggetto in Servizi di dominio Active Directory, ad esempio ID e IADsPropertyList, non possono essere usate nel contenitore Oggetti eliminati. Per altre informazioni e un esempio di codice che illustra come eseguire l'associazione al contenitore Oggetti eliminati, vedere la funzione di esempio GetDeletedObjectsContainer riportata di seguito.

Enumerazione di oggetti eliminati

L'interfacciaIDirectorySearchviene usata per cercare gli oggetti eliminati.

Per enumerare gli oggetti eliminati

  1. Ottenere l'interfacciaIDirectorySearchper il contenitore Oggetti eliminati. Questa operazione viene eseguita tramite l'associazione al contenitore Oggetti eliminati e richiedendo l'interfaccia IDirectorySearch. Per altre informazioni e un esempio di codice che illustra come eseguire l'associazione al contenitore Oggetti eliminati, vedere l'esempio di funzione GetDeletedObjectsContainer.
  2. Impostare la preferenza di ricerca ADS_SEARCHPREF_SEARCH_SCOPE su ADS_SCOPE_ONELEVEL usando il metodo IDirectorySearch::SetSearchPreference. È anche possibile usare la preferenza ADS_SCOPE_SUBTREE, ma il contenitore Oggetti eliminati è un solo livello, quindi l'uso di ADS_SCOPE_SUBTREE è ridondante.
  3. Impostare la preferenza di ricerca ADS_SEARCHPREF_TOMBSTONE su TRUE. In questo modo, la ricerca include oggetti eliminati.
  4. Impostare la preferenza di ricerca ADS_SEARCHPREF_PAGESIZE su un valore minore o uguale a 1000. Questa operazione è facoltativa, ma se questa operazione non viene eseguita, non è possibile recuperare più di 1000 oggetti eliminati.
  5. Imposta il filtro di ricerca nella chiamata a IDirectorySearch::ExecuteSearch a "(isDeleted=TRUE)". In questo modo la ricerca recupera solo gli oggetti con l'attributo isDeleted impostato su TRUE.

Per un codice di esempio di codice che illustra come enumerare gli oggetti eliminati, vedere l'esempio di funzione EnumDeletedObjects seguente.

La ricerca può essere ulteriormente perfezionata aggiungendo al filtro di ricerca come illustrato in dialetto LDAP. Ad esempio, per cercare tutti gli oggetti eliminati con un nome che inizia con "Jeff", il filtro di ricerca verrà impostato su "(&(isDeleted=TRUE)(cn=Jeff*))).

Poiché gli oggetti eliminati hanno la maggior parte dei relativi attributi rimossi quando vengono eliminati, non è possibile associare direttamente a un oggetto eliminato. L'opzione ADS_FAST_BIND deve essere specificata quando si esegue l'associazione a un oggetto eliminato. Ciò significa che le interfacce ADSI usate per lavorare con un oggetto Servizi di dominio Active Directory, ad esempio ID e IADsPropertyList, non possono essere usate in un contenitore di oggetti eliminati.

Ricerca di un oggetto eliminato specifico

È anche possibile trovare un oggetto eliminato specifico. Se il objectGUID dell'oggetto è noto, può essere usato per cercare l'oggetto con tale objectGUID specifico. Per altre informazioni e un esempio di codice che illustra come trovare un oggetto eliminato specifico, vedere FindDeletedObjectByGUID di seguito.

RecuperaContenitoreOggettiEliminati

Nell'esempio di codice C++ seguente viene illustrato come eseguire l'associazione al contenitore Oggetti eliminati.

//***************************************************************************
//
//  GetDeletedObjectsContainer()
//
//  Binds to the Deleted Object container.
//
//***************************************************************************

HRESULT GetDeletedObjectsContainer(IADsContainer **ppContainer)
{
    if(NULL == ppContainer)
    {
        return E_INVALIDARG;
    }

    HRESULT hr;
    IADs *pRoot;

    *ppContainer = NULL;

    // Bind to the rootDSE object.
    hr = ADsOpenObject(L"LDAP://rootDSE",
                    NULL,
                    NULL,
                    ADS_SECURE_AUTHENTICATION,
                    IID_IADs,
                    (LPVOID*)&pRoot);
    if(SUCCEEDED(hr))
    {
        VARIANT var;
        
        VariantInit(&var);

        // Get the current domain DN.
        hr = pRoot->Get(CComBSTR("defaultNamingContext"), &var);
        if(SUCCEEDED(hr))
        {
            // Build the binding string.
            LPWSTR pwszFormat = L"LDAP://<WKGUID=%s,%s>";
            LPWSTR pwszPath;

            pwszPath = new WCHAR[wcslen(pwszFormat) + wcslen(GUID_DELETED_OBJECTS_CONTAINER_W) + wcslen(var.bstrVal)];
            if(NULL != pwszPath)
            {
                swprintf_s(pwszPath, pwszFormat, GUID_DELETED_OBJECTS_CONTAINER_W, var.bstrVal);

                // Bind to the object.
                hr = ADsOpenObject(pwszPath,
                                NULL,
                                NULL,
                                ADS_FAST_BIND | ADS_SECURE_AUTHENTICATION,
                                IID_IADsContainer,
                                (LPVOID*)ppContainer);

                delete pwszPath;
            }
            else
            {
                hr = E_OUTOFMEMORY;
            }

            VariantClear(&var);        
        }

        pRoot->Release(); 
    }

    return hr;
}

EnumeraOggettiEliminati

Nell'esempio di codice C++ seguente viene illustrato come enumerare gli oggetti nel contenitore Oggetti eliminati.

//***************************************************************************
//
//  EnumDeletedObjects()
//
//  Enumerates all of the objects in the Deleted Objects container.
//
//***************************************************************************

HRESULT EnumDeletedObjects()
{
    HRESULT hr;
    IADsContainer *pDeletedObjectsCont = NULL;
    IDirectorySearch *pSearch = NULL;

    hr = GetDeletedObjectsContainer(&pDeletedObjectsCont);
    if(FAILED(hr))
    {
        goto cleanup;
    }

    hr = pDeletedObjectsCont->QueryInterface(IID_IDirectorySearch, (LPVOID*)&pSearch);    
    if(FAILED(hr))
    {
        goto cleanup;
    }

    ADS_SEARCH_HANDLE hSearch;

    // Only search for direct child objects of the container.
    ADS_SEARCHPREF_INFO rgSearchPrefs[3];
    rgSearchPrefs[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
    rgSearchPrefs[0].vValue.dwType = ADSTYPE_INTEGER;
    rgSearchPrefs[0].vValue.Integer = ADS_SCOPE_ONELEVEL;

    // Search for deleted objects.
    rgSearchPrefs[1].dwSearchPref = ADS_SEARCHPREF_TOMBSTONE;
    rgSearchPrefs[1].vValue.dwType = ADSTYPE_BOOLEAN;
    rgSearchPrefs[1].vValue.Boolean = TRUE;

    // Set the page size.
    rgSearchPrefs[2].dwSearchPref = ADS_SEARCHPREF_PAGESIZE;
    rgSearchPrefs[2].vValue.dwType = ADSTYPE_INTEGER;
    rgSearchPrefs[2].vValue.Integer = 1000;

    // Set the search preference
    hr = pSearch->SetSearchPreference(rgSearchPrefs, ARRAYSIZE(rgSearchPrefs));
    if(FAILED(hr))
    {
        goto cleanup;
    }

    // Set the search filter.
    LPWSTR pszSearch = L"(cn=*)";

    // Set the attributes to retrieve.
    LPWSTR rgAttributes[] = {L"cn", L"distinguishedName", L"lastKnownParent"};

    // Execute the search
    hr = pSearch->ExecuteSearch(    pszSearch,
                                    rgAttributes,
                                    ARRAYSIZE(rgAttributes),
                                    &hSearch);
    if(SUCCEEDED(hr))
    {    
        // Call IDirectorySearch::GetNextRow() to retrieve the next row of data
        while(S_OK == (hr = pSearch->GetNextRow(hSearch)))
        {
            ADS_SEARCH_COLUMN col;
            UINT i;
            
            // Enumerate the retrieved attributes.
            for(i = 0; i < ARRAYSIZE(rgAttributes); i++)
            {
                hr = pSearch->GetColumn(hSearch, rgAttributes[i], &col);
                if(SUCCEEDED(hr))
                {
                    switch(col.dwADsType)
                    {
                        case ADSTYPE_CASE_IGNORE_STRING:
                        case ADSTYPE_DN_STRING:
                        case ADSTYPE_PRINTABLE_STRING:
                        case ADSTYPE_NUMERIC_STRING:
                        case ADSTYPE_OCTET_STRING:
                            wprintf(L"%s: ", rgAttributes[i]); 
                            for(DWORD x = 0; x < col.dwNumValues; x++)
                            {
                                wprintf(col.pADsValues[x].CaseIgnoreString); 
                                if((x + 1) < col.dwNumValues)
                                {
                                    wprintf(L","); 
                                }
                            }
                            wprintf(L"\n"); 
                            break;
                    }

                    pSearch->FreeColumn(&col);
                }
            }

            wprintf(L"\n");
        }

        // Close the search handle to cleanup.
        pSearch->CloseSearchHandle(hSearch);
    }

cleanup:

    if(pDeletedObjectsCont)
    {
        pDeletedObjectsCont->Release();
    }

    if(pSearch)
    {
        pSearch->Release();
    }

    return hr;
}

FindDeletedObjectByGUID

Nell'esempio seguente di codice C++, viene mostrato come trovare un oggetto eliminato specifico dalla proprietà objectGUID dell'oggetto.

HRESULT FindDeletedObjectByGUID(IADs *padsDomain, GUID *pguid)
{
    HRESULT hr;
    IDirectorySearch *pSearch = NULL;
    LPWSTR pwszGuid = NULL;

    hr = padsDomain->QueryInterface(IID_IDirectorySearch, (LPVOID*)&pSearch);    
    if(FAILED(hr))
    {
        goto cleanup;
    }

    ADS_SEARCH_HANDLE hSearch;

    // Search the entire tree.
    ADS_SEARCHPREF_INFO rgSearchPrefs[2];
    rgSearchPrefs[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
    rgSearchPrefs[0].vValue.dwType = ADSTYPE_INTEGER;
    rgSearchPrefs[0].vValue.Integer = ADS_SCOPE_SUBTREE;

    // Search for deleted objects.
    rgSearchPrefs[1].dwSearchPref = ADS_SEARCHPREF_TOMBSTONE;
    rgSearchPrefs[1].vValue.dwType = ADSTYPE_BOOLEAN;
    rgSearchPrefs[1].vValue.Boolean = TRUE;

    // Set the search preference.
    hr = pSearch->SetSearchPreference(rgSearchPrefs, 2);
    if(FAILED(hr))
    {
        goto cleanup;
    }

    // Set the search filter.
    hr = ADsEncodeBinaryData((LPBYTE)pguid, sizeof(GUID), &pwszGuid);
    if(FAILED(hr))
    {
        goto cleanup;
    }

    LPWSTR pwszFormat = L"(objectGUID=%s)";

    LPWSTR pwszSearch = new WCHAR[lstrlenW(pwszFormat) + lstrlenW(pwszGuid) + 1];
    if(NULL == pwszSearch)
    {
        goto cleanup;
    }
    
    swprintf_s(pwszSearch, pwszFormat, pwszGuid);
    
    FreeADsMem(pwszGuid);

    // Set the attributes to retrieve.
    LPWSTR rgAttributes[] = {L"cn", L"distinguishedName", L"lastKnownParent"};

    // Execute the search.
    hr = pSearch->ExecuteSearch(    pwszSearch,
                                    rgAttributes,
                                    3,
                                    &hSearch);
    if(SUCCEEDED(hr))
    {    
        // Call IDirectorySearch::GetNextRow() to retrieve the next row of data.
        while(S_OK == (hr = pSearch->GetNextRow(hSearch)))
        {
            ADS_SEARCH_COLUMN col;
            UINT i;
            
            // Enumerate the retrieved attributes.
            for(i = 0; i < ARRAYSIZE(rgAttributes); i++)
            {
                hr = pSearch->GetColumn(hSearch, rgAttributes[i], &col);
                if(SUCCEEDED(hr))
                {
                    switch(col.dwADsType)
                    {
                        case ADSTYPE_CASE_IGNORE_STRING:
                        case ADSTYPE_DN_STRING:
                        case ADSTYPE_PRINTABLE_STRING:
                        case ADSTYPE_NUMERIC_STRING:
                            wprintf(L"%s: ", rgAttributes[i]); 
                            for(DWORD x = 0; x < col.dwNumValues; x++)
                            {
                                wprintf(col.pADsValues[x].CaseIgnoreString); 
                                if((x + 1) < col.dwNumValues)
                                {
                                    wprintf(L","); 
                                }
                            }
                            wprintf(L"\n"); 
                            break;

                        case ADSTYPE_OCTET_STRING:
                            wprintf(L"%s: ", rgAttributes[i]); 
                            for(DWORD x = 0; x < col.dwNumValues; x++)
                            {
                                GUID guid;
                                LPBYTE pb = col.pADsValues[x].OctetString.lpValue;
                                WCHAR wszGUID[MAX_PATH];

                                // Convert the octet string into a GUID.
                                guid.Data1 = *((long*)pb);
                                pb += sizeof(guid.Data1);
                                guid.Data2 = *((short*)pb);
                                pb += sizeof(guid.Data2);
                                guid.Data3 = *((short*)pb);
                                pb += sizeof(guid.Data3);
                                CopyMemory(&guid.Data4, pb, sizeof(guid.Data4));

                                // Convert the GUID into a string.
                                StringFromGUID2(guid, wszGUID, MAX_PATH);
                                wprintf(wszGUID);

                                if((x + 1) < col.dwNumValues)
                                {
                                    wprintf(L","); 
                                }
                                OutputDebugStringW(wszGUID);
                                OutputDebugStringW(L"\n");

                                {
                                    DWORD a;

                                    for(a = 0, *wszGUID = 0; a < col.pADsValues[x].OctetString.dwLength; a++)
                                    {
                                        swprintf_s(wszGUID + lstrlenW(wszGUID), L"%02X", *(LPBYTE)(col.pADsValues[x].OctetString.lpValue + a));
                                    }

                                    OutputDebugStringW(wszGUID);
                                    OutputDebugStringW(L"\n");
                                }
                            }
                            wprintf(L"\n"); 
                            break;

                    }

                    pSearch->FreeColumn(&col);
                }
            }

            wprintf(L"\n");
        }

        // Close the search handle to cleanup.
        pSearch->CloseSearchHandle(hSearch);
    }

cleanup:

    if(pwszSearch)
    {
        delete pwszSearch;
    }
    
    if(pSearch)
    {
        pSearch->Release();
    }

    return hr;
}