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

#define CLONE_IT_IF_READONLY
#define ARRAYSIZE(a)    (sizeof(a)/sizeof(a[0]))

class CShClientSite : public IOleClientSite, public IAdviseSink2
{
public:
    CShClientSite(HWND hwndOwner, LPCTSTR pszCmdLine);
    LPCTSTR  ParseCmdLine(LPCTSTR pszCmdLine);
    HRESULT DoVerb(LONG iVerb);
    void    CloseOleObject();
    void    ReleaseOleObject();
    void    ReleaseStorage(void);
    void    MaySaveAs(void);
    void    Draw(HWND hwnd, HDC hdc);
    void    GetFileName(LPTSTR szFile, UINT cchMax);
    void    Quit(void) { _hwndOwner = NULL ; _fQuit = TRUE; }
    BOOL    FContinue(void) { return !_fQuit; }

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

    // IOleClientSite
    virtual HRESULT STDMETHODCALLTYPE SaveObject(void);
    virtual HRESULT STDMETHODCALLTYPE GetMoniker(DWORD, DWORD, IMoniker **);
    virtual HRESULT STDMETHODCALLTYPE GetContainer(IOleContainer **);
    virtual HRESULT STDMETHODCALLTYPE ShowObject(void);
    virtual HRESULT STDMETHODCALLTYPE OnShowWindow(BOOL fShow);
    virtual HRESULT STDMETHODCALLTYPE RequestNewObjectLayout(void);

    // IAdviseSink2
    virtual void STDMETHODCALLTYPE OnDataChange(FORMATETC *,STGMEDIUM *);
    virtual void STDMETHODCALLTYPE OnViewChange(DWORD dwAspect,LONG lindex);
    virtual void STDMETHODCALLTYPE OnRename(IMoniker *pmk);
    virtual void STDMETHODCALLTYPE OnSave(void);
    virtual void STDMETHODCALLTYPE OnClose(void);
    virtual void STDMETHODCALLTYPE OnLinkSrcChange(IMoniker *pmk);

protected:
    ~CShClientSite();

    UINT                _cRef;
    HWND                _hwndOwner;
    LPSTORAGE           _pstgDoc;       // document
    LPSTORAGE           _pstg;          // the embedding (only one)
    LPPERSISTSTORAGE    _ppstg;
    LPOLEOBJECT         _pole;
    BOOL                _fDirty:1;
    BOOL                _fNeedToSave:1;
    BOOL                _fReadOnly:1;
    BOOL                _fCloned:1;
    BOOL                _fQuit:1;
    BOOL                _fCloseImmediately:1;
    DWORD               _dwConnection;  // non-zero, if valid
    WCHAR               _wszFileName[MAX_PATH];
};
typedef CShClientSite * LPSHCLIENTSITE;

const TCHAR c_szAppName[] = TEXT("ShellOleViewer");
LRESULT CALLBACK ShWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
void DisplayError(HWND hwndOwner, HRESULT hres, UINT idsMsg, LPCTSTR szFileName);

HINSTANCE g_hinst = NULL;

extern "C"
BOOL APIENTRY LibMain(HANDLE hDll, DWORD dwReason, LPVOID lpReserved)
{
    switch(dwReason) {
    case DLL_PROCESS_ATTACH:
        g_hinst = (HINSTANCE)hDll;
        DisableThreadLibraryCalls(g_hinst);
        break;

    default:
        break;
    }

    return TRUE;
}

extern "C" void WINAPI
OpenScrap_RunDLL_Common(HWND hwndStub, HINSTANCE hInstApp, LPTSTR pszCmdLine, int nCmdShow)
{
    WNDCLASS wc;

    // wc.cbSize        = sizeof(WNDCLASSEX);
    wc.style         = CS_DBLCLKS|CS_VREDRAW|CS_HREDRAW ;
    wc.lpfnWndProc   = ShWndProc ;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = sizeof(LPSHCLIENTSITE) ;
    wc.hInstance     = g_hinst ;
    wc.hIcon         = NULL ;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW) ;
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH) ;
    wc.lpszMenuName  = NULL ;
    wc.lpszClassName = c_szAppName ;
    // wc.hIconSm       = NULL;

    RegisterClass(&wc);

    HWND hwndClientSite = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_OVERLAPPEDWINDOW,
                                   c_szAppName,
#ifdef DEBUG
                                   TEXT("(Debug only) SHOLE.EXE"),
                                   WS_VISIBLE | WS_OVERLAPPEDWINDOW,
#else
                                   TEXT(""),
                                   WS_OVERLAPPEDWINDOW,
