mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2497 lines
68 KiB
2497 lines
68 KiB
|
|
/****************************** Module Header ******************************\
|
|
* Module Name: Srvr.c Server Main module
|
|
*
|
|
* Purpose: Includes All the server communication related routines.
|
|
*
|
|
* Created: Oct 1990.
|
|
*
|
|
* Copyright (c) 1985, 1986, 1987, 1988, 1989 Microsoft Corporation
|
|
*
|
|
* History:
|
|
* Raor: Wrote the original version.
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
#include "ole2int.h"
|
|
//#include <shellapi.h>
|
|
// #include "cmacs.h"
|
|
#include <dde.h>
|
|
|
|
// for RemDdeRevokeClassFactory and HDDESRVR
|
|
#include <olerem.h>
|
|
|
|
#include "srvr.h"
|
|
#include "ddedebug.h"
|
|
#include "ddesrvr.h"
|
|
ASSERTDATA
|
|
|
|
#define WM_DONOTDESTROY WM_USER+1
|
|
|
|
#ifdef FIREWALLS
|
|
BOOL bShowed = FALSE;
|
|
void ShowVersion (void);
|
|
#endif
|
|
|
|
#ifdef _CHICAGO_
|
|
#define DdeCHAR CHAR
|
|
#define Ddelstrcmp lstrcmpA
|
|
#define DdeGetClassName GetClassNameA
|
|
#define szCDDEServer "CDDEServer"
|
|
#else
|
|
#define DdeCHAR WCHAR
|
|
#define Ddelstrcmp lstrcmpW
|
|
#define DdeGetClassName GetClassName
|
|
#define szCDDEServer OLESTR("CDDEServer")
|
|
#endif
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CDDEServer::Create
|
|
//
|
|
// Synopsis: Create a server window to service a particular class
|
|
//
|
|
// Effects: Using lpclass, and the information in lpDdeInfo, create
|
|
// a server window that is ready to respond to initiate
|
|
// messages from this class.
|
|
//
|
|
// Arguments: [lpclass] -- Class name
|
|
// [rclsid] -- Class ID
|
|
// [lpDdeInfo] -- Class Object information
|
|
// [phwnd] -- Out pointer for new window
|
|
// [aOriginalClass] -- For TreatAs/Convert to case
|
|
// [cnvtyp] -- Conversion type
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Signals:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Derivation:
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 5-28-94 kevinro Commented/cleaned
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
INTERNAL CDDEServer::Create
|
|
(LPOLESTR lpclass,
|
|
REFCLSID rclsid,
|
|
LPDDECLASSINFO lpDdeInfo,
|
|
HWND FAR * phwnd,
|
|
ATOM aOriginalClass,
|
|
CNVTYP cnvtyp)
|
|
{
|
|
// REVIEW what happens if we have two MDI servers register the
|
|
// same class factory?.
|
|
|
|
LPSRVR lpDDEsrvr = NULL;
|
|
ATOM aExe = NULL;
|
|
|
|
intrDebugOut((DEB_DDE_INIT,
|
|
"0 _IN CDDEServer::Create(lpclass=%ws)\n",
|
|
lpclass));
|
|
|
|
// add the app atom to global list
|
|
if (!ValidateSrvrClass (lpclass, &aExe))
|
|
{
|
|
intrDebugOut((DEB_IWARN,
|
|
"CDDEServer::Create(%ws) Invalid Class\n",
|
|
lpclass));
|
|
|
|
return OLE_E_CLSID;
|
|
}
|
|
|
|
lpDDEsrvr = new CDDEServer;
|
|
RetZS (lpDDEsrvr, E_OUTOFMEMORY);
|
|
|
|
// set the signature handle and the app atom.
|
|
lpDDEsrvr->m_chk = chkDdeSrvr;
|
|
lpDDEsrvr->m_aClass = wGlobalAddAtom (lpclass);
|
|
lpDDEsrvr->m_clsid = rclsid; // Class ID (already TreatAs'd)
|
|
lpDDEsrvr->m_aOriginalClass = wDupAtom (aOriginalClass);
|
|
lpDDEsrvr->m_pClassFactory = NULL;
|
|
lpDDEsrvr->m_dwClassFactoryKey = lpDdeInfo->dwRegistrationKey;
|
|
lpDDEsrvr->m_aExe = aExe;
|
|
lpDDEsrvr->m_cnvtyp = cnvtyp;
|
|
lpDDEsrvr->m_fcfFlags = lpDdeInfo->dwFlags;
|
|
|
|
lpDDEsrvr->m_bTerminate = FALSE; // Set if we are terminating.
|
|
lpDDEsrvr->m_hcli = NULL; // handle to the first block of clients list
|
|
lpDDEsrvr->m_termNo = 0; // termination count
|
|
lpDDEsrvr->m_cSrvrClients= 0; // no of clients;
|
|
lpDDEsrvr->m_fDoNotDestroyWindow= 0;
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef FIREWALLS
|
|
AssertSz(lpDdeInfo.dwFlags <= REGCLS_MULTI_SEPARATE, "invalid server options");
|
|
#endif
|
|
|
|
// Create the server window and do not show it.
|
|
//
|
|
// We are explicitly calling CreateWindowA here.
|
|
// The DDE tracking layer will attempt to convert hCommands to UNICODE
|
|
// if the two windows in the conversation are both UNICODE.
|
|
// This window is created as a child of the common server window for this
|
|
// thread. When this thread dies, the common server window is destroyed if
|
|
// it exists, which will cause all of the child windows to be destroyed also.
|
|
//
|
|
//
|
|
if (!(lpDDEsrvr->m_hwnd = DdeCreateWindowEx (0, gOleWindowClass,
|
|
szCDDEServer,
|
|
WS_OVERLAPPED | WS_CHILD,
|
|
0,0,0,0,
|
|
(HWND)TLSGetDdeServer(),
|
|
NULL,
|
|
g_hinst, NULL)))
|
|
{
|
|
goto errReturn;
|
|
}
|
|
|
|
// fix up the WindowProc entry point.
|
|
SetWindowLong(lpDDEsrvr->m_hwnd, GWL_WNDPROC, (LONG)SrvrWndProc);
|
|
|
|
//
|
|
// The following will inform the class object in the class registration table
|
|
// that this window should be notified when the class object is revoked. This
|
|
// enables the window to shutdown properly.
|
|
//
|
|
// If there isn't a class factory, which happens for single instance servers
|
|
// which were launched with a filename, then m_dwClassFactory will be 0,
|
|
// in which case we don't make the set call.
|
|
//
|
|
if(lpDDEsrvr->m_dwClassFactoryKey != 0)
|
|
{
|
|
if(!SetDdeServerWindow(lpDDEsrvr->m_dwClassFactoryKey,lpDDEsrvr->m_hwnd))
|
|
{
|
|
intrDebugOut((DEB_IERROR,
|
|
"0 CDDEServer::Create unable to SetDdeServerWindow\n"));
|
|
goto errReturn;
|
|
}
|
|
}
|
|
|
|
intrDebugOut((DEB_DDE_INIT,
|
|
"DDE Server window for %ws created in task %x\n",
|
|
lpclass,GetCurrentThreadId()));
|
|
|
|
// save the ptr to the server struct in the window.
|
|
SetWindowLong (lpDDEsrvr->m_hwnd, 0, (LONG)lpDDEsrvr);
|
|
|
|
// Set the signature.
|
|
SetWindowWord (lpDDEsrvr->m_hwnd, WW_LE, WC_LE);
|
|
|
|
*phwnd = lpDDEsrvr->m_hwnd;
|
|
|
|
|
|
intrDebugOut((DEB_DDE_INIT,
|
|
"0 _OUT CDDEServer::Create returns %x\n",
|
|
NOERROR));
|
|
return NOERROR;
|
|
|
|
errReturn:
|
|
AssertSz (0, "CDDEServer::Create errReturn");
|
|
if (lpDDEsrvr)
|
|
{
|
|
if (lpDDEsrvr->m_hwnd)
|
|
SSDestroyWindow (lpDDEsrvr->m_hwnd);
|
|
|
|
if (lpDDEsrvr->m_aClass)
|
|
GlobalDeleteAtom (lpDDEsrvr->m_aClass);
|
|
|
|
if (lpDDEsrvr->m_aExe)
|
|
GlobalDeleteAtom (lpDDEsrvr->m_aExe);
|
|
delete lpDDEsrvr;
|
|
}
|
|
|
|
intrDebugOut((DEB_IERROR,
|
|
"0 _OUT CDDEServer::Create returns %x\n",
|
|
E_OUTOFMEMORY));
|
|
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
|
|
|
|
// ValidateSrvrClass checks whether the given server class is valid by
|
|
// looking in the registration database.
|
|
|
|
INTERNAL_(BOOL) ValidateSrvrClass (
|
|
LPOLESTR lpclass,
|
|
ATOM FAR * lpAtom
|
|
)
|
|
{
|
|
WCHAR buf[MAX_STR];
|
|
LONG cb = MAX_STR;
|
|
WCHAR key[MAX_STR];
|
|
LPOLESTR lptmp;
|
|
LPOLESTR lpbuf;
|
|
WCHAR ch;
|
|
CLSID clsid;
|
|
|
|
if (CLSIDFromProgID (lpclass, &clsid) != NOERROR)
|
|
{
|
|
// ProgId is not correctly registered in reg db
|
|
return FALSE;
|
|
}
|
|
|
|
lstrcpyW (key, lpclass);
|
|
lstrcatW (key, OLESTR("\\protocol\\StdFileEditing\\server"));
|
|
|
|
if (RegQueryValue (HKEY_CLASSES_ROOT, key, buf, &cb))
|
|
return TRUE;
|
|
|
|
if (!buf[0])
|
|
{
|
|
AssertSz (0, "ValidateSrvrClass failed.");
|
|
return FALSE;
|
|
}
|
|
|
|
// Get exe name without path and then get an atom for that
|
|
lptmp = lpbuf = buf;
|
|
while (ch = *lptmp)
|
|
{
|
|
lptmp++;
|
|
if (ch == '\\' || ch == ':')
|
|
lpbuf = lptmp;
|
|
}
|
|
*lpAtom = wGlobalAddAtom (lpbuf);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
INTERNAL RemDdeRevokeClassFactory
|
|
(LPSRVR lpsrvr)
|
|
{
|
|
HRESULT hr;
|
|
intrDebugOut((DEB_ITRACE,
|
|
"0 _IN RemDdeRevokeClassFactory(%x)\n",
|
|
lpsrvr));
|
|
|
|
ChkS(lpsrvr);
|
|
hr = lpsrvr->Revoke();
|
|
intrDebugOut((DEB_ITRACE,
|
|
"0 OUT RemDdeRevokeClassFactory(%x) %x\n",
|
|
lpsrvr,hr));
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
INTERNAL CDDEServer::Revoke ()
|
|
{
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%x _IN CDDEServer::Revoke() m_cSrvrClients=%x\n",
|
|
this,
|
|
m_cSrvrClients));
|
|
HRESULT hr;
|
|
|
|
ChkS(this);
|
|
|
|
//
|
|
// Can't revoke if there are still clients. QueryRevokeCLassFactory
|
|
// determines if there are still clients attached.
|
|
//
|
|
if (!QueryRevokeClassFactory ())
|
|
{
|
|
intrDebugOut((DEB_IERROR,
|
|
"QueryRevokeClassFactory failed!"));
|
|
hr = RPC_E_DDE_REVOKE;
|
|
goto exitRtn;
|
|
}
|
|
|
|
if (m_cSrvrClients)
|
|
{
|
|
m_bTerminate = TRUE;
|
|
// if there are any clients connected to this classfactory,
|
|
// send terminates.
|
|
SendServerTerminateMsg ();
|
|
m_bTerminate = FALSE;
|
|
}
|
|
|
|
hr = FreeSrvrMem ();
|
|
|
|
exitRtn:
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%x OUT CDDEServer::Revoke(%x) hr = %x\n",
|
|
this, hr));
|
|
return hr;
|
|
}
|
|
|
|
INTERNAL_(void) CDDEServer::SendServerTerminateMsg ()
|
|
{
|
|
|
|
HANDLE hcliPrev = NULL;
|
|
PCLILIST pcli;
|
|
HANDLE *phandle;
|
|
HANDLE hcli;
|
|
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%x _IN CDDEServer::SendServerTerminateMsg\n",
|
|
this));
|
|
|
|
hcli = m_hcli;
|
|
while (hcli) {
|
|
if ((pcli = (PCLILIST) LocalLock (hcli)) == NULL)
|
|
{
|
|
Assert(0);
|
|
goto exitRtn;
|
|
}
|
|
|
|
phandle = (HANDLE *) (pcli->info);
|
|
while (phandle < (HANDLE *)(pcli + 1)) {
|
|
if (*phandle)
|
|
{
|
|
PostMessageToClientWithReply ((HWND)(*phandle), WM_DDE_TERMINATE,
|
|
(WPARAM) m_hwnd, NULL, WM_DDE_TERMINATE);
|
|
Assert (m_cSrvrClients);
|
|
m_cSrvrClients--;
|
|
}
|
|
phandle++;
|
|
phandle++;
|
|
}
|
|
|
|
hcliPrev = hcli;
|
|
hcli = pcli->hcliNext;
|
|
LocalUnlock (hcliPrev);
|
|
}
|
|
|
|
exitRtn:
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%x OUT CDDEServer::SendServerTerminateMsg\n",
|
|
this));
|
|
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CDDEServer::FreeSrvrMem
|
|
//
|
|
// Synopsis: Free's up a CDDEServer.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: [void] --
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Signals:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Derivation:
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 6-26-94 kevinro Commented
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
INTERNAL CDDEServer::FreeSrvrMem
|
|
(void)
|
|
{
|
|
HRESULT hr;
|
|
// REVIEW: Not clear how this works in the synchronous mode
|
|
// Release for class factory is called only when everything is
|
|
// cleaned and srvr app can post WM_QUIT at this stage
|
|
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%x _IN CDDEServer::FreeSrvrMem\n",
|
|
this));
|
|
|
|
PCLILIST pcliPrev;
|
|
HANDLE hcli, hcliPrev;
|
|
|
|
if (m_bTerminate)
|
|
{
|
|
AssertSz (0, "terminate flag is not FALSE");
|
|
}
|
|
|
|
|
|
if (m_aExe)
|
|
{
|
|
GlobalDeleteAtom (m_aExe);
|
|
}
|
|
|
|
|
|
// We deliberately do not call this->Lock (FALSE)
|
|
// If the server has revoked his class object without
|
|
// waiting for his Lock count to go to zero, then
|
|
// presumably he doesn't need us to unlock him. In fact,
|
|
// doing such an unlock might confuse a server who then
|
|
// tries to call CoRevokeClassObject recursively.
|
|
if (m_pClassFactory)
|
|
{
|
|
m_pClassFactory->Release();
|
|
m_pClassFactory = NULL;
|
|
}
|
|
|
|
hcli = m_hcli;
|
|
while (hcli)
|
|
{
|
|
hcliPrev = hcli;
|
|
if (pcliPrev = (PCLILIST) LocalLock (hcliPrev))
|
|
{
|
|
hcli = pcliPrev->hcliNext;
|
|
}
|
|
else
|
|
{
|
|
AssertSz (0, "Corrupt internal data structure or out-of-memory");
|
|
hcli = NULL;
|
|
}
|
|
Verify (0==LocalUnlock (hcliPrev));
|
|
Verify (NULL==LocalFree (hcliPrev));
|
|
}
|
|
|
|
hr = DestroyDdeSrvrWindow(m_hwnd,m_aClass);
|
|
if (hr != NOERROR)
|
|
{
|
|
//
|
|
// Well now, if DestroyWindow fails, there isn't a whole heck of
|
|
// alot we can do about it. It could mean that the window was
|
|
// destroyed previously, or the parent window was destroyed during
|
|
// thread shutdown. We should still continue to cleanup
|
|
//
|
|
intrDebugOut((DEB_IERROR,
|
|
"%x CDDEServer::FreeSrvrMem DestroyDdeSrvrWindow failed %x\n",
|
|
this,
|
|
hr));
|
|
}
|
|
|
|
if (m_aClass)
|
|
{
|
|
GlobalDeleteAtom (m_aClass);
|
|
}
|
|
|
|
delete this;
|
|
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%x _OUT CDDEServer::FreeSrvrMem\n",
|
|
this));
|
|
return NOERROR;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: SrvrHandleIncomingCall
|
|
//
|
|
// Synopsis: Setup and call the CallControl to dispatch a call to the server
|
|
//
|
|
// 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 SrvrDispatchIncomingCall to actuall process the call.
|
|
//
|
|
// This routine should only be called by the SrvrWndProc
|
|
//
|
|
//
|
|
// Arguments: [lpsrvr] -- Points to the server
|
|
// [hwnd] -- hwnd of server
|
|
// [hdata] -- Handle to data
|
|
// [wParam] -- hwnd of client
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Signals:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 6-05-94 kevinro Commented/cleaned
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
INTERNAL SrvrHandleIncomingCall(LPSRVR lpsrvr,
|
|
HWND hwnd,
|
|
HANDLE hdata,
|
|
HWND wParam)
|
|
{
|
|
VDATEHEAP();
|
|
HRESULT hresult = NOERROR;
|
|
SRVRDISPATCHDATA srvrdispdata;
|
|
DISPATCHDATA dispatchdata;
|
|
|
|
intrDebugOut((DEB_ITRACE,
|
|
"0 _IN SrvrHandleIncomingCall lpsrvr=%x hwnd=%x hdata=%x wParam=%x\n",
|
|
lpsrvr,
|
|
hwnd,
|
|
hdata,
|
|
wParam));
|
|
|
|
srvrdispdata.wDispFunc = DDE_DISP_SRVRWNDPROC;
|
|
srvrdispdata.hwnd = hwnd;
|
|
srvrdispdata.hData = hdata;
|
|
srvrdispdata.wParam = wParam;
|
|
srvrdispdata.lpsrvr = lpsrvr;
|
|
|
|
dispatchdata.pData = &srvrdispdata;
|
|
|
|
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 = &(lpsrvr->m_pCallMgr);
|
|
IRpcChannelBuffer * pChannel = &(lpsrvr->m_pCallMgr);
|
|
hresult = STAInvoke(&rpcMsg, CALLCAT_SYNCHRONOUS, pStub, pChannel, NULL, &dwFault);
|
|
|
|
intrDebugOut((DEB_ITRACE,
|
|
"0 _OUT SrvrHandleIncomingCall hresult=%x\n",
|
|
hresult));
|
|
|
|
return(hresult);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: SrvrDispatchIncomingCall
|
|
//
|
|
// Synopsis: Dispatch a call into the server.
|
|
//
|
|
// Effects: At the moment, the only incoming call that requires handling
|
|
// by the server window is Execute. This routine dispatchs to it,
|
|
// and returns.
|
|
//
|
|
// Arguments: [psdd] --
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Signals:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 6-05-94 kevinro Commented/cleaned
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
INTERNAL SrvrDispatchIncomingCall(PSRVRDISPATCHDATA psdd)
|
|
{
|
|
VDATEHEAP();
|
|
HRESULT hr;
|
|
intrDebugOut((DEB_ITRACE,
|
|
"0 _IN SrvrDispatchIncomingCall psdd(%x)\n",psdd));
|
|
|
|
hr = psdd->lpsrvr->SrvrExecute (psdd->hwnd,
|
|
psdd->hData,
|
|
(HWND)(psdd->wParam));
|
|
|
|
intrDebugOut((DEB_ITRACE,
|
|
"0 _OUT SrvrDispatchIncomingCall psdd(%x) hr =%x\n",
|
|
psdd,
|
|
hr));
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
// REVIEW: Revoking Class Factory will not be successful if
|
|
// any clients are either connected to the classfactory
|
|
// or to the object instances.
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: SrvrWndProc
|
|
//
|
|
// Synopsis: This is the server window procedure.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: [hwndIn] -- Window handle (may not be full. See note)
|
|
// [msg] --
|
|
// [wParam] --
|
|
// [lParam] --
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Signals:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 8-03-94 kevinro Created
|
|
//
|
|
// 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) SrvrWndProc (
|
|
HWND hwndIn,
|
|
UINT msg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
BOOL fRevoke=FALSE;
|
|
LPSRVR lpsrvr;
|
|
WORD status = NULL;
|
|
HANDLE hdata;
|
|
ATOM aItem;
|
|
HRESULT retval;
|
|
|
|
//
|
|
// The following hwnd variable is used to determine the full HWND, in the
|
|
// event we were dispatched in a 16 bit process.
|
|
//
|
|
HWND hwnd;
|
|
|
|
#ifdef FIREWALLS
|
|
HWND hwndClient;
|
|
#endif
|
|
|
|
|
|
switch (msg){
|
|
|
|
case WM_DDE_INITIATE:
|
|
VDATEHEAP();
|
|
#ifdef FIREWALLS
|
|
AssertSz (lpsrvr, "No server window handle in server window");
|
|
#endif
|
|
hwnd = ConvertToFullHWND(hwndIn);
|
|
|
|
lpsrvr = (LPSRVR)GetWindowLong (hwnd, 0);
|
|
if (lpsrvr->m_bTerminate){
|
|
// we are terminating, no more connections
|
|
break;
|
|
}
|
|
|
|
// class is not matching, so it is not definitely for us.
|
|
// for apps sending the EXE for initiate, do not allow if the app
|
|
// is mutiple instance (Bug fix for winworks).
|
|
|
|
if (!(lpsrvr->m_aClass == (ATOM)(LOWORD(lParam)) ||
|
|
(NOERROR==wCompatibleClasses (LOWORD(lParam), lpsrvr->m_aClass)) ||
|
|
(lpsrvr->m_aExe == (ATOM)(LOWORD(lParam)) && IsSingleServerInstance() )))
|
|
{
|
|
break;
|
|
}
|
|
|
|
intrDebugOut((DEB_DDE_INIT,"::SrvrWndProc INITIATE\n"));
|
|
|
|
|
|
if (!lpsrvr->HandleInitMsg (lParam))
|
|
{
|
|
if (!(aSysTopic == (ATOM)(HIWORD(lParam))))
|
|
{
|
|
//
|
|
// If this isn't a sys topic, then it must be a request for
|
|
// a specific document. Send a message to the
|
|
// children windows, asking for the document. If one of them
|
|
// may send an ACK to the client.
|
|
//
|
|
|
|
// if the server window is not the right window for
|
|
// DDE conversation, then try with the doc windows.
|
|
BOOL fAckSent = SendInitMsgToChildren (hwnd, msg, wParam, lParam);
|
|
|
|
#ifdef KEVINRO_OLDCODE
|
|
The following code was removed, because I don't belive it is required
|
|
any longer. I am not 100% sure yet, so I have left it in. If you find it,
|
|
you can probably remove it.
|
|
It appears to be trying to claim the SINGLE_USE class factory from the class
|
|
factory table. It does this when a child document window sends an ACK to the
|
|
client, claiming to support the document being asked for. It really doesn't
|
|
make too much sense here, since a single use server would have already removed
|
|
its class factory if there was an open document.
|
|
|
|
Anyway, the 16-bit version had a direct hack into the class factory table. We
|
|
don't have that anymore, so this code wouldn't work anyway.
|
|
|
|
if (lpsrvr->m_fcfFlags==REGCLS_SINGLEUSE)
|
|
{
|
|
if (lpsrvr->m_pfAvail)
|
|
{
|
|
// Hide the entry in the class factory table so that no 2.0
|
|
// client can connect to the same server.
|
|
Assert (!IsBadWritePtr (lpsrvr->m_pfAvail, sizeof(BOOL)));
|
|
*(lpsrvr->m_pfAvail) = FALSE;
|
|
}
|
|
}
|
|
#endif // KEVINRO_OLDCODE
|
|
intrDebugOut((DEB_DDE_INIT,"SrvrWndProc Child Init\n"));
|
|
return fAckSent;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// We can enterain this client. Put him in our client list
|
|
// and acknowledge the initiate.
|
|
|
|
if (!AddClient ((LPHANDLE)&lpsrvr->m_hcli, (HWND)wParam,(HWND)/*fLocked*/FALSE))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Now its time to grab up the class factory from the class
|
|
// factory table. When this window was created, the class factory
|
|
// was available. However, it is possible that it has already
|
|
// been claimed by someone else. So, we try grabbing it (which
|
|
// normally should succeed). If it fails, then delete the client
|
|
// and don't acknowledge.
|
|
//
|
|
|
|
if (lpsrvr->m_pClassFactory == NULL)
|
|
{
|
|
DdeClassInfo ddeInfo;
|
|
ddeInfo.dwContextMask = CLSCTX_LOCAL_SERVER |
|
|
CLSCTX_INPROC_SERVER;
|
|
intrDebugOut((DEB_DDE_INIT,"SrvrWndProc getting class factory\n"));
|
|
//
|
|
// The following asks for control of the class
|
|
// factory in the case of a single use class
|
|
//
|
|
ddeInfo.fClaimFactory = TRUE;
|
|
ddeInfo.dwRegistrationKey = lpsrvr->m_dwClassFactoryKey;
|
|
|
|
if (GetClassInformationFromKey(&ddeInfo) == FALSE)
|
|
{
|
|
intrDebugOut((DEB_IERROR,"SrvrWndProc failed to get class factory\n"));
|
|
//
|
|
// Whoops, we were not able to grab the class factory
|
|
// Cleanup and hop out
|
|
if (!FindClient ((LPHANDLE)lpsrvr->m_hcli,(HWND)wParam, TRUE))
|
|
{
|
|
intrAssert(!"FindClient failed\n");
|
|
}
|
|
return(0);
|
|
}
|
|
lpsrvr->m_pClassFactory = (IClassFactory *)ddeInfo.punk;
|
|
lpsrvr->m_fcfFlags = ddeInfo.dwFlags;
|
|
}
|
|
|
|
intrAssert(lpsrvr->m_pClassFactory != NULL);
|
|
|
|
lpsrvr->m_cSrvrClients++;
|
|
|
|
lpsrvr->Lock (TRUE, (HWND)wParam);
|
|
|
|
// Post acknowledge
|
|
DuplicateAtom (LOWORD(lParam));
|
|
DuplicateAtom (HIWORD(lParam));
|
|
SSSendMessage ((HWND)wParam, WM_DDE_ACK, (WPARAM)hwnd, lParam);
|
|
|
|
|
|
return 1L; // fAckSent==TRUE
|
|
VDATEHEAP();
|
|
break;
|
|
|
|
|
|
case WM_DDE_EXECUTE:
|
|
VDATEHEAP();
|
|
hwnd = ConvertToFullHWND(hwndIn);
|
|
|
|
lpsrvr = (LPSRVR)GetWindowLong (hwnd, 0);
|
|
|
|
hdata = GET_WM_DDE_EXECUTE_HDATA(wParam,lParam);
|
|
|
|
#ifdef FIREWALLS
|
|
AssertSz (lpsrvr, "No server handle in server window");
|
|
#endif
|
|
|
|
intrDebugOut((DEB_ITRACE,"SrvrWndProc WM_DDE_EXECUTE\n"));
|
|
|
|
#ifdef FIREWALLS
|
|
// find the client in the client list.
|
|
hwndClient = FindClient (lpsrvr->m_hcli, (HWND)wParam, FALSE);
|
|
AssertSz (hwndClient, "Client is missing from the server")
|
|
#endif
|
|
// Are we terminating
|
|
if (lpsrvr->m_bTerminate) {
|
|
intrDebugOut((DEB_ITRACE,
|
|
"SrvrWndProc WM_DDE_EXECUTE ignored for TERMINATE\n"));
|
|
// !!! are we supposed to free the data
|
|
GlobalFree (hdata);
|
|
break;
|
|
}
|
|
|
|
retval = SrvrHandleIncomingCall(lpsrvr,hwnd,hdata,(HWND)wParam);
|
|
|
|
if (NOERROR!=retval)
|
|
{
|
|
intrDebugOut((DEB_IERROR,
|
|
"SrvrWndProc SrvrHandleIncomingCall fail %x\n",
|
|
retval));
|
|
}
|
|
SET_MSG_STATUS (retval, status)
|
|
|
|
if (!lpsrvr->m_bTerminate)
|
|
{
|
|
// REVIEW: We are making an assumption that, we will not be posting
|
|
// any DDE messages because of calling the SrvrExecute.
|
|
// If we post any messages, before we post the acknowledge
|
|
// we will be in trouble.
|
|
|
|
lParam = MAKE_DDE_LPARAM(WM_DDE_ACK,status,(UINT) hdata);
|
|
|
|
intrDebugOut((DEB_ITRACE,
|
|
"SrvrWndProc WM_DDE_EXECUTE sending %x for ack\n",status));
|
|
|
|
// Post the acknowledge to the client
|
|
if (!PostMessageToClient ((HWND) wParam,
|
|
WM_DDE_ACK, (UINT) hwnd, lParam)) {
|
|
// if the window died or post failed, delete the atom.
|
|
GlobalFree (hdata);
|
|
DDEFREE(WM_DDE_ACK,lParam);
|
|
}
|
|
}
|
|
VDATEHEAP();
|
|
break;
|
|
|
|
|
|
|
|
case WM_DDE_TERMINATE:
|
|
intrDebugOut((DEB_ITRACE,
|
|
"SrvrWndProc WM_DDE_TERMINATE\n"));
|
|
|
|
hwnd = ConvertToFullHWND(hwndIn);
|
|
|
|
lpsrvr = (LPSRVR)GetWindowLong (hwnd, 0);
|
|
|
|
#ifdef FIREWALLS
|
|
// find the client in the client list.
|
|
hwndClient = FindClient (lpsrvr->m_hcli, (HWND)wParam, FALSE);
|
|
AssertSz (hwndClient, "Client is missing from the server")
|
|
#endif
|
|
Putsi (lpsrvr->m_bTerminate);
|
|
if (lpsrvr->m_bTerminate)
|
|
{
|
|
AssertSz (0, "Unexpected code path");
|
|
}
|
|
else
|
|
{
|
|
// If client initiated the terminate. post matching terminate
|
|
PostMessageToClient ((HWND)wParam,
|
|
WM_DDE_TERMINATE,
|
|
(UINT) hwnd,
|
|
NULL);
|
|
--lpsrvr->m_cSrvrClients;
|
|
if (0==lpsrvr->m_cSrvrClients
|
|
&& lpsrvr->QueryRevokeClassFactory())
|
|
{
|
|
#ifdef KEVINRO_OLD_CODE
|
|
if (lpsrvr->m_phwndDde)
|
|
{
|
|
// Remove from class factory table
|
|
*(lpsrvr->m_phwndDde) = (HWND)0;
|
|
}
|
|
#endif // KEVINRO_OLD_CODE
|
|
fRevoke = TRUE;
|
|
}
|
|
|
|
lpsrvr->Lock (FALSE, (HWND)wParam); // Unlock server
|
|
FindClient (lpsrvr->m_hcli, (HWND)wParam, /*fDelete*/TRUE);
|
|
|
|
if (fRevoke)
|
|
{
|
|
lpsrvr->Revoke();
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
case WM_DDE_REQUEST:
|
|
aItem = GET_WM_DDE_REQUEST_ITEM(wParam,lParam);
|
|
|
|
hwnd = ConvertToFullHWND(hwndIn);
|
|
|
|
lpsrvr = (LPSRVR)GetWindowLong (hwnd, 0);
|
|
|
|
intrDebugOut((DEB_ITRACE,
|
|
"SrvrWndProc WM_DDE_REQUEST(aItem=%x)\n",aItem));
|
|
|
|
if (lpsrvr->m_bTerminate || !IsWindowValid ((HWND) wParam))
|
|
{
|
|
goto RequestErr;
|
|
}
|
|
|
|
if(RequestDataStd (aItem, (HANDLE FAR *)&hdata) != NOERROR)
|
|
{
|
|
|
|
lParam = MAKE_DDE_LPARAM(WM_DDE_ACK,0x8000,aItem);
|
|
|
|
// if request failed, then acknowledge with error.
|
|
if (!PostMessageToClient ((HWND)wParam, WM_DDE_ACK,
|
|
(UINT) hwnd, lParam))
|
|
{
|
|
DDEFREE(WM_DDE_ACK,lParam);
|
|
RequestErr:
|
|
if (aItem)
|
|
{
|
|
GlobalDeleteAtom (aItem);
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lParam = MAKE_DDE_LPARAM(WM_DDE_REQUEST,
|
|
(UINT) hdata,(UINT) aItem);
|
|
|
|
// post the data message and we are not asking for any
|
|
// acknowledge.
|
|
|
|
if (!PostMessageToClient ((HWND)wParam, WM_DDE_DATA,
|
|
(UINT) hwnd, lParam))
|
|
{
|
|
GlobalFree (hdata);
|
|
DDEFREE(WM_DDE_REQUEST,lParam);
|
|
goto RequestErr;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_DONOTDESTROY:
|
|
intrDebugOut((DEB_ITRACE,
|
|
"SrvrWndProc WM_DONOTDESTROY %x\n",
|
|
wParam));
|
|
|
|
//
|
|
// This message is only sent by 32-bit code that has been
|
|
// given our full handle
|
|
//
|
|
|
|
lpsrvr = (LPSRVR)GetWindowLong (hwndIn, 0);
|
|
|
|
//
|
|
// The WM_DONOTDESTROY message tells the server how to
|
|
// handle the following WM_USER message. If wParam is set,
|
|
// then the m_fDoNotDestroyWindow flag will be set, which
|
|
// keeps us from destroying the server window. If cleared,
|
|
// it will enable the destruction. This message is sent
|
|
// from the MaybeCreateDocWindow routine
|
|
//
|
|
|
|
lpsrvr->m_fDoNotDestroyWindow = wParam;
|
|
return 0;
|
|
break;
|
|
|
|
case WM_USER:
|
|
intrDebugOut((DEB_ITRACE,
|
|
"SrvrWndProc WM_USER\n"));
|
|
//
|
|
// This message is only sent by 32-bit code that has been
|
|
// given our full handle
|
|
//
|
|
|
|
lpsrvr = (LPSRVR)GetWindowLong (hwndIn, 0);
|
|
|
|
// cftable.cpp sends a WM_USER message to destory the DDE
|
|
// server window when a 2.0 client has connected to a
|
|
// SDI 2.0 server (and no 1.0 client should be allowed to also
|
|
// connect.
|
|
// cftable.cpp cannot call RemDdeRevokeClassFactory directly
|
|
// becuase they may be in different processes.
|
|
//
|
|
// The m_fDoNotDestroyWindow flag is used by
|
|
// MaybeCreateDocWindow in the case that the server is a
|
|
// single use server, and revokes its class factory when
|
|
// an object is created. MaybeCreateDocWindow will set this
|
|
// flag, telling us to ignore the message.
|
|
//
|
|
// returning 0 means we did destroy, 1 means we did not.
|
|
|
|
if (!lpsrvr->m_fDoNotDestroyWindow)
|
|
{
|
|
RemDdeRevokeClassFactory(lpsrvr);
|
|
return(0);
|
|
}
|
|
return 1;
|
|
break;
|
|
|
|
default:
|
|
return SSDefWindowProc (hwndIn, msg, wParam, lParam);
|
|
}
|
|
|
|
return 0L;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CDDEServer::HandleInitMsg
|
|
//
|
|
// Synopsis: Determine if we are going to handle the INITIATE message.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: [lParam] --
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Signals:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Derivation:
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 5-28-94 kevinro Commented/cleaned
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
INTERNAL_(BOOL) CDDEServer::HandleInitMsg(LPARAM lParam)
|
|
{
|
|
|
|
// If it is not system or Ole, this is not the server.
|
|
if (!((aSysTopic == (ATOM)(HIWORD(lParam))) || (aOLE == (ATOM)(HIWORD(lParam)))))
|
|
{
|
|
return FALSE;
|
|
}
|
|
Assert (m_fcfFlags<=REGCLS_MULTI_SEPARATE);
|
|
|
|
// single instance MDI accept
|
|
if (m_fcfFlags != REGCLS_SINGLEUSE)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// this server is multiple instance. So, check for any clients or docs.
|
|
if (!GetWindow (m_hwnd, GW_CHILD) && 0==m_cSrvrClients)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
// AddClient: Adds a client entry to the list.
|
|
// Each client entry is a pair of handles; key handle
|
|
// and data handle. Ecah list entry contains space for
|
|
// MAX_LIST of pairs of handles.
|
|
|
|
INTERNAL_(BOOL) AddClient
|
|
(
|
|
LPHANDLE lphead, // ptr to loc which contains the head handle
|
|
HANDLE hkey, // key
|
|
HANDLE hdata // hdata
|
|
)
|
|
{
|
|
|
|
HANDLE hcli = NULL;
|
|
HANDLE hcliPrev = NULL;
|
|
PCLILIST pcli;
|
|
HANDLE *phandle;
|
|
|
|
|
|
hcli = *lphead;
|
|
|
|
// if the entry is already present, return error.
|
|
if (hcli && FindClient (hcli, hkey, FALSE))
|
|
return FALSE;
|
|
|
|
while (hcli) {
|
|
if (hcliPrev)
|
|
LocalUnlock (hcliPrev);
|
|
|
|
if ((pcli = (PCLILIST) LocalLock (hcli)) == NULL)
|
|
return FALSE;
|
|
|
|
phandle = (HANDLE *) pcli->info;
|
|
while (phandle < (HANDLE *)(pcli + 1)) {
|
|
if (*phandle == NULL) {
|
|
*phandle++ = hkey;
|
|
*phandle++ = hdata;
|
|
LocalUnlock (hcli);
|
|
return TRUE;
|
|
}
|
|
phandle++;
|
|
phandle++;
|
|
}
|
|
hcliPrev = hcli;
|
|
hcli = pcli->hcliNext;
|
|
lphead = (LPHANDLE)&pcli->hcliNext;
|
|
}
|
|
|
|
// not in the list.
|
|
hcli = LocalAlloc (LMEM_MOVEABLE | LMEM_ZEROINIT, sizeof (CLILIST));
|
|
if (hcli == NULL)
|
|
goto errRtn;
|
|
|
|
if ((pcli = (PCLILIST) LocalLock (hcli)) == NULL)
|
|
goto errRtn;
|
|
|
|
// set the link to this handle in the previous entry
|
|
*lphead = hcli;
|
|
if (hcliPrev)
|
|
LocalUnlock (hcliPrev);
|
|
|
|
phandle = (HANDLE *) pcli->info;
|
|
*phandle++ = hkey;
|
|
*phandle++ = hdata;
|
|
LocalUnlock (hcli);
|
|
return TRUE;
|
|
|
|
errRtn:
|
|
|
|
if (hcliPrev)
|
|
LocalUnlock (hcliPrev);
|
|
|
|
if (hcli)
|
|
LocalFree (hcli);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
// FindClient: finds a client and deletes the client if necessary.
|
|
INTERNAL_(HANDLE) FindClient
|
|
(
|
|
HANDLE hcli,
|
|
HANDLE hkey,
|
|
BOOL bDelete
|
|
)
|
|
{
|
|
HANDLE hcliPrev = NULL;
|
|
PCLILIST pcli;
|
|
HANDLE *phandle;
|
|
HANDLE hdata;
|
|
|
|
while (hcli) {
|
|
if ((pcli = (PCLILIST) LocalLock (hcli)) == NULL)
|
|
return FALSE;
|
|
|
|
phandle = (HANDLE *) pcli->info;
|
|
while (phandle < (HANDLE *)(pcli + 1)) {
|
|
if (*phandle == hkey) {
|
|
if (bDelete)
|
|
*phandle = NULL;
|
|
|
|
hdata = *++phandle;
|
|
LocalUnlock (hcli);
|
|
return hdata;
|
|
}
|
|
phandle++;
|
|
phandle++;
|
|
}
|
|
hcliPrev = hcli;
|
|
hcli = pcli->hcliNext;
|
|
LocalUnlock (hcliPrev);
|
|
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CDDEServer::SrvrExecute
|
|
//
|
|
// Synopsis: takes care of the WM_DDE_EXECUTE for the server.
|
|
//
|
|
// Effects: Parses the EXECUTE string, and determines what it should be
|
|
// done.
|
|
//
|
|
// Arguments: [hwnd] -- Server window
|
|
// [hdata] -- Handle to EXECUTE string
|
|
// [hwndClient] -- Client window
|
|
//
|
|
// Requires:
|
|
// hdata is an ANSI string. It was passed to us by a DDE client.
|
|
//
|
|
// Returns:
|
|
//
|
|
// Signals:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Derivation:
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 6-05-94 kevinro Commented/cleaned
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
INTERNAL CDDEServer::SrvrExecute
|
|
(
|
|
HWND hwnd,
|
|
HANDLE hdata,
|
|
HWND hwndClient
|
|
)
|
|
{
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%x _IN CDDESrvr::SrvrExecute(hwnd=%x,hdata=%x,hwndClient=%x)\n",
|
|
this,
|
|
hwnd,
|
|
hdata,
|
|
hwndClient));
|
|
|
|
ATOM aCmd;
|
|
BOOL fActivate;
|
|
|
|
LPSTR lpdata = NULL;
|
|
HANDLE hdup = NULL;
|
|
HRESULT hresult = E_UNEXPECTED;
|
|
|
|
LPSTR lpdocname;
|
|
LPSTR lptemplate;
|
|
LPCLIENT lpdocClient = NULL;
|
|
LPSTR lpnextarg;
|
|
LPSTR lpclassname;
|
|
LPSTR lpitemname;
|
|
LPSTR lpopt;
|
|
CLSID clsid;
|
|
WORD wCmdType;
|
|
BOOL bCreateInst = FALSE;
|
|
LPUNKNOWN pUnk = NULL;
|
|
|
|
LPPERSISTSTORAGE pPersistStg=NULL;
|
|
|
|
// REVIEW: if any methods called on the objects genarate DDE messages
|
|
// before we return from Execute, we will be in trouble.
|
|
|
|
|
|
// REVIEW: this code can be lot simplified if we do the argument scanning
|
|
// seperately and return the ptrs to the args. Rewrite later on.
|
|
|
|
ErrZS (hdup = UtDupGlobal (hdata,GMEM_MOVEABLE), E_OUTOFMEMORY);
|
|
|
|
ErrZS (lpdata = (LPSTR)GlobalLock (hdup), E_OUTOFMEMORY);
|
|
|
|
intrDebugOut((DEB_ITRACE,
|
|
"CDDESrvr::SrvrExecute(lpdata = %s)\n",lpdata));
|
|
|
|
if (*lpdata++ != '[') // commands start with the left sqaure bracket
|
|
{
|
|
hresult = ResultFromScode (RPC_E_DDE_SYNTAX_EXECUTE);
|
|
goto errRtn;
|
|
}
|
|
|
|
hresult = ReportResult(0, RPC_E_DDE_SYNTAX_EXECUTE, 0, 0);
|
|
// scan upto the first arg
|
|
if (!(wCmdType = ScanCommand (lpdata, WT_SRVR, &lpdocname, &aCmd)))
|
|
goto errRtn;
|
|
|
|
if (wCmdType == NON_OLE_COMMAND)
|
|
{
|
|
if (!UtilQueryProtocol (m_aClass, PROTOCOL_EXECUTE))
|
|
hresult = ReportResult(0, RPC_E_DDE_PROTOCOL, 0, 0);
|
|
else {
|
|
// REVIEW: StdExecute has to be mapped on to the StdCommandProtocol
|
|
// What command do we map on to?
|
|
|
|
AssertSz (0, "StdExecute is being called for server");
|
|
}
|
|
|
|
goto errRtn1;
|
|
}
|
|
|
|
if (aCmd == aStdExit)
|
|
{
|
|
if (*lpdocname)
|
|
goto errRtn1;
|
|
|
|
hresult = NOERROR;
|
|
// REVIEW: Do we have to initiate any terminations from the
|
|
// the servr side? Check how this works with excel.
|
|
goto end2;
|
|
}
|
|
|
|
// scan the next argument.
|
|
if (!(lpnextarg = ScanArg(lpdocname)))
|
|
goto errRtn;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// [StdShowItem("docname", "itemname"[, "true"])]
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
if (aCmd == aStdShowItem) {
|
|
|
|
// first find the documnet. If the doc does not exist, then
|
|
// blow it off.
|
|
|
|
if (!(lpdocClient = FindDocObj (lpdocname)))
|
|
goto errRtn1;
|
|
|
|
lpitemname = lpnextarg;
|
|
|
|
if( !(lpopt = ScanArg(lpitemname)))
|
|
goto errRtn1;
|
|
|
|
// scan for the optional parameter
|
|
// Optional can be only TRUE or FALSE.
|
|
|
|
fActivate = FALSE;
|
|
if (*lpopt) {
|
|
|
|
if( !(lpnextarg = ScanBoolArg (lpopt, (BOOL FAR *)&fActivate)))
|
|
goto errRtn1;
|
|
|
|
if (*lpnextarg)
|
|
goto errRtn1;
|
|
|
|
}
|
|
|
|
|
|
// scan it. But, igonre the arg.
|
|
hresult = lpdocClient->DocShowItem (lpitemname, !fActivate);
|
|
goto end2;
|
|
|
|
|
|
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// [StdCloseDocument ("docname")]
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
if (aCmd == aStdClose) {
|
|
if (!(lpdocClient = FindDocObj (lpdocname)))
|
|
goto errRtn1;
|
|
|
|
if (*lpnextarg)
|
|
goto errRtn1;
|
|
|
|
// REVIEW: Do we have to do anything for shutting down the
|
|
// the app? Is the client going to initiate the terminate?.
|
|
// if we need to initiate the terminates, make sure we post
|
|
// the ACK first.
|
|
|
|
lpdocClient->Revoke();
|
|
goto end2;
|
|
}
|
|
|
|
|
|
if (aCmd == aStdOpen)
|
|
{
|
|
// find if any doc level object is already registerd.
|
|
// if the object is registerd, then no need to call srvr app.
|
|
if (FindDocObj (lpdocname))
|
|
{
|
|
// A client has already opened the document or user opened the
|
|
// doc. We should do an addref to the docobj
|
|
|
|
#ifdef TRY
|
|
if (m_cSrvrClients == 0)
|
|
// Why are we doing this?
|
|
hresult = lpdocClient->m_lpoleObj->AddRef();
|
|
else
|
|
#endif
|
|
hresult = NOERROR;
|
|
goto end1;
|
|
}
|
|
}
|
|
|
|
if (aCmd == aStdCreate || aCmd == aStdCreateFromTemplate) {
|
|
lpclassname = lpdocname;
|
|
lpdocname = lpnextarg;
|
|
if( !(lpnextarg = ScanArg(lpdocname)))
|
|
goto errRtn1;
|
|
|
|
}
|
|
|
|
// check whether we can create/open more than one doc.
|
|
|
|
if ((m_fcfFlags == REGCLS_SINGLEUSE) &&
|
|
GetWindow (m_hwnd, GW_CHILD))
|
|
goto errRtn;
|
|
|
|
|
|
ErrZ (CLSIDFromAtom(m_aClass, &clsid));
|
|
|
|
|
|
//
|
|
// Generate a wide version of the name
|
|
//
|
|
|
|
WCHAR awcWideDocName[MAX_STR];
|
|
|
|
if (MultiByteToWideChar(CP_ACP,0,lpdocname,-1,awcWideDocName,MAX_STR) == FALSE)
|
|
{
|
|
Assert(!"Unable to convert characters");
|
|
hresult = E_UNEXPECTED;
|
|
goto errRtn;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// [StdOpenDocument ("docname")]
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Document does not exist.
|
|
if (aCmd == aStdOpen)
|
|
{
|
|
ErrRtnH (wClassesMatch (clsid, awcWideDocName));
|
|
ErrRtnH (wFileBind (awcWideDocName, &pUnk));
|
|
}
|
|
|
|
|
|
ErrRtnH (CreateInstance (clsid, awcWideDocName, lpdocname, pUnk, &lpdocClient, hwndClient));
|
|
bCreateInst = TRUE;
|
|
|
|
if (aCmd == aStdOpen)
|
|
{
|
|
// Temporary flag to indicate someone will INITIATE on this doc.
|
|
// The flag is reset after the INITITATE.
|
|
// This is Yet-Another-Excel-Hack. See ::QueryRevokeClassFactory
|
|
lpdocClient->m_fCreatedNotConnected = TRUE;
|
|
}
|
|
else
|
|
{
|
|
lpdocClient->m_fEmbed = TRUE;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// [StdNewDocument ("classname", "docname")]
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
if (aCmd == aStdCreate)
|
|
{
|
|
hresult = lpdocClient->DoInitNew();
|
|
lpdocClient->m_fCreatedNotConnected = TRUE;
|
|
goto end;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// [StdNewFormTemplate ("classname", "docname". "templatename)]
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
if (aCmd == aStdCreateFromTemplate)
|
|
{
|
|
ErrRtnH (lpdocClient->DoInitNew());
|
|
lpdocClient->m_fCreatedNotConnected = TRUE;
|
|
IPersistFile FAR * lpPF;
|
|
lptemplate = lpnextarg;
|
|
|
|
if(!(lpnextarg = ScanArg(lpnextarg)))
|
|
{
|
|
goto errRtn;
|
|
}
|
|
|
|
|
|
hresult = lpdocClient->m_lpoleObj->QueryInterface(IID_IPersistFile,(LPLPVOID)&lpPF);
|
|
if (hresult == NOERROR)
|
|
{
|
|
WCHAR awcWideTemplate[MAX_STR];
|
|
|
|
if (MultiByteToWideChar(CP_ACP,0,lpdocname,-1,awcWideTemplate,MAX_STR) != FALSE)
|
|
{
|
|
hresult = lpPF->Load(awcWideTemplate, 0);
|
|
}
|
|
else
|
|
{
|
|
Assert(!"Unable to convert characters");
|
|
lpPF->Release();
|
|
hresult = E_UNEXPECTED;
|
|
goto end;
|
|
}
|
|
|
|
lpPF->Release();
|
|
lpdocClient->m_fEmbed = TRUE;
|
|
}
|
|
else
|
|
{
|
|
goto end;
|
|
}
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// [StdEditDocument ("docname")]
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// REVIEW: Do we have to call InitNew for editing an embedded object
|
|
|
|
if (aCmd == aStdEdit)
|
|
{
|
|
lpdocClient->m_fEmbed = TRUE;
|
|
lpdocClient->m_fGotEditNoPokeNativeYet = TRUE;
|
|
lpdocClient->m_fCreatedNotConnected = TRUE;
|
|
goto end;
|
|
}
|
|
|
|
intrDebugOut((DEB_IERROR,
|
|
"%x CDDESrvr::SrvrExecute Unknown command\n",
|
|
this));
|
|
|
|
end:
|
|
|
|
if (hresult != NOERROR)
|
|
goto errRtn;
|
|
end1:
|
|
// make sure that the srg string is indeed terminated by
|
|
// NULL.
|
|
if (*lpnextarg)
|
|
{
|
|
hresult = RPC_E_DDE_SYNTAX_EXECUTE;
|
|
}
|
|
errRtn:
|
|
|
|
if ( hresult != NOERROR)
|
|
{
|
|
if (bCreateInst && lpdocClient)
|
|
{
|
|
lpdocClient->DestroyInstance ();
|
|
lpdocClient = NULL; //DestroyInstance invalidates the pointer
|
|
}
|
|
}
|
|
|
|
end2:
|
|
errRtn1:
|
|
|
|
if (lpdata)
|
|
GlobalUnlock (hdup);
|
|
|
|
if (hdup)
|
|
GlobalFree (hdup);
|
|
if (pUnk)
|
|
pUnk->Release();
|
|
|
|
if (pPersistStg)
|
|
pPersistStg->Release();
|
|
|
|
Assert (GetScode(hresult) != E_UNEXPECTED);
|
|
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%x _OUT CDDESrvr::SrvrExecute hresult=%x\n",
|
|
this,
|
|
hresult));
|
|
|
|
return hresult;
|
|
}
|
|
|
|
|
|
|
|
|
|
// Maybe CreateDocWindow
|
|
//
|
|
// Return NOERROR only if a doc window was created and it sent an ACK.
|
|
//
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: MaybeCreateDocWindow
|
|
//
|
|
// Synopsis: Determine if a DocWindow should be created
|
|
//
|
|
// Effects: Given a class, and a filename atom, determine if this thread
|
|
// should be the server for this request.
|
|
//
|
|
// Arguments: [aClass] -- Class of object (PROGID)
|
|
// [aFile] -- Filename (ATOM)
|
|
// [hwndDdeServer] -- HWND of CDDEServer
|
|
// [hwndSender] -- HWND of new requesting client
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Signals:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 6-29-94 kevinro Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
INTERNAL MaybeCreateDocWindow
|
|
(ATOM aClass,
|
|
ATOM aFile,
|
|
HWND hwndDdeServer,
|
|
HWND hwndSender)
|
|
{
|
|
CLSID clsid = CLSID_NULL;
|
|
LPUNKNOWN pUnk = NULL;
|
|
HWND hwndClient = NULL;
|
|
ULONG fAckSent = FALSE;
|
|
LPSRVR pDdeSrvr = NULL;
|
|
WCHAR szFile [MAX_STR];
|
|
BOOL fTrue = TRUE;
|
|
BOOL fRunningInSDI = FALSE;
|
|
HRESULT hresult = NOERROR;
|
|
IClassFactory *pcf = NULL;
|
|
IPersistFile *ppf = NULL;
|
|
DdeClassInfo ddeClassInfo;
|
|
|
|
intrDebugOut((DEB_DDE_INIT,
|
|
"MaybeCreateDocWindow(aClass=%x(%ws),aFile=%x,"
|
|
"hwndDdeServer=%x,hwndSender=%x\n",
|
|
aClass,wAtomName(aClass),aFile,hwndDdeServer,hwndSender));
|
|
|
|
//
|
|
// If the window isn't valid, it would be very bad.
|
|
//
|
|
if (!IsWindowValid(hwndDdeServer))
|
|
{
|
|
intrDebugOut((DEB_DDE_INIT,
|
|
"MaybeCreateDocWindow: hwndDdeServer is invalid\n"));
|
|
hresult = E_UNEXPECTED;
|
|
goto exitRtn;
|
|
}
|
|
|
|
//
|
|
// We need the filename, which is passed in an Atom
|
|
//
|
|
Assert (IsFile (aFile));
|
|
if (GlobalGetAtomName(aFile,szFile,MAX_STR) == 0)
|
|
{
|
|
//
|
|
// The filename was not valid
|
|
//
|
|
hresult = S_FALSE;
|
|
intrDebugOut((DEB_IERROR,
|
|
"MaybeCreateDocWindow Invalid file atom\n"));
|
|
goto exitRtn;
|
|
}
|
|
|
|
intrDebugOut((DEB_DDE_INIT,
|
|
"MaybeCreateDocWindow File=(%ws)\n",
|
|
WIDECHECK(szFile)));
|
|
|
|
//
|
|
// Get the class of the object. The class was passed as an atom
|
|
// in the INITIATE message.
|
|
//
|
|
if (CLSIDFromAtomWithTreatAs (&aClass, &clsid, NULL))
|
|
{
|
|
intrDebugOut((DEB_IERROR,
|
|
"MaybeCreateDocWindow CLSIDFromAtom failed\n"));
|
|
|
|
hresult = S_FALSE;
|
|
goto exitRtn;
|
|
}
|
|
|
|
if (CoIsOle1Class(clsid))
|
|
{
|
|
// we shouldn't even be looking at this INIT message
|
|
hresult = S_FALSE;
|
|
intrDebugOut((DEB_DDE_INIT,
|
|
"MaybeCreateDocWindow Its an OLE 1.0 class\n"));
|
|
goto exitRtn;
|
|
}
|
|
|
|
//
|
|
// First of three cases is to see if the object is running in our
|
|
// local apartment. If it is, then this is the object we need to create
|
|
// a DDEServer for.
|
|
//
|
|
// Otherwise, We are going to try and load this file.
|
|
// Therefore, we need the class factory from the CFT.
|
|
//
|
|
// GetClassInformationForDde won't find a match if the class factory was
|
|
// single use, and is now hidden or invalid.
|
|
//
|
|
// If there was no class information available, then we are going to
|
|
// check to see if the object is in the local ROT. If it is in the
|
|
// local ROT, then we will use it, since it is registered and
|
|
// available for use by others
|
|
//
|
|
|
|
ddeClassInfo.dwContextMask = CLSCTX_LOCAL_SERVER | CLSCTX_INPROC_SERVER;
|
|
ddeClassInfo.fClaimFactory = TRUE;
|
|
|
|
if ( GetLocalRunningObjectForDde(szFile, &pUnk) == NOERROR)
|
|
{
|
|
intrDebugOut((DEB_DDE_INIT,
|
|
"Found %ws in ROT\n",WIDECHECK(szFile)));
|
|
//
|
|
// Elsewhere in the code, we need to know if this is an SDI server.
|
|
// The old code determined this by detecting that there is a running
|
|
// object, and there was no class factory registered.
|
|
// This is sick, and obscene. Compatibilities says we need to get the
|
|
// class info anyway. However, we don't want to claim it.
|
|
//
|
|
|
|
ddeClassInfo.fClaimFactory = FALSE;
|
|
fRunningInSDI = !GetClassInformationForDde(clsid,&ddeClassInfo);
|
|
}
|
|
else if (!GetClassInformationForDde(clsid,&ddeClassInfo))
|
|
{
|
|
intrDebugOut((DEB_IERROR,
|
|
"No class registered for %ws\n",WIDECHECK(szFile)));
|
|
|
|
hresult = S_FALSE;
|
|
goto exitRtn;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Otherwise, we are registered as the server for this class. This
|
|
// means we can create this object.
|
|
//
|
|
// A 1.0 client will have launched the server with a command line
|
|
// like server.exe -Embedding filename The server ignored the filename
|
|
// so now we must make it load the file by binding the moniker.
|
|
//
|
|
// KevinRo: The old code did a bind moniker here, on the filename,
|
|
// which went through the ROT, didn't find the object, so went for
|
|
// a server. This isn't terribly safe, since we could end up binding
|
|
// out of process when we really didn't mean to. So, I have made this
|
|
// routine just use the ClassFactory we retrieve from the
|
|
// local class factory table.
|
|
//
|
|
|
|
intrDebugOut((DEB_DDE_INIT,
|
|
"Found classinfo: Loading %ws\n",WIDECHECK(szFile)));
|
|
|
|
|
|
//
|
|
// Need to insure that the server doesn't go away on us. The following
|
|
// tells the server not to destroy itself.
|
|
//
|
|
SSSendMessage(hwndDdeServer,WM_DONOTDESTROY,TRUE,0);
|
|
|
|
intrAssert(ddeClassInfo.punk != NULL);
|
|
pcf = (IClassFactory *) ddeClassInfo.punk;
|
|
|
|
hresult = pcf->CreateInstance(NULL,IID_IUnknown,(void **)&pUnk);
|
|
|
|
if (hresult != NOERROR)
|
|
{
|
|
intrDebugOut((DEB_IERROR,
|
|
"MaybeCreateDocWindow CreateInstancefailed File=(%ws)\n",
|
|
WIDECHECK(szFile)));
|
|
goto sndMsg;
|
|
}
|
|
|
|
//
|
|
// Get the IPersistFile interface, and ask the object to load
|
|
// itself.
|
|
//
|
|
hresult = pUnk->QueryInterface(IID_IPersistFile,(void **)&ppf);
|
|
if (hresult != NOERROR)
|
|
{
|
|
intrDebugOut((DEB_IERROR,
|
|
"MaybeCreateDocWindow QI IPF failed File=(%ws)\n",
|
|
WIDECHECK(szFile)));
|
|
goto sndMsg;
|
|
}
|
|
//
|
|
// Attempt to load the object. The flags STGM_READWRITE are the
|
|
// same default values used by a standard bind context.
|
|
//
|
|
hresult = ppf->Load(szFile,STGM_READWRITE);
|
|
if (hresult != NOERROR)
|
|
{
|
|
intrDebugOut((DEB_IERROR,
|
|
"MaybeCreateDocWindow ppf->Load(%ws) failed %x\n",
|
|
WIDECHECK(szFile),
|
|
hresult));
|
|
goto sndMsg;
|
|
}
|
|
sndMsg:
|
|
SSSendMessage(hwndDdeServer,WM_DONOTDESTROY,FALSE,0);
|
|
if (hresult != NOERROR)
|
|
{
|
|
goto exitRtn;
|
|
|
|
}
|
|
intrDebugOut((DEB_DDE_INIT,
|
|
"Loading %ws complete\n",WIDECHECK(szFile)));
|
|
|
|
}
|
|
|
|
|
|
intrAssert(IsWindowValid(hwndDdeServer));
|
|
intrAssert (pUnk);
|
|
|
|
pDdeSrvr = (LPSRVR) GetWindowLong (hwndDdeServer, 0);
|
|
if (pDdeSrvr == NULL)
|
|
{
|
|
intrAssert(pDdeSrvr != NULL);
|
|
hresult = E_UNEXPECTED;
|
|
goto exitRtn;
|
|
}
|
|
|
|
// This actually creates the doc window as a child of the server window
|
|
// Do not set the client site becuase this is a link.
|
|
hresult = CDefClient::Create (pDdeSrvr,
|
|
pUnk,
|
|
szFile,
|
|
/*fSetClientSite*/FALSE,
|
|
/*fDoAdvise*/TRUE,
|
|
fRunningInSDI,
|
|
&hwndClient);
|
|
|
|
if (hresult != NOERROR)
|
|
{
|
|
intrDebugOut((DEB_IERROR,
|
|
"MaybeCreateDocWindow CDefClient::Create failed %x\n",
|
|
hresult));
|
|
goto exitRtn;
|
|
}
|
|
|
|
Assert (IsWindowValid (hwndClient));
|
|
|
|
//
|
|
// Pass along the original DDE_INIT to the newly created window.
|
|
// That window should respond by sending an ACK to the 1.0 client.
|
|
//
|
|
fAckSent = SSSendMessage (hwndClient,
|
|
WM_DDE_INITIATE,
|
|
(UINT) hwndSender,
|
|
MAKELONG(aClass, aFile));
|
|
if (!fAckSent)
|
|
{
|
|
intrDebugOut((DEB_IERROR,
|
|
"MaybeCreateDocWindow !fAckSent\n"));
|
|
hresult = CO_E_APPDIDNTREG;
|
|
}
|
|
|
|
exitRtn:
|
|
|
|
if (ppf)
|
|
{
|
|
ppf->Release();
|
|
}
|
|
if (pUnk)
|
|
{
|
|
pUnk->Release();
|
|
}
|
|
if (pcf)
|
|
{
|
|
pcf->Release();
|
|
}
|
|
|
|
intrDebugOut((DEB_DDE_INIT,
|
|
"MaybeCreateDocWindow returns %x\n",
|
|
hresult));
|
|
return hresult;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: SendMsgToChildren
|
|
//
|
|
// Synopsis: This routine sends the msg to all child windows.
|
|
//
|
|
// Arguments: [hwnd] -- Hwnd of parent window
|
|
// [msg] -- Message and parameters to send
|
|
// [wParam] --
|
|
// [lParam] --
|
|
//
|
|
// Notes: This routine will stop on the first non-zero return code.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL SendMsgToChildren (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
intrDebugOut((DEB_ITRACE,
|
|
"0 _IN SendMsgToChildren(hwnd=%x,msg=%x,wParam=%x,lParam=%x)\n",
|
|
hwnd,msg,wParam,lParam));
|
|
|
|
BOOL fAckSent = FALSE;
|
|
|
|
hwnd = GetWindow(hwnd, GW_CHILD);
|
|
|
|
//
|
|
// This routine is to be called only from one place, which is
|
|
// in the handling of WM_DDE_INITIATE. Because of that, we will terminate
|
|
// the loop on the first non-zero return code.
|
|
//
|
|
Assert (msg == WM_DDE_INITIATE);
|
|
|
|
while (hwnd)
|
|
{
|
|
intrDebugOut((DEB_ITRACE," SendMsgToChildren send to hwnd=%x\n",hwnd));
|
|
|
|
if (fAckSent = (1L==SSSendMessage (hwnd, msg, wParam, lParam)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hwnd = GetWindow (hwnd, GW_HWNDNEXT);
|
|
}
|
|
|
|
intrDebugOut((DEB_ITRACE,"0 OUT SendMsgToChildren returns %x\n",fAckSent));
|
|
return(fAckSent);
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: SendInitMsgToChildren
|
|
//
|
|
// Synopsis: Sends an init message to all child windows of the hwnd
|
|
//
|
|
// Effects: This routine will send an init message to all children
|
|
// of the given window. It is assuming that the lParam is
|
|
// the atom that contains the topic (ie filename) of the
|
|
// object being looked for.
|
|
//
|
|
// Arguments: [hwnd] -- hwnd of server window
|
|
// [msg] -- MSG to send
|
|
// [wParam] -- hwnd of client window
|
|
// [lParam] -- HIWORD(lParam) is atom of filename
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Signals:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 6-28-94 kevinro Commented
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL SendInitMsgToChildren (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
intrDebugOut((DEB_DDE_INIT,
|
|
"0 _IN SendInitMsgToChildren(hwnd=%x,msg=%x,wParam=%x,lParam=%x)\n",
|
|
hwnd,msg,wParam,lParam));
|
|
|
|
BOOL fAckSent = FALSE;
|
|
|
|
fAckSent = SendMsgToChildren(hwnd,msg,wParam,lParam);
|
|
|
|
//
|
|
// If no windows acknowledged, then we might need to create a doc window
|
|
//
|
|
if (!fAckSent)
|
|
{
|
|
ATOM aTopic = HIWORD(lParam);
|
|
Assert (IsAtom(aTopic));
|
|
|
|
// if someone's trying to initiate on a filename, i.e., for a link
|
|
// then create the doc window on demand because 2.0 servers do not
|
|
// register doc windows. They don't even accept "-Embedding filename"
|
|
// on the command line
|
|
if (aTopic != aOLE && aTopic != aSysTopic && IsFile (aTopic))
|
|
{
|
|
intrDebugOut((DEB_DDE_INIT," Initiate for link %ws\n",wAtomName(aTopic)));
|
|
HRESULT hresult = MaybeCreateDocWindow (LOWORD(lParam), aTopic,
|
|
hwnd, (HWND)wParam);
|
|
|
|
fAckSent = (NOERROR==hresult);
|
|
}
|
|
}
|
|
intrDebugOut((DEB_DDE_INIT,
|
|
"0 _OUT SendInitMsgToChildren fAckSent=%x\n",fAckSent));
|
|
return fAckSent;
|
|
}
|
|
|
|
|
|
|
|
INTERNAL_(HRESULT) RequestDataStd
|
|
(
|
|
ATOM aItem,
|
|
LPHANDLE lphdde
|
|
)
|
|
{
|
|
|
|
|
|
HANDLE hnew = NULL;
|
|
|
|
if (!aItem)
|
|
goto errRtn;
|
|
|
|
if (aItem == aEditItems){
|
|
hnew = MakeGlobal ("StdHostNames\tStdDocDimensions\tStdTargetDevice");
|
|
goto PostData;
|
|
|
|
}
|
|
|
|
if (aItem == aProtocols) {
|
|
hnew = MakeGlobal ("Embedding\tStdFileEditing");
|
|
goto PostData;
|
|
}
|
|
|
|
if (aItem == aTopics) {
|
|
hnew = MakeGlobal ("Doc");
|
|
goto PostData;
|
|
}
|
|
|
|
if (aItem == aFormats) {
|
|
hnew = MakeGlobal ("Picture\tBitmap");
|
|
goto PostData;
|
|
}
|
|
|
|
if (aItem == aStatus) {
|
|
hnew = MakeGlobal ("Ready");
|
|
goto PostData;
|
|
}
|
|
|
|
// format we do not understand.
|
|
goto errRtn;
|
|
|
|
PostData:
|
|
|
|
// Duplicate the DDE data
|
|
if (MakeDDEData (hnew, CF_TEXT, lphdde, TRUE)){
|
|
// !!! why are we duplicating the atom.
|
|
DuplicateAtom (aItem);
|
|
return NOERROR;
|
|
}
|
|
errRtn:
|
|
return ReportResult(0, S_FALSE, 0, 0);
|
|
}
|
|
|
|
|
|
//IsSingleServerInstance: returns true if the app is single server app else
|
|
//false.
|
|
|
|
INTERNAL_(BOOL) IsSingleServerInstance ()
|
|
{
|
|
HWND hwnd;
|
|
WORD cnt = 0;
|
|
HTASK hTask;
|
|
DdeCHAR buf[MAX_STR];
|
|
|
|
hwnd = GetWindow (GetDesktopWindow(), GW_CHILD);
|
|
hTask = GetCurrentThreadId();
|
|
|
|
while (hwnd) {
|
|
if (hTask == ((HTASK) GetWindowThreadProcessId (hwnd,NULL))) {
|
|
DdeGetClassName (hwnd, buf, MAX_STR);
|
|
if (Ddelstrcmp (buf, SRVR_CLASS) == 0)
|
|
cnt++;
|
|
}
|
|
hwnd = GetWindow (hwnd, GW_HWNDNEXT);
|
|
}
|
|
#ifdef FIREWALLS
|
|
AssertSz (cnt > 0, "srvr window instance count is zero");
|
|
#endif
|
|
if (cnt == 1)
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
// QueryRevokeClassFactory: returns FALSE if there are clients
|
|
// connected tothis class factory;
|
|
INTERNAL_(BOOL) CDDEServer::QueryRevokeClassFactory ()
|
|
{
|
|
|
|
HWND hwnd;
|
|
LPCLIENT lpclient;
|
|
|
|
Assert (IsWindow (m_hwnd));
|
|
hwnd = GetWindow (m_hwnd, GW_CHILD);
|
|
while (hwnd)
|
|
{
|
|
Assert (IsWindow (hwnd));
|
|
lpclient = (LPCLIENT)GetWindowLong (hwnd, 0);
|
|
if (lpclient->m_cClients != 0 || lpclient->m_fCreatedNotConnected)
|
|
return FALSE;
|
|
hwnd = GetWindow (hwnd, GW_HWNDNEXT);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CDDEServer::CreateInstance
|
|
//
|
|
// Synopsis: Create an instance of a document
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: [lpclassName] --
|
|
// [lpWidedocName] --
|
|
// [lpdocName] --
|
|
// [pUnk] --
|
|
// [lplpdocClient] --
|
|
// [hwndClient] --
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Signals:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Derivation:
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 5-30-94 kevinro Commented/cleaned
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
INTERNAL CDDEServer::CreateInstance
|
|
(
|
|
REFCLSID lpclassName,
|
|
LPOLESTR lpWidedocName,
|
|
LPSTR lpdocName,
|
|
LPUNKNOWN pUnk,
|
|
LPCLIENT FAR* lplpdocClient,
|
|
HWND hwndClient)
|
|
{
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%p _IN CDDEServer::CreateInstance(lpWidedocName=%ws,hwndClient=%x)\n",
|
|
this,
|
|
WIDECHECK(lpWidedocName),
|
|
hwndClient));
|
|
|
|
|
|
LPUNKNOWN pUnk2=NULL;
|
|
LPOLEOBJECT lpoleObj= NULL; // unknown object
|
|
HRESULT hresult;
|
|
|
|
ChkS(this);
|
|
|
|
if (NULL==pUnk)
|
|
{
|
|
Assert (m_pClassFactory);
|
|
hresult = m_pClassFactory->CreateInstance (NULL, IID_IUnknown, (LPLPVOID)&pUnk2);
|
|
|
|
if (hresult != NOERROR)
|
|
{
|
|
return hresult;
|
|
}
|
|
|
|
// Now that we have *used* the DDE server window, we can unlock
|
|
// the server.
|
|
// The OLE1 OleLockServer API opens a dummy DDE system channel
|
|
// and just leaves it open until OleunlockServer is called.
|
|
// Since we have now used this channel, we know it was not created
|
|
// for the purpose of locking the server.
|
|
this->Lock (FALSE, hwndClient);
|
|
|
|
// if it is an SDI app, we must revoke the ClassFactory after using it
|
|
// it is only good for "one-shot" createinstance call.
|
|
if (m_fcfFlags == REGCLS_SINGLEUSE)
|
|
{
|
|
m_pClassFactory->Release(); // done with the ClassFactory
|
|
Puts ("NULLing m_pCF\r\n");
|
|
m_pClassFactory = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pUnk2 = pUnk;
|
|
pUnk->AddRef();
|
|
}
|
|
|
|
hresult = CDefClient::Create ((LPSRVR)this,
|
|
pUnk2,
|
|
lpWidedocName,
|
|
/*fSetClientSite*/FALSE,
|
|
/*fDoAdvise*/pUnk!=NULL);
|
|
|
|
intrAssert (pUnk2 != NULL);
|
|
if (pUnk2 != NULL)
|
|
{
|
|
pUnk2->Release();
|
|
}
|
|
|
|
pUnk2 = NULL;
|
|
|
|
// REVIEW: error recovery
|
|
if (!(*lplpdocClient = FindDocObj (lpdocName)))
|
|
{
|
|
intrAssert(!"Document created but not found");
|
|
}
|
|
else
|
|
{
|
|
// set the server instance flag so that WM_DDE_INITIATE will not icrement
|
|
// the ref count. (EXCEL BUG)
|
|
(*lplpdocClient)->m_bCreateInst = TRUE;
|
|
}
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%p _OUT CDDEServer::CreateInstance hresult=%x\n",
|
|
this,hresult));
|
|
return hresult;
|
|
}
|
|
|
|
|
|
INTERNAL_(void) CDDEServer::Lock
|
|
(BOOL fLock, // lock or unlock?
|
|
HWND hwndClient) // on behalf of which window?
|
|
{
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%p _IN CDDEServer::Lock(fLock=%x,hwndCient=%x)\n",
|
|
this,
|
|
fLock,
|
|
hwndClient));
|
|
|
|
VDATEHEAP();
|
|
BOOL fIsLocked = (BOOL) FindClient (m_hcli, hwndClient, /*fDelete*/FALSE);
|
|
|
|
if (fLock && !fIsLocked)
|
|
{
|
|
if (m_pClassFactory)
|
|
{
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%p ::Locking %x\n",
|
|
this,
|
|
m_pClassFactory));
|
|
|
|
m_pClassFactory->LockServer (TRUE);
|
|
// Only way to change the data associated with a client window
|
|
// is to delete it and re-add it with the new data.
|
|
FindClient (m_hcli, hwndClient, /*fDelete*/ TRUE);
|
|
AddClient (&m_hcli, hwndClient, (HANDLE) TRUE); // mark as locked
|
|
}
|
|
}
|
|
else if (!fLock && fIsLocked)
|
|
{
|
|
if (m_pClassFactory)
|
|
{
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%p ::UnLocking %x\n",
|
|
this,
|
|
m_pClassFactory));
|
|
m_pClassFactory->LockServer (FALSE);
|
|
FindClient (m_hcli, hwndClient, /*fDelete*/ TRUE);
|
|
AddClient (&m_hcli, hwndClient, (HANDLE) FALSE); //mark as unlocked
|
|
}
|
|
}
|
|
VDATEHEAP();
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%p _OUT CDDEServer::Lock(fLock=%x,hwndCient=%x)\n",
|
|
this,
|
|
fLock,
|
|
hwndClient));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
INTERNAL CDefClient::DestroyInstance
|
|
(void)
|
|
{
|
|
Puts ("DestroyInstance\r\n");
|
|
// We just created the instance. we ran into error.
|
|
// just call Release.
|
|
m_pUnkOuter->AddRef();
|
|
ReleaseObjPtrs();
|
|
Verify (0==m_pUnkOuter->Release());
|
|
// "this" should be deleted now
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
|
|
INTERNAL CDefClient::SetClientSite
|
|
(void)
|
|
{
|
|
HRESULT hresult = m_lpoleObj->SetClientSite (&m_OleClientSite);
|
|
if (hresult==NOERROR)
|
|
{
|
|
m_fDidSetClientSite = TRUE;
|
|
}
|
|
else
|
|
{
|
|
Warn ("SetClientSite failed");
|
|
}
|
|
return hresult;
|
|
}
|
|
|
|
|
|
// implementations of IRpcStubBuffer methods
|
|
STDMETHODIMP CDdeServerCallMgr::QueryInterface
|
|
( REFIID iid, LPVOID * ppvObj )
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG)CDdeServerCallMgr::AddRef ()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG)CDdeServerCallMgr::Release ()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDdeServerCallMgr::Connect
|
|
(IUnknown * pUnkServer )
|
|
{
|
|
// do nothing
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP_(void) CDdeServerCallMgr::Disconnect
|
|
()
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
STDMETHODIMP_(IRpcStubBuffer*) CDdeServerCallMgr::IsIIDSupported
|
|
(REFIID riid)
|
|
{
|
|
// do nothing
|
|
return NULL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CDdeServerCallMgr::CountRefs
|
|
()
|
|
{
|
|
// do nothing
|
|
return 1;
|
|
}
|
|
|
|
STDMETHODIMP CDdeServerCallMgr::DebugServerQueryInterface
|
|
(void ** ppv )
|
|
{
|
|
// do nothing
|
|
*ppv = NULL;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(void) CDdeServerCallMgr::DebugServerRelease
|
|
(void * pv)
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
STDMETHODIMP CDdeServerCallMgr::Invoke
|
|
(RPCOLEMESSAGE *_prpcmsg, IRpcChannelBuffer *_pRpcChannelBuffer)
|
|
{
|
|
DISPATCHDATA *pdispdata = (PDISPATCHDATA) _prpcmsg->Buffer;
|
|
return DispatchCall( pdispdata );
|
|
}
|
|
|
|
|
|
// Provided IRpcChannelBuffer methods (for callback methods side)
|
|
STDMETHODIMP CDdeServerCallMgr::GetBuffer(
|
|
/* [in] */ RPCOLEMESSAGE __RPC_FAR *pMessage,
|
|
/* [in] */ REFIID riid)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDdeServerCallMgr::SendReceive(
|
|
/* [out][in] */ RPCOLEMESSAGE __RPC_FAR *pMessage,
|
|
/* [out] */ ULONG __RPC_FAR *pStatus)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDdeServerCallMgr::FreeBuffer(
|
|
/* [in] */ RPCOLEMESSAGE __RPC_FAR *pMessage)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDdeServerCallMgr::GetDestCtx(
|
|
/* [out] */ DWORD __RPC_FAR *pdwDestContext,
|
|
/* [out] */ void __RPC_FAR *__RPC_FAR *ppvDestContext)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDdeServerCallMgr::IsConnected( void)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDdeServerCallMgr::SendReceive2(
|
|
/* [out][in] */ RPCOLEMESSAGE __RPC_FAR *pMessage,
|
|
/* [out] */ ULONG __RPC_FAR *pStatus)
|
|
{
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%p _IN CDdeServerCallMgr::SendReceive2(pMessage=%x,pStatus=%x)\n",
|
|
this,
|
|
pMessage,
|
|
pStatus));
|
|
|
|
DDECALLDATA *pCD = ((DDECALLDATA *) pMessage->Buffer);
|
|
|
|
if(!PostMessageToClient(pCD->hwndSvr,
|
|
pCD->wMsg,
|
|
pCD->wParam,
|
|
pCD->lParam))
|
|
{
|
|
intrDebugOut((DEB_ITRACE, "SendRecieve2(%x)PostMessageToClient failed", this));
|
|
return RPC_E_SERVER_DIED;
|
|
}
|
|
|
|
|
|
CAptCallCtrl *pCallCtrl = GetAptCallCtrl();
|
|
|
|
CCliModalLoop *pCML = pCallCtrl->GetTopCML();
|
|
|
|
HRESULT hres = S_OK;
|
|
BOOL fWait = !(m_pDefClient->m_CallState == SERVERCALLEX_ISHANDLED);
|
|
|
|
while (fWait)
|
|
{
|
|
HRESULT hr = OleModalLoopBlockFn(NULL, pCML, NULL);
|
|
|
|
if (m_pDefClient->m_CallState == SERVERCALLEX_ISHANDLED)
|
|
{
|
|
fWait = FALSE;
|
|
}
|
|
else if (hr != RPC_S_CALLPENDING)
|
|
{
|
|
fWait = FALSE;
|
|
hres = hr; // return result from OleModalLoopBlockFn()
|
|
}
|
|
}
|
|
|
|
if (FAILED(hres))
|
|
{
|
|
intrDebugOut((DEB_ITRACE, "**** CDdeServerCallMgr::SendReceive2 OleModalLoopBlockFn returned %x ***\n", hres));
|
|
}
|
|
|
|
intrDebugOut((DEB_ITRACE,
|
|
"%p _OUT CDdeServerCallMgr::SendReceive2(pMessage=%x,pStatus=%x)\n",
|
|
this,
|
|
pMessage,
|
|
pStatus));
|
|
|
|
return hres;
|
|
}
|