Source code of Windows XP (NT5)
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.

240 lines
7.5 KiB

  1. //*** inst.cpp -- 'instance' (CoCreate + initialization) mechanism
  2. // SYNOPSIS
  3. // CInstClassFactory_Create create 'stub loader' class factory
  4. // InstallBrowBandInst install BrowserBand instance into registry
  5. // InstallInstAndBag install arbitrary instance into registry
  6. // - debug
  7. // DBCreateInitInst create an
  8. //
  9. // DESCRIPTION
  10. // the 'instance' mechanism provides an easy way to create and initialize
  11. // a class from the registry (w/o writing any code).
  12. //
  13. // an 'instance' consists of an INSTID (unique to the instance), a CLSID
  14. // (for the code), and an InitPropertyBag (to initialize the instance).
  15. //
  16. // it is fully transparent to CoCreateInstance; that is, one can do a
  17. // CCI of an INSTID and it will create it and initialize it w/ the caller
  18. // none the wiser. (actually there will be at least one tip-off, namely
  19. // that IPS::GetClassID on the instance will return the 'code' CLSID not
  20. // the 'instance' INSTID [which is as it should be, since this is exactly
  21. // how persistance works when one programmatically creates his own multiple
  22. // instances and then persists them.
  23. //
  24. // the INSTID is in the HKR/CLSID section of the registry (just like a
  25. // 'normal' CLSID). the code points to shdocvw. when shdocvw hits the
  26. // failure case in its DllGetClassObject search, it looks for the magic
  27. // key 'HKCR/CLSID/{instid}/Instance'. if it finds it, it knows it's
  28. // dealing w/ an INSTID, and builds a class factory 'stub loader' which
  29. // has sufficient information to find the 'code' CLSID and the 'init'
  30. // property bag.
  31. #include "priv.h"
  32. //***
  33. // NOTES
  34. // perf: failure case is cheap, only does a RegOpen, no object creation.
  35. // positions to the 'Instance' part, must 'ChDir' to get to InitXxx part.
  36. HKEY GetInstKey(LPTSTR pszInst)
  37. {
  38. TCHAR szRegName[MAX_PATH]; // "CLSID/{instid}/Instance" size?
  39. // "CLSID/{instid}/Instance"
  40. ASSERT(ARRAYSIZE(szRegName) >= 5 + 1 + GUIDSTR_MAX - 1 + 1 + 8 + 1);
  41. ASSERT(lstrlen(pszInst) == GUIDSTR_MAX - 1);
  42. wnsprintf(szRegName, ARRAYSIZE(szRegName), TEXT("CLSID\\%s\\Instance"), pszInst);
  43. HKEY hk = NULL;
  44. RegOpenKeyEx(HKEY_CLASSES_ROOT, szRegName, 0, KEY_READ, &hk);
  45. return hk;
  46. }
  47. class CInstClassFactory : IClassFactory
  48. {
  49. public:
  50. // IUnknown
  51. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  52. STDMETHODIMP_(ULONG) AddRef(void);
  53. STDMETHODIMP_(ULONG) Release(void);
  54. // IClassFacotry
  55. STDMETHODIMP CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv);
  56. STDMETHODIMP LockServer(BOOL fLock);
  57. CInstClassFactory() { DllAddRef(); _cRef = 1; };
  58. HRESULT Init(REFCLSID rclsid);
  59. private:
  60. ~CInstClassFactory();
  61. LONG _cRef;
  62. HKEY _hkey; // hkey for instance info
  63. };
  64. // NOTES
  65. // called when class isn't in our sccls.c CCI table. we see if it's an
  66. // instance, and if so we make a stub for it that gives sufficient info
  67. // for our CreateInstance to create and init it.
  68. //
  69. // n.b. we keep the failure case as cheap as possible (just a regkey check,
  70. // no object creation etc.).
  71. //
  72. STDAPI CInstClassFactory_Create(REFCLSID rclsid, REFIID riid, void **ppv)
  73. {
  74. HRESULT hr = E_OUTOFMEMORY;
  75. CInstClassFactory *pcf = new CInstClassFactory();
  76. if (pcf)
  77. {
  78. hr = pcf->Init(rclsid);
  79. if (SUCCEEDED(hr))
  80. hr = pcf->QueryInterface(riid, ppv);
  81. pcf->Release();
  82. }
  83. return hr;
  84. }
  85. HRESULT CInstClassFactory::Init(REFCLSID rclsid)
  86. {
  87. ASSERT(_hkey == NULL); // only init me once please
  88. TCHAR szClass[GUIDSTR_MAX];
  89. // "CLSID/{instid}/Instance"
  90. SHStringFromGUID(rclsid, szClass, ARRAYSIZE(szClass));
  91. _hkey = GetInstKey(szClass);
  92. return _hkey ? S_OK : E_OUTOFMEMORY;
  93. }
  94. CInstClassFactory::~CInstClassFactory()
  95. {
  96. if (_hkey)
  97. RegCloseKey(_hkey);
  98. DllRelease();
  99. }
  100. ULONG CInstClassFactory::AddRef()
  101. {
  102. return InterlockedIncrement(&_cRef);
  103. }
  104. ULONG CInstClassFactory::Release()
  105. {
  106. if (InterlockedDecrement(&_cRef))
  107. return _cRef;
  108. delete this;
  109. return 0;
  110. }
  111. HRESULT CInstClassFactory::QueryInterface(REFIID riid, void **ppv)
  112. {
  113. static const QITAB qit[] = {
  114. QITABENT(CInstClassFactory, IClassFactory), // IID_IClassFactory
  115. { 0 },
  116. };
  117. return QISearch(this, qit, riid, ppv);
  118. }
  119. HRESULT CInstClassFactory::CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  120. {
  121. HRESULT hr = E_FAIL; // the usual optimism :-)
  122. *ppv = NULL;
  123. ASSERT(_hkey); // o.w. shouldn't ever get here
  124. // get object (vs. instance) CLSID and create it
  125. // AppCompat: the "RealGuide" explorer bar from Real Audio has an extraneous
  126. // double quote at the end of its CLSID value. This causes SHGetValue to fail
  127. // if given only an szClass[GUIDSTR_MAX] buffer, so we'll bump up the size.
  128. TCHAR szClass[GUIDSTR_MAX + 1];
  129. DWORD cbTmp = sizeof(szClass);
  130. DWORD err = SHGetValue(_hkey, NULL, TEXT("CLSID"), NULL, szClass, &cbTmp);
  131. hr = HRESULT_FROM_WIN32(err);
  132. if (SUCCEEDED(hr))
  133. {
  134. // If there's a useless char at the end of the guid, we'll truncate it
  135. // to avoid making assumptions about GUIDFromString. GUIDSTR_MAX includes
  136. // the null terminator, so szClass[GUIDSTR_MAX - 1] should always be 0
  137. // for a proper guid.
  138. szClass[GUIDSTR_MAX - 1] = 0;
  139. CLSID clsid;
  140. hr = GUIDFromString(szClass, &clsid) ? S_OK : E_FAIL;
  141. if (SUCCEEDED(hr))
  142. {
  143. IUnknown* pUnk;
  144. if (NOERROR == SHGetValue(_hkey, NULL, TEXT("LoadWithoutCOM"), NULL, NULL, NULL))
  145. hr = SHCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IUnknown, &pUnk));
  146. else
  147. hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IUnknown, &pUnk));
  148. if (SUCCEEDED(hr))
  149. {
  150. // try to load from propertybag first
  151. IPropertyBag *pbag;
  152. hr = SHCreatePropertyBagOnRegKey(_hkey, L"InitPropertyBag", STGM_READ, IID_PPV_ARG(IPropertyBag, &pbag));
  153. if (SUCCEEDED(hr))
  154. {
  155. hr = SHLoadFromPropertyBag(pUnk, pbag);
  156. pbag->Release();
  157. }
  158. // Did the property bag interface exist and did it load properly?
  159. if ( FAILED(hr))
  160. {
  161. // No property bag interface or did not load suyccessfully, try stream
  162. // Store this state temporarily, if stream fails too then we'll return the object
  163. // with this hr
  164. HRESULT hrPropBag = hr;
  165. IPersistStream* pPerStream;
  166. hr = pUnk->QueryInterface(IID_PPV_ARG(IPersistStream, &pPerStream));
  167. if (SUCCEEDED(hr))
  168. {
  169. IStream* pStream = SHOpenRegStream(_hkey, TEXT("InitStream"), NULL, STGM_READ);
  170. if (pStream)
  171. {
  172. hr = pPerStream->Load(pStream);
  173. pStream->Release();
  174. }
  175. else
  176. hr = E_FAIL;
  177. pPerStream->Release();
  178. }
  179. else
  180. hr = hrPropBag;
  181. }
  182. if (SUCCEEDED(hr))
  183. {
  184. hr = pUnk->QueryInterface(riid, ppv);
  185. }
  186. pUnk->Release();
  187. }
  188. }
  189. }
  190. return hr;
  191. }
  192. HRESULT CInstClassFactory::LockServer(BOOL fLock)
  193. {
  194. if (fLock)
  195. DllAddRef();
  196. else
  197. DllRelease();
  198. return S_OK;
  199. }