Platform SDK: Active Directory, ADSI, and Directory Services |
The following sample code shows how a client application searches the global catalog for an SCP. In this sample, the client application has a hard-coded GUID string that identifies the service. The service's installation program stored the same GUID string as one of the values of the SCPs multi-valued keywords attribute.
This sample actually consists of two routines. The GetGC routine retrieves an IDirectorySearch pointer for a global catalog (GC). The ScpLocate routine uses the IDirectorySearch methods to search the GC.
The GC contains a partial replica of every object in the forest, but it does not contain all of the SCP attributes that the client needs. First, the client must search the GC to find the SCP and retrieve its DN. Then the client uses the SCP's DN to bind to an IDirectoryObject pointer on the SCP. The client then calls the IDirectoryObject::GetObjectAttributes method to retrieve the rest of the attributes.
DWORD ScpLocate( TCHAR *pszDN, // Returns distinguished name of SCP TCHAR *pszServiceDNSName, // Returns service's DNS name TCHAR *pszServiceDNSNameType, // Returns type of DNS name TCHAR *pszClass, // Returns name of service class USHORT *pusPort) // Returns service port { HRESULT hr; // Params for IDirectoryObject LPOLESTR szSCPPath = new OLECHAR[MAX_PATH]; IDirectoryObject *pSCP = NULL; ADS_ATTR_INFO *pPropEntries = NULL; DWORD dwNumAttrGot; // Structures and Params for IDirectorySearch IDirectorySearch *pSearch = NULL; DWORD dwPref, dwAttrs; TCHAR szQuery[255]; ADS_SEARCH_COLUMN Col; ADS_SEARCH_HANDLE hSearch = NULL; ADS_SEARCHPREF_INFO SearchPref[2]; // Properties to retrieve from the SCP object. TCHAR *szAttribs[]={ {TEXT("distinguishedName")}, {TEXT("serviceClassName")}, {TEXT("serviceDNSName")}, {TEXT("serviceDNSNameType")}, {TEXT("serviceBindingInformation")} }; // First, get an IDirectorySearch pointer for the global catalog. // See the GetGC sample code below. hr = GetGC(&pSearch); if (FAILED(hr)) { ReportError(TEXT("GetGC failed"), hr); goto Cleanup; } // Set up the search. We want to do a deep search. // Note that we are not expecting thousands of objects // in this example, so we will ask for 1000 rows / page. dwPref=sizeof(SearchPref)/sizeof(ADS_SEARCHPREF_INFO); SearchPref[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE; SearchPref[0].vValue.dwType = ADSTYPE_INTEGER; SearchPref[0].vValue.Integer = ADS_SCOPE_SUBTREE; SearchPref[1].dwSearchPref = ADS_SEARCHPREF_PAGESIZE; SearchPref[1].vValue.dwType = ADSTYPE_INTEGER; SearchPref[1].vValue.Integer = 1000; hr = pSearch->SetSearchPreference(SearchPref, dwPref); fprintf (stderr, "SetSearchPreference: 0x%x\n", hr); if (FAILED(hr)) { fprintf (stderr, "Failed to set search prefs: hr:0x%x\n", hr); goto Cleanup; } // Search for an exact match on our product GUID. _tcscpy(szQuery, TEXT("keywords=A762885A-AA44-11d2-81F1-00C04FB9624E")); // Execute the search. From the GC we can get the distinguished name // of the SCP. Use the DN to bind to the SCP and get the other // properties. hr = pSearch->ExecuteSearch(szQuery, szAttribs, 1, &hSearch); fprintf (stderr, "ExecuteSearch: 0x%x\n", hr); if (FAILED(hr)) { ReportError(TEXT("ExecuteSearch failed."), hr); goto Cleanup; } // Loop through the results. Each row should be an instance of the // service identified by the product GUID. // TODO: Add logic to select from multiple service instances. hr = pSearch->GetNextRow(hSearch); while (SUCCEEDED(hr) && hr !=S_ADS_NOMORE_ROWS) { hr = pSearch->GetColumn(hSearch, TEXT("distinguishedName"), &Col); if (FAILED(hr)) break; _tcscpy(pszDN, Col.pADsValues->CaseIgnoreString); pSearch->FreeColumn(&Col); hr = pSearch->GetNextRow(hSearch); } // Now bind to the DN to get the other properties. wcscpy(szSCPPath, L"LDAP://"); wcscat(szSCPPath, pszDN); hr = ADsGetObject(szSCPPath, IID_IDirectoryObject, (void**)&pSCP); if (SUCCEEDED(hr)) { dwAttrs=sizeof(szAttribs)/sizeof(LPWSTR); hr = pSCP->GetObjectAttributes( szAttribs, dwAttrs, &pPropEntries, &dwNumAttrGot); if(FAILED(hr)) { ReportError(TEXT("GetObjectAttributes Failed."), hr); goto Cleanup; } // Loop through the entries returned by GetObjectAttributes // and save the values in the appropriate buffers. for (int i=0;i<(LONG)dwAttrs;i++) { if (_tcscmp(TEXT("distinguishedName"), pPropEntries[i].pszAttrName) ==0) { _tcscpy(pszDN, pPropEntries[i].pADsValues->CaseIgnoreString); } if (_tcscmp(TEXT("serviceDNSName"), pPropEntries[i].pszAttrName)==0) { _tcscpy(pszServiceDNSName, pPropEntries[i].pADsValues->CaseIgnoreString); } if (_tcscmp(TEXT("serviceDNSNameType"), pPropEntries[i].pszAttrName)==0) { _tcscpy(pszServiceDNSNameType, pPropEntries[i].pADsValues->CaseIgnoreString); } if (_tcscmp(TEXT("serviceClassName"), pPropEntries[i].pszAttrName)==0) { _tcscpy(pszClass, pPropEntries[i].pADsValues->CaseIgnoreString); } if (_tcscmp(TEXT("serviceBindingInformation"), pPropEntries[i].pszAttrName)==0) { *pusPort=(USHORT)_ttoi( pPropEntries[i].pADsValues->CaseIgnoreString); } } } Cleanup: if (pSCP) pSCP->Release(); if (pPropEntries) FreeADsMem(pPropEntries); if (hSearch) pSearch->CloseSearchHandle(hSearch); return hr; } //*********************************************************** // GetGC // Retrieves an IDirectorySearch pointer for a global catalog (GC) //*********************************************************** HRESULT GetGC(IDirectorySearch **ppDS) { HRESULT hr; IEnumVARIANT *pEnum = NULL; IADsContainer *pCont = NULL; VARIANT var; IDispatch *pDisp = NULL; ULONG lFetch; // Set IDirectorySearch pointer to NULL. *ppDS = NULL; // First, bind to the GC: namespace container object. The "real" GC DN // is a single immediate child of the GC: namespace, which must // be obtained using enumeration. hr = ADsOpenObject(TEXT("GC:"), NULL, NULL, ADS_SECURE_AUTHENTICATION, //Use Secure Authentication IID_IADsContainer, (void**)&pCont); if (FAILED(hr)) { _tprintf(TEXT("ADsOpenObject failed: 0x%x\n"), hr); goto cleanup; } // Fetch an enumeration interface for the GC container. hr = ADsBuildEnumerator(pCont, &pEnum); if (FAILED(hr)) { _tprintf(TEXT("ADsBuildEnumerator failed: 0x%x\n"), hr); goto cleanup; } //Now enumerate. There's only one child of the GC: object. hr = ADsEnumerateNext(pEnum, 1, &var, &lFetch); if (FAILED(hr)) { _tprintf(TEXT("ADsEnumerateNext failed: 0x%x\n"), hr); goto cleanup; } if (( hr == S_OK ) && ( lFetch == 1 ) ) { pDisp = V_DISPATCH(&var); hr = pDisp->QueryInterface( IID_IDirectorySearch, (void**)ppDS); } cleanup: if (pEnum) ADsFreeEnumerator(pEnum); if (pCont) pCont->Release(); if (pDisp) (pDisp)->Release(); return hr; }