使用 ADS_SEARCHPREF_DIRSYNC的範例程序代碼
下列程式代碼範例會使用目錄同步處理 (DirSync) 控件的 ADSI 實作,搜尋 Active Directory 伺服器的本機網域分割區,以尋找自上一次呼叫之後變更的用戶物件。
程式代碼範例會使用 IDirectorySearch 介面,從網域分割區的根目錄搜尋。 在呼叫 ExecuteSearch 方法之前,此範例會呼叫 SetSearchPreference 方法來指定ADS_SEARCHPREF_SEARCH_SCOPE、ADS_SEARCHPREF_DIRSYNC和ADS_SEARCHPREF_TOMBSTONE搜尋喜好設定。 範圍必須指定子樹搜尋。 使用ADS_SEARCHPREF_DIRSYNC搜尋喜好設定,指定包含 Cookie 長度及其指標的ADS_PROV_SPECIFIC結構。
第一次呼叫此應用程式時,它會指定 Null Cookie 和零長度。 這會導致搜尋作業執行完整讀取,並針對符合篩選條件的所有對象傳回所有要求的屬性。 除了搜尋結果,伺服器會傳回有效的 Cookie 和 Cookie 長度。 在後續執行時,程式會擷取快取的Cookie和長度,並使用它們來擷取自上一次執行以來的變更。
請注意,此範例只會快取登錄中的Cookie和Cookie長度。 在實際的同步處理應用程式中,將參數儲存在與 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;
}