#include "shole.h"
#include "ids.h"
#include "scguid.h"

#ifdef FEATURE_SHELLEXTENSION

extern "C" const TCHAR c_szCLSID[];

class CTemplateFolder : public IShellFolder, public IPersistFolder
{
public:
    CTemplateFolder();
    ~CTemplateFolder();
    inline BOOL ConstructedSuccessfully() { return _hdpaMap != NULL; }

    // IUnKnown
    virtual HRESULT __stdcall QueryInterface(REFIID,void **);
    virtual ULONG   __stdcall AddRef(void);
    virtual ULONG   __stdcall Release(void);

    // IShellFolder
    virtual HRESULT __stdcall ParseDisplayName(HWND hwndOwner,
        LPBC pbcReserved, LPOLESTR lpszDisplayName,
        ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes);

    virtual HRESULT __stdcall EnumObjects( THIS_ HWND hwndOwner, DWORD grfFlags, LPENUMIDLIST * ppenumIDList);

    virtual HRESULT __stdcall BindToObject(LPCITEMIDLIST pidl, LPBC pbcReserved,
                                 REFIID riid, LPVOID * ppvOut);
    virtual HRESULT __stdcall BindToStorage(LPCITEMIDLIST pidl, LPBC pbcReserved,
                                 REFIID riid, LPVOID * ppvObj);
    virtual HRESULT __stdcall CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
    virtual HRESULT __stdcall CreateViewObject (HWND hwndOwner, REFIID riid, LPVOID * ppvOut);
    virtual HRESULT __stdcall GetAttributesOf(UINT cidl, LPCITEMIDLIST * apidl,
                                    ULONG * rgfInOut);
    virtual HRESULT __stdcall GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl,
                                 REFIID riid, UINT * prgfInOut, LPVOID * ppvOut);
    virtual HRESULT __stdcall GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName);
    virtual HRESULT __stdcall SetNameOf(HWND hwndOwner, LPCITEMIDLIST pidl,
                                 LPCOLESTR lpszName, DWORD uFlags,
                                 LPITEMIDLIST * ppidlOut);

    // IPersistFolder
    virtual HRESULT __stdcall GetClassID(LPCLSID lpClassID);
    virtual HRESULT __stdcall Initialize(LPCITEMIDLIST pidl);

protected:
    // Defview callback
    friend HRESULT CALLBACK DefViewCallback(
                                LPSHELLVIEW psvOuter, LPSHELLFOLDER psf,
                                HWND hwndOwner, UINT uMsg,
                                WPARAM wParam, LPARAM lParam);
    HRESULT GetDetailsOfDVM(UINT ici, DETAILSINFO *pdi);

    BOOL IsMyPidl(LPCITEMIDLIST pidl)
	{ return (pidl->mkid.abID[0] == 'S' && pidl->mkid.abID[1] == 'N'); }

    UINT _cRef;

    //
    //  To speed up name lookups, we cache the mapping between CLSIDs and
    //  display names.  We cannot persist this mapping because it won't
    //  survive localization or ANSI/UNICODE interop.
    //
    typedef struct {
        CLSID clsid;
        TCHAR achName[MAX_PATH];
    } CLSIDMAP, *PCLSIDMAP;

    HDPA _hdpaMap;

    HRESULT GetNameOf(LPCITEMIDLIST pidl, LPCTSTR *ppsz);
};

class CEnumTemplate : public IEnumIDList
{
public:
    CEnumTemplate(DWORD grfFlags);
    ~CEnumTemplate();

protected:
    // IUnKnown
    virtual HRESULT __stdcall QueryInterface(REFIID,void **);
    virtual ULONG   __stdcall AddRef(void);
    virtual ULONG   __stdcall Release(void);

    virtual HRESULT __stdcall Next(ULONG celt,
                      LPITEMIDLIST *rgelt,
                      ULONG *pceltFetched);
    virtual HRESULT __stdcall Skip(ULONG celt);
    virtual HRESULT __stdcall Reset();
    virtual HRESULT __stdcall Clone(IEnumIDList **ppenum);

    UINT	_cRef;
    const DWORD	_grfFlags;
    UINT	_iCur;
    HKEY	_hkeyCLSID;
};

//
//  For Win95/NT interop, our PIDLs are always UNICODE.
//  Use explicit packing for Win32/64 interop.
#include <pshpack1.h>
typedef struct _TIDL {
    USHORT          cb;             // This matches SHITEMID
    BYTE            abID[2];        // This matches SHITEMID
    CLSID	    clsid;
} TIDL;
typedef const UNALIGNED TIDL *PTIDL;

