winedbg: Don't dereference possibly NULL thread pointer.
[wine/zf.git] / dlls / ole32 / stg_prop.c
blob7b1cd801cf1f4edf8c772dfe1b7350dee0e523f2
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 %u.\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 %u.\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, %u, %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, %u.\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, %d, %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].u.iVal = This->codePage;
515 break;
516 case PID_LOCALE:
517 rgpropvar[i].vt = VT_I4;
518 rgpropvar[i].u.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 0x%08x (%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->u.pszVal, srcCP, &prop->u.pszVal, targetCP);
633 if (SUCCEEDED(hr))
634 prop->vt = VT_LPSTR;
635 break;
636 case VT_BSTR:
637 if ((prop->u.bstrVal = SysAllocStringLen(propvar->u.bstrVal, SysStringLen(propvar->u.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 0x%08x 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 %d\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, %d, %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 = rgpropvar[i].u.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].u.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, %d, %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, %d, %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, %d, %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, %d, %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, 0x%08x)\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("(%u, %u)\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->u.cVal);
1261 TRACE("Read char 0x%x ('%c')\n", prop->u.cVal, prop->u.cVal);
1262 break;
1263 case VT_UI1:
1264 hr = buffer_read_byte(buffer, offset, &prop->u.bVal);
1265 TRACE("Read byte 0x%x\n", prop->u.bVal);
1266 break;
1267 case VT_BOOL:
1268 hr = buffer_read_word(buffer, offset, (WORD *)&prop->u.boolVal);
1269 TRACE("Read bool %d\n", prop->u.boolVal);
1270 break;
1271 case VT_I2:
1272 hr = buffer_read_word(buffer, offset, (WORD *)&prop->u.iVal);
1273 TRACE("Read short %d\n", prop->u.iVal);
1274 break;
1275 case VT_UI2:
1276 hr = buffer_read_word(buffer, offset, &prop->u.uiVal);
1277 TRACE("Read ushort %d\n", prop->u.uiVal);
1278 break;
1279 case VT_INT:
1280 case VT_I4:
1281 hr = buffer_read_dword(buffer, offset, (DWORD *)&prop->u.lVal);
1282 TRACE("Read long %d\n", prop->u.lVal);
1283 break;
1284 case VT_UINT:
1285 case VT_UI4:
1286 hr = buffer_read_dword(buffer, offset, &prop->u.ulVal);
1287 TRACE("Read ulong %d\n", prop->u.ulVal);
1288 break;
1289 case VT_I8:
1290 hr = buffer_read_uint64(buffer, offset, (ULARGE_INTEGER *)&prop->u.hVal);
1291 TRACE("Read long long %s\n", wine_dbgstr_longlong(prop->u.hVal.QuadPart));
1292 break;
1293 case VT_UI8:
1294 hr = buffer_read_uint64(buffer, offset, &prop->u.uhVal);
1295 TRACE("Read ulong long %s\n", wine_dbgstr_longlong(prop->u.uhVal.QuadPart));
1296 break;
1297 case VT_R8:
1298 hr = buffer_read_len(buffer, offset, &prop->u.dblVal, sizeof(prop->u.dblVal));
1299 TRACE("Read double %f\n", prop->u.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->u.pszVal = allocate(allocate_data, count);
1318 if (prop->u.pszVal)
1320 if (FAILED(hr = buffer_read_len(buffer, offset, prop->u.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->u.pszVal[count / sizeof(WCHAR) - 1] = '\0';
1330 TRACE("Read string value %s\n",
1331 debugstr_w(prop->u.pwszVal));
1333 else
1335 /* Make sure it's NULL-terminated */
1336 prop->u.pszVal[count - 1] = '\0';
1337 TRACE("Read string value %s\n", debugstr_a(prop->u.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->u.bstrVal = SysAllocStringLen(NULL, wcount); /* FIXME: use allocator? */
1372 if (prop->u.bstrVal)
1374 if (codepage == CP_UNICODE)
1375 hr = buffer_read_len(buffer, offset, prop->u.bstrVal, count);
1376 else
1377 MultiByteToWideChar(codepage, 0, (LPCSTR)(buffer->data + offset), count, prop->u.bstrVal, wcount);
1379 prop->u.bstrVal[wcount - 1] = '\0';
1380 TRACE("Read string value %s\n", debugstr_w(prop->u.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->u.blob.cbSize = count;
1397 prop->u.blob.pBlobData = allocate(allocate_data, count);
1398 if (prop->u.blob.pBlobData)
1400 hr = buffer_read_len(buffer, offset, prop->u.blob.pBlobData, count);
1401 TRACE("Read blob value of size %d\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->u.pwszVal = allocate(allocate_data, count * sizeof(WCHAR));
1417 if (prop->u.pwszVal)
1419 if (SUCCEEDED(hr = buffer_read_len(buffer, offset, prop->u.pwszVal, count * sizeof(WCHAR))))
1421 /* make sure string is NULL-terminated */
1422 prop->u.pwszVal[count - 1] = '\0';
1423 PropertyStorage_ByteSwapString(prop->u.pwszVal, count);
1424 TRACE("Read string value %s\n", debugstr_w(prop->u.pwszVal));
1427 else
1428 hr = STG_E_INSUFFICIENTMEMORY;
1429 break;
1431 case VT_FILETIME:
1432 hr = buffer_read_uint64(buffer, offset, (ULARGE_INTEGER *)&prop->u.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->u.pclipdata = allocate(allocate_data, sizeof (CLIPDATA));
1449 prop->u.pclipdata->cbSize = len;
1450 prop->u.pclipdata->ulClipFmt = tag;
1451 prop->u.pclipdata->pClipData = allocate(allocate_data, len - sizeof(prop->u.pclipdata->ulClipFmt));
1452 hr = buffer_read_len(buffer, offset, prop->u.pclipdata->pClipData, len - sizeof(prop->u.pclipdata->ulClipFmt));
1454 else
1455 hr = STG_E_INVALIDPARAMETER;
1457 break;
1458 case VT_CLSID:
1459 if (!(prop->u.puuid = allocate(allocate_data, sizeof (*prop->u.puuid))))
1460 return STG_E_INSUFFICIENTMEMORY;
1462 if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*prop->u.puuid))))
1463 StorageUtl_ReadGUID(buffer->data, offset, prop->u.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->u.cac.pElems);
1482 case VT_UI1: return sizeof(*prop->u.caub.pElems);
1483 case VT_I2: return sizeof(*prop->u.cai.pElems);
1484 case VT_UI2: return sizeof(*prop->u.caui.pElems);
1485 case VT_BOOL: return sizeof(*prop->u.cabool.pElems);
1486 case VT_I4: return sizeof(*prop->u.cal.pElems);
1487 case VT_UI4: return sizeof(*prop->u.caul.pElems);
1488 case VT_R4: return sizeof(*prop->u.caflt.pElems);
1489 case VT_ERROR: return sizeof(*prop->u.cascode.pElems);
1490 case VT_I8: return sizeof(*prop->u.cah.pElems);
1491 case VT_UI8: return sizeof(*prop->u.cauh.pElems);
1492 case VT_R8: return sizeof(*prop->u.cadbl.pElems);
1493 case VT_CY: return sizeof(*prop->u.cacy.pElems);
1494 case VT_DATE: return sizeof(*prop->u.cadate.pElems);
1495 case VT_FILETIME: return sizeof(*prop->u.cafiletime.pElems);
1496 case VT_CLSID: return sizeof(*prop->u.cauuid.pElems);
1497 case VT_VARIANT: return sizeof(*prop->u.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->u.capropvar.pElems = allocate(allocate_data, elemsize * count)))
1545 prop->u.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->u.capropvar.pElems[i], &elem.u.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 %d\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 0x%08x\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 %d\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 0x%08x\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 %d\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 0x%08x\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 %d 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 %d.\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 %d is not null-terminated.\n", i);
1713 return E_FAIL;
1716 TRACE("Reading entry with ID %#x, %d 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 %d (stream length is %d)\n", fmtOffset.dwOffset,
1800 stat.cbSize.u.LowPart);
1801 hr = STG_E_INVALIDHEADER;
1802 goto end;
1804 /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
1805 * follows not one, but two sections. The first contains the standard properties
1806 * for the document summary information, and the second consists of user-defined
1807 * properties. This is the only case in which multiple sections are
1808 * allowed.
1809 * Reading the second stream isn't implemented yet.
1811 hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, &sectionHdr);
1812 if (FAILED(hr))
1813 goto end;
1814 /* The section size includes the section header, so check it */
1815 if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER))
1817 WARN("section header too small, got %d\n", sectionHdr.cbSection);
1818 hr = STG_E_INVALIDHEADER;
1819 goto end;
1821 buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection -
1822 sizeof(PROPERTYSECTIONHEADER));
1823 if (!buf)
1825 hr = STG_E_INSUFFICIENTMEMORY;
1826 goto end;
1828 read_buffer.data = buf;
1829 read_buffer.size = sectionHdr.cbSection - sizeof(sectionHdr);
1831 hr = IStream_Read(This->stm, read_buffer.data, read_buffer.size, &count);
1832 if (FAILED(hr))
1833 goto end;
1834 TRACE("Reading %d properties:\n", sectionHdr.cProperties);
1835 for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
1837 PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(read_buffer.data +
1838 i * sizeof(PROPERTYIDOFFSET));
1840 if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) ||
1841 idOffset->dwOffset > sectionHdr.cbSection - sizeof(DWORD))
1842 hr = STG_E_INVALIDPOINTER;
1843 else
1845 if (idOffset->propid >= PID_FIRST_USABLE &&
1846 idOffset->propid < PID_MIN_READONLY && idOffset->propid >
1847 This->highestProp)
1848 This->highestProp = idOffset->propid;
1849 if (idOffset->propid == PID_DICTIONARY)
1851 /* Don't read the dictionary yet, its entries depend on the
1852 * code page. Just store the offset so we know to read it
1853 * later.
1855 dictOffset = idOffset->dwOffset;
1856 TRACE("Dictionary offset is %d\n", dictOffset);
1858 else
1860 PROPVARIANT prop;
1862 PropVariantInit(&prop);
1863 if (SUCCEEDED(PropertyStorage_ReadProperty(&prop, &read_buffer,
1864 idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER), This->codePage,
1865 Allocate_CoTaskMemAlloc, NULL)))
1867 TRACE("Read property with ID 0x%08x, type %d\n",
1868 idOffset->propid, prop.vt);
1869 switch(idOffset->propid)
1871 case PID_CODEPAGE:
1872 if (prop.vt == VT_I2)
1873 This->codePage = (UINT)prop.u.iVal;
1874 break;
1875 case PID_LOCALE:
1876 if (prop.vt == VT_I4)
1877 This->locale = (LCID)prop.u.lVal;
1878 break;
1879 case PID_BEHAVIOR:
1880 if (prop.vt == VT_I4 && prop.u.lVal)
1881 This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
1882 /* The format should already be 1, but just in case */
1883 This->format = 1;
1884 break;
1885 default:
1886 hr = PropertyStorage_StorePropWithId(This,
1887 idOffset->propid, &prop, This->codePage);
1890 PropVariantClear(&prop);
1894 if (!This->codePage)
1896 /* default to Unicode unless told not to, as specified on msdn */
1897 if (This->grfFlags & PROPSETFLAG_ANSI)
1898 This->codePage = GetACP();
1899 else
1900 This->codePage = CP_UNICODE;
1902 if (!This->locale)
1903 This->locale = LOCALE_SYSTEM_DEFAULT;
1904 TRACE("Code page is %d, locale is %d\n", This->codePage, This->locale);
1905 if (dictOffset)
1906 hr = PropertyStorage_ReadDictionary(This, &read_buffer, dictOffset - sizeof(PROPERTYSECTIONHEADER));
1908 end:
1909 HeapFree(GetProcessHeap(), 0, buf);
1910 if (FAILED(hr))
1912 dictionary_destroy(This->name_to_propid);
1913 This->name_to_propid = NULL;
1914 dictionary_destroy(This->propid_to_name);
1915 This->propid_to_name = NULL;
1916 dictionary_destroy(This->propid_to_prop);
1917 This->propid_to_prop = NULL;
1919 return hr;
1922 static void PropertyStorage_MakeHeader(PropertyStorage_impl *This,
1923 PROPERTYSETHEADER *hdr)
1925 assert(hdr);
1926 StorageUtl_WriteWord(&hdr->wByteOrder, 0, PROPSETHDR_BYTEORDER_MAGIC);
1927 StorageUtl_WriteWord(&hdr->wFormat, 0, This->format);
1928 StorageUtl_WriteDWord(&hdr->dwOSVer, 0, This->originatorOS);
1929 StorageUtl_WriteGUID(&hdr->clsid, 0, &This->clsid);
1930 StorageUtl_WriteDWord(&hdr->reserved, 0, 1);
1933 static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl *This,
1934 FORMATIDOFFSET *fmtOffset)
1936 assert(fmtOffset);
1937 StorageUtl_WriteGUID(fmtOffset, 0, &This->fmtid);
1938 StorageUtl_WriteDWord(fmtOffset, offsetof(FORMATIDOFFSET, dwOffset),
1939 sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET));
1942 static void PropertyStorage_MakeSectionHdr(DWORD cbSection, DWORD numProps,
1943 PROPERTYSECTIONHEADER *hdr)
1945 assert(hdr);
1946 StorageUtl_WriteDWord(hdr, 0, cbSection);
1947 StorageUtl_WriteDWord(hdr, offsetof(PROPERTYSECTIONHEADER, cProperties), numProps);
1950 static void PropertyStorage_MakePropertyIdOffset(DWORD propid, DWORD dwOffset,
1951 PROPERTYIDOFFSET *propIdOffset)
1953 assert(propIdOffset);
1954 StorageUtl_WriteDWord(propIdOffset, 0, propid);
1955 StorageUtl_WriteDWord(propIdOffset, offsetof(PROPERTYIDOFFSET, dwOffset), dwOffset);
1958 static inline HRESULT PropertyStorage_WriteWStringToStream(IStream *stm,
1959 LPCWSTR str, DWORD len, DWORD *written)
1961 #ifdef WORDS_BIGENDIAN
1962 WCHAR *leStr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1963 HRESULT hr;
1965 if (!leStr)
1966 return E_OUTOFMEMORY;
1967 memcpy(leStr, str, len * sizeof(WCHAR));
1968 PropertyStorage_ByteSwapString(leStr, len);
1969 hr = IStream_Write(stm, leStr, len, written);
1970 HeapFree(GetProcessHeap(), 0, leStr);
1971 return hr;
1972 #else
1973 return IStream_Write(stm, str, len * sizeof(WCHAR), written);
1974 #endif
1977 struct DictionaryClosure
1979 HRESULT hr;
1980 DWORD bytesWritten;
1983 static BOOL PropertyStorage_DictionaryWriter(const void *key,
1984 const void *value, void *extra, void *closure)
1986 PropertyStorage_impl *This = extra;
1987 struct DictionaryClosure *c = closure;
1988 DWORD propid, keyLen;
1989 ULONG count;
1991 assert(key);
1992 assert(closure);
1993 StorageUtl_WriteDWord(&propid, 0, PtrToUlong(value));
1994 c->hr = IStream_Write(This->stm, &propid, sizeof(propid), &count);
1995 if (FAILED(c->hr))
1996 goto end;
1997 c->bytesWritten += sizeof(DWORD);
1998 if (This->codePage == CP_UNICODE)
2000 DWORD pad = 0, pad_len;
2002 StorageUtl_WriteDWord(&keyLen, 0, lstrlenW((LPCWSTR)key) + 1);
2003 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
2004 if (FAILED(c->hr))
2005 goto end;
2006 c->bytesWritten += sizeof(DWORD);
2007 c->hr = PropertyStorage_WriteWStringToStream(This->stm, key, keyLen,
2008 &count);
2009 if (FAILED(c->hr))
2010 goto end;
2011 keyLen *= sizeof(WCHAR);
2012 c->bytesWritten += keyLen;
2014 /* Align to 4 bytes. */
2015 pad_len = sizeof(DWORD) - keyLen % sizeof(DWORD);
2016 if (pad_len)
2018 c->hr = IStream_Write(This->stm, &pad, pad_len, &count);
2019 if (FAILED(c->hr))
2020 goto end;
2021 c->bytesWritten += pad_len;
2024 else
2026 StorageUtl_WriteDWord(&keyLen, 0, strlen((LPCSTR)key) + 1);
2027 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
2028 if (FAILED(c->hr))
2029 goto end;
2030 c->bytesWritten += sizeof(DWORD);
2031 c->hr = IStream_Write(This->stm, key, keyLen, &count);
2032 if (FAILED(c->hr))
2033 goto end;
2034 c->bytesWritten += keyLen;
2036 end:
2037 return SUCCEEDED(c->hr);
2040 #define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)
2042 /* Writes the dictionary to the stream. Assumes without checking that the
2043 * dictionary isn't empty.
2045 static HRESULT PropertyStorage_WriteDictionaryToStream(
2046 PropertyStorage_impl *This, DWORD *sectionOffset)
2048 HRESULT hr;
2049 LARGE_INTEGER seek;
2050 PROPERTYIDOFFSET propIdOffset;
2051 ULONG count;
2052 DWORD dwTemp;
2053 struct DictionaryClosure closure;
2055 assert(sectionOffset);
2057 /* The dictionary's always the first property written, so seek to its
2058 * spot.
2060 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER);
2061 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2062 if (FAILED(hr))
2063 goto end;
2064 PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY, *sectionOffset,
2065 &propIdOffset);
2066 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
2067 if (FAILED(hr))
2068 goto end;
2070 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
2071 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2072 if (FAILED(hr))
2073 goto end;
2074 StorageUtl_WriteDWord(&dwTemp, 0, dictionary_num_entries(This->name_to_propid));
2075 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2076 if (FAILED(hr))
2077 goto end;
2078 *sectionOffset += sizeof(dwTemp);
2080 closure.hr = S_OK;
2081 closure.bytesWritten = 0;
2082 dictionary_enumerate(This->name_to_propid, PropertyStorage_DictionaryWriter,
2083 &closure);
2084 hr = closure.hr;
2085 if (FAILED(hr))
2086 goto end;
2087 *sectionOffset += closure.bytesWritten;
2088 if (closure.bytesWritten % sizeof(DWORD))
2090 DWORD padding = sizeof(DWORD) - closure.bytesWritten % sizeof(DWORD);
2091 TRACE("adding %d bytes of padding\n", padding);
2092 *sectionOffset += padding;
2095 end:
2096 return hr;
2099 static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This,
2100 DWORD propNum, DWORD propid, const PROPVARIANT *var, DWORD *sectionOffset)
2102 DWORD len, dwType, dwTemp, bytesWritten;
2103 HRESULT hr;
2104 LARGE_INTEGER seek;
2105 PROPERTYIDOFFSET propIdOffset;
2106 ULARGE_INTEGER ularge;
2107 ULONG count;
2109 assert(var);
2110 assert(sectionOffset);
2112 TRACE("%p, %d, 0x%08x, (%d), (%d)\n", This, propNum, propid, var->vt,
2113 *sectionOffset);
2115 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) +
2116 propNum * sizeof(PROPERTYIDOFFSET);
2117 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2118 if (FAILED(hr))
2119 goto end;
2120 PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset);
2121 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
2122 if (FAILED(hr))
2123 goto end;
2125 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
2126 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2127 if (FAILED(hr))
2128 goto end;
2129 StorageUtl_WriteDWord(&dwType, 0, var->vt);
2130 hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count);
2131 if (FAILED(hr))
2132 goto end;
2133 *sectionOffset += sizeof(dwType);
2135 switch (var->vt)
2137 case VT_EMPTY:
2138 case VT_NULL:
2139 bytesWritten = 0;
2140 break;
2141 case VT_I1:
2142 case VT_UI1:
2143 hr = IStream_Write(This->stm, &var->u.cVal, sizeof(var->u.cVal),
2144 &count);
2145 bytesWritten = count;
2146 break;
2147 case VT_I2:
2148 case VT_UI2:
2150 WORD wTemp;
2152 StorageUtl_WriteWord(&wTemp, 0, var->u.iVal);
2153 hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count);
2154 bytesWritten = count;
2155 break;
2157 case VT_I4:
2158 case VT_UI4:
2160 StorageUtl_WriteDWord(&dwTemp, 0, var->u.lVal);
2161 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2162 bytesWritten = count;
2163 break;
2165 case VT_I8:
2166 case VT_UI8:
2168 StorageUtl_WriteULargeInteger(&ularge, 0, &var->u.uhVal);
2169 hr = IStream_Write(This->stm, &ularge, sizeof(ularge), &bytesWritten);
2170 break;
2172 case VT_LPSTR:
2174 if (This->codePage == CP_UNICODE)
2175 len = (lstrlenW(var->u.pwszVal) + 1) * sizeof(WCHAR);
2176 else
2177 len = lstrlenA(var->u.pszVal) + 1;
2178 StorageUtl_WriteDWord(&dwTemp, 0, len);
2179 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2180 if (FAILED(hr))
2181 goto end;
2182 hr = IStream_Write(This->stm, var->u.pszVal, len, &count);
2183 bytesWritten = count + sizeof(DWORD);
2184 break;
2186 case VT_BSTR:
2188 if (This->codePage == CP_UNICODE)
2190 len = SysStringByteLen(var->u.bstrVal) + sizeof(WCHAR);
2191 StorageUtl_WriteDWord(&dwTemp, 0, len);
2192 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2193 if (SUCCEEDED(hr))
2194 hr = IStream_Write(This->stm, var->u.bstrVal, len, &count);
2196 else
2198 char *str;
2200 len = WideCharToMultiByte(This->codePage, 0, var->u.bstrVal, SysStringLen(var->u.bstrVal) + 1,
2201 NULL, 0, NULL, NULL);
2203 str = heap_alloc(len);
2204 if (!str)
2206 hr = E_OUTOFMEMORY;
2207 goto end;
2210 WideCharToMultiByte(This->codePage, 0, var->u.bstrVal, SysStringLen(var->u.bstrVal),
2211 str, len, NULL, NULL);
2212 StorageUtl_WriteDWord(&dwTemp, 0, len);
2213 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2214 if (SUCCEEDED(hr))
2215 hr = IStream_Write(This->stm, str, len, &count);
2216 heap_free(str);
2219 bytesWritten = count + sizeof(DWORD);
2220 break;
2222 case VT_LPWSTR:
2224 len = lstrlenW(var->u.pwszVal) + 1;
2226 StorageUtl_WriteDWord(&dwTemp, 0, len);
2227 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2228 if (FAILED(hr))
2229 goto end;
2230 hr = IStream_Write(This->stm, var->u.pwszVal, len * sizeof(WCHAR),
2231 &count);
2232 bytesWritten = count + sizeof(DWORD);
2233 break;
2235 case VT_FILETIME:
2237 FILETIME temp;
2239 StorageUtl_WriteULargeInteger(&temp, 0, (const ULARGE_INTEGER *)&var->u.filetime);
2240 hr = IStream_Write(This->stm, &temp, sizeof(temp), &count);
2241 bytesWritten = count;
2242 break;
2244 case VT_CF:
2246 DWORD cf_hdr[2];
2248 len = var->u.pclipdata->cbSize;
2249 StorageUtl_WriteDWord(&cf_hdr[0], 0, len + 8);
2250 StorageUtl_WriteDWord(&cf_hdr[1], 0, var->u.pclipdata->ulClipFmt);
2251 hr = IStream_Write(This->stm, cf_hdr, sizeof(cf_hdr), &count);
2252 if (FAILED(hr))
2253 goto end;
2254 hr = IStream_Write(This->stm, var->u.pclipdata->pClipData,
2255 len - sizeof(var->u.pclipdata->ulClipFmt), &count);
2256 if (FAILED(hr))
2257 goto end;
2258 bytesWritten = count + sizeof cf_hdr;
2259 break;
2261 case VT_CLSID:
2263 CLSID temp;
2265 StorageUtl_WriteGUID(&temp, 0, var->u.puuid);
2266 hr = IStream_Write(This->stm, &temp, sizeof(temp), &count);
2267 bytesWritten = count;
2268 break;
2270 default:
2271 FIXME("unsupported type: %d\n", var->vt);
2272 return STG_E_INVALIDPARAMETER;
2275 if (SUCCEEDED(hr))
2277 *sectionOffset += bytesWritten;
2278 if (bytesWritten % sizeof(DWORD))
2280 DWORD padding = sizeof(DWORD) - bytesWritten % sizeof(DWORD);
2281 TRACE("adding %d bytes of padding\n", padding);
2282 *sectionOffset += padding;
2286 end:
2287 return hr;
2290 struct PropertyClosure
2292 HRESULT hr;
2293 DWORD propNum;
2294 DWORD *sectionOffset;
2297 static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value,
2298 void *extra, void *closure)
2300 PropertyStorage_impl *This = extra;
2301 struct PropertyClosure *c = closure;
2303 assert(key);
2304 assert(value);
2305 assert(extra);
2306 assert(closure);
2307 c->hr = PropertyStorage_WritePropertyToStream(This, c->propNum++,
2308 PtrToUlong(key), value, c->sectionOffset);
2309 return SUCCEEDED(c->hr);
2312 static HRESULT PropertyStorage_WritePropertiesToStream(
2313 PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset)
2315 struct PropertyClosure closure;
2317 assert(sectionOffset);
2318 closure.hr = S_OK;
2319 closure.propNum = startingPropNum;
2320 closure.sectionOffset = sectionOffset;
2321 dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter,
2322 &closure);
2323 return closure.hr;
2326 static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This)
2328 HRESULT hr;
2329 ULONG count = 0;
2330 LARGE_INTEGER seek = { {0} };
2331 PROPERTYSETHEADER hdr;
2332 FORMATIDOFFSET fmtOffset;
2334 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2335 if (FAILED(hr))
2336 goto end;
2337 PropertyStorage_MakeHeader(This, &hdr);
2338 hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count);
2339 if (FAILED(hr))
2340 goto end;
2341 if (count != sizeof(hdr))
2343 hr = STG_E_WRITEFAULT;
2344 goto end;
2347 PropertyStorage_MakeFmtIdOffset(This, &fmtOffset);
2348 hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count);
2349 if (FAILED(hr))
2350 goto end;
2351 if (count != sizeof(fmtOffset))
2353 hr = STG_E_WRITEFAULT;
2354 goto end;
2356 hr = S_OK;
2358 end:
2359 return hr;
2362 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This)
2364 PROPERTYSECTIONHEADER sectionHdr;
2365 HRESULT hr;
2366 ULONG count;
2367 LARGE_INTEGER seek;
2368 DWORD numProps, prop, sectionOffset, dwTemp;
2369 PROPVARIANT var;
2371 PropertyStorage_WriteHeadersToStream(This);
2373 /* Count properties. Always at least one property, the code page */
2374 numProps = 1;
2375 if (dictionary_num_entries(This->name_to_propid))
2376 numProps++;
2377 if (This->locale != LOCALE_SYSTEM_DEFAULT)
2378 numProps++;
2379 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2380 numProps++;
2381 numProps += dictionary_num_entries(This->propid_to_prop);
2383 /* Write section header with 0 bytes right now, I'll adjust it after
2384 * writing properties.
2386 PropertyStorage_MakeSectionHdr(0, numProps, &sectionHdr);
2387 seek.QuadPart = SECTIONHEADER_OFFSET;
2388 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2389 if (FAILED(hr))
2390 goto end;
2391 hr = IStream_Write(This->stm, &sectionHdr, sizeof(sectionHdr), &count);
2392 if (FAILED(hr))
2393 goto end;
2395 prop = 0;
2396 sectionOffset = sizeof(PROPERTYSECTIONHEADER) +
2397 numProps * sizeof(PROPERTYIDOFFSET);
2399 if (dictionary_num_entries(This->name_to_propid))
2401 prop++;
2402 hr = PropertyStorage_WriteDictionaryToStream(This, &sectionOffset);
2403 if (FAILED(hr))
2404 goto end;
2407 PropVariantInit(&var);
2409 var.vt = VT_I2;
2410 var.u.iVal = This->codePage;
2411 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE,
2412 &var, &sectionOffset);
2413 if (FAILED(hr))
2414 goto end;
2416 if (This->locale != LOCALE_SYSTEM_DEFAULT)
2418 var.vt = VT_I4;
2419 var.u.lVal = This->locale;
2420 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE,
2421 &var, &sectionOffset);
2422 if (FAILED(hr))
2423 goto end;
2426 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2428 var.vt = VT_I4;
2429 var.u.lVal = 1;
2430 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR,
2431 &var, &sectionOffset);
2432 if (FAILED(hr))
2433 goto end;
2436 hr = PropertyStorage_WritePropertiesToStream(This, prop, &sectionOffset);
2437 if (FAILED(hr))
2438 goto end;
2440 /* Now write the byte count of the section */
2441 seek.QuadPart = SECTIONHEADER_OFFSET;
2442 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2443 if (FAILED(hr))
2444 goto end;
2445 StorageUtl_WriteDWord(&dwTemp, 0, sectionOffset);
2446 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2448 end:
2449 return hr;
2452 /***********************************************************************
2453 * PropertyStorage_Construct
2455 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This)
2457 dictionary_destroy(This->name_to_propid);
2458 This->name_to_propid = NULL;
2459 dictionary_destroy(This->propid_to_name);
2460 This->propid_to_name = NULL;
2461 dictionary_destroy(This->propid_to_prop);
2462 This->propid_to_prop = NULL;
2465 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This)
2467 HRESULT hr = S_OK;
2469 This->name_to_propid = dictionary_create(
2470 PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy,
2471 This);
2472 if (!This->name_to_propid)
2474 hr = STG_E_INSUFFICIENTMEMORY;
2475 goto end;
2477 This->propid_to_name = dictionary_create(PropertyStorage_PropCompare,
2478 NULL, This);
2479 if (!This->propid_to_name)
2481 hr = STG_E_INSUFFICIENTMEMORY;
2482 goto end;
2484 This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
2485 PropertyStorage_PropertyDestroy, This);
2486 if (!This->propid_to_prop)
2488 hr = STG_E_INSUFFICIENTMEMORY;
2489 goto end;
2491 end:
2492 if (FAILED(hr))
2493 PropertyStorage_DestroyDictionaries(This);
2494 return hr;
2497 static HRESULT PropertyStorage_BaseConstruct(IStream *stm,
2498 REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps)
2500 HRESULT hr = S_OK;
2502 assert(pps);
2503 assert(rfmtid);
2504 *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps);
2505 if (!*pps)
2506 return E_OUTOFMEMORY;
2508 (*pps)->IPropertyStorage_iface.lpVtbl = &IPropertyStorage_Vtbl;
2509 (*pps)->ref = 1;
2510 InitializeCriticalSection(&(*pps)->cs);
2511 (*pps)->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PropertyStorage_impl.cs");
2512 (*pps)->stm = stm;
2513 (*pps)->fmtid = *rfmtid;
2514 (*pps)->grfMode = grfMode;
2516 hr = PropertyStorage_CreateDictionaries(*pps);
2517 if (FAILED(hr))
2519 (*pps)->cs.DebugInfo->Spare[0] = 0;
2520 DeleteCriticalSection(&(*pps)->cs);
2521 HeapFree(GetProcessHeap(), 0, *pps);
2522 *pps = NULL;
2524 else IStream_AddRef( stm );
2526 return hr;
2529 static HRESULT PropertyStorage_ConstructFromStream(IStream *stm,
2530 REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps)
2532 PropertyStorage_impl *ps;
2533 HRESULT hr;
2535 assert(pps);
2536 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2537 if (SUCCEEDED(hr))
2539 hr = PropertyStorage_ReadFromStream(ps);
2540 if (SUCCEEDED(hr))
2542 *pps = &ps->IPropertyStorage_iface;
2543 TRACE("PropertyStorage %p constructed\n", ps);
2544 hr = S_OK;
2546 else IPropertyStorage_Release( &ps->IPropertyStorage_iface );
2548 return hr;
2551 static HRESULT PropertyStorage_ConstructEmpty(IStream *stm,
2552 REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
2554 PropertyStorage_impl *ps;
2555 HRESULT hr;
2557 assert(pps);
2558 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2559 if (SUCCEEDED(hr))
2561 ps->format = 0;
2562 ps->grfFlags = grfFlags;
2563 if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2564 ps->format = 1;
2565 /* default to Unicode unless told not to, as specified on msdn */
2566 if (ps->grfFlags & PROPSETFLAG_ANSI)
2567 ps->codePage = GetACP();
2568 else
2569 ps->codePage = CP_UNICODE;
2570 ps->locale = LOCALE_SYSTEM_DEFAULT;
2571 TRACE("Code page is %d, locale is %d\n", ps->codePage, ps->locale);
2572 *pps = &ps->IPropertyStorage_iface;
2573 TRACE("PropertyStorage %p constructed\n", ps);
2574 hr = S_OK;
2576 return hr;
2580 /***********************************************************************
2581 * Implementation of IPropertySetStorage
2584 struct enum_stat_propset_stg
2586 IEnumSTATPROPSETSTG IEnumSTATPROPSETSTG_iface;
2587 LONG refcount;
2588 STATPROPSETSTG *stats;
2589 size_t current;
2590 size_t count;
2593 static struct enum_stat_propset_stg *impl_from_IEnumSTATPROPSETSTG(IEnumSTATPROPSETSTG *iface)
2595 return CONTAINING_RECORD(iface, struct enum_stat_propset_stg, IEnumSTATPROPSETSTG_iface);
2598 static HRESULT WINAPI enum_stat_propset_stg_QueryInterface(IEnumSTATPROPSETSTG *iface, REFIID riid, void **obj)
2600 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
2602 if (IsEqualIID(riid, &IID_IEnumSTATPROPSETSTG) ||
2603 IsEqualIID(riid, &IID_IUnknown))
2605 *obj = iface;
2606 IEnumSTATPROPSETSTG_AddRef(iface);
2607 return S_OK;
2610 WARN("Unsupported interface %s.\n", debugstr_guid(riid));
2611 return E_NOINTERFACE;
2614 static ULONG WINAPI enum_stat_propset_stg_AddRef(IEnumSTATPROPSETSTG *iface)
2616 struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface);
2617 LONG refcount = InterlockedIncrement(&psenum->refcount);
2619 TRACE("%p, refcount %u.\n", iface, refcount);
2621 return refcount;
2624 static ULONG WINAPI enum_stat_propset_stg_Release(IEnumSTATPROPSETSTG *iface)
2626 struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface);
2627 LONG refcount = InterlockedDecrement(&psenum->refcount);
2629 TRACE("%p, refcount %u.\n", iface, refcount);
2631 if (!refcount)
2633 heap_free(psenum->stats);
2634 heap_free(psenum);
2637 return refcount;
2640 static HRESULT WINAPI enum_stat_propset_stg_Next(IEnumSTATPROPSETSTG *iface, ULONG celt,
2641 STATPROPSETSTG *ret, ULONG *fetched)
2643 struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface);
2644 ULONG count = 0;
2646 TRACE("%p, %u, %p, %p.\n", iface, celt, ret, fetched);
2648 if (psenum->current == ~0u)
2649 psenum->current = 0;
2651 while (count < celt && psenum->current < psenum->count)
2652 ret[count++] = psenum->stats[psenum->current++];
2654 if (fetched)
2655 *fetched = count;
2657 return count < celt ? S_FALSE : S_OK;
2660 static HRESULT WINAPI enum_stat_propset_stg_Skip(IEnumSTATPROPSETSTG *iface, ULONG celt)
2662 FIXME("%p, %u.\n", iface, celt);
2664 return S_OK;
2667 static HRESULT WINAPI enum_stat_propset_stg_Reset(IEnumSTATPROPSETSTG *iface)
2669 struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface);
2671 TRACE("%p.\n", iface);
2673 psenum->current = ~0u;
2675 return S_OK;
2678 static HRESULT WINAPI enum_stat_propset_stg_Clone(IEnumSTATPROPSETSTG *iface, IEnumSTATPROPSETSTG **ppenum)
2680 FIXME("%p, %p.\n", iface, ppenum);
2682 return E_NOTIMPL;
2685 static const IEnumSTATPROPSETSTGVtbl enum_stat_propset_stg_vtbl =
2687 enum_stat_propset_stg_QueryInterface,
2688 enum_stat_propset_stg_AddRef,
2689 enum_stat_propset_stg_Release,
2690 enum_stat_propset_stg_Next,
2691 enum_stat_propset_stg_Skip,
2692 enum_stat_propset_stg_Reset,
2693 enum_stat_propset_stg_Clone,
2696 static HRESULT create_enum_stat_propset_stg(StorageImpl *storage, IEnumSTATPROPSETSTG **ret)
2698 IStorage *stg = &storage->base.IStorage_iface;
2699 IEnumSTATSTG *penum = NULL;
2700 STATSTG stat;
2701 ULONG count;
2702 HRESULT hr;
2704 struct enum_stat_propset_stg *enum_obj;
2706 enum_obj = heap_alloc_zero(sizeof(*enum_obj));
2707 if (!enum_obj)
2708 return E_OUTOFMEMORY;
2710 enum_obj->IEnumSTATPROPSETSTG_iface.lpVtbl = &enum_stat_propset_stg_vtbl;
2711 enum_obj->refcount = 1;
2713 /* add all the property set elements into a list */
2714 hr = IStorage_EnumElements(stg, 0, NULL, 0, &penum);
2715 if (FAILED(hr))
2716 goto done;
2718 /* Allocate stats array and fill it. */
2719 while ((hr = IEnumSTATSTG_Next(penum, 1, &stat, &count)) == S_OK)
2721 enum_obj->count++;
2722 CoTaskMemFree(stat.pwcsName);
2725 if (FAILED(hr))
2726 goto done;
2728 enum_obj->stats = heap_alloc(enum_obj->count * sizeof(*enum_obj->stats));
2729 if (!enum_obj->stats)
2731 hr = E_OUTOFMEMORY;
2732 goto done;
2734 enum_obj->count = 0;
2736 if (FAILED(hr = IEnumSTATSTG_Reset(penum)))
2737 goto done;
2739 while (IEnumSTATSTG_Next(penum, 1, &stat, &count) == S_OK)
2741 if (!stat.pwcsName)
2742 continue;
2744 if (stat.pwcsName[0] == 5 && stat.type == STGTY_STREAM)
2746 STATPROPSETSTG *ptr = &enum_obj->stats[enum_obj->count++];
2748 PropStgNameToFmtId(stat.pwcsName, &ptr->fmtid);
2750 TRACE("adding %s - %s.\n", debugstr_w(stat.pwcsName), debugstr_guid(&ptr->fmtid));
2752 ptr->mtime = stat.mtime;
2753 ptr->atime = stat.atime;
2754 ptr->ctime = stat.ctime;
2755 ptr->grfFlags = stat.grfMode;
2756 ptr->clsid = stat.clsid;
2758 CoTaskMemFree(stat.pwcsName);
2761 done:
2763 if (penum)
2764 IEnumSTATSTG_Release(penum);
2766 if (SUCCEEDED(hr))
2768 *ret = &enum_obj->IEnumSTATPROPSETSTG_iface;
2770 else
2772 *ret = NULL;
2773 IEnumSTATPROPSETSTG_Release(&enum_obj->IEnumSTATPROPSETSTG_iface);
2776 return hr;
2779 /************************************************************************
2780 * IPropertySetStorage_fnQueryInterface (IUnknown)
2782 * This method forwards to the common QueryInterface implementation
2784 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
2785 IPropertySetStorage *ppstg,
2786 REFIID riid,
2787 void** ppvObject)
2789 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2790 return IStorage_QueryInterface( &This->base.IStorage_iface, riid, ppvObject );
2793 /************************************************************************
2794 * IPropertySetStorage_fnAddRef (IUnknown)
2796 * This method forwards to the common AddRef implementation
2798 static ULONG WINAPI IPropertySetStorage_fnAddRef(
2799 IPropertySetStorage *ppstg)
2801 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2802 return IStorage_AddRef( &This->base.IStorage_iface );
2805 /************************************************************************
2806 * IPropertySetStorage_fnRelease (IUnknown)
2808 * This method forwards to the common Release implementation
2810 static ULONG WINAPI IPropertySetStorage_fnRelease(
2811 IPropertySetStorage *ppstg)
2813 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2814 return IStorage_Release( &This->base.IStorage_iface );
2817 /************************************************************************
2818 * IPropertySetStorage_fnCreate (IPropertySetStorage)
2820 static HRESULT WINAPI IPropertySetStorage_fnCreate(
2821 IPropertySetStorage *ppstg,
2822 REFFMTID rfmtid,
2823 const CLSID* pclsid,
2824 DWORD grfFlags,
2825 DWORD grfMode,
2826 IPropertyStorage** ppprstg)
2828 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2829 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2830 IStream *stm = NULL;
2831 HRESULT r;
2833 TRACE("%p %s %08x %08x %p\n", This, debugstr_guid(rfmtid), grfFlags,
2834 grfMode, ppprstg);
2836 /* be picky */
2837 if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
2839 r = STG_E_INVALIDFLAG;
2840 goto end;
2843 if (!rfmtid)
2845 r = E_INVALIDARG;
2846 goto end;
2849 /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
2850 * storage, not a stream. For now, disallow it.
2852 if (grfFlags & PROPSETFLAG_NONSIMPLE)
2854 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2855 r = STG_E_INVALIDFLAG;
2856 goto end;
2859 r = FmtIdToPropStgName(rfmtid, name);
2860 if (FAILED(r))
2861 goto end;
2863 r = IStorage_CreateStream( &This->base.IStorage_iface, name, grfMode, 0, 0, &stm );
2864 if (FAILED(r))
2865 goto end;
2867 r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg);
2869 IStream_Release( stm );
2871 end:
2872 TRACE("returning 0x%08x\n", r);
2873 return r;
2876 /************************************************************************
2877 * IPropertySetStorage_fnOpen (IPropertySetStorage)
2879 static HRESULT WINAPI IPropertySetStorage_fnOpen(
2880 IPropertySetStorage *ppstg,
2881 REFFMTID rfmtid,
2882 DWORD grfMode,
2883 IPropertyStorage** ppprstg)
2885 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2886 IStream *stm = NULL;
2887 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2888 HRESULT r;
2890 TRACE("%p %s %08x %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
2892 /* be picky */
2893 if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
2894 grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
2896 r = STG_E_INVALIDFLAG;
2897 goto end;
2900 if (!rfmtid)
2902 r = E_INVALIDARG;
2903 goto end;
2906 r = FmtIdToPropStgName(rfmtid, name);
2907 if (FAILED(r))
2908 goto end;
2910 r = IStorage_OpenStream( &This->base.IStorage_iface, name, 0, grfMode, 0, &stm );
2911 if (FAILED(r))
2912 goto end;
2914 r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg);
2916 IStream_Release( stm );
2918 end:
2919 TRACE("returning 0x%08x\n", r);
2920 return r;
2923 /************************************************************************
2924 * IPropertySetStorage_fnDelete (IPropertySetStorage)
2926 static HRESULT WINAPI IPropertySetStorage_fnDelete(
2927 IPropertySetStorage *ppstg,
2928 REFFMTID rfmtid)
2930 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2931 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2932 HRESULT r;
2934 TRACE("%p %s\n", This, debugstr_guid(rfmtid));
2936 if (!rfmtid)
2937 return E_INVALIDARG;
2939 r = FmtIdToPropStgName(rfmtid, name);
2940 if (FAILED(r))
2941 return r;
2943 return IStorage_DestroyElement(&This->base.IStorage_iface, name);
2946 static HRESULT WINAPI IPropertySetStorage_fnEnum(IPropertySetStorage *iface, IEnumSTATPROPSETSTG **enum_obj)
2948 StorageImpl *storage = impl_from_IPropertySetStorage(iface);
2950 TRACE("%p, %p.\n", iface, enum_obj);
2952 if (!enum_obj)
2953 return E_INVALIDARG;
2955 return create_enum_stat_propset_stg(storage, enum_obj);
2958 /***********************************************************************
2959 * vtables
2961 const IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
2963 IPropertySetStorage_fnQueryInterface,
2964 IPropertySetStorage_fnAddRef,
2965 IPropertySetStorage_fnRelease,
2966 IPropertySetStorage_fnCreate,
2967 IPropertySetStorage_fnOpen,
2968 IPropertySetStorage_fnDelete,
2969 IPropertySetStorage_fnEnum
2972 static const IPropertyStorageVtbl IPropertyStorage_Vtbl =
2974 IPropertyStorage_fnQueryInterface,
2975 IPropertyStorage_fnAddRef,
2976 IPropertyStorage_fnRelease,
2977 IPropertyStorage_fnReadMultiple,
2978 IPropertyStorage_fnWriteMultiple,
2979 IPropertyStorage_fnDeleteMultiple,
2980 IPropertyStorage_fnReadPropertyNames,
2981 IPropertyStorage_fnWritePropertyNames,
2982 IPropertyStorage_fnDeletePropertyNames,
2983 IPropertyStorage_fnCommit,
2984 IPropertyStorage_fnRevert,
2985 IPropertyStorage_fnEnum,
2986 IPropertyStorage_fnSetTimes,
2987 IPropertyStorage_fnSetClass,
2988 IPropertyStorage_fnStat,
2991 /***********************************************************************
2992 * Format ID <-> name conversion
2994 static const WCHAR szSummaryInfo[] = L"\5SummaryInformation";
2995 static const WCHAR szDocSummaryInfo[] = L"\5DocumentSummaryInformation";
2997 #define BITS_PER_BYTE 8
2998 #define CHARMASK 0x1f
2999 #define BITS_IN_CHARMASK 5
3000 #define NUM_ALPHA_CHARS 26
3002 /***********************************************************************
3003 * FmtIdToPropStgName [ole32.@]
3004 * Returns the storage name of the format ID rfmtid.
3005 * PARAMS
3006 * rfmtid [I] Format ID for which to return a storage name
3007 * str [O] Storage name associated with rfmtid.
3009 * RETURNS
3010 * E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise.
3012 * NOTES
3013 * str must be at least CCH_MAX_PROPSTG_NAME characters in length.
3015 HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str)
3017 static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
3019 TRACE("%s, %p\n", debugstr_guid(rfmtid), str);
3021 if (!rfmtid) return E_INVALIDARG;
3022 if (!str) return E_INVALIDARG;
3024 if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
3025 lstrcpyW(str, szSummaryInfo);
3026 else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
3027 lstrcpyW(str, szDocSummaryInfo);
3028 else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid))
3029 lstrcpyW(str, szDocSummaryInfo);
3030 else
3032 const BYTE *fmtptr;
3033 WCHAR *pstr = str;
3034 ULONG bitsRemaining = BITS_PER_BYTE;
3036 *pstr++ = 5;
3037 for (fmtptr = (const BYTE *)rfmtid; fmtptr < (const BYTE *)rfmtid + sizeof(FMTID); )
3039 ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
3041 if (bitsRemaining >= BITS_IN_CHARMASK)
3043 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
3044 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
3045 *pstr <= 'z')
3046 *pstr += 'A' - 'a';
3047 pstr++;
3048 bitsRemaining -= BITS_IN_CHARMASK;
3049 if (bitsRemaining == 0)
3051 fmtptr++;
3052 bitsRemaining = BITS_PER_BYTE;
3055 else
3057 if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID))
3058 i |= *fmtptr << bitsRemaining;
3059 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
3060 bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK;
3063 *pstr = 0;
3065 TRACE("returning %s\n", debugstr_w(str));
3066 return S_OK;
3069 /***********************************************************************
3070 * PropStgNameToFmtId [ole32.@]
3071 * Returns the format ID corresponding to the given name.
3072 * PARAMS
3073 * str [I] Storage name to convert to a format ID.
3074 * rfmtid [O] Format ID corresponding to str.
3076 * RETURNS
3077 * E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to
3078 * a format ID, S_OK otherwise.
3080 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid)
3082 HRESULT hr = STG_E_INVALIDNAME;
3084 TRACE("%s, %p\n", debugstr_w(str), rfmtid);
3086 if (!rfmtid) return E_INVALIDARG;
3087 if (!str) return STG_E_INVALIDNAME;
3089 if (!lstrcmpiW(str, szDocSummaryInfo))
3091 *rfmtid = FMTID_DocSummaryInformation;
3092 hr = S_OK;
3094 else if (!lstrcmpiW(str, szSummaryInfo))
3096 *rfmtid = FMTID_SummaryInformation;
3097 hr = S_OK;
3099 else
3101 ULONG bits;
3102 BYTE *fmtptr = (BYTE *)rfmtid - 1;
3103 const WCHAR *pstr = str;
3105 memset(rfmtid, 0, sizeof(*rfmtid));
3106 for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE;
3107 bits += BITS_IN_CHARMASK)
3109 ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored;
3110 WCHAR wc;
3112 if (bitsUsed == 0)
3113 fmtptr++;
3114 wc = *++pstr - 'A';
3115 if (wc > NUM_ALPHA_CHARS)
3117 wc += 'A' - 'a';
3118 if (wc > NUM_ALPHA_CHARS)
3120 wc += 'a' - '0' + NUM_ALPHA_CHARS;
3121 if (wc > CHARMASK)
3123 WARN("invalid character (%d)\n", *pstr);
3124 goto end;
3128 *fmtptr |= wc << bitsUsed;
3129 bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK);
3130 if (bitsStored < BITS_IN_CHARMASK)
3132 wc >>= BITS_PER_BYTE - bitsUsed;
3133 if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE)
3135 if (wc != 0)
3137 WARN("extra bits\n");
3138 goto end;
3140 break;
3142 fmtptr++;
3143 *fmtptr |= (BYTE)wc;
3146 hr = S_OK;
3148 end:
3149 return hr;
3152 #ifdef __i386__ /* thiscall functions are i386-specific */
3154 #define DEFINE_STDCALL_WRAPPER(num,func,args) \
3155 __ASM_STDCALL_FUNC(func, args, \
3156 "popl %eax\n\t" \
3157 "popl %ecx\n\t" \
3158 "pushl %eax\n\t" \
3159 "movl (%ecx), %eax\n\t" \
3160 "jmp *(4*(" #num "))(%eax)" )
3162 DEFINE_STDCALL_WRAPPER(0,Allocate_PMemoryAllocator,8)
3163 extern void* WINAPI Allocate_PMemoryAllocator(void *this, ULONG cbSize);
3165 #else
3167 static void* WINAPI Allocate_PMemoryAllocator(void *this, ULONG cbSize)
3169 void* (WINAPI *fn)(void*,ULONG) = **(void***)this;
3170 return fn(this, cbSize);
3173 #endif
3175 BOOLEAN WINAPI StgConvertPropertyToVariant(const SERIALIZEDPROPERTYVALUE* prop,
3176 USHORT CodePage, PROPVARIANT* pvar, void* pma)
3178 struct read_buffer read_buffer;
3179 HRESULT hr;
3181 read_buffer.data = (BYTE *)prop;
3182 read_buffer.size = ~(size_t)0;
3183 hr = PropertyStorage_ReadProperty(pvar, &read_buffer, 0, CodePage, Allocate_PMemoryAllocator, pma);
3185 if (FAILED(hr))
3187 FIXME("should raise C++ exception on failure\n");
3188 PropVariantInit(pvar);
3191 return FALSE;
3194 SERIALIZEDPROPERTYVALUE* WINAPI StgConvertVariantToProperty(const PROPVARIANT *pvar,
3195 USHORT CodePage, SERIALIZEDPROPERTYVALUE *pprop, ULONG *pcb, PROPID pid,
3196 BOOLEAN fReserved, ULONG *pcIndirect)
3198 FIXME("%p,%d,%p,%p,%d,%d,%p\n", pvar, CodePage, pprop, pcb, pid, fReserved, pcIndirect);
3200 return NULL;
3203 HRESULT WINAPI StgCreatePropStg(IUnknown *unk, REFFMTID fmt, const CLSID *clsid,
3204 DWORD flags, DWORD reserved, IPropertyStorage **prop_stg)
3206 IStorage *stg;
3207 IStream *stm;
3208 HRESULT r;
3210 TRACE("%p %s %s %08x %d %p\n", unk, debugstr_guid(fmt), debugstr_guid(clsid), flags, reserved, prop_stg);
3212 if (!fmt || reserved)
3214 r = E_INVALIDARG;
3215 goto end;
3218 if (flags & PROPSETFLAG_NONSIMPLE)
3220 r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
3221 if (FAILED(r))
3222 goto end;
3224 /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to create a
3225 * storage, not a stream. For now, disallow it.
3227 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
3228 IStorage_Release(stg);
3229 r = STG_E_INVALIDFLAG;
3231 else
3233 r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
3234 if (FAILED(r))
3235 goto end;
3237 r = PropertyStorage_ConstructEmpty(stm, fmt, flags,
3238 STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
3240 IStream_Release( stm );
3243 end:
3244 TRACE("returning 0x%08x\n", r);
3245 return r;
3248 HRESULT WINAPI StgOpenPropStg(IUnknown *unk, REFFMTID fmt, DWORD flags,
3249 DWORD reserved, IPropertyStorage **prop_stg)
3251 IStorage *stg;
3252 IStream *stm;
3253 HRESULT r;
3255 TRACE("%p %s %08x %d %p\n", unk, debugstr_guid(fmt), flags, reserved, prop_stg);
3257 if (!fmt || reserved)
3259 r = E_INVALIDARG;
3260 goto end;
3263 if (flags & PROPSETFLAG_NONSIMPLE)
3265 r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
3266 if (FAILED(r))
3267 goto end;
3269 /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to open a
3270 * storage, not a stream. For now, disallow it.
3272 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
3273 IStorage_Release(stg);
3274 r = STG_E_INVALIDFLAG;
3276 else
3278 r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
3279 if (FAILED(r))
3280 goto end;
3282 r = PropertyStorage_ConstructFromStream(stm, fmt,
3283 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
3285 IStream_Release( stm );
3288 end:
3289 TRACE("returning 0x%08x\n", r);
3290 return r;