#endif
                                   CW_USEDEFAULT, CW_USEDEFAULT,
                                   128, 128, NULL, NULL, g_hinst, NULL);
    if (hwndClientSite)
    {
        HRESULT hres;

        hres = OleInitialize(NULL);
        if (SUCCEEDED(hres))
        {
            DWORD dwTick;
            LPSHCLIENTSITE pscs= new CShClientSite(hwndClientSite, pszCmdLine);

            if (pscs)
            {
                UINT cRef;

                hres = pscs->DoVerb(OLEIVERB_OPEN);

                if (hres == S_OK)
                {
                    MSG msg;
                    while (pscs->FContinue() && GetMessage(&msg, NULL, 0, 0))
                    {
                        TranslateMessage(&msg);
                        DispatchMessage(&msg);
                    }
                }
                else
                {
                    // DoVerb failed.

                    if (FAILED(hres) || (hres>=IDS_HRES_MIN && hres<IDS_HRES_MAX))
                    {
                        TCHAR szFile[MAX_PATH];
                        pscs->GetFileName(szFile, ARRAYSIZE(szFile));
                        DisplayError(hwndClientSite, hres, IDS_ERR_DOVERB, szFile);
                    }
                    DestroyWindow(hwndClientSite);
                }

                //
                //  We call them just in case, the following Release
                // does not release the object.
                //
                pscs->ReleaseOleObject();
                pscs->ReleaseStorage();
                pscs->MaySaveAs();

                cRef = pscs->Release();
                ASSERT(cRef==0);
            }

            DebugMsg(DM_TRACE, TEXT("so TR - WinMain About to call OleUninitialize"));
            dwTick = GetCurrentTime();
            OleUninitialize();
            DebugMsg(DM_TRACE, TEXT("so TR - WinMain OleUninitialize took %d ticks"), GetCurrentTime()-dwTick);
        }

        if (IsWindow(hwndClientSite)) {
            DebugMsg(DM_WARNING, TEXT("so WA - WinMain IsWindow(hwndClientSite) is still TRUE"));
            DestroyWindow(hwndClientSite);
        }
    }
}

extern "C" void WINAPI
OpenScrap_RunDLL(HWND hwndStub, HINSTANCE hAppInstance, LPSTR lpszCmdLine, int nCmdShow)
{
#ifdef UNICODE
    UINT iLen = lstrlenA(lpszCmdLine)+1;
    LPWSTR  lpwszCmdLine;

    lpwszCmdLine = (LPWSTR)LocalAlloc(LPTR,iLen*sizeof(WCHAR));
    if (lpwszCmdLine)
    {
        MultiByteToWideChar(CP_ACP, 0,
                            lpszCmdLine, -1,
                            lpwszCmdLine, iLen);
        OpenScrap_RunDLL_Common( hwndStub,
                                 hAppInstance,
                                 lpwszCmdLine,
                                 nCmdShow );
        LocalFree(lpwszCmdLine);
    }
#else
    OpenScrap_RunDLL_Common( hwndStub,
                             hAppInstance,
                             lpszCmdLine,
                             nCmdShow );
#endif
}

extern "C" void WINAPI
OpenScrap_RunDLLW(HWND hwndStub, HINSTANCE hAppInstance, LPWSTR lpwszCmdLine, int nCmdShow)
{
#ifdef UNICODE
    OpenScrap_RunDLL_Common( hwndStub,
                             hAppInstance,
                             lpwszCmdLine,
                             nCmdShow );
#else
    UINT iLen = WideCharToMultiByte(CP_ACP, 0,
                                    lpwszCmdLine, -1,
                                    NULL, 0, NULL, NULL)+1;
    LPSTR  lpszCmdLine;

    lpszCmdLine = (LPSTR)LocalAlloc(LPTR,iLen);
    if (lpszCmdLine)
    {
        WideCharToMultiByte(CP_ACP, 0,
                            lpwszCmdLine, -1,
                            lpszCmdLine, iLen,
                            NULL, NULL);
        OpenScrap_RunDLL_Common( hwndStub,
                                 hAppInstance,
                                 lpszCmdLine,
                                 nCmdShow );
        LocalFree(lpszCmdLine);
    }
#endif
}


#ifdef DEBUG
//
// Type checking
//
static RUNDLLPROC lpfnRunDLL=OpenScrap_RunDLL;
#endif

