Example Code for Implementation of the Property Sheet COM Object

The following code example can be used to implement an Active Directory property sheet extension.

IShellExtInit Implementation

The following C++ code example can be used to implement the IShellExtInit methods.

/**************************************************************************

    CPropSheetExt::Initialize()

**************************************************************************/

STDMETHODIMP CPropSheetExt::Initialize( LPCITEMIDLIST pidlFolder,
                                        IDataObject *pDataObj,
                                        HKEY hKeyProgId)
{
    STGMEDIUM   stm;
    FORMATETC   fe;
    HRESULT     hr = S_OK;

    if(NULL == pDataObj)
    {
        return E_INVALIDARG;
    }

    hr = pDataObj->QueryInterface(IID_IDataObject, (LPVOID*)&m_pdo);
    if(FAILED(hr))
    {
        return hr;
    }

    fe.cfFormat = RegisterClipboardFormat(CFSTR_DSOBJECTNAMES);
    fe.ptd = NULL;
    fe.dwAspect = DVASPECT_CONTENT;
    fe.lindex = -1;
    fe.tymed = TYMED_HGLOBAL;
    hr = m_pdo->GetData(&fe, &stm);
    if(SUCCEEDED(hr))
    {
        LPDSOBJECTNAMES pdson = (LPDSOBJECTNAMES)GlobalLock(stm.hGlobal);

        if(pdson)
        {
            LPWSTR  pwszName = (LPWSTR)((LPBYTE)pdson + pdson->aObjects[0].offsetName);
            
            m_pwszADsPath = (LPWSTR)GlobalAlloc(GPTR, (lstrlenW(pwszName) + 1) * sizeof(WCHAR));
            if(m_pwszADsPath)
            {
                wcscpy_s(m_pwszADsPath, pwszName);
            }
            
            LPWSTR  pwszClass = (LPWSTR)((LPBYTE)pdson + pdson->aObjects[0].offsetClass);
            pwszClass = NULL;

            GlobalUnlock(stm.hGlobal);
        }
        
        ReleaseStgMedium(&stm);
    }

    m_hwndNotifyObj = CreateADsNotificationObject(pDataObj);

    if(m_hwndNotifyObj)
    {
        ADSPROPINITPARAMS   InitParams;
        
        hr = GetADsPageInfo(m_hwndNotifyObj, &InitParams);
        if(SUCCEEDED(hr))
        {
            if(InitParams.pDsObj)
            {
                hr = InitParams.pDsObj->QueryInterface( IID_IDirectoryObject, 
                                                        (LPVOID*) & m_pDirObj);
            }
        }
    }

    fe.cfFormat = RegisterClipboardFormat(CFSTR_DS_DISPLAY_SPEC_OPTIONS);
    fe.ptd = NULL;
    fe.dwAspect = DVASPECT_CONTENT;
    fe.lindex = -1;
    fe.tymed = TYMED_HGLOBAL;
    hr = pDataObj->GetData(&fe, &stm);
    if(SUCCEEDED(hr))
    {
        PDSDISPLAYSPECOPTIONS   pdso;
        
        pdso = (PDSDISPLAYSPECOPTIONS)GlobalLock(stm.hGlobal);
        if(pdso)
        {
            DWORD   dwBytes = GlobalSize(stm.hGlobal);

            m_pDispSpecOpts = (PDSDISPLAYSPECOPTIONS)GlobalAlloc(GPTR, dwBytes);
            if(m_pDispSpecOpts)
            {
                CopyMemory(m_pDispSpecOpts, pdso, dwBytes);
            }
            
            GlobalUnlock(stm.hGlobal);
        }

        ReleaseStgMedium(&stm);
    }

    return hr;
}

IShellPropSheetExt Implementation

The following C++ code example can be used to implement the IShellPropSheetExt methods.

/**************************************************************************

    CPropSheetExt::AddPages()

**************************************************************************/

