diff options
Diffstat (limited to 'private/ole32/com/dde/server/srvr.cxx')
-rw-r--r-- | private/ole32/com/dde/server/srvr.cxx | 2497 |
1 files changed, 2497 insertions, 0 deletions
diff --git a/private/ole32/com/dde/server/srvr.cxx b/private/ole32/com/dde/server/srvr.cxx new file mode 100644 index 000000000..2f2d8eb40 --- /dev/null +++ b/private/ole32/com/dde/server/srvr.cxx @@ -0,0 +1,2497 @@ + +/****************************** 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; +} |