다음을 통해 공유


ADS_SEARCHPREF_DIRSYNC 사용하는 예제 코드

다음 코드 예제에서는 디렉터리 동기화(DirSync) 컨트롤의 ADSI 구현을 사용하여 이전 호출 이후 변경된 사용자 개체에 대한 Active Directory 서버의 로컬 도메인 파티션을 검색합니다.

코드 예제에서는 IDirectorySearch 인터페이스를 사용하여 도메인 파티션의 루트에서 검색합니다. ExecuteSearch 메서드를 호출하기 전에 SetSearchPreference 메서드를 호출하여 ADS_SEARCHPREF_SEARCH_SCOPE, ADS_SEARCHPREF_DIRSYNCADS_SEARCHPREF_TOMBSTONE 검색 기본 설정을 지정합니다. scope 하위 트리 검색을 지정해야 합니다. ADS_SEARCHPREF_DIRSYNC 검색 기본 설정을 사용하여 쿠키의 길이와 포인터를 포함하는 ADS_PROV_SPECIFIC 구조를 지정합니다.

이 애플리케이션이 처음 호출되면 null 쿠키와 길이가 0으로 지정됩니다. 이렇게 하면 검색 작업이 전체 읽기를 수행하여 필터와 일치하는 모든 개체에 대해 요청된 모든 특성을 반환합니다. 서버는 검색 결과와 함께 유효한 쿠키 및 쿠키 길이를 반환합니다. 후속 실행에서 프로그램은 캐시된 쿠키 및 길이를 검색하고 이를 사용하여 이전 실행 이후 변경 내용을 검색합니다.

이 샘플은 단순히 레지스트리의 쿠키 및 쿠키 길이를 캐시합니다. 실제 동기화 애플리케이션에서 Active Directory 서버와 일관되게 유지되는 동일한 스토리지에 매개 변수를 저장합니다. 이렇게 하면 데이터베이스가 백업에서 복원된 경우 매개 변수 및 개체 데이터가 동기화된 상태로 유지됩니다.

//  Add adsiid.lib to project
//  Add activeds.lib to project

#include "stdafx.h"
#include "windows.h"
#include "stdio.h"
#include "activeds.h"
#include "adshlp.h"
#include "atlbase.h"
#include "iads.h"
#include "tchar.h"
#include "string.h"
#include "mbstring.h"
#include "stdlib.h"

typedef struct {WCHAR objectGUID[40];
                WCHAR ADsPath[MAX_PATH];
                WCHAR phoneNumber[32];
                BOOL  isDeleted;
               } MyUserData;

//  Forward declaration.
VOID BuildGUIDString(WCHAR *szGUID, LPBYTE pGUID);
VOID WriteObjectDataToStorage(MyUserData *userdata, BOOL bUpdate);

