//+------------------------------------------------------------------- // // File: marshal.cxx // // Contents: class implementing standard COM interface marshaling // // Classes: CStdMarshal // // History: 20-Feb-95 Rickhi Created // // DCOMWORK: (maybe) implement Extended form marshal packet // // PERFWORK: during unmarshal and RMD compare the MOXID in the STDOBJREF // to the one for the current apartment. If equal, then i know the IPID is // just an index into the IPID table and i can index into it, grab the // channel ptr and hence the stdid ptr and do very fast unmarshal or RMD // with no table lookup or list walking. // //-------------------------------------------------------------------- #include #include // CStdMarshal #include // CIPIDTable, COXIDTable, CMIDTable #include // CRIFTable #include // CRpcResolver #include // CStdIdentity #include // CRpcChannelBuffer #include // CAptRpcChnl, CSrvCallCtrl #include // CLSCTX_PS_DLL #include // SASIZE #include // LOCK/UNLOCK etc #include // GetAppCompatabilityFlags #if DBG==1 // this flag and interface are used in debug to enable simpler testing // of the esoteric NonNDR stub code feature. BOOL gfFakeNonNDR = FALSE; const GUID IID_ICube = {0x00000139,0x0001,0x0008,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; #endif // DBG // BUGBUG: this is not quite reliable enough. Maybe best solution is // CoGetCurrentProcessId plus sequence number. LONG gIPIDSeqNum = 0; // mappings from MSHLFLAGS to STDOBJREF flags static ULONG mapMFtoSORF[] = { SORF_NULL, // MSHLFLAGS_NORMAL SORF_NULL, // MSHLFLAGS_TABLESTRONG SORF_TBLWEAK // MSHLFLAGS_TABLEWEAK }; // NULL resolver string array DUALSTRINGARRAY saNULL = {0,0}; // number of remote AddRefs to acquire when we need more. #define REM_ADDREF_CNT 5 // out internal psclass factory implementation EXTERN_C HRESULT PrxDllGetClassObject(REFCLSID clsid, REFIID iid, void **ppv); // structure used to post a delayed remote release call to ourself. typedef struct tagPOSTRELRIFREF { OXIDEntry *pOXIDEntry; // server OXIDEntry USHORT cRifRef; // count of entries in arRifRef REMINTERFACEREF arRifRef; // array of REMINTERFACEREFs } POSTRELRIFREF; //+------------------------------------------------------------------- // // Member: CStdMarshal::CStdMarshal/Init, public // // Synopsis: constructor/initializer of a standard marshaler // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- CStdMarshal::CStdMarshal() : _dwFlags(0), _pChnl(NULL) { // Caller must call Init before doing anything! This just makes it // easier for the identity object to figure out the init parameters // before initializing us. } void CStdMarshal::Init(IUnknown *punkObj, CStdIdentity *pStdId, REFCLSID rclsidHandler, DWORD dwFlags) { ASSERT_LOCK_DONTCARE // may be released if def handler calls CreateIdHdlr // server side we need to do the FirstMarshal work. // client side we assume disconnected until we connect the first IPIDEntry // and assume NOPING until we see any interface that needs pinging _dwFlags = dwFlags; _dwFlags |= (ServerSide()) ? SMFLAGS_FIRSTMARSHAL : SMFLAGS_DISCONNECTED | SMFLAGS_NOPING; _pFirstIPID = NULL; _cIPIDs = 0; _pStdId = pStdId; _pChnl = NULL; _cNestedCalls = 0; _cTableRefs = 0; _dwMarshalTime = 0; _clsidHandler = rclsidHandler; _pSecureRemUnk = NULL; ComDebOut((DEB_MARSHAL,"CStdMarshal %s New this:%x pStdId:%x punkObj:%x\n", (ClientSide()) ? "CLIENT" : "SERVER", this, pStdId, punkObj)); AssertValid(); } //+------------------------------------------------------------------- // // Member: CStdMarshal::~CStdMarshal, public // // Synopsis: destructor of a standard marshaler // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- CStdMarshal::~CStdMarshal() { ComDebOut((DEB_MARSHAL, "CStdMarshal %s Deleted this:%x\n", (ClientSide()) ? "CLIENT" : "SERVER", this)); ASSERT_LOCK_RELEASED if (ClientSide()) { // Due to backward compatibility, we are not allowed to release // interface proxies in Disconnect since the client might try to // reconnect later and expects the same interface pointer values. // Since we are going away now, we go release the proxies. ReleaseCliIPIDs(); if (_pSecureRemUnk != NULL) { _pSecureRemUnk->Release(); } } if (_pChnl) { // release the channel _pChnl->Release(); } ASSERT_LOCK_RELEASED } //+------------------------------------------------------------------- // // Member: CStdMarshal::GetUnmarshalClass, public // // Synopsis: returns the clsid of the standard marshaller // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- STDMETHODIMP CStdMarshal::GetUnmarshalClass(REFIID riid, LPVOID pv, DWORD dwDestCtx, LPVOID pvDestCtx, DWORD mshlflags, LPCLSID pClsid) { AssertValid(); ASSERT_LOCK_RELEASED *pClsid = CLSID_StdMarshal; return S_OK; } //+------------------------------------------------------------------- // // Member: CStdMarshal::GetMarshalSizeMax, public // // Synopsis: Returns an upper bound on the amount of data for // a standard interface marshal. // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- STDMETHODIMP CStdMarshal::GetMarshalSizeMax(REFIID riid, LPVOID pv, DWORD dwDestCtx, LPVOID pvDestCtx, DWORD mshlflags, LPDWORD pSize) { AssertValid(); Win4Assert(gdwPsaMaxSize != 0); ASSERT_LOCK_RELEASED *pSize = sizeof(OBJREF) + gdwPsaMaxSize; return S_OK; } //+------------------------------------------------------------------- // // Function: MarshalObjRef, private // // Synopsis: Marshals interface into the objref. // // Arguements: [objref] - object reference // [riid] - interface id to marshal // [pv] - interface to marshal // [mshlflags] - marshal flags // // Algorithm: Get the correct standard identity and ask it to do // all the work. // // History: 25-Mar-95 AlexMit Created // //-------------------------------------------------------------------- INTERNAL MarshalObjRef(OBJREF &objref, REFIID riid, void *pv, DWORD mshlflags) { TRACECALL(TRACE_MARSHAL, "MarshalObjRef"); ComDebOut((DEB_MARSHAL, "MarshalObjRef: riid:%I pv:%x flags:%x\n", &riid, pv, mshlflags)); ASSERT_LOCK_RELEASED HRESULT hr = InitChannelIfNecessary(); if (SUCCEEDED(hr)) { // Find or create the StdId for this object. We need to get a strong // reference to guard against an incoming last release on another // thread which would cause us to Disconnect this StdId. DWORD dwFlags = IDLF_CREATE | IDLF_STRONG; dwFlags |= (mshlflags & MSHLFLAGS_NOPING) ? IDLF_NOPING : 0; CStdIdentity *pStdID; hr = LookupIDFromUnk((IUnknown *)pv, dwFlags, &pStdID); if (hr == NOERROR) { hr = pStdID->MarshalObjRef(objref, riid, pv, mshlflags); pStdID->DecStrongCnt(TRUE); // fKeepAlive } } ASSERT_LOCK_RELEASED ComDebOut((DEB_MARSHAL, "MarshalObjRef: hr:%x\n", hr)); return hr; } //+------------------------------------------------------------------- // // Function: MarshalInternalObjRef, private // // Synopsis: Marshals an internal interface into the objref. // // Arguements: [objref] - object reference // [riid] - interface id to marshal // [pv] - interface to marshal // [mshlflags] - marshal flags // [ppStdId] - StdId to return (may be NULL) // // Algorithm: Create a StdIdentity and ask it to do the work. // // Notes: This differs from the normal MarshalObjRef in that it does // not look in the OID table for an already marshaled interface, // nor does it register the marshaled interface in the OID table. // This is used for internal interfaces such as the IObjServer // and IRemUnknown. // // History: 25-Oct-95 Rickhi Created // //-------------------------------------------------------------------- INTERNAL MarshalInternalObjRef(OBJREF &objref, REFIID riid, void *pv, DWORD mshlflags, void **ppStdId) { TRACECALL(TRACE_MARSHAL, "MarshalInternalObjRef"); ComDebOut((DEB_MARSHAL, "MarshalInternalObjRef: riid:%I pv:%x flags:%x\n", &riid, pv, mshlflags)); ASSERT_LOCK_RELEASED HRESULT hr = InitChannelIfNecessary(); if (SUCCEEDED(hr)) { if (!IsEqualGUID(riid, IID_IRundown)) { // NOTE: make sure the local OXID is registered with the resolver. // See the discussion on the Chicken and Egg problem in ipidtbl.cxx // COXIDTable::GetLocalEntry for why this is necessary. LOCK MOID moid; hr = gResolver.ServerGetPreRegMOID(&moid); UNLOCK } if (SUCCEEDED(hr)) { // Find or create the StdId for this object. We need to get a strong // reference to guard against an incoming last release on another // thread which would cause us to Disconnect this StdId. IUnknown *pUnkId; // ignored CStdIdentity *pStdId = new CStdIdentity(STDID_SERVER, NULL, (IUnknown *)pv, &pUnkId); if (pStdId != NULL) { hr = pStdId->MarshalObjRef(objref, riid, pv, mshlflags); if (SUCCEEDED(hr) && ppStdId) { *ppStdId = (void *)pStdId; } else { pStdId->Release(); } } else { hr = E_OUTOFMEMORY; } } } ASSERT_LOCK_RELEASED ComDebOut((DEB_MARSHAL, "MarshalInternalObjRef: hr:%x\n", hr)); return hr; } //+------------------------------------------------------------------- // // Member: CStdMarshal::MarshalInterface, public // // Synopsis: marshals the interface into the stream. // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- STDMETHODIMP CStdMarshal::MarshalInterface(IStream *pStm, REFIID riid, LPVOID pv, DWORD dwDestCtx, LPVOID pvDestCtx, DWORD mshlflags) { ComDebOut((DEB_MARSHAL, "CStdMarshal::MarshalInterface this:%x pStm:%x riid:%I pv:%x dwCtx:%x pvCtx:%x flags:%x\n", this, pStm, &riid, pv, dwDestCtx, pvDestCtx, mshlflags)); AssertValid(); ASSERT_LOCK_RELEASED // Marshal the interface into an objref, then write the objref // into the provided stream. OBJREF objref; HRESULT hr = MarshalObjRef(objref, riid, pv, mshlflags); if (SUCCEEDED(hr)) { // write the objref into the stream hr = WriteObjRef(pStm, objref, dwDestCtx); if (FAILED(hr)) { // undo whatever we just did, ignore error from here since // the stream write error supercedes any error from here. ReleaseMarshalObjRef(objref); } // free resources associated with the objref. FreeObjRef(objref); } ASSERT_LOCK_RELEASED ComDebOut((DEB_MARSHAL,"CStdMarshal::MarshalInterface this:%x hr:%x\n", this, hr)); return hr; } //+------------------------------------------------------------------- // // Member: CStdMarshal::MarshalObjRef, public // // Synopsis: marshals the interface into the objref. // // History: 25-Mar-95 AlexMit Seperated from MarshalInterface // //-------------------------------------------------------------------- HRESULT CStdMarshal::MarshalObjRef(OBJREF &objref, REFIID riid, LPVOID pv, DWORD mshlflags) { ComDebOut((DEB_MARSHAL, "CStdMarshal::MarsalObjRef this:%x riid:%I pv:%x flags:%x\n", this, &riid, pv, mshlflags)); AssertValid(); // validate the parameters. we dont allow TABLE cases if we are // a client side object. if ((mshlflags & MSHLFLAGS_TABLE) && ClientSide()) return E_INVALIDARG; // count of Refs we are handing out. In the table cases we pass out // zero refs because we dont know how many times it will be unmarshaled // (and hence how many references to count). Zero refs will cause the // client to call back and ask for more references if it does not already // have any (which has the side effect of making sure the object still // exists, which is required by RunningObjectTable). ULONG cRefs = (mshlflags & MSHLFLAGS_TABLE) ? 0 : (ClientSide()) ? 1 : REM_ADDREF_CNT; ASSERT_LOCK_RELEASED LOCK HRESULT hr = PreventDisconnect(); if (SUCCEEDED(hr)) { // The first time through we have some extra work to do so go off // and do that now. Next time we can just bypass all that work. if (_dwFlags & SMFLAGS_FIRSTMARSHAL) { hr = FirstMarshal((IUnknown *)pv, mshlflags); } if (SUCCEEDED(hr)) { // Create the IPID table entry. On the server side this may // cause the creation of an interface stub, on the client side // it may just take away one of our references or it may call // the server to get more references for the interface being // marshaled. IPIDEntry *pIPIDEntry; hr = MarshalIPID(riid, cRefs, mshlflags, &pIPIDEntry); if (SUCCEEDED(hr)) { // fill in the rest of the OBJREF FillObjRef(objref, cRefs, mshlflags, pIPIDEntry); } } } UNLOCK ASSERT_LOCK_RELEASED // it is now OK to allow real disconnects in. HRESULT hr2 = HandlePendingDisconnect(hr); if (FAILED(hr2) && SUCCEEDED(hr)) { // a disconnect came in while marshaling. The ObjRef has a // reference to the OXIDEntry so go free that now. FreeObjRef(objref); } ComDebOut((DEB_MARSHAL, "CStdMarshal::MarshalObjRef this:%x hr:%x\n", this, hr2)); return hr2; } //+------------------------------------------------------------------- // // Member: CStdMarshal::FillObjRef, private // // Synopsis: Fill in the fields of an OBJREF // // History: 21-Sep-95 Rickhi Created // //+------------------------------------------------------------------- void CStdMarshal::FillObjRef(OBJREF &objref, ULONG cRefs, DWORD mshlflags, IPIDEntry *pIPIDEntry) { ComDebOut((DEB_MARSHAL, "FillObjRef pObjRef:%x\n", &objref)); ASSERT_LOCK_HELD AssertDisconnectPrevented(); Win4Assert(pIPIDEntry); OXIDEntry **ppOXIDEntry; // first, fill in the STDOBJREF section STDOBJREF *pStd = &ORSTD(objref).std; FillSTD(pStd, cRefs, mshlflags, pIPIDEntry); // next fill in the rest of the OBJREF objref.signature = OBJREF_SIGNATURE; // 'MEOW' objref.iid = pIPIDEntry->iid; // interface iid if (_dwFlags & SMFLAGS_HANDLER) { // handler form, copy in the clsid objref.flags = OBJREF_HANDLER; ORHDL(objref).clsid = _clsidHandler; ppOXIDEntry = (OXIDEntry **) &ORHDL(objref).saResAddr; } else { objref.flags = OBJREF_STANDARD; ppOXIDEntry = (OXIDEntry **) &ORSTD(objref).saResAddr; } // TRICK: in order to keep the objref a fixed size internally, // we use the saResAddr.size field as a ptr to the OXIDEntry. We // pay attention to this in ReadObjRef, WriteObjRef, and FreeObjRef. *ppOXIDEntry = pIPIDEntry->pOXIDEntry; Win4Assert(*ppOXIDEntry != NULL); IncOXIDRefCnt(*ppOXIDEntry); ASSERT_LOCK_HELD } //+------------------------------------------------------------------- // // Member: CStdMarshal::FillSTD, public // // Synopsis: Fill in the STDOBJREF fields of an OBJREF // // History: 21-Sep-95 Rickhi Created // //+------------------------------------------------------------------- void CStdMarshal::FillSTD(STDOBJREF *pStd, ULONG cRefs, DWORD mshlflags, IPIDEntry *pIPIDEntry) { // fill in the STDOBJREF to return to the caller. pStd->flags = mapMFtoSORF[mshlflags & MSHLFLAGS_TABLE]; pStd->flags |= (pIPIDEntry->dwFlags & IPIDF_NOPING) ? SORF_NOPING : 0; pStd->flags |= (pIPIDEntry->dwFlags & IPIDF_NONNDRSTUB) ? SORF_NONNDR : 0; pStd->cPublicRefs = cRefs; pStd->ipid = pIPIDEntry->ipid; OIDFromMOID(_pStdId->GetOID(), &pStd->oid); OXIDFromMOXID(pIPIDEntry->pOXIDEntry->moxid, &pStd->oxid); ValidateSTD(pStd); DbgDumpSTD(pStd); } //+------------------------------------------------------------------- // // Member: CStdMarshal::FirstMarshal, private // // Synopsis: Does some first-time server side marshal stuff // // Parameters: [pUnk] - interface being marshalled // [mshlflags] - flags for marshaling // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- HRESULT CStdMarshal::FirstMarshal(IUnknown *pUnk, DWORD mshlflags) { ComDebOut((DEB_MARSHAL, "CStdMarshal::FirstMarshal this:%x pUnk:%x\n", this, pUnk)); Win4Assert(ServerSide()); Win4Assert(_dwFlags & SMFLAGS_FIRSTMARSHAL); Win4Assert(_pChnl == NULL); AssertValid(); AssertDisconnectPrevented(); ASSERT_LOCK_HELD // have now executed this code so dont do it again. _dwFlags &= ~SMFLAGS_FIRSTMARSHAL; if (mshlflags & MSHLFLAGS_NOPING) { // if the first interface is marked as NOPING, then all interfaces // for the object are treated as NOPING, otherwise, all interfaces // are marked as PING. MakeSrvIPIDEntry will look at _dwFlags to // determine whether to mark each IPIDEntry as NOPING or not. _dwFlags |= SMFLAGS_NOPING; } // get our local OXID. This should have already been created, and // so wont cause the LOCK to be released. OXIDEntry *pOXIDEntry; HRESULT hr = gOXIDTbl.GetLocalEntry(&pOXIDEntry); if (SUCCEEDED(hr)) { // create a channel for this object. CRpcChannelBuffer *pChnl; hr = CreateChannel(pOXIDEntry, 0, GUID_NULL, GUID_NULL, &pChnl); } ASSERT_LOCK_HELD AssertDisconnectPrevented(); ComDebOut((DEB_MARSHAL, "CStdMarshal::FirstMarshal this:%x hr:%x\n", this, hr)); return hr; } //+------------------------------------------------------------------- // // Member: CStdMarshal::MarshalIPID, private // // Synopsis: finds or creates an interface stub and IPID entry // for the given object interface. // // Arguments: [riid] - interface to look for // [cRefs] - count of references wanted // [mshlflags] - marshal flags // [ppEntry] - place to return IPIDEntry ptr // // Returns: S_OK if succeeded // // History: 20-Feb-95 Rickhi Created // //-------------------------------------------------------------------- HRESULT CStdMarshal::MarshalIPID(REFIID riid, ULONG cRefs, DWORD mshlflags, IPIDEntry **ppIPIDEntry) { TRACECALL(TRACE_MARSHAL, "CStdMarshal::MarshalIPID"); ComDebOut((DEB_MARSHAL, "CStdMarshal::MarshalIPID this:%x riid:%I cRefs:%x mshlflags:%x ppEntry:%x\n", this, &riid, cRefs, mshlflags, ppIPIDEntry)); AssertValid(); AssertDisconnectPrevented(); ASSERT_LOCK_HELD // validate input parms. Win4Assert(!(IsEqualIID(riid, IID_NULL) || IsEqualIID(riid, IID_IMarshal))); // look for an existing IPIDEntry for the requested interface IPIDEntry *pEntry; HRESULT hr = FindIPIDEntry(riid, &pEntry); if (FAILED(hr)) { // no entry currently exists. on the server side we try to create one. // on the client side we do a remote QI for the requested interface. if (ServerSide()) { // this call fail if we are disconnected during a yield. hr = MakeSrvIPIDEntry(riid, &pEntry); } else { hr = RemQIAndUnmarshal(1, (GUID *)&riid, NULL); if (SUCCEEDED(hr)) { hr = FindIPIDEntry(riid, &pEntry); } } } if (SUCCEEDED(hr)) { // REFCOUNTING: if (ServerSide()) { // remember the latest marshal time so we can tell if the ping // server has run us down too early. This can happen when an // existing client dies and we remarshal the interface just // moments before the pingserver tells us the first guy is gone // and before the new client has had time to unmarshal and ping. _dwMarshalTime = GetCurrentTime(); // inc the refcnt for the IPIDEntry and optionaly the stdid. Note // that for TABLE marshals cRefs is 0 (that's the number that gets // placed in the packet) but we do want a reference so we ask for // 1 here. ReleaseMarshalData will undo the 1. ULONG cRefs2 = (mshlflags & MSHLFLAGS_TABLE) ? 1 : cRefs; IncSrvIPIDCnt(pEntry, cRefs2, 0, NULL, mshlflags); } else // client side, { // we dont support marshaling weak refs on the client side, though // we do support marshaling strong from a weak client by going to // the server and getting a strong reference. Win4Assert(!(mshlflags & MSHLFLAGS_WEAK)); if (cRefs >= pEntry->cStrongRefs) { // need more references than we own, go get more from server // to satisfy the marshal. Get a few extra refs for ourselves // unless we are a weak client. ULONG cExtraRefs = (_dwFlags & SMFLAGS_WEAKCLIENT) ? 0 : REM_ADDREF_CNT; hr = RemoteAddRef(pEntry, pEntry->pOXIDEntry, cRefs + cExtraRefs, 0); if (SUCCEEDED(hr)) { // add in the extra references we asked for (if any). pEntry->cStrongRefs += cExtraRefs; } } else { // we have enough references to satisfy this request (and still // keep some for ourselves), just subtract from the IPIDEntry pEntry->cStrongRefs -= cRefs; } // mark this object as having been client-side marshaled so // that we can tell the resolver whether or not it needs to // ping this object if we release it before the OID is registered. _dwFlags |= SMFLAGS_CLIENTMARSHALED; } // do some debug stuff ValidateIPIDEntry(pEntry); ComDebOut((DEB_MARSHAL, "pEntry:%x cRefs:%x cStdId:%x\n", pEntry, pEntry->cStrongRefs, _pStdId->GetRC())); } *ppIPIDEntry = pEntry; ASSERT_LOCK_HELD AssertDisconnectPrevented(); ComDebOut((DEB_MARSHAL, "CStdMarshal::MarshalIPID hr:%x pIPIDEntry\n", hr, *ppIPIDEntry)); return hr; } //+------------------------------------------------------------------- // // Member: CStdMarshal::UnmarshalInterface, public // // Synopsis: Unmarshals an Interface from a stream. // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- STDMETHODIMP CStdMarshal::UnmarshalInterface(LPSTREAM pStm, REFIID riid, VOID **ppv) { ComDebOut((DEB_MARSHAL, "CStdMarshal::UnmarsalInterface this:%x pStm:%x riid:%I\n", this, pStm, &riid)); AssertValid(); ASSERT_LOCK_RELEASED // read the objref from the stream and find or create an instance // of CStdMarshal for its OID. Then ask that guy to do the rest of // the unmarshal (create the interface proxy) OBJREF objref; HRESULT hr = ReadObjRef(pStm, objref); if (SUCCEEDED(hr)) { // pass objref to subroutine to unmarshal the objref hr = ::UnmarshalObjRef(objref, ppv); // release the objref we read FreeObjRef(objref); } ASSERT_LOCK_RELEASED ComDebOut((DEB_MARSHAL, "UnmarsalInterface this:%x pv:%x hr:\n", this, *ppv, hr)); return hr; } //+------------------------------------------------------------------- // // Function: UnmarshalObjRef, private // // Synopsis: UnMarshals interface from objref. // // Arguements: [objref] - object reference // [ppv] - proxy // // Algorithm: Get the correct standard identity and ask it to do // all the work. // // History: 25-Mar-95 AlexMit Created // //-------------------------------------------------------------------- INTERNAL UnmarshalObjRef(OBJREF &objref, void **ppv) { ASSERT_LOCK_RELEASED CStdMarshal *pStdMshl; HRESULT hr = FindStdMarshal(objref, &pStdMshl); if (SUCCEEDED(hr)) { // pass objref to subroutine to unmarshal the objref hr = pStdMshl->UnmarshalObjRef(objref, ppv); CALLHOOKOBJECTCREATE(S_OK,ORHDL(objref).clsid,objref.iid,(IUnknown **)ppv); pStdMshl->Release(); } else { // we could not create the indentity or handler, release the // marshaled objref. ReleaseMarshalObjRef(objref); } ASSERT_LOCK_RELEASED return hr; } //+------------------------------------------------------------------- // // Function: ChkIfLocalOID, private // // Synopsis: Helper function for UnmarshalInternalObjRef & FindStdMarshal // // Arguements: [objref] - object reference // [ppStdMshl] - CStdMarshal returned // // Algorithm: Read the objref, get the OID. If we already have an identity // for this OID return it AddRefd. // // History: 21-May-95 MurthyS Created. // //-------------------------------------------------------------------- INTERNAL_(BOOL) ChkIfLocalOID(OBJREF &objref, CStdIdentity **ppStdId) { STDOBJREF *pStd = &ORSTD(objref).std; BOOL flocal = FALSE; ComDebOut((DEB_MARSHAL, "ChkIfLocalOID (IN) poid: %x\n", &pStd->oid)); Win4Assert((*ppStdId == NULL) && "ChkIfLocalOID: pStdId != NULL"); ASSERT_LOCK_RELEASED LOCK OXIDEntry *pOXIDEntry = GetOXIDFromObjRef(objref); if (pOXIDEntry == GetLocalOXIDEntry()) { flocal = TRUE; // OXID is for this apartment, look IPID up in the IPIDTable // directly, and extract the CStdMarshal from it. IPIDEntry *pEntry = gIPIDTbl.LookupIPID(pStd->ipid); if (pEntry && pEntry->pChnl) { // get the Identity *ppStdId = pEntry->pChnl->GetStdId(); (*ppStdId)->AddRef(); } } UNLOCK ASSERT_LOCK_RELEASED return flocal; } //+------------------------------------------------------------------- // // Function: UnmarshalInternalObjRef, private // // Synopsis: UnMarshals an internally-used interface from objref. // // Arguements: [objref] - object reference // [ppv] - proxy // // Algorithm: Create a StdId and ask it to do the work. // // Notes: This differs from UnmarshalObjRef in that it does not lookup // or register the OID. This saves a fair amount of work and // avoids initializing the OID table. // // History: 25-Oct-95 Rickhi Created // //-------------------------------------------------------------------- INTERNAL UnmarshalInternalObjRef(OBJREF &objref, void **ppv) { ASSERT_LOCK_RELEASED HRESULT hr = S_OK; CStdIdentity *pStdId = NULL; if (ChkIfLocalOID(objref, &pStdId)) { if (pStdId) { // set OID in objref to match that in returned std identity OIDFromMOID(pStdId->GetOID(), &ORSTD(objref).std.oid); } else { hr = CO_E_OBJNOTCONNECTED; } } else { ASSERT_LOCK_RELEASED hr = CreateIdentityHandler(NULL, ORSTD(objref).std.flags, IID_IStdIdentity, (void **)&pStdId); } if (SUCCEEDED(hr)) { // pass objref to subroutine to unmarshal the objref. tell StdId not // to register the OID in the OID table. pStdId->IgnoreOID(); hr = pStdId->UnmarshalObjRef(objref, ppv); CALLHOOKOBJECTCREATE(S_OK,ORHDL(objref).clsid,objref.iid,(IUnknown **)ppv); pStdId->Release(); } ASSERT_LOCK_RELEASED return hr; } //+------------------------------------------------------------------- // // Member: CStdMarshal::UnmarshalObjRef, private // // Synopsis: unmarshals the objref. Called by CoUnmarshalInterface, // UnmarshalObjRef APIs, and UnmarshalInterface method. // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- HRESULT CStdMarshal::UnmarshalObjRef(OBJREF &objref, void **ppv) { ComDebOut((DEB_MARSHAL, "CStdMarshal::UnmarsalObjRef this:%x objref:%x riid:%I\n", this, &objref, &objref.iid)); AssertValid(); STDOBJREF *pStd = &ORSTD(objref).std; OXIDEntry *pOXIDEntry = GetOXIDFromObjRef(objref); DbgDumpSTD(pStd); ASSERT_LOCK_RELEASED LOCK // Prevent a disconnect from occuring while unmarshaling the // interface since we may have to yield the ORPC lock. HRESULT hr = PreventPendingDisconnect(); if (SUCCEEDED(hr)) { if (objref.flags & OBJREF_HANDLER) { // handler form, extract the handler clsid and set our flags _dwFlags |= SMFLAGS_HANDLER; _clsidHandler = ORHDL(objref).clsid; } // if no OID registered yet, do that now. only possible on client side // during reconnect. MOID moid; MOIDFromOIDAndMID(pStd->oid, pOXIDEntry->pMIDEntry->mid, &moid); hr = _pStdId->SetOID(moid); if (SUCCEEDED(hr)) { // find or create the IPID entry for the interface. On the client // side this may cause the creation of an interface proxy. It will // also manipulate the reference counts. hr = UnmarshalIPID(objref.iid, pStd, pOXIDEntry, ppv); } } UNLOCK ASSERT_LOCK_RELEASED if (ClientSide()) { if (SUCCEEDED(hr)) { if (_pStdId->IsAggregated()) { // we are currently holding a proxy pointer. If aggregated, // the controlling unknown may want to override this pointer // with his own version, so issue a QI to give it that chance. IUnknown *pUnk = (IUnknown *)*ppv; #ifdef WX86OLE if (gcwx86.IsN2XProxy(pUnk)) { // Tell wx86 thunk layer to thunk as IUnknown gcwx86.SetStubInvokeFlag((BOOL)1); } #endif hr = pUnk->QueryInterface(objref.iid, ppv); pUnk->Release(); } } else { // cleanup our state on failure (only meaningful on client side, // since if the unmarshal failed on the server side, the interface // is already cleaned up). ReleaseMarshalObjRef(objref); } } // now let pending disconnect through. on server-side, ignore any // error from HPD and pay attention only to the unmarshal result, since // a successful unmarshal on the server side may result in a disconnect // if that was the last external reference to the object. HRESULT hr2 = HandlePendingDisconnect(hr); if (FAILED(hr2) && ClientSide()) { if (SUCCEEDED(hr)) { // a disconnect came in while unmarshaling. ppv contains an // AddRef'd interface pointer so go Release that now. ((IUnknown *)*ppv)->Release(); } hr = hr2; } ComDebOut((DEB_MARSHAL, "CStdMarshal::UnmarsalObjRef this:%x hr:%x\n", this, hr)); return hr; } //+------------------------------------------------------------------- // // Member: CStdMarshal::UnmarshalIPID, private // // Synopsis: finds or creates an interface proxy for the given // interface. may also do a remote query interface. // // Arguements: [riid] - the interface to return // [std] - standard objref to unmarshal from // [pOXIDEntry] - ptr to OXIDEntry of the server // [ppv] - interface ptr of type riid returned, AddRef'd // // Returns: S_OK if succeeded // // History: 20-Feb-95 Rickhi Created // //-------------------------------------------------------------------- HRESULT CStdMarshal::UnmarshalIPID(REFIID riid, STDOBJREF *pStd, OXIDEntry *pOXIDEntry, void **ppv) { TRACECALL(TRACE_MARSHAL, "CStdMarshal::UnmarshalIPID"); ComDebOut((DEB_MARSHAL, "CStdMarshal::UnmarshalIPID this:%x riid:%I pStd:%x pOXIDEntry:%x\n", this, &riid, pStd, pOXIDEntry)); DbgDumpSTD(pStd); AssertValid(); AssertDisconnectPrevented(); ASSERT_LOCK_HELD // validate input params. Win4Assert(!(IsEqualIID(riid, IID_NULL) || IsEqualIID(riid, IID_IMarshal))); Win4Assert(pStd != NULL); ValidateSTD(pStd); Win4Assert(pOXIDEntry); // look for an existing IPIDEntry for the requested interface. IPIDEntry *pEntry; HRESULT hr = FindIPIDEntry(riid, &pEntry); #ifdef WX86OLE BOOL fSameApt = SUCCEEDED(hr); PVOID pvPSThunk = NULL; #endif // REFCOUNTING: if (ClientSide()) { if (FAILED(hr)) { // no IPID Entry exists yet for the requested interface. We do // have a STDOBJREF. Create the interface proxy and IPIDEntry // now, and connect it up. If successful, the proxy will be // fully connected upon return, with pEntry->cStrongRefs set // to pStd->cPublicRefs. if (ppv) *ppv = NULL; hr = MakeCliIPIDEntry(riid, pStd, pOXIDEntry, &pEntry); } else if (pEntry->dwFlags & IPIDF_DISCONNECTED) { // reconnect the IPID entry to the server. this will set // pEntry->cStrongRefs to pStd->cPublicRefs. Even though we could // yield, the IPIDEntry is guarenteed connected on return // (cause we are holding the lock on return). hr = ConnectIPIDEntry(pStd, pOXIDEntry, pEntry); } else if ((pStd->flags & SORF_WEAKREF) && (pEntry->pOXIDEntry->dwFlags & OXIDF_MACHINE_LOCAL)) { // add the refcnt to our weak total for this IPIDEntry pEntry->cWeakRefs += pStd->cPublicRefs; } else { // add the refcnt to our strong total for this IPIDEntry pEntry->cStrongRefs += pStd->cPublicRefs; } } else if (SUCCEEDED(hr)) { // unmarshaling in the server apartment. If the cRefs is zero, // then the interface was TABLE marshalled and we dont do // anything to the IPID RefCnts since the object must live until // ReleaseMarshalData is called on it. #ifdef WX86OLE pvPSThunk = gcwx86.UnmarshalledInSameApt(pEntry->pv, riid); #endif if (pStd->cPublicRefs > 0) { // normal case, dec the ref counts from the IPID entry, // OLE always passed fLastReleaseCloses = FALSE on // Unmarshal and RMD so do the same here. DWORD mshlflags = (pStd->flags & SORF_WEAKREF) ? (MSHLFLAGS_WEAK | MSHLFLAGS_KEEPALIVE) : (MSHLFLAGS_NORMAL | MSHLFLAGS_KEEPALIVE); DecSrvIPIDCnt(pEntry, pStd->cPublicRefs, 0, NULL, mshlflags); } } if (SUCCEEDED(hr) && ppv) { ValidateIPIDEntry(pEntry); // extract and AddRef the pointer to return to the caller. // Do this before releasing the lock (which we might do below // on the server-side in DecSrvIPIDCnt. // NOTE: we are calling App code while holding the lock, // but there is no way to avoid this. Win4Assert(IsValidInterface(pEntry->pv)); *ppv = pEntry->pv; ((IUnknown *)*ppv)->AddRef(); AssertOutPtrIface(hr, *ppv); if (_dwFlags & SMFLAGS_WEAKCLIENT && !(pStd->flags & SORF_WEAKREF)) { // make the client interface weak, ignore errors. UNLOCK ASSERT_LOCK_RELEASED RemoteChangeRef(0,0); ASSERT_LOCK_RELEASED LOCK } #ifdef WX86OLE // If we unmarshalled in the same apartment as the object and Wx86 // recognized the interface then change the returned proxy to the // proxy created for the Wx86 PSThunk. if (pvPSThunk == (PVOID)-1) { // Wx86 recognized the interface, but could not establish a // PSThunk for it. Force an error return. *ppv = NULL; hr = E_NOINTERFACE; } else if (pvPSThunk != NULL) { // Wx86 recognized the interface and did establish a PSThunk // for it. Force a successful return with Wx86 proxy interface. *ppv = pvPSThunk; } #endif } ComDebOut((DEB_MARSHAL, "pEntry:%x cRefs:%x cStdId:%x\n", pEntry, (SUCCEEDED(hr)) ? pEntry->cStrongRefs : 0, _pStdId->GetRC())); ASSERT_LOCK_HELD AssertDisconnectPrevented(); ComDebOut((DEB_MARSHAL, "CStdMarshal::UnmarshalIPID hr:%x\n", hr)); return hr; } //+------------------------------------------------------------------- // // Member: CStdMarshal::PrivateCopyProxy, internal // // Synopsis: Creates a copy of a proxy and IPID entry. // // Arguements: [pProxy] - Proxy to copy // [ppProxy] - return copy here. // //-------------------------------------------------------------------- HRESULT CStdMarshal::PrivateCopyProxy( IUnknown *pv, IUnknown **ppv ) { TRACECALL(TRACE_MARSHAL, "CStdMarshal::PrivateCopyProxy"); ComDebOut((DEB_MARSHAL, "CStdMarshal::PrivateCopyProxy this:%x pv:%x\n", this, pv)); // Don't copy stubs. if (ServerSide()) return E_INVALIDARG; ASSERT_LOCK_RELEASED LOCK // Prevent a disconnect from occuring while unmarshaling the // interface since we may have to yield the ORPC lock. HRESULT hr = PreventPendingDisconnect(); if (SUCCEEDED(hr)) { // Find the proxy to copy. IPIDEntry *pEntry; hr = FindIPIDEntryByInterface(pv, &pEntry); if (SUCCEEDED(hr)) { // Don't copy disconnected proxies. if (pEntry->dwFlags & IPIDF_DISCONNECTED) hr = RPC_E_DISCONNECTED; // IUnknown can't be copied. else if (IsEqualGUID( pEntry->iid, IID_IUnknown )) hr = E_INVALIDARG; else { BOOL fNonNDRProxy; IRpcProxyBuffer *pProxy; hr = CreateProxy(pEntry->iid, &pProxy, (void **)ppv, &fNonNDRProxy); if (SUCCEEDED(hr)) { IPIDEntry *pIpidCopy; // add a disconnected IPID entry to the table. hr = AddIPIDEntry(NULL, &pEntry->ipid, pEntry->iid, NULL, pProxy, *ppv, &pIpidCopy); if (SUCCEEDED(hr)) { // mark this IPID as a copy so we dont free it during // ReleaseIPIDs. pIpidCopy->dwFlags |= IPIDF_COPY; // connect the IPIDEntry before adding it to the table so // that we dont have to worry about races between Unmarshal, // Disconnect, and ReconnectProxies. // Make up an objref. Mark it as NOPING since we dont // really have any references and we dont really need // any because if we ever try to marshal it we will // find the original IPIDEntry and use that. NOPING // also lets us skip this IPID in DisconnectCliIPIDs. STDOBJREF std; OXIDFromMOXID(pEntry->pOXIDEntry->moxid, &std.oxid); std.ipid = pEntry->ipid; std.cPublicRefs = 1; std.flags = SORF_NOPING; hr = ConnectIPIDEntry(&std, pEntry->pOXIDEntry, pIpidCopy); // Add this IPID entry after the original. pIpidCopy->pNextOID = pEntry->pNextOID; pEntry->pNextOID = pIpidCopy; _cIPIDs++; } else { // could not get an IPIDEntry, release the proxy, need to // release the lock to do this. UNLOCK ASSERT_LOCK_RELEASED pProxy->Release(); ((IUnknown *)*ppv)->Release(); ASSERT_LOCK_RELEASED LOCK } } } } if (SUCCEEDED(hr)) { ValidateIPIDEntry(pEntry); AssertOutPtrIface(hr, *ppv); } AssertDisconnectPrevented(); } ASSERT_LOCK_HELD UNLOCK ASSERT_LOCK_RELEASED // Now let pending disconnect through. HRESULT hr2 = HandlePendingDisconnect(hr); if (FAILED(hr2) && SUCCEEDED(hr)) { // a disconnect came in while creating the proxy. ppv contains // an AddRef'd interface pointer so go Release that now. ((IUnknown *)*ppv)->Release(); } ComDebOut((DEB_MARSHAL, "CStdMarshal::PrivateCopyProxy hr:%x\n", hr2)); return hr2; } //+------------------------------------------------------------------- // // Member: MakeSrvIPIDEntry, private // // Synopsis: creates a server side IPID table entry // // Arguements: [riid] - the interface to return // [ppEntry] - IPIDEntry returned // // History: 20-Feb-95 Rickhi Created // //-------------------------------------------------------------------- HRESULT CStdMarshal::MakeSrvIPIDEntry(REFIID riid, IPIDEntry **ppEntry) { Win4Assert(ServerSide()); AssertValid(); AssertDisconnectPrevented(); ASSERT_LOCK_HELD BOOL fNonNDRStub; void *pv; IRpcStubBuffer *pStub; HRESULT hr = CreateStub(riid, &pStub, &pv, &fNonNDRStub); if (SUCCEEDED(hr)) { OXIDEntry *pOXIDEntry = _pChnl->GetOXIDEntry(); IPID ipidDummy; hr = AddIPIDEntry(pOXIDEntry, &ipidDummy, riid, _pChnl, pStub, pv, ppEntry); if (SUCCEEDED(hr)) { if (_dwFlags & SMFLAGS_NOPING) { // object does no need pinging, turn on NOPING (*ppEntry)->dwFlags |= IPIDF_NOPING; } if (fNonNDRStub) { // the stub was a custom 16bit one requested by WOW, mark the // IPIDEntry as holding a non-NDR stub so we know to set the // SORF_NONNDR flag in the StdObjRef when marshaling. This // tells local clients whether to create a MIDL generated // proxy or custom proxy. Functionality to support OLE // Automation on DCOM. (*ppEntry)->dwFlags |= IPIDF_NONNDRSTUB; } // increment the OXIDEntry ref count so that it stays // around as long as the IPIDEntry points to it. It gets // decremented when we disconnect the IPIDEntry. IncOXIDRefCnt(pOXIDEntry); // chain the IPIDEntries for this OID together (*ppEntry)->pNextOID = _pFirstIPID; _pFirstIPID = *ppEntry; } else { // release the stub. we need to release the lock to do this. UNLOCK ASSERT_LOCK_RELEASED pStub->Release(); ASSERT_LOCK_RELEASED LOCK } } ASSERT_LOCK_HELD AssertDisconnectPrevented(); return hr; } //+------------------------------------------------------------------- // // Member: MakeCliIPIDEntry, private // // Synopsis: creates a client side IPID table entry // // Arguements: [riid] - the interface to return // [pStd] - standard objref // [pOXIDEntry] - OXIDEntry of the server // [ppEntry] - IPIDEntry returned // // History: 20-Feb-95 Rickhi Created // //-------------------------------------------------------------------- HRESULT CStdMarshal::MakeCliIPIDEntry(REFIID riid, STDOBJREF *pStd, OXIDEntry *pOXIDEntry, IPIDEntry **ppEntry) { Win4Assert(ClientSide()); AssertValid(); AssertDisconnectPrevented(); Win4Assert(pOXIDEntry); ASSERT_LOCK_HELD BOOL fNonNDRProxy; void *pv; IRpcProxyBuffer *pProxy; HRESULT hr = CreateProxy(riid, &pProxy, &pv, &fNonNDRProxy); if (SUCCEEDED(hr)) { // add a disconnected IPID entry to the table. hr = AddIPIDEntry(NULL, &pStd->ipid, riid, NULL, pProxy, pv, ppEntry); if (pv) { // throw away our reference here, we will get it back later // in UnmarshalIPID ((IUnknown *)pv)->Release(); } if (SUCCEEDED(hr)) { if (fNonNDRProxy) { // the proxy is a custom 16bit one requested by WOW, mark the // IPIDEntry as holding a non-NDR proxy so we know to set the // LOCALF_NOTNDR flag in the local header when we call on it // (see CRpcChannelBuffer::ClientGetBuffer). Functionality to // support OLE Automation on DCOM. (*ppEntry)->dwFlags |= IPIDF_NONNDRPROXY; } if (pStd->flags & SORF_NONNDR) { // need to remember this flag so we can tell other // unmarshalers if we remarshal it. (*ppEntry)->dwFlags |= IPIDF_NONNDRSTUB; } // connect the IPIDEntry before adding it to the table so // that we dont have to worry about races between Unmarshal, // Disconnect, and ReconnectProxies. hr = ConnectIPIDEntry(pStd, pOXIDEntry, *ppEntry); // chain the IPIDEntries for this OID together. On client side // always add the entry to the list regardless of whether connect // succeeded. (*ppEntry)->pNextOID = _pFirstIPID; _pFirstIPID = *ppEntry; _cIPIDs++; } else { // could not get an IPIDEntry, release the proxy, need to // release the lock to do this. UNLOCK ASSERT_LOCK_RELEASED pProxy->Release(); ASSERT_LOCK_RELEASED LOCK } } ASSERT_LOCK_HELD AssertDisconnectPrevented(); return hr; } //+------------------------------------------------------------------- // // Member: ConnectIPIDEntry, private // // Synopsis: connects a client side IPID table entry to the server // // Arguments: [pStd] - standard objref // [pOXIDEntry] - OXIDEntry for the server // [pEntry] - IPIDEntry to connect, already has a proxy // and the IID filled in. // // Notes: This routine is re-entrant, it may be called multiple // times for the same IPIDEntry, with part of the work done // in one call and part in another. Only if the entry is // fully set up will it return S_OK and mark the entry as // connected. DisconnectCliIPIDs handles cleanup of partial // connections. // // History: 20-Feb-95 Rickhi Created // //-------------------------------------------------------------------- HRESULT CStdMarshal::ConnectIPIDEntry(STDOBJREF *pStd, OXIDEntry *pOXIDEntry, IPIDEntry *pEntry) { ComDebOut((DEB_MARSHAL, "CStdMarshal::ConnectIPIDEntry this:%x ipid:%I pOXIDEntry:%x pIPIDEntry:%x\n", this, &pStd->ipid, pOXIDEntry, pEntry)); Win4Assert(ClientSide()); AssertDisconnectPrevented(); AssertValid(); Win4Assert(pOXIDEntry); ASSERT_LOCK_HELD HRESULT hr = S_OK; // mark the object as having attempted to connect an IPIDEntry so that // if we fail somewhere in this routine and dont mark the whole object // as connected, Disconnect will still try to clean things up. _dwFlags |= SMFLAGS_TRIEDTOCONNECT; if (!(pStd->flags & SORF_NOPING)) { // this interface requires pinging, turn off NOPING for this object // and this IPIDEntry. _dwFlags &= ~SMFLAGS_NOPING; pEntry->dwFlags &= ~IPIDF_NOPING; } if (!(_dwFlags & (SMFLAGS_REGISTEREDOID | SMFLAGS_NOPING))) { // register the OID with the ping server so it will get pinged hr = gResolver.ClientRegisterOIDWithPingServer(pStd->oid, pOXIDEntry); if (FAILED(hr)) { return hr; } _dwFlags |= SMFLAGS_REGISTEREDOID; } // Go get any references we need that are not already included in the // STDOBJREF. These references will have been added to the counts in // the IPIDEntry upon return. Any references in the STDOBJREF will be // added to the IPIDEntry count only if the connect succeeds, otherwise // ReleaseMarshalObjRef (which will clean up STDOBJREF references) will // get called by higher level code. hr = GetNeededRefs(pStd, pOXIDEntry, pEntry); if (FAILED(hr)) { return hr; } if (pEntry->pChnl == NULL) { // create a channel for this oxid/ipid pair. On the client side we // create one channel per proxy (and hence per IPID). hr = CreateChannel(pOXIDEntry, pStd->flags, pStd->ipid, pEntry->iid, &pEntry->pChnl); if (SUCCEEDED(hr)) { // update this IPID table entry. must update ipid too since // on reconnect it differs from the old value. IncOXIDRefCnt(pOXIDEntry); pEntry->pOXIDEntry = pOXIDEntry; pEntry->ipid = pStd->ipid; pEntry->pChnl->SetIPIDEntry(pEntry); } } if (SUCCEEDED(hr)) { // Release the lock while we connect the proxy. We have to do // this because the IDispatch proxy makes an Rpc call during // Connect (Yuk!), which causes the channel to assert that the // lock is released. The proxy MUST be able to handle multiple // simultaneous or nested connects to the same channel ptr, since // it is possible when we yield the lock for another thread to // come in here and try a connect. void *pv = NULL; IRpcProxyBuffer * pProxy = (IRpcProxyBuffer *)(pEntry->pStub); if (pProxy) { // HACKALERT: OleAutomation returns NULL pv in CreateProxy // in cases where they dont know whether to return an NDR // proxy or a custom-format proxy. So we have to go connect // the proxy first then Query for the real interface once that // is done. BOOL fGetpv = (pEntry->pv) ? FALSE : TRUE; UNLOCK ASSERT_LOCK_RELEASED hr = pProxy->Connect(pEntry->pChnl); if (fGetpv && SUCCEEDED(hr)) { #ifdef WX86OLE if (gcwx86.IsN2XProxy(pProxy)) { // If we are creating a proxy for an object that is // living on the x86 side then we need to set the // StubInvoke flag to allow QI to thunk the // custom interface QI. gcwx86.SetStubInvokeFlag((BOOL)2); } #endif hr = pProxy->QueryInterface(pEntry->iid, &pv); AssertOutPtrIface(hr, pv); if(SUCCEEDED(hr)) { #ifdef WX86OLE // Call whole32 thunk layer to play with the ref count // and aggregate the proxy to the controlling unknown. gcwx86.AggregateProxy(_pStdId->GetCtrlUnk(), (IUnknown *)pv); #endif // Release our reference here. // We keep a weak reference to pv. ((IUnknown *)pv)->Release(); } } ASSERT_LOCK_RELEASED LOCK } // Regardless of errors from Connect and QI we wont try to cleanup // any of the work we have done so far in this routine. The routine // is reentrant (by the same thread or by different threads) and // those calls could be using some of resources we have already // allocated. Instead, we rely on DisconnectCliIPIDs to cleanup // the partial allocation of resources. if (pEntry->dwFlags & IPIDF_DISCONNECTED) { // Mark the IPIDEntry as connected so we dont try to connect // again. Also, as long as there is one IPID connected, the // whole object is considered connected. This allows disconnect // to find the newly connected IPID and disconnect it later. // Infact, DisconnectCliIPIDs relies on there being at least // one IPID with a non-NULL OXIDEntry. It is safe to set this // now because Disconnects have been temporarily turned off. if (SUCCEEDED(hr)) { if (pv) { // assign the interface pointer pEntry->pv = pv; } AssertDisconnectPrevented(); pEntry->dwFlags &= ~IPIDF_DISCONNECTED; _dwFlags &= ~SMFLAGS_DISCONNECTED; } } else { // while the lock was released, the IPIDEntry got connected // by another thread (or by a nested call on this thread). // Ignore any errors from Connect or QI since apparently // things are connected now. hr = S_OK; } if (SUCCEEDED(hr)) { // Add in any references we were given. If we were given 0 refs // and the interface is noping, then pretend like we got 1 ref. ULONG cRefs = ((pStd->cPublicRefs == 0) && (pStd->flags & SORF_NOPING)) ? 1 : pStd->cPublicRefs; // figure out if we have weak or strong references. To be weak // they must be local to this machine and the SORF flag set. BOOL fWeak = ((pStd->flags & SORF_WEAKREF) && (pOXIDEntry->dwFlags & OXIDF_MACHINE_LOCAL)); if (fWeak) pEntry->cWeakRefs += cRefs; else pEntry->cStrongRefs += cRefs; } // in debug build, ensure that we did not screw up ValidateIPIDEntry(pEntry); } ASSERT_LOCK_HELD AssertDisconnectPrevented(); ComDebOut((DEB_MARSHAL, "CStdMarshal::ConnectIPIDEntry this:%x pOXIDEntry:%x pChnl:%x hr:%x\n", this, pEntry->pOXIDEntry, pEntry->pChnl, hr)); return hr; } //+------------------------------------------------------------------- // // Member: GetNeededRefs, private // // Synopsis: Figures out if any references are needed and goes and gets // them from the server. // // Arguments: [pStd] - standard objref // [pOXIDEntry] - OXIDEntry for the server // [pEntry] - IPIDEntry to connect, already has a proxy // and the IID filled in. // // History: 20-Feb-95 Rickhi Created // //-------------------------------------------------------------------- HRESULT CStdMarshal::GetNeededRefs(STDOBJREF *pStd, OXIDEntry *pOXIDEntry, IPIDEntry *pEntry) { HRESULT hr = S_OK; if ((pStd->flags & (SORF_NOPING | SORF_WEAKREF)) == 0) { // if we dont have any and weren't given any strong refs, go get some. ULONG cNeedStrong = ((pEntry->cStrongRefs + pStd->cPublicRefs) == 0) ? REM_ADDREF_CNT : 0; // if we are using secure refs and we dont have any, go get some. ULONG cNeedSecure = ((gCapabilities & EOAC_SECURE_REFS) && (pEntry->cPrivateRefs == 0)) ? 1 : 0; if (cNeedStrong || cNeedSecure) { // Need to go get some references from the remote server. Note // that we will yield here but we dont have to worry about it because // the IPIDEntry is still marked as disconnected. hr = RemoteAddRef(pEntry, pOXIDEntry, cNeedStrong, cNeedSecure); if (SUCCEEDED(hr)) { pEntry->cStrongRefs += cNeedStrong; pEntry->cPrivateRefs += cNeedSecure; } } } return hr; } //+------------------------------------------------------------------- // // Member: CStdMarshal::ReconnectProxies // // Synopsis: Reconnects the proxies to a new server (functionality // used by the OLE default handler). // // History: 20-Feb-95 Rickhi Created. // // CODEWORK: CreateServer should just ask for all these interfaces // during the create. // // BUGBUG: fail this call if freethreaded // //-------------------------------------------------------------------- void CStdMarshal::ReconnectProxies() { ComDebOut((DEB_MARSHAL,"CStdMarshal::ReconnectProxies this:%x pFirst:%x\n", this, _pFirstIPID)); AssertValid(); Win4Assert(ClientSide()); ASSERT_LOCK_RELEASED LOCK // must be at least 1 proxy already connected in order to be able // to reconnect the other proxies. We cant just ASSERT that's true // because we were not holding the lock on entry. HRESULT hr = PreventDisconnect(); if (SUCCEEDED(hr)) { // allocate a stack buffer to hold the IPIDs IID *pIIDsAlloc = (IID *) _alloca(_cIPIDs * sizeof(IID)); IID *pIIDs = pIIDsAlloc; USHORT cIIDs = 0; IPIDEntry *pNextIPID = _pFirstIPID; while (pNextIPID) { // Don't allow reconnection for fancy new servers or with // secure proxies. if (pNextIPID->dwFlags & IPIDF_COPY) { hr = E_FAIL; break; } if ((pNextIPID->dwFlags & IPIDF_DISCONNECTED)) { // not connected, add it to the list to be connected. *pIIDs = pNextIPID->iid; pIIDs++; cIIDs++; } pNextIPID = pNextIPID->pNextOID; } if (cIIDs != 0 && SUCCEEDED(hr)) { // we have looped filling in the IID list, and there are // entries int he list. go call QI on server now and // unmarshal the results. hr = RemQIAndUnmarshal(cIIDs, pIIDsAlloc, NULL); } } DbgWalkIPIDs(); UNLOCK ASSERT_LOCK_RELEASED // this will handle any Disconnect that came in while we were busy. hr = HandlePendingDisconnect(hr); ComDebOut((DEB_MARSHAL,"CStdMarshal::ReconnectProxies [OUT] this:%x\n", this)); return; } //+------------------------------------------------------------------- // // Member: CStdMarshal::ReleaseMarshalData, public // // Synopsis: Releases the references added by MarshalInterface // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- STDMETHODIMP CStdMarshal::ReleaseMarshalData(LPSTREAM pStm) { ComDebOut((DEB_MARSHAL, "CStdMarshal::ReleaseMarshalData this:%x pStm:%x\n", this, pStm)); AssertValid(); ASSERT_LOCK_RELEASED OBJREF objref; HRESULT hr = ReadObjRef(pStm, objref); if (SUCCEEDED(hr)) { // call worker API to do the rest of the work hr = ::ReleaseMarshalObjRef(objref); // deallocate the objref we read FreeObjRef(objref); } ASSERT_LOCK_RELEASED ComDebOut((DEB_MARSHAL, "CStdMarshal::ReleaseMarshalData this:%x hr:%x\n", this, hr)); return hr; } //+------------------------------------------------------------------- // // Function: ReleaseMarshalObjRef, private // // Synopsis: Releases the references added by MarshalObjRef // // Arguements: [objref] - object reference // // Algorithm: Get the correct standard identity and ask it to do // a ReleaseMarshalData. // // History: 19-Jun-95 Rickhi Created // //-------------------------------------------------------------------- INTERNAL ReleaseMarshalObjRef(OBJREF &objref) { ComDebOut((DEB_MARSHAL, "ReleaseMarshalObjRef objref:%x\n", &objref)); ASSERT_LOCK_RELEASED HRESULT hr = InitChannelIfNecessary(); if (SUCCEEDED(hr)) { CStdMarshal *pStdMshl; hr = FindStdMarshal(objref, &pStdMshl); if (SUCCEEDED(hr)) { // only do the RMD if on the server side. if (pStdMshl->ServerSide()) { // pass objref to subroutine to Release the marshaled data hr = pStdMshl->ReleaseMarshalObjRef(objref); } pStdMshl->Release(); } else { // we could not find or create an identity. If the server is // outside this apartment, try to issue a remote release on // the interface. if the OXID is local and we could not find // the identity, there is nothing left to cleanup. LOCK OXIDEntry *pOXIDEntry = GetOXIDFromObjRef(objref); if (pOXIDEntry != GetLocalOXIDEntry()) { // make a remote release call RemoteReleaseObjRef(objref); } UNLOCK } } ASSERT_LOCK_RELEASED ComDebOut((DEB_MARSHAL, "ReleaseMarshalObjRef hr:%x\n", hr)); return hr; } //+------------------------------------------------------------------- // // Member: CStdMarshal::ReleaseMarshalObjRef, public // // Synopsis: Releases the references added by MarshalObjRef // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- HRESULT CStdMarshal::ReleaseMarshalObjRef(OBJREF &objref) { ComDebOut((DEB_MARSHAL, "CStdMarshal::ReleaseMarshalObjRef this:%x objref:%x\n", this, &objref)); AssertValid(); HRESULT hr = S_OK; STDOBJREF *pStd = &ORSTD(objref).std; ValidateSTD(pStd); ASSERT_LOCK_RELEASED LOCK // REFCOUNTING: if (ServerSide()) { // look for an existing IPIDEntry for the given IPID IPIDEntry *pEntry; hr = FindIPIDEntryByIPID(pStd->ipid, &pEntry); if (SUCCEEDED(hr) && !(pEntry->dwFlags & IPIDF_DISCONNECTED)) { // subtract the ref count from the IPIDEntry, may Release the // StdId if this was the last reference for this IPIDEntry. // we need to figure out how it was marshalled, strong/weak etc // in order to set the flags and cRefs correctly to pass to // DecSrvIPIDCnt. if (pStd->cPublicRefs == 0) { // table case DWORD mshlflags = (pStd->flags & SORF_TBLWEAK) ? MSHLFLAGS_TABLEWEAK : MSHLFLAGS_TABLESTRONG; DecSrvIPIDCnt(pEntry, 1, 0, NULL, mshlflags); } else { // normal or weak case DWORD mshlflags = (pStd->flags & SORF_WEAKREF) ? MSHLFLAGS_WEAK : MSHLFLAGS_NORMAL; DecSrvIPIDCnt(pEntry, pStd->cPublicRefs, 0, NULL, mshlflags); } } } else // client side { if ((pStd->cPublicRefs == 0) || (pStd->flags & SORF_NOPING)) { // there are no references, or this interface does not // need pinging, so there is nothing to do. ; } else { // look for an existing IPIDEntry for the given IPID IPIDEntry *pEntry; hr = FindIPIDEntryByIPID(pStd->ipid, &pEntry); if (SUCCEEDED(hr) && !(pEntry->dwFlags & IPIDF_DISCONNECTED)) { // add these to the cRefs of this entry, they will get freed // when we do the remote release. Saves an Rpc call now. if ((pStd->flags & SORF_WEAKREF) && (pEntry->pOXIDEntry->dwFlags & OXIDF_MACHINE_LOCAL)) pEntry->cWeakRefs += pStd->cPublicRefs; else pEntry->cStrongRefs += pStd->cPublicRefs; } else { // client side, no matching IPIDEntry so just contact the remote // server to remove the reference. ignore errors since there is // nothing we can do about them anyway. RemoteReleaseObjRef(objref); hr = S_OK; } } } UNLOCK ASSERT_LOCK_RELEASED ComDebOut((DEB_MARSHAL, "CStdMarshal::ReleaseMarshalObjRef this:%x hr:%x\n", this, hr)); return hr; } //+------------------------------------------------------------------- // // Member: CStdMarshal::PreventDisconnect, public // // Synopsis: Prevents a Disconnect from occurring until a matching // HandlePendingDisconnect is called. // // History: 21-Sep-95 Rickhi Created // // The ORPC LOCK is yielded at many places in order to make calls on // application interfaces (server-side objects, stubs, proxies, // handlers, remote objects, resolver, etc). In order to keep the // code (reasonably?) simple, disconnects are prevented from occuring // while in the middle of (potentially) complex operations, and while // there are outstanding calls on interfaces to this object. // // To accomplish this, a counter (_cNestedCalls) is atomically incremented. // When _cNestedCalls != 0 and a Disconnect arrives, the object is flagged // as PendingDisconnect. When HandlePendingDisconnect is called, it // decrements the _cNestedCalls. If the _cNestedCalls == 0 and there is // a pending disconnect, the real Disconnect is done. // //-------------------------------------------------------------------- HRESULT CStdMarshal::PreventDisconnect() { ASSERT_LOCK_HELD // treat this as a nested call so that if we yield, a real // disconnect wont come through, instead it will be treated // as pending. That allows us to avoid checking our state // for Disconnected every time we yield the ORPC LOCK. InterlockedIncrement(&_cNestedCalls); if (_dwFlags & (SMFLAGS_DISCONNECTED | SMFLAGS_PENDINGDISCONNECT)) return CO_E_OBJNOTCONNECTED; return S_OK; } //+------------------------------------------------------------------- // // Member: CStdMarshal::PreventPendingDisconnect, public // // Synopsis: similar to PreventDisconnect but special case for use // in UnmarshalObjRef (since the client side starts out // in the Disconnected state until the first unmarshal is done). // // History: 21-Sep-95 Rickhi Created // //+------------------------------------------------------------------- HRESULT CStdMarshal::PreventPendingDisconnect() { ASSERT_LOCK_HELD InterlockedIncrement(&_cNestedCalls); if (_dwFlags & (ClientSide() ? SMFLAGS_PENDINGDISCONNECT : SMFLAGS_PENDINGDISCONNECT | SMFLAGS_DISCONNECTED)) return CO_E_OBJNOTCONNECTED; return S_OK; } //+------------------------------------------------------------------- // // Member: CStdMarshal::HandlePendingDisconnect, public // // Synopsis: Reverses a call to PreventDisconnect and lets a // pending disconnect through. // // History: 21-Sep-95 Rickhi Created // //+------------------------------------------------------------------- HRESULT CStdMarshal::HandlePendingDisconnect(HRESULT hr) { ASSERT_LOCK_RELEASED // treat this as a nested call so that if we yield, a real // disconnect wont come through, instead it will be treated // as pending. That allows us to avoid checking our state // for Disconnected every time we yield the ORPC LOCK. if (InterlockedDecrement(&_cNestedCalls) == 0 && (_dwFlags & SMFLAGS_PENDINGDISCONNECT)) { Disconnect(); hr = FAILED(hr) ? hr : CO_E_OBJNOTCONNECTED; } return hr; } //+------------------------------------------------------------------- // // Member: CStdMarshal::DisconnectObject, public // // Synopsis: part of IMarshal interface, this is legal only on the // server side. // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- STDMETHODIMP CStdMarshal::DisconnectObject(DWORD dwReserved) { ComDebOut((DEB_MARSHAL, "CStdMarshal::DisconnectObject this:%x dwRes:%x\n", this, dwReserved)); AssertValid(); ASSERT_LOCK_RELEASED // this operation is not legal from the client side (although // IProxyManager::Disconnect is), but we still have to return S_OK // in either case for backward compatibility. if (ServerSide()) { Disconnect(); } ASSERT_LOCK_RELEASED return S_OK; } //+------------------------------------------------------------------- // // Member: CStdMarshal::Disconnect, public // // Synopsis: client side - disconnects proxies from the channel. // server side - disconnects stubs from the server object. // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- void CStdMarshal::Disconnect(void) { ComDebOut((DEB_MARSHAL, "CStdMarshal::Disconnect this:%x\n", this)); AssertValid(); ASSERT_LOCK_RELEASED LOCK if ((_dwFlags & SMFLAGS_DISCONNECTED) && !(_dwFlags & SMFLAGS_TRIEDTOCONNECT)) { // already disconnected, no partial connects, nothing to do ComDebOut((DEB_MARSHAL,"CStdMarshal::Disconnect [already done]:%x\n",this)); UNLOCK ASSERT_LOCK_RELEASED return; } // Revoke ID from the ID table if registered. This prevents other // marshals/unmarshals from finding this identity that is about to // be disconnected. This is the ONLY state that should change, since // we dont want to screw up any work-in-progress on other threads // or in calls higher up the stack. _pStdId->RevokeOID(); if (_cNestedCalls != 0) { // we dont allow disconnect to occur inside a nested call since we // dont want state to vanish in the middle of a call, but we do // remember that we want to disconnect and will do it when the // stack unwinds (or other threads complete). _dwFlags |= SMFLAGS_PENDINGDISCONNECT; ComDebOut((DEB_MARSHAL,"CStdMarshal::Disconnect [pending]:%x\n",this)); UNLOCK; ASSERT_LOCK_RELEASED return; } // No calls in progress and not already disconnected, OK to really // disconnect now. First mark ourself as disconnected incase we // get reentered while releasing a stub pointer. _dwFlags |= SMFLAGS_DISCONNECTED; // turn on disconnected _dwFlags &= ~(SMFLAGS_PENDINGDISCONNECT | // turn off pending disconnect SMFLAGS_TRIEDTOCONNECT); // turn off tried to connect // disconnect all our IPIDs if (ServerSide()) DisconnectSrvIPIDs(); else DisconnectCliIPIDs(); UNLOCK ASSERT_LOCK_RELEASED if (ServerSide()) { // HACK - 16 and 32 bit Word 6.0 crash if you release all the objects // it left lying around at CoUninitialize. Leak them. COleTls tls; // If we are not uninitializing, then call the release. if ((tls->dwFlags & OLETLS_THREADUNINITIALIZING) == 0 || // If we are in WOW and the app is not word, then call the release. (IsWOWThread() && (g_pOleThunkWOW->GetAppCompatibilityFlags() & OACF_NO_UNINIT_CLEANUP) == 0) || // If the app is not 32 bit word, then call the release. !IsTaskName( L"winword.exe" )) { // on the server side, we have to tell the stdid to release his // controlling unknown of the real object. _pStdId->ReleaseCtrlUnk(); } } ASSERT_LOCK_RELEASED ComDebOut((DEB_MARSHAL,"CStdMarshal::Disconnect [complete]:%x\n",this)); } //+------------------------------------------------------------------- // // Member: CStdMarshal::DisconnectCliIPIDs // // Synopsis: disconnects client side IPIDs for this object. // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- void CStdMarshal::DisconnectCliIPIDs() { ComDebOut((DEB_MARSHAL,"CStdMarshal::DisconnectCliIPIDs this:%x pFirst:%x\n", this, _pFirstIPID)); Win4Assert(ClientSide()); Win4Assert(_dwFlags & SMFLAGS_DISCONNECTED); // YIELD WARNING: Do not yield between here and the matching comment // below, since we are mucking with internal state that could get // messed up if a reconnect (or unmarshal) is done. ASSERT_LOCK_HELD // on client side, we cant actually release the proxies until the // object goes away (backward compatibility), so we just release // our references to the remote guy, disconnect the proxies, and // delete the channels, but hold on to the IPIDEntries. REMINTERFACEREF *pRifRefAlloc = (REMINTERFACEREF *) _alloca(_cIPIDs * 2 * sizeof(REMINTERFACEREF)); REMINTERFACEREF *pRifRef = pRifRefAlloc; OXIDEntry *pOXID = NULL; USHORT cRifRef = 0; IPIDEntry *pEntry = _pFirstIPID; while (pEntry) { // we have to handle the case where ConnectIPIDEntry partially (but // not completely) set up the IPIDEntry, hence we cant just check // for the IPIDF_DISCONNECTED flag. ValidateIPIDEntry(pEntry); // NOTE: we are calling Proxy code here while holding the ORPC LOCK. // There is no way to get around this without introducing race // conditions. We cant just disconnect the channel and leave the // proxy connected cause some proxies (like IDispatch) do weird shit, // like keeping separate pointers to the server. if (pEntry->pStub) // NULL for IUnknown IPID { ComDebOut((DEB_MARSHAL, "Disconnect pProxy:%x\n", pEntry->pStub)); ((IRpcProxyBuffer *)pEntry->pStub)->Disconnect(); pEntry->pv = NULL; } if (!(pEntry->dwFlags & IPIDF_NOPING)) { // the object pays attention to pings (and hence refcounts) if (pEntry->cStrongRefs > 0 || pEntry->cPrivateRefs > 0) { // we own some strong references on this interface, fill // in an interfaceref so we release them. pRifRef->cPublicRefs = pEntry->cStrongRefs; pRifRef->cPrivateRefs = pEntry->cPrivateRefs; pRifRef->ipid = pEntry->ipid; pRifRef++; cRifRef++; } if (pEntry->cWeakRefs > 0) { // we own some weak references on this interface, fill // in an interfaceref so we release them. pRifRef->cPublicRefs = pEntry->cWeakRefs; pRifRef->cPrivateRefs = 0; pRifRef->ipid = pEntry->ipid; // mark the IPID as weak so that RemRelease on the server // knows to release weak references instead of strong refs. pRifRef->ipid.Data1 |= IPIDFLAG_WEAKREF; pRifRef++; cRifRef++; } } pEntry->cStrongRefs = 0; pEntry->cWeakRefs = 0; pEntry->cPrivateRefs = 0; pEntry->dwFlags |= IPIDF_DISCONNECTED | IPIDF_NOPING; if (pEntry->pChnl) { // release the channel for this IPID pEntry->pChnl->Release(); pEntry->pChnl = NULL; } if (pEntry->pOXIDEntry) { // We will be decrementing the OXID refcnt as we release IPIDEntries // but we dont want the OXIDEntry to go away until after we make the // RemoteRelease call below, so we hold on to it here. if (pOXID == NULL) { pOXID = pEntry->pOXIDEntry; IncOXIDRefCnt(pOXID); } // If we ever go to a model where different IPIDEntries on the // same object can point to different OXIDEntires, then we need // to re-write this code to batch the releases by OXID. Win4Assert(pOXID == pEntry->pOXIDEntry); // release the RefCnt on the OXIDEntry DecOXIDRefCnt(pEntry->pOXIDEntry); pEntry->pOXIDEntry = NULL; } // get next IPID in chain for this object pEntry = pEntry->pNextOID; } if (_pChnl) { // release the last client side channel _pChnl->Release(); _pChnl = NULL; } if (_dwFlags & SMFLAGS_REGISTEREDOID) { // Tell the resolver to stop pinging the OID. The OID is only // registered on the client side. Win4Assert(ClientSide()); gResolver.ClientDeRegisterOIDFromPingServer(_pStdId->GetOID(), _dwFlags & SMFLAGS_CLIENTMARSHALED); } // turn these flags off so re-connect (with new OID) will behave properly. _dwFlags &= ~(SMFLAGS_CLIENTMARSHALED | SMFLAGS_REGISTEREDOID | SMFLAGS_NOPING); // YIELD WARNING: Up this this point we have been mucking with our // internal state. We cant yield before this point or a reconnect // proxies could get all screwed up. It is OK to yield after this point // because all internal state changes are now complete. The function // to release the remote references yield. if (cRifRef != 0) { // we have looped filling in the RifRef and entries exist in the // array. go call the server now to release the IPIDs. Win4Assert(pOXID); // must have been at least one RemoteReleaseRifRef(pOXID, cRifRef, pRifRefAlloc); } if (pOXID) { // Now release the refcnt (if any) we put on the OXIDEntry above // to hold it DecOXIDRefCnt(pOXID); } ASSERT_LOCK_HELD DbgWalkIPIDs(); ComDebOut((DEB_MARSHAL, "CStdMarshal::DisconnectCliIPIDs this:%x\n",this)); } //+------------------------------------------------------------------- // // Member: CStdMarshal::DisconnectSrvIPIDs // // Synopsis: disconnects the server side IPIDs for this object. // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- void CStdMarshal::DisconnectSrvIPIDs() { ComDebOut((DEB_MARSHAL, "CStdMarshal::DisconnectSrvIPIDs this:%x pFirst:%x\n",this, _pFirstIPID)); Win4Assert(ServerSide()); // there should be no other threads looking at these IPIDs at this time, // since Marshal, Unmarshal, and Dispatch all call PreventDisconnect, // Disconnect checks the disconnected flag directly, RMD holds the // lock over it's whole execution, RemAddRef and RemRelease hold the // lock and check the disconnected flag of the IPIDEntry, and // RemQueryInterface calls PreventDisconnect. Win4Assert(_dwFlags & SMFLAGS_DISCONNECTED); Win4Assert(_cNestedCalls == 0); ASSERT_LOCK_HELD // while holding the lock, flag each IPID as disconnected so that no // more incoming calls are dispatched to this object. We also unchain // the IPIDs to ensure that no other threads are pointing at them. IPIDEntry *pFirstIPID = _pFirstIPID; _pFirstIPID = NULL; IPIDEntry *pEntry = pFirstIPID; while (pEntry) { pEntry->dwFlags |= IPIDF_VACANT | IPIDF_DISCONNECTED; // release the refcnt on the OXIDEntry and NULL it DecOXIDRefCnt(pEntry->pOXIDEntry); pEntry->pOXIDEntry = NULL; pEntry = pEntry->pNextOID; } // now release the LOCK since we will be calling into app code to // disconnect the stubs, and to release the external connection counts. // There should be no other pointers to these IPIDEntries now, so it // is safe to muck with their fields (except the dwFlags which is looked // at by Dispatch and was already set above). UNLOCK ASSERT_LOCK_RELEASED IPIDEntry *pLastIPID; pEntry = pFirstIPID; while (pEntry) { if (pEntry->dwFlags & IPIDF_NOTIFYACT) { // the activation code asked to be notified when the refcnt // on this interface reaches zero. Turn the flag off so we // don't call twice. pEntry->dwFlags &= ~IPIDF_NOTIFYACT; NotifyActivation(FALSE, (IUnknown *)(pEntry->pv)); } if (pEntry->pStub) // pStub is NULL for IUnknown IPID { ComDebOut((DEB_MARSHAL, "Disconnect pStub:%x\n", pEntry->pStub)); ((IUnknown *)pEntry->pv)->Release(); ((IRpcStubBuffer *)pEntry->pStub)->Disconnect(); pEntry->pStub->Release(); pEntry->pStub = NULL; pEntry->pv = NULL; } if (pEntry->cWeakRefs > 0) { // Release weak references on the StdId. pEntry->cWeakRefs = 0; _pStdId->Release(); } if (pEntry->cStrongRefs > 0) { // Release strong references on the StdId. Note that 16bit // 16bit OLE always passed fLastReleaseCloses = FALSE in // DisconnectObject so we do the same here. pEntry->cStrongRefs = 0; _pStdId->DecStrongCnt(TRUE); // fKeepAlive } if (pEntry->cPrivateRefs > 0) { // Release private references on the StdId. Note that 16bit // 16bit OLE always passed fLastReleaseCloses = FALSE in // DisconnectObject so we do the same here. pEntry->cPrivateRefs = 0; _pStdId->DecStrongCnt(TRUE); // fKeepAlive } pLastIPID = pEntry; pEntry = pEntry->pNextOID; } ASSERT_LOCK_RELEASED LOCK if (pFirstIPID) { // now we release all entries. gIPIDTbl.ReleaseEntryList(pFirstIPID, pLastIPID); } ASSERT_LOCK_HELD DbgWalkIPIDs(); ComDebOut((DEB_MARSHAL, "CStdMarshal::DisconnectSrvIPIDs [OUT] this:%x\n",this)); } //+------------------------------------------------------------------- // // Member: CStdMarshal::InstantiatedProxy, public // // Synopsis: return requested interfaces to the caller if instantiated // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- BOOL CStdMarshal::InstantiatedProxy(REFIID riid, void **ppv, HRESULT *phr) { ComDebOut((DEB_MARSHAL, "CStdMarshal::InstantiatedProxy this:%x riid:%I ppv:%x\n", this, &riid, ppv)); AssertValid(); Win4Assert(ClientSide()); Win4Assert(*ppv == NULL); Win4Assert(*phr == S_OK); BOOL fRet = FALSE; ASSERT_LOCK_RELEASED LOCK // look for an existing IPIDEntry for the requested interface IPIDEntry *pEntry; HRESULT hr = FindIPIDEntry(riid, &pEntry); if (SUCCEEDED(hr) && pEntry->pv) { // found the ipid entry, now extract the interface // pointer to return to the caller. Win4Assert(IsValidInterface(pEntry->pv)); *ppv = pEntry->pv; fRet = TRUE; } else if (_cIPIDs == 0) { // no IPIDEntry for the requested interface, and we have never // been connected to the server. Return E_NOINTERFACE in this // case. This is different from having been connected then // disconnected, where we return CO_E_OBJNOTCONNECTED. *phr = E_NOINTERFACE; Win4Assert(fRet == FALSE); } else if (_dwFlags & SMFLAGS_PENDINGDISCONNECT) { // no IPIDEntry for the requested interface and disconnect is // pending, so return an error. *phr = CO_E_OBJNOTCONNECTED; Win4Assert(fRet == FALSE); } else { // no IPIDEntry, we are not disconnected, and we do have other // instantiated proxies. QueryMultipleInterfaces expects // *phr == S_OK and FALSE returned. Win4Assert(*phr == S_OK); Win4Assert(fRet == FALSE); } UNLOCK ASSERT_LOCK_RELEASED ComDebOut((DEB_MARSHAL, "CStdMarshal::InstantiatedProxy hr:%x pv:%x fRet:%x\n", *phr, *ppv, fRet)); return fRet; } //+------------------------------------------------------------------- // // Member: CStdMarshal::QueryRemoteInterfaces, public // // Synopsis: return requested interfaces to the caller if supported // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- HRESULT CStdMarshal::QueryRemoteInterfaces(USHORT cIIDs, IID *pIIDs, SQIResult *pQIRes) { ComDebOut((DEB_MARSHAL, "CStdMarshal::QueryRemoteInterfaces this:%x pIIDs:%x pQIRes:%x\n", this, pIIDs, pQIRes)); AssertValid(); Win4Assert(ClientSide()); Win4Assert(cIIDs > 0); ASSERT_LOCK_RELEASED LOCK HRESULT hr = PreventDisconnect(); if (SUCCEEDED(hr)) { // call QI on the remote guy and unmarshal the results hr = RemQIAndUnmarshal(cIIDs, pIIDs, pQIRes); } else { // cant call out because we're disconnected so return error for // each requested interface. for (USHORT i=0; ihr = hr; } } UNLOCK ASSERT_LOCK_RELEASED // if the object was disconnected while in the middle of the call, // then we still return SUCCESS for any interfaces we acquired. The // reason is that we do have the proxies, and this matches the // behaviour of a QI for an instantiated proxy on a disconnected // object. hr = HandlePendingDisconnect(hr); ComDebOut((DEB_MARSHAL, "CStdMarshal::QueryRemoteInterfaces this:%x hr:%x\n", this, hr)); return hr; } //+------------------------------------------------------------------- // // Member: CStdMarshal::RemQIAndUnmarshal, private // // Synopsis: call QI on remote guy, then unmarshal the STDOBJREF // to create the IPID, and return the interface ptr. // // History: 20-Feb-95 Rickhi Created. // // Notes: Caller must guarantee at least one IPIDEntry is connected. // This function does a sparse fill of the result array. // //-------------------------------------------------------------------- HRESULT CStdMarshal::RemQIAndUnmarshal(USHORT cIIDs, IID *pIIDs, SQIResult *pQIRes) { ComDebOut((DEB_MARSHAL, "CStdMarshal::RemQIAndUnmarshal this:%x cIIDs:%x pIIDs:%x pQIRes:%x\n", this, cIIDs, pIIDs, pQIRes)); AssertValid(); AssertDisconnectPrevented(); Win4Assert(_pFirstIPID); // must be at least 1 IPIDEntry ASSERT_LOCK_HELD // we need an IPID to call RemoteQueryInterface with, any one will // do so long as it is connected (in the reconnect case there may be // only one connected IPID) so we pick the first one in the chain that // is connected. IPIDEntry *pIPIDEntry = GetConnectedIPID(); // remember what type of reference to get since we yield the lock // and cant rely on _dwFlags later. BOOL fWeakClient = (_dwFlags & SMFLAGS_WEAKCLIENT); // call the remote guy REMQIRESULT *pRemQiRes = NULL; IRemUnknown *pRemUnk; HRESULT hr = GetSecureRemUnk( &pRemUnk, pIPIDEntry->pOXIDEntry ); if (SUCCEEDED(hr)) { hr = RemoteQueryInterface(pRemUnk, pIPIDEntry, cIIDs, pIIDs, &pRemQiRes, fWeakClient); } // need to remember the result ptr so we can free it. REMQIRESULT *pRemQiResNext = pRemQiRes; // unmarshal each STDOBJREF returned. Note that while we did the // RemoteQI we could have yielded (or nested) and did another // RemoteQI for the same interfaces, so we have to call UnmarshalIPID // which will find any existing IPIDEntry and bump its refcnt. HRESULT hr2; HRESULT *phr = &hr2; void *pv; void **ppv = &pv; for (USHORT i=0; ipv; phr = &pQIRes->hr; pQIRes++; } if (SUCCEEDED(hr)) { if (SUCCEEDED(pRemQiResNext->hResult)) { if (fWeakClient) { // mark the std objref with the weak reference flag so // that UnmarshalIPID adds the references to the correct // count. pRemQiResNext->std.flags |= SORF_WEAKREF; } *phr = UnmarshalIPID(*pIIDs, &pRemQiResNext->std, pIPIDEntry->pOXIDEntry, (pQIRes) ? ppv : NULL); if (FAILED(*phr)) { // could not unmarshal, release the resources with the // server. RemoteReleaseStdObjRef(&pRemQiResNext->std, pIPIDEntry->pOXIDEntry); } } else if (pQIRes) { // the requested interface was not returned so set the // return code and interface ptr. *phr = pRemQiResNext->hResult; *ppv = NULL; } pIIDs++; pRemQiResNext++; } else { // the whole call failed so return the error for each // requested interface. *phr = hr; *ppv = NULL; } // make sure the ptr value is NULL on failure. It may be NULL or // non-NULL on success. (ReconnectProxies wants NULL). Win4Assert(SUCCEEDED(*phr) || *ppv == NULL); } // free the result buffer CoTaskMemFree(pRemQiRes); ASSERT_LOCK_HELD AssertDisconnectPrevented(); ComDebOut((DEB_MARSHAL, "CStdMarshal::RemQIAndUnmarshal this:%x hr:%x\n", this, hr)); return hr; } //+------------------------------------------------------------------- // // Member: CStdMarshal::RemIsConnected, private // // Synopsis: Returns TRUE if most likely connected, FALSE if definitely // not connected or pending disconnect. // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- BOOL CStdMarshal::RemIsConnected(void) { AssertValid(); Assert(ClientSide()); // the default link depends on us returning FALSE if we are either // disconnected or just pending disconnect, in order that they avoid // running their cleanup code twice. BOOL fRes = (_dwFlags & (SMFLAGS_DISCONNECTED | SMFLAGS_PENDINGDISCONNECT)) ? FALSE : TRUE; ComDebOut((DEB_MARSHAL, "CStdMarshal::RemIsConnected this:%x fResult:%x\n", this, fRes)); return fRes; } //+------------------------------------------------------------------- // // Member: CreateChannel, private // // Synopsis: Creates an instance of the Rpc Channel. // // History: 20-Feb-95 Rickhi Created // //-------------------------------------------------------------------- HRESULT CStdMarshal::CreateChannel(OXIDEntry *pOXIDEntry, DWORD dwFlags, REFIPID ripid, REFIID riid, CRpcChannelBuffer **ppChnl) { ASSERT_LOCK_HELD HRESULT hr = S_OK; if (_pChnl == NULL) { DWORD cState = ServerSide() ? server_cs : client_cs; cState |= (dwFlags & SORF_FREETHREADED) ? freethreaded_cs : 0; // make a channel. We dont need the call control stuff so just // create the base class. _pChnl = new CRpcChannelBuffer(_pStdId, pOXIDEntry, cState); if (_pChnl == NULL) { hr = E_OUTOFMEMORY; } } if (SUCCEEDED(hr) && ClientSide()) { *ppChnl = _pChnl->Copy(pOXIDEntry, ripid, riid); if (*ppChnl == NULL) { hr = E_OUTOFMEMORY; } } else { *ppChnl = _pChnl; } ASSERT_LOCK_HELD return hr; } //+------------------------------------------------------------------- // // Member: GetPSFactory, private // // Synopsis: loads the proxy/stub factory for given IID // // History: 20-Feb-95 Rickhi Created // //-------------------------------------------------------------------- HRESULT CStdMarshal::GetPSFactory(REFIID riid, IUnknown *pUnkWow, BOOL fServer, IPSFactoryBuffer **ppIPSF, BOOL *pfNonNDR) { ComDebOut((DEB_MARSHAL, "CStdMarshal::GetPSFactory this:%x riid:%I pUnkWow:%x\n", this, &riid, pUnkWow)); ASSERT_LOCK_RELEASED // map iid to classid CLSID clsid; HRESULT hr = gRIFTbl.RegisterInterface(riid, fServer, &clsid); #ifdef WX86OLE BOOL fWx86 = FALSE; #endif if (SUCCEEDED(hr)) { BOOL fWow = FALSE; if (IsWOWThread()) { // figure out if this is a custom interface from a 16bit // app, since we have to load the 16bit proxy code if so. IThunkManager *pThkMgr; g_pOleThunkWOW->GetThunkManager(&pThkMgr); Win4Assert(pThkMgr && "pUnk in WOW does not support IThunkManager."); if (pUnkWow) fWow = pThkMgr->IsCustom3216Proxy(pUnkWow, riid); else fWow = pThkMgr->IsIIDRequested(riid); pThkMgr->Release(); } #ifdef WX86OLE // If we are in a Wx86 process then we need to determine if the // PSFactory needs to be an x86 or native one. else if (gcwx86.IsWx86Enabled()) { // Callout to wx86 to ask it to determine if an x86 PS factory // is required. Whole32 can tell if the stub needs to be x86 // by determining if pUnkWow is a custom interface proxy or not. // Whole32 can determine if a x86 proxy is required by checking // if the riid is one for a custom interface that is expected // to be returned. fWx86 = gcwx86.NeedX86PSFactory(pUnkWow, riid); } #endif // if we are loading a 16bit custom proxy then mark it as non NDR *pfNonNDR = (fWow) ? TRUE : FALSE; if (IsEqualGUID(clsid, CLSID_PSOlePrx32)) { // its our internal CLSID so go straight to our class factory. hr = PrxDllGetClassObject(clsid, IID_IPSFactoryBuffer, (void **)ppIPSF); } else { #ifdef WX86OLE DWORD dwContext = fWow ? CLSCTX_INPROC_SERVER16 : (fWx86 ? CLSCTX_INPROC_SERVERX86 : CLSCTX_INPROC_SERVER) | CLSCTX_PS_DLL; #else DWORD dwContext = fWow ? CLSCTX_INPROC_SERVER16 : CLSCTX_INPROC_SERVER | CLSCTX_PS_DLL; #endif // load the dll and get the PS class object hr = ICoGetClassObject(clsid, dwContext, NULL, IID_IPSFactoryBuffer, (void **)ppIPSF); #ifdef WX86OLE if (fWx86 && FAILED(hr)) { // if we are looking for an x86 PSFactory and we didn't find // one on InprocServerX86 key then we need to check // InprocServer32 key as well. hr = ICoGetClassObject(clsid, CLSCTX_INPROC_SERVER | CLSCTX_PS_DLL, NULL, IID_IPSFactoryBuffer, (void **)ppIPSF); if (SUCCEEDED(hr) && (! gcwx86.IsN2XProxy((IUnknown *)*ppIPSF))) { ((IUnknown *)*ppIPSF)->Release(); hr = REGDB_E_CLASSNOTREG; } } #endif AssertOutPtrIface(hr, *ppIPSF); } } #if DBG==1 // if the fake NonNDR flag is set and its the test interface, then // trick the code into thinking this is a nonNDR proxy. This is to // enable simpler testing of an esoteric feature. if (gfFakeNonNDR && IsEqualIID(riid, IID_ICube)) { *pfNonNDR = TRUE; } #endif ComDebOut((DEB_MARSHAL, "CStdMarshal::GetPSFactory this:%x pIPSF:%x fNonNDR:%x hr:%x\n", this, *ppIPSF, *pfNonNDR, hr)); ASSERT_LOCK_RELEASED return hr; } //+------------------------------------------------------------------- // // Member: CreateProxy, private // // Synopsis: creates an interface proxy for the given interface // // Returns: [ppv] - interface of type riid, AddRef'd // // History: 20-Feb-95 Rickhi Created // //-------------------------------------------------------------------- HRESULT CStdMarshal::CreateProxy(REFIID riid, IRpcProxyBuffer **ppProxy, void **ppv, BOOL *pfNonNDR) { TRACECALL(TRACE_MARSHAL, "CreateProxy"); ComDebOut((DEB_MARSHAL, "CStdMarshal::CreateProxy this:%x riid:%I\n", this, &riid)); AssertValid(); Win4Assert(ClientSide()); Win4Assert(ppProxy != NULL); ASSERT_LOCK_HELD // get the controlling IUnknown of this object IUnknown *punkCtrl = _pStdId->GetCtrlUnk(); Win4Assert(punkCtrl != NULL); if (InlineIsEqualGUID(riid, IID_IUnknown)) { // there is no proxy for IUnknown so we handle that case here punkCtrl->AddRef(); *ppv = (void **)punkCtrl; *ppProxy = NULL; *pfNonNDR = FALSE; return S_OK; } UNLOCK ASSERT_LOCK_RELEASED // now construct the proxy for the interface IPSFactoryBuffer *pIPSF = NULL; HRESULT hr = GetPSFactory(riid, NULL, FALSE, &pIPSF, pfNonNDR); if (SUCCEEDED(hr)) { // got the class factory, now create an instance hr = pIPSF->CreateProxy(punkCtrl, riid, ppProxy, ppv); AssertOutPtrIface(hr, *ppProxy); pIPSF->Release(); } ASSERT_LOCK_RELEASED LOCK ComDebOut((DEB_MARSHAL, "CStdMarshal::CreateProxy this:%x pProxy:%x pv:%x fNonNDR:%x hr:%x\n", this, *ppProxy, *ppv, *pfNonNDR, hr)); return hr; } //+------------------------------------------------------------------- // // Member: CreateStub, private // // Synopsis: creates an interface stub and adds it to the IPID table // // History: 20-Feb-95 Rickhi Created // //-------------------------------------------------------------------- HRESULT CStdMarshal::CreateStub(REFIID riid, IRpcStubBuffer **ppStub, void **ppv, BOOL *pfNonNDR) { TRACECALL(TRACE_MARSHAL, "CreateStub"); ComDebOut((DEB_MARSHAL, "CStdMarshal::CreateStub this:%x riid:%I\n", this, &riid)); AssertValid(); Win4Assert(ServerSide()); Win4Assert(ppStub != NULL); ASSERT_LOCK_HELD // get the IUnknown of the object IUnknown *punkObj = _pStdId->GetServer(); Win4Assert(punkObj != NULL); if (InlineIsEqualGUID(riid, IID_IUnknown)) { // there is no stub for IUnknown so we handle that here *ppv = (void *)punkObj; *ppStub = NULL; *pfNonNDR = FALSE; return S_OK; } UNLOCK ASSERT_LOCK_RELEASED // make sure the object supports the given interface, so we dont // waste a bunch of effort creating a stub if the interface is // not supported. IUnknown *pUnkIf = NULL; HRESULT hr; #ifdef WX86OLE if (gcwx86.IsN2XProxy(punkObj)) { // If we are creating a stub for an object that is living on the // x86 side then we need to set the StubInvoke flag to allow QI // to thunk the custom interface QI. gcwx86.SetStubInvokeFlag((BOOL)1); } #endif hr = punkObj->QueryInterface(riid, (void **)&pUnkIf); AssertOutPtrIface(hr, pUnkIf); if (SUCCEEDED(hr)) { // now construct the stub for the interface IPSFactoryBuffer *pIPSF = NULL; hr = GetPSFactory(riid, pUnkIf, TRUE, &pIPSF, pfNonNDR); if (SUCCEEDED(hr)) { // got the class factory, now create an instance hr = pIPSF->CreateStub(riid, punkObj, ppStub); AssertOutPtrIface(hr, *ppStub); pIPSF->Release(); } if (SUCCEEDED(hr)) { // remember the interface pointer *ppv = (void *)pUnkIf; } else { // error, release the interface and return NULL pUnkIf->Release(); *ppv = NULL; } } ASSERT_LOCK_RELEASED LOCK ComDebOut((DEB_MARSHAL, "CStdMarshal::CreateStub this:%x pStub:%x pv:%x fNonNDR:%x hr:%x\n", this, *ppStub, *ppv, *pfNonNDR, hr)); return hr; } //+------------------------------------------------------------------- // // Member: FindIPIDEntry, private // // Synopsis: Finds an IPIDEntry, chained off this object, with the // given riid. // // History: 20-Feb-95 Rickhi Created // //-------------------------------------------------------------------- HRESULT CStdMarshal::FindIPIDEntry(REFIID riid, IPIDEntry **ppEntry) { ComDebOut((DEB_OXID,"CStdMarshal::FindIPIDEntry ppEntry:%x riid:%I\n", ppEntry, &riid)); ASSERT_LOCK_HELD IPIDEntry *pEntry = _pFirstIPID; while (pEntry) { if (InlineIsEqualGUID(riid, pEntry->iid)) { *ppEntry = pEntry; return S_OK; } pEntry = pEntry->pNextOID; // get next entry in object chain } ASSERT_LOCK_HELD return E_NOINTERFACE; } //+------------------------------------------------------------------- // // Member: FindIPIDEntryByIPID, private // // Synopsis: returns the IPIDEntry ptr for the given IPID // // History: 20-Feb-95 Rickhi Created // //-------------------------------------------------------------------- HRESULT CStdMarshal::FindIPIDEntryByIPID(REFIPID ripid, IPIDEntry **ppEntry) { ASSERT_LOCK_HELD IPIDEntry *pEntry = _pFirstIPID; while (pEntry) { if (InlineIsEqualGUID(pEntry->ipid, ripid)) { *ppEntry = pEntry; return S_OK; } pEntry = pEntry->pNextOID; // get next entry in object chain } ASSERT_LOCK_HELD return E_NOINTERFACE; } //+------------------------------------------------------------------- // // Member: FindIPIDEntryByInterface, internal // // Synopsis: returns the IPIDEntry ptr for the given proxy // //-------------------------------------------------------------------- HRESULT CStdMarshal::FindIPIDEntryByInterface(void *pProxy, IPIDEntry **ppEntry) { ASSERT_LOCK_HELD IPIDEntry *pEntry = _pFirstIPID; *ppEntry = NULL; while (pEntry) { if (pEntry->pv == pProxy) { *ppEntry = pEntry; break; } pEntry = pEntry->pNextOID; } if (*ppEntry != NULL) return S_OK; else return E_NOINTERFACE; } //+------------------------------------------------------------------- // // Member: IncSrvIPIDCnt, protected // // Synopsis: increments the refcnt on the IPID entry, and optionally // AddRefs the StdId. // // History: 20-Feb-95 Rickhi Created // //-------------------------------------------------------------------- HRESULT CStdMarshal::IncSrvIPIDCnt(IPIDEntry *pEntry, ULONG cRefs, ULONG cPrivateRefs, SECURITYBINDING *pName, DWORD mshlflags) { ComDebOut((DEB_MARSHAL, "IncSrvIPIDCnt this:%x pIPID:%x cRefs:%x cPrivateRefs:%x\n", this, pEntry, cRefs, cPrivateRefs)); Win4Assert(ServerSide()); Win4Assert(pEntry); Win4Assert(cRefs > 0 || cPrivateRefs > 0); ASSERT_LOCK_HELD HRESULT hr = S_OK; if (cPrivateRefs != 0) { // Add a reference. hr = gSRFTbl.IncRef( cPrivateRefs, pEntry->ipid, pName ); if (SUCCEEDED(hr)) { BOOL fNotify = (pEntry->cPrivateRefs == 0) ? TRUE : FALSE; pEntry->cPrivateRefs += cPrivateRefs; if (fNotify) { // this inc causes the count to go from zero to non-zero, so we // inc the strong count on the stdid to hold it alive until this // IPID is released. IncStrongAndNotifyAct(pEntry, mshlflags); } } } if (SUCCEEDED(hr)) { if (mshlflags & (MSHLFLAGS_TABLESTRONG | MSHLFLAGS_TABLEWEAK)) { // Table Marshal Case: inc the number of table marshals. IncTableCnt(); } if (mshlflags & (MSHLFLAGS_WEAK | MSHLFLAGS_TABLEWEAK)) { if (pEntry->cWeakRefs == 0) { // this inc causes the count to go from zero to non-zero, so we // AddRef the stdid to hold it alive until this IPID is released. _pStdId->AddRef(); } pEntry->cWeakRefs += cRefs; } else { BOOL fNotify = (pEntry->cStrongRefs == 0) ? TRUE : FALSE; pEntry->cStrongRefs += cRefs; if (fNotify) { // this inc causes the count to go from zero to non-zero, so we // inc the strong count on the stdid to hold it alive until this // IPID is released. IncStrongAndNotifyAct(pEntry, mshlflags); } } } ASSERT_LOCK_HELD return hr; } //+------------------------------------------------------------------- // // Member: IncTableCnt, public // // Synopsis: increments the count of table marshals // // History: 9-Oct-96 Rickhi Created // //-------------------------------------------------------------------- void CStdMarshal::IncTableCnt(void) { ASSERT_LOCK_HELD // If something was marshaled for a table, we have to ignore // rundowns until a subsequent RMD is called for it, at which // time we start paying attention to rundowns again. Since there // can be any number of table marshals, we have to refcnt them. _cTableRefs++; _dwFlags |= SMFLAGS_IGNORERUNDOWN; } //+------------------------------------------------------------------- // // Member: IncStrongAndNotifyAct, private // // Synopsis: notifies the activation code when this interface refcnt // goes from 0 to non-zero and the activation code asked to be // notified, and also increments the strong refcnt. // // History: 21-Apr-96 Rickhi Created // //-------------------------------------------------------------------- void CStdMarshal::IncStrongAndNotifyAct(IPIDEntry *pEntry, DWORD mshlflags) { ASSERT_LOCK_HELD // inc the strong count on the stdid to hold it alive until this // IPIDEntry is released. _pStdId->IncStrongCnt(); if (mshlflags & MSHLFLAGS_NOTIFYACTIVATION && !(pEntry->dwFlags & IPIDF_NOTIFYACT)) { // the activation code asked to be notified when the refcnt // on this interface goes positive, and when it reaches // zero again. Set a flag so we remember to notify // activation when the strong reference reference count // goes back down to zero. pEntry->dwFlags |= IPIDF_NOTIFYACT; UNLOCK ASSERT_LOCK_RELEASED BOOL fOK = NotifyActivation(TRUE, (IUnknown *)(pEntry->pv)); ASSERT_LOCK_RELEASED LOCK if (!fOK) { // call failed, so dont bother notifying pEntry->dwFlags &= ~IPIDF_NOTIFYACT; } } } //+------------------------------------------------------------------- // // Member: DecSrvIPIDCnt, protected // // Synopsis: decrements the refcnt on the IPID entry, and optionally // Releases the StdId. // // History: 20-Feb-95 Rickhi Created // //-------------------------------------------------------------------- void CStdMarshal::DecSrvIPIDCnt(IPIDEntry *pEntry, ULONG cRefs, ULONG cPrivateRefs, SECURITYBINDING *pName, DWORD mshlflags) { ComDebOut((DEB_MARSHAL, "DecSrvIPIDCnt this:%x pIPID:%x cRefs:%x cPrivateRefs:%x\n", this, pEntry, cRefs, cPrivateRefs)); Win4Assert(ServerSide()); Win4Assert(pEntry); Win4Assert(cRefs > 0 || cPrivateRefs > 0); ASSERT_LOCK_HELD // Note: we dont care about holding the LOCK over the Release call since // the guy who called us is holding a ref to the StdId, so this Release // wont cause us to go away. if (mshlflags & (MSHLFLAGS_TABLESTRONG | MSHLFLAGS_TABLEWEAK)) { // Table Marshal Case: dec the number of table marshals. DecTableCnt(); } if (mshlflags & (MSHLFLAGS_WEAK | MSHLFLAGS_TABLEWEAK)) { Win4Assert(pEntry->cWeakRefs >= cRefs); pEntry->cWeakRefs -= cRefs; if (pEntry->cWeakRefs == 0) { // this dec caused the count to go from non-zero to zero, so we // Release the stdid since this IPID is no longer holding it alive. _pStdId->Release(); } } else { // Adjust the strong reference count. Don't let the caller release // too many times. if (pEntry->cStrongRefs < cRefs) { ComDebOut((DEB_WARN,"DecSrvIPIDCnt too many releases. IPID entry: 0x%x Extra releases: 0x%x", pEntry, cRefs-pEntry->cStrongRefs)); cRefs = pEntry->cStrongRefs; } pEntry->cStrongRefs -= cRefs; if (pEntry->cStrongRefs == 0 && cRefs != 0) { // this dec caused the count to go from non-zero to zero, so we // dec the strong count on the stdid since the public references // on this IPID is no longer hold it alive. DecStrongAndNotifyAct(pEntry, mshlflags); } // Adjust the secure reference count. Don't let the caller release // too many times. if (pName != NULL) { cPrivateRefs = gSRFTbl.DecRef(cPrivateRefs, pEntry->ipid, pName); } else { cPrivateRefs = 0; } Win4Assert( pEntry->cPrivateRefs >= cPrivateRefs ); pEntry->cPrivateRefs -= cPrivateRefs; if (pEntry->cPrivateRefs == 0 && cPrivateRefs != 0) { // this dec caused the count to go from non-zero to zero, so we // dec the strong count on the stdid since the private references // on this IPID is no longer hold it alive. DecStrongAndNotifyAct(pEntry, mshlflags); } } ASSERT_LOCK_HELD } //+------------------------------------------------------------------- // // Member: DecTableCnt, public // // Synopsis: decrements the count of table marshals // // History: 9-Oct-96 Rickhi Created // //-------------------------------------------------------------------- void CStdMarshal::DecTableCnt(void) { ASSERT_LOCK_HELD // If something was marshaled for a table, we have to ignore // rundowns until a subsequent RMD is called for it, at which // time we start paying attention to rundowns again. Since there // can be any number of table marshals, we have to refcnt them. // This is also used by CoLockObjectExternal. if (--_cTableRefs == 0) { // this was the last table marshal, so now we have to pay // attention to rundown from normal clients, so that if all // clients go away we cleanup. _dwFlags &= ~SMFLAGS_IGNORERUNDOWN; } } //+------------------------------------------------------------------- // // Member: DecStrongAndNotifyAct, private // // Synopsis: notifies the activation code if this interface has // been released and the activation code asked to be // notified, and also decrements the strong refcnt. // // History: 21-Apr-96 Rickhi Created // //-------------------------------------------------------------------- void CStdMarshal::DecStrongAndNotifyAct(IPIDEntry *pEntry, DWORD mshlflags) { ASSERT_LOCK_HELD BOOL fNotifyAct = FALSE; if ((pEntry->dwFlags & IPIDF_NOTIFYACT) && pEntry->cStrongRefs == 0 && pEntry->cPrivateRefs == 0) { // the activation code asked to be notified when the refcnt // on this interface reaches zero. Turn the flag off so we // don't call twice. pEntry->dwFlags &= ~IPIDF_NOTIFYACT; fNotifyAct = TRUE; } UNLOCK ASSERT_LOCK_RELEASED if (fNotifyAct) { NotifyActivation(FALSE, (IUnknown *)(pEntry->pv)); } _pStdId->DecStrongCnt(mshlflags & MSHLFLAGS_KEEPALIVE); ASSERT_LOCK_RELEASED LOCK } //+------------------------------------------------------------------- // // Member: AddIPIDEntry, private // // Synopsis: Allocates and fills in an entry in the IPID table. // The returned entry is not yet in the IPID chain. // // History: 20-Feb-95 Rickhi Created // //-------------------------------------------------------------------- HRESULT CStdMarshal::AddIPIDEntry(OXIDEntry *pOXIDEntry, IPID *pipid, REFIID riid, CRpcChannelBuffer *pChnl, IUnknown *pUnkStub, void *pv, IPIDEntry **ppEntry) { ComDebOut((DEB_MARSHAL,"AddIPIDEntry this:%x pOXID:%x iid:%I pStub:%x pv:%x\n", this, pOXIDEntry, &riid, pUnkStub, pv)); ASSERT_LOCK_HELD // CODEWORK: while we released the lock to create the proxy or stub, // the same interface could have been marshaled/unmarshaled. We should // go check for duplicates now. This is just an optimization, not a // requirement. // get a new entry in the IPID table. IPIDEntry *pEntryNew = gIPIDTbl.FirstFree(); if (pEntryNew == NULL) { // no free slots and could not allocate more memory to grow return E_OUTOFMEMORY; } if (ServerSide()) { // create an IPID for this entry DWORD *pdw = &pipid->Data1; *pdw = gIPIDTbl.GetEntryIndex(pEntryNew); // IPID table index *(pdw+1) = GetCurrentProcessId(); // current PID *(pdw+2) = GetCurrentThreadId(); // current TID *(pdw+3) = gIPIDSeqNum++; // process sequence # } *ppEntry = pEntryNew; pEntryNew->ipid = *pipid; pEntryNew->iid = riid; pEntryNew->pChnl = pChnl; pEntryNew->pStub = pUnkStub; pEntryNew->pv = pv; pEntryNew->dwFlags = ServerSide() ? IPIDF_SERVERENTRY : IPIDF_DISCONNECTED | IPIDF_NOPING; pEntryNew->cStrongRefs = 0; pEntryNew->cWeakRefs = 0; pEntryNew->cPrivateRefs = 0; pEntryNew->pOXIDEntry = pOXIDEntry; ASSERT_LOCK_HELD ComDebOut((DEB_MARSHAL,"AddIPIDEntry this:%x pIPIDEntry:%x ipid:%I\n", this, pEntryNew, &pEntryNew->ipid)); return S_OK; } //+------------------------------------------------------------------- // // Member: ReleaseCliIPIDs, private // // Synopsis: walks the IPID table releasing the proxy/stub entries // on the IPIDEntries associated with this Object. // // History: 20-Feb-95 Rickhi Created // //-------------------------------------------------------------------- void CStdMarshal::ReleaseCliIPIDs(void) { Win4Assert(ClientSide()); ASSERT_LOCK_RELEASED LOCK // first thing we do is detach the chain of IPIDs from the CStdMarshal // while holding the LOCK. Then release the lock and walk the chain // releasing the proxy/stub pointers. Note there should not be any other // pointers to any of these IPIDs, so it is OK to muck with their state. IPIDEntry *pFirstIPID = _pFirstIPID; _pFirstIPID = NULL; UNLOCK ASSERT_LOCK_RELEASED; IPIDEntry *pLastIPID; IPIDEntry *pEntry = pFirstIPID; while (pEntry) { // mark the entry as vacant and disconnected. Note we dont put // it back in the FreeList yet. We leave it chained to the other // IPIDs in the list, and add the whole chain to the FreeList at // the end. pEntry->dwFlags |= IPIDF_VACANT | IPIDF_DISCONNECTED; if (pEntry->pStub) { ComDebOut((DEB_MARSHAL,"ReleaseProxy pProxy:%x\n", pEntry->pStub)); pEntry->pStub->Release(); pEntry->pStub = NULL; } pLastIPID = pEntry; pEntry = pEntry->pNextOID; } if (pFirstIPID != NULL) { // now take the LOCK again and release all the IPIDEntries back into // the IPIDTable in one fell swoop. ASSERT_LOCK_RELEASED LOCK gIPIDTbl.ReleaseEntryList(pFirstIPID, pLastIPID); UNLOCK ASSERT_LOCK_RELEASED } ASSERT_LOCK_RELEASED } //+------------------------------------------------------------------------ // // Member: CStdMarshal::LockClient/UnLockClient // // Synopsis: Locks the client side object during outgoing calls in order // to prevent the object going away in a nested disconnect. // // Notes: UnLockClient is not safe in the freethreaded model. // Fortunately pending disconnect can only be set in the // apartment model on the client side. // // History: 12-Jun-95 Rickhi Created // //------------------------------------------------------------------------- ULONG CStdMarshal::LockClient(void) { Win4Assert(ClientSide()); InterlockedIncrement(&_cNestedCalls); return (_pStdId->GetCtrlUnk())->AddRef(); } ULONG CStdMarshal::UnLockClient(void) { Win4Assert(ClientSide()); if ((InterlockedDecrement(&_cNestedCalls) == 0) && (_dwFlags & SMFLAGS_PENDINGDISCONNECT)) { Disconnect(); } return (_pStdId->GetCtrlUnk())->Release(); } //+------------------------------------------------------------------- // // Member: CStdMarshal::GetSecureRemUnk, public // // Synopsis: If the marshaller has its own remote unknown, use it. // Otherwise use the OXID's remote unknown. // // History: 2-Apr-96 AlexMit Created // //-------------------------------------------------------------------- HRESULT CStdMarshal::GetSecureRemUnk( IRemUnknown **ppSecureRemUnk, OXIDEntry *pOXIDEntry ) { ComDebOut((DEB_OXID, "CStdMarshal::GetSecureRemUnk ppRemUnk:%x\n", ppSecureRemUnk)); ASSERT_LOCK_DONTCARE if (_pSecureRemUnk != NULL) { *ppSecureRemUnk = _pSecureRemUnk; return S_OK; } else { return gOXIDTbl.GetRemUnk( pOXIDEntry, ppSecureRemUnk ); } } //+------------------------------------------------------------------- // // Member: CStdMarshal::LookupStub, private // // Synopsis: used by the channel to acquire the stub ptr for debugging // // History: 12-Jun-95 Rickhi Created // //-------------------------------------------------------------------- HRESULT CStdMarshal::LookupStub(REFIID riid, IRpcStubBuffer **ppStub) { AssertValid(); Win4Assert(ServerSide()); ASSERT_LOCK_RELEASED LOCK IPIDEntry *pEntry; HRESULT hr = FindIPIDEntry(riid, &pEntry); if (SUCCEEDED(hr)) { *ppStub = (IRpcStubBuffer *)pEntry->pStub; } UNLOCK ASSERT_LOCK_RELEASED return hr; } #if DBG==1 //+------------------------------------------------------------------- // // Member: CStdMarshal::GetOXID, private, debug // // Synopsis: returns the OXID for this object // // History: 20-Feb-95 Rickhi Created // //-------------------------------------------------------------------- REFMOXID CStdMarshal::GetMOXID(void) { ASSERT_LOCK_HELD if (ServerSide()) { // local to this apartment, use the local OXID return GetLocalOXIDEntry()->moxid; } else { Win4Assert(_pChnl); return _pChnl->GetMOXID(); } } //+------------------------------------------------------------------- // // Member: CStdMarshal::DbgWalkIPIDs // // Synopsis: Validates that the state of all the IPIDs is consistent. // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- void CStdMarshal::DbgWalkIPIDs(void) { IPIDEntry *pEntry = _pFirstIPID; while (pEntry) { ValidateIPIDEntry(pEntry); pEntry = pEntry->pNextOID; } } //+------------------------------------------------------------------- // // Member: CStdMarshal::AssertValid // // Synopsis: Validates that the state of the object is consistent. // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- void CStdMarshal::AssertValid() { LOCK Win4Assert((_dwFlags & ~(SMFLAGS_CLIENT_SIDE | SMFLAGS_REGISTEREDOID | SMFLAGS_PENDINGDISCONNECT | SMFLAGS_DISCONNECTED | SMFLAGS_FIRSTMARSHAL | SMFLAGS_HANDLER | SMFLAGS_WEAKCLIENT | SMFLAGS_IGNORERUNDOWN | SMFLAGS_CLIENTMARSHALED | SMFLAGS_NOPING | SMFLAGS_TRIEDTOCONNECT)) == 0); Win4Assert(_pStdId != NULL); Win4Assert(IsValidInterface(_pStdId)); if (_pChnl != NULL) { Win4Assert(IsValidInterface(_pChnl)); _pChnl->AssertValid(FALSE, FALSE); } DbgWalkIPIDs(); UNLOCK } //+------------------------------------------------------------------- // // Member: CStdMarshal::AssertDisconnectPrevented, private // // Synopsis: Just ensures that no disconnects can/have arrived. // // History: 21-Sep-95 Rickhi Created // //+------------------------------------------------------------------- void CStdMarshal::AssertDisconnectPrevented() { ASSERT_LOCK_HELD if (ServerSide()) Win4Assert(!(_dwFlags & SMFLAGS_DISCONNECTED)); Win4Assert(_cNestedCalls > 0); } //+------------------------------------------------------------------- // // Member: CStdMarshal::ValidateSTD // // Synopsis: Ensures that the STDOBJREF is valid // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- void CStdMarshal::ValidateSTD(STDOBJREF *pStd) { LOCK // validate the flags field Win4Assert((pStd->flags & SORF_RSRVD_MBZ) == 0); // validate the OID OID oid; OIDFromMOID(_pStdId->GetOID(), &oid); Win4Assert(pStd->oid == oid); if (ServerSide() || _pChnl != NULL) { // validate the OXID OXID oxid; OXIDFromMOXID(GetMOXID(), &oxid); Win4Assert(pStd->oxid == oxid ); } UNLOCK } //+------------------------------------------------------------------- // // Function: DbgDumpSTD // // Synopsis: dumps a formated STDOBJREF to the debugger // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- void DbgDumpSTD(STDOBJREF *pStd) { ULARGE_INTEGER *puintOxid = (ULARGE_INTEGER *)&pStd->oxid; ULARGE_INTEGER *puintOid = (ULARGE_INTEGER *)&pStd->oid; ComDebOut((DEB_MARSHAL, "\n\tpStd:%x flags:%08x cPublicRefs:%08x\n\toxid: %08x %08x\n\t oid: %08x %08x\n\tipid:%I\n", pStd, pStd->flags, pStd->cPublicRefs, puintOxid->HighPart, puintOxid->LowPart, puintOid->HighPart, puintOid->LowPart, &pStd->ipid)); } //+------------------------------------------------------------------- // // Member: CStdMarshal::ValidateIPIDEntry // // Synopsis: Ensures that the IPIDEntry is valid // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- void CStdMarshal::ValidateIPIDEntry(IPIDEntry *pEntry) { // ask the table to validate the IPID entry gIPIDTbl.ValidateIPIDEntry(pEntry, ServerSide(), _pChnl); } //+------------------------------------------------------------------- // // Member: CStdMarshal::DbgDumpInterfaceList // // Synopsis: Prints the list of Interfaces on the object. // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- void CStdMarshal::DbgDumpInterfaceList(void) { ComDebOut((DEB_ERROR, "\tInterfaces left on object are:\n")); LOCK // walk the IPID list printing the friendly name of each interface IPIDEntry *pEntry = _pFirstIPID; while (pEntry) { WCHAR wszName[MAX_PATH]; GetInterfaceName(pEntry->iid, wszName); ComDebOut((DEB_ERROR,"\t\t %ws\t cRefs:%x\n",wszName,pEntry->cStrongRefs)); pEntry = pEntry->pNextOID; } UNLOCK } #endif // DBG == 1 //+------------------------------------------------------------------- // // Function: RemoteQueryInterface, private // // Synopsis: call RemoteQueryInterface on remote server. // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- INTERNAL RemoteQueryInterface(IRemUnknown *pRemUnk, IPIDEntry *pIPIDEntry, USHORT cIIDs, IID *pIIDs, REMQIRESULT **ppQiRes, BOOL fWeakClient) { ComDebOut((DEB_MARSHAL, "RemoteQueryInterface pIPIDEntry:%x cIIDs:%x, pIIDs:%x riid:%I\n", pIPIDEntry, cIIDs, pIIDs, pIIDs)); Win4Assert(pIPIDEntry->pOXIDEntry); // must have a resolved oxid ASSERT_LOCK_HELD // set the IPID according to whether we want strong or weak // references. It will only be weak if we are an OLE container // and are talking to an embedding running on the same machine. IPID ipid = pIPIDEntry->ipid; if (fWeakClient) { ipid.Data1 |= IPIDFLAG_WEAKREF; } UNLOCK ASSERT_LOCK_RELEASED HRESULT hr = pRemUnk->RemQueryInterface(ipid, REM_ADDREF_CNT, cIIDs, pIIDs, ppQiRes); ASSERT_LOCK_RELEASED LOCK ASSERT_LOCK_HELD ComDebOut((DEB_MARSHAL, "RemoteQueryInterface hr:%x pQIRes:%x\n", hr, *ppQiRes)); return hr; } //+------------------------------------------------------------------- // // Function: RemoteAddRef, private // // Synopsis: calls the remote server to AddRef one of its interfaces // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- INTERNAL RemoteAddRef(IPIDEntry *pIPIDEntry, OXIDEntry *pOXIDEntry, ULONG cStrongRefs, ULONG cSecureRefs) { ComDebOut((DEB_MARSHAL, "RemoteAddRef cRefs:%x cSecure:%x ipid:%I\n", cStrongRefs, cSecureRefs, &pIPIDEntry->ipid)); ASSERT_LOCK_HELD // if the object does not require pinging, it is also ignoring // reference counts, so there is no need to go get more, just // pretend like we did. if (pIPIDEntry->dwFlags & IPIDF_NOPING) { return S_OK; } // get the IRemUnknown for the remote server IRemUnknown *pRemUnk; HRESULT hr = gOXIDTbl.GetRemUnk(pOXIDEntry, &pRemUnk); if (SUCCEEDED(hr)) { // call RemAddRef on the interface REMINTERFACEREF rifRef; rifRef.ipid = pIPIDEntry->ipid; rifRef.cPublicRefs = cStrongRefs; rifRef.cPrivateRefs = cSecureRefs; UNLOCK ASSERT_LOCK_RELEASED HRESULT ignore; hr = pRemUnk->RemAddRef(1, &rifRef, &ignore); ASSERT_LOCK_RELEASED LOCK } ASSERT_LOCK_HELD ComDebOut((DEB_MARSHAL, "RemoteAddRef hr:%x\n", hr)); return hr; } //+------------------------------------------------------------------- // // Function: RemoteReleaseRifRef // // Synopsis: calls the remote server to release some IPIDs // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- INTERNAL RemoteReleaseRifRef(OXIDEntry *pOXIDEntry, USHORT cRifRef, REMINTERFACEREF *pRifRef) { Win4Assert(pRifRef); ComDebOut((DEB_MARSHAL, "RemoteRelease pOXID:%x cRifRef:%x pRifRef:%x cRefs:%x ipid:%I\n", pOXIDEntry, cRifRef, pRifRef, pRifRef->cPublicRefs, &pRifRef->ipid)); Win4Assert(pOXIDEntry); ASSERT_LOCK_HELD HRESULT hr; if (IsSTAThread() && FAILED(CanMakeOutCall(CALLCAT_SYNCHRONOUS, IID_IRundown))) { // the call control will not let this apartment model thread make // the outgoing release call (cause we're inside an InputSync call) // so we post ourselves a message to do it later. hr = PostReleaseRifRef(pOXIDEntry, cRifRef, pRifRef); } else { // get the IRemUnknown for the remote server IRemUnknown *pRemUnk; hr = gOXIDTbl.GetRemUnk(pOXIDEntry, &pRemUnk); if (SUCCEEDED(hr)) { UNLOCK ASSERT_LOCK_RELEASED hr = pRemUnk->RemRelease(cRifRef, pRifRef); ASSERT_LOCK_RELEASED LOCK } } ComDebOut((DEB_MARSHAL, "RemoteRelease hr:%x\n", hr)); ASSERT_LOCK_HELD return hr; } //+------------------------------------------------------------------- // // Function: PostReleaseRifRef // // Synopsis: Post a message to ourself to call RemoteReleaseRifRef later. // This is used to make a synchronous remote Release call when // a Release is done inside of an InputSync call. The call is // delayed until we are out of the InputSync call, since the // call control wont allow a synch call inside an inputsync call. // // History: 05-Apr-96 Rickhi Created. // //-------------------------------------------------------------------- INTERNAL PostReleaseRifRef(OXIDEntry *pOXIDEntry, USHORT cRifRef, REMINTERFACEREF *pRifRef) { Win4Assert(pRifRef); ComDebOut((DEB_MARSHAL, "PostRelease pOXID:%x cRifRef:%x pRifRef:%x cRefs:%x ipid:%I\n", pOXIDEntry, cRifRef, pRifRef, pRifRef->cPublicRefs, &pRifRef->ipid)); Win4Assert(pOXIDEntry); ASSERT_LOCK_HELD OXIDEntry *pLocalOXIDEntry = NULL; HRESULT hr = gOXIDTbl.GetLocalEntry(&pLocalOXIDEntry); if (SUCCEEDED(hr)) { // allocate a structure to hold the data and copy in the RifRef // list, OXIDEntry, and count of entries. Inc the OXID RefCnt to // ensure it stays alive until the posted message is processed. hr = E_OUTOFMEMORY; ULONG cbRifRef = cRifRef * sizeof(REMINTERFACEREF); ULONG cbAlloc = sizeof(POSTRELRIFREF) + (cbRifRef-1); POSTRELRIFREF *pRelRifRef = (POSTRELRIFREF *) PrivMemAlloc(cbAlloc); if (pRelRifRef) { IncOXIDRefCnt(pOXIDEntry); // keep alive pRelRifRef->pOXIDEntry = pOXIDEntry; pRelRifRef->cRifRef = cRifRef; memcpy(&pRelRifRef->arRifRef, pRifRef, cbRifRef); if (!PostMessage((HWND)pLocalOXIDEntry->hServerSTA, WM_OLE_ORPC_RELRIFREF, GetCurrentThreadId(), (LPARAM)pRelRifRef)) { // Post failed, free the structure and report an error. DecOXIDRefCnt(pOXIDEntry); PrivMemFree(pRelRifRef); hr = RPC_E_SYS_CALL_FAILED; } } } ComDebOut((DEB_MARSHAL, "PostRelease hr:%x\n", hr)); ASSERT_LOCK_HELD return hr; } //+------------------------------------------------------------------- // // Function: HandlePostReleaseRifRef // // Synopsis: Handles the ReleaseRifRef message that was posted to the // current thread (by the current thread) in order to do a // delayed remote release call. See PostReleaseRifRef above. // // History: 05-Apr-96 Rickhi Created. // //-------------------------------------------------------------------- INTERNAL HandlePostReleaseRifRef(LPARAM param) { Win4Assert(param); ComDebOut((DEB_MARSHAL, "HandlePostRelease pRifRef:%x\n", param)); POSTRELRIFREF *pRelRifRef = (POSTRELRIFREF *)param; ASSERT_LOCK_RELEASED LOCK // simply make the real remote release call now, then release the // reference we have on the OXIDEntry, and free the message buffer. // If this call fails, dont try again, otherwise we could spin busy // waiting. Instead, just let Rundown clean up the server. RemoteReleaseRifRef(pRelRifRef->pOXIDEntry, pRelRifRef->cRifRef, &pRelRifRef->arRifRef); DecOXIDRefCnt(pRelRifRef->pOXIDEntry); UNLOCK ASSERT_LOCK_RELEASED PrivMemFree(pRelRifRef); ComDebOut((DEB_MARSHAL, "HandlePostRelease hr:%x\n", S_OK)); return S_OK; } //+------------------------------------------------------------------- // // Member: RemoteChangeRef // // Synopsis: calls the remote server to convert interface refereces // from strong to weak or vise versa. This behaviour is // required to support silent updates in the OLE container / // link / embedding scenarios. // // Notes: This functionality is not exposed in FreeThreaded apps // or in remote apps. The implication being that the container // must be on the same machine as the embedding. // // History: 20-Nov-95 Rickhi Created. // //-------------------------------------------------------------------- HRESULT CStdMarshal::RemoteChangeRef(BOOL fLock, BOOL fLastUnlockReleases) { ComDebOut((DEB_MARSHAL, "RemoteChangeRef \n")); Win4Assert(ClientSide()); Win4Assert(IsSTAThread()); // not allowed in MTA Apartment ASSERT_LOCK_RELEASED // must be at least 1 proxy already connected in order to be able // to do this. We cant just ASSERT that's true because we were not // holding the lock on entry. LOCK HRESULT hr = PreventDisconnect(); // A previous version of OLE set the object to weak even it it was // currently disconnected, and it remembered that it was weak and set // any new interfaces that it later accquired to weak. I emulate that // behaviour here. if (fLock) _dwFlags &= ~SMFLAGS_WEAKCLIENT; else _dwFlags |= SMFLAGS_WEAKCLIENT; if (SUCCEEDED(hr)) { REMINTERFACEREF *pRifRefAlloc = (REMINTERFACEREF *) _alloca(_cIPIDs * sizeof(REMINTERFACEREF)); REMINTERFACEREF *pRifRef = pRifRefAlloc; DWORD cSecure = gCapabilities & EOAC_SECURE_REFS ? 1 : 0; USHORT cIIDs = 0; OXIDEntry *pOXIDEntry = NULL; IPIDEntry *pNextIPID = _pFirstIPID; while (pNextIPID) { if (!(pNextIPID->dwFlags & IPIDF_DISCONNECTED)) { if (pOXIDEntry == NULL) { // This is the first connected IPID we encountered. // Get its OXID entry and make sure it is for a server // process on the current machine. if (!(pNextIPID->pOXIDEntry->dwFlags & OXIDF_MACHINE_LOCAL)) { // OXID is for a remote process. Abandon this call. Win4Assert(cIIDs == 0); // skip call below Win4Assert(pOXIDEntry == NULL); // dont dec below Win4Assert(hr == S_OK); // report success break; // exit while loop } // Remember the OXID and AddRef it to keep it alive // over the duration of the call. pOXIDEntry = pNextIPID->pOXIDEntry; IncOXIDRefCnt(pOXIDEntry); } pRifRef->ipid = pNextIPID->ipid; if (!fLock && pNextIPID->cStrongRefs > 0) { pRifRef->cPublicRefs = pNextIPID->cStrongRefs; pRifRef->cPrivateRefs = pNextIPID->cPrivateRefs; pNextIPID->cWeakRefs += pNextIPID->cStrongRefs; pNextIPID->cStrongRefs = 0; pNextIPID->cPrivateRefs = 0; pRifRef++; cIIDs++; } else if (fLock && pNextIPID->cStrongRefs == 0) { pRifRef->cPublicRefs = pNextIPID->cWeakRefs; pRifRef->cPrivateRefs = cSecure; pNextIPID->cStrongRefs += pNextIPID->cWeakRefs; pNextIPID->cWeakRefs = 0; pNextIPID->cPrivateRefs = cSecure; pRifRef++; cIIDs++; } } // get next IPIDentry for this object pNextIPID = pNextIPID->pNextOID; } if (cIIDs != 0) { // we have looped filling in the IPID list, and there are // entries in the list. go call the server now. First, set up // the flags, then reset the RifRef pointer since we trashed // it while walking the list above. DWORD dwFlags = (fLock) ? IRUF_CONVERTTOSTRONG : IRUF_CONVERTTOWEAK; if (fLastUnlockReleases) dwFlags |= IRUF_DISCONNECTIFLASTSTRONG; hr = RemoteChangeRifRef(pOXIDEntry, dwFlags, cIIDs, pRifRefAlloc); } if (pOXIDEntry) { // release the OXIDEntry DecOXIDRefCnt(pOXIDEntry); } } else { // A previous implementation of OLE returned S_OK if the object was // disconnected. I emulate that behaviour here. hr = S_OK; } DbgWalkIPIDs(); UNLOCK ASSERT_LOCK_RELEASED // this will handle any Disconnect that came in while we were busy. hr = HandlePendingDisconnect(hr); ComDebOut((DEB_MARSHAL, "RemoteChangeRef hr:%x\n", hr)); ASSERT_LOCK_RELEASED return hr; } //+------------------------------------------------------------------- // // Function: RemoteChangeRifRef // // Synopsis: calls the remote server to release some IPIDs // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- INTERNAL RemoteChangeRifRef(OXIDEntry *pOXIDEntry, DWORD dwFlags, USHORT cRifRef, REMINTERFACEREF *pRifRef) { Win4Assert(pRifRef); ComDebOut((DEB_MARSHAL, "RemoteChangeRifRef pOXID:%x cRifRef:%x pRifRef:%x cRefs:%x ipid:%I\n", pOXIDEntry, cRifRef, pRifRef, pRifRef->cPublicRefs, &(pRifRef->ipid))); Win4Assert(pOXIDEntry); ASSERT_LOCK_HELD // get the IRemUnknown for the remote server IRemUnknown *pRemUnk; HRESULT hr = gOXIDTbl.GetRemUnk(pOXIDEntry, &pRemUnk); if (SUCCEEDED(hr)) { UNLOCK ASSERT_LOCK_RELEASED hr = ((IRemUnknown2 *)pRemUnk)->RemChangeRef(dwFlags, cRifRef, pRifRef); ASSERT_LOCK_RELEASED LOCK } ComDebOut((DEB_MARSHAL, "RemoteChangeRifRef hr:%x\n", hr)); ASSERT_LOCK_HELD return hr; } //+------------------------------------------------------------------- // // Function: RemoteReleaseStdObjRef // // Synopsis: calls the remote server to release an ObjRef // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- INTERNAL RemoteReleaseStdObjRef(STDOBJREF *pStd, OXIDEntry *pOXIDEntry) { ComDebOut((DEB_MARSHAL, "RemoteReleaseStdObjRef pStd:%x\n pOXIDEntry:%x", pStd, pOXIDEntry)); ASSERT_LOCK_HELD REMINTERFACEREF rifRef; rifRef.ipid = pStd->ipid; rifRef.cPublicRefs = pStd->cPublicRefs; rifRef.cPrivateRefs = 0; // incase we get disconnected while in the RemRelease call // we need to extract the OXIDEntry and AddRef it. IncOXIDRefCnt(pOXIDEntry); RemoteReleaseRifRef(pOXIDEntry, 1, &rifRef); DecOXIDRefCnt(pOXIDEntry); ComDebOut((DEB_MARSHAL, "RemoteReleaseStdObjRef hr:%x\n", S_OK)); ASSERT_LOCK_HELD return S_OK; } //+------------------------------------------------------------------- // // Function: RemoteReleaseObjRef // // Synopsis: calls the remote server to release an ObjRef // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- INTERNAL RemoteReleaseObjRef(OBJREF &objref) { return RemoteReleaseStdObjRef(&ORSTD(objref).std, GetOXIDFromObjRef(objref)); } //+------------------------------------------------------------------- // // Function: GetOXIDFromObjRef, private // // Synopsis: extracts the OXID from the OBJREF. // // History: 09-Jan-96 Rickhi Created. // //-------------------------------------------------------------------- OXIDEntry *GetOXIDFromObjRef(OBJREF &objref) { // TRICK: Internally we use the saResAddr.size field as the ptr // to the OXIDEntry. See ReadObjRef and FillObjRef. OXIDEntry *pOXIDEntry = (objref.flags & OBJREF_STANDARD) ? *(OXIDEntry **)&ORSTD(objref).saResAddr : *(OXIDEntry **)&ORHDL(objref).saResAddr; Win4Assert(pOXIDEntry); return pOXIDEntry; } //+------------------------------------------------------------------- // // Function: WriteObjRef, private // // Synopsis: Writes the objref into the stream // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- INTERNAL WriteObjRef(IStream *pStm, OBJREF &objref, DWORD dwDestCtx) { ASSERT_LOCK_RELEASED ULONG cbToWrite = (objref.flags & OBJREF_STANDARD) ? (2*sizeof(ULONG)) + sizeof(IID) + sizeof(STDOBJREF) : (2*sizeof(ULONG)) + sizeof(IID) + sizeof(STDOBJREF) + sizeof(CLSID); // write the fixed-sized part of the OBJREF into the stream HRESULT hr = pStm->Write(&objref, cbToWrite, NULL); if (SUCCEEDED(hr)) { // write the resolver address into the stream. // TRICK: Internally we use the saResAddr.size field as the ptr // to the OXIDEntry. See ReadObjRef and FillObjRef. DUALSTRINGARRAY *psa; OXIDEntry *pOXIDEntry = GetOXIDFromObjRef(objref); if (pOXIDEntry->pMIDEntry != gpLocalMIDEntry || dwDestCtx == MSHCTX_DIFFERENTMACHINE) { // the interface is for a remote server, or it is going to a // remote client, therefore, marshal the resolver strings psa = pOXIDEntry->pMIDEntry->Node.psaKey; Win4Assert(psa->wNumEntries != 0); } else { // the interface is for an OXID local to this machine and // the interface is not going to a remote client, marshal an // empty string (we pay attention to this in ReadObjRef) psa = &saNULL; } // These string bindings always come from the object exporter // who has already padded the size to 8 bytes. hr = pStm->Write(psa, SASIZE(psa->wNumEntries), NULL); ComDebOut((DEB_MARSHAL,"WriteObjRef psa:%x\n", psa)); } ASSERT_LOCK_RELEASED return hr; } //+------------------------------------------------------------------- // // Function: ReadObjRef, private // // Synopsis: Reads the objref from the stream // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- INTERNAL ReadObjRef(IStream *pStm, OBJREF &objref) { ASSERT_LOCK_RELEASED // read the signature, flags, and iid fields of the objref so we know // what kind of objref we are dealing with and how big it is. HRESULT hr = StRead(pStm, &objref, 2*sizeof(ULONG) + sizeof(IID)); if (SUCCEEDED(hr)) { if ((objref.signature != OBJREF_SIGNATURE) || (objref.flags & OBJREF_RSRVD_MBZ) || (objref.flags == 0)) { // the objref signature is bad, or one of the reserved // bits in the flags is set, or none of the required bits // in the flags is set. the objref cant be interpreted so // fail the call. Win4Assert(!"Invalid Objref Flags"); return RPC_E_INVALID_OBJREF; } // compute the size of the remainder of the objref and // include the size fields for the resolver string array STDOBJREF *pStd = &ORSTD(objref).std; DUALSTRINGARRAY *psa; ULONG cbToRead; if (objref.flags & OBJREF_STANDARD) { cbToRead = sizeof(STDOBJREF) + sizeof(ULONG); psa = &ORSTD(objref).saResAddr; } else if (objref.flags & OBJREF_HANDLER) { cbToRead = sizeof(STDOBJREF) + sizeof(CLSID) + sizeof(ULONG); psa = &ORHDL(objref).saResAddr; } else if (objref.flags & OBJREF_CUSTOM) { cbToRead = sizeof(CLSID) + 2*sizeof(DWORD); // clsid + cbExtension + size psa = NULL; } // read the rest of the (fixed sized) objref from the stream hr = StRead(pStm, pStd, cbToRead); if (SUCCEEDED(hr)) { if (psa != NULL) { // Non custom interface. Make sure the resolver string array // has some sensible values. if (psa->wNumEntries != 0 && psa->wSecurityOffset >= psa->wNumEntries) { hr = RPC_E_INVALID_OBJREF; } } else { // custom marshaled interface if (ORCST(objref).cbExtension != 0) { // skip past the extensions since we currently dont // know about any extension types. LARGE_INTEGER dlibMove; dlibMove.LowPart = ORCST(objref).cbExtension; dlibMove.HighPart = 0; hr = pStm->Seek(dlibMove, STREAM_SEEK_CUR, NULL); } } } if (SUCCEEDED(hr) && psa) { // Non custom interface. The data that follows is a variable // sized string array. Allocate memory for it and then read it. DbgDumpSTD(pStd); DUALSTRINGARRAY *psaNew; cbToRead = psa->wNumEntries * sizeof(WCHAR); if (cbToRead == 0) { // server must be local to this machine, just get the local // resolver strings and use them to resolve the OXID psaNew = gpsaLocalResolver; } else { // allocate space to read the strings psaNew = (DUALSTRINGARRAY *) _alloca(cbToRead + sizeof(ULONG)); if (psaNew != NULL) { // update the size fields and read in the rest of the data psaNew->wSecurityOffset = psa->wSecurityOffset; psaNew->wNumEntries = psa->wNumEntries; hr = StRead(pStm, psaNew->aStringArray, cbToRead); } else { psa->wNumEntries = 0; psa->wSecurityOffset = 0; hr = E_OUTOFMEMORY; // seek the stream past what we should have read, ignore // seek errors, since the OOM takes precedence. LARGE_INTEGER libMove; libMove.LowPart = cbToRead; libMove.HighPart = 0; pStm->Seek(libMove, STREAM_SEEK_CUR, 0); } } // TRICK: internally we want to keep the ObjRef a fixed size // structure, even though we have variable sized data. To do // this i use the saResAddr.size field of the ObjRef as a ptr // to the OXIDEntry. We pay attention to this in FillObjRef, // WriteObjRef and FreeObjRef. if (SUCCEEDED(hr)) { // resolve the OXID. ASSERT_LOCK_RELEASED LOCK OXIDEntry *pOXIDEntry = NULL; hr = gResolver.ClientResolveOXID(pStd->oxid, psaNew, &pOXIDEntry); UNLOCK ASSERT_LOCK_RELEASED *((void **) psa) = pOXIDEntry; } else { *((void **) psa) = NULL; } } } ComDebOut((DEB_MARSHAL,"ReadObjRef hr:%x objref:%x\n", hr, &objref)); ASSERT_LOCK_RELEASED return hr; } //+------------------------------------------------------------------- // // Function: FreeObjRef, private // // Synopsis: Releases an objref that was read in from a stream via // ReadObjRef. // // History: 20-Feb-95 Rickhi Created. // // Notes: Anybody who calls ReadObjRef should call this guy to // free the objref. This decrements the refcnt on the // embedded pointer to the OXIDEntry. // //-------------------------------------------------------------------- INTERNAL_(void) FreeObjRef(OBJREF &objref) { if (objref.flags & (OBJREF_STANDARD | OBJREF_HANDLER)) { // TRICK: Internally we use the saResAddr.size field as the ptr to // the OXIDEntry. See ReadObjRef, WriteObjRef and FillObjRef. OXIDEntry *pOXIDEntry = GetOXIDFromObjRef(objref); LOCK Win4Assert(pOXIDEntry); DecOXIDRefCnt(pOXIDEntry); UNLOCK } } //+------------------------------------------------------------------- // // Function: MakeFakeObjRef, private // // Synopsis: Invents an OBJREF that can be unmarshaled in this process. // The objref is partially fact (the OXIDEntry) and partially // fiction (the OID). // // History: 16-Jan-96 Rickhi Created. // // Notes: This is used by MakeSCMProxy and GetRemUnk. Note that // the pOXIDEntry is not AddRef'd here because the OBJREF // created is only short-lived the callers guarantee it's // lifetime, so FreeObjRef need not be called. // //-------------------------------------------------------------------- INTERNAL MakeFakeObjRef(OBJREF &objref, OXIDEntry *pOXIDEntry, REFIPID ripid, REFIID riid) { // first, invent an OID since this could fail. STDOBJREF *pStd = &ORSTD(objref).std; HRESULT hr = gResolver.ServerGetReservedID(&pStd->oid); if (SUCCEEDED(hr)) { pStd->flags = SORF_NOPING | SORF_FREETHREADED; pStd->cPublicRefs = 1; pStd->ipid = ripid; OXIDFromMOXID(pOXIDEntry->moxid, &pStd->oxid); // TRICK: Internally we use the saResAddr.size field as the ptr to // the OXIDEntry. See ReadObjRef, WriteObjRef and FillObjRef. OXIDEntry **ppOXIDEntry = (OXIDEntry **) &ORSTD(objref).saResAddr; *ppOXIDEntry = pOXIDEntry; objref.signature = OBJREF_SIGNATURE; objref.flags = OBJREF_STANDARD; objref.iid = riid; } return hr; } //+------------------------------------------------------------------- // // Function: MakeCallableFromAnyApt, private // // Synopsis: set SORF_FREETHREADED in OBJREF so unmarshaled proxy // can be called from any apartment. // // History: 16-Jan-96 Rickhi Created. // //-------------------------------------------------------------------- void MakeCallableFromAnyApt(OBJREF &objref) { STDOBJREF *pStd = &ORSTD(objref).std; pStd->flags |= SORF_FREETHREADED; } //+------------------------------------------------------------------- // // Function: FindStdMarshal, private // // Synopsis: Finds the CStdMarshal for the OID read from the stream // // Arguements: [objref] - object reference // [ppStdMshl] - CStdMarshal returned, AddRef'd // // Algorithm: Read the objref, get the OID. If we already have an identity // for this OID, use that, otherwise either create an identity // object, or create a handler (which in turn will create the // identity). The identity inherits CStdMarshal. // // History: 20-Feb-95 Rickhi Created. // //-------------------------------------------------------------------- INTERNAL FindStdMarshal(OBJREF &objref, CStdMarshal **ppStdMshl) { ComDebOut((DEB_MARSHAL, "FindStdMarshal objref:%x ppStdMshl:%x\n", &objref, ppStdMshl)); HRESULT hr = CO_E_OBJNOTCONNECTED; CStdIdentity *pStdId = NULL; if (ChkIfLocalOID(objref, &pStdId)) { if (pStdId) { hr = S_OK; } else { hr = CO_E_OBJNOTCONNECTED; } } else { STDOBJREF *pStd = &ORSTD(objref).std; ComDebOut((DEB_MARSHAL, "poid: %x\n", &pStd->oid)); ASSERT_LOCK_RELEASED LOCK OXIDEntry *pOXIDEntry = GetOXIDFromObjRef(objref); // OXID is for different apartment, check the identity table for // an existing OID. MOID moid; MOIDFromOIDAndMID(pStd->oid, pOXIDEntry->pMIDEntry->mid, &moid); hr = LookupIDFromID(moid, TRUE, &pStdId); if (FAILED(hr)) { CStdIdentity *pStdIdPrev = NULL; BOOL fDuplicate = FALSE; if (objref.flags & OBJREF_STANDARD) { // create an instance of the identity for this OID. We want // to be holding the lock while we do this since it wont // exercise any app code. hr = CreateIdentityHandler(NULL, pStd->flags, IID_IStdIdentity, (void **)&pStdId); AssertOutPtrIface(hr, pStdId); if (SUCCEEDED(hr)) { // set the identity while holding the lock. The result is // checked below and we release if this fails. hr = pStdId->SetOID(moid); Win4Assert(pStdIdPrev == NULL); } } else { // create an instance of the handler. the handler will // aggregate in the identity, but will pass GUID_NULL for // the OID so that the identity is not set in the table yet. Win4Assert(!(ORHDL(objref).std.flags & SORF_FREETHREADED)); // dont want to hold the lock while creating the handler // since this involves running app code and calling the // SCM etc. UNLOCK ASSERT_LOCK_RELEASED hr = CoCreateInstance(ORHDL(objref).clsid, NULL, CLSCTX_INPROC_HANDLER, IID_IStdIdentity, (void **)&pStdId); AssertOutPtrIface(hr, pStdId); ASSERT_LOCK_RELEASED LOCK // look for the OID in the table again, since it may have // been added while we released the lock to create the // handler. if (SUCCEEDED(LookupIDFromID(moid, TRUE, &pStdIdPrev))) { // object was unmarshaled while we released the lock // to create the handler, so we will use the existing one. // since we are releasing app code, we need to release the // lock. fDuplicate = TRUE; } else if (SUCCEEDED(hr)) { // set the OID now while we are holding the lock. hr = pStdId->SetOID(moid); Win4Assert(pStdIdPrev == NULL); } } if (pStdId && (FAILED(hr) || fDuplicate)) { Win4Assert( (FAILED(hr) && (pStdIdPrev == NULL)) || (fDuplicate && (pStdIdPrev != NULL)) ); UNLOCK ASSERT_LOCK_RELEASED pStdId->Release(); pStdId = pStdIdPrev; ASSERT_LOCK_RELEASED LOCK } } UNLOCK ASSERT_LOCK_RELEASED } *ppStdMshl = (CStdMarshal *)pStdId; AssertOutPtrIface(hr, *ppStdMshl); ComDebOut((DEB_MARSHAL, "FindStdMarshal pStdMshl:%x hr:%x\n", *ppStdMshl, hr)); return hr; } //+------------------------------------------------------------------------ // // Function: CompleteObjRef, public // // Synopsis: Fills in the missing fields of an OBJREF from a STDOBJREF // and resolves the OXID. Also sets fLocal to TRUE if the // object was marshaled in this apartment. // // History: 22-Jan-96 Rickhi Created // //------------------------------------------------------------------------- HRESULT CompleteObjRef(OBJREF &objref, OXID_INFO &oxidInfo, REFIID riid, BOOL *pfLocal) { // tweak the objref so we can call ReleaseMarshalObjRef or UnmarshalObjRef objref.signature = OBJREF_SIGNATURE; objref.flags = OBJREF_STANDARD; objref.iid = riid; HRESULT hr = InitChannelIfNecessary(); if (FAILED(hr)) return hr; ASSERT_LOCK_RELEASED LOCK OXIDEntry *pOXIDEntry = NULL; MIDEntry *pMIDEntry; hr = GetLocalMIDEntry(&pMIDEntry); if (SUCCEEDED(hr)) { hr = FindOrCreateOXIDEntry(ORSTD(objref).std.oxid, oxidInfo, FOCOXID_NOREF, gpsaLocalResolver, gLocalMid, pMIDEntry, &pOXIDEntry); } if (SUCCEEDED(hr)) { OXIDEntry **ppOXIDEntry = (OXIDEntry **) &ORSTD(objref).saResAddr; *ppOXIDEntry = pOXIDEntry; *pfLocal = (pOXIDEntry == GetLocalOXIDEntry()); } UNLOCK ASSERT_LOCK_RELEASED return hr; }