1 /* 2 * Shell Instance Objects - Add hot water and stir until dissolved. 3 * 4 * Copyright 2005 Michael Jung 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 /* 'Shell Instance Objects' allow you to add a node to the shell namespace 22 * (typically a shortcut to some location in the filesystem), just by setting 23 * some registry entries. This feature was introduced with win2k. Please 24 * search for 'Shell Instance Objects' on MSDN to get more information. */ 25 26 #include <stdarg.h> 27 28 #define COBJMACROS 29 30 #include "windef.h" 31 #include "winbase.h" 32 #include "winreg.h" 33 #include "objbase.h" 34 #include "oleauto.h" 35 36 #include "shdocvw.h" 37 38 #include "wine/debug.h" 39 40 WINE_DEFAULT_DEBUG_CHANNEL(shdocvw); 41 42 #define CHARS_IN_GUID 39 43 44 /****************************************************************************** 45 * RegistryPropertyBag 46 * 47 * Gives access to a registry key's values via the IPropertyBag interface. 48 */ 49 typedef struct _RegistryPropertyBag { 50 IPropertyBag IPropertyBag_iface; 51 LONG m_cRef; 52 HKEY m_hInitPropertyBagKey; 53 } RegistryPropertyBag; 54 55 static inline RegistryPropertyBag *impl_from_IPropertyBag(IPropertyBag *iface) 56 { 57 return CONTAINING_RECORD(iface, RegistryPropertyBag, IPropertyBag_iface); 58 } 59 60 static HRESULT WINAPI RegistryPropertyBag_IPropertyBag_QueryInterface(IPropertyBag *iface, 61 REFIID riid, void **ppv) 62 { 63 RegistryPropertyBag *This = impl_from_IPropertyBag(iface); 64 65 TRACE("(iface=%p, riid=%s, ppv=%p)\n", iface, debugstr_guid(riid), ppv); 66 67 if (!ppv) 68 return E_INVALIDARG; 69 70 if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IPropertyBag, riid)) { 71 *ppv = &This->IPropertyBag_iface; 72 } else { 73 *ppv = NULL; 74 return E_NOINTERFACE; 75 } 76 77 IUnknown_AddRef((IUnknown*)*ppv); 78 return S_OK; 79 } 80 81 static ULONG WINAPI RegistryPropertyBag_IPropertyBag_AddRef(IPropertyBag *iface) 82 { 83 RegistryPropertyBag *This = impl_from_IPropertyBag(iface); 84 ULONG cRef; 85 86 TRACE("(iface=%p)\n", iface); 87 88 cRef = InterlockedIncrement(&This->m_cRef); 89 90 if (cRef == 1) 91 SHDOCVW_LockModule(); 92 93 return cRef; 94 } 95 96 static ULONG WINAPI RegistryPropertyBag_IPropertyBag_Release(IPropertyBag *iface) 97 { 98 RegistryPropertyBag *This = impl_from_IPropertyBag(iface); 99 ULONG cRef; 100 101 TRACE("(iface=%p)\n", iface); 102 103 cRef = InterlockedDecrement(&This->m_cRef); 104 105 if (cRef == 0) { 106 TRACE("Destroying This=%p)\n", This); 107 RegCloseKey(This->m_hInitPropertyBagKey); 108 heap_free(This); 109 SHDOCVW_UnlockModule(); 110 } 111 112 return cRef; 113 } 114 115 static HRESULT WINAPI RegistryPropertyBag_IPropertyBag_Read(IPropertyBag *iface, 116 LPCOLESTR pwszPropName, VARIANT *pVar, IErrorLog *pErrorLog) 117 { 118 RegistryPropertyBag *This = impl_from_IPropertyBag(iface); 119 WCHAR *pwszValue; 120 DWORD dwType, cbData; 121 LONG res; 122 VARTYPE vtDst = V_VT(pVar); 123 HRESULT hr = S_OK; 124 125 TRACE("(iface=%p, pwszPropName=%s, pVar=%p, pErrorLog=%p)\n", iface, debugstr_w(pwszPropName), 126 pVar, pErrorLog); 127 128 res = RegQueryValueExW(This->m_hInitPropertyBagKey, pwszPropName, NULL, &dwType, NULL, &cbData); 129 if (res != ERROR_SUCCESS) 130 return E_INVALIDARG; 131 132 pwszValue = heap_alloc(cbData); 133 if (!pwszValue) 134 return E_OUTOFMEMORY; 135 136 res = RegQueryValueExW(This->m_hInitPropertyBagKey, pwszPropName, NULL, &dwType, 137 (LPBYTE)pwszValue, &cbData); 138 if (res != ERROR_SUCCESS) { 139 heap_free(pwszValue); 140 return E_INVALIDARG; 141 } 142 143 V_VT(pVar) = VT_BSTR; 144 V_BSTR(pVar) = SysAllocString(pwszValue); 145 heap_free(pwszValue); 146 147 if (vtDst != VT_BSTR) { 148 hr = VariantChangeTypeEx(pVar, pVar, LOCALE_SYSTEM_DEFAULT, 0, vtDst); 149 if (FAILED(hr)) 150 SysFreeString(V_BSTR(pVar)); 151 } 152 153 return hr; 154 } 155 156 static HRESULT WINAPI RegistryPropertyBag_IPropertyBag_Write(IPropertyBag *iface, 157 LPCOLESTR pwszPropName, VARIANT *pVar) 158 { 159 FIXME("(iface=%p, pwszPropName=%s, pVar=%p) stub\n", iface, debugstr_w(pwszPropName), pVar); 160 return E_NOTIMPL; 161 } 162 163 static const IPropertyBagVtbl RegistryPropertyBag_IPropertyBagVtbl = { 164 RegistryPropertyBag_IPropertyBag_QueryInterface, 165 RegistryPropertyBag_IPropertyBag_AddRef, 166 RegistryPropertyBag_IPropertyBag_Release, 167 RegistryPropertyBag_IPropertyBag_Read, 168 RegistryPropertyBag_IPropertyBag_Write 169 }; 170 171 static HRESULT RegistryPropertyBag_Constructor(HKEY hInitPropertyBagKey, REFIID riid, LPVOID *ppvObject) { 172 HRESULT hr = E_FAIL; 173 RegistryPropertyBag *pRegistryPropertyBag; 174 175 TRACE("(hInitPropertyBagKey=%p, riid=%s, ppvObject=%p)\n", hInitPropertyBagKey, 176 debugstr_guid(riid), ppvObject); 177 178 pRegistryPropertyBag = heap_alloc(sizeof(RegistryPropertyBag)); 179 if (pRegistryPropertyBag) { 180 pRegistryPropertyBag->IPropertyBag_iface.lpVtbl = &RegistryPropertyBag_IPropertyBagVtbl; 181 pRegistryPropertyBag->m_cRef = 0; 182 pRegistryPropertyBag->m_hInitPropertyBagKey = hInitPropertyBagKey; 183 184 /* The clasping AddRef/Release is for the case that QueryInterface fails, which will result 185 * in a reference count of 0 in the Release call, which will result in object destruction.*/ 186 IPropertyBag_AddRef(&pRegistryPropertyBag->IPropertyBag_iface); 187 hr = IPropertyBag_QueryInterface(&pRegistryPropertyBag->IPropertyBag_iface, riid, ppvObject); 188 IPropertyBag_Release(&pRegistryPropertyBag->IPropertyBag_iface); 189 } 190 191 return hr; 192 } 193 194 /****************************************************************************** 195 * InstanceObjectFactory 196 * Builds Instance Objects and asks them to initialize themselves based on the 197 * values of a PropertyBag. 198 */ 199 typedef struct _InstanceObjectFactory { 200 IClassFactory IClassFactory_iface; 201 LONG m_cRef; 202 CLSID m_clsidInstance; /* CLSID of the objects to create. */ 203 IPropertyBag *m_pPropertyBag; /* PropertyBag to initialize those objects. */ 204 } InstanceObjectFactory; 205 206 static inline InstanceObjectFactory *impl_from_IClassFactory(IClassFactory *iface) 207 { 208 return CONTAINING_RECORD(iface, InstanceObjectFactory, IClassFactory_iface); 209 } 210 211 static HRESULT WINAPI InstanceObjectFactory_IClassFactory_QueryInterface(IClassFactory *iface, 212 REFIID riid, void **ppv) 213 { 214 InstanceObjectFactory *This = impl_from_IClassFactory(iface); 215 216 TRACE("iface=%p, riid=%s, ppv=%p)\n", iface, debugstr_guid(riid), ppv); 217 218 if (!ppv) 219 return E_INVALIDARG; 220 221 if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IClassFactory, riid)) { 222 *ppv = &This->IClassFactory_iface; 223 } else { 224 *ppv = NULL; 225 return E_NOINTERFACE; 226 } 227 228 IUnknown_AddRef((IUnknown*)*ppv); 229 return S_OK; 230 } 231 232 static ULONG WINAPI InstanceObjectFactory_IClassFactory_AddRef(IClassFactory *iface) 233 { 234 InstanceObjectFactory *This = impl_from_IClassFactory(iface); 235 ULONG cRef; 236 237 TRACE("(iface=%p)\n", iface); 238 239 cRef = InterlockedIncrement(&This->m_cRef); 240 241 if (cRef == 1) 242 IClassFactory_LockServer(iface, TRUE); 243 244 return cRef; 245 } 246 247 static ULONG WINAPI InstanceObjectFactory_IClassFactory_Release(IClassFactory *iface) 248 { 249 InstanceObjectFactory *This = impl_from_IClassFactory(iface); 250 ULONG cRef; 251 252 TRACE("(iface=%p)\n", iface); 253 254 cRef = InterlockedDecrement(&This->m_cRef); 255 256 if (cRef == 0) { 257 IClassFactory_LockServer(iface, FALSE); 258 IPropertyBag_Release(This->m_pPropertyBag); 259 heap_free(This); 260 } 261 262 return cRef; 263 } 264 265 static HRESULT WINAPI InstanceObjectFactory_IClassFactory_CreateInstance(IClassFactory *iface, 266 IUnknown *pUnkOuter, REFIID riid, LPVOID *ppvObj) 267 { 268 InstanceObjectFactory *This = impl_from_IClassFactory(iface); 269 IPersistPropertyBag *pPersistPropertyBag; 270 HRESULT hr; 271 272 TRACE("(pUnkOuter=%p, riid=%s, ppvObj=%p)\n", pUnkOuter, debugstr_guid(riid), ppvObj); 273 274 hr = CoCreateInstance(&This->m_clsidInstance, NULL, CLSCTX_INPROC_SERVER, 275 &IID_IPersistPropertyBag, (LPVOID*)&pPersistPropertyBag); 276 if (FAILED(hr)) { 277 TRACE("Failed to create instance of %s. hr = %08x\n", 278 debugstr_guid(&This->m_clsidInstance), hr); 279 return hr; 280 } 281 282 hr = IPersistPropertyBag_Load(pPersistPropertyBag, This->m_pPropertyBag, NULL); 283 if (FAILED(hr)) { 284 TRACE("Failed to initialize object from PropertyBag: hr = %08x\n", hr); 285 IPersistPropertyBag_Release(pPersistPropertyBag); 286 return hr; 287 } 288 289 hr = IPersistPropertyBag_QueryInterface(pPersistPropertyBag, riid, ppvObj); 290 IPersistPropertyBag_Release(pPersistPropertyBag); 291 292 return hr; 293 } 294 295 static HRESULT WINAPI InstanceObjectFactory_IClassFactory_LockServer(IClassFactory *iface, 296 BOOL fLock) 297 { 298 TRACE("(iface=%p, fLock=%d) stub\n", iface, fLock); 299 300 if (fLock) 301 SHDOCVW_LockModule(); 302 else 303 SHDOCVW_UnlockModule(); 304 305 return S_OK; 306 } 307 308 static const IClassFactoryVtbl InstanceObjectFactory_IClassFactoryVtbl = { 309 InstanceObjectFactory_IClassFactory_QueryInterface, 310 InstanceObjectFactory_IClassFactory_AddRef, 311 InstanceObjectFactory_IClassFactory_Release, 312 InstanceObjectFactory_IClassFactory_CreateInstance, 313 InstanceObjectFactory_IClassFactory_LockServer 314 }; 315 316 static HRESULT InstanceObjectFactory_Constructor(REFCLSID rclsid, IPropertyBag *pPropertyBag, 317 REFIID riid, LPVOID *ppvObject) 318 { 319 InstanceObjectFactory *pInstanceObjectFactory; 320 HRESULT hr = E_FAIL; 321 322 TRACE("(RegistryPropertyBag=%p, riid=%s, ppvObject=%p)\n", pPropertyBag, 323 debugstr_guid(riid), ppvObject); 324 325 pInstanceObjectFactory = heap_alloc(sizeof(InstanceObjectFactory)); 326 if (pInstanceObjectFactory) { 327 pInstanceObjectFactory->IClassFactory_iface.lpVtbl = &InstanceObjectFactory_IClassFactoryVtbl; 328 pInstanceObjectFactory->m_cRef = 0; 329 pInstanceObjectFactory->m_clsidInstance = *rclsid; 330 pInstanceObjectFactory->m_pPropertyBag = pPropertyBag; 331 IPropertyBag_AddRef(pPropertyBag); 332 333 IClassFactory_AddRef(&pInstanceObjectFactory->IClassFactory_iface); 334 hr = IClassFactory_QueryInterface(&pInstanceObjectFactory->IClassFactory_iface, 335 riid, ppvObject); 336 IClassFactory_Release(&pInstanceObjectFactory->IClassFactory_iface); 337 } 338 339 return hr; 340 } 341 342 /****************************************************************************** 343 * SHDOCVW_GetShellInstanceObjectClassObject [Internal] 344 * 345 * Figure if there is a 'Shell Instance Object' conformant registry entry for 346 * the given CLSID and if so create and return a corresponding ClassObject. 347 * 348 * PARAMS 349 * rclsid [I] CLSID of the 'Shell Instance Object'. 350 * riid [I] Desired interface. Only IClassFactory supported. 351 * ppvClassObj [O] The corresponding ClassObject. 352 * 353 * RETURNS 354 * Success: S_OK, 355 * Failure: CLASS_E_CLASSNOTAVAILABLE 356 */ 357 HRESULT SHDOCVW_GetShellInstanceObjectClassObject(REFCLSID rclsid, REFIID riid, 358 LPVOID *ppvClassObj) 359 { 360 WCHAR wszInstanceKey[] = { 'C','L','S','I','D','\\','{','0','0','0','0','0','0','0','0','-', 361 '0','0','0','0','-','0','0','0','0','-','0','0','0','0','-','0','0','0','0','0','0','0','0', 362 '0','0','0','0','}','\\','I','n','s','t','a','n','c','e', 0 }; 363 static const WCHAR wszCLSID[] = { 'C','L','S','I','D',0 }; 364 static const WCHAR wszInitPropertyBag[] = 365 { 'I','n','i','t','P','r','o','p','e','r','t','y','B','a','g',0 }; 366 WCHAR wszCLSIDInstance[CHARS_IN_GUID]; 367 CLSID clsidInstance; 368 HKEY hInstanceKey, hInitPropertyBagKey; 369 DWORD dwType, cbBytes = sizeof(wszCLSIDInstance); 370 IPropertyBag *pInitPropertyBag; 371 HRESULT hr; 372 LONG res; 373 374 TRACE("(rclsid=%s, riid=%s, ppvClassObject=%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), 375 ppvClassObj); 376 377 /* Figure if there is an 'Instance' subkey for the given CLSID and acquire a handle. */ 378 if (!StringFromGUID2(rclsid, wszInstanceKey + 6, CHARS_IN_GUID)) 379 return CLASS_E_CLASSNOTAVAILABLE; 380 wszInstanceKey[5+CHARS_IN_GUID] = '\\'; /* Repair the null-termination. */ 381 if (ERROR_SUCCESS != RegOpenKeyExW(HKEY_CLASSES_ROOT, wszInstanceKey, 0, KEY_READ, &hInstanceKey)) 382 /* If there is no 'Instance' subkey, then it's not a Shell Instance Object. */ 383 return CLASS_E_CLASSNOTAVAILABLE; 384 385 if (ERROR_SUCCESS != RegQueryValueExW(hInstanceKey, wszCLSID, NULL, &dwType, (LPBYTE)wszCLSIDInstance, &cbBytes) || 386 FAILED(CLSIDFromString(wszCLSIDInstance, &clsidInstance))) 387 { 388 /* 'Instance' should have a 'CLSID' value with a well-formed clsid-string. */ 389 FIXME("Failed to infer instance CLSID! %s\n", debugstr_w(wszCLSIDInstance)); 390 RegCloseKey(hInstanceKey); 391 return CLASS_E_CLASSNOTAVAILABLE; 392 } 393 394 /* Try to open the 'InitPropertyBag' subkey. */ 395 res = RegOpenKeyExW(hInstanceKey, wszInitPropertyBag, 0, KEY_READ, &hInitPropertyBagKey); 396 RegCloseKey(hInstanceKey); 397 if (res != ERROR_SUCCESS) { 398 /* Besides 'InitPropertyBag's, shell instance objects might be initialized by streams. 399 * So this case might not be an error. */ 400 TRACE("No InitPropertyBag key found!\n"); 401 return CLASS_E_CLASSNOTAVAILABLE; 402 } 403 404 /* If the construction succeeds, the new RegistryPropertyBag is responsible for closing 405 * hInitPropertyBagKey. */ 406 hr = RegistryPropertyBag_Constructor(hInitPropertyBagKey, &IID_IPropertyBag, 407 (LPVOID*)&pInitPropertyBag); 408 if (FAILED(hr)) { 409 RegCloseKey(hInitPropertyBagKey); 410 return hr; 411 } 412 413 /* Construct an Instance Object Factory, which creates objects of class 'clsidInstance' 414 * and asks them to initialize themselves with the help of the 'pInitiPropertyBag' */ 415 hr = InstanceObjectFactory_Constructor(&clsidInstance, pInitPropertyBag, riid, ppvClassObj); 416 IPropertyBag_Release(pInitPropertyBag); /* The factory will hold a reference the bag. */ 417 418 return hr; 419 } 420