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

1374 lines
43 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1993.
//
// File: actapi.cxx
//
// Contents: Functions that activate objects residing in persistent storage.
//
// Functions: CoGetPersistentInstanceEx
//
// History: 20-Sep-95 GregJen Created
//
//--------------------------------------------------------------------------
#include <ole2int.h>
#include <iface.h>
#include <objsrv.h>
#include <compname.hxx>
#include "resolver.hxx"
#include "smstg.hxx"
#include "objact.hxx"
#include "clsctx.hxx"
#include "treat.hxx"
// We use this to calculate the hash value for the path
extern DWORD CalcFileMonikerHash(LPWSTR pwszPath);
//+-------------------------------------------------------------------------
//
// Class: CSplit_QI
//
// Synopsis: Helper for splitting a multi_QI block into separate arrays
//
// Arguments: [pMqi] - pointer to multi_QI array
//
// History: 14-Nov-95 GregJen Created
//
// notes: the RPC calls to the SCM take a bunch of arrays, some [in], and
// some [out]. We get called with an array of structs. This class
// splits everything out of the array of MULTI_QI structs, and
// makes arrays for the RPC call parameters.
//--------------------------------------------------------------------------
class CSplit_QI
{
private:
PMInterfacePointer SomePMItfPtrs[2];
HRESULT SomeHRs[2];
IID SomeIIDs[2];
DWORD _dwCount;
char * _pAllocBlock;
public:
PMInterfacePointer * _pItfArray;
HRESULT * _pHrArray;
IID * _pIIDArray;
// we just have a constructor and a destructor
CSplit_QI( HRESULT & hr, DWORD count, MULTI_QI * pInputArray );
~CSplit_QI();
};
//+-------------------------------------------------------------------------
//
// Function: CSplit_QI constructor
//
// Synopsis: Helper for allocating the arrays for a multi-qi call
//
// Arguments: [hr] - hr to return by reference
// [count] - number of IIDs requested
// [pInputArray] - the MULTI_QI structure passed in to us
//
// Returns: S_OK - everything set up OK
//
// History: 01-Dec-95 GregJen Created
////
//--------------------------------------------------------------------------
CSplit_QI::CSplit_QI( HRESULT & hr, DWORD count, MULTI_QI * pInputArray )
{
_pAllocBlock = NULL;
_pItfArray = NULL;
_dwCount = count;
// if they only asked for 1 or 2, save time by just using
// our memory on the stack
if ( count <= 2 )
{
_pItfArray = SomePMItfPtrs;
_pHrArray = SomeHRs;
_pIIDArray = SomeIIDs;
for ( DWORD i = 0; i < count; i++ )
{
_pIIDArray[i] = *(pInputArray[i].pIID);
}
memset( _pItfArray, 0, sizeof(SomePMItfPtrs) );
hr = S_OK;
return;
}
ULONG ulItfArrSz = count * sizeof( PMInterfacePointer );
ULONG ulHRArrSz = count * sizeof( HRESULT );
ULONG ulIIDArrSz = count * sizeof( IID );
_pAllocBlock = (char * )PrivMemAlloc( ulItfArrSz +
ulHRArrSz +
ulIIDArrSz );
if ( _pAllocBlock )
{
hr = S_OK;
// carve up the allocated block
_pItfArray = (PMInterfacePointer *) _pAllocBlock;
_pHrArray = (HRESULT *) (_pAllocBlock +
ulItfArrSz );
_pIIDArray = (IID * ) ( _pAllocBlock +
ulItfArrSz +
ulHRArrSz );
// copy the IIDs and zero the MInterfacePointers
for ( DWORD i = 0; i < count; i++ )
{
_pIIDArray[i] = *(pInputArray[i].pIID);
}
memset( _pItfArray, 0, ulItfArrSz );
}
else
{
hr = E_OUTOFMEMORY;
}
}
//+-------------------------------------------------------------------------
//
// Function: CSplit_QI destructor
//
// Synopsis: Helper for freeing the arrays for a multi-qi call
//
// Arguments: none
//
// Returns: nothing
//
// History: 01-Dec-95 GregJen Created
//
//--------------------------------------------------------------------------
CSplit_QI::~CSplit_QI()
{
// make sure to clean up any dangling interface pointers
if ( _pItfArray )
{
for ( DWORD i = 0; i < _dwCount; i++ )
{
if ( _pItfArray[i] )
{
CXmitRpcStream xrpc( (InterfaceData*)_pItfArray[i] );
CoReleaseMarshalData(&xrpc);
MyMemFree(_pItfArray[i]);
_pItfArray[i] = NULL;
}
}
}
// only do the free if we allocated something
if ( _pAllocBlock )
{
PrivMemFree( _pAllocBlock );
}
}
//+-------------------------------------------------------------------------
//
// Function: UpdateResultsArray
//
// Synopsis: Helper for returning the correct hr from a multi-qi call
//
// Arguments: [hrIn] - hr from the calling function
// [dwCount] - number of IIDs requested
// [pResults] - where to put pointer to returned interface
//
// Returns: S_OK - All Interface are OK
//
// History: 30-Aug-95 GregJen Created
////
//--------------------------------------------------------------------------
inline
HRESULT
UpdateResultsArray( HRESULT hrIn, DWORD dwCount, MULTI_QI * pResults )
{
HRESULT hr = hrIn;
DWORD i;
// make sure the HR is set correctly
if ( SUCCEEDED( hrIn ) )
{
// assume no interfaces were found
DWORD dwFound = 0;
for ( i=0; i<dwCount; i++ )
{
if ( FAILED( pResults[i].hr ) )
pResults[i].pItf = NULL;
else
{
dwFound++;
Win4Assert(pResults[i].pItf != NULL );
}
}
if ( dwFound == 0 )
{
// if there was only 1 interface, return its hr.
if ( dwCount == 1 )
hr = pResults[0].hr;
else
hr = E_NOINTERFACE;
}
else if ( dwFound < dwCount )
hr = CO_S_NOTALLINTERFACES;
}
else
{
// failed - set all the hr's to the overall failure code,
// and clean up any interface pointers we got
for ( i=0; i<dwCount; i++ )
{
if ( pResults[i].pItf )
pResults[i].pItf->Release();
pResults[i].pItf = NULL;
pResults[i].hr = hr;
}
}
return hr;
}
//+-------------------------------------------------------------------------
//
// Function: DoBetterUnmarshal
//
// Synopsis: Helper for unmarshaling an interface from remote
//
// Arguments: [pIFD] - serialized interface reference returned by SCM
// [riid] - interface ID requested by application
// [ppvUnk] - where to put pointer to returned interface
//
// Returns: S_OK - Interface unmarshaled
//
// Algorithm: Convert marshaled data to a stream and then unmarshal
// to the right interface
//
//
// History: 11-May-93 Ricksa Created
//
//--------------------------------------------------------------------------
inline
HRESULT DoBetterUnmarshal(MInterfacePointer *&pIFD, REFIID riid, IUnknown **ppvUnk)
{
// Convert returned interface to a stream
CXmitRpcStream xrpc( (InterfaceData*)pIFD );
HRESULT hr = CoUnmarshalInterface(&xrpc, riid, (void **) ppvUnk);
//CODEWORK: Stress revealed CoGetClassObject returning a null class factory
// and S_OK
Win4Assert(((hr == S_OK && *ppvUnk != NULL) ||
(hr != S_OK && *ppvUnk == NULL))
&& "DoBetterUnmarshal QueryInterface failure");
MyMemFree(pIFD);
pIFD = NULL;
return hr;
}
//+-------------------------------------------------------------------------
//
// Function: UnmarshalMultipleSCMResults
//
// Synopsis: Common routine for dealing with results from SCM
//
// Arguments: [sc] - SCODE returned by SCM
// [pIFD] - serialized interface reference returned by SCM
// [riid] - interface ID requested by application
// [ppunk] - where to put pointer to returned interface
// [pwszDllPath] - path to DLL if there is one.
// [ppunk] - pointer to returned interface.
// [usMethodOrdinal] - method for error reporting
//
// Returns: TRUE - processing is complete for the call
// FALSE - this is a DLL and client needs to instantiate.
//
// Algorithm: If the SCODE indicates a failure, then this sets an
// SCODE indicating that the service controller returned
// an error and propagates the result from the SCM. Otherwise,
// if the SCM has returned a result indicating that a
// handler has been returned, the handler DLL is cached.
// If a marshaled interface has been returned, then that is
// unmarshaled. If an inprocess server has been returned,
// the DLL is cached and the class object is created.
//
// History: 11-May-93 Ricksa Created
//
// Notes: This routine is simply a helper for CoGetPersistentInstance.
//
//--------------------------------------------------------------------------
BOOL UnmarshalMultipleSCMResults(
HRESULT& hr,
PMInterfacePointer *pItfArray,
DWORD dwContext,
REFCLSID rclsid,
IUnknown * punkOuter,
DWORD dwCount,
IID * pIIDs,
HRESULT * pHrArray,
MULTI_QI * pResults,
DWORD dwDllThreadModel,
WCHAR *pwszDllPath,
IClassFactory **ppvCf)
{
BOOL fResult = TRUE;
DWORD i;
HRESULT hr2;
IUnknown * pUnk;
if (SUCCEEDED(hr))
{
// Flag for fall through from a 16 bit case
BOOL f16BitFallThru = FALSE;
// Flag for fall through from got-handler
BOOL fGetClassObject = FALSE;
BOOL fHandlerAndServer = FALSE;
switch (hr)
{
#ifdef GET_INPROC_FROM_SCM
case SCM_S_HANDLER16:
CairoleDebugOut((DEB_ACTIVATE,
"16-bit InprocHandler\n"));
// Note: if the process is a 32 bit process and the
// DLL is a 16 bit DLL, the load will fail. Since
// we assume that this is a fairly rare case, we
// let the lower level code discover this.
f16BitFallThru = TRUE;
#ifdef WX86OLE
case SCM_S_HANDLERX86:
#endif
case SCM_S_HANDLER:
CairoleDebugOut((DEB_ACTIVATE,
"InprocHandler(%ws)\n",pwszDllPath));
// Just in case we chicken out and back out our changes
if (!f16BitFallThru)
{
// Validate that 32 bit handler DLL is being loaded
// in the correct process.
hr = CheckScmHandlerResult(pwszDllPath);
if (hr != NOERROR)
{
break;
}
}
// Figure out if we really need the class object for the
// handler. Otherwise we will just put it in the cache
// and unmarshal the class object.
fGetClassObject =
(dwContext & CLSCTX_INPROC_HANDLER) ? TRUE : FALSE;
/***
#else // GET_INPROC_FROM_SCM
// Only time we should be in this path is when we called the
// SCM for non-INPROC and get advised that a handler exists
fGetClassObject = FALSE;
***/
// Store the handler returned
pUnk = gdllcacheHandler.Add(rclsid, IID_IClassFactory,
dwDllThreadModel, pwszDllPath, fGetClassObject,
(hr == SCM_S_HANDLER16),
#ifdef WX86OLE
(hr == SCM_S_HANDLERX86),
#endif
hr);
if (FAILED(hr))
{
return TRUE;
}
if (fGetClassObject)
{
// Request was really for a handler so we are done.
*ppvCf = (IClassFactory*) pUnk;
fResult = FALSE;
break;
}
// We got a handler back but we have just cached it to make
// processing faster when we create a real instance of an
// object. So we unmarshal the real object.
fHandlerAndServer = TRUE;
#endif // GET_INPROC_FROM_SCM
case S_OK :
if ( punkOuter && !fHandlerAndServer )
return CLASS_E_NOAGGREGATION;
for ( i=0; i<dwCount; i++, pResults++ )
{
pResults->hr = pHrArray[i];
if ( SUCCEEDED( pHrArray[i] ) )
{
hr2 = DoBetterUnmarshal( pItfArray[i],
*(pResults->pIID),
&pResults->pItf);
// ... and try to set the overall HR correctly
pResults->hr = hr2;
if ( FAILED( hr2 ) )
hr = CO_S_NOTALLINTERFACES;
}
else
hr = CO_S_NOTALLINTERFACES;
}
break;
#ifdef GET_INPROC_FROM_SCM
case SCM_S_INPROCSERVER16:
CairoleDebugOut((DEB_ACTIVATE, "16-bit InprocServer\n"));
#ifdef WX86OLE
case SCM_S_INPROCSERVERX86:
#endif
case SCM_S_INPROCSERVER:
CairoleDebugOut((DEB_ACTIVATE, "InprocServer(%ws)\n",pwszDllPath));
// Just in case we chicken out and back out our changes
// This is an inprocesses server -- we want cache that information
// and do the work of instantiating an object.
*ppvCf = (IClassFactory*) gdllcacheInprocSrv.Add(rclsid, IID_IClassFactory,
dwDllThreadModel, pwszDllPath, TRUE,
(hr == SCM_S_INPROCSERVER16),
#ifdef WX86OLE
(hr == SCM_S_INPROCSERVERX86),
#endif
hr);
// If we actually got an inproc server object successfully
// then we want to continue processing otherwise we can
// just return the error that occurred.
if (SUCCEEDED(hr))
{
fResult = FALSE;
}
#else // GET_INPROC_FROM_SCM
// Error: Should never come here as we handled INPROC_SERVERS
// before calling SCM
Win4Assert((FALSE) && "UnmarshalMultipleSCMResults: SCM_S_INPROC return from SCM");
#endif // GET_INPROC_FROM_SCM
}
}
return fResult;
}
//+-------------------------------------------------------------------------
//
// Function: CoGetInstanceFromFile
//
// Synopsis: Returns an instantiated interface to an object whose
// stored state resides on disk.
//
// Arguments: [pServerInfo] - server information block
// [dwCtrl] - kind of server required
// [grfMode] - how to open the storage if it is a file.
// [pwszName] - name of storage if it is a file.
// [pstg] - IStorage to use for object
// [pclsidOverride]
// [ppvUnk] - where to put bound interface pointer
//
// Returns: S_OK - object bound successfully
// MISSING
//--------------------------------------------------------------------------
STDAPI CoGetInstanceFromFile(
COSERVERINFO * pServerInfo,
CLSID * pclsidOverride,
IUnknown * punkOuter, // only relevant locally
DWORD dwClsCtx,
DWORD grfMode,
OLECHAR * pwszName,
DWORD dwCount,
MULTI_QI * pResults )
{
TRACECALL(TRACE_ACTIVATION, "CoGetInstanceFromFile");
#ifdef DCOM
if ( pServerInfo &&
( ! IsValidPtrIn( pServerInfo, sizeof(COSERVERINFO) ) ||
pServerInfo->dwReserved1 ||
pServerInfo->dwReserved2 ) )
#else
if ( pServerInfo )
#endif
return E_INVALIDARG;
return GetInstanceHelper( pServerInfo,
pclsidOverride,
punkOuter,
dwClsCtx,
grfMode,
pwszName,
NULL,
dwCount,
pResults );
}
//+-------------------------------------------------------------------------
//
// Function: CoGetInstanceFromIStorage
//
// Synopsis: Returns an instantiated interface to an object whose
// stored state resides on disk.
//
// Arguments: [pServerInfo] - server information block
// [dwCtrl] - kind of server required
// [grfMode] - how to open the storage if it is a file.
// [pwszName] - name of storage if it is a file.
// [pstg] - IStorage to use for object
// [pclsidOverride]
// [ppvUnk] - where to put bound interface pointer
//
// Returns: S_OK - object bound successfully
// MISSING
//
//--------------------------------------------------------------------------
STDAPI CoGetInstanceFromIStorage(
COSERVERINFO * pServerInfo,
CLSID * pclsidOverride,
IUnknown * punkOuter, // only relevant locally
DWORD dwClsCtx,
struct IStorage * pstg,
DWORD dwCount,
MULTI_QI * pResults )
{
STATSTG statstg;
CLSID clsid;
HRESULT hr;
TRACECALL(TRACE_ACTIVATION, "CoGetInstanceFromIStorage");
#ifdef DCOM
if ( pServerInfo &&
( ! IsValidPtrIn( pServerInfo, sizeof(COSERVERINFO) ) ||
pServerInfo->dwReserved1 ||
pServerInfo->dwReserved2 ) )
#else
if ( pServerInfo )
#endif
return E_INVALIDARG;
statstg.pwcsName = 0;
hr = pstg->Stat(&statstg, STATFLAG_DEFAULT);
if ( FAILED(hr) )
return hr;
if ( pclsidOverride == NULL )
clsid = statstg.clsid;
else
clsid = *pclsidOverride;
hr = GetInstanceHelper( pServerInfo,
pclsidOverride,
punkOuter,
dwClsCtx,
statstg.grfMode,
statstg.pwcsName,
pstg,
dwCount,
pResults );
PrivMemFree( statstg.pwcsName );
return hr;
}
HRESULT GetInstanceHelper(
COSERVERINFO * pServerInfo,
CLSID * pclsidOverride,
IUnknown * punkOuter, // only relevant locally
DWORD dwClsCtx,
DWORD grfMode,
OLECHAR * pwszName,
struct IStorage * pstg,
DWORD dwCount,
MULTI_QI * pResults )
{
if (!IsApartmentInitialized())
return CO_E_NOTINITIALIZED;
IUnknown *punk;
WCHAR awcNameBuf[MAX_PATH];
WCHAR * pwszNameUNC = awcNameBuf;
WCHAR awcServer[MAX_PATH];
WCHAR * pwszServer = awcServer;
DWORD dwDllServerType = IsSTAThread() ? APT_THREADED : FREE_THREADED;
IClassFactory * pcf;
HRESULT hr = E_FAIL;
BOOL fExitBlock;
BOOL bFileWasOpened;
DWORD i; // handy iterator
// Make sure input request is at least slightly logical
if ( ((pwszName == NULL) && (pstg == NULL))
|| ((dwClsCtx & ~CLSCTX_VALID_MASK) != 0)
|| ( dwCount < 1 )
|| ( pResults == NULL ) )
{
return E_INVALIDARG;
}
// check the MULTI_QI for validity (and clear out the hresults)
for ( i=0; i<dwCount; i++ )
{
if ( pResults[i].pItf || !pResults[i].pIID )
{
hr = E_INVALIDARG;
goto final_exit;
}
pResults[i].hr = E_NOINTERFACE;
}
bFileWasOpened = FALSE;
CLSID clsid;
if (pwszName)
{
// If there is a path supplied convert it to a normalized form
// so it can be used by any process in the net.
hr = ProcessPath(pwszName, &pwszNameUNC, &pwszServer);
if (FAILED(hr))
{
goto exit_point;
}
// Limit on loops for retrying to get class of object
DWORD cGetClassRetries = 0;
// We loop here looking for either the running object or
// for the class of the file. We do this because there
// are race conditions where the can be starting or stopping
// and the class of the object might not be available because
// of the opening mode of the object's server.
do
{
// Look in the ROT first to see if we need to bother
// looking up the class of the file.
if (GetObjectFromRotByPath(pwszName,
(IUnknown **) &punk) == S_OK)
{
// Got object from ROT so we are done.
goto qiexit_point;
}
// Try to get the class of the file
if ( pclsidOverride != NULL )
{
clsid = *pclsidOverride;
hr = S_OK;
}
else
{
hr = GetClassFile(pwszName, &clsid);
bFileWasOpened = TRUE;
}
if (hr == STG_E_ACCESSDENIED)
{
// The point here of the sleep is to try to let the
// operation that is holding the class id unavailable
// complete.
Sleep(GET_CLASS_RETRY_SLEEP_MS);
continue;
}
// Either we succeeded or something other than error
// access denied occurred here. For all these cases
// we break the loop.
break;
} while (cGetClassRetries++ < GET_CLASS_RETRY_MAX);
if (FAILED(hr))
{
// If we were unable to determine the classid, and the
// caller provided one as a Ole1 clsid, try loading it
// If it succeeds, then return
if (pclsidOverride != NULL)
{
goto dde_exit;
}
goto final_exit;
}
}
CLSID tmpClsid;
hr = OleGetAutoConvert(clsid, &tmpClsid);
if ( ( hr == REGDB_E_KEYMISSING ) || ( hr == REGDB_E_CLASSNOTREG ) )
{
// do nothing
}
else if ( FAILED(hr) )
{
goto exit_point;
}
else
{
clsid = tmpClsid;
}
hr = GetTreatAs(clsid, clsid);
if ( FAILED(hr) )
{
goto exit_point;
}
// Make sure we are asking for the correct inproc server
dwClsCtx = RemapClassCtxForInProcServer(dwClsCtx);
//
// If this is a OLE 1.0 class, then do a DdeBindToObject on it,
// and return.
//
if (CoIsOle1Class(clsid))
{
if (pwszName != NULL)
{
goto dde_exit;
}
else
{
//
// Something is fishy here. We don't have a pwszName,
// yet CoIsOle1Class returned the class as an ole1 class.
// To get to this point without a pwszName, there must have
// been a pstg passed into the API.
//
// This isn't supposed to happen. To recover, just fall
// through and load the class as an OLE 2.0 class
//
CairoleDebugOut((DEB_ERROR,
"CoIsOle1Class is TRUE on a storage!\n"));
}
}
// At this point, we know the clsid we want to activate
pcf = (IClassFactory *)
SearchCacheOrLoadInProc(clsid,
IID_IClassFactory,
(pwszServer != NULL),
FALSE,
dwClsCtx,
dwDllServerType,
hr);
if ( pcf ==NULL )
{
// Marshal pstg since SCM can't deal with unmarshaled objects
CSafeStgMarshaled MarshalledStg(pstg, MSHCTX_DIFFERENTMACHINE, hr);
MInterfacePointer * pMrshlStg;
if ( pstg == 0 )
pMrshlStg = 0;
else
pMrshlStg = MarshalledStg;
if (FAILED(hr))
goto exit_point;
// split the array of structs into individual arrays
CSplit_QI SplitQI( hr, dwCount, pResults );
DWORD cLoops = 0;
BOOL FoundInROT;
#ifndef GET_INPROC_FROM_SCM
// Just in case we chicken out and back out our changes
dwClsCtx &= ~(CLSCTX_INPROC_SERVERS | CLSCTX_INPROC_HANDLERS); // make sure we don't ask for inproc stuff
#endif // GET_INPROC_FROM_SCM
do
{
hr = gResolver.GetPersistentInstance( pServerInfo,
&clsid,
dwClsCtx,
grfMode,
bFileWasOpened,
pwszName,
pMrshlStg,
dwCount,
SplitQI._pIIDArray,
&FoundInROT,
SplitQI._pItfArray,
SplitQI._pHrArray,
&dwDllServerType,
&pwszServer );
fExitBlock = UnmarshalMultipleSCMResults(hr,
SplitQI._pItfArray,
dwClsCtx,
clsid,
punkOuter,
dwCount,
SplitQI._pIIDArray,
SplitQI._pHrArray,
pResults,
dwDllServerType,
pwszServer,
&pcf);
// If we get something from the ROT, we need to retry until
// we get an object. Because objects can disappear from the
// ROT async to us, we need to retry a few times. But since
// this theoretically could happen forever, we place an arbitrary
// limit on the number of retries to the ROT.
} while( (hr != NOERROR)
&& (FoundInROT)
&& (++cLoops < 5));
}
if ( pcf )
{
// Create the instance and do the qi's
hr = GetObjectHelperMulti( pcf,
grfMode,
punkOuter,
pwszName,
pstg,
dwCount,
NULL,
NULL,
NULL,
pResults );
pcf->Release();
}
exit_point:
hr = UpdateResultsArray( hr, dwCount, pResults );
final_exit:
if ( pwszServer != awcServer )
PrivMemFree( pwszServer );
return hr;
dde_exit:
if (hr != MK_E_CANTOPENFILE)
{
COleTls Tls;
if( Tls->dwFlags & OLETLS_DISABLE_OLE1DDE )
{
// If this app doesn't want or can tolerate having a DDE
// window then currently it can't use OLE1 classes because
// they are implemented using DDE windows.
//
hr = CO_E_OLE1DDE_DISABLED;
goto final_exit;
}
hr = DdeBindToObject(pwszName,
clsid,
FALSE,
IID_IUnknown,
(void **)&punk);
if (FAILED(hr))
goto final_exit;
}
// FALLTHRU to qi exit point
qiexit_point:
// Get the requested interfaces
for ( i = 0; i<dwCount; i++ )
{
pResults[i].hr = punk->QueryInterface(*(pResults[i].pIID),
(void**)&pResults[i].pItf );
}
punk->Release();
// Got object from ROT so we are done.
goto exit_point;
}
//+-------------------------------------------------------------------------
//
// Function: ICoGetClassObject
//
// Synopsis: Internal entry point that returns an instantiated class object
//
// Arguments: [rclsid] - class id for class object
// [dwContext] - kind of server we wish
// [pvReserved] - Reserved
// [riid] - interface to bind class object
// [ppvClassObj] - where to put interface pointer
//
// Returns: S_OK - successfully bound class object
//
// Algorithm: First, the context is validated. Then we try to use
// any cached information by looking up either cached in
// process servers or handlers based on the context.
// If no cached information suffices, we call the SCM
// to find out what to use. If the SCM returns a handler
// or an inprocess server, we cache that information.
// If the class is implemented by a local server, then
// the class object is unmarshaled. Otherwise, the object
// is instantiated locally using the returned DLL.
//
//
// History: 15-Nov-94 Ricksa Split into external and internal calls
//
//
//--------------------------------------------------------------------------
STDAPI ICoGetClassObject(
REFCLSID rclsid,
DWORD dwContext,
COSERVERINFO * pServerInfo,
REFIID riid,
void FAR* FAR* ppvClassObj)
{
TRACECALL(TRACE_ACTIVATION, "CoGetClassObject");
IUnknown *punk = NULL;
HRESULT hr = S_OK;
WCHAR *pwszDllToLoad = NULL;
MInterfacePointer *pIFD = NULL;
CLSID clsid;
DWORD dwDllServerType = IsSTAThread() ? APT_THREADED : FREE_THREADED;
#ifdef DCOM
if ( pServerInfo &&
( ! IsValidPtrIn( pServerInfo, sizeof(COSERVERINFO) ) ||
pServerInfo->dwReserved1 ||
pServerInfo->dwReserved2 ) )
#else
if ( pServerInfo )
#endif
return E_INVALIDARG;
BEGIN_BLOCK
// IsInternalCLSID will also check to determine if the CLSID is
// an OLE 1.0 CLSID, in which case we get back our internal
// class factory.
if (IsInternalCLSID(rclsid, riid, hr, ppvClassObj))
{
// this is an internally implemented clsid, or an OLE 1.0 class
// so we already got the class factory (if available) and set
// the return code appropriately.
EXIT_BLOCK;
}
if (FAILED(hr = GetTreatAs(rclsid, clsid)))
{
EXIT_BLOCK;
}
punk = SearchCacheOrLoadInProc(clsid,
riid,
FALSE,
FALSE,
dwContext,
dwDllServerType,
hr);
// If still don't have a punk, go to the scm
if (!punk)
{
// Ask the service controller for the class object
#ifndef GET_INPROC_FROM_SCM
// Just in case we chicken out and back out our changes
dwContext &= ~(CLSCTX_INPROC_SERVERS | CLSCTX_INPROC_HANDLERS); //
#endif // GET_INPROC_FROM_SCM
hr = gResolver.GetClassObject(clsid, dwContext, (IID *)&riid,
pServerInfo, &pIFD, &dwDllServerType, &pwszDllToLoad);
// A proxy/stub DLL needs to be loaded as both no matter what
if (dwContext & CLSCTX_PS_DLL)
{
dwDllServerType = BOTH_THREADED;
}
if (FAILED(hr))
{
EXIT_BLOCK;
}
// Flag for special handler behavior
BOOL fGetClassObject;
// Flag for fall through from a 16 bit case
BOOL f16BitFallThru = FALSE;
switch (hr)
{
#ifdef GET_INPROC_FROM_SCM
case SCM_S_HANDLER16:
CairoleDebugOut((DEB_ACTIVATE,
"16-bit InprocHandler\n"));
// Note: if the process is a 32 bit process and the
// DLL is a 16 bit DLL, the load will fail. Since
// we assume that this is a fairly rare case, we
// let the lower level code discover this.
f16BitFallThru = TRUE;
#ifdef WX86OLE
case SCM_S_HANDLERX86:
#endif
case SCM_S_HANDLER:
CairoleDebugOut((DEB_ACTIVATE,
"InprocHandler(%ws)\n",pwszDllToLoad));
// Just in case we chicken out and back out our changes
if (!f16BitFallThru)
{
// Validate that 32 bit handler DLL is being loaded
// in the correct process.
hr = CheckScmHandlerResult(pwszDllToLoad);
if (hr != NOERROR)
{
break;
}
}
// Figure out if we really need the class object for the
// handler. Otherwise we will just put it in the cache
// and unmarshal the class object.
fGetClassObject =
(dwContext & CLSCTX_INPROC_HANDLER) ? TRUE : FALSE;
/***
#else // GET_INPROC_FROM_SCM
// Only time we should be in this path is when we called the
// SCM for non-INPROC and get advised that a handler exists
fGetClassObject = FALSE;
***/
// Store the handler returned
punk = gdllcacheHandler.Add(clsid, riid, dwDllServerType,
pwszDllToLoad, fGetClassObject,
(hr == SCM_S_HANDLER16),
#ifdef WX86OLE
(hr == SCM_S_HANDLERX86),
#endif
hr);
if (fGetClassObject)
{
// Request was really for a handler so we are done.
break;
}
// We got a handler back but we have just cached it to make
// processing faster when we create a real instance of an
// object. So we unmarshal the real object.
#endif // GET_INPROC_FROM_SCM
case S_OK :
//hr = DoUnmarshal((InterfaceData*)pIFD, riid,(void**) &punk);
hr = DoBetterUnmarshal(pIFD, riid, &punk);
break;
#ifdef GET_INPROC_FROM_SCM
case SCM_S_INPROCSERVER16:
CairoleDebugOut((DEB_ACTIVATE,
"16-bit InprocServer\n"));
#ifdef WX86OLE
case SCM_S_INPROCSERVERX86:
#endif
case SCM_S_INPROCSERVER:
CairoleDebugOut((DEB_ACTIVATE,
"InprocServer(%ws)\n",pwszDllToLoad));
// Just in case we chicken out and back out our changes
// In process server for class object
punk = gdllcacheInprocSrv.Add(clsid, riid, dwDllServerType,
pwszDllToLoad, TRUE,(hr == SCM_S_INPROCSERVER16),
#ifdef WX86OLE
(hr == SCM_S_INPROCSERVERX86),
#endif
hr);
#else // GET_INPROC_FROM_SCM
// Error: Should never come here as we handled INPROC_SERVERS
// before calling SCM
Win4Assert((FALSE) && "IOldCoGetClassObject: SCM_S_INPROC return from SCM");
#endif // GET_INPROC_FROM_SCM
}
}
*ppvClassObj = punk;
if ((punk == NULL) && SUCCEEDED(hr))
{
hr = E_OUTOFMEMORY;
}
END_BLOCK;
if (pwszDllToLoad != NULL)
{
MyMemFree(pwszDllToLoad);
}
CALLHOOKOBJECTCREATE(hr,clsid,riid,(IUnknown **)ppvClassObj);
return hr;
}
//+-------------------------------------------------------------------------
//
// Function: CoCreateInstanceEx
//
// Synopsis: Returns an instantiated interface to an object
//
//
// Arguments: [Clsid] - requested CLSID
// [pServerInfo] - server information block
// [punkOuter] - controlling unknown for aggregating
// [dwCtrl] - kind of server required
// [dwCount] - count of interfaces
// [pResults] - MULTI_QI struct of interfaces
//
// Returns: S_OK - object bound successfully
// MISSING
//
//
//--------------------------------------------------------------------------
STDAPI CoCreateInstanceEx(
REFCLSID Clsid,
IUnknown * punkOuter, // only relevant locally
DWORD dwClsCtx,
COSERVERINFO * pServerInfo,
DWORD dwCount,
MULTI_QI * pResults )
{
TRACECALL(TRACE_ACTIVATION, "CoCreateInstanceEx");
if (!IsApartmentInitialized())
return CO_E_NOTINITIALIZED;
WCHAR awcNameBuf[MAX_PATH];
WCHAR * pwszNameUNC = awcNameBuf;
WCHAR awcServer[MAX_PATH];
WCHAR * pwszServer = awcServer;
DWORD dwDllServerType = IsSTAThread() ? APT_THREADED : FREE_THREADED;
IClassFactory * pcf = NULL;
HRESULT hr;
BOOL fExitBlock;
DWORD i;
#ifdef WX86OLE
BOOL fPunkIsProxy;
#endif
#ifdef DCOM
if ( pServerInfo &&
( ! IsValidPtrIn( pServerInfo, sizeof(COSERVERINFO) ) ||
pServerInfo->dwReserved1 ||
pServerInfo->dwReserved2 ) )
#else
if ( pServerInfo )
#endif
return E_INVALIDARG;
// Make sure input request is at least slightly logical
if ( ((dwClsCtx & ~CLSCTX_VALID_MASK) != 0)
|| ( dwCount < 1 )
|| ( pResults == NULL ) )
{
hr = E_INVALIDARG;
goto final_exit;
}
// check the MULTI_QI for validity (and clear out the hresults)
for ( i=0; i<dwCount; i++ )
{
if ( pResults[i].pItf || !pResults[i].pIID )
{
hr = E_INVALIDARG;
goto final_exit;
}
pResults[i].hr = E_NOINTERFACE;
}
CLSID realclsid;
hr = GetTreatAs(Clsid, realclsid);
if ( FAILED(hr) )
{
goto exit_point;
}
// Make sure we are asking for the correct inproc server
dwClsCtx = RemapClassCtxForInProcServer(dwClsCtx);
// IsInternalCLSID will also check to determine if the CLSID is
// an OLE 1.0 CLSID, in which case we get back our internal
// class factory.
if (IsInternalCLSID(Clsid, IID_IClassFactory, hr, (void **)&pcf))
{
// this is an internally implemented clsid, or an OLE 1.0 class
// so we already got the class factory (if available) and set
// the return code appropriately.
;
}
else
{
// At this point, we know the clsid we want to activate
pcf = (IClassFactory *)
SearchCacheOrLoadInProc(realclsid,
IID_IClassFactory,
(pwszServer != NULL),
FALSE,
dwClsCtx,
dwDllServerType,
hr);
}
if ( pcf == NULL )
{
// split the array of structs into individual arrays
CSplit_QI SplitQI( hr, dwCount, pResults );
if ( FAILED(hr) )
goto exit_point;
#ifndef GET_INPROC_FROM_SCM
// Just in case we chicken out and back out our changes
dwClsCtx &= ~(CLSCTX_INPROC_SERVERS | CLSCTX_INPROC_HANDLERS); // make sure we don't ask for inproc stuff
#endif // GET_INPROC_FROM_SCM
hr = gResolver.CreateInstance(pServerInfo,
&realclsid,
dwClsCtx,
dwCount,
SplitQI._pIIDArray,
SplitQI._pItfArray,
SplitQI._pHrArray,
&dwDllServerType,
&pwszServer);
fExitBlock = UnmarshalMultipleSCMResults(hr,
SplitQI._pItfArray,
dwClsCtx,
realclsid,
punkOuter,
dwCount,
SplitQI._pIIDArray,
SplitQI._pHrArray,
pResults,
dwDllServerType,
pwszServer,
&pcf);
if (fExitBlock)
goto exit_point;
}
// if we loaded it inproc, get the interfaces
if ( pcf )
{
IUnknown * pUnk;
HRESULT hr2;
#ifdef WX86OLE
// If we are calling through the wx86 thunk layer then set a
// flag that to let it know that ole32 is calling and let any
// custom interface that was specified for an out parameter to
// x86 code via an api be thunked as IUnknown since we know it
// will just be returned back to x86 code.
fPunkIsProxy = gcwx86.IsN2XProxy(pcf);
if (fPunkIsProxy)
{
gcwx86.SetStubInvokeFlag((UCHAR)-1);
}
#endif
// ask for the first interface (we'll use it as our IUnknown)
hr = pcf->CreateInstance(punkOuter,*(pResults[0].pIID), (void**) &pUnk );
// note that we don't need the pcf anymore, whether there is an
// error or not.
pcf->Release();
if ( FAILED(hr) )
goto exit_point;
for ( i=0; i<dwCount; i++ )
{
#ifdef WX86OLE
// If we are calling through the wx86 thunk layer then set a
// flag that to let it know that ole32 is calling and let any
// custom interface that was specified for an out parameter to
// x86 code via an api be thunked as IUnknown since we know it
// will just be returned back to x86 code.
if (fPunkIsProxy)
{
gcwx86.SetStubInvokeFlag((UCHAR)-1);
}
#endif
hr2 = pUnk->QueryInterface( *(pResults[i].pIID),
(void**)&pResults[i].pItf );
pResults[i].hr = hr2;
}
pUnk->Release();
// rely on the UpdateResultsArray to count up failed QI's
}
exit_point:
hr = UpdateResultsArray( hr, dwCount, pResults );
final_exit:
if ( pwszServer != awcServer )
PrivMemFree( pwszServer );
return hr;
}