| /* |
| * 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 <stdarg.h> |
| |
| #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 ADJUST_THIS(c,m,p) ((c*)(((long)p)-(long)&(((c*)0)->lp##m##Vtbl))) |
| #define STATIC_CAST(i,p) ((i*)&p->lp##i##Vtbl) |
| #define CHARS_IN_GUID 39 |
| |
| /****************************************************************************** |
| * RegistryPropertyBag |
| * |
| * Gives access to a registry key's values via the IPropertyBag interface. |
| */ |
| typedef struct _RegistryPropertyBag { |
| const IPropertyBagVtbl *lpIPropertyBagVtbl; |
| LONG m_cRef; |
| HKEY m_hInitPropertyBagKey; |
| } RegistryPropertyBag; |
| |
| static void RegistryPropertyBag_Destroy(RegistryPropertyBag *This) { |
| TRACE("This=%p)\n", This); |
| |
| RegCloseKey(This->m_hInitPropertyBagKey); |
| heap_free(This); |
| } |
| |
| static HRESULT WINAPI RegistryPropertyBag_IPropertyBag_QueryInterface(IPropertyBag *iface, |
| REFIID riid, void **ppv) |
| { |
| RegistryPropertyBag *This = ADJUST_THIS(RegistryPropertyBag, 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 = STATIC_CAST(IPropertyBag, This); |
| } else { |
| *ppv = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| IUnknown_AddRef((IUnknown*)*ppv); |
| return S_OK; |
| } |
| |
| static ULONG WINAPI RegistryPropertyBag_IPropertyBag_AddRef(IPropertyBag *iface) { |
| RegistryPropertyBag *This = ADJUST_THIS(RegistryPropertyBag, 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 = ADJUST_THIS(RegistryPropertyBag, IPropertyBag, iface); |
| ULONG cRef; |
| |
| TRACE("(iface=%p)\n", iface); |
| |
| cRef = InterlockedDecrement(&This->m_cRef); |
| |
| if (cRef == 0) { |
| RegistryPropertyBag_Destroy(This); |
| SHDOCVW_UnlockModule(); |
| } |
| |
| return cRef; |
| } |
| |
| static HRESULT WINAPI RegistryPropertyBag_IPropertyBag_Read(IPropertyBag *iface, |
| LPCOLESTR pwszPropName, VARIANT *pVar, IErrorLog *pErrorLog) |
| { |
| RegistryPropertyBag *This = ADJUST_THIS(RegistryPropertyBag, 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->lpIPropertyBagVtbl = &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(STATIC_CAST(IPropertyBag, pRegistryPropertyBag)); |
| hr = IPropertyBag_QueryInterface(STATIC_CAST(IPropertyBag, pRegistryPropertyBag), riid, ppvObject); |
| IPropertyBag_Release(STATIC_CAST(IPropertyBag, pRegistryPropertyBag)); |
| } |
| |
| return hr; |
| } |
| |
| /****************************************************************************** |
| * InstanceObjectFactory |
| * Builds Instance Objects and asks them to initialize themselves based on the |
| * values of a PropertyBag. |
| */ |
| typedef struct _InstanceObjectFactory { |
| const IClassFactoryVtbl *lpIClassFactoryVtbl; |
| LONG m_cRef; |
| CLSID m_clsidInstance; /* CLSID of the objects to create. */ |
| IPropertyBag *m_pPropertyBag; /* PropertyBag to initialize those objects. */ |
| } InstanceObjectFactory; |
| |
| static void InstanceObjectFactory_Destroy(InstanceObjectFactory *This) { |
| IPropertyBag_Release(This->m_pPropertyBag); |
| heap_free(This); |
| } |
| |
| static HRESULT WINAPI InstanceObjectFactory_IClassFactory_QueryInterface(IClassFactory *iface, |
| REFIID riid, LPVOID* ppv) |
| { |
| InstanceObjectFactory *This = ADJUST_THIS(InstanceObjectFactory, 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 = STATIC_CAST(IClassFactory, This); |
| } else { |
| *ppv = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| IUnknown_AddRef((IUnknown*)*ppv); |
| return S_OK; |
| } |
| |
| static ULONG WINAPI InstanceObjectFactory_IClassFactory_AddRef(IClassFactory *iface) |
| { |
| InstanceObjectFactory *This = ADJUST_THIS(InstanceObjectFactory, 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 = ADJUST_THIS(InstanceObjectFactory, IClassFactory, iface); |
| ULONG cRef; |
| |
| TRACE("(iface=%p)\n", iface); |
| |
| cRef = InterlockedDecrement(&This->m_cRef); |
| |
| if (cRef == 0) { |
| IClassFactory_LockServer(iface, FALSE); |
| InstanceObjectFactory_Destroy(This); |
| } |
| |
| return cRef; |
| } |
| |
| static HRESULT WINAPI InstanceObjectFactory_IClassFactory_CreateInstance(IClassFactory *iface, |
| IUnknown *pUnkOuter, REFIID riid, LPVOID *ppvObj) |
| { |
| InstanceObjectFactory *This = ADJUST_THIS(InstanceObjectFactory, 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 ProperyBag: 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->lpIClassFactoryVtbl = &InstanceObjectFactory_IClassFactoryVtbl; |
| pInstanceObjectFactory->m_cRef = 0; |
| pInstanceObjectFactory->m_clsidInstance = *rclsid; |
| pInstanceObjectFactory->m_pPropertyBag = pPropertyBag; |
| IPropertyBag_AddRef(pPropertyBag); |
| |
| IClassFactory_AddRef(STATIC_CAST(IClassFactory, pInstanceObjectFactory)); |
| hr = IClassFactory_QueryInterface(STATIC_CAST(IClassFactory, pInstanceObjectFactory), |
| riid, ppvObject); |
| IClassFactory_Release(STATIC_CAST(IClassFactory, pInstanceObjectFactory)); |
| } |
| |
| 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 }; |
| const WCHAR wszCLSID[] = { 'C','L','S','I','D',0 }; |
| 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) || |
| !(wszInstanceKey[5+CHARS_IN_GUID]='\\') || /* Repair the null-termination. */ |
| 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 (RegQueryValueExW(hInstanceKey, wszCLSID, NULL, &dwType, (LPBYTE)wszCLSIDInstance, &cbBytes) |
| != ERROR_SUCCESS || 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 |
| * hInitProperyBagKey. */ |
| 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; |
| } |