HELPERS.CPP

/* 
* HELPERS.CPP
*
* Utility functions for drawing various graphical effects
* as well as coordinate conversion functions.
*
* Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
*
* Kraig Brockschmidt, Microsoft
* Internet : kraigb@microsoft.com
* Compuserve: >INTERNET:kraigb@microsoft.com
*/


#include "inoledll.h"


/*
* INOLE_MetafilePictIconFree
*
* Purpose:
* Deletes the metafile contained in a METAFILEPICT structure and
* frees the memory for the structure itself.
*
* Parameters:
* hMetaPict HGLOBAL metafilepict structure created in
* OleMetafilePictFromIconAndLabel
*
* Return Value:
* None
*/

STDAPI_(void) INOLE_MetafilePictIconFree(HGLOBAL hMetaPict)
{
LPMETAFILEPICT pMF;

if (NULL==hMetaPict)
return;

pMF=(LPMETAFILEPICT)GlobalLock(hMetaPict);

if (NULL!=pMF)
{
if (NULL!=pMF->hMF)
DeleteMetaFile(pMF->hMF);
}

GlobalUnlock(hMetaPict);
GlobalFree(hMetaPict);
return;
}





/*
* INOLE_SwitchDisplayAspect
*
* Purpose:
* Switch the currently cached display aspect between DVASPECT_ICON
* and DVASPECT_CONTENT. When setting up icon aspect, any currently
* cached content cache is discarded and any advise connections for
* content aspect are broken.
*
* Parameters:
* pObj IUnknown * to the object in question
* pdwCurAspect DWORD * containing the current aspect which
* will contain the new aspect on output.
* dwNewAspect DWORD with the aspect to switch to.
* hMetaPict HGLOBAL containing the CF_METAFILEPICT with
* the icon.
* fDeleteOld BOOL indicating if we're to delete the old
* aspect from the cache.
* fViewAdvise BOOL indicating if we're to establish an
* advise with the object for this new aspect.
* pSink IAdviseSink * to the notification sink.
* pfMustUpdate BOOL * in which to return whether or not
* an update from a running server is necessary.
*
* Return Value:
* HRESULT NOERROR or an error code in which case the cache
* remains unchanged.
*/

STDAPI INOLE_SwitchDisplayAspect(IUnknown *pObj, LPDWORD pdwCurAspect
, DWORD dwNewAspect, HGLOBAL hMetaPict, BOOL fDeleteOld
, BOOL fViewAdvise, IAdviseSink *pSink, BOOL *pfMustUpdate)
{
IOleCache *pCache=NULL;
FORMATETC fe;
STGMEDIUM stm;
DWORD dwAdvf;
DWORD dwNewConnection;
DWORD dwOldAspect=*pdwCurAspect;
HRESULT hr;

if (pfMustUpdate)
*pfMustUpdate=FALSE;

hr=pObj->QueryInterface(IID_IOleCache, (void **)&pCache);

if (FAILED(hr))
return ResultFromScode(E_FAIL);

//Establish new cache with the new aspect
SETFormatEtc(fe, 0, dwNewAspect, NULL, TYMED_NULL, -1);

/*
* If we are using a custom icon for the display aspect then
* we won't want to establish notifications with the data
* source, that is, we don't want to change the icon. Otherwise
* we link up the given advise sink.
*/
if (DVASPECT_ICON==dwNewAspect && NULL!=hMetaPict)
dwAdvf=ADVF_NODATA;
else
dwAdvf=ADVF_PRIMEFIRST;

hr=pCache->Cache(&fe, dwAdvf, &dwNewConnection);

if (FAILED(hr))
{
pCache->Release();
return hr;
}

*pdwCurAspect=dwNewAspect;

/*
* Stuff the custom icon into the cache, or force an update
* from the server.
*/
if (DVASPECT_ICON==dwNewAspect && NULL!=hMetaPict)
{
SETFormatEtc(fe, CF_METAFILEPICT, DVASPECT_ICON, NULL
, TYMED_MFPICT, -1);

stm.tymed=TYMED_MFPICT;
stm.hGlobal=hMetaPict;
stm.pUnkForRelease=NULL;

hr=pCache->SetData(&fe, &stm, FALSE);
}
else
{
if (pfMustUpdate)
*pfMustUpdate=TRUE;
}

if (fViewAdvise && pSink)
{
IViewObject *pView=NULL;

hr=pObj->QueryInterface(IID_IViewObject, (void **)&pView);

if (SUCCEEDED(hr))
{
pView->SetAdvise(dwNewAspect, 0, pSink);
pView->Release();
}
}

/*
* Remove existing caches for the old display aspect to cut
* down on needless storage overhead. If you want to switch
* frequently between icon and content aspects, then it is
* best to actually keep both presentations in the cache.
*/
if (fDeleteOld)
{
IEnumSTATDATA *pEnum=NULL;
STATDATA sd;

hr=pCache->EnumCache(&pEnum);

while(NOERROR==hr)
{
hr=pEnum->Next(1, &sd, NULL);

if (NOERROR==hr)
{
//Remove old aspect caches
if (sd.formatetc.dwAspect==dwOldAspect)
pCache->Uncache(sd.dwConnection);
}
}

if (NULL!=pEnum)
pEnum->Release();
}

pCache->Release();
return NOERROR;
}



