PROPPAGE.CPP
//=--------------------------------------------------------------------------= 
// PropPage.Cpp 
//=--------------------------------------------------------------------------= 
// Copyright 1995 - 1998 Microsoft Corporation.  All Rights Reserved. 
// 
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF  
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO  
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A  
// PARTICULAR PURPOSE. 
//=--------------------------------------------------------------------------= 
// 
// implementation of CPropertyPage object. 
// 
#include "IPServer.H" 
#include "PropPage.H" 
#include "Util.H" 
#include "Globals.H" 
 
// for ASSERT and FAIL 
// 
SZTHISFILE 
 
// this variable is used to pass the pointer to the object to the hwnd. 
// 
static CPropertyPage *s_pLastPageCreated; 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::CPropertyPage 
//=--------------------------------------------------------------------------= 
// constructor. 
// 
// Parameters: 
//    IUnknown *          - [in] controlling unknown 
//    int                 - [in] object type. 
// 
// Notes: 
// 
#pragma warning(disable:4355)  // using 'this' in constructor 
CPropertyPage::CPropertyPage 
( 
    IUnknown         *pUnkOuter, 
    int               iObjectType 
) 
: CUnknownObject(pUnkOuter, this), m_ObjectType(iObjectType) 
{ 
    // initialize various dudes. 
    // 
    m_pPropertyPageSite = NULL; 
    m_hwnd = NULL; 
    m_fDirty = FALSE; 
    m_fActivated = FALSE; 
    m_cObjects = 0; 
} 
#pragma warning(default:4355)  // using 'this' in constructor 
 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::~CPropertyPage 
