OLESTD.C

/************************************************************************* 
**
** OLE 2 Standard Utilities
**
** olestd.c
**
** This file contains utilities that are useful for most standard
** OLE 2.0 compound document type applications.
**
** (c) Copyright Microsoft Corp. 1992-1996 All Rights Reserved
**
*************************************************************************/

#define NONAMELESSUNION // use strict ANSI standard (for DVOBJ.H)

#define STRICT 1
#include "olestd.h"
#include <stdlib.h>
#include <ctype.h>
#include <shellapi.h>
#include "common.h"

OLEDBGDATA

static char szAssertMemAlloc[] = "CoGetMalloc failed";
static int IsCloseFormatEtc(FORMATETC FAR* pFetcLeft, FORMATETC FAR* pFetcRight);


/* OleStdSetupAdvises
** ------------------
** Setup the standard View advise required by a standard,
** compound document-oriented container. Such a container relies on
** Ole to manage the presentation of the Ole object. The container
** call IViewObject::Draw to render (display) the object.
**
** This helper routine performs the following tasks:
** setup View advise
** Call IOleObject::SetHostNames
** Call OleSetContainedObject
**
** fCreate should be set to TRUE if the object is being created. if
** an existing object is being loaded, then fCreate should be FALSE.
** if it is a creation situation, then the ADVF_PRIMEFIRST flag is
** used when settinp up the IViewObject::Advise. This will result in
** the immediate sending of the initial picture.
**
** NOTE: the standard container does NOT need to set up an OLE
** Advise (IOleObject::Advise). this routine does NOT set up an OLE
** Advise (a previous version of this function used to setup this
** advise, but it was not useful).
*/
STDAPI_(BOOL) OleStdSetupAdvises(LPOLEOBJECT lpOleObject, DWORD dwDrawAspect,
LPOLESTR lpszContainerApp, LPOLESTR lpszContainerObj,
LPADVISESINK lpAdviseSink, BOOL fCreate)
{
LPVIEWOBJECT lpViewObject;
HRESULT hrErr;
BOOL fStatus = TRUE;
#if defined( SPECIAL_CONTAINER )
DWORD dwTemp;
#endif

hrErr = lpOleObject->lpVtbl->QueryInterface(
lpOleObject,
&IID_IViewObject,
(LPVOID FAR*)&lpViewObject
);

/* Setup View advise */
if (hrErr == NOERROR) {

OLEDBG_BEGIN2("IViewObject::SetAdvise called\r\n")
lpViewObject->lpVtbl->SetAdvise(
lpViewObject,
dwDrawAspect,
(fCreate ? ADVF_PRIMEFIRST : 0),
lpAdviseSink
);
OLEDBG_END2

OleStdRelease((LPUNKNOWN)lpViewObject);
} else {
fStatus = FALSE;
}

#if defined( SPECIAL_CONTAINER )
/* Setup OLE advise.
** NOTE: normally containers do NOT need to setup an OLE
** advise. this advise connection is only useful for the OLE's
** DefHandler and the OleLink object implementation. some
** special container's might need to setup this advise for
** programatic reasons.
**
** NOTE: this advise will be torn down automatically by the
** server when we release the object, therefore we do not need
** to store the connection id.
*/
OLEDBG_BEGIN2("IOleObject::Advise called\r\n")
hrErr = lpOleObject->lpVtbl->Advise(
lpOleObject,
lpAdviseSink,
(DWORD FAR*)&dwTemp
);
OLEDBG_END2
if (hrErr != NOERROR) fStatus = FALSE;
#endif

/* Setup the host names for the OLE object. */
OLEDBG_BEGIN2("IOleObject::SetHostNames called\r\n")
hrErr = lpOleObject->lpVtbl->SetHostNames(
lpOleObject,
lpszContainerApp,
lpszContainerObj
);
OLEDBG_END2
if (hrErr != NOERROR) fStatus = FALSE;

/* Inform the loadded object's handler/inproc-server that it is in
** its embedding container's process.
*/
OLEDBG_BEGIN2("OleSetContainedObject(TRUE) called\r\n")
OleSetContainedObject((LPUNKNOWN)lpOleObject, TRUE);
OLEDBG_END2

return fStatus;
}


/* OleStdSwitchDisplayAspect
** -------------------------
** Switch the currently cached display aspect between DVASPECT_ICON
** and DVASPECT_CONTENT.
**
** NOTE: when setting up icon aspect, any currently cached content
** cache is discarded and any advise connections for content aspect
** are broken.
**
** RETURNS:
** S_OK -- new display aspect setup successfully
** E_INVALIDARG -- IOleCache interface is NOT supported (this is
** required).
** <other SCODE> -- any SCODE that can be returned by
** IOleCache::Cache method.
** NOTE: if an error occurs then the current display aspect and
** cache contents unchanged.
*/
STDAPI OleStdSwitchDisplayAspect(
LPOLEOBJECT lpOleObj,
LPDWORD lpdwCurAspect,
DWORD dwNewAspect,
HGLOBAL hMetaPict,
BOOL fDeleteOldAspect,
BOOL fSetupViewAdvise,
LPADVISESINK lpAdviseSink,
BOOL FAR* lpfMustUpdate
)
{
LPOLECACHE lpOleCache = NULL;
LPVIEWOBJECT lpViewObj = NULL;
LPENUMSTATDATA lpEnumStatData = NULL;
STATDATA StatData;
FORMATETC FmtEtc;
STGMEDIUM Medium;
DWORD dwAdvf;
DWORD dwNewConnection;
DWORD dwOldAspect = *lpdwCurAspect;
HRESULT hrErr;

if (lpfMustUpdate)
*lpfMustUpdate = FALSE;

lpOleCache = (LPOLECACHE)OleStdQueryInterface(
(LPUNKNOWN)lpOleObj,&IID_IOleCache);

// if IOleCache* is NOT available, do nothing
if (! lpOleCache)
return E_INVALIDARG;

// Setup new cache with the new aspect
FmtEtc.cfFormat = 0; // whatever is needed to draw
FmtEtc.ptd = NULL;
FmtEtc.dwAspect = dwNewAspect;
FmtEtc.lindex = -1;
FmtEtc.tymed = TYMED_NULL;

/* NOTE: if we are setting up Icon aspect with a custom icon
** then we do not want DataAdvise notifications to ever change
** the contents of the data cache. thus we set up a NODATA
** advise connection. otherwise we set up a standard DataAdvise
** connection.
*/
if (dwNewAspect == DVASPECT_ICON && hMetaPict)
dwAdvf = ADVF_NODATA;
else
dwAdvf = ADVF_PRIMEFIRST;

OLEDBG_BEGIN2("IOleCache::Cache called\r\n")
hrErr = lpOleCache->lpVtbl->Cache(
lpOleCache,
(LPFORMATETC)&FmtEtc,
dwAdvf,
(LPDWORD)&dwNewConnection
);
OLEDBG_END2

if (! SUCCEEDED(hrErr)) {
OleDbgOutHResult("IOleCache::Cache returned", hrErr);
OleStdRelease((LPUNKNOWN)lpOleCache);
return hrErr;
}

*lpdwCurAspect = dwNewAspect;

/* NOTE: if we are setting up Icon aspect with a custom icon,
** then stuff the icon into the cache. otherwise the cache must
** be forced to be updated. set the *lpfMustUpdate flag to tell
** caller to force the object to Run so that the cache will be
** updated.
*/
if (dwNewAspect == DVASPECT_ICON && hMetaPict) {

FmtEtc.cfFormat = CF_METAFILEPICT;
FmtEtc.ptd = NULL;
FmtEtc.dwAspect = DVASPECT_ICON;
FmtEtc.lindex = -1;
FmtEtc.tymed = TYMED_MFPICT;

Medium.tymed = TYMED_MFPICT;
Medium.u.hGlobal = hMetaPict;
Medium.pUnkForRelease = NULL;

OLEDBG_BEGIN2("IOleCache::SetData called\r\n")
hrErr = lpOleCache->lpVtbl->SetData(
lpOleCache,
(LPFORMATETC)&FmtEtc,
(LPSTGMEDIUM)&Medium,
FALSE /* fRelease */
);
OLEDBG_END2
} else {
if (lpfMustUpdate)
*lpfMustUpdate = TRUE;
}

if (fSetupViewAdvise && lpAdviseSink) {
/* NOTE: re-establish the ViewAdvise connection */
lpViewObj = (LPVIEWOBJECT)OleStdQueryInterface(
(LPUNKNOWN)lpOleObj,&IID_IViewObject);

if (lpViewObj) {

OLEDBG_BEGIN2("IViewObject::SetAdvise called\r\n")
lpViewObj->lpVtbl->SetAdvise(
lpViewObj,
dwNewAspect,
0,
lpAdviseSink
);
OLEDBG_END2

OleStdRelease((LPUNKNOWN)lpViewObj);
}
}

/* NOTE: remove any existing caches that are set up for the old
** display aspect. It WOULD be possible to retain the caches set
** up for the old aspect, but this would increase the storage
** space required for the object and possibly require additional
** overhead to maintain the unused cachaes. For these reasons the
** strategy to delete the previous caches is prefered. if it is a
** requirement to quickly switch between Icon and Content
** display, then it would be better to keep both aspect caches.
*/

if (fDeleteOldAspect) {
OLEDBG_BEGIN2("IOleCache::EnumCache called\r\n")
hrErr = lpOleCache->lpVtbl->EnumCache(
lpOleCache,
(LPENUMSTATDATA FAR*)&lpEnumStatData
);
OLEDBG_END2

while(hrErr == NOERROR) {
hrErr = lpEnumStatData->lpVtbl->Next(
lpEnumStatData,
1,
(LPSTATDATA)&StatData,
NULL
);
if (hrErr != NOERROR)
break; // DONE! no more caches.

if (StatData.formatetc.dwAspect == dwOldAspect) {

// Remove previous cache with old aspect
OLEDBG_BEGIN2("IOleCache::Uncache called\r\n")
lpOleCache->lpVtbl->Uncache(lpOleCache,StatData.dwConnection);
OLEDBG_END2
}
}

if (lpEnumStatData) {
OleStdVerifyRelease(
(LPUNKNOWN)lpEnumStatData,
OLESTR ("OleStdSwitchDisplayAspect: Cache enumerator NOT released"));
}
}

if (lpOleCache)
OleStdRelease((LPUNKNOWN)lpOleCache);

return NOERROR;
}


/* OleStdSetIconInCache
** --------------------
** SetData a new icon into the existing DVASPECT_ICON cache.
**
** RETURNS:
** HRESULT returned from IOleCache::SetData
*/
STDAPI OleStdSetIconInCache(LPOLEOBJECT lpOleObj, HGLOBAL hMetaPict)
{
LPOLECACHE lpOleCache = NULL;
FORMATETC FmtEtc;
STGMEDIUM Medium;
HRESULT hrErr;

if (! hMetaPict)
return FALSE; // invalid icon

lpOleCache = (LPOLECACHE)OleStdQueryInterface(
(LPUNKNOWN)lpOleObj,&IID_IOleCache);
if (! lpOleCache)
return FALSE; // if IOleCache* is NOT available, do nothing

FmtEtc.cfFormat = CF_METAFILEPICT;
FmtEtc.ptd = NULL;
FmtEtc.dwAspect = DVASPECT_ICON;
FmtEtc.lindex = -1;
FmtEtc.tymed = TYMED_MFPICT;

// stuff the icon into the cache.
Medium.tymed = TYMED_MFPICT;
Medium.u.hGlobal = hMetaPict;
Medium.pUnkForRelease = NULL;

OLEDBG_BEGIN2("IOleCache::SetData called\r\n")
hrErr = lpOleCache->lpVtbl->SetData(
lpOleCache,
(LPFORMATETC)&FmtEtc,
(LPSTGMEDIUM)&Medium,
FALSE /* fRelease */
);
OLEDBG_END2

OleStdRelease((LPUNKNOWN)lpOleCache);

return hrErr;
}



