|
|
//*** inst.cpp -- 'instance' (CoCreate + initialization) mechanism
// SYNOPSIS
// CInstClassFactory_Create create 'stub loader' class factory
// InstallBrowBandInst install BrowserBand instance into registry
// InstallInstAndBag install arbitrary instance into registry
// - debug
// DBCreateInitInst create an
//
// DESCRIPTION
// the 'instance' mechanism provides an easy way to create and initialize
// a class from the registry (w/o writing any code).
//
// an 'instance' consists of an INSTID (unique to the instance), a CLSID
// (for the code), and an InitPropertyBag (to initialize the instance).
//
// it is fully transparent to CoCreateInstance; that is, one can do a
// CCI of an INSTID and it will create it and initialize it w/ the caller
// none the wiser. (actually there will be at least one tip-off, namely
// that IPS::GetClassID on the instance will return the 'code' CLSID not
// the 'instance' INSTID [which is as it should be, since this is exactly
// how persistance works when one programmatically creates his own multiple
// instances and then persists them.
//
// the INSTID is in the HKR/CLSID section of the registry (just like a
// 'normal' CLSID). the code points to shdocvw. when shdocvw hits the
// failure case in its DllGetClassObject search, it looks for the magic
// key 'HKCR/CLSID/{instid}/Instance'. if it finds it, it knows it's
// dealing w/ an INSTID, and builds a class factory 'stub loader' which
// has sufficient information to find the 'code' CLSID and the 'init'
// property bag.
#include "priv.h"
//***
// NOTES
// perf: failure case is cheap, only does a RegOpen, no object creation.
// positions to the 'Instance' part, must 'ChDir' to get to InitXxx part.
HKEY GetInstKey(LPTSTR pszInst) { TCHAR szRegName[MAX_PATH]; // "CLSID/{instid}/Instance" size?
// "CLSID/{instid}/Instance"
ASSERT(ARRAYSIZE(szRegName) >= 5 + 1 + GUIDSTR_MAX - 1 + 1 + 8 + 1); ASSERT(lstrlen(pszInst) == GUIDSTR_MAX - 1); HKEY hk = NULL; HRESULT hr = StringCchPrintf(szRegName, ARRAYSIZE(szRegName), TEXT("CLSID\\%s\\Instance"), pszInst); if (SUCCEEDED(hr)) { RegOpenKeyEx(HKEY_CLASSES_ROOT, szRegName, 0, KEY_QUERY_VALUE, &hk); } return hk; }
class CInstClassFactory : IClassFactory { public: // IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void);
// IClassFacotry
STDMETHODIMP CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv); STDMETHODIMP LockServer(BOOL fLock);
CInstClassFactory() { DllAddRef(); _cRef = 1; }; HRESULT Init(REFCLSID rclsid);
private: ~CInstClassFactory();
LONG _cRef; HKEY _hkey; // hkey for instance info
};
// NOTES
// called when class isn't in our sccls.c CCI table. we see if it's an
// instance, and if so we make a stub for it that gives sufficient info
// for our CreateInstance to create and init it.
//
// n.b. we keep the failure case as cheap as possible (just a regkey check,
// no object creation etc.).
//
STDAPI CInstClassFactory_Create(REFCLSID rclsid, REFIID riid, void **ppv) { HRESULT hr = E_OUTOFMEMORY; CInstClassFactory *pcf = new CInstClassFactory(); if (pcf) { hr = pcf->Init(rclsid); if (SUCCEEDED(hr)) hr = pcf->QueryInterface(riid, ppv); pcf->Release(); } return hr; }
HRESULT CInstClassFactory::Init(REFCLSID rclsid) { ASSERT(_hkey == NULL); // only init me once please
TCHAR szClass[GUIDSTR_MAX];
// "CLSID/{instid}/Instance"
SHStringFromGUID(rclsid, szClass, ARRAYSIZE(szClass)); _hkey = GetInstKey(szClass); return _hkey ? S_OK : E_OUTOFMEMORY; }
CInstClassFactory::~CInstClassFactory() { if (_hkey) RegCloseKey(_hkey);
DllRelease(); }
ULONG CInstClassFactory::AddRef() { return InterlockedIncrement(&_cRef); }
ULONG CInstClassFactory::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; }
HRESULT CInstClassFactory::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CInstClassFactory, IClassFactory), // IID_IClassFactory
{ 0 }, }; return QISearch(this, qit, riid, ppv); }
HRESULT CInstClassFactory::CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { HRESULT hr = E_FAIL; // the usual optimism :-)
*ppv = NULL;
ASSERT(_hkey); // o.w. shouldn't ever get here
// get object (vs. instance) CLSID and create it
// AppCompat: the "RealGuide" explorer bar from Real Audio has an extraneous
// double quote at the end of its CLSID value. This causes SHGetValue to fail
// if given only an szClass[GUIDSTR_MAX] buffer, so we'll bump up the size.
TCHAR szClass[GUIDSTR_MAX + 1];
DWORD cbTmp = sizeof(szClass); DWORD err = SHGetValue(_hkey, NULL, TEXT("CLSID"), NULL, szClass, &cbTmp); hr = HRESULT_FROM_WIN32(err);
if (SUCCEEDED(hr)) { // If there's a useless char at the end of the guid, we'll truncate it
// to avoid making assumptions about GUIDFromString. GUIDSTR_MAX includes
// the null terminator, so szClass[GUIDSTR_MAX - 1] should always be 0
// for a proper guid.
szClass[GUIDSTR_MAX - 1] = 0;
CLSID clsid; hr = GUIDFromString(szClass, &clsid) ? S_OK : E_FAIL;
if (SUCCEEDED(hr)) { IUnknown* pUnk; if (NOERROR == SHGetValue(_hkey, NULL, TEXT("LoadWithoutCOM"), NULL, NULL, NULL)) hr = SHCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IUnknown, &pUnk)); else hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IUnknown, &pUnk));
if (SUCCEEDED(hr)) { // try to load from propertybag first
IPropertyBag *pbag; hr = SHCreatePropertyBagOnRegKey(_hkey, L"InitPropertyBag", STGM_READ, IID_PPV_ARG(IPropertyBag, &pbag)); if (SUCCEEDED(hr)) { hr = SHLoadFromPropertyBag(pUnk, pbag); pbag->Release(); }
// Did the property bag interface exist and did it load properly?
if ( FAILED(hr)) { // No property bag interface or did not load suyccessfully, try stream
// Store this state temporarily, if stream fails too then we'll return the object
// with this hr
HRESULT hrPropBag = hr;
IPersistStream* pPerStream;
hr = pUnk->QueryInterface(IID_PPV_ARG(IPersistStream, &pPerStream));
if (SUCCEEDED(hr)) { IStream* pStream = SHOpenRegStream(_hkey, TEXT("InitStream"), NULL, STGM_READ); if (pStream) { hr = pPerStream->Load(pStream);
pStream->Release(); } else hr = E_FAIL;
pPerStream->Release(); } else hr = hrPropBag; }
if (SUCCEEDED(hr)) { hr = pUnk->QueryInterface(riid, ppv); }
pUnk->Release(); } } }
return hr; }
HRESULT CInstClassFactory::LockServer(BOOL fLock) { if (fLock) DllAddRef(); else DllRelease(); return S_OK; }
|