wined3d: Fix minor typo from shader patch.
[wine.git] / dlls / ole32 / stg_prop.c
blob8db5dabdf91deaef7987a11998f42c2cd75556b3
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
12 * Copyright 2005 Juan Lang
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 * There's a decent overview of property set storage here:
29 * http://msdn.microsoft.com/archive/en-us/dnarolegen/html/msdn_propset.asp
30 * It's a little bit out of date, and more definitive references are given
31 * below, but it gives the best "big picture" that I've found.
33 * TODO:
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
44 #include <assert.h>
45 #include <stdarg.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
50 #define COBJMACROS
51 #define NONAMELESSUNION
52 #define NONAMELESSSTRUCT
54 #include "windef.h"
55 #include "winbase.h"
56 #include "winnls.h"
57 #include "winuser.h"
58 #include "wine/unicode.h"
59 #include "wine/debug.h"
60 #include "dictionary.h"
61 #include "storage32.h"
62 #include "enumx.h"
64 WINE_DEFAULT_DEBUG_CHANNEL(storage);
66 static inline StorageImpl *impl_from_IPropertySetStorage( IPropertySetStorage *iface )
68 return (StorageImpl *)((char*)iface - FIELD_OFFSET(StorageImpl, base.pssVtbl));
71 /* These are documented in MSDN, e.g.
72 * http://msdn.microsoft.com/library/en-us/stg/stg/property_set_header.asp
73 * http://msdn.microsoft.com/library/library/en-us/stg/stg/section.asp
74 * but they don't seem to be in any header file.
76 #define PROPSETHDR_BYTEORDER_MAGIC 0xfffe
77 #define PROPSETHDR_OSVER_KIND_WIN16 0
78 #define PROPSETHDR_OSVER_KIND_MAC 1
79 #define PROPSETHDR_OSVER_KIND_WIN32 2
81 #define CP_UNICODE 1200
83 #define MAX_VERSION_0_PROP_NAME_LENGTH 256
85 #define CFTAG_WINDOWS (-1L)
86 #define CFTAG_MACINTOSH (-2L)
87 #define CFTAG_FMTID (-3L)
88 #define CFTAG_NODATA 0L
90 /* The format version (and what it implies) is described here:
91 * http://msdn.microsoft.com/library/en-us/stg/stg/format_version.asp
93 typedef struct tagPROPERTYSETHEADER
95 WORD wByteOrder; /* always 0xfffe */
96 WORD wFormat; /* can be zero or one */
97 DWORD dwOSVer; /* OS version of originating system */
98 CLSID clsid; /* application CLSID */
99 DWORD reserved; /* always 1 */
100 } PROPERTYSETHEADER;
102 typedef struct tagFORMATIDOFFSET
104 FMTID fmtid;
105 DWORD dwOffset; /* from beginning of stream */
106 } FORMATIDOFFSET;
108 typedef struct tagPROPERTYSECTIONHEADER
110 DWORD cbSection;
111 DWORD cProperties;
112 } PROPERTYSECTIONHEADER;
114 typedef struct tagPROPERTYIDOFFSET
116 DWORD propid;
117 DWORD dwOffset; /* from beginning of section */
118 } PROPERTYIDOFFSET;
120 typedef struct tagPropertyStorage_impl PropertyStorage_impl;
122 /* Initializes the property storage from the stream (and undoes any uncommitted
123 * changes in the process.) Returns an error if there is an error reading or
124 * if the stream format doesn't match what's expected.
126 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *);
128 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *);
130 /* Creates the dictionaries used by the property storage. If successful, all
131 * the dictionaries have been created. If failed, none has been. (This makes
132 * it a bit easier to deal with destroying them.)
134 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *);
136 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *);
138 /* Copies from propvar to prop. If propvar's type is VT_LPSTR, copies the
139 * string using PropertyStorage_StringCopy.
141 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
142 const PROPVARIANT *propvar, LCID targetCP, LCID srcCP);
144 /* Copies the string src, which is encoded using code page srcCP, and returns
145 * it in *dst, in the code page specified by targetCP. The returned string is
146 * allocated using CoTaskMemAlloc.
147 * If srcCP is CP_UNICODE, src is in fact an LPCWSTR. Similarly, if targetCP
148 * is CP_UNICODE, the returned string is in fact an LPWSTR.
149 * Returns S_OK on success, something else on failure.
151 static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
152 LCID targetCP);
154 static const IPropertyStorageVtbl IPropertyStorage_Vtbl;
155 static const IEnumSTATPROPSETSTGVtbl IEnumSTATPROPSETSTG_Vtbl;
156 static const IEnumSTATPROPSTGVtbl IEnumSTATPROPSTG_Vtbl;
157 static HRESULT create_EnumSTATPROPSETSTG(StorageImpl *, IEnumSTATPROPSETSTG**);
158 static HRESULT create_EnumSTATPROPSTG(PropertyStorage_impl *, IEnumSTATPROPSTG**);
160 /***********************************************************************
161 * Implementation of IPropertyStorage
163 struct tagPropertyStorage_impl
165 const IPropertyStorageVtbl *vtbl;
166 LONG ref;
167 CRITICAL_SECTION cs;
168 IStream *stm;
169 BOOL dirty;
170 FMTID fmtid;
171 CLSID clsid;
172 WORD format;
173 DWORD originatorOS;
174 DWORD grfFlags;
175 DWORD grfMode;
176 UINT codePage;
177 LCID locale;
178 PROPID highestProp;
179 struct dictionary *name_to_propid;
180 struct dictionary *propid_to_name;
181 struct dictionary *propid_to_prop;
184 /************************************************************************
185 * IPropertyStorage_fnQueryInterface (IPropertyStorage)
187 static HRESULT WINAPI IPropertyStorage_fnQueryInterface(
188 IPropertyStorage *iface,
189 REFIID riid,
190 void** ppvObject)
192 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
194 if ( (This==0) || (ppvObject==0) )
195 return E_INVALIDARG;
197 *ppvObject = 0;
199 if (IsEqualGUID(&IID_IUnknown, riid) ||
200 IsEqualGUID(&IID_IPropertyStorage, riid))
202 IPropertyStorage_AddRef(iface);
203 *ppvObject = (IPropertyStorage*)iface;
204 return S_OK;
207 return E_NOINTERFACE;
210 /************************************************************************
211 * IPropertyStorage_fnAddRef (IPropertyStorage)
213 static ULONG WINAPI IPropertyStorage_fnAddRef(
214 IPropertyStorage *iface)
216 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
217 return InterlockedIncrement(&This->ref);
220 /************************************************************************
221 * IPropertyStorage_fnRelease (IPropertyStorage)
223 static ULONG WINAPI IPropertyStorage_fnRelease(
224 IPropertyStorage *iface)
226 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
227 ULONG ref;
229 ref = InterlockedDecrement(&This->ref);
230 if (ref == 0)
232 TRACE("Destroying %p\n", This);
233 if (This->dirty)
234 IPropertyStorage_Commit(iface, STGC_DEFAULT);
235 IStream_Release(This->stm);
236 DeleteCriticalSection(&This->cs);
237 PropertyStorage_DestroyDictionaries(This);
238 HeapFree(GetProcessHeap(), 0, This);
240 return ref;
243 static PROPVARIANT *PropertyStorage_FindProperty(PropertyStorage_impl *This,
244 DWORD propid)
246 PROPVARIANT *ret = NULL;
248 dictionary_find(This->propid_to_prop, (void *)propid, (void **)&ret);
249 TRACE("returning %p\n", ret);
250 return ret;
253 /* Returns NULL if name is NULL. */
254 static PROPVARIANT *PropertyStorage_FindPropertyByName(
255 PropertyStorage_impl *This, LPCWSTR name)
257 PROPVARIANT *ret = NULL;
258 PROPID propid;
260 if (!name)
261 return NULL;
262 if (This->codePage == CP_UNICODE)
264 if (dictionary_find(This->name_to_propid, name, (void **)&propid))
265 ret = PropertyStorage_FindProperty(This, (PROPID)propid);
267 else
269 LPSTR ansiName;
270 HRESULT hr = PropertyStorage_StringCopy((LPCSTR)name, CP_UNICODE,
271 &ansiName, This->codePage);
273 if (SUCCEEDED(hr))
275 if (dictionary_find(This->name_to_propid, ansiName,
276 (void **)&propid))
277 ret = PropertyStorage_FindProperty(This, (PROPID)propid);
278 CoTaskMemFree(ansiName);
281 TRACE("returning %p\n", ret);
282 return ret;
285 static LPWSTR PropertyStorage_FindPropertyNameById(PropertyStorage_impl *This,
286 DWORD propid)
288 LPWSTR ret = NULL;
290 dictionary_find(This->propid_to_name, (void *)propid, (void **)&ret);
291 TRACE("returning %p\n", ret);
292 return ret;
295 /************************************************************************
296 * IPropertyStorage_fnReadMultiple (IPropertyStorage)
298 static HRESULT WINAPI IPropertyStorage_fnReadMultiple(
299 IPropertyStorage* iface,
300 ULONG cpspec,
301 const PROPSPEC rgpspec[],
302 PROPVARIANT rgpropvar[])
304 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
305 HRESULT hr = S_OK;
306 ULONG i;
308 TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
310 if (!cpspec)
311 return S_FALSE;
312 if (!rgpspec || !rgpropvar)
313 return E_INVALIDARG;
314 EnterCriticalSection(&This->cs);
315 for (i = 0; i < cpspec; i++)
317 PropVariantInit(&rgpropvar[i]);
318 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
320 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
321 rgpspec[i].u.lpwstr);
323 if (prop)
324 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop, GetACP(),
325 This->codePage);
327 else
329 switch (rgpspec[i].u.propid)
331 case PID_CODEPAGE:
332 rgpropvar[i].vt = VT_I2;
333 rgpropvar[i].u.iVal = This->codePage;
334 break;
335 case PID_LOCALE:
336 rgpropvar[i].vt = VT_I4;
337 rgpropvar[i].u.lVal = This->locale;
338 break;
339 default:
341 PROPVARIANT *prop = PropertyStorage_FindProperty(This,
342 rgpspec[i].u.propid);
344 if (prop)
345 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop,
346 GetACP(), This->codePage);
347 else
348 hr = S_FALSE;
353 LeaveCriticalSection(&This->cs);
354 return hr;
357 static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
358 LCID dstCP)
360 HRESULT hr = S_OK;
361 int len;
363 TRACE("%s, %p, %ld, %ld\n",
364 srcCP == CP_UNICODE ? debugstr_w((LPCWSTR)src) : debugstr_a(src), dst,
365 dstCP, srcCP);
366 assert(src);
367 assert(dst);
368 *dst = NULL;
369 if (dstCP == srcCP)
371 size_t len;
373 if (dstCP == CP_UNICODE)
374 len = (strlenW((LPCWSTR)src) + 1) * sizeof(WCHAR);
375 else
376 len = strlen(src) + 1;
377 *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
378 if (!*dst)
379 hr = STG_E_INSUFFICIENTMEMORY;
380 else
381 memcpy(*dst, src, len);
383 else
385 if (dstCP == CP_UNICODE)
387 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
388 *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
389 if (!*dst)
390 hr = STG_E_INSUFFICIENTMEMORY;
391 else
392 MultiByteToWideChar(srcCP, 0, src, -1, (LPWSTR)*dst, len);
394 else
396 LPWSTR wideStr;
398 if (srcCP == CP_UNICODE)
399 wideStr = (LPWSTR)src;
400 else
402 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
403 wideStr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
404 if (wideStr)
405 MultiByteToWideChar(srcCP, 0, src, -1, wideStr, len);
406 else
407 hr = STG_E_INSUFFICIENTMEMORY;
409 if (SUCCEEDED(hr))
411 len = WideCharToMultiByte(dstCP, 0, wideStr, -1, NULL, 0,
412 NULL, NULL);
413 *dst = CoTaskMemAlloc(len);
414 if (!*dst)
415 hr = STG_E_INSUFFICIENTMEMORY;
416 else
418 BOOL defCharUsed = FALSE;
420 if (WideCharToMultiByte(dstCP, 0, wideStr, -1, *dst, len,
421 NULL, &defCharUsed) == 0 || defCharUsed)
423 CoTaskMemFree(*dst);
424 *dst = NULL;
425 hr = HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION);
429 if (wideStr != (LPWSTR)src)
430 HeapFree(GetProcessHeap(), 0, wideStr);
433 TRACE("returning 0x%08lx (%s)\n", hr,
434 dstCP == CP_UNICODE ? debugstr_w((LPCWSTR)*dst) : debugstr_a(*dst));
435 return hr;
438 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
439 const PROPVARIANT *propvar, LCID targetCP, LCID srcCP)
441 HRESULT hr = S_OK;
443 assert(prop);
444 assert(propvar);
445 if (propvar->vt == VT_LPSTR)
447 hr = PropertyStorage_StringCopy(propvar->u.pszVal, srcCP,
448 &prop->u.pszVal, targetCP);
449 if (SUCCEEDED(hr))
450 prop->vt = VT_LPSTR;
452 else
453 PropVariantCopy(prop, propvar);
454 return hr;
457 /* Stores the property with id propid and value propvar into this property
458 * storage. lcid is ignored if propvar's type is not VT_LPSTR. If propvar's
459 * type is VT_LPSTR, converts the string using lcid as the source code page
460 * and This->codePage as the target code page before storing.
461 * As a side effect, may change This->format to 1 if the type of propvar is
462 * a version 1-only property.
464 static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
465 PROPID propid, const PROPVARIANT *propvar, LCID lcid)
467 HRESULT hr = S_OK;
468 PROPVARIANT *prop = PropertyStorage_FindProperty(This, propid);
470 assert(propvar);
471 if (propvar->vt & VT_BYREF || propvar->vt & VT_ARRAY)
472 This->format = 1;
473 switch (propvar->vt)
475 case VT_DECIMAL:
476 case VT_I1:
477 case VT_INT:
478 case VT_UINT:
479 case VT_VECTOR|VT_I1:
480 This->format = 1;
482 TRACE("Setting 0x%08lx to type %d\n", propid, propvar->vt);
483 if (prop)
485 PropVariantClear(prop);
486 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
487 lcid);
489 else
491 prop = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
492 sizeof(PROPVARIANT));
493 if (prop)
495 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
496 lcid);
497 if (SUCCEEDED(hr))
499 dictionary_insert(This->propid_to_prop, (void *)propid, prop);
500 if (propid > This->highestProp)
501 This->highestProp = propid;
503 else
504 HeapFree(GetProcessHeap(), 0, prop);
506 else
507 hr = STG_E_INSUFFICIENTMEMORY;
509 return hr;
512 /* Adds the name srcName to the name dictionaries, mapped to property ID id.
513 * srcName is encoded in code page cp, and is converted to This->codePage.
514 * If cp is CP_UNICODE, srcName is actually a unicode string.
515 * As a side effect, may change This->format to 1 if srcName is too long for
516 * a version 0 property storage.
517 * Doesn't validate id.
519 static HRESULT PropertyStorage_StoreNameWithId(PropertyStorage_impl *This,
520 LPCSTR srcName, LCID cp, PROPID id)
522 LPSTR name;
523 HRESULT hr;
525 assert(srcName);
527 hr = PropertyStorage_StringCopy((LPCSTR)srcName, cp, &name, This->codePage);
528 if (SUCCEEDED(hr))
530 if (This->codePage == CP_UNICODE)
532 if (lstrlenW((LPWSTR)name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
533 This->format = 1;
535 else
537 if (strlen(name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
538 This->format = 1;
540 TRACE("Adding prop name %s, propid %ld\n",
541 This->codePage == CP_UNICODE ? debugstr_w((LPCWSTR)name) :
542 debugstr_a(name), id);
543 dictionary_insert(This->name_to_propid, name, (void *)id);
544 dictionary_insert(This->propid_to_name, (void *)id, name);
546 return hr;
549 /************************************************************************
550 * IPropertyStorage_fnWriteMultiple (IPropertyStorage)
552 static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
553 IPropertyStorage* iface,
554 ULONG cpspec,
555 const PROPSPEC rgpspec[],
556 const PROPVARIANT rgpropvar[],
557 PROPID propidNameFirst)
559 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
560 HRESULT hr = S_OK;
561 ULONG i;
563 TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
565 if (cpspec && (!rgpspec || !rgpropvar))
566 return E_INVALIDARG;
567 if (!(This->grfMode & STGM_READWRITE))
568 return STG_E_ACCESSDENIED;
569 EnterCriticalSection(&This->cs);
570 This->dirty = TRUE;
571 This->originatorOS = (DWORD)MAKELONG(LOWORD(GetVersion()),
572 PROPSETHDR_OSVER_KIND_WIN32) ;
573 for (i = 0; i < cpspec; i++)
575 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
577 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
578 rgpspec[i].u.lpwstr);
580 if (prop)
581 PropVariantCopy(prop, &rgpropvar[i]);
582 else
584 /* Note that I don't do the special cases here that I do below,
585 * because naming the special PIDs isn't supported.
587 if (propidNameFirst < PID_FIRST_USABLE ||
588 propidNameFirst >= PID_MIN_READONLY)
589 hr = STG_E_INVALIDPARAMETER;
590 else
592 PROPID nextId = max(propidNameFirst, This->highestProp + 1);
594 hr = PropertyStorage_StoreNameWithId(This,
595 (LPCSTR)rgpspec[i].u.lpwstr, CP_UNICODE, nextId);
596 if (SUCCEEDED(hr))
597 hr = PropertyStorage_StorePropWithId(This, nextId,
598 &rgpropvar[i], GetACP());
602 else
604 switch (rgpspec[i].u.propid)
606 case PID_DICTIONARY:
607 /* Can't set the dictionary */
608 hr = STG_E_INVALIDPARAMETER;
609 break;
610 case PID_CODEPAGE:
611 /* Can only set the code page if nothing else has been set */
612 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
613 rgpropvar[i].vt == VT_I2)
615 This->codePage = rgpropvar[i].u.iVal;
616 if (This->codePage == CP_UNICODE)
617 This->grfFlags &= ~PROPSETFLAG_ANSI;
618 else
619 This->grfFlags |= PROPSETFLAG_ANSI;
621 else
622 hr = STG_E_INVALIDPARAMETER;
623 break;
624 case PID_LOCALE:
625 /* Can only set the locale if nothing else has been set */
626 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
627 rgpropvar[i].vt == VT_I4)
628 This->locale = rgpropvar[i].u.lVal;
629 else
630 hr = STG_E_INVALIDPARAMETER;
631 break;
632 case PID_ILLEGAL:
633 /* silently ignore like MSDN says */
634 break;
635 default:
636 if (rgpspec[i].u.propid >= PID_MIN_READONLY)
637 hr = STG_E_INVALIDPARAMETER;
638 else
639 hr = PropertyStorage_StorePropWithId(This,
640 rgpspec[i].u.propid, &rgpropvar[i], GetACP());
644 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
645 IPropertyStorage_Commit(iface, STGC_DEFAULT);
646 LeaveCriticalSection(&This->cs);
647 return hr;
650 /************************************************************************
651 * IPropertyStorage_fnDeleteMultiple (IPropertyStorage)
653 static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
654 IPropertyStorage* iface,
655 ULONG cpspec,
656 const PROPSPEC rgpspec[])
658 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
659 ULONG i;
660 HRESULT hr;
662 TRACE("(%p, %ld, %p)\n", iface, cpspec, rgpspec);
664 if (cpspec && !rgpspec)
665 return E_INVALIDARG;
666 if (!(This->grfMode & STGM_READWRITE))
667 return STG_E_ACCESSDENIED;
668 hr = S_OK;
669 EnterCriticalSection(&This->cs);
670 This->dirty = TRUE;
671 for (i = 0; i < cpspec; i++)
673 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
675 PROPID propid;
677 if (dictionary_find(This->name_to_propid,
678 (void *)rgpspec[i].u.lpwstr, (void **)&propid))
679 dictionary_remove(This->propid_to_prop, (void *)propid);
681 else
683 if (rgpspec[i].u.propid >= PID_FIRST_USABLE &&
684 rgpspec[i].u.propid < PID_MIN_READONLY)
685 dictionary_remove(This->propid_to_prop,
686 (void *)rgpspec[i].u.propid);
687 else
688 hr = STG_E_INVALIDPARAMETER;
691 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
692 IPropertyStorage_Commit(iface, STGC_DEFAULT);
693 LeaveCriticalSection(&This->cs);
694 return hr;
697 /************************************************************************
698 * IPropertyStorage_fnReadPropertyNames (IPropertyStorage)
700 static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames(
701 IPropertyStorage* iface,
702 ULONG cpropid,
703 const PROPID rgpropid[],
704 LPOLESTR rglpwstrName[])
706 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
707 ULONG i;
708 HRESULT hr = S_FALSE;
710 TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
712 if (cpropid && (!rgpropid || !rglpwstrName))
713 return E_INVALIDARG;
714 EnterCriticalSection(&This->cs);
715 for (i = 0; i < cpropid && SUCCEEDED(hr); i++)
717 LPWSTR name = PropertyStorage_FindPropertyNameById(This, rgpropid[i]);
719 if (name)
721 size_t len = lstrlenW(name);
723 hr = S_OK;
724 rglpwstrName[i] = CoTaskMemAlloc((len + 1) * sizeof(WCHAR));
725 if (rglpwstrName)
726 memcpy(rglpwstrName, name, (len + 1) * sizeof(WCHAR));
727 else
728 hr = STG_E_INSUFFICIENTMEMORY;
730 else
731 rglpwstrName[i] = NULL;
733 LeaveCriticalSection(&This->cs);
734 return hr;
737 /************************************************************************
738 * IPropertyStorage_fnWritePropertyNames (IPropertyStorage)
740 static HRESULT WINAPI IPropertyStorage_fnWritePropertyNames(
741 IPropertyStorage* iface,
742 ULONG cpropid,
743 const PROPID rgpropid[],
744 const LPOLESTR rglpwstrName[])
746 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
747 ULONG i;
748 HRESULT hr;
750 TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
752 if (cpropid && (!rgpropid || !rglpwstrName))
753 return E_INVALIDARG;
754 if (!(This->grfMode & STGM_READWRITE))
755 return STG_E_ACCESSDENIED;
756 hr = S_OK;
757 EnterCriticalSection(&This->cs);
758 This->dirty = TRUE;
759 for (i = 0; SUCCEEDED(hr) && i < cpropid; i++)
761 if (rgpropid[i] != PID_ILLEGAL)
762 hr = PropertyStorage_StoreNameWithId(This, (LPCSTR)rglpwstrName[i],
763 CP_UNICODE, rgpropid[i]);
765 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
766 IPropertyStorage_Commit(iface, STGC_DEFAULT);
767 LeaveCriticalSection(&This->cs);
768 return hr;
771 /************************************************************************
772 * IPropertyStorage_fnDeletePropertyNames (IPropertyStorage)
774 static HRESULT WINAPI IPropertyStorage_fnDeletePropertyNames(
775 IPropertyStorage* iface,
776 ULONG cpropid,
777 const PROPID rgpropid[])
779 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
780 ULONG i;
781 HRESULT hr;
783 TRACE("(%p, %ld, %p)\n", iface, cpropid, rgpropid);
785 if (cpropid && !rgpropid)
786 return E_INVALIDARG;
787 if (!(This->grfMode & STGM_READWRITE))
788 return STG_E_ACCESSDENIED;
789 hr = S_OK;
790 EnterCriticalSection(&This->cs);
791 This->dirty = TRUE;
792 for (i = 0; i < cpropid; i++)
794 LPWSTR name = NULL;
796 if (dictionary_find(This->propid_to_name, (void *)rgpropid[i],
797 (void **)&name))
799 dictionary_remove(This->propid_to_name, (void *)rgpropid[i]);
800 dictionary_remove(This->name_to_propid, name);
803 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
804 IPropertyStorage_Commit(iface, STGC_DEFAULT);
805 LeaveCriticalSection(&This->cs);
806 return hr;
809 /************************************************************************
810 * IPropertyStorage_fnCommit (IPropertyStorage)
812 static HRESULT WINAPI IPropertyStorage_fnCommit(
813 IPropertyStorage* iface,
814 DWORD grfCommitFlags)
816 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
817 HRESULT hr;
819 TRACE("(%p, 0x%08lx)\n", iface, grfCommitFlags);
821 if (!(This->grfMode & STGM_READWRITE))
822 return STG_E_ACCESSDENIED;
823 EnterCriticalSection(&This->cs);
824 if (This->dirty)
825 hr = PropertyStorage_WriteToStream(This);
826 else
827 hr = S_OK;
828 LeaveCriticalSection(&This->cs);
829 return hr;
832 /************************************************************************
833 * IPropertyStorage_fnRevert (IPropertyStorage)
835 static HRESULT WINAPI IPropertyStorage_fnRevert(
836 IPropertyStorage* iface)
838 HRESULT hr;
839 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
841 TRACE("%p\n", iface);
843 EnterCriticalSection(&This->cs);
844 if (This->dirty)
846 PropertyStorage_DestroyDictionaries(This);
847 hr = PropertyStorage_CreateDictionaries(This);
848 if (SUCCEEDED(hr))
849 hr = PropertyStorage_ReadFromStream(This);
851 else
852 hr = S_OK;
853 LeaveCriticalSection(&This->cs);
854 return hr;
857 /************************************************************************
858 * IPropertyStorage_fnEnum (IPropertyStorage)
860 static HRESULT WINAPI IPropertyStorage_fnEnum(
861 IPropertyStorage* iface,
862 IEnumSTATPROPSTG** ppenum)
864 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
865 return create_EnumSTATPROPSTG(This, ppenum);
868 /************************************************************************
869 * IPropertyStorage_fnSetTimes (IPropertyStorage)
871 static HRESULT WINAPI IPropertyStorage_fnSetTimes(
872 IPropertyStorage* iface,
873 const FILETIME* pctime,
874 const FILETIME* patime,
875 const FILETIME* pmtime)
877 FIXME("\n");
878 return E_NOTIMPL;
881 /************************************************************************
882 * IPropertyStorage_fnSetClass (IPropertyStorage)
884 static HRESULT WINAPI IPropertyStorage_fnSetClass(
885 IPropertyStorage* iface,
886 REFCLSID clsid)
888 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
890 TRACE("%p, %s\n", iface, debugstr_guid(clsid));
892 if (!clsid)
893 return E_INVALIDARG;
894 if (!(This->grfMode & STGM_READWRITE))
895 return STG_E_ACCESSDENIED;
896 memcpy(&This->clsid, clsid, sizeof(This->clsid));
897 This->dirty = TRUE;
898 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
899 IPropertyStorage_Commit(iface, STGC_DEFAULT);
900 return S_OK;
903 /************************************************************************
904 * IPropertyStorage_fnStat (IPropertyStorage)
906 static HRESULT WINAPI IPropertyStorage_fnStat(
907 IPropertyStorage* iface,
908 STATPROPSETSTG* statpsstg)
910 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
911 STATSTG stat;
912 HRESULT hr;
914 TRACE("%p, %p\n", iface, statpsstg);
916 if (!statpsstg)
917 return E_INVALIDARG;
919 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
920 if (SUCCEEDED(hr))
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;
930 return hr;
933 static int PropertyStorage_PropNameCompare(const void *a, const void *b,
934 void *extra)
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);
943 else
944 return lstrcmpiW((LPCWSTR)a, (LPCWSTR)b);
946 else
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);
951 else
952 return lstrcmpiA((LPCSTR)a, (LPCSTR)b);
956 static void PropertyStorage_PropNameDestroy(void *k, void *d, void *extra)
958 CoTaskMemFree(k);
961 static int PropertyStorage_PropCompare(const void *a, const void *b,
962 void *extra)
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)
980 DWORD i;
982 /* Swap characters to host order.
983 * FIXME: alignment?
985 for (i = 0; i < len; i++)
986 str[i] = le16toh(str[i]);
988 #else
989 #define PropertyStorage_ByteSwapString(s, l)
990 #endif
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
995 * end of the buffer.
997 static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This,
998 BYTE *ptr)
1000 DWORD numEntries, i;
1001 HRESULT hr = S_OK;
1003 assert(This->name_to_propid);
1004 assert(This->propid_to_name);
1006 StorageUtl_ReadDWord(ptr, 0, &numEntries);
1007 TRACE("Reading %ld entries:\n", numEntries);
1008 ptr += sizeof(DWORD);
1009 for (i = 0; SUCCEEDED(hr) && i < numEntries; i++)
1011 PROPID propid;
1012 DWORD cbEntry;
1014 StorageUtl_ReadDWord(ptr, 0, &propid);
1015 ptr += sizeof(PROPID);
1016 StorageUtl_ReadDWord(ptr, 0, &cbEntry);
1017 ptr += sizeof(DWORD);
1018 TRACE("Reading entry with ID 0x%08lx, %ld bytes\n", propid, cbEntry);
1019 /* Make sure the source string is NULL-terminated */
1020 if (This->codePage != CP_UNICODE)
1021 ptr[cbEntry - 1] = '\0';
1022 else
1023 *((LPWSTR)ptr + cbEntry / sizeof(WCHAR)) = '\0';
1024 hr = PropertyStorage_StoreNameWithId(This, (char*)ptr, This->codePage, propid);
1025 if (This->codePage == CP_UNICODE)
1027 /* Unicode entries are padded to DWORD boundaries */
1028 if (cbEntry % sizeof(DWORD))
1029 ptr += sizeof(DWORD) - (cbEntry % sizeof(DWORD));
1031 ptr += sizeof(DWORD) + cbEntry;
1033 return hr;
1036 /* FIXME: there isn't any checking whether the read property extends past the
1037 * end of the buffer.
1039 static HRESULT PropertyStorage_ReadProperty(PropertyStorage_impl *This,
1040 PROPVARIANT *prop, const BYTE *data)
1042 HRESULT hr = S_OK;
1044 assert(prop);
1045 assert(data);
1046 StorageUtl_ReadDWord(data, 0, (DWORD *)&prop->vt);
1047 data += sizeof(DWORD);
1048 switch (prop->vt)
1050 case VT_EMPTY:
1051 case VT_NULL:
1052 break;
1053 case VT_I1:
1054 prop->u.cVal = *(const char *)data;
1055 TRACE("Read char 0x%x ('%c')\n", prop->u.cVal, prop->u.cVal);
1056 break;
1057 case VT_UI1:
1058 prop->u.bVal = *(const UCHAR *)data;
1059 TRACE("Read byte 0x%x\n", prop->u.bVal);
1060 break;
1061 case VT_I2:
1062 StorageUtl_ReadWord(data, 0, (WORD*)&prop->u.iVal);
1063 TRACE("Read short %d\n", prop->u.iVal);
1064 break;
1065 case VT_UI2:
1066 StorageUtl_ReadWord(data, 0, &prop->u.uiVal);
1067 TRACE("Read ushort %d\n", prop->u.uiVal);
1068 break;
1069 case VT_INT:
1070 case VT_I4:
1071 StorageUtl_ReadDWord(data, 0, (DWORD*)&prop->u.lVal);
1072 TRACE("Read long %ld\n", prop->u.lVal);
1073 break;
1074 case VT_UINT:
1075 case VT_UI4:
1076 StorageUtl_ReadDWord(data, 0, &prop->u.ulVal);
1077 TRACE("Read ulong %ld\n", prop->u.ulVal);
1078 break;
1079 case VT_LPSTR:
1081 DWORD count;
1083 StorageUtl_ReadDWord(data, 0, &count);
1084 if (This->codePage == CP_UNICODE && count / 2)
1086 WARN("Unicode string has odd number of bytes\n");
1087 hr = STG_E_INVALIDHEADER;
1089 else
1091 prop->u.pszVal = CoTaskMemAlloc(count);
1092 if (prop->u.pszVal)
1094 memcpy(prop->u.pszVal, data + sizeof(DWORD), count);
1095 /* This is stored in the code page specified in This->codePage.
1096 * Don't convert it, the caller will just store it as-is.
1098 if (This->codePage == CP_UNICODE)
1100 /* Make sure it's NULL-terminated */
1101 prop->u.pszVal[count / sizeof(WCHAR) - 1] = '\0';
1102 TRACE("Read string value %s\n",
1103 debugstr_w(prop->u.pwszVal));
1105 else
1107 /* Make sure it's NULL-terminated */
1108 prop->u.pszVal[count - 1] = '\0';
1109 TRACE("Read string value %s\n", debugstr_a(prop->u.pszVal));
1112 else
1113 hr = STG_E_INSUFFICIENTMEMORY;
1115 break;
1117 case VT_LPWSTR:
1119 DWORD count;
1121 StorageUtl_ReadDWord(data, 0, &count);
1122 prop->u.pwszVal = CoTaskMemAlloc(count * sizeof(WCHAR));
1123 if (prop->u.pwszVal)
1125 memcpy(prop->u.pwszVal, data + sizeof(DWORD),
1126 count * sizeof(WCHAR));
1127 /* make sure string is NULL-terminated */
1128 prop->u.pwszVal[count - 1] = '\0';
1129 PropertyStorage_ByteSwapString(prop->u.pwszVal, count);
1130 TRACE("Read string value %s\n", debugstr_w(prop->u.pwszVal));
1132 else
1133 hr = STG_E_INSUFFICIENTMEMORY;
1134 break;
1136 case VT_FILETIME:
1137 StorageUtl_ReadULargeInteger(data, 0,
1138 (ULARGE_INTEGER *)&prop->u.filetime);
1139 break;
1140 case VT_CF:
1142 DWORD len = 0, tag = 0;
1144 StorageUtl_ReadDWord(data, 0, &len);
1145 StorageUtl_ReadDWord(data, 4, &tag);
1146 if (len > 8)
1148 len -= 8;
1149 prop->u.pclipdata = CoTaskMemAlloc(sizeof (CLIPDATA));
1150 prop->u.pclipdata->cbSize = len;
1151 prop->u.pclipdata->ulClipFmt = tag;
1152 prop->u.pclipdata->pClipData = CoTaskMemAlloc(len);
1153 memcpy(prop->u.pclipdata->pClipData, data+8, len);
1155 else
1156 hr = STG_E_INVALIDPARAMETER;
1158 break;
1159 default:
1160 FIXME("unsupported type %d\n", prop->vt);
1161 hr = STG_E_INVALIDPARAMETER;
1163 return hr;
1166 static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
1167 PROPERTYSETHEADER *hdr)
1169 BYTE buf[sizeof(PROPERTYSETHEADER)];
1170 ULONG count = 0;
1171 HRESULT hr;
1173 assert(stm);
1174 assert(hdr);
1175 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1176 if (SUCCEEDED(hr))
1178 if (count != sizeof(buf))
1180 WARN("read only %ld\n", count);
1181 hr = STG_E_INVALIDHEADER;
1183 else
1185 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder),
1186 &hdr->wByteOrder);
1187 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat),
1188 &hdr->wFormat);
1189 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer),
1190 &hdr->dwOSVer);
1191 StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid),
1192 &hdr->clsid);
1193 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved),
1194 &hdr->reserved);
1197 TRACE("returning 0x%08lx\n", hr);
1198 return hr;
1201 static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm,
1202 FORMATIDOFFSET *fmt)
1204 BYTE buf[sizeof(FORMATIDOFFSET)];
1205 ULONG count = 0;
1206 HRESULT hr;
1208 assert(stm);
1209 assert(fmt);
1210 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1211 if (SUCCEEDED(hr))
1213 if (count != sizeof(buf))
1215 WARN("read only %ld\n", count);
1216 hr = STG_E_INVALIDHEADER;
1218 else
1220 StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid),
1221 &fmt->fmtid);
1222 StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset),
1223 &fmt->dwOffset);
1226 TRACE("returning 0x%08lx\n", hr);
1227 return hr;
1230 static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
1231 PROPERTYSECTIONHEADER *hdr)
1233 BYTE buf[sizeof(PROPERTYSECTIONHEADER)];
1234 ULONG count = 0;
1235 HRESULT hr;
1237 assert(stm);
1238 assert(hdr);
1239 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1240 if (SUCCEEDED(hr))
1242 if (count != sizeof(buf))
1244 WARN("read only %ld\n", count);
1245 hr = STG_E_INVALIDHEADER;
1247 else
1249 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1250 cbSection), &hdr->cbSection);
1251 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1252 cProperties), &hdr->cProperties);
1255 TRACE("returning 0x%08lx\n", hr);
1256 return hr;
1259 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This)
1261 PROPERTYSETHEADER hdr;
1262 FORMATIDOFFSET fmtOffset;
1263 PROPERTYSECTIONHEADER sectionHdr;
1264 LARGE_INTEGER seek;
1265 ULONG i;
1266 STATSTG stat;
1267 HRESULT hr;
1268 BYTE *buf = NULL;
1269 ULONG count = 0;
1270 DWORD dictOffset = 0;
1272 This->dirty = FALSE;
1273 This->highestProp = 0;
1274 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
1275 if (FAILED(hr))
1276 goto end;
1277 if (stat.cbSize.u.HighPart)
1279 WARN("stream too big\n");
1280 /* maximum size varies, but it can't be this big */
1281 hr = STG_E_INVALIDHEADER;
1282 goto end;
1284 if (stat.cbSize.u.LowPart == 0)
1286 /* empty stream is okay */
1287 hr = S_OK;
1288 goto end;
1290 else if (stat.cbSize.u.LowPart < sizeof(PROPERTYSETHEADER) +
1291 sizeof(FORMATIDOFFSET))
1293 WARN("stream too small\n");
1294 hr = STG_E_INVALIDHEADER;
1295 goto end;
1297 seek.QuadPart = 0;
1298 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1299 if (FAILED(hr))
1300 goto end;
1301 hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr);
1302 /* I've only seen reserved == 1, but the article says I shouldn't disallow
1303 * higher values.
1305 if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1)
1307 WARN("bad magic in prop set header\n");
1308 hr = STG_E_INVALIDHEADER;
1309 goto end;
1311 if (hdr.wFormat != 0 && hdr.wFormat != 1)
1313 WARN("bad format version %d\n", hdr.wFormat);
1314 hr = STG_E_INVALIDHEADER;
1315 goto end;
1317 This->format = hdr.wFormat;
1318 memcpy(&This->clsid, &hdr.clsid, sizeof(This->clsid));
1319 This->originatorOS = hdr.dwOSVer;
1320 if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC)
1321 WARN("File comes from a Mac, strings will probably be screwed up\n");
1322 hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset);
1323 if (FAILED(hr))
1324 goto end;
1325 if (fmtOffset.dwOffset > stat.cbSize.u.LowPart)
1327 WARN("invalid offset %ld (stream length is %ld)\n", fmtOffset.dwOffset,
1328 stat.cbSize.u.LowPart);
1329 hr = STG_E_INVALIDHEADER;
1330 goto end;
1332 /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
1333 * follow not one, but two sections. The first is the standard properties
1334 * for the document summary information, and the second is user-defined
1335 * properties. This is the only case in which multiple sections are
1336 * allowed.
1337 * Reading the second stream isn't implemented yet.
1339 hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, &sectionHdr);
1340 if (FAILED(hr))
1341 goto end;
1342 /* The section size includes the section header, so check it */
1343 if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER))
1345 WARN("section header too small, got %ld\n", sectionHdr.cbSection);
1346 hr = STG_E_INVALIDHEADER;
1347 goto end;
1349 buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection -
1350 sizeof(PROPERTYSECTIONHEADER));
1351 if (!buf)
1353 hr = STG_E_INSUFFICIENTMEMORY;
1354 goto end;
1356 hr = IStream_Read(This->stm, buf, sectionHdr.cbSection -
1357 sizeof(PROPERTYSECTIONHEADER), &count);
1358 if (FAILED(hr))
1359 goto end;
1360 TRACE("Reading %ld properties:\n", sectionHdr.cProperties);
1361 for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
1363 PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(buf +
1364 i * sizeof(PROPERTYIDOFFSET));
1366 if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) ||
1367 idOffset->dwOffset >= sectionHdr.cbSection - sizeof(DWORD))
1368 hr = STG_E_INVALIDPOINTER;
1369 else
1371 if (idOffset->propid >= PID_FIRST_USABLE &&
1372 idOffset->propid < PID_MIN_READONLY && idOffset->propid >
1373 This->highestProp)
1374 This->highestProp = idOffset->propid;
1375 if (idOffset->propid == PID_DICTIONARY)
1377 /* Don't read the dictionary yet, its entries depend on the
1378 * code page. Just store the offset so we know to read it
1379 * later.
1381 dictOffset = idOffset->dwOffset;
1382 TRACE("Dictionary offset is %ld\n", dictOffset);
1384 else
1386 PROPVARIANT prop;
1388 memset(&prop, 0, sizeof prop);
1389 if (SUCCEEDED(PropertyStorage_ReadProperty(This, &prop,
1390 buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER))))
1392 TRACE("Read property with ID 0x%08lx, type %d\n",
1393 idOffset->propid, prop.vt);
1394 switch(idOffset->propid)
1396 case PID_CODEPAGE:
1397 if (prop.vt == VT_I2)
1398 This->codePage = (UINT)prop.u.iVal;
1399 break;
1400 case PID_LOCALE:
1401 if (prop.vt == VT_I4)
1402 This->locale = (LCID)prop.u.lVal;
1403 break;
1404 case PID_BEHAVIOR:
1405 if (prop.vt == VT_I4 && prop.u.lVal)
1406 This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
1407 /* The format should already be 1, but just in case */
1408 This->format = 1;
1409 break;
1410 default:
1411 hr = PropertyStorage_StorePropWithId(This,
1412 idOffset->propid, &prop, This->codePage);
1418 if (!This->codePage)
1420 /* default to Unicode unless told not to, as specified here:
1421 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
1423 if (This->grfFlags & PROPSETFLAG_ANSI)
1424 This->codePage = GetACP();
1425 else
1426 This->codePage = CP_UNICODE;
1428 if (!This->locale)
1429 This->locale = LOCALE_SYSTEM_DEFAULT;
1430 TRACE("Code page is %d, locale is %ld\n", This->codePage, This->locale);
1431 if (dictOffset)
1432 hr = PropertyStorage_ReadDictionary(This,
1433 buf + dictOffset - sizeof(PROPERTYSECTIONHEADER));
1435 end:
1436 HeapFree(GetProcessHeap(), 0, buf);
1437 if (FAILED(hr))
1439 dictionary_destroy(This->name_to_propid);
1440 This->name_to_propid = NULL;
1441 dictionary_destroy(This->propid_to_name);
1442 This->propid_to_name = NULL;
1443 dictionary_destroy(This->propid_to_prop);
1444 This->propid_to_prop = NULL;
1446 return hr;
1449 static void PropertyStorage_MakeHeader(PropertyStorage_impl *This,
1450 PROPERTYSETHEADER *hdr)
1452 assert(hdr);
1453 StorageUtl_WriteWord((BYTE *)&hdr->wByteOrder, 0,
1454 PROPSETHDR_BYTEORDER_MAGIC);
1455 StorageUtl_WriteWord((BYTE *)&hdr->wFormat, 0, This->format);
1456 StorageUtl_WriteDWord((BYTE *)&hdr->dwOSVer, 0, This->originatorOS);
1457 StorageUtl_WriteGUID((BYTE *)&hdr->clsid, 0, &This->clsid);
1458 StorageUtl_WriteDWord((BYTE *)&hdr->reserved, 0, 1);
1461 static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl *This,
1462 FORMATIDOFFSET *fmtOffset)
1464 assert(fmtOffset);
1465 StorageUtl_WriteGUID((BYTE *)fmtOffset, 0, &This->fmtid);
1466 StorageUtl_WriteDWord((BYTE *)fmtOffset, offsetof(FORMATIDOFFSET, dwOffset),
1467 sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET));
1470 static void PropertyStorage_MakeSectionHdr(DWORD cbSection, DWORD numProps,
1471 PROPERTYSECTIONHEADER *hdr)
1473 assert(hdr);
1474 StorageUtl_WriteDWord((BYTE *)hdr, 0, cbSection);
1475 StorageUtl_WriteDWord((BYTE *)hdr,
1476 offsetof(PROPERTYSECTIONHEADER, cProperties), numProps);
1479 static void PropertyStorage_MakePropertyIdOffset(DWORD propid, DWORD dwOffset,
1480 PROPERTYIDOFFSET *propIdOffset)
1482 assert(propIdOffset);
1483 StorageUtl_WriteDWord((BYTE *)propIdOffset, 0, propid);
1484 StorageUtl_WriteDWord((BYTE *)propIdOffset,
1485 offsetof(PROPERTYIDOFFSET, dwOffset), dwOffset);
1488 struct DictionaryClosure
1490 HRESULT hr;
1491 DWORD bytesWritten;
1494 static BOOL PropertyStorage_DictionaryWriter(const void *key,
1495 const void *value, void *extra, void *closure)
1497 PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
1498 struct DictionaryClosure *c = (struct DictionaryClosure *)closure;
1499 DWORD propid;
1500 ULONG count;
1502 assert(key);
1503 assert(closure);
1504 StorageUtl_WriteDWord((LPBYTE)&propid, 0, (DWORD)value);
1505 c->hr = IStream_Write(This->stm, &propid, sizeof(propid), &count);
1506 if (FAILED(c->hr))
1507 goto end;
1508 c->bytesWritten += sizeof(DWORD);
1509 if (This->codePage == CP_UNICODE)
1511 DWORD keyLen, pad = 0;
1513 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0,
1514 (lstrlenW((LPWSTR)key) + 1) * sizeof(WCHAR));
1515 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1516 if (FAILED(c->hr))
1517 goto end;
1518 c->bytesWritten += sizeof(DWORD);
1519 /* Rather than allocate a copy, I'll swap the string to little-endian
1520 * in-place, write it, then swap it back.
1522 PropertyStorage_ByteSwapString(key, keyLen);
1523 c->hr = IStream_Write(This->stm, key, keyLen, &count);
1524 PropertyStorage_ByteSwapString(key, keyLen);
1525 if (FAILED(c->hr))
1526 goto end;
1527 c->bytesWritten += keyLen;
1528 if (keyLen % sizeof(DWORD))
1530 c->hr = IStream_Write(This->stm, &pad,
1531 sizeof(DWORD) - keyLen % sizeof(DWORD), &count);
1532 if (FAILED(c->hr))
1533 goto end;
1534 c->bytesWritten += sizeof(DWORD) - keyLen % sizeof(DWORD);
1537 else
1539 DWORD keyLen;
1541 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0, strlen((LPCSTR)key) + 1);
1542 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1543 if (FAILED(c->hr))
1544 goto end;
1545 c->bytesWritten += sizeof(DWORD);
1546 c->hr = IStream_Write(This->stm, key, keyLen, &count);
1547 if (FAILED(c->hr))
1548 goto end;
1549 c->bytesWritten += keyLen;
1551 end:
1552 return SUCCEEDED(c->hr);
1555 #define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)
1557 /* Writes the dictionary to the stream. Assumes without checking that the
1558 * dictionary isn't empty.
1560 static HRESULT PropertyStorage_WriteDictionaryToStream(
1561 PropertyStorage_impl *This, DWORD *sectionOffset)
1563 HRESULT hr;
1564 LARGE_INTEGER seek;
1565 PROPERTYIDOFFSET propIdOffset;
1566 ULONG count;
1567 DWORD dwTemp;
1568 struct DictionaryClosure closure;
1570 assert(sectionOffset);
1572 /* The dictionary's always the first property written, so seek to its
1573 * spot.
1575 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER);
1576 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1577 if (FAILED(hr))
1578 goto end;
1579 PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY, *sectionOffset,
1580 &propIdOffset);
1581 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1582 if (FAILED(hr))
1583 goto end;
1585 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1586 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1587 if (FAILED(hr))
1588 goto end;
1589 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0,
1590 dictionary_num_entries(This->name_to_propid));
1591 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1592 if (FAILED(hr))
1593 goto end;
1594 *sectionOffset += sizeof(dwTemp);
1596 closure.hr = S_OK;
1597 closure.bytesWritten = 0;
1598 dictionary_enumerate(This->name_to_propid, PropertyStorage_DictionaryWriter,
1599 &closure);
1600 hr = closure.hr;
1601 if (FAILED(hr))
1602 goto end;
1603 *sectionOffset += closure.bytesWritten;
1604 if (closure.bytesWritten % sizeof(DWORD))
1606 TRACE("adding %ld bytes of padding\n", sizeof(DWORD) -
1607 closure.bytesWritten % sizeof(DWORD));
1608 *sectionOffset += sizeof(DWORD) - closure.bytesWritten % sizeof(DWORD);
1611 end:
1612 return hr;
1615 static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This,
1616 DWORD propNum, DWORD propid, PROPVARIANT *var, DWORD *sectionOffset)
1618 HRESULT hr;
1619 LARGE_INTEGER seek;
1620 PROPERTYIDOFFSET propIdOffset;
1621 ULONG count;
1622 DWORD dwType, bytesWritten;
1624 assert(var);
1625 assert(sectionOffset);
1627 TRACE("%p, %ld, 0x%08lx, (%d), (%ld)\n", This, propNum, propid, var->vt,
1628 *sectionOffset);
1630 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) +
1631 propNum * sizeof(PROPERTYIDOFFSET);
1632 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1633 if (FAILED(hr))
1634 goto end;
1635 PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset);
1636 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1637 if (FAILED(hr))
1638 goto end;
1640 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1641 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1642 if (FAILED(hr))
1643 goto end;
1644 StorageUtl_WriteDWord((LPBYTE)&dwType, 0, var->vt);
1645 hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count);
1646 if (FAILED(hr))
1647 goto end;
1648 *sectionOffset += sizeof(dwType);
1650 switch (var->vt)
1652 case VT_EMPTY:
1653 case VT_NULL:
1654 bytesWritten = 0;
1655 break;
1656 case VT_I1:
1657 case VT_UI1:
1658 hr = IStream_Write(This->stm, &var->u.cVal, sizeof(var->u.cVal),
1659 &count);
1660 bytesWritten = count;
1661 break;
1662 case VT_I2:
1663 case VT_UI2:
1665 WORD wTemp;
1667 StorageUtl_WriteWord((LPBYTE)&wTemp, 0, var->u.iVal);
1668 hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count);
1669 bytesWritten = count;
1670 break;
1672 case VT_I4:
1673 case VT_UI4:
1675 DWORD dwTemp;
1677 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, var->u.lVal);
1678 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1679 bytesWritten = count;
1680 break;
1682 case VT_LPSTR:
1684 DWORD len, dwTemp;
1686 if (This->codePage == CP_UNICODE)
1687 len = (lstrlenW(var->u.pwszVal) + 1) * sizeof(WCHAR);
1688 else
1689 len = lstrlenA(var->u.pszVal) + 1;
1690 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1691 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1692 if (FAILED(hr))
1693 goto end;
1694 hr = IStream_Write(This->stm, var->u.pszVal, len, &count);
1695 bytesWritten = count + sizeof(DWORD);
1696 break;
1698 case VT_LPWSTR:
1700 DWORD len = lstrlenW(var->u.pwszVal) + 1, dwTemp;
1702 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1703 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1704 if (FAILED(hr))
1705 goto end;
1706 hr = IStream_Write(This->stm, var->u.pwszVal, len * sizeof(WCHAR),
1707 &count);
1708 bytesWritten = count + sizeof(DWORD);
1709 break;
1711 case VT_FILETIME:
1713 FILETIME temp;
1715 StorageUtl_WriteULargeInteger((BYTE *)&temp, 0,
1716 (ULARGE_INTEGER *)&var->u.filetime);
1717 hr = IStream_Write(This->stm, &temp, sizeof(FILETIME), &count);
1718 bytesWritten = count;
1719 break;
1721 case VT_CF:
1723 DWORD cf_hdr[2], len;
1725 len = var->u.pclipdata->cbSize;
1726 StorageUtl_WriteDWord((LPBYTE)&cf_hdr[0], 0, len + 8);
1727 StorageUtl_WriteDWord((LPBYTE)&cf_hdr[1], 0, var->u.pclipdata->ulClipFmt);
1728 hr = IStream_Write(This->stm, &cf_hdr, sizeof(cf_hdr), &count);
1729 if (FAILED(hr))
1730 goto end;
1731 hr = IStream_Write(This->stm, &var->u.pclipdata->pClipData, len, &count);
1732 if (FAILED(hr))
1733 goto end;
1734 bytesWritten = count + sizeof cf_hdr;
1735 break;
1737 default:
1738 FIXME("unsupported type: %d\n", var->vt);
1739 return STG_E_INVALIDPARAMETER;
1742 if (SUCCEEDED(hr))
1744 *sectionOffset += bytesWritten;
1745 if (bytesWritten % sizeof(DWORD))
1747 TRACE("adding %ld bytes of padding\n", sizeof(DWORD) -
1748 bytesWritten % sizeof(DWORD));
1749 *sectionOffset += sizeof(DWORD) - bytesWritten % sizeof(DWORD);
1753 end:
1754 return hr;
1757 struct PropertyClosure
1759 HRESULT hr;
1760 DWORD propNum;
1761 DWORD *sectionOffset;
1764 static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value,
1765 void *extra, void *closure)
1767 PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
1768 struct PropertyClosure *c = (struct PropertyClosure *)closure;
1770 assert(key);
1771 assert(value);
1772 assert(extra);
1773 assert(closure);
1774 c->hr = PropertyStorage_WritePropertyToStream(This,
1775 c->propNum++, (DWORD)key, (PROPVARIANT *)value, c->sectionOffset);
1776 return SUCCEEDED(c->hr);
1779 static HRESULT PropertyStorage_WritePropertiesToStream(
1780 PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset)
1782 struct PropertyClosure closure;
1784 assert(sectionOffset);
1785 closure.hr = S_OK;
1786 closure.propNum = startingPropNum;
1787 closure.sectionOffset = sectionOffset;
1788 dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter,
1789 &closure);
1790 return closure.hr;
1793 static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This)
1795 HRESULT hr;
1796 ULONG count = 0;
1797 LARGE_INTEGER seek = { {0} };
1798 PROPERTYSETHEADER hdr;
1799 FORMATIDOFFSET fmtOffset;
1801 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1802 if (FAILED(hr))
1803 goto end;
1804 PropertyStorage_MakeHeader(This, &hdr);
1805 hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count);
1806 if (FAILED(hr))
1807 goto end;
1808 if (count != sizeof(hdr))
1810 hr = STG_E_WRITEFAULT;
1811 goto end;
1814 PropertyStorage_MakeFmtIdOffset(This, &fmtOffset);
1815 hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count);
1816 if (FAILED(hr))
1817 goto end;
1818 if (count != sizeof(fmtOffset))
1820 hr = STG_E_WRITEFAULT;
1821 goto end;
1823 hr = S_OK;
1825 end:
1826 return hr;
1829 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This)
1831 PROPERTYSECTIONHEADER sectionHdr;
1832 HRESULT hr;
1833 ULONG count;
1834 LARGE_INTEGER seek;
1835 DWORD numProps, prop, sectionOffset, dwTemp;
1836 PROPVARIANT var;
1838 PropertyStorage_WriteHeadersToStream(This);
1840 /* Count properties. Always at least one property, the code page */
1841 numProps = 1;
1842 if (dictionary_num_entries(This->name_to_propid))
1843 numProps++;
1844 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1845 numProps++;
1846 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1847 numProps++;
1848 numProps += dictionary_num_entries(This->propid_to_prop);
1850 /* Write section header with 0 bytes right now, I'll adjust it after
1851 * writing properties.
1853 PropertyStorage_MakeSectionHdr(0, numProps, &sectionHdr);
1854 seek.QuadPart = SECTIONHEADER_OFFSET;
1855 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1856 if (FAILED(hr))
1857 goto end;
1858 hr = IStream_Write(This->stm, &sectionHdr, sizeof(sectionHdr), &count);
1859 if (FAILED(hr))
1860 goto end;
1862 prop = 0;
1863 sectionOffset = sizeof(PROPERTYSECTIONHEADER) +
1864 numProps * sizeof(PROPERTYIDOFFSET);
1866 if (dictionary_num_entries(This->name_to_propid))
1868 prop++;
1869 hr = PropertyStorage_WriteDictionaryToStream(This, &sectionOffset);
1870 if (FAILED(hr))
1871 goto end;
1874 PropVariantInit(&var);
1876 var.vt = VT_I2;
1877 var.u.iVal = This->codePage;
1878 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE,
1879 &var, &sectionOffset);
1880 if (FAILED(hr))
1881 goto end;
1883 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1885 var.vt = VT_I4;
1886 var.u.lVal = This->locale;
1887 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE,
1888 &var, &sectionOffset);
1889 if (FAILED(hr))
1890 goto end;
1893 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1895 var.vt = VT_I4;
1896 var.u.lVal = 1;
1897 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR,
1898 &var, &sectionOffset);
1899 if (FAILED(hr))
1900 goto end;
1903 hr = PropertyStorage_WritePropertiesToStream(This, prop, &sectionOffset);
1904 if (FAILED(hr))
1905 goto end;
1907 /* Now write the byte count of the section */
1908 seek.QuadPart = SECTIONHEADER_OFFSET;
1909 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1910 if (FAILED(hr))
1911 goto end;
1912 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, sectionOffset);
1913 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1915 end:
1916 return hr;
1919 /***********************************************************************
1920 * PropertyStorage_Construct
1922 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This)
1924 dictionary_destroy(This->name_to_propid);
1925 This->name_to_propid = NULL;
1926 dictionary_destroy(This->propid_to_name);
1927 This->propid_to_name = NULL;
1928 dictionary_destroy(This->propid_to_prop);
1929 This->propid_to_prop = NULL;
1932 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This)
1934 HRESULT hr = S_OK;
1936 This->name_to_propid = dictionary_create(
1937 PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy,
1938 This);
1939 if (!This->name_to_propid)
1941 hr = STG_E_INSUFFICIENTMEMORY;
1942 goto end;
1944 This->propid_to_name = dictionary_create(PropertyStorage_PropCompare,
1945 NULL, This);
1946 if (!This->propid_to_name)
1948 hr = STG_E_INSUFFICIENTMEMORY;
1949 goto end;
1951 This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
1952 PropertyStorage_PropertyDestroy, This);
1953 if (!This->propid_to_prop)
1955 hr = STG_E_INSUFFICIENTMEMORY;
1956 goto end;
1958 end:
1959 if (FAILED(hr))
1960 PropertyStorage_DestroyDictionaries(This);
1961 return hr;
1964 static HRESULT PropertyStorage_BaseConstruct(IStream *stm,
1965 REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps)
1967 HRESULT hr = S_OK;
1969 assert(pps);
1970 assert(rfmtid);
1971 *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps);
1972 if (!*pps)
1973 return E_OUTOFMEMORY;
1975 (*pps)->vtbl = &IPropertyStorage_Vtbl;
1976 (*pps)->ref = 1;
1977 InitializeCriticalSection(&(*pps)->cs);
1978 (*pps)->stm = stm;
1979 memcpy(&(*pps)->fmtid, rfmtid, sizeof((*pps)->fmtid));
1980 (*pps)->grfMode = grfMode;
1982 hr = PropertyStorage_CreateDictionaries(*pps);
1983 if (FAILED(hr))
1985 IStream_Release(stm);
1986 DeleteCriticalSection(&(*pps)->cs);
1987 HeapFree(GetProcessHeap(), 0, *pps);
1988 *pps = NULL;
1991 return hr;
1994 static HRESULT PropertyStorage_ConstructFromStream(IStream *stm,
1995 REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps)
1997 PropertyStorage_impl *ps;
1998 HRESULT hr;
2000 assert(pps);
2001 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2002 if (SUCCEEDED(hr))
2004 hr = PropertyStorage_ReadFromStream(ps);
2005 if (SUCCEEDED(hr))
2007 *pps = (IPropertyStorage *)ps;
2008 TRACE("PropertyStorage %p constructed\n", ps);
2009 hr = S_OK;
2011 else
2013 PropertyStorage_DestroyDictionaries(ps);
2014 HeapFree(GetProcessHeap(), 0, ps);
2017 return hr;
2020 static HRESULT PropertyStorage_ConstructEmpty(IStream *stm,
2021 REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
2023 PropertyStorage_impl *ps;
2024 HRESULT hr;
2026 assert(pps);
2027 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2028 if (SUCCEEDED(hr))
2030 ps->format = 0;
2031 ps->grfFlags = grfFlags;
2032 if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2033 ps->format = 1;
2034 /* default to Unicode unless told not to, as specified here:
2035 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2037 if (ps->grfFlags & PROPSETFLAG_ANSI)
2038 ps->codePage = GetACP();
2039 else
2040 ps->codePage = CP_UNICODE;
2041 ps->locale = LOCALE_SYSTEM_DEFAULT;
2042 TRACE("Code page is %d, locale is %ld\n", ps->codePage, ps->locale);
2043 *pps = (IPropertyStorage *)ps;
2044 TRACE("PropertyStorage %p constructed\n", ps);
2045 hr = S_OK;
2047 return hr;
2051 /***********************************************************************
2052 * Implementation of IPropertySetStorage
2055 /************************************************************************
2056 * IPropertySetStorage_fnQueryInterface (IUnknown)
2058 * This method forwards to the common QueryInterface implementation
2060 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
2061 IPropertySetStorage *ppstg,
2062 REFIID riid,
2063 void** ppvObject)
2065 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2066 return IStorage_QueryInterface( (IStorage*)This, riid, ppvObject );
2069 /************************************************************************
2070 * IPropertySetStorage_fnAddRef (IUnknown)
2072 * This method forwards to the common AddRef implementation
2074 static ULONG WINAPI IPropertySetStorage_fnAddRef(
2075 IPropertySetStorage *ppstg)
2077 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2078 return IStorage_AddRef( (IStorage*)This );
2081 /************************************************************************
2082 * IPropertySetStorage_fnRelease (IUnknown)
2084 * This method forwards to the common Release implementation
2086 static ULONG WINAPI IPropertySetStorage_fnRelease(
2087 IPropertySetStorage *ppstg)
2089 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2090 return IStorage_Release( (IStorage*)This );
2093 /************************************************************************
2094 * IPropertySetStorage_fnCreate (IPropertySetStorage)
2096 static HRESULT WINAPI IPropertySetStorage_fnCreate(
2097 IPropertySetStorage *ppstg,
2098 REFFMTID rfmtid,
2099 const CLSID* pclsid,
2100 DWORD grfFlags,
2101 DWORD grfMode,
2102 IPropertyStorage** ppprstg)
2104 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2105 WCHAR name[CCH_MAX_PROPSTG_NAME];
2106 IStream *stm = NULL;
2107 HRESULT r;
2109 TRACE("%p %s %08lx %08lx %p\n", This, debugstr_guid(rfmtid), grfFlags,
2110 grfMode, ppprstg);
2112 /* be picky */
2113 if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
2115 r = STG_E_INVALIDFLAG;
2116 goto end;
2119 if (!rfmtid)
2121 r = E_INVALIDARG;
2122 goto end;
2125 /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
2126 * storage, not a stream. For now, disallow it.
2128 if (grfFlags & PROPSETFLAG_NONSIMPLE)
2130 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2131 r = STG_E_INVALIDFLAG;
2132 goto end;
2135 r = FmtIdToPropStgName(rfmtid, name);
2136 if (FAILED(r))
2137 goto end;
2139 r = IStorage_CreateStream( (IStorage*)This, name, grfMode, 0, 0, &stm );
2140 if (FAILED(r))
2141 goto end;
2143 r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg);
2145 end:
2146 TRACE("returning 0x%08lx\n", r);
2147 return r;
2150 /************************************************************************
2151 * IPropertySetStorage_fnOpen (IPropertySetStorage)
2153 static HRESULT WINAPI IPropertySetStorage_fnOpen(
2154 IPropertySetStorage *ppstg,
2155 REFFMTID rfmtid,
2156 DWORD grfMode,
2157 IPropertyStorage** ppprstg)
2159 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2160 IStream *stm = NULL;
2161 WCHAR name[CCH_MAX_PROPSTG_NAME];
2162 HRESULT r;
2164 TRACE("%p %s %08lx %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
2166 /* be picky */
2167 if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
2168 grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
2170 r = STG_E_INVALIDFLAG;
2171 goto end;
2174 if (!rfmtid)
2176 r = E_INVALIDARG;
2177 goto end;
2180 r = FmtIdToPropStgName(rfmtid, name);
2181 if (FAILED(r))
2182 goto end;
2184 r = IStorage_OpenStream((IStorage*) This, name, 0, grfMode, 0, &stm );
2185 if (FAILED(r))
2186 goto end;
2188 r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg);
2190 end:
2191 TRACE("returning 0x%08lx\n", r);
2192 return r;
2195 /************************************************************************
2196 * IPropertySetStorage_fnDelete (IPropertySetStorage)
2198 static HRESULT WINAPI IPropertySetStorage_fnDelete(
2199 IPropertySetStorage *ppstg,
2200 REFFMTID rfmtid)
2202 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2203 IStorage *stg = NULL;
2204 WCHAR name[CCH_MAX_PROPSTG_NAME];
2205 HRESULT r;
2207 TRACE("%p %s\n", This, debugstr_guid(rfmtid));
2209 if (!rfmtid)
2210 return E_INVALIDARG;
2212 r = FmtIdToPropStgName(rfmtid, name);
2213 if (FAILED(r))
2214 return r;
2216 stg = (IStorage*) This;
2217 return IStorage_DestroyElement(stg, name);
2220 /************************************************************************
2221 * IPropertySetStorage_fnEnum (IPropertySetStorage)
2223 static HRESULT WINAPI IPropertySetStorage_fnEnum(
2224 IPropertySetStorage *ppstg,
2225 IEnumSTATPROPSETSTG** ppenum)
2227 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2228 return create_EnumSTATPROPSETSTG(This, ppenum);
2231 /************************************************************************
2232 * Implement IEnumSTATPROPSETSTG using enumx
2234 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnQueryInterface(
2235 IEnumSTATPROPSETSTG *iface,
2236 REFIID riid,
2237 void** ppvObject)
2239 return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
2242 static ULONG WINAPI IEnumSTATPROPSETSTG_fnAddRef(
2243 IEnumSTATPROPSETSTG *iface)
2245 return enumx_AddRef((enumx_impl*)iface);
2248 static ULONG WINAPI IEnumSTATPROPSETSTG_fnRelease(
2249 IEnumSTATPROPSETSTG *iface)
2251 return enumx_Release((enumx_impl*)iface);
2254 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnNext(
2255 IEnumSTATPROPSETSTG *iface,
2256 ULONG celt,
2257 STATPROPSETSTG *rgelt,
2258 ULONG *pceltFetched)
2260 return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
2263 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnSkip(
2264 IEnumSTATPROPSETSTG *iface,
2265 ULONG celt)
2267 return enumx_Skip((enumx_impl*)iface, celt);
2270 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnReset(
2271 IEnumSTATPROPSETSTG *iface)
2273 return enumx_Reset((enumx_impl*)iface);
2276 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnClone(
2277 IEnumSTATPROPSETSTG *iface,
2278 IEnumSTATPROPSETSTG **ppenum)
2280 return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
2283 static HRESULT create_EnumSTATPROPSETSTG(
2284 StorageImpl *This,
2285 IEnumSTATPROPSETSTG** ppenum)
2287 IStorage *stg = (IStorage*) &This->base.lpVtbl;
2288 IEnumSTATSTG *penum = NULL;
2289 STATSTG stat;
2290 ULONG count;
2291 HRESULT r;
2292 STATPROPSETSTG statpss;
2293 enumx_impl *enumx;
2295 TRACE("%p %p\n", This, ppenum);
2297 enumx = enumx_allocate(&IID_IEnumSTATPROPSETSTG,
2298 &IEnumSTATPROPSETSTG_Vtbl,
2299 sizeof (STATPROPSETSTG));
2301 /* add all the property set elements into a list */
2302 r = IStorage_EnumElements(stg, 0, NULL, 0, &penum);
2303 if (FAILED(r))
2304 return E_OUTOFMEMORY;
2306 while (1)
2308 count = 0;
2309 r = IEnumSTATSTG_Next(penum, 1, &stat, &count);
2310 if (FAILED(r))
2311 break;
2312 if (!count)
2313 break;
2314 if (!stat.pwcsName)
2315 continue;
2316 if (stat.pwcsName[0] == 5 && stat.type == STGTY_STREAM)
2318 PropStgNameToFmtId(stat.pwcsName, &statpss.fmtid);
2319 TRACE("adding %s (%s)\n", debugstr_w(stat.pwcsName),
2320 debugstr_guid(&statpss.fmtid));
2321 statpss.mtime = stat.mtime;
2322 statpss.atime = stat.atime;
2323 statpss.ctime = stat.ctime;
2324 statpss.grfFlags = stat.grfMode;
2325 memcpy(&statpss.clsid, &stat.clsid, sizeof stat.clsid);
2326 enumx_add_element(enumx, &statpss);
2328 CoTaskMemFree(stat.pwcsName);
2330 IEnumSTATSTG_Release(penum);
2332 *ppenum = (IEnumSTATPROPSETSTG*) enumx;
2334 return S_OK;
2337 /************************************************************************
2338 * Implement IEnumSTATPROPSTG using enumx
2340 static HRESULT WINAPI IEnumSTATPROPSTG_fnQueryInterface(
2341 IEnumSTATPROPSTG *iface,
2342 REFIID riid,
2343 void** ppvObject)
2345 return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
2348 static ULONG WINAPI IEnumSTATPROPSTG_fnAddRef(
2349 IEnumSTATPROPSTG *iface)
2351 return enumx_AddRef((enumx_impl*)iface);
2354 static ULONG WINAPI IEnumSTATPROPSTG_fnRelease(
2355 IEnumSTATPROPSTG *iface)
2357 return enumx_Release((enumx_impl*)iface);
2360 static HRESULT WINAPI IEnumSTATPROPSTG_fnNext(
2361 IEnumSTATPROPSTG *iface,
2362 ULONG celt,
2363 STATPROPSTG *rgelt,
2364 ULONG *pceltFetched)
2366 return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
2369 static HRESULT WINAPI IEnumSTATPROPSTG_fnSkip(
2370 IEnumSTATPROPSTG *iface,
2371 ULONG celt)
2373 return enumx_Skip((enumx_impl*)iface, celt);
2376 static HRESULT WINAPI IEnumSTATPROPSTG_fnReset(
2377 IEnumSTATPROPSTG *iface)
2379 return enumx_Reset((enumx_impl*)iface);
2382 static HRESULT WINAPI IEnumSTATPROPSTG_fnClone(
2383 IEnumSTATPROPSTG *iface,
2384 IEnumSTATPROPSTG **ppenum)
2386 return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
2389 static BOOL prop_enum_stat(const void *k, const void *v, void *extra, void *arg)
2391 enumx_impl *enumx = arg;
2392 PROPID propid = (PROPID) k;
2393 const PROPVARIANT *prop = v;
2394 STATPROPSTG stat;
2396 stat.lpwstrName = NULL;
2397 stat.propid = propid;
2398 stat.vt = prop->vt;
2400 enumx_add_element(enumx, &stat);
2402 return TRUE;
2405 static HRESULT create_EnumSTATPROPSTG(
2406 PropertyStorage_impl *This,
2407 IEnumSTATPROPSTG** ppenum)
2409 enumx_impl *enumx;
2411 TRACE("%p %p\n", This, ppenum);
2413 enumx = enumx_allocate(&IID_IEnumSTATPROPSTG,
2414 &IEnumSTATPROPSTG_Vtbl,
2415 sizeof (STATPROPSTG));
2417 dictionary_enumerate(This->propid_to_prop, prop_enum_stat, enumx);
2419 *ppenum = (IEnumSTATPROPSTG*) enumx;
2421 return S_OK;
2424 /***********************************************************************
2425 * vtables
2427 const IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
2429 IPropertySetStorage_fnQueryInterface,
2430 IPropertySetStorage_fnAddRef,
2431 IPropertySetStorage_fnRelease,
2432 IPropertySetStorage_fnCreate,
2433 IPropertySetStorage_fnOpen,
2434 IPropertySetStorage_fnDelete,
2435 IPropertySetStorage_fnEnum
2438 static const IPropertyStorageVtbl IPropertyStorage_Vtbl =
2440 IPropertyStorage_fnQueryInterface,
2441 IPropertyStorage_fnAddRef,
2442 IPropertyStorage_fnRelease,
2443 IPropertyStorage_fnReadMultiple,
2444 IPropertyStorage_fnWriteMultiple,
2445 IPropertyStorage_fnDeleteMultiple,
2446 IPropertyStorage_fnReadPropertyNames,
2447 IPropertyStorage_fnWritePropertyNames,
2448 IPropertyStorage_fnDeletePropertyNames,
2449 IPropertyStorage_fnCommit,
2450 IPropertyStorage_fnRevert,
2451 IPropertyStorage_fnEnum,
2452 IPropertyStorage_fnSetTimes,
2453 IPropertyStorage_fnSetClass,
2454 IPropertyStorage_fnStat,
2457 static const IEnumSTATPROPSETSTGVtbl IEnumSTATPROPSETSTG_Vtbl =
2459 IEnumSTATPROPSETSTG_fnQueryInterface,
2460 IEnumSTATPROPSETSTG_fnAddRef,
2461 IEnumSTATPROPSETSTG_fnRelease,
2462 IEnumSTATPROPSETSTG_fnNext,
2463 IEnumSTATPROPSETSTG_fnSkip,
2464 IEnumSTATPROPSETSTG_fnReset,
2465 IEnumSTATPROPSETSTG_fnClone,
2468 static const IEnumSTATPROPSTGVtbl IEnumSTATPROPSTG_Vtbl =
2470 IEnumSTATPROPSTG_fnQueryInterface,
2471 IEnumSTATPROPSTG_fnAddRef,
2472 IEnumSTATPROPSTG_fnRelease,
2473 IEnumSTATPROPSTG_fnNext,
2474 IEnumSTATPROPSTG_fnSkip,
2475 IEnumSTATPROPSTG_fnReset,
2476 IEnumSTATPROPSTG_fnClone,
2479 /***********************************************************************
2480 * Format ID <-> name conversion
2482 static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
2483 'I','n','f','o','r','m','a','t','i','o','n',0 };
2484 static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t',
2485 'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
2487 #define BITS_PER_BYTE 8
2488 #define CHARMASK 0x1f
2489 #define BITS_IN_CHARMASK 5
2490 #define NUM_ALPHA_CHARS 26
2492 /***********************************************************************
2493 * FmtIdToPropStgName [ole32.@]
2494 * Returns the storage name of the format ID rfmtid.
2495 * PARAMS
2496 * rfmtid [I] Format ID for which to return a storage name
2497 * str [O] Storage name associated with rfmtid.
2499 * RETURNS
2500 * E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise.
2502 * NOTES
2503 * str must be at least CCH_MAX_PROPSTG_NAME characters in length.
2504 * Based on the algorithm described here:
2505 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2507 HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str)
2509 static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
2511 TRACE("%s, %p\n", debugstr_guid(rfmtid), str);
2513 if (!rfmtid) return E_INVALIDARG;
2514 if (!str) return E_INVALIDARG;
2516 if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
2517 lstrcpyW(str, szSummaryInfo);
2518 else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
2519 lstrcpyW(str, szDocSummaryInfo);
2520 else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid))
2521 lstrcpyW(str, szDocSummaryInfo);
2522 else
2524 BYTE *fmtptr;
2525 WCHAR *pstr = str;
2526 ULONG bitsRemaining = BITS_PER_BYTE;
2528 *pstr++ = 5;
2529 for (fmtptr = (BYTE *)rfmtid; fmtptr < (BYTE *)rfmtid + sizeof(FMTID); )
2531 ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
2533 if (bitsRemaining >= BITS_IN_CHARMASK)
2535 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
2536 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
2537 *pstr <= 'z')
2538 *pstr += 'A' - 'a';
2539 pstr++;
2540 bitsRemaining -= BITS_IN_CHARMASK;
2541 if (bitsRemaining == 0)
2543 fmtptr++;
2544 bitsRemaining = BITS_PER_BYTE;
2547 else
2549 if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID))
2550 i |= *fmtptr << bitsRemaining;
2551 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
2552 bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK;
2555 *pstr = 0;
2557 TRACE("returning %s\n", debugstr_w(str));
2558 return S_OK;
2561 /***********************************************************************
2562 * PropStgNameToFmtId [ole32.@]
2563 * Returns the format ID corresponding to the given name.
2564 * PARAMS
2565 * str [I] Storage name to convert to a format ID.
2566 * rfmtid [O] Format ID corresponding to str.
2568 * RETURNS
2569 * E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to
2570 * a format ID, S_OK otherwise.
2572 * NOTES
2573 * Based on the algorithm described here:
2574 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2576 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid)
2578 HRESULT hr = STG_E_INVALIDNAME;
2580 TRACE("%s, %p\n", debugstr_w(str), rfmtid);
2582 if (!rfmtid) return E_INVALIDARG;
2583 if (!str) return STG_E_INVALIDNAME;
2585 if (!lstrcmpiW(str, szDocSummaryInfo))
2587 memcpy(rfmtid, &FMTID_DocSummaryInformation, sizeof(*rfmtid));
2588 hr = S_OK;
2590 else if (!lstrcmpiW(str, szSummaryInfo))
2592 memcpy(rfmtid, &FMTID_SummaryInformation, sizeof(*rfmtid));
2593 hr = S_OK;
2595 else
2597 ULONG bits;
2598 BYTE *fmtptr = (BYTE *)rfmtid - 1;
2599 const WCHAR *pstr = str;
2601 memset(rfmtid, 0, sizeof(*rfmtid));
2602 for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE;
2603 bits += BITS_IN_CHARMASK)
2605 ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored;
2606 WCHAR wc;
2608 if (bitsUsed == 0)
2609 fmtptr++;
2610 wc = *++pstr - 'A';
2611 if (wc > NUM_ALPHA_CHARS)
2613 wc += 'A' - 'a';
2614 if (wc > NUM_ALPHA_CHARS)
2616 wc += 'a' - '0' + NUM_ALPHA_CHARS;
2617 if (wc > CHARMASK)
2619 WARN("invalid character (%d)\n", *pstr);
2620 goto end;
2624 *fmtptr |= wc << bitsUsed;
2625 bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK);
2626 if (bitsStored < BITS_IN_CHARMASK)
2628 wc >>= BITS_PER_BYTE - bitsUsed;
2629 if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE)
2631 if (wc != 0)
2633 WARN("extra bits\n");
2634 goto end;
2636 break;
2638 fmtptr++;
2639 *fmtptr |= (BYTE)wc;
2642 hr = S_OK;
2644 end:
2645 return hr;