2158 lines
58 KiB
C
2158 lines
58 KiB
C
|
/*************************************************************************
|
||
|
**
|
||
|
** OLE 2 Sample Code
|
||
|
**
|
||
|
** linking.c
|
||
|
**
|
||
|
** This file contains the major interfaces, methods and related support
|
||
|
** functions for implementing linking to items. The code
|
||
|
** contained in this file is used by BOTH the Container and Server
|
||
|
** (Object) versions of the Outline sample code.
|
||
|
**
|
||
|
** As a server SVROUTL supports linking to the whole document object
|
||
|
** (either a file-based document or as an embedded object). It also
|
||
|
** supports linking to ranges (or PseudoObjects).
|
||
|
**
|
||
|
** As a container CNTROUTL supports linking to embedded objects.
|
||
|
** (see file svrpsobj.c for Pseudo Object implementation)
|
||
|
**
|
||
|
** OleDoc Object
|
||
|
** exposed interfaces:
|
||
|
** IPersistFile
|
||
|
** IOleItemContainer
|
||
|
** IExternalConnection
|
||
|
**
|
||
|
** (c) Copyright Microsoft Corp. 1992 - 1993 All Rights Reserved
|
||
|
**
|
||
|
*************************************************************************/
|
||
|
|
||
|
#include "outline.h"
|
||
|
|
||
|
OLEDBGDATA
|
||
|
|
||
|
extern LPOUTLINEAPP g_lpApp;
|
||
|
|
||
|
|
||
|
|
||
|
STDMETHODIMP OleDoc_ItemCont_GetObjectA(
|
||
|
LPOLEITEMCONTAINER lpThis,
|
||
|
LPSTR lpszItem,
|
||
|
DWORD dwSpeedNeeded,
|
||
|
LPBINDCTX lpbc,
|
||
|
REFIID riid,
|
||
|
LPVOID FAR*lplpvObject
|
||
|
);
|
||
|
|
||
|
STDMETHODIMP OleDoc_ItemCont_GetObjectStorageA(
|
||
|
LPOLEITEMCONTAINER lpThis,
|
||
|
LPSTR lpszItem,
|
||
|
LPBINDCTX lpbc,
|
||
|
REFIID riid,
|
||
|
LPVOID FAR*lplpvStorage
|
||
|
);
|
||
|
|
||
|
#if defined(OLE_CNTR)
|
||
|
|
||
|
STDMETHODIMP OleDoc_ItemCont_IsRunningA(
|
||
|
LPOLEITEMCONTAINER lpThis,
|
||
|
LPSTR lpszItem
|
||
|
);
|
||
|
|
||
|
HRESULT ContainerDoc_IsRunningA(
|
||
|
LPCONTAINERDOC lpContainerDoc,
|
||
|
LPSTR lpszItem);
|
||
|
|
||
|
HRESULT ContainerDoc_GetObjectA(
|
||
|
LPCONTAINERDOC lpContainerDoc,
|
||
|
LPSTR lpszItem,
|
||
|
DWORD dwSpeedNeeded,
|
||
|
REFIID riid,
|
||
|
LPVOID FAR*lplpvObject
|
||
|
);
|
||
|
|
||
|
HRESULT ContainerDoc_GetObjectStorageA(
|
||
|
LPCONTAINERDOC lpContainerDoc,
|
||
|
LPSTR lpszItem,
|
||
|
LPSTORAGE FAR*lplpStg);
|
||
|
|
||
|
#endif // OLE_CNTR
|
||
|
|
||
|
#if defined(OLE_SERVER)
|
||
|
|
||
|
HRESULT ServerDoc_IsRunningA(LPSERVERDOC lpServerDoc, LPSTR lpszItem);
|
||
|
|
||
|
HRESULT ServerDoc_GetObjectA(
|
||
|
LPSERVERDOC lpServerDoc,
|
||
|
LPSTR lpszItem,
|
||
|
REFIID riid,
|
||
|
LPVOID FAR*lplpvObject);
|
||
|
|
||
|
#endif // OLE_SERVER
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*************************************************************************
|
||
|
** OleDoc::IPersistFile interface implementation
|
||
|
*************************************************************************/
|
||
|
|
||
|
// IPersistFile::QueryInterface
|
||
|
STDMETHODIMP OleDoc_PFile_QueryInterface(
|
||
|
LPPERSISTFILE lpThis,
|
||
|
REFIID riid,
|
||
|
LPVOID FAR* lplpvObj
|
||
|
)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc = ((struct CDocPersistFileImpl FAR*)lpThis)->lpOleDoc;
|
||
|
|
||
|
return OleDoc_QueryInterface(lpOleDoc, riid, lplpvObj);
|
||
|
}
|
||
|
|
||
|
|
||
|
// IPersistFile::AddRef
|
||
|
STDMETHODIMP_(ULONG) OleDoc_PFile_AddRef(LPPERSISTFILE lpThis)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc = ((struct CDocPersistFileImpl FAR*)lpThis)->lpOleDoc;
|
||
|
|
||
|
OleDbgAddRefMethod(lpThis, "IPersistFile");
|
||
|
|
||
|
return OleDoc_AddRef(lpOleDoc);
|
||
|
}
|
||
|
|
||
|
|
||
|
// IPersistFile::Release
|
||
|
STDMETHODIMP_(ULONG) OleDoc_PFile_Release (LPPERSISTFILE lpThis)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc = ((struct CDocPersistFileImpl FAR*)lpThis)->lpOleDoc;
|
||
|
|
||
|
OleDbgReleaseMethod(lpThis, "IPersistFile");
|
||
|
|
||
|
return OleDoc_Release(lpOleDoc);
|
||
|
}
|
||
|
|
||
|
|
||
|
// IPersistFile::GetClassID
|
||
|
STDMETHODIMP OleDoc_PFile_GetClassID (
|
||
|
LPPERSISTFILE lpThis,
|
||
|
CLSID FAR* lpclsid
|
||
|
)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc = ((struct CDocPersistFileImpl FAR*)lpThis)->lpOleDoc;
|
||
|
LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;
|
||
|
OleDbgOut2("OleDoc_PFile_GetClassID\r\n");
|
||
|
|
||
|
#if defined( OLE_SERVER ) && defined( SVR_TREATAS )
|
||
|
|
||
|
/* 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)lpOleDoc, lpclsid);
|
||
|
#else
|
||
|
*lpclsid = CLSID_APP;
|
||
|
#endif
|
||
|
return NOERROR;
|
||
|
}
|
||
|
|
||
|
|
||
|
// IPersistFile::IsDirty
|
||
|
STDMETHODIMP OleDoc_PFile_IsDirty(LPPERSISTFILE lpThis)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc = ((struct CDocPersistFileImpl FAR*)lpThis)->lpOleDoc;
|
||
|
OleDbgOut2("OleDoc_PFile_IsDirty\r\n");
|
||
|
|
||
|
if (OutlineDoc_IsModified((LPOUTLINEDOC)lpOleDoc))
|
||
|
return NOERROR;
|
||
|
else
|
||
|
return ResultFromScode(S_FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
// IPersistFile::Load
|
||
|
STDMETHODIMP OleDoc_PFile_LoadA(
|
||
|
LPPERSISTFILE lpThis,
|
||
|
LPCSTR lpszFileName,
|
||
|
DWORD grfMode
|
||
|
)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc = ((struct CDocPersistFileImpl FAR*)lpThis)->lpOleDoc;
|
||
|
SCODE sc;
|
||
|
|
||
|
OLEDBG_BEGIN2("OleDoc_PFile_Load\r\n")
|
||
|
|
||
|
/* OLE2NOTE: grfMode passed from the caller indicates if the caller
|
||
|
** needs Read or ReadWrite permissions. if appropriate the
|
||
|
** callee should open the file with the requested permissions.
|
||
|
** the caller will normally not impose sharing permissions.
|
||
|
**
|
||
|
** the sample code currently always opens its file ReadWrite.
|
||
|
*/
|
||
|
|
||
|
if (OutlineDoc_LoadFromFile((LPOUTLINEDOC)lpOleDoc, (LPSTR)lpszFileName))
|
||
|
sc = S_OK;
|
||
|
else
|
||
|
sc = E_FAIL;
|
||
|
|
||
|
OLEDBG_END2
|
||
|
return ResultFromScode(sc);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// IPersistFile::Load
|
||
|
STDMETHODIMP OleDoc_PFile_Load (
|
||
|
LPPERSISTFILE lpThis,
|
||
|
LPCOLESTR lpszFileName,
|
||
|
DWORD grfMode
|
||
|
)
|
||
|
{
|
||
|
CREATESTR(lpsz, lpszFileName)
|
||
|
|
||
|
HRESULT hr = OleDoc_PFile_LoadA(lpThis, lpsz, grfMode);
|
||
|
|
||
|
FREESTR(lpsz)
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// IPersistFile::Save
|
||
|
STDMETHODIMP OleDoc_PFile_SaveA (
|
||
|
LPPERSISTFILE lpThis,
|
||
|
LPCSTR lpszFileName,
|
||
|
BOOL fRemember
|
||
|
)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc = ((struct CDocPersistFileImpl FAR*)lpThis)->lpOleDoc;
|
||
|
LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;
|
||
|
SCODE sc;
|
||
|
|
||
|
OLEDBG_BEGIN2("OleDoc_PFile_Save\r\n")
|
||
|
|
||
|
/* OLE2NOTE: it is only legal to perform a Save or SaveAs operation
|
||
|
** on a file-based document. if the document is an embedded
|
||
|
** object then we can not be changed to a file-base object.
|
||
|
**
|
||
|
** fRemember lpszFileName Type of Save
|
||
|
** ----------------------------------------------
|
||
|
** TRUE NULL SAVE
|
||
|
** TRUE ! NULL SAVE AS
|
||
|
** FALSE ! NULL SAVE COPY AS
|
||
|
** FALSE NULL ***error***
|
||
|
*/
|
||
|
if ( (lpszFileName==NULL || (lpszFileName != NULL && fRemember))
|
||
|
&& ((lpOutlineDoc->m_docInitType != DOCTYPE_FROMFILE
|
||
|
&& lpOutlineDoc->m_docInitType != DOCTYPE_NEW)) ) {
|
||
|
OLEDBG_END2
|
||
|
return ResultFromScode(E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
if (OutlineDoc_SaveToFile(
|
||
|
(LPOUTLINEDOC)lpOleDoc,
|
||
|
lpszFileName,
|
||
|
lpOutlineDoc->m_cfSaveFormat,
|
||
|
fRemember)) {
|
||
|
sc = S_OK;
|
||
|
} else
|
||
|
sc = E_FAIL;
|
||
|
|
||
|
OLEDBG_END2
|
||
|
return ResultFromScode(sc);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// IPersistFile::Save
|
||
|
STDMETHODIMP OleDoc_PFile_Save(
|
||
|
LPPERSISTFILE lpThis,
|
||
|
LPCOLESTR lpszFileName,
|
||
|
BOOL fRemember
|
||
|
)
|
||
|
{
|
||
|
CREATESTR(lpsz, lpszFileName)
|
||
|
|
||
|
HRESULT hr = OleDoc_PFile_SaveA(lpThis, lpsz, fRemember);
|
||
|
|
||
|
FREESTR(lpsz)
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// IPersistFile::SaveCompleted
|
||
|
STDMETHODIMP OleDoc_PFile_SaveCompletedA (
|
||
|
LPPERSISTFILE lpThis,
|
||
|
LPCSTR lpszFileName
|
||
|
)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc = ((struct CDocPersistFileImpl FAR*)lpThis)->lpOleDoc;
|
||
|
LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;
|
||
|
|
||
|
OleDbgOut2("OleDoc_PFile_SaveCompleted\r\n");
|
||
|
|
||
|
/* This method is called after IPersistFile::Save is called. during
|
||
|
** the period between Save and SaveCompleted the object must
|
||
|
** consider itself in NOSCRIBBLE mode (ie. it is NOT allowed to
|
||
|
** write to its file. here the object can clear its NOSCRIBBLE
|
||
|
** mode flag. the outline app never scribbles to its storage, so
|
||
|
** we have nothing to do.
|
||
|
*/
|
||
|
return NOERROR;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
STDMETHODIMP OleDoc_PFile_SaveCompleted (
|
||
|
LPPERSISTFILE lpThis,
|
||
|
LPCOLESTR lpszFileName
|
||
|
)
|
||
|
{
|
||
|
CREATESTR(lpsz, lpszFileName)
|
||
|
|
||
|
HRESULT hr = OleDoc_PFile_SaveCompletedA(lpThis, lpsz);
|
||
|
|
||
|
FREESTR(lpsz)
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// IPersistFile::GetCurFile
|
||
|
STDMETHODIMP OleDoc_PFile_GetCurFileA (
|
||
|
LPPERSISTFILE lpThis,
|
||
|
LPSTR FAR* lplpszFileName
|
||
|
)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc = ((struct CDocPersistFileImpl FAR*)lpThis)->lpOleDoc;
|
||
|
LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;
|
||
|
LPMALLOC lpMalloc;
|
||
|
LPSTR lpsz;
|
||
|
SCODE sc;
|
||
|
OleDbgOut2("OleDoc_PFile_GetCurFile\r\n");
|
||
|
|
||
|
/* OLE2NOTE: we must make sure to set all out ptr parameters to NULL. */
|
||
|
*lplpszFileName = NULL;
|
||
|
|
||
|
/*********************************************************************
|
||
|
** OLE2NOTE: memory returned for the lplpszFileName must be
|
||
|
** allocated appropriately using the current registered IMalloc
|
||
|
** interface. the allows the ownership of the memory to be
|
||
|
** passed to the caller (even if in another process).
|
||
|
*********************************************************************/
|
||
|
|
||
|
CoGetMalloc(MEMCTX_TASK, &lpMalloc);
|
||
|
if (! lpMalloc) {
|
||
|
return ResultFromScode(E_FAIL);
|
||
|
}
|
||
|
|
||
|
if (lpOutlineDoc->m_docInitType == DOCTYPE_FROMFILE) {
|
||
|
/* valid filename associated; return file name */
|
||
|
lpsz = (LPSTR)lpMalloc->lpVtbl->Alloc(
|
||
|
lpMalloc,
|
||
|
lstrlen((LPSTR)lpOutlineDoc->m_szFileName)+1
|
||
|
);
|
||
|
if (! lpsz) {
|
||
|
sc = E_OUTOFMEMORY;
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
lstrcpy(lpsz, (LPSTR)lpOutlineDoc->m_szFileName);
|
||
|
sc = S_OK;
|
||
|
} else {
|
||
|
/* no file associated; return default file name prompt */
|
||
|
lpsz=(LPSTR)lpMalloc->lpVtbl->Alloc(lpMalloc, sizeof(DEFEXTENSION)+3);
|
||
|
wsprintf(lpsz, "*.%s", DEFEXTENSION);
|
||
|
sc = S_FALSE;
|
||
|
}
|
||
|
|
||
|
error:
|
||
|
OleStdRelease((LPUNKNOWN)lpMalloc);
|
||
|
*lplpszFileName = lpsz;
|
||
|
return ResultFromScode(sc);
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP OleDoc_PFile_GetCurFile (
|
||
|
LPPERSISTFILE lpThis,
|
||
|
LPOLESTR FAR* lplpszFileName
|
||
|
)
|
||
|
{
|
||
|
LPSTR lpsz;
|
||
|
|
||
|
HRESULT hr = OleDoc_PFile_GetCurFileA(lpThis, &lpsz);
|
||
|
|
||
|
CopyAndFreeSTR(lpsz, lplpszFileName);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
/*************************************************************************
|
||
|
** OleDoc::IOleItemContainer interface implementation
|
||
|
*************************************************************************/
|
||
|
|
||
|
// IOleItemContainer::QueryInterface
|
||
|
STDMETHODIMP OleDoc_ItemCont_QueryInterface(
|
||
|
LPOLEITEMCONTAINER lpThis,
|
||
|
REFIID riid,
|
||
|
LPVOID FAR* lplpvObj
|
||
|
)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc =
|
||
|
((struct CDocOleItemContainerImpl FAR*)lpThis)->lpOleDoc;
|
||
|
|
||
|
return OleDoc_QueryInterface(lpOleDoc, riid, lplpvObj);
|
||
|
}
|
||
|
|
||
|
|
||
|
// IOleItemContainer::AddRef
|
||
|
STDMETHODIMP_(ULONG) OleDoc_ItemCont_AddRef(LPOLEITEMCONTAINER lpThis)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc =
|
||
|
((struct CDocOleItemContainerImpl FAR*)lpThis)->lpOleDoc;
|
||
|
|
||
|
OleDbgAddRefMethod(lpThis, "IOleItemContainer");
|
||
|
|
||
|
return OleDoc_AddRef((LPOLEDOC)lpOleDoc);
|
||
|
}
|
||
|
|
||
|
|
||
|
// IOleItemContainer::Release
|
||
|
STDMETHODIMP_(ULONG) OleDoc_ItemCont_Release(LPOLEITEMCONTAINER lpThis)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc =
|
||
|
((struct CDocOleItemContainerImpl FAR*)lpThis)->lpOleDoc;
|
||
|
|
||
|
OleDbgReleaseMethod(lpThis, "IOleItemContainer");
|
||
|
|
||
|
return OleDoc_Release((LPOLEDOC)lpOleDoc);
|
||
|
}
|
||
|
|
||
|
|
||
|
// IOleItemContainer::ParseDisplayName
|
||
|
STDMETHODIMP OleDoc_ItemCont_ParseDisplayNameA(
|
||
|
LPOLEITEMCONTAINER lpThis,
|
||
|
LPBC lpbc,
|
||
|
LPSTR lpszDisplayName,
|
||
|
ULONG FAR* lpchEaten,
|
||
|
LPMONIKER FAR* lplpmkOut
|
||
|
)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc =
|
||
|
((struct CDocOleItemContainerImpl FAR*)lpThis)->lpOleDoc;
|
||
|
char szItemName[MAXNAMESIZE];
|
||
|
LPUNKNOWN lpUnk;
|
||
|
HRESULT hrErr;
|
||
|
OleDbgOut2("OleDoc_ItemCont_ParseDisplayName\r\n");
|
||
|
|
||
|
/* OLE2NOTE: we must make sure to set all out ptr parameters to NULL. */
|
||
|
*lplpmkOut = NULL;
|
||
|
|
||
|
*lpchEaten = OleStdGetItemToken(
|
||
|
lpszDisplayName,
|
||
|
szItemName,
|
||
|
sizeof(szItemName)
|
||
|
);
|
||
|
|
||
|
/* OLE2NOTE: get a pointer to a running instance of the object. we
|
||
|
** should force the object to go running if necessary (even if
|
||
|
** this means launching its server EXE). this is the meaining of
|
||
|
** BINDSPEED_INDEFINITE. Parsing a Moniker is known to be an
|
||
|
** "EXPENSIVE" operation.
|
||
|
*/
|
||
|
hrErr = OleDoc_ItemCont_GetObjectA(
|
||
|
lpThis,
|
||
|
szItemName,
|
||
|
BINDSPEED_INDEFINITE,
|
||
|
lpbc,
|
||
|
&IID_IUnknown,
|
||
|
(LPVOID FAR*)&lpUnk
|
||
|
);
|
||
|
|
||
|
if (hrErr == NOERROR) {
|
||
|
OleStdRelease(lpUnk); // item name FOUND; don't need obj ptr.
|
||
|
|
||
|
CreateItemMonikerA(OLESTDDELIM, szItemName, lplpmkOut);
|
||
|
|
||
|
} else
|
||
|
*lpchEaten = 0; // item name is NOT valid
|
||
|
|
||
|
return hrErr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP OleDoc_ItemCont_ParseDisplayName(
|
||
|
LPOLEITEMCONTAINER lpThis,
|
||
|
LPBC lpbc,
|
||
|
LPOLESTR lpszDisplayName,
|
||
|
ULONG FAR* lpchEaten,
|
||
|
LPMONIKER FAR* lplpmkOut
|
||
|
)
|
||
|
{
|
||
|
CREATESTR(lpsz, lpszDisplayName)
|
||
|
|
||
|
HRESULT hr = OleDoc_ItemCont_ParseDisplayNameA(lpThis, lpbc,
|
||
|
lpsz, lpchEaten, lplpmkOut);
|
||
|
|
||
|
FREESTR(lpsz);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
// IOleItemContainer::EnumObjects
|
||
|
STDMETHODIMP OleDoc_ItemCont_EnumObjects(
|
||
|
LPOLEITEMCONTAINER lpThis,
|
||
|
DWORD grfFlags,
|
||
|
LPENUMUNKNOWN FAR* lplpenumUnknown
|
||
|
)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc =
|
||
|
((struct CDocOleItemContainerImpl FAR*)lpThis)->lpOleDoc;
|
||
|
|
||
|
OLEDBG_BEGIN2("OleDoc_ItemCont_EnumObjects\r\n")
|
||
|
|
||
|
/* OLE2NOTE: we must make sure to set all out ptr parameters to NULL. */
|
||
|
*lplpenumUnknown = NULL;
|
||
|
|
||
|
/* OLE2NOTE: this method should be implemented to allow programatic
|
||
|
** clients the ability to what elements the container holds.
|
||
|
** this method is NOT called in the standard linking scenarios.
|
||
|
**
|
||
|
** grfFlags can be one of the following:
|
||
|
** OLECONTF_EMBEDDINGS -- enumerate embedded objects
|
||
|
** OLECONTF_LINKS -- enumerate linked objects
|
||
|
** OLECONTF_OTHERS -- enumerate non-OLE compound doc objs
|
||
|
** OLECONTF_ONLYUSER -- enumerate only objs named by user
|
||
|
** OLECONTF_ONLYIFRUNNING-- enumerate only objs in running state
|
||
|
*/
|
||
|
|
||
|
OleDbgAssertSz(0, "NOT YET IMPLEMENTED!");
|
||
|
|
||
|
OLEDBG_END2
|
||
|
return ResultFromScode(E_NOTIMPL);
|
||
|
}
|
||
|
|
||
|
|
||
|
// IOleItemContainer::LockContainer
|
||
|
STDMETHODIMP OleDoc_ItemCont_LockContainer(
|
||
|
LPOLEITEMCONTAINER lpThis,
|
||
|
BOOL fLock
|
||
|
)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc =
|
||
|
((struct CDocOleItemContainerImpl FAR*)lpThis)->lpOleDoc;
|
||
|
HRESULT hrErr;
|
||
|
OLEDBG_BEGIN2("OleDoc_ItemCont_LockContainer\r\n")
|
||
|
|
||
|
#if defined( _DEBUG )
|
||
|
if (fLock) {
|
||
|
++lpOleDoc->m_cCntrLock;
|
||
|
OleDbgOutRefCnt3(
|
||
|
"OleDoc_ItemCont_LockContainer: cLock++\r\n",
|
||
|
lpOleDoc,
|
||
|
lpOleDoc->m_cCntrLock
|
||
|
);
|
||
|
} else {
|
||
|
/* OLE2NOTE: when there are no open documents and the app is not
|
||
|
** under the control of the user and there are no outstanding
|
||
|
** locks on the app, then revoke our ClassFactory to enable the
|
||
|
** app to shut down.
|
||
|
*/
|
||
|
--lpOleDoc->m_cCntrLock;
|
||
|
OleDbgAssertSz (
|
||
|
lpOleDoc->m_cCntrLock >= 0,
|
||
|
"OleDoc_ItemCont_LockContainer(FALSE) called with cLock == 0"
|
||
|
);
|
||
|
|
||
|
if (lpOleDoc->m_cCntrLock == 0) {
|
||
|
OleDbgOutRefCnt2(
|
||
|
"OleDoc_ItemCont_LockContainer: UNLOCKED\r\n",
|
||
|
lpOleDoc, lpOleDoc->m_cCntrLock);
|
||
|
} else {
|
||
|
OleDbgOutRefCnt3(
|
||
|
"OleDoc_ItemCont_LockContainer: cLock--\r\n",
|
||
|
lpOleDoc, lpOleDoc->m_cCntrLock);
|
||
|
}
|
||
|
}
|
||
|
#endif // _DEBUG
|
||
|
|
||
|
/* OLE2NOTE: in order to hold the document alive we call
|
||
|
** CoLockObjectExternal to add a strong reference to our Doc
|
||
|
** object. this will keep the Doc alive when all other external
|
||
|
** references release us. whenever an embedded object goes
|
||
|
** running a LockContainer(TRUE) is called. when the embedded
|
||
|
** object shuts down (ie. transitions from running to loaded)
|
||
|
** LockContainer(FALSE) is called. if the user issues File.Close
|
||
|
** the document will shut down in any case ignoring any
|
||
|
** outstanding LockContainer locks because CoDisconnectObject is
|
||
|
** called in OleDoc_Close. this will forceably break any
|
||
|
** existing strong reference counts including counts that we add
|
||
|
** ourselves by calling CoLockObjectExternal and guarantee that
|
||
|
** the Doc object gets its final release (ie. cRefs goes to 0).
|
||
|
*/
|
||
|
hrErr = OleDoc_Lock(lpOleDoc, fLock, TRUE /* fLastUnlockReleases */);
|
||
|
|
||
|
OLEDBG_END2
|
||
|
return hrErr;
|
||
|
}
|
||
|
|
||
|
|
||
|
// IOleItemContainer::GetObject
|
||
|
STDMETHODIMP OleDoc_ItemCont_GetObjectA(
|
||
|
LPOLEITEMCONTAINER lpThis,
|
||
|
LPSTR lpszItem,
|
||
|
DWORD dwSpeedNeeded,
|
||
|
LPBINDCTX lpbc,
|
||
|
REFIID riid,
|
||
|
LPVOID FAR* lplpvObject
|
||
|
)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc =
|
||
|
((struct CDocOleItemContainerImpl FAR*)lpThis)->lpOleDoc;
|
||
|
HRESULT hrErr;
|
||
|
|
||
|
OLEDBG_BEGIN2("OleDoc_ItemCont_GetObject\r\n")
|
||
|
|
||
|
/* OLE2NOTE: we must make sure to set all out ptr parameters to NULL. */
|
||
|
*lplpvObject = NULL;
|
||
|
|
||
|
#if defined( OLE_SERVER )
|
||
|
|
||
|
/* OLE2NOTE: SERVER ONLY version should return PseudoObjects with
|
||
|
** BINDSPEED_IMMEDIATE, thus the dwSpeedNeeded is not important
|
||
|
** in the case of a pure server.
|
||
|
*/
|
||
|
hrErr = ServerDoc_GetObjectA(
|
||
|
(LPSERVERDOC)lpOleDoc, lpszItem,riid,lplpvObject);
|
||
|
#endif
|
||
|
#if defined( OLE_CNTR )
|
||
|
|
||
|
/* OLE2NOTE: dwSpeedNeeded indicates how long the caller is willing
|
||
|
** to wait for us to get the object:
|
||
|
** BINDSPEED_IMMEDIATE -- only if obj already loaded && IsRunning
|
||
|
** BINDSPEED_MODERATE -- load obj if necessary && if IsRunning
|
||
|
** BINDSPEED_INDEFINITE-- force obj to load and run if necessary
|
||
|
*/
|
||
|
hrErr = ContainerDoc_GetObjectA(
|
||
|
(LPCONTAINERDOC)lpOleDoc,lpszItem,dwSpeedNeeded,riid,lplpvObject);
|
||
|
#endif
|
||
|
|
||
|
OLEDBG_END2
|
||
|
return hrErr;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
STDMETHODIMP OleDoc_ItemCont_GetObject(
|
||
|
LPOLEITEMCONTAINER lpThis,
|
||
|
LPOLESTR lpszItem,
|
||
|
DWORD dwSpeedNeeded,
|
||
|
LPBINDCTX lpbc,
|
||
|
REFIID riid,
|
||
|
LPVOID FAR* lplpvObject
|
||
|
)
|
||
|
{
|
||
|
CREATESTR(lpsz, lpszItem)
|
||
|
|
||
|
HRESULT hr = OleDoc_ItemCont_GetObjectA(lpThis, lpsz, dwSpeedNeeded, lpbc,
|
||
|
riid, lplpvObject);
|
||
|
|
||
|
FREESTR(lpsz)
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
// IOleItemContainer::GetObjectStorage
|
||
|
STDMETHODIMP OleDoc_ItemCont_GetObjectStorageA(
|
||
|
LPOLEITEMCONTAINER lpThis,
|
||
|
LPSTR lpszItem,
|
||
|
LPBINDCTX lpbc,
|
||
|
REFIID riid,
|
||
|
LPVOID FAR* lplpvStorage
|
||
|
)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc =
|
||
|
((struct CDocOleItemContainerImpl FAR*)lpThis)->lpOleDoc;
|
||
|
OleDbgOut2("OleDoc_ItemCont_GetObjectStorage\r\n");
|
||
|
|
||
|
/* OLE2NOTE: we must make sure to set all out ptr parameters to NULL. */
|
||
|
*lplpvStorage = NULL;
|
||
|
|
||
|
#if defined( OLE_SERVER )
|
||
|
/* OLE2NOTE: in the SERVER ONLY version, item names identify pseudo
|
||
|
** objects. pseudo objects, do NOT have identifiable storage.
|
||
|
*/
|
||
|
return ResultFromScode(E_FAIL);
|
||
|
#endif
|
||
|
#if defined( OLE_CNTR )
|
||
|
// We can only return an IStorage* type pointer
|
||
|
if (! IsEqualIID(riid, &IID_IStorage))
|
||
|
return ResultFromScode(E_FAIL);
|
||
|
|
||
|
return ContainerDoc_GetObjectStorageA(
|
||
|
(LPCONTAINERDOC)lpOleDoc,
|
||
|
lpszItem,
|
||
|
(LPSTORAGE FAR*)lplpvStorage
|
||
|
);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP OleDoc_ItemCont_GetObjectStorage(
|
||
|
LPOLEITEMCONTAINER lpThis,
|
||
|
LPOLESTR lpszItem,
|
||
|
LPBINDCTX lpbc,
|
||
|
REFIID riid,
|
||
|
LPVOID FAR* lplpvStorage
|
||
|
)
|
||
|
{
|
||
|
CREATESTR(lpsz, lpszItem)
|
||
|
|
||
|
HRESULT hr = OleDoc_ItemCont_GetObjectStorageA(lpThis, lpsz, lpbc,
|
||
|
riid, lplpvStorage);
|
||
|
|
||
|
FREESTR(lpsz)
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
// IOleItemContainer::IsRunning
|
||
|
STDMETHODIMP OleDoc_ItemCont_IsRunningA(
|
||
|
LPOLEITEMCONTAINER lpThis,
|
||
|
LPSTR lpszItem
|
||
|
)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc =
|
||
|
((struct CDocOleItemContainerImpl FAR*)lpThis)->lpOleDoc;
|
||
|
HRESULT hrErr;
|
||
|
|
||
|
OLEDBG_BEGIN2("OleDoc_ItemCont_IsRunning\r\n")
|
||
|
|
||
|
/* OLE2NOTE: Check if item name is valid. if so then return if
|
||
|
** Object is running. PseudoObjects in the Server version are
|
||
|
** always considered running. Ole objects in the container must
|
||
|
** be checked if they are running.
|
||
|
*/
|
||
|
|
||
|
#if defined( OLE_SERVER )
|
||
|
hrErr = ServerDoc_IsRunningA((LPSERVERDOC)lpOleDoc, lpszItem);
|
||
|
#endif
|
||
|
#if defined( OLE_CNTR )
|
||
|
hrErr = ContainerDoc_IsRunningA((LPCONTAINERDOC)lpOleDoc, lpszItem);
|
||
|
#endif
|
||
|
|
||
|
OLEDBG_END2
|
||
|
return hrErr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP OleDoc_ItemCont_IsRunning(
|
||
|
LPOLEITEMCONTAINER lpThis,
|
||
|
LPOLESTR lpszItem
|
||
|
)
|
||
|
{
|
||
|
CREATESTR(lpsz, lpszItem)
|
||
|
|
||
|
HRESULT hr = OleDoc_ItemCont_IsRunningA(lpThis,lpsz);
|
||
|
|
||
|
FREESTR(lpsz)
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
/*************************************************************************
|
||
|
** OleDoc::IExternalConnection interface implementation
|
||
|
*************************************************************************/
|
||
|
|
||
|
// IExternalConnection::QueryInterface
|
||
|
STDMETHODIMP OleDoc_ExtConn_QueryInterface(
|
||
|
LPEXTERNALCONNECTION lpThis,
|
||
|
REFIID riid,
|
||
|
LPVOID FAR* lplpvObj
|
||
|
)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc =
|
||
|
((struct CDocExternalConnectionImpl FAR*)lpThis)->lpOleDoc;
|
||
|
|
||
|
return OleDoc_QueryInterface(lpOleDoc, riid, lplpvObj);
|
||
|
}
|
||
|
|
||
|
|
||
|
// IExternalConnection::AddRef
|
||
|
STDMETHODIMP_(ULONG) OleDoc_ExtConn_AddRef(LPEXTERNALCONNECTION lpThis)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc =
|
||
|
((struct CDocExternalConnectionImpl FAR*)lpThis)->lpOleDoc;
|
||
|
|
||
|
OleDbgAddRefMethod(lpThis, "IExternalConnection");
|
||
|
|
||
|
return OleDoc_AddRef(lpOleDoc);
|
||
|
}
|
||
|
|
||
|
|
||
|
// IExternalConnection::Release
|
||
|
STDMETHODIMP_(ULONG) OleDoc_ExtConn_Release (LPEXTERNALCONNECTION lpThis)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc =
|
||
|
((struct CDocExternalConnectionImpl FAR*)lpThis)->lpOleDoc;
|
||
|
|
||
|
OleDbgReleaseMethod(lpThis, "IExternalConnection");
|
||
|
|
||
|
return OleDoc_Release(lpOleDoc);
|
||
|
}
|
||
|
|
||
|
|
||
|
// IExternalConnection::AddConnection
|
||
|
STDMETHODIMP_(DWORD) OleDoc_ExtConn_AddConnection(
|
||
|
LPEXTERNALCONNECTION lpThis,
|
||
|
DWORD extconn,
|
||
|
DWORD reserved
|
||
|
)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc =
|
||
|
((struct CDocExternalConnectionImpl FAR*)lpThis)->lpOleDoc;
|
||
|
|
||
|
if( extconn & EXTCONN_STRONG ) {
|
||
|
|
||
|
#if defined( _DEBUG )
|
||
|
OleDbgOutRefCnt3(
|
||
|
"OleDoc_ExtConn_AddConnection: dwStrongExtConn++\r\n",
|
||
|
lpOleDoc,
|
||
|
lpOleDoc->m_dwStrongExtConn + 1
|
||
|
);
|
||
|
#endif
|
||
|
return ++(lpOleDoc->m_dwStrongExtConn);
|
||
|
} else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
// IExternalConnection::ReleaseConnection
|
||
|
STDMETHODIMP_(DWORD) OleDoc_ExtConn_ReleaseConnection(
|
||
|
LPEXTERNALCONNECTION lpThis,
|
||
|
DWORD extconn,
|
||
|
DWORD reserved,
|
||
|
BOOL fLastReleaseCloses
|
||
|
)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc =
|
||
|
((struct CDocExternalConnectionImpl FAR*)lpThis)->lpOleDoc;
|
||
|
|
||
|
if( extconn & EXTCONN_STRONG ){
|
||
|
DWORD dwSave = --(lpOleDoc->m_dwStrongExtConn);
|
||
|
#if defined( _DEBUG )
|
||
|
OLEDBG_BEGIN2( (fLastReleaseCloses ?
|
||
|
"OleDoc_ExtConn_ReleaseConnection(TRUE)\r\n" :
|
||
|
"OleDoc_ExtConn_ReleaseConnection(FALSE)\r\n") )
|
||
|
OleDbgOutRefCnt3(
|
||
|
"OleDoc_ExtConn_ReleaseConnection: dwStrongExtConn--\r\n",
|
||
|
lpOleDoc,
|
||
|
lpOleDoc->m_dwStrongExtConn
|
||
|
);
|
||
|
OleDbgAssertSz (
|
||
|
lpOleDoc->m_dwStrongExtConn >= 0,
|
||
|
"OleDoc_ExtConn_ReleaseConnection called with dwStrong == 0"
|
||
|
);
|
||
|
#endif // _DEBUG
|
||
|
|
||
|
if( lpOleDoc->m_dwStrongExtConn == 0 && fLastReleaseCloses )
|
||
|
OleDoc_Close(lpOleDoc, OLECLOSE_SAVEIFDIRTY);
|
||
|
|
||
|
OLEDBG_END2
|
||
|
return dwSave;
|
||
|
} else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*************************************************************************
|
||
|
** OleDoc Common Support Functions
|
||
|
*************************************************************************/
|
||
|
|
||
|
|
||
|
/* OleDoc_GetFullMoniker
|
||
|
** ---------------------
|
||
|
** Return the full, absolute moniker of the document.
|
||
|
**
|
||
|
** NOTE: the caller must release the pointer returned when done.
|
||
|
*/
|
||
|
LPMONIKER OleDoc_GetFullMoniker(LPOLEDOC lpOleDoc, DWORD dwAssign)
|
||
|
{
|
||
|
LPMONIKER lpMoniker = NULL;
|
||
|
|
||
|
OLEDBG_BEGIN3("OleDoc_GetFullMoniker\r\n")
|
||
|
|
||
|
if (lpOleDoc->m_lpSrcDocOfCopy) {
|
||
|
/* CASE I: this document was created for a copy or drag/drop
|
||
|
** operation. generate the moniker which identifies the
|
||
|
** source document of the original copy.
|
||
|
*/
|
||
|
if (! lpOleDoc->m_fLinkSourceAvail)
|
||
|
goto done; // we already know a moniker is not available
|
||
|
|
||
|
lpMoniker=OleDoc_GetFullMoniker(lpOleDoc->m_lpSrcDocOfCopy, dwAssign);
|
||
|
}
|
||
|
else if (lpOleDoc->m_lpFileMoniker) {
|
||
|
|
||
|
/* CASE II: this document is a top-level user document (either
|
||
|
** file-based or untitled). return the FileMoniker stored
|
||
|
** with the document; it uniquely identifies the document.
|
||
|
*/
|
||
|
// we must AddRef the moniker to pass out a ptr
|
||
|
lpOleDoc->m_lpFileMoniker->lpVtbl->AddRef(lpOleDoc->m_lpFileMoniker);
|
||
|
|
||
|
lpMoniker = lpOleDoc->m_lpFileMoniker;
|
||
|
}
|
||
|
|
||
|
#if defined( OLE_SERVER )
|
||
|
|
||
|
else if (((LPSERVERDOC)lpOleDoc)->m_lpOleClientSite) {
|
||
|
|
||
|
/* CASE III: this document is an embedded object, ask our
|
||
|
** container for our moniker.
|
||
|
*/
|
||
|
OLEDBG_BEGIN2("IOleClientSite::GetMoniker called\r\n");
|
||
|
((LPSERVERDOC)lpOleDoc)->m_lpOleClientSite->lpVtbl->GetMoniker(
|
||
|
((LPSERVERDOC)lpOleDoc)->m_lpOleClientSite,
|
||
|
dwAssign,
|
||
|
OLEWHICHMK_OBJFULL,
|
||
|
&lpMoniker
|
||
|
);
|
||
|
OLEDBG_END2
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
else {
|
||
|
lpMoniker = NULL;
|
||
|
}
|
||
|
|
||
|
done:
|
||
|
OLEDBG_END3
|
||
|
return lpMoniker;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* OleDoc_DocRenamedUpdate
|
||
|
** -----------------------
|
||
|
** Update the documents registration in the running object table (ROT).
|
||
|
** Also inform all embedded OLE objects (container only) and/or psedudo
|
||
|
** objects (server only) that the name of the document has changed.
|
||
|
*/
|
||
|
void OleDoc_DocRenamedUpdate(LPOLEDOC lpOleDoc, LPMONIKER lpmkDoc)
|
||
|
{
|
||
|
OLEDBG_BEGIN3("OleDoc_DocRenamedUpdate\r\n")
|
||
|
|
||
|
OleDoc_AddRef(lpOleDoc);
|
||
|
|
||
|
/* OLE2NOTE: we must re-register ourselves as running when we
|
||
|
** get a new moniker assigned (ie. when we are renamed).
|
||
|
*/
|
||
|
OLEDBG_BEGIN3("OleStdRegisterAsRunning called\r\n")
|
||
|
OleStdRegisterAsRunning(
|
||
|
(LPUNKNOWN)&lpOleDoc->m_Unknown,
|
||
|
lpmkDoc,
|
||
|
&lpOleDoc->m_dwRegROT
|
||
|
);
|
||
|
OLEDBG_END3
|
||
|
|
||
|
#if defined( OLE_SERVER )
|
||
|
{
|
||
|
LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOleDoc;
|
||
|
LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;
|
||
|
|
||
|
/* OLE2NOTE: inform any linking clients that the document has been
|
||
|
** renamed.
|
||
|
*/
|
||
|
ServerDoc_SendAdvise (
|
||
|
lpServerDoc,
|
||
|
OLE_ONRENAME,
|
||
|
lpmkDoc,
|
||
|
0 /* advf -- not relevant here */
|
||
|
);
|
||
|
|
||
|
/* OLE2NOTE: inform any clients of pseudo objects
|
||
|
** within our document, that our document's
|
||
|
** Moniker has changed.
|
||
|
*/
|
||
|
ServerNameTable_InformAllPseudoObjectsDocRenamed(
|
||
|
(LPSERVERNAMETABLE)lpOutlineDoc->m_lpNameTable, lpmkDoc);
|
||
|
}
|
||
|
#endif
|
||
|
#if defined( OLE_CNTR )
|
||
|
{
|
||
|
LPCONTAINERDOC lpContainerDoc = (LPCONTAINERDOC)lpOleDoc;
|
||
|
|
||
|
/* OLE2NOTE: must tell all OLE objects that our container
|
||
|
** moniker changed.
|
||
|
*/
|
||
|
ContainerDoc_InformAllOleObjectsDocRenamed(
|
||
|
lpContainerDoc,
|
||
|
lpmkDoc
|
||
|
);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
OleDoc_Release(lpOleDoc); // release artificial AddRef above
|
||
|
OLEDBG_END3
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
#if defined( OLE_SERVER )
|
||
|
|
||
|
/*************************************************************************
|
||
|
** ServerDoc Supprt Functions Used by Server versions
|
||
|
*************************************************************************/
|
||
|
|
||
|
|
||
|
/* ServerDoc_PseudoObjLockDoc
|
||
|
** --------------------------
|
||
|
** Add a lock on the Doc on behalf of the PseudoObject. the Doc may not
|
||
|
** close while the Doc exists.
|
||
|
**
|
||
|
** when a pseudo object is first created, it calls this method to
|
||
|
** guarantee that the document stays alive (PseudoObj_Init).
|
||
|
** when a pseudo object is destroyed, it call
|
||
|
** ServerDoc_PseudoObjUnlockDoc to release this hold on the document.
|
||
|
*/
|
||
|
void ServerDoc_PseudoObjLockDoc(LPSERVERDOC lpServerDoc)
|
||
|
{
|
||
|
LPOLEDOC lpOleDoc = (LPOLEDOC)lpServerDoc;
|
||
|
ULONG cPseudoObj;
|
||
|
|
||
|
cPseudoObj = ++lpServerDoc->m_cPseudoObj;
|
||
|
|
||
|
#if defined( _DEBUG )
|
||
|
OleDbgOutRefCnt3(
|
||
|
"ServerDoc_PseudoObjLockDoc: cPseudoObj++\r\n",
|
||
|
lpServerDoc,
|
||
|
cPseudoObj
|
||
|
);
|
||
|
#endif
|
||
|
OleDoc_Lock(lpOleDoc, TRUE /* fLock */, 0 /* not applicable */);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ServerDoc_PseudoObjUnlockDoc
|
||
|
** ----------------------------
|
||
|
** Release the lock on the Doc on behalf of the PseudoObject. if this was
|
||
|
** the last lock on the Doc, then it will shutdown.
|
||
|
*/
|
||
|
void ServerDoc_PseudoObjUnlockDoc(
|
||
|
LPSERVERDOC lpServerDoc,
|
||
|
LPPSEUDOOBJ lpPseudoObj
|
||
|
)
|
||
|
{
|
||
|
ULONG cPseudoObj;
|
||
|
LPOLEDOC lpOleDoc = (LPOLEDOC)lpServerDoc;
|
||
|
OLEDBG_BEGIN3("ServerDoc_PseudoObjUnlockDoc\r\n")
|
||
|
|
||
|
/* OLE2NOTE: when there are no active pseudo objects in the Doc and
|
||
|
** the Doc is not visible, and if there are no outstanding locks
|
||
|
** on the Doc, then this is a "silent update"
|
||
|
** situation. our Doc is being used programatically by some
|
||
|
** client; it is NOT accessible to the user because it is
|
||
|
** NOT visible. thus since all Locks have been released, we
|
||
|
** will close the document. if the app is only running due
|
||
|
** to the presence of this document, then the app will now
|
||
|
** also shut down.
|
||
|
*/
|
||
|
cPseudoObj = --lpServerDoc->m_cPseudoObj;
|
||
|
|
||
|
#if defined( _DEBUG )
|
||
|
OleDbgAssertSz (
|
||
|
lpServerDoc->m_cPseudoObj >= 0,
|
||
|
"PseudoObjUnlockDoc called with cPseudoObj == 0"
|
||
|
);
|
||
|
|
||
|
OleDbgOutRefCnt3(
|
||
|
"ServerDoc_PseudoObjUnlockDoc: cPseudoObj--\r\n",
|
||
|
lpServerDoc,
|
||
|
cPseudoObj
|
||
|
);
|
||
|
#endif
|
||
|
OleDoc_Lock(lpOleDoc, FALSE /* fLock */, TRUE /* fLastUnlockReleases */);
|
||
|
|
||
|
OLEDBG_END3
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ServerDoc_GetObject
|
||
|
** -------------------
|
||
|
**
|
||
|
** Return a pointer to an object identified by an item string
|
||
|
** (lpszItem). For a server-only app, the object returned will be a
|
||
|
** pseudo object.
|
||
|
*/
|
||
|
HRESULT ServerDoc_GetObjectA(
|
||
|
LPSERVERDOC lpServerDoc,
|
||
|
LPSTR lpszItem,
|
||
|
REFIID riid,
|
||
|
LPVOID FAR* lplpvObject
|
||
|
)
|
||
|
{
|
||
|
LPPSEUDOOBJ lpPseudoObj;
|
||
|
LPSERVERNAMETABLE lpServerNameTable =
|
||
|
(LPSERVERNAMETABLE)((LPOUTLINEDOC)lpServerDoc)->m_lpNameTable;
|
||
|
|
||
|
*lplpvObject = NULL;
|
||
|
|
||
|
/* Get the PseudoObj which corresponds to an item name. if the item
|
||
|
** name does NOT exist in the name table then NO object is
|
||
|
** returned. the ServerNameTable_GetPseudoObj routine finds a
|
||
|
** name entry corresponding to the item name, it then checks if
|
||
|
** a PseudoObj has already been allocated. if so, it returns the
|
||
|
** existing object, otherwise it allocates a new PseudoObj.
|
||
|
*/
|
||
|
lpPseudoObj = ServerNameTable_GetPseudoObj(
|
||
|
lpServerNameTable,
|
||
|
lpszItem,
|
||
|
lpServerDoc
|
||
|
);
|
||
|
|
||
|
if (! lpPseudoObj) {
|
||
|
*lplpvObject = NULL;
|
||
|
return ResultFromScode(MK_E_NOOBJECT);
|
||
|
}
|
||
|
|
||
|
// return the desired interface pointer of the pseudo object.
|
||
|
return PseudoObj_QueryInterface(lpPseudoObj, riid, lplpvObject);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
HRESULT ServerDoc_GetObject(
|
||
|
LPSERVERDOC lpServerDoc,
|
||
|
LPOLESTR lpszItem,
|
||
|
REFIID riid,
|
||
|
LPVOID FAR* lplpvObject
|
||
|
)
|
||
|
{
|
||
|
CREATESTR(pstr, lpszItem)
|
||
|
|
||
|
HRESULT hr = ServerDoc_GetObjectA(lpServerDoc, pstr, riid, lplpvObject);
|
||
|
|
||
|
FREESTR(pstr)
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* ServerDoc_IsRunning
|
||
|
** -------------------
|
||
|
**
|
||
|
** Check if the object identified by an item string (lpszItem) is in
|
||
|
** the running state. For a server-only app, if the item name exists in
|
||
|
** in the NameTable then the item name is considered running.
|
||
|
** IOleItemContainer::GetObject would succeed.
|
||
|
*/
|
||
|
|
||
|
HRESULT ServerDoc_IsRunningA(LPSERVERDOC lpServerDoc, LPSTR lpszItem)
|
||
|
{
|
||
|
LPOUTLINENAMETABLE lpOutlineNameTable =
|
||
|
((LPOUTLINEDOC)lpServerDoc)->m_lpNameTable;
|
||
|
LPSERVERNAME lpServerName;
|
||
|
|
||
|
lpServerName = (LPSERVERNAME)OutlineNameTable_FindName(
|
||
|
lpOutlineNameTable,
|
||
|
lpszItem
|
||
|
);
|
||
|
|
||
|
if (lpServerName)
|
||
|
return NOERROR;
|
||
|
else
|
||
|
return ResultFromScode(MK_E_NOOBJECT);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ServerDoc_GetSelRelMoniker
|
||
|
** --------------------------
|
||
|
** Retrieve the relative item moniker which identifies the given
|
||
|
** selection (lplrSel).
|
||
|
**
|
||
|
** Returns NULL if a moniker can NOT be created.
|
||
|
*/
|
||
|
|
||
|
LPMONIKER ServerDoc_GetSelRelMoniker(
|
||
|
LPSERVERDOC lpServerDoc,
|
||
|
LPLINERANGE lplrSel,
|
||
|
DWORD dwAssign
|
||
|
)
|
||
|
{
|
||
|
LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
|
||
|
LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc;
|
||
|
LPSERVERNAMETABLE lpServerNameTable =
|
||
|
(LPSERVERNAMETABLE)lpOutlineDoc->m_lpNameTable;
|
||
|
LPOUTLINENAMETABLE lpOutlineNameTable =
|
||
|
(LPOUTLINENAMETABLE)lpServerNameTable;
|
||
|
LPOUTLINENAME lpOutlineName;
|
||
|
LPMONIKER lpmk;
|
||
|
|
||
|
lpOutlineName=OutlineNameTable_FindNamedRange(lpOutlineNameTable,lplrSel);
|
||
|
|
||
|
if (lpOutlineName) {
|
||
|
/* the selection range already has a name assigned */
|
||
|
CreateItemMonikerA(OLESTDDELIM, lpOutlineName->m_szName, &lpmk);
|
||
|
} else {
|
||
|
char szbuf[MAXNAMESIZE];
|
||
|
|
||
|
switch (dwAssign) {
|
||
|
|
||
|
case GETMONIKER_FORCEASSIGN:
|
||
|
|
||
|
/* Force the assignment of the name. This is called when a
|
||
|
** Paste Link actually occurs. At this point we want to
|
||
|
** create a Name and add it to the NameTable in order to
|
||
|
** track the source of the link. This name (as all
|
||
|
** names) will be updated upon editing of the document.
|
||
|
*/
|
||
|
wsprintf(
|
||
|
szbuf,
|
||
|
"%s %ld",
|
||
|
(LPSTR)DEFRANGENAMEPREFIX,
|
||
|
++(lpServerDoc->m_nNextRangeNo)
|
||
|
);
|
||
|
|
||
|
lpOutlineName = OutlineApp_CreateName(lpOutlineApp);
|
||
|
|
||
|
if (lpOutlineName) {
|
||
|
lstrcpy(lpOutlineName->m_szName, szbuf);
|
||
|
lpOutlineName->m_nStartLine = lplrSel->m_nStartLine;
|
||
|
lpOutlineName->m_nEndLine = lplrSel->m_nEndLine;
|
||
|
OutlineDoc_AddName(lpOutlineDoc, lpOutlineName);
|
||
|
} else {
|
||
|
// REVIEW: do we need "Out-of-Memory" error message here?
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case GETMONIKER_TEMPFORUSER:
|
||
|
|
||
|
/* Create a name to show to the user in the Paste
|
||
|
** Special dialog but do NOT yet incur the overhead
|
||
|
** of adding a Name to the NameTable. The Moniker
|
||
|
** generated should be useful to display to the user
|
||
|
** to indicate the source of the copy, but will NOT
|
||
|
** be used to create a link directly (the caller
|
||
|
** should ask again for a moniker specifying FORCEASSIGN).
|
||
|
** we will generate the name that would be the next
|
||
|
** auto-generated range name, BUT will NOT actually
|
||
|
** increment the range counter.
|
||
|
*/
|
||
|
wsprintf(
|
||
|
szbuf,
|
||
|
"%s %ld",
|
||
|
(LPSTR)DEFRANGENAMEPREFIX,
|
||
|
(lpServerDoc->m_nNextRangeNo)+1
|
||
|
);
|
||
|
break;
|
||
|
|
||
|
case GETMONIKER_ONLYIFTHERE:
|
||
|
|
||
|
/* the caller only wants a name if one has already been
|
||
|
** assigned. we have already above checked if the
|
||
|
** current selection has a name, so we will simply
|
||
|
** return NULL here.
|
||
|
*/
|
||
|
return NULL; // no moniker is assigned
|
||
|
|
||
|
default:
|
||
|
return NULL; // unknown flag given
|
||
|
}
|
||
|
|
||
|
CreateItemMonikerA(OLESTDDELIM, szbuf, &lpmk);
|
||
|
}
|
||
|
return lpmk;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ServerDoc_GetSelFullMoniker
|
||
|
** ---------------------------
|
||
|
** Retrieve the full absolute moniker which identifies the given
|
||
|
** selection (lplrSel).
|
||
|
** this moniker is created as a composite of the absolute moniker for
|
||
|
** the entire document appended with an item moniker which identifies
|
||
|
** the selection relative to the document.
|
||
|
** Returns NULL if a moniker can NOT be created.
|
||
|
*/
|
||
|
LPMONIKER ServerDoc_GetSelFullMoniker(
|
||
|
LPSERVERDOC lpServerDoc,
|
||
|
LPLINERANGE lplrSel,
|
||
|
DWORD dwAssign
|
||
|
)
|
||
|
{
|
||
|
LPMONIKER lpmkDoc = NULL;
|
||
|
LPMONIKER lpmkItem = NULL;
|
||
|
LPMONIKER lpmkFull = NULL;
|
||
|
|
||
|
lpmkDoc = OleDoc_GetFullMoniker(
|
||
|
(LPOLEDOC)lpServerDoc,
|
||
|
dwAssign
|
||
|
);
|
||
|
if (! lpmkDoc) return NULL;
|
||
|
|
||
|
lpmkItem = ServerDoc_GetSelRelMoniker(
|
||
|
lpServerDoc,
|
||
|
lplrSel,
|
||
|
dwAssign
|
||
|
);
|
||
|
if (lpmkItem) {
|
||
|
CreateGenericComposite(lpmkDoc, lpmkItem, (LPMONIKER FAR*)&lpmkFull);
|
||
|
OleStdRelease((LPUNKNOWN)lpmkItem);
|
||
|
}
|
||
|
|
||
|
if (lpmkDoc)
|
||
|
OleStdRelease((LPUNKNOWN)lpmkDoc);
|
||
|
|
||
|
return lpmkFull;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ServerNameTable_EditLineUpdate
|
||
|
* -------------------------------
|
||
|
*
|
||
|
* Update the table when a line at nEditIndex is edited.
|
||
|
*/
|
||
|
void ServerNameTable_EditLineUpdate(
|
||
|
LPSERVERNAMETABLE lpServerNameTable,
|
||
|
int nEditIndex
|
||
|
)
|
||
|
{
|
||
|
LPOUTLINENAMETABLE lpOutlineNameTable =
|
||
|
(LPOUTLINENAMETABLE)lpServerNameTable;
|
||
|
LPOUTLINENAME lpOutlineName;
|
||
|
LINERANGE lrSel;
|
||
|
LPPSEUDOOBJ lpPseudoObj;
|
||
|
int i;
|
||
|
|
||
|
for(i = 0; i < lpOutlineNameTable->m_nCount; i++) {
|
||
|
lpOutlineName=OutlineNameTable_GetName(lpOutlineNameTable, i);
|
||
|
|
||
|
lpPseudoObj = ((LPSERVERNAME)lpOutlineName)->m_lpPseudoObj;
|
||
|
|
||
|
/* if there is a pseudo object associated with this name, then
|
||
|
** check if the line that was modified is included within
|
||
|
** the named range.
|
||
|
*/
|
||
|
if (lpPseudoObj) {
|
||
|
OutlineName_GetSel(lpOutlineName, &lrSel);
|
||
|
|
||
|
if(((int)lrSel.m_nStartLine <= nEditIndex) &&
|
||
|
((int)lrSel.m_nEndLine >= nEditIndex)) {
|
||
|
|
||
|
// inform linking clients data has changed
|
||
|
PseudoObj_SendAdvise(
|
||
|
lpPseudoObj,
|
||
|
OLE_ONDATACHANGE,
|
||
|
NULL, /* lpmkDoc -- not relevant here */
|
||
|
0 /* advf -- no flags necessary */
|
||
|
);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ServerNameTable_InformAllPseudoObjectsDocRenamed
|
||
|
* ------------------------------------------------
|
||
|
*
|
||
|
* Inform all pseudo object clients that the name of the pseudo
|
||
|
* object has changed.
|
||
|
*/
|
||
|
void ServerNameTable_InformAllPseudoObjectsDocRenamed(
|
||
|
LPSERVERNAMETABLE lpServerNameTable,
|
||
|
LPMONIKER lpmkDoc
|
||
|
)
|
||
|
{
|
||
|
LPOUTLINENAMETABLE lpOutlineNameTable =
|
||
|
(LPOUTLINENAMETABLE)lpServerNameTable;
|
||
|
LPOUTLINENAME lpOutlineName;
|
||
|
LPPSEUDOOBJ lpPseudoObj;
|
||
|
LPMONIKER lpmkObj;
|
||
|
int i;
|
||
|
|
||
|
OLEDBG_BEGIN2("ServerNameTable_InformAllPseudoObjectsDocRenamed\r\n");
|
||
|
|
||
|
for(i = 0; i < lpOutlineNameTable->m_nCount; i++) {
|
||
|
lpOutlineName=OutlineNameTable_GetName(lpOutlineNameTable, i);
|
||
|
|
||
|
lpPseudoObj = ((LPSERVERNAME)lpOutlineName)->m_lpPseudoObj;
|
||
|
|
||
|
/* if there is a pseudo object associated with this name, then
|
||
|
** send OnRename advise to its linking clients.
|
||
|
*/
|
||
|
if (lpPseudoObj &&
|
||
|
((lpmkObj=PseudoObj_GetFullMoniker(lpPseudoObj,lpmkDoc))!=NULL)) {
|
||
|
|
||
|
// inform the clients that the name has changed
|
||
|
PseudoObj_SendAdvise (
|
||
|
lpPseudoObj,
|
||
|
OLE_ONRENAME,
|
||
|
lpmkObj,
|
||
|
0 /* advf -- not relevant here */
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
OLEDBG_END2
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ServerNameTable_InformAllPseudoObjectsDocSaved
|
||
|
* ------------------------------------------------
|
||
|
*
|
||
|
* Inform all pseudo object clients that the name of the pseudo
|
||
|
* object has changed.
|
||
|
*/
|
||
|
void ServerNameTable_InformAllPseudoObjectsDocSaved(
|
||
|
LPSERVERNAMETABLE lpServerNameTable,
|
||
|
LPMONIKER lpmkDoc
|
||
|
)
|
||
|
{
|
||
|
LPOUTLINENAMETABLE lpOutlineNameTable =
|
||
|
(LPOUTLINENAMETABLE)lpServerNameTable;
|
||
|
LPOUTLINENAME lpOutlineName;
|
||
|
LPPSEUDOOBJ lpPseudoObj;
|
||
|
LPMONIKER lpmkObj;
|
||
|
int i;
|
||
|
|
||
|
OLEDBG_BEGIN2("ServerNameTable_InformAllPseudoObjectsDocSaved\r\n");
|
||
|
|
||
|
for(i = 0; i < lpOutlineNameTable->m_nCount; i++) {
|
||
|
lpOutlineName=OutlineNameTable_GetName(lpOutlineNameTable, i);
|
||
|
|
||
|
lpPseudoObj = ((LPSERVERNAME)lpOutlineName)->m_lpPseudoObj;
|
||
|
|
||
|
/* if there is a pseudo object associated with this name, then
|
||
|
** send OnSave advise to its linking clients.
|
||
|
*/
|
||
|
if (lpPseudoObj &&
|
||
|
((lpmkObj=PseudoObj_GetFullMoniker(lpPseudoObj,lpmkDoc))!=NULL)) {
|
||
|
|
||
|
// inform the clients that the name has been saved
|
||
|
PseudoObj_SendAdvise (
|
||
|
lpPseudoObj,
|
||
|
OLE_ONSAVE,
|
||
|
NULL, /* lpmkDoc -- not relevant here */
|
||
|
0 /* advf -- not relevant here */
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
OLEDBG_END2
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ServerNameTable_SendPendingAdvises
|
||
|
* ----------------------------------
|
||
|
*
|
||
|
* Send any pending change notifications for pseudo objects.
|
||
|
* while ReDraw is diabled on the ServerDoc, then change advise
|
||
|
* notifications are not sent to pseudo object clients.
|
||
|
*/
|
||
|
void ServerNameTable_SendPendingAdvises(LPSERVERNAMETABLE lpServerNameTable)
|
||
|
{
|
||
|
LPOUTLINENAMETABLE lpOutlineNameTable =
|
||
|
(LPOUTLINENAMETABLE)lpServerNameTable;
|
||
|
LPSERVERNAME lpServerName;
|
||
|
int i;
|
||
|
|
||
|
for(i = 0; i < lpOutlineNameTable->m_nCount; i++) {
|
||
|
lpServerName = (LPSERVERNAME)OutlineNameTable_GetName(
|
||
|
lpOutlineNameTable,
|
||
|
i
|
||
|
);
|
||
|
ServerName_SendPendingAdvises(lpServerName);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ServerNameTable_GetPseudoObj
|
||
|
** ----------------------------
|
||
|
**
|
||
|
** Return a pointer to a pseudo object identified by an item string
|
||
|
** (lpszItem). if the pseudo object already exists, then return the
|
||
|
** existing object, otherwise allocate a new pseudo object.
|
||
|
*/
|
||
|
LPPSEUDOOBJ ServerNameTable_GetPseudoObj(
|
||
|
LPSERVERNAMETABLE lpServerNameTable,
|
||
|
LPSTR lpszItem,
|
||
|
LPSERVERDOC lpServerDoc
|
||
|
)
|
||
|
{
|
||
|
LPSERVERNAME lpServerName;
|
||
|
|
||
|
lpServerName = (LPSERVERNAME)OutlineNameTable_FindName(
|
||
|
(LPOUTLINENAMETABLE)lpServerNameTable,
|
||
|
lpszItem
|
||
|
);
|
||
|
|
||
|
if (lpServerName)
|
||
|
return ServerName_GetPseudoObj(lpServerName, lpServerDoc);
|
||
|
else
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ServerNameTable_CloseAllPseudoObjs
|
||
|
* ----------------------------------
|
||
|
*
|
||
|
* Force all pseudo objects to close. this results in sending OnClose
|
||
|
* notification to each pseudo object's linking clients.
|
||
|
*/
|
||
|
void ServerNameTable_CloseAllPseudoObjs(LPSERVERNAMETABLE lpServerNameTable)
|
||
|
{
|
||
|
LPOUTLINENAMETABLE lpOutlineNameTable =
|
||
|
(LPOUTLINENAMETABLE)lpServerNameTable;
|
||
|
LPSERVERNAME lpServerName;
|
||
|
int i;
|
||
|
|
||
|
OLEDBG_BEGIN3("ServerNameTable_CloseAllPseudoObjs\r\n")
|
||
|
|
||
|
for(i = 0; i < lpOutlineNameTable->m_nCount; i++) {
|
||
|
lpServerName = (LPSERVERNAME)OutlineNameTable_GetName(
|
||
|
lpOutlineNameTable,
|
||
|
i
|
||
|
);
|
||
|
ServerName_ClosePseudoObj(lpServerName);
|
||
|
}
|
||
|
|
||
|
OLEDBG_END3
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* ServerName_SetSel
|
||
|
* -----------------
|
||
|
*
|
||
|
* Change the line range of a name.
|
||
|
*/
|
||
|
void ServerName_SetSel(
|
||
|
LPSERVERNAME lpServerName,
|
||
|
LPLINERANGE lplrSel,
|
||
|
BOOL fRangeModified
|
||
|
)
|
||
|
{
|
||
|
LPOUTLINENAME lpOutlineName = (LPOUTLINENAME)lpServerName;
|
||
|
BOOL fPseudoObjChanged = fRangeModified;
|
||
|
|
||
|
if (lpOutlineName->m_nStartLine != lplrSel->m_nStartLine) {
|
||
|
lpOutlineName->m_nStartLine = lplrSel->m_nStartLine;
|
||
|
fPseudoObjChanged = TRUE;
|
||
|
}
|
||
|
|
||
|
if (lpOutlineName->m_nEndLine != lplrSel->m_nEndLine) {
|
||
|
lpOutlineName->m_nEndLine = lplrSel->m_nEndLine;
|
||
|
fPseudoObjChanged = TRUE;
|
||
|
}
|
||
|
|
||
|
/* OLE2NOTE: if the range of an active pseudo object has
|
||
|
** changed, then inform any linking clients that the object
|
||
|
** has changed.
|
||
|
*/
|
||
|
if (lpServerName->m_lpPseudoObj && fPseudoObjChanged) {
|
||
|
PseudoObj_SendAdvise(
|
||
|
lpServerName->m_lpPseudoObj,
|
||
|
OLE_ONDATACHANGE,
|
||
|
NULL, /* lpmkDoc -- not relevant here */
|
||
|
0 /* advf -- no flags necessary */
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ServerName_SendPendingAdvises
|
||
|
* -----------------------------
|
||
|
*
|
||
|
* Send any pending change notifications for the associated
|
||
|
* pseudo objects for this name (if one exists).
|
||
|
* while ReDraw is diabled on the ServerDoc, then change advise
|
||
|
* notifications are not sent to pseudo object clients.
|
||
|
*/
|
||
|
void ServerName_SendPendingAdvises(LPSERVERNAME lpServerName)
|
||
|
{
|
||
|
if (! lpServerName->m_lpPseudoObj)
|
||
|
return; // no associated pseudo object
|
||
|
|
||
|
if (lpServerName->m_lpPseudoObj->m_fDataChanged)
|
||
|
PseudoObj_SendAdvise(
|
||
|
lpServerName->m_lpPseudoObj,
|
||
|
OLE_ONDATACHANGE,
|
||
|
NULL, /* lpmkDoc -- not relevant here */
|
||
|
0 /* advf -- no flags necessary */
|
||
|
);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ServerName_GetPseudoObj
|
||
|
** -----------------------
|
||
|
**
|
||
|
** Return a pointer to a pseudo object associated to a ServerName.
|
||
|
** if the pseudo object already exists, then return the
|
||
|
** existing object, otherwise allocate a new pseudo object.
|
||
|
**
|
||
|
** NOTE: the PseudoObj is returned with a 0 refcnt if first created,
|
||
|
** else the existing refcnt is unchanged.
|
||
|
*/
|
||
|
LPPSEUDOOBJ ServerName_GetPseudoObj(
|
||
|
LPSERVERNAME lpServerName,
|
||
|
LPSERVERDOC lpServerDoc
|
||
|
)
|
||
|
{
|
||
|
// Check if a PseudoObj already exists
|
||
|
if (lpServerName->m_lpPseudoObj)
|
||
|
return lpServerName->m_lpPseudoObj;
|
||
|
|
||
|
// A PseudoObj does NOT already exist, allocate a new one.
|
||
|
lpServerName->m_lpPseudoObj=(LPPSEUDOOBJ) New((DWORD)sizeof(PSEUDOOBJ));
|
||
|
if (lpServerName->m_lpPseudoObj == NULL) {
|
||
|
OleDbgAssertSz(lpServerName->m_lpPseudoObj != NULL, "Error allocating PseudoObj");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
PseudoObj_Init(lpServerName->m_lpPseudoObj, lpServerName, lpServerDoc);
|
||
|
return lpServerName->m_lpPseudoObj;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ServerName_ClosePseudoObj
|
||
|
* -------------------------
|
||
|
*
|
||
|
* if there is an associated pseudo objects for this name (if one
|
||
|
* exists), then close it. this results in sending OnClose
|
||
|
* notification to the pseudo object's linking clients.
|
||
|
*/
|
||
|
void ServerName_ClosePseudoObj(LPSERVERNAME lpServerName)
|
||
|
{
|
||
|
if (!lpServerName || !lpServerName->m_lpPseudoObj)
|
||
|
return; // no associated pseudo object
|
||
|
|
||
|
PseudoObj_Close(lpServerName->m_lpPseudoObj);
|
||
|
}
|
||
|
|
||
|
|
||
|
#endif // OLE_SERVER
|
||
|
|
||
|
|
||
|
#if defined( OLE_CNTR )
|
||
|
|
||
|
|
||
|
/*************************************************************************
|
||
|
** ContainerDoc Supprt Functions Used by Container versions
|
||
|
*************************************************************************/
|
||
|
|
||
|
|
||
|
/* ContainerLine_GetRelMoniker
|
||
|
** ---------------------------
|
||
|
** Retrieve the relative item moniker which identifies the OLE object
|
||
|
** relative to the container document.
|
||
|
**
|
||
|
** Returns NULL if a moniker can NOT be created.
|
||
|
*/
|
||
|
LPMONIKER ContainerLine_GetRelMoniker(
|
||
|
LPCONTAINERLINE lpContainerLine,
|
||
|
DWORD dwAssign
|
||
|
)
|
||
|
{
|
||
|
LPMONIKER lpmk = NULL;
|
||
|
|
||
|
/* OLE2NOTE: we should only give out a moniker for the OLE object
|
||
|
** if the object is allowed to be linked to from the inside. if
|
||
|
** so we are allowed to give out a moniker which binds to the
|
||
|
** running OLE object). if the object is an OLE 2.0 embedded
|
||
|
** object then it is allowed to be linked to from the inside. if
|
||
|
** the object is either an OleLink or an OLE 1.0 embedding
|
||
|
** then it can not be linked to from the inside.
|
||
|
** if we were a container/server app then we could offer linking
|
||
|
** to the outside of the object (ie. a pseudo object within our
|
||
|
** document). we are a container only app that does not support
|
||
|
** linking to ranges of its data.
|
||
|
*/
|
||
|
|
||
|
switch (dwAssign) {
|
||
|
|
||
|
case GETMONIKER_FORCEASSIGN:
|
||
|
|
||
|
/* Force the assignment of the name. This is called when a
|
||
|
** Paste Link actually occurs. From now on we want
|
||
|
** to inform the OLE object that its moniker is
|
||
|
** assigned and is thus necessary to register itself
|
||
|
** in the RunningObjectTable.
|
||
|
*/
|
||
|
CreateItemMonikerA(
|
||
|
OLESTDDELIM, lpContainerLine->m_szStgName, &lpmk);
|
||
|
|
||
|
/* OLE2NOTE: if the OLE object is already loaded and it
|
||
|
** is being assigned a moniker for the first time,
|
||
|
** then we need to inform it that it now has a moniker
|
||
|
** assigned by calling IOleObject::SetMoniker. this
|
||
|
** will force the OLE object to register in the
|
||
|
** RunningObjectTable when it enters the running
|
||
|
** state. if the object is not currently loaded,
|
||
|
** SetMoniker will be called automatically later when
|
||
|
** the object is loaded by the function
|
||
|
** ContainerLine_LoadOleObject.
|
||
|
*/
|
||
|
if (! lpContainerLine->m_fMonikerAssigned) {
|
||
|
|
||
|
/* we must remember forever more that this object has a
|
||
|
** moniker assigned.
|
||
|
*/
|
||
|
lpContainerLine->m_fMonikerAssigned = TRUE;
|
||
|
|
||
|
// we are now dirty and must be saved
|
||
|
OutlineDoc_SetModified(
|
||
|
(LPOUTLINEDOC)lpContainerLine->m_lpDoc,
|
||
|
TRUE, /* fModified */
|
||
|
FALSE, /* fDataChanged--N/A for container ver. */
|
||
|
FALSE /* fSizeChanged--N/A for container ver. */
|
||
|
);
|
||
|
|
||
|
if (lpContainerLine->m_lpOleObj) {
|
||
|
OLEDBG_BEGIN2("IOleObject::SetMoniker called\r\n")
|
||
|
lpContainerLine->m_lpOleObj->lpVtbl->SetMoniker(
|
||
|
lpContainerLine->m_lpOleObj,
|
||
|
OLEWHICHMK_OBJREL,
|
||
|
lpmk
|
||
|
);
|
||
|
OLEDBG_END2
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case GETMONIKER_ONLYIFTHERE:
|
||
|
|
||
|
/* If the OLE object currently has a moniker assigned,
|
||
|
** then return it.
|
||
|
*/
|
||
|
if (lpContainerLine->m_fMonikerAssigned) {
|
||
|
|
||
|
CreateItemMonikerA(
|
||
|
OLESTDDELIM,
|
||
|
lpContainerLine->m_szStgName,
|
||
|
&lpmk
|
||
|
);
|
||
|
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case GETMONIKER_TEMPFORUSER:
|
||
|
|
||
|
/* Return the moniker that would be used for the OLE
|
||
|
** object but do NOT force moniker assignment at
|
||
|
** this point. Since our strategy is to use the
|
||
|
** storage name of the object as its item name, we
|
||
|
** can simply create the corresponding ItemMoniker
|
||
|
** (indepenedent of whether the moniker is currently
|
||
|
** assigned or not).
|
||
|
*/
|
||
|
CreateItemMonikerA(
|
||
|
OLESTDDELIM,
|
||
|
lpContainerLine->m_szStgName,
|
||
|
&lpmk
|
||
|
);
|
||
|
|
||
|
break;
|
||
|
|
||
|
case GETMONIKER_UNASSIGN:
|
||
|
|
||
|
lpContainerLine->m_fMonikerAssigned = FALSE;
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
return lpmk;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ContainerLine_GetFullMoniker
|
||
|
** ----------------------------
|
||
|
** Retrieve the full absolute moniker which identifies the OLE object
|
||
|
** in the container document.
|
||
|
** this moniker is created as a composite of the absolute moniker for
|
||
|
** the entire document appended with an item moniker which identifies
|
||
|
** the OLE object relative to the document.
|
||
|
** Returns NULL if a moniker can NOT be created.
|
||
|
*/
|
||
|
LPMONIKER ContainerLine_GetFullMoniker(
|
||
|
LPCONTAINERLINE lpContainerLine,
|
||
|
DWORD dwAssign
|
||
|
)
|
||
|
{
|
||
|
LPMONIKER lpmkDoc = NULL;
|
||
|
LPMONIKER lpmkItem = NULL;
|
||
|
LPMONIKER lpmkFull = NULL;
|
||
|
|
||
|
lpmkDoc = OleDoc_GetFullMoniker(
|
||
|
(LPOLEDOC)lpContainerLine->m_lpDoc,
|
||
|
dwAssign
|
||
|
);
|
||
|
if (! lpmkDoc) return NULL;
|
||
|
|
||
|
lpmkItem = ContainerLine_GetRelMoniker(lpContainerLine, dwAssign);
|
||
|
|
||
|
if (lpmkItem) {
|
||
|
CreateGenericComposite(lpmkDoc, lpmkItem, (LPMONIKER FAR*)&lpmkFull);
|
||
|
OleStdRelease((LPUNKNOWN)lpmkItem);
|
||
|
}
|
||
|
|
||
|
if (lpmkDoc)
|
||
|
OleStdRelease((LPUNKNOWN)lpmkDoc);
|
||
|
|
||
|
return lpmkFull;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ContainerDoc_InformAllOleObjectsDocRenamed
|
||
|
** ------------------------------------------
|
||
|
** Inform all OLE objects that the name of the ContainerDoc has changed.
|
||
|
*/
|
||
|
void ContainerDoc_InformAllOleObjectsDocRenamed(
|
||
|
LPCONTAINERDOC lpContainerDoc,
|
||
|
LPMONIKER lpmkDoc
|
||
|
)
|
||
|
{
|
||
|
LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
|
||
|
int i;
|
||
|
LPLINE lpLine;
|
||
|
|
||
|
for (i = 0; i < lpLL->m_nNumLines; i++) {
|
||
|
lpLine=LineList_GetLine(lpLL, i);
|
||
|
|
||
|
if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE)) {
|
||
|
LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)lpLine;
|
||
|
|
||
|
/* OLE2NOTE: if the OLE object is already loaded AND the
|
||
|
** object already has a moniker assigned, then we need
|
||
|
** to inform it that the moniker of the ContainerDoc has
|
||
|
** changed. of course, this means the full moniker of
|
||
|
** the object has changed. to do this we call
|
||
|
** IOleObject::SetMoniker. this will force the OLE
|
||
|
** object to re-register in the RunningObjectTable if it
|
||
|
** is currently in the running state. it is not in the
|
||
|
** running state, the object handler can make not that
|
||
|
** the object has a new moniker. if the object is not
|
||
|
** currently loaded, SetMoniker will be called
|
||
|
** automatically later when the object is loaded by the
|
||
|
** function ContainerLine_LoadOleObject.
|
||
|
** also if the object is a linked object, we always want
|
||
|
** to call SetMoniker on the link so that in case the
|
||
|
** link source is contained within our same container,
|
||
|
** the link source will be tracked. the link rebuilds
|
||
|
** its absolute moniker if it has a relative moniker.
|
||
|
*/
|
||
|
if (lpContainerLine->m_lpOleObj) {
|
||
|
if (lpContainerLine->m_fMonikerAssigned ||
|
||
|
lpContainerLine->m_dwLinkType != 0) {
|
||
|
OLEDBG_BEGIN2("IOleObject::SetMoniker called\r\n")
|
||
|
lpContainerLine->m_lpOleObj->lpVtbl->SetMoniker(
|
||
|
lpContainerLine->m_lpOleObj,
|
||
|
OLEWHICHMK_CONTAINER,
|
||
|
lpmkDoc
|
||
|
);
|
||
|
OLEDBG_END2
|
||
|
}
|
||
|
|
||
|
/* OLE2NOTE: we must call IOleObject::SetHostNames so
|
||
|
** any open objects can update their window titles.
|
||
|
*/
|
||
|
OLEDBG_BEGIN2("IOleObject::SetHostNames called\r\n")
|
||
|
|
||
|
CallIOleObjectSetHostNamesA(
|
||
|
lpContainerLine->m_lpOleObj,
|
||
|
APPNAME,
|
||
|
((LPOUTLINEDOC)lpContainerDoc)->m_lpszDocTitle
|
||
|
);
|
||
|
|
||
|
OLEDBG_END2
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ContainerDoc_GetObject
|
||
|
** ----------------------
|
||
|
** Return a pointer to the desired interface of an object identified
|
||
|
** by an item string (lpszItem). the object returned will be an OLE
|
||
|
** object (either link or embedding).
|
||
|
**
|
||
|
** OLE2NOTE: we must force the object to run because we are
|
||
|
** REQUIRED to return a pointer the OLE object in the
|
||
|
** RUNNING state.
|
||
|
**
|
||
|
** dwSpeedNeeded indicates how long the caller is willing
|
||
|
** to wait for us to get the object:
|
||
|
** BINDSPEED_IMMEDIATE -- only if obj already loaded && IsRunning
|
||
|
** BINDSPEED_MODERATE -- load obj if necessary && if IsRunning
|
||
|
** BINDSPEED_INDEFINITE-- force obj to load and run if necessary
|
||
|
*/
|
||
|
HRESULT ContainerDoc_GetObjectA(
|
||
|
LPCONTAINERDOC lpContainerDoc,
|
||
|
LPSTR lpszItem,
|
||
|
DWORD dwSpeedNeeded,
|
||
|
REFIID riid,
|
||
|
LPVOID FAR* lplpvObject
|
||
|
)
|
||
|
{
|
||
|
LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
|
||
|
int i;
|
||
|
LPLINE lpLine;
|
||
|
BOOL fMatchFound = FALSE;
|
||
|
DWORD dwStatus;
|
||
|
HRESULT hrErr;
|
||
|
|
||
|
*lplpvObject = NULL;
|
||
|
|
||
|
for (i = 0; i < lpLL->m_nNumLines; i++) {
|
||
|
lpLine=LineList_GetLine(lpLL, i);
|
||
|
|
||
|
if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE)) {
|
||
|
LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)lpLine;
|
||
|
|
||
|
if (lstrcmp(lpContainerLine->m_szStgName, lpszItem) == 0) {
|
||
|
|
||
|
fMatchFound = TRUE; // valid item name
|
||
|
|
||
|
// check if object is loaded.
|
||
|
if (lpContainerLine->m_lpOleObj == NULL) {
|
||
|
|
||
|
// if BINDSPEED_IMMEDIATE is requested, object must
|
||
|
// ALREADY be loadded.
|
||
|
if (dwSpeedNeeded == BINDSPEED_IMMEDIATE)
|
||
|
return ResultFromScode(MK_E_EXCEEDEDDEADLINE);
|
||
|
|
||
|
ContainerLine_LoadOleObject(lpContainerLine);
|
||
|
if (! lpContainerLine->m_lpOleObj)
|
||
|
return ResultFromScode(E_OUTOFMEMORY);
|
||
|
}
|
||
|
|
||
|
/* OLE2NOTE: check if the object is allowed to be linked
|
||
|
** to from the inside (ie. we are allowed to
|
||
|
** give out a moniker which binds to the running
|
||
|
** OLE object). if the object is an OLE
|
||
|
** 2.0 embedded object then it is allowed to be
|
||
|
** linked to from the inside. if the object is
|
||
|
** either an OleLink or an OLE 1.0 embedding
|
||
|
** then it can not be linked to from the inside.
|
||
|
** if we were a container/server app then we
|
||
|
** could offer linking to the outside of the
|
||
|
** object (ie. a pseudo object within our
|
||
|
** document). we are a container only app that
|
||
|
** does not support linking to ranges of its data.
|
||
|
*/
|
||
|
OLEDBG_BEGIN2("IOleObject::GetMiscStatus called\r\n");
|
||
|
lpContainerLine->m_lpOleObj->lpVtbl->GetMiscStatus(
|
||
|
lpContainerLine->m_lpOleObj,
|
||
|
DVASPECT_CONTENT, /* aspect is not important */
|
||
|
(LPDWORD)&dwStatus
|
||
|
);
|
||
|
OLEDBG_END2
|
||
|
if (dwStatus & OLEMISC_CANTLINKINSIDE)
|
||
|
return ResultFromScode(MK_E_NOOBJECT);
|
||
|
|
||
|
// check if object is running.
|
||
|
if (! OleIsRunning(lpContainerLine->m_lpOleObj)) {
|
||
|
|
||
|
// if BINDSPEED_MODERATE is requested, object must
|
||
|
// ALREADY be running.
|
||
|
if (dwSpeedNeeded == BINDSPEED_MODERATE)
|
||
|
return ResultFromScode(MK_E_EXCEEDEDDEADLINE);
|
||
|
|
||
|
/* OLE2NOTE: we have found a match for the item name.
|
||
|
** now we must return a pointer to the desired
|
||
|
** interface on the RUNNING object. we must
|
||
|
** carefully load the object and initially ask for
|
||
|
** an interface that we are sure the loaded form of
|
||
|
** the object supports. if we immediately ask the
|
||
|
** loaded object for the desired interface, the
|
||
|
** QueryInterface call might fail if it is an
|
||
|
** interface that is supported only when the object
|
||
|
** is running. thus we force the object to load and
|
||
|
** return its IUnknown*. then we force the object to
|
||
|
** run, and then finally, we can ask for the
|
||
|
** actually requested interface.
|
||
|
*/
|
||
|
hrErr = ContainerLine_RunOleObject(lpContainerLine);
|
||
|
if (hrErr != NOERROR) {
|
||
|
return hrErr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Retrieve the requested interface
|
||
|
*lplpvObject = OleStdQueryInterface(
|
||
|
(LPUNKNOWN)lpContainerLine->m_lpOleObj, riid);
|
||
|
|
||
|
break; // Match FOUND!
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (*lplpvObject != NULL) {
|
||
|
return NOERROR;
|
||
|
} else
|
||
|
return (fMatchFound ? ResultFromScode(E_NOINTERFACE)
|
||
|
: ResultFromScode(MK_E_NOOBJECT));
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT ContainerDoc_GetObject(
|
||
|
LPCONTAINERDOC lpContainerDoc,
|
||
|
LPOLESTR lpszItem,
|
||
|
DWORD dwSpeedNeeded,
|
||
|
REFIID riid,
|
||
|
LPVOID FAR* lplpvObject
|
||
|
)
|
||
|
{
|
||
|
CREATESTR(lpsz, lpszItem)
|
||
|
|
||
|
HRESULT hr = ContainerDoc_GetObjectA(lpContainerDoc, lpsz, dwSpeedNeeded,
|
||
|
riid, lplpvObject);
|
||
|
|
||
|
FREESTR(lpsz)
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
/* ContainerDoc_GetObjectStorage
|
||
|
** -----------------------------
|
||
|
** Return a pointer to the IStorage* used by the object identified
|
||
|
** by an item string (lpszItem). the object identified could be either
|
||
|
** an OLE object (either link or embedding).
|
||
|
*/
|
||
|
HRESULT ContainerDoc_GetObjectStorageA(
|
||
|
LPCONTAINERDOC lpContainerDoc,
|
||
|
LPSTR lpszItem,
|
||
|
LPSTORAGE FAR* lplpStg
|
||
|
)
|
||
|
{
|
||
|
LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
|
||
|
int i;
|
||
|
LPLINE lpLine;
|
||
|
|
||
|
*lplpStg = NULL;
|
||
|
|
||
|
for (i = 0; i < lpLL->m_nNumLines; i++) {
|
||
|
lpLine=LineList_GetLine(lpLL, i);
|
||
|
|
||
|
if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE)) {
|
||
|
LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)lpLine;
|
||
|
|
||
|
if (lstrcmp(lpContainerLine->m_szStgName, lpszItem) == 0) {
|
||
|
|
||
|
*lplpStg = lpContainerLine->m_lpStg;
|
||
|
break; // Match FOUND!
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (*lplpStg != NULL) {
|
||
|
return NOERROR;
|
||
|
} else
|
||
|
return ResultFromScode(MK_E_NOOBJECT);
|
||
|
}
|
||
|
|
||
|
HRESULT ContainerDoc_GetObjectStorage(
|
||
|
LPCONTAINERDOC lpContainerDoc,
|
||
|
LPOLESTR lpszItem,
|
||
|
LPSTORAGE FAR* lplpStg
|
||
|
)
|
||
|
{
|
||
|
CREATESTR(lpsz, lpszItem)
|
||
|
|
||
|
HRESULT hr = ContainerDoc_GetObjectStorageA(lpContainerDoc, lpsz, lplpStg);
|
||
|
|
||
|
FREESTR(lpsz)
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
/* ContainerDoc_IsRunning
|
||
|
** ----------------------
|
||
|
** Check if the object identified by an item string (lpszItem) is in
|
||
|
** the running state.
|
||
|
** For a container-only app, a check is made if the OLE object
|
||
|
** associated with the item name is running.
|
||
|
*/
|
||
|
HRESULT ContainerDoc_IsRunningA(LPCONTAINERDOC lpContainerDoc, LPSTR lpszItem)
|
||
|
{
|
||
|
LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
|
||
|
int i;
|
||
|
LPLINE lpLine;
|
||
|
DWORD dwStatus;
|
||
|
|
||
|
for (i = 0; i < lpLL->m_nNumLines; i++) {
|
||
|
lpLine=LineList_GetLine(lpLL, i);
|
||
|
|
||
|
if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE)) {
|
||
|
LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)lpLine;
|
||
|
|
||
|
if (lstrcmp(lpContainerLine->m_szStgName, lpszItem) == 0) {
|
||
|
|
||
|
/* OLE2NOTE: we have found a match for the item name.
|
||
|
** now we must check if the OLE object is running.
|
||
|
** we will load the object if not already loaded.
|
||
|
*/
|
||
|
if (! lpContainerLine->m_lpOleObj) {
|
||
|
ContainerLine_LoadOleObject(lpContainerLine);
|
||
|
if (! lpContainerLine->m_lpOleObj)
|
||
|
return ResultFromScode(E_OUTOFMEMORY);
|
||
|
}
|
||
|
|
||
|
/* OLE2NOTE: check if the object is allowed to be linked
|
||
|
** to from the inside (ie. we are allowed to
|
||
|
** give out a moniker which binds to the running
|
||
|
** OLE object). if the object is an OLE
|
||
|
** 2.0 embedded object then it is allowed to be
|
||
|
** linked to from the inside. if the object is
|
||
|
** either an OleLink or an OLE 1.0 embedding
|
||
|
** then it can not be linked to from the inside.
|
||
|
** if we were a container/server app then we
|
||
|
** could offer linking to the outside of the
|
||
|
** object (ie. a pseudo object within our
|
||
|
** document). we are a container only app that
|
||
|
** does not support linking to ranges of its data.
|
||
|
*/
|
||
|
OLEDBG_BEGIN2("IOleObject::GetMiscStatus called\r\n")
|
||
|
lpContainerLine->m_lpOleObj->lpVtbl->GetMiscStatus(
|
||
|
lpContainerLine->m_lpOleObj,
|
||
|
DVASPECT_CONTENT, /* aspect is not important */
|
||
|
(LPDWORD)&dwStatus
|
||
|
);
|
||
|
OLEDBG_END2
|
||
|
if (dwStatus & OLEMISC_CANTLINKINSIDE)
|
||
|
return ResultFromScode(MK_E_NOOBJECT);
|
||
|
|
||
|
if (OleIsRunning(lpContainerLine->m_lpOleObj))
|
||
|
return NOERROR;
|
||
|
else
|
||
|
return ResultFromScode(S_FALSE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// no object was found corresponding to the item name
|
||
|
return ResultFromScode(MK_E_NOOBJECT);
|
||
|
}
|
||
|
|
||
|
HRESULT ContainerDoc_IsRunning(LPCONTAINERDOC lpContainerDoc, LPOLESTR lpszItem)
|
||
|
{
|
||
|
CREATESTR(lpsz, lpszItem)
|
||
|
|
||
|
HRESULT hr = ContainerDoc_IsRunningA(lpContainerDoc, lpsz);
|
||
|
|
||
|
FREESTR(lpsz)
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
#endif // OLE_CNTR
|