/* OleStdDoConvert
** ---------------
** Do the container-side responsibilities for converting an object.
** This function would be used in conjunction with the OleUIConvert
** dialog. If the user selects to convert an object then the
** container must do the following:
** 1. unload the object.
** 2. write the NEW CLSID and NEW user type name
** string into the storage of the object,
** BUT write the OLD format tag.
** 3. force an update of the object to force the actual
** conversion of the data bits.
**
** This function takes care of step 2.
*/
STDAPI OleStdDoConvert(LPSTORAGE lpStg, REFCLSID rClsidNew)
{
HRESULT error;
CLSID clsidOld;
CLIPFORMAT cfOld;
LPOLESTR lpszOld = NULL;
OLECHAR szNew[OLEUI_CCHKEYMAX];

if ((error = ReadClassStg(lpStg, &clsidOld)) != NOERROR) {
clsidOld = CLSID_NULL;
goto errRtn;
}

// read old fmt/old user type; sets out params to NULL on error
error = ReadFmtUserTypeStg(lpStg, &cfOld, &lpszOld);
OleDbgAssert(error == NOERROR || (cfOld == 0 && lpszOld == NULL));

// get new user type name; if error, set to NULL string
if (OleStdGetUserTypeOfClass(
(LPCLSID)rClsidNew, szNew, OLEUI_CCHKEYMAX,NULL /* hKey */) == 0)
OLESTRCPY (szNew, OLESTR (""));

// write class stg
if ((error = WriteClassStg(lpStg, rClsidNew)) != NOERROR)
goto errRtn;

// write old fmt/new user type;
if ((error = WriteFmtUserTypeStg(lpStg, cfOld, szNew)) != NOERROR)
goto errRewriteInfo;

// set convert bit
if ((error = SetConvertStg(lpStg, TRUE)) != NOERROR)
goto errRewriteInfo;

goto okRtn;

errRewriteInfo:
(void)WriteClassStg(lpStg, &clsidOld);
(void)WriteFmtUserTypeStg(lpStg, cfOld, lpszOld);

errRtn:

okRtn:
OleStdFreeString(lpszOld, NULL);
return error;
}


/* OleStdGetTreatAsFmtUserType
** ---------------------------
** Determine if the application should perform a TreatAs (ActivateAs
** object or emulation) operation for the object that is stored in
** the storage.
**
** if the CLSID written in the storage is not the same as the
** application's own CLSID (clsidApp), then a TreatAs operation
** should take place. if so determine the format the data should be
** written and the user type name of the object the app should
** emulate (ie. pretend to be). if this information is not written
** in the storage then it is looked up in the REGDB. if it can not
** be found in the REGDB, then the TreatAs operation can NOT be
** executed.
**
** RETURNS: TRUE -- if TreatAs should be performed.
** valid lpclsid, lplpszType, lpcfFmt to TreatAs are returned
** (NOTE: lplpszType must be freed by caller)
** FALSE -- NO TreatAs. lpszType will be NULL.
** lpclsid = CLSID_NULL; lplpszType = lpcfFmt = NULL;
*/
STDAPI_(BOOL) OleStdGetTreatAsFmtUserType(
REFCLSID rclsidApp,
LPSTORAGE lpStg,
CLSID FAR* lpclsid,
CLIPFORMAT FAR* lpcfFmt,
LPOLESTR FAR* lplpszType
)
{
HRESULT hrErr;
HKEY hKey;
LONG lRet;
UINT lSize;
OLECHAR szBuf[OLEUI_CCHKEYMAX];

*lpclsid = CLSID_NULL;
*lpcfFmt = 0;
*lplpszType = NULL;

hrErr = ReadClassStg(lpStg, lpclsid);
if (hrErr == NOERROR &&
! IsEqualCLSID(lpclsid, &CLSID_NULL) &&
! IsEqualCLSID(lpclsid, rclsidApp)) {

hrErr = ReadFmtUserTypeStg(lpStg,(CLIPFORMAT FAR*)lpcfFmt,lplpszType);
if (hrErr == NOERROR && lplpszType && *lpcfFmt != 0)
return TRUE; // Do TreatAs. info was in lpStg.

/* read info from REGDB
** *lpcfFmt = value of field: CLSID\{...}\DataFormats\DefaultFile
** *lplpszType = value of field: CLSID\{...}
*/
//Open up the root key.
lRet=RegOpenKey(HKEY_CLASSES_ROOT, NULL, &hKey);
if (lRet != (LONG)ERROR_SUCCESS)
return FALSE;
*lpcfFmt = OleStdGetDefaultFileFormatOfClass(lpclsid, hKey);
if (*lpcfFmt == 0)
return FALSE;
lSize = OleStdGetUserTypeOfClass(lpclsid,szBuf,OLEUI_CCHKEYMAX, hKey);
if (lSize == 0)
return FALSE;
*lplpszType = OleStdCopyString(szBuf, NULL);
} else {
return FALSE; // NO TreatAs
}
}



/* OleStdDoTreatAsClass
** --------------------
** Do the container-side responsibilities for "ActivateAs" (aka.
** TreatAs) for an object.
** This function would be used in conjunction with the OleUIConvert
** dialog. If the user selects to ActivateAs an object then the
** container must do the following:
** 1. unload ALL objects of the OLD class that app knows about
** 2. add the TreatAs tag in the registration database
** by calling CoTreatAsClass().
** 3. lazily it can reload the objects; when the objects
** are reloaded the TreatAs will take effect.
**
** This function takes care of step 2.
*/
STDAPI OleStdDoTreatAsClass(LPOLESTR lpszUserType, REFCLSID rclsid, REFCLSID rclsidNew)
{
HRESULT hrErr;
LPOLESTR lpszCLSID = NULL;
char lpszAnsiCLSID[256];
LONG lRet;
HKEY hKey;
char lpszAnsiUserType[256];

OLEDBG_BEGIN2("CoTreatAsClass called\r\n")
hrErr = CoTreatAsClass(rclsid, rclsidNew);
OLEDBG_END2

if ((hrErr != NOERROR) && lpszUserType) {
lRet = RegOpenKey(HKEY_CLASSES_ROOT, (LPCSTR)"CLSID",
(HKEY FAR *)&hKey);
StringFromCLSID(rclsid, /*(LPOLESTR FAR*)*/&lpszCLSID);
W2A (lpszCLSID, lpszAnsiCLSID, 256);
W2A (lpszUserType, lpszAnsiUserType, 256);
RegSetValue(hKey, lpszAnsiCLSID, REG_SZ, lpszAnsiUserType,
lstrlen(lpszAnsiUserType));

if (lpszCLSID)
OleStdFreeString(lpszCLSID, NULL);

hrErr = CoTreatAsClass(rclsid, rclsidNew);
RegCloseKey(hKey);
}

return hrErr;
}



/* OleStdIsOleLink
** ---------------
** Returns TRUE if the OleObject is infact an OLE link object. this
** checks if IOleLink interface is supported. if so, the object is a
** link, otherwise not.
*/
STDAPI_(BOOL) OleStdIsOleLink(LPUNKNOWN lpUnk)
{
LPOLELINK lpOleLink;

lpOleLink = (LPOLELINK)OleStdQueryInterface(lpUnk, &IID_IOleLink);

if (lpOleLink) {
OleStdRelease((LPUNKNOWN)lpOleLink);
return TRUE;
} else
return FALSE;
}


/* OleStdQueryInterface
** --------------------
** Returns the desired interface pointer if exposed by the given object.
** Returns NULL if the interface is not available.
** eg.:
** lpDataObj = OleStdQueryInterface(lpOleObj, &IID_DataObject);
*/
STDAPI_(LPUNKNOWN) OleStdQueryInterface(LPUNKNOWN lpUnk, REFIID riid)
{
LPUNKNOWN lpInterface;
HRESULT hrErr;

hrErr = lpUnk->lpVtbl->QueryInterface(
lpUnk,
riid,
(LPVOID FAR*)&lpInterface
);

if (hrErr == NOERROR)
return lpInterface;
else
return NULL;
}


/* OleStdGetData
** -------------
** Retrieve data from an IDataObject in a specified format on a
** global memory block. This function ALWAYS returns a private copy
** of the data to the caller. if necessary a copy is made of the
** data (ie. if lpMedium->pUnkForRelease != NULL). The caller assumes
** ownership of the data block in all cases and must free the data
** when done with it. The caller may directly free the data handle
** returned (taking care whether it is a simple HGLOBAL or a HANDLE
** to a MetafilePict) or the caller may call
** ReleaseStgMedium(lpMedium). this OLE helper function will do the
** right thing.
**
** PARAMETERS:
** LPDATAOBJECT lpDataObj -- object on which GetData should be
** called.
** CLIPFORMAT cfFormat -- desired clipboard format (eg. CF_TEXT)
** DVTARGETDEVICE FAR* lpTargetDevice -- target device for which
** the data should be composed. This may
** be NULL. NULL can be used whenever the
** data format is insensitive to target
** device or when the caller does not care
** what device is used.
** LPSTGMEDIUM lpMedium -- ptr to STGMEDIUM struct. the
** resultant medium from the
** IDataObject::GetData call is
** returned.
**
** RETURNS:
** HGLOBAL -- global memory handle of retrieved data block.
** NULL -- if error.
*/
STDAPI_(HGLOBAL) OleStdGetData(
LPDATAOBJECT lpDataObj,
CLIPFORMAT cfFormat,
DVTARGETDEVICE FAR* lpTargetDevice,
DWORD dwDrawAspect,
LPSTGMEDIUM lpMedium
)
{
HRESULT hrErr;
FORMATETC formatetc;
HGLOBAL hGlobal = NULL;
HGLOBAL hCopy;
LPVOID lp;

formatetc.cfFormat = cfFormat;
formatetc.ptd = lpTargetDevice;
formatetc.dwAspect = dwDrawAspect;
formatetc.lindex = -1;

switch (cfFormat) {
case CF_METAFILEPICT:
formatetc.tymed = TYMED_MFPICT;
break;

case CF_BITMAP:
formatetc.tymed = TYMED_GDI;
break;

default:
formatetc.tymed = TYMED_HGLOBAL;
break;
}

OLEDBG_BEGIN2("IDataObject::GetData called\r\n")
hrErr = lpDataObj->lpVtbl->GetData(
lpDataObj,
(LPFORMATETC)&formatetc,
lpMedium
);
OLEDBG_END2

if (hrErr != NOERROR)
return NULL;

if ((hGlobal = lpMedium->u.hGlobal) == NULL)
return NULL;

// Check if hGlobal really points to valid memory
if ((lp = GlobalLock(hGlobal)) != NULL) {
if (IsBadReadPtr(lp, 1)) {
GlobalUnlock(hGlobal);
return NULL; // ERROR: memory is NOT valid
}
GlobalUnlock(hGlobal);
}

if (hGlobal != NULL && lpMedium->pUnkForRelease != NULL) {
/* NOTE: the callee wants to retain ownership of the data.
** this is indicated by passing a non-NULL pUnkForRelease.
** thus, we will make a copy of the data and release the
** callee's copy.
*/

hCopy = OleDuplicateData(hGlobal, cfFormat, GHND|GMEM_SHARE);
ReleaseStgMedium(lpMedium); // release callee's copy of data

hGlobal = hCopy;
lpMedium->u.hGlobal = hCopy;
lpMedium->pUnkForRelease = NULL;
}
return hGlobal;
}


/* OleStdMalloc
** ------------
** allocate memory using the currently active IMalloc* allocator
*/
STDAPI_(LPVOID) OleStdMalloc(ULONG ulSize)
{
LPVOID pout;
LPMALLOC pmalloc;

if (CoGetMalloc(MEMCTX_TASK, &pmalloc) != NOERROR) {
OleDbgAssertSz(0, szAssertMemAlloc);
return NULL;
}

pout = (LPVOID)pmalloc->lpVtbl->Alloc(pmalloc, ulSize);

if (pmalloc != NULL) {
ULONG refs = pmalloc->lpVtbl->Release(pmalloc);
}

return pout;
}


/* OleStdRealloc
** -------------
** re-allocate memory using the currently active IMalloc* allocator
*/
STDAPI_(LPVOID) OleStdRealloc(LPVOID pmem, ULONG ulSize)
{
LPVOID pout = NULL;
LPMALLOC pmalloc;

if (CoGetMalloc(MEMCTX_TASK, &pmalloc) != NOERROR) {
OleDbgAssertSz(0, szAssertMemAlloc);
return NULL;
}

if (pmalloc != NULL)
{
ULONG refs;
pout = (LPVOID)pmalloc->lpVtbl->Realloc(pmalloc, pmem, ulSize);
refs = pmalloc->lpVtbl->Release(pmalloc);
}

return pout;
}


