dmband: Rewrite band lbin list parsing.
[wine.git] / dlls / ole32 / stg_prop.c
blob8bc159c7dd5c4aef2940079fdb05e72598fa07bb
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 #include "windef.h"
48 #include "winbase.h"
49 #include "winnls.h"
50 #include "winuser.h"
51 #include "wine/asm.h"
52 #include "wine/debug.h"
53 #include "wine/heap.h"
54 #include "dictionary.h"
55 #include "storage32.h"
56 #include "oleauto.h"
58 WINE_DEFAULT_DEBUG_CHANNEL(storage);
60 static inline StorageImpl *impl_from_IPropertySetStorage( IPropertySetStorage *iface )
62 return CONTAINING_RECORD(iface, StorageImpl, base.IPropertySetStorage_iface);
65 /* These are documented in MSDN,
66 * but they don't seem to be in any header file.
68 #define PROPSETHDR_BYTEORDER_MAGIC 0xfffe
69 #define PROPSETHDR_OSVER_KIND_WIN16 0
70 #define PROPSETHDR_OSVER_KIND_MAC 1
71 #define PROPSETHDR_OSVER_KIND_WIN32 2
73 #define CP_UNICODE 1200
75 #define MAX_VERSION_0_PROP_NAME_LENGTH 256
77 #define CFTAG_WINDOWS (-1L)
78 #define CFTAG_MACINTOSH (-2L)
79 #define CFTAG_FMTID (-3L)
80 #define CFTAG_NODATA 0L
82 #define ALIGNED_LENGTH(_Len, _Align) (((_Len)+(_Align))&~(_Align))
84 typedef struct tagPROPERTYSETHEADER
86 WORD wByteOrder; /* always 0xfffe */
87 WORD wFormat; /* can be zero or one */
88 DWORD dwOSVer; /* OS version of originating system */
89 CLSID clsid; /* application CLSID */
90 DWORD reserved; /* always 1 */
91 } PROPERTYSETHEADER;
93 typedef struct tagFORMATIDOFFSET
95 FMTID fmtid;
96 DWORD dwOffset; /* from beginning of stream */
97 } FORMATIDOFFSET;
99 typedef struct tagPROPERTYSECTIONHEADER
101 DWORD cbSection;
102 DWORD cProperties;
103 } PROPERTYSECTIONHEADER;
105 typedef struct tagPROPERTYIDOFFSET
107 DWORD propid;
108 DWORD dwOffset; /* from beginning of section */
109 } PROPERTYIDOFFSET;
111 typedef struct tagPropertyStorage_impl PropertyStorage_impl;
113 /* Initializes the property storage from the stream (and undoes any uncommitted
114 * changes in the process.) Returns an error if there is an error reading or
115 * if the stream format doesn't match what's expected.
117 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *);
119 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *);
121 /* Creates the dictionaries used by the property storage. If successful, all
122 * the dictionaries have been created. If failed, none has been. (This makes
123 * it a bit easier to deal with destroying them.)
125 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *);
127 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *);
129 /* Copies from propvar to prop. If propvar's type is VT_LPSTR, copies the
130 * string using PropertyStorage_StringCopy.
132 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
133 const PROPVARIANT *propvar, UINT targetCP, UINT srcCP);
135 /* Copies the string src, which is encoded using code page srcCP, and returns
136 * it in *dst, in the code page specified by targetCP. The returned string is
137 * allocated using CoTaskMemAlloc.
138 * If srcCP is CP_UNICODE, src is in fact an LPCWSTR. Similarly, if targetCP
139 * is CP_UNICODE, the returned string is in fact an LPWSTR.
140 * Returns S_OK on success, something else on failure.
142 static HRESULT PropertyStorage_StringCopy(LPCSTR src, UINT srcCP, LPSTR *dst,
143 UINT targetCP);
145 static const IPropertyStorageVtbl IPropertyStorage_Vtbl;
147 /***********************************************************************
148 * Implementation of IPropertyStorage
150 struct tagPropertyStorage_impl
152 IPropertyStorage IPropertyStorage_iface;
153 LONG ref;
154 CRITICAL_SECTION cs;
155 IStream *stm;
156 BOOL dirty;
157 FMTID fmtid;
158 CLSID clsid;
159 WORD format;
160 DWORD originatorOS;
161 DWORD grfFlags;
162 DWORD grfMode;
163 UINT codePage;
164 LCID locale;
165 PROPID highestProp;
166 struct dictionary *name_to_propid;
167 struct dictionary *propid_to_name;
168 struct dictionary *propid_to_prop;
171 static inline PropertyStorage_impl *impl_from_IPropertyStorage(IPropertyStorage *iface)
173 return CONTAINING_RECORD(iface, PropertyStorage_impl, IPropertyStorage_iface);
176 struct enum_stat_prop_stg
178 IEnumSTATPROPSTG IEnumSTATPROPSTG_iface;
179 LONG refcount;
180 PropertyStorage_impl *storage;
181 STATPROPSTG *stats;
182 size_t current;
183 size_t count;
186 static struct enum_stat_prop_stg *impl_from_IEnumSTATPROPSTG(IEnumSTATPROPSTG *iface)
188 return CONTAINING_RECORD(iface, struct enum_stat_prop_stg, IEnumSTATPROPSTG_iface);
191 static HRESULT WINAPI enum_stat_prop_stg_QueryInterface(IEnumSTATPROPSTG *iface, REFIID riid, void **obj)
193 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
195 if (IsEqualIID(riid, &IID_IEnumSTATPROPSTG) ||
196 IsEqualIID(riid, &IID_IUnknown))
198 *obj = iface;
199 IEnumSTATPROPSTG_AddRef(iface);
200 return S_OK;
203 WARN("Unsupported interface %s.\n", debugstr_guid(riid));
204 return E_NOINTERFACE;
207 static ULONG WINAPI enum_stat_prop_stg_AddRef(IEnumSTATPROPSTG *iface)
209 struct enum_stat_prop_stg *penum = impl_from_IEnumSTATPROPSTG(iface);
210 LONG refcount = InterlockedIncrement(&penum->refcount);
212 TRACE("%p, refcount %lu.\n", iface, refcount);
214 return refcount;
217 static ULONG WINAPI enum_stat_prop_stg_Release(IEnumSTATPROPSTG *iface)
219 struct enum_stat_prop_stg *penum = impl_from_IEnumSTATPROPSTG(iface);
220 LONG refcount = InterlockedDecrement(&penum->refcount);
222 TRACE("%p, refcount %lu.\n", iface, refcount);
224 if (!refcount)
226 IPropertyStorage_Release(&penum->storage->IPropertyStorage_iface);
227 heap_free(penum->stats);
228 heap_free(penum);
231 return refcount;
234 static HRESULT WINAPI enum_stat_prop_stg_Next(IEnumSTATPROPSTG *iface, ULONG celt, STATPROPSTG *ret, ULONG *fetched)
236 struct enum_stat_prop_stg *penum = impl_from_IEnumSTATPROPSTG(iface);
237 ULONG count = 0;
238 WCHAR *name;
240 TRACE("%p, %lu, %p, %p.\n", iface, celt, ret, fetched);
242 if (penum->current == ~0u)
243 penum->current = 0;
245 while (count < celt && penum->current < penum->count)
247 *ret = penum->stats[penum->current++];
249 if (dictionary_find(penum->storage->propid_to_name, UlongToPtr(ret->propid), (void **)&name))
251 SIZE_T size = (lstrlenW(name) + 1) * sizeof(WCHAR);
252 ret->lpwstrName = CoTaskMemAlloc(size);
253 if (ret->lpwstrName)
254 memcpy(ret->lpwstrName, name, size);
256 ret++;
257 count++;
260 if (fetched)
261 *fetched = count;
263 return count < celt ? S_FALSE : S_OK;
266 static HRESULT WINAPI enum_stat_prop_stg_Skip(IEnumSTATPROPSTG *iface, ULONG celt)
268 FIXME("%p, %lu.\n", iface, celt);
270 return S_OK;
273 static HRESULT WINAPI enum_stat_prop_stg_Reset(IEnumSTATPROPSTG *iface)
275 struct enum_stat_prop_stg *penum = impl_from_IEnumSTATPROPSTG(iface);
277 TRACE("%p.\n", iface);
279 penum->current = ~0u;
281 return S_OK;
284 static HRESULT WINAPI enum_stat_prop_stg_Clone(IEnumSTATPROPSTG *iface, IEnumSTATPROPSTG **ppenum)
286 FIXME("%p, %p.\n", iface, ppenum);
288 return E_NOTIMPL;
291 static const IEnumSTATPROPSTGVtbl enum_stat_prop_stg_vtbl =
293 enum_stat_prop_stg_QueryInterface,
294 enum_stat_prop_stg_AddRef,
295 enum_stat_prop_stg_Release,
296 enum_stat_prop_stg_Next,
297 enum_stat_prop_stg_Skip,
298 enum_stat_prop_stg_Reset,
299 enum_stat_prop_stg_Clone,
302 static BOOL prop_enum_stat(const void *k, const void *v, void *extra, void *arg)
304 struct enum_stat_prop_stg *stg = arg;
305 PROPID propid = PtrToUlong(k);
306 const PROPVARIANT *prop = v;
307 STATPROPSTG *dest;
309 dest = &stg->stats[stg->count];
311 dest->lpwstrName = NULL;
312 dest->propid = propid;
313 dest->vt = prop->vt;
314 stg->count++;
316 return TRUE;
319 static BOOL prop_enum_stat_count(const void *k, const void *v, void *extra, void *arg)
321 DWORD *count = arg;
323 *count += 1;
325 return TRUE;
328 static HRESULT create_enum_stat_prop_stg(PropertyStorage_impl *storage, IEnumSTATPROPSTG **ret)
330 struct enum_stat_prop_stg *enum_obj;
331 DWORD count;
333 enum_obj = heap_alloc_zero(sizeof(*enum_obj));
334 if (!enum_obj)
335 return E_OUTOFMEMORY;
337 enum_obj->IEnumSTATPROPSTG_iface.lpVtbl = &enum_stat_prop_stg_vtbl;
338 enum_obj->refcount = 1;
339 enum_obj->storage = storage;
340 IPropertyStorage_AddRef(&storage->IPropertyStorage_iface);
342 count = 0;
343 dictionary_enumerate(storage->propid_to_prop, prop_enum_stat_count, &count);
345 if (count)
347 if (!(enum_obj->stats = heap_alloc(sizeof(*enum_obj->stats) * count)))
349 IEnumSTATPROPSTG_Release(&enum_obj->IEnumSTATPROPSTG_iface);
350 return E_OUTOFMEMORY;
353 dictionary_enumerate(storage->propid_to_prop, prop_enum_stat, enum_obj);
356 *ret = &enum_obj->IEnumSTATPROPSTG_iface;
358 return S_OK;
361 /************************************************************************
362 * IPropertyStorage_fnQueryInterface (IPropertyStorage)
364 static HRESULT WINAPI IPropertyStorage_fnQueryInterface(
365 IPropertyStorage *iface,
366 REFIID riid,
367 void** ppvObject)
369 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
371 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
373 if (!ppvObject)
374 return E_INVALIDARG;
376 *ppvObject = 0;
378 if (IsEqualGUID(&IID_IUnknown, riid) ||
379 IsEqualGUID(&IID_IPropertyStorage, riid))
381 IPropertyStorage_AddRef(iface);
382 *ppvObject = iface;
383 return S_OK;
386 return E_NOINTERFACE;
389 /************************************************************************
390 * IPropertyStorage_fnAddRef (IPropertyStorage)
392 static ULONG WINAPI IPropertyStorage_fnAddRef(
393 IPropertyStorage *iface)
395 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
396 return InterlockedIncrement(&This->ref);
399 /************************************************************************
400 * IPropertyStorage_fnRelease (IPropertyStorage)
402 static ULONG WINAPI IPropertyStorage_fnRelease(
403 IPropertyStorage *iface)
405 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
406 ULONG ref;
408 ref = InterlockedDecrement(&This->ref);
409 if (ref == 0)
411 TRACE("Destroying %p\n", This);
412 if (This->dirty)
413 IPropertyStorage_Commit(iface, STGC_DEFAULT);
414 IStream_Release(This->stm);
415 This->cs.DebugInfo->Spare[0] = 0;
416 DeleteCriticalSection(&This->cs);
417 PropertyStorage_DestroyDictionaries(This);
418 HeapFree(GetProcessHeap(), 0, This);
420 return ref;
423 static PROPVARIANT *PropertyStorage_FindProperty(PropertyStorage_impl *This,
424 DWORD propid)
426 PROPVARIANT *ret = NULL;
428 dictionary_find(This->propid_to_prop, UlongToPtr(propid), (void **)&ret);
429 TRACE("returning %p\n", ret);
430 return ret;
433 /* Returns NULL if name is NULL. */
434 static PROPVARIANT *PropertyStorage_FindPropertyByName(
435 PropertyStorage_impl *This, LPCWSTR name)
437 PROPVARIANT *ret = NULL;
438 void *propid;
440 if (!name)
441 return NULL;
442 if (This->codePage == CP_UNICODE)
444 if (dictionary_find(This->name_to_propid, name, &propid))
445 ret = PropertyStorage_FindProperty(This, PtrToUlong(propid));
447 else
449 LPSTR ansiName;
450 HRESULT hr = PropertyStorage_StringCopy((LPCSTR)name, CP_UNICODE,
451 &ansiName, This->codePage);
453 if (SUCCEEDED(hr))
455 if (dictionary_find(This->name_to_propid, ansiName, &propid))
456 ret = PropertyStorage_FindProperty(This, PtrToUlong(propid));
457 CoTaskMemFree(ansiName);
460 TRACE("returning %p\n", ret);
461 return ret;
464 static LPWSTR PropertyStorage_FindPropertyNameById(PropertyStorage_impl *This,
465 DWORD propid)
467 LPWSTR ret = NULL;
469 dictionary_find(This->propid_to_name, UlongToPtr(propid), (void **)&ret);
470 TRACE("returning %p\n", ret);
471 return ret;
474 /************************************************************************
475 * IPropertyStorage_fnReadMultiple (IPropertyStorage)
477 static HRESULT WINAPI IPropertyStorage_fnReadMultiple(
478 IPropertyStorage* iface,
479 ULONG cpspec,
480 const PROPSPEC rgpspec[],
481 PROPVARIANT rgpropvar[])
483 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
484 HRESULT hr = S_OK;
485 ULONG i;
487 TRACE("%p, %lu, %p, %p\n", iface, cpspec, rgpspec, rgpropvar);
489 if (!cpspec)
490 return S_FALSE;
491 if (!rgpspec || !rgpropvar)
492 return E_INVALIDARG;
493 EnterCriticalSection(&This->cs);
494 for (i = 0; i < cpspec; i++)
496 PropVariantInit(&rgpropvar[i]);
497 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
499 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
500 rgpspec[i].lpwstr);
502 if (prop)
503 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop, GetACP(),
504 This->codePage);
506 else
508 switch (rgpspec[i].propid)
510 case PID_CODEPAGE:
511 rgpropvar[i].vt = VT_I2;
512 rgpropvar[i].iVal = This->codePage;
513 break;
514 case PID_LOCALE:
515 rgpropvar[i].vt = VT_I4;
516 rgpropvar[i].lVal = This->locale;
517 break;
518 default:
520 PROPVARIANT *prop = PropertyStorage_FindProperty(This,
521 rgpspec[i].propid);
523 if (prop)
524 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop,
525 GetACP(), This->codePage);
526 else
527 hr = S_FALSE;
532 LeaveCriticalSection(&This->cs);
533 return hr;
536 static HRESULT PropertyStorage_StringCopy(LPCSTR src, UINT srcCP, LPSTR *dst, UINT dstCP)
538 HRESULT hr = S_OK;
539 int len;
541 TRACE("%s, %p, %d, %d\n",
542 srcCP == CP_UNICODE ? debugstr_w((LPCWSTR)src) : debugstr_a(src), dst,
543 dstCP, srcCP);
544 assert(src);
545 assert(dst);
546 *dst = NULL;
547 if (dstCP == srcCP)
549 size_t len;
551 if (dstCP == CP_UNICODE)
552 len = (lstrlenW((LPCWSTR)src) + 1) * sizeof(WCHAR);
553 else
554 len = strlen(src) + 1;
555 *dst = CoTaskMemAlloc(len);
556 if (!*dst)
557 hr = STG_E_INSUFFICIENTMEMORY;
558 else
559 memcpy(*dst, src, len);
561 else
563 if (dstCP == CP_UNICODE)
565 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
566 *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
567 if (!*dst)
568 hr = STG_E_INSUFFICIENTMEMORY;
569 else
570 MultiByteToWideChar(srcCP, 0, src, -1, (LPWSTR)*dst, len);
572 else
574 LPCWSTR wideStr = NULL;
575 LPWSTR wideStr_tmp = NULL;
577 if (srcCP == CP_UNICODE)
578 wideStr = (LPCWSTR)src;
579 else
581 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
582 wideStr_tmp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
583 if (wideStr_tmp)
585 MultiByteToWideChar(srcCP, 0, src, -1, wideStr_tmp, len);
586 wideStr = wideStr_tmp;
588 else
589 hr = STG_E_INSUFFICIENTMEMORY;
591 if (SUCCEEDED(hr))
593 len = WideCharToMultiByte(dstCP, 0, wideStr, -1, NULL, 0,
594 NULL, NULL);
595 *dst = CoTaskMemAlloc(len);
596 if (!*dst)
597 hr = STG_E_INSUFFICIENTMEMORY;
598 else
600 BOOL defCharUsed = FALSE;
602 if (WideCharToMultiByte(dstCP, 0, wideStr, -1, *dst, len,
603 NULL, &defCharUsed) == 0 || defCharUsed)
605 CoTaskMemFree(*dst);
606 *dst = NULL;
607 hr = HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION);
611 HeapFree(GetProcessHeap(), 0, wideStr_tmp);
614 TRACE("returning %#lx (%s)\n", hr,
615 dstCP == CP_UNICODE ? debugstr_w((LPCWSTR)*dst) : debugstr_a(*dst));
616 return hr;
619 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop, const PROPVARIANT *propvar,
620 UINT targetCP, UINT srcCP)
622 HRESULT hr = S_OK;
624 assert(prop);
625 assert(propvar);
627 switch (propvar->vt)
629 case VT_LPSTR:
630 hr = PropertyStorage_StringCopy(propvar->pszVal, srcCP, &prop->pszVal, targetCP);
631 if (SUCCEEDED(hr))
632 prop->vt = VT_LPSTR;
633 break;
634 case VT_BSTR:
635 if ((prop->bstrVal = SysAllocStringLen(propvar->bstrVal, SysStringLen(propvar->bstrVal))))
636 prop->vt = VT_BSTR;
637 else
638 hr = E_OUTOFMEMORY;
639 break;
640 default:
641 hr = PropVariantCopy(prop, propvar);
644 return hr;
647 /* Stores the property with id propid and value propvar into this property
648 * storage. lcid is ignored if propvar's type is not VT_LPSTR. If propvar's
649 * type is VT_LPSTR, converts the string using lcid as the source code page
650 * and This->codePage as the target code page before storing.
651 * As a side effect, may change This->format to 1 if the type of propvar is
652 * a version 1-only property.
654 static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
655 PROPID propid, const PROPVARIANT *propvar, UINT cp)
657 HRESULT hr = S_OK;
658 PROPVARIANT *prop = PropertyStorage_FindProperty(This, propid);
660 assert(propvar);
661 if (propvar->vt & VT_BYREF || propvar->vt & VT_ARRAY)
662 This->format = 1;
663 switch (propvar->vt)
665 case VT_DECIMAL:
666 case VT_I1:
667 case VT_INT:
668 case VT_UINT:
669 case VT_VECTOR|VT_I1:
670 This->format = 1;
672 TRACE("Setting %#lx to type %d\n", propid, propvar->vt);
673 if (prop)
675 PropVariantClear(prop);
676 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage, cp);
678 else
680 prop = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
681 sizeof(PROPVARIANT));
682 if (prop)
684 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage, cp);
685 if (SUCCEEDED(hr))
687 dictionary_insert(This->propid_to_prop, UlongToPtr(propid), prop);
688 if (propid > This->highestProp)
689 This->highestProp = propid;
691 else
692 HeapFree(GetProcessHeap(), 0, prop);
694 else
695 hr = STG_E_INSUFFICIENTMEMORY;
697 return hr;
700 /* Adds the name srcName to the name dictionaries, mapped to property ID id.
701 * srcName is encoded in code page cp, and is converted to This->codePage.
702 * If cp is CP_UNICODE, srcName is actually a unicode string.
703 * As a side effect, may change This->format to 1 if srcName is too long for
704 * a version 0 property storage.
705 * Doesn't validate id.
707 static HRESULT PropertyStorage_StoreNameWithId(PropertyStorage_impl *This,
708 LPCSTR srcName, UINT cp, PROPID id)
710 LPSTR name;
711 HRESULT hr;
713 assert(srcName);
715 hr = PropertyStorage_StringCopy(srcName, cp, &name, This->codePage);
716 if (SUCCEEDED(hr))
718 if (This->codePage == CP_UNICODE)
720 if (lstrlenW((LPWSTR)name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
721 This->format = 1;
723 else
725 if (strlen(name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
726 This->format = 1;
728 TRACE("Adding prop name %s, propid %ld\n",
729 This->codePage == CP_UNICODE ? debugstr_w((LPCWSTR)name) :
730 debugstr_a(name), id);
731 dictionary_insert(This->name_to_propid, name, UlongToPtr(id));
732 dictionary_insert(This->propid_to_name, UlongToPtr(id), name);
734 return hr;
737 /************************************************************************
738 * IPropertyStorage_fnWriteMultiple (IPropertyStorage)
740 static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
741 IPropertyStorage* iface,
742 ULONG cpspec,
743 const PROPSPEC rgpspec[],
744 const PROPVARIANT rgpropvar[],
745 PROPID propidNameFirst)
747 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
748 HRESULT hr = S_OK;
749 ULONG i;
751 TRACE("%p, %lu, %p, %p.\n", iface, cpspec, rgpspec, rgpropvar);
753 if (cpspec && (!rgpspec || !rgpropvar))
754 return E_INVALIDARG;
755 if (!(This->grfMode & STGM_READWRITE))
756 return STG_E_ACCESSDENIED;
757 EnterCriticalSection(&This->cs);
758 This->dirty = TRUE;
759 This->originatorOS = (DWORD)MAKELONG(LOWORD(GetVersion()),
760 PROPSETHDR_OSVER_KIND_WIN32) ;
761 for (i = 0; i < cpspec; i++)
763 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
765 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
766 rgpspec[i].lpwstr);
768 if (prop)
769 PropVariantCopy(prop, &rgpropvar[i]);
770 else
772 /* Note that I don't do the special cases here that I do below,
773 * because naming the special PIDs isn't supported.
775 if (propidNameFirst < PID_FIRST_USABLE ||
776 propidNameFirst >= PID_MIN_READONLY)
777 hr = STG_E_INVALIDPARAMETER;
778 else
780 PROPID nextId = max(propidNameFirst, This->highestProp + 1);
782 hr = PropertyStorage_StoreNameWithId(This,
783 (LPCSTR)rgpspec[i].lpwstr, CP_UNICODE, nextId);
784 if (SUCCEEDED(hr))
785 hr = PropertyStorage_StorePropWithId(This, nextId,
786 &rgpropvar[i], GetACP());
790 else
792 switch (rgpspec[i].propid)
794 case PID_DICTIONARY:
795 /* Can't set the dictionary */
796 hr = STG_E_INVALIDPARAMETER;
797 break;
798 case PID_CODEPAGE:
799 /* Can only set the code page if nothing else has been set */
800 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
801 rgpropvar[i].vt == VT_I2)
803 This->codePage = (USHORT)rgpropvar[i].iVal;
804 if (This->codePage == CP_UNICODE)
805 This->grfFlags &= ~PROPSETFLAG_ANSI;
806 else
807 This->grfFlags |= PROPSETFLAG_ANSI;
809 else
810 hr = STG_E_INVALIDPARAMETER;
811 break;
812 case PID_LOCALE:
813 /* Can only set the locale if nothing else has been set */
814 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
815 rgpropvar[i].vt == VT_I4)
816 This->locale = rgpropvar[i].lVal;
817 else
818 hr = STG_E_INVALIDPARAMETER;
819 break;
820 case PID_ILLEGAL:
821 /* silently ignore like MSDN says */
822 break;
823 default:
824 if (rgpspec[i].propid >= PID_MIN_READONLY)
825 hr = STG_E_INVALIDPARAMETER;
826 else
827 hr = PropertyStorage_StorePropWithId(This,
828 rgpspec[i].propid, &rgpropvar[i], GetACP());
832 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
833 IPropertyStorage_Commit(iface, STGC_DEFAULT);
834 LeaveCriticalSection(&This->cs);
835 return hr;
838 /************************************************************************
839 * IPropertyStorage_fnDeleteMultiple (IPropertyStorage)
841 static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
842 IPropertyStorage* iface,
843 ULONG cpspec,
844 const PROPSPEC rgpspec[])
846 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
847 ULONG i;
848 HRESULT hr;
850 TRACE("%p, %ld, %p.\n", iface, cpspec, rgpspec);
852 if (cpspec && !rgpspec)
853 return E_INVALIDARG;
854 if (!(This->grfMode & STGM_READWRITE))
855 return STG_E_ACCESSDENIED;
856 hr = S_OK;
857 EnterCriticalSection(&This->cs);
858 This->dirty = TRUE;
859 for (i = 0; i < cpspec; i++)
861 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
863 void *propid;
865 if (dictionary_find(This->name_to_propid, rgpspec[i].lpwstr, &propid))
866 dictionary_remove(This->propid_to_prop, propid);
868 else
870 if (rgpspec[i].propid >= PID_FIRST_USABLE &&
871 rgpspec[i].propid < PID_MIN_READONLY)
872 dictionary_remove(This->propid_to_prop, UlongToPtr(rgpspec[i].propid));
873 else
874 hr = STG_E_INVALIDPARAMETER;
877 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
878 IPropertyStorage_Commit(iface, STGC_DEFAULT);
879 LeaveCriticalSection(&This->cs);
880 return hr;
883 /************************************************************************
884 * IPropertyStorage_fnReadPropertyNames (IPropertyStorage)
886 static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames(
887 IPropertyStorage* iface,
888 ULONG cpropid,
889 const PROPID rgpropid[],
890 LPOLESTR rglpwstrName[])
892 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
893 ULONG i;
894 HRESULT hr = S_FALSE;
896 TRACE("%p, %ld, %p, %p.\n", iface, cpropid, rgpropid, rglpwstrName);
898 if (cpropid && (!rgpropid || !rglpwstrName))
899 return E_INVALIDARG;
900 EnterCriticalSection(&This->cs);
901 for (i = 0; i < cpropid && SUCCEEDED(hr); i++)
903 LPWSTR name = PropertyStorage_FindPropertyNameById(This, rgpropid[i]);
905 if (name)
907 size_t len = lstrlenW(name);
909 hr = S_OK;
910 rglpwstrName[i] = CoTaskMemAlloc((len + 1) * sizeof(WCHAR));
911 if (rglpwstrName[i])
912 memcpy(rglpwstrName[i], name, (len + 1) * sizeof(WCHAR));
913 else
914 hr = STG_E_INSUFFICIENTMEMORY;
916 else
917 rglpwstrName[i] = NULL;
919 LeaveCriticalSection(&This->cs);
920 return hr;
923 /************************************************************************
924 * IPropertyStorage_fnWritePropertyNames (IPropertyStorage)
926 static HRESULT WINAPI IPropertyStorage_fnWritePropertyNames(
927 IPropertyStorage* iface,
928 ULONG cpropid,
929 const PROPID rgpropid[],
930 const LPOLESTR rglpwstrName[])
932 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
933 ULONG i;
934 HRESULT hr;
936 TRACE("%p, %lu, %p, %p.\n", iface, cpropid, rgpropid, rglpwstrName);
938 if (cpropid && (!rgpropid || !rglpwstrName))
939 return E_INVALIDARG;
940 if (!(This->grfMode & STGM_READWRITE))
941 return STG_E_ACCESSDENIED;
942 hr = S_OK;
943 EnterCriticalSection(&This->cs);
944 This->dirty = TRUE;
945 for (i = 0; SUCCEEDED(hr) && i < cpropid; i++)
947 if (rgpropid[i] != PID_ILLEGAL)
948 hr = PropertyStorage_StoreNameWithId(This, (LPCSTR)rglpwstrName[i],
949 CP_UNICODE, rgpropid[i]);
951 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
952 IPropertyStorage_Commit(iface, STGC_DEFAULT);
953 LeaveCriticalSection(&This->cs);
954 return hr;
957 /************************************************************************
958 * IPropertyStorage_fnDeletePropertyNames (IPropertyStorage)
960 static HRESULT WINAPI IPropertyStorage_fnDeletePropertyNames(
961 IPropertyStorage* iface,
962 ULONG cpropid,
963 const PROPID rgpropid[])
965 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
966 ULONG i;
967 HRESULT hr;
969 TRACE("%p, %ld, %p.\n", iface, cpropid, rgpropid);
971 if (cpropid && !rgpropid)
972 return E_INVALIDARG;
973 if (!(This->grfMode & STGM_READWRITE))
974 return STG_E_ACCESSDENIED;
975 hr = S_OK;
976 EnterCriticalSection(&This->cs);
977 This->dirty = TRUE;
978 for (i = 0; i < cpropid; i++)
980 LPWSTR name = NULL;
982 if (dictionary_find(This->propid_to_name, UlongToPtr(rgpropid[i]), (void **)&name))
984 dictionary_remove(This->propid_to_name, UlongToPtr(rgpropid[i]));
985 dictionary_remove(This->name_to_propid, name);
988 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
989 IPropertyStorage_Commit(iface, STGC_DEFAULT);
990 LeaveCriticalSection(&This->cs);
991 return hr;
994 /************************************************************************
995 * IPropertyStorage_fnCommit (IPropertyStorage)
997 static HRESULT WINAPI IPropertyStorage_fnCommit(
998 IPropertyStorage* iface,
999 DWORD grfCommitFlags)
1001 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
1002 HRESULT hr;
1004 TRACE("%p, %#lx.\n", iface, grfCommitFlags);
1006 if (!(This->grfMode & STGM_READWRITE))
1007 return STG_E_ACCESSDENIED;
1008 EnterCriticalSection(&This->cs);
1009 if (This->dirty)
1010 hr = PropertyStorage_WriteToStream(This);
1011 else
1012 hr = S_OK;
1013 LeaveCriticalSection(&This->cs);
1014 return hr;
1017 /************************************************************************
1018 * IPropertyStorage_fnRevert (IPropertyStorage)
1020 static HRESULT WINAPI IPropertyStorage_fnRevert(
1021 IPropertyStorage* iface)
1023 HRESULT hr;
1024 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
1026 TRACE("%p\n", iface);
1028 EnterCriticalSection(&This->cs);
1029 if (This->dirty)
1031 PropertyStorage_DestroyDictionaries(This);
1032 hr = PropertyStorage_CreateDictionaries(This);
1033 if (SUCCEEDED(hr))
1034 hr = PropertyStorage_ReadFromStream(This);
1036 else
1037 hr = S_OK;
1038 LeaveCriticalSection(&This->cs);
1039 return hr;
1042 /************************************************************************
1043 * IPropertyStorage_fnEnum (IPropertyStorage)
1045 static HRESULT WINAPI IPropertyStorage_fnEnum(IPropertyStorage *iface, IEnumSTATPROPSTG **ppenum)
1047 PropertyStorage_impl *storage = impl_from_IPropertyStorage(iface);
1049 TRACE("%p, %p.\n", iface, ppenum);
1051 return create_enum_stat_prop_stg(storage, ppenum);
1054 /************************************************************************
1055 * IPropertyStorage_fnSetTimes (IPropertyStorage)
1057 static HRESULT WINAPI IPropertyStorage_fnSetTimes(
1058 IPropertyStorage* iface,
1059 const FILETIME* pctime,
1060 const FILETIME* patime,
1061 const FILETIME* pmtime)
1063 FIXME("\n");
1064 return E_NOTIMPL;
1067 /************************************************************************
1068 * IPropertyStorage_fnSetClass (IPropertyStorage)
1070 static HRESULT WINAPI IPropertyStorage_fnSetClass(
1071 IPropertyStorage* iface,
1072 REFCLSID clsid)
1074 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
1076 TRACE("%p, %s\n", iface, debugstr_guid(clsid));
1078 if (!clsid)
1079 return E_INVALIDARG;
1080 if (!(This->grfMode & STGM_READWRITE))
1081 return STG_E_ACCESSDENIED;
1082 This->clsid = *clsid;
1083 This->dirty = TRUE;
1084 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
1085 IPropertyStorage_Commit(iface, STGC_DEFAULT);
1086 return S_OK;
1089 /************************************************************************
1090 * IPropertyStorage_fnStat (IPropertyStorage)
1092 static HRESULT WINAPI IPropertyStorage_fnStat(
1093 IPropertyStorage* iface,
1094 STATPROPSETSTG* statpsstg)
1096 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
1097 STATSTG stat;
1098 HRESULT hr;
1100 TRACE("%p, %p\n", iface, statpsstg);
1102 if (!statpsstg)
1103 return E_INVALIDARG;
1105 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
1106 if (SUCCEEDED(hr))
1108 statpsstg->fmtid = This->fmtid;
1109 statpsstg->clsid = This->clsid;
1110 statpsstg->grfFlags = This->grfFlags;
1111 statpsstg->mtime = stat.mtime;
1112 statpsstg->ctime = stat.ctime;
1113 statpsstg->atime = stat.atime;
1114 statpsstg->dwOSVersion = This->originatorOS;
1116 return hr;
1119 static int PropertyStorage_PropNameCompare(const void *a, const void *b,
1120 void *extra)
1122 PropertyStorage_impl *This = extra;
1124 if (This->codePage == CP_UNICODE)
1126 TRACE("(%s, %s)\n", debugstr_w(a), debugstr_w(b));
1127 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1128 return wcscmp(a, b);
1129 else
1130 return lstrcmpiW(a, b);
1132 else
1134 TRACE("(%s, %s)\n", debugstr_a(a), debugstr_a(b));
1135 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1136 return lstrcmpA(a, b);
1137 else
1138 return lstrcmpiA(a, b);
1142 static void PropertyStorage_PropNameDestroy(void *k, void *d, void *extra)
1144 CoTaskMemFree(k);
1147 static int PropertyStorage_PropCompare(const void *a, const void *b,
1148 void *extra)
1150 TRACE("%lu, %lu.\n", PtrToUlong(a), PtrToUlong(b));
1151 return PtrToUlong(a) - PtrToUlong(b);
1154 static void PropertyStorage_PropertyDestroy(void *k, void *d, void *extra)
1156 PropVariantClear(d);
1157 HeapFree(GetProcessHeap(), 0, d);
1160 #ifdef WORDS_BIGENDIAN
1161 /* Swaps each character in str to or from little endian; assumes the conversion
1162 * is symmetric, that is, that lendian16toh is equivalent to htole16.
1164 static void PropertyStorage_ByteSwapString(LPWSTR str, size_t len)
1166 DWORD i;
1168 /* Swap characters to host order.
1169 * FIXME: alignment?
1171 for (i = 0; i < len; i++)
1172 str[i] = lendian16toh(str[i]);
1174 #else
1175 #define PropertyStorage_ByteSwapString(s, l)
1176 #endif
1178 static void* WINAPI Allocate_CoTaskMemAlloc(void *this, ULONG size)
1180 return CoTaskMemAlloc(size);
1183 struct read_buffer
1185 BYTE *data;
1186 size_t size;
1189 static HRESULT buffer_test_offset(const struct read_buffer *buffer, size_t offset, size_t len)
1191 return len > buffer->size || offset > buffer->size - len ? STG_E_READFAULT : S_OK;
1194 static HRESULT buffer_read_uint64(const struct read_buffer *buffer, size_t offset, ULARGE_INTEGER *data)
1196 HRESULT hr;
1198 if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*data))))
1199 StorageUtl_ReadULargeInteger(buffer->data, offset, data);
1201 return hr;
1204 static HRESULT buffer_read_dword(const struct read_buffer *buffer, size_t offset, DWORD *data)
1206 HRESULT hr;
1208 if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*data))))
1209 StorageUtl_ReadDWord(buffer->data, offset, data);
1211 return hr;
1214 static HRESULT buffer_read_word(const struct read_buffer *buffer, size_t offset, WORD *data)
1216 HRESULT hr;
1218 if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*data))))
1219 StorageUtl_ReadWord(buffer->data, offset, data);
1221 return hr;
1224 static HRESULT buffer_read_byte(const struct read_buffer *buffer, size_t offset, BYTE *data)
1226 HRESULT hr;
1228 if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*data))))
1229 *data = *(buffer->data + offset);
1231 return hr;
1234 static HRESULT buffer_read_len(const struct read_buffer *buffer, size_t offset, void *dest, size_t len)
1236 HRESULT hr;
1238 if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, len)))
1239 memcpy(dest, buffer->data + offset, len);
1241 return hr;
1244 static HRESULT propertystorage_read_scalar(PROPVARIANT *prop, const struct read_buffer *buffer, size_t offset,
1245 UINT codepage, void* (WINAPI *allocate)(void *this, ULONG size), void *allocate_data)
1247 HRESULT hr;
1249 assert(!(prop->vt & (VT_ARRAY | VT_VECTOR)));
1251 switch (prop->vt)
1253 case VT_EMPTY:
1254 case VT_NULL:
1255 hr = S_OK;
1256 break;
1257 case VT_I1:
1258 hr = buffer_read_byte(buffer, offset, (BYTE *)&prop->cVal);
1259 TRACE("Read char 0x%x ('%c')\n", prop->cVal, prop->cVal);
1260 break;
1261 case VT_UI1:
1262 hr = buffer_read_byte(buffer, offset, &prop->bVal);
1263 TRACE("Read byte 0x%x\n", prop->bVal);
1264 break;
1265 case VT_BOOL:
1266 hr = buffer_read_word(buffer, offset, (WORD *)&prop->boolVal);
1267 TRACE("Read bool %d\n", prop->boolVal);
1268 break;
1269 case VT_I2:
1270 hr = buffer_read_word(buffer, offset, (WORD *)&prop->iVal);
1271 TRACE("Read short %d\n", prop->iVal);
1272 break;
1273 case VT_UI2:
1274 hr = buffer_read_word(buffer, offset, &prop->uiVal);
1275 TRACE("Read ushort %d\n", prop->uiVal);
1276 break;
1277 case VT_INT:
1278 case VT_I4:
1279 hr = buffer_read_dword(buffer, offset, (DWORD *)&prop->lVal);
1280 TRACE("Read long %ld\n", prop->lVal);
1281 break;
1282 case VT_UINT:
1283 case VT_UI4:
1284 hr = buffer_read_dword(buffer, offset, &prop->ulVal);
1285 TRACE("Read ulong %ld\n", prop->ulVal);
1286 break;
1287 case VT_I8:
1288 hr = buffer_read_uint64(buffer, offset, (ULARGE_INTEGER *)&prop->hVal);
1289 TRACE("Read long long %s\n", wine_dbgstr_longlong(prop->hVal.QuadPart));
1290 break;
1291 case VT_UI8:
1292 hr = buffer_read_uint64(buffer, offset, &prop->uhVal);
1293 TRACE("Read ulong long %s\n", wine_dbgstr_longlong(prop->uhVal.QuadPart));
1294 break;
1295 case VT_R8:
1296 hr = buffer_read_len(buffer, offset, &prop->dblVal, sizeof(prop->dblVal));
1297 TRACE("Read double %f\n", prop->dblVal);
1298 break;
1299 case VT_LPSTR:
1301 DWORD count;
1303 if (FAILED(hr = buffer_read_dword(buffer, offset, &count)))
1304 break;
1306 offset += sizeof(DWORD);
1308 if (codepage == CP_UNICODE && count % sizeof(WCHAR))
1310 WARN("Unicode string has odd number of bytes\n");
1311 hr = STG_E_INVALIDHEADER;
1313 else
1315 prop->pszVal = allocate(allocate_data, count);
1316 if (prop->pszVal)
1318 if (FAILED(hr = buffer_read_len(buffer, offset, prop->pszVal, count)))
1319 break;
1321 /* This is stored in the code page specified in codepage.
1322 * Don't convert it, the caller will just store it as-is.
1324 if (codepage == CP_UNICODE)
1326 /* Make sure it's NULL-terminated */
1327 prop->pszVal[count / sizeof(WCHAR) - 1] = '\0';
1328 TRACE("Read string value %s\n",
1329 debugstr_w(prop->pwszVal));
1331 else
1333 /* Make sure it's NULL-terminated */
1334 prop->pszVal[count - 1] = '\0';
1335 TRACE("Read string value %s\n", debugstr_a(prop->pszVal));
1338 else
1339 hr = STG_E_INSUFFICIENTMEMORY;
1341 break;
1343 case VT_BSTR:
1345 DWORD count, wcount;
1347 if (FAILED(hr = buffer_read_dword(buffer, offset, &count)))
1348 break;
1350 offset += sizeof(DWORD);
1352 if (codepage == CP_UNICODE && count % sizeof(WCHAR))
1354 WARN("Unicode string has odd number of bytes\n");
1355 hr = STG_E_INVALIDHEADER;
1357 else
1359 if (codepage == CP_UNICODE)
1360 wcount = count / sizeof(WCHAR);
1361 else
1363 if (FAILED(hr = buffer_test_offset(buffer, offset, count)))
1364 break;
1365 wcount = MultiByteToWideChar(codepage, 0, (LPCSTR)(buffer->data + offset), count, NULL, 0);
1368 prop->bstrVal = SysAllocStringLen(NULL, wcount); /* FIXME: use allocator? */
1370 if (prop->bstrVal)
1372 if (codepage == CP_UNICODE)
1373 hr = buffer_read_len(buffer, offset, prop->bstrVal, count);
1374 else
1375 MultiByteToWideChar(codepage, 0, (LPCSTR)(buffer->data + offset), count, prop->bstrVal, wcount);
1377 prop->bstrVal[wcount - 1] = '\0';
1378 TRACE("Read string value %s\n", debugstr_w(prop->bstrVal));
1380 else
1381 hr = STG_E_INSUFFICIENTMEMORY;
1383 break;
1385 case VT_BLOB:
1387 DWORD count;
1389 if (FAILED(hr = buffer_read_dword(buffer, offset, &count)))
1390 break;
1392 offset += sizeof(DWORD);
1394 prop->blob.cbSize = count;
1395 prop->blob.pBlobData = allocate(allocate_data, count);
1396 if (prop->blob.pBlobData)
1398 hr = buffer_read_len(buffer, offset, prop->blob.pBlobData, count);
1399 TRACE("Read blob value of size %ld\n", count);
1401 else
1402 hr = STG_E_INSUFFICIENTMEMORY;
1403 break;
1405 case VT_LPWSTR:
1407 DWORD count;
1409 if (FAILED(hr = buffer_read_dword(buffer, offset, &count)))
1410 break;
1412 offset += sizeof(DWORD);
1414 prop->pwszVal = allocate(allocate_data, count * sizeof(WCHAR));
1415 if (prop->pwszVal)
1417 if (SUCCEEDED(hr = buffer_read_len(buffer, offset, prop->pwszVal, count * sizeof(WCHAR))))
1419 /* make sure string is NULL-terminated */
1420 prop->pwszVal[count - 1] = '\0';
1421 PropertyStorage_ByteSwapString(prop->pwszVal, count);
1422 TRACE("Read string value %s\n", debugstr_w(prop->pwszVal));
1425 else
1426 hr = STG_E_INSUFFICIENTMEMORY;
1427 break;
1429 case VT_FILETIME:
1430 hr = buffer_read_uint64(buffer, offset, (ULARGE_INTEGER *)&prop->filetime);
1431 break;
1432 case VT_CF:
1434 DWORD len = 0, tag = 0;
1436 if (SUCCEEDED(hr = buffer_read_dword(buffer, offset, &len)))
1437 hr = buffer_read_dword(buffer, offset + sizeof(DWORD), &tag);
1438 if (FAILED(hr))
1439 break;
1441 offset += 2 * sizeof(DWORD);
1443 if (len > 8)
1445 len -= 8;
1446 prop->pclipdata = allocate(allocate_data, sizeof (CLIPDATA));
1447 prop->pclipdata->cbSize = len;
1448 prop->pclipdata->ulClipFmt = tag;
1449 prop->pclipdata->pClipData = allocate(allocate_data, len - sizeof(prop->pclipdata->ulClipFmt));
1450 hr = buffer_read_len(buffer, offset, prop->pclipdata->pClipData, len - sizeof(prop->pclipdata->ulClipFmt));
1452 else
1453 hr = STG_E_INVALIDPARAMETER;
1455 break;
1456 case VT_CLSID:
1457 if (!(prop->puuid = allocate(allocate_data, sizeof (*prop->puuid))))
1458 return STG_E_INSUFFICIENTMEMORY;
1460 if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*prop->puuid))))
1461 StorageUtl_ReadGUID(buffer->data, offset, prop->puuid);
1463 break;
1464 default:
1465 FIXME("unsupported type %d\n", prop->vt);
1466 hr = STG_E_INVALIDPARAMETER;
1469 return hr;
1472 static size_t propertystorage_get_elemsize(const PROPVARIANT *prop)
1474 if (!(prop->vt & VT_VECTOR))
1475 return 0;
1477 switch (prop->vt & ~VT_VECTOR)
1479 case VT_I1: return sizeof(*prop->cac.pElems);
1480 case VT_UI1: return sizeof(*prop->caub.pElems);
1481 case VT_I2: return sizeof(*prop->cai.pElems);
1482 case VT_UI2: return sizeof(*prop->caui.pElems);
1483 case VT_BOOL: return sizeof(*prop->cabool.pElems);
1484 case VT_I4: return sizeof(*prop->cal.pElems);
1485 case VT_UI4: return sizeof(*prop->caul.pElems);
1486 case VT_R4: return sizeof(*prop->caflt.pElems);
1487 case VT_ERROR: return sizeof(*prop->cascode.pElems);
1488 case VT_I8: return sizeof(*prop->cah.pElems);
1489 case VT_UI8: return sizeof(*prop->cauh.pElems);
1490 case VT_R8: return sizeof(*prop->cadbl.pElems);
1491 case VT_CY: return sizeof(*prop->cacy.pElems);
1492 case VT_DATE: return sizeof(*prop->cadate.pElems);
1493 case VT_FILETIME: return sizeof(*prop->cafiletime.pElems);
1494 case VT_CLSID: return sizeof(*prop->cauuid.pElems);
1495 case VT_VARIANT: return sizeof(*prop->capropvar.pElems);
1496 default:
1497 FIXME("Unhandled type %#x.\n", prop->vt);
1498 return 0;
1502 static HRESULT PropertyStorage_ReadProperty(PROPVARIANT *prop, const struct read_buffer *buffer,
1503 size_t offset, UINT codepage, void* (WINAPI *allocate)(void *this, ULONG size), void *allocate_data)
1505 HRESULT hr;
1506 DWORD vt;
1508 assert(prop);
1509 assert(buffer->data);
1511 if (FAILED(hr = buffer_read_dword(buffer, offset, &vt)))
1512 return hr;
1514 offset += sizeof(DWORD);
1515 prop->vt = vt;
1517 if (prop->vt & VT_VECTOR)
1519 DWORD count, i;
1521 switch (prop->vt & VT_VECTOR)
1523 case VT_BSTR:
1524 case VT_VARIANT:
1525 case VT_LPSTR:
1526 case VT_LPWSTR:
1527 case VT_CF:
1528 FIXME("Vector with variable length elements are not supported.\n");
1529 return STG_E_INVALIDPARAMETER;
1530 default:
1534 if (SUCCEEDED(hr = buffer_read_dword(buffer, offset, &count)))
1536 size_t elemsize = propertystorage_get_elemsize(prop);
1537 PROPVARIANT elem;
1539 offset += sizeof(DWORD);
1541 if ((prop->capropvar.pElems = allocate(allocate_data, elemsize * count)))
1543 prop->capropvar.cElems = count;
1544 elem.vt = prop->vt & ~VT_VECTOR;
1546 for (i = 0; i < count; ++i)
1548 if (SUCCEEDED(hr = propertystorage_read_scalar(&elem, buffer, offset + i * elemsize, codepage,
1549 allocate, allocate_data)))
1551 memcpy(&prop->capropvar.pElems[i], &elem.lVal, elemsize);
1555 else
1556 hr = STG_E_INSUFFICIENTMEMORY;
1559 else if (prop->vt & VT_ARRAY)
1561 FIXME("VT_ARRAY properties are not supported.\n");
1562 hr = STG_E_INVALIDPARAMETER;
1564 else
1565 hr = propertystorage_read_scalar(prop, buffer, offset, codepage, allocate, allocate_data);
1567 return hr;
1570 static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
1571 PROPERTYSETHEADER *hdr)
1573 BYTE buf[sizeof(PROPERTYSETHEADER)];
1574 ULONG count = 0;
1575 HRESULT hr;
1577 assert(stm);
1578 assert(hdr);
1579 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1580 if (SUCCEEDED(hr))
1582 if (count != sizeof(buf))
1584 WARN("read only %ld\n", count);
1585 hr = STG_E_INVALIDHEADER;
1587 else
1589 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder),
1590 &hdr->wByteOrder);
1591 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat),
1592 &hdr->wFormat);
1593 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer),
1594 &hdr->dwOSVer);
1595 StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid),
1596 &hdr->clsid);
1597 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved),
1598 &hdr->reserved);
1601 TRACE("returning %#lx\n", hr);
1602 return hr;
1605 static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm,
1606 FORMATIDOFFSET *fmt)
1608 BYTE buf[sizeof(FORMATIDOFFSET)];
1609 ULONG count = 0;
1610 HRESULT hr;
1612 assert(stm);
1613 assert(fmt);
1614 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1615 if (SUCCEEDED(hr))
1617 if (count != sizeof(buf))
1619 WARN("read only %ld\n", count);
1620 hr = STG_E_INVALIDHEADER;
1622 else
1624 StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid),
1625 &fmt->fmtid);
1626 StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset),
1627 &fmt->dwOffset);
1630 TRACE("returning %#lx\n", hr);
1631 return hr;
1634 static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
1635 PROPERTYSECTIONHEADER *hdr)
1637 BYTE buf[sizeof(PROPERTYSECTIONHEADER)];
1638 ULONG count = 0;
1639 HRESULT hr;
1641 assert(stm);
1642 assert(hdr);
1643 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1644 if (SUCCEEDED(hr))
1646 if (count != sizeof(buf))
1648 WARN("read only %ld\n", count);
1649 hr = STG_E_INVALIDHEADER;
1651 else
1653 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1654 cbSection), &hdr->cbSection);
1655 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1656 cProperties), &hdr->cProperties);
1659 TRACE("returning %#lx\n", hr);
1660 return hr;
1663 /* Reads the dictionary from the memory buffer beginning at ptr. Interprets
1664 * the entries according to the values of This->codePage and This->locale.
1666 static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This, const struct read_buffer *buffer,
1667 size_t offset)
1669 DWORD numEntries, i;
1670 HRESULT hr;
1672 assert(This->name_to_propid);
1673 assert(This->propid_to_name);
1675 if (FAILED(hr = buffer_read_dword(buffer, offset, &numEntries)))
1676 return hr;
1678 TRACE("Reading %ld entries:\n", numEntries);
1680 offset += sizeof(DWORD);
1682 for (i = 0; SUCCEEDED(hr) && i < numEntries; i++)
1684 PROPID propid;
1685 DWORD cbEntry;
1686 WCHAR ch = 0;
1688 if (SUCCEEDED(hr = buffer_read_dword(buffer, offset, &propid)))
1689 hr = buffer_read_dword(buffer, offset + sizeof(PROPID), &cbEntry);
1690 if (FAILED(hr))
1691 break;
1693 offset += sizeof(PROPID) + sizeof(DWORD);
1695 if (FAILED(hr = buffer_test_offset(buffer, offset, This->codePage == CP_UNICODE ?
1696 ALIGNED_LENGTH(cbEntry * sizeof(WCHAR), 3) : cbEntry)))
1698 WARN("Broken name length for entry %ld.\n", i);
1699 return hr;
1702 /* Make sure the source string is NULL-terminated */
1703 if (This->codePage != CP_UNICODE)
1704 buffer_read_byte(buffer, offset + cbEntry - 1, (BYTE *)&ch);
1705 else
1706 buffer_read_word(buffer, offset + (cbEntry - 1) * sizeof(WCHAR), &ch);
1708 if (ch)
1710 WARN("Dictionary entry name %ld is not null-terminated.\n", i);
1711 return E_FAIL;
1714 TRACE("Reading entry with ID %#lx, %ld chars, name %s.\n", propid, cbEntry, This->codePage == CP_UNICODE ?
1715 debugstr_wn((WCHAR *)buffer->data, cbEntry) : debugstr_an((char *)buffer->data, cbEntry));
1717 hr = PropertyStorage_StoreNameWithId(This, (char *)buffer->data + offset, This->codePage, propid);
1718 /* Unicode entries are padded to DWORD boundaries */
1719 if (This->codePage == CP_UNICODE)
1720 cbEntry = ALIGNED_LENGTH(cbEntry * sizeof(WCHAR), 3);
1722 offset += cbEntry;
1725 return hr;
1728 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This)
1730 struct read_buffer read_buffer;
1731 PROPERTYSETHEADER hdr;
1732 FORMATIDOFFSET fmtOffset;
1733 PROPERTYSECTIONHEADER sectionHdr;
1734 LARGE_INTEGER seek;
1735 ULONG i;
1736 STATSTG stat;
1737 HRESULT hr;
1738 BYTE *buf = NULL;
1739 ULONG count = 0;
1740 DWORD dictOffset = 0;
1742 This->dirty = FALSE;
1743 This->highestProp = 0;
1744 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
1745 if (FAILED(hr))
1746 goto end;
1747 if (stat.cbSize.HighPart)
1749 WARN("stream too big\n");
1750 /* maximum size varies, but it can't be this big */
1751 hr = STG_E_INVALIDHEADER;
1752 goto end;
1754 if (stat.cbSize.LowPart == 0)
1756 /* empty stream is okay */
1757 hr = S_OK;
1758 goto end;
1760 else if (stat.cbSize.LowPart < sizeof(PROPERTYSETHEADER) +
1761 sizeof(FORMATIDOFFSET))
1763 WARN("stream too small\n");
1764 hr = STG_E_INVALIDHEADER;
1765 goto end;
1767 seek.QuadPart = 0;
1768 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1769 if (FAILED(hr))
1770 goto end;
1771 hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr);
1772 /* I've only seen reserved == 1, but the article says I shouldn't disallow
1773 * higher values.
1775 if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1)
1777 WARN("bad magic in prop set header\n");
1778 hr = STG_E_INVALIDHEADER;
1779 goto end;
1781 if (hdr.wFormat != 0 && hdr.wFormat != 1)
1783 WARN("bad format version %d\n", hdr.wFormat);
1784 hr = STG_E_INVALIDHEADER;
1785 goto end;
1787 This->format = hdr.wFormat;
1788 This->clsid = hdr.clsid;
1789 This->originatorOS = hdr.dwOSVer;
1790 if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC)
1791 WARN("File comes from a Mac, strings will probably be screwed up\n");
1792 hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset);
1793 if (FAILED(hr))
1794 goto end;
1795 if (fmtOffset.dwOffset > stat.cbSize.LowPart)
1797 WARN("invalid offset %ld (stream length is %ld)\n", fmtOffset.dwOffset, stat.cbSize.LowPart);
1798 hr = STG_E_INVALIDHEADER;
1799 goto end;
1801 /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
1802 * follows not one, but two sections. The first contains the standard properties
1803 * for the document summary information, and the second consists of user-defined
1804 * properties. This is the only case in which multiple sections are
1805 * allowed.
1806 * Reading the second stream isn't implemented yet.
1808 hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, &sectionHdr);
1809 if (FAILED(hr))
1810 goto end;
1811 /* The section size includes the section header, so check it */
1812 if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER))
1814 WARN("section header too small, got %ld\n", sectionHdr.cbSection);
1815 hr = STG_E_INVALIDHEADER;
1816 goto end;
1818 buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection -
1819 sizeof(PROPERTYSECTIONHEADER));
1820 if (!buf)
1822 hr = STG_E_INSUFFICIENTMEMORY;
1823 goto end;
1825 read_buffer.data = buf;
1826 read_buffer.size = sectionHdr.cbSection - sizeof(sectionHdr);
1828 hr = IStream_Read(This->stm, read_buffer.data, read_buffer.size, &count);
1829 if (FAILED(hr))
1830 goto end;
1831 TRACE("Reading %ld properties:\n", sectionHdr.cProperties);
1832 for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
1834 PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(read_buffer.data +
1835 i * sizeof(PROPERTYIDOFFSET));
1837 if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) ||
1838 idOffset->dwOffset > sectionHdr.cbSection - sizeof(DWORD))
1839 hr = STG_E_INVALIDPOINTER;
1840 else
1842 if (idOffset->propid >= PID_FIRST_USABLE &&
1843 idOffset->propid < PID_MIN_READONLY && idOffset->propid >
1844 This->highestProp)
1845 This->highestProp = idOffset->propid;
1846 if (idOffset->propid == PID_DICTIONARY)
1848 /* Don't read the dictionary yet, its entries depend on the
1849 * code page. Just store the offset so we know to read it
1850 * later.
1852 dictOffset = idOffset->dwOffset;
1853 TRACE("Dictionary offset is %ld\n", dictOffset);
1855 else
1857 PROPVARIANT prop;
1859 PropVariantInit(&prop);
1860 if (SUCCEEDED(PropertyStorage_ReadProperty(&prop, &read_buffer,
1861 idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER), This->codePage,
1862 Allocate_CoTaskMemAlloc, NULL)))
1864 TRACE("Read property with ID %#lx, type %d\n", idOffset->propid, prop.vt);
1865 switch(idOffset->propid)
1867 case PID_CODEPAGE:
1868 if (prop.vt == VT_I2)
1869 This->codePage = (USHORT)prop.iVal;
1870 break;
1871 case PID_LOCALE:
1872 if (prop.vt == VT_I4)
1873 This->locale = (LCID)prop.lVal;
1874 break;
1875 case PID_BEHAVIOR:
1876 if (prop.vt == VT_I4 && prop.lVal)
1877 This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
1878 /* The format should already be 1, but just in case */
1879 This->format = 1;
1880 break;
1881 default:
1882 hr = PropertyStorage_StorePropWithId(This,
1883 idOffset->propid, &prop, This->codePage);
1886 PropVariantClear(&prop);
1890 if (!This->codePage)
1892 /* default to Unicode unless told not to, as specified on msdn */
1893 if (This->grfFlags & PROPSETFLAG_ANSI)
1894 This->codePage = GetACP();
1895 else
1896 This->codePage = CP_UNICODE;
1898 if (!This->locale)
1899 This->locale = LOCALE_SYSTEM_DEFAULT;
1900 TRACE("Code page is %d, locale is %ld\n", This->codePage, This->locale);
1901 if (dictOffset)
1902 hr = PropertyStorage_ReadDictionary(This, &read_buffer, dictOffset - sizeof(PROPERTYSECTIONHEADER));
1904 end:
1905 HeapFree(GetProcessHeap(), 0, buf);
1906 if (FAILED(hr))
1908 dictionary_destroy(This->name_to_propid);
1909 This->name_to_propid = NULL;
1910 dictionary_destroy(This->propid_to_name);
1911 This->propid_to_name = NULL;
1912 dictionary_destroy(This->propid_to_prop);
1913 This->propid_to_prop = NULL;
1915 return hr;
1918 static void PropertyStorage_MakeHeader(PropertyStorage_impl *This,
1919 PROPERTYSETHEADER *hdr)
1921 assert(hdr);
1922 StorageUtl_WriteWord(&hdr->wByteOrder, 0, PROPSETHDR_BYTEORDER_MAGIC);
1923 StorageUtl_WriteWord(&hdr->wFormat, 0, This->format);
1924 StorageUtl_WriteDWord(&hdr->dwOSVer, 0, This->originatorOS);
1925 StorageUtl_WriteGUID(&hdr->clsid, 0, &This->clsid);
1926 StorageUtl_WriteDWord(&hdr->reserved, 0, 1);
1929 static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl *This,
1930 FORMATIDOFFSET *fmtOffset)
1932 assert(fmtOffset);
1933 StorageUtl_WriteGUID(fmtOffset, 0, &This->fmtid);
1934 StorageUtl_WriteDWord(fmtOffset, offsetof(FORMATIDOFFSET, dwOffset),
1935 sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET));
1938 static void PropertyStorage_MakeSectionHdr(DWORD cbSection, DWORD numProps,
1939 PROPERTYSECTIONHEADER *hdr)
1941 assert(hdr);
1942 StorageUtl_WriteDWord(hdr, 0, cbSection);
1943 StorageUtl_WriteDWord(hdr, offsetof(PROPERTYSECTIONHEADER, cProperties), numProps);
1946 static void PropertyStorage_MakePropertyIdOffset(DWORD propid, DWORD dwOffset,
1947 PROPERTYIDOFFSET *propIdOffset)
1949 assert(propIdOffset);
1950 StorageUtl_WriteDWord(propIdOffset, 0, propid);
1951 StorageUtl_WriteDWord(propIdOffset, offsetof(PROPERTYIDOFFSET, dwOffset), dwOffset);
1954 static inline HRESULT PropertyStorage_WriteWStringToStream(IStream *stm,
1955 LPCWSTR str, DWORD len, DWORD *written)
1957 #ifdef WORDS_BIGENDIAN
1958 WCHAR *leStr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1959 HRESULT hr;
1961 if (!leStr)
1962 return E_OUTOFMEMORY;
1963 memcpy(leStr, str, len * sizeof(WCHAR));
1964 PropertyStorage_ByteSwapString(leStr, len);
1965 hr = IStream_Write(stm, leStr, len, written);
1966 HeapFree(GetProcessHeap(), 0, leStr);
1967 return hr;
1968 #else
1969 return IStream_Write(stm, str, len * sizeof(WCHAR), written);
1970 #endif
1973 struct DictionaryClosure
1975 HRESULT hr;
1976 DWORD bytesWritten;
1979 static BOOL PropertyStorage_DictionaryWriter(const void *key,
1980 const void *value, void *extra, void *closure)
1982 PropertyStorage_impl *This = extra;
1983 struct DictionaryClosure *c = closure;
1984 DWORD propid, keyLen;
1985 ULONG count;
1987 assert(key);
1988 assert(closure);
1989 StorageUtl_WriteDWord(&propid, 0, PtrToUlong(value));
1990 c->hr = IStream_Write(This->stm, &propid, sizeof(propid), &count);
1991 if (FAILED(c->hr))
1992 goto end;
1993 c->bytesWritten += sizeof(DWORD);
1994 if (This->codePage == CP_UNICODE)
1996 DWORD pad = 0, pad_len;
1998 StorageUtl_WriteDWord(&keyLen, 0, lstrlenW((LPCWSTR)key) + 1);
1999 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
2000 if (FAILED(c->hr))
2001 goto end;
2002 c->bytesWritten += sizeof(DWORD);
2003 c->hr = PropertyStorage_WriteWStringToStream(This->stm, key, keyLen,
2004 &count);
2005 if (FAILED(c->hr))
2006 goto end;
2007 keyLen *= sizeof(WCHAR);
2008 c->bytesWritten += keyLen;
2010 /* Align to 4 bytes. */
2011 pad_len = sizeof(DWORD) - keyLen % sizeof(DWORD);
2012 if (pad_len)
2014 c->hr = IStream_Write(This->stm, &pad, pad_len, &count);
2015 if (FAILED(c->hr))
2016 goto end;
2017 c->bytesWritten += pad_len;
2020 else
2022 StorageUtl_WriteDWord(&keyLen, 0, strlen((LPCSTR)key) + 1);
2023 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
2024 if (FAILED(c->hr))
2025 goto end;
2026 c->bytesWritten += sizeof(DWORD);
2027 c->hr = IStream_Write(This->stm, key, keyLen, &count);
2028 if (FAILED(c->hr))
2029 goto end;
2030 c->bytesWritten += keyLen;
2032 end:
2033 return SUCCEEDED(c->hr);
2036 #define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)
2038 /* Writes the dictionary to the stream. Assumes without checking that the
2039 * dictionary isn't empty.
2041 static HRESULT PropertyStorage_WriteDictionaryToStream(
2042 PropertyStorage_impl *This, DWORD *sectionOffset)
2044 HRESULT hr;
2045 LARGE_INTEGER seek;
2046 PROPERTYIDOFFSET propIdOffset;
2047 ULONG count;
2048 DWORD dwTemp;
2049 struct DictionaryClosure closure;
2051 assert(sectionOffset);
2053 /* The dictionary's always the first property written, so seek to its
2054 * spot.
2056 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER);
2057 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2058 if (FAILED(hr))
2059 goto end;
2060 PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY, *sectionOffset,
2061 &propIdOffset);
2062 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
2063 if (FAILED(hr))
2064 goto end;
2066 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
2067 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2068 if (FAILED(hr))
2069 goto end;
2070 StorageUtl_WriteDWord(&dwTemp, 0, dictionary_num_entries(This->name_to_propid));
2071 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2072 if (FAILED(hr))
2073 goto end;
2074 *sectionOffset += sizeof(dwTemp);
2076 closure.hr = S_OK;
2077 closure.bytesWritten = 0;
2078 dictionary_enumerate(This->name_to_propid, PropertyStorage_DictionaryWriter,
2079 &closure);
2080 hr = closure.hr;
2081 if (FAILED(hr))
2082 goto end;
2083 *sectionOffset += closure.bytesWritten;
2084 if (closure.bytesWritten % sizeof(DWORD))
2086 DWORD padding = sizeof(DWORD) - closure.bytesWritten % sizeof(DWORD);
2087 TRACE("adding %ld bytes of padding\n", padding);
2088 *sectionOffset += padding;
2091 end:
2092 return hr;
2095 static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This,
2096 DWORD propNum, DWORD propid, const PROPVARIANT *var, DWORD *sectionOffset)
2098 DWORD len, dwType, dwTemp, bytesWritten;
2099 HRESULT hr;
2100 LARGE_INTEGER seek;
2101 PROPERTYIDOFFSET propIdOffset;
2102 ULARGE_INTEGER ularge;
2103 ULONG count;
2105 assert(var);
2106 assert(sectionOffset);
2108 TRACE("%p, %ld, %#lx, %d, %ld.\n", This, propNum, propid, var->vt,
2109 *sectionOffset);
2111 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) +
2112 propNum * sizeof(PROPERTYIDOFFSET);
2113 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2114 if (FAILED(hr))
2115 goto end;
2116 PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset);
2117 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
2118 if (FAILED(hr))
2119 goto end;
2121 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
2122 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2123 if (FAILED(hr))
2124 goto end;
2125 StorageUtl_WriteDWord(&dwType, 0, var->vt);
2126 hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count);
2127 if (FAILED(hr))
2128 goto end;
2129 *sectionOffset += sizeof(dwType);
2131 switch (var->vt)
2133 case VT_EMPTY:
2134 case VT_NULL:
2135 bytesWritten = 0;
2136 break;
2137 case VT_I1:
2138 case VT_UI1:
2139 hr = IStream_Write(This->stm, &var->cVal, sizeof(var->cVal),
2140 &count);
2141 bytesWritten = count;
2142 break;
2143 case VT_I2:
2144 case VT_UI2:
2146 WORD wTemp;
2148 StorageUtl_WriteWord(&wTemp, 0, var->iVal);
2149 hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count);
2150 bytesWritten = count;
2151 break;
2153 case VT_I4:
2154 case VT_UI4:
2156 StorageUtl_WriteDWord(&dwTemp, 0, var->lVal);
2157 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2158 bytesWritten = count;
2159 break;
2161 case VT_I8:
2162 case VT_UI8:
2164 StorageUtl_WriteULargeInteger(&ularge, 0, &var->uhVal);
2165 hr = IStream_Write(This->stm, &ularge, sizeof(ularge), &bytesWritten);
2166 break;
2168 case VT_LPSTR:
2170 if (This->codePage == CP_UNICODE)
2171 len = (lstrlenW(var->pwszVal) + 1) * sizeof(WCHAR);
2172 else
2173 len = lstrlenA(var->pszVal) + 1;
2174 StorageUtl_WriteDWord(&dwTemp, 0, len);
2175 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2176 if (FAILED(hr))
2177 goto end;
2178 hr = IStream_Write(This->stm, var->pszVal, len, &count);
2179 bytesWritten = count + sizeof(DWORD);
2180 break;
2182 case VT_BSTR:
2184 if (This->codePage == CP_UNICODE)
2186 len = SysStringByteLen(var->bstrVal) + sizeof(WCHAR);
2187 StorageUtl_WriteDWord(&dwTemp, 0, len);
2188 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2189 if (SUCCEEDED(hr))
2190 hr = IStream_Write(This->stm, var->bstrVal, len, &count);
2192 else
2194 char *str;
2196 len = WideCharToMultiByte(This->codePage, 0, var->bstrVal, SysStringLen(var->bstrVal) + 1,
2197 NULL, 0, NULL, NULL);
2199 str = heap_alloc(len);
2200 if (!str)
2202 hr = E_OUTOFMEMORY;
2203 goto end;
2206 WideCharToMultiByte(This->codePage, 0, var->bstrVal, SysStringLen(var->bstrVal),
2207 str, len, NULL, NULL);
2208 StorageUtl_WriteDWord(&dwTemp, 0, len);
2209 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2210 if (SUCCEEDED(hr))
2211 hr = IStream_Write(This->stm, str, len, &count);
2212 heap_free(str);
2215 bytesWritten = count + sizeof(DWORD);
2216 break;
2218 case VT_LPWSTR:
2220 len = lstrlenW(var->pwszVal) + 1;
2222 StorageUtl_WriteDWord(&dwTemp, 0, len);
2223 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2224 if (FAILED(hr))
2225 goto end;
2226 hr = IStream_Write(This->stm, var->pwszVal, len * sizeof(WCHAR),
2227 &count);
2228 bytesWritten = count + sizeof(DWORD);
2229 break;
2231 case VT_FILETIME:
2233 FILETIME temp;
2235 StorageUtl_WriteULargeInteger(&temp, 0, (const ULARGE_INTEGER *)&var->filetime);
2236 hr = IStream_Write(This->stm, &temp, sizeof(temp), &count);
2237 bytesWritten = count;
2238 break;
2240 case VT_CF:
2242 DWORD cf_hdr[2];
2244 len = var->pclipdata->cbSize;
2245 StorageUtl_WriteDWord(&cf_hdr[0], 0, len + 8);
2246 StorageUtl_WriteDWord(&cf_hdr[1], 0, var->pclipdata->ulClipFmt);
2247 hr = IStream_Write(This->stm, cf_hdr, sizeof(cf_hdr), &count);
2248 if (FAILED(hr))
2249 goto end;
2250 hr = IStream_Write(This->stm, var->pclipdata->pClipData,
2251 len - sizeof(var->pclipdata->ulClipFmt), &count);
2252 if (FAILED(hr))
2253 goto end;
2254 bytesWritten = count + sizeof cf_hdr;
2255 break;
2257 case VT_CLSID:
2259 CLSID temp;
2261 StorageUtl_WriteGUID(&temp, 0, var->puuid);
2262 hr = IStream_Write(This->stm, &temp, sizeof(temp), &count);
2263 bytesWritten = count;
2264 break;
2266 case VT_BLOB:
2268 StorageUtl_WriteDWord(&dwTemp, 0, var->blob.cbSize);
2269 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2270 if (FAILED(hr))
2271 goto end;
2272 hr = IStream_Write(This->stm, var->blob.pBlobData, var->blob.cbSize, &count);
2273 bytesWritten = count + sizeof(DWORD);
2274 break;
2276 default:
2277 FIXME("unsupported type: %d\n", var->vt);
2278 return STG_E_INVALIDPARAMETER;
2281 if (SUCCEEDED(hr))
2283 *sectionOffset += bytesWritten;
2284 if (bytesWritten % sizeof(DWORD))
2286 DWORD padding = sizeof(DWORD) - bytesWritten % sizeof(DWORD);
2287 TRACE("adding %ld bytes of padding\n", padding);
2288 *sectionOffset += padding;
2292 end:
2293 return hr;
2296 struct PropertyClosure
2298 HRESULT hr;
2299 DWORD propNum;
2300 DWORD *sectionOffset;
2303 static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value,
2304 void *extra, void *closure)
2306 PropertyStorage_impl *This = extra;
2307 struct PropertyClosure *c = closure;
2309 assert(key);
2310 assert(value);
2311 assert(extra);
2312 assert(closure);
2313 c->hr = PropertyStorage_WritePropertyToStream(This, c->propNum++,
2314 PtrToUlong(key), value, c->sectionOffset);
2315 return SUCCEEDED(c->hr);
2318 static HRESULT PropertyStorage_WritePropertiesToStream(
2319 PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset)
2321 struct PropertyClosure closure;
2323 assert(sectionOffset);
2324 closure.hr = S_OK;
2325 closure.propNum = startingPropNum;
2326 closure.sectionOffset = sectionOffset;
2327 dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter,
2328 &closure);
2329 return closure.hr;
2332 static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This)
2334 HRESULT hr;
2335 ULONG count = 0;
2336 LARGE_INTEGER seek = { {0} };
2337 PROPERTYSETHEADER hdr;
2338 FORMATIDOFFSET fmtOffset;
2340 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2341 if (FAILED(hr))
2342 goto end;
2343 PropertyStorage_MakeHeader(This, &hdr);
2344 hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count);
2345 if (FAILED(hr))
2346 goto end;
2347 if (count != sizeof(hdr))
2349 hr = STG_E_WRITEFAULT;
2350 goto end;
2353 PropertyStorage_MakeFmtIdOffset(This, &fmtOffset);
2354 hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count);
2355 if (FAILED(hr))
2356 goto end;
2357 if (count != sizeof(fmtOffset))
2359 hr = STG_E_WRITEFAULT;
2360 goto end;
2362 hr = S_OK;
2364 end:
2365 return hr;
2368 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This)
2370 PROPERTYSECTIONHEADER sectionHdr;
2371 HRESULT hr;
2372 ULONG count;
2373 LARGE_INTEGER seek;
2374 DWORD numProps, prop, sectionOffset, dwTemp;
2375 PROPVARIANT var;
2377 PropertyStorage_WriteHeadersToStream(This);
2379 /* Count properties. Always at least one property, the code page */
2380 numProps = 1;
2381 if (dictionary_num_entries(This->name_to_propid))
2382 numProps++;
2383 if (This->locale != LOCALE_SYSTEM_DEFAULT)
2384 numProps++;
2385 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2386 numProps++;
2387 numProps += dictionary_num_entries(This->propid_to_prop);
2389 /* Write section header with 0 bytes right now, I'll adjust it after
2390 * writing properties.
2392 PropertyStorage_MakeSectionHdr(0, numProps, &sectionHdr);
2393 seek.QuadPart = SECTIONHEADER_OFFSET;
2394 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2395 if (FAILED(hr))
2396 goto end;
2397 hr = IStream_Write(This->stm, &sectionHdr, sizeof(sectionHdr), &count);
2398 if (FAILED(hr))
2399 goto end;
2401 prop = 0;
2402 sectionOffset = sizeof(PROPERTYSECTIONHEADER) +
2403 numProps * sizeof(PROPERTYIDOFFSET);
2405 if (dictionary_num_entries(This->name_to_propid))
2407 prop++;
2408 hr = PropertyStorage_WriteDictionaryToStream(This, &sectionOffset);
2409 if (FAILED(hr))
2410 goto end;
2413 PropVariantInit(&var);
2415 var.vt = VT_I2;
2416 var.iVal = This->codePage;
2417 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE,
2418 &var, &sectionOffset);
2419 if (FAILED(hr))
2420 goto end;
2422 if (This->locale != LOCALE_SYSTEM_DEFAULT)
2424 var.vt = VT_I4;
2425 var.lVal = This->locale;
2426 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE,
2427 &var, &sectionOffset);
2428 if (FAILED(hr))
2429 goto end;
2432 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2434 var.vt = VT_I4;
2435 var.lVal = 1;
2436 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR,
2437 &var, &sectionOffset);
2438 if (FAILED(hr))
2439 goto end;
2442 hr = PropertyStorage_WritePropertiesToStream(This, prop, &sectionOffset);
2443 if (FAILED(hr))
2444 goto end;
2446 /* Now write the byte count of the section */
2447 seek.QuadPart = SECTIONHEADER_OFFSET;
2448 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2449 if (FAILED(hr))
2450 goto end;
2451 StorageUtl_WriteDWord(&dwTemp, 0, sectionOffset);
2452 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2454 end:
2455 return hr;
2458 /***********************************************************************
2459 * PropertyStorage_Construct
2461 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This)
2463 dictionary_destroy(This->name_to_propid);
2464 This->name_to_propid = NULL;
2465 dictionary_destroy(This->propid_to_name);
2466 This->propid_to_name = NULL;
2467 dictionary_destroy(This->propid_to_prop);
2468 This->propid_to_prop = NULL;
2471 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This)
2473 HRESULT hr = S_OK;
2475 This->name_to_propid = dictionary_create(
2476 PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy,
2477 This);
2478 if (!This->name_to_propid)
2480 hr = STG_E_INSUFFICIENTMEMORY;
2481 goto end;
2483 This->propid_to_name = dictionary_create(PropertyStorage_PropCompare,
2484 NULL, This);
2485 if (!This->propid_to_name)
2487 hr = STG_E_INSUFFICIENTMEMORY;
2488 goto end;
2490 This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
2491 PropertyStorage_PropertyDestroy, This);
2492 if (!This->propid_to_prop)
2494 hr = STG_E_INSUFFICIENTMEMORY;
2495 goto end;
2497 end:
2498 if (FAILED(hr))
2499 PropertyStorage_DestroyDictionaries(This);
2500 return hr;
2503 static HRESULT PropertyStorage_BaseConstruct(IStream *stm,
2504 REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps)
2506 HRESULT hr = S_OK;
2508 assert(pps);
2509 assert(rfmtid);
2510 *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps);
2511 if (!*pps)
2512 return E_OUTOFMEMORY;
2514 (*pps)->IPropertyStorage_iface.lpVtbl = &IPropertyStorage_Vtbl;
2515 (*pps)->ref = 1;
2516 InitializeCriticalSection(&(*pps)->cs);
2517 (*pps)->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PropertyStorage_impl.cs");
2518 (*pps)->stm = stm;
2519 (*pps)->fmtid = *rfmtid;
2520 (*pps)->grfMode = grfMode;
2522 hr = PropertyStorage_CreateDictionaries(*pps);
2523 if (FAILED(hr))
2525 (*pps)->cs.DebugInfo->Spare[0] = 0;
2526 DeleteCriticalSection(&(*pps)->cs);
2527 HeapFree(GetProcessHeap(), 0, *pps);
2528 *pps = NULL;
2530 else IStream_AddRef( stm );
2532 return hr;
2535 static HRESULT PropertyStorage_ConstructFromStream(IStream *stm,
2536 REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps)
2538 PropertyStorage_impl *ps;
2539 HRESULT hr;
2541 assert(pps);
2542 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2543 if (SUCCEEDED(hr))
2545 hr = PropertyStorage_ReadFromStream(ps);
2546 if (SUCCEEDED(hr))
2548 *pps = &ps->IPropertyStorage_iface;
2549 TRACE("PropertyStorage %p constructed\n", ps);
2550 hr = S_OK;
2552 else IPropertyStorage_Release( &ps->IPropertyStorage_iface );
2554 return hr;
2557 static HRESULT PropertyStorage_ConstructEmpty(IStream *stm,
2558 REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
2560 PropertyStorage_impl *ps;
2561 HRESULT hr;
2563 assert(pps);
2564 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2565 if (SUCCEEDED(hr))
2567 ps->format = 0;
2568 ps->grfFlags = grfFlags;
2569 if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2570 ps->format = 1;
2571 /* default to Unicode unless told not to, as specified on msdn */
2572 if (ps->grfFlags & PROPSETFLAG_ANSI)
2573 ps->codePage = GetACP();
2574 else
2575 ps->codePage = CP_UNICODE;
2576 ps->locale = LOCALE_SYSTEM_DEFAULT;
2577 TRACE("Code page is %d, locale is %ld\n", ps->codePage, ps->locale);
2578 *pps = &ps->IPropertyStorage_iface;
2579 TRACE("PropertyStorage %p constructed\n", ps);
2580 hr = S_OK;
2582 return hr;
2586 /***********************************************************************
2587 * Implementation of IPropertySetStorage
2590 struct enum_stat_propset_stg
2592 IEnumSTATPROPSETSTG IEnumSTATPROPSETSTG_iface;
2593 LONG refcount;
2594 STATPROPSETSTG *stats;
2595 size_t current;
2596 size_t count;
2599 static struct enum_stat_propset_stg *impl_from_IEnumSTATPROPSETSTG(IEnumSTATPROPSETSTG *iface)
2601 return CONTAINING_RECORD(iface, struct enum_stat_propset_stg, IEnumSTATPROPSETSTG_iface);
2604 static HRESULT WINAPI enum_stat_propset_stg_QueryInterface(IEnumSTATPROPSETSTG *iface, REFIID riid, void **obj)
2606 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
2608 if (IsEqualIID(riid, &IID_IEnumSTATPROPSETSTG) ||
2609 IsEqualIID(riid, &IID_IUnknown))
2611 *obj = iface;
2612 IEnumSTATPROPSETSTG_AddRef(iface);
2613 return S_OK;
2616 WARN("Unsupported interface %s.\n", debugstr_guid(riid));
2617 return E_NOINTERFACE;
2620 static ULONG WINAPI enum_stat_propset_stg_AddRef(IEnumSTATPROPSETSTG *iface)
2622 struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface);
2623 LONG refcount = InterlockedIncrement(&psenum->refcount);
2625 TRACE("%p, refcount %lu.\n", iface, refcount);
2627 return refcount;
2630 static ULONG WINAPI enum_stat_propset_stg_Release(IEnumSTATPROPSETSTG *iface)
2632 struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface);
2633 LONG refcount = InterlockedDecrement(&psenum->refcount);
2635 TRACE("%p, refcount %lu.\n", iface, refcount);
2637 if (!refcount)
2639 heap_free(psenum->stats);
2640 heap_free(psenum);
2643 return refcount;
2646 static HRESULT WINAPI enum_stat_propset_stg_Next(IEnumSTATPROPSETSTG *iface, ULONG celt,
2647 STATPROPSETSTG *ret, ULONG *fetched)
2649 struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface);
2650 ULONG count = 0;
2652 TRACE("%p, %lu, %p, %p.\n", iface, celt, ret, fetched);
2654 if (psenum->current == ~0u)
2655 psenum->current = 0;
2657 while (count < celt && psenum->current < psenum->count)
2658 ret[count++] = psenum->stats[psenum->current++];
2660 if (fetched)
2661 *fetched = count;
2663 return count < celt ? S_FALSE : S_OK;
2666 static HRESULT WINAPI enum_stat_propset_stg_Skip(IEnumSTATPROPSETSTG *iface, ULONG celt)
2668 FIXME("%p, %lu.\n", iface, celt);
2670 return S_OK;
2673 static HRESULT WINAPI enum_stat_propset_stg_Reset(IEnumSTATPROPSETSTG *iface)
2675 struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface);
2677 TRACE("%p.\n", iface);
2679 psenum->current = ~0u;
2681 return S_OK;
2684 static HRESULT WINAPI enum_stat_propset_stg_Clone(IEnumSTATPROPSETSTG *iface, IEnumSTATPROPSETSTG **ppenum)
2686 FIXME("%p, %p.\n", iface, ppenum);
2688 return E_NOTIMPL;
2691 static const IEnumSTATPROPSETSTGVtbl enum_stat_propset_stg_vtbl =
2693 enum_stat_propset_stg_QueryInterface,
2694 enum_stat_propset_stg_AddRef,
2695 enum_stat_propset_stg_Release,
2696 enum_stat_propset_stg_Next,
2697 enum_stat_propset_stg_Skip,
2698 enum_stat_propset_stg_Reset,
2699 enum_stat_propset_stg_Clone,
2702 static HRESULT create_enum_stat_propset_stg(StorageImpl *storage, IEnumSTATPROPSETSTG **ret)
2704 IStorage *stg = &storage->base.IStorage_iface;
2705 IEnumSTATSTG *penum = NULL;
2706 STATSTG stat;
2707 ULONG count;
2708 HRESULT hr;
2710 struct enum_stat_propset_stg *enum_obj;
2712 enum_obj = heap_alloc_zero(sizeof(*enum_obj));
2713 if (!enum_obj)
2714 return E_OUTOFMEMORY;
2716 enum_obj->IEnumSTATPROPSETSTG_iface.lpVtbl = &enum_stat_propset_stg_vtbl;
2717 enum_obj->refcount = 1;
2719 /* add all the property set elements into a list */
2720 hr = IStorage_EnumElements(stg, 0, NULL, 0, &penum);
2721 if (FAILED(hr))
2722 goto done;
2724 /* Allocate stats array and fill it. */
2725 while ((hr = IEnumSTATSTG_Next(penum, 1, &stat, &count)) == S_OK)
2727 enum_obj->count++;
2728 CoTaskMemFree(stat.pwcsName);
2731 if (FAILED(hr))
2732 goto done;
2734 enum_obj->stats = heap_alloc(enum_obj->count * sizeof(*enum_obj->stats));
2735 if (!enum_obj->stats)
2737 hr = E_OUTOFMEMORY;
2738 goto done;
2740 enum_obj->count = 0;
2742 if (FAILED(hr = IEnumSTATSTG_Reset(penum)))
2743 goto done;
2745 while (IEnumSTATSTG_Next(penum, 1, &stat, &count) == S_OK)
2747 if (!stat.pwcsName)
2748 continue;
2750 if (stat.pwcsName[0] == 5 && stat.type == STGTY_STREAM)
2752 STATPROPSETSTG *ptr = &enum_obj->stats[enum_obj->count++];
2754 PropStgNameToFmtId(stat.pwcsName, &ptr->fmtid);
2756 TRACE("adding %s - %s.\n", debugstr_w(stat.pwcsName), debugstr_guid(&ptr->fmtid));
2758 ptr->mtime = stat.mtime;
2759 ptr->atime = stat.atime;
2760 ptr->ctime = stat.ctime;
2761 ptr->grfFlags = stat.grfMode;
2762 ptr->clsid = stat.clsid;
2764 CoTaskMemFree(stat.pwcsName);
2767 done:
2769 if (penum)
2770 IEnumSTATSTG_Release(penum);
2772 if (SUCCEEDED(hr))
2774 *ret = &enum_obj->IEnumSTATPROPSETSTG_iface;
2776 else
2778 *ret = NULL;
2779 IEnumSTATPROPSETSTG_Release(&enum_obj->IEnumSTATPROPSETSTG_iface);
2782 return hr;
2785 /************************************************************************
2786 * IPropertySetStorage_fnQueryInterface (IUnknown)
2788 * This method forwards to the common QueryInterface implementation
2790 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
2791 IPropertySetStorage *ppstg,
2792 REFIID riid,
2793 void** ppvObject)
2795 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2796 return IStorage_QueryInterface( &This->base.IStorage_iface, riid, ppvObject );
2799 /************************************************************************
2800 * IPropertySetStorage_fnAddRef (IUnknown)
2802 * This method forwards to the common AddRef implementation
2804 static ULONG WINAPI IPropertySetStorage_fnAddRef(
2805 IPropertySetStorage *ppstg)
2807 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2808 return IStorage_AddRef( &This->base.IStorage_iface );
2811 /************************************************************************
2812 * IPropertySetStorage_fnRelease (IUnknown)
2814 * This method forwards to the common Release implementation
2816 static ULONG WINAPI IPropertySetStorage_fnRelease(
2817 IPropertySetStorage *ppstg)
2819 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2820 return IStorage_Release( &This->base.IStorage_iface );
2823 /************************************************************************
2824 * IPropertySetStorage_fnCreate (IPropertySetStorage)
2826 static HRESULT WINAPI IPropertySetStorage_fnCreate(
2827 IPropertySetStorage *ppstg,
2828 REFFMTID rfmtid,
2829 const CLSID* pclsid,
2830 DWORD grfFlags,
2831 DWORD grfMode,
2832 IPropertyStorage** ppprstg)
2834 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2835 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2836 IStream *stm = NULL;
2837 HRESULT r;
2839 TRACE("%p, %s %#lx, %#lx, %p.\n", This, debugstr_guid(rfmtid), grfFlags,
2840 grfMode, ppprstg);
2842 /* be picky */
2843 if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
2845 r = STG_E_INVALIDFLAG;
2846 goto end;
2849 if (!rfmtid)
2851 r = E_INVALIDARG;
2852 goto end;
2855 /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
2856 * storage, not a stream. For now, disallow it.
2858 if (grfFlags & PROPSETFLAG_NONSIMPLE)
2860 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2861 r = STG_E_INVALIDFLAG;
2862 goto end;
2865 r = FmtIdToPropStgName(rfmtid, name);
2866 if (FAILED(r))
2867 goto end;
2869 r = IStorage_CreateStream( &This->base.IStorage_iface, name, grfMode, 0, 0, &stm );
2870 if (FAILED(r))
2871 goto end;
2873 r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg);
2875 IStream_Release( stm );
2877 end:
2878 TRACE("returning %#lx\n", r);
2879 return r;
2882 /************************************************************************
2883 * IPropertySetStorage_fnOpen (IPropertySetStorage)
2885 static HRESULT WINAPI IPropertySetStorage_fnOpen(
2886 IPropertySetStorage *ppstg,
2887 REFFMTID rfmtid,
2888 DWORD grfMode,
2889 IPropertyStorage** ppprstg)
2891 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2892 IStream *stm = NULL;
2893 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2894 HRESULT r;
2896 TRACE("%p, %s, %#lx, %p.\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
2898 /* be picky */
2899 if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
2900 grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
2902 r = STG_E_INVALIDFLAG;
2903 goto end;
2906 if (!rfmtid)
2908 r = E_INVALIDARG;
2909 goto end;
2912 r = FmtIdToPropStgName(rfmtid, name);
2913 if (FAILED(r))
2914 goto end;
2916 r = IStorage_OpenStream( &This->base.IStorage_iface, name, 0, grfMode, 0, &stm );
2917 if (FAILED(r))
2918 goto end;
2920 r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg);
2922 IStream_Release( stm );
2924 end:
2925 TRACE("returning %#lx\n", r);
2926 return r;
2929 /************************************************************************
2930 * IPropertySetStorage_fnDelete (IPropertySetStorage)
2932 static HRESULT WINAPI IPropertySetStorage_fnDelete(
2933 IPropertySetStorage *ppstg,
2934 REFFMTID rfmtid)
2936 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2937 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2938 HRESULT r;
2940 TRACE("%p %s\n", This, debugstr_guid(rfmtid));
2942 if (!rfmtid)
2943 return E_INVALIDARG;
2945 r = FmtIdToPropStgName(rfmtid, name);
2946 if (FAILED(r))
2947 return r;
2949 return IStorage_DestroyElement(&This->base.IStorage_iface, name);
2952 static HRESULT WINAPI IPropertySetStorage_fnEnum(IPropertySetStorage *iface, IEnumSTATPROPSETSTG **enum_obj)
2954 StorageImpl *storage = impl_from_IPropertySetStorage(iface);
2956 TRACE("%p, %p.\n", iface, enum_obj);
2958 if (!enum_obj)
2959 return E_INVALIDARG;
2961 return create_enum_stat_propset_stg(storage, enum_obj);
2964 /***********************************************************************
2965 * vtables
2967 const IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
2969 IPropertySetStorage_fnQueryInterface,
2970 IPropertySetStorage_fnAddRef,
2971 IPropertySetStorage_fnRelease,
2972 IPropertySetStorage_fnCreate,
2973 IPropertySetStorage_fnOpen,
2974 IPropertySetStorage_fnDelete,
2975 IPropertySetStorage_fnEnum
2978 static const IPropertyStorageVtbl IPropertyStorage_Vtbl =
2980 IPropertyStorage_fnQueryInterface,
2981 IPropertyStorage_fnAddRef,
2982 IPropertyStorage_fnRelease,
2983 IPropertyStorage_fnReadMultiple,
2984 IPropertyStorage_fnWriteMultiple,
2985 IPropertyStorage_fnDeleteMultiple,
2986 IPropertyStorage_fnReadPropertyNames,
2987 IPropertyStorage_fnWritePropertyNames,
2988 IPropertyStorage_fnDeletePropertyNames,
2989 IPropertyStorage_fnCommit,
2990 IPropertyStorage_fnRevert,
2991 IPropertyStorage_fnEnum,
2992 IPropertyStorage_fnSetTimes,
2993 IPropertyStorage_fnSetClass,
2994 IPropertyStorage_fnStat,
2997 /***********************************************************************
2998 * Format ID <-> name conversion
3000 static const WCHAR szSummaryInfo[] = L"\5SummaryInformation";
3001 static const WCHAR szDocSummaryInfo[] = L"\5DocumentSummaryInformation";
3003 #define BITS_PER_BYTE 8
3004 #define CHARMASK 0x1f
3005 #define BITS_IN_CHARMASK 5
3006 #define NUM_ALPHA_CHARS 26
3008 /***********************************************************************
3009 * FmtIdToPropStgName [ole32.@]
3010 * Returns the storage name of the format ID rfmtid.
3011 * PARAMS
3012 * rfmtid [I] Format ID for which to return a storage name
3013 * str [O] Storage name associated with rfmtid.
3015 * RETURNS
3016 * E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise.
3018 * NOTES
3019 * str must be at least CCH_MAX_PROPSTG_NAME characters in length.
3021 HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str)
3023 static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
3025 TRACE("%s, %p\n", debugstr_guid(rfmtid), str);
3027 if (!rfmtid) return E_INVALIDARG;
3028 if (!str) return E_INVALIDARG;
3030 if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
3031 lstrcpyW(str, szSummaryInfo);
3032 else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
3033 lstrcpyW(str, szDocSummaryInfo);
3034 else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid))
3035 lstrcpyW(str, szDocSummaryInfo);
3036 else
3038 const BYTE *fmtptr;
3039 WCHAR *pstr = str;
3040 ULONG bitsRemaining = BITS_PER_BYTE;
3042 *pstr++ = 5;
3043 for (fmtptr = (const BYTE *)rfmtid; fmtptr < (const BYTE *)rfmtid + sizeof(FMTID); )
3045 ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
3047 if (bitsRemaining >= BITS_IN_CHARMASK)
3049 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
3050 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
3051 *pstr <= 'z')
3052 *pstr += 'A' - 'a';
3053 pstr++;
3054 bitsRemaining -= BITS_IN_CHARMASK;
3055 if (bitsRemaining == 0)
3057 fmtptr++;
3058 bitsRemaining = BITS_PER_BYTE;
3061 else
3063 if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID))
3064 i |= *fmtptr << bitsRemaining;
3065 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
3066 bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK;
3069 *pstr = 0;
3071 TRACE("returning %s\n", debugstr_w(str));
3072 return S_OK;
3075 /***********************************************************************
3076 * PropStgNameToFmtId [ole32.@]
3077 * Returns the format ID corresponding to the given name.
3078 * PARAMS
3079 * str [I] Storage name to convert to a format ID.
3080 * rfmtid [O] Format ID corresponding to str.
3082 * RETURNS
3083 * E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to
3084 * a format ID, S_OK otherwise.
3086 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid)
3088 HRESULT hr = STG_E_INVALIDNAME;
3090 TRACE("%s, %p\n", debugstr_w(str), rfmtid);
3092 if (!rfmtid) return E_INVALIDARG;
3093 if (!str) return STG_E_INVALIDNAME;
3095 if (!lstrcmpiW(str, szDocSummaryInfo))
3097 *rfmtid = FMTID_DocSummaryInformation;
3098 hr = S_OK;
3100 else if (!lstrcmpiW(str, szSummaryInfo))
3102 *rfmtid = FMTID_SummaryInformation;
3103 hr = S_OK;
3105 else
3107 ULONG bits;
3108 BYTE *fmtptr = (BYTE *)rfmtid - 1;
3109 const WCHAR *pstr = str;
3111 memset(rfmtid, 0, sizeof(*rfmtid));
3112 for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE;
3113 bits += BITS_IN_CHARMASK)
3115 ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored;
3116 WCHAR wc;
3118 if (bitsUsed == 0)
3119 fmtptr++;
3120 wc = *++pstr - 'A';
3121 if (wc > NUM_ALPHA_CHARS)
3123 wc += 'A' - 'a';
3124 if (wc > NUM_ALPHA_CHARS)
3126 wc += 'a' - '0' + NUM_ALPHA_CHARS;
3127 if (wc > CHARMASK)
3129 WARN("invalid character (%d)\n", *pstr);
3130 goto end;
3134 *fmtptr |= wc << bitsUsed;
3135 bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK);
3136 if (bitsStored < BITS_IN_CHARMASK)
3138 wc >>= BITS_PER_BYTE - bitsUsed;
3139 if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE)
3141 if (wc != 0)
3143 WARN("extra bits\n");
3144 goto end;
3146 break;
3148 fmtptr++;
3149 *fmtptr |= (BYTE)wc;
3152 hr = S_OK;
3154 end:
3155 return hr;
3158 #ifdef __i386__ /* thiscall functions are i386-specific */
3160 #define DEFINE_STDCALL_WRAPPER(num,func,args) \
3161 __ASM_STDCALL_FUNC(func, args, \
3162 "popl %eax\n\t" \
3163 "popl %ecx\n\t" \
3164 "pushl %eax\n\t" \
3165 "movl (%ecx), %eax\n\t" \
3166 "jmp *(4*(" #num "))(%eax)" )
3168 DEFINE_STDCALL_WRAPPER(0,Allocate_PMemoryAllocator,8)
3169 extern void* WINAPI Allocate_PMemoryAllocator(void *this, ULONG cbSize);
3171 #else
3173 static void* WINAPI Allocate_PMemoryAllocator(void *this, ULONG cbSize)
3175 void* (WINAPI *fn)(void*,ULONG) = **(void***)this;
3176 return fn(this, cbSize);
3179 #endif
3181 BOOLEAN WINAPI StgConvertPropertyToVariant(const SERIALIZEDPROPERTYVALUE* prop,
3182 USHORT CodePage, PROPVARIANT* pvar, void* pma)
3184 struct read_buffer read_buffer;
3185 HRESULT hr;
3187 read_buffer.data = (BYTE *)prop;
3188 read_buffer.size = ~(size_t)0;
3189 hr = PropertyStorage_ReadProperty(pvar, &read_buffer, 0, CodePage, Allocate_PMemoryAllocator, pma);
3191 if (FAILED(hr))
3193 FIXME("should raise C++ exception on failure\n");
3194 PropVariantInit(pvar);
3197 return FALSE;
3200 SERIALIZEDPROPERTYVALUE* WINAPI StgConvertVariantToProperty(const PROPVARIANT *pvar,
3201 USHORT CodePage, SERIALIZEDPROPERTYVALUE *pprop, ULONG *pcb, PROPID pid,
3202 BOOLEAN fReserved, ULONG *pcIndirect)
3204 FIXME("%p, %d, %p, %p, %ld, %d, %p.\n", pvar, CodePage, pprop, pcb, pid, fReserved, pcIndirect);
3206 return NULL;
3209 HRESULT WINAPI StgCreatePropStg(IUnknown *unk, REFFMTID fmt, const CLSID *clsid,
3210 DWORD flags, DWORD reserved, IPropertyStorage **prop_stg)
3212 IStorage *stg;
3213 IStream *stm;
3214 HRESULT r;
3216 TRACE("%p, %s, %s, %#lx, %ld, %p.\n", unk, debugstr_guid(fmt), debugstr_guid(clsid), flags, reserved, prop_stg);
3218 if (!fmt || reserved)
3220 r = E_INVALIDARG;
3221 goto end;
3224 if (flags & PROPSETFLAG_NONSIMPLE)
3226 r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
3227 if (FAILED(r))
3228 goto end;
3230 /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to create a
3231 * storage, not a stream. For now, disallow it.
3233 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
3234 IStorage_Release(stg);
3235 r = STG_E_INVALIDFLAG;
3237 else
3239 r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
3240 if (FAILED(r))
3241 goto end;
3243 r = PropertyStorage_ConstructEmpty(stm, fmt, flags,
3244 STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
3246 IStream_Release( stm );
3249 end:
3250 TRACE("returning %#lx\n", r);
3251 return r;
3254 HRESULT WINAPI StgOpenPropStg(IUnknown *unk, REFFMTID fmt, DWORD flags,
3255 DWORD reserved, IPropertyStorage **prop_stg)
3257 IStorage *stg;
3258 IStream *stm;
3259 HRESULT r;
3261 TRACE("%p, %s, %#lx, %ld, %p.\n", unk, debugstr_guid(fmt), flags, reserved, prop_stg);
3263 if (!fmt || reserved)
3265 r = E_INVALIDARG;
3266 goto end;
3269 if (flags & PROPSETFLAG_NONSIMPLE)
3271 r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
3272 if (FAILED(r))
3273 goto end;
3275 /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to open a
3276 * storage, not a stream. For now, disallow it.
3278 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
3279 IStorage_Release(stg);
3280 r = STG_E_INVALIDFLAG;
3282 else
3284 r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
3285 if (FAILED(r))
3286 goto end;
3288 r = PropertyStorage_ConstructFromStream(stm, fmt,
3289 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
3291 IStream_Release( stm );
3294 end:
3295 TRACE("returning %#lx\n", r);
3296 return r;