//====START_GENERATED_PROLOG======================================
//
//
//   COMPONENT_NAME: oddataxfer
//
//   CLASSES: none
//
//   ORIGINS: 82,27,94
//
//
//   (C) COPYRIGHT International Business Machines Corp. 1995,1996
//   All Rights Reserved
//   Licensed Materials - Property of IBM
//   US Government Users Restricted Rights - Use, duplication or
//   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
//   	
//   IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
//   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//   PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR
//   CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
//   USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
//   OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
//   OR PERFORMANCE OF THIS SOFTWARE.
//
//====END_GENERATED_PROLOG========================================
//
// @(#) 1.24 nt/src/storage/WLinkSpc.cpp, oddataxfer, od96nt, odnt9712d 9/20/96 09:17:38 [ 3/21/97 17:14:07 ]
/*
	File:		LinkSpec.cpp

	Contains:	Implementation for ODLinkSpec class.

	Owned by:	Craig Carper

	Copyright:	© 1993 - 1995 by Apple Computer, Inc., all rights reserved.

	Change History (most recent first):

		<14>	 10/8/95	TJ		Fixes Recomended by Refball
		<13>	 8/16/95	NP		1274946: ErrorDef.idl problems. Add include
									file.
		<12>	 8/12/95	T‚		1276812 Need to use TempObjs and TempRefs
									for exception safety and to avoid TRY
									blocks, 1276807 Opt./Bug: use StdTypIO
									routines for portable streaming & smaller
									footprint
		<11>	  8/3/95	RR		#1257260: Collapse B classes. Remove
									somInit methods. Don't call IsInitialized
									or SubclassResponsibility
		<10>	 5/26/95	VL		1251403: Multithreading naming support.
		 <9>	 4/27/95	CC		1242555: GetOriginatingProcessID:  Removed
									obsolete $5 comment.
		 <8>	 3/10/95	CC		1225926:  Fixed typo in FromThisDraft().
		 <7>	 1/27/95	TJ		Changed ODAppleEvent to AppleEvent.
		 <6>	12/20/94	VL		1195012: Make Storage calls be
									marshallable.
		 <5>	12/15/94	CC		1205709 LinkSpec.cpp - Removed AcquirePart(),
									GetData(), and GetDataSize() methods;
									changed ReadLinkSpec(), WriteLinkSpec(),
									ReadFromAppleEvent(), and
									WriteToAppleEvent().
		 <4>	 12/8/94	CC		1203864 WriteToAppleEvent: changed &_fData
									to _fData, to copy the link data correctly.
		 <3>	 9/15/94	CC		1186775 - Added SOM_CATCH to
									FromThisDraft(); Return som error from
									WriteToAppleEvent().
		 <2>	 7/27/94	CC		Removed GetProcessLaunchDate, added
									FromThisDraft, changed GetProcessID to
									GetOriginatingProcessID.
		 <1>	 6/21/94	CC		first checked in

		------------------- CC ---- Converted to SOM

		 <2>	  5/9/94	MB		#1162181: Changes necessary to install MMM.
		 <1>	 3/25/94	CC		first checked in

		------------------- CC ---- Moved from Core to Storage subsystem

		 <3>	 3/24/94	CC		Changed WriteToProperty() to
									WriteLinkSpec(), ReadFromProperty() to
									ReadLinkSpec() (1153049); write version in
									WriteToAppleEvent().
		 <2>	 3/15/94	MB		Changes to support SCpp/ASLM builds,
									#1150864.
		 <2>	  2/8/94	T‚		Throw -> THROW & some code clean up
		 <1>	 1/21/94	CC		first checked in
		 <4>	12/21/93	VL		Changed XMPStorageUnit::GetValueSize to
									StorageUnit::GetSize.
		 <3>	 12/8/93	CC		Added WriteToAppleEvent,
									ReadFromAppleEvent, and accessors for
									fields.
		 <2>	 12/3/93	T‚		Stop including XMPError.h, it is included
									as ErrorDef.h inside Except.h
		 <1>	11/15/93	CC		first checked in
	To Do:
*/

#define VARIABLE_MACROS

#include <stdio.h>

#define ODLinkSpec_Class_Source
#include <LinkSpec.xih>

#ifndef SOM_Module_OpenDoc_StdProps_defined
#include "StdProps.xh"
#endif

#ifndef SOM_Module_OpenDoc_StdTypes_defined
#include "StdTypes.xh"
#endif

#ifndef SOM_ODDraft_xh
#include "Draft.xh"
#endif  

#ifndef SOM_ODStorageUnit_xh
#include "StorageU.xh"
#endif 

// rrk 3 apr 96 namespace
#ifndef SOM_ODNameSpace_xh
#include <NamSpac.xh>
#endif

#ifndef SOM_DocumentManager_xh
#include <DocMgr.xh>
#endif

#ifndef _EXCEPT_
#include "Except.h"
#endif

#ifndef _ODMEMORY_
#include "ODMemory.h"
#endif

#ifndef __ERRORS__
#include <Errors.h>
#endif

#ifndef _BARRAY_
#include <BArray.h>
#endif

#ifndef _STDTYPIO_
#include <StdTypIO.h>
#endif

#ifndef _TEMPOBJ_
#include <TempObj.h>
#endif

#ifndef _STORUTIL_
#include <StorUtil.h>
#endif

#ifndef _UTILERRS_
#include "UtilErrs.h"
#endif

#ifdef OD_PAGETUNE_H
#include <ODPagtun.h>
#endif

#include <wcstr.h> // for wcslen function

#ifndef _ODUTILW_
#include "ODUtilW.h"
#endif
  
#ifndef _OLEDATA_H_
#include "OleData.h"
#endif

#ifndef _OLEXFER_H_
#include "OleXfer.h"
#endif

#ifndef _OLEMGRW_ 
#include "OleMgrW.h"   
#endif   