/*
* INOLE_SetIconInCache
*
* Purpose:
* Stores an iconic presentation metafile in the cache.
*
* Parameters:
* pObj IUnknown * of the object.
* hMetaPict HGLOBAL containing the presentation.
*
* Return Value:
* HRESULT From IOleCache::SetData.
*/

STDAPI INOLE_SetIconInCache(IUnknown *pObj, HGLOBAL hMetaPict)
{
IOleCache *pCache;
FORMATETC fe;
STGMEDIUM stm;
HRESULT hr;

if (NULL==hMetaPict)
return ResultFromScode(E_INVALIDARG);

if (FAILED(pObj->QueryInterface(IID_IOleCache, (void **)&pCache)))
return ResultFromScode(E_NOINTERFACE);

SETFormatEtc(fe, CF_METAFILEPICT, DVASPECT_ICON, NULL
, TYMED_MFPICT, -1);

stm.tymed=TYMED_MFPICT;
stm.hGlobal=hMetaPict;
stm.pUnkForRelease=NULL;

hr=pCache->SetData(&fe, &stm, FALSE);
pCache->Release();

return hr;
}





/*
* INOLE_GetUserTypeOfClass
*
* Purpose:
* Returns the user type (human readable class name) of the
* specified class as stored in the registry.
*
* Parameters:
* clsID CLSID in question
* iName UINT index to the name to retrieve, where
* zero is the name found as the value of
* of the CLSID, anything else tries
* AuxUserType\iName.
* pszUserType LPTSTR in which to return the type
* cch UINT length of pszUserType
*
* Return Value:
* UINT Number of characters in returned string.
* 0 on error.
*/

STDAPI_(UINT) INOLE_GetUserTypeOfClass(REFCLSID clsID, UINT iName
, LPTSTR pszUserType, UINT cch)
{
LPTSTR pszCLSID;
LPTSTR pszProgID;
TCHAR szKey[300];
LONG dw;
LONG lRet;

if (!pszUserType)
return 0;

*pszUserType='\0';

//Get a string containing the class name
StringFromCLSID(clsID, &pszCLSID);

if (0==iName)
wsprintf(szKey, TEXT("CLSID\\%s"), pszCLSID);
else
{
wsprintf(szKey, TEXT("CLSID\\%s\\AuxUserType\\%u")
, pszCLSID, iName);
}

CoTaskMemFree(pszCLSID);

dw=cch;
lRet=RegQueryValue(HKEY_CLASSES_ROOT, szKey, pszUserType, &dw);

if (ERROR_SUCCESS!=lRet)
{
lstrcpyn(pszUserType, TEXT("Unknown"), cch);

if (CoIsOle1Class(clsID))
{
//Try to get ProgID value for OLE 1 class
ProgIDFromCLSID(clsID, &pszProgID);

dw=cch;
lRet=RegQueryValue(HKEY_CLASSES_ROOT, pszProgID
, pszUserType, &dw);

CoTaskMemFree(pszProgID);

if (ERROR_SUCCESS!=lRet)
dw=0;
}
}

return (UINT)dw;
}