void DisplayError(HWND hwndOwner, HRESULT hres, UINT idsMsg, LPCTSTR pszFileName)
{
    TCHAR szErrMsg[MAX_PATH*2];
    TCHAR szFancyErr[MAX_PATH*2];
    HRSRC hrsrc;

    if (HIWORD(hres))
    {
        BOOL fSuccess = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY,
                  NULL,
                  hres,
                  0,
                  szErrMsg,
                  ARRAYSIZE(szErrMsg),
                  (va_list *)&pszFileName);
        if (!fSuccess) {
            idsMsg++;   // map IDS_ERR_DOVERB to IDS_ERR_DOVERB_F
        }
    } else {
        LoadString(g_hinst, LOWORD(hres), szErrMsg, ARRAYSIZE(szErrMsg));
    }

    szFancyErr[0] = TEXT('\0');
    hrsrc = FindResource(g_hinst, MAKEINTRESOURCE(IDR_FANCYERR), RT_RCDATA);
    if (hrsrc)
    {
        HGLOBAL hmem = LoadResource(g_hinst, hrsrc);
        if (hmem)
        {
            HRESULT* phres = (HRESULT*)LockResource(hmem);
            if (phres)
            {
                UINT i;
                LPTSTR pszLoad = szFancyErr;
                int cchLeft = ARRAYSIZE(szFancyErr);
                for (i=0; phres[i] && cchLeft>0; i++) {
                    if (phres[i] == hres)
                    {
                        int cchRead;
                        cchRead = LoadString(g_hinst, IDS_FANCYERR+i, pszLoad, cchLeft);
                        pszLoad += cchRead;
                        cchLeft -= cchRead;
                    }
                }

                //
                // If we have a fancy error message, hide ugly message
                // from FormatMessage.
                //
                if (szFancyErr[0]) {
                    szErrMsg[0] = TEXT('\0');
                }
            }
        }
    }

    ShellMessageBox(g_hinst,
                    hwndOwner,
                    MAKEINTRESOURCE(idsMsg),
                    MAKEINTRESOURCE(IDS_TITLE_ERR),
                    MB_OK | MB_ICONWARNING | MB_SETFOREGROUND,
                    pszFileName,
                    szErrMsg,
                    szFancyErr,
                    hres);
}


void CShClientSite::CloseOleObject()
{
    if (_pole)
        _pole->Close(OLECLOSE_NOSAVE);
}

void CShClientSite::ReleaseOleObject()
{
    UINT cRef;
    if (_pole)
    {
        if (_dwConnection) {
            _pole->Unadvise(_dwConnection);
            _dwConnection = 0;
        }
        _pole->SetClientSite(NULL);
        cRef = _pole->Release();
        DebugMsg(DM_TRACE, TEXT("so - TR SCS::ReleaseOleObject IOleObj::Rel returned (%d)"), cRef);
        _pole=NULL;
    }

    if (_ppstg)
    {
        cRef=_ppstg->Release();
        _ppstg=NULL;
        DebugMsg(DM_TRACE, TEXT("so TR - SCS::ReleaseOleObject IPSTG::Release returned (%x)"), cRef);
    }
}

void CShClientSite::ReleaseStorage(void)
{
    UINT cRef;

    if (_pstg)
    {
        cRef=_pstg->Release();
        _pstg=NULL;
        DebugMsg(DM_TRACE, TEXT("so TR - SCS::ReleaseStorage _pstg->Release returned (%x)"), cRef);
    }

    if (_pstgDoc)
    {
        cRef=_pstgDoc->Release();
        _pstgDoc=NULL;
        DebugMsg(DM_TRACE, TEXT("so TR - SCS::ReleaseStorage _pstgDoc->Release returned (%x)"), cRef);
    }
}

