Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2018 lines
57 KiB

/*************************************************************************
**
** 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