/*
* INOLE_DoConvert
*
* Purpose:
* Convert an embedded or linked object to another type, working
* in conjunection with OleUIConvert.
*
* Parameters:
* pIStorage IStorage * to the object's data.
* clsID CLSID to which we convert the object.
*
* Return Value:
* HRESULT The usual.
*/

STDAPI INOLE_DoConvert(IStorage *pIStorage, REFCLSID clsID)
{
HRESULT hr;
CLSID clsIDOrg;
CLIPFORMAT cfOrg;
LPTSTR pszOrg=NULL;
TCHAR szNew[256];

if (FAILED(ReadClassStg(pIStorage, &clsIDOrg)))
return ResultFromScode(E_FAIL);

//Read original format/user type
hr=ReadFmtUserTypeStg(pIStorage, &cfOrg, &pszOrg);

//Get new user type
if (0==INOLE_GetUserTypeOfClass(clsID, 0, szNew, 256))
lstrcpy(szNew, TEXT(""));

//Write new class into the storage
if (SUCCEEDED(WriteClassStg(pIStorage, clsID)))
{
if (SUCCEEDED(WriteFmtUserTypeStg(pIStorage, cfOrg
, szNew)))
{
SetConvertStg(pIStorage, TRUE);
CoTaskMemFree((void *)pszOrg);
return NOERROR;
}

//Failed to write new type, restore the old class
WriteClassStg(pIStorage, clsIDOrg);
}

CoTaskMemFree((void *)pszOrg);
return ResultFromScode(E_FAIL);
}




/*
* INOLE_CopyString
*
* Purpose:
* Copies a string allocated with CoTaskMemAlloc.
*
* Parameters:
* pszSrc LPTSTR to the string to copy.
*
* Return Value:
* LPTSTR New string or a NULL.
*/

STDAPI_(LPTSTR) INOLE_CopyString(LPTSTR pszSrc)
{
IMalloc *pIMalloc;
LPTSTR pszDst;
UINT cch;

cch=lstrlen(pszSrc);

if (FAILED(CoGetMalloc(MEMCTX_TASK, &pIMalloc)))
return NULL;

pszDst=(LPTSTR)pIMalloc->Alloc((cch+1)*sizeof(TCHAR));

if (NULL!=pszDst)
lstrcpy(pszDst, pszSrc);

pIMalloc->Release();
return pszDst;
}




/*
* INOLE_ObjectDescriptorFromOleObject
*
* Purpose:
* Fills and returns an OBJECTDESCRIPTOR structure. Information
* for the structure is obtained from an IOleObject instance.
*
* Parameters:
* pObj IOleObject * from which to retrieve information.
* dwAspect DWORD with the display aspect
* ptl POINTL from upper-left corner of object where
* mouse went down for use with Drag & Drop.
* pszl LPSIZEL (optional) if the object is being scaled in
* its container, then the container should pass the
* extents that it is using to display the object.
*
* Return Value:
* HBGLOBAL Handle to OBJECTDESCRIPTOR structure.
*/

