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.
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
51 #define NONAMELESSUNION
52 #define NONAMELESSSTRUCT
58 #include "wine/unicode.h"
59 #include "wine/debug.h"
60 #include "dictionary.h"
61 #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 #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 */
102 typedef struct tagFORMATIDOFFSET
105 DWORD dwOffset; /* from beginning of stream */
108 typedef struct tagPROPERTYSECTIONHEADER
112 } PROPERTYSECTIONHEADER;
114 typedef struct tagPROPERTYIDOFFSET
117 DWORD dwOffset; /* from beginning of section */
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,
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;
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,
192 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
194 if ( (This==0) || (ppvObject==0) )
199 if (IsEqualGUID(&IID_IUnknown, riid) ||
200 IsEqualGUID(&IID_IPropertyStorage, riid))
202 IPropertyStorage_AddRef(iface);
203 *ppvObject = (IPropertyStorage*)iface;
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;
229 ref = InterlockedDecrement(&This->ref);
232 TRACE("Destroying %p\n", This);
234 IPropertyStorage_Commit(iface, STGC_DEFAULT);
235 IStream_Release(This->stm);
236 DeleteCriticalSection(&This->cs);
237 PropertyStorage_DestroyDictionaries(This);
238 HeapFree(GetProcessHeap(), 0, This);
243 static PROPVARIANT *PropertyStorage_FindProperty(PropertyStorage_impl *This,
246 PROPVARIANT *ret = NULL;
248 dictionary_find(This->propid_to_prop, (void *)propid, (void **)&ret);
249 TRACE("returning %p\n", ret);
253 /* Returns NULL if name is NULL. */
254 static PROPVARIANT *PropertyStorage_FindPropertyByName(
255 PropertyStorage_impl *This, LPCWSTR name)
257 PROPVARIANT *ret = NULL;
262 if (This->codePage == CP_UNICODE)
264 if (dictionary_find(This->name_to_propid, name, (void **)&propid))
265 ret = PropertyStorage_FindProperty(This, (PROPID)propid);
270 HRESULT hr = PropertyStorage_StringCopy((LPCSTR)name, CP_UNICODE,
271 &ansiName, This->codePage);
275 if (dictionary_find(This->name_to_propid, ansiName,
277 ret = PropertyStorage_FindProperty(This, (PROPID)propid);
278 CoTaskMemFree(ansiName);
281 TRACE("returning %p\n", ret);
285 static LPWSTR PropertyStorage_FindPropertyNameById(PropertyStorage_impl *This,
290 dictionary_find(This->propid_to_name, (void *)propid, (void **)&ret);
291 TRACE("returning %p\n", ret);
295 /************************************************************************
296 * IPropertyStorage_fnReadMultiple (IPropertyStorage)
298 static HRESULT WINAPI IPropertyStorage_fnReadMultiple(
299 IPropertyStorage* iface,
301 const PROPSPEC rgpspec[],
302 PROPVARIANT rgpropvar[])
304 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
308 TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
312 if (!rgpspec || !rgpropvar)
314 EnterCriticalSection(&This->cs);
315 for (i = 0; i < cpspec; i++)
317 PropVariantInit(&rgpropvar[i]);
318 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
320 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
321 rgpspec[i].u.lpwstr);
324 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop, GetACP(),
329 switch (rgpspec[i].u.propid)
332 rgpropvar[i].vt = VT_I2;
333 rgpropvar[i].u.iVal = This->codePage;
336 rgpropvar[i].vt = VT_I4;
337 rgpropvar[i].u.lVal = This->locale;
341 PROPVARIANT *prop = PropertyStorage_FindProperty(This,
342 rgpspec[i].u.propid);
345 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop,
346 GetACP(), This->codePage);
353 LeaveCriticalSection(&This->cs);
357 static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
363 TRACE("%s, %p, %ld, %ld\n",
364 srcCP == CP_UNICODE ? debugstr_w((LPCWSTR)src) : debugstr_a(src), dst,
373 if (dstCP == CP_UNICODE)
374 len = (strlenW((LPCWSTR)src) + 1) * sizeof(WCHAR);
376 len = strlen(src) + 1;
377 *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
379 hr = STG_E_INSUFFICIENTMEMORY;
381 memcpy(*dst, src, len);
385 if (dstCP == CP_UNICODE)
387 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
388 *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
390 hr = STG_E_INSUFFICIENTMEMORY;
392 MultiByteToWideChar(srcCP, 0, src, -1, (LPWSTR)*dst, len);
398 if (srcCP == CP_UNICODE)
399 wideStr = (LPWSTR)src;
402 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
403 wideStr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
405 MultiByteToWideChar(srcCP, 0, src, -1, wideStr, len);
407 hr = STG_E_INSUFFICIENTMEMORY;
411 len = WideCharToMultiByte(dstCP, 0, wideStr, -1, NULL, 0,
413 *dst = CoTaskMemAlloc(len);
415 hr = STG_E_INSUFFICIENTMEMORY;
418 BOOL defCharUsed = FALSE;
420 if (WideCharToMultiByte(dstCP, 0, wideStr, -1, *dst, len,
421 NULL, &defCharUsed) == 0 || defCharUsed)
425 hr = HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION);
429 if (wideStr != (LPWSTR)src)
430 HeapFree(GetProcessHeap(), 0, wideStr);
433 TRACE("returning 0x%08lx (%s)\n", hr,
434 dstCP == CP_UNICODE ? debugstr_w((LPCWSTR)*dst) : debugstr_a(*dst));
438 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
439 const PROPVARIANT *propvar, LCID targetCP, LCID srcCP)
445 if (propvar->vt == VT_LPSTR)
447 hr = PropertyStorage_StringCopy(propvar->u.pszVal, srcCP,
448 &prop->u.pszVal, targetCP);
453 PropVariantCopy(prop, propvar);
457 /* Stores the property with id propid and value propvar into this property
458 * storage. lcid is ignored if propvar's type is not VT_LPSTR. If propvar's
459 * type is VT_LPSTR, converts the string using lcid as the source code page
460 * and This->codePage as the target code page before storing.
461 * As a side effect, may change This->format to 1 if the type of propvar is
462 * a version 1-only property.
464 static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
465 PROPID propid, const PROPVARIANT *propvar, LCID lcid)
468 PROPVARIANT *prop = PropertyStorage_FindProperty(This, propid);
471 if (propvar->vt & VT_BYREF || propvar->vt & VT_ARRAY)
479 case VT_VECTOR|VT_I1:
482 TRACE("Setting 0x%08lx to type %d\n", propid, propvar->vt);
485 PropVariantClear(prop);
486 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
491 prop = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
492 sizeof(PROPVARIANT));
495 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
499 dictionary_insert(This->propid_to_prop, (void *)propid, prop);
500 if (propid > This->highestProp)
501 This->highestProp = propid;
504 HeapFree(GetProcessHeap(), 0, prop);
507 hr = STG_E_INSUFFICIENTMEMORY;
512 /* Adds the name srcName to the name dictionaries, mapped to property ID id.
513 * srcName is encoded in code page cp, and is converted to This->codePage.
514 * If cp is CP_UNICODE, srcName is actually a unicode string.
515 * As a side effect, may change This->format to 1 if srcName is too long for
516 * a version 0 property storage.
517 * Doesn't validate id.
519 static HRESULT PropertyStorage_StoreNameWithId(PropertyStorage_impl *This,
520 LPCSTR srcName, LCID cp, PROPID id)
527 hr = PropertyStorage_StringCopy((LPCSTR)srcName, cp, &name, This->codePage);
530 if (This->codePage == CP_UNICODE)
532 if (lstrlenW((LPWSTR)name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
537 if (strlen(name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
540 TRACE("Adding prop name %s, propid %ld\n",
541 This->codePage == CP_UNICODE ? debugstr_w((LPCWSTR)name) :
542 debugstr_a(name), id);
543 dictionary_insert(This->name_to_propid, name, (void *)id);
544 dictionary_insert(This->propid_to_name, (void *)id, name);
549 /************************************************************************
550 * IPropertyStorage_fnWriteMultiple (IPropertyStorage)
552 static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
553 IPropertyStorage* iface,
555 const PROPSPEC rgpspec[],
556 const PROPVARIANT rgpropvar[],
557 PROPID propidNameFirst)
559 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
563 TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
565 if (cpspec && (!rgpspec || !rgpropvar))
567 if (!(This->grfMode & STGM_READWRITE))
568 return STG_E_ACCESSDENIED;
569 EnterCriticalSection(&This->cs);
571 This->originatorOS = (DWORD)MAKELONG(LOWORD(GetVersion()),
572 PROPSETHDR_OSVER_KIND_WIN32) ;
573 for (i = 0; i < cpspec; i++)
575 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
577 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
578 rgpspec[i].u.lpwstr);
581 PropVariantCopy(prop, &rgpropvar[i]);
584 /* Note that I don't do the special cases here that I do below,
585 * because naming the special PIDs isn't supported.
587 if (propidNameFirst < PID_FIRST_USABLE ||
588 propidNameFirst >= PID_MIN_READONLY)
589 hr = STG_E_INVALIDPARAMETER;
592 PROPID nextId = max(propidNameFirst, This->highestProp + 1);
594 hr = PropertyStorage_StoreNameWithId(This,
595 (LPCSTR)rgpspec[i].u.lpwstr, CP_UNICODE, nextId);
597 hr = PropertyStorage_StorePropWithId(This, nextId,
598 &rgpropvar[i], GetACP());
604 switch (rgpspec[i].u.propid)
607 /* Can't set the dictionary */
608 hr = STG_E_INVALIDPARAMETER;
611 /* Can only set the code page if nothing else has been set */
612 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
613 rgpropvar[i].vt == VT_I2)
615 This->codePage = rgpropvar[i].u.iVal;
616 if (This->codePage == CP_UNICODE)
617 This->grfFlags &= ~PROPSETFLAG_ANSI;
619 This->grfFlags |= PROPSETFLAG_ANSI;
622 hr = STG_E_INVALIDPARAMETER;
625 /* Can only set the locale if nothing else has been set */
626 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
627 rgpropvar[i].vt == VT_I4)
628 This->locale = rgpropvar[i].u.lVal;
630 hr = STG_E_INVALIDPARAMETER;
633 /* silently ignore like MSDN says */
636 if (rgpspec[i].u.propid >= PID_MIN_READONLY)
637 hr = STG_E_INVALIDPARAMETER;
639 hr = PropertyStorage_StorePropWithId(This,
640 rgpspec[i].u.propid, &rgpropvar[i], GetACP());
644 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
645 IPropertyStorage_Commit(iface, STGC_DEFAULT);
646 LeaveCriticalSection(&This->cs);
650 /************************************************************************
651 * IPropertyStorage_fnDeleteMultiple (IPropertyStorage)
653 static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
654 IPropertyStorage* iface,
656 const PROPSPEC rgpspec[])
658 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
662 TRACE("(%p, %ld, %p)\n", iface, cpspec, rgpspec);
664 if (cpspec && !rgpspec)
666 if (!(This->grfMode & STGM_READWRITE))
667 return STG_E_ACCESSDENIED;
669 EnterCriticalSection(&This->cs);
671 for (i = 0; i < cpspec; i++)
673 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
677 if (dictionary_find(This->name_to_propid,
678 (void *)rgpspec[i].u.lpwstr, (void **)&propid))
679 dictionary_remove(This->propid_to_prop, (void *)propid);
683 if (rgpspec[i].u.propid >= PID_FIRST_USABLE &&
684 rgpspec[i].u.propid < PID_MIN_READONLY)
685 dictionary_remove(This->propid_to_prop,
686 (void *)rgpspec[i].u.propid);
688 hr = STG_E_INVALIDPARAMETER;
691 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
692 IPropertyStorage_Commit(iface, STGC_DEFAULT);
693 LeaveCriticalSection(&This->cs);
697 /************************************************************************
698 * IPropertyStorage_fnReadPropertyNames (IPropertyStorage)
700 static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames(
701 IPropertyStorage* iface,
703 const PROPID rgpropid[],
704 LPOLESTR rglpwstrName[])
706 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
708 HRESULT hr = S_FALSE;
710 TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
712 if (cpropid && (!rgpropid || !rglpwstrName))
714 EnterCriticalSection(&This->cs);
715 for (i = 0; i < cpropid && SUCCEEDED(hr); i++)
717 LPWSTR name = PropertyStorage_FindPropertyNameById(This, rgpropid[i]);
721 size_t len = lstrlenW(name);
724 rglpwstrName[i] = CoTaskMemAlloc((len + 1) * sizeof(WCHAR));
726 memcpy(rglpwstrName, name, (len + 1) * sizeof(WCHAR));
728 hr = STG_E_INSUFFICIENTMEMORY;
731 rglpwstrName[i] = NULL;
733 LeaveCriticalSection(&This->cs);
737 /************************************************************************
738 * IPropertyStorage_fnWritePropertyNames (IPropertyStorage)
740 static HRESULT WINAPI IPropertyStorage_fnWritePropertyNames(
741 IPropertyStorage* iface,
743 const PROPID rgpropid[],
744 const LPOLESTR rglpwstrName[])
746 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
750 TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
752 if (cpropid && (!rgpropid || !rglpwstrName))
754 if (!(This->grfMode & STGM_READWRITE))
755 return STG_E_ACCESSDENIED;
757 EnterCriticalSection(&This->cs);
759 for (i = 0; SUCCEEDED(hr) && i < cpropid; i++)
761 if (rgpropid[i] != PID_ILLEGAL)
762 hr = PropertyStorage_StoreNameWithId(This, (LPCSTR)rglpwstrName[i],
763 CP_UNICODE, rgpropid[i]);
765 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
766 IPropertyStorage_Commit(iface, STGC_DEFAULT);
767 LeaveCriticalSection(&This->cs);
771 /************************************************************************
772 * IPropertyStorage_fnDeletePropertyNames (IPropertyStorage)
774 static HRESULT WINAPI IPropertyStorage_fnDeletePropertyNames(
775 IPropertyStorage* iface,
777 const PROPID rgpropid[])
779 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
783 TRACE("(%p, %ld, %p)\n", iface, cpropid, rgpropid);
785 if (cpropid && !rgpropid)
787 if (!(This->grfMode & STGM_READWRITE))
788 return STG_E_ACCESSDENIED;
790 EnterCriticalSection(&This->cs);
792 for (i = 0; i < cpropid; i++)
796 if (dictionary_find(This->propid_to_name, (void *)rgpropid[i],
799 dictionary_remove(This->propid_to_name, (void *)rgpropid[i]);
800 dictionary_remove(This->name_to_propid, name);
803 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
804 IPropertyStorage_Commit(iface, STGC_DEFAULT);
805 LeaveCriticalSection(&This->cs);
809 /************************************************************************
810 * IPropertyStorage_fnCommit (IPropertyStorage)
812 static HRESULT WINAPI IPropertyStorage_fnCommit(
813 IPropertyStorage* iface,
814 DWORD grfCommitFlags)
816 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
819 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);
843 EnterCriticalSection(&This->cs);
846 PropertyStorage_DestroyDictionaries(This);
847 hr = PropertyStorage_CreateDictionaries(This);
849 hr = PropertyStorage_ReadFromStream(This);
853 LeaveCriticalSection(&This->cs);
857 /************************************************************************
858 * IPropertyStorage_fnEnum (IPropertyStorage)
860 static HRESULT WINAPI IPropertyStorage_fnEnum(
861 IPropertyStorage* iface,
862 IEnumSTATPROPSTG** ppenum)
864 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
865 return create_EnumSTATPROPSTG(This, ppenum);
868 /************************************************************************
869 * IPropertyStorage_fnSetTimes (IPropertyStorage)
871 static HRESULT WINAPI IPropertyStorage_fnSetTimes(
872 IPropertyStorage* iface,
873 const FILETIME* pctime,
874 const FILETIME* patime,
875 const FILETIME* pmtime)
881 /************************************************************************
882 * IPropertyStorage_fnSetClass (IPropertyStorage)
884 static HRESULT WINAPI IPropertyStorage_fnSetClass(
885 IPropertyStorage* iface,
888 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
890 TRACE("%p, %s\n", iface, debugstr_guid(clsid));
894 if (!(This->grfMode & STGM_READWRITE))
895 return STG_E_ACCESSDENIED;
896 memcpy(&This->clsid, clsid, sizeof(This->clsid));
898 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
899 IPropertyStorage_Commit(iface, STGC_DEFAULT);
903 /************************************************************************
904 * IPropertyStorage_fnStat (IPropertyStorage)
906 static HRESULT WINAPI IPropertyStorage_fnStat(
907 IPropertyStorage* iface,
908 STATPROPSETSTG* statpsstg)
910 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
914 TRACE("%p, %p\n", iface, 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;
1003 assert(This->name_to_propid);
1004 assert(This->propid_to_name);
1006 StorageUtl_ReadDWord(ptr, 0, &numEntries);
1007 TRACE("Reading %ld entries:\n", numEntries);
1008 ptr += sizeof(DWORD);
1009 for (i = 0; SUCCEEDED(hr) && i < numEntries; i++)
1014 StorageUtl_ReadDWord(ptr, 0, &propid);
1015 ptr += sizeof(PROPID);
1016 StorageUtl_ReadDWord(ptr, 0, &cbEntry);
1017 ptr += sizeof(DWORD);
1018 TRACE("Reading entry with ID 0x%08lx, %ld bytes\n", propid, cbEntry);
1019 /* Make sure the source string is NULL-terminated */
1020 if (This->codePage != CP_UNICODE)
1021 ptr[cbEntry - 1] = '\0';
1023 *((LPWSTR)ptr + cbEntry / sizeof(WCHAR)) = '\0';
1024 hr = PropertyStorage_StoreNameWithId(This, (char*)ptr, This->codePage, propid);
1025 if (This->codePage == CP_UNICODE)
1027 /* Unicode entries are padded to DWORD boundaries */
1028 if (cbEntry % sizeof(DWORD))
1029 ptr += sizeof(DWORD) - (cbEntry % sizeof(DWORD));
1031 ptr += sizeof(DWORD) + cbEntry;
1036 /* FIXME: there isn't any checking whether the read property extends past the
1037 * end of the buffer.
1039 static HRESULT PropertyStorage_ReadProperty(PropertyStorage_impl *This,
1040 PROPVARIANT *prop, const BYTE *data)
1046 StorageUtl_ReadDWord(data, 0, (DWORD *)&prop->vt);
1047 data += sizeof(DWORD);
1054 prop->u.cVal = *(const char *)data;
1055 TRACE("Read char 0x%x ('%c')\n", prop->u.cVal, prop->u.cVal);
1058 prop->u.bVal = *(const UCHAR *)data;
1059 TRACE("Read byte 0x%x\n", prop->u.bVal);
1062 StorageUtl_ReadWord(data, 0, (WORD*)&prop->u.iVal);
1063 TRACE("Read short %d\n", prop->u.iVal);
1066 StorageUtl_ReadWord(data, 0, &prop->u.uiVal);
1067 TRACE("Read ushort %d\n", prop->u.uiVal);
1071 StorageUtl_ReadDWord(data, 0, (DWORD*)&prop->u.lVal);
1072 TRACE("Read long %ld\n", prop->u.lVal);
1076 StorageUtl_ReadDWord(data, 0, &prop->u.ulVal);
1077 TRACE("Read ulong %ld\n", prop->u.ulVal);
1083 StorageUtl_ReadDWord(data, 0, &count);
1084 if (This->codePage == CP_UNICODE && count / 2)
1086 WARN("Unicode string has odd number of bytes\n");
1087 hr = STG_E_INVALIDHEADER;
1091 prop->u.pszVal = CoTaskMemAlloc(count);
1094 memcpy(prop->u.pszVal, data + sizeof(DWORD), count);
1095 /* This is stored in the code page specified in This->codePage.
1096 * Don't convert it, the caller will just store it as-is.
1098 if (This->codePage == CP_UNICODE)
1100 /* Make sure it's NULL-terminated */
1101 prop->u.pszVal[count / sizeof(WCHAR) - 1] = '\0';
1102 TRACE("Read string value %s\n",
1103 debugstr_w(prop->u.pwszVal));
1107 /* Make sure it's NULL-terminated */
1108 prop->u.pszVal[count - 1] = '\0';
1109 TRACE("Read string value %s\n", debugstr_a(prop->u.pszVal));
1113 hr = STG_E_INSUFFICIENTMEMORY;
1121 StorageUtl_ReadDWord(data, 0, &count);
1122 prop->u.pwszVal = CoTaskMemAlloc(count * sizeof(WCHAR));
1123 if (prop->u.pwszVal)
1125 memcpy(prop->u.pwszVal, data + sizeof(DWORD),
1126 count * sizeof(WCHAR));
1127 /* make sure string is NULL-terminated */
1128 prop->u.pwszVal[count - 1] = '\0';
1129 PropertyStorage_ByteSwapString(prop->u.pwszVal, count);
1130 TRACE("Read string value %s\n", debugstr_w(prop->u.pwszVal));
1133 hr = STG_E_INSUFFICIENTMEMORY;
1137 StorageUtl_ReadULargeInteger(data, 0,
1138 (ULARGE_INTEGER *)&prop->u.filetime);
1142 DWORD len = 0, tag = 0;
1144 StorageUtl_ReadDWord(data, 0, &len);
1145 StorageUtl_ReadDWord(data, 4, &tag);
1149 prop->u.pclipdata = CoTaskMemAlloc(sizeof (CLIPDATA));
1150 prop->u.pclipdata->cbSize = len;
1151 prop->u.pclipdata->ulClipFmt = tag;
1152 prop->u.pclipdata->pClipData = CoTaskMemAlloc(len);
1153 memcpy(prop->u.pclipdata->pClipData, data+8, len);
1156 hr = STG_E_INVALIDPARAMETER;
1160 FIXME("unsupported type %d\n", prop->vt);
1161 hr = STG_E_INVALIDPARAMETER;
1166 static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
1167 PROPERTYSETHEADER *hdr)
1169 BYTE buf[sizeof(PROPERTYSETHEADER)];
1175 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1178 if (count != sizeof(buf))
1180 WARN("read only %ld\n", count);
1181 hr = STG_E_INVALIDHEADER;
1185 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder),
1187 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat),
1189 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer),
1191 StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid),
1193 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved),
1197 TRACE("returning 0x%08lx\n", hr);
1201 static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm,
1202 FORMATIDOFFSET *fmt)
1204 BYTE buf[sizeof(FORMATIDOFFSET)];
1210 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1213 if (count != sizeof(buf))
1215 WARN("read only %ld\n", count);
1216 hr = STG_E_INVALIDHEADER;
1220 StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid),
1222 StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset),
1226 TRACE("returning 0x%08lx\n", hr);
1230 static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
1231 PROPERTYSECTIONHEADER *hdr)
1233 BYTE buf[sizeof(PROPERTYSECTIONHEADER)];
1239 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1242 if (count != sizeof(buf))
1244 WARN("read only %ld\n", count);
1245 hr = STG_E_INVALIDHEADER;
1249 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1250 cbSection), &hdr->cbSection);
1251 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1252 cProperties), &hdr->cProperties);
1255 TRACE("returning 0x%08lx\n", hr);
1259 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This)
1261 PROPERTYSETHEADER hdr;
1262 FORMATIDOFFSET fmtOffset;
1263 PROPERTYSECTIONHEADER sectionHdr;
1270 DWORD dictOffset = 0;
1272 This->dirty = FALSE;
1273 This->highestProp = 0;
1274 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
1277 if (stat.cbSize.u.HighPart)
1279 WARN("stream too big\n");
1280 /* maximum size varies, but it can't be this big */
1281 hr = STG_E_INVALIDHEADER;
1284 if (stat.cbSize.u.LowPart == 0)
1286 /* empty stream is okay */
1290 else if (stat.cbSize.u.LowPart < sizeof(PROPERTYSETHEADER) +
1291 sizeof(FORMATIDOFFSET))
1293 WARN("stream too small\n");
1294 hr = STG_E_INVALIDHEADER;
1298 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1301 hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr);
1302 /* I've only seen reserved == 1, but the article says I shouldn't disallow
1305 if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1)
1307 WARN("bad magic in prop set header\n");
1308 hr = STG_E_INVALIDHEADER;
1311 if (hdr.wFormat != 0 && hdr.wFormat != 1)
1313 WARN("bad format version %d\n", hdr.wFormat);
1314 hr = STG_E_INVALIDHEADER;
1317 This->format = hdr.wFormat;
1318 memcpy(&This->clsid, &hdr.clsid, sizeof(This->clsid));
1319 This->originatorOS = hdr.dwOSVer;
1320 if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC)
1321 WARN("File comes from a Mac, strings will probably be screwed up\n");
1322 hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset);
1325 if (fmtOffset.dwOffset > stat.cbSize.u.LowPart)
1327 WARN("invalid offset %ld (stream length is %ld)\n", fmtOffset.dwOffset,
1328 stat.cbSize.u.LowPart);
1329 hr = STG_E_INVALIDHEADER;
1332 /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
1333 * follow not one, but two sections. The first is the standard properties
1334 * for the document summary information, and the second is user-defined
1335 * properties. This is the only case in which multiple sections are
1337 * Reading the second stream isn't implemented yet.
1339 hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, §ionHdr);
1342 /* The section size includes the section header, so check it */
1343 if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER))
1345 WARN("section header too small, got %ld\n", sectionHdr.cbSection);
1346 hr = STG_E_INVALIDHEADER;
1349 buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection -
1350 sizeof(PROPERTYSECTIONHEADER));
1353 hr = STG_E_INSUFFICIENTMEMORY;
1356 hr = IStream_Read(This->stm, buf, sectionHdr.cbSection -
1357 sizeof(PROPERTYSECTIONHEADER), &count);
1360 TRACE("Reading %ld properties:\n", sectionHdr.cProperties);
1361 for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
1363 PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(buf +
1364 i * sizeof(PROPERTYIDOFFSET));
1366 if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) ||
1367 idOffset->dwOffset >= sectionHdr.cbSection - sizeof(DWORD))
1368 hr = STG_E_INVALIDPOINTER;
1371 if (idOffset->propid >= PID_FIRST_USABLE &&
1372 idOffset->propid < PID_MIN_READONLY && idOffset->propid >
1374 This->highestProp = idOffset->propid;
1375 if (idOffset->propid == PID_DICTIONARY)
1377 /* Don't read the dictionary yet, its entries depend on the
1378 * code page. Just store the offset so we know to read it
1381 dictOffset = idOffset->dwOffset;
1382 TRACE("Dictionary offset is %ld\n", dictOffset);
1388 PropVariantInit(&prop);
1389 if (SUCCEEDED(PropertyStorage_ReadProperty(This, &prop,
1390 buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER))))
1392 TRACE("Read property with ID 0x%08lx, type %d\n",
1393 idOffset->propid, prop.vt);
1394 switch(idOffset->propid)
1397 if (prop.vt == VT_I2)
1398 This->codePage = (UINT)prop.u.iVal;
1401 if (prop.vt == VT_I4)
1402 This->locale = (LCID)prop.u.lVal;
1405 if (prop.vt == VT_I4 && prop.u.lVal)
1406 This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
1407 /* The format should already be 1, but just in case */
1411 hr = PropertyStorage_StorePropWithId(This,
1412 idOffset->propid, &prop, This->codePage);
1418 if (!This->codePage)
1420 /* default to Unicode unless told not to, as specified here:
1421 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
1423 if (This->grfFlags & PROPSETFLAG_ANSI)
1424 This->codePage = GetACP();
1426 This->codePage = CP_UNICODE;
1429 This->locale = LOCALE_SYSTEM_DEFAULT;
1430 TRACE("Code page is %d, locale is %ld\n", This->codePage, This->locale);
1432 hr = PropertyStorage_ReadDictionary(This,
1433 buf + dictOffset - sizeof(PROPERTYSECTIONHEADER));
1436 HeapFree(GetProcessHeap(), 0, buf);
1439 dictionary_destroy(This->name_to_propid);
1440 This->name_to_propid = NULL;
1441 dictionary_destroy(This->propid_to_name);
1442 This->propid_to_name = NULL;
1443 dictionary_destroy(This->propid_to_prop);
1444 This->propid_to_prop = NULL;
1449 static void PropertyStorage_MakeHeader(PropertyStorage_impl *This,
1450 PROPERTYSETHEADER *hdr)
1453 StorageUtl_WriteWord((BYTE *)&hdr->wByteOrder, 0,
1454 PROPSETHDR_BYTEORDER_MAGIC);
1455 StorageUtl_WriteWord((BYTE *)&hdr->wFormat, 0, This->format);
1456 StorageUtl_WriteDWord((BYTE *)&hdr->dwOSVer, 0, This->originatorOS);
1457 StorageUtl_WriteGUID((BYTE *)&hdr->clsid, 0, &This->clsid);
1458 StorageUtl_WriteDWord((BYTE *)&hdr->reserved, 0, 1);
1461 static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl *This,
1462 FORMATIDOFFSET *fmtOffset)
1465 StorageUtl_WriteGUID((BYTE *)fmtOffset, 0, &This->fmtid);
1466 StorageUtl_WriteDWord((BYTE *)fmtOffset, offsetof(FORMATIDOFFSET, dwOffset),
1467 sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET));
1470 static void PropertyStorage_MakeSectionHdr(DWORD cbSection, DWORD numProps,
1471 PROPERTYSECTIONHEADER *hdr)
1474 StorageUtl_WriteDWord((BYTE *)hdr, 0, cbSection);
1475 StorageUtl_WriteDWord((BYTE *)hdr,
1476 offsetof(PROPERTYSECTIONHEADER, cProperties), numProps);
1479 static void PropertyStorage_MakePropertyIdOffset(DWORD propid, DWORD dwOffset,
1480 PROPERTYIDOFFSET *propIdOffset)
1482 assert(propIdOffset);
1483 StorageUtl_WriteDWord((BYTE *)propIdOffset, 0, propid);
1484 StorageUtl_WriteDWord((BYTE *)propIdOffset,
1485 offsetof(PROPERTYIDOFFSET, dwOffset), dwOffset);
1488 struct DictionaryClosure
1494 static BOOL PropertyStorage_DictionaryWriter(const void *key,
1495 const void *value, void *extra, void *closure)
1497 PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
1498 struct DictionaryClosure *c = (struct DictionaryClosure *)closure;
1504 StorageUtl_WriteDWord((LPBYTE)&propid, 0, (DWORD)value);
1505 c->hr = IStream_Write(This->stm, &propid, sizeof(propid), &count);
1508 c->bytesWritten += sizeof(DWORD);
1509 if (This->codePage == CP_UNICODE)
1511 DWORD keyLen, pad = 0;
1513 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0,
1514 (lstrlenW((LPCWSTR)key) + 1) * sizeof(WCHAR));
1515 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1518 c->bytesWritten += sizeof(DWORD);
1519 /* Rather than allocate a copy, I'll swap the string to little-endian
1520 * in-place, write it, then swap it back.
1522 PropertyStorage_ByteSwapString(key, keyLen);
1523 c->hr = IStream_Write(This->stm, key, keyLen, &count);
1524 PropertyStorage_ByteSwapString(key, keyLen);
1527 c->bytesWritten += keyLen;
1528 if (keyLen % sizeof(DWORD))
1530 c->hr = IStream_Write(This->stm, &pad,
1531 sizeof(DWORD) - keyLen % sizeof(DWORD), &count);
1534 c->bytesWritten += sizeof(DWORD) - keyLen % sizeof(DWORD);
1541 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0, strlen((LPCSTR)key) + 1);
1542 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1545 c->bytesWritten += sizeof(DWORD);
1546 c->hr = IStream_Write(This->stm, key, keyLen, &count);
1549 c->bytesWritten += keyLen;
1552 return SUCCEEDED(c->hr);
1555 #define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)
1557 /* Writes the dictionary to the stream. Assumes without checking that the
1558 * dictionary isn't empty.
1560 static HRESULT PropertyStorage_WriteDictionaryToStream(
1561 PropertyStorage_impl *This, DWORD *sectionOffset)
1565 PROPERTYIDOFFSET propIdOffset;
1568 struct DictionaryClosure closure;
1570 assert(sectionOffset);
1572 /* The dictionary's always the first property written, so seek to its
1575 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER);
1576 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1579 PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY, *sectionOffset,
1581 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1585 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1586 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1589 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0,
1590 dictionary_num_entries(This->name_to_propid));
1591 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1594 *sectionOffset += sizeof(dwTemp);
1597 closure.bytesWritten = 0;
1598 dictionary_enumerate(This->name_to_propid, PropertyStorage_DictionaryWriter,
1603 *sectionOffset += closure.bytesWritten;
1604 if (closure.bytesWritten % sizeof(DWORD))
1606 TRACE("adding %ld bytes of padding\n", sizeof(DWORD) -
1607 closure.bytesWritten % sizeof(DWORD));
1608 *sectionOffset += sizeof(DWORD) - closure.bytesWritten % sizeof(DWORD);
1615 static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This,
1616 DWORD propNum, DWORD propid, PROPVARIANT *var, DWORD *sectionOffset)
1620 PROPERTYIDOFFSET propIdOffset;
1622 DWORD dwType, bytesWritten;
1625 assert(sectionOffset);
1627 TRACE("%p, %ld, 0x%08lx, (%d), (%ld)\n", This, propNum, propid, var->vt,
1630 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) +
1631 propNum * sizeof(PROPERTYIDOFFSET);
1632 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1635 PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset);
1636 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1640 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1641 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1644 StorageUtl_WriteDWord((LPBYTE)&dwType, 0, var->vt);
1645 hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count);
1648 *sectionOffset += sizeof(dwType);
1658 hr = IStream_Write(This->stm, &var->u.cVal, sizeof(var->u.cVal),
1660 bytesWritten = count;
1667 StorageUtl_WriteWord((LPBYTE)&wTemp, 0, var->u.iVal);
1668 hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count);
1669 bytesWritten = count;
1677 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, var->u.lVal);
1678 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1679 bytesWritten = count;
1686 if (This->codePage == CP_UNICODE)
1687 len = (lstrlenW(var->u.pwszVal) + 1) * sizeof(WCHAR);
1689 len = lstrlenA(var->u.pszVal) + 1;
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.pszVal, len, &count);
1695 bytesWritten = count + sizeof(DWORD);
1700 DWORD len = lstrlenW(var->u.pwszVal) + 1, dwTemp;
1702 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1703 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1706 hr = IStream_Write(This->stm, var->u.pwszVal, len * sizeof(WCHAR),
1708 bytesWritten = count + sizeof(DWORD);
1715 StorageUtl_WriteULargeInteger((BYTE *)&temp, 0,
1716 (ULARGE_INTEGER *)&var->u.filetime);
1717 hr = IStream_Write(This->stm, &temp, sizeof(FILETIME), &count);
1718 bytesWritten = count;
1723 DWORD cf_hdr[2], len;
1725 len = var->u.pclipdata->cbSize;
1726 StorageUtl_WriteDWord((LPBYTE)&cf_hdr[0], 0, len + 8);
1727 StorageUtl_WriteDWord((LPBYTE)&cf_hdr[1], 0, var->u.pclipdata->ulClipFmt);
1728 hr = IStream_Write(This->stm, &cf_hdr, sizeof(cf_hdr), &count);
1731 hr = IStream_Write(This->stm, &var->u.pclipdata->pClipData, len, &count);
1734 bytesWritten = count + sizeof cf_hdr;
1738 FIXME("unsupported type: %d\n", var->vt);
1739 return STG_E_INVALIDPARAMETER;
1744 *sectionOffset += bytesWritten;
1745 if (bytesWritten % sizeof(DWORD))
1747 TRACE("adding %ld bytes of padding\n", sizeof(DWORD) -
1748 bytesWritten % sizeof(DWORD));
1749 *sectionOffset += sizeof(DWORD) - bytesWritten % sizeof(DWORD);
1757 struct PropertyClosure
1761 DWORD *sectionOffset;
1764 static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value,
1765 void *extra, void *closure)
1767 PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
1768 struct PropertyClosure *c = (struct PropertyClosure *)closure;
1774 c->hr = PropertyStorage_WritePropertyToStream(This,
1775 c->propNum++, (DWORD)key, (PROPVARIANT *)value, c->sectionOffset);
1776 return SUCCEEDED(c->hr);
1779 static HRESULT PropertyStorage_WritePropertiesToStream(
1780 PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset)
1782 struct PropertyClosure closure;
1784 assert(sectionOffset);
1786 closure.propNum = startingPropNum;
1787 closure.sectionOffset = sectionOffset;
1788 dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter,
1793 static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This)
1797 LARGE_INTEGER seek = { {0} };
1798 PROPERTYSETHEADER hdr;
1799 FORMATIDOFFSET fmtOffset;
1801 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1804 PropertyStorage_MakeHeader(This, &hdr);
1805 hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count);
1808 if (count != sizeof(hdr))
1810 hr = STG_E_WRITEFAULT;
1814 PropertyStorage_MakeFmtIdOffset(This, &fmtOffset);
1815 hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count);
1818 if (count != sizeof(fmtOffset))
1820 hr = STG_E_WRITEFAULT;
1829 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This)
1831 PROPERTYSECTIONHEADER sectionHdr;
1835 DWORD numProps, prop, sectionOffset, dwTemp;
1838 PropertyStorage_WriteHeadersToStream(This);
1840 /* Count properties. Always at least one property, the code page */
1842 if (dictionary_num_entries(This->name_to_propid))
1844 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1846 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1848 numProps += dictionary_num_entries(This->propid_to_prop);
1850 /* Write section header with 0 bytes right now, I'll adjust it after
1851 * writing properties.
1853 PropertyStorage_MakeSectionHdr(0, numProps, §ionHdr);
1854 seek.QuadPart = SECTIONHEADER_OFFSET;
1855 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1858 hr = IStream_Write(This->stm, §ionHdr, sizeof(sectionHdr), &count);
1863 sectionOffset = sizeof(PROPERTYSECTIONHEADER) +
1864 numProps * sizeof(PROPERTYIDOFFSET);
1866 if (dictionary_num_entries(This->name_to_propid))
1869 hr = PropertyStorage_WriteDictionaryToStream(This, §ionOffset);
1874 PropVariantInit(&var);
1877 var.u.iVal = This->codePage;
1878 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE,
1879 &var, §ionOffset);
1883 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1886 var.u.lVal = This->locale;
1887 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE,
1888 &var, §ionOffset);
1893 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1897 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR,
1898 &var, §ionOffset);
1903 hr = PropertyStorage_WritePropertiesToStream(This, prop, §ionOffset);
1907 /* Now write the byte count of the section */
1908 seek.QuadPart = SECTIONHEADER_OFFSET;
1909 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1912 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, sectionOffset);
1913 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1919 /***********************************************************************
1920 * PropertyStorage_Construct
1922 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This)
1924 dictionary_destroy(This->name_to_propid);
1925 This->name_to_propid = NULL;
1926 dictionary_destroy(This->propid_to_name);
1927 This->propid_to_name = NULL;
1928 dictionary_destroy(This->propid_to_prop);
1929 This->propid_to_prop = NULL;
1932 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This)
1936 This->name_to_propid = dictionary_create(
1937 PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy,
1939 if (!This->name_to_propid)
1941 hr = STG_E_INSUFFICIENTMEMORY;
1944 This->propid_to_name = dictionary_create(PropertyStorage_PropCompare,
1946 if (!This->propid_to_name)
1948 hr = STG_E_INSUFFICIENTMEMORY;
1951 This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
1952 PropertyStorage_PropertyDestroy, This);
1953 if (!This->propid_to_prop)
1955 hr = STG_E_INSUFFICIENTMEMORY;
1960 PropertyStorage_DestroyDictionaries(This);
1964 static HRESULT PropertyStorage_BaseConstruct(IStream *stm,
1965 REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps)
1971 *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps);
1973 return E_OUTOFMEMORY;
1975 (*pps)->vtbl = &IPropertyStorage_Vtbl;
1977 InitializeCriticalSection(&(*pps)->cs);
1979 memcpy(&(*pps)->fmtid, rfmtid, sizeof((*pps)->fmtid));
1980 (*pps)->grfMode = grfMode;
1982 hr = PropertyStorage_CreateDictionaries(*pps);
1985 IStream_Release(stm);
1986 DeleteCriticalSection(&(*pps)->cs);
1987 HeapFree(GetProcessHeap(), 0, *pps);
1994 static HRESULT PropertyStorage_ConstructFromStream(IStream *stm,
1995 REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps)
1997 PropertyStorage_impl *ps;
2001 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2004 hr = PropertyStorage_ReadFromStream(ps);
2007 *pps = (IPropertyStorage *)ps;
2008 TRACE("PropertyStorage %p constructed\n", ps);
2013 PropertyStorage_DestroyDictionaries(ps);
2014 HeapFree(GetProcessHeap(), 0, ps);
2020 static HRESULT PropertyStorage_ConstructEmpty(IStream *stm,
2021 REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
2023 PropertyStorage_impl *ps;
2027 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2031 ps->grfFlags = grfFlags;
2032 if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2034 /* default to Unicode unless told not to, as specified here:
2035 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2037 if (ps->grfFlags & PROPSETFLAG_ANSI)
2038 ps->codePage = GetACP();
2040 ps->codePage = CP_UNICODE;
2041 ps->locale = LOCALE_SYSTEM_DEFAULT;
2042 TRACE("Code page is %d, locale is %ld\n", ps->codePage, ps->locale);
2043 *pps = (IPropertyStorage *)ps;
2044 TRACE("PropertyStorage %p constructed\n", ps);
2051 /***********************************************************************
2052 * Implementation of IPropertySetStorage
2055 /************************************************************************
2056 * IPropertySetStorage_fnQueryInterface (IUnknown)
2058 * This method forwards to the common QueryInterface implementation
2060 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
2061 IPropertySetStorage *ppstg,
2065 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2066 return IStorage_QueryInterface( (IStorage*)This, riid, ppvObject );
2069 /************************************************************************
2070 * IPropertySetStorage_fnAddRef (IUnknown)
2072 * This method forwards to the common AddRef implementation
2074 static ULONG WINAPI IPropertySetStorage_fnAddRef(
2075 IPropertySetStorage *ppstg)
2077 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2078 return IStorage_AddRef( (IStorage*)This );
2081 /************************************************************************
2082 * IPropertySetStorage_fnRelease (IUnknown)
2084 * This method forwards to the common Release implementation
2086 static ULONG WINAPI IPropertySetStorage_fnRelease(
2087 IPropertySetStorage *ppstg)
2089 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2090 return IStorage_Release( (IStorage*)This );
2093 /************************************************************************
2094 * IPropertySetStorage_fnCreate (IPropertySetStorage)
2096 static HRESULT WINAPI IPropertySetStorage_fnCreate(
2097 IPropertySetStorage *ppstg,
2099 const CLSID* pclsid,
2102 IPropertyStorage** ppprstg)
2104 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2105 WCHAR name[CCH_MAX_PROPSTG_NAME];
2106 IStream *stm = NULL;
2109 TRACE("%p %s %08lx %08lx %p\n", This, debugstr_guid(rfmtid), grfFlags,
2113 if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
2115 r = STG_E_INVALIDFLAG;
2125 /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
2126 * storage, not a stream. For now, disallow it.
2128 if (grfFlags & PROPSETFLAG_NONSIMPLE)
2130 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2131 r = STG_E_INVALIDFLAG;
2135 r = FmtIdToPropStgName(rfmtid, name);
2139 r = IStorage_CreateStream( (IStorage*)This, name, grfMode, 0, 0, &stm );
2143 r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg);
2146 TRACE("returning 0x%08lx\n", r);
2150 /************************************************************************
2151 * IPropertySetStorage_fnOpen (IPropertySetStorage)
2153 static HRESULT WINAPI IPropertySetStorage_fnOpen(
2154 IPropertySetStorage *ppstg,
2157 IPropertyStorage** ppprstg)
2159 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2160 IStream *stm = NULL;
2161 WCHAR name[CCH_MAX_PROPSTG_NAME];
2164 TRACE("%p %s %08lx %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
2167 if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
2168 grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
2170 r = STG_E_INVALIDFLAG;
2180 r = FmtIdToPropStgName(rfmtid, name);
2184 r = IStorage_OpenStream((IStorage*) This, name, 0, grfMode, 0, &stm );
2188 r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg);
2191 TRACE("returning 0x%08lx\n", r);
2195 /************************************************************************
2196 * IPropertySetStorage_fnDelete (IPropertySetStorage)
2198 static HRESULT WINAPI IPropertySetStorage_fnDelete(
2199 IPropertySetStorage *ppstg,
2202 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2203 IStorage *stg = NULL;
2204 WCHAR name[CCH_MAX_PROPSTG_NAME];
2207 TRACE("%p %s\n", This, debugstr_guid(rfmtid));
2210 return E_INVALIDARG;
2212 r = FmtIdToPropStgName(rfmtid, name);
2216 stg = (IStorage*) This;
2217 return IStorage_DestroyElement(stg, name);
2220 /************************************************************************
2221 * IPropertySetStorage_fnEnum (IPropertySetStorage)
2223 static HRESULT WINAPI IPropertySetStorage_fnEnum(
2224 IPropertySetStorage *ppstg,
2225 IEnumSTATPROPSETSTG** ppenum)
2227 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2228 return create_EnumSTATPROPSETSTG(This, ppenum);
2231 /************************************************************************
2232 * Implement IEnumSTATPROPSETSTG using enumx
2234 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnQueryInterface(
2235 IEnumSTATPROPSETSTG *iface,
2239 return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
2242 static ULONG WINAPI IEnumSTATPROPSETSTG_fnAddRef(
2243 IEnumSTATPROPSETSTG *iface)
2245 return enumx_AddRef((enumx_impl*)iface);
2248 static ULONG WINAPI IEnumSTATPROPSETSTG_fnRelease(
2249 IEnumSTATPROPSETSTG *iface)
2251 return enumx_Release((enumx_impl*)iface);
2254 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnNext(
2255 IEnumSTATPROPSETSTG *iface,
2257 STATPROPSETSTG *rgelt,
2258 ULONG *pceltFetched)
2260 return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
2263 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnSkip(
2264 IEnumSTATPROPSETSTG *iface,
2267 return enumx_Skip((enumx_impl*)iface, celt);
2270 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnReset(
2271 IEnumSTATPROPSETSTG *iface)
2273 return enumx_Reset((enumx_impl*)iface);
2276 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnClone(
2277 IEnumSTATPROPSETSTG *iface,
2278 IEnumSTATPROPSETSTG **ppenum)
2280 return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
2283 static HRESULT create_EnumSTATPROPSETSTG(
2285 IEnumSTATPROPSETSTG** ppenum)
2287 IStorage *stg = (IStorage*) &This->base.lpVtbl;
2288 IEnumSTATSTG *penum = NULL;
2292 STATPROPSETSTG statpss;
2295 TRACE("%p %p\n", This, ppenum);
2297 enumx = enumx_allocate(&IID_IEnumSTATPROPSETSTG,
2298 &IEnumSTATPROPSETSTG_Vtbl,
2299 sizeof (STATPROPSETSTG));
2301 /* add all the property set elements into a list */
2302 r = IStorage_EnumElements(stg, 0, NULL, 0, &penum);
2304 return E_OUTOFMEMORY;
2309 r = IEnumSTATSTG_Next(penum, 1, &stat, &count);
2316 if (stat.pwcsName[0] == 5 && stat.type == STGTY_STREAM)
2318 PropStgNameToFmtId(stat.pwcsName, &statpss.fmtid);
2319 TRACE("adding %s (%s)\n", debugstr_w(stat.pwcsName),
2320 debugstr_guid(&statpss.fmtid));
2321 statpss.mtime = stat.mtime;
2322 statpss.atime = stat.atime;
2323 statpss.ctime = stat.ctime;
2324 statpss.grfFlags = stat.grfMode;
2325 memcpy(&statpss.clsid, &stat.clsid, sizeof stat.clsid);
2326 enumx_add_element(enumx, &statpss);
2328 CoTaskMemFree(stat.pwcsName);
2330 IEnumSTATSTG_Release(penum);
2332 *ppenum = (IEnumSTATPROPSETSTG*) enumx;
2337 /************************************************************************
2338 * Implement IEnumSTATPROPSTG using enumx
2340 static HRESULT WINAPI IEnumSTATPROPSTG_fnQueryInterface(
2341 IEnumSTATPROPSTG *iface,
2345 return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
2348 static ULONG WINAPI IEnumSTATPROPSTG_fnAddRef(
2349 IEnumSTATPROPSTG *iface)
2351 return enumx_AddRef((enumx_impl*)iface);
2354 static ULONG WINAPI IEnumSTATPROPSTG_fnRelease(
2355 IEnumSTATPROPSTG *iface)
2357 return enumx_Release((enumx_impl*)iface);
2360 static HRESULT WINAPI IEnumSTATPROPSTG_fnNext(
2361 IEnumSTATPROPSTG *iface,
2364 ULONG *pceltFetched)
2366 return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
2369 static HRESULT WINAPI IEnumSTATPROPSTG_fnSkip(
2370 IEnumSTATPROPSTG *iface,
2373 return enumx_Skip((enumx_impl*)iface, celt);
2376 static HRESULT WINAPI IEnumSTATPROPSTG_fnReset(
2377 IEnumSTATPROPSTG *iface)
2379 return enumx_Reset((enumx_impl*)iface);
2382 static HRESULT WINAPI IEnumSTATPROPSTG_fnClone(
2383 IEnumSTATPROPSTG *iface,
2384 IEnumSTATPROPSTG **ppenum)
2386 return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
2389 static BOOL prop_enum_stat(const void *k, const void *v, void *extra, void *arg)
2391 enumx_impl *enumx = arg;
2392 PROPID propid = (PROPID) k;
2393 const PROPVARIANT *prop = v;
2396 stat.lpwstrName = NULL;
2397 stat.propid = propid;
2400 enumx_add_element(enumx, &stat);
2405 static HRESULT create_EnumSTATPROPSTG(
2406 PropertyStorage_impl *This,
2407 IEnumSTATPROPSTG** ppenum)
2411 TRACE("%p %p\n", This, ppenum);
2413 enumx = enumx_allocate(&IID_IEnumSTATPROPSTG,
2414 &IEnumSTATPROPSTG_Vtbl,
2415 sizeof (STATPROPSTG));
2417 dictionary_enumerate(This->propid_to_prop, prop_enum_stat, enumx);
2419 *ppenum = (IEnumSTATPROPSTG*) enumx;
2424 /***********************************************************************
2427 const IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
2429 IPropertySetStorage_fnQueryInterface,
2430 IPropertySetStorage_fnAddRef,
2431 IPropertySetStorage_fnRelease,
2432 IPropertySetStorage_fnCreate,
2433 IPropertySetStorage_fnOpen,
2434 IPropertySetStorage_fnDelete,
2435 IPropertySetStorage_fnEnum
2438 static const IPropertyStorageVtbl IPropertyStorage_Vtbl =
2440 IPropertyStorage_fnQueryInterface,
2441 IPropertyStorage_fnAddRef,
2442 IPropertyStorage_fnRelease,
2443 IPropertyStorage_fnReadMultiple,
2444 IPropertyStorage_fnWriteMultiple,
2445 IPropertyStorage_fnDeleteMultiple,
2446 IPropertyStorage_fnReadPropertyNames,
2447 IPropertyStorage_fnWritePropertyNames,
2448 IPropertyStorage_fnDeletePropertyNames,
2449 IPropertyStorage_fnCommit,
2450 IPropertyStorage_fnRevert,
2451 IPropertyStorage_fnEnum,
2452 IPropertyStorage_fnSetTimes,
2453 IPropertyStorage_fnSetClass,
2454 IPropertyStorage_fnStat,
2457 static const IEnumSTATPROPSETSTGVtbl IEnumSTATPROPSETSTG_Vtbl =
2459 IEnumSTATPROPSETSTG_fnQueryInterface,
2460 IEnumSTATPROPSETSTG_fnAddRef,
2461 IEnumSTATPROPSETSTG_fnRelease,
2462 IEnumSTATPROPSETSTG_fnNext,
2463 IEnumSTATPROPSETSTG_fnSkip,
2464 IEnumSTATPROPSETSTG_fnReset,
2465 IEnumSTATPROPSETSTG_fnClone,
2468 static const IEnumSTATPROPSTGVtbl IEnumSTATPROPSTG_Vtbl =
2470 IEnumSTATPROPSTG_fnQueryInterface,
2471 IEnumSTATPROPSTG_fnAddRef,
2472 IEnumSTATPROPSTG_fnRelease,
2473 IEnumSTATPROPSTG_fnNext,
2474 IEnumSTATPROPSTG_fnSkip,
2475 IEnumSTATPROPSTG_fnReset,
2476 IEnumSTATPROPSTG_fnClone,
2479 /***********************************************************************
2480 * Format ID <-> name conversion
2482 static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
2483 'I','n','f','o','r','m','a','t','i','o','n',0 };
2484 static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t',
2485 'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
2487 #define BITS_PER_BYTE 8
2488 #define CHARMASK 0x1f
2489 #define BITS_IN_CHARMASK 5
2490 #define NUM_ALPHA_CHARS 26
2492 /***********************************************************************
2493 * FmtIdToPropStgName [ole32.@]
2494 * Returns the storage name of the format ID rfmtid.
2496 * rfmtid [I] Format ID for which to return a storage name
2497 * str [O] Storage name associated with rfmtid.
2500 * E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise.
2503 * str must be at least CCH_MAX_PROPSTG_NAME characters in length.
2504 * Based on the algorithm described here:
2505 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2507 HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str)
2509 static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
2511 TRACE("%s, %p\n", debugstr_guid(rfmtid), str);
2513 if (!rfmtid) return E_INVALIDARG;
2514 if (!str) return E_INVALIDARG;
2516 if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
2517 lstrcpyW(str, szSummaryInfo);
2518 else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
2519 lstrcpyW(str, szDocSummaryInfo);
2520 else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid))
2521 lstrcpyW(str, szDocSummaryInfo);
2526 ULONG bitsRemaining = BITS_PER_BYTE;
2529 for (fmtptr = (const BYTE *)rfmtid; fmtptr < (const BYTE *)rfmtid + sizeof(FMTID); )
2531 ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
2533 if (bitsRemaining >= BITS_IN_CHARMASK)
2535 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
2536 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
2540 bitsRemaining -= BITS_IN_CHARMASK;
2541 if (bitsRemaining == 0)
2544 bitsRemaining = BITS_PER_BYTE;
2549 if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID))
2550 i |= *fmtptr << bitsRemaining;
2551 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
2552 bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK;
2557 TRACE("returning %s\n", debugstr_w(str));
2561 /***********************************************************************
2562 * PropStgNameToFmtId [ole32.@]
2563 * Returns the format ID corresponding to the given name.
2565 * str [I] Storage name to convert to a format ID.
2566 * rfmtid [O] Format ID corresponding to str.
2569 * E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to
2570 * a format ID, S_OK otherwise.
2573 * Based on the algorithm described here:
2574 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2576 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid)
2578 HRESULT hr = STG_E_INVALIDNAME;
2580 TRACE("%s, %p\n", debugstr_w(str), rfmtid);
2582 if (!rfmtid) return E_INVALIDARG;
2583 if (!str) return STG_E_INVALIDNAME;
2585 if (!lstrcmpiW(str, szDocSummaryInfo))
2587 memcpy(rfmtid, &FMTID_DocSummaryInformation, sizeof(*rfmtid));
2590 else if (!lstrcmpiW(str, szSummaryInfo))
2592 memcpy(rfmtid, &FMTID_SummaryInformation, sizeof(*rfmtid));
2598 BYTE *fmtptr = (BYTE *)rfmtid - 1;
2599 const WCHAR *pstr = str;
2601 memset(rfmtid, 0, sizeof(*rfmtid));
2602 for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE;
2603 bits += BITS_IN_CHARMASK)
2605 ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored;
2611 if (wc > NUM_ALPHA_CHARS)
2614 if (wc > NUM_ALPHA_CHARS)
2616 wc += 'a' - '0' + NUM_ALPHA_CHARS;
2619 WARN("invalid character (%d)\n", *pstr);
2624 *fmtptr |= wc << bitsUsed;
2625 bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK);
2626 if (bitsStored < BITS_IN_CHARMASK)
2628 wc >>= BITS_PER_BYTE - bitsUsed;
2629 if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE)
2633 WARN("extra bits\n");
2639 *fmtptr |= (BYTE)wc;