//
//  This is the TIDL constructor -- it has the cbZero at the end.
//
typedef struct _TIDLCONS {
    TIDL            tidl;
    USHORT          cbZero;
} TIDLCONS;

#include <poppack.h>

class CTemplateUIObj : public IExtractIcon, public IDataObject, public IContextMenu
{
public:
    static HRESULT Create(REFCLSID, REFIID, LPVOID*);
protected:
    CTemplateUIObj(REFCLSID rclsid)
                    : _clsid(rclsid), _cRef(1)
                                { DllAddRef(); }
    ~CTemplateUIObj()           { DllRelease(); }
    HRESULT _CreateInstance(IStorage* pstg);

    // IUnKnown
    virtual HRESULT __stdcall QueryInterface(REFIID,void **);
    virtual ULONG   __stdcall AddRef(void);
    virtual ULONG   __stdcall Release(void);

    // *** IExtractIcon methods ***
    virtual HRESULT __stdcall GetIconLocation(
                         UINT   uFlags, LPTSTR  szIconFile,
                         UINT   cchMax, int   * piIndex,
                         UINT  * pwFlags);

    virtual HRESULT __stdcall Extract(
                           LPCTSTR pszFile, UINT          nIconIndex,
                           HICON   *phiconLarge, HICON   *phiconSmall,
                           UINT    nIconSize);

    // IDataObject
    virtual HRESULT __stdcall GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium);
    virtual HRESULT __stdcall GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium);
    virtual HRESULT __stdcall QueryGetData(FORMATETC *pformatetc);
    virtual HRESULT __stdcall GetCanonicalFormatEtc(FORMATETC *pformatectIn, FORMATETC *pformatetcOut);
    virtual HRESULT __stdcall SetData(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease);
    virtual HRESULT __stdcall EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc);
    virtual HRESULT __stdcall DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection);
    virtual HRESULT __stdcall DUnadvise(DWORD dwConnection);
    virtual HRESULT __stdcall EnumDAdvise(IEnumSTATDATA **ppenumAdvise);

    // IContextMenu
    virtual HRESULT __stdcall QueryContextMenu(
                                HMENU hmenu,
                                UINT indexMenu,
                                UINT idCmdFirst,
                                UINT idCmdLast,
                                UINT uFlags);

    virtual HRESULT __stdcall InvokeCommand(
                             LPCMINVOKECOMMANDINFO lpici);

    virtual HRESULT __stdcall GetCommandString(
                                UINT        idCmd,
                                UINT        uType,
                                UINT      * pwReserved,
                                LPSTR       pszName,
                                UINT        cchMax);

    UINT	_cRef;
    CLSID	_clsid;
};


CTemplateFolder::CTemplateFolder() : _cRef(1)
{
    _hdpaMap = DPA_Create(10);
    OleInitialize(NULL);
    DllAddRef();
}

CTemplateFolder::~CTemplateFolder()
{
    if (_hdpaMap) {
        for (int i = DPA_GetPtrCount(_hdpaMap) - 1; i >= 0; i--) {
            PCLSIDMAP pmap = (PCLSIDMAP)DPA_FastGetPtr(_hdpaMap, i);
            LocalFree(pmap);
        }
        DPA_Destroy(_hdpaMap);
    }

    OleUninitialize();
    DllRelease();
}

HRESULT CTemplateFolder::QueryInterface(REFIID riid, LPVOID * ppvObj)
{
    if (IsEqualIID(riid, IID_IShellFolder) || IsEqualIID(riid, IID_IUnknown))
    {
        *ppvObj = (IShellFolder*)this;
        _cRef++;
        return S_OK;
    }
    else if (IsEqualIID(riid, IID_IPersistFolder))
    {
        *ppvObj = (IPersistFolder*)this;
        _cRef++;
        return S_OK;
    }
    *ppvObj = NULL;

    return E_NOINTERFACE;
}

ULONG CTemplateFolder::AddRef()
{
    _cRef++;
    return _cRef;
}

ULONG CTemplateFolder::Release()
{
    _cRef--;
    if (_cRef > 0)
        return _cRef;

    delete this;
    return 0;
}