/* OleStdFree
** ----------
** free memory using the currently active IMalloc* allocator
*/
STDAPI_(void) OleStdFree(LPVOID pmem)
{
LPMALLOC pmalloc;

if (pmem == NULL)
return;

if (CoGetMalloc(MEMCTX_TASK, &pmalloc) != NOERROR) {
OleDbgAssertSz(0, szAssertMemAlloc);
return;
}


if (pmalloc != NULL)
{
ULONG refs;
#if defined(_DEBUG)
int iReturn;
iReturn = pmalloc->lpVtbl->DidAlloc(pmalloc, pmem);
OleDbgAssertSz(1 == iReturn, "OleStdFree tried to release a suspicious pointer");
#endif
pmalloc->lpVtbl->Free(pmalloc, pmem);
refs = pmalloc->lpVtbl->Release(pmalloc);
}
}


/* OleStdGetSize
** -------------
** Get the size of a memory block that was allocated using the
** currently active IMalloc* allocator.
*/
STDAPI_(ULONG) OleStdGetSize(LPVOID pmem)
{
ULONG ulSize;
LPMALLOC pmalloc;

if (CoGetMalloc(MEMCTX_TASK, &pmalloc) != NOERROR) {
OleDbgAssertSz(0, szAssertMemAlloc);
return (ULONG)-1;
}

ulSize = pmalloc->lpVtbl->GetSize(pmalloc, pmem);

if (pmalloc != NULL) {
ULONG refs = pmalloc->lpVtbl->Release(pmalloc);
}

return ulSize;
}


/* OleStdFreeString
** ----------------
** Free a string that was allocated with the currently active
** IMalloc* allocator.
**
** if the caller has the current IMalloc* handy, then it can be
** passed as a argument, otherwise this function will retrieve the
** active allocator and use it.
*/
STDAPI_(void) OleStdFreeString(LPOLESTR lpsz, LPMALLOC lpMalloc)
{
BOOL fMustRelease = FALSE;

if (! lpMalloc) {
if (CoGetMalloc(MEMCTX_TASK, &lpMalloc) != NOERROR)
return;
fMustRelease = TRUE;
}

lpMalloc->lpVtbl->Free(lpMalloc, lpsz);

if (fMustRelease)
lpMalloc->lpVtbl->Release(lpMalloc);
}


/* OleStdCopyString
** ----------------
** Copy a string into memory allocated with the currently active
** IMalloc* allocator.
**
** if the caller has the current IMalloc* handy, then it can be
** passed as a argument, otherwise this function will retrieve the
** active allocator and use it.
*/
STDAPI_(LPOLESTR) OleStdCopyString(LPOLESTR lpszSrc, LPMALLOC lpMalloc)
{
LPOLESTR lpszDest = NULL;
BOOL fMustRelease = FALSE;
UINT lSize = OLESTRLEN(lpszSrc);

if (! lpMalloc) {
if (CoGetMalloc(MEMCTX_TASK, &lpMalloc) != NOERROR)
return NULL;
fMustRelease = TRUE;
}

lpszDest = lpMalloc->lpVtbl->Alloc(lpMalloc, (lSize+1)*sizeof(OLECHAR));

if (lpszDest)
OLESTRCPY(lpszDest, lpszSrc);

if (fMustRelease)
lpMalloc->lpVtbl->Release(lpMalloc);
return lpszDest;
}


/*
* OleStdCreateStorageOnHGlobal()
*
* Purpose:
* Create a memory based IStorage*.
*
* NOTE: if fDeleteOnRelease==TRUE, then the ILockBytes is created
* such that it will delete them memory on its last release.
* the IStorage on created on top of the ILockBytes in NOT
* created with STGM_DELETEONRELEASE. when the IStorage receives
* its last release, it will release the ILockBytes which will
* in turn free the memory. it is in fact an error to specify
* STGM_DELETEONRELEASE in this situation.
*
* Parameters:
* hGlobal -- handle to MEM_SHARE allocated memory. may be NULL and
* memory will be automatically allocated.
* fDeleteOnRelease -- controls if the memory is freed on the last release.
* 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:
* SCODE - S_OK if successful
*/
STDAPI_(LPSTORAGE) OleStdCreateStorageOnHGlobal(
HANDLE hGlobal,
BOOL fDeleteOnRelease,
DWORD grfMode
)
{
DWORD grfCreateMode=grfMode | (hGlobal==NULL ? STGM_CREATE:STGM_CONVERT);
HRESULT hrErr;
LPLOCKBYTES lpLockBytes = NULL;
DWORD reserved = 0;
LPSTORAGE lpStg = NULL;

hrErr = CreateILockBytesOnHGlobal(
hGlobal,
fDeleteOnRelease,
(LPLOCKBYTES FAR*)&lpLockBytes
);
if (hrErr != NOERROR)
return NULL;

hrErr = StgCreateDocfileOnILockBytes(
lpLockBytes,
grfCreateMode,
reserved,
(LPSTORAGE FAR*)&lpStg
);
if (hrErr != NOERROR) {
OleStdRelease((LPUNKNOWN)lpLockBytes);
return NULL;
}
OleStdRelease((LPUNKNOWN)lpLockBytes);
return lpStg;
}



/*
* OleStdCreateTempStorage()
*
* Purpose:
* Create a temporay IStorage* that will DeleteOnRelease.
* this can be either memory based or file based.
*
* Parameters:
* fUseMemory -- controls if memory-based or file-based stg is created
* grfMode -- storage mode flags
*
* Return Value:
* LPSTORAGE - if successful, NULL otherwise
*/
STDAPI_(LPSTORAGE) OleStdCreateTempStorage(BOOL fUseMemory, DWORD grfMode)
{
LPSTORAGE lpstg;
HRESULT hrErr;
DWORD reserved = 0;

if (fUseMemory) {
lpstg = OleStdCreateStorageOnHGlobal(
NULL, /* auto allocate */
TRUE, /* delete on release */

grfMode 
);
} else {
/* allocate a temp docfile that will delete on last release */
hrErr = StgCreateDocfile(
NULL,
grfMode | STGM_DELETEONRELEASE | STGM_CREATE,
reserved,
&lpstg
);
if (hrErr != NOERROR)
return NULL;
}
return lpstg;
}


/* OleStdGetOleObjectData
** ----------------------
** Render CF_EMBEDSOURCE/CF_EMBEDDEDOBJECT data on an TYMED_ISTORAGE
** medium by asking the object to save into the storage.
** the object must support IPersistStorage.
**
** if lpMedium->tymed == TYMED_NULL, then a delete-on-release
** storage is allocated (either file-based or memory-base depending
** the value of fUseMemory). this is useful to support an
** IDataObject::GetData call where the callee must allocate the
** medium.
**
** if lpMedium->tymed == TYMED_ISTORAGE, then the data is writen
** into the passed in IStorage. this is useful to support an
** IDataObject::GetDataHere call where the caller has allocated his
** own IStorage.
*/
STDAPI OleStdGetOleObjectData(
LPPERSISTSTORAGE lpPStg,
LPFORMATETC lpformatetc,
LPSTGMEDIUM lpMedium,
BOOL fUseMemory
)
{
LPSTORAGE lpstg = NULL;
DWORD reserved = 0;
SCODE sc = S_OK;
HRESULT hrErr;

lpMedium->pUnkForRelease = NULL;

if (lpMedium->tymed == TYMED_NULL) {

if (lpformatetc->tymed & TYMED_ISTORAGE) {

/* allocate a temp docfile that will delete on last release */
lpstg = OleStdCreateTempStorage(
TRUE /*fUseMemory*/,
STGM_READWRITE | STGM_TRANSACTED | STGM_SHARE_EXCLUSIVE
);
if (!lpstg)
return E_OUTOFMEMORY;

lpMedium->u.pstg = lpstg;
lpMedium->tymed = TYMED_ISTORAGE;
lpMedium->pUnkForRelease = NULL;
} else {
return DATA_E_FORMATETC;
}
} else if (lpMedium->tymed == TYMED_ISTORAGE) {
lpMedium->tymed = TYMED_ISTORAGE;
} else {
return DATA_E_FORMATETC;
}

// NOTE: even if OleSave returns an error you should still call
// SaveCompleted.

OLEDBG_BEGIN2("OleSave called\r\n")
hrErr = OleSave(lpPStg, lpMedium->u.pstg, FALSE /* fSameAsLoad */);
OLEDBG_END2

if (hrErr != NOERROR) {
OleDbgOutHResult("WARNING: OleSave returned", hrErr);
sc = hrErr;
}
OLEDBG_BEGIN2("IPersistStorage::SaveCompleted called\r\n")
hrErr = lpPStg->lpVtbl->SaveCompleted(lpPStg, NULL);
OLEDBG_END2

if (hrErr != NOERROR) {
OleDbgOutHResult("WARNING: SaveCompleted returned",hrErr);
if (sc == S_OK)
sc = hrErr;
}

return sc;
}


STDAPI OleStdGetLinkSourceData(
LPMONIKER lpmk,
LPCLSID lpClsID,
LPFORMATETC lpformatetc,
LPSTGMEDIUM lpMedium
)
{
LPSTREAM lpstm = NULL;
DWORD reserved = 0;
HRESULT hrErr;

if (lpMedium->tymed == TYMED_NULL) {
if (lpformatetc->tymed & TYMED_ISTREAM) {
hrErr = CreateStreamOnHGlobal(
NULL, /* auto allocate */
TRUE, /* delete on release */
(LPSTREAM FAR*)&lpstm
);
if (hrErr != NOERROR) {
lpMedium->pUnkForRelease = NULL;
return E_OUTOFMEMORY;
}
lpMedium->u.pstm = lpstm;
lpMedium->tymed = TYMED_ISTREAM;
lpMedium->pUnkForRelease = NULL;
} else {
lpMedium->pUnkForRelease = NULL;
return DATA_E_FORMATETC;
}
} else {
if (lpMedium->tymed == TYMED_ISTREAM) {
lpMedium->tymed = TYMED_ISTREAM;
lpMedium->u.pstm = lpMedium->u.pstm;
lpMedium->pUnkForRelease = NULL;
} else {
lpMedium->pUnkForRelease = NULL;
return DATA_E_FORMATETC;
}
}

hrErr = OleSaveToStream((LPPERSISTSTREAM)lpmk, lpMedium->u.pstm);
if (hrErr != NOERROR) return hrErr;
return WriteClassStm(lpMedium->u.pstm, lpClsID);
}

/*
* OleStdGetObjectDescriptorData
*
* Purpose:
* Fills and returns a OBJECTDESCRIPTOR structure.
* See OBJECTDESCRIPTOR for more information.
*
* Parameters:
* clsid CLSID CLSID of object being transferred
* dwDrawAspect DWORD Display Aspect of object
* sizel SIZEL Size of object in HIMETRIC
* pointl POINTL Offset from upper-left corner of object where mouse went
* down for drag. Meaningful only when drag-drop is used.
* dwStatus DWORD OLEMISC flags
* lpszFullUserTypeName LPSTR Full User Type Name
* lpszSrcOfCopy LPSTR Source of Copy
*
* Return Value:
* HBGLOBAL Handle to OBJECTDESCRIPTOR structure.
*/
STDAPI_(HGLOBAL) OleStdGetObjectDescriptorData(
CLSID clsid,
DWORD dwDrawAspect,
SIZEL sizel,
POINTL pointl,
DWORD dwStatus,
LPOLESTR lpszFullUserTypeName,
LPOLESTR lpszSrcOfCopy
)
{
HGLOBAL hMem = NULL;
IBindCtx FAR *pbc = NULL;
LPOBJECTDESCRIPTOR lpOD;
DWORD dwObjectDescSize, dwFullUserTypeNameLen, dwSrcOfCopyLen;
DWORD dwODSize ;

// Get the length of Full User Type Name; Add 1 for the null terminator
dwFullUserTypeNameLen = lpszFullUserTypeName ?
OLESTRLEN(lpszFullUserTypeName)+1 : 0;

// Get the Source of Copy string and it's length; Add 1 for the null terminator
if (lpszSrcOfCopy)
dwSrcOfCopyLen = OLESTRLEN(lpszSrcOfCopy)+1;
else {
// No src moniker so use user type name as source string.
lpszSrcOfCopy = lpszFullUserTypeName;
dwSrcOfCopyLen = dwFullUserTypeNameLen;
}

// Allocate space for OBJECTDESCRIPTOR and the additional string data
dwObjectDescSize = sizeof(OBJECTDESCRIPTOR);
dwODSize = dwObjectDescSize + (sizeof(OLECHAR) *
(dwFullUserTypeNameLen + dwSrcOfCopyLen)) ;
hMem = GlobalAlloc((GMEM_MOVEABLE | GMEM_SHARE), dwODSize) ;
if (NULL == hMem)
goto error;

lpOD = (LPOBJECTDESCRIPTOR)GlobalLock(hMem);

// Set the FullUserTypeName offset and copy the string
if (lpszFullUserTypeName)
{
lpOD->dwFullUserTypeName = dwObjectDescSize;
OLESTRCPY((LPOLESTR)((LPSTR)lpOD + lpOD->dwFullUserTypeName) ,
lpszFullUserTypeName);
}
else lpOD->dwFullUserTypeName = 0; // zero offset indicates that string is not present

// Set the SrcOfCopy offset and copy the string
if (lpszSrcOfCopy)
{
lpOD->dwSrcOfCopy = dwObjectDescSize +
(sizeof(OLECHAR) * dwFullUserTypeNameLen) ;
OLESTRCPY((LPOLESTR)((LPSTR)lpOD + lpOD->dwSrcOfCopy), lpszSrcOfCopy);
}
else lpOD->dwSrcOfCopy = 0; // zero offset indicates that string is not present

// Initialize the rest of the OBJECTDESCRIPTOR
lpOD->cbSize = dwODSize ;
lpOD->clsid = clsid;
lpOD->dwDrawAspect = dwDrawAspect;
lpOD->sizel = sizel;
lpOD->pointl = pointl;
lpOD->dwStatus = dwStatus;

GlobalUnlock(hMem);
return hMem;

error:
if (hMem)
{
GlobalUnlock(hMem);
GlobalFree(hMem);
}
return NULL;
}

