| /* |
| * standard IPropertyStore implementation |
| * |
| * Copyright 2012 Vincent Povirk for CodeWeavers |
| * |
| * 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 |
| */ |
| |
| #define COBJMACROS |
| #include "config.h" |
| |
| #include <stdarg.h> |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "objbase.h" |
| #include "rpcproxy.h" |
| #include "propsys.h" |
| #include "wine/debug.h" |
| #include "wine/unicode.h" |
| #include "wine/list.h" |
| |
| #include "initguid.h" |
| #include "propsys_private.h" |
| |
| DEFINE_GUID(FMTID_NamedProperties, 0xd5cdd505, 0x2e9c, 0x101b, 0x93, 0x97, 0x08, 0x00, 0x2b, 0x2c, 0xf9, 0xae); |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(propsys); |
| |
| typedef struct { |
| struct list entry; |
| DWORD pid; |
| PROPVARIANT propvar; |
| PSC_STATE state; |
| } propstore_value; |
| |
| typedef struct { |
| struct list entry; |
| GUID fmtid; |
| struct list values; /* list of struct propstore_value */ |
| DWORD count; |
| } propstore_format; |
| |
| typedef struct { |
| IPropertyStoreCache IPropertyStoreCache_iface; |
| LONG ref; |
| CRITICAL_SECTION lock; |
| struct list formats; /* list of struct propstore_format */ |
| } PropertyStore; |
| |
| static inline PropertyStore *impl_from_IPropertyStoreCache(IPropertyStoreCache *iface) |
| { |
| return CONTAINING_RECORD(iface, PropertyStore, IPropertyStoreCache_iface); |
| } |
| |
| static HRESULT WINAPI PropertyStore_QueryInterface(IPropertyStoreCache *iface, REFIID iid, |
| void **ppv) |
| { |
| PropertyStore *This = impl_from_IPropertyStoreCache(iface); |
| TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv); |
| |
| if (!ppv) return E_INVALIDARG; |
| |
| if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IPropertyStore, iid) || |
| IsEqualIID(&IID_IPropertyStoreCache, iid)) |
| { |
| *ppv = &This->IPropertyStoreCache_iface; |
| } |
| else |
| { |
| FIXME("No interface for %s\n", debugstr_guid(iid)); |
| *ppv = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| IUnknown_AddRef((IUnknown*)*ppv); |
| return S_OK; |
| } |
| |
| static ULONG WINAPI PropertyStore_AddRef(IPropertyStoreCache *iface) |
| { |
| PropertyStore *This = impl_from_IPropertyStoreCache(iface); |
| ULONG ref = InterlockedIncrement(&This->ref); |
| |
| TRACE("(%p) refcount=%u\n", iface, ref); |
| |
| return ref; |
| } |
| |
| static void destroy_format(propstore_format *format) |
| { |
| propstore_value *cursor, *cursor2; |
| LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &format->values, propstore_value, entry) |
| { |
| PropVariantClear(&cursor->propvar); |
| HeapFree(GetProcessHeap(), 0, cursor); |
| } |
| HeapFree(GetProcessHeap(), 0, format); |
| } |
| |
| static ULONG WINAPI PropertyStore_Release(IPropertyStoreCache *iface) |
| { |
| PropertyStore *This = impl_from_IPropertyStoreCache(iface); |
| ULONG ref = InterlockedDecrement(&This->ref); |
| |
| TRACE("(%p) refcount=%u\n", iface, ref); |
| |
| if (ref == 0) |
| { |
| propstore_format *cursor, *cursor2; |
| This->lock.DebugInfo->Spare[0] = 0; |
| DeleteCriticalSection(&This->lock); |
| LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &This->formats, propstore_format, entry) |
| destroy_format(cursor); |
| HeapFree(GetProcessHeap(), 0, This); |
| } |
| |
| return ref; |
| } |
| |
| static HRESULT WINAPI PropertyStore_GetCount(IPropertyStoreCache *iface, |
| DWORD *cProps) |
| { |
| PropertyStore *This = impl_from_IPropertyStoreCache(iface); |
| propstore_format *format; |
| |
| TRACE("%p,%p\n", iface, cProps); |
| |
| if (!cProps) |
| return E_POINTER; |
| |
| *cProps = 0; |
| |
| EnterCriticalSection(&This->lock); |
| |
| LIST_FOR_EACH_ENTRY(format, &This->formats, propstore_format, entry) |
| *cProps += format->count; |
| |
| LeaveCriticalSection(&This->lock); |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI PropertyStore_GetAt(IPropertyStoreCache *iface, |
| DWORD iProp, PROPERTYKEY *pkey) |
| { |
| PropertyStore *This = impl_from_IPropertyStoreCache(iface); |
| propstore_format *format=NULL, *format_candidate; |
| propstore_value *value; |
| HRESULT hr; |
| |
| TRACE("%p,%d,%p\n", iface, iProp, pkey); |
| |
| if (!pkey) |
| return E_POINTER; |
| |
| EnterCriticalSection(&This->lock); |
| |
| LIST_FOR_EACH_ENTRY(format_candidate, &This->formats, propstore_format, entry) |
| { |
| if (format_candidate->count > iProp) |
| { |
| format = format_candidate; |
| pkey->fmtid = format->fmtid; |
| break; |
| } |
| |
| iProp -= format_candidate->count; |
| } |
| |
| if (format) |
| { |
| LIST_FOR_EACH_ENTRY(value, &format->values, propstore_value, entry) |
| { |
| if (iProp == 0) |
| { |
| pkey->pid = value->pid; |
| break; |
| } |
| |
| iProp--; |
| } |
| |
| hr = S_OK; |
| } |
| else |
| hr = E_INVALIDARG; |
| |
| LeaveCriticalSection(&This->lock); |
| |
| return hr; |
| } |
| |
| static HRESULT PropertyStore_LookupValue(PropertyStore *This, REFPROPERTYKEY key, |
| BOOL insert, propstore_value **result) |
| { |
| propstore_format *format=NULL, *format_candidate; |
| propstore_value *value=NULL, *value_candidate; |
| |
| if (IsEqualGUID(&key->fmtid, &FMTID_NamedProperties)) |
| { |
| /* This is used in the property store format [MS-PROPSTORE] |
| * for named values and probably gets special treatment. */ |
| ERR("don't know how to handle FMTID_NamedProperties\n"); |
| return E_FAIL; |
| } |
| |
| LIST_FOR_EACH_ENTRY(format_candidate, &This->formats, propstore_format, entry) |
| { |
| if (IsEqualGUID(&format_candidate->fmtid, &key->fmtid)) |
| { |
| format = format_candidate; |
| break; |
| } |
| } |
| |
| if (!format) |
| { |
| if (!insert) |
| return TYPE_E_ELEMENTNOTFOUND; |
| |
| format = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*format)); |
| if (!format) |
| return E_OUTOFMEMORY; |
| |
| format->fmtid = key->fmtid; |
| list_init(&format->values); |
| list_add_tail(&This->formats, &format->entry); |
| } |
| |
| LIST_FOR_EACH_ENTRY(value_candidate, &format->values, propstore_value, entry) |
| { |
| if (value_candidate->pid == key->pid) |
| { |
| value = value_candidate; |
| break; |
| } |
| } |
| |
| if (!value) |
| { |
| if (!insert) |
| return TYPE_E_ELEMENTNOTFOUND; |
| |
| value = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*value)); |
| if (!value) |
| return E_OUTOFMEMORY; |
| |
| value->pid = key->pid; |
| list_add_tail(&format->values, &value->entry); |
| format->count++; |
| } |
| |
| *result = value; |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI PropertyStore_GetValue(IPropertyStoreCache *iface, |
| REFPROPERTYKEY key, PROPVARIANT *pv) |
| { |
| PropertyStore *This = impl_from_IPropertyStoreCache(iface); |
| propstore_value *value; |
| HRESULT hr; |
| |
| TRACE("%p,%p,%p\n", iface, key, pv); |
| |
| if (!pv) |
| return E_POINTER; |
| |
| EnterCriticalSection(&This->lock); |
| |
| hr = PropertyStore_LookupValue(This, key, FALSE, &value); |
| |
| if (SUCCEEDED(hr)) |
| hr = PropVariantCopy(pv, &value->propvar); |
| else if (hr == TYPE_E_ELEMENTNOTFOUND) |
| { |
| PropVariantInit(pv); |
| hr = S_OK; |
| } |
| |
| LeaveCriticalSection(&This->lock); |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI PropertyStore_SetValue(IPropertyStoreCache *iface, |
| REFPROPERTYKEY key, REFPROPVARIANT propvar) |
| { |
| PropertyStore *This = impl_from_IPropertyStoreCache(iface); |
| propstore_value *value; |
| HRESULT hr; |
| PROPVARIANT temp; |
| |
| TRACE("%p,%p,%p\n", iface, key, propvar); |
| |
| EnterCriticalSection(&This->lock); |
| |
| hr = PropertyStore_LookupValue(This, key, TRUE, &value); |
| |
| if (SUCCEEDED(hr)) |
| hr = PropVariantCopy(&temp, propvar); |
| |
| if (SUCCEEDED(hr)) |
| { |
| PropVariantClear(&value->propvar); |
| value->propvar = temp; |
| } |
| |
| LeaveCriticalSection(&This->lock); |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI PropertyStore_Commit(IPropertyStoreCache *iface) |
| { |
| FIXME("%p: stub\n", iface); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI PropertyStore_GetState(IPropertyStoreCache *iface, |
| REFPROPERTYKEY key, PSC_STATE *pstate) |
| { |
| PropertyStore *This = impl_from_IPropertyStoreCache(iface); |
| propstore_value *value; |
| HRESULT hr; |
| |
| TRACE("%p,%p,%p\n", iface, key, pstate); |
| |
| EnterCriticalSection(&This->lock); |
| |
| hr = PropertyStore_LookupValue(This, key, FALSE, &value); |
| |
| if (SUCCEEDED(hr)) |
| *pstate = value->state; |
| |
| LeaveCriticalSection(&This->lock); |
| |
| if (FAILED(hr)) |
| *pstate = PSC_NORMAL; |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI PropertyStore_GetValueAndState(IPropertyStoreCache *iface, |
| REFPROPERTYKEY key, PROPVARIANT *ppropvar, PSC_STATE *pstate) |
| { |
| PropertyStore *This = impl_from_IPropertyStoreCache(iface); |
| propstore_value *value; |
| HRESULT hr; |
| |
| TRACE("%p,%p,%p,%p\n", iface, key, ppropvar, pstate); |
| |
| EnterCriticalSection(&This->lock); |
| |
| hr = PropertyStore_LookupValue(This, key, FALSE, &value); |
| |
| if (SUCCEEDED(hr)) |
| hr = PropVariantCopy(ppropvar, &value->propvar); |
| |
| if (SUCCEEDED(hr)) |
| *pstate = value->state; |
| |
| LeaveCriticalSection(&This->lock); |
| |
| if (FAILED(hr)) |
| { |
| PropVariantInit(ppropvar); |
| *pstate = PSC_NORMAL; |
| } |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI PropertyStore_SetState(IPropertyStoreCache *iface, |
| REFPROPERTYKEY key, PSC_STATE pstate) |
| { |
| PropertyStore *This = impl_from_IPropertyStoreCache(iface); |
| propstore_value *value; |
| HRESULT hr; |
| |
| TRACE("%p,%p,%d\n", iface, key, pstate); |
| |
| EnterCriticalSection(&This->lock); |
| |
| hr = PropertyStore_LookupValue(This, key, FALSE, &value); |
| |
| if (SUCCEEDED(hr)) |
| value->state = pstate; |
| |
| LeaveCriticalSection(&This->lock); |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI PropertyStore_SetValueAndState(IPropertyStoreCache *iface, |
| REFPROPERTYKEY key, const PROPVARIANT *ppropvar, PSC_STATE state) |
| { |
| PropertyStore *This = impl_from_IPropertyStoreCache(iface); |
| propstore_value *value; |
| HRESULT hr; |
| PROPVARIANT temp; |
| |
| TRACE("%p,%p,%p,%d\n", iface, key, ppropvar, state); |
| |
| EnterCriticalSection(&This->lock); |
| |
| hr = PropertyStore_LookupValue(This, key, TRUE, &value); |
| |
| if (SUCCEEDED(hr)) |
| hr = PropVariantCopy(&temp, ppropvar); |
| |
| if (SUCCEEDED(hr)) |
| { |
| PropVariantClear(&value->propvar); |
| value->propvar = temp; |
| value->state = state; |
| } |
| |
| LeaveCriticalSection(&This->lock); |
| |
| return hr; |
| } |
| |
| static const IPropertyStoreCacheVtbl PropertyStore_Vtbl = { |
| PropertyStore_QueryInterface, |
| PropertyStore_AddRef, |
| PropertyStore_Release, |
| PropertyStore_GetCount, |
| PropertyStore_GetAt, |
| PropertyStore_GetValue, |
| PropertyStore_SetValue, |
| PropertyStore_Commit, |
| PropertyStore_GetState, |
| PropertyStore_GetValueAndState, |
| PropertyStore_SetState, |
| PropertyStore_SetValueAndState |
| }; |
| |
| HRESULT PropertyStore_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv) |
| { |
| PropertyStore *This; |
| HRESULT ret; |
| |
| TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppv); |
| |
| *ppv = NULL; |
| |
| if (pUnkOuter) return CLASS_E_NOAGGREGATION; |
| |
| This = HeapAlloc(GetProcessHeap(), 0, sizeof(PropertyStore)); |
| if (!This) return E_OUTOFMEMORY; |
| |
| This->IPropertyStoreCache_iface.lpVtbl = &PropertyStore_Vtbl; |
| This->ref = 1; |
| InitializeCriticalSection(&This->lock); |
| This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PropertyStore.lock"); |
| list_init(&This->formats); |
| |
| ret = IPropertyStoreCache_QueryInterface(&This->IPropertyStoreCache_iface, iid, ppv); |
| IPropertyStoreCache_Release(&This->IPropertyStoreCache_iface); |
| |
| return ret; |
| } |