Directory Services |
The following code example shows how to call Active Directory using COM, C++, and OLE DB. This is a console application that searches the directory for all users under the coho.salmon.Fabrikam.com tree and displays the name and ADsPath properties to the console window.
ADSI defines two GUIDs for dialects:
The following uses the LDAP dialect.
You may also pass DBGUID_DEFAULT as the dialect. In this case, ADSI attempts to use the SQL dialect first; if that fails, it retries using the LDAP dialect. For more information, see LDAP dialect and SQL dialect.
For more information about OLE DB, see the OLE DB Programmer's Guide.
The following are COM Interface pointers for OLE DB.
IDBInitialize* pIDBInitialize = NULL; IDBCreateSession* pCreateSession = NULL; IDBCreateCommand* pICreateCommand = NULL; IRowset* pIRowset = NULL; ICommandText* pICommandText = NULL; IDBProperties* pIDBProperties = NULL; IColumnsInfo* pIColumnsInfo = NULL; IAccessor* pIAccessor = NULL; HACCESSOR hAccessor = NULL; // Accessor Handle. DBCOLUMNINFO* pDBColumnInfo; // Column data for returned rows. DBPROP InitProperties[4]; // Array for OLE DB connection info. DBPROPSET rgInitPropSet[1]; // Array for OLE DB connection info. int i; ULONG cCol; ULONG cbColOffset = 0; DBBINDING rgBind[MAX_COL]; // Array for column binding info. HROW hRows[5]; // Array of handles of returned rows. HROW* pRows = &hRows[0]; // Pointer to rows. LONG cNumRows; // Number of rows returned. WCHAR* pStringsBuffer; // String buffer. HRESULT hr; // COM HRESULT variable. ULONG cMaxRowSize; // Buffer size for one row of data.
In the following code example, set up a query string using the LDAP dialect.
LPCTSTR wCmdString =OLESTR( "<LDAP://coho.salmon.Fabrikam.com/cn=users,dc=coho,dc=salmon,dc=Fabrikam,dc=com>;((objectClass=user)); name, adspath;subtree"); // Initialize The Component Object Module library. CoInitialize(NULL);
Now the COM function CoCreateInstance is called. The class CLSID_ADsDSOObject refers to the OLE DB Active Directory provider. This function returns an IDBInitialize interface. This interface is the root of the OLE DB connection.
// Obtain access to the OLE DB - ODBC provider - CLSID_ADsDSOObject. hr =CoCreateInstance(CLSID_ADsDSOObject , NULL, CLSCTX_INPROC_SERVER, IID_IDBInitialize, (void **)&pIDBInitialize);
To prepare to call the IDBProperties::SetProperties method, build an array of the properties for the OLE DB connection. The following code example initializes the DBPROP INITPROPERTIES array, and fills in the user ID and password, and turns off user interface prompting (DBPROP_INIT_PROMPT).
for (i = 0; i < 3; i++ ) { VariantInit(&InitProperties[i].vValue); InitProperties[i].dwOptions = DBPROPOPTIONS_REQUIRED; InitProperties[i].colid = DB_NULLID; } // Level of prompting performed for the connection process. InitProperties[0].dwPropertyID = DBPROP_INIT_PROMPT; InitProperties[0].vValue.vt = VT_I2; InitProperties[0].vValue.iVal = DBPROMPT_NOPROMPT;
InitProperties[1].dwPropertyID = DBPROP_AUTH_USERID; InitProperties[1].vValue.vt = VT_BSTR; InitProperties[1].vValue.bstrVal = SysAllocString((LPOLESTR)L"user");
InitProperties[2].dwPropertyID = DBPROP_AUTH_PASSWORD; InitProperties[2].vValue.vt = VT_BSTR; InitProperties[2].vValue.bstrVal = SysAllocString((LPOLESTR)L"password");
Call QueryInterface to get the IDBInitialize interface for the IDBProperties interface. Call the IDBProperties::SetProperties method to pass the DBPROPSET structure, which contains a pointer to the InitProperties DBPROP structure filled in a previous step.
// Set the InitProperties array into the property set. rgInitPropSet[0].guidPropertySet = DBPROPSET_DBINIT; rgInitPropSet[0].cProperties = 4; rgInitPropSet[0].rgProperties = InitProperties; // Set initialization properties. pIDBInitialize->QueryInterface(IID_IDBProperties, (void **)&pIDBProperties); pIDBProperties->SetProperties(1,rgInitPropSet); pIDBProperties->Release();
Now, call the Initialize method to establish the connection. This step connects IDBInitialize with the directory.
pIDBInitialize->Initialize();
hr = pIDBInitialize->QueryInterface(IID_IDBCreateSession, (void **) &pCreateSession);
hr = pCreateSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown **) &pICreateCommand);
hr = pICreateCommand->CreateCommand(NULL, IID_ICommandText, (IUnknown **) &pICommandText);
Before executing the query, pass the command text for the query along with the dialect of the command text. In this case, use the LDAP dialect. You can also pass DBGUID_DBSQL, providing the query text is in that format.
hr = pICommandText->SetCommandText(DBGUID_LDAPDialect, wCmdString);
Next, tell Active Directory OLE DB provider to execute the statement. If successful, an IRowset interface is returned.
hr= pICommandText->Execute(NULL, IID_IRowset, // Interface requested. NULL, &cNumRows, // Number of rows returned. (IUnknown **) &pIRowset); // IRowset interface.
An IRowset object is now available. Send a query for the IColumnsInfo interface to obtain the column information of the result set.
hr = pIRowset->QueryInterface(IID_IColumnsInfo, (void **) &pIColumnsInfo); CheckHRESULT(hr,"");
Now, use the IColumnsInfo::GetColumnInfo method to get the column data from the command object.
hr = pIColumnsInfo->GetColumnInfo( &cCol, &pDBColumnInfo, &pStringsBuffer ); CheckHRESULT(hr,""); DWORD dwText;
Display the name of each column in the result set. Delimit the output with tab characters.
wszDispBuffer[0]='\0'; for(ULONG nCount=0 ; nCount < cCol; nCount++) { if (pDBColumnInfo[nCount].pwszName) wcscat(wszDispBuffer, pDBColumnInfo[nCount].pwszName); dwText = wcslen(wszDispBuffer); wszDispBuffer[dwText++] = L'\t'; wszDispBuffer[dwText] = L'\0'; } // NULL terminate the display buffer. if (*wszDispBuffer) wszDispBuffer[wcslen(wszDispBuffer)-1]=L'\0'; _putws(wszDispBuffer);
Next, create column bindings for the result set. This code example notifies the IAccessor, created in the following example, to return column data as Unicode strings. This simplifies this example as no type coercion is necessary.
DWORD dwOffset = 0; for (ULONG ulBind=0; ulBind < cCol; ulBind++) { // Binding structure. rgBind[ulBind].dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS; rgBind[ulBind].eParamIO = DBPARAMIO_NOTPARAM; rgBind[ulBind].iOrdinal = pDBColumnInfo[ulBind].iOrdinal; rgBind[ulBind].wType = DBTYPE_WSTR; rgBind[ulBind].pTypeInfo = NULL; rgBind[ulBind].obValue = dwOffset + offsetof(DBCOLUMNDATA,bData); rgBind[ulBind].obLength = dwOffset + offsetof(DBCOLUMNDATA,dwLength); rgBind[ulBind].obStatus = dwOffset + offsetof(DBCOLUMNDATA,wStatus); rgBind[ulBind].cbMaxLen = MAXROWLEN; rgBind[ulBind].pObject = NULL; rgBind[ulBind].pBindExt = NULL; rgBind[ulBind].dwFlags = 0; rgBind[ulBind].dwMemOwner = DBMEMOWNER_CLIENTOWNED; rgBind[ulBind].bPrecision = 0; rgBind[ulBind].bScale = 0; dwOffset += rgBind[ulBind].cbMaxLen + offsetof( DBCOLUMNDATA, bData ); dwOffset = ROUND_UP( dwOffset, COLUMN_ALIGNVAL ); //iBind++; } cMaxRowSize = dwOffset;
Use the IAccessor interface to get an accessor for the bindings from the rowset. Then call the IAccessor::CreateAccessor method, which returns an array of accessor handles. This is an array of handles to the rows in the result set.
hr = pIRowset->QueryInterface( IID_IAccessor, (void**)&pIAccessor ); CheckHRESULT(hr,""); hr = pIAccessor->CreateAccessor( DBACCESSOR_ROWDATA, ulBind, rgBind, 0, &hAccessor, NULL );
First, allocate a block of memory (pRowData) that will hold one row of the result set. Then call the IRowset::GetNextRows method to pass in the number of rows desired, and pointers to the return row data (pRowData), and the number of rows returned (cRowsObtained). When the row data is available, call PrintRowData.
BYTE* pRowData = NULL; // Memory for data. // Create a buffer for row data, large enough to hold the largest row. pRowData = (BYTE *) malloc( cMaxRowSize ); ULONG cRowsObtained; // Number of rows obtained. HROW rghRows[NUMROWS_CHUNK]; // Row handles. // Process all the rows, one by one, NUMROWS_CHUNK rows at a time. do { hr = pIRowset->GetNextRows( 0, // cbChapter 0, // cRowsToSkip NUMROWS_CHUNK, // cRowsDesired &cRowsObtained, // cRowsObtained &pRows ); // Filled in with row handles. // If all rows are not retrieved. if ( cRowsObtained ) { // Loop over rows obtained, getting data for each. for (ULONG ulRow=0; ulRow < cRowsObtained; ulRow++ ) { // Retrieve the row. hr = pIRowset->GetData( pRows[ulRow], // Row Handle. hAccessor, // Handle to Accessor. pRowData ); // Pointer to Row Data. // Print to screen. PrintRowData( rgBind,pDBColumnInfo, ulBind, pRowData ); } // Release the row handles. hr = pIRowset->ReleaseRows( cRowsObtained, rghRows, 0, 0, 0); } } while (cRowsObtained);
This function takes the column binding structure (DBBINDING), the column data structure (DBCOLUMNINFO), the number of columns bound, and a BYTE pointer to the row data. It retrieves the column offsets in the row data, retrieves the column data for that column, and displays the data to the console window in tab delimited format. The dwStatus attribute of the DBCOLUMN structure is read to assert that the OLE DB provider was able to successfully retrieve the column data. Recall that when you initially bound the columns, it was specified that all column data shall come back as Unicode strings.
void PrintRowData ( DBBINDING* rgBind, // DBBINDING Structure DBCOLUMNINFO* pDBColumnInfo, // DBCOLUMNINFO Structure ULONG ulNumColsBind, // Number of columns bound to BYTE* pData // Row data retrieved with // IRowset::GetData() ) { DWORD dwOutIndex =0; // Index for tracking the output string ULONG ulCurrentCol; // Index for the current column DBCOLUMNDATA* pColumn; // Data structure // Print each column that is bound to. for (ulCurrentCol=0, wszDispBuffer[0]='\0'; ulCurrentCol < ulNumColsBind; ulCurrentCol++) { // Get a pointer to the column info - // found right behind the status offset // for this column in the data buffer. pColumn = (DBCOLUMNDATA *) (pData + rgBind[ulCurrentCol].obStatus); if (pDBColumnInfo[ulCurrentCol].pwszName) { switch (pColumn->wStatus) { case DBSTATUS_S_ISNULL: wcscat(wszDispBuffer, L"<NULL>"); break; case DBSTATUS_S_OK: case DBSTATUS_S_TRUNCATED: { if ((LPWSTR)pColumn->bData) wcscat (wszDispBuffer, (LPWSTR)pColumn->bData); else wcscat (wszDispBuffer,L"null"); break; } case DBSTATUS_E_CANTCONVERTVALUE: wcscat(wszDispBuffer, L"<cannot convert value>"); break; default: wcscat(wszDispBuffer,L"<unknown>"); break; } dwOutIndex = wcslen(wszDispBuffer); wszDispBuffer[dwOutIndex++] = L'\t'; wszDispBuffer[dwOutIndex] = L'\0'; } } // The last tab goes away. wszDispBuffer[--dwOutIndex] = L'\0'; // Print the row to the screen. _putws(wszDispBuffer); }