Bind to Deleted Objects Container

Bind to Deleted Objects Container

Post by Rajes » Wed, 14 Aug 2002 07:13:23



I can bind to the deleted objects container using the
IDirectorySearch Interface. How do I bind to the deleted
objects container in AD using .Net classes or from Visual
Basic-(cannot use IDirectorySearch there).
 
 
 

Bind to Deleted Objects Container

Post by MVP for II » Wed, 14 Aug 2002 21:46:18


Copied and pasted from the MSDN see http://msdn.microsoft.com/library and
the ADSI reference
Example Code to Retrieve Changes Using USNChanged

The following code example uses the uSNChanged attribute of an Active
Directory object to retrieve changes since a previous query. The code
example can perform either a full synchronization or an incremental update.
For a full synchronization, the sample application connects to the rootDSE
of a domain controller and reads the following parameters that it stores to
be used in the next incremental synchronization:

  a.. The DNS name of the DC. Incremental synchronizations must be performed
on the same DC as the previous synchronization.
  b.. The invocationID GUID of the DC. The code example uses this value to
detect that the DC has been restored from a backup, in which case, the
sample must perform a full synchronization.
  c.. The highestCommittedUSN. This value becomes the lower bound for the
uSNChanged filter on the next incremental synchronization.
The code example uses the IDirectorySearch interface, specifying the
distinguished name of the base of the search, a search scope, and a filter.
There are no restrictions on the search base or scope. In addition to
specifying the objects of interest, the filter must also specify a
uSNChanged comparison, such as (uSNChanged>=lowerBoundUSN). For a full
synchronization, lowerBoundUSN is zero. For an incremental synchronization,
it is the 1 plus the higestCommittedUSN value from the previous search.

Note that this sample application is intended only to show how to use
uSNChanged to retrieve changes from Active Directory. It prints the changes
and does not actually synchronize the data in a secondary storage.
Consequently, it does not show how to handle issues like moved objects or
"no parent" conditions. It does show how to retrieve deleted objects, but it
does not show how an application uses the objectGUID of the deleted objects
to determine the corresponding object to delete in the storage.

Also, the sample caches the DC name, invocationID, and higestCommittedUSN in
the registry. In a real synchronization application, you must store the
parameters in the same storage that are kept consistent with Active
Directory. This ensures that the parameters and object data remain
synchronized if your database is ever restored from a backup.

#include <windows.h>
#include <stdio.h>
#include <activeds.h>
#include <ntdsapi.h>
#include <atlbase.h>

typedef struct _MYUSERDATA
{
    WCHAR objectGUID[40];
    WCHAR distinguishedName[MAX_PATH];
    WCHAR phoneNumber[32];

} MYUSERDATA, *PMYUSERDATA;

#define ARRAYSIZE(__buf__) (sizeof(__buf__)/sizeof(__buf__[0]))

// Forward declaration
VOID BuildGUIDString(WCHAR *szGUID, LPBYTE pGUID);
VOID WriteObjectDataToStorage(PMYUSERDATA pUserData, BOOL bUpdate);
VOID DeleteObjectDataFromStorage(PMYUSERDATA pUserData);

