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