NT4/private/ole32/com/remote/dde/client/ddeproxy.cxx
2020-09-30 17:12:29 +02:00

2900 lines
76 KiB
C++

/*
copyright (c) 1992 Microsoft Corporation
Module Name:
ddeproxy.cpp
Abstract:
This module contains the code for the dde proxy (wrapper)
Author:
Srini Koppolu (srinik) 22-June-1992
Jason Fuller (jasonful) 24-July-1992
*/
#include "ddeproxy.h"
#include <tls.h>
DebugOnly (static UINT v_cDdeObjects=0;)
/*
* IMPLEMENTATION of CDdeObject
*
*/
#ifdef OLD
#define UpdateExtent(old,new) do { if ((long)new!=old) {old=(long)new; } } while (0)
#endif
//+---------------------------------------------------------------------------
//
// Function: CreateDdeClientHwnd
//
// Synopsis: Creates a per thread ClientDde window.
//
// Effects: This window is created so we can keep a list of windows that
// need to be cleaned up in the event the thread dies or OLE32 is
// unloaded. In the case of DLL unload, we will fault if we don't
// cleanup this window, since user will dispatch messages to
// non-existant code. The easy way to track these windows is to
// make them children of a common per thread window.
//
// This routine is called by the TLSGetDdeClient() routine to
// create a window per thread. This window doesn't need to respond
// to DDE Initiates.
//
// Arguments: [void] --
//
// Returns: HWND to DdeClientWindow.
//
// History: 12-10-94 kevinro Created
//
//----------------------------------------------------------------------------
HWND CreateDdeClientHwnd(void)
{
return SSCreateWindowExA(0,"STATIC","DdeClientHwnd",WS_DISABLED,
0,0,0,0,NULL,NULL,hinstSO,NULL);
}
//+---------------------------------------------------------------------------
//
// Function: GetDdeCallControlInterface
//
// Synopsis: Gets the thread specific instance of the DDE CallControl
//
// Effects: If there is already a per thread CallControl interface,
// AddRef() it and return the value. Otherwise, get one and
// set it in the TLS data structure. The TLS structure is
// a global per thread structure that contains various values.
//
// Each threads per instance version of the CallControl will
// be stored in a global place. ReleaseDdeCallControlInterface
// is used to free up instances of the interface.
//
// Arguments: (none)
//
// Requires:
//
// Returns: Per thread instance of ICallControl. NULL if error
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 5-13-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
PCALLCONTROL GetDdeCallControlInterface()
{
PCALLCONTROL pDdeControl = (PCALLCONTROL)TLSGetDdeCallControl();
if (pDdeControl != NULL)
{
pDdeControl->AddRef();
return(pDdeControl);
}
//
// There wasn't one in the TLS data. Create one, and return it.
//
ORIGINDATA origindata;
//
// There is a single channel control (static implementation)
// for all DDE windows in the process. The hwndCli is set
// to NULL, since DDE messages are handled specially by
// the MessageFilter code.
//
origindata.pChCont = (PCHANNELCONTROL)&g_CDdeChannelControl;
origindata.CallOrigin = CALLORIGIN_DDE;
origindata.hwnd = (HWND)0;
origindata.wFirstMsg = WM_DDE_FIRST;
origindata.wLastMsg = WM_DDE_LAST;
if(CoGetCallControl(&origindata, &pDdeControl) == NOERROR)
{
TLSSetDdeCallControl(pDdeControl);
}
else
{
intrAssert(!"Could not CoGetCallControl");
pDdeControl = NULL;
}
return(pDdeControl);
}
//+---------------------------------------------------------------------------
//
// Function: ReleaseDdeCallControlInterface
//
// Synopsis: This function will release the per thread CallControl
// interface. If the Release() returns zero, then the
// function will set the per thread instance to NULL.
//
// Effects:
//
// Arguments: (none)
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 5-13-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
void ReleaseDdeCallControlInterface()
{
PCALLCONTROL pDdeControl = (PCALLCONTROL)TLSGetDdeCallControl();
intrAssert(pDdeControl != NULL);
#if DBG == 1
if (pDdeControl == NULL)
{
DebugBreak();
}
#endif
if ((pDdeControl != NULL) && (pDdeControl->Release() == 0))
{
TLSSetDdeCallControl(NULL);
}
}
// CreateDdeProxy
//
// This corresponds to ProxyManager::Create in 2.0
//
INTERNAL_ (LPUNKNOWN) CreateDdeProxy
(IUnknown * pUnkOuter,
REFCLSID clsid)
{
LPUNKNOWN punk;
intrDebugOut((DEB_ITRACE,"CreateDdeProxy(pUnkOuter=%x)\n",pUnkOuter));
punk = CDdeObject::Create (pUnkOuter, clsid);
intrDebugOut((DEB_ITRACE,
"CreateDdeProxy(pUnkOuter=%x) returns %x\n",
pUnkOuter,
punk));
return punk;
}
//+---------------------------------------------------------------------------
//
// Method: CDdeObject::Create
//
// Synopsis: Creates a CDdeObject
//
// Effects:
//
// Arguments: [pUnkOuter] -- Controlling IUnknown
// [clsid] -- OLE1 ClassID
// [ulObjType] -- Object type. Optional: def to OT_EMBEDDED
// [aTopic] -- Atom of link. Optional: def to NULL
// [szItem] -- String for link object (def to NULL)
// [ppdde] -- Output pointer to CDdeObject (def to NULL)
// [fAllowNullClsid] -- Is NULL clsid OK? Default: false
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History:
//
// Notes:
//
//----------------------------------------------------------------------------
INTERNAL_(LPUNKNOWN) CDdeObject::Create
(IUnknown * pUnkOuter,
REFCLSID clsid,
ULONG ulObjType,// optional, default OT_EMBEDDED
ATOM aTopic, // optional, only relevant if ulObjType==OT_LINK
LPOLESTR szItem, // optional, only relevant if ulObjType==OT_LINK
CDdeObject * * ppdde, // optional, thing created
BOOL fAllowNullClsid) // default FALSE
{
intrDebugOut((DEB_ITRACE,"CDdeObject::Create(%x,ulObjType=%x)\n",
pUnkOuter,
ulObjType));
CDdeObject * pDdeObject;
static int iTopic=1; // used to make topic names unique
WCHAR szTopic[30];
Assert (ulObjType==OT_LINK || ulObjType==OT_EMBEDDED);
Assert (ulObjType != OT_LINK || wIsValidAtom(aTopic));
if (ppdde)
*ppdde = NULL;
if (NULL==(pDdeObject = new CDdeObject (pUnkOuter))
|| NULL == pDdeObject->m_pDataAdvHolder
|| NULL == pDdeObject->m_pOleAdvHolder)
{
Assert (!"new CDdeObject failed");
return NULL;
}
pDdeObject->m_refs = 1;
pDdeObject->m_clsid = clsid;
pDdeObject->m_aClass = wAtomFromCLSID(clsid);
#ifdef OLE1INTEROP
pDdeObject->m_fOle1interop = TRUE;
#endif
if (ulObjType==OT_LINK)
{
pDdeObject->m_aTopic = wDupAtom (aTopic);
pDdeObject->m_aItem = wGlobalAddAtom (szItem);
// Never close a linked document
pDdeObject->m_fNoStdCloseDoc = TRUE;
}
else
{
// This string may actually be visible in the Window Title Bar for a sec.
InterlockedIncrement((long *)&iTopic);
wsprintf (szTopic,OLESTR("Embedded Object #%u"), iTopic);
Assert (lstrlenW(szTopic) < 30);
pDdeObject->m_aItem = NULL;
pDdeObject->m_aTopic = wGlobalAddAtom (szTopic);
}
pDdeObject->m_bOldSvr = wIsOldServer (pDdeObject->m_aClass);
pDdeObject->m_ulObjType = ulObjType;
// we can only run if we have a MFI
pDdeObject->m_aExeName = wGetExeNameAtom(clsid);
intrDebugOut((DEB_ITRACE,
"::Create(%x,aTopic=%x,aItem=%x,szItem=%ws,aExeName=%x)\n",
pDdeObject,
pDdeObject->m_aTopic,
pDdeObject->m_aItem,
szItem?szItem:L"<NULL>",
pDdeObject->m_aExeName));
if (ppdde)
*ppdde = pDdeObject;
return &pDdeObject->m_Unknown;
}
// Constructor
CDdeObject::CDdeObject (IUnknown * pUnkOuter) :
m_Unknown(this),
CONSTRUCT_DEBUG
m_Data(this),
m_Ole(this),
m_PersistStg(this),
m_ProxyMgr(this),
m_OleItemContainer(this)
{
intrDebugOut((DEB_ITRACE,"CDdeObject::CDdeObject(%x)\n",this));
if (!pUnkOuter)
pUnkOuter = &m_Unknown;
m_pUnkOuter = pUnkOuter;
m_bRunning = FALSE;
m_pOleClientSite = NULL;
m_pstg = NULL;
m_pSysChannel = NULL;
m_pDocChannel = NULL;
m_bInitNew = NULL;
m_hNative = NULL;
m_hPict = NULL;
m_hExtra = NULL;
m_cfExtra = NULL;
m_cfPict = 0;
m_aItem = NULL;
m_iAdvSave = 0;
m_iAdvClose = 0;
m_iAdvChange = 0;
m_fDidAdvNative = FALSE;
m_pOleAdvHolder = NULL;
m_pDataAdvHolder = NULL;
m_fDidSendOnClose = FALSE;
m_fNoStdCloseDoc = FALSE;
m_fDidStdCloseDoc = FALSE;
m_fDidStdOpenDoc = FALSE;
m_fDidGetObject = FALSE;
m_fDidLaunchApp = FALSE;
m_fUpdateOnSave = TRUE;
m_fVisible = FALSE;
m_fWasEverVisible = FALSE;
m_fCalledOnShow = FALSE;
m_fGotCloseData = FALSE;
m_cLocks = 1; // connections are initially locked
m_chk = chkDdeObj;
m_ptd = NULL;
m_fDoingSendOnDataChange = FALSE;
#ifdef _CHICAGO_
//Note:POSTPPC
_DelayDelete = NoDelay;
#endif // _CHICAGO_
CreateOleAdviseHolder (&m_pOleAdvHolder);
Assert (m_pOleAdvHolder);
CreateDataAdviseHolder (&m_pDataAdvHolder);
Assert (m_pDataAdvHolder);
#ifdef OLD
m_cxContentExtent = 2000; // 2 centimeters , totally random default
m_cyContentExtent = 2000;
#endif
m_wTerminate = Terminate_None;
DebugOnly (v_cDdeObjects++;)
Putsi (v_cDdeObjects);
}
CDdeObject::~CDdeObject
(void)
{
intrDebugOut((DEB_ITRACE,"CDdeObject::~CDdeObject(%x)\n",this));
if (m_pDocChannel)
{
intrDebugOut((DEB_IWARN , "Abnormal situation: Doc Channel not deleted. Server died?"));
delete m_pDocChannel;
}
if (m_pSysChannel)
{
Warn ("Abnormal situation: Sys Channel not deleted. Server died?");
delete m_pSysChannel;
}
if (m_hNative)
{
GlobalFree(m_hNative);
}
if (m_hPict)
{
wFreeData (m_hPict, m_cfPict, TRUE);
}
if (m_hExtra)
{
wFreeData (m_hExtra, m_cfExtra, TRUE);
}
// release all the pointers that we remember
if (m_pOleClientSite)
{
DeclareVisibility (FALSE);
m_pOleClientSite->Release();
}
if (m_pDataAdvHolder)
m_pDataAdvHolder->Release();
if (m_pOleAdvHolder)
m_pOleAdvHolder->Release();
if (m_pstg)
m_pstg->Release();
if (m_aExeName)
GlobalDeleteAtom (m_aExeName);
if (m_aClass)
GlobalDeleteAtom (m_aClass);
if (m_aTopic)
GlobalDeleteAtom (m_aTopic);
if (m_aItem)
GlobalDeleteAtom (m_aItem);
if (m_ptd)
delete m_ptd;
m_chk = 0;
DebugOnly (v_cDdeObjects--;)
Putsi (v_cDdeObjects);
}
// Handles WM_DDE_ACKs received while in initiate state. If this is the first
// reply, save its window handle. If multiple replies are received, take the
// one with the prefered instance, if there is one. Keep a count of
// WM_DDE_TERMINATEs we send so that we don't shut the window until we get
// all of the responses for WM_DDE_TERMINATEs.
INTERNAL_(void) CDdeObject::OnInitAck (LPDDE_CHANNEL pChannel, HWND hwndSvr)
{
intrDebugOut((DEB_ITRACE,"CDdeObject::OnInitAck(%x,hwndSvr=%x)\n",this,hwndSvr));
#ifdef _MAC
#else
if (!IsWindow (hwndSvr))
{
Assert (0);
return;
}
if (pChannel->hwndSvr) { // if we already have a handle
intrDebugOut((DEB_ITRACE,
"CDdeObject::OnInitAck(%x,hwndSvr=%x) Already have hwndSvr=%x\n",
this,
hwndSvr,
pChannel->hwndSvr));
// just take the very first one. Direct post is OK
MPostWM_DDE_TERMINATE(hwndSvr,pChannel->hwndCli);
// Expect an extra WM_DDE_TERMINATE
++pChannel->iExtraTerms;
} else {
// this is the server we want
pChannel->hwndSvr = hwndSvr;
pChannel->iExtraTerms = NULL;
intrDebugOut((DEB_ITRACE,
"CDdeObject::OnInitAck(%x,hwndSvr=%x) Established Connection\n",
this,
hwndSvr,
pChannel->hwndSvr));
}
#endif _MAC
}
INTERNAL_(BOOL) CDdeObject::OnAck (LPDDE_CHANNEL pChannel, LONG lParam)
{
intrDebugOut((DEB_ITRACE,"CDdeObject::OnAck(%x,lParam=%x)\n",this,lParam));
BOOL retval = TRUE;
ATOM aItem;
WORD wStatus;
HANDLE hData;
if( pChannel->iAwaitAck == AA_EXECUTE)
{
wStatus = GET_WM_DDE_EXECACK_STATUS( NULL, lParam );
hData = GET_WM_DDE_EXECACK_HDATA( NULL, lParam );
}
else
{
wStatus = GET_WM_DDE_ACK_STATUS( NULL, lParam );
aItem = GET_WM_DDE_ACK_ITEM( NULL, lParam );
}
// check for busy bit
if (wStatus & 0x4000)
{
// we got busy from the server.
pChannel->fRejected = TRUE;
// tell the wait loop that we got a busy ack
//CoSetAckState(pChannel->pCI , FALSE,TRUE, SERVERCALLEX_RETRYLATER);
intrDebugOut((DEB_ITRACE,"::OnAck(%x) Busy SetCallState(SERVERCALLEX_RETRYLATER)\n",this));
pChannel->pCallCont->SetCallState(pChannel->pCD, SERVERCALLEX_RETRYLATER, NOERROR);
return TRUE;
}
// just reset the flag always
m_wTerminate = Terminate_None;
intrDebugOut((DEB_ITRACE,
"::OnAck(%x)aItem=%x(%ws) wStatus=\n",
this,
aItem,
wAtomName(aItem),
wStatus));
if (pChannel->iAwaitAck == AA_EXECUTE)
{
GlobalFree (hData);
pChannel->hCommands = NULL;
}
else
{
if (hData)
GlobalDeleteAtom ((ATOM)hData);
}
// even if the client got terminate we have to go thru this path.
if (pChannel->wTimer) {
KillTimer (pChannel->hwndCli, 1);
pChannel->wTimer = 0;
}
if (pChannel->iAwaitAck == AA_POKE)
// We have to free the data first. OnAck can trigger
// another Poke (like pokehostnames)
wFreePokeData (pChannel, (m_bOldSvr && m_aClass==aMSDraw));
if (!(wStatus & POSITIVE_ACK))
{
intrDebugOut((DEB_ITRACE,"::OnAck(%x) OnAck got an ack with fAck==FALSE.\n",this));
// A negative ack is OK when doing a temporary advise from
// IsFormatAvailable(). Also, apps don't seem to positively
// ack all unadvises.
// review: johannp : this is the case were have to inform the reply rejected call
retval = FALSE;
// we got the ack and can leave the wait loop
//CoSetAckState(pChannel->pCI, FALSE);
intrDebugOut((DEB_ITRACE,
"::OnAck(%x) ***_ NACK _*** SetCallState(ISHANDLED,RPC_E_DDE_NACK)\n",
this));
pChannel->pCallCont->SetCallState(pChannel->pCD, SERVERCALLEX_ISHANDLED, RPC_E_DDE_NACK);
// MSDraw frees hOptions even on a NACK, despite official DDE rules.
if (pChannel->iAwaitAck == AA_ADVISE && m_clsid != CLSID_MSDraw)
GlobalFree (pChannel->hopt);
}
else
{
// we got the ack and can leave the wait loop
// CoSetAckState(pChannel->pCI, FALSE);
intrDebugOut((DEB_ITRACE,
"::OnAck(%x) POSITIVE_ACK SetCallState(SERVERCALLEX_ISHANDLED)\n",
this));
pChannel->pCallCont->SetCallState(pChannel->pCD, SERVERCALLEX_ISHANDLED, NOERROR);
}
pChannel->hopt = NULL;
pChannel->iAwaitAck = NULL;
return retval;
}
INTERNAL_(void) CDdeObject::OnTimer (LPDDE_CHANNEL pChannel)
{
intrDebugOut((DEB_ITRACE,"CDdeObject::OnTimer(%x)\n",this));
// Since there is only one timer for each client, just
// repost the message and delete the timer.
#ifdef _MAC
#else
KillTimer (pChannel->hwndCli, 1);
pChannel->wTimer = 0;
if (wPostMessageToServer(pChannel, pChannel->wMsg, pChannel->lParam,FALSE))
return ;
// Postmessage failed. We need to getback to the main stream of
// commands for the object.
OnAck (pChannel, pChannel->lParam);
#endif _MAC
}
// Called when we get a WM_DDE_DATA message in reponse to
// a DDE_REQUEST we sent to check if a format is available.
//
INTERNAL CDdeObject::OnDataAvailable
(LPDDE_CHANNEL pChannel,
HANDLE hDdeData,
ATOM aItem)
{
intrDebugOut((DEB_ITRACE,"CDdeObject::OnDataAvailable(%x)\n",this));
CLIPFORMAT cf;
Assert (AA_REQUESTAVAILABLE == pChannel->iAwaitAck);
intrAssert( wIsValidAtom(aItem));
DDEDATA * pDdeData = (DDEDATA *) GlobalLock (hDdeData);
RetZS (pDdeData, E_OUTOFMEMORY);
if (!pDdeData->fAckReq && aItem)
{
GlobalDeleteAtom (aItem);
}
cf = pDdeData->cfFormat;
GlobalUnlock (hDdeData);
wFreeData (wHandleFromDdeData (hDdeData), cf);
return NOERROR;
}
// Called for WM_DDE_DATA message. If data is from an ADVISE-ON-CLOSE and this
// is there are no more outstanding ADVISE-ON-CLOSE requests, close the
// document and end the conversation.
//+---------------------------------------------------------------------------
//
// Method: CDdeObject::OnData
//
// Synopsis: Called when a WM_DDE_DATA message is recieved. If the data
// is from an ADVISE_ON_CLOSE, and there are no more
// outstanding ADVISE_ON_CLOSE request, close the document
// and end the conversation.
//
// Effects: The effects of this routine are complex.
//
// Wow! What else can be said. This routine does alot of stuff in response
// to an incoming WM_DDE_DATA message. There are basically two flavors of
// response here. First is when we were expecting to get this result,
// in which case we know what we wanted to do with the data. Second is
// when the data just arrives, but we didn't expect it. These cases could
// indicate that the server is shutting down.
//
//
// Arguments: [pChannel] -- The DDE channel recieving the message
// [hDdeData] -- Handle to the data
// [aItem] -- Atom to the item
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 5-16-94 kevinro Restructured and commented
//
// Notes:
//
//
// Be extra careful you change this routine.
// This is especially neccesary if you are going to exit early. The
// way that hDdeData is free'd or kept should be understood before
// changing.
//
//
//----------------------------------------------------------------------------
INTERNAL CDdeObject::OnData
(LPDDE_CHANNEL pChannel,
HANDLE hDdeData,
ATOM aItem)
{
intrDebugOut((DEB_ITRACE,
"CDdeObject::OnData(%x,pChannel=%x,hDdeData=%x,aItem=%x(%s)iAwaitAck=%x\n",
this,
pChannel,
hDdeData,
aItem,
wAtomNameA(aItem),
pChannel->iAwaitAck));
#ifdef _MAC
#else
DDEDATA * lpDdeData = NULL;
BOOL fAck = TRUE;
int iAdvOpt;
BOOL fCallBack;
HRESULT hresult = NOERROR;
ICallControl *lpCallCont = pChannel->pCallCont;
intrAssert(lpCallCont != NULL);
BOOL fRequested = FALSE;
intrAssert(wIsValidAtom(aItem));
int iAwaitAck = pChannel->iAwaitAck;
//
// If we were waiting for this data, then we are sitting in the
// modal loop. Set the call state on the call control interface
// to indicate that a response was recieved. Pass NOERROR to indicate
// that there was success. If an error is determined later, then
// the state will be set a second time.
//
if ((AA_REQUEST == iAwaitAck) || (AA_REQUESTAVAILABLE == iAwaitAck))
{
intrDebugOut((DEB_ITRACE,
"::OnData(%x) AA_REQUEST/AVAILABLE pCD->id=%x\n",
this,
pChannel->pCD?pChannel->pCD->id:0xBAD));
//
// Regardless of the outcome of this call, we have recieved a
// response. Set the Awaiting Ack state to nothing.
//
pChannel->iAwaitAck = AA_NONE;
//
// Determine if this channels call data is valid, and is in use.
// If the channel is in use, then then its id will not be
// CALLDATAID_UNUSED. If the call data isn't valid, then something
// is wrong, but there isn't a good way to recover.
//
if (pChannel->pCD && (pChannel->pCD->id != CALLDATAID_UNUSED))
{
// call only if there is a call id
//CoSetAckState(pChannel->pCI, FALSE); // clear waiting flag
intrDebugOut((DEB_ITRACE,"::OnData(%x) SetCallState(SERVERCALLEX_ISHANDLED)\n",this));
pChannel->pCallCont->SetCallState(pChannel->pCD,
SERVERCALLEX_ISHANDLED,
NOERROR);
}
}
//
// Check the string for aItem, looking for advise options. The variable
// iAdvOpt will be set to indicate what type of data we just got, such
// as ON_CHANGE, etc. If the option is invalid, then we won't know
// what to do with it.
//
if ((hresult=wScanItemOptions (aItem, (int *) &iAdvOpt)) != NOERROR)
{
intrAssert(!"Item found with unknown advise option\n");
LPARAM lp;
if(!wPostMessageToServer (pChannel,
WM_DDE_ACK,
lp = MAKE_DDE_LPARAM (WM_DDE_ACK,NEGATIVE_ACK, aItem),TRUE))
{
hresult = RPC_E_SERVER_DIED;
}
//
// Did we need to free hDdeData here? No, according to the DDE spec, if the
// receiever responds with a NACK, then the sender is responsible for
// freeing the data.
//
return hresult;
}
//
// If the server sent no data, there ain't much we can do about it.
//
if (hDdeData == NULL)
{
intrDebugOut((DEB_IERROR,
"::OnData(%x)hDdeData is NULL!\n",
this));
return(RPC_E_INVALID_PARAMETER);
}
//
// Lock the data into memory so we can use it. Be careful, the way
// this routine was written originally, there are places that free
// and realloc hDdeData. Specifically, the call to KeepData. Carefully
// evaluate each place where you are returning, to insure the memory
// isn't leaked. (if you have time, please restructure this routine
// so it is easier to understand.
//
if (!(lpDdeData = (DDEDATA FAR *) GlobalLock(hDdeData)))
{
intrDebugOut((DEB_IERROR,
"::OnData(%x)GlobalLock on lpDdeData failed\n",
this));
//
// BUGBUG: (KevinRo)Did we need to free hDdeData here? I think
// we should have if the fRelease flag was set. The old code
// didn't. Need to research this further (ie you figure it out!)
//
return ResultFromScode (E_OUTOFMEMORY);
}
intrDebugOut((INTR_DDE,
"::OnData(%x) lpDdeData->cfFormat=%x\n",
this,
(UINT)lpDdeData->cfFormat));
//
// The server will set fAckReq if it wants a response.
// don't call HIC for call where not acknoewledge is requested
//
fAck = lpDdeData->fAckReq;
if (pChannel->bTerminating) {
intrDebugOut((INTR_DDE,"::OnData(%x) Got DDE_DATA in terminate sequence\n",this));
//
// BUGBUG: this is very dangerous since the pointer on the
// hDocWnd does not get deleted and a further will
// DDE message will GPF - we need to fix this!!!
//
GlobalUnlock (hDdeData);
GlobalFree (hDdeData);
goto exitRtn;
}
//
// (KevinRo) Found this comment:
//
// important that we post the acknowledge first. Otherwise the
// messages are not in sync.
//
// The above comment might be intended to mean that the acknowledge needs to be
// send now, because we may call one of the advise functions below, which in
// turn may send another message to the OLE 1.0 server. Therefore, we ACK now,
// so the messages to the OLE 1.0 server are in the correct order.
//
if (fAck)
{
LPARAM lp;
if(!wPostMessageToServer (pChannel,
WM_DDE_ACK,
lp=MAKE_DDE_LPARAM(WM_DDE_ACK,POSITIVE_ACK, aItem),TRUE))
{
return(RPC_E_SERVER_DIED);
}
}
//
// this call is now an async call and can not be rejected be HandleIncomingMessage
//
if ((AA_REQUESTAVAILABLE == pChannel->iAwaitAck) && (lpDdeData->fResponse))
{
//
// For some reasons, OnDataAvailable will be the one to delete this data.
// I don't understand it, but lets roll with it. (KevinRo)
//
GlobalUnlock (hDdeData);
return OnDataAvailable (pChannel, hDdeData, aItem);
}
//
// If the clipboard format is binary, and the topic is aStdDocName, then this
// OnData is a RENAME
//
if (lpDdeData->cfFormat == (short)g_cfBinary && aItem== aStdDocName)
{
// ON_RENAME
//
// The data should be the new name, in ANSI.
//
ChangeTopic ((LPSTR)lpDdeData->Value);
GlobalUnlock (hDdeData);
GlobalFree (hDdeData);
return(NOERROR);
}
//
// Based on iAdvOpt, determine if we can callback. This one is a little
// hard to understand. I don't either. CanCallBack appears to return
// true if the count is 0,1, or 3, but returns FALSE if its 2 or
// greater than 3. There are no comments in the old code as to why
// this is. I am leaving it, since it must have been put there for
// a reason. See CanCallBack in ddeworker.cxx for futher (ie no) details
//
switch (iAdvOpt)
{
case ON_SAVE:
fCallBack = CanCallBack(&m_iAdvSave);
intrDebugOut((INTR_DDE,
"::OnData(%x)ON_SAVE m_iAdvSave=%x\n",
this,
m_iAdvSave));
break;
case ON_CLOSE:
fCallBack = CanCallBack(&m_iAdvClose);
intrDebugOut((INTR_DDE,
"::OnData(%x)ON_CLOSE m_iAdvClose=%x\n",
this,
m_iAdvClose));
break;
case ON_CHANGE:
fCallBack = TRUE;
intrDebugOut((INTR_DDE,
"::OnData(%x)ON_CHANGE m_iAdvClose=%x\n",
this,
m_iAdvClose));
break;
default:
intrAssert( !"Unknown iAdvOpt: Somethings really broke");
}
// Keep the data in a cache for a future GetData call
// which may be triggered a few lines later by the
// SendOnDataChange().
fRequested = lpDdeData->fResponse;
// The call to KeepData will change hDdeData and
// invalidate lpDdeData. Check out KeepData for details. The net
// result is that hDdeData is no longer valid
GlobalUnlock (hDdeData);
lpDdeData=NULL;
hresult = KeepData (pChannel, hDdeData);
//
// This is unpleasant, but if KeepData fails, we need to
// call SetCallState again, resetting the error code. This
// code is such a mess that rearranging it to do
// it in a rational way is going to be too much work given
// the amount of time I have until shipping.
//
// If you have time, please simplify this code. Thanks
//
if (hresult != NOERROR)
{
//
// At this point, hDdeData has been unlocked, and deleted by
// the KeepData routine. Therefore, the return here doesn't
// need to be concerned with cleaning up after hDdeData
//
intrDebugOut((DEB_ITRACE,
"::OnData(%x) KeepData failed %x\n",
this,
hresult));
//
// Reset the error code on the call control
//
if ((AA_REQUEST == iAwaitAck) || (AA_REQUESTAVAILABLE == iAwaitAck))
{
if (pChannel->pCD && (pChannel->pCD->id != CALLDATAID_UNUSED))
{
pChannel->pCallCont->SetCallState(pChannel->pCD,
SERVERCALLEX_ISHANDLED,
hresult);
}
}
goto exitRtn;
}
if (fRequested)
{
// We REQUESTed the data. So, we are no longer waiting.
// Do NOT call SendOnDataChange because the data hasn't
// really changed again, we just requested it to satisfy
// a call to GetData, which was probably called by the
// real SendOnDataChange.
intrDebugOut((INTR_DDE,
"::OnData(%x) fRequested DATA\n",
this));
iAwaitAck = NULL;
hresult = NOERROR;
goto exitRtn;
}
//
// Now we have decided this is data we had not asked for. This makes
// it a change/close/saved notificiation.
//
intrDebugOut((INTR_DDE,"::OnData(%x) Non requested DATA\n",this));
pChannel->AddReference();
if (fCallBack && iAdvOpt != ON_CHANGE)
{
// ON_CHANGE will be handled by OleCallback, below
intrDebugOut((INTR_DDE,
"::OnData(%x)Dispatching SendOnDataChange\n",
this));
//
// There are a couple of things to note about the following. First,
// the iid of the call doesn't matter. Since OLE 1.0 servers don't
// do nested calls, the original LID (Logical ID) can be any random
// value. Therefore, we don't initalize it.
//
// According to JohannP, the calltype of these calls is supposed
// to be CALLTYPE_SYNC. I don't fully understand why they are.
// I am taking is decision on faith.
//
// Using the new call control interfaces, we do the following.
//
IID iid;
DDEDISPATCHDATA ddedispdata;
DISPATCHDATA dispatchdata;
INTERFACEINFO32 ifInfo;
ifInfo.pUnk = m_pDataAdvHolder;
ifInfo.iid = IID_IDataAdviseHolder;
//
// We are about to call method #6 in the interface,
// which is SendOnDataChange
//
ifInfo.wMethod = 6;
ifInfo.callcat = CALLCAT_SYNCHRONOUS;
//dispatchdata.scode = S_OK;
dispatchdata.pData = (LPVOID) &ddedispdata;
ddedispdata.pCDdeObject = this;
ddedispdata.wDispFunc = DDE_DISP_SENDONDATACHANGE;
ddedispdata.iArg = iAdvOpt;
lpCallCont->HandleDispatchCall((DWORD)GetWindowTask(pChannel->hwndSvr),
iid,
&ifInfo,
&dispatchdata);
}
if (fCallBack && (pChannel->pCallCont != NULL))
{
// in 1.0 ON_CLOSE comes with data
if (iAdvOpt==ON_CLOSE)
{
intrDebugOut((INTR_DDE,
"::OnData(%x) iAdvOpt == ON_CLOSE, send ON_SAVE\n",
this));
m_fGotCloseData = TRUE;
hresult = OleCallBack(ON_SAVE,pChannel);
if (hresult != NOERROR)
{
goto errRel;
}
//ErrRtnH (DdeHandleIncomingCall(pChannel->hwndSvr, CALLTYPE_TOPLEVEL) );
//ErrRtnH (OleCallBack (ON_SAVE));
}
// check if app can handle this call
// we do not need to call HIC for SendOnClose
hresult = OleCallBack (iAdvOpt,pChannel);
}
errRel:
// Don't use pChannel after this. It can get deleted. (srinik)
if (pChannel->ReleaseReference() == 0)
{
m_pDocChannel = NULL;
}
exitRtn:
if (!fAck && aItem)
{
GlobalDeleteAtom (aItem);
}
return hresult;
#endif _MAC
}
//+---------------------------------------------------------------------------
//
// Method: CDdeObject::OleCallBack
//
// Synopsis: Send all the right notifications whan a Save or Close happens.
//
// Effects: OleCallBack is a double duty function. It is called in two
// different cases.
//
// First, is to setup the callback, and call HandleIncomingCall.
// Second is from DispatchCall() in the CDdeChannelControl.
//
// The reason for doing it this way is we localize the setup
// and processing of these calls to one routine. Therefore,
// we can go to one spot in the code to find all of the call
// back information.
//
// Arguments: [iAdvOpt] -- Which Advise operation to perform
// [pChannel] -- Which channel is being called back
//
// Requires: pChannel == NULL, and the AdviseHolders are called.
// pChannel != NULL, and the call is setup, and HandleIncomingCall
// is setup.
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 5-23-94 kevinro Created
//
// Notes:
//
// WARNING: This code sucks completely. One of the major problems you need
// to know about is that the CDdeObject may go away as part of the normal
// processing of some of the below. Be very careful about any processing
// that might occur after an ON_CLOSE
//
//----------------------------------------------------------------------------
INTERNAL CDdeObject::OleCallBack (int iAdvOpt, LPDDE_CHANNEL pChannel)
{
HRESULT hresult = NOERROR;
IID iid;
DDEDISPATCHDATA ddedispdata;
DISPATCHDATA dispatchdata;
INTERFACEINFO32 ifInfo;
ICallControl *lpCallCont = NULL;
//
// If the channel isn't NULL, then setup the data structures for calling
// off to the call control.
//
if (pChannel != NULL)
{
lpCallCont = pChannel->pCallCont;
if (lpCallCont == NULL)
{
return(E_UNEXPECTED);
}
//
// Only do this work if we really have to
//
dispatchdata.scode = S_OK;
dispatchdata.pData = (LPVOID) &ddedispdata;
ddedispdata.pCDdeObject = this;
ddedispdata.wDispFunc = DDE_DISP_OLECALLBACK;
ddedispdata.iArg = iAdvOpt;
}
intrDebugOut((DEB_ITRACE,
"CDdeObject::OleCallBack(%x,iAdvOpt=%x,pChannel=%x,lpCallCont=%x)\n",
this,
iAdvOpt,
pChannel,
lpCallCont));
//
// Determine what needs to be done, based on the iAdvOpt. This should be
// one of the handled cases below, otherwise its an error.
//
switch (iAdvOpt)
{
case ON_CLOSE:
if (pChannel != NULL)
{
intrDebugOut((DEB_ITRACE,
"::OleCallBack(%x) setup for ON_CLOSE\n",
this));
ifInfo.pUnk = m_pOleAdvHolder;
ifInfo.iid = IID_IOleAdviseHolder;
// IOleAdviseHolder::SendOnClose is method 8
ifInfo.wMethod = 8;
ifInfo.callcat = CALLCAT_SYNCHRONOUS;
}
else
{
intrDebugOut((DEB_ITRACE,"::OleCallBack(%x) ON_CLOSE\n",this));
DeclareVisibility (FALSE);
RetZ (!m_fDidSendOnClose); // This SendOnClose should happen 1st
// Don't let OnTerminate() do it too
hresult = SendOnClose();
//
// WARNING WARNING WARNING: SendOnClose() may have caused the
// destruction of this CDdeObject. Touch nothing on the way
// out. Actually, if you have time, which I currently don't,
// see what you can do with reference counting tricks to
// insure this object doesn't die during this callback.
// Its a tricky problem, and we are shipping in 2 weeks.
// (KevinRo 8/6/94)
//
}
break;
case ON_SAVE:
if (pChannel != NULL)
{
intrDebugOut((DEB_ITRACE,
"::OleCallBack(%x) setup for ON_SAVE\n",
this));
if (m_pOleClientSite == NULL)
{
ifInfo.pUnk = m_pOleClientSite;
ifInfo.iid = IID_IOleClientSite;
// IOleClientSite::SaveObject method 7
ifInfo.wMethod = 7;
ifInfo.callcat = CALLCAT_SYNCHRONOUS;
}
else
{
// Going to call the IOleAdviseHolder
ifInfo.pUnk = m_pOleAdvHolder;
ifInfo.iid = IID_IOleAdviseHolder;
// IOleAdviseHolder::SendOnSave method 7
// (Yes, same ordinal as above, I double checked)
ifInfo.wMethod = 7;
ifInfo.callcat = CALLCAT_SYNCHRONOUS;
}
}
else
{
intrDebugOut((DEB_ITRACE,"::OleCallBack(%x) ON_SAVE\n",this));
if (m_pOleClientSite)
{
// We just got data from the server, so we don't want to
// ask him for it again when the container does a save.
m_fUpdateOnSave = FALSE;
m_pOleClientSite->SaveObject();
// SendOnSave is called in PS::SaveCompleted
m_fUpdateOnSave = TRUE;
}
else
{
// Link case
RetZS (m_pOleAdvHolder, E_OUTOFMEMORY);
m_pOleAdvHolder->SendOnSave();
}
}
break;
case ON_CHANGE:
if (pChannel != NULL)
{
// Going to call the IDataAdviseHolder
ifInfo.pUnk = m_pDataAdvHolder;
ifInfo.iid = IID_IDataAdviseHolder;
// IDataAdviseHolder::SendOnDataChange method 6
ifInfo.wMethod = 6;
ifInfo.callcat = CALLCAT_SYNCHRONOUS;
}
else
{
RetZS (m_pDataAdvHolder, E_OUTOFMEMORY);
intrDebugOut((DEB_ITRACE,"::OleCallBack(%x) ON_CHANGE\n",this));
hresult = SendOnDataChange (ON_CHANGE);
}
break;
default:
intrDebugOut((DEB_ITRACE,
"CDdeObject::OleCallBack(%x,iAdvOpt=%x) UNKNOWN iAdvOpt\n",
this,
iAdvOpt));
intrAssert(!"Unexpected iAdvOpt");
return(E_UNEXPECTED);
break;
}
//
// There are a couple of things to note about the following. First,
// the iid of the call doesn't matter. Since OLE 1.0 servers don't
// do nested calls, the original LID (Logical ID) can be any random
// value. Therefore, we don't initalize it.
//
// According to JohannP, the calltype of these calls is supposed
// to be CALLTYPE_SYNCHRONOUS. I don't fully understand why they are.
// I am taking is decision on faith.
//
//
// Its possible that during the handling of this call that this object
// will get deleted. This is a pain in the butt. This means that anything
// used after this call MUST be protected. lpCallCont happens to be one of
// these. We have been having problems with lpCallCont being released as
// part of the object cleanup. The call control code will access member
// variables on its way out of the HandleDispatch. We need to bracket
// the call below so this doesn't happen.
//
if (pChannel != NULL)
{
//
// We normally keep a per thread copy of the call control interface.
// When the reference count for that copy goes to zero, we need to
// flush that copy. We can do the addref here, because the current
// reference is the same value that would be returned by
// GetDdeCallControlInterface. The balancing call, however, needs
// to be ReleaseDdeCallControlInterface, since it may need to flush
// the TLS value.
//
lpCallCont->AddRef();
hresult = lpCallCont->HandleDispatchCall((DWORD)GetWindowTask(pChannel->hwndSvr),
iid,
&ifInfo,
&dispatchdata);
ReleaseDdeCallControlInterface();
}
return hresult;
}
INTERNAL CDdeObject::SendOnDataChange
(int iAdvOpt)
{
intrDebugOut((DEB_ITRACE,"CDdeObject::SendOnDataChange(%x)\n",this));
HRESULT hresult;
RetZS (m_pDataAdvHolder, E_OUTOFMEMORY);
m_fDoingSendOnDataChange = TRUE;
hresult = m_pDataAdvHolder->SendOnDataChange (&m_Data,
DVASPECT_CONTENT,
0);
if (ON_CLOSE==iAdvOpt)
{
hresult = m_pDataAdvHolder->SendOnDataChange (&m_Data,
DVASPECT_CONTENT,
ADVF_DATAONSTOP);
}
m_fDoingSendOnDataChange = FALSE;
return hresult;
}
INTERNAL CDdeObject::SendOnClose
(void)
{
intrDebugOut((DEB_ITRACE,"CDdeObject::SendOnClose(%x)\n",this));
RetZS (m_pOleAdvHolder, E_OUTOFMEMORY);
m_fDidSendOnClose = TRUE;
RetErr (m_pOleAdvHolder->SendOnClose() );
return NOERROR;
}
INTERNAL CDdeObject::OnTerminate
(LPDDE_CHANNEL pChannel,
HWND hwndPost)
{
intrDebugOut((DEB_ITRACE,
"CDdeObject::OnTerminate(%x,pChannel=%x,hwndPost=%x)\n",
this,
pChannel,
hwndPost));
//
// If the hwndPost and hwndSvr are different, then it is one of two
// cases. We could have recieved more than one Acknowlege during our
// initiate, in which case the count iExtraTerms would have been
// incremented, and this terminate is accounted for iExtraTerms.
//
// The other case is that we were terminated by a window that was
// NOT the window we were conversing with.
//
if (pChannel->hwndSvr != hwndPost)
{
intrDebugOut((DEB_ITRACE,
"::OnTerminate(%x) Extra terms is 0x%x \n",
this,
pChannel->iExtraTerms));
//
// iExtraTerms shouldn't go below zero. If it does, something
// has gone wrong. We have been seeing some problems with the
// HWND mapping layer in the past. If the following condition
// ever trips, then it is possible that another mapping
// problem has been seen.
//
#if DBG == 1
if((pChannel->iExtraTerms == 0 ) &&
(((DWORD)pChannel->hwndSvr) & (0xffff)) == ((DWORD)hwndPost & (0xffff)))
{
intrDebugOut((DEB_ERROR,
"*** OnTerminate expected hwnd=%x got hwnd=%x ***\n",
pChannel->hwndSvr,hwndPost));
intrDebugOut((DEB_ERROR,
"\n*** Call KevinRo or SanfordS ***\n\n",
pChannel->hwndSvr,hwndPost));
}
#endif
--pChannel->iExtraTerms;
intrAssert((pChannel->iExtraTerms >= 0) && "Call KevinRo or SanfordS");
return NOERROR;
}
if (m_wTerminate == Terminate_Detect) {
// we should only detect the call but not execute the code
// set the state to Received
m_wTerminate = Terminate_Received;
pChannel->iAwaitAck = NULL;
// Since Excel incorrectly did not send an ACK, we need to
// delete the handle in the DDE message ourselves.
if (pChannel->hCommands)
{
GlobalFree (pChannel->hCommands);
pChannel->hCommands = NULL;
}
//CoSetAckState(pChannel->pCI, FALSE);
intrDebugOut((DEB_ITRACE,
"::OnTerminate(%x) Terminate_Detect SERVERCALLEX_ISHANDLED\n",
this));
pChannel->pCallCont->SetCallState(pChannel->pCD, SERVERCALLEX_ISHANDLED, RPC_E_SERVER_DIED);
return NOERROR;
}
RetZ (pChannel);
ChkDR (this);
if (!pChannel->bTerminating)
{
// Got unprompted terminate
BOOL bBusy;
// Necessary safety bracket
m_pUnkOuter->AddRef();
bBusy = wClearWaitState (pChannel);
if (pChannel->iAwaitAck || bBusy)
{
pChannel->iAwaitAck = NULL;
//CoSetAckState(pChannel->pCI, FALSE);
intrDebugOut((DEB_ITRACE,"::OnTerminate(%x) !bTerminating SERVERCALLEX_ISHANDLED,RPC_E_DDE_UNEXP_MSG\n",this));
pChannel->pCallCont->SetCallState(pChannel->pCD, SERVERCALLEX_ISHANDLED, RPC_E_DDE_UNEXP_MSG);
}
if (!m_fDidSendOnClose)
{
intrDebugOut((DEB_ITRACE,
"::OnTerminate(%x) SendOnClose from terminate\n",
this));
BOOL f= m_fNoStdCloseDoc;
m_fNoStdCloseDoc = TRUE;
DeclareVisibility (FALSE);
SendOnClose();
m_fNoStdCloseDoc = f;
}
else
{
intrDebugOut((DEB_ITRACE,
"::OnTerminate(%x) Already did SendOnClose\n",
this));
Puts ("Already did SendOnClose\n");
}
intrDebugOut((DEB_ITRACE,
"::OnTerminate(%x) Posting DDE_TERMINATE as reply\n",
this));
wPostMessageToServer (pChannel, WM_DDE_TERMINATE, NULL,FALSE);
// The terminate that we are sending itself is a reply, so we don't
// need to do WaitForReply.
DeleteChannel (pChannel);
// Necessary safety bracket
m_pUnkOuter->Release();
}
else
{
intrDebugOut((DEB_ITRACE,
"::OnTerminate(%x) Received DDE_TERMINATE in reply\n",
this));
// We sent the WM_DDE_TERMINATE and we got the acknowledge for it
pChannel->hwndSvr = NULL;
pChannel->iExtraTerms == NULL;
pChannel->iAwaitAck = NULL;
//CoSetAckState(pChannel->pCI, FALSE);
intrDebugOut((DEB_ITRACE,"::OnTerminate(%x) bTerminating SERVERCALLEX_ISHANDLED\n",this));
pChannel->pCallCont->SetCallState(pChannel->pCD, SERVERCALLEX_ISHANDLED, NOERROR);
}
Puts ("OnTerminate() done.\n");
return NOERROR;
}
INTERNAL_(BOOL) CDdeObject::AllocDdeChannel
(LPDDE_CHANNEL * lplpChannel, LPSTR lpszWndClass)
{
intrDebugOut((DEB_ITRACE,
"CDdeObject::AllocDdeChannel(%x,WndClass=%s)\n",
this,
lpszWndClass));
//
// Try to get the global call control interface
//
PCALLCONTROL pCallCont = GetDdeCallControlInterface();
if (pCallCont == NULL)
{
//
// Couldn't allocate a CallControl. Fail.
//
intrAssert(pCallCont != NULL);
return(FALSE);
}
//
// Now try to allocate a channel
//
if (!(*lplpChannel = (LPDDE_CHANNEL) new DDE_CHANNEL ))
{
//
// This failed, so give back the CallControl
//
intrAssert(*lplpChannel != NULL);
ReleaseDdeCallControlInterface();
return FALSE;
}
(*lplpChannel)->m_cRefs = 1;
(*lplpChannel)->hwndSvr = NULL;
(*lplpChannel)->bTerminating = FALSE;
(*lplpChannel)->wTimer = NULL;
(*lplpChannel)->hDdePoke = NULL;
(*lplpChannel)->hCommands = NULL;
(*lplpChannel)->hopt = NULL;
(*lplpChannel)->dwStartTickCount= 0;
(*lplpChannel)->msgFirst = 0;
(*lplpChannel)->msgLast = 0;
(*lplpChannel)->fRejected = FALSE;
(*lplpChannel)->wChannelDeleted = 0;
//(*lplpChannel)->pCI = NULL;
(*lplpChannel)->pCD = NULL;
(*lplpChannel)->pCallCont = pCallCont;
(*lplpChannel)->pChanCont = &g_CDdeChannelControl;
if (!((*lplpChannel)->hwndCli = SSCreateWindowExA(0,
lpszWndClass,
"DDE Channel",
WS_CHILD,
0,0,0,0,
(HWND)TLSGetDdeClientWindow(),
NULL,
hinstSO,
NULL)))
{
intrAssert (!"Could not create AllocDdeChannel window");
//
// DeleteChannel will give back the CallControl
//
DeleteChannel(*lplpChannel);
*lplpChannel = NULL;
return FALSE;
}
SetWindowLong ((*lplpChannel)->hwndCli, 0, (LONG) this);
return TRUE;
}
INTERNAL_(BOOL) CDdeObject::InitSysConv()
{
DWORD dwResult;
intrDebugOut((DEB_ITRACE,"CDdeObject::InitSysConv(%x)\n",this));
dwResult = wInitiate (m_pSysChannel, m_aClass, aOLE);
if (!dwResult)
{
intrDebugOut((DEB_ITRACE,"\t::InitSysConv(%x) Try aSysTopic\n",this));
dwResult = wInitiate (m_pSysChannel, m_aClass, aSysTopic);
}
if (!dwResult)
{
intrDebugOut((DEB_ITRACE,"\t::InitSysConv(%x) is failing\n",this));
}
return(dwResult);
}
INTERNAL_(void) CDdeObject::SetTopic(ATOM aTopic)
{
intrDebugOut((DEB_ITRACE,"CDdeObject::SetTopic(%x)\n",this));
intrAssert(wIsValidAtom(aTopic));
if (m_aTopic)
GlobalDeleteAtom (m_aTopic);
m_aTopic = aTopic;
}
INTERNAL CDdeObject::TermConv
(LPDDE_CHANNEL pChannel,
BOOL fWait) // Default==TRUE. FALSE only in ProxyManager::Disconnect
{
intrDebugOut((DEB_ITRACE,
"CDdeObject::TermConv(%x,pChannel=%x)\n",
this,
pChannel));
HRESULT hres;
if (!pChannel)
{
return NOERROR;
}
if (!wPostMessageToServer (pChannel, WM_DDE_TERMINATE, 0,FALSE))
{
intrDebugOut((DEB_ITRACE,
"CDdeObject::TermConv(%x) Server Probably Died\n",
this));
hres = RPC_E_SERVER_DIED;
}
else
{
pChannel->bTerminating = TRUE;
Putsi (fWait);
hres = fWait ? WaitForReply (pChannel,
AA_TERMINATE,
/*fStdCloseDoc*/FALSE,
/*fDetectTerminate*/ FALSE) : NOERROR;
if (pChannel==m_pDocChannel)
{
DeclareVisibility (FALSE);
if (!m_fDidSendOnClose)
{
SendOnClose();
}
}
}
DeleteChannel (pChannel);
intrDebugOut((DEB_ITRACE,"::TermConv(%x) returns %x\n",this,hres));
return hres;
}
INTERNAL_(void) CDdeObject::DeleteChannel (LPDDE_CHANNEL pChannel)
{
BOOL fDocChannel = FALSE;
intrDebugOut((DEB_ITRACE,
"CDdeObject::DeleteChannel(%x,pChannel=%x)\n",
this,
pChannel));
if (pChannel == NULL)
{
return;
}
if (pChannel == m_pDocChannel)
fDocChannel = TRUE;
// delete any data if we were in busy mode.
wClearWaitState (pChannel);
if (pChannel == m_pDocChannel)
{
intrDebugOut((DEB_ITRACE,
"::DeleteChannel(%x)Clean up pDocChannel\n",
this));
// Cleanup per-conversation information
m_fDidSendOnClose = FALSE;
m_fDidStdCloseDoc = FALSE;
m_ConnectionTable.Erase();
m_iAdvSave = 0;
m_iAdvClose= 0;
m_fWasEverVisible = FALSE;
m_fGotCloseData = FALSE;
if (m_ptd)
{
delete m_ptd;
m_ptd = NULL;
}
if (m_pstg)
{
m_pstg->Release();
m_pstg = NULL;
}
if (m_pDataAdvHolder)
{
Verify (0==m_pDataAdvHolder->Release());
}
CreateDataAdviseHolder (&m_pDataAdvHolder);
if (m_pOleAdvHolder)
{
m_pOleAdvHolder->Release(); // may not return 0 if we are
// in a SendOnClose
}
CreateOleAdviseHolder (&m_pOleAdvHolder);
}
if (pChannel->hwndCli)
{
intrDebugOut((DEB_ITRACE,
"::DeleteChannel(%x)Destroy hwndCli(%x)\n",
this,
pChannel->hwndCli));
Assert (IsWindow (pChannel->hwndCli));
Assert (this==(CDdeObject *)GetWindowLong (pChannel->hwndCli, 0));
Verify (SSDestroyWindow (pChannel->hwndCli));
}
if (pChannel == m_pDocChannel)
{
m_pDocChannel = NULL;
}
else
{
intrAssert(pChannel == m_pSysChannel);
m_pSysChannel = NULL;
}
if (pChannel->pCallCont != NULL)
{
ReleaseDdeCallControlInterface();
pChannel->pCallCont = NULL;
}
// Channel will be deleted in the modallp.cpp
// if flag is on.
if (pChannel->wChannelDeleted == Channel_InModalloop)
{
intrDebugOut((DEB_ITRACE,
"::DeleteChannel(%x) Channel(%x) in Modal Loop\n",
this,pChannel));
pChannel->wChannelDeleted = Channel_DeleteNow;
}
else
{
if (pChannel->ReleaseReference() == 0)
pChannel = NULL;
}
if (fDocChannel)
m_pDocChannel = pChannel;
}
const WCHAR EMB_STR[]= OLESTR(" -Embedding ") ;
INTERNAL_(BOOL) CDdeObject::LaunchApp (void)
{
intrDebugOut((DEB_ITRACE,"CDdeObject::LaunchApp(%x)\n",this));
STARTUPINFO startInfo;
PROCESS_INFORMATION procInfo;
BOOL fProcStarted;
WCHAR cmdline[MAX_PATH + sizeof(EMB_STR)];
WCHAR exeName[MAX_PATH + sizeof(cmdline)];
//
// Init all fields of startInfo to zero
//
memset((void *)&startInfo,0,sizeof(startInfo));
//
// The normal startup is set here.
//
startInfo.wShowWindow = SW_NORMAL;
startInfo.dwFlags = STARTF_USESHOWWINDOW;
m_fDidLaunchApp = FALSE;
DWORD dw;
//
// Do our best to find the path
//
intrAssert(wIsValidAtom(m_aExeName));
if (m_aExeName == 0)
{
//
// There is no exe name to execute. Can't start it.
//
return(FALSE);
}
dw = SearchPath(NULL,wAtomName(m_aExeName),NULL,MAX_PATH,exeName, NULL);
if ((dw == 0) || (dw > MAX_PATH))
{
intrDebugOut((DEB_ITRACE,
"::LaunchApp(%x) SearchPath failed. Do Default",this));
//
// SearchPath failed. Use the default
//
GlobalGetAtomName (m_aExeName, exeName, MAX_PATH);
}
memcpy(cmdline, EMB_STR,sizeof(EMB_STR));
if (m_ulObjType == OT_LINK)
{
intrAssert(wIsValidAtom(m_aTopic));
// File name
Assert (wAtomName (m_aTopic));
lstrcatW (cmdline, wAtomName (m_aTopic));
}
if (m_clsid == CLSID_ExcelWorksheet // Stupid apps that show themselves
|| m_clsid == CLSID_ExcelMacrosheet // when they're not supposed to
|| m_clsid == CLSID_ExcelChart
|| m_clsid == CLSID_PBrush)
{
startInfo.wShowWindow = SW_SHOWMINNOACTIVE;
}
//
// According to the spec, the most robust way to start the app is to
// only use a cmdline that consists of the exe name, followed by the
// command line arguments.
//
lstrcatW(exeName,cmdline);
Assert((lstrlenW(exeName)+1) < sizeof(exeName));
intrDebugOut((DEB_ITRACE,
"CDdeObject::LaunchApp(%x) Starting '%ws' \n",
this,
exeName));
if (IsWOWThreadCallable())
{
HRESULT hr;
hr = g_pOleThunkWOW->WinExec16(exeName, startInfo.wShowWindow);
fProcStarted = SUCCEEDED(hr);
#if DBG==1
if (!fProcStarted)
{
intrDebugOut((DEB_ITRACE,
"::LaunchApp(%x) in Wow FAILED(%x) TO START %ws \n",
this,
hr,
exeName));
}
#endif
}
else
{
fProcStarted = CreateProcess(NULL,
exeName,
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
&startInfo,
&procInfo);
if (fProcStarted)
{
//
// Let's give the server a chance to register itself. On NT,
// CreateProcess gets the other process going, but returns
// to let it run asynchronously. This isn't good, since we
// need some way of knowing when it has started, so we can
// send the DDE_INITIATES 'after' they create their DDE
// window.
//
// Maximum timeout we want here shall be set at 30 seconds.
// This should give enough time for even a 16bit WOW app to
// start. This number was picked by trial and error. Normal
// apps that go into an InputIdle state will return as soon
// as they are ready. Therefore, we normally won't wait
// the full duration.
//
ULONG ulTimeoutDuration = 30000L;
//
// Now modify this start time to handle classes
// that have known problems. This list includes:
//
switch(WaitForInputIdle(procInfo.hProcess, ulTimeoutDuration))
{
case 0:
intrDebugOut((DEB_ITRACE,
"::LaunchApp, %ws started\n",
exeName));
break;
case WAIT_TIMEOUT:
intrDebugOut((DEB_ITRACE,
"::LaunchApp, %ws wait timeout at %u (dec) ms. Go Anyway\n",
exeName,
ulTimeoutDuration));
break;
default:
intrDebugOut((DEB_ITRACE,
"::LaunchApp, %ws unknown condition (%x)\n",
exeName,
GetLastError()));
}
//
// We are already done with the Process and Thread handles
//
CloseHandle(procInfo.hProcess);
CloseHandle(procInfo.hThread);
}
else
{
intrDebugOut((DEB_ITRACE,
"::LaunchApp(%x) FAILED(%x) TO START %ws \n",
this,
GetLastError(),
exeName));
}
}
if (fProcStarted)
{
// If we ran the server, it should not be visible yet.
DeclareVisibility (FALSE);
m_fDidLaunchApp = TRUE;
}
return fProcStarted;
}
INTERNAL CDdeObject::MaybeUnlaunchApp (void)
{
intrDebugOut((DEB_ITRACE,"CDdeObject::MaybeUnlaunchApp(%x)\n",this));
if (m_fDidLaunchApp
&& !m_fDidGetObject
&& (m_clsid == CLSID_ExcelWorksheet
|| m_clsid == CLSID_ExcelMacrosheet
|| m_clsid == CLSID_ExcelChart))
{
return UnlaunchApp();
}
return NOERROR;
}
INTERNAL CDdeObject::UnlaunchApp (void)
{
intrDebugOut((DEB_ITRACE,"CDdeObject::UnlaunchApp(%x)\n",this));
HANDLE hCommands;
HRESULT hresult = NOERROR;
RetZS (AllocDdeChannel (&m_pSysChannel, SYS_CLASSA), E_OUTOFMEMORY);
ErrZS (InitSysConv(), E_UNEXPECTED);
ErrRtnH (PostSysCommand (m_pSysChannel,(LPSTR) &achStdExit, /*bStdNew*/FALSE,
/*fWait*/FALSE));
hCommands = m_pSysChannel->hCommands;
hresult = TermConv (m_pSysChannel);
// Since Excel incorrectly did not send an ACK, we need to
// delete the handle ("[StdExit]") in the DDE message ourselves.
if (hCommands)
GlobalFree (hCommands);
return hresult;
errRtn:
DeleteChannel (m_pSysChannel);
return hresult;
}
INTERNAL CDdeObject::Execute
(LPDDE_CHANNEL pChannel,
HANDLE hdata,
BOOL fStdCloseDoc,
BOOL fWait,
BOOL fDetectTerminate)
{
intrDebugOut((DEB_ITRACE,"CDdeObject::Execute(%x,hdata=%x)\n",this,hdata));
LPARAM lp;
#ifdef _CHICAGO_
//POSTPPC
if (fWait && (CanMakeOutCall(pChannel) == FALSE))
{
intrDebugOut((DEB_ERROR, "::Execute(%x) can not call out\n", this));
GlobalFree (hdata);
return ResultFromScode (E_UNEXPECTED);
}
#endif
if (wPostMessageToServer (pChannel,
WM_DDE_EXECUTE,
lp=MAKE_DDE_LPARAM(WM_DDE_EXECUTE,0, hdata),TRUE))
{
if (fStdCloseDoc)
{
// Prepare to free the handle if Excel does not send an Ack
pChannel->hCommands = hdata;
}
return fWait ? WaitForReply (pChannel, AA_EXECUTE, fStdCloseDoc,
fDetectTerminate)
: NOERROR ;
}
GlobalFree (hdata);
return ReportResult(0, RPC_E_DDE_POST, 0, 0);
}
INTERNAL_(HRESULT) CDdeObject::AdviseOn (CLIPFORMAT cfFormat, int iAdvOn)
{
intrDebugOut((DEB_ITRACE,
"CDdeObject::AdviseOn(%x,cfFormat=%x,iAdvOn=%x)\n",
this,
cfFormat,
iAdvOn));
HANDLE hopt=NULL;
DDEADVISE * lpopt=NULL;
ATOM aItem=(ATOM)0;
HRESULT hresult = ReportResult(0, E_UNEXPECTED, 0, 0);
RetZ (m_pDocChannel);
if (NOERROR == m_ConnectionTable.Lookup (cfFormat, NULL))
{
// We already got a call to DataObject::Advise on this format.
intrDebugOut((DEB_ITRACE,
"::AdviseOn(%x) Advise had been done on cfFormat=%x\n",
this,
cfFormat));
return NOERROR;
}
UpdateAdviseCounts (cfFormat, iAdvOn, +1);
if (m_fDidSendOnClose)
{
intrDebugOut((DEB_ITRACE,"::AdviseOn(%x)Ignoring Advise because we are closing\n",this));
return NOERROR;
}
if (!(hopt = GlobalAlloc (GMEM_DDESHARE | GMEM_ZEROINIT, sizeof(DDEADVISE))))
{
intrDebugOut((DEB_ITRACE,"::AdviseOn(%x)GlobalAlloc returned NULL\n",this));
return ReportResult(0, E_OUTOFMEMORY, 0, 0);
}
if (!(lpopt = (DDEADVISE FAR *) GlobalLock (hopt)))
{
intrDebugOut((DEB_ITRACE,"::AdviseOn(%x)GlobalLock returned NULL\n",this));
GlobalFree (hopt);
return ReportResult(0, E_OUTOFMEMORY, 0, 0);
}
lpopt->fAckReq = TRUE;
lpopt->fDeferUpd = FALSE;
lpopt->cfFormat = cfFormat;
m_pDocChannel->hopt = hopt;
if (iAdvOn == ON_RENAME)
{
aItem = wDupAtom (aStdDocName);
intrAssert(wIsValidAtom(aItem));
}
else
{
intrAssert(wIsValidAtom(m_aItem));
aItem = wExtendAtom (m_aItem, iAdvOn);
intrAssert(wIsValidAtom(aItem));
}
intrDebugOut((DEB_ITRACE,
"::AdviseOn(%x) lpopt->cfFormat = %x, aItem=%x (%ws)\n",
this,
lpopt->cfFormat,
aItem,
wAtomName(aItem)));
GlobalUnlock (hopt);
LPARAM lp;
if (FALSE==wPostMessageToServer (m_pDocChannel, WM_DDE_ADVISE,
lp=MAKE_DDE_LPARAM(WM_DDE_ADVISE,hopt,aItem),TRUE))
{
intrDebugOut((DEB_ITRACE,"::AdviseOn(%x)wPostMessageToServer failed\n",this));
if (aItem)
GlobalDeleteAtom (aItem);
if (hopt)
GlobalFree (hopt);
return ResultFromScode (RPC_E_DDE_POST);
}
ErrRtnH (WaitForReply (m_pDocChannel, AA_ADVISE));
return NOERROR;
errRtn:
hresult = (RPC_E_DDE_NACK == hresult) ? DV_E_CLIPFORMAT : hresult;
intrDebugOut((DEB_ITRACE,
"::AdviseOn(%x) errRet, AdviseRejected, returning %x\n",
this,hresult));
return hresult;
}
INTERNAL CDdeObject::UpdateAdviseCounts
(CLIPFORMAT cf,
int iAdvOn,
signed int cDelta)
{
intrDebugOut((DEB_ITRACE,"CDdeObject::UpdateAdviseCounts(%x)\n",this));
if (cf==g_cfBinary)
return NOERROR;
// Update m_iAdv* flags
#define macro(Notif, NOTIF) \
if (iAdvOn == ON_##NOTIF) \
m_iAdv##Notif += cDelta; \
if (m_iAdv##Notif < 0) \
m_iAdv##Notif = 0; \
else if (m_iAdv##Notif > 2) \
m_iAdv##Notif = 2;
macro (Close, CLOSE)
macro (Save, SAVE)
macro (Change,CHANGE)
#undef macro
Assert (m_iAdvClose < 3 && m_iAdvSave < 3 && m_iAdvChange < 3);
Assert (m_iAdvClose >= 0 && m_iAdvSave >= 0 && m_iAdvChange >= 0);
if (cf == g_cfNative)
{
if (iAdvOn != ON_CHANGE)
m_fDidAdvNative = (cDelta > 0);
else
intrDebugOut((DEB_ITRACE,
"::UpdateAdviseCounts(%x)Asked advise on cfNative\n",
this));
}
return NOERROR;
}
INTERNAL CDdeObject::UnAdviseOn (CLIPFORMAT cfFormat, int iAdvOn)
{
intrDebugOut((DEB_ITRACE,
"CDdeObject::UnAdviseOn(%x,cfFormat=%x,iAdvOn=%x)\n",
this,cfFormat,iAdvOn));
HRESULT hr;
ATOM aItem= (ATOM)0;
RetZ (m_pDocChannel);
UpdateAdviseCounts (cfFormat, iAdvOn, -1);
if (m_fDidSendOnClose)
{
intrDebugOut((DEB_ITRACE,
"CDdeObject::UnAdviseOn(%x) Ignored because closing\n",
this));
return NOERROR;
}
if (wTerminateIsComing (m_pDocChannel))
{
// We already did a StdCloseDocument, so the server is not willing
// to do an unadvise even though the default hanlder asked us to.
intrDebugOut((DEB_ITRACE,
"CDdeObject::UnAdviseOn(%x) Terminate coming\n",
this));
return NOERROR;
}
if (iAdvOn == ON_RENAME)
{
aItem = wDupAtom (aStdDocName);
intrAssert(wIsValidAtom(aItem));
}
else
{
intrAssert(wIsValidAtom(m_aItem));
aItem = wExtendAtom (m_aItem, iAdvOn);
intrAssert(wIsValidAtom(aItem));
}
if (!wPostMessageToServer(m_pDocChannel, WM_DDE_UNADVISE,
MAKE_DDE_LPARAM (WM_DDE_UNADVISE,cfFormat,aItem),FALSE))
{
if (aItem)
GlobalDeleteAtom (aItem);
intrDebugOut((DEB_ITRACE,
"CDdeObject::UnAdviseOn(%x) wPostMessage failed\n",
this));
return RPC_E_DDE_POST;
}
// Wait For Reply
hr = WaitForReply (m_pDocChannel, AA_UNADVISE, FALSE);
if (hr != NOERROR && hr != RPC_E_DDE_NACK)
{
intrDebugOut((DEB_ITRACE,
"::UnAdviseOn(%x)WaitForReply returns %x\n",
this));
return hr;
}
if (cfFormat==m_cfPict)
{
if (m_hPict)
{
// Invalidate the cache so when someone explicitly asks for
// the data, they will get fresh data.
wFreeData (m_hPict, m_cfPict, TRUE);
m_hPict = (HANDLE)0;
m_cfPict = 0;
}
}
// Due to a bug in the OLE1 libraries, unadvising on a presentation
// format effectively unadvises on native.
if (cfFormat != g_cfNative && m_fDidAdvNative)
{
if (iAdvOn == ON_SAVE)
{
// to reflect the fact that the native advise connection was lost
m_iAdvSave--;
m_fDidAdvNative = FALSE;
RetErr (AdviseOn (g_cfNative, ON_SAVE)); // re-establish
}
else if (iAdvOn == ON_CLOSE)
{
// to reflect the fact that the native advise connection was lost
m_iAdvClose--;
m_fDidAdvNative = FALSE;
RetErr (AdviseOn (g_cfNative, ON_CLOSE));
}
}
return NOERROR;
}
//
// Post a message to a 1.0 server (callee) and wait for the acknowledge
//
INTERNAL CDdeObject::Poke
(ATOM aItem, HANDLE hDdePoke)
{
HRESULT hr;
intrDebugOut((DEB_ITRACE,"CDdeObject::Poke(%x)\n",this));
ATOM aTmpItem;
intrAssert(wIsValidAtom(aItem));
aTmpItem = wDupAtom (aItem);
intrAssert(wIsValidAtom(aTmpItem));
m_pDocChannel->hDdePoke = hDdePoke;
LPARAM lp;
if (hr = wPostMessageToServer (m_pDocChannel,
WM_DDE_POKE,
lp=MAKE_DDE_LPARAM(WM_DDE_POKE,hDdePoke,aTmpItem),TRUE))
{
hr = WaitForReply (m_pDocChannel, AA_POKE);
intrDebugOut((DEB_ITRACE,"::Poke(%x) returning %x\n",this,hr));
return hr;
}
intrDebugOut((DEB_ITRACE,"::Poke(%x)wPostMessage failed %x\n",this,hr));
// Error case
if (aTmpItem)
GlobalDeleteAtom (aTmpItem);
wFreePokeData (m_pDocChannel, m_bOldSvr && m_aClass==aMSDraw);
hr = RPC_E_DDE_POST;
intrDebugOut((DEB_ITRACE,"::Poke(%x)wPostMessage returns %x\n",this,hr));
return hr;
}
INTERNAL CDdeObject::PostSysCommand
(LPDDE_CHANNEL pChannel,
LPCSTR szCmd,
BOOL fStdNew,
BOOL fWait)
{
intrDebugOut((DEB_ITRACE,
"CDdeObject::PostSysCommand(%x,szCmd=%s,fStdNew=%x,fWait=%x)\n",
this,
szCmd,
fStdNew,
fWait));
ULONG size;
WORD len;
LPSTR lpdata= NULL;
HANDLE hdata = NULL;
HRESULT hresult;
#define LN_FUDGE 16 // [],(), 3 * 3 (2 double quotes and comma)
len = strlen (szCmd);
// for StdNewDocument command add class name
if (fStdNew)
len += wAtomLenA (m_aClass);
// Now add the document length.
len += wAtomLenA (m_aTopic);
// now add the fudge factor for the Quotes etc.
len += LN_FUDGE;
// allocate the buffer and set the command.
if (!(hdata = GlobalAlloc (GMEM_DDESHARE, size = len)))
return ReportResult(0, E_OUTOFMEMORY, 0, 0);
if (!(lpdata = (LPSTR)GlobalLock (hdata))) {
Assert (0);
GlobalFree (hdata);
return ReportResult(0, E_OUTOFMEMORY, 0, 0);
}
strcpy (lpdata, (LPSTR)"["); // [
strcat (lpdata, szCmd); // [StdCmd
if (strcmp (szCmd, "StdExit"))
{
strcat (lpdata, "(\""); // [StdCmd("
if (fStdNew)
{
len = strlen (lpdata);
GlobalGetAtomNameA (m_aClass, (LPSTR)lpdata + len, size-len);
// [StdCmd("class
strcat (lpdata, "\",\""); // [StdCmd("class","
}
len = strlen (lpdata);
// now get the topic name.
GlobalGetAtomNameA (m_aTopic, lpdata + len, (WORD)size - len);
// [StdCmd("class","topic
strcat (lpdata, "\")"); // [StdCmd("class","topic")
}
strcat (lpdata, "]");
Assert (strlen(lpdata) < size);
intrDebugOut((DEB_ITRACE,"::PostSysCommand(%x) hData(%s)\n",this,lpdata));
GlobalUnlock (hdata);
// return Execute (m_pSysChannel, hdata, /*fStdClose*/FALSE, fWait);
// REVIEW: this fixed bug 1856 (johannp)
// JasonFul - does it break something else?
hresult = Execute (m_pSysChannel,
hdata,
/*fStdClose*/FALSE,
fWait,
/*fDetectTerminate*/ TRUE);
intrDebugOut((DEB_ITRACE,"::PostSysCommand(%x) returns \n",this,hresult));
return hresult;
}
//+---------------------------------------------------------------------------
//
// Method: CDdeObject::KeepData
//
// Synopsis: Given the DDEDATA structure from a WM_DDE_DATA message, extract
// the real data and keep it till GetData or Save is done.
//
//
// Effects:
//
// Arguments: [pChannel] --
// [hDdeData] --
//
// Requires:
//
// Returns: E_OUTOFMEMORY or E_HANDLE if failure, NOERROR if success
//
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 5-14-94 kevinro Commented
//
// Notes:
//
//----------------------------------------------------------------------------
INTERNAL CDdeObject::KeepData
(LPDDE_CHANNEL pChannel, HANDLE hDdeData)
{
intrDebugOut((DEB_ITRACE,"CDdeObject::KeepData(%x)\n",this));
DDEDATA * lpDdeData = NULL;
HANDLE hData;
CLIPFORMAT cfFormat;
if (!(lpDdeData = (DDEDATA *) (GlobalLock (hDdeData))))
{
return E_OUTOFMEMORY;;
}
cfFormat = lpDdeData->cfFormat;
intrDebugOut((DEB_ITRACE,
"::KeepData(%x) Keeping cfFormat=%x\n",
this,
cfFormat));
GlobalUnlock (hDdeData);
// Possible Side effect of wHandleFromDdeData() is the freeing of hDdeData
if (!(hData = wHandleFromDdeData (hDdeData))
|| !wIsValidHandle (hData, cfFormat) )
{
Assert(0);
return ReportResult(0, E_OUTOFMEMORY, 0, 0);
}
if (cfFormat == g_cfNative) {
if (m_hNative)
GlobalFree (m_hNative);
// Keep the native data
RetErr (wTransferHandle (&m_hNative, &hData, cfFormat));
}
else if (cfFormat == CF_METAFILEPICT ||
cfFormat == CF_BITMAP ||
cfFormat == CF_DIB)
{
if (m_hPict)
wFreeData (m_hPict, m_cfPict, TRUE);
m_cfPict = cfFormat;
// Keep the presentation data
RetErr (wTransferHandle (&m_hPict, &hData, cfFormat));
#ifdef OLD
// Remember size of picture so we can return
// a reasonable answer for GetExtent
if (cfFormat == CF_METAFILEPICT)
{
LPMETAFILEPICT lpMfp = (LPMETAFILEPICT) GlobalLock (m_hPict);
if (NULL==lpMfp)
return E_HANDLE;
UpdateExtent (m_cxContentExtent, lpMfp->xExt);
UpdateExtent (m_cyContentExtent, lpMfp->yExt);
GlobalUnlock (m_hPict);
}
else if (cfFormat==CF_BITMAP)
{
BITMAP bm;
if (0==GetObject (m_hPict, sizeof(BITMAP), (LPVOID) &bm))
return E_HANDLE;
UpdateExtent (m_cxContentExtent,
wPixelsToHiMetric (bm.bmWidth, giPpliX));
UpdateExtent (m_cyContentExtent,
wPixelsToHiMetric (bm.bmHeight,giPpliY));
}
else if (cfFormat==CF_DIB)
{
BITMAPINFOHEADER * pbminfohdr;
pbminfohdr = (BITMAPINFOHEADER *) GlobalLock (m_hPict);
if (NULL==pbminfohdr)
return E_HANDLE;
UpdateExtent (m_cxContentExtent,
wPixelsToHiMetric (pbminfohdr->biWidth, giPpliX));
UpdateExtent (m_cyContentExtent,
wPixelsToHiMetric (pbminfohdr->biHeight,giPpliY));
GlobalUnlock (m_hPict);
}
#endif
}
else
{
if (m_hExtra)
wFreeData (m_hExtra, m_cfExtra, TRUE);
m_cfExtra = cfFormat;
wTransferHandle (&m_hExtra, &hData, cfFormat);
}
return NOERROR;
}
// IsFormatAvailable
//
// Does a temporary DDE_REQUEST to see if server supports a format
// Returns NOERROR if format is available.
//
INTERNAL CDdeObject::IsFormatAvailable
(LPFORMATETC pformatetc)
{
intrDebugOut((DEB_ITRACE,"CDdeObject::IsFormatAvailable(%x)\n",this));
ATOM aItem=(ATOM)0;
HRESULT hresult;
Puts ("DdeObject::IsFormatAvailable\n");
if (!HasValidLINDEX(pformatetc))
{
intrDebugOut((DEB_IERROR, "\t!HasValidLINDEX(pformatetc)\n"));
return(DV_E_LINDEX);
}
if (0==pformatetc->cfFormat)
return ResultFromScode (E_INVALIDARG);
if (pformatetc->dwAspect & DVASPECT_ICON)
{
if (pformatetc->cfFormat==CF_METAFILEPICT)
{
// This is always available. we get it from the exe.
return NOERROR;
}
// an icon must be a metafile
return ResultFromScode (S_FALSE);
}
if (!(pformatetc->dwAspect & (DVASPECT_CONTENT | DVASPECT_DOCPRINT)))
{
// 1.0 does not support Thumb.
return ReportResult(0, S_FALSE, 0, 0);
}
if (NOERROR == (hresult=m_ConnectionTable.Lookup (pformatetc->cfFormat, NULL)))
{
// We already got a call to DataObject::Advise on this format,
// so it must be available.
Puts ("DataObject::Advise had been done on this format.\n");
return NOERROR;
}
else
{
// Lookup () didn't find this format.
ErrZ (GetScode(hresult)==S_FALSE);
}
intrAssert(wIsValidAtom(m_aItem));
aItem = wDupAtom (m_aItem);
intrAssert(wIsValidAtom(aItem));
LPARAM lp;
if(!wPostMessageToServer (m_pDocChannel, WM_DDE_REQUEST,
lp = MAKE_DDE_LPARAM (WM_DDE_REQUEST,pformatetc->cfFormat,aItem),TRUE))
{
return(RPC_E_DDE_POST);
}
if (NOERROR==WaitForReply (m_pDocChannel, AA_REQUESTAVAILABLE))
return NOERROR;
// Last ditch effort: Advise
if (NOERROR== AdviseOn (pformatetc->cfFormat, ON_SAVE))
{
// We cannot Unadvise because an OLE 1.0 bug
// terminates DDE advise connections for ALL formats.
//// UnAdviseOn (pformatetc->cfFormat, ON_SAVE);
// Instead, just remember we did this advise.
m_ConnectionTable.Add (0, pformatetc->cfFormat, ADVFDDE_ONSAVE);
return NOERROR;
}
return ResultFromScode (S_FALSE);
errRtn:
AssertSz (0, "Error in CDdeObject::IsFormatAvailable");
Puth (hresult); Putn();
if (aItem)
GlobalDeleteAtom (aItem);
return hresult;
}
INTERNAL CDdeObject::ChangeTopic
(LPSTR lpszTopic)
{
intrDebugOut((DEB_ITRACE,"CDdeObject::ChangeTopic(%x,lpszTopic=%s)\n",this,lpszTopic));
HRESULT hresult;
LPMONIKER pmkFile=NULL;
LPMONIKER pmkItem=NULL;
LPMONIKER pmkComp=NULL;
LPMONIKER pmkNewName=NULL;
ATOM aTopic = wGlobalAddAtomA (lpszTopic);
intrAssert(wIsValidAtom(aTopic));
// Yet-Another-Excel-Hack
// Excel 4.0 sends StdDocumentName every time it saves,
// whether or not the file name has actually changed. Bug 2957
if (aTopic != m_aTopic)
{
ErrRtnH (CreateOle1FileMoniker (wAtomName(aTopic), m_clsid, &pmkFile));
if (m_aItem)
{
intrAssert (wIsValidAtom (m_aItem));
ErrRtnH (CreateItemMoniker (OLESTR("!"), wAtomName (m_aItem), &pmkItem));
ErrRtnH (CreateGenericComposite (pmkFile, pmkItem, &pmkComp));
(pmkNewName = pmkComp)->AddRef();
}
else
{
(pmkNewName = pmkFile)->AddRef();
}
RetZS (m_pOleAdvHolder, E_OUTOFMEMORY);
RetZ (pmkNewName);
ErrRtnH (m_pOleAdvHolder->SendOnRename (pmkNewName));
}
SetTopic (aTopic);
hresult = NOERROR;
errRtn:
if (pmkFile)
pmkFile->Release();
if (pmkItem)
pmkItem->Release();
if (pmkComp)
pmkComp->Release();
if (pmkNewName)
pmkNewName->Release();
return hresult;
}
//+---------------------------------------------------------------------------
//
// Method: CDdeObject::ChangeItem
//
// Synopsis: Changes the m_aItem atom, using an Ansi string
//
// Effects:
//
// Arguments: [szItem] -- Ansi string for the new item
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 5-12-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
INTERNAL_(void) CDdeObject::ChangeItem
(LPSTR szItem)
{
intrDebugOut((DEB_ITRACE,"CDdeObject::ChangeItem(%x,szItem=%s)\n",this,szItem));
intrAssert(wIsValidAtom(m_aItem));
if (m_aItem)
GlobalDeleteAtom (m_aItem);
m_aItem = wGlobalAddAtomA (szItem);
intrAssert(wIsValidAtom(m_aItem));
}
INTERNAL CDdeObject::DeclareVisibility
(BOOL f,
BOOL fCallOnShowIfNec)
{
intrDebugOut((DEB_ITRACE,"CDdeObject::DelcareVisibility(%x)\n",this));
if (f)
m_fWasEverVisible = TRUE;
if ((f && (!m_fVisible || !m_fCalledOnShow)) ||
(!f && m_fVisible))
{
if (m_pOleClientSite && fCallOnShowIfNec && m_clsid != CLSID_Package)
{
m_pOleClientSite->OnShowWindow (f);
m_fCalledOnShow = f;
}
m_fVisible = f;
}
return NOERROR;
}
INTERNAL CDdeObject::Update
(BOOL fRequirePresentation)
{
intrDebugOut((DEB_ITRACE,
"CDdeObject::Update(%x,fRequiredPresentation=%x)\n",
this,
fRequirePresentation));
// Get latest data
// OLE 1.0 spec says servers must supply metafile format.
HRESULT hresult = RequestData (m_cfPict ? m_cfPict : CF_METAFILEPICT);
if (fRequirePresentation && hresult!=NOERROR)
return hresult;
RetErr (RequestData (g_cfNative));
SendOnDataChange (ON_CHANGE);
return NOERROR;
}
INTERNAL CDdeObject::Save
(LPSTORAGE pstg)
{
intrDebugOut((DEB_ITRACE,"CDdeObject::Save(%x)\n",this));
VDATEIFACE (pstg);
#ifdef OLE1INTEROP
RetErr (StSave10NativeData (pstg, m_hNative, m_fOle1interop));
#else
RetErr (StSave10NativeData (pstg, m_hNative, FALSE));
#endif
if (m_aItem)
{
intrAssert(wIsValidAtom(m_aItem));
RetErr (StSave10ItemName (pstg, wAtomNameA (m_aItem)));
}
RetErr (wWriteFmtUserType (pstg, m_clsid));
return NOERROR;
}
/*
* IMPLEMENTATION of CUnknownImpl
*
*/
STDMETHODIMP_(ULONG) NC(CDdeObject,CUnknownImpl)::AddRef()
{
ChkD(m_pDdeObject);
return InterlockedAddRef(&(m_pDdeObject->m_refs));
}
STDMETHODIMP_(ULONG) NC(CDdeObject,CUnknownImpl)::Release()
{
ChkD(m_pDdeObject);
Assert (m_pDdeObject->m_refs != 0);
ULONG ul;
if ((ul=InterlockedRelease(&(m_pDdeObject->m_refs))) == 0) {
m_pDdeObject->m_ProxyMgr.Disconnect();
#ifdef _CHICAGO_
//Note:POSTPPC
// the object can not be delete if guarded
// which is the case if DelayDelete state is 'DelayIt'
//
if (m_pDdeObject->_DelayDelete == DelayIt)
{
// set the state to ReadyToDelete and
// the object will be deleted at the
// UnGuard call
m_pDdeObject->_DelayDelete = ReadyToDelete;
intrDebugOut((DEB_IWARN ,"Can not release CDdeObject\n"));
return 1;
}
#endif //_CHICAGO_
delete m_pDdeObject;
return 0;
}
return ul;
}
STDMETHODIMP NC(CDdeObject,CUnknownImpl)::QueryInterface(REFIID iid, LPLPVOID ppv)
{
ChkD(m_pDdeObject);
if (iid == IID_IUnknown) {
*ppv = (void FAR *)&m_pDdeObject->m_Unknown;
AddRef();
return NOERROR;
}
else if (iid == IID_IOleObject)
*ppv = (void FAR *) &(m_pDdeObject->m_Ole);
else if (iid == IID_IDataObject)
*ppv = (void FAR *) &(m_pDdeObject->m_Data);
else if (iid == IID_IPersist || iid == IID_IPersistStorage)
*ppv = (void FAR *) &(m_pDdeObject->m_PersistStg);
else if (iid == IID_IProxyManager)
*ppv = (void FAR *) &(m_pDdeObject->m_ProxyMgr);
else if (iid == IID_IOleItemContainer
|| iid == IID_IOleContainer
|| iid == IID_IParseDisplayName)
*ppv = (void FAR *) &(m_pDdeObject->m_OleItemContainer);
else {
Puts ("INTERFACE NOT FOUND \r\n");
*ppv = NULL;
return ReportResult(0, E_NOINTERFACE, 0, 0);
}
m_pDdeObject->m_pUnkOuter->AddRef();
return NOERROR;
}