Condividi tramite


Registrazione dell'oggetto COM della pagina proprietà in uno specificatore di visualizzazione

Quando si usa COM per creare una DLL di estensione della finestra delle proprietà per Active Directory Domain Services, è necessario registrare anche l'estensione con il Registro di sistema di Windows e Active Directory Domain Services. La registrazione dell'estensione consente agli snap-in MMC amministrativi di Active Directory e alla shell di Windows di riconoscere l'estensione.

Registrazione nel Registro di sistema di Windows

Come tutti i server COM, un'estensione della finestra delle proprietà deve essere registrata nel Registro di sistema di Windows. L'estensione viene registrata nella chiave seguente.

HKEY_CLASSES_ROOT
   CLSID
      <clsid>

<clsid> è la rappresentazione di stringa del CLSID generato dalla funzioneStringFromCLSID. Nella chiave <clsid> è presente una chiave InProcServer32 che identifica l'oggetto come server in-proc a 32 bit. Nella chiave di InProcServer32, il percorso della DLL viene specificato nel valore predefinito e il modello di threading viene specificato nel valore ThreadingModel. Tutte le estensioni della finestra delle proprietà devono usare il modello di threading "Apartment".

Registrazione con Servizi di dominio Active Directory

La registrazione dell'estensione del foglio delle proprietà è specifica per un solo locale. Se l'estensione della finestra delle proprietà si applica a tutte le impostazioni locali, deve essere registrata nella classe oggetto displaySpecifier oggetto in tutti i sottocontenitori delle impostazioni locali nel contenitore Identificatori di visualizzazione. Se l'estensione della finestra delle proprietà è localizzata per determinate impostazioni locali, registrarla nel displaySpecifier oggetto in tale sottocontenitore delle impostazioni locali. Per ulteriori informazioni sul contenitore Display Specifiers e le località, vedere Display Specifiers e Contenitore DisplaySpecifiers.

Esistono tre attributi del specificatore di visualizzazione sotto cui è possibile registrare un'estensione della pagina delle proprietà. Si tratta di adminPropertyPages, shellPropertyPagese adminMultiselectPropertyPages.

L'attributo adminPropertyPages identifica le pagine delle proprietà amministrative da visualizzare negli snap-in amministrativi di Active Directory. La pagina delle proprietà viene visualizzata quando l'utente visualizza le proprietà per gli oggetti della classe appropriata in uno degli snap-in MMC amministrativi di Active Directory.

L'attributoshellPropertyPagesidentifica le pagine delle proprietà dell'utente finale da visualizzare nella shell di Windows. La pagina delle proprietà viene visualizzata quando l'utente visualizza le proprietà per gli oggetti della classe appropriata in Esplora risorse. A partire dai sistemi operativi Windows Server 2003, la shell di Windows non visualizza più gli oggetti di Servizi di dominio Active Directory.

L'adminMultiselectPropertyPages è disponibile solo sui sistemi operativi Windows Server 2003. La pagina delle proprietà viene visualizzata quando l'utente visualizza le proprietà per più oggetti della classe appropriata in uno degli snap-in MMC amministrativi di Active Directory.

Tutti questi attributi sono a valori multipli.

I valori per gli attributi adminPropertyPages e shellPropertyPages richiedono il formato seguente.

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

I valori per l'attributoadminMultiselectPropertyPages richiedono il formato seguente.

<order number>,<clsid>

"<numero di ordine>" è un numero senza segno che rappresenta la posizione della pagina nel foglio. Quando viene visualizzata una finestra delle proprietà, i valori vengono ordinati usando un confronto tra il numero di ordine<di ogni valore>". Se più valori hanno lo stesso numero di ordine<>", tali oggetti COM della pagina delle proprietà vengono caricati nell'ordine in cui vengono letti dal server Active Directory. Se possibile, è consigliabile usare un numero di ordine non esistente<>"; ovvero una non utilizzata da altri valori nella proprietà . Non esiste una posizione iniziale prestabilita e le lacune sono consentite nella sequenza "<numero di ordine>".

