mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1210 lines
28 KiB
1210 lines
28 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: Item.c Object(item) main module
|
|
*
|
|
* Purpose: Includes All the object releated routiens.
|
|
*
|
|
* Created: Oct 1990.
|
|
*
|
|
* Copyright (c) 1990, 1991 Microsoft Corporation
|
|
*
|
|
* History:
|
|
* Raor (../10/1990) Designed, coded
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
|
|
#include "ole2int.h"
|
|
//#include "cmacs.h"
|
|
#include <dde.h>
|
|
#include "ddeatoms.h"
|
|
#include "ddedebug.h"
|
|
#include "srvr.h"
|
|
#include "itemutil.h"
|
|
|
|
ASSERTDATA
|
|
|
|
|
|
// !!!change child enumeration.
|
|
// !!!No consistency in errors (Sometimes Bools and sometimes HRESULT).
|
|
|
|
|
|
//SearchItem: Searches for a given item in a document tree.
|
|
//If found, returns the corresponding client ptr.
|
|
|
|
INTERNAL_(LPCLIENT) CDefClient::SearchItem
|
|
(
|
|
LPOLESTR lpitemname
|
|
)
|
|
|
|
{
|
|
ATOM aItem;
|
|
LPCLIENT lpclient;
|
|
|
|
ChkC(this);
|
|
Assert (m_pdoc==this);
|
|
Assert (m_bContainer);
|
|
|
|
Puts ("DefClient::SearchItem\r\n");
|
|
// If the item passed is an atom, get its name.
|
|
if (!HIWORD(lpitemname))
|
|
aItem = (ATOM) (LOWORD((DWORD)lpitemname));
|
|
else if (!lpitemname[0])
|
|
aItem = NULL;
|
|
else
|
|
aItem = GlobalFindAtom (lpitemname);
|
|
|
|
// walk thru the items list and mtach for the itemname.
|
|
lpclient = this;
|
|
|
|
while (lpclient) {
|
|
ChkC(lpclient);
|
|
if (lpclient->m_aItem == aItem)
|
|
return lpclient;
|
|
// The NULL item is the client that is a container (the whole doc).
|
|
// REVIEW: jasonful
|
|
if (lpclient->m_bContainer && aItem==NULL)
|
|
return lpclient;
|
|
lpclient = lpclient->m_lpNextItem;
|
|
}
|
|
|
|
Puts ("SearchItem failed\r\n");
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// FindItem: Given the itemname and the doc obj ptr,
|
|
// searches for the the item (object) in the document tree.
|
|
// Items are lonked to the doc obj.
|
|
|
|
INTERNAL_(HRESULT) CDefClient::FindItem
|
|
(
|
|
LPOLESTR lpitemname,
|
|
LPCLIENT FAR * lplpclient
|
|
)
|
|
{
|
|
LPCLIENT lpclient;
|
|
WCHAR buf[MAX_STR];
|
|
|
|
Puts ("DefClient::FindItem "); Puts (lpitemname); Putn();
|
|
ChkC(this);
|
|
|
|
if (lpclient = SearchItem (lpitemname)) {
|
|
// we found the item window
|
|
|
|
ChkC(lpclient);
|
|
*lplpclient = lpclient;
|
|
return NOERROR;
|
|
|
|
}
|
|
|
|
if (!HIWORD(lpitemname)){
|
|
if (LOWORD(lpitemname))
|
|
GlobalGetAtomName ((ATOM)LOWORD((DWORD)lpitemname),
|
|
buf, MAX_STR);
|
|
else
|
|
buf[0] = NULL;
|
|
|
|
lpitemname = buf;
|
|
}
|
|
|
|
// Item (object)window is not created yet. Let us create one.
|
|
return RegisterItem (lpitemname, lplpclient, TRUE);
|
|
}
|
|
|
|
|
|
|
|
//RegisterItem: Given the document handle and the item string
|
|
//creates item with the given name in the doc obj list..
|
|
|
|
INTERNAL CDefClient::RegisterItem
|
|
(LPOLESTR lpitemname,
|
|
LPCLIENT FAR * lplpclient,
|
|
BOOL bSrvr)
|
|
{
|
|
LPCLIENT pitemNew = NULL;
|
|
HRESULT hresult = ReportResult(0, E_UNEXPECTED, 0, 0);
|
|
LPOLEOBJECT lpoleObj = NULL;
|
|
LPOLEITEMCONTAINER lpcontainer;
|
|
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%x _IN CDefClient::RegisterItem(%ws)\n",
|
|
this,WIDECHECK(lpitemname)));
|
|
ChkC(this);
|
|
AssertIsDoc(this);
|
|
*lplpclient = NULL;
|
|
|
|
ErrZS (pitemNew = new CDefClient(NULL), E_OUTOFMEMORY);
|
|
|
|
pitemNew->m_bTerminate = FALSE;
|
|
pitemNew->m_bContainer = FALSE; // not a container, i.e.,document
|
|
|
|
|
|
// Set containing document
|
|
pitemNew->m_pdoc = this;
|
|
m_pUnkOuter->AddRef(); // item keeps its document alive
|
|
// Corresponding Release is in CDefClient::~CDefClient
|
|
|
|
if (!HIWORD(lpitemname)) {
|
|
AssertSz (!bSrvr, "invalid lpitemname in RegisterItem\r\n");
|
|
pitemNew->m_aItem = LOWORD((DWORD)lpitemname);
|
|
}
|
|
else if (!lpitemname[0])
|
|
pitemNew->m_aItem = NULL;
|
|
else
|
|
pitemNew->m_aItem = wGlobalAddAtom (lpitemname);
|
|
|
|
lpoleObj = m_lpoleObj;
|
|
|
|
// Call the server if the item is not one of the standard items.
|
|
if (bSrvr) {
|
|
|
|
// Call the server app for container interface
|
|
hresult = lpoleObj->QueryInterface (IID_IOleItemContainer, (LPVOID FAR *)&lpcontainer);
|
|
|
|
if (hresult != NOERROR)
|
|
{
|
|
intrDebugOut((DEB_IERROR,
|
|
"%x ::RegisterItem(%ws) No IOleContainer intr\n",
|
|
this,WIDECHECK(lpitemname)));
|
|
goto errRtn;
|
|
}
|
|
|
|
hresult = lpcontainer->GetObject(lpitemname, BINDSPEED_INDEFINITE, 0,
|
|
IID_IOleObject, (LPLPVOID)&pitemNew->m_lpoleObj);
|
|
|
|
if (hresult != NOERROR)
|
|
{
|
|
intrDebugOut((DEB_ERROR,
|
|
"IOleItemContainer::GetObject(%ws,...) failed (hr=%x)\n",
|
|
lpitemname,
|
|
hresult));
|
|
}
|
|
|
|
lpcontainer->Release ();
|
|
if (hresult != NOERROR)
|
|
goto errRtn;
|
|
|
|
hresult = pitemNew->m_lpoleObj->QueryInterface (IID_IDataObject, (LPLPVOID)
|
|
&pitemNew->m_lpdataObj);
|
|
|
|
if (hresult != NOERROR)
|
|
{
|
|
intrDebugOut((DEB_ERROR,
|
|
"::QueryInterface(IID_IDataObject) failed (hr=%x)\n",
|
|
hresult));
|
|
pitemNew->m_lpoleObj->Release();
|
|
goto errRtn;
|
|
}
|
|
|
|
|
|
// This is for Packager, in particular. If client does not advise
|
|
// on any data, we still need to do an OLE advise so we can get
|
|
// OnClose notifications.
|
|
pitemNew->DoOle20Advise (OLE_CLOSED, (CLIPFORMAT)0);
|
|
}
|
|
|
|
|
|
|
|
// This keeps the CDefClient alive until _we_ are done with it
|
|
// The corresponding Release is in CDefClient::Revoke
|
|
pitemNew->m_pUnkOuter->AddRef();
|
|
|
|
pitemNew->m_lpNextItem = m_lpNextItem;
|
|
pitemNew->m_hwnd = m_hwnd; // set the window handle to
|
|
// same as the doc level window
|
|
|
|
m_lpNextItem = pitemNew;
|
|
*lplpclient = pitemNew;
|
|
|
|
hresult = NOERROR;
|
|
goto exitRtn;
|
|
|
|
errRtn:
|
|
if (pitemNew) {
|
|
delete pitemNew;
|
|
}
|
|
|
|
exitRtn:
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%x CDefClient::RegisterItem(%ws) hresult=%x\n",
|
|
this,WIDECHECK(lpitemname),hresult));
|
|
|
|
|
|
return(hresult);
|
|
}
|
|
|
|
|
|
|
|
// Return NOERROR if "this" document has no items which have connections
|
|
// (client windows).
|
|
//
|
|
INTERNAL CDefClient::NoItemConnections (void)
|
|
{
|
|
PCLINFO pclinfo = NULL;
|
|
HANDLE hcliPrev = NULL;
|
|
HANDLE hcli;
|
|
PCLILIST pcli;
|
|
HANDLE *phandle;
|
|
|
|
ChkCR (this);
|
|
AssertIsDoc (this);
|
|
LPCLIENT pitem;
|
|
for (pitem = m_lpNextItem;
|
|
pitem;
|
|
pitem = pitem->m_lpNextItem)
|
|
{
|
|
ChkCR (pitem);
|
|
if (pitem->m_aItem == aStdDocName)
|
|
continue;
|
|
hcli = pitem->m_hcliInfo;
|
|
while (hcli)
|
|
{
|
|
if ((pcli = (PCLILIST) LocalLock (hcli)) == NULL)
|
|
return ResultFromScode (S_FALSE);
|
|
|
|
phandle = (HANDLE *) (pcli->info);
|
|
while (phandle < (HANDLE *)(pcli + 1))
|
|
{
|
|
if (*phandle)
|
|
{
|
|
LocalUnlock (hcli);
|
|
return ResultFromScode (S_FALSE);
|
|
}
|
|
else
|
|
{
|
|
phandle++;
|
|
phandle++;
|
|
}
|
|
}
|
|
|
|
hcliPrev = hcli;
|
|
hcli = pcli->hcliNext;
|
|
LocalUnlock (hcliPrev);
|
|
}
|
|
}
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
|
|
INTERNAL_(void) CDefClient::DeleteAdviseInfo (void)
|
|
{
|
|
|
|
|
|
PCLINFO pclinfo = NULL;
|
|
HANDLE hcliPrev = NULL;
|
|
PCLILIST pcli;
|
|
HANDLE *phandle;
|
|
HANDLE hcli;
|
|
HANDLE hcliInfo;
|
|
|
|
Puts ("DefClient::DeleteAdviseInfo\r\n");
|
|
ChkC(this);
|
|
hcli = m_hcliInfo;
|
|
while (hcli) {
|
|
if ((pcli = (PCLILIST) LocalLock (hcli)) == NULL)
|
|
return;
|
|
|
|
phandle = (HANDLE *) (pcli->info);
|
|
while (phandle < (HANDLE *)(pcli + 1)) {
|
|
if (*phandle) {
|
|
*phandle++ = 0;
|
|
|
|
// delete the printer dev info block
|
|
if(pclinfo = (PCLINFO)LocalLock ((hcliInfo = *phandle++))){
|
|
if(pclinfo->hdevInfo)
|
|
GlobalFree (pclinfo->hdevInfo);
|
|
|
|
LocalUnlock (hcliInfo);
|
|
// no free if lock failed
|
|
LocalFree (hcliInfo);
|
|
}
|
|
} else {
|
|
phandle++;
|
|
phandle++;
|
|
|
|
}
|
|
}
|
|
|
|
hcliPrev = hcli;
|
|
hcli = pcli->hcliNext;
|
|
LocalUnlock (hcliPrev);
|
|
LocalFree (hcliPrev); // free the block;
|
|
}
|
|
m_hcliInfo = NULL;
|
|
}
|
|
|
|
|
|
|
|
//DeleteFromItemsList: Deletes a client from the object lists of
|
|
//all the objects of a given document. Thie client possibly
|
|
//is terminating the conversation with our doc window.
|
|
//
|
|
INTERNAL_(void) CDefClient::DeleteFromItemsList
|
|
(HWND hwndClient)
|
|
{
|
|
HANDLE hclinfo;
|
|
PCLINFO pclinfo;
|
|
LPCLIENT lpclient;
|
|
LPCLIENT FAR* ppitemLast = NULL;
|
|
BOOL fRevokedDoc = FALSE;
|
|
static int staticcounter;
|
|
int counter = ++staticcounter;
|
|
|
|
Puts ("DefClient::DeleteFromItemsList "); Puti(counter); Putn();
|
|
AssertIsDoc(this);
|
|
lpclient = this;
|
|
ppitemLast = &m_lpNextItem;
|
|
while (lpclient)
|
|
{
|
|
ChkC(lpclient);
|
|
BOOL fDoc = (lpclient==this);
|
|
if (fDoc)
|
|
{
|
|
AssertIsDoc (lpclient);
|
|
// Remove window from doc's master list
|
|
HWND hwnd = (HWND) FindClient (lpclient->m_hcli, hwndClient, /*fDelete*/TRUE);
|
|
Assert (hwnd==hwndClient);
|
|
}
|
|
|
|
hclinfo = FindClient (lpclient->m_hcliInfo, hwndClient, /*fDelete*/TRUE);
|
|
LPCLIENT pitemNext = lpclient->m_lpNextItem;
|
|
|
|
// We must make sure no other client is connected (linked)
|
|
// to this item before deleting.
|
|
if (!fDoc && AreNoClients (lpclient->m_hcliInfo))
|
|
{
|
|
Assert (ppitemLast);
|
|
if (ppitemLast && !fDoc)
|
|
{
|
|
// Remove from linked list
|
|
*ppitemLast = lpclient->m_lpNextItem;
|
|
}
|
|
fRevokedDoc |= fDoc;
|
|
lpclient->Revoke ();
|
|
}
|
|
else
|
|
{
|
|
ppitemLast = &(lpclient->m_lpNextItem);
|
|
}
|
|
if (hclinfo)
|
|
{
|
|
if(pclinfo = (PCLINFO)LocalLock (hclinfo))
|
|
{
|
|
if(pclinfo->hdevInfo)
|
|
GlobalFree (pclinfo->hdevInfo);
|
|
LocalUnlock (hclinfo);
|
|
}
|
|
LocalFree (hclinfo);
|
|
}
|
|
lpclient = pitemNext;
|
|
}
|
|
|
|
// Handle invisible update
|
|
if (!fRevokedDoc && !m_fEmbed //&& !m_fGotDdeAdvise
|
|
&& NOERROR ==NoItemConnections()
|
|
&& AreNoClients (m_hcliInfo)
|
|
&& AreNoClients (m_hcli) )
|
|
{
|
|
ChkC (this);
|
|
Assert (m_lpoleObj);
|
|
Assert (m_lpdataObj);
|
|
ReleaseObjPtrs();
|
|
}
|
|
|
|
Puts ("DefClient::DeleteFromItemsList Done "); Puti(counter); Putn();
|
|
}
|
|
|
|
|
|
INTERNAL_(void) CDefClient::RemoveItemFromItemList
|
|
(void)
|
|
{
|
|
// Make sure it's an item
|
|
Assert (m_pdoc != this && !m_bContainer);
|
|
|
|
LPCLIENT lpclient = m_pdoc;
|
|
ChkC (lpclient);
|
|
LPCLIENT FAR* ppitemLast = &(m_pdoc->m_lpNextItem);
|
|
|
|
while (lpclient)
|
|
{
|
|
ChkC(lpclient);
|
|
if (lpclient==this)
|
|
{
|
|
// Remove from linked list
|
|
*ppitemLast = lpclient->m_lpNextItem;
|
|
break;
|
|
}
|
|
ppitemLast = &(lpclient->m_lpNextItem);
|
|
lpclient = lpclient->m_lpNextItem;
|
|
}
|
|
Revoke();
|
|
}
|
|
|
|
|
|
|
|
|
|
INTERNAL_(void) CDefClient::ReleaseAllItems ()
|
|
{
|
|
LPCLIENT lpclient;
|
|
|
|
Puts ("DefClient::ReleaseAllItems\r\n");
|
|
AssertIsDoc(this);
|
|
|
|
// leave the doc level object.
|
|
lpclient = m_lpNextItem;
|
|
|
|
while (lpclient)
|
|
{
|
|
ChkC(this);
|
|
LPCLIENT pitemNext = lpclient->m_lpNextItem;
|
|
lpclient->Revoke();
|
|
lpclient = pitemNext;
|
|
}
|
|
// After revoking all the items, we can't keep any refernces to them.
|
|
m_lpNextItem = NULL;
|
|
}
|
|
|
|
|
|
|
|
INTERNAL_(void) CDefClient::DeleteAllItems ()
|
|
{
|
|
LPCLIENT lpclient;
|
|
|
|
Puts ("DefClient::DeleteAllItems\r\n");
|
|
AssertIsDoc(this);
|
|
|
|
// leave the doc level object.
|
|
lpclient = m_lpNextItem;
|
|
|
|
while (lpclient)
|
|
{
|
|
ChkC(lpclient);
|
|
if (ISATOM(lpclient->m_aItem))
|
|
GlobalDeleteAtom (lpclient->m_aItem);
|
|
// Delete client advise info
|
|
lpclient->DeleteAdviseInfo ();
|
|
|
|
lpclient = lpclient->m_lpNextItem;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
// PokeData: Prepares and gives the data to the server app thru
|
|
// the SetData object method.
|
|
|
|
INTERNAL CDefClient::PokeData(HWND hwndClient,ATOM aItem,HANDLE hPoke)
|
|
{
|
|
HRESULT hresult = ReportResult(0, E_UNEXPECTED, 0, 0);
|
|
DDEPOKE FAR * lpPoke = NULL;
|
|
int format;
|
|
BOOL fRelease = FALSE;
|
|
LPPERSISTSTORAGE pPersistStg=NULL;
|
|
FORMATETC formatetc;
|
|
STGMEDIUM medium;
|
|
|
|
// Due to a C7 bug, do not use a structure initialization for STGMEDIUM
|
|
medium.tymed = TYMED_HGLOBAL;
|
|
medium.hGlobal = NULL; // invalid
|
|
medium.pUnkForRelease= NULL;
|
|
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%p CDefClient::PokeData(hwndClient=%x,aItem=%x,hPoke=%x)\n",
|
|
this,hwndClient,aItem,hPoke));
|
|
|
|
ChkC(this);
|
|
AssertIsDoc (this);
|
|
|
|
|
|
// Until now, m_aItem had been the client-generated (ugly) document name.
|
|
// Now it becomes the actual item name, which will almost always be NULL.
|
|
// Only in the TreatAs/ConvertTo case will it be non-NULL.
|
|
m_aItem = aItem;
|
|
|
|
formatetc.cfFormat = 0; /* invalid */
|
|
formatetc.ptd = m_ptd;
|
|
formatetc.lindex = DEF_LINDEX;
|
|
formatetc.dwAspect = DVASPECT_CONTENT;
|
|
formatetc.tymed = TYMED_HGLOBAL;
|
|
|
|
ErrZS (hPoke && (lpPoke = (DDEPOKE FAR *) GlobalLock (hPoke)),
|
|
E_OUTOFMEMORY);
|
|
|
|
format = formatetc.cfFormat = (UINT)(unsigned short)lpPoke->cfFormat;
|
|
Assert (format);
|
|
fRelease = lpPoke->fRelease;
|
|
|
|
// We found the item. Now prepare the data to be given to the object
|
|
// MakeItemData returns a newly allocated handle.
|
|
if (!(medium.hGlobal = MakeItemData (lpPoke, hPoke, format)))
|
|
goto errRtn;
|
|
|
|
// Change type acording to format (not that default has been set above)
|
|
if (format == CF_METAFILEPICT)
|
|
formatetc.tymed = medium.tymed = TYMED_MFPICT;
|
|
else
|
|
if (format == CF_BITMAP)
|
|
formatetc.tymed = medium.tymed = TYMED_GDI;
|
|
|
|
// Now send the data to the object
|
|
|
|
|
|
if (formatetc.cfFormat==g_cfNative)
|
|
{
|
|
m_fGotEditNoPokeNativeYet = FALSE;
|
|
|
|
// Cannot do SetData. Must do PersisStg::Load on an IStorage
|
|
// made from the native data, i.e., medium.hGlobal.
|
|
|
|
Assert (m_plkbytNative==NULL);
|
|
ErrRtnH (CreateILockBytesOnHGlobal (medium.hGlobal,
|
|
/*fDeleteOnRelease*/TRUE,
|
|
&m_plkbytNative));
|
|
|
|
Assert (m_pstgNative==NULL);
|
|
|
|
if (NOERROR==StgIsStorageILockBytes(m_plkbytNative))
|
|
{
|
|
// This is a flattened 2.0 storage
|
|
ErrRtnH (StgOpenStorageOnILockBytes (m_plkbytNative,
|
|
(LPSTORAGE)NULL,
|
|
STGM_READWRITE| STGM_SHARE_EXCLUSIVE| STGM_DIRECT,
|
|
(SNB)NULL,
|
|
0,
|
|
&m_pstgNative));
|
|
}
|
|
else
|
|
{
|
|
// It is a raw 1.0 Native handle.
|
|
// This is the TreatAs/ ConvertTo case.
|
|
LPLOCKBYTES plkbyt = NULL;
|
|
Assert (m_psrvrParent->m_aOriginalClass);
|
|
|
|
ErrRtnH (wCreateStgAroundNative (medium.hGlobal,
|
|
m_psrvrParent->m_aOriginalClass,
|
|
m_psrvrParent->m_aClass,
|
|
m_psrvrParent->m_cnvtyp,
|
|
m_aItem,
|
|
&m_pstgNative,
|
|
&plkbyt));
|
|
|
|
|
|
Assert (m_plkbytNative);
|
|
if (m_plkbytNative)
|
|
{
|
|
// This should free the original native hGlobal also.
|
|
m_plkbytNative->Release();
|
|
medium.hGlobal = NULL;
|
|
}
|
|
m_plkbytNative = plkbyt;
|
|
|
|
}
|
|
|
|
RetZ (m_pstgNative);
|
|
Assert (m_lpoleObj);
|
|
ErrRtnH (m_lpoleObj->QueryInterface (IID_IPersistStorage,
|
|
(LPLPVOID) &pPersistStg));
|
|
hresult = pPersistStg->Load (m_pstgNative);
|
|
pPersistStg->Release();
|
|
pPersistStg=NULL;
|
|
ErrRtnH (hresult);
|
|
|
|
// Now that we have initialized the object, we can call SetClientSite
|
|
ErrRtnH (SetClientSite() );
|
|
|
|
// This is for Packager, in particular. If client does not advise
|
|
// on any data, we still need to do an OLE advise so we can get
|
|
// OnClose notifications.
|
|
ErrRtnH (DoOle20Advise (OLE_CLOSED, (CLIPFORMAT)0));
|
|
}
|
|
else
|
|
{
|
|
if (m_fGotEditNoPokeNativeYet)
|
|
{
|
|
// We got StdEdit, but instead of getting Poke for native data,
|
|
// we got poke for someother format. So we want to generate
|
|
// InitNew() call for the object.
|
|
|
|
ErrRtnH (DoInitNew()); // the function clears the flag
|
|
}
|
|
|
|
// Not native format, do SetData
|
|
// Callee frees medium, i.e., the hglobal returned by MakeItemData
|
|
Assert (m_lpdataObj);
|
|
hresult = m_lpdataObj->SetData (&formatetc, &medium, TRUE);
|
|
#ifdef _DEBUG
|
|
if (hresult != NOERROR)
|
|
{
|
|
Puts ("****WARNING: SetData failed. cfFormat==");
|
|
WCHAR sz[100];
|
|
GetClipboardFormatName (formatetc.cfFormat, sz, 100);
|
|
Puts (sz);
|
|
Putn();
|
|
}
|
|
#endif
|
|
// We free the data if server deos not return NOERROR.
|
|
// Otherwise server must've deleted it.
|
|
if (hresult == NOERROR)
|
|
medium.hGlobal = NULL;
|
|
}
|
|
|
|
|
|
errRtn:
|
|
GlobalUnlock (hPoke);
|
|
|
|
if (fRelease && hPoke)
|
|
GlobalFree (hPoke);
|
|
|
|
// Do NOT free medium.hGlobal, because it becomes the hGlobal on which
|
|
// m_plkbytNative (and therefore m_pstgNative) is based.
|
|
// It will be freed when m_plkbytNative is Release().
|
|
// if (medium.hGlobal)
|
|
// ReleaseStgMedium(&medium);
|
|
|
|
if (pPersistStg)
|
|
pPersistStg->Release();
|
|
|
|
return hresult;
|
|
}
|
|
|
|
|
|
|
|
INTERNAL_(HRESULT) CDefClient::UnAdviseData
|
|
(HWND hwndClient,
|
|
ATOM aItem)
|
|
{
|
|
WCHAR buf[MAX_STR];
|
|
int options;
|
|
LPCLIENT lpclient;
|
|
HRESULT hresult = ReportResult(0, E_UNEXPECTED, 0, 0);
|
|
HANDLE hclinfo = NULL;
|
|
PCLINFO pclinfo = NULL;
|
|
|
|
Puts ("DefClient::UnadviseData\r\n");
|
|
ChkC(this);
|
|
|
|
if (aItem == NULL)
|
|
{
|
|
buf[0] = NULL;
|
|
}
|
|
else
|
|
{
|
|
GlobalGetAtomName (aItem, buf, MAX_STR);
|
|
}
|
|
|
|
// Scan for the advise options like "Close", "Save" etc
|
|
// at the end of the item.
|
|
|
|
ErrRtnH (ScanItemOptions (buf, (int far *)&options));
|
|
|
|
// Now get the corresponding object.
|
|
ErrRtnH (FindItem (buf, (LPCLIENT FAR *)&lpclient));
|
|
|
|
// Find the client structure to be attached to the object.
|
|
if ((hclinfo = FindClient (lpclient->m_hcliInfo, hwndClient, FALSE)) == NULL ||
|
|
(pclinfo = (PCLINFO) LocalLock (hclinfo)) == NULL )
|
|
{
|
|
hresult = ReportResult(0, E_OUTOFMEMORY, 0, 0);
|
|
goto errRtn;
|
|
}
|
|
|
|
pclinfo->options &= (~(0x0001 << options));
|
|
|
|
errRtn:
|
|
if (pclinfo)
|
|
LocalUnlock (hclinfo);
|
|
return hresult;
|
|
|
|
}
|
|
|
|
|
|
|
|
// AdviseStdItems: This routine takes care of the DDEADVISE for a
|
|
//particular object in given document. Creates a client strutcure
|
|
//and attaches to the property list of the object window.
|
|
|
|
INTERNAL_(HRESULT) CDefClient::AdviseStdItems
|
|
(
|
|
|
|
HWND hwndClient,
|
|
ATOM aItem,
|
|
HANDLE hopt,
|
|
BOOL FAR * lpfack
|
|
)
|
|
{
|
|
|
|
DDEADVISE FAR *lpopt;
|
|
HRESULT hresult = ReportResult(0, E_UNEXPECTED, 0, 0);
|
|
|
|
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%x _IN CDefClient::AdviseStdItems(hwndClient=%x,aItem=%x(%ws),hopt=%x)\n",
|
|
this,
|
|
hwndClient,
|
|
aItem,
|
|
wAtomName(aItem),
|
|
hopt));
|
|
|
|
ChkC(this);
|
|
ErrZS (lpopt = (DDEADVISE FAR *) GlobalLock (hopt), E_OUTOFMEMORY);
|
|
|
|
AssertSz (aItem == aStdDocName, "AdviseStdItem is not Documentname");
|
|
|
|
*lpfack = lpopt->fAckReq;
|
|
hresult = (HRESULT)SetStdInfo (hwndClient, OLESTR("StdDocumentName"), NULL);
|
|
|
|
|
|
if (lpopt)
|
|
GlobalUnlock (hopt);
|
|
|
|
errRtn:
|
|
|
|
if (hresult == NOERROR)
|
|
{
|
|
// Rules say to free handle if ACK will be positive
|
|
GlobalFree (hopt);
|
|
}
|
|
Assert (hresult==NOERROR);
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%x _OUT CDefClient::AdviseStdItems hresult=%x\n",
|
|
this,
|
|
hresult));
|
|
|
|
return hresult;
|
|
}
|
|
|
|
|
|
|
|
//AdviseData: This routine takes care of the DDE_ADVISE for a
|
|
//particular object in given document. Creates a client strutcure
|
|
//and attaches to the property list of the object window.
|
|
|
|
INTERNAL CDefClient::AdviseData
|
|
(
|
|
HWND hwndClient,
|
|
ATOM aItem,
|
|
HANDLE hopt,
|
|
BOOL FAR * lpfack
|
|
)
|
|
{
|
|
DDEADVISE FAR *lpopt = NULL;
|
|
int format = NULL;
|
|
WCHAR buf[MAX_STR];
|
|
OLE_NOTIFICATION options;
|
|
LPCLIENT lpclient;
|
|
HRESULT hresult = ReportResult(0, E_UNEXPECTED, 0, 0);
|
|
HANDLE hclinfo = NULL;
|
|
PCLINFO pclinfo = NULL;
|
|
BOOL fAllocatedClInfo = FALSE;
|
|
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%x _IN CDefClient::AdviseData(hwndClient=%x,aItem=%x(%ws),hopt=%x)\n",
|
|
this,
|
|
hwndClient,
|
|
aItem,
|
|
wAtomName(aItem),
|
|
hopt));
|
|
ChkC(this);
|
|
if (m_fGotEditNoPokeNativeYet) {
|
|
// We got StdEdit, but instead of getting Poke for native data,
|
|
// we got advise. So we want to generate InitNew() call for
|
|
// the object.
|
|
|
|
DoInitNew(); // the function clears the flag
|
|
}
|
|
|
|
m_fGotDdeAdvise = TRUE;
|
|
|
|
ErrZS (lpopt = (DDEADVISE FAR *) GlobalLock (hopt), E_OUTOFMEMORY);
|
|
|
|
if (!aItem)
|
|
buf[0] = NULL;
|
|
else
|
|
GlobalGetAtomName (aItem, buf, MAX_STR);
|
|
|
|
// Scan for the advise options like "Close", "Save" etc
|
|
// at the end of the item.
|
|
|
|
// ack flag should be set before the error return. Otherwise the
|
|
// the atom is getting deleted.
|
|
|
|
*lpfack = lpopt->fAckReq;
|
|
ErrRtnH (ScanItemOptions (buf, (int far *)&options));
|
|
|
|
// Now get the corresponding item.
|
|
ErrRtnH (FindItem (buf, (LPCLIENT FAR *)&lpclient));
|
|
|
|
if (!IsFormatAvailable ((CLIPFORMAT)(unsigned short)lpopt->cfFormat)){
|
|
hresult = ReportResult(0, DV_E_CLIPFORMAT, 0, 0); // this format is not supported;
|
|
goto errRtn;
|
|
}
|
|
|
|
lpclient->DoOle20Advise (options, (CLIPFORMAT)(unsigned short)lpopt->cfFormat);
|
|
|
|
|
|
// Create the client structure to be attcahed to the object.
|
|
if (!(hclinfo = FindClient (lpclient->m_hcliInfo, hwndClient, FALSE)))
|
|
{
|
|
hclinfo = LocalAlloc (LMEM_MOVEABLE | LMEM_ZEROINIT, sizeof (CLINFO));
|
|
fAllocatedClInfo = TRUE;
|
|
}
|
|
|
|
|
|
if (hclinfo == NULL || (pclinfo = (PCLINFO) LocalLock (hclinfo)) == NULL){
|
|
hresult = ReportResult(0, E_OUTOFMEMORY, 0, 0);
|
|
goto errRtn;
|
|
}
|
|
|
|
// Remember the client window (Needed for sending DATA later on
|
|
// when the data change message comes from the server)
|
|
|
|
pclinfo->hwnd = hwndClient;
|
|
if ((CLIPFORMAT)(unsigned short)lpopt->cfFormat == g_cfNative)
|
|
pclinfo->bnative = TRUE;
|
|
else
|
|
pclinfo->format = (CLIPFORMAT)(unsigned short)lpopt->cfFormat;
|
|
|
|
// Remeber the data transfer options
|
|
pclinfo->options |= (1 << options) ;
|
|
|
|
pclinfo->bdata = !lpopt->fDeferUpd;
|
|
LocalUnlock (hclinfo);
|
|
pclinfo = NULL;
|
|
|
|
// if the entry exists already, delete it.
|
|
FindClient (lpclient->m_hcliInfo, hwndClient, /*fDelete*/TRUE);
|
|
|
|
// Now add this client to item client list
|
|
// !!! This error recovery is not correct.
|
|
if(!AddClient ((LPHANDLE)&lpclient->m_hcliInfo, hwndClient, hclinfo))
|
|
goto errRtn;
|
|
|
|
|
|
errRtn:
|
|
if (lpopt)
|
|
GlobalUnlock (hopt);
|
|
|
|
if (pclinfo)
|
|
LocalUnlock (hclinfo);
|
|
|
|
if (hresult==NOERROR)
|
|
{
|
|
// hresult==NOERROR iff we will send a postive ACK, so we must
|
|
// free the hOptions handle.
|
|
GlobalFree (hopt);
|
|
}
|
|
else
|
|
{
|
|
intrDebugOut((DEB_IERROR,
|
|
"%x ::AdviseData() failing.\n",this));
|
|
// We free hclinfo because it was not stored in the item's
|
|
// client list via the AddClient just before the errRtn label.
|
|
if (hclinfo && fAllocatedClInfo)
|
|
LocalFree (hclinfo);
|
|
|
|
}
|
|
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%x _OUT CDefClient::AdviseData() returns hresult = %x\n",
|
|
this, hresult));
|
|
|
|
return hresult;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
INTERNAL_(BOOL) CDefClient::IsFormatAvailable
|
|
(CLIPFORMAT cfFormat)
|
|
{
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%x _IN CDefClient::IsFormatAvailable(cfFormat=%x)\n",
|
|
this, cfFormat));
|
|
|
|
ChkC(this);
|
|
|
|
BOOL f = ((cfFormat==g_cfNative) || UtIsFormatSupported (m_lpdataObj, DATADIR_GET, cfFormat));
|
|
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%x _OUT CDefClient::IsFormatAvailable(cfFormat=%x) returning %x\n",
|
|
this, cfFormat,f));
|
|
|
|
return f;
|
|
}
|
|
|
|
|
|
//RequestData: Sends data in response to a DDE Request message.
|
|
// for agiven doc and an object.
|
|
|
|
INTERNAL_(HRESULT) CDefClient::RequestData
|
|
(
|
|
HWND hwndClient,
|
|
ATOM aItem,
|
|
USHORT cfFormat,
|
|
LPHANDLE lphdde
|
|
)
|
|
{
|
|
|
|
HRESULT hresult = NOERROR;
|
|
LPCLIENT lpclient;
|
|
FORMATETC formatetc;
|
|
STGMEDIUM medium;
|
|
// Due to a C7 bug, do not use a structure initialization for STGMEDIUM
|
|
medium.tymed = TYMED_NULL;
|
|
medium.hGlobal = 0;
|
|
medium.pUnkForRelease= NULL;
|
|
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%x _IN CDefClient::RequestData(hwndClient=%x,aItem=%x(%ws),cfFormat=%x,lphdde=%x)\n",
|
|
this,
|
|
hwndClient,
|
|
aItem,
|
|
wAtomName(aItem),
|
|
cfFormat,
|
|
lphdde));
|
|
ChkC(this);
|
|
|
|
// If edit environment Send data if we can
|
|
if (aItem == aEditItems)
|
|
{
|
|
hresult = RequestDataStd (aItem, lphdde);
|
|
goto exitRtn;
|
|
}
|
|
|
|
hresult = FindItem ((LPOLESTR) MAKEINTATOM(aItem),(LPCLIENT FAR *)&lpclient);
|
|
if (hresult != NOERROR)
|
|
{
|
|
goto errRtn;
|
|
}
|
|
|
|
ChkC (lpclient);
|
|
|
|
formatetc.cfFormat = cfFormat;
|
|
formatetc.ptd = lpclient->m_ptd;
|
|
formatetc.lindex = DEF_LINDEX;
|
|
formatetc.dwAspect = DVASPECT_CONTENT;
|
|
formatetc.tymed = TYMED_HGLOBAL;
|
|
|
|
|
|
hresult = ReportResult(0, DV_E_FORMATETC, 0, 0);
|
|
if (!lpclient->IsFormatAvailable (formatetc.cfFormat))
|
|
{
|
|
goto errRtn;
|
|
}
|
|
|
|
|
|
// Now ask the item for the given format data
|
|
|
|
SendDevInfo (hwndClient);
|
|
|
|
wSetTymed (&formatetc);
|
|
hresult = lpclient->GetData (&formatetc, &medium);
|
|
if (hresult != NOERROR)
|
|
{
|
|
intrDebugOut((DEB_IERROR,
|
|
"GetData returns hresult=%x\n",
|
|
hresult));
|
|
goto errRtn;
|
|
}
|
|
if (medium.tymed & ~(TYMED_HGLOBAL | TYMED_MFPICT | TYMED_GDI))
|
|
{
|
|
AssertSz (0, "Got a storage medium of type other than hGlobal");
|
|
goto errRtn;
|
|
}
|
|
if (cfFormat == CF_METAFILEPICT)
|
|
{
|
|
ChangeOwner (medium.hGlobal);
|
|
}
|
|
|
|
|
|
// Duplicate the DDE data
|
|
// medium.hGlobal is freed by MakeDdeData or by the client once the
|
|
// DDE_DATA is posted with *lphdde.
|
|
if (MakeDDEData (medium.hGlobal, cfFormat, lphdde, TRUE)){
|
|
// !!! Why do we have to duplicate the atom
|
|
DuplicateAtom (aItem);
|
|
hresult = NOERROR;
|
|
}
|
|
else
|
|
hresult = E_OUTOFMEMORY;
|
|
|
|
errRtn:
|
|
exitRtn:
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%x _OUT CDefClient::RequestData() returning %x\n",
|
|
this, hresult));
|
|
|
|
return hresult;
|
|
}
|
|
|
|
|
|
|
|
// REVIEW: needs review. Item callvback has to be split
|
|
|
|
// ItemCallback: Calback routine for the server to inform the
|
|
// data changes. When the change message is received, DDE data
|
|
// message is sent to each of the clients depending on the
|
|
// options.
|
|
|
|
INTERNAL_(HRESULT) CDefClient::ItemCallBack
|
|
(
|
|
int msg, // notification message
|
|
LPOLESTR szNewName // for OLE_RENAMED notification
|
|
)
|
|
{
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%x _IN CDefClient::ItemCallBack(msg=%x,szNewName=%x)\n",
|
|
this,
|
|
szNewName));
|
|
|
|
HRESULT hresult = NOERROR;
|
|
BOOL bSaved;
|
|
LPCLIENT lpclientRename;
|
|
LPCLIENT lpclient;
|
|
|
|
ChkC(this);
|
|
|
|
if (msg == OLE_RENAMED) {
|
|
|
|
Assert (szNewName);
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%x ::ItemCallBack(szNewName=(%ws))\n",
|
|
WIDECHECK(szNewName)));
|
|
|
|
|
|
if (!m_bContainer)
|
|
{
|
|
lpclient = (LPCLIENT)GetWindowLong (m_hwnd, 0);
|
|
Assert (lpclient==m_pdoc);
|
|
}
|
|
else
|
|
lpclient = this;
|
|
|
|
Assert (lpclient->m_chk==chkDefClient);
|
|
|
|
// Replace the internally-stored name
|
|
if (lpclient->m_aItem)
|
|
{
|
|
GlobalDeleteAtom (lpclient->m_aItem);
|
|
lpclient->m_aItem = wGlobalAddAtom (szNewName);
|
|
}
|
|
|
|
|
|
// find if any StdDocName item is present at all
|
|
if (lpclientRename =
|
|
lpclient->SearchItem ((LPOLESTR) MAKEINTATOM(aStdDocName)))
|
|
{
|
|
HANDLE hDdeData=NULL;
|
|
|
|
//
|
|
// We have a new name in UNICODE. Need to create a new
|
|
// name in ANSI.
|
|
//
|
|
LPSTR lpName = CreateAnsiFromUnicode(szNewName);
|
|
|
|
HANDLE hNewName = wNewHandle (lpName, strlen(lpName) + 1);
|
|
|
|
PrivMemFree(lpName);
|
|
|
|
// hNewName is freed by MakeDDEData
|
|
|
|
if (!MakeDDEData (hNewName, (int)g_cfBinary, &hDdeData, FALSE))
|
|
{
|
|
hresult = ReportResult(0, E_OUTOFMEMORY, 0, 0);
|
|
goto errrtn;
|
|
}
|
|
|
|
Assert (hDdeData);
|
|
lpclientRename->SendRenameMsgs (hDdeData);
|
|
GlobalFree (hDdeData);
|
|
|
|
// Post termination for each of the doc clients that did not
|
|
// advise on rename
|
|
lpclient->TerminateNonRenameClients (lpclientRename);
|
|
}
|
|
|
|
|
|
Assert (FALSE == lpclient->m_fEmbed);
|
|
|
|
// REVIEW: what is this?
|
|
//lpclient->m_fEmbed = FALSE;
|
|
|
|
hresult = NOERROR;
|
|
|
|
errrtn:
|
|
Assert (hresult == NOERROR);
|
|
goto exitRtn;
|
|
|
|
} else {
|
|
|
|
// Enumerate all the clients and send DDE_DATA if necessary.
|
|
bSaved = SendDataMsg (msg);
|
|
|
|
// REVIEW: Hack from 1.0 for old pre-OLE-library apps
|
|
if ((msg == OLE_SAVED) && m_fEmbed && !bSaved)
|
|
return ReportResult(0, RPC_E_DDE_CANT_UPDATE, 0, 0);
|
|
|
|
hresult = NOERROR;
|
|
}
|
|
|
|
exitRtn:
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%x _OUT CDefClient::ItemCallBack() returning hresult=%x\n",
|
|
this,hresult));
|
|
|
|
return(hresult);
|
|
}
|
|
|
|
|
|
// This func should definitely be replaced by use of MFC map. (IsEmpty)
|
|
INTERNAL_(BOOL) AreNoClients (HANDLE hcli)
|
|
{
|
|
HANDLE hcliPrev = NULL;
|
|
PCLILIST pcli;
|
|
HANDLE *phandle;
|
|
|
|
while (hcli) {
|
|
if ((pcli = (PCLILIST) LocalLock (hcli)) == NULL)
|
|
{
|
|
Puth (hcli);
|
|
Putn();
|
|
Assert(0);
|
|
return TRUE;
|
|
}
|
|
|
|
phandle = (HANDLE *) pcli->info;
|
|
while (phandle < (HANDLE *)(pcli + 1))
|
|
{
|
|
if (*phandle)
|
|
{
|
|
LocalUnlock (hcli);
|
|
return FALSE;
|
|
}
|
|
phandle++;
|
|
phandle++;
|
|
}
|
|
hcliPrev = hcli;
|
|
hcli = pcli->hcliNext;
|
|
LocalUnlock (hcliPrev);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#ifdef _DEBUG
|
|
// For use in CodeView
|
|
// NOTE: Returns a static string
|
|
INTERNAL_(LPOLESTR) a2s (ATOM a)
|
|
{
|
|
static WCHAR sz[256];
|
|
GlobalGetAtomName (a, sz, 256);
|
|
return sz;
|
|
}
|
|
#endif
|