/****************************** 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 // #include "cmacs.h" #include // for RemDdeRevokeClassFactory and HDDESRVR #include #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; }