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

1453 lines
49 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1993.
//
// File: cobjact.cxx
//
// Contents: Functions that activate objects residing in persistent storage.
//
// Functions: UnmarshalSCMResult
// CoGetPersistentInstance
// CoGetClassObject
//
// History: 11-May-93 Ricksa Created
// 31-Dec-93 ErikGav Chicago port
// 07-Apr-94 BruceMa Fixed parameter (#5573)
// 02-May-94 BruceMa CoGetPersistentInstance could return
// wrong interface
// 24-Jun-94 BruceMa Memory allocation check
// 07-Jul-94 BruceMa UnmarshalSCMResult mem alloc check
// 28-Jul-94 BruceMa Memory sift fix
// 30-Jan-95 Ricksa New ROT
//
//--------------------------------------------------------------------------
#include <ole2int.h>
#include <clsctx.hxx>
#include <iface.h>
#include <objsrv.h>
#include <compname.hxx>
#include "resolver.hxx"
#include "smstg.hxx"
#include "objact.hxx"
#include "treat.hxx"
// We use this to calculate the hash value for the path
DWORD CalcFileMonikerHash(LPWSTR pwszPath);
// computer name. Note that the static constructor does nothing. When the
// object first gets used, the computer name is extracted from the registry.
// we cant do it in the constructor because some things that are loaded
// early in the boot process (before registry Apis are ready) statically
// link to this Dll.
CComputerName g_CompName;
// Number of classes that we will try to use the old binding logic on
const MAX_MULTI_STEP_CLASSES = 2;
// Table of classes. Right now this is only WordPerfect.
// BUGBUG: We should put this information in the registry and then other
// badly written 16 bit apps can work as well.
// BUGBUG: We should reinvestigate whether the WP delayed class registration
// logic is necessary in the thunk layer.
const CLSID clsidMultiStep[MAX_MULTI_STEP_CLASSES] =
{{ 0x89FE3FE3, 0x9FF6, 0x101B, {0xB6, 0x78, 0x04, 0x02, 0x1C, 0x00, 0x70, 0x02}},
{ 0x1395F281, 0x4326, 0x101b, {0x8B, 0x9A, 0xCE, 0x29, 0x3E, 0xF3, 0x84, 0x49}}};
#ifdef _CHICAGO_
CRpcResolver gResolver;
#endif
//+-------------------------------------------------------------------------
//
// Function: GetMultiStepClassFactory
//
// Synopsis: Get a class object for a class that can't stand the new
// optimized binding logic.
//
// Arguments: [rclsid] - class for code we wish to get
// [dwClsctx] - class context required.
// [ppcf] - where to put the class factory.
//
// Returns: S_OK - we are returning a class factor
// S_FALSE - this is not an old style object
// Other - some failure occurred.
//
// History: 05-11-95 Ricksa Created
//
//+-------------------------------------------------------------------------
HRESULT GetMultiStepClassFactory(
REFCLSID rclsid,
DWORD dwClsctx,
IClassFactory **ppcf)
{
HRESULT hr = S_FALSE;
// We only care if this is a request for a local server because in the
// in proc case there is no time lag between calls.
if (dwClsctx & CLSCTX_LOCAL_SERVER)
{
// Is this a class that we know about that should be a slow bind?
for (int i = 0; i < MAX_MULTI_STEP_CLASSES; i++)
{
if (IsEqualCLSID(clsidMultiStep[i], rclsid))
{
// Get the class object & return it.
hr = ICoGetClassObject(rclsid, CLSCTX_LOCAL_SERVER, NULL,
IID_IClassFactory, (void **) ppcf);
break;
}
}
}
return hr;
}
//+-------------------------------------------------------------------------
//
// Function: GetObjectByPath
//
// Synopsis: Get object from the ROT by its path
//
// Arguments: [pwszName] - name of object to get from the ROT
// [ppvUnk] - unk to return to caller
// [riid] - interface to get.
//
// Returns: S_OK - got object from the ROT
// S_FALSE - object was not in the ROT
// Other - QueryInterface for IID failed.
//
// Notes: Ask ROT if path is in the ROT. If it is QI for the requested
// interface.
//
// History: 03-15-95 Ricksa Created
//
// Notes: This routine counts on the fact that S_FALSE is not a valid
// response IUnknown::QueryInterface.
//
//+-------------------------------------------------------------------------
HRESULT GetObjectByPath(WCHAR *pwszName, void **ppvUnk, REFIID riid)
{
HRESULT hr = S_FALSE;
IUnknown *punk;
if (GetObjectFromRotByPath(pwszName, &punk) == S_OK)
{
// Get the requested interface
hr = punk->QueryInterface(riid, ppvUnk);
Win4Assert((hr != S_FALSE) && "GetObjectByPath QI ret S_FALSE");
punk->Release();
}
return hr;
}
//+-------------------------------------------------------------------------
//
// Function: RemapClassCtxForInProcServer
//
// Synopsis: Remap CLSCTX so that the correct type of inproc server will
// be requested.
//
// Arguments: [dwCtrl] - requested server context
//
// Returns: Updated dwCtrl appropriate for the process' context
//
// Notes: If an inproc server is requested make sure it is the right
// type for the process. In other words, we only load 16 bit
// inproc servers into 16 bit processes and 32 bit servers
// into 32 bit processes. The special logic here is only for
// 16 bit servers since the attempt to load a 16-bit DLL into
// a 32 bit process will fail anyway.
//
// History: 01-11-95 Ricksa Created
//
//+-------------------------------------------------------------------------
DWORD RemapClassCtxForInProcServer(DWORD dwCtrl)
{
if (IsWOWThread())
{
// 16 bit process - remap CLSCTX_INPROC_SERVER if necessary
if ((dwCtrl & CLSCTX_INPROC_SERVER) != 0)
{
// Turn on the 16 bit inproc server request and turn off the
// 32 bit server request flag.
dwCtrl = (dwCtrl & ~CLSCTX_INPROC_SERVER) | CLSCTX_INPROC_SERVER16;
}
// if handlers are requested make sure 16-bit is looked for first
// We mask out 32bit handler flag for Wow threads because we will
// always look for a 32 bit handler if we don't find a 16 bit one
if ((dwCtrl & CLSCTX_INPROC_HANDLER) != 0)
{
// Turn on the 16 bit inproc handler request and turn off the
// 32 bit handler request flag.
dwCtrl = (dwCtrl & ~CLSCTX_INPROC_HANDLER) | CLSCTX_INPROC_HANDLER16;
}
}
#ifdef WX86OLE
//
// if we are allowed to remap the clsctx flags and we are running in a
// wx86 process then remap the flags to prefer an x86 inproc
else if (((dwCtrl & CLSCTX_NO_REMAP) == 0) && (gcwx86.IsWx86Enabled()))
{
if (dwCtrl & CLSCTX_INPROC_SERVER)
{
dwCtrl |= CLSCTX_INPROC_SERVERX86;
}
if (dwCtrl & CLSCTX_INPROC_HANDLER)
{
dwCtrl |= CLSCTX_INPROC_HANDLERX86;
}
}
#endif
else
{
if ((dwCtrl & CLSCTX_INPROC_SERVER) != 0)
{
// Turn off the 16 bit inproc server request
dwCtrl &= ~CLSCTX_INPROC_SERVER16;
}
}
#ifdef WX86OLE
if (dwCtrl & CLSCTX_NO_REMAP)
{
dwCtrl &= ~CLSCTX_NO_REMAP;
}
#endif
return dwCtrl;
}
//+-------------------------------------------------------------------------
//
// Function: CheckScmHandlerResult
//
// Synopsis: Verify that a 32 bit Handler DLL is being returned to the
// right context.
//
// Arguments: [pwszDllToLoad] - DLL to load
//
// Returns: S_OK - Everything is OK
// CO_E_ERRORINDLL - Trying to load bad DLL for process type.
//
// Notes: This is only important for 16 bit processes to prevent them
// from loading 32 handler code. We only allow the loading of
// OLE32.DLL as a handler in a 16 bit process because it is
// already loaded anyway.
//
// History: 01-11-95 Ricksa Created
//
//+-------------------------------------------------------------------------
HRESULT CheckScmHandlerResult(WCHAR *pwszDllToLoad)
{
HRESULT hr = S_OK;
if (IsWOWThread())
{
// We are a 16 bit process. Unless this is OLE32.DLL, we will fail
// the load of the handler.
// Scan string for last "\" if any.
WCHAR *pwszScan = pwszDllToLoad;
WCHAR *pwszLastComponent = pwszDllToLoad;
while(*pwszScan != 0)
{
if ((*pwszScan == '\\') || (*pwszScan == '/'))
{
pwszLastComponent = pwszScan + 1;
}
pwszScan++;
}
// Compare last path component to see if it ole32.dll
if (lstrcmpiW(pwszLastComponent, L"OLE32.DLL") != 0)
{
hr = CO_E_ERRORINDLL;
}
}
return hr;
}
//+-------------------------------------------------------------------------
//
// Function: IsInternalCLSID
//
// Synopsis: checks if the given clsid is an internal class, and
// bypasses the TreatAs and SCM lookup if so. Also checks for
// OLE 1.0 classes, which are actually considered to be
// internal, since their OLE 2.0 implementation wrapper is
// ours.
//
// Arguments: [rclsid] - clsid to look for
// [riid] - the iid requested
// [hr] - returns the hresult from DllGetClassObject
// [ppvClassObj] - where to return the class factory
//
// Returns: TRUE - its an internal class, hr is the return code from
// DllGetClassObject and if hr==S_OK ppvClassObj
// is the class factory.
// FALSE - not an internal class
//
// Notes: internal classes can not be overridden because there are
// other mechanisms for creating them eg CreateFileMoniker that
// bypass implementation lookup.
//
// History: 5-04-94 Rickhi Created
// 5-04-94 KevinRo Added OLE 1.0 support
//
//+-------------------------------------------------------------------------
BOOL IsInternalCLSID(REFCLSID rclsid,
REFIID riid,
HRESULT &hr,
void ** ppvClassObj)
{
DWORD *ptr = (DWORD *) &rclsid;
CLSID clsid = rclsid;
if (*(ptr+1) == 0x00000000 && // all internal clsid's have these
*(ptr+2) == 0x000000C0 && // common values
*(ptr+3) == 0x46000000)
{
// internal class (eg file moniker). just ask our selves
// for the class factory.
//
//
// Its possible that an OLE 1.0 class has been marked
// as TREATAS as part of an upgrade. Here we are going
// to handle the loading of OLE 1.0 servers. We
// need to do the GetTreatAs trick first. Rather than
// invalidate this perfectly good caching routine, the
// GetTreatAs will only be done if the clsid is in the
// range of the OLE 1.0 UUID's. Note that the GetTreatAs
// done here will add the class to the cache, so if the
// resulting class is outside of the internal range, the
// lookup done later will be nice and fast.
//
WORD hiWord = HIWORD(clsid.Data1);
if (hiWord == 3 || hiWord == 4)
{
//
// Its in the OLE 1.0 class range. See if it has
// been marked as 'treatas'
//
GetTreatAs(rclsid, clsid);
ptr = (DWORD *) &clsid;
hiWord = HIWORD(clsid.Data1);
}
if ((*ptr > 0x000002ff && *ptr < 0x00000321) ||
(hiWord == 3 || hiWord == 4))
{
// internal class (eg file moniker) or an OLE 1.0 class.
// just ask our selves for the class factory.
hr = DllGetClassObject(clsid, riid, ppvClassObj);
return TRUE;
}
}
// not an internal class.
return FALSE;
}
//+-------------------------------------------------------------------------
//
// Function: DoUnmarshal
//
// 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 IUnknown. Next, use unknown to query interface to
//
//
// History: 11-May-93 Ricksa Created
//
// Notes: This helper should go away when the server marshals
// to the interface that it actually wants to return.
//
//--------------------------------------------------------------------------
HRESULT DoUnmarshal(InterfaceData *pIFD, REFIID riid, void **ppvUnk)
{
// Convert returned interface to a stream
CXmitRpcStream xrpc(pIFD);
// Unmarshal interface
XIUnknown xunk;
HRESULT hr = CoUnmarshalInterface(&xrpc, IID_IUnknown, (void **) &xunk);
//CODEWORK: Stress revealed CoGetClassObject returning a null class factory
// and S_OK
Win4Assert(((hr == S_OK && xunk != NULL) ||
(hr != S_OK && xunk == NULL)) &&
"DoUnamrshal CoUnmarshalInterface failure");
if (SUCCEEDED(hr))
{
hr = xunk->QueryInterface(riid, ppvUnk);
}
//CODEWORK: Stress revealed CoGetClassObject returning a null class factory
// and S_OK
Win4Assert(((hr == S_OK && *ppvUnk != NULL) ||
(hr != S_OK && *ppvUnk == NULL))
&& "DoUnamrshal QueryInterface failure");
MyMemFree(pIFD);
return hr;
}
//+-------------------------------------------------------------------------
//
// Function: UnmarshalSCMResult
//
// 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 UnmarshalSCMResult(
HRESULT& hr,
InterfaceData *pIFD,
REFCLSID rclsid,
REFIID riid,
void **ppvUnk,
DWORD dwDllThreadModel,
WCHAR *pwszDllPath,
void **ppvCf)
{
BOOL fResult = TRUE;
#ifndef _UNICODE
TCHAR *ptszDllPath;
UINT cb;
int ret;
#else // !_UNICODE
const TCHAR *ptszDllPath;
#endif // _UNICODE
if (SUCCEEDED(hr))
{
// 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",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;
}
}
// Store handler class object -- when the request to unmarshal the
// object call CoGetClassObject, the call will automatically find
// this handler in the cache.
#ifndef _UNICODE
// Now that SCM doesn't return inproc results we should never exercise
// this code path. It is here for completeness.
ptszDllPath = NULL;
Win4Assert((pwszDllPath) && "UnmarshalSCMResult: (pwszDllPath == NULL)");
cb = (lstrlenW(pwszDllPath) + 1) * sizeof(WCHAR);
ptszDllPath = (LPTSTR) PrivMemAlloc(cb);
if (ptszDllPath == NULL)
{
hr = E_OUTOFMEMORY;
return(TRUE);
}
ret = WideCharToMultiByte( (AreFileApisANSI())?CP_ACP:CP_OEMCP,WC_COMPOSITECHECK,
pwszDllPath, -1, ptszDllPath, cb, NULL, NULL);
#if DBG==1
CairoleAssert(ret != 0 && "Lost characters in Unicode->Ansi conversion");
#endif
#else // !_UNICODE
ptszDllPath = pwszDllPath;
#endif // _UNICODE
gdllcacheHandler.Add(rclsid, IID_IClassFactory, dwDllThreadModel,
ptszDllPath, FALSE,(hr == SCM_S_HANDLER16),
#ifdef WX86OLE
(hr == SCM_S_HANDLERX86),
#endif
hr);
#ifndef _UNICODE
PrivMemFree(ptszDllPath);
#endif
ptszDllPath = NULL;
if (FAILED(hr))
{
return TRUE;
}
// Fall into unmarshal code if we also have an interface pointer
if ( pIFD == NULL )
{
break;
}
#endif // GET_INPROC_FROM_SCM
case S_OK :
hr = DoUnmarshal(pIFD, riid, ppvUnk);
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.
#ifndef _UNICODE
ptszDllPath = NULL;
Win4Assert((pwszDllPath) && "UnmarshalSCMResult: (pwszDllPath == NULL)");
cb = (lstrlenW(pwszDllPath) + 1) * sizeof(WCHAR);
ptszDllPath = (LPTSTR) PrivMemAlloc(cb);
if (ptszDllPath == NULL)
{
hr = E_OUTOFMEMORY;
return(TRUE);
}
ret = WideCharToMultiByte( (AreFileApisANSI())?CP_ACP:CP_OEMCP,WC_COMPOSITECHECK,
pwszDllPath, -1, ptszDllPath, cb, NULL, NULL);
#if DBG==1
CairoleAssert(ret != 0 && "Lost characters in Unicode->Ansi conversion");
#endif
#else // !_UNICODE
ptszDllPath = pwszDllPath;
#endif // _UNICODE
*ppvCf = gdllcacheInprocSrv.Add(rclsid, IID_IClassFactory,
dwDllThreadModel, ptszDllPath, TRUE,
(hr == SCM_S_INPROCSERVER16),
#ifdef WX86OLE
(hr == SCM_S_INPROCSERVERX86),
#endif
hr);
#ifndef _UNICODE
PrivMemFree(ptszDllPath);
#endif
ptszDllPath = NULL;
// 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) && "UnmarshalSCMResult: SCM_S_INPROC return from SCM");
#endif // GET_INPROC_FROM_SCM
}
}
return fResult;
}
//+-------------------------------------------------------------------------
//
// Function: CoGetPersistentInstance
//
// Synopsis: Returns an instantiated interface to an object whose
// stored state resides on disk.
//
// Arguments: [riid] - interface to bind object to
// [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
// [rclsidOle1] -- ClassID if OLE 1.0 (CLSID_NULL otherwise)
// [pfOle1Loaded] -- Returns TRUE if loaded as OLE 1.0
// [ppvUnk] - where to put bound interface pointer
//
// Returns: S_OK - object bound successfully
// MISSING
//
// Algorithm: Parameters are validated first. Then the class ID is retrieved
// from the storage. We check whether we can use a cached
// in process server. If we can't we call through to the
// SCM to get the information. We call UnmarshalSCMResult
// to process the return from the SCM. If the result is
// completely processed, we return that result to the caller.
// Otherwise, if the server is inprocess, we go through the
// steps to create and bind the interface by calling
// GetObjectHelper. Finally, we QI to the requested interface.
//
// History: 11-May-93 Ricksa Created
//
//--------------------------------------------------------------------------
#ifdef _CHICAGO_
STDAPI CoGetPersistentInstance(
REFIID riid,
DWORD dwCtrl,
DWORD grfMode,
WCHAR *pwszName,
struct IStorage *pstg,
REFCLSID rclsidOle1,
BOOL * pfOle1Loaded,
void **ppvUnk)
{
TRACECALL(TRACE_ACTIVATION, "CoGetPersistentInstance");
CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IStorage,(IUnknown **)&pstg);
if (!IsApartmentInitialized())
return CO_E_NOTINITIALIZED;
IClassFactory *pcf = NULL;
WCHAR *pwszDllPath = NULL;
InterfaceData *pIFD = NULL;
IUnknown *punk = NULL;
WCHAR awcNameBuf[MAX_PATH];
WCHAR *pwszNameUNC = awcNameBuf;
WCHAR awcServer[MAX_PATH];
WCHAR *pwszServer = awcServer;
DWORD dwDllTypeToGet = IsSTAThread() ? APT_THREADED : FREE_THREADED;
DWORD dwDllServerType;
DWORD dwHash = 0;
COleTls Tls;
HRESULT hr;
BEGIN_BLOCK
// Set output parameter in case of error.
*ppvUnk = NULL;
// Make sure input request is at least slightly logical
if (((pstg != NULL) && (pwszName != NULL))
|| ((pstg == NULL) && (pwszName == NULL))
|| ((dwCtrl & ~CLSCTX_SERVER) != 0))
{
hr = E_INVALIDARG;
EXIT_BLOCK;
}
// Make sure we are asking for the correct inproc server
dwCtrl = RemapClassCtxForInProcServer(dwCtrl);
CLSID clsid;
if(pfOle1Loaded != NULL)
{
*pfOle1Loaded = FALSE;
}
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))
{
EXIT_BLOCK;
}
// 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 ((hr = GetObjectByPath(pwszName, ppvUnk, riid)) != S_FALSE)
{
// Got object from ROT so we are done.
return hr;
}
// Try to get the class of the file
hr = GetClassFileEx(pwszName, &clsid, rclsidOle1);
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 (rclsidOle1 != CLSID_NULL)
{
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;
EXIT_BLOCK;
}
if (hr != MK_E_CANTOPENFILE)
hr = DdeBindToObject(pwszName,
rclsidOle1,
FALSE,
riid,
ppvUnk);
if(pfOle1Loaded != NULL)
{
*pfOle1Loaded = TRUE;
}
}
EXIT_BLOCK;
}
}
else
{
pwszNameUNC = NULL;
pwszServer = NULL;
STATSTG statstg;
if (FAILED(hr = pstg->Stat(&statstg, STATFLAG_NONAME)))
{
EXIT_BLOCK;
}
clsid = statstg.clsid;
}
GetTreatAs(clsid, clsid);
//
// If this is a OLE 1.0 class, then do a DdeBindToObject on it,
// and return.
//
if (CoIsOle1Class(clsid))
{
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;
EXIT_BLOCK;
}
if (pwszName != NULL)
{
if (hr != MK_E_CANTOPENFILE)
hr = DdeBindToObject(pwszName,clsid,FALSE,riid,ppvUnk);
if(pfOle1Loaded != NULL)
{
*pfOle1Loaded = TRUE;
}
EXIT_BLOCK;
}
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"));
}
}
// Default to success at this point
hr = S_OK;
// We cache information about in process servers so we look in the
// cache first in hopes of saving some time.
pcf = (IClassFactory *)
SearchCacheOrLoadInProc(clsid,
IID_IClassFactory,
(pwszServer != NULL),
FALSE,
dwCtrl,
dwDllTypeToGet,
hr);
if ((pcf == NULL)
&& ((hr = GetMultiStepClassFactory(clsid, dwCtrl, &pcf))
== S_FALSE))
{
// Marshal pstg since SCM can't deal with unmarshaled objects
fsCSafeStgMarshaled sms(pstg, MSHCTX_DIFFERENTMACHINE, hr);
if (FAILED(hr))
{
EXIT_BLOCK;
}
BOOL fExitBlock;
DWORD cLoops = 0;
do
{
dwDllServerType = dwDllTypeToGet;
#ifndef GET_INPROC_FROM_SCM
// Just in case we chicken out and back out our changes
dwCtrl &= ~(CLSCTX_INPROC_SERVERS | CLSCTX_INPROC_HANDLERS); // make sure we don't ask for inproc stuff
#endif // GET_INPROC_FROM_SCM
// Forward call to service controller
hr = gResolver.ActivateObject(clsid, dwCtrl, grfMode,
pwszNameUNC, sms, &pIFD, &dwDllServerType,
&pwszDllPath, pwszServer);
fExitBlock = UnmarshalSCMResult(hr, pIFD, clsid, riid, ppvUnk,
dwDllServerType, pwszDllPath, (void **) &pcf);
if ((hr != NOERROR) && (dwDllServerType == GOT_FROM_ROT))
{
// Got something from the ROT. We need to make sure the
// ROT is clean, so we try accessing the ROT again by
// name which will go through the logic that cleans the
// table of bogus entries. Note that we can actually find
// a valid item while we are doing this clean up.
if ((hr = GetObjectByPath(pwszName, ppvUnk, riid))
!= S_FALSE)
{
// Got object from ROT so we are done.
return hr;
}
// This might be our last loop so make sure that this
// will return an error. Since this is a highly unexpected
// error case, we return that.
hr = E_UNEXPECTED;
}
// 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) && (dwDllServerType == GOT_FROM_ROT)
&& (++cLoops < 5));
if (fExitBlock)
{
EXIT_BLOCK;
}
}
if (SUCCEEDED(hr))
{
hr = GetObjectHelper(pcf, grfMode, pwszName, pstg, NULL, &punk);
if (SUCCEEDED(hr))
{
hr = punk->QueryInterface(riid, ppvUnk);
}
}
END_BLOCK;
if (pcf != NULL)
{
pcf->Release();
}
if (punk != NULL)
{
punk->Release();
}
// RPC stubs allocated path so we trust that it is null or valid.
if (pwszDllPath != NULL)
{
MyMemFree(pwszDllPath);
}
CALLHOOKOBJECT(hr,rclsidOle1,riid,(IUnknown **)ppvUnk);
return hr;
}
#endif
//+-------------------------------------------------------------------------
//
// Function: CoGetClassObject
//
// Synopsis: External entry point that returns an instantiated class object
//
// Arguments: [rclsid] - class id for class object
// [dwContext] - kind of server we wish
// [pvReserved] - Reserved for future use
// [riid] - interface to bind class object
// [ppvClassObj] - where to put interface pointer
//
// Returns: S_OK - successfully bound class object
//
// Algorithm: Validate all then parameters and then pass this to the
// internal version of the call.
//
// History: 11-May-93 Ricksa Created
// 11-May-94 KevinRo Added OLE 1.0 support
// 23-May-94 AlexT Make sure we're initialized!
// 15-Nov-94 Ricksa Split into external and internal calls
//
//
//--------------------------------------------------------------------------
STDAPI CoGetClassObject(
REFCLSID rclsid,
DWORD dwContext,
LPVOID pvReserved,
REFIID riid,
void FAR* FAR* ppvClassObj)
{
OLETRACEIN((API_CoGetClassObject, PARAMFMT("rclsid= %I, dwContext= %x, pvReserved= %p, riid= %I, ppvClassObj= %p"),
&rclsid, dwContext, pvReserved, &riid, ppvClassObj));
TRACECALL(TRACE_ACTIVATION, "CoGetClassObject");
HRESULT hr = E_INVALIDARG;
if (ppvClassObj)
{
*ppvClassObj = NULL;
}
if (!IsApartmentInitialized())
{
CairoleDebugOut((DEB_ERROR, "CoGetClassObject failed - Appartment not initialized\n"));
hr = CO_E_NOTINITIALIZED;
goto errRtn;
}
// Validate parameters
if (IsValidPtrIn(&rclsid, sizeof(CLSID))
&& IsValidPtrIn(&riid, sizeof(IID))
&& IsValidPtrOut(ppvClassObj, sizeof(void *))
#ifdef WX86OLE
&& ((dwContext & ~(CLSCTX_ALL | CLSCTX_INPROC_SERVER16 |
CLSCTX_INPROC_SERVERX86 |
CLSCTX_NO_REMAP |
CLSCTX_PS_DLL |
CLSCTX_INPROC_HANDLERX86)) == 0))
#else
&& ((dwContext & ~(CLSCTX_ALL | CLSCTX_INPROC_SERVER16 |
CLSCTX_PS_DLL)) == 0))
#endif
{
*ppvClassObj = NULL;
// Make sure we are asking for the correct inproc server
dwContext = RemapClassCtxForInProcServer(dwContext);
#ifdef DCOM
hr = ICoGetClassObject(rclsid,
dwContext,
(COSERVERINFO *) pvReserved,
riid,
ppvClassObj);
#else
hr = IOldCoGetClassObject(rclsid, dwContext, pvReserved, riid,
ppvClassObj);
#endif
}
errRtn:
OLETRACEOUT((API_CoGetClassObject, hr));
return hr;
}
#ifndef DCOM
//+-------------------------------------------------------------------------
//
// Function: IOldCoGetClassObject
//
// 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 IOldCoGetClassObject(
REFCLSID rclsid,
DWORD dwContext,
LPVOID pvReserved,
REFIID riid,
void FAR* FAR* ppvClassObj)
{
TRACECALL(TRACE_ACTIVATION, "CoGetClassObject");
IUnknown *punk = NULL;
HRESULT hr = S_OK;
WCHAR *pwszDllToLoad = NULL;
InterfaceData *pIFD = NULL;
CLSID clsid;
DWORD dwDllServerType = IsSTAThread() ? APT_THREADED : FREE_THREADED;
#ifndef _UNICODE
TCHAR *ptszDllToLoad;
UINT cb;
int ret;
#else // !_UNICODE
const TCHAR *ptszDllToLoad;
#endif // _UNICODE
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;
}
// Make sure we are asking for the correct inproc server
// We do this here even though it might already be done in CoGetClassObject
// as we could get here directly from CoCreateInstance.
dwContext = RemapClassCtxForInProcServer(dwContext);
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); // make sure we don't ask for inproc stuff
#endif // GET_INPROC_FROM_SCM
hr = gResolver.OldGetClassObject(clsid, dwContext, NULL, &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)
{
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));
#ifdef GET_INPROC_FROM_SCM
// 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;
#endif // GET_INPROC_FROM_SCM
#ifndef _UNICODE
// Now that SCM doesn't return inproc results we should never exercise
// this code path. It is here for completeness.
ptszDllToLoad = NULL;
Win4Assert((pwszDllToLoad) && "IOldCoGetClassObject: (pwszDllToLoad == NULL)");
cb = (lstrlenW(pwszDllToLoad) + 1) * sizeof(WCHAR);
ptszDllToLoad = (LPTSTR) PrivMemAlloc(cb);
if (ptszDllToLoad == NULL)
{
return(E_OUTOFMEMORY);
}
ret = WideCharToMultiByte( (AreFileApisANSI())?CP_ACP:CP_OEMCP,WC_COMPOSITECHECK,
pwszDllToLoad, -1, ptszDllToLoad, cb, NULL, NULL);
#if DBG==1
CairoleAssert(ret != 0 && "Lost characters in Unicode->Ansi conversion");
#endif
#else // !_UNICODE
ptszDllPath = pwszDllToLoad;
#endif // _UNICODE
// Store the handler returned
punk = gdllcacheHandler.Add(clsid, riid, dwDllServerType,
ptszDllToLoad, fGetClassObject,
(hr == SCM_S_HANDLER16),
#ifdef WX86OLE
(hr == SCM_S_HANDLERX86),
#endif
hr);
#ifndef _UNICODE
PrivMemFree(ptszDllToLoad);
#endif
ptszDllToLoad = NULL;
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.
case S_OK :
hr = DoUnmarshal(pIFD, riid, (void **) &punk);
break;
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));
#ifdef GET_INPROC_FROM_SCM
// Just in case we chicken out and back out our changes
// In process server for class object
#ifndef _UNICODE
ptszDllToLoad = NULL;
Win4Assert((pwszDllToLoad) && "IOldCoGetClassObject: (pwszDllToLoad == NULL)");
cb = (lstrlenW(pwszDllToLoad) + 1) * sizeof(WCHAR);
ptszDllToLoad = (LPTSTR) PrivMemAlloc(cb);
if (ptszDllToLoad == NULL)
{
return(E_OUTOFMEMORY);
}
ret = WideCharToMultiByte( (AreFileApisANSI())?CP_ACP:CP_OEMCP,WC_COMPOSITECHECK,
pwszDllToLoad, -1, ptszDllToLoad, cb, NULL, NULL);
#if DBG==1
CairoleAssert(ret != 0 && "Lost characters in Unicode->Ansi conversion");
#endif
#else // !_UNICODE
ptszDllPath = pwszDllToLoad;
#endif // _UNICODE
punk = gdllcacheInprocSrv.Add(clsid, riid, dwDllServerType,
ptszDllToLoad, TRUE,(hr == SCM_S_INPROCSERVER16),
#ifdef WX86OLE
(hr == SCM_S_INPROCSERVERX86),
#endif
hr);
#ifndef _UNICODE
PrivMemFree(ptszDllToLoad);
#endif
ptszDllToLoad = NULL;
#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;
}
#endif // !DCOM
#ifndef GET_INPROC_FROM_SCM
// Just in case we chicken out and back out our changes
//+---------------------------------------------------------------------------
//
// Function: SearchCacheOrLoadInProc
//
// Synopsis: Searches the cache for requested classid.
// If not found looks to see if an inproc server or handler
// can be loaded (if requested).
//
// Arguments: [rclsid] Class ID
// [riid] Interface required of class object
// [fRemote] Whether path is remote
// [fForScm] Whether it's the scm requesting
// [dwContext] Which context to load
// [dwCallerThreadModel] Which threading model to load
// [hr] Reference to HRESULT for error returns
//
// Returns: ~NULL - Class factory for object
// NULL - Class factory could not be found or
// constructed.
//
// History: 2-5-96 murthys Created
//
// Notes:
//
//----------------------------------------------------------------------------
IUnknown *SearchCacheOrLoadInProc(REFCLSID rclsid,
REFIID riid,
BOOL fRemote,
BOOL fForSCM,
DWORD dwContext,
DWORD dwDllServerType,
HRESULT &hr)
{
IUnknown *punk = NULL;
#ifdef WX86OLE
if (gcwx86.IsWx86Enabled())
{
if (dwContext & CLSCTX_INPROC_SERVERX86)
{
// Search cache for in process
punk = gdllcacheInprocSrv.GetOrLoadClass(rclsid,
riid,
FALSE,
FALSE,
TRUE,
dwContext & CLSCTX_INPROC_SERVERS,
dwDllServerType,
hr);
}
if ((punk == NULL) && (dwContext & CLSCTX_INPROC_HANDLERX86))
{
// Search cache for a cached handler DLL
punk = gdllcacheHandler.GetOrLoadClass(rclsid,
riid,
FALSE,
FALSE,
TRUE,
dwContext & CLSCTX_INPROC_HANDLERS,
dwDllServerType,
hr);
}
}
#endif
if ((punk == NULL) &&
(dwContext & (CLSCTX_INPROC_SERVERS) ))
{
punk = gdllcacheInprocSrv.GetOrLoadClass(rclsid,
riid,
FALSE,
FALSE,
#ifdef WX86OLE
FALSE,
#endif
dwContext & CLSCTX_INPROC_SERVERS,
dwDllServerType,
hr);
}
if (punk == NULL && dwContext & CLSCTX_INPROC_HANDLERS)
{
punk = gdllcacheHandler.GetOrLoadClass(rclsid,
riid,
FALSE,
FALSE,
#ifdef WX86OLE
FALSE,
#endif
dwContext & CLSCTX_INPROC_HANDLERS,
dwDllServerType,
hr);
}
return(punk);
}
#endif // GET_INPROC_FROM_SCM