#include "privcpp.h"

#define CPP_FUNCTIONS
// #include <crtfree.h>

UINT    g_cfFileContents;
UINT    g_cfFileDescriptor;
UINT    g_cfObjectDescriptor;
UINT    g_cfEmbedSource;
UINT    g_cfFileNameW;

INT     g_cxIcon;
INT     g_cyIcon;
INT     g_cxArrange;
INT     g_cyArrange;
HFONT   g_hfontTitle;

static TCHAR szUserType[] = TEXT("Package");
static TCHAR szDefTempFile[] = TEXT("PKG");

CPackage::CPackage() : 
    _cRef(1)
{
    DebugMsg(DM_TRACE, "pack - CPackage() called.");
    g_cRefThisDll++;

    // Excel v.5 - v2000 has a hosting bug when they host an object as a link.
    // They always remove their hpmbed->hpobj object, yet all their methods
    // on the IOleClientSite interface they give us dereference this and fault.
    //
    TCHAR szProcess[MAX_PATH];
    if (GetModuleFileName(NULL, szProcess, ARRAYSIZE(szProcess)) &&
        !lstrcmp(TEXT("EXCEL.EXE"), PathFindFileName(szProcess)))
        _fNoIOleClientSiteCalls = TRUE;
    
    ASSERT(_cf == 0);
    ASSERT(_panetype == NOTHING);
    ASSERT(_pEmbed == NULL);
    ASSERT(_pCml == NULL);
    ASSERT(_fLoaded == FALSE);
    
    ASSERT(_lpszContainerApp == NULL);
    ASSERT(_lpszContainerObj == NULL);
    
    ASSERT(_fIsDirty == FALSE);
    
    ASSERT(_pIStorage == NULL);       
    ASSERT(_pstmFileContents == NULL);
    ASSERT(_pstm == NULL);
    
    ASSERT(_pIPersistStorage == NULL);
    ASSERT(_pIDataObject == NULL);
    ASSERT(_pIOleObject == NULL);    
    ASSERT(_pIViewObject2 == NULL);
    ASSERT(_pIAdviseSink == NULL);
    ASSERT(_pIRunnableObject == NULL);
        
    ASSERT(_pIDataAdviseHolder == NULL);
    ASSERT(_pIOleAdviseHolder == NULL);
    ASSERT(_pIOleClientSite == NULL);
    
    ASSERT(_pViewSink == NULL);
    ASSERT(_dwViewAspects == 0);
    ASSERT(_dwViewAdvf == 0);

    ASSERT(_cVerbs == 0);
    ASSERT(_nCurVerb == 0);
    ASSERT(_pVerbs == NULL);
    ASSERT(_pcm == NULL);
}


CPackage::~CPackage()
{
    DebugMsg(DM_TRACE, "pack - ~CPackage() called.");
   
    // We should never be destroyed unless our ref count is zero.
    ASSERT(_cRef == 0);
    
    g_cRefThisDll--;
    
    // Destroy our interfaces...
    //
    delete _pIOleObject;
    delete _pIViewObject2;
    delete _pIDataObject;
    delete _pIPersistStorage;
    delete _pIAdviseSink;
    delete _pIRunnableObject;

    // Destroy the packaged file structure...
    //
    DestroyIC();
    
    // we destroy depending on which type of object we had packaged
    switch(_panetype)
    {
    case PEMBED:
        if (_pEmbed->pszTempName) {
            DeleteFile(_pEmbed->pszTempName);
            delete _pEmbed->pszTempName;
        }
        delete _pEmbed;
        break;
        
    case CMDLINK:
        delete _pCml;
        break;

    }
    
    // Release Advise pointers...
    //
    if (_pIDataAdviseHolder)
        _pIDataAdviseHolder->Release();
    if (_pIOleAdviseHolder)
        _pIOleAdviseHolder->Release();
    if (_pIOleClientSite)
        _pIOleClientSite->Release();


    // Release Storage pointers...
    //
    if (_pIStorage)
        _pIStorage->Release();
    if (_pstmFileContents)
        _pstmFileContents->Release();
    if (_pstm)
        _pstm->Release();
    
    delete _lpszContainerApp;
    delete _lpszContainerObj;

    ReleaseContextMenu();
    if (NULL != _pVerbs)
    {
        for (ULONG i = 0; i < _cVerbs; i++)
        {
            delete _pVerbs[i].lpszVerbName;
        }
        delete _pVerbs;
    }
    
    DebugMsg(DM_TRACE,"CPackage being destroyed. _cRef == %d",_cRef);
}

HRESULT CPackage::Init() 
{
    // 
    // initializes parts of a package object that have a potential to fail
    // return:  S_OK            -- everything initialized
    //          E_FAIL          -- error in initialzation
    //          E_OUTOFMEMORY   -- out of memory
    //
    
    DebugMsg(DM_TRACE, "pack - Init() called.");

    // Initialize all the interfaces...
    //
    if (!(_pIOleObject        = new CPackage_IOleObject(this)))
        return E_OUTOFMEMORY;
    if (!(_pIViewObject2      = new CPackage_IViewObject2(this)))
        return E_OUTOFMEMORY;
    if (!(_pIDataObject       = new CPackage_IDataObject(this)))
        return E_OUTOFMEMORY;
    if (!(_pIPersistStorage   = new CPackage_IPersistStorage(this)))
        return E_OUTOFMEMORY;
    if (!(_pIPersistFile      = new CPackage_IPersistFile(this)))
        return E_OUTOFMEMORY;
    if (!(_pIAdviseSink       = new CPackage_IAdviseSink(this)))
        return E_OUTOFMEMORY;
    if (!(_pIRunnableObject   = new CPackage_IRunnableObject(this)))
        return E_OUTOFMEMORY;
    
    // Get some system metrics that we'll need later...
    //
    LOGFONT lf;
    SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, FALSE);
    SystemParametersInfo(SPI_ICONHORIZONTALSPACING, 0, &g_cxArrange, FALSE);
    SystemParametersInfo(SPI_ICONVERTICALSPACING, 0, &g_cyArrange, FALSE);
    g_cxIcon = GetSystemMetrics(SM_CXICON);
    g_cyIcon = GetSystemMetrics(SM_CYICON);
    g_hfontTitle = CreateFontIndirect(&lf);
    
    // register some clipboard formats that we support...
    //
    g_cfFileContents    = RegisterClipboardFormat(CFSTR_FILECONTENTS);
    g_cfFileDescriptor  = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
    g_cfObjectDescriptor= RegisterClipboardFormat(CFSTR_OBJECTDESCRIPTOR);
    g_cfEmbedSource     = RegisterClipboardFormat(CFSTR_EMBEDSOURCE);
    g_cfFileNameW       = RegisterClipboardFormat(TEXT("FileNameW"));
    
    // Initialize a generic icon
    _lpic = IconCreate();
    IconRefresh();
   

    return S_OK;
}

