NT4/private/ole32/com/objact/sobjact.cxx
2020-09-30 17:12:29 +02:00

781 lines
23 KiB
C++

//+-------------------------------------------------------------------------
//
// 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 <ole2int.h>
#include <iface.h>
#include <olerem.h>
#include "..\..\ole232\stdimpl\handler.hxx"
#include "resolver.hxx"
#include "smstg.hxx"
#include "objact.hxx"
#include "service.hxx"
#include <sobjact.hxx>
#include <comsrgt.hxx>
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;
}