/////////////////////////////////////////////////////////////////////////////
// PropertyPageSite.cpp | Implementation of the TCPropertyPageSite class.
//

#include "pch.h"
#include "..\Inc\TCLib.h"
#include "PropertyPageSite.h"
#include "AutoHandle.h"


/////////////////////////////////////////////////////////////////////////////
// TCPropertyPageSite


/////////////////////////////////////////////////////////////////////////////
// Group=Construction / Destruction

/////////////////////////////////////////////////////////////////////////////
// Parameters:
//   clsidPage - The CLSID of a property page to be created and managed by
// this object. Calls Page_Create to create the object.
//   pPage - An IPropertyPage* of an existing property page component object
// to be managed by this object. Calls Page_Attach to attach to the page.
//
// See Also: TCPropertyPageSite::Page_Create, TCPropertyPageSite::Page_Attach
TCPropertyPageSite::TCPropertyPageSite()
{
  CommonConstruct();
}

TCPropertyPageSite::TCPropertyPageSite(REFCLSID clsidPage)
{
  CommonConstruct();
  HRESULT hr = Page_Create(clsidPage);
  ZSucceeded(hr);
}

TCPropertyPageSite::TCPropertyPageSite(IPropertyPage* pPage)
{
  CommonConstruct();
  HRESULT hr = Page_Attach(pPage);
  ZSucceeded(hr);
}

TCPropertyPageSite::~TCPropertyPageSite()
{
  CommonDestruct();
}

