| Platform SDK: Active Directory, ADSI, and Directory Services |
The following code fragment contains a function that creates a group with only the essential properties explicitly set (cn, sAMAccountType, groupType) and containing no members:
////////////////////////////////////////////////////////////////////////////////////////////////////
/* CreateSimpleGroup() - Function for creating a basic group
Parameters
IDirectoryObject *pDirObject - Parent Directory Object for the new group
LPWSTR pwCommonName - Common Name for the new group
IADs ** ppObjRet - Pointer to the Pointer which will receive the new Group
int iGroupType - Bitflags for new group:
ADS_GROUP_TYPE_GLOBAL_GROUP,
ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP,
ADS_GROUP_TYPE_UNIVERSAL_GROUP,
ADS_GROUP_TYPE_SECURITY_ENABLED
*/
HRESULT CreateSimpleGroup(IDirectoryObject *pDirObject, LPWSTR pwCommonName,LPWSTR pwSamAcctName,IADs *ppObjRet,int iGroupType)
{
if(!pDirObject)
return E_INVALIDARG;
//Check if the group type is Universal Security
//if true, make sure domain is in native mode.
if(((iGroupType & ADS_GROUP_TYPE_UNIVERSAL_GROUP)==ADS_GROUP_TYPE_UNIVERSAL_GROUP)&&((iGroupType & ADS_GROUP_TYPE_SECURITY_ENABLED)==ADS_GROUP_TYPE_SECURITY_ENABLED))
{
//Check whether the domain containing the container is in mixed mode
hr = CheckDomainModeOfObject(pDirObject, &bIsMixed);
if (SUCCEEDED(hr))
{
if (bIsMixed)
return E_INVALIDARG;
}
else
{
return hr;
}
}
//SamAccountName CANNOT be bigger than 20 characters.
if (wcslen(pwSamAcctName) >20)
{
return E_FAIL;
}
HRESULT hr;
/*
ADSVALUE is used to specify attribute information for calling IDirectoryObject::CreateDSObject()
When Creating a new group, the required attributes are : objectClass,sAMAccountName and
groupType.
In this function the "objectClass" is set to "group".
The "sAMAccountName" is the Windows NT 4.0 name.
In Windows 2000/Windows NT 4 mixed-mode environment, this attribute
will be exposed to the Windows NT 4 computers. Therefore, this name must be
globally unique throughout the network and cannot exceed 20 characters in length.
*/
ADSVALUE sAMValue;
ADSVALUE classValue;
ADSVALUE groupType;
LPDISPATCH pDisp;
WCHAR pwCommonNameFull[1024];
// Build an Array of ADS_ATTR_INFO structures
// Note that the sAMAccountName and groupType entries contain
// a pointer to the respective ADSVALUE structures defined previously
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"groupType", ADS_ATTR_UPDATE,
ADSTYPE_CASE_IGNORE_STRING, &groupType, 1}
};
// Get the size of the array
DWORD dwAttrs = sizeof(attrInfo)/sizeof(ADS_ATTR_INFO);
/*
For ADSVALUES, the dwType member and
the data value member (in this case "CaseIgnoreString")
must be set.
*/
// To create a group, this parameter must be set to "group".
classValue.dwType = ADSTYPE_CASE_IGNORE_STRING;
classValue.CaseIgnoreString = L"group";
// Set sAMAccountName to the name passed to this function
sAMValue.dwType=ADSTYPE_CASE_IGNORE_STRING;
sAMValue.CaseIgnoreString = pwSamAcctName;
// Set the groupType to the group type passed to this function
groupType.dwType=ADSTYPE_INTEGER;
groupType.Integer = iGroupType;
//Note that CN is limited to 64 characters.
// Take the passed commonname and prefix a 'CN=' to conform
// to the format that IDirectoryObject::CreateDSObject() requires
wsprintfW(pwCommonNameFull,L"CN=%s",pwCommonName);
// Create the new group
hr = pDirObject->CreateDSObject( pwCommonNameFull, attrInfo,
dwAttrs, &pDisp );
if (SUCCEEDED(hr))
{
// Query the new group for an IADs to be returned from this function
hr = pDisp->QueryInterface(IID_IADs,(void**) ppObjRet);
pDisp->Release();
pDisp = NULL;
}
return hr;
}
HRESULT GetDomainMode(IADs *pDomain, BOOL *bIsMixed)
{
HRESULT hr = E_FAIL;
VARIANT var;
if (pDomain)
{
VariantClear(&var);
//Get the ntMixedDomain attribute
LPOLESTR szAttribute = L"ntMixedDomain";
hr = pDomain->Get(szAttribute,&var);
if (SUCCEEDED(hr))
{
//Type should be VT_I4.
if (var.vt==VT_I4)
{
//Zero means native mode.
if (var.lVal == 0)
*bIsMixed = FALSE;
//One means mixed mode.
else if (var.lVal == 1)
*bIsMixed = TRUE;
else
hr=E_FAIL;
}
}
VariantClear(&var);
}
return hr;
}
HRESULT CheckDomainModeOfObject(IDirectoryObject *pDirObject, BOOL *bIsMixed)
{
HRESULT hr = E_FAIL;
IADs *pDomain = NULL;
VARIANT VarTest;
WCHAR *pFound = NULL;
int iLen;
WCHAR *pDomainPath = new WCHAR[MAX_PATH*2];
//Check whether the domain containing the container is in mixed mode
WCHAR *pVal = NULL;
pVal = GetDirectoryObjectAttrib(pDirObject,L"canonicalName");
if (pVal)
{
//Parse the canonical name for the DNS name of the domain
pFound = wcschr(pVal,'/');
//Bind to the domain using the dns name, get defaultnamingcontext,
if (pFound)
{
iLen = pFound - pVal;
wcscpy(pDomainPath, L"LDAP://");
wcsncat(pDomainPath, pVal,iLen);
wcscat(pDomainPath, L"/rootDSE");
wprintf(L"DNS Name: %s\n", pDomainPath);
VariantClear(&VarTest);
hr = ADsOpenObject(pDomainPath,
NULL,
NULL,
ADS_SECURE_AUTHENTICATION, //Use Secure Authentication
IID_IADs,
(void**)&pDomain);
if (SUCCEEDED(hr))
{
hr = pDomain->Get(L"defaultNamingContext",&VarTest);
if (SUCCEEDED(hr))
{
wcscpy(pDomainPath, L"LDAP://");
wcsncat(pDomainPath, pVal,iLen);
wcscat(pDomainPath, L"/");
wcscat(pDomainPath, VarTest.bstrVal);
VariantClear(&VarTest);
if (pDomain)
pDomain->Release();
if (SUCCEEDED(hr))
{
hr = ADsOpenObject(pDomainPath,
NULL,
NULL,
ADS_SECURE_AUTHENTICATION, //Use Secure Authentication
IID_IADs,
(void**)&pDomain);
if (SUCCEEDED(hr))
{
hr = GetDomainMode(pDomain, bIsMixed);
}
}
}
}
if (pDomain)
pDomain->Release();
}
}
return hr;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/*
GetDirectoryObjectAttrib() - Returns the value of the attribute named in pAttrName
from the IDirectoryObject passed
Parameters
IDirectoryObject *pDirObject - Object from which to retrieve an attribute value
LPWSTR pAttrName - Name of attribute to retrieve
*/
WCHAR * GetDirectoryObjectAttrib(IDirectoryObject *pDirObject,LPWSTR pAttrName)
{
HRESULT hr;
ADS_ATTR_INFO *pAttrInfo=NULL;
DWORD dwReturn;
static WCHAR pwReturn[1024];
pwReturn[0] = 0l;
hr = pDirObject->GetObjectAttributes( &pAttrName,
1,
&pAttrInfo,
&dwReturn );
if ( SUCCEEDED(hr) )
{
for(DWORD idx=0; idx < dwReturn;idx++, pAttrInfo++ )
{
if ( _wcsicmp(pAttrInfo->pszAttrName,pAttrName) == 0 )
{
wcscpy(pwReturn,pAttrInfo->pADsValues->CaseIgnoreString);
break;
}
}
FreeADsMem( pAttrInfo );
}
return pwReturn;
}
The following subroutine creates a group with only the essential properties explicitly set (cn, sAMAccountType, groupType) and containing no members:
'////////////////////////////////////////////////////////////////////////////////////////////////////
' CreateSimpleGroup() - Function for creating a basic group
'
' Parameters
'
' oDirObject As IDirectoryObject - Parent Directory Object for the new group
' ByVal sCommonName As String - Common Name for the new group
' ByVal sSAMAcctName As String - Pointer to the Pointer which will receive the new Group
' oDirObject As IDirectoryObject - Parent Directory Object for the new group
' ByVal sCommonName As String - Common Name for the new group
' ByVal sSAMAcctName As String - Sam Account Name for the new group
' oDirObjectRet As IDirectoryObject - New object returned
' ByVal iGroupType As Long - Bitflags for new group:
' ADS_GROUP_TYPE_GLOBAL_GROUP,
' ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP,
' ADS_GROUP_TYPE_UNIVERSAL_GROUP,
' ADS_GROUP_TYPE_SECURITY_ENABLE
Sub CreateSimpleGroup(oDirObject As IDirectoryObject, ByVal sCommonName As String, ByVal sSAMAcctName As String, oDirObjectRet As IDirectoryObject, ByVal iGroupType As Long)
If Len(sSAMAcctName) > 20 Then
MsgBox "SamAccountName CANNOT be bigger than 20 characters"
Exit Sub
End If
Dim sGroupType As String
Dim oNewObject As IADs
Dim oIadsContDirObj As IADsContainer
Set oIadsContDirObj = oDirObject
' Get the string value for the group type
sGroupType = Str(iGroupType)
' Get a New group object
Set oNewObject = oIadsContDirObj.Create("group", "CN=" & sCommonName)
' Put the required attributes
oNewObject.Put "sAMAccountName", sSAMAcctName
oNewObject.Put "GroupType", iGroupType
' Commit the new group
oNewObject.SetInfo
' Print group vitals
DisplayMessage ">>> Created new GROUP with a groupeType of " & Str(iGroupType)
PrintIADSObject oNewObject
Set oDirObjectRet = oNewObject
Set oNewObject = Nothing
Set oIadsContDirObj = Nothing
End Sub