//+------------------------------------------------------------------------- // // 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 #include #include #include #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; iRelease(); 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; ihr = 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; iRelease(); } 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; iQueryInterface(*(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; iCreateInstance(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; iQueryInterface( *(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; }