/*
* OleStdGetObjectDescriptorDataFromOleObject
*
* Purpose:
* Fills and returns a OBJECTDESCRIPTOR structure. Information for the structure is
* obtained from an OLEOBJECT.
* See OBJECTDESCRIPTOR for more information.
*
* Parameters:
* lpOleObj LPOLEOBJECT OleObject from which ONJECTDESCRIPTOR info
* is obtained.
* lpszSrcOfCopy LPSTR string to identify source of copy.
* May be NULL in which case IOleObject::GetMoniker is called
* to get the moniker of the object. if the object is loaded
* as part of a data transfer document, then usually
* lpOleClientSite==NULL is passed to OleLoad when loading
* the object. in this case the IOleObject:GetMoniker call
* will always fail (it tries to call back to the object's
* client site). in this situation a non-NULL lpszSrcOfCopy
* parameter should be passed.
* dwDrawAspect DWORD Display Aspect of object
* pointl POINTL Offset from upper-left corner of object where
* mouse went down for drag. Meaningful only when drag-drop
* is used.
* lpSizelHim SIZEL (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.
* May be NULL if the object is NOT being scaled. in this
* case, IViewObject2::GetExtent will be called to get the
* extents from the object.
*
* Return Value:
* HBGLOBAL Handle to OBJECTDESCRIPTOR structure.
*/

STDAPI_(HGLOBAL) OleStdGetObjectDescriptorDataFromOleObject(
LPOLEOBJECT lpOleObj,
LPOLESTR lpszSrcOfCopy,
DWORD dwDrawAspect,
POINTL pointl,
LPSIZEL lpSizelHim
)
{
CLSID clsid;
LPOLESTR lpszFullUserTypeName = NULL;
char lpszAnsiFullUserTypeName[256];
LPMONIKER lpSrcMonikerOfCopy = NULL;
HGLOBAL hObjDesc;
IBindCtx FAR *pbc = NULL;
HRESULT hrErr;
SIZEL sizelHim;
BOOL fFreeSrcOfCopy = FALSE;
LPOLELINK lpOleLink = (LPOLELINK)
OleStdQueryInterface((LPUNKNOWN)lpOleObj,&IID_IOleLink);
LPVIEWOBJECT2 lpViewObj2 = (LPVIEWOBJECT2)
OleStdQueryInterface((LPUNKNOWN)lpOleObj, &IID_IViewObject2);
BOOL fIsLink = (lpOleLink ? TRUE : FALSE);
OLECHAR szLinkedTypeFmt[80];
char szAnsiLinkedTypeFmt[80];
LPOLESTR lpszBuf = NULL;
char lpszAnsiBuf[256];
DWORD dwStatus = 0;

// Get CLSID
OLEDBG_BEGIN2("IOleObject::GetUserClassID called\r\n")
hrErr = lpOleObj->lpVtbl->GetUserClassID(lpOleObj, &clsid);
OLEDBG_END2
if (hrErr != NOERROR)
clsid = CLSID_NULL;

// Get FullUserTypeName
OLEDBG_BEGIN2("IOleObject::GetUserType called\r\n")
hrErr = lpOleObj->lpVtbl->GetUserType(
lpOleObj,
USERCLASSTYPE_FULL,
/*(LPOLESTR FAR*)*/&lpszFullUserTypeName
);
OLEDBG_END2

/* if object is a link, then expand usertypename to be "Linked %s" */
if (fIsLink && lpszFullUserTypeName) {
if (0 == LoadString(ghInst, (UINT)IDS_OLE2UIPASTELINKEDTYPE,
/*(LPSTR)*/szAnsiLinkedTypeFmt, 80))
lstrcpy(szAnsiLinkedTypeFmt, /*(LPSTR)*/"Linked %s");
A2W (szAnsiLinkedTypeFmt, szLinkedTypeFmt, 80);
lpszBuf = OleStdMalloc(
(OLESTRLEN(lpszFullUserTypeName)+OLESTRLEN(szLinkedTypeFmt)+1)*sizeof(OLECHAR));
if (lpszBuf) {
W2A (lpszFullUserTypeName, lpszAnsiFullUserTypeName, 256);
wsprintf(lpszAnsiBuf, szAnsiLinkedTypeFmt, lpszAnsiFullUserTypeName);
OleStdFreeString(lpszFullUserTypeName, NULL);
A2W (lpszAnsiBuf, lpszBuf, 256);
lpszFullUserTypeName = lpszBuf;
}
}

/* Get Source Of Copy
** if the object is an embedding, then get the object's moniker
** if the object is a link, then get the link source moniker
*/
if (fIsLink) {

OLEDBG_BEGIN2("IOleLink::GetSourceDisplayName called\r\n")
hrErr = lpOleLink->lpVtbl->GetSourceDisplayName(
lpOleLink, &lpszSrcOfCopy );
OLEDBG_END2
fFreeSrcOfCopy = TRUE;

} else {

if (lpszSrcOfCopy == NULL) {
OLEDBG_BEGIN2("IOleObject::GetMoniker called\r\n")
hrErr = lpOleObj->lpVtbl->GetMoniker(
lpOleObj,
OLEGETMONIKER_TEMPFORUSER,
OLEWHICHMK_OBJFULL,
(LPMONIKER FAR*)&lpSrcMonikerOfCopy
);
OLEDBG_END2
if (hrErr == NOERROR)
{
CreateBindCtx(0, (LPBC FAR*)&pbc);
lpSrcMonikerOfCopy->lpVtbl->GetDisplayName(
lpSrcMonikerOfCopy, pbc, NULL, &lpszSrcOfCopy);
pbc->lpVtbl->Release(pbc);
fFreeSrcOfCopy = TRUE;
}
}
}

// Get SIZEL
if (lpSizelHim) {
// Use extents passed by the caller
sizelHim = *lpSizelHim;
} else if (lpViewObj2) {
// Get the current extents from the object
OLEDBG_BEGIN2("IViewObject2::GetExtent called\r\n")
hrErr = lpViewObj2->lpVtbl->GetExtent(
lpViewObj2,
dwDrawAspect,
-1, /*lindex*/
NULL, /*ptd*/
(LPSIZEL)&sizelHim
);
OLEDBG_END2
if (hrErr != NOERROR)
sizelHim.cx = sizelHim.cy = 0;
} else {
sizelHim.cx = sizelHim.cy = 0;
}

// Get DWSTATUS
OLEDBG_BEGIN2("IOleObject::GetMiscStatus called\r\n")
hrErr = lpOleObj->lpVtbl->GetMiscStatus(
lpOleObj,
dwDrawAspect,
&dwStatus
);
OLEDBG_END2
if (hrErr != NOERROR)
dwStatus = 0;

// Get OBJECTDESCRIPTOR
hObjDesc = OleStdGetObjectDescriptorData(
clsid,
dwDrawAspect,
sizelHim,
pointl,
dwStatus,
lpszFullUserTypeName,
lpszSrcOfCopy
);
if (! hObjDesc)
goto error;

// Clean up
if (lpszFullUserTypeName)
OleStdFreeString(lpszFullUserTypeName, NULL);
if (fFreeSrcOfCopy && lpszSrcOfCopy)
OleStdFreeString(lpszSrcOfCopy, NULL);
if (lpSrcMonikerOfCopy)
OleStdRelease((LPUNKNOWN)lpSrcMonikerOfCopy);
if (lpOleLink)
OleStdRelease((LPUNKNOWN)lpOleLink);
if (lpViewObj2)
OleStdRelease((LPUNKNOWN)lpViewObj2);

return hObjDesc;

error:
if (lpszFullUserTypeName)
OleStdFreeString(lpszFullUserTypeName, NULL);
if (fFreeSrcOfCopy && lpszSrcOfCopy)
OleStdFreeString(lpszSrcOfCopy, NULL);
if (lpSrcMonikerOfCopy)
OleStdRelease((LPUNKNOWN)lpSrcMonikerOfCopy);
if (lpOleLink)
OleStdRelease((LPUNKNOWN)lpOleLink);
if (lpViewObj2)
OleStdRelease((LPUNKNOWN)lpViewObj2);

return NULL;
}

/*
* OleStdFillObjectDescriptorFromData
*
* Purpose:
* Fills and returns a OBJECTDESCRIPTOR structure. The source object will
* offer CF_OBJECTDESCRIPTOR if it is an OLE2 object, CF_OWNERLINK if it
* is an OLE1 object, or CF_FILENAME if it has been copied to the clipboard
* by FileManager.
*
* Parameters:
* lpDataObject LPDATAOBJECT Source object
* lpmedium LPSTGMEDIUM Storage medium
* lpcfFmt CLIPFORMAT FAR * Format offered by lpDataObject
* (OUT parameter)
*
* Return Value:
* HBGLOBAL Handle to OBJECTDESCRIPTOR structure.
*/

