/****************************** Module Header ******************************\ * Module Name: DDE.C (Extensible Compound Documents -DDE) * * Copyright (c) 1985 - 1991 Microsoft Corporation * * PURPOSE: Handles all API routines for the dde sub-dll of the ole dll. * * History: * Raor,Srinik (../../90,91) Designed and coded * curts created portable version for WIN16/32 * \***************************************************************************/ #include #include "dde.h" #include "dll.h" #include "strsafe.h" /* #define GRAPHBUG */ // ### may not need seperate wndproc for system topic! HANDLE GetDDEDataHandle (DDEDATA FAR *, UINT, HANDLE); extern ATOM aSystem; extern ATOM aOle; extern HANDLE hInstDLL; // DocWndProc: Window procedure used to document DDE conversations LRESULT FAR PASCAL DocWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ){ PEDIT_DDE pedit = NULL; LPOBJECT_LE lpobj = NULL; Puts("DocWndProc"); if (lpobj = (LPOBJECT_LE) GetWindowLongPtr (hwnd, 0)) { pedit = lpobj->pDocEdit; } else { // Can't cope so just pass this message on DEBUG_OUT ("SYS: doc conv block missing",0); return DefWindowProc (hwnd, message, wParam, lParam); } switch (message){ case WM_DDE_ACK: DEBUG_OUT ("WM_DDE_ACK ", 0); if (pedit->bTerminating){ // ### this error recovery may not be correct. DEBUG_OUT ("No action due to termination process",0) break; } switch(pedit->awaitAck){ case AA_INITIATE: HandleAckInitMsg (pedit, (HWND)wParam); if (LOWORD(lParam)) GlobalDeleteAtom (LOWORD(lParam)); if (HIWORD(lParam)) GlobalDeleteAtom (HIWORD(lParam)); break; case AA_REQUEST: Puts("Request"); HandleAck (lpobj, pedit, wParam, lParam); break; case AA_UNADVISE: Puts("Unadvise"); HandleAck (lpobj, pedit, wParam, lParam); break; case AA_EXECUTE: Puts("Execute"); HandleAck (lpobj, pedit, wParam, lParam); break; case AA_ADVISE: Puts("Advise"); HandleAck (lpobj, pedit, wParam, lParam); break; case AA_POKE: // freeing pokedata is done in handleack Puts("Poke"); HandleAck (lpobj, pedit, wParam, lParam); break; default: DEBUG_OUT ("received ACK We don't know how to handle ",0) break; } // end of switch break; case WM_TIMER: HandleTimerMsg (lpobj, pedit); break; case WM_DDE_DATA: DEBUG_OUT ("WM_DDE_DATA",0); HandleDataMsg (lpobj, GET_WM_DDE_DATA_HDATA(wParam,lParam), GET_WM_DDE_DATA_ITEM(wParam,lParam)); DDEFREE(message, lParam); break; case WM_DDE_TERMINATE: DEBUG_OUT ("WM_DDE_TERMINATE",0); HandleTermMsg (lpobj, pedit, (HWND)wParam, TRUE); break; case WM_DESTROY: DEBUG_OUT ("Client window being destroyed", 0) pedit->hClient = NULL; break; default: return DefWindowProc (hwnd, message, wParam, lParam); } return 0L; } // SrvrWndProc: Window Procedure for System Topic DDE conversations // wndproc for system topic LRESULT FAR PASCAL SrvrWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ){ PEDIT_DDE pedit = NULL; LPOBJECT_LE lpobj = NULL; Puts("SysWndProc"); if (lpobj = (LPOBJECT_LE) GetWindowLongPtr (hwnd, 0)) { pedit = lpobj->pSysEdit; } else { // Can't cope so just pass this message on DEBUG_OUT ("SYS: conv edit block missing",0); return DefWindowProc (hwnd, message, wParam, lParam); } switch (message){ case WM_DDE_ACK: DEBUG_OUT ("SYS: WM_DDE_ACK",0); if(pedit->bTerminating){ //### Error recovery may not be OK. DEBUG_OUT ("No action due to termination process",0) break; } switch (pedit->awaitAck) { case AA_INITIATE: #ifdef HISTORY if (GETWINDOWUINT((HWND)wParam, GWW_HINSTANCE) == pedit->hInst || IsSrvrDLLwnd ((HWND)wParam, pedit->hInst)) { // For exact instance match or for // DLL instance match, keep the new one pedit->hServer = (HWND)wParam; } else { ++pedit->extraTerm; // This post directly is alright since we are // terminating extra initiates. PostMessage ((HWND)wParam, WM_DDE_TERMINATE, hwnd, 0); } #else HandleAckInitMsg (pedit, (HWND)wParam); #endif if (LOWORD(lParam)) GlobalDeleteAtom (LOWORD(lParam)); if (HIWORD(lParam)) GlobalDeleteAtom (HIWORD(lParam)); break; case AA_EXECUTE: HandleAck(lpobj, pedit, wParam, lParam); break; default: DEBUG_OUT ("received ACK We don't know how to handle ",0) break; } break; case WM_TIMER: HandleTimerMsg (lpobj, pedit); break; case WM_DDE_TERMINATE: HandleTermMsg (lpobj, pedit, (HWND)wParam, FALSE); break; case WM_DESTROY: DEBUG_OUT ("destroy window for the sys connection", 0); pedit->hClient = NULL; break; default: return DefWindowProc (hwnd, message, wParam, lParam); } return 0L; } void INTERNAL HandleTimerMsg ( LPOBJECT_LE lpobj, PEDIT_DDE pedit ){ // Since there is only one timer for each client, just // repost the message and delete the timer. KillTimer (pedit->hClient, 1); pedit->wTimer = 0; if (PostMessageToServer(pedit, pedit->msg, pedit->lParam)) return ; // return something. // Postmessage failed. We need to getback to the main stream of // commands for the object. HandleAck (lpobj, pedit, (WPARAM)NULL, pedit->lParam); return ; } void INTERNAL HandleTermMsg (lpobj, pedit, hwndPost, bDoc) LPOBJECT_LE lpobj; PEDIT_DDE pedit; HWND hwndPost; BOOL bDoc; { UINT asyncCmd; BOOL bBusy; if (pedit->hServer != hwndPost){ DEBUG_OUT ("Got terminate for extra conversation",0) if (--pedit->extraTerm == 0 && pedit->bTerminating) ScheduleAsyncCmd (lpobj); return; } if (!pedit->bTerminating){ // If we are waiting for any ack, then goto next step with error // delete any data if we were in busy mode. bBusy = DeleteBusyData (lpobj, pedit); asyncCmd = lpobj->asyncCmd; PostMessageToServer(pedit, WM_DDE_TERMINATE, 0); pedit->hServer = NULL; if (pedit->awaitAck || bBusy) { // Set error and goto next step. lpobj->subErr = OLE_ERROR_COMM; pedit->awaitAck = 0; ScheduleAsyncCmd (lpobj); } // If the command is delete, do not delete // the edit blocks. It will be deleted // in the OleLnkDelete routine and for delete it is // possible that by the time we come here, the object // may not exist at all. if (asyncCmd != OLE_DELETE){ // QueryOpen() is done because excel is sending WM_DDE_TERMINATE // for system without sending for doc in case of failure. if (bDoc || QueryOpen (lpobj)) { // if the termination is for document and no async command // terminate the server conversation also. if ((asyncCmd == OLE_NONE) || (asyncCmd == OLE_REQUESTDATA) || (asyncCmd == OLE_OTHER) || (asyncCmd == OLE_SETDATA) || (asyncCmd == OLE_RUN) || (asyncCmd == OLE_SHOW) || (asyncCmd == OLE_SETUPDATEOPTIONS)) { if (lpobj->pDocEdit && lpobj->pDocEdit->awaitAck) // we are waiting for an ack on Doc channel. So start // the unlaunch process after we get the ack. lpobj->bUnlaunchLater = TRUE; else CallEmbLnkDelete (lpobj); } else { if (bDoc) DeleteDocEdit (lpobj); } }else DeleteSrvrEdit (lpobj); } } else { pedit->hServer = NULL; if (pedit->extraTerm == 0) ScheduleAsyncCmd (lpobj); } } // HandleAckInitMsg: Handles WM_DDE_ACKs received while in initiate state. If // this is the first reply, save its window handle. If multiple replies // are received, take the one with the prefered instance, if there is // one. Keep a count of WM_DDE_TERMINATEs we send so that we don't shut // the window until we get all of the responses for WM_DDE_TERMINATEs. void INTERNAL HandleAckInitMsg ( PEDIT_DDE pedit, HWND hserver ){ Puts("HandleAckInitMsg"); if (pedit->hServer){ // just take the very first one. Direct post is OK PostMessage (hserver, WM_DDE_TERMINATE, (WPARAM)pedit->hClient, 0); ++pedit->extraTerm; } else pedit->hServer = hserver; } // HandleAck: returns 0 if is not positive, else non-0. Should probably be // a macro. BOOL INTERNAL HandleAck ( LPOBJECT_LE lpobj, PEDIT_DDE pedit, WPARAM wParam, LPARAM lParam ){ WORD wStatus = GET_WM_DDE_ACK_STATUS(wParam,lParam); HANDLE hData = NULL; BOOL retval = TRUE; UNREFERENCED_PARAMETER(wParam); // check for busy bit if ((wStatus & 0x4000) && ContextCallBack ((LPOLEOBJECT)lpobj, OLE_QUERY_RETRY)){ // we got busy from the server. create a timer and wait for time out. // We do not need makeprocinstance since, DLLs are single insance, all // we need to do is export for this function. if ((pedit->wTimer = SetTimer (pedit->hClient, 1, 3000, NULL))) return TRUE; } // even if the client got terminate we have to go thru this path. if (pedit->wTimer) { KillTimer (pedit->hClient, 1); pedit->wTimer = 0; } if (pedit->awaitAck == AA_POKE) // We have to free the data first. Handleack can trigger // another Poke (like pokehostnames) FreePokeData (lpobj, pedit); if (pedit->awaitAck == AA_EXECUTE) { hData = GET_WM_DDE_EXECACK_HDATA(wParam,lParam); if (hData) GlobalFree (hData); } else { ATOM aItem = GET_WM_DDE_ACK_ITEM(wParam,lParam); ASSERT (CheckAtomValid(aItem),"Invalid atom in ACK") if (aItem) GlobalDeleteAtom (aItem); } if (!(wStatus & 0x8000)) { // error case. set the error DEBUG_OUT ("DDE ACK with failure", 0) if (lpobj->errHint){ lpobj->subErr = lpobj->errHint; lpobj->errHint = OLE_OK; } else lpobj->subErr = OLE_ERROR_COMM; retval = FALSE; if (pedit->awaitAck == AA_ADVISE) { #ifdef ASSERT ASSERT (pedit->hopt, "failed advise, options block missing"); #endif GlobalFree (pedit->hopt); } } pedit->hopt = NULL; pedit->awaitAck = 0; ScheduleAsyncCmd (lpobj); return retval; } // HandleDataMsg: Called for WM_DDE_DATA message. If data is from an // ADVISE-ON-CLOSE and this is there are no more outstanding // ADVISE-ON-CLOSE requests, close the document and end the // conversation. void INTERNAL HandleDataMsg ( LPOBJECT_LE lpobj, HANDLE hdata, ATOM aItem ){ DDEDATA far *lpdata = NULL; BOOL fAck; BOOL fRelease; int options; PEDIT_DDE pedit; Puts("HandleDataMsg"); if (ScanItemOptions (aItem, (int far *)&options) != OLE_OK) { DEBUG_OUT (FALSE, "Improper item options"); return; } pedit = lpobj->pDocEdit; if (hdata) { if (!(lpdata = (DDEDATA FAR *) GlobalLock(hdata))) return; fAck = lpdata->fAckReq; fRelease = lpdata->fRelease; if (pedit->bTerminating) { DEBUG_OUT ("Got DDE_DATA in terminate sequence",0) fRelease = TRUE; } else { if ((OLECLIPFORMAT)lpdata->cfFormat == cfBinary && aItem == aStdDocName) { ChangeDocName (lpobj, (LPSTR)lpdata->Value); } else SetData (lpobj, hdata, options); // important that we post the acknowledge first. Otherwist the // messages are not in sync. if (fAck) { LPARAM lparamNew = MAKE_DDE_LPARAM(WM_DDE_ACK,POSITIVE_ACK,aItem); PostMessageToServer (pedit, WM_DDE_ACK, lparamNew); } else if (aItem) GlobalDeleteAtom (aItem); if ((lpdata->fResponse) && (pedit->awaitAck == AA_REQUEST)) { // we sent the request. So, schedule next step. pedit->awaitAck = 0; ScheduleAsyncCmd (lpobj); } } GlobalUnlock (hdata); if (fRelease) GlobalFree (hdata); } else { if (CanCallback (lpobj, options)) { if (options != OLE_CLOSED) ContextCallBack ((LPOLEOBJECT)lpobj, options); else lpobj->bSvrClosing = FALSE; } } // PREfix COM+ 27104 // The call to ScheduleAsyncCmd above can cause EmbLnkDelete to be called. // In that case lpobj->pDocEdit has already been deleted, and we don't need // to start another EmbLinkDelete. if ((options == OLE_CLOSED) && (lpobj->pDocEdit)) { if ((lpobj->pDocEdit->nAdviseClose <= 2) && (lpobj->asyncCmd == OLE_NONE)) { InitAsyncCmd (lpobj, OLE_SERVERUNLAUNCH, EMBLNKDELETE); EmbLnkDelete (lpobj); } } } HANDLE GetDDEDataHandle ( DDEDATA far *lpdata, UINT cfFormat, HANDLE hdata ){ if (cfFormat == CF_METAFILEPICT) { #ifdef _WIN64 return (*(void* _unaligned*)lpdata->Value); #else return LongToHandle(*(LONG*)lpdata->Value); #endif } if (cfFormat == CF_BITMAP || cfFormat == CF_ENHMETAFILE) return LongToHandle(*(LONG*)lpdata->Value); if (cfFormat == CF_DIB) return GlobalReAlloc (LongToHandle(*(LONG*)lpdata->Value), 0L, GMEM_MODIFY|GMEM_SHARE); return CopyData (((LPSTR)lpdata)+4, (DWORD)(GlobalSize (hdata) - 4)); } // SetData: Given the DDEDATA structure from a WM_DDE_DATA message, set up the // appropriate data in lpobj. If the native is in native format, add // that field, otherwise, if it is in picture format, ask the picture // to add it itself. void INTERNAL SetData ( LPOBJECT_LE lpobj, HANDLE hdata, int options ){ DDEDATA far *lpdata = NULL; OLESTATUS retVal = OLE_ERROR_MEMORY; HANDLE hdataDDE; Puts("SetData"); if (!(lpdata = (DDEDATA far *) (GlobalLock (hdata)))) goto errrtn; if (!(hdataDDE = GetDDEDataHandle (lpdata, lpdata->cfFormat, hdata))) goto errrtn; if ((OLECLIPFORMAT)lpdata->cfFormat == cfNative) { retVal = (*lpobj->head.lpvtbl->ChangeData) ( (LPOLEOBJECT)lpobj, hdataDDE, lpobj->head.lpclient, TRUE); // use this data, don't copy } else if ((BOOL)lpdata->cfFormat && (lpdata->cfFormat == (int)GetPictType (lpobj))) { retVal = (*lpobj->lpobjPict->lpvtbl->ChangeData) (lpobj->lpobjPict, hdataDDE, lpobj->head.lpclient, lpdata->fRelease); } else { // case of extra data in the object. DeleteExtraData (lpobj); lpobj->cfExtra = lpdata->cfFormat; lpobj->hextraData = hdataDDE; goto end; } if (retVal == OLE_OK) { SetExtents (lpobj); if (CanCallback (lpobj, options)) { if (options == OLE_CLOSED) { ContextCallBack ((LPOLEOBJECT)lpobj, OLE_CHANGED); ContextCallBack ((LPOLEOBJECT)lpobj, OLE_CLOSED); lpobj->bSvrClosing = FALSE; } else ContextCallBack ((LPOLEOBJECT)lpobj, options); } } end: errrtn: if (lpdata) GlobalUnlock (hdata); return; } // SysStartConvDDE: Starts a system conversation. Returns a handle to that // conversation, or NULL. BOOL INTERNAL InitSrvrConv ( LPOBJECT_LE lpobj, HANDLE hInst ){ HANDLE hedit = NULL; PEDIT_DDE pedit = NULL; Puts("InitSrvrConv"); if (!lpobj->hSysEdit) { hedit = LocalAlloc (LMEM_MOVEABLE | LMEM_ZEROINIT, sizeof (EDIT_DDE)); if (hedit == NULL || ((pedit = (PEDIT_DDE) LocalLock (hedit)) == NULL)) goto errRtn; } else { hedit = lpobj->hSysEdit; pedit = lpobj->pSysEdit; UtilMemClr ((PSTR) pedit, sizeof (EDIT_DDE)); } if((pedit->hClient = CreateWindow ("OleSrvrWndClass", "", WS_OVERLAPPED,0,0,0,0,NULL,NULL, hInstDLL, NULL)) == NULL) goto errRtn; lpobj->hSysEdit = hedit; lpobj->pSysEdit = pedit; pedit->hInst = hInst; pedit->awaitAck = AA_INITIATE; SetWindowLongPtr (pedit->hClient, 0, (LONG_PTR)lpobj); SendMessage ((HWND)-1, WM_DDE_INITIATE, (WPARAM)pedit->hClient, MAKELPARAM (lpobj->app, aOle)); ASSERT (CheckAtomValid(aOle),"systopic invalid atom") pedit->awaitAck = 0; if (pedit->hServer == NULL) { pedit->awaitAck = AA_INITIATE; // Now try the System topic SendMessage ((HWND)-1, WM_DDE_INITIATE, (WPARAM)pedit->hClient, MAKELPARAM (lpobj->app, aSystem)); ASSERT (CheckAtomValid(aSystem),"systopic invalid atom") pedit->awaitAck = 0; if (pedit->hServer == NULL) { DEBUG_OUT ("Srver connection failed", 0); goto errRtn; } } // Put the long ptr handle in the object. return TRUE; errRtn: if (pedit) { if (pedit->hClient) DestroyWindow (pedit->hClient); LocalUnlock (hedit); } if (hedit) LocalFree (hedit); lpobj->hSysEdit = NULL; lpobj->pSysEdit = NULL; return FALSE; } // TermSrvrConv: Ends conversation indicated by hedit. void INTERNAL TermSrvrConv (LPOBJECT_LE lpobj) { PEDIT_DDE pedit; Puts("TermSrvrConv"); if (!(pedit = lpobj->pSysEdit)) return; if (PostMessageToServer (pedit, WM_DDE_TERMINATE, 0)){ lpobj->bAsync = TRUE; pedit->bTerminating = TRUE; } else { pedit->bTerminating = FALSE; lpobj->subErr = OLE_ERROR_TERMINATE; } return; } void INTERNAL DeleteAbortData ( LPOBJECT_LE lpobj, PEDIT_DDE pedit ){ UNREFERENCED_PARAMETER(lpobj); // kill if any timer active. if (pedit->wTimer) { KillTimer (pedit->hClient, 1); pedit->wTimer = 0; } return; } BOOL INTERNAL DeleteBusyData ( LPOBJECT_LE lpobj, PEDIT_DDE pedit ){ UNREFERENCED_PARAMETER(lpobj); // kill if any timer active. if (pedit->wTimer) { KillTimer (pedit->hClient, 1); pedit->wTimer = 0; if (pedit->hData) { GlobalFree (pedit->hData); pedit->hData = NULL; } if (pedit->hopt) { GlobalFree (pedit->hopt); pedit->hopt = NULL; } if (pedit->awaitAck && (HIWORD(pedit->lParam))) { if (pedit->awaitAck == AA_EXECUTE) { HANDLE hData = GET_WM_DDE_EXECACK_HDATA(pedit->wParam, pedit->lParam); if (hData) GlobalFree (hData); } else { ASSERT (CheckAtomValid(HIWORD(pedit->lParam)), "Invalid atom in ACK") if (HIWORD(pedit->lParam)) GlobalDeleteAtom (HIWORD(pedit->lParam)); } // we want to wipe out the HIWORD of lParam pedit->lParam &= 0x0000FFFF; } return TRUE; } return FALSE; } void INTERNAL DeleteSrvrEdit ( LPOBJECT_LE lpobj ){ PEDIT_DDE pedit; Puts("deleteSrvrEdit"); if (!(pedit = lpobj->pSysEdit)) return; // delete any data if we were in busy mode. DeleteBusyData (lpobj, pedit); if (pedit->hClient) DestroyWindow (pedit->hClient); if (lpobj->pSysEdit) LocalUnlock (lpobj->hSysEdit); if (lpobj->hSysEdit) LocalFree (lpobj->hSysEdit); lpobj->hSysEdit = NULL; lpobj->pSysEdit = NULL; return; } void INTERNAL SendStdExit ( LPOBJECT_LE lpobj ){ Puts("SendSrvrExit"); if (!lpobj->pSysEdit) return; SrvrExecute (lpobj, MapStrToH ("[StdExit]")); } void INTERNAL SendStdClose ( LPOBJECT_LE lpobj ){ Puts("SendDocClose"); if (!lpobj->pDocEdit) return; DocExecute (lpobj, MapStrToH ("[StdCloseDocument]")); } // SrvrExecute: Sends execute command to system conversation. BOOL INTERNAL SrvrExecute ( LPOBJECT_LE lpobj, HANDLE hdata ){ PEDIT_DDE pedit = NULL; int retval = FALSE; Puts("SrvrExecute"); pedit = lpobj->pSysEdit; if (hdata == NULL || pedit == NULL) { lpobj->subErr = OLE_ERROR_MEMORY; return FALSE; } if (lpobj->bOldLink) { GlobalFree (hdata); return TRUE; } if (PostMessageToServer (pedit, WM_DDE_EXECUTE, (LPARAM)hdata)) { // data is being freed in the acknowledge lpobj->bAsync = TRUE; pedit->awaitAck = AA_EXECUTE; return TRUE; } else { lpobj->subErr = OLE_ERROR_COMMAND; GlobalFree (hdata); return FALSE; } } // StartConvDDE: Starts the document conversation for an object based on // .app and .topic atoms. BOOL FARINTERNAL InitDocConv ( LPOBJECT_LE lpobj, BOOL fNetDlg ){ // ### This routine looks very similar to IitSrvrConv // combine with the it HANDLE hedit = NULL; PEDIT_DDE pedit = NULL; char buf[MAX_NET_NAME]; int nDrive = 2; // drive C char cOldDrive; Puts("InitDocConv"); if (QueryOpen (lpobj)){ DEBUG_OUT ("Attempt to start already existing conversation",0); return FALSE; } cOldDrive = lpobj->cDrive; if (CheckNetDrive (lpobj, fNetDlg) != OLE_OK) return FALSE; if (!lpobj->pDocEdit) { hedit = LocalAlloc (LMEM_MOVEABLE | LMEM_ZEROINIT, sizeof (EDIT_DDE)); if (hedit == NULL || ((pedit = (PEDIT_DDE) LocalLock (hedit)) == NULL)){ lpobj->subErr = OLE_ERROR_MEMORY; goto errRtn; } } else { hedit = lpobj->hDocEdit; pedit = lpobj->pDocEdit; UtilMemClr ((PSTR) pedit, sizeof (EDIT_DDE)); } if ((pedit->hClient = CreateWindow ("OleDocWndClass", "Window Name", WS_OVERLAPPED,0,0,0,0,NULL,NULL, hInstDLL, NULL)) == NULL) { lpobj->subErr = OLE_ERROR_MEMORY; goto errRtn; } lpobj->hDocEdit = hedit; lpobj->pDocEdit = pedit; SetWindowLongPtr (pedit->hClient, 0, (LONG_PTR)lpobj); // buf will filled by netname in the first call to SetNextNetDrive() buf[0] = '\0'; do { pedit->awaitAck = AA_INITIATE; // !!! Where are the atom counts bumped? SendMessage ((HWND)-1, WM_DDE_INITIATE, (WPARAM)pedit->hClient, MAKELPARAM (lpobj->app, lpobj->topic)); pedit->awaitAck = 0; if (pedit->hServer) { if ((cOldDrive != lpobj->cDrive) && (lpobj->asyncCmd != OLE_CREATEFROMFILE)) ContextCallBack ((LPOLEOBJECT)lpobj, OLE_RENAMED); return TRUE; } } while ((lpobj->head.ctype == CT_LINK) && (lpobj->aNetName) && SetNextNetDrive (lpobj, &nDrive, buf)) ; errRtn: if (cOldDrive != lpobj->cDrive) { // put back the old drive lpobj->cDrive = cOldDrive; ChangeTopic (lpobj); } if (pedit && pedit->hClient) DestroyWindow (pedit->hClient); if (pedit) LocalUnlock (hedit); if (hedit) LocalFree (hedit); lpobj->hDocEdit = NULL; lpobj->pDocEdit = NULL; return FALSE; } // Execute: Sends an execute string WM_DDE_EXECUTE to the document conversation. BOOL INTERNAL DocExecute( LPOBJECT_LE lpobj, HANDLE hdata ){ PEDIT_DDE pedit; Puts("DocExecute"); pedit = lpobj->pDocEdit; if (hdata == NULL || pedit == NULL) return FALSE; if (lpobj->bOldLink) { GlobalFree (hdata); return TRUE; } if (PostMessageToServer (pedit, WM_DDE_EXECUTE, (LPARAM)hdata)) { // data is being freed in the execute command pedit->awaitAck = AA_EXECUTE; lpobj->bAsync = TRUE; return TRUE; } else { lpobj->subErr = OLE_ERROR_COMMAND; GlobalFree (hdata); return FALSE; } } // EndConvDDE: terminates the doc level conversation. void INTERNAL TermDocConv ( LPOBJECT_LE lpobj ){ PEDIT_DDE pedit; Puts ("TermDocConv"); DEBUG_OUT ("About to terminate convs from destroy",0) if (!(pedit = lpobj->pDocEdit)) return; if (PostMessageToServer (pedit, WM_DDE_TERMINATE, 0)) { pedit->bTerminating = TRUE; lpobj->bAsync = TRUE; } else lpobj->subErr = OLE_ERROR_TERMINATE; return; } // Deletes the document conversdation memory. void INTERNAL DeleteDocEdit (lpobj) LPOBJECT_LE lpobj; { PEDIT_DDE pedit; Puts ("DeleteDocEdit"); if (!(pedit = lpobj->pDocEdit)) return; // delete any data if we were in busy mode. DeleteBusyData (lpobj, pedit); // Delete if any data blocks. if (pedit->hClient) DestroyWindow (pedit->hClient); if (lpobj->pDocEdit) LocalUnlock (lpobj->hDocEdit); if (lpobj->hDocEdit) LocalFree (lpobj->hDocEdit); lpobj->hDocEdit = NULL; lpobj->pDocEdit = NULL; return; } // LeLauchApp: Launches app based on the ClassName in lpobj. // History: // curts changed LoadModule calls to WinExec // HANDLE INTERNAL LeLaunchApp (LPOBJECT_LE lpobj) { // struct CMDSHOW // { // WORD first; // WORD second; // } cmdShow = {2, SW_SHOWNORMAL}; // // struct // { // WORD wEnvSeg; // LPSTR lpcmdline; // struct CMDSHOW FAR *lpCmdShow; // DWORD dwReserved; // } paramBlock; WORD cmdShow = SW_SHOWNORMAL; char cmdline[MAX_STR]; char exeName[MAX_STR]; char lpstr[2*MAX_STR]; HANDLE hInst; #define EMB_STR " -Embedding " Puts("LeLaunchApp"); if (!GlobalGetAtomName (lpobj->aServer, exeName, MAX_STR)) { return NULL; } cmdline[0] = ' '; if (lpobj->bOldLink) { cmdShow = SW_SHOWMINIMIZED; if (!GlobalGetAtomName (lpobj->topic, cmdline + 1, MAX_STR - 1)) { cmdline[1] = '\0'; } } else { StringCchCopy ((LPSTR)cmdline+1, sizeof(cmdline)-1, (LPSTR) EMB_STR); // For all link servers we want to give the filename on the command // line. But Excel is not registering the document before returning // from WinMain, if it has auto load macros. So, we want send StdOpen // for the old servers, instead of giving the file name on the command // line. if (lpobj->bOleServer && (lpobj->fCmd & LN_MASK) == LN_LNKACT) { if (!GlobalGetAtomName (lpobj->topic, cmdline+sizeof(EMB_STR), MAX_STR-sizeof(EMB_STR)-1)) cmdline[sizeof(EMB_STR)] = '\0'; } if (lpobj->fCmd & ACT_MINIMIZE) cmdShow = SW_SHOWMINIMIZED; else if (!(lpobj->fCmd & (ACT_SHOW | ACT_DOVERB)) // we want to launch with show in create invisible case // even though ACT_SHOW flag will be false && ((lpobj->fCmd & LN_MASK) != LN_NEW)) cmdShow = SW_HIDE; } // paramBlock.wEnvSeg = NULL; // paramBlock.lpcmdline = (LPSTR)cmdline; // paramBlock.lpCmdShow = &cmdShow; // paramBlock.dwReserved = NULL; if (FAILED(StringCchCopy(lpstr, sizeof(lpstr), exeName))) return NULL; if (FAILED(StringCchCat(lpstr, sizeof(lpstr), cmdline))) return NULL; if ((hInst = (HANDLE)ULongToPtr(WinExec(lpstr, cmdShow))) < (HANDLE)32) hInst = NULL; if (!hInst) { LPSTR lptmp; char ch; // strip off the path and try again lptmp = (LPSTR)exeName; lptmp += lstrlen ((LPSTR) exeName); ch = *lptmp; while (ch != '\\' && ch != ':') { if (lptmp == (LPSTR) exeName) { // exe did not have path in it's name. we already tried // loading and it failed, no point trying again. return NULL; } else ch = *--lptmp; } if (FAILED(StringCchCopy(lpstr, sizeof(lpstr), ++lptmp))) return NULL; if (FAILED(StringCchCat(lpstr, sizeof(lpstr), cmdline))) return NULL; if ((hInst = (HANDLE)ULongToPtr(WinExec(lpstr,cmdShow))) < (HANDLE)32) hInst = NULL; } return hInst; } //ScanItemOptions: Scan for the item options like Close/Save etc. int INTERNAL ScanItemOptions ( ATOM aItem, int far *lpoptions ){ ATOM aModifier; LPSTR lpbuf; char buf[MAX_STR] = {0}; *lpoptions = OLE_CHANGED; if (!aItem) { // NULL item with no modifier means OLE_CHANGED for NULL item return OLE_OK; } if(GlobalGetAtomName (aItem, (LPSTR)buf, MAX_STR) == 0) { return OLE_ERROR_SYNTAX; } lpbuf = (LPSTR)buf; while ( *lpbuf && *lpbuf != '/') lpbuf++; // no modifier same as /change if (*lpbuf == '\0') return OLE_OK; *lpbuf++ = '\0'; // seperate out the item string // We are using this in the caller. if (!(aModifier = GlobalFindAtom (lpbuf))) return OLE_ERROR_SYNTAX; if (aModifier == aChange) return OLE_OK; // Is it a save? if (aModifier == aSave){ *lpoptions = OLE_SAVED; return OLE_OK; } // Is it a Close? if (aModifier == aClose){ *lpoptions = OLE_CLOSED; return OLE_OK; } // unknown modifier return OLE_ERROR_SYNTAX; } void INTERNAL ChangeDocName ( LPOBJECT_LE lpobj, LPSTR lpdata ){ ATOM aOldTopic; OLESTATUS retVal; aOldTopic = lpobj->topic; lpobj->topic = GlobalAddAtom (lpdata); if ((retVal = SetNetName (lpobj)) != OLE_OK) { if (lpobj->topic) GlobalDeleteAtom (lpobj->topic); lpobj->topic = aOldTopic; return; // !!! what should we do in case of error? Currently, we will not // change the topic if SetNetName fails. } if (aOldTopic) GlobalDeleteAtom (aOldTopic); // Delete the link data block if (lpobj->hLink) { GlobalFree (lpobj->hLink); lpobj->hLink = NULL; } ContextCallBack ((LPOLEOBJECT)lpobj, OLE_RENAMED); } BOOL INTERNAL CanCallback ( LPOBJECT_LE lpobj, int options ){ LPINT lpCount; if (options == OLE_CLOSED) { lpobj->bSvrClosing = TRUE; lpCount = &(lpobj->pDocEdit->nAdviseClose); } else if (options == OLE_SAVED) { if (lpobj->head.ctype == CT_LINK) return TRUE; lpCount = &(lpobj->pDocEdit->nAdviseSave); } else { // it must be due to request if ((lpobj->pDocEdit->awaitAck == AA_REQUEST) && lpobj->pDocEdit->bCallLater) return FALSE; return TRUE; } switch (*lpCount) { case 1: break; case 2: ++(*lpCount); return FALSE; case 3: --(*lpCount); break; default: return FALSE; } return TRUE; } void FARINTERNAL CallEmbLnkDelete ( LPOBJECT_LE lpobj ){ InitAsyncCmd (lpobj, OLE_SERVERUNLAUNCH,EMBLNKDELETE); EmbLnkDelete (lpobj); if (lpobj->head.ctype == CT_EMBEDDED) { lpobj->bSvrClosing = TRUE; ContextCallBack ((LPOLEOBJECT)lpobj, OLE_CLOSED); if (FarCheckObject ((LPOLEOBJECT)lpobj)) lpobj->bSvrClosing = FALSE; } }