windows-nt/Source/XPSP1/NT/shell/ext/pack/pack2.cpp

1568 lines
44 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
#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;
}