void CShClientSite::MaySaveAs()
{
    DebugMsg(DM_TRACE, TEXT("so TR - SCS::MaySaveAs called (%d,%d)"), _fCloned, _fNeedToSave);
    if (_fCloned)
    {
        TCHAR szTempFile[MAX_PATH];
#ifdef UNICODE
        lstrcpyn(szTempFile,_wszFileName,ARRAYSIZE(szTempFile));
#else
        WideCharToMultiByte(CP_ACP, 0, _wszFileName, -1, szTempFile, ARRAYSIZE(szTempFile), NULL, NULL);
#endif

        UINT id = IDNO;
        if (_fNeedToSave)
        {
            id= ShellMessageBox(g_hinst,
                        _hwndOwner,
                        MAKEINTRESOURCE(IDS_WOULDYOUSAVEAS),
                        MAKEINTRESOURCE(IDS_TITLE),
                        MB_YESNO | MB_ICONQUESTION | MB_SETFOREGROUND,
                        NULL);
        }

        DebugMsg(DM_TRACE, TEXT("so TR - SCS::MaySaveAs id==%d"), id);

        if (id==IDYES)
        {
            TCHAR szDesktop[MAX_PATH];
            SHGetSpecialFolderPath(NULL, szDesktop, CSIDL_DESKTOP, FALSE);

            BOOL fContinue;
            do
            {
                fContinue = FALSE;

                TCHAR szFile[MAX_PATH];
                TCHAR szFilter[64];
                szFile[0] = TEXT('\0');
                LoadString(g_hinst, IDS_SCRAPFILTER, szFilter, ARRAYSIZE(szFilter));

                OPENFILENAME of = {
                    sizeof(OPENFILENAME), // DWORD        lStructSize;
                    _hwndOwner,               // HWND         hwndOwner;
                    NULL,                     // HINSTANCE    hInstance;
                    szFilter,         // LPCSTR       lpstrFilter;
                    NULL,                     // LPSTR        lpstrCustomFilter;
                    0,                // DWORD        nMaxCustFilter;
                    1,                // DWORD        nFilterIndex;
                    szFile,           // LPSTR        lpstrFile;
                    ARRAYSIZE(szFile),    // DWORD        nMaxFile;
                    NULL,                     // LPSTR        lpstrFileTitle;
                    0,                // DWORD        nMaxFileTitle;
                    szDesktop,        // LPCSTR       lpstrInitialDir;
                    NULL,                     // LPCSTR       lpstrTitle;
                    OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT
                     | OFN_NOREADONLYRETURN | OFN_PATHMUSTEXIST,
                                          // DWORD        Flags;
                    0,                // WORD         nFileOffset;
                    0,                // WORD         nFileExtension;
                    TEXT("shs"),                      // LPCSTR       lpstrDefExt;
                    NULL,                     // LPARAM       lCustData;
                    NULL,                     // LPOFNHOOKPROC lpfnHook;
                    NULL,                     // LPCSTR       lpTemplateName;
                };

                if (GetSaveFileName(&of))
                {
                    DeleteFile(szFile);
                    BOOL fRet = MoveFile(szTempFile, szFile);
                    if (fRet)
                    {
                        // Indicated that the temp file is moved
                        szTempFile[0] = TEXT('\0');
                    }
                    else
                    {
                        id = ShellMessageBox(g_hinst,
                                _hwndOwner,
                                MAKEINTRESOURCE(IDS_MOVEFAILED),
                                MAKEINTRESOURCE(IDS_TITLE),
                                MB_YESNO | MB_ICONWARNING | MB_SETFOREGROUND);
                        if (id==IDYES)
                        {
                            fContinue = TRUE;
                        }
                    }
                }
            } while (fContinue);
        }

        // If the temp file is not moved, delete it.
        if (szTempFile[0])
        {
            DeleteFile(szTempFile);
        }
    }
}

void CShClientSite::Draw(HWND hwnd, HDC hdc)
{
#ifdef DEBUG
    if (_ppstg)
    {
        HRESULT hres;
        RECT rc;

        GetClientRect(hwnd, &rc);

        hres = OleDraw(_ppstg, DVASPECT_ICON, hdc, &rc);
        DebugMsg(DM_TRACE, TEXT("so TR - SCS::Draw OleDraw(DVASPECT_ICON) returned %x"), hres);

        if (FAILED(hres))
        {
            LPVIEWOBJECT2 pview;
            hres = _ppstg->QueryInterface(IID_IViewObject2, (LPVOID*)&pview);
            if (SUCCEEDED(hres))
            {
                SIZE size;
                hres = pview->GetExtent(DVASPECT_CONTENT, (DWORD)-1,
                                        (DVTARGETDEVICE*)NULL, &size);
                DebugMsg(DM_TRACE, TEXT("so TR - SCS::Draw IVO2::GetExtent returned %x"), hres);
                if (SUCCEEDED(hres))
                {
                    int mmOld = SetMapMode(hdc, MM_HIMETRIC);
                    LPtoDP(hdc, (LPPOINT)&size, 1);
                    rc.right = size.cx;
                    rc.bottom = -size.cy;
                    SetMapMode(hdc, mmOld);
                }
                pview->Release();
            }
            hres = OleDraw(_ppstg, DVASPECT_CONTENT, hdc, &rc);
            DebugMsg(DM_TRACE, TEXT("so TR - SCS::Draw OleDraw(DVASPECT_CONTENT,%d,%d) returned %x"),
                        hres, rc.right, rc.bottom);
        }

        LPOLELINK plink;
        if (SUCCEEDED(hres = _ppstg->QueryInterface(IID_IOleLink, (LPVOID *)&plink)))
        {
            LPOLESTR pwsz;
            hres = plink->GetSourceDisplayName(&pwsz);
            if (SUCCEEDED(hres))
            {
#ifdef UNICODE
                TextOut(hdc, 0, 0, pwsz, lstrlen(pwsz));
#else
                TCHAR szDisplayName[256] = TEXT("##ERROR##");
                WideCharToMultiByte(CP_ACP, 0, pwsz, -1, szDisplayName, ARRAYSIZE(szDisplayName), NULL, NULL);
                TextOut(hdc, 0, 0, szDisplayName, lstrlen(szDisplayName));
#endif
                CoTaskMemFree(pwsz);
            }
            else
            {
                DebugMsg(DM_TRACE, TEXT("so TR SCS:Draw IMK:GetSDN failed %x"), hres);
            }
            plink->Release();
        }
        else
        {
            DebugMsg(DM_TRACE, TEXT("so TR SCS:Draw IPSTG:QI failed %x"), hres);
        }
    }
#endif
}

