Leaked source code of windows server 2003
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.

243 lines
7.9 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. HKEY hk = NULL;
  43. HRESULT hr = StringCchPrintf(szRegName, ARRAYSIZE(szRegName), TEXT("CLSID\\%s\\Instance"), pszInst);
  44. if (SUCCEEDED(hr))
  45. {
  46. RegOpenKeyEx(HKEY_CLASSES_ROOT, szRegName, 0, KEY_QUERY_VALUE, &hk);
  47. }
  48. return hk;
  49. }
  50. class CInstClassFactory : IClassFactory
  51. {
  52. public:
  53. // IUnknown
  54. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  55. STDMETHODIMP_(ULONG) AddRef(void);
  56. STDMETHODIMP_(ULONG) Release(void);
  57. // IClassFacotry
  58. STDMETHODIMP CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv);
  59. STDMETHODIMP LockServer(BOOL fLock);
  60. CInstClassFactory() { DllAddRef(); _cRef = 1; };
  61. HRESULT Init(REFCLSID rclsid);
  62. private:
  63. ~CInstClassFactory();
  64. LONG _cRef;
  65. HKEY _hkey; // hkey for instance info
  66. };
  67. // NOTES
  68. // called when class isn't in our sccls.c CCI table. we see if it's an
  69. // instance, and if so we make a stub for it that gives sufficient info
  70. // for our CreateInstance to create and init it.
  71. //
  72. // n.b. we keep the failure case as cheap as possible (just a regkey check,
  73. // no object creation etc.).
  74. //
  75. STDAPI CInstClassFactory_Create(REFCLSID rclsid, REFIID riid, void **ppv)
  76. {
  77. HRESULT hr = E_OUTOFMEMORY;
  78. CInstClassFactory *pcf = new CInstClassFactory();
  79. if (pcf)
  80. {
  81. hr = pcf->Init(rclsid);
  82. if (SUCCEEDED(hr))
  83. hr = pcf->QueryInterface(riid, ppv);
  84. pcf->Release();
  85. }
  86. return hr;
  87. }
  88. HRESULT CInstClassFactory::Init(REFCLSID rclsid)
  89. {
  90. ASSERT(_hkey == NULL); // only init me once please
  91. TCHAR szClass[GUIDSTR_MAX];
  92. // "CLSID/{instid}/Instance"
  93. SHStringFromGUID(rclsid, szClass, ARRAYSIZE(szClass));
  94. _hkey = GetInstKey(szClass);
  95. return _hkey ? S_OK : E_OUTOFMEMORY;
  96. }
  97. CInstClassFactory::~CInstClassFactory()
  98. {
  99. if (_hkey)
  100. RegCloseKey(_hkey);
  101. DllRelease();
  102. }
  103. ULONG CInstClassFactory::AddRef()
  104. {
  105. return InterlockedIncrement(&_cRef);
  106. }
  107. ULONG CInstClassFactory::Release()
  108. {
  109. ASSERT( 0 != _cRef );
  110. ULONG cRef = InterlockedDecrement(&_cRef);
  111. if ( 0 == cRef )
  112. {
  113. delete this;
  114. }
  115. return cRef;
  116. }
  117. HRESULT CInstClassFactory::QueryInterface(REFIID riid, void **ppv)
  118. {
  119. static const QITAB qit[] = {
  120. QITABENT(CInstClassFactory, IClassFactory), // IID_IClassFactory
  121. { 0 },
  122. };
  123. return QISearch(this, qit, riid, ppv);
  124. }
  125. HRESULT CInstClassFactory::CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  126. {
  127. HRESULT hr = E_FAIL; // the usual optimism :-)
  128. *ppv = NULL;
  129. ASSERT(_hkey); // o.w. shouldn't ever get here
  130. // get object (vs. instance) CLSID and create it
  131. // AppCompat: the "RealGuide" explorer bar from Real Audio has an extraneous
  132. // double quote at the end of its CLSID value. This causes SHGetValue to fail
  133. // if given only an szClass[GUIDSTR_MAX] buffer, so we'll bump up the size.
  134. TCHAR szClass[GUIDSTR_MAX + 1];
  135. DWORD cbTmp = sizeof(szClass);
  136. DWORD err = SHGetValue(_hkey, NULL, TEXT("CLSID"), NULL, szClass, &cbTmp);
  137. hr = HRESULT_FROM_WIN32(err);
  138. if (SUCCEEDED(hr))
  139. {
  140. // If there's a useless char at the end of the guid, we'll truncate it
  141. // to avoid making assumptions about GUIDFromString. GUIDSTR_MAX includes
  142. // the null terminator, so szClass[GUIDSTR_MAX - 1] should always be 0
  143. // for a proper guid.
  144. szClass[GUIDSTR_MAX - 1] = 0;
  145. CLSID clsid;
  146. hr = GUIDFromString(szClass, &clsid) ? S_OK : E_FAIL;
  147. if (SUCCEEDED(hr))
  148. {
  149. IUnknown* pUnk;
  150. if (NOERROR == SHGetValue(_hkey, NULL, TEXT("LoadWithoutCOM"), NULL, NULL, NULL))
  151. hr = SHCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IUnknown, &pUnk));
  152. else
  153. hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IUnknown, &pUnk));
  154. if (SUCCEEDED(hr))
  155. {
  156. // try to load from propertybag first
  157. IPropertyBag *pbag;
  158. hr = SHCreatePropertyBagOnRegKey(_hkey, L"InitPropertyBag", STGM_READ, IID_PPV_ARG(IPropertyBag, &pbag));
  159. if (SUCCEEDED(hr))
  160. {
  161. hr = SHLoadFromPropertyBag(pUnk, pbag);
  162. pbag->Release();
  163. }
  164. // Did the property bag interface exist and did it load properly?
  165. if ( FAILED(hr))
  166. {
  167. // No property bag interface or did not load suyccessfully, try stream
  168. // Store this state temporarily, if stream fails too then we'll return the object
  169. // with this hr
  170. HRESULT hrPropBag = hr;
  171. IPersistStream* pPerStream;
  172. hr = pUnk->QueryInterface(IID_PPV_ARG(IPersistStream, &pPerStream));
  173. if (SUCCEEDED(hr))
  174. {
  175. IStream* pStream = SHOpenRegStream(_hkey, TEXT("InitStream"), NULL, STGM_READ);
  176. if (pStream)
  177. {
  178. hr = pPerStream->Load(pStream);
  179. pStream->Release();
  180. }
  181. else
  182. hr = E_FAIL;
  183. pPerStream->Release();
  184. }
  185. else
  186. hr = hrPropBag;
  187. }
  188. if (SUCCEEDED(hr))
  189. {
  190. hr = pUnk->QueryInterface(riid, ppv);
  191. }
  192. pUnk->Release();
  193. }
  194. }
  195. }
  196. return hr;
  197. }
  198. HRESULT CInstClassFactory::LockServer(BOOL fLock)
  199. {
  200. if (fLock)
  201. DllAddRef();
  202. else
  203. DllRelease();
  204. return S_OK;
  205. }