STDAPI_(HGLOBAL) OleStdFillObjectDescriptorFromData(
LPDATAOBJECT lpDataObject,
LPSTGMEDIUM lpmedium,
CLIPFORMAT FAR* lpcfFmt
)
{
CLSID clsid;
SIZEL sizelHim;
POINTL pointl;
LPOLESTR lpsz, szFullUserTypeName, szSrcOfCopy, szClassName, szDocName, szItemName;
int nClassName, nDocName, nItemName, nFullUserTypeName;
LPOLESTR szBuf = NULL;
HGLOBAL hMem = NULL;
HKEY hKey = NULL;
LPMALLOC pIMalloc = NULL;
DWORD dw = OLEUI_CCHKEYMAX;
HGLOBAL hObjDesc;
HRESULT hrErr;
char szAnsiString[256];


// GetData CF_OBJECTDESCRIPTOR format from the object on the clipboard.
// Only OLE 2 objects on the clipboard will offer CF_OBJECTDESCRIPTOR
if (hMem = OleStdGetData(
lpDataObject,
(CLIPFORMAT)cfObjectDescriptor,
NULL,
DVASPECT_CONTENT,
lpmedium))
{
*lpcfFmt = cfObjectDescriptor;
return hMem; // Don't drop to clean up at the end of this function
}
// If CF_OBJECTDESCRIPTOR is not available, i.e. if this is not an OLE2 object,
// check if this is an OLE 1 object. OLE 1 objects will offer CF_OWNERLINK
else if (hMem = OleStdGetData(
lpDataObject,
(CLIPFORMAT)cfOwnerLink,
NULL,
DVASPECT_CONTENT,
lpmedium))
{
*lpcfFmt = cfOwnerLink;
// CF_OWNERLINK contains null-terminated strings for class name, document name
// and item name with two null terminating characters at the end
szClassName = /*(LPOLESTR)*/GlobalLock(hMem);
nClassName = OLESTRLEN(szClassName);
szDocName = szClassName + nClassName + 1;
nDocName = OLESTRLEN(szDocName);
szItemName = szDocName + nDocName + 1;
nItemName = OLESTRLEN(szItemName);

hrErr = CoGetMalloc(MEMCTX_TASK, &pIMalloc);
if (hrErr != NOERROR)
goto error;

// Find FullUserTypeName from Registration database using class name
if (RegOpenKey(HKEY_CLASSES_ROOT, NULL, &hKey) != ERROR_SUCCESS)
goto error;

// Allocate space for szFullUserTypeName & szSrcOfCopy. Maximum length of FullUserTypeName
// is OLEUI_CCHKEYMAX. SrcOfCopy is constructed by concatenating FullUserTypeName, Document
// Name and ItemName separated by spaces.
szBuf = /*(LPOLESTR)*/pIMalloc->lpVtbl->Alloc(pIMalloc,
(DWORD)2*OLEUI_CCHKEYMAX+nDocName+nItemName+4);
if (NULL == szBuf)
goto error;
szFullUserTypeName = szBuf;
szSrcOfCopy = szFullUserTypeName+OLEUI_CCHKEYMAX+1;

// Get FullUserTypeName
if (RegQueryValue(hKey, NULL, szAnsiString, &dw) != ERROR_SUCCESS)
goto error;
A2W (szAnsiString, szFullUserTypeName, OLEUI_CCHKEYMAX);

// Build up SrcOfCopy string from FullUserTypeName, DocumentName & ItemName
lpsz = szSrcOfCopy;
OLESTRCPY(lpsz, szFullUserTypeName);
nFullUserTypeName = OLESTRLEN(szFullUserTypeName);
lpsz[nFullUserTypeName]=' ';
lpsz += nFullUserTypeName+1;
OLESTRCPY(lpsz, szDocName);
lpsz[nDocName] = ' ';
lpsz += nDocName+1;
OLESTRCPY(lpsz, szItemName);

sizelHim.cx = sizelHim.cy = 0;
pointl.x = pointl.y = 0;

CLSIDFromProgID(szClassName, &clsid);

hObjDesc = OleStdGetObjectDescriptorData(
clsid,
DVASPECT_CONTENT,
sizelHim,
pointl,
0,
szFullUserTypeName,
szSrcOfCopy
);
if (!hObjDesc)
goto error;
}
// Check if object is CF_FILENAME
else if (hMem = OleStdGetData(
lpDataObject,
(CLIPFORMAT)cfFileName,
NULL,
DVASPECT_CONTENT,
lpmedium))
{
*lpcfFmt = cfFileName;
lpsz = /*(LPOLESTR)*/GlobalLock(hMem);
hrErr = GetClassFile(lpsz, &clsid);

/* NOTE: if the file does not have an OLE class
** associated, then use the OLE 1 Packager as the class of
** the object to be created. this is the behavior of
** OleCreateFromData API
*/
if (hrErr != NOERROR)
CLSIDFromProgID(OLESTR("Package"), &clsid);
sizelHim.cx = sizelHim.cy = 0;
pointl.x = pointl.y = 0;

hrErr = CoGetMalloc(MEMCTX_TASK, &pIMalloc);
if (hrErr != NOERROR)
goto error;
szBuf = /*(LPOLESTR)*/pIMalloc->lpVtbl->Alloc(pIMalloc, (DWORD)OLEUI_CCHKEYMAX);
if (NULL == szBuf)
goto error;

OleStdGetUserTypeOfClass(&clsid, szBuf, OLEUI_CCHKEYMAX, NULL);

hObjDesc = OleStdGetObjectDescriptorData(
clsid,
DVASPECT_CONTENT,
sizelHim,
pointl,
0,
szBuf,
lpsz
);
if (!hObjDesc)
goto error;
}
else goto error;

// Clean up
if (szBuf)
pIMalloc->lpVtbl->Free(pIMalloc, (LPVOID)szBuf);
if (pIMalloc)
pIMalloc->lpVtbl->Release(pIMalloc);
if (hMem)
{
GlobalUnlock(hMem);
GlobalFree(hMem);
}
if (hKey)
RegCloseKey(hKey);
return hObjDesc;

error:
if (szBuf)
pIMalloc->lpVtbl->Free(pIMalloc, (LPVOID)szBuf);
if (pIMalloc)
pIMalloc->lpVtbl->Release(pIMalloc);
if (hMem)
{
GlobalUnlock(hMem);
GlobalFree(hMem);
}
if (hKey)
RegCloseKey(hKey);
return NULL;
}


#if defined( OBSOLETE )

/*************************************************************************
** The following API's have been converted into macros:
** OleStdQueryOleObjectData
** OleStdQueryLinkSourceData
** OleStdQueryObjectDescriptorData
** OleStdQueryFormatMedium
** OleStdCopyMetafilePict
** OleStdGetDropEffect
**
** These macros are defined in olestd.h
*************************************************************************/

STDAPI OleStdQueryOleObjectData(LPFORMATETC lpformatetc)
{
if (lpformatetc->tymed & TYMED_ISTORAGE) {
return NOERROR;
} else {
return DATA_E_FORMATETC;
}
}


STDAPI OleStdQueryLinkSourceData(LPFORMATETC lpformatetc)
{
if (lpformatetc->tymed & TYMED_ISTREAM) {
return NOERROR;
} else {
return DATA_E_FORMATETC;
}
}


STDAPI OleStdQueryObjectDescriptorData(LPFORMATETC lpformatetc)
{
if (lpformatetc->tymed & TYMED_HGLOBAL) {
return NOERROR;
} else {
return DATA_E_FORMATETC;
}
}


STDAPI OleStdQueryFormatMedium(LPFORMATETC lpformatetc, TYMED tymed)
{
if (lpformatetc->tymed & tymed) {
return NOERROR;
} else {
return DATA_E_FORMATETC;
}
}


/*
* OleStdCopyMetafilePict()
*
* Purpose:
* Make an independent copy of a MetafilePict
* Parameters:
*
* Return Value:
* TRUE if successful, else FALSE.
*/
STDAPI_(BOOL) OleStdCopyMetafilePict(HANDLE hpictin, HANDLE FAR* phpictout)
{
HANDLE hpictout;
LPMETAFILEPICT ppictin, ppictout;

if (hpictin == NULL || phpictout == NULL) {
OleDbgAssert(hpictin == NULL || phpictout == NULL);
return FALSE;
}

*phpictout = NULL;

if ((ppictin = (LPMETAFILEPICT)GlobalLock(hpictin)) == NULL) {
return FALSE;
}

hpictout = GlobalAlloc(GHND|GMEM_SHARE, sizeof(METAFILEPICT));

if (hpictout && (ppictout = (LPMETAFILEPICT)GlobalLock(hpictout))){
ppictout->hMF = CopyMetaFile(ppictin->hMF, NULL);
ppictout->xExt = ppictin->xExt;
ppictout->yExt = ppictin->yExt;
ppictout->mm = ppictin->mm;
GlobalUnlock(hpictout);
}

*phpictout = hpictout;

return TRUE;

}


/* OleStdGetDropEffect
** -------------------
**
** Convert a keyboard state into a DROPEFFECT.
**
** returns the DROPEFFECT value derived from the key state.
** the following is the standard interpretation:
** no modifier -- Default Drop (NULL is returned)
** CTRL -- DROPEFFECT_COPY
** SHIFT -- DROPEFFECT_MOVE
** CTRL-SHIFT -- DROPEFFECT_LINK
**
** Default Drop: this depends on the type of the target application.
** this is re-interpretable by each target application. a typical
** interpretation is if the drag is local to the same document
** (which is source of the drag) then a MOVE operation is
** performed. if the drag is not local, then a COPY operation is
** performed.
*/
STDAPI_(DWORD) OleStdGetDropEffect( DWORD grfKeyState )
{

if (grfKeyState & MK_CONTROL) {

if (grfKeyState & MK_SHIFT)
return DROPEFFECT_LINK;
else
return DROPEFFECT_COPY;

} else if (grfKeyState & MK_SHIFT)
return DROPEFFECT_MOVE;

return 0; // no modifier -- do default operation
}
#endif // OBSOLETE


/*
* OleStdGetMetafilePictFromOleObject()
*
* Purpose:
* Generate a MetafilePict by drawing the OLE object.
* Parameters:
* lpOleObj LPOLEOBJECT pointer to OLE Object
* dwDrawAspect DWORD Display Aspect of object
* lpSizelHim SIZEL (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.
* May be NULL if the object is NOT being scaled. in this
* case, IViewObject2::GetExtent will be called to get the
* extents from the object.
* ptd TARGETDEVICE FAR* (optional) target device to render
* metafile for. May be NULL.
*
* Return Value:
* HANDLE -- handle of allocated METAFILEPICT
*/
STDAPI_(HANDLE) OleStdGetMetafilePictFromOleObject(
LPOLEOBJECT lpOleObj,
DWORD dwDrawAspect,
LPSIZEL lpSizelHim,
DVTARGETDEVICE FAR* ptd
)
{
LPVIEWOBJECT2 lpViewObj2 = NULL;
HDC hDC;
HMETAFILE hmf;
HANDLE hMetaPict;
LPMETAFILEPICT lpPict;
RECT rcHim;
RECTL rclHim;
SIZEL sizelHim;
HRESULT hrErr;
SIZE size;
POINT point;

lpViewObj2 = (LPVIEWOBJECT2)OleStdQueryInterface(
(LPUNKNOWN)lpOleObj, &IID_IViewObject2);
if (! lpViewObj2)
return NULL;

// Get SIZEL
if (lpSizelHim) {
// Use extents passed by the caller
sizelHim = *lpSizelHim;
} else {
// Get the current extents from the object
OLEDBG_BEGIN2("IViewObject2::GetExtent called\r\n")
hrErr = lpViewObj2->lpVtbl->GetExtent(
lpViewObj2,
dwDrawAspect,
-1, /*lindex*/
ptd, /*ptd*/
(LPSIZEL)&sizelHim
);
OLEDBG_END2
if (hrErr != NOERROR)
sizelHim.cx = sizelHim.cy = 0;
}

hDC = CreateMetaFile(NULL);

rclHim.left = 0;
rclHim.top = 0;
rclHim.right = sizelHim.cx;
rclHim.bottom = sizelHim.cy;

rcHim.left = (int)rclHim.left;
rcHim.top = (int)rclHim.top;
rcHim.right = (int)rclHim.right;
rcHim.bottom = (int)rclHim.bottom;

SetWindowOrgEx(hDC, rcHim.left, rcHim.top, &point);
SetWindowExtEx(hDC, rcHim.right-rcHim.left, rcHim.bottom-rcHim.top,&size);

OLEDBG_BEGIN2("IViewObject::Draw called\r\n")
hrErr = lpViewObj2->lpVtbl->Draw(
lpViewObj2,
dwDrawAspect,
-1,
NULL,
ptd,
NULL,
hDC,
(LPRECTL)&rclHim,
(LPRECTL)&rclHim,
NULL,
0
);
OLEDBG_END2

OleStdRelease((LPUNKNOWN)lpViewObj2);
if (hrErr != NOERROR) {
OleDbgOutHResult("IViewObject::Draw returned", hrErr);
CloseMetaFile(hDC);
return(NULL);
}

hmf = CloseMetaFile(hDC);

hMetaPict = GlobalAlloc(GHND|GMEM_SHARE, sizeof(METAFILEPICT));

if (hMetaPict && (lpPict = (LPMETAFILEPICT)GlobalLock(hMetaPict))){
lpPict->hMF = hmf;
lpPict->xExt = (int)sizelHim.cx ;
lpPict->yExt = (int)sizelHim.cy ;
lpPict->mm = MM_ANISOTROPIC;
GlobalUnlock(hMetaPict);
}

return hMetaPict;
}


