//+------------------------------------------------------------------------- // // 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 #include #include #include #include #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