msvcrt: Reworked _fullpath.
[wine/wine-kai.git] / dlls / ole32 / stg_prop.c
blob1d7b2dbd0b398300e356c543eaac793e51db1527
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 static inline StorageImpl *impl_from_IPropertySetStorage( IPropertySetStorage *iface )
68 return (StorageImpl *)((char*)iface - FIELD_OFFSET(StorageImpl, base.pssVtbl));
71 /* These are documented in MSDN, e.g.
72 * http://msdn.microsoft.com/library/en-us/stg/stg/property_set_header.asp
73 * http://msdn.microsoft.com/library/library/en-us/stg/stg/section.asp
74 * but they don't seem to be in any header file.
76 #define PROPSETHDR_BYTEORDER_MAGIC 0xfffe
77 #define PROPSETHDR_OSVER_KIND_WIN16 0
78 #define PROPSETHDR_OSVER_KIND_MAC 1
79 #define PROPSETHDR_OSVER_KIND_WIN32 2
81 #define CP_UNICODE 1200
83 #define MAX_VERSION_0_PROP_NAME_LENGTH 256
85 /* The format version (and what it implies) is described here:
86 * http://msdn.microsoft.com/library/en-us/stg/stg/format_version.asp
88 typedef struct tagPROPERTYSETHEADER
90 WORD wByteOrder; /* always 0xfffe */
91 WORD wFormat; /* can be zero or one */
92 DWORD dwOSVer; /* OS version of originating system */
93 CLSID clsid; /* application CLSID */
94 DWORD reserved; /* always 1 */
95 } PROPERTYSETHEADER;
97 typedef struct tagFORMATIDOFFSET
99 FMTID fmtid;
100 DWORD dwOffset; /* from beginning of stream */
101 } FORMATIDOFFSET;
103 typedef struct tagPROPERTYSECTIONHEADER
105 DWORD cbSection;
106 DWORD cProperties;
107 } PROPERTYSECTIONHEADER;
109 typedef struct tagPROPERTYIDOFFSET
111 DWORD propid;
112 DWORD dwOffset; /* from beginning of section */
113 } PROPERTYIDOFFSET;
115 struct tagPropertyStorage_impl;
117 /* Initializes the property storage from the stream (and undoes any uncommitted
118 * changes in the process.) Returns an error if there is an error reading or
119 * if the stream format doesn't match what's expected.
121 static HRESULT PropertyStorage_ReadFromStream(struct tagPropertyStorage_impl *);
123 static HRESULT PropertyStorage_WriteToStream(struct tagPropertyStorage_impl *);
125 /* Creates the dictionaries used by the property storage. If successful, all
126 * the dictionaries have been created. If failed, none has been. (This makes
127 * it a bit easier to deal with destroying them.)
129 static HRESULT PropertyStorage_CreateDictionaries(
130 struct tagPropertyStorage_impl *);
132 static void PropertyStorage_DestroyDictionaries(
133 struct tagPropertyStorage_impl *);
135 /* Copies from propvar to prop. If propvar's type is VT_LPSTR, copies the
136 * string using PropertyStorage_StringCopy.
138 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
139 const PROPVARIANT *propvar, LCID targetCP, LCID srcCP);
141 /* Copies the string src, which is encoded using code page srcCP, and returns
142 * it in *dst, in the code page specified by targetCP. The returned string is
143 * allocated using CoTaskMemAlloc.
144 * If srcCP is CP_UNICODE, src is in fact an LPCWSTR. Similarly, if targetCP
145 * is CP_UNICODE, the returned string is in fact an LPWSTR.
146 * Returns S_OK on success, something else on failure.
148 static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
149 LCID targetCP);
151 static const IPropertyStorageVtbl IPropertyStorage_Vtbl;
153 /***********************************************************************
154 * Implementation of IPropertyStorage
156 typedef struct tagPropertyStorage_impl
158 const IPropertyStorageVtbl *vtbl;
159 LONG ref;
160 CRITICAL_SECTION cs;
161 IStream *stm;
162 BOOL dirty;
163 FMTID fmtid;
164 CLSID clsid;
165 WORD format;
166 DWORD originatorOS;
167 DWORD grfFlags;
168 DWORD grfMode;
169 UINT codePage;
170 LCID locale;
171 PROPID highestProp;
172 struct dictionary *name_to_propid;
173 struct dictionary *propid_to_name;
174 struct dictionary *propid_to_prop;
175 } PropertyStorage_impl;
177 /************************************************************************
178 * IPropertyStorage_fnQueryInterface (IPropertyStorage)
180 static HRESULT WINAPI IPropertyStorage_fnQueryInterface(
181 IPropertyStorage *iface,
182 REFIID riid,
183 void** ppvObject)
185 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
187 if ( (This==0) || (ppvObject==0) )
188 return E_INVALIDARG;
190 *ppvObject = 0;
192 if (IsEqualGUID(&IID_IUnknown, riid) ||
193 IsEqualGUID(&IID_IPropertyStorage, riid))
195 IPropertyStorage_AddRef(iface);
196 *ppvObject = (IPropertyStorage*)iface;
197 return S_OK;
200 return E_NOINTERFACE;
203 /************************************************************************
204 * IPropertyStorage_fnAddRef (IPropertyStorage)
206 static ULONG WINAPI IPropertyStorage_fnAddRef(
207 IPropertyStorage *iface)
209 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
210 return InterlockedIncrement(&This->ref);
213 /************************************************************************
214 * IPropertyStorage_fnRelease (IPropertyStorage)
216 static ULONG WINAPI IPropertyStorage_fnRelease(
217 IPropertyStorage *iface)
219 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
220 ULONG ref;
222 ref = InterlockedDecrement(&This->ref);
223 if (ref == 0)
225 TRACE("Destroying %p\n", This);
226 if (This->dirty)
227 IPropertyStorage_Commit(iface, STGC_DEFAULT);
228 IStream_Release(This->stm);
229 DeleteCriticalSection(&This->cs);
230 PropertyStorage_DestroyDictionaries(This);
231 HeapFree(GetProcessHeap(), 0, This);
233 return ref;
236 static PROPVARIANT *PropertyStorage_FindProperty(PropertyStorage_impl *This,
237 DWORD propid)
239 PROPVARIANT *ret = NULL;
241 assert(This);
242 dictionary_find(This->propid_to_prop, (void *)propid, (void **)&ret);
243 TRACE("returning %p\n", ret);
244 return ret;
247 /* Returns NULL if name is NULL. */
248 static PROPVARIANT *PropertyStorage_FindPropertyByName(
249 PropertyStorage_impl *This, LPCWSTR name)
251 PROPVARIANT *ret = NULL;
252 PROPID propid;
254 assert(This);
255 if (!name)
256 return NULL;
257 if (This->codePage == CP_UNICODE)
259 if (dictionary_find(This->name_to_propid, name, (void **)&propid))
260 ret = PropertyStorage_FindProperty(This, (PROPID)propid);
262 else
264 LPSTR ansiName;
265 HRESULT hr = PropertyStorage_StringCopy((LPCSTR)name, CP_UNICODE,
266 &ansiName, This->codePage);
268 if (SUCCEEDED(hr))
270 if (dictionary_find(This->name_to_propid, ansiName,
271 (void **)&propid))
272 ret = PropertyStorage_FindProperty(This, (PROPID)propid);
273 CoTaskMemFree(ansiName);
276 TRACE("returning %p\n", ret);
277 return ret;
280 static LPWSTR PropertyStorage_FindPropertyNameById(PropertyStorage_impl *This,
281 DWORD propid)
283 LPWSTR ret = NULL;
285 assert(This);
286 dictionary_find(This->propid_to_name, (void *)propid, (void **)&ret);
287 TRACE("returning %p\n", ret);
288 return ret;
291 /************************************************************************
292 * IPropertyStorage_fnReadMultiple (IPropertyStorage)
294 static HRESULT WINAPI IPropertyStorage_fnReadMultiple(
295 IPropertyStorage* iface,
296 ULONG cpspec,
297 const PROPSPEC rgpspec[],
298 PROPVARIANT rgpropvar[])
300 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
301 HRESULT hr = S_FALSE;
302 ULONG i;
304 TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
305 if (!This)
306 return E_INVALIDARG;
307 if (cpspec && (!rgpspec || !rgpropvar))
308 return E_INVALIDARG;
309 EnterCriticalSection(&This->cs);
310 for (i = 0; i < cpspec; i++)
312 PropVariantInit(&rgpropvar[i]);
313 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
315 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
316 rgpspec[i].u.lpwstr);
318 if (prop)
319 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop, GetACP(),
320 This->codePage);
322 else
324 switch (rgpspec[i].u.propid)
326 case PID_CODEPAGE:
327 rgpropvar[i].vt = VT_I2;
328 rgpropvar[i].u.iVal = This->codePage;
329 break;
330 case PID_LOCALE:
331 rgpropvar[i].vt = VT_I4;
332 rgpropvar[i].u.lVal = This->locale;
333 break;
334 default:
336 PROPVARIANT *prop = PropertyStorage_FindProperty(This,
337 rgpspec[i].u.propid);
339 if (prop)
340 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop,
341 GetACP(), This->codePage);
346 LeaveCriticalSection(&This->cs);
347 return hr;
350 static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
351 LCID dstCP)
353 HRESULT hr = S_OK;
354 int len;
356 TRACE("%s, %p, %ld, %ld\n",
357 srcCP == CP_UNICODE ? debugstr_w((LPCWSTR)src) : debugstr_a(src), dst,
358 dstCP, srcCP);
359 assert(src);
360 assert(dst);
361 *dst = NULL;
362 if (dstCP == srcCP)
364 size_t len;
366 if (dstCP == CP_UNICODE)
367 len = (strlenW((LPCWSTR)src) + 1) * sizeof(WCHAR);
368 else
369 len = strlen(src) + 1;
370 *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
371 if (!*dst)
372 hr = STG_E_INSUFFICIENTMEMORY;
373 else
374 memcpy(*dst, src, len);
376 else
378 if (dstCP == CP_UNICODE)
380 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
381 *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
382 if (!*dst)
383 hr = STG_E_INSUFFICIENTMEMORY;
384 else
385 MultiByteToWideChar(srcCP, 0, src, -1, (LPWSTR)*dst, len);
387 else
389 LPWSTR wideStr;
391 if (srcCP == CP_UNICODE)
392 wideStr = (LPWSTR)src;
393 else
395 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
396 wideStr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
397 if (wideStr)
398 MultiByteToWideChar(srcCP, 0, src, -1, wideStr, len);
399 else
400 hr = STG_E_INSUFFICIENTMEMORY;
402 if (SUCCEEDED(hr))
404 len = WideCharToMultiByte(dstCP, 0, wideStr, -1, NULL, 0,
405 NULL, NULL);
406 *dst = CoTaskMemAlloc(len);
407 if (!*dst)
408 hr = STG_E_INSUFFICIENTMEMORY;
409 else
411 BOOL defCharUsed = FALSE;
413 if (WideCharToMultiByte(dstCP, 0, wideStr, -1, *dst, len,
414 NULL, &defCharUsed) == 0 || defCharUsed)
416 CoTaskMemFree(*dst);
417 *dst = NULL;
418 hr = HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION);
422 if (wideStr != (LPWSTR)src)
423 HeapFree(GetProcessHeap(), 0, wideStr);
426 TRACE("returning 0x%08lx (%s)\n", hr,
427 dstCP == CP_UNICODE ? debugstr_w((LPCWSTR)*dst) : debugstr_a(*dst));
428 return hr;
431 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
432 const PROPVARIANT *propvar, LCID targetCP, LCID srcCP)
434 HRESULT hr = S_OK;
436 assert(prop);
437 assert(propvar);
438 if (propvar->vt == VT_LPSTR)
440 hr = PropertyStorage_StringCopy(propvar->u.pszVal, srcCP,
441 &prop->u.pszVal, targetCP);
442 if (SUCCEEDED(hr))
443 prop->vt = VT_LPSTR;
445 else
446 PropVariantCopy(prop, propvar);
447 return hr;
450 /* Stores the property with id propid and value propvar into this property
451 * storage. lcid is ignored if propvar's type is not VT_LPSTR. If propvar's
452 * type is VT_LPSTR, converts the string using lcid as the source code page
453 * and This->codePage as the target code page before storing.
454 * As a side effect, may change This->format to 1 if the type of propvar is
455 * a version 1-only property.
457 static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
458 PROPID propid, const PROPVARIANT *propvar, LCID lcid)
460 HRESULT hr = S_OK;
461 PROPVARIANT *prop = PropertyStorage_FindProperty(This, propid);
463 assert(This);
464 assert(propvar);
465 if (propvar->vt & VT_BYREF || propvar->vt & VT_ARRAY)
466 This->format = 1;
467 switch (propvar->vt)
469 case VT_DECIMAL:
470 case VT_I1:
471 case VT_INT:
472 case VT_UINT:
473 case VT_VECTOR|VT_I1:
474 This->format = 1;
476 TRACE("Setting 0x%08lx to type %d\n", propid, propvar->vt);
477 if (prop)
479 PropVariantClear(prop);
480 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
481 lcid);
483 else
485 prop = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
486 sizeof(PROPVARIANT));
487 if (prop)
489 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
490 lcid);
491 if (SUCCEEDED(hr))
493 dictionary_insert(This->propid_to_prop, (void *)propid, prop);
494 if (propid > This->highestProp)
495 This->highestProp = propid;
497 else
498 HeapFree(GetProcessHeap(), 0, prop);
500 else
501 hr = STG_E_INSUFFICIENTMEMORY;
503 return hr;
506 /* Adds the name srcName to the name dictionaries, mapped to property ID id.
507 * srcName is encoded in code page cp, and is converted to This->codePage.
508 * If cp is CP_UNICODE, srcName is actually a unicode string.
509 * As a side effect, may change This->format to 1 if srcName is too long for
510 * a version 0 property storage.
511 * Doesn't validate id.
513 static HRESULT PropertyStorage_StoreNameWithId(PropertyStorage_impl *This,
514 LPCSTR srcName, LCID cp, PROPID id)
516 LPSTR name;
517 HRESULT hr;
519 assert(srcName);
521 hr = PropertyStorage_StringCopy((LPCSTR)srcName, cp, &name, This->codePage);
522 if (SUCCEEDED(hr))
524 if (This->codePage == CP_UNICODE)
526 if (lstrlenW((LPWSTR)name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
527 This->format = 1;
529 else
531 if (strlen(name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
532 This->format = 1;
534 TRACE("Adding prop name %s, propid %ld\n",
535 This->codePage == CP_UNICODE ? debugstr_w((LPCWSTR)name) :
536 debugstr_a(name), id);
537 dictionary_insert(This->name_to_propid, name, (void *)id);
538 dictionary_insert(This->propid_to_name, (void *)id, name);
540 return hr;
543 /************************************************************************
544 * IPropertyStorage_fnWriteMultiple (IPropertyStorage)
546 static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
547 IPropertyStorage* iface,
548 ULONG cpspec,
549 const PROPSPEC rgpspec[],
550 const PROPVARIANT rgpropvar[],
551 PROPID propidNameFirst)
553 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
554 HRESULT hr = S_OK;
555 ULONG i;
557 TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
558 if (!This)
559 return E_INVALIDARG;
560 if (cpspec && (!rgpspec || !rgpropvar))
561 return E_INVALIDARG;
562 if (!(This->grfMode & STGM_READWRITE))
563 return STG_E_ACCESSDENIED;
564 EnterCriticalSection(&This->cs);
565 This->dirty = TRUE;
566 This->originatorOS = (DWORD)MAKELONG(LOWORD(GetVersion()),
567 PROPSETHDR_OSVER_KIND_WIN32) ;
568 for (i = 0; i < cpspec; i++)
570 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
572 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
573 rgpspec[i].u.lpwstr);
575 if (prop)
576 PropVariantCopy(prop, &rgpropvar[i]);
577 else
579 /* Note that I don't do the special cases here that I do below,
580 * because naming the special PIDs isn't supported.
582 if (propidNameFirst < PID_FIRST_USABLE ||
583 propidNameFirst >= PID_MIN_READONLY)
584 hr = STG_E_INVALIDPARAMETER;
585 else
587 PROPID nextId = max(propidNameFirst, This->highestProp + 1);
589 hr = PropertyStorage_StoreNameWithId(This,
590 (LPCSTR)rgpspec[i].u.lpwstr, CP_UNICODE, nextId);
591 if (SUCCEEDED(hr))
592 hr = PropertyStorage_StorePropWithId(This, nextId,
593 &rgpropvar[i], GetACP());
597 else
599 switch (rgpspec[i].u.propid)
601 case PID_DICTIONARY:
602 /* Can't set the dictionary */
603 hr = STG_E_INVALIDPARAMETER;
604 break;
605 case PID_CODEPAGE:
606 /* Can only set the code page if nothing else has been set */
607 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
608 rgpropvar[i].vt == VT_I2)
610 This->codePage = rgpropvar[i].u.iVal;
611 if (This->codePage == CP_UNICODE)
612 This->grfFlags &= ~PROPSETFLAG_ANSI;
613 else
614 This->grfFlags |= PROPSETFLAG_ANSI;
616 else
617 hr = STG_E_INVALIDPARAMETER;
618 break;
619 case PID_LOCALE:
620 /* Can only set the locale if nothing else has been set */
621 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
622 rgpropvar[i].vt == VT_I4)
623 This->locale = rgpropvar[i].u.lVal;
624 else
625 hr = STG_E_INVALIDPARAMETER;
626 break;
627 case PID_ILLEGAL:
628 /* silently ignore like MSDN says */
629 break;
630 default:
631 if (rgpspec[i].u.propid >= PID_MIN_READONLY)
632 hr = STG_E_INVALIDPARAMETER;
633 else
634 hr = PropertyStorage_StorePropWithId(This,
635 rgpspec[i].u.propid, &rgpropvar[i], GetACP());
639 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
640 IPropertyStorage_Commit(iface, STGC_DEFAULT);
641 LeaveCriticalSection(&This->cs);
642 return hr;
645 /************************************************************************
646 * IPropertyStorage_fnDeleteMultiple (IPropertyStorage)
648 static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
649 IPropertyStorage* iface,
650 ULONG cpspec,
651 const PROPSPEC rgpspec[])
653 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
654 ULONG i;
655 HRESULT hr;
657 TRACE("(%p, %ld, %p)\n", iface, cpspec, rgpspec);
658 if (!This)
659 return E_INVALIDARG;
660 if (cpspec && !rgpspec)
661 return E_INVALIDARG;
662 if (!(This->grfMode & STGM_READWRITE))
663 return STG_E_ACCESSDENIED;
664 hr = S_OK;
665 EnterCriticalSection(&This->cs);
666 This->dirty = TRUE;
667 for (i = 0; i < cpspec; i++)
669 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
671 PROPID propid;
673 if (dictionary_find(This->name_to_propid,
674 (void *)rgpspec[i].u.lpwstr, (void **)&propid))
675 dictionary_remove(This->propid_to_prop, (void *)propid);
677 else
679 if (rgpspec[i].u.propid >= PID_FIRST_USABLE &&
680 rgpspec[i].u.propid < PID_MIN_READONLY)
681 dictionary_remove(This->propid_to_prop,
682 (void *)rgpspec[i].u.propid);
683 else
684 hr = STG_E_INVALIDPARAMETER;
687 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
688 IPropertyStorage_Commit(iface, STGC_DEFAULT);
689 LeaveCriticalSection(&This->cs);
690 return hr;
693 /************************************************************************
694 * IPropertyStorage_fnReadPropertyNames (IPropertyStorage)
696 static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames(
697 IPropertyStorage* iface,
698 ULONG cpropid,
699 const PROPID rgpropid[],
700 LPOLESTR rglpwstrName[])
702 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
703 ULONG i;
704 HRESULT hr = S_FALSE;
706 TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
707 if (!This)
708 return E_INVALIDARG;
709 if (cpropid && (!rgpropid || !rglpwstrName))
710 return E_INVALIDARG;
711 EnterCriticalSection(&This->cs);
712 for (i = 0; i < cpropid && SUCCEEDED(hr); i++)
714 LPWSTR name = PropertyStorage_FindPropertyNameById(This, rgpropid[i]);
716 if (name)
718 size_t len = lstrlenW(name);
720 hr = S_OK;
721 rglpwstrName[i] = CoTaskMemAlloc((len + 1) * sizeof(WCHAR));
722 if (rglpwstrName)
723 memcpy(rglpwstrName, name, (len + 1) * sizeof(WCHAR));
724 else
725 hr = STG_E_INSUFFICIENTMEMORY;
727 else
728 rglpwstrName[i] = NULL;
730 LeaveCriticalSection(&This->cs);
731 return hr;
734 /************************************************************************
735 * IPropertyStorage_fnWritePropertyNames (IPropertyStorage)
737 static HRESULT WINAPI IPropertyStorage_fnWritePropertyNames(
738 IPropertyStorage* iface,
739 ULONG cpropid,
740 const PROPID rgpropid[],
741 const LPOLESTR rglpwstrName[])
743 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
744 ULONG i;
745 HRESULT hr;
747 TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
748 if (!This)
749 return E_INVALIDARG;
750 if (cpropid && (!rgpropid || !rglpwstrName))
751 return E_INVALIDARG;
752 if (!(This->grfMode & STGM_READWRITE))
753 return STG_E_ACCESSDENIED;
754 hr = S_OK;
755 EnterCriticalSection(&This->cs);
756 This->dirty = TRUE;
757 for (i = 0; SUCCEEDED(hr) && i < cpropid; i++)
759 if (rgpropid[i] != PID_ILLEGAL)
760 hr = PropertyStorage_StoreNameWithId(This, (LPCSTR)rglpwstrName[i],
761 CP_UNICODE, rgpropid[i]);
763 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
764 IPropertyStorage_Commit(iface, STGC_DEFAULT);
765 LeaveCriticalSection(&This->cs);
766 return hr;
769 /************************************************************************
770 * IPropertyStorage_fnDeletePropertyNames (IPropertyStorage)
772 static HRESULT WINAPI IPropertyStorage_fnDeletePropertyNames(
773 IPropertyStorage* iface,
774 ULONG cpropid,
775 const PROPID rgpropid[])
777 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
778 ULONG i;
779 HRESULT hr;
781 TRACE("(%p, %ld, %p)\n", iface, cpropid, rgpropid);
782 if (!This)
783 return E_INVALIDARG;
784 if (cpropid && !rgpropid)
785 return E_INVALIDARG;
786 if (!(This->grfMode & STGM_READWRITE))
787 return STG_E_ACCESSDENIED;
788 hr = S_OK;
789 EnterCriticalSection(&This->cs);
790 This->dirty = TRUE;
791 for (i = 0; i < cpropid; i++)
793 LPWSTR name = NULL;
795 if (dictionary_find(This->propid_to_name, (void *)rgpropid[i],
796 (void **)&name))
798 dictionary_remove(This->propid_to_name, (void *)rgpropid[i]);
799 dictionary_remove(This->name_to_propid, name);
802 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
803 IPropertyStorage_Commit(iface, STGC_DEFAULT);
804 LeaveCriticalSection(&This->cs);
805 return hr;
808 /************************************************************************
809 * IPropertyStorage_fnCommit (IPropertyStorage)
811 static HRESULT WINAPI IPropertyStorage_fnCommit(
812 IPropertyStorage* iface,
813 DWORD grfCommitFlags)
815 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
816 HRESULT hr;
818 TRACE("(%p, 0x%08lx)\n", iface, grfCommitFlags);
819 if (!This)
820 return E_INVALIDARG;
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);
842 if (!This)
843 return E_INVALIDARG;
845 EnterCriticalSection(&This->cs);
846 if (This->dirty)
848 PropertyStorage_DestroyDictionaries(This);
849 hr = PropertyStorage_CreateDictionaries(This);
850 if (SUCCEEDED(hr))
851 hr = PropertyStorage_ReadFromStream(This);
853 else
854 hr = S_OK;
855 LeaveCriticalSection(&This->cs);
856 return hr;
859 /************************************************************************
860 * IPropertyStorage_fnEnum (IPropertyStorage)
862 static HRESULT WINAPI IPropertyStorage_fnEnum(
863 IPropertyStorage* iface,
864 IEnumSTATPROPSTG** ppenum)
866 FIXME("\n");
867 return E_NOTIMPL;
870 /************************************************************************
871 * IPropertyStorage_fnSetTimes (IPropertyStorage)
873 static HRESULT WINAPI IPropertyStorage_fnSetTimes(
874 IPropertyStorage* iface,
875 const FILETIME* pctime,
876 const FILETIME* patime,
877 const FILETIME* pmtime)
879 FIXME("\n");
880 return E_NOTIMPL;
883 /************************************************************************
884 * IPropertyStorage_fnSetClass (IPropertyStorage)
886 static HRESULT WINAPI IPropertyStorage_fnSetClass(
887 IPropertyStorage* iface,
888 REFCLSID clsid)
890 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
892 TRACE("%p, %s\n", iface, debugstr_guid(clsid));
893 if (!This || !clsid)
894 return E_INVALIDARG;
895 if (!(This->grfMode & STGM_READWRITE))
896 return STG_E_ACCESSDENIED;
897 memcpy(&This->clsid, clsid, sizeof(This->clsid));
898 This->dirty = TRUE;
899 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
900 IPropertyStorage_Commit(iface, STGC_DEFAULT);
901 return S_OK;
904 /************************************************************************
905 * IPropertyStorage_fnStat (IPropertyStorage)
907 static HRESULT WINAPI IPropertyStorage_fnStat(
908 IPropertyStorage* iface,
909 STATPROPSETSTG* statpsstg)
911 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
912 STATSTG stat;
913 HRESULT hr;
915 TRACE("%p, %p\n", iface, statpsstg);
916 if (!This || !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);
1004 assert(This->name_to_propid);
1005 assert(This->propid_to_name);
1007 StorageUtl_ReadDWord(ptr, 0, &numEntries);
1008 TRACE("Reading %ld entries:\n", numEntries);
1009 ptr += sizeof(DWORD);
1010 for (i = 0; SUCCEEDED(hr) && i < numEntries; i++)
1012 PROPID propid;
1013 DWORD cbEntry;
1015 StorageUtl_ReadDWord(ptr, 0, &propid);
1016 ptr += sizeof(PROPID);
1017 StorageUtl_ReadDWord(ptr, 0, &cbEntry);
1018 ptr += sizeof(DWORD);
1019 TRACE("Reading entry with ID 0x%08lx, %ld bytes\n", propid, cbEntry);
1020 /* Make sure the source string is NULL-terminated */
1021 if (This->codePage != CP_UNICODE)
1022 ptr[cbEntry - 1] = '\0';
1023 else
1024 *((LPWSTR)ptr + cbEntry / sizeof(WCHAR)) = '\0';
1025 hr = PropertyStorage_StoreNameWithId(This, (char*)ptr, This->codePage, propid);
1026 if (This->codePage == CP_UNICODE)
1028 /* Unicode entries are padded to DWORD boundaries */
1029 if (cbEntry % sizeof(DWORD))
1030 ptr += sizeof(DWORD) - (cbEntry % sizeof(DWORD));
1032 ptr += sizeof(DWORD) + cbEntry;
1034 return hr;
1037 /* FIXME: there isn't any checking whether the read property extends past the
1038 * end of the buffer.
1040 static HRESULT PropertyStorage_ReadProperty(PropertyStorage_impl *This,
1041 PROPVARIANT *prop, const BYTE *data)
1043 HRESULT hr = S_OK;
1045 assert(prop);
1046 assert(data);
1047 StorageUtl_ReadDWord(data, 0, (DWORD *)&prop->vt);
1048 data += sizeof(DWORD);
1049 switch (prop->vt)
1051 case VT_EMPTY:
1052 case VT_NULL:
1053 break;
1054 case VT_I1:
1055 prop->u.cVal = *(const char *)data;
1056 TRACE("Read char 0x%x ('%c')\n", prop->u.cVal, prop->u.cVal);
1057 break;
1058 case VT_UI1:
1059 prop->u.bVal = *(const UCHAR *)data;
1060 TRACE("Read byte 0x%x\n", prop->u.bVal);
1061 break;
1062 case VT_I2:
1063 StorageUtl_ReadWord(data, 0, (WORD*)&prop->u.iVal);
1064 TRACE("Read short %d\n", prop->u.iVal);
1065 break;
1066 case VT_UI2:
1067 StorageUtl_ReadWord(data, 0, &prop->u.uiVal);
1068 TRACE("Read ushort %d\n", prop->u.uiVal);
1069 break;
1070 case VT_INT:
1071 case VT_I4:
1072 StorageUtl_ReadDWord(data, 0, (DWORD*)&prop->u.lVal);
1073 TRACE("Read long %ld\n", prop->u.lVal);
1074 break;
1075 case VT_UINT:
1076 case VT_UI4:
1077 StorageUtl_ReadDWord(data, 0, &prop->u.ulVal);
1078 TRACE("Read ulong %ld\n", prop->u.ulVal);
1079 break;
1080 case VT_LPSTR:
1082 DWORD count;
1084 StorageUtl_ReadDWord(data, 0, &count);
1085 if (This->codePage == CP_UNICODE && count / 2)
1087 WARN("Unicode string has odd number of bytes\n");
1088 hr = STG_E_INVALIDHEADER;
1090 else
1092 prop->u.pszVal = CoTaskMemAlloc(count);
1093 if (prop->u.pszVal)
1095 memcpy(prop->u.pszVal, data + sizeof(DWORD), count);
1096 /* This is stored in the code page specified in This->codePage.
1097 * Don't convert it, the caller will just store it as-is.
1099 if (This->codePage == CP_UNICODE)
1101 /* Make sure it's NULL-terminated */
1102 prop->u.pszVal[count / sizeof(WCHAR) - 1] = '\0';
1103 TRACE("Read string value %s\n",
1104 debugstr_w(prop->u.pwszVal));
1106 else
1108 /* Make sure it's NULL-terminated */
1109 prop->u.pszVal[count - 1] = '\0';
1110 TRACE("Read string value %s\n", debugstr_a(prop->u.pszVal));
1113 else
1114 hr = STG_E_INSUFFICIENTMEMORY;
1116 break;
1118 case VT_LPWSTR:
1120 DWORD count;
1122 StorageUtl_ReadDWord(data, 0, &count);
1123 prop->u.pwszVal = CoTaskMemAlloc(count * sizeof(WCHAR));
1124 if (prop->u.pwszVal)
1126 memcpy(prop->u.pwszVal, data + sizeof(DWORD),
1127 count * sizeof(WCHAR));
1128 /* make sure string is NULL-terminated */
1129 prop->u.pwszVal[count - 1] = '\0';
1130 PropertyStorage_ByteSwapString(prop->u.pwszVal, count);
1131 TRACE("Read string value %s\n", debugstr_w(prop->u.pwszVal));
1133 else
1134 hr = STG_E_INSUFFICIENTMEMORY;
1135 break;
1137 case VT_FILETIME:
1138 StorageUtl_ReadULargeInteger(data, 0,
1139 (ULARGE_INTEGER *)&prop->u.filetime);
1140 break;
1141 default:
1142 FIXME("unsupported type %d\n", prop->vt);
1143 hr = STG_E_INVALIDPARAMETER;
1145 return hr;
1148 static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
1149 PROPERTYSETHEADER *hdr)
1151 BYTE buf[sizeof(PROPERTYSETHEADER)];
1152 ULONG count = 0;
1153 HRESULT hr;
1155 assert(stm);
1156 assert(hdr);
1157 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1158 if (SUCCEEDED(hr))
1160 if (count != sizeof(buf))
1162 WARN("read %ld, expected %d\n", count, sizeof(buf));
1163 hr = STG_E_INVALIDHEADER;
1165 else
1167 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder),
1168 &hdr->wByteOrder);
1169 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat),
1170 &hdr->wFormat);
1171 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer),
1172 &hdr->dwOSVer);
1173 StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid),
1174 &hdr->clsid);
1175 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved),
1176 &hdr->reserved);
1179 TRACE("returning 0x%08lx\n", hr);
1180 return hr;
1183 static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm,
1184 FORMATIDOFFSET *fmt)
1186 BYTE buf[sizeof(FORMATIDOFFSET)];
1187 ULONG count = 0;
1188 HRESULT hr;
1190 assert(stm);
1191 assert(fmt);
1192 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1193 if (SUCCEEDED(hr))
1195 if (count != sizeof(buf))
1197 WARN("read %ld, expected %d\n", count, sizeof(buf));
1198 hr = STG_E_INVALIDHEADER;
1200 else
1202 StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid),
1203 &fmt->fmtid);
1204 StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset),
1205 &fmt->dwOffset);
1208 TRACE("returning 0x%08lx\n", hr);
1209 return hr;
1212 static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
1213 PROPERTYSECTIONHEADER *hdr)
1215 BYTE buf[sizeof(PROPERTYSECTIONHEADER)];
1216 ULONG count = 0;
1217 HRESULT hr;
1219 assert(stm);
1220 assert(hdr);
1221 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1222 if (SUCCEEDED(hr))
1224 if (count != sizeof(buf))
1226 WARN("read %ld, expected %d\n", count, sizeof(buf));
1227 hr = STG_E_INVALIDHEADER;
1229 else
1231 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1232 cbSection), &hdr->cbSection);
1233 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1234 cProperties), &hdr->cProperties);
1237 TRACE("returning 0x%08lx\n", hr);
1238 return hr;
1241 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This)
1243 PROPERTYSETHEADER hdr;
1244 FORMATIDOFFSET fmtOffset;
1245 PROPERTYSECTIONHEADER sectionHdr;
1246 LARGE_INTEGER seek;
1247 ULONG i;
1248 STATSTG stat;
1249 HRESULT hr;
1250 BYTE *buf = NULL;
1251 ULONG count = 0;
1252 DWORD dictOffset = 0;
1254 assert(This);
1255 This->dirty = FALSE;
1256 This->highestProp = 0;
1257 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
1258 if (FAILED(hr))
1259 goto end;
1260 if (stat.cbSize.u.HighPart)
1262 WARN("stream too big\n");
1263 /* maximum size varies, but it can't be this big */
1264 hr = STG_E_INVALIDHEADER;
1265 goto end;
1267 if (stat.cbSize.u.LowPart == 0)
1269 /* empty stream is okay */
1270 hr = S_OK;
1271 goto end;
1273 else if (stat.cbSize.u.LowPart < sizeof(PROPERTYSETHEADER) +
1274 sizeof(FORMATIDOFFSET))
1276 WARN("stream too small\n");
1277 hr = STG_E_INVALIDHEADER;
1278 goto end;
1280 seek.QuadPart = 0;
1281 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1282 if (FAILED(hr))
1283 goto end;
1284 hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr);
1285 /* I've only seen reserved == 1, but the article says I shouldn't disallow
1286 * higher values.
1288 if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1)
1290 WARN("bad magic in prop set header\n");
1291 hr = STG_E_INVALIDHEADER;
1292 goto end;
1294 if (hdr.wFormat != 0 && hdr.wFormat != 1)
1296 WARN("bad format version %d\n", hdr.wFormat);
1297 hr = STG_E_INVALIDHEADER;
1298 goto end;
1300 This->format = hdr.wFormat;
1301 memcpy(&This->clsid, &hdr.clsid, sizeof(This->clsid));
1302 This->originatorOS = hdr.dwOSVer;
1303 if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC)
1304 WARN("File comes from a Mac, strings will probably be screwed up\n");
1305 hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset);
1306 if (FAILED(hr))
1307 goto end;
1308 if (fmtOffset.dwOffset > stat.cbSize.u.LowPart)
1310 WARN("invalid offset %ld (stream length is %ld)\n", fmtOffset.dwOffset,
1311 stat.cbSize.u.LowPart);
1312 hr = STG_E_INVALIDHEADER;
1313 goto end;
1315 /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
1316 * follow not one, but two sections. The first is the standard properties
1317 * for the document summary information, and the second is user-defined
1318 * properties. This is the only case in which multiple sections are
1319 * allowed.
1320 * Reading the second stream isn't implemented yet.
1322 hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, &sectionHdr);
1323 if (FAILED(hr))
1324 goto end;
1325 /* The section size includes the section header, so check it */
1326 if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER))
1328 WARN("section header too small, got %ld, expected at least %d\n",
1329 sectionHdr.cbSection, sizeof(PROPERTYSECTIONHEADER));
1330 hr = STG_E_INVALIDHEADER;
1331 goto end;
1333 buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection -
1334 sizeof(PROPERTYSECTIONHEADER));
1335 if (!buf)
1337 hr = STG_E_INSUFFICIENTMEMORY;
1338 goto end;
1340 hr = IStream_Read(This->stm, buf, sectionHdr.cbSection -
1341 sizeof(PROPERTYSECTIONHEADER), &count);
1342 if (FAILED(hr))
1343 goto end;
1344 TRACE("Reading %ld properties:\n", sectionHdr.cProperties);
1345 for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
1347 PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(buf +
1348 i * sizeof(PROPERTYIDOFFSET));
1350 if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) ||
1351 idOffset->dwOffset >= sectionHdr.cbSection - sizeof(DWORD))
1352 hr = STG_E_INVALIDPOINTER;
1353 else
1355 if (idOffset->propid >= PID_FIRST_USABLE &&
1356 idOffset->propid < PID_MIN_READONLY && idOffset->propid >
1357 This->highestProp)
1358 This->highestProp = idOffset->propid;
1359 if (idOffset->propid == PID_DICTIONARY)
1361 /* Don't read the dictionary yet, its entries depend on the
1362 * code page. Just store the offset so we know to read it
1363 * later.
1365 dictOffset = idOffset->dwOffset;
1366 TRACE("Dictionary offset is %ld\n", dictOffset);
1368 else
1370 PROPVARIANT prop;
1372 if (SUCCEEDED(PropertyStorage_ReadProperty(This, &prop,
1373 buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER))))
1375 TRACE("Read property with ID 0x%08lx, type %d\n",
1376 idOffset->propid, prop.vt);
1377 switch(idOffset->propid)
1379 case PID_CODEPAGE:
1380 if (prop.vt == VT_I2)
1381 This->codePage = (UINT)prop.u.iVal;
1382 break;
1383 case PID_LOCALE:
1384 if (prop.vt == VT_I4)
1385 This->locale = (LCID)prop.u.lVal;
1386 break;
1387 case PID_BEHAVIOR:
1388 if (prop.vt == VT_I4 && prop.u.lVal)
1389 This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
1390 /* The format should already be 1, but just in case */
1391 This->format = 1;
1392 break;
1393 default:
1394 hr = PropertyStorage_StorePropWithId(This,
1395 idOffset->propid, &prop, This->codePage);
1401 if (!This->codePage)
1403 /* default to Unicode unless told not to, as specified here:
1404 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
1406 if (This->grfFlags & PROPSETFLAG_ANSI)
1407 This->codePage = GetACP();
1408 else
1409 This->codePage = CP_UNICODE;
1411 if (!This->locale)
1412 This->locale = LOCALE_SYSTEM_DEFAULT;
1413 TRACE("Code page is %d, locale is %ld\n", This->codePage, This->locale);
1414 if (dictOffset)
1415 hr = PropertyStorage_ReadDictionary(This,
1416 buf + dictOffset - sizeof(PROPERTYSECTIONHEADER));
1418 end:
1419 HeapFree(GetProcessHeap(), 0, buf);
1420 if (FAILED(hr))
1422 dictionary_destroy(This->name_to_propid);
1423 This->name_to_propid = NULL;
1424 dictionary_destroy(This->propid_to_name);
1425 This->propid_to_name = NULL;
1426 dictionary_destroy(This->propid_to_prop);
1427 This->propid_to_prop = NULL;
1429 return hr;
1432 static void PropertyStorage_MakeHeader(PropertyStorage_impl *This,
1433 PROPERTYSETHEADER *hdr)
1435 assert(This);
1436 assert(hdr);
1437 StorageUtl_WriteWord((BYTE *)&hdr->wByteOrder, 0,
1438 PROPSETHDR_BYTEORDER_MAGIC);
1439 StorageUtl_WriteWord((BYTE *)&hdr->wFormat, 0, This->format);
1440 StorageUtl_WriteDWord((BYTE *)&hdr->dwOSVer, 0, This->originatorOS);
1441 StorageUtl_WriteGUID((BYTE *)&hdr->clsid, 0, &This->clsid);
1442 StorageUtl_WriteDWord((BYTE *)&hdr->reserved, 0, 1);
1445 static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl *This,
1446 FORMATIDOFFSET *fmtOffset)
1448 assert(This);
1449 assert(fmtOffset);
1450 StorageUtl_WriteGUID((BYTE *)fmtOffset, 0, &This->fmtid);
1451 StorageUtl_WriteDWord((BYTE *)fmtOffset, offsetof(FORMATIDOFFSET, dwOffset),
1452 sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET));
1455 static void PropertyStorage_MakeSectionHdr(DWORD cbSection, DWORD numProps,
1456 PROPERTYSECTIONHEADER *hdr)
1458 assert(hdr);
1459 StorageUtl_WriteDWord((BYTE *)hdr, 0, cbSection);
1460 StorageUtl_WriteDWord((BYTE *)hdr,
1461 offsetof(PROPERTYSECTIONHEADER, cProperties), numProps);
1464 static void PropertyStorage_MakePropertyIdOffset(DWORD propid, DWORD dwOffset,
1465 PROPERTYIDOFFSET *propIdOffset)
1467 assert(propIdOffset);
1468 StorageUtl_WriteDWord((BYTE *)propIdOffset, 0, propid);
1469 StorageUtl_WriteDWord((BYTE *)propIdOffset,
1470 offsetof(PROPERTYIDOFFSET, dwOffset), dwOffset);
1473 struct DictionaryClosure
1475 HRESULT hr;
1476 DWORD bytesWritten;
1479 static BOOL PropertyStorage_DictionaryWriter(const void *key,
1480 const void *value, void *extra, void *closure)
1482 PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
1483 struct DictionaryClosure *c = (struct DictionaryClosure *)closure;
1484 DWORD propid;
1485 ULONG count;
1487 assert(key);
1488 assert(This);
1489 assert(closure);
1490 StorageUtl_WriteDWord((LPBYTE)&propid, 0, (DWORD)value);
1491 c->hr = IStream_Write(This->stm, &propid, sizeof(propid), &count);
1492 if (FAILED(c->hr))
1493 goto end;
1494 c->bytesWritten += sizeof(DWORD);
1495 if (This->codePage == CP_UNICODE)
1497 DWORD keyLen, pad = 0;
1499 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0,
1500 (lstrlenW((LPWSTR)key) + 1) * sizeof(WCHAR));
1501 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1502 if (FAILED(c->hr))
1503 goto end;
1504 c->bytesWritten += sizeof(DWORD);
1505 /* Rather than allocate a copy, I'll swap the string to little-endian
1506 * in-place, write it, then swap it back.
1508 PropertyStorage_ByteSwapString(key, keyLen);
1509 c->hr = IStream_Write(This->stm, key, keyLen, &count);
1510 PropertyStorage_ByteSwapString(key, keyLen);
1511 if (FAILED(c->hr))
1512 goto end;
1513 c->bytesWritten += keyLen;
1514 if (keyLen % sizeof(DWORD))
1516 c->hr = IStream_Write(This->stm, &pad,
1517 sizeof(DWORD) - keyLen % sizeof(DWORD), &count);
1518 if (FAILED(c->hr))
1519 goto end;
1520 c->bytesWritten += sizeof(DWORD) - keyLen % sizeof(DWORD);
1523 else
1525 DWORD keyLen;
1527 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0, strlen((LPCSTR)key) + 1);
1528 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1529 if (FAILED(c->hr))
1530 goto end;
1531 c->bytesWritten += sizeof(DWORD);
1532 c->hr = IStream_Write(This->stm, key, keyLen, &count);
1533 if (FAILED(c->hr))
1534 goto end;
1535 c->bytesWritten += keyLen;
1537 end:
1538 return SUCCEEDED(c->hr);
1541 #define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)
1543 /* Writes the dictionary to the stream. Assumes without checking that the
1544 * dictionary isn't empty.
1546 static HRESULT PropertyStorage_WriteDictionaryToStream(
1547 PropertyStorage_impl *This, DWORD *sectionOffset)
1549 HRESULT hr;
1550 LARGE_INTEGER seek;
1551 PROPERTYIDOFFSET propIdOffset;
1552 ULONG count;
1553 DWORD dwTemp;
1554 struct DictionaryClosure closure;
1556 assert(This);
1557 assert(sectionOffset);
1559 /* The dictionary's always the first property written, so seek to its
1560 * spot.
1562 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER);
1563 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1564 if (FAILED(hr))
1565 goto end;
1566 PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY, *sectionOffset,
1567 &propIdOffset);
1568 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1569 if (FAILED(hr))
1570 goto end;
1572 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1573 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1574 if (FAILED(hr))
1575 goto end;
1576 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0,
1577 dictionary_num_entries(This->name_to_propid));
1578 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1579 if (FAILED(hr))
1580 goto end;
1581 *sectionOffset += sizeof(dwTemp);
1583 closure.hr = S_OK;
1584 closure.bytesWritten = 0;
1585 dictionary_enumerate(This->name_to_propid, PropertyStorage_DictionaryWriter,
1586 &closure);
1587 hr = closure.hr;
1588 if (FAILED(hr))
1589 goto end;
1590 *sectionOffset += closure.bytesWritten;
1591 if (closure.bytesWritten % sizeof(DWORD))
1593 TRACE("adding %ld bytes of padding\n", sizeof(DWORD) -
1594 closure.bytesWritten % sizeof(DWORD));
1595 *sectionOffset += sizeof(DWORD) - closure.bytesWritten % sizeof(DWORD);
1598 end:
1599 return hr;
1602 static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This,
1603 DWORD propNum, DWORD propid, PROPVARIANT *var, DWORD *sectionOffset)
1605 HRESULT hr;
1606 LARGE_INTEGER seek;
1607 PROPERTYIDOFFSET propIdOffset;
1608 ULONG count;
1609 DWORD dwType, bytesWritten;
1611 assert(This);
1612 assert(var);
1613 assert(sectionOffset);
1615 TRACE("%p, %ld, 0x%08lx, (%d), (%ld)\n", This, propNum, propid, var->vt,
1616 *sectionOffset);
1618 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) +
1619 propNum * sizeof(PROPERTYIDOFFSET);
1620 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1621 if (FAILED(hr))
1622 goto end;
1623 PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset);
1624 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1625 if (FAILED(hr))
1626 goto end;
1628 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1629 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1630 if (FAILED(hr))
1631 goto end;
1632 StorageUtl_WriteDWord((LPBYTE)&dwType, 0, var->vt);
1633 hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count);
1634 if (FAILED(hr))
1635 goto end;
1636 *sectionOffset += sizeof(dwType);
1638 switch (var->vt)
1640 case VT_EMPTY:
1641 case VT_NULL:
1642 bytesWritten = 0;
1643 break;
1644 case VT_I1:
1645 case VT_UI1:
1646 hr = IStream_Write(This->stm, &var->u.cVal, sizeof(var->u.cVal),
1647 &count);
1648 bytesWritten = count;
1649 break;
1650 case VT_I2:
1651 case VT_UI2:
1653 WORD wTemp;
1655 StorageUtl_WriteWord((LPBYTE)&wTemp, 0, var->u.iVal);
1656 hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count);
1657 bytesWritten = count;
1658 break;
1660 case VT_I4:
1661 case VT_UI4:
1663 DWORD dwTemp;
1665 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, var->u.lVal);
1666 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1667 bytesWritten = count;
1668 break;
1670 case VT_LPSTR:
1672 DWORD len, dwTemp;
1674 if (This->codePage == CP_UNICODE)
1675 len = (lstrlenW(var->u.pwszVal) + 1) * sizeof(WCHAR);
1676 else
1677 len = lstrlenA(var->u.pszVal) + 1;
1678 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1679 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1680 if (FAILED(hr))
1681 goto end;
1682 hr = IStream_Write(This->stm, var->u.pszVal, len, &count);
1683 bytesWritten = count + sizeof(DWORD);
1684 break;
1686 case VT_LPWSTR:
1688 DWORD len = lstrlenW(var->u.pwszVal) + 1, dwTemp;
1690 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1691 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1692 if (FAILED(hr))
1693 goto end;
1694 hr = IStream_Write(This->stm, var->u.pwszVal, len * sizeof(WCHAR),
1695 &count);
1696 bytesWritten = count + sizeof(DWORD);
1697 break;
1699 case VT_FILETIME:
1701 FILETIME temp;
1703 StorageUtl_WriteULargeInteger((BYTE *)&temp, 0,
1704 (ULARGE_INTEGER *)&var->u.filetime);
1705 hr = IStream_Write(This->stm, &temp, sizeof(FILETIME), &count);
1706 bytesWritten = count;
1707 break;
1709 default:
1710 FIXME("unsupported type: %d\n", var->vt);
1711 return STG_E_INVALIDPARAMETER;
1714 if (SUCCEEDED(hr))
1716 *sectionOffset += bytesWritten;
1717 if (bytesWritten % sizeof(DWORD))
1719 TRACE("adding %ld bytes of padding\n", sizeof(DWORD) -
1720 bytesWritten % sizeof(DWORD));
1721 *sectionOffset += sizeof(DWORD) - bytesWritten % sizeof(DWORD);
1725 end:
1726 return hr;
1729 struct PropertyClosure
1731 HRESULT hr;
1732 DWORD propNum;
1733 DWORD *sectionOffset;
1736 static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value,
1737 void *extra, void *closure)
1739 PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
1740 struct PropertyClosure *c = (struct PropertyClosure *)closure;
1742 assert(key);
1743 assert(value);
1744 assert(extra);
1745 assert(closure);
1746 c->hr = PropertyStorage_WritePropertyToStream(This,
1747 c->propNum++, (DWORD)key, (PROPVARIANT *)value, c->sectionOffset);
1748 return SUCCEEDED(c->hr);
1751 static HRESULT PropertyStorage_WritePropertiesToStream(
1752 PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset)
1754 struct PropertyClosure closure;
1756 assert(This);
1757 assert(sectionOffset);
1758 closure.hr = S_OK;
1759 closure.propNum = startingPropNum;
1760 closure.sectionOffset = sectionOffset;
1761 dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter,
1762 &closure);
1763 return closure.hr;
1766 static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This)
1768 HRESULT hr;
1769 ULONG count = 0;
1770 LARGE_INTEGER seek = { {0} };
1771 PROPERTYSETHEADER hdr;
1772 FORMATIDOFFSET fmtOffset;
1774 assert(This);
1775 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1776 if (FAILED(hr))
1777 goto end;
1778 PropertyStorage_MakeHeader(This, &hdr);
1779 hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count);
1780 if (FAILED(hr))
1781 goto end;
1782 if (count != sizeof(hdr))
1784 hr = STG_E_WRITEFAULT;
1785 goto end;
1788 PropertyStorage_MakeFmtIdOffset(This, &fmtOffset);
1789 hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count);
1790 if (FAILED(hr))
1791 goto end;
1792 if (count != sizeof(fmtOffset))
1794 hr = STG_E_WRITEFAULT;
1795 goto end;
1797 hr = S_OK;
1799 end:
1800 return hr;
1803 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This)
1805 PROPERTYSECTIONHEADER sectionHdr;
1806 HRESULT hr;
1807 ULONG count;
1808 LARGE_INTEGER seek;
1809 DWORD numProps, prop, sectionOffset, dwTemp;
1810 PROPVARIANT var;
1812 assert(This);
1814 PropertyStorage_WriteHeadersToStream(This);
1816 /* Count properties. Always at least one property, the code page */
1817 numProps = 1;
1818 if (dictionary_num_entries(This->name_to_propid))
1819 numProps++;
1820 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1821 numProps++;
1822 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1823 numProps++;
1824 numProps += dictionary_num_entries(This->propid_to_prop);
1826 /* Write section header with 0 bytes right now, I'll adjust it after
1827 * writing properties.
1829 PropertyStorage_MakeSectionHdr(0, numProps, &sectionHdr);
1830 seek.QuadPart = SECTIONHEADER_OFFSET;
1831 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1832 if (FAILED(hr))
1833 goto end;
1834 hr = IStream_Write(This->stm, &sectionHdr, sizeof(sectionHdr), &count);
1835 if (FAILED(hr))
1836 goto end;
1838 prop = 0;
1839 sectionOffset = sizeof(PROPERTYSECTIONHEADER) +
1840 numProps * sizeof(PROPERTYIDOFFSET);
1842 if (dictionary_num_entries(This->name_to_propid))
1844 prop++;
1845 hr = PropertyStorage_WriteDictionaryToStream(This, &sectionOffset);
1846 if (FAILED(hr))
1847 goto end;
1850 PropVariantInit(&var);
1852 var.vt = VT_I2;
1853 var.u.iVal = This->codePage;
1854 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE,
1855 &var, &sectionOffset);
1856 if (FAILED(hr))
1857 goto end;
1859 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1861 var.vt = VT_I4;
1862 var.u.lVal = This->locale;
1863 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE,
1864 &var, &sectionOffset);
1865 if (FAILED(hr))
1866 goto end;
1869 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1871 var.vt = VT_I4;
1872 var.u.lVal = 1;
1873 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR,
1874 &var, &sectionOffset);
1875 if (FAILED(hr))
1876 goto end;
1879 hr = PropertyStorage_WritePropertiesToStream(This, prop, &sectionOffset);
1880 if (FAILED(hr))
1881 goto end;
1883 /* Now write the byte count of the section */
1884 seek.QuadPart = SECTIONHEADER_OFFSET;
1885 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1886 if (FAILED(hr))
1887 goto end;
1888 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, sectionOffset);
1889 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1891 end:
1892 return hr;
1895 /***********************************************************************
1896 * PropertyStorage_Construct
1898 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This)
1900 assert(This);
1901 dictionary_destroy(This->name_to_propid);
1902 This->name_to_propid = NULL;
1903 dictionary_destroy(This->propid_to_name);
1904 This->propid_to_name = NULL;
1905 dictionary_destroy(This->propid_to_prop);
1906 This->propid_to_prop = NULL;
1909 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This)
1911 HRESULT hr = S_OK;
1913 assert(This);
1914 This->name_to_propid = dictionary_create(
1915 PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy,
1916 This);
1917 if (!This->name_to_propid)
1919 hr = STG_E_INSUFFICIENTMEMORY;
1920 goto end;
1922 This->propid_to_name = dictionary_create(PropertyStorage_PropCompare,
1923 NULL, This);
1924 if (!This->propid_to_name)
1926 hr = STG_E_INSUFFICIENTMEMORY;
1927 goto end;
1929 This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
1930 PropertyStorage_PropertyDestroy, This);
1931 if (!This->propid_to_prop)
1933 hr = STG_E_INSUFFICIENTMEMORY;
1934 goto end;
1936 end:
1937 if (FAILED(hr))
1938 PropertyStorage_DestroyDictionaries(This);
1939 return hr;
1942 static HRESULT PropertyStorage_BaseConstruct(IStream *stm,
1943 REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps)
1945 HRESULT hr = S_OK;
1947 assert(pps);
1948 assert(rfmtid);
1949 *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps);
1950 if (!*pps)
1951 return E_OUTOFMEMORY;
1953 (*pps)->vtbl = &IPropertyStorage_Vtbl;
1954 (*pps)->ref = 1;
1955 InitializeCriticalSection(&(*pps)->cs);
1956 (*pps)->stm = stm;
1957 memcpy(&(*pps)->fmtid, rfmtid, sizeof((*pps)->fmtid));
1958 (*pps)->grfMode = grfMode;
1960 hr = PropertyStorage_CreateDictionaries(*pps);
1961 if (FAILED(hr))
1963 IStream_Release(stm);
1964 DeleteCriticalSection(&(*pps)->cs);
1965 HeapFree(GetProcessHeap(), 0, *pps);
1966 *pps = NULL;
1969 return hr;
1972 static HRESULT PropertyStorage_ConstructFromStream(IStream *stm,
1973 REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps)
1975 PropertyStorage_impl *ps;
1976 HRESULT hr;
1978 assert(pps);
1979 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
1980 if (SUCCEEDED(hr))
1982 hr = PropertyStorage_ReadFromStream(ps);
1983 if (SUCCEEDED(hr))
1985 *pps = (IPropertyStorage *)ps;
1986 TRACE("PropertyStorage %p constructed\n", ps);
1987 hr = S_OK;
1989 else
1991 PropertyStorage_DestroyDictionaries(ps);
1992 HeapFree(GetProcessHeap(), 0, ps);
1995 return hr;
1998 static HRESULT PropertyStorage_ConstructEmpty(IStream *stm,
1999 REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
2001 PropertyStorage_impl *ps;
2002 HRESULT hr;
2004 assert(pps);
2005 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2006 if (SUCCEEDED(hr))
2008 ps->format = 0;
2009 ps->grfFlags = grfFlags;
2010 if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2011 ps->format = 1;
2012 /* default to Unicode unless told not to, as specified here:
2013 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2015 if (ps->grfFlags & PROPSETFLAG_ANSI)
2016 ps->codePage = GetACP();
2017 else
2018 ps->codePage = CP_UNICODE;
2019 ps->locale = LOCALE_SYSTEM_DEFAULT;
2020 TRACE("Code page is %d, locale is %ld\n", ps->codePage, ps->locale);
2021 *pps = (IPropertyStorage *)ps;
2022 TRACE("PropertyStorage %p constructed\n", ps);
2023 hr = S_OK;
2025 return hr;
2029 /***********************************************************************
2030 * Implementation of IPropertySetStorage
2033 /************************************************************************
2034 * IPropertySetStorage_fnQueryInterface (IUnknown)
2036 * This method forwards to the common QueryInterface implementation
2038 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
2039 IPropertySetStorage *ppstg,
2040 REFIID riid,
2041 void** ppvObject)
2043 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2044 return IStorage_QueryInterface( (IStorage*)This, riid, ppvObject );
2047 /************************************************************************
2048 * IPropertySetStorage_fnAddRef (IUnknown)
2050 * This method forwards to the common AddRef implementation
2052 static ULONG WINAPI IPropertySetStorage_fnAddRef(
2053 IPropertySetStorage *ppstg)
2055 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2056 return IStorage_AddRef( (IStorage*)This );
2059 /************************************************************************
2060 * IPropertySetStorage_fnRelease (IUnknown)
2062 * This method forwards to the common Release implementation
2064 static ULONG WINAPI IPropertySetStorage_fnRelease(
2065 IPropertySetStorage *ppstg)
2067 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2068 return IStorage_Release( (IStorage*)This );
2071 /************************************************************************
2072 * IPropertySetStorage_fnCreate (IPropertySetStorage)
2074 static HRESULT WINAPI IPropertySetStorage_fnCreate(
2075 IPropertySetStorage *ppstg,
2076 REFFMTID rfmtid,
2077 const CLSID* pclsid,
2078 DWORD grfFlags,
2079 DWORD grfMode,
2080 IPropertyStorage** ppprstg)
2082 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2083 WCHAR name[CCH_MAX_PROPSTG_NAME];
2084 IStream *stm = NULL;
2085 HRESULT r;
2087 TRACE("%p %s %08lx %08lx %p\n", This, debugstr_guid(rfmtid), grfFlags,
2088 grfMode, ppprstg);
2090 /* be picky */
2091 if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
2093 r = STG_E_INVALIDFLAG;
2094 goto end;
2097 if (!rfmtid)
2099 r = E_INVALIDARG;
2100 goto end;
2103 /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
2104 * storage, not a stream. For now, disallow it.
2106 if (grfFlags & PROPSETFLAG_NONSIMPLE)
2108 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2109 r = STG_E_INVALIDFLAG;
2110 goto end;
2113 r = FmtIdToPropStgName(rfmtid, name);
2114 if (FAILED(r))
2115 goto end;
2117 r = IStorage_CreateStream( (IStorage*)This, name, grfMode, 0, 0, &stm );
2118 if (FAILED(r))
2119 goto end;
2121 r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg);
2123 end:
2124 TRACE("returning 0x%08lx\n", r);
2125 return r;
2128 /************************************************************************
2129 * IPropertySetStorage_fnOpen (IPropertySetStorage)
2131 static HRESULT WINAPI IPropertySetStorage_fnOpen(
2132 IPropertySetStorage *ppstg,
2133 REFFMTID rfmtid,
2134 DWORD grfMode,
2135 IPropertyStorage** ppprstg)
2137 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2138 IStream *stm = NULL;
2139 WCHAR name[CCH_MAX_PROPSTG_NAME];
2140 HRESULT r;
2142 TRACE("%p %s %08lx %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
2144 /* be picky */
2145 if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
2146 grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
2148 r = STG_E_INVALIDFLAG;
2149 goto end;
2152 if (!rfmtid)
2154 r = E_INVALIDARG;
2155 goto end;
2158 r = FmtIdToPropStgName(rfmtid, name);
2159 if (FAILED(r))
2160 goto end;
2162 r = IStorage_OpenStream((IStorage*) This, name, 0, grfMode, 0, &stm );
2163 if (FAILED(r))
2164 goto end;
2166 r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg);
2168 end:
2169 TRACE("returning 0x%08lx\n", r);
2170 return r;
2173 /************************************************************************
2174 * IPropertySetStorage_fnDelete (IPropertySetStorage)
2176 static HRESULT WINAPI IPropertySetStorage_fnDelete(
2177 IPropertySetStorage *ppstg,
2178 REFFMTID rfmtid)
2180 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2181 IStorage *stg = NULL;
2182 WCHAR name[CCH_MAX_PROPSTG_NAME];
2183 HRESULT r;
2185 TRACE("%p %s\n", This, debugstr_guid(rfmtid));
2187 if (!rfmtid)
2188 return E_INVALIDARG;
2190 r = FmtIdToPropStgName(rfmtid, name);
2191 if (FAILED(r))
2192 return r;
2194 stg = (IStorage*) This;
2195 return IStorage_DestroyElement(stg, name);
2198 /************************************************************************
2199 * IPropertySetStorage_fnEnum (IPropertySetStorage)
2201 static HRESULT WINAPI IPropertySetStorage_fnEnum(
2202 IPropertySetStorage *ppstg,
2203 IEnumSTATPROPSETSTG** ppenum)
2205 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2206 FIXME("%p\n", This);
2207 return E_NOTIMPL;
2211 /***********************************************************************
2212 * vtables
2214 const IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
2216 IPropertySetStorage_fnQueryInterface,
2217 IPropertySetStorage_fnAddRef,
2218 IPropertySetStorage_fnRelease,
2219 IPropertySetStorage_fnCreate,
2220 IPropertySetStorage_fnOpen,
2221 IPropertySetStorage_fnDelete,
2222 IPropertySetStorage_fnEnum
2225 static const IPropertyStorageVtbl IPropertyStorage_Vtbl =
2227 IPropertyStorage_fnQueryInterface,
2228 IPropertyStorage_fnAddRef,
2229 IPropertyStorage_fnRelease,
2230 IPropertyStorage_fnReadMultiple,
2231 IPropertyStorage_fnWriteMultiple,
2232 IPropertyStorage_fnDeleteMultiple,
2233 IPropertyStorage_fnReadPropertyNames,
2234 IPropertyStorage_fnWritePropertyNames,
2235 IPropertyStorage_fnDeletePropertyNames,
2236 IPropertyStorage_fnCommit,
2237 IPropertyStorage_fnRevert,
2238 IPropertyStorage_fnEnum,
2239 IPropertyStorage_fnSetTimes,
2240 IPropertyStorage_fnSetClass,
2241 IPropertyStorage_fnStat,
2244 /***********************************************************************
2245 * Format ID <-> name conversion
2247 static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
2248 'I','n','f','o','r','m','a','t','i','o','n',0 };
2249 static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t',
2250 'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
2252 #define BITS_PER_BYTE 8
2253 #define CHARMASK 0x1f
2254 #define BITS_IN_CHARMASK 5
2255 #define NUM_ALPHA_CHARS 26
2257 /***********************************************************************
2258 * FmtIdToPropStgName [ole32.@]
2259 * Returns the storage name of the format ID rfmtid.
2260 * PARAMS
2261 * rfmtid [I] Format ID for which to return a storage name
2262 * str [O] Storage name associated with rfmtid.
2264 * RETURNS
2265 * E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise.
2267 * NOTES
2268 * str must be at least CCH_MAX_PROPSTG_NAME characters in length.
2269 * Based on the algorithm described here:
2270 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2272 HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str)
2274 static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
2276 TRACE("%s, %p\n", debugstr_guid(rfmtid), str);
2278 if (!rfmtid) return E_INVALIDARG;
2279 if (!str) return E_INVALIDARG;
2281 if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
2282 lstrcpyW(str, szSummaryInfo);
2283 else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
2284 lstrcpyW(str, szDocSummaryInfo);
2285 else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid))
2286 lstrcpyW(str, szDocSummaryInfo);
2287 else
2289 BYTE *fmtptr;
2290 WCHAR *pstr = str;
2291 ULONG bitsRemaining = BITS_PER_BYTE;
2293 *pstr++ = 5;
2294 for (fmtptr = (BYTE *)rfmtid; fmtptr < (BYTE *)rfmtid + sizeof(FMTID); )
2296 ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
2298 if (bitsRemaining >= BITS_IN_CHARMASK)
2300 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
2301 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
2302 *pstr <= 'z')
2303 *pstr += 'A' - 'a';
2304 pstr++;
2305 bitsRemaining -= BITS_IN_CHARMASK;
2306 if (bitsRemaining == 0)
2308 fmtptr++;
2309 bitsRemaining = BITS_PER_BYTE;
2312 else
2314 if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID))
2315 i |= *fmtptr << bitsRemaining;
2316 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
2317 bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK;
2320 *pstr = 0;
2322 TRACE("returning %s\n", debugstr_w(str));
2323 return S_OK;
2326 /***********************************************************************
2327 * PropStgNameToFmtId [ole32.@]
2328 * Returns the format ID corresponding to the given name.
2329 * PARAMS
2330 * str [I] Storage name to convert to a format ID.
2331 * rfmtid [O] Format ID corresponding to str.
2333 * RETURNS
2334 * E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to
2335 * a format ID, S_OK otherwise.
2337 * NOTES
2338 * Based on the algorithm described here:
2339 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2341 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid)
2343 HRESULT hr = STG_E_INVALIDNAME;
2345 TRACE("%s, %p\n", debugstr_w(str), rfmtid);
2347 if (!rfmtid) return E_INVALIDARG;
2348 if (!str) return STG_E_INVALIDNAME;
2350 if (!lstrcmpiW(str, szDocSummaryInfo))
2352 memcpy(rfmtid, &FMTID_DocSummaryInformation, sizeof(*rfmtid));
2353 hr = S_OK;
2355 else if (!lstrcmpiW(str, szSummaryInfo))
2357 memcpy(rfmtid, &FMTID_SummaryInformation, sizeof(*rfmtid));
2358 hr = S_OK;
2360 else
2362 ULONG bits;
2363 BYTE *fmtptr = (BYTE *)rfmtid - 1;
2364 const WCHAR *pstr = str;
2366 memset(rfmtid, 0, sizeof(*rfmtid));
2367 for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE;
2368 bits += BITS_IN_CHARMASK)
2370 ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored;
2371 WCHAR wc;
2373 if (bitsUsed == 0)
2374 fmtptr++;
2375 wc = *++pstr - 'A';
2376 if (wc > NUM_ALPHA_CHARS)
2378 wc += 'A' - 'a';
2379 if (wc > NUM_ALPHA_CHARS)
2381 wc += 'a' - '0' + NUM_ALPHA_CHARS;
2382 if (wc > CHARMASK)
2384 WARN("invalid character (%d)\n", *pstr);
2385 goto end;
2389 *fmtptr |= wc << bitsUsed;
2390 bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK);
2391 if (bitsStored < BITS_IN_CHARMASK)
2393 wc >>= BITS_PER_BYTE - bitsUsed;
2394 if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE)
2396 if (wc != 0)
2398 WARN("extra bits\n");
2399 goto end;
2401 break;
2403 fmtptr++;
2404 *fmtptr |= (BYTE)wc;
2407 hr = S_OK;
2409 end:
2410 return hr;