/****************************** Module Header ******************************\ * Module Name: le.c * * Purpose: Handles all API routines for the dde L&E sub-dll of the ole dll. * * Created: 1990 * * Copyright (c) 1990, 1991 Microsoft Corporation * * History: * Raor, srinik (../../1990,91) Designed and coded * curts created portable version for win16/32 * \***************************************************************************/ #include #include "dll.h" #define EMB_ID_INDEX 3 // index of ones digit in #000 char embStr[] = "#000"; extern HANDLE hInfo; extern OLECLIPFORMAT cfNetworkName; HANDLE GetNetNameHandle (LPOBJECT_LE); BOOL AreTopicsEqual (LPOBJECT_LE, LPOBJECT_LE); ATOM FARINTERNAL wAtomCat (ATOM, ATOM); OLEOBJECTVTBL vtblLE = { LeQueryProtocol, // check whether the speced protocol is supported LeRelease, // release LeShow, // Show LeDoVerb, // run LeGetData, LeSetData, LeSetTargetDevice, // LeSetBounds, // set viewport bounds LeEnumFormat, // returns format LeSetColorScheme, // set color scheme LeRelease, // delete LeSetHostNames, // LeSaveToStream, // write to file LeClone, // clone object LeCopyFromLink, // Create embedded from Link LeEqual, // test whether the object data is similar LeCopy, // copy to clip LeDraw, // draw the object LeActivate, // activate LeExecute, // excute the given commands LeClose, // stop LeUpdate, // Update LeReconnect, // Reconnect LeObjectConvert, // convert object to specified type LeGetUpdateOptions, // Get Link Update options LeSetUpdateOptions, // Set Link Update options ObjRename, // Change Object name ObjQueryName, // Get current object name LeQueryType, // object Type LeQueryBounds, // QueryBounds ObjQuerySize, // Find the size of the object LeQueryOpen, // Query open LeQueryOutOfDate, // query whether object is current LeQueryReleaseStatus, // returns release status LeQueryReleaseError, // assynchronusrelease error LeQueryReleaseMethod, // the method/proc which is in assynchronus // operation. LeRequestData, // requestdata LeObjectLong, // objectLong LeChangeData // change native data of existing object }; ////////////////////////////////////////////////////////////////////////////// // // OLESTATUS FAR PASCAL LeObjectLong (lpoleobj, wFlags, lpData) // // // Returns whether a given object is still processing a previous // asynchronous command. returns OLE_BUSY if the object is still // processing the previous command // // Arguments: // // lpoleobj - ole object pointer // wFlags - get, set flags // lpData - long pointer to data // // Returns: // // OLE_OK // OLE_ERROR_OBJECT // // Effects: // ////////////////////////////////////////////////////////////////////////////// OLESTATUS FARINTERNAL LeObjectLong ( LPOLEOBJECT lpoleobj, UINT wFlags, LPLONG lpData ){ LPOBJECT_LE lpobj = (LPOBJECT_LE)lpoleobj; LONG lData; Puts("LeObjectLong"); if (!FarCheckObject((LPOLEOBJECT) lpobj)) return OLE_ERROR_OBJECT; if ((lpobj->head.ctype != CT_EMBEDDED) && (lpobj->head.ctype != CT_LINK)) return OLE_ERROR_OBJECT; if (wFlags & OF_HANDLER) { lData = lpobj->lHandlerData; if (wFlags & OF_SET) lpobj->lHandlerData = *lpData; if (wFlags & OF_GET) *lpData = lData; } else { lData = lpobj->lAppData; if (wFlags & OF_SET) lpobj->lAppData = *lpData; if (wFlags & OF_GET) *lpData = lData; } return OLE_OK; } ////////////////////////////////////////////////////////////////////////////// // // OLESTATUS FAR PASCAL LeQueryReleaseStatus (lpoleobj) // // // Returns whether a given object is still processing a previous // asynchronous command. returns OLE_BUSY if the object is still // processing the previous command // // Arguments: // // lpoleobj - ole object pointer // // Returns: // // OLE_BUSY - object is busy // OLE_OK - not busy // // Effects: // ////////////////////////////////////////////////////////////////////////////// OLESTATUS FAR PASCAL LeQueryReleaseStatus (LPOLEOBJECT lpoleobj) { LPOBJECT_LE lpobj = (LPOBJECT_LE)lpoleobj; // probe async will clean up the channels // if the server died. PROBE_ASYNC (lpobj); return OLE_OK; } ////////////////////////////////////////////////////////////////////////////// // // OLESTATUS FAR PASCAL LeQueryReleaseError (lpoleobj) // // returns the errors of an asynchronous command. // // Arguments: // // lpoleobj - ole object pointer // // Returns: // // OLE_ERROR_.. - if there is any error // OLE_OK - no error // // Note: This api is typically valid only during the callback of // OLE_RELEASE. // ////////////////////////////////////////////////////////////////////////////// OLESTATUS FAR PASCAL LeQueryReleaseError (LPOLEOBJECT lpoleobj) { LPOBJECT_LE lpobj = (LPOBJECT_LE)lpoleobj; return lpobj->mainErr; } ////////////////////////////////////////////////////////////////////////////// // // OLE_RELEASE_METHOD FAR PASCAL LeQueryReleaseMethod (lpoleobj) // // returns the method/command of the asynchronous command which // resulted in the OLE_RELEASE call back. // // Arguments: // // lpoleobj - ole object pointer // // Returns: // OLE_RELEASE_METHOD // // Note: This api is typically valid only during the callback of // OLE_RELEASE. Using this api, clients can decide which previous // asynchronous command resulted in OLE_RELEASE. // ////////////////////////////////////////////////////////////////////////////// OLE_RELEASE_METHOD FAR PASCAL LeQueryReleaseMethod (LPOLEOBJECT lpoleobj) { LPOBJECT_LE lpobj = (LPOBJECT_LE)lpoleobj; return lpobj->oldasyncCmd; } ////////////////////////////////////////////////////////////////////////////// // // LPVOID FARINTERNAL LeQueryProtocol (lpoleobj, lpprotocol) // // Given an oject, returns the new object handle for the new protocol. // Does the conversion of objects from one protocol to another one. // // Arguments: // // lpoleobj - ole object pointer // lpprotocol - ptr to new protocol string // // Returns: // lpobj - New object handle // null - if the protocol is not supported. // // ////////////////////////////////////////////////////////////////////////////// LPVOID FARINTERNAL LeQueryProtocol ( LPOLEOBJECT lpoleobj, OLE_LPCSTR lpprotocol ){ LPOBJECT_LE lpobj = (LPOBJECT_LE)lpoleobj; if (lpobj->bOldLink) return NULL; if (!lstrcmp (lpprotocol, PROTOCOL_EDIT)) return lpobj; if (!lstrcmp (lpprotocol, PROTOCOL_EXECUTE)) { if (UtilQueryProtocol (lpobj, lpprotocol)) return lpobj; return NULL; } return NULL; } ////////////////////////////////////////////////////////////////////////////// // // OLESTATUS EmbLnkDelete (lpoleobj) // // Routine for the object termination/deletion. Schedules differnt // asynchronous commands depending on different conditions. // Arguments: // // Sends "StdClose" only if it is Ok to close the document. Sends // "StdExit" only if the server has to be unlaunched. Deletes the object // only if the original command is OLE_DELETE. No need to call back the // client if the deletion is internal. // // While delete, this routine is entered several times. EAIT_FOR_ASYNC_MSG // results in going back to from where it is called and the next DDE message // brings back the control to this routine. // // Arguments: // // lpobj - object pointer // // Returns: // // ////////////////////////////////////////////////////////////////////////////// OLESTATUS FARINTERNAL EmbLnkDelete (LPOBJECT_LE lpobj) { HOBJECT hobj; switch (lpobj->subRtn) { case 0: SKIP_TO (!QueryClose (lpobj), step1); // Send "StdCloseDocument" SendStdClose (lpobj); WAIT_FOR_ASYNC_MSG (lpobj); case 1: step1: SETSTEP (lpobj, 1); // End the doc conversation TermDocConv (lpobj); WAIT_FOR_ASYNC_MSG (lpobj); case 2: // delete the doc edit block. It is Ok even if the object // is not actually getting deleted. DeleteDocEdit (lpobj); // if need to unluanch the app, send stdexit. SKIP_TO (!QueryUnlaunch (lpobj), step3); SendStdExit (lpobj); WAIT_FOR_ASYNC_MSG (lpobj); case 3: step3: SETSTEP (lpobj, 3); // Do not set any errors. // Terminate the server conversation. TermSrvrConv (lpobj); WAIT_FOR_ASYNC_MSG (lpobj); case 4: // delete the server edit block DeleteSrvrEdit (lpobj); if (lpobj->asyncCmd != OLE_DELETE) { // if this delete is called because of unlauncinh of // object because of some error, no need to // call end asynchronous. It should have been already // called from somewhere else. if (lpobj->asyncCmd == OLE_SERVERUNLAUNCH){ // send the async cmd; CLEARASYNCCMD (lpobj); } else EndAsyncCmd (lpobj); return OLE_OK; } // for real delete delete the atoms and space. DeleteObjectAtoms (lpobj); if (lpobj->lpobjPict) (*lpobj->lpobjPict->lpvtbl->Delete) (lpobj->lpobjPict); if (lpobj->hnative) GlobalFree (lpobj->hnative); if (lpobj->hLink) GlobalFree (lpobj->hLink); if (lpobj->hhostNames) GlobalFree (lpobj->hhostNames); if (lpobj->htargetDevice) GlobalFree (lpobj->htargetDevice); if (lpobj->hdocDimensions) GlobalFree (lpobj->hdocDimensions); DeleteExtraData (lpobj); DocDeleteObject ((LPOLEOBJECT) lpobj); // send the async cmd; EndAsyncCmd (lpobj); if (lpobj->head.iTable != INVALID_INDEX) DecreaseHandlerObjCount (lpobj->head.iTable); hobj = lpobj->head.hobj; ASSERT (hobj, "Object handle NULL in delete") GlobalUnlock (hobj); GlobalFree (hobj); return OLE_OK; } return OLE_ERROR_GENERIC; } ////////////////////////////////////////////////////////////////////////////// // // OLESTATUS FARINTERNAL LeRelease (lpoleobj) // // Deletes the given object. This is can be asynchronous operation. // // Arguments: // // lpoleobj - ole object pointer // // Returns: // // OLE_WAIT_FOR_RELASE: If any DDE_TRANSACTIONS have been queued // OLE_OK : If deletion successfully // OLE_ERROR_... : If any error // ////////////////////////////////////////////////////////////////////////////// OLESTATUS FARINTERNAL LeRelease (LPOLEOBJECT lpoleobj) { LPOBJECT_LE lpobj = (LPOBJECT_LE) lpoleobj; // For delete allow if the object has been aborted. PROBE_ASYNC (lpobj); // reset the flags so that we do not delete the object based on the old // flags lpobj->fCmd = 0; InitAsyncCmd (lpobj, OLE_DELETE, EMBLNKDELETE); return EmbLnkDelete (lpobj); } ////////////////////////////////////////////////////////////////////////////// // // OLESTATUS FARINTERNAL LeClone (lpoleobjsrc, lpclient, lhclientdoc, lpobjname, lplpobj) // // Clones a given object. // // Arguments: // // lpoleobjsrc: ptr to the src object. // lpclient: client callback handle // lhclientdoc: doc handle // lpobjname: object name // lplpobj: holder for returning object. // // Returns: // OLE_OK : successful // OLE_ERROR_... : error // // Note: If the object being cloned is connected to the server, then // the cloned object is not connected to the server. For linked // objects, OleConnect has to be called. // // ////////////////////////////////////////////////////////////////////////////// OLESTATUS FARINTERNAL LeClone ( LPOLEOBJECT lpoleobjsrc, LPOLECLIENT lpclient, LHCLIENTDOC lhclientdoc, OLE_LPCSTR lpobjname, LPOLEOBJECT FAR * lplpoleobj ){ LPOBJECT_LE lpobjsrc = (LPOBJECT_LE) lpoleobjsrc; LPOBJECT_LE lpobj = (LPOBJECT_LE) NULL; int retval = OLE_ERROR_MEMORY; // Assumes all the creates are in order // PROBE_OBJECT_BLANK(lpobjsrc); PROBE_CREATE_ASYNC(lpobjsrc); if (!(lpobj = LeCreateBlank(lhclientdoc, (LPSTR)lpobjname, lpobjsrc->head.ctype))) goto errRtn; lpobj->head.lpclient = lpclient; lpobj->head.iTable = lpobjsrc->head.iTable; //!!! dll loading lpobj->head.lpvtbl = lpobjsrc->head.lpvtbl; // set the atoms. lpobj->app = DuplicateAtom (lpobjsrc->app); lpobj->topic = DuplicateAtom (lpobjsrc->topic); lpobj->item = DuplicateAtom (lpobjsrc->item); lpobj->aServer = DuplicateAtom (lpobjsrc->aServer); lpobj->bOleServer = lpobjsrc->bOleServer; lpobj->verb = lpobjsrc->verb; lpobj->fCmd = lpobjsrc->fCmd; lpobj->aNetName = DuplicateAtom (lpobjsrc->aNetName); lpobj->cDrive = lpobjsrc->cDrive; lpobj->dwNetInfo = lpobjsrc->dwNetInfo; if (lpobjsrc->htargetDevice) lpobj->htargetDevice = DuplicateGlobal (lpobjsrc->htargetDevice, GMEM_MOVEABLE); if (lpobjsrc->head.ctype == CT_EMBEDDED) { if (lpobjsrc->hnative) { if (!(lpobj->hnative = DuplicateGlobal (lpobjsrc->hnative, GMEM_MOVEABLE))) goto errRtn; } if (lpobjsrc->hdocDimensions) lpobj->hdocDimensions = DuplicateGlobal (lpobjsrc->hdocDimensions, GMEM_MOVEABLE); if (lpobjsrc->hlogpal) lpobj->hlogpal = DuplicateGlobal (lpobjsrc->hlogpal, GMEM_MOVEABLE); SetEmbeddedTopic (lpobj); } else { lpobj->bOldLink = lpobjsrc->bOldLink; lpobj->optUpdate = lpobjsrc->optUpdate; } retval = OLE_OK; // if picture is needed clone the picture object. if ((!lpobjsrc->lpobjPict) || ((retval = (*lpobjsrc->lpobjPict->lpvtbl->Clone)(lpobjsrc->lpobjPict, lpclient, lhclientdoc, lpobjname, (LPOLEOBJECT FAR *)&lpobj->lpobjPict)) == OLE_OK)) { SetExtents (lpobj); *lplpoleobj = (LPOLEOBJECT)lpobj; if (lpobj->lpobjPict) lpobj->lpobjPict->lpParent = (LPOLEOBJECT) lpobj; } return retval; errRtn: // This oledelete should not result in any async communication. if (lpobj) OleDelete ((LPOLEOBJECT)lpobj); return retval; } ////////////////////////////////////////////////////////////////////////////// // // OLESTATUS FARINTERNAL LeCopyFromLink (lpoleobjsrc, lpclient, lhclientdoc, lpobjname, lplpobj) // // Creates an embedded object from a lonked object. If the linked object // is not activated, then launches the server, gets the native data and // unlaunches the server. All these operations are done silently. // // Arguments: // // lpoleobjsrc: ptr to the src object. // lpclient: client callback handle // lhclientdoc: doc handle // lpobjname: object name // lplpobj: holder for returning object. // // Returns: // OLE_OK : successful // OLE_ERROR_... : error // OLE_WAITF_FOR_RELEASE : if DDE transcation is queued // // Note: Could result in asynchronous operation if there is any // DDE operaion involved in getting any data from the server. // // Also, If there is any error in getting the native data, the // client is expected delete the object after the OLE_RELEASE // call back // ////////////////////////////////////////////////////////////////////////////// OLESTATUS FARINTERNAL LeCopyFromLink ( LPOLEOBJECT lpoleobjsrc, LPOLECLIENT lpclient, LHCLIENTDOC lhclientdoc, OLE_LPCSTR lpobjname, LPOLEOBJECT FAR * lplpoleobj ){ LPOBJECT_LE lpobjsrc = (LPOBJECT_LE)lpoleobjsrc; LPOBJECT_LE lpobj; int retval; *lplpoleobj = (LPOLEOBJECT)NULL; PROBE_OLDLINK (lpobjsrc); if (lpobjsrc->head.ctype != CT_LINK) return OLE_ERROR_NOT_LINK; PROBE_ASYNC (lpobjsrc); PROBE_SVRCLOSING(lpobjsrc); if ((retval = LeClone ((LPOLEOBJECT)lpobjsrc, lpclient, lhclientdoc, lpobjname, (LPOLEOBJECT FAR *)&lpobj)) != OLE_OK) return retval; // we successfully cloned the object. if picture object has native data // then grab it and put it in LE object. otherwise activate and get native // data also. if (lpobj->lpobjPict && (*lpobj->lpobjPict->lpvtbl->EnumFormats) (lpobj->lpobjPict, 0) == cfNative){ // Now we know that the picture object is of native format, and it // means that it is a generic object. So grab the handle to native // data and put it in LE object. lpobj->hnative = ((LPOBJECT_GEN) (lpobj->lpobjPict))->hData; ((LPOBJECT_GEN) (lpobj->lpobjPict))->hData = (HANDLE)NULL; (*lpobj->lpobjPict->lpvtbl->Delete) (lpobj->lpobjPict); lpobj->lpobjPict = (LPOLEOBJECT)NULL; SetEmbeddedTopic (lpobj); *lplpoleobj = (LPOLEOBJECT)lpobj; return OLE_OK; } else { // if necessary launch, get native data and unlaunch the app. lpobj->fCmd = LN_LNKACT | ACT_REQUEST | ACT_NATIVE | (QueryOpen(lpobjsrc) ? ACT_TERMDOC : ACT_UNLAUNCH); InitAsyncCmd (lpobj, OLE_COPYFROMLNK, LNKOPENUPDATE); if ((retval = LnkOpenUpdate (lpobj)) > OLE_WAIT_FOR_RELEASE) LeRelease ((LPOLEOBJECT)lpobj); else *lplpoleobj = (LPOLEOBJECT)lpobj; return retval; // we will be changing the topic in end conversation. } } ////////////////////////////////////////////////////////////////////////////// // // OLESTATUS FARINTERNAL LeEqual (lpoleobj1, lpoleobj2) // // Checks whethere two objects are equal. Checks for equality // of links, native data and picture data. // // Arguments: // // lpoleobj1: first object // lpoleobj2: second object // // Returns: // OLE_OK : equal // OLE_ERROR_NOT_EQUAL : if not equal // OLE_ERROR_..... : any errors // // Note: If any of the objects are connectd to the servers, leequal operaion // may not make much sense because the data might be changing from the // the server // ////////////////////////////////////////////////////////////////////////////// OLESTATUS FARINTERNAL LeEqual ( LPOLEOBJECT lpoleobj1, LPOLEOBJECT lpoleobj2 ){ LPOBJECT_LE lpobj1 = (LPOBJECT_LE)lpoleobj1; LPOBJECT_LE lpobj2 = (LPOBJECT_LE)lpoleobj2; if (lpobj1->app != lpobj2->app) return OLE_ERROR_NOT_EQUAL; // type of the objects is same. Otherwise this routine won't be called if (lpobj1->head.ctype == CT_LINK) { if (AreTopicsEqual (lpobj1, lpobj2) && (lpobj1->item == lpobj2->item)) return OLE_OK; return OLE_ERROR_NOT_EQUAL; } else { ASSERT (lpobj1->head.ctype == CT_EMBEDDED, "Invalid ctype in LeEqual") if (lpobj1->item != lpobj2->item) return OLE_ERROR_NOT_EQUAL; if (CmpGlobals (lpobj1->hnative, lpobj2->hnative)) return OLE_OK; else return OLE_ERROR_NOT_EQUAL; } //### we may have to compare the picture data also } ////////////////////////////////////////////////////////////////////////////// // // OLESTATUS FARINTERNAL LeCopy (lpoleobj) // // Copies the object to the clipboard. Even for linked objects // we do not render the objectlink. It is up to the client app // to render object link // // Arguments: // // lpoleobj: object handle // // Returns: // OLE_OK : successful // OLE_ERROR_..... : any errors // // Note: Library does not open the clipboard. Client is supposed to // open the librray before this call is made // ////////////////////////////////////////////////////////////////////////////// OLESTATUS FARINTERNAL LeCopy (LPOLEOBJECT lpoleobj) { LPOBJECT_LE lpobj = (LPOBJECT_LE)lpoleobj; HANDLE hlink = (HANDLE)NULL; HANDLE hnative = (HANDLE)NULL; PROBE_OLDLINK (lpobj); // Assumes all the creates are in order // PROBE_OBJECT_BLANK(lpobj); PROBE_CREATE_ASYNC(lpobj); if (lpobj->head.ctype == CT_EMBEDDED){ if (!(hnative = DuplicateGlobal (lpobj->hnative, GMEM_MOVEABLE))) return OLE_ERROR_MEMORY; SetClipboardData (cfNative, hnative); } hlink = GetLink (lpobj); if (!(hlink = DuplicateGlobal (hlink, GMEM_MOVEABLE))) return OLE_ERROR_MEMORY; SetClipboardData (cfOwnerLink, hlink); // copy network name if it exists if (lpobj->head.ctype == CT_LINK && lpobj->aNetName) { HANDLE hNetName; if (hNetName = GetNetNameHandle (lpobj)) SetClipboardData (cfNetworkName, hNetName); } if (lpobj->lpobjPict) return (*lpobj->lpobjPict->lpvtbl->CopyToClipboard)(lpobj->lpobjPict); return OLE_OK; } ////////////////////////////////////////////////////////////////////////////// // // OLESTATUS FARINTERNAL LeQueryBounds (lpoleobj, lpRc) // // Returns the bounding rectangle of the object. Returns topleft // as zero always and the units are himetric units. // // Arguments: // // lpoleobj: object handle // // Returns: // OLE_OK : successful // OLE_ERROR_..... : any errors // // Note: Library does not open the clipboard. Client is supposed to // open the librray before this call is made // ////////////////////////////////////////////////////////////////////////////// OLESTATUS FARINTERNAL LeQueryBounds ( LPOLEOBJECT lpoleobj, LPRECT lpRc ){ LPOBJECT_LE lpobj = (LPOBJECT_LE)lpoleobj; Puts("LeQueryBounds"); // MM_HIMETRIC units lpRc->left = 0; lpRc->top = 0; lpRc->right = (int) lpobj->head.cx; lpRc->bottom = (int) lpobj->head.cy; if (lpRc->right || lpRc->bottom) return OLE_OK; if (!lpobj->lpobjPict) return OLE_ERROR_BLANK; return (*lpobj->lpobjPict->lpvtbl->QueryBounds) (lpobj->lpobjPict, lpRc); } ////////////////////////////////////////////////////////////////////////////// // // OLESTATUS FARINTERNAL LeDraw (lpoleobj, hdc, lprc, lpWrc, hdcTarget) // // Draws the object. Calls the picture object for drawing the object // // // Arguments: // // lpoleobj: source object // hdc: handle to dest dc. Could be metafile dc // lprc: rectangle into which the object should be drawn // should be in himetric units and topleft // could be nonzero. // hdctarget: Target dc for which the object should be drawn // (Ex: Draw metafile on the dest dc using the attributes // of traget dc). // // Returns: // OLE_OK : successful // OLE_ERROR_BLANK : no picture // // ////////////////////////////////////////////////////////////////////////////// OLESTATUS FARINTERNAL LeDraw ( LPOLEOBJECT lpoleobj, HDC hdc, OLE_CONST RECT FAR* lprc, OLE_CONST RECT FAR* lpWrc, HDC hdcTarget ){ LPOBJECT_LE lpobj = (LPOBJECT_LE)lpoleobj; if (lpobj->lpobjPict) return (*lpobj->lpobjPict->lpvtbl->Draw) (lpobj->lpobjPict, hdc, lprc, lpWrc, hdcTarget); return OLE_ERROR_BLANK; } ////////////////////////////////////////////////////////////////////////////// // // OLECLIPFORMAT FARINTERNAL LeEnumFormat (lpoleobj, cfFormat) // // Enumerates the object formats. // // // Arguments: // // lpoleobj : source object // cfFormat : ref fprmat // // Returns: // NULL : no more formats or if we do not understand the // given format. // // Note: Even if the object is connected, we do not enumerate all the formats // the server can render. Server protocol can render the format list // only on system channel. Object can be connected only on the doc // channel // ////////////////////////////////////////////////////////////////////////////// OLECLIPFORMAT FARINTERNAL LeEnumFormat ( LPOLEOBJECT lpoleobj, OLECLIPFORMAT cfFormat ){ LPOBJECT_LE lpobj = (LPOBJECT_LE)lpoleobj; Puts("LeEnumFormat"); ASSERT((lpobj->head.ctype == CT_LINK)||(lpobj->head.ctype == CT_EMBEDDED), "Invalid Object Type"); // switch is not used because case won't take variable argument if (cfFormat == (OLECLIPFORMAT)NULL) { if (lpobj->head.ctype == CT_EMBEDDED) return cfNative; else return (lpobj->bOldLink ? cfLink : cfObjectLink); } if (cfFormat == cfNative) { if (lpobj->head.ctype == CT_EMBEDDED) return cfOwnerLink; else return 0; } if (cfFormat == cfObjectLink) { if (lpobj->aNetName) return cfNetworkName; else cfFormat = (OLECLIPFORMAT)NULL; } else if (cfFormat == cfOwnerLink || cfFormat == cfLink || cfFormat == cfNetworkName) cfFormat = (OLECLIPFORMAT)NULL; if (lpobj->lpobjPict) return (*lpobj->lpobjPict->lpvtbl->EnumFormats) (lpobj->lpobjPict, cfFormat); return 0; } //////////////////////////////////////////////////////////////////////////////// // // // OLESTATUS FARINTERNAL LeRequestData (lpoleobj, cfFormat) // // Requests data from the server for a given format, if the server // is connected. If the server is not connected returns error. // // // Arguments: // // lpoleobj: source object // cfFormat: ref fprmat // // Returns: // OLE_WAIT_FOR_RELEASE : If the data request data is sent to // the server. // OLE_ERROR_NOT_OPEN : Server is not open for data // // Note: If the server is ready, sends request to the server. When the // the data comes back from the server OLE_DATA_READY is sent in // the callback and the client can use Getdata to get the data. // // ////////////////////////////////////////////////////////////////////////////// OLESTATUS FARINTERNAL LeRequestData ( LPOLEOBJECT lpoleobj, OLECLIPFORMAT cfFormat ){ LPOBJECT_LE lpobj = (LPOBJECT_LE)lpoleobj; // Assumes all the creates are in order PROBE_ASYNC(lpobj); PROBE_SVRCLOSING(lpobj); if (!QueryOpen (lpobj)) return OLE_ERROR_NOT_OPEN; if (cfFormat == cfOwnerLink || cfFormat == cfObjectLink) return OLE_ERROR_FORMAT; if (!(cfFormat == cfNative && lpobj->head.ctype == CT_EMBEDDED) && (cfFormat != (OLECLIPFORMAT) GetPictType (lpobj))) { DeleteExtraData (lpobj); lpobj->cfExtra = cfFormat; } InitAsyncCmd (lpobj, OLE_REQUESTDATA, REQUESTDATA); lpobj->pDocEdit->bCallLater = FALSE; return RequestData(lpobj, cfFormat); } OLESTATUS RequestData ( LPOBJECT_LE lpobj, OLECLIPFORMAT cfFormat ){ switch (lpobj->subRtn) { case 0: RequestOn (lpobj, cfFormat); WAIT_FOR_ASYNC_MSG (lpobj); case 1: ProcessErr (lpobj); return EndAsyncCmd (lpobj); default: ASSERT (TRUE, "unexpected step in Requestdata"); return OLE_ERROR_GENERIC; } } //////////////////////////////////////////////////////////////////////////////// // // // OLESTATUS FARINTERNAL LeGetData (lpoleobj, cfFormat, lphandle) // // Returns the data handle for a given format // // Arguments: // // lpoleobj: source object // cfFormat: ref fprmat // lphandle: handle return // // Returns: // NULL : no more formats or if we do not understand the // given format. // // Note: Even if the object is connected, we do not get the data from the // server. Getdata can not be used for getting data in any other // format other than the formats available with the object on // the client side. // ////////////////////////////////////////////////////////////////////////////// OLESTATUS FARINTERNAL LeGetData ( LPOLEOBJECT lpoleobj, OLECLIPFORMAT cfFormat, LPHANDLE lphandle ){ LPOBJECT_LE lpobj = (LPOBJECT_LE)lpoleobj; // Assumes all the creates are in order // PROBE_OBJECT_BLANK(lpobj); PROBE_CREATE_ASYNC(lpobj); *lphandle = (HANDLE)NULL; // The assumption made here is that the native data can be in either // LE object or picture object. if ((cfFormat == cfNative) && (lpobj->hnative)) { ASSERT ((lpobj->head.ctype == CT_EMBEDDED) || (!lpobj->lpobjPict) || ((*lpobj->lpobjPict->lpvtbl->EnumFormats) (lpobj->lpobjPict, NULL) != cfNative), "Native data at wrong Place"); *lphandle = lpobj->hnative; return OLE_OK; } if (cfFormat == cfOwnerLink && lpobj->head.ctype == CT_EMBEDDED) { if (*lphandle = GetLink (lpobj)) return OLE_OK; return OLE_ERROR_BLANK; } if ((cfFormat == cfObjectLink || cfFormat == cfLink) && lpobj->head.ctype == CT_LINK) { if (*lphandle = GetLink (lpobj)) return OLE_OK; return OLE_ERROR_BLANK; } if (cfFormat == cfNetworkName) { if (lpobj->aNetName && (*lphandle = GetNetNameHandle (lpobj))) return OLE_WARN_DELETE_DATA; return OLE_ERROR_BLANK; } if (cfFormat == (OLECLIPFORMAT)lpobj->cfExtra) { if (*lphandle = lpobj->hextraData) return OLE_OK; return OLE_ERROR_BLANK; } if (!lpobj->lpobjPict && cfFormat) return OLE_ERROR_FORMAT; return (*lpobj->lpobjPict->lpvtbl->GetData) (lpobj->lpobjPict, cfFormat, lphandle); } OLESTATUS FARINTERNAL LeQueryOutOfDate (LPOLEOBJECT lpoleobj) { UNREFERENCED_PARAMETER(lpoleobj); return OLE_OK; } //////////////////////////////////////////////////////////////////////////////// // // OLESTATUS FARINTERNAL LeObjectConvert (lpoleobj, lpprotocol, lpclient, lhclientdoc, lpobjname, lplpobj) // // Converts a given linked/embedded object to static object. // // Arguments: // lpoleobj : source object // lpprotocol : protocol // lpclient : client callback for the new object // lhclientdoc: client doc // lpobjname : object name // lplpoleobj : object return // // // Returns: // OLE_OK : successful // OLE_ERROR_.... : any errors // ////////////////////////////////////////////////////////////////////////////// OLESTATUS FARINTERNAL LeObjectConvert ( LPOLEOBJECT lpoleobj, LPCSTR lpprotocol, LPOLECLIENT lpclient, LHCLIENTDOC lhclientdoc, LPCSTR lpobjname, LPOLEOBJECT FAR * lplpoleobj ){ LPOBJECT_LE lpobj = (LPOBJECT_LE)lpoleobj; OLESTATUS retVal; PROBE_ASYNC (lpobj); *lplpoleobj = (LPOLEOBJECT)NULL; if (lstrcmp (lpprotocol, PROTOCOL_STATIC)) return OLE_ERROR_PROTOCOL; if (!lpobj->lpobjPict || ((*lpobj->lpobjPict->lpvtbl->QueryType) (lpobj->lpobjPict, NULL) == OLE_ERROR_GENERIC)) { // Either no picture object or non-standard picture object. // Create a metafile Object. HDC hMetaDC; RECT rc; HANDLE hMF = (HANDLE)NULL, hmfp = (HANDLE)NULL; LPMETAFILEPICT lpmfp; OleQueryBounds ((LPOLEOBJECT) lpobj, &rc); if (!(hMetaDC = CreateMetaFile (NULL))) goto Cleanup; MSetWindowOrg (hMetaDC, rc.left, rc.top); MSetWindowExt (hMetaDC, rc.right - rc.left, rc.bottom - rc.top); retVal = OleDraw ((LPOLEOBJECT) lpobj, hMetaDC, &rc, &rc, NULL); hMF = CloseMetaFile (hMetaDC); if ((retVal != OLE_OK) || !hMF) goto Cleanup; if (!(hmfp = GlobalAlloc (GMEM_MOVEABLE, sizeof (METAFILEPICT)))) goto Cleanup; if (!(lpmfp = (LPMETAFILEPICT) GlobalLock (hmfp))) goto Cleanup; lpmfp->hMF = hMF; lpmfp->mm = MM_ANISOTROPIC; lpmfp->xExt = rc.right - rc.left; lpmfp->yExt = rc.top - rc.bottom; GlobalUnlock (hmfp); if (*lplpoleobj = (LPOLEOBJECT) MfCreateObject (hmfp, lpclient, TRUE, lhclientdoc, lpobjname, CT_STATIC)) return OLE_OK; Cleanup: if (hMF) DeleteMetaFile (hMF); if (hmfp) GlobalFree (hmfp); return OLE_ERROR_MEMORY; } // Picture object is one of the standard objects if ((retVal = (*lpobj->lpobjPict->lpvtbl->Clone) (lpobj->lpobjPict, lpclient, lhclientdoc, lpobjname, lplpoleobj)) == OLE_OK) { (*lplpoleobj)->ctype = CT_STATIC; DocAddObject ((LPCLIENTDOC) lhclientdoc, *lplpoleobj, lpobjname); } return retVal; } // internal method used for changing picture/native data OLESTATUS FARINTERNAL LeChangeData ( LPOLEOBJECT lpoleobj, HANDLE hnative, LPOLECLIENT lpoleclient, BOOL fDelete ){ LPOBJECT_LE lpobj = (LPOBJECT_LE)lpoleobj; UNREFERENCED_PARAMETER(lpoleclient); if (!fDelete) { if (!(hnative = DuplicateGlobal (hnative, GMEM_MOVEABLE))) return OLE_ERROR_MEMORY; } // In case of a CopyFromLink, eventhough the object type is CT_LINK, the // native data should go to LE object rather than the picture object, as // we are going to change the object type to embedded after the required // data is recieved. if ((lpobj->head.ctype == CT_LINK) && (lpobj->asyncCmd != OLE_COPYFROMLNK) && (lpobj->asyncCmd != OLE_CREATEFROMFILE)) { if (lpobj->lpobjPict) return (*lpobj->lpobjPict->lpvtbl->SetData) (lpobj->lpobjPict, cfNative, hnative); } else { // It must be embedded. if (lpobj->hnative) GlobalFree (lpobj->hnative); lpobj->hnative = hnative; return OLE_OK; } GlobalFree(hnative); return OLE_ERROR_BLANK; } //////////////////////////////////////////////////////////////////////////////// // // LPOBJECT_LE FARINTERNAL LeCreateBlank (lhclientdoc, lpobjname, ctype) // // Create a blank object. Global block is used for the object and it is // locked once sucessful. Unlocking is done only while deletion. Object // is added to the corresponding doc. // // 'LE' signature is used for object validation. // // Arguments: // lhclientdoc : client doc handle // lpobjname : object name // ctype : type of object to be created // // Returns: // LPOBJECT : successful // NULL : any errors // ////////////////////////////////////////////////////////////////////////////// LPOBJECT_LE FARINTERNAL LeCreateBlank ( LHCLIENTDOC lhclientdoc, LPSTR lpobjname, LONG ctype ){ HOBJECT hobj; LPOBJECT_LE lpobj; if (!(ctype == CT_LINK || ctype == CT_EMBEDDED || ctype == CT_OLDLINK)) return NULL; if (!(hobj = GlobalAlloc (GMEM_MOVEABLE|GMEM_ZEROINIT, sizeof (OBJECT_LE)))) return NULL; if (!(lpobj = (LPOBJECT_LE) GlobalLock (hobj))) { GlobalFree (hobj); return NULL; } if (ctype == CT_OLDLINK) { ctype = CT_LINK; lpobj->bOldLink = TRUE; } lpobj->head.objId[0] = 'L'; lpobj->head.objId[1] = 'E'; lpobj->head.ctype = ctype; lpobj->head.iTable = INVALID_INDEX; lpobj->head.lpvtbl = (LPOLEOBJECTVTBL)&vtblLE; if (ctype == CT_LINK){ lpobj->optUpdate = oleupdate_always; }else { lpobj->optUpdate = oleupdate_onclose; } lpobj->head.hobj = hobj; DocAddObject ((LPCLIENTDOC) lhclientdoc, (LPOLEOBJECT) lpobj, lpobjname); return lpobj; } void FARINTERNAL SetExtents (LPOBJECT_LE lpobj) { RECT rc = {0, 0, 0, 0}; if (lpobj->lpobjPict) { if ((*lpobj->lpobjPict->lpvtbl->QueryBounds) (lpobj->lpobjPict, (LPRECT)&rc) == OLE_OK) { // Bounds are in MM_HIMETRIC units lpobj->head.cx = (LONG) (rc.right - rc.left); lpobj->head.cy = (LONG) (rc.bottom - rc.top); } return; } } //////////////////////////////////////////////////////////////////////////////// // // OLESTATUS FARINTERNAL LeSaveToStream (lpoleobj, lpstream) // // Save the object to the stream. Uses the stream functions provided // in the lpclient. // // Format: (!!! Document the fomrat here). // // // // Arguments: // lpoleobj - pointer to ole object // lpstream - pointer to stream // // Returns: // LPOBJECT : successful // NULL : any errors // ////////////////////////////////////////////////////////////////////////////// OLESTATUS FARINTERNAL LeSaveToStream ( LPOLEOBJECT lpoleobj, LPOLESTREAM lpstream ){ DWORD dwFileVer = GetFileVersion(lpoleobj); LPOBJECT_LE lpobj = (LPOBJECT_LE)lpoleobj; // PROBE_OBJECT_BLANK(lpobj); PROBE_CREATE_ASYNC(lpobj); if (lpobj->head.ctype == CT_LINK && lpobj->bOldLink) lpobj->head.ctype = CT_OLDLINK; if (PutBytes (lpstream, (LPSTR) &dwFileVer, sizeof(LONG))) return OLE_ERROR_STREAM; if (PutBytes (lpstream, (LPSTR) &lpobj->head.ctype, sizeof(LONG))) return OLE_ERROR_STREAM; if (lpobj->bOldLink) lpobj->head.ctype = CT_OLDLINK; return LeStreamWrite (lpstream, lpobj); } //////////////////////////////////////////////////////////////////////////////// // // OLESTATUS FARINTERNAL LeLoadFromStream (lpstream, lpclient, lhclientdoc, lpobjname, lplpoleobject, ctype, aClass, cfFormat) // // Create an object, loading the object from the stream. // // Arguments: // lpstream : stream table // lpclient : client callback table // lhclientdoc : Doc handle foe which the object should be created // lpobjname : Object name // lplpoleobject : object return // ctype : Type of object // aClass : class atom // cfFormat : render format // // Returns: // LPOBJECT : successful // NULL : any errors // ////////////////////////////////////////////////////////////////////////////// OLESTATUS FARINTERNAL LeLoadFromStream ( LPOLESTREAM lpstream, LPOLECLIENT lpclient, LHCLIENTDOC lhclientdoc, LPSTR lpobjname, LPOLEOBJECT FAR * lplpoleobject, LONG ctype, ATOM aClass, OLECLIPFORMAT cfFormat ){ LPOBJECT_LE lpobj = (LPOBJECT_LE)NULL; OLESTATUS retval = OLE_ERROR_STREAM; LONG type; // this not same as ctype char chVerb [80]; *lplpoleobject = (LPOLEOBJECT)NULL; if (!(lpobj = LeCreateBlank (lhclientdoc, lpobjname, ctype))) return OLE_ERROR_MEMORY; lpobj->head.lpclient = lpclient; lpobj->app = aClass; // if the entry is present, then it is lpobj->bOleServer = QueryVerb (lpobj, 0, (LPSTR)&chVerb, 80); if (LeStreamRead (lpstream, lpobj) == OLE_OK) { // Get exe name from aClass and set it as aServer SetExeAtom (lpobj); if (!GetBytes (lpstream, (LPSTR) &dwVerFromFile, sizeof(LONG))) { if (!GetBytes (lpstream, (LPSTR) &type, sizeof(LONG))) { if (type == CT_NULL) retval = OLE_OK; else if (aClass = GetAtomFromStream (lpstream)) { retval = DefLoadFromStream (lpstream, NULL, lpclient, lhclientdoc, lpobjname, (LPOLEOBJECT FAR *)&lpobj->lpobjPict, CT_PICTURE, aClass, cfFormat); } } } if (retval == OLE_OK) { SetExtents (lpobj); *lplpoleobject = (LPOLEOBJECT) lpobj; if (lpobj->lpobjPict) lpobj->lpobjPict->lpParent = (LPOLEOBJECT) lpobj; if ((lpobj->head.ctype != CT_LINK) || (!InitDocConv (lpobj, !POPUP_NETDLG)) || (lpobj->optUpdate >= oleupdate_oncall)) return OLE_OK; lpobj->fCmd = ACT_ADVISE; // If it's auto update, then get the latest data. if (lpobj->optUpdate == oleupdate_always) lpobj->fCmd |= ACT_REQUEST; FarInitAsyncCmd (lpobj, OLE_LOADFROMSTREAM, LNKOPENUPDATE); return LnkOpenUpdate (lpobj); } } // This delete will not run into async command. We did not even // even connect. OleDelete ((LPOLEOBJECT) lpobj); return OLE_ERROR_STREAM; } // OLESTATUS INTERNAL LeStreamRead ( LPOLESTREAM lpstream, LPOBJECT_LE lpobj ){ DWORD dwBytes; LPSTR lpstr; OLESTATUS retval = OLE_OK; if (!(lpobj->topic = GetAtomFromStream(lpstream)) && (lpobj->head.ctype != CT_EMBEDDED)) return OLE_ERROR_STREAM; // !!! This atom could be NULL. How do we distinguish the // error case lpobj->item = GetAtomFromStream(lpstream); if (lpobj->head.ctype == CT_EMBEDDED) { if (GetBytes (lpstream, (LPSTR) &dwBytes, sizeof(LONG))) return OLE_ERROR_STREAM; if (!(lpobj->hnative = GlobalAlloc (GMEM_MOVEABLE, dwBytes))) return OLE_ERROR_MEMORY; else if (!(lpstr = GlobalLock (lpobj->hnative))) { GlobalFree (lpobj->hnative); return OLE_ERROR_MEMORY; } else { if (GetBytes(lpstream, lpstr, dwBytes)) retval = OLE_ERROR_STREAM; GlobalUnlock (lpobj->hnative); } if (retval == OLE_OK) SetEmbeddedTopic (lpobj); } else { if (lpobj->aNetName = GetAtomFromStream (lpstream)) { if (HIWORD(dwVerFromFile) == OS_MAC) { // if it is a mac file this field will have "ZONE:MACHINE:" // string. Lets prepend this to the topic, so that server // app or user can fix the string ATOM aTemp; aTemp = wAtomCat (lpobj->aNetName, lpobj->topic); GlobalDeleteAtom (lpobj->aNetName); lpobj->aNetName = (ATOM)0; GlobalDeleteAtom (lpobj->topic); lpobj->topic = aTemp; } else SetNetDrive (lpobj); } if (HIWORD(dwVerFromFile) != OS_MAC) { if (GetBytes (lpstream, (LPSTR) &lpobj->dwNetInfo, sizeof(LONG))) return OLE_ERROR_STREAM; } if (GetBytes (lpstream, (LPSTR) &lpobj->optUpdate, sizeof(LONG))) return OLE_ERROR_STREAM; } return retval; } OLESTATUS INTERNAL LeStreamWrite ( LPOLESTREAM lpstream, LPOBJECT_LE lpobj ){ DWORD dwFileVer = GetFileVersion((LPOLEOBJECT)lpobj); LPSTR lpstr; DWORD dwBytes = 0L; LONG nullType = CT_NULL; int error; if (PutAtomIntoStream(lpstream, lpobj->app)) return OLE_ERROR_STREAM; if (lpobj->head.ctype == CT_EMBEDDED) { // we set the topic at load time, no point in saving it if (PutBytes (lpstream, (LPSTR) &dwBytes, sizeof(LONG))) return OLE_ERROR_STREAM; } else { if (PutAtomIntoStream(lpstream, lpobj->topic)) return OLE_ERROR_STREAM; } #ifdef OLD if (PutAtomIntoStream(lpstream, lpobj->topic)) return OLE_ERROR_STREAM; #endif if (PutAtomIntoStream(lpstream, lpobj->item)) return OLE_ERROR_STREAM; // !!! deal with objects > 64k if (lpobj->head.ctype == CT_EMBEDDED) { if (!lpobj->hnative) return OLE_ERROR_BLANK; // assumption low bytes are first dwBytes = (DWORD)GlobalSize (lpobj->hnative); if (PutBytes (lpstream, (LPSTR)&dwBytes, sizeof(LONG))) return OLE_ERROR_STREAM; if (!(lpstr = GlobalLock (lpobj->hnative))) return OLE_ERROR_MEMORY; error = PutBytes (lpstream, lpstr, dwBytes); GlobalUnlock (lpobj->hnative); if (error) return OLE_ERROR_STREAM; } else { if (PutAtomIntoStream(lpstream, lpobj->aNetName)) return OLE_ERROR_STREAM; if (PutBytes (lpstream, (LPSTR) &lpobj->dwNetInfo, sizeof(LONG))) return OLE_ERROR_STREAM; if (PutBytes (lpstream, (LPSTR) &lpobj->optUpdate, sizeof(LONG))) return OLE_ERROR_STREAM; } if (lpobj->lpobjPict) return (*lpobj->lpobjPict->lpvtbl->SaveToStream) (lpobj->lpobjPict, lpstream); if (PutBytes (lpstream, (LPSTR) &dwFileVer, sizeof(LONG))) return OLE_ERROR_STREAM; if (PutBytes (lpstream, (LPSTR) &nullType, sizeof(LONG))) return OLE_ERROR_STREAM; return OLE_OK; } /***************************** Public Function ****************************\ * OLESTATUS FARINTERNAL LeQueryType (lpobj, lptype) * * Effects: * * History: * Wrote it. \***************************************************************************/ OLESTATUS FARINTERNAL LeQueryType ( LPOLEOBJECT lpoleobj, LPLONG lptype ){ LPOBJECT_LE lpobj = (LPOBJECT_LE)lpoleobj; Puts("LeQueryType"); if ((lpobj->head.ctype == CT_EMBEDDED) || (lpobj->asyncCmd == OLE_COPYFROMLNK) || (lpobj->asyncCmd == OLE_CREATEFROMFILE)) *lptype = CT_EMBEDDED; else if ((lpobj->head.ctype == CT_LINK) || (lpobj->head.ctype == CT_OLDLINK)) *lptype = CT_LINK; else return OLE_ERROR_OBJECT; return OLE_OK; } // ContextCallBack: internal function. Calls callback function of // with flags. int FARINTERNAL ContextCallBack ( LPOLEOBJECT lpobj, OLE_NOTIFICATION flags ){ LPOLECLIENT lpclient; Puts("ContextCallBack"); if (!FarCheckObject(lpobj)) return FALSE; if (!(lpclient = lpobj->lpclient)) return FALSE; ASSERT (lpclient->lpvtbl->CallBack, "Client Callback ptr is NULL"); return ((*lpclient->lpvtbl->CallBack) (lpclient, flags, lpobj)); } void FARINTERNAL DeleteExtraData (LPOBJECT_LE lpobj) { if (lpobj->hextraData == (HANDLE)NULL) return; switch (lpobj->cfExtra) { case CF_BITMAP: DeleteObject (lpobj->hextraData); break; case CF_METAFILEPICT: { LPMETAFILEPICT lpmfp; if (!(lpmfp = (LPMETAFILEPICT) GlobalLock (lpobj->hextraData))) break; DeleteMetaFile (lpmfp->hMF); GlobalUnlock (lpobj->hextraData); GlobalFree (lpobj->hextraData); break; } default: GlobalFree (lpobj->hextraData); } lpobj->hextraData = (HANDLE)NULL; } void INTERNAL DeleteObjectAtoms(LPOBJECT_LE lpobj) { if (lpobj->app) { GlobalDeleteAtom (lpobj->app); lpobj->app = (ATOM)0; } if (lpobj->topic) { GlobalDeleteAtom (lpobj->topic); lpobj->topic = (ATOM)0; } if (lpobj->item) { GlobalDeleteAtom (lpobj->item); lpobj->item = (ATOM)0; } if (lpobj->aServer) { GlobalDeleteAtom (lpobj->aServer); lpobj->aServer = (ATOM)0; } if (lpobj->aNetName) { GlobalDeleteAtom (lpobj->aNetName); lpobj->aNetName = (ATOM)0; } } // LeGetUpdateOptions: Gets the update options. OLESTATUS FARINTERNAL LeGetUpdateOptions ( LPOLEOBJECT lpoleobj, OLEOPT_UPDATE FAR *lpOptions ){ LPOBJECT_LE lpobj = (LPOBJECT_LE)lpoleobj; if (lpobj->head.ctype != CT_LINK) return OLE_ERROR_OBJECT; *lpOptions = lpobj->optUpdate; return OLE_OK; } OLESTATUS FARINTERNAL LnkPaste ( LPOLECLIENT lpclient, LHCLIENTDOC lhclientdoc, LPSTR lpobjname, LPOLEOBJECT FAR * lplpoleobject, OLEOPT_RENDER optRender, OLECLIPFORMAT cfFormat, OLECLIPFORMAT sfFormat ){ LPOBJECT_LE lpobj = NULL; OLESTATUS retval = OLE_ERROR_MEMORY; LPSTR lpClass = NULL; if (!(lpobj = LeCreateBlank (lhclientdoc, lpobjname, CT_LINK))) goto errRtn; lpobj->head.lpclient = lpclient; #ifdef OLD if (!bWLO) { // we are not running under WLO if (!(hInfo = GetClipboardData (sfFormat))) { if (hInfo = GetClipboardData (cfLink)) lpobj->bOldLink = TRUE; } } #endif if (!hInfo) goto errRtn; if (!IsClipboardFormatAvailable (sfFormat)) lpobj->bOldLink = TRUE; if (!SetLink (lpobj, hInfo, &lpClass)) goto errRtn; if ((retval = SetNetName(lpobj)) != OLE_OK) { // see whether network name is on the clipboard and try to use it HANDLE hNetName; LPSTR lpNetName; if (!IsClipboardFormatAvailable (cfNetworkName)) goto errRtn; if (!(hNetName = GetClipboardData (cfNetworkName))) goto errRtn; if (!(lpNetName = GlobalLock (hNetName))) goto errRtn; GlobalUnlock (hNetName); if (!(lpobj->aNetName = GlobalAddAtom (lpNetName))) goto errRtn; SetNetDrive (lpobj); } retval = CreatePictFromClip (lpclient, lhclientdoc, lpobjname, (LPOLEOBJECT FAR *)&lpobj->lpobjPict, optRender, cfFormat, lpClass, CT_PICTURE); if (retval == OLE_OK) { SetExtents (lpobj); // why do we have to update the link, do we show it? // Reconnect if we could and advise for updates *lplpoleobject = (LPOLEOBJECT)lpobj; if (lpobj->lpobjPict) lpobj->lpobjPict->lpParent = (LPOLEOBJECT) lpobj; if (!InitDocConv (lpobj, !POPUP_NETDLG)) return OLE_OK; // document is not loaded , it is OK. lpobj->fCmd = ACT_ADVISE | ACT_REQUEST; FarInitAsyncCmd (lpobj, OLE_LNKPASTE, LNKOPENUPDATE); return LnkOpenUpdate (lpobj); } else { errRtn: if (lpobj) OleDelete ((LPOLEOBJECT)lpobj); } return retval; } // !!! EmbPaste and LnkPaste Can be combined OLESTATUS FARINTERNAL EmbPaste ( LPOLECLIENT lpclient, LHCLIENTDOC lhclientdoc, LPSTR lpobjname, LPOLEOBJECT FAR * lplpoleobject, OLEOPT_RENDER optRender, OLECLIPFORMAT cfFormat ){ LPOBJECT_LE lpobj = NULL; HANDLE hnative; OLESTATUS retval = OLE_ERROR_MEMORY; LPSTR lpClass = NULL; if (!IsClipboardFormatAvailable (cfOwnerLink)) return OLE_ERROR_CLIPBOARD; if (!(lpobj = LeCreateBlank (lhclientdoc, lpobjname, CT_EMBEDDED))) goto errRtn; lpobj->head.lpclient = lpclient; #ifdef OLD if (!bWLO) { // we are not running under WLO hInfo = GetClipboardData (cfOwnerLink); } #endif if (!hInfo) goto errRtn; if (!SetLink (lpobj, hInfo, &lpClass)) goto errRtn; SetEmbeddedTopic (lpobj); hnative = GetClipboardData (cfNative); if (!(lpobj->hnative = DuplicateGlobal (hnative, GMEM_MOVEABLE))) goto errRtn; retval = CreatePictFromClip (lpclient, lhclientdoc, lpobjname, (LPOLEOBJECT FAR *)&lpobj->lpobjPict, optRender, cfFormat, lpClass, CT_PICTURE); if (retval == OLE_OK) { SetExtents (lpobj); *lplpoleobject = (LPOLEOBJECT) lpobj; if (lpobj->lpobjPict) lpobj->lpobjPict->lpParent = (LPOLEOBJECT) lpobj; } else { errRtn: // Note: This oledelete should not result in any async commands. if (lpobj) OleDelete ((LPOLEOBJECT)lpobj); } #ifdef EXCEL_BUG // Some server apps (ex: Excel) copy picture (to clipboard) which is // formatted for printer DC. So, we want to update the picture if the // server app is running, and the it's a old server if ((retval == OLE_OK) && (!lpobj->bOleServer)) { lpobj->fCmd = LN_EMBACT | ACT_NOLAUNCH | ACT_REQUEST | ACT_UNLAUNCH; FarInitAsyncCmd (lpobj, OLE_EMBPASTE, EMBOPENUPDATE); if ((retval = EmbOpenUpdate (lpobj)) > OLE_WAIT_FOR_RELEASE) return OLE_OK; } #endif return retval; } BOOL INTERNAL SetLink ( LPOBJECT_LE lpobj, HANDLE hinfo, LPSTR FAR * lpLpClass ){ LPSTR lpinfo; char chVerb[80]; // If there exits a conversation, then terminate it. if (!(lpinfo = GlobalLock (hinfo))) return FALSE; *lpLpClass = lpinfo; lpobj->app = GlobalAddAtom (lpinfo); SetExeAtom (lpobj); lpobj->bOleServer = QueryVerb (lpobj, 0, (LPSTR)&chVerb, 80); // lpobj->aServer = GetAppAtom (lpinfo); lpinfo += lstrlen (lpinfo) + 1; lpobj->topic = GlobalAddAtom (lpinfo); lpinfo += lstrlen (lpinfo) + 1; if (*lpinfo) lpobj->item = GlobalAddAtom (lpinfo); else lpobj->item = (ATOM)0; if (lpobj->hLink) { // As the atoms have already changed, GlobalFree (lpobj->hLink); // lpobj->hLink becomes irrelevant. lpobj->hLink = NULL; } if (lpinfo) GlobalUnlock(hinfo); if (!lpobj->app) return FALSE; if (!lpobj->topic && (lpobj->head.ctype == CT_LINK)) return FALSE; lpobj->hLink = DuplicateGlobal (hinfo, GMEM_MOVEABLE); return TRUE; } HANDLE INTERNAL GetLink (LPOBJECT_LE lpobj) { HANDLE hLink = NULL; LPSTR lpLink; int len; WORD size; if (lpobj->hLink) return lpobj->hLink; size = 4; // three nulls and one null at the end size += (WORD)GlobalGetAtomLen (lpobj->app); size += (WORD)GlobalGetAtomLen (lpobj->topic); size += (WORD)GlobalGetAtomLen (lpobj->item); if (!(hLink = GlobalAlloc (GMEM_MOVEABLE, (DWORD) size))) return NULL; if (!(lpLink = GlobalLock (hLink))) { GlobalFree (hLink); return NULL; } len = (int) GlobalGetAtomName (lpobj->app, lpLink, size); lpLink += ++len; len = (int) GlobalGetAtomName (lpobj->topic, lpLink, (size -= (WORD)len)); lpLink += ++len; if (!lpobj->item) *lpLink = '\0'; else { len = (int) GlobalGetAtomName (lpobj->item, lpLink, size - len); lpLink += len; } *++lpLink = '\0'; // put another null the end GlobalUnlock (hLink); return (lpobj->hLink = hLink); } void FARINTERNAL SetEmbeddedTopic (LPOBJECT_LE lpobj) { LPCLIENTDOC lpdoc; char buf[MAX_STR]; char buf1[MAX_STR]; LPSTR lpstr, lptmp; int len; if (lpobj->topic) GlobalDeleteAtom (lpobj->topic); if (lpobj->aNetName) { GlobalDeleteAtom (lpobj->aNetName); lpobj->aNetName = (ATOM)0; } lpobj->cDrive = '\0'; lpobj->dwNetInfo = 0; lpobj->head.ctype = CT_EMBEDDED; lpdoc = (LPCLIENTDOC) lpobj->head.lhclientdoc; lpstr = (LPSTR) buf; lptmp = (LPSTR) buf1; ASSERT(lpdoc->aDoc, "lpdoc->aDoc is null"); if (!GlobalGetAtomName (lpdoc->aDoc, lpstr, sizeof(buf))) goto fail; // strip the path lpstr += (len = lstrlen(lpstr)); while (--lpstr != (LPSTR) buf) { if ((*lpstr == '\\') || (*lpstr == ':')) { lpstr++; break; } } if (!(len = GlobalGetAtomName (lpdoc->aClass, lptmp, sizeof(buf1)))) goto fail; if (lstrlen(lpstr) + 2 + len < MAX_STR - 1) { lstrcat (lptmp, "%"); lstrcat (lptmp, lpstr); lstrcat (lptmp, "%"); } lpstr = lptmp; lptmp += lstrlen (lptmp); if (lpobj->head.aObjName) { if (!GlobalGetAtomName (lpobj->head.aObjName, lptmp, sizeof(buf1)-lstrlen(lpstr)-1)) goto fail; } if ((embStr[EMB_ID_INDEX] += 1) > '9') { embStr[EMB_ID_INDEX] = '0'; if ((embStr[EMB_ID_INDEX - 1] += 1) > '9') { embStr[EMB_ID_INDEX - 1] = '0'; if ((embStr[EMB_ID_INDEX - 2] += 1) > '9') embStr[EMB_ID_INDEX - 2] = '0'; } } if (lstrlen(lpstr) - lstrlen(embStr) < MAX_STR - 1) lstrcat (lptmp, embStr); else goto fail; lpobj->topic = GlobalAddAtom (lpstr); goto end; fail: lpobj->topic = (ATOM)0; // Topic, item have changed, lpobj->hLink is out of date. end: if (lpobj->hLink) { GlobalFree (lpobj->hLink); lpobj->hLink = NULL; } } ///////////////////////////////////////////////////////////////////// // // // Routines related to the asynchronous processing. // // // ///////////////////////////////////////////////////////////////////// void NextAsyncCmd ( LPOBJECT_LE lpobj, UINT mainRtn ){ lpobj->mainRtn = mainRtn; lpobj->subRtn = 0; } void InitAsyncCmd ( LPOBJECT_LE lpobj, UINT cmd, UINT mainRtn ){ lpobj->asyncCmd = cmd; lpobj->mainErr = OLE_OK; lpobj->mainRtn = mainRtn; lpobj->subRtn = 0; lpobj->subErr = 0; lpobj->bAsync = 0; lpobj->endAsync = 0; lpobj->errHint = 0; } OLESTATUS EndAsyncCmd (LPOBJECT_LE lpobj) { OLESTATUS olderr; if (!lpobj->endAsync) { lpobj->asyncCmd = OLE_NONE; return OLE_OK; } // this is an asynchronous operation. Send callback with or without // error. switch (lpobj->asyncCmd) { case OLE_DELETE: break; case OLE_COPYFROMLNK: case OLE_CREATEFROMFILE: // change the topic name to embedded. SetEmbeddedTopic (lpobj); break; case OLE_LOADFROMSTREAM: case OLE_LNKPASTE: case OLE_RUN: case OLE_SHOW: case OLE_ACTIVATE: case OLE_UPDATE: case OLE_CLOSE: case OLE_RECONNECT: case OLE_CREATELINKFROMFILE: case OLE_CREATEINVISIBLE: case OLE_CREATE: case OLE_CREATEFROMTEMPLATE: case OLE_SETUPDATEOPTIONS: case OLE_SERVERUNLAUNCH: case OLE_SETDATA: case OLE_REQUESTDATA: case OLE_OTHER: break; case OLE_EMBPASTE: lpobj->mainErr = OLE_OK; break; default: DEBUG_OUT ("unexpected maincmd", 0); break; } lpobj->bAsync = FALSE; lpobj->endAsync = FALSE; lpobj->oldasyncCmd = lpobj->asyncCmd; olderr = lpobj->mainErr; lpobj->asyncCmd = OLE_NONE; // no async command in progress. if (lpobj->head.lpclient) ContextCallBack ((LPOLEOBJECT)lpobj, OLE_RELEASE); lpobj->mainErr = OLE_OK; return olderr; } BOOL ProcessErr (LPOBJECT_LE lpobj) { if (lpobj->subErr == OLE_OK) return FALSE; if (lpobj->mainErr == OLE_OK) lpobj->mainErr = lpobj->subErr; lpobj->subErr = OLE_OK; return TRUE; } void ScheduleAsyncCmd (LPOBJECT_LE lpobj) { // replacs this with direct proc jump later on. lpobj->bAsync = FALSE; // if the object is active and we do pokes we go thru this path // !!! We may have to go thru the endasynccmd. if ((lpobj->asyncCmd == OLE_OTHER) || ((lpobj->asyncCmd == OLE_SETDATA) && !lpobj->mainRtn)) { lpobj->endAsync = TRUE; lpobj->mainErr = lpobj->subErr; EndAsyncCmd (lpobj); if (lpobj->bUnlaunchLater) { lpobj->bUnlaunchLater = FALSE; CallEmbLnkDelete(lpobj); } return; } switch (lpobj->mainRtn) { case EMBLNKDELETE: EmbLnkDelete (lpobj); break; case LNKOPENUPDATE: LnkOpenUpdate (lpobj); break; case DOCSHOW: DocShow (lpobj); break; case EMBOPENUPDATE: EmbOpenUpdate (lpobj); break; case EMBLNKCLOSE: EmbLnkClose (lpobj); break; case LNKSETUPDATEOPTIONS: LnkSetUpdateOptions (lpobj); break; case LNKCHANGELNK: LnkChangeLnk (lpobj); break; case REQUESTDATA: RequestData (lpobj, 0); break; default: DEBUG_OUT ("Unexpected asyn command", 0); break; } return; } void SetNetDrive (LPOBJECT_LE lpobj) { char buf[MAX_STR]; if (GlobalGetAtomName (lpobj->topic, buf, sizeof(buf)) && (buf[1] == ':')) { AnsiUpperBuff ((LPSTR) buf, 1); lpobj->cDrive = buf[0]; } } HANDLE GetNetNameHandle (LPOBJECT_LE lpobj) { HANDLE hNetName; LPSTR lpNetName; int size; if (!(size = GlobalGetAtomLen (lpobj->aNetName))) return NULL; size++; if (!(hNetName = GlobalAlloc (GMEM_MOVEABLE, (DWORD) size))) return NULL; if (lpNetName = GlobalLock (hNetName)) { GlobalUnlock (hNetName); if (GlobalGetAtomName(lpobj->aNetName, lpNetName, size)) return hNetName; } // error case GlobalFree (hNetName); return NULL; } BOOL AreTopicsEqual ( LPOBJECT_LE lpobj1, LPOBJECT_LE lpobj2 ){ char buf1[MAX_STR]; char buf2[MAX_STR]; if (lpobj1->aNetName != lpobj2->aNetName) return FALSE; if (!lpobj1->aNetName) { if (lpobj1->topic == lpobj2->topic) return TRUE; return FALSE; } if (!GlobalGetAtomName (lpobj1->topic, buf1, MAX_STR)) return FALSE; if (!GlobalGetAtomName (lpobj2->topic, buf2, MAX_STR)) return FALSE; if (!lstrcmpi (&buf1[1], &buf2[1])) return TRUE; return FALSE; } ATOM FARINTERNAL wAtomCat ( ATOM a1, ATOM a2 ){ char buf[MAX_STR+MAX_STR]; LPSTR lpBuf = (LPSTR)buf; if (!GlobalGetAtomName (a1, lpBuf, MAX_STR+MAX_STR)) return (ATOM)0; lpBuf += lstrlen(lpBuf); if (!GlobalGetAtomName(a2, lpBuf, MAX_STR)) return (ATOM)0; return GlobalAddAtom ((LPSTR) buf); }