//  DoDirSyncSearch
HRESULT DoDirSyncSearch(
            LPWSTR pszSearchFilter,  //  Search filter.
            LPWSTR *pAttributeNames, //  Attributes to retrieve.
            DWORD dwAttributes,      //  Number of attributes.
            PUCHAR *ppCookie,        //  Pointer to previous cookie.
            PULONG pulCookieLength,  //  Length of previous cookie.
            LPWSTR szDC)             //  Name of DC to bind to.
{
    IADs *pRootDSE = NULL;
    IDirectorySearch *pSearch = NULL;
    ADS_SEARCH_HANDLE hSearch = NULL;
    ADS_SEARCHPREF_INFO arSearchPrefs[3];
    ADS_PROV_SPECIFIC dirsync;
    ADS_SEARCH_COLUMN col;
    HRESULT hr = S_OK;
    VARIANT var;
    MyUserData userdata;
    BOOL bUpdate = FALSE;
    DWORD dwCount = 0;
    
    //  Validate input parameters.
    if (!pulCookieLength || !ppCookie || !szDC) 
    {
        wprintf(L"Invalid parameter.\n");
        return E_FAIL;
    }
    
    LPOLESTR szDSPath = new OLECHAR[MAX_PATH];
    LPOLESTR szServerPath = new OLECHAR[MAX_PATH];
    VariantInit(&var);
    
    //  If cookie is non-NULL, this is an update. Otherwise, it is a full-read.
    if (*ppCookie)
        bUpdate = TRUE;
    CoInitialize(NULL);
    
    //  If there is a DC name from the previous USN sync, 
    //  include it in the binding string.
    if (szDC[0]) 
    {
        wcsncpy_s(szServerPath,MAX_PATH,L"LDAP://",MAX_PATH);
        wcsncat_s(szServerPath, MAX_PATH,szDC,MAX_PATH-wcslen(szServerPath));
        wcsncat_s(szServerPath, MAX_PATH,L"/",MAX_PATH-wcslen(szServerPath));
    }
    else
        wcsncpy_s(szServerPath, MAX_PATH,L"LDAP://",MAX_PATH);
    
    // Bind to root DSE.
    wcsncpy_s(szDSPath,MAX_PATH,szServerPath,MAX_PATH);
    wcsncat_s(szDSPath, MAX_PATH,L"rootDSE",MAX_PATH-wcslen(szDSPath));
    wprintf(L"RootDSE binding string: %s\n", szDSPath);
    hr = ADsGetObject(szDSPath, 
                  IID_IADs,
                  (void**)&pRootDSE);
    if (FAILED(hr)) 
    {
        wprintf(L"failed to bind to rootDSE: 0x%x\n", hr);
        goto cleanup;
    }
    
    //  Save the name of the DC connected to in order to connect to 
    //  the same DC on the next dirsync operation.
    if (! szDC[0])
    {
        hr = pRootDSE->Get(CComBSTR("DnsHostName"), &var);
        wcsncpy_s(szServerPath,MAX_PATH,L"LDAP://",MAX_PATH);
        wcsncat_s(szServerPath, MAX_PATH,var.bstrVal, MAX_PATH-wcslen(szServerPath));
        wcsncat_s(szServerPath, MAX_PATH,L"/", MAX_PATH-wcslen(szServerPath));
    }
    
    //  Get an IDirectorySearch pointer to the root of the domain partition.
    hr = pRootDSE->Get(CComBSTR("defaultNamingContext"), &var);
    wcsncpy_s(szDSPath,MAX_PATH,szServerPath,MAX_PATH);
    wcsncat_s(szDSPath, MAX_PATH,var.bstrVal, MAX_PATH - wcslen(szDSPath));
    hr = ADsGetObject(szDSPath, IID_IDirectorySearch, (void**) &pSearch);
    if (FAILED(hr)) 
    {
        wprintf(L"failed to get IDirectorySearch: 0x%x\n", hr);
        goto cleanup;
    }
    
    //  Initialize the structure to pass in the cookie.
    //  On the first call, the cookie is NULL and the length is zero.
    //  On later calls, the cookie and length are the values returned by 
    //  the previous call.
    dirsync.dwLength = *pulCookieLength;
    dirsync.lpValue = *ppCookie;
    arSearchPrefs[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE; 
    arSearchPrefs[0].vValue.dwType = ADSTYPE_INTEGER; 
    arSearchPrefs[0].vValue.Integer = ADS_SCOPE_SUBTREE; 
    arSearchPrefs[1].dwSearchPref = ADS_SEARCHPREF_DIRSYNC; 
    arSearchPrefs[1].vValue.dwType = ADSTYPE_PROV_SPECIFIC;
    arSearchPrefs[1].vValue.ProviderSpecific = dirsync;
    hr = pSearch->SetSearchPreference(arSearchPrefs, 2);
    if (FAILED(hr)) 
    {
        wprintf(L"failed to set search prefs: 0x%x\n", hr);
        goto cleanup;
    }
    
    // Search for the objects indicated by the search filter.
    hr = pSearch->ExecuteSearch(pszSearchFilter,
                    pAttributeNames, dwAttributes, &hSearch );
    if (FAILED(hr)) 
    {
        wprintf(L"failed to set execute search: 0x%x\n", hr);
        goto cleanup;
    }
    
    //  Loop through the rows of the search result
    //  Each row is an object that has changed since the previous call.
    hr = pSearch->GetNextRow( hSearch);
    while ( SUCCEEDED(hr) && hr != S_ADS_NOMORE_ROWS )
    {
        ZeroMemory(&userdata, sizeof(MyUserData) );
        
        //  Retrieve the attributes for each object.
        //  With a DirSync search, a GetColumn call can return success even
        //  though no values are set for the specified attribute. If this
        //  happens, col.pADsValues is NULL; the following code checks
        //  col.pADsValues before trying to access it.
        //  Get the telephone number.
        hr = pSearch->GetColumn( hSearch, L"telephoneNumber", &col );
        if ( SUCCEEDED(hr) ) 
        {
            if (col.dwADsType == ADSTYPE_CASE_IGNORE_STRING && col.pADsValues)
                wcscpy_s(userdata.phoneNumber, col.pADsValues->CaseIgnoreString);
            pSearch->FreeColumn( &col );
        }
        
        //  Get the isDeleted attribute.
        hr = pSearch->GetColumn( hSearch, L"isDeleted", &col );
        if ( SUCCEEDED(hr) ) 
        {
            if (col.dwADsType == ADSTYPE_BOOLEAN && col.pADsValues)
                userdata.isDeleted = col.pADsValues->Boolean;
            pSearch->FreeColumn( &col );
        }
        
        //  Get the ADsPath.
        hr = pSearch->GetColumn( hSearch, L"ADsPath", &col );
        if ( SUCCEEDED(hr) ) 
        {
            if (col.dwADsType == ADSTYPE_CASE_IGNORE_STRING && col.pADsValues)
                wcscpy_s(userdata.ADsPath, col.pADsValues->CaseIgnoreString);
            pSearch->FreeColumn( &col );
        }
        
        //  Get the objectGUID number.
        hr = pSearch->GetColumn( hSearch, L"objectGUID", &col );
        if ( SUCCEEDED(hr) ) 
        {
            WCHAR szGUID[40]; //  string version of the objectGUID attribute
            if ((col.dwADsType == ADSTYPE_OCTET_STRING) && col.pADsValues &&
                (col.pADsValues->OctetString.lpValue))
            {
                BuildGUIDString(szGUID, (LPBYTE) col.pADsValues->OctetString.lpValue);
                wcscpy_s(userdata.objectGUID, szGUID);
            }
            pSearch->FreeColumn( &col );
        }
        
        WriteObjectDataToStorage(&userdata, bUpdate);
        dwCount++;
        hr = pSearch->GetNextRow( hSearch);
    }
    wprintf(L"dwCount: %d\n", dwCount);

    //  After looping through the results, get the cookie.
    if (hr == S_ADS_NOMORE_ROWS ) 
    {
        hr = pSearch->GetColumn( hSearch, ADS_DIRSYNC_COOKIE, &col );
        if ( SUCCEEDED(hr) ) {
            if (col.dwADsType == ADSTYPE_PROV_SPECIFIC && col.pADsValues) 
            {
                wprintf(L"Got cookie\n");
                *pulCookieLength = col.pADsValues->ProviderSpecific.dwLength;
                *ppCookie = (PUCHAR) AllocADsMem (*pulCookieLength);
                memcpy(*ppCookie, col.pADsValues->ProviderSpecific.lpValue, 
                    *pulCookieLength);
            }
            pSearch->FreeColumn( &col );
        } else
            wprintf(L"no cookie: 0x%x\n", hr);
    }

cleanup:
    if (pRootDSE)
        pRootDSE->Release();
    if (hSearch)
        pSearch->CloseSearchHandle(hSearch);
    if (pSearch)
        pSearch->Release();

    VariantClear(&var);
    CoUninitialize();
    delete [] szServerPath;
    delete [] szDSPath;

    return hr;
}

//  WriteObjectDataToStorage routine
VOID WriteObjectDataToStorage(MyUserData *userdata, BOOL bUpdate)
{
    if (bUpdate)
        wprintf(L"UPDATE:\n");
    else
        wprintf(L"INITIAL DATA:\n");
    wprintf(L"   objectGUID: %s\n", userdata->objectGUID);
    wprintf(L"   ADsPath: %s\n", userdata->ADsPath);
    wprintf(L"   phoneNumber: %s\n", userdata->phoneNumber);
    if (userdata->isDeleted)
        wprintf(L"   DELETED OBJECT\n");
    wprintf(L"---------------------------------------------\n");
    return;

}

//  WriteCookieAndDCtoStorage routine
//  This example caches the cookie in the registry. In a real 
//  synchronization application, store these parameters in the
//  same storage that you are keeping consistent with Active Directory.
//  This ensures that the parameters and object data remain in sync if 
//  the storage is ever restored from a backup.
DWORD WriteCookieAndDCtoStorage(UCHAR *pCookie, 
                                ULONG ulLength,
                                WCHAR *pszDCName) 
{
    HKEY hReg = NULL;
    DWORD dwStat = NO_ERROR;

    //  Create a registry key under 
    //  HKEY_CURRENT_USER\SOFTWARE\Vendor\Product.
    dwStat = RegCreateKeyExW(HKEY_CURRENT_USER,
                             L"Software\\Microsoft\\Windows 2000 AD-Synchro-DirSync",
                             0,
                             NULL,
                             REG_OPTION_NON_VOLATILE,
                             KEY_ALL_ACCESS,
                             NULL,
                             &hReg,
                             NULL);
    if (dwStat != NO_ERROR) 
    {
        wprintf(L"RegCreateKeyEx failed: 0x%x\n", dwStat);
        return dwStat;
    }

    //  Cache the cookie as a value under the registry key.
    dwStat = RegSetValueExW(hReg, L"Cookie", 0, REG_BINARY,
                            (const BYTE *)pCookie, ulLength);
    if (dwStat != NO_ERROR)
        wprintf(L"RegSetValueEx for cookie failed: 0x%x\n", dwStat);

    //  Cache the cookie length as a value under the registry key.
    dwStat = RegSetValueExW(hReg, L"Cookie Length", 0, REG_DWORD,
                            (const BYTE *)&ulLength, sizeof(DWORD) );
    if (dwStat != NO_ERROR)
        wprintf(L"RegSetValueEx for cookie length failed: 0x%x\n", dwStat);

    //  Cache the DC name as a value under the registry key.
    dwStat = RegSetValueExW(hReg, L"DC name", 0, REG_SZ,
        (const BYTE *)pszDCName, 2*(wcslen(pszDCName)) );
    if (dwStat != NO_ERROR)
        wprintf(L"RegSetValueEx for DC name failed: 0x%x\n", dwStat);
    RegCloseKey(hReg);
    return dwStat;
}

//  GetCookieAndDCfromStorage routine
DWORD GetCookieAndDCfromStorage(PUCHAR *ppCookie,        //  Receives pointer to cookie.
                                PULONG pulCookieLength,  //  Receives length of cookie.
                                WCHAR *pszDCName)        //  Receives name of DC to bind to.
{ 
    HKEY hReg = NULL;
    DWORD dwStat;
    DWORD dwLen;

    //  Open the registry key.
    dwStat = RegOpenKeyExW(HKEY_CURRENT_USER,
                           L"Software\\Microsoft\\Windows 2000 AD-Synchro-DirSync",
                           0,
                           KEY_QUERY_VALUE,
                           &hReg);
    if (dwStat != NO_ERROR) 
    {
        wprintf(L"RegOpenKeyEx failed: 0x%x\n", dwStat);
        return dwStat;
    }

    //  Get the length of the cookie from the registry.
    dwLen = sizeof(DWORD);
    dwStat = RegQueryValueExW(hReg, L"Cookie Length", NULL, NULL, 
                              (LPBYTE)pulCookieLength, &dwLen );
    if (dwStat != NO_ERROR) {
        wprintf(L"RegQueryValueEx failed to get length: 0x%x\n", dwStat);
        return dwStat;
    }

    //  Allocate a buffer for the cookie value.
    *ppCookie = (PUCHAR) GlobalAlloc(GPTR, *pulCookieLength);
    if (!*ppCookie) 
    {
        wprintf(L"GlobalAlloc failed: %u\n", GetLastError() );
        return dwStat;
    }

    //  Get the cookie from the registry.
    dwStat = RegQueryValueExW(hReg, L"Cookie", NULL, NULL, 
                              (LPBYTE)*ppCookie, pulCookieLength );
    if (dwStat != NO_ERROR) {
        wprintf(L"RegQueryValueEx failed to get cookie: 0x%x\n", dwStat);
        return dwStat;
    }

    //  Get the DC name from the registry.
    dwLen = MAX_PATH;
    dwStat = RegQueryValueExW(hReg, L"DC name", NULL, NULL, 
                              (LPBYTE)pszDCName, &dwLen );
    if (dwStat != NO_ERROR) {
        wprintf(L"RegQueryValueEx failed to get DC name: 0x%x\n", dwStat);
        return dwStat;
    }
    else
        pszDCName[dwLen] = 0;
    RegCloseKey(hReg);
    return NO_ERROR;
}

//  BuildGUIDString
//  Routine that makes the GUID into a string in directory service bind form
VOID 
    BuildGUIDString(WCHAR *szGUID, LPBYTE pGUID)
{
    DWORD i = 0x0;
    DWORD dwlen = sizeof(GUID);
    WCHAR buf[4];

    wcscpy_s(szGUID,MAX_PATH,L"");

    for (i;i<dwlen;i++) 
    {
        swprintf_s(buf, L"%02x", pGUID[i]);
        wcscat_s(szGUID, MAX_PATH,buf);
    }
}

//  Entry point for application
int main(int argc, char* argv[])
{
    DWORD dwStat;
    ULONG ulLength;
    UCHAR *pCookie;
    WCHAR szDCName[MAX_PATH];
    HRESULT hr;

    if (argc==1) 
    {
        //  Perform a full synchronization.
        //  Initialize the synchronization parameters to zero or NULL.
        wprintf(L"Performing a full sync.\n");
        szDCName[0] = '\0';
        ulLength = 0;
        pCookie = NULL;
    } 
    else
    {
        //  Perform an incremental synchronization.
        //  Initialize synchronization parameters from storage.
        wprintf(L"Retrieving changes only.\n");
        dwStat = GetCookieAndDCfromStorage(&pCookie, &ulLength, szDCName);
        if (dwStat != NO_ERROR) 
        {
            wprintf(L"Could not get the cookie: %u\n", dwStat);
            goto cleanup;
        }
    }

    //  Perform the search and update the synchronization parameters.
    hr = DoDirSyncSearch(L"(objectClass=user)",
                         NULL,       //  Retrieve all attributes.
                         -1, 
                         &pCookie, 
                         &ulLength,
                         szDCName);
    if (FAILED(hr)) 
    {
        wprintf(L"DoDirSyncSearch failed: 0x%x\n", hr);
        goto cleanup;
    }

    //  Cache the returned synchronization parameters in storage.
    wprintf(L"Caching the synchronization parameters.\n");
    dwStat = WriteCookieAndDCtoStorage(pCookie, ulLength, szDCName);
    if (dwStat != NO_ERROR) 
    {
        wprintf(L"Could not cache the cookie: %u\n", dwStat);
        goto cleanup;
    }

cleanup:
    if (pCookie)
        GlobalFree (pCookie);
    return 1;
}