Microsoft KB Archive/325189

= 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 &quot;The current row is the last row and remains to be unchanged&quot;. Instead, the description should say &quot;for now, current row is the last row&quot;, and if you want to know if this is really the last row, you have to call ADsGetLastError.&quot;

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: &quot;MAIN.CXX&quot; 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) { 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(&quot;\n&quot;); }       else { for (DWORD i=0; iGetColumn(                        hSearchHandle,                         pszAttrNames[i],                         &Column                         );

if (hr == E_ADS_COLUMN_NOT_SET) continue;

BAIL_ON_FAILURE(hr);

PrintColumn(&Column, pszAttrNames[i]);

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

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

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

