586 lines
14 KiB
C++
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;
|
||
|
}
|