//=--------------------------------------------------------------------------= 
// destructor. 
// 
// Notes: 
// 
CPropertyPage::~CPropertyPage() 
{ 
    // clean up our window. 
    // 
    if (m_hwnd) { 
        SetWindowLong(m_hwnd, GWL_USERDATA, 0xffffffff); 
        DestroyWindow(m_hwnd); 
    } 
 
    // release all the objects we're holding on to. 
    // 
    m_ReleaseAllObjects(); 
 
    // release the site 
    // 
    QUICK_RELEASE(m_pPropertyPageSite); 
} 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::InternalQueryInterface 
//=--------------------------------------------------------------------------= 
// we support IPP and IPP2. 
// 
// Parameters: 
//    REFIID        - [in]  interface they want 
//    void **       - [out] where they want to put the resulting object ptr. 
// 
// Output: 
//    HRESULT       - S_OK, E_NOINTERFACE 
// 
// Notes: 
// 
HRESULT CPropertyPage::InternalQueryInterface 
( 
    REFIID  riid, 
    void  **ppvObjOut 
) 
{ 
    IUnknown *pUnk; 
 
    *ppvObjOut = NULL; 
 
    if (DO_GUIDS_MATCH(IID_IPropertyPage, riid)) { 
        pUnk = (IUnknown *)this; 
    } else if (DO_GUIDS_MATCH(IID_IPropertyPage2, riid)) { 
        pUnk = (IUnknown *)this; 
    } else { 
        return CUnknownObject::InternalQueryInterface(riid, ppvObjOut); 
    } 
 
    pUnk->AddRef(); 
    *ppvObjOut = (void *)pUnk; 
    return S_OK; 
} 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::SetPageSite    [IPropertyPage] 
//=--------------------------------------------------------------------------= 
// the initialization function for a property page through which the page 
// receives an IPropertyPageSite pointer. 
// 
// Parameters: 
//    IPropertyPageSite *        - [in] new site. 
// 
// Output: 
//    HRESULT 
// 
// Notes; 
// 
STDMETHODIMP CPropertyPage::SetPageSite 
( 
    IPropertyPageSite *pPropertyPageSite 
) 
{ 
    RELEASE_OBJECT(m_pPropertyPageSite); 
    m_pPropertyPageSite = pPropertyPageSite; 
    ADDREF_OBJECT(pPropertyPageSite); 
 
    return S_OK; 
} 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::Activate    [IPropertyPage] 
//=--------------------------------------------------------------------------= 
// instructs the page to create it's display window as a child of hwndparent 
// and to position it according to prc. 
// 
// Parameters: 
//    HWND                - [in]  parent window 
//    LPCRECT             - [in]  where to position ourselves 
//    BOOL                - [in]  whether we're modal or not. 
// 
// Output: 
//    HRESULT 
// 
// Notes: 
// 
STDMETHODIMP CPropertyPage::Activate 
( 
    HWND    hwndParent, 
    LPCRECT prcBounds, 
    BOOL    fModal 
) 
{ 
    HRESULT hr; 
 
    // first make sure the dialog window is loaded and created. 
    // 
    hr = m_EnsureLoaded(); 
    RETURN_ON_FAILURE(hr); 
 
    // set our parent window if we haven't done so yet. 
    // 
    if (!m_fActivated) { 
        SetParent(m_hwnd, hwndParent); 
        m_fActivated = TRUE; 
    } 
 
    // now move ourselves to where we're told to be and show ourselves 
    // 
    Move(prcBounds); 
    ShowWindow(m_hwnd, SW_SHOW); 
 
    return S_OK; 
} 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::Deactivate    [IPropertyPage] 
//=--------------------------------------------------------------------------= 
// instructs the page to destroy the window created in activate 
// 
// Output: 
//    HRESULT 
// 
// Notes: 
// 
STDMETHODIMP CPropertyPage::Deactivate 
( 
    void 
) 
{ 
    // blow away yon window. 
    // 
    if (m_hwnd) 
        DestroyWindow(m_hwnd); 
    m_hwnd = NULL; 
    m_fActivated = FALSE; 
 
    return S_OK; 
} 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::GetPageInfo    [IPropertyPage] 
//=--------------------------------------------------------------------------= 
// asks the page to fill a PROPPAGEINFO structure 
// 
// Parameters: 
//    PROPPAGEINFO *    - [out] where to put info. 
// 
// Output: 
//    HRESULT 
// 
// Notes: 
// 
STDMETHODIMP CPropertyPage::GetPageInfo 
( 
    PROPPAGEINFO *pPropPageInfo 
) 
{ 
    RECT rect; 
 
    CHECK_POINTER(pPropPageInfo); 
 
    m_EnsureLoaded(); 
 
    // clear it out first. 
    // 
    memset(pPropPageInfo, 0, sizeof(PROPPAGEINFO)); 
 
    pPropPageInfo->pszTitle = OLESTRFROMRESID(TITLEIDOFPROPPAGE(m_ObjectType)); 
    pPropPageInfo->pszDocString = OLESTRFROMRESID(DOCSTRINGIDOFPROPPAGE(m_ObjectType)); 
    pPropPageInfo->pszHelpFile = OLESTRFROMANSI(HELPFILEOFPROPPAGE(m_ObjectType)); 
    pPropPageInfo->dwHelpContext = HELPCONTEXTOFPROPPAGE(m_ObjectType); 
 
    if (!(pPropPageInfo->pszTitle && pPropPageInfo->pszDocString && pPropPageInfo->pszHelpFile)) 
        goto CleanUp; 
 
    // if we've got a window yet, go and set up the size information they want. 
    // 
    if (m_hwnd) { 
        GetWindowRect(m_hwnd, &rect); 
 
        pPropPageInfo->size.cx = rect.right - rect.left; 
        pPropPageInfo->size.cy = rect.bottom - rect.top; 
    } 
 
    return S_OK; 
 
  CleanUp: 
    if (pPropPageInfo->pszDocString) CoTaskMemFree(pPropPageInfo->pszDocString); 
    if (pPropPageInfo->pszHelpFile) CoTaskMemFree(pPropPageInfo->pszHelpFile); 
    if (pPropPageInfo->pszTitle) CoTaskMemFree(pPropPageInfo->pszTitle); 
 
    return E_OUTOFMEMORY; 
} 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::SetObjects    [IPropertyPage] 
//=--------------------------------------------------------------------------= 
// provides the page with the objects being affected by the changes. 
// 
// Parameters: 
//    ULONG            - [in] count of objects. 
//    IUnknown **      - [in] objects. 
// 
// Output: 
//    HRESULT 
// 
// Notes: 
// 
STDMETHODIMP CPropertyPage::SetObjects 
( 
    ULONG      cObjects, 
    IUnknown **ppUnkObjects 
) 
{ 
    HRESULT hr; 
    ULONG   x; 
 
    // free up all the old objects first. 
    // 
    m_ReleaseAllObjects(); 
 
    if (!cObjects) 
        return S_OK; 
 
    // now go and set up the new ones. 
    // 
    m_ppUnkObjects = (IUnknown **)HeapAlloc(g_hHeap, 0, cObjects * sizeof(IUnknown *)); 
    RETURN_ON_NULLALLOC(m_ppUnkObjects); 
 
    // loop through and copy over all the objects. 
    // 
    for (x = 0; x < cObjects; x++) { 
        m_ppUnkObjects[x] = ppUnkObjects[x]; 
        ADDREF_OBJECT(m_ppUnkObjects[x]); 
    } 
 
    // go and tell the object that there are new objects 
    // 
    hr = S_OK; 
    m_cObjects = cObjects; 
    // if we've got a window, go and notify it that we've got new objects. 
    // 
    if (m_hwnd) 
        SendMessage(m_hwnd, PPM_NEWOBJECTS, 0, (LPARAM)&hr); 
    if (SUCCEEDED(hr)) m_fDirty = FALSE; 
 
    return hr; 
} 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::Show    [IPropertyPage] 
//=--------------------------------------------------------------------------= 
// asks the page to show or hide its window 
// 
// Parameters: 
//    UINT             - [in] whether to show or hide 
// 
// Output: 
//    HRESULT 
// 
// Notes: 
// 
STDMETHODIMP CPropertyPage::Show 
( 
    UINT nCmdShow 
) 
{ 
    if (m_hwnd) 
        ShowWindow(m_hwnd, nCmdShow); 
    else 
        return E_UNEXPECTED; 
 
    return S_OK; 
} 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::Move    [IPropertyPage] 
//=--------------------------------------------------------------------------= 
// asks the page to relocate and resize itself to a position other than what 
// was specified through Activate 
// 
// Parameters: 
//    LPCRECT        - [in] new position and size 
// 
// Output: 
//    HRESULT 
// 
// Notes: 
// 
STDMETHODIMP CPropertyPage::Move 
( 
    LPCRECT prcBounds 
) 
{ 
    // do what they sez 
    // 
    if (m_hwnd) 
        SetWindowPos(m_hwnd, NULL, prcBounds->left, prcBounds->top, 
                     prcBounds->right - prcBounds->left, 
                     prcBounds->bottom - prcBounds->top, 
                     SWP_NOZORDER); 
    else 
        return E_UNEXPECTED; 
 
    return S_OK; 
} 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::IsPageDirty    [IPropertyPage] 
//=--------------------------------------------------------------------------= 
// asks the page whether it has changed its state 
// 
// Output 
//    S_OK            - yep 
//    S_FALSE         - nope 
// 
// Notes: 
// 
STDMETHODIMP CPropertyPage::IsPageDirty 
( 
    void 
) 
{ 
    return m_fDirty ? S_OK : S_FALSE; 
} 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::Apply    [IPropertyPage] 
//=--------------------------------------------------------------------------= 
// instructs the page to send its changes to all the objects passed through 
// SetObjects() 
// 
// Output: 
//    HRESULT 
// 
// Notes: 
// 
STDMETHODIMP CPropertyPage::Apply 
( 
    void 
) 
{ 
    HRESULT hr = S_OK; 
 
    if (m_hwnd) { 
        SendMessage(m_hwnd, PPM_APPLY, 0, (LPARAM)&hr); 
        RETURN_ON_FAILURE(hr); 
 
        if (m_fDirty) { 
            m_fDirty = FALSE; 
            if (m_pPropertyPageSite) 
                m_pPropertyPageSite->OnStatusChange(PROPPAGESTATUS_DIRTY); 
        } 
    } else 
        return E_UNEXPECTED; 
 
    return S_OK; 
} 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::Help    [IPropertyPage] 
//=--------------------------------------------------------------------------= 
// instructs the page that the help button was clicked. 
// 
// Parameters: 
//    LPCOLESTR        - [in] help directory 
// 
// Output: 
//    HRESULT 
// 
// Notes: 
// 
STDMETHODIMP CPropertyPage::Help 
( 
    LPCOLESTR pszHelpDir 
) 
{ 
    BOOL f; 
 
    ASSERT(m_hwnd, "How can somebody have clicked Help, but we don't have an hwnd?"); 
 
    // oblige them and show the help. 
    // 
    MAKE_ANSIPTR_FROMWIDE(psz, pszHelpDir); 
    f = WinHelp(m_hwnd, psz, HELP_CONTEXT, HELPCONTEXTOFPROPPAGE(m_ObjectType)); 
 
    return f ? S_OK : E_FAIL; 
} 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::TranslateAccelerator    [IPropertyPage] 
//=--------------------------------------------------------------------------= 
// informs the page of keyboard events, allowing it to implement it's own 
// keyboard interface. 
// 
// Parameters: 
//    LPMSG            - [in] message that triggered this 
// 
// Output: 
//    HRESULT 
// 
// Notes: 
// 
STDMETHODIMP CPropertyPage::TranslateAccelerator 
( 
    LPMSG pmsg 
) 
{ 
    ASSERT(m_hwnd, "How can we get a TranslateAccelerator call if we're not visible?"); 
 
    // just pass this message on to the dialog proc and see if they want it. 
    // 
    return IsDialogMessage(m_hwnd, pmsg) ? S_OK : S_FALSE; 
} 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::EditProperty    [IPropertyPage2] 
//=--------------------------------------------------------------------------= 
// instructs the page to set the focus to the property matching the dispid. 
// 
// Parameters: 
//    DISPID            - [in] dispid of property to set focus to. 
// 
// Output: 
//    HRESULT 
// 
// Notes: 
// 
STDMETHODIMP CPropertyPage::EditProperty 
( 
    DISPID dispid 
) 
{ 
    HRESULT hr = E_NOTIMPL; 
 
    // send the message on to the control, and see what they want to do with it. 
    // 
    SendMessage(m_hwnd, PPM_EDITPROPERTY, (WPARAM)dispid, (LPARAM)&hr); 
 
    return hr; 
} 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::m_EnsureLoaded 
//=--------------------------------------------------------------------------= 
// makes sure the dialog is actually loaded 
// 
// Output: 
//    HRESULT 
// 
// Notes: 
// 
HRESULT CPropertyPage::m_EnsureLoaded 
( 
    void 
) 
{ 
    HRESULT hr = S_OK; 
 
    // duh 
    // 
    if (m_hwnd) 
        return S_OK; 
 
    // set up the global variable so that when we're in the dialog proc, we can 
    // stuff this in the hwnd 
    // 
    // crit sect this whole creation process for apartment threading support. 
    // 
    EnterCriticalSection(&g_CriticalSection); 
    s_pLastPageCreated = this; 
 
    // create the dialog window 
    // 
    CreateDialog(GetResourceHandle(), TEMPLATENAMEOFPROPPAGE(m_ObjectType), GetParkingWindow(), 
                          (DLGPROC)CPropertyPage::PropPageDlgProc); 
    ASSERT(m_hwnd, "Couldn't load Dialog Resource!!!"); 
    if (!m_hwnd) { 
        LeaveCriticalSection(&g_CriticalSection); 
        return HRESULT_FROM_WIN32(GetLastError()); 
    } 
 
    // clean up variables and leave the critical section 
    // 
    s_pLastPageCreated = NULL; 
    LeaveCriticalSection(&g_CriticalSection); 
 
    // go and notify the window that it should pick up any objects that are 
    // available 
    // 
    SendMessage(m_hwnd, PPM_NEWOBJECTS, 0, (LPARAM)&hr); 
 
    return hr; 
} 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::m_ReleaseAllObjects 
//=--------------------------------------------------------------------------= 
// releases all the objects that we're working with 
// 
// Notes: 
// 
void CPropertyPage::m_ReleaseAllObjects 
( 
    void 
) 
{ 
    HRESULT hr; 
    UINT x; 
 
    if (!m_cObjects) 
        return; 
 
    // some people will want to stash pointers in the PPM_INITOBJECTS case, so 
    // we want to tell them to release them now. 
    // 
    SendMessage(m_hwnd, PPM_FREEOBJECTS, 0, (LPARAM)&hr); 
 
    // loop through and blow them all away. 
    // 
    for (x = 0; x < m_cObjects; x++) 
        QUICK_RELEASE(m_ppUnkObjects[x]); 
 
    HeapFree(g_hHeap, 0, m_ppUnkObjects); 
    m_ppUnkObjects = NULL; 
} 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::PropPageDlgProc 
//=--------------------------------------------------------------------------= 
// static global helper dialog proc that gets called before we pass the message 
// on to anybody .. 
// 
// Parameters: 
//    - see win32sdk docs on DialogProc 
// 
// Notes: 
// 
BOOL CALLBACK CPropertyPage::PropPageDlgProc 
( 
    HWND    hwnd, 
    UINT    msg, 
    WPARAM  wParam, 
    LPARAM  lParam 
) 
{ 
    CPropertyPage *pPropertyPage; 
 
    // get the window long, and see if it's been set to the object this hwnd 
    // is operating against.  if not, go and set it now. 
    // 
    pPropertyPage = (CPropertyPage *)GetWindowLong(hwnd, GWL_USERDATA); 
    if ((ULONG)pPropertyPage == 0xffffffff) 
        return FALSE; 
    if (!pPropertyPage) { 
        SetWindowLong(hwnd, GWL_USERDATA, (LONG)s_pLastPageCreated); 
        pPropertyPage = s_pLastPageCreated; 
        pPropertyPage->m_hwnd = hwnd; 
    } 
 
    ASSERT(pPropertyPage, "Uh oh.  Got a window, but no CpropertyPage for it!"); 
 
    // just call the user dialog proc and see if they want to do anything. 
    // 
    return pPropertyPage->DialogProc(hwnd, msg, wParam, lParam); 
} 
 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::FirstControl 