//********************************************************************
// DoUSNSyncSearch
//********************************************************************
HRESULT DoUSNSyncSearch(
    LPWSTR pszSearchBaseDN,      // Distinguished name of search base
    ULONG ulScope,               // Scope of the search
    LPWSTR *pAttributeNames,     // Attributes to retrieve
    DWORD dwAttributes,          // Number of attributes
    LPWSTR pszPrevInvocationID,  // GUID string for DC's invocationID
    LPWSTR pszPrevHighUSN,       // Highest USN from previous sync
    LPWSTR pszDC)                // Name of DC to bind to
{
    LPWSTR pszServerPath = NULL;
    LPWSTR pszDSPath = NULL;
    IADs *pRootDSE = NULL;
    IADs *pDCService = NULL;
    IADs *pDeletedObj = NULL;
    IDirectorySearch *pSearch = NULL;
    ADS_SEARCH_HANDLE hSearch = NULL;
    ADS_SEARCHPREF_INFO arSearchPrefs[3];
    WCHAR szSearchFilter[256];    // Search filter
    ADS_SEARCH_COLUMN col;
    MYUSERDATA userdata;
    void HUGEP *pArray;
    WCHAR szGUID[40];
    INT64 iLowerBoundUSN;
    HRESULT hr = E_FAIL;
    DWORD dwCount = 0;
    VARIANT var;
    BOOL bUpdate = TRUE;

    // Validate input parameters.
    if (!pszPrevInvocationID || !pszPrevHighUSN || !pszDC)
    {
        wprintf(L"Invalid parameter.\n");
        return E_INVALIDARG;
    }

    VariantInit(&var);

    // Allocate the server path string buffer.
    pszServerPath = new WCHAR[7 + wcslen(pszDC) + 1 + 1];
    if(!pszServerPath)
    {
        wprintf(L"failed to allocate memory");
        hr = E_OUTOFMEMORY;
        goto cleanup;
    }

    // If there exists a DC name from the previous USN synchronization,
    // include it in the binding string.
    if (pszDC[0])
    {
        wcscpy(pszServerPath, L"LDAP://");
        wcscat(pszServerPath, pszDC);
        wcscat(pszServerPath, L"/");
    }
    else
    {
        wcscpy(pszServerPath, L"LDAP://");
    }

    // Allocate the DS path string buffer.
    pszDSPath = new WCHAR[wcslen(pszServerPath) + 7 + 1];
    if(!pszDSPath)
    {
        wprintf(L"failed to allocate memory");
        hr = E_OUTOFMEMORY;
        goto cleanup;
    }

    // Bind to the root DSE.
    wcscpy(pszDSPath, pszServerPath);
    wcscat(pszDSPath, L"rootDSE");
    hr = ADsOpenObject(pszDSPath,
                    NULL,
                    NULL,
                    ADS_SECURE_AUTHENTICATION,
                    IID_IADs,
                    (void**)&pRootDSE);
    if (FAILED(hr))
    {
        wprintf(L"failed to bind to root: 0x%x\n", hr);
        goto cleanup;
    }

    // Get the name of the DC connected to.
    hr = pRootDSE->Get(CComBSTR("DnsHostName"), &var);
    if (FAILED(hr))
    {
        wprintf(L"failed to get DnsHostName: 0x%x\n", hr);
        goto cleanup;
    }

    // Compare it to the DC name from the previous USN sync operation.
    // If not the same, perform a full synchronization.
    if (_wcsicmp(pszDC, var.bstrVal) != 0)
    {
        bUpdate = FALSE;

        // Reallocate the string buffer.
        delete pszServerPath;
        pszServerPath = new WCHAR[7 + wcslen(var.bstrVal) + 1 + 1];
        if(!pszServerPath)
        {
            wprintf(L"failed to allocate memory");
            hr = E_OUTOFMEMORY;
            goto cleanup;
        }

        // Use the DC name in the bind string prefix.
        wcscpy(pszServerPath, L"LDAP://");
        wcscat(pszServerPath, var.bstrVal);
        wcscat(pszServerPath, L"/");
    }

    // Bind to the DC service object to get the invocationID.
    // The dsServiceName property of root DSE contains the distinguished
    // name of this DC service object.
    VariantClear(&var);
    hr = pRootDSE->Get(CComBSTR("dsServiceName"), &var);
    if (FAILED(hr))
    {
        wprintf(L"failed to get \"dsServiceName\"\n");
        goto cleanup;
    }

    // Reallocate the DS path buffer.
    delete pszDSPath;
    pszDSPath = new WCHAR[wcslen(pszServerPath) + wcslen(var.bstrVal) + 1];
    if(!pszDSPath)
    {
        wprintf(L"failed to allocate memory");
        hr = E_OUTOFMEMORY;
        goto cleanup;
    }

    wcscpy(pszDSPath, pszServerPath);
    wcscat(pszDSPath, var.bstrVal);
    hr = ADsOpenObject(pszDSPath,
                    NULL,
                    NULL,
                    ADS_SECURE_AUTHENTICATION,
                    IID_IADs,
                    (void**)&pDCService);
    VariantClear(&var);
    if (FAILED(hr))
    {
        wprintf(L"failed to bind to the DC service object: 0x%x\n", hr);
        goto cleanup;
    }

    // Get the invocationID GUID from the service object.
    hr = pDCService->Get(CComBSTR("invocationID"), &var);
    if (FAILED(hr))
    {
        wprintf(L"failed to get \"invocationID\"\n");
        goto cleanup;
    }

    hr = SafeArrayAccessData((SAFEARRAY*)(var.pparray), (void HUGEP*
FAR*)&pArray);
    if (FAILED(hr))
    {
        wprintf(L"failed to get hugep: 0x%x\n", hr);
        goto cleanup;
    }

    BuildGUIDString(szGUID, (LPBYTE)pArray);
    VariantClear(&var);

    // Compare the invocationID GUID to the GUID string from the previous
    // synchronization. If not the same, this is a different DC or the DC
    // was restored from backup; perform a full synchronization.
    if (_wcsicmp(szGUID, pszPrevInvocationID)!=0)
    {
        bUpdate = FALSE;
        wcscpy(pszPrevInvocationID, szGUID);  // Save the invocationID GUID.
    }

    // If previous high USN is an empty string, handle this as a full
synchronization.
    if (pszPrevHighUSN[0] == '\0')
    {
        bUpdate = FALSE;
    }

    // Set the lower bound USN to zero if this is a full synchronization.
    // Otherwise, set it to the previous high USN plus one.
    if (bUpdate == FALSE)
    {
        iLowerBoundUSN = 0;
    }
    else
    {
        iLowerBoundUSN = _wtoi64(pszPrevHighUSN) + 1; // Convert the string
to an integer.
    }

    // Get and save the current high USN.
    hr = pRootDSE->Get(CComBSTR("highestCommittedUSN"), &var);
    if (FAILED(hr))
    {
        wprintf(L"failed to get \"highestCommittedUSN\"\n");
        goto cleanup;
    }

    wcscpy(pszPrevHighUSN, var.bstrVal);
    wprintf(L"current highestCommittedUSN: %s\n", pszPrevHighUSN);
    VariantClear(&var);

    // Reallocate the DS path buffer.
    delete pszDSPath;
    pszDSPath = new WCHAR[wcslen(pszServerPath) + wcslen(pszSearchBaseDN) +
1];
    if(!pszDSPath)
    {
        wprintf(L"failed to allocate memory");
        hr = E_OUTOFMEMORY;
        goto cleanup;
    }

    // Get an IDirectorySearch pointer to the base of the search.
    wcscpy(pszDSPath, pszServerPath);
    wcscat(pszDSPath, pszSearchBaseDN);
    hr = ADsOpenObject(pszDSPath,
                    NULL,
                    NULL,
                    ADS_SECURE_AUTHENTICATION,
                    IID_IDirectorySearch,
                    (void**)&pSearch);
    if (FAILED(hr))
    {
        wprintf(L"failed to get IDirectorySearch: 0x%x\n", hr);
        goto cleanup;
    }

    // Set up the scope and page size search preferences.
    arSearchPrefs [0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
    arSearchPrefs
...

read more »

 
 
 

Bind to Deleted Objects Container

Post by MVP for II » Wed, 14 Aug 2002 21:57:56


Sorry,
forgot to add that the search syntax in dotnet is practially the same as in
any other search using the DirectorySearcher (wrapper for IDirectorySearch)
class.
In the MSDN there are some simple samples that show how this works.

--

 
 
 

Bind to Deleted Objects Container

Post by Rajes » Fri, 16 Aug 2002 04:54:29


With VB.Net if I try to bind to the deleted objects
container using the path
"LDAP://testserver/<WKGUID=18E2EA80684F11D2B9AA00C04F79F805
,dc=testdomain,dc=com>" , I get an error message
saying 'There is no such object on the server'.
Hence I cannot pass this DirectoryEntry as the root
argument to the DirectorySearcher object.
I am logged in as Administrator.

What is the correct call to bind to the deleted objects
container? What are the requirements - like being logged
in as administrator etc?

Rajesh

Quote:>-----Original Message-----
>Sorry,
>forgot to add that the search syntax in dotnet is

practially the same as in
Quote:>any other search using the DirectorySearcher (wrapper for
IDirectorySearch)
>class.
>In the MSDN there are some simple samples that show how
this works.

>--

>.

 
 
 

1. Deleted Objects container

I am trying to query the objects in the "Deleted Objects" container using
the IDirectorySearch interface.  In order to do this I have to first bind to
the container.  I use the bind path
LDAP://machine:389/<WKGUID=GUID_DELETED_OBJECTS_CONTAINER_W,defaultNamingCon
text>

where GUID_DELETED_OBJECTS_CONTAINER_W is the value of the constant defined
in NtDsAPI.h and defaultNamingContext is the value of that attribute in
LDAP://machine:389/rootDSE.

I get an error back saying that the object does not exist.  I used ADsView,
ADSI Edit and User&Computers in Advanced mode and it appears that in fact
the container doesn't exist, but when trying to create it in ADSI Edit under
the domain using the appropriate objectGUID it says that it already exists.

Since the only reason for the Deleted Objects container is for replication
does it only show up when replication is configured?  Is there some other
configuration parameter that I need to set to get it to show up or get
created for real?

Robin H. Sanner

2. Failed Password Blocks Remote Sharing Attempts

3. Can't bind to users container

4. ODS V8.2 and V9 at SAS User Group meeting May 22

5. How to create a new container object?

6. Designer/2000 Training

7. Creating child objects in a container

8. W2K Server on NT Domain!

9. Querying the 'Parent/Container' object for an LDAP user

10. Can't rename object in configuration container

11. Changing IIS ADSI container object from IISWebDirectory to IISWebVirtualDirectory

12. How Delete Entries From LDAP Containers?