"<clsid>" è la rappresentazione di stringa del CLSID prodotto dalla funzione StringFromCLSID.

"<dati facoltativi>" è un valore stringa non obbligatorio. Questo valore può essere recuperato dall'oggetto COM della pagina delle proprietà usando il puntatoreIDataObjectpassato al relativo metodo IShellExtInit::Initialize. L'oggetto COM della pagina delle proprietà ottiene questi dati chiamando IDataObject::GetData con il formato degli Appunti CFSTR_DSPROPERTYPAGEINFO. Viene fornito un HGLOBAL che contiene una struttura DSPROPERTYPAGEINFO. La struttura DSPROPERTYPAGEINFO contiene una stringa Unicode che contiene i "<dati facoltativi>". Non sono consentiti i "<dati facoltativi>" con l'attributo adminMultiselectPropertyPages. Nell'esempio seguente di codice C/C++ viene mostrato come ottenere i "<dati facoltativi>".

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

Un'estensione della finestra delle proprietà può implementare più pagine delle proprietà; un possibile uso di "<dati facoltativi>" consiste nel assegnare un nome alle pagine da visualizzare. In questo modo è possibile scegliere di implementare più oggetti COM, uno per ogni pagina o un singolo oggetto COM per gestire più pagine.

L'esempio di codice seguente è un valore di esempio che può essere usato per gli attributi adminPropertyPages, shellPropertyPageso adminMultiselectPropertyPages.

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

Importante

Per la shell di Windows, i dati dell'identificatore di visualizzazione vengono recuperati all'accesso utente e memorizzati nella cache per la sessione utente. Per gli snap-in amministrativi, i dati dell'identificatore di visualizzazione vengono recuperati quando viene caricato lo snap-in e vengono memorizzati nella cache per la durata del processo. Per la shell di Windows, indica che le modifiche apportate agli identificatori di visualizzazione diventano effettive dopo che un utente si disconnette e quindi esegue di nuovo l'accesso. Per gli snap-in amministrativi, le modifiche diventano effettive quando viene caricato lo snap-in o il file della console.

 

Aggiunta di un valore agli attributi dell'estensione del foglio delle proprietà

La procedura seguente descrive come registrare un'estensione della scheda delle proprietà sotto uno degli attributi dell'estensione della scheda delle proprietà.

Registrazione di un'estensione della finestra delle proprietà in uno degli attributi dell'estensione della finestra delle proprietà

  1. Verificare che l'estensione non esista già nei valori dell'attributo.
  2. Aggiungi il nuovo valore alla fine dell'elenco di ordinamento delle pagine delle proprietà. Impostare la parte "<numero di ordine>" del valore dell'attributo sul valore successivo dopo il numero di ordine esistente più alto.
  3. Il metodo IADs::P utEx viene usato per aggiungere il nuovo valore all'attributo. Il parametro lnControlCode deve essere impostato su ADS_PROPERTY_APPEND in modo che il nuovo valore venga aggiunto ai valori esistenti e non sovrascrive quindi i valori esistenti. Il metodo IADs::SetInfo deve essere chiamato successivamente per eseguire il commit della modifica nella directory.

Tenere presente che la stessa estensione della finestra delle proprietà può essere registrata per più di una classe oggetto.

Il metodo IADs::P utEx viene usato per aggiungere il nuovo valore all'attributo. Il parametro lnControlCode deve essere impostato su ADS_PROPERTY_APPEND, in modo che il nuovo valore venga accodato ai valori esistenti e non sovrascrive quindi i valori esistenti. Il metodo IADs::SetInfo deve essere chiamato dopo per confermare la modifica nella directory.

Nell'esempio di codice seguente viene aggiunta un'estensione della finestra delle proprietà alla classe group nelle impostazioni locali predefinite del computer. Tenere presente che la funzione AddPropertyPageToDisplaySpecifier verifica il CLSID dell'estensione della finestra delle proprietà nei valori esistenti, ottiene il numero di ordine più alto e aggiunge il valore per la pagina delle proprietà usando IADs::PutEx con il codice di controllo 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;

}