使用 IDirectorySearch 接口进行搜索

IDirectorySearch 接口为查询目录或全局目录的数据提供了一个高级且低开销的接口。 IDirectorySearch COM 接口只能与 vtable 配合使用,因此基于自动化的开发环境无法使用。

执行搜索

  1. 绑定到目录中的对象。
  2. 调用 QueryInterface 以获取 IDirectorySearch 指针。
  3. 使用 IDirectorySearch 指针来运行搜索。 调用 IDirectorySearch::ExecuteSearch 方法,并传递搜索筛选器、请求的属性名称和其他参数。

有关搜索筛选器语法的详细信息,请参阅搜索筛选器语法

查询的执行取决于提供程序。 对于某些提供程序,在调用 IDirectorySearch::GetFirstRowIDirectorySearch::GetNextRow 之前不会执行实际查询。 IDirectorySearch 接口可直接使用搜索筛选器。 既不需要 SQL 方言,也不需要 LDAP 方言。

IDirectorySearch 接口提供了逐行枚举结果集的方法。 IDirectorySearch::GetFirstRow 方法会检索第一行,IDirectorySearch::GetNextRow 方法会从当前行移动到下一行。 当查询到最后一行时,调用这些方法将返回 S_ADS_NOMORE_ROWS 错误代码。 相反,IDirectorySearch::GetPreviousRow 则是每次向后移动一行。 S_ADS_NOMORE_ROWS 返回值表示已到达结果集的第一行。 这些方法会对客户端内存中驻留的结果集进行操作。 因此,在执行分页和异步搜索且关闭 _CACHE_RESULTS 选项时,向后滚动可能会产生意外的后果。

找到相应的行后,调用 IDirectorySearch::GetColumn 可逐列获取数据项。 每次调用时,你都要传递感兴趣列的名称。 返回的数据项是指向 ADS_SEARCH_COLUMN 结构的指针。 GetColumn 会为你分配此结构,但必须使用 FreeColumn 来释放它。 调用 CloseSearchHandle 以完成搜索操作。

执行目录搜索

  1. 绑定到 LDAP 提供程序。 它可以是域控制器或全局目录提供程序。

  2. 通过调用 QueryInterface 检索 IDirectorySearch COM 接口;此操作可能已在步骤 1 初始绑定时完成。

    (可选)调用 SetSearchPreference 以选择用于处理搜索结果的选项。

  3. 调用 ExecuteSearch。 根据 SetSearchPreference 中设置的选项,这样可能会也可能不会开始执行查询。

  4. 调用 GetNextRow 将行索引(IDirectorySearch 的内部索引)移至第一行。

  5. 使用 GetColumn 从行中读取数据,然后调用 FreeColumn 释放 GetColumn 分配的内存。

  6. 重复步骤 5,直到从搜索结果中检索到所有数据,或直到 GetNextRow 返回 S_ADS_NOMORE_ROWS。

  7. 完成后,调用 AbandonSearchCloseSearchHandle

下面的代码示例演示此方法。 要开始绑定到 ADSI,请调用 ADsOpenObject 函数。

HRESULT hr = S_OK; // COM result variable
ADS_SEARCH_COLUMN col;  // COL for iterations
LPWSTR szUsername = NULL; // user name
LPWSTR szPassword = NULL; // password
 
// Interface Pointers.
IDirectorySearch     *pDSSearch    =NULL;
 
// Initialize COM.
CoInitialize(0);

// Add code to securely retrieve the user name and password or
// leave both as NULL to use the default security context.
 
// Open a connection with server.
hr = ADsOpenObject(L"LDAP://coho.salmon.Fabrikam.com", 
szUsername,
szPassword,
ADS_SECURE_AUTHENTICATION,
IID_IDirectorySearch,
(void **)&pDSSearch);

这样会提供一个指向 IDirectorySearch 接口的指针。

现在有了 IDirectoryInterface 实例的 COM 接口,请调用 IDirectorySearch::SetSearchPreference

构建一个属性名称数组,为调用 IDirectorySearch::ExecuteSearch 函数做好准备。 属性名称在 Active Directory 的架构中定义。 有关架构定义的详细信息,请参阅 ADSI 架构模型。 如果对象支持,列出的属性名称将作为搜索结果集返回。

LPWSTR pszAttr[] = { L"description", L"Name", L"distinguishedname" };
ADS_SEARCH_HANDLE hSearch;
DWORD dwCount = 0;
DWORD dwAttrNameSize = sizeof(pszAttr)/sizeof(LPWSTR);

现在,调用 ExecuteSearch 函数。 在调用 GetNextRow 方法之前不会运行搜索。

// Search for all objects with the 'cn' property that start with c.
hr = pDSSearch->ExecuteSearch(L"(cn=c*)",pszAttr ,dwAttrNameSize,&hSearch );

调用 GetNextRow 以循环访问结果中的行。 然后,查询每一行的“description”属性。 如果找到该属性,就会显示出来。

LPWSTR pszColumn;
    while( pDSSearch->GetNextRow( hSearch) != S_ADS_NOMORE_ROWS )
    {
        // Get the property.
        hr = pDSSearch->GetColumn( hSearch, L"description", &col );
 
        // If this object supports this attribute, display it.
        if ( SUCCEEDED(hr) )
        { 
           if (col.dwADsType == ADSTYPE_CASE_IGNORE_STRING)
              wprintf(L"The description property:%s\r\n", col.pADsValues->CaseIgnoreString); 
           pDSSearch->FreeColumn( &col );
        }
        else
            puts("description property NOT available");
        puts("------------------------------------------------");
        dwCount++;
    }
    pDSSearch->CloseSearchHandle(hSearch);
    pDSSearch->Release();

要结束搜索,请调用 AbandonSearch 方法。

请注意,如果未设置页面大小,GetNextRow 会阻止,直到整个结果集返回到客户端。 如果设置了页面大小,则 GetNextRow 会阻止,直到返回第一页(页面大小 = 页中的行数)。 如果设置了页面大小,而查询要进行排序,并且尚未对至少一个索引属性进行搜索,那么页面大小值将被忽略,服务器将在返回数据之前计算整个结果集。 这会影响 GetNextRow 阻止,直到查询完成。

注意

要将此查询从目录搜索改为全局目录搜索,则需要更改 ADsOpenObject 调用。

 

// Open a connection with the server.
hr = ADsOpenObject(L"GC://coho.salmon.Fabrikam.com", 
szUsername, 
szPassword, 
ADS_SECURE_AUTHENTICATION,
IID_IDirectorySearch,
(void **)&pDSSearch);