mshtml: Implement MediaQueryList's matches prop.
[wine.git] / dlls / ole32 / stg_prop.c
blob8efb8ed55436769ffe890f08e265d06f9c034bf5
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
12 * Copyright 2005 Juan Lang
13 * Copyright 2006 Mike McCormack
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 * TODO:
30 * - I don't honor the maximum property set size.
31 * - Certain bogus files could result in reading past the end of a buffer.
32 * - Mac-generated files won't be read correctly, even if they're little
33 * endian, because I disregard whether the generator was a Mac. This means
34 * strings will probably be munged (as I don't understand Mac scripts.)
35 * - Not all PROPVARIANT types are supported.
36 * - User defined properties are not supported, see comment in
37 * PropertyStorage_ReadFromStream
40 #include <assert.h>
41 #include <stdarg.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
46 #define COBJMACROS
47 #define NONAMELESSUNION
49 #include "windef.h"
50 #include "winbase.h"
51 #include "winnls.h"
52 #include "winuser.h"
53 #include "wine/asm.h"
54 #include "wine/debug.h"
55 #include "wine/heap.h"
56 #include "dictionary.h"
57 #include "storage32.h"
58 #include "oleauto.h"
60 WINE_DEFAULT_DEBUG_CHANNEL(storage);
62 static inline StorageImpl *impl_from_IPropertySetStorage( IPropertySetStorage *iface )
64 return CONTAINING_RECORD(iface, StorageImpl, base.IPropertySetStorage_iface);
67 /* These are documented in MSDN,
68 * but they don't seem to be in any header file.
70 #define PROPSETHDR_BYTEORDER_MAGIC 0xfffe
71 #define PROPSETHDR_OSVER_KIND_WIN16 0
72 #define PROPSETHDR_OSVER_KIND_MAC 1
73 #define PROPSETHDR_OSVER_KIND_WIN32 2
75 #define CP_UNICODE 1200
77 #define MAX_VERSION_0_PROP_NAME_LENGTH 256
79 #define CFTAG_WINDOWS (-1L)
80 #define CFTAG_MACINTOSH (-2L)
81 #define CFTAG_FMTID (-3L)
82 #define CFTAG_NODATA 0L
84 #define ALIGNED_LENGTH(_Len, _Align) (((_Len)+(_Align))&~(_Align))
86 typedef struct tagPROPERTYSETHEADER
88 WORD wByteOrder; /* always 0xfffe */
89 WORD wFormat; /* can be zero or one */
90 DWORD dwOSVer; /* OS version of originating system */
91 CLSID clsid; /* application CLSID */
92 DWORD reserved; /* always 1 */
93 } PROPERTYSETHEADER;
95 typedef struct tagFORMATIDOFFSET
97 FMTID fmtid;
98 DWORD dwOffset; /* from beginning of stream */
99 } FORMATIDOFFSET;
101 typedef struct tagPROPERTYSECTIONHEADER
103 DWORD cbSection;
104 DWORD cProperties;
105 } PROPERTYSECTIONHEADER;
107 typedef struct tagPROPERTYIDOFFSET
109 DWORD propid;
110 DWORD dwOffset; /* from beginning of section */
111 } PROPERTYIDOFFSET;
113 typedef struct tagPropertyStorage_impl PropertyStorage_impl;
115 /* Initializes the property storage from the stream (and undoes any uncommitted
116 * changes in the process.) Returns an error if there is an error reading or
117 * if the stream format doesn't match what's expected.
119 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *);
121 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *);
123 /* Creates the dictionaries used by the property storage. If successful, all
124 * the dictionaries have been created. If failed, none has been. (This makes
125 * it a bit easier to deal with destroying them.)
127 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *);
129 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *);
131 /* Copies from propvar to prop. If propvar's type is VT_LPSTR, copies the
132 * string using PropertyStorage_StringCopy.
134 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
135 const PROPVARIANT *propvar, UINT targetCP, UINT srcCP);
137 /* Copies the string src, which is encoded using code page srcCP, and returns
138 * it in *dst, in the code page specified by targetCP. The returned string is
139 * allocated using CoTaskMemAlloc.
140 * If srcCP is CP_UNICODE, src is in fact an LPCWSTR. Similarly, if targetCP
141 * is CP_UNICODE, the returned string is in fact an LPWSTR.
142 * Returns S_OK on success, something else on failure.
144 static HRESULT PropertyStorage_StringCopy(LPCSTR src, UINT srcCP, LPSTR *dst,
145 UINT targetCP);
147 static const IPropertyStorageVtbl IPropertyStorage_Vtbl;
149 /***********************************************************************
150 * Implementation of IPropertyStorage
152 struct tagPropertyStorage_impl
154 IPropertyStorage IPropertyStorage_iface;
155 LONG ref;
156 CRITICAL_SECTION cs;
157 IStream *stm;
158 BOOL dirty;
159 FMTID fmtid;
160 CLSID clsid;
161 WORD format;
162 DWORD originatorOS;
163 DWORD grfFlags;
164 DWORD grfMode;
165 UINT codePage;
166 LCID locale;
167 PROPID highestProp;
168 struct dictionary *name_to_propid;
169 struct dictionary *propid_to_name;
170 struct dictionary *propid_to_prop;
173 static inline PropertyStorage_impl *impl_from_IPropertyStorage(IPropertyStorage *iface)
175 return CONTAINING_RECORD(iface, PropertyStorage_impl, IPropertyStorage_iface);
178 struct enum_stat_prop_stg
180 IEnumSTATPROPSTG IEnumSTATPROPSTG_iface;
181 LONG refcount;
182 PropertyStorage_impl *storage;
183 STATPROPSTG *stats;
184 size_t current;
185 size_t count;
188 static struct enum_stat_prop_stg *impl_from_IEnumSTATPROPSTG(IEnumSTATPROPSTG *iface)
190 return CONTAINING_RECORD(iface, struct enum_stat_prop_stg, IEnumSTATPROPSTG_iface);
193 static HRESULT WINAPI enum_stat_prop_stg_QueryInterface(IEnumSTATPROPSTG *iface, REFIID riid, void **obj)
195 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
197 if (IsEqualIID(riid, &IID_IEnumSTATPROPSTG) ||
198 IsEqualIID(riid, &IID_IUnknown))
200 *obj = iface;
201 IEnumSTATPROPSTG_AddRef(iface);
202 return S_OK;
205 WARN("Unsupported interface %s.\n", debugstr_guid(riid));
206 return E_NOINTERFACE;
209 static ULONG WINAPI enum_stat_prop_stg_AddRef(IEnumSTATPROPSTG *iface)
211 struct enum_stat_prop_stg *penum = impl_from_IEnumSTATPROPSTG(iface);
212 LONG refcount = InterlockedIncrement(&penum->refcount);
214 TRACE("%p, refcount %lu.\n", iface, refcount);
216 return refcount;
219 static ULONG WINAPI enum_stat_prop_stg_Release(IEnumSTATPROPSTG *iface)
221 struct enum_stat_prop_stg *penum = impl_from_IEnumSTATPROPSTG(iface);
222 LONG refcount = InterlockedDecrement(&penum->refcount);
224 TRACE("%p, refcount %lu.\n", iface, refcount);
226 if (!refcount)
228 IPropertyStorage_Release(&penum->storage->IPropertyStorage_iface);
229 heap_free(penum->stats);
230 heap_free(penum);
233 return refcount;
236 static HRESULT WINAPI enum_stat_prop_stg_Next(IEnumSTATPROPSTG *iface, ULONG celt, STATPROPSTG *ret, ULONG *fetched)
238 struct enum_stat_prop_stg *penum = impl_from_IEnumSTATPROPSTG(iface);
239 ULONG count = 0;
240 WCHAR *name;
242 TRACE("%p, %lu, %p, %p.\n", iface, celt, ret, fetched);
244 if (penum->current == ~0u)
245 penum->current = 0;
247 while (count < celt && penum->current < penum->count)
249 *ret = penum->stats[penum->current++];
251 if (dictionary_find(penum->storage->propid_to_name, UlongToPtr(ret->propid), (void **)&name))
253 SIZE_T size = (lstrlenW(name) + 1) * sizeof(WCHAR);
254 ret->lpwstrName = CoTaskMemAlloc(size);
255 if (ret->lpwstrName)
256 memcpy(ret->lpwstrName, name, size);
258 ret++;
259 count++;
262 if (fetched)
263 *fetched = count;
265 return count < celt ? S_FALSE : S_OK;
268 static HRESULT WINAPI enum_stat_prop_stg_Skip(IEnumSTATPROPSTG *iface, ULONG celt)
270 FIXME("%p, %lu.\n", iface, celt);
272 return S_OK;
275 static HRESULT WINAPI enum_stat_prop_stg_Reset(IEnumSTATPROPSTG *iface)
277 struct enum_stat_prop_stg *penum = impl_from_IEnumSTATPROPSTG(iface);
279 TRACE("%p.\n", iface);
281 penum->current = ~0u;
283 return S_OK;
286 static HRESULT WINAPI enum_stat_prop_stg_Clone(IEnumSTATPROPSTG *iface, IEnumSTATPROPSTG **ppenum)
288 FIXME("%p, %p.\n", iface, ppenum);
290 return E_NOTIMPL;
293 static const IEnumSTATPROPSTGVtbl enum_stat_prop_stg_vtbl =
295 enum_stat_prop_stg_QueryInterface,
296 enum_stat_prop_stg_AddRef,
297 enum_stat_prop_stg_Release,
298 enum_stat_prop_stg_Next,
299 enum_stat_prop_stg_Skip,
300 enum_stat_prop_stg_Reset,
301 enum_stat_prop_stg_Clone,
304 static BOOL prop_enum_stat(const void *k, const void *v, void *extra, void *arg)
306 struct enum_stat_prop_stg *stg = arg;
307 PROPID propid = PtrToUlong(k);
308 const PROPVARIANT *prop = v;
309 STATPROPSTG *dest;
311 dest = &stg->stats[stg->count];
313 dest->lpwstrName = NULL;
314 dest->propid = propid;
315 dest->vt = prop->vt;
316 stg->count++;
318 return TRUE;
321 static BOOL prop_enum_stat_count(const void *k, const void *v, void *extra, void *arg)
323 DWORD *count = arg;
325 *count += 1;
327 return TRUE;
330 static HRESULT create_enum_stat_prop_stg(PropertyStorage_impl *storage, IEnumSTATPROPSTG **ret)
332 struct enum_stat_prop_stg *enum_obj;
333 DWORD count;
335 enum_obj = heap_alloc_zero(sizeof(*enum_obj));
336 if (!enum_obj)
337 return E_OUTOFMEMORY;
339 enum_obj->IEnumSTATPROPSTG_iface.lpVtbl = &enum_stat_prop_stg_vtbl;
340 enum_obj->refcount = 1;
341 enum_obj->storage = storage;
342 IPropertyStorage_AddRef(&storage->IPropertyStorage_iface);
344 count = 0;
345 dictionary_enumerate(storage->propid_to_prop, prop_enum_stat_count, &count);
347 if (count)
349 if (!(enum_obj->stats = heap_alloc(sizeof(*enum_obj->stats) * count)))
351 IEnumSTATPROPSTG_Release(&enum_obj->IEnumSTATPROPSTG_iface);
352 return E_OUTOFMEMORY;
355 dictionary_enumerate(storage->propid_to_prop, prop_enum_stat, enum_obj);
358 *ret = &enum_obj->IEnumSTATPROPSTG_iface;
360 return S_OK;
363 /************************************************************************
364 * IPropertyStorage_fnQueryInterface (IPropertyStorage)
366 static HRESULT WINAPI IPropertyStorage_fnQueryInterface(
367 IPropertyStorage *iface,
368 REFIID riid,
369 void** ppvObject)
371 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
373 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
375 if (!ppvObject)
376 return E_INVALIDARG;
378 *ppvObject = 0;
380 if (IsEqualGUID(&IID_IUnknown, riid) ||
381 IsEqualGUID(&IID_IPropertyStorage, riid))
383 IPropertyStorage_AddRef(iface);
384 *ppvObject = iface;
385 return S_OK;
388 return E_NOINTERFACE;
391 /************************************************************************
392 * IPropertyStorage_fnAddRef (IPropertyStorage)
394 static ULONG WINAPI IPropertyStorage_fnAddRef(
395 IPropertyStorage *iface)
397 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
398 return InterlockedIncrement(&This->ref);
401 /************************************************************************
402 * IPropertyStorage_fnRelease (IPropertyStorage)
404 static ULONG WINAPI IPropertyStorage_fnRelease(
405 IPropertyStorage *iface)
407 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
408 ULONG ref;
410 ref = InterlockedDecrement(&This->ref);
411 if (ref == 0)
413 TRACE("Destroying %p\n", This);
414 if (This->dirty)
415 IPropertyStorage_Commit(iface, STGC_DEFAULT);
416 IStream_Release(This->stm);
417 This->cs.DebugInfo->Spare[0] = 0;
418 DeleteCriticalSection(&This->cs);
419 PropertyStorage_DestroyDictionaries(This);
420 HeapFree(GetProcessHeap(), 0, This);
422 return ref;
425 static PROPVARIANT *PropertyStorage_FindProperty(PropertyStorage_impl *This,
426 DWORD propid)
428 PROPVARIANT *ret = NULL;
430 dictionary_find(This->propid_to_prop, UlongToPtr(propid), (void **)&ret);
431 TRACE("returning %p\n", ret);
432 return ret;
435 /* Returns NULL if name is NULL. */
436 static PROPVARIANT *PropertyStorage_FindPropertyByName(
437 PropertyStorage_impl *This, LPCWSTR name)
439 PROPVARIANT *ret = NULL;
440 void *propid;
442 if (!name)
443 return NULL;
444 if (This->codePage == CP_UNICODE)
446 if (dictionary_find(This->name_to_propid, name, &propid))
447 ret = PropertyStorage_FindProperty(This, PtrToUlong(propid));
449 else
451 LPSTR ansiName;
452 HRESULT hr = PropertyStorage_StringCopy((LPCSTR)name, CP_UNICODE,
453 &ansiName, This->codePage);
455 if (SUCCEEDED(hr))
457 if (dictionary_find(This->name_to_propid, ansiName, &propid))
458 ret = PropertyStorage_FindProperty(This, PtrToUlong(propid));
459 CoTaskMemFree(ansiName);
462 TRACE("returning %p\n", ret);
463 return ret;
466 static LPWSTR PropertyStorage_FindPropertyNameById(PropertyStorage_impl *This,
467 DWORD propid)
469 LPWSTR ret = NULL;
471 dictionary_find(This->propid_to_name, UlongToPtr(propid), (void **)&ret);
472 TRACE("returning %p\n", ret);
473 return ret;
476 /************************************************************************
477 * IPropertyStorage_fnReadMultiple (IPropertyStorage)
479 static HRESULT WINAPI IPropertyStorage_fnReadMultiple(
480 IPropertyStorage* iface,
481 ULONG cpspec,
482 const PROPSPEC rgpspec[],
483 PROPVARIANT rgpropvar[])
485 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
486 HRESULT hr = S_OK;
487 ULONG i;
489 TRACE("%p, %lu, %p, %p\n", iface, cpspec, rgpspec, rgpropvar);
491 if (!cpspec)
492 return S_FALSE;
493 if (!rgpspec || !rgpropvar)
494 return E_INVALIDARG;
495 EnterCriticalSection(&This->cs);
496 for (i = 0; i < cpspec; i++)
498 PropVariantInit(&rgpropvar[i]);
499 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
501 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
502 rgpspec[i].u.lpwstr);
504 if (prop)
505 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop, GetACP(),
506 This->codePage);
508 else
510 switch (rgpspec[i].u.propid)
512 case PID_CODEPAGE:
513 rgpropvar[i].vt = VT_I2;
514 rgpropvar[i].iVal = This->codePage;
515 break;
516 case PID_LOCALE:
517 rgpropvar[i].vt = VT_I4;
518 rgpropvar[i].lVal = This->locale;
519 break;
520 default:
522 PROPVARIANT *prop = PropertyStorage_FindProperty(This,
523 rgpspec[i].u.propid);
525 if (prop)
526 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop,
527 GetACP(), This->codePage);
528 else
529 hr = S_FALSE;
534 LeaveCriticalSection(&This->cs);
535 return hr;
538 static HRESULT PropertyStorage_StringCopy(LPCSTR src, UINT srcCP, LPSTR *dst, UINT dstCP)
540 HRESULT hr = S_OK;
541 int len;
543 TRACE("%s, %p, %d, %d\n",
544 srcCP == CP_UNICODE ? debugstr_w((LPCWSTR)src) : debugstr_a(src), dst,
545 dstCP, srcCP);
546 assert(src);
547 assert(dst);
548 *dst = NULL;
549 if (dstCP == srcCP)
551 size_t len;
553 if (dstCP == CP_UNICODE)
554 len = (lstrlenW((LPCWSTR)src) + 1) * sizeof(WCHAR);
555 else
556 len = strlen(src) + 1;
557 *dst = CoTaskMemAlloc(len);
558 if (!*dst)
559 hr = STG_E_INSUFFICIENTMEMORY;
560 else
561 memcpy(*dst, src, len);
563 else
565 if (dstCP == CP_UNICODE)
567 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
568 *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
569 if (!*dst)
570 hr = STG_E_INSUFFICIENTMEMORY;
571 else
572 MultiByteToWideChar(srcCP, 0, src, -1, (LPWSTR)*dst, len);
574 else
576 LPCWSTR wideStr = NULL;
577 LPWSTR wideStr_tmp = NULL;
579 if (srcCP == CP_UNICODE)
580 wideStr = (LPCWSTR)src;
581 else
583 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
584 wideStr_tmp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
585 if (wideStr_tmp)
587 MultiByteToWideChar(srcCP, 0, src, -1, wideStr_tmp, len);
588 wideStr = wideStr_tmp;
590 else
591 hr = STG_E_INSUFFICIENTMEMORY;
593 if (SUCCEEDED(hr))
595 len = WideCharToMultiByte(dstCP, 0, wideStr, -1, NULL, 0,
596 NULL, NULL);
597 *dst = CoTaskMemAlloc(len);
598 if (!*dst)
599 hr = STG_E_INSUFFICIENTMEMORY;
600 else
602 BOOL defCharUsed = FALSE;
604 if (WideCharToMultiByte(dstCP, 0, wideStr, -1, *dst, len,
605 NULL, &defCharUsed) == 0 || defCharUsed)
607 CoTaskMemFree(*dst);
608 *dst = NULL;
609 hr = HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION);
613 HeapFree(GetProcessHeap(), 0, wideStr_tmp);
616 TRACE("returning %#lx (%s)\n", hr,
617 dstCP == CP_UNICODE ? debugstr_w((LPCWSTR)*dst) : debugstr_a(*dst));
618 return hr;
621 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop, const PROPVARIANT *propvar,
622 UINT targetCP, UINT srcCP)
624 HRESULT hr = S_OK;
626 assert(prop);
627 assert(propvar);
629 switch (propvar->vt)
631 case VT_LPSTR:
632 hr = PropertyStorage_StringCopy(propvar->pszVal, srcCP, &prop->pszVal, targetCP);
633 if (SUCCEEDED(hr))
634 prop->vt = VT_LPSTR;
635 break;
636 case VT_BSTR:
637 if ((prop->bstrVal = SysAllocStringLen(propvar->bstrVal, SysStringLen(propvar->bstrVal))))
638 prop->vt = VT_BSTR;
639 else
640 hr = E_OUTOFMEMORY;
641 break;
642 default:
643 hr = PropVariantCopy(prop, propvar);
646 return hr;
649 /* Stores the property with id propid and value propvar into this property
650 * storage. lcid is ignored if propvar's type is not VT_LPSTR. If propvar's
651 * type is VT_LPSTR, converts the string using lcid as the source code page
652 * and This->codePage as the target code page before storing.
653 * As a side effect, may change This->format to 1 if the type of propvar is
654 * a version 1-only property.
656 static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
657 PROPID propid, const PROPVARIANT *propvar, UINT cp)
659 HRESULT hr = S_OK;
660 PROPVARIANT *prop = PropertyStorage_FindProperty(This, propid);
662 assert(propvar);
663 if (propvar->vt & VT_BYREF || propvar->vt & VT_ARRAY)
664 This->format = 1;
665 switch (propvar->vt)
667 case VT_DECIMAL:
668 case VT_I1:
669 case VT_INT:
670 case VT_UINT:
671 case VT_VECTOR|VT_I1:
672 This->format = 1;
674 TRACE("Setting %#lx to type %d\n", propid, propvar->vt);
675 if (prop)
677 PropVariantClear(prop);
678 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage, cp);
680 else
682 prop = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
683 sizeof(PROPVARIANT));
684 if (prop)
686 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage, cp);
687 if (SUCCEEDED(hr))
689 dictionary_insert(This->propid_to_prop, UlongToPtr(propid), prop);
690 if (propid > This->highestProp)
691 This->highestProp = propid;
693 else
694 HeapFree(GetProcessHeap(), 0, prop);
696 else
697 hr = STG_E_INSUFFICIENTMEMORY;
699 return hr;
702 /* Adds the name srcName to the name dictionaries, mapped to property ID id.
703 * srcName is encoded in code page cp, and is converted to This->codePage.
704 * If cp is CP_UNICODE, srcName is actually a unicode string.
705 * As a side effect, may change This->format to 1 if srcName is too long for
706 * a version 0 property storage.
707 * Doesn't validate id.
709 static HRESULT PropertyStorage_StoreNameWithId(PropertyStorage_impl *This,
710 LPCSTR srcName, UINT cp, PROPID id)
712 LPSTR name;
713 HRESULT hr;
715 assert(srcName);
717 hr = PropertyStorage_StringCopy(srcName, cp, &name, This->codePage);
718 if (SUCCEEDED(hr))
720 if (This->codePage == CP_UNICODE)
722 if (lstrlenW((LPWSTR)name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
723 This->format = 1;
725 else
727 if (strlen(name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
728 This->format = 1;
730 TRACE("Adding prop name %s, propid %ld\n",
731 This->codePage == CP_UNICODE ? debugstr_w((LPCWSTR)name) :
732 debugstr_a(name), id);
733 dictionary_insert(This->name_to_propid, name, UlongToPtr(id));
734 dictionary_insert(This->propid_to_name, UlongToPtr(id), name);
736 return hr;
739 /************************************************************************
740 * IPropertyStorage_fnWriteMultiple (IPropertyStorage)
742 static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
743 IPropertyStorage* iface,
744 ULONG cpspec,
745 const PROPSPEC rgpspec[],
746 const PROPVARIANT rgpropvar[],
747 PROPID propidNameFirst)
749 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
750 HRESULT hr = S_OK;
751 ULONG i;
753 TRACE("%p, %lu, %p, %p.\n", iface, cpspec, rgpspec, rgpropvar);
755 if (cpspec && (!rgpspec || !rgpropvar))
756 return E_INVALIDARG;
757 if (!(This->grfMode & STGM_READWRITE))
758 return STG_E_ACCESSDENIED;
759 EnterCriticalSection(&This->cs);
760 This->dirty = TRUE;
761 This->originatorOS = (DWORD)MAKELONG(LOWORD(GetVersion()),
762 PROPSETHDR_OSVER_KIND_WIN32) ;
763 for (i = 0; i < cpspec; i++)
765 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
767 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
768 rgpspec[i].u.lpwstr);
770 if (prop)
771 PropVariantCopy(prop, &rgpropvar[i]);
772 else
774 /* Note that I don't do the special cases here that I do below,
775 * because naming the special PIDs isn't supported.
777 if (propidNameFirst < PID_FIRST_USABLE ||
778 propidNameFirst >= PID_MIN_READONLY)
779 hr = STG_E_INVALIDPARAMETER;
780 else
782 PROPID nextId = max(propidNameFirst, This->highestProp + 1);
784 hr = PropertyStorage_StoreNameWithId(This,
785 (LPCSTR)rgpspec[i].u.lpwstr, CP_UNICODE, nextId);
786 if (SUCCEEDED(hr))
787 hr = PropertyStorage_StorePropWithId(This, nextId,
788 &rgpropvar[i], GetACP());
792 else
794 switch (rgpspec[i].u.propid)
796 case PID_DICTIONARY:
797 /* Can't set the dictionary */
798 hr = STG_E_INVALIDPARAMETER;
799 break;
800 case PID_CODEPAGE:
801 /* Can only set the code page if nothing else has been set */
802 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
803 rgpropvar[i].vt == VT_I2)
805 This->codePage = (USHORT)rgpropvar[i].iVal;
806 if (This->codePage == CP_UNICODE)
807 This->grfFlags &= ~PROPSETFLAG_ANSI;
808 else
809 This->grfFlags |= PROPSETFLAG_ANSI;
811 else
812 hr = STG_E_INVALIDPARAMETER;
813 break;
814 case PID_LOCALE:
815 /* Can only set the locale if nothing else has been set */
816 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
817 rgpropvar[i].vt == VT_I4)
818 This->locale = rgpropvar[i].lVal;
819 else
820 hr = STG_E_INVALIDPARAMETER;
821 break;
822 case PID_ILLEGAL:
823 /* silently ignore like MSDN says */
824 break;
825 default:
826 if (rgpspec[i].u.propid >= PID_MIN_READONLY)
827 hr = STG_E_INVALIDPARAMETER;
828 else
829 hr = PropertyStorage_StorePropWithId(This,
830 rgpspec[i].u.propid, &rgpropvar[i], GetACP());
834 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
835 IPropertyStorage_Commit(iface, STGC_DEFAULT);
836 LeaveCriticalSection(&This->cs);
837 return hr;
840 /************************************************************************
841 * IPropertyStorage_fnDeleteMultiple (IPropertyStorage)
843 static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
844 IPropertyStorage* iface,
845 ULONG cpspec,
846 const PROPSPEC rgpspec[])
848 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
849 ULONG i;
850 HRESULT hr;
852 TRACE("%p, %ld, %p.\n", iface, cpspec, rgpspec);
854 if (cpspec && !rgpspec)
855 return E_INVALIDARG;
856 if (!(This->grfMode & STGM_READWRITE))
857 return STG_E_ACCESSDENIED;
858 hr = S_OK;
859 EnterCriticalSection(&This->cs);
860 This->dirty = TRUE;
861 for (i = 0; i < cpspec; i++)
863 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
865 void *propid;
867 if (dictionary_find(This->name_to_propid, rgpspec[i].u.lpwstr, &propid))
868 dictionary_remove(This->propid_to_prop, propid);
870 else
872 if (rgpspec[i].u.propid >= PID_FIRST_USABLE &&
873 rgpspec[i].u.propid < PID_MIN_READONLY)
874 dictionary_remove(This->propid_to_prop, UlongToPtr(rgpspec[i].u.propid));
875 else
876 hr = STG_E_INVALIDPARAMETER;
879 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
880 IPropertyStorage_Commit(iface, STGC_DEFAULT);
881 LeaveCriticalSection(&This->cs);
882 return hr;
885 /************************************************************************
886 * IPropertyStorage_fnReadPropertyNames (IPropertyStorage)
888 static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames(
889 IPropertyStorage* iface,
890 ULONG cpropid,
891 const PROPID rgpropid[],
892 LPOLESTR rglpwstrName[])
894 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
895 ULONG i;
896 HRESULT hr = S_FALSE;
898 TRACE("%p, %ld, %p, %p.\n", iface, cpropid, rgpropid, rglpwstrName);
900 if (cpropid && (!rgpropid || !rglpwstrName))
901 return E_INVALIDARG;
902 EnterCriticalSection(&This->cs);
903 for (i = 0; i < cpropid && SUCCEEDED(hr); i++)
905 LPWSTR name = PropertyStorage_FindPropertyNameById(This, rgpropid[i]);
907 if (name)
909 size_t len = lstrlenW(name);
911 hr = S_OK;
912 rglpwstrName[i] = CoTaskMemAlloc((len + 1) * sizeof(WCHAR));
913 if (rglpwstrName[i])
914 memcpy(rglpwstrName[i], name, (len + 1) * sizeof(WCHAR));
915 else
916 hr = STG_E_INSUFFICIENTMEMORY;
918 else
919 rglpwstrName[i] = NULL;
921 LeaveCriticalSection(&This->cs);
922 return hr;
925 /************************************************************************
926 * IPropertyStorage_fnWritePropertyNames (IPropertyStorage)
928 static HRESULT WINAPI IPropertyStorage_fnWritePropertyNames(
929 IPropertyStorage* iface,
930 ULONG cpropid,
931 const PROPID rgpropid[],
932 const LPOLESTR rglpwstrName[])
934 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
935 ULONG i;
936 HRESULT hr;
938 TRACE("%p, %lu, %p, %p.\n", iface, cpropid, rgpropid, rglpwstrName);
940 if (cpropid && (!rgpropid || !rglpwstrName))
941 return E_INVALIDARG;
942 if (!(This->grfMode & STGM_READWRITE))
943 return STG_E_ACCESSDENIED;
944 hr = S_OK;
945 EnterCriticalSection(&This->cs);
946 This->dirty = TRUE;
947 for (i = 0; SUCCEEDED(hr) && i < cpropid; i++)
949 if (rgpropid[i] != PID_ILLEGAL)
950 hr = PropertyStorage_StoreNameWithId(This, (LPCSTR)rglpwstrName[i],
951 CP_UNICODE, rgpropid[i]);
953 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
954 IPropertyStorage_Commit(iface, STGC_DEFAULT);
955 LeaveCriticalSection(&This->cs);
956 return hr;
959 /************************************************************************
960 * IPropertyStorage_fnDeletePropertyNames (IPropertyStorage)
962 static HRESULT WINAPI IPropertyStorage_fnDeletePropertyNames(
963 IPropertyStorage* iface,
964 ULONG cpropid,
965 const PROPID rgpropid[])
967 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
968 ULONG i;
969 HRESULT hr;
971 TRACE("%p, %ld, %p.\n", iface, cpropid, rgpropid);
973 if (cpropid && !rgpropid)
974 return E_INVALIDARG;
975 if (!(This->grfMode & STGM_READWRITE))
976 return STG_E_ACCESSDENIED;
977 hr = S_OK;
978 EnterCriticalSection(&This->cs);
979 This->dirty = TRUE;
980 for (i = 0; i < cpropid; i++)
982 LPWSTR name = NULL;
984 if (dictionary_find(This->propid_to_name, UlongToPtr(rgpropid[i]), (void **)&name))
986 dictionary_remove(This->propid_to_name, UlongToPtr(rgpropid[i]));
987 dictionary_remove(This->name_to_propid, name);
990 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
991 IPropertyStorage_Commit(iface, STGC_DEFAULT);
992 LeaveCriticalSection(&This->cs);
993 return hr;
996 /************************************************************************
997 * IPropertyStorage_fnCommit (IPropertyStorage)
999 static HRESULT WINAPI IPropertyStorage_fnCommit(
1000 IPropertyStorage* iface,
1001 DWORD grfCommitFlags)
1003 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
1004 HRESULT hr;
1006 TRACE("%p, %#lx.\n", iface, grfCommitFlags);
1008 if (!(This->grfMode & STGM_READWRITE))
1009 return STG_E_ACCESSDENIED;
1010 EnterCriticalSection(&This->cs);
1011 if (This->dirty)
1012 hr = PropertyStorage_WriteToStream(This);
1013 else
1014 hr = S_OK;
1015 LeaveCriticalSection(&This->cs);
1016 return hr;
1019 /************************************************************************
1020 * IPropertyStorage_fnRevert (IPropertyStorage)
1022 static HRESULT WINAPI IPropertyStorage_fnRevert(
1023 IPropertyStorage* iface)
1025 HRESULT hr;
1026 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
1028 TRACE("%p\n", iface);
1030 EnterCriticalSection(&This->cs);
1031 if (This->dirty)
1033 PropertyStorage_DestroyDictionaries(This);
1034 hr = PropertyStorage_CreateDictionaries(This);
1035 if (SUCCEEDED(hr))
1036 hr = PropertyStorage_ReadFromStream(This);
1038 else
1039 hr = S_OK;
1040 LeaveCriticalSection(&This->cs);
1041 return hr;
1044 /************************************************************************
1045 * IPropertyStorage_fnEnum (IPropertyStorage)
1047 static HRESULT WINAPI IPropertyStorage_fnEnum(IPropertyStorage *iface, IEnumSTATPROPSTG **ppenum)
1049 PropertyStorage_impl *storage = impl_from_IPropertyStorage(iface);
1051 TRACE("%p, %p.\n", iface, ppenum);
1053 return create_enum_stat_prop_stg(storage, ppenum);
1056 /************************************************************************
1057 * IPropertyStorage_fnSetTimes (IPropertyStorage)
1059 static HRESULT WINAPI IPropertyStorage_fnSetTimes(
1060 IPropertyStorage* iface,
1061 const FILETIME* pctime,
1062 const FILETIME* patime,
1063 const FILETIME* pmtime)
1065 FIXME("\n");
1066 return E_NOTIMPL;
1069 /************************************************************************
1070 * IPropertyStorage_fnSetClass (IPropertyStorage)
1072 static HRESULT WINAPI IPropertyStorage_fnSetClass(
1073 IPropertyStorage* iface,
1074 REFCLSID clsid)
1076 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
1078 TRACE("%p, %s\n", iface, debugstr_guid(clsid));
1080 if (!clsid)
1081 return E_INVALIDARG;
1082 if (!(This->grfMode & STGM_READWRITE))
1083 return STG_E_ACCESSDENIED;
1084 This->clsid = *clsid;
1085 This->dirty = TRUE;
1086 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
1087 IPropertyStorage_Commit(iface, STGC_DEFAULT);
1088 return S_OK;
1091 /************************************************************************
1092 * IPropertyStorage_fnStat (IPropertyStorage)
1094 static HRESULT WINAPI IPropertyStorage_fnStat(
1095 IPropertyStorage* iface,
1096 STATPROPSETSTG* statpsstg)
1098 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
1099 STATSTG stat;
1100 HRESULT hr;
1102 TRACE("%p, %p\n", iface, statpsstg);
1104 if (!statpsstg)
1105 return E_INVALIDARG;
1107 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
1108 if (SUCCEEDED(hr))
1110 statpsstg->fmtid = This->fmtid;
1111 statpsstg->clsid = This->clsid;
1112 statpsstg->grfFlags = This->grfFlags;
1113 statpsstg->mtime = stat.mtime;
1114 statpsstg->ctime = stat.ctime;
1115 statpsstg->atime = stat.atime;
1116 statpsstg->dwOSVersion = This->originatorOS;
1118 return hr;
1121 static int PropertyStorage_PropNameCompare(const void *a, const void *b,
1122 void *extra)
1124 PropertyStorage_impl *This = extra;
1126 if (This->codePage == CP_UNICODE)
1128 TRACE("(%s, %s)\n", debugstr_w(a), debugstr_w(b));
1129 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1130 return wcscmp(a, b);
1131 else
1132 return lstrcmpiW(a, b);
1134 else
1136 TRACE("(%s, %s)\n", debugstr_a(a), debugstr_a(b));
1137 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1138 return lstrcmpA(a, b);
1139 else
1140 return lstrcmpiA(a, b);
1144 static void PropertyStorage_PropNameDestroy(void *k, void *d, void *extra)
1146 CoTaskMemFree(k);
1149 static int PropertyStorage_PropCompare(const void *a, const void *b,
1150 void *extra)
1152 TRACE("%lu, %lu.\n", PtrToUlong(a), PtrToUlong(b));
1153 return PtrToUlong(a) - PtrToUlong(b);
1156 static void PropertyStorage_PropertyDestroy(void *k, void *d, void *extra)
1158 PropVariantClear(d);
1159 HeapFree(GetProcessHeap(), 0, d);
1162 #ifdef WORDS_BIGENDIAN
1163 /* Swaps each character in str to or from little endian; assumes the conversion
1164 * is symmetric, that is, that lendian16toh is equivalent to htole16.
1166 static void PropertyStorage_ByteSwapString(LPWSTR str, size_t len)
1168 DWORD i;
1170 /* Swap characters to host order.
1171 * FIXME: alignment?
1173 for (i = 0; i < len; i++)
1174 str[i] = lendian16toh(str[i]);
1176 #else
1177 #define PropertyStorage_ByteSwapString(s, l)
1178 #endif
1180 static void* WINAPI Allocate_CoTaskMemAlloc(void *this, ULONG size)
1182 return CoTaskMemAlloc(size);
1185 struct read_buffer
1187 BYTE *data;
1188 size_t size;
1191 static HRESULT buffer_test_offset(const struct read_buffer *buffer, size_t offset, size_t len)
1193 return len > buffer->size || offset > buffer->size - len ? STG_E_READFAULT : S_OK;
1196 static HRESULT buffer_read_uint64(const struct read_buffer *buffer, size_t offset, ULARGE_INTEGER *data)
1198 HRESULT hr;
1200 if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*data))))
1201 StorageUtl_ReadULargeInteger(buffer->data, offset, data);
1203 return hr;
1206 static HRESULT buffer_read_dword(const struct read_buffer *buffer, size_t offset, DWORD *data)
1208 HRESULT hr;
1210 if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*data))))
1211 StorageUtl_ReadDWord(buffer->data, offset, data);
1213 return hr;
1216 static HRESULT buffer_read_word(const struct read_buffer *buffer, size_t offset, WORD *data)
1218 HRESULT hr;
1220 if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*data))))
1221 StorageUtl_ReadWord(buffer->data, offset, data);
1223 return hr;
1226 static HRESULT buffer_read_byte(const struct read_buffer *buffer, size_t offset, BYTE *data)
1228 HRESULT hr;
1230 if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*data))))
1231 *data = *(buffer->data + offset);
1233 return hr;
1236 static HRESULT buffer_read_len(const struct read_buffer *buffer, size_t offset, void *dest, size_t len)
1238 HRESULT hr;
1240 if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, len)))
1241 memcpy(dest, buffer->data + offset, len);
1243 return hr;
1246 static HRESULT propertystorage_read_scalar(PROPVARIANT *prop, const struct read_buffer *buffer, size_t offset,
1247 UINT codepage, void* (WINAPI *allocate)(void *this, ULONG size), void *allocate_data)
1249 HRESULT hr;
1251 assert(!(prop->vt & (VT_ARRAY | VT_VECTOR)));
1253 switch (prop->vt)
1255 case VT_EMPTY:
1256 case VT_NULL:
1257 hr = S_OK;
1258 break;
1259 case VT_I1:
1260 hr = buffer_read_byte(buffer, offset, (BYTE *)&prop->cVal);
1261 TRACE("Read char 0x%x ('%c')\n", prop->cVal, prop->cVal);
1262 break;
1263 case VT_UI1:
1264 hr = buffer_read_byte(buffer, offset, &prop->bVal);
1265 TRACE("Read byte 0x%x\n", prop->bVal);
1266 break;
1267 case VT_BOOL:
1268 hr = buffer_read_word(buffer, offset, (WORD *)&prop->boolVal);
1269 TRACE("Read bool %d\n", prop->boolVal);
1270 break;
1271 case VT_I2:
1272 hr = buffer_read_word(buffer, offset, (WORD *)&prop->iVal);
1273 TRACE("Read short %d\n", prop->iVal);
1274 break;
1275 case VT_UI2:
1276 hr = buffer_read_word(buffer, offset, &prop->uiVal);
1277 TRACE("Read ushort %d\n", prop->uiVal);
1278 break;
1279 case VT_INT:
1280 case VT_I4:
1281 hr = buffer_read_dword(buffer, offset, (DWORD *)&prop->lVal);
1282 TRACE("Read long %ld\n", prop->lVal);
1283 break;
1284 case VT_UINT:
1285 case VT_UI4:
1286 hr = buffer_read_dword(buffer, offset, &prop->ulVal);
1287 TRACE("Read ulong %ld\n", prop->ulVal);
1288 break;
1289 case VT_I8:
1290 hr = buffer_read_uint64(buffer, offset, (ULARGE_INTEGER *)&prop->hVal);
1291 TRACE("Read long long %s\n", wine_dbgstr_longlong(prop->hVal.QuadPart));
1292 break;
1293 case VT_UI8:
1294 hr = buffer_read_uint64(buffer, offset, &prop->uhVal);
1295 TRACE("Read ulong long %s\n", wine_dbgstr_longlong(prop->uhVal.QuadPart));
1296 break;
1297 case VT_R8:
1298 hr = buffer_read_len(buffer, offset, &prop->dblVal, sizeof(prop->dblVal));
1299 TRACE("Read double %f\n", prop->dblVal);
1300 break;
1301 case VT_LPSTR:
1303 DWORD count;
1305 if (FAILED(hr = buffer_read_dword(buffer, offset, &count)))
1306 break;
1308 offset += sizeof(DWORD);
1310 if (codepage == CP_UNICODE && count % sizeof(WCHAR))
1312 WARN("Unicode string has odd number of bytes\n");
1313 hr = STG_E_INVALIDHEADER;
1315 else
1317 prop->pszVal = allocate(allocate_data, count);
1318 if (prop->pszVal)
1320 if (FAILED(hr = buffer_read_len(buffer, offset, prop->pszVal, count)))
1321 break;
1323 /* This is stored in the code page specified in codepage.
1324 * Don't convert it, the caller will just store it as-is.
1326 if (codepage == CP_UNICODE)
1328 /* Make sure it's NULL-terminated */
1329 prop->pszVal[count / sizeof(WCHAR) - 1] = '\0';
1330 TRACE("Read string value %s\n",
1331 debugstr_w(prop->pwszVal));
1333 else
1335 /* Make sure it's NULL-terminated */
1336 prop->pszVal[count - 1] = '\0';
1337 TRACE("Read string value %s\n", debugstr_a(prop->pszVal));
1340 else
1341 hr = STG_E_INSUFFICIENTMEMORY;
1343 break;
1345 case VT_BSTR:
1347 DWORD count, wcount;
1349 if (FAILED(hr = buffer_read_dword(buffer, offset, &count)))
1350 break;
1352 offset += sizeof(DWORD);
1354 if (codepage == CP_UNICODE && count % sizeof(WCHAR))
1356 WARN("Unicode string has odd number of bytes\n");
1357 hr = STG_E_INVALIDHEADER;
1359 else
1361 if (codepage == CP_UNICODE)
1362 wcount = count / sizeof(WCHAR);
1363 else
1365 if (FAILED(hr = buffer_test_offset(buffer, offset, count)))
1366 break;
1367 wcount = MultiByteToWideChar(codepage, 0, (LPCSTR)(buffer->data + offset), count, NULL, 0);
1370 prop->bstrVal = SysAllocStringLen(NULL, wcount); /* FIXME: use allocator? */
1372 if (prop->bstrVal)
1374 if (codepage == CP_UNICODE)
1375 hr = buffer_read_len(buffer, offset, prop->bstrVal, count);
1376 else
1377 MultiByteToWideChar(codepage, 0, (LPCSTR)(buffer->data + offset), count, prop->bstrVal, wcount);
1379 prop->bstrVal[wcount - 1] = '\0';
1380 TRACE("Read string value %s\n", debugstr_w(prop->bstrVal));
1382 else
1383 hr = STG_E_INSUFFICIENTMEMORY;
1385 break;
1387 case VT_BLOB:
1389 DWORD count;
1391 if (FAILED(hr = buffer_read_dword(buffer, offset, &count)))
1392 break;
1394 offset += sizeof(DWORD);
1396 prop->blob.cbSize = count;
1397 prop->blob.pBlobData = allocate(allocate_data, count);
1398 if (prop->blob.pBlobData)
1400 hr = buffer_read_len(buffer, offset, prop->blob.pBlobData, count);
1401 TRACE("Read blob value of size %ld\n", count);
1403 else
1404 hr = STG_E_INSUFFICIENTMEMORY;
1405 break;
1407 case VT_LPWSTR:
1409 DWORD count;
1411 if (FAILED(hr = buffer_read_dword(buffer, offset, &count)))
1412 break;
1414 offset += sizeof(DWORD);
1416 prop->pwszVal = allocate(allocate_data, count * sizeof(WCHAR));
1417 if (prop->pwszVal)
1419 if (SUCCEEDED(hr = buffer_read_len(buffer, offset, prop->pwszVal, count * sizeof(WCHAR))))
1421 /* make sure string is NULL-terminated */
1422 prop->pwszVal[count - 1] = '\0';
1423 PropertyStorage_ByteSwapString(prop->pwszVal, count);
1424 TRACE("Read string value %s\n", debugstr_w(prop->pwszVal));
1427 else
1428 hr = STG_E_INSUFFICIENTMEMORY;
1429 break;
1431 case VT_FILETIME:
1432 hr = buffer_read_uint64(buffer, offset, (ULARGE_INTEGER *)&prop->filetime);
1433 break;
1434 case VT_CF:
1436 DWORD len = 0, tag = 0;
1438 if (SUCCEEDED(hr = buffer_read_dword(buffer, offset, &len)))
1439 hr = buffer_read_dword(buffer, offset + sizeof(DWORD), &tag);
1440 if (FAILED(hr))
1441 break;
1443 offset += 2 * sizeof(DWORD);
1445 if (len > 8)
1447 len -= 8;
1448 prop->pclipdata = allocate(allocate_data, sizeof (CLIPDATA));
1449 prop->pclipdata->cbSize = len;
1450 prop->pclipdata->ulClipFmt = tag;
1451 prop->pclipdata->pClipData = allocate(allocate_data, len - sizeof(prop->pclipdata->ulClipFmt));
1452 hr = buffer_read_len(buffer, offset, prop->pclipdata->pClipData, len - sizeof(prop->pclipdata->ulClipFmt));
1454 else
1455 hr = STG_E_INVALIDPARAMETER;
1457 break;
1458 case VT_CLSID:
1459 if (!(prop->puuid = allocate(allocate_data, sizeof (*prop->puuid))))
1460 return STG_E_INSUFFICIENTMEMORY;
1462 if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*prop->puuid))))
1463 StorageUtl_ReadGUID(buffer->data, offset, prop->puuid);
1465 break;
1466 default:
1467 FIXME("unsupported type %d\n", prop->vt);
1468 hr = STG_E_INVALIDPARAMETER;
1471 return hr;
1474 static size_t propertystorage_get_elemsize(const PROPVARIANT *prop)
1476 if (!(prop->vt & VT_VECTOR))
1477 return 0;
1479 switch (prop->vt & ~VT_VECTOR)
1481 case VT_I1: return sizeof(*prop->cac.pElems);
1482 case VT_UI1: return sizeof(*prop->caub.pElems);
1483 case VT_I2: return sizeof(*prop->cai.pElems);
1484 case VT_UI2: return sizeof(*prop->caui.pElems);
1485 case VT_BOOL: return sizeof(*prop->cabool.pElems);
1486 case VT_I4: return sizeof(*prop->cal.pElems);
1487 case VT_UI4: return sizeof(*prop->caul.pElems);
1488 case VT_R4: return sizeof(*prop->caflt.pElems);
1489 case VT_ERROR: return sizeof(*prop->cascode.pElems);
1490 case VT_I8: return sizeof(*prop->cah.pElems);
1491 case VT_UI8: return sizeof(*prop->cauh.pElems);
1492 case VT_R8: return sizeof(*prop->cadbl.pElems);
1493 case VT_CY: return sizeof(*prop->cacy.pElems);
1494 case VT_DATE: return sizeof(*prop->cadate.pElems);
1495 case VT_FILETIME: return sizeof(*prop->cafiletime.pElems);
1496 case VT_CLSID: return sizeof(*prop->cauuid.pElems);
1497 case VT_VARIANT: return sizeof(*prop->capropvar.pElems);
1498 default:
1499 FIXME("Unhandled type %#x.\n", prop->vt);
1500 return 0;
1504 static HRESULT PropertyStorage_ReadProperty(PROPVARIANT *prop, const struct read_buffer *buffer,
1505 size_t offset, UINT codepage, void* (WINAPI *allocate)(void *this, ULONG size), void *allocate_data)
1507 HRESULT hr;
1508 DWORD vt;
1510 assert(prop);
1511 assert(buffer->data);
1513 if (FAILED(hr = buffer_read_dword(buffer, offset, &vt)))
1514 return hr;
1516 offset += sizeof(DWORD);
1517 prop->vt = vt;
1519 if (prop->vt & VT_VECTOR)
1521 DWORD count, i;
1523 switch (prop->vt & VT_VECTOR)
1525 case VT_BSTR:
1526 case VT_VARIANT:
1527 case VT_LPSTR:
1528 case VT_LPWSTR:
1529 case VT_CF:
1530 FIXME("Vector with variable length elements are not supported.\n");
1531 return STG_E_INVALIDPARAMETER;
1532 default:
1536 if (SUCCEEDED(hr = buffer_read_dword(buffer, offset, &count)))
1538 size_t elemsize = propertystorage_get_elemsize(prop);
1539 PROPVARIANT elem;
1541 offset += sizeof(DWORD);
1543 if ((prop->capropvar.pElems = allocate(allocate_data, elemsize * count)))
1545 prop->capropvar.cElems = count;
1546 elem.vt = prop->vt & ~VT_VECTOR;
1548 for (i = 0; i < count; ++i)
1550 if (SUCCEEDED(hr = propertystorage_read_scalar(&elem, buffer, offset + i * elemsize, codepage,
1551 allocate, allocate_data)))
1553 memcpy(&prop->capropvar.pElems[i], &elem.lVal, elemsize);
1557 else
1558 hr = STG_E_INSUFFICIENTMEMORY;
1561 else if (prop->vt & VT_ARRAY)
1563 FIXME("VT_ARRAY properties are not supported.\n");
1564 hr = STG_E_INVALIDPARAMETER;
1566 else
1567 hr = propertystorage_read_scalar(prop, buffer, offset, codepage, allocate, allocate_data);
1569 return hr;
1572 static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
1573 PROPERTYSETHEADER *hdr)
1575 BYTE buf[sizeof(PROPERTYSETHEADER)];
1576 ULONG count = 0;
1577 HRESULT hr;
1579 assert(stm);
1580 assert(hdr);
1581 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1582 if (SUCCEEDED(hr))
1584 if (count != sizeof(buf))
1586 WARN("read only %ld\n", count);
1587 hr = STG_E_INVALIDHEADER;
1589 else
1591 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder),
1592 &hdr->wByteOrder);
1593 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat),
1594 &hdr->wFormat);
1595 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer),
1596 &hdr->dwOSVer);
1597 StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid),
1598 &hdr->clsid);
1599 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved),
1600 &hdr->reserved);
1603 TRACE("returning %#lx\n", hr);
1604 return hr;
1607 static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm,
1608 FORMATIDOFFSET *fmt)
1610 BYTE buf[sizeof(FORMATIDOFFSET)];
1611 ULONG count = 0;
1612 HRESULT hr;
1614 assert(stm);
1615 assert(fmt);
1616 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1617 if (SUCCEEDED(hr))
1619 if (count != sizeof(buf))
1621 WARN("read only %ld\n", count);
1622 hr = STG_E_INVALIDHEADER;
1624 else
1626 StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid),
1627 &fmt->fmtid);
1628 StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset),
1629 &fmt->dwOffset);
1632 TRACE("returning %#lx\n", hr);
1633 return hr;
1636 static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
1637 PROPERTYSECTIONHEADER *hdr)
1639 BYTE buf[sizeof(PROPERTYSECTIONHEADER)];
1640 ULONG count = 0;
1641 HRESULT hr;
1643 assert(stm);
1644 assert(hdr);
1645 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1646 if (SUCCEEDED(hr))
1648 if (count != sizeof(buf))
1650 WARN("read only %ld\n", count);
1651 hr = STG_E_INVALIDHEADER;
1653 else
1655 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1656 cbSection), &hdr->cbSection);
1657 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1658 cProperties), &hdr->cProperties);
1661 TRACE("returning %#lx\n", hr);
1662 return hr;
1665 /* Reads the dictionary from the memory buffer beginning at ptr. Interprets
1666 * the entries according to the values of This->codePage and This->locale.
1668 static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This, const struct read_buffer *buffer,
1669 size_t offset)
1671 DWORD numEntries, i;
1672 HRESULT hr;
1674 assert(This->name_to_propid);
1675 assert(This->propid_to_name);
1677 if (FAILED(hr = buffer_read_dword(buffer, offset, &numEntries)))
1678 return hr;
1680 TRACE("Reading %ld entries:\n", numEntries);
1682 offset += sizeof(DWORD);
1684 for (i = 0; SUCCEEDED(hr) && i < numEntries; i++)
1686 PROPID propid;
1687 DWORD cbEntry;
1688 WCHAR ch = 0;
1690 if (SUCCEEDED(hr = buffer_read_dword(buffer, offset, &propid)))
1691 hr = buffer_read_dword(buffer, offset + sizeof(PROPID), &cbEntry);
1692 if (FAILED(hr))
1693 break;
1695 offset += sizeof(PROPID) + sizeof(DWORD);
1697 if (FAILED(hr = buffer_test_offset(buffer, offset, This->codePage == CP_UNICODE ?
1698 ALIGNED_LENGTH(cbEntry * sizeof(WCHAR), 3) : cbEntry)))
1700 WARN("Broken name length for entry %ld.\n", i);
1701 return hr;
1704 /* Make sure the source string is NULL-terminated */
1705 if (This->codePage != CP_UNICODE)
1706 buffer_read_byte(buffer, offset + cbEntry - 1, (BYTE *)&ch);
1707 else
1708 buffer_read_word(buffer, offset + (cbEntry - 1) * sizeof(WCHAR), &ch);
1710 if (ch)
1712 WARN("Dictionary entry name %ld is not null-terminated.\n", i);
1713 return E_FAIL;
1716 TRACE("Reading entry with ID %#lx, %ld chars, name %s.\n", propid, cbEntry, This->codePage == CP_UNICODE ?
1717 debugstr_wn((WCHAR *)buffer->data, cbEntry) : debugstr_an((char *)buffer->data, cbEntry));
1719 hr = PropertyStorage_StoreNameWithId(This, (char *)buffer->data + offset, This->codePage, propid);
1720 /* Unicode entries are padded to DWORD boundaries */
1721 if (This->codePage == CP_UNICODE)
1722 cbEntry = ALIGNED_LENGTH(cbEntry * sizeof(WCHAR), 3);
1724 offset += cbEntry;
1727 return hr;
1730 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This)
1732 struct read_buffer read_buffer;
1733 PROPERTYSETHEADER hdr;
1734 FORMATIDOFFSET fmtOffset;
1735 PROPERTYSECTIONHEADER sectionHdr;
1736 LARGE_INTEGER seek;
1737 ULONG i;
1738 STATSTG stat;
1739 HRESULT hr;
1740 BYTE *buf = NULL;
1741 ULONG count = 0;
1742 DWORD dictOffset = 0;
1744 This->dirty = FALSE;
1745 This->highestProp = 0;
1746 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
1747 if (FAILED(hr))
1748 goto end;
1749 if (stat.cbSize.u.HighPart)
1751 WARN("stream too big\n");
1752 /* maximum size varies, but it can't be this big */
1753 hr = STG_E_INVALIDHEADER;
1754 goto end;
1756 if (stat.cbSize.u.LowPart == 0)
1758 /* empty stream is okay */
1759 hr = S_OK;
1760 goto end;
1762 else if (stat.cbSize.u.LowPart < sizeof(PROPERTYSETHEADER) +
1763 sizeof(FORMATIDOFFSET))
1765 WARN("stream too small\n");
1766 hr = STG_E_INVALIDHEADER;
1767 goto end;
1769 seek.QuadPart = 0;
1770 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1771 if (FAILED(hr))
1772 goto end;
1773 hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr);
1774 /* I've only seen reserved == 1, but the article says I shouldn't disallow
1775 * higher values.
1777 if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1)
1779 WARN("bad magic in prop set header\n");
1780 hr = STG_E_INVALIDHEADER;
1781 goto end;
1783 if (hdr.wFormat != 0 && hdr.wFormat != 1)
1785 WARN("bad format version %d\n", hdr.wFormat);
1786 hr = STG_E_INVALIDHEADER;
1787 goto end;
1789 This->format = hdr.wFormat;
1790 This->clsid = hdr.clsid;
1791 This->originatorOS = hdr.dwOSVer;
1792 if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC)
1793 WARN("File comes from a Mac, strings will probably be screwed up\n");
1794 hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset);
1795 if (FAILED(hr))
1796 goto end;
1797 if (fmtOffset.dwOffset > stat.cbSize.u.LowPart)
1799 WARN("invalid offset %ld (stream length is %ld)\n", fmtOffset.dwOffset, stat.cbSize.u.LowPart);
1800 hr = STG_E_INVALIDHEADER;
1801 goto end;
1803 /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
1804 * follows not one, but two sections. The first contains the standard properties
1805 * for the document summary information, and the second consists of user-defined
1806 * properties. This is the only case in which multiple sections are
1807 * allowed.
1808 * Reading the second stream isn't implemented yet.
1810 hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, &sectionHdr);
1811 if (FAILED(hr))
1812 goto end;
1813 /* The section size includes the section header, so check it */
1814 if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER))
1816 WARN("section header too small, got %ld\n", sectionHdr.cbSection);
1817 hr = STG_E_INVALIDHEADER;
1818 goto end;
1820 buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection -
1821 sizeof(PROPERTYSECTIONHEADER));
1822 if (!buf)
1824 hr = STG_E_INSUFFICIENTMEMORY;
1825 goto end;
1827 read_buffer.data = buf;
1828 read_buffer.size = sectionHdr.cbSection - sizeof(sectionHdr);
1830 hr = IStream_Read(This->stm, read_buffer.data, read_buffer.size, &count);
1831 if (FAILED(hr))
1832 goto end;
1833 TRACE("Reading %ld properties:\n", sectionHdr.cProperties);
1834 for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
1836 PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(read_buffer.data +
1837 i * sizeof(PROPERTYIDOFFSET));
1839 if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) ||
1840 idOffset->dwOffset > sectionHdr.cbSection - sizeof(DWORD))
1841 hr = STG_E_INVALIDPOINTER;
1842 else
1844 if (idOffset->propid >= PID_FIRST_USABLE &&
1845 idOffset->propid < PID_MIN_READONLY && idOffset->propid >
1846 This->highestProp)
1847 This->highestProp = idOffset->propid;
1848 if (idOffset->propid == PID_DICTIONARY)
1850 /* Don't read the dictionary yet, its entries depend on the
1851 * code page. Just store the offset so we know to read it
1852 * later.
1854 dictOffset = idOffset->dwOffset;
1855 TRACE("Dictionary offset is %ld\n", dictOffset);
1857 else
1859 PROPVARIANT prop;
1861 PropVariantInit(&prop);
1862 if (SUCCEEDED(PropertyStorage_ReadProperty(&prop, &read_buffer,
1863 idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER), This->codePage,
1864 Allocate_CoTaskMemAlloc, NULL)))
1866 TRACE("Read property with ID %#lx, type %d\n", idOffset->propid, prop.vt);
1867 switch(idOffset->propid)
1869 case PID_CODEPAGE:
1870 if (prop.vt == VT_I2)
1871 This->codePage = (USHORT)prop.iVal;
1872 break;
1873 case PID_LOCALE:
1874 if (prop.vt == VT_I4)
1875 This->locale = (LCID)prop.lVal;
1876 break;
1877 case PID_BEHAVIOR:
1878 if (prop.vt == VT_I4 && prop.lVal)
1879 This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
1880 /* The format should already be 1, but just in case */
1881 This->format = 1;
1882 break;
1883 default:
1884 hr = PropertyStorage_StorePropWithId(This,
1885 idOffset->propid, &prop, This->codePage);
1888 PropVariantClear(&prop);
1892 if (!This->codePage)
1894 /* default to Unicode unless told not to, as specified on msdn */
1895 if (This->grfFlags & PROPSETFLAG_ANSI)
1896 This->codePage = GetACP();
1897 else
1898 This->codePage = CP_UNICODE;
1900 if (!This->locale)
1901 This->locale = LOCALE_SYSTEM_DEFAULT;
1902 TRACE("Code page is %d, locale is %ld\n", This->codePage, This->locale);
1903 if (dictOffset)
1904 hr = PropertyStorage_ReadDictionary(This, &read_buffer, dictOffset - sizeof(PROPERTYSECTIONHEADER));
1906 end:
1907 HeapFree(GetProcessHeap(), 0, buf);
1908 if (FAILED(hr))
1910 dictionary_destroy(This->name_to_propid);
1911 This->name_to_propid = NULL;
1912 dictionary_destroy(This->propid_to_name);
1913 This->propid_to_name = NULL;
1914 dictionary_destroy(This->propid_to_prop);
1915 This->propid_to_prop = NULL;
1917 return hr;
1920 static void PropertyStorage_MakeHeader(PropertyStorage_impl *This,
1921 PROPERTYSETHEADER *hdr)
1923 assert(hdr);
1924 StorageUtl_WriteWord(&hdr->wByteOrder, 0, PROPSETHDR_BYTEORDER_MAGIC);
1925 StorageUtl_WriteWord(&hdr->wFormat, 0, This->format);
1926 StorageUtl_WriteDWord(&hdr->dwOSVer, 0, This->originatorOS);
1927 StorageUtl_WriteGUID(&hdr->clsid, 0, &This->clsid);
1928 StorageUtl_WriteDWord(&hdr->reserved, 0, 1);
1931 static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl *This,
1932 FORMATIDOFFSET *fmtOffset)
1934 assert(fmtOffset);
1935 StorageUtl_WriteGUID(fmtOffset, 0, &This->fmtid);
1936 StorageUtl_WriteDWord(fmtOffset, offsetof(FORMATIDOFFSET, dwOffset),
1937 sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET));
1940 static void PropertyStorage_MakeSectionHdr(DWORD cbSection, DWORD numProps,
1941 PROPERTYSECTIONHEADER *hdr)
1943 assert(hdr);
1944 StorageUtl_WriteDWord(hdr, 0, cbSection);
1945 StorageUtl_WriteDWord(hdr, offsetof(PROPERTYSECTIONHEADER, cProperties), numProps);
1948 static void PropertyStorage_MakePropertyIdOffset(DWORD propid, DWORD dwOffset,
1949 PROPERTYIDOFFSET *propIdOffset)
1951 assert(propIdOffset);
1952 StorageUtl_WriteDWord(propIdOffset, 0, propid);
1953 StorageUtl_WriteDWord(propIdOffset, offsetof(PROPERTYIDOFFSET, dwOffset), dwOffset);
1956 static inline HRESULT PropertyStorage_WriteWStringToStream(IStream *stm,
1957 LPCWSTR str, DWORD len, DWORD *written)
1959 #ifdef WORDS_BIGENDIAN
1960 WCHAR *leStr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1961 HRESULT hr;
1963 if (!leStr)
1964 return E_OUTOFMEMORY;
1965 memcpy(leStr, str, len * sizeof(WCHAR));
1966 PropertyStorage_ByteSwapString(leStr, len);
1967 hr = IStream_Write(stm, leStr, len, written);
1968 HeapFree(GetProcessHeap(), 0, leStr);
1969 return hr;
1970 #else
1971 return IStream_Write(stm, str, len * sizeof(WCHAR), written);
1972 #endif
1975 struct DictionaryClosure
1977 HRESULT hr;
1978 DWORD bytesWritten;
1981 static BOOL PropertyStorage_DictionaryWriter(const void *key,
1982 const void *value, void *extra, void *closure)
1984 PropertyStorage_impl *This = extra;
1985 struct DictionaryClosure *c = closure;
1986 DWORD propid, keyLen;
1987 ULONG count;
1989 assert(key);
1990 assert(closure);
1991 StorageUtl_WriteDWord(&propid, 0, PtrToUlong(value));
1992 c->hr = IStream_Write(This->stm, &propid, sizeof(propid), &count);
1993 if (FAILED(c->hr))
1994 goto end;
1995 c->bytesWritten += sizeof(DWORD);
1996 if (This->codePage == CP_UNICODE)
1998 DWORD pad = 0, pad_len;
2000 StorageUtl_WriteDWord(&keyLen, 0, lstrlenW((LPCWSTR)key) + 1);
2001 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
2002 if (FAILED(c->hr))
2003 goto end;
2004 c->bytesWritten += sizeof(DWORD);
2005 c->hr = PropertyStorage_WriteWStringToStream(This->stm, key, keyLen,
2006 &count);
2007 if (FAILED(c->hr))
2008 goto end;
2009 keyLen *= sizeof(WCHAR);
2010 c->bytesWritten += keyLen;
2012 /* Align to 4 bytes. */
2013 pad_len = sizeof(DWORD) - keyLen % sizeof(DWORD);
2014 if (pad_len)
2016 c->hr = IStream_Write(This->stm, &pad, pad_len, &count);
2017 if (FAILED(c->hr))
2018 goto end;
2019 c->bytesWritten += pad_len;
2022 else
2024 StorageUtl_WriteDWord(&keyLen, 0, strlen((LPCSTR)key) + 1);
2025 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
2026 if (FAILED(c->hr))
2027 goto end;
2028 c->bytesWritten += sizeof(DWORD);
2029 c->hr = IStream_Write(This->stm, key, keyLen, &count);
2030 if (FAILED(c->hr))
2031 goto end;
2032 c->bytesWritten += keyLen;
2034 end:
2035 return SUCCEEDED(c->hr);
2038 #define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)
2040 /* Writes the dictionary to the stream. Assumes without checking that the
2041 * dictionary isn't empty.
2043 static HRESULT PropertyStorage_WriteDictionaryToStream(
2044 PropertyStorage_impl *This, DWORD *sectionOffset)
2046 HRESULT hr;
2047 LARGE_INTEGER seek;
2048 PROPERTYIDOFFSET propIdOffset;
2049 ULONG count;
2050 DWORD dwTemp;
2051 struct DictionaryClosure closure;
2053 assert(sectionOffset);
2055 /* The dictionary's always the first property written, so seek to its
2056 * spot.
2058 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER);
2059 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2060 if (FAILED(hr))
2061 goto end;
2062 PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY, *sectionOffset,
2063 &propIdOffset);
2064 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
2065 if (FAILED(hr))
2066 goto end;
2068 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
2069 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2070 if (FAILED(hr))
2071 goto end;
2072 StorageUtl_WriteDWord(&dwTemp, 0, dictionary_num_entries(This->name_to_propid));
2073 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2074 if (FAILED(hr))
2075 goto end;
2076 *sectionOffset += sizeof(dwTemp);
2078 closure.hr = S_OK;
2079 closure.bytesWritten = 0;
2080 dictionary_enumerate(This->name_to_propid, PropertyStorage_DictionaryWriter,
2081 &closure);
2082 hr = closure.hr;
2083 if (FAILED(hr))
2084 goto end;
2085 *sectionOffset += closure.bytesWritten;
2086 if (closure.bytesWritten % sizeof(DWORD))
2088 DWORD padding = sizeof(DWORD) - closure.bytesWritten % sizeof(DWORD);
2089 TRACE("adding %ld bytes of padding\n", padding);
2090 *sectionOffset += padding;
2093 end:
2094 return hr;
2097 static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This,
2098 DWORD propNum, DWORD propid, const PROPVARIANT *var, DWORD *sectionOffset)
2100 DWORD len, dwType, dwTemp, bytesWritten;
2101 HRESULT hr;
2102 LARGE_INTEGER seek;
2103 PROPERTYIDOFFSET propIdOffset;
2104 ULARGE_INTEGER ularge;
2105 ULONG count;
2107 assert(var);
2108 assert(sectionOffset);
2110 TRACE("%p, %ld, %#lx, %d, %ld.\n", This, propNum, propid, var->vt,
2111 *sectionOffset);
2113 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) +
2114 propNum * sizeof(PROPERTYIDOFFSET);
2115 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2116 if (FAILED(hr))
2117 goto end;
2118 PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset);
2119 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
2120 if (FAILED(hr))
2121 goto end;
2123 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
2124 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2125 if (FAILED(hr))
2126 goto end;
2127 StorageUtl_WriteDWord(&dwType, 0, var->vt);
2128 hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count);
2129 if (FAILED(hr))
2130 goto end;
2131 *sectionOffset += sizeof(dwType);
2133 switch (var->vt)
2135 case VT_EMPTY:
2136 case VT_NULL:
2137 bytesWritten = 0;
2138 break;
2139 case VT_I1:
2140 case VT_UI1:
2141 hr = IStream_Write(This->stm, &var->cVal, sizeof(var->cVal),
2142 &count);
2143 bytesWritten = count;
2144 break;
2145 case VT_I2:
2146 case VT_UI2:
2148 WORD wTemp;
2150 StorageUtl_WriteWord(&wTemp, 0, var->iVal);
2151 hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count);
2152 bytesWritten = count;
2153 break;
2155 case VT_I4:
2156 case VT_UI4:
2158 StorageUtl_WriteDWord(&dwTemp, 0, var->lVal);
2159 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2160 bytesWritten = count;
2161 break;
2163 case VT_I8:
2164 case VT_UI8:
2166 StorageUtl_WriteULargeInteger(&ularge, 0, &var->uhVal);
2167 hr = IStream_Write(This->stm, &ularge, sizeof(ularge), &bytesWritten);
2168 break;
2170 case VT_LPSTR:
2172 if (This->codePage == CP_UNICODE)
2173 len = (lstrlenW(var->pwszVal) + 1) * sizeof(WCHAR);
2174 else
2175 len = lstrlenA(var->pszVal) + 1;
2176 StorageUtl_WriteDWord(&dwTemp, 0, len);
2177 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2178 if (FAILED(hr))
2179 goto end;
2180 hr = IStream_Write(This->stm, var->pszVal, len, &count);
2181 bytesWritten = count + sizeof(DWORD);
2182 break;
2184 case VT_BSTR:
2186 if (This->codePage == CP_UNICODE)
2188 len = SysStringByteLen(var->bstrVal) + sizeof(WCHAR);
2189 StorageUtl_WriteDWord(&dwTemp, 0, len);
2190 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2191 if (SUCCEEDED(hr))
2192 hr = IStream_Write(This->stm, var->bstrVal, len, &count);
2194 else
2196 char *str;
2198 len = WideCharToMultiByte(This->codePage, 0, var->bstrVal, SysStringLen(var->bstrVal) + 1,
2199 NULL, 0, NULL, NULL);
2201 str = heap_alloc(len);
2202 if (!str)
2204 hr = E_OUTOFMEMORY;
2205 goto end;
2208 WideCharToMultiByte(This->codePage, 0, var->bstrVal, SysStringLen(var->bstrVal),
2209 str, len, NULL, NULL);
2210 StorageUtl_WriteDWord(&dwTemp, 0, len);
2211 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2212 if (SUCCEEDED(hr))
2213 hr = IStream_Write(This->stm, str, len, &count);
2214 heap_free(str);
2217 bytesWritten = count + sizeof(DWORD);
2218 break;
2220 case VT_LPWSTR:
2222 len = lstrlenW(var->pwszVal) + 1;
2224 StorageUtl_WriteDWord(&dwTemp, 0, len);
2225 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2226 if (FAILED(hr))
2227 goto end;
2228 hr = IStream_Write(This->stm, var->pwszVal, len * sizeof(WCHAR),
2229 &count);
2230 bytesWritten = count + sizeof(DWORD);
2231 break;
2233 case VT_FILETIME:
2235 FILETIME temp;
2237 StorageUtl_WriteULargeInteger(&temp, 0, (const ULARGE_INTEGER *)&var->filetime);
2238 hr = IStream_Write(This->stm, &temp, sizeof(temp), &count);
2239 bytesWritten = count;
2240 break;
2242 case VT_CF:
2244 DWORD cf_hdr[2];
2246 len = var->pclipdata->cbSize;
2247 StorageUtl_WriteDWord(&cf_hdr[0], 0, len + 8);
2248 StorageUtl_WriteDWord(&cf_hdr[1], 0, var->pclipdata->ulClipFmt);
2249 hr = IStream_Write(This->stm, cf_hdr, sizeof(cf_hdr), &count);
2250 if (FAILED(hr))
2251 goto end;
2252 hr = IStream_Write(This->stm, var->pclipdata->pClipData,
2253 len - sizeof(var->pclipdata->ulClipFmt), &count);
2254 if (FAILED(hr))
2255 goto end;
2256 bytesWritten = count + sizeof cf_hdr;
2257 break;
2259 case VT_CLSID:
2261 CLSID temp;
2263 StorageUtl_WriteGUID(&temp, 0, var->puuid);
2264 hr = IStream_Write(This->stm, &temp, sizeof(temp), &count);
2265 bytesWritten = count;
2266 break;
2268 case VT_BLOB:
2270 StorageUtl_WriteDWord(&dwTemp, 0, var->blob.cbSize);
2271 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2272 if (FAILED(hr))
2273 goto end;
2274 hr = IStream_Write(This->stm, var->blob.pBlobData, var->blob.cbSize, &count);
2275 bytesWritten = count + sizeof(DWORD);
2276 break;
2278 default:
2279 FIXME("unsupported type: %d\n", var->vt);
2280 return STG_E_INVALIDPARAMETER;
2283 if (SUCCEEDED(hr))
2285 *sectionOffset += bytesWritten;
2286 if (bytesWritten % sizeof(DWORD))
2288 DWORD padding = sizeof(DWORD) - bytesWritten % sizeof(DWORD);
2289 TRACE("adding %ld bytes of padding\n", padding);
2290 *sectionOffset += padding;
2294 end:
2295 return hr;
2298 struct PropertyClosure
2300 HRESULT hr;
2301 DWORD propNum;
2302 DWORD *sectionOffset;
2305 static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value,
2306 void *extra, void *closure)
2308 PropertyStorage_impl *This = extra;
2309 struct PropertyClosure *c = closure;
2311 assert(key);
2312 assert(value);
2313 assert(extra);
2314 assert(closure);
2315 c->hr = PropertyStorage_WritePropertyToStream(This, c->propNum++,
2316 PtrToUlong(key), value, c->sectionOffset);
2317 return SUCCEEDED(c->hr);
2320 static HRESULT PropertyStorage_WritePropertiesToStream(
2321 PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset)
2323 struct PropertyClosure closure;
2325 assert(sectionOffset);
2326 closure.hr = S_OK;
2327 closure.propNum = startingPropNum;
2328 closure.sectionOffset = sectionOffset;
2329 dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter,
2330 &closure);
2331 return closure.hr;
2334 static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This)
2336 HRESULT hr;
2337 ULONG count = 0;
2338 LARGE_INTEGER seek = { {0} };
2339 PROPERTYSETHEADER hdr;
2340 FORMATIDOFFSET fmtOffset;
2342 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2343 if (FAILED(hr))
2344 goto end;
2345 PropertyStorage_MakeHeader(This, &hdr);
2346 hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count);
2347 if (FAILED(hr))
2348 goto end;
2349 if (count != sizeof(hdr))
2351 hr = STG_E_WRITEFAULT;
2352 goto end;
2355 PropertyStorage_MakeFmtIdOffset(This, &fmtOffset);
2356 hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count);
2357 if (FAILED(hr))
2358 goto end;
2359 if (count != sizeof(fmtOffset))
2361 hr = STG_E_WRITEFAULT;
2362 goto end;
2364 hr = S_OK;
2366 end:
2367 return hr;
2370 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This)
2372 PROPERTYSECTIONHEADER sectionHdr;
2373 HRESULT hr;
2374 ULONG count;
2375 LARGE_INTEGER seek;
2376 DWORD numProps, prop, sectionOffset, dwTemp;
2377 PROPVARIANT var;
2379 PropertyStorage_WriteHeadersToStream(This);
2381 /* Count properties. Always at least one property, the code page */
2382 numProps = 1;
2383 if (dictionary_num_entries(This->name_to_propid))
2384 numProps++;
2385 if (This->locale != LOCALE_SYSTEM_DEFAULT)
2386 numProps++;
2387 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2388 numProps++;
2389 numProps += dictionary_num_entries(This->propid_to_prop);
2391 /* Write section header with 0 bytes right now, I'll adjust it after
2392 * writing properties.
2394 PropertyStorage_MakeSectionHdr(0, numProps, &sectionHdr);
2395 seek.QuadPart = SECTIONHEADER_OFFSET;
2396 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2397 if (FAILED(hr))
2398 goto end;
2399 hr = IStream_Write(This->stm, &sectionHdr, sizeof(sectionHdr), &count);
2400 if (FAILED(hr))
2401 goto end;
2403 prop = 0;
2404 sectionOffset = sizeof(PROPERTYSECTIONHEADER) +
2405 numProps * sizeof(PROPERTYIDOFFSET);
2407 if (dictionary_num_entries(This->name_to_propid))
2409 prop++;
2410 hr = PropertyStorage_WriteDictionaryToStream(This, &sectionOffset);
2411 if (FAILED(hr))
2412 goto end;
2415 PropVariantInit(&var);
2417 var.vt = VT_I2;
2418 var.iVal = This->codePage;
2419 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE,
2420 &var, &sectionOffset);
2421 if (FAILED(hr))
2422 goto end;
2424 if (This->locale != LOCALE_SYSTEM_DEFAULT)
2426 var.vt = VT_I4;
2427 var.lVal = This->locale;
2428 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE,
2429 &var, &sectionOffset);
2430 if (FAILED(hr))
2431 goto end;
2434 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2436 var.vt = VT_I4;
2437 var.lVal = 1;
2438 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR,
2439 &var, &sectionOffset);
2440 if (FAILED(hr))
2441 goto end;
2444 hr = PropertyStorage_WritePropertiesToStream(This, prop, &sectionOffset);
2445 if (FAILED(hr))
2446 goto end;
2448 /* Now write the byte count of the section */
2449 seek.QuadPart = SECTIONHEADER_OFFSET;
2450 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2451 if (FAILED(hr))
2452 goto end;
2453 StorageUtl_WriteDWord(&dwTemp, 0, sectionOffset);
2454 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2456 end:
2457 return hr;
2460 /***********************************************************************
2461 * PropertyStorage_Construct
2463 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This)
2465 dictionary_destroy(This->name_to_propid);
2466 This->name_to_propid = NULL;
2467 dictionary_destroy(This->propid_to_name);
2468 This->propid_to_name = NULL;
2469 dictionary_destroy(This->propid_to_prop);
2470 This->propid_to_prop = NULL;
2473 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This)
2475 HRESULT hr = S_OK;
2477 This->name_to_propid = dictionary_create(
2478 PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy,
2479 This);
2480 if (!This->name_to_propid)
2482 hr = STG_E_INSUFFICIENTMEMORY;
2483 goto end;
2485 This->propid_to_name = dictionary_create(PropertyStorage_PropCompare,
2486 NULL, This);
2487 if (!This->propid_to_name)
2489 hr = STG_E_INSUFFICIENTMEMORY;
2490 goto end;
2492 This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
2493 PropertyStorage_PropertyDestroy, This);
2494 if (!This->propid_to_prop)
2496 hr = STG_E_INSUFFICIENTMEMORY;
2497 goto end;
2499 end:
2500 if (FAILED(hr))
2501 PropertyStorage_DestroyDictionaries(This);
2502 return hr;
2505 static HRESULT PropertyStorage_BaseConstruct(IStream *stm,
2506 REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps)
2508 HRESULT hr = S_OK;
2510 assert(pps);
2511 assert(rfmtid);
2512 *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps);
2513 if (!*pps)
2514 return E_OUTOFMEMORY;
2516 (*pps)->IPropertyStorage_iface.lpVtbl = &IPropertyStorage_Vtbl;
2517 (*pps)->ref = 1;
2518 InitializeCriticalSection(&(*pps)->cs);
2519 (*pps)->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PropertyStorage_impl.cs");
2520 (*pps)->stm = stm;
2521 (*pps)->fmtid = *rfmtid;
2522 (*pps)->grfMode = grfMode;
2524 hr = PropertyStorage_CreateDictionaries(*pps);
2525 if (FAILED(hr))
2527 (*pps)->cs.DebugInfo->Spare[0] = 0;
2528 DeleteCriticalSection(&(*pps)->cs);
2529 HeapFree(GetProcessHeap(), 0, *pps);
2530 *pps = NULL;
2532 else IStream_AddRef( stm );
2534 return hr;
2537 static HRESULT PropertyStorage_ConstructFromStream(IStream *stm,
2538 REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps)
2540 PropertyStorage_impl *ps;
2541 HRESULT hr;
2543 assert(pps);
2544 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2545 if (SUCCEEDED(hr))
2547 hr = PropertyStorage_ReadFromStream(ps);
2548 if (SUCCEEDED(hr))
2550 *pps = &ps->IPropertyStorage_iface;
2551 TRACE("PropertyStorage %p constructed\n", ps);
2552 hr = S_OK;
2554 else IPropertyStorage_Release( &ps->IPropertyStorage_iface );
2556 return hr;
2559 static HRESULT PropertyStorage_ConstructEmpty(IStream *stm,
2560 REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
2562 PropertyStorage_impl *ps;
2563 HRESULT hr;
2565 assert(pps);
2566 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2567 if (SUCCEEDED(hr))
2569 ps->format = 0;
2570 ps->grfFlags = grfFlags;
2571 if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2572 ps->format = 1;
2573 /* default to Unicode unless told not to, as specified on msdn */
2574 if (ps->grfFlags & PROPSETFLAG_ANSI)
2575 ps->codePage = GetACP();
2576 else
2577 ps->codePage = CP_UNICODE;
2578 ps->locale = LOCALE_SYSTEM_DEFAULT;
2579 TRACE("Code page is %d, locale is %ld\n", ps->codePage, ps->locale);
2580 *pps = &ps->IPropertyStorage_iface;
2581 TRACE("PropertyStorage %p constructed\n", ps);
2582 hr = S_OK;
2584 return hr;
2588 /***********************************************************************
2589 * Implementation of IPropertySetStorage
2592 struct enum_stat_propset_stg
2594 IEnumSTATPROPSETSTG IEnumSTATPROPSETSTG_iface;
2595 LONG refcount;
2596 STATPROPSETSTG *stats;
2597 size_t current;
2598 size_t count;
2601 static struct enum_stat_propset_stg *impl_from_IEnumSTATPROPSETSTG(IEnumSTATPROPSETSTG *iface)
2603 return CONTAINING_RECORD(iface, struct enum_stat_propset_stg, IEnumSTATPROPSETSTG_iface);
2606 static HRESULT WINAPI enum_stat_propset_stg_QueryInterface(IEnumSTATPROPSETSTG *iface, REFIID riid, void **obj)
2608 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
2610 if (IsEqualIID(riid, &IID_IEnumSTATPROPSETSTG) ||
2611 IsEqualIID(riid, &IID_IUnknown))
2613 *obj = iface;
2614 IEnumSTATPROPSETSTG_AddRef(iface);
2615 return S_OK;
2618 WARN("Unsupported interface %s.\n", debugstr_guid(riid));
2619 return E_NOINTERFACE;
2622 static ULONG WINAPI enum_stat_propset_stg_AddRef(IEnumSTATPROPSETSTG *iface)
2624 struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface);
2625 LONG refcount = InterlockedIncrement(&psenum->refcount);
2627 TRACE("%p, refcount %lu.\n", iface, refcount);
2629 return refcount;
2632 static ULONG WINAPI enum_stat_propset_stg_Release(IEnumSTATPROPSETSTG *iface)
2634 struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface);
2635 LONG refcount = InterlockedDecrement(&psenum->refcount);
2637 TRACE("%p, refcount %lu.\n", iface, refcount);
2639 if (!refcount)
2641 heap_free(psenum->stats);
2642 heap_free(psenum);
2645 return refcount;
2648 static HRESULT WINAPI enum_stat_propset_stg_Next(IEnumSTATPROPSETSTG *iface, ULONG celt,
2649 STATPROPSETSTG *ret, ULONG *fetched)
2651 struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface);
2652 ULONG count = 0;
2654 TRACE("%p, %lu, %p, %p.\n", iface, celt, ret, fetched);
2656 if (psenum->current == ~0u)
2657 psenum->current = 0;
2659 while (count < celt && psenum->current < psenum->count)
2660 ret[count++] = psenum->stats[psenum->current++];
2662 if (fetched)
2663 *fetched = count;
2665 return count < celt ? S_FALSE : S_OK;
2668 static HRESULT WINAPI enum_stat_propset_stg_Skip(IEnumSTATPROPSETSTG *iface, ULONG celt)
2670 FIXME("%p, %lu.\n", iface, celt);
2672 return S_OK;
2675 static HRESULT WINAPI enum_stat_propset_stg_Reset(IEnumSTATPROPSETSTG *iface)
2677 struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface);
2679 TRACE("%p.\n", iface);
2681 psenum->current = ~0u;
2683 return S_OK;
2686 static HRESULT WINAPI enum_stat_propset_stg_Clone(IEnumSTATPROPSETSTG *iface, IEnumSTATPROPSETSTG **ppenum)
2688 FIXME("%p, %p.\n", iface, ppenum);
2690 return E_NOTIMPL;
2693 static const IEnumSTATPROPSETSTGVtbl enum_stat_propset_stg_vtbl =
2695 enum_stat_propset_stg_QueryInterface,
2696 enum_stat_propset_stg_AddRef,
2697 enum_stat_propset_stg_Release,
2698 enum_stat_propset_stg_Next,
2699 enum_stat_propset_stg_Skip,
2700 enum_stat_propset_stg_Reset,
2701 enum_stat_propset_stg_Clone,
2704 static HRESULT create_enum_stat_propset_stg(StorageImpl *storage, IEnumSTATPROPSETSTG **ret)
2706 IStorage *stg = &storage->base.IStorage_iface;
2707 IEnumSTATSTG *penum = NULL;
2708 STATSTG stat;
2709 ULONG count;
2710 HRESULT hr;
2712 struct enum_stat_propset_stg *enum_obj;
2714 enum_obj = heap_alloc_zero(sizeof(*enum_obj));
2715 if (!enum_obj)
2716 return E_OUTOFMEMORY;
2718 enum_obj->IEnumSTATPROPSETSTG_iface.lpVtbl = &enum_stat_propset_stg_vtbl;
2719 enum_obj->refcount = 1;
2721 /* add all the property set elements into a list */
2722 hr = IStorage_EnumElements(stg, 0, NULL, 0, &penum);
2723 if (FAILED(hr))
2724 goto done;
2726 /* Allocate stats array and fill it. */
2727 while ((hr = IEnumSTATSTG_Next(penum, 1, &stat, &count)) == S_OK)
2729 enum_obj->count++;
2730 CoTaskMemFree(stat.pwcsName);
2733 if (FAILED(hr))
2734 goto done;
2736 enum_obj->stats = heap_alloc(enum_obj->count * sizeof(*enum_obj->stats));
2737 if (!enum_obj->stats)
2739 hr = E_OUTOFMEMORY;
2740 goto done;
2742 enum_obj->count = 0;
2744 if (FAILED(hr = IEnumSTATSTG_Reset(penum)))
2745 goto done;
2747 while (IEnumSTATSTG_Next(penum, 1, &stat, &count) == S_OK)
2749 if (!stat.pwcsName)
2750 continue;
2752 if (stat.pwcsName[0] == 5 && stat.type == STGTY_STREAM)
2754 STATPROPSETSTG *ptr = &enum_obj->stats[enum_obj->count++];
2756 PropStgNameToFmtId(stat.pwcsName, &ptr->fmtid);
2758 TRACE("adding %s - %s.\n", debugstr_w(stat.pwcsName), debugstr_guid(&ptr->fmtid));
2760 ptr->mtime = stat.mtime;
2761 ptr->atime = stat.atime;
2762 ptr->ctime = stat.ctime;
2763 ptr->grfFlags = stat.grfMode;
2764 ptr->clsid = stat.clsid;
2766 CoTaskMemFree(stat.pwcsName);
2769 done:
2771 if (penum)
2772 IEnumSTATSTG_Release(penum);
2774 if (SUCCEEDED(hr))
2776 *ret = &enum_obj->IEnumSTATPROPSETSTG_iface;
2778 else
2780 *ret = NULL;
2781 IEnumSTATPROPSETSTG_Release(&enum_obj->IEnumSTATPROPSETSTG_iface);
2784 return hr;
2787 /************************************************************************
2788 * IPropertySetStorage_fnQueryInterface (IUnknown)
2790 * This method forwards to the common QueryInterface implementation
2792 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
2793 IPropertySetStorage *ppstg,
2794 REFIID riid,
2795 void** ppvObject)
2797 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2798 return IStorage_QueryInterface( &This->base.IStorage_iface, riid, ppvObject );
2801 /************************************************************************
2802 * IPropertySetStorage_fnAddRef (IUnknown)
2804 * This method forwards to the common AddRef implementation
2806 static ULONG WINAPI IPropertySetStorage_fnAddRef(
2807 IPropertySetStorage *ppstg)
2809 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2810 return IStorage_AddRef( &This->base.IStorage_iface );
2813 /************************************************************************
2814 * IPropertySetStorage_fnRelease (IUnknown)
2816 * This method forwards to the common Release implementation
2818 static ULONG WINAPI IPropertySetStorage_fnRelease(
2819 IPropertySetStorage *ppstg)
2821 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2822 return IStorage_Release( &This->base.IStorage_iface );
2825 /************************************************************************
2826 * IPropertySetStorage_fnCreate (IPropertySetStorage)
2828 static HRESULT WINAPI IPropertySetStorage_fnCreate(
2829 IPropertySetStorage *ppstg,
2830 REFFMTID rfmtid,
2831 const CLSID* pclsid,
2832 DWORD grfFlags,
2833 DWORD grfMode,
2834 IPropertyStorage** ppprstg)
2836 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2837 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2838 IStream *stm = NULL;
2839 HRESULT r;
2841 TRACE("%p, %s %#lx, %#lx, %p.\n", This, debugstr_guid(rfmtid), grfFlags,
2842 grfMode, ppprstg);
2844 /* be picky */
2845 if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
2847 r = STG_E_INVALIDFLAG;
2848 goto end;
2851 if (!rfmtid)
2853 r = E_INVALIDARG;
2854 goto end;
2857 /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
2858 * storage, not a stream. For now, disallow it.
2860 if (grfFlags & PROPSETFLAG_NONSIMPLE)
2862 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2863 r = STG_E_INVALIDFLAG;
2864 goto end;
2867 r = FmtIdToPropStgName(rfmtid, name);
2868 if (FAILED(r))
2869 goto end;
2871 r = IStorage_CreateStream( &This->base.IStorage_iface, name, grfMode, 0, 0, &stm );
2872 if (FAILED(r))
2873 goto end;
2875 r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg);
2877 IStream_Release( stm );
2879 end:
2880 TRACE("returning %#lx\n", r);
2881 return r;
2884 /************************************************************************
2885 * IPropertySetStorage_fnOpen (IPropertySetStorage)
2887 static HRESULT WINAPI IPropertySetStorage_fnOpen(
2888 IPropertySetStorage *ppstg,
2889 REFFMTID rfmtid,
2890 DWORD grfMode,
2891 IPropertyStorage** ppprstg)
2893 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2894 IStream *stm = NULL;
2895 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2896 HRESULT r;
2898 TRACE("%p, %s, %#lx, %p.\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
2900 /* be picky */
2901 if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
2902 grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
2904 r = STG_E_INVALIDFLAG;
2905 goto end;
2908 if (!rfmtid)
2910 r = E_INVALIDARG;
2911 goto end;
2914 r = FmtIdToPropStgName(rfmtid, name);
2915 if (FAILED(r))
2916 goto end;
2918 r = IStorage_OpenStream( &This->base.IStorage_iface, name, 0, grfMode, 0, &stm );
2919 if (FAILED(r))
2920 goto end;
2922 r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg);
2924 IStream_Release( stm );
2926 end:
2927 TRACE("returning %#lx\n", r);
2928 return r;
2931 /************************************************************************
2932 * IPropertySetStorage_fnDelete (IPropertySetStorage)
2934 static HRESULT WINAPI IPropertySetStorage_fnDelete(
2935 IPropertySetStorage *ppstg,
2936 REFFMTID rfmtid)
2938 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2939 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2940 HRESULT r;
2942 TRACE("%p %s\n", This, debugstr_guid(rfmtid));
2944 if (!rfmtid)
2945 return E_INVALIDARG;
2947 r = FmtIdToPropStgName(rfmtid, name);
2948 if (FAILED(r))
2949 return r;
2951 return IStorage_DestroyElement(&This->base.IStorage_iface, name);
2954 static HRESULT WINAPI IPropertySetStorage_fnEnum(IPropertySetStorage *iface, IEnumSTATPROPSETSTG **enum_obj)
2956 StorageImpl *storage = impl_from_IPropertySetStorage(iface);
2958 TRACE("%p, %p.\n", iface, enum_obj);
2960 if (!enum_obj)
2961 return E_INVALIDARG;
2963 return create_enum_stat_propset_stg(storage, enum_obj);
2966 /***********************************************************************
2967 * vtables
2969 const IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
2971 IPropertySetStorage_fnQueryInterface,
2972 IPropertySetStorage_fnAddRef,
2973 IPropertySetStorage_fnRelease,
2974 IPropertySetStorage_fnCreate,
2975 IPropertySetStorage_fnOpen,
2976 IPropertySetStorage_fnDelete,
2977 IPropertySetStorage_fnEnum
2980 static const IPropertyStorageVtbl IPropertyStorage_Vtbl =
2982 IPropertyStorage_fnQueryInterface,
2983 IPropertyStorage_fnAddRef,
2984 IPropertyStorage_fnRelease,
2985 IPropertyStorage_fnReadMultiple,
2986 IPropertyStorage_fnWriteMultiple,
2987 IPropertyStorage_fnDeleteMultiple,
2988 IPropertyStorage_fnReadPropertyNames,
2989 IPropertyStorage_fnWritePropertyNames,
2990 IPropertyStorage_fnDeletePropertyNames,
2991 IPropertyStorage_fnCommit,
2992 IPropertyStorage_fnRevert,
2993 IPropertyStorage_fnEnum,
2994 IPropertyStorage_fnSetTimes,
2995 IPropertyStorage_fnSetClass,
2996 IPropertyStorage_fnStat,
2999 /***********************************************************************
3000 * Format ID <-> name conversion
3002 static const WCHAR szSummaryInfo[] = L"\5SummaryInformation";
3003 static const WCHAR szDocSummaryInfo[] = L"\5DocumentSummaryInformation";
3005 #define BITS_PER_BYTE 8
3006 #define CHARMASK 0x1f
3007 #define BITS_IN_CHARMASK 5
3008 #define NUM_ALPHA_CHARS 26
3010 /***********************************************************************
3011 * FmtIdToPropStgName [ole32.@]
3012 * Returns the storage name of the format ID rfmtid.
3013 * PARAMS
3014 * rfmtid [I] Format ID for which to return a storage name
3015 * str [O] Storage name associated with rfmtid.
3017 * RETURNS
3018 * E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise.
3020 * NOTES
3021 * str must be at least CCH_MAX_PROPSTG_NAME characters in length.
3023 HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str)
3025 static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
3027 TRACE("%s, %p\n", debugstr_guid(rfmtid), str);
3029 if (!rfmtid) return E_INVALIDARG;
3030 if (!str) return E_INVALIDARG;
3032 if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
3033 lstrcpyW(str, szSummaryInfo);
3034 else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
3035 lstrcpyW(str, szDocSummaryInfo);
3036 else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid))
3037 lstrcpyW(str, szDocSummaryInfo);
3038 else
3040 const BYTE *fmtptr;
3041 WCHAR *pstr = str;
3042 ULONG bitsRemaining = BITS_PER_BYTE;
3044 *pstr++ = 5;
3045 for (fmtptr = (const BYTE *)rfmtid; fmtptr < (const BYTE *)rfmtid + sizeof(FMTID); )
3047 ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
3049 if (bitsRemaining >= BITS_IN_CHARMASK)
3051 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
3052 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
3053 *pstr <= 'z')
3054 *pstr += 'A' - 'a';
3055 pstr++;
3056 bitsRemaining -= BITS_IN_CHARMASK;
3057 if (bitsRemaining == 0)
3059 fmtptr++;
3060 bitsRemaining = BITS_PER_BYTE;
3063 else
3065 if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID))
3066 i |= *fmtptr << bitsRemaining;
3067 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
3068 bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK;
3071 *pstr = 0;
3073 TRACE("returning %s\n", debugstr_w(str));
3074 return S_OK;
3077 /***********************************************************************
3078 * PropStgNameToFmtId [ole32.@]
3079 * Returns the format ID corresponding to the given name.
3080 * PARAMS
3081 * str [I] Storage name to convert to a format ID.
3082 * rfmtid [O] Format ID corresponding to str.
3084 * RETURNS
3085 * E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to
3086 * a format ID, S_OK otherwise.
3088 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid)
3090 HRESULT hr = STG_E_INVALIDNAME;
3092 TRACE("%s, %p\n", debugstr_w(str), rfmtid);
3094 if (!rfmtid) return E_INVALIDARG;
3095 if (!str) return STG_E_INVALIDNAME;
3097 if (!lstrcmpiW(str, szDocSummaryInfo))
3099 *rfmtid = FMTID_DocSummaryInformation;
3100 hr = S_OK;
3102 else if (!lstrcmpiW(str, szSummaryInfo))
3104 *rfmtid = FMTID_SummaryInformation;
3105 hr = S_OK;
3107 else
3109 ULONG bits;
3110 BYTE *fmtptr = (BYTE *)rfmtid - 1;
3111 const WCHAR *pstr = str;
3113 memset(rfmtid, 0, sizeof(*rfmtid));
3114 for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE;
3115 bits += BITS_IN_CHARMASK)
3117 ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored;
3118 WCHAR wc;
3120 if (bitsUsed == 0)
3121 fmtptr++;
3122 wc = *++pstr - 'A';
3123 if (wc > NUM_ALPHA_CHARS)
3125 wc += 'A' - 'a';
3126 if (wc > NUM_ALPHA_CHARS)
3128 wc += 'a' - '0' + NUM_ALPHA_CHARS;
3129 if (wc > CHARMASK)
3131 WARN("invalid character (%d)\n", *pstr);
3132 goto end;
3136 *fmtptr |= wc << bitsUsed;
3137 bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK);
3138 if (bitsStored < BITS_IN_CHARMASK)
3140 wc >>= BITS_PER_BYTE - bitsUsed;
3141 if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE)
3143 if (wc != 0)
3145 WARN("extra bits\n");
3146 goto end;
3148 break;
3150 fmtptr++;
3151 *fmtptr |= (BYTE)wc;
3154 hr = S_OK;
3156 end:
3157 return hr;
3160 #ifdef __i386__ /* thiscall functions are i386-specific */
3162 #define DEFINE_STDCALL_WRAPPER(num,func,args) \
3163 __ASM_STDCALL_FUNC(func, args, \
3164 "popl %eax\n\t" \
3165 "popl %ecx\n\t" \
3166 "pushl %eax\n\t" \
3167 "movl (%ecx), %eax\n\t" \
3168 "jmp *(4*(" #num "))(%eax)" )
3170 DEFINE_STDCALL_WRAPPER(0,Allocate_PMemoryAllocator,8)
3171 extern void* WINAPI Allocate_PMemoryAllocator(void *this, ULONG cbSize);
3173 #else
3175 static void* WINAPI Allocate_PMemoryAllocator(void *this, ULONG cbSize)
3177 void* (WINAPI *fn)(void*,ULONG) = **(void***)this;
3178 return fn(this, cbSize);
3181 #endif
3183 BOOLEAN WINAPI StgConvertPropertyToVariant(const SERIALIZEDPROPERTYVALUE* prop,
3184 USHORT CodePage, PROPVARIANT* pvar, void* pma)
3186 struct read_buffer read_buffer;
3187 HRESULT hr;
3189 read_buffer.data = (BYTE *)prop;
3190 read_buffer.size = ~(size_t)0;
3191 hr = PropertyStorage_ReadProperty(pvar, &read_buffer, 0, CodePage, Allocate_PMemoryAllocator, pma);
3193 if (FAILED(hr))
3195 FIXME("should raise C++ exception on failure\n");
3196 PropVariantInit(pvar);
3199 return FALSE;
3202 SERIALIZEDPROPERTYVALUE* WINAPI StgConvertVariantToProperty(const PROPVARIANT *pvar,
3203 USHORT CodePage, SERIALIZEDPROPERTYVALUE *pprop, ULONG *pcb, PROPID pid,
3204 BOOLEAN fReserved, ULONG *pcIndirect)
3206 FIXME("%p, %d, %p, %p, %ld, %d, %p.\n", pvar, CodePage, pprop, pcb, pid, fReserved, pcIndirect);
3208 return NULL;
3211 HRESULT WINAPI StgCreatePropStg(IUnknown *unk, REFFMTID fmt, const CLSID *clsid,
3212 DWORD flags, DWORD reserved, IPropertyStorage **prop_stg)
3214 IStorage *stg;
3215 IStream *stm;
3216 HRESULT r;
3218 TRACE("%p, %s, %s, %#lx, %ld, %p.\n", unk, debugstr_guid(fmt), debugstr_guid(clsid), flags, reserved, prop_stg);
3220 if (!fmt || reserved)
3222 r = E_INVALIDARG;
3223 goto end;
3226 if (flags & PROPSETFLAG_NONSIMPLE)
3228 r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
3229 if (FAILED(r))
3230 goto end;
3232 /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to create a
3233 * storage, not a stream. For now, disallow it.
3235 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
3236 IStorage_Release(stg);
3237 r = STG_E_INVALIDFLAG;
3239 else
3241 r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
3242 if (FAILED(r))
3243 goto end;
3245 r = PropertyStorage_ConstructEmpty(stm, fmt, flags,
3246 STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
3248 IStream_Release( stm );
3251 end:
3252 TRACE("returning %#lx\n", r);
3253 return r;
3256 HRESULT WINAPI StgOpenPropStg(IUnknown *unk, REFFMTID fmt, DWORD flags,
3257 DWORD reserved, IPropertyStorage **prop_stg)
3259 IStorage *stg;
3260 IStream *stm;
3261 HRESULT r;
3263 TRACE("%p, %s, %#lx, %ld, %p.\n", unk, debugstr_guid(fmt), flags, reserved, prop_stg);
3265 if (!fmt || reserved)
3267 r = E_INVALIDARG;
3268 goto end;
3271 if (flags & PROPSETFLAG_NONSIMPLE)
3273 r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
3274 if (FAILED(r))
3275 goto end;
3277 /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to open a
3278 * storage, not a stream. For now, disallow it.
3280 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
3281 IStorage_Release(stg);
3282 r = STG_E_INVALIDFLAG;
3284 else
3286 r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
3287 if (FAILED(r))
3288 goto end;
3290 r = PropertyStorage_ConstructFromStream(stm, fmt,
3291 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
3293 IStream_Release( stm );
3296 end:
3297 TRACE("returning %#lx\n", r);
3298 return r;