Contoh Kode Menggunakan ADS_SEARCHPREF_DIRSYNC
Contoh kode berikut menggunakan implementasi ADSI dari kontrol sinkronisasi direktori (DirSync) untuk mencari partisi domain lokal server Direktori Aktif untuk objek pengguna yang diubah sejak panggilan sebelumnya.
Contoh kode menggunakan antarmuka IDirectorySearch untuk mencari dari akar partisi domain. Sebelum memanggil metode ExecuteSearch, contoh memanggil metode SetSearchPreference untuk menentukan preferensi pencarian ADS_SEARCHPREF_SEARCH_SCOPE, ADS_SEARCHPREF_DIRSYNC, dan ADS_SEARCHPREF_TOMBSTONE. Cakupan harus menentukan pencarian subtree. Dengan preferensi pencarian ADS_SEARCHPREF_DIRSYNC, tentukan struktur ADS_PROV_SPECIFIC yang berisi panjang cookie dan pointer ke dalamnya.
Pertama kali aplikasi ini dipanggil, aplikasi ini menentukan cookie null dan panjang nol. Hal ini menyebabkan operasi pencarian melakukan bacaan penuh, mengembalikan semua atribut yang diminta untuk semua objek yang cocok dengan filter. Seiring dengan hasil pencarian, server mengembalikan cookie yang valid dan panjang cookie. Pada eksekusi berikutnya, program mengambil cookie cache dan panjang dan menggunakannya untuk mengambil perubahan sejak eksekusi sebelumnya.
Ketahuilah bahwa sampel ini hanya menyimpan cookie dan panjang cookie dalam registri. Dalam aplikasi sinkronisasi nyata, simpan parameter dalam penyimpanan yang sama dengan yang Anda jaga agar konsisten dengan server Direktori Aktif. Ini memastikan bahwa parameter dan data objek tetap sinkron jika database Anda pernah dipulihkan dari cadangan.
// 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;
}