Registrazione dell'oggetto COM pagina delle proprietà in un identificatore di visualizzazione

Quando si usa COM per creare una DLL di estensione della finestra delle proprietà per Dominio di Active Directory Services, è necessario registrare anche l'estensione con il Registro di sistema di Windows e i servizi Dominio di Active Directory. 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 come prodotto dalla funzione StringFromCLSID . <Nella chiave clsid> è presente una chiave InProcServer32 che identifica l'oggetto come server in-proc a 32 bit. Sotto la chiave 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 i servizi di Dominio di Active Directory

La registrazione dell'estensione della finestra delle proprietà è specifica per una sola impostazione locale. Se l'estensione della finestra delle proprietà si applica a tutte le impostazioni locali, deve essere registrata nell'oggetto displaySpecifier della classe 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 nell'oggetto displaySpecifier in tale sottocontenitore delle impostazioni locali. Per altre informazioni sul contenitore e le impostazioni locali degli identificatori di visualizzazione, vedere Contenitore Display Specifiers e DisplaySpecifiers.

Esistono tre attributi dell'identificatore di visualizzazione in cui è possibile registrare un'estensione della finestra delle proprietà. Si tratta di adminPropertyPages, shellPropertyPages e 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'attributo shellPropertyPages identifica 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 Dominio di Active Directory Services.

AdminMultiselectPropertyPages è disponibile solo nei 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 multivalore.

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

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

I valori per l'attributo adminMultiselectPropertyPages richiedono il formato seguente.

<order number>,<clsid>

Il "<numero> 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 del "<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> ordine" non esistente, ovvero uno non utilizzato da altri valori nella proprietà . Non esiste una posizione iniziale prestabilita e le lacune sono consentite nella sequenza "<numero> 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 puntatore IDataObject passato 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. Fornisce una struttura HGLOBAL che contiene una struttura DSPROPERTYPAGEINFO La struttura DSPROPERTYPAGEINFO contiene una stringa Unicode contenente i "<dati> facoltativi". L'attributo "<dati facoltativi" non è consentito con l'attributo adminMultiselectPropertyPages.> Nell'esempio di codice C/C++ seguente viene illustrato come recuperare 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 dei "<dati> facoltativi" consiste nel denominare le 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, shellPropertyPages o 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 della finestra delle proprietà

La procedura seguente descrive come registrare un'estensione della finestra delle proprietà in uno degli attributi di estensione della finestra 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. Aggiungere il nuovo valore alla fine dell'elenco di ordinamento delle pagine delle proprietà. Viene impostata la parte "<order number>" del valore dell'attributo sul valore successivo dopo il numero di ordine più alto esistente.
  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 i valori esistenti. Il metodo IADs::SetInfo deve essere chiamato in seguito 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 aggiunto ai valori esistenti e non sovrascriva pertanto i valori esistenti. Il metodo IADs::SetInfo deve essere chiamato dopo il commit della 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::P utEx 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;

}