Microsoft KB Archive/325189

From BetaArchive Wiki
Knowledge Base


INFO: Truncated Results When Calling IDirectorySearch::GetNextRow

Article ID: 325189

Article Last Modified on 2/12/2004



APPLIES TO

  • Microsoft Active Directory Service Interfaces 2.5
  • Microsoft Active Directory Service Interfaces 2.5



This article was previously published under Q325189

SUMMARY

MSDN documentation about the IDirectorySearch::GetNextRow function is incomplete. The description of return value S_ADS_NOMORE_ROWS is false. This code does not mean "The current row is the last row and remains to be unchanged". Instead, the description should say "for now, current row is the last row", and if you want to know if this is really the last row, you have to call ADsGetLastError."

For each failed operation, ADSI sets an extended error code, which can be queried using ADsGetLastError. If this extended error is not handled by developer, a search operation may conclude prematurely before all rows have been processed. For additional information about the latest service pack for Microsoft Windows 2000, click the following article number to view the article in the Microsoft Knowledge Base:

260910 How to Obtain the Latest Windows 2000 Service Pack


MORE INFORMATION

When the IDirectorySearch::GetNextRow function returns S_ADS_NOMORE_ROWS, it may not have retrieved all the data from the server. In some cases, S_ADS_NOMORE_ROWS is returned by GetNextRow function when the server was unable to find an entry that matched the search criteria within a predefined two-minute time limit. This two-minute time limit is defined by means of an LDAP policy. For additional information about LDAP policies, click the article number below to view the article in the Microsoft Knowledge Base:

315071 HOW TO: View and Set LDAP Policies by Using Ntdsutil.exe


If the server exceeds the two-minute time limit, it returns an LDAP cookie in the response so that you can restart the search where it left off. Inefficient searches and heavily loaded systems can cause the server to exceed the time limit. When the server cannot find an efficient index to search, the server may have to apply the filter to every object in the directory, in which case it can run through many entries and not find a match within the two-minute time limit.

Therefore, when returning S_ADS_NOMORE_ROWS, ADSI also sets an extended error code, which can be queried using ADsGetLastError function. If ADsGetLastError returns ERROR_MORE_DATA, it means that the server has not completed the query and must call GetNextRow again.

The Platform SDK sample file, DsSrch, can be modified to handle this search method as shown in the following code sample:

"MAIN.CXX" file
//------------------------------------------------------------------
// 
//  Function:   Q325189_GetNextRow
// 
//  Synopsis:
// 
//------------------------------------------------------------------
HRESULT Q325189_GetNextRow(IDirectorySearch *pDSSearch, ADS_SEARCH_HANDLE hSearchHandle)
{
    HRESULT hr = S_OK;
    DWORD dwADsExtError = ERROR_SUCCESS;
    WCHAR szErrorBuf[512];
    WCHAR szNameBuf[128];

    do
    {
        // Clear ADSI extended error
        dwADsExtError = ERROR_SUCCESS;
        ADsSetLastError(ERROR_SUCCESS, NULL, NULL);

        // Next row
        hr = pDSSearch->GetNextRow(hSearchHandle);
        BAIL_ON_FAILURE(hr);

        // Check ADSI extend error if we got S_ADS_NOMORE_ROWS
        if (S_ADS_NOMORE_ROWS == hr)
        {
            hr = ADsGetLastError(&dwADsExtError, szErrorBuf, 512, szNameBuf, 128);
            BAIL_ON_FAILURE(hr);

            if (ERROR_MORE_DATA != dwADsExtError)
                // All data received
                return ERROR_NO_DATA;
        }

    } while (ERROR_MORE_DATA == dwADsExtError);

    return ERROR_MORE_DATA;

error:
    return hr;
}


//------------------------------------------------------------------
// 
//  Function:   main
// 
//  Synopsis:
// 
//------------------------------------------------------------------
INT _CRTAPI1
main(int argc, char * argv[])
{
...
    // *** MODIFIED FOR Q325189 *** 
    //hr = pDSSearch->GetNextRow(
    //         hSearchHandle
    //         );
    //BAIL_ON_FAILURE(hr);
    hr = Q325189_GetNextRow(pDSSearch, hSearchHandle); 
    // *** EOM ***

    // *** MODIFIED FOR Q325189 *** 
    //while (hr != S_ADS_NOMORE_ROWS && nRows < dwMaxRows) {<BR/>
         BAIL_ON_FAILURE(hr);
    while (nRows < dwMaxRows) {
    // *** EOM ***
        nRows++;

        if (dwNumberAttributes == -1) {
            hr = pDSSearch->GetNextColumnName(
                     hSearchHandle,
                     &pszColumnName
                     );
            BAIL_ON_FAILURE(hr);

            while (hr != S_ADS_NOMORE_COLUMNS) {
                hr = pDSSearch->GetColumn(
                         hSearchHandle,
                         pszColumnName,
                         &Column
                         );

                if (FAILED(hr)  && hr != E_ADS_COLUMN_NOT_SET)
                    goto error;

                if (SUCCEEDED(hr)) {
                    PrintColumn(&Column, pszColumnName);
                    pDSSearch->FreeColumn(&Column);
                }

                FreeADsMem(pszColumnName);
                hr = pDSSearch->GetNextColumnName(
                         hSearchHandle,
                         &pszColumnName
                         );
                BAIL_ON_FAILURE(hr);
            }
            printf("\n");
        }
        else {
            for (DWORD i=0; i<dwNumberAttributes; i++) {
                hr = pDSSearch->GetColumn(
                         hSearchHandle,
                         pszAttrNames[i],
                         &Column
                         );

                if (hr == E_ADS_COLUMN_NOT_SET)
                    continue;

                BAIL_ON_FAILURE(hr);

                PrintColumn(&Column, pszAttrNames[i]);

                pDSSearch->FreeColumn(&Column);
            }
        printf("\n");
        }

    // *** MODIFIED FOR Q325189 *** 
        //hr = pDSSearch->GetNextRow(
        //         hSearchHandle
        //         );
        //BAIL_ON_FAILURE(hr);
        hr = Q325189_GetNextRow(pDSSearch, hSearchHandle); 
    // *** END OF MODIFICATION ***
    }

    wprintf (L"Total Rows: %d\n", nRows);
...
}
                

REFERENCES

For more information, visit the following MSDN Web sites:

  • IDirectorySearch::GetNextRow

http://msdn.microsoft.com/library/en-us/netdir/adsi/idirectorysearch_getnextrow.asp

  • Platform SDK: Directory Services - ADsGetLastError

http://msdn.microsoft.com/library/en-us/netdir/adsi/adsgetlasterror.asp

  • Active Directory Services Interface Error Codes in Windows 2000

http://support.microsoft.com/default.aspx?scid=kb;EN-US;q232282

  • Creating More Efficient Microsoft Active Directory-Enabled Applications

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnactdir/html/efficientadapps.asp



Additional query words: IDirectorySearch GetNextRow S_ADS_NOMORE_ROWS ERROR_MORE_DATA ADsGetLastError truncated

Keywords: kbinfo kbwin2ksp4fix kbdswadsi2003swept KB325189