STDAPI_(HGLOBAL) INOLE_ObjectDescriptorFromOleObject
(IOleObject *pObj, DWORD dwAspect, POINTL ptl, LPSIZEL pszl)
{
CLSID clsID;
LPTSTR pszName=NULL;
LPTSTR pszSrc=NULL;
BOOL fLink=FALSE;
IOleLink *pLink;
TCHAR szName[512];
DWORD dwMisc=0;
SIZEL szl;
HGLOBAL hMem;
HRESULT hr;
#ifdef WIN32ANSI
//#ifndef UNICODE
LPWSTRpszw;
char szTemp[512];
#endif

if (SUCCEEDED(pObj->QueryInterface(IID_IOleLink
, (void **)&pLink)))
fLink=TRUE;

if (FAILED(pObj->GetUserClassID(&clsID)))
clsID=CLSID_NULL;

//Get user string, expand to "Linked %s" if this is link
#ifndef WIN32ANSI
//#ifdef UNICODE
pObj->GetUserType(USERCLASSTYPE_FULL, &pszName);
#else
pObj->GetUserType(USERCLASSTYPE_FULL, &pszw);
WideCharToMultiByte(CP_ACP, 0, pszw, -1, szTemp
, sizeof(szTemp), NULL, NULL);
pszName=szTemp;
#endif

if (fLink && NULL!=pszName)
wsprintf(szName, TEXT("Linked %s"), pszName);
else
lstrcpy(szName, pszName);

#ifndef WIN32ANSI
//#ifdef UNICODE
CoTaskMemFree(pszName);
#else
CoTaskMemFree(pszw);
pszw=NULL;
#endif

/*
* Get the source name of this object using either the
* link display name (for link) or a moniker display
* name.
*/

if (fLink)
{
#ifndef WIN32ANSI
//#ifdef UNICODE
hr=pLink->GetSourceDisplayName(&pszSrc);
#else
hr=pLink->GetSourceDisplayName(&pszw);
WideCharToMultiByte(CP_ACP, 0, pszw, -1, szTemp
, sizeof(szTemp), NULL, NULL);
pszSrc=szTemp;
#endif
}
else
{
IMoniker *pmk;

hr=pObj->GetMoniker(OLEGETMONIKER_TEMPFORUSER
, OLEWHICHMK_OBJFULL, &pmk);

if (SUCCEEDED(hr))
{
IBindCtx *pbc;
CreateBindCtx(0, &pbc);

#ifndef WIN32ANSI
//#ifdef UNICODE
pmk->GetDisplayName(pbc, NULL, &pszSrc);
#else
pmk->GetDisplayName(pbc, NULL, &pszw);
WideCharToMultiByte(CP_ACP, 0, pszw, -1, szTemp
, sizeof(szTemp), NULL, NULL);
pszSrc=szTemp;
#endif

pbc->Release();

pmk->Release();
}
}

if (fLink)
pLink->Release();

//Get MiscStatus bits
hr=pObj->GetMiscStatus(dwAspect, &dwMisc);

//Get OBJECTDESCRIPTOR
hMem=INOLE_AllocObjectDescriptor(clsID, dwAspect, szl, ptl
, dwMisc, szName, pszSrc);

#ifndef WIN32ANSI
//#ifdef UNICODE
CoTaskMemFree(pszSrc);
#else
if (NULL!=pszw)
CoTaskMemFree(pszw);
#endif

return hMem;
}





/*
* INOLE_AllocObjectDescriptor
*
* Purpose:
* Allocated and fills an OBJECTDESCRIPTOR structure.
*
* Parameters:
* clsID CLSID to store.
* dwAspect DWORD with the display aspect
* pszl LPSIZEL (optional) if the object is being scaled in
* its container, then the container should pass the
* extents that it is using to display the object.
* ptl POINTL from upper-left corner of object where
* mouse went down for use with Drag & Drop.
* dwMisc DWORD containing MiscStatus flags
* pszName LPTSTR naming the object to copy
* pszSrc LPTSTR identifying the source of the object.
*
* Return Value:
* HBGLOBAL Handle to OBJECTDESCRIPTOR structure.
*/

