읽기 특성Schema 및 classSchema 개체
이 항목에서는 스키마 컨테이너의 attributeSchema 및 classSchema 개체에서 직접 읽기 위한 코드 예제 및 지침을 제공합니다. 대부분의 프로그래밍 상황에서 클래스 또는 특성 정의에 대한 데이터를 읽어야 하는 경우 추상 스키마 읽기에 설명된 대로 추상 스키마에서 데이터를 읽는 것이 더 효과적입니다.
스키마 컨테이너에서 읽는 데 사용되는 인터페이스 및 기술은 Active Directory Domain Services 개체를 읽는 데 사용되는 인터페이스입니다. 지침은 다음과 같습니다.
- 스키마 컨테이너에 바인딩하려면 서버리스 바인딩 및 RootDSE에 설명된 대로 rootDSE에 바인딩하고 schemaNamingContext 속성을 읽어 검색할 수 있는 고유 이름을 가져옵니다.
- 스키마 컨테이너에 IADsContainer 포인터를 사용하여 attributeSchema 및 classSchema 개체를 열거합니다.
- IAD 또는IDirectoryObject 인터페이스를 사용하여 attributeSchema 및 classSchema 개체의 속성을 검색합니다.
- ADsOpenObject 또는 ADsGetObject 함수를 사용하여 attributeSchema 또는 classSchema 개체에 직접 바인딩합니다.
- 여러 attributeSchema 또는 classSchema 개체를 읽는 경우 스키마 컨테이너의 IADsContainer 포인터에 바인딩하고 IADsContainer.GetObject 메서드를 사용하여 개별 클래스 및 특성 개체에 바인딩하여 성능을 높일 수 있습니다. 이는 개별 클래스 및 특성 개체에 바인딩하기 위해 반복 ADsOpenObject 또는 ADsGetObject 호출을 하는 것보다 더 효율적입니다. cn 특성을 사용하여 IADsContainer.GetObject 호출에 대한 상대 경로를 빌드합니다(예: 사용자 클래스에 대한 classSchema 개체의 경우 "cn=user").
- 스키마 컨테이너에 대한 IDirectorySearch 포인터를 사용하여 검색 필터와 일치하는 특성 또는 클래스에 대한 스키마를 쿼리합니다.
스키마 개체를 검색하는 다양한 방법을 보여 주는 코드 예제는 스키 마 개체 검색에 대한 예제 코드를 참조하세요.
다음 C++ 코드 예제에서는 스키마 컨테이너의 IADsContainer 포인터에 바인딩한 다음 ADsBuildEnumerator 및 ADsEnumerateNext 함수를 사용하여 해당 내용을 열거합니다. 열거형에는 모든 attributeSchema 및 classSchema 개체와 추상 스키마인 단일 subSchema 개체가 포함됩니다.
열거된 각 개체에 대해 코드 예제에서는 IADs.Class 속성을 사용하여 특성Schema 또는 classSchema 개체인지 여부를 확인합니다. 코드 예제에서는 추상 스키마에서 사용할 수 없는 속성을 읽는 방법을 보여줍니다.
// Add activeds.lib to the project.
// Add adsiid.lib to the project.
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <ole2.h>
#include <activeds.h>
#include <atlbase.h>
// Forward declarations.
void ProcessAttribute(IADs *pChild);
void ProcessClass(IADs *pChild);
// Entry point for the application.
int wmain(int argc, WCHAR* argv[])
{
HRESULT hr;
hr = CoInitialize(NULL);
if(SUCCEEDED(hr))
{
CComBSTR sbstrDSPath;
CComVariant svar;
IADs *pRootDSE = NULL;
IADsContainer *pSchema = NULL;
IEnumVARIANT *pEnum = NULL;
ULONG lFetch;
CComBSTR sbstrClass;
DWORD dwClasses = 0, dwAttributes = 0, dwUnknownClass = 0;
// Bind to rootDSE to get the schemaNamingContext property.
hr = ADsGetObject(L"LDAP://rootDSE", IID_IADs, (void**)&pRootDSE);
if (FAILED(hr))
{
wprintf(L"ADsGetObject failed: 0x%x\n", hr);
goto cleanup;
}
hr = pRootDSE->Get(CComBSTR("schemaNamingContext"), &svar);
sbstrDSPath = "LDAP://";
sbstrDSPath += svar.bstrVal;
// Bind to the actual schema container.
wprintf(L"Binding to %s\n", sbstrDSPath);
hr = ADsGetObject(sbstrDSPath, IID_IADsContainer, (void**) &pSchema);
if (FAILED(hr))
{
wprintf(L"ADsGetObject to schema failed: 0x%x\n", hr);
goto cleanup;
}
// Enumerate the attribute and class objects in the schema container.
hr = ADsBuildEnumerator(pSchema, &pEnum);
if (FAILED(hr))
{
wprintf(L"ADsBuildEnumerator failed: 0x%x\n", hr);
goto cleanup;
}
hr = ADsEnumerateNext(pEnum, 1, &svar, &lFetch);
while(S_OK == hr && 1 == lFetch)
{
IADs *pChild = NULL;
// Get an IADs pointer on the child object.
hr = V_DISPATCH(&svar)->QueryInterface(IID_IADs, (void**) &pChild);
if (SUCCEEDED(hr))
{
// Verify that this is a class, attribute, or subSchema object.
hr = pChild->get_Class(&sbstrClass);
if (SUCCEEDED(hr))
{
// Get data. This depends on type of schema element.
if (_wcsicmp(L"classSchema", sbstrClass) == 0)
{
dwClasses++;
wprintf(L"%s\n", sbstrClass);
ProcessClass(pChild);
wprintf(L"\n");
}
else if (_wcsicmp(L"attributeSchema", sbstrClass) == 0)
{
dwAttributes++;
wprintf(L"%s\n", sbstrClass);
ProcessAttribute(pChild);
wprintf(L"\n");
}
else if (_wcsicmp(L"subSchema", sbstrClass) == 0)
{
wprintf(L"abstract schema");
wprintf(L"\n");
}
else
{
dwUnknownClass++;
}
}
pChild->Release();
}
hr = ADsEnumerateNext(pEnum, 1, &svar, &lFetch);
}
wprintf(L"Classes: %u\nAttributes: %u\nUnknown: %u\n", dwClasses, dwAttributes, dwUnknownClass);
cleanup:
if (pRootDSE)
{
pRootDSE->Release();
}
if (pEnum)
{
ADsFreeEnumerator(pEnum);
}
if (pSchema)
{
pSchema->Release();
}
}
CoUninitialize();
return 0;
}
// PrintGUIDFromVariant
// Prints a GUID in string format.
void PrintGUIDFromVariant(VARIANT varGUID)
{
HRESULT hr;
void HUGEP *pArray;
WCHAR szGUID[40];
LPGUID pGUID;
DWORD dwLen = sizeof(GUID);
hr = SafeArrayAccessData(V_ARRAY(&varGUID), &pArray);
if(SUCCEEDED(hr))
{
pGUID = (LPGUID)pArray;
// Convert GUID to string.
StringFromGUID2(*pGUID, szGUID, 40);
// Print GUID.
wprintf(L",%s",szGUID);
SafeArrayUnaccessData(V_ARRAY(&varGUID));
}
}
// PrintADSPropertyValue
void PrintADSPropertyValue(VARIANT var, BSTR bstrPropName, HRESULT hr)
{
if (E_ADS_PROPERTY_NOT_FOUND == hr)
{
wprintf(L"-- not set --\n");
}
else if (FAILED(hr))
{
wprintf(L"get %s failed: 0x%x\n", bstrPropName, hr);
}
else
{
switch (var.vt)
{
case VT_BSTR:
wprintf(L"%s\n", var.bstrVal);
break;
case VT_I4:
wprintf(L"%u\n", var.iVal);
break;
case VT_BOOL:
wprintf(L"%s\n", var.boolVal ? L"TRUE" : L"FALSE");
break;
default:
wprintf(L"-- ??? --\n");
}
}
}
// ProcessAttribute
// Gets information about an attributeClass object.
void ProcessAttribute(IADs *pChild)
{
HRESULT hr;
CComVariant svar;
CComBSTR sbstrPropName;
// Get the attribute Common-Name (cn) property.
sbstrPropName = "cn";
hr = pChild->Get(sbstrPropName, &svar);
PrintADSPropertyValue(svar, sbstrPropName, hr);
// Get the attribute lDAPDisplayName.
sbstrPropName = "lDAPDisplayName";
hr = pChild->Get(sbstrPropName, &svar);
PrintADSPropertyValue(svar, sbstrPropName, hr);
// Get the class linkID.
sbstrPropName = "linkID";
hr = pChild->Get(sbstrPropName, &svar);
PrintADSPropertyValue(svar, sbstrPropName, hr);
// Get the attribute schemaIDGUID.
sbstrPropName = "schemaIDGUID";
hr = pChild->Get(sbstrPropName, &svar);
if (E_ADS_PROPERTY_NOT_FOUND == hr)
{
wprintf(L"-- not set --\n");
}
else if(SUCCEEDED(hr))
{
PrintGUIDFromVariant(svar);
}
// Get the attribute attributeSecurityGUID.
sbstrPropName = "attributeSecurityGUID";
hr = pChild->Get(sbstrPropName, &svar);
if (E_ADS_PROPERTY_NOT_FOUND == hr)
{
wprintf(L"-- not set --\n");
}
else if (SUCCEEDED(hr))
{
PrintGUIDFromVariant(svar);
}
// Get the attribute attributeSyntax property.
sbstrPropName = "attributeSyntax";
hr = pChild->Get(sbstrPropName, &svar);
PrintADSPropertyValue(svar, sbstrPropName, hr);
// Get the attribute's oMSyntax property.
sbstrPropName = "oMSyntax";
hr = pChild->Get(sbstrPropName, &svar);
PrintADSPropertyValue(svar, sbstrPropName, hr);
}
// ProcessClass
// Gets information about a schema class.
void ProcessClass(IADs *pChild)
{
HRESULT hr;
CComVariant svar;
CComBSTR sbstrPropName;
// Get the class cn.
sbstrPropName = "cn";
hr = pChild->Get(sbstrPropName, &svar);
PrintADSPropertyValue(svar, sbstrPropName, hr);
// Get the class lDAPDisplayName.
sbstrPropName = "lDAPDisplayName";
hr = pChild->Get(sbstrPropName, &svar);
PrintADSPropertyValue(svar, sbstrPropName, hr);
// Get the class schemaIDGUID.
sbstrPropName = "schemaIDGUID";
hr = pChild->Get(sbstrPropName, &svar);
if (FAILED(hr))
{
wprintf(L",get schemaIDGUID failed: 0x%x", hr);
}
else
{
PrintGUIDFromVariant(svar);
}
// Get the class adminDescription property.
sbstrPropName = "adminDescription";
hr = pChild->Get(sbstrPropName, &svar);
PrintADSPropertyValue(svar, sbstrPropName, hr);
// Get the class adminDisplayName property.
sbstrPropName = "adminDisplayName";
hr = pChild->Get(sbstrPropName, &svar);
PrintADSPropertyValue(svar, sbstrPropName, hr);
// Get the class rDNAttID.
sbstrPropName = "rDNAttID";
hr = pChild->Get(sbstrPropName, &svar);
PrintADSPropertyValue(svar, sbstrPropName, hr);
// Get the class defaultHidingValue.
sbstrPropName = "defaultHidingValue";
hr = pChild->Get(sbstrPropName, &svar);
PrintADSPropertyValue(svar, sbstrPropName, hr);
// Get the class defaultObjectCategory.
sbstrPropName = "defaultObjectCategory";
hr = pChild->Get(sbstrPropName, &svar);
PrintADSPropertyValue(svar, sbstrPropName, hr);
// Get the class systemOnly value.
sbstrPropName = "systemOnly";
hr = pChild->Get(sbstrPropName, &svar);
PrintADSPropertyValue(svar, sbstrPropName, hr);
// Get the class defaultSecurityDescriptor.
sbstrPropName = "defaultSecurityDescriptor";
hr = pChild->Get(sbstrPropName, &svar);
PrintADSPropertyValue(svar, sbstrPropName, hr);
}