1180 lines
37 KiB
C
1180 lines
37 KiB
C
|
/*************************************************************************
|
|||
|
**
|
|||
|
** OLE 2 Server Sample Code
|
|||
|
**
|
|||
|
** oledoc.c
|
|||
|
**
|
|||
|
** This file contains general OleDoc methods and related support
|
|||
|
** functions. OleDoc implementation is used by both the Container
|
|||
|
** versions and the Server (Object) versions of the Outline Sample.
|
|||
|
**
|
|||
|
** This file includes general support for the following:
|
|||
|
** 1. show/hide doc window
|
|||
|
** 2. QueryInterface, AddRef, Release
|
|||
|
** 3. document locking (calls CoLockObjectExternal)
|
|||
|
** 4. document shutdown (Close, Destroy)
|
|||
|
** 5. clipboard support
|
|||
|
**
|
|||
|
** OleDoc Object
|
|||
|
** exposed interfaces:
|
|||
|
** IUnknown
|
|||
|
** IPersistFile
|
|||
|
** IOleItemContainer
|
|||
|
** IDataObject
|
|||
|
**
|
|||
|
** (c) Copyright Microsoft Corp. 1992 - 1993 All Rights Reserved
|
|||
|
**
|
|||
|
*************************************************************************/
|
|||
|
|
|||
|
|
|||
|
#include "outline.h"
|
|||
|
|
|||
|
OLEDBGDATA
|
|||
|
|
|||
|
extern LPOUTLINEAPP g_lpApp;
|
|||
|
|
|||
|
extern IUnknownVtbl g_OleDoc_UnknownVtbl;
|
|||
|
extern IPersistFileVtbl g_OleDoc_PersistFileVtbl;
|
|||
|
extern IOleItemContainerVtbl g_OleDoc_OleItemContainerVtbl;
|
|||
|
extern IExternalConnectionVtbl g_OleDoc_ExternalConnectionVtbl;
|
|||
|
extern IDataObjectVtbl g_OleDoc_DataObjectVtbl;
|
|||
|
|
|||
|
#if defined( USE_DRAGDROP )
|
|||
|
extern IDropTargetVtbl g_OleDoc_DropTargetVtbl;
|
|||
|
extern IDropSourceVtbl g_OleDoc_DropSourceVtbl;
|
|||
|
#endif // USE_DRAGDROP
|
|||
|
|
|||
|
#if defined( INPLACE_CNTR )
|
|||
|
extern BOOL g_fInsideOutContainer;
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
/* OleDoc_Init
|
|||
|
* -----------
|
|||
|
*
|
|||
|
* Initialize the fields of a new OleDoc 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.) Doc_InitNewFile to set the OleDoc to (Untitled)
|
|||
|
* 2.) Doc_LoadFromFile to associate the OleDoc 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 OleDoc_Init(LPOLEDOC lpOleDoc, BOOL fDataTransferDoc)
|
|||
|
{
|
|||
|
LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;
|
|||
|
LPLINELIST lpLL = (LPLINELIST)&((LPOUTLINEDOC)lpOleDoc)->m_LineList;
|
|||
|
|
|||
|
lpOleDoc->m_cRef = 0;
|
|||
|
lpOleDoc->m_dwStrongExtConn = 0;
|
|||
|
#if defined( _DEBUG )
|
|||
|
lpOleDoc->m_cCntrLock = 0;
|
|||
|
#endif
|
|||
|
lpOleDoc->m_lpStg = NULL;
|
|||
|
lpOleDoc->m_lpLLStm = NULL;
|
|||
|
lpOleDoc->m_lpNTStm = NULL;
|
|||
|
lpOleDoc->m_dwRegROT = 0;
|
|||
|
lpOleDoc->m_lpFileMoniker = NULL;
|
|||
|
lpOleDoc->m_fLinkSourceAvail = FALSE;
|
|||
|
lpOleDoc->m_lpSrcDocOfCopy = NULL;
|
|||
|
lpOleDoc->m_fObjIsClosing = FALSE;
|
|||
|
lpOleDoc->m_fObjIsDestroying = FALSE;
|
|||
|
lpOleDoc->m_fUpdateEditMenu = FALSE;
|
|||
|
|
|||
|
#if defined( USE_DRAGDROP )
|
|||
|
lpOleDoc->m_dwTimeEnterScrollArea = 0L;
|
|||
|
lpOleDoc->m_dwNextScrollTime = 0L;
|
|||
|
lpOleDoc->m_dwLastScrollDir = SCROLLDIR_NULL;
|
|||
|
lpOleDoc->m_fRegDragDrop = FALSE;
|
|||
|
lpOleDoc->m_fLocalDrag = FALSE;
|
|||
|
lpOleDoc->m_fCanDropCopy = FALSE;
|
|||
|
lpOleDoc->m_fCanDropLink = FALSE;
|
|||
|
lpOleDoc->m_fLocalDrop = FALSE;
|
|||
|
lpOleDoc->m_fDragLeave = FALSE;
|
|||
|
lpOleDoc->m_fPendingDrag = FALSE;
|
|||
|
#endif
|
|||
|
#if defined( INPLACE_SVR ) || defined( INPLACE_CNTR )
|
|||
|
lpOleDoc->m_fCSHelpMode = FALSE; // Shift-F1 context
|
|||
|
// sensitive help mode
|
|||
|
#endif
|
|||
|
|
|||
|
INIT_INTERFACEIMPL(
|
|||
|
&lpOleDoc->m_Unknown,
|
|||
|
&g_OleDoc_UnknownVtbl,
|
|||
|
lpOleDoc
|
|||
|
);
|
|||
|
|
|||
|
INIT_INTERFACEIMPL(
|
|||
|
&lpOleDoc->m_PersistFile,
|
|||
|
&g_OleDoc_PersistFileVtbl,
|
|||
|
lpOleDoc
|
|||
|
);
|
|||
|
|
|||
|
INIT_INTERFACEIMPL(
|
|||
|
&lpOleDoc->m_OleItemContainer,
|
|||
|
&g_OleDoc_OleItemContainerVtbl,
|
|||
|
lpOleDoc
|
|||
|
);
|
|||
|
|
|||
|
INIT_INTERFACEIMPL(
|
|||
|
&lpOleDoc->m_ExternalConnection,
|
|||
|
&g_OleDoc_ExternalConnectionVtbl,
|
|||
|
lpOleDoc
|
|||
|
);
|
|||
|
|
|||
|
INIT_INTERFACEIMPL(
|
|||
|
&lpOleDoc->m_DataObject,
|
|||
|
&g_OleDoc_DataObjectVtbl,
|
|||
|
lpOleDoc
|
|||
|
);
|
|||
|
|
|||
|
#if defined( USE_DRAGDROP )
|
|||
|
INIT_INTERFACEIMPL(
|
|||
|
&lpOleDoc->m_DropSource,
|
|||
|
&g_OleDoc_DropSourceVtbl,
|
|||
|
lpOleDoc
|
|||
|
);
|
|||
|
|
|||
|
INIT_INTERFACEIMPL(
|
|||
|
&lpOleDoc->m_DropTarget,
|
|||
|
&g_OleDoc_DropTargetVtbl,
|
|||
|
lpOleDoc
|
|||
|
);
|
|||
|
#endif // USE_DRAGDROP
|
|||
|
|
|||
|
/*
|
|||
|
** OLE2NOTE: each user level document addref's the app object in
|
|||
|
** order to guarentee that the app does not shut down while the
|
|||
|
** doc is still open.
|
|||
|
*/
|
|||
|
|
|||
|
// OLE2NOTE: data transfer documents should not hold the app alive
|
|||
|
if (! fDataTransferDoc)
|
|||
|
OleApp_DocLockApp(lpOleApp);
|
|||
|
|
|||
|
#if defined( OLE_SERVER )
|
|||
|
/* OLE2NOTE: perform initialization specific for an OLE server */
|
|||
|
if (! ServerDoc_Init((LPSERVERDOC)lpOleDoc, fDataTransferDoc))
|
|||
|
return FALSE;
|
|||
|
#endif
|
|||
|
#if defined( OLE_CNTR )
|
|||
|
|
|||
|
/* OLE2NOTE: perform initialization specific for an OLE container */
|
|||
|
if (! ContainerDoc_Init((LPCONTAINERDOC)lpOleDoc, fDataTransferDoc))
|
|||
|
return FALSE;
|
|||
|
#endif
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* OleDoc_InitNewFile
|
|||
|
* ------------------
|
|||
|
*
|
|||
|
* Initialize the document to be a new (Untitled) document.
|
|||
|
* This function sets the docInitType to DOCTYPE_NEW.
|
|||
|
*
|
|||
|
* OLE2NOTE: if this is a visible user document then generate a unique
|
|||
|
* untitled name that we can use to register in the RunningObjectTable.
|
|||
|
* We need a unique name so that clients can link to data in this document
|
|||
|
* even when the document is in the un-saved (untitled) state. it would be
|
|||
|
* ambiguous to register two documents titled "Outline1" in the ROT. we
|
|||
|
* thus generate the lowest numbered document that is not already
|
|||
|
* registered in the ROT.
|
|||
|
*/
|
|||
|
BOOL OleDoc_InitNewFile(LPOLEDOC lpOleDoc)
|
|||
|
{
|
|||
|
LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;
|
|||
|
|
|||
|
static UINT uUnique = 1;
|
|||
|
|
|||
|
OleDbgAssert(lpOutlineDoc->m_docInitType == DOCTYPE_UNKNOWN);
|
|||
|
|
|||
|
#if defined( OLE_CNTR )
|
|||
|
{
|
|||
|
LPCONTAINERDOC lpContainerDoc = (LPCONTAINERDOC)lpOleDoc;
|
|||
|
#if defined( _DEBUG )
|
|||
|
OleDbgAssertSz(lpOleDoc->m_lpStg == NULL,
|
|||
|
"Setting to untitled with current file open"
|
|||
|
);
|
|||
|
#endif
|
|||
|
|
|||
|
/* Create a temp, (delete-on-release) file base storage
|
|||
|
** for the untitled document.
|
|||
|
*/
|
|||
|
lpOleDoc->m_lpStg = OleStdCreateRootStorage(
|
|||
|
NULL,
|
|||
|
STGM_SHARE_EXCLUSIVE
|
|||
|
);
|
|||
|
if (! lpOleDoc->m_lpStg) return FALSE;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
lpOutlineDoc->m_docInitType = DOCTYPE_NEW;
|
|||
|
|
|||
|
if (! lpOutlineDoc->m_fDataTransferDoc) {
|
|||
|
/* OLE2NOTE: choose a unique name for a Moniker so that
|
|||
|
** potential clients can link to our new, untitled document.
|
|||
|
** if links are established (and currently are connected),
|
|||
|
** then they will be notified that we have been renamed when
|
|||
|
** this document is saved to a file.
|
|||
|
*/
|
|||
|
|
|||
|
lpOleDoc->m_fLinkSourceAvail = TRUE;
|
|||
|
|
|||
|
// REVIEW: should load UNTITLED string from string resource
|
|||
|
OleStdCreateTempFileMoniker(
|
|||
|
UNTITLED,
|
|||
|
(UINT FAR*)&uUnique,
|
|||
|
lpOutlineDoc->m_szFileName,
|
|||
|
&lpOleDoc->m_lpFileMoniker
|
|||
|
);
|
|||
|
|
|||
|
OLEDBG_BEGIN3("OleStdRegisterAsRunning called\r\n")
|
|||
|
OleStdRegisterAsRunning(
|
|||
|
(LPUNKNOWN)&lpOleDoc->m_PersistFile,
|
|||
|
(LPMONIKER)lpOleDoc->m_lpFileMoniker,
|
|||
|
&lpOleDoc->m_dwRegROT
|
|||
|
);
|
|||
|
OLEDBG_END3
|
|||
|
|
|||
|
lpOutlineDoc->m_lpszDocTitle = lpOutlineDoc->m_szFileName;
|
|||
|
OutlineDoc_SetTitle(lpOutlineDoc, FALSE /*fMakeUpperCase*/);
|
|||
|
} else {
|
|||
|
lstrcpy(lpOutlineDoc->m_szFileName, UNTITLED);
|
|||
|
lpOutlineDoc->m_lpszDocTitle = lpOutlineDoc->m_szFileName;
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* OleDoc_ShowWindow
|
|||
|
* -----------------
|
|||
|
*
|
|||
|
* Show the window of the document to the user.
|
|||
|
* make sure app window is visible and bring the document to the top.
|
|||
|
* if the document is a file-based document or a new untitled
|
|||
|
* document, give the user the control over the life-time of the doc.
|
|||
|
*/
|
|||
|
void OleDoc_ShowWindow(LPOLEDOC lpOleDoc)
|
|||
|
{
|
|||
|
LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
|
|||
|
LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;
|
|||
|
LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;
|
|||
|
LPLINELIST lpLL = (LPLINELIST)&((LPOUTLINEDOC)lpOleDoc)->m_LineList;
|
|||
|
#if defined( OLE_SERVER )
|
|||
|
LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOleDoc;
|
|||
|
#endif // OLE_SERVER
|
|||
|
|
|||
|
OLEDBG_BEGIN3("OleDoc_ShowWindow\r\n")
|
|||
|
|
|||
|
/* OLE2NOTE: while the document is visible, we do NOT want it to be
|
|||
|
** prematurely destroyed when a linking client disconnects. thus
|
|||
|
** we must inform OLE to hold an external lock on our document.
|
|||
|
** this arranges that OLE holds at least 1 reference to our
|
|||
|
** document that will NOT be released until we release this
|
|||
|
** external lock. later, when the document window is hidden, we
|
|||
|
** will release this external lock.
|
|||
|
*/
|
|||
|
if (! IsWindowVisible(lpOutlineDoc->m_hWndDoc))
|
|||
|
OleDoc_Lock(lpOleDoc, TRUE /* fLock */, 0 /* not applicable */);
|
|||
|
|
|||
|
#if defined( USE_DRAGDROP )
|
|||
|
/* OLE2NOTE: since our window is now being made visible, we will
|
|||
|
** register our window as a potential drop target. when the
|
|||
|
** window is hidden there is no reason to be registered as a
|
|||
|
** drop target.
|
|||
|
*/
|
|||
|
if (! lpOleDoc->m_fRegDragDrop) {
|
|||
|
OLEDBG_BEGIN2("RegisterDragDrop called\r\n")
|
|||
|
RegisterDragDrop(
|
|||
|
LineList_GetWindow(lpLL),
|
|||
|
(LPDROPTARGET)&lpOleDoc->m_DropTarget
|
|||
|
);
|
|||
|
OLEDBG_END2
|
|||
|
lpOleDoc->m_fRegDragDrop = TRUE;
|
|||
|
}
|
|||
|
#endif // USE_DRAGDROP
|
|||
|
|
|||
|
#if defined( USE_FRAMETOOLS )
|
|||
|
{
|
|||
|
/* OLE2NOTE: we need to enable our frame level tools
|
|||
|
*/
|
|||
|
FrameTools_Enable(lpOutlineDoc->m_lpFrameTools, TRUE);
|
|||
|
}
|
|||
|
#endif // USE_FRAMETOOLS
|
|||
|
|
|||
|
#if defined( OLE_SERVER )
|
|||
|
|
|||
|
if (lpOutlineDoc->m_docInitType == DOCTYPE_EMBEDDED &&
|
|||
|
lpServerDoc->m_lpOleClientSite != NULL) {
|
|||
|
|
|||
|
/* OLE2NOTE: we must also ask our container to show itself if
|
|||
|
** it is not already visible and to scroll us into view. we
|
|||
|
** must make sure to call this BEFORE showing our server's
|
|||
|
** window and taking focus. we do not want our container's
|
|||
|
** window to end up on top.
|
|||
|
*/
|
|||
|
OLEDBG_BEGIN2("IOleClientSite::ShowObject called\r\n");
|
|||
|
lpServerDoc->m_lpOleClientSite->lpVtbl->ShowObject(
|
|||
|
lpServerDoc->m_lpOleClientSite
|
|||
|
);
|
|||
|
OLEDBG_END2
|
|||
|
|
|||
|
/* OLE2NOTE: if we are an embedded object and we are not
|
|||
|
** in-place active in our containers window, we must inform our
|
|||
|
** embedding container that our window is opening.
|
|||
|
** the container must now hatch our object.
|
|||
|
*/
|
|||
|
|
|||
|
#if defined( INPLACE_SVR )
|
|||
|
if (! lpServerDoc->m_fInPlaceActive)
|
|||
|
#endif
|
|||
|
{
|
|||
|
OLEDBG_BEGIN2("IOleClientSite::OnShowWindow(TRUE) called\r\n");
|
|||
|
lpServerDoc->m_lpOleClientSite->lpVtbl->OnShowWindow(
|
|||
|
lpServerDoc->m_lpOleClientSite,
|
|||
|
TRUE
|
|||
|
);
|
|||
|
OLEDBG_END2
|
|||
|
}
|
|||
|
|
|||
|
/* OLE2NOTE: the life-time of our document is controlled by our
|
|||
|
** client and NOT by the user. we are not an independent
|
|||
|
** file-level object. we simply want to show our window here.
|
|||
|
**
|
|||
|
** if we are not in-place active (ie. we are opening
|
|||
|
** our own window), we must make sure our main app window is
|
|||
|
** visible. we do not, however, want to give the user
|
|||
|
** control of the App window; we do not want OleApp_ShowWindow
|
|||
|
** to call OleApp_Lock on behalf of the user.
|
|||
|
*/
|
|||
|
if (! IsWindowVisible(lpOutlineApp->m_hWndApp) ||
|
|||
|
IsIconic(lpOutlineApp->m_hWndApp)) {
|
|||
|
#if defined( INPLACE_SVR )
|
|||
|
if (! ((LPSERVERDOC)lpOleDoc)->m_fInPlaceActive)
|
|||
|
#endif
|
|||
|
OleApp_ShowWindow(lpOleApp, FALSE /* fGiveUserCtrl */);
|
|||
|
SetFocus(lpOutlineDoc->m_hWndDoc);
|
|||
|
}
|
|||
|
|
|||
|
} else
|
|||
|
#endif // OLE_SERVER
|
|||
|
|
|||
|
{ // DOCTYPE_NEW || DOCTYPE_FROMFILE
|
|||
|
|
|||
|
// we must make sure our app window is visible
|
|||
|
OleApp_ShowWindow(lpOleApp, TRUE /* fGiveUserCtrl */);
|
|||
|
}
|
|||
|
|
|||
|
// make document window visible and make sure it is not minimized
|
|||
|
ShowWindow(lpOutlineDoc->m_hWndDoc, SW_SHOWNORMAL);
|
|||
|
SetFocus(lpOutlineDoc->m_hWndDoc);
|
|||
|
|
|||
|
OLEDBG_END3
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* OleDoc_HideWindow
|
|||
|
* -----------------
|
|||
|
*
|
|||
|
* Hide the window of the document from the user.
|
|||
|
* take away the control of the document by the user.
|
|||
|
*/
|
|||
|
void OleDoc_HideWindow(LPOLEDOC lpOleDoc, BOOL fShutdown)
|
|||
|
{
|
|||
|
LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;
|
|||
|
LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;
|
|||
|
LPLINELIST lpLL = (LPLINELIST)&((LPOUTLINEDOC)lpOleDoc)->m_LineList;
|
|||
|
|
|||
|
if (! IsWindowVisible(lpOutlineDoc->m_hWndDoc))
|
|||
|
return; // already visible
|
|||
|
|
|||
|
OLEDBG_BEGIN3("OleDoc_HideWindow\r\n")
|
|||
|
|
|||
|
#if defined( USE_DRAGDROP )
|
|||
|
// The document's window is being hidden, revoke it as a DropTarget
|
|||
|
if (lpOleDoc->m_fRegDragDrop) {
|
|||
|
OLEDBG_BEGIN2("RevokeDragDrop called\r\n");
|
|||
|
RevokeDragDrop(LineList_GetWindow(lpLL));
|
|||
|
OLEDBG_END2
|
|||
|
|
|||
|
lpOleDoc->m_fRegDragDrop = FALSE ;
|
|||
|
}
|
|||
|
#endif // USE_DRAGDROP
|
|||
|
|
|||
|
/* OLE2NOTE: the document is now being hidden, so we must release
|
|||
|
** the external lock made when the document was made visible.
|
|||
|
** if this is a shutdown situation (fShutdown==TRUE), then OLE
|
|||
|
** is instructed to release our document. if this is that last
|
|||
|
** external lock on our document, thus enabling our document to
|
|||
|
** complete its shutdown operation. If This is not a shutdown
|
|||
|
** situation (eg. in-place server hiding its window when
|
|||
|
** UIDeactivating or IOleObject::DoVerb(OLEVERB_HIDE) is called),
|
|||
|
** then OLE is told to NOT immediately release the document.
|
|||
|
** this leaves the document in an unstable state where the next
|
|||
|
** Lock/Unlock sequence will shut the document down (eg. a
|
|||
|
** linking client connecting and disconnecting).
|
|||
|
*/
|
|||
|
if (IsWindowVisible(lpOutlineDoc->m_hWndDoc))
|
|||
|
OleDoc_Lock(lpOleDoc, FALSE /* fLock */, fShutdown);
|
|||
|
|
|||
|
ShowWindow(((LPOUTLINEDOC)lpOleDoc)->m_hWndDoc, SW_HIDE);
|
|||
|
|
|||
|
#if defined( OLE_SERVER )
|
|||
|
{
|
|||
|
LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOleDoc;
|
|||
|
|
|||
|
/* OLE2NOTE: if we are an embedded object and we are not
|
|||
|
** in-place active, we must inform our
|
|||
|
** embedding container that our window is hiding (closing
|
|||
|
** from the user's perspective). the container must now
|
|||
|
** un-hatch our object.
|
|||
|
*/
|
|||
|
if (lpServerDoc->m_lpOleClientSite != NULL
|
|||
|
#if defined( INPLACE_SVR )
|
|||
|
&& !lpServerDoc->m_fInPlaceVisible
|
|||
|
#endif
|
|||
|
) {
|
|||
|
OLEDBG_BEGIN2("IOleClientSite::OnShowWindow(FALSE) called\r\n");
|
|||
|
lpServerDoc->m_lpOleClientSite->lpVtbl->OnShowWindow(
|
|||
|
lpServerDoc->m_lpOleClientSite,
|
|||
|
FALSE
|
|||
|
);
|
|||
|
OLEDBG_END2
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
/* OLE2NOTE: if there are no more documents visible to the user.
|
|||
|
** and the app itself is not under user control, then
|
|||
|
** it has no reason to stay visible. we thus should hide the
|
|||
|
** app. we can not directly destroy the app, because it may be
|
|||
|
** validly being used programatically by another client
|
|||
|
** application and should remain running. it should simply be
|
|||
|
** hidded from the user.
|
|||
|
*/
|
|||
|
OleApp_HideIfNoReasonToStayVisible(lpOleApp);
|
|||
|
OLEDBG_END3
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* OleDoc_Lock
|
|||
|
** -----------
|
|||
|
** Lock/Unlock the Doc object. if the last lock is unlocked and
|
|||
|
** fLastUnlockReleases == TRUE, then the Doc object will shut down
|
|||
|
** (ie. it will recieve its final release and its refcnt will go to 0).
|
|||
|
*/
|
|||
|
HRESULT OleDoc_Lock(LPOLEDOC lpOleDoc, BOOL fLock, BOOL fLastUnlockReleases)
|
|||
|
{
|
|||
|
HRESULT hrErr;
|
|||
|
|
|||
|
#if defined( _DEBUG )
|
|||
|
if (fLock) {
|
|||
|
OLEDBG_BEGIN2("CoLockObjectExternal(lpDoc,TRUE) called\r\n")
|
|||
|
} else {
|
|||
|
if (fLastUnlockReleases)
|
|||
|
OLEDBG_BEGIN2("CoLockObjectExternal(lpDoc,FALSE,TRUE) called\r\n")
|
|||
|
else
|
|||
|
OLEDBG_BEGIN2("CoLockObjectExternal(lpDoc,FALSE,FALSE) called\r\n")
|
|||
|
}
|
|||
|
#endif // _DEBUG
|
|||
|
|
|||
|
hrErr = CoLockObjectExternal(
|
|||
|
(LPUNKNOWN)&lpOleDoc->m_Unknown, fLock, fLastUnlockReleases);
|
|||
|
|
|||
|
OLEDBG_END2
|
|||
|
return hrErr;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* OleDoc_AddRef
|
|||
|
** -------------
|
|||
|
**
|
|||
|
** increment the ref count of the document object.
|
|||
|
**
|
|||
|
** Returns the new ref count on the object
|
|||
|
*/
|
|||
|
ULONG OleDoc_AddRef(LPOLEDOC lpOleDoc)
|
|||
|
{
|
|||
|
++lpOleDoc->m_cRef;
|
|||
|
|
|||
|
#if defined( _DEBUG )
|
|||
|
OleDbgOutRefCnt4(
|
|||
|
"OleDoc_AddRef: cRef++\r\n",
|
|||
|
lpOleDoc,
|
|||
|
lpOleDoc->m_cRef
|
|||
|
);
|
|||
|
#endif
|
|||
|
return lpOleDoc->m_cRef;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* OleDoc_Release
|
|||
|
** --------------
|
|||
|
**
|
|||
|
** decrement the ref count of the document object.
|
|||
|
** if the ref count goes to 0, then the document is destroyed.
|
|||
|
**
|
|||
|
** Returns the remaining ref count on the object
|
|||
|
*/
|
|||
|
ULONG OleDoc_Release (LPOLEDOC lpOleDoc)
|
|||
|
{
|
|||
|
ULONG cRef;
|
|||
|
LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
|
|||
|
LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;
|
|||
|
|
|||
|
/*********************************************************************
|
|||
|
** OLE2NOTE: when the obj refcnt == 0, then destroy the object. **
|
|||
|
** otherwise the object is still in use. **
|
|||
|
*********************************************************************/
|
|||
|
|
|||
|
cRef = --lpOleDoc->m_cRef;
|
|||
|
|
|||
|
#if defined( _DEBUG )
|
|||
|
OleDbgAssertSz (lpOleDoc->m_cRef >= 0, "Release called with cRef == 0");
|
|||
|
|
|||
|
OleDbgOutRefCnt4(
|
|||
|
"OleDoc_Release: cRef--\r\n", lpOleDoc, cRef);
|
|||
|
#endif
|
|||
|
if (cRef == 0)
|
|||
|
OutlineDoc_Destroy((LPOUTLINEDOC)lpOleDoc);
|
|||
|
|
|||
|
return cRef;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* OleDoc_QueryInterface
|
|||
|
** ---------------------
|
|||
|
**
|
|||
|
** Retrieve a pointer to an interface on the document object.
|
|||
|
**
|
|||
|
** OLE2NOTE: this function will AddRef the ref cnt of the object.
|
|||
|
**
|
|||
|
** Returns S_OK if interface is successfully retrieved.
|
|||
|
** E_NOINTERFACE if the interface is not supported
|
|||
|
*/
|
|||
|
HRESULT OleDoc_QueryInterface(
|
|||
|
LPOLEDOC lpOleDoc,
|
|||
|
REFIID riid,
|
|||
|
LPVOID FAR* lplpvObj
|
|||
|
)
|
|||
|
{
|
|||
|
LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;
|
|||
|
SCODE sc = E_NOINTERFACE;
|
|||
|
|
|||
|
/* OLE2NOTE: we must make sure to set all out ptr parameters to NULL. */
|
|||
|
*lplpvObj = NULL;
|
|||
|
|
|||
|
if (IsEqualIID(riid, &IID_IUnknown)) {
|
|||
|
OleDbgOut4("OleDoc_QueryInterface: IUnknown* RETURNED\r\n");
|
|||
|
|
|||
|
*lplpvObj = (LPVOID) &lpOleDoc->m_Unknown;
|
|||
|
OleDoc_AddRef(lpOleDoc);
|
|||
|
sc = S_OK;
|
|||
|
}
|
|||
|
else if(lpOutlineDoc->m_fDataTransferDoc
|
|||
|
&& IsEqualIID(riid, &IID_IDataObject)) {
|
|||
|
OleDbgOut4("OleDoc_QueryInterface: IDataObject* RETURNED\r\n");
|
|||
|
|
|||
|
*lplpvObj = (LPVOID) &lpOleDoc->m_DataObject;
|
|||
|
OleDoc_AddRef(lpOleDoc);
|
|||
|
sc = S_OK;
|
|||
|
}
|
|||
|
|
|||
|
/* OLE2NOTE: if this document is a DataTransferDocument used to
|
|||
|
** support a clipboard or drag/drop operation, then it should
|
|||
|
** only expose IUnknown, IDataObject, and IDropSource
|
|||
|
** interfaces. if the document is a normal user document, then
|
|||
|
** we will also continue to consider our other interfaces.
|
|||
|
*/
|
|||
|
if (lpOutlineDoc->m_fDataTransferDoc)
|
|||
|
goto done;
|
|||
|
|
|||
|
if(IsEqualIID(riid,&IID_IPersist) || IsEqualIID(riid,&IID_IPersistFile)) {
|
|||
|
OleDbgOut4("OleDoc_QueryInterface: IPersistFile* RETURNED\r\n");
|
|||
|
|
|||
|
*lplpvObj = (LPVOID) &lpOleDoc->m_PersistFile;
|
|||
|
OleDoc_AddRef(lpOleDoc);
|
|||
|
sc = S_OK;
|
|||
|
}
|
|||
|
else if(IsEqualIID(riid, &IID_IOleItemContainer) ||
|
|||
|
IsEqualIID(riid, &IID_IOleContainer) ||
|
|||
|
IsEqualIID(riid, &IID_IParseDisplayName) ) {
|
|||
|
OleDbgOut4("OleDoc_QueryInterface: IOleItemContainer* RETURNED\r\n");
|
|||
|
|
|||
|
*lplpvObj = (LPVOID) &lpOleDoc->m_OleItemContainer;
|
|||
|
OleDoc_AddRef(lpOleDoc);
|
|||
|
sc = S_OK;
|
|||
|
}
|
|||
|
else if(IsEqualIID(riid, &IID_IExternalConnection)) {
|
|||
|
OleDbgOut4("OleDoc_QueryInterface: IExternalConnection* RETURNED\r\n");
|
|||
|
|
|||
|
*lplpvObj = (LPVOID) &lpOleDoc->m_ExternalConnection;
|
|||
|
OleDoc_AddRef(lpOleDoc);
|
|||
|
sc = S_OK;
|
|||
|
}
|
|||
|
|
|||
|
#if defined( USE_DRAGDROP )
|
|||
|
else if(IsEqualIID(riid, &IID_IDropTarget)) {
|
|||
|
OleDbgOut4("OleDoc_QueryInterface: IDropTarget* RETURNED\r\n");
|
|||
|
|
|||
|
*lplpvObj = (LPVOID) &lpOleDoc->m_DropTarget;
|
|||
|
OleDoc_AddRef(lpOleDoc);
|
|||
|
sc = S_OK;
|
|||
|
}
|
|||
|
else if(IsEqualIID(riid, &IID_IDropSource)) {
|
|||
|
OleDbgOut4("OleDoc_QueryInterface: IDropSource* RETURNED\r\n");
|
|||
|
|
|||
|
*lplpvObj = (LPVOID) &lpOleDoc->m_DropSource;
|
|||
|
OleDoc_AddRef(lpOleDoc);
|
|||
|
sc = S_OK;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
#if defined( OLE_CNTR )
|
|||
|
else if (IsEqualIID(riid, &IID_IOleUILinkContainer)) {
|
|||
|
OleDbgOut4("OleDoc_QueryInterface: IOleUILinkContainer* RETURNED\r\n");
|
|||
|
|
|||
|
*lplpvObj=(LPVOID)&((LPCONTAINERDOC)lpOleDoc)->m_OleUILinkContainer;
|
|||
|
OleDoc_AddRef(lpOleDoc);
|
|||
|
sc = S_OK;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
#if defined( OLE_SERVER )
|
|||
|
|
|||
|
/* OLE2NOTE: if OLE server version, than also offer the server
|
|||
|
** specific interfaces: IOleObject and IPersistStorage.
|
|||
|
*/
|
|||
|
else if (IsEqualIID(riid, &IID_IOleObject)) {
|
|||
|
OleDbgOut4("OleDoc_QueryInterface: IOleObject* RETURNED\r\n");
|
|||
|
|
|||
|
*lplpvObj = (LPVOID) &((LPSERVERDOC)lpOleDoc)->m_OleObject;
|
|||
|
OleDoc_AddRef(lpOleDoc);
|
|||
|
sc = S_OK;
|
|||
|
}
|
|||
|
else if(IsEqualIID(riid, &IID_IPersistStorage)) {
|
|||
|
OleDbgOut4("OleDoc_QueryInterface: IPersistStorage* RETURNED\r\n");
|
|||
|
|
|||
|
*lplpvObj = (LPVOID) &((LPSERVERDOC)lpOleDoc)->m_PersistStorage;
|
|||
|
OleDoc_AddRef(lpOleDoc);
|
|||
|
sc = S_OK;
|
|||
|
}
|
|||
|
else if(IsEqualIID(riid, &IID_IDataObject)) {
|
|||
|
OleDbgOut4("OleDoc_QueryInterface: IDataObject* RETURNED\r\n");
|
|||
|
|
|||
|
*lplpvObj = (LPVOID) &lpOleDoc->m_DataObject;
|
|||
|
OleDoc_AddRef(lpOleDoc);
|
|||
|
sc = S_OK;
|
|||
|
}
|
|||
|
|
|||
|
#if defined( SVR_TREATAS )
|
|||
|
else if(IsEqualIID(riid, &IID_IStdMarshalInfo)) {
|
|||
|
OleDbgOut4("OleDoc_QueryInterface: IStdMarshalInfo* RETURNED\r\n");
|
|||
|
|
|||
|
*lplpvObj = (LPVOID) &((LPSERVERDOC)lpOleDoc)->m_StdMarshalInfo;
|
|||
|
OleDoc_AddRef(lpOleDoc);
|
|||
|
sc = S_OK;
|
|||
|
}
|
|||
|
#endif // SVR_TREATAS
|
|||
|
|
|||
|
#if defined( INPLACE_SVR )
|
|||
|
else if (IsEqualIID(riid, &IID_IOleWindow) ||
|
|||
|
IsEqualIID(riid, &IID_IOleInPlaceObject)) {
|
|||
|
OleDbgOut4("OleDoc_QueryInterface: IOleInPlaceObject* RETURNED\r\n");
|
|||
|
|
|||
|
*lplpvObj = (LPVOID) &((LPSERVERDOC)lpOleDoc)->m_OleInPlaceObject;
|
|||
|
OleDoc_AddRef(lpOleDoc);
|
|||
|
sc = S_OK;
|
|||
|
}
|
|||
|
#endif // INPLACE_SVR
|
|||
|
#endif // OLE_SERVER
|
|||
|
|
|||
|
done:
|
|||
|
OleDbgQueryInterfaceMethod(*lplpvObj);
|
|||
|
|
|||
|
return ResultFromScode(sc);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* OleDoc_Close
|
|||
|
* ------------
|
|||
|
*
|
|||
|
* Close the document.
|
|||
|
* This functions performs the actions that are in common to all
|
|||
|
* document types which derive from OleDoc (eg. ContainerDoc and
|
|||
|
* ServerDoc) which are required to close a document.
|
|||
|
*
|
|||
|
* Returns:
|
|||
|
* FALSE -- user canceled the closing of the doc.
|
|||
|
* TRUE -- the doc was successfully closed
|
|||
|
*/
|
|||
|
|
|||
|
BOOL OleDoc_Close(LPOLEDOC lpOleDoc, DWORD dwSaveOption)
|
|||
|
{
|
|||
|
LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;
|
|||
|
LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
|
|||
|
LPOLEDOC lpClipboardDoc;
|
|||
|
LPLINELIST lpLL = (LPLINELIST)&((LPOUTLINEDOC)lpOleDoc)->m_LineList;
|
|||
|
BOOL fAbortIfSaveCanceled = (dwSaveOption == OLECLOSE_PROMPTSAVE);
|
|||
|
|
|||
|
if (! lpOleDoc)
|
|||
|
return TRUE; // active doc's are already destroyed
|
|||
|
|
|||
|
if (lpOleDoc->m_fObjIsClosing)
|
|||
|
return TRUE; // Closing is already in progress
|
|||
|
|
|||
|
OLEDBG_BEGIN3("OleDoc_Close\r\n")
|
|||
|
|
|||
|
if (! OutlineDoc_CheckSaveChanges((LPOUTLINEDOC)lpOleDoc,&dwSaveOption)
|
|||
|
&& fAbortIfSaveCanceled) {
|
|||
|
OLEDBG_END3
|
|||
|
return FALSE; // cancel closing the doc
|
|||
|
}
|
|||
|
|
|||
|
lpOleDoc->m_fObjIsClosing = TRUE; // guard against recursive call
|
|||
|
|
|||
|
/* OLE2NOTE: in order to have a stable app and doc during the
|
|||
|
** process of closing, we intially AddRef the App and Doc ref
|
|||
|
** cnts and later Release them. These initial AddRefs are
|
|||
|
** artificial; they simply guarantee that these objects do not
|
|||
|
** get destroyed until the end of this routine.
|
|||
|
*/
|
|||
|
OleApp_AddRef(lpOleApp);
|
|||
|
OleDoc_AddRef(lpOleDoc);
|
|||
|
|
|||
|
#if defined( OLE_CNTR )
|
|||
|
{
|
|||
|
LPCONTAINERDOC lpContainerDoc = (LPCONTAINERDOC)lpOleDoc;
|
|||
|
|
|||
|
/* OLE2NOTE: force all OLE objects to close. this forces all
|
|||
|
** OLE object to transition from running to loaded. we can
|
|||
|
** NOT exit if any embeddings are still running.
|
|||
|
** if an object can't be closed and this close operation was
|
|||
|
** started by the user, then we will abort closing our document.
|
|||
|
*/
|
|||
|
if (! ContainerDoc_CloseAllOleObjects(lpContainerDoc, OLECLOSE_NOSAVE)
|
|||
|
&& fAbortIfSaveCanceled) {
|
|||
|
OleDoc_Release(lpOleDoc); // release artificial AddRef above
|
|||
|
OleApp_Release(lpOleApp); // release artificial AddRef above
|
|||
|
lpOleDoc->m_fObjIsClosing = FALSE; // clear recursion guard
|
|||
|
|
|||
|
OLEDBG_END3
|
|||
|
return FALSE; // Closing is aborted
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
#if defined( INPLACE_SVR )
|
|||
|
/* OLE2NOTE: if the server is currently in-place active we must
|
|||
|
** deactivate it now before closing
|
|||
|
*/
|
|||
|
ServerDoc_DoInPlaceDeactivate((LPSERVERDOC)lpOleDoc);
|
|||
|
#endif
|
|||
|
|
|||
|
/* OLE2NOTE: if this document is the source of data for the
|
|||
|
** clipboard, then flush the clipboard. it is important to flush
|
|||
|
** the clipboard BEFORE calling sending any notifications to
|
|||
|
** clients (eg. IOleClientSite::OnShowWindow(FALSE)) which could
|
|||
|
** give them a chance to run and try to get our clipboard data
|
|||
|
** object that we want to destroy. (eg. our app tries to
|
|||
|
** update the paste button of the toolbar when
|
|||
|
** WM_ACTIVATEAPP is received.)
|
|||
|
*/
|
|||
|
lpClipboardDoc = (LPOLEDOC)lpOutlineApp->m_lpClipboardDoc;
|
|||
|
if (lpClipboardDoc &&
|
|||
|
lpClipboardDoc->m_lpSrcDocOfCopy == lpOleDoc) {
|
|||
|
OleApp_FlushClipboard(lpOleApp);
|
|||
|
}
|
|||
|
|
|||
|
/* OLE2NOTE: Revoke the object from the Running Object Table. it is
|
|||
|
** best if the object is revoke prior to calling
|
|||
|
** COLockObjectExternal(FALSE,TRUE) which is called when the
|
|||
|
** document window is hidden from the user.
|
|||
|
*/
|
|||
|
OLEDBG_BEGIN3("OleStdRevokeAsRunning called\r\n")
|
|||
|
OleStdRevokeAsRunning(&lpOleDoc->m_dwRegROT);
|
|||
|
OLEDBG_END3
|
|||
|
|
|||
|
/* OLE2NOTE: if the user is in control of the document, the user
|
|||
|
** accounts for one refcnt on the document. Closing the
|
|||
|
** document is achieved by releasing the object on behalf of
|
|||
|
** the user. if the document is not referenced by any other
|
|||
|
** clients, then the document will also be destroyed. if it
|
|||
|
** is referenced by other clients, then it will remain until
|
|||
|
** they release it. it is important to hide the window and call
|
|||
|
** IOleClientSite::OnShowWindow(FALSE) BEFORE sending OnClose
|
|||
|
** notification.
|
|||
|
*/
|
|||
|
OleDoc_HideWindow(lpOleDoc, TRUE);
|
|||
|
|
|||
|
#if defined( OLE_SERVER )
|
|||
|
{
|
|||
|
LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOleDoc;
|
|||
|
LPSERVERNAMETABLE lpServerNameTable =
|
|||
|
(LPSERVERNAMETABLE)((LPOUTLINEDOC)lpOleDoc)->m_lpNameTable;
|
|||
|
|
|||
|
/* OLE2NOTE: force all pseudo objects to close. this informs all
|
|||
|
** linking clients of pseudo objects to release their PseudoObj.
|
|||
|
*/
|
|||
|
ServerNameTable_CloseAllPseudoObjs(lpServerNameTable);
|
|||
|
|
|||
|
/* OLE2NOTE: send last OnDataChange notification to clients
|
|||
|
** that have registered for data notifications when object
|
|||
|
** stops running (ADVF_DATAONSTOP), if the data in our
|
|||
|
** object has ever changed. it is best to only send this
|
|||
|
** notification if necessary.
|
|||
|
*/
|
|||
|
if (lpServerDoc->m_lpDataAdviseHldr) {
|
|||
|
if (lpServerDoc->m_fSendDataOnStop) {
|
|||
|
ServerDoc_SendAdvise(
|
|||
|
(LPSERVERDOC)lpOleDoc,
|
|||
|
OLE_ONDATACHANGE,
|
|||
|
NULL, /* lpmkDoc -- not relevant here */
|
|||
|
ADVF_DATAONSTOP
|
|||
|
);
|
|||
|
}
|
|||
|
/* OLE2NOTE: we just sent the last data notification that we
|
|||
|
** need to send; release our DataAdviseHolder. we SHOULD be
|
|||
|
** the only one using it.
|
|||
|
*/
|
|||
|
|
|||
|
OleStdVerifyRelease(
|
|||
|
(LPUNKNOWN)lpServerDoc->m_lpDataAdviseHldr,
|
|||
|
"DataAdviseHldr not released properly"
|
|||
|
);
|
|||
|
lpServerDoc->m_lpDataAdviseHldr = NULL;
|
|||
|
}
|
|||
|
|
|||
|
// OLE2NOTE: inform all of our linking clients that we are closing.
|
|||
|
|
|||
|
|
|||
|
if (lpServerDoc->m_lpOleAdviseHldr) {
|
|||
|
ServerDoc_SendAdvise(
|
|||
|
(LPSERVERDOC)lpOleDoc,
|
|||
|
OLE_ONCLOSE,
|
|||
|
NULL, /* lpmkDoc -- not relevant here */
|
|||
|
0 /* advf -- not relevant here */
|
|||
|
);
|
|||
|
|
|||
|
/* OLE2NOTE: OnClose is the last notification that we need to
|
|||
|
** send; release our OleAdviseHolder. we SHOULD be the only
|
|||
|
** one using it. this will make our destructor realize that
|
|||
|
** OnClose notification has already been sent.
|
|||
|
*/
|
|||
|
OleStdVerifyRelease(
|
|||
|
(LPUNKNOWN)lpServerDoc->m_lpOleAdviseHldr,
|
|||
|
"OleAdviseHldr not released properly"
|
|||
|
);
|
|||
|
lpServerDoc->m_lpOleAdviseHldr = NULL;
|
|||
|
}
|
|||
|
|
|||
|
/* release our Container's ClientSite. */
|
|||
|
if(lpServerDoc->m_lpOleClientSite) {
|
|||
|
OleStdRelease((LPUNKNOWN)lpServerDoc->m_lpOleClientSite);
|
|||
|
lpServerDoc->m_lpOleClientSite = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
if (lpOleDoc->m_lpLLStm) {
|
|||
|
/* release our LineList stream. */
|
|||
|
OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpLLStm);
|
|||
|
lpOleDoc->m_lpLLStm = NULL;
|
|||
|
}
|
|||
|
|
|||
|
if (lpOleDoc->m_lpNTStm) {
|
|||
|
/* release our NameTable stream. */
|
|||
|
OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpNTStm);
|
|||
|
lpOleDoc->m_lpNTStm = NULL;
|
|||
|
}
|
|||
|
|
|||
|
if (lpOleDoc->m_lpStg) {
|
|||
|
/* release our doc storage. */
|
|||
|
OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpStg);
|
|||
|
lpOleDoc->m_lpStg = NULL;
|
|||
|
}
|
|||
|
|
|||
|
if (lpOleDoc->m_lpFileMoniker) {
|
|||
|
OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpFileMoniker);
|
|||
|
lpOleDoc->m_lpFileMoniker = NULL;
|
|||
|
}
|
|||
|
|
|||
|
/* OLE2NOTE: this call forces all external connections to our
|
|||
|
** object to close down and therefore guarantees that we receive
|
|||
|
** all releases associated with those external connections.
|
|||
|
*/
|
|||
|
OLEDBG_BEGIN2("CoDisconnectObject(lpDoc) called\r\n")
|
|||
|
CoDisconnectObject((LPUNKNOWN)&lpOleDoc->m_Unknown, 0);
|
|||
|
OLEDBG_END2
|
|||
|
|
|||
|
OleDoc_Release(lpOleDoc); // release artificial AddRef above
|
|||
|
OleApp_Release(lpOleApp); // release artificial AddRef above
|
|||
|
|
|||
|
OLEDBG_END3
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* OleDoc_Destroy
|
|||
|
* --------------
|
|||
|
*
|
|||
|
* Free all OLE related resources that had been allocated for a document.
|
|||
|
*/
|
|||
|
void OleDoc_Destroy(LPOLEDOC lpOleDoc)
|
|||
|
{
|
|||
|
LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
|
|||
|
LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;
|
|||
|
|
|||
|
if (lpOleDoc->m_fObjIsDestroying)
|
|||
|
return; // Doc destruction is already in progress
|
|||
|
|
|||
|
lpOleDoc->m_fObjIsDestroying = TRUE; // guard against recursive call
|
|||
|
|
|||
|
#if defined( OLE_SERVER )
|
|||
|
|
|||
|
/* OLE2NOTE: it is ALWAYS necessary to make sure that the work we
|
|||
|
** do in our OleDoc_Close function is performed before we
|
|||
|
** destroy our document object. this includes revoking from the
|
|||
|
** Running Object Table (ROT), sending OnClose notification,
|
|||
|
** revoking from Drag/Drop, closing all pseudo objects, etc.
|
|||
|
** There are some tricky scenarios involving linking and
|
|||
|
** when IOleObject::Close is called versus when we get our
|
|||
|
** final release causing us to call our OleDoc_Destroy
|
|||
|
** (destructor) function.
|
|||
|
**
|
|||
|
** SCENARIO 1 -- closing from server (File.Exit or File.Close)
|
|||
|
** OleDoc_Close function is called directly by the
|
|||
|
** server in response to the menu command
|
|||
|
** (WM_COMMAND processing).
|
|||
|
**
|
|||
|
** SCENARIO 2 -- closed by embedding container
|
|||
|
** our embedding container calls IOleObject::Close
|
|||
|
** directly.
|
|||
|
**
|
|||
|
** SCENARIO 3 -- silent-update final release
|
|||
|
** THIS IS THE TRICKY ONE!!!
|
|||
|
** in the case that our object is launched because
|
|||
|
** a linking client calls IOleObject::Update on
|
|||
|
** its link, then our object will be run
|
|||
|
** invisibly, typically GetData will be called,
|
|||
|
** and then the connection from the linking client
|
|||
|
** will be released. the release of this last
|
|||
|
** linking connection should cause our object to
|
|||
|
** shut down.
|
|||
|
** there are 2 strategies to deal with this scenario:
|
|||
|
**
|
|||
|
** STRATEGY 1 -- implement IExternalConnection.
|
|||
|
** IExternalConnection::AddConnection will be
|
|||
|
** called (by the StubManager) every time that an
|
|||
|
** external (linking) connection is created or
|
|||
|
** CoLockObjectExternal is called. the object
|
|||
|
** should maintain a count of strong connections
|
|||
|
** (m_dwStrongExtConn). IExternalConnection::
|
|||
|
** ReleaseConnection will be called when these
|
|||
|
** connections are released. when the
|
|||
|
** m_dwStrongExtConn transistions to 0, the object
|
|||
|
** should call its IOleObject::Close function.
|
|||
|
** this assumes that CoLockObjectExternal is used
|
|||
|
** to manage locks by the object itself (eg. when
|
|||
|
** the object is visible to the user--fUserCtrl,
|
|||
|
** and when PseudoObjects are created, etc.)
|
|||
|
** this is the strategy implemented by SVROUTL.
|
|||
|
**
|
|||
|
** STRATEGY 2 -- guard both the destructor
|
|||
|
** function and the Close function. if the
|
|||
|
** destructor is called directly without Close
|
|||
|
** first being called, then call Close before
|
|||
|
** proceeding with the destruction code.
|
|||
|
** previously SVROUTL was organized in this
|
|||
|
** manner. that old code is conditionaly compiled
|
|||
|
** away with "#ifdef OBSOLETE" below. this
|
|||
|
** method has the disadvantage that external
|
|||
|
** remoting is no longer possible by the time the
|
|||
|
** Close is called making it impossible for
|
|||
|
** the object to ask its container to save the
|
|||
|
** object if the object is dirty. this can result
|
|||
|
** in data loss. thus STRATEGY 1 is safer.
|
|||
|
** consider the scenario where an in-place
|
|||
|
** container UIDeactivates an object but does NOT
|
|||
|
** keep the object locked running (this is
|
|||
|
** required--see CntrLine_IPSite_OnInPlaceActivate
|
|||
|
** in cntrline.c), then, if a linking client binds
|
|||
|
** and unbinds from the object, the object will be
|
|||
|
** destroyed and will NOT have an opportunity to
|
|||
|
** be saved. by implementing IExternalConnection,
|
|||
|
** a server can insulate itself from a poorly
|
|||
|
** written container.
|
|||
|
*/
|
|||
|
#if defined( _DEBUG )
|
|||
|
|
|||
|
#ifndef WIN32
|
|||
|
// this is not a valid assert in Ole32; if file moniker binding
|
|||
|
// fails, for example, we will only get releases coming in
|
|||
|
// (no external connections are involved because OLE32 does a
|
|||
|
// private rpc to the server (us) where the IPersistFile::Load is
|
|||
|
// done.
|
|||
|
|
|||
|
OleDbgAssertSz(
|
|||
|
(lpOutlineDoc->m_fDataTransferDoc || lpOleDoc->m_fObjIsClosing),
|
|||
|
"Destroy called without Close being called\r\n"
|
|||
|
);
|
|||
|
#endif //!WIN32
|
|||
|
|
|||
|
#endif // _DEBUG
|
|||
|
#if defined( OBSOLETE )
|
|||
|
/* OLE2NOTE: if the document destructor is called directly because
|
|||
|
** the object's refcnt went to 0 (ie. without OleDoc_Close first
|
|||
|
** being called), then we need to make sure that the document is
|
|||
|
** properly closed before destroying the object. this scenario
|
|||
|
** could arise during a silent-update of a link. calling
|
|||
|
** OleDoc_Close here guarantees that the clipboard will be
|
|||
|
** properly flushed, the doc's moniker will be properly revoked,
|
|||
|
** the document will be saved if necessary, etc.
|
|||
|
*/
|
|||
|
if (!lpOutlineDoc->m_fDataTransferDoc && !lpOleDoc->m_fObjIsClosing)
|
|||
|
OleDoc_Close(lpOleDoc, OLECLOSE_NOSAVE);
|
|||
|
#endif
|
|||
|
|
|||
|
{
|
|||
|
LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOleDoc;
|
|||
|
/* OLE2NOTE: perform processing specific for an OLE server */
|
|||
|
|
|||
|
#if defined( SVR_TREATAS )
|
|||
|
if (lpServerDoc->m_lpszTreatAsType) {
|
|||
|
OleStdFreeString(lpServerDoc->m_lpszTreatAsType, NULL);
|
|||
|
lpServerDoc->m_lpszTreatAsType = NULL;
|
|||
|
}
|
|||
|
#endif // SVR_TREATAS
|
|||
|
|
|||
|
#if defined( INPLACE_SVR )
|
|||
|
if (IsWindow(lpServerDoc->m_hWndHatch))
|
|||
|
DestroyWindow(lpServerDoc->m_hWndHatch);
|
|||
|
#endif // INPLACE_SVR
|
|||
|
}
|
|||
|
#endif // OLE_SERVER
|
|||
|
|
|||
|
if (lpOleDoc->m_lpLLStm) {
|
|||
|
/* release our LineList stream. */
|
|||
|
OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpLLStm);
|
|||
|
lpOleDoc->m_lpLLStm = NULL;
|
|||
|
}
|
|||
|
|
|||
|
if (lpOleDoc->m_lpNTStm) {
|
|||
|
/* release our NameTable stream. */
|
|||
|
OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpNTStm);
|
|||
|
lpOleDoc->m_lpNTStm = NULL;
|
|||
|
}
|
|||
|
|
|||
|
if (lpOleDoc->m_lpStg) {
|
|||
|
/* release our doc storage. */
|
|||
|
OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpStg);
|
|||
|
lpOleDoc->m_lpStg = NULL;
|
|||
|
}
|
|||
|
|
|||
|
if (lpOleDoc->m_lpFileMoniker) {
|
|||
|
OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpFileMoniker);
|
|||
|
lpOleDoc->m_lpFileMoniker = NULL;
|
|||
|
}
|
|||
|
|
|||
|
/*****************************************************************
|
|||
|
** OLE2NOTE: each document addref's the app object in order to **
|
|||
|
** guarentee that the app does not shut down while the doc **
|
|||
|
** is still open. since this doc is now destroyed, we will **
|
|||
|
** release this refcnt now. if there are now more open **
|
|||
|
** documents AND the app is not under the control of the **
|
|||
|
** user (ie. launched by OLE) then the app will revoke its **
|
|||
|
** ClassFactory. if there are no more references to the **
|
|||
|
** ClassFactory after it is revoked, then the app will shut **
|
|||
|
** down. this whole procedure is triggered by calling **
|
|||
|
** OutlineApp_DocUnlockApp. **
|
|||
|
*****************************************************************/
|
|||
|
|
|||
|
OutlineApp_DocUnlockApp(lpOutlineApp, lpOutlineDoc);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* OleDoc_SetUpdateEditMenuFlag
|
|||
|
* ----------------------------
|
|||
|
*
|
|||
|
* Purpose:
|
|||
|
* Set/clear the UpdateEditMenuFlag in OleDoc.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* fUpdate new value of the flag
|
|||
|
*
|
|||
|
* Returns:
|
|||
|
*/
|
|||
|
void OleDoc_SetUpdateEditMenuFlag(LPOLEDOC lpOleDoc, BOOL fUpdate)
|
|||
|
{
|
|||
|
if (!lpOleDoc)
|
|||
|
return;
|
|||
|
|
|||
|
lpOleDoc->m_fUpdateEditMenu = fUpdate;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* OleDoc_GetUpdateEditMenuFlag
|
|||
|
* ----------------------------
|
|||
|
*
|
|||
|
* Purpose:
|
|||
|
* Get the value of the UpdateEditMenuFlag in OleDoc
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
*
|
|||
|
* Returns:
|
|||
|
* value of the flag
|
|||
|
*/
|
|||
|
BOOL OleDoc_GetUpdateEditMenuFlag(LPOLEDOC lpOleDoc)
|
|||
|
{
|
|||
|
if (!lpOleDoc)
|
|||
|
return FALSE;
|
|||
|
|
|||
|
return lpOleDoc->m_fUpdateEditMenu;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*************************************************************************
|
|||
|
** OleDoc::IUnknown interface implementation
|
|||
|
*************************************************************************/
|
|||
|
|
|||
|
STDMETHODIMP OleDoc_Unk_QueryInterface(
|
|||
|
LPUNKNOWN lpThis,
|
|||
|
REFIID riid,
|
|||
|
LPVOID FAR* lplpvObj
|
|||
|
)
|
|||
|
{
|
|||
|
LPOLEDOC lpOleDoc = ((struct CDocUnknownImpl FAR*)lpThis)->lpOleDoc;
|
|||
|
|
|||
|
return OleDoc_QueryInterface(lpOleDoc, riid, lplpvObj);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
STDMETHODIMP_(ULONG) OleDoc_Unk_AddRef(LPUNKNOWN lpThis)
|
|||
|
{
|
|||
|
LPOLEDOC lpOleDoc = ((struct CDocUnknownImpl FAR*)lpThis)->lpOleDoc;
|
|||
|
|
|||
|
OleDbgAddRefMethod(lpThis, "IUnknown");
|
|||
|
|
|||
|
return OleDoc_AddRef(lpOleDoc);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
STDMETHODIMP_(ULONG) OleDoc_Unk_Release (LPUNKNOWN lpThis)
|
|||
|
{
|
|||
|
LPOLEDOC lpOleDoc = ((struct CDocUnknownImpl FAR*)lpThis)->lpOleDoc;
|
|||
|
|
|||
|
OleDbgReleaseMethod(lpThis, "IUnknown");
|
|||
|
|
|||
|
return OleDoc_Release(lpOleDoc);
|
|||
|
}
|
|||
|
|