STDMETHODIMP CPropSheetExt::AddPages(   LPFNADDPROPSHEETPAGE pfnAddPage, 
                                        LPARAM lParam)
{
    PROPSHEETPAGE  psp;
    HPROPSHEETPAGE hPage;

    psp.dwSize        = sizeof(psp);
    psp.dwFlags       = PSP_USETITLE | PSP_USECALLBACK;
    psp.hInstance     = g_hInst;
    psp.pszTemplate   = MAKEINTRESOURCE(IDD_PAGEDLG);
    psp.hIcon         = 0;
    psp.pszTitle      = g_szPageTitle;
    psp.pfnDlgProc    = (DLGPROC)PageDlgProc;
    psp.pcRefParent   = NULL;
    psp.pfnCallback   = PageCallbackProc;
    //pass the object pointer to the dialog box
    psp.lParam        = (LPARAM)this;

    hPage = CreatePropertySheetPage(&psp);
                
    if(hPage) 
    {
        if(pfnAddPage(hPage, lParam))
        {
            /*
            Maintain this object until the page is released in 
            PageCallbackProc.
            */
            this->AddRef();
            return S_OK;
        }
        else
        {
            DestroyPropertySheetPage(hPage);
        }
    }
    else
    {
        return E_OUTOFMEMORY;
    }

    return E_FAIL;
}

/**************************************************************************

    CPropSheetExt::ReplacePage()

**************************************************************************/

STDMETHODIMP CPropSheetExt::ReplacePage(    UINT uPageID, 
                                            LPFNADDPROPSHEETPAGE lpfnAddPage, 
                                            LPARAM lParam)
{
    return E_NOTIMPL;
}

Miscellaneous Implementation

The following C++ code example can be used to implement the utility methods and functions used in the previous code example.

HWND CreateADsNotificationObject(IDataObject *pDataObject)
{
    STGMEDIUM   stm;
    FORMATETC   fe;
    HRESULT     hr;
    HWND        hwndNotifyObject = NULL;

    /*
    The "DsAdminMultiSelectClipFormat" clipboard format is supported by the 
    data object if the Active Directory Users and Computers snap-in supports 
    multi-selection property sheets.
    */
    fe.cfFormat = RegisterClipboardFormat(TEXT("DsAdminMultiSelectClipFormat"));
    fe.ptd = NULL;
    fe.dwAspect = DVASPECT_CONTENT;
    fe.lindex = -1;
    fe.tymed = TYMED_HGLOBAL;
    hr = pDataObject->GetData(&fe, &stm);
    if (SUCCEEDED(hr))
    {
        PWSTR pwzUniqueID = (LPWSTR)GlobalLock(stm.hGlobal);

        if (pwzUniqueID)
        {
            hr = ADsPropCreateNotifyObj(pDataObject, pwzUniqueID, &hwndNotifyObject);

            GlobalUnlock(stm.hGlobal);
        }
            
        ReleaseStgMedium(&stm);
    }
    else
    {
        fe.cfFormat = RegisterClipboardFormat(CFSTR_DSOBJECTNAMES);
        fe.ptd = NULL;
        fe.dwAspect = DVASPECT_CONTENT;
        fe.lindex = -1;
        fe.tymed = TYMED_HGLOBAL;
        hr = pDataObject->GetData(&fe, &stm);
        if(SUCCEEDED(hr))
        {
            LPDSOBJECTNAMES pdson = (LPDSOBJECTNAMES)GlobalLock(stm.hGlobal);

            if(pdson)
            {
                LPWSTR  pwszName = (LPWSTR)((LPBYTE)pdson + pdson->aObjects[0].offsetName);
                
                hr = ADsPropCreateNotifyObj(pDataObject, pwszName, &hwndNotifyObject);

                GlobalUnlock(stm.hGlobal);
            }
            
            ReleaseStgMedium(&stm);
        }
    }

    return hwndNotifyObject;
}