#ifndef _OLECTL_H_
#include "OleCtl.h"   
#endif   

#ifndef _WINBASE_
#include "winbase.h"
#endif
 
#ifndef _OLEWRAP_H_
#include "OleWrap.h"
#endif
 
#ifndef _WINERROR_
#include <winerror.h>
#endif

#include <qprintf.h>

#include <wtypes.h>

#pragma segment ODLinkSpec

//==============================================================================
// Constants
//==============================================================================
const ODULong kLinkSpecVersion = 1;	// Second version

// ¥¥¥¥ kODSourcePartKey also defined for Drag & Drop ¥¥¥¥
// ¥¥¥¥ These don't belong here ¥¥¥¥
#define kODProcessLaunchDateKey	'pldt'
#define kODSourcePartKey			'part'
#define kODLinkSpecDataKey			'data'
#define kODLinkSpecDataSizeKey		'size'
#define kODLinkSpecVersionKey		'vers'

// This function needed because MS doesn't support sscanf in a dll !
void ReadHex(char *, char *, ULONG *);

// Macro to figure the size of a link specification item moniker name 
#define ODLINKSPECSIZE(dataSize)  ( strlen(ODLINKITEMPREFIX) +	\
									 6*(2*sizeof(ODULong)) 	  +	\
									 (2*dataSize) 			  +	\
									 20 						\
							      )							
// rrk 18 apr 96 - temp stuff until wrapper is ready.
#ifdef LATER 
   // nothing - will be done by the wrapper
#else
  #ifndef INITGUID
    #define INITGUID
    #include <initguid.h>  // rrk 24 apr 96 - I copied this include from Novell's OleMgrW.cpp.  If I 
                       //                 don't include initguid.h I cannot link - get CLSID_OleWrapper not defined.
    DEFINE_GUID(CLSID_OleWrapper,   	0x84446680, 0x2470, 0x101b, 0x8E, 0xC8, 0x04,0x02,0x1C,0x00,0x70,0x02); 
    #undef  INITGUID
  #else
    DEFINE_GUID(CLSID_OleWrapper,   	0x84446680, 0x2470, 0x101b, 0x8E, 0xC8, 0x04,0x02,0x1C,0x00,0x70,0x02); 
  #endif
#endif // LATER

#define ODLINKITEMPREFIX		"ODLink"	/* Item moniker prefix */ 
// rrk 18 apr 96 end temp stuff
//==============================================================================
// ODLinkSpec
//==============================================================================

#ifdef _PLATFORM_WIN32_
//------------------------------------------------------------------------------
// ODLinkSpec: somInit
//------------------------------------------------------------------------------

SOM_Scope void  SOMLINK ODLinkSpecsomInit(ODLinkSpec *somSelf)
{
    ODLinkSpecData *somThis = ODLinkSpecGetData(somSelf);
    ODLinkSpecMethodDebug("ODLinkSpec","somInit");

    parent_somInit(somSelf);

	_fPart = kODNULL;
	_fPartData = CreateEmptyByteArrayStruct(0);

}
#endif
//------------------------------------------------------------------------------
// ODLinkSpec::somUninit
//------------------------------------------------------------------------------

SOM_Scope void  SOMLINK ODLinkSpecsomUninit(ODLinkSpec *somSelf)
{
    ODLinkSpecData *somThis = ODLinkSpecGetData(somSelf);
    ODLinkSpecMethodDebug("ODLinkSpec","somUninit");

#ifdef _PLATFORM_WIN32_
	DisposeByteArrayStruct(_fPartData);
#endif

    parent_somUninit(somSelf);

    if (_fpmkFull)
	    ((IUnknown*)_fpmkFull)->Release();
}

/* METHOD: InitLinkSpec
 *
 * DESCRIPTION:
 *   Initialize this link spec object
 */
SOM_Scope void SOMLINK ODLinkSpecInitLinkSpec(
	ODLinkSpec	*somSelf,
	Environment	*ev,
	ODPart		*part,
	ODByteArray	*partData)
{
	ODLinkSpecData	*somThis	= ODLinkSpecGetData(somSelf);

//	data, size $$$$ were old params
	
	somSelf->InitBaseLinkSpec(ev, part, partData);

#ifdef _PLATFORM_WIN32_
	if ( partData )
	{
		DisposeByteArrayStruct(_fPartData);
		_fPartData = CopyByteArrayStruct(partData);
	}

	_fPart = part;
#endif
	
	TRY
		ODGetCurrentProcess(&_fProcessID);
	// 19 apr 96 rrk
	        FILETIME ExitTime, KernelTime, UserTime;
        // 23 aug 96 rrk GetProcessTimes is not supported on Win95.  _fProcessLaunchDate
        //           will always be left at zero on that platform.
	        GetProcessTimes(GetCurrentProcess(),&_fProcessLaunchDate, &ExitTime, &KernelTime, &UserTime);

	
	CATCH_ALL
		somSelf->VoidLinkSpec(ev);
		RERAISE;
	ENDTRY
}

#ifdef _PLATFORM_WIN32_
//------------------------------------------------------------------------------
// ODLinkSpec::GetPart
//------------------------------------------------------------------------------

SOM_Scope ODPart*  SOMLINK ODLinkSpecGetPart(ODLinkSpec *somSelf, Environment *ev)
{
    ODLinkSpecData *somThis = ODLinkSpecGetData(somSelf);
    ODLinkSpecMethodDebug("ODLinkSpec","GetPart");

	return _fPart;

	return kODNULL;

}

//------------------------------------------------------------------------------
// ODLinkSpec::GetPartData
//------------------------------------------------------------------------------

