/************************************************************************* ** ** OLE 2 Server Sample Code ** ** svrbase.c ** ** This file contains all interfaces, methods and related support ** functions for the basic OLE Object (Server) application. The ** basic OLE Object application supports embedding an object and ** linking to a file-based or embedded object as a whole. The basic ** Object application includes the following implementation objects: ** ** ClassFactory (aka. ClassObject) Object (see file classfac.c) ** exposed interfaces: ** IClassFactory interface ** ** ServerDoc Object ** exposed interfaces: ** IUnknown ** IOleObject interface ** IPersistStorage interface ** IDataObject interface ** ** ServerApp Object ** exposed interfaces: ** IUnknown ** ** (c) Copyright Microsoft Corp. 1992 - 1993 All Rights Reserved ** *************************************************************************/ #include "outline.h" OLEDBGDATA extern LPOUTLINEAPP g_lpApp; extern IOleObjectVtbl g_SvrDoc_OleObjectVtbl; extern IPersistStorageVtbl g_SvrDoc_PersistStorageVtbl; #if defined( INPLACE_SVR ) extern IOleInPlaceObjectVtbl g_SvrDoc_OleInPlaceObjectVtbl; extern IOleInPlaceActiveObjectVtbl g_SvrDoc_OleInPlaceActiveObjectVtbl; #endif // INPLACE_SVR #if defined( SVR_TREATAS ) extern IStdMarshalInfoVtbl g_SvrDoc_StdMarshalInfoVtbl; #endif // SVR_TREATAS // REVIEW: should use string resource for messages extern char ErrMsgSaving[]; extern char ErrMsgFormatNotSupported[]; static char ErrMsgPSSaveFail[] = "PSSave failed"; static char ErrMsgLowMemNClose[] = "Warning OUT OF MEMORY! We must close down"; extern char g_szUpdateCntrDoc[] = "&Update %s"; extern char g_szExitNReturnToCntrDoc[] = "E&xit && Return to %s"; /************************************************************************* ** ServerDoc::IOleObject interface implementation *************************************************************************/ // IOleObject::QueryInterface method STDMETHODIMP SvrDoc_OleObj_QueryInterface( LPOLEOBJECT lpThis, REFIID riid, LPVOID FAR* lplpvObj ) { LPSERVERDOC lpServerDoc = ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc; return OleDoc_QueryInterface((LPOLEDOC)lpServerDoc, riid, lplpvObj); } // IOleObject::AddRef method STDMETHODIMP_(ULONG) SvrDoc_OleObj_AddRef(LPOLEOBJECT lpThis) { LPSERVERDOC lpServerDoc = ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc; OleDbgAddRefMethod(lpThis, "IOleObject"); return OleDoc_AddRef((LPOLEDOC)lpServerDoc); } // IOleObject::Release method STDMETHODIMP_(ULONG) SvrDoc_OleObj_Release(LPOLEOBJECT lpThis) { LPSERVERDOC lpServerDoc = ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc; OleDbgReleaseMethod(lpThis, "IOleObject"); return OleDoc_Release((LPOLEDOC)lpServerDoc); } // IOleObject::SetClientSite method STDMETHODIMP SvrDoc_OleObj_SetClientSite( LPOLEOBJECT lpThis, LPOLECLIENTSITE lpclientSite ) { LPSERVERDOC lpServerDoc = ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc; LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc; OLEDBG_BEGIN2("SvrDoc_OleObj_SetClientSite\r\n") // SetClientSite is only valid to call on an embedded object if (lpOutlineDoc->m_docInitType != DOCTYPE_EMBEDDED) { OleDbgAssert(lpOutlineDoc->m_docInitType == DOCTYPE_EMBEDDED); OLEDBG_END2 return ResultFromScode(E_UNEXPECTED); } /* if we currently have a client site ptr, then release it. */ if (lpServerDoc->m_lpOleClientSite) OleStdRelease((LPUNKNOWN)lpServerDoc->m_lpOleClientSite); lpServerDoc->m_lpOleClientSite = (LPOLECLIENTSITE) lpclientSite; // OLE2NOTE: to be able to hold onto clientSite pointer, we must AddRef it if (lpclientSite) lpclientSite->lpVtbl->AddRef(lpclientSite); OLEDBG_END2 return NOERROR; } // IOleObject::GetClientSite method STDMETHODIMP SvrDoc_OleObj_GetClientSite( LPOLEOBJECT lpThis, LPOLECLIENTSITE FAR* lplpClientSite ) { LPSERVERDOC lpServerDoc = ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc; OleDbgOut2("SvrDoc_OleObj_GetClientSite\r\n"); /* OLE2NOTE: we MUST AddRef this interface pointer to give the ** caller a personal copy of the pointer */ lpServerDoc->m_lpOleClientSite->lpVtbl->AddRef( lpServerDoc->m_lpOleClientSite ); *lplpClientSite = lpServerDoc->m_lpOleClientSite; return NOERROR; } // IOleObject::SetHostNames method STDMETHODIMP SvrDoc_OleObj_SetHostNamesA( LPOLEOBJECT lpThis, LPCSTR szContainerApp, LPCSTR szContainerObj ) { LPSERVERDOC lpServerDoc = ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc; LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc; OleDbgOut2("SvrDoc_OleObj_SetHostNames\r\n"); LSTRCPYN((LPSTR)lpServerDoc->m_szContainerApp, szContainerApp, sizeof(lpServerDoc->m_szContainerApp)); LSTRCPYN((LPSTR)lpServerDoc->m_szContainerObj, szContainerObj, sizeof(lpServerDoc->m_szContainerObj)); /* The Window title for an embedded object is constructed as ** follows: ** - in ** ** here we construct the current document title portion of the ** name which follows the '-'. OutlineDoc_SetTitle prepends the ** " - " to the document title. */ // REVIEW: this string should be loaded from string resource wsprintf(lpOutlineDoc->m_szFileName, "%s in %s", (LPSTR)SHORTUSERTYPENAME, (LPSTR)lpServerDoc->m_szContainerObj); lpOutlineDoc->m_lpszDocTitle = lpOutlineDoc->m_szFileName; OutlineDoc_SetTitle(lpOutlineDoc, FALSE /*fMakeUpperCase*/); /* OLE2NOTE: update the application menus correctly for an embedded ** object. the changes include: ** 1 Remove File/New and File/Open (SDI ONLY) ** 2 Change File/Save As.. to File/Save Copy As.. ** 3 Change File menu so it contains "Update" instead of "Save" ** 4 Change File/Exit to File/Exit & Return to " */ ServerDoc_UpdateMenu(lpServerDoc); return NOERROR; } STDMETHODIMP SvrDoc_OleObj_SetHostNames( LPOLEOBJECT lpThis, LPCOLESTR szContainerApp, LPCOLESTR szContainerObj ) { CREATESTR(pstrApp, szContainerApp) CREATESTR(pstrObj, szContainerObj) HRESULT hr = SvrDoc_OleObj_SetHostNamesA(lpThis, pstrApp, pstrObj); FREESTR(pstrApp) FREESTR(pstrObj) return hr; } // IOleObject::Close method STDMETHODIMP SvrDoc_OleObj_Close( LPOLEOBJECT lpThis, DWORD dwSaveOption ) { LPSERVERDOC lpServerDoc = ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc; BOOL fStatus; OLEDBG_BEGIN2("SvrDoc_OleObj_Close\r\n") /* OLE2NOTE: the OLE 2.0 user model is that embedded objects should ** always be saved when closed WITHOUT any prompting to the ** user. this is the recommendation irregardless of whether the ** object is activated in-place or open in its own window. ** this is a CHANGE from the OLE 1.0 user model where it ** was the guideline that servers always prompt to save changes. ** thus OLE 2.0 compound document oriented container's should ** always pass dwSaveOption==OLECLOSE_SAVEIFDIRTY. it is ** possible that for programmatic uses a container may want to ** specify a different dwSaveOption. the implementation of ** various save options can be tricky, particularly considering ** cases involving in-place activation. the following would be ** reasonable behavior: ** ** (1) OLECLOSE_SAVEIFDIRTY: if dirty, save. close. ** (2) OLECLOSE_NOSAVE: close. ** (3) OLECLOSE_PROMPTSAVE: ** (a) object visible, but not in-place: ** if not dirty, close. ** switch(prompt) ** case IDYES: save. close. ** case IDNO: close. ** case IDCANCEL: return OLE_E_PROMPTSAVECANCELLED ** (b) object invisible (includes UIDeactivated object) ** if dirty, save. close. ** NOTE: NO PROMPT. it is not appropriate to prompt ** if the object is not visible. ** (c) object is in-place active: ** if dirty, save. close. ** NOTE: NO PROMPT. it is not appropriate to prompt ** if the object is active in-place. */ fStatus = OutlineDoc_Close((LPOUTLINEDOC)lpServerDoc, dwSaveOption); OleDbgAssertSz(fStatus == TRUE, "SvrDoc_OleObj_Close failed\r\n"); OLEDBG_END2 return (fStatus ? NOERROR : ResultFromScode(E_FAIL)); } // IOleObject::SetMoniker method STDMETHODIMP SvrDoc_OleObj_SetMoniker( LPOLEOBJECT lpThis, DWORD dwWhichMoniker, LPMONIKER lpmk ) { LPSERVERDOC lpServerDoc = ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc; LPOLEDOC lpOleDoc = (LPOLEDOC)lpServerDoc; LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc; LPMONIKER lpmkFull = NULL; HRESULT hrErr; SCODE sc; OLEDBG_BEGIN2("SvrDoc_OleObj_SetMoniker\r\n") /* OLE2NOTE: if our full moniker is passed then we can use it, ** otherwise we must call back to our ClientSite to get our full ** moniker. */ if (dwWhichMoniker == OLEWHICHMK_OBJFULL) { /* Register the document as running with the new moniker and ** notify any clients that our moniker has changed. */ OleDoc_DocRenamedUpdate(lpOleDoc, lpmk); if (lpOutlineDoc->m_docInitType != DOCTYPE_EMBEDDED) { IBindCtx FAR *pbc = NULL; LPSTR lpszName = NULL; /* OLE2NOTE: if this is a FILE-based or untitled document ** then we should accept this new moniker as our document's ** moniker. we will remember this moniker instead of the ** FileMoniker that we have by default. this allows ** systems that use special monikers to track the ** location of documents to inform a document that is a ** link source of its special moniker. this enables the ** document to use this special moniker when building ** composite monikers to identify contained objects and ** pseudo objects (ranges). ** ** we should also use the DisplayName form of this ** moniker as our document name in our window title. */ if (lpOleDoc->m_lpFileMoniker) { lpOleDoc->m_lpFileMoniker->lpVtbl->Release( lpOleDoc->m_lpFileMoniker); } lpOleDoc->m_lpFileMoniker = lpmk; // we must AddRef the moniker to hold on to it lpmk->lpVtbl->AddRef(lpmk); /* we should also use the DisplayName form of this ** moniker as our document name in our window title. */ CreateBindCtx(0, (LPBC FAR*)&pbc); CallIMonikerGetDisplayNameA(lpmk,pbc,NULL,&lpszName); pbc->lpVtbl->Release(pbc); if (lpszName) { LSTRCPYN(lpOutlineDoc->m_szFileName, lpszName, sizeof(lpOutlineDoc->m_szFileName)); lpOutlineDoc->m_lpszDocTitle = lpOutlineDoc->m_szFileName; OutlineDoc_SetTitle(lpOutlineDoc, FALSE /*fMakeUpperCase*/); OleStdFreeString(lpszName, NULL); } } OLEDBG_END2 return NOERROR; } /* if the passed moniker was NOT a full moniker then we must call ** back to our ClientSite to get our full moniker. this is ** needed in order to register in the RunningObjectTable. if we ** don't have a ClientSite then this is an error. */ if (lpServerDoc->m_lpOleClientSite == NULL) { sc = E_FAIL; goto error; } hrErr = lpServerDoc->m_lpOleClientSite->lpVtbl->GetMoniker( lpServerDoc->m_lpOleClientSite, OLEGETMONIKER_ONLYIFTHERE, OLEWHICHMK_OBJFULL, &lpmkFull ); if (hrErr != NOERROR) { sc = GetScode(hrErr); goto error; } /* Register the document as running with the new moniker and ** notify any clients that our moniker has changed. */ OleDoc_DocRenamedUpdate(lpOleDoc, lpmkFull); if (lpmkFull) OleStdRelease((LPUNKNOWN)lpmkFull); OLEDBG_END2 return NOERROR; error: OLEDBG_END2 return ResultFromScode(sc); } // IOleObject::GetMoniker method STDMETHODIMP SvrDoc_OleObj_GetMoniker( LPOLEOBJECT lpThis, DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER FAR* lplpmk ) { LPSERVERDOC lpServerDoc = ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc; LPOLEDOC lpOleDoc = (LPOLEDOC)lpServerDoc; SCODE sc; OLEDBG_BEGIN2("SvrDoc_OleObj_GetMoniker\r\n") /* OLE2NOTE: we must make sure to set all out parameters to NULL. */ *lplpmk = NULL; if (lpServerDoc->m_lpOleClientSite) { /* document is an embedded object. retrieve our moniker from ** our container. */ OLEDBG_BEGIN2("IOleClientSite::GetMoniker called\r\n") sc = GetScode( lpServerDoc->m_lpOleClientSite->lpVtbl->GetMoniker( lpServerDoc->m_lpOleClientSite, dwAssign, dwWhichMoniker, lplpmk ) ); OLEDBG_END2 } else if (lpOleDoc->m_lpFileMoniker) { /* document is a top-level user document (either ** file-based or untitled). return the FileMoniker stored ** with the document; it uniquely identifies the document. */ if (dwWhichMoniker == OLEWHICHMK_CONTAINER) sc = E_INVALIDARG; // file-based object has no CONTAINER moniker else { *lplpmk = lpOleDoc->m_lpFileMoniker; (*lplpmk)->lpVtbl->AddRef(*lplpmk); // must AddRef to pass out ptr sc = S_OK; } } else { // document is not yet fully initialized => no moniker sc = E_FAIL; } OLEDBG_END2 return ResultFromScode(sc); } // IOleObject::InitFromData method STDMETHODIMP SvrDoc_OleObj_InitFromData( LPOLEOBJECT lpThis, LPDATAOBJECT lpDataObject, BOOL fCreation, DWORD reserved ) { LPSERVERDOC lpServerDoc = ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc; OLEDBG_BEGIN2("SvrDoc_OleObj_InitFromData\r\n") // REVIEW: NOT YET IMPLEMENTED OLEDBG_END2 return ResultFromScode(E_NOTIMPL); } // IOleObject::GetClipboardData method STDMETHODIMP SvrDoc_OleObj_GetClipboardData( LPOLEOBJECT lpThis, DWORD reserved, LPDATAOBJECT FAR* lplpDataObject ) { LPSERVERDOC lpServerDoc = ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc; OLEDBG_BEGIN2("SvrDoc_OleObj_GetClipboardData\r\n") // REVIEW: NOT YET IMPLEMENTED OLEDBG_END2 return ResultFromScode(E_NOTIMPL); } // IOleObject::DoVerb method STDMETHODIMP SvrDoc_OleObj_DoVerb( LPOLEOBJECT lpThis, LONG lVerb, LPMSG lpmsg, LPOLECLIENTSITE lpActiveSite, LONG lindex, HWND hwndParent, LPCRECT lprcPosRect ) { LPSERVERDOC lpServerDoc = ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc; LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc; SCODE sc = S_OK; OLEDBG_BEGIN2("SvrDoc_OleObj_DoVerb\r\n") switch (lVerb) { default: /* OLE2NOTE: when an unknown verb number is given, the ** server must take careful action: ** 1. if it is one of the specially defined OLEIVERB ** (negative numbered) verbs, the app should return an ** error (E_NOTIMPL) and perform no action. ** ** 2. if the verb is a application specific verb ** (positive numbered verb), then the app should ** return the special scode (OLEOBJ_S_INVALIDVERB). BUT, ** we should still perform our normal primary verb action. */ if (lVerb < 0) { OLEDBG_END2 return ResultFromScode(E_NOTIMPL); } else { sc = OLEOBJ_S_INVALIDVERB; } // deliberatly fall through to Primary Verb #if !defined( INPLACE_SVR ) case 0: case OLEIVERB_SHOW: case OLEIVERB_OPEN: OutlineDoc_ShowWindow(lpOutlineDoc); break; case OLEIVERB_HIDE: OleDoc_HideWindow((LPOLEDOC)lpServerDoc, FALSE /*fShutdown*/); break; #endif // ! INPLACE_SVR #if defined( INPLACE_SVR ) case 0: case OLEIVERB_SHOW: /* OLE2NOTE: if our window is already open (visible) then ** we should simply surface the open window. if not, ** then we can do our primary action of in-place ** activation. */ if ( lpServerDoc->m_lpOleClientSite && ! (IsWindowVisible(lpOutlineDoc->m_hWndDoc) && ! lpServerDoc->m_fInPlaceActive) ) { ServerDoc_DoInPlaceActivate( lpServerDoc, lVerb, lpmsg, lpActiveSite); } OutlineDoc_ShowWindow(lpOutlineDoc); break; case 1: case OLEIVERB_OPEN: ServerDoc_DoInPlaceDeactivate(lpServerDoc); OutlineDoc_ShowWindow(lpOutlineDoc); break; case OLEIVERB_HIDE: if (lpServerDoc->m_fInPlaceActive) { SvrDoc_IPObj_UIDeactivate( (LPOLEINPLACEOBJECT)&lpServerDoc->m_OleInPlaceObject); #if defined( SVR_INSIDEOUT ) /* OLE2NOTE: an inside-out style in-place server will ** NOT hide its window in UIDeactive (an outside-in ** style object will hide its window in ** UIDeactivate). thus we need to explicitly hide ** our window now. */ ServerDoc_DoInPlaceHide(lpServerDoc); #endif // INSIEDOUT } else { OleDoc_HideWindow((LPOLEDOC)lpServerDoc, FALSE /*fShutdown*/); } break; case OLEIVERB_UIACTIVATE: #if defined( SVR_INSIDEOUT ) /* OLE2NOTE: only an inside-out style object supports ** INPLACEACTIVATE verb */ case OLEIVERB_INPLACEACTIVATE: #endif // SVR_INSIDEOUT /* OLE2NOTE: if our window is already open (visible) then ** we can NOT activate in-place. */ if (IsWindowVisible(lpOutlineDoc->m_hWndDoc) && ! lpServerDoc->m_fInPlaceActive ) { sc = OLE_E_NOT_INPLACEACTIVE; } else { sc = GetScode( ServerDoc_DoInPlaceActivate( lpServerDoc, lVerb, lpmsg, lpActiveSite) ); if (SUCCEEDED(sc)) OutlineDoc_ShowWindow(lpOutlineDoc); } break; #endif // INPLACE_SVR } OLEDBG_END2 return ResultFromScode(sc); } // IOleObject::EnumVerbs method STDMETHODIMP SvrDoc_OleObj_EnumVerbs( LPOLEOBJECT lpThis, LPENUMOLEVERB FAR* lplpenumOleVerb ) { OleDbgOut2("SvrDoc_OleObj_EnumVerbs\r\n"); /* OLE2NOTE: we must make sure to set all out parameters to NULL. */ *lplpenumOleVerb = NULL; /* An object implemented as a server EXE (as this sample ** is) may simply return OLE_S_USEREG to instruct the OLE ** DefHandler to call the OleReg* helper API which uses info in ** the registration database. Alternatively, the OleRegEnumVerbs ** API may be called directly. Objects implemented as a server ** DLL may NOT return OLE_S_USEREG; they must call the OleReg* ** API or provide their own implementation. For EXE based ** objects it is more efficient to return OLE_S_USEREG, because ** in then the verb enumerator is instantiated in the callers ** process space and no LRPC remoting is required. */ return ResultFromScode(OLE_S_USEREG); } // IOleObject::Update method STDMETHODIMP SvrDoc_OleObj_Update(LPOLEOBJECT lpThis) { OleDbgOut2("SvrDoc_OleObj_Update\r\n"); /* OLE2NOTE: a server-only app is always "up-to-date". ** a container-app which contains links where the link source ** has changed since the last update of the link would be ** considered "out-of-date". the "Update" method instructs the ** object to get an update from any out-of-date links. */ return NOERROR; } // IOleObject::IsUpToDate method STDMETHODIMP SvrDoc_OleObj_IsUpToDate(LPOLEOBJECT lpThis) { OleDbgOut2("SvrDoc_OleObj_IsUpToDate\r\n"); /* OLE2NOTE: a server-only app is always "up-to-date". ** a container-app which contains links where the link source ** has changed since the last update of the link would be ** considered "out-of-date". */ return NOERROR; } // IOleObject::GetUserClassID method STDMETHODIMP SvrDoc_OleObj_GetUserClassID( LPOLEOBJECT lpThis, LPCLSID lpClassID ) { LPSERVERDOC lpServerDoc = ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc; OleDbgOut2("SvrDoc_OleObj_GetClassID\r\n"); /* OLE2NOTE: we must be carefull to return the correct CLSID here. ** if we are currently preforming a "TreatAs (aka. ActivateAs)" ** operation then we need to return the class of the object ** written in the storage of the object. otherwise we would ** return our own class id. */ return ServerDoc_GetClassID(lpServerDoc, lpClassID); } // IOleObject::GetUserType method STDMETHODIMP SvrDoc_OleObj_GetUserTypeA( LPOLEOBJECT lpThis, DWORD dwFormOfType, LPSTR FAR* lpszUserType ) { LPSERVERDOC lpServerDoc = ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc; OleDbgOut2("SvrDoc_OleObj_GetUserType\r\n"); /* OLE2NOTE: we must make sure to set all out parameters to NULL. */ *lpszUserType = NULL; /* OLE2NOTE: we must be carefull to return the correct user type here. ** if we are currently preforming a "TreatAs (aka. ActivateAs)" ** operation then we need to return the user type name that ** corresponds to the class of the object we are currently ** emmulating. otherwise we should return our normal user type ** name corresponding to our own class. This routine determines ** the current clsid in effect. ** ** An object implemented as a server EXE (as this sample ** is) may simply return OLE_S_USEREG to instruct the OLE ** DefHandler to call the OleReg* helper API which uses info in ** the registration database. Alternatively, the OleRegGetUserType ** API may be called directly. Objects implemented as a server ** DLL may NOT return OLE_S_USEREG; they must call the OleReg* ** API or provide their own implementation. For EXE based ** objects it is more efficient to return OLE_S_USEREG, because ** in then the return string is instantiated in the callers ** process space and no LRPC remoting is required. */ #if defined( SVR_TREATAS ) if (! IsEqualCLSID(&lpServerDoc->m_clsidTreatAs, &CLSID_NULL) ) return OleRegGetUserTypeA( &lpServerDoc->m_clsidTreatAs,dwFormOfType,lpszUserType); else #endif // SVR_TREATAS return ResultFromScode(OLE_S_USEREG); } STDMETHODIMP SvrDoc_OleObj_GetUserType( LPOLEOBJECT lpThis, DWORD dwFormOfType, LPOLESTR FAR* lpszUserType ) { LPSTR pstr; HRESULT hr = SvrDoc_OleObj_GetUserTypeA(lpThis, dwFormOfType, &pstr); CopyAndFreeSTR(pstr, lpszUserType); return hr; } // IOleObject::SetExtent method STDMETHODIMP SvrDoc_OleObj_SetExtent( LPOLEOBJECT lpThis, DWORD dwDrawAspect, LPSIZEL lplgrc ) { OleDbgOut2("SvrDoc_OleObj_SetExtent\r\n"); /* SVROUTL does NOT allow the object's size to be set by its ** container. the size of the ServerDoc object is determined by ** the data contained within the document. */ return ResultFromScode(E_FAIL); } // IOleObject::GetExtent method STDMETHODIMP SvrDoc_OleObj_GetExtent( LPOLEOBJECT lpThis, DWORD dwDrawAspect, LPSIZEL lpsizel ) { LPOLEDOC lpOleDoc = (LPOLEDOC)((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc; OleDbgOut2("SvrDoc_OleObj_GetExtent\r\n"); /* OLE2NOTE: it is VERY important to check which aspect the caller ** is asking about. an object implemented by a server EXE MAY ** fail to return extents when asked for DVASPECT_ICON. */ if (dwDrawAspect == DVASPECT_CONTENT) { OleDoc_GetExtent(lpOleDoc, lpsizel); return NOERROR; } #if defined( LATER ) else if (dwDrawAspect == DVASPECT_THUMBNAIL) { /* as our thumbnail we will render only the first page of the ** document. calculate extents of our thumbnail rendering. ** ** OLE2NOTE: thumbnails are most often used by applications in ** FindFile or FileOpen type dialogs to give the user a ** quick view of the contents of the file or object. */ OleDoc_GetThumbnailExtent(lpOleDoc, lpsizel); return NOERROR; } #endif else { return ResultFromScode(E_FAIL); } } // IOleObject::Advise method STDMETHODIMP SvrDoc_OleObj_Advise( LPOLEOBJECT lpThis, LPADVISESINK lpAdvSink, LPDWORD lpdwConnection ) { LPSERVERDOC lpServerDoc = ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc; HRESULT hrErr; SCODE sc; OLEDBG_BEGIN2("SvrDoc_OleObj_Advise\r\n"); if (lpServerDoc->m_OleDoc.m_fObjIsClosing) { // We don't accept any more Advise's once we're closing sc = OLE_E_ADVISENOTSUPPORTED; goto error; } if (lpServerDoc->m_lpOleAdviseHldr == NULL && CreateOleAdviseHolder(&lpServerDoc->m_lpOleAdviseHldr) != NOERROR) { sc = E_OUTOFMEMORY; goto error; } OLEDBG_BEGIN2("IOleAdviseHolder::Advise called\r\n") hrErr = lpServerDoc->m_lpOleAdviseHldr->lpVtbl->Advise( lpServerDoc->m_lpOleAdviseHldr, lpAdvSink, lpdwConnection ); OLEDBG_END2 OLEDBG_END2 return hrErr; error: OLEDBG_END2 *lpdwConnection = 0; return ResultFromScode(sc); } // IOleObject::Unadvise method STDMETHODIMP SvrDoc_OleObj_Unadvise(LPOLEOBJECT lpThis, DWORD dwConnection) { LPSERVERDOC lpServerDoc = ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc; HRESULT hrErr; SCODE sc; OLEDBG_BEGIN2("SvrDoc_OleObj_Unadvise\r\n"); if (lpServerDoc->m_lpOleAdviseHldr == NULL) { sc = E_FAIL; goto error; } OLEDBG_BEGIN2("IOleAdviseHolder::Unadvise called\r\n") hrErr = lpServerDoc->m_lpOleAdviseHldr->lpVtbl->Unadvise( lpServerDoc->m_lpOleAdviseHldr, dwConnection ); OLEDBG_END2 OLEDBG_END2 return hrErr; error: OLEDBG_END2 return ResultFromScode(sc); } // IOleObject::EnumAdvise method STDMETHODIMP SvrDoc_OleObj_EnumAdvise( LPOLEOBJECT lpThis, LPENUMSTATDATA FAR* lplpenumAdvise ) { LPSERVERDOC lpServerDoc = ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc; HRESULT hrErr; SCODE sc; OLEDBG_BEGIN2("SvrDoc_OleObj_EnumAdvise\r\n"); /* OLE2NOTE: we must make sure to set all out parameters to NULL. */ *lplpenumAdvise = NULL; if (lpServerDoc->m_lpOleAdviseHldr == NULL) { sc = E_FAIL; goto error; } OLEDBG_BEGIN2("IOleAdviseHolder::EnumAdvise called\r\n") hrErr = lpServerDoc->m_lpOleAdviseHldr->lpVtbl->EnumAdvise( lpServerDoc->m_lpOleAdviseHldr, lplpenumAdvise ); OLEDBG_END2 OLEDBG_END2 return hrErr; error: OLEDBG_END2 return ResultFromScode(sc); } // IOleObject::GetMiscStatus method STDMETHODIMP SvrDoc_OleObj_GetMiscStatus( LPOLEOBJECT lpThis, DWORD dwAspect, DWORD FAR* lpdwStatus ) { LPSERVERDOC lpServerDoc = ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc; LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc; OleDbgOut2("SvrDoc_OleObj_GetMiscStatus\r\n"); /* Get our default MiscStatus for the given Aspect. this ** information is registered in the RegDB. We query the RegDB ** here to guarantee that the value returned from this method ** agrees with the values in RegDB. in this way we only have to ** maintain the info in one place (in the RegDB). Alternatively ** we could have the values hard coded here. */ OleRegGetMiscStatus((REFCLSID)&CLSID_APP, dwAspect, lpdwStatus); /* OLE2NOTE: check if the data copied is compatible to be ** linked by an OLE 1.0 container. it is compatible if ** either the data is an untitled document, a file, or a ** selection of data within a file. if the data is part of ** an embedded object, then it is NOT compatible to be ** linked by an OLE 1.0 container. if it is compatible then ** we must include OLEMISC_CANLINKBYOLE1 as part of the ** dwStatus flags transfered via CF_OBJECTDESCRIPTOR or ** CF_LINKSRCDESCRIPTOR. */ if (lpOutlineDoc->m_docInitType == DOCTYPE_NEW || lpOutlineDoc->m_docInitType == DOCTYPE_FROMFILE) *lpdwStatus |= OLEMISC_CANLINKBYOLE1; #if defined( INPLACE_SVR ) if (dwAspect == DVASPECT_CONTENT) *lpdwStatus |= (OLEMISC_INSIDEOUT | OLEMISC_ACTIVATEWHENVISIBLE); #endif // INPLACE_SVR return NOERROR; } // IOleObject::SetColorScheme method STDMETHODIMP SvrDoc_OleObj_SetColorScheme( LPOLEOBJECT lpThis, LPLOGPALETTE lpLogpal ) { OleDbgOut2("SvrDoc_OleObj_SetColorScheme\r\n"); // REVIEW: NOT YET IMPLEMENTED return ResultFromScode(E_NOTIMPL); } /************************************************************************* ** ServerDoc::IPersistStorage interface implementation *************************************************************************/ // IPersistStorage::QueryInterface method STDMETHODIMP SvrDoc_PStg_QueryInterface( LPPERSISTSTORAGE lpThis, REFIID riid, LPVOID FAR* lplpvObj ) { LPSERVERDOC lpServerDoc = ((struct CDocPersistStorageImpl FAR*)lpThis)->lpServerDoc; return OleDoc_QueryInterface((LPOLEDOC)lpServerDoc, riid, lplpvObj); } // IPersistStorage::AddRef method STDMETHODIMP_(ULONG) SvrDoc_PStg_AddRef(LPPERSISTSTORAGE lpThis) { LPSERVERDOC lpServerDoc = ((struct CDocPersistStorageImpl FAR*)lpThis)->lpServerDoc; OleDbgAddRefMethod(lpThis, "IPersistStorage"); return OleDoc_AddRef((LPOLEDOC)lpServerDoc); } // IPersistStorage::Release method STDMETHODIMP_(ULONG) SvrDoc_PStg_Release(LPPERSISTSTORAGE lpThis) { LPSERVERDOC lpServerDoc = ((struct CDocPersistStorageImpl FAR*)lpThis)->lpServerDoc; OleDbgReleaseMethod(lpThis, "IPersistStorage"); return OleDoc_Release((LPOLEDOC)lpServerDoc); } // IPersistStorage::GetClassID method STDMETHODIMP SvrDoc_PStg_GetClassID( LPPERSISTSTORAGE lpThis, LPCLSID lpClassID ) { LPSERVERDOC lpServerDoc = ((struct CDocPersistStorageImpl FAR*)lpThis)->lpServerDoc; OleDbgOut2("SvrDoc_PStg_GetClassID\r\n"); /* OLE2NOTE: we must be carefull to return the correct CLSID here. ** if we are currently preforming a "TreatAs (aka. ActivateAs)" ** operation then we need to return the class of the object ** written in the storage of the object. otherwise we would ** return our own class id. */ return ServerDoc_GetClassID(lpServerDoc, lpClassID); } // IPersistStorage::IsDirty method STDMETHODIMP SvrDoc_PStg_IsDirty(LPPERSISTSTORAGE lpThis) { LPSERVERDOC lpServerDoc = ((struct CDocPersistStorageImpl FAR*)lpThis)->lpServerDoc; OleDbgOut2("SvrDoc_PStg_IsDirty\r\n"); if (OutlineDoc_IsModified((LPOUTLINEDOC)lpServerDoc)) return NOERROR; else return ResultFromScode(S_FALSE); } // IPersistStorage::InitNew method STDMETHODIMP SvrDoc_PStg_InitNew( LPPERSISTSTORAGE lpThis, LPSTORAGE lpStg ) { LPSERVERDOC lpServerDoc = ((struct CDocPersistStorageImpl FAR*)lpThis)->lpServerDoc; LPOLEDOC lpOleDoc = (LPOLEDOC)lpServerDoc; LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp; LPSTR lpszUserType = (LPSTR)FULLUSERTYPENAME; HRESULT hrErr; SCODE sc; OLEDBG_BEGIN2("SvrDoc_PStg_InitNew\r\n") #if defined( SVR_TREATAS ) { LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp; CLSID clsid; CLIPFORMAT cfFmt; LPSTR lpszType; /* OLE2NOTE: if the Server is capable of supporting "TreatAs" ** (aka. ActivateAs), it must read the class that is written ** into the storage. if this class is NOT the app's own ** class ID, then this is a TreatAs operation. the server ** then must faithfully pretend to be the class that is ** written into the storage. it must also faithfully write ** the data back to the storage in the SAME format as is ** written in the storage. ** ** SVROUTL and ISVROTL can emulate each other. they have the ** simplification that they both read/write the identical ** format. thus for these apps no actual conversion of the ** native bits is actually required. */ lpServerDoc->m_clsidTreatAs = CLSID_NULL; if (OleStdGetTreatAsFmtUserType(&CLSID_APP, lpStg, &clsid, (CLIPFORMAT FAR*)&cfFmt, (LPSTR FAR*)&lpszType)) { if (cfFmt == lpOutlineApp->m_cfOutline) { // We should perform TreatAs operation if (lpServerDoc->m_lpszTreatAsType) OleStdFreeString(lpServerDoc->m_lpszTreatAsType, NULL); lpServerDoc->m_clsidTreatAs = clsid; ((LPOUTLINEDOC)lpServerDoc)->m_cfSaveFormat = cfFmt; lpServerDoc->m_lpszTreatAsType = lpszType; lpszUserType = lpServerDoc->m_lpszTreatAsType; OleDbgOut3("SvrDoc_PStg_InitNew: TreateAs ==> '"); OleDbgOutNoPrefix3(lpServerDoc->m_lpszTreatAsType); OleDbgOutNoPrefix3("'\r\n"); } else { // ERROR: we ONLY support TreatAs for CF_OUTLINE format OleDbgOut("SvrDoc_PStg_InitNew: INVALID TreatAs Format\r\n"); OleStdFreeString(lpszType, NULL); } } } #endif // SVR_TREATAS /* OLE2NOTE: a server EXE object should write its format tag to its ** storage in InitNew so that the DefHandler can know the format ** of the object. this is particularly important if the objects ** uses CF_METATFILE or CF_DIB as its format. the DefHandler ** automatically avoids separately storing presentation cache ** data when the object's native data is a standard presentation ** format. */ WriteFmtUserTypeStgA(lpStg,lpOutlineApp->m_cfOutline,lpszUserType); // set the doc to a new embedded object. if (! ServerDoc_InitNewEmbed(lpServerDoc)) { sc = E_FAIL; goto error; } /* OLE2NOTE: An embedded object must guarantee that it can save ** even in low memory situations. it must be able to ** successfully save itself without consuming any additional ** memory. this means that a server is NOT supposed to open or ** create any streams or storages when ** IPersistStorage::Save(fSameAsLoad==TRUE) is called. thus an ** embedded object should hold onto its storage and pre-open and ** hold open any streams that it will need later when it is time ** to save. */ hrErr = CallIStorageCreateStreamA( lpStg, "LineList", STGM_WRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, 0, &lpOleDoc->m_lpLLStm ); if (hrErr != NOERROR) { OleDbgAssertSz(hrErr==NOERROR,"Could not create LineList stream"); OleDbgOutHResult("LineList CreateStream returned", hrErr); sc = GetScode(hrErr); goto error; } hrErr = CallIStorageCreateStreamA( lpStg, "NameTable", STGM_WRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, 0, &lpOleDoc->m_lpNTStm ); if (hrErr != NOERROR) { OleDbgAssertSz(hrErr==NOERROR,"Could not create NameTable stream"); OleDbgOutHResult("NameTable CreateStream returned", hrErr); sc = GetScode(hrErr); goto error; } lpOleDoc->m_lpStg = lpStg; // OLE2NOTE: to be able to hold onto IStorage* pointer, we must AddRef it lpStg->lpVtbl->AddRef(lpStg); OLEDBG_END2 return NOERROR; error: OLEDBG_END2 return ResultFromScode(sc); } // IPersistStorage::Load method STDMETHODIMP SvrDoc_PStg_Load( LPPERSISTSTORAGE lpThis, LPSTORAGE lpStg ) { LPSERVERDOC lpServerDoc = ((struct CDocPersistStorageImpl FAR*)lpThis)->lpServerDoc; LPOLEDOC lpOleDoc = (LPOLEDOC)lpServerDoc; LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc; SCODE sc; HRESULT hrErr; OLEDBG_BEGIN2("SvrDoc_PStg_Load\r\n") if (OutlineDoc_LoadFromStg((LPOUTLINEDOC)lpServerDoc, lpStg)) { ((LPOUTLINEDOC)lpServerDoc)->m_docInitType = DOCTYPE_EMBEDDED; /* OLE2NOTE: we need to check if the ConvertStg bit is on. if ** so, we need to clear the ConvertStg bit and mark the ** document as dirty so as to force a save when the document ** is closed. the actual conversion of the bits should be ** performed when the data is loaded from the IStorage*. in ** our case any conversion of data formats would be done in ** OutlineDoc_LoadFromStg function. in reality both SVROUTL ** and ISVROTL read and write the same format so no actual ** conversion of data bits is necessary. */ if (GetConvertStg(lpStg) == NOERROR) { SetConvertStg(lpStg, FALSE); OleDbgOut3("SvrDoc_PStg_Load: ConvertStg==TRUE\r\n"); OutlineDoc_SetModified(lpOutlineDoc, TRUE, FALSE, FALSE); } } else { sc = E_FAIL; goto error; } /* OLE2NOTE: An embedded object must guarantee that it can save ** even in low memory situations. it must be able to ** successfully save itself without consuming any additional ** memory. this means that a server is NOT supposed to open or ** create any streams or storages when ** IPersistStorage::Save(fSameAsLoad==TRUE) is called. thus an ** embedded object should hold onto its storage and pre-open and ** hold open any streams that it will need later when it is time ** to save. */ if (lpOleDoc->m_lpLLStm) OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpLLStm); hrErr = CallIStorageOpenStreamA( lpStg, "LineList", NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &lpOleDoc->m_lpLLStm ); if (hrErr != NOERROR) { OleDbgAssertSz(hrErr==NOERROR,"Could not create LineList stream"); OleDbgOutHResult("LineList CreateStream returned", hrErr); sc = GetScode(hrErr); goto error; } if (lpOleDoc->m_lpNTStm) OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpNTStm); hrErr = CallIStorageOpenStreamA( lpStg, "NameTable", NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &lpOleDoc->m_lpNTStm ); if (hrErr != NOERROR) { OleDbgAssertSz(hrErr==NOERROR,"Could not create NameTable stream"); OleDbgOutHResult("NameTable CreateStream returned", hrErr); sc = GetScode(hrErr); goto error; } lpOleDoc->m_lpStg = lpStg; // OLE2NOTE: to be able to hold onto IStorage* pointer, we must AddRef it lpStg->lpVtbl->AddRef(lpStg); OLEDBG_END2 return NOERROR; error: OLEDBG_END2 return ResultFromScode(sc); } // IPersistStorage::Save method STDMETHODIMP SvrDoc_PStg_Save( LPPERSISTSTORAGE lpThis, LPSTORAGE lpStg, BOOL fSameAsLoad ) { LPSERVERDOC lpServerDoc = ((struct CDocPersistStorageImpl FAR*)lpThis)->lpServerDoc; LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc; BOOL fStatus; SCODE sc; OLEDBG_BEGIN2("SvrDoc_PStg_Save\r\n") fStatus = OutlineDoc_SaveSelToStg( (LPOUTLINEDOC)lpServerDoc, NULL, lpOutlineDoc->m_cfSaveFormat, lpStg, fSameAsLoad, FALSE ); if (! fStatus) { OutlineApp_ErrorMessage(g_lpApp, ErrMsgPSSaveFail); sc = E_FAIL; goto error; } lpServerDoc->m_fSaveWithSameAsLoad = fSameAsLoad; lpServerDoc->m_fNoScribbleMode = TRUE; OLEDBG_END2 return NOERROR; error: OLEDBG_END2 return ResultFromScode(sc); } // IPersistStorage::SaveCompleted method STDMETHODIMP SvrDoc_PStg_SaveCompleted( LPPERSISTSTORAGE lpThis, LPSTORAGE lpStgNew ) { LPSERVERDOC lpServerDoc = ((struct CDocPersistStorageImpl FAR*)lpThis)->lpServerDoc; LPOLEDOC lpOleDoc = (LPOLEDOC)lpServerDoc; LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc; HRESULT hrErr; OLEDBG_BEGIN2("SvrDoc_PStg_SaveCompleted\r\n") /* OLE2NOTE: this sample application is a pure server application. ** a container/server application would have to call SaveCompleted ** for each of its contained compound document objects. if a new ** storage was given, then the container/server would have to ** open the corresponding new sub-storage for each compound ** document object and pass as an argument in the SaveCompleted ** call. */ /* OLE2NOTE: it is only legal to perform a Save or SaveAs operation ** on an embedded object. if the document is a file-based document ** then we can not be changed to a IStorage-base object. ** ** fSameAsLoad lpStgNew Type of Save Send OnSave ** --------------------------------------------------------- ** TRUE NULL SAVE YES ** TRUE ! NULL SAVE * YES ** FALSE ! NULL SAVE AS YES ** FALSE NULL SAVE COPY AS NO ** ** * this is a strange case that is possible. it is inefficient ** for the caller; it would be better to pass lpStgNew==NULL for ** the Save operation. */ if ( ((lpServerDoc->m_fSaveWithSameAsLoad && lpStgNew==NULL) || lpStgNew) && (lpOutlineDoc->m_docInitType != DOCTYPE_EMBEDDED) ) { OLEDBG_END2 return ResultFromScode(E_INVALIDARG); } /* OLE2NOTE: inform any linking clients that the document has been ** saved. in addition, any currently active pseudo objects ** should also inform their clients. we should only broadcast an ** OnSave notification if a Save or SaveAs operation was ** performed. we do NOT want to send the notification if a ** SaveCopyAs operation was performed. */ if (lpStgNew || lpServerDoc->m_fSaveWithSameAsLoad) { /* OLE2NOTE: if IPersistStorage::Save has been called, then we ** need to clear the dirty bit and send OnSave notification. ** if HandsOffStorage is called directly without first ** calling Save, then we do NOT want to clear the dirty bit ** and send OnSave when SaveCompleted is called. */ if (lpServerDoc->m_fNoScribbleMode) { OutlineDoc_SetModified(lpOutlineDoc, FALSE, FALSE, FALSE); ServerDoc_SendAdvise ( lpServerDoc, OLE_ONSAVE, NULL, /* lpmkDoc -- not relevant here */ 0 /* advf -- not relevant here */ ); } lpServerDoc->m_fSaveWithSameAsLoad = FALSE; } lpServerDoc->m_fNoScribbleMode = FALSE; /* OLE2NOTE: An embedded object must guarantee that it can save ** even in low memory situations. it must be able to ** successfully save itself without consuming any additional ** memory. this means that a server is NOT supposed to open or ** create any streams or storages when ** IPersistStorage::Save(fSameAsLoad==TRUE) is called. thus an ** embedded object should hold onto its storage and pre-open and ** hold open any streams that it will need later when it is time ** to save. if this is a SaveAs situtation, then we want to ** pre-open and hold open our streams to guarantee that a ** subsequent save will be successful in low-memory. if we fail ** to open these streams then we want to force ourself to close ** to make sure the can't make editing changes that can't be ** later saved. */ if ( lpStgNew && !lpServerDoc->m_fSaveWithSameAsLoad ) { // release previous streams if (lpOleDoc->m_lpLLStm) { OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpLLStm); lpOleDoc->m_lpLLStm = NULL; } if (lpOleDoc->m_lpNTStm) { OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpNTStm); lpOleDoc->m_lpNTStm = NULL; } if (lpOleDoc->m_lpStg) { OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpStg); lpOleDoc->m_lpStg = NULL; } hrErr = CallIStorageOpenStreamA( lpStgNew, "LineList", NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &lpOleDoc->m_lpLLStm ); if (hrErr != NOERROR) { OleDbgAssertSz(hrErr==NOERROR,"Could not create LineList stream"); OleDbgOutHResult("LineList CreateStream returned", hrErr); goto error; } hrErr = CallIStorageOpenStreamA( lpStgNew, "NameTable", NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &lpOleDoc->m_lpNTStm ); if (hrErr != NOERROR) { OleDbgAssertSz(hrErr==NOERROR,"Could not create NameTable stream"); OleDbgOutHResult("NameTable CreateStream returned", hrErr); goto error; } lpOleDoc->m_lpStg = lpStgNew; // OLE2NOTE: to hold onto IStorage* pointer, we must AddRef it lpStgNew->lpVtbl->AddRef(lpStgNew); } OLEDBG_END2 return NOERROR; error: OLEDBG_END2 return ResultFromScode(E_OUTOFMEMORY); } // IPersistStorage::HandsOffStorage method STDMETHODIMP SvrDoc_PStg_HandsOffStorage(LPPERSISTSTORAGE lpThis) { LPSERVERDOC lpServerDoc = ((struct CDocPersistStorageImpl FAR*)lpThis)->lpServerDoc; LPOLEDOC lpOleDoc = (LPOLEDOC)lpServerDoc; OLEDBG_BEGIN2("SvrDoc_PStg_HandsOffStorage\r\n") /* OLE2NOTE: An embedded object must guarantee that it can save ** even in low memory situations. it must be able to ** successfully save itself without consuming any additional ** memory. this means that a server is NOT supposed to open or ** create any streams or storages when ** IPersistStorage::Save(fSameAsLoad==TRUE) is called. thus an ** embedded object should hold onto its storage and pre-open and ** hold open any streams that it will need later when it is time ** to save. Now when HandsOffStorage is called the object must ** release its storage and any streams that is holds open. ** later when SaveCompleted is called, it will be given back its ** storage. */ if (lpOleDoc->m_lpLLStm) { OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpLLStm); lpOleDoc->m_lpLLStm = NULL; } if (lpOleDoc->m_lpNTStm) { OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpNTStm); lpOleDoc->m_lpNTStm = NULL; } if (lpOleDoc->m_lpStg) { OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpStg); lpOleDoc->m_lpStg = NULL; } OLEDBG_END2 return NOERROR; } #if defined( SVR_TREATAS ) /************************************************************************* ** ServerDoc::IStdMarshalInfo interface implementation *************************************************************************/ // IStdMarshalInfo::QueryInterface method STDMETHODIMP SvrDoc_StdMshl_QueryInterface( LPSTDMARSHALINFO lpThis, REFIID riid, LPVOID FAR* lplpvObj ) { LPSERVERDOC lpServerDoc = ((struct CDocStdMarshalInfoImpl FAR*)lpThis)->lpServerDoc; return OleDoc_QueryInterface((LPOLEDOC)lpServerDoc, riid, lplpvObj); } // IStdMarshalInfo::AddRef method STDMETHODIMP_(ULONG) SvrDoc_StdMshl_AddRef(LPSTDMARSHALINFO lpThis) { LPSERVERDOC lpServerDoc = ((struct CDocStdMarshalInfoImpl FAR*)lpThis)->lpServerDoc; OleDbgAddRefMethod(lpThis, "IStdMarshalInfo"); return OleDoc_AddRef((LPOLEDOC)lpServerDoc); } // IStdMarshalInfo::Release method STDMETHODIMP_(ULONG) SvrDoc_StdMshl_Release(LPSTDMARSHALINFO lpThis) { LPSERVERDOC lpServerDoc = ((struct CDocStdMarshalInfoImpl FAR*)lpThis)->lpServerDoc; OleDbgReleaseMethod(lpThis, "IStdMarshalInfo"); return OleDoc_Release((LPOLEDOC)lpServerDoc); } // IStdMarshalInfo::GetClassForHandler STDMETHODIMP SvrDoc_StdMshl_GetClassForHandler( LPSTDMARSHALINFO lpThis, DWORD dwDestContext, LPVOID pvDestContext, LPCLSID lpClassID ) { LPSERVERDOC lpServerDoc = ((struct CDocStdMarshalInfoImpl FAR*)lpThis)->lpServerDoc; OleDbgOut2("SvrDoc_StdMshl_GetClassForHandler\r\n"); // OLE2NOTE: we only handle LOCAL marshal context. if (dwDestContext != MSHCTX_LOCAL || pvDestContext != NULL) return ResultFromScode(E_INVALIDARG); /* OLE2NOTE: we must return our REAL clsid, NOT the clsid that we ** are pretending to be if a "TreatAs" is in effect. */ *lpClassID = CLSID_APP; return NOERROR; } #endif // SVR_TREATAS /************************************************************************* ** ServerDoc Support Functions *************************************************************************/ /* ServerDoc_Init * -------------- * * Initialize the fields of a new ServerDoc object. The object is initially * not associated with a file or an (Untitled) document. This function sets * the docInitType to DOCTYPE_UNKNOWN. After calling this function the * caller should call: * 1.) OutlineDoc_InitNewFile to set the ServerDoc to (Untitled) * 2.) OutlineDoc_LoadFromFile to associate the ServerDoc with a file. * This function creates a new window for the document. * * NOTE: the window is initially created with a NIL size. it must be * sized and positioned by the caller. also the document is initially * created invisible. the caller must call OutlineDoc_ShowWindow * after sizing it to make the document window visible. */ BOOL ServerDoc_Init(LPSERVERDOC lpServerDoc, BOOL fDataTransferDoc) { lpServerDoc->m_cPseudoObj = 0; lpServerDoc->m_lpOleClientSite = NULL; lpServerDoc->m_lpOleAdviseHldr = NULL; lpServerDoc->m_lpDataAdviseHldr = NULL; // initialy doc does not have any storage lpServerDoc->m_fNoScribbleMode = FALSE; lpServerDoc->m_fSaveWithSameAsLoad = FALSE; lpServerDoc->m_szContainerApp[0] = '\0'; lpServerDoc->m_szContainerObj[0] = '\0'; lpServerDoc->m_nNextRangeNo = 0L; lpServerDoc->m_lrSrcSelOfCopy.m_nStartLine = -1; lpServerDoc->m_lrSrcSelOfCopy.m_nEndLine = -1; lpServerDoc->m_fDataChanged = FALSE; lpServerDoc->m_fSizeChanged = FALSE; lpServerDoc->m_fSendDataOnStop = FALSE; #if defined( SVR_TREATAS ) lpServerDoc->m_clsidTreatAs = CLSID_NULL; lpServerDoc->m_lpszTreatAsType = NULL; #endif // SVR_TREATAS #if defined( INPLACE_SVR ) lpServerDoc->m_hWndHatch = CreateHatchWindow( OutlineApp_GetWindow(g_lpApp), OutlineApp_GetInstance(g_lpApp) ); if (!lpServerDoc->m_hWndHatch) return FALSE; lpServerDoc->m_fInPlaceActive = FALSE; lpServerDoc->m_fInPlaceVisible = FALSE; lpServerDoc->m_fUIActive = FALSE; lpServerDoc->m_lpIPData = NULL; lpServerDoc->m_fMenuHelpMode = FALSE; // F1 pressed in menu INIT_INTERFACEIMPL( &lpServerDoc->m_OleInPlaceObject, &g_SvrDoc_OleInPlaceObjectVtbl, lpServerDoc ); INIT_INTERFACEIMPL( &lpServerDoc->m_OleInPlaceActiveObject, &g_SvrDoc_OleInPlaceActiveObjectVtbl, lpServerDoc ); #endif // INPLACE_SVR INIT_INTERFACEIMPL( &lpServerDoc->m_OleObject, &g_SvrDoc_OleObjectVtbl, lpServerDoc ); INIT_INTERFACEIMPL( &lpServerDoc->m_PersistStorage, &g_SvrDoc_PersistStorageVtbl, lpServerDoc ); #if defined( SVR_TREATAS ) INIT_INTERFACEIMPL( &lpServerDoc->m_StdMarshalInfo, &g_SvrDoc_StdMarshalInfoVtbl, lpServerDoc ); #endif // SVR_TREATAS return TRUE; } /* ServerDoc_InitNewEmbed * ---------------------- * * Initialize the ServerDoc object to be a new embedded object document. * This function sets the docInitType to DOCTYPE_EMBED. */ BOOL ServerDoc_InitNewEmbed(LPSERVERDOC lpServerDoc) { LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc; OleDbgAssert(lpOutlineDoc->m_docInitType == DOCTYPE_UNKNOWN); lpOutlineDoc->m_docInitType = DOCTYPE_EMBEDDED; /* The Window title for an embedded object is constructed as ** follows: ** - in ** ** here we construct the current document title portion of the ** name which follows the '-'. OutlineDoc_SetTitle prepends the ** " - " to the document title. */ // REVIEW: this string should be loaded from string resource wsprintf(lpOutlineDoc->m_szFileName, "%s in %s", (LPSTR)SHORTUSERTYPENAME, (LPSTR)DEFCONTAINERNAME); lpOutlineDoc->m_lpszDocTitle = lpOutlineDoc->m_szFileName; /* OLE2NOTE: an embedding should be marked as initially dirty so ** that on close we always call IOleClientSite::SaveObject. */ OutlineDoc_SetModified(lpOutlineDoc, TRUE, FALSE, FALSE); OutlineDoc_SetTitle(lpOutlineDoc, FALSE /*fMakeUpperCase*/); return TRUE; } /* ServerDoc_SendAdvise * -------------------- * * This function sends an advise notification on behalf of a specific * doc object to all its clients. */ void ServerDoc_SendAdvise( LPSERVERDOC lpServerDoc, WORD wAdvise, LPMONIKER lpmkDoc, DWORD dwAdvf ) { LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc; LPOLEDOC lpOleDoc = (LPOLEDOC)lpServerDoc; switch (wAdvise) { case OLE_ONDATACHANGE: // inform clients that the data of the object has changed if (lpOutlineDoc->m_nDisableDraw == 0) { /* drawing is currently enabled. inform clients that ** the data of the object has changed */ lpServerDoc->m_fDataChanged = FALSE; /* OLE2NOTE: we must note the time of last change ** for our object in the RunningObjectTable. ** this is used as the basis to answer ** IOleObject::IsUpToDate. we only want to note ** the change time when an actual change takes ** place. we do NOT want to set it when we are ** notifying clients of ADVF_DATAONSTOP */ if (dwAdvf == 0) OleStdNoteObjectChangeTime(lpOleDoc->m_dwRegROT); if (lpServerDoc->m_lpDataAdviseHldr) { OLEDBG_BEGIN2("IDataAdviseHolder::SendOnDataChange called\r\n"); lpServerDoc->m_lpDataAdviseHldr->lpVtbl->SendOnDataChange( lpServerDoc->m_lpDataAdviseHldr, (LPDATAOBJECT)&lpOleDoc->m_DataObject, 0, dwAdvf ); OLEDBG_END2 } #if defined( INPLACE_SVR ) /* OLE2NOTE: if the ServerDoc is currently in-place UI active, ** then is it important to renegotiate the size for the ** in-place document window BEFORE sending OnDataChange ** (which will cause the window to repaint). */ if (lpServerDoc->m_fSizeChanged) { lpServerDoc->m_fSizeChanged = FALSE; if (lpServerDoc->m_fInPlaceActive) ServerDoc_UpdateInPlaceWindowOnExtentChange(lpServerDoc); } #endif /* OLE2NOTE: we do NOT need to tell our pseudo objects to ** broadcast OnDataChange notification because ** they will do it automatically when an editing ** change in the document affects a PseudoObj. ** (see OutlineNameTable_AddLineUpdate, ** OutlineNameTable_DeleteLineUpdate, ** and ServerNameTable_EditLineUpdate) */ } else { /* drawing is currently disabled. do not send ** notifications or call ** IOleInPlaceObject::OnPosRectChange until drawing ** is re-enabled. */ } break; case OLE_ONCLOSE: // inform clients that the document is shutting down if (lpServerDoc->m_lpOleAdviseHldr) { OLEDBG_BEGIN2("IOleAdviseHolder::SendOnClose called\r\n"); lpServerDoc->m_lpOleAdviseHldr->lpVtbl->SendOnClose( lpServerDoc->m_lpOleAdviseHldr ); OLEDBG_END2 } /* OLE2NOTE: we do NOT need to tell our pseudo objects to ** broadcast OnClose notification because they will do ** it automatically when the pseudo object is closed. ** (see PseudoObj_Close) */ break; case OLE_ONSAVE: // inform clients that the object has been saved OLEDBG_BEGIN3("ServerDoc_SendAdvise ONSAVE\r\n"); if (lpServerDoc->m_lpOleAdviseHldr) { OLEDBG_BEGIN2("IOleAdviseHolder::SendOnSave called\r\n"); lpServerDoc->m_lpOleAdviseHldr->lpVtbl->SendOnSave( lpServerDoc->m_lpOleAdviseHldr ); OLEDBG_END2 } /* OLE2NOTE: inform any clients of pseudo objects ** within our document, that our document has been ** saved. */ ServerNameTable_InformAllPseudoObjectsDocSaved( (LPSERVERNAMETABLE)lpOutlineDoc->m_lpNameTable, lpmkDoc ); OLEDBG_END3 break; case OLE_ONRENAME: // inform clients that the object's name has changed OLEDBG_BEGIN3("ServerDoc_SendAdvise ONRENAME\r\n"); if (lpmkDoc && lpServerDoc->m_lpOleAdviseHldr) { OLEDBG_BEGIN2("IOleAdviseHolder::SendOnRename called\r\n"); lpServerDoc->m_lpOleAdviseHldr->lpVtbl->SendOnRename( lpServerDoc->m_lpOleAdviseHldr, lpmkDoc ); OLEDBG_END2 } OLEDBG_END3 break; } } /* ServerDoc_GetClassID ** -------------------- ** Return the class ID corresponding to the bits in the storage. ** normally this will be our application's given CLSID. but if a ** "TreateAs (aka. ActivateAs)" operation is taking place, then our ** application needs to pretend to be the class of the object that ** we are emulating. this is also the class that will be written ** into the storage. */ HRESULT ServerDoc_GetClassID(LPSERVERDOC lpServerDoc, LPCLSID lpclsid) { #if defined( SVR_TREATAS ) if (! IsEqualCLSID(&lpServerDoc->m_clsidTreatAs, &CLSID_NULL)) *lpclsid = lpServerDoc->m_clsidTreatAs; else #endif // SVR_TREATAS *lpclsid = CLSID_APP; return NOERROR; } /* ServerDoc_UpdateMenu * -------------------- * * Update menu for embedding mode. the changes include: * 1 Remove File/New and File/Open (SDI ONLY) * 2 Change File/Save As.. to File/Save Copy As.. * 3 Change File menu so it contains "Update" instead of "Save" * 4 Change File/Exit to File/Exit & Return to " */ void ServerDoc_UpdateMenu(LPSERVERDOC lpServerDoc) { char str[256]; HWND hWndMain; HMENU hMenu; OleDbgOut2("ServerDoc_UpdateMenu\r\n"); hWndMain=g_lpApp->m_hWndApp; hMenu=GetMenu(hWndMain); #if defined( SDI_VERSION ) /* SDI ONLY: Remove File/New and File/Open */ DeleteMenu(hMenu, IDM_F_NEW, MF_BYCOMMAND); DeleteMenu(hMenu, IDM_F_OPEN, MF_BYCOMMAND); #endif // Change File.Save As.. to File.Save Copy As.. */ ModifyMenu(hMenu,IDM_F_SAVEAS, MF_STRING, IDM_F_SAVEAS, "Save Copy As.."); // Change File.Save to "&Update " wsprintf(str, g_szUpdateCntrDoc, lpServerDoc->m_szContainerObj); ModifyMenu(hMenu, IDM_F_SAVE, MF_STRING, IDM_F_SAVE, str); // Change File/Exit to File/Exit & Return to " */ wsprintf(str, g_szExitNReturnToCntrDoc, lpServerDoc->m_szContainerObj); ModifyMenu(hMenu, IDM_F_EXIT, MF_STRING, IDM_F_EXIT, str); DrawMenuBar(hWndMain); } #if defined( MDI_VERSION ) // NOTE: ServerDoc_RestoreMenu is actually redundant because the // app is dying when the function is called. (In SDI, the // app will terminate when the ref counter of the server doc // is zero). However, it is important for MDI. /* ServerDoc_RestoreMenu * --------------------- * * Reset the menu to non-embedding mode */ void ServerDoc_RestoreMenu(LPSERVERDOC lpServerDoc) { LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp; HWND hWndMain; HMENU hMenu; OleDbgOut2("ServerDoc_RestoreMenu\r\n"); hWndMain = lpOutlineApp->m_hWndApp; hMenu = GetMenu(hWndMain); /* Add back File/New, File/Open.. and File/Save */ InsertMenu(hMenu, IDM_F_SAVEAS, MF_BYCOMMAND | MF_ENABLED | MF_STRING, IDM_F_NEW, "&New"); InsertMenu(hMenu, IDM_F_SAVEAS, MF_BYCOMMAND | MF_ENABLED | MF_STRING, IDM_F_OPEN, "&Open..."); /* Change File menu so it contains "Save As..." instead of */ /* "Save Copy As..." */ ModifyMenu(hMenu, IDM_F_SAVEAS, MF_STRING, IDM_F_SAVEAS, "Save &As.."); /* Change File menu so it contains "Save" instead of "Update" */ ModifyMenu(hMenu, IDM_F_SAVE, MF_STRING, IDM_F_SAVE, "&Save"); /* Change File menu so it contains "Exit" */ /* instead of just "Exit & Return to " */ ModifyMenu(hMenu, IDM_F_EXIT, MF_STRING, IDM_F_EXIT, "E&xit"); DrawMenuBar (hWndMain); } #endif // MDI_VERSION