STDMETHODIMP CShClientSite::QueryInterface(REFIID riid,
        void **ppvObject)
{
    HRESULT hres;
    if (IsEqualGUID(riid, IID_IOleClientSite) || IsEqualGUID(riid, IID_IUnknown)) {
        _cRef++;
        *ppvObject = (LPOLECLIENTSITE)this;
        hres = NOERROR;
    }
    else if (IsEqualGUID(riid, IID_IAdviseSink) || IsEqualGUID(riid, IID_IAdviseSink2)) {
        _cRef++;
        *ppvObject = (LPADVISESINK2)this;
        hres = NOERROR;
    }
    else
    {
        *ppvObject = NULL;
        hres = ResultFromScode(E_NOINTERFACE);
    }
    return hres;
}

STDMETHODIMP_(ULONG) CShClientSite::AddRef(void)
{
    return ++_cRef;
}

STDMETHODIMP_(ULONG) CShClientSite::Release(void)
{
    if (--_cRef>0) {
        return _cRef;
    }

    delete this;
    return 0;
}


void Scrap_UpdateCachedData(LPSTORAGE pstgDoc, LPOLEOBJECT pole, LPPERSIST pps)
{
    extern void Scrap_CacheClipboardData(LPSTORAGE pstgDoc, LPDATAOBJECT pdtobj, LPPERSIST pps);
    DebugMsg(DM_TRACE, TEXT("so TR - S_UCD called"));
    if (pstgDoc && pole && pps)
    {
        IDataObject *pdtobj = NULL;
        HRESULT hres = pole->QueryInterface(IID_IDataObject, (LPVOID*)&pdtobj);
        if (SUCCEEDED(hres)) {
            DebugMsg(DM_TRACE, TEXT("so TR - S_UCD QI succeeded"));
            Scrap_CacheClipboardData(pstgDoc, pdtobj, pps);
            pdtobj->Release();
        }
    }
}

STDMETHODIMP CShClientSite::SaveObject(void)
{
    DebugMsg(DM_TRACE, TEXT("sc TR - CSCS::SaveObject called"));
    //
    // NOTES: We need to update the cache here.
    //  Doing so on ::OnSave does not work (async)
    //  Doing so on ::OnClose is too late.
    //
    Scrap_UpdateCachedData(_pstgDoc, _pole, _ppstg);

    HRESULT hres;
    if (_pstg && _ppstg)
    {
        hres = OleSave(_ppstg, _pstg, TRUE);
        if (SUCCEEDED(hres))
        {
            hres = _ppstg->SaveCompleted(NULL);
        }
    }
    else
    {
        hres = ResultFromScode(E_FAIL);
    }
    return hres;
}

STDMETHODIMP CShClientSite::GetMoniker(DWORD dwAssign,
    DWORD dwWhichMoniker,
    IMoniker **ppmk)
{
    HRESULT hres;

    *ppmk = NULL;

    switch(dwWhichMoniker)
    {
    case OLEWHICHMK_CONTAINER:
        hres = CreateFileMoniker(_wszFileName, ppmk);
        break;

    case OLEWHICHMK_OBJREL:
        hres = CreateItemMoniker(L"\\", L"Object", ppmk);
        break;

    case OLEWHICHMK_OBJFULL:
        {
            LPMONIKER pmkItem;
            hres = CreateItemMoniker(L"\\", L"Object", &pmkItem);
            if (SUCCEEDED(hres))
            {
                LPMONIKER pmkDoc;
                hres = CreateFileMoniker(_wszFileName, &pmkDoc);
                if (SUCCEEDED(hres))
                {
                    hres = CreateGenericComposite(pmkDoc, pmkItem, ppmk);
                    pmkDoc->Release();
                }
                pmkItem->Release();
            }
        }
        break;

    default:
        hres = ResultFromScode(E_INVALIDARG);
    }

    return hres;
}

STDMETHODIMP CShClientSite::GetContainer(
    IOleContainer **ppContainer)
{
    *ppContainer = NULL;
    return ResultFromScode(E_NOINTERFACE);
}

STDMETHODIMP CShClientSite::ShowObject(void)
{
    return NOERROR;
}

STDMETHODIMP CShClientSite::OnShowWindow(BOOL fShow)
{
    DebugMsg(DM_TRACE, TEXT("so TR - CSCS::OnShowWindow called with %d"), fShow);
    return NOERROR;
}

STDMETHODIMP CShClientSite::RequestNewObjectLayout(void)
{
    return ResultFromScode(E_NOTIMPL);
}