/* Call Release on the object that is expected to go away.
** if the refcnt of the object did no go to 0 then give a debug message.
*/
STDAPI_(ULONG) OleStdVerifyRelease(LPUNKNOWN lpUnk, LPOLESTR lpszMsg)
{
ULONG cRef;
char lpszAnsiMsg[256];

W2A (lpszMsg, lpszAnsiMsg, 256);
cRef = lpUnk->lpVtbl->Release(lpUnk);

#if defined( _DEBUG )
if (cRef != 0) {
char szBuf[80];
if (lpszMsg)
MessageBox(NULL, lpszAnsiMsg, NULL, MB_ICONEXCLAMATION | MB_OK);
wsprintf(
/*(LPSTR)*/szBuf,
"refcnt (%ld) != 0 after object (0x%lx) release\n",
cRef,
lpUnk
);
if (lpszMsg)
OleDbgOut1(lpszAnsiMsg);
OleDbgOut1(/*(LPSTR)*/szBuf);
OleDbgAssertSz(cRef == 0, /*(LPSTR)*/szBuf);
} else {
char szBuf[80];
wsprintf(
/*(LPSTR)*/szBuf,
"refcnt = 0 after object (0x%lx) release\n", lpUnk
);
OleDbgOut4(/*(LPSTR)*/szBuf);
}
#endif
return cRef;
}


/* Call Release on the object that is NOT necessarily expected to go away.
*/
STDAPI_(ULONG) OleStdRelease(LPUNKNOWN lpUnk)
{
ULONG cRef;

cRef = lpUnk->lpVtbl->Release(lpUnk);

#if defined( _DEBUG )
{
char szBuf[80];
wsprintf(
/*(LPSTR)*/szBuf,
"refcnt = %ld after object (0x%lx) release\n",

cRef, 
lpUnk
);
OleDbgOut4(/*(LPSTR)*/szBuf);
}
#endif
return cRef;
}


/* OleStdInitVtbl
** --------------
**
** Initialize an interface Vtbl to ensure that there are no NULL
** function pointers in the Vtbl. All entries in the Vtbl are
** set to a valid funtion pointer (OleStdNullMethod) that issues
** debug assert message (message box) and returns E_NOTIMPL if called.
**
** NOTE: this funtion does not initialize the Vtbl with usefull
** function pointers, only valid function pointers to avoid the
** horrible run-time crash when a call is made through the Vtbl with
** a NULL function pointer. this API is only necessary when
** initializing the Vtbl's in C. C++ guarantees that all interface
** functions (in C++ terms -- pure virtual functions) are implemented.
*/

STDAPI_(void) OleStdInitVtbl(LPVOID lpVtbl, UINT nSizeOfVtbl)
{
LPVOID FAR* lpFuncPtrArr = (LPVOID FAR*)lpVtbl;
UINT nMethods = nSizeOfVtbl/sizeof(VOID FAR*);
UINT i;

for (i = 0; i < nMethods; i++) {
lpFuncPtrArr[i] = OleStdNullMethod;
}
}


/* OleStdCheckVtbl
** ---------------
**
** Check if all entries in the Vtbl are properly initialized with
** valid function pointers. If any entries are either NULL or
** OleStdNullMethod, then this function returns FALSE. If compiled
** for _DEBUG this function reports which function pointers are
** invalid.
**
** RETURNS: TRUE if all entries in Vtbl are valid
** FALSE otherwise.
*/

STDAPI_(BOOL) OleStdCheckVtbl(LPVOID lpVtbl, UINT nSizeOfVtbl, LPOLESTR lpszIface)
{
LPVOID FAR* lpFuncPtrArr = (LPVOID FAR*)lpVtbl;
UINT nMethods = nSizeOfVtbl/sizeof(VOID FAR*);
UINT i;
BOOL fStatus = TRUE;
int nChar = 0;

for (i = 0; i < nMethods; i++) {
if (lpFuncPtrArr[i] == NULL || lpFuncPtrArr[i] == OleStdNullMethod) {
#if defined( _DEBUG )
char szAnsiStr[256];
char szBuf[256];
W2A (lpszIface, szAnsiStr, 256);
wsprintf(szBuf, "%s::method# %d NOT valid!", szAnsiStr, i);
OleDbgOut1(/*(LPSTR)*/szBuf);
#endif
fStatus = FALSE;
}
}
return fStatus;
}


/* OleStdNullMethod
** ----------------
** Dummy method used by OleStdInitVtbl to initialize an interface
** Vtbl to ensure that there are no NULL function pointers in the
** Vtbl. All entries in the Vtbl are set to this function. this
** function issues a debug assert message (message box) and returns
** E_NOTIMPL if called. If all is done properly, this function will
** NEVER be called!
*/
STDMETHODIMP OleStdNullMethod(LPUNKNOWN lpThis)
{
MessageBox(
NULL,
"ERROR: INTERFACE METHOD NOT IMPLEMENTED!\r\n",
NULL,
MB_SYSTEMMODAL | MB_ICONHAND | MB_OK
);

return E_NOTIMPL;
}



static BOOL GetFileTimes(LPSTR lpszFileName, FILETIME FAR* pfiletime)
{
#ifdef WIN32
WIN32_FIND_DATA fd;
HANDLE hFind;
hFind = FindFirstFile(lpszFileName,&fd);
if (hFind == NULL || hFind == INVALID_HANDLE_VALUE) {
return FALSE;
}
FindClose(hFind);
*pfiletime = fd.ftLastWriteTime;
return TRUE;
#else
static char sz[256];
static struct _find_t fileinfo;

LSTRCPYN(/*(LPSTR)*/sz, lpszFileName, 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,pfiletime));
#endif
}



/* OleStdRegisterAsRunning
** -----------------------
** Register a moniker in the RunningObjectTable.
** if there is an existing registration (*lpdwRegister!=NULL), then
** first revoke that registration.
**
** new dwRegister key is returned via *lpdwRegister parameter.
*/
STDAPI_(void) OleStdRegisterAsRunning(LPUNKNOWN lpUnk, LPMONIKER lpmkFull, DWORD FAR* lpdwRegister)
{
LPRUNNINGOBJECTTABLE lpROT;
HRESULT hrErr;
DWORD dwOldRegister = *lpdwRegister;

OLEDBG_BEGIN2("OleStdRegisterAsRunning\r\n")

OLEDBG_BEGIN2("GetRunningObjectTable called\r\n")
hrErr = GetRunningObjectTable(0,(LPRUNNINGOBJECTTABLE FAR*)&lpROT);
OLEDBG_END2

if (hrErr == NOERROR) {

/* register as running if a valid moniker is passed
**
** NOTE: we deliberately register the new moniker BEFORE
** revoking the old moniker just in case the object
** currently has no external locks. if the object has no
** locks then revoking it from the running object table will
** cause the object's StubManager to initiate shutdown of
** the object.
*/
if (lpmkFull) {

OLEDBG_BEGIN2("IRunningObjectTable::Register called\r\n")
lpROT->lpVtbl->Register(lpROT, 0, lpUnk,lpmkFull,lpdwRegister);
OLEDBG_END2

#if defined(_DEBUG)
{
char szBuf[512];
LPOLESTR lpszDisplay;
char lpszAnsiDisplay[256];
LPBC lpbc;

CreateBindCtx(0, (LPBC FAR*)&lpbc);
lpmkFull->lpVtbl->GetDisplayName(
lpmkFull,
lpbc,
NULL,
/*(LPOLESTR FAR*)*/&lpszDisplay
);
OleStdRelease((LPUNKNOWN)lpbc);
W2A (lpszDisplay, lpszAnsiDisplay, 256);
wsprintf(
szBuf,
"Moniker '%s' REGISTERED as [0x%lx] in ROT\r\n",
lpszAnsiDisplay,
*lpdwRegister
);
OleDbgOut2(szBuf);
OleStdFreeString(lpszDisplay, NULL);
}
#endif // _DEBUG

}

// if already registered, revoke
if (dwOldRegister != 0) {

#if defined(_DEBUG)
{
char szBuf[512];

wsprintf(
szBuf,
"Moniker [0x%lx] REVOKED from ROT\r\n",
dwOldRegister
);
OleDbgOut2(szBuf);
}
#endif // _DEBUG

OLEDBG_BEGIN2("IRunningObjectTable::Revoke called\r\n")
lpROT->lpVtbl->Revoke(lpROT, dwOldRegister);
OLEDBG_END2
}

OleStdRelease((LPUNKNOWN)lpROT);
} else {
OleDbgAssertSz(
lpROT != NULL,
"OleStdRegisterAsRunning: GetRunningObjectTable FAILED\r\n"
);
}

OLEDBG_END2
}



/* OleStdRevokeAsRunning
** ---------------------
** Revoke a moniker from the RunningObjectTable if there is an
** existing registration (*lpdwRegister!=NULL).
**
** *lpdwRegister parameter will be set to NULL.
*/
STDAPI_(void) OleStdRevokeAsRunning(DWORD FAR* lpdwRegister)
{
LPRUNNINGOBJECTTABLE lpROT;
HRESULT hrErr;

OLEDBG_BEGIN2("OleStdRevokeAsRunning\r\n")

// if still registered, then revoke
if (*lpdwRegister != 0) {

OLEDBG_BEGIN2("GetRunningObjectTable called\r\n")
hrErr = GetRunningObjectTable(0,(LPRUNNINGOBJECTTABLE FAR*)&lpROT);
OLEDBG_END2

if (hrErr == NOERROR) {

#if defined(_DEBUG)
{
char szBuf[512];

wsprintf(
szBuf,
"Moniker [0x%lx] REVOKED from ROT\r\n",
*lpdwRegister
);
OleDbgOut2(szBuf);
}
#endif // _DEBUG

OLEDBG_BEGIN2("IRunningObjectTable::Revoke called\r\n")
lpROT->lpVtbl->Revoke(lpROT, *lpdwRegister);
OLEDBG_END2

*lpdwRegister = 0;

OleStdRelease((LPUNKNOWN)lpROT);
} else {
OleDbgAssertSz(
lpROT != NULL,
"OleStdRevokeAsRunning: GetRunningObjectTable FAILED\r\n"
);
}
}
OLEDBG_END2
}


/* OleStdNoteFileChangeTime
** ------------------------
** Note the time a File-Based object has been saved in the
** RunningObjectTable. These change times are used as the basis for
** IOleObject::IsUpToDate.
** It is important to set the time of the file-based object
** following a save operation to exactly the time of the saved file.
** this helps IOleObject::IsUpToDate to give the correct answer
** after a file has been saved.
*/
STDAPI_(void) OleStdNoteFileChangeTime(LPOLESTR lpszFileName, DWORD dwRegister)
{
char lpszAnsiFileName[256];

W2A (lpszFileName, lpszAnsiFileName, 256);
if (dwRegister != 0) {

LPRUNNINGOBJECTTABLE lprot;
FILETIME filetime;

if (GetFileTimes(lpszAnsiFileName, &filetime) &&
GetRunningObjectTable(0,&lprot) == NOERROR)
{
lprot->lpVtbl->NoteChangeTime( lprot, dwRegister, &filetime );
lprot->lpVtbl->Release(lprot);

OleDbgOut2("IRunningObjectTable::NoteChangeTime called\r\n");
}
}
}


/* OleStdNoteObjectChangeTime
** --------------------------
** Set the last change time of an object that is registered in the
** RunningObjectTable. These change times are used as the basis for
** IOleObject::IsUpToDate.
**
** every time the object sends out a OnDataChange notification, it
** should update the Time of last change in the ROT.
**
** NOTE: this function set the change time to the current time.
*/
STDAPI_(void) OleStdNoteObjectChangeTime(DWORD dwRegister)
{
if (dwRegister != 0) {

LPRUNNINGOBJECTTABLE lprot;
FILETIME filetime;

if (GetRunningObjectTable(0,&lprot) == NOERROR)
{
CoFileTimeNow( &filetime );
lprot->lpVtbl->NoteChangeTime( lprot, dwRegister, &filetime );
lprot->lpVtbl->Release(lprot);

OleDbgOut2("IRunningObjectTable::NoteChangeTime called\r\n");
}
}
}