STDAPI_(HGLOBAL) INOLE_AllocObjectDescriptor(CLSID clsID
, DWORD dwAspect, SIZEL szl, POINTL ptl, DWORD dwMisc
, LPTSTR pszName, LPTSTR pszSrc)
{
HGLOBAL hMem=NULL;
LPOBJECTDESCRIPTOR pOD;
DWORD cb, cbStruct;
DWORD cchName, cchSrc;

cchName=lstrlen(pszName)+1;

if (NULL!=pszSrc)
cchSrc=lstrlen(pszSrc)+1;
else
{
cchSrc=cchName;
pszSrc=pszName;
}

/*
* Note: CFSTR_OBJECTDESCRIPTOR is an ANSI structure.
* That means strings in it must be ANSI. OLE will do
* internal conversions back to Unicode as necessary,
* but we have to put ANSI strings in it ourselves.
*/
cbStruct=sizeof(OBJECTDESCRIPTOR);
cb=cbStruct+(sizeof(WCHAR)*(cchName+cchSrc)); //HACK

hMem=GlobalAlloc(GHND, cb);

if (NULL==hMem)
return NULL;

pOD=(LPOBJECTDESCRIPTOR)GlobalLock(hMem);

pOD->cbSize=cb;
pOD->clsid=clsID;
pOD->dwDrawAspect=dwAspect;
pOD->sizel=szl;
pOD->pointl=ptl;
pOD->dwStatus=dwMisc;

if (pszName)
{
pOD->dwFullUserTypeName=cbStruct;
#ifdef WIN32ANSI
MultiByteToWideChar(CP_ACP, 0, pszName, -1
, (LPWSTR)((LPBYTE)pOD+pOD->dwFullUserTypeName), cchName);
#else
lstrcpy((LPTSTR)((LPBYTE)pOD+pOD->dwFullUserTypeName)
, pszName);
#endif
}
else
pOD->dwFullUserTypeName=0; //No string

if (pszSrc)
{
pOD->dwSrcOfCopy=cbStruct+(cchName*sizeof(WCHAR));

#ifdef WIN32ANSI
MultiByteToWideChar(CP_ACP, 0, pszSrc, -1
, (LPWSTR)((LPBYTE)pOD+pOD->dwSrcOfCopy), cchSrc);
#else
lstrcpy((LPTSTR)((LPBYTE)pOD+pOD->dwSrcOfCopy), pszSrc);
#endif
}
else
pOD->dwSrcOfCopy=0; //No string

GlobalUnlock(hMem);
return hMem;
}





/*
* INOLE_CreateStorageOnHGlobal
*
* Purpose:
* Create a memory based storage object on an memory lockbytes.
*
* Parameters:
* grfMode -- flags passed to StgCreateDocfileOnILockBytes
*
* NOTE: if hGlobal is NULL, then a new IStorage is created and
* STGM_CREATE flag is passed to StgCreateDocfileOnILockBytes.
* if hGlobal is non-NULL, then it is assumed that the hGlobal already
* has an IStorage inside it and STGM_CONVERT flag is passed
* to StgCreateDocfileOnILockBytes.
*
* Return Value:
* IStorage * Pointer to the storage we create.
*/

STDAPI_(IStorage *) INOLE_CreateStorageOnHGlobal(DWORD grfMode)
{
DWORD grfCreate=grfMode | STGM_CREATE;
HRESULT hr;
ILockBytes *pLB=NULL;
IStorage *pIStorage=NULL;

hr=CreateILockBytesOnHGlobal(NULL, TRUE, &pLB);

if (FAILED(hr))
return NULL;

hr=StgCreateDocfileOnILockBytes(pLB, grfCreate, 0, &pIStorage);

pLB->Release();

return SUCCEEDED(hr) ? pIStorage : NULL;
}








/*
* INOLE_GetLinkSourceData
*
* Purpose:
* Creates a CFSTR_LINKSOURCE data format, that is, a serialized
* moniker in a stream.
*
* Parameters:
* pmk IMoniker * of the link.
* pClsID CLSID * to write into the stream.
* pFE FORMATETC * describing the data which
* should have TYMED_ISTREAM in it.
* pSTM STGMEDIUM in which to return the data.
* If this has TYMED_NULL, then this function
* allocates the stream, otherwise it writes
* into the stream in pSTM->pstm.
*/

STDAPI INOLE_GetLinkSourceData(IMoniker *pmk, LPCLSID pClsID
, LPFORMATETC pFE, LPSTGMEDIUM pSTM)
{
LPSTREAM pIStream = NULL;
DWORD reserved = 0;
HRESULT hr;

pSTM->pUnkForRelease=NULL;

if (TYMED_NULL==pSTM->tymed)
{
if (pFE->tymed & TYMED_ISTREAM)
{
hr=CreateStreamOnHGlobal(NULL, TRUE, &pIStream);

if (FAILED(hr))
return ResultFromScode(E_OUTOFMEMORY);

pSTM->pstm=pIStream;
pSTM->tymed=TYMED_ISTREAM;
}
else
return ResultFromScode(DATA_E_FORMATETC);
}
else
{
if (TYMED_ISTREAM==pSTM->tymed)
{
pSTM->tymed=TYMED_ISTREAM;
pSTM->pstm=pSTM->pstm;
}
else
return ResultFromScode(DATA_E_FORMATETC);
}

hr=OleSaveToStream(pmk, pSTM->pstm);

if (FAILED(hr))
return hr;

return WriteClassStm(pSTM->pstm, *pClsID);
}





