ws2_32: Explicitly return WSAENOTSOCK for a file handle in getsockopt() except SO_OPE...
[wine.git] / dlls / ole32 / stg_prop.c
blobcd30dad8c992236c062bb5a39d0dba3b50e98e78
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].iVal = This->codePage;
515 break;
516 case PID_LOCALE:
517 rgpropvar[i].vt = VT_I4;
518 rgpropvar[i].lVal = This->locale;
519 break;
520 default:
522 PROPVARIANT *prop = PropertyStorage_FindProperty(This,
523 rgpspec[i].u.propid);
525 if (prop)
526 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop,
527 GetACP(), This->codePage);
528 else
529 hr = S_FALSE;
534 LeaveCriticalSection(&This->cs);
535 return hr;
538 static HRESULT PropertyStorage_StringCopy(LPCSTR src, UINT srcCP, LPSTR *dst, UINT dstCP)
540 HRESULT hr = S_OK;
541 int len;
543 TRACE("%s, %p, %d, %d\n",
544 srcCP == CP_UNICODE ? debugstr_w((LPCWSTR)src) : debugstr_a(src), dst,
545 dstCP, srcCP);
546 assert(src);
547 assert(dst);
548 *dst = NULL;
549 if (dstCP == srcCP)
551 size_t len;
553 if (dstCP == CP_UNICODE)
554 len = (lstrlenW((LPCWSTR)src) + 1) * sizeof(WCHAR);
555 else
556 len = strlen(src) + 1;
557 *dst = CoTaskMemAlloc(len);
558 if (!*dst)
559 hr = STG_E_INSUFFICIENTMEMORY;
560 else
561 memcpy(*dst, src, len);
563 else
565 if (dstCP == CP_UNICODE)
567 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
568 *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
569 if (!*dst)
570 hr = STG_E_INSUFFICIENTMEMORY;
571 else
572 MultiByteToWideChar(srcCP, 0, src, -1, (LPWSTR)*dst, len);
574 else
576 LPCWSTR wideStr = NULL;
577 LPWSTR wideStr_tmp = NULL;
579 if (srcCP == CP_UNICODE)
580 wideStr = (LPCWSTR)src;
581 else
583 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
584 wideStr_tmp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
585 if (wideStr_tmp)
587 MultiByteToWideChar(srcCP, 0, src, -1, wideStr_tmp, len);
588 wideStr = wideStr_tmp;
590 else
591 hr = STG_E_INSUFFICIENTMEMORY;
593 if (SUCCEEDED(hr))
595 len = WideCharToMultiByte(dstCP, 0, wideStr, -1, NULL, 0,
596 NULL, NULL);
597 *dst = CoTaskMemAlloc(len);
598 if (!*dst)
599 hr = STG_E_INSUFFICIENTMEMORY;
600 else
602 BOOL defCharUsed = FALSE;
604 if (WideCharToMultiByte(dstCP, 0, wideStr, -1, *dst, len,
605 NULL, &defCharUsed) == 0 || defCharUsed)
607 CoTaskMemFree(*dst);
608 *dst = NULL;
609 hr = HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION);
613 HeapFree(GetProcessHeap(), 0, wideStr_tmp);
616 TRACE("returning 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->pszVal, srcCP, &prop->pszVal, targetCP);
633 if (SUCCEEDED(hr))
634 prop->vt = VT_LPSTR;
635 break;
636 case VT_BSTR:
637 if ((prop->bstrVal = SysAllocStringLen(propvar->bstrVal, SysStringLen(propvar->bstrVal))))
638 prop->vt = VT_BSTR;
639 else
640 hr = E_OUTOFMEMORY;
641 break;
642 default:
643 hr = PropVariantCopy(prop, propvar);
646 return hr;
649 /* Stores the property with id propid and value propvar into this property
650 * storage. lcid is ignored if propvar's type is not VT_LPSTR. If propvar's
651 * type is VT_LPSTR, converts the string using lcid as the source code page
652 * and This->codePage as the target code page before storing.
653 * As a side effect, may change This->format to 1 if the type of propvar is
654 * a version 1-only property.
656 static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
657 PROPID propid, const PROPVARIANT *propvar, UINT cp)
659 HRESULT hr = S_OK;
660 PROPVARIANT *prop = PropertyStorage_FindProperty(This, propid);
662 assert(propvar);
663 if (propvar->vt & VT_BYREF || propvar->vt & VT_ARRAY)
664 This->format = 1;
665 switch (propvar->vt)
667 case VT_DECIMAL:
668 case VT_I1:
669 case VT_INT:
670 case VT_UINT:
671 case VT_VECTOR|VT_I1:
672 This->format = 1;
674 TRACE("Setting 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].iVal;
806 if (This->codePage == CP_UNICODE)
807 This->grfFlags &= ~PROPSETFLAG_ANSI;
808 else
809 This->grfFlags |= PROPSETFLAG_ANSI;
811 else
812 hr = STG_E_INVALIDPARAMETER;
813 break;
814 case PID_LOCALE:
815 /* Can only set the locale if nothing else has been set */
816 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
817 rgpropvar[i].vt == VT_I4)
818 This->locale = rgpropvar[i].lVal;
819 else
820 hr = STG_E_INVALIDPARAMETER;
821 break;
822 case PID_ILLEGAL:
823 /* silently ignore like MSDN says */
824 break;
825 default:
826 if (rgpspec[i].u.propid >= PID_MIN_READONLY)
827 hr = STG_E_INVALIDPARAMETER;
828 else
829 hr = PropertyStorage_StorePropWithId(This,
830 rgpspec[i].u.propid, &rgpropvar[i], GetACP());
834 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
835 IPropertyStorage_Commit(iface, STGC_DEFAULT);
836 LeaveCriticalSection(&This->cs);
837 return hr;
840 /************************************************************************
841 * IPropertyStorage_fnDeleteMultiple (IPropertyStorage)
843 static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
844 IPropertyStorage* iface,
845 ULONG cpspec,
846 const PROPSPEC rgpspec[])
848 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
849 ULONG i;
850 HRESULT hr;
852 TRACE("(%p, %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->cVal);
1261 TRACE("Read char 0x%x ('%c')\n", prop->cVal, prop->cVal);
1262 break;
1263 case VT_UI1:
1264 hr = buffer_read_byte(buffer, offset, &prop->bVal);
1265 TRACE("Read byte 0x%x\n", prop->bVal);
1266 break;
1267 case VT_BOOL:
1268 hr = buffer_read_word(buffer, offset, (WORD *)&prop->boolVal);
1269 TRACE("Read bool %d\n", prop->boolVal);
1270 break;
1271 case VT_I2:
1272 hr = buffer_read_word(buffer, offset, (WORD *)&prop->iVal);
1273 TRACE("Read short %d\n", prop->iVal);
1274 break;
1275 case VT_UI2:
1276 hr = buffer_read_word(buffer, offset, &prop->uiVal);
1277 TRACE("Read ushort %d\n", prop->uiVal);
1278 break;
1279 case VT_INT:
1280 case VT_I4:
1281 hr = buffer_read_dword(buffer, offset, (DWORD *)&prop->lVal);
1282 TRACE("Read long %d\n", prop->lVal);
1283 break;
1284 case VT_UINT:
1285 case VT_UI4:
1286 hr = buffer_read_dword(buffer, offset, &prop->ulVal);
1287 TRACE("Read ulong %d\n", prop->ulVal);
1288 break;
1289 case VT_I8:
1290 hr = buffer_read_uint64(buffer, offset, (ULARGE_INTEGER *)&prop->hVal);
1291 TRACE("Read long long %s\n", wine_dbgstr_longlong(prop->hVal.QuadPart));
1292 break;
1293 case VT_UI8:
1294 hr = buffer_read_uint64(buffer, offset, &prop->uhVal);
1295 TRACE("Read ulong long %s\n", wine_dbgstr_longlong(prop->uhVal.QuadPart));
1296 break;
1297 case VT_R8:
1298 hr = buffer_read_len(buffer, offset, &prop->dblVal, sizeof(prop->dblVal));
1299 TRACE("Read double %f\n", prop->dblVal);
1300 break;
1301 case VT_LPSTR:
1303 DWORD count;
1305 if (FAILED(hr = buffer_read_dword(buffer, offset, &count)))
1306 break;
1308 offset += sizeof(DWORD);
1310 if (codepage == CP_UNICODE && count % sizeof(WCHAR))
1312 WARN("Unicode string has odd number of bytes\n");
1313 hr = STG_E_INVALIDHEADER;
1315 else
1317 prop->pszVal = allocate(allocate_data, count);
1318 if (prop->pszVal)
1320 if (FAILED(hr = buffer_read_len(buffer, offset, prop->pszVal, count)))
1321 break;
1323 /* This is stored in the code page specified in codepage.
1324 * Don't convert it, the caller will just store it as-is.
1326 if (codepage == CP_UNICODE)
1328 /* Make sure it's NULL-terminated */
1329 prop->pszVal[count / sizeof(WCHAR) - 1] = '\0';
1330 TRACE("Read string value %s\n",
1331 debugstr_w(prop->pwszVal));
1333 else
1335 /* Make sure it's NULL-terminated */
1336 prop->pszVal[count - 1] = '\0';
1337 TRACE("Read string value %s\n", debugstr_a(prop->pszVal));
1340 else
1341 hr = STG_E_INSUFFICIENTMEMORY;
1343 break;
1345 case VT_BSTR:
1347 DWORD count, wcount;
1349 if (FAILED(hr = buffer_read_dword(buffer, offset, &count)))
1350 break;
1352 offset += sizeof(DWORD);
1354 if (codepage == CP_UNICODE && count % sizeof(WCHAR))
1356 WARN("Unicode string has odd number of bytes\n");
1357 hr = STG_E_INVALIDHEADER;
1359 else
1361 if (codepage == CP_UNICODE)
1362 wcount = count / sizeof(WCHAR);
1363 else
1365 if (FAILED(hr = buffer_test_offset(buffer, offset, count)))
1366 break;
1367 wcount = MultiByteToWideChar(codepage, 0, (LPCSTR)(buffer->data + offset), count, NULL, 0);
1370 prop->bstrVal = SysAllocStringLen(NULL, wcount); /* FIXME: use allocator? */
1372 if (prop->bstrVal)
1374 if (codepage == CP_UNICODE)
1375 hr = buffer_read_len(buffer, offset, prop->bstrVal, count);
1376 else
1377 MultiByteToWideChar(codepage, 0, (LPCSTR)(buffer->data + offset), count, prop->bstrVal, wcount);
1379 prop->bstrVal[wcount - 1] = '\0';
1380 TRACE("Read string value %s\n", debugstr_w(prop->bstrVal));
1382 else
1383 hr = STG_E_INSUFFICIENTMEMORY;
1385 break;
1387 case VT_BLOB:
1389 DWORD count;
1391 if (FAILED(hr = buffer_read_dword(buffer, offset, &count)))
1392 break;
1394 offset += sizeof(DWORD);
1396 prop->blob.cbSize = count;
1397 prop->blob.pBlobData = allocate(allocate_data, count);
1398 if (prop->blob.pBlobData)
1400 hr = buffer_read_len(buffer, offset, prop->blob.pBlobData, count);
1401 TRACE("Read blob value of size %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->pwszVal = allocate(allocate_data, count * sizeof(WCHAR));
1417 if (prop->pwszVal)
1419 if (SUCCEEDED(hr = buffer_read_len(buffer, offset, prop->pwszVal, count * sizeof(WCHAR))))
1421 /* make sure string is NULL-terminated */
1422 prop->pwszVal[count - 1] = '\0';
1423 PropertyStorage_ByteSwapString(prop->pwszVal, count);
1424 TRACE("Read string value %s\n", debugstr_w(prop->pwszVal));
1427 else
1428 hr = STG_E_INSUFFICIENTMEMORY;
1429 break;
1431 case VT_FILETIME:
1432 hr = buffer_read_uint64(buffer, offset, (ULARGE_INTEGER *)&prop->filetime);
1433 break;
1434 case VT_CF:
1436 DWORD len = 0, tag = 0;
1438 if (SUCCEEDED(hr = buffer_read_dword(buffer, offset, &len)))
1439 hr = buffer_read_dword(buffer, offset + sizeof(DWORD), &tag);
1440 if (FAILED(hr))
1441 break;
1443 offset += 2 * sizeof(DWORD);
1445 if (len > 8)
1447 len -= 8;
1448 prop->pclipdata = allocate(allocate_data, sizeof (CLIPDATA));
1449 prop->pclipdata->cbSize = len;
1450 prop->pclipdata->ulClipFmt = tag;
1451 prop->pclipdata->pClipData = allocate(allocate_data, len - sizeof(prop->pclipdata->ulClipFmt));
1452 hr = buffer_read_len(buffer, offset, prop->pclipdata->pClipData, len - sizeof(prop->pclipdata->ulClipFmt));
1454 else
1455 hr = STG_E_INVALIDPARAMETER;
1457 break;
1458 case VT_CLSID:
1459 if (!(prop->puuid = allocate(allocate_data, sizeof (*prop->puuid))))
1460 return STG_E_INSUFFICIENTMEMORY;
1462 if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*prop->puuid))))
1463 StorageUtl_ReadGUID(buffer->data, offset, prop->puuid);
1465 break;
1466 default:
1467 FIXME("unsupported type %d\n", prop->vt);
1468 hr = STG_E_INVALIDPARAMETER;
1471 return hr;
1474 static size_t propertystorage_get_elemsize(const PROPVARIANT *prop)
1476 if (!(prop->vt & VT_VECTOR))
1477 return 0;
1479 switch (prop->vt & ~VT_VECTOR)
1481 case VT_I1: return sizeof(*prop->cac.pElems);
1482 case VT_UI1: return sizeof(*prop->caub.pElems);
1483 case VT_I2: return sizeof(*prop->cai.pElems);
1484 case VT_UI2: return sizeof(*prop->caui.pElems);
1485 case VT_BOOL: return sizeof(*prop->cabool.pElems);
1486 case VT_I4: return sizeof(*prop->cal.pElems);
1487 case VT_UI4: return sizeof(*prop->caul.pElems);
1488 case VT_R4: return sizeof(*prop->caflt.pElems);
1489 case VT_ERROR: return sizeof(*prop->cascode.pElems);
1490 case VT_I8: return sizeof(*prop->cah.pElems);
1491 case VT_UI8: return sizeof(*prop->cauh.pElems);
1492 case VT_R8: return sizeof(*prop->cadbl.pElems);
1493 case VT_CY: return sizeof(*prop->cacy.pElems);
1494 case VT_DATE: return sizeof(*prop->cadate.pElems);
1495 case VT_FILETIME: return sizeof(*prop->cafiletime.pElems);
1496 case VT_CLSID: return sizeof(*prop->cauuid.pElems);
1497 case VT_VARIANT: return sizeof(*prop->capropvar.pElems);
1498 default:
1499 FIXME("Unhandled type %#x.\n", prop->vt);
1500 return 0;
1504 static HRESULT PropertyStorage_ReadProperty(PROPVARIANT *prop, const struct read_buffer *buffer,
1505 size_t offset, UINT codepage, void* (WINAPI *allocate)(void *this, ULONG size), void *allocate_data)
1507 HRESULT hr;
1508 DWORD vt;
1510 assert(prop);
1511 assert(buffer->data);
1513 if (FAILED(hr = buffer_read_dword(buffer, offset, &vt)))
1514 return hr;
1516 offset += sizeof(DWORD);
1517 prop->vt = vt;
1519 if (prop->vt & VT_VECTOR)
1521 DWORD count, i;
1523 switch (prop->vt & VT_VECTOR)
1525 case VT_BSTR:
1526 case VT_VARIANT:
1527 case VT_LPSTR:
1528 case VT_LPWSTR:
1529 case VT_CF:
1530 FIXME("Vector with variable length elements are not supported.\n");
1531 return STG_E_INVALIDPARAMETER;
1532 default:
1536 if (SUCCEEDED(hr = buffer_read_dword(buffer, offset, &count)))
1538 size_t elemsize = propertystorage_get_elemsize(prop);
1539 PROPVARIANT elem;
1541 offset += sizeof(DWORD);
1543 if ((prop->capropvar.pElems = allocate(allocate_data, elemsize * count)))
1545 prop->capropvar.cElems = count;
1546 elem.vt = prop->vt & ~VT_VECTOR;
1548 for (i = 0; i < count; ++i)
1550 if (SUCCEEDED(hr = propertystorage_read_scalar(&elem, buffer, offset + i * elemsize, codepage,
1551 allocate, allocate_data)))
1553 memcpy(&prop->capropvar.pElems[i], &elem.lVal, elemsize);
1557 else
1558 hr = STG_E_INSUFFICIENTMEMORY;
1561 else if (prop->vt & VT_ARRAY)
1563 FIXME("VT_ARRAY properties are not supported.\n");
1564 hr = STG_E_INVALIDPARAMETER;
1566 else
1567 hr = propertystorage_read_scalar(prop, buffer, offset, codepage, allocate, allocate_data);
1569 return hr;
1572 static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
1573 PROPERTYSETHEADER *hdr)
1575 BYTE buf[sizeof(PROPERTYSETHEADER)];
1576 ULONG count = 0;
1577 HRESULT hr;
1579 assert(stm);
1580 assert(hdr);
1581 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1582 if (SUCCEEDED(hr))
1584 if (count != sizeof(buf))
1586 WARN("read only %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.iVal;
1874 break;
1875 case PID_LOCALE:
1876 if (prop.vt == VT_I4)
1877 This->locale = (LCID)prop.lVal;
1878 break;
1879 case PID_BEHAVIOR:
1880 if (prop.vt == VT_I4 && prop.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->cVal, sizeof(var->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->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->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->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->pwszVal) + 1) * sizeof(WCHAR);
2176 else
2177 len = lstrlenA(var->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->pszVal, len, &count);
2183 bytesWritten = count + sizeof(DWORD);
2184 break;
2186 case VT_BSTR:
2188 if (This->codePage == CP_UNICODE)
2190 len = SysStringByteLen(var->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->bstrVal, len, &count);
2196 else
2198 char *str;
2200 len = WideCharToMultiByte(This->codePage, 0, var->bstrVal, SysStringLen(var->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->bstrVal, SysStringLen(var->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->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->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->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->pclipdata->cbSize;
2249 StorageUtl_WriteDWord(&cf_hdr[0], 0, len + 8);
2250 StorageUtl_WriteDWord(&cf_hdr[1], 0, var->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->pclipdata->pClipData,
2255 len - sizeof(var->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->puuid);
2266 hr = IStream_Write(This->stm, &temp, sizeof(temp), &count);
2267 bytesWritten = count;
2268 break;
2270 case VT_BLOB:
2272 StorageUtl_WriteDWord(&dwTemp, 0, var->blob.cbSize);
2273 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2274 if (FAILED(hr))
2275 goto end;
2276 hr = IStream_Write(This->stm, var->blob.pBlobData, var->blob.cbSize, &count);
2277 bytesWritten = count + sizeof(DWORD);
2278 break;
2280 default:
2281 FIXME("unsupported type: %d\n", var->vt);
2282 return STG_E_INVALIDPARAMETER;
2285 if (SUCCEEDED(hr))
2287 *sectionOffset += bytesWritten;
2288 if (bytesWritten % sizeof(DWORD))
2290 DWORD padding = sizeof(DWORD) - bytesWritten % sizeof(DWORD);
2291 TRACE("adding %d bytes of padding\n", padding);
2292 *sectionOffset += padding;
2296 end:
2297 return hr;
2300 struct PropertyClosure
2302 HRESULT hr;
2303 DWORD propNum;
2304 DWORD *sectionOffset;
2307 static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value,
2308 void *extra, void *closure)
2310 PropertyStorage_impl *This = extra;
2311 struct PropertyClosure *c = closure;
2313 assert(key);
2314 assert(value);
2315 assert(extra);
2316 assert(closure);
2317 c->hr = PropertyStorage_WritePropertyToStream(This, c->propNum++,
2318 PtrToUlong(key), value, c->sectionOffset);
2319 return SUCCEEDED(c->hr);
2322 static HRESULT PropertyStorage_WritePropertiesToStream(
2323 PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset)
2325 struct PropertyClosure closure;
2327 assert(sectionOffset);
2328 closure.hr = S_OK;
2329 closure.propNum = startingPropNum;
2330 closure.sectionOffset = sectionOffset;
2331 dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter,
2332 &closure);
2333 return closure.hr;
2336 static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This)
2338 HRESULT hr;
2339 ULONG count = 0;
2340 LARGE_INTEGER seek = { {0} };
2341 PROPERTYSETHEADER hdr;
2342 FORMATIDOFFSET fmtOffset;
2344 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2345 if (FAILED(hr))
2346 goto end;
2347 PropertyStorage_MakeHeader(This, &hdr);
2348 hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count);
2349 if (FAILED(hr))
2350 goto end;
2351 if (count != sizeof(hdr))
2353 hr = STG_E_WRITEFAULT;
2354 goto end;
2357 PropertyStorage_MakeFmtIdOffset(This, &fmtOffset);
2358 hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count);
2359 if (FAILED(hr))
2360 goto end;
2361 if (count != sizeof(fmtOffset))
2363 hr = STG_E_WRITEFAULT;
2364 goto end;
2366 hr = S_OK;
2368 end:
2369 return hr;
2372 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This)
2374 PROPERTYSECTIONHEADER sectionHdr;
2375 HRESULT hr;
2376 ULONG count;
2377 LARGE_INTEGER seek;
2378 DWORD numProps, prop, sectionOffset, dwTemp;
2379 PROPVARIANT var;
2381 PropertyStorage_WriteHeadersToStream(This);
2383 /* Count properties. Always at least one property, the code page */
2384 numProps = 1;
2385 if (dictionary_num_entries(This->name_to_propid))
2386 numProps++;
2387 if (This->locale != LOCALE_SYSTEM_DEFAULT)
2388 numProps++;
2389 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2390 numProps++;
2391 numProps += dictionary_num_entries(This->propid_to_prop);
2393 /* Write section header with 0 bytes right now, I'll adjust it after
2394 * writing properties.
2396 PropertyStorage_MakeSectionHdr(0, numProps, &sectionHdr);
2397 seek.QuadPart = SECTIONHEADER_OFFSET;
2398 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2399 if (FAILED(hr))
2400 goto end;
2401 hr = IStream_Write(This->stm, &sectionHdr, sizeof(sectionHdr), &count);
2402 if (FAILED(hr))
2403 goto end;
2405 prop = 0;
2406 sectionOffset = sizeof(PROPERTYSECTIONHEADER) +
2407 numProps * sizeof(PROPERTYIDOFFSET);
2409 if (dictionary_num_entries(This->name_to_propid))
2411 prop++;
2412 hr = PropertyStorage_WriteDictionaryToStream(This, &sectionOffset);
2413 if (FAILED(hr))
2414 goto end;
2417 PropVariantInit(&var);
2419 var.vt = VT_I2;
2420 var.iVal = This->codePage;
2421 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE,
2422 &var, &sectionOffset);
2423 if (FAILED(hr))
2424 goto end;
2426 if (This->locale != LOCALE_SYSTEM_DEFAULT)
2428 var.vt = VT_I4;
2429 var.lVal = This->locale;
2430 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE,
2431 &var, &sectionOffset);
2432 if (FAILED(hr))
2433 goto end;
2436 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2438 var.vt = VT_I4;
2439 var.lVal = 1;
2440 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR,
2441 &var, &sectionOffset);
2442 if (FAILED(hr))
2443 goto end;
2446 hr = PropertyStorage_WritePropertiesToStream(This, prop, &sectionOffset);
2447 if (FAILED(hr))
2448 goto end;
2450 /* Now write the byte count of the section */
2451 seek.QuadPart = SECTIONHEADER_OFFSET;
2452 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2453 if (FAILED(hr))
2454 goto end;
2455 StorageUtl_WriteDWord(&dwTemp, 0, sectionOffset);
2456 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2458 end:
2459 return hr;
2462 /***********************************************************************
2463 * PropertyStorage_Construct
2465 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This)
2467 dictionary_destroy(This->name_to_propid);
2468 This->name_to_propid = NULL;
2469 dictionary_destroy(This->propid_to_name);
2470 This->propid_to_name = NULL;
2471 dictionary_destroy(This->propid_to_prop);
2472 This->propid_to_prop = NULL;
2475 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This)
2477 HRESULT hr = S_OK;
2479 This->name_to_propid = dictionary_create(
2480 PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy,
2481 This);
2482 if (!This->name_to_propid)
2484 hr = STG_E_INSUFFICIENTMEMORY;
2485 goto end;
2487 This->propid_to_name = dictionary_create(PropertyStorage_PropCompare,
2488 NULL, This);
2489 if (!This->propid_to_name)
2491 hr = STG_E_INSUFFICIENTMEMORY;
2492 goto end;
2494 This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
2495 PropertyStorage_PropertyDestroy, This);
2496 if (!This->propid_to_prop)
2498 hr = STG_E_INSUFFICIENTMEMORY;
2499 goto end;
2501 end:
2502 if (FAILED(hr))
2503 PropertyStorage_DestroyDictionaries(This);
2504 return hr;
2507 static HRESULT PropertyStorage_BaseConstruct(IStream *stm,
2508 REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps)
2510 HRESULT hr = S_OK;
2512 assert(pps);
2513 assert(rfmtid);
2514 *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps);
2515 if (!*pps)
2516 return E_OUTOFMEMORY;
2518 (*pps)->IPropertyStorage_iface.lpVtbl = &IPropertyStorage_Vtbl;
2519 (*pps)->ref = 1;
2520 InitializeCriticalSection(&(*pps)->cs);
2521 (*pps)->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PropertyStorage_impl.cs");
2522 (*pps)->stm = stm;
2523 (*pps)->fmtid = *rfmtid;
2524 (*pps)->grfMode = grfMode;
2526 hr = PropertyStorage_CreateDictionaries(*pps);
2527 if (FAILED(hr))
2529 (*pps)->cs.DebugInfo->Spare[0] = 0;
2530 DeleteCriticalSection(&(*pps)->cs);
2531 HeapFree(GetProcessHeap(), 0, *pps);
2532 *pps = NULL;
2534 else IStream_AddRef( stm );
2536 return hr;
2539 static HRESULT PropertyStorage_ConstructFromStream(IStream *stm,
2540 REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps)
2542 PropertyStorage_impl *ps;
2543 HRESULT hr;
2545 assert(pps);
2546 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2547 if (SUCCEEDED(hr))
2549 hr = PropertyStorage_ReadFromStream(ps);
2550 if (SUCCEEDED(hr))
2552 *pps = &ps->IPropertyStorage_iface;
2553 TRACE("PropertyStorage %p constructed\n", ps);
2554 hr = S_OK;
2556 else IPropertyStorage_Release( &ps->IPropertyStorage_iface );
2558 return hr;
2561 static HRESULT PropertyStorage_ConstructEmpty(IStream *stm,
2562 REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
2564 PropertyStorage_impl *ps;
2565 HRESULT hr;
2567 assert(pps);
2568 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2569 if (SUCCEEDED(hr))
2571 ps->format = 0;
2572 ps->grfFlags = grfFlags;
2573 if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2574 ps->format = 1;
2575 /* default to Unicode unless told not to, as specified on msdn */
2576 if (ps->grfFlags & PROPSETFLAG_ANSI)
2577 ps->codePage = GetACP();
2578 else
2579 ps->codePage = CP_UNICODE;
2580 ps->locale = LOCALE_SYSTEM_DEFAULT;
2581 TRACE("Code page is %d, locale is %d\n", ps->codePage, ps->locale);
2582 *pps = &ps->IPropertyStorage_iface;
2583 TRACE("PropertyStorage %p constructed\n", ps);
2584 hr = S_OK;
2586 return hr;
2590 /***********************************************************************
2591 * Implementation of IPropertySetStorage
2594 struct enum_stat_propset_stg
2596 IEnumSTATPROPSETSTG IEnumSTATPROPSETSTG_iface;
2597 LONG refcount;
2598 STATPROPSETSTG *stats;
2599 size_t current;
2600 size_t count;
2603 static struct enum_stat_propset_stg *impl_from_IEnumSTATPROPSETSTG(IEnumSTATPROPSETSTG *iface)
2605 return CONTAINING_RECORD(iface, struct enum_stat_propset_stg, IEnumSTATPROPSETSTG_iface);
2608 static HRESULT WINAPI enum_stat_propset_stg_QueryInterface(IEnumSTATPROPSETSTG *iface, REFIID riid, void **obj)
2610 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
2612 if (IsEqualIID(riid, &IID_IEnumSTATPROPSETSTG) ||
2613 IsEqualIID(riid, &IID_IUnknown))
2615 *obj = iface;
2616 IEnumSTATPROPSETSTG_AddRef(iface);
2617 return S_OK;
2620 WARN("Unsupported interface %s.\n", debugstr_guid(riid));
2621 return E_NOINTERFACE;
2624 static ULONG WINAPI enum_stat_propset_stg_AddRef(IEnumSTATPROPSETSTG *iface)
2626 struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface);
2627 LONG refcount = InterlockedIncrement(&psenum->refcount);
2629 TRACE("%p, refcount %u.\n", iface, refcount);
2631 return refcount;
2634 static ULONG WINAPI enum_stat_propset_stg_Release(IEnumSTATPROPSETSTG *iface)
2636 struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface);
2637 LONG refcount = InterlockedDecrement(&psenum->refcount);
2639 TRACE("%p, refcount %u.\n", iface, refcount);
2641 if (!refcount)
2643 heap_free(psenum->stats);
2644 heap_free(psenum);
2647 return refcount;
2650 static HRESULT WINAPI enum_stat_propset_stg_Next(IEnumSTATPROPSETSTG *iface, ULONG celt,
2651 STATPROPSETSTG *ret, ULONG *fetched)
2653 struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface);
2654 ULONG count = 0;
2656 TRACE("%p, %u, %p, %p.\n", iface, celt, ret, fetched);
2658 if (psenum->current == ~0u)
2659 psenum->current = 0;
2661 while (count < celt && psenum->current < psenum->count)
2662 ret[count++] = psenum->stats[psenum->current++];
2664 if (fetched)
2665 *fetched = count;
2667 return count < celt ? S_FALSE : S_OK;
2670 static HRESULT WINAPI enum_stat_propset_stg_Skip(IEnumSTATPROPSETSTG *iface, ULONG celt)
2672 FIXME("%p, %u.\n", iface, celt);
2674 return S_OK;
2677 static HRESULT WINAPI enum_stat_propset_stg_Reset(IEnumSTATPROPSETSTG *iface)
2679 struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface);
2681 TRACE("%p.\n", iface);
2683 psenum->current = ~0u;
2685 return S_OK;
2688 static HRESULT WINAPI enum_stat_propset_stg_Clone(IEnumSTATPROPSETSTG *iface, IEnumSTATPROPSETSTG **ppenum)
2690 FIXME("%p, %p.\n", iface, ppenum);
2692 return E_NOTIMPL;
2695 static const IEnumSTATPROPSETSTGVtbl enum_stat_propset_stg_vtbl =
2697 enum_stat_propset_stg_QueryInterface,
2698 enum_stat_propset_stg_AddRef,
2699 enum_stat_propset_stg_Release,
2700 enum_stat_propset_stg_Next,
2701 enum_stat_propset_stg_Skip,
2702 enum_stat_propset_stg_Reset,
2703 enum_stat_propset_stg_Clone,
2706 static HRESULT create_enum_stat_propset_stg(StorageImpl *storage, IEnumSTATPROPSETSTG **ret)
2708 IStorage *stg = &storage->base.IStorage_iface;
2709 IEnumSTATSTG *penum = NULL;
2710 STATSTG stat;
2711 ULONG count;
2712 HRESULT hr;
2714 struct enum_stat_propset_stg *enum_obj;
2716 enum_obj = heap_alloc_zero(sizeof(*enum_obj));
2717 if (!enum_obj)
2718 return E_OUTOFMEMORY;
2720 enum_obj->IEnumSTATPROPSETSTG_iface.lpVtbl = &enum_stat_propset_stg_vtbl;
2721 enum_obj->refcount = 1;
2723 /* add all the property set elements into a list */
2724 hr = IStorage_EnumElements(stg, 0, NULL, 0, &penum);
2725 if (FAILED(hr))
2726 goto done;
2728 /* Allocate stats array and fill it. */
2729 while ((hr = IEnumSTATSTG_Next(penum, 1, &stat, &count)) == S_OK)
2731 enum_obj->count++;
2732 CoTaskMemFree(stat.pwcsName);
2735 if (FAILED(hr))
2736 goto done;
2738 enum_obj->stats = heap_alloc(enum_obj->count * sizeof(*enum_obj->stats));
2739 if (!enum_obj->stats)
2741 hr = E_OUTOFMEMORY;
2742 goto done;
2744 enum_obj->count = 0;
2746 if (FAILED(hr = IEnumSTATSTG_Reset(penum)))
2747 goto done;
2749 while (IEnumSTATSTG_Next(penum, 1, &stat, &count) == S_OK)
2751 if (!stat.pwcsName)
2752 continue;
2754 if (stat.pwcsName[0] == 5 && stat.type == STGTY_STREAM)
2756 STATPROPSETSTG *ptr = &enum_obj->stats[enum_obj->count++];
2758 PropStgNameToFmtId(stat.pwcsName, &ptr->fmtid);
2760 TRACE("adding %s - %s.\n", debugstr_w(stat.pwcsName), debugstr_guid(&ptr->fmtid));
2762 ptr->mtime = stat.mtime;
2763 ptr->atime = stat.atime;
2764 ptr->ctime = stat.ctime;
2765 ptr->grfFlags = stat.grfMode;
2766 ptr->clsid = stat.clsid;
2768 CoTaskMemFree(stat.pwcsName);
2771 done:
2773 if (penum)
2774 IEnumSTATSTG_Release(penum);
2776 if (SUCCEEDED(hr))
2778 *ret = &enum_obj->IEnumSTATPROPSETSTG_iface;
2780 else
2782 *ret = NULL;
2783 IEnumSTATPROPSETSTG_Release(&enum_obj->IEnumSTATPROPSETSTG_iface);
2786 return hr;
2789 /************************************************************************
2790 * IPropertySetStorage_fnQueryInterface (IUnknown)
2792 * This method forwards to the common QueryInterface implementation
2794 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
2795 IPropertySetStorage *ppstg,
2796 REFIID riid,
2797 void** ppvObject)
2799 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2800 return IStorage_QueryInterface( &This->base.IStorage_iface, riid, ppvObject );
2803 /************************************************************************
2804 * IPropertySetStorage_fnAddRef (IUnknown)
2806 * This method forwards to the common AddRef implementation
2808 static ULONG WINAPI IPropertySetStorage_fnAddRef(
2809 IPropertySetStorage *ppstg)
2811 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2812 return IStorage_AddRef( &This->base.IStorage_iface );
2815 /************************************************************************
2816 * IPropertySetStorage_fnRelease (IUnknown)
2818 * This method forwards to the common Release implementation
2820 static ULONG WINAPI IPropertySetStorage_fnRelease(
2821 IPropertySetStorage *ppstg)
2823 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2824 return IStorage_Release( &This->base.IStorage_iface );
2827 /************************************************************************
2828 * IPropertySetStorage_fnCreate (IPropertySetStorage)
2830 static HRESULT WINAPI IPropertySetStorage_fnCreate(
2831 IPropertySetStorage *ppstg,
2832 REFFMTID rfmtid,
2833 const CLSID* pclsid,
2834 DWORD grfFlags,
2835 DWORD grfMode,
2836 IPropertyStorage** ppprstg)
2838 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2839 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2840 IStream *stm = NULL;
2841 HRESULT r;
2843 TRACE("%p %s %08x %08x %p\n", This, debugstr_guid(rfmtid), grfFlags,
2844 grfMode, ppprstg);
2846 /* be picky */
2847 if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
2849 r = STG_E_INVALIDFLAG;
2850 goto end;
2853 if (!rfmtid)
2855 r = E_INVALIDARG;
2856 goto end;
2859 /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
2860 * storage, not a stream. For now, disallow it.
2862 if (grfFlags & PROPSETFLAG_NONSIMPLE)
2864 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2865 r = STG_E_INVALIDFLAG;
2866 goto end;
2869 r = FmtIdToPropStgName(rfmtid, name);
2870 if (FAILED(r))
2871 goto end;
2873 r = IStorage_CreateStream( &This->base.IStorage_iface, name, grfMode, 0, 0, &stm );
2874 if (FAILED(r))
2875 goto end;
2877 r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg);
2879 IStream_Release( stm );
2881 end:
2882 TRACE("returning 0x%08x\n", r);
2883 return r;
2886 /************************************************************************
2887 * IPropertySetStorage_fnOpen (IPropertySetStorage)
2889 static HRESULT WINAPI IPropertySetStorage_fnOpen(
2890 IPropertySetStorage *ppstg,
2891 REFFMTID rfmtid,
2892 DWORD grfMode,
2893 IPropertyStorage** ppprstg)
2895 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2896 IStream *stm = NULL;
2897 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2898 HRESULT r;
2900 TRACE("%p %s %08x %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
2902 /* be picky */
2903 if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
2904 grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
2906 r = STG_E_INVALIDFLAG;
2907 goto end;
2910 if (!rfmtid)
2912 r = E_INVALIDARG;
2913 goto end;
2916 r = FmtIdToPropStgName(rfmtid, name);
2917 if (FAILED(r))
2918 goto end;
2920 r = IStorage_OpenStream( &This->base.IStorage_iface, name, 0, grfMode, 0, &stm );
2921 if (FAILED(r))
2922 goto end;
2924 r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg);
2926 IStream_Release( stm );
2928 end:
2929 TRACE("returning 0x%08x\n", r);
2930 return r;
2933 /************************************************************************
2934 * IPropertySetStorage_fnDelete (IPropertySetStorage)
2936 static HRESULT WINAPI IPropertySetStorage_fnDelete(
2937 IPropertySetStorage *ppstg,
2938 REFFMTID rfmtid)
2940 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2941 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2942 HRESULT r;
2944 TRACE("%p %s\n", This, debugstr_guid(rfmtid));
2946 if (!rfmtid)
2947 return E_INVALIDARG;
2949 r = FmtIdToPropStgName(rfmtid, name);
2950 if (FAILED(r))
2951 return r;
2953 return IStorage_DestroyElement(&This->base.IStorage_iface, name);
2956 static HRESULT WINAPI IPropertySetStorage_fnEnum(IPropertySetStorage *iface, IEnumSTATPROPSETSTG **enum_obj)
2958 StorageImpl *storage = impl_from_IPropertySetStorage(iface);
2960 TRACE("%p, %p.\n", iface, enum_obj);
2962 if (!enum_obj)
2963 return E_INVALIDARG;
2965 return create_enum_stat_propset_stg(storage, enum_obj);
2968 /***********************************************************************
2969 * vtables
2971 const IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
2973 IPropertySetStorage_fnQueryInterface,
2974 IPropertySetStorage_fnAddRef,
2975 IPropertySetStorage_fnRelease,
2976 IPropertySetStorage_fnCreate,
2977 IPropertySetStorage_fnOpen,
2978 IPropertySetStorage_fnDelete,
2979 IPropertySetStorage_fnEnum
2982 static const IPropertyStorageVtbl IPropertyStorage_Vtbl =
2984 IPropertyStorage_fnQueryInterface,
2985 IPropertyStorage_fnAddRef,
2986 IPropertyStorage_fnRelease,
2987 IPropertyStorage_fnReadMultiple,
2988 IPropertyStorage_fnWriteMultiple,
2989 IPropertyStorage_fnDeleteMultiple,
2990 IPropertyStorage_fnReadPropertyNames,
2991 IPropertyStorage_fnWritePropertyNames,
2992 IPropertyStorage_fnDeletePropertyNames,
2993 IPropertyStorage_fnCommit,
2994 IPropertyStorage_fnRevert,
2995 IPropertyStorage_fnEnum,
2996 IPropertyStorage_fnSetTimes,
2997 IPropertyStorage_fnSetClass,
2998 IPropertyStorage_fnStat,
3001 /***********************************************************************
3002 * Format ID <-> name conversion
3004 static const WCHAR szSummaryInfo[] = L"\5SummaryInformation";
3005 static const WCHAR szDocSummaryInfo[] = L"\5DocumentSummaryInformation";
3007 #define BITS_PER_BYTE 8
3008 #define CHARMASK 0x1f
3009 #define BITS_IN_CHARMASK 5
3010 #define NUM_ALPHA_CHARS 26
3012 /***********************************************************************
3013 * FmtIdToPropStgName [ole32.@]
3014 * Returns the storage name of the format ID rfmtid.
3015 * PARAMS
3016 * rfmtid [I] Format ID for which to return a storage name
3017 * str [O] Storage name associated with rfmtid.
3019 * RETURNS
3020 * E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise.
3022 * NOTES
3023 * str must be at least CCH_MAX_PROPSTG_NAME characters in length.
3025 HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str)
3027 static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
3029 TRACE("%s, %p\n", debugstr_guid(rfmtid), str);
3031 if (!rfmtid) return E_INVALIDARG;
3032 if (!str) return E_INVALIDARG;
3034 if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
3035 lstrcpyW(str, szSummaryInfo);
3036 else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
3037 lstrcpyW(str, szDocSummaryInfo);
3038 else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid))
3039 lstrcpyW(str, szDocSummaryInfo);
3040 else
3042 const BYTE *fmtptr;
3043 WCHAR *pstr = str;
3044 ULONG bitsRemaining = BITS_PER_BYTE;
3046 *pstr++ = 5;
3047 for (fmtptr = (const BYTE *)rfmtid; fmtptr < (const BYTE *)rfmtid + sizeof(FMTID); )
3049 ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
3051 if (bitsRemaining >= BITS_IN_CHARMASK)
3053 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
3054 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
3055 *pstr <= 'z')
3056 *pstr += 'A' - 'a';
3057 pstr++;
3058 bitsRemaining -= BITS_IN_CHARMASK;
3059 if (bitsRemaining == 0)
3061 fmtptr++;
3062 bitsRemaining = BITS_PER_BYTE;
3065 else
3067 if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID))
3068 i |= *fmtptr << bitsRemaining;
3069 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
3070 bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK;
3073 *pstr = 0;
3075 TRACE("returning %s\n", debugstr_w(str));
3076 return S_OK;
3079 /***********************************************************************
3080 * PropStgNameToFmtId [ole32.@]
3081 * Returns the format ID corresponding to the given name.
3082 * PARAMS
3083 * str [I] Storage name to convert to a format ID.
3084 * rfmtid [O] Format ID corresponding to str.
3086 * RETURNS
3087 * E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to
3088 * a format ID, S_OK otherwise.
3090 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid)
3092 HRESULT hr = STG_E_INVALIDNAME;
3094 TRACE("%s, %p\n", debugstr_w(str), rfmtid);
3096 if (!rfmtid) return E_INVALIDARG;
3097 if (!str) return STG_E_INVALIDNAME;
3099 if (!lstrcmpiW(str, szDocSummaryInfo))
3101 *rfmtid = FMTID_DocSummaryInformation;
3102 hr = S_OK;
3104 else if (!lstrcmpiW(str, szSummaryInfo))
3106 *rfmtid = FMTID_SummaryInformation;
3107 hr = S_OK;
3109 else
3111 ULONG bits;
3112 BYTE *fmtptr = (BYTE *)rfmtid - 1;
3113 const WCHAR *pstr = str;
3115 memset(rfmtid, 0, sizeof(*rfmtid));
3116 for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE;
3117 bits += BITS_IN_CHARMASK)
3119 ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored;
3120 WCHAR wc;
3122 if (bitsUsed == 0)
3123 fmtptr++;
3124 wc = *++pstr - 'A';
3125 if (wc > NUM_ALPHA_CHARS)
3127 wc += 'A' - 'a';
3128 if (wc > NUM_ALPHA_CHARS)
3130 wc += 'a' - '0' + NUM_ALPHA_CHARS;
3131 if (wc > CHARMASK)
3133 WARN("invalid character (%d)\n", *pstr);
3134 goto end;
3138 *fmtptr |= wc << bitsUsed;
3139 bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK);
3140 if (bitsStored < BITS_IN_CHARMASK)
3142 wc >>= BITS_PER_BYTE - bitsUsed;
3143 if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE)
3145 if (wc != 0)
3147 WARN("extra bits\n");
3148 goto end;
3150 break;
3152 fmtptr++;
3153 *fmtptr |= (BYTE)wc;
3156 hr = S_OK;
3158 end:
3159 return hr;
3162 #ifdef __i386__ /* thiscall functions are i386-specific */
3164 #define DEFINE_STDCALL_WRAPPER(num,func,args) \
3165 __ASM_STDCALL_FUNC(func, args, \
3166 "popl %eax\n\t" \
3167 "popl %ecx\n\t" \
3168 "pushl %eax\n\t" \
3169 "movl (%ecx), %eax\n\t" \
3170 "jmp *(4*(" #num "))(%eax)" )
3172 DEFINE_STDCALL_WRAPPER(0,Allocate_PMemoryAllocator,8)
3173 extern void* WINAPI Allocate_PMemoryAllocator(void *this, ULONG cbSize);
3175 #else
3177 static void* WINAPI Allocate_PMemoryAllocator(void *this, ULONG cbSize)
3179 void* (WINAPI *fn)(void*,ULONG) = **(void***)this;
3180 return fn(this, cbSize);
3183 #endif
3185 BOOLEAN WINAPI StgConvertPropertyToVariant(const SERIALIZEDPROPERTYVALUE* prop,
3186 USHORT CodePage, PROPVARIANT* pvar, void* pma)
3188 struct read_buffer read_buffer;
3189 HRESULT hr;
3191 read_buffer.data = (BYTE *)prop;
3192 read_buffer.size = ~(size_t)0;
3193 hr = PropertyStorage_ReadProperty(pvar, &read_buffer, 0, CodePage, Allocate_PMemoryAllocator, pma);
3195 if (FAILED(hr))
3197 FIXME("should raise C++ exception on failure\n");
3198 PropVariantInit(pvar);
3201 return FALSE;
3204 SERIALIZEDPROPERTYVALUE* WINAPI StgConvertVariantToProperty(const PROPVARIANT *pvar,
3205 USHORT CodePage, SERIALIZEDPROPERTYVALUE *pprop, ULONG *pcb, PROPID pid,
3206 BOOLEAN fReserved, ULONG *pcIndirect)
3208 FIXME("%p,%d,%p,%p,%d,%d,%p\n", pvar, CodePage, pprop, pcb, pid, fReserved, pcIndirect);
3210 return NULL;
3213 HRESULT WINAPI StgCreatePropStg(IUnknown *unk, REFFMTID fmt, const CLSID *clsid,
3214 DWORD flags, DWORD reserved, IPropertyStorage **prop_stg)
3216 IStorage *stg;
3217 IStream *stm;
3218 HRESULT r;
3220 TRACE("%p %s %s %08x %d %p\n", unk, debugstr_guid(fmt), debugstr_guid(clsid), flags, reserved, prop_stg);
3222 if (!fmt || reserved)
3224 r = E_INVALIDARG;
3225 goto end;
3228 if (flags & PROPSETFLAG_NONSIMPLE)
3230 r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
3231 if (FAILED(r))
3232 goto end;
3234 /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to create a
3235 * storage, not a stream. For now, disallow it.
3237 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
3238 IStorage_Release(stg);
3239 r = STG_E_INVALIDFLAG;
3241 else
3243 r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
3244 if (FAILED(r))
3245 goto end;
3247 r = PropertyStorage_ConstructEmpty(stm, fmt, flags,
3248 STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
3250 IStream_Release( stm );
3253 end:
3254 TRACE("returning 0x%08x\n", r);
3255 return r;
3258 HRESULT WINAPI StgOpenPropStg(IUnknown *unk, REFFMTID fmt, DWORD flags,
3259 DWORD reserved, IPropertyStorage **prop_stg)
3261 IStorage *stg;
3262 IStream *stm;
3263 HRESULT r;
3265 TRACE("%p %s %08x %d %p\n", unk, debugstr_guid(fmt), flags, reserved, prop_stg);
3267 if (!fmt || reserved)
3269 r = E_INVALIDARG;
3270 goto end;
3273 if (flags & PROPSETFLAG_NONSIMPLE)
3275 r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
3276 if (FAILED(r))
3277 goto end;
3279 /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to open a
3280 * storage, not a stream. For now, disallow it.
3282 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
3283 IStorage_Release(stg);
3284 r = STG_E_INVALIDFLAG;
3286 else
3288 r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
3289 if (FAILED(r))
3290 goto end;
3292 r = PropertyStorage_ConstructFromStream(stm, fmt,
3293 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
3295 IStream_Release( stm );
3298 end:
3299 TRACE("returning 0x%08x\n", r);
3300 return r;