HRESULT CTemplateFolder_CreateInstance(LPUNKNOWN * ppunk)
{
    *ppunk = NULL;

    CTemplateFolder* ptfld = new CTemplateFolder();
    if (ptfld) {
        if (ptfld->ConstructedSuccessfully()) {
            *ppunk = (IShellFolder *)ptfld;
            return S_OK;
        }
        ptfld->Release();
    }
    return E_OUTOFMEMORY;
}

HRESULT CTemplateFolder::ParseDisplayName(HWND hwndOwner,
        LPBC pbcReserved, LPOLESTR lpszDisplayName,
        ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes)
{
    return E_NOTIMPL;
}

HRESULT CTemplateFolder::EnumObjects(HWND hwndOwner, DWORD grfFlags, LPENUMIDLIST * ppenumIDList)
{
    *ppenumIDList = new CEnumTemplate(grfFlags);
    return *ppenumIDList ? S_OK : E_OUTOFMEMORY;
}

HRESULT CTemplateFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbcReserved,
                                 REFIID riid, LPVOID * ppvOut)
{
    return E_NOTIMPL;
}

HRESULT CTemplateFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbcReserved,
                                 REFIID riid, LPVOID * ppvObj)
{
    return E_NOTIMPL;
}

//
//  If the name is in the cache, celebrate our good fortune and return it.
//  Else, go get the name from the registry and cache it for later.
//
HRESULT CTemplateFolder::GetNameOf(LPCITEMIDLIST pidl, LPCTSTR *ppsz)
{
    if (!IsMyPidl(pidl))
        return E_INVALIDARG;

    HRESULT hres;
    PTIDL ptidl = (PTIDL)pidl;
    CLSIDMAP map;
    map.clsid = ptidl->clsid;           // Align the CLSID
    PCLSIDMAP pmap;
    for (int i = DPA_GetPtrCount(_hdpaMap) - 1; i >= 0; i--) {
        pmap = (PCLSIDMAP)DPA_FastGetPtr(_hdpaMap, i);
        if (IsEqualGUID(pmap->clsid, map.clsid)) {
            *ppsz = pmap->achName;
            return S_OK;
        }
    }

    //
    //  Not in cache -- go find it in the registry
    //
    TCHAR szKey[GUIDSTR_MAX + 6];
    _KeyNameFromCLSID(map.clsid, szKey, ARRAYSIZE(szKey));
    LONG dwSize = ARRAYSIZE(map.achName);
    LONG lError = RegQueryValue(HKEY_CLASSES_ROOT, szKey, map.achName, &dwSize);
    if (lError == ERROR_SUCCESS)
    {
        UINT cb = FIELD_OFFSET(CLSIDMAP, achName[lstrlen(map.achName)+1]);
        pmap = (PCLSIDMAP)LocalAlloc(LMEM_FIXED, cb);
        if (pmap) {
            CopyMemory(pmap, &map, cb);
            if (DPA_AppendPtr(_hdpaMap, pmap) >= 0) {
                *ppsz = pmap->achName;
                hres = S_OK;
            } else {
                LocalFree(pmap);
                hres = E_OUTOFMEMORY;
            }
        } else {
            hres = E_OUTOFMEMORY;
        }
    }
    else
    {
        hres = HRESULT_FROM_WIN32(lError);
    }

    return hres;

}

HRESULT CTemplateFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
    LPCTSTR psz1, psz2;
    HRESULT hres;

    hres = GetNameOf(pidl1, &psz1);
    if (SUCCEEDED(hres)) {
        hres = GetNameOf(pidl2, &psz2);
        if (SUCCEEDED(hres)) {
            hres = ResultFromShort(lstrcmpi(psz1, psz2));
        }
    }
    return hres;
}


HRESULT CTemplateFolder::GetDetailsOfDVM(UINT ici, DETAILSINFO *pdi)
{
    HRESULT hres;

    switch (ici) {
    case 0:
        if (pdi->pidl) {
            hres = GetDisplayNameOf(pdi->pidl, SHGDN_NORMAL, &pdi->str);
        } else {
            pdi->fmt = LVCFMT_LEFT;
            pdi->cxChar = 30;
            pdi->str.uType = STRRET_CSTR;
            lstrcpyA(pdi->str.cStr, "Name"); 
            hres = S_OK;
        }
        break;

    default:
        hres = E_NOTIMPL;
        break;
    }
    return hres;
}