/* OleStdCreateTempFileMoniker
** ---------------------------
** return the next available FileMoniker that can be used as the
** name of an untitled document.
** the FileMoniker is built of the form:
** <lpszPrefixString><number>
** eg. "Outline1", "Outline2", etc.
**
** The RunningObjectTable (ROT) is consulted to determine if a
** FileMoniker is in use. If the name is in use then the number is
** incremented and the ROT is checked again.
**
** Parameters:
** LPSTR lpszPrefixString - prefix used to build the name
** UINT FAR* lpuUnique - (IN-OUT) last used number.
** this number is used to make the
** name unique. on entry, the input
** number is incremented. on output,
** the number used is returned. this
** number should be passed again
** unchanged on the next call.
** LPSTR lpszName - (OUT) buffer used to build string.
** caller must be sure buffer is large
** enough to hold the generated string.
** LPMONIKER FAR* lplpmk - (OUT) next unused FileMoniker
**
** Returns:
** void
**
** Comments:
** This function is similar in spirit to the Windows API
** CreateTempFileName.
*/
STDAPI_(void) OleStdCreateTempFileMoniker(
LPOLESTR lpszPrefixString,
UINT FAR* lpuUnique,
LPOLESTR lpszName,
LPMONIKER FAR* lplpmk
)
{
LPRUNNINGOBJECTTABLE lpROT = NULL;
UINT i = (lpuUnique != NULL ? *lpuUnique : 1);
HRESULT hrErr;
char lpszAnsiPrefixString[256], lpszAnsiName[256];

W2A (lpszPrefixString, lpszAnsiPrefixString, 256);
wsprintf(lpszAnsiName, "%s%d", lpszAnsiPrefixString, i++);
A2W (lpszAnsiName, lpszName, OLEUI_CCHLABELMAX);
CreateFileMoniker(lpszName, lplpmk);

OLEDBG_BEGIN2("GetRunningObjectTable called\r\n")
hrErr = GetRunningObjectTable(0,(LPRUNNINGOBJECTTABLE FAR*)&lpROT);
OLEDBG_END2

if (hrErr == NOERROR) {

while (1) {
if (! *lplpmk)
break; // failed to create FileMoniker

OLEDBG_BEGIN2("IRunningObjectTable::IsRunning called\r\n")
hrErr = lpROT->lpVtbl->IsRunning(lpROT,*lplpmk);
OLEDBG_END2

if (hrErr != NOERROR)
break; // the Moniker is NOT running; found unused one!

OleStdVerifyRelease(
(LPUNKNOWN)*lplpmk,
OLESTR("OleStdCreateTempFileMoniker: Moniker NOT released")
);

wsprintf(lpszAnsiName, "%s%d", lpszAnsiPrefixString, i++);
A2W (lpszAnsiName, lpszName, OLEUI_CCHLABELMAX);
CreateFileMoniker(lpszName, lplpmk);
}

OleStdRelease((LPUNKNOWN)lpROT);
}

if (lpuUnique != NULL)
*lpuUnique = i;
}


/* OleStdGetFirstMoniker
** ---------------------
** return the first piece of a moniker.
**
** NOTE: if the given moniker is not a generic composite moniker,
** then an AddRef'ed pointer to the given moniker is returned.
*/
STDAPI_(LPMONIKER) OleStdGetFirstMoniker(LPMONIKER lpmk)
{
LPMONIKER lpmkFirst = NULL;
LPENUMMONIKER lpenumMoniker;
DWORD dwMksys;
HRESULT hrErr;

if (! lpmk)
return NULL;

if (lpmk->lpVtbl->IsSystemMoniker(lpmk, (LPDWORD)&dwMksys) == NOERROR
&& dwMksys == MKSYS_GENERICCOMPOSITE) {

/* NOTE: the moniker is a GenericCompositeMoniker.
** enumerate the moniker to pull off the first piece.
*/

hrErr = lpmk->lpVtbl->Enum(
lpmk,
TRUE /* fForward */,
(LPENUMMONIKER FAR*)&lpenumMoniker
);
if (hrErr != NOERROR)
return NULL; // ERROR: give up!

hrErr = lpenumMoniker->lpVtbl->Next(
lpenumMoniker,
1,
(LPMONIKER FAR*)&lpmkFirst,
NULL
);
lpenumMoniker->lpVtbl->Release(lpenumMoniker);
return lpmkFirst;

} else {
/* NOTE: the moniker is NOT a GenericCompositeMoniker.
** return an AddRef'ed pointer to the input moniker.
*/
lpmk->lpVtbl->AddRef(lpmk);
return lpmk;
}
}


/* OleStdGetLenFilePrefixOfMoniker
** -------------------------------
** if the first piece of the Moniker is a FileMoniker, then return
** the length of the filename string.
**
** lpmk pointer to moniker
**
** Returns
** 0 if moniker does NOT start with a FileMoniker
** uLen string length of filename prefix of the display name
** retrieved from the given (lpmk) moniker.
*/
STDAPI_(ULONG) OleStdGetLenFilePrefixOfMoniker(LPMONIKER lpmk)
{
LPMONIKER lpmkFirst = NULL;
DWORD dwMksys;
LPOLESTR lpsz = NULL;
LPBC lpbc = NULL;
ULONG uLen = 0;
HRESULT hrErr;

if (! lpmk)
return 0;

lpmkFirst = OleStdGetFirstMoniker(lpmk);
if (lpmkFirst) {
if ( (lpmkFirst->lpVtbl->IsSystemMoniker(
lpmkFirst, (LPDWORD)&dwMksys) == NOERROR)
&& dwMksys == MKSYS_FILEMONIKER) {

hrErr = CreateBindCtx(0, (LPBC FAR*)&lpbc);
if (hrErr == NOERROR) {
hrErr = lpmkFirst->lpVtbl->GetDisplayName(
lpmkFirst,
lpbc,
NULL, /* pmkToLeft */
/*(LPOLESTR FAR*)*/&lpsz
);
if (hrErr == NOERROR && lpsz != NULL) {
uLen = OLESTRLEN(lpsz);
OleStdFreeString(lpsz, NULL);
}
OleStdRelease((LPUNKNOWN)lpbc);
}
}
lpmkFirst->lpVtbl->Release(lpmkFirst);
}
return uLen;
}


/* OleStdMkParseDisplayName
** Parse a string into a Moniker by calling the OLE API
** MkParseDisplayName. if the original link source class was an OLE1
** class, then attempt the parsing assuming the same class applies.
**
** if the class of the previous link source was an OLE1 class,
** then first attempt to parse a string that it is qualified
** with the progID associated with the OLE1 class. this more
** closely matches the semantics of OLE1 where the class of
** link sources is not expected to change. prefixing the
** string with "@<ProgID -- OLE1 class name>!" will force the
** parsing of the string to assume the file is of that
** class.
** NOTE: this trick of prepending the string with "@<ProgID>
** only works for OLE1 classes.
**
** PARAMETERS:
** REFCLSID rClsid -- original class of link source.
** CLSID_NULL if class is unknown
** ... other parameters the same as MkParseDisplayName API ...
**
** RETURNS
** NOERROR if string parsed successfully
** else error code returned by MkParseDisplayName
*/
STDAPI OleStdMkParseDisplayName(
REFCLSID rClsid,
LPBC lpbc,
LPOLESTR lpszUserName,
ULONG FAR* lpchEaten,
LPMONIKER FAR* lplpmk
)
{
HRESULT hrErr;

if (!IsEqualCLSID(rClsid,&CLSID_NULL) && CoIsOle1Class(rClsid) &&
lpszUserName[0] != '@') {
LPOLESTR lpszBuf;
LPOLESTR lpszProgID;
char lpszAnsiBuf[256], lpszAnsiProgID[256], lpszAnsiUserName[256];

// Prepend "@<ProgID>!" to the input string
ProgIDFromCLSID(rClsid, &lpszProgID);
if (lpszProgID == NULL)
goto Cont1;
lpszBuf = OleStdMalloc(
((ULONG)OLESTRLEN(lpszUserName)+OLESTRLEN(lpszProgID)+3) * sizeof(OLECHAR));
if (lpszBuf == NULL) {
if (lpszProgID)
OleStdFree(lpszProgID);
goto Cont1;
}

W2A (lpszProgID, lpszAnsiProgID, 256);
W2A (lpszUserName, lpszAnsiUserName, 256);
wsprintf(lpszAnsiBuf, "@%s!%s", lpszAnsiProgID, lpszAnsiUserName);
A2W (lpszAnsiBuf, lpszBuf, OLESTRLEN(lpszUserName)+OLESTRLEN(lpszProgID)+3);
OLEDBG_BEGIN2("MkParseDisplayName called\r\n")
hrErr = MkParseDisplayName(lpbc, lpszBuf, lpchEaten, lplpmk);
OLEDBG_END2

if (lpszProgID)
OleStdFree(lpszProgID);
if (lpszBuf)
OleStdFree(lpszBuf);

if (hrErr == NOERROR)
return NOERROR;
}

Cont1:
OLEDBG_BEGIN2("MkParseDisplayName called\r\n")
hrErr = MkParseDisplayName(lpbc, lpszUserName, lpchEaten, lplpmk);
OLEDBG_END2

return hrErr;
}


/*
* OleStdMarkPasteEntryList
*
* Purpose:
* Mark each entry in the PasteEntryList if its format is available from
* the source IDataObject*. the dwScratchSpace field of each PasteEntry
* is set to TRUE if available, else FALSE.
*
* Parameters:
* LPOLEUIPASTEENTRY array of PasteEntry structures
* int count of elements in PasteEntry array
* LPDATAOBJECT source IDataObject* pointer
*
* Return Value:
* none
*/
STDAPI_(void) OleStdMarkPasteEntryList(
LPDATAOBJECT lpSrcDataObj,
LPOLEUIPASTEENTRY lpPriorityList,
int cEntries
)
{
LPENUMFORMATETC lpEnumFmtEtc = NULL;
#define FORMATETC_MAX 20
FORMATETC rgfmtetc[FORMATETC_MAX];
int i;
HRESULT hrErr;
long j, cFetched;

// Clear all marks
for (i = 0; i < cEntries; i++) {
lpPriorityList[i].dwScratchSpace = FALSE;

if (! lpPriorityList[i].fmtetc.cfFormat) {
// caller wants this item always considered available
// (by specifying a NULL format)
lpPriorityList[i].dwScratchSpace = TRUE;
} else if (lpPriorityList[i].fmtetc.cfFormat == cfEmbeddedObject
|| lpPriorityList[i].fmtetc.cfFormat == cfEmbedSource
|| lpPriorityList[i].fmtetc.cfFormat == cfFileName) {

// if there is an OLE object format, then handle it
// specially by calling OleQueryCreateFromData. the caller
// need only specify one object type format.
OLEDBG_BEGIN2("OleQueryCreateFromData called\r\n")
hrErr = OleQueryCreateFromData(lpSrcDataObj);
OLEDBG_END2
if(NOERROR == hrErr) {
lpPriorityList[i].dwScratchSpace = TRUE;
}
} else if (lpPriorityList[i].fmtetc.cfFormat == cfLinkSource) {

// if there is OLE 2.0 LinkSource format, then handle it
// specially by calling OleQueryLinkFromData.
OLEDBG_BEGIN2("OleQueryLinkFromData called\r\n")
hrErr = OleQueryLinkFromData(lpSrcDataObj);
OLEDBG_END2
if(NOERROR == hrErr) {
lpPriorityList[i].dwScratchSpace = TRUE;
}
}
}

OLEDBG_BEGIN2("IDataObject::EnumFormatEtc called\r\n")
hrErr = lpSrcDataObj->lpVtbl->EnumFormatEtc(
lpSrcDataObj,
DATADIR_GET,
(LPENUMFORMATETC FAR*)&lpEnumFmtEtc
);
OLEDBG_END2

if (hrErr != NOERROR)
return; // unable to get format enumerator

// Enumerate the formats offered by the source
// Loop over all formats offered by the source
cFetched = 0;
_fmemset(rgfmtetc,0,sizeof(rgfmtetc[FORMATETC_MAX]) );
if (lpEnumFmtEtc->lpVtbl->Next(
lpEnumFmtEtc, FORMATETC_MAX, rgfmtetc, &cFetched) == NOERROR
|| (cFetched > 0 && cFetched <= FORMATETC_MAX) )
{

for (j = 0; j < cFetched; j++)
{
for (i = 0; i < cEntries; i++)
{
if (! lpPriorityList[i].dwScratchSpace &&
IsCloseFormatEtc(&rgfmtetc[j], &lpPriorityList[i].fmtetc))
{
lpPriorityList[i].dwScratchSpace = TRUE;
}
}
}
} // endif

// Clean up
if (lpEnumFmtEtc)
OleStdRelease((LPUNKNOWN)lpEnumFmtEtc);
}