/////////////////////////////////////////////////////////////////////////////
// Description: Creates and attaches to a property page component object.
//
// Attempts to create an instance of the property page component object
// specified by the /clsidPage/ CLSID. If successful, this method delegates
// to the Page_Attach method and saves the page's CLSID.
//
// Parameters:
//   clsidPage - The CLSID of the property page to be created and managed by
// this object.
//
// Return Value: S_OK if successful, otherwise a standard COM error code.
//
// See Also: TCPropertyPageSite::Page_Attach, TCPropertyPageSite::GetClassID
HRESULT TCPropertyPageSite::Page_Create(REFCLSID clsidPage)
{
  // Attempt to create the specified property page
  IPropertyPagePtr pPage;
  HRESULT hr = pPage.CreateInstance(clsidPage);
  if (FAILED(hr))
    return hr;

  // Delegate to the Attach method
  if (FAILED(hr = Page_Attach(pPage)))
    return hr;

  // Save the page's CLSID
  m_clsid = clsidPage;

  // Indicate success
  return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// Description: Attaches an existing IPropertyPage interface.
//
// Manages the specified IPropertyPage interface and sets m_xPageSite as the
// page's site by calling IPropertyPage::SetPageSite. It then retrieves and
// stores the page's general informatioin by calling the
// IPropertyPage::GetPageInfo method and informs the page, by calling the 
// IPropertyPage::SetObjects method, of the array of objects which it will
// be editing. If the site window is visible, the Page_Show method is called
// to activate and display the property page.
//
// This method is used internally (by Page_Create) and does not need to be
// called directly. It is public, however, to allow a more advanced usage.
//
// When this method is called directly (not from Page_Create), the CLSID of
// the property page cannot be determined, so GetClassID will return
// CLSID_NULL.
//
// Parameters:
//   pPage - An IPropertyPage* of an existing property page component object
// to be managed by this object.
//
// Return Value: S_OK if successful, otherwise the result of the failed
// interface method.
//
// See Also: TCPropertyPageSite::constructor,
// TCPropertyPageSite::Page_Create, TCPropertyPageSite::Page_Show,
// TCPropertyPageSite::OnGetWindow, TCPropertyPageSite::GetTitle,
// TCPropertyPageSite::GetSize, TCPropertyPageSite::GetDocString,
// TCPropertyPageSite::GetHelpFile, TCPropertyPageSite::GetHelpContext,
// TCPropertyPageSite::SetObjects
HRESULT TCPropertyPageSite::Page_Attach(IPropertyPage* pPage)
{
  // Ensure that no previous page is attached
  assert(NULL == m_pPage);

  // Set ourself as the page site for the page
  HRESULT hr = pPage->SetPageSite(&m_xPageSite);
  if (FAILED(hr))
    return hr;

  // Get the page information
  hr = pPage->GetPageInfo(&m_info);
  if (FAILED(hr))
    return hr;

  // Set the objects of the page
  if (m_vecObjects.size())
  {
    std::vector<IUnknown*> vecUnk;
    vecUnk.resize(m_vecObjects.size());
    std::copy(m_vecObjects.begin(), m_vecObjects.end(), vecUnk.begin());
// VS.Net 2003 port: see "Breaking Changes in the Standard C++ Library Since Visual C++ 6.0" in documentation
#if _MSC_VER >= 1310
    hr = pPage->SetObjects(vecUnk.size(), &(*(vecUnk.begin())));
#else
    hr = pPage->SetObjects(vecUnk.size(), vecUnk.begin());
#endif
    if (FAILED(hr))
      return hr;
  }

  // Save the page pointer
  m_pPage = pPage;

  // Show the page if the page site window is visible
  HWND hwnd = OnGetWindow();
  if (NULL != hwnd && ::IsWindowVisible(hwnd))
    hr = Page_Show();

  // Indicate success
  return hr;
}

/////////////////////////////////////////////////////////////////////////////
// Description: Detaches the managed property page.
//
// Detaches the managed property page by releasing the strings in m_info,
// deactivating the property page, if active, and detaching the IPropertyPage
// interface pointer.
//
// TODO: Should this also set the managed property page's site to NULL?
//
// Return Value: The IPropertyPage interface pointer that was detached from
// this site object.
//
// See Also: TCPropertyPageSite::constructor, TCPropertyPageSite::destructor,
// TCPropertyPageSite::Page_Create, TCPropertyPageSite::Page_Attach,
// TCPropertyPageSite::m_info
IPropertyPage* TCPropertyPageSite::Page_Detach()
{
  __try
  {
    // Release the PROPPAGEINFO structure
    CoTaskMemFree(m_info.pszTitle);
    CoTaskMemFree(m_info.pszDocString);
    CoTaskMemFree(m_info.pszHelpFile);
    ZeroMemory(&m_info, sizeof(m_info));
    m_info.cb = sizeof(m_info);

    // Deactivate the page, if it is activated
    if (m_bActivated && NULL != m_pPage)
      m_pPage->Deactivate();
    m_bActivated = false;

    // Clear the CLSID of the page
    m_clsid = CLSID_NULL;

    // Detach the pointer
    return m_pPage.Detach();
  }
  __except(1)
  {
    _TRACE0("TCPropertyPageSite::Detach(): Exception Caught!\n");
    return NULL;
  }
}

/////////////////////////////////////////////////////////////////////////////
// Description: Common construction processing.
//
// Performs common construction processing.
void TCPropertyPageSite::CommonConstruct()
{
  m_xPageSite.m_pThis = this;
  m_clsid = CLSID_NULL;
  ZeroMemory(&m_info, sizeof(m_info));
  m_info.cb = sizeof(m_info);
  m_bActivated = false;
  m_bDirty = false;
}

/////////////////////////////////////////////////////////////////////////////
// Description: Common destruction processing.
//
// Destructs the object by calling Page_Detach and releasing the detached
// pointer.
//
// See Also: TCPropertyPageSite::destructor, TCPropertyPageSite::Page_Detach
void TCPropertyPageSite::CommonDestruct()
{
  __try
  {
    IPropertyPage* pPage = Page_Detach();
    if (NULL != pPage)
      pPage->Release();
  }
  __except(1)
  {
    _TRACE0("TCPropertyPageSite::CommonDestruct(): Exception Caught!\n");
  }
}


/////////////////////////////////////////////////////////////////////////////
// Group=Attributes

/////////////////////////////////////////////////////////////////////////////
// Description: Provides the IUnknown pointers of the objects affected by
// the managed property page.
//
// Provides the IUnknown pointers of the objects affected by the managed
// property page. The pointers are copied to the m_vecObjects data member. If
// a property page is attached, the IPropertyPage::SetObjects method of the
// page is called.
//
// Parameters:
//   cObjects - [in] Number of *IUnknown* pointers in the array pointed to by
//   /ppUnk/.
//   ppUnk - [in] Pointer to an array of *IUnknown* interface pointers where
//   each pointer identifies a unique object affected by the managed property
//   page.
//
// Return Value: S_OK if successful, otherwise the return code from the
// property page's interface method.
HRESULT TCPropertyPageSite::SetObjects(ULONG cObjects, IUnknown** ppUnk)
{
  // Release any previous vector of objects
  m_vecObjects.clear();

  // Copy the specified array to the vector of objects
  m_vecObjects.resize(cObjects);
  std::copy(ppUnk, ppUnk + cObjects, m_vecObjects.begin());

  // Set the objects of the page, if the page exists
  if (NULL != m_pPage)
    return m_pPage->SetObjects(cObjects, ppUnk);

  // Indicate success
  return S_OK;
}


/////////////////////////////////////////////////////////////////////////////
// Group=Operations

/////////////////////////////////////////////////////////////////////////////
// Description: Message processing method to be called by the derived class.
//
// Handles the WM_CREATE, WM_SIZE, and WM_DESTROY window messages to affect
// the managed property page appropriately.
//
// A derived class must call this somewhere in it's message handling logic.
// For an MFC CWnd-derived class, the OnWndMsg virtual function is and ideal
// place for this:
//
//      BOOL TCActiveXPropertyPage::OnWndMsg(UINT message, WPARAM wParam,
//        LPARAM lParam, LRESULT* plResult)
//      {
//        // Allow the TCPropertyPageSite class a crack at the message
//        if (OnPageSiteWndMsg(message, wParam, lParam, plResult))
//          return TRUE;
//      …
//        // Perform default processing
//        return CPropertyPage::OnWndMsg(message, wParam, lParam, plResult);
//      }
//
// Return Value: TRUE if message processing should cease; otherwise FALSE.
//
// Parameters:
//   message - [in] Specifies the message being processed.
//   wParam - [in] Specifies additional message-dependent information.
//   lParam - [in] Specifies additional message-dependent information.
//   plResult - [out] The return value of the message handling.
BOOL TCPropertyPageSite::OnPageSiteWndMsg(UINT message, WPARAM wParam,
  LPARAM lParam, LRESULT* plResult)
{
  // Handle certain messages
  switch (message)
  {
    case WM_CREATE:
    {
      // Ensure that the page is activated
      if (!m_bActivated && FAILED(Activate()))
      {
        *plResult = LRESULT(-1);
        return TRUE;
      }
      break;
    }
    case WM_SIZE:
    {
      // Size the property page to match
      if (NULL != m_pPage)
      {
        RECT rect = {0, 0, LOWORD(lParam), HIWORD(lParam)};
        m_pPage->Move(&rect);
      }
      break;
    }
    case WM_DESTROY:
    {
      // Deactivate the page, if activated
      if (m_bActivated)
      {
        m_pPage->Deactivate();
        m_bActivated = false;
      }
      break;
    }
  }

  // Allow processing to continue
  return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// Description: Displays the managed property page.
//
// Displays the managed property page by first activating the page, if not
// already active, and then calling the IPropertyPage::Show method with the
// SW_SHOW parameter.
//
// Return Value: S_OK if successful, otherwise the return code from the
// property page's interface method.
//
// See Also: TCPropertyPageSite::Page_Hide, TCPropertyPageSite::Activate
HRESULT TCPropertyPageSite::Page_Show()
{
  // Activate the page, if not already active
  HRESULT hr;
  if (!m_bActivated && FAILED(hr = Activate()))
    return hr;

  // Show the property page and this window
  hr = m_pPage->Show(SW_SHOW);
  return hr;
}

/////////////////////////////////////////////////////////////////////////////
// Description: Hides the managed property page.
//
// Hides the managed property page by calling the IPropertyPage::Show method
// with the SW_HIDE parameter.
//
// Return Value: S_OK if successful, otherwise the return code from the
// property page's interface method.
//
// See Also: TCPropertyPageSite::Page_Show
HRESULT TCPropertyPageSite::Page_Hide()
{
  // Hide the property page
  return m_pPage->Show(SW_HIDE);
}

/////////////////////////////////////////////////////////////////////////////
// Description: Displays help for the managed property page.
//
// Displays help for the managed property page by first deriving the path
// name for the page's help file. The "HelpDir" and "InProcServer32" registry
// subkeys are used, in that order, to determine where in the file system the
// page's help file might be located. This path is then specified in a call
// to the IPropertyPage::Help method, which allows the page to display it's
// help in some alternate way. If that method call fails, the GetHelpFile and
// GetHelpContext methods are used in a call to the Win32 WinHelp API.
// 
// Return Value: S_OK if successful, otherwise the return code from the
// property page's failed interface method or from a failed registry API.
//
// See Also: TCPropertyPageSite::GetHelpFile,
// TCPropertyPageSite::GetHelpContext, TCRegKeyHandle
HRESULT TCPropertyPageSite::Page_Help()
{
  // Create the registry key name
  OLECHAR szCLSID[_MAX_PATH] = OLESTR("CLSID\\");
  int cchCLSIDKey = wcslen(szCLSID);

  // Convert the stored CLSID to a string representation
  StringFromGUID2(GetClassID(), szCLSID + cchCLSIDKey,
    sizeofArray(szCLSID) - cchCLSIDKey);

  // Open the registry key
  DWORD dw;
  USES_CONVERSION;
  TCRegKeyHandle hkey;
  LRESULT lr = RegCreateKeyEx(HKEY_CLASSES_ROOT, OLE2T(szCLSID), 0, REG_NONE,
    REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, &dw);
  if (ERROR_SUCCESS != lr)
    return lr;

  // Open the registry subkey
  TCHAR szPath[_MAX_PATH];
  TCRegKeyHandle hkeySub;
  lr = RegCreateKeyEx(hkey, TEXT("HelpDir"), 0, REG_NONE,
    REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeySub, &dw);
  if (ERROR_SUCCESS == lr)
  {
    // Read the default subkey value
    DWORD dwType = REG_SZ, cbData = sizeof(szPath);
    lr = RegQueryValueEx(hkeySub, NULL, NULL, &dwType, (PBYTE)szPath,
      &cbData);
  }

  // Try another registry subkey if "HelpDir" failed
  if (ERROR_SUCCESS != lr)
  {
    lr = RegCreateKeyEx(hkey, TEXT("InProcServer32"), 0, REG_NONE,
      REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeySub, &dw);
    if (ERROR_SUCCESS == lr)
    {
      // Read the default subkey value
      DWORD dwType = REG_SZ, cbData = sizeof(szPath);
      if (ERROR_SUCCESS == (lr = RegQueryValueEx(hkeySub, NULL, NULL,
        &dwType, (PBYTE)szPath, &cbData)))
      {
        // Trim down to just the pathname
        TCHAR szDrive[_MAX_DRIVE], szDir[_MAX_DIR];
        _tsplitpath(szPath, szDrive, szDir, NULL, NULL);
        _tmakepath(szPath, szDrive, szDir, NULL, NULL);
      }
    }
  }

  // Return if "InProcServer32" failed
  if (ERROR_SUCCESS != lr)
    return lr;

  // Allow the property page to display it's help
  HRESULT hr = m_pPage->Help(T2COLE(szPath));
  if (SUCCEEDED(hr))
    return hr;

  // Property page failed on call to Help, so use PROPPAGEINFO
  TCHAR szDrive[_MAX_DRIVE], szDir[_MAX_DIR];
  _tsplitpath(szPath, szDrive, szDir, NULL, NULL);
  _tmakepath(szPath, szDrive, szDir, STRTYPE2CT(GetHelpFile()), NULL);
  if (!WinHelp(OnGetWindow(), szPath, HELP_CONTEXTPOPUP, GetHelpContext()))
    return HRESULT_FROM_WIN32(::GetLastError());

  // Indicate success
  return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// Description: Processes keystroke messages.
//
// Simply delegates to the IPropertyPage::TranslateAccelerator method.
//
// Parameters:
//   pMsg - [in] Pointer to the MSG structure describing the keystroke to
// process. 
//
// Return Value: S_OK if successful, otherwise the return code from the
// property page's interface method.
HRESULT TCPropertyPageSite::Page_TranslateAccelerator(MSG* pMsg)
{
  assert(NULL != m_pPage);
  return m_pPage->TranslateAccelerator(pMsg);
}


/////////////////////////////////////////////////////////////////////////////
// Description: Queries the property page for the specified interface.
//
// Delegates to the QueryInterface method of the property page. However, the
// interfaces IPropertyPage and IPropertyPage2 are explicitly never supported
// through this method. This is to help ensure that the dirty flag of the
// property page is always modified through the TCPropertyPageSite class, and
// not directly through the IPropertyPage or IPropertyPage2 interfaces. This
// could obviously be abused if the property page supported some other
// interface that directly modified its dirty flags (e.g. IPropertyPage3).
//
// Parameters:
//   riid - Identifier of the interface being requested.
//   ppvObject - Indirectly points to the interface specified in /iid/. If
// the object does not support the interface specified in iid, *ppvObject
// is set to NULL.
//
// Return Value: S_OK if the interface is supported, E_NOINTERFACE if not.
HRESULT TCPropertyPageSite::Page_QI(REFIID iid, void** ppvObject)
{
  // Check for forbidden interface
  if (IID_IPropertyPage == iid || IID_IPropertyPage2 == iid)
    return E_NOINTERFACE;

  // Otherwise, delegate to the property page
  assert(NULL != m_pPage);
  return m_pPage->QueryInterface(iid, ppvObject);
}


/////////////////////////////////////////////////////////////////////////////
// Group=Overrides

/////////////////////////////////////////////////////////////////////////////
// Description: Override to perform additional processing of the
// IPropertyPageSite::OnStatusChange interface method.
//
// A derived class can override this to perform additional processing when
// the IPropertyPageSite::OnStatusChange interface method is called by the
// managed property page.
//
// Parameters:
//   dwFlags - [in] Flags to indicate what changes have occurred.
//
// Return Value: S_OK to indicate that the status change was noted.
//
// See Also: TCPropertyPageSite::XPageSite::OnStatusChange
HRESULT TCPropertyPageSite::OnStatusChange(DWORD dwFlags)
{
  UNUSED(dwFlags);
  return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// Description: Override to perform additional processing of the
// IPropertyPageSite::GetLocaleID interface method.
//
// A derived class can override this to provide a locale ID to the managed
// property page. The default implementation returns the current thread's
// locale.
//
// Parameters:
//   pLocaleID - [out] Pointer to locale identifier.
//
// Return Value: S_OK to indicate that the locale was returned successfully.
//
// See Also: TCPropertyPageSite::XPageSite::GetLocaleID
HRESULT TCPropertyPageSite::OnGetLocaleID(LCID* pLocaleID)
{
  *pLocaleID = GetThreadLocale();
  return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// Description: Override to perform additional processing of the
// IPropertyPageSite::GetPageContainer interface method.
//
// A derived class can override this to provide an interface pointer of the
// interface pointer to the managed property page. The default
// implementation simply returns E_NOTIMPL.
//
// Parameters:
//   ppUnk - [out] Address of IUnknown* pointer variable that receives the
// interface pointer to the container object.
//
// Return Value: E_NOTIMPL is the only return value allowed at this time. See
// TCPropertyPageSite::XPageSite::GetPageContainer for possible ideas of how
// this could be used.
//
// See Also: TCPropertyPageSite::XPageSite::GetPageContainer
HRESULT TCPropertyPageSite::OnGetPageContainer(IUnknown** ppUnk)
{
  UNUSED(ppUnk);
  return E_NOTIMPL;
}

/////////////////////////////////////////////////////////////////////////////
// Description: Override to perform additional processing of the
// IPropertyPageSite::TranslateAccelerator interface method.
//
// A derived class can override this to process a keystroke message, if it
// desires. The default implementation simply returns E_NOTIMPL.
//
// Parameters:
//   pMsg - [in] Pointer to the MSG structure describing the keystroke to
//   process. 
//
// Return Value:
//   S_OK - The page site processed the message. 
//   S_FALSE - The page site did not process the message. 
//   E_NOTIMPL - The page site does not support keyboard processing. 
//
// See Also: TCPropertyPageSite::XPageSite::TranslateAccelerator
HRESULT TCPropertyPageSite::OnTranslateAccelerator(MSG* pMsg)
{
  UNUSED(pMsg);
  return E_NOTIMPL;
}

/////////////////////////////////////////////////////////////////////////////
// Description: Override to specify if the page site's frame window is modal
// or modeless.
//
// A derived class can override this to specify that it's frame window is
// modeless. The default returns TRUE, indicating that the site's frame
// window is modal. This is used by the Activate method to supply a parameter
// to the IPropertyPage::Activate interface method.
//
// Return Value: TRUE to specify that the site's frame window is modal; FALSE
// to specify that it's modeless.
//
// See Also: TCPropertyPageSite::Activate
BOOL TCPropertyPageSite::OnIsModal()
{
  return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// Description: Override to specify the page site's window.
//
// A derived class should override this to specify it's site window. This
// override is provided so that the class is independent of any particular
// windowing framework, aside from Win32 itself.
//
// Return Value: The page site's window. This window will be used as the
// parent window of the managed property page's window.
//
// TODO: Shouldn't this method be *pure* virtual to enforce a derived class
// to override it?
//
// See Also: TCPropertyPageSite::Activate
HWND TCPropertyPageSite::OnGetWindow()
{
  return NULL;
}


/////////////////////////////////////////////////////////////////////////////
// Group=Implementation

/////////////////////////////////////////////////////////////////////////////
// Description: Activates the managed property page.
//
// Activates the managed property page, if not already active. The
// IPropertyPage::Activate method of the managed property page is used to
// create the property page window in the entire client area of the site's
// window.
//
// Note: Upon activating the property page, the window styles of the page
// site and property page windows are modified to enable Win32's built-in
// child dialog box keyboard handling. After many futile attempt, this proved
// to be much easier and more compatible than attempting to use the
// TranslateAccelerators interface methods.
//
// Return Value: S_OK if successful, otherwise the return code from the
// property page's interface method.
//
// See Also: TCPropertyPageSite::OnGetWindow, TCPropertyPageSite::OnIsModal
HRESULT TCPropertyPageSite::Activate()
{
  __try
  {
    // Do nothing if page is already activated
    if (m_bActivated)
    {
      _TRACE0("TCPropertyPageSite::Activate(): Already activated.\n");
      return S_OK;
    }

    // Get the page site window
    HWND hwnd = OnGetWindow();
    assert(NULL != hwnd);

    // Compute the page's rectangle
    RECT rect;
    ::GetClientRect(hwnd, &rect);

    // Activate the property page
    HRESULT hr = m_pPage->Activate(hwnd, &rect, OnIsModal());
    if (m_bActivated = SUCCEEDED(hr))
    {
      // Modify the style and extended style of this window
      UINT nFlags = SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE |
        SWP_FRAMECHANGED | SWP_NOACTIVATE;
      DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE);
      DWORD dwStyleEx = GetWindowLong(hwnd, GWL_EXSTYLE);
      if (WC_DIALOG == MAKEINTRESOURCE(GetClassLong(hwnd, GCW_ATOM)))
      {
        SetWindowLong(hwnd, GWL_STYLE, dwStyle | DS_CONTROL);
        SetWindowLong(hwnd, GWL_EXSTYLE, dwStyleEx | WS_EX_CONTROLPARENT);
        SetWindowPos(hwnd, NULL, 0, 0, 0, 0, nFlags);
      }

      // Modify the style and extended style of all child dialog windows
      hwnd = ::GetWindow(hwnd, GW_CHILD);
      while (NULL != hwnd)
      {
        if (WC_DIALOG == MAKEINTRESOURCE(GetClassLong(hwnd, GCW_ATOM)))
        {
          dwStyle = GetWindowLong(hwnd, GWL_STYLE);
          SetWindowLong(hwnd, GWL_STYLE, dwStyle | DS_CONTROL);
          dwStyleEx = GetWindowLong(hwnd, GWL_EXSTYLE);
          SetWindowLong(hwnd, GWL_EXSTYLE, dwStyleEx | WS_EX_CONTROLPARENT);
          SetWindowPos(hwnd, NULL, 0, 0, 0, 0, nFlags);
        }
        hwnd = ::GetWindow(hwnd, GW_HWNDNEXT);
      }
    }
    return hr;
  }
  __except(1)
  {
    _TRACE0("TCPropertyPageSite::Activate(): Unknown exception.\n");
    return E_UNEXPECTED;
  }
}


/////////////////////////////////////////////////////////////////////////////
// Group=IUnknown Interface Methods

/////////////////////////////////////////////////////////////////////////////
// Description: Returns pointers to supported interfaces.
//
// Supports the IUnknown and IPropertyPageSite interfaces.
//
// Return Value: Returns one of the following standard results:
//
//   S_OK - The specified IID is supported on this object.
//   E_NOINTERFACE - The specified IID is *not* supported on this object.
//   E_POINTER - /ppvObject/ is an invalid pointer.
//
// See Also: TCPropertyPageSite
STDMETHODIMP TCPropertyPageSite::XPageSite::QueryInterface(REFIID riid,
  void** ppvObject)
{
  __try
  {
    // Determine the requested interface
    if (IID_IUnknown == riid || IID_IPropertyPageSite == riid)
    {
      *((IUnknown**)ppvObject) = this;
      return S_OK;
    }

    // Requested interface is not implemented by this object
    *ppvObject = NULL;
    return E_NOINTERFACE;
  }
  __except(1)
  {
    return E_POINTER;
  }
}

/////////////////////////////////////////////////////////////////////////////
// Description: Increments reference count.
//
// Does nothing since the lifetime of this object is not dependent on a
// reference count.
//
// Return Value: This implementation always returns 1.
//
// See Also: TCPropertyPageSite
STDMETHODIMP_(ULONG) TCPropertyPageSite::XPageSite::AddRef()
{
  return 1;
}

/////////////////////////////////////////////////////////////////////////////
// Description: Decrements reference count.
//
// Does nothing since the lifetime of this object is not dependent on a
// reference count.
//
// Return Value: This implementation always returns 1.
//
// See Also: TCPropertyPageSite
STDMETHODIMP_(ULONG) TCPropertyPageSite::XPageSite::Release()
{
  return 1;
}


/////////////////////////////////////////////////////////////////////////////
// Group=IPropertyPageSite Interface Methods

/////////////////////////////////////////////////////////////////////////////
// Description: Indicates that the user has modified property values on the
// property page.
//
// This method is called by the managed property page to indicate a change in
// status. This implementation ignores status changes until the page becomes
// active. Otherwise, the dirty flag of the site object is set and the
// TCPropertyPageSite::OnStatusChange virtual override is called.
//
// Parameters:
//   dwFlags - [in] Flags to indicate what changes have occurred. The dwFlags
// parameter can contain either of these two values to indicate the type of
// status change:
//
//   PROPPAGESTATUS_DIRTY - Values in page have changed so the state of the
// Apply button should be updated.
//
//   PROPPAGESTATUS_VALIDATE - Now is an appropriate time to apply changes.
//
// Return Value: The result of the TCPropertyPageSite::OnStatusChange virtual
// override is returned.
//
// See Also: TCPropertyPageSite, TCPropertyPageSite::OnStatusChange,
// TCPropertyPageSite::Page_IsDirty
STDMETHODIMP TCPropertyPageSite::XPageSite::OnStatusChange(DWORD dwFlags)
{
  // Display a trace message under _DEBUG builds
  #ifdef _DEBUG
    _bstr_t bstrFlags;
    if (dwFlags & PROPPAGESTATUS_DIRTY)
      bstrFlags += L"PROPPAGESTATUS_DIRTY ";
    if (dwFlags & PROPPAGESTATUS_VALIDATE)
      bstrFlags += L"PROPPAGESTATUS_VALIDATE ";
    if (dwFlags & PROPPAGESTATUS_CLEAN)
      bstrFlags += L"PROPPAGESTATUS_CLEAN";
    _TRACE_BEGIN
    _TRACE_PART2("TCPropertyPageSite::XPageSite::OnStatusChange(0x%08X)\n\t%ls\n",
      dwFlags, LPCWSTR(bstrFlags));
    _TRACE_PART1("\tIsPageDirty() returns %s\n",
      m_pThis->Page_IsDirty() ? TEXT("true") : TEXT("false"));
    _TRACE_END
  #endif // _DEBUG

  // Ignore status change until page is activated
  if (!m_pThis->m_bActivated)
    return S_OK;

  // Set the dirty flag
  m_pThis->m_bDirty = true;

  // Delegate to virtual function
  return m_pThis->OnStatusChange(dwFlags);
}

/////////////////////////////////////////////////////////////////////////////
// Description: Returns the locale identifier so the property page can adjust
// itself to country-specific settings.
//
// Returns the locale identifier (an LCID) that the managed property page can
// use to adjust itself to the language in use and other country-specific
// settings.
// 
// This implementation gets the locale of the current thread and then calls
// the TCPropertyPageSite::OnGetLocaleID virtual override.
//
// Parameters:
//   pLocaleID - [out] Pointer to locale identifier. 
//
// Return Value: The result of the TCPropertyPageSite::OnGetLocaleID virtual
// override is returned.
//
// See Also: TCPropertyPageSite, TCPropertyPageSite::OnGetLocaleID
STDMETHODIMP TCPropertyPageSite::XPageSite::GetLocaleID(LCID* pLocaleID)
{
  __try
  {
    // Display a trace message under _DEBUG builds
    _TRACE0("TCPropertyPageSite::XPageSite::GetLocaleID()\n");

    // Initialize the [out] parameter
    *pLocaleID = GetThreadLocale();

    // Delegate to virtual function
    return m_pThis->OnGetLocaleID(pLocaleID);
  }
  __except(1)
  {
    return E_POINTER;
  }
}

/////////////////////////////////////////////////////////////////////////////
// Description: Returns an IUnknown pointer for the object representing the
// entire property frame dialog box that contains all the pages.
//
// Returns an IUnknown pointer to the object representing the entire property
// frame dialog box that contains all the pages. Calling this method could
// potentially allow one page to navigate to another.
//
// However, there are no "container" interfaces currently defined for this
// role, so this method always fails in the current standard property frame
// implementation. But since the role of TCPropertyPageSite is to create
// non-standard property frames, this method could be used as a channel for
// property pages to communicate with a custom property frame.
//
// This implementation initializes */ppUnk/ to NULL and then calls the
// TCPropertyPageSite::OnGetPageContainer virtual override.
//
// Parameters:
//   ppUnk - [out] Address of IUnknown* pointer variable that receives the
// interface pointer to the container object.
//
// Return Value: The result of the TCPropertyPageSite::OnGetPageContainer
// virtual override is returned.
//
// See Also: TCPropertyPageSite, TCPropertyPageSite::OnGetPageContainer
STDMETHODIMP TCPropertyPageSite::XPageSite::GetPageContainer(IUnknown** ppUnk)
{
  __try
  {
    // Display a trace message under _DEBUG builds
    _TRACE0("TCPropertyPageSite::XPageSite::GetPageContainer()\n");

    // Initialize the [out] parameter
    *ppUnk = NULL;

    // Delegate to virtual function
    return m_pThis->OnGetPageContainer(ppUnk);
  }
  __except(1)
  {
    return E_POINTER;
  }
}

/////////////////////////////////////////////////////////////////////////////
// Description: Passes a keystroke to the property frame for processing.
//
// Instructs the page site to process a keystroke if it desires. This
// implementation does nothing more than call the
// TCPropertyPageSite::OnTranslateAccelerator virtual override.
//
// Parameters:
//   pMsg - [in] Pointer to the MSG structure to be processed.
//
// Return Value: The result of the TCPropertyPageSite::OnTranslateAccelerator
// virtual override is returned.
//
// See Also: TCPropertyPageSite, TCPropertyPageSite::OnTranslateAccelerator
STDMETHODIMP TCPropertyPageSite::XPageSite::TranslateAccelerator(MSG* pMsg)
{
  __try
  {
    // Display a trace message under _DEBUG builds
    _TRACE0("TCPropertyPageSite::XPageSite::TranslateAccelerator()\n");

    // Delegate to virtual function
    return m_pThis->OnTranslateAccelerator(pMsg);
  }
  __except(1)
  {
    return E_POINTER;
  }
}