//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1993. // // File: sobjact.cxx // // Contents: Activation Functions used by object servers. // // Functions: CoRegisterClassObject // CoRevokeClassObject // CoAddRefServerProcess // CoReleaseServerProcess // CoSuspendClassObjects // // Classes: CObjServer // // History: 12-May-93 Ricksa Created // //-------------------------------------------------------------------------- #include #include #include #include "..\..\ole232\stdimpl\handler.hxx" #include "resolver.hxx" #include "smstg.hxx" #include "objact.hxx" #include "service.hxx" #include #include CObjServer *gpMTAObjServer = NULL; extern INTERNAL CreateCommonDdeWindow(void); //+------------------------------------------------------------------------- // // Function: CoRegisterClassObject, public // // Synopsis: Register a class object in the requested context // // Arguments: [rclsid] - class ID // [pUnk] - class object // [dwContext] - context to register it in // [flags] - single/multiple use. // [lpdwRegister] - registration cookie // // Returns: S_OK - object is successfully registered // // Algorithm: Validate the parmeters. The get the class factory interface. // Then add the class object to the list and finally notify // the SCM that the service is started. // // History: 12-May-93 Ricksa Created // 26-Jul-94 AndyH #20843 - restarting OLE in the shared WOW // //-------------------------------------------------------------------------- STDAPI CoRegisterClassObject( REFCLSID rclsid, IUnknown FAR* pUnk, DWORD dwContext, DWORD flags, LPDWORD lpdwRegister) { HRESULT hr; #ifdef WX86OLE BOOL fContextArgBad; #endif OLETRACEIN((API_CoRegisterClassObject, PARAMFMT("rclsid= %I, pUnk= %p, dwContext= %x, flags= %x, lpdwRegister= %p"), &rclsid, pUnk, dwContext, flags, lpdwRegister)); TRACECALL(TRACE_ACTIVATION, "CoRegisterClassObject"); if (!IsApartmentInitialized()) { hr = CO_E_NOTINITIALIZED; goto errRtn; } // Validate the out parameter if (!IsValidPtrOut(lpdwRegister, sizeof(DWORD))) { CairoleAssert(IsValidPtrOut(lpdwRegister, sizeof(DWORD)) && "CoRegisterClassObject invalid registration ptr"); hr = E_INVALIDARG; goto errRtn; } *lpdwRegister = 0; // Validate the pUnk if (!IsValidInterface(pUnk)) { CairoleAssert(IsValidInterface(pUnk) && "CoRegisterClassObject invalid pUnk"); hr = E_INVALIDARG; goto errRtn; } // Hook the pUnk CALLHOOKOBJECT(S_OK,rclsid,IID_IClassFactory,&pUnk); // Validate context flags #ifdef WX86OLE if (gcwx86.IsWx86Enabled()) { fContextArgBad = (dwContext & (~(CLSCTX_ALL | CLSCTX_INPROC_SERVER16 | CLSCTX_INPROC_SERVERX86) | CLSCTX_INPROC_HANDLER | CLSCTX_INPROC_HANDLERX86)); } else { fContextArgBad = (dwContext & (~(CLSCTX_ALL | CLSCTX_INPROC_SERVER16) | CLSCTX_INPROC_HANDLER)); } if (fContextArgBad) #else if ((dwContext & (~(CLSCTX_ALL | CLSCTX_INPROC_SERVER16) | CLSCTX_INPROC_HANDLER)) != 0) #endif { hr = E_INVALIDARG; goto errRtn; } // Validate flag flags if (flags > (REGCLS_SUSPENDED | REGCLS_MULTI_SEPARATE | REGCLS_SURROGATE)) { hr = E_INVALIDARG; goto errRtn; } if ((flags & REGCLS_SURROGATE) && !(dwContext & CLSCTX_LOCAL_SERVER)) { hr = E_INVALIDARG; goto errRtn; } #ifdef WX86OLE if (flags & REGCLS_MULTIPLEUSE) { if (gcwx86.IsWx86Enabled()) { if (dwContext & CLSCTX_INPROC_SERVERX86) { dwContext |= CLSCTX_INPROC_HANDLERX86 | CLSCTX_INPROC_HANDLER; } else { dwContext |= CLSCTX_INPROC; } } else { dwContext |= CLSCTX_INPROC; } } #else if (flags & REGCLS_MULTIPLEUSE) { dwContext |= CLSCTX_INPROC; } #endif if (dwContext & CLSCTX_LOCAL_SERVER) { // thread safe incase we are in MultiThreaded model. COleStaticLock lck(gmxsOleMisc); // Make sure an instance of CObjServer exists for this thread. // The SCM will call back on it to activate objects. CObjServer *pObjServer = GetObjServer(); if (pObjServer == NULL) { COleTls Tls; // no activation server for this apartment yet, go make one now. HRESULT hr = E_OUTOFMEMORY; pObjServer = new CObjServer(hr); if (FAILED(hr)) { delete pObjServer; return hr; } // If we want to service OLE1 clients, we need to create the // common Dde window now if it has not already been done. if( !(Tls->dwFlags & OLETLS_DISABLE_OLE1DDE) ) { CreateCommonDdeWindow(); } } } // Put our object in the server table hr = gdllcacheInprocSrv.RegisterServer(rclsid, pUnk, flags, dwContext, lpdwRegister); errRtn: OLETRACEOUT((API_CoRegisterClassObject, hr)); return hr; } //+------------------------------------------------------------------------- // // Function: CoRevokeClassObject, public // // Synopsis: Revoke a previously registered class object // // Arguments: [dwRegister] - registration key returned from CoRegister... // // Returns: S_OK - class successfully deregistered. // // Algorithm: Ask cache to deregister the class object. // // History: 12-May-93 Ricksa Created // //-------------------------------------------------------------------------- STDAPI CoRevokeClassObject(DWORD dwRegister) { OLETRACEIN((API_CoRevokeClassObject, PARAMFMT("dwRegister= %x"), dwRegister)); TRACECALL(TRACE_ACTIVATION, "CoRevokeClassObject"); HRESULT hr = CO_E_NOTINITIALIZED; if (IsApartmentInitialized()) { // Try to revoke the object hr = gdllcacheInprocSrv.Revoke(dwRegister); } OLETRACEOUT((API_CoRevokeClassObject, hr)); return hr; } //+------------------------------------------------------------------------- // // Function: CoAddRefServerProcess, public // // Synopsis: Increments the global per-process server reference count. // See CDllCache::AddRefServerProcess for more detail. // // History: 17-Apr-96 Rickhi Created // //-------------------------------------------------------------------------- STDAPI_(ULONG) CoAddRefServerProcess(void) { return gdllcacheInprocSrv.AddRefServerProcess(); } //+------------------------------------------------------------------------- // // Function: CoReleaseServerProcess, public // // Synopsis: Decrements the global per-process server reference count. // See CDllCache::ReleaseServerProcess for more detail. // // History: 17-Apr-96 Rickhi Created // //-------------------------------------------------------------------------- STDAPI_(ULONG) CoReleaseServerProcess(void) { return gdllcacheInprocSrv.ReleaseServerProcess(); } //+------------------------------------------------------------------------- // // Function: CoSuspendClassObjects, public // // Synopsis: suspends all registered LOCAL_SERVER class objects for this // process so that no new activation calls from the SCM will // be accepted. // // History: 17-Apr-96 Rickhi Created // //-------------------------------------------------------------------------- STDAPI CoSuspendClassObjects(void) { return gdllcacheInprocSrv.SuspendProcessClassObjects(); } //+------------------------------------------------------------------------- // // Function: CoResumeClassObjects, public // // Synopsis: resumes all registered LOCAL_SERVER class objects for this // process that are currently marked as SUSPENDED, so that new // activation calls from the SCM will now be accepted. // // History: 17-Apr-96 Rickhi Created // //-------------------------------------------------------------------------- STDAPI CoResumeClassObjects(void) { return gdllcacheInprocSrv.ResumeProcessClassObjects(); } //+------------------------------------------------------------------- // // Member: CObjServer::CObjServer, public // // Synopsis: construction // // History: 10 Apr 95 AlexMit Created // //-------------------------------------------------------------------- CObjServer::CObjServer(HRESULT &hr) { _hr = MarshalInternalObjRef(_objref, IID_IObjServer, (IObjServer*) this, MSHLFLAGS_NOPING, NULL); if (SUCCEEDED(_hr)) { SetObjServer(this); } hr = _hr; ComDebOut((DEB_ACTIVATE, "CObjServer::CObjServer _hr:%x\n", _hr)); } //+------------------------------------------------------------------- // // Member: CObjServer::~CObjServer, public // // Synopsis: dtor for activation object // // History: 19 Jun 95 Rickhi Created // //-------------------------------------------------------------------- CObjServer::~CObjServer() { if (SUCCEEDED(_hr)) { // only do RMD if the marshall was sucessfull SetObjServer(NULL); _hr = ReleaseMarshalObjRef(_objref); Win4Assert(SUCCEEDED(_hr)); FreeObjRef(_objref); } ComDebOut((DEB_ACTIVATE, "CObjServer::~CObjServer _hr:%x\n", _hr)); } //+------------------------------------------------------------------- // // Member: CObjServer::AddRef, public // // Synopsis: we dont refcnt this object so this is a noop // // History: 10 Apr 95 AlexMit Created // //-------------------------------------------------------------------- ULONG CObjServer::AddRef(void) { return 1; } //+------------------------------------------------------------------- // // Member: CObjServer::Release, public // // Synopsis: we dont refcnt this object so this is a noop // // History: 10 Apr 95 AlexMit Created // //-------------------------------------------------------------------- ULONG CObjServer::Release(void) { return 1; } //+------------------------------------------------------------------- // // Member: CObjServer::QueryInterface, public // // Synopsis: returns supported interfaces // // History: 10 Apr 95 AlexMit Created // //-------------------------------------------------------------------- STDMETHODIMP CObjServer::QueryInterface(REFIID riid, void **ppv) { if (IsEqualIID(riid, IID_IObjServer) || // more common than IUnknown IsEqualIID(riid, IID_IUnknown)) { *ppv = (IObjServer *) this; AddRef(); return S_OK; } *ppv = NULL; return E_NOINTERFACE; } //+------------------------------------------------------------------- // // Member: ObjactThreadUninitialize // // Synopsis: Cleans up the CObjServer object // // History: 10 Apr 95 AlexMit Created // //-------------------------------------------------------------------- STDAPI_(void) ObjactThreadUninitialize(void) { CObjServer *pObjServer = GetObjServer(); if (pObjServer != NULL) { delete pObjServer; } } //+------------------------------------------------------------------------- // // Member: CObjServer::ObjectServerGetClassObject // //-------------------------------------------------------------------------- STDMETHODIMP CObjServer::ObjectServerGetClassObject( const GUID *guidCLSID, IID *pIID, BOOL fSurrogate, MInterfacePointer **ppIFD, DWORD * pStatus ) { ComDebOut((DEB_ACTIVATE, "CObjServer::ObjectServerGetClassObject clsid:%I\n", &guidCLSID)); *pStatus = RPC_S_OK; // Check access. if (!CheckObjactAccess()) { return HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED ); } HRESULT hr; IUnknown *pcf; // Get the class object #ifdef WX86OLE hr = gdllcacheInprocSrv.GetClass(*guidCLSID, *pIID, FALSE, TRUE, fSurrogate, FALSE, &pcf); #else hr = gdllcacheInprocSrv.GetClass(*guidCLSID, *pIID, FALSE, TRUE, fSurrogate, &pcf); #endif if (pcf) { // We got the class object, create a buffer and marshal it for return. // Marshal it NORMAL and turn on the NotifyActivation flag so we can // add an implicit LockServer during marshaling to plug inherent races // in the IClassFactory protocol. hr = MarshalHelper(pcf, *pIID, MSHLFLAGS_NORMAL | MSHLFLAGS_NOTIFYACTIVATION, (InterfaceData**) ppIFD); } else { // If we get a NULL, this means that we have // recieved the request after we have decided // to stop, so we tell the caller we are stopping // so they can start a new copy of the server. if ( hr == S_OK ) hr = CO_E_SERVER_STOPPING; } ComDebOut((DEB_ACTIVATE, "CObjServer::ObjectServerGetClassObject hr:%x\n", hr)); return hr; } //+--------------------------------------------------------------------------- // // Member: CObjServer::ObjectServerCreateInstance // //---------------------------------------------------------------------------- STDMETHODIMP CObjServer::ObjectServerCreateInstance( /* [in] */ const GUID *rclsid, /* [in] */ DWORD dwInterfaces, /* [size_is][in] */ IID *pIIDs, /* [size_is][out] */ MInterfacePointer **ppIFDs, /* [size_is][out] */ HRESULT *pResults, /* [out] */ DWORD * pStatus ) { ComDebOut((DEB_ACTIVATE, "CObjServer::ObjectServerCreateInstance clsid:%I\n", rclsid)); *pStatus = RPC_S_OK; // Check access. if (!CheckObjactAccess()) { return HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED ); } HRESULT hr; IUnknown *pcf; // Get the class object #ifdef WX86OLE hr = gdllcacheInprocSrv.GetClass(*rclsid, IID_IClassFactory, FALSE, TRUE, FALSE, FALSE, &pcf); #else hr = gdllcacheInprocSrv.GetClass(*rclsid, IID_IClassFactory, FALSE, TRUE, FALSE, &pcf); #endif if (pcf) { // first, check if the server is willing to accept the incoming call // on IClassFactory. The reason we need this is that EXCEL's message // filter rejects calls on IID_IClassFactory if it is busy. They dont // know about IID_IObjServer. hr = HandleIncomingCall(IID_IClassFactory, 3, CALLCAT_SYNCHRONOUS, (void *)pcf); if (SUCCEEDED(hr)) { // Load the object hr = GetInstanceHelperMulti((IClassFactory *)pcf, dwInterfaces, pIIDs, ppIFDs, pResults, NULL); } pcf->Release(); } else { // If we get a NULL, this means that we have // recieved the request after we have decided // to stop, so we tell the caller we are stopping // so they can start a new copy of the server. if ( hr == S_OK ) hr = CO_E_SERVER_STOPPING; } ComDebOut((DEB_ACTIVATE, "CObjServer::ObjectServerCreateInstance hr:%x\n", hr)); return hr; } //+------------------------------------------------------------------------- // // Member: CObjServer::ObjectServerGetInstance // //-------------------------------------------------------------------------- STDMETHODIMP CObjServer::ObjectServerGetInstance( /* [in] */ const GUID *rclsid, /* [in] */ DWORD grfMode, /* [unique][string][in] */ WCHAR *pwszPath, /* [unique][in] */ MInterfacePointer *pIFDstg, /* [in] */ DWORD Interfaces, /* [size_is][in] */ IID *pIIDs, /* [unique][in] */ MInterfacePointer *pIFDFromROT, /* [size_is][out] */ MInterfacePointer **ppIFDs, /* [size_is][out] */ HRESULT *pResults, /* [out] */ DWORD * pStatus ) { HRESULT hr = S_OK; *pStatus = RPC_S_OK; // Check access. if (!CheckObjactAccess()) { return HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED ); } if (pIFDFromROT != NULL) { // If the SCM has passed us an object from the ROT, we // try to use that first by unmarshalling it and then // marshaling it normal. CXmitRpcStream xrpcForUnmarshal((InterfaceData*)pIFDFromROT); IUnknown *punk; hr = CoUnmarshalInterface(&xrpcForUnmarshal, IID_IUnknown, (void **) &punk); if (SUCCEEDED(hr)) { hr = E_NOINTERFACE; for ( DWORD i = 0; i < Interfaces; i++ ) { // Stream to put marshaled interface in CXmitRpcStream xrpc; HRESULT hr2; // use DIFFERENTMACHINE so we get the long form OBJREF hr2 = CoMarshalInterface(&xrpc, pIIDs[i], punk, MSHCTX_DIFFERENTMACHINE, NULL, MSHLFLAGS_NORMAL); if (SUCCEEDED(hr2)) { // Report OK if any interface is found. hr = hr2; xrpc.AssignSerializedInterface((InterfaceData **) &ppIFDs[i]); } pResults[i] = hr2; } // Don't need the unknown ptr any more punk->Release(); return hr; } // Assume any errors are the result of a stale entry in the ROT // so we just fall into the regular code path from here. hr = S_OK; } CSafeMarshaledStg smstg( (InterfaceData*) pIFDstg, hr); if (FAILED(hr)) { return hr; } // Get the class object IUnknown *pcf = NULL; #ifdef WX86OLE hr = gdllcacheInprocSrv.GetClass(*rclsid, IID_IClassFactory, FALSE, TRUE, FALSE, FALSE, &pcf); #else hr = gdllcacheInprocSrv.GetClass(*rclsid, IID_IClassFactory, FALSE, TRUE, FALSE, &pcf); #endif if (pcf) { // first, check if the server is willing to accept the incoming call // on IClassFactory. The reason we need this is that EXCEL's message // filter rejects calls on IID_IClassFactory if it is busy. They dont // know about IID_IObjServer. hr = HandleIncomingCall(IID_IClassFactory, 3, CALLCAT_SYNCHRONOUS, (void *)pcf); if (SUCCEEDED(hr)) { // Load the object hr = GetObjectHelperMulti((IClassFactory *)pcf, grfMode, NULL, pwszPath, smstg, Interfaces, pIIDs, ppIFDs, pResults, NULL); } pcf->Release(); } else { // If we get a NULL, this means that we have // recieved the request after we have decided // to stop, so we tell the caller we are stopping // so they can start a new copy of the server. if ( hr == S_OK ) hr = CO_E_SERVER_STOPPING; } return hr; } //+------------------------------------------------------------------------- // // Member: CObjServer::ObjectServerLoadDll // // Synopsis: Loads the requested dll into a surrogate process which // implements the ISurrogate interface // //-------------------------------------------------------------------------- STDMETHODIMP CObjServer::ObjectServerLoadDll( /* [in] */ const GUID *rclsid, /* [out] */ DWORD* pStatus) { ComDebOut((DEB_ACTIVATE, "ObjectServerLoadDll clsid:%I\n", rclsid)); *pStatus = RPC_S_OK; HRESULT hr = CCOMSurrogate::LoadDllServer(*rclsid); ComDebOut((DEB_ACTIVATE, "ObjectServerLoadDll hr:%x\n", hr)); return hr; } //+------------------------------------------------------------------------- // // Function: NotifyActivation // // Synopsis: Add/Remove implicit IClassFactory::LockServer during marshal // and last external release of an interface pointer. // // Arguments: [fLock] - whether to Lock or Unlock // [pUnk] - ptr to object interface // // Returns: TRUE - call again during last release // FALSE - dont call again during last release // // History: 12-May-96 RickHi Created // // Notes: there is an inherent race condition in the IClassFactory (and // derived interfaces) in that between the time a client gets the // ICF pointer and the time they call LockServer(TRUE), a server could // shut down. In order to plug this hole, COM's activation code will // attempt to do an implicit LockServer(TRUE) on the server side of // CoGetClassObject during the marshaling of the class object // interface. Since we dont know for sure that it is IClassFactory // being marshaled, we QI for it here. // //-------------------------------------------------------------------------- INTERNAL_(BOOL) NotifyActivation(BOOL fLock, IUnknown *pUnk) { ComDebOut((DEB_ACTIVATE, "NotifyActivation fLock:%x pUnk:%x\n", fLock, pUnk)); // If the object supports IClassFactory, do an implicit LockServer(TRUE) // on behalf of the client when the interface is first marshaled by // CoGetClassObject. When the last external reference to the interface is // release, do an implicit LockServer(FALSE). IClassFactory *pICF = NULL; if (SUCCEEDED(pUnk->QueryInterface(IID_IClassFactory, (void **)&pICF))) { pICF->LockServer(fLock); pICF->Release(); return TRUE; } return FALSE; } //+------------------------------------------------------------------------- // // Function: CoRegisterSurrogate, public // // Synopsis: Register an ISurrogate interface for a surrogate process // // Arguments: [pSurrogate] - existing ISurrogate interface ponter // // Returns: S_OK - object is successfully registered // // Algorithm: Validate the parameter. Then set a global pointer to the // value of the pSurrogate parameter // // History: 2-Jun-96 t-AdamE Created // //-------------------------------------------------------------------------- STDAPI CoRegisterSurrogate(ISurrogate* pSurrogate) { HRESULT hr; OLETRACEIN((API_CoRegisterSurrogate, PARAMFMT("pSurrogate= %p"), pSurrogate)); TRACECALL(TRACE_ACTIVATION, "CoRegisterSurrogate"); if (!IsApartmentInitialized()) { hr = CO_E_NOTINITIALIZED; goto errRtn; } // Validate the pSurrogate if (!IsValidInterface(pSurrogate)) { CairoleAssert(IsValidInterface(pSurrogate) && "CoRegisterSurrogate invalid pSurrogate"); hr = E_INVALIDARG; goto errRtn; } hr = CCOMSurrogate::InitializeISurrogate(pSurrogate); errRtn: OLETRACEOUT((API_CoRegisterSurrogate, hr)); return hr; }