windows-nt/Source/XPSP1/NT/com/oleutest/letest/outline/oledoc.c
2020-09-26 16:20:57 +08:00

1180 lines
37 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*************************************************************************
**
** 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);
}