winegcc: Add entry symbol underscore when building linker command.
[wine.git] / dlls / ole32 / stg_prop.c
blobadf4c2f3370ca4d1d44cb06883e298eb09dc46f1
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 ULONG count;
2108 assert(var);
2109 assert(sectionOffset);
2111 TRACE("%p, %d, 0x%08x, (%d), (%d)\n", This, propNum, propid, var->vt,
2112 *sectionOffset);
2114 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) +
2115 propNum * sizeof(PROPERTYIDOFFSET);
2116 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2117 if (FAILED(hr))
2118 goto end;
2119 PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset);
2120 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
2121 if (FAILED(hr))
2122 goto end;
2124 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
2125 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2126 if (FAILED(hr))
2127 goto end;
2128 StorageUtl_WriteDWord(&dwType, 0, var->vt);
2129 hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count);
2130 if (FAILED(hr))
2131 goto end;
2132 *sectionOffset += sizeof(dwType);
2134 switch (var->vt)
2136 case VT_EMPTY:
2137 case VT_NULL:
2138 bytesWritten = 0;
2139 break;
2140 case VT_I1:
2141 case VT_UI1:
2142 hr = IStream_Write(This->stm, &var->u.cVal, sizeof(var->u.cVal),
2143 &count);
2144 bytesWritten = count;
2145 break;
2146 case VT_I2:
2147 case VT_UI2:
2149 WORD wTemp;
2151 StorageUtl_WriteWord(&wTemp, 0, var->u.iVal);
2152 hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count);
2153 bytesWritten = count;
2154 break;
2156 case VT_I4:
2157 case VT_UI4:
2159 StorageUtl_WriteDWord(&dwTemp, 0, var->u.lVal);
2160 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2161 bytesWritten = count;
2162 break;
2164 case VT_LPSTR:
2166 if (This->codePage == CP_UNICODE)
2167 len = (lstrlenW(var->u.pwszVal) + 1) * sizeof(WCHAR);
2168 else
2169 len = lstrlenA(var->u.pszVal) + 1;
2170 StorageUtl_WriteDWord(&dwTemp, 0, len);
2171 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2172 if (FAILED(hr))
2173 goto end;
2174 hr = IStream_Write(This->stm, var->u.pszVal, len, &count);
2175 bytesWritten = count + sizeof(DWORD);
2176 break;
2178 case VT_BSTR:
2180 if (This->codePage == CP_UNICODE)
2182 len = SysStringByteLen(var->u.bstrVal) + sizeof(WCHAR);
2183 StorageUtl_WriteDWord(&dwTemp, 0, len);
2184 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2185 if (SUCCEEDED(hr))
2186 hr = IStream_Write(This->stm, var->u.bstrVal, len, &count);
2188 else
2190 char *str;
2192 len = WideCharToMultiByte(This->codePage, 0, var->u.bstrVal, SysStringLen(var->u.bstrVal) + 1,
2193 NULL, 0, NULL, NULL);
2195 str = heap_alloc(len);
2196 if (!str)
2198 hr = E_OUTOFMEMORY;
2199 goto end;
2202 WideCharToMultiByte(This->codePage, 0, var->u.bstrVal, SysStringLen(var->u.bstrVal),
2203 str, len, NULL, NULL);
2204 StorageUtl_WriteDWord(&dwTemp, 0, len);
2205 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2206 if (SUCCEEDED(hr))
2207 hr = IStream_Write(This->stm, str, len, &count);
2208 heap_free(str);
2211 bytesWritten = count + sizeof(DWORD);
2212 break;
2214 case VT_LPWSTR:
2216 len = lstrlenW(var->u.pwszVal) + 1;
2218 StorageUtl_WriteDWord(&dwTemp, 0, len);
2219 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2220 if (FAILED(hr))
2221 goto end;
2222 hr = IStream_Write(This->stm, var->u.pwszVal, len * sizeof(WCHAR),
2223 &count);
2224 bytesWritten = count + sizeof(DWORD);
2225 break;
2227 case VT_FILETIME:
2229 FILETIME temp;
2231 StorageUtl_WriteULargeInteger(&temp, 0, (const ULARGE_INTEGER *)&var->u.filetime);
2232 hr = IStream_Write(This->stm, &temp, sizeof(temp), &count);
2233 bytesWritten = count;
2234 break;
2236 case VT_CF:
2238 DWORD cf_hdr[2];
2240 len = var->u.pclipdata->cbSize;
2241 StorageUtl_WriteDWord(&cf_hdr[0], 0, len + 8);
2242 StorageUtl_WriteDWord(&cf_hdr[1], 0, var->u.pclipdata->ulClipFmt);
2243 hr = IStream_Write(This->stm, cf_hdr, sizeof(cf_hdr), &count);
2244 if (FAILED(hr))
2245 goto end;
2246 hr = IStream_Write(This->stm, var->u.pclipdata->pClipData,
2247 len - sizeof(var->u.pclipdata->ulClipFmt), &count);
2248 if (FAILED(hr))
2249 goto end;
2250 bytesWritten = count + sizeof cf_hdr;
2251 break;
2253 case VT_CLSID:
2255 CLSID temp;
2257 StorageUtl_WriteGUID(&temp, 0, var->u.puuid);
2258 hr = IStream_Write(This->stm, &temp, sizeof(temp), &count);
2259 bytesWritten = count;
2260 break;
2262 default:
2263 FIXME("unsupported type: %d\n", var->vt);
2264 return STG_E_INVALIDPARAMETER;
2267 if (SUCCEEDED(hr))
2269 *sectionOffset += bytesWritten;
2270 if (bytesWritten % sizeof(DWORD))
2272 DWORD padding = sizeof(DWORD) - bytesWritten % sizeof(DWORD);
2273 TRACE("adding %d bytes of padding\n", padding);
2274 *sectionOffset += padding;
2278 end:
2279 return hr;
2282 struct PropertyClosure
2284 HRESULT hr;
2285 DWORD propNum;
2286 DWORD *sectionOffset;
2289 static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value,
2290 void *extra, void *closure)
2292 PropertyStorage_impl *This = extra;
2293 struct PropertyClosure *c = closure;
2295 assert(key);
2296 assert(value);
2297 assert(extra);
2298 assert(closure);
2299 c->hr = PropertyStorage_WritePropertyToStream(This, c->propNum++,
2300 PtrToUlong(key), value, c->sectionOffset);
2301 return SUCCEEDED(c->hr);
2304 static HRESULT PropertyStorage_WritePropertiesToStream(
2305 PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset)
2307 struct PropertyClosure closure;
2309 assert(sectionOffset);
2310 closure.hr = S_OK;
2311 closure.propNum = startingPropNum;
2312 closure.sectionOffset = sectionOffset;
2313 dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter,
2314 &closure);
2315 return closure.hr;
2318 static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This)
2320 HRESULT hr;
2321 ULONG count = 0;
2322 LARGE_INTEGER seek = { {0} };
2323 PROPERTYSETHEADER hdr;
2324 FORMATIDOFFSET fmtOffset;
2326 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2327 if (FAILED(hr))
2328 goto end;
2329 PropertyStorage_MakeHeader(This, &hdr);
2330 hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count);
2331 if (FAILED(hr))
2332 goto end;
2333 if (count != sizeof(hdr))
2335 hr = STG_E_WRITEFAULT;
2336 goto end;
2339 PropertyStorage_MakeFmtIdOffset(This, &fmtOffset);
2340 hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count);
2341 if (FAILED(hr))
2342 goto end;
2343 if (count != sizeof(fmtOffset))
2345 hr = STG_E_WRITEFAULT;
2346 goto end;
2348 hr = S_OK;
2350 end:
2351 return hr;
2354 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This)
2356 PROPERTYSECTIONHEADER sectionHdr;
2357 HRESULT hr;
2358 ULONG count;
2359 LARGE_INTEGER seek;
2360 DWORD numProps, prop, sectionOffset, dwTemp;
2361 PROPVARIANT var;
2363 PropertyStorage_WriteHeadersToStream(This);
2365 /* Count properties. Always at least one property, the code page */
2366 numProps = 1;
2367 if (dictionary_num_entries(This->name_to_propid))
2368 numProps++;
2369 if (This->locale != LOCALE_SYSTEM_DEFAULT)
2370 numProps++;
2371 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2372 numProps++;
2373 numProps += dictionary_num_entries(This->propid_to_prop);
2375 /* Write section header with 0 bytes right now, I'll adjust it after
2376 * writing properties.
2378 PropertyStorage_MakeSectionHdr(0, numProps, &sectionHdr);
2379 seek.QuadPart = SECTIONHEADER_OFFSET;
2380 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2381 if (FAILED(hr))
2382 goto end;
2383 hr = IStream_Write(This->stm, &sectionHdr, sizeof(sectionHdr), &count);
2384 if (FAILED(hr))
2385 goto end;
2387 prop = 0;
2388 sectionOffset = sizeof(PROPERTYSECTIONHEADER) +
2389 numProps * sizeof(PROPERTYIDOFFSET);
2391 if (dictionary_num_entries(This->name_to_propid))
2393 prop++;
2394 hr = PropertyStorage_WriteDictionaryToStream(This, &sectionOffset);
2395 if (FAILED(hr))
2396 goto end;
2399 PropVariantInit(&var);
2401 var.vt = VT_I2;
2402 var.u.iVal = This->codePage;
2403 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE,
2404 &var, &sectionOffset);
2405 if (FAILED(hr))
2406 goto end;
2408 if (This->locale != LOCALE_SYSTEM_DEFAULT)
2410 var.vt = VT_I4;
2411 var.u.lVal = This->locale;
2412 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE,
2413 &var, &sectionOffset);
2414 if (FAILED(hr))
2415 goto end;
2418 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2420 var.vt = VT_I4;
2421 var.u.lVal = 1;
2422 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR,
2423 &var, &sectionOffset);
2424 if (FAILED(hr))
2425 goto end;
2428 hr = PropertyStorage_WritePropertiesToStream(This, prop, &sectionOffset);
2429 if (FAILED(hr))
2430 goto end;
2432 /* Now write the byte count of the section */
2433 seek.QuadPart = SECTIONHEADER_OFFSET;
2434 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2435 if (FAILED(hr))
2436 goto end;
2437 StorageUtl_WriteDWord(&dwTemp, 0, sectionOffset);
2438 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2440 end:
2441 return hr;
2444 /***********************************************************************
2445 * PropertyStorage_Construct
2447 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This)
2449 dictionary_destroy(This->name_to_propid);
2450 This->name_to_propid = NULL;
2451 dictionary_destroy(This->propid_to_name);
2452 This->propid_to_name = NULL;
2453 dictionary_destroy(This->propid_to_prop);
2454 This->propid_to_prop = NULL;
2457 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This)
2459 HRESULT hr = S_OK;
2461 This->name_to_propid = dictionary_create(
2462 PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy,
2463 This);
2464 if (!This->name_to_propid)
2466 hr = STG_E_INSUFFICIENTMEMORY;
2467 goto end;
2469 This->propid_to_name = dictionary_create(PropertyStorage_PropCompare,
2470 NULL, This);
2471 if (!This->propid_to_name)
2473 hr = STG_E_INSUFFICIENTMEMORY;
2474 goto end;
2476 This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
2477 PropertyStorage_PropertyDestroy, This);
2478 if (!This->propid_to_prop)
2480 hr = STG_E_INSUFFICIENTMEMORY;
2481 goto end;
2483 end:
2484 if (FAILED(hr))
2485 PropertyStorage_DestroyDictionaries(This);
2486 return hr;
2489 static HRESULT PropertyStorage_BaseConstruct(IStream *stm,
2490 REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps)
2492 HRESULT hr = S_OK;
2494 assert(pps);
2495 assert(rfmtid);
2496 *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps);
2497 if (!*pps)
2498 return E_OUTOFMEMORY;
2500 (*pps)->IPropertyStorage_iface.lpVtbl = &IPropertyStorage_Vtbl;
2501 (*pps)->ref = 1;
2502 InitializeCriticalSection(&(*pps)->cs);
2503 (*pps)->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PropertyStorage_impl.cs");
2504 (*pps)->stm = stm;
2505 (*pps)->fmtid = *rfmtid;
2506 (*pps)->grfMode = grfMode;
2508 hr = PropertyStorage_CreateDictionaries(*pps);
2509 if (FAILED(hr))
2511 (*pps)->cs.DebugInfo->Spare[0] = 0;
2512 DeleteCriticalSection(&(*pps)->cs);
2513 HeapFree(GetProcessHeap(), 0, *pps);
2514 *pps = NULL;
2516 else IStream_AddRef( stm );
2518 return hr;
2521 static HRESULT PropertyStorage_ConstructFromStream(IStream *stm,
2522 REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps)
2524 PropertyStorage_impl *ps;
2525 HRESULT hr;
2527 assert(pps);
2528 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2529 if (SUCCEEDED(hr))
2531 hr = PropertyStorage_ReadFromStream(ps);
2532 if (SUCCEEDED(hr))
2534 *pps = &ps->IPropertyStorage_iface;
2535 TRACE("PropertyStorage %p constructed\n", ps);
2536 hr = S_OK;
2538 else IPropertyStorage_Release( &ps->IPropertyStorage_iface );
2540 return hr;
2543 static HRESULT PropertyStorage_ConstructEmpty(IStream *stm,
2544 REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
2546 PropertyStorage_impl *ps;
2547 HRESULT hr;
2549 assert(pps);
2550 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2551 if (SUCCEEDED(hr))
2553 ps->format = 0;
2554 ps->grfFlags = grfFlags;
2555 if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2556 ps->format = 1;
2557 /* default to Unicode unless told not to, as specified on msdn */
2558 if (ps->grfFlags & PROPSETFLAG_ANSI)
2559 ps->codePage = GetACP();
2560 else
2561 ps->codePage = CP_UNICODE;
2562 ps->locale = LOCALE_SYSTEM_DEFAULT;
2563 TRACE("Code page is %d, locale is %d\n", ps->codePage, ps->locale);
2564 *pps = &ps->IPropertyStorage_iface;
2565 TRACE("PropertyStorage %p constructed\n", ps);
2566 hr = S_OK;
2568 return hr;
2572 /***********************************************************************
2573 * Implementation of IPropertySetStorage
2576 struct enum_stat_propset_stg
2578 IEnumSTATPROPSETSTG IEnumSTATPROPSETSTG_iface;
2579 LONG refcount;
2580 STATPROPSETSTG *stats;
2581 size_t current;
2582 size_t count;
2585 static struct enum_stat_propset_stg *impl_from_IEnumSTATPROPSETSTG(IEnumSTATPROPSETSTG *iface)
2587 return CONTAINING_RECORD(iface, struct enum_stat_propset_stg, IEnumSTATPROPSETSTG_iface);
2590 static HRESULT WINAPI enum_stat_propset_stg_QueryInterface(IEnumSTATPROPSETSTG *iface, REFIID riid, void **obj)
2592 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
2594 if (IsEqualIID(riid, &IID_IEnumSTATPROPSETSTG) ||
2595 IsEqualIID(riid, &IID_IUnknown))
2597 *obj = iface;
2598 IEnumSTATPROPSETSTG_AddRef(iface);
2599 return S_OK;
2602 WARN("Unsupported interface %s.\n", debugstr_guid(riid));
2603 return E_NOINTERFACE;
2606 static ULONG WINAPI enum_stat_propset_stg_AddRef(IEnumSTATPROPSETSTG *iface)
2608 struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface);
2609 LONG refcount = InterlockedIncrement(&psenum->refcount);
2611 TRACE("%p, refcount %u.\n", iface, refcount);
2613 return refcount;
2616 static ULONG WINAPI enum_stat_propset_stg_Release(IEnumSTATPROPSETSTG *iface)
2618 struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface);
2619 LONG refcount = InterlockedDecrement(&psenum->refcount);
2621 TRACE("%p, refcount %u.\n", iface, refcount);
2623 if (!refcount)
2625 heap_free(psenum->stats);
2626 heap_free(psenum);
2629 return refcount;
2632 static HRESULT WINAPI enum_stat_propset_stg_Next(IEnumSTATPROPSETSTG *iface, ULONG celt,
2633 STATPROPSETSTG *ret, ULONG *fetched)
2635 struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface);
2636 ULONG count = 0;
2638 TRACE("%p, %u, %p, %p.\n", iface, celt, ret, fetched);
2640 if (psenum->current == ~0u)
2641 psenum->current = 0;
2643 while (count < celt && psenum->current < psenum->count)
2644 ret[count++] = psenum->stats[psenum->current++];
2646 if (fetched)
2647 *fetched = count;
2649 return count < celt ? S_FALSE : S_OK;
2652 static HRESULT WINAPI enum_stat_propset_stg_Skip(IEnumSTATPROPSETSTG *iface, ULONG celt)
2654 FIXME("%p, %u.\n", iface, celt);
2656 return S_OK;
2659 static HRESULT WINAPI enum_stat_propset_stg_Reset(IEnumSTATPROPSETSTG *iface)
2661 struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface);
2663 TRACE("%p.\n", iface);
2665 psenum->current = ~0u;
2667 return S_OK;
2670 static HRESULT WINAPI enum_stat_propset_stg_Clone(IEnumSTATPROPSETSTG *iface, IEnumSTATPROPSETSTG **ppenum)
2672 FIXME("%p, %p.\n", iface, ppenum);
2674 return E_NOTIMPL;
2677 static const IEnumSTATPROPSETSTGVtbl enum_stat_propset_stg_vtbl =
2679 enum_stat_propset_stg_QueryInterface,
2680 enum_stat_propset_stg_AddRef,
2681 enum_stat_propset_stg_Release,
2682 enum_stat_propset_stg_Next,
2683 enum_stat_propset_stg_Skip,
2684 enum_stat_propset_stg_Reset,
2685 enum_stat_propset_stg_Clone,
2688 static HRESULT create_enum_stat_propset_stg(StorageImpl *storage, IEnumSTATPROPSETSTG **ret)
2690 IStorage *stg = &storage->base.IStorage_iface;
2691 IEnumSTATSTG *penum = NULL;
2692 STATSTG stat;
2693 ULONG count;
2694 HRESULT hr;
2696 struct enum_stat_propset_stg *enum_obj;
2698 enum_obj = heap_alloc_zero(sizeof(*enum_obj));
2699 if (!enum_obj)
2700 return E_OUTOFMEMORY;
2702 enum_obj->IEnumSTATPROPSETSTG_iface.lpVtbl = &enum_stat_propset_stg_vtbl;
2703 enum_obj->refcount = 1;
2705 /* add all the property set elements into a list */
2706 hr = IStorage_EnumElements(stg, 0, NULL, 0, &penum);
2707 if (FAILED(hr))
2708 goto done;
2710 /* Allocate stats array and fill it. */
2711 while ((hr = IEnumSTATSTG_Next(penum, 1, &stat, &count)) == S_OK)
2713 enum_obj->count++;
2714 CoTaskMemFree(stat.pwcsName);
2717 if (FAILED(hr))
2718 goto done;
2720 enum_obj->stats = heap_alloc(enum_obj->count * sizeof(*enum_obj->stats));
2721 if (!enum_obj->stats)
2723 hr = E_OUTOFMEMORY;
2724 goto done;
2726 enum_obj->count = 0;
2728 if (FAILED(hr = IEnumSTATSTG_Reset(penum)))
2729 goto done;
2731 while (IEnumSTATSTG_Next(penum, 1, &stat, &count) == S_OK)
2733 if (!stat.pwcsName)
2734 continue;
2736 if (stat.pwcsName[0] == 5 && stat.type == STGTY_STREAM)
2738 STATPROPSETSTG *ptr = &enum_obj->stats[enum_obj->count++];
2740 PropStgNameToFmtId(stat.pwcsName, &ptr->fmtid);
2742 TRACE("adding %s - %s.\n", debugstr_w(stat.pwcsName), debugstr_guid(&ptr->fmtid));
2744 ptr->mtime = stat.mtime;
2745 ptr->atime = stat.atime;
2746 ptr->ctime = stat.ctime;
2747 ptr->grfFlags = stat.grfMode;
2748 ptr->clsid = stat.clsid;
2750 CoTaskMemFree(stat.pwcsName);
2753 done:
2755 if (penum)
2756 IEnumSTATSTG_Release(penum);
2758 if (SUCCEEDED(hr))
2760 *ret = &enum_obj->IEnumSTATPROPSETSTG_iface;
2762 else
2764 *ret = NULL;
2765 IEnumSTATPROPSETSTG_Release(&enum_obj->IEnumSTATPROPSETSTG_iface);
2768 return hr;
2771 /************************************************************************
2772 * IPropertySetStorage_fnQueryInterface (IUnknown)
2774 * This method forwards to the common QueryInterface implementation
2776 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
2777 IPropertySetStorage *ppstg,
2778 REFIID riid,
2779 void** ppvObject)
2781 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2782 return IStorage_QueryInterface( &This->base.IStorage_iface, riid, ppvObject );
2785 /************************************************************************
2786 * IPropertySetStorage_fnAddRef (IUnknown)
2788 * This method forwards to the common AddRef implementation
2790 static ULONG WINAPI IPropertySetStorage_fnAddRef(
2791 IPropertySetStorage *ppstg)
2793 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2794 return IStorage_AddRef( &This->base.IStorage_iface );
2797 /************************************************************************
2798 * IPropertySetStorage_fnRelease (IUnknown)
2800 * This method forwards to the common Release implementation
2802 static ULONG WINAPI IPropertySetStorage_fnRelease(
2803 IPropertySetStorage *ppstg)
2805 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2806 return IStorage_Release( &This->base.IStorage_iface );
2809 /************************************************************************
2810 * IPropertySetStorage_fnCreate (IPropertySetStorage)
2812 static HRESULT WINAPI IPropertySetStorage_fnCreate(
2813 IPropertySetStorage *ppstg,
2814 REFFMTID rfmtid,
2815 const CLSID* pclsid,
2816 DWORD grfFlags,
2817 DWORD grfMode,
2818 IPropertyStorage** ppprstg)
2820 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2821 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2822 IStream *stm = NULL;
2823 HRESULT r;
2825 TRACE("%p %s %08x %08x %p\n", This, debugstr_guid(rfmtid), grfFlags,
2826 grfMode, ppprstg);
2828 /* be picky */
2829 if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
2831 r = STG_E_INVALIDFLAG;
2832 goto end;
2835 if (!rfmtid)
2837 r = E_INVALIDARG;
2838 goto end;
2841 /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
2842 * storage, not a stream. For now, disallow it.
2844 if (grfFlags & PROPSETFLAG_NONSIMPLE)
2846 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2847 r = STG_E_INVALIDFLAG;
2848 goto end;
2851 r = FmtIdToPropStgName(rfmtid, name);
2852 if (FAILED(r))
2853 goto end;
2855 r = IStorage_CreateStream( &This->base.IStorage_iface, name, grfMode, 0, 0, &stm );
2856 if (FAILED(r))
2857 goto end;
2859 r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg);
2861 IStream_Release( stm );
2863 end:
2864 TRACE("returning 0x%08x\n", r);
2865 return r;
2868 /************************************************************************
2869 * IPropertySetStorage_fnOpen (IPropertySetStorage)
2871 static HRESULT WINAPI IPropertySetStorage_fnOpen(
2872 IPropertySetStorage *ppstg,
2873 REFFMTID rfmtid,
2874 DWORD grfMode,
2875 IPropertyStorage** ppprstg)
2877 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2878 IStream *stm = NULL;
2879 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2880 HRESULT r;
2882 TRACE("%p %s %08x %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
2884 /* be picky */
2885 if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
2886 grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
2888 r = STG_E_INVALIDFLAG;
2889 goto end;
2892 if (!rfmtid)
2894 r = E_INVALIDARG;
2895 goto end;
2898 r = FmtIdToPropStgName(rfmtid, name);
2899 if (FAILED(r))
2900 goto end;
2902 r = IStorage_OpenStream( &This->base.IStorage_iface, name, 0, grfMode, 0, &stm );
2903 if (FAILED(r))
2904 goto end;
2906 r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg);
2908 IStream_Release( stm );
2910 end:
2911 TRACE("returning 0x%08x\n", r);
2912 return r;
2915 /************************************************************************
2916 * IPropertySetStorage_fnDelete (IPropertySetStorage)
2918 static HRESULT WINAPI IPropertySetStorage_fnDelete(
2919 IPropertySetStorage *ppstg,
2920 REFFMTID rfmtid)
2922 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2923 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2924 HRESULT r;
2926 TRACE("%p %s\n", This, debugstr_guid(rfmtid));
2928 if (!rfmtid)
2929 return E_INVALIDARG;
2931 r = FmtIdToPropStgName(rfmtid, name);
2932 if (FAILED(r))
2933 return r;
2935 return IStorage_DestroyElement(&This->base.IStorage_iface, name);
2938 static HRESULT WINAPI IPropertySetStorage_fnEnum(IPropertySetStorage *iface, IEnumSTATPROPSETSTG **enum_obj)
2940 StorageImpl *storage = impl_from_IPropertySetStorage(iface);
2942 TRACE("%p, %p.\n", iface, enum_obj);
2944 if (!enum_obj)
2945 return E_INVALIDARG;
2947 return create_enum_stat_propset_stg(storage, enum_obj);
2950 /***********************************************************************
2951 * vtables
2953 const IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
2955 IPropertySetStorage_fnQueryInterface,
2956 IPropertySetStorage_fnAddRef,
2957 IPropertySetStorage_fnRelease,
2958 IPropertySetStorage_fnCreate,
2959 IPropertySetStorage_fnOpen,
2960 IPropertySetStorage_fnDelete,
2961 IPropertySetStorage_fnEnum
2964 static const IPropertyStorageVtbl IPropertyStorage_Vtbl =
2966 IPropertyStorage_fnQueryInterface,
2967 IPropertyStorage_fnAddRef,
2968 IPropertyStorage_fnRelease,
2969 IPropertyStorage_fnReadMultiple,
2970 IPropertyStorage_fnWriteMultiple,
2971 IPropertyStorage_fnDeleteMultiple,
2972 IPropertyStorage_fnReadPropertyNames,
2973 IPropertyStorage_fnWritePropertyNames,
2974 IPropertyStorage_fnDeletePropertyNames,
2975 IPropertyStorage_fnCommit,
2976 IPropertyStorage_fnRevert,
2977 IPropertyStorage_fnEnum,
2978 IPropertyStorage_fnSetTimes,
2979 IPropertyStorage_fnSetClass,
2980 IPropertyStorage_fnStat,
2983 /***********************************************************************
2984 * Format ID <-> name conversion
2986 static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
2987 'I','n','f','o','r','m','a','t','i','o','n',0 };
2988 static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t',
2989 'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
2991 #define BITS_PER_BYTE 8
2992 #define CHARMASK 0x1f
2993 #define BITS_IN_CHARMASK 5
2994 #define NUM_ALPHA_CHARS 26
2996 /***********************************************************************
2997 * FmtIdToPropStgName [ole32.@]
2998 * Returns the storage name of the format ID rfmtid.
2999 * PARAMS
3000 * rfmtid [I] Format ID for which to return a storage name
3001 * str [O] Storage name associated with rfmtid.
3003 * RETURNS
3004 * E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise.
3006 * NOTES
3007 * str must be at least CCH_MAX_PROPSTG_NAME characters in length.
3009 HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str)
3011 static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
3013 TRACE("%s, %p\n", debugstr_guid(rfmtid), str);
3015 if (!rfmtid) return E_INVALIDARG;
3016 if (!str) return E_INVALIDARG;
3018 if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
3019 lstrcpyW(str, szSummaryInfo);
3020 else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
3021 lstrcpyW(str, szDocSummaryInfo);
3022 else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid))
3023 lstrcpyW(str, szDocSummaryInfo);
3024 else
3026 const BYTE *fmtptr;
3027 WCHAR *pstr = str;
3028 ULONG bitsRemaining = BITS_PER_BYTE;
3030 *pstr++ = 5;
3031 for (fmtptr = (const BYTE *)rfmtid; fmtptr < (const BYTE *)rfmtid + sizeof(FMTID); )
3033 ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
3035 if (bitsRemaining >= BITS_IN_CHARMASK)
3037 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
3038 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
3039 *pstr <= 'z')
3040 *pstr += 'A' - 'a';
3041 pstr++;
3042 bitsRemaining -= BITS_IN_CHARMASK;
3043 if (bitsRemaining == 0)
3045 fmtptr++;
3046 bitsRemaining = BITS_PER_BYTE;
3049 else
3051 if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID))
3052 i |= *fmtptr << bitsRemaining;
3053 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
3054 bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK;
3057 *pstr = 0;
3059 TRACE("returning %s\n", debugstr_w(str));
3060 return S_OK;
3063 /***********************************************************************
3064 * PropStgNameToFmtId [ole32.@]
3065 * Returns the format ID corresponding to the given name.
3066 * PARAMS
3067 * str [I] Storage name to convert to a format ID.
3068 * rfmtid [O] Format ID corresponding to str.
3070 * RETURNS
3071 * E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to
3072 * a format ID, S_OK otherwise.
3074 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid)
3076 HRESULT hr = STG_E_INVALIDNAME;
3078 TRACE("%s, %p\n", debugstr_w(str), rfmtid);
3080 if (!rfmtid) return E_INVALIDARG;
3081 if (!str) return STG_E_INVALIDNAME;
3083 if (!lstrcmpiW(str, szDocSummaryInfo))
3085 *rfmtid = FMTID_DocSummaryInformation;
3086 hr = S_OK;
3088 else if (!lstrcmpiW(str, szSummaryInfo))
3090 *rfmtid = FMTID_SummaryInformation;
3091 hr = S_OK;
3093 else
3095 ULONG bits;
3096 BYTE *fmtptr = (BYTE *)rfmtid - 1;
3097 const WCHAR *pstr = str;
3099 memset(rfmtid, 0, sizeof(*rfmtid));
3100 for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE;
3101 bits += BITS_IN_CHARMASK)
3103 ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored;
3104 WCHAR wc;
3106 if (bitsUsed == 0)
3107 fmtptr++;
3108 wc = *++pstr - 'A';
3109 if (wc > NUM_ALPHA_CHARS)
3111 wc += 'A' - 'a';
3112 if (wc > NUM_ALPHA_CHARS)
3114 wc += 'a' - '0' + NUM_ALPHA_CHARS;
3115 if (wc > CHARMASK)
3117 WARN("invalid character (%d)\n", *pstr);
3118 goto end;
3122 *fmtptr |= wc << bitsUsed;
3123 bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK);
3124 if (bitsStored < BITS_IN_CHARMASK)
3126 wc >>= BITS_PER_BYTE - bitsUsed;
3127 if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE)
3129 if (wc != 0)
3131 WARN("extra bits\n");
3132 goto end;
3134 break;
3136 fmtptr++;
3137 *fmtptr |= (BYTE)wc;
3140 hr = S_OK;
3142 end:
3143 return hr;
3146 #ifdef __i386__ /* thiscall functions are i386-specific */
3148 #define DEFINE_STDCALL_WRAPPER(num,func,args) \
3149 __ASM_STDCALL_FUNC(func, args, \
3150 "popl %eax\n\t" \
3151 "popl %ecx\n\t" \
3152 "pushl %eax\n\t" \
3153 "movl (%ecx), %eax\n\t" \
3154 "jmp *(4*(" #num "))(%eax)" )
3156 DEFINE_STDCALL_WRAPPER(0,Allocate_PMemoryAllocator,8)
3157 extern void* WINAPI Allocate_PMemoryAllocator(void *this, ULONG cbSize);
3159 #else
3161 static void* WINAPI Allocate_PMemoryAllocator(void *this, ULONG cbSize)
3163 void* (WINAPI *fn)(void*,ULONG) = **(void***)this;
3164 return fn(this, cbSize);
3167 #endif
3169 BOOLEAN WINAPI StgConvertPropertyToVariant(const SERIALIZEDPROPERTYVALUE* prop,
3170 USHORT CodePage, PROPVARIANT* pvar, void* pma)
3172 struct read_buffer read_buffer;
3173 HRESULT hr;
3175 read_buffer.data = (BYTE *)prop;
3176 read_buffer.size = ~(size_t)0;
3177 hr = PropertyStorage_ReadProperty(pvar, &read_buffer, 0, CodePage, Allocate_PMemoryAllocator, pma);
3179 if (FAILED(hr))
3181 FIXME("should raise C++ exception on failure\n");
3182 PropVariantInit(pvar);
3185 return FALSE;
3188 SERIALIZEDPROPERTYVALUE* WINAPI StgConvertVariantToProperty(const PROPVARIANT *pvar,
3189 USHORT CodePage, SERIALIZEDPROPERTYVALUE *pprop, ULONG *pcb, PROPID pid,
3190 BOOLEAN fReserved, ULONG *pcIndirect)
3192 FIXME("%p,%d,%p,%p,%d,%d,%p\n", pvar, CodePage, pprop, pcb, pid, fReserved, pcIndirect);
3194 return NULL;
3197 HRESULT WINAPI StgCreatePropStg(IUnknown *unk, REFFMTID fmt, const CLSID *clsid,
3198 DWORD flags, DWORD reserved, IPropertyStorage **prop_stg)
3200 IStorage *stg;
3201 IStream *stm;
3202 HRESULT r;
3204 TRACE("%p %s %s %08x %d %p\n", unk, debugstr_guid(fmt), debugstr_guid(clsid), flags, reserved, prop_stg);
3206 if (!fmt || reserved)
3208 r = E_INVALIDARG;
3209 goto end;
3212 if (flags & PROPSETFLAG_NONSIMPLE)
3214 r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
3215 if (FAILED(r))
3216 goto end;
3218 /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to create a
3219 * storage, not a stream. For now, disallow it.
3221 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
3222 IStorage_Release(stg);
3223 r = STG_E_INVALIDFLAG;
3225 else
3227 r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
3228 if (FAILED(r))
3229 goto end;
3231 r = PropertyStorage_ConstructEmpty(stm, fmt, flags,
3232 STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
3234 IStream_Release( stm );
3237 end:
3238 TRACE("returning 0x%08x\n", r);
3239 return r;
3242 HRESULT WINAPI StgOpenPropStg(IUnknown *unk, REFFMTID fmt, DWORD flags,
3243 DWORD reserved, IPropertyStorage **prop_stg)
3245 IStorage *stg;
3246 IStream *stm;
3247 HRESULT r;
3249 TRACE("%p %s %08x %d %p\n", unk, debugstr_guid(fmt), flags, reserved, prop_stg);
3251 if (!fmt || reserved)
3253 r = E_INVALIDARG;
3254 goto end;
3257 if (flags & PROPSETFLAG_NONSIMPLE)
3259 r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
3260 if (FAILED(r))
3261 goto end;
3263 /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to open a
3264 * storage, not a stream. For now, disallow it.
3266 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
3267 IStorage_Release(stg);
3268 r = STG_E_INVALIDFLAG;
3270 else
3272 r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
3273 if (FAILED(r))
3274 goto end;
3276 r = PropertyStorage_ConstructFromStream(stm, fmt,
3277 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
3279 IStream_Release( stm );
3282 end:
3283 TRACE("returning 0x%08x\n", r);
3284 return r;