////////////////////////////////////////////////////////////////////////
//
// IUnknown Methods...
//
////////////////////////////////////////////////////////////////////////

HRESULT CPackage::QueryInterface(REFIID iid, void ** ppvObj)
{
    DebugMsg(DM_TRACE, "pack - QueryInterface() called.");
    
    if (iid == IID_IUnknown) {
        DebugMsg(DM_TRACE, "         getting IID_IUnknown");
        *ppvObj = (void *)this;
    }
    else if (iid == IID_IOleObject) {
        DebugMsg(DM_TRACE, "         getting IID_IOleObject");
        *ppvObj = (void *)_pIOleObject;
    }
    else if ((iid == IID_IViewObject2) || (iid == IID_IViewObject)) {
        DebugMsg(DM_TRACE, "         getting IID_IViewObject");
        *ppvObj = (void *)_pIViewObject2;
    }
    else if (iid == IID_IDataObject) {
        DebugMsg(DM_TRACE, "         getting IID_IDataObject");
        *ppvObj = (void *)_pIDataObject;
    }
    else if ((iid == IID_IPersistStorage) || (iid == IID_IPersist)) {
        DebugMsg(DM_TRACE, "         getting IID_IPersistStorage");
        *ppvObj = (void *)_pIPersistStorage;
    }
    else if (iid == IID_IPersistFile) {
        DebugMsg(DM_TRACE, "         getting IID_IPersistFile");
        *ppvObj = (void *)_pIPersistFile;
    }
    else if (iid == IID_IAdviseSink) {
        DebugMsg(DM_TRACE, "         getting IID_IAdviseSink");
        *ppvObj = (void *)_pIAdviseSink;
    }
    else if (iid == IID_IRunnableObject) {
        DebugMsg(DM_TRACE, "         getting IID_IRunnableObject");
        *ppvObj = (void *)_pIRunnableObject;
    }
    else if (iid == IID_IEnumOLEVERB)
    {
        DebugMsg(DM_TRACE, "         getting IID_IEnumOLEVERB");
        *ppvObj = (IEnumOLEVERB*) this;
    }
    else {
        *ppvObj = NULL;
        return E_NOINTERFACE;
    }

    ((LPUNKNOWN)*ppvObj)->AddRef();
    return S_OK;
}

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

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

    delete this;
    return 0;
}

HRESULT CPackage_CreateInstnace(LPUNKNOWN * ppunk)
{
    DebugMsg(DM_TRACE, "pack - CreateInstance called");
    
    *ppunk = NULL;              // null the out param
    
    CPackage* pPack = new CPackage;
    if (!pPack)  
        return E_OUTOFMEMORY;
    
    if (FAILED(pPack->Init())) {
        delete pPack;
        return E_OUTOFMEMORY;
    }

    *ppunk = pPack;
    return S_OK;
}

STDMETHODIMP CPackage::Next(ULONG celt, OLEVERB* rgVerbs, ULONG* pceltFetched)
{
    HRESULT hr;
    if (NULL != rgVerbs)
    {
        if (1 == celt)
        {
            if (_nCurVerb < _cVerbs)
            {
                ASSERT(NULL != _pVerbs);
                *rgVerbs = _pVerbs[_nCurVerb];
                if ((NULL != _pVerbs[_nCurVerb].lpszVerbName) &&
                    (NULL != (rgVerbs->lpszVerbName = (LPWSTR) CoTaskMemAlloc(
                        (lstrlenW(_pVerbs[_nCurVerb].lpszVerbName) + 1) * SIZEOF(WCHAR)))))
                {
                    StrCpyW(rgVerbs->lpszVerbName, _pVerbs[_nCurVerb].lpszVerbName);
                }
                _nCurVerb++;
                hr = S_OK;
            }
            else
            {
                hr = S_FALSE;
            }
            if (NULL != pceltFetched)
            {
                *pceltFetched = (S_OK == hr) ? 1 : 0;
            }
        }
        else if (NULL != pceltFetched)
        {
            int cVerbsToCopy = min(celt, _cVerbs - _nCurVerb);
            if (cVerbsToCopy > 0)
            {
                ASSERT(NULL != _pVerbs);
                CopyMemory(rgVerbs, &(_pVerbs[_nCurVerb]), cVerbsToCopy * sizeof(OLEVERB));
                for (int i = 0; i < cVerbsToCopy; i++)
                {
                    if ((NULL != _pVerbs[_nCurVerb + i].lpszVerbName) &&
                        (NULL != (rgVerbs[i].lpszVerbName = (LPWSTR) CoTaskMemAlloc(
                            (lstrlenW(_pVerbs[_nCurVerb + i].lpszVerbName) + 1) * SIZEOF(WCHAR)))))
                    {
                        StrCpyW(rgVerbs[i].lpszVerbName, _pVerbs[_nCurVerb + i].lpszVerbName);
                    }
                }
                _nCurVerb += cVerbsToCopy;
            }
            *pceltFetched = (ULONG) cVerbsToCopy;
            hr = (celt == (ULONG) cVerbsToCopy) ? S_OK : S_FALSE;
        }
        else
        {
            hr = E_INVALIDARG;
        }
    }
    else
    {
        hr = E_INVALIDARG;
    }
    return hr;
}

STDMETHODIMP CPackage::Skip(ULONG celt)
{
    if (_nCurVerb + celt > _cVerbs)
    {
        // there aren't enough elements, go to the end and return S_FALSE
        _nCurVerb = _cVerbs;
        return S_FALSE;
    }
    else
    {
        _nCurVerb += celt;
        return S_OK;
    }
}

STDMETHODIMP CPackage::Reset()
{
    _nCurVerb = 0;
    return S_OK;
}

STDMETHODIMP CPackage::Clone(IEnumOLEVERB** ppEnum)
{
    if (NULL != ppEnum)
    {
        *ppEnum = NULL;
    }
    return E_NOTIMPL;
}

///////////////////////////////////////////////////////////////////
//
// Package helper functions
//
///////////////////////////////////////////////////////////////////

