xref: /reactos/dll/win32/propsys/propstore.c (revision 527f2f90)
1 /*
2  * standard IPropertyStore implementation
3  *
4  * Copyright 2012 Vincent Povirk for CodeWeavers
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 #include "propsys_private.h"
22 
23 #include <wine/list.h>
24 
25 #include <initguid.h>
26 DEFINE_GUID(FMTID_NamedProperties, 0xd5cdd505, 0x2e9c, 0x101b, 0x93, 0x97, 0x08, 0x00, 0x2b, 0x2c, 0xf9, 0xae);
27 
28 typedef struct {
29     struct list entry;
30     DWORD pid;
31     PROPVARIANT propvar;
32     PSC_STATE state;
33 } propstore_value;
34 
35 typedef struct {
36     struct list entry;
37     GUID fmtid;
38     struct list values; /* list of struct propstore_value */
39     DWORD count;
40 } propstore_format;
41 
42 typedef struct {
43     IPropertyStoreCache IPropertyStoreCache_iface;
44     LONG ref;
45     CRITICAL_SECTION lock;
46     struct list formats; /* list of struct propstore_format */
47 } PropertyStore;
48 
49 static inline PropertyStore *impl_from_IPropertyStoreCache(IPropertyStoreCache *iface)
50 {
51     return CONTAINING_RECORD(iface, PropertyStore, IPropertyStoreCache_iface);
52 }
53 
54 static HRESULT WINAPI PropertyStore_QueryInterface(IPropertyStoreCache *iface, REFIID iid,
55     void **ppv)
56 {
57     PropertyStore *This = impl_from_IPropertyStoreCache(iface);
58     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
59 
60     if (!ppv) return E_INVALIDARG;
61 
62     if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IPropertyStore, iid) ||
63         IsEqualIID(&IID_IPropertyStoreCache, iid))
64     {
65         *ppv = &This->IPropertyStoreCache_iface;
66     }
67     else
68     {
69         FIXME("No interface for %s\n", debugstr_guid(iid));
70         *ppv = NULL;
71         return E_NOINTERFACE;
72     }
73 
74     IUnknown_AddRef((IUnknown*)*ppv);
75     return S_OK;
76 }
77 
78 static ULONG WINAPI PropertyStore_AddRef(IPropertyStoreCache *iface)
79 {
80     PropertyStore *This = impl_from_IPropertyStoreCache(iface);
81     ULONG ref = InterlockedIncrement(&This->ref);
82 
83     TRACE("(%p) refcount=%u\n", iface, ref);
84 
85     return ref;
86 }
87 
88 static void destroy_format(propstore_format *format)
89 {
90     propstore_value *cursor, *cursor2;
91     LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &format->values, propstore_value, entry)
92     {
93         PropVariantClear(&cursor->propvar);
94         HeapFree(GetProcessHeap(), 0, cursor);
95     }
96     HeapFree(GetProcessHeap(), 0, format);
97 }
98 
99 static ULONG WINAPI PropertyStore_Release(IPropertyStoreCache *iface)
100 {
101     PropertyStore *This = impl_from_IPropertyStoreCache(iface);
102     ULONG ref = InterlockedDecrement(&This->ref);
103 
104     TRACE("(%p) refcount=%u\n", iface, ref);
105 
106     if (ref == 0)
107     {
108         propstore_format *cursor, *cursor2;
109         This->lock.DebugInfo->Spare[0] = 0;
110         DeleteCriticalSection(&This->lock);
111         LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &This->formats, propstore_format, entry)
112             destroy_format(cursor);
113         HeapFree(GetProcessHeap(), 0, This);
114     }
115 
116     return ref;
117 }
118 
119 static HRESULT WINAPI PropertyStore_GetCount(IPropertyStoreCache *iface,
120     DWORD *cProps)
121 {
122     PropertyStore *This = impl_from_IPropertyStoreCache(iface);
123     propstore_format *format;
124 
125     TRACE("%p,%p\n", iface, cProps);
126 
127     if (!cProps)
128         return E_POINTER;
129 
130     *cProps = 0;
131 
132     EnterCriticalSection(&This->lock);
133 
134     LIST_FOR_EACH_ENTRY(format, &This->formats, propstore_format, entry)
135         *cProps += format->count;
136 
137     LeaveCriticalSection(&This->lock);
138 
139     return S_OK;
140 }
141 
142 static HRESULT WINAPI PropertyStore_GetAt(IPropertyStoreCache *iface,
143     DWORD iProp, PROPERTYKEY *pkey)
144 {
145     PropertyStore *This = impl_from_IPropertyStoreCache(iface);
146     propstore_format *format=NULL, *format_candidate;
147     propstore_value *value;
148     HRESULT hr;
149 
150     TRACE("%p,%d,%p\n", iface, iProp, pkey);
151 
152     if (!pkey)
153         return E_POINTER;
154 
155     EnterCriticalSection(&This->lock);
156 
157     LIST_FOR_EACH_ENTRY(format_candidate, &This->formats, propstore_format, entry)
158     {
159         if (format_candidate->count > iProp)
160         {
161             format = format_candidate;
162             pkey->fmtid = format->fmtid;
163             break;
164         }
165 
166         iProp -= format_candidate->count;
167     }
168 
169     if (format)
170     {
171         LIST_FOR_EACH_ENTRY(value, &format->values, propstore_value, entry)
172         {
173             if (iProp == 0)
174             {
175                 pkey->pid = value->pid;
176                 break;
177             }
178 
179             iProp--;
180         }
181 
182         hr = S_OK;
183     }
184     else
185         hr = E_INVALIDARG;
186 
187     LeaveCriticalSection(&This->lock);
188 
189     return hr;
190 }
191 
192 static HRESULT PropertyStore_LookupValue(PropertyStore *This, REFPROPERTYKEY key,
193     int insert, propstore_value **result)
194 {
195     propstore_format *format=NULL, *format_candidate;
196     propstore_value *value=NULL, *value_candidate;
197 
198     if (IsEqualGUID(&key->fmtid, &FMTID_NamedProperties))
199     {
200         /* This is used in the property store format [MS-PROPSTORE]
201          * for named values and probably gets special treatment. */
202         ERR("don't know how to handle FMTID_NamedProperties\n");
203         return E_FAIL;
204     }
205 
206     LIST_FOR_EACH_ENTRY(format_candidate, &This->formats, propstore_format, entry)
207     {
208         if (IsEqualGUID(&format_candidate->fmtid, &key->fmtid))
209         {
210             format = format_candidate;
211             break;
212         }
213     }
214 
215     if (!format)
216     {
217         if (!insert)
218             return TYPE_E_ELEMENTNOTFOUND;
219 
220         format = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*format));
221         if (!format)
222             return E_OUTOFMEMORY;
223 
224         format->fmtid = key->fmtid;
225         list_init(&format->values);
226         list_add_tail(&This->formats, &format->entry);
227     }
228 
229     LIST_FOR_EACH_ENTRY(value_candidate, &format->values, propstore_value, entry)
230     {
231         if (value_candidate->pid == key->pid)
232         {
233             value = value_candidate;
234             break;
235         }
236     }
237 
238     if (!value)
239     {
240         if (!insert)
241             return TYPE_E_ELEMENTNOTFOUND;
242 
243         value = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*value));
244         if (!value)
245             return E_OUTOFMEMORY;
246 
247         value->pid = key->pid;
248         list_add_tail(&format->values, &value->entry);
249         format->count++;
250     }
251 
252     *result = value;
253 
254     return S_OK;
255 }
256 
257 static HRESULT WINAPI PropertyStore_GetValue(IPropertyStoreCache *iface,
258     REFPROPERTYKEY key, PROPVARIANT *pv)
259 {
260     PropertyStore *This = impl_from_IPropertyStoreCache(iface);
261     propstore_value *value;
262     HRESULT hr;
263 
264     TRACE("%p,%p,%p\n", iface, key, pv);
265 
266     if (!pv)
267         return E_POINTER;
268 
269     EnterCriticalSection(&This->lock);
270 
271     hr = PropertyStore_LookupValue(This, key, 0, &value);
272 
273     if (SUCCEEDED(hr))
274         hr = PropVariantCopy(pv, &value->propvar);
275     else if (hr == TYPE_E_ELEMENTNOTFOUND)
276     {
277         PropVariantInit(pv);
278         hr = S_OK;
279     }
280 
281     LeaveCriticalSection(&This->lock);
282 
283     return hr;
284 }
285 
286 static HRESULT WINAPI PropertyStore_SetValue(IPropertyStoreCache *iface,
287     REFPROPERTYKEY key, REFPROPVARIANT propvar)
288 {
289     PropertyStore *This = impl_from_IPropertyStoreCache(iface);
290     propstore_value *value;
291     HRESULT hr;
292     PROPVARIANT temp;
293 
294     TRACE("%p,%p,%p\n", iface, key, propvar);
295 
296     EnterCriticalSection(&This->lock);
297 
298     hr = PropertyStore_LookupValue(This, key, 1, &value);
299 
300     if (SUCCEEDED(hr))
301         hr = PropVariantCopy(&temp, propvar);
302 
303     if (SUCCEEDED(hr))
304     {
305         PropVariantClear(&value->propvar);
306         value->propvar = temp;
307     }
308 
309     LeaveCriticalSection(&This->lock);
310 
311     return hr;
312 }
313 
314 static HRESULT WINAPI PropertyStore_Commit(IPropertyStoreCache *iface)
315 {
316     FIXME("%p: stub\n", iface);
317     return S_OK;
318 }
319 
320 static HRESULT WINAPI PropertyStore_GetState(IPropertyStoreCache *iface,
321     REFPROPERTYKEY key, PSC_STATE *pstate)
322 {
323     PropertyStore *This = impl_from_IPropertyStoreCache(iface);
324     propstore_value *value;
325     HRESULT hr;
326 
327     TRACE("%p,%p,%p\n", iface, key, pstate);
328 
329     EnterCriticalSection(&This->lock);
330 
331     hr = PropertyStore_LookupValue(This, key, 0, &value);
332 
333     if (SUCCEEDED(hr))
334         *pstate = value->state;
335 
336     LeaveCriticalSection(&This->lock);
337 
338     if (FAILED(hr))
339         *pstate = PSC_NORMAL;
340 
341     return hr;
342 }
343 
344 static HRESULT WINAPI PropertyStore_GetValueAndState(IPropertyStoreCache *iface,
345     REFPROPERTYKEY key, PROPVARIANT *ppropvar, PSC_STATE *pstate)
346 {
347     PropertyStore *This = impl_from_IPropertyStoreCache(iface);
348     propstore_value *value;
349     HRESULT hr;
350 
351     TRACE("%p,%p,%p,%p\n", iface, key, ppropvar, pstate);
352 
353     EnterCriticalSection(&This->lock);
354 
355     hr = PropertyStore_LookupValue(This, key, 0, &value);
356 
357     if (SUCCEEDED(hr))
358         hr = PropVariantCopy(ppropvar, &value->propvar);
359 
360     if (SUCCEEDED(hr))
361         *pstate = value->state;
362 
363     LeaveCriticalSection(&This->lock);
364 
365     if (FAILED(hr))
366     {
367         PropVariantInit(ppropvar);
368         *pstate = PSC_NORMAL;
369     }
370 
371     return hr;
372 }
373 
374 static HRESULT WINAPI PropertyStore_SetState(IPropertyStoreCache *iface,
375     REFPROPERTYKEY key, PSC_STATE pstate)
376 {
377     PropertyStore *This = impl_from_IPropertyStoreCache(iface);
378     propstore_value *value;
379     HRESULT hr;
380 
381     TRACE("%p,%p,%d\n", iface, key, pstate);
382 
383     EnterCriticalSection(&This->lock);
384 
385     hr = PropertyStore_LookupValue(This, key, 0, &value);
386 
387     if (SUCCEEDED(hr))
388         value->state = pstate;
389 
390     LeaveCriticalSection(&This->lock);
391 
392     return hr;
393 }
394 
395 static HRESULT WINAPI PropertyStore_SetValueAndState(IPropertyStoreCache *iface,
396     REFPROPERTYKEY key, const PROPVARIANT *ppropvar, PSC_STATE state)
397 {
398     PropertyStore *This = impl_from_IPropertyStoreCache(iface);
399     propstore_value *value;
400     HRESULT hr;
401     PROPVARIANT temp;
402 
403     TRACE("%p,%p,%p,%d\n", iface, key, ppropvar, state);
404 
405     EnterCriticalSection(&This->lock);
406 
407     hr = PropertyStore_LookupValue(This, key, 1, &value);
408 
409     if (SUCCEEDED(hr))
410         hr = PropVariantCopy(&temp, ppropvar);
411 
412     if (SUCCEEDED(hr))
413     {
414         PropVariantClear(&value->propvar);
415         value->propvar = temp;
416         value->state = state;
417     }
418 
419     LeaveCriticalSection(&This->lock);
420 
421     return hr;
422 }
423 
424 static const IPropertyStoreCacheVtbl PropertyStore_Vtbl = {
425     PropertyStore_QueryInterface,
426     PropertyStore_AddRef,
427     PropertyStore_Release,
428     PropertyStore_GetCount,
429     PropertyStore_GetAt,
430     PropertyStore_GetValue,
431     PropertyStore_SetValue,
432     PropertyStore_Commit,
433     PropertyStore_GetState,
434     PropertyStore_GetValueAndState,
435     PropertyStore_SetState,
436     PropertyStore_SetValueAndState
437 };
438 
439 HRESULT PropertyStore_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
440 {
441     PropertyStore *This;
442     HRESULT ret;
443 
444     TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppv);
445 
446     *ppv = NULL;
447 
448     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
449 
450     This = HeapAlloc(GetProcessHeap(), 0, sizeof(PropertyStore));
451     if (!This) return E_OUTOFMEMORY;
452 
453     This->IPropertyStoreCache_iface.lpVtbl = &PropertyStore_Vtbl;
454     This->ref = 1;
455     InitializeCriticalSection(&This->lock);
456     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PropertyStore.lock");
457     list_init(&This->formats);
458 
459     ret = IPropertyStoreCache_QueryInterface(&This->IPropertyStoreCache_iface, iid, ppv);
460     IPropertyStoreCache_Release(&This->IPropertyStoreCache_iface);
461 
462     return ret;
463 }
464