/* * Shell Instance Objects - Add hot water and stir until dissolved. * * Copyright 2005 Michael Jung * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ /* 'Shell Instance Objects' allow you to add a node to the shell namespace * (typically a shortcut to some location in the filesystem), just by setting * some registry entries. This feature was introduced with win2k. Please * search for 'Shell Instance Objects' on MSDN to get more information. */ #include #define COBJMACROS #include "windef.h" #include "winbase.h" #include "winreg.h" #include "objbase.h" #include "oleauto.h" #include "shdocvw.h" #include "wine/unicode.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(shdocvw); #define CHARS_IN_GUID 39 /****************************************************************************** * RegistryPropertyBag * * Gives access to a registry key's values via the IPropertyBag interface. */ typedef struct _RegistryPropertyBag { IPropertyBag IPropertyBag_iface; LONG m_cRef; HKEY m_hInitPropertyBagKey; } RegistryPropertyBag; static inline RegistryPropertyBag *impl_from_IPropertyBag(IPropertyBag *iface) { return CONTAINING_RECORD(iface, RegistryPropertyBag, IPropertyBag_iface); } static HRESULT WINAPI RegistryPropertyBag_IPropertyBag_QueryInterface(IPropertyBag *iface, REFIID riid, void **ppv) { RegistryPropertyBag *This = impl_from_IPropertyBag(iface); TRACE("(iface=%p, riid=%s, ppv=%p)\n", iface, debugstr_guid(riid), ppv); if (!ppv) return E_INVALIDARG; if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IPropertyBag, riid)) { *ppv = &This->IPropertyBag_iface; } else { *ppv = NULL; return E_NOINTERFACE; } IUnknown_AddRef((IUnknown*)*ppv); return S_OK; } static ULONG WINAPI RegistryPropertyBag_IPropertyBag_AddRef(IPropertyBag *iface) { RegistryPropertyBag *This = impl_from_IPropertyBag(iface); ULONG cRef; TRACE("(iface=%p)\n", iface); cRef = InterlockedIncrement(&This->m_cRef); if (cRef == 1) SHDOCVW_LockModule(); return cRef; } static ULONG WINAPI RegistryPropertyBag_IPropertyBag_Release(IPropertyBag *iface) { RegistryPropertyBag *This = impl_from_IPropertyBag(iface); ULONG cRef; TRACE("(iface=%p)\n", iface); cRef = InterlockedDecrement(&This->m_cRef); if (cRef == 0) { TRACE("Destroying This=%p)\n", This); RegCloseKey(This->m_hInitPropertyBagKey); heap_free(This); SHDOCVW_UnlockModule(); } return cRef; } static HRESULT WINAPI RegistryPropertyBag_IPropertyBag_Read(IPropertyBag *iface, LPCOLESTR pwszPropName, VARIANT *pVar, IErrorLog *pErrorLog) { RegistryPropertyBag *This = impl_from_IPropertyBag(iface); WCHAR *pwszValue; DWORD dwType, cbData; LONG res; VARTYPE vtDst = V_VT(pVar); HRESULT hr = S_OK; TRACE("(iface=%p, pwszPropName=%s, pVar=%p, pErrorLog=%p)\n", iface, debugstr_w(pwszPropName), pVar, pErrorLog); res = RegQueryValueExW(This->m_hInitPropertyBagKey, pwszPropName, NULL, &dwType, NULL, &cbData); if (res != ERROR_SUCCESS) return E_INVALIDARG; pwszValue = heap_alloc(cbData); if (!pwszValue) return E_OUTOFMEMORY; res = RegQueryValueExW(This->m_hInitPropertyBagKey, pwszPropName, NULL, &dwType, (LPBYTE)pwszValue, &cbData); if (res != ERROR_SUCCESS) { heap_free(pwszValue); return E_INVALIDARG; } V_VT(pVar) = VT_BSTR; V_BSTR(pVar) = SysAllocString(pwszValue); heap_free(pwszValue); if (vtDst != VT_BSTR) { hr = VariantChangeTypeEx(pVar, pVar, LOCALE_SYSTEM_DEFAULT, 0, vtDst); if (FAILED(hr)) SysFreeString(V_BSTR(pVar)); } return hr; } static HRESULT WINAPI RegistryPropertyBag_IPropertyBag_Write(IPropertyBag *iface, LPCOLESTR pwszPropName, VARIANT *pVar) { FIXME("(iface=%p, pwszPropName=%s, pVar=%p) stub\n", iface, debugstr_w(pwszPropName), pVar); return E_NOTIMPL; } static const IPropertyBagVtbl RegistryPropertyBag_IPropertyBagVtbl = { RegistryPropertyBag_IPropertyBag_QueryInterface, RegistryPropertyBag_IPropertyBag_AddRef, RegistryPropertyBag_IPropertyBag_Release, RegistryPropertyBag_IPropertyBag_Read, RegistryPropertyBag_IPropertyBag_Write }; static HRESULT RegistryPropertyBag_Constructor(HKEY hInitPropertyBagKey, REFIID riid, LPVOID *ppvObject) { HRESULT hr = E_FAIL; RegistryPropertyBag *pRegistryPropertyBag; TRACE("(hInitPropertyBagKey=%p, riid=%s, ppvObject=%p)\n", hInitPropertyBagKey, debugstr_guid(riid), ppvObject); pRegistryPropertyBag = heap_alloc(sizeof(RegistryPropertyBag)); if (pRegistryPropertyBag) { pRegistryPropertyBag->IPropertyBag_iface.lpVtbl = &RegistryPropertyBag_IPropertyBagVtbl; pRegistryPropertyBag->m_cRef = 0; pRegistryPropertyBag->m_hInitPropertyBagKey = hInitPropertyBagKey; /* The clasping AddRef/Release is for the case that QueryInterface fails, which will result * in a reference count of 0 in the Release call, which will result in object destruction.*/ IPropertyBag_AddRef(&pRegistryPropertyBag->IPropertyBag_iface); hr = IPropertyBag_QueryInterface(&pRegistryPropertyBag->IPropertyBag_iface, riid, ppvObject); IPropertyBag_Release(&pRegistryPropertyBag->IPropertyBag_iface); } return hr; } /****************************************************************************** * InstanceObjectFactory * Builds Instance Objects and asks them to initialize themselves based on the * values of a PropertyBag. */ typedef struct _InstanceObjectFactory { IClassFactory IClassFactory_iface; LONG m_cRef; CLSID m_clsidInstance; /* CLSID of the objects to create. */ IPropertyBag *m_pPropertyBag; /* PropertyBag to initialize those objects. */ } InstanceObjectFactory; static inline InstanceObjectFactory *impl_from_IClassFactory(IClassFactory *iface) { return CONTAINING_RECORD(iface, InstanceObjectFactory, IClassFactory_iface); } static HRESULT WINAPI InstanceObjectFactory_IClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, void **ppv) { InstanceObjectFactory *This = impl_from_IClassFactory(iface); TRACE("iface=%p, riid=%s, ppv=%p)\n", iface, debugstr_guid(riid), ppv); if (!ppv) return E_INVALIDARG; if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IClassFactory, riid)) { *ppv = &This->IClassFactory_iface; } else { *ppv = NULL; return E_NOINTERFACE; } IUnknown_AddRef((IUnknown*)*ppv); return S_OK; } static ULONG WINAPI InstanceObjectFactory_IClassFactory_AddRef(IClassFactory *iface) { InstanceObjectFactory *This = impl_from_IClassFactory(iface); ULONG cRef; TRACE("(iface=%p)\n", iface); cRef = InterlockedIncrement(&This->m_cRef); if (cRef == 1) IClassFactory_LockServer(iface, TRUE); return cRef; } static ULONG WINAPI InstanceObjectFactory_IClassFactory_Release(IClassFactory *iface) { InstanceObjectFactory *This = impl_from_IClassFactory(iface); ULONG cRef; TRACE("(iface=%p)\n", iface); cRef = InterlockedDecrement(&This->m_cRef); if (cRef == 0) { IClassFactory_LockServer(iface, FALSE); IPropertyBag_Release(This->m_pPropertyBag); heap_free(This); } return cRef; } static HRESULT WINAPI InstanceObjectFactory_IClassFactory_CreateInstance(IClassFactory *iface, IUnknown *pUnkOuter, REFIID riid, LPVOID *ppvObj) { InstanceObjectFactory *This = impl_from_IClassFactory(iface); IPersistPropertyBag *pPersistPropertyBag; HRESULT hr; TRACE("(pUnkOuter=%p, riid=%s, ppvObj=%p)\n", pUnkOuter, debugstr_guid(riid), ppvObj); hr = CoCreateInstance(&This->m_clsidInstance, NULL, CLSCTX_INPROC_SERVER, &IID_IPersistPropertyBag, (LPVOID*)&pPersistPropertyBag); if (FAILED(hr)) { TRACE("Failed to create instance of %s. hr = %08x\n", debugstr_guid(&This->m_clsidInstance), hr); return hr; } hr = IPersistPropertyBag_Load(pPersistPropertyBag, This->m_pPropertyBag, NULL); if (FAILED(hr)) { TRACE("Failed to initialize object from PropertyBag: hr = %08x\n", hr); IPersistPropertyBag_Release(pPersistPropertyBag); return hr; } hr = IPersistPropertyBag_QueryInterface(pPersistPropertyBag, riid, ppvObj); IPersistPropertyBag_Release(pPersistPropertyBag); return hr; } static HRESULT WINAPI InstanceObjectFactory_IClassFactory_LockServer(IClassFactory *iface, BOOL fLock) { TRACE("(iface=%p, fLock=%d) stub\n", iface, fLock); if (fLock) SHDOCVW_LockModule(); else SHDOCVW_UnlockModule(); return S_OK; } static const IClassFactoryVtbl InstanceObjectFactory_IClassFactoryVtbl = { InstanceObjectFactory_IClassFactory_QueryInterface, InstanceObjectFactory_IClassFactory_AddRef, InstanceObjectFactory_IClassFactory_Release, InstanceObjectFactory_IClassFactory_CreateInstance, InstanceObjectFactory_IClassFactory_LockServer }; static HRESULT InstanceObjectFactory_Constructor(REFCLSID rclsid, IPropertyBag *pPropertyBag, REFIID riid, LPVOID *ppvObject) { InstanceObjectFactory *pInstanceObjectFactory; HRESULT hr = E_FAIL; TRACE("(RegistryPropertyBag=%p, riid=%s, ppvObject=%p)\n", pPropertyBag, debugstr_guid(riid), ppvObject); pInstanceObjectFactory = heap_alloc(sizeof(InstanceObjectFactory)); if (pInstanceObjectFactory) { pInstanceObjectFactory->IClassFactory_iface.lpVtbl = &InstanceObjectFactory_IClassFactoryVtbl; pInstanceObjectFactory->m_cRef = 0; pInstanceObjectFactory->m_clsidInstance = *rclsid; pInstanceObjectFactory->m_pPropertyBag = pPropertyBag; IPropertyBag_AddRef(pPropertyBag); IClassFactory_AddRef(&pInstanceObjectFactory->IClassFactory_iface); hr = IClassFactory_QueryInterface(&pInstanceObjectFactory->IClassFactory_iface, riid, ppvObject); IClassFactory_Release(&pInstanceObjectFactory->IClassFactory_iface); } return hr; } /****************************************************************************** * SHDOCVW_GetShellInstanceObjectClassObject [Internal] * * Figure if there is a 'Shell Instance Object' conformant registry entry for * the given CLSID and if so create and return a corresponding ClassObject. * * PARAMS * rclsid [I] CLSID of the 'Shell Instance Object'. * riid [I] Desired interface. Only IClassFactory supported. * ppvClassObj [O] The corresponding ClassObject. * * RETURNS * Success: S_OK, * Failure: CLASS_E_CLASSNOTAVAILABLE */ HRESULT SHDOCVW_GetShellInstanceObjectClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvClassObj) { WCHAR wszInstanceKey[] = { 'C','L','S','I','D','\\','{','0','0','0','0','0','0','0','0','-', '0','0','0','0','-','0','0','0','0','-','0','0','0','0','-','0','0','0','0','0','0','0','0', '0','0','0','0','}','\\','I','n','s','t','a','n','c','e', 0 }; static const WCHAR wszCLSID[] = { 'C','L','S','I','D',0 }; static const WCHAR wszInitPropertyBag[] = { 'I','n','i','t','P','r','o','p','e','r','t','y','B','a','g',0 }; WCHAR wszCLSIDInstance[CHARS_IN_GUID]; CLSID clsidInstance; HKEY hInstanceKey, hInitPropertyBagKey; DWORD dwType, cbBytes = sizeof(wszCLSIDInstance); IPropertyBag *pInitPropertyBag; HRESULT hr; LONG res; TRACE("(rclsid=%s, riid=%s, ppvClassObject=%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppvClassObj); /* Figure if there is an 'Instance' subkey for the given CLSID and acquire a handle. */ if (!StringFromGUID2(rclsid, wszInstanceKey + 6, CHARS_IN_GUID)) return CLASS_E_CLASSNOTAVAILABLE; wszInstanceKey[5+CHARS_IN_GUID] = '\\'; /* Repair the null-termination. */ if (ERROR_SUCCESS != RegOpenKeyExW(HKEY_CLASSES_ROOT, wszInstanceKey, 0, KEY_READ, &hInstanceKey)) /* If there is no 'Instance' subkey, then it's not a Shell Instance Object. */ return CLASS_E_CLASSNOTAVAILABLE; if (ERROR_SUCCESS != RegQueryValueExW(hInstanceKey, wszCLSID, NULL, &dwType, (LPBYTE)wszCLSIDInstance, &cbBytes) || FAILED(CLSIDFromString(wszCLSIDInstance, &clsidInstance))) { /* 'Instance' should have a 'CLSID' value with a well-formed clsid-string. */ FIXME("Failed to infer instance CLSID! %s\n", debugstr_w(wszCLSIDInstance)); RegCloseKey(hInstanceKey); return CLASS_E_CLASSNOTAVAILABLE; } /* Try to open the 'InitPropertyBag' subkey. */ res = RegOpenKeyExW(hInstanceKey, wszInitPropertyBag, 0, KEY_READ, &hInitPropertyBagKey); RegCloseKey(hInstanceKey); if (res != ERROR_SUCCESS) { /* Besides 'InitPropertyBag's, shell instance objects might be initialized by streams. * So this case might not be an error. */ TRACE("No InitPropertyBag key found!\n"); return CLASS_E_CLASSNOTAVAILABLE; } /* If the construction succeeds, the new RegistryPropertyBag is responsible for closing * hInitPropertyBagKey. */ hr = RegistryPropertyBag_Constructor(hInitPropertyBagKey, &IID_IPropertyBag, (LPVOID*)&pInitPropertyBag); if (FAILED(hr)) { RegCloseKey(hInitPropertyBagKey); return hr; } /* Construct an Instance Object Factory, which creates objects of class 'clsidInstance' * and asks them to initialize themselves with the help of the 'pInitiPropertyBag' */ hr = InstanceObjectFactory_Constructor(&clsidInstance, pInitPropertyBag, riid, ppvClassObj); IPropertyBag_Release(pInitPropertyBag); /* The factory will hold a reference the bag. */ return hr; }