HRESULT  CPackage::EmbedInitFromFile(LPCTSTR lpFileName, BOOL fInitFile) 
{
    //
    // get's the file size of the packaged file and set's the name 
    // of the packaged file if fInitFile == TRUE.
    // return:  S_OK    -- initialized ok
    //          E_FAIL  -- error initializing file
    //
    
    DWORD dwSize;

    
    // if this is the first time we've been called, then we need to allocate
    // memory for the _pEmbed structure
    if (_pEmbed == NULL) 
    {
        _pEmbed = new EMBED;
        if (_pEmbed)
        {
            _pEmbed->pszTempName = NULL;
            _pEmbed->hTask = NULL;
            _pEmbed->poo = NULL;
            _pEmbed->fIsOleFile = TRUE;
        }
    }

    if (_pEmbed == NULL)
        return E_OUTOFMEMORY;

    
    // open the file to package...
    //
    HANDLE fh = CreateFile(lpFileName, GENERIC_READ, FILE_SHARE_READWRITE, 
            NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); 
    if (fh == INVALID_HANDLE_VALUE) 
    {
        DWORD dwError = GetLastError();
        return E_FAIL;
    }

    _panetype = PEMBED;
    
    // Get the size of the file
    _pEmbed->fd.nFileSizeLow = GetFileSize(fh, &dwSize);
    if (_pEmbed->fd.nFileSizeLow == 0xFFFFFFFF) 
    {
        DWORD dwError = GetLastError();
        return E_FAIL;
    }
    ASSERT(dwSize == 0);
    _pEmbed->fd.nFileSizeHigh = 0L;
    _pEmbed->fd.dwFlags = FD_FILESIZE;

    // We only want to set the filename if this is the file to be packaged.
    // If it's only a temp file that we're reloading (fInitFile == FALSE) then
    // don't bother setting the filename.
    //
    if (fInitFile) 
    {
        lstrcpy(_pEmbed->fd.cFileName,lpFileName);
        DestroyIC();
        _lpic = IconCreateFromFile(lpFileName);
        if (_pIDataAdviseHolder)
            _pIDataAdviseHolder->SendOnDataChange(_pIDataObject,0, NULL);
        if (_pViewSink)
            _pViewSink->OnViewChange(_dwViewAspects,_dwViewAdvf);
    }

    _fIsDirty = TRUE;
    CloseHandle(fh);
    return S_OK;
}    

HRESULT CPackage::CmlInitFromFile(LPTSTR lpFileName, BOOL fUpdateIcon, PANETYPE paneType) 
{
    // if this is the first time we've been called, then we need to allocate
    // memory for the _pCml structure
    if (_pCml == NULL) 
    {
        _pCml = new CML;
        if (_pCml)
        {
            // we don't use this, but an old packager accessing us might.
            _pCml->fCmdIsLink = FALSE;
        }
    }

    if (_pCml == NULL)
        return E_OUTOFMEMORY;

    _panetype = paneType;
    lstrcpy(_pCml->szCommandLine,lpFileName);
    _fIsDirty = TRUE;
    
    if (fUpdateIcon)
    {
        DestroyIC();
        _lpic = IconCreateFromFile(lpFileName);
    
        if (_pIDataAdviseHolder)
            _pIDataAdviseHolder->SendOnDataChange(_pIDataObject,0, NULL);
    
        if (_pViewSink)
            _pViewSink->OnViewChange(_dwViewAspects,_dwViewAdvf);
    }
    return S_OK;
}    


HRESULT CPackage::InitFromPackInfo(LPPACKAGER_INFO lppi)
{
    HRESULT hr;
    DWORD   dwFileAttributes = GetFileAttributes(lppi->szFilename);
    
    // Ok, we need to test whether the user tried to package a folder
    // instead of a file.  If s/he did, then we'll just create a link
    // to that folder instead of an embedded file.
    //

    if (-1 == dwFileAttributes)
    {
        hr = CmlInitFromFile(lppi->szFilename, FALSE, PACKAGE);
    }
    else if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    {
        hr = CmlInitFromFile(lppi->szFilename, FALSE, CMDLINK);
    }
    else
    {
        // we pass FALSE here, because we don't want to write the icon
        // information.
        //      
        hr = EmbedInitFromFile(lppi->szFilename, FALSE);
        lstrcpy(_pEmbed->fd.cFileName,lppi->szFilename);
        _panetype = PEMBED;
    }

    // set the icon information    
    if (PathFileExists(lppi->szFilename))
        lstrcpy(_lpic->szIconPath, *lppi->szIconPath? lppi->szIconPath : lppi->szFilename);

    _lpic->iDlgIcon = lppi->iIcon;
    lstrcpy(_lpic->szIconText,lppi->szLabel);
    
    IconRefresh();

    // we need to tell the client we want to be saved...it should be smart
    // enough to do it anyway, but we can't take any chances.
    if (_pIOleClientSite)
        _pIOleClientSite->SaveObject();

    return hr;
}    

HRESULT CPackage::CreateTempFileName()
{
    ASSERT(NULL != _pEmbed);
    TCHAR szDefPath[MAX_PATH];
    if (_pEmbed->pszTempName)
    {
        return S_OK;
    }
    else if (GetTempPath(ARRAYSIZE(szDefPath), szDefPath))
    {
        LPTSTR pszFile;
        if ((NULL != _lpic) && (TEXT('\0') != _lpic->szIconText[0]))
        {
            pszFile = _lpic->szIconText;
        }
        else
        {
            pszFile = PathFindFileName(_pEmbed->fd.cFileName);
        }
        PathAppend(szDefPath, pszFile);
        if (PathFileExists(szDefPath))
        {
            TCHAR szOriginal[MAX_PATH];
            lstrcpy(szOriginal, szDefPath);
            PathYetAnotherMakeUniqueName(szDefPath, szOriginal, NULL, NULL);
        }
        
        _pEmbed->pszTempName = new TCHAR[lstrlen(szDefPath) + 1];
        if (!_pEmbed->pszTempName) 
        {
            DebugMsg(DM_TRACE,"            couldn't alloc memory for pszTempName!!");
            return E_OUTOFMEMORY;
        }    
        lstrcpy(_pEmbed->pszTempName, szDefPath);
        return S_OK;
    }
    else
    {
        DebugMsg(DM_TRACE,"            couldn't get temp path!!");
        return E_FAIL;
    }
}

