Mendaftarkan Objek COM Halaman Properti di Penentu Tampilan

Saat Anda menggunakan COM untuk membuat DLL ekstensi lembar properti untuk Active Directory Domain Services, Anda juga harus mendaftarkan ekstensi dengan registri Windows dan Active Directory Domain Services. Mendaftarkan ekstensi memungkinkan snap-in MMC administratif Direktori Aktif dan shell Windows untuk mengenali ekstensi.

Mendaftar di Windows Registry

Seperti semua server COM, ekstensi lembar properti harus terdaftar di registri Windows. Ekstensi terdaftar di bawah kunci berikut.

HKEY_CLASSES_ROOT
   CLSID
      <clsid>

<clsid adalah representasi string DARI CLSID seperti yang dihasilkan oleh fungsi StringFromCLSID.> <Di bawah kunci clsid>, ada kunci InProcServer32 yang mengidentifikasi objek sebagai server in-proc 32-bit. Di bawah kunci InProcServer32 , lokasi DLL ditentukan dalam nilai default dan model utas ditentukan dalam nilai ThreadingModel . Semua ekstensi lembar properti harus menggunakan model utas "Apartemen".

Mendaftar dengan Active Directory Domain Services

Pendaftaran ekstensi lembar properti khusus untuk satu lokal. Jika ekstensi lembar properti berlaku untuk semua lokal, ekstensi harus didaftarkan di kelas objek displaySpecifier objek di semua subkontainer lokal dalam kontainer Penentu Tampilan. Jika ekstensi lembar properti dilokalkan untuk lokal tertentu, daftarkan di objek displaySpecifier di subkontainer lokal tersebut. Untuk informasi selengkapnya tentang kontainer dan lokal Penentu Tampilan, lihat Penentu Tampilan dan Kontainer DisplaySpecifiers.

Ada tiga atribut penentu tampilan yang dapat didaftarkan oleh ekstensi lembar properti. Ini adalah adminPropertyPages, shellPropertyPages, dan adminMultiselectPropertyPages.

Atribut adminPropertyPages mengidentifikasi halaman properti administratif untuk ditampilkan di snap-in administratif Direktori Aktif. Halaman properti muncul saat pengguna melihat properti untuk objek kelas yang sesuai di salah satu snap-in MMC administratif Direktori Aktif.

Atribut shellPropertyPages mengidentifikasi halaman properti pengguna akhir untuk ditampilkan di shell Windows. Halaman properti muncul saat pengguna melihat properti untuk objek kelas yang sesuai di Windows Explorer. Dimulai dengan sistem operasi Windows Server 2003, shell Windows tidak lagi menampilkan objek dari Active Directory Domain Services.

AdminMultiselectPropertyPages hanya tersedia di sistem operasi Windows Server 2003. Halaman properti muncul saat pengguna melihat properti untuk lebih dari satu objek kelas yang sesuai di salah satu snap-in MMC administratif Direktori Aktif.

Semua atribut ini bernilai multinilai.

Nilai untuk atribut adminPropertyPages dan shellPropertyPages memerlukan format berikut.

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

Nilai untuk atribut adminMultiselectPropertyPages memerlukan format berikut.

<order number>,<clsid>

"<Nomor> pesanan" adalah angka yang tidak ditandatangani yang mewakili posisi halaman dalam lembar. Saat lembar properti ditampilkan, nilai diurutkan menggunakan perbandingan "<nomor> pesanan" setiap nilai. Jika lebih dari satu nilai memiliki "<nomor pesanan>" yang sama, objek COM halaman properti tersebut dimuat dalam urutan pembacaannya dari server Direktori Aktif. Jika memungkinkan, Anda harus menggunakan "<nomor> pesanan" yang tidak ada; yaitu, yang tidak digunakan oleh nilai lain dalam properti . Tidak ada posisi awal yang ditentukan, dan celah diizinkan dalam urutan "<nomor> pesanan".

