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.
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
42 * - IPropertyStorage::Enum is unimplemented
52 #define NONAMELESSUNION
53 #define NONAMELESSSTRUCT
59 #include "wine/unicode.h"
60 #include "wine/debug.h"
61 #include "dictionary.h"
62 #include "storage32.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 /* The format version (and what it implies) is described here:
86 * http://msdn.microsoft.com/library/en-us/stg/stg/format_version.asp
88 typedef struct tagPROPERTYSETHEADER
90 WORD wByteOrder; /* always 0xfffe */
91 WORD wFormat; /* can be zero or one */
92 DWORD dwOSVer; /* OS version of originating system */
93 CLSID clsid; /* application CLSID */
94 DWORD reserved; /* always 1 */
97 typedef struct tagFORMATIDOFFSET
100 DWORD dwOffset; /* from beginning of stream */
103 typedef struct tagPROPERTYSECTIONHEADER
107 } PROPERTYSECTIONHEADER;
109 typedef struct tagPROPERTYIDOFFSET
112 DWORD dwOffset; /* from beginning of section */
115 struct tagPropertyStorage_impl;
117 /* Initializes the property storage from the stream (and undoes any uncommitted
118 * changes in the process.) Returns an error if there is an error reading or
119 * if the stream format doesn't match what's expected.
121 static HRESULT PropertyStorage_ReadFromStream(struct tagPropertyStorage_impl *);
123 static HRESULT PropertyStorage_WriteToStream(struct tagPropertyStorage_impl *);
125 /* Creates the dictionaries used by the property storage. If successful, all
126 * the dictionaries have been created. If failed, none has been. (This makes
127 * it a bit easier to deal with destroying them.)
129 static HRESULT PropertyStorage_CreateDictionaries(
130 struct tagPropertyStorage_impl *);
132 static void PropertyStorage_DestroyDictionaries(
133 struct tagPropertyStorage_impl *);
135 /* Copies from propvar to prop. If propvar's type is VT_LPSTR, copies the
136 * string using PropertyStorage_StringCopy.
138 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
139 const PROPVARIANT *propvar, LCID targetCP, LCID srcCP);
141 /* Copies the string src, which is encoded using code page srcCP, and returns
142 * it in *dst, in the code page specified by targetCP. The returned string is
143 * allocated using CoTaskMemAlloc.
144 * If srcCP is CP_UNICODE, src is in fact an LPCWSTR. Similarly, if targetCP
145 * is CP_UNICODE, the returned string is in fact an LPWSTR.
146 * Returns S_OK on success, something else on failure.
148 static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
151 static const IPropertyStorageVtbl IPropertyStorage_Vtbl;
153 /***********************************************************************
154 * Implementation of IPropertyStorage
156 typedef struct tagPropertyStorage_impl
158 const IPropertyStorageVtbl *vtbl;
172 struct dictionary *name_to_propid;
173 struct dictionary *propid_to_name;
174 struct dictionary *propid_to_prop;
175 } PropertyStorage_impl;
177 /************************************************************************
178 * IPropertyStorage_fnQueryInterface (IPropertyStorage)
180 static HRESULT WINAPI IPropertyStorage_fnQueryInterface(
181 IPropertyStorage *iface,
185 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
187 if ( (This==0) || (ppvObject==0) )
192 if (IsEqualGUID(&IID_IUnknown, riid) ||
193 IsEqualGUID(&IID_IPropertyStorage, riid))
195 IPropertyStorage_AddRef(iface);
196 *ppvObject = (IPropertyStorage*)iface;
200 return E_NOINTERFACE;
203 /************************************************************************
204 * IPropertyStorage_fnAddRef (IPropertyStorage)
206 static ULONG WINAPI IPropertyStorage_fnAddRef(
207 IPropertyStorage *iface)
209 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
210 return InterlockedIncrement(&This->ref);
213 /************************************************************************
214 * IPropertyStorage_fnRelease (IPropertyStorage)
216 static ULONG WINAPI IPropertyStorage_fnRelease(
217 IPropertyStorage *iface)
219 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
222 ref = InterlockedDecrement(&This->ref);
225 TRACE("Destroying %p\n", This);
227 IPropertyStorage_Commit(iface, STGC_DEFAULT);
228 IStream_Release(This->stm);
229 DeleteCriticalSection(&This->cs);
230 PropertyStorage_DestroyDictionaries(This);
231 HeapFree(GetProcessHeap(), 0, This);
236 static PROPVARIANT *PropertyStorage_FindProperty(PropertyStorage_impl *This,
239 PROPVARIANT *ret = NULL;
242 dictionary_find(This->propid_to_prop, (void *)propid, (void **)&ret);
243 TRACE("returning %p\n", ret);
247 /* Returns NULL if name is NULL. */
248 static PROPVARIANT *PropertyStorage_FindPropertyByName(
249 PropertyStorage_impl *This, LPCWSTR name)
251 PROPVARIANT *ret = NULL;
257 if (This->codePage == CP_UNICODE)
259 if (dictionary_find(This->name_to_propid, name, (void **)&propid))
260 ret = PropertyStorage_FindProperty(This, (PROPID)propid);
265 HRESULT hr = PropertyStorage_StringCopy((LPCSTR)name, CP_UNICODE,
266 &ansiName, This->codePage);
270 if (dictionary_find(This->name_to_propid, ansiName,
272 ret = PropertyStorage_FindProperty(This, (PROPID)propid);
273 CoTaskMemFree(ansiName);
276 TRACE("returning %p\n", ret);
280 static LPWSTR PropertyStorage_FindPropertyNameById(PropertyStorage_impl *This,
286 dictionary_find(This->propid_to_name, (void *)propid, (void **)&ret);
287 TRACE("returning %p\n", ret);
291 /************************************************************************
292 * IPropertyStorage_fnReadMultiple (IPropertyStorage)
294 static HRESULT WINAPI IPropertyStorage_fnReadMultiple(
295 IPropertyStorage* iface,
297 const PROPSPEC rgpspec[],
298 PROPVARIANT rgpropvar[])
300 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
301 HRESULT hr = S_FALSE;
304 TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
307 if (cpspec && (!rgpspec || !rgpropvar))
309 EnterCriticalSection(&This->cs);
310 for (i = 0; i < cpspec; i++)
312 PropVariantInit(&rgpropvar[i]);
313 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
315 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
316 rgpspec[i].u.lpwstr);
319 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop, GetACP(),
324 switch (rgpspec[i].u.propid)
327 rgpropvar[i].vt = VT_I2;
328 rgpropvar[i].u.iVal = This->codePage;
331 rgpropvar[i].vt = VT_I4;
332 rgpropvar[i].u.lVal = This->locale;
336 PROPVARIANT *prop = PropertyStorage_FindProperty(This,
337 rgpspec[i].u.propid);
340 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop,
341 GetACP(), This->codePage);
346 LeaveCriticalSection(&This->cs);
350 static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
356 TRACE("%s, %p, %ld, %ld\n",
357 srcCP == CP_UNICODE ? debugstr_w((LPCWSTR)src) : debugstr_a(src), dst,
366 if (dstCP == CP_UNICODE)
367 len = (strlenW((LPCWSTR)src) + 1) * sizeof(WCHAR);
369 len = strlen(src) + 1;
370 *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
372 hr = STG_E_INSUFFICIENTMEMORY;
374 memcpy(*dst, src, len);
378 if (dstCP == CP_UNICODE)
380 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
381 *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
383 hr = STG_E_INSUFFICIENTMEMORY;
385 MultiByteToWideChar(srcCP, 0, src, -1, (LPWSTR)*dst, len);
391 if (srcCP == CP_UNICODE)
392 wideStr = (LPWSTR)src;
395 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
396 wideStr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
398 MultiByteToWideChar(srcCP, 0, src, -1, wideStr, len);
400 hr = STG_E_INSUFFICIENTMEMORY;
404 len = WideCharToMultiByte(dstCP, 0, wideStr, -1, NULL, 0,
406 *dst = CoTaskMemAlloc(len);
408 hr = STG_E_INSUFFICIENTMEMORY;
411 BOOL defCharUsed = FALSE;
413 if (WideCharToMultiByte(dstCP, 0, wideStr, -1, *dst, len,
414 NULL, &defCharUsed) == 0 || defCharUsed)
418 hr = HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION);
422 if (wideStr != (LPWSTR)src)
423 HeapFree(GetProcessHeap(), 0, wideStr);
426 TRACE("returning 0x%08lx (%s)\n", hr,
427 dstCP == CP_UNICODE ? debugstr_w((LPCWSTR)*dst) : debugstr_a(*dst));
431 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
432 const PROPVARIANT *propvar, LCID targetCP, LCID srcCP)
438 if (propvar->vt == VT_LPSTR)
440 hr = PropertyStorage_StringCopy(propvar->u.pszVal, srcCP,
441 &prop->u.pszVal, targetCP);
446 PropVariantCopy(prop, propvar);
450 /* Stores the property with id propid and value propvar into this property
451 * storage. lcid is ignored if propvar's type is not VT_LPSTR. If propvar's
452 * type is VT_LPSTR, converts the string using lcid as the source code page
453 * and This->codePage as the target code page before storing.
454 * As a side effect, may change This->format to 1 if the type of propvar is
455 * a version 1-only property.
457 static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
458 PROPID propid, const PROPVARIANT *propvar, LCID lcid)
461 PROPVARIANT *prop = PropertyStorage_FindProperty(This, propid);
465 if (propvar->vt & VT_BYREF || propvar->vt & VT_ARRAY)
473 case VT_VECTOR|VT_I1:
476 TRACE("Setting 0x%08lx to type %d\n", propid, propvar->vt);
479 PropVariantClear(prop);
480 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
485 prop = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
486 sizeof(PROPVARIANT));
489 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
493 dictionary_insert(This->propid_to_prop, (void *)propid, prop);
494 if (propid > This->highestProp)
495 This->highestProp = propid;
498 HeapFree(GetProcessHeap(), 0, prop);
501 hr = STG_E_INSUFFICIENTMEMORY;
506 /* Adds the name srcName to the name dictionaries, mapped to property ID id.
507 * srcName is encoded in code page cp, and is converted to This->codePage.
508 * If cp is CP_UNICODE, srcName is actually a unicode string.
509 * As a side effect, may change This->format to 1 if srcName is too long for
510 * a version 0 property storage.
511 * Doesn't validate id.
513 static HRESULT PropertyStorage_StoreNameWithId(PropertyStorage_impl *This,
514 LPCSTR srcName, LCID cp, PROPID id)
521 hr = PropertyStorage_StringCopy((LPCSTR)srcName, cp, &name, This->codePage);
524 if (This->codePage == CP_UNICODE)
526 if (lstrlenW((LPWSTR)name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
531 if (strlen(name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
534 TRACE("Adding prop name %s, propid %ld\n",
535 This->codePage == CP_UNICODE ? debugstr_w((LPCWSTR)name) :
536 debugstr_a(name), id);
537 dictionary_insert(This->name_to_propid, name, (void *)id);
538 dictionary_insert(This->propid_to_name, (void *)id, name);
543 /************************************************************************
544 * IPropertyStorage_fnWriteMultiple (IPropertyStorage)
546 static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
547 IPropertyStorage* iface,
549 const PROPSPEC rgpspec[],
550 const PROPVARIANT rgpropvar[],
551 PROPID propidNameFirst)
553 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
557 TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
560 if (cpspec && (!rgpspec || !rgpropvar))
562 if (!(This->grfMode & STGM_READWRITE))
563 return STG_E_ACCESSDENIED;
564 EnterCriticalSection(&This->cs);
566 This->originatorOS = (DWORD)MAKELONG(LOWORD(GetVersion()),
567 PROPSETHDR_OSVER_KIND_WIN32) ;
568 for (i = 0; i < cpspec; i++)
570 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
572 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
573 rgpspec[i].u.lpwstr);
576 PropVariantCopy(prop, &rgpropvar[i]);
579 /* Note that I don't do the special cases here that I do below,
580 * because naming the special PIDs isn't supported.
582 if (propidNameFirst < PID_FIRST_USABLE ||
583 propidNameFirst >= PID_MIN_READONLY)
584 hr = STG_E_INVALIDPARAMETER;
587 PROPID nextId = max(propidNameFirst, This->highestProp + 1);
589 hr = PropertyStorage_StoreNameWithId(This,
590 (LPCSTR)rgpspec[i].u.lpwstr, CP_UNICODE, nextId);
592 hr = PropertyStorage_StorePropWithId(This, nextId,
593 &rgpropvar[i], GetACP());
599 switch (rgpspec[i].u.propid)
602 /* Can't set the dictionary */
603 hr = STG_E_INVALIDPARAMETER;
606 /* Can only set the code page if nothing else has been set */
607 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
608 rgpropvar[i].vt == VT_I2)
610 This->codePage = rgpropvar[i].u.iVal;
611 if (This->codePage == CP_UNICODE)
612 This->grfFlags &= ~PROPSETFLAG_ANSI;
614 This->grfFlags |= PROPSETFLAG_ANSI;
617 hr = STG_E_INVALIDPARAMETER;
620 /* Can only set the locale if nothing else has been set */
621 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
622 rgpropvar[i].vt == VT_I4)
623 This->locale = rgpropvar[i].u.lVal;
625 hr = STG_E_INVALIDPARAMETER;
628 /* silently ignore like MSDN says */
631 if (rgpspec[i].u.propid >= PID_MIN_READONLY)
632 hr = STG_E_INVALIDPARAMETER;
634 hr = PropertyStorage_StorePropWithId(This,
635 rgpspec[i].u.propid, &rgpropvar[i], GetACP());
639 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
640 IPropertyStorage_Commit(iface, STGC_DEFAULT);
641 LeaveCriticalSection(&This->cs);
645 /************************************************************************
646 * IPropertyStorage_fnDeleteMultiple (IPropertyStorage)
648 static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
649 IPropertyStorage* iface,
651 const PROPSPEC rgpspec[])
653 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
657 TRACE("(%p, %ld, %p)\n", iface, cpspec, rgpspec);
660 if (cpspec && !rgpspec)
662 if (!(This->grfMode & STGM_READWRITE))
663 return STG_E_ACCESSDENIED;
665 EnterCriticalSection(&This->cs);
667 for (i = 0; i < cpspec; i++)
669 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
673 if (dictionary_find(This->name_to_propid,
674 (void *)rgpspec[i].u.lpwstr, (void **)&propid))
675 dictionary_remove(This->propid_to_prop, (void *)propid);
679 if (rgpspec[i].u.propid >= PID_FIRST_USABLE &&
680 rgpspec[i].u.propid < PID_MIN_READONLY)
681 dictionary_remove(This->propid_to_prop,
682 (void *)rgpspec[i].u.propid);
684 hr = STG_E_INVALIDPARAMETER;
687 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
688 IPropertyStorage_Commit(iface, STGC_DEFAULT);
689 LeaveCriticalSection(&This->cs);
693 /************************************************************************
694 * IPropertyStorage_fnReadPropertyNames (IPropertyStorage)
696 static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames(
697 IPropertyStorage* iface,
699 const PROPID rgpropid[],
700 LPOLESTR rglpwstrName[])
702 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
704 HRESULT hr = S_FALSE;
706 TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
709 if (cpropid && (!rgpropid || !rglpwstrName))
711 EnterCriticalSection(&This->cs);
712 for (i = 0; i < cpropid && SUCCEEDED(hr); i++)
714 LPWSTR name = PropertyStorage_FindPropertyNameById(This, rgpropid[i]);
718 size_t len = lstrlenW(name);
721 rglpwstrName[i] = CoTaskMemAlloc((len + 1) * sizeof(WCHAR));
723 memcpy(rglpwstrName, name, (len + 1) * sizeof(WCHAR));
725 hr = STG_E_INSUFFICIENTMEMORY;
728 rglpwstrName[i] = NULL;
730 LeaveCriticalSection(&This->cs);
734 /************************************************************************
735 * IPropertyStorage_fnWritePropertyNames (IPropertyStorage)
737 static HRESULT WINAPI IPropertyStorage_fnWritePropertyNames(
738 IPropertyStorage* iface,
740 const PROPID rgpropid[],
741 const LPOLESTR rglpwstrName[])
743 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
747 TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
750 if (cpropid && (!rgpropid || !rglpwstrName))
752 if (!(This->grfMode & STGM_READWRITE))
753 return STG_E_ACCESSDENIED;
755 EnterCriticalSection(&This->cs);
757 for (i = 0; SUCCEEDED(hr) && i < cpropid; i++)
759 if (rgpropid[i] != PID_ILLEGAL)
760 hr = PropertyStorage_StoreNameWithId(This, (LPCSTR)rglpwstrName[i],
761 CP_UNICODE, rgpropid[i]);
763 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
764 IPropertyStorage_Commit(iface, STGC_DEFAULT);
765 LeaveCriticalSection(&This->cs);
769 /************************************************************************
770 * IPropertyStorage_fnDeletePropertyNames (IPropertyStorage)
772 static HRESULT WINAPI IPropertyStorage_fnDeletePropertyNames(
773 IPropertyStorage* iface,
775 const PROPID rgpropid[])
777 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
781 TRACE("(%p, %ld, %p)\n", iface, cpropid, rgpropid);
784 if (cpropid && !rgpropid)
786 if (!(This->grfMode & STGM_READWRITE))
787 return STG_E_ACCESSDENIED;
789 EnterCriticalSection(&This->cs);
791 for (i = 0; i < cpropid; i++)
795 if (dictionary_find(This->propid_to_name, (void *)rgpropid[i],
798 dictionary_remove(This->propid_to_name, (void *)rgpropid[i]);
799 dictionary_remove(This->name_to_propid, name);
802 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
803 IPropertyStorage_Commit(iface, STGC_DEFAULT);
804 LeaveCriticalSection(&This->cs);
808 /************************************************************************
809 * IPropertyStorage_fnCommit (IPropertyStorage)
811 static HRESULT WINAPI IPropertyStorage_fnCommit(
812 IPropertyStorage* iface,
813 DWORD grfCommitFlags)
815 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
818 TRACE("(%p, 0x%08lx)\n", iface, grfCommitFlags);
821 if (!(This->grfMode & STGM_READWRITE))
822 return STG_E_ACCESSDENIED;
823 EnterCriticalSection(&This->cs);
825 hr = PropertyStorage_WriteToStream(This);
828 LeaveCriticalSection(&This->cs);
832 /************************************************************************
833 * IPropertyStorage_fnRevert (IPropertyStorage)
835 static HRESULT WINAPI IPropertyStorage_fnRevert(
836 IPropertyStorage* iface)
839 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
841 TRACE("%p\n", iface);
845 EnterCriticalSection(&This->cs);
848 PropertyStorage_DestroyDictionaries(This);
849 hr = PropertyStorage_CreateDictionaries(This);
851 hr = PropertyStorage_ReadFromStream(This);
855 LeaveCriticalSection(&This->cs);
859 /************************************************************************
860 * IPropertyStorage_fnEnum (IPropertyStorage)
862 static HRESULT WINAPI IPropertyStorage_fnEnum(
863 IPropertyStorage* iface,
864 IEnumSTATPROPSTG** 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)
883 /************************************************************************
884 * IPropertyStorage_fnSetClass (IPropertyStorage)
886 static HRESULT WINAPI IPropertyStorage_fnSetClass(
887 IPropertyStorage* iface,
890 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
892 TRACE("%p, %s\n", iface, debugstr_guid(clsid));
895 if (!(This->grfMode & STGM_READWRITE))
896 return STG_E_ACCESSDENIED;
897 memcpy(&This->clsid, clsid, sizeof(This->clsid));
899 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
900 IPropertyStorage_Commit(iface, STGC_DEFAULT);
904 /************************************************************************
905 * IPropertyStorage_fnStat (IPropertyStorage)
907 static HRESULT WINAPI IPropertyStorage_fnStat(
908 IPropertyStorage* iface,
909 STATPROPSETSTG* statpsstg)
911 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
915 TRACE("%p, %p\n", iface, statpsstg);
916 if (!This || !statpsstg)
919 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
922 memcpy(&statpsstg->fmtid, &This->fmtid, sizeof(statpsstg->fmtid));
923 memcpy(&statpsstg->clsid, &This->clsid, sizeof(statpsstg->clsid));
924 statpsstg->grfFlags = This->grfFlags;
925 memcpy(&statpsstg->mtime, &stat.mtime, sizeof(statpsstg->mtime));
926 memcpy(&statpsstg->ctime, &stat.ctime, sizeof(statpsstg->ctime));
927 memcpy(&statpsstg->atime, &stat.atime, sizeof(statpsstg->atime));
928 statpsstg->dwOSVersion = This->originatorOS;
933 static int PropertyStorage_PropNameCompare(const void *a, const void *b,
936 PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
938 if (This->codePage == CP_UNICODE)
940 TRACE("(%s, %s)\n", debugstr_w(a), debugstr_w(b));
941 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
942 return lstrcmpW((LPCWSTR)a, (LPCWSTR)b);
944 return lstrcmpiW((LPCWSTR)a, (LPCWSTR)b);
948 TRACE("(%s, %s)\n", debugstr_a(a), debugstr_a(b));
949 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
950 return lstrcmpA((LPCSTR)a, (LPCSTR)b);
952 return lstrcmpiA((LPCSTR)a, (LPCSTR)b);
956 static void PropertyStorage_PropNameDestroy(void *k, void *d, void *extra)
961 static int PropertyStorage_PropCompare(const void *a, const void *b,
964 TRACE("(%ld, %ld)\n", (PROPID)a, (PROPID)b);
965 return (PROPID)a - (PROPID)b;
968 static void PropertyStorage_PropertyDestroy(void *k, void *d, void *extra)
970 PropVariantClear((PROPVARIANT *)d);
971 HeapFree(GetProcessHeap(), 0, d);
974 #ifdef WORDS_BIGENDIAN
975 /* Swaps each character in str to or from little endian; assumes the conversion
976 * is symmetric, that is, that le16toh is equivalent to htole16.
978 static void PropertyStorage_ByteSwapString(LPWSTR str, size_t len)
982 /* Swap characters to host order.
985 for (i = 0; i < len; i++)
986 str[i] = le16toh(str[i]);
989 #define PropertyStorage_ByteSwapString(s, l)
992 /* Reads the dictionary from the memory buffer beginning at ptr. Interprets
993 * the entries according to the values of This->codePage and This->locale.
994 * FIXME: there isn't any checking whether the read property extends past the
997 static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This,
1000 DWORD numEntries, i;
1004 assert(This->name_to_propid);
1005 assert(This->propid_to_name);
1007 StorageUtl_ReadDWord(ptr, 0, &numEntries);
1008 TRACE("Reading %ld entries:\n", numEntries);
1009 ptr += sizeof(DWORD);
1010 for (i = 0; SUCCEEDED(hr) && i < numEntries; i++)
1015 StorageUtl_ReadDWord(ptr, 0, &propid);
1016 ptr += sizeof(PROPID);
1017 StorageUtl_ReadDWord(ptr, 0, &cbEntry);
1018 ptr += sizeof(DWORD);
1019 TRACE("Reading entry with ID 0x%08lx, %ld bytes\n", propid, cbEntry);
1020 /* Make sure the source string is NULL-terminated */
1021 if (This->codePage != CP_UNICODE)
1022 ptr[cbEntry - 1] = '\0';
1024 *((LPWSTR)ptr + cbEntry / sizeof(WCHAR)) = '\0';
1025 hr = PropertyStorage_StoreNameWithId(This, (char*)ptr, This->codePage, propid);
1026 if (This->codePage == CP_UNICODE)
1028 /* Unicode entries are padded to DWORD boundaries */
1029 if (cbEntry % sizeof(DWORD))
1030 ptr += sizeof(DWORD) - (cbEntry % sizeof(DWORD));
1032 ptr += sizeof(DWORD) + cbEntry;
1037 /* FIXME: there isn't any checking whether the read property extends past the
1038 * end of the buffer.
1040 static HRESULT PropertyStorage_ReadProperty(PropertyStorage_impl *This,
1041 PROPVARIANT *prop, const BYTE *data)
1047 StorageUtl_ReadDWord(data, 0, (DWORD *)&prop->vt);
1048 data += sizeof(DWORD);
1055 prop->u.cVal = *(const char *)data;
1056 TRACE("Read char 0x%x ('%c')\n", prop->u.cVal, prop->u.cVal);
1059 prop->u.bVal = *(const UCHAR *)data;
1060 TRACE("Read byte 0x%x\n", prop->u.bVal);
1063 StorageUtl_ReadWord(data, 0, (WORD*)&prop->u.iVal);
1064 TRACE("Read short %d\n", prop->u.iVal);
1067 StorageUtl_ReadWord(data, 0, &prop->u.uiVal);
1068 TRACE("Read ushort %d\n", prop->u.uiVal);
1072 StorageUtl_ReadDWord(data, 0, (DWORD*)&prop->u.lVal);
1073 TRACE("Read long %ld\n", prop->u.lVal);
1077 StorageUtl_ReadDWord(data, 0, &prop->u.ulVal);
1078 TRACE("Read ulong %ld\n", prop->u.ulVal);
1084 StorageUtl_ReadDWord(data, 0, &count);
1085 if (This->codePage == CP_UNICODE && count / 2)
1087 WARN("Unicode string has odd number of bytes\n");
1088 hr = STG_E_INVALIDHEADER;
1092 prop->u.pszVal = CoTaskMemAlloc(count);
1095 memcpy(prop->u.pszVal, data + sizeof(DWORD), count);
1096 /* This is stored in the code page specified in This->codePage.
1097 * Don't convert it, the caller will just store it as-is.
1099 if (This->codePage == CP_UNICODE)
1101 /* Make sure it's NULL-terminated */
1102 prop->u.pszVal[count / sizeof(WCHAR) - 1] = '\0';
1103 TRACE("Read string value %s\n",
1104 debugstr_w(prop->u.pwszVal));
1108 /* Make sure it's NULL-terminated */
1109 prop->u.pszVal[count - 1] = '\0';
1110 TRACE("Read string value %s\n", debugstr_a(prop->u.pszVal));
1114 hr = STG_E_INSUFFICIENTMEMORY;
1122 StorageUtl_ReadDWord(data, 0, &count);
1123 prop->u.pwszVal = CoTaskMemAlloc(count * sizeof(WCHAR));
1124 if (prop->u.pwszVal)
1126 memcpy(prop->u.pwszVal, data + sizeof(DWORD),
1127 count * sizeof(WCHAR));
1128 /* make sure string is NULL-terminated */
1129 prop->u.pwszVal[count - 1] = '\0';
1130 PropertyStorage_ByteSwapString(prop->u.pwszVal, count);
1131 TRACE("Read string value %s\n", debugstr_w(prop->u.pwszVal));
1134 hr = STG_E_INSUFFICIENTMEMORY;
1138 StorageUtl_ReadULargeInteger(data, 0,
1139 (ULARGE_INTEGER *)&prop->u.filetime);
1142 FIXME("unsupported type %d\n", prop->vt);
1143 hr = STG_E_INVALIDPARAMETER;
1148 static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
1149 PROPERTYSETHEADER *hdr)
1151 BYTE buf[sizeof(PROPERTYSETHEADER)];
1157 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1160 if (count != sizeof(buf))
1162 WARN("read %ld, expected %d\n", count, sizeof(buf));
1163 hr = STG_E_INVALIDHEADER;
1167 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder),
1169 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat),
1171 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer),
1173 StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid),
1175 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved),
1179 TRACE("returning 0x%08lx\n", hr);
1183 static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm,
1184 FORMATIDOFFSET *fmt)
1186 BYTE buf[sizeof(FORMATIDOFFSET)];
1192 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1195 if (count != sizeof(buf))
1197 WARN("read %ld, expected %d\n", count, sizeof(buf));
1198 hr = STG_E_INVALIDHEADER;
1202 StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid),
1204 StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset),
1208 TRACE("returning 0x%08lx\n", hr);
1212 static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
1213 PROPERTYSECTIONHEADER *hdr)
1215 BYTE buf[sizeof(PROPERTYSECTIONHEADER)];
1221 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1224 if (count != sizeof(buf))
1226 WARN("read %ld, expected %d\n", count, sizeof(buf));
1227 hr = STG_E_INVALIDHEADER;
1231 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1232 cbSection), &hdr->cbSection);
1233 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1234 cProperties), &hdr->cProperties);
1237 TRACE("returning 0x%08lx\n", hr);
1241 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This)
1243 PROPERTYSETHEADER hdr;
1244 FORMATIDOFFSET fmtOffset;
1245 PROPERTYSECTIONHEADER sectionHdr;
1252 DWORD dictOffset = 0;
1255 This->dirty = FALSE;
1256 This->highestProp = 0;
1257 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
1260 if (stat.cbSize.u.HighPart)
1262 WARN("stream too big\n");
1263 /* maximum size varies, but it can't be this big */
1264 hr = STG_E_INVALIDHEADER;
1267 if (stat.cbSize.u.LowPart == 0)
1269 /* empty stream is okay */
1273 else if (stat.cbSize.u.LowPart < sizeof(PROPERTYSETHEADER) +
1274 sizeof(FORMATIDOFFSET))
1276 WARN("stream too small\n");
1277 hr = STG_E_INVALIDHEADER;
1281 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1284 hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr);
1285 /* I've only seen reserved == 1, but the article says I shouldn't disallow
1288 if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1)
1290 WARN("bad magic in prop set header\n");
1291 hr = STG_E_INVALIDHEADER;
1294 if (hdr.wFormat != 0 && hdr.wFormat != 1)
1296 WARN("bad format version %d\n", hdr.wFormat);
1297 hr = STG_E_INVALIDHEADER;
1300 This->format = hdr.wFormat;
1301 memcpy(&This->clsid, &hdr.clsid, sizeof(This->clsid));
1302 This->originatorOS = hdr.dwOSVer;
1303 if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC)
1304 WARN("File comes from a Mac, strings will probably be screwed up\n");
1305 hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset);
1308 if (fmtOffset.dwOffset > stat.cbSize.u.LowPart)
1310 WARN("invalid offset %ld (stream length is %ld)\n", fmtOffset.dwOffset,
1311 stat.cbSize.u.LowPart);
1312 hr = STG_E_INVALIDHEADER;
1315 /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
1316 * follow not one, but two sections. The first is the standard properties
1317 * for the document summary information, and the second is user-defined
1318 * properties. This is the only case in which multiple sections are
1320 * Reading the second stream isn't implemented yet.
1322 hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, §ionHdr);
1325 /* The section size includes the section header, so check it */
1326 if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER))
1328 WARN("section header too small, got %ld, expected at least %d\n",
1329 sectionHdr.cbSection, sizeof(PROPERTYSECTIONHEADER));
1330 hr = STG_E_INVALIDHEADER;
1333 buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection -
1334 sizeof(PROPERTYSECTIONHEADER));
1337 hr = STG_E_INSUFFICIENTMEMORY;
1340 hr = IStream_Read(This->stm, buf, sectionHdr.cbSection -
1341 sizeof(PROPERTYSECTIONHEADER), &count);
1344 TRACE("Reading %ld properties:\n", sectionHdr.cProperties);
1345 for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
1347 PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(buf +
1348 i * sizeof(PROPERTYIDOFFSET));
1350 if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) ||
1351 idOffset->dwOffset >= sectionHdr.cbSection - sizeof(DWORD))
1352 hr = STG_E_INVALIDPOINTER;
1355 if (idOffset->propid >= PID_FIRST_USABLE &&
1356 idOffset->propid < PID_MIN_READONLY && idOffset->propid >
1358 This->highestProp = idOffset->propid;
1359 if (idOffset->propid == PID_DICTIONARY)
1361 /* Don't read the dictionary yet, its entries depend on the
1362 * code page. Just store the offset so we know to read it
1365 dictOffset = idOffset->dwOffset;
1366 TRACE("Dictionary offset is %ld\n", dictOffset);
1372 if (SUCCEEDED(PropertyStorage_ReadProperty(This, &prop,
1373 buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER))))
1375 TRACE("Read property with ID 0x%08lx, type %d\n",
1376 idOffset->propid, prop.vt);
1377 switch(idOffset->propid)
1380 if (prop.vt == VT_I2)
1381 This->codePage = (UINT)prop.u.iVal;
1384 if (prop.vt == VT_I4)
1385 This->locale = (LCID)prop.u.lVal;
1388 if (prop.vt == VT_I4 && prop.u.lVal)
1389 This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
1390 /* The format should already be 1, but just in case */
1394 hr = PropertyStorage_StorePropWithId(This,
1395 idOffset->propid, &prop, This->codePage);
1401 if (!This->codePage)
1403 /* default to Unicode unless told not to, as specified here:
1404 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
1406 if (This->grfFlags & PROPSETFLAG_ANSI)
1407 This->codePage = GetACP();
1409 This->codePage = CP_UNICODE;
1412 This->locale = LOCALE_SYSTEM_DEFAULT;
1413 TRACE("Code page is %d, locale is %ld\n", This->codePage, This->locale);
1415 hr = PropertyStorage_ReadDictionary(This,
1416 buf + dictOffset - sizeof(PROPERTYSECTIONHEADER));
1419 HeapFree(GetProcessHeap(), 0, buf);
1422 dictionary_destroy(This->name_to_propid);
1423 This->name_to_propid = NULL;
1424 dictionary_destroy(This->propid_to_name);
1425 This->propid_to_name = NULL;
1426 dictionary_destroy(This->propid_to_prop);
1427 This->propid_to_prop = NULL;
1432 static void PropertyStorage_MakeHeader(PropertyStorage_impl *This,
1433 PROPERTYSETHEADER *hdr)
1437 StorageUtl_WriteWord((BYTE *)&hdr->wByteOrder, 0,
1438 PROPSETHDR_BYTEORDER_MAGIC);
1439 StorageUtl_WriteWord((BYTE *)&hdr->wFormat, 0, This->format);
1440 StorageUtl_WriteDWord((BYTE *)&hdr->dwOSVer, 0, This->originatorOS);
1441 StorageUtl_WriteGUID((BYTE *)&hdr->clsid, 0, &This->clsid);
1442 StorageUtl_WriteDWord((BYTE *)&hdr->reserved, 0, 1);
1445 static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl *This,
1446 FORMATIDOFFSET *fmtOffset)
1450 StorageUtl_WriteGUID((BYTE *)fmtOffset, 0, &This->fmtid);
1451 StorageUtl_WriteDWord((BYTE *)fmtOffset, offsetof(FORMATIDOFFSET, dwOffset),
1452 sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET));
1455 static void PropertyStorage_MakeSectionHdr(DWORD cbSection, DWORD numProps,
1456 PROPERTYSECTIONHEADER *hdr)
1459 StorageUtl_WriteDWord((BYTE *)hdr, 0, cbSection);
1460 StorageUtl_WriteDWord((BYTE *)hdr,
1461 offsetof(PROPERTYSECTIONHEADER, cProperties), numProps);
1464 static void PropertyStorage_MakePropertyIdOffset(DWORD propid, DWORD dwOffset,
1465 PROPERTYIDOFFSET *propIdOffset)
1467 assert(propIdOffset);
1468 StorageUtl_WriteDWord((BYTE *)propIdOffset, 0, propid);
1469 StorageUtl_WriteDWord((BYTE *)propIdOffset,
1470 offsetof(PROPERTYIDOFFSET, dwOffset), dwOffset);
1473 struct DictionaryClosure
1479 static BOOL PropertyStorage_DictionaryWriter(const void *key,
1480 const void *value, void *extra, void *closure)
1482 PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
1483 struct DictionaryClosure *c = (struct DictionaryClosure *)closure;
1490 StorageUtl_WriteDWord((LPBYTE)&propid, 0, (DWORD)value);
1491 c->hr = IStream_Write(This->stm, &propid, sizeof(propid), &count);
1494 c->bytesWritten += sizeof(DWORD);
1495 if (This->codePage == CP_UNICODE)
1497 DWORD keyLen, pad = 0;
1499 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0,
1500 (lstrlenW((LPWSTR)key) + 1) * sizeof(WCHAR));
1501 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1504 c->bytesWritten += sizeof(DWORD);
1505 /* Rather than allocate a copy, I'll swap the string to little-endian
1506 * in-place, write it, then swap it back.
1508 PropertyStorage_ByteSwapString(key, keyLen);
1509 c->hr = IStream_Write(This->stm, key, keyLen, &count);
1510 PropertyStorage_ByteSwapString(key, keyLen);
1513 c->bytesWritten += keyLen;
1514 if (keyLen % sizeof(DWORD))
1516 c->hr = IStream_Write(This->stm, &pad,
1517 sizeof(DWORD) - keyLen % sizeof(DWORD), &count);
1520 c->bytesWritten += sizeof(DWORD) - keyLen % sizeof(DWORD);
1527 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0, strlen((LPCSTR)key) + 1);
1528 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1531 c->bytesWritten += sizeof(DWORD);
1532 c->hr = IStream_Write(This->stm, key, keyLen, &count);
1535 c->bytesWritten += keyLen;
1538 return SUCCEEDED(c->hr);
1541 #define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)
1543 /* Writes the dictionary to the stream. Assumes without checking that the
1544 * dictionary isn't empty.
1546 static HRESULT PropertyStorage_WriteDictionaryToStream(
1547 PropertyStorage_impl *This, DWORD *sectionOffset)
1551 PROPERTYIDOFFSET propIdOffset;
1554 struct DictionaryClosure closure;
1557 assert(sectionOffset);
1559 /* The dictionary's always the first property written, so seek to its
1562 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER);
1563 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1566 PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY, *sectionOffset,
1568 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1572 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1573 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1576 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0,
1577 dictionary_num_entries(This->name_to_propid));
1578 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1581 *sectionOffset += sizeof(dwTemp);
1584 closure.bytesWritten = 0;
1585 dictionary_enumerate(This->name_to_propid, PropertyStorage_DictionaryWriter,
1590 *sectionOffset += closure.bytesWritten;
1591 if (closure.bytesWritten % sizeof(DWORD))
1593 TRACE("adding %ld bytes of padding\n", sizeof(DWORD) -
1594 closure.bytesWritten % sizeof(DWORD));
1595 *sectionOffset += sizeof(DWORD) - closure.bytesWritten % sizeof(DWORD);
1602 static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This,
1603 DWORD propNum, DWORD propid, PROPVARIANT *var, DWORD *sectionOffset)
1607 PROPERTYIDOFFSET propIdOffset;
1609 DWORD dwType, bytesWritten;
1613 assert(sectionOffset);
1615 TRACE("%p, %ld, 0x%08lx, (%d), (%ld)\n", This, propNum, propid, var->vt,
1618 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) +
1619 propNum * sizeof(PROPERTYIDOFFSET);
1620 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1623 PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset);
1624 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1628 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1629 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1632 StorageUtl_WriteDWord((LPBYTE)&dwType, 0, var->vt);
1633 hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count);
1636 *sectionOffset += sizeof(dwType);
1646 hr = IStream_Write(This->stm, &var->u.cVal, sizeof(var->u.cVal),
1648 bytesWritten = count;
1655 StorageUtl_WriteWord((LPBYTE)&wTemp, 0, var->u.iVal);
1656 hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count);
1657 bytesWritten = count;
1665 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, var->u.lVal);
1666 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1667 bytesWritten = count;
1674 if (This->codePage == CP_UNICODE)
1675 len = (lstrlenW(var->u.pwszVal) + 1) * sizeof(WCHAR);
1677 len = lstrlenA(var->u.pszVal) + 1;
1678 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1679 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1682 hr = IStream_Write(This->stm, var->u.pszVal, len, &count);
1683 bytesWritten = count + sizeof(DWORD);
1688 DWORD len = lstrlenW(var->u.pwszVal) + 1, dwTemp;
1690 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1691 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1694 hr = IStream_Write(This->stm, var->u.pwszVal, len * sizeof(WCHAR),
1696 bytesWritten = count + sizeof(DWORD);
1703 StorageUtl_WriteULargeInteger((BYTE *)&temp, 0,
1704 (ULARGE_INTEGER *)&var->u.filetime);
1705 hr = IStream_Write(This->stm, &temp, sizeof(FILETIME), &count);
1706 bytesWritten = count;
1710 FIXME("unsupported type: %d\n", var->vt);
1711 return STG_E_INVALIDPARAMETER;
1716 *sectionOffset += bytesWritten;
1717 if (bytesWritten % sizeof(DWORD))
1719 TRACE("adding %ld bytes of padding\n", sizeof(DWORD) -
1720 bytesWritten % sizeof(DWORD));
1721 *sectionOffset += sizeof(DWORD) - bytesWritten % sizeof(DWORD);
1729 struct PropertyClosure
1733 DWORD *sectionOffset;
1736 static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value,
1737 void *extra, void *closure)
1739 PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
1740 struct PropertyClosure *c = (struct PropertyClosure *)closure;
1746 c->hr = PropertyStorage_WritePropertyToStream(This,
1747 c->propNum++, (DWORD)key, (PROPVARIANT *)value, c->sectionOffset);
1748 return SUCCEEDED(c->hr);
1751 static HRESULT PropertyStorage_WritePropertiesToStream(
1752 PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset)
1754 struct PropertyClosure closure;
1757 assert(sectionOffset);
1759 closure.propNum = startingPropNum;
1760 closure.sectionOffset = sectionOffset;
1761 dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter,
1766 static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This)
1770 LARGE_INTEGER seek = { {0} };
1771 PROPERTYSETHEADER hdr;
1772 FORMATIDOFFSET fmtOffset;
1775 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1778 PropertyStorage_MakeHeader(This, &hdr);
1779 hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count);
1782 if (count != sizeof(hdr))
1784 hr = STG_E_WRITEFAULT;
1788 PropertyStorage_MakeFmtIdOffset(This, &fmtOffset);
1789 hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count);
1792 if (count != sizeof(fmtOffset))
1794 hr = STG_E_WRITEFAULT;
1803 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This)
1805 PROPERTYSECTIONHEADER sectionHdr;
1809 DWORD numProps, prop, sectionOffset, dwTemp;
1814 PropertyStorage_WriteHeadersToStream(This);
1816 /* Count properties. Always at least one property, the code page */
1818 if (dictionary_num_entries(This->name_to_propid))
1820 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1822 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1824 numProps += dictionary_num_entries(This->propid_to_prop);
1826 /* Write section header with 0 bytes right now, I'll adjust it after
1827 * writing properties.
1829 PropertyStorage_MakeSectionHdr(0, numProps, §ionHdr);
1830 seek.QuadPart = SECTIONHEADER_OFFSET;
1831 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1834 hr = IStream_Write(This->stm, §ionHdr, sizeof(sectionHdr), &count);
1839 sectionOffset = sizeof(PROPERTYSECTIONHEADER) +
1840 numProps * sizeof(PROPERTYIDOFFSET);
1842 if (dictionary_num_entries(This->name_to_propid))
1845 hr = PropertyStorage_WriteDictionaryToStream(This, §ionOffset);
1850 PropVariantInit(&var);
1853 var.u.iVal = This->codePage;
1854 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE,
1855 &var, §ionOffset);
1859 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1862 var.u.lVal = This->locale;
1863 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE,
1864 &var, §ionOffset);
1869 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1873 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR,
1874 &var, §ionOffset);
1879 hr = PropertyStorage_WritePropertiesToStream(This, prop, §ionOffset);
1883 /* Now write the byte count of the section */
1884 seek.QuadPart = SECTIONHEADER_OFFSET;
1885 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1888 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, sectionOffset);
1889 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1895 /***********************************************************************
1896 * PropertyStorage_Construct
1898 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This)
1901 dictionary_destroy(This->name_to_propid);
1902 This->name_to_propid = NULL;
1903 dictionary_destroy(This->propid_to_name);
1904 This->propid_to_name = NULL;
1905 dictionary_destroy(This->propid_to_prop);
1906 This->propid_to_prop = NULL;
1909 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This)
1914 This->name_to_propid = dictionary_create(
1915 PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy,
1917 if (!This->name_to_propid)
1919 hr = STG_E_INSUFFICIENTMEMORY;
1922 This->propid_to_name = dictionary_create(PropertyStorage_PropCompare,
1924 if (!This->propid_to_name)
1926 hr = STG_E_INSUFFICIENTMEMORY;
1929 This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
1930 PropertyStorage_PropertyDestroy, This);
1931 if (!This->propid_to_prop)
1933 hr = STG_E_INSUFFICIENTMEMORY;
1938 PropertyStorage_DestroyDictionaries(This);
1942 static HRESULT PropertyStorage_BaseConstruct(IStream *stm,
1943 REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps)
1949 *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps);
1951 return E_OUTOFMEMORY;
1953 (*pps)->vtbl = &IPropertyStorage_Vtbl;
1955 InitializeCriticalSection(&(*pps)->cs);
1957 memcpy(&(*pps)->fmtid, rfmtid, sizeof((*pps)->fmtid));
1958 (*pps)->grfMode = grfMode;
1960 hr = PropertyStorage_CreateDictionaries(*pps);
1963 IStream_Release(stm);
1964 DeleteCriticalSection(&(*pps)->cs);
1965 HeapFree(GetProcessHeap(), 0, *pps);
1972 static HRESULT PropertyStorage_ConstructFromStream(IStream *stm,
1973 REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps)
1975 PropertyStorage_impl *ps;
1979 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
1982 hr = PropertyStorage_ReadFromStream(ps);
1985 *pps = (IPropertyStorage *)ps;
1986 TRACE("PropertyStorage %p constructed\n", ps);
1991 PropertyStorage_DestroyDictionaries(ps);
1992 HeapFree(GetProcessHeap(), 0, ps);
1998 static HRESULT PropertyStorage_ConstructEmpty(IStream *stm,
1999 REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
2001 PropertyStorage_impl *ps;
2005 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2009 ps->grfFlags = grfFlags;
2010 if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2012 /* default to Unicode unless told not to, as specified here:
2013 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2015 if (ps->grfFlags & PROPSETFLAG_ANSI)
2016 ps->codePage = GetACP();
2018 ps->codePage = CP_UNICODE;
2019 ps->locale = LOCALE_SYSTEM_DEFAULT;
2020 TRACE("Code page is %d, locale is %ld\n", ps->codePage, ps->locale);
2021 *pps = (IPropertyStorage *)ps;
2022 TRACE("PropertyStorage %p constructed\n", ps);
2029 /***********************************************************************
2030 * Implementation of IPropertySetStorage
2033 /************************************************************************
2034 * IPropertySetStorage_fnQueryInterface (IUnknown)
2036 * This method forwards to the common QueryInterface implementation
2038 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
2039 IPropertySetStorage *ppstg,
2043 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2044 return IStorage_QueryInterface( (IStorage*)This, riid, ppvObject );
2047 /************************************************************************
2048 * IPropertySetStorage_fnAddRef (IUnknown)
2050 * This method forwards to the common AddRef implementation
2052 static ULONG WINAPI IPropertySetStorage_fnAddRef(
2053 IPropertySetStorage *ppstg)
2055 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2056 return IStorage_AddRef( (IStorage*)This );
2059 /************************************************************************
2060 * IPropertySetStorage_fnRelease (IUnknown)
2062 * This method forwards to the common Release implementation
2064 static ULONG WINAPI IPropertySetStorage_fnRelease(
2065 IPropertySetStorage *ppstg)
2067 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2068 return IStorage_Release( (IStorage*)This );
2071 /************************************************************************
2072 * IPropertySetStorage_fnCreate (IPropertySetStorage)
2074 static HRESULT WINAPI IPropertySetStorage_fnCreate(
2075 IPropertySetStorage *ppstg,
2077 const CLSID* pclsid,
2080 IPropertyStorage** ppprstg)
2082 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2083 WCHAR name[CCH_MAX_PROPSTG_NAME];
2084 IStream *stm = NULL;
2087 TRACE("%p %s %08lx %08lx %p\n", This, debugstr_guid(rfmtid), grfFlags,
2091 if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
2093 r = STG_E_INVALIDFLAG;
2103 /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
2104 * storage, not a stream. For now, disallow it.
2106 if (grfFlags & PROPSETFLAG_NONSIMPLE)
2108 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2109 r = STG_E_INVALIDFLAG;
2113 r = FmtIdToPropStgName(rfmtid, name);
2117 r = IStorage_CreateStream( (IStorage*)This, name, grfMode, 0, 0, &stm );
2121 r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg);
2124 TRACE("returning 0x%08lx\n", r);
2128 /************************************************************************
2129 * IPropertySetStorage_fnOpen (IPropertySetStorage)
2131 static HRESULT WINAPI IPropertySetStorage_fnOpen(
2132 IPropertySetStorage *ppstg,
2135 IPropertyStorage** ppprstg)
2137 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2138 IStream *stm = NULL;
2139 WCHAR name[CCH_MAX_PROPSTG_NAME];
2142 TRACE("%p %s %08lx %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
2145 if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
2146 grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
2148 r = STG_E_INVALIDFLAG;
2158 r = FmtIdToPropStgName(rfmtid, name);
2162 r = IStorage_OpenStream((IStorage*) This, name, 0, grfMode, 0, &stm );
2166 r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg);
2169 TRACE("returning 0x%08lx\n", r);
2173 /************************************************************************
2174 * IPropertySetStorage_fnDelete (IPropertySetStorage)
2176 static HRESULT WINAPI IPropertySetStorage_fnDelete(
2177 IPropertySetStorage *ppstg,
2180 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2181 IStorage *stg = NULL;
2182 WCHAR name[CCH_MAX_PROPSTG_NAME];
2185 TRACE("%p %s\n", This, debugstr_guid(rfmtid));
2188 return E_INVALIDARG;
2190 r = FmtIdToPropStgName(rfmtid, name);
2194 stg = (IStorage*) This;
2195 return IStorage_DestroyElement(stg, name);
2198 /************************************************************************
2199 * IPropertySetStorage_fnEnum (IPropertySetStorage)
2201 static HRESULT WINAPI IPropertySetStorage_fnEnum(
2202 IPropertySetStorage *ppstg,
2203 IEnumSTATPROPSETSTG** ppenum)
2205 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2206 FIXME("%p\n", This);
2211 /***********************************************************************
2214 const IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
2216 IPropertySetStorage_fnQueryInterface,
2217 IPropertySetStorage_fnAddRef,
2218 IPropertySetStorage_fnRelease,
2219 IPropertySetStorage_fnCreate,
2220 IPropertySetStorage_fnOpen,
2221 IPropertySetStorage_fnDelete,
2222 IPropertySetStorage_fnEnum
2225 static const IPropertyStorageVtbl IPropertyStorage_Vtbl =
2227 IPropertyStorage_fnQueryInterface,
2228 IPropertyStorage_fnAddRef,
2229 IPropertyStorage_fnRelease,
2230 IPropertyStorage_fnReadMultiple,
2231 IPropertyStorage_fnWriteMultiple,
2232 IPropertyStorage_fnDeleteMultiple,
2233 IPropertyStorage_fnReadPropertyNames,
2234 IPropertyStorage_fnWritePropertyNames,
2235 IPropertyStorage_fnDeletePropertyNames,
2236 IPropertyStorage_fnCommit,
2237 IPropertyStorage_fnRevert,
2238 IPropertyStorage_fnEnum,
2239 IPropertyStorage_fnSetTimes,
2240 IPropertyStorage_fnSetClass,
2241 IPropertyStorage_fnStat,
2244 /***********************************************************************
2245 * Format ID <-> name conversion
2247 static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
2248 'I','n','f','o','r','m','a','t','i','o','n',0 };
2249 static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t',
2250 'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
2252 #define BITS_PER_BYTE 8
2253 #define CHARMASK 0x1f
2254 #define BITS_IN_CHARMASK 5
2255 #define NUM_ALPHA_CHARS 26
2257 /***********************************************************************
2258 * FmtIdToPropStgName [ole32.@]
2259 * Returns the storage name of the format ID rfmtid.
2261 * rfmtid [I] Format ID for which to return a storage name
2262 * str [O] Storage name associated with rfmtid.
2265 * E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise.
2268 * str must be at least CCH_MAX_PROPSTG_NAME characters in length.
2269 * Based on the algorithm described here:
2270 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2272 HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str)
2274 static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
2276 TRACE("%s, %p\n", debugstr_guid(rfmtid), str);
2278 if (!rfmtid) return E_INVALIDARG;
2279 if (!str) return E_INVALIDARG;
2281 if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
2282 lstrcpyW(str, szSummaryInfo);
2283 else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
2284 lstrcpyW(str, szDocSummaryInfo);
2285 else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid))
2286 lstrcpyW(str, szDocSummaryInfo);
2291 ULONG bitsRemaining = BITS_PER_BYTE;
2294 for (fmtptr = (BYTE *)rfmtid; fmtptr < (BYTE *)rfmtid + sizeof(FMTID); )
2296 ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
2298 if (bitsRemaining >= BITS_IN_CHARMASK)
2300 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
2301 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
2305 bitsRemaining -= BITS_IN_CHARMASK;
2306 if (bitsRemaining == 0)
2309 bitsRemaining = BITS_PER_BYTE;
2314 if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID))
2315 i |= *fmtptr << bitsRemaining;
2316 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
2317 bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK;
2322 TRACE("returning %s\n", debugstr_w(str));
2326 /***********************************************************************
2327 * PropStgNameToFmtId [ole32.@]
2328 * Returns the format ID corresponding to the given name.
2330 * str [I] Storage name to convert to a format ID.
2331 * rfmtid [O] Format ID corresponding to str.
2334 * E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to
2335 * a format ID, S_OK otherwise.
2338 * Based on the algorithm described here:
2339 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2341 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid)
2343 HRESULT hr = STG_E_INVALIDNAME;
2345 TRACE("%s, %p\n", debugstr_w(str), rfmtid);
2347 if (!rfmtid) return E_INVALIDARG;
2348 if (!str) return STG_E_INVALIDNAME;
2350 if (!lstrcmpiW(str, szDocSummaryInfo))
2352 memcpy(rfmtid, &FMTID_DocSummaryInformation, sizeof(*rfmtid));
2355 else if (!lstrcmpiW(str, szSummaryInfo))
2357 memcpy(rfmtid, &FMTID_SummaryInformation, sizeof(*rfmtid));
2363 BYTE *fmtptr = (BYTE *)rfmtid - 1;
2364 const WCHAR *pstr = str;
2366 memset(rfmtid, 0, sizeof(*rfmtid));
2367 for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE;
2368 bits += BITS_IN_CHARMASK)
2370 ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored;
2376 if (wc > NUM_ALPHA_CHARS)
2379 if (wc > NUM_ALPHA_CHARS)
2381 wc += 'a' - '0' + NUM_ALPHA_CHARS;
2384 WARN("invalid character (%d)\n", *pstr);
2389 *fmtptr |= wc << bitsUsed;
2390 bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK);
2391 if (bitsStored < BITS_IN_CHARMASK)
2393 wc >>= BITS_PER_BYTE - bitsUsed;
2394 if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE)
2398 WARN("extra bits\n");
2404 *fmtptr |= (BYTE)wc;