Example
Code for Extending the Schema Programmatically
/*
** schema.cxx: The following code example installs 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.
**
** Attributes: courseTugboat integer
** speedTugboat integer
** maxPayloadTugboat integer
** allowedTugboat integer
** consistencyGUID octet string
** consistencyChildCount integer
**
** Class: policyParametersTugboat
**
** 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) 2003 Microsoft Corporation
** All Rights Reserved
**
*/
#include <windows.h>
#include <ole2.h>
#include <iads.h>
#include <activeds.h>
#include <stdio.h>
#include <atlbase.h>
#define COURSE_ATTR_NAME L"Course-Tugboat"
#define COURSE_ATTR_RDN L"CN=Course-Tugboat"
#define COURSE_ATTR_LDAP_NAME L"courseTugboat"
#define SPEED_ATTR_NAME L"Speed-Tugboat"
#define SPEED_ATTR_RDN L"CN=Speed-Tugboat"
#define SPEED_ATTR_LDAP_NAME L"speedTugboat"
#define MAX_PAYLOAD_ATTR_NAME L"Max-Payload-Tugboat"
#define MAX_PAYLOAD_ATTR_RDN L"CN=Max-Payload-Tugboat"
#define MAX_PAYLOAD_ATTR_LDAP_NAME L"maxPayloadTugboat"
#define ALLOWED_ATTR_NAME L"Allowed-Tugboat"
#define ALLOWED_ATTR_RDN L"CN=Allowed-Tugboat"
#define ALLOWED_ATTR_LDAP_NAME L"allowedTugboat"
#define CONSISTENCY_GUID_ATTR_NAME L"Consistency-GUID"
#define CONSISTENCY_GUID_ATTR_RDN L"CN=Consistency-GUID"
#define CONSISTENCY_GUID_ATTR_LDAP_NAME L"consistencyGUID"
#define CONSISTENCY_CHILD_ATTR_NAME L"Consistency-Child-Count"
#define CONSISTENCY_CHILD_ATTR_RDN L"CN=Consistency-Child-Count"
#define CONSISTENCY_CHILD_ATTR_LDAP_NAME L"consistencyChildCount"
#define TUGBOAT_CLASS_NAME L"Policy-Parameters-Tugboat"
#define TUGBOAT_CLASS_RDN L"CN=Policy-Parameters-Tugboat"
// Forward declaration of error report
void ReportErrorW(LPCWSTR pwszDefaultMsg, 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; this will affect system performance
// when moving to an identity-based schema.
// GUIDS for the 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 main(void)
{
HRESULT hr;
IADs *pRoot = NULL;
VARIANT varDSRoot,
varSchemaUpdate;
LPWSTR pwszDSPath = NULL;
LPWSTR pwszGPCPath = NULL;
LPWSTR pwszContPath = NULL;
// Returned by CreateDSObject
IDispatch *pDisp = NULL;
// Pointers to schema objects
IDirectoryObject *pSchema = NULL,
*pGpc = NULL;
// 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 the name of the schema container,
// stored in the "schemaNamingContext" operational attribute.
hr = ADsGetObject(L"LDAP://RootDSE",
IID_IADs,
(void**)&pRoot);
if(FAILED(hr))
{
goto cleanup;
}
// Get IDirectoryObject on the root DSE as well; use this for
// forcing a schema update later.
VariantInit(&varDSRoot);
hr = pRoot->Get(CComBSTR("schemaNamingContext"), &varDSRoot);
if(FAILED(hr))
{
goto cleanup;
}
wprintf(L"\nDS Root:%S\n", varDSRoot.bstrVal);
// ADsPath of the schema container
pwszDSPath = new WCHAR[7 + lstrlenW(varDSRoot.bstrVal) + 1];
if(!pwszDSPath)
{
goto cleanup;
}
lstrcpyW(pwszDSPath, L"LDAP://");
lstrcatW(pwszDSPath, varDSRoot.bstrVal);
// ADsPath of the Group Policy Container class-schema object
pwszGPCPath = new WCHAR[33 + lstrlenW(varDSRoot.bstrVal) + 1];
if(!pwszGPCPath)
{
goto cleanup;
}
lstrcpyW(pwszGPCPath, L"LDAP://CN=Group-Policy-Container,");
lstrcatW(pwszGPCPath, varDSRoot.bstrVal);
// ADsPath of the Container class-schema object
pwszContPath = new WCHAR[20 + lstrlenW(varDSRoot.bstrVal) + 1];
if(!pwszContPath)
{
goto cleanup;
}
lstrcpyW(pwszContPath, L"LDAP://CN=Container,");
lstrcatW(pwszContPath, varDSRoot.bstrVal);
// Bind to the schema container and get the IDirectoryObject interface
// for it.
hr = ADsGetObject(pwszDSPath,
IID_IDirectoryObject,
(void**)&pSchema);
if(FAILED(hr))
{
goto cleanup;
}
//**********************************************************************
// 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); // Calculate attribute count.
cn.dwType = ADSTYPE_CASE_IGNORE_STRING;
cn.CaseIgnoreString = CONSISTENCY_CHILD_ATTR_NAME;
singleValued.dwType = ADSTYPE_BOOLEAN;
singleValued.Boolean = VARIANT_TRUE;
oid.dwType = ADSTYPE_CASE_IGNORE_STRING;
oid.CaseIgnoreString = L"1.2.840.113556.1.4.7000.161"; // Reserved. Test OID.
objectClass.dwType = ADSTYPE_CASE_IGNORE_STRING;
objectClass.CaseIgnoreString = L"attributeSchema";
syntax.dwType = ADSTYPE_CASE_IGNORE_STRING;
syntax.CaseIgnoreString = L"2.5.5.9"; // 2.5.5.9 = Integer
omSyntax.dwType = ADSTYPE_INTEGER;
omSyntax.Integer = 2;
//
// The ldap display name is 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 lowercase.
// For this example, an explicit LDAP display name is provided to show
// how it is done.
ldapname.dwType = ADSTYPE_CASE_IGNORE_STRING;
ldapname.CaseIgnoreString = CONSISTENCY_CHILD_ATTR_LDAP_NAME;
//
// 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(CONSISTENCY_CHILD_ATTR_RDN,
attrArray,
dwAttrs,
&pDisp);
if (FAILED(hr))
{
ReportErrorW(L"CreateDSObject failed.", hr);
}
else
{
wprintf(L"\n");
wprintf(CONSISTENCY_CHILD_ATTR_NAME);
wprintf(L" Attribute defined.\n");
// The IDispatch interface returned by the CreateDSObject call is not used.
// Release it now.
pDisp->Release();
pDisp = NULL;
}
//**********************************************************************
// 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 = L"Consistency-GUID";
singleValued.dwType = ADSTYPE_BOOLEAN;
singleValued.Boolean = VARIANT_TRUE;
oid.dwType = ADSTYPE_CASE_IGNORE_STRING;
oid.CaseIgnoreString = L"1.2.840.113556.1.4.7000.160"; // Reserved. Test OID.
objectClass.dwType = ADSTYPE_CASE_IGNORE_STRING;
objectClass.CaseIgnoreString = L"attributeSchema";
syntax.dwType = ADSTYPE_CASE_IGNORE_STRING;
syntax.CaseIgnoreString = L"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 lowercase.
// For this example, an explicit LDAP display name is provided to show
// how it is done.
ldapname.dwType = ADSTYPE_CASE_IGNORE_STRING;
ldapname.CaseIgnoreString = CONSISTENCY_GUID_ATTR_LDAP_NAME;
//
// 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(CONSISTENCY_GUID_ATTR_RDN,
attrArray,
dwAttrs,
&pDisp);
if (FAILED(hr))
{
ReportErrorW(L"CreateDSObject failed.", hr);
}
else
{
wprintf(L"\n");
wprintf(CONSISTENCY_GUID_ATTR_NAME);
wprintf(L" Attribute defined.\n");
// The IDispatch interface, returned by the CreateDSObject call, is not used.
// Release it now.
pDisp->Release();
pDisp = NULL;
}
//**********************************************************************
// Course-Tugboat
//**********************************************************************
// 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); // Calculate attribute count.
cn.dwType = ADSTYPE_CASE_IGNORE_STRING;
cn.CaseIgnoreString = COURSE_ATTR_NAME;
singleValued.dwType = ADSTYPE_BOOLEAN;
singleValued.Boolean = VARIANT_TRUE;
oid.dwType = ADSTYPE_CASE_IGNORE_STRING;
oid.CaseIgnoreString = L"1.2.840.113556.1.4.7000.155"; // Reserved. Test OID.
objectClass.dwType = ADSTYPE_CASE_IGNORE_STRING;
objectClass.CaseIgnoreString = L"attributeSchema";
syntax.dwType = ADSTYPE_CASE_IGNORE_STRING;
syntax.CaseIgnoreString = L"2.5.5.9"; // 2.5.5.9 = Integer
omSyntax.dwType = ADSTYPE_INTEGER;
omSyntax.Integer = 2;
//
// The LDAP display name is 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 lowercase.
// For this example, an explicit LDAP display name is provided to show
// how it is done.
ldapname.dwType = ADSTYPE_CASE_IGNORE_STRING;
ldapname.CaseIgnoreString = COURSE_ATTR_LDAP_NAME;
//
// 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(COURSE_ATTR_RDN,
attrArray,
dwAttrs,
&pDisp);
if (FAILED(hr))
{
ReportErrorW(L"CreateDSObject failed.", hr);
}
else
{
wprintf(L"\n");
wprintf(COURSE_ATTR_NAME);
wprintf(L" Attribute defined.\n");
// Do not use the IDispatch interface returned by the CreateDSObject call.
// Release it now.
pDisp->Release();
pDisp = NULL;
}
//**********************************************************************
// Speed-Tugboat
//**********************************************************************
// 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); // Calculate attribute count.
cn.dwType = ADSTYPE_CASE_IGNORE_STRING;
cn.CaseIgnoreString = SPEED_ATTR_NAME;
singleValued.dwType = ADSTYPE_BOOLEAN;
singleValued.Boolean = VARIANT_TRUE;
oid.dwType = ADSTYPE_CASE_IGNORE_STRING;
oid.CaseIgnoreString = L"1.2.840.113556.1.4.7000.156"; // Reserved. Test OID.
objectClass.dwType = ADSTYPE_CASE_IGNORE_STRING;
objectClass.CaseIgnoreString = L"attributeSchema";
syntax.dwType = ADSTYPE_CASE_IGNORE_STRING;
syntax.CaseIgnoreString = L"2.5.5.9"; // 2.5.5.9 = Integer
omSyntax.dwType = ADSTYPE_INTEGER;
omSyntax.Integer = 2;
//
// The LDAP display name is 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 lowercase.
// For this example, an explicit LDAP display name is provided to show
// how it is done.
ldapname.dwType = ADSTYPE_CASE_IGNORE_STRING;
ldapname.CaseIgnoreString = SPEED_ATTR_LDAP_NAME;
//
// Schema-ID-Guid is provided by the server if 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(SPEED_ATTR_RDN,
attrArray,
dwAttrs,
&pDisp);
if (FAILED(hr))
{
ReportErrorW(L"CreateDSObject failed.", hr);
}
else
{
wprintf(L"\n");
wprintf(SPEED_ATTR_NAME);
wprintf(L" Attribute defined.\n");
// Do not use the IDispatch interface returned by the
// CreateDSObject call. Release it now.
pDisp->Release();
pDisp = NULL;
}
//**********************************************************************
// Max-Payload-Tugboat
//**********************************************************************
// 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); // Calculate attribute count.
cn.dwType = ADSTYPE_CASE_IGNORE_STRING;
cn.CaseIgnoreString = MAX_PAYLOAD_ATTR_NAME;
singleValued.dwType = ADSTYPE_BOOLEAN;
singleValued.Boolean = VARIANT_TRUE;
oid.dwType = ADSTYPE_CASE_IGNORE_STRING;
oid.CaseIgnoreString = L"1.2.840.113556.1.4.7000.157"; // Reserved. Test OID.
objectClass.dwType = ADSTYPE_CASE_IGNORE_STRING;
objectClass.CaseIgnoreString = L"attributeSchema";
syntax.dwType = ADSTYPE_CASE_IGNORE_STRING;
syntax.CaseIgnoreString = L"2.5.5.9"; // 2.5.5.9 = Integer
omSyntax.dwType = ADSTYPE_INTEGER;
omSyntax.Integer = 2;
//
// The LDAP display name is 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 lowercase.
// For this example, an explicit LDAP display name is provided to show
// how it is done.
ldapname.dwType = ADSTYPE_CASE_IGNORE_STRING;
ldapname.CaseIgnoreString = MAX_PAYLOAD_ATTR_LDAP_NAME;
//
// 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(MAX_PAYLOAD_ATTR_RDN,
attrArray,
dwAttrs,
&pDisp);
if (FAILED(hr))
{
ReportErrorW(L"CreateDSObject failed.", hr);
}
else
{
wprintf(L"\n");
wprintf(MAX_PAYLOAD_ATTR_NAME);
wprintf(L" Attribute defined.\n");
// Do not use the IDispatch interface returned by the
// CreateDSObject call. Release it now.
pDisp->Release();
pDisp = NULL;
}
//**********************************************************************
// Allowed-Tugboat
//**********************************************************************
// 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); // Calculate attribute count.
cn.dwType = ADSTYPE_CASE_IGNORE_STRING;
cn.CaseIgnoreString = ALLOWED_ATTR_NAME;
singleValued.dwType = ADSTYPE_BOOLEAN;
singleValued.Boolean = VARIANT_TRUE;
oid.dwType = ADSTYPE_CASE_IGNORE_STRING;
oid.CaseIgnoreString = L"1.2.840.113556.1.4.7000.159"; // Reserved. Test OID.
objectClass.dwType = ADSTYPE_CASE_IGNORE_STRING;
objectClass.CaseIgnoreString = L"attributeSchema";
syntax.dwType = ADSTYPE_CASE_IGNORE_STRING;
syntax.CaseIgnoreString = L"2.5.5.8"; // 2.5.5.8 = Boolean
omSyntax.dwType = ADSTYPE_INTEGER;
omSyntax.Integer = 1;
//
// The LDAP display name is 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 lowercase.
// For this example, an explicit LDAP display name is provided to show
// how it is done.
ldapname.dwType = ADSTYPE_CASE_IGNORE_STRING;
ldapname.CaseIgnoreString = ALLOWED_ATTR_LDAP_NAME;
//
// 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(ALLOWED_ATTR_RDN,
attrArray,
dwAttrs,
&pDisp);
if (FAILED(hr))
{
ReportErrorW(L"CreateDSObject failed.", hr);
}
else
{
wprintf(L"\n");
wprintf(ALLOWED_ATTR_NAME);
wprintf(L" Attribute defined.\n");
// Do not use the IDispatch interface returned by the
// CreateDSObject call. Release it now.
pDisp->Release();
pDisp = NULL;
}
// Force an update of the schema cache to create the class that includes
// these attributes. 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(CComBSTR("schemaUpdateNow"), varSchemaUpdate);
hr = pRoot->SetInfo();
if (FAILED(hr))
{
ReportErrorW(L"Force Schema Recalc failed.", hr);
}
//**********************************************************************
// Policy-Parameters-Tugboat
//**********************************************************************
//
// Create a new class object and add attributes
// (including the new ones) to the class.
//
cn.dwType = ADSTYPE_CASE_IGNORE_STRING;
cn.CaseIgnoreString = TUGBOAT_CLASS_NAME;
objectClass.dwType = ADSTYPE_CASE_IGNORE_STRING;
objectClass.CaseIgnoreString = L"classSchema";
oid.dwType = ADSTYPE_CASE_IGNORE_STRING;
oid.CaseIgnoreString = L"1.2.840.113556.1.5.7000.92"; // Reserved. Test OID.
subClassOf.dwType = ADSTYPE_CASE_IGNORE_STRING;
subClassOf.CaseIgnoreString = L"serviceConnectionPoint";
defaultSecurityDesc.dwType = ADSTYPE_CASE_IGNORE_STRING;
defaultSecurityDesc.CaseIgnoreString = L"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 = COURSE_ATTR_LDAP_NAME;
mayContain[1].dwType = ADSTYPE_CASE_IGNORE_STRING;
mayContain[1].CaseIgnoreString = SPEED_ATTR_LDAP_NAME;
mayContain[2].dwType = ADSTYPE_CASE_IGNORE_STRING;
mayContain[2].CaseIgnoreString = MAX_PAYLOAD_ATTR_LDAP_NAME;
mayContain[3].dwType = ADSTYPE_CASE_IGNORE_STRING;
mayContain[3].CaseIgnoreString = ALLOWED_ATTR_LDAP_NAME;
mayContain[4].dwType = ADSTYPE_CASE_IGNORE_STRING;
mayContain[4].CaseIgnoreString = CONSISTENCY_GUID_ATTR_LDAP_NAME;
mayContain[5].dwType = ADSTYPE_CASE_IGNORE_STRING;
mayContain[5].CaseIgnoreString = CONSISTENCY_CHILD_ATTR_LDAP_NAME;
// 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); // Calculate attribute count.
hr = pSchema->CreateDSObject(TUGBOAT_CLASS_RDN,
classArray,
dwAttrs,
&pDisp);
if (FAILED(hr))
{
ReportErrorW(L"CreateDSObject failed.", hr);
}
else
{
wprintf(L"\nClass defined.\n");
// Do not use the IDispatch interface returned by the
// CreateDSObject call. Release it now.
pDisp->Release();
pDisp = NULL;
}
//**************************************************************************
// Add the consistency attributes to Group-Policy-Container and Container
//**************************************************************************
hr = ADsGetObject(pwszGPCPath,
IID_IDirectoryObject,
(void**)&pGpc);
if (FAILED(hr))
{
ReportErrorW(L"Read GPC class object failed.", hr);
goto cleanup;
}
else
{
wprintf(L"\nRetrieved GPC class object.\n");
mayContain[0].dwType = ADSTYPE_CASE_IGNORE_STRING;
mayContain[0].CaseIgnoreString = CONSISTENCY_GUID_ATTR_LDAP_NAME;
mayContain[1].dwType = ADSTYPE_CASE_IGNORE_STRING;
mayContain[1].CaseIgnoreString = CONSISTENCY_CHILD_ATTR_LDAP_NAME;
hr = pGpc->SetObjectAttributes(gpcUpdate, 1, &iAttrsMod);
if (FAILED(hr))
{
ReportErrorW(L"Update GPC Class object failed.", hr);
}
else
{
wprintf(L"\nUpdated GPC Class object.\n");
}
}
// Done with class object.
pGpc->Release();
pGpc = NULL;
//
// Apply the consistency attributes to Container. The ATTR_INFO
// required is filled in. Apply it.
//
hr = ADsGetObject(pwszContPath,
IID_IDirectoryObject,
(void**)&pGpc);
if (FAILED(hr))
{
ReportErrorW(L"Read Container class object failed.", hr);
goto cleanup;
}
else
{
wprintf(L"\nRetrieved Container class object.\n");
hr = pGpc->SetObjectAttributes(gpcUpdate,1,&iAttrsMod);
if (FAILED(hr))
{
ReportErrorW(L"Update Container Class object failed.", hr);
}
else
{
wprintf(L"\nUpdated Container Class object.\n");
}
}
// Force an update of the schema cache to use the changes immediately.
// 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(CComBSTR("schemaUpdateNow"), varSchemaUpdate);
hr = pRoot->SetInfo();
if (FAILED(hr))
{
ReportErrorW(L"Force Schema Recalc failed.", hr);
}
cleanup:
VariantClear(&varDSRoot);
VariantClear(&varSchemaUpdate);
if(pwszDSPath)
{
delete pwszDSPath;
}
if(pwszGPCPath)
{
delete pwszGPCPath;
}
if(pwszContPath)
{
delete pwszContPath;
}
if(pDisp)
{
pDisp->Release();
}
if(pSchema)
{
pSchema->Release();
}
if(pRoot)
{
pRoot->Release();
}
CoUninitialize();
}
// Simple error message reporter
void ReportErrorW(LPCWSTR pwszDefaultMsg, DWORD dwErr)
{
DWORD dwStatus;
LPWSTR pwszMsg;
dwStatus = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dwErr,
LANG_NEUTRAL,
(LPWSTR)&pwszMsg,
64,
NULL);
if (dwStatus != 0)
{
wprintf(L"%s %s", pwszDefaultMsg, pwszMsg);
LocalFree(pwszMsg);
}
else
{
wprintf(L"%s %X\n", pwszDefaultMsg, dwErr);
}
}