SOM_Scope ODByteArray  SOMLINK ODLinkSpecGetPartData(ODLinkSpec *somSelf, Environment *ev)
{
    ODLinkSpecData *somThis = ODLinkSpecGetData(somSelf);
    ODLinkSpecMethodDebug("ODLinkSpec","GetPartData");

	SOM_TRY
	
	return CopyByteArrayStruct(&_fPartData);

	SOM_CATCH_ALL

	SOM_ENDTRY
	  return CreateEmptyByteArrayStruct(0);
}
#endif

/* METHOD: FromThisDraft
 *
 * DESCRIPTION:
 *   Look at the process serial number and so on to see if this link spec
 *   originated in this processes draft
 */
SOM_Scope ODBoolean  SOMLINK ODLinkSpecFromThisDraft(
	ODLinkSpec 	*somSelf,
	Environment *ev)
{
    ODLinkSpecData 		*somThis = ODLinkSpecGetData(somSelf);
	ProcessSerialNumber myPSN;
	BOOL				isSamePSN;

	SOM_TRY
	
	ODGetCurrentProcess(&myPSN);
	ODSameProcess(&_fProcessID, &myPSN, &isSamePSN);
	/*	return	isSamePSN; */
 /* LATER: && (launchDate == myProcessInfo.processLaunchDate) */;

	// 19 apr 96 rrk
	FILETIME ExitTime, KernelTime, UserTime, LaunchDate;
        // 22 aug 96 rrk on Win95 GetProcessTimes does not work.  We will
        //               initial the fields to zero, and so they will remain.
        LaunchDate.dwLowDateTime = LaunchDate.dwHighDateTime = 0;
	GetProcessTimes(GetCurrentProcess(),&LaunchDate, &ExitTime, &KernelTime, &UserTime);
	return (isSamePSN && 
               (LaunchDate.dwLowDateTime == _fProcessLaunchDate.dwLowDateTime) &&
               (LaunchDate.dwHighDateTime == _fProcessLaunchDate.dwHighDateTime));

	SOM_CATCH_ALL

	SOM_ENDTRY
	  return kODFalse;
}

/* METHOD: GetOriginatingProcessID
 *
 * DESCRIPTION:
 *   Get the process id of the person who created this link specification
 */
SOM_Scope void SOMLINK ODLinkSpecGetOriginatingProcessID(
	ODLinkSpec			*somSelf,
	Environment			*ev,
	ProcessSerialNumber	*psn)
{
	ODLinkSpecData	*somThis	= ODLinkSpecGetData(somSelf);

	*psn	= _fProcessID;
}


/* METHOD OVERRIDE: WriteLinkSpec
 *
 * DESCRIPTION:
 *   Externalize this link specification to the given pre-focused
 *	 storage unit
 */