"clsid" adalah representasi string DARI CLSID seperti yang dihasilkan oleh fungsi StringFromCLSID.><

"<Data> opsional" adalah nilai string yang tidak diperlukan. Nilai ini dapat diambil oleh objek COM halaman properti menggunakan penunjuk IDataObject yang diteruskan ke metode IShellExtInit::Initialize-nya. Objek COM halaman properti mendapatkan data ini dengan memanggil IDataObject::GetData dengan format clipboard CFSTR_DSPROPERTYPAGEINFO. Ini menyediakan HGLOBAL yang berisi struktur DSPROPERTYPAGEINFO Struktur DSPROPERTYPAGEINFO berisi string Unicode yang berisi "<data> opsional". "<Data opsional" tidak diizinkan dengan atribut adminMultiselectPropertyPages.> Contoh kode C/C++ berikut menunjukkan cara mengambil "<data> opsional".

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

Ekstensi lembar properti dapat menerapkan lebih dari satu halaman properti; salah satu kemungkinan penggunaan "<data> opsional" adalah memberi nama halaman yang akan ditampilkan. Ini memberikan fleksibilitas memilih untuk mengimplementasikan beberapa objek COM, satu untuk setiap halaman, atau satu objek COM untuk menangani beberapa halaman.

Contoh kode berikut adalah contoh nilai yang dapat digunakan untuk atribut adminPropertyPages, shellPropertyPages, atau adminMultiselectPropertyPages.

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

Penting

Untuk shell Windows, data penentu tampilan diambil saat masuk pengguna, dan di-cache untuk sesi pengguna. Untuk snap-in administratif, data penentu tampilan diambil saat snap-in dimuat, dan di-cache selama masa proses. Untuk shell Windows, ini menunjukkan bahwa perubahan pada penentu tampilan berlaku setelah pengguna keluar lalu masuk lagi. Untuk snap-in administratif, perubahan berlaku saat file snap-in atau konsol dimuat.

 

Menambahkan Nilai ke Atribut Ekstensi Lembar Properti

Prosedur berikut menjelaskan cara mendaftarkan ekstensi lembar properti di bawah salah satu atribut ekstensi lembar properti.

Mendaftarkan ekstensi lembar properti di bawah salah satu atribut ekstensi lembar properti

  1. Pastikan bahwa ekstensi belum ada dalam nilai atribut.
  2. Tambahkan nilai baru di akhir daftar urutan halaman properti. Itu diatur bagian "<nomor> pesanan" dari nilai atribut ke nilai berikutnya setelah nomor pesanan tertinggi yang ada.
  3. Metode IADs::P utEx digunakan untuk menambahkan nilai baru ke atribut . Parameter lnControlCode harus diatur ke ADS_PROPERTY_APPEND sehingga nilai baru ditambahkan ke nilai yang ada dan tidak, oleh karena itu, menimpa nilai yang ada. Metode IADs::SetInfo harus dipanggil setelahnya untuk menerapkan perubahan ke direktori.

Ketahuilah bahwa ekstensi lembar properti yang sama dapat didaftarkan untuk lebih dari satu kelas objek.

Metode IADs::P utEx digunakan untuk menambahkan nilai baru ke atribut . Parameter lnControlCode harus diatur ke ADS_PROPERTY_APPEND, sehingga nilai baru ditambahkan ke nilai yang ada dan tidak, oleh karena itu, menimpa nilai yang ada. Metode IADs::SetInfo harus dipanggil setelah untuk menerapkan perubahan ke direktori.

Contoh kode berikut menambahkan ekstensi lembar properti ke kelas grup di lokal default komputer. Ketahuilah bahwa fungsi AddPropertyPageToDisplaySpecifier memverifikasi ekstensi lembar properti CLSID dalam nilai yang ada, mendapatkan nomor pesanan tertinggi, dan menambahkan nilai untuk halaman properti menggunakan IADs::P utEx dengan kode kontrol 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;

}