//+----------------------------------------------------------------------------
//
//  Windows NT Active Directory Property Page Sample
//
//  The code contained in this source file is for demonstration purposes only.
//  No warrantee is expressed or implied and Microsoft disclaims all liability
//  for the consequenses of the use of this source code.
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1999
//
//  File:       host.cxx
//
//  Contents:   CDsPropPageHost, the class that exposes IShellExtInit and
//              IShellPropSheetExt
//              Also, the ClassFactory and IUnknown code.
//
//  History:    8-Sep-97 Eric Brown created
//
//-----------------------------------------------------------------------------

#include "page.h"

CLIPFORMAT g_cfDsObjectNames = 0;
const CLSID CLSID_SamplePage = { /* cca62184-294f-11d1-bcfe-00c04fd8d5b6 */
    0xcca62184,
    0x294f,
    0x11d1,
    {0xbc, 0xfe, 0x00, 0xc0, 0x4f, 0xd8, 0xd5, 0xb6}
};

//+----------------------------------------------------------------------------
//
//  Member:     CDsPropPageHost::CDsPropPageHost
//
//-----------------------------------------------------------------------------
CDsPropPageHost::CDsPropPageHost() :
    m_pDataObj(NULL),
    m_uRefs(1)
{
}

//+----------------------------------------------------------------------------
//
//  Member:     CDsPropPageHost::~CDsPropPageHost
//
//-----------------------------------------------------------------------------
CDsPropPageHost::~CDsPropPageHost()
{
    if (m_pDataObj)
    {
        m_pDataObj->Release();
    }
}

//+----------------------------------------------------------------------------
//
//  Member:     CDsPropPageHost::IShellExtInit::Initialize
//
//  Returns:    HRESULTS
//
//-----------------------------------------------------------------------------
STDMETHODIMP
CDsPropPageHost::Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj,
                            HKEY hKeyID)
{
    if (IsBadReadPtr(pDataObj, sizeof(LPDATAOBJECT)))
    {
        return E_INVALIDARG;
    }

    if (m_pDataObj)
    {
        m_pDataObj->Release();
        m_pDataObj = NULL;
    }

    // Hang onto the IDataObject we are being passed.

    m_pDataObj = pDataObj;
    if (m_pDataObj)
    {
        m_pDataObj->AddRef();
    }
    else
    {
        return E_INVALIDARG;
    }

    // Check to see if we have our clipboard format registered, if not then
    // lets do it.

    if (!g_cfDsObjectNames)
    {
        g_cfDsObjectNames = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOBJECTNAMES);
    }
    if (!g_cfDsObjectNames)
    {
        return E_FAIL;
    }

    return S_OK;
}

