You should set up
your ADSI development environment before we start. ADSI is
very easy and fun to program. This page gives you a quick start on ADSI programming. For
more advanced topics and provider specific information, you should visit the
provider's page
and adsi help file. How do I:
- Connect/bind
to a directory object
First, you must bind to a directory object before any other operations, such as
searching, reading and modifying attributes, or enumerating are allowed. You can
connect or bind to a directory object remotely.
- Move an object
An object can be moved from a
given parent to a new destination parent.
- Read Schema
Each ADSI provider has a
schema which defines the classes, attributes, and syntax.
What's Next?
Now that you are managing basic ADSI
operations, you can continue your quest to set up a fully functioning directory. To focus
on tasks you want to accomplish for the directory you're connecting to, visit the
provider's pages (WinNT for NT 4.0, LDAP for Active Directory, SiteServer, Exchange and
other LDAP directories, and more).
Connecting/binding to a directory
object
Before you bind to a directory object, you must know the
ADsPath. For example, in the Windows NT 4.0 directory, the path can be
WinNT://domainName/object. For Windows 2000 Active Directory, you can bind to it as
LDAP://DC=MyCompany, DC=COM. For Exchange Server, you should know the Exchange
Server name and its organization, for example, LDAP://exchSrv01/O=Microsoft. For more
information about each provider, visit the provider pages.
To bind to an object, use the GetObject
function for Visual Basic and the ADsGetObject function for Visual C++.
These functions run in the context of the currently logged on user.
'---Visual Basic Sample: Binding to JSmith user in
ARCADIABAY domain
Set obj = GetObject("WinNT://ARCADIABAY/JSmith,user")
'Source code can be found in the \samples\start\Binding\vb directory.
// Visual C++ Sample: Binding to JSmith user in ARCADIABAY
domain
HRESULT hr;
IADs *pADs;
hr = ADsGetObject( L"WinNT://ARCADIABAY/JSmith,user", IID_IADs, (void**)
&pADs );
//...
pADs->Release(); // Do not forget to release when you're done.
//Source code can be found in the \samples\start\Binding\vc
directory.
Back to top
Specifying
alternate credentials
Sometimes, you would like to bind to another object that
has a different set of credentials. This can be accomplished using the IADsOpenDSObject
interface, or the ADsOpenObject function for Visual C++.
'---- Visual Basic Sample: Binding to the JSmith user in
the ArcadiaBay domain with an alternate credential
Set dso = GetObject("WinNT:")
Set usr = dso.OpenDSObject("WinNT://ARCADIABAY/JSmith,user",
"Administrator", "secret", ADS_SECURE_AUTHENTICATION)
'Source code can be found in the \samples\start\Binding\vb
directory.
//---- Visual C++ Sample: Binding to the JSmith user in the
ArcadiaBay domain with an alternate credential
IADs *pADs;
HRESULT hr;
hr = ADsOpenObject(L"WinNT://ARCADIABAY/JSmith,user",
L"Administrator", L"secret", ADS_SECURE_AUTHENTICATION, IID_IADs,
(void**) &pADs );
//...
// Make sure that you release it after use.
pADs->Release();
//Source code can be found in the \samples\start\Binding\vc
directory.
Back to top
Enumerating
children in a container
Once you bind to an object which is a container, you'll be
able to enumerate its children using a normal For...each statement in Visual Basic, or
using the ADsBuildEnumerator function for Visual C++. Note that only
immediate children are enumerated using this call. If you want to list all of the objects
of a subtree, you must use the Search function.
' Visual Basic Sample: Enumerating children for a
given container
Set cont = GetObject("WinNT://INDEPENDENCE")
For Each obj In cont
Debug.Print obj.Name & " (" & obj.Class & ")"
Next
'Source code can be found in the \samples\start\Enum\vb
directory.
// Visual C++ Sample: Enumerating children for a given
container
////////////////////////////////
// Bind to a domain object
////////////////////////////////
hr = ADsGetObject(L"WinNT://INDEPENDENCE", IID_IADsContainer,
(void**) &pCont );
if ( !SUCCEEDED(hr) )
{
return hr;
}
/////////////////////////////////
// Enumerate
/////////////////////////////////
IEnumVARIANT *pEnum = NULL;
hr = ADsBuildEnumerator( pCont, &pEnum );
if ( SUCCEEDED(hr) )
{
VARIANT var;
ULONG lFetch;
IADs *pChild=NULL;
VariantInit(&var);
while( SUCCEEDED(ADsEnumerateNext( pEnum, 1,
&var, &lFetch )) && lFetch == 1 )
{
hr =
V_DISPATCH(&var)->QueryInterface( IID_IADs, (void**) &pChild );
if ( SUCCEEDED(hr) )
{
BSTR bstrName;
BSTR bstrClass;
// Get more information on the child classes
pChild->get_Name(&bstrName);
pChild->get_Class(&bstrClass);
printf("%S\t(%S)\n", bstrName, bstrClass );
// Clean-up
SysFreeString(bstrName);
SysFreeString(bstrClass);
pChild->Release();
}
VariantClear(&var);
}
}
// Do not forget to free up the enumerator using ADsFreeEnumerator.
if ( pEnum )
{
ADsFreeEnumerator( pEnum );
}
pCont->Release();
// Source code can be found in the \samples\start\Enum\vc
directory.
Back to top
Reading an attribute value on
an object
To read an attribute, you can use the IADs::Get
method for both Visual Basic and Visual C++. If you are a Visual C++ programmer, you
can also use the IDirectoryObject::GetObjectAttributes method. Note that not
all providers support the IDirectoryObject interface.
'--- Visual Basic Sample: Getting attributes with single
and multiple values
Set usr =
GetObject("WinNT://INDEPENDENCE/Administrator,user")
' ------ Reading an attribute with a single value
Debug.Print usr.Name
Debug.Print usr.FullName
'Or you can also use generic Get
Debug.Print usr.Get("FullName")
'------ Reading attributes with multiple values
sid = usr.Get("objectSID")
For Each sidByte In sid
Debug.Print Hex(sidByte)
Next
'Or you can also use LBound and UBound for an attribute with multiple values.
For i = LBound(sid) To UBound(sid)
Debug.Print Hex(sid(i))
Next i
'Source code can be found in the \samples\start\Read\vb
directory.
'--- Visual C++ Sample: Getting attributes with single and
multiple values
HRESULT hr;
IADs *pUsr=NULL;
CoInitialize(NULL);
////////////////////////////////////
// Bind to a directory object
////////////////////////////////////
hr = ADsGetObject(L"WinNT://INDEPENDENCE/Administrator,user",
IID_IADs, (void**) &pUsr );
if ( !SUCCEEDED(hr) )
{
return hr;
}
//////////////////////////////////////////
// Get an attribute with a single value
//////////////////////////////////////////
VARIANT var;
VariantInit(&var);
hr = pUsr->Get(L"FullName", &var );
if ( SUCCEEDED(hr) )
{
printf("FullName: %S\n",
V_BSTR(&var) );
VariantClear(&var);
}
if ( pUsr )
{
pUsr->Release();
}
////////////////////////////////////////////////////////////////////
// Get an attribute with a multiple value from a service object
////////////////////////////////////////////////////////////////////
IADs *pSvc = NULL;
hr =
ADsGetObject(L"WinNT://INDEPENDENCE/ANDYHAR11/Browser,service", IID_IADs,
(void**) &pSvc );
if ( !SUCCEEDED(hr) )
{
return hr;
}
hr = pSvc->Get(L"Dependencies", &var );
if ( SUCCEEDED(hr) )
{
LONG lstart, lend;
SAFEARRAY *sa = V_ARRAY( &var );
VARIANT varItem;
// Get the lower and upper bound
hr = SafeArrayGetLBound( sa, 1, &lstart );
hr = SafeArrayGetUBound( sa, 1, &lend );
// Now iterate and print the content
VariantInit(&varItem);
printf("Getting service dependencies using IADs
:\n");
for ( long idx=lstart; idx < lend; idx++ )
{
hr =
SafeArrayGetElement( sa, &idx, &varItem );
printf("%S ",
V_BSTR(&varItem));
VariantClear(&varItem);
}
printf("\n");
VariantClear(&var);
}
// Clean-up
if ( pSvc )
{
pSvc->Release();
}
//////////////////////////////////////////////////////////////////////////
// Using IDirectoryObject to get an attribute with
multiple values
// Note: NOT all providers support this interface.
/////////////////////////////////////////////////////////////////////////
IDirectoryObject *pDirObject=NULL;
ADS_ATTR_INFO *pAttrInfo=NULL;
DWORD
dwReturn;
LPWSTR
pAttrNames[]={L"objectClass" };
DWORD
dwNumAttr=sizeof(pAttrNames)/sizeof(LPWSTR);
hr =
ADsGetObject(L"LDAP://CN=Administrator,CN=Users,DC=windows2000,DC=nttest,DC=microsoft,DC=com",
IID_IDirectoryObject,
(void**) &pDirObject );
if ( !SUCCEEDED(hr) )
{
return hr;
}
// Now get the attribute
hr = pDirObject->GetObjectAttributes( pAttrNames,
dwNumAttr,
&pAttrInfo,
&dwReturn );
if ( SUCCEEDED(hr) )
{
printf("Getting the objectClass multivalue
attribute using IDirectoryObject :\n");
for (DWORD val=0; val <
pAttrInfo->dwNumValues; val++, pAttrInfo->pADsValues++)
{
printf(" %S\n", pAttrInfo->pADsValues->CaseIgnoreString);
}
FreeADsMem(pAttrInfo); // the pAttrInfo must be
freed using FreeADsMem
}
//Clean-up
pDirObject->Release();
CoUninitialize();
'Source code can be found in the \samples\start\Read\vc
directory.
Back to top
Writing an attribute on an object
You can modify an attribute using the IADs::Put
and IADs::PutEx methods. For Visual C++, you can also use the IDirectoryObject::SetObjectAttributes
method.
'--- Visual Basic Sample: Setting attributes with
single and multiple values
'Bind to a user
Set usr = GetObject("LDAP://CN=Jane
Johnson,OU=DSys,DC=windows2000,DC=nttest,DC=microsoft,DC=com")
'Modify single attributes
usr.Put "givenName", "Jane"
usr.Put "sn", "Johnson"
'Modify multivalue attributes
usr.Put "otherTelephone", Array("425 844 1234", "425 924
4321")
'Commit to the directory
usr.SetInfo
'Source code can be found in the \samples\start\Write\vb
directory.
//--- Visual C++ Sample: Setting attributes with
single and multiple values
HRESULT hr;
IADs *pADs=NULL;
LPWSTR pszADsPath = L"LDAP://CN=Jane
Johnson,OU=DSys,DC=windows2000,DC=nttest,DC=microsoft,DC=com";
CoInitialize(NULL);
//////////////////////////////////////
// Modifying attributes using IADs
//////////////////////////////////////
hr = ADsGetObject(pszADsPath,
IID_IADs,
(void**) &pADs );
if (!SUCCEEDED(hr) )
{
return hr;
}
VARIANT var;
// We omit checking the result for brevity.
// First Name
VariantInit(&var);
V_BSTR(&var) = SysAllocString(L"Jane");
V_VT(&var) = VT_BSTR;
hr = pADs->Put( L"givenName", var );
// Last Name
VariantClear(&var);
V_BSTR(&var) = SysAllocString(L"Johnson");
V_VT(&var) = VT_BSTR;
hr = pADs->Put( L"givenName", var );
VariantClear(&var);
// Other Telephones
LPWSTR pszPhones[] = { L"425 844 1234", L"425 924
4321" };
DWORD dwNumber = sizeof( pszPhones ) /sizeof(LPWSTR);
hr = ADsBuildVarArrayStr( pszPhones, dwNumber, &var );
hr = pADs->Put( L"otherTelephone", var );
VariantClear(&var);
hr = pADs->SetInfo();
pADs->Release();
if (!SUCCEEDED(hr) )
{
return hr;
}
/////////////////////////////////////////////////////
// Alternatively, you can use IDirectoryObject
/////////////////////////////////////////////////////
IDirectoryObject *pDir=NULL;
hr = ADsGetObject(pszADsPath,
IID_IDirectoryObject,
(void**) &pDir );
if ( !SUCCEEDED(hr) )
{
return hr;
}
DWORD dwReturn;
ADSVALUE snValue;
ADSVALUE fNameValue;
ADSVALUE phoneValue[2];
ADS_ATTR_INFO attrInfo[] = {
{L"givenName",ADS_ATTR_UPDATE,
ADSTYPE_CASE_IGNORE_STRING,&snValue,1},
{L"sn", ADS_ATTR_UPDATE,
ADSTYPE_CASE_IGNORE_STRING,&fNameValue,1 },
{L"otherTelephone", ADS_ATTR_UPDATE,
ADSTYPE_CASE_IGNORE_STRING, phoneValue,2 }
};
DWORD dwAttrs = sizeof(attrInfo)/sizeof(ADS_ATTR_INFO);
///// First Name ///////////
fNameValue.dwType=ADSTYPE_CASE_IGNORE_STRING;
fNameValue.CaseIgnoreString = L"Johnson";
///// Last Name ///////////
snValue.dwType= ADSTYPE_CASE_IGNORE_STRING;
snValue.CaseIgnoreString = L"Johnson";
///// Other Telephone ///////////
phoneValue[0].dwType = ADSTYPE_CASE_IGNORE_STRING;
phoneValue[0].CaseIgnoreString = L"425 844 1234";
phoneValue[1].dwType = ADSTYPE_CASE_IGNORE_STRING;
phoneValue[1].CaseIgnoreString = L"425 924 4321";
hr = pDir->SetObjectAttributes(attrInfo, dwAttrs, &dwReturn);
pDir->Release();
if ( !SUCCEEDED(hr) )
{
return hr;
}
CoUninitialize();
return 0;
//Source code can be found in the \samples\start\Write\vc
directory.
Back to top
Creating an object
To create an object, you can use the IADsContainer::Create
method for both Visaul Basic and Visual C++. You can also use the IDirectoryObject::CreateDSObject
method.
'-- Visual Basic Sample: Creating a user object
Set cont = GetObject("WinNT://INDEPENDENCE")
'---- Creating a user
Set usr = cont.Create("user", "JohnD")
usr.FullName = "John Doe"
usr.SetInfo
'Source code can be found in the \samples\start\Create\vb
directory.
//-- Visual C++ Sample: Creating a user object
HRESULT hr;
IADsContainer *pCont = NULL;
CoInitialize(NULL);
hr = ADsGetObject( L"WinNT://INDEPENDENCE",
IID_IADsContainer, (void**) &pCont );
if (!SUCCEEDED(hr) )
{
return hr;
}
IADs *pADs=NULL;
IDispatch *pDisp=NULL;
hr = pCont->Create(L"user", L"AliceW",
&pDisp );
pCont->Release();
if( !SUCCEEDED(hr) )
{
return hr;
}
hr = pDisp->QueryInterface( IID_IADs, (void**) &pADs );
pDisp->Release();
if ( !SUCCEEDED(hr) )
{
return 0;
}
pADs->SetInfo(); // Commit
pADs->Release(); // Release
///////////////////////////////////////////////////////////
// Use IDirectoryObject to create an object.
////////////////////////////////////////////////////////////
IDirectoryObject *pDirObject=NULL;
ADSVALUE sAMValue;
ADSVALUE uPNValue;
ADSVALUE classValue;
ADS_ATTR_INFO attrInfo[] =
{
{ L"objectClass", ADS_ATTR_UPDATE,
ADSTYPE_CASE_IGNORE_STRING, &classValue, 1 },
{L"sAMAccountName", ADS_ATTR_UPDATE,
ADSTYPE_CASE_IGNORE_STRING, &sAMValue, 1},
{L"userPrincipalName",
ADS_ATTR_UPDATE, ADSTYPE_CASE_IGNORE_STRING, &uPNValue, 1},
};
DWORD dwAttrs = sizeof(attrInfo)/sizeof(ADS_ATTR_INFO);
classValue.dwType = ADSTYPE_CASE_IGNORE_STRING;
classValue.CaseIgnoreString = L"user";
sAMValue.dwType=ADSTYPE_CASE_IGNORE_STRING;
sAMValue.CaseIgnoreString = L"mikes";
uPNValue.dwType=ADSTYPE_CASE_IGNORE_STRING;
uPNValue.CaseIgnoreString = L"mikes@arcadiabay.com";
// Create this user in an organizational unit
hr =
ADsGetObject(L"LDAP://OU=DSys,DC=windows2000,DC=nttest,DC=microsoft,DC=com",
IID_IDirectoryObject, (void**) &pDirObject );
if ( SUCCEEDED(hr) )
{
hr = pDirObject->CreateDSObject(
L"CN=Mike Smith", attrInfo,
dwAttrs, &pDisp );
if ( SUCCEEDED(hr) )
{
pDisp->Release();
}
}
pDirObject->Release();
CoUninitialize();
'Source code can be found in the \samples\start\Create\vc
directory.
Back to top
Deleting an object
You can use the IADsContainer::Delete
method in Visual Basic or Visual C++. You can also use the IDirectoryObject::DeleteDSObject
method to delete an object if you are using Visual C++.
'-- Visual Basic Sample: Deleting a user object
Set cont = GetObject("WinNT://INDEPENDENCE")
'---- Deleting a user
cont.Delete "user", "JohnD"
'Source code can be found in the \samples\start\Delete\vb directory.
//-- Visual C++ Sample: Deleting a user object using the IADsContainer::Delete
and IDirectoryObject::DeleteDSObject methods
HRESULT hr;
IADsContainer *pCont=NULL;
CoInitialize(NULL);
hr = ADsGetObject(L"WinNT://INDEPENDENCE", IID_IADsContainer,
(void**) &pCont);
if ( !SUCCEEDED(hr) )
{
return hr;
}
///////////////////////////////////////////////////////
// Using IADsContainer::Delete to delete a user
///////////////////////////////////////////////////////
hr = pCont->Delete(L"user", L"AliceW");
pCont->Release();
/////////////////////////////////////////////////////////////////////
// Using IDirectoryObject::DeleteDSObject to delete a
user
/////////////////////////////////////////////////////////////////////
IDirectoryObject *pDirObject=NULL;
hr =
ADsGetObject(L"LDAP://OU=DSys,DC=windows2000,DC=nttest,DC=microsoft,DC=com",
IID_IDirectoryObject, (void**) &pDirObject );
if ( SUCCEEDED(hr) )
{
hr =
pDirObject->DeleteDSObject(L"CN=Mike Smith");
pDirObject->Release();
}
CoUninitialize();
'Source code can be found in the \samples\start\Delete\vc
directory.
Back to top
Renaming an object
You can rename an object using the IADsContainer::MoveHere
method. First, you must know the object's parent.
'-- Visual Basic Sample: Renaming a user object using the IADsContainer::MoveHere
method
Set cont = GetObject("WinNT://INDEPENDENCE")
'---- Renaming a user and updating its attributes
Set usr = cont.MoveHere("WinNT://INDEPENDENCE/JSmith",
"JJohnson")
usr.FullName = "Jane Johnson"
usr.SetInfo
'Source code can be found in the \samples\start\Rename\vb directory.
//-- Visual C++ Sample: Renaming a user object using the IADsContainer::MoveHere
method
HRESULT hr;
IADsContainer *pCont=NULL;
IDispatch *pDisp=NULL;
CoInitialize(NULL);
hr = ADsGetObject(L"WinNT://INDEPENDENCE", IID_IADsContainer,
(void**) &pCont);
if ( !SUCCEEDED(hr) )
{
return hr;
}
hr =
pCont->MoveHere(L"WinNT://INDEPENDENCE/JSmith",L"JJohnson",
&pDisp );
if ( SUCCEEDED(hr) )
{
pDisp->Release();
}
CoUninitialize();
'Source code can be found in the \samples\start\Rename\vc
directory.
Back to top
Moving an object
To move an object, you must bind to the
parent's destination first, then call the IADsContainer::MoveHere method.
Note that you can move and rename and object at the same time.
'-- Visual Basic Sample: Moving user object using the IADsContainer::MoveHere
method
'Bind to the parent object's destination.
Set cont =
GetObject("LDAP://OU=DSys,DC=windows2000,DC=nttest,DC=microsoft,DC=com")
'---- Moving a user from one organization to another.
Set usr = cont.MoveHere("LDAP://CN=Mike
Smith,OU=MCS,DC=windows2000,DC=nttest,DC=microsoft,DC=com", vbNullString)
'Source code can be found in the \samples\start\Move\vb
directory.
//-- Visual C++ Sample: Moving a user object using the IADsContainer::MoveHere
method
///////////////////////////////////////////////
// First, bind to the destination container.
///////////////////////////////////////////////
HRESULT hr;
IADsContainer *pCont=NULL;
CoInitialize(NULL);
hr =
ADsGetObject(L"LDAP://OU=MCS,DC=windows2000,DC=nttest,DC=microsoft,DC=com",
IID_IADsContainer,
(void**) &pCont );
if ( !SUCCEEDED(hr) )
{
return hr;
}
///////////////////////////////////////////////////////
// Now, move the object to the bound container.
///////////////////////////////////////////////////////
IDispatch *pDisp=NULL;
hr = pCont->MoveHere(L"LDAP://CN=Mike
Smith,OU=DSys,DC=windows2000,DC=nttest,DC=microsoft,DC=com", NULL, &pDisp );
pCont->Release();
if (SUCCEEDED(hr) )
{
// You may do another operation here, such as
updating attributes.
pDisp->Release();
}
CoUninitialize();
'Source code can be found in the \samples\start\Move\vc
directory.
Back to top
Getting a child object
In ADSI, a container object exposes an IADsContainer
interface. The IADsContainer interface has the IADsContainer::GetObject
method so that you can bind directly to a child object. Note that the object returned by
the IADsContainer::GetObject method has the same security context as the object on
which the method was called. This means that if you have pointer to a container object and
you know the relative path to a child object, you can avoid having to pass credentials
again when binding to the child object. This may be useful when your application is
binding using alternate credentials (that is, when it is not in the context of the logged
on user).
'-- Visual Basic Sample: Getting a child object
'Bind to the parent container.
Set cont = GetObject("WinNT://INDEPENDENCE")
'---- Getting a child object from the container
Set usr = cont.GetObject("user", "JJohnson")
'Source code can be found in the \samples\start\Child\vb directory.
//Visual C++ Sample: Getting a child object
#define RETURN_ON_FAILURE(hr) if(!SUCCEEDED(hr)) return hr;
HRESULT hr;
CoInitialize(NULL);
IADsContainer *pCont=NULL;
hr =
ADsGetObject(L"LDAP://DC=windows2000,DC=nttest,DC=microsoft,DC=com",
IID_IADsContainer,
(void**) &pCont );
RETURN_ON_FAILURE(hr);
////////////////////////////////////////////////////////////////////////////
// Get the child from the container.
// Note in the LDAP provider you can go down more than one level.
////////////////////////////////////////////////////////////////////////////
IDispatch *pDisp = NULL;
IADs *pADs = NULL;
hr = pCont->GetObject(L"user", L"CN=Mike Smith,
OU=DSys", &pDisp );
pCont->Release();
RETURN_ON_FAILURE(hr);
hr = pDisp->QueryInterface( IID_IADs, (void**) &pADs );
pDisp->Release();
RETURN_ON_FAILURE(hr);
// Do something with pADs here.
pADs->Release();
CoUninitialize();
// Source code can be found in the \samples\start\Child\vc
directory.
Back to top
Getting the parent of an object
In ADSI, a directory object is represented by an ADSI COM
object, and every ADSI COM object exposes an IADs interface. The IADs
interface has the IADs::get_Parent method so that you can get the ADsPath for the
parent of the directory object. You can use that ADsPath to bind to the parent object.
'-- Visual Basic Sample: Getting a parent object
'Bind to an object.
Set usr = GetObject("WinNT://INDEPENDENCE/JJohnson,user")
'---- Getting a parent object using the IADs::get_Parent method.
Set cont = GetObject(usr.Parent)
' Source code can be found in the \samples\start\Parent\vb
directory.
//-- Visual C++ Sample: Getting a parent
object
HRESULT hr;
CoInitialize(NULL);
/////////////////////////////////////////
//Bind to an object
/////////////////////////////////////////
IADs *pADs = NULL;
hr = ADsGetObject(L"WinNT://INDEPENDENCE/JJohnson", IID_IADs,
(void**) &pADs );
if (!SUCCEEDED(hr) )
{
return hr;
}
BSTR bstrParent;
IADs *pParent=NULL;
/////////////////////////////////
// Get the ADs Parent's Path
/////////////////////////////////
pADs->get_Parent(&bstrParent);
pADs->Release();
////////////////////////////////
// Bind to the Parent
////////////////////////////////
hr = ADsGetObject( bstrParent, IID_IADs, (void**) &pParent );
SysFreeString(bstrParent);
if (SUCCEEDED(hr) )
{
// Do something with pParent.
pParent->Release();
}
CoUninitialize();
// Source code can be found in the \samples\start\Parent\vc
directory.
Back to top
Searching for a set of objects
You must use ADO to perform searches when using Visual
Basic. If you use Visual C++, you can also use the IDirectorySearch
interface.
'-- Visual Basic Sample: Searching for all groups
Dim con As New Connection, rs As New Recordset
Dim Com As New Command
'Open a Connection object
con.Provider = "ADsDSOObject"
con.Open "Active Directory Provider"
' Create a command object on this connection
Set Com.ActiveConnection = con
Com.CommandText = "select name from
'LDAP://DC=windows2000,DC=nttest,DC=microsoft,DC=com' where objectCategory='group' ORDER
BY NAME"
Set rs = Com.Execute
'--------------------------------------
' Navigate the record set
'----------------------------------------
While Not rs.EOF
Debug.Print rs.Fields("Name")
rs.MoveNext
Wend
' Source code can be found in the \samples\start\Search\vb
directory.
//-- Visual C++ Sample: Searching for all groups
HRESULT hr;
IDirectorySearch *pSearch;
CoInitialize(NULL);
///////////////////////////////////////////////////////
// Bind to the object, it serves as a base search
//////////////////////////////////////////////////////
hr =
ADsGetObject(L"LDAP://DC=windows2000,DC=nttest,DC=microsoft,DC=com",
IID_IDirectorySearch,
(void**) &pSearch );
if ( !SUCCEEDED(hr) )
{
return hr;
}
///////////////////////////////////////
// Perform a subtree search
/////////////////////////////////////////
ADS_SEARCHPREF_INFO prefInfo[1];
prefInfo[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
prefInfo[0].vValue.dwType = ADSTYPE_INTEGER;
prefInfo[0].vValue.Integer = ADS_SCOPE_SUBTREE;
hr = pSearch->SetSearchPreference( prefInfo, 1);
////////////////////////////////////
// Prepare for attribute return
////////////////////////////////////
LPWSTR pszAttr[] = { L"Name"};
ADS_SEARCH_HANDLE hSearch;
DWORD dwCount= sizeof(pszAttr)/sizeof(LPWSTR);
//////////////////////////////////////////
// Search for all groups in a domain
//////////////////////////////////////////
hr = pSearch->ExecuteSearch(L"(objectCategory=Group)",
pszAttr, dwCount, &hSearch );
if ( !SUCCEEDED(hr) )
{
pSearch->Release();
return hr;
}
//////////////////////////////////////////
// Now enumerate the result
//////////////////////////////////////////
ADS_SEARCH_COLUMN col;
while( pSearch->GetNextRow(hSearch) != S_ADS_NOMORE_ROWS )
{
// Get 'Name' attribute
hr = pSearch->GetColumn( hSearch,
pszAttr[0], &col );
if ( SUCCEEDED(hr) )
{
printf("%S\n", col.pADsValues->CaseIgnoreString);
pSearch->FreeColumn(
&col ); // You need to FreeColumn after use.
}
}
////////////////////
// Clean-up
////////////////////
pSearch->CloseSearchHandle(hSearch);
pSearch->Release();
CoUninitialize();
// Source code can be found in the \samples\start\Search\vc
directory.
Back to top
Filtering a
set of objects
'-- Visual Basic Sample: Getting groups and users
'Bind to a domain object
Set dom = GetObject("WinNT://INDEPENDENCE")
'Show all users and groups
dom.Filter = Array("user", "group")
For Each obj In dom
Debug.Print obj.Name & " (" & obj.Class &
")"
Next
' Source code can be found in the \samples\start\Filter\vb
directory.
// -- Visual C++ Sample: Getting groups and users
HRESULT hr;
IADsContainer *pCont=NULL;
CoInitialize(NULL);
///////////////////////////////////
// Bind to the object
//////////////////////////////////
hr = ADsGetObject(L"WinNT://INDEPENDENCE", IID_IADsContainer,
(void**) &pCont );
if ( !SUCCEEDED(hr) )
{
return hr;
}
///////////////////////////////////
// Build variant filter
//////////////////////////////////
LPWSTR pszFilter[] = { L"user", L"group" };
DWORD dwNumber = sizeof( pszFilter ) /sizeof(LPWSTR);
VARIANT var;
hr = ADsBuildVarArrayStr( pszFilter, dwNumber, &var );
if ( !SUCCEEDED(hr) )
{
pCont->Release();
return hr;
}
///////////////////////////////////
// Set the filter
//////////////////////////////////
hr = pCont->put_Filter(var);
VariantClear(&var);
if (!SUCCEEDED(hr) )
{
pCont->Release();
return hr;
}
////////////////////////////////////////////
// Enumerate the result
////////////////////////////////////////////
IEnumVARIANT *pEnum = NULL;
hr = ADsBuildEnumerator( pCont, &pEnum );
pCont->Release(); // This is no longer needed, since we have the
enumerator already.
if ( SUCCEEDED(hr) )
{
VARIANT var;
ULONG lFetch;
IADs *pChild=NULL;
VariantInit(&var);
while( SUCCEEDED(ADsEnumerateNext( pEnum, 1,
&var, &lFetch )) && lFetch == 1 )
{
hr =
V_DISPATCH(&var)->QueryInterface( IID_IADs, (void**) &pChild );
if ( SUCCEEDED(hr) )
{
BSTR bstrName;
BSTR bstrClass;
// Get more information on the child classes
pChild->get_Name(&bstrName);
pChild->get_Class(&bstrClass);
printf("%S\t\t(%S)\n", bstrName, bstrClass );
// Clean-up
SysFreeString(bstrName);
SysFreeString(bstrClass);
pChild->Release();
}
VariantClear(&var);
}
}
CoUninitialize();
// Source code can be found in the \samples\start\Filter\vc
directory.
Back to top
Reading the Schema
Most providers support the schema. The schema contains
class and attribute definitions. ADSI abstracts the schema in the well known location,
Provider://schema. Some providers may prefer to use the real schema for schema operations.
Each object carries the schema location in which its class is defined. This
information can be obtained using the IADs::get_Class method.
'-- Visual Basic Sample: Listing a schema information
'Bind to a schema container
Set Scm = GetObject("WinNT://INDEPENDENCE/Schema")
'Show all items in the schema container
For Each obj In Scm
Debug.Print obj.Name & " (" & obj.Class &
")"
Next
'You can also bind to an object and get the schema location
Set dom = GetObject("WinNT://INDEPENDENCE")
Debug.Print dom.Schema
Set Class = GetObject(dom.Schema)
'Mandatory attributes
For Each prop In Class.MandatoryProperties
Debug.Print prop
Next
'Optional attributes
For Each prop In Class.OptionalProperties
Debug.Print prop
Next
'Source code can be found in the \samples\start\Schema\vb
directory.
//-- Visual C++ Sample: Listing schema information
IADsContainer *pSchema=NULL;
HRESULT hr;
CoInitialize(NULL);
hr = ADsGetObject(L"WinNT://INDEPENDENCE/Schema",
IID_IADsContainer, (void**) &pSchema );
if ( !SUCCEEDED(hr) )
{
return hr;
}
////////////// Enumerate Schema objects
///////////////////////////////////
IEnumVARIANT *pEnum = NULL;
hr = ADsBuildEnumerator( pSchema, &pEnum );
pSchema->Release(); // This is no longer needed, since we have the
enumerator already.
if ( SUCCEEDED(hr) )
{
VARIANT var;
ULONG lFetch;
IADs *pChild=NULL;
VariantInit(&var);
while( SUCCEEDED(ADsEnumerateNext( pEnum, 1,
&var, &lFetch )) && lFetch == 1 )
{
hr =
V_DISPATCH(&var)->QueryInterface( IID_IADs, (void**) &pChild );
if ( SUCCEEDED(hr) )
{
BSTR bstrName;
BSTR bstrClass;
// Get more information on the child classes
pChild->get_Name(&bstrName);
pChild->get_Class(&bstrClass);
printf("%S\t\t(%S)\n", bstrName, bstrClass );
// Clean-up
SysFreeString(bstrName);
SysFreeString(bstrClass);
pChild->Release();
}
VariantClear(&var);
}
}
CoUninitialize();
// Source code can be found in the \samples\start\Schema\vc
directory.
Back to top
|