mirror of https://github.com/lianthony/NT4.0
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.
625 lines
20 KiB
625 lines
20 KiB
//+-------------------------------------------------------------------
|
|
//
|
|
// File: idtable.cxx
|
|
//
|
|
// Contents: identity table
|
|
//
|
|
// Functions:
|
|
//
|
|
// History: 1-Dec-93 CraigWi Created
|
|
// 14-Apr-95 Rickhi ReVamped
|
|
//
|
|
//--------------------------------------------------------------------
|
|
#include <ole2int.h>
|
|
#include <idtable.hxx>
|
|
#include <locks.hxx> // LOCK/UNLOCK
|
|
#include <resolver.hxx> // gResolver
|
|
#include <comsrgt.hxx>
|
|
|
|
CIDArray *gpOIDTable = NULL;
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Function: LookupIDFromUnk, private
|
|
//
|
|
// Synopsis: Looks up and may create the identity object for the given
|
|
// object. If the identity object is created, it is not
|
|
// aggregated to the given object.
|
|
//
|
|
// Identity lookup is based on pUnkControl.
|
|
//
|
|
// Arguments: [pUnk] -- the object; not necessarily the controlling unknown
|
|
// [dwflags] -- see IDLFLAGS in idtable.hxx
|
|
// [ppStdId] -- when S_OK is returned, this is the identity
|
|
//
|
|
// Returns: S_OK - identity now exists (whether created here or not)
|
|
// CO_E_OBJNOTREG - no identity and !fCreate
|
|
// E_OUTOFMEMORY -
|
|
// E_UNEXPECTED - at least: no controlling unknown
|
|
//
|
|
//
|
|
// Notes: If the StdId is client-side, the returned pointer will hold
|
|
// the object alive.
|
|
//
|
|
// If the StdId is server-side, the returned pointer will hold
|
|
// the object alive only if IDLF_STRONG is set, otherwise it
|
|
// just holds the identity alive.
|
|
//
|
|
// History: 11-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
INTERNAL LookupIDFromUnk(IUnknown *pUnk, DWORD dwFlags, CStdIdentity **ppStdId)
|
|
{
|
|
// QI for IStdID; if ok, return that
|
|
if (pUnk->QueryInterface(IID_IStdIdentity, (void **)ppStdId) == NOERROR)
|
|
return S_OK;
|
|
|
|
// QI for controlling unknown; should succeed
|
|
IUnknown *pUnkControl;
|
|
if (pUnk->QueryInterface(IID_IUnknown, (void **)&pUnkControl) != NOERROR)
|
|
return E_UNEXPECTED;
|
|
|
|
|
|
HRESULT hr = S_OK;
|
|
CStdIdentity *pStdId = NULL;
|
|
CStdIdentity *pStdIdForRelease = NULL;
|
|
|
|
// scan for value in map; may find one attached to object created by now
|
|
IDENTRY identry;
|
|
identry.m_tid = GetCurrentApartmentId();
|
|
identry.m_pUnkControl = pUnkControl;
|
|
|
|
// lock others out of the table while we do our stuff...
|
|
ASSERT_LOCK_RELEASED
|
|
LOCK
|
|
|
|
int iID;
|
|
|
|
if (gpOIDTable == NULL)
|
|
{
|
|
iID = -1;
|
|
hr = CO_E_OBJNOTREG;
|
|
|
|
if (dwFlags & IDLF_CREATE)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
gpOIDTable = new CIDArray;
|
|
}
|
|
|
|
if (gpOIDTable == NULL)
|
|
{
|
|
UNLOCK
|
|
ASSERT_LOCK_RELEASED
|
|
pUnkControl->Release();
|
|
*ppStdId = NULL;
|
|
return hr;
|
|
}
|
|
|
|
// change the GrowBy value to something better than 1
|
|
gpOIDTable->SetSize(0, 20);
|
|
}
|
|
else
|
|
{
|
|
iID = gpOIDTable->IndexOf((void *)&identry.m_tid,
|
|
sizeof(identry.m_tid) + sizeof(identry.m_pUnkControl),
|
|
offsetof(IDENTRY, m_tid));
|
|
}
|
|
|
|
Win4Assert(gpOIDTable != NULL);
|
|
|
|
if (iID == -1)
|
|
{
|
|
hr = CO_E_OBJNOTREG; // assume no creation
|
|
|
|
if (dwFlags & IDLF_CREATE)
|
|
{
|
|
// try to create one. Must release the lock to do this since
|
|
// we have to go ask the app a bunch of questions.
|
|
|
|
UNLOCK
|
|
ASSERT_LOCK_RELEASED
|
|
|
|
hr = S_OK;
|
|
IUnknown *pUnkID; // internal unknown of Identity, ignored on
|
|
// the server side.
|
|
|
|
pStdId = new CStdIdentity(STDID_SERVER, NULL,pUnkControl, &pUnkID);
|
|
if (pStdId == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
ASSERT_LOCK_RELEASED
|
|
LOCK
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
MOID moid;
|
|
if (dwFlags & IDLF_NOPING)
|
|
{
|
|
// object wont be pinged so dont bother using a
|
|
// pre-registered oid, just use a reserved one. Save
|
|
// the pre-registered ones for pinged objects.
|
|
hr = gResolver.ServerGetReservedMOID(&moid);
|
|
}
|
|
else
|
|
{
|
|
// object will be pinged, so get a pre-registered OID.
|
|
// Do this while the lock is released incase we have
|
|
// to Rpc to the resolver. Note this could yield if we
|
|
// have to pre-register more OIDs so do this before
|
|
// checking the table again.
|
|
hr = gResolver.ServerGetPreRegMOID(&moid);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// while we released the lock, another thread could have
|
|
// come along and created the identity for this object,
|
|
// so we need to check again.
|
|
|
|
iID = gpOIDTable->IndexOf((void *)&identry.m_tid,
|
|
sizeof(identry.m_tid) + sizeof(identry.m_pUnkControl),
|
|
offsetof(IDENTRY, m_tid));
|
|
|
|
if (iID == -1)
|
|
{
|
|
// make the created StdId the identity for the object.
|
|
hr = pStdId->SetOID(moid);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// need to set the marshal time of the object to
|
|
// ensure that it does not run down when if the lock
|
|
// is released before our first marshal is complete.
|
|
pStdId->SetMarshalTime();
|
|
|
|
if (dwFlags & IDLF_STRONG)
|
|
{
|
|
pStdId->IncStrongCnt();
|
|
pStdId->Release();
|
|
}
|
|
|
|
if (dwFlags & IDLF_NOPING)
|
|
{
|
|
pStdId->SetNoPing();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// OOM on SetOID, release Identity and return error
|
|
pStdIdForRelease = pStdId;
|
|
pStdId = NULL;
|
|
Win4Assert(iID == -1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// release the one we created and use the one in the
|
|
// tbl. we get it below in the (iID != -1) case.
|
|
pStdIdForRelease = pStdId;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// cant allocate an OID. Release the StdId we created
|
|
// when we exit the lock and return an error
|
|
|
|
pStdIdForRelease = pStdId;
|
|
pStdId = NULL;
|
|
Win4Assert(iID == -1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (iID != -1)
|
|
{
|
|
// found, addref pStdId which holds the identity alive
|
|
pStdId = gpOIDTable->ElementAt(iID).m_pStdID;
|
|
|
|
if (dwFlags & IDLF_STRONG)
|
|
pStdId->IncStrongCnt();
|
|
else
|
|
pStdId->AddRef();
|
|
|
|
Win4Assert(hr == S_OK);
|
|
}
|
|
|
|
|
|
#if DBG == 1
|
|
if (pStdId != NULL)
|
|
{
|
|
if (iID == -1)
|
|
{
|
|
// object was created, need to get the iID for debug
|
|
iID = gpOIDTable->IndexOf((void *)&identry.m_tid,
|
|
sizeof(identry.m_tid) + sizeof(identry.m_pUnkControl),
|
|
offsetof(IDENTRY, m_tid));
|
|
Win4Assert(iID != -1);
|
|
}
|
|
|
|
// verify correctness of entry
|
|
Win4Assert(pUnkControl == gpOIDTable->ElementAt(iID).m_pUnkControl);
|
|
Win4Assert(IsEqualGUID(pStdId->GetOID(), gpOIDTable->ElementAt(iID).m_moid));
|
|
Win4Assert(gpOIDTable->ElementAt(iID).m_tid == identry.m_tid);
|
|
}
|
|
#endif
|
|
|
|
*ppStdId = pStdId;
|
|
|
|
UNLOCK
|
|
ASSERT_LOCK_RELEASED
|
|
|
|
// Release any of the pointers we dont need. Must unlock before
|
|
// doing this cause it will call app code.
|
|
|
|
if (pStdIdForRelease)
|
|
{
|
|
ASSERT_LOCK_RELEASED
|
|
pStdIdForRelease->Release();
|
|
}
|
|
|
|
pUnkControl->Release();
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Function: LookupIDFromID, private
|
|
//
|
|
// Synopsis: Lookup an identity object based on an OID; does not create.
|
|
//
|
|
// Arguments: [moid] -- The identity
|
|
// [ppStdID] -- The cooresponding identity object if successful
|
|
//
|
|
// Returns: S_OK - have the identity object
|
|
// CO_E_OBJNOTREG - not present (when we looked)
|
|
//
|
|
// History: 11-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
INTERNAL LookupIDFromID(REFMOID moid, BOOL fAddRef, CStdIdentity **ppStdID)
|
|
{
|
|
// ComDebOut((DEB_MARSHAL, "LookupIDFromID fAddRef:%x ppStdId:%x oid:%I\n",
|
|
// fAddRef, ppStdID, &moid));
|
|
ASSERT_LOCK_HELD
|
|
|
|
*ppStdID = NULL;
|
|
|
|
if (gpOIDTable == NULL)
|
|
{
|
|
// no table, dont do lookup
|
|
return CO_E_OBJNOTREG;
|
|
}
|
|
|
|
IDENTRY identry;
|
|
identry.m_moid = moid;
|
|
identry.m_tid = GetCurrentApartmentId();
|
|
|
|
int iID = gpOIDTable->IndexOf((void *)&identry.m_moid,
|
|
sizeof(identry.m_moid) + sizeof(identry.m_tid),
|
|
offsetof(IDENTRY, m_moid));
|
|
|
|
if (iID != -1)
|
|
{
|
|
// found, addref pStdID which holds the identity alive
|
|
*ppStdID = gpOIDTable->ElementAt(iID).m_pStdID;
|
|
|
|
if (fAddRef)
|
|
{
|
|
// I sure hope the app doesn't try anything fancy in AddRef
|
|
// that would cause a deadlock here! (That is, in the aggregated
|
|
// case we will run app code).
|
|
(*ppStdID)->AddRef();
|
|
}
|
|
|
|
#if DBG == 1
|
|
// verify correctness of entry
|
|
Win4Assert(IsEqualGUID(moid, gpOIDTable->ElementAt(iID).m_moid));
|
|
Win4Assert(IsEqualGUID(moid, (*ppStdID)->GetOID()));
|
|
Win4Assert(gpOIDTable->ElementAt(iID).m_tid == identry.m_tid);
|
|
#endif
|
|
}
|
|
|
|
return (*ppStdID == NULL) ? CO_E_OBJNOTREG : NOERROR;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Function: SetObjectID, private
|
|
//
|
|
// Synopsis: Called by the object id creation and unmarshal functions
|
|
// to establish the identity for an object (handler or server).
|
|
// Can fail if we discover an existing identity.
|
|
//
|
|
// Identity lookup is based on pUnkControl.
|
|
//
|
|
// Arguments: [moid] -- The id for the object
|
|
// [pUnkControl] -- The controlling uknown of the object being
|
|
// identitified.
|
|
// [pStdID] -- The identity object itself.
|
|
//
|
|
// Returns: S_OK - identity was set successfully
|
|
// CO_E_OBJISREG - object was already registered (as determined
|
|
// by pUnkControl); *ppStdIDExisting set (if requested).
|
|
// E_OUTOFMEMORY -
|
|
//
|
|
// History: 11-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
INTERNAL SetObjectID(REFMOID moid, IUnknown *pUnkControl, CStdIdentity *pStdID)
|
|
{
|
|
ComDebOut((DEB_MARSHAL, "SetObjectID pUnk:%x pStdId:%x oid:%I\n",
|
|
pUnkControl, pStdID, &moid));
|
|
Win4Assert(!IsEqualGUID(moid, GUID_NULL));
|
|
ASSERT_LOCK_HELD
|
|
|
|
|
|
HRESULT hr = S_OK;
|
|
if (gpOIDTable == NULL)
|
|
{
|
|
gpOIDTable = new CIDArray;
|
|
if (gpOIDTable == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// change the GrowBy value to something better than 1
|
|
gpOIDTable->SetSize(0, 20);
|
|
}
|
|
|
|
IDENTRY identry;
|
|
identry.m_moid = moid;
|
|
identry.m_tid = GetCurrentApartmentId();
|
|
identry.m_pUnkControl = pUnkControl;
|
|
identry.m_pStdID = pStdID;
|
|
|
|
#if DBG==1
|
|
// scan for value in map; better not find one
|
|
// CODEWORK: for freethreaded handler case we may need to allow
|
|
// finding a duplicate entry, and throw away the second copy.
|
|
|
|
int iID = gpOIDTable->IndexOf((void *)&identry.m_tid,
|
|
sizeof(identry.m_tid) + sizeof(identry.m_pUnkControl),
|
|
offsetof(IDENTRY, m_tid));
|
|
|
|
if (iID != -1)
|
|
{
|
|
// if found, another thread created identity for same object;
|
|
// this is an error.
|
|
Win4Assert(!"Already Registered OID");
|
|
}
|
|
#endif
|
|
|
|
// add at end; no addrefs
|
|
if (gpOIDTable->Add(identry) == -1)
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
ASSERT_LOCK_HELD
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Function: ClearObjectID, private
|
|
//
|
|
// Synopsis: Called during the revokation of the id only. Clears
|
|
// the identity entry in the table.
|
|
//
|
|
// Identity lookup is based on oid.
|
|
//
|
|
// Arguments: [moid] -- The identity
|
|
// [pUnkControl] -- The object for which the identity is being
|
|
// revoked; used for asserts only.
|
|
// [pStdID] -- The identity object; used for asserts only.
|
|
//
|
|
// Returns: S_OK - removed successfully
|
|
// CO_E_OBJNOTREG - not present (often ignored).
|
|
//
|
|
// History: 11-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
INTERNAL ClearObjectID(REFMOID moid, IUnknown *pUnkControl, CStdIdentity *pStdID)
|
|
{
|
|
ComDebOut((DEB_MARSHAL, "ClearObjectID pUnk:%x pStdId:%x oid:%I\n",
|
|
pUnkControl, pStdID, &moid));
|
|
ASSERT_LOCK_HELD
|
|
|
|
HRESULT hr = NOERROR;
|
|
|
|
IDENTRY identry;
|
|
identry.m_moid = moid;
|
|
identry.m_tid = GetCurrentApartmentId();
|
|
|
|
int iID = gpOIDTable->IndexOf((void *)&identry.m_moid,
|
|
sizeof(identry.m_moid) + sizeof(identry.m_tid),
|
|
offsetof(IDENTRY, m_moid));
|
|
|
|
if (iID != -1)
|
|
{
|
|
// found, remove it.
|
|
#if DBG == 1
|
|
// verify correctness of entry
|
|
Win4Assert(pUnkControl == gpOIDTable->ElementAt(iID).m_pUnkControl);
|
|
Win4Assert(IsEqualGUID(pStdID->GetOID(), gpOIDTable->ElementAt(iID).m_moid));
|
|
Win4Assert(pStdID == gpOIDTable->ElementAt(iID).m_pStdID);
|
|
Win4Assert(gpOIDTable->ElementAt(iID).m_tid == identry.m_tid);
|
|
#endif
|
|
|
|
Win4Assert(gpOIDTable->GetSize() != 0);
|
|
int iLast = gpOIDTable->GetSize() - 1;
|
|
if (iID != iLast)
|
|
{
|
|
// element removed is not last; copy last element to current
|
|
gpOIDTable->ElementAt(iID) = gpOIDTable->ElementAt(iLast);
|
|
}
|
|
|
|
// now setsize to one less to remove the now unused last element
|
|
gpOIDTable->SetSize(iLast);
|
|
|
|
// for surrogates, we need to detect when there are no clients
|
|
// using servers in the surrogate process -- we rely on the
|
|
// fact that the OIDTable must be empty when there are no clients
|
|
|
|
// if there are no external clients, this process should terminate
|
|
// if its a surrogate process
|
|
if(iLast == 0)
|
|
{
|
|
(void)CCOMSurrogate::FreeSurrogate();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Win4Assert(!"ClearObjectID not found!");
|
|
hr = CO_E_OBJNOTREG;
|
|
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Function: IDTableUninitializeHelper, private
|
|
//
|
|
// Synopsis: Clears the id table memory for the specified thread (or all
|
|
// if party model). This involves scanning the table and for
|
|
// entries on the current thread, calling
|
|
// IMarshal::DisconnectObject.
|
|
//
|
|
// The purpose of this routine is to simulate inter-thread rundown
|
|
// as well as clean up memory.
|
|
//
|
|
// History: 23-Dec-93 CraigWi Created.
|
|
// 26-Apr-94 CraigWi Now called per-thread and disconnects
|
|
//
|
|
// Note: This function should only be called when the IDTable
|
|
// really needs to be uninitialized. For the party model, this
|
|
// means that it should only be called when the last thread
|
|
// is exiting.
|
|
//
|
|
// This function must NOT assume that it is being called within
|
|
// a critical section.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
INTERNAL_(void) IDTableThreadUninitializeHelper(DWORD tid)
|
|
{
|
|
// The table being uninitialized is resized as items are deleted. Thus
|
|
// if an element in the middle is removed, the last element in the table
|
|
// will be copied into that slot and the table will shrink. Also, some
|
|
// of the calls made while cleaning up an entry will free other entries,
|
|
// causing further swapping.
|
|
|
|
ASSERT_LOCK_RELEASED
|
|
LOCK
|
|
|
|
int i = gpOIDTable->GetSize() - 1;
|
|
while (i >= 0)
|
|
{
|
|
if (gpOIDTable->ElementAt(i).m_tid == tid)
|
|
{
|
|
Win4Assert(IsValidInterface(gpOIDTable->ElementAt(i).m_pStdID));
|
|
CStdIdentity *pStdID = gpOIDTable->ElementAt(i).m_pStdID;
|
|
pStdID->AddRef();
|
|
|
|
ComDebOut((DEB_ERROR,
|
|
"Object [%s] at %lx still has [%x] connections\n",
|
|
pStdID->IsClient() ? "CLIENT" : "SERVER",
|
|
gpOIDTable->ElementAt(i).m_pUnkControl, pStdID->GetRC()));
|
|
|
|
pStdID->DbgDumpInterfaceList();
|
|
|
|
// release lock since the disconnect could take a long time.
|
|
UNLOCK
|
|
ASSERT_LOCK_RELEASED
|
|
|
|
pStdID->Disconnect();
|
|
pStdID->Release();
|
|
|
|
// re-request the lock since we need to guard the GetSize below
|
|
ASSERT_LOCK_RELEASED
|
|
LOCK
|
|
}
|
|
i--;
|
|
if (i >= gpOIDTable->GetSize())
|
|
i = gpOIDTable->GetSize() - 1;
|
|
}
|
|
|
|
UNLOCK
|
|
ASSERT_LOCK_RELEASED
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Function: IDTableUninitialize, public
|
|
//
|
|
// Synopsis: Clears the id table memory for the current apartment.
|
|
//
|
|
// History: 13 Apr 95 AlexMit Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
INTERNAL_(void) IDTableThreadUninitialize(void)
|
|
{
|
|
if (gpOIDTable)
|
|
{
|
|
IDTableThreadUninitializeHelper(GetCurrentApartmentId());
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: IDTableProcessUninitialize
|
|
//
|
|
// Synopsis: Process specific IDTable uninitialization
|
|
//
|
|
// Effects: Frees up table memory
|
|
//
|
|
// Requires: All thread specific uninitialization already complete. This
|
|
// function assumes that the caller is holding the
|
|
// g_mxsSingleThreadOle mutex (so that no other thread is trying
|
|
// to use the table while we clean it up).
|
|
//
|
|
// History: 29-Jun-94 AlexT Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
INTERNAL_(void) IDTableProcessUninitialize(void)
|
|
{
|
|
if (gpOIDTable)
|
|
{
|
|
gpOIDTable->RemoveAll();
|
|
delete gpOIDTable;
|
|
gpOIDTable = NULL;
|
|
}
|
|
}
|
|
|
|
#if DBG == 1
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Function: Dbg_FindRemoteHdlr
|
|
//
|
|
// Synopsis: finds a remote object handler for the specified object,
|
|
// and returns an instance of IMarshal on it. This is debug
|
|
// code for assert that reference counts are as expected and
|
|
// is used by tmarshal.exe.
|
|
//
|
|
// History: 23-Nov-93 Rickhi Created
|
|
// 23-Dec-93 CraigWi Changed to identity object
|
|
//
|
|
//--------------------------------------------------------------------
|
|
extern "C" IMarshal * _stdcall Dbg_FindRemoteHdlr(IUnknown *punkObj)
|
|
{
|
|
// validate input parms
|
|
Win4Assert(punkObj);
|
|
|
|
IMarshal *pIM = NULL;
|
|
CStdIdentity *pStdID;
|
|
HRESULT hr = LookupIDFromUnk(punkObj, 0, &pStdID);
|
|
if (hr == NOERROR)
|
|
{
|
|
pIM = (IMarshal *)pStdID;
|
|
}
|
|
|
|
return pIM;
|
|
}
|
|
#endif // DBG==1
|
|
|