HRESULT CALLBACK DefViewCallback(LPSHELLVIEW psvOuter, LPSHELLFOLDER psf,
                                HWND hwndOwner, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    // DefView GPF if I don't pass the callback function!
    // DebugMsg(DM_TRACE, "sc TR - DefViewCallBack %d,%x,%x", uMsg, wParam, lParam);
    switch(uMsg)
    {
    case DVM_WINDOWDESTROY:
	DebugMsg(DM_TRACE, TEXT("sc TR - DefViewCallBack Calling OleFlushClipboard"));
	OleFlushClipboard();
	return S_OK;
    case DVM_GETDETAILSOF:
        return ((CTemplateFolder*)psf)->GetDetailsOfDVM((UINT)wParam, (DETAILSINFO*)lParam);
    }
    // DefView GPF if it returns S_FALSE as the default!
    return E_FAIL; // S_FALSE;
}


HRESULT CTemplateFolder::CreateViewObject (HWND hwndOwner, REFIID riid, LPVOID * ppvOut)
{
    if (IsEqualIID(riid, IID_IShellView))
    {
        CSFV csfv = {
            SIZEOF(CSFV),       // cbSize
            this,               // pshf
            NULL,               // psvOuter
            NULL,               // pidl
            0,
            DefViewCallback,    // pfnCallback
            FVM_ICON,
        };
        return SHCreateShellFolderViewEx(&csfv, (LPSHELLVIEW *)ppvOut);
    }
    return E_NOINTERFACE;
}

HRESULT CTemplateFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST * apidl,
                                    ULONG * rgfInOut)
{
    if (cidl==1)
    {
	UINT rgfOut = SFGAO_CANCOPY /* | SFGAO_HASPROPSHEET */;
	*rgfInOut &= rgfOut;
    }
    else
    {
	*rgfInOut = 0;
    }
    return S_OK;
}

HRESULT CTemplateFolder::GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl,
                                 REFIID riid, UINT * prgfInOut, LPVOID * ppvOut)
{
    HRESULT hres = E_INVALIDARG;
    if (cidl==1 && IsMyPidl(apidl[0]))
    {
	PTIDL ptidl = (PTIDL)apidl[0];
	hres = CTemplateUIObj::Create(ptidl->clsid, riid, ppvOut);
    }

    return hres;
}

HRESULT _KeyNameFromCLSID(REFCLSID rclsid, LPTSTR pszKey, UINT cchMax)
{
    ASSERT(cchMax - 6 >= GUIDSTR_MAX);
    lstrcpyn(pszKey, TEXT("CLSID\\"), cchMax);
    SHStringFromGUID(rclsid, pszKey + 6, cchMax - 6);
    return S_OK;
}

HRESULT CTemplateFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName)
{
    LPCTSTR psz;
    HRESULT hres;

    hres = GetNameOf(pidl, &psz);
    if (SUCCEEDED(hres)) {
#ifdef UNICODE
        lpName->uType = STRRET_WSTR;
        hres = SHStrDupW(psz, &lpName->pOleStr);
#else
        lstrcpyn(lpName->cStr, psz, ARRAYSIZE(lpName->cStr));
        hres = S_OK;
#endif
    }
    return hres;
}

HRESULT CTemplateFolder::SetNameOf(HWND hwndOwner, LPCITEMIDLIST pidl,
                                 LPCOLESTR lpszName, DWORD uFlags,
                                 LPITEMIDLIST * ppidlOut)
{
    return E_NOTIMPL;
}

HRESULT __stdcall CTemplateFolder::GetClassID(LPCLSID lpClassID)
{
    *lpClassID = CLSID_CTemplateFolder;
    return S_OK;
}

HRESULT __stdcall CTemplateFolder::Initialize(LPCITEMIDLIST pidl)
{
    return S_OK;
}

CEnumTemplate::CEnumTemplate(DWORD grfFlags)
    : _cRef(1), _grfFlags(grfFlags), _iCur(0), _hkeyCLSID(NULL)
{
    DllAddRef();
}

CEnumTemplate::~CEnumTemplate()
{
    if (_hkeyCLSID) {
        RegCloseKey(_hkeyCLSID);
    }
    DllRelease();
}

HRESULT CEnumTemplate::QueryInterface(REFIID riid, LPVOID * ppvObj)
{
    if (IsEqualIID(riid, IID_IEnumIDList) || IsEqualIID(riid, IID_IUnknown))
    {
        *ppvObj = (IEnumIDList*)this;
        _cRef++;
        return S_OK;
    }
    *ppvObj = NULL;

    return E_NOINTERFACE;
}

