|
|
/*************************************************************************
** ** 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: ** <server app name> - <obj short type> in <cont. doc name> ** ** here we construct the current document title portion of the ** name which follows the '-'. OutlineDoc_SetTitle prepends the ** "<server app name> - " 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 <client doc>" */ 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: ** <server app name> - <obj short type> in <cont. doc name> ** ** here we construct the current document title portion of the ** name which follows the '-'. OutlineDoc_SetTitle prepends the ** "<server app name> - " 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 <client doc>" */ 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 <container doc>"
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 <container doc>" */
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 <client doc>" */ ModifyMenu(hMenu, IDM_F_EXIT, MF_STRING, IDM_F_EXIT, "E&xit");
DrawMenuBar (hWndMain); }
#endif // MDI_VERSION
|