SOM_Scope void SOMLINK ODLinkSpecWriteLinkSpec(
	ODLinkSpec		*somSelf,
	Environment		*ev,
	ODStorageUnit	*su)
{
	ODLinkSpecData		*somThis= ODLinkSpecGetData(somSelf);

	OleWrapper                      *pWrap  = ODOLEManager::GetShell()->GetActiveDocumentManager(ev)->GetOleWrapper(ev);

	LPMONIKER 			pmkItem	= NULL;
	LPMONIKER 			pmkFull	= NULL;
	ODULong				cbSrcOfCopy, cbFullUserTypeName;
	LPSTREAM			pStream;
	OBJECTDESCRIPTOR	*lpOD;
	char				szTemp[500];
	STATSTG				Status;
	HRESULT				hr;
	HGLOBAL				hGlobal;
	ODULong 			size, *lValue;
	char	 			*pBuffer;
	ODVolatile(su);

	/* Add the kODLinkSpec property, which is really the CF_LINKSOURCE
	 * OLE clipboard format...
         * rrk 29 Mar 96; DR4 uses ODSUForceFocus here, so we do the same.
	 *  
         * The design says we have to change the beginning of
         * this method, but I think that's wrong.  kODLinkSpec is not assumed to be in kODPropContents
         * in this code.
	 */
     SOM_TRY
	if (! su)
	  	THROW(kODErrIllegalNullStorageUnitInput);

	ODSUForceFocus(ev, su, kODNULL, kODLinkSpec);
	size = su->GetSize(ev);
	su->DeleteValue(ev, size);

	TRY
		/* Create the item moniker name string, a combination of the
		 * fixed size variables expressed in hex - followed by a stream
		 * of hex bytes for the data.
		 */
	        /*
		 * rrk - 29 Mar 96 - insert version code from DR4.  The version is the first thing written
		 *                   or read from the LinkSpec.
		 */

	        ODULong version = kLinkSpecVersion;
	        StorageUnitSetValue(su, ev, sizeof(ODULong), (ODValue) &version);
                // rrk 24 jun 96 - if we do not have any left-hand moniker (pWrap->fpMoniker) then
		//                 we just return.  We don't want to put any link information in
		//                 the data transfer object.
		// rrk 13 Aug 96 - do not use fpMoniker 
		IMoniker *theMoniker;
		hr = pWrap->fpIOleObject->GetMoniker(OLEGETMONIKER_FORCEASSIGN,
						     OLEWHICHMK_OBJFULL,
                       						     &theMoniker);
		if ( FAILED(hr) || (theMoniker == kODNULL) ) {
		  WARNMSG_DEBUG(WARN_INDEX(-1),"ODClipboard: Did not get a moniker from the Ole Wrapper");
		         // 20 aug 96 - if we can't get the moniker, we need to clean up the storage unit passed
		         //             in to us.  Specifically, we'd better remove the kODPropLinkSpec the part
		         //             put on.
		  THROW(kODErrCannotGetExternalLink); 
		  return;
		}

		pBuffer	= somSelf->WriteLinkSpecString(ev);
		if (pBuffer)
		{

			/* Convert ANSI to unicode for our moniker name
			 */
			LPOLESTR	pwBuffer;

			size		= (strlen(pBuffer) + 1) * 2;
			pwBuffer	= (LPOLESTR)ODNewPtr(size);
			if(pwBuffer)
			{
				A2W(pBuffer,pwBuffer,size);
				ODDisposePtr(pBuffer);
				pBuffer	= (char*)pwBuffer;
			}

			/* Now create a full composite moniker out of link source
			 * descriptor string...
			 */
			// following added 14 may 96 rrk from /oot/opendoc/src/Novell...
			// For WIN32 CreateItemMoniker takes a unicode string, we must create
			LPOLESTR pwDelim;
			ODULong  size = (strlen(OLESTDDELIM) + 1) * 2;

			pwDelim =  (LPOLESTR) ODNewPtr(size);
			if (pwDelim) {
				A2W(OLESTDDELIM, pwDelim, size);
				CreateItemMoniker(pwDelim, (LPOLESTR) pBuffer, &pmkItem);
				ODDisposePtr(pwDelim);
			}

			//			CreateItemMoniker((LPCOLESTR)OLESTDDELIM,(LPOLESTR)pBuffer,&pmkItem);
			if (pmkItem)
			{
#ifndef LATER // rrk 18 april wait until olewrapper complete.  The real file moniker is based on the name of
			    //              the document.  We'll just invent a dummy name for now.
			    // rrk 4 jun 96 OK let's try it
#ifdef ODDebugClipboard

				char* dispName;
				IBindCtx *bndctxf, *bndctxi, *bndctxc;

				CreateBindCtx(0, &bndctxf);
			        theMoniker->GetDisplayName(bndctxf, NULL,(LPOLESTR*)&dispName);
				/* Convert Unicode to ANSII
									 */
				ODULong cbString  = (wcslen((LPOLESTR)dispName)+1) * sizeof(OLECHAR);
				LPSTR pszTemp		= (char *)ODNewPtr(cbString);
				if (pszTemp)
				{
				   W2A((LPOLESTR)dispName,pszTemp,cbString);
				   dispName	= pszTemp;
				}
				PRINT("WriteLinkSpec: GetDisplayName on file moniker yields:\n\t\"%s\".\n", dispName);

				CreateBindCtx(0, &bndctxi);
			        pmkItem->GetDisplayName(bndctxi, NULL,(LPOLESTR*)&dispName);
				/* Convert Unicode to ANSII
									 */
				cbString  = (wcslen((LPOLESTR)dispName)+1) * sizeof(OLECHAR);
				pszTemp		= (char *)ODNewPtr(cbString);
				if (pszTemp)
				{
				   W2A((LPOLESTR)dispName,pszTemp,cbString);
				   dispName	= pszTemp;
				}
				PRINT("WriteLinkSpec: GetDisplayName on item moniker yields:\n\t\"%s\".\n", dispName);
	
#endif // ODDebugClipboard
				//				CreateGenericComposite(pWrap->fpMoniker,pmkItem,&pmkFull);
				theMoniker->ComposeWith(pmkItem,FALSE,&pmkFull);
#ifdef ODDebugClipboard
				CreateBindCtx(0, &bndctxc);
			        pmkFull->GetDisplayName(bndctxc, NULL,(LPOLESTR*)&dispName);
				/* Convert Unicode to ANSII
									 */
				cbString  = (wcslen((LPOLESTR)dispName)+1) * sizeof(OLECHAR);
				pszTemp		= (char *)ODNewPtr(cbString);
				if (pszTemp)
				{
				   W2A((LPOLESTR)dispName,pszTemp,cbString);
				   dispName	= pszTemp;
				}
				PRINT("WriteLinkSpec: GetDisplayName on composite moniker yields:\n\t\"%s\".\n", dispName);
				CLSID clsid;
				hr = pmkFull->GetClassID(&clsid);
	                        hr = StringFromCLSID(clsid, (LPOLESTR*) &dispName);
				/* Convert Unicode to ANSII
									 */
				cbString  = (wcslen((LPOLESTR)dispName)+1) * sizeof(OLECHAR);
				pszTemp		= (char *)ODNewPtr(cbString);
				if (pszTemp)
				{
				   W2A((LPOLESTR)dispName,pszTemp,cbString);
				   dispName	= pszTemp;
				}
				PRINT("WriteLinkSpec: CLSID for composite moniker is:\n\t\"%s\".\n", dispName);
#endif // ODDebugClipboard
#else
				LPMONIKER tempMoniker;
				char tempFileName[] = "TemporaryFileMoniker";
                                LPOLESTR unicodeFileName;
				ODULong size = (strlen(tempFileName) + 1) * 2;
				unicodeFileName = (LPOLESTR) ODNewPtr(size);
				if (unicodeFileName) {
				  A2W(tempFileName, unicodeFileName, size);

				  CreateFileMoniker(unicodeFileName, &tempMoniker);
				  ODDisposePtr(unicodeFileName);
				  }
				if (!tempMoniker)
			           PRINT("WriteLinkSpec; CreateFileMoniker failed; GetLastError returns %d.\n", GetLastError());
				CreateGenericComposite(tempMoniker,pmkItem,&pmkFull);
				if (!pmkFull)
			           PRINT("WriteLinkSpec; CreateGenericComposite failed; GetLastError returns %d.\n", GetLastError());
				tempMoniker->Release();
#endif
				if (pmkFull)
				{
					/* Create a stream to serialize the moniker into
					 */
					hGlobal	= GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD,0);
					if (hGlobal)
					{
						hr	= CreateStreamOnHGlobal(hGlobal,FALSE,&pStream);
						if (SUCCEEDED(hr))
						{
							/* Now write out the CLSID and MONIKER in the
							 * standard CF_LINKSOURCE format ... We also
							 * save OUR class id after the serialized
							 * CLSID and MONIKER. That is wierd, but the
							 * code in OLE2UI does this, so it must be right?
							 */
#if 1 // This call to OleSaveToStream does a lot of stuff.  If we have trouble here, we
      // might want to change this "if 1" to "if 0."  The "else" clause here does all the OleSaveToStream
      // gorp by hand.
							hr	= OleSaveToStream(pmkFull,pStream);
#else
							CLSID clsid;
							hr = pmkFull->GetClassID(&clsid);
        					if (FAILED(hr))
		        			  PRINT("WriteLinkSpec: GetClassID failed; severity %d facility %d code %d.\n",
				            		  HRESULT_SEVERITY(hr), HRESULT_FACILITY(hr), HRESULT_CODE(hr));
							
							hr = WriteClassStm(pStream, clsid);
        					if (FAILED(hr))
		        			  PRINT("WriteLinkSpec: WriteClassStm failed; severity %d facility %d code %d.\n",
				            		  HRESULT_SEVERITY(hr), HRESULT_FACILITY(hr), HRESULT_CODE(hr));
							
							hr = pmkFull->Save(pStream, TRUE);  // true for "clear dirty flag"
        					if (FAILED(hr))
		        			  PRINT("WriteLinkSpec: Save failed; severity %d facility %d code %d.\n",
				            		  HRESULT_SEVERITY(hr), HRESULT_FACILITY(hr), HRESULT_CODE(hr));
#endif
							if (SUCCEEDED(hr))
							  WriteClassStm(pStream,CLSID_OleWrapper);
						        else WARNMSG_DEBUG(WARN_INDEX(-1),"WriteLinkSpec; OleSaveToStream returned %d.\n", HRESULT_CODE(hr));
	
							/* Figure out the size of the stream
							 */
							pStream->Stat(&Status,STGTY_STREAM);
							size	= Status.cbSize.LowPart;
	
							/* Now write the stream state back to our property						
							 */
							lValue	= (ODULong*)GlobalLock(hGlobal);
							StorageUnitSetValue(su,ev,size,(ODValue)lValue);
							GlobalUnlock(hGlobal);
	
							/* Throw away our stream
							 */

							pStream->Release();
						}
						else WARNMSG_DEBUG(WARN_INDEX(-1),"WriteLinkSpec; CreateStreamOnHGlobal returned %d.\n", HRESULT_CODE(hr));
						GlobalFree(hGlobal);
					}
					if (_fpmkFull)
						((IUnknown*)_fpmkFull)->Release();
					_fpmkFull	= (LPVOID)pmkFull;
				}
#ifdef LATER
				OleStdRelease(pmkItem);
#else
                                ((LPUNKNOWN)pmkItem)->Release();
#endif
			}
			else WARNMSG_DEBUG(WARN_INDEX(-1),"WriteLinkSpec; CreateItemMoniker failed; GetLastError returns %d.\n", GetLastError());
			ODDisposePtr(pBuffer);
		}
	CATCH_ALL
		su->Remove(ev);
                somExceptionFree(ev);  SOM_InitEnvironment(ev);
                SetErrorCode(kODNoError);
		return;
		//RERAISE;
	ENDTRY

	/* Add the kODLinkSpecDescriptor property, which is really the
	 * CF_LINKSOURCEDESCRIPTOR OLE clipboard format
	 */
	if (su->Exists(ev,(ODPropertyName) kODNULL, kODLinkSpecDescriptor, 0) == kODFalse)
		su->AddValue(ev,kODLinkSpecDescriptor);
	else
	{
		su->Focus(ev,(ODPropertyName)kODNULL,kODPosSame,
				  kODLinkSpecDescriptor,0,kODPosUndefined);
		size = su->GetSize(ev);
		su->DeleteValue(ev,size);
	}
	/* rrk 29 Mar 95 - need to internationalize these two strings: "OpenDoc Part" and
         *                 "OpenDoc part content."
	 */
	lpOD  						= (OBJECTDESCRIPTOR*)szTemp;
	memset(lpOD,0,sizeof(OBJECTDESCRIPTOR));
	lpOD->cbSize				= sizeof(OBJECTDESCRIPTOR);
	lpOD->clsid		 		    = CLSID_OleWrapper;
	lpOD->dwFullUserTypeName	= sizeof(OBJECTDESCRIPTOR);
	HRESULT  hrStatus  = OleRegGetMiscStatus(CLSID_OleWrapper,DVASPECT_CONTENT,&lpOD->dwStatus);
	lpOD->dwDrawAspect = DVASPECT_CONTENT;

	// rrk 17 jul 96 add i18n
	char* myString;
	myString = catgets(ODcat_handle, LINKSPEC_SET, LINKSPEC_10, "OpenDoc Part");
	/* Convert ANSI to unicode			 */
	LPOLESTR	pwBuffer;

	size		= (strlen(myString) + 1) * 2;
	pwBuffer	= (LPOLESTR)ODNewPtr(size);
	if(pwBuffer) {
	  A2W(myString,pwBuffer,size);
	}

	cbFullUserTypeName			= (strlen(myString) + 1)*sizeof(OLECHAR);
	memcpy(((char*)lpOD)+lpOD->dwFullUserTypeName,
		   pwBuffer,
		   cbFullUserTypeName);
	lpOD->dwSrcOfCopy			= sizeof(OBJECTDESCRIPTOR) + cbFullUserTypeName;
	myString = catgets(ODcat_handle, LINKSPEC_SET, LINKSPEC_20, "OpenDoc part content");
	/* Convert ANSI to unicode			 */

	size		= (strlen(myString) + 1) * 2;
	pwBuffer	= (LPOLESTR)ODNewPtr(size);
	if(pwBuffer) {
	  A2W(myString,pwBuffer,size);
	}
	cbSrcOfCopy					= (strlen(myString) + 1)*sizeof(OLECHAR);
	memcpy(((char*)lpOD)+lpOD->dwSrcOfCopy,
		   pwBuffer,
		   cbSrcOfCopy);

	size	= sizeof(OBJECTDESCRIPTOR) + cbSrcOfCopy + cbFullUserTypeName;
        lpOD->cbSize = size; // rrk 18 sep 96.  Not sure, but I think this cbSize field is the size of the whole struct
	StorageUnitSetValue(su,ev,size,(ODValue)lpOD);
	parent_WriteLinkSpec(somSelf, ev, su);
     SOM_CATCH_ALL
     SOM_ENDTRY
}