/**************************************************************************

    GetADsPageInfo()
   
**************************************************************************/

HRESULT GetADsPageInfo(HWND hwndNotifyObject, ADSPROPINITPARAMS *pip)
{
    if(!IsWindow(hwndNotifyObject))
    {
        return E_INVALIDARG;
    }

    ADSPROPINITPARAMS   InitParams;
    
    InitParams.dwSize = sizeof(ADSPROPINITPARAMS);
    if(ADsPropGetInitInfo(hwndNotifyObject, &InitParams))
    {
        *pip = InitParams;
    
        return InitParams.hr;
    }
    
    return E_FAIL;
}

/**************************************************************************

    CPropSheetExt::PageDlgProc

**************************************************************************/

#define THIS_POINTER_PROP  TEXT("ThisPointerProperty")

BOOL CALLBACK CPropSheetExt::PageDlgProc(   HWND hWnd, 
                                            UINT uMsg, 
                                            WPARAM wParam, 
                                            LPARAM lParam)
{
    // Get the object pointer.
    CPropSheetExt *pExt = (CPropSheetExt*)GetProp(hWnd, THIS_POINTER_PROP);
    
    switch(uMsg)
    {
    case WM_INITDIALOG:
        {
            /*
            Get the pointer to the object. This is contained in the LPARAM of 
            the PROPSHEETPAGE structure.
            */
            LPPROPSHEETPAGE pPage = (LPPROPSHEETPAGE)lParam;

            if(pPage)
            {
                pExt = (CPropSheetExt*)pPage->lParam;

                if(pExt)
                {
                    // Set the page window handle in the object.
                    pExt->m_hwndPage = hWnd;
                    
                    // Store the object pointer with this particular page dialog.
                    SetProp(hWnd, THIS_POINTER_PROP, (HANDLE)pExt);

                    ADsPropSetHwnd(pExt->m_hwndNotifyObj, hWnd, g_szPageTitle);

                    // Forward the message to the message handler.
                    return pExt->OnInitDialog(wParam, lParam);
                }
            }
        }
        break;

    case WM_NOTIFY:
        if(pExt)
        {
            // Forward the message to the message handler.
            return pExt->OnNotify(wParam, lParam);
        }
        break;
    
    case WM_COMMAND:
        if(pExt)
        {
            // Forward the message to the message handler.
            return pExt->OnCommand(wParam, lParam);
        }
        return FALSE;
    
    case WM_DESTROY:
        if(pExt)
        {
            // Forward the message to the message handler.
            pExt->OnDestroy();
        }
        
        // Remove the property from the page.
        RemoveProp(hWnd, THIS_POINTER_PROP);
        break; 
    }

    return FALSE;
}


/**************************************************************************

    CPropSheetExt::PageCallbackProc()

    This function is called when the page is created and released. This is 
    even called if the page is never actually displayed. 

**************************************************************************/

UINT CALLBACK CPropSheetExt::PageCallbackProc(  HWND hWnd,
                                                UINT uMsg,
                                                LPPROPSHEETPAGE ppsp)
{
    switch(uMsg)
    {
    case PSPCB_CREATE:
        // Must return TRUE to create the page.
        return TRUE;

    case PSPCB_RELEASE:
        {
            /*
            Release the object. This is called even if the page dialog was 
            never actually created.
            */
            CPropSheetExt *pPropSheetExt = (CPropSheetExt*)ppsp->lParam;

            if(pPropSheetExt)
            {
                if(IsWindow(pPropSheetExt->m_hwndNotifyObj))
                {
                    SendMessage(pPropSheetExt->m_hwndNotifyObj, 
                        WM_ADSPROP_NOTIFY_EXIT, 
                        0, 
                        0);

                    pPropSheetExt->m_hwndNotifyObj = NULL;
                }
                pPropSheetExt->Release();
            }
        }
        break;
    }

    return FALSE;
}