ULONG CEnumTemplate::AddRef()
{
    _cRef++;
    return _cRef;
}

ULONG CEnumTemplate::Release()
{
    _cRef--;
    if (_cRef > 0)
        return _cRef;

    delete this;
    return 0;
}

HRESULT CEnumTemplate::Next(ULONG celt,
                      LPITEMIDLIST *rgelt,
                      ULONG *pceltFetched)
{
    // Assume error
    if (pceltFetched) {
        *pceltFetched = 0;
    }

    if (!(_grfFlags & SHCONTF_NONFOLDERS)) {
	return S_FALSE;
    }

    if (!_hkeyCLSID)
    {
        if (RegOpenKey(HKEY_CLASSES_ROOT, c_szCLSID, &_hkeyCLSID) != ERROR_SUCCESS)
        {
            return E_FAIL;
        }
    }

    TCHAR szKeyBuf[128];    // enough for {CLSID} or "ProgId/XXX"

    //  Subtract 64 to allow room for the goo we append later on
    while (RegEnumKey(HKEY_CLASSES_ROOT, _iCur++, szKeyBuf, ARRAYSIZE(szKeyBuf) - 64) == ERROR_SUCCESS)
    {
	TCHAR szT[128];
	LONG dwRead;
	int cchKey = lstrlen(szKeyBuf);

	// Check for \NotInsertable.
	lstrcpy(szKeyBuf+cchKey, TEXT("\\NotInsertable"));
	dwRead = ARRAYSIZE(szT);
	if (RegQueryValue(HKEY_CLASSES_ROOT, szKeyBuf, szT, &dwRead) == ERROR_SUCCESS) {
	    continue;
	}

	BOOL fInsertable = FALSE;
//
// Let's stop supporting OLE1 servers anymore.
//
#if 0
	lstrcpy(szKeyBuf+cchKey, TEXT("\\protocol\\StdFileEditing\\server"));
	dwRead = ARRAYSIZE(szT);
	if (RegQueryValue(HKEY_CLASSES_ROOT, szKeyBuf, szT, &dwRead) == ERROR_SUCCESS)
	{
	    fInsertable = TRUE;
	}
	else
#endif
	{
	    lstrcpy(szKeyBuf+cchKey, TEXT("\\Insertable"));
	    dwRead = ARRAYSIZE(szT);
	    if (RegQueryValue(HKEY_CLASSES_ROOT, szKeyBuf, szT, &dwRead) == ERROR_SUCCESS)
	    {
		fInsertable = TRUE;
	    }
	}

	if (fInsertable)
	{
	    lstrcpy(szKeyBuf+cchKey, TEXT("\\CLSID"));
	    dwRead = ARRAYSIZE(szT);
	    if (RegQueryValue(HKEY_CLASSES_ROOT, szKeyBuf, szT, &dwRead) == ERROR_SUCCESS)
	    {
		TIDLCONS tidlCons;
                CLSID clsid;            // Aligned version
		tidlCons.tidl.cb = sizeof(TIDL);
		tidlCons.tidl.abID[0] = 'S';
		tidlCons.tidl.abID[1] = 'N';

		if (GUIDFromString(szT, &clsid))
		{
                    tidlCons.tidl.clsid = clsid;
                    tidlCons.cbZero = 0;
		    rgelt[0] = ILClone((LPITEMIDLIST)&tidlCons);
		    *pceltFetched = 1;
		    return S_OK;
		}
	    }
	}
    }

    return S_FALSE;     // no more element
}

HRESULT CEnumTemplate::Skip(ULONG celt)
{
    return E_NOTIMPL;
}

HRESULT CEnumTemplate::Reset()
{
    return E_NOTIMPL;
}

HRESULT CEnumTemplate::Clone(IEnumIDList **ppenum)
{
    return E_NOTIMPL;
}


//==========================================================================
// CTemplateUIObj members (IUnknown override)
//==========================================================================

HRESULT CTemplateUIObj::QueryInterface(REFIID riid, LPVOID * ppvObj)
{
    if (IsEqualIID(riid, IID_IExtractIcon) || IsEqualIID(riid, IID_IUnknown))
    {
        *ppvObj = (IExtractIcon*)this;
        _cRef++;
        return S_OK;
    }
    else if (IsEqualIID(riid, IID_IDataObject))
    {
        *ppvObj = (IDataObject*)this;
        _cRef++;
        return S_OK;
    }
    else if (IsEqualIID(riid, IID_IContextMenu))
    {
        *ppvObj = (IContextMenu*)this;
        _cRef++;
        return S_OK;
    }
    *ppvObj = NULL;

    return E_NOINTERFACE;
}