//
// _cRef <- 2 because _hwndOwner has a reference count as well.
//
CShClientSite::CShClientSite(HWND hwndOwner, LPCTSTR pszCmdLine)
                : _cRef(2), _hwndOwner(hwndOwner),
                  _pstgDoc(NULL), _pstg(NULL), _ppstg(NULL), _pole(NULL),
                  _fDirty(FALSE), _fNeedToSave(FALSE),
                  _fReadOnly(FALSE), _fCloned(FALSE), _fCloseImmediately(FALSE),
                  _fQuit(FALSE)
{
    LPCTSTR pszFileName = ParseCmdLine(pszCmdLine);

//
// We'd better deal with quoted LFN name.
//
#ifdef NASHVILLE
    //
    // Strip out quotes if exists.
    //
    TCHAR szT[MAX_PATH];
    if (*pszFileName==TEXT('"'))
    {
        lstrcpy(szT, pszFileName+1);
        LPTSTR pszT = CharPrev(szT, szT+lstrlen(szT));
        if (*pszT==TEXT('"')) {
            *pszT=TEXT('\0');
        }
        pszFileName = szT;
    }
#endif // NASHVILLE

#ifdef UNICODE
    lstrcpyn(_wszFileName, pszFileName, ARRAYSIZE(_wszFileName));
#else
    MultiByteToWideChar(CP_ACP, 0, pszFileName, lstrlen(pszFileName)+1,
                        _wszFileName, ARRAYSIZE(_wszFileName));
#endif

    ASSERT(_hwndOwner)
    SetWindowLongPtr(_hwndOwner, GWLP_USERDATA, (LONG_PTR)this);
}

CShClientSite::~CShClientSite()
{
    ReleaseOleObject();
    ReleaseStorage();
}

LPCTSTR _SkipSpace(LPCTSTR psz)
{
    while(*psz==TEXT(' '))
        psz++;
    return psz;
}

LPCTSTR CShClientSite::ParseCmdLine(LPCTSTR pszCmdLine)
{
    for (LPCTSTR psz = _SkipSpace(pszCmdLine);
         (*psz == TEXT('/') || *psz == TEXT('-')) && *++psz;
         psz = _SkipSpace(psz))
    {
        switch(*psz++)
        {
        case TEXT('r'):
        case TEXT('R'):
            _fReadOnly = TRUE;
            break;

        case TEXT('x'):
        case TEXT('X'):
            _fCloseImmediately = TRUE;
            break;
        }
    }

    return psz;
}

void CShClientSite::GetFileName(LPTSTR szFile, UINT cchMax)
{
#ifdef UNICODE
    lstrcpyn(szFile, _wszFileName, cchMax);
#else
    WideCharToMultiByte(CP_ACP, 0, _wszFileName, -1, szFile, cchMax, NULL, NULL);
#endif
}
const WCHAR c_wszContents[] = WSTR_SCRAPITEM;

