ole32: Implement IPropertyStorage::Enum using enumx.
[wine/multimedia.git] / dlls / ole32 / stg_prop.c
blobe0567c825d8ccc6adfd67030869f322673856cea
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
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 * There's a decent overview of property set storage here:
29 * http://msdn.microsoft.com/archive/en-us/dnarolegen/html/msdn_propset.asp
30 * It's a little bit out of date, and more definitive references are given
31 * below, but it gives the best "big picture" that I've found.
33 * TODO:
34 * - I don't honor the maximum property set size.
35 * - Certain bogus files could result in reading past the end of a buffer.
36 * - Mac-generated files won't be read correctly, even if they're little
37 * endian, because I disregard whether the generator was a Mac. This means
38 * strings will probably be munged (as I don't understand Mac scripts.)
39 * - Not all PROPVARIANT types are supported.
40 * - User defined properties are not supported, see comment in
41 * PropertyStorage_ReadFromStream
44 #include <assert.h>
45 #include <stdarg.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
50 #define COBJMACROS
51 #define NONAMELESSUNION
52 #define NONAMELESSSTRUCT
54 #include "windef.h"
55 #include "winbase.h"
56 #include "winnls.h"
57 #include "winuser.h"
58 #include "wine/unicode.h"
59 #include "wine/debug.h"
60 #include "dictionary.h"
61 #include "storage32.h"
62 #include "enumx.h"
64 WINE_DEFAULT_DEBUG_CHANNEL(storage);
66 static inline StorageImpl *impl_from_IPropertySetStorage( IPropertySetStorage *iface )
68 return (StorageImpl *)((char*)iface - FIELD_OFFSET(StorageImpl, base.pssVtbl));
71 /* These are documented in MSDN, e.g.
72 * http://msdn.microsoft.com/library/en-us/stg/stg/property_set_header.asp
73 * http://msdn.microsoft.com/library/library/en-us/stg/stg/section.asp
74 * but they don't seem to be in any header file.
76 #define PROPSETHDR_BYTEORDER_MAGIC 0xfffe
77 #define PROPSETHDR_OSVER_KIND_WIN16 0
78 #define PROPSETHDR_OSVER_KIND_MAC 1
79 #define PROPSETHDR_OSVER_KIND_WIN32 2
81 #define CP_UNICODE 1200
83 #define MAX_VERSION_0_PROP_NAME_LENGTH 256
85 #define CFTAG_WINDOWS (-1L)
86 #define CFTAG_MACINTOSH (-2L)
87 #define CFTAG_FMTID (-3L)
88 #define CFTAG_NODATA 0L
90 /* The format version (and what it implies) is described here:
91 * http://msdn.microsoft.com/library/en-us/stg/stg/format_version.asp
93 typedef struct tagPROPERTYSETHEADER
95 WORD wByteOrder; /* always 0xfffe */
96 WORD wFormat; /* can be zero or one */
97 DWORD dwOSVer; /* OS version of originating system */
98 CLSID clsid; /* application CLSID */
99 DWORD reserved; /* always 1 */
100 } PROPERTYSETHEADER;
102 typedef struct tagFORMATIDOFFSET
104 FMTID fmtid;
105 DWORD dwOffset; /* from beginning of stream */
106 } FORMATIDOFFSET;
108 typedef struct tagPROPERTYSECTIONHEADER
110 DWORD cbSection;
111 DWORD cProperties;
112 } PROPERTYSECTIONHEADER;
114 typedef struct tagPROPERTYIDOFFSET
116 DWORD propid;
117 DWORD dwOffset; /* from beginning of section */
118 } PROPERTYIDOFFSET;
120 typedef struct tagPropertyStorage_impl PropertyStorage_impl;
122 /* Initializes the property storage from the stream (and undoes any uncommitted
123 * changes in the process.) Returns an error if there is an error reading or
124 * if the stream format doesn't match what's expected.
126 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *);
128 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *);
130 /* Creates the dictionaries used by the property storage. If successful, all
131 * the dictionaries have been created. If failed, none has been. (This makes
132 * it a bit easier to deal with destroying them.)
134 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *);
136 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *);
138 /* Copies from propvar to prop. If propvar's type is VT_LPSTR, copies the
139 * string using PropertyStorage_StringCopy.
141 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
142 const PROPVARIANT *propvar, LCID targetCP, LCID srcCP);
144 /* Copies the string src, which is encoded using code page srcCP, and returns
145 * it in *dst, in the code page specified by targetCP. The returned string is
146 * allocated using CoTaskMemAlloc.
147 * If srcCP is CP_UNICODE, src is in fact an LPCWSTR. Similarly, if targetCP
148 * is CP_UNICODE, the returned string is in fact an LPWSTR.
149 * Returns S_OK on success, something else on failure.
151 static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
152 LCID targetCP);
154 static const IPropertyStorageVtbl IPropertyStorage_Vtbl;
155 static const IEnumSTATPROPSETSTGVtbl IEnumSTATPROPSETSTG_Vtbl;
156 static const IEnumSTATPROPSTGVtbl IEnumSTATPROPSTG_Vtbl;
157 static HRESULT create_EnumSTATPROPSETSTG(StorageImpl *, IEnumSTATPROPSETSTG**);
158 static HRESULT create_EnumSTATPROPSTG(PropertyStorage_impl *, IEnumSTATPROPSTG**);
160 /***********************************************************************
161 * Implementation of IPropertyStorage
163 struct tagPropertyStorage_impl
165 const IPropertyStorageVtbl *vtbl;
166 LONG ref;
167 CRITICAL_SECTION cs;
168 IStream *stm;
169 BOOL dirty;
170 FMTID fmtid;
171 CLSID clsid;
172 WORD format;
173 DWORD originatorOS;
174 DWORD grfFlags;
175 DWORD grfMode;
176 UINT codePage;
177 LCID locale;
178 PROPID highestProp;
179 struct dictionary *name_to_propid;
180 struct dictionary *propid_to_name;
181 struct dictionary *propid_to_prop;
184 /************************************************************************
185 * IPropertyStorage_fnQueryInterface (IPropertyStorage)
187 static HRESULT WINAPI IPropertyStorage_fnQueryInterface(
188 IPropertyStorage *iface,
189 REFIID riid,
190 void** ppvObject)
192 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
194 if ( (This==0) || (ppvObject==0) )
195 return E_INVALIDARG;
197 *ppvObject = 0;
199 if (IsEqualGUID(&IID_IUnknown, riid) ||
200 IsEqualGUID(&IID_IPropertyStorage, riid))
202 IPropertyStorage_AddRef(iface);
203 *ppvObject = (IPropertyStorage*)iface;
204 return S_OK;
207 return E_NOINTERFACE;
210 /************************************************************************
211 * IPropertyStorage_fnAddRef (IPropertyStorage)
213 static ULONG WINAPI IPropertyStorage_fnAddRef(
214 IPropertyStorage *iface)
216 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
217 return InterlockedIncrement(&This->ref);
220 /************************************************************************
221 * IPropertyStorage_fnRelease (IPropertyStorage)
223 static ULONG WINAPI IPropertyStorage_fnRelease(
224 IPropertyStorage *iface)
226 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
227 ULONG ref;
229 ref = InterlockedDecrement(&This->ref);
230 if (ref == 0)
232 TRACE("Destroying %p\n", This);
233 if (This->dirty)
234 IPropertyStorage_Commit(iface, STGC_DEFAULT);
235 IStream_Release(This->stm);
236 DeleteCriticalSection(&This->cs);
237 PropertyStorage_DestroyDictionaries(This);
238 HeapFree(GetProcessHeap(), 0, This);
240 return ref;
243 static PROPVARIANT *PropertyStorage_FindProperty(PropertyStorage_impl *This,
244 DWORD propid)
246 PROPVARIANT *ret = NULL;
248 assert(This);
249 dictionary_find(This->propid_to_prop, (void *)propid, (void **)&ret);
250 TRACE("returning %p\n", ret);
251 return ret;
254 /* Returns NULL if name is NULL. */
255 static PROPVARIANT *PropertyStorage_FindPropertyByName(
256 PropertyStorage_impl *This, LPCWSTR name)
258 PROPVARIANT *ret = NULL;
259 PROPID propid;
261 assert(This);
262 if (!name)
263 return NULL;
264 if (This->codePage == CP_UNICODE)
266 if (dictionary_find(This->name_to_propid, name, (void **)&propid))
267 ret = PropertyStorage_FindProperty(This, (PROPID)propid);
269 else
271 LPSTR ansiName;
272 HRESULT hr = PropertyStorage_StringCopy((LPCSTR)name, CP_UNICODE,
273 &ansiName, This->codePage);
275 if (SUCCEEDED(hr))
277 if (dictionary_find(This->name_to_propid, ansiName,
278 (void **)&propid))
279 ret = PropertyStorage_FindProperty(This, (PROPID)propid);
280 CoTaskMemFree(ansiName);
283 TRACE("returning %p\n", ret);
284 return ret;
287 static LPWSTR PropertyStorage_FindPropertyNameById(PropertyStorage_impl *This,
288 DWORD propid)
290 LPWSTR ret = NULL;
292 assert(This);
293 dictionary_find(This->propid_to_name, (void *)propid, (void **)&ret);
294 TRACE("returning %p\n", ret);
295 return ret;
298 /************************************************************************
299 * IPropertyStorage_fnReadMultiple (IPropertyStorage)
301 static HRESULT WINAPI IPropertyStorage_fnReadMultiple(
302 IPropertyStorage* iface,
303 ULONG cpspec,
304 const PROPSPEC rgpspec[],
305 PROPVARIANT rgpropvar[])
307 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
308 HRESULT hr = S_OK;
309 ULONG i;
311 TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
313 if (cpspec && (!rgpspec || !rgpropvar))
314 return E_INVALIDARG;
315 EnterCriticalSection(&This->cs);
316 for (i = 0; i < cpspec; i++)
318 PropVariantInit(&rgpropvar[i]);
319 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
321 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
322 rgpspec[i].u.lpwstr);
324 if (prop)
325 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop, GetACP(),
326 This->codePage);
328 else
330 switch (rgpspec[i].u.propid)
332 case PID_CODEPAGE:
333 rgpropvar[i].vt = VT_I2;
334 rgpropvar[i].u.iVal = This->codePage;
335 break;
336 case PID_LOCALE:
337 rgpropvar[i].vt = VT_I4;
338 rgpropvar[i].u.lVal = This->locale;
339 break;
340 default:
342 PROPVARIANT *prop = PropertyStorage_FindProperty(This,
343 rgpspec[i].u.propid);
345 if (prop)
346 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop,
347 GetACP(), This->codePage);
348 else
349 hr = S_FALSE;
354 LeaveCriticalSection(&This->cs);
355 return hr;
358 static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
359 LCID dstCP)
361 HRESULT hr = S_OK;
362 int len;
364 TRACE("%s, %p, %ld, %ld\n",
365 srcCP == CP_UNICODE ? debugstr_w((LPCWSTR)src) : debugstr_a(src), dst,
366 dstCP, srcCP);
367 assert(src);
368 assert(dst);
369 *dst = NULL;
370 if (dstCP == srcCP)
372 size_t len;
374 if (dstCP == CP_UNICODE)
375 len = (strlenW((LPCWSTR)src) + 1) * sizeof(WCHAR);
376 else
377 len = strlen(src) + 1;
378 *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
379 if (!*dst)
380 hr = STG_E_INSUFFICIENTMEMORY;
381 else
382 memcpy(*dst, src, len);
384 else
386 if (dstCP == CP_UNICODE)
388 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
389 *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
390 if (!*dst)
391 hr = STG_E_INSUFFICIENTMEMORY;
392 else
393 MultiByteToWideChar(srcCP, 0, src, -1, (LPWSTR)*dst, len);
395 else
397 LPWSTR wideStr;
399 if (srcCP == CP_UNICODE)
400 wideStr = (LPWSTR)src;
401 else
403 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
404 wideStr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
405 if (wideStr)
406 MultiByteToWideChar(srcCP, 0, src, -1, wideStr, len);
407 else
408 hr = STG_E_INSUFFICIENTMEMORY;
410 if (SUCCEEDED(hr))
412 len = WideCharToMultiByte(dstCP, 0, wideStr, -1, NULL, 0,
413 NULL, NULL);
414 *dst = CoTaskMemAlloc(len);
415 if (!*dst)
416 hr = STG_E_INSUFFICIENTMEMORY;
417 else
419 BOOL defCharUsed = FALSE;
421 if (WideCharToMultiByte(dstCP, 0, wideStr, -1, *dst, len,
422 NULL, &defCharUsed) == 0 || defCharUsed)
424 CoTaskMemFree(*dst);
425 *dst = NULL;
426 hr = HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION);
430 if (wideStr != (LPWSTR)src)
431 HeapFree(GetProcessHeap(), 0, wideStr);
434 TRACE("returning 0x%08lx (%s)\n", hr,
435 dstCP == CP_UNICODE ? debugstr_w((LPCWSTR)*dst) : debugstr_a(*dst));
436 return hr;
439 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
440 const PROPVARIANT *propvar, LCID targetCP, LCID srcCP)
442 HRESULT hr = S_OK;
444 assert(prop);
445 assert(propvar);
446 if (propvar->vt == VT_LPSTR)
448 hr = PropertyStorage_StringCopy(propvar->u.pszVal, srcCP,
449 &prop->u.pszVal, targetCP);
450 if (SUCCEEDED(hr))
451 prop->vt = VT_LPSTR;
453 else
454 PropVariantCopy(prop, propvar);
455 return hr;
458 /* Stores the property with id propid and value propvar into this property
459 * storage. lcid is ignored if propvar's type is not VT_LPSTR. If propvar's
460 * type is VT_LPSTR, converts the string using lcid as the source code page
461 * and This->codePage as the target code page before storing.
462 * As a side effect, may change This->format to 1 if the type of propvar is
463 * a version 1-only property.
465 static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
466 PROPID propid, const PROPVARIANT *propvar, LCID lcid)
468 HRESULT hr = S_OK;
469 PROPVARIANT *prop = PropertyStorage_FindProperty(This, propid);
471 assert(This);
472 assert(propvar);
473 if (propvar->vt & VT_BYREF || propvar->vt & VT_ARRAY)
474 This->format = 1;
475 switch (propvar->vt)
477 case VT_DECIMAL:
478 case VT_I1:
479 case VT_INT:
480 case VT_UINT:
481 case VT_VECTOR|VT_I1:
482 This->format = 1;
484 TRACE("Setting 0x%08lx to type %d\n", propid, propvar->vt);
485 if (prop)
487 PropVariantClear(prop);
488 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
489 lcid);
491 else
493 prop = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
494 sizeof(PROPVARIANT));
495 if (prop)
497 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
498 lcid);
499 if (SUCCEEDED(hr))
501 dictionary_insert(This->propid_to_prop, (void *)propid, prop);
502 if (propid > This->highestProp)
503 This->highestProp = propid;
505 else
506 HeapFree(GetProcessHeap(), 0, prop);
508 else
509 hr = STG_E_INSUFFICIENTMEMORY;
511 return hr;
514 /* Adds the name srcName to the name dictionaries, mapped to property ID id.
515 * srcName is encoded in code page cp, and is converted to This->codePage.
516 * If cp is CP_UNICODE, srcName is actually a unicode string.
517 * As a side effect, may change This->format to 1 if srcName is too long for
518 * a version 0 property storage.
519 * Doesn't validate id.
521 static HRESULT PropertyStorage_StoreNameWithId(PropertyStorage_impl *This,
522 LPCSTR srcName, LCID cp, PROPID id)
524 LPSTR name;
525 HRESULT hr;
527 assert(srcName);
529 hr = PropertyStorage_StringCopy((LPCSTR)srcName, cp, &name, This->codePage);
530 if (SUCCEEDED(hr))
532 if (This->codePage == CP_UNICODE)
534 if (lstrlenW((LPWSTR)name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
535 This->format = 1;
537 else
539 if (strlen(name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
540 This->format = 1;
542 TRACE("Adding prop name %s, propid %ld\n",
543 This->codePage == CP_UNICODE ? debugstr_w((LPCWSTR)name) :
544 debugstr_a(name), id);
545 dictionary_insert(This->name_to_propid, name, (void *)id);
546 dictionary_insert(This->propid_to_name, (void *)id, name);
548 return hr;
551 /************************************************************************
552 * IPropertyStorage_fnWriteMultiple (IPropertyStorage)
554 static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
555 IPropertyStorage* iface,
556 ULONG cpspec,
557 const PROPSPEC rgpspec[],
558 const PROPVARIANT rgpropvar[],
559 PROPID propidNameFirst)
561 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
562 HRESULT hr = S_OK;
563 ULONG i;
565 TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
567 if (cpspec && (!rgpspec || !rgpropvar))
568 return E_INVALIDARG;
569 if (!(This->grfMode & STGM_READWRITE))
570 return STG_E_ACCESSDENIED;
571 EnterCriticalSection(&This->cs);
572 This->dirty = TRUE;
573 This->originatorOS = (DWORD)MAKELONG(LOWORD(GetVersion()),
574 PROPSETHDR_OSVER_KIND_WIN32) ;
575 for (i = 0; i < cpspec; i++)
577 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
579 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
580 rgpspec[i].u.lpwstr);
582 if (prop)
583 PropVariantCopy(prop, &rgpropvar[i]);
584 else
586 /* Note that I don't do the special cases here that I do below,
587 * because naming the special PIDs isn't supported.
589 if (propidNameFirst < PID_FIRST_USABLE ||
590 propidNameFirst >= PID_MIN_READONLY)
591 hr = STG_E_INVALIDPARAMETER;
592 else
594 PROPID nextId = max(propidNameFirst, This->highestProp + 1);
596 hr = PropertyStorage_StoreNameWithId(This,
597 (LPCSTR)rgpspec[i].u.lpwstr, CP_UNICODE, nextId);
598 if (SUCCEEDED(hr))
599 hr = PropertyStorage_StorePropWithId(This, nextId,
600 &rgpropvar[i], GetACP());
604 else
606 switch (rgpspec[i].u.propid)
608 case PID_DICTIONARY:
609 /* Can't set the dictionary */
610 hr = STG_E_INVALIDPARAMETER;
611 break;
612 case PID_CODEPAGE:
613 /* Can only set the code page if nothing else has been set */
614 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
615 rgpropvar[i].vt == VT_I2)
617 This->codePage = rgpropvar[i].u.iVal;
618 if (This->codePage == CP_UNICODE)
619 This->grfFlags &= ~PROPSETFLAG_ANSI;
620 else
621 This->grfFlags |= PROPSETFLAG_ANSI;
623 else
624 hr = STG_E_INVALIDPARAMETER;
625 break;
626 case PID_LOCALE:
627 /* Can only set the locale if nothing else has been set */
628 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
629 rgpropvar[i].vt == VT_I4)
630 This->locale = rgpropvar[i].u.lVal;
631 else
632 hr = STG_E_INVALIDPARAMETER;
633 break;
634 case PID_ILLEGAL:
635 /* silently ignore like MSDN says */
636 break;
637 default:
638 if (rgpspec[i].u.propid >= PID_MIN_READONLY)
639 hr = STG_E_INVALIDPARAMETER;
640 else
641 hr = PropertyStorage_StorePropWithId(This,
642 rgpspec[i].u.propid, &rgpropvar[i], GetACP());
646 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
647 IPropertyStorage_Commit(iface, STGC_DEFAULT);
648 LeaveCriticalSection(&This->cs);
649 return hr;
652 /************************************************************************
653 * IPropertyStorage_fnDeleteMultiple (IPropertyStorage)
655 static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
656 IPropertyStorage* iface,
657 ULONG cpspec,
658 const PROPSPEC rgpspec[])
660 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
661 ULONG i;
662 HRESULT hr;
664 TRACE("(%p, %ld, %p)\n", iface, cpspec, rgpspec);
666 if (cpspec && !rgpspec)
667 return E_INVALIDARG;
668 if (!(This->grfMode & STGM_READWRITE))
669 return STG_E_ACCESSDENIED;
670 hr = S_OK;
671 EnterCriticalSection(&This->cs);
672 This->dirty = TRUE;
673 for (i = 0; i < cpspec; i++)
675 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
677 PROPID propid;
679 if (dictionary_find(This->name_to_propid,
680 (void *)rgpspec[i].u.lpwstr, (void **)&propid))
681 dictionary_remove(This->propid_to_prop, (void *)propid);
683 else
685 if (rgpspec[i].u.propid >= PID_FIRST_USABLE &&
686 rgpspec[i].u.propid < PID_MIN_READONLY)
687 dictionary_remove(This->propid_to_prop,
688 (void *)rgpspec[i].u.propid);
689 else
690 hr = STG_E_INVALIDPARAMETER;
693 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
694 IPropertyStorage_Commit(iface, STGC_DEFAULT);
695 LeaveCriticalSection(&This->cs);
696 return hr;
699 /************************************************************************
700 * IPropertyStorage_fnReadPropertyNames (IPropertyStorage)
702 static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames(
703 IPropertyStorage* iface,
704 ULONG cpropid,
705 const PROPID rgpropid[],
706 LPOLESTR rglpwstrName[])
708 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
709 ULONG i;
710 HRESULT hr = S_FALSE;
712 TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
714 if (cpropid && (!rgpropid || !rglpwstrName))
715 return E_INVALIDARG;
716 EnterCriticalSection(&This->cs);
717 for (i = 0; i < cpropid && SUCCEEDED(hr); i++)
719 LPWSTR name = PropertyStorage_FindPropertyNameById(This, rgpropid[i]);
721 if (name)
723 size_t len = lstrlenW(name);
725 hr = S_OK;
726 rglpwstrName[i] = CoTaskMemAlloc((len + 1) * sizeof(WCHAR));
727 if (rglpwstrName)
728 memcpy(rglpwstrName, name, (len + 1) * sizeof(WCHAR));
729 else
730 hr = STG_E_INSUFFICIENTMEMORY;
732 else
733 rglpwstrName[i] = NULL;
735 LeaveCriticalSection(&This->cs);
736 return hr;
739 /************************************************************************
740 * IPropertyStorage_fnWritePropertyNames (IPropertyStorage)
742 static HRESULT WINAPI IPropertyStorage_fnWritePropertyNames(
743 IPropertyStorage* iface,
744 ULONG cpropid,
745 const PROPID rgpropid[],
746 const LPOLESTR rglpwstrName[])
748 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
749 ULONG i;
750 HRESULT hr;
752 TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
754 if (cpropid && (!rgpropid || !rglpwstrName))
755 return E_INVALIDARG;
756 if (!(This->grfMode & STGM_READWRITE))
757 return STG_E_ACCESSDENIED;
758 hr = S_OK;
759 EnterCriticalSection(&This->cs);
760 This->dirty = TRUE;
761 for (i = 0; SUCCEEDED(hr) && i < cpropid; i++)
763 if (rgpropid[i] != PID_ILLEGAL)
764 hr = PropertyStorage_StoreNameWithId(This, (LPCSTR)rglpwstrName[i],
765 CP_UNICODE, rgpropid[i]);
767 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
768 IPropertyStorage_Commit(iface, STGC_DEFAULT);
769 LeaveCriticalSection(&This->cs);
770 return hr;
773 /************************************************************************
774 * IPropertyStorage_fnDeletePropertyNames (IPropertyStorage)
776 static HRESULT WINAPI IPropertyStorage_fnDeletePropertyNames(
777 IPropertyStorage* iface,
778 ULONG cpropid,
779 const PROPID rgpropid[])
781 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
782 ULONG i;
783 HRESULT hr;
785 TRACE("(%p, %ld, %p)\n", iface, cpropid, rgpropid);
787 if (cpropid && !rgpropid)
788 return E_INVALIDARG;
789 if (!(This->grfMode & STGM_READWRITE))
790 return STG_E_ACCESSDENIED;
791 hr = S_OK;
792 EnterCriticalSection(&This->cs);
793 This->dirty = TRUE;
794 for (i = 0; i < cpropid; i++)
796 LPWSTR name = NULL;
798 if (dictionary_find(This->propid_to_name, (void *)rgpropid[i],
799 (void **)&name))
801 dictionary_remove(This->propid_to_name, (void *)rgpropid[i]);
802 dictionary_remove(This->name_to_propid, name);
805 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
806 IPropertyStorage_Commit(iface, STGC_DEFAULT);
807 LeaveCriticalSection(&This->cs);
808 return hr;
811 /************************************************************************
812 * IPropertyStorage_fnCommit (IPropertyStorage)
814 static HRESULT WINAPI IPropertyStorage_fnCommit(
815 IPropertyStorage* iface,
816 DWORD grfCommitFlags)
818 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
819 HRESULT hr;
821 TRACE("(%p, 0x%08lx)\n", iface, grfCommitFlags);
823 if (!(This->grfMode & STGM_READWRITE))
824 return STG_E_ACCESSDENIED;
825 EnterCriticalSection(&This->cs);
826 if (This->dirty)
827 hr = PropertyStorage_WriteToStream(This);
828 else
829 hr = S_OK;
830 LeaveCriticalSection(&This->cs);
831 return hr;
834 /************************************************************************
835 * IPropertyStorage_fnRevert (IPropertyStorage)
837 static HRESULT WINAPI IPropertyStorage_fnRevert(
838 IPropertyStorage* iface)
840 HRESULT hr;
841 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
843 TRACE("%p\n", iface);
845 EnterCriticalSection(&This->cs);
846 if (This->dirty)
848 PropertyStorage_DestroyDictionaries(This);
849 hr = PropertyStorage_CreateDictionaries(This);
850 if (SUCCEEDED(hr))
851 hr = PropertyStorage_ReadFromStream(This);
853 else
854 hr = S_OK;
855 LeaveCriticalSection(&This->cs);
856 return hr;
859 /************************************************************************
860 * IPropertyStorage_fnEnum (IPropertyStorage)
862 static HRESULT WINAPI IPropertyStorage_fnEnum(
863 IPropertyStorage* iface,
864 IEnumSTATPROPSTG** ppenum)
866 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
867 return create_EnumSTATPROPSTG(This, ppenum);
870 /************************************************************************
871 * IPropertyStorage_fnSetTimes (IPropertyStorage)
873 static HRESULT WINAPI IPropertyStorage_fnSetTimes(
874 IPropertyStorage* iface,
875 const FILETIME* pctime,
876 const FILETIME* patime,
877 const FILETIME* pmtime)
879 FIXME("\n");
880 return E_NOTIMPL;
883 /************************************************************************
884 * IPropertyStorage_fnSetClass (IPropertyStorage)
886 static HRESULT WINAPI IPropertyStorage_fnSetClass(
887 IPropertyStorage* iface,
888 REFCLSID clsid)
890 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
892 TRACE("%p, %s\n", iface, debugstr_guid(clsid));
894 if (!clsid)
895 return E_INVALIDARG;
896 if (!(This->grfMode & STGM_READWRITE))
897 return STG_E_ACCESSDENIED;
898 memcpy(&This->clsid, clsid, sizeof(This->clsid));
899 This->dirty = TRUE;
900 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
901 IPropertyStorage_Commit(iface, STGC_DEFAULT);
902 return S_OK;
905 /************************************************************************
906 * IPropertyStorage_fnStat (IPropertyStorage)
908 static HRESULT WINAPI IPropertyStorage_fnStat(
909 IPropertyStorage* iface,
910 STATPROPSETSTG* statpsstg)
912 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
913 STATSTG stat;
914 HRESULT hr;
916 TRACE("%p, %p\n", iface, statpsstg);
918 if (!statpsstg)
919 return E_INVALIDARG;
921 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
922 if (SUCCEEDED(hr))
924 memcpy(&statpsstg->fmtid, &This->fmtid, sizeof(statpsstg->fmtid));
925 memcpy(&statpsstg->clsid, &This->clsid, sizeof(statpsstg->clsid));
926 statpsstg->grfFlags = This->grfFlags;
927 memcpy(&statpsstg->mtime, &stat.mtime, sizeof(statpsstg->mtime));
928 memcpy(&statpsstg->ctime, &stat.ctime, sizeof(statpsstg->ctime));
929 memcpy(&statpsstg->atime, &stat.atime, sizeof(statpsstg->atime));
930 statpsstg->dwOSVersion = This->originatorOS;
932 return hr;
935 static int PropertyStorage_PropNameCompare(const void *a, const void *b,
936 void *extra)
938 PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
940 if (This->codePage == CP_UNICODE)
942 TRACE("(%s, %s)\n", debugstr_w(a), debugstr_w(b));
943 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
944 return lstrcmpW((LPCWSTR)a, (LPCWSTR)b);
945 else
946 return lstrcmpiW((LPCWSTR)a, (LPCWSTR)b);
948 else
950 TRACE("(%s, %s)\n", debugstr_a(a), debugstr_a(b));
951 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
952 return lstrcmpA((LPCSTR)a, (LPCSTR)b);
953 else
954 return lstrcmpiA((LPCSTR)a, (LPCSTR)b);
958 static void PropertyStorage_PropNameDestroy(void *k, void *d, void *extra)
960 CoTaskMemFree(k);
963 static int PropertyStorage_PropCompare(const void *a, const void *b,
964 void *extra)
966 TRACE("(%ld, %ld)\n", (PROPID)a, (PROPID)b);
967 return (PROPID)a - (PROPID)b;
970 static void PropertyStorage_PropertyDestroy(void *k, void *d, void *extra)
972 PropVariantClear((PROPVARIANT *)d);
973 HeapFree(GetProcessHeap(), 0, d);
976 #ifdef WORDS_BIGENDIAN
977 /* Swaps each character in str to or from little endian; assumes the conversion
978 * is symmetric, that is, that le16toh is equivalent to htole16.
980 static void PropertyStorage_ByteSwapString(LPWSTR str, size_t len)
982 DWORD i;
984 /* Swap characters to host order.
985 * FIXME: alignment?
987 for (i = 0; i < len; i++)
988 str[i] = le16toh(str[i]);
990 #else
991 #define PropertyStorage_ByteSwapString(s, l)
992 #endif
994 /* Reads the dictionary from the memory buffer beginning at ptr. Interprets
995 * the entries according to the values of This->codePage and This->locale.
996 * FIXME: there isn't any checking whether the read property extends past the
997 * end of the buffer.
999 static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This,
1000 BYTE *ptr)
1002 DWORD numEntries, i;
1003 HRESULT hr = S_OK;
1005 assert(This);
1006 assert(This->name_to_propid);
1007 assert(This->propid_to_name);
1009 StorageUtl_ReadDWord(ptr, 0, &numEntries);
1010 TRACE("Reading %ld entries:\n", numEntries);
1011 ptr += sizeof(DWORD);
1012 for (i = 0; SUCCEEDED(hr) && i < numEntries; i++)
1014 PROPID propid;
1015 DWORD cbEntry;
1017 StorageUtl_ReadDWord(ptr, 0, &propid);
1018 ptr += sizeof(PROPID);
1019 StorageUtl_ReadDWord(ptr, 0, &cbEntry);
1020 ptr += sizeof(DWORD);
1021 TRACE("Reading entry with ID 0x%08lx, %ld bytes\n", propid, cbEntry);
1022 /* Make sure the source string is NULL-terminated */
1023 if (This->codePage != CP_UNICODE)
1024 ptr[cbEntry - 1] = '\0';
1025 else
1026 *((LPWSTR)ptr + cbEntry / sizeof(WCHAR)) = '\0';
1027 hr = PropertyStorage_StoreNameWithId(This, (char*)ptr, This->codePage, propid);
1028 if (This->codePage == CP_UNICODE)
1030 /* Unicode entries are padded to DWORD boundaries */
1031 if (cbEntry % sizeof(DWORD))
1032 ptr += sizeof(DWORD) - (cbEntry % sizeof(DWORD));
1034 ptr += sizeof(DWORD) + cbEntry;
1036 return hr;
1039 /* FIXME: there isn't any checking whether the read property extends past the
1040 * end of the buffer.
1042 static HRESULT PropertyStorage_ReadProperty(PropertyStorage_impl *This,
1043 PROPVARIANT *prop, const BYTE *data)
1045 HRESULT hr = S_OK;
1047 assert(prop);
1048 assert(data);
1049 StorageUtl_ReadDWord(data, 0, (DWORD *)&prop->vt);
1050 data += sizeof(DWORD);
1051 switch (prop->vt)
1053 case VT_EMPTY:
1054 case VT_NULL:
1055 break;
1056 case VT_I1:
1057 prop->u.cVal = *(const char *)data;
1058 TRACE("Read char 0x%x ('%c')\n", prop->u.cVal, prop->u.cVal);
1059 break;
1060 case VT_UI1:
1061 prop->u.bVal = *(const UCHAR *)data;
1062 TRACE("Read byte 0x%x\n", prop->u.bVal);
1063 break;
1064 case VT_I2:
1065 StorageUtl_ReadWord(data, 0, (WORD*)&prop->u.iVal);
1066 TRACE("Read short %d\n", prop->u.iVal);
1067 break;
1068 case VT_UI2:
1069 StorageUtl_ReadWord(data, 0, &prop->u.uiVal);
1070 TRACE("Read ushort %d\n", prop->u.uiVal);
1071 break;
1072 case VT_INT:
1073 case VT_I4:
1074 StorageUtl_ReadDWord(data, 0, (DWORD*)&prop->u.lVal);
1075 TRACE("Read long %ld\n", prop->u.lVal);
1076 break;
1077 case VT_UINT:
1078 case VT_UI4:
1079 StorageUtl_ReadDWord(data, 0, &prop->u.ulVal);
1080 TRACE("Read ulong %ld\n", prop->u.ulVal);
1081 break;
1082 case VT_LPSTR:
1084 DWORD count;
1086 StorageUtl_ReadDWord(data, 0, &count);
1087 if (This->codePage == CP_UNICODE && count / 2)
1089 WARN("Unicode string has odd number of bytes\n");
1090 hr = STG_E_INVALIDHEADER;
1092 else
1094 prop->u.pszVal = CoTaskMemAlloc(count);
1095 if (prop->u.pszVal)
1097 memcpy(prop->u.pszVal, data + sizeof(DWORD), count);
1098 /* This is stored in the code page specified in This->codePage.
1099 * Don't convert it, the caller will just store it as-is.
1101 if (This->codePage == CP_UNICODE)
1103 /* Make sure it's NULL-terminated */
1104 prop->u.pszVal[count / sizeof(WCHAR) - 1] = '\0';
1105 TRACE("Read string value %s\n",
1106 debugstr_w(prop->u.pwszVal));
1108 else
1110 /* Make sure it's NULL-terminated */
1111 prop->u.pszVal[count - 1] = '\0';
1112 TRACE("Read string value %s\n", debugstr_a(prop->u.pszVal));
1115 else
1116 hr = STG_E_INSUFFICIENTMEMORY;
1118 break;
1120 case VT_LPWSTR:
1122 DWORD count;
1124 StorageUtl_ReadDWord(data, 0, &count);
1125 prop->u.pwszVal = CoTaskMemAlloc(count * sizeof(WCHAR));
1126 if (prop->u.pwszVal)
1128 memcpy(prop->u.pwszVal, data + sizeof(DWORD),
1129 count * sizeof(WCHAR));
1130 /* make sure string is NULL-terminated */
1131 prop->u.pwszVal[count - 1] = '\0';
1132 PropertyStorage_ByteSwapString(prop->u.pwszVal, count);
1133 TRACE("Read string value %s\n", debugstr_w(prop->u.pwszVal));
1135 else
1136 hr = STG_E_INSUFFICIENTMEMORY;
1137 break;
1139 case VT_FILETIME:
1140 StorageUtl_ReadULargeInteger(data, 0,
1141 (ULARGE_INTEGER *)&prop->u.filetime);
1142 break;
1143 case VT_CF:
1145 DWORD len = 0, type = 0, tag = 0;
1147 StorageUtl_ReadDWord(data, 0, &len);
1148 StorageUtl_ReadDWord(data, 4, &tag);
1149 StorageUtl_ReadDWord(data, 8, &type);
1150 if (tag == CFTAG_WINDOWS && len > 12)
1152 prop->u.pclipdata = CoTaskMemAlloc(sizeof (CLIPDATA));
1153 prop->u.pclipdata->cbSize = len;
1154 prop->u.pclipdata->ulClipFmt = type;
1155 prop->u.pclipdata->pClipData = CoTaskMemAlloc(len);
1156 memcpy(prop->u.pclipdata->pClipData, data+12, len - 12);
1157 TRACE("returning %p, len %ld\n", prop->u.pclipdata->pClipData, len - 12);
1159 else
1160 hr = STG_E_INVALIDPARAMETER;
1162 break;
1163 default:
1164 FIXME("unsupported type %d\n", prop->vt);
1165 hr = STG_E_INVALIDPARAMETER;
1167 return hr;
1170 static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
1171 PROPERTYSETHEADER *hdr)
1173 BYTE buf[sizeof(PROPERTYSETHEADER)];
1174 ULONG count = 0;
1175 HRESULT hr;
1177 assert(stm);
1178 assert(hdr);
1179 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1180 if (SUCCEEDED(hr))
1182 if (count != sizeof(buf))
1184 WARN("read only %ld\n", count);
1185 hr = STG_E_INVALIDHEADER;
1187 else
1189 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder),
1190 &hdr->wByteOrder);
1191 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat),
1192 &hdr->wFormat);
1193 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer),
1194 &hdr->dwOSVer);
1195 StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid),
1196 &hdr->clsid);
1197 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved),
1198 &hdr->reserved);
1201 TRACE("returning 0x%08lx\n", hr);
1202 return hr;
1205 static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm,
1206 FORMATIDOFFSET *fmt)
1208 BYTE buf[sizeof(FORMATIDOFFSET)];
1209 ULONG count = 0;
1210 HRESULT hr;
1212 assert(stm);
1213 assert(fmt);
1214 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1215 if (SUCCEEDED(hr))
1217 if (count != sizeof(buf))
1219 WARN("read only %ld\n", count);
1220 hr = STG_E_INVALIDHEADER;
1222 else
1224 StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid),
1225 &fmt->fmtid);
1226 StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset),
1227 &fmt->dwOffset);
1230 TRACE("returning 0x%08lx\n", hr);
1231 return hr;
1234 static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
1235 PROPERTYSECTIONHEADER *hdr)
1237 BYTE buf[sizeof(PROPERTYSECTIONHEADER)];
1238 ULONG count = 0;
1239 HRESULT hr;
1241 assert(stm);
1242 assert(hdr);
1243 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1244 if (SUCCEEDED(hr))
1246 if (count != sizeof(buf))
1248 WARN("read only %ld\n", count);
1249 hr = STG_E_INVALIDHEADER;
1251 else
1253 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1254 cbSection), &hdr->cbSection);
1255 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1256 cProperties), &hdr->cProperties);
1259 TRACE("returning 0x%08lx\n", hr);
1260 return hr;
1263 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This)
1265 PROPERTYSETHEADER hdr;
1266 FORMATIDOFFSET fmtOffset;
1267 PROPERTYSECTIONHEADER sectionHdr;
1268 LARGE_INTEGER seek;
1269 ULONG i;
1270 STATSTG stat;
1271 HRESULT hr;
1272 BYTE *buf = NULL;
1273 ULONG count = 0;
1274 DWORD dictOffset = 0;
1276 assert(This);
1277 This->dirty = FALSE;
1278 This->highestProp = 0;
1279 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
1280 if (FAILED(hr))
1281 goto end;
1282 if (stat.cbSize.u.HighPart)
1284 WARN("stream too big\n");
1285 /* maximum size varies, but it can't be this big */
1286 hr = STG_E_INVALIDHEADER;
1287 goto end;
1289 if (stat.cbSize.u.LowPart == 0)
1291 /* empty stream is okay */
1292 hr = S_OK;
1293 goto end;
1295 else if (stat.cbSize.u.LowPart < sizeof(PROPERTYSETHEADER) +
1296 sizeof(FORMATIDOFFSET))
1298 WARN("stream too small\n");
1299 hr = STG_E_INVALIDHEADER;
1300 goto end;
1302 seek.QuadPart = 0;
1303 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1304 if (FAILED(hr))
1305 goto end;
1306 hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr);
1307 /* I've only seen reserved == 1, but the article says I shouldn't disallow
1308 * higher values.
1310 if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1)
1312 WARN("bad magic in prop set header\n");
1313 hr = STG_E_INVALIDHEADER;
1314 goto end;
1316 if (hdr.wFormat != 0 && hdr.wFormat != 1)
1318 WARN("bad format version %d\n", hdr.wFormat);
1319 hr = STG_E_INVALIDHEADER;
1320 goto end;
1322 This->format = hdr.wFormat;
1323 memcpy(&This->clsid, &hdr.clsid, sizeof(This->clsid));
1324 This->originatorOS = hdr.dwOSVer;
1325 if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC)
1326 WARN("File comes from a Mac, strings will probably be screwed up\n");
1327 hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset);
1328 if (FAILED(hr))
1329 goto end;
1330 if (fmtOffset.dwOffset > stat.cbSize.u.LowPart)
1332 WARN("invalid offset %ld (stream length is %ld)\n", fmtOffset.dwOffset,
1333 stat.cbSize.u.LowPart);
1334 hr = STG_E_INVALIDHEADER;
1335 goto end;
1337 /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
1338 * follow not one, but two sections. The first is the standard properties
1339 * for the document summary information, and the second is user-defined
1340 * properties. This is the only case in which multiple sections are
1341 * allowed.
1342 * Reading the second stream isn't implemented yet.
1344 hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, &sectionHdr);
1345 if (FAILED(hr))
1346 goto end;
1347 /* The section size includes the section header, so check it */
1348 if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER))
1350 WARN("section header too small, got %ld\n", sectionHdr.cbSection);
1351 hr = STG_E_INVALIDHEADER;
1352 goto end;
1354 buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection -
1355 sizeof(PROPERTYSECTIONHEADER));
1356 if (!buf)
1358 hr = STG_E_INSUFFICIENTMEMORY;
1359 goto end;
1361 hr = IStream_Read(This->stm, buf, sectionHdr.cbSection -
1362 sizeof(PROPERTYSECTIONHEADER), &count);
1363 if (FAILED(hr))
1364 goto end;
1365 TRACE("Reading %ld properties:\n", sectionHdr.cProperties);
1366 for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
1368 PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(buf +
1369 i * sizeof(PROPERTYIDOFFSET));
1371 if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) ||
1372 idOffset->dwOffset >= sectionHdr.cbSection - sizeof(DWORD))
1373 hr = STG_E_INVALIDPOINTER;
1374 else
1376 if (idOffset->propid >= PID_FIRST_USABLE &&
1377 idOffset->propid < PID_MIN_READONLY && idOffset->propid >
1378 This->highestProp)
1379 This->highestProp = idOffset->propid;
1380 if (idOffset->propid == PID_DICTIONARY)
1382 /* Don't read the dictionary yet, its entries depend on the
1383 * code page. Just store the offset so we know to read it
1384 * later.
1386 dictOffset = idOffset->dwOffset;
1387 TRACE("Dictionary offset is %ld\n", dictOffset);
1389 else
1391 PROPVARIANT prop;
1393 if (SUCCEEDED(PropertyStorage_ReadProperty(This, &prop,
1394 buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER))))
1396 TRACE("Read property with ID 0x%08lx, type %d\n",
1397 idOffset->propid, prop.vt);
1398 switch(idOffset->propid)
1400 case PID_CODEPAGE:
1401 if (prop.vt == VT_I2)
1402 This->codePage = (UINT)prop.u.iVal;
1403 break;
1404 case PID_LOCALE:
1405 if (prop.vt == VT_I4)
1406 This->locale = (LCID)prop.u.lVal;
1407 break;
1408 case PID_BEHAVIOR:
1409 if (prop.vt == VT_I4 && prop.u.lVal)
1410 This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
1411 /* The format should already be 1, but just in case */
1412 This->format = 1;
1413 break;
1414 default:
1415 hr = PropertyStorage_StorePropWithId(This,
1416 idOffset->propid, &prop, This->codePage);
1422 if (!This->codePage)
1424 /* default to Unicode unless told not to, as specified here:
1425 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
1427 if (This->grfFlags & PROPSETFLAG_ANSI)
1428 This->codePage = GetACP();
1429 else
1430 This->codePage = CP_UNICODE;
1432 if (!This->locale)
1433 This->locale = LOCALE_SYSTEM_DEFAULT;
1434 TRACE("Code page is %d, locale is %ld\n", This->codePage, This->locale);
1435 if (dictOffset)
1436 hr = PropertyStorage_ReadDictionary(This,
1437 buf + dictOffset - sizeof(PROPERTYSECTIONHEADER));
1439 end:
1440 HeapFree(GetProcessHeap(), 0, buf);
1441 if (FAILED(hr))
1443 dictionary_destroy(This->name_to_propid);
1444 This->name_to_propid = NULL;
1445 dictionary_destroy(This->propid_to_name);
1446 This->propid_to_name = NULL;
1447 dictionary_destroy(This->propid_to_prop);
1448 This->propid_to_prop = NULL;
1450 return hr;
1453 static void PropertyStorage_MakeHeader(PropertyStorage_impl *This,
1454 PROPERTYSETHEADER *hdr)
1456 assert(This);
1457 assert(hdr);
1458 StorageUtl_WriteWord((BYTE *)&hdr->wByteOrder, 0,
1459 PROPSETHDR_BYTEORDER_MAGIC);
1460 StorageUtl_WriteWord((BYTE *)&hdr->wFormat, 0, This->format);
1461 StorageUtl_WriteDWord((BYTE *)&hdr->dwOSVer, 0, This->originatorOS);
1462 StorageUtl_WriteGUID((BYTE *)&hdr->clsid, 0, &This->clsid);
1463 StorageUtl_WriteDWord((BYTE *)&hdr->reserved, 0, 1);
1466 static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl *This,
1467 FORMATIDOFFSET *fmtOffset)
1469 assert(This);
1470 assert(fmtOffset);
1471 StorageUtl_WriteGUID((BYTE *)fmtOffset, 0, &This->fmtid);
1472 StorageUtl_WriteDWord((BYTE *)fmtOffset, offsetof(FORMATIDOFFSET, dwOffset),
1473 sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET));
1476 static void PropertyStorage_MakeSectionHdr(DWORD cbSection, DWORD numProps,
1477 PROPERTYSECTIONHEADER *hdr)
1479 assert(hdr);
1480 StorageUtl_WriteDWord((BYTE *)hdr, 0, cbSection);
1481 StorageUtl_WriteDWord((BYTE *)hdr,
1482 offsetof(PROPERTYSECTIONHEADER, cProperties), numProps);
1485 static void PropertyStorage_MakePropertyIdOffset(DWORD propid, DWORD dwOffset,
1486 PROPERTYIDOFFSET *propIdOffset)
1488 assert(propIdOffset);
1489 StorageUtl_WriteDWord((BYTE *)propIdOffset, 0, propid);
1490 StorageUtl_WriteDWord((BYTE *)propIdOffset,
1491 offsetof(PROPERTYIDOFFSET, dwOffset), dwOffset);
1494 struct DictionaryClosure
1496 HRESULT hr;
1497 DWORD bytesWritten;
1500 static BOOL PropertyStorage_DictionaryWriter(const void *key,
1501 const void *value, void *extra, void *closure)
1503 PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
1504 struct DictionaryClosure *c = (struct DictionaryClosure *)closure;
1505 DWORD propid;
1506 ULONG count;
1508 assert(key);
1509 assert(This);
1510 assert(closure);
1511 StorageUtl_WriteDWord((LPBYTE)&propid, 0, (DWORD)value);
1512 c->hr = IStream_Write(This->stm, &propid, sizeof(propid), &count);
1513 if (FAILED(c->hr))
1514 goto end;
1515 c->bytesWritten += sizeof(DWORD);
1516 if (This->codePage == CP_UNICODE)
1518 DWORD keyLen, pad = 0;
1520 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0,
1521 (lstrlenW((LPWSTR)key) + 1) * sizeof(WCHAR));
1522 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1523 if (FAILED(c->hr))
1524 goto end;
1525 c->bytesWritten += sizeof(DWORD);
1526 /* Rather than allocate a copy, I'll swap the string to little-endian
1527 * in-place, write it, then swap it back.
1529 PropertyStorage_ByteSwapString(key, keyLen);
1530 c->hr = IStream_Write(This->stm, key, keyLen, &count);
1531 PropertyStorage_ByteSwapString(key, keyLen);
1532 if (FAILED(c->hr))
1533 goto end;
1534 c->bytesWritten += keyLen;
1535 if (keyLen % sizeof(DWORD))
1537 c->hr = IStream_Write(This->stm, &pad,
1538 sizeof(DWORD) - keyLen % sizeof(DWORD), &count);
1539 if (FAILED(c->hr))
1540 goto end;
1541 c->bytesWritten += sizeof(DWORD) - keyLen % sizeof(DWORD);
1544 else
1546 DWORD keyLen;
1548 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0, strlen((LPCSTR)key) + 1);
1549 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1550 if (FAILED(c->hr))
1551 goto end;
1552 c->bytesWritten += sizeof(DWORD);
1553 c->hr = IStream_Write(This->stm, key, keyLen, &count);
1554 if (FAILED(c->hr))
1555 goto end;
1556 c->bytesWritten += keyLen;
1558 end:
1559 return SUCCEEDED(c->hr);
1562 #define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)
1564 /* Writes the dictionary to the stream. Assumes without checking that the
1565 * dictionary isn't empty.
1567 static HRESULT PropertyStorage_WriteDictionaryToStream(
1568 PropertyStorage_impl *This, DWORD *sectionOffset)
1570 HRESULT hr;
1571 LARGE_INTEGER seek;
1572 PROPERTYIDOFFSET propIdOffset;
1573 ULONG count;
1574 DWORD dwTemp;
1575 struct DictionaryClosure closure;
1577 assert(This);
1578 assert(sectionOffset);
1580 /* The dictionary's always the first property written, so seek to its
1581 * spot.
1583 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER);
1584 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1585 if (FAILED(hr))
1586 goto end;
1587 PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY, *sectionOffset,
1588 &propIdOffset);
1589 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1590 if (FAILED(hr))
1591 goto end;
1593 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1594 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1595 if (FAILED(hr))
1596 goto end;
1597 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0,
1598 dictionary_num_entries(This->name_to_propid));
1599 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1600 if (FAILED(hr))
1601 goto end;
1602 *sectionOffset += sizeof(dwTemp);
1604 closure.hr = S_OK;
1605 closure.bytesWritten = 0;
1606 dictionary_enumerate(This->name_to_propid, PropertyStorage_DictionaryWriter,
1607 &closure);
1608 hr = closure.hr;
1609 if (FAILED(hr))
1610 goto end;
1611 *sectionOffset += closure.bytesWritten;
1612 if (closure.bytesWritten % sizeof(DWORD))
1614 TRACE("adding %ld bytes of padding\n", sizeof(DWORD) -
1615 closure.bytesWritten % sizeof(DWORD));
1616 *sectionOffset += sizeof(DWORD) - closure.bytesWritten % sizeof(DWORD);
1619 end:
1620 return hr;
1623 static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This,
1624 DWORD propNum, DWORD propid, PROPVARIANT *var, DWORD *sectionOffset)
1626 HRESULT hr;
1627 LARGE_INTEGER seek;
1628 PROPERTYIDOFFSET propIdOffset;
1629 ULONG count;
1630 DWORD dwType, bytesWritten;
1632 assert(This);
1633 assert(var);
1634 assert(sectionOffset);
1636 TRACE("%p, %ld, 0x%08lx, (%d), (%ld)\n", This, propNum, propid, var->vt,
1637 *sectionOffset);
1639 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) +
1640 propNum * sizeof(PROPERTYIDOFFSET);
1641 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1642 if (FAILED(hr))
1643 goto end;
1644 PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset);
1645 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1646 if (FAILED(hr))
1647 goto end;
1649 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1650 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1651 if (FAILED(hr))
1652 goto end;
1653 StorageUtl_WriteDWord((LPBYTE)&dwType, 0, var->vt);
1654 hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count);
1655 if (FAILED(hr))
1656 goto end;
1657 *sectionOffset += sizeof(dwType);
1659 switch (var->vt)
1661 case VT_EMPTY:
1662 case VT_NULL:
1663 bytesWritten = 0;
1664 break;
1665 case VT_I1:
1666 case VT_UI1:
1667 hr = IStream_Write(This->stm, &var->u.cVal, sizeof(var->u.cVal),
1668 &count);
1669 bytesWritten = count;
1670 break;
1671 case VT_I2:
1672 case VT_UI2:
1674 WORD wTemp;
1676 StorageUtl_WriteWord((LPBYTE)&wTemp, 0, var->u.iVal);
1677 hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count);
1678 bytesWritten = count;
1679 break;
1681 case VT_I4:
1682 case VT_UI4:
1684 DWORD dwTemp;
1686 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, var->u.lVal);
1687 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1688 bytesWritten = count;
1689 break;
1691 case VT_LPSTR:
1693 DWORD len, dwTemp;
1695 if (This->codePage == CP_UNICODE)
1696 len = (lstrlenW(var->u.pwszVal) + 1) * sizeof(WCHAR);
1697 else
1698 len = lstrlenA(var->u.pszVal) + 1;
1699 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1700 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1701 if (FAILED(hr))
1702 goto end;
1703 hr = IStream_Write(This->stm, var->u.pszVal, len, &count);
1704 bytesWritten = count + sizeof(DWORD);
1705 break;
1707 case VT_LPWSTR:
1709 DWORD len = lstrlenW(var->u.pwszVal) + 1, dwTemp;
1711 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1712 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1713 if (FAILED(hr))
1714 goto end;
1715 hr = IStream_Write(This->stm, var->u.pwszVal, len * sizeof(WCHAR),
1716 &count);
1717 bytesWritten = count + sizeof(DWORD);
1718 break;
1720 case VT_FILETIME:
1722 FILETIME temp;
1724 StorageUtl_WriteULargeInteger((BYTE *)&temp, 0,
1725 (ULARGE_INTEGER *)&var->u.filetime);
1726 hr = IStream_Write(This->stm, &temp, sizeof(FILETIME), &count);
1727 bytesWritten = count;
1728 break;
1730 default:
1731 FIXME("unsupported type: %d\n", var->vt);
1732 return STG_E_INVALIDPARAMETER;
1735 if (SUCCEEDED(hr))
1737 *sectionOffset += bytesWritten;
1738 if (bytesWritten % sizeof(DWORD))
1740 TRACE("adding %ld bytes of padding\n", sizeof(DWORD) -
1741 bytesWritten % sizeof(DWORD));
1742 *sectionOffset += sizeof(DWORD) - bytesWritten % sizeof(DWORD);
1746 end:
1747 return hr;
1750 struct PropertyClosure
1752 HRESULT hr;
1753 DWORD propNum;
1754 DWORD *sectionOffset;
1757 static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value,
1758 void *extra, void *closure)
1760 PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
1761 struct PropertyClosure *c = (struct PropertyClosure *)closure;
1763 assert(key);
1764 assert(value);
1765 assert(extra);
1766 assert(closure);
1767 c->hr = PropertyStorage_WritePropertyToStream(This,
1768 c->propNum++, (DWORD)key, (PROPVARIANT *)value, c->sectionOffset);
1769 return SUCCEEDED(c->hr);
1772 static HRESULT PropertyStorage_WritePropertiesToStream(
1773 PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset)
1775 struct PropertyClosure closure;
1777 assert(This);
1778 assert(sectionOffset);
1779 closure.hr = S_OK;
1780 closure.propNum = startingPropNum;
1781 closure.sectionOffset = sectionOffset;
1782 dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter,
1783 &closure);
1784 return closure.hr;
1787 static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This)
1789 HRESULT hr;
1790 ULONG count = 0;
1791 LARGE_INTEGER seek = { {0} };
1792 PROPERTYSETHEADER hdr;
1793 FORMATIDOFFSET fmtOffset;
1795 assert(This);
1796 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1797 if (FAILED(hr))
1798 goto end;
1799 PropertyStorage_MakeHeader(This, &hdr);
1800 hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count);
1801 if (FAILED(hr))
1802 goto end;
1803 if (count != sizeof(hdr))
1805 hr = STG_E_WRITEFAULT;
1806 goto end;
1809 PropertyStorage_MakeFmtIdOffset(This, &fmtOffset);
1810 hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count);
1811 if (FAILED(hr))
1812 goto end;
1813 if (count != sizeof(fmtOffset))
1815 hr = STG_E_WRITEFAULT;
1816 goto end;
1818 hr = S_OK;
1820 end:
1821 return hr;
1824 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This)
1826 PROPERTYSECTIONHEADER sectionHdr;
1827 HRESULT hr;
1828 ULONG count;
1829 LARGE_INTEGER seek;
1830 DWORD numProps, prop, sectionOffset, dwTemp;
1831 PROPVARIANT var;
1833 assert(This);
1835 PropertyStorage_WriteHeadersToStream(This);
1837 /* Count properties. Always at least one property, the code page */
1838 numProps = 1;
1839 if (dictionary_num_entries(This->name_to_propid))
1840 numProps++;
1841 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1842 numProps++;
1843 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1844 numProps++;
1845 numProps += dictionary_num_entries(This->propid_to_prop);
1847 /* Write section header with 0 bytes right now, I'll adjust it after
1848 * writing properties.
1850 PropertyStorage_MakeSectionHdr(0, numProps, &sectionHdr);
1851 seek.QuadPart = SECTIONHEADER_OFFSET;
1852 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1853 if (FAILED(hr))
1854 goto end;
1855 hr = IStream_Write(This->stm, &sectionHdr, sizeof(sectionHdr), &count);
1856 if (FAILED(hr))
1857 goto end;
1859 prop = 0;
1860 sectionOffset = sizeof(PROPERTYSECTIONHEADER) +
1861 numProps * sizeof(PROPERTYIDOFFSET);
1863 if (dictionary_num_entries(This->name_to_propid))
1865 prop++;
1866 hr = PropertyStorage_WriteDictionaryToStream(This, &sectionOffset);
1867 if (FAILED(hr))
1868 goto end;
1871 PropVariantInit(&var);
1873 var.vt = VT_I2;
1874 var.u.iVal = This->codePage;
1875 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE,
1876 &var, &sectionOffset);
1877 if (FAILED(hr))
1878 goto end;
1880 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1882 var.vt = VT_I4;
1883 var.u.lVal = This->locale;
1884 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE,
1885 &var, &sectionOffset);
1886 if (FAILED(hr))
1887 goto end;
1890 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1892 var.vt = VT_I4;
1893 var.u.lVal = 1;
1894 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR,
1895 &var, &sectionOffset);
1896 if (FAILED(hr))
1897 goto end;
1900 hr = PropertyStorage_WritePropertiesToStream(This, prop, &sectionOffset);
1901 if (FAILED(hr))
1902 goto end;
1904 /* Now write the byte count of the section */
1905 seek.QuadPart = SECTIONHEADER_OFFSET;
1906 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1907 if (FAILED(hr))
1908 goto end;
1909 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, sectionOffset);
1910 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1912 end:
1913 return hr;
1916 /***********************************************************************
1917 * PropertyStorage_Construct
1919 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This)
1921 assert(This);
1922 dictionary_destroy(This->name_to_propid);
1923 This->name_to_propid = NULL;
1924 dictionary_destroy(This->propid_to_name);
1925 This->propid_to_name = NULL;
1926 dictionary_destroy(This->propid_to_prop);
1927 This->propid_to_prop = NULL;
1930 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This)
1932 HRESULT hr = S_OK;
1934 assert(This);
1935 This->name_to_propid = dictionary_create(
1936 PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy,
1937 This);
1938 if (!This->name_to_propid)
1940 hr = STG_E_INSUFFICIENTMEMORY;
1941 goto end;
1943 This->propid_to_name = dictionary_create(PropertyStorage_PropCompare,
1944 NULL, This);
1945 if (!This->propid_to_name)
1947 hr = STG_E_INSUFFICIENTMEMORY;
1948 goto end;
1950 This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
1951 PropertyStorage_PropertyDestroy, This);
1952 if (!This->propid_to_prop)
1954 hr = STG_E_INSUFFICIENTMEMORY;
1955 goto end;
1957 end:
1958 if (FAILED(hr))
1959 PropertyStorage_DestroyDictionaries(This);
1960 return hr;
1963 static HRESULT PropertyStorage_BaseConstruct(IStream *stm,
1964 REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps)
1966 HRESULT hr = S_OK;
1968 assert(pps);
1969 assert(rfmtid);
1970 *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps);
1971 if (!*pps)
1972 return E_OUTOFMEMORY;
1974 (*pps)->vtbl = &IPropertyStorage_Vtbl;
1975 (*pps)->ref = 1;
1976 InitializeCriticalSection(&(*pps)->cs);
1977 (*pps)->stm = stm;
1978 memcpy(&(*pps)->fmtid, rfmtid, sizeof((*pps)->fmtid));
1979 (*pps)->grfMode = grfMode;
1981 hr = PropertyStorage_CreateDictionaries(*pps);
1982 if (FAILED(hr))
1984 IStream_Release(stm);
1985 DeleteCriticalSection(&(*pps)->cs);
1986 HeapFree(GetProcessHeap(), 0, *pps);
1987 *pps = NULL;
1990 return hr;
1993 static HRESULT PropertyStorage_ConstructFromStream(IStream *stm,
1994 REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps)
1996 PropertyStorage_impl *ps;
1997 HRESULT hr;
1999 assert(pps);
2000 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2001 if (SUCCEEDED(hr))
2003 hr = PropertyStorage_ReadFromStream(ps);
2004 if (SUCCEEDED(hr))
2006 *pps = (IPropertyStorage *)ps;
2007 TRACE("PropertyStorage %p constructed\n", ps);
2008 hr = S_OK;
2010 else
2012 PropertyStorage_DestroyDictionaries(ps);
2013 HeapFree(GetProcessHeap(), 0, ps);
2016 return hr;
2019 static HRESULT PropertyStorage_ConstructEmpty(IStream *stm,
2020 REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
2022 PropertyStorage_impl *ps;
2023 HRESULT hr;
2025 assert(pps);
2026 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2027 if (SUCCEEDED(hr))
2029 ps->format = 0;
2030 ps->grfFlags = grfFlags;
2031 if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2032 ps->format = 1;
2033 /* default to Unicode unless told not to, as specified here:
2034 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2036 if (ps->grfFlags & PROPSETFLAG_ANSI)
2037 ps->codePage = GetACP();
2038 else
2039 ps->codePage = CP_UNICODE;
2040 ps->locale = LOCALE_SYSTEM_DEFAULT;
2041 TRACE("Code page is %d, locale is %ld\n", ps->codePage, ps->locale);
2042 *pps = (IPropertyStorage *)ps;
2043 TRACE("PropertyStorage %p constructed\n", ps);
2044 hr = S_OK;
2046 return hr;
2050 /***********************************************************************
2051 * Implementation of IPropertySetStorage
2054 /************************************************************************
2055 * IPropertySetStorage_fnQueryInterface (IUnknown)
2057 * This method forwards to the common QueryInterface implementation
2059 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
2060 IPropertySetStorage *ppstg,
2061 REFIID riid,
2062 void** ppvObject)
2064 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2065 return IStorage_QueryInterface( (IStorage*)This, riid, ppvObject );
2068 /************************************************************************
2069 * IPropertySetStorage_fnAddRef (IUnknown)
2071 * This method forwards to the common AddRef implementation
2073 static ULONG WINAPI IPropertySetStorage_fnAddRef(
2074 IPropertySetStorage *ppstg)
2076 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2077 return IStorage_AddRef( (IStorage*)This );
2080 /************************************************************************
2081 * IPropertySetStorage_fnRelease (IUnknown)
2083 * This method forwards to the common Release implementation
2085 static ULONG WINAPI IPropertySetStorage_fnRelease(
2086 IPropertySetStorage *ppstg)
2088 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2089 return IStorage_Release( (IStorage*)This );
2092 /************************************************************************
2093 * IPropertySetStorage_fnCreate (IPropertySetStorage)
2095 static HRESULT WINAPI IPropertySetStorage_fnCreate(
2096 IPropertySetStorage *ppstg,
2097 REFFMTID rfmtid,
2098 const CLSID* pclsid,
2099 DWORD grfFlags,
2100 DWORD grfMode,
2101 IPropertyStorage** ppprstg)
2103 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2104 WCHAR name[CCH_MAX_PROPSTG_NAME];
2105 IStream *stm = NULL;
2106 HRESULT r;
2108 TRACE("%p %s %08lx %08lx %p\n", This, debugstr_guid(rfmtid), grfFlags,
2109 grfMode, ppprstg);
2111 /* be picky */
2112 if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
2114 r = STG_E_INVALIDFLAG;
2115 goto end;
2118 if (!rfmtid)
2120 r = E_INVALIDARG;
2121 goto end;
2124 /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
2125 * storage, not a stream. For now, disallow it.
2127 if (grfFlags & PROPSETFLAG_NONSIMPLE)
2129 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2130 r = STG_E_INVALIDFLAG;
2131 goto end;
2134 r = FmtIdToPropStgName(rfmtid, name);
2135 if (FAILED(r))
2136 goto end;
2138 r = IStorage_CreateStream( (IStorage*)This, name, grfMode, 0, 0, &stm );
2139 if (FAILED(r))
2140 goto end;
2142 r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg);
2144 end:
2145 TRACE("returning 0x%08lx\n", r);
2146 return r;
2149 /************************************************************************
2150 * IPropertySetStorage_fnOpen (IPropertySetStorage)
2152 static HRESULT WINAPI IPropertySetStorage_fnOpen(
2153 IPropertySetStorage *ppstg,
2154 REFFMTID rfmtid,
2155 DWORD grfMode,
2156 IPropertyStorage** ppprstg)
2158 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2159 IStream *stm = NULL;
2160 WCHAR name[CCH_MAX_PROPSTG_NAME];
2161 HRESULT r;
2163 TRACE("%p %s %08lx %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
2165 /* be picky */
2166 if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
2167 grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
2169 r = STG_E_INVALIDFLAG;
2170 goto end;
2173 if (!rfmtid)
2175 r = E_INVALIDARG;
2176 goto end;
2179 r = FmtIdToPropStgName(rfmtid, name);
2180 if (FAILED(r))
2181 goto end;
2183 r = IStorage_OpenStream((IStorage*) This, name, 0, grfMode, 0, &stm );
2184 if (FAILED(r))
2185 goto end;
2187 r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg);
2189 end:
2190 TRACE("returning 0x%08lx\n", r);
2191 return r;
2194 /************************************************************************
2195 * IPropertySetStorage_fnDelete (IPropertySetStorage)
2197 static HRESULT WINAPI IPropertySetStorage_fnDelete(
2198 IPropertySetStorage *ppstg,
2199 REFFMTID rfmtid)
2201 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2202 IStorage *stg = NULL;
2203 WCHAR name[CCH_MAX_PROPSTG_NAME];
2204 HRESULT r;
2206 TRACE("%p %s\n", This, debugstr_guid(rfmtid));
2208 if (!rfmtid)
2209 return E_INVALIDARG;
2211 r = FmtIdToPropStgName(rfmtid, name);
2212 if (FAILED(r))
2213 return r;
2215 stg = (IStorage*) This;
2216 return IStorage_DestroyElement(stg, name);
2219 /************************************************************************
2220 * IPropertySetStorage_fnEnum (IPropertySetStorage)
2222 static HRESULT WINAPI IPropertySetStorage_fnEnum(
2223 IPropertySetStorage *ppstg,
2224 IEnumSTATPROPSETSTG** ppenum)
2226 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2227 return create_EnumSTATPROPSETSTG(This, ppenum);
2230 /************************************************************************
2231 * Implement IEnumSTATPROPSETSTG using enumx
2233 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnQueryInterface(
2234 IEnumSTATPROPSETSTG *iface,
2235 REFIID riid,
2236 void** ppvObject)
2238 return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
2241 static ULONG WINAPI IEnumSTATPROPSETSTG_fnAddRef(
2242 IEnumSTATPROPSETSTG *iface)
2244 return enumx_AddRef((enumx_impl*)iface);
2247 static ULONG WINAPI IEnumSTATPROPSETSTG_fnRelease(
2248 IEnumSTATPROPSETSTG *iface)
2250 return enumx_Release((enumx_impl*)iface);
2253 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnNext(
2254 IEnumSTATPROPSETSTG *iface,
2255 ULONG celt,
2256 STATPROPSETSTG *rgelt,
2257 ULONG *pceltFetched)
2259 return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
2262 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnSkip(
2263 IEnumSTATPROPSETSTG *iface,
2264 ULONG celt)
2266 return enumx_Skip((enumx_impl*)iface, celt);
2269 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnReset(
2270 IEnumSTATPROPSETSTG *iface)
2272 return enumx_Reset((enumx_impl*)iface);
2275 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnClone(
2276 IEnumSTATPROPSETSTG *iface,
2277 IEnumSTATPROPSETSTG **ppenum)
2279 return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
2282 static HRESULT create_EnumSTATPROPSETSTG(
2283 StorageImpl *This,
2284 IEnumSTATPROPSETSTG** ppenum)
2286 IStorage *stg = (IStorage*) &This->base.lpVtbl;
2287 IEnumSTATSTG *penum = NULL;
2288 STATSTG stat;
2289 ULONG count;
2290 HRESULT r;
2291 STATPROPSETSTG statpss;
2292 enumx_impl *enumx;
2294 TRACE("%p %p\n", This, ppenum);
2296 enumx = enumx_allocate(&IID_IEnumSTATPROPSETSTG,
2297 &IEnumSTATPROPSETSTG_Vtbl,
2298 sizeof (STATPROPSETSTG));
2300 /* add all the property set elements into a list */
2301 r = IStorage_EnumElements(stg, 0, NULL, 0, &penum);
2302 if (FAILED(r))
2303 return E_OUTOFMEMORY;
2305 while (1)
2307 count = 0;
2308 r = IEnumSTATSTG_Next(penum, 1, &stat, &count);
2309 if (FAILED(r))
2310 break;
2311 if (!count)
2312 break;
2313 if (!stat.pwcsName)
2314 continue;
2315 if (stat.pwcsName[0] == 5 && stat.type == STGTY_STREAM)
2317 PropStgNameToFmtId(stat.pwcsName, &statpss.fmtid);
2318 TRACE("adding %s (%s)\n", debugstr_w(stat.pwcsName),
2319 debugstr_guid(&statpss.fmtid));
2320 statpss.mtime = stat.mtime;
2321 statpss.atime = stat.atime;
2322 statpss.ctime = stat.ctime;
2323 statpss.grfFlags = stat.grfMode;
2324 memcpy(&statpss.clsid, &stat.clsid, sizeof stat.clsid);
2325 enumx_add_element(enumx, &statpss);
2327 CoTaskMemFree(stat.pwcsName);
2329 IEnumSTATSTG_Release(penum);
2331 *ppenum = (IEnumSTATPROPSETSTG*) enumx;
2333 return S_OK;
2336 /************************************************************************
2337 * Implement IEnumSTATPROPSTG using enumx
2339 static HRESULT WINAPI IEnumSTATPROPSTG_fnQueryInterface(
2340 IEnumSTATPROPSTG *iface,
2341 REFIID riid,
2342 void** ppvObject)
2344 return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
2347 static ULONG WINAPI IEnumSTATPROPSTG_fnAddRef(
2348 IEnumSTATPROPSTG *iface)
2350 return enumx_AddRef((enumx_impl*)iface);
2353 static ULONG WINAPI IEnumSTATPROPSTG_fnRelease(
2354 IEnumSTATPROPSTG *iface)
2356 return enumx_Release((enumx_impl*)iface);
2359 static HRESULT WINAPI IEnumSTATPROPSTG_fnNext(
2360 IEnumSTATPROPSTG *iface,
2361 ULONG celt,
2362 STATPROPSTG *rgelt,
2363 ULONG *pceltFetched)
2365 return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
2368 static HRESULT WINAPI IEnumSTATPROPSTG_fnSkip(
2369 IEnumSTATPROPSTG *iface,
2370 ULONG celt)
2372 return enumx_Skip((enumx_impl*)iface, celt);
2375 static HRESULT WINAPI IEnumSTATPROPSTG_fnReset(
2376 IEnumSTATPROPSTG *iface)
2378 return enumx_Reset((enumx_impl*)iface);
2381 static HRESULT WINAPI IEnumSTATPROPSTG_fnClone(
2382 IEnumSTATPROPSTG *iface,
2383 IEnumSTATPROPSTG **ppenum)
2385 return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
2388 static BOOL prop_enum_stat(const void *k, const void *v, void *extra, void *arg)
2390 enumx_impl *enumx = arg;
2391 PROPID propid = (PROPID) k;
2392 const PROPVARIANT *prop = v;
2393 STATPROPSTG stat;
2395 stat.lpwstrName = NULL;
2396 stat.propid = propid;
2397 stat.vt = prop->vt;
2399 enumx_add_element(enumx, &stat);
2401 return TRUE;
2404 static HRESULT create_EnumSTATPROPSTG(
2405 PropertyStorage_impl *This,
2406 IEnumSTATPROPSTG** ppenum)
2408 enumx_impl *enumx;
2410 TRACE("%p %p\n", This, ppenum);
2412 enumx = enumx_allocate(&IID_IEnumSTATPROPSTG,
2413 &IEnumSTATPROPSTG_Vtbl,
2414 sizeof (STATPROPSTG));
2416 dictionary_enumerate(This->propid_to_prop, prop_enum_stat, enumx);
2418 *ppenum = (IEnumSTATPROPSTG*) enumx;
2420 return S_OK;
2423 /***********************************************************************
2424 * vtables
2426 const IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
2428 IPropertySetStorage_fnQueryInterface,
2429 IPropertySetStorage_fnAddRef,
2430 IPropertySetStorage_fnRelease,
2431 IPropertySetStorage_fnCreate,
2432 IPropertySetStorage_fnOpen,
2433 IPropertySetStorage_fnDelete,
2434 IPropertySetStorage_fnEnum
2437 static const IPropertyStorageVtbl IPropertyStorage_Vtbl =
2439 IPropertyStorage_fnQueryInterface,
2440 IPropertyStorage_fnAddRef,
2441 IPropertyStorage_fnRelease,
2442 IPropertyStorage_fnReadMultiple,
2443 IPropertyStorage_fnWriteMultiple,
2444 IPropertyStorage_fnDeleteMultiple,
2445 IPropertyStorage_fnReadPropertyNames,
2446 IPropertyStorage_fnWritePropertyNames,
2447 IPropertyStorage_fnDeletePropertyNames,
2448 IPropertyStorage_fnCommit,
2449 IPropertyStorage_fnRevert,
2450 IPropertyStorage_fnEnum,
2451 IPropertyStorage_fnSetTimes,
2452 IPropertyStorage_fnSetClass,
2453 IPropertyStorage_fnStat,
2456 static const IEnumSTATPROPSETSTGVtbl IEnumSTATPROPSETSTG_Vtbl =
2458 IEnumSTATPROPSETSTG_fnQueryInterface,
2459 IEnumSTATPROPSETSTG_fnAddRef,
2460 IEnumSTATPROPSETSTG_fnRelease,
2461 IEnumSTATPROPSETSTG_fnNext,
2462 IEnumSTATPROPSETSTG_fnSkip,
2463 IEnumSTATPROPSETSTG_fnReset,
2464 IEnumSTATPROPSETSTG_fnClone,
2467 static const IEnumSTATPROPSTGVtbl IEnumSTATPROPSTG_Vtbl =
2469 IEnumSTATPROPSTG_fnQueryInterface,
2470 IEnumSTATPROPSTG_fnAddRef,
2471 IEnumSTATPROPSTG_fnRelease,
2472 IEnumSTATPROPSTG_fnNext,
2473 IEnumSTATPROPSTG_fnSkip,
2474 IEnumSTATPROPSTG_fnReset,
2475 IEnumSTATPROPSTG_fnClone,
2478 /***********************************************************************
2479 * Format ID <-> name conversion
2481 static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
2482 'I','n','f','o','r','m','a','t','i','o','n',0 };
2483 static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t',
2484 'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
2486 #define BITS_PER_BYTE 8
2487 #define CHARMASK 0x1f
2488 #define BITS_IN_CHARMASK 5
2489 #define NUM_ALPHA_CHARS 26
2491 /***********************************************************************
2492 * FmtIdToPropStgName [ole32.@]
2493 * Returns the storage name of the format ID rfmtid.
2494 * PARAMS
2495 * rfmtid [I] Format ID for which to return a storage name
2496 * str [O] Storage name associated with rfmtid.
2498 * RETURNS
2499 * E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise.
2501 * NOTES
2502 * str must be at least CCH_MAX_PROPSTG_NAME characters in length.
2503 * Based on the algorithm described here:
2504 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2506 HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str)
2508 static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
2510 TRACE("%s, %p\n", debugstr_guid(rfmtid), str);
2512 if (!rfmtid) return E_INVALIDARG;
2513 if (!str) return E_INVALIDARG;
2515 if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
2516 lstrcpyW(str, szSummaryInfo);
2517 else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
2518 lstrcpyW(str, szDocSummaryInfo);
2519 else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid))
2520 lstrcpyW(str, szDocSummaryInfo);
2521 else
2523 BYTE *fmtptr;
2524 WCHAR *pstr = str;
2525 ULONG bitsRemaining = BITS_PER_BYTE;
2527 *pstr++ = 5;
2528 for (fmtptr = (BYTE *)rfmtid; fmtptr < (BYTE *)rfmtid + sizeof(FMTID); )
2530 ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
2532 if (bitsRemaining >= BITS_IN_CHARMASK)
2534 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
2535 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
2536 *pstr <= 'z')
2537 *pstr += 'A' - 'a';
2538 pstr++;
2539 bitsRemaining -= BITS_IN_CHARMASK;
2540 if (bitsRemaining == 0)
2542 fmtptr++;
2543 bitsRemaining = BITS_PER_BYTE;
2546 else
2548 if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID))
2549 i |= *fmtptr << bitsRemaining;
2550 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
2551 bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK;
2554 *pstr = 0;
2556 TRACE("returning %s\n", debugstr_w(str));
2557 return S_OK;
2560 /***********************************************************************
2561 * PropStgNameToFmtId [ole32.@]
2562 * Returns the format ID corresponding to the given name.
2563 * PARAMS
2564 * str [I] Storage name to convert to a format ID.
2565 * rfmtid [O] Format ID corresponding to str.
2567 * RETURNS
2568 * E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to
2569 * a format ID, S_OK otherwise.
2571 * NOTES
2572 * Based on the algorithm described here:
2573 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2575 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid)
2577 HRESULT hr = STG_E_INVALIDNAME;
2579 TRACE("%s, %p\n", debugstr_w(str), rfmtid);
2581 if (!rfmtid) return E_INVALIDARG;
2582 if (!str) return STG_E_INVALIDNAME;
2584 if (!lstrcmpiW(str, szDocSummaryInfo))
2586 memcpy(rfmtid, &FMTID_DocSummaryInformation, sizeof(*rfmtid));
2587 hr = S_OK;
2589 else if (!lstrcmpiW(str, szSummaryInfo))
2591 memcpy(rfmtid, &FMTID_SummaryInformation, sizeof(*rfmtid));
2592 hr = S_OK;
2594 else
2596 ULONG bits;
2597 BYTE *fmtptr = (BYTE *)rfmtid - 1;
2598 const WCHAR *pstr = str;
2600 memset(rfmtid, 0, sizeof(*rfmtid));
2601 for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE;
2602 bits += BITS_IN_CHARMASK)
2604 ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored;
2605 WCHAR wc;
2607 if (bitsUsed == 0)
2608 fmtptr++;
2609 wc = *++pstr - 'A';
2610 if (wc > NUM_ALPHA_CHARS)
2612 wc += 'A' - 'a';
2613 if (wc > NUM_ALPHA_CHARS)
2615 wc += 'a' - '0' + NUM_ALPHA_CHARS;
2616 if (wc > CHARMASK)
2618 WARN("invalid character (%d)\n", *pstr);
2619 goto end;
2623 *fmtptr |= wc << bitsUsed;
2624 bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK);
2625 if (bitsStored < BITS_IN_CHARMASK)
2627 wc >>= BITS_PER_BYTE - bitsUsed;
2628 if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE)
2630 if (wc != 0)
2632 WARN("extra bits\n");
2633 goto end;
2635 break;
2637 fmtptr++;
2638 *fmtptr |= (BYTE)wc;
2641 hr = S_OK;
2643 end:
2644 return hr;