削除済みオブジェクトの取得
削除されたオブジェクトは、削除済みオブジェクトコンテナに格納されます。 通常、削除済みオブジェクトコンテナーは表示されませんが、削除済みオブジェクトコンテナーにはadministratorsグループのメンバーがバインドできます。 削除済みオブジェクトコンテナーの内容を列挙し、IDirectorySearchhインターフェイスとADS_SEARCHPREF_TOMBSTONE検索設定を使用して個々の削除済みオブジェクト属性を取得できます。
削除済みオブジェクトコンテナーは、Ntdsapi.hで定義されている既知のGUID_DELETED_OBJECTS_CONTAINERにバインドすることによって取得できます。 既知のGUIDへのバインドの詳細については、 WKGUIDを使用した既知のオブジェクトへのバインドを参照してください。
削除済みオブジェクトコンテナーにバインドするときは、ADS_FAST_BINDオプションを指定します。 つまり、IADsやIADsPropertyListなどのActive Directory Domain Servicesのオブジェクトを操作するために使用されるADSIインターフェイスは、削除済みオブジェクトコンテナーでは使用できません。 削除済みオブジェクトコンテナーにバインドする方法の詳細とコード例については、以下のGetDeletedObjectsContainer関数の例を参照してください。
削除済みオブジェクトの列挙
IDirectorySearchインターフェイスは、削除されたオブジェクトの検索に使用されます。
削除済みオブジェクトを列挙するには
- 削除済みオブジェクトコンテナーのIDirectorySearchインターフェイスを取得します。 これを行うには、削除済みオブジェクトコンテナーにバインドし、IDirectorySearchインターフェイスを要求します。 削除済みオブジェクトコンテナーにバインドする方法を示すコード例と詳細については、次のGetDeletedObjectsContainer関数の例を参照してください。
- IDirectorySearch::SetSearchPreferenceメソッドを使用して、AADS_SEARCHPREF_SEARCH_SCOPE検索設定をADS_SCOPE_ONELEVELに設定します。 ADS_SCOPE_SUBTREE設定を使用することもできますが、削除済みオブジェクトコンテナーは単一のレベルであるため、ADS_SCOPE_SUBTREEの使用は冗長です。
- ADS_SEARCHPREF_TOMBSTONE検索設定をTRUEに設定します。 これにより、削除されたオブジェクトが検索に含まれるようになります。
- ADS_SEARCHPREF_PAGESIZE 検索設定を1000以下の値に設定します。 これは省略可能ですが、これを行わないと、1000個を超える削除済みオブジェクトを取得できません。
- IDirectorySearch::ExecuteSearch呼び出しの検索フィルターを「(isDeleted=TRUE)」に設定します。 これにより、検索ではisDeleted属性がTRUEに設定されているオブジェクトのみが取得されます。
削除されたオブジェクトを列挙する方法を示すコード例については、次のEnumDeletedObjects関数の例を参照してください。
LDAP Dialectに示すように、検索フィルターに追加することで検索をさらに絞り込むことができる。 たとえば、"Jeff"で始まる名前を持つ削除済みオブジェクトをすべて検索するには、検索フィルターを" ( &(isDeleted=TRUE) (cn=Jeff*)) "に設定します。
削除されたオブジェクトは、削除されるときにほとんどの属性が削除されるため、削除されたオブジェクトに直接バインドすることはできません。 削除されたオブジェクトにバインドする場合は、ADS_FAST_BINDオプションを指定する必要があります。 つまり、IADsやIADsPropertyListなどのActive Directory Domain Servicesオブジェクトを操作するために使用されるADSIインターフェイスは、削除されたオブジェクトコンテナーでは使用できません。
特定の削除済みオブジェクトの検索
特定の削除されたオブジェクトを検索することもできます。 オブジェクトのobjectGUID がわかっている場合は、その特定のobjectGUIDを持つオブジェクトを検索するために使用できます。 特定の削除されたオブジェクトを検索する方法を示すコード例と詳細については、以下のFindDeletedObjectByGUID を参照してください。
GetDeletedObjectsContainer
次のC++コード例は、削除済みオブジェクトコンテナーにバインドする方法を示しています。
//***************************************************************************
//
// 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
次のC++コード例は、削除済みオブジェクトコンテナー内のオブジェクトを列挙する方法を示しています。
//***************************************************************************
//
// 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
次のC++コード例は、オブジェクトのobjectGUIDプロパティから特定の削除済みオブジェクトを検索する方法を示しています。
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;
}