ddraw/tests: Add another invalid arguments test for surface QI.
[wine.git] / dlls / ole32 / stg_prop.c
bloba2598832bf118f437ae5b3931cc82ca8cefd60ad
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
12 * Copyright 2005 Juan Lang
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 * TODO:
29 * - I don't honor the maximum property set size.
30 * - Certain bogus files could result in reading past the end of a buffer.
31 * - Mac-generated files won't be read correctly, even if they're little
32 * endian, because I disregard whether the generator was a Mac. This means
33 * strings will probably be munged (as I don't understand Mac scripts.)
34 * - Not all PROPVARIANT types are supported.
35 * - User defined properties are not supported, see comment in
36 * PropertyStorage_ReadFromStream
39 #include "config.h"
40 #include "wine/port.h"
42 #include <assert.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
48 #define COBJMACROS
49 #define NONAMELESSUNION
51 #include "windef.h"
52 #include "winbase.h"
53 #include "winnls.h"
54 #include "winuser.h"
55 #include "wine/unicode.h"
56 #include "wine/debug.h"
57 #include "dictionary.h"
58 #include "storage32.h"
59 #include "enumx.h"
60 #include "oleauto.h"
62 WINE_DEFAULT_DEBUG_CHANNEL(storage);
64 static inline StorageImpl *impl_from_IPropertySetStorage( IPropertySetStorage *iface )
66 return CONTAINING_RECORD(iface, StorageImpl, base.IPropertySetStorage_iface);
69 /* These are documented in MSDN,
70 * but they don't seem to be in any header file.
72 #define PROPSETHDR_BYTEORDER_MAGIC 0xfffe
73 #define PROPSETHDR_OSVER_KIND_WIN16 0
74 #define PROPSETHDR_OSVER_KIND_MAC 1
75 #define PROPSETHDR_OSVER_KIND_WIN32 2
77 #define CP_UNICODE 1200
79 #define MAX_VERSION_0_PROP_NAME_LENGTH 256
81 #define CFTAG_WINDOWS (-1L)
82 #define CFTAG_MACINTOSH (-2L)
83 #define CFTAG_FMTID (-3L)
84 #define CFTAG_NODATA 0L
86 typedef struct tagPROPERTYSETHEADER
88 WORD wByteOrder; /* always 0xfffe */
89 WORD wFormat; /* can be zero or one */
90 DWORD dwOSVer; /* OS version of originating system */
91 CLSID clsid; /* application CLSID */
92 DWORD reserved; /* always 1 */
93 } PROPERTYSETHEADER;
95 typedef struct tagFORMATIDOFFSET
97 FMTID fmtid;
98 DWORD dwOffset; /* from beginning of stream */
99 } FORMATIDOFFSET;
101 typedef struct tagPROPERTYSECTIONHEADER
103 DWORD cbSection;
104 DWORD cProperties;
105 } PROPERTYSECTIONHEADER;
107 typedef struct tagPROPERTYIDOFFSET
109 DWORD propid;
110 DWORD dwOffset; /* from beginning of section */
111 } PROPERTYIDOFFSET;
113 typedef struct tagPropertyStorage_impl PropertyStorage_impl;
115 /* Initializes the property storage from the stream (and undoes any uncommitted
116 * changes in the process.) Returns an error if there is an error reading or
117 * if the stream format doesn't match what's expected.
119 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *);
121 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *);
123 /* Creates the dictionaries used by the property storage. If successful, all
124 * the dictionaries have been created. If failed, none has been. (This makes
125 * it a bit easier to deal with destroying them.)
127 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *);
129 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *);
131 /* Copies from propvar to prop. If propvar's type is VT_LPSTR, copies the
132 * string using PropertyStorage_StringCopy.
134 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
135 const PROPVARIANT *propvar, LCID targetCP, LCID srcCP);
137 /* Copies the string src, which is encoded using code page srcCP, and returns
138 * it in *dst, in the code page specified by targetCP. The returned string is
139 * allocated using CoTaskMemAlloc.
140 * If srcCP is CP_UNICODE, src is in fact an LPCWSTR. Similarly, if targetCP
141 * is CP_UNICODE, the returned string is in fact an LPWSTR.
142 * Returns S_OK on success, something else on failure.
144 static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
145 LCID targetCP);
147 static const IPropertyStorageVtbl IPropertyStorage_Vtbl;
148 static const IEnumSTATPROPSETSTGVtbl IEnumSTATPROPSETSTG_Vtbl;
149 static const IEnumSTATPROPSTGVtbl IEnumSTATPROPSTG_Vtbl;
150 static HRESULT create_EnumSTATPROPSETSTG(StorageImpl *, IEnumSTATPROPSETSTG**);
151 static HRESULT create_EnumSTATPROPSTG(PropertyStorage_impl *, IEnumSTATPROPSTG**);
153 /***********************************************************************
154 * Implementation of IPropertyStorage
156 struct tagPropertyStorage_impl
158 IPropertyStorage IPropertyStorage_iface;
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;
177 static inline PropertyStorage_impl *impl_from_IPropertyStorage(IPropertyStorage *iface)
179 return CONTAINING_RECORD(iface, PropertyStorage_impl, IPropertyStorage_iface);
182 /************************************************************************
183 * IPropertyStorage_fnQueryInterface (IPropertyStorage)
185 static HRESULT WINAPI IPropertyStorage_fnQueryInterface(
186 IPropertyStorage *iface,
187 REFIID riid,
188 void** ppvObject)
190 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
192 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
194 if (!ppvObject)
195 return E_INVALIDARG;
197 *ppvObject = 0;
199 if (IsEqualGUID(&IID_IUnknown, riid) ||
200 IsEqualGUID(&IID_IPropertyStorage, riid))
202 IPropertyStorage_AddRef(iface);
203 *ppvObject = iface;
204 return S_OK;
207 return E_NOINTERFACE;
210 /************************************************************************
211 * IPropertyStorage_fnAddRef (IPropertyStorage)
213 static ULONG WINAPI IPropertyStorage_fnAddRef(
214 IPropertyStorage *iface)
216 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
217 return InterlockedIncrement(&This->ref);
220 /************************************************************************
221 * IPropertyStorage_fnRelease (IPropertyStorage)
223 static ULONG WINAPI IPropertyStorage_fnRelease(
224 IPropertyStorage *iface)
226 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
227 ULONG ref;
229 ref = InterlockedDecrement(&This->ref);
230 if (ref == 0)
232 TRACE("Destroying %p\n", This);
233 if (This->dirty)
234 IPropertyStorage_Commit(iface, STGC_DEFAULT);
235 IStream_Release(This->stm);
236 This->cs.DebugInfo->Spare[0] = 0;
237 DeleteCriticalSection(&This->cs);
238 PropertyStorage_DestroyDictionaries(This);
239 HeapFree(GetProcessHeap(), 0, This);
241 return ref;
244 static PROPVARIANT *PropertyStorage_FindProperty(PropertyStorage_impl *This,
245 DWORD propid)
247 PROPVARIANT *ret = NULL;
249 dictionary_find(This->propid_to_prop, UlongToPtr(propid), (void **)&ret);
250 TRACE("returning %p\n", ret);
251 return ret;
254 /* Returns NULL if name is NULL. */
255 static PROPVARIANT *PropertyStorage_FindPropertyByName(
256 PropertyStorage_impl *This, LPCWSTR name)
258 PROPVARIANT *ret = NULL;
259 void *propid;
261 if (!name)
262 return NULL;
263 if (This->codePage == CP_UNICODE)
265 if (dictionary_find(This->name_to_propid, name, &propid))
266 ret = PropertyStorage_FindProperty(This, PtrToUlong(propid));
268 else
270 LPSTR ansiName;
271 HRESULT hr = PropertyStorage_StringCopy((LPCSTR)name, CP_UNICODE,
272 &ansiName, This->codePage);
274 if (SUCCEEDED(hr))
276 if (dictionary_find(This->name_to_propid, ansiName, &propid))
277 ret = PropertyStorage_FindProperty(This, PtrToUlong(propid));
278 CoTaskMemFree(ansiName);
281 TRACE("returning %p\n", ret);
282 return ret;
285 static LPWSTR PropertyStorage_FindPropertyNameById(PropertyStorage_impl *This,
286 DWORD propid)
288 LPWSTR ret = NULL;
290 dictionary_find(This->propid_to_name, UlongToPtr(propid), (void **)&ret);
291 TRACE("returning %p\n", ret);
292 return ret;
295 /************************************************************************
296 * IPropertyStorage_fnReadMultiple (IPropertyStorage)
298 static HRESULT WINAPI IPropertyStorage_fnReadMultiple(
299 IPropertyStorage* iface,
300 ULONG cpspec,
301 const PROPSPEC rgpspec[],
302 PROPVARIANT rgpropvar[])
304 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
305 HRESULT hr = S_OK;
306 ULONG i;
308 TRACE("(%p, %d, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
310 if (!cpspec)
311 return S_FALSE;
312 if (!rgpspec || !rgpropvar)
313 return E_INVALIDARG;
314 EnterCriticalSection(&This->cs);
315 for (i = 0; i < cpspec; i++)
317 PropVariantInit(&rgpropvar[i]);
318 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
320 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
321 rgpspec[i].u.lpwstr);
323 if (prop)
324 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop, GetACP(),
325 This->codePage);
327 else
329 switch (rgpspec[i].u.propid)
331 case PID_CODEPAGE:
332 rgpropvar[i].vt = VT_I2;
333 rgpropvar[i].u.iVal = This->codePage;
334 break;
335 case PID_LOCALE:
336 rgpropvar[i].vt = VT_I4;
337 rgpropvar[i].u.lVal = This->locale;
338 break;
339 default:
341 PROPVARIANT *prop = PropertyStorage_FindProperty(This,
342 rgpspec[i].u.propid);
344 if (prop)
345 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop,
346 GetACP(), This->codePage);
347 else
348 hr = S_FALSE;
353 LeaveCriticalSection(&This->cs);
354 return hr;
357 static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
358 LCID dstCP)
360 HRESULT hr = S_OK;
361 int len;
363 TRACE("%s, %p, %d, %d\n",
364 srcCP == CP_UNICODE ? debugstr_w((LPCWSTR)src) : debugstr_a(src), dst,
365 dstCP, srcCP);
366 assert(src);
367 assert(dst);
368 *dst = NULL;
369 if (dstCP == srcCP)
371 size_t len;
373 if (dstCP == CP_UNICODE)
374 len = (strlenW((LPCWSTR)src) + 1) * sizeof(WCHAR);
375 else
376 len = strlen(src) + 1;
377 *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
378 if (!*dst)
379 hr = STG_E_INSUFFICIENTMEMORY;
380 else
381 memcpy(*dst, src, len);
383 else
385 if (dstCP == CP_UNICODE)
387 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
388 *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
389 if (!*dst)
390 hr = STG_E_INSUFFICIENTMEMORY;
391 else
392 MultiByteToWideChar(srcCP, 0, src, -1, (LPWSTR)*dst, len);
394 else
396 LPCWSTR wideStr = NULL;
397 LPWSTR wideStr_tmp = NULL;
399 if (srcCP == CP_UNICODE)
400 wideStr = (LPCWSTR)src;
401 else
403 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
404 wideStr_tmp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
405 if (wideStr_tmp)
407 MultiByteToWideChar(srcCP, 0, src, -1, wideStr_tmp, len);
408 wideStr = wideStr_tmp;
410 else
411 hr = STG_E_INSUFFICIENTMEMORY;
413 if (SUCCEEDED(hr))
415 len = WideCharToMultiByte(dstCP, 0, wideStr, -1, NULL, 0,
416 NULL, NULL);
417 *dst = CoTaskMemAlloc(len);
418 if (!*dst)
419 hr = STG_E_INSUFFICIENTMEMORY;
420 else
422 BOOL defCharUsed = FALSE;
424 if (WideCharToMultiByte(dstCP, 0, wideStr, -1, *dst, len,
425 NULL, &defCharUsed) == 0 || defCharUsed)
427 CoTaskMemFree(*dst);
428 *dst = NULL;
429 hr = HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION);
433 HeapFree(GetProcessHeap(), 0, wideStr_tmp);
436 TRACE("returning 0x%08x (%s)\n", hr,
437 dstCP == CP_UNICODE ? debugstr_w((LPCWSTR)*dst) : debugstr_a(*dst));
438 return hr;
441 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
442 const PROPVARIANT *propvar, LCID targetCP, LCID srcCP)
444 HRESULT hr = S_OK;
446 assert(prop);
447 assert(propvar);
448 if (propvar->vt == VT_LPSTR)
450 hr = PropertyStorage_StringCopy(propvar->u.pszVal, srcCP,
451 &prop->u.pszVal, targetCP);
452 if (SUCCEEDED(hr))
453 prop->vt = VT_LPSTR;
455 else
456 PropVariantCopy(prop, propvar);
457 return hr;
460 /* Stores the property with id propid and value propvar into this property
461 * storage. lcid is ignored if propvar's type is not VT_LPSTR. If propvar's
462 * type is VT_LPSTR, converts the string using lcid as the source code page
463 * and This->codePage as the target code page before storing.
464 * As a side effect, may change This->format to 1 if the type of propvar is
465 * a version 1-only property.
467 static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
468 PROPID propid, const PROPVARIANT *propvar, LCID lcid)
470 HRESULT hr = S_OK;
471 PROPVARIANT *prop = PropertyStorage_FindProperty(This, propid);
473 assert(propvar);
474 if (propvar->vt & VT_BYREF || propvar->vt & VT_ARRAY)
475 This->format = 1;
476 switch (propvar->vt)
478 case VT_DECIMAL:
479 case VT_I1:
480 case VT_INT:
481 case VT_UINT:
482 case VT_VECTOR|VT_I1:
483 This->format = 1;
485 TRACE("Setting 0x%08x to type %d\n", propid, propvar->vt);
486 if (prop)
488 PropVariantClear(prop);
489 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
490 lcid);
492 else
494 prop = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
495 sizeof(PROPVARIANT));
496 if (prop)
498 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
499 lcid);
500 if (SUCCEEDED(hr))
502 dictionary_insert(This->propid_to_prop, UlongToPtr(propid), prop);
503 if (propid > This->highestProp)
504 This->highestProp = propid;
506 else
507 HeapFree(GetProcessHeap(), 0, prop);
509 else
510 hr = STG_E_INSUFFICIENTMEMORY;
512 return hr;
515 /* Adds the name srcName to the name dictionaries, mapped to property ID id.
516 * srcName is encoded in code page cp, and is converted to This->codePage.
517 * If cp is CP_UNICODE, srcName is actually a unicode string.
518 * As a side effect, may change This->format to 1 if srcName is too long for
519 * a version 0 property storage.
520 * Doesn't validate id.
522 static HRESULT PropertyStorage_StoreNameWithId(PropertyStorage_impl *This,
523 LPCSTR srcName, LCID cp, PROPID id)
525 LPSTR name;
526 HRESULT hr;
528 assert(srcName);
530 hr = PropertyStorage_StringCopy(srcName, cp, &name, This->codePage);
531 if (SUCCEEDED(hr))
533 if (This->codePage == CP_UNICODE)
535 if (lstrlenW((LPWSTR)name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
536 This->format = 1;
538 else
540 if (strlen(name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
541 This->format = 1;
543 TRACE("Adding prop name %s, propid %d\n",
544 This->codePage == CP_UNICODE ? debugstr_w((LPCWSTR)name) :
545 debugstr_a(name), id);
546 dictionary_insert(This->name_to_propid, name, UlongToPtr(id));
547 dictionary_insert(This->propid_to_name, UlongToPtr(id), name);
549 return hr;
552 /************************************************************************
553 * IPropertyStorage_fnWriteMultiple (IPropertyStorage)
555 static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
556 IPropertyStorage* iface,
557 ULONG cpspec,
558 const PROPSPEC rgpspec[],
559 const PROPVARIANT rgpropvar[],
560 PROPID propidNameFirst)
562 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
563 HRESULT hr = S_OK;
564 ULONG i;
566 TRACE("(%p, %d, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
568 if (cpspec && (!rgpspec || !rgpropvar))
569 return E_INVALIDARG;
570 if (!(This->grfMode & STGM_READWRITE))
571 return STG_E_ACCESSDENIED;
572 EnterCriticalSection(&This->cs);
573 This->dirty = TRUE;
574 This->originatorOS = (DWORD)MAKELONG(LOWORD(GetVersion()),
575 PROPSETHDR_OSVER_KIND_WIN32) ;
576 for (i = 0; i < cpspec; i++)
578 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
580 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
581 rgpspec[i].u.lpwstr);
583 if (prop)
584 PropVariantCopy(prop, &rgpropvar[i]);
585 else
587 /* Note that I don't do the special cases here that I do below,
588 * because naming the special PIDs isn't supported.
590 if (propidNameFirst < PID_FIRST_USABLE ||
591 propidNameFirst >= PID_MIN_READONLY)
592 hr = STG_E_INVALIDPARAMETER;
593 else
595 PROPID nextId = max(propidNameFirst, This->highestProp + 1);
597 hr = PropertyStorage_StoreNameWithId(This,
598 (LPCSTR)rgpspec[i].u.lpwstr, CP_UNICODE, nextId);
599 if (SUCCEEDED(hr))
600 hr = PropertyStorage_StorePropWithId(This, nextId,
601 &rgpropvar[i], GetACP());
605 else
607 switch (rgpspec[i].u.propid)
609 case PID_DICTIONARY:
610 /* Can't set the dictionary */
611 hr = STG_E_INVALIDPARAMETER;
612 break;
613 case PID_CODEPAGE:
614 /* Can only set the code page if nothing else has been set */
615 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
616 rgpropvar[i].vt == VT_I2)
618 This->codePage = rgpropvar[i].u.iVal;
619 if (This->codePage == CP_UNICODE)
620 This->grfFlags &= ~PROPSETFLAG_ANSI;
621 else
622 This->grfFlags |= PROPSETFLAG_ANSI;
624 else
625 hr = STG_E_INVALIDPARAMETER;
626 break;
627 case PID_LOCALE:
628 /* Can only set the locale if nothing else has been set */
629 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
630 rgpropvar[i].vt == VT_I4)
631 This->locale = rgpropvar[i].u.lVal;
632 else
633 hr = STG_E_INVALIDPARAMETER;
634 break;
635 case PID_ILLEGAL:
636 /* silently ignore like MSDN says */
637 break;
638 default:
639 if (rgpspec[i].u.propid >= PID_MIN_READONLY)
640 hr = STG_E_INVALIDPARAMETER;
641 else
642 hr = PropertyStorage_StorePropWithId(This,
643 rgpspec[i].u.propid, &rgpropvar[i], GetACP());
647 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
648 IPropertyStorage_Commit(iface, STGC_DEFAULT);
649 LeaveCriticalSection(&This->cs);
650 return hr;
653 /************************************************************************
654 * IPropertyStorage_fnDeleteMultiple (IPropertyStorage)
656 static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
657 IPropertyStorage* iface,
658 ULONG cpspec,
659 const PROPSPEC rgpspec[])
661 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
662 ULONG i;
663 HRESULT hr;
665 TRACE("(%p, %d, %p)\n", iface, cpspec, rgpspec);
667 if (cpspec && !rgpspec)
668 return E_INVALIDARG;
669 if (!(This->grfMode & STGM_READWRITE))
670 return STG_E_ACCESSDENIED;
671 hr = S_OK;
672 EnterCriticalSection(&This->cs);
673 This->dirty = TRUE;
674 for (i = 0; i < cpspec; i++)
676 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
678 void *propid;
680 if (dictionary_find(This->name_to_propid, rgpspec[i].u.lpwstr, &propid))
681 dictionary_remove(This->propid_to_prop, propid);
683 else
685 if (rgpspec[i].u.propid >= PID_FIRST_USABLE &&
686 rgpspec[i].u.propid < PID_MIN_READONLY)
687 dictionary_remove(This->propid_to_prop, UlongToPtr(rgpspec[i].u.propid));
688 else
689 hr = STG_E_INVALIDPARAMETER;
692 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
693 IPropertyStorage_Commit(iface, STGC_DEFAULT);
694 LeaveCriticalSection(&This->cs);
695 return hr;
698 /************************************************************************
699 * IPropertyStorage_fnReadPropertyNames (IPropertyStorage)
701 static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames(
702 IPropertyStorage* iface,
703 ULONG cpropid,
704 const PROPID rgpropid[],
705 LPOLESTR rglpwstrName[])
707 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
708 ULONG i;
709 HRESULT hr = S_FALSE;
711 TRACE("(%p, %d, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
713 if (cpropid && (!rgpropid || !rglpwstrName))
714 return E_INVALIDARG;
715 EnterCriticalSection(&This->cs);
716 for (i = 0; i < cpropid && SUCCEEDED(hr); i++)
718 LPWSTR name = PropertyStorage_FindPropertyNameById(This, rgpropid[i]);
720 if (name)
722 size_t len = lstrlenW(name);
724 hr = S_OK;
725 rglpwstrName[i] = CoTaskMemAlloc((len + 1) * sizeof(WCHAR));
726 if (rglpwstrName[i])
727 memcpy(rglpwstrName[i], name, (len + 1) * sizeof(WCHAR));
728 else
729 hr = STG_E_INSUFFICIENTMEMORY;
731 else
732 rglpwstrName[i] = NULL;
734 LeaveCriticalSection(&This->cs);
735 return hr;
738 /************************************************************************
739 * IPropertyStorage_fnWritePropertyNames (IPropertyStorage)
741 static HRESULT WINAPI IPropertyStorage_fnWritePropertyNames(
742 IPropertyStorage* iface,
743 ULONG cpropid,
744 const PROPID rgpropid[],
745 const LPOLESTR rglpwstrName[])
747 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
748 ULONG i;
749 HRESULT hr;
751 TRACE("(%p, %d, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
753 if (cpropid && (!rgpropid || !rglpwstrName))
754 return E_INVALIDARG;
755 if (!(This->grfMode & STGM_READWRITE))
756 return STG_E_ACCESSDENIED;
757 hr = S_OK;
758 EnterCriticalSection(&This->cs);
759 This->dirty = TRUE;
760 for (i = 0; SUCCEEDED(hr) && i < cpropid; i++)
762 if (rgpropid[i] != PID_ILLEGAL)
763 hr = PropertyStorage_StoreNameWithId(This, (LPCSTR)rglpwstrName[i],
764 CP_UNICODE, rgpropid[i]);
766 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
767 IPropertyStorage_Commit(iface, STGC_DEFAULT);
768 LeaveCriticalSection(&This->cs);
769 return hr;
772 /************************************************************************
773 * IPropertyStorage_fnDeletePropertyNames (IPropertyStorage)
775 static HRESULT WINAPI IPropertyStorage_fnDeletePropertyNames(
776 IPropertyStorage* iface,
777 ULONG cpropid,
778 const PROPID rgpropid[])
780 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
781 ULONG i;
782 HRESULT hr;
784 TRACE("(%p, %d, %p)\n", iface, cpropid, rgpropid);
786 if (cpropid && !rgpropid)
787 return E_INVALIDARG;
788 if (!(This->grfMode & STGM_READWRITE))
789 return STG_E_ACCESSDENIED;
790 hr = S_OK;
791 EnterCriticalSection(&This->cs);
792 This->dirty = TRUE;
793 for (i = 0; i < cpropid; i++)
795 LPWSTR name = NULL;
797 if (dictionary_find(This->propid_to_name, UlongToPtr(rgpropid[i]), (void **)&name))
799 dictionary_remove(This->propid_to_name, UlongToPtr(rgpropid[i]));
800 dictionary_remove(This->name_to_propid, name);
803 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
804 IPropertyStorage_Commit(iface, STGC_DEFAULT);
805 LeaveCriticalSection(&This->cs);
806 return hr;
809 /************************************************************************
810 * IPropertyStorage_fnCommit (IPropertyStorage)
812 static HRESULT WINAPI IPropertyStorage_fnCommit(
813 IPropertyStorage* iface,
814 DWORD grfCommitFlags)
816 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
817 HRESULT hr;
819 TRACE("(%p, 0x%08x)\n", iface, grfCommitFlags);
821 if (!(This->grfMode & STGM_READWRITE))
822 return STG_E_ACCESSDENIED;
823 EnterCriticalSection(&This->cs);
824 if (This->dirty)
825 hr = PropertyStorage_WriteToStream(This);
826 else
827 hr = S_OK;
828 LeaveCriticalSection(&This->cs);
829 return hr;
832 /************************************************************************
833 * IPropertyStorage_fnRevert (IPropertyStorage)
835 static HRESULT WINAPI IPropertyStorage_fnRevert(
836 IPropertyStorage* iface)
838 HRESULT hr;
839 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
841 TRACE("%p\n", iface);
843 EnterCriticalSection(&This->cs);
844 if (This->dirty)
846 PropertyStorage_DestroyDictionaries(This);
847 hr = PropertyStorage_CreateDictionaries(This);
848 if (SUCCEEDED(hr))
849 hr = PropertyStorage_ReadFromStream(This);
851 else
852 hr = S_OK;
853 LeaveCriticalSection(&This->cs);
854 return hr;
857 /************************************************************************
858 * IPropertyStorage_fnEnum (IPropertyStorage)
860 static HRESULT WINAPI IPropertyStorage_fnEnum(
861 IPropertyStorage* iface,
862 IEnumSTATPROPSTG** ppenum)
864 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
865 return create_EnumSTATPROPSTG(This, ppenum);
868 /************************************************************************
869 * IPropertyStorage_fnSetTimes (IPropertyStorage)
871 static HRESULT WINAPI IPropertyStorage_fnSetTimes(
872 IPropertyStorage* iface,
873 const FILETIME* pctime,
874 const FILETIME* patime,
875 const FILETIME* pmtime)
877 FIXME("\n");
878 return E_NOTIMPL;
881 /************************************************************************
882 * IPropertyStorage_fnSetClass (IPropertyStorage)
884 static HRESULT WINAPI IPropertyStorage_fnSetClass(
885 IPropertyStorage* iface,
886 REFCLSID clsid)
888 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
890 TRACE("%p, %s\n", iface, debugstr_guid(clsid));
892 if (!clsid)
893 return E_INVALIDARG;
894 if (!(This->grfMode & STGM_READWRITE))
895 return STG_E_ACCESSDENIED;
896 This->clsid = *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 = impl_from_IPropertyStorage(iface);
911 STATSTG stat;
912 HRESULT hr;
914 TRACE("%p, %p\n", iface, statpsstg);
916 if (!statpsstg)
917 return E_INVALIDARG;
919 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
920 if (SUCCEEDED(hr))
922 statpsstg->fmtid = This->fmtid;
923 statpsstg->clsid = This->clsid;
924 statpsstg->grfFlags = This->grfFlags;
925 statpsstg->mtime = stat.mtime;
926 statpsstg->ctime = stat.ctime;
927 statpsstg->atime = stat.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 = 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(a, b);
943 else
944 return lstrcmpiW(a, b);
946 else
948 TRACE("(%s, %s)\n", debugstr_a(a), debugstr_a(b));
949 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
950 return lstrcmpA(a, b);
951 else
952 return lstrcmpiA(a, 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("(%d, %d)\n", PtrToUlong(a), PtrToUlong(b));
965 return PtrToUlong(a) - PtrToUlong(b);
968 static void PropertyStorage_PropertyDestroy(void *k, void *d, void *extra)
970 PropVariantClear(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 lendian16toh 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] = lendian16toh(str[i]);
988 #else
989 #define PropertyStorage_ByteSwapString(s, l)
990 #endif
992 /* Reads the dictionary from the memory buffer beginning at ptr. Interprets
993 * the entries according to the values of This->codePage and This->locale.
994 * FIXME: there isn't any checking whether the read property extends past the
995 * end of the buffer.
997 static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This,
998 BYTE *ptr)
1000 DWORD numEntries, i;
1001 HRESULT hr = S_OK;
1003 assert(This->name_to_propid);
1004 assert(This->propid_to_name);
1006 StorageUtl_ReadDWord(ptr, 0, &numEntries);
1007 TRACE("Reading %d 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%08x, %d bytes\n", propid, cbEntry);
1019 /* Make sure the source string is NULL-terminated */
1020 if (This->codePage != CP_UNICODE)
1021 ptr[cbEntry - 1] = '\0';
1022 else
1023 *((LPWSTR)ptr + cbEntry / sizeof(WCHAR)) = '\0';
1024 hr = PropertyStorage_StoreNameWithId(This, (char*)ptr, This->codePage, propid);
1025 if (This->codePage == CP_UNICODE)
1027 /* Unicode entries are padded to DWORD boundaries */
1028 if (cbEntry % sizeof(DWORD))
1029 ptr += sizeof(DWORD) - (cbEntry % sizeof(DWORD));
1031 ptr += sizeof(DWORD) + cbEntry;
1033 return hr;
1036 static void* WINAPI Allocate_CoTaskMemAlloc(void *this, ULONG size)
1038 return CoTaskMemAlloc(size);
1041 /* FIXME: there isn't any checking whether the read property extends past the
1042 * end of the buffer.
1044 static HRESULT PropertyStorage_ReadProperty(PROPVARIANT *prop, const BYTE *data,
1045 UINT codepage, void* (WINAPI *allocate)(void *this, ULONG size), void *allocate_data)
1047 HRESULT hr = S_OK;
1049 assert(prop);
1050 assert(data);
1051 StorageUtl_ReadDWord(data, 0, (DWORD *)&prop->vt);
1052 data += sizeof(DWORD);
1053 switch (prop->vt)
1055 case VT_EMPTY:
1056 case VT_NULL:
1057 break;
1058 case VT_I1:
1059 prop->u.cVal = *(const char *)data;
1060 TRACE("Read char 0x%x ('%c')\n", prop->u.cVal, prop->u.cVal);
1061 break;
1062 case VT_UI1:
1063 prop->u.bVal = *data;
1064 TRACE("Read byte 0x%x\n", prop->u.bVal);
1065 break;
1066 case VT_I2:
1067 StorageUtl_ReadWord(data, 0, (WORD*)&prop->u.iVal);
1068 TRACE("Read short %d\n", prop->u.iVal);
1069 break;
1070 case VT_UI2:
1071 StorageUtl_ReadWord(data, 0, &prop->u.uiVal);
1072 TRACE("Read ushort %d\n", prop->u.uiVal);
1073 break;
1074 case VT_INT:
1075 case VT_I4:
1076 StorageUtl_ReadDWord(data, 0, (DWORD*)&prop->u.lVal);
1077 TRACE("Read long %d\n", prop->u.lVal);
1078 break;
1079 case VT_UINT:
1080 case VT_UI4:
1081 StorageUtl_ReadDWord(data, 0, &prop->u.ulVal);
1082 TRACE("Read ulong %d\n", prop->u.ulVal);
1083 break;
1084 case VT_LPSTR:
1086 DWORD count;
1088 StorageUtl_ReadDWord(data, 0, &count);
1089 if (codepage == CP_UNICODE && count % 2)
1091 WARN("Unicode string has odd number of bytes\n");
1092 hr = STG_E_INVALIDHEADER;
1094 else
1096 prop->u.pszVal = allocate(allocate_data, count);
1097 if (prop->u.pszVal)
1099 memcpy(prop->u.pszVal, data + sizeof(DWORD), count);
1100 /* This is stored in the code page specified in codepage.
1101 * Don't convert it, the caller will just store it as-is.
1103 if (codepage == CP_UNICODE)
1105 /* Make sure it's NULL-terminated */
1106 prop->u.pszVal[count / sizeof(WCHAR) - 1] = '\0';
1107 TRACE("Read string value %s\n",
1108 debugstr_w(prop->u.pwszVal));
1110 else
1112 /* Make sure it's NULL-terminated */
1113 prop->u.pszVal[count - 1] = '\0';
1114 TRACE("Read string value %s\n", debugstr_a(prop->u.pszVal));
1117 else
1118 hr = STG_E_INSUFFICIENTMEMORY;
1120 break;
1122 case VT_BSTR:
1124 DWORD count, wcount;
1126 StorageUtl_ReadDWord(data, 0, &count);
1127 if (codepage == CP_UNICODE && count % 2)
1129 WARN("Unicode string has odd number of bytes\n");
1130 hr = STG_E_INVALIDHEADER;
1132 else
1134 if (codepage == CP_UNICODE)
1135 wcount = count / 2;
1136 else
1137 wcount = MultiByteToWideChar(codepage, 0, (LPCSTR)(data + sizeof(DWORD)), count, NULL, 0);
1139 prop->u.bstrVal = SysAllocStringLen(NULL, wcount); /* FIXME: use allocator? */
1141 if (prop->u.bstrVal)
1143 if (codepage == CP_UNICODE)
1144 memcpy(prop->u.bstrVal, data + sizeof(DWORD), count);
1145 else
1146 MultiByteToWideChar(codepage, 0, (LPCSTR)(data + sizeof(DWORD)), count, prop->u.bstrVal, wcount);
1148 prop->u.bstrVal[wcount - 1] = '\0';
1149 TRACE("Read string value %s\n", debugstr_w(prop->u.bstrVal));
1151 else
1152 hr = STG_E_INSUFFICIENTMEMORY;
1154 break;
1156 case VT_BLOB:
1158 DWORD count;
1160 StorageUtl_ReadDWord(data, 0, &count);
1161 prop->u.blob.cbSize = count;
1162 prop->u.blob.pBlobData = allocate(allocate_data, count);
1163 if (prop->u.blob.pBlobData)
1165 memcpy(prop->u.blob.pBlobData, data + sizeof(DWORD), count);
1166 TRACE("Read blob value of size %d\n", count);
1168 else
1169 hr = STG_E_INSUFFICIENTMEMORY;
1170 break;
1172 case VT_LPWSTR:
1174 DWORD count;
1176 StorageUtl_ReadDWord(data, 0, &count);
1177 prop->u.pwszVal = allocate(allocate_data, count * sizeof(WCHAR));
1178 if (prop->u.pwszVal)
1180 memcpy(prop->u.pwszVal, data + sizeof(DWORD),
1181 count * sizeof(WCHAR));
1182 /* make sure string is NULL-terminated */
1183 prop->u.pwszVal[count - 1] = '\0';
1184 PropertyStorage_ByteSwapString(prop->u.pwszVal, count);
1185 TRACE("Read string value %s\n", debugstr_w(prop->u.pwszVal));
1187 else
1188 hr = STG_E_INSUFFICIENTMEMORY;
1189 break;
1191 case VT_FILETIME:
1192 StorageUtl_ReadULargeInteger(data, 0,
1193 (ULARGE_INTEGER *)&prop->u.filetime);
1194 break;
1195 case VT_CF:
1197 DWORD len = 0, tag = 0;
1199 StorageUtl_ReadDWord(data, 0, &len);
1200 StorageUtl_ReadDWord(data, 4, &tag);
1201 if (len > 8)
1203 len -= 8;
1204 prop->u.pclipdata = allocate(allocate_data, sizeof (CLIPDATA));
1205 prop->u.pclipdata->cbSize = len;
1206 prop->u.pclipdata->ulClipFmt = tag;
1207 prop->u.pclipdata->pClipData = allocate(allocate_data, len - sizeof(prop->u.pclipdata->ulClipFmt));
1208 memcpy(prop->u.pclipdata->pClipData, data+8, len - sizeof(prop->u.pclipdata->ulClipFmt));
1210 else
1211 hr = STG_E_INVALIDPARAMETER;
1213 break;
1214 default:
1215 FIXME("unsupported type %d\n", prop->vt);
1216 hr = STG_E_INVALIDPARAMETER;
1218 return hr;
1221 static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
1222 PROPERTYSETHEADER *hdr)
1224 BYTE buf[sizeof(PROPERTYSETHEADER)];
1225 ULONG count = 0;
1226 HRESULT hr;
1228 assert(stm);
1229 assert(hdr);
1230 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1231 if (SUCCEEDED(hr))
1233 if (count != sizeof(buf))
1235 WARN("read only %d\n", count);
1236 hr = STG_E_INVALIDHEADER;
1238 else
1240 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder),
1241 &hdr->wByteOrder);
1242 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat),
1243 &hdr->wFormat);
1244 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer),
1245 &hdr->dwOSVer);
1246 StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid),
1247 &hdr->clsid);
1248 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved),
1249 &hdr->reserved);
1252 TRACE("returning 0x%08x\n", hr);
1253 return hr;
1256 static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm,
1257 FORMATIDOFFSET *fmt)
1259 BYTE buf[sizeof(FORMATIDOFFSET)];
1260 ULONG count = 0;
1261 HRESULT hr;
1263 assert(stm);
1264 assert(fmt);
1265 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1266 if (SUCCEEDED(hr))
1268 if (count != sizeof(buf))
1270 WARN("read only %d\n", count);
1271 hr = STG_E_INVALIDHEADER;
1273 else
1275 StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid),
1276 &fmt->fmtid);
1277 StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset),
1278 &fmt->dwOffset);
1281 TRACE("returning 0x%08x\n", hr);
1282 return hr;
1285 static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
1286 PROPERTYSECTIONHEADER *hdr)
1288 BYTE buf[sizeof(PROPERTYSECTIONHEADER)];
1289 ULONG count = 0;
1290 HRESULT hr;
1292 assert(stm);
1293 assert(hdr);
1294 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1295 if (SUCCEEDED(hr))
1297 if (count != sizeof(buf))
1299 WARN("read only %d\n", count);
1300 hr = STG_E_INVALIDHEADER;
1302 else
1304 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1305 cbSection), &hdr->cbSection);
1306 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1307 cProperties), &hdr->cProperties);
1310 TRACE("returning 0x%08x\n", hr);
1311 return hr;
1314 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This)
1316 PROPERTYSETHEADER hdr;
1317 FORMATIDOFFSET fmtOffset;
1318 PROPERTYSECTIONHEADER sectionHdr;
1319 LARGE_INTEGER seek;
1320 ULONG i;
1321 STATSTG stat;
1322 HRESULT hr;
1323 BYTE *buf = NULL;
1324 ULONG count = 0;
1325 DWORD dictOffset = 0;
1327 This->dirty = FALSE;
1328 This->highestProp = 0;
1329 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
1330 if (FAILED(hr))
1331 goto end;
1332 if (stat.cbSize.u.HighPart)
1334 WARN("stream too big\n");
1335 /* maximum size varies, but it can't be this big */
1336 hr = STG_E_INVALIDHEADER;
1337 goto end;
1339 if (stat.cbSize.u.LowPart == 0)
1341 /* empty stream is okay */
1342 hr = S_OK;
1343 goto end;
1345 else if (stat.cbSize.u.LowPart < sizeof(PROPERTYSETHEADER) +
1346 sizeof(FORMATIDOFFSET))
1348 WARN("stream too small\n");
1349 hr = STG_E_INVALIDHEADER;
1350 goto end;
1352 seek.QuadPart = 0;
1353 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1354 if (FAILED(hr))
1355 goto end;
1356 hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr);
1357 /* I've only seen reserved == 1, but the article says I shouldn't disallow
1358 * higher values.
1360 if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1)
1362 WARN("bad magic in prop set header\n");
1363 hr = STG_E_INVALIDHEADER;
1364 goto end;
1366 if (hdr.wFormat != 0 && hdr.wFormat != 1)
1368 WARN("bad format version %d\n", hdr.wFormat);
1369 hr = STG_E_INVALIDHEADER;
1370 goto end;
1372 This->format = hdr.wFormat;
1373 This->clsid = hdr.clsid;
1374 This->originatorOS = hdr.dwOSVer;
1375 if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC)
1376 WARN("File comes from a Mac, strings will probably be screwed up\n");
1377 hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset);
1378 if (FAILED(hr))
1379 goto end;
1380 if (fmtOffset.dwOffset > stat.cbSize.u.LowPart)
1382 WARN("invalid offset %d (stream length is %d)\n", fmtOffset.dwOffset,
1383 stat.cbSize.u.LowPart);
1384 hr = STG_E_INVALIDHEADER;
1385 goto end;
1387 /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
1388 * follows not one, but two sections. The first contains the standard properties
1389 * for the document summary information, and the second consists of user-defined
1390 * properties. This is the only case in which multiple sections are
1391 * allowed.
1392 * Reading the second stream isn't implemented yet.
1394 hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, &sectionHdr);
1395 if (FAILED(hr))
1396 goto end;
1397 /* The section size includes the section header, so check it */
1398 if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER))
1400 WARN("section header too small, got %d\n", sectionHdr.cbSection);
1401 hr = STG_E_INVALIDHEADER;
1402 goto end;
1404 buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection -
1405 sizeof(PROPERTYSECTIONHEADER));
1406 if (!buf)
1408 hr = STG_E_INSUFFICIENTMEMORY;
1409 goto end;
1411 hr = IStream_Read(This->stm, buf, sectionHdr.cbSection -
1412 sizeof(PROPERTYSECTIONHEADER), &count);
1413 if (FAILED(hr))
1414 goto end;
1415 TRACE("Reading %d properties:\n", sectionHdr.cProperties);
1416 for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
1418 PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(buf +
1419 i * sizeof(PROPERTYIDOFFSET));
1421 if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) ||
1422 idOffset->dwOffset > sectionHdr.cbSection - sizeof(DWORD))
1423 hr = STG_E_INVALIDPOINTER;
1424 else
1426 if (idOffset->propid >= PID_FIRST_USABLE &&
1427 idOffset->propid < PID_MIN_READONLY && idOffset->propid >
1428 This->highestProp)
1429 This->highestProp = idOffset->propid;
1430 if (idOffset->propid == PID_DICTIONARY)
1432 /* Don't read the dictionary yet, its entries depend on the
1433 * code page. Just store the offset so we know to read it
1434 * later.
1436 dictOffset = idOffset->dwOffset;
1437 TRACE("Dictionary offset is %d\n", dictOffset);
1439 else
1441 PROPVARIANT prop;
1443 PropVariantInit(&prop);
1444 if (SUCCEEDED(PropertyStorage_ReadProperty(&prop,
1445 buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER),
1446 This->codePage, Allocate_CoTaskMemAlloc, NULL)))
1448 TRACE("Read property with ID 0x%08x, type %d\n",
1449 idOffset->propid, prop.vt);
1450 switch(idOffset->propid)
1452 case PID_CODEPAGE:
1453 if (prop.vt == VT_I2)
1454 This->codePage = (UINT)prop.u.iVal;
1455 break;
1456 case PID_LOCALE:
1457 if (prop.vt == VT_I4)
1458 This->locale = (LCID)prop.u.lVal;
1459 break;
1460 case PID_BEHAVIOR:
1461 if (prop.vt == VT_I4 && prop.u.lVal)
1462 This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
1463 /* The format should already be 1, but just in case */
1464 This->format = 1;
1465 break;
1466 default:
1467 hr = PropertyStorage_StorePropWithId(This,
1468 idOffset->propid, &prop, This->codePage);
1471 PropVariantClear(&prop);
1475 if (!This->codePage)
1477 /* default to Unicode unless told not to, as specified on msdn */
1478 if (This->grfFlags & PROPSETFLAG_ANSI)
1479 This->codePage = GetACP();
1480 else
1481 This->codePage = CP_UNICODE;
1483 if (!This->locale)
1484 This->locale = LOCALE_SYSTEM_DEFAULT;
1485 TRACE("Code page is %d, locale is %d\n", This->codePage, This->locale);
1486 if (dictOffset)
1487 hr = PropertyStorage_ReadDictionary(This,
1488 buf + dictOffset - sizeof(PROPERTYSECTIONHEADER));
1490 end:
1491 HeapFree(GetProcessHeap(), 0, buf);
1492 if (FAILED(hr))
1494 dictionary_destroy(This->name_to_propid);
1495 This->name_to_propid = NULL;
1496 dictionary_destroy(This->propid_to_name);
1497 This->propid_to_name = NULL;
1498 dictionary_destroy(This->propid_to_prop);
1499 This->propid_to_prop = NULL;
1501 return hr;
1504 static void PropertyStorage_MakeHeader(PropertyStorage_impl *This,
1505 PROPERTYSETHEADER *hdr)
1507 assert(hdr);
1508 StorageUtl_WriteWord((BYTE *)&hdr->wByteOrder, 0,
1509 PROPSETHDR_BYTEORDER_MAGIC);
1510 StorageUtl_WriteWord((BYTE *)&hdr->wFormat, 0, This->format);
1511 StorageUtl_WriteDWord((BYTE *)&hdr->dwOSVer, 0, This->originatorOS);
1512 StorageUtl_WriteGUID((BYTE *)&hdr->clsid, 0, &This->clsid);
1513 StorageUtl_WriteDWord((BYTE *)&hdr->reserved, 0, 1);
1516 static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl *This,
1517 FORMATIDOFFSET *fmtOffset)
1519 assert(fmtOffset);
1520 StorageUtl_WriteGUID((BYTE *)fmtOffset, 0, &This->fmtid);
1521 StorageUtl_WriteDWord((BYTE *)fmtOffset, offsetof(FORMATIDOFFSET, dwOffset),
1522 sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET));
1525 static void PropertyStorage_MakeSectionHdr(DWORD cbSection, DWORD numProps,
1526 PROPERTYSECTIONHEADER *hdr)
1528 assert(hdr);
1529 StorageUtl_WriteDWord((BYTE *)hdr, 0, cbSection);
1530 StorageUtl_WriteDWord((BYTE *)hdr,
1531 offsetof(PROPERTYSECTIONHEADER, cProperties), numProps);
1534 static void PropertyStorage_MakePropertyIdOffset(DWORD propid, DWORD dwOffset,
1535 PROPERTYIDOFFSET *propIdOffset)
1537 assert(propIdOffset);
1538 StorageUtl_WriteDWord((BYTE *)propIdOffset, 0, propid);
1539 StorageUtl_WriteDWord((BYTE *)propIdOffset,
1540 offsetof(PROPERTYIDOFFSET, dwOffset), dwOffset);
1543 static inline HRESULT PropertStorage_WriteWStringToStream(IStream *stm,
1544 LPCWSTR str, DWORD len, DWORD *written)
1546 #ifdef WORDS_BIGENDIAN
1547 WCHAR *leStr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1548 HRESULT hr;
1550 if (!leStr)
1551 return E_OUTOFMEMORY;
1552 memcpy(leStr, str, len * sizeof(WCHAR));
1553 PropertyStorage_ByteSwapString(leStr, len);
1554 hr = IStream_Write(stm, leStr, len, written);
1555 HeapFree(GetProcessHeap(), 0, leStr);
1556 return hr;
1557 #else
1558 return IStream_Write(stm, str, len, written);
1559 #endif
1562 struct DictionaryClosure
1564 HRESULT hr;
1565 DWORD bytesWritten;
1568 static BOOL PropertyStorage_DictionaryWriter(const void *key,
1569 const void *value, void *extra, void *closure)
1571 PropertyStorage_impl *This = extra;
1572 struct DictionaryClosure *c = closure;
1573 DWORD propid;
1574 ULONG count;
1576 assert(key);
1577 assert(closure);
1578 StorageUtl_WriteDWord((LPBYTE)&propid, 0, PtrToUlong(value));
1579 c->hr = IStream_Write(This->stm, &propid, sizeof(propid), &count);
1580 if (FAILED(c->hr))
1581 goto end;
1582 c->bytesWritten += sizeof(DWORD);
1583 if (This->codePage == CP_UNICODE)
1585 DWORD keyLen, pad = 0;
1587 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0,
1588 (lstrlenW((LPCWSTR)key) + 1) * sizeof(WCHAR));
1589 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1590 if (FAILED(c->hr))
1591 goto end;
1592 c->bytesWritten += sizeof(DWORD);
1593 c->hr = PropertStorage_WriteWStringToStream(This->stm, key, keyLen,
1594 &count);
1595 if (FAILED(c->hr))
1596 goto end;
1597 c->bytesWritten += keyLen * sizeof(WCHAR);
1598 if (keyLen % sizeof(DWORD))
1600 c->hr = IStream_Write(This->stm, &pad,
1601 sizeof(DWORD) - keyLen % sizeof(DWORD), &count);
1602 if (FAILED(c->hr))
1603 goto end;
1604 c->bytesWritten += sizeof(DWORD) - keyLen % sizeof(DWORD);
1607 else
1609 DWORD keyLen;
1611 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0, strlen((LPCSTR)key) + 1);
1612 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1613 if (FAILED(c->hr))
1614 goto end;
1615 c->bytesWritten += sizeof(DWORD);
1616 c->hr = IStream_Write(This->stm, key, keyLen, &count);
1617 if (FAILED(c->hr))
1618 goto end;
1619 c->bytesWritten += keyLen;
1621 end:
1622 return SUCCEEDED(c->hr);
1625 #define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)
1627 /* Writes the dictionary to the stream. Assumes without checking that the
1628 * dictionary isn't empty.
1630 static HRESULT PropertyStorage_WriteDictionaryToStream(
1631 PropertyStorage_impl *This, DWORD *sectionOffset)
1633 HRESULT hr;
1634 LARGE_INTEGER seek;
1635 PROPERTYIDOFFSET propIdOffset;
1636 ULONG count;
1637 DWORD dwTemp;
1638 struct DictionaryClosure closure;
1640 assert(sectionOffset);
1642 /* The dictionary's always the first property written, so seek to its
1643 * spot.
1645 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER);
1646 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1647 if (FAILED(hr))
1648 goto end;
1649 PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY, *sectionOffset,
1650 &propIdOffset);
1651 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1652 if (FAILED(hr))
1653 goto end;
1655 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1656 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1657 if (FAILED(hr))
1658 goto end;
1659 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0,
1660 dictionary_num_entries(This->name_to_propid));
1661 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1662 if (FAILED(hr))
1663 goto end;
1664 *sectionOffset += sizeof(dwTemp);
1666 closure.hr = S_OK;
1667 closure.bytesWritten = 0;
1668 dictionary_enumerate(This->name_to_propid, PropertyStorage_DictionaryWriter,
1669 &closure);
1670 hr = closure.hr;
1671 if (FAILED(hr))
1672 goto end;
1673 *sectionOffset += closure.bytesWritten;
1674 if (closure.bytesWritten % sizeof(DWORD))
1676 DWORD padding = sizeof(DWORD) - closure.bytesWritten % sizeof(DWORD);
1677 TRACE("adding %d bytes of padding\n", padding);
1678 *sectionOffset += padding;
1681 end:
1682 return hr;
1685 static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This,
1686 DWORD propNum, DWORD propid, const PROPVARIANT *var, DWORD *sectionOffset)
1688 HRESULT hr;
1689 LARGE_INTEGER seek;
1690 PROPERTYIDOFFSET propIdOffset;
1691 ULONG count;
1692 DWORD dwType, bytesWritten;
1694 assert(var);
1695 assert(sectionOffset);
1697 TRACE("%p, %d, 0x%08x, (%d), (%d)\n", This, propNum, propid, var->vt,
1698 *sectionOffset);
1700 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) +
1701 propNum * sizeof(PROPERTYIDOFFSET);
1702 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1703 if (FAILED(hr))
1704 goto end;
1705 PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset);
1706 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1707 if (FAILED(hr))
1708 goto end;
1710 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1711 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1712 if (FAILED(hr))
1713 goto end;
1714 StorageUtl_WriteDWord((LPBYTE)&dwType, 0, var->vt);
1715 hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count);
1716 if (FAILED(hr))
1717 goto end;
1718 *sectionOffset += sizeof(dwType);
1720 switch (var->vt)
1722 case VT_EMPTY:
1723 case VT_NULL:
1724 bytesWritten = 0;
1725 break;
1726 case VT_I1:
1727 case VT_UI1:
1728 hr = IStream_Write(This->stm, &var->u.cVal, sizeof(var->u.cVal),
1729 &count);
1730 bytesWritten = count;
1731 break;
1732 case VT_I2:
1733 case VT_UI2:
1735 WORD wTemp;
1737 StorageUtl_WriteWord((LPBYTE)&wTemp, 0, var->u.iVal);
1738 hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count);
1739 bytesWritten = count;
1740 break;
1742 case VT_I4:
1743 case VT_UI4:
1745 DWORD dwTemp;
1747 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, var->u.lVal);
1748 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1749 bytesWritten = count;
1750 break;
1752 case VT_LPSTR:
1754 DWORD len, dwTemp;
1756 if (This->codePage == CP_UNICODE)
1757 len = (lstrlenW(var->u.pwszVal) + 1) * sizeof(WCHAR);
1758 else
1759 len = lstrlenA(var->u.pszVal) + 1;
1760 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1761 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1762 if (FAILED(hr))
1763 goto end;
1764 hr = IStream_Write(This->stm, var->u.pszVal, len, &count);
1765 bytesWritten = count + sizeof(DWORD);
1766 break;
1768 case VT_LPWSTR:
1770 DWORD len = lstrlenW(var->u.pwszVal) + 1, dwTemp;
1772 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1773 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1774 if (FAILED(hr))
1775 goto end;
1776 hr = IStream_Write(This->stm, var->u.pwszVal, len * sizeof(WCHAR),
1777 &count);
1778 bytesWritten = count + sizeof(DWORD);
1779 break;
1781 case VT_FILETIME:
1783 FILETIME temp;
1785 StorageUtl_WriteULargeInteger((BYTE *)&temp, 0,
1786 (const ULARGE_INTEGER *)&var->u.filetime);
1787 hr = IStream_Write(This->stm, &temp, sizeof(FILETIME), &count);
1788 bytesWritten = count;
1789 break;
1791 case VT_CF:
1793 DWORD cf_hdr[2], len;
1795 len = var->u.pclipdata->cbSize;
1796 StorageUtl_WriteDWord((LPBYTE)&cf_hdr[0], 0, len + 8);
1797 StorageUtl_WriteDWord((LPBYTE)&cf_hdr[1], 0, var->u.pclipdata->ulClipFmt);
1798 hr = IStream_Write(This->stm, cf_hdr, sizeof(cf_hdr), &count);
1799 if (FAILED(hr))
1800 goto end;
1801 hr = IStream_Write(This->stm, var->u.pclipdata->pClipData,
1802 len - sizeof(var->u.pclipdata->ulClipFmt), &count);
1803 if (FAILED(hr))
1804 goto end;
1805 bytesWritten = count + sizeof cf_hdr;
1806 break;
1808 case VT_CLSID:
1810 CLSID temp;
1812 StorageUtl_WriteGUID((BYTE *)&temp, 0, var->u.puuid);
1813 hr = IStream_Write(This->stm, &temp, sizeof(temp), &count);
1814 bytesWritten = count;
1815 break;
1817 default:
1818 FIXME("unsupported type: %d\n", var->vt);
1819 return STG_E_INVALIDPARAMETER;
1822 if (SUCCEEDED(hr))
1824 *sectionOffset += bytesWritten;
1825 if (bytesWritten % sizeof(DWORD))
1827 DWORD padding = sizeof(DWORD) - bytesWritten % sizeof(DWORD);
1828 TRACE("adding %d bytes of padding\n", padding);
1829 *sectionOffset += padding;
1833 end:
1834 return hr;
1837 struct PropertyClosure
1839 HRESULT hr;
1840 DWORD propNum;
1841 DWORD *sectionOffset;
1844 static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value,
1845 void *extra, void *closure)
1847 PropertyStorage_impl *This = extra;
1848 struct PropertyClosure *c = closure;
1850 assert(key);
1851 assert(value);
1852 assert(extra);
1853 assert(closure);
1854 c->hr = PropertyStorage_WritePropertyToStream(This, c->propNum++,
1855 PtrToUlong(key), value, c->sectionOffset);
1856 return SUCCEEDED(c->hr);
1859 static HRESULT PropertyStorage_WritePropertiesToStream(
1860 PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset)
1862 struct PropertyClosure closure;
1864 assert(sectionOffset);
1865 closure.hr = S_OK;
1866 closure.propNum = startingPropNum;
1867 closure.sectionOffset = sectionOffset;
1868 dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter,
1869 &closure);
1870 return closure.hr;
1873 static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This)
1875 HRESULT hr;
1876 ULONG count = 0;
1877 LARGE_INTEGER seek = { {0} };
1878 PROPERTYSETHEADER hdr;
1879 FORMATIDOFFSET fmtOffset;
1881 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1882 if (FAILED(hr))
1883 goto end;
1884 PropertyStorage_MakeHeader(This, &hdr);
1885 hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count);
1886 if (FAILED(hr))
1887 goto end;
1888 if (count != sizeof(hdr))
1890 hr = STG_E_WRITEFAULT;
1891 goto end;
1894 PropertyStorage_MakeFmtIdOffset(This, &fmtOffset);
1895 hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count);
1896 if (FAILED(hr))
1897 goto end;
1898 if (count != sizeof(fmtOffset))
1900 hr = STG_E_WRITEFAULT;
1901 goto end;
1903 hr = S_OK;
1905 end:
1906 return hr;
1909 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This)
1911 PROPERTYSECTIONHEADER sectionHdr;
1912 HRESULT hr;
1913 ULONG count;
1914 LARGE_INTEGER seek;
1915 DWORD numProps, prop, sectionOffset, dwTemp;
1916 PROPVARIANT var;
1918 PropertyStorage_WriteHeadersToStream(This);
1920 /* Count properties. Always at least one property, the code page */
1921 numProps = 1;
1922 if (dictionary_num_entries(This->name_to_propid))
1923 numProps++;
1924 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1925 numProps++;
1926 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1927 numProps++;
1928 numProps += dictionary_num_entries(This->propid_to_prop);
1930 /* Write section header with 0 bytes right now, I'll adjust it after
1931 * writing properties.
1933 PropertyStorage_MakeSectionHdr(0, numProps, &sectionHdr);
1934 seek.QuadPart = SECTIONHEADER_OFFSET;
1935 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1936 if (FAILED(hr))
1937 goto end;
1938 hr = IStream_Write(This->stm, &sectionHdr, sizeof(sectionHdr), &count);
1939 if (FAILED(hr))
1940 goto end;
1942 prop = 0;
1943 sectionOffset = sizeof(PROPERTYSECTIONHEADER) +
1944 numProps * sizeof(PROPERTYIDOFFSET);
1946 if (dictionary_num_entries(This->name_to_propid))
1948 prop++;
1949 hr = PropertyStorage_WriteDictionaryToStream(This, &sectionOffset);
1950 if (FAILED(hr))
1951 goto end;
1954 PropVariantInit(&var);
1956 var.vt = VT_I2;
1957 var.u.iVal = This->codePage;
1958 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE,
1959 &var, &sectionOffset);
1960 if (FAILED(hr))
1961 goto end;
1963 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1965 var.vt = VT_I4;
1966 var.u.lVal = This->locale;
1967 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE,
1968 &var, &sectionOffset);
1969 if (FAILED(hr))
1970 goto end;
1973 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1975 var.vt = VT_I4;
1976 var.u.lVal = 1;
1977 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR,
1978 &var, &sectionOffset);
1979 if (FAILED(hr))
1980 goto end;
1983 hr = PropertyStorage_WritePropertiesToStream(This, prop, &sectionOffset);
1984 if (FAILED(hr))
1985 goto end;
1987 /* Now write the byte count of the section */
1988 seek.QuadPart = SECTIONHEADER_OFFSET;
1989 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1990 if (FAILED(hr))
1991 goto end;
1992 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, sectionOffset);
1993 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1995 end:
1996 return hr;
1999 /***********************************************************************
2000 * PropertyStorage_Construct
2002 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This)
2004 dictionary_destroy(This->name_to_propid);
2005 This->name_to_propid = NULL;
2006 dictionary_destroy(This->propid_to_name);
2007 This->propid_to_name = NULL;
2008 dictionary_destroy(This->propid_to_prop);
2009 This->propid_to_prop = NULL;
2012 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This)
2014 HRESULT hr = S_OK;
2016 This->name_to_propid = dictionary_create(
2017 PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy,
2018 This);
2019 if (!This->name_to_propid)
2021 hr = STG_E_INSUFFICIENTMEMORY;
2022 goto end;
2024 This->propid_to_name = dictionary_create(PropertyStorage_PropCompare,
2025 NULL, This);
2026 if (!This->propid_to_name)
2028 hr = STG_E_INSUFFICIENTMEMORY;
2029 goto end;
2031 This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
2032 PropertyStorage_PropertyDestroy, This);
2033 if (!This->propid_to_prop)
2035 hr = STG_E_INSUFFICIENTMEMORY;
2036 goto end;
2038 end:
2039 if (FAILED(hr))
2040 PropertyStorage_DestroyDictionaries(This);
2041 return hr;
2044 static HRESULT PropertyStorage_BaseConstruct(IStream *stm,
2045 REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps)
2047 HRESULT hr = S_OK;
2049 assert(pps);
2050 assert(rfmtid);
2051 *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps);
2052 if (!*pps)
2053 return E_OUTOFMEMORY;
2055 (*pps)->IPropertyStorage_iface.lpVtbl = &IPropertyStorage_Vtbl;
2056 (*pps)->ref = 1;
2057 InitializeCriticalSection(&(*pps)->cs);
2058 (*pps)->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PropertyStorage_impl.cs");
2059 (*pps)->stm = stm;
2060 (*pps)->fmtid = *rfmtid;
2061 (*pps)->grfMode = grfMode;
2063 hr = PropertyStorage_CreateDictionaries(*pps);
2064 if (FAILED(hr))
2066 (*pps)->cs.DebugInfo->Spare[0] = 0;
2067 DeleteCriticalSection(&(*pps)->cs);
2068 HeapFree(GetProcessHeap(), 0, *pps);
2069 *pps = NULL;
2071 else IStream_AddRef( stm );
2073 return hr;
2076 static HRESULT PropertyStorage_ConstructFromStream(IStream *stm,
2077 REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps)
2079 PropertyStorage_impl *ps;
2080 HRESULT hr;
2082 assert(pps);
2083 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2084 if (SUCCEEDED(hr))
2086 hr = PropertyStorage_ReadFromStream(ps);
2087 if (SUCCEEDED(hr))
2089 *pps = &ps->IPropertyStorage_iface;
2090 TRACE("PropertyStorage %p constructed\n", ps);
2091 hr = S_OK;
2093 else IPropertyStorage_Release( &ps->IPropertyStorage_iface );
2095 return hr;
2098 static HRESULT PropertyStorage_ConstructEmpty(IStream *stm,
2099 REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
2101 PropertyStorage_impl *ps;
2102 HRESULT hr;
2104 assert(pps);
2105 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2106 if (SUCCEEDED(hr))
2108 ps->format = 0;
2109 ps->grfFlags = grfFlags;
2110 if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2111 ps->format = 1;
2112 /* default to Unicode unless told not to, as specified on msdn */
2113 if (ps->grfFlags & PROPSETFLAG_ANSI)
2114 ps->codePage = GetACP();
2115 else
2116 ps->codePage = CP_UNICODE;
2117 ps->locale = LOCALE_SYSTEM_DEFAULT;
2118 TRACE("Code page is %d, locale is %d\n", ps->codePage, ps->locale);
2119 *pps = &ps->IPropertyStorage_iface;
2120 TRACE("PropertyStorage %p constructed\n", ps);
2121 hr = S_OK;
2123 return hr;
2127 /***********************************************************************
2128 * Implementation of IPropertySetStorage
2131 /************************************************************************
2132 * IPropertySetStorage_fnQueryInterface (IUnknown)
2134 * This method forwards to the common QueryInterface implementation
2136 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
2137 IPropertySetStorage *ppstg,
2138 REFIID riid,
2139 void** ppvObject)
2141 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2142 return IStorage_QueryInterface( &This->base.IStorage_iface, riid, ppvObject );
2145 /************************************************************************
2146 * IPropertySetStorage_fnAddRef (IUnknown)
2148 * This method forwards to the common AddRef implementation
2150 static ULONG WINAPI IPropertySetStorage_fnAddRef(
2151 IPropertySetStorage *ppstg)
2153 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2154 return IStorage_AddRef( &This->base.IStorage_iface );
2157 /************************************************************************
2158 * IPropertySetStorage_fnRelease (IUnknown)
2160 * This method forwards to the common Release implementation
2162 static ULONG WINAPI IPropertySetStorage_fnRelease(
2163 IPropertySetStorage *ppstg)
2165 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2166 return IStorage_Release( &This->base.IStorage_iface );
2169 /************************************************************************
2170 * IPropertySetStorage_fnCreate (IPropertySetStorage)
2172 static HRESULT WINAPI IPropertySetStorage_fnCreate(
2173 IPropertySetStorage *ppstg,
2174 REFFMTID rfmtid,
2175 const CLSID* pclsid,
2176 DWORD grfFlags,
2177 DWORD grfMode,
2178 IPropertyStorage** ppprstg)
2180 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2181 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2182 IStream *stm = NULL;
2183 HRESULT r;
2185 TRACE("%p %s %08x %08x %p\n", This, debugstr_guid(rfmtid), grfFlags,
2186 grfMode, ppprstg);
2188 /* be picky */
2189 if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
2191 r = STG_E_INVALIDFLAG;
2192 goto end;
2195 if (!rfmtid)
2197 r = E_INVALIDARG;
2198 goto end;
2201 /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
2202 * storage, not a stream. For now, disallow it.
2204 if (grfFlags & PROPSETFLAG_NONSIMPLE)
2206 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2207 r = STG_E_INVALIDFLAG;
2208 goto end;
2211 r = FmtIdToPropStgName(rfmtid, name);
2212 if (FAILED(r))
2213 goto end;
2215 r = IStorage_CreateStream( &This->base.IStorage_iface, name, grfMode, 0, 0, &stm );
2216 if (FAILED(r))
2217 goto end;
2219 r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg);
2221 IStream_Release( stm );
2223 end:
2224 TRACE("returning 0x%08x\n", r);
2225 return r;
2228 /************************************************************************
2229 * IPropertySetStorage_fnOpen (IPropertySetStorage)
2231 static HRESULT WINAPI IPropertySetStorage_fnOpen(
2232 IPropertySetStorage *ppstg,
2233 REFFMTID rfmtid,
2234 DWORD grfMode,
2235 IPropertyStorage** ppprstg)
2237 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2238 IStream *stm = NULL;
2239 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2240 HRESULT r;
2242 TRACE("%p %s %08x %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
2244 /* be picky */
2245 if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
2246 grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
2248 r = STG_E_INVALIDFLAG;
2249 goto end;
2252 if (!rfmtid)
2254 r = E_INVALIDARG;
2255 goto end;
2258 r = FmtIdToPropStgName(rfmtid, name);
2259 if (FAILED(r))
2260 goto end;
2262 r = IStorage_OpenStream( &This->base.IStorage_iface, name, 0, grfMode, 0, &stm );
2263 if (FAILED(r))
2264 goto end;
2266 r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg);
2268 IStream_Release( stm );
2270 end:
2271 TRACE("returning 0x%08x\n", r);
2272 return r;
2275 /************************************************************************
2276 * IPropertySetStorage_fnDelete (IPropertySetStorage)
2278 static HRESULT WINAPI IPropertySetStorage_fnDelete(
2279 IPropertySetStorage *ppstg,
2280 REFFMTID rfmtid)
2282 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2283 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2284 HRESULT r;
2286 TRACE("%p %s\n", This, debugstr_guid(rfmtid));
2288 if (!rfmtid)
2289 return E_INVALIDARG;
2291 r = FmtIdToPropStgName(rfmtid, name);
2292 if (FAILED(r))
2293 return r;
2295 return IStorage_DestroyElement(&This->base.IStorage_iface, name);
2298 /************************************************************************
2299 * IPropertySetStorage_fnEnum (IPropertySetStorage)
2301 static HRESULT WINAPI IPropertySetStorage_fnEnum(
2302 IPropertySetStorage *ppstg,
2303 IEnumSTATPROPSETSTG** ppenum)
2305 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2306 return create_EnumSTATPROPSETSTG(This, ppenum);
2309 /************************************************************************
2310 * Implement IEnumSTATPROPSETSTG using enumx
2312 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnQueryInterface(
2313 IEnumSTATPROPSETSTG *iface,
2314 REFIID riid,
2315 void** ppvObject)
2317 return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
2320 static ULONG WINAPI IEnumSTATPROPSETSTG_fnAddRef(
2321 IEnumSTATPROPSETSTG *iface)
2323 return enumx_AddRef((enumx_impl*)iface);
2326 static ULONG WINAPI IEnumSTATPROPSETSTG_fnRelease(
2327 IEnumSTATPROPSETSTG *iface)
2329 return enumx_Release((enumx_impl*)iface);
2332 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnNext(
2333 IEnumSTATPROPSETSTG *iface,
2334 ULONG celt,
2335 STATPROPSETSTG *rgelt,
2336 ULONG *pceltFetched)
2338 return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
2341 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnSkip(
2342 IEnumSTATPROPSETSTG *iface,
2343 ULONG celt)
2345 return enumx_Skip((enumx_impl*)iface, celt);
2348 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnReset(
2349 IEnumSTATPROPSETSTG *iface)
2351 return enumx_Reset((enumx_impl*)iface);
2354 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnClone(
2355 IEnumSTATPROPSETSTG *iface,
2356 IEnumSTATPROPSETSTG **ppenum)
2358 return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
2361 static HRESULT create_EnumSTATPROPSETSTG(
2362 StorageImpl *This,
2363 IEnumSTATPROPSETSTG** ppenum)
2365 IStorage *stg = &This->base.IStorage_iface;
2366 IEnumSTATSTG *penum = NULL;
2367 STATSTG stat;
2368 ULONG count;
2369 HRESULT r;
2370 STATPROPSETSTG statpss;
2371 enumx_impl *enumx;
2373 TRACE("%p %p\n", This, ppenum);
2375 enumx = enumx_allocate(&IID_IEnumSTATPROPSETSTG,
2376 &IEnumSTATPROPSETSTG_Vtbl,
2377 sizeof (STATPROPSETSTG));
2379 /* add all the property set elements into a list */
2380 r = IStorage_EnumElements(stg, 0, NULL, 0, &penum);
2381 if (FAILED(r))
2383 enumx_Release(enumx);
2384 return E_OUTOFMEMORY;
2387 while (1)
2389 count = 0;
2390 r = IEnumSTATSTG_Next(penum, 1, &stat, &count);
2391 if (FAILED(r))
2392 break;
2393 if (!count)
2394 break;
2395 if (!stat.pwcsName)
2396 continue;
2397 if (stat.pwcsName[0] == 5 && stat.type == STGTY_STREAM)
2399 PropStgNameToFmtId(stat.pwcsName, &statpss.fmtid);
2400 TRACE("adding %s (%s)\n", debugstr_w(stat.pwcsName),
2401 debugstr_guid(&statpss.fmtid));
2402 statpss.mtime = stat.mtime;
2403 statpss.atime = stat.atime;
2404 statpss.ctime = stat.ctime;
2405 statpss.grfFlags = stat.grfMode;
2406 statpss.clsid = stat.clsid;
2407 enumx_add_element(enumx, &statpss);
2409 CoTaskMemFree(stat.pwcsName);
2411 IEnumSTATSTG_Release(penum);
2413 *ppenum = (IEnumSTATPROPSETSTG*) enumx;
2415 return S_OK;
2418 /************************************************************************
2419 * Implement IEnumSTATPROPSTG using enumx
2421 static HRESULT WINAPI IEnumSTATPROPSTG_fnQueryInterface(
2422 IEnumSTATPROPSTG *iface,
2423 REFIID riid,
2424 void** ppvObject)
2426 return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
2429 static ULONG WINAPI IEnumSTATPROPSTG_fnAddRef(
2430 IEnumSTATPROPSTG *iface)
2432 return enumx_AddRef((enumx_impl*)iface);
2435 static ULONG WINAPI IEnumSTATPROPSTG_fnRelease(
2436 IEnumSTATPROPSTG *iface)
2438 return enumx_Release((enumx_impl*)iface);
2441 static HRESULT WINAPI IEnumSTATPROPSTG_fnNext(
2442 IEnumSTATPROPSTG *iface,
2443 ULONG celt,
2444 STATPROPSTG *rgelt,
2445 ULONG *pceltFetched)
2447 return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
2450 static HRESULT WINAPI IEnumSTATPROPSTG_fnSkip(
2451 IEnumSTATPROPSTG *iface,
2452 ULONG celt)
2454 return enumx_Skip((enumx_impl*)iface, celt);
2457 static HRESULT WINAPI IEnumSTATPROPSTG_fnReset(
2458 IEnumSTATPROPSTG *iface)
2460 return enumx_Reset((enumx_impl*)iface);
2463 static HRESULT WINAPI IEnumSTATPROPSTG_fnClone(
2464 IEnumSTATPROPSTG *iface,
2465 IEnumSTATPROPSTG **ppenum)
2467 return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
2470 static BOOL prop_enum_stat(const void *k, const void *v, void *extra, void *arg)
2472 enumx_impl *enumx = arg;
2473 PROPID propid = PtrToUlong(k);
2474 const PROPVARIANT *prop = v;
2475 STATPROPSTG stat;
2477 stat.lpwstrName = NULL;
2478 stat.propid = propid;
2479 stat.vt = prop->vt;
2481 enumx_add_element(enumx, &stat);
2483 return TRUE;
2486 static HRESULT create_EnumSTATPROPSTG(
2487 PropertyStorage_impl *This,
2488 IEnumSTATPROPSTG** ppenum)
2490 enumx_impl *enumx;
2492 TRACE("%p %p\n", This, ppenum);
2494 enumx = enumx_allocate(&IID_IEnumSTATPROPSTG,
2495 &IEnumSTATPROPSTG_Vtbl,
2496 sizeof (STATPROPSTG));
2498 dictionary_enumerate(This->propid_to_prop, prop_enum_stat, enumx);
2500 *ppenum = (IEnumSTATPROPSTG*) enumx;
2502 return S_OK;
2505 /***********************************************************************
2506 * vtables
2508 const IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
2510 IPropertySetStorage_fnQueryInterface,
2511 IPropertySetStorage_fnAddRef,
2512 IPropertySetStorage_fnRelease,
2513 IPropertySetStorage_fnCreate,
2514 IPropertySetStorage_fnOpen,
2515 IPropertySetStorage_fnDelete,
2516 IPropertySetStorage_fnEnum
2519 static const IPropertyStorageVtbl IPropertyStorage_Vtbl =
2521 IPropertyStorage_fnQueryInterface,
2522 IPropertyStorage_fnAddRef,
2523 IPropertyStorage_fnRelease,
2524 IPropertyStorage_fnReadMultiple,
2525 IPropertyStorage_fnWriteMultiple,
2526 IPropertyStorage_fnDeleteMultiple,
2527 IPropertyStorage_fnReadPropertyNames,
2528 IPropertyStorage_fnWritePropertyNames,
2529 IPropertyStorage_fnDeletePropertyNames,
2530 IPropertyStorage_fnCommit,
2531 IPropertyStorage_fnRevert,
2532 IPropertyStorage_fnEnum,
2533 IPropertyStorage_fnSetTimes,
2534 IPropertyStorage_fnSetClass,
2535 IPropertyStorage_fnStat,
2538 static const IEnumSTATPROPSETSTGVtbl IEnumSTATPROPSETSTG_Vtbl =
2540 IEnumSTATPROPSETSTG_fnQueryInterface,
2541 IEnumSTATPROPSETSTG_fnAddRef,
2542 IEnumSTATPROPSETSTG_fnRelease,
2543 IEnumSTATPROPSETSTG_fnNext,
2544 IEnumSTATPROPSETSTG_fnSkip,
2545 IEnumSTATPROPSETSTG_fnReset,
2546 IEnumSTATPROPSETSTG_fnClone,
2549 static const IEnumSTATPROPSTGVtbl IEnumSTATPROPSTG_Vtbl =
2551 IEnumSTATPROPSTG_fnQueryInterface,
2552 IEnumSTATPROPSTG_fnAddRef,
2553 IEnumSTATPROPSTG_fnRelease,
2554 IEnumSTATPROPSTG_fnNext,
2555 IEnumSTATPROPSTG_fnSkip,
2556 IEnumSTATPROPSTG_fnReset,
2557 IEnumSTATPROPSTG_fnClone,
2560 /***********************************************************************
2561 * Format ID <-> name conversion
2563 static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
2564 'I','n','f','o','r','m','a','t','i','o','n',0 };
2565 static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t',
2566 'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
2568 #define BITS_PER_BYTE 8
2569 #define CHARMASK 0x1f
2570 #define BITS_IN_CHARMASK 5
2571 #define NUM_ALPHA_CHARS 26
2573 /***********************************************************************
2574 * FmtIdToPropStgName [ole32.@]
2575 * Returns the storage name of the format ID rfmtid.
2576 * PARAMS
2577 * rfmtid [I] Format ID for which to return a storage name
2578 * str [O] Storage name associated with rfmtid.
2580 * RETURNS
2581 * E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise.
2583 * NOTES
2584 * str must be at least CCH_MAX_PROPSTG_NAME characters in length.
2586 HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str)
2588 static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
2590 TRACE("%s, %p\n", debugstr_guid(rfmtid), str);
2592 if (!rfmtid) return E_INVALIDARG;
2593 if (!str) return E_INVALIDARG;
2595 if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
2596 lstrcpyW(str, szSummaryInfo);
2597 else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
2598 lstrcpyW(str, szDocSummaryInfo);
2599 else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid))
2600 lstrcpyW(str, szDocSummaryInfo);
2601 else
2603 const BYTE *fmtptr;
2604 WCHAR *pstr = str;
2605 ULONG bitsRemaining = BITS_PER_BYTE;
2607 *pstr++ = 5;
2608 for (fmtptr = (const BYTE *)rfmtid; fmtptr < (const BYTE *)rfmtid + sizeof(FMTID); )
2610 ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
2612 if (bitsRemaining >= BITS_IN_CHARMASK)
2614 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
2615 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
2616 *pstr <= 'z')
2617 *pstr += 'A' - 'a';
2618 pstr++;
2619 bitsRemaining -= BITS_IN_CHARMASK;
2620 if (bitsRemaining == 0)
2622 fmtptr++;
2623 bitsRemaining = BITS_PER_BYTE;
2626 else
2628 if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID))
2629 i |= *fmtptr << bitsRemaining;
2630 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
2631 bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK;
2634 *pstr = 0;
2636 TRACE("returning %s\n", debugstr_w(str));
2637 return S_OK;
2640 /***********************************************************************
2641 * PropStgNameToFmtId [ole32.@]
2642 * Returns the format ID corresponding to the given name.
2643 * PARAMS
2644 * str [I] Storage name to convert to a format ID.
2645 * rfmtid [O] Format ID corresponding to str.
2647 * RETURNS
2648 * E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to
2649 * a format ID, S_OK otherwise.
2651 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid)
2653 HRESULT hr = STG_E_INVALIDNAME;
2655 TRACE("%s, %p\n", debugstr_w(str), rfmtid);
2657 if (!rfmtid) return E_INVALIDARG;
2658 if (!str) return STG_E_INVALIDNAME;
2660 if (!lstrcmpiW(str, szDocSummaryInfo))
2662 *rfmtid = FMTID_DocSummaryInformation;
2663 hr = S_OK;
2665 else if (!lstrcmpiW(str, szSummaryInfo))
2667 *rfmtid = FMTID_SummaryInformation;
2668 hr = S_OK;
2670 else
2672 ULONG bits;
2673 BYTE *fmtptr = (BYTE *)rfmtid - 1;
2674 const WCHAR *pstr = str;
2676 memset(rfmtid, 0, sizeof(*rfmtid));
2677 for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE;
2678 bits += BITS_IN_CHARMASK)
2680 ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored;
2681 WCHAR wc;
2683 if (bitsUsed == 0)
2684 fmtptr++;
2685 wc = *++pstr - 'A';
2686 if (wc > NUM_ALPHA_CHARS)
2688 wc += 'A' - 'a';
2689 if (wc > NUM_ALPHA_CHARS)
2691 wc += 'a' - '0' + NUM_ALPHA_CHARS;
2692 if (wc > CHARMASK)
2694 WARN("invalid character (%d)\n", *pstr);
2695 goto end;
2699 *fmtptr |= wc << bitsUsed;
2700 bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK);
2701 if (bitsStored < BITS_IN_CHARMASK)
2703 wc >>= BITS_PER_BYTE - bitsUsed;
2704 if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE)
2706 if (wc != 0)
2708 WARN("extra bits\n");
2709 goto end;
2711 break;
2713 fmtptr++;
2714 *fmtptr |= (BYTE)wc;
2717 hr = S_OK;
2719 end:
2720 return hr;
2723 #ifdef __i386__ /* thiscall functions are i386-specific */
2725 #define DEFINE_STDCALL_WRAPPER(num,func,args) \
2726 __ASM_STDCALL_FUNC(func, args, \
2727 "popl %eax\n\t" \
2728 "popl %ecx\n\t" \
2729 "pushl %eax\n\t" \
2730 "movl (%ecx), %eax\n\t" \
2731 "jmp *(4*(" #num "))(%eax)" )
2733 DEFINE_STDCALL_WRAPPER(0,Allocate_PMemoryAllocator,8)
2734 extern void* WINAPI Allocate_PMemoryAllocator(void *this, ULONG cbSize);
2736 #else
2738 static void* WINAPI Allocate_PMemoryAllocator(void *this, ULONG cbSize)
2740 void* (WINAPI *fn)(void*,ULONG) = **(void***)this;
2741 return fn(this, cbSize);
2744 #endif
2746 BOOLEAN WINAPI StgConvertPropertyToVariant(const SERIALIZEDPROPERTYVALUE* prop,
2747 USHORT CodePage, PROPVARIANT* pvar, void* pma)
2749 HRESULT hr;
2751 hr = PropertyStorage_ReadProperty(pvar, (const BYTE*)prop, CodePage, Allocate_PMemoryAllocator, pma);
2753 if (FAILED(hr))
2755 FIXME("should raise C++ exception on failure\n");
2756 PropVariantInit(pvar);
2759 return FALSE;
2762 SERIALIZEDPROPERTYVALUE* WINAPI StgConvertVariantToProperty(const PROPVARIANT *pvar,
2763 USHORT CodePage, SERIALIZEDPROPERTYVALUE *pprop, ULONG *pcb, PROPID pid,
2764 BOOLEAN fReserved, ULONG *pcIndirect)
2766 FIXME("%p,%d,%p,%p,%d,%d,%p\n", pvar, CodePage, pprop, pcb, pid, fReserved, pcIndirect);
2768 return NULL;
2771 HRESULT WINAPI StgCreatePropStg(IUnknown *unk, REFFMTID fmt, const CLSID *clsid,
2772 DWORD flags, DWORD reserved, IPropertyStorage **prop_stg)
2774 IStorage *stg;
2775 IStream *stm;
2776 HRESULT r;
2778 TRACE("%p %s %s %08x %d %p\n", unk, debugstr_guid(fmt), debugstr_guid(clsid), flags, reserved, prop_stg);
2780 if (!fmt || reserved)
2782 r = E_INVALIDARG;
2783 goto end;
2786 if (flags & PROPSETFLAG_NONSIMPLE)
2788 r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
2789 if (FAILED(r))
2790 goto end;
2792 /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to create a
2793 * storage, not a stream. For now, disallow it.
2795 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2796 IStorage_Release(stg);
2797 r = STG_E_INVALIDFLAG;
2799 else
2801 r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
2802 if (FAILED(r))
2803 goto end;
2805 r = PropertyStorage_ConstructEmpty(stm, fmt, flags,
2806 STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
2808 IStream_Release( stm );
2811 end:
2812 TRACE("returning 0x%08x\n", r);
2813 return r;
2816 HRESULT WINAPI StgOpenPropStg(IUnknown *unk, REFFMTID fmt, DWORD flags,
2817 DWORD reserved, IPropertyStorage **prop_stg)
2819 IStorage *stg;
2820 IStream *stm;
2821 HRESULT r;
2823 TRACE("%p %s %08x %d %p\n", unk, debugstr_guid(fmt), flags, reserved, prop_stg);
2825 if (!fmt || reserved)
2827 r = E_INVALIDARG;
2828 goto end;
2831 if (flags & PROPSETFLAG_NONSIMPLE)
2833 r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
2834 if (FAILED(r))
2835 goto end;
2837 /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to open a
2838 * storage, not a stream. For now, disallow it.
2840 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2841 IStorage_Release(stg);
2842 r = STG_E_INVALIDFLAG;
2844 else
2846 r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
2847 if (FAILED(r))
2848 goto end;
2850 r = PropertyStorage_ConstructFromStream(stm, fmt,
2851 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
2853 IStream_Release( stm );
2856 end:
2857 TRACE("returning 0x%08x\n", r);
2858 return r;