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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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: There's a lot missing in here. Biggies:
34 * - There are all sorts of restricions I don't honor, like maximum property
35 * set byte size, maximum property name length
36 * - Certain bogus files could result in reading past the end of a buffer.
37 * - Write support is missing.
38 * - This will probably fail on big-endian machines, especially reading and
40 * - Mac-generated files won't be read correctly, even if they're little
41 * endian, because I disregard whether the generator was a Mac. This means
42 * strings will probably be munged (as I don't understand Mac scripts.)
43 * - Not all PROPVARIANT types are supported.
44 * There are lots more unimplemented features, see FIXMEs below.
54 #define NONAMELESSUNION
55 #define NONAMELESSSTRUCT
61 #include "wine/unicode.h"
62 #include "wine/debug.h"
63 #include "dictionary.h"
64 #include "storage32.h"
66 WINE_DEFAULT_DEBUG_CHANNEL(storage);
68 #define _IPropertySetStorage_Offset ((int)(&(((StorageImpl*)0)->base.pssVtbl)))
69 #define _ICOM_THIS_From_IPropertySetStorage(class, name) \
70 class* This = (class*)(((char*)name)-_IPropertySetStorage_Offset)
72 /* These are documented in MSDN, e.g.
73 * http://msdn.microsoft.com/library/en-us/stg/stg/property_set_header.asp
74 * http://msdn.microsoft.com/library/library/en-us/stg/stg/section.asp
75 * but they don't seem to be in any header file.
77 #define PROPSETHDR_BYTEORDER_MAGIC 0xfffe
78 #define PROPSETHDR_OSVER_KIND_WIN16 0
79 #define PROPSETHDR_OSVER_KIND_MAC 1
80 #define PROPSETHDR_OSVER_KIND_WIN32 2
82 /* The format version (and what it implies) is described here:
83 * http://msdn.microsoft.com/library/en-us/stg/stg/format_version.asp
85 typedef struct tagPROPERTYSETHEADER
87 WORD wByteOrder; /* always 0xfffe */
88 WORD wFormat; /* can be zero or one */
89 DWORD dwOSVer; /* OS version of originating system */
90 CLSID clsid; /* application CLSID */
91 DWORD reserved; /* always 1 */
94 typedef struct tagFORMATIDOFFSET
97 DWORD dwOffset; /* from beginning of stream */
100 typedef struct tagPROPERTYSECTIONHEADER
104 } PROPERTYSECTIONHEADER;
106 typedef struct tagPROPERTYIDOFFSET
112 /* Initializes the property storage from the stream (and undoes any uncommitted
113 * changes in the process.) Returns an error if there is an error reading or
114 * if the stream format doesn't match what's expected.
116 static HRESULT PropertyStorage_ReadFromStream(IPropertyStorage *iface);
118 static HRESULT PropertyStorage_WriteToStream(IPropertyStorage *iface);
120 static IPropertyStorageVtbl IPropertyStorage_Vtbl;
122 /***********************************************************************
123 * Implementation of IPropertyStorage
125 typedef struct tagPropertyStorage_impl
127 IPropertyStorageVtbl *vtbl;
140 struct dictionary *name_to_propid;
141 struct dictionary *propid_to_name;
142 struct dictionary *propid_to_prop;
143 } PropertyStorage_impl;
145 /************************************************************************
146 * IPropertyStorage_fnQueryInterface (IPropertyStorage)
148 static HRESULT WINAPI IPropertyStorage_fnQueryInterface(
149 IPropertyStorage *iface,
153 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
155 if ( (This==0) || (ppvObject==0) )
160 if (IsEqualGUID(&IID_IUnknown, riid) ||
161 IsEqualGUID(&IID_IPropertyStorage, riid))
163 IPropertyStorage_AddRef(iface);
164 *ppvObject = (IPropertyStorage*)iface;
168 return E_NOINTERFACE;
171 /************************************************************************
172 * IPropertyStorage_fnAddRef (IPropertyStorage)
174 static ULONG WINAPI IPropertyStorage_fnAddRef(
175 IPropertyStorage *iface)
177 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
178 return InterlockedIncrement(&This->ref);
181 /************************************************************************
182 * IPropertyStorage_fnRelease (IPropertyStorage)
184 static ULONG WINAPI IPropertyStorage_fnRelease(
185 IPropertyStorage *iface)
187 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
190 ref = InterlockedDecrement(&This->ref);
193 TRACE("Destroying %p\n", This);
194 IStream_Release(This->stm);
195 DeleteCriticalSection(&This->cs);
196 dictionary_destroy(This->name_to_propid);
197 dictionary_destroy(This->propid_to_name);
198 dictionary_destroy(This->propid_to_prop);
199 HeapFree(GetProcessHeap(), 0, This);
204 static PROPVARIANT *PropertyStorage_FindProperty(PropertyStorage_impl *This,
207 PROPVARIANT *ret = NULL;
211 dictionary_find(This->propid_to_prop, (void *)propid, (void **)&ret);
215 static PROPVARIANT *PropertyStorage_FindPropertyByName(
216 PropertyStorage_impl *This, LPCWSTR name)
218 PROPVARIANT *ret = NULL;
223 if (dictionary_find(This->name_to_propid, name, (void **)&propid))
224 ret = PropertyStorage_FindProperty(This, (PROPID)propid);
228 static LPWSTR PropertyStorage_FindPropertyNameById(PropertyStorage_impl *This,
235 dictionary_find(This->propid_to_name, (void *)propid, (void **)&ret);
239 /************************************************************************
240 * IPropertyStorage_fnReadMultiple (IPropertyStorage)
242 static HRESULT WINAPI IPropertyStorage_fnReadMultiple(
243 IPropertyStorage* iface,
245 const PROPSPEC rgpspec[],
246 PROPVARIANT rgpropvar[])
248 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
252 TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
255 if (cpspec && (!rgpspec || !rgpropvar))
257 EnterCriticalSection(&This->cs);
258 for (i = 0; i < cpspec; i++)
260 PropVariantInit(&rgpropvar[i]);
261 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
263 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
264 rgpspec[i].u.lpwstr);
267 PropVariantCopy(&rgpropvar[i], prop);
271 PROPVARIANT *prop = PropertyStorage_FindProperty(This,
272 rgpspec[i].u.propid);
275 PropVariantCopy(&rgpropvar[i], prop);
278 LeaveCriticalSection(&This->cs);
282 static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
283 PROPID propid, const PROPVARIANT *propvar)
286 PROPVARIANT *prop = PropertyStorage_FindProperty(This, propid);
290 PropVariantClear(prop);
291 PropVariantCopy(prop, propvar);
295 prop = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
296 sizeof(PROPVARIANT));
299 PropVariantCopy(prop, propvar);
300 dictionary_insert(This->propid_to_prop, (void *)propid, prop);
303 hr = STG_E_INSUFFICIENTMEMORY;
308 /************************************************************************
309 * IPropertyStorage_fnWriteMultiple (IPropertyStorage)
311 static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
312 IPropertyStorage* iface,
314 const PROPSPEC rgpspec[],
315 const PROPVARIANT rgpropvar[],
316 PROPID propidNameFirst)
318 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
322 TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
325 if (cpspec && (!rgpspec || !rgpropvar))
327 if (!(This->grfMode & STGM_READWRITE))
328 return STG_E_ACCESSDENIED;
329 EnterCriticalSection(&This->cs);
331 This->originatorOS = (DWORD)MAKELONG(LOWORD(GetVersion()),
332 PROPSETHDR_OSVER_KIND_WIN32) ;
333 for (i = 0; i < cpspec; i++)
335 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
337 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
338 rgpspec[i].u.lpwstr);
341 PropVariantCopy(prop, &rgpropvar[i]);
344 if (propidNameFirst < PID_FIRST_USABLE ||
345 propidNameFirst >= PID_MIN_READONLY)
346 hr = STG_E_INVALIDPARAMETER;
349 PROPID nextId = max(propidNameFirst, This->highestProp + 1);
350 size_t len = strlenW(rgpspec[i].u.lpwstr) + 1;
351 LPWSTR name = HeapAlloc(GetProcessHeap(), 0,
352 len * sizeof(WCHAR));
354 strcpyW(name, rgpspec[i].u.lpwstr);
355 dictionary_insert(This->name_to_propid, name,
357 dictionary_insert(This->propid_to_name, (void *)nextId,
359 This->highestProp = nextId;
360 hr = PropertyStorage_StorePropWithId(This, nextId,
367 /* FIXME: certain propid's have special behavior. E.g., you can't
368 * set propid 0, and setting PID_BEHAVIOR affects the
371 hr = PropertyStorage_StorePropWithId(This, rgpspec[i].u.propid,
375 LeaveCriticalSection(&This->cs);
379 /************************************************************************
380 * IPropertyStorage_fnDeleteMultiple (IPropertyStorage)
382 static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
383 IPropertyStorage* iface,
385 const PROPSPEC rgpspec[])
387 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
391 TRACE("(%p, %ld, %p)\n", iface, cpspec, rgpspec);
396 if (!(This->grfMode & STGM_READWRITE))
397 return STG_E_ACCESSDENIED;
399 EnterCriticalSection(&This->cs);
400 for (i = 0; i < cpspec; i++)
402 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
406 if (dictionary_find(This->name_to_propid,
407 (void *)rgpspec[i].u.lpwstr, (void **)&propid))
408 dictionary_remove(This->propid_to_prop, (void *)propid);
412 /* FIXME: certain propid's have special meaning. For example,
413 * removing propid 0 is supposed to remove the dictionary, and
414 * removing PID_BEHAVIOR should change this to a case-insensitive
415 * property set. Unknown "read-only" propid's should be ignored.
417 dictionary_remove(This->propid_to_prop,
418 (void *)rgpspec[i].u.propid);
421 LeaveCriticalSection(&This->cs);
425 /************************************************************************
426 * IPropertyStorage_fnReadPropertyNames (IPropertyStorage)
428 static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames(
429 IPropertyStorage* iface,
431 const PROPID rgpropid[],
432 LPOLESTR rglpwstrName[])
434 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
438 TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
441 if (cpropid && (!rgpropid || !rglpwstrName))
443 /* MSDN says S_FALSE is returned if no strings matching rgpropid are found,
447 EnterCriticalSection(&This->cs);
448 for (i = 0; i < cpropid && SUCCEEDED(hr); i++)
450 LPWSTR name = PropertyStorage_FindPropertyNameById(This, rgpropid[i]);
454 size_t len = lstrlenW(name);
457 rglpwstrName[i] = CoTaskMemAlloc((len + 1) * sizeof(WCHAR));
459 memcpy(rglpwstrName, name, (len + 1) * sizeof(WCHAR));
461 hr = STG_E_INSUFFICIENTMEMORY;
464 rglpwstrName[i] = NULL;
466 LeaveCriticalSection(&This->cs);
470 /************************************************************************
471 * IPropertyStorage_fnWritePropertyNames (IPropertyStorage)
473 static HRESULT WINAPI IPropertyStorage_fnWritePropertyNames(
474 IPropertyStorage* iface,
476 const PROPID rgpropid[],
477 const LPOLESTR rglpwstrName[])
479 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
483 TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
486 if (cpropid && (!rgpropid || !rglpwstrName))
488 if (!(This->grfMode & STGM_READWRITE))
489 return STG_E_ACCESSDENIED;
491 EnterCriticalSection(&This->cs);
492 for (i = 0; i < cpropid; i++)
494 if (rgpropid[i] != PID_ILLEGAL)
496 size_t len = lstrlenW(rglpwstrName[i] + 1);
497 LPWSTR name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
499 strcpyW(name, rglpwstrName[i]);
500 dictionary_insert(This->name_to_propid, name, (void *)rgpropid[i]);
501 dictionary_insert(This->propid_to_name, (void *)rgpropid[i], name);
504 LeaveCriticalSection(&This->cs);
508 /************************************************************************
509 * IPropertyStorage_fnDeletePropertyNames (IPropertyStorage)
511 static HRESULT WINAPI IPropertyStorage_fnDeletePropertyNames(
512 IPropertyStorage* iface,
514 const PROPID rgpropid[])
516 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
520 TRACE("(%p, %ld, %p)\n", iface, cpropid, rgpropid);
523 if (cpropid && !rgpropid)
525 if (!(This->grfMode & STGM_READWRITE))
526 return STG_E_ACCESSDENIED;
528 EnterCriticalSection(&This->cs);
529 for (i = 0; i < cpropid; i++)
533 if (dictionary_find(This->propid_to_name, (void *)rgpropid[i],
536 dictionary_remove(This->propid_to_name, (void *)rgpropid[i]);
537 dictionary_remove(This->name_to_propid, name);
540 LeaveCriticalSection(&This->cs);
544 /************************************************************************
545 * IPropertyStorage_fnCommit (IPropertyStorage)
547 static HRESULT WINAPI IPropertyStorage_fnCommit(
548 IPropertyStorage* iface,
549 DWORD grfCommitFlags)
551 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
554 TRACE("(%p, 0x%08lx)\n", iface, grfCommitFlags);
557 if (!(This->grfMode & STGM_READWRITE))
558 return STG_E_ACCESSDENIED;
559 EnterCriticalSection(&This->cs);
561 hr = PropertyStorage_WriteToStream(iface);
564 LeaveCriticalSection(&This->cs);
568 /************************************************************************
569 * IPropertyStorage_fnRevert (IPropertyStorage)
571 static HRESULT WINAPI IPropertyStorage_fnRevert(
572 IPropertyStorage* iface)
575 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
577 TRACE("%p\n", iface);
581 EnterCriticalSection(&This->cs);
583 hr = PropertyStorage_ReadFromStream(iface);
586 LeaveCriticalSection(&This->cs);
590 /************************************************************************
591 * IPropertyStorage_fnEnum (IPropertyStorage)
593 static HRESULT WINAPI IPropertyStorage_fnEnum(
594 IPropertyStorage* iface,
595 IEnumSTATPROPSTG** ppenum)
601 /************************************************************************
602 * IPropertyStorage_fnSetTimes (IPropertyStorage)
604 static HRESULT WINAPI IPropertyStorage_fnSetTimes(
605 IPropertyStorage* iface,
606 const FILETIME* pctime,
607 const FILETIME* patime,
608 const FILETIME* pmtime)
614 /************************************************************************
615 * IPropertyStorage_fnSetClass (IPropertyStorage)
617 static HRESULT WINAPI IPropertyStorage_fnSetClass(
618 IPropertyStorage* iface,
621 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
623 TRACE("%p, %s\n", iface, debugstr_guid(clsid));
626 if (!(This->grfMode & STGM_READWRITE))
627 return STG_E_ACCESSDENIED;
628 memcpy(&This->clsid, clsid, sizeof(This->clsid));
633 /************************************************************************
634 * IPropertyStorage_fnStat (IPropertyStorage)
636 static HRESULT WINAPI IPropertyStorage_fnStat(
637 IPropertyStorage* iface,
638 STATPROPSETSTG* statpsstg)
640 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
644 TRACE("%p, %p\n", iface, statpsstg);
645 if (!This || !statpsstg)
648 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
651 memcpy(&statpsstg->fmtid, &This->fmtid, sizeof(statpsstg->fmtid));
652 memcpy(&statpsstg->clsid, &This->clsid, sizeof(statpsstg->clsid));
653 statpsstg->grfFlags = This->grfFlags;
654 memcpy(&statpsstg->mtime, &stat.mtime, sizeof(statpsstg->mtime));
655 memcpy(&statpsstg->ctime, &stat.ctime, sizeof(statpsstg->ctime));
656 memcpy(&statpsstg->atime, &stat.atime, sizeof(statpsstg->atime));
657 statpsstg->dwOSVersion = This->originatorOS;
662 static int PropertyStorage_PropNameCompare(const void *a, const void *b,
665 PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
667 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
668 return strcmpW((LPCWSTR)a, (LPCWSTR)b);
670 return strcmpiW((LPCWSTR)a, (LPCWSTR)b);
673 static void PropertyStorage_PropNameDestroy(void *k, void *d, void *extra)
675 HeapFree(GetProcessHeap(), 0, k);
678 static int PropertyStorage_PropCompare(const void *a, const void *b,
681 return (PROPID)a - (PROPID)b;
684 static void PropertyStorage_PropertyDestroy(void *k, void *d, void *extra)
686 PropVariantClear((PROPVARIANT *)d);
687 HeapFree(GetProcessHeap(), 0, d);
690 /* Reads the dictionary from the memory buffer beginning at ptr. Interprets
691 * the entries according to the values of This->codePage and This->locale.
692 * FIXME: there isn't any checking whether the read property extends past the
695 static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This,
698 DWORD numEntries = *(DWORD *)ptr;
701 ptr += sizeof(DWORD);
702 This->name_to_propid = dictionary_create(PropertyStorage_PropNameCompare,
703 PropertyStorage_PropNameDestroy, This);
704 This->propid_to_name = dictionary_create(PropertyStorage_PropCompare, NULL,
706 if (This->name_to_propid && This->propid_to_name)
710 for (i = 0; SUCCEEDED(hr) && i < numEntries; i++)
716 StorageUtl_ReadDWord(ptr, 0, &propid);
717 ptr += sizeof(PROPID);
718 StorageUtl_ReadDWord(ptr, 0, &cbEntry);
719 ptr += sizeof(DWORD);
720 /* FIXME: if host is big-endian, this'll suck to convert */
721 len = MultiByteToWideChar(This->codePage, 0, ptr, cbEntry, NULL, 0);
723 hr = HRESULT_FROM_WIN32(GetLastError());
726 LPWSTR name = HeapAlloc(GetProcessHeap(), 0,
727 len * sizeof(WCHAR));
731 MultiByteToWideChar(This->codePage, 0, ptr + sizeof(DWORD),
733 dictionary_insert(This->name_to_propid, name,
735 dictionary_insert(This->propid_to_name, (void *)propid,
737 TRACE("Property %s maps to id %ld\n", debugstr_w(name),
741 hr = STG_E_INSUFFICIENTMEMORY;
743 ptr += sizeof(DWORD) + cbEntry;
744 /* Unicode entries are padded to DWORD boundaries */
745 if (This->codePage == 1200 && cbEntry % sizeof(DWORD))
746 ptr += sizeof(DWORD) - (cbEntry % sizeof(DWORD));
751 /* one or the other failed, free the other */
752 if (This->name_to_propid)
754 dictionary_destroy(This->name_to_propid);
755 This->name_to_propid = NULL;
757 if (This->propid_to_name)
759 dictionary_destroy(This->propid_to_name);
760 This->propid_to_name = NULL;
762 hr = STG_E_INSUFFICIENTMEMORY;
767 /* FIXME: there isn't any checking whether the read property extends past the
770 static HRESULT PropertyStorage_ReadProperty(PROPVARIANT *prop, DWORD type,
783 prop->u.cVal = *(const char *)data;
784 TRACE("Read char 0x%x ('%c')\n", prop->u.cVal, prop->u.cVal);
788 prop->u.bVal = *(const UCHAR *)data;
789 TRACE("Read byte 0x%x\n", prop->u.bVal);
793 StorageUtl_ReadWord(data, 0, &prop->u.iVal);
794 TRACE("Read short %d\n", prop->u.iVal);
798 StorageUtl_ReadWord(data, 0, &prop->u.uiVal);
799 TRACE("Read ushort %d\n", prop->u.uiVal);
803 StorageUtl_ReadDWord(data, 0, &prop->u.lVal);
804 TRACE("Read long %ld\n", prop->u.lVal);
808 StorageUtl_ReadDWord(data, 0, &prop->u.ulVal);
809 TRACE("Read ulong %ld\n", prop->u.ulVal);
813 DWORD count = *(const DWORD *)data;
815 prop->u.pszVal = CoTaskMemAlloc(count);
818 /* FIXME: if the host is big-endian, this'll suck */
819 memcpy(prop->u.pszVal, data + sizeof(DWORD), count);
821 /* FIXME: so far so good, but this may be Unicode or DBCS depending
824 TRACE("Read string value %s\n", debugstr_a(prop->u.pszVal));
827 hr = STG_E_INSUFFICIENTMEMORY;
832 DWORD count = *(DWORD *)data;
834 prop->u.pwszVal = CoTaskMemAlloc(count * sizeof(WCHAR));
837 /* FIXME: if the host is big-endian, gotta swap every char */
838 memcpy(prop->u.pwszVal, data + sizeof(DWORD),
839 count * sizeof(WCHAR));
840 prop->vt = VT_LPWSTR;
841 TRACE("Read string value %s\n", debugstr_w(prop->u.pwszVal));
844 hr = STG_E_INSUFFICIENTMEMORY;
848 /* FIXME: endianness */
849 prop->vt = VT_FILETIME;
850 memcpy(&prop->u.filetime, data, sizeof(FILETIME));
853 FIXME("unsupported type %ld\n", type);
854 hr = STG_E_INVALIDPARAMETER;
859 static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
860 PROPERTYSETHEADER *hdr)
862 BYTE buf[sizeof(PROPERTYSETHEADER)];
866 hr = IStream_Read(stm, buf, sizeof(buf), &count);
869 if (count != sizeof(buf))
871 WARN("read %ld, expected %d\n", count, sizeof(buf));
872 hr = STG_E_INVALIDHEADER;
876 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder),
878 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat),
880 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer),
882 StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid),
884 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved),
888 TRACE("returning 0x%08lx\n", hr);
892 static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm,
895 BYTE buf[sizeof(FORMATIDOFFSET)];
899 hr = IStream_Read(stm, buf, sizeof(buf), &count);
902 if (count != sizeof(buf))
904 WARN("read %ld, expected %d\n", count, sizeof(buf));
905 hr = STG_E_INVALIDHEADER;
909 StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid),
911 StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset),
915 TRACE("returning 0x%08lx\n", hr);
919 static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
920 PROPERTYSECTIONHEADER *hdr)
922 BYTE buf[sizeof(PROPERTYSECTIONHEADER)];
926 hr = IStream_Read(stm, buf, sizeof(buf), &count);
929 if (count != sizeof(buf))
931 WARN("read %ld, expected %d\n", count, sizeof(buf));
932 hr = STG_E_INVALIDHEADER;
936 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
937 cbSection), &hdr->cbSection);
938 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
939 cProperties), &hdr->cProperties);
942 TRACE("returning 0x%08lx\n", hr);
946 static HRESULT PropertyStorage_ReadFromStream(IPropertyStorage *iface)
948 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
949 PROPERTYSETHEADER hdr;
950 FORMATIDOFFSET fmtOffset;
951 PROPERTYSECTIONHEADER sectionHdr;
957 DWORD dictOffset = 0;
963 This->highestProp = 0;
964 dictionary_destroy(This->name_to_propid);
965 This->name_to_propid = NULL;
966 dictionary_destroy(This->propid_to_name);
967 This->propid_to_name = NULL;
968 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
971 if (stat.cbSize.u.HighPart)
973 WARN("stream too big\n");
974 /* maximum size varies, but it can't be this big */
975 hr = STG_E_INVALIDHEADER;
978 if (stat.cbSize.u.LowPart == 0)
980 /* empty stream is okay, we might be being called from Create */
984 else if (stat.cbSize.u.LowPart < sizeof(PROPERTYSETHEADER) +
985 sizeof(FORMATIDOFFSET))
987 WARN("stream too small\n");
988 hr = STG_E_INVALIDHEADER;
991 hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr);
992 /* I've only seen reserved == 1, but the article says I shouldn't disallow
995 if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1)
997 WARN("bad magic in prop set header\n");
998 hr = STG_E_INVALIDHEADER;
1001 if (hdr.wFormat != 0 && hdr.wFormat != 1)
1003 WARN("bad format version %d\n", hdr.wFormat);
1004 hr = STG_E_INVALIDHEADER;
1007 memcpy(&This->clsid, &hdr.clsid, sizeof(This->clsid));
1008 This->originatorOS = hdr.dwOSVer;
1009 if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC)
1010 WARN("File comes from a Mac, strings will probably be screwed up\n");
1011 hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset);
1014 if (fmtOffset.dwOffset > stat.cbSize.u.LowPart)
1016 WARN("invalid offset %ld (stream length is %ld)\n", fmtOffset.dwOffset,
1017 stat.cbSize.u.LowPart);
1018 hr = STG_E_INVALIDHEADER;
1021 /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
1022 * follow not one, but two sections. The first is the standard properties
1023 * for the document summary information, and the second is user-defined
1024 * properties. This is the only case in which multiple sections are
1026 * Reading the second stream isn't implemented yet.
1028 hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, §ionHdr);
1031 /* The section size includes the section header, so check it */
1032 if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER))
1034 WARN("section header too small, got %ld, expected at least %d\n",
1035 sectionHdr.cbSection, sizeof(PROPERTYSECTIONHEADER));
1036 hr = STG_E_INVALIDHEADER;
1039 buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection -
1040 sizeof(PROPERTYSECTIONHEADER));
1043 hr = STG_E_INSUFFICIENTMEMORY;
1046 hr = IStream_Read(This->stm, buf, sectionHdr.cbSection -
1047 sizeof(PROPERTYSECTIONHEADER), &count);
1050 dictionary_destroy(This->propid_to_prop);
1051 This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
1052 PropertyStorage_PropertyDestroy, This);
1053 for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
1055 PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(buf +
1056 i * sizeof(PROPERTYIDOFFSET));
1058 if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) ||
1059 idOffset->dwOffset >= sectionHdr.cbSection - sizeof(DWORD))
1060 hr = STG_E_INVALIDPOINTER;
1063 if (idOffset->propid >= PID_FIRST_USABLE &&
1064 idOffset->propid < PID_MIN_READONLY && idOffset->propid >
1066 This->highestProp = idOffset->propid;
1067 if (idOffset->propid == 0)
1069 /* Don't read the dictionary yet, its entries depend on the
1070 * code page. Just store the offset so we know to read it
1073 dictOffset = idOffset->dwOffset;
1080 StorageUtl_ReadDWord(buf,
1081 idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER), &type);
1082 if (SUCCEEDED(PropertyStorage_ReadProperty(&prop, type,
1083 buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER) +
1086 /* FIXME: the PID_CODEPAGE and PID_LOCALE special cases
1087 * aren't really needed, just look them up in
1088 * propid_to_prop when needed
1090 switch(idOffset->propid)
1093 if (prop.vt == VT_I2)
1094 This->codePage = (UINT)prop.u.iVal;
1097 if (prop.vt == VT_I4)
1098 This->locale = (LCID)prop.u.lVal;
1101 if (prop.vt == VT_I4 && prop.u.lVal)
1102 This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
1105 hr = PropertyStorage_StorePropWithId(This,
1106 idOffset->propid, &prop);
1112 if (!This->codePage)
1114 /* default to Unicode unless told not to, as specified here:
1115 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
1117 if (This->grfFlags & PROPSETFLAG_ANSI)
1118 This->codePage = GetACP();
1120 This->codePage = 1200;
1123 This->locale = LOCALE_SYSTEM_DEFAULT;
1124 TRACE("Code page is %d, locale is %ld\n", This->codePage, This->locale);
1126 PropertyStorage_ReadDictionary(This,
1127 buf + dictOffset - sizeof(PROPERTYSECTIONHEADER));
1130 HeapFree(GetProcessHeap(), 0, buf);
1135 static HRESULT PropertyStorage_WriteToStream(IPropertyStorage *iface)
1141 /***********************************************************************
1142 * PropertyStorage_Construct
1144 static HRESULT PropertyStorage_Construct(IStream *stm, REFFMTID rfmtid,
1145 DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
1147 PropertyStorage_impl *ps;
1150 ps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof *ps);
1152 return E_OUTOFMEMORY;
1154 ps->vtbl = &IPropertyStorage_Vtbl;
1156 InitializeCriticalSection(&ps->cs);
1158 memcpy(&ps->fmtid, rfmtid, sizeof(ps->fmtid));
1159 ps->grfFlags = grfFlags;
1160 ps->grfMode = grfMode;
1162 hr = PropertyStorage_ReadFromStream((IPropertyStorage *)ps);
1165 *pps = (IPropertyStorage *)ps;
1166 TRACE("PropertyStorage %p constructed\n", ps);
1170 HeapFree(GetProcessHeap(), 0, ps);
1175 /***********************************************************************
1176 * Implementation of IPropertySetStorage
1179 #define BITS_PER_BYTE 8
1180 #define CHARMASK 0x1f
1181 #define BITS_IN_CHARMASK 5
1183 /* Converts rfmtid to a string and returns the resulting string. If rfmtid
1184 * is a well-known FMTID, it just returns a static string. Otherwise it
1185 * creates the appropriate string name in str, which must be 27 characters
1186 * in length, and returns str.
1187 * Based on the algorithm described here:
1188 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
1190 static LPCWSTR format_id_to_name(REFFMTID rfmtid, LPWSTR str)
1192 static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
1193 static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
1194 'I','n','f','o','r','m','a','t','i','o','n',0 };
1195 static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t',
1196 'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',
1200 if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
1201 ret = szSummaryInfo;
1202 else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
1203 ret = szDocSummaryInfo;
1208 ULONG bitsRemaining = BITS_PER_BYTE;
1211 for (fmtptr = (BYTE *)rfmtid; fmtptr < (BYTE *)rfmtid + sizeof(FMTID); )
1213 ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
1215 if (bitsRemaining >= BITS_IN_CHARMASK)
1217 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
1218 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
1222 bitsRemaining -= BITS_IN_CHARMASK;
1223 if (bitsRemaining == 0)
1226 bitsRemaining = BITS_PER_BYTE;
1231 if (++fmtptr < (BYTE *)rfmtid + sizeof(FMTID))
1232 i |= *fmtptr << bitsRemaining;
1233 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
1239 TRACE("returning %s\n", debugstr_w(ret));
1243 /************************************************************************
1244 * IPropertySetStorage_fnQueryInterface (IUnknown)
1246 * This method forwards to the common QueryInterface implementation
1248 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
1249 IPropertySetStorage *ppstg,
1253 _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
1254 return StorageBaseImpl_QueryInterface( (IStorage*)This, riid, ppvObject );
1257 /************************************************************************
1258 * IPropertySetStorage_fnAddRef (IUnknown)
1260 * This method forwards to the common AddRef implementation
1262 static ULONG WINAPI IPropertySetStorage_fnAddRef(
1263 IPropertySetStorage *ppstg)
1265 _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
1266 return StorageBaseImpl_AddRef( (IStorage*)This );
1269 /************************************************************************
1270 * IPropertySetStorage_fnRelease (IUnknown)
1272 * This method forwards to the common Release implementation
1274 static ULONG WINAPI IPropertySetStorage_fnRelease(
1275 IPropertySetStorage *ppstg)
1277 _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
1278 return StorageBaseImpl_Release( (IStorage*)This );
1281 /************************************************************************
1282 * IPropertySetStorage_fnCreate (IPropertySetStorage)
1284 static HRESULT WINAPI IPropertySetStorage_fnCreate(
1285 IPropertySetStorage *ppstg,
1287 const CLSID* pclsid,
1290 IPropertyStorage** ppprstg)
1292 _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
1294 LPCWSTR name = NULL;
1295 IStream *stm = NULL;
1298 TRACE("%p %s %08lx %08lx %p\n", This, debugstr_guid(rfmtid), grfFlags,
1302 if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
1304 r = STG_E_INVALIDFLAG;
1314 /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
1315 * storage, not a stream. For now, disallow it.
1317 if (grfFlags & PROPSETFLAG_NONSIMPLE)
1319 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
1320 r = STG_E_INVALIDFLAG;
1324 name = format_id_to_name(rfmtid, nameBuf);
1326 r = IStorage_CreateStream( (IStorage*)This, name, grfMode, 0, 0, &stm );
1330 r = PropertyStorage_Construct(stm, rfmtid, grfFlags, grfMode, ppprstg);
1333 TRACE("returning 0x%08lx\n", r);
1337 /************************************************************************
1338 * IPropertySetStorage_fnOpen (IPropertySetStorage)
1340 static HRESULT WINAPI IPropertySetStorage_fnOpen(
1341 IPropertySetStorage *ppstg,
1344 IPropertyStorage** ppprstg)
1346 _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
1347 IStream *stm = NULL;
1349 LPCWSTR name = NULL;
1352 TRACE("%p %s %08lx %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
1355 if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
1356 grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
1358 r = STG_E_INVALIDFLAG;
1368 name = format_id_to_name(rfmtid, nameBuf);
1370 r = IStorage_OpenStream((IStorage*) This, name, 0, grfMode, 0, &stm );
1374 r = PropertyStorage_Construct(stm, rfmtid, PROPSETFLAG_DEFAULT, grfMode,
1378 TRACE("returning 0x%08lx\n", r);
1382 /************************************************************************
1383 * IPropertySetStorage_fnDelete (IPropertySetStorage)
1385 static HRESULT WINAPI IPropertySetStorage_fnDelete(
1386 IPropertySetStorage *ppstg,
1389 _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
1390 IStorage *stg = NULL;
1392 LPCWSTR name = NULL;
1394 TRACE("%p %s\n", This, debugstr_guid(rfmtid));
1397 return E_INVALIDARG;
1399 name = format_id_to_name(rfmtid, nameBuf);
1401 return STG_E_FILENOTFOUND;
1403 stg = (IStorage*) This;
1404 return IStorage_DestroyElement(stg, name);
1407 /************************************************************************
1408 * IPropertySetStorage_fnEnum (IPropertySetStorage)
1410 static HRESULT WINAPI IPropertySetStorage_fnEnum(
1411 IPropertySetStorage *ppstg,
1412 IEnumSTATPROPSETSTG** ppenum)
1414 _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
1415 FIXME("%p\n", This);
1420 /***********************************************************************
1423 IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
1425 IPropertySetStorage_fnQueryInterface,
1426 IPropertySetStorage_fnAddRef,
1427 IPropertySetStorage_fnRelease,
1428 IPropertySetStorage_fnCreate,
1429 IPropertySetStorage_fnOpen,
1430 IPropertySetStorage_fnDelete,
1431 IPropertySetStorage_fnEnum
1434 static IPropertyStorageVtbl IPropertyStorage_Vtbl =
1436 IPropertyStorage_fnQueryInterface,
1437 IPropertyStorage_fnAddRef,
1438 IPropertyStorage_fnRelease,
1439 IPropertyStorage_fnReadMultiple,
1440 IPropertyStorage_fnWriteMultiple,
1441 IPropertyStorage_fnDeleteMultiple,
1442 IPropertyStorage_fnReadPropertyNames,
1443 IPropertyStorage_fnWritePropertyNames,
1444 IPropertyStorage_fnDeletePropertyNames,
1445 IPropertyStorage_fnCommit,
1446 IPropertyStorage_fnRevert,
1447 IPropertyStorage_fnEnum,
1448 IPropertyStorage_fnSetTimes,
1449 IPropertyStorage_fnSetClass,
1450 IPropertyStorage_fnStat,