Bagikan melalui


Mengambil Objek yang Dihapus

Objek yang dihapus disimpan dalam kontainer Objek Dihapus. Kontainer Objek Dihapus biasanya tidak terlihat, tetapi kontainer Objek Dihapus dapat diikat oleh anggota grup administrator. Konten kontainer Objek Yang Dihapus dapat dijumlahkan dan atribut objek yang dihapus individual dapat diperoleh menggunakan antarmuka IDirectorySearch dengan preferensi pencarian ADS_SEARCHPREF_TOMBSTONE.

Kontainer Objek Yang Dihapus dapat diperoleh dengan mengikat guid terkenal GUID_DELETED_OBJECTS_CONTAINER yang ditentukan dalam Ntdsapi.h. Untuk informasi selengkapnya tentang pengikatan ke GUID terkenal, lihat Mengikat ke Objek Terkenal Menggunakan WKGUID.

Tentukan opsi ADS_FAST_BIND saat mengikat ke kontainer Objek Dihapus. Ini berarti bahwa antarmuka ADSI yang digunakan untuk bekerja dengan objek di Active Directory Domain Services, seperti IAD dan IADsPropertyList, tidak dapat digunakan pada kontainer Objek Yang Dihapus. Untuk informasi selengkapnya dan contoh kode yang menunjukkan cara mengikat ke kontainer Objek Yang Dihapus, lihat fungsi contoh GetDeletedObjectsContainer di bawah ini.

Menghitung Objek Yang Dihapus

Antarmuka IDirectorySearch digunakan untuk mencari objek yang dihapus.

Untuk menghitung objek yang dihapus

  1. Dapatkan antarmuka IDirectorySearch untuk kontainer Objek Yang Dihapus. Ini dicapai dengan mengikat ke kontainer Objek Dihapus dan meminta antarmuka IDirectorySearch . Untuk informasi selengkapnya dan contoh kode yang memperlihatkan cara mengikat ke kontainer Objek Terhapus, lihat contoh fungsi GetDeletedObjectsContainer berikut.
  2. Atur preferensi pencarian ADS_SEARCHPREF_SEARCH_SCOPE ke ADS_SCOPE_ONELEVEL menggunakan metode IDirectorySearch::SetSearchPreference . Preferensi ADS_SCOPE_SUBTREE juga dapat digunakan, tetapi kontainer Objek Yang Dihapus hanya satu tingkat, jadi menggunakan ADS_SCOPE_SUBTREE berlebihan.
  3. Atur preferensi pencarian ADS_SEARCHPREF_TOMBSTONE ke TRUE. Ini menyebabkan pencarian menyertakan objek yang dihapus.
  4. Atur preferensi pencarian ADS_SEARCHPREF_PAGESIZE ke nilai kurang dari, atau sama dengan, 1000. Ini opsional, tetapi jika ini tidak dilakukan, tidak lebih dari 1000 objek yang dihapus dapat diambil.
  5. Atur filter pencarian di panggilan IDirectorySearch::ExecuteSearch ke "(isDeleted=TRUE)". Ini menyebabkan pencarian hanya mengambil objek dengan atribut isDeleted yang diatur ke TRUE.

Untuk contoh kode kode yang memperlihatkan cara menghitung objek yang dihapus, lihat contoh fungsi EnumDeletedObjects berikut.

Pencarian dapat disempurnakan lebih lanjut dengan menambahkan ke filter pencarian seperti yang ditunjukkan dalam Dialek LDAP. Misalnya, untuk mencari semua objek yang dihapus dengan nama yang dimulai dengan "Jeff", filter pencarian akan diatur ke "(&(isDeleted=TRUE)(cn=Jeff*))".

Karena objek yang dihapus memiliki sebagian besar atribut yang dihapus saat dihapus, tidak dimungkinkan untuk mengikat langsung ke objek yang dihapus. Opsi ADS_FAST_BIND harus ditentukan saat mengikat ke objek yang dihapus. Ini berarti bahwa antarmuka ADSI yang digunakan untuk bekerja dengan objek Active Directory Domain Services, seperti IAD dan IADsPropertyList, tidak dapat digunakan pada kontainer objek yang dihapus.

Menemukan Objek Tertentu yang Dihapus

Dimungkinkan juga untuk menemukan objek tertentu yang dihapus. Jika objectGUID objek diketahui, objek dapat digunakan untuk mencari objek dengan objectGUID tertentu tersebut. Untuk informasi selengkapnya dan contoh kode yang memperlihatkan cara menemukan objek tertentu yang dihapus, lihat FindDeletedObjectByGUID di bawah ini.

GetDeletedObjectsContainer

Contoh kode C++ berikut menunjukkan cara mengikat ke kontainer Objek Dihapus.

//***************************************************************************
//
//  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;
}

EnumDeletedObjects

Contoh kode C++ berikut menunjukkan cara menghitung objek dalam kontainer Objek Yang Dihapus.

//***************************************************************************
//
//  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

Contoh kode C++ berikut menunjukkan cara menemukan objek tertentu yang dihapus dari properti objectGUID objek.

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;
}