//
// Returns:
//      S_OK, succeeded. Start the message loop.
//      S_FALSE, succeeded. Release the object.
//      Others, failed.
//
HRESULT CShClientSite::DoVerb(LONG iVerb)
{
    HRESULT hres;
    DWORD wStgm;

    // Must be called only once.
    if (_pstgDoc) {
        return ResultFromScode(E_UNEXPECTED);
    }

    wStgm = _fReadOnly ?
                (STGM_READ | STGM_SHARE_DENY_WRITE) :
                (STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE);
    hres = StgIsStorageFile(_wszFileName);
    if (hres != S_OK)
    {
        if (hres==S_FALSE) {
            hres = IDS_HRES_INVALID_SCRAPFILE;
        }
        return hres;
    }

    hres = StgOpenStorage(_wszFileName, NULL, wStgm, NULL, 0, &_pstgDoc);

#ifndef CLONE_IT_IF_READONLY
    //
    //  If we are opening without read-only flag and StgOpenStorage failed
    // with STG_E_ACCESSDENIED, retry it with read-only mode.
    //
    if ((hres==STG_E_ACCESSDENIED) && !_fReadOnly)
    {
        DebugMsg(DM_TRACE, TEXT("so TR - CSCS::DoVerb first StgOpenStorage failed, retrying it in read-only mode"));
        _fReadOnly = TRUE;
        wStgm = (STGM_READ | STGM_SHARE_DENY_WRITE);
        hres = StgOpenStorage(_wszFileName, NULL, wStgm, NULL, 0, &_pstgDoc);
    }
#else // CLONE_IT_IF_READONLY
    //
    //  If we are opening without read-only flag and StgOpenStorage failed
    // with STG_E_ACCESSDENIED, retry it with read-only mode.
    //
    if ((hres==STG_E_ACCESSDENIED) && !_fReadOnly)
    {
        LPSTORAGE pstgRead;
        DebugMsg(DM_TRACE, TEXT("so TR - CSCS::DoVerb first StgOpenStorage failed, retrying it in read-only mode"));
        hres = StgOpenStorage(_wszFileName, NULL, STGM_READ | STGM_SHARE_DENY_WRITE, NULL, 0, &pstgRead);
        if (SUCCEEDED(hres))
        {
            TCHAR szDesktop[MAX_PATH];
            TCHAR szTempFile[MAX_PATH];
            SHGetSpecialFolderPath(_hwndOwner, szDesktop, CSIDL_DESKTOP, FALSE);
            GetTempFileName(szDesktop, TEXT("Sh"), 0, szTempFile);
#ifdef UNICODE
            lstrcpyn(_wszFileName,szTempFile,ARRAYSIZE(szTempFile));
#else
            MultiByteToWideChar(CP_ACP, 0, szTempFile, -1, _wszFileName, ARRAYSIZE(_wszFileName));
#endif

            hres = StgCreateDocfile(_wszFileName,
                            STGM_DIRECT | STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
                            0, &_pstgDoc);
            if (SUCCEEDED(hres))
            {
                hres = pstgRead->CopyTo(0, NULL, NULL, _pstgDoc);
                _pstgDoc->Release();
                _pstgDoc = NULL;

                if (SUCCEEDED(hres))
                {
                    hres = StgOpenStorage(_wszFileName, NULL, wStgm, NULL, 0, &_pstgDoc);
                    if (SUCCEEDED(hres))
                    {
                        _fCloned = TRUE;
                    }
                }
                else
                {
                    DeleteFile(szTempFile);
                }
            }
            pstgRead->Release();
        }
    }
#endif // CLONE_IT_IF_READONLY

    if (SUCCEEDED(hres))
    {
        if (_fReadOnly) {
            wStgm = STGM_READ|STGM_SHARE_EXCLUSIVE;
        }
        hres = _pstgDoc->OpenStorage(c_wszContents, NULL, wStgm, NULL, 0, &_pstg);
        if (SUCCEEDED(hres))
        {
            hres = OleLoad(_pstg, IID_IPersistStorage, this, (LPVOID *)&_ppstg);

            if (SUCCEEDED(hres)) {
                hres = _ppstg->QueryInterface(IID_IOleObject, (LPVOID *)&_pole);

                if (SUCCEEDED(hres))
                {
                    hres = _pole->Advise(this, &_dwConnection);
                    DebugMsg(DM_TRACE, TEXT("so TR - CSCS::DoVerb IOleObject::Advise returned %x"), hres);
                    if (SUCCEEDED(hres))
                    {
                        TCHAR szTitle[MAX_PATH];
                        WCHAR wszTitle[MAX_PATH];
                        LoadString(g_hinst, IDS_TITLE, szTitle, ARRAYSIZE(szTitle));
#ifdef UNICODE
                        lstrcpyn(wszTitle,szTitle,ARRAYSIZE(wszTitle));
#else
                        MultiByteToWideChar(CP_ACP, 0, szTitle, lstrlen(szTitle)+1,
                                wszTitle, ARRAYSIZE(wszTitle));
#endif

                        GetFileName(szTitle, ARRAYSIZE(szTitle));
                        LPCWSTR pwszDisplayName = _wszFileName;
#ifndef UNICODE
                        WCHAR wszDisplayName[MAX_PATH];
#endif
                        SHFILEINFO info;
                        DWORD result = (DWORD)SHGetFileInfo(szTitle, 0,
                            &info, sizeof(info), SHGFI_DISPLAYNAME);

                        if(result && *info.szDisplayName)
                        {
#ifdef UNICODE
                            pwszDisplayName = info.szDisplayName;
#else
                            MultiByteToWideChar(CP_ACP, 0,
                                    info.szDisplayName, -1,
                                    wszDisplayName, ARRAYSIZE(wszDisplayName));
                            pwszDisplayName = wszDisplayName;
#endif
                        }

                        _pole->SetHostNames(wszTitle, pwszDisplayName);

                        //
                        // OLEBUG? Unless _hwndOwner has the input focus, 16-bit
                        //  server won't get the input focus.
                        //
                        SetFocus(_hwndOwner);

                        hres = _pole->DoVerb(iVerb, NULL, this, 0, _hwndOwner, NULL);
                        DebugMsg(DM_TRACE, TEXT("so TR - CSCS::DoVerb IOleObject::DoVerb returned %x"), hres);
                        if (SUCCEEDED(hres) && _fCloseImmediately) {
                            hres = S_FALSE;
                        }
                    }
                }
                else
                {
                    DebugMsg(DM_TRACE, TEXT("so ER - CSCS::DoVerb IPSTG::QI failed %x"), hres);
                }
            }
            else
            {
                DebugMsg(DM_TRACE, TEXT("so ER - CSCS::DoVerb OleLoad failed %x"), hres);
            }
        }
        else
        {
            DebugMsg(DM_TRACE, TEXT("so ER - CSCS::DoVerb _pstgDoc->OpenStorage failed %x"), hres);

            //
            // Notes: If we just return this hres as is, the user will see
            //  "Can't open file, FOO.SHS", which is bogus. We need to
            //  translate it into a much informative message.
            //
            hres = IDS_HRES_INVALID_SCRAPFILE;
        }
    }
    else
    {
        DebugMsg(DM_TRACE, TEXT("so ER - CSCS::DoVerb StgOpenStg failed %x"), hres);
    }

    return hres;
}