ULONG CTemplateUIObj::AddRef()
{
    _cRef++;
    return _cRef;
}

ULONG CTemplateUIObj::Release()
{
    _cRef--;
    if (_cRef > 0)
        return _cRef;

    delete this;
    return 0;
}

//
// NOTES: This logic MUST be identical to the one in the shell.
//
int _ParseIconLocation(LPTSTR pszIconFile)
{
    int iIndex = 0;
    LPTSTR pszComma = StrChr(pszIconFile, TEXT(','));

    if (pszComma) {
        *pszComma++ = 0;            // terminate the icon file name.
        iIndex = StrToInt(pszComma);
    }
    PathRemoveBlanks(pszIconFile);
    return iIndex;
}


//==========================================================================
// CTemplateUIObj members (IExtractIcon override)
//==========================================================================

//
// szClass -- Specifies either CLSID\{CLSID} or ProgID
//
HRESULT _GetDefaultIcon(LPCTSTR szClass, LPTSTR szIconFile, UINT cchMax, int *piIndex)
{
    TCHAR szKey[256];
    wsprintf(szKey, TEXT("%s\\DefaultIcon"), szClass);
    TCHAR szValue[MAX_PATH+40];
    LONG dwSize = ARRAYSIZE(szValue);
    if (RegQueryValue(HKEY_CLASSES_ROOT, szKey, szValue, &dwSize) == ERROR_SUCCESS)
    {
	*piIndex = _ParseIconLocation(szValue);
	lstrcpyn(szIconFile, szValue, cchMax);
	return S_OK;
    }
    return E_FAIL;
}

HRESULT CTemplateUIObj::GetIconLocation(
                         UINT   uFlags, LPTSTR  szIconFile,
                         UINT   cchMax, int   * piIndex,
                         UINT  * pwFlags)
{
    *pwFlags = GIL_PERCLASS;	// Always per-class

    TCHAR szKey[128];
    HRESULT hres = _KeyNameFromCLSID(_clsid, szKey, ARRAYSIZE(szKey));
    if (SUCCEEDED(hres))
    {
	//
	// First, look at "CLSID\{CLSID}\DefautlIcon"
	//
	hres = _GetDefaultIcon(szKey, szIconFile, cchMax, piIndex);
	if (FAILED(hres))
	{
	    //
	    // Then, look at "ProgID\DefaultIcon" to work-around a bug
	    //  of "Wave Sound".
	    //
    	    lstrcat(szKey, TEXT("\\ProgID"));
	    TCHAR szValue[MAX_PATH+40];
	    LONG dwSize = ARRAYSIZE(szValue);
	    if (RegQueryValue(HKEY_CLASSES_ROOT, szKey, szValue, &dwSize) == ERROR_SUCCESS)
	    {
		hres = _GetDefaultIcon(szValue, szIconFile, cchMax, piIndex);
	    }
	}
    }
    return hres;
}

HRESULT CTemplateUIObj::Extract(
                           LPCTSTR pszFile, UINT          nIconIndex,
                           HICON   *phiconLarge, HICON   *phiconSmall,
                           UINT    nIconSize)
{
    return S_FALSE;
}

HRESULT CTemplateUIObj::Create(REFCLSID rclsid, REFIID riid, LPVOID* ppvOut)
{
    CTemplateUIObj *pti = new CTemplateUIObj(rclsid);
    HRESULT hres;
    if (pti) {
        hres = pti->QueryInterface(riid, ppvOut);
        pti->Release();
        return hres;
    }

    *ppvOut=NULL;
    return E_OUTOFMEMORY;
}

//==========================================================================
// CTemplateUIObj members (IDataObject override)
//==========================================================================

HRESULT CTemplateUIObj::_CreateInstance(IStorage* pstg)
{
    HRESULT hres;
    IPersistStorage* pps = NULL;
    hres = OleCreate(_clsid, IID_IPersistStorage, OLERENDER_DRAW,
                     NULL, NULL, pstg, (LPVOID*)&pps);
    DebugMsg(DM_TRACE, TEXT("so TR - TUO:CI OleCreate returned (%x)"), hres);

    if (SUCCEEDED(hres))
    {
        hres = OleSave(pps, pstg, TRUE);
        DebugMsg(DM_TRACE, TEXT("so TR - TUO:CI OleSave returned (%x)"), hres);
        pps->HandsOffStorage();
        pps->Release();

        if (SUCCEEDED(hres))
        {
            hres = pstg->Commit(STGC_OVERWRITE);
            DebugMsg(DM_TRACE, TEXT("so TR - TUO:CI pstg->Commit returned (%x)"), hres);
        }
    }

    return hres;
}