//+----------------------------------------------------------------------------
//
//  Member:     CDsPropPageHost::IShellExtInit::AddPages
//
//  Returns:    HRESULTS
//
//-----------------------------------------------------------------------------
STDMETHODIMP
CDsPropPageHost::AddPages(LPFNADDPROPSHEETPAGE pAddPageProc, LPARAM lParam)
{
    HRESULT hr = S_OK;
    HPROPSHEETPAGE hPage;
    STGMEDIUM ObjMedium = {TYMED_NULL};
    FORMATETC fmte = {g_cfDsObjectNames, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
    LPDSOBJECTNAMES pDsObjectNames;
    PWSTR pwzObjName;
    PWSTR pwzClass;
    CDsPropPage * PropPage;

    //
    // Get the path to the DS object from the data object.
    // Note: This call runs on the caller's main thread. The pages' window
    // procs run on a different thread, so don't reference the data object
    // from a winproc unless it is first marshalled on this thread.
    //
    hr = m_pDataObj->GetData(&fmte, &ObjMedium);
    if (!SUCCEEDED(hr))
    {
        return hr;
    }

    pDsObjectNames = (LPDSOBJECTNAMES)ObjMedium.hGlobal;

    if (pDsObjectNames->cItems < 1)
    {
        hr = E_FAIL;
        goto Cleanup;
    }

    pwzObjName = (PWSTR)ByteOffset(pDsObjectNames,
                                   pDsObjectNames->aObjects[0].offsetName);
    pwzClass = (PWSTR)ByteOffset(pDsObjectNames,
                                 pDsObjectNames->aObjects[0].offsetClass);

    //
    // NOTIFY_OBJ
    // Create/contact the notification object.
    //
    HWND hNotifyObj;

    hr = ADsPropCreateNotifyObj(m_pDataObj, pwzObjName, &hNotifyObj);

    if (FAILED(hr))
    {
        goto Cleanup;
    }

    //
    // Create the page.
    //
    PropPage = new CDsPropPage(hNotifyObj);

    hr = PropPage->Init(pwzObjName, pwzClass);

    if (!SUCCEEDED(hr))
    {
        goto Cleanup;
    }

    hr = PropPage->CreatePage(&hPage);

    if (!SUCCEEDED(hr))
    {
        goto Cleanup;
    }

    //
    // Invoke the callback function, which will add the page to the list of
    // pages that will be used to create the property sheet.
    //
    (*pAddPageProc)(hPage, lParam);

Cleanup:
    ReleaseStgMedium(&ObjMedium);

    return hr;
}

//+----------------------------------------------------------------------------
//
//  Member: CDsPropPageHost::IShellExtInit::ReplacePage
//
//  Notes:  Not used.
//
//-----------------------------------------------------------------------------
STDMETHODIMP
CDsPropPageHost::ReplacePage(UINT uPageID,
                             LPFNADDPROPSHEETPAGE lpfnReplaceWith,
                             LPARAM lParam)
{
    return E_NOTIMPL;
}

//+----------------------------------------------------------------------------
//
//  Member:     CDsPropPageHost::IUnknown::QueryInterface
//
//  Synopsis:   Returns requested interface pointer
//
//-----------------------------------------------------------------------------
STDMETHODIMP
CDsPropPageHost::QueryInterface(REFIID riid, void ** ppvObject)
{
    if (IID_IUnknown == riid)
    {
        *ppvObject = (IUnknown *)(LPSHELLEXTINIT)this;
    }
    else if (IID_IShellExtInit == riid)
    {
        *ppvObject = (LPSHELLEXTINIT)this;
    }
    else if (IID_IShellPropSheetExt == riid)
    {
        *ppvObject = (LPSHELLPROPSHEETEXT)this;
    }
    else
    {
        *ppvObject = NULL;
        return E_NOINTERFACE;
    }
    AddRef();
    return S_OK;
}

//+----------------------------------------------------------------------------
//
//  Member:     CDsPropPageHost::IUnknown::AddRef
//
//  Synopsis:   increments reference count
//
//  Returns:    the reference count
//
//-----------------------------------------------------------------------------
STDMETHODIMP_(ULONG)
CDsPropPageHost::AddRef(void)
{
    return InterlockedIncrement((long *)&m_uRefs);
}

//+----------------------------------------------------------------------------
//
//  Member:     CDsPropPageHost::IUnknown::Release
//
//  Synopsis:   Decrements the object's reference count and frees it when
//              no longer referenced.
//
//  Returns:    zero if the reference count is zero or non-zero otherwise
//
//-----------------------------------------------------------------------------
STDMETHODIMP_(ULONG)
CDsPropPageHost::Release(void)
{
    unsigned long uTmp;
    if ((uTmp = InterlockedDecrement((long *)&m_uRefs)) == 0)
    {
        delete this;
    }
    return uTmp;
}

//+----------------------------------------------------------------------------
//
//      CDsPropPageHostCF - class factory for the CDsPropPageHost object
//
//-----------------------------------------------------------------------------

//+----------------------------------------------------------------------------
//
//  Member:     CDsPropPageHostCF::Create
//
//  Synopsis:   creates a new class factory object
//
//-----------------------------------------------------------------------------
IClassFactory *
CDsPropPageHostCF::Create(void)
{
    return new CDsPropPageHostCF();
}

//+----------------------------------------------------------------------------
//
//  Member:     CDsPropPageHostCF::CDsPropPageHostCF
//
//  Synopsis:   ctor
//
//-----------------------------------------------------------------------------
CDsPropPageHostCF::CDsPropPageHostCF() :
    m_uRefs(1)
{
}

//+----------------------------------------------------------------------------
//
//  Member:     CDsPropPageHostCF::~CDsPropPageHostCF
//
//  Synopsis:   dtor
//
//-----------------------------------------------------------------------------
CDsPropPageHostCF::~CDsPropPageHostCF(void)
{
}

//+----------------------------------------------------------------------------
//
//  Member:     CDsPropPageHostCF::IUnknown::QueryInterface
//
//  Synopsis:   Returns requested interface pointer
//
//-----------------------------------------------------------------------------
STDMETHODIMP
CDsPropPageHostCF::QueryInterface(REFIID riid, void ** ppvObject)
{
    if (IID_IUnknown == riid)
    {
        *ppvObject = (IUnknown *)this;
    }
    else if (IsEqualIID(IID_IClassFactory, riid))
    {
        *ppvObject = (IClassFactory *)this;
    }
    else
    {
        *ppvObject = NULL;
        return E_NOINTERFACE;
    }
    AddRef();
    return S_OK;
}

//+----------------------------------------------------------------------------
//
//  Member:     CDsPropPageHostCF::IUnknown::AddRef
//
//  Synopsis:   increments reference count
//
//  Returns:    the new reference count
//
//-----------------------------------------------------------------------------
STDMETHODIMP_(ULONG)
CDsPropPageHostCF::AddRef(void)
{
    return InterlockedIncrement((long *)&m_uRefs);
}

//+----------------------------------------------------------------------------
//
//  Member:     CDsPropPageHostCF::IUnknown::Release
//
//  Synopsis:   decrement the refcount
//
//  Returns:    the new reference count
//
//-----------------------------------------------------------------------------
STDMETHODIMP_(ULONG)
CDsPropPageHostCF::Release(void)
{
    unsigned long uTmp;
    if ((uTmp = InterlockedDecrement((long *)&m_uRefs)) == 0)
    {
        delete this;
    }
    return uTmp;
}

//+----------------------------------------------------------------------------
//
//  Member:     CDsPropPageHostCF::IClassFactory::CreateInstance
//
//  Synopsis:   create an incore instance of the proppage host class object
//
//  Arguments:  [pUnkOuter] - aggregator
//              [riid]      - requested interface
//              [ppvObject] - receptor for itf ptr
//
//  Returns:    HRESULTS
//
//-----------------------------------------------------------------------------
STDMETHODIMP
CDsPropPageHostCF::CreateInstance(IUnknown *pUnkOuter, REFIID riid,
                                   void **ppvObject)
{
    HRESULT hr = S_OK;
    *ppvObject = NULL;

    CDsPropPageHost * pPropPage = new CDsPropPageHost();
    if (pPropPage == NULL)
    {
        return E_OUTOFMEMORY;
    }

    hr = pPropPage->QueryInterface(riid, ppvObject);
    if (FAILED(hr))
    {
        pPropPage->Release();
        return hr;
    }

    //
    // We got a refcount of one when launched, and the above QI increments it
    // to 2, so call release to take it back to 1.
    //
    pPropPage->Release();
    return hr;
}

//+----------------------------------------------------------------------------
//
//  Member:     CDsPropPageHostCF::IClassFactory::LockServer
//
//  Synopsis:   Called with fLock set to TRUE to indicate that the server
//              should continue to run even if none of its objects are active
//
//  Arguments:  [fLock] - increment/decrement the instance count
//
//  Returns:    HRESULTS
//
//-----------------------------------------------------------------------------
STDMETHODIMP
CDsPropPageHostCF::LockServer(BOOL fLock)
{
    CDll::LockServer(fLock);
    return S_OK;
}