/*
* INOLE_RegisterAsRunning
*
* Purpose:
* Registers a moniker as running in the running object
* table, revoking any existing registration.
*
* Parameters:
* pUnk IUnknown * of the object.
* pmk IMoniker * naming the object.
* dwFlags DWORD flags to pass to IROT::Register
* pdwReg LPDWORD in which to store the registration key.
*
* Return Value:
* None
*/

STDAPI_(void) INOLE_RegisterAsRunning(IUnknown *pUnk
, IMoniker *pmk, DWORD dwFlags, LPDWORD pdwReg)
{
IRunningObjectTable *pROT;
HRESULT hr;
DWORD dwReg=*pdwReg;

if (NULL==pmk || NULL==pUnk || NULL==pdwReg)
return;

dwReg=*pdwReg;

if (FAILED(GetRunningObjectTable(0, &pROT)))
return;

hr=pROT->Register(dwFlags, pUnk, pmk, pdwReg);

if (MK_S_MONIKERALREADYREGISTERED==GetScode(hr))
{
if (0!=dwReg)
pROT->Revoke(dwReg);
}

pROT->Release();
return;
}



/*
* INOLE_RevokeAsRunning
*
* Purpose:
* Wrapper for IRunningObjectTable::Revoke
*
* Parameters:
* pdwReg LPDWORD containing the key on input, zeroed
* on output.
*
* Return Value:
* None
*/

STDAPI_(void) INOLE_RevokeAsRunning(LPDWORD pdwReg)
{
IRunningObjectTable *pROT;

if (0==*pdwReg)
return;

if (FAILED(GetRunningObjectTable(0, &pROT)))
return;

pROT->Revoke(*pdwReg);
pROT->Release();

*pdwReg=0;
return;
}



/*
* INOLE_NoteChangeTime
*
* Purpose:
* Wrapper for IRunningObjectTable::NoteChangeTime
*
* Parameters:
* dwReg DWORD identifying the running object to note.
* pft FILETIME * containing the new time. If NULL,
* then we'll get it from CoFileTimeNow.
* pszFile LPTSTR to a filename. If pft is NULL, we'll
* retrieve times from this file.
*
* Return Value:
* None
*/

STDAPI_(void) INOLE_NoteChangeTime(DWORD dwReg, FILETIME *pft
, LPTSTR pszFile)
{
IRunningObjectTable *pROT;
FILETIME ft;

if (NULL==pft)
{
CoFileTimeNow(&ft);
pft=&ft;
}

if (NULL!=pszFile)
GetFileTimes(pszFile, pft);

if (FAILED(GetRunningObjectTable(0, &pROT)))
return;

pROT->NoteChangeTime(dwReg, pft);
pROT->Release();
return;
}




/*
* GetFileTimes
* (Internal)
*
* Purpose:
* Retrieve the FILETIME structure for a given file.
*/

static BOOL GetFileTimes(LPTSTR pszFileName, FILETIME *pft)
{
#ifdef WIN32
WIN32_FIND_DATA fd;
HANDLE hFind;

hFind=FindFirstFile(pszFileName, &fd);

if (NULL==hFind || INVALID_HANDLE_VALUE==hFind)
return FALSE;

FindClose(hFind);
*pft=fd.ftLastWriteTime;
return TRUE;
#else
static char sz[256];
static struct _find_t fileinfo;

lstrcpyn((LPSTR)sz, pszFileName, sizeof(sz)-1);
sz[sizeof(sz)-1]= '\0';
AnsiToOem(sz, sz);

return (_dos_findfirst(sz,_A_NORMAL|_A_HIDDEN|_A_SUBDIR|_A_SYSTEM
, (struct _find_t *)&fileinfo) == 0
&& CoDosDateTimeToFileTime(fileinfo.wr_date, fileinfo.wr_time
, pft));
#endif
}