2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
12 * Copyright 2005 Juan Lang
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 * There's a decent overview of property set storage here:
29 * http://msdn.microsoft.com/archive/en-us/dnarolegen/html/msdn_propset.asp
30 * It's a little bit out of date, and more definitive references are given
31 * below, but it gives the best "big picture" that I've found.
33 * TODO: There's a lot missing in here. Biggies:
34 * - There are all sorts of restricions I don't honor, like maximum property
35 * set byte size, maximum property name length
36 * - Certain bogus files could result in reading past the end of a buffer.
37 * - This will probably fail on big-endian machines, especially reading and
39 * - Mac-generated files won't be read correctly, even if they're little
40 * endian, because I disregard whether the generator was a Mac. This means
41 * strings will probably be munged (as I don't understand Mac scripts.)
42 * - Not all PROPVARIANT types are supported.
43 * There are lots more unimplemented features, see FIXMEs below.
53 #define NONAMELESSUNION
54 #define NONAMELESSSTRUCT
60 #include "wine/unicode.h"
61 #include "wine/debug.h"
62 #include "dictionary.h"
63 #include "storage32.h"
65 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
67 #define _IPropertySetStorage_Offset ((int)(&(((StorageImpl*)0)->base.pssVtbl)))
68 #define _ICOM_THIS_From_IPropertySetStorage(class, name) \
69 class* This = (class*)(((char*)name)-_IPropertySetStorage_Offset)
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 /* The format version (and what it implies) is described here:
84 * http://msdn.microsoft.com/library/en-us/stg/stg/format_version.asp
86 typedef struct tagPROPERTYSETHEADER
88 WORD wByteOrder
; /* always 0xfffe */
89 WORD wFormat
; /* can be zero or one */
90 DWORD dwOSVer
; /* OS version of originating system */
91 CLSID clsid
; /* application CLSID */
92 DWORD reserved
; /* always 1 */
95 typedef struct tagFORMATIDOFFSET
98 DWORD dwOffset
; /* from beginning of stream */
101 typedef struct tagPROPERTYSECTIONHEADER
105 } PROPERTYSECTIONHEADER
;
107 typedef struct tagPROPERTYIDOFFSET
110 DWORD dwOffset
; /* from beginning of section */
113 struct tagPropertyStorage_impl
;
115 /* Initializes the property storage from the stream (and undoes any uncommitted
116 * changes in the process.) Returns an error if there is an error reading or
117 * if the stream format doesn't match what's expected.
119 static HRESULT
PropertyStorage_ReadFromStream(struct tagPropertyStorage_impl
*);
121 static HRESULT
PropertyStorage_WriteToStream(struct tagPropertyStorage_impl
*);
123 /* Creates the dictionaries used by the property storage. If successful, all
124 * the dictionaries have been created. If failed, none has been. (This makes
125 * it a bit easier to deal with destroying them.)
127 static HRESULT
PropertyStorage_CreateDictionaries(
128 struct tagPropertyStorage_impl
*);
130 static void PropertyStorage_DestroyDictionaries(
131 struct tagPropertyStorage_impl
*);
133 static IPropertyStorageVtbl IPropertyStorage_Vtbl
;
135 /***********************************************************************
136 * Implementation of IPropertyStorage
138 typedef struct tagPropertyStorage_impl
140 IPropertyStorageVtbl
*vtbl
;
153 struct dictionary
*name_to_propid
;
154 struct dictionary
*propid_to_name
;
155 struct dictionary
*propid_to_prop
;
156 } PropertyStorage_impl
;
158 /************************************************************************
159 * IPropertyStorage_fnQueryInterface (IPropertyStorage)
161 static HRESULT WINAPI
IPropertyStorage_fnQueryInterface(
162 IPropertyStorage
*iface
,
166 PropertyStorage_impl
*This
= (PropertyStorage_impl
*)iface
;
168 if ( (This
==0) || (ppvObject
==0) )
173 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
174 IsEqualGUID(&IID_IPropertyStorage
, riid
))
176 IPropertyStorage_AddRef(iface
);
177 *ppvObject
= (IPropertyStorage
*)iface
;
181 return E_NOINTERFACE
;
184 /************************************************************************
185 * IPropertyStorage_fnAddRef (IPropertyStorage)
187 static ULONG WINAPI
IPropertyStorage_fnAddRef(
188 IPropertyStorage
*iface
)
190 PropertyStorage_impl
*This
= (PropertyStorage_impl
*)iface
;
191 return InterlockedIncrement(&This
->ref
);
194 /************************************************************************
195 * IPropertyStorage_fnRelease (IPropertyStorage)
197 static ULONG WINAPI
IPropertyStorage_fnRelease(
198 IPropertyStorage
*iface
)
200 PropertyStorage_impl
*This
= (PropertyStorage_impl
*)iface
;
203 ref
= InterlockedDecrement(&This
->ref
);
206 TRACE("Destroying %p\n", This
);
208 IPropertyStorage_Commit(iface
, STGC_DEFAULT
);
209 IStream_Release(This
->stm
);
210 DeleteCriticalSection(&This
->cs
);
211 PropertyStorage_DestroyDictionaries(This
);
212 HeapFree(GetProcessHeap(), 0, This
);
217 static PROPVARIANT
*PropertyStorage_FindProperty(PropertyStorage_impl
*This
,
220 PROPVARIANT
*ret
= NULL
;
223 dictionary_find(This
->propid_to_prop
, (void *)propid
, (void **)&ret
);
224 TRACE("returning %p\n", ret
);
228 /* Returns NULL if name is NULL. */
229 static PROPVARIANT
*PropertyStorage_FindPropertyByName(
230 PropertyStorage_impl
*This
, LPCWSTR name
)
232 PROPVARIANT
*ret
= NULL
;
238 if (dictionary_find(This
->name_to_propid
, name
, (void **)&propid
))
239 ret
= PropertyStorage_FindProperty(This
, (PROPID
)propid
);
240 TRACE("returning %p\n", ret
);
244 static LPWSTR
PropertyStorage_FindPropertyNameById(PropertyStorage_impl
*This
,
250 dictionary_find(This
->propid_to_name
, (void *)propid
, (void **)&ret
);
251 TRACE("returning %p\n", ret
);
255 /************************************************************************
256 * IPropertyStorage_fnReadMultiple (IPropertyStorage)
258 static HRESULT WINAPI
IPropertyStorage_fnReadMultiple(
259 IPropertyStorage
* iface
,
261 const PROPSPEC rgpspec
[],
262 PROPVARIANT rgpropvar
[])
264 PropertyStorage_impl
*This
= (PropertyStorage_impl
*)iface
;
265 HRESULT hr
= S_FALSE
;
268 TRACE("(%p, %ld, %p, %p)\n", iface
, cpspec
, rgpspec
, rgpropvar
);
271 if (cpspec
&& (!rgpspec
|| !rgpropvar
))
273 EnterCriticalSection(&This
->cs
);
274 for (i
= 0; i
< cpspec
; i
++)
276 PropVariantInit(&rgpropvar
[i
]);
277 if (rgpspec
[i
].ulKind
== PRSPEC_LPWSTR
)
279 PROPVARIANT
*prop
= PropertyStorage_FindPropertyByName(This
,
280 rgpspec
[i
].u
.lpwstr
);
283 PropVariantCopy(&rgpropvar
[i
], prop
);
287 PROPVARIANT
*prop
= PropertyStorage_FindProperty(This
,
288 rgpspec
[i
].u
.propid
);
291 PropVariantCopy(&rgpropvar
[i
], prop
);
294 LeaveCriticalSection(&This
->cs
);
298 static HRESULT
PropertyStorage_StorePropWithId(PropertyStorage_impl
*This
,
299 PROPID propid
, const PROPVARIANT
*propvar
)
302 PROPVARIANT
*prop
= PropertyStorage_FindProperty(This
, propid
);
306 TRACE("Setting 0x%08lx to type %d\n", propid
, propvar
->vt
);
309 PropVariantClear(prop
);
310 PropVariantCopy(prop
, propvar
);
314 prop
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
315 sizeof(PROPVARIANT
));
318 PropVariantCopy(prop
, propvar
);
319 dictionary_insert(This
->propid_to_prop
, (void *)propid
, prop
);
320 if (propid
> This
->highestProp
)
321 This
->highestProp
= propid
;
324 hr
= STG_E_INSUFFICIENTMEMORY
;
329 /************************************************************************
330 * IPropertyStorage_fnWriteMultiple (IPropertyStorage)
332 static HRESULT WINAPI
IPropertyStorage_fnWriteMultiple(
333 IPropertyStorage
* iface
,
335 const PROPSPEC rgpspec
[],
336 const PROPVARIANT rgpropvar
[],
337 PROPID propidNameFirst
)
339 PropertyStorage_impl
*This
= (PropertyStorage_impl
*)iface
;
343 TRACE("(%p, %ld, %p, %p)\n", iface
, cpspec
, rgpspec
, rgpropvar
);
346 if (cpspec
&& (!rgpspec
|| !rgpropvar
))
348 if (!(This
->grfMode
& STGM_READWRITE
))
349 return STG_E_ACCESSDENIED
;
350 EnterCriticalSection(&This
->cs
);
352 This
->originatorOS
= (DWORD
)MAKELONG(LOWORD(GetVersion()),
353 PROPSETHDR_OSVER_KIND_WIN32
) ;
354 for (i
= 0; i
< cpspec
; i
++)
356 if (rgpspec
[i
].ulKind
== PRSPEC_LPWSTR
)
358 PROPVARIANT
*prop
= PropertyStorage_FindPropertyByName(This
,
359 rgpspec
[i
].u
.lpwstr
);
362 PropVariantCopy(prop
, &rgpropvar
[i
]);
365 /* Note that I don't do the special cases here that I do below,
366 * because naming the special PIDs isn't supported.
368 if (propidNameFirst
< PID_FIRST_USABLE
||
369 propidNameFirst
>= PID_MIN_READONLY
)
370 hr
= STG_E_INVALIDPARAMETER
;
373 PROPID nextId
= max(propidNameFirst
, This
->highestProp
+ 1);
374 size_t len
= strlenW(rgpspec
[i
].u
.lpwstr
) + 1;
375 LPWSTR name
= HeapAlloc(GetProcessHeap(), 0,
376 len
* sizeof(WCHAR
));
378 strcpyW(name
, rgpspec
[i
].u
.lpwstr
);
379 TRACE("Adding prop name %s, propid %ld\n", debugstr_w(name
),
381 dictionary_insert(This
->name_to_propid
, name
,
383 dictionary_insert(This
->propid_to_name
, (void *)nextId
,
385 hr
= PropertyStorage_StorePropWithId(This
, nextId
,
392 switch (rgpspec
[i
].u
.propid
)
395 /* Can't set the dictionary */
396 hr
= STG_E_INVALIDPARAMETER
;
399 /* Can only set the code page if nothing else has been set */
400 if (dictionary_num_entries(This
->propid_to_prop
) == 0 &&
401 rgpropvar
[i
].vt
== VT_I2
)
402 This
->codePage
= rgpropvar
[i
].u
.iVal
;
404 hr
= STG_E_INVALIDPARAMETER
;
407 /* Can only set the locale if nothing else has been set */
408 if (dictionary_num_entries(This
->propid_to_prop
) == 0 &&
409 rgpropvar
[i
].vt
== VT_I4
)
410 This
->locale
= rgpropvar
[i
].u
.lVal
;
412 hr
= STG_E_INVALIDPARAMETER
;
415 /* silently ignore like MSDN says */
418 if (rgpspec
[i
].u
.propid
>= PID_MIN_READONLY
)
419 hr
= STG_E_INVALIDPARAMETER
;
421 hr
= PropertyStorage_StorePropWithId(This
,
422 rgpspec
[i
].u
.propid
, &rgpropvar
[i
]);
426 if (This
->grfFlags
& PROPSETFLAG_UNBUFFERED
)
427 IPropertyStorage_Commit(iface
, STGC_DEFAULT
);
428 LeaveCriticalSection(&This
->cs
);
432 /************************************************************************
433 * IPropertyStorage_fnDeleteMultiple (IPropertyStorage)
435 static HRESULT WINAPI
IPropertyStorage_fnDeleteMultiple(
436 IPropertyStorage
* iface
,
438 const PROPSPEC rgpspec
[])
440 PropertyStorage_impl
*This
= (PropertyStorage_impl
*)iface
;
444 TRACE("(%p, %ld, %p)\n", iface
, cpspec
, rgpspec
);
447 if (cpspec
&& !rgpspec
)
449 if (!(This
->grfMode
& STGM_READWRITE
))
450 return STG_E_ACCESSDENIED
;
452 EnterCriticalSection(&This
->cs
);
454 for (i
= 0; i
< cpspec
; i
++)
456 if (rgpspec
[i
].ulKind
== PRSPEC_LPWSTR
)
460 if (dictionary_find(This
->name_to_propid
,
461 (void *)rgpspec
[i
].u
.lpwstr
, (void **)&propid
))
462 dictionary_remove(This
->propid_to_prop
, (void *)propid
);
466 if (rgpspec
[i
].u
.propid
>= PID_FIRST_USABLE
&&
467 rgpspec
[i
].u
.propid
< PID_MIN_READONLY
)
468 dictionary_remove(This
->propid_to_prop
,
469 (void *)rgpspec
[i
].u
.propid
);
471 hr
= STG_E_INVALIDPARAMETER
;
474 if (This
->grfFlags
& PROPSETFLAG_UNBUFFERED
)
475 IPropertyStorage_Commit(iface
, STGC_DEFAULT
);
476 LeaveCriticalSection(&This
->cs
);
480 /************************************************************************
481 * IPropertyStorage_fnReadPropertyNames (IPropertyStorage)
483 static HRESULT WINAPI
IPropertyStorage_fnReadPropertyNames(
484 IPropertyStorage
* iface
,
486 const PROPID rgpropid
[],
487 LPOLESTR rglpwstrName
[])
489 PropertyStorage_impl
*This
= (PropertyStorage_impl
*)iface
;
491 HRESULT hr
= S_FALSE
;
493 TRACE("(%p, %ld, %p, %p)\n", iface
, cpropid
, rgpropid
, rglpwstrName
);
496 if (cpropid
&& (!rgpropid
|| !rglpwstrName
))
498 EnterCriticalSection(&This
->cs
);
499 for (i
= 0; i
< cpropid
&& SUCCEEDED(hr
); i
++)
501 LPWSTR name
= PropertyStorage_FindPropertyNameById(This
, rgpropid
[i
]);
505 size_t len
= lstrlenW(name
);
508 rglpwstrName
[i
] = CoTaskMemAlloc((len
+ 1) * sizeof(WCHAR
));
510 memcpy(rglpwstrName
, name
, (len
+ 1) * sizeof(WCHAR
));
512 hr
= STG_E_INSUFFICIENTMEMORY
;
515 rglpwstrName
[i
] = NULL
;
517 LeaveCriticalSection(&This
->cs
);
521 /************************************************************************
522 * IPropertyStorage_fnWritePropertyNames (IPropertyStorage)
524 static HRESULT WINAPI
IPropertyStorage_fnWritePropertyNames(
525 IPropertyStorage
* iface
,
527 const PROPID rgpropid
[],
528 const LPOLESTR rglpwstrName
[])
530 PropertyStorage_impl
*This
= (PropertyStorage_impl
*)iface
;
534 TRACE("(%p, %ld, %p, %p)\n", iface
, cpropid
, rgpropid
, rglpwstrName
);
537 if (cpropid
&& (!rgpropid
|| !rglpwstrName
))
539 if (!(This
->grfMode
& STGM_READWRITE
))
540 return STG_E_ACCESSDENIED
;
542 EnterCriticalSection(&This
->cs
);
544 for (i
= 0; i
< cpropid
; i
++)
546 if (rgpropid
[i
] != PID_ILLEGAL
)
548 size_t len
= lstrlenW(rglpwstrName
[i
]) + 1;
549 LPWSTR name
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
551 strcpyW(name
, rglpwstrName
[i
]);
552 dictionary_insert(This
->name_to_propid
, name
, (void *)rgpropid
[i
]);
553 dictionary_insert(This
->propid_to_name
, (void *)rgpropid
[i
], name
);
556 if (This
->grfFlags
& PROPSETFLAG_UNBUFFERED
)
557 IPropertyStorage_Commit(iface
, STGC_DEFAULT
);
558 LeaveCriticalSection(&This
->cs
);
562 /************************************************************************
563 * IPropertyStorage_fnDeletePropertyNames (IPropertyStorage)
565 static HRESULT WINAPI
IPropertyStorage_fnDeletePropertyNames(
566 IPropertyStorage
* iface
,
568 const PROPID rgpropid
[])
570 PropertyStorage_impl
*This
= (PropertyStorage_impl
*)iface
;
574 TRACE("(%p, %ld, %p)\n", iface
, cpropid
, rgpropid
);
577 if (cpropid
&& !rgpropid
)
579 if (!(This
->grfMode
& STGM_READWRITE
))
580 return STG_E_ACCESSDENIED
;
582 EnterCriticalSection(&This
->cs
);
584 for (i
= 0; i
< cpropid
; i
++)
588 if (dictionary_find(This
->propid_to_name
, (void *)rgpropid
[i
],
591 dictionary_remove(This
->propid_to_name
, (void *)rgpropid
[i
]);
592 dictionary_remove(This
->name_to_propid
, name
);
595 if (This
->grfFlags
& PROPSETFLAG_UNBUFFERED
)
596 IPropertyStorage_Commit(iface
, STGC_DEFAULT
);
597 LeaveCriticalSection(&This
->cs
);
601 /************************************************************************
602 * IPropertyStorage_fnCommit (IPropertyStorage)
604 static HRESULT WINAPI
IPropertyStorage_fnCommit(
605 IPropertyStorage
* iface
,
606 DWORD grfCommitFlags
)
608 PropertyStorage_impl
*This
= (PropertyStorage_impl
*)iface
;
611 TRACE("(%p, 0x%08lx)\n", iface
, grfCommitFlags
);
614 if (!(This
->grfMode
& STGM_READWRITE
))
615 return STG_E_ACCESSDENIED
;
616 EnterCriticalSection(&This
->cs
);
618 hr
= PropertyStorage_WriteToStream(This
);
621 LeaveCriticalSection(&This
->cs
);
625 /************************************************************************
626 * IPropertyStorage_fnRevert (IPropertyStorage)
628 static HRESULT WINAPI
IPropertyStorage_fnRevert(
629 IPropertyStorage
* iface
)
632 PropertyStorage_impl
*This
= (PropertyStorage_impl
*)iface
;
634 TRACE("%p\n", iface
);
638 EnterCriticalSection(&This
->cs
);
641 PropertyStorage_DestroyDictionaries(This
);
642 hr
= PropertyStorage_CreateDictionaries(This
);
644 hr
= PropertyStorage_ReadFromStream(This
);
648 LeaveCriticalSection(&This
->cs
);
652 /************************************************************************
653 * IPropertyStorage_fnEnum (IPropertyStorage)
655 static HRESULT WINAPI
IPropertyStorage_fnEnum(
656 IPropertyStorage
* iface
,
657 IEnumSTATPROPSTG
** ppenum
)
663 /************************************************************************
664 * IPropertyStorage_fnSetTimes (IPropertyStorage)
666 static HRESULT WINAPI
IPropertyStorage_fnSetTimes(
667 IPropertyStorage
* iface
,
668 const FILETIME
* pctime
,
669 const FILETIME
* patime
,
670 const FILETIME
* pmtime
)
676 /************************************************************************
677 * IPropertyStorage_fnSetClass (IPropertyStorage)
679 static HRESULT WINAPI
IPropertyStorage_fnSetClass(
680 IPropertyStorage
* iface
,
683 PropertyStorage_impl
*This
= (PropertyStorage_impl
*)iface
;
685 TRACE("%p, %s\n", iface
, debugstr_guid(clsid
));
688 if (!(This
->grfMode
& STGM_READWRITE
))
689 return STG_E_ACCESSDENIED
;
690 memcpy(&This
->clsid
, clsid
, sizeof(This
->clsid
));
692 if (This
->grfFlags
& PROPSETFLAG_UNBUFFERED
)
693 IPropertyStorage_Commit(iface
, STGC_DEFAULT
);
697 /************************************************************************
698 * IPropertyStorage_fnStat (IPropertyStorage)
700 static HRESULT WINAPI
IPropertyStorage_fnStat(
701 IPropertyStorage
* iface
,
702 STATPROPSETSTG
* statpsstg
)
704 PropertyStorage_impl
*This
= (PropertyStorage_impl
*)iface
;
708 TRACE("%p, %p\n", iface
, statpsstg
);
709 if (!This
|| !statpsstg
)
712 hr
= IStream_Stat(This
->stm
, &stat
, STATFLAG_NONAME
);
715 memcpy(&statpsstg
->fmtid
, &This
->fmtid
, sizeof(statpsstg
->fmtid
));
716 memcpy(&statpsstg
->clsid
, &This
->clsid
, sizeof(statpsstg
->clsid
));
717 statpsstg
->grfFlags
= This
->grfFlags
;
718 memcpy(&statpsstg
->mtime
, &stat
.mtime
, sizeof(statpsstg
->mtime
));
719 memcpy(&statpsstg
->ctime
, &stat
.ctime
, sizeof(statpsstg
->ctime
));
720 memcpy(&statpsstg
->atime
, &stat
.atime
, sizeof(statpsstg
->atime
));
721 statpsstg
->dwOSVersion
= This
->originatorOS
;
726 static int PropertyStorage_PropNameCompare(const void *a
, const void *b
,
729 PropertyStorage_impl
*This
= (PropertyStorage_impl
*)extra
;
731 TRACE("(%s, %s)\n", debugstr_w(a
), debugstr_w(b
));
732 /* FIXME: this assumes property names are always Unicode, but they
733 * might be ANSI, depending on whether This->grfFlags & PROPSETFLAG_ANSI
736 if (This
->grfFlags
& PROPSETFLAG_CASE_SENSITIVE
)
737 return strcmpW((LPCWSTR
)a
, (LPCWSTR
)b
);
739 return strcmpiW((LPCWSTR
)a
, (LPCWSTR
)b
);
742 static void PropertyStorage_PropNameDestroy(void *k
, void *d
, void *extra
)
744 HeapFree(GetProcessHeap(), 0, k
);
747 static int PropertyStorage_PropCompare(const void *a
, const void *b
,
750 TRACE("(%ld, %ld)\n", (PROPID
)a
, (PROPID
)b
);
751 return (PROPID
)a
- (PROPID
)b
;
754 static void PropertyStorage_PropertyDestroy(void *k
, void *d
, void *extra
)
756 PropVariantClear((PROPVARIANT
*)d
);
757 HeapFree(GetProcessHeap(), 0, d
);
760 /* Reads the dictionary from the memory buffer beginning at ptr. Interprets
761 * the entries according to the values of This->codePage and This->locale.
762 * FIXME: there isn't any checking whether the read property extends past the
764 * FIXME: this always stores dictionary entries as Unicode, but it should store
765 * them as ANSI if (This->grfFlags & PROPSETFLAG_ANSI) is true.
767 static HRESULT
PropertyStorage_ReadDictionary(PropertyStorage_impl
*This
,
774 assert(This
->name_to_propid
);
775 assert(This
->propid_to_name
);
777 StorageUtl_ReadDWord(ptr
, 0, &numEntries
);
778 TRACE("Reading %ld entries:\n", numEntries
);
779 ptr
+= sizeof(DWORD
);
780 for (i
= 0; SUCCEEDED(hr
) && i
< numEntries
; i
++)
786 StorageUtl_ReadDWord(ptr
, 0, &propid
);
787 ptr
+= sizeof(PROPID
);
788 StorageUtl_ReadDWord(ptr
, 0, &cbEntry
);
789 ptr
+= sizeof(DWORD
);
790 /* FIXME: if host is big-endian, this'll suck to convert */
791 TRACE("Reading entry with ID 0x%08lx, %ld bytes\n", propid
, cbEntry
);
792 if (This
->codePage
!= CP_UNICODE
)
794 int len
= MultiByteToWideChar(This
->codePage
, 0, ptr
, cbEntry
,
798 hr
= HRESULT_FROM_WIN32(GetLastError());
801 name
= HeapAlloc(GetProcessHeap(), 0,
802 len
* sizeof(WCHAR
));
804 MultiByteToWideChar(This
->codePage
, 0, ptr
, cbEntry
, name
,
807 hr
= STG_E_INSUFFICIENTMEMORY
;
812 name
= HeapAlloc(GetProcessHeap(), 0, cbEntry
);
814 lstrcpyW(name
, (LPWSTR
)ptr
);
816 hr
= STG_E_INSUFFICIENTMEMORY
;
817 /* Unicode entries are padded to DWORD boundaries */
818 if (cbEntry
% sizeof(DWORD
))
819 ptr
+= sizeof(DWORD
) - (cbEntry
% sizeof(DWORD
));
823 dictionary_insert(This
->name_to_propid
, name
, (void *)propid
);
824 dictionary_insert(This
->propid_to_name
, (void *)propid
, name
);
825 TRACE("Property %s maps to id %ld\n", debugstr_w(name
), propid
);
827 ptr
+= sizeof(DWORD
) + cbEntry
;
832 /* FIXME: there isn't any checking whether the read property extends past the
835 static HRESULT
PropertyStorage_ReadProperty(PROPVARIANT
*prop
, const BYTE
*data
)
841 StorageUtl_ReadDWord(data
, 0, (DWORD
*)&prop
->vt
);
842 data
+= sizeof(DWORD
);
849 prop
->u
.cVal
= *(const char *)data
;
850 TRACE("Read char 0x%x ('%c')\n", prop
->u
.cVal
, prop
->u
.cVal
);
853 prop
->u
.bVal
= *(const UCHAR
*)data
;
854 TRACE("Read byte 0x%x\n", prop
->u
.bVal
);
857 StorageUtl_ReadWord(data
, 0, &prop
->u
.iVal
);
858 TRACE("Read short %d\n", prop
->u
.iVal
);
861 StorageUtl_ReadWord(data
, 0, &prop
->u
.uiVal
);
862 TRACE("Read ushort %d\n", prop
->u
.uiVal
);
865 StorageUtl_ReadDWord(data
, 0, &prop
->u
.lVal
);
866 TRACE("Read long %ld\n", prop
->u
.lVal
);
869 StorageUtl_ReadDWord(data
, 0, &prop
->u
.ulVal
);
870 TRACE("Read ulong %ld\n", prop
->u
.ulVal
);
876 StorageUtl_ReadDWord(data
, 0, &count
);
877 prop
->u
.pszVal
= CoTaskMemAlloc(count
);
880 /* FIXME: if the host is big-endian, this'll suck */
881 memcpy(prop
->u
.pszVal
, data
+ sizeof(DWORD
), count
);
882 /* FIXME: so far so good, but this may be Unicode or DBCS depending
885 TRACE("Read string value %s\n", debugstr_a(prop
->u
.pszVal
));
888 hr
= STG_E_INSUFFICIENTMEMORY
;
895 StorageUtl_ReadDWord(data
, 0, &count
);
896 prop
->u
.pwszVal
= CoTaskMemAlloc(count
* sizeof(WCHAR
));
899 /* FIXME: if the host is big-endian, gotta swap every char */
900 memcpy(prop
->u
.pwszVal
, data
+ sizeof(DWORD
),
901 count
* sizeof(WCHAR
));
902 TRACE("Read string value %s\n", debugstr_w(prop
->u
.pwszVal
));
905 hr
= STG_E_INSUFFICIENTMEMORY
;
909 /* FIXME: endianness */
910 memcpy(&prop
->u
.filetime
, data
, sizeof(FILETIME
));
913 FIXME("unsupported type %d\n", prop
->vt
);
914 hr
= STG_E_INVALIDPARAMETER
;
919 static HRESULT
PropertyStorage_ReadHeaderFromStream(IStream
*stm
,
920 PROPERTYSETHEADER
*hdr
)
922 BYTE buf
[sizeof(PROPERTYSETHEADER
)];
928 hr
= IStream_Read(stm
, buf
, sizeof(buf
), &count
);
931 if (count
!= sizeof(buf
))
933 WARN("read %ld, expected %d\n", count
, sizeof(buf
));
934 hr
= STG_E_INVALIDHEADER
;
938 StorageUtl_ReadWord(buf
, offsetof(PROPERTYSETHEADER
, wByteOrder
),
940 StorageUtl_ReadWord(buf
, offsetof(PROPERTYSETHEADER
, wFormat
),
942 StorageUtl_ReadDWord(buf
, offsetof(PROPERTYSETHEADER
, dwOSVer
),
944 StorageUtl_ReadGUID(buf
, offsetof(PROPERTYSETHEADER
, clsid
),
946 StorageUtl_ReadDWord(buf
, offsetof(PROPERTYSETHEADER
, reserved
),
950 TRACE("returning 0x%08lx\n", hr
);
954 static HRESULT
PropertyStorage_ReadFmtIdOffsetFromStream(IStream
*stm
,
957 BYTE buf
[sizeof(FORMATIDOFFSET
)];
963 hr
= IStream_Read(stm
, buf
, sizeof(buf
), &count
);
966 if (count
!= sizeof(buf
))
968 WARN("read %ld, expected %d\n", count
, sizeof(buf
));
969 hr
= STG_E_INVALIDHEADER
;
973 StorageUtl_ReadGUID(buf
, offsetof(FORMATIDOFFSET
, fmtid
),
975 StorageUtl_ReadDWord(buf
, offsetof(FORMATIDOFFSET
, dwOffset
),
979 TRACE("returning 0x%08lx\n", hr
);
983 static HRESULT
PropertyStorage_ReadSectionHeaderFromStream(IStream
*stm
,
984 PROPERTYSECTIONHEADER
*hdr
)
986 BYTE buf
[sizeof(PROPERTYSECTIONHEADER
)];
992 hr
= IStream_Read(stm
, buf
, sizeof(buf
), &count
);
995 if (count
!= sizeof(buf
))
997 WARN("read %ld, expected %d\n", count
, sizeof(buf
));
998 hr
= STG_E_INVALIDHEADER
;
1002 StorageUtl_ReadDWord(buf
, offsetof(PROPERTYSECTIONHEADER
,
1003 cbSection
), &hdr
->cbSection
);
1004 StorageUtl_ReadDWord(buf
, offsetof(PROPERTYSECTIONHEADER
,
1005 cProperties
), &hdr
->cProperties
);
1008 TRACE("returning 0x%08lx\n", hr
);
1012 static HRESULT
PropertyStorage_ReadFromStream(PropertyStorage_impl
*This
)
1014 PROPERTYSETHEADER hdr
;
1015 FORMATIDOFFSET fmtOffset
;
1016 PROPERTYSECTIONHEADER sectionHdr
;
1023 DWORD dictOffset
= 0;
1026 This
->dirty
= FALSE
;
1027 This
->highestProp
= 0;
1028 hr
= IStream_Stat(This
->stm
, &stat
, STATFLAG_NONAME
);
1031 if (stat
.cbSize
.u
.HighPart
)
1033 WARN("stream too big\n");
1034 /* maximum size varies, but it can't be this big */
1035 hr
= STG_E_INVALIDHEADER
;
1038 if (stat
.cbSize
.u
.LowPart
== 0)
1040 /* empty stream is okay */
1044 else if (stat
.cbSize
.u
.LowPart
< sizeof(PROPERTYSETHEADER
) +
1045 sizeof(FORMATIDOFFSET
))
1047 WARN("stream too small\n");
1048 hr
= STG_E_INVALIDHEADER
;
1052 hr
= IStream_Seek(This
->stm
, seek
, STREAM_SEEK_SET
, NULL
);
1055 hr
= PropertyStorage_ReadHeaderFromStream(This
->stm
, &hdr
);
1056 /* I've only seen reserved == 1, but the article says I shouldn't disallow
1059 if (hdr
.wByteOrder
!= PROPSETHDR_BYTEORDER_MAGIC
|| hdr
.reserved
< 1)
1061 WARN("bad magic in prop set header\n");
1062 hr
= STG_E_INVALIDHEADER
;
1065 if (hdr
.wFormat
!= 0 && hdr
.wFormat
!= 1)
1067 WARN("bad format version %d\n", hdr
.wFormat
);
1068 hr
= STG_E_INVALIDHEADER
;
1071 memcpy(&This
->clsid
, &hdr
.clsid
, sizeof(This
->clsid
));
1072 This
->originatorOS
= hdr
.dwOSVer
;
1073 if (PROPSETHDR_OSVER_KIND(hdr
.dwOSVer
) == PROPSETHDR_OSVER_KIND_MAC
)
1074 WARN("File comes from a Mac, strings will probably be screwed up\n");
1075 hr
= PropertyStorage_ReadFmtIdOffsetFromStream(This
->stm
, &fmtOffset
);
1078 if (fmtOffset
.dwOffset
> stat
.cbSize
.u
.LowPart
)
1080 WARN("invalid offset %ld (stream length is %ld)\n", fmtOffset
.dwOffset
,
1081 stat
.cbSize
.u
.LowPart
);
1082 hr
= STG_E_INVALIDHEADER
;
1085 /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
1086 * follow not one, but two sections. The first is the standard properties
1087 * for the document summary information, and the second is user-defined
1088 * properties. This is the only case in which multiple sections are
1090 * Reading the second stream isn't implemented yet.
1092 hr
= PropertyStorage_ReadSectionHeaderFromStream(This
->stm
, §ionHdr
);
1095 /* The section size includes the section header, so check it */
1096 if (sectionHdr
.cbSection
< sizeof(PROPERTYSECTIONHEADER
))
1098 WARN("section header too small, got %ld, expected at least %d\n",
1099 sectionHdr
.cbSection
, sizeof(PROPERTYSECTIONHEADER
));
1100 hr
= STG_E_INVALIDHEADER
;
1103 buf
= HeapAlloc(GetProcessHeap(), 0, sectionHdr
.cbSection
-
1104 sizeof(PROPERTYSECTIONHEADER
));
1107 hr
= STG_E_INSUFFICIENTMEMORY
;
1110 hr
= IStream_Read(This
->stm
, buf
, sectionHdr
.cbSection
-
1111 sizeof(PROPERTYSECTIONHEADER
), &count
);
1114 TRACE("Reading %ld properties:\n", sectionHdr
.cProperties
);
1115 for (i
= 0; SUCCEEDED(hr
) && i
< sectionHdr
.cProperties
; i
++)
1117 PROPERTYIDOFFSET
*idOffset
= (PROPERTYIDOFFSET
*)(buf
+
1118 i
* sizeof(PROPERTYIDOFFSET
));
1120 if (idOffset
->dwOffset
< sizeof(PROPERTYSECTIONHEADER
) ||
1121 idOffset
->dwOffset
>= sectionHdr
.cbSection
- sizeof(DWORD
))
1122 hr
= STG_E_INVALIDPOINTER
;
1125 if (idOffset
->propid
>= PID_FIRST_USABLE
&&
1126 idOffset
->propid
< PID_MIN_READONLY
&& idOffset
->propid
>
1128 This
->highestProp
= idOffset
->propid
;
1129 if (idOffset
->propid
== PID_DICTIONARY
)
1131 /* Don't read the dictionary yet, its entries depend on the
1132 * code page. Just store the offset so we know to read it
1135 dictOffset
= idOffset
->dwOffset
;
1136 TRACE("Dictionary offset is %ld\n", dictOffset
);
1142 if (SUCCEEDED(PropertyStorage_ReadProperty(&prop
,
1143 buf
+ idOffset
->dwOffset
- sizeof(PROPERTYSECTIONHEADER
))))
1145 TRACE("Read property with ID 0x%08lx, type %d\n",
1146 idOffset
->propid
, prop
.vt
);
1147 switch(idOffset
->propid
)
1150 if (prop
.vt
== VT_I2
)
1151 This
->codePage
= (UINT
)prop
.u
.iVal
;
1154 if (prop
.vt
== VT_I4
)
1155 This
->locale
= (LCID
)prop
.u
.lVal
;
1158 if (prop
.vt
== VT_I4
&& prop
.u
.lVal
)
1159 This
->grfFlags
|= PROPSETFLAG_CASE_SENSITIVE
;
1162 hr
= PropertyStorage_StorePropWithId(This
,
1163 idOffset
->propid
, &prop
);
1169 if (!This
->codePage
)
1171 /* default to Unicode unless told not to, as specified here:
1172 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
1174 if (This
->grfFlags
& PROPSETFLAG_ANSI
)
1175 This
->codePage
= GetACP();
1177 This
->codePage
= CP_UNICODE
;
1180 This
->locale
= LOCALE_SYSTEM_DEFAULT
;
1181 TRACE("Code page is %d, locale is %ld\n", This
->codePage
, This
->locale
);
1183 hr
= PropertyStorage_ReadDictionary(This
,
1184 buf
+ dictOffset
- sizeof(PROPERTYSECTIONHEADER
));
1187 HeapFree(GetProcessHeap(), 0, buf
);
1190 dictionary_destroy(This
->name_to_propid
);
1191 This
->name_to_propid
= NULL
;
1192 dictionary_destroy(This
->propid_to_name
);
1193 This
->propid_to_name
= NULL
;
1194 dictionary_destroy(This
->propid_to_prop
);
1195 This
->propid_to_prop
= NULL
;
1200 static void PropertyStorage_MakeHeader(PropertyStorage_impl
*This
,
1201 PROPERTYSETHEADER
*hdr
)
1205 StorageUtl_WriteWord((BYTE
*)&hdr
->wByteOrder
, 0,
1206 PROPSETHDR_BYTEORDER_MAGIC
);
1207 /* FIXME: should be able to write format 0 property sets too, depending
1208 * on whether I have too long string names or if case-sensitivity is set.
1209 * For now always write format 1.
1211 StorageUtl_WriteWord((BYTE
*)&hdr
->wFormat
, 0, 1);
1212 StorageUtl_WriteDWord((BYTE
*)&hdr
->dwOSVer
, 0, This
->originatorOS
);
1213 StorageUtl_WriteGUID((BYTE
*)&hdr
->clsid
, 0, &This
->clsid
);
1214 StorageUtl_WriteDWord((BYTE
*)&hdr
->reserved
, 0, 1);
1217 static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl
*This
,
1218 FORMATIDOFFSET
*fmtOffset
)
1222 StorageUtl_WriteGUID((BYTE
*)fmtOffset
, 0, &This
->fmtid
);
1223 StorageUtl_WriteDWord((BYTE
*)fmtOffset
, offsetof(FORMATIDOFFSET
, dwOffset
),
1224 sizeof(PROPERTYSETHEADER
) + sizeof(FORMATIDOFFSET
));
1227 static void PropertyStorage_MakeSectionHdr(DWORD cbSection
, DWORD numProps
,
1228 PROPERTYSECTIONHEADER
*hdr
)
1231 StorageUtl_WriteDWord((BYTE
*)hdr
, 0, cbSection
);
1232 StorageUtl_WriteDWord((BYTE
*)hdr
,
1233 offsetof(PROPERTYSECTIONHEADER
, cProperties
), numProps
);
1236 static void PropertyStorage_MakePropertyIdOffset(DWORD propid
, DWORD dwOffset
,
1237 PROPERTYIDOFFSET
*propIdOffset
)
1239 assert(propIdOffset
);
1240 StorageUtl_WriteDWord((BYTE
*)propIdOffset
, 0, propid
);
1241 StorageUtl_WriteDWord((BYTE
*)propIdOffset
,
1242 offsetof(PROPERTYIDOFFSET
, dwOffset
), dwOffset
);
1245 struct DictionaryClosure
1251 static BOOL
PropertyStorage_DictionaryWriter(const void *key
,
1252 const void *value
, void *extra
, void *closure
)
1254 PropertyStorage_impl
*This
= (PropertyStorage_impl
*)extra
;
1255 struct DictionaryClosure
*c
= (struct DictionaryClosure
*)closure
;
1262 StorageUtl_WriteDWord((LPBYTE
)&propid
, 0, (DWORD
)value
);
1263 c
->hr
= IStream_Write(This
->stm
, &propid
, sizeof(propid
), &count
);
1266 c
->bytesWritten
+= sizeof(DWORD
);
1267 if (This
->codePage
== CP_UNICODE
)
1269 DWORD keyLen
, pad
= 0;
1271 StorageUtl_WriteDWord((LPBYTE
)&keyLen
, 0,
1272 (lstrlenW((LPWSTR
)key
) + 1) * sizeof(WCHAR
));
1273 c
->hr
= IStream_Write(This
->stm
, &keyLen
, sizeof(keyLen
), &count
);
1276 c
->bytesWritten
+= sizeof(DWORD
);
1277 /* FIXME: endian-convert every char (yuck) */
1278 c
->hr
= IStream_Write(This
->stm
, key
, keyLen
, &count
);
1281 c
->bytesWritten
+= keyLen
;
1282 if (keyLen
% sizeof(DWORD
))
1284 c
->hr
= IStream_Write(This
->stm
, &pad
,
1285 sizeof(DWORD
) - keyLen
% sizeof(DWORD
), &count
);
1288 c
->bytesWritten
+= sizeof(DWORD
) - keyLen
% sizeof(DWORD
);
1293 int len
= WideCharToMultiByte(This
->codePage
, 0, (LPWSTR
)key
, -1, NULL
,
1295 LPBYTE buf
= HeapAlloc(GetProcessHeap(), 0, len
);
1300 c
->hr
= STG_E_INSUFFICIENTMEMORY
;
1303 /* FIXME: endian-convert multibyte chars? Ick! */
1304 WideCharToMultiByte(This
->codePage
, 0, (LPWSTR
)key
, -1, buf
, len
,
1306 StorageUtl_WriteDWord((LPBYTE
)&dwLen
, 0, len
);
1307 c
->hr
= IStream_Write(This
->stm
, &dwLen
, sizeof(dwLen
), &count
);
1310 HeapFree(GetProcessHeap(), 0, buf
);
1313 c
->bytesWritten
+= sizeof(DWORD
);
1314 c
->hr
= IStream_Write(This
->stm
, buf
, len
, &count
);
1317 HeapFree(GetProcessHeap(), 0, buf
);
1320 c
->bytesWritten
+= len
;
1321 HeapFree(GetProcessHeap(), 0, buf
);
1324 return SUCCEEDED(c
->hr
);
1327 #define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)
1329 /* Writes the dictionary to the stream. Assumes without checking that the
1330 * dictionary isn't empty.
1332 static HRESULT
PropertyStorage_WriteDictionaryToStream(
1333 PropertyStorage_impl
*This
, DWORD
*sectionOffset
)
1337 PROPERTYIDOFFSET propIdOffset
;
1340 struct DictionaryClosure closure
;
1343 assert(sectionOffset
);
1345 /* The dictionary's always the first property written, so seek to its
1348 seek
.QuadPart
= SECTIONHEADER_OFFSET
+ sizeof(PROPERTYSECTIONHEADER
);
1349 hr
= IStream_Seek(This
->stm
, seek
, STREAM_SEEK_SET
, NULL
);
1352 PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY
, *sectionOffset
,
1354 hr
= IStream_Write(This
->stm
, &propIdOffset
, sizeof(propIdOffset
), &count
);
1358 seek
.QuadPart
= SECTIONHEADER_OFFSET
+ *sectionOffset
;
1359 hr
= IStream_Seek(This
->stm
, seek
, STREAM_SEEK_SET
, NULL
);
1362 StorageUtl_WriteDWord((LPBYTE
)&dwTemp
, 0,
1363 dictionary_num_entries(This
->name_to_propid
));
1364 hr
= IStream_Write(This
->stm
, &dwTemp
, sizeof(dwTemp
), &count
);
1367 *sectionOffset
+= sizeof(dwTemp
);
1370 closure
.bytesWritten
= 0;
1371 dictionary_enumerate(This
->name_to_propid
, PropertyStorage_DictionaryWriter
,
1376 *sectionOffset
+= closure
.bytesWritten
;
1377 if (closure
.bytesWritten
% sizeof(DWORD
))
1379 TRACE("adding %ld bytes of padding\n", sizeof(DWORD
) -
1380 closure
.bytesWritten
% sizeof(DWORD
));
1381 *sectionOffset
+= sizeof(DWORD
) - closure
.bytesWritten
% sizeof(DWORD
);
1388 static HRESULT
PropertyStorage_WritePropertyToStream(PropertyStorage_impl
*This
,
1389 DWORD propNum
, DWORD propid
, PROPVARIANT
*var
, DWORD
*sectionOffset
)
1393 PROPERTYIDOFFSET propIdOffset
;
1395 DWORD dwType
, bytesWritten
;
1399 assert(sectionOffset
);
1401 TRACE("%p, %ld, 0x%08lx, (%d), (%ld)\n", This
, propNum
, propid
, var
->vt
,
1404 seek
.QuadPart
= SECTIONHEADER_OFFSET
+ sizeof(PROPERTYSECTIONHEADER
) +
1405 propNum
* sizeof(PROPERTYIDOFFSET
);
1406 hr
= IStream_Seek(This
->stm
, seek
, STREAM_SEEK_SET
, NULL
);
1409 PropertyStorage_MakePropertyIdOffset(propid
, *sectionOffset
, &propIdOffset
);
1410 hr
= IStream_Write(This
->stm
, &propIdOffset
, sizeof(propIdOffset
), &count
);
1414 seek
.QuadPart
= SECTIONHEADER_OFFSET
+ *sectionOffset
;
1415 hr
= IStream_Seek(This
->stm
, seek
, STREAM_SEEK_SET
, NULL
);
1418 StorageUtl_WriteDWord((LPBYTE
)&dwType
, 0, var
->vt
);
1419 hr
= IStream_Write(This
->stm
, &dwType
, sizeof(dwType
), &count
);
1422 *sectionOffset
+= sizeof(dwType
);
1432 hr
= IStream_Write(This
->stm
, &var
->u
.cVal
, sizeof(var
->u
.cVal
),
1434 bytesWritten
= count
;
1441 StorageUtl_WriteWord((LPBYTE
)&wTemp
, 0, var
->u
.iVal
);
1442 hr
= IStream_Write(This
->stm
, &wTemp
, sizeof(wTemp
), &count
);
1443 bytesWritten
= count
;
1451 StorageUtl_WriteDWord((LPBYTE
)&dwTemp
, 0, var
->u
.lVal
);
1452 hr
= IStream_Write(This
->stm
, &dwTemp
, sizeof(dwTemp
), &count
);
1453 bytesWritten
= count
;
1460 if (This
->codePage
== CP_UNICODE
)
1461 len
= (lstrlenW(var
->u
.pwszVal
) + 1) * sizeof(WCHAR
);
1463 len
= lstrlenA(var
->u
.pszVal
) + 1;
1464 StorageUtl_WriteDWord((LPBYTE
)&dwTemp
, 0, len
);
1465 hr
= IStream_Write(This
->stm
, &dwTemp
, sizeof(dwTemp
), &count
);
1468 hr
= IStream_Write(This
->stm
, var
->u
.pszVal
, len
, &count
);
1469 bytesWritten
= count
+ sizeof(DWORD
);
1474 DWORD len
= lstrlenW(var
->u
.pwszVal
) + 1, dwTemp
;
1476 StorageUtl_WriteDWord((LPBYTE
)&dwTemp
, 0, len
);
1477 hr
= IStream_Write(This
->stm
, &dwTemp
, sizeof(dwTemp
), &count
);
1480 hr
= IStream_Write(This
->stm
, var
->u
.pwszVal
, len
* sizeof(WCHAR
),
1482 bytesWritten
= count
+ sizeof(DWORD
);
1486 FIXME("unsupported type: %d\n", var
->vt
);
1487 return STG_E_INVALIDPARAMETER
;
1492 *sectionOffset
+= bytesWritten
;
1493 if (bytesWritten
% sizeof(DWORD
))
1495 TRACE("adding %ld bytes of padding\n", sizeof(DWORD
) -
1496 bytesWritten
% sizeof(DWORD
));
1497 *sectionOffset
+= sizeof(DWORD
) - bytesWritten
% sizeof(DWORD
);
1505 struct PropertyClosure
1509 DWORD
*sectionOffset
;
1512 static BOOL
PropertyStorage_PropertiesWriter(const void *key
, const void *value
,
1513 void *extra
, void *closure
)
1515 PropertyStorage_impl
*This
= (PropertyStorage_impl
*)extra
;
1516 struct PropertyClosure
*c
= (struct PropertyClosure
*)closure
;
1522 c
->hr
= PropertyStorage_WritePropertyToStream(This
,
1523 c
->propNum
++, (DWORD
)key
, (PROPVARIANT
*)value
, c
->sectionOffset
);
1524 return SUCCEEDED(c
->hr
);
1527 static HRESULT
PropertyStorage_WritePropertiesToStream(
1528 PropertyStorage_impl
*This
, DWORD startingPropNum
, DWORD
*sectionOffset
)
1530 struct PropertyClosure closure
;
1533 assert(sectionOffset
);
1535 closure
.propNum
= startingPropNum
;
1536 closure
.sectionOffset
= sectionOffset
;
1537 dictionary_enumerate(This
->propid_to_prop
, PropertyStorage_PropertiesWriter
,
1542 static HRESULT
PropertyStorage_WriteHeadersToStream(PropertyStorage_impl
*This
)
1546 LARGE_INTEGER seek
= { {0} };
1547 PROPERTYSETHEADER hdr
;
1548 FORMATIDOFFSET fmtOffset
;
1551 hr
= IStream_Seek(This
->stm
, seek
, STREAM_SEEK_SET
, NULL
);
1554 PropertyStorage_MakeHeader(This
, &hdr
);
1555 hr
= IStream_Write(This
->stm
, &hdr
, sizeof(hdr
), &count
);
1558 if (count
!= sizeof(hdr
))
1560 hr
= STG_E_WRITEFAULT
;
1564 PropertyStorage_MakeFmtIdOffset(This
, &fmtOffset
);
1565 hr
= IStream_Write(This
->stm
, &fmtOffset
, sizeof(fmtOffset
), &count
);
1568 if (count
!= sizeof(fmtOffset
))
1570 hr
= STG_E_WRITEFAULT
;
1579 static HRESULT
PropertyStorage_WriteToStream(PropertyStorage_impl
*This
)
1581 PROPERTYSECTIONHEADER sectionHdr
;
1585 DWORD numProps
, prop
, sectionOffset
, dwTemp
;
1590 PropertyStorage_WriteHeadersToStream(This
);
1592 /* Count properties. Always at least one property, the code page */
1594 if (dictionary_num_entries(This
->name_to_propid
))
1596 if (This
->locale
!= LOCALE_SYSTEM_DEFAULT
)
1598 if (This
->grfFlags
& PROPSETFLAG_CASE_SENSITIVE
)
1600 numProps
+= dictionary_num_entries(This
->propid_to_prop
);
1602 /* Write section header with 0 bytes right now, I'll adjust it after
1603 * writing properties.
1605 PropertyStorage_MakeSectionHdr(0, numProps
, §ionHdr
);
1606 seek
.QuadPart
= SECTIONHEADER_OFFSET
;
1607 hr
= IStream_Seek(This
->stm
, seek
, STREAM_SEEK_SET
, NULL
);
1610 hr
= IStream_Write(This
->stm
, §ionHdr
, sizeof(sectionHdr
), &count
);
1615 sectionOffset
= sizeof(PROPERTYSECTIONHEADER
) +
1616 numProps
* sizeof(PROPERTYIDOFFSET
);
1618 if (dictionary_num_entries(This
->name_to_propid
))
1621 hr
= PropertyStorage_WriteDictionaryToStream(This
, §ionOffset
);
1626 PropVariantInit(&var
);
1629 var
.u
.iVal
= This
->codePage
;
1630 hr
= PropertyStorage_WritePropertyToStream(This
, prop
++, PID_CODEPAGE
,
1631 &var
, §ionOffset
);
1635 if (This
->locale
!= LOCALE_SYSTEM_DEFAULT
)
1638 var
.u
.lVal
= This
->locale
;
1639 hr
= PropertyStorage_WritePropertyToStream(This
, prop
++, PID_LOCALE
,
1640 &var
, §ionOffset
);
1645 if (This
->grfFlags
& PROPSETFLAG_CASE_SENSITIVE
)
1649 hr
= PropertyStorage_WritePropertyToStream(This
, prop
++, PID_BEHAVIOR
,
1650 &var
, §ionOffset
);
1655 hr
= PropertyStorage_WritePropertiesToStream(This
, prop
, §ionOffset
);
1659 /* Now write the byte count of the section */
1660 seek
.QuadPart
= SECTIONHEADER_OFFSET
;
1661 hr
= IStream_Seek(This
->stm
, seek
, STREAM_SEEK_SET
, NULL
);
1664 StorageUtl_WriteDWord((LPBYTE
)&dwTemp
, 0, sectionOffset
);
1665 hr
= IStream_Write(This
->stm
, &dwTemp
, sizeof(dwTemp
), &count
);
1671 /***********************************************************************
1672 * PropertyStorage_Construct
1674 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl
*This
)
1677 dictionary_destroy(This
->name_to_propid
);
1678 This
->name_to_propid
= NULL
;
1679 dictionary_destroy(This
->propid_to_name
);
1680 This
->propid_to_name
= NULL
;
1681 dictionary_destroy(This
->propid_to_prop
);
1682 This
->propid_to_prop
= NULL
;
1685 static HRESULT
PropertyStorage_CreateDictionaries(PropertyStorage_impl
*This
)
1690 This
->name_to_propid
= dictionary_create(
1691 PropertyStorage_PropNameCompare
, PropertyStorage_PropNameDestroy
,
1693 if (!This
->name_to_propid
)
1695 hr
= STG_E_INSUFFICIENTMEMORY
;
1698 This
->propid_to_name
= dictionary_create(PropertyStorage_PropCompare
,
1700 if (!This
->propid_to_name
)
1702 hr
= STG_E_INSUFFICIENTMEMORY
;
1705 This
->propid_to_prop
= dictionary_create(PropertyStorage_PropCompare
,
1706 PropertyStorage_PropertyDestroy
, This
);
1707 if (!This
->propid_to_prop
)
1709 hr
= STG_E_INSUFFICIENTMEMORY
;
1714 PropertyStorage_DestroyDictionaries(This
);
1718 static HRESULT
PropertyStorage_BaseConstruct(IStream
*stm
,
1719 REFFMTID rfmtid
, DWORD grfMode
, PropertyStorage_impl
**pps
)
1725 *pps
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof **pps
);
1727 return E_OUTOFMEMORY
;
1729 (*pps
)->vtbl
= &IPropertyStorage_Vtbl
;
1731 InitializeCriticalSection(&(*pps
)->cs
);
1733 memcpy(&(*pps
)->fmtid
, rfmtid
, sizeof((*pps
)->fmtid
));
1734 (*pps
)->grfMode
= grfMode
;
1736 hr
= PropertyStorage_CreateDictionaries(*pps
);
1741 static HRESULT
PropertyStorage_ConstructFromStream(IStream
*stm
,
1742 REFFMTID rfmtid
, DWORD grfMode
, IPropertyStorage
** pps
)
1744 PropertyStorage_impl
*ps
;
1748 hr
= PropertyStorage_BaseConstruct(stm
, rfmtid
, grfMode
, &ps
);
1751 hr
= PropertyStorage_ReadFromStream(ps
);
1754 *pps
= (IPropertyStorage
*)ps
;
1755 TRACE("PropertyStorage %p constructed\n", ps
);
1760 PropertyStorage_DestroyDictionaries(ps
);
1761 HeapFree(GetProcessHeap(), 0, ps
);
1767 static HRESULT
PropertyStorage_ConstructEmpty(IStream
*stm
,
1768 REFFMTID rfmtid
, DWORD grfFlags
, DWORD grfMode
, IPropertyStorage
** pps
)
1770 PropertyStorage_impl
*ps
;
1774 hr
= PropertyStorage_BaseConstruct(stm
, rfmtid
, grfMode
, &ps
);
1777 ps
->grfFlags
= grfFlags
;
1778 /* default to Unicode unless told not to, as specified here:
1779 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
1781 if (ps
->grfFlags
& PROPSETFLAG_ANSI
)
1782 ps
->codePage
= GetACP();
1784 ps
->codePage
= CP_UNICODE
;
1785 ps
->locale
= LOCALE_SYSTEM_DEFAULT
;
1786 TRACE("Code page is %d, locale is %ld\n", ps
->codePage
, ps
->locale
);
1787 *pps
= (IPropertyStorage
*)ps
;
1788 TRACE("PropertyStorage %p constructed\n", ps
);
1795 /***********************************************************************
1796 * Implementation of IPropertySetStorage
1799 /************************************************************************
1800 * IPropertySetStorage_fnQueryInterface (IUnknown)
1802 * This method forwards to the common QueryInterface implementation
1804 static HRESULT WINAPI
IPropertySetStorage_fnQueryInterface(
1805 IPropertySetStorage
*ppstg
,
1809 _ICOM_THIS_From_IPropertySetStorage(StorageImpl
, ppstg
);
1810 return IStorage_QueryInterface( (IStorage
*)This
, riid
, ppvObject
);
1813 /************************************************************************
1814 * IPropertySetStorage_fnAddRef (IUnknown)
1816 * This method forwards to the common AddRef implementation
1818 static ULONG WINAPI
IPropertySetStorage_fnAddRef(
1819 IPropertySetStorage
*ppstg
)
1821 _ICOM_THIS_From_IPropertySetStorage(StorageImpl
, ppstg
);
1822 return IStorage_AddRef( (IStorage
*)This
);
1825 /************************************************************************
1826 * IPropertySetStorage_fnRelease (IUnknown)
1828 * This method forwards to the common Release implementation
1830 static ULONG WINAPI
IPropertySetStorage_fnRelease(
1831 IPropertySetStorage
*ppstg
)
1833 _ICOM_THIS_From_IPropertySetStorage(StorageImpl
, ppstg
);
1834 return IStorage_Release( (IStorage
*)This
);
1837 /************************************************************************
1838 * IPropertySetStorage_fnCreate (IPropertySetStorage)
1840 static HRESULT WINAPI
IPropertySetStorage_fnCreate(
1841 IPropertySetStorage
*ppstg
,
1843 const CLSID
* pclsid
,
1846 IPropertyStorage
** ppprstg
)
1848 _ICOM_THIS_From_IPropertySetStorage(StorageImpl
, ppstg
);
1849 WCHAR name
[CCH_MAX_PROPSTG_NAME
];
1850 IStream
*stm
= NULL
;
1853 TRACE("%p %s %08lx %08lx %p\n", This
, debugstr_guid(rfmtid
), grfFlags
,
1857 if (grfMode
!= (STGM_CREATE
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
))
1859 r
= STG_E_INVALIDFLAG
;
1869 /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
1870 * storage, not a stream. For now, disallow it.
1872 if (grfFlags
& PROPSETFLAG_NONSIMPLE
)
1874 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
1875 r
= STG_E_INVALIDFLAG
;
1879 r
= FmtIdToPropStgName(rfmtid
, name
);
1883 r
= IStorage_CreateStream( (IStorage
*)This
, name
, grfMode
, 0, 0, &stm
);
1887 r
= PropertyStorage_ConstructEmpty(stm
, rfmtid
, grfFlags
, grfMode
, ppprstg
);
1890 TRACE("returning 0x%08lx\n", r
);
1894 /************************************************************************
1895 * IPropertySetStorage_fnOpen (IPropertySetStorage)
1897 static HRESULT WINAPI
IPropertySetStorage_fnOpen(
1898 IPropertySetStorage
*ppstg
,
1901 IPropertyStorage
** ppprstg
)
1903 _ICOM_THIS_From_IPropertySetStorage(StorageImpl
, ppstg
);
1904 IStream
*stm
= NULL
;
1905 WCHAR name
[CCH_MAX_PROPSTG_NAME
];
1908 TRACE("%p %s %08lx %p\n", This
, debugstr_guid(rfmtid
), grfMode
, ppprstg
);
1911 if (grfMode
!= (STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
) &&
1912 grfMode
!= (STGM_READ
|STGM_SHARE_EXCLUSIVE
))
1914 r
= STG_E_INVALIDFLAG
;
1924 r
= FmtIdToPropStgName(rfmtid
, name
);
1928 r
= IStorage_OpenStream((IStorage
*) This
, name
, 0, grfMode
, 0, &stm
);
1932 r
= PropertyStorage_ConstructFromStream(stm
, rfmtid
, grfMode
, ppprstg
);
1935 TRACE("returning 0x%08lx\n", r
);
1939 /************************************************************************
1940 * IPropertySetStorage_fnDelete (IPropertySetStorage)
1942 static HRESULT WINAPI
IPropertySetStorage_fnDelete(
1943 IPropertySetStorage
*ppstg
,
1946 _ICOM_THIS_From_IPropertySetStorage(StorageImpl
, ppstg
);
1947 IStorage
*stg
= NULL
;
1948 WCHAR name
[CCH_MAX_PROPSTG_NAME
];
1951 TRACE("%p %s\n", This
, debugstr_guid(rfmtid
));
1954 return E_INVALIDARG
;
1956 r
= FmtIdToPropStgName(rfmtid
, name
);
1960 stg
= (IStorage
*) This
;
1961 return IStorage_DestroyElement(stg
, name
);
1964 /************************************************************************
1965 * IPropertySetStorage_fnEnum (IPropertySetStorage)
1967 static HRESULT WINAPI
IPropertySetStorage_fnEnum(
1968 IPropertySetStorage
*ppstg
,
1969 IEnumSTATPROPSETSTG
** ppenum
)
1971 _ICOM_THIS_From_IPropertySetStorage(StorageImpl
, ppstg
);
1972 FIXME("%p\n", This
);
1977 /***********************************************************************
1980 IPropertySetStorageVtbl IPropertySetStorage_Vtbl
=
1982 IPropertySetStorage_fnQueryInterface
,
1983 IPropertySetStorage_fnAddRef
,
1984 IPropertySetStorage_fnRelease
,
1985 IPropertySetStorage_fnCreate
,
1986 IPropertySetStorage_fnOpen
,
1987 IPropertySetStorage_fnDelete
,
1988 IPropertySetStorage_fnEnum
1991 static IPropertyStorageVtbl IPropertyStorage_Vtbl
=
1993 IPropertyStorage_fnQueryInterface
,
1994 IPropertyStorage_fnAddRef
,
1995 IPropertyStorage_fnRelease
,
1996 IPropertyStorage_fnReadMultiple
,
1997 IPropertyStorage_fnWriteMultiple
,
1998 IPropertyStorage_fnDeleteMultiple
,
1999 IPropertyStorage_fnReadPropertyNames
,
2000 IPropertyStorage_fnWritePropertyNames
,
2001 IPropertyStorage_fnDeletePropertyNames
,
2002 IPropertyStorage_fnCommit
,
2003 IPropertyStorage_fnRevert
,
2004 IPropertyStorage_fnEnum
,
2005 IPropertyStorage_fnSetTimes
,
2006 IPropertyStorage_fnSetClass
,
2007 IPropertyStorage_fnStat
,
2010 /***********************************************************************
2011 * Format ID <-> name conversion
2013 static const WCHAR szSummaryInfo
[] = { 5,'S','u','m','m','a','r','y',
2014 'I','n','f','o','r','m','a','t','i','o','n',0 };
2015 static const WCHAR szDocSummaryInfo
[] = { 5,'D','o','c','u','m','e','n','t',
2016 'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
2018 #define BITS_PER_BYTE 8
2019 #define CHARMASK 0x1f
2020 #define BITS_IN_CHARMASK 5
2021 #define NUM_ALPHA_CHARS 26
2023 /***********************************************************************
2024 * FmtIdToPropStgName [ole32.@]
2025 * Returns the storage name of the format ID rfmtid.
2027 * rfmtid [I] Format ID for which to return a storage name
2028 * str [O] Storage name associated with rfmtid.
2031 * E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise.
2034 * str must be at least CCH_MAX_PROPSTG_NAME characters in length.
2035 * Based on the algorithm described here:
2036 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2038 HRESULT WINAPI
FmtIdToPropStgName(const FMTID
*rfmtid
, LPOLESTR str
)
2040 static const char fmtMap
[] = "abcdefghijklmnopqrstuvwxyz012345";
2042 TRACE("%s, %p\n", debugstr_guid(rfmtid
), str
);
2044 if (!rfmtid
) return E_INVALIDARG
;
2045 if (!str
) return E_INVALIDARG
;
2047 if (IsEqualGUID(&FMTID_SummaryInformation
, rfmtid
))
2048 lstrcpyW(str
, szSummaryInfo
);
2049 else if (IsEqualGUID(&FMTID_DocSummaryInformation
, rfmtid
))
2050 lstrcpyW(str
, szDocSummaryInfo
);
2051 else if (IsEqualGUID(&FMTID_UserDefinedProperties
, rfmtid
))
2052 lstrcpyW(str
, szDocSummaryInfo
);
2057 ULONG bitsRemaining
= BITS_PER_BYTE
;
2060 for (fmtptr
= (BYTE
*)rfmtid
; fmtptr
< (BYTE
*)rfmtid
+ sizeof(FMTID
); )
2062 ULONG i
= *fmtptr
>> (BITS_PER_BYTE
- bitsRemaining
);
2064 if (bitsRemaining
>= BITS_IN_CHARMASK
)
2066 *pstr
= (WCHAR
)(fmtMap
[i
& CHARMASK
]);
2067 if (bitsRemaining
== BITS_PER_BYTE
&& *pstr
>= 'a' &&
2071 bitsRemaining
-= BITS_IN_CHARMASK
;
2072 if (bitsRemaining
== 0)
2075 bitsRemaining
= BITS_PER_BYTE
;
2080 if (++fmtptr
< (BYTE
*)rfmtid
+ sizeof(FMTID
))
2081 i
|= *fmtptr
<< bitsRemaining
;
2082 *pstr
++ = (WCHAR
)(fmtMap
[i
& CHARMASK
]);
2083 bitsRemaining
+= BITS_PER_BYTE
- BITS_IN_CHARMASK
;
2088 TRACE("returning %s\n", debugstr_w(str
));
2092 /***********************************************************************
2093 * PropStgNameToFmtId [ole32.@]
2094 * Returns the format ID corresponding to the given name.
2096 * str [I] Storage name to convert to a format ID.
2097 * rfmtid [O] Format ID corresponding to str.
2100 * E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to
2101 * a format ID, S_OK otherwise.
2104 * Based on the algorithm described here:
2105 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2107 HRESULT WINAPI
PropStgNameToFmtId(const LPOLESTR str
, FMTID
*rfmtid
)
2109 HRESULT hr
= E_INVALIDARG
;
2111 TRACE("%s, %p\n", debugstr_w(str
), rfmtid
);
2113 if (!rfmtid
) return E_INVALIDARG
;
2114 if (!str
) return E_INVALIDARG
;
2116 if (!lstrcmpiW(str
, szDocSummaryInfo
))
2118 memcpy(rfmtid
, &FMTID_DocSummaryInformation
, sizeof(*rfmtid
));
2121 else if (!lstrcmpiW(str
, szSummaryInfo
))
2123 memcpy(rfmtid
, &FMTID_SummaryInformation
, sizeof(*rfmtid
));
2129 BYTE
*fmtptr
= (BYTE
*)rfmtid
- 1;
2130 const WCHAR
*pstr
= str
;
2132 memset(rfmtid
, 0, sizeof(*rfmtid
));
2133 for (bits
= 0; bits
< sizeof(FMTID
) * BITS_PER_BYTE
;
2134 bits
+= BITS_IN_CHARMASK
)
2136 ULONG bitsUsed
= bits
% BITS_PER_BYTE
, bitsStored
;
2142 if (wc
> NUM_ALPHA_CHARS
)
2145 if (wc
> NUM_ALPHA_CHARS
)
2147 wc
+= 'a' - '0' + NUM_ALPHA_CHARS
;
2150 WARN("invalid character (%d)\n", *pstr
);
2155 *fmtptr
|= wc
<< bitsUsed
;
2156 bitsStored
= min(BITS_PER_BYTE
- bitsUsed
, BITS_IN_CHARMASK
);
2157 if (bitsStored
< BITS_IN_CHARMASK
)
2159 wc
>>= BITS_PER_BYTE
- bitsUsed
;
2160 if (bits
+ bitsStored
== sizeof(FMTID
) * BITS_PER_BYTE
)
2164 WARN("extra bits\n");
2170 *fmtptr
|= (BYTE
)wc
;