Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
При использовании COM для создания библиотеки DLL расширения свойств для доменных служб Active Directory необходимо также зарегистрировать расширение в реестре Windows и доменных службах Active Directory. Регистрация расширения позволяет компонентам управления MMC для Active Directory и оболочке Windows распознавать это расширение.
Регистрация в реестре Windows
Как и все COM-серверы, расширение листа свойств должно быть зарегистрировано в реестре Windows. Расширение зарегистрировано в следующем ключе.
HKEY_CLASSES_ROOT
CLSID
<clsid>
<clsid> представляет строковое представление CLSID, созданного функцией StringFromCLSID. В ключе <clsid> находится ключ InProcServer32, который определяет объект как 32-разрядный внутрь процессный сервер. В ключе InProcServer32 расположение библиотеки DLL указывается в значении по умолчанию, а модель потоков указывается в значении ThreadingModel. Все расширения листа свойств должны использовать модель потоков "Apartment".
Регистрация в доменных службах Active Directory
Регистрация расширения листа свойств специфична для одной языковой версии. Если расширение листа свойств применяется ко всем языковым стандартам, оно должно быть зарегистрировано в объекте класса дисплейСпецификатор во всех подконтейнерах языкового стандарта в контейнере спецификаторов отображения. Если расширение листа свойств локализовано для определенного языкового стандарта, зарегистрируйте его в объекте displaySpecifier в этом подконтейнере языкового стандарта. Для получения дополнительной информации о контейнере спецификаторов отображения и локалях, см. спецификаторы отображения и контейнер DisplaySpecifiers.
Существует три атрибута описателя отображения, в которых можно зарегистрировать расширение листа свойств. Это adminPropertyPages, shellPropertyPagesи adminMultiselectPropertyPages.
Атрибут adminPropertyPages определяет административные страницы свойств для отображения в оснастках администрирования Active Directory. Страница свойств отображается, когда пользователи просматривают свойства объектов соответствующего класса в одной из оснасток MMC администрирования Active Directory.
Атрибут shellPropertyPages определяет страницы свойств конечных пользователей для отображения в оболочке Windows. Страница свойств отображается, когда пользователь просматривает свойства для объектов соответствующего класса в проводнике Windows. Начиная с операционных систем Windows Server 2003 оболочка Windows больше не отображает объекты из доменных служб Active Directory.
adminMultiselectPropertyPages доступен только в операционных системах Windows Server 2003. Страница свойств отображается, когда пользователь просматривает свойства для более чем одного объекта соответствующего класса в одном из модулей управления MMC администратора Active Directory.
Все эти атрибуты являются многозначными.
Значения атрибутов adminPropertyPages и shellPropertyPages требуют следующего формата.
<order number>,<clsid>,<optional data>
Значения атрибута adminMultiselectPropertyPages требуют следующего формата.
<order number>,<clsid>
"<номер заказа>" — это неподписанный номер, представляющий позицию страницы на листе. При отображении листа свойств значения сортируются с помощью сравнения каждого значения по его "<порядкового номера>". Если несколько значений имеют одинаковый "<порядковый номер>", эти объекты COM страницы свойств загружаются в том порядке, в котором они считываются с сервера Active Directory. Если это возможно, следует использовать не существующий номер заказа<>"; т. е. один из них не используется другими значениями в свойстве. В последовательности "<порядковый номер>" нет предписанной стартовой позиции, и разрешены пробелы.
"<clsid>" является строковым представлением CLSID, созданным функцией StringFromCLSID.
Значение "<необязательных данных>" является строковым значением, которое не требуется. Это значение можно получить с помощью COM-объекта страницы свойств, используя указатель IDataObject, переданный в его метод IShellExtInit::Initialize. Объект COM страницы свойств получает эти данные путем вызова IDataObject::GetData с форматом буфера обмена CFSTR_DSPROPERTYPAGEINFO. Это предоставляет HGLOBAL, в которой содержится структура DSPROPERTYPAGEINFO. Структура DSPROPERTYPAGEINFO содержит строку Юникода, в которой содержатся "<необязательные данные>". Параметр "<необязательные данные>" не допускается с атрибутом adminMultiselectPropertyPages. В следующем примере кода C/C++ показано, как получить "<необязательные данные>".
fe.cfFormat = RegisterClipboardFormat(CFSTR_DSPROPERTYPAGEINFO);
fe.ptd = NULL;
fe.dwAspect = DVASPECT_CONTENT;
fe.lindex = -1;
fe.tymed = TYMED_HGLOBAL;
hr = pDataObj->GetData(&fe, &stm);
if(SUCCEEDED(hr))
{
DSPROPERTYPAGEINFO *pPageInfo;
pPageInfo = (DSPROPERTYPAGEINFO*)GlobalLock(stm.hGlobal);
if(pPageInfo)
{
LPWSTR pwszData;
pwszData = (LPWSTR)((LPBYTE)pPageInfo + pPageInfo->offsetString);
pwszData = NULL;
GlobalUnlock(stm.hGlobal);
}
ReleaseStgMedium(&stm);
}
Расширение листа свойств может реализовать несколько страниц свойств; одним из возможных вариантов использования<необязательных данных>является имя страниц для отображения. Это обеспечивает гибкость при выборе реализации нескольких COM-объектов, по одному для каждой страницы или одного COM-объекта для обработки нескольких страниц.
Следующий пример кода — это пример значения, которое можно использовать для adminPropertyPages, shellPropertyPagesили атрибутов adminMultiselectPropertyPages.
1,{6dfe6485-a212-11d0-bcd5-00c04fd8d5b6}
Важный
Для оболочки Windows данные описателя отображения извлекаются при входе пользователя и кэшируются для сеанса пользователя. Для административных оснасток данные спецификатора отображения извлекаются при загрузке оснастки и кэшируются на протяжении существования процесса. Для оболочки Windows это означает, что изменения в описателях отображения вступают в силу после того, как пользователь выйдет из системы и потом снова войдет. Для оснастки администрирования изменения вступают в силу при загрузке оснастки или консольного файла.
Добавление значения в атрибуты расширения листа свойств
В следующей процедуре описывается, как зарегистрировать расширение листа свойств в рамках одного из его атрибутов.
Регистрация расширения листа свойств по одному из его атрибутов
- Убедитесь, что расширение еще не существует в значениях атрибутов.
- Добавьте новое значение в конец списка упорядочивания страниц свойств. Установите значение "<номер заказа>" части значения атрибута на следующее значение выше самого большого существующего номера заказа.
- Метод IADs::PutEx используется для добавления нового значения в атрибут. Параметр lnControlCode должен иметь значение ADS_PROPERTY_APPEND, чтобы новое значение было добавлено к существующим значениям и не перезаписывает существующие значения. Метод IADs::SetInfo должен вызываться после фиксации изменения в каталоге.
Помните, что одно и то же расширение листа свойств можно зарегистрировать для нескольких классов объектов.
Метод IADs::PutEx используется для добавления нового значения в атрибут. Параметр lnControlCode должен иметь значение ADS_PROPERTY_APPEND, чтобы новое значение было добавлено к существующим значениям и не перезаписывались существующие значения. Метод IADs::SetInfo должен вызываться после фиксации изменения в каталоге.
В следующем примере кода к классу группы в стандартной локали компьютера по умолчанию добавляется расширение свойств. Помните, что функция AddPropertyPageToDisplaySpecifier проверяет расширение листа свойств CLSID в существующих значениях, получает самый высокий порядковый номер и добавляет значение страницы свойств с помощью IADs::PutEx с кодом управления ADS_PROPERTY_APPEND.
// Add msvcrt.dll to the project.
// Add activeds.lib to the project.
// Add adsiid.lib to the project.
#include "stdafx.h"
#include <wchar.h>
#include <objbase.h>
#include <activeds.h>
#include "atlbase.h"
HRESULT AddPropertyPageToDisplaySpecifier(LPOLESTR szClassName, // ldapDisplayName of the class.
CLSID *pPropPageCLSID // CLSID of property page COM object.
);
HRESULT BindToDisplaySpecifiersContainerByLocale(LCID *locale,
IADsContainer **ppDispSpecCont
);
HRESULT GetDisplaySpecifier(IADsContainer *pContainer, LPOLESTR szDispSpec, IADs **ppObject);
// Entry point for the application.
void wmain(int argc, wchar_t *argv[ ])
{
wprintf(L"This program adds a sample property page to the display specifier for group class in the local computer's default locale.\n");
// Initialize COM.
CoInitialize(NULL);
HRESULT hr = S_OK;
// Class ID for the sample property page.
LPOLESTR szCLSID = L"{D9FCE809-8A10-11d2-A7E7-00C04F79DC0F}";
LPOLESTR szClass = L"group";
CLSID clsid;
// Convert to GUID.
hr = CLSIDFromString(
szCLSID, // Pointer to the string representation of the CLSID.
&clsid // Pointer to the CLSID.
);
hr = AddPropertyPageToDisplaySpecifier(szClass, // ldapDisplayName of the class.
&clsid // CLSID of property page COM object.
);
if (S_OK == hr)
wprintf(L"Property page registered successfully\n");
else if (S_FALSE == hr)
wprintf(L"Property page was not added because it was already registered.\n");
else
wprintf(L"Property page was not added. HR: %x.\n");
// Uninitialize COM.
CoUninitialize();
return;
}
// Adds a property page to Active Directory admin snap-ins.
HRESULT AddPropertyPageToDisplaySpecifier(LPOLESTR szClassName, // ldapDisplayName of class.
CLSID *pPropPageCLSID // CLSID of property page COM object.
)
{
HRESULT hr = E_FAIL;
IADsContainer *pContainer = NULL;
LPOLESTR szDispSpec = new OLECHAR[MAX_PATH];
IADs *pObject = NULL;
VARIANT var;
CComBSTR sbstrProperty = L"adminPropertyPages";
LCID locale = NULL;
// Get the display specifier container using default system locale.
// When adding a property page COM object, specify the locale
// because the registration is locale-specific.
// This means if you created a property page
// for German, explicitly add it to the 407 container,
// so that it will be used when a computer is running with locale
// set to German and not the locale set on the
// computer where this application is running.
hr = BindToDisplaySpecifiersContainerByLocale(&locale,
&pContainer
);
// Handle fail case where dispspec object
// is not found and give option to create one.
if (SUCCEEDED(hr))
{
// Bind to display specifier object for the specified class.
// Build the display specifier name.
#ifdef _MBCS
wcscpy_s(szDispSpec, szClassName);
wcscat_s(szDispSpec, L"-Display");
hr = GetDisplaySpecifier(pContainer, szDispSpec, &pObject);
#endif _MBCS#endif _MBCS
if (SUCCEEDED(hr))
{
// Convert GUID to string.
LPOLESTR szDSGUID = new WCHAR [39];
::StringFromGUID2(*pPropPageCLSID, szDSGUID, 39);
// Get the adminPropertyPages property.
hr = pObject->GetEx(sbstrProperty, &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);
if (SUCCEEDED(hr))
{
hr = SafeArrayGetUBound(sa, 1, &lend);
}
if (SUCCEEDED(hr))
{
// Iterate the values to verify if the prop page CLSID
// is already registered.
VariantInit(&varItem);
BOOL bExists = FALSE;
UINT uiLastItem = 0;
UINT uiTemp = 0;
INT iOffset = 0;
LPOLESTR szMainStr = new OLECHAR[MAX_PATH];
LPOLESTR szItem = new OLECHAR[MAX_PATH];
LPOLESTR szStr = NULL;
for (long idx=lstart; idx <= lend; idx++)
{
hr = SafeArrayGetElement(sa, &idx, &varItem);
if (SUCCEEDED(hr))
{
#ifdef _MBCS
// Verify that the specified CLSID is already registered.
wcscpy_s(szMainStr,varItem.bstrVal);
if (wcsstr(szMainStr,szDSGUID))
bExists = TRUE;
// Get the index which is the number before the first comma.
szStr = wcschr(szMainStr, ',');
iOffset = (int)(szStr - szMainStr);
wcsncpy_s(szItem, szMainStr, iOffset);
szItem[iOffset]=0L;
uiTemp = _wtoi(szItem);
if (uiTemp > uiLastItem)
uiLastItem = uiTemp;
VariantClear(&varItem);
#endif _MBCS
}
}
// If the CLSID is not registered, add it.
if (!bExists)
{
// Build the value to add.
LPOLESTR szValue = new OLECHAR[MAX_PATH];
// Next index to add at end of list.
#ifdef _MBCS
uiLastItem++;
_itow_s(uiLastItem, szValue, 10);
wcscat_s(szValue,L",");
// Add the class ID for the property page.
wcscat_s(szValue,szDSGUID);
wprintf(L"Value to add: %s\n", szValue);
#endif _MBCS
VARIANT varAdd;
// Only one value to add
LPOLESTR pszAddStr[1];
pszAddStr[0]=szValue;
ADsBuildVarArrayStr(pszAddStr, 1, &varAdd);
hr = pObject->PutEx(ADS_PROPERTY_APPEND, sbstrProperty, varAdd);
if (SUCCEEDED(hr))
{
// Commit the change.
hr = pObject->SetInfo();
}
}
else
hr = S_FALSE;
}
}
VariantClear(&var);
}
if (pObject)
pObject->Release();
}
return hr;
}
// This function returns a pointer to the display specifier container
// for the specified locale.
// If locale is NULL, use the default system locale and then return the locale in the locale param.
HRESULT BindToDisplaySpecifiersContainerByLocale(LCID *locale,
IADsContainer **ppDispSpecCont
)
{
HRESULT hr = E_FAIL;
if ((!ppDispSpecCont)||(!locale))
return E_POINTER;
// If no locale is specified, use the default system locale.
if (!(*locale))
{
*locale = GetSystemDefaultLCID();
if (!(*locale))
return E_FAIL;
}
// Verify that it is a valid locale.
if (!IsValidLocale(*locale, LCID_SUPPORTED))
return E_INVALIDARG;
LPOLESTR szPath = new OLECHAR[MAX_PATH*2];
IADs *pObj = NULL;
VARIANT var;
hr = ADsOpenObject(L"LDAP://rootDSE",
NULL,
NULL,
ADS_SECURE_AUTHENTICATION, // Use Secure Authentication.
IID_IADs,
(void**)&pObj);
if (SUCCEEDED(hr))
{
// Get the DN to the config container.
hr = pObj->Get(CComBSTR("configurationNamingContext"), &var);
if (SUCCEEDED(hr))
{
#ifdef _MBCS
// Build the string to bind to the DisplaySpecifiers container.
swprintf_s(szPath,L"LDAP://cn=%x,cn=DisplaySpecifiers,%s", *locale, var.bstrVal);
// Bind to the DisplaySpecifiers container.
*ppDispSpecCont = NULL;
hr = ADsOpenObject(szPath,
NULL,
NULL,
ADS_SECURE_AUTHENTICATION, // Use Secure Authentication.
IID_IADsContainer,
(void**)ppDispSpecCont);
#endif _MBCS
if(FAILED(hr))
{
if (*ppDispSpecCont)
{
(*ppDispSpecCont)->Release();
(*ppDispSpecCont) = NULL;
}
}
}
}
// Cleanup.
VariantClear(&var);
if (pObj)
pObj->Release();
return hr;
}
HRESULT GetDisplaySpecifier(IADsContainer *pContainer, LPOLESTR szDispSpec, IADs **ppObject)
{
HRESULT hr = E_FAIL;
CComBSTR sbstrDSPath;
IDispatch *pDisp = NULL;
if (!pContainer)
{
hr = E_POINTER;
return hr;
}
// Verify other pointers.
// Initialize the output pointer.
(*ppObject) = NULL;
// Build relative path to the display specifier object.
sbstrDSPath = "CN=";
sbstrDSPath += szDispSpec;
// Use child object binding with IADsContainer::GetObject.
hr = pContainer->GetObject(CComBSTR("displaySpecifier"),
sbstrDSPath,
&pDisp);
if (SUCCEEDED(hr))
{
hr = pDisp->QueryInterface(IID_IADs, (void**)ppObject);
if (FAILED(hr))
{
// Clean up.
if (*ppObject)
(*ppObject)->Release();
}
}
if (pDisp)
pDisp->Release();
return hr;
}