HRESULT CPackage::CreateTempFile() 
{
    //
    // used to create a temporary file that holds the file contents of the
    // packaged file.  the old packager used to keep the packaged file in 
    // memory which is just a total waste.  so, being as we're much more 
    // efficient, we create a temp file whenever someone wants to do something
    // with our contents.  we initialze the temp file from the original file
    // to package or our persistent storage depending on whether we are a new
    // package or a loaded package
    // return:  S_OK    -- temp file created
    //          E_FAIL  -- error creating temp file
    //
    
    DebugMsg(DM_TRACE,"            CreateTempFile() called.");

    HRESULT hr = CreateTempFileName();
    if (FAILED(hr))
    {
        return hr;
    }

    if (PathFileExists(_pEmbed->pszTempName))
    {
        DebugMsg(DM_TRACE,"            already have a temp file!!");
        return S_OK;
    }
    
    // if we weren't loaded from a storage then we're in the process of
    // creating a package, and should be able to copy the packaged file
    // to create a temp file
    //
    if (!_fLoaded) 
    {
        if (!(CopyFile(_pEmbed->fd.cFileName, _pEmbed->pszTempName, FALSE))) 
        {
            DebugMsg(DM_TRACE,"            couldn't copy file!!");
            return E_FAIL;
        }
    }
    else 
    {
        TCHAR szTempFile[MAX_PATH];
        // copy the file name because _pEmbed may get re-created below,
        // but we want to hold on to this filename:
        lstrcpy(szTempFile, _pEmbed->pszTempName);
        
        // if we have a valid stream, but not a file contents stream, 
        // it's because we went hands off and didn't know where to put
        // the seek pointer to init the filecontents stream.  so, we
        // call PackageReadFromStream to create the FileContents stream
        //
        if (_pstm && !_pstmFileContents) 
        {
            if (FAILED(PackageReadFromStream(_pstm))) 
            {
                DebugMsg(DM_TRACE,"            couldn't read from stream!!");
                return E_FAIL;
            }
        }
        
        IStream* pstm;
        _pstmFileContents->Clone(&pstm);  // we don't want to move the seek
                                          // pointer on our FileContents stream
        
        if (FAILED(CopyStreamToFile(pstm, szTempFile))) 
        {
            DebugMsg(DM_TRACE,"            couldn't copy from stream!!");
            pstm->Release();
            return E_FAIL;
        }
        else
        {
            ASSERT(_pEmbed);
            delete _pEmbed->pszTempName;
            if (NULL != (_pEmbed->pszTempName = new TCHAR[lstrlen(szTempFile) + 1]))
            {
                lstrcpy(_pEmbed->pszTempName, szTempFile);
            }
            else
            {
                return E_OUTOFMEMORY;
            }
        }
        pstm->Release();
    }

    
    // whenever we create a tempfile we are activating the contents which 
    // means we are dirty until we get a save message
    return S_OK;
}



///////////////////////////////////////////////////////////////////////
//
// Data Transfer Functions
//
///////////////////////////////////////////////////////////////////////

HRESULT CPackage::GetFileDescriptor(LPFORMATETC pFE, LPSTGMEDIUM pSTM) 
{
    FILEGROUPDESCRIPTOR *pfgd;

    
    DebugMsg(DM_TRACE,"            Getting File Descriptor");

    // we only support HGLOBAL at this time
    //
    if (!(pFE->tymed & TYMED_HGLOBAL)) {
        DebugMsg(DM_TRACE,"            does not support HGLOBAL!");
        return DATA_E_FORMATETC;
    }

    //// Copy file descriptor to HGLOBAL ///////////////////////////
    //
    pSTM->tymed = TYMED_HGLOBAL;
    
    // render the file descriptor 
    if (!(pfgd = (FILEGROUPDESCRIPTOR *)GlobalAlloc(GPTR,
        sizeof(FILEGROUPDESCRIPTOR))))
        return E_OUTOFMEMORY;

    pSTM->hGlobal = pfgd;
    
    pfgd->cItems = 1;

    switch(_panetype) 
    {
        case PEMBED:
            pfgd->fgd[0] = _pEmbed->fd;
            GetDisplayName(pfgd->fgd[0].cFileName, _pEmbed->fd.cFileName);
            break;

        case CMDLINK:
            // the label for the package will serve as the filename for the
            // shortcut we're going to create.
            lstrcpy(pfgd->fgd[0].cFileName, _lpic->szIconText);
            // harcoded use of .lnk extension!!
            lstrcat(pfgd->fgd[0].cFileName, TEXT(".lnk"));

            // we want to add the little arrow to the shortcut.
            pfgd->fgd[0].dwFlags = FD_LINKUI;
            break;
    }
    return S_OK;
}

HRESULT CPackage::GetFileContents(LPFORMATETC pFE, LPSTGMEDIUM pSTM) 
{
    void *  lpvDest = NULL;
    DWORD   dwSize;
    HANDLE  hFile = NULL;
    DWORD   cb;
    HRESULT hr = E_FAIL;
    
    DebugMsg(DM_TRACE,"            Getting File Contents");
    
    //// Copy file contents to ISTREAM ///////////////////////////
    // 
    // NOTE: Hopefully, everyone using our object supports TYMED_ISTREAM,
    // otherwise we could get some really slow behavior.  We might later
    // want to implement TYMED_ISTORAGE as well and shove our file contents
    // into a single stream named CONTENTS.
    //
    if (pFE->tymed & TYMED_ISTREAM) {
        DebugMsg(DM_TRACE,"            using TYMED_ISTREAM");
        pSTM->tymed = TYMED_ISTREAM;

        switch (_panetype) {
            case PEMBED:
                if (_pstmFileContents)
                    hr = _pstmFileContents->Clone(&pSTM->pstm);
                else 
                    return E_FAIL;
                break;

            case CMDLINK:
                hr = CreateStreamOnHGlobal(NULL, TRUE, &pSTM->pstm);
                if (SUCCEEDED(hr))
                {
                    hr = CreateShortcutOnStream(pSTM->pstm);
                    if (FAILED(hr))
                    {
                        pSTM->pstm->Release();
                        pSTM->pstm = NULL;
                    }
                }
                break;
        }
        return hr;
    }
    
    //// Copy file contents to HGLOBAL ///////////////////////////
    //
    // NOTE: This is really icky and could potentially be very slow if
    // somebody decides to package really large files.  Hopefully, 
    // everyone should be able to get the info it wants through TYMED_ISTREAM,
    // but this is here as a common denominator
    //
    if (pFE->tymed & TYMED_HGLOBAL) {
        DebugMsg(DM_TRACE,"            using TYMED_HGLOBAL");
        pSTM->tymed = TYMED_HGLOBAL;

        if (_panetype == CMDLINK) {
            DebugMsg(DM_TRACE,
                "    H_GLOBAL not supported for CMDLINK");
            return DATA_E_FORMATETC;
        }
        
        dwSize = _pEmbed->fd.nFileSizeLow;
        
        // caller is responsible for freeing this memory, even if we fail.
        if (!(lpvDest = GlobalAlloc(GPTR, dwSize))) {
            DebugMsg(DM_TRACE,"            out o memory!!");
            return E_OUTOFMEMORY;
        }
        pSTM->hGlobal = lpvDest;
        
        // This will reinitialize our FileContents stream if we had to get
        // rid of it.  For instance, we have to get rid of all our storage
        // pointers in HandsOffStorage, but there's no need to reinit our 
        // FileContents stream unless we need it again.
        //
        if (_pstm && !_pstmFileContents)
            PackageReadFromStream(_pstm);
        
        if (_pstmFileContents) {
            IStream* pstm;
            hr = _pstmFileContents->Clone(&pstm);
            if (FAILED(hr))
                return hr;
            hr = pstm->Read(lpvDest, dwSize, &cb);
            pstm->Release();
            if (FAILED(hr))
                return hr;
        }
        else
            return E_FAIL;
        
        if (FAILED(hr) || cb != dwSize) {
            DebugMsg(DM_TRACE,"            error reading from stream!!");
            return E_FAIL;
        }
        return hr;
    }
    
    return DATA_E_FORMATETC;
}