/* METHOD OVERRIDE: ReadLinkSpec
 *
 * DESCRIPTION:
 *   Read the link specification from a given storage unit
 */
SOM_Scope void SOMLINK ODLinkSpecReadLinkSpec(
	ODLinkSpec		*somSelf,
	Environment		*ev,
	ODStorageUnit	*su)
{
	ODLinkSpecData	*somThis		= ODLinkSpecGetData(somSelf);
	LPMONIKER 		pmkFull			= NULL;
	LPMONIKER 		pmkItem			= NULL;
	char  	 		*pHexDigit		= NULL;
	LPSTR			pszTemp			= NULL;
	ODULong			cbString;
	LPBINDCTX		pBindCtx;
	HGLOBAL			hGlobal;
	LPSTREAM		pStream;
	ODULong 		size, mkType;
	IEnumMoniker	        *penMoniker;
	HRESULT			hr;
	CLSID			clsID;
	char	 		*pBuffer;
	ODULong			version;

	// Pre-focused property must contain a link spec value
	if (! su)
	  	THROW(kODErrIllegalNullStorageUnitInput);
	if (!ODSUExistsThenFocus(ev, su, (ODPropertyName) kODNULL, kODLinkSpec))
		THROW(kODErrNoLinkSpecValue);

	SOM_TRY
	        /*
		 * rrk - 29 Mar 96 - insert version code from DR4.  The version is the first thing written
		 *                   or read from the LinkSpec.
		 */
	  
	        StorageUnitGetValue(su, ev, sizeof(ODULong), (ODValue) &version);
 	        if (version != kLinkSpecVersion) 
		THROW(kODErrUnknownLinkSpecVersion);

		/* Read the link source specification into a global memory block
		 */
		size	= su->GetSize(ev);
		hGlobal	= GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD,size);
		if (hGlobal)
		{
			pBuffer	= (LPSTR)GlobalLock(hGlobal);
			if(pBuffer)
			{
				StorageUnitGetValue(su,ev,size,(ODValue)pBuffer);
				GlobalUnlock(hGlobal);

				/* Now create a stream based on the link specification and
				 * extract the CLSID and composite moniker name
				 */
				hr	= CreateStreamOnHGlobal(hGlobal,FALSE,&pStream);
				if (SUCCEEDED(hr))
				{
					/* Load the moniker that is contained within this
					 * stream
					 */
#if 1 // This call to OleLoadFromStream does a lot of stuff.  If we have trouble here, we
      // might want to change this "if 1" to "if 0."  The "else" clause here does all the OleLoadFromStream
      // gorp by hand.
					hr	= OleLoadFromStream(pStream,
											IID_IMoniker,
											(void**)&pmkFull);
#else
					hr = ReadClassStm(pStream,&clsID);
					if (FAILED(hr))
					  PRINT("ReadLinkSpec: ReadClassStm failed; severity %d facility %d code %d.\n",
						  HRESULT_SEVERITY(hr), HRESULT_FACILITY(hr), HRESULT_CODE(hr));

					hr = CoCreateInstance(clsID, 
							      NULL, 
							      CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER,
							      IID_IMoniker, (void**)&pmkFull);
					if (FAILED(hr))
					  PRINT("ReadLinkSpec: CoCreateInstance failed; severity %d facility %d code %d.\n",
						  HRESULT_SEVERITY(hr), HRESULT_FACILITY(hr), HRESULT_CODE(hr));

					IPersistStream* pIPersistStream;
	                                hr = pmkFull->QueryInterface(IID_IPersistStream, (void**)&pIPersistStream);
					if (FAILED(hr))
					  PRINT("ReadLinkSpec: QueryInterface failed; severity %d facility %d code %d.\n",
						  HRESULT_SEVERITY(hr), HRESULT_FACILITY(hr), HRESULT_CODE(hr));

					hr = pIPersistStream->Load(pStream);
					if (FAILED(hr))
					  PRINT("ReadLinkSpec: Load failed; severity %d facility %d code %d.\n",
						  HRESULT_SEVERITY(hr), HRESULT_FACILITY(hr), HRESULT_CODE(hr));

#endif
					if (SUCCEEDED(hr))
					{
						ReadClassStm(pStream,&clsID);

						/* Store the composite moniker away within ourselves
						 */
						if (_fpmkFull)
							((IUnknown*)_fpmkFull)->Release();
						_fpmkFull	= (LPVOID)pmkFull;
	
						/* Now extract our standard information, if the
						 * moniker is from an OpenDoc link source. Use a
						 * reverse-direction enumerator so that the first
						 * element we get should be an item moniker...
						 */
						hr	= ((IMoniker*)_fpmkFull)->Enum(FALSE,&penMoniker);
						if (SUCCEEDED(hr))
						{
							hr	= penMoniker->Next(1,&pmkItem,NULL);
							if (SUCCEEDED(hr))
							{
								/* If this IS an item moniker, and one of ours
								 * we can extract the necessary info from
								 * the item moniker's text string...
								 */
								hr	= pmkItem->IsSystemMoniker(&mkType);
								if  (  SUCCEEDED(hr)
									&& (mkType==MKSYS_ITEMMONIKER)
									&& SUCCEEDED(CreateBindCtx(0,&pBindCtx))
									)
								{
									/* Get the display name of this item
									 * moniker.
									 */
									hr	= pmkItem->GetDisplayName(pBindCtx,
																  NULL,
																  (LPOLESTR*)&pHexDigit);
									//#ifdef WIN32						
									/* BUG WORKAROUND: for Daytona OLE2 only
									 * where the item moniker name is wrong
									 * and gets preceded with an unwanted "\"
									 */
									if (!strcmp(pHexDigit,(char*)OLESTDDELIM))
									{
//										_asm { int 3 }; // DAYTONA WORKAROUND
										pHexDigit += 2;
									}

									/* Convert Unicode to ANSII
									 */
									cbString	= (wcslen((LPOLESTR)pHexDigit)+1) * sizeof(OLECHAR);
									pszTemp		= (char *)ODNewPtr(cbString);
									if (pszTemp)
									{
										W2A((LPOLESTR)pHexDigit,pszTemp,cbString);
										pHexDigit	= pszTemp;
									}
									//#endif
									pBindCtx->Release();
								}
					                  	else WARNMSG_DEBUG(WARN_INDEX(-1),"WriteLinkSpec; IsSystemMoniker returned %d.\n", HRESULT_CODE(hr)); 
								
								/* If it is one of our item monikers, read
								 * in our state variables from the moniker's
								 * text string...
								 */
								if (  SUCCEEDED(hr)
								   && pHexDigit
								   )
								{
									pHexDigit = strstr(pHexDigit,
													   ODLINKITEMPREFIX);
									if (pHexDigit)
									{
										pHexDigit += strlen(ODLINKITEMPREFIX);
										somSelf->ReadLinkSpecString(ev,pHexDigit);
									}
								}
					                	else WARNMSG_DEBUG(WARN_INDEX(-1),"ReadLinkSpec; GetDisplayName returned %d.\n", HRESULT_CODE(hr));
								pmkItem->Release();
							}
					         	else WARNMSG_DEBUG(WARN_INDEX(-1),"ReadLinkSpec; Next returned %d.\n", HRESULT_CODE(hr));
							penMoniker->Release();
						}
						else WARNMSG_DEBUG(WARN_INDEX(-1),"ReadLinkSpec; Enum returned %d.\n", HRESULT_CODE(hr));
					}
					else {
                                              WARNMSG_DEBUG(WARN_INDEX(-1),"ReadLinkSpec; OleLoadFromStream returned %d.\n", HRESULT_CODE(hr));
					     }
					pStream->Release();
				}
				else WARNMSG_DEBUG(WARN_INDEX(-1),"ReadLinkSpec; CreateStreamOnHGlobal returned %d.\n", HRESULT_CODE(hr));
			}
			GlobalFree(hGlobal);
		}
	parent_ReadLinkSpec(somSelf, ev, su);
	SOM_CATCH_ALL
		somSelf->VoidLinkSpec(ev);
	//RERAISE;
	SOM_ENDTRY
}


/* METHOD: UseItemName
 *
 * DESCRIPTION:
 *   Use just the item name piece of the moniker to figure out this link
 *   specification: the implication is that this moniker WAS created by this
 *   process otherwise you would never get here !
 */
SOM_Scope void SOMLINK ODLinkSpecUseItemName(
	ODLinkSpec	*somSelf,
	Environment	*ev,
	char		*pszItemName)
{
	ODLinkSpecData	*somThis	= ODLinkSpecGetData(somSelf);

	pszItemName = strstr(pszItemName,
						 ODLINKITEMPREFIX);
	if (pszItemName)
	{
		pszItemName += strlen(ODLINKITEMPREFIX);
		somSelf->ReadLinkSpecString(ev,pszItemName);
	}
}

/* METHOD: VoidLinkSpec
 *
 * DESCRIPTION:
 *   Empty out the link specification
 */
SOM_Scope void SOMLINK ODLinkSpecVoidLinkSpec(
	ODLinkSpec	*somSelf,
	Environment	*ev)
{
	ODLinkSpecData	*somThis	= ODLinkSpecGetData(somSelf);

	DisposeByteArrayStruct(_fPartData);
	if(_fpmkFull)
		((IUnknown *)_fpmkFull)->Release();
	memset(somThis,0,sizeof(ODLinkSpecData));
	_fPartData = CreateEmptyByteArrayStruct(0);
}

/* METHOD: WriteLinkSpecString
 *
 * DESCRIPTION:
 *   Allocate and fill in a text string that represents our current OpenDoc
 *   link specification.
 */
SOM_Scope char * SOMLINK ODLinkSpecWriteLinkSpecString(
	ODLinkSpec	*somSelf,
	Environment	*ev)
{
	ODLinkSpecData	*somThis	= ODLinkSpecGetData(somSelf);
	char			*pBuffer;
	char			*pHexDigit;
	ODULong 		index, size;
	ODULong			*lValue;

	/* Allocate a new piece of memory to hold our current link specification
	 * as a text string.
	 */
	pBuffer	= (char*)ODNewPtr(ODLINKSPECSIZE(_fPartData._length));
	{
		/* Copy in the prefix, which also acts as the version number !
		 */
		strcpy(pBuffer,ODLINKITEMPREFIX);
		pHexDigit = pBuffer + strlen(pBuffer);

		/* Write the standard properties of this link specification
		 */
		sprintf(pHexDigit,"%08lx",(ODULong)_fProcessID.lowLongOfPSN);
		pHexDigit	+= 8;
		sprintf(pHexDigit,"%08lx",(ODULong)_fProcessID.highLongOfPSN);
		//#ifdef WIN32
		pHexDigit	+= 8;
		sprintf(pHexDigit,"%08lx",(ODULong)_fProcessLaunchDate.dwLowDateTime);
		pHexDigit	+= 8;
		sprintf(pHexDigit,"%08lx",(ODULong)_fProcessLaunchDate.dwHighDateTime);
		//#else
		/* LATER: need to write the LaunchDate as a 64bit FILETIME so we
		 * can link between Win16 and Win32 OpenDoc parts. Also need to find
		 * a way to access 16bit process ids from 32bit !
		 */
		//_asm {int 3};
		//#endif
		pHexDigit	+= 8;
		sprintf(pHexDigit,"%08lx",(ODULong)_fPart);
		pHexDigit	+= 8;
		sprintf(pHexDigit,"%08lx",(ODULong)_fPartData._length);
		pHexDigit	+= 8;

		/* Now write the link data out and NULL terminate
		 */
		// rrk 2 jun 96 - this used to work, but now adding 1 to the length causes problems
		//                when the data size is divisible by four.
		//size		= _fPartData._length ? (_fPartData._length / 4) + 1 : 0;
		size		= _fPartData._length ? (_fPartData._length / 4) : 0;
		if (_fPartData._length % 4) size++;
		for ( index = 0, lValue = (ODULong*)_fPartData._buffer;
			  index < size;
			  index++  , lValue++
			)
		{
			sprintf(pHexDigit,"%08lx",*lValue);
			pHexDigit	+= 8;
		}
		*pHexDigit	= 0;
	}
	return	pBuffer;
}

/* METHOD: ReadLinkSpecString
 *
 * DESCRIPTION:
 *   Read in our instance variables from an OpenDoc link specification string
 */
SOM_Scope void SOMLINK ODLinkSpecReadLinkSpecString(
	ODLinkSpec	*somSelf,
	Environment	*ev,
	char		*pHexDigit)
{
	ODLinkSpecData	*somThis	= ODLinkSpecGetData(somSelf);
	ODULong    		index,size;
	ODULong			*lValue;
	char			*Data;
	ODULong			DataSize;

	/* Read the standard properties of this link specification
	 */
	ReadHex(pHexDigit,"%08lx",&_fProcessID.lowLongOfPSN);
	pHexDigit	+= 8;
	ReadHex(pHexDigit,"%08lx",&_fProcessID.highLongOfPSN);
	//#ifdef WIN32
	pHexDigit	+= 8;
	ReadHex(pHexDigit,"%08lx",&_fProcessLaunchDate.dwLowDateTime);
	pHexDigit	+= 8;
	ReadHex(pHexDigit,"%08lx",&_fProcessLaunchDate.dwHighDateTime);
	//#else
	/* LATER: need to read the LaunchDate from a 64bit FILETIME so we
	 * can link between Win16 and Win32 OpenDoc parts. Also need to find
	 * a way to access 16bit process ids from 32bit !
	 */
	//_asm {int 3};
	//#endif
	pHexDigit	+= 8;
	ReadHex(pHexDigit,"%08lx",(ULONG*)&_fPart);
	pHexDigit	+= 8;
	ReadHex(pHexDigit,"%08lx",&DataSize);
	pHexDigit	+= 8;

	/* Now read in the link data

	// rrk 2 jun 96 - this used to work, but now adding 1 to the length causes problems
	//                when the data size is divisible by four.	 */
	  //size		= DataSize ? (DataSize / 4) + 1 : 0;
	  size		= DataSize ? (DataSize / 4) : 0;
	  if (DataSize % 4) size++;
	if (DataSize)
	{
   		Data = (char*)ODNewPtr(DataSize);
		for ( index = 0, lValue = (ODULong*)Data;
			  (index < size) && lValue;
			  index++  , lValue++
			)
		{
			ReadHex(pHexDigit,"%08lx",lValue);
			pHexDigit	+= 8;
		}
		ODByteArray	*pbaTemp	= CreateByteArray(Data,DataSize);
		_fPartData	= CopyByteArrayStruct(pbaTemp);
		ODDisposePtr(Data);
		DisposeByteArray(pbaTemp);
	}
}


/* METHOD: GetMoniker
 *
 * DESCRIPTION:
 *   Return the full moniker for this link specification
 */
SOM_Scope void * SOMLINK ODLinkSpecGetMoniker(
	ODLinkSpec	*somSelf,
	Environment	*ev)
{
	ODLinkSpecData	*somThis	= ODLinkSpecGetData(somSelf);

	return _fpmkFull;
}

/* FUNCTION: ReadHex
 *
 * DESCRIPTION:
 *   Read the given text string in order to not use SSCANF in our dll ...
 */
void ReadHex(
	char 	*pString,
	char 	*pFormat,
	ULONG 	*pValue)
{
	ULONG	Result		= 0;
	UINT	ch;
	int		i;

	for (i = 0; i < 8; i++)
	{
		ch	= *(pString + i);
		if ((ch <= '9') && (ch >= '0'))
			ch 	= ch - '0';
		if ((ch <= 'F') && (ch >= 'A'))
			ch 	= ch - 'A' + 10;
		if ((ch <= 'f') && (ch >= 'a'))
			ch 	= ch - 'a' + 10;

		Result	 = Result * 0x10;
		Result	+= ch;
	}
	*pValue	= Result;
}

/* FUNCTION: ODGetCurrentProcess
 *
 * DESCRIPTION:
 *   Obtain the unique process serial number
 */
long ODGetCurrentProcess( ProcessSerialNumber *pPSN)

{
  pPSN->lowLongOfPSN  = (ODULong)GetCurrentProcessId();
  pPSN->highLongOfPSN = CoGetCurrentProcess();
  return 0;
}

/* FUNCTION: ODSameProcess
 *
 * DESCRIPTION:
 *   Return TRUE in SameProcess if these are identical process serial numbers
 */
long ODSameProcess( ProcessSerialNumber *pPSN1, ProcessSerialNumber *pPSN2, BOOL *pbSameProcess)

{
  *pbSameProcess = !(memcmp(pPSN1, pPSN2, sizeof(ProcessSerialNumber)));
  return 0;
}			 