STDMETHODIMP_(void) CShClientSite::OnDataChange(FORMATETC *pFormatetc, STGMEDIUM *pStgmed)
{
    DebugMsg(DM_TRACE, TEXT("so TR - CSCS::OnDataChange called"));
    _fDirty = TRUE;
}

STDMETHODIMP_(void) CShClientSite::OnViewChange(DWORD dwAspect, LONG lindex)
{
    DebugMsg(DM_TRACE, TEXT("so TR - CSCS::OnViewChange called"));
    _fDirty = TRUE;
}

STDMETHODIMP_(void) CShClientSite::OnRename(IMoniker *pmk)
{
    DebugMsg(DM_TRACE, TEXT("so TR - CSCS::OnRename called"));
    _fDirty = TRUE;
}

STDMETHODIMP_(void) CShClientSite::OnSave(void)
{
    DebugMsg(DM_TRACE, TEXT("so TR - CSCS::OnSave called"));
    _fNeedToSave = TRUE;
}

STDMETHODIMP_(void) CShClientSite::OnClose(void)
{
    DebugMsg(DM_TRACE, TEXT("so TR - CSCS::OnClose called"));
    if (_fNeedToSave /* && _fDirty */)
    {
        HRESULT hres;
        hres=OleSave(_ppstg, _pstg, TRUE);      // fSameStorage=TRUE
        DebugMsg(DM_TRACE, TEXT("so TR - CSCS:OnClose OleSave returned (%x)"), hres);
        hres=_ppstg->HandsOffStorage();
        DebugMsg(DM_TRACE, TEXT("so TR - CSCS:OnClose IPS:HandsOffStorage returned (%x)"), hres);
        if (SUCCEEDED(hres))
        {
            hres = _pstg->Commit(STGC_OVERWRITE);
            DebugMsg(DM_TRACE, TEXT("so TR - CSCS:OnClose _psg->Commit returned (%x)"), hres);
            hres = _pstgDoc->Commit(STGC_OVERWRITE);
            DebugMsg(DM_TRACE, TEXT("so TR - CSCS:OnClose _psgDoc->Commit returned (%x)"), hres);
        }
    }

    //
    // WARNING:
    //
    //  OLE1 server pukes if we release object here. However, we need to
    // call IOleObject::UnAdvice and IOleObject::SetClientSite(NULL) here
    // to avoid memory leak (RPC keeps 3 reference counts to IOleClientSite
    // if we delay it as well).
    //
    // ReleaseOleObject();
    //
    if (_dwConnection) {
        _pole->Unadvise(_dwConnection);
        _dwConnection = 0;
    }
    _pole->SetClientSite(NULL);

    PostMessage(_hwndOwner, WM_USER, 0, 0);
}

STDMETHODIMP_(void) CShClientSite::OnLinkSrcChange
(
    IMoniker *pmk
)
{
    DebugMsg(DM_TRACE, TEXT("so TR - CSCS::OnLinkSrcChange called"));
    _fDirty = TRUE;
}

LRESULT CALLBACK ShWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;
    HDC hdc;
    LPSHCLIENTSITE pscs = (LPSHCLIENTSITE)GetWindowLongPtr(hwnd, GWLP_USERDATA);

    switch(uMsg)
    {
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        if (pscs)
        {
            pscs->Draw(hwnd, hdc);
        }
        EndPaint(hwnd, &ps);
        break;

    case WM_CLOSE:
        if (pscs)
        {
            pscs->CloseOleObject();
            DestroyWindow(hwnd);
        }
        break;

    case WM_USER:
        if (pscs)
        {
            pscs->ReleaseOleObject();
            PostMessage(hwnd, WM_CLOSE, 0, 0);
        }
        break;

    case WM_DESTROY:
        DebugMsg(DM_WARNING, TEXT("so WA - ShWndProc processing WM_DESTROY"));
        if (pscs)
        {
            pscs->Quit();
            pscs->Release();
            SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
        }
        else
        {
            DebugMsg(DM_WARNING, TEXT("so WA - ShWndProc pscs==NULL on WM_DESTROY"));
        }

#if 0
        //
        // Process all the pending messages, before we post WM_QUIT message.
        //
        MSG msg;
        while (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
#endif

        break;

    default:
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    return 0;
}
