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