-Wpointer-sign fixes for gcc 4.0.
[wine/multimedia.git] / dlls / ole32 / stg_prop.c
blobfe6d1fb453c496b735c7d113bae6bc343a4a20a5
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., 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:
34 * - I don't honor the maximum property set size.
35 * - Certain bogus files could result in reading past the end of a buffer.
36 * - Mac-generated files won't be read correctly, even if they're little
37 * endian, because I disregard whether the generator was a Mac. This means
38 * strings will probably be munged (as I don't understand Mac scripts.)
39 * - Not all PROPVARIANT types are supported.
40 * - User defined properties are not supported, see comment in
41 * PropertyStorage_ReadFromStream
42 * - IPropertyStorage::Enum is unimplemented
45 #include <assert.h>
46 #include <stdarg.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
51 #define COBJMACROS
52 #define NONAMELESSUNION
53 #define NONAMELESSSTRUCT
55 #include "windef.h"
56 #include "winbase.h"
57 #include "winnls.h"
58 #include "winuser.h"
59 #include "wine/unicode.h"
60 #include "wine/debug.h"
61 #include "dictionary.h"
62 #include "storage32.h"
64 WINE_DEFAULT_DEBUG_CHANNEL(storage);
66 #define _IPropertySetStorage_Offset ((int)(&(((StorageImpl*)0)->base.pssVtbl)))
67 #define _ICOM_THIS_From_IPropertySetStorage(class, name) \
68 class* This = (class*)(((char*)name)-_IPropertySetStorage_Offset)
70 /* These are documented in MSDN, e.g.
71 * http://msdn.microsoft.com/library/en-us/stg/stg/property_set_header.asp
72 * http://msdn.microsoft.com/library/library/en-us/stg/stg/section.asp
73 * but they don't seem to be in any header file.
75 #define PROPSETHDR_BYTEORDER_MAGIC 0xfffe
76 #define PROPSETHDR_OSVER_KIND_WIN16 0
77 #define PROPSETHDR_OSVER_KIND_MAC 1
78 #define PROPSETHDR_OSVER_KIND_WIN32 2
80 #define CP_UNICODE 1200
82 #define MAX_VERSION_0_PROP_NAME_LENGTH 256
84 /* The format version (and what it implies) is described here:
85 * http://msdn.microsoft.com/library/en-us/stg/stg/format_version.asp
87 typedef struct tagPROPERTYSETHEADER
89 WORD wByteOrder; /* always 0xfffe */
90 WORD wFormat; /* can be zero or one */
91 DWORD dwOSVer; /* OS version of originating system */
92 CLSID clsid; /* application CLSID */
93 DWORD reserved; /* always 1 */
94 } PROPERTYSETHEADER;
96 typedef struct tagFORMATIDOFFSET
98 FMTID fmtid;
99 DWORD dwOffset; /* from beginning of stream */
100 } FORMATIDOFFSET;
102 typedef struct tagPROPERTYSECTIONHEADER
104 DWORD cbSection;
105 DWORD cProperties;
106 } PROPERTYSECTIONHEADER;
108 typedef struct tagPROPERTYIDOFFSET
110 DWORD propid;
111 DWORD dwOffset; /* from beginning of section */
112 } PROPERTYIDOFFSET;
114 struct tagPropertyStorage_impl;
116 /* Initializes the property storage from the stream (and undoes any uncommitted
117 * changes in the process.) Returns an error if there is an error reading or
118 * if the stream format doesn't match what's expected.
120 static HRESULT PropertyStorage_ReadFromStream(struct tagPropertyStorage_impl *);
122 static HRESULT PropertyStorage_WriteToStream(struct tagPropertyStorage_impl *);
124 /* Creates the dictionaries used by the property storage. If successful, all
125 * the dictionaries have been created. If failed, none has been. (This makes
126 * it a bit easier to deal with destroying them.)
128 static HRESULT PropertyStorage_CreateDictionaries(
129 struct tagPropertyStorage_impl *);
131 static void PropertyStorage_DestroyDictionaries(
132 struct tagPropertyStorage_impl *);
134 /* Copies from propvar to prop. If propvar's type is VT_LPSTR, copies the
135 * string using PropertyStorage_StringCopy.
137 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
138 const PROPVARIANT *propvar, LCID targetCP, LCID srcCP);
140 /* Copies the string src, which is encoded using code page srcCP, and returns
141 * it in *dst, in the code page specified by targetCP. The returned string is
142 * allocated using CoTaskMemAlloc.
143 * If srcCP is CP_UNICODE, src is in fact an LPCWSTR. Similarly, if targetCP
144 * is CP_UNICODE, the returned string is in fact an LPWSTR.
145 * Returns S_OK on success, something else on failure.
147 static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
148 LCID targetCP);
150 static const IPropertyStorageVtbl IPropertyStorage_Vtbl;
152 /***********************************************************************
153 * Implementation of IPropertyStorage
155 typedef struct tagPropertyStorage_impl
157 const IPropertyStorageVtbl *vtbl;
158 LONG ref;
159 CRITICAL_SECTION cs;
160 IStream *stm;
161 BOOL dirty;
162 FMTID fmtid;
163 CLSID clsid;
164 WORD format;
165 DWORD originatorOS;
166 DWORD grfFlags;
167 DWORD grfMode;
168 UINT codePage;
169 LCID locale;
170 PROPID highestProp;
171 struct dictionary *name_to_propid;
172 struct dictionary *propid_to_name;
173 struct dictionary *propid_to_prop;
174 } PropertyStorage_impl;
176 /************************************************************************
177 * IPropertyStorage_fnQueryInterface (IPropertyStorage)
179 static HRESULT WINAPI IPropertyStorage_fnQueryInterface(
180 IPropertyStorage *iface,
181 REFIID riid,
182 void** ppvObject)
184 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
186 if ( (This==0) || (ppvObject==0) )
187 return E_INVALIDARG;
189 *ppvObject = 0;
191 if (IsEqualGUID(&IID_IUnknown, riid) ||
192 IsEqualGUID(&IID_IPropertyStorage, riid))
194 IPropertyStorage_AddRef(iface);
195 *ppvObject = (IPropertyStorage*)iface;
196 return S_OK;
199 return E_NOINTERFACE;
202 /************************************************************************
203 * IPropertyStorage_fnAddRef (IPropertyStorage)
205 static ULONG WINAPI IPropertyStorage_fnAddRef(
206 IPropertyStorage *iface)
208 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
209 return InterlockedIncrement(&This->ref);
212 /************************************************************************
213 * IPropertyStorage_fnRelease (IPropertyStorage)
215 static ULONG WINAPI IPropertyStorage_fnRelease(
216 IPropertyStorage *iface)
218 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
219 ULONG ref;
221 ref = InterlockedDecrement(&This->ref);
222 if (ref == 0)
224 TRACE("Destroying %p\n", This);
225 if (This->dirty)
226 IPropertyStorage_Commit(iface, STGC_DEFAULT);
227 IStream_Release(This->stm);
228 DeleteCriticalSection(&This->cs);
229 PropertyStorage_DestroyDictionaries(This);
230 HeapFree(GetProcessHeap(), 0, This);
232 return ref;
235 static PROPVARIANT *PropertyStorage_FindProperty(PropertyStorage_impl *This,
236 DWORD propid)
238 PROPVARIANT *ret = NULL;
240 assert(This);
241 dictionary_find(This->propid_to_prop, (void *)propid, (void **)&ret);
242 TRACE("returning %p\n", ret);
243 return ret;
246 /* Returns NULL if name is NULL. */
247 static PROPVARIANT *PropertyStorage_FindPropertyByName(
248 PropertyStorage_impl *This, LPCWSTR name)
250 PROPVARIANT *ret = NULL;
251 PROPID propid;
253 assert(This);
254 if (!name)
255 return NULL;
256 if (This->codePage == CP_UNICODE)
258 if (dictionary_find(This->name_to_propid, name, (void **)&propid))
259 ret = PropertyStorage_FindProperty(This, (PROPID)propid);
261 else
263 LPSTR ansiName;
264 HRESULT hr = PropertyStorage_StringCopy((LPCSTR)name, CP_UNICODE,
265 &ansiName, This->codePage);
267 if (SUCCEEDED(hr))
269 if (dictionary_find(This->name_to_propid, ansiName,
270 (void **)&propid))
271 ret = PropertyStorage_FindProperty(This, (PROPID)propid);
272 CoTaskMemFree(ansiName);
275 TRACE("returning %p\n", ret);
276 return ret;
279 static LPWSTR PropertyStorage_FindPropertyNameById(PropertyStorage_impl *This,
280 DWORD propid)
282 LPWSTR ret = NULL;
284 assert(This);
285 dictionary_find(This->propid_to_name, (void *)propid, (void **)&ret);
286 TRACE("returning %p\n", ret);
287 return ret;
290 /************************************************************************
291 * IPropertyStorage_fnReadMultiple (IPropertyStorage)
293 static HRESULT WINAPI IPropertyStorage_fnReadMultiple(
294 IPropertyStorage* iface,
295 ULONG cpspec,
296 const PROPSPEC rgpspec[],
297 PROPVARIANT rgpropvar[])
299 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
300 HRESULT hr = S_FALSE;
301 ULONG i;
303 TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
304 if (!This)
305 return E_INVALIDARG;
306 if (cpspec && (!rgpspec || !rgpropvar))
307 return E_INVALIDARG;
308 EnterCriticalSection(&This->cs);
309 for (i = 0; i < cpspec; i++)
311 PropVariantInit(&rgpropvar[i]);
312 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
314 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
315 rgpspec[i].u.lpwstr);
317 if (prop)
318 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop, GetACP(),
319 This->codePage);
321 else
323 switch (rgpspec[i].u.propid)
325 case PID_CODEPAGE:
326 rgpropvar[i].vt = VT_I2;
327 rgpropvar[i].u.iVal = This->codePage;
328 break;
329 case PID_LOCALE:
330 rgpropvar[i].vt = VT_I4;
331 rgpropvar[i].u.lVal = This->locale;
332 break;
333 default:
335 PROPVARIANT *prop = PropertyStorage_FindProperty(This,
336 rgpspec[i].u.propid);
338 if (prop)
339 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop,
340 GetACP(), This->codePage);
345 LeaveCriticalSection(&This->cs);
346 return hr;
349 static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
350 LCID dstCP)
352 HRESULT hr = S_OK;
353 int len;
355 TRACE("%s, %p, %ld, %ld\n",
356 srcCP == CP_UNICODE ? debugstr_w((LPCWSTR)src) : debugstr_a(src), dst,
357 dstCP, srcCP);
358 assert(src);
359 assert(dst);
360 *dst = NULL;
361 if (dstCP == srcCP)
363 size_t len;
365 if (dstCP == CP_UNICODE)
366 len = (strlenW((LPCWSTR)src) + 1) * sizeof(WCHAR);
367 else
368 len = strlen(src) + 1;
369 *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
370 if (!*dst)
371 hr = STG_E_INSUFFICIENTMEMORY;
372 else
373 memcpy(*dst, src, len);
375 else
377 if (dstCP == CP_UNICODE)
379 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
380 *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
381 if (!*dst)
382 hr = STG_E_INSUFFICIENTMEMORY;
383 else
384 MultiByteToWideChar(srcCP, 0, src, -1, (LPWSTR)*dst, len);
386 else
388 LPWSTR wideStr;
390 if (srcCP == CP_UNICODE)
391 wideStr = (LPWSTR)src;
392 else
394 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
395 wideStr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
396 if (wideStr)
397 MultiByteToWideChar(srcCP, 0, src, -1, wideStr, len);
398 else
399 hr = STG_E_INSUFFICIENTMEMORY;
401 if (SUCCEEDED(hr))
403 len = WideCharToMultiByte(dstCP, 0, wideStr, -1, NULL, 0,
404 NULL, NULL);
405 *dst = CoTaskMemAlloc(len);
406 if (!*dst)
407 hr = STG_E_INSUFFICIENTMEMORY;
408 else
410 BOOL defCharUsed = FALSE;
412 if (WideCharToMultiByte(dstCP, 0, wideStr, -1, *dst, len,
413 NULL, &defCharUsed) == 0 || defCharUsed)
415 CoTaskMemFree(*dst);
416 *dst = NULL;
417 hr = HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION);
421 if (wideStr != (LPWSTR)src)
422 HeapFree(GetProcessHeap(), 0, wideStr);
425 TRACE("returning 0x%08lx (%s)\n", hr,
426 dstCP == CP_UNICODE ? debugstr_w((LPCWSTR)*dst) : debugstr_a(*dst));
427 return hr;
430 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
431 const PROPVARIANT *propvar, LCID targetCP, LCID srcCP)
433 HRESULT hr = S_OK;
435 assert(prop);
436 assert(propvar);
437 if (propvar->vt == VT_LPSTR)
439 hr = PropertyStorage_StringCopy(propvar->u.pszVal, srcCP,
440 &prop->u.pszVal, targetCP);
441 if (SUCCEEDED(hr))
442 prop->vt = VT_LPSTR;
444 else
445 PropVariantCopy(prop, propvar);
446 return hr;
449 /* Stores the property with id propid and value propvar into this property
450 * storage. lcid is ignored if propvar's type is not VT_LPSTR. If propvar's
451 * type is VT_LPSTR, converts the string using lcid as the source code page
452 * and This->codePage as the target code page before storing.
453 * As a side effect, may change This->format to 1 if the type of propvar is
454 * a version 1-only property.
456 static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
457 PROPID propid, const PROPVARIANT *propvar, LCID lcid)
459 HRESULT hr = S_OK;
460 PROPVARIANT *prop = PropertyStorage_FindProperty(This, propid);
462 assert(This);
463 assert(propvar);
464 if (propvar->vt & VT_BYREF || propvar->vt & VT_ARRAY)
465 This->format = 1;
466 switch (propvar->vt)
468 case VT_DECIMAL:
469 case VT_I1:
470 case VT_INT:
471 case VT_UINT:
472 case VT_VECTOR|VT_I1:
473 This->format = 1;
475 TRACE("Setting 0x%08lx to type %d\n", propid, propvar->vt);
476 if (prop)
478 PropVariantClear(prop);
479 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
480 lcid);
482 else
484 prop = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
485 sizeof(PROPVARIANT));
486 if (prop)
488 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
489 lcid);
490 if (SUCCEEDED(hr))
492 dictionary_insert(This->propid_to_prop, (void *)propid, prop);
493 if (propid > This->highestProp)
494 This->highestProp = propid;
496 else
497 HeapFree(GetProcessHeap(), 0, prop);
499 else
500 hr = STG_E_INSUFFICIENTMEMORY;
502 return hr;
505 /* Adds the name srcName to the name dictionaries, mapped to property ID id.
506 * srcName is encoded in code page cp, and is converted to This->codePage.
507 * If cp is CP_UNICODE, srcName is actually a unicode string.
508 * As a side effect, may change This->format to 1 if srcName is too long for
509 * a version 0 property storage.
510 * Doesn't validate id.
512 static HRESULT PropertyStorage_StoreNameWithId(PropertyStorage_impl *This,
513 LPCSTR srcName, LCID cp, PROPID id)
515 LPSTR name;
516 HRESULT hr;
518 assert(srcName);
520 hr = PropertyStorage_StringCopy((LPCSTR)srcName, cp, &name, This->codePage);
521 if (SUCCEEDED(hr))
523 if (This->codePage == CP_UNICODE)
525 if (lstrlenW((LPWSTR)name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
526 This->format = 1;
528 else
530 if (strlen(name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
531 This->format = 1;
533 TRACE("Adding prop name %s, propid %ld\n",
534 This->codePage == CP_UNICODE ? debugstr_w((LPCWSTR)name) :
535 debugstr_a(name), id);
536 dictionary_insert(This->name_to_propid, name, (void *)id);
537 dictionary_insert(This->propid_to_name, (void *)id, name);
539 return hr;
542 /************************************************************************
543 * IPropertyStorage_fnWriteMultiple (IPropertyStorage)
545 static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
546 IPropertyStorage* iface,
547 ULONG cpspec,
548 const PROPSPEC rgpspec[],
549 const PROPVARIANT rgpropvar[],
550 PROPID propidNameFirst)
552 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
553 HRESULT hr = S_OK;
554 ULONG i;
556 TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
557 if (!This)
558 return E_INVALIDARG;
559 if (cpspec && (!rgpspec || !rgpropvar))
560 return E_INVALIDARG;
561 if (!(This->grfMode & STGM_READWRITE))
562 return STG_E_ACCESSDENIED;
563 EnterCriticalSection(&This->cs);
564 This->dirty = TRUE;
565 This->originatorOS = (DWORD)MAKELONG(LOWORD(GetVersion()),
566 PROPSETHDR_OSVER_KIND_WIN32) ;
567 for (i = 0; i < cpspec; i++)
569 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
571 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
572 rgpspec[i].u.lpwstr);
574 if (prop)
575 PropVariantCopy(prop, &rgpropvar[i]);
576 else
578 /* Note that I don't do the special cases here that I do below,
579 * because naming the special PIDs isn't supported.
581 if (propidNameFirst < PID_FIRST_USABLE ||
582 propidNameFirst >= PID_MIN_READONLY)
583 hr = STG_E_INVALIDPARAMETER;
584 else
586 PROPID nextId = max(propidNameFirst, This->highestProp + 1);
588 hr = PropertyStorage_StoreNameWithId(This,
589 (LPCSTR)rgpspec[i].u.lpwstr, CP_UNICODE, nextId);
590 if (SUCCEEDED(hr))
591 hr = PropertyStorage_StorePropWithId(This, nextId,
592 &rgpropvar[i], GetACP());
596 else
598 switch (rgpspec[i].u.propid)
600 case PID_DICTIONARY:
601 /* Can't set the dictionary */
602 hr = STG_E_INVALIDPARAMETER;
603 break;
604 case PID_CODEPAGE:
605 /* Can only set the code page if nothing else has been set */
606 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
607 rgpropvar[i].vt == VT_I2)
609 This->codePage = rgpropvar[i].u.iVal;
610 if (This->codePage == CP_UNICODE)
611 This->grfFlags &= ~PROPSETFLAG_ANSI;
612 else
613 This->grfFlags |= PROPSETFLAG_ANSI;
615 else
616 hr = STG_E_INVALIDPARAMETER;
617 break;
618 case PID_LOCALE:
619 /* Can only set the locale if nothing else has been set */
620 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
621 rgpropvar[i].vt == VT_I4)
622 This->locale = rgpropvar[i].u.lVal;
623 else
624 hr = STG_E_INVALIDPARAMETER;
625 break;
626 case PID_ILLEGAL:
627 /* silently ignore like MSDN says */
628 break;
629 default:
630 if (rgpspec[i].u.propid >= PID_MIN_READONLY)
631 hr = STG_E_INVALIDPARAMETER;
632 else
633 hr = PropertyStorage_StorePropWithId(This,
634 rgpspec[i].u.propid, &rgpropvar[i], GetACP());
638 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
639 IPropertyStorage_Commit(iface, STGC_DEFAULT);
640 LeaveCriticalSection(&This->cs);
641 return hr;
644 /************************************************************************
645 * IPropertyStorage_fnDeleteMultiple (IPropertyStorage)
647 static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
648 IPropertyStorage* iface,
649 ULONG cpspec,
650 const PROPSPEC rgpspec[])
652 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
653 ULONG i;
654 HRESULT hr;
656 TRACE("(%p, %ld, %p)\n", iface, cpspec, rgpspec);
657 if (!This)
658 return E_INVALIDARG;
659 if (cpspec && !rgpspec)
660 return E_INVALIDARG;
661 if (!(This->grfMode & STGM_READWRITE))
662 return STG_E_ACCESSDENIED;
663 hr = S_OK;
664 EnterCriticalSection(&This->cs);
665 This->dirty = TRUE;
666 for (i = 0; i < cpspec; i++)
668 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
670 PROPID propid;
672 if (dictionary_find(This->name_to_propid,
673 (void *)rgpspec[i].u.lpwstr, (void **)&propid))
674 dictionary_remove(This->propid_to_prop, (void *)propid);
676 else
678 if (rgpspec[i].u.propid >= PID_FIRST_USABLE &&
679 rgpspec[i].u.propid < PID_MIN_READONLY)
680 dictionary_remove(This->propid_to_prop,
681 (void *)rgpspec[i].u.propid);
682 else
683 hr = STG_E_INVALIDPARAMETER;
686 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
687 IPropertyStorage_Commit(iface, STGC_DEFAULT);
688 LeaveCriticalSection(&This->cs);
689 return hr;
692 /************************************************************************
693 * IPropertyStorage_fnReadPropertyNames (IPropertyStorage)
695 static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames(
696 IPropertyStorage* iface,
697 ULONG cpropid,
698 const PROPID rgpropid[],
699 LPOLESTR rglpwstrName[])
701 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
702 ULONG i;
703 HRESULT hr = S_FALSE;
705 TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
706 if (!This)
707 return E_INVALIDARG;
708 if (cpropid && (!rgpropid || !rglpwstrName))
709 return E_INVALIDARG;
710 EnterCriticalSection(&This->cs);
711 for (i = 0; i < cpropid && SUCCEEDED(hr); i++)
713 LPWSTR name = PropertyStorage_FindPropertyNameById(This, rgpropid[i]);
715 if (name)
717 size_t len = lstrlenW(name);
719 hr = S_OK;
720 rglpwstrName[i] = CoTaskMemAlloc((len + 1) * sizeof(WCHAR));
721 if (rglpwstrName)
722 memcpy(rglpwstrName, name, (len + 1) * sizeof(WCHAR));
723 else
724 hr = STG_E_INSUFFICIENTMEMORY;
726 else
727 rglpwstrName[i] = NULL;
729 LeaveCriticalSection(&This->cs);
730 return hr;
733 /************************************************************************
734 * IPropertyStorage_fnWritePropertyNames (IPropertyStorage)
736 static HRESULT WINAPI IPropertyStorage_fnWritePropertyNames(
737 IPropertyStorage* iface,
738 ULONG cpropid,
739 const PROPID rgpropid[],
740 const LPOLESTR rglpwstrName[])
742 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
743 ULONG i;
744 HRESULT hr;
746 TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
747 if (!This)
748 return E_INVALIDARG;
749 if (cpropid && (!rgpropid || !rglpwstrName))
750 return E_INVALIDARG;
751 if (!(This->grfMode & STGM_READWRITE))
752 return STG_E_ACCESSDENIED;
753 hr = S_OK;
754 EnterCriticalSection(&This->cs);
755 This->dirty = TRUE;
756 for (i = 0; SUCCEEDED(hr) && i < cpropid; i++)
758 if (rgpropid[i] != PID_ILLEGAL)
759 hr = PropertyStorage_StoreNameWithId(This, (LPCSTR)rglpwstrName[i],
760 CP_UNICODE, rgpropid[i]);
762 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
763 IPropertyStorage_Commit(iface, STGC_DEFAULT);
764 LeaveCriticalSection(&This->cs);
765 return hr;
768 /************************************************************************
769 * IPropertyStorage_fnDeletePropertyNames (IPropertyStorage)
771 static HRESULT WINAPI IPropertyStorage_fnDeletePropertyNames(
772 IPropertyStorage* iface,
773 ULONG cpropid,
774 const PROPID rgpropid[])
776 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
777 ULONG i;
778 HRESULT hr;
780 TRACE("(%p, %ld, %p)\n", iface, cpropid, rgpropid);
781 if (!This)
782 return E_INVALIDARG;
783 if (cpropid && !rgpropid)
784 return E_INVALIDARG;
785 if (!(This->grfMode & STGM_READWRITE))
786 return STG_E_ACCESSDENIED;
787 hr = S_OK;
788 EnterCriticalSection(&This->cs);
789 This->dirty = TRUE;
790 for (i = 0; i < cpropid; i++)
792 LPWSTR name = NULL;
794 if (dictionary_find(This->propid_to_name, (void *)rgpropid[i],
795 (void **)&name))
797 dictionary_remove(This->propid_to_name, (void *)rgpropid[i]);
798 dictionary_remove(This->name_to_propid, name);
801 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
802 IPropertyStorage_Commit(iface, STGC_DEFAULT);
803 LeaveCriticalSection(&This->cs);
804 return hr;
807 /************************************************************************
808 * IPropertyStorage_fnCommit (IPropertyStorage)
810 static HRESULT WINAPI IPropertyStorage_fnCommit(
811 IPropertyStorage* iface,
812 DWORD grfCommitFlags)
814 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
815 HRESULT hr;
817 TRACE("(%p, 0x%08lx)\n", iface, grfCommitFlags);
818 if (!This)
819 return E_INVALIDARG;
820 if (!(This->grfMode & STGM_READWRITE))
821 return STG_E_ACCESSDENIED;
822 EnterCriticalSection(&This->cs);
823 if (This->dirty)
824 hr = PropertyStorage_WriteToStream(This);
825 else
826 hr = S_OK;
827 LeaveCriticalSection(&This->cs);
828 return hr;
831 /************************************************************************
832 * IPropertyStorage_fnRevert (IPropertyStorage)
834 static HRESULT WINAPI IPropertyStorage_fnRevert(
835 IPropertyStorage* iface)
837 HRESULT hr;
838 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
840 TRACE("%p\n", iface);
841 if (!This)
842 return E_INVALIDARG;
844 EnterCriticalSection(&This->cs);
845 if (This->dirty)
847 PropertyStorage_DestroyDictionaries(This);
848 hr = PropertyStorage_CreateDictionaries(This);
849 if (SUCCEEDED(hr))
850 hr = PropertyStorage_ReadFromStream(This);
852 else
853 hr = S_OK;
854 LeaveCriticalSection(&This->cs);
855 return hr;
858 /************************************************************************
859 * IPropertyStorage_fnEnum (IPropertyStorage)
861 static HRESULT WINAPI IPropertyStorage_fnEnum(
862 IPropertyStorage* iface,
863 IEnumSTATPROPSTG** ppenum)
865 FIXME("\n");
866 return E_NOTIMPL;
869 /************************************************************************
870 * IPropertyStorage_fnSetTimes (IPropertyStorage)
872 static HRESULT WINAPI IPropertyStorage_fnSetTimes(
873 IPropertyStorage* iface,
874 const FILETIME* pctime,
875 const FILETIME* patime,
876 const FILETIME* pmtime)
878 FIXME("\n");
879 return E_NOTIMPL;
882 /************************************************************************
883 * IPropertyStorage_fnSetClass (IPropertyStorage)
885 static HRESULT WINAPI IPropertyStorage_fnSetClass(
886 IPropertyStorage* iface,
887 REFCLSID clsid)
889 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
891 TRACE("%p, %s\n", iface, debugstr_guid(clsid));
892 if (!This || !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);
915 if (!This || !statpsstg)
916 return E_INVALIDARG;
918 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
919 if (SUCCEEDED(hr))
921 memcpy(&statpsstg->fmtid, &This->fmtid, sizeof(statpsstg->fmtid));
922 memcpy(&statpsstg->clsid, &This->clsid, sizeof(statpsstg->clsid));
923 statpsstg->grfFlags = This->grfFlags;
924 memcpy(&statpsstg->mtime, &stat.mtime, sizeof(statpsstg->mtime));
925 memcpy(&statpsstg->ctime, &stat.ctime, sizeof(statpsstg->ctime));
926 memcpy(&statpsstg->atime, &stat.atime, sizeof(statpsstg->atime));
927 statpsstg->dwOSVersion = This->originatorOS;
929 return hr;
932 static int PropertyStorage_PropNameCompare(const void *a, const void *b,
933 void *extra)
935 PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
937 if (This->codePage == CP_UNICODE)
939 TRACE("(%s, %s)\n", debugstr_w(a), debugstr_w(b));
940 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
941 return lstrcmpW((LPCWSTR)a, (LPCWSTR)b);
942 else
943 return lstrcmpiW((LPCWSTR)a, (LPCWSTR)b);
945 else
947 TRACE("(%s, %s)\n", debugstr_a(a), debugstr_a(b));
948 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
949 return lstrcmpA((LPCSTR)a, (LPCSTR)b);
950 else
951 return lstrcmpiA((LPCSTR)a, (LPCSTR)b);
955 static void PropertyStorage_PropNameDestroy(void *k, void *d, void *extra)
957 CoTaskMemFree(k);
960 static int PropertyStorage_PropCompare(const void *a, const void *b,
961 void *extra)
963 TRACE("(%ld, %ld)\n", (PROPID)a, (PROPID)b);
964 return (PROPID)a - (PROPID)b;
967 static void PropertyStorage_PropertyDestroy(void *k, void *d, void *extra)
969 PropVariantClear((PROPVARIANT *)d);
970 HeapFree(GetProcessHeap(), 0, d);
973 #ifdef WORDS_BIGENDIAN
974 /* Swaps each character in str to or from little endian; assumes the conversion
975 * is symmetric, that is, that le16toh is equivalent to htole16.
977 static void PropertyStorage_ByteSwapString(LPWSTR str, size_t len)
979 DWORD i;
981 /* Swap characters to host order.
982 * FIXME: alignment?
984 for (i = 0; i < len; i++)
985 str[i] = le16toh(str[i]);
987 #else
988 #define PropertyStorage_ByteSwapString(s, l)
989 #endif
991 /* Reads the dictionary from the memory buffer beginning at ptr. Interprets
992 * the entries according to the values of This->codePage and This->locale.
993 * FIXME: there isn't any checking whether the read property extends past the
994 * end of the buffer.
996 static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This,
997 BYTE *ptr)
999 DWORD numEntries, i;
1000 HRESULT hr = S_OK;
1002 assert(This);
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, 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, &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, &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 default:
1141 FIXME("unsupported type %d\n", prop->vt);
1142 hr = STG_E_INVALIDPARAMETER;
1144 return hr;
1147 static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
1148 PROPERTYSETHEADER *hdr)
1150 BYTE buf[sizeof(PROPERTYSETHEADER)];
1151 ULONG count = 0;
1152 HRESULT hr;
1154 assert(stm);
1155 assert(hdr);
1156 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1157 if (SUCCEEDED(hr))
1159 if (count != sizeof(buf))
1161 WARN("read %ld, expected %d\n", count, sizeof(buf));
1162 hr = STG_E_INVALIDHEADER;
1164 else
1166 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder),
1167 &hdr->wByteOrder);
1168 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat),
1169 &hdr->wFormat);
1170 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer),
1171 &hdr->dwOSVer);
1172 StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid),
1173 &hdr->clsid);
1174 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved),
1175 &hdr->reserved);
1178 TRACE("returning 0x%08lx\n", hr);
1179 return hr;
1182 static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm,
1183 FORMATIDOFFSET *fmt)
1185 BYTE buf[sizeof(FORMATIDOFFSET)];
1186 ULONG count = 0;
1187 HRESULT hr;
1189 assert(stm);
1190 assert(fmt);
1191 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1192 if (SUCCEEDED(hr))
1194 if (count != sizeof(buf))
1196 WARN("read %ld, expected %d\n", count, sizeof(buf));
1197 hr = STG_E_INVALIDHEADER;
1199 else
1201 StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid),
1202 &fmt->fmtid);
1203 StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset),
1204 &fmt->dwOffset);
1207 TRACE("returning 0x%08lx\n", hr);
1208 return hr;
1211 static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
1212 PROPERTYSECTIONHEADER *hdr)
1214 BYTE buf[sizeof(PROPERTYSECTIONHEADER)];
1215 ULONG count = 0;
1216 HRESULT hr;
1218 assert(stm);
1219 assert(hdr);
1220 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1221 if (SUCCEEDED(hr))
1223 if (count != sizeof(buf))
1225 WARN("read %ld, expected %d\n", count, sizeof(buf));
1226 hr = STG_E_INVALIDHEADER;
1228 else
1230 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1231 cbSection), &hdr->cbSection);
1232 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1233 cProperties), &hdr->cProperties);
1236 TRACE("returning 0x%08lx\n", hr);
1237 return hr;
1240 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This)
1242 PROPERTYSETHEADER hdr;
1243 FORMATIDOFFSET fmtOffset;
1244 PROPERTYSECTIONHEADER sectionHdr;
1245 LARGE_INTEGER seek;
1246 ULONG i;
1247 STATSTG stat;
1248 HRESULT hr;
1249 BYTE *buf = NULL;
1250 ULONG count = 0;
1251 DWORD dictOffset = 0;
1253 assert(This);
1254 This->dirty = FALSE;
1255 This->highestProp = 0;
1256 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
1257 if (FAILED(hr))
1258 goto end;
1259 if (stat.cbSize.u.HighPart)
1261 WARN("stream too big\n");
1262 /* maximum size varies, but it can't be this big */
1263 hr = STG_E_INVALIDHEADER;
1264 goto end;
1266 if (stat.cbSize.u.LowPart == 0)
1268 /* empty stream is okay */
1269 hr = S_OK;
1270 goto end;
1272 else if (stat.cbSize.u.LowPart < sizeof(PROPERTYSETHEADER) +
1273 sizeof(FORMATIDOFFSET))
1275 WARN("stream too small\n");
1276 hr = STG_E_INVALIDHEADER;
1277 goto end;
1279 seek.QuadPart = 0;
1280 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1281 if (FAILED(hr))
1282 goto end;
1283 hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr);
1284 /* I've only seen reserved == 1, but the article says I shouldn't disallow
1285 * higher values.
1287 if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1)
1289 WARN("bad magic in prop set header\n");
1290 hr = STG_E_INVALIDHEADER;
1291 goto end;
1293 if (hdr.wFormat != 0 && hdr.wFormat != 1)
1295 WARN("bad format version %d\n", hdr.wFormat);
1296 hr = STG_E_INVALIDHEADER;
1297 goto end;
1299 This->format = hdr.wFormat;
1300 memcpy(&This->clsid, &hdr.clsid, sizeof(This->clsid));
1301 This->originatorOS = hdr.dwOSVer;
1302 if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC)
1303 WARN("File comes from a Mac, strings will probably be screwed up\n");
1304 hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset);
1305 if (FAILED(hr))
1306 goto end;
1307 if (fmtOffset.dwOffset > stat.cbSize.u.LowPart)
1309 WARN("invalid offset %ld (stream length is %ld)\n", fmtOffset.dwOffset,
1310 stat.cbSize.u.LowPart);
1311 hr = STG_E_INVALIDHEADER;
1312 goto end;
1314 /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
1315 * follow not one, but two sections. The first is the standard properties
1316 * for the document summary information, and the second is user-defined
1317 * properties. This is the only case in which multiple sections are
1318 * allowed.
1319 * Reading the second stream isn't implemented yet.
1321 hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, &sectionHdr);
1322 if (FAILED(hr))
1323 goto end;
1324 /* The section size includes the section header, so check it */
1325 if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER))
1327 WARN("section header too small, got %ld, expected at least %d\n",
1328 sectionHdr.cbSection, sizeof(PROPERTYSECTIONHEADER));
1329 hr = STG_E_INVALIDHEADER;
1330 goto end;
1332 buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection -
1333 sizeof(PROPERTYSECTIONHEADER));
1334 if (!buf)
1336 hr = STG_E_INSUFFICIENTMEMORY;
1337 goto end;
1339 hr = IStream_Read(This->stm, buf, sectionHdr.cbSection -
1340 sizeof(PROPERTYSECTIONHEADER), &count);
1341 if (FAILED(hr))
1342 goto end;
1343 TRACE("Reading %ld properties:\n", sectionHdr.cProperties);
1344 for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
1346 PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(buf +
1347 i * sizeof(PROPERTYIDOFFSET));
1349 if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) ||
1350 idOffset->dwOffset >= sectionHdr.cbSection - sizeof(DWORD))
1351 hr = STG_E_INVALIDPOINTER;
1352 else
1354 if (idOffset->propid >= PID_FIRST_USABLE &&
1355 idOffset->propid < PID_MIN_READONLY && idOffset->propid >
1356 This->highestProp)
1357 This->highestProp = idOffset->propid;
1358 if (idOffset->propid == PID_DICTIONARY)
1360 /* Don't read the dictionary yet, its entries depend on the
1361 * code page. Just store the offset so we know to read it
1362 * later.
1364 dictOffset = idOffset->dwOffset;
1365 TRACE("Dictionary offset is %ld\n", dictOffset);
1367 else
1369 PROPVARIANT prop;
1371 if (SUCCEEDED(PropertyStorage_ReadProperty(This, &prop,
1372 buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER))))
1374 TRACE("Read property with ID 0x%08lx, type %d\n",
1375 idOffset->propid, prop.vt);
1376 switch(idOffset->propid)
1378 case PID_CODEPAGE:
1379 if (prop.vt == VT_I2)
1380 This->codePage = (UINT)prop.u.iVal;
1381 break;
1382 case PID_LOCALE:
1383 if (prop.vt == VT_I4)
1384 This->locale = (LCID)prop.u.lVal;
1385 break;
1386 case PID_BEHAVIOR:
1387 if (prop.vt == VT_I4 && prop.u.lVal)
1388 This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
1389 /* The format should already be 1, but just in case */
1390 This->format = 1;
1391 break;
1392 default:
1393 hr = PropertyStorage_StorePropWithId(This,
1394 idOffset->propid, &prop, This->codePage);
1400 if (!This->codePage)
1402 /* default to Unicode unless told not to, as specified here:
1403 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
1405 if (This->grfFlags & PROPSETFLAG_ANSI)
1406 This->codePage = GetACP();
1407 else
1408 This->codePage = CP_UNICODE;
1410 if (!This->locale)
1411 This->locale = LOCALE_SYSTEM_DEFAULT;
1412 TRACE("Code page is %d, locale is %ld\n", This->codePage, This->locale);
1413 if (dictOffset)
1414 hr = PropertyStorage_ReadDictionary(This,
1415 buf + dictOffset - sizeof(PROPERTYSECTIONHEADER));
1417 end:
1418 HeapFree(GetProcessHeap(), 0, buf);
1419 if (FAILED(hr))
1421 dictionary_destroy(This->name_to_propid);
1422 This->name_to_propid = NULL;
1423 dictionary_destroy(This->propid_to_name);
1424 This->propid_to_name = NULL;
1425 dictionary_destroy(This->propid_to_prop);
1426 This->propid_to_prop = NULL;
1428 return hr;
1431 static void PropertyStorage_MakeHeader(PropertyStorage_impl *This,
1432 PROPERTYSETHEADER *hdr)
1434 assert(This);
1435 assert(hdr);
1436 StorageUtl_WriteWord((BYTE *)&hdr->wByteOrder, 0,
1437 PROPSETHDR_BYTEORDER_MAGIC);
1438 StorageUtl_WriteWord((BYTE *)&hdr->wFormat, 0, This->format);
1439 StorageUtl_WriteDWord((BYTE *)&hdr->dwOSVer, 0, This->originatorOS);
1440 StorageUtl_WriteGUID((BYTE *)&hdr->clsid, 0, &This->clsid);
1441 StorageUtl_WriteDWord((BYTE *)&hdr->reserved, 0, 1);
1444 static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl *This,
1445 FORMATIDOFFSET *fmtOffset)
1447 assert(This);
1448 assert(fmtOffset);
1449 StorageUtl_WriteGUID((BYTE *)fmtOffset, 0, &This->fmtid);
1450 StorageUtl_WriteDWord((BYTE *)fmtOffset, offsetof(FORMATIDOFFSET, dwOffset),
1451 sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET));
1454 static void PropertyStorage_MakeSectionHdr(DWORD cbSection, DWORD numProps,
1455 PROPERTYSECTIONHEADER *hdr)
1457 assert(hdr);
1458 StorageUtl_WriteDWord((BYTE *)hdr, 0, cbSection);
1459 StorageUtl_WriteDWord((BYTE *)hdr,
1460 offsetof(PROPERTYSECTIONHEADER, cProperties), numProps);
1463 static void PropertyStorage_MakePropertyIdOffset(DWORD propid, DWORD dwOffset,
1464 PROPERTYIDOFFSET *propIdOffset)
1466 assert(propIdOffset);
1467 StorageUtl_WriteDWord((BYTE *)propIdOffset, 0, propid);
1468 StorageUtl_WriteDWord((BYTE *)propIdOffset,
1469 offsetof(PROPERTYIDOFFSET, dwOffset), dwOffset);
1472 struct DictionaryClosure
1474 HRESULT hr;
1475 DWORD bytesWritten;
1478 static BOOL PropertyStorage_DictionaryWriter(const void *key,
1479 const void *value, void *extra, void *closure)
1481 PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
1482 struct DictionaryClosure *c = (struct DictionaryClosure *)closure;
1483 DWORD propid;
1484 ULONG count;
1486 assert(key);
1487 assert(This);
1488 assert(closure);
1489 StorageUtl_WriteDWord((LPBYTE)&propid, 0, (DWORD)value);
1490 c->hr = IStream_Write(This->stm, &propid, sizeof(propid), &count);
1491 if (FAILED(c->hr))
1492 goto end;
1493 c->bytesWritten += sizeof(DWORD);
1494 if (This->codePage == CP_UNICODE)
1496 DWORD keyLen, pad = 0;
1498 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0,
1499 (lstrlenW((LPWSTR)key) + 1) * sizeof(WCHAR));
1500 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1501 if (FAILED(c->hr))
1502 goto end;
1503 c->bytesWritten += sizeof(DWORD);
1504 /* Rather than allocate a copy, I'll swap the string to little-endian
1505 * in-place, write it, then swap it back.
1507 PropertyStorage_ByteSwapString(key, keyLen);
1508 c->hr = IStream_Write(This->stm, key, keyLen, &count);
1509 PropertyStorage_ByteSwapString(key, keyLen);
1510 if (FAILED(c->hr))
1511 goto end;
1512 c->bytesWritten += keyLen;
1513 if (keyLen % sizeof(DWORD))
1515 c->hr = IStream_Write(This->stm, &pad,
1516 sizeof(DWORD) - keyLen % sizeof(DWORD), &count);
1517 if (FAILED(c->hr))
1518 goto end;
1519 c->bytesWritten += sizeof(DWORD) - keyLen % sizeof(DWORD);
1522 else
1524 DWORD keyLen;
1526 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0, strlen((LPCSTR)key) + 1);
1527 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1528 if (FAILED(c->hr))
1529 goto end;
1530 c->bytesWritten += sizeof(DWORD);
1531 c->hr = IStream_Write(This->stm, key, keyLen, &count);
1532 if (FAILED(c->hr))
1533 goto end;
1534 c->bytesWritten += keyLen;
1536 end:
1537 return SUCCEEDED(c->hr);
1540 #define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)
1542 /* Writes the dictionary to the stream. Assumes without checking that the
1543 * dictionary isn't empty.
1545 static HRESULT PropertyStorage_WriteDictionaryToStream(
1546 PropertyStorage_impl *This, DWORD *sectionOffset)
1548 HRESULT hr;
1549 LARGE_INTEGER seek;
1550 PROPERTYIDOFFSET propIdOffset;
1551 ULONG count;
1552 DWORD dwTemp;
1553 struct DictionaryClosure closure;
1555 assert(This);
1556 assert(sectionOffset);
1558 /* The dictionary's always the first property written, so seek to its
1559 * spot.
1561 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER);
1562 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1563 if (FAILED(hr))
1564 goto end;
1565 PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY, *sectionOffset,
1566 &propIdOffset);
1567 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1568 if (FAILED(hr))
1569 goto end;
1571 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1572 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1573 if (FAILED(hr))
1574 goto end;
1575 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0,
1576 dictionary_num_entries(This->name_to_propid));
1577 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1578 if (FAILED(hr))
1579 goto end;
1580 *sectionOffset += sizeof(dwTemp);
1582 closure.hr = S_OK;
1583 closure.bytesWritten = 0;
1584 dictionary_enumerate(This->name_to_propid, PropertyStorage_DictionaryWriter,
1585 &closure);
1586 hr = closure.hr;
1587 if (FAILED(hr))
1588 goto end;
1589 *sectionOffset += closure.bytesWritten;
1590 if (closure.bytesWritten % sizeof(DWORD))
1592 TRACE("adding %ld bytes of padding\n", sizeof(DWORD) -
1593 closure.bytesWritten % sizeof(DWORD));
1594 *sectionOffset += sizeof(DWORD) - closure.bytesWritten % sizeof(DWORD);
1597 end:
1598 return hr;
1601 static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This,
1602 DWORD propNum, DWORD propid, PROPVARIANT *var, DWORD *sectionOffset)
1604 HRESULT hr;
1605 LARGE_INTEGER seek;
1606 PROPERTYIDOFFSET propIdOffset;
1607 ULONG count;
1608 DWORD dwType, bytesWritten;
1610 assert(This);
1611 assert(var);
1612 assert(sectionOffset);
1614 TRACE("%p, %ld, 0x%08lx, (%d), (%ld)\n", This, propNum, propid, var->vt,
1615 *sectionOffset);
1617 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) +
1618 propNum * sizeof(PROPERTYIDOFFSET);
1619 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1620 if (FAILED(hr))
1621 goto end;
1622 PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset);
1623 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1624 if (FAILED(hr))
1625 goto end;
1627 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1628 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1629 if (FAILED(hr))
1630 goto end;
1631 StorageUtl_WriteDWord((LPBYTE)&dwType, 0, var->vt);
1632 hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count);
1633 if (FAILED(hr))
1634 goto end;
1635 *sectionOffset += sizeof(dwType);
1637 switch (var->vt)
1639 case VT_EMPTY:
1640 case VT_NULL:
1641 bytesWritten = 0;
1642 break;
1643 case VT_I1:
1644 case VT_UI1:
1645 hr = IStream_Write(This->stm, &var->u.cVal, sizeof(var->u.cVal),
1646 &count);
1647 bytesWritten = count;
1648 break;
1649 case VT_I2:
1650 case VT_UI2:
1652 WORD wTemp;
1654 StorageUtl_WriteWord((LPBYTE)&wTemp, 0, var->u.iVal);
1655 hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count);
1656 bytesWritten = count;
1657 break;
1659 case VT_I4:
1660 case VT_UI4:
1662 DWORD dwTemp;
1664 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, var->u.lVal);
1665 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1666 bytesWritten = count;
1667 break;
1669 case VT_LPSTR:
1671 DWORD len, dwTemp;
1673 if (This->codePage == CP_UNICODE)
1674 len = (lstrlenW(var->u.pwszVal) + 1) * sizeof(WCHAR);
1675 else
1676 len = lstrlenA(var->u.pszVal) + 1;
1677 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1678 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1679 if (FAILED(hr))
1680 goto end;
1681 hr = IStream_Write(This->stm, var->u.pszVal, len, &count);
1682 bytesWritten = count + sizeof(DWORD);
1683 break;
1685 case VT_LPWSTR:
1687 DWORD len = lstrlenW(var->u.pwszVal) + 1, dwTemp;
1689 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1690 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1691 if (FAILED(hr))
1692 goto end;
1693 hr = IStream_Write(This->stm, var->u.pwszVal, len * sizeof(WCHAR),
1694 &count);
1695 bytesWritten = count + sizeof(DWORD);
1696 break;
1698 case VT_FILETIME:
1700 FILETIME temp;
1702 StorageUtl_WriteULargeInteger((BYTE *)&temp, 0,
1703 (ULARGE_INTEGER *)&var->u.filetime);
1704 hr = IStream_Write(This->stm, &temp, sizeof(FILETIME), &count);
1705 bytesWritten = count;
1706 break;
1708 default:
1709 FIXME("unsupported type: %d\n", var->vt);
1710 return STG_E_INVALIDPARAMETER;
1713 if (SUCCEEDED(hr))
1715 *sectionOffset += bytesWritten;
1716 if (bytesWritten % sizeof(DWORD))
1718 TRACE("adding %ld bytes of padding\n", sizeof(DWORD) -
1719 bytesWritten % sizeof(DWORD));
1720 *sectionOffset += sizeof(DWORD) - bytesWritten % sizeof(DWORD);
1724 end:
1725 return hr;
1728 struct PropertyClosure
1730 HRESULT hr;
1731 DWORD propNum;
1732 DWORD *sectionOffset;
1735 static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value,
1736 void *extra, void *closure)
1738 PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
1739 struct PropertyClosure *c = (struct PropertyClosure *)closure;
1741 assert(key);
1742 assert(value);
1743 assert(extra);
1744 assert(closure);
1745 c->hr = PropertyStorage_WritePropertyToStream(This,
1746 c->propNum++, (DWORD)key, (PROPVARIANT *)value, c->sectionOffset);
1747 return SUCCEEDED(c->hr);
1750 static HRESULT PropertyStorage_WritePropertiesToStream(
1751 PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset)
1753 struct PropertyClosure closure;
1755 assert(This);
1756 assert(sectionOffset);
1757 closure.hr = S_OK;
1758 closure.propNum = startingPropNum;
1759 closure.sectionOffset = sectionOffset;
1760 dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter,
1761 &closure);
1762 return closure.hr;
1765 static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This)
1767 HRESULT hr;
1768 ULONG count = 0;
1769 LARGE_INTEGER seek = { {0} };
1770 PROPERTYSETHEADER hdr;
1771 FORMATIDOFFSET fmtOffset;
1773 assert(This);
1774 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1775 if (FAILED(hr))
1776 goto end;
1777 PropertyStorage_MakeHeader(This, &hdr);
1778 hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count);
1779 if (FAILED(hr))
1780 goto end;
1781 if (count != sizeof(hdr))
1783 hr = STG_E_WRITEFAULT;
1784 goto end;
1787 PropertyStorage_MakeFmtIdOffset(This, &fmtOffset);
1788 hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count);
1789 if (FAILED(hr))
1790 goto end;
1791 if (count != sizeof(fmtOffset))
1793 hr = STG_E_WRITEFAULT;
1794 goto end;
1796 hr = S_OK;
1798 end:
1799 return hr;
1802 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This)
1804 PROPERTYSECTIONHEADER sectionHdr;
1805 HRESULT hr;
1806 ULONG count;
1807 LARGE_INTEGER seek;
1808 DWORD numProps, prop, sectionOffset, dwTemp;
1809 PROPVARIANT var;
1811 assert(This);
1813 PropertyStorage_WriteHeadersToStream(This);
1815 /* Count properties. Always at least one property, the code page */
1816 numProps = 1;
1817 if (dictionary_num_entries(This->name_to_propid))
1818 numProps++;
1819 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1820 numProps++;
1821 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1822 numProps++;
1823 numProps += dictionary_num_entries(This->propid_to_prop);
1825 /* Write section header with 0 bytes right now, I'll adjust it after
1826 * writing properties.
1828 PropertyStorage_MakeSectionHdr(0, numProps, &sectionHdr);
1829 seek.QuadPart = SECTIONHEADER_OFFSET;
1830 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1831 if (FAILED(hr))
1832 goto end;
1833 hr = IStream_Write(This->stm, &sectionHdr, sizeof(sectionHdr), &count);
1834 if (FAILED(hr))
1835 goto end;
1837 prop = 0;
1838 sectionOffset = sizeof(PROPERTYSECTIONHEADER) +
1839 numProps * sizeof(PROPERTYIDOFFSET);
1841 if (dictionary_num_entries(This->name_to_propid))
1843 prop++;
1844 hr = PropertyStorage_WriteDictionaryToStream(This, &sectionOffset);
1845 if (FAILED(hr))
1846 goto end;
1849 PropVariantInit(&var);
1851 var.vt = VT_I2;
1852 var.u.iVal = This->codePage;
1853 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE,
1854 &var, &sectionOffset);
1855 if (FAILED(hr))
1856 goto end;
1858 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1860 var.vt = VT_I4;
1861 var.u.lVal = This->locale;
1862 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE,
1863 &var, &sectionOffset);
1864 if (FAILED(hr))
1865 goto end;
1868 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1870 var.vt = VT_I4;
1871 var.u.lVal = 1;
1872 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR,
1873 &var, &sectionOffset);
1874 if (FAILED(hr))
1875 goto end;
1878 hr = PropertyStorage_WritePropertiesToStream(This, prop, &sectionOffset);
1879 if (FAILED(hr))
1880 goto end;
1882 /* Now write the byte count of the section */
1883 seek.QuadPart = SECTIONHEADER_OFFSET;
1884 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1885 if (FAILED(hr))
1886 goto end;
1887 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, sectionOffset);
1888 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1890 end:
1891 return hr;
1894 /***********************************************************************
1895 * PropertyStorage_Construct
1897 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This)
1899 assert(This);
1900 dictionary_destroy(This->name_to_propid);
1901 This->name_to_propid = NULL;
1902 dictionary_destroy(This->propid_to_name);
1903 This->propid_to_name = NULL;
1904 dictionary_destroy(This->propid_to_prop);
1905 This->propid_to_prop = NULL;
1908 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This)
1910 HRESULT hr = S_OK;
1912 assert(This);
1913 This->name_to_propid = dictionary_create(
1914 PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy,
1915 This);
1916 if (!This->name_to_propid)
1918 hr = STG_E_INSUFFICIENTMEMORY;
1919 goto end;
1921 This->propid_to_name = dictionary_create(PropertyStorage_PropCompare,
1922 NULL, This);
1923 if (!This->propid_to_name)
1925 hr = STG_E_INSUFFICIENTMEMORY;
1926 goto end;
1928 This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
1929 PropertyStorage_PropertyDestroy, This);
1930 if (!This->propid_to_prop)
1932 hr = STG_E_INSUFFICIENTMEMORY;
1933 goto end;
1935 end:
1936 if (FAILED(hr))
1937 PropertyStorage_DestroyDictionaries(This);
1938 return hr;
1941 static HRESULT PropertyStorage_BaseConstruct(IStream *stm,
1942 REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps)
1944 HRESULT hr = S_OK;
1946 assert(pps);
1947 assert(rfmtid);
1948 *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps);
1949 if (!*pps)
1950 return E_OUTOFMEMORY;
1952 (*pps)->vtbl = &IPropertyStorage_Vtbl;
1953 (*pps)->ref = 1;
1954 InitializeCriticalSection(&(*pps)->cs);
1955 (*pps)->stm = stm;
1956 memcpy(&(*pps)->fmtid, rfmtid, sizeof((*pps)->fmtid));
1957 (*pps)->grfMode = grfMode;
1959 hr = PropertyStorage_CreateDictionaries(*pps);
1960 if (FAILED(hr))
1962 IStream_Release(stm);
1963 DeleteCriticalSection(&(*pps)->cs);
1964 HeapFree(GetProcessHeap(), 0, *pps);
1965 *pps = NULL;
1968 return hr;
1971 static HRESULT PropertyStorage_ConstructFromStream(IStream *stm,
1972 REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps)
1974 PropertyStorage_impl *ps;
1975 HRESULT hr;
1977 assert(pps);
1978 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
1979 if (SUCCEEDED(hr))
1981 hr = PropertyStorage_ReadFromStream(ps);
1982 if (SUCCEEDED(hr))
1984 *pps = (IPropertyStorage *)ps;
1985 TRACE("PropertyStorage %p constructed\n", ps);
1986 hr = S_OK;
1988 else
1990 PropertyStorage_DestroyDictionaries(ps);
1991 HeapFree(GetProcessHeap(), 0, ps);
1994 return hr;
1997 static HRESULT PropertyStorage_ConstructEmpty(IStream *stm,
1998 REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
2000 PropertyStorage_impl *ps;
2001 HRESULT hr;
2003 assert(pps);
2004 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2005 if (SUCCEEDED(hr))
2007 ps->format = 0;
2008 ps->grfFlags = grfFlags;
2009 if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2010 ps->format = 1;
2011 /* default to Unicode unless told not to, as specified here:
2012 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2014 if (ps->grfFlags & PROPSETFLAG_ANSI)
2015 ps->codePage = GetACP();
2016 else
2017 ps->codePage = CP_UNICODE;
2018 ps->locale = LOCALE_SYSTEM_DEFAULT;
2019 TRACE("Code page is %d, locale is %ld\n", ps->codePage, ps->locale);
2020 *pps = (IPropertyStorage *)ps;
2021 TRACE("PropertyStorage %p constructed\n", ps);
2022 hr = S_OK;
2024 return hr;
2028 /***********************************************************************
2029 * Implementation of IPropertySetStorage
2032 /************************************************************************
2033 * IPropertySetStorage_fnQueryInterface (IUnknown)
2035 * This method forwards to the common QueryInterface implementation
2037 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
2038 IPropertySetStorage *ppstg,
2039 REFIID riid,
2040 void** ppvObject)
2042 _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
2043 return IStorage_QueryInterface( (IStorage*)This, riid, ppvObject );
2046 /************************************************************************
2047 * IPropertySetStorage_fnAddRef (IUnknown)
2049 * This method forwards to the common AddRef implementation
2051 static ULONG WINAPI IPropertySetStorage_fnAddRef(
2052 IPropertySetStorage *ppstg)
2054 _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
2055 return IStorage_AddRef( (IStorage*)This );
2058 /************************************************************************
2059 * IPropertySetStorage_fnRelease (IUnknown)
2061 * This method forwards to the common Release implementation
2063 static ULONG WINAPI IPropertySetStorage_fnRelease(
2064 IPropertySetStorage *ppstg)
2066 _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
2067 return IStorage_Release( (IStorage*)This );
2070 /************************************************************************
2071 * IPropertySetStorage_fnCreate (IPropertySetStorage)
2073 static HRESULT WINAPI IPropertySetStorage_fnCreate(
2074 IPropertySetStorage *ppstg,
2075 REFFMTID rfmtid,
2076 const CLSID* pclsid,
2077 DWORD grfFlags,
2078 DWORD grfMode,
2079 IPropertyStorage** ppprstg)
2081 _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
2082 WCHAR name[CCH_MAX_PROPSTG_NAME];
2083 IStream *stm = NULL;
2084 HRESULT r;
2086 TRACE("%p %s %08lx %08lx %p\n", This, debugstr_guid(rfmtid), grfFlags,
2087 grfMode, ppprstg);
2089 /* be picky */
2090 if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
2092 r = STG_E_INVALIDFLAG;
2093 goto end;
2096 if (!rfmtid)
2098 r = E_INVALIDARG;
2099 goto end;
2102 /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
2103 * storage, not a stream. For now, disallow it.
2105 if (grfFlags & PROPSETFLAG_NONSIMPLE)
2107 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2108 r = STG_E_INVALIDFLAG;
2109 goto end;
2112 r = FmtIdToPropStgName(rfmtid, name);
2113 if (FAILED(r))
2114 goto end;
2116 r = IStorage_CreateStream( (IStorage*)This, name, grfMode, 0, 0, &stm );
2117 if (FAILED(r))
2118 goto end;
2120 r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg);
2122 end:
2123 TRACE("returning 0x%08lx\n", r);
2124 return r;
2127 /************************************************************************
2128 * IPropertySetStorage_fnOpen (IPropertySetStorage)
2130 static HRESULT WINAPI IPropertySetStorage_fnOpen(
2131 IPropertySetStorage *ppstg,
2132 REFFMTID rfmtid,
2133 DWORD grfMode,
2134 IPropertyStorage** ppprstg)
2136 _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
2137 IStream *stm = NULL;
2138 WCHAR name[CCH_MAX_PROPSTG_NAME];
2139 HRESULT r;
2141 TRACE("%p %s %08lx %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
2143 /* be picky */
2144 if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
2145 grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
2147 r = STG_E_INVALIDFLAG;
2148 goto end;
2151 if (!rfmtid)
2153 r = E_INVALIDARG;
2154 goto end;
2157 r = FmtIdToPropStgName(rfmtid, name);
2158 if (FAILED(r))
2159 goto end;
2161 r = IStorage_OpenStream((IStorage*) This, name, 0, grfMode, 0, &stm );
2162 if (FAILED(r))
2163 goto end;
2165 r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg);
2167 end:
2168 TRACE("returning 0x%08lx\n", r);
2169 return r;
2172 /************************************************************************
2173 * IPropertySetStorage_fnDelete (IPropertySetStorage)
2175 static HRESULT WINAPI IPropertySetStorage_fnDelete(
2176 IPropertySetStorage *ppstg,
2177 REFFMTID rfmtid)
2179 _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
2180 IStorage *stg = NULL;
2181 WCHAR name[CCH_MAX_PROPSTG_NAME];
2182 HRESULT r;
2184 TRACE("%p %s\n", This, debugstr_guid(rfmtid));
2186 if (!rfmtid)
2187 return E_INVALIDARG;
2189 r = FmtIdToPropStgName(rfmtid, name);
2190 if (FAILED(r))
2191 return r;
2193 stg = (IStorage*) This;
2194 return IStorage_DestroyElement(stg, name);
2197 /************************************************************************
2198 * IPropertySetStorage_fnEnum (IPropertySetStorage)
2200 static HRESULT WINAPI IPropertySetStorage_fnEnum(
2201 IPropertySetStorage *ppstg,
2202 IEnumSTATPROPSETSTG** ppenum)
2204 _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
2205 FIXME("%p\n", This);
2206 return E_NOTIMPL;
2210 /***********************************************************************
2211 * vtables
2213 const IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
2215 IPropertySetStorage_fnQueryInterface,
2216 IPropertySetStorage_fnAddRef,
2217 IPropertySetStorage_fnRelease,
2218 IPropertySetStorage_fnCreate,
2219 IPropertySetStorage_fnOpen,
2220 IPropertySetStorage_fnDelete,
2221 IPropertySetStorage_fnEnum
2224 static const IPropertyStorageVtbl IPropertyStorage_Vtbl =
2226 IPropertyStorage_fnQueryInterface,
2227 IPropertyStorage_fnAddRef,
2228 IPropertyStorage_fnRelease,
2229 IPropertyStorage_fnReadMultiple,
2230 IPropertyStorage_fnWriteMultiple,
2231 IPropertyStorage_fnDeleteMultiple,
2232 IPropertyStorage_fnReadPropertyNames,
2233 IPropertyStorage_fnWritePropertyNames,
2234 IPropertyStorage_fnDeletePropertyNames,
2235 IPropertyStorage_fnCommit,
2236 IPropertyStorage_fnRevert,
2237 IPropertyStorage_fnEnum,
2238 IPropertyStorage_fnSetTimes,
2239 IPropertyStorage_fnSetClass,
2240 IPropertyStorage_fnStat,
2243 /***********************************************************************
2244 * Format ID <-> name conversion
2246 static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
2247 'I','n','f','o','r','m','a','t','i','o','n',0 };
2248 static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t',
2249 'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
2251 #define BITS_PER_BYTE 8
2252 #define CHARMASK 0x1f
2253 #define BITS_IN_CHARMASK 5
2254 #define NUM_ALPHA_CHARS 26
2256 /***********************************************************************
2257 * FmtIdToPropStgName [ole32.@]
2258 * Returns the storage name of the format ID rfmtid.
2259 * PARAMS
2260 * rfmtid [I] Format ID for which to return a storage name
2261 * str [O] Storage name associated with rfmtid.
2263 * RETURNS
2264 * E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise.
2266 * NOTES
2267 * str must be at least CCH_MAX_PROPSTG_NAME characters in length.
2268 * Based on the algorithm described here:
2269 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2271 HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str)
2273 static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
2275 TRACE("%s, %p\n", debugstr_guid(rfmtid), str);
2277 if (!rfmtid) return E_INVALIDARG;
2278 if (!str) return E_INVALIDARG;
2280 if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
2281 lstrcpyW(str, szSummaryInfo);
2282 else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
2283 lstrcpyW(str, szDocSummaryInfo);
2284 else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid))
2285 lstrcpyW(str, szDocSummaryInfo);
2286 else
2288 BYTE *fmtptr;
2289 WCHAR *pstr = str;
2290 ULONG bitsRemaining = BITS_PER_BYTE;
2292 *pstr++ = 5;
2293 for (fmtptr = (BYTE *)rfmtid; fmtptr < (BYTE *)rfmtid + sizeof(FMTID); )
2295 ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
2297 if (bitsRemaining >= BITS_IN_CHARMASK)
2299 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
2300 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
2301 *pstr <= 'z')
2302 *pstr += 'A' - 'a';
2303 pstr++;
2304 bitsRemaining -= BITS_IN_CHARMASK;
2305 if (bitsRemaining == 0)
2307 fmtptr++;
2308 bitsRemaining = BITS_PER_BYTE;
2311 else
2313 if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID))
2314 i |= *fmtptr << bitsRemaining;
2315 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
2316 bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK;
2319 *pstr = 0;
2321 TRACE("returning %s\n", debugstr_w(str));
2322 return S_OK;
2325 /***********************************************************************
2326 * PropStgNameToFmtId [ole32.@]
2327 * Returns the format ID corresponding to the given name.
2328 * PARAMS
2329 * str [I] Storage name to convert to a format ID.
2330 * rfmtid [O] Format ID corresponding to str.
2332 * RETURNS
2333 * E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to
2334 * a format ID, S_OK otherwise.
2336 * NOTES
2337 * Based on the algorithm described here:
2338 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2340 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid)
2342 HRESULT hr = STG_E_INVALIDNAME;
2344 TRACE("%s, %p\n", debugstr_w(str), rfmtid);
2346 if (!rfmtid) return E_INVALIDARG;
2347 if (!str) return STG_E_INVALIDNAME;
2349 if (!lstrcmpiW(str, szDocSummaryInfo))
2351 memcpy(rfmtid, &FMTID_DocSummaryInformation, sizeof(*rfmtid));
2352 hr = S_OK;
2354 else if (!lstrcmpiW(str, szSummaryInfo))
2356 memcpy(rfmtid, &FMTID_SummaryInformation, sizeof(*rfmtid));
2357 hr = S_OK;
2359 else
2361 ULONG bits;
2362 BYTE *fmtptr = (BYTE *)rfmtid - 1;
2363 const WCHAR *pstr = str;
2365 memset(rfmtid, 0, sizeof(*rfmtid));
2366 for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE;
2367 bits += BITS_IN_CHARMASK)
2369 ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored;
2370 WCHAR wc;
2372 if (bitsUsed == 0)
2373 fmtptr++;
2374 wc = *++pstr - 'A';
2375 if (wc > NUM_ALPHA_CHARS)
2377 wc += 'A' - 'a';
2378 if (wc > NUM_ALPHA_CHARS)
2380 wc += 'a' - '0' + NUM_ALPHA_CHARS;
2381 if (wc > CHARMASK)
2383 WARN("invalid character (%d)\n", *pstr);
2384 goto end;
2388 *fmtptr |= wc << bitsUsed;
2389 bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK);
2390 if (bitsStored < BITS_IN_CHARMASK)
2392 wc >>= BITS_PER_BYTE - bitsUsed;
2393 if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE)
2395 if (wc != 0)
2397 WARN("extra bits\n");
2398 goto end;
2400 break;
2402 fmtptr++;
2403 *fmtptr |= (BYTE)wc;
2406 hr = S_OK;
2408 end:
2409 return hr;