Example Code for Extending the Schema Programmatically
/*
** schema.cxx: Example of installing an Active Directory Schema extension.
**
** This example creates 6 new attributes, 1 new class, and modifies
** two existing classes by adding new MAY HAVE attributes. These are used
** in the DS Enabled Applications sample programs for the 1998 PDC.
**
** Attributes: courseBattleCruiser integer
** speedBattleCruiser integer
** maxPayloadBattleCruiser integer
** allowedBattleCruiser integer
** consistencyGUID octet string
** consistencyChildCount integer
**
** Class: policyParametersBattleCruiser
**
** Classes Modified: container, groupPolicyContainer
**
** The modified classes have the two consistency attributes added.
**
** Notes: Must be run on a DC which is the current Schema Master and has
** schema updates enabled using the registry key and value:
**
** KEY: HKLM\CurrentControlSet\Services\NTDS\Parameters
** Value: Schema Update Allowed, REG_DWORD, 1
**
** Libraries: activeds.lib, adsiid.lib
**
** Copyright (c) 1997, 1998 Microsoft Corporation
** All Rights Reserved
**
*/
#include "stdafx.h"
// forward declaration of error report
void ReportError(TCHAR *defaultMsg, DWORD dwErr);
// GUIDs for schema extensions. Provide your own GUID for each extension so
// they are the same in every installation. If the DS assigns them your GUIDs
// will be different in every installation, which will hurt you down the road
// when we move to an identity-based schema.
// GUIDS for Battle Cruiser Policy attributes
// {C45F05B2-4D16-11d2-800E-0080C76670C0}
static const GUID attrGuid1 =
{ 0xc45f05b2, 0x4d16, 0x11d2, { 0x80, 0xe, 0x0, 0x80, 0xc7, 0x66, 0x70, 0xc0 } };
// {C45F05B3-4D16-11d2-800E-0080C76670C0}
static const GUID attrGuid2 =
{ 0xc45f05b3, 0x4d16, 0x11d2, { 0x80, 0xe, 0x0, 0x80, 0xc7, 0x66, 0x70, 0xc0 } };
// {C45F05B4-4D16-11d2-800E-0080C76670C0}
static const GUID attrGuid3 =
{ 0xc45f05b4, 0x4d16, 0x11d2, { 0x80, 0xe, 0x0, 0x80, 0xc7, 0x66, 0x70, 0xc0 } };
// {C45F05B6-4D16-11d2-800E-0080C76670C0}
static const GUID attrGuid4 =
{ 0xc45f05b6, 0x4d16, 0x11d2, { 0x80, 0xe, 0x0, 0x80, 0xc7, 0x66, 0x70, 0xc0 } };
// GUIDs for Consistency checking attributes
// {7707464B-4D9D-11d2-AF2D-00C04FB9624E}
static const GUID attrGuid5 =
{ 0x7707464b, 0x4d9d, 0x11d2, { 0xaf, 0x2d, 0x0, 0xc0, 0x4f, 0xb9, 0x62, 0x4e } };
// {7707464C-4D9D-11d2-AF2D-00C04FB9624E}
static const GUID attrGuid6 =
{ 0x7707464c, 0x4d9d, 0x11d2, { 0xaf, 0x2d, 0x0, 0xc0, 0x4f, 0xb9, 0x62, 0x4e } };
// {C45F05B5-4D16-11d2-800E-0080C76670C0}
static const GUID classGuid1 =
{ 0xc45f05b5, 0x4d16, 0x11d2, { 0x80, 0xe, 0x0, 0x80, 0xc7, 0x66, 0x70, 0xc0 } };
void __cdecl
main()
{
HRESULT hr;
IADs *pRoot;
VARIANT varDSRoot, varSchemaUpdate;
TCHAR dsPath[MAX_PATH];
TCHAR gpcPath[MAX_PATH];
TCHAR contPath[MAX_PATH];
// Returned by CreateDSObject
IDispatch *pDisp;
// Pointers to schema objects
IDirectoryObject *pSchema,
*pGpc;
// Data structures for creating schema objects
//
// attribute values: these are unions and cannot be statically initialized.
//
ADSVALUE cn,
singleValued,
oid,
syntax,
omSyntax,
ldapname,
idGuid,
objectClass,
objectClassCategory,
subClassOf,
defaultSecurityDesc,
defaultHidingValue,
mayContain[6];
// ATTR_INFO for creating an attributeSchema object
// Each ADS_ATTR_INFO describes one attribute of an object to be
// stored in the DS.
ADS_ATTR_INFO attrArray[] = {
{L"cn",ADS_ATTR_UPDATE,ADSTYPE_CASE_IGNORE_STRING,&cn,1},
{L"isSingleValued",ADS_ATTR_UPDATE,ADSTYPE_BOOLEAN,&singleValued,1},
{L"objectClass",ADS_ATTR_UPDATE,ADSTYPE_CASE_IGNORE_STRING,&objectClass,1},
{L"attributeID",ADS_ATTR_UPDATE,ADSTYPE_CASE_IGNORE_STRING,&oid,1},
{L"attributeSyntax",ADS_ATTR_UPDATE,ADSTYPE_INTEGER,&syntax,1},
{L"oMSyntax",ADS_ATTR_UPDATE,ADSTYPE_INTEGER,&omSyntax,1},
{L"lDAPDisplayName",ADS_ATTR_UPDATE,ADSTYPE_CASE_IGNORE_STRING,&ldapname,1},
{L"schemaIdGUID",ADS_ATTR_UPDATE,ADSTYPE_OCTET_STRING,&idGuid,1},
};
// ATTR_INFO for creating a classSchema object
ADS_ATTR_INFO classArray[] = {
{L"cn",ADS_ATTR_UPDATE,ADSTYPE_CASE_IGNORE_STRING,&cn,1},
{L"objectClass",ADS_ATTR_UPDATE,ADSTYPE_CASE_IGNORE_STRING,&objectClass,1},
{L"governsID",ADS_ATTR_UPDATE,ADSTYPE_CASE_IGNORE_STRING,&oid,1},
{L"objectClassCategory",ADS_ATTR_UPDATE,ADSTYPE_INTEGER,&objectClassCategory,1},
{L"schemaIdGUID",ADS_ATTR_UPDATE,ADSTYPE_OCTET_STRING,&idGuid,1},
{L"defaultSecurityDescriptor",ADS_ATTR_UPDATE,ADSTYPE_CASE_IGNORE_STRING,&defaultSecurityDesc,1},
{L"defaultHidingValue",ADS_ATTR_UPDATE,ADSTYPE_BOOLEAN,&defaultHidingValue,1},
{L"subClassOf",ADS_ATTR_UPDATE,ADSTYPE_CASE_IGNORE_STRING,&subClassOf,1},
{L"mayContain",ADS_ATTR_UPDATE,ADSTYPE_CASE_IGNORE_STRING,&mayContain[0],6},
};
// ATTR_INFO for adding attributes to Group Policy Container
ADS_ATTR_INFO gpcUpdate[] = {
{L"mayContain",ADS_ATTR_APPEND,ADSTYPE_CASE_IGNORE_STRING,&mayContain[0],2},
};
DWORD dwAttrs;
ULONG iAttrsMod;
hr = CoInitializeEx(NULL,COINIT_MULTITHREADED| COINIT_DISABLE_OLE1DDE);
// Get the name of the schema container for this domain.
// Read the Root DSE from the default DS, which will be the DS for
// the local domain. This will get us the name of the schema container,
// which is stored in the "schemaNamingContext" operational attribute.
hr = ADsGetObject(TEXT("LDAP://RootDSE"),
IID_IADs,
(void**)&pRoot);
// Get IDirectoryObject on the root DSE as well, we will use this for
// forcing a schema update later on...
hr = pRoot->Get(L"schemaNamingContext",&varDSRoot);
printf("\nDS Root:%S\n",varDSRoot.bstrVal);
// ADsPath of the schema container
_tcscpy(dsPath,TEXT("LDAP://"));
_tcscat(dsPath,varDSRoot.bstrVal);
// ADsPath of the Group Policy Container class-schema object
_tcscpy(gpcPath,TEXT("LDAP://"));
_tcscat(gpcPath,TEXT("CN=Group-Policy-Container,"));
_tcscat(gpcPath,varDSRoot.bstrVal);
// ADsPath of the Container class-schema object
_tcscpy(contPath,TEXT("LDAP://"));
_tcscat(contPath,TEXT("CN=Container,"));
_tcscat(contPath,varDSRoot.bstrVal);
// Done with variant
hr = VariantClear(&varDSRoot);
// Bind to the schema container and get the IDirectoryObject interface
// on it.
hr = ADsGetObject(dsPath,
IID_IDirectoryObject,
(void**)&pSchema);
//**********************************************************************
// Consistency-Child-Count
//**********************************************************************
// Create a new attribute object. Set the values into the attribute
// unions, then call the method to create the object.
//
dwAttrs = sizeof(attrArray)/sizeof(ADS_ATTR_INFO); // Figure out attribute count
cn.dwType = ADSTYPE_CASE_IGNORE_STRING;
cn.CaseIgnoreString=TEXT("Consistency-Child-Count");
singleValued.dwType = ADSTYPE_BOOLEAN;
singleValued.Boolean = VARIANT_TRUE;
oid.dwType = ADSTYPE_CASE_IGNORE_STRING;
oid.CaseIgnoreString=TEXT("1.2.840.113556.1.4.7000.161"); // reserved test OID
objectClass.dwType = ADSTYPE_CASE_IGNORE_STRING;
objectClass.CaseIgnoreString=TEXT("attributeSchema");
syntax.dwType = ADSTYPE_CASE_IGNORE_STRING;
syntax.CaseIgnoreString = TEXT("2.5.5.9"); // 2.5.5.9 = Integer
omSyntax.dwType = ADSTYPE_INTEGER;
omSyntax.Integer = 2;
//
// The ldap display name will be defaulted by the server and should
// not be provided unless different than the name computed from the CN
// attribute - the LDAP name is the CN with the hyphens removed and
// case delimiting substituted. The initial character is always lowercased.
// For this example we are providing an explicit LDAP display name to illustrate
// how it is done.
ldapname.dwType = ADSTYPE_CASE_IGNORE_STRING;
ldapname.CaseIgnoreString = TEXT("consistencyChildCount");
//
// Schema-ID-Guid is provided by the server is the client does not
// provide it. This is a good example of how to write an Octet String
// to the DS.
idGuid.dwType = ADSTYPE_OCTET_STRING;
idGuid.OctetString.dwLength = sizeof(attrGuid6);
idGuid.OctetString.lpValue = (LPBYTE)&attrGuid6;
hr = pSchema->CreateDSObject(L"cn=Consistency-Child-Count",
attrArray,
dwAttrs,
&pDisp);
if (hr != NO_ERROR) {
ReportError(TEXT("Create Attribute Consistency-Child-Count failed."),hr);
// return;
} else {
printf("\nConsistency-Child-Count Attribute defined.\n");
// We are not going to use the IDispatch interface returned by the
// CreateDSObject call, so release it now.
pDisp->Release();
}
//**********************************************************************
// Consistency-GUID
//**********************************************************************
// Create a new attribute object. Set the values into the attribute
// unions, then call the method to create the object.
//
cn.dwType = ADSTYPE_CASE_IGNORE_STRING;
cn.CaseIgnoreString=TEXT("Consistency-GUID");
singleValued.dwType = ADSTYPE_BOOLEAN;
singleValued.Boolean = VARIANT_TRUE;
oid.dwType = ADSTYPE_CASE_IGNORE_STRING;
oid.CaseIgnoreString=TEXT("1.2.840.113556.1.4.7000.160"); // reserved test OID
objectClass.dwType = ADSTYPE_CASE_IGNORE_STRING;
objectClass.CaseIgnoreString=TEXT("attributeSchema");
syntax.dwType = ADSTYPE_CASE_IGNORE_STRING;
syntax.CaseIgnoreString = TEXT("2.5.5.10"); // 2.5.5.10 = Octet String
omSyntax.dwType = ADSTYPE_INTEGER;
omSyntax.Integer = 4;
//
// The ldap display name will be defaulted by the server and should
// not be provided unless different than the name computed from the CN
// attribute - the LDAP name is the CN with the hyphens removed and
// case delimiting substituted. The initial character is always lowercased.
// For this example we are providing an explicit LDAP display name to illustrate
// how it is done.
ldapname.dwType = ADSTYPE_CASE_IGNORE_STRING;
ldapname.CaseIgnoreString = TEXT("consistencyGUID");
//
// Schema-ID-Guid is provided by the server is the client does not
// provide it. This is a good example of how to write an Octet String
// to the DS.
idGuid.dwType = ADSTYPE_OCTET_STRING;
idGuid.OctetString.dwLength = sizeof(attrGuid5);
idGuid.OctetString.lpValue = (LPBYTE)&attrGuid5;
hr = pSchema->CreateDSObject(L"cn=Consistency-GUID",
attrArray,
dwAttrs,
&pDisp);
if (hr != NO_ERROR) {
ReportError(TEXT("Create Attribute Consistency-GUID failed."),hr);
// return;
} else {
printf("\nConsistency-GUID Attribute defined.\n");
// We are not going to use the IDispatch interface returned by the
// CreateDSObject call, so release it now.
pDisp->Release();
}
//**********************************************************************
// Course-Battle-Cruiser
//**********************************************************************
// Create a new attribute object. Set the values into the attribute
// unions, then call the method to create the object.
//
dwAttrs = sizeof(attrArray)/sizeof(ADS_ATTR_INFO); // Figure out attribute count
cn.dwType = ADSTYPE_CASE_IGNORE_STRING;
cn.CaseIgnoreString=TEXT("Course-Battle-Cruiser");
singleValued.dwType = ADSTYPE_BOOLEAN;
singleValued.Boolean = VARIANT_TRUE;
oid.dwType = ADSTYPE_CASE_IGNORE_STRING;
oid.CaseIgnoreString=TEXT("1.2.840.113556.1.4.7000.155"); // reserved test OID
objectClass.dwType = ADSTYPE_CASE_IGNORE_STRING;
objectClass.CaseIgnoreString=TEXT("attributeSchema");
syntax.dwType = ADSTYPE_CASE_IGNORE_STRING;
syntax.CaseIgnoreString = TEXT("2.5.5.9"); // 2.5.5.9 = Integer
omSyntax.dwType = ADSTYPE_INTEGER;
omSyntax.Integer = 2;
//
// The ldap display name will be defaulted by the server and should
// not be provided unless different than the name computed from the CN
// attribute - the LDAP name is the CN with the hyphens removed and
// case delimiting substituted. The initial character is always lowercased.
// For this example we are providing an explicit LDAP display name to illustrate
// how it is done.
ldapname.dwType = ADSTYPE_CASE_IGNORE_STRING;
ldapname.CaseIgnoreString = TEXT("courseBattleCruiser");
//
// Schema-ID-Guid is provided by the server is the client does not
// provide it. This is a good example of how to write an Octet String
// to the DS.
idGuid.dwType = ADSTYPE_OCTET_STRING;
idGuid.OctetString.dwLength = sizeof(attrGuid1);
idGuid.OctetString.lpValue = (LPBYTE)&attrGuid1;
hr = pSchema->CreateDSObject(L"cn=Course-Battle-Cruiser",
attrArray,
dwAttrs,
&pDisp);
if (hr != NO_ERROR) {
ReportError(TEXT("Create Attribute Course-Battle-Cruiser failed."),hr);
//return;
} else {
printf("\nCourse-Battle-Cruiser Attribute defined.\n");
// We are not going to use the IDispatch interface returned by the
// CreateDSObject call, so release it now.
pDisp->Release();
}
//**********************************************************************
// Speed-Battle-Cruiser
//**********************************************************************
// Create a new attribute object. Set the values into the attribute
// unions, then call the method to create the object.
//
dwAttrs = sizeof(attrArray)/sizeof(ADS_ATTR_INFO); // Figure out attribute count
cn.dwType = ADSTYPE_CASE_IGNORE_STRING;
cn.CaseIgnoreString=TEXT("Speed-Battle-Cruiser");
singleValued.dwType = ADSTYPE_BOOLEAN;
singleValued.Boolean = VARIANT_TRUE;
oid.dwType = ADSTYPE_CASE_IGNORE_STRING;
oid.CaseIgnoreString=TEXT("1.2.840.113556.1.4.7000.156"); // reserved test OID
objectClass.dwType = ADSTYPE_CASE_IGNORE_STRING;
objectClass.CaseIgnoreString=TEXT("attributeSchema");
syntax.dwType = ADSTYPE_CASE_IGNORE_STRING;
syntax.CaseIgnoreString = TEXT("2.5.5.9"); // 2.5.5.9 = Integer
omSyntax.dwType = ADSTYPE_INTEGER;
omSyntax.Integer = 2;
//
// The ldap display name will be defaulted by the server and should
// not be provided unless different than the name computed from the CN
// attribute - the LDAP name is the CN with the hyphens removed and
// case delimiting substituted. The initial character is always lowercased.
// For this example we are providing an explicit LDAP display name to illustrate
// how it is done.
ldapname.dwType = ADSTYPE_CASE_IGNORE_STRING;
ldapname.CaseIgnoreString = TEXT("speedBattleCruiser");
//
// Schema-ID-Guid is provided by the server is the client does not
// provide it. This is a good example of how to write an Octet String
// to the DS.
idGuid.dwType = ADSTYPE_OCTET_STRING;
idGuid.OctetString.dwLength = sizeof(attrGuid2);
idGuid.OctetString.lpValue = (LPBYTE)&attrGuid2;
hr = pSchema->CreateDSObject(L"cn=Speed-Battle-Cruiser",
attrArray,
dwAttrs,
&pDisp);
if (hr != NO_ERROR) {
ReportError(TEXT("Create Attribute Speed-Battle-Cruiser failed."),hr);
//return;
} else {
printf("\nSpeed-Battle-Cruiser Attribute defined.\n");
// We are not going to use the IDispatch interface returned by the
// CreateDSObject call, so release it now.
pDisp->Release();
}
//**********************************************************************
// Max-Payload-Battle-Cruiser
//**********************************************************************
// Create a new attribute object. Set the values into the attribute
// unions, then call the method to create the object.
//
dwAttrs = sizeof(attrArray)/sizeof(ADS_ATTR_INFO); // Figure out attribute count
cn.dwType = ADSTYPE_CASE_IGNORE_STRING;
cn.CaseIgnoreString=TEXT("Max-Payload-Battle-Cruiser");
singleValued.dwType = ADSTYPE_BOOLEAN;
singleValued.Boolean = VARIANT_TRUE;
oid.dwType = ADSTYPE_CASE_IGNORE_STRING;
oid.CaseIgnoreString=TEXT("1.2.840.113556.1.4.7000.157"); // reserved test OID
objectClass.dwType = ADSTYPE_CASE_IGNORE_STRING;
objectClass.CaseIgnoreString=TEXT("attributeSchema");
syntax.dwType = ADSTYPE_CASE_IGNORE_STRING;
syntax.CaseIgnoreString = TEXT("2.5.5.9"); // 2.5.5.9 = Integer
omSyntax.dwType = ADSTYPE_INTEGER;
omSyntax.Integer = 2;
//
// The ldap display name will be defaulted by the server and should
// not be provided unless different than the name computed from the CN
// attribute - the LDAP name is the CN with the hyphens removed and
// case delimiting substituted. The initial character is always lowercased.
// For this example we are providing an explicit LDAP display name to illustrate
// how it is done.
ldapname.dwType = ADSTYPE_CASE_IGNORE_STRING;
ldapname.CaseIgnoreString = TEXT("maxPayloadBattleCruiser");
//
// Schema-ID-Guid is provided by the server is the client does not
// provide it. This is a good example of how to write an Octet String
// to the DS.
idGuid.dwType = ADSTYPE_OCTET_STRING;
idGuid.OctetString.dwLength = sizeof(attrGuid3);
idGuid.OctetString.lpValue = (LPBYTE)&attrGuid3;
hr = pSchema->CreateDSObject(L"cn=Max-Payload-Battle-Cruiser",
attrArray,
dwAttrs,
&pDisp);
if (hr != NO_ERROR) {
ReportError(TEXT("Create Attribute Max-Payload-Battle-Cruiser failed."),hr);
// return;
} else {
printf("\nMax-Payload-Battle-Cruiser Attribute defined.\n");
// We are not going to use the IDispatch interface returned by the
// CreateDSObject call, so release it now.
pDisp->Release();
}
//**********************************************************************
// Allowed-Battle-Cruiser
//**********************************************************************
// Create a new attribute object. Set the values into the attribute
// unions, then call the method to create the object.
//
dwAttrs = sizeof(attrArray)/sizeof(ADS_ATTR_INFO); // Figure out attribute count
cn.dwType = ADSTYPE_CASE_IGNORE_STRING;
cn.CaseIgnoreString=TEXT("Allowed-Battle-Cruiser");
singleValued.dwType = ADSTYPE_BOOLEAN;
singleValued.Boolean = VARIANT_TRUE;
oid.dwType = ADSTYPE_CASE_IGNORE_STRING;
oid.CaseIgnoreString=TEXT("1.2.840.113556.1.4.7000.159"); // reserved test OID
objectClass.dwType = ADSTYPE_CASE_IGNORE_STRING;
objectClass.CaseIgnoreString=TEXT("attributeSchema");
syntax.dwType = ADSTYPE_CASE_IGNORE_STRING;
syntax.CaseIgnoreString = TEXT("2.5.5.8"); // 2.5.5.8 = Boolean
omSyntax.dwType = ADSTYPE_INTEGER;
omSyntax.Integer = 1;
//
// The ldap display name will be defaulted by the server and should
// not be provided unless different than the name computed from the CN
// attribute - the LDAP name is the CN with the hyphens removed and
// case delimiting substituted. The initial character is always lowercased.
// For this example we are providing an explicit LDAP display name to illustrate
// how it is done.
ldapname.dwType = ADSTYPE_CASE_IGNORE_STRING;
ldapname.CaseIgnoreString = TEXT("allowedBattleCruiser");
//
// Schema-ID-Guid is provided by the server is the client does not
// provide it. This is a good example of how to write an Octet String
// to the DS.
idGuid.dwType = ADSTYPE_OCTET_STRING;
idGuid.OctetString.dwLength = sizeof(attrGuid4);
idGuid.OctetString.lpValue = (LPBYTE)&attrGuid4;
hr = pSchema->CreateDSObject(L"cn=Allowed-Battle-Cruiser",
attrArray,
dwAttrs,
&pDisp);
if (hr != NO_ERROR) {
ReportError(TEXT("Create Attribute Allowed-Battle-Cruiser failed."),hr);
//return;
} else {
printf("\nAllowed-Battle-Cruiser Attribute defined.\n");
// We are not going to use the IDispatch interface returned by the
// CreateDSObject call, so release it now.
pDisp->Release();
}
// Force an update of the schema cache so we can create the class that
// includes these attributes. We force a synchronous schema update by writing
// the operational attribute "schemaUpdateNow" to the Root DSE.
//
varSchemaUpdate.vt = VT_I4;
varSchemaUpdate.intVal = 1;
hr = pRoot->Put(TEXT("schemaUpdateNow"),varSchemaUpdate);
hr = pRoot->SetInfo();
if (hr != NO_ERROR) {
ReportError(TEXT("Force Schema Recalc failed."),hr);
}
//**********************************************************************
// Policy-Parameters-Battle-Cruiser
//**********************************************************************
//
// Create a new class object and add attributes
// (including the new ones) to the class
//
cn.dwType = ADSTYPE_CASE_IGNORE_STRING;
cn.CaseIgnoreString=TEXT("Policy-Parameters-Battle-Cruiser");
objectClass.dwType = ADSTYPE_CASE_IGNORE_STRING;
objectClass.CaseIgnoreString=TEXT("classSchema");
oid.dwType = ADSTYPE_CASE_IGNORE_STRING;
oid.CaseIgnoreString=TEXT("1.2.840.113556.1.5.7000.92"); // Reserved Test OID
subClassOf.dwType = ADSTYPE_CASE_IGNORE_STRING;
subClassOf.CaseIgnoreString = TEXT("serviceConnectionPoint");
defaultSecurityDesc.dwType = ADSTYPE_CASE_IGNORE_STRING;
defaultSecurityDesc.CaseIgnoreString = TEXT("D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)S:(AU;SAFA;WDWOSDDTWPCRCCDCSW;;;WD)");
defaultHidingValue.dwType = ADSTYPE_BOOLEAN;
defaultHidingValue.Boolean = -1;
mayContain[0].dwType = ADSTYPE_CASE_IGNORE_STRING;
mayContain[0].CaseIgnoreString = TEXT("courseBattleCruiser");
mayContain[1].dwType = ADSTYPE_CASE_IGNORE_STRING;
mayContain[1].CaseIgnoreString = TEXT("speedBattleCruiser");
mayContain[2].dwType = ADSTYPE_CASE_IGNORE_STRING;
mayContain[2].CaseIgnoreString = TEXT("maxPayloadBattleCruiser");
mayContain[3].dwType = ADSTYPE_CASE_IGNORE_STRING;
mayContain[3].CaseIgnoreString = TEXT("allowedBattleCruiser");
mayContain[4].dwType = ADSTYPE_CASE_IGNORE_STRING;
mayContain[4].CaseIgnoreString = TEXT("consistencyGUID");
mayContain[5].dwType = ADSTYPE_CASE_IGNORE_STRING;
mayContain[5].CaseIgnoreString = TEXT("consistencyChildCount");
// Object-Class-Category=
// 88_CLASS 0
// STRUCTURAL_CLASS 1
// ABSTRACT_CLASS 2
// AUXILIARY_CLASS 3
objectClassCategory.dwType = ADSTYPE_INTEGER;
objectClassCategory.Integer = 1;
dwAttrs = sizeof(classArray)/sizeof(ADS_ATTR_INFO); // Figure out attribute count
hr = pSchema->CreateDSObject(L"cn=Policy-Parameters-Battle-Cruiser",
classArray,
dwAttrs,
&pDisp);
if (hr != NO_ERROR) {
ReportError(TEXT("Create Class Policy-Parameters-Battle-Cruiser failed."),hr);
} else {
printf("\nBattle Cruiser Class defined.\n");
// We are not going to use the IDispatch interface returned by the
// CreateDSObject call, so release it now.
pDisp->Release();
}
//**************************************************************************
// Add the consistency attributes to Group-Policy-Container and Container
//**************************************************************************
hr = ADsGetObject(gpcPath,
IID_IDirectoryObject,
(void**)&pGpc);
if (hr != NO_ERROR) {
ReportError(TEXT("Read GPC class object failed."),hr);
return;
} else {
printf("\nRetrieved GPC class object.\n");
mayContain[0].dwType = ADSTYPE_CASE_IGNORE_STRING;
mayContain[0].CaseIgnoreString = TEXT("consistencyGUID");
mayContain[1].dwType = ADSTYPE_CASE_IGNORE_STRING;
mayContain[1].CaseIgnoreString = TEXT("consistencyChildCount");
hr = pGpc->SetObjectAttributes(gpcUpdate,1,&iAttrsMod);
if (hr != NO_ERROR) {
ReportError(TEXT("Update GPC Class object failed."),hr);
} else {
printf("\nUpdated GPC Class object.\n");
}
}
// Done with class object
pGpc->Release();
//
// Now apply the consistency attributes to Container. The ATTR_INFO
// we need is already filled in, we just need to apply it.
//
hr = ADsGetObject(contPath,
IID_IDirectoryObject,
(void**)&pGpc);
if (hr != NO_ERROR) {
ReportError(TEXT("Read Container class object failed."),hr);
return;
} else {
printf("\nRetrieved Container class object.\n");
hr = pGpc->SetObjectAttributes(gpcUpdate,1,&iAttrsMod);
if (hr != NO_ERROR) {
ReportError(TEXT("Update Container Class object failed."),hr);
} else {
printf("\nUpdated Container Class object.\n");
}
}
// Force an update of the schema cache so we can use the changes immediately.
// We force a synchronous schema update by writing
// the operational attribute "schemaUpdateNow" to the Root DSE.
//
varSchemaUpdate.vt = VT_I4;
varSchemaUpdate.intVal = 1;
hr = pRoot->Put(TEXT("schemaUpdateNow"),varSchemaUpdate);
hr = pRoot->SetInfo();
if (hr != NO_ERROR) {
ReportError(TEXT("Force Schema Recalc failed."),hr);
}
// Done with Schema Container
pSchema->Release();
// Done with Root DSE
pRoot->Release();
//
CoUninitialize();
}
// Simple error message reporter
void ReportError(TCHAR *pTxt,DWORD err)
{
DWORD dwStatus;
TCHAR *pBuf;
dwStatus = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|
FORMAT_MESSAGE_ALLOCATE_BUFFER|
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
LANG_NEUTRAL,
(USHORT*)&pBuf,
64,
NULL);
if (dwStatus != 0) {
_tprintf(TEXT("%s %s"),pTxt,pBuf);
LocalFree(pBuf);
} else {
_tprintf(TEXT("%s %X\n"),pTxt,err);
}
}