/***************************************************************************\
* Module Name: Doc.c Document Main module
*
* Purpose: Includes All the document level object communication related.
*
* Created: Oct 1990.
*
* Copyright (c) 1990, 1991 Microsoft Corporation
*
* History:
* Raor (../10/1990) Designed, coded (modified for 2.0)
*
\***************************************************************************/
#include "ole2int.h"
//#include "cmacs.h"
#include <dde.h>
#include "srvr.h"
#include "ddedebug.h"
#include "valid.h"
ASSERTDATA
extern ATOM aStdClose;
extern ATOM aStdShowItem;
extern ATOM aStdDoVerbItem;
extern ATOM aStdDocName;
extern ATOM aTrue;
extern ATOM aFalse;
extern HANDLE hddeRename;
extern HWND hwndRename;
#ifdef _CHICAGO_
#define szDDEViewObj "DDE ViewObj"
#else
#define szDDEViewObj L"DDE ViewObj"
#endif
// HRESULT DdeHandleIncomingCall(HWND hwndCli, WORD wCallType);
//
// Call CoHandleIncomingCall which will call the message filter
// Note: only call this functions with:
// - CALLTYPE_TOPLEVEL ... for synchranous calls
// - CALLTYPE_ASYNC ... for async calls
//
HRESULT DdeHandleIncomingCall(HWND hwndCli, WORD wCallType)
{
Assert(!"DdeHandleIncomingCall not implemented");
return(RPC_E_CALL_REJECTED);
// Review: bug: get the correcr hwnd
switch ( /* CoHandleIncomingCall(hwndCli, wCallType, NULL) */ wCallType) {
default:
case SERVERCALL_ISHANDLED: // call can be proccesed
return NOERROR;
case SERVERCALL_REJECTED: // call rejected
return ResultFromScode(RPC_E_CALL_REJECTED);
case SERVERCALL_RETRYLATER: // call should be retried later
return ResultFromScode(RPC_E_DDE_BUSY);
}
}
INTERNAL CDefClient::Create
(
LPSRVR lhOLESERVER,
LPUNKNOWN lpunkObj,
LPOLESTR lpdocName,
const BOOL fSetClientSite,
const BOOL fDoAdvise,
const BOOL fRunningInSDI, // optional
HWND FAR* phwnd // optional
)
{
LPSRVR lpsrvr = NULL;
LPCLIENT lpclient = NULL;
HANDLE hclient = NULL;
HRESULT hresult = NOERROR;
intrDebugOut((DEB_ITRACE,
"0 _IN CDefClient::Create(lpsrvr=%x,lpdocName=%ws)\n",
lhOLESERVER,WIDECHECK(lpdocName)));
// REVIEW: server's termination has already started. Are
// we going to see this condition in the synchronous mode.
lpsrvr = (LPSRVR)lhOLESERVER;
if (lpsrvr && lpsrvr->m_bTerminate)
{
Assert(0);
return ReportResult(0, RPC_E_DDE_REVOKE, 0, 0);
}
#ifdef FIREWALLS
PROBE_READ(lpunkObj);
PROBE_READ(lpmkObj);
PROBE_WRITE(lplhobj);
#endif
lpclient = new CDefClient (/*pUnkOuter==*/NULL);;
Assert(lpclient->m_pUnkOuter);
lpclient->m_aItem = wGlobalAddAtom (/*lpszObjName*/lpdocName);
lpclient->m_fRunningInSDI = fRunningInSDI;
lpclient->m_psrvrParent = lpsrvr;
// A doc has itself as its containing document
lpclient->m_pdoc = lpclient;
ErrRtnH (lpunkObj->QueryInterface (IID_IOleObject,
(LPLPVOID) &lpclient->m_lpoleObj));
ErrRtnH (lpunkObj->QueryInterface (IID_IDataObject,
(LPLPVOID) &lpclient->m_lpdataObj));
// Lock object; do after the QI so that ReleaseObjPtrs will unlock correctly
lpclient->m_fLocked =
(NOERROR==CoLockObjectExternal (lpunkObj, TRUE, /*dont care*/ FALSE));
if (!(lpclient->m_hwnd = DdeCreateWindowEx(0, gOleWindowClass,szDDEViewObj,
WS_CHILD,0,0,0,0,lpsrvr->m_hwnd,NULL, g_hinst, NULL)))
{
intrDebugOut((DEB_ITRACE,"CDefClient::Create() couldn't create window\n"));
goto errRtn;
}
// fix up the WindowProc entry point.
SetWindowLong(lpclient->m_hwnd, GWL_WNDPROC, (LONG)DocWndProc);
if (fDoAdvise)
{
// This is for Packager, in particular, and manual links.
// If client does not advise on any data, we still need
// to do an OLE advise so we can get OnClose notifications.
ErrRtnH (lpclient->DoOle20Advise (OLE_CLOSED, (CLIPFORMAT)0));
}
intrDebugOut((DEB_ITRACE," Doc window %x created\n",lpclient->m_hwnd));
// Set out parm (window)
if (phwnd != NULL)
{
*phwnd = lpclient->m_hwnd;
}
if (fSetClientSite)
{
// Should not set the client site if the object has not been
// initialized yet, by BindMoniker (i.e. PersistFile::Load)
// or PersistStorage::Load
if (lpclient->SetClientSite() != NOERROR)
{
goto errRtn;
}
}
Putsi(lpclient->m_cRef);
SetWindowLong (lpclient->m_hwnd, 0, (LONG)lpclient);
SetWindowWord (lpclient->m_hwnd, WW_LE, WC_LE);
SetWindowLong (lpclient->m_hwnd,WW_HANDLE,
(GetWindowLong (lpsrvr->m_hwnd, WW_HANDLE)));
hresult = NOERROR;
exitRtn:
intrDebugOut((DEB_ITRACE,
"0 _OUT CDefClient::Create(lpsrvr=%x,lpdocName=%ws) hr=%x\n",
lhOLESERVER,
WIDECHECK(lpdocName),
hresult));
return(hresult);
errRtn:
intrDebugOut((DEB_ITRACE,"CDefClient::Create() in error handling routine\n"));
if (lpclient)
{
if (lpclient->m_hwnd)
SSDestroyWindow (lpclient->m_hwnd);
if (lpclient->m_aItem)
GlobalDeleteAtom (lpclient->m_aItem);
delete lpclient;
}
hresult = E_OUTOFMEMORY;
goto exitRtn;
}
INTERNAL CDefClient::Revoke (BOOL fRelease)
{
Puts ("DefClient::Revoke "); Puth(this); Puta(m_aItem); Putn();
ChkC(this);
ReleaseObjPtrs();
// We are done with this CDefClient but someone may still have a reference
// to an instance of one of our nested classes. In particular, an advise
// holder may be holding on to our sink, or an item may have a pointer
// to us if we are its parent document. So we cannot actually do a
// "delete this".
// The corresponding AddRef is in CDefClient::Create
// or CDefClient::RegisterItem
m_pUnkOuter->Release();
Puts ("DefClient::Revoke done\r\n");
return NOERROR;
}
INTERNAL CDefClient::ReleaseObjPtrs
(void)
{
intrDebugOut((DEB_ITRACE,
"%p _IN CDefClient::ReleaseObjPtrs\n",
this));
ULONG ulResult;
if (m_lpoleObj && m_fLocked)
{
// Unlock object. Set m_fLocked to FALSE first to prevent reentrant
// problems (unlock causes close which causes this routine to be called)
m_fLocked = FALSE;
CoLockObjectExternal(m_lpoleObj, FALSE, TRUE);
}
if (m_lpoleObj)
{
if (m_fDidSetClientSite)
m_lpoleObj->SetClientSite(NULL);
DoOle20UnAdviseAll();
Assert (m_lpoleObj);
if (m_lpoleObj)
{
ulResult = m_lpoleObj->Release();
m_lpoleObj = NULL;
}
intrDebugOut((DEB_ITRACE,
"%p _OUT ::ReleaseObjPtrs lpoleObj ulResult=%x\n",
this,ulResult));
}
if (m_lpdataObj)
{
// Must do it this way because the Release can cause recursion
LPDATAOBJECT pdata = m_lpdataObj;
m_lpdataObj = NULL;
ulResult = pdata->Release();
intrDebugOut((DEB_ITRACE,
"%p _OUT ::ReleaseObjPtrs pdata ulResult=%x\n",
this,ulResult));
}
intrDebugOut((DEB_ITRACE,
"%p _OUT CDefClient::ReleaseObjPtrs\n",
this));
return NOERROR;
}
#if 000
//RevokeAllDocs : revokes all the doc objects attached to a given
//server.
INTERNAL_(HRESULT) CDDEServer::RevokeAllDocObjs ()
{
HWND hwnd;
HWND hwndnext;
LPCLIENT lpclient;
Puts ("RevokeAllDocObjs\r\n");
ChkS(this);
hwnd = GetWindow (m_hwnd, GW_CHILD);
// Go thru each of the child windows and revoke the corresponding
// document. Doc windows are child windows for the server window.
while (hwnd){
// sequence is important
hwndnext = GetWindow (hwnd, GW_HWNDNEXT);
lpclient = ((LPCLIENT)GetWindowLong (hwnd, 0));
lpclient->Revoke();
hwnd = hwndnext;
}
return NOERROR;
}
#endif
// FindDoc: Given a doc obj, searches for the doc obj
// in the given class factory tree. returns true if the
// doc obj is available.
INTERNAL_(LPCLIENT) CDDEServer::FindDocObj
(
LPSTR lpdocname
)
{
ATOM aItem;
HWND hwnd;
LPCLIENT lpclient;
ChkS(this);
aItem = (ATOM)GlobalFindAtomA (lpdocname);
Assert (IsWindowValid (m_hwnd));
hwnd = GetWindow (m_hwnd, GW_CHILD);
Assert (NULL==hwnd || IsWindowValid (hwnd));
while (hwnd)
{
lpclient = (LPCLIENT)GetWindowLong (hwnd, 0);
if (lpclient->m_aItem == aItem)
{
intrDebugOut((DEB_ITRACE,
"FindDocObj found %s lpclient=%x\n",
lpdocname,
lpclient));
return lpclient;
}
hwnd = GetWindow (hwnd, GW_HWNDNEXT);
}
return NULL;
}
BOOL PostAckToClient(HWND hwndClient,HWND hwndServer,ATOM aItem,DWORD retval)
{
HRESULT hr = TRUE;
intrDebugOut((DEB_ITRACE,
"0 _IN PostAckToClient(hwndClient=%x,hwndServer=%x,aItem=%x(%ws)\n",
hwndClient,
hwndServer,
aItem,
wAtomName(aItem)));
DWORD status = 0;
SET_MSG_STATUS (retval, status);
LPARAM lp = MAKE_DDE_LPARAM(WM_DDE_ACK,status,aItem);
if (!PostMessageToClient (hwndClient,
WM_DDE_ACK,
(UINT)hwndServer,
lp))
{
DDEFREE(WM_DDE_ACK,lp);
hr = FALSE;
}
intrDebugOut((DEB_ITRACE,
"0 _OUT PostAckToClient returns %x\n",
hr));
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: DocHandleIncomingCall
//
// Synopsis: Setup and call the CallControl to dispatch a call to the doc
//
// Effects: A call has been made from the client that requires us to call
// into our server. This must be routed through the call control.
// This routine sets up the appropriate data structures, and
// calls into the CallControl. The CallControl will in turn
// call DocDispatchIncomingCall to actually process the call.
//
// This routine should only be called by the DocWndProc
//
//
// Arguments: [pDocData] -- Points to DOCDISPATCHDATA for this call
// This contains all required information for
// handling the message.
//
// Requires:
//
// Returns: If an error is returned, it is assumed that the Dispatch
// routine was not reached. DocDispatchIncomingCall() should
// not be returning an error.
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 6-05-94 kevinro Commented/cleaned
//
// Notes:
//
// This is a co-routine with DocDispatchIncomingCall. See that routine
// for more details
//
//----------------------------------------------------------------------------
INTERNAL DocHandleIncomingCall(PDOCDISPATCHDATA pDocData)
{
HRESULT hresult = NOERROR;
DISPATCHDATA dispatchdata;
DWORD callcat;
intrAssert(pDocData != NULL);
intrDebugOut((DEB_ITRACE,
"0 _IN DocHandleIncomingCall lpclient=%x pDocData=%x\n",
pDocData->lpclient,
pDocData));
//
// TERMINATE messages must always be handled ASYNC, and cannot
// be rejected.
//
if (pDocData->msg == WM_DDE_TERMINATE)
{
callcat = CALLCAT_ASYNC;
}
else
{
callcat = CALLCAT_SYNCHRONOUS;
}
dispatchdata.pData = (LPVOID) pDocData;
pDocData->wDispFunc = DDE_DISP_DOCWNDPROC;
RPCOLEMESSAGE rpcMsg;
RPC_SERVER_INTERFACE RpcInterfaceInfo;
DWORD dwFault;
rpcMsg.iMethod = 0;
rpcMsg.Buffer = &dispatchdata;
rpcMsg.cbBuffer = sizeof(dispatchdata);
rpcMsg.reserved2[1] = &RpcInterfaceInfo;
*MSG_TO_IIDPTR(&rpcMsg) = GUID_NULL;
IRpcStubBuffer * pStub = &(pDocData->lpclient->m_pCallMgr);
IRpcChannelBuffer * pChannel = &(pDocData->lpclient->m_pCallMgr);
hresult = STAInvoke(&rpcMsg, callcat, pStub, pChannel, NULL, &dwFault);
intrDebugOut((DEB_ITRACE,
"0 _OUT DocHandleIncomingCall hresult=%x\n",
hresult));
return(hresult);
}
//+---------------------------------------------------------------------------
//
// Function: DocDispatchIncomingCall
//
// Synopsis: Dispatch a call into the client.
//
// Effects: This is a co-routine to DocHandleIncomingCall. This routine
// is called to implement functions that call into the CDefClient
// object. It is dispatched by the call control stuff, and helps
// to insure the server is ready to accept these calls.
//
// This routine is coded as a continuation of the DocWndProc.
// There is a switch on the message. Each message does slightly
// different things. The code to handle each message was snatched
// from the original DocWndProc, before it was converted to the
// new call control mechanism.
//
//
// Arguments: [pDocData] -- Points to Doc Dispatch Data, which are the
// parameters to DocWndProc which need processing
//
// Requires:
// pDocData cannot be NULL
//
// Returns:
// This routine will always return NOERROR. There are no useful
// error returns to be made from here. If you decide to return
// an error, check the DocHandleIncomingCall and DocWndProc to be
// sure the error paths are correctly handled. Some of the
// cases do additional error processing in the DocWndProc on
// error.
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 6-05-94 kevinro Commented/cleaned
//
// Notes:
//
//----------------------------------------------------------------------------
INTERNAL DocDispatchIncomingCall(PDOCDISPATCHDATA pDocData)
{
LPCLIENT lpclient = pDocData->lpclient;
BOOL fack;
HANDLE hdata = pDocData->hdata;
ATOM aItem = pDocData->aItem;
WPARAM wParam = pDocData->wParam;
HWND hwnd = pDocData->hwnd;
HRESULT retval;
intrDebugOut((DEB_ITRACE,
"0 _IN DocDispatchIncomingCall pDocData(%x)\n",
pDocData));
intrAssert(pDocData);
//
// This switch statement is intended to continue the functionality found
// in the DocWndProc. These routines are directly interrelated to the
// same cases in DocWndProc, and need special care before changing.
//
switch (pDocData->msg)
{
case WM_DDE_TERMINATE:
{
intrDebugOut((DEB_ITRACE,
"DDIC: WM_DDE_TERMINATE hwnd=%x \n",
pDocData->hwnd));
//
// Here is a fine hack for you. 32-bit applications appear to shut
// down slightly differently than 16-bit applications. There is a
// problem with Lotus Notes interacting with 32-bit OFFICE apps.
// When notes has done an OleCreateFromFile, it starts the
// 32-bit hidden, and does a normal DDE conversation. However,
// on termination, the 32-bit Doc Window was going away during
// this routine. Since the world is now multi-tasking, Lotus
// Notes was seeing its server window die because we get
// pre-empted during DeleteItemsFromList giving the 16-bit app a
// chance to run. Since Lotus is in the
// standard OleQueryReleaseStatus() loop, it will detect that the
// server has shutdown BEFORE it gets the terminate message.
// Go figure. So, insure that we post the reply DDE_TERMINATE
// before destroying our window, or the 16-bit applications
// won't like us.
//
PostMessageToClient ((HWND)wParam, WM_DDE_TERMINATE,
(UINT) hwnd, NULL);
// Client initiated the termination. So, we should remove
// his window from any of our doc or items' lists.
lpclient->DeleteFromItemsList ((HWND)wParam
/*, lpclient->m_fEmbed*/);
ChkC (lpclient);
// REVIEW: If the termination is sent from the client side,
// lpoleObj will not be NULL.
if (lpclient->m_cClients == 0 && lpclient->m_fEmbed)
{
Assert (lpclient->m_chk==chkDefClient);
lpclient->ReleaseAllItems ();
Assert (lpclient->m_chk==chkDefClient);
Assert (NULL==lpclient->m_lpoleObj &&
NULL==lpclient->m_lpdataObj) ;
}
break;
}
case WM_DDE_EXECUTE:
{
intrDebugOut((DEB_ITRACE,
"DDIC: WM_DDE_EXECUTE hwnd=%x hdata=%x\n",
pDocData->hwnd,
pDocData->hdata));
//
// The following state variables appear to be used by
// the execute code.
//
lpclient->m_ExecuteAck.f = TRUE; // assume we will need to
lpclient->m_ExecuteAck.hdata = hdata;
lpclient->m_ExecuteAck.hwndFrom = (HWND)wParam;
lpclient->m_ExecuteAck.hwndTo = hwnd;
//
// In the event that the command is a StdClose, we need
// to force the client to stay around for the duration of
// the call.
//
lpclient->m_pUnkOuter->AddRef();
retval = lpclient->DocExecute (hdata);
if (lpclient->m_ExecuteAck.f)
{
lpclient->SendExecuteAck (retval);
}
lpclient->m_pUnkOuter->Release();
break;
}
case WM_DDE_POKE:
{
int iStdItem;
intrDebugOut((DEB_ITRACE,
"DDIC: WM_DDE_POKE hwnd=%x aItem=%x(%ws) hdata=%x\n",
hwnd,
aItem,
wAtomName(aItem),
hdata));
if (iStdItem = GetStdItemIndex (aItem))
{
retval = lpclient->PokeStdItems ((HWND)wParam,
aItem,
hdata,
iStdItem);
}
else
{
retval = lpclient->PokeData ((HWND)wParam,
aItem,
hdata);
// This is allowed to fail. PowerPoint tries to poke
// data with cfFormat=="StdSave" and "StdFont"
}
if (!PostAckToClient((HWND)wParam,hwnd,aItem,retval))
{
goto errRtn;
}
break;
}
case WM_DDE_ADVISE:
{
intrDebugOut((DEB_ITRACE,
"DDIC: WM_DDE_ADVISE hwnd=%x aItem=%x(%ws) hdata=%x\n",
hwnd,
aItem,
wAtomName(aItem),
hdata));
if (IsAdviseStdItems (aItem))
{
retval = lpclient->AdviseStdItems ((HWND)wParam,
aItem,
hdata,
(BOOL FAR *)&fack);
}
else
{
retval = lpclient->AdviseData ((HWND)wParam,
aItem,
hdata,
(BOOL FAR *)&fack);
}
if (fack)
{
if (!PostAckToClient((HWND)wParam,hwnd,aItem,retval))
{
GlobalFree(hdata);
}
}
else if (aItem != NULL)
{
GlobalDeleteAtom (aItem);
}
break;
}
case WM_DDE_UNADVISE:
{
intrDebugOut((DEB_ITRACE,
"DDIC: WM_DDE_UNADVISE hwnd=%x aItem=%x(%ws)\n",
hwnd,
aItem,
wAtomName(aItem)));
retval = lpclient->UnAdviseData ((HWND)wParam, aItem);
if (!PostAckToClient((HWND)wParam,hwnd,aItem,retval))
{
goto errRtn;
}
break;
}
case WM_DDE_REQUEST:
{
intrDebugOut((DEB_ITRACE,
"DDIC: WM_DDE_REQUEST hwnd=%x aItem=%x(%ws) cfFormat=%x\n",
hwnd,
aItem,
wAtomName(aItem),
(USHORT)LOWORD(pDocData->lParam)));
retval = lpclient->RequestData ((HWND)wParam,
aItem,
LOWORD(pDocData->lParam),
(HANDLE FAR *)&hdata);
if (retval == NOERROR)
{
// post the data message and we are not asking for any
// acknowledge.
intrDebugOut((DEB_ITRACE,
"DDIC: posting WM_DDE_DATA\n"));
LPARAM lp = MAKE_DDE_LPARAM(WM_DDE_DATA,hdata,aItem);
if (!PostMessageToClient ((HWND)wParam, WM_DDE_DATA, (UINT) hwnd,lp))
{
// hdata will be freed by the client because fRelease
// was set to true by MakeDdeData (called by RequestData)
DDEFREE(WM_DDE_DATA,lp);
goto errRtn;
}
}
else
{
if (!PostAckToClient((HWND)wParam,hwnd,aItem,retval))
{
goto errRtn;
}
}
break;
}
default:
//
// Whoops, this is very bad. We should never get here.
//
intrAssert(!"Unknown MSG in DocWndProc: Very Bad indeed");
}
exitRtn:
intrDebugOut((DEB_ITRACE,
"0 _OUT DocDispatchIncomingCall pDocData(%x) hr = 0\n",
pDocData));
return(NOERROR);
errRtn:
intrDebugOut((DEB_IERROR,
"***** ERROR DDIC pDocData(%x) Post ACK failed. \n",
pDocData));
if (aItem != NULL)
{
GlobalDeleteAtom (aItem);
}
goto exitRtn;
}
//+---------------------------------------------------------------------------
//
// Function: DocWndProc
//
// Synopsis: Document Window Procedure
//
// Effects: Processes DDE messages for a document window.
//
// Arguments: [hwnd] --
// [msg] --
// [wParam] --
// [lParam] --
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 6-05-94 kevinro Commented/cleaned
//
// Notes:
//
// When running in a VDM, it is possible that this window was dispatched
// without having a full window handle. This happens when the getmessage
// was dispatched from 16-bit. Therefore, we need to convert the hwnd to
// a full hwnd before doing any comparision functions.
//
//----------------------------------------------------------------------------
STDAPI_(LRESULT) DocWndProc (
HWND hwndIn,
UINT msg,
WPARAM wParam,
LPARAM lParam
)
{
//
// Since this is the DocWndProc, we aren't going to initialize
// any of the local variables. This function is called as a WNDPROC,
// which is pretty often.
//
DOCDISPATCHDATA docData;
LPCLIENT lpclient;
WORD status=0;
HANDLE hdata;
ATOM aItem;
HRESULT retval;
LPOLEOBJECT lpoleObj;
HWND hwnd;
DebugOnly (HWND hwndClient;)
// REVIEW: We need to take care of the bug, related to
// Excel. Excel does the sys level connection and sends
// terminates immediately before making the connection
// to the doc level. If we send release to classfactory
// app may revoke the classfactory.
// REVIEW: It may not be necessary to do the blocking.
// ReVIEW: For fixing the ref count right way for
// CreateInstance Vs. WM_DDE_INIT
#ifdef LATER
if (AddMessage (hwnd, msg, wParam, lParam, WT_DOC))
return 0L;
#endif
switch (msg){
case WM_CREATE:
intrDebugOut((DEB_ITRACE,
"DocWndProc: CreateWindow hwndIn=%x\n",
hwndIn));
break;
case WM_DDE_INITIATE:
hwnd = ConvertToFullHWND(hwndIn);
intrAssert(IsWindowValid(hwnd));
lpclient = (LPCLIENT)GetWindowLong (hwnd, 0);
intrDebugOut((DEB_ITRACE,
"DocWndProc: WM_DDE_INITIATE hwnd=%x\n",
hwnd));
ChkC(lpclient);
// REVIEW: We may not allow initiates to get thru
// while we are waiting for terminates. So, this case
// may not arise
// Need to verify that m_pCI is not NULL during incoming
// calls from the client.
//
if (lpclient->m_fCallData)
{
intrDebugOut((DEB_ITRACE,
"DocWndProc: No initiates while waiting on terminate\n"));
break;
}
// if we are the document then respond.
if (! (lpclient->m_aItem == (ATOM)(HIWORD(lParam))))
{
break;
}
// We can enterain this client. Put this window in the client list
// and acknowledge the initiate.
if (!AddClient ((LPHANDLE)&lpclient->m_hcli, (HWND)wParam, (HWND)wParam))
{
break;
}
// post the acknowledge
DuplicateAtom (LOWORD(lParam));
DuplicateAtom (HIWORD(lParam));
SSSendMessage ((HWND)wParam, WM_DDE_ACK, (WPARAM)hwnd, lParam);
lpclient->m_cClients++;
lpclient->m_fCreatedNotConnected = FALSE;
lpoleObj = lpclient->m_lpoleObj;
// we have added an addref because of createinstance.
if (lpclient->m_bCreateInst)
{
lpclient->m_bCreateInst = FALSE;
}
return 1L; // fAckSent
break;
case WM_DDE_EXECUTE:
{
hwnd = ConvertToFullHWND(hwndIn);
intrAssert(IsWindowValid(hwnd));
lpclient = (LPCLIENT)GetWindowLong (hwnd, 0);
hdata = GET_WM_DDE_EXECUTE_HDATA(wParam,lParam);
intrDebugOut((DEB_ITRACE,
"DocWndProc: WM_DDE_EXECUTE hwnd=%x hdata=%x\n",
hwnd,
hdata));
ChkC(lpclient);
#ifdef FIREWALLS
// find the client in the client list.
hwndClient = FindClient (lpclient->m_hcli, (HWND)wParam, FALSE);
AssertSz (hwndClient, "Client is missing from the server");
#endif
if (!IsWindowValid ((HWND)wParam) || lpclient->m_fCallData)
{
if (lpclient->m_fCallData)
{
// This means the terminate has already been sent
// to this window (from AdviseSink::OnClose) so
// we can ignore the StdCloseDocument.
}
else
{
intrAssert(!"Execute received from dead sending window");
}
// Since we are not sending an ACK, the sender will not
// have the chance to free the hCommands handle.
DDEFREE(WM_DDE_ACK,lParam);
// GlobalFree (hdata);
break;
}
//
// Fill in the used items in DocData
//
docData.hwnd = hwnd;
docData.msg = msg;
docData.wParam = wParam;
docData.hdata = hdata;
docData.lpclient = lpclient;
retval = DocHandleIncomingCall(&docData);
//
// If error return, then we didn't call DocDispatchIncomingCall
// and therefore need to send a NACK
//
if (retval != NOERROR)
{
lpclient->SendExecuteAck (retval);
}
break;
}
case WM_DDE_TERMINATE:
hwnd = ConvertToFullHWND(hwndIn);
intrAssert(IsWindowValid(hwnd));
lpclient = (LPCLIENT)GetWindowLong (hwnd, 0);
intrDebugOut((DEB_ITRACE,
"DocWndProc: WM_DDE_TERMINATE hwnd=%x\n",
hwnd));
ChkC(lpclient);
//
// If m_fCallData, then we are are waiting for a terminate, which
// means we generated the original terminate message. If
// this is so, then we are waiting for a reply to the
// terminate. Set the AckState and break;
//
if (lpclient->m_fCallData)
{
lpclient->SetCallState(SERVERCALLEX_ISHANDLED);
break;
}
#ifdef _DEBUG
// find the client in the client list.
hwndClient = (HWND)FindClient (lpclient->m_hcli,(HWND)wParam, FALSE);
AssertSz(hwndClient, "Client is missing from the server");
#endif
AssertIsDoc (lpclient);
Assert (lpclient->m_cClients > 0);
lpclient->m_cClients--;
// Necessary safety bracket
lpclient->m_pUnkOuter->AddRef();
// terminate has to be handled always
// The DocHandleIncomingCall() routine will set the
// calltype to be CALLTYPE_ASYNC
// async calls are never rejected
docData.hwnd = hwnd;
docData.msg = msg;
docData.wParam = wParam;
docData.lpclient = lpclient;
retval = DocHandleIncomingCall(&docData);
intrAssert(retval == NOERROR);
// Necessary safety bracket
lpclient->m_pUnkOuter->Release();
break;
case WM_DESTROY:
intrDebugOut((DEB_ITRACE,
"DocWndProc: WM_DESTROY\n"));
break;
case WM_DDE_POKE:
{
hwnd = ConvertToFullHWND(hwndIn);
intrAssert(IsWindowValid(hwnd));
lpclient = (LPCLIENT)GetWindowLong (hwnd, 0);
hdata = GET_WM_DDE_POKE_HDATA(wParam,lParam);
aItem = GET_WM_DDE_POKE_ITEM(wParam,lParam);
ChkC(lpclient);
if (!IsWindowValid ((HWND) wParam) || lpclient->m_fCallData)
{
// The sending window is invalid or we have already sent a
// TERMINATE to it (as indicated by m_pCI != NULL).
// We cannot ACK the message, so we must free any
// handles or atoms.
Warn ("Ignoring message");
FreePokeData (hdata);
LDeleteAtom:
if (aItem != NULL)
{
GlobalDeleteAtom (aItem);
}
break;
}
docData.hwnd = hwnd;
docData.msg = msg;
docData.wParam = wParam;
docData.hdata = hdata;
docData.aItem = aItem;
docData.lpclient = lpclient;
retval = DocHandleIncomingCall(&docData);
if (retval != NOERROR)
{
SET_MSG_STATUS (retval, status);
// !!! If the fRelease is false and the post fails
// then we are not freeing the hdata. Are we supposed to
// REVIEW: The assumption is
LPARAM lp = MAKE_DDE_LPARAM(WM_DDE_ACK,status,aItem);
if (!PostMessageToClient ((HWND)wParam, WM_DDE_ACK, (UINT) hwnd,lp))
{
DDEFREE(WM_DDE_ACK,lp);
goto LDeleteAtom;
}
}
// johannp: set the busy bit here in the status word
break;
}
case WM_DDE_ADVISE:
{
hwnd = ConvertToFullHWND(hwndIn);
intrAssert(IsWindowValid(hwnd));
lpclient = (LPCLIENT)GetWindowLong (hwnd, 0);
HANDLE hOptions = GET_WM_DDE_ADVISE_HOPTIONS(wParam,lParam);
aItem = GET_WM_DDE_ADVISE_ITEM(wParam,lParam);
ChkC(lpclient);
if (!IsWindowValid ((HWND)wParam) || lpclient->m_fCallData)
{
AdviseErr:
Warn ("Ignoring advise message");
//
// GlobalFree wants a handle, we are giving it a DWORD.
//
GlobalFree (hOptions);
break;
}
docData.hwnd = hwnd;
docData.msg = msg;
docData.wParam = wParam;
docData.hdata = hOptions;
docData.aItem = aItem;
docData.lpclient = lpclient;
retval = DocHandleIncomingCall(&docData);
if (retval != NOERROR)
{
SET_MSG_STATUS (retval, status);
LPARAM lp = MAKE_DDE_LPARAM(WM_DDE_ACK,status,aItem);
if (!PostMessageToClient ((HWND)wParam, WM_DDE_ACK, (UINT) hwnd,lp))
{
DDEFREE(WM_DDE_ACK,lp);
goto AdviseErr;
}
}
break;
}
case WM_DDE_UNADVISE:
{
hwnd = ConvertToFullHWND(hwndIn);
intrAssert(IsWindowValid(hwnd));
lpclient = (LPCLIENT)GetWindowLong (hwnd, 0);
aItem = HIWORD(lParam);
ChkC(lpclient);
if (!IsWindowValid ((HWND)wParam) || lpclient->m_fCallData)
{
goto LDeleteAtom;
}
docData.hwnd = hwnd;
docData.msg = msg;
docData.wParam = wParam;
docData.aItem = aItem;
docData.lpclient = lpclient;
retval = DocHandleIncomingCall(&docData);
if (retval != NOERROR)
{
SET_MSG_STATUS (retval, status);
LPARAM lp = MAKE_DDE_LPARAM(WM_DDE_ACK,status,aItem);
if (!PostMessageToClient ((HWND)wParam, WM_DDE_ACK, (UINT) hwnd,lp))
{
DDEFREE(WM_DDE_ACK,lp);
goto LDeleteAtom;
}
}
break;
}
case WM_DDE_REQUEST:
{
hwnd = ConvertToFullHWND(hwndIn);
intrAssert(IsWindowValid(hwnd));
lpclient = (LPCLIENT)GetWindowLong (hwnd, 0);
aItem = HIWORD(lParam);
ChkC(lpclient);
if (!IsWindowValid ((HWND) wParam) || lpclient->m_fCallData)
{
goto LDeleteAtom;
}
docData.hwnd = hwnd;
docData.msg = msg;
docData.wParam = wParam;
docData.lParam = lParam;
docData.aItem = aItem;
docData.lpclient = lpclient;
retval = DocHandleIncomingCall(&docData);
if (retval != NOERROR)
{
if (retval == RPC_E_DDE_BUSY)
{
status = 0x4000;
}
else
{
status = 0; // negative acknowledge
}
LPARAM lp = MAKE_DDE_LPARAM(WM_DDE_ACK,status,aItem);
// if request failed, then acknowledge with error.
if (!PostMessageToClient ((HWND)wParam, WM_DDE_ACK, (UINT) hwnd,lp))
{
DDEFREE(WM_DDE_ACK,lp);
goto LDeleteAtom;
}
}
break;
}
default:
return SSDefWindowProc (hwndIn, msg, wParam, lParam);
}
return 0L;
}
INTERNAL_(void) CDefClient::SendExecuteAck
(HRESULT hresult)
{
AssertIsDoc (this);
WORD status = NULL;
SET_MSG_STATUS (hresult, status);
m_ExecuteAck.f = FALSE;
LPARAM lParam = MAKE_DDE_LPARAM (WM_DDE_ACK,status,
(UINT) m_ExecuteAck.hdata);
// Post the acknowledge to the client
if (!PostMessageToClient (m_ExecuteAck.hwndFrom, WM_DDE_ACK,
(UINT) m_ExecuteAck.hwndTo, lParam))
{
Assert (0);
// the window either died or post failed, delete the data
DDEFREE (WM_DDE_ACK,lParam);
GlobalFree (m_ExecuteAck.hdata);
}
}
//DocExecute: Interprets the execute command for the
//document conversation.
INTERNAL CDefClient::DocExecute
(HANDLE hdata)
{
ATOM acmd;
BOOL fShow;
BOOL fActivate;
HANDLE hdup = NULL;
HRESULT retval = ReportResult(0, S_OOM, 0, 0);
LPSTR lpitemname;
LPSTR lpopt;
LPSTR lpnextarg;
LPSTR lpdata = NULL;
LPSTR lpverb = NULL;
INT verb;
WORD wCmdType;
ChkC(this);
intrDebugOut((DEB_ITRACE,
"%p _IN CDefClient::DocExecute(hdata=%x)\n",
this,
hdata));
// !!!Can we modify the string which has been passed to us
// rather than duplicating the data. This will get some speed
// and save some space.
if(!(hdup = UtDupGlobal(hdata,GMEM_MOVEABLE)))
goto errRtn;
if (!(lpdata = (LPSTR)GlobalLock (hdup)))
goto errRtn;
retval = ReportResult(0, RPC_E_DDE_SYNTAX_EXECUTE, 0, 0);
intrDebugOut((DEB_ITRACE,
"%p _IN CDefClient::DocExecute command=(%s)\n",
this,
lpdata));
if(*lpdata++ != '[') // commands start with the left sqaure bracket
goto errRtn;
// scan the command and scan upto the first arg.
if (!(wCmdType = ScanCommand(lpdata, WT_DOC, &lpnextarg, &acmd)))
goto errRtn;
if (wCmdType == NON_OLE_COMMAND) {
#ifdef LATER
if (lpsrvr = (LPSRVR) GetWindowLong (GetParent (hwnd), 0)) {
if (!UtilQueryProtocol (lpsrvr->aClass, PROTOCOL_EXECUTE))
retval = OLE_ERROR_PROTOCOL;
else {
#ifdef FIREWALLS
if (!CheckPointer (lpoledoc, WRITE_ACCESS))
AssertSz (0, "Invalid LPOLESERVERDOC");
else if (!CheckPointer (lpoledoc->lpvtbl, WRITE_ACCESS))
AssertSz (0, "Invalid LPOLESERVERDOCVTBL");
else
AssertSz (lpoledoc->lpvtbl->Execute,
"Invalid pointer to Execute method");
#endif
retval = (*lpoledoc->lpvtbl->Execute) (lpoledoc, hdata);
}
}
#endif
AssertSz(0, "Doc level execute is being called");
goto errRtn;
}
//////////////////////////////////////////////////////////////////////////
//
// [StdCloseDocument]
//
//////////////////////////////////////////////////////////////////////////
if (acmd == aStdClose){
LPCLIENT lpclient=NULL;
// if not terminated by NULL error
if (*lpnextarg)
goto errRtn;
if ((retval = FindItem (NULL, (LPCLIENT FAR *)&lpclient)) != NOERROR)
return retval;
lpclient->m_fGotStdCloseDoc = TRUE;
retval = lpclient->m_lpoleObj->Close (OLECLOSE_SAVEIFDIRTY);
goto end;
}
//////////////////////////////////////////////////////////////////////////
//
// [StdDoVerbItem("itemname", verb, BOOL, BOOL]
//
//////////////////////////////////////////////////////////////////////////
if (acmd == aStdDoVerbItem){
lpitemname = lpnextarg;
if(!(lpverb = ScanArg(lpnextarg)))
goto errRtn;
if(!(lpnextarg = ScanNumArg(lpverb, &verb)))
goto errRtn;
#ifdef FIREWALLS
AssertSz (verb < 9 , "Unexpected verb number");
#endif
// now scan the show BOOL
if (!(lpnextarg = ScanBoolArg (lpnextarg, (BOOL FAR *)&fShow)))
goto errRtn;
fActivate = FALSE;
// if activate BOOL is present, scan it.
if (*lpnextarg) {
if (!(lpnextarg = ScanBoolArg (lpnextarg, (BOOL FAR *)&fActivate)))
goto errRtn;
}
if (*lpnextarg)
goto errRtn;
if (m_fEmbed)
{
// This is a totally bogus call to SetHostNames whose only
// purpose is to notify the server that this is an embedded
// (not linked) object.
if (!m_fDidRealSetHostNames)
{
Puts ("Bogus call to SetHostNames before DoVerb\r\n");
m_lpoleObj->SetHostNames (OLESTR("Container"), OLESTR("Object"));
}
}
// REVIEW: We are assuming that calling the Docdoverb method
// will not post any more DDE messahes.
retval = DocDoVerbItem (lpitemname, verb, fShow, !fActivate);
goto end;
}
//////////////////////////////////////////////////////////////////////////
//
// [StdShowItem("itemname"[, "true"])]
//
//////////////////////////////////////////////////////////////////////////
if (acmd != aStdShowItem)
goto errRtn;
lpitemname = lpnextarg;
if(!(lpopt = ScanArg(lpitemname)))
goto errRtn;
// Now scan for optional parameter.
fActivate = FALSE;
if (*lpopt) {
if(!(lpnextarg = ScanBoolArg (lpopt, (BOOL FAR *)&fActivate)))
goto errRtn;
if (*lpnextarg)
goto errRtn;
}
if (m_fEmbed)
{
// This is a totally bogus call to SetHostNames whose only
// purpose is to notify the server that this is an embedded
// (not linked) object.
// REVIEW LINK
if (!m_fDidRealSetHostNames)
{
Puts ("Bogus call to SetHostNames before ShowItem\r\n");
m_lpoleObj->SetHostNames (OLESTR("Container"), OLESTR("Object"));
}
}
retval = DocShowItem (lpitemname, !fActivate);
end:
errRtn:
if (lpdata)
GlobalUnlock (hdup);
if (hdup)
GlobalFree (hdup);
intrDebugOut((DEB_ITRACE,
"%p _OUT CDefClient::DocExecute(hdata=%x) hresult=%x\n",
this,
hdata,
retval));
return (HRESULT)retval;
}
INTERNAL_(HRESULT) CDefClient::DocShowItem
(
LPSTR lpAnsiitemname,
BOOL fAct
)
{
LPCLIENT lpclient;
HRESULT retval;
LPOLEOBJECT lpoleObj;
ChkC(this);
WCHAR lpitemname[MAX_STR];
if (MultiByteToWideChar(CP_ACP,0,lpAnsiitemname,-1,lpitemname,MAX_STR) == FALSE)
{
Assert(!"Unable to convert characters");
return(E_UNEXPECTED);
}
if ((retval = FindItem (lpitemname, (LPCLIENT FAR *)&lpclient))
!= NOERROR)
return retval;
ChkC(lpclient);
lpoleObj = lpclient->m_lpoleObj;
#ifdef FIREWALLS1
if (!CheckPointer (lpoleObj->lpvtbl, WRITE_ACCESS))
AssertSz (0, "Invalid LPOLEOBJECTVTBL");
else
AssertSz (lpoleObj->lpvtbl->DoVerb,
"Invalid pointer to DoVerb method");
#endif
// protocol sends false for activating and TRUE for not activating.
// for api send TRUE for avtivating and FALSE for not activating.
return lpclient->m_lpoleObj->DoVerb(OLEVERB_SHOW, NULL, NULL, NULL, NULL, NULL);
}
INTERNAL_(HRESULT) CDefClient::DocDoVerbItem
(
LPSTR lpAnsiitemname,
WORD verb,
BOOL fShow,
BOOL fAct
)
{
LPCLIENT lpclient;
HRESULT retval;
WCHAR lpitemname[MAX_STR];
if (MultiByteToWideChar(CP_ACP,0,lpAnsiitemname,-1,lpitemname,MAX_STR) == FALSE)
{
Assert(!"Unable to convert characters");
return(E_UNEXPECTED);
}
ChkC(this);
Puts ("DefClient::DocDoVerbItem\r\n");
if ((retval = FindItem (lpitemname, (LPCLIENT FAR *)&lpclient))
!= NOERROR)
return retval;
ChkC(lpclient);
#ifdef FIREWALLS1
if (!CheckPointer (lpclient->lpoleObj->lpvtbl, WRITE_ACCESS))
AssertSz (0, "Invalid LPOLEOBJECTVTBL");
else
AssertSz (lpclient->lpoleObj->lpvtbl->DoVerb,
"Invalid pointer to DoVerb method");
#endif
// pass TRUE to activate and False not to activate. Differnt from
// protocol.
retval = lpclient->m_lpoleObj->DoVerb(verb, NULL, &m_OleClientSite, NULL, NULL, NULL);
// Apparently an obsolete version of Lotus Notes is the only
// container (other than Cltest) that sets fShow=FALSE
if (!fShow && lpclient->m_lpoleObj && lpclient->m_fEmbed)
lpclient->m_lpoleObj->DoVerb(OLEIVERB_HIDE, NULL, &m_OleClientSite, NULL, NULL, NULL);
return retval;
}
INTERNAL CDefClient::DoInitNew()
{
HRESULT hresult;
ATOM aClass;
LPPERSISTSTORAGE pPersistStg=NULL;
hresult = m_lpoleObj->QueryInterface(IID_IPersistStorage,
(LPLPVOID)&pPersistStg);
if (hresult == NOERROR)
{
CLSID clsid;
RetZ (pPersistStg);
ErrRtnH (CreateILockBytesOnHGlobal ((HGLOBAL)NULL,
/*fDeleteOnRelease*/ TRUE,
&m_plkbytNative));
ErrZS (m_plkbytNative, E_OUTOFMEMORY);
ErrRtnH (StgCreateDocfileOnILockBytes
(m_plkbytNative,
grfCreateStg, 0,
&m_pstgNative));
ErrZS (m_pstgNative, E_OUTOFMEMORY);
aClass = m_psrvrParent->m_cnvtyp == cnvtypTreatAs ? m_psrvrParent->m_aOriginalClass
: m_psrvrParent->m_aClass;
// Write appropriate class tag
ErrZS (CLSIDFromAtom (aClass,(LPCLSID)&clsid),
REGDB_E_CLASSNOTREG);
ErrRtnH (WriteClassStg (m_pstgNative, clsid));
// Provide server with a storage to use for its persistent
// storage, i.e., native data. We remember this IStorage and the
// ILockBytes it is built on.
ErrRtnH (pPersistStg->InitNew(m_pstgNative));
m_fGotEditNoPokeNativeYet = FALSE;
// Now that we have initialized the object, we are allowed to
// set the client site, and advise.
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.
DoOle20Advise (OLE_CLOSED, (CLIPFORMAT)0);
}
else
{
AssertSz (0, "Can't get IPersistStorage from OleObj\r\n");
}
m_fEmbed = TRUE;
errRtn:
if (pPersistStg)
pPersistStg->Release();
return hresult;
}
// FreePokeData: Frees the poked dats.
INTERNAL_(void) FreePokeData
(
HANDLE hdde
)
{
DDEPOKE FAR * lpdde;
Puts ("FreePokeData\r\n");
if (hdde) {
if (lpdde = (DDEPOKE FAR *) GlobalLock (hdde)) {
GlobalUnlock (hdde);
FreeGDIdata (*(LPHANDLE)lpdde->Value, lpdde->cfFormat);
}
GlobalFree (hdde);
}
}
// Returns TRUE if GDI format else returns FALSE
INTERNAL_(BOOL) FreeGDIdata
(
HANDLE hData,
CLIPFORMAT cfFormat
)
{
Puts ("FreeGDIData\r\n");
if (cfFormat == CF_METAFILEPICT) {
LPMETAFILEPICT lpMfp;
if (lpMfp = (LPMETAFILEPICT) GlobalLock (hData)) {
GlobalUnlock (hData);
DeleteMetaFile (lpMfp->hMF);
}
GlobalFree (hData);
}
else if (cfFormat == CF_BITMAP)
DeleteObject (hData);
else if (cfFormat == CF_DIB)
GlobalFree (hData);
else
return FALSE;
return TRUE;
}