Sdílet prostřednictvím


Registrace objektu COM vlastností stránky ve specifikátoru zobrazení

Při použití modelu COM k vytvoření rozšiřující knihovny DLL pro seznam vlastností pro služby Active Directory Domain Services musíte také zaregistrovat rozšíření v registru systému Windows a ve službách Active Directory Domain Services. Registrace rozšíření umožňuje, aby moduly snap-in konzoly MMC pro správu služby Active Directory a prostředí systému Windows rozšíření rozpoznaly.

Registrace v registru Systému Windows

Podobně jako všechny servery COM musí být rozšíření seznamu vlastností registrováno v registru Systému Windows. Rozšíření se zaregistruje pod následujícím klíčem.

HKEY_CLASSES_ROOT
   CLSID
      <clsid>

<clsid> je řetězcová reprezentace CLSID vytvořená funkcí StringFromCLSID. Pod klíčem <clsid> existuje klíč InProcServer32, který identifikuje objekt jako 32bitový server in-proc. Pod klíčem InProcServer32 je umístění knihovny DLL specifikováno ve výchozí hodnotě a model threadingu je zadán v hodnotě ThreadingModel. Všechna rozšíření seznamu vlastností musí používat model vláken "Apartment".

Registrace ve službě Active Directory Domain Services

Registrace rozšíření karty vlastností je specifická pro jedno místní nastavení. Pokud rozšíření seznamu vlastností platí pro všechny národní prostředí, musí být registrováno ve třídě objektu displaySpecifier objekt ve všech podkontejnerech národního prostředí v kontejneru Display Specifiers. Pokud je rozšíření seznamu vlastností lokalizováno pro určité národní prostředí, zaregistrujte ho v displaySpecifier objekt v podkontejneru národního prostředí. Další informace o kontejneru a národních prostředích specifikátorů zobrazení naleznete v tématu Specifikátory zobrazení a DisplaySpecifiers Kontejner.

Existují tři atributy specifikátoru zobrazení, pod kterými lze zaregistrovat rozšíření seznamu vlastností. Jedná se o adminPropertyPages, shellPropertyPagesa adminMultiselectPropertyPages.

Atribut adminPropertyPages identifikuje stránky vlastností pro správu, které se mají zobrazit v modulu snap-in pro správu služby Active Directory. Stránka vlastností se zobrazí, když uživatel zobrazí vlastnosti pro objekty příslušné třídy v jednom z modulů snap-in konzoly MMC pro správu služby Active Directory.

Atribut shellPropertyPages identifikuje stránky vlastností koncového uživatele, které se mají zobrazit v prostředí Windows. Stránka vlastností se zobrazí, když uživatel zobrazí vlastnosti pro objekty příslušné třídy v Průzkumníku Windows. Od operačních systémů Windows Server 2003 už prostředí Windows nezobrazuje objekty ze služby Active Directory Domain Services.

adminMultiselectPropertyPages je k dispozici pouze v operačních systémech Windows Server 2003. Stránka vlastností se zobrazí, když uživatel zobrazí vlastnosti pro více než jeden objekt příslušné třídy v jednom z modulů snap-in konzoly MMC pro správu služby Active Directory.

Všechny tyto atributy jsou vícehodnotové.

Hodnoty pro adminPropertyPages a shellPropertyPages atributy vyžadují následující formát.

<order number>,<clsid>,<optional data>

Hodnoty pro adminMultiselectPropertyPages atribut vyžadují následující formát.

<order number>,<clsid>

"<číslo objednávky>" je číslo bez znaménka, které představuje pozici stránky v listu. Při zobrazení seznamu vlastností se hodnoty seřadí pomocí porovnání hodnot "<číslo objednávky>". Pokud má více hodnot stejné „<číslo objednávky>“, objekty COM na stránce vlastností se načítají v pořadí, v jakém se čtou ze serveru služby Active Directory. Pokud je to možné, měli byste použít neexistující<číslo objednávky>, to znamená, takové, které není používáno jinými hodnotami v položce. Neexistuje žádná předepsané počáteční pozice a mezery jsou povoleny v posloupnosti "<číslo objednávky>".