void DrawIconToDC(HDC hdcMF, LPIC lpic)
{
    RECT  rcTemp;
    HFONT hfont = NULL;

    // Initializae the metafile
    SetWindowOrgEx(hdcMF, 0, 0, NULL);
    SetWindowExtEx(hdcMF, lpic->rc.right - 1, lpic->rc.bottom - 1, NULL);

    SetRect(&rcTemp, 0, 0, lpic->rc.right,lpic->rc.bottom);
    hfont = SelectFont(hdcMF, g_hfontTitle);
    
    // Center the icon
    DrawIcon(hdcMF, (rcTemp.right - g_cxIcon) / 2, 0, lpic->hDlgIcon);
    
    // Center the text below the icon
    SetBkMode(hdcMF, TRANSPARENT);
    SetTextAlign(hdcMF, TA_CENTER);
    TextOut(hdcMF, rcTemp.right / 2, g_cxIcon + 1, lpic->szIconText,
            lstrlen(lpic->szIconText));

    if (hfont)
        SelectObject(hdcMF, hfont);
}


HRESULT CPackage::GetMetafilePict(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
{
    LPMETAFILEPICT      lpmfpict;
    RECT                rcTemp;
    LPIC                lpic = _lpic;
    HDC                 hdcMF = NULL;
    
    
    DebugMsg(DM_TRACE,"            Getting MetafilePict");
    
    if (!(pFE->tymed & TYMED_MFPICT)) {
        DebugMsg(DM_TRACE,"            does not support MFPICT!");
        return DATA_E_FORMATETC;
    }
    pSTM->tymed = TYMED_MFPICT;
    
    // Allocate memory for the metafilepict and get a pointer to it
    // NOTE: the caller is responsible for freeing this memory, even on fail
    //
    if (!(pSTM->hMetaFilePict = GlobalAlloc(GPTR, sizeof(METAFILEPICT))))
        return E_OUTOFMEMORY;
    lpmfpict = (LPMETAFILEPICT)pSTM->hMetaFilePict;
        
    // Create the metafile
    if (!(hdcMF = CreateMetaFile(NULL))) 
        return E_OUTOFMEMORY;

    DrawIconToDC(hdcMF, lpic);
    
    // Map to device independent coordinates
    SetRect(&rcTemp, 0, 0, lpic->rc.right,lpic->rc.bottom);
    rcTemp.right =
        MulDiv((rcTemp.right - rcTemp.left), HIMETRIC_PER_INCH, DEF_LOGPIXELSX);
    rcTemp.bottom =
        MulDiv((rcTemp.bottom - rcTemp.top), HIMETRIC_PER_INCH, DEF_LOGPIXELSY);

    // Finish filling in the metafile header
    lpmfpict->mm = MM_ANISOTROPIC;
    lpmfpict->xExt = rcTemp.right;
    lpmfpict->yExt = rcTemp.bottom;
    lpmfpict->hMF = CloseMetaFile(hdcMF);
    
    return S_OK;
}

HRESULT CPackage::GetEnhMetafile(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
{
    DebugMsg(DM_TRACE,"            Getting EnhancedMetafile");
    
    if (!(pFE->tymed & TYMED_ENHMF)) {
        DebugMsg(DM_TRACE,"            does not support ENHMF!");
        return DATA_E_FORMATETC;
    }

    // Map to device independent coordinates
    RECT rcTemp;
    SetRect(&rcTemp, 0, 0, _lpic->rc.right,_lpic->rc.bottom);
    rcTemp.right =
        MulDiv((rcTemp.right - rcTemp.left), HIMETRIC_PER_INCH, DEF_LOGPIXELSX);
    rcTemp.bottom =
        MulDiv((rcTemp.bottom - rcTemp.top), HIMETRIC_PER_INCH, DEF_LOGPIXELSY);

    HDC hdc = CreateEnhMetaFile(NULL, NULL, &rcTemp, NULL);
    if (hdc)
    {
        DrawIconToDC(hdc, _lpic);

        pSTM->tymed = TYMED_ENHMF;
        pSTM->hEnhMetaFile = CloseEnhMetaFile(hdc);

        return S_OK;
    }
    else
    {
        pSTM->tymed = TYMED_NULL;

        return E_OUTOFMEMORY;
    }
}

HRESULT CPackage::GetObjectDescriptor(LPFORMATETC pFE, LPSTGMEDIUM pSTM) 
{
    LPOBJECTDESCRIPTOR lpobj;
    DWORD   dwFullUserTypeNameLen;
    
    DebugMsg(DM_TRACE,"            Getting Object Descriptor");

    // we only support HGLOBAL at this time
    //
    if (!(pFE->tymed & TYMED_HGLOBAL)) {
        DebugMsg(DM_TRACE,"            does not support HGLOBAL!");
        return DATA_E_FORMATETC;
    }

    //// Copy file descriptor to HGLOBAL ///////////////////////////

    dwFullUserTypeNameLen = 0; //lstrlen(szUserType) + 1;
    pSTM->tymed = TYMED_HGLOBAL;

    if (!(lpobj = (OBJECTDESCRIPTOR *)GlobalAlloc(GPTR,
        sizeof(OBJECTDESCRIPTOR)+dwFullUserTypeNameLen)))
        return E_OUTOFMEMORY;

    pSTM->hGlobal = lpobj;
    
    lpobj->cbSize       = sizeof(OBJECTDESCRIPTOR)+dwFullUserTypeNameLen;
    lpobj->clsid        = CLSID_CPackage;
    lpobj->dwDrawAspect = DVASPECT_CONTENT|DVASPECT_ICON;
    _pIOleObject->GetMiscStatus(DVASPECT_CONTENT|DVASPECT_ICON,&(lpobj->dwStatus));
    lpobj->dwFullUserTypeName = 0L; //sizeof(OBJECTDESCRIPTOR);
    lpobj->dwSrcOfCopy = 0L;

    // lstrcpy((LPTSTR)lpobj+lpobj->dwFullUserTypeName, szUserType);
    return S_OK;
}


/////////////////////////////////////////////////////////////////////////
//
// Stream I/O Functions
//
/////////////////////////////////////////////////////////////////////////

HRESULT CPackage::PackageReadFromStream(IStream* pstm)
{
    //
    // initialize the package object from a stream
    // return:  s_OK   - package properly initialized
    //          E_FAIL - error initializing package
    //
    
    WORD  w;
    DWORD dw;
    
    DebugMsg(DM_TRACE, "pack - PackageReadFromStream called.");

    // read in the package size, which we don't really need, but the old 
    // packager puts it there.
    if (FAILED(pstm->Read(&dw, sizeof(dw), NULL)))
        return E_FAIL;

    // NOTE: Ok, this is really dumb.  The old packager allowed the user
    // to create packages without giving them icons or labels, which
    // in my opinion is just dumb, it should have at least created a default
    // icon and shoved it in the persistent storage...oh well...
    //     So if the appearance type comes back as NOTHING ( == 0)
    // then we just won't read any icon information.
    
    // read in the appearance type
    pstm->Read(&w, sizeof(w), NULL);
    
    // read in the icon information
    if (w == (WORD)ICON)
    {
        if (FAILED(IconReadFromStream(pstm))) 
        {
            DebugMsg(DM_TRACE,"         error reading icon info!!");
            return E_FAIL;
        }
    }
    else if (w == (WORD)PICTURE)
    {
        DebugMsg(DM_TRACE, "         old Packager Appearance, not supported!!");
        // NOTE: Ideally, we could just ignore the appearance and continue, but to
        // do so, we'll need to know how much information to skip over before continuing
        // to read from the stream
        ShellMessageBox(g_hinst,
                        NULL,
                        MAKEINTRESOURCE(IDS_OLD_FORMAT_ERROR),
                        MAKEINTRESOURCE(IDS_APP_TITLE),
                        MB_OK | MB_ICONERROR | MB_TASKMODAL);
        return E_FAIL;
    }
    
    // read in the contents type
    pstm->Read(&w, sizeof(w), NULL);

    _panetype = (PANETYPE)w;
    
    switch((PANETYPE)w)
    {
    case PEMBED:
        // read in the contents information
        return EmbedReadFromStream(pstm);

    case CMDLINK:
        // read in the contents information
        return CmlReadFromStream(pstm); 

    default:
        return E_FAIL;
    }
}

//
// read the icon info from a stream
// return:  S_OK   -- icon read correctly
//          E_FAIL -- error reading icon
//
HRESULT CPackage::IconReadFromStream(IStream* pstm) 
{
    LPIC lpic = IconCreate();
    if (lpic)
    {
        CHAR szTemp[MAX_PATH];
        StringReadFromStream(pstm, szTemp, ARRAYSIZE(szTemp));
        SHAnsiToTChar(szTemp, lpic->szIconText, ARRAYSIZE(lpic->szIconText));
        
        StringReadFromStream(pstm, szTemp, ARRAYSIZE(szTemp));
        SHAnsiToTChar(szTemp, lpic->szIconPath, ARRAYSIZE(lpic->szIconPath));
        
        WORD wDlgIcon;
        pstm->Read(&wDlgIcon, sizeof(wDlgIcon), NULL);
        lpic->iDlgIcon = (INT) wDlgIcon;
        GetCurrentIcon(lpic);
        IconCalcSize(lpic);
    }

    DestroyIC();
    _lpic = lpic;

    return lpic ? S_OK : E_FAIL;
}

HRESULT CPackage::EmbedReadFromStream(IStream* pstm) 
{
    //
    // reads embedded file contents from a stream
    // return:  S_OK   - contents read succesfully
    //          E_FAIL - error reading contents
    //
    
    DWORD dwSize;
    CHAR  szFileName[MAX_PATH];
    
    DebugMsg(DM_TRACE, "pack - EmbedReadFromStream called.");
    
    pstm->Read(&dwSize, sizeof(dwSize), NULL);  // get string size
    pstm->Read(szFileName, dwSize, NULL);       // get string
    pstm->Read(&dwSize, sizeof(dwSize), NULL);  // get file size

    // we don't do anything with the file contents here, because anything
    // we do could be a potentially expensive operation.  so, we just clone
    // the stream and hold onto it for future use.
    
    if (_pstmFileContents) 
        _pstmFileContents->Release();
        
    if (FAILED(pstm->Clone(&_pstmFileContents)))
        return E_FAIL;

    if (_pEmbed) {
        if (_pEmbed->pszTempName) {
            DeleteFile(_pEmbed->pszTempName);
            delete _pEmbed->pszTempName;
        }
        delete _pEmbed;
    }

    _pEmbed = new EMBED;
    if (NULL != _pEmbed)
    {
        _pEmbed->fd.dwFlags = FD_FILESIZE;
        _pEmbed->fd.nFileSizeLow = dwSize;
        _pEmbed->fd.nFileSizeHigh = 0;
        SHAnsiToTChar(szFileName, _pEmbed->fd.cFileName, ARRAYSIZE(_pEmbed->fd.cFileName));
        DebugMsg(DM_TRACE,"         %s\n\r         %d",_pEmbed->fd.cFileName,_pEmbed->fd.nFileSizeLow);
        return S_OK;
    }
    else
    {
        return E_OUTOFMEMORY;
    }
}


HRESULT CPackage::CmlReadFromStream(IStream* pstm)
{
    //
    // reads command line contents from a stream
    // return:  S_OK   - contents read succesfully
    //          E_FAIL - error reading contents
    //

    WORD w;
    CHAR  szCmdLink[CBCMDLINKMAX];
    
    DebugMsg(DM_TRACE, "pack - CmlReadFromStream called.");

    // read in the fCmdIsLink and the command line string
    if (FAILED(pstm->Read(&w, sizeof(w), NULL)))    
        return E_FAIL;
    StringReadFromStream(pstm, szCmdLink, ARRAYSIZE(szCmdLink));

    if (_pCml != NULL)
        delete _pCml;
    
    _pCml = new CML;
    SHAnsiToTChar(szCmdLink, _pCml->szCommandLine, ARRAYSIZE(_pCml->szCommandLine));
    
    return S_OK;
}    
    

HRESULT CPackage::PackageWriteToStream(IStream* pstm)
{
    //
    // write the package object to a stream
    // return:  s_OK   - package properly written
    //          E_FAIL - error writing package
    //
    
    WORD w;
    DWORD cb = 0L;
    DWORD dwSize;
      
    DebugMsg(DM_TRACE, "pack - PackageWriteToStream called.");

    // write out a DWORD where the package size will go
    if (FAILED(pstm->Write(&cb, sizeof(DWORD), NULL)))
        return E_FAIL;
    
    // write out the appearance type
    w = (WORD)ICON;
    if (FAILED(pstm->Write(&w, sizeof(WORD), NULL)))
        return E_FAIL;
    cb += 2*sizeof(WORD);       // for appearance type and contents type
    
    // write out the icon information
    if (FAILED(IconWriteToStream(pstm,&dwSize))) 
    {
        DebugMsg(DM_TRACE,"         error writing icon info!!");
        return E_FAIL;
    }
    cb += dwSize;

    // write out the contents type
    w = (WORD)_panetype;
    if (FAILED(pstm->Write(&_panetype, sizeof(WORD), NULL)))
        return E_FAIL;

    switch(_panetype) 
    {
        case PEMBED:
            
            // write out the contents information
            if (FAILED(EmbedWriteToStream(pstm,&dwSize))) 
            {
                DebugMsg(DM_TRACE,"         error writing embed info!!");
                return E_FAIL;
            }
            cb += dwSize;
            break;

        case CMDLINK:
            // write out the contents information
            if (FAILED(CmlWriteToStream(pstm,&dwSize))) {
                DebugMsg(DM_TRACE,"         error writing cml info!!");
                return E_FAIL;
            }
            cb += dwSize;
            break;
    }

    
    LARGE_INTEGER li = {0, 0};
    if (FAILED(pstm->Seek(li, STREAM_SEEK_SET, NULL)))
        return E_FAIL;
    if (FAILED(pstm->Write(&cb, sizeof(DWORD), NULL)))
        return E_FAIL;
    
    return S_OK;
}


//
// write the icon to a stream
// return:  s_OK   - icon properly written
//          E_FAIL - error writing icon
//
HRESULT CPackage::IconWriteToStream(IStream* pstm, DWORD *pdw)
{
    DWORD cb = 0;
    CHAR szTemp[MAX_PATH];
    SHTCharToAnsi(_lpic->szIconText, szTemp, ARRAYSIZE(szTemp));
    HRESULT hr = StringWriteToStream(pstm, szTemp, &cb);
    if (SUCCEEDED(hr))
    {
        SHTCharToAnsi(_lpic->szIconPath, szTemp, ARRAYSIZE(szTemp));
        hr = StringWriteToStream(pstm, szTemp, &cb);
        if (SUCCEEDED(hr))
        {
            DWORD dwWrite;
            WORD wDlgIcon = (WORD) _lpic->iDlgIcon;
            hr = pstm->Write(&wDlgIcon, sizeof(wDlgIcon), &dwWrite);
            if (SUCCEEDED(hr))
            {
                cb += dwWrite;
                if (pdw)
                    *pdw = cb;
            }
        }
    }
    return hr;
}

//
// write embedded file contents to a stream
// return:  S_OK   - contents written succesfully
//          E_FAIL - error writing contents
//
HRESULT CPackage::EmbedWriteToStream(IStream* pstm, DWORD *pdw)
{
    DWORD cb = 0;
    CHAR szTemp[MAX_PATH];
    SHTCharToAnsi(_pEmbed->fd.cFileName, szTemp, ARRAYSIZE(szTemp));
    DWORD dwSize = lstrlenA(szTemp) + 1;
    HRESULT hr = pstm->Write(&dwSize, sizeof(dwSize), &cb);
    if (SUCCEEDED(hr))
    {
        DWORD dwWrite = 0;
        hr = StringWriteToStream(pstm, szTemp, &dwWrite);
        if (SUCCEEDED(hr))
        {
            cb += dwWrite;
            hr = pstm->Write(&_pEmbed->fd.nFileSizeLow, sizeof(_pEmbed->fd.nFileSizeLow), &dwWrite);
            if (SUCCEEDED(hr))
            {
                cb += dwWrite;

                // we want to make sure our file contents stream always points to latest 
                // saved file contents
                //
                // NOTE: If we're not saving to our loaded stream, we shouldn't keep a
                // pointer to it, because we're in a SaveAs situation, and we don't want
                // to be hanging onto pointers to other peoples streams.
                //
                if (_pstmFileContents && _pstm == pstm)
                {
                    _pstmFileContents->Release();
                    pstm->Clone(&_pstmFileContents);
                }

                // This is for screwy apps, like MSWorks that ask us to save ourselves 
                // before they've even told us to initialize ourselves.  
                //
                if (_pEmbed->fd.cFileName[0])
                {
                    hr = CopyFileToStream(_pEmbed->pszTempName, pstm);
                    if (SUCCEEDED(hr))
                    {
                        cb += _pEmbed->fd.nFileSizeLow;
                    }
                }
                if (pdw)
                    *pdw = cb;
            }
        }
    }
    return hr;
}

//
// write embedded file contents to a stream
// return:  S_OK   - contents written succesfully
//          E_FAIL - error writing contents
//
HRESULT CPackage::CmlWriteToStream(IStream* pstm, DWORD *pdw)
{
    DWORD cb = 0;
    WORD w = (WORD)_pCml->fCmdIsLink;
    
    DebugMsg(DM_TRACE, "pack - CmlWriteToStream called.");

    if (FAILED(pstm->Write(&w, sizeof(w), NULL)))
        return E_FAIL;                                   // write fCmdIsLink
    cb += sizeof(w);      // for fCmdIsLink

    CHAR szTemp[MAX_PATH];
    SHTCharToAnsi(_pCml->szCommandLine, szTemp, ARRAYSIZE(szTemp));
    HRESULT hres = StringWriteToStream(pstm, szTemp, &cb);
    if (FAILED(hres))
        return hres;                                   // write command link

    // return the number of bytes written in the outparam    
    if (pdw)
        *pdw = cb;
    
    return S_OK;
}


HRESULT CPackage::CreateShortcutOnStream(IStream* pstm)
{
    HRESULT hr;
    IShellLink *psl;
    TCHAR szArgs[CBCMDLINKMAX - MAX_PATH];
    TCHAR szPath[MAX_PATH];
    
    hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
        IID_IShellLink, (void **)&psl);
    if (SUCCEEDED(hr))
    {
        IPersistStream *pps;

        lstrcpy(szPath,_pCml->szCommandLine);
        PathSeparateArgs(szPath, szArgs);

        psl->SetPath(szPath);
        psl->SetIconLocation(_lpic->szIconPath, _lpic->iDlgIcon);
        psl->SetShowCmd(SW_SHOW);
        psl->SetArguments(szArgs);
        
        hr = psl->QueryInterface(IID_IPersistStream, (void **)&pps);
        if (SUCCEEDED(hr))
        {
            hr = pps->Save(pstm,TRUE);
            pps->Release();
        }
        psl->Release();
    }
    
    LARGE_INTEGER li = {0,0};
    pstm->Seek(li,STREAM_SEEK_SET,NULL);

    return hr;
}

HRESULT CPackage::InitVerbEnum(OLEVERB* pVerbs, ULONG cVerbs)
{
    if (NULL != _pVerbs)
    {
        for (ULONG i = 0; i < _cVerbs; i++)
        {
            delete _pVerbs[i].lpszVerbName;
        }
        delete _pVerbs;
    }
    _pVerbs = pVerbs;
    _cVerbs = cVerbs;
    _nCurVerb = 0;
    return (NULL != pVerbs) ? S_OK : E_FAIL;
}

VOID CPackage::ReleaseContextMenu()
{
    if (NULL != _pcm)
    {
        _pcm->Release();
        _pcm = NULL;
    }
}

HRESULT CPackage::GetContextMenu(IContextMenu** ppcm)
{
    HRESULT hr = E_FAIL;
    ASSERT(NULL != ppcm);
    if (NULL != _pcm)
    {
        _pcm->AddRef();
        *ppcm = _pcm;
        hr = S_OK;
    }
    else if ((PEMBED == _panetype) || (CMDLINK == _panetype))
    {
        if (PEMBED == _panetype)
        {
            hr = CreateTempFileName();
        }
        else
        {
            hr = S_OK;
        }
        if (SUCCEEDED(hr))
        {
            LPITEMIDLIST pidl = SHSimpleIDListFromPath((PEMBED == _panetype) ?
                                                        _pEmbed->pszTempName :
                                                        _pCml->szCommandLine);
            if (NULL != pidl)
            {
                IShellFolder* psf;
                LPCITEMIDLIST pidlChild;
                if (SUCCEEDED(hr = SHBindToIDListParent(pidl, IID_IShellFolder, (void **)&psf, &pidlChild)))
                {
                    hr = psf->GetUIObjectOf(NULL, 1, &pidlChild, IID_IContextMenu, NULL, (void**) &_pcm);
                    if (SUCCEEDED(hr))
                    {
                        _pcm->AddRef();
                        *ppcm = _pcm;
                    }
                    psf->Release();
                }
                ILFree(pidl);
            }
            else
            {
                hr = E_OUTOFMEMORY;
            }
        }
    }
    return hr;
}

HRESULT CPackage::IconRefresh()
{
    // we refresh the icon.  typically, this will be called the first time
    // the package is created to load the new icon and calculate how big
    // it should be.  this will also be called after we edit the package,
    // since the user might have changed the icon.
    
    // First, load the appropriate icon.  We'll load the icon specified by
    // lpic->szIconPath and lpic->iDlgIcon if possible, otherwise we'll just
    // use the generic packager icon.
    //
    GetCurrentIcon(_lpic);

    // Next, we need to have the icon recalculate its size, since it's text
    // might have changed, causing it to get bigger or smaller.
    //
    IconCalcSize(_lpic);

    // Next, notify our containers that our view has changed.
    if (_pIDataAdviseHolder)
        _pIDataAdviseHolder->SendOnDataChange(_pIDataObject,0, NULL);
    if (_pViewSink)
        _pViewSink->OnViewChange(_dwViewAspects,_dwViewAdvf);

    // Set our dirty flag
    _fIsDirty = TRUE;

    return S_OK;
}

    
int CPackage::RunWizard()
{
    PACKAGER_INFO packInfo;
    HRESULT hr;
    
    PackWiz_CreateWizard(NULL, &packInfo);

    InitFromPackInfo(&packInfo);

    hr = OleSetClipboard(_pIDataObject);
    if (FAILED(hr))
    {
        ShellMessageBox(g_hinst,
                        NULL,
                        MAKEINTRESOURCE(IDS_COPY_ERROR),
                        MAKEINTRESOURCE(IDS_APP_TITLE),
                        MB_ICONERROR | MB_TASKMODAL | MB_OK);
        return -1;
    }

    // we need to do this.  our OleUninitialze call at the end, free the
    // libarary and our dataobject on the clipboard unless we flush
    // the clipboard.
    
    hr = OleFlushClipboard();
    if (FAILED(hr))
    {
        ShellMessageBox(g_hinst,
                        NULL,
                        MAKEINTRESOURCE(IDS_COPY_ERROR),
                        MAKEINTRESOURCE(IDS_APP_TITLE),
                        MB_ICONERROR | MB_TASKMODAL | MB_OK);
        return -1;
    }
    
    ShellMessageBox(g_hinst,
                    NULL,
                    MAKEINTRESOURCE(IDS_COPY_COMPLETE),
                    MAKEINTRESOURCE(IDS_APP_TITLE),
                    MB_ICONINFORMATION | MB_TASKMODAL | MB_OK);
    
    return 0;
}

void CPackage::DestroyIC()
{
    if (_lpic)
    {
        if (_lpic->hDlgIcon)
            DestroyIcon(_lpic->hDlgIcon);
        
        GlobalFree(_lpic);
    }
}

STDAPI_(BOOL) PackWizRunFromExe()
{
    OleInitialize(NULL);

    CPackage *pPackage = new CPackage;
    if (pPackage)
    {
        pPackage->Init();
        pPackage->RunWizard();
        pPackage->Release();
    }
    
    OleUninitialize();
    return 0;
}