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((LPWSTR
)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
= (BYTE
*)rfmtid
; fmtptr
< (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
;