//=--------------------------------------------------------------------------= 
// returns the first controlish object that we are showing ourselves for. 
// returns a cookie that must be passed in for Next ... 
// 
// Parameters: 
//    DWORD *    - [out] cookie to be used for Next 
// 
// Output: 
//    IUnknown * 
// 
// Notes: 
// 
IUnknown *CPropertyPage::FirstControl 
( 
    DWORD *pdwCookie 
) 
{ 
    // just use the implementation of NEXT. 
    // 
    *pdwCookie = 0; 
    return NextControl(pdwCookie); 
} 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::NextControl 
//=--------------------------------------------------------------------------= 
// returns the next control in the chain of people to work with given a cookie 
// 
// Parameters: 
//    DWORD *            - [in/out] cookie to get next from, and new cookie. 
// 
// Output: 
//    IUnknown * 
// 
// Notes: 
// 
IUnknown *CPropertyPage::NextControl 
( 
    DWORD *pdwCookie 
) 
{ 
    UINT      i; 
 
    // go looking through all the objects that we've got, and find the 
    // first non-null one. 
    // 
    for (i = *pdwCookie; i < m_cObjects; i++) { 
        if (!m_ppUnkObjects[i]) continue; 
 
        *pdwCookie = i + 1;                // + 1 so we start at next item next time 
        return m_ppUnkObjects[i]; 
    } 
 
    // couldn't find it . 
    // 
    *pdwCookie = 0xffffffff; 
    return NULL; 
} 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::MakeDirty    [helper, callable] 
//=--------------------------------------------------------------------------= 
// marks a page as dirty. 
// 
// Notes: 
// 
void CPropertyPage::MakeDirty 
( 
    void 
) 
{ 
    m_fDirty = TRUE; 
    if (m_pPropertyPageSite) 
        m_pPropertyPageSite->OnStatusChange(PROPPAGESTATUS_DIRTY|PROPPAGESTATUS_VALIDATE); 
} 
 
 
// from Globals.C 
// 
extern HINSTANCE g_hInstResources; 
 
 
//=--------------------------------------------------------------------------= 
// CPropertyPage::GetResourceHandle    [helper, callable] 
//=--------------------------------------------------------------------------= 
// returns current resource handle, based on pagesites ambient LCID. 
// 
// Output: 
//    HINSTANCE 
// 
// Notes: 
// 
HINSTANCE CPropertyPage::GetResourceHandle 
( 
    void 
) 
{ 
    if (!g_fSatelliteLocalization) 
        return g_hInstance; 
 
    // if we've already got it, then there's not all that much to do. 
    // don't need to crit sect this one right here since even if they do fall 
    // into the ::GetResourceHandle call, it'll properly deal with things. 
    // 
    if (g_hInstResources) 
        return g_hInstResources; 
 
    // we'll get the ambient localeid from the host, and pass that on to the 
    // automation object. 
    // 
    // enter a critical section for g_lcidLocale and g_fHavelocale 
    // 
    EnterCriticalSection(&g_CriticalSection); 
    if (!g_fHaveLocale) { 
        if (m_pPropertyPageSite) { 
            m_pPropertyPageSite->GetLocaleID(&g_lcidLocale); 
            g_fHaveLocale = TRUE; 
        } 
    } 
    LeaveCriticalSection(&g_CriticalSection); 
 
    return ::GetResourceHandle(); 
}