Поделиться через


Регистрация com-объекта страницы свойств в описателях отображения

При использовании 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-разрядный сервер в proc. В разделе ключа InProcServer32 расположение библиотеки DLL указывается в значении по умолчанию, а модель потоков указывается в значении ThreadingModel. Все расширения листа свойств должны использовать модель потоков "Apartment".

Регистрация в службах домен Active Directory

Регистрация расширения листа свойств относится к одному языковому стандарту. Если расширение листа свойств применяется ко всем языковым стандартам, оно должно быть зарегистрировано в объекте object displaySpecifier во всех подконтейнерах языкового стандарта в контейнере описателей отображения. Если расширение листа свойств локализовано для определенного языкового стандарта, зарегистрируйте его в объекте 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 это означает, что изменения, внесенные в описатели отображения, вступили в силу после выхода пользователя, а затем снова войдите в систему. Для оснастки администрирования изменения вступают в силу при загрузке оснастки или консольного файла.

 

Добавление значения в атрибуты расширения листа свойств

В следующей процедуре описывается регистрация расширения листа свойств в одном из атрибутов расширения листа свойств.

Регистрация расширения листа свойств в одном из атрибутов расширения листа свойств

  1. Убедитесь, что расширение еще не существует в значениях атрибутов.
  2. Добавьте новое значение в конце списка упорядочивания страниц свойств. Это задает< для части атрибута значение "порядковый номер>" следующим значением после самого высокого существующего номера заказа.
  3. Метод IADs::P utEx используется для добавления нового значения в атрибут. Параметр lnControlCode должен иметь значение ADS_PROPERTY_APPEND , чтобы новое значение было добавлено к существующим значениям и не перезаписывает существующие значения. Метод IADs::SetInfo должен вызываться после фиксации изменения в каталоге.

Помните, что одно и то же расширение листа свойств можно зарегистрировать для нескольких классов объектов.

Метод IADs::P utEx используется для добавления нового значения в атрибут. Параметр lnControlCode должен иметь значение ADS_PROPERTY_APPEND, чтобы новое значение было добавлено к существующим значениям и не перезаписывает существующие значения. Метод IADs::SetInfo должен вызываться после фиксации изменения в каталоге.

В следующем примере кода добавляется расширение листа свойств в класс группы в языковом стандарте компьютера по умолчанию. Помните, что функция AddPropertyPageToDisplaySpecifier проверяет расширение листа свойств CLSID в существующих значениях, получает наибольшее число заказа и добавляет значение для страницы свойств с помощью IADs::P utEx с кодом элемента управления 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;

}