Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1502 lines
47 KiB

//+-----------------------------------------------------------------------
//
// File: ipidtbl.cxx
//
// Contents: IPID (interface pointer identifier) table.
//
// Classes: CIPIDTable
//
// History: 02-Feb-95 Rickhi Created
//
// Notes: All synchronization is the responsibility of the caller.
//
//-------------------------------------------------------------------------
#include <ole2int.h>
#include <ipidtbl.hxx> // CIPIDTable
#include <resolver.hxx> // CRpcResolver
#include <service.hxx> // SASIZE
#include <remoteu.hxx> // CRemoteUnknown
#include <marshal.hxx> // UnmarshalObjRef
#include <idtable.hxx> // LookupIDFromUnk
#include <callctrl.hxx> // OleModalLoopBlockFn
// global tables
CMIDTable gMIDTbl; // machine ID table
COXIDTable gOXIDTbl; // object exported ID table
CIPIDTable gIPIDTbl; // interface pointer ID table
MIDEntry *gpLocalMIDEntry = NULL; // local machine MIDEntry
OXIDEntry *gpMTAOXIDEntry = NULL; // MTA OXIDEntry
DUALSTRINGARRAY *gpsaLocalResolver = NULL; // local OXIDResolver address
OXIDEntry COXIDTable::_InUseHead = { &_InUseHead, &_InUseHead };
OXIDEntry COXIDTable::_CleanupHead = { &_CleanupHead, &_CleanupHead };
OXIDEntry COXIDTable::_ExpireHead = { &_ExpireHead, &_ExpireHead };
DWORD COXIDTable::_cExpired = 0;
CStringHashTable CMIDTable::_HashTbl; // hash table for MIDEntries
CPageAllocator CMIDTable::_palloc; // allocator for MIDEntries
CPageAllocator COXIDTable::_palloc; // allocator for OXIDEntries
CPageAllocator CIPIDTable::_palloc; // allocator for IPIDEntries
//+------------------------------------------------------------------------
//
// Machine Identifier hash table buckets. This is defined as a global
// so that we dont have to run any code to initialize the hash table.
//
//+------------------------------------------------------------------------
SHashChain MIDBuckets[23] = {
{&MIDBuckets[0], &MIDBuckets[0]},
{&MIDBuckets[1], &MIDBuckets[1]},
{&MIDBuckets[2], &MIDBuckets[2]},
{&MIDBuckets[3], &MIDBuckets[3]},
{&MIDBuckets[4], &MIDBuckets[4]},
{&MIDBuckets[5], &MIDBuckets[5]},
{&MIDBuckets[6], &MIDBuckets[6]},
{&MIDBuckets[7], &MIDBuckets[7]},
{&MIDBuckets[8], &MIDBuckets[8]},
{&MIDBuckets[9], &MIDBuckets[9]},
{&MIDBuckets[10], &MIDBuckets[10]},
{&MIDBuckets[11], &MIDBuckets[11]},
{&MIDBuckets[12], &MIDBuckets[12]},
{&MIDBuckets[13], &MIDBuckets[13]},
{&MIDBuckets[14], &MIDBuckets[14]},
{&MIDBuckets[15], &MIDBuckets[15]},
{&MIDBuckets[16], &MIDBuckets[16]},
{&MIDBuckets[17], &MIDBuckets[17]},
{&MIDBuckets[18], &MIDBuckets[18]},
{&MIDBuckets[19], &MIDBuckets[19]},
{&MIDBuckets[20], &MIDBuckets[20]},
{&MIDBuckets[21], &MIDBuckets[21]},
{&MIDBuckets[22], &MIDBuckets[22]}
};
//+------------------------------------------------------------------------
//
// Member: CIPIDTbl::Initialize, public
//
// Synopsis: Initializes the IPID table.
//
// History: 02-Feb-96 Rickhi Created
//
//-------------------------------------------------------------------------
void CIPIDTable::Initialize()
{
ComDebOut((DEB_OXID, "CIPIDTable::Initialize\n"));
_palloc.Initialize(sizeof(IPIDEntry), IPIDS_PER_PAGE);
}
//+------------------------------------------------------------------------
//
// Member: CIPIDTbl::Cleanup, public
//
// Synopsis: Cleanup the ipid table.
//
// History: 02-Feb-95 Rickhi Created
//
//-------------------------------------------------------------------------
void CIPIDTable::Cleanup()
{
ComDebOut((DEB_OXID, "CIPIDTable::Cleanup\n"));
_palloc.Cleanup();
}
//+------------------------------------------------------------------------
//
// Member: CIPIDTbl::LookupIPID, public
//
// Synopsis: Finds an entry in the IPID table with the given IPID.
// This is used by the unmarshalling code, the dispatch
// code, and CRemoteUnknown.
//
// Notes: This method should be called instead of GetEntryPtr
// whenever you dont know if the IPID is valid or not (eg it
// came in off the network), since this validates the IPID
// index to ensure its within the table size, as well as
// validating the rest of the IPID.
//
// History: 02-Feb-95 Rickhi Created
//
//-------------------------------------------------------------------------
IPIDEntry *CIPIDTable::LookupIPID(REFIPID ripid)
{
ASSERT_LOCK_HELD
// Validate the IPID index that is passed in, since this came in off
// off the net it could be bogus and we dont want to fault on it.
// first dword of the ipid is the index into the ipid table.
if (_palloc.IsValidIndex(ripid.Data1))
{
IPIDEntry *pIPIDEntry = GetEntryPtr(ripid.Data1);
// entry must be server side and not vacant
if ((pIPIDEntry->dwFlags & (IPIDF_SERVERENTRY | IPIDF_VACANT)) ==
IPIDF_SERVERENTRY)
{
// validate the rest of the guid
if (InlineIsEqualGUID(pIPIDEntry->ipid, ripid))
return pIPIDEntry;
}
}
return NULL;
}
//+-------------------------------------------------------------------
//
// Member: CIPIDTable::ReleaseEntryList
//
// Synopsis: return a linked list of IPIDEntry to the table's free list
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
void CIPIDTable::ReleaseEntryList(IPIDEntry *pFirst, IPIDEntry *pLast)
{
ASSERT_LOCK_HELD
Win4Assert(pLast->pNextOID == NULL);
#if DBG==1
// In debug, walk the list to ensure they are released, vacant,
// disconnected etc.
IPIDEntry *pEntry = pFirst;
while (pEntry != NULL)
{
Win4Assert(pEntry->pOXIDEntry == NULL); // must already be released
Win4Assert(pEntry->dwFlags & IPIDF_VACANT);
Win4Assert(pEntry->dwFlags & IPIDF_DISCONNECTED);
pEntry = pEntry->pNextOID;
}
#endif
_palloc.ReleaseEntryList((PageEntry *)pFirst, (PageEntry *)pLast);
}
#if DBG==1
//+-------------------------------------------------------------------
//
// Member: CIPIDTable::ValidateIPIDEntry
//
// Synopsis: Ensures the IPIDEntry is valid.
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
void CIPIDTable::ValidateIPIDEntry(IPIDEntry *pEntry, BOOL fServerSide,
CRpcChannelBuffer *pChnl)
{
// validate the IPID flags
Win4Assert(!(pEntry->dwFlags & IPIDF_VACANT));
if (fServerSide)
{
// server side must have SERVERENTRY ipids
Win4Assert(pEntry->dwFlags & IPIDF_SERVERENTRY);
}
else
{
// client side must not have SERVERENTRY ipids
Win4Assert(!(pEntry->dwFlags & IPIDF_SERVERENTRY));
}
// Validate the pStub interface
if (IsEqualIID(pEntry->iid, IID_IUnknown))
{
// there is no proxy or stub for IUnknown interface
Win4Assert(pEntry->pStub == NULL);
}
else
{
if ((pEntry->dwFlags & IPIDF_DISCONNECTED) &&
(pEntry->dwFlags & IPIDF_SERVERENTRY))
{
// disconnected server side has NULL pStub
Win4Assert(pEntry->pStub == NULL);
}
else
{
// both connected and disconnected client side has valid proxy
Win4Assert(pEntry->pStub != NULL);
Win4Assert(IsValidInterface(pEntry->pStub));
}
}
// Validate the interface pointer (pv)
if (!(pEntry->dwFlags & IPIDF_DISCONNECTED))
{
Win4Assert(pEntry->pv != NULL);
Win4Assert(IsValidInterface(pEntry->pv));
}
// Validate the channel ptr
if (fServerSide)
{
// all stubs share the same channel on the server side
Win4Assert(pEntry->pChnl == pChnl);
}
else
{
// all proxies have their own different channel on client side
Win4Assert(pEntry->pChnl != pChnl || pEntry->pChnl == NULL);
}
// Validate the RefCnts
if (!(pEntry->dwFlags & IPIDF_DISCONNECTED) && !fServerSide)
{
// if connected, must be > 0 refcnt on client side.
// potentially not > 0 if TABLE marshal on server side.
Win4Assert(pEntry->cStrongRefs + pEntry->cWeakRefs > 0);
}
// Validate the OXIDEntry
if (pEntry->pOXIDEntry)
{
OXIDEntry *pOX = pEntry->pOXIDEntry;
if (fServerSide)
{
// check OXID tid and pid
Win4Assert(pOX->dwPid == GetCurrentProcessId());
if ((pOX->dwFlags & OXIDF_MTASERVER))
Win4Assert(pOX->dwTid == 0);
else
Win4Assert(pOX->dwTid == GetCurrentThreadId());
if (pChnl != NULL)
{
// CODEWORK: ensure OXID is same as the rest of the object
// Win4Assert(IsEqualGUID(pOX->moxid, GetMOXID()));
}
}
}
// Validate the pNextOID
if (pEntry->pNextOID != NULL)
{
// ensure it is within the bounds of the table
Win4Assert(GetEntryIndex(pEntry) != -1);
// cant point back to self or we have a circular list
Win4Assert(pEntry->pNextOID != pEntry);
}
}
#endif
//+------------------------------------------------------------------------
//
// Member: COXIDTbl::Initialize, public
//
// Synopsis:
//
// History: 02-Feb-96 Rickhi Created
//
//-------------------------------------------------------------------------
void COXIDTable::Initialize()
{
ComDebOut((DEB_OXID, "COXIDTable::Initialize\n"));
_palloc.Initialize(sizeof(OXIDEntry), OXIDS_PER_PAGE);
}
//+------------------------------------------------------------------------
//
// Member: COXIDTbl::Cleanup, public
//
// Synopsis: Cleanup the OXID table.
//
// History: 02-Feb-95 Rickhi Created
//
//-------------------------------------------------------------------------
void COXIDTable::Cleanup()
{
ComDebOut((DEB_OXID, "COXIDTable::Cleanup\n"));
ASSERT_LOCK_HELD
// the lists better be empty before we delete the entries
AssertListsEmpty();
_palloc.Cleanup();
}
//+------------------------------------------------------------------------
//
// Member: COXIDTbl::AddEntry, public
//
// Synopsis: Adds an entry to the OXID table. The entry is AddRef'd.
//
// History: 02-Feb-95 Rickhi Created
//
//-------------------------------------------------------------------------
HRESULT COXIDTable::AddEntry(REFOXID roxid, OXID_INFO *poxidInfo,
MIDEntry *pMIDEntry, OXIDEntry **ppEntry)
{
Win4Assert(poxidInfo != NULL);
Win4Assert(pMIDEntry != NULL);
ASSERT_LOCK_HELD
// find first free entry slot, grow table if necessary
OXIDEntry *pEntry = (OXIDEntry *) _palloc.AllocEntry();
if (pEntry == NULL)
{
ComDebOut((DEB_ERROR,"Out Of Memory in COXIDTable::AddEntry\n"));
return E_OUTOFMEMORY;
}
// chain it on the list of inuse entries
pEntry->pPrev = &_InUseHead;
_InUseHead.pNext->pPrev = pEntry;
pEntry->pNext = _InUseHead.pNext;
_InUseHead.pNext = pEntry;
// Copy oxidInfo into OXIDEntry.
MOXIDFromOXIDAndMID(roxid, pMIDEntry->mid, &pEntry->moxid);
pEntry->cRefs = 1; // caller gets one reference
pEntry->cWaiters = 0;
pEntry->dwPid = poxidInfo->dwPid;
pEntry->dwTid = poxidInfo->dwTid;
pEntry->dwFlags = (poxidInfo->dwPid == 0) ? 0 : OXIDF_MACHINE_LOCAL;
pEntry->dwFlags |= (poxidInfo->dwTid != 0) ? 0 : OXIDF_MTASERVER;
pEntry->pRUSTA = NULL;
pEntry->pRUMTA = NULL;
pEntry->ipidRundown = poxidInfo->ipidRemUnknown;
pEntry->hServerSTA = NULL;
pEntry->hServerMTA = NULL;
pEntry->pMIDEntry = pMIDEntry;
pEntry->hComplete = NULL;
pEntry->cCalls = 0;
pEntry->cResolverRef = 0;
IncMIDRefCnt(pMIDEntry);
HRESULT hr = S_OK;
if (poxidInfo->dwPid != GetCurrentProcessId())
{
// This OXID is for an apartment outside the current process. We
// need to make an RPC binding handle from the supplied strings.
Win4Assert(poxidInfo->psa != NULL &&
poxidInfo->psa->aStringArray[0] != 0);
// Set the MSWMSG flag if the transport is MSWMSG.
RPC_STATUS sc = CheckClientMswmsg(poxidInfo->psa->aStringArray,
&pEntry->dwFlags);
// Make a binding handle from the string bindings.
if (sc == RPC_S_OK)
{
sc = RpcBindingFromStringBinding(poxidInfo->psa->aStringArray,
&pEntry->hServerSTA);
}
// Pass our blocking function to MSWMSG. When we make calls out,
// MSWMSG will call the blocking function.
if (sc == RPC_S_OK && (pEntry->dwFlags & OXIDF_MSWMSG))
{
sc = I_RpcBindingSetAsync(pEntry->hServerSTA, OleModalLoopBlockFn);
}
// Set security on the binding handle if necessary.
if (sc == RPC_S_OK)
{
hr = SetAuthnService( pEntry->hServerSTA, poxidInfo, pEntry );
}
else
{
hr = HRESULT_FROM_WIN32(sc);
}
}
// Get a shutdown event for server side MTAs. Don't use the event
// cache because the event isn't always reset.
else if (pEntry->dwFlags & OXIDF_MTASERVER)
{
#ifdef _CHICAGO_
pEntry->hComplete = CreateEventA( NULL, FALSE, FALSE, NULL );
#else //_CHICAGO_
pEntry->hComplete = CreateEvent( NULL, FALSE, FALSE, NULL );
#endif //_CHICAGO_
if (pEntry->hComplete == NULL)
hr = RPC_E_OUT_OF_RESOURCES;
}
if (FAILED(hr))
{
// failed, release the OXIDEntry
DecOXIDRefCnt(pEntry);
pEntry = NULL;
}
*ppEntry = pEntry;
gOXIDTbl.ValidateOXID();
ComDebOut((DEB_OXID,"COXIDTable::AddEntry pEntry:%x moxid:%I\n",
pEntry, (pEntry) ? &pEntry->moxid : &GUID_NULL));
return hr;
}
//+------------------------------------------------------------------------
//
// Member: COXIDTbl::LookupOXID, public
//
// Synopsis: finds an entry in the OXID table with the given OXID.
// This is used by the unmarshalling code. The returned
// entry has been AddRef'd.
//
// History: 02-Feb-95 Rickhi Created
//
// PERFWORK: we could move the OXIDEntry to the head of the InUse list on
// the assumption that it will be the most frequently used item
// in the near future.
//
//-------------------------------------------------------------------------
OXIDEntry *COXIDTable::LookupOXID(REFOXID roxid, REFMID rmid)
{
ASSERT_LOCK_HELD
MOXID moxid;
MOXIDFromOXIDAndMID(roxid, rmid, &moxid);
// first, search the InUse list.
OXIDEntry *pEntry = SearchList(moxid, &_InUseHead);
if (pEntry == NULL)
{
// not found on InUse list, search the Expire list.
if ((pEntry = SearchList(moxid, &_ExpireHead)) != NULL)
{
// found it, unchain it from the list of Expire entries
pEntry->pPrev->pNext = pEntry->pNext;
pEntry->pNext->pPrev = pEntry->pPrev;
// chain it on the list of InUse entries
pEntry->pPrev = &_InUseHead;
_InUseHead.pNext->pPrev = pEntry;
pEntry->pNext = _InUseHead.pNext;
_InUseHead.pNext = pEntry;
// reset the cRefs field (which was overloaded with the
// expire time by ReleaseEntry), and count one less entry.
pEntry->cRefs = 1;
_cExpired--;
}
}
ComDebOut((DEB_OXID,"COXIDTable::LookupOXID pEntry:%x moxid:%I\n",
pEntry, &moxid));
gOXIDTbl.ValidateOXID();
return pEntry;
}
//+------------------------------------------------------------------------
//
// Member: COXIDTbl::SearchList, private
//
// Synopsis: Searches the specified list for a matching OXID entry.
// This is a subroutine of LookupOXID.
//
// History: 25-Aug-95 Rickhi Created
//
//-------------------------------------------------------------------------
OXIDEntry *COXIDTable::SearchList(REFMOXID rmoxid, OXIDEntry *pStart)
{
ASSERT_LOCK_HELD
OXIDEntry *pEntry = pStart->pNext;
while (pEntry != pStart)
{
if (InlineIsEqualGUID(rmoxid, pEntry->moxid))
{
IncOXIDRefCnt(pEntry);
return pEntry; // found a match, return it
}
pEntry = pEntry->pNext; // try next one in use
}
return NULL;
}
//+------------------------------------------------------------------------
//
// Member: COXIDTbl::ReleaseEntry, public
//
// Synopsis: removes an entry from the OXID table InUse list and
// places it on the Expire list. Entries on the Expire list
// will be cleaned up by a worker thread at a later time, or
// placed back on the InUse list by LookupOXID.
//
// History: 02-Feb-95 Rickhi Created
//
//-------------------------------------------------------------------------
void COXIDTable::ReleaseEntry(OXIDEntry *pEntry)
{
Win4Assert(pEntry);
Win4Assert(pEntry->cRefs == 0); // must be no users of this entry
gOXIDTbl.ValidateOXID();
ASSERT_LOCK_HELD
if (pEntry->dwFlags & OXIDF_PENDINGRELEASE)
{
return; // already being deleted, just ignore.
}
// unchain it from the list of InUse entries
pEntry->pPrev->pNext = pEntry->pNext;
pEntry->pNext->pPrev = pEntry->pPrev;
// chain it on the *END* of the list of Expire entries, and
// count one more expired entry.
pEntry->pPrev = _ExpireHead.pPrev;
pEntry->pNext = &_ExpireHead;
_ExpireHead.pPrev->pNext= pEntry;
_ExpireHead.pPrev = pEntry;
_cExpired++;
// set the time when it was placed on the Expire list. This (may be)
// used to determine when this entry should really expire.
pEntry->cRefs = GetTickCount();
// Free anything hanging around on the cleanup list. This may release
// the lock.
FreeCleanupEntries();
ASSERT_LOCK_HELD
ComDebOut((DEB_OXID,"COXIDTable::ReleaseEntry pEntry:%x\n", pEntry));
}
//+------------------------------------------------------------------------
//
// Member: COXIDTbl::FreeExpiredEntries, public
//
// Synopsis: Walks the Expire list and deletes the OXIDEntries that
// were placed on the expire list before the given time.
//
// History: 02-Feb-95 Rickhi Created
//
//-------------------------------------------------------------------------
void COXIDTable::FreeExpiredEntries(DWORD dwTime)
{
ASSERT_LOCK_HELD
while (_ExpireHead.pNext != &_ExpireHead)
{
#if 0
// CODEWORK: currently we never use the ExpireTime function,
// we only call this routine from ChannelProcessUninit, so ignore
// the expire time and release all the entries.
// there is an entry on the list. check its time stamp (which
// was placed in the cRefs field)
if ((DWORD)_ExpireHead.pNext->cRefs - dwTime > 0)
{
// this entry has not yet expired. All entries after this
// one must not have expired either, so exit early.
break;
}
#endif
// unchain it from the list of Expire entries, and count one less
// expired entry.
OXIDEntry *pEntry = _ExpireHead.pNext;
pEntry->pPrev->pNext = pEntry->pNext;
pEntry->pNext->pPrev = pEntry->pPrev;
_cExpired--;
ExpireEntry(pEntry);
}
// The worker thread moves entries to the cleanup list while holding the
// lock. Since the expire list is now empty no more OXIDs can be added
// to the cleanup list. Now would be a good time to free items on the
// cleanup list.
FreeCleanupEntries();
AssertListsEmpty(); // the lists better be empty now
ComDebOut((DEB_OXID, "COXIDTable::FreeExpiredEntries dwTime:%x\n", dwTime));
}
//+------------------------------------------------------------------------
//
// Member: COXIDTbl::FreeCleanupEntries, public
//
// Synopsis: Deletes all OXID entries on the Cleanup list.
//
// History: 02-Feb-95 Rickhi Created
//
//-------------------------------------------------------------------------
void COXIDTable::FreeCleanupEntries()
{
ASSERT_LOCK_HELD
while (_CleanupHead.pNext != &_CleanupHead)
{
// Unchain the entries and free all resources it holds.
OXIDEntry *pEntry = _CleanupHead.pNext;
_CleanupHead.pNext = pEntry->pNext;
ExpireEntry(pEntry);
}
ComDebOut((DEB_OXID, "COXIDTable::FreeCleanupEntries\n"));
}
//+------------------------------------------------------------------------
//
// Member: COXIDTable::NumOxidsToRemove
//
// Synopsis: Returns the number of OXIDs on the expired list that can be
// freed.
//
// History: 03-Jun-96 AlexMit Created
//
//-------------------------------------------------------------------------
DWORD COXIDTable::NumOxidsToRemove()
{
ASSERT_LOCK_HELD
// Compute how many extra OXIDs are on the expired list.
if (_cExpired > OXIDTBL_MAXEXPIRED)
return _cExpired - OXIDTBL_MAXEXPIRED;
else
return 0;
}
//+------------------------------------------------------------------------
//
// Member: COXIDTable::GetOxidsToRemove
//
// Synopsis: Builds a list of OXIDs old enough to be deleted. Removes
// them from the expired list and puts them on the cleanup list.
// Moves machine local OXIDs directly to the cleanup list.
//
// History: 03-Jun-42 AlexMit Created
//
//-------------------------------------------------------------------------
void COXIDTable::GetOxidsToRemove( OXID_REF *pRef, DWORD *pNum )
{
OXIDEntry *pEntry;
ASSERT_LOCK_HELD
// Expire entries until the expired list is short enough.
*pNum = 0;
while (_cExpired > OXIDTBL_MAXEXPIRED)
{
// Only count machine remote OXIDs.
pEntry = _ExpireHead.pNext;
if ((pEntry->dwFlags & OXIDF_MACHINE_LOCAL) == 0)
{
// Add the OXID to the list to deregister.
MIDFromMOXID( pEntry->moxid, &pRef->mid );
OXIDFromMOXID( pEntry->moxid, &pRef->oxid );
pRef->refs = pEntry->cResolverRef;
pRef++;
*pNum += 1;
}
// Remove the OXID from the expired list and put it on a list
// of OXIDs to be released by some apartment thread.
_cExpired--;
pEntry->pPrev->pNext = pEntry->pNext;
pEntry->pNext->pPrev = pEntry->pPrev;
pEntry->pNext = _CleanupHead.pNext;
_CleanupHead.pNext = pEntry;
}
}
//+------------------------------------------------------------------------
//
// Member: COXIDTbl::ExpireEntry, private
//
// Synopsis: deletes all state associated with an OXIDEntry that has
// been expired.
//
// History: 02-Feb-95 Rickhi Created
//
//-------------------------------------------------------------------------
void COXIDTable::ExpireEntry(OXIDEntry *pEntry)
{
ComDebOut((DEB_OXID, "COXIDTable::ExpireEntry pEntry:%x\n", pEntry));
Win4Assert(pEntry);
Win4Assert(!(pEntry->dwFlags & OXIDF_PENDINGRELEASE));
ASSERT_LOCK_HELD
if (pEntry->pRUSTA || pEntry->pRUMTA)
{
// release the IRemUnknown. Note that the IRemUnk is an object
// proxy who's IPIDEntry holds a reference back to the very
// OXIDEntry we are releasing. In order to prevent recursive
// Release's we set a simple flag here and check for it above.
pEntry->dwFlags |= OXIDF_PENDINGRELEASE;
UNLOCK
ASSERT_LOCK_RELEASED
if (pEntry->pRUSTA)
{
pEntry->pRUSTA->Release();
}
if (pEntry->pRUMTA)
{
pEntry->pRUMTA->Release();
}
ASSERT_LOCK_RELEASED
LOCK
}
if (pEntry->hServerSTA != NULL)
{
// Note that if hServerSTA is an HWND (apartment model, same process)
// then it should have been cleaned up already in ThreadStop. We
// just assert that here.
Win4Assert(pEntry->dwPid != GetCurrentProcessId());
// hServerSTA is an RPC binding handle. Free it.
RPC_STATUS sc = RpcBindingFree(&pEntry->hServerSTA);
ComDebErr(sc != RPC_S_OK, "RpcBindingFree failed.\n");
}
if (pEntry->hServerMTA != NULL)
{
// hServerMTA is an RPC binding handle. Free it.
Win4Assert(pEntry->dwPid != GetCurrentProcessId());
RPC_STATUS sc = RpcBindingFree(&pEntry->hServerMTA);
ComDebErr(sc != RPC_S_OK, "RpcBindingFree failed.\n");
}
// dec the refcnt on the MIDEntry
DecMIDRefCnt(pEntry->pMIDEntry);
// Release the call shutdown event.
if (pEntry->hComplete != NULL)
CloseHandle( pEntry->hComplete );
// zero out the fields
memset(pEntry, 0, sizeof(OXIDEntry));
// return it to the allocator
_palloc.ReleaseEntry((PageEntry *)pEntry);
ComDebOut((DEB_OXID,"COXIDTable::ExpireEntry pEntry:%x\n", pEntry));
}
//+------------------------------------------------------------------------
//
// Function: COXIDTbl::DecOXIDRefCnt, public
//
// Synopsis: release one reference to the OXIDEntry and release
// the entry if the count goes to zero.
//
// History: 02-Feb-95 Rickhi Created
//
//-------------------------------------------------------------------------
void DecOXIDRefCnt(OXIDEntry *pEntry)
{
Win4Assert(pEntry);
ASSERT_LOCK_HELD
ComDebOut((DEB_OXID,
"DecOXIDRefCnt pEntry:%x cRefs[%x]\n", pEntry, pEntry->cRefs-1));
pEntry->cRefs--;
if (pEntry->cRefs == 0)
{
gOXIDTbl.ReleaseEntry(pEntry);
}
}
//+-------------------------------------------------------------------
//
// Member: COXIDTable::GetRemUnk, public
//
// Synopsis: Find or create the proxy for the IRemUnknown for the
// specified OXID
//
// History: 27-Mar-95 AlexMit Created
//
//--------------------------------------------------------------------
HRESULT COXIDTable::GetRemUnk(OXIDEntry *pOXIDEntry, IRemUnknown **ppRemUnk)
{
ComDebOut((DEB_OXID, "COXIDTable::GetRemUnk pOXIDEntry:%x ppRemUnk:%x\n",
pOXIDEntry, ppRemUnk));
ASSERT_LOCK_HELD
HRESULT hr = S_OK;
if (IsMTAThread())
{
// return the MTA version of the IRemUnknown proxy.
if (pOXIDEntry->pRUMTA == NULL)
{
hr = MakeRemUnk(pOXIDEntry);
}
*ppRemUnk = pOXIDEntry->pRUMTA;
}
else
{
// return the STA version of the IRemUnknown proxy.
if (pOXIDEntry->pRUSTA == NULL)
{
hr = MakeRemUnk(pOXIDEntry);
}
*ppRemUnk = pOXIDEntry->pRUSTA;
}
ComDebOut((DEB_OXID, "COXIDTable::GetRemUnk pOXIDEntry:%x pRU:%x hr:%x\n",
pOXIDEntry, *ppRemUnk, hr));
return hr;
}
//+-------------------------------------------------------------------
//
// Member: COXIDTable::MakeRemUnk, private
//
// Synopsis: Create the proxy for the IRemUnknown for the
// specified OXID and current apartments threading model.
//
// History: 27-Mar-95 AlexMit Created
//
//--------------------------------------------------------------------
HRESULT COXIDTable::MakeRemUnk(OXIDEntry *pOXIDEntry)
{
// There is no remote unknown proxy for this entry, get one.
// Make up an objref, then unmarshal it to create a proxy to
// the remunk object in the server.
// on the same machine, we ask for the IRundown interface since we may
// need the RemChangeRef method. IRundown inherits from IRemUnknown2
// and IRemUnknown.
REFIID riid = (pOXIDEntry->dwFlags & OXIDF_MACHINE_LOCAL)
? IID_IRundown : IID_IRemUnknown;
OBJREF objref;
HRESULT hr = MakeFakeObjRef(objref, pOXIDEntry, pOXIDEntry->ipidRundown, riid);
UNLOCK
ASSERT_LOCK_RELEASED
IRemUnknown *pRU = NULL;
if (SUCCEEDED(hr))
{
hr = UnmarshalInternalObjRef(objref, (void **)&pRU);
}
ASSERT_LOCK_RELEASED
LOCK
if (SUCCEEDED(hr) && IsMTAThread() && pOXIDEntry->pRUMTA == NULL)
{
pOXIDEntry->pRUMTA = pRU;
// need to adjust the internal refcnt on the OXIDEntry, since
// the IRemUnknown has an IPID that holds a reference to it.
// Dont use DecOXIDRefCnt since that would delete if it was 0.
Win4Assert(pOXIDEntry->cRefs > 0);
pOXIDEntry->cRefs--;
}
else if (SUCCEEDED(hr) && IsSTAThread() && pOXIDEntry->pRUSTA == NULL)
{
pOXIDEntry->pRUSTA = pRU;
// need to adjust the internal refcnt on the OXIDEntry, since
// the IRemUnknown has an IPID that holds a reference to it.
// Dont use DecOXIDRefCnt since that would delete if it was 0.
Win4Assert(pOXIDEntry->cRefs > 0);
pOXIDEntry->cRefs--;
}
else if (pRU)
{
// either setting of the security failed OR, we released the
// lock and when we took the lock again some other thread had already
// created the proxy. In either case we just release the one we made.
UNLOCK
ASSERT_LOCK_RELEASED
pRU->Release();
ASSERT_LOCK_RELEASED
LOCK
}
ComDebOut((DEB_OXID, "COXIDTable::GetRemUnk pOXIDEntry:%x pRU:%x hr:%x\n",
pOXIDEntry, pRU, hr));
return hr;
}
//+------------------------------------------------------------------------
//
// Member: COXIDTbl::GetLocalEntry, public
//
// Synopsis: Finds an entry in the OXID table for the local apartment.
// If no entry exists, it creates an entry, and starts RPC
// listening if appropriate.
//
// History: 20-Feb-95 Rickhi Created
//
// Notes: Marshalling the remote unknown causes recursion back to
// this function. The recursion is terminated because
// GetLocalOXIDEntry is not NULL on the second call.
//
//-------------------------------------------------------------------------
HRESULT COXIDTable::GetLocalEntry(OXIDEntry **ppEntry)
{
ComDebOut((DEB_OXID, "COXIDTable::GetLocalEntry ppEntry:%x\n", ppEntry));
ASSERT_LOCK_HELD
HRESULT hr = S_OK;
MIDEntry *pMIDEntry;
*ppEntry = GetLocalOXIDEntry();
if (*ppEntry == NULL && SUCCEEDED(hr = GetLocalMIDEntry(&pMIDEntry)))
{
// No local OXID entry exists, make one.
// NOTE: Chicken And Egg Problem.
//
// Marshaling needs the local OXIDEntry. The local OXIDEntry needs
// the local OXID. To get the local OXID we have to call the resolver.
// To call the resolver we need the IPID for IRemUnknown. To get the
// IPID for IRemUnknown, we need to marshal CRemoteUnknown!
//
// To get around this problem, we create a local OXIDEntry (that has
// a 0 OXID and NULL ipidRemUnknown) so that marshaling can find it.
// Then we marshal the RemoteUnknown and extract its IPID value, stick
// it in the local OXIDEntry. When we call the resolver (to get some
// pre-registered OIDs) we get the real OXID value which we then stuff
// in the local OXIDEntry.
OXID_INFO oxidInfo;
oxidInfo.dwTid = (IsMTAThread()) ? 0 : GetCurrentThreadId();
oxidInfo.dwPid = GetCurrentProcessId();
oxidInfo.ipidRemUnknown = GUID_NULL;
oxidInfo.psa = NULL;
oxidInfo.dwAuthnHint = RPC_C_AUTHN_LEVEL_NONE;
// NOTE: temp creation of OXID. We dont know the real OXID until
// we call the resolver. So, we use 0 temporarily (it wont conflict
// with any other MOXIDs we might be searching for because we already
// have the real MID and our local resolver wont give out a 0 OXID).
// The OXID will be replaced with the real one when we register
// with the resolver in CRpcResolver::ServerAllocateOXIDAndOIDs.
OXID oxid;
memset(&oxid, 0, sizeof(oxid));
hr = AddEntry(oxid, &oxidInfo, pMIDEntry, ppEntry);
if (SUCCEEDED(hr))
{
// Set the local OXID index and marshal IRemUnknown. Note
// that the index must be set before we construct the
// CRemoteUnknown since that calls MarshalObjRef which
// recurses back into GetLocalEntry. Setting the LocalOXID
// now allows us to break the recursion.
SetLocalOXIDEntry(*ppEntry);
// Create the remote unknown for this apartment. It places
// itself in TLS or in the global gpMTARemoteUnknown.
hr = E_OUTOFMEMORY; // assume OOM
CRemoteUnknown *pRemUnk = new CRemoteUnknown(hr,
&(*ppEntry)->ipidRundown);
if (FAILED(hr))
{
// remove the Local OXID entry. This will also clean up
// pRemUnk if the allocation succeeded but ctor failed.
if (IsSTAThread())
{
ReleaseLocalSTAEntry();
}
else
{
ReleaseLocalMTAEntry();
}
}
}
}
ComDebOut((DEB_OXID, "COXIDTable::GetLocalEntry this:%x pEntry:%x\n",
this, *ppEntry));
return hr;
}
//+------------------------------------------------------------------------
//
// Member: COXIDTbl::ReleaseLocalSTAEntry, public
//
// Synopsis: releases the OXIDEntry for the current STA apartment.
//
// History: 20-Feb-95 Rickhi Created
//
//+------------------------------------------------------------------------
void COXIDTable::ReleaseLocalSTAEntry(void)
{
ComDebOut((DEB_OXID, "COXIDTable::ReleaseLocalSTAEntry\n"));
Win4Assert(IsSTAThread());
ASSERT_LOCK_HELD
COleTls tls;
OXIDEntry *pOXIDEntry = (OXIDEntry *)(tls->pOXIDEntry);
if (pOXIDEntry)
{
// get the CRemoteUnknown for this apartment.
CRemoteUnknown *pRemUnk = tls->pRemoteUnk;
tls->pRemoteUnk = NULL;
// this guy ignores refcounts so we delete him directly.
delete pRemUnk;
// de-register the OXID and OIDs with the resolver.
gResolver.ServerFreeOXID(pOXIDEntry);
// Clear the apartment OXID Entry.
tls->pOXIDEntry = NULL;
// now decrement its count.
DecOXIDRefCnt(pOXIDEntry);
}
}
//+------------------------------------------------------------------------
//
// Member: COXIDTbl::ReleaseLocalMTAEntry, public
//
// Synopsis: releases the OXIDEntry for the current apartment.
//
// History: 20-Feb-95 Rickhi Created
//
//+------------------------------------------------------------------------
void COXIDTable::ReleaseLocalMTAEntry(void)
{
ComDebOut((DEB_OXID, "COXIDTable::ReleaseLocalMTAEntry\n"));
ASSERT_LOCK_HELD
OXIDEntry *pOXIDEntry = gpMTAOXIDEntry;
if (pOXIDEntry)
{
// get the CRemoteUnknown for this apartment.
CRemoteUnknown *pRemUnk = gpMTARemoteUnknown;;
gpMTARemoteUnknown = NULL;
// this guy ignores refcounts so we delete him directly.
delete pRemUnk;
// de-register the OXID and OIDs with the resolver.
gResolver.ServerFreeOXID(pOXIDEntry);
// Clear the MTA apartment OXID Entry.
gpMTAOXIDEntry = NULL;
// now decrement its count.
DecOXIDRefCnt(pOXIDEntry);
}
}
//+-------------------------------------------------------------------
//
// Function: FindOrCreateOXIDEntry
//
// Synopsis: finds or adds an OXIDEntry for the given OXID. May
// also create a MIDEntry if one does not yet exist.
//
// History: 22-Jan-96 Rickhi Created.
//
//--------------------------------------------------------------------
HRESULT FindOrCreateOXIDEntry(REFOXID roxid,
OXID_INFO &oxidInfo,
FOCOXID eResolverRef,
DUALSTRINGARRAY *psaResolver,
REFMID rmid,
MIDEntry *pMIDEntry,
OXIDEntry **ppOXIDEntry)
{
ComDebOut((DEB_OXID,"FindOrCreateOXIDEntry oxid:%08x %08x oxidInfo:%x psa:%ws pMIDEntry:%x\n",
roxid, &oxidInfo, psaResolver, pMIDEntry));
gOXIDTbl.ValidateOXID();
ASSERT_LOCK_HELD
HRESULT hr = S_OK;
// check if the OXIDEntry was created while we were resolving it.
*ppOXIDEntry = gOXIDTbl.LookupOXID(roxid, rmid);
if (*ppOXIDEntry == NULL)
{
BOOL fReleaseMIDEntry = FALSE;
if (pMIDEntry == NULL)
{
// dont yet have a MIDEntry for the machine so go add it
hr = gMIDTbl.FindOrCreateMIDEntry(rmid, psaResolver, &pMIDEntry);
fReleaseMIDEntry = TRUE;
}
if (pMIDEntry)
{
// add a new the OXIDEntry
hr = gOXIDTbl.AddEntry(roxid, &oxidInfo, pMIDEntry, ppOXIDEntry);
if (fReleaseMIDEntry)
{
// undo the reference added by FindOrCreateMIDEntry
DecMIDRefCnt(pMIDEntry);
}
}
}
if (SUCCEEDED(hr) && eResolverRef == FOCOXID_REF)
{
// Increment the count of references handed to us from the resolver.
(*ppOXIDEntry)->cResolverRef += 1;
}
gOXIDTbl.ValidateOXID();
ComDebOut((DEB_OXID,"FindOrCreateOXIDEntry pOXIDEntry:%x hr:%x\n",
*ppOXIDEntry, hr));
ASSERT_LOCK_HELD
return hr;
}
//+------------------------------------------------------------------------
//
// Function: GetLocalOXIDEntry
//
// Synopsis: Get either the global or the TLS OXIDEntry based on the
// threading model of the current thread.
//
// History: 05-May-95 AlexMit Created
//
//-------------------------------------------------------------------------
OXIDEntry *GetLocalOXIDEntry()
{
ASSERT_LOCK_HELD
COleTls tls;
if (tls->dwFlags & OLETLS_APARTMENTTHREADED)
return (OXIDEntry *)(tls->pOXIDEntry);
return gpMTAOXIDEntry;
}
//+------------------------------------------------------------------------
//
// Function: SetLocalOXIDEntry
//
// Synopsis: Set either the global or the TLS OXIDEntry based on the
// threading model of the current thread.
//
// History: 05-May-95 AlexMit Created
//
//-------------------------------------------------------------------------
void SetLocalOXIDEntry(OXIDEntry *pOXIDEntry)
{
ASSERT_LOCK_HELD
COleTls tls;
if (tls->dwFlags & OLETLS_APARTMENTTHREADED)
{
tls->pOXIDEntry = (void *)pOXIDEntry;
return;
}
gpMTAOXIDEntry = pOXIDEntry;
}
//+------------------------------------------------------------------------
//
// Function: CoGetTidFromIPID
//
// Synopsis: Take an IPID and return the thread id the object is on.
// MSWMSG calls this function during dispatches.
//
//-------------------------------------------------------------------------
STDAPI_(DWORD) CoGetTIDFromIPID( UUID *pIPID )
{
DWORD iTid = 0;
LOCK
IPIDEntry *pEntry = gIPIDTbl.LookupIPID( *pIPID );
if (pEntry != NULL && pEntry->pOXIDEntry != NULL)
{
iTid = pEntry->pOXIDEntry->dwTid;
}
UNLOCK
return iTid;
}
//+------------------------------------------------------------------------
//
// Function: CleanupMIDEntry
//
// Synopsis: Called by the MID hash table when cleaning up any leftover
// entries.
//
// History: 02-Feb-96 Rickhi Created
//
//-------------------------------------------------------------------------
void CleanupMIDEntry(SHashChain *pNode)
{
gMIDTbl.ReleaseEntry((MIDEntry *)pNode);
}
//+------------------------------------------------------------------------
//
// Member: CMIDTbl::Initialize, public
//
// Synopsis: Initializes the MID table.
//
// History: 02-Feb-96 Rickhi Created
//
//-------------------------------------------------------------------------
void CMIDTable::Initialize()
{
ComDebOut((DEB_OXID, "CMIDTable::Initialize\n"));
_HashTbl.Initialize(MIDBuckets);
_palloc.Initialize(sizeof(MIDEntry), MIDS_PER_PAGE);
}
//+------------------------------------------------------------------------
//
// Member: CMIDTbl::Cleanup, public
//
// Synopsis: Cleanup the MID table.
//
// History: 02-Feb-95 Rickhi Created
//
//-------------------------------------------------------------------------
void CMIDTable::Cleanup()
{
ComDebOut((DEB_OXID, "CMIDTable::Cleanup\n"));
_HashTbl.Cleanup(CleanupMIDEntry);
_palloc.Cleanup();
}
//+------------------------------------------------------------------------
//
// Member: CMIDTable::FindOrCreateMIDEntry, public
//
// Synopsis: Looks for existing copy of the string array in the MID table,
// creates one if not found
//
// History: 05-Jan-96 Rickhi Created
//
//-------------------------------------------------------------------------
HRESULT CMIDTable::FindOrCreateMIDEntry(REFMID rmid,
DUALSTRINGARRAY *psaResolver,
MIDEntry **ppMIDEntry)
{
ComDebOut((DEB_OXID, "CMIDTable::FindOrCreateMIDEntry psa:%x\n", psaResolver));
Win4Assert(psaResolver != NULL);
ASSERT_LOCK_HELD
HRESULT hr = S_OK;
DWORD dwHash;
*ppMIDEntry = LookupMID(psaResolver, &dwHash);
if (*ppMIDEntry == NULL)
{
hr = AddMIDEntry(rmid, dwHash, psaResolver, ppMIDEntry);
}
ASSERT_LOCK_HELD
ComDebOut((DEB_OXID, "CMIDTable::FindOrCreateEntry pMIDEntry:%x hr:%x\n", *ppMIDEntry, hr));
return hr;
}
//+------------------------------------------------------------------------
//
// Member: CMIDTable::LookupMID, public
//
// Synopsis: Looks for existing copy of the string array in the MID table.
//
// History: 05-Jan-96 Rickhi Created
//
//-------------------------------------------------------------------------
MIDEntry *CMIDTable::LookupMID(DUALSTRINGARRAY *psaResolver, DWORD *pdwHash)
{
ComDebOut((DEB_OXID, "CMIDTable::LookupMID psa:%x\n", psaResolver));
Win4Assert(psaResolver != NULL);
ASSERT_LOCK_HELD
*pdwHash = _HashTbl.Hash(psaResolver);
MIDEntry *pMIDEntry = (MIDEntry *) _HashTbl.Lookup(*pdwHash, psaResolver);
if (pMIDEntry)
{
// found the node, AddRef it and return
IncMIDRefCnt(pMIDEntry);
}
ASSERT_LOCK_HELD
ComDebOut((DEB_OXID, "CMIDTable::LookupMID pMIDEntry:%x\n", pMIDEntry));
return pMIDEntry;
}
//+------------------------------------------------------------------------
//
// Member: CMIDTable::AddEntry, public
//
// Synopsis: Adds an entry to the MID table. The entry is AddRef'd.
//
// History: 05-Jan-96 Rickhi Created
//
//-------------------------------------------------------------------------
HRESULT CMIDTable::AddMIDEntry(REFMID rmid, DWORD dwHash,
DUALSTRINGARRAY *psaResolver,
MIDEntry **ppMIDEntry)
{
ComDebOut((DEB_OXID, "CMIDTable::AddMIDEntry rmid:%08x %08x dwHash:%x psa:%x\n",
rmid, dwHash, psaResolver));
Win4Assert(psaResolver != NULL);
ASSERT_LOCK_HELD
// We must make a copy of the psa to store in the table, since we are
// using the one read in from ReadObjRef (or allocated by MIDL).
DUALSTRINGARRAY *psaNew;
HRESULT hr = CopyStringArray(psaResolver, NULL, &psaNew);
if (FAILED(hr))
return hr;
MIDEntry *pMIDEntry = (MIDEntry *) _palloc.AllocEntry();
if (pMIDEntry)
{
pMIDEntry->cRefs = 1;
pMIDEntry->dwFlags = 0;
pMIDEntry->mid = rmid;
// add the entry to the hash table
_HashTbl.Add(dwHash, psaNew, &pMIDEntry->Node);
hr = S_OK;
// set the maximum size of any resolver PSA we have seen. This is used
// when computing the max marshal size during interface marshaling.
DWORD dwpsaSize = SASIZE(psaNew->wNumEntries);
if (dwpsaSize > gdwPsaMaxSize)
{
gdwPsaMaxSize = dwpsaSize;
}
}
else
{
// cant create a MIDEntry, free the copy of the string array.
PrivMemFree(psaNew);
hr = E_OUTOFMEMORY;
}
*ppMIDEntry = pMIDEntry;
ASSERT_LOCK_HELD
ComDebOut((DEB_OXID, "CMIDTable::AddMIDEntry pMIDEntry:%x hr:%x\n", *ppMIDEntry, hr));
return hr;
}
//+------------------------------------------------------------------------
//
// Member: CMIDTable::ReleaseEntry, public
//
// Synopsis: remove the MIDEntry from the hash table and free the memory
//
// History: 05-Jan-96 Rickhi Created
//
//-------------------------------------------------------------------------
void CMIDTable::ReleaseEntry(MIDEntry *pMIDEntry)
{
ComDebOut((DEB_OXID, "CMIDTable::ReleaseEntry pMIDEntry:%x\n", pMIDEntry));
Win4Assert(pMIDEntry->cRefs == 0);
ASSERT_LOCK_HELD
// delete the string array
PrivMemFree(pMIDEntry->Node.psaKey);
// remove from the hash chain and delete the node
_HashTbl.Remove(&pMIDEntry->Node.chain);
_palloc.ReleaseEntry((PageEntry *)pMIDEntry);
}
//+------------------------------------------------------------------------
//
// Function: DecMIDRefCnt, public
//
// Synopsis: release one reference to the MIDEntry and release
// the entry if the count goes to zero.
//
// History: 05-Jan-96 Rickhi Created
//
//-------------------------------------------------------------------------
void DecMIDRefCnt(MIDEntry *pMIDEntry)
{
Win4Assert(pMIDEntry);
ASSERT_LOCK_HELD
ComDebOut((DEB_OXID,
"DecMIDRefCnt pMIDEntry:%x cRefs[%x]\n", pMIDEntry, pMIDEntry->cRefs-1));
pMIDEntry->cRefs--;
if (pMIDEntry->cRefs == 0)
{
gMIDTbl.ReleaseEntry(pMIDEntry);
}
}
//+------------------------------------------------------------------------
//
// Function: GetLocalMIDEntry
//
// Synopsis: Get or create the MID (Machine ID) entry for the local
// machine. gpLocalMIDEntry holds the network address for the
// local OXID resolver.
//
// History: 05-Jan-96 Rickhi Created
//
//-------------------------------------------------------------------------
HRESULT GetLocalMIDEntry(MIDEntry **ppMIDEntry)
{
ASSERT_LOCK_HELD
HRESULT hr = S_OK;
if (gpLocalMIDEntry == NULL)
{
// make sure we have the local resolver string bindings
RPC_STATUS sc = gResolver.GetConnection();
if (sc == RPC_S_OK)
{
// Create a MID entry for the Local Resolver
hr = gMIDTbl.FindOrCreateMIDEntry(gLocalMid, gpsaLocalResolver,
&gpLocalMIDEntry);
}
else
{
hr = MAKE_SCODE(SEVERITY_ERROR, FACILITY_WIN32, sc);
}
}
*ppMIDEntry = gpLocalMIDEntry;
return hr;
}