"<clsid>" je řetězcová reprezentace CLSID, která je vytvořena StringFromCLSID funkce.

"<nepovinná data>" je řetězcová hodnota, která není povinná. Tuto hodnotu může načíst COM objekt stránky vlastností pomocí ukazatele IDataObject předaný jeho IShellExtInit::Initialize metodě. Objekt COM stránky vlastnosti získá tato data voláním IDataObject::GetData s formátem schránky CFSTR_DSPROPERTYPAGEINFO. To poskytuje HGLOBAL, který obsahuje DSPROPERTYPAGEINFO strukturu. Struktura DSPROPERTYPAGEINFO obsahuje řetězec Unicode, který obsahuje "<volitelná data>". U atributu adminMultiselectPropertyPages nejsou povolena volitelná data<>. Následující příklad kódu C/C++ ukazuje způsob, jak získat "<nepovinná data>".

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);
}

Rozšíření seznamu vlastností může implementovat více než jednu stránku vlastností; jedním z možných použití "<volitelných datových>" je pojmenování stránek, které se mají zobrazit. To poskytuje flexibilitu při volbě implementace více objektů MODELU COM, jednoho pro každou stránku nebo jednoho objektu MODELU COM pro zpracování více stránek.

Následující příklad kódu je příklad hodnoty, kterou lze použít pro adminPropertyPages, shellPropertyPages, nebo adminMultiselectPropertyPages atributy.

1,{6dfe6485-a212-11d0-bcd5-00c04fd8d5b6}

Důležitý

V prostředí Windows se data specifikátoru zobrazení načtou při přihlášení uživatele a ukládají se do mezipaměti pro uživatelskou relaci. U modulů snap-in pro správu se data specifikátoru zobrazení načtou při načtení modulu snap-in a po celou dobu trvání procesu se ukládají do mezipaměti. U prostředí Windows to znamená, že se změny zobrazovaných specifikátorů projeví po odhlášení uživatele a opětovném přihlášení. U modulů snap-in pro správu se změny projeví při načtení souboru modulu snap-in nebo konzoly.

 

Přidání hodnoty do atributů rozšíření seznamu vlastností

Následující postup popisuje, jak zaregistrovat rozšíření seznamu vlastností v jednom z atributů rozšíření seznamu vlastností.

Registrace rozšíření listu vlastností v rámci jednoho z atributů rozšíření listu vlastností

  1. Ujistěte se, že rozšíření ještě v hodnotách atributů neexistuje.
  2. Přidejte novou hodnotu na konec seznamu pořadí vlastnostní stránky. Tím se nastaví část "<číslo objednávky>" hodnoty atributu na další hodnotu za nejvyšším existujícím číslem objednávky.
  3. Metoda IADs::PutEx se používá k přidání nové hodnoty do atributu. Parametr lnControlCode musí být nastaven na ADS_PROPERTY_APPEND, aby se nová hodnota připojila k existujícím hodnotám a nepřepsala stávající hodnoty. Metoda ID::SetInfo musí být volána poté, aby se změna potvrdí do adresáře.

Mějte na paměti, že stejné rozšíření seznamu vlastností lze zaregistrovat pro více než jednu třídu objektů.

Metoda IADs::PutEx se používá k přidání nové hodnoty do atributu. Parametr lnControlCode musí být nastaven na ADS_PROPERTY_APPEND, aby se nová hodnota připojila k existujícím hodnotám a nepřepsala stávající hodnoty. Metoda ID::SetInfo musí být volána po potvrzení změny do adresáře.

Následující příklad kódu přidá rozšíření seznamu vlastností do třídy skupiny ve výchozím národním prostředí počítače. Mějte na paměti, že funkce AddPropertyPageToDisplaySpecifier ověřuje rozšíření listu vlastností CLSID v existujících hodnotách, získá nejvyšší pořadové číslo a přidá hodnotu pro stránku vlastnosti pomocí IADs::PutEx s řídicím kódem 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;

}