HRESULT CTemplateUIObj::GetData(LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium)
{
    HRESULT hres = DATA_E_FORMATETC;

    pmedium->pUnkForRelease = NULL;
    pmedium->pstg = NULL;

    //
    // NOTES: We should avoid calling _OpenStorage if we don't support
    //  the format.
    //

    if (pformatetcIn->cfFormat == CF_EMBEDDEDOBJECT
        && pformatetcIn->tymed == TYMED_ISTORAGE)
    {
        IStorage* pstg = NULL;
        hres = StgCreateDocfile(NULL, STGM_DIRECT | STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pstg);
        DebugMsg(DM_TRACE, TEXT("so TR - TUO:GD StgCreateDocfile returned (%x)"), hres);
        if (SUCCEEDED(hres))
        {
            hres = _CreateInstance(pstg);
            if (SUCCEEDED(hres)) {
                pmedium->tymed = TYMED_ISTORAGE;
                pmedium->pstg = pstg;
            } else {
                pstg->Release();
            }
        }
    }
    else if (pformatetcIn->cfFormat == CF_OBJECTDESCRIPTOR
	&& pformatetcIn->tymed == TYMED_HGLOBAL)
    {
	DebugMsg(DM_TRACE, TEXT("so TR - TUO:GD cfFormat==CF_OBJECTDESCRIPTOR"));

	static WCHAR szUserTypeName[] = L"Foo";	// REARCHITECT: this code is really miss, and might end up returning Foo
	static WCHAR szSrcOfCopy[] = L"Bar";
	UINT cbUserTypeName = sizeof(szUserTypeName);
	UINT cbSrcOfCopy = sizeof(szSrcOfCopy);
	pmedium->hGlobal = GlobalAlloc(GPTR, sizeof(OBJECTDESCRIPTOR)+cbUserTypeName+cbSrcOfCopy);
	if (pmedium->hGlobal)
	{
	    OBJECTDESCRIPTOR* podsc = (OBJECTDESCRIPTOR*)pmedium->hGlobal;
	    podsc->cbSize = sizeof(OBJECTDESCRIPTOR);
	    podsc->clsid = _clsid;
	    podsc->dwDrawAspect = 0; // The source does not draw the object
	    // podsc->sizel = { 0, 0 }; // The source does not draw the object
	    // podsc->pointl = { 0, 0 };
	    podsc->dwStatus = 0; // FEATURE: read it from registry! CLSID/MiscStatus
	    podsc->dwFullUserTypeName = sizeof(OBJECTDESCRIPTOR);
	    podsc->dwSrcOfCopy = sizeof(OBJECTDESCRIPTOR)+cbUserTypeName;
	    LPBYTE pbT = (LPBYTE)(podsc+1);
            lstrcpyW((LPWSTR)pbT, szUserTypeName);
            lstrcpyW((LPWSTR)(pbT+cbUserTypeName), szSrcOfCopy);
	    Assert(pbT == ((LPBYTE)podsc)+podsc->dwFullUserTypeName);
	    Assert(pbT+cbUserTypeName == ((LPBYTE)podsc)+podsc->dwSrcOfCopy);

	    pmedium->tymed = TYMED_HGLOBAL;
	    hres = S_OK;
	}
	else
	{
	    hres = E_OUTOFMEMORY;
	}
    }
    return hres;
}

HRESULT CTemplateUIObj::GetDataHere(LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium)
{
    HRESULT hres = DATA_E_FORMATETC;

    if (pformatetcIn->cfFormat == CF_EMBEDDEDOBJECT
        && pformatetcIn->tymed == TYMED_ISTORAGE && pmedium->tymed == TYMED_ISTORAGE)
    {
        hres = _CreateInstance(pmedium->pstg);
    }

    return hres;
}

HRESULT CTemplateUIObj::QueryGetData(LPFORMATETC pformatetcIn)
{
    if (pformatetcIn->cfFormat == CF_EMBEDDEDOBJECT
        && pformatetcIn->tymed == TYMED_ISTORAGE)
    {
        return S_OK;
    }
    else if (pformatetcIn->cfFormat == CF_OBJECTDESCRIPTOR
	&& pformatetcIn->tymed == TYMED_HGLOBAL)
    {
	return S_OK;
    }
    return DATA_E_FORMATETC;
}

HRESULT CTemplateUIObj::GetCanonicalFormatEtc(LPFORMATETC pformatetc, LPFORMATETC pformatetcOut)
{
    //
    //  This is the simplest implemtation. It means we always return
    // the data in the format requested.
    //
    return ResultFromScode(DATA_S_SAMEFORMATETC);
}

HRESULT CTemplateUIObj::SetData(LPFORMATETC pformatetc, STGMEDIUM  * pmedium, BOOL fRelease)
{
    return E_FAIL;
}

HRESULT CTemplateUIObj::EnumFormatEtc(DWORD dwDirection, LPENUMFORMATETC * ppenumFormatEtc)
{
    static FORMATETC s_afmt[] = { { (CLIPFORMAT)CF_EMBEDDEDOBJECT }, {(CLIPFORMAT)CF_OBJECTDESCRIPTOR} };
    return SHCreateStdEnumFmtEtc(ARRAYSIZE(s_afmt), s_afmt, ppenumFormatEtc);
}

HRESULT CTemplateUIObj::DAdvise(FORMATETC * pFormatetc, DWORD advf, LPADVISESINK pAdvSink, DWORD * pdwConnection)
{
    return ResultFromScode(OLE_E_ADVISENOTSUPPORTED);
}

HRESULT CTemplateUIObj::DUnadvise(DWORD dwConnection)
{
    return ResultFromScode(OLE_E_ADVISENOTSUPPORTED);
}

HRESULT CTemplateUIObj::EnumDAdvise(LPENUMSTATDATA * ppenumAdvise)
{
    return ResultFromScode(OLE_E_ADVISENOTSUPPORTED);
}

#define TIDC_INVALID    -1
#define TIDC_COPY	0
#define TIDC_MAX	1

HRESULT CTemplateUIObj::QueryContextMenu(
                    HMENU hmenu,
                    UINT indexMenu,
                    UINT idCmdFirst,
                    UINT idCmdLast,
                    UINT uFlags)
{
    DebugMsg(DM_TRACE, TEXT("sc TR - CTUI::QCM called (uFlags=%x)"), uFlags);

    //
    // REVIEW: Checking CMF_DVFILE is subtle, need to be documented clearly!
    //
    if (!(uFlags & (CMF_VERBSONLY|CMF_DVFILE)))
    {
	MENUITEMINFO mii = {
	    sizeof(MENUITEMINFO),
	    MIIM_STATE|MIIM_ID|MIIM_TYPE,
	    MFT_STRING,
	    MFS_DEFAULT,
	    idCmdFirst+TIDC_COPY,
	    NULL, NULL, NULL, 0,
	    TEXT("&Copy"),	// FEATURE: Support NLS, the Copy operation might have a different name in other languages
	    5
	};
	InsertMenuItem(hmenu, indexMenu++, TRUE, &mii);
    }
    return ResultFromShort(TIDC_MAX);
}

HRESULT CTemplateUIObj::InvokeCommand(
                 LPCMINVOKECOMMANDINFO lpici)
{
    HRESULT hres;
    DebugMsg(DM_TRACE, TEXT("sc TR - CTUI::IC called (%x)"), lpici->lpVerb);
    int idCmd = TIDC_INVALID;

    if (HIWORD(lpici->lpVerb))
    {
	if (lstrcmpiA(lpici->lpVerb, "copy") == 0) {
	    idCmd = TIDC_COPY;
	}
    }
    else
    {
	idCmd = LOWORD(lpici->lpVerb);
    }

    switch(idCmd)
    {
    case TIDC_COPY:
	hres = OleSetClipboard(this);
	break;

    default:
	hres = E_INVALIDARG;
	break;
    }

    return hres;
}

HRESULT CTemplateUIObj::GetCommandString(
                                UINT        idCmd,
                                UINT        uType,
                                UINT      * pwReserved,
                                LPSTR       pszName,
                                UINT        cchMax)
{
    DebugMsg(DM_TRACE, TEXT("sc TR - CTUI::GCS called (%d, %x)"), idCmd, uType);
    return E_NOTIMPL;
}

#endif // FEATURE_SHELLEXTENSION