windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/cmp/asp51/tlbcache.cpp
2020-09-26 16:20:57 +08:00

586 lines
14 KiB
C++

/*===================================================================
Microsoft Denali
Microsoft Confidential.
Copyright 1997 Microsoft Corporation. All Rights Reserved.
Component: Typelibrary cache
File: tlbcache.cpp
Owner: DmitryR
This is the typelibrary cache source file.
===================================================================*/
#include "denpre.h"
#pragma hdrstop
#include "tlbcache.h"
#include "memchk.h"
/*===================================================================
Globals
===================================================================*/
CTypelibCache g_TypelibCache;
/*===================================================================
C T y p e l i b C a c h e E n t r y
===================================================================*/
/*===================================================================
CTypelibCacheEntry::CTypelibCacheEntry
Constructor
===================================================================*/
CTypelibCacheEntry::CTypelibCacheEntry()
:
m_fInited(FALSE), m_fIdsCached(FALSE), m_fStrAllocated(FALSE),
m_wszProgId(NULL), m_clsid(CLSID_NULL), m_cmModel(cmUnknown),
m_idOnStartPage(DISPID_UNKNOWN), m_idOnEndPage(DISPID_UNKNOWN),
m_gipTypelib(NULL_GIP_COOKIE)
{
}
/*===================================================================
CTypelibCacheEntry::~CTypelibCacheEntry
Destructor
===================================================================*/
CTypelibCacheEntry::~CTypelibCacheEntry()
{
if (m_gipTypelib != NULL_GIP_COOKIE)
{
g_GIPAPI.Revoke(m_gipTypelib);
}
if (m_fStrAllocated)
{
Assert(m_wszProgId);
free(m_wszProgId);
}
}
/*===================================================================
CTypelibCacheEntry::StoreProgID
Store ProgID with the structure
Parameters
wszProgId ProgId
cbProgId ProgId byte length
Returns
HRESULT
===================================================================*/
HRESULT CTypelibCacheEntry::StoreProgID
(
LPWSTR wszProgId,
DWORD cbProgId
)
{
Assert(wszProgId);
Assert(*wszProgId != L'\0');
Assert(cbProgId == (wcslen(wszProgId) * sizeof(WCHAR)));
// required buffer length
DWORD cbBuffer = cbProgId + sizeof(WCHAR);
WCHAR *wszBuffer = (WCHAR *)m_rgbStrBuffer;
if (cbBuffer > sizeof(m_rgbStrBuffer))
{
// the prog id doesn't fit into the member buffer -> allocate
wszBuffer = (WCHAR *)malloc(cbBuffer);
if (!wszBuffer)
return E_OUTOFMEMORY;
m_fStrAllocated = TRUE;
}
memcpy(wszBuffer, wszProgId, cbBuffer);
m_wszProgId = wszBuffer;
return S_OK;
}
/*===================================================================
CTypelibCacheEntry::InitByProgID
Real constructor.
Store ProgID. Init CLinkElem portion with ProgID.
Parameters
wszProgId ProgId
cbProgId ProgId byte length
Returns
HRESULT
===================================================================*/
HRESULT CTypelibCacheEntry::InitByProgID
(
LPWSTR wszProgId,
DWORD cbProgId
)
{
StoreProgID(wszProgId, cbProgId);
// init link with prog id as key (length excludes null term)
CLinkElem::Init(m_wszProgId, cbProgId);
m_fInited = TRUE;
return S_OK;
}
/*===================================================================
CTypelibCacheEntry::InitByCLSID
Real constructor.
Store ProgID. Init CLinkElem portion with CLSID.
Parameters
clsid CLSID
wszProgId ProgId
Returns
HRESULT
===================================================================*/
HRESULT CTypelibCacheEntry::InitByCLSID
(
const CLSID &clsid,
LPWSTR wszProgid
)
{
StoreProgID(wszProgid, CbWStr(wszProgid));
m_clsid = clsid;
// init link with CLSID id as key
CLinkElem::Init(&m_clsid, sizeof(m_clsid));
m_fInited = TRUE;
return S_OK;
}
/*===================================================================
C T y p e l i b C a c h e
===================================================================*/
/*===================================================================
CTypelibCache::CTypelibCache
Constructor
===================================================================*/
CTypelibCache::CTypelibCache()
:
m_fInited(FALSE)
{
}
/*===================================================================
CTypelibCache::~CTypelibCache
Destructor
===================================================================*/
CTypelibCache::~CTypelibCache()
{
UnInit();
}
/*===================================================================
CTypelibCache::Init
Init
Returns
HRESULT
===================================================================*/
HRESULT CTypelibCache::Init()
{
HRESULT hr;
hr = m_htProgIdEntries.Init(199);
if (FAILED(hr))
return hr;
hr = m_htCLSIDEntries.Init(97);
if (FAILED(hr))
return hr;
ErrInitCriticalSection(&m_csLock, hr);
if (FAILED(hr))
return(hr);
m_fInited = TRUE;
return S_OK;
}
/*===================================================================
CTypelibCache::UnInit
UnInit
Returns
HRESULT
===================================================================*/
HRESULT CTypelibCache::UnInit()
{
CTypelibCacheEntry *pEntry;
CLinkElem *pLink;
// ProgID Hash table
pLink = m_htProgIdEntries.Head();
while (pLink)
{
pEntry = static_cast<CTypelibCacheEntry *>(pLink);
pLink = pLink->m_pNext;
// remove the entry
delete pEntry;
}
m_htProgIdEntries.UnInit();
// CLSID Hash table
pLink = m_htCLSIDEntries.Head();
while (pLink)
{
pEntry = static_cast<CTypelibCacheEntry *>(pLink);
pLink = pLink->m_pNext;
// remove the entry
delete pEntry;
}
m_htCLSIDEntries.UnInit();
// Critical section
if (m_fInited)
{
DeleteCriticalSection(&m_csLock);
m_fInited = FALSE;
}
return S_OK;
}
/*===================================================================
CTypelibCache::CreateComponent
Create the component using the cached info.
Adds cache entry if needed.
To be called from Server.CreateObject
Parameters
bstrProgID prog id
pHitObj HitObj needed for creation
ppdisp [out] IDispatch *
pclsid [out] CLSID
Returns
HRESULT
===================================================================*/
HRESULT CTypelibCache::CreateComponent
(
BSTR bstrProgID,
CHitObj *pHitObj,
IDispatch **ppdisp,
CLSID *pclsid
)
{
HRESULT hr;
CLinkElem *pElem;
CTypelibCacheEntry *pEntry;
CComponentObject *pObj;
COnPageInfo OnPageInfo;
CompModel cmModel;
*pclsid = CLSID_NULL;
*ppdisp = NULL;
if (bstrProgID == NULL || *bstrProgID == L'\0')
return E_FAIL;
WCHAR *wszProgId = bstrProgID;
DWORD cbProgId = CbWStr(wszProgId); // do strlen once
BOOL fFound = FALSE;
BOOL bDispIdsCached = FALSE;
Lock();
pElem = m_htProgIdEntries.FindElem(wszProgId, cbProgId);
if (pElem)
{
// remember the elements of the entry while inside lock
pEntry = static_cast<CTypelibCacheEntry *>(pElem);
// return clsid
*pclsid = pEntry->m_clsid;
// prepate OnPageInfo with DispIds to pass to the creation function
if (pEntry->m_fIdsCached)
{
OnPageInfo.m_rgDispIds[ONPAGEINFO_ONSTARTPAGE] = pEntry->m_idOnStartPage;
OnPageInfo.m_rgDispIds[ONPAGEINFO_ONENDPAGE] = pEntry->m_idOnEndPage;
bDispIdsCached = TRUE;
}
// remember the model
cmModel = pEntry->m_cmModel;
fFound = TRUE;
}
UnLock();
if (fFound)
{
// create the object
hr = pHitObj->PPageComponentManager()->AddScopedUnnamedInstantiated
(
csPage,
*pclsid,
cmModel,
bDispIdsCached ? &OnPageInfo : NULL,
&pObj
);
// get IDispatch*
if (SUCCEEDED(hr))
hr = pObj->GetAddRefdIDispatch(ppdisp);
// return if succeeded
if (SUCCEEDED(hr))
{
// don't keep the object around unless needed
if (pObj->FEarlyReleaseAllowed())
pHitObj->PPageComponentManager()->RemoveComponent(pObj);
return S_OK;
}
// on failure remove from the cache and pretend
// as if it was never found
Lock();
pElem = m_htProgIdEntries.DeleteElem(wszProgId, cbProgId);
UnLock();
if (pElem)
{
// Element removed from cache - delete the CacheEntry
pEntry = static_cast<CTypelibCacheEntry *>(pElem);
delete pEntry;
}
// don't return bogus CLSID
*pclsid = CLSID_NULL;
}
// Not found in the cache. Create the object, get the info, and
// insert the new cache entry.
hr = CLSIDFromProgID((LPCOLESTR)wszProgId, pclsid);
if (FAILED(hr))
return hr; // couldn't even get clsid - don't cache
hr = pHitObj->PPageComponentManager()->AddScopedUnnamedInstantiated
(
csPage,
*pclsid,
cmUnknown,
NULL,
&pObj
);
if (FAILED(hr))
return hr; // couldn't create object - don't cache
// object created - get IDispatch*
if (SUCCEEDED(hr))
hr = pObj->GetAddRefdIDispatch(ppdisp);
if (FAILED(hr))
return hr; // couldn't get IDispatch* - don't cache
// create new CTypelibCacheEntry
pEntry = new CTypelibCacheEntry;
if (!pEntry)
return S_OK; // no harm
// init link entry
if (FAILED(pEntry->InitByProgID(wszProgId, cbProgId)))
{
delete pEntry;
return S_OK; // no harm
}
// remember stuff in pEntry
pEntry->m_clsid = *pclsid;
pEntry->m_cmModel = pObj->GetModel();
const COnPageInfo *pOnPageInfo = pObj->GetCachedOnPageInfo();
if (pOnPageInfo)
{
pEntry->m_fIdsCached = TRUE;
pEntry->m_idOnStartPage = pOnPageInfo->m_rgDispIds[ONPAGEINFO_ONSTARTPAGE];
pEntry->m_idOnEndPage = pOnPageInfo->m_rgDispIds[ONPAGEINFO_ONENDPAGE];
}
else
{
pEntry->m_fIdsCached = FALSE;
pEntry->m_idOnStartPage = DISPID_UNKNOWN;
pEntry->m_idOnEndPage = DISPID_UNKNOWN;
}
// try to get the typelib
pEntry->m_gipTypelib = NULL_GIP_COOKIE;
if (FIsWinNT()) // don't do GIP cookies on Win95
{
ITypeInfo *ptinfo;
if (SUCCEEDED((*ppdisp)->GetTypeInfo(0, 0, &ptinfo)))
{
ITypeLib *ptlib;
UINT idx;
if (SUCCEEDED(ptinfo->GetContainingTypeLib(&ptlib, &idx)))
{
// got it! convert to gip cookie
DWORD gip;
if (SUCCEEDED(g_GIPAPI.Register(ptlib, IID_ITypeLib, &gip)))
{
// remember the gip cookie with pEntry
pEntry->m_gipTypelib = gip;
}
ptlib->Release();
}
ptinfo->Release();
}
}
// pEntry is ready -- try to insert it into cache
BOOL fInserted = FALSE;
Lock();
// make sure some other thread didn't insert it already
if (m_htProgIdEntries.FindElem(wszProgId, cbProgId) == NULL)
{
if (m_htProgIdEntries.AddElem(pEntry))
fInserted = TRUE;
}
UnLock();
// cleanup
if (!fInserted)
delete pEntry;
// don't keep the object around unless needed
if (pObj->FEarlyReleaseAllowed())
pHitObj->PPageComponentManager()->RemoveComponent(pObj);
return S_OK;
}
/*===================================================================
CTypelibCache::RememberProgidToCLSIDMapping
Adds an entry to CLSID hashtable.
To be called from template after mapping ProgId to CLSID.
Parameters
wszProgID prog id
clsid clsid
Returns
HRESULT
===================================================================*/
HRESULT CTypelibCache::RememberProgidToCLSIDMapping
(
WCHAR *wszProgid,
const CLSID &clsid
)
{
HRESULT hr;
CLinkElem *pElem;
CTypelibCacheEntry *pEntry;
// check if already there first
BOOL fFound = FALSE;
Lock();
if (m_htCLSIDEntries.FindElem(&clsid, sizeof(CLSID)) != NULL)
fFound = TRUE;
UnLock();
if (fFound)
return S_OK;
// create new entry
pEntry = new CTypelibCacheEntry;
if (!pEntry)
return E_OUTOFMEMORY;
BOOL fInserted = FALSE;
// remember prog id and init link entry
hr = pEntry->InitByCLSID(clsid, wszProgid);
if (SUCCEEDED(hr))
{
Lock();
// make sure some other thread didn't insert it already
if (m_htCLSIDEntries.FindElem(&clsid, sizeof(CLSID)) == NULL)
{
if (m_htCLSIDEntries.AddElem(pEntry))
fInserted = TRUE;
}
UnLock();
}
// cleanup
if (!fInserted)
delete pEntry;
return hr;
}
/*===================================================================
CTypelibCache::UpdateMappedCLSID
Update CLSID since remembered.
To be called from object creation code to update CLSID
if the object creation failed.
Parameters
*pclsid [in, out] CLSID
Returns
HRESULT
S_FALSE = didn't change
S_OK = did change and the new one found
===================================================================*/
HRESULT CTypelibCache::UpdateMappedCLSID
(
CLSID *pclsid
)
{
HRESULT hr = S_FALSE; // not found
CLinkElem *pElem;
CTypelibCacheEntry *pEntry;
CLSID clsidNew;
Lock();
// Do everything under lock so the ProgID stored in
// the entry is still valid.
// Not very perfomant -- but is is an error path anyway
pElem = m_htCLSIDEntries.FindElem(pclsid, sizeof(CLSID));
if (pElem)
{
pEntry = static_cast<CTypelibCacheEntry *>(pElem);
// find new mapping
if (SUCCEEDED(CLSIDFromProgID(pEntry->m_wszProgId, &clsidNew)))
{
// compare with the old one
if (!IsEqualCLSID(clsidNew, *pclsid))
{
// the mapping did change!
*pclsid = clsidNew;
hr = S_OK;
}
}
}
UnLock();
return hr;
}