/* OleStdGetPriorityClipboardFormat
** --------------------------------
**
** Retrieve the first clipboard format in a list for which data
** exists in the source IDataObject*.
**
** Returns -1 if no acceptable match is found.
** index of first acceptable match in the priority list.
**
*/
STDAPI_(int) OleStdGetPriorityClipboardFormat(
LPDATAOBJECT lpSrcDataObj,
LPOLEUIPASTEENTRY lpPriorityList,
int cEntries
)
{
int i;
int nFmtEtc = -1;

// Mark all entries that the Source provides
OleStdMarkPasteEntryList(lpSrcDataObj, lpPriorityList, cEntries);

// Loop over the target's priority list of formats
for (i = 0; i < cEntries; i++)
{
if (lpPriorityList[i].dwFlags != OLEUIPASTE_PASTEONLY &&
!(lpPriorityList[i].dwFlags & OLEUIPASTE_PASTE))
continue;

// get first marked entry
if (lpPriorityList[i].dwScratchSpace) {
nFmtEtc = i;
break; // Found priority format; DONE
}
}

return nFmtEtc;
}


/* OleStdIsDuplicateFormat
** -----------------------
** Returns TRUE if the lpFmtEtc->cfFormat is found in the array of
** FormatEtc structures.
*/
STDAPI_(BOOL) OleStdIsDuplicateFormat(
LPFORMATETC lpFmtEtc,
LPFORMATETC arrFmtEtc,
int nFmtEtc
)
{
int i;

for (i = 0; i < nFmtEtc; i++) {
if (IsEqualFORMATETC((*lpFmtEtc), arrFmtEtc[i]))
return TRUE;
}

return FALSE;
}


/* OleStdGetItemToken
* ------------------
*
* LPSTR lpszSrc - Pointer to a source string
* LPSTR lpszDst - Pointer to destination buffer
*
* Will copy one token from the lpszSrc buffer to the lpszItem buffer.
* It considers all alpha-numeric and white space characters as valid
* characters for a token. the first non-valid character delimates the
* token.
*
* returns the number of charaters eaten.
*/
STDAPI_(ULONG) OleStdGetItemToken(LPOLESTR lpszSrc, LPOLESTR lpszDst, int nMaxChars)
{
ULONG chEaten = 0L;
char szAnsiSrc[256], szAnsiDst[256];
LPSTR lpszAnsiDst = szAnsiDst;
LPSTR lpszAnsiSrc = szAnsiSrc;

W2A (lpszSrc, szAnsiSrc, 256);

// skip leading delimeter characters
while (*lpszAnsiSrc && --nMaxChars > 0
&& ((*lpszAnsiSrc=='/') || (*lpszAnsiSrc=='\\') ||
(*lpszAnsiSrc=='!') || (*lpszAnsiSrc==':'))) {
*lpszAnsiSrc++;
chEaten++;
}

// Extract token string (up to first delimeter char or EOS)
while (*lpszAnsiSrc && --nMaxChars > 0
&& !((*lpszAnsiSrc=='/') || (*lpszAnsiSrc=='\\') ||
(*lpszAnsiSrc=='!') || (*lpszAnsiSrc==':'))) {
*lpszAnsiDst++ = *lpszAnsiSrc++;
chEaten++;
}
*lpszAnsiDst = '\0';

A2W (szAnsiDst, lpszDst, OLEUI_CCHPATHMAX);
return chEaten;
}

/*************************************************************************
** OleStdCreateRootStorage
** create a root level Storage given a filename that is compatible
** to be used by a top-level OLE container. if the filename
** specifies an existing file, then an error is returned.
** the root storage (Docfile) that is created by this function
** is suitable to be used to create child storages for embedings.
** (CreateChildStorage can be used to create child storages.)
** NOTE: the root-level storage is opened in transacted mode.
*************************************************************************/

STDAPI_(LPSTORAGE) OleStdCreateRootStorage(LPOLESTR lpszStgName, DWORD grfMode)
{
HRESULT hr;
DWORD grfCreateMode = STGM_READWRITE | STGM_TRANSACTED;
DWORD reserved = 0;
LPSTORAGE lpRootStg;
char szMsg[64];

// if temp file is being created, enable delete-on-release
if (! lpszStgName)
grfCreateMode |= STGM_DELETEONRELEASE;

hr = StgCreateDocfile(
lpszStgName,
grfMode | grfCreateMode,
reserved,
(LPSTORAGE FAR*)&lpRootStg
);

if (hr == NOERROR)
return lpRootStg; // existing file successfully opened

OleDbgOutHResult("StgCreateDocfile returned", hr);

if (0 == LoadString(ghInst, (UINT)IDS_OLESTDNOCREATEFILE, /*(LPSTR)*/szMsg, 64))
return NULL;

MessageBox(NULL, /*(LPSTR)*/szMsg, NULL,MB_ICONEXCLAMATION | MB_OK);
return NULL;
}


/*************************************************************************
** OleStdOpenRootStorage
** open a root level Storage given a filename that is compatible
** to be used by a top-level OLE container. if the file does not
** exist then an error is returned.
** the root storage (Docfile) that is opened by this function
** is suitable to be used to create child storages for embedings.
** (CreateChildStorage can be used to create child storages.)
** NOTE: the root-level storage is opened in transacted mode.
*************************************************************************/

STDAPI_(LPSTORAGE) OleStdOpenRootStorage(LPOLESTR lpszStgName, DWORD grfMode)
{
HRESULT hr;
DWORD reserved = 0;
LPSTORAGE lpRootStg;
char szMsg[64];

if (lpszStgName) {

hr = StgOpenStorage(
lpszStgName,
NULL,
grfMode | STGM_TRANSACTED,
NULL,
reserved,
(LPSTORAGE FAR*)&lpRootStg
);

if (hr == NOERROR)
return lpRootStg; // existing file successfully opened

OleDbgOutHResult("StgOpenStorage returned", hr);
}

if (0 == LoadString(ghInst, (UINT)IDS_OLESTDNOOPENFILE, szMsg, 64))
return NULL;

MessageBox(NULL, /*(LPSTR)*/szMsg, NULL,MB_ICONEXCLAMATION | MB_OK);
return NULL;
}


/*************************************************************************
** OpenOrCreateRootStorage
** open a root level Storage given a filename that is compatible
** to be used by a top-level OLE container. if the filename
** specifies an existing file, then it is open, otherwise a new file
** with the given name is created.
** the root storage (Docfile) that is created by this function
** is suitable to be used to create child storages for embedings.
** (CreateChildStorage can be used to create child storages.)
** NOTE: the root-level storage is opened in transacted mode.
*************************************************************************/

STDAPI_(LPSTORAGE) OleStdOpenOrCreateRootStorage(LPOLESTR lpszStgName, DWORD grfMode)
{
HRESULT hrErr;
SCODE sc;
DWORD reserved = 0;
LPSTORAGE lpRootStg;
char szMsg[64];

if (lpszStgName) {

hrErr = StgOpenStorage(
lpszStgName,
NULL,
grfMode | STGM_READWRITE | STGM_TRANSACTED,
NULL,
reserved,
(LPSTORAGE FAR*)&lpRootStg
);

if (hrErr == NOERROR) 
return lpRootStg; // existing file successfully opened

OleDbgOutHResult("StgOpenStorage returned", hrErr);
sc = hrErr;

if (sc!=STG_E_FILENOTFOUND && sc!=STG_E_FILEALREADYEXISTS) {
return NULL;
}
}

/* if file did not already exist, try to create a new one */
hrErr = StgCreateDocfile(
lpszStgName,
grfMode | STGM_READWRITE | STGM_TRANSACTED,
reserved,
(LPSTORAGE FAR*)&lpRootStg
);

if (hrErr == NOERROR)
return lpRootStg; // existing file successfully opened

OleDbgOutHResult("StgCreateDocfile returned", hrErr);

if (0 == LoadString(ghInst, (UINT)IDS_OLESTDNOCREATEFILE, /*(LPSTR)*/szMsg, 64))
return NULL;

MessageBox(NULL, /*(LPSTR)*/szMsg, NULL, MB_ICONEXCLAMATION | MB_OK);
return NULL;
}


/*
** OleStdCreateChildStorage
** create a child Storage inside the given lpStg that is compatible
** to be used by an embedded OLE object. the return value from this
** function can be passed to OleCreateXXX functions.
** NOTE: the child storage is opened in transacted mode.
*/
STDAPI_(LPSTORAGE) OleStdCreateChildStorage(LPSTORAGE lpStg, LPOLESTR lpszStgName)
{
if (lpStg != NULL) {
LPSTORAGE lpChildStg;
DWORD grfMode = (STGM_READWRITE | STGM_TRANSACTED |
STGM_SHARE_EXCLUSIVE);
DWORD reserved = 0;

HRESULT hrErr = lpStg->lpVtbl->CreateStorage(
lpStg,
lpszStgName,
grfMode,
reserved,
reserved,
(LPSTORAGE FAR*)&lpChildStg
);

if (hrErr == NOERROR)
return lpChildStg;

OleDbgOutHResult("lpStg->lpVtbl->CreateStorage returned", hrErr);
}
return NULL;
}


/*
** OleStdOpenChildStorage
** open a child Storage inside the given lpStg that is compatible
** to be used by an embedded OLE object. the return value from this
** function can be passed to OleLoad function.
** NOTE: the child storage is opened in transacted mode.
*/
STDAPI_(LPSTORAGE) OleStdOpenChildStorage(LPSTORAGE lpStg, LPOLESTR lpszStgName, DWORD grfMode)
{
LPSTORAGE lpChildStg;
LPSTORAGE lpstgPriority = NULL;
DWORD reserved = 0;
HRESULT hrErr;

if (lpStg != NULL) {

hrErr = lpStg->lpVtbl->OpenStorage(
lpStg,
lpszStgName,
lpstgPriority,
grfMode | STGM_TRANSACTED | STGM_SHARE_EXCLUSIVE,
NULL,
reserved,
(LPSTORAGE FAR*)&lpChildStg
);

if (hrErr == NOERROR)
return lpChildStg;

OleDbgOutHResult("lpStg->lpVtbl->OpenStorage returned", hrErr);
}
return NULL;
}


/* OleStdCommitStorage
** -------------------
** Commit the changes to the given IStorage*. This routine can be
** called on either a root-level storage as used by an OLE-Container
** or by a child storage as used by an embedded object.
**
** This routine first attempts to perform this commit in a safe
** manner. if this fails it then attempts to do the commit in a less
** robust manner (STGC_OVERWRITE).
*/
STDAPI_(BOOL) OleStdCommitStorage(LPSTORAGE lpStg)
{
HRESULT hrErr;

// make the changes permanent
hrErr = lpStg->lpVtbl->Commit(lpStg, 0);

if (hrErr == STG_E_MEDIUMFULL) {
// try to commit changes in less robust manner.
OleDbgOut("Warning: commiting with STGC_OVERWRITE specified\n");
hrErr = lpStg->lpVtbl->Commit(lpStg, STGC_OVERWRITE);
}

if (hrErr != NOERROR)
{
char szMsg[64];

if (0 == LoadString(ghInst, (UINT)IDS_OLESTDDISKFULL, /*(LPSTR)*/szMsg, 64))
return FALSE;

MessageBox(NULL, /*(LPSTR)*/szMsg, NULL, MB_ICONEXCLAMATION | MB_OK);
return FALSE;
}
else {
return TRUE;
}
}


/* OleStdDestroyAllElements
** ------------------------
** Destroy all elements within an open storage. this is subject
** to the current transaction.
*/
STDAPI OleStdDestroyAllElements(LPSTORAGE lpStg)
{
IEnumSTATSTG FAR* lpEnum;
STATSTG sstg;
HRESULT hrErr;

hrErr = lpStg->lpVtbl->EnumElements(
lpStg, 0, NULL, 0, (IEnumSTATSTG FAR* FAR*)&lpEnum);

if (hrErr != NOERROR)
return hrErr;

while (1) {
if (lpEnum->lpVtbl->Next(lpEnum, 1, &sstg, NULL) != NOERROR)
break;
lpStg->lpVtbl->DestroyElement(lpStg, sstg.pwcsName);
OleStdFree(sstg.pwcsName);
}
lpEnum->lpVtbl->Release(lpEnum);
return NOERROR;
}

// returns 1 for a close match
// (all fields match exactly except the tymed which simply overlaps)
// 0 for no match

int IsCloseFormatEtc(FORMATETC FAR* pFetcLeft, FORMATETC FAR* pFetcRight)
{
if (pFetcLeft->cfFormat != pFetcRight->cfFormat)
return 0;
else if (!OleStdCompareTargetDevice (pFetcLeft->ptd, pFetcRight->ptd))
return 0;
if (pFetcLeft->dwAspect != pFetcRight->dwAspect)
return 0;
return((pFetcLeft->tymed | pFetcRight->tymed) != 0);
}