プロパティページのCOMオブジェクトを表示指定子に登録します

COMを使用してActive Directoryドメインサービスのプロパティシート拡張DLLを作成する場合は、WindowsレジストリとActive Directoryドメインサービスにも拡張機能を登録する必要があります。 拡張機能を登録すると、Active Directory管理MMCスナップインとWindowsシェルが拡張機能を認識できるようになります。

Windowsレジストリへの登録

すべてのCOMサーバーと同様に、プロパティシート拡張機能はWindowsレジストリに登録する必要があります。 拡張機能は、次のキーの下に登録されます。

HKEY_CLASSES_ROOT
   CLSID
      <clsid>

<clsid> は、StringFromCLSID関数によって生成されるCLSIDの文字列表現です。 <clsid>キーの下には、オブジェクトを-bitインプロセスサーバーとして識別するInProcServer32キーがあります。 InProcServer32キーの下では、DLLの場所が既定値で指定され、スレッドモデルが ThreadingModel値で指定されています。 すべてのプロパティシート拡張機能は、"アパートメント"スレッドモデルを使用する必要があります。

Active Directoryドメインサービスへの登録

プロパティシート拡張機能の登録は、1つのロケールに固有です。 プロパティシート拡張機能がすべてのロケールに適用される場合は、表示指定子コンテナー内のすべてのロケールサブコンテナーのオブジェクトクラス displaySpecifierオブジェクトに登録する必要があります。 プロパティシート拡張機能が特定のロケール用にローカライズされている場合は、そのロケールサブコンテナーのdisplaySpecifierオブジェクトに登録します。 Display Specifiersコンテナとロケールの詳細については、Display SpecifiersDisplaySpecifiers Containerを参照のこと。

プロパティシート拡張機能を登録できる表示指定子属性は3つあります。 これらは、adminPropertyPages, shellPropertyPages、およびadminMultiselectPropertyPagesです。

adminPropertyPages属性は、Active Directory管理スナップインに表示する管理プロパティページを識別します。プロパティページは、ユーザーがActive Directory管理MMCスナップインのいずれかで適切なクラスのオブジェクトのプロパティを表示すると表示されます。

shellPropertyPages属性は、Windowsシェルに表示するエンドユーザープロパティページを識別します。 プロパティページは、ユーザーがエクスプローラーで適切なクラスのオブジェクトのプロパティを表示すると表示されます。 Windows Server 2003オペレーティングシステム以降、WindowsシェルではActive Directoryドメインサービスのオブジェクトが表示されなくなりました。

adminMultiselectPropertyPagesは、Windows Server 2003オペレーティングシステムでのみ使用できます。 プロパティページは、ユーザーがいずれかのActive Directory管理MMCスナップインで適切なクラスの複数のオブジェクトのプロパティを表示すると表示されます。

これらの属性はすべて複数値です。

adminPropertyPages属性とshellPropertyPages属性の値には、次の形式が必要です。

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

adminMultiselectPropertyPages属性の値には、次の形式が必要です。

<order number>,<clsid>

<オーダー番号>は、シート内のページ位置を表す符号なし番号である。 プロパティ・シートが表示されると、各値の「<順序番号>」の比較を使って値がソートされる。 複数の値が同じ<順序番号>を持つ場合、それらのプロパティページCOMオブジェクトはActive Directoryサーバーから読み取られた順序で読み込まれます。 可能であれば、存在しない<順序番号>を使用する必要があります。つまり、プロパティ内の他の値で使用されていないものです。 指定された開始位置はなく、<順序番号>シーケンスではギャップが許可されます。

<clsid>は、StringFromCLSID関数によって生成されるCLSIDの文字列表現です。

<省略可能なデータ>は、必須ではない文字列値です。 この値は、IShellExtInit::Initialize メソッドに渡された IDataObject ポインターを使用して、プロパティページのCOMオブジェクトによって取得できます。 プロパティページの COM オブジェクトは、CFSTR_DSPROPERTYPAGEINFO クリップボード形式を使用して IDataObject::GetData を呼び出すことによって、このデータを取得します。 これにより、DSPROPERTYPAGEINFO 構造体を含む HGLOBAL が提供されます。DSPROPERTYPAGEINFO 構造体には、<省略可能なデータ>を含む Unicode 文字列が含まれています。 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);
}

プロパティシート拡張機能は、複数のプロパティページを実装できます<省略可能なデータ>の使用方法の1つは、表示するページに名前を付けることです。 これにより、複数のCOMオブジェクト、ページごとに1つ、または複数のページを処理する1つのCOMオブジェクトを実装するための柔軟な選択が可能になります。

次のコード例は、adminPropertyPages, shellPropertyPages、またはor adminMultiselectPropertyPages属性に使用できる値の例です。

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

重要

Windowsシェルの場合、表示指定子データはユーザーのログオン時に取得され、ユーザーセッション用にキャッシュされます。 管理スナップインの場合、表示指定子データはスナップインの読み込み時に取得され、プロセスの有効期間中はキャッシュされます。 Windowsシェルの場合、これは、ユーザーがログオフしてから再度ログオンした後に、表示指定子への変更が有効になることを示します。 管理スナップインの場合、スナップインまたはコンソールファイルが読み込まれると、変更が有効になります。

 

プロパティシート拡張属性への値の追加

次の手順では、プロパティシート拡張属性のいずれかにプロパティシート拡張を登録する方法について説明します。

プロパティシート拡張属性のいずれかにプロパティシート拡張を登録します

  1. 拡張機能が属性値にまだ存在していないことを確認します。
  2. プロパティページの順序リストの末尾に新しい値を追加します。 これは、属性値の<注文番号>部分を、既存の最も大きい順序番号の後の次の値に設定します。
  3. IADs::PutExメソッドを使用して、新しい値を属性に追加します。 新しい値が既存の値に追加され、そのため既存の値が上書きされないように、lnControlCodeパラメーターをADS_PROPERTY_APPENDに設定する必要があります。 ディレクトリへの変更をコミットするには、後でIADs::SetInfoメソッドを呼び出す必要があります。

同じプロパティシート拡張機能を複数のオブジェクトクラスに登録できることに注意してください。

IADs::PutExメソッドを使用して、新しい値を属性に追加します。 新しい値が既存の値に追加され、そのため既存の値が上書きされないように、lnControlCodeパラメーターをADS_PROPERTY_APPENDに設定する必要があります。 ディレクトリへの変更をコミットするには、後でIADs::SetInfoメソッドを呼び出す必要があります。

次のコード例では、コンピューターの既定のロケールでグループクラスにプロパティシート拡張機能を追加します。 AddPropertyPageToDisplaySpecifier 関数は、既存の値のプロパティシート拡張 CLSID を検証し、最上位の順序番号を取得し、ADS_PROPERTY_APPEND 制御コードを使用して IADs::PutEx を使用してプロパティページの値を追加することに注意してください。

//  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;

}