1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
4 *
5 * This file contains the compound file implementation
6 * of the storage interface.
7 *
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
13 *
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.
18 *
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.
23 *
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
27 *
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
37 */
38
39 #include <assert.h>
40 #include <stdarg.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44
45 #define COBJMACROS
46 #define NONAMELESSUNION
47
48 #include "windef.h"
49 #include "winbase.h"
50 #include "winnls.h"
51 #include "winuser.h"
52 #include "wine/asm.h"
53 #include "wine/debug.h"
54 #include "dictionary.h"
55 #include "storage32.h"
56 #include "enumx.h"
57 #include "oleauto.h"
58
59 WINE_DEFAULT_DEBUG_CHANNEL(storage);
60
impl_from_IPropertySetStorage(IPropertySetStorage * iface)61 static inline StorageImpl *impl_from_IPropertySetStorage( IPropertySetStorage *iface )
62 {
63 return CONTAINING_RECORD(iface, StorageImpl, base.IPropertySetStorage_iface);
64 }
65
66 /* These are documented in MSDN,
67 * but they don't seem to be in any header file.
68 */
69 #define PROPSETHDR_BYTEORDER_MAGIC 0xfffe
70 #define PROPSETHDR_OSVER_KIND_WIN16 0
71 #define PROPSETHDR_OSVER_KIND_MAC 1
72 #define PROPSETHDR_OSVER_KIND_WIN32 2
73
74 #define CP_UNICODE 1200
75
76 #define MAX_VERSION_0_PROP_NAME_LENGTH 256
77
78 #define CFTAG_WINDOWS (-1L)
79 #define CFTAG_MACINTOSH (-2L)
80 #define CFTAG_FMTID (-3L)
81 #define CFTAG_NODATA 0L
82
83 typedef struct tagPROPERTYSETHEADER
84 {
85 WORD wByteOrder; /* always 0xfffe */
86 WORD wFormat; /* can be zero or one */
87 DWORD dwOSVer; /* OS version of originating system */
88 CLSID clsid; /* application CLSID */
89 DWORD reserved; /* always 1 */
90 } PROPERTYSETHEADER;
91
92 typedef struct tagFORMATIDOFFSET
93 {
94 FMTID fmtid;
95 DWORD dwOffset; /* from beginning of stream */
96 } FORMATIDOFFSET;
97
98 typedef struct tagPROPERTYSECTIONHEADER
99 {
100 DWORD cbSection;
101 DWORD cProperties;
102 } PROPERTYSECTIONHEADER;
103
104 typedef struct tagPROPERTYIDOFFSET
105 {
106 DWORD propid;
107 DWORD dwOffset; /* from beginning of section */
108 } PROPERTYIDOFFSET;
109
110 typedef struct tagPropertyStorage_impl PropertyStorage_impl;
111
112 /* Initializes the property storage from the stream (and undoes any uncommitted
113 * changes in the process.) Returns an error if there is an error reading or
114 * if the stream format doesn't match what's expected.
115 */
116 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *);
117
118 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *);
119
120 /* Creates the dictionaries used by the property storage. If successful, all
121 * the dictionaries have been created. If failed, none has been. (This makes
122 * it a bit easier to deal with destroying them.)
123 */
124 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *);
125
126 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *);
127
128 /* Copies from propvar to prop. If propvar's type is VT_LPSTR, copies the
129 * string using PropertyStorage_StringCopy.
130 */
131 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
132 const PROPVARIANT *propvar, LCID targetCP, LCID srcCP);
133
134 /* Copies the string src, which is encoded using code page srcCP, and returns
135 * it in *dst, in the code page specified by targetCP. The returned string is
136 * allocated using CoTaskMemAlloc.
137 * If srcCP is CP_UNICODE, src is in fact an LPCWSTR. Similarly, if targetCP
138 * is CP_UNICODE, the returned string is in fact an LPWSTR.
139 * Returns S_OK on success, something else on failure.
140 */
141 static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
142 LCID targetCP);
143
144 static const IPropertyStorageVtbl IPropertyStorage_Vtbl;
145 static const IEnumSTATPROPSETSTGVtbl IEnumSTATPROPSETSTG_Vtbl;
146 static const IEnumSTATPROPSTGVtbl IEnumSTATPROPSTG_Vtbl;
147 static HRESULT create_EnumSTATPROPSETSTG(StorageImpl *, IEnumSTATPROPSETSTG**);
148 static HRESULT create_EnumSTATPROPSTG(PropertyStorage_impl *, IEnumSTATPROPSTG**);
149
150 /***********************************************************************
151 * Implementation of IPropertyStorage
152 */
153 struct tagPropertyStorage_impl
154 {
155 IPropertyStorage IPropertyStorage_iface;
156 LONG ref;
157 CRITICAL_SECTION cs;
158 IStream *stm;
159 BOOL dirty;
160 FMTID fmtid;
161 CLSID clsid;
162 WORD format;
163 DWORD originatorOS;
164 DWORD grfFlags;
165 DWORD grfMode;
166 UINT codePage;
167 LCID locale;
168 PROPID highestProp;
169 struct dictionary *name_to_propid;
170 struct dictionary *propid_to_name;
171 struct dictionary *propid_to_prop;
172 };
173
impl_from_IPropertyStorage(IPropertyStorage * iface)174 static inline PropertyStorage_impl *impl_from_IPropertyStorage(IPropertyStorage *iface)
175 {
176 return CONTAINING_RECORD(iface, PropertyStorage_impl, IPropertyStorage_iface);
177 }
178
179 /************************************************************************
180 * IPropertyStorage_fnQueryInterface (IPropertyStorage)
181 */
IPropertyStorage_fnQueryInterface(IPropertyStorage * iface,REFIID riid,void ** ppvObject)182 static HRESULT WINAPI IPropertyStorage_fnQueryInterface(
183 IPropertyStorage *iface,
184 REFIID riid,
185 void** ppvObject)
186 {
187 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
188
189 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
190
191 if (!ppvObject)
192 return E_INVALIDARG;
193
194 *ppvObject = 0;
195
196 if (IsEqualGUID(&IID_IUnknown, riid) ||
197 IsEqualGUID(&IID_IPropertyStorage, riid))
198 {
199 IPropertyStorage_AddRef(iface);
200 *ppvObject = iface;
201 return S_OK;
202 }
203
204 return E_NOINTERFACE;
205 }
206
207 /************************************************************************
208 * IPropertyStorage_fnAddRef (IPropertyStorage)
209 */
IPropertyStorage_fnAddRef(IPropertyStorage * iface)210 static ULONG WINAPI IPropertyStorage_fnAddRef(
211 IPropertyStorage *iface)
212 {
213 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
214 return InterlockedIncrement(&This->ref);
215 }
216
217 /************************************************************************
218 * IPropertyStorage_fnRelease (IPropertyStorage)
219 */
IPropertyStorage_fnRelease(IPropertyStorage * iface)220 static ULONG WINAPI IPropertyStorage_fnRelease(
221 IPropertyStorage *iface)
222 {
223 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
224 ULONG ref;
225
226 ref = InterlockedDecrement(&This->ref);
227 if (ref == 0)
228 {
229 TRACE("Destroying %p\n", This);
230 if (This->dirty)
231 IPropertyStorage_Commit(iface, STGC_DEFAULT);
232 IStream_Release(This->stm);
233 This->cs.DebugInfo->Spare[0] = 0;
234 DeleteCriticalSection(&This->cs);
235 PropertyStorage_DestroyDictionaries(This);
236 HeapFree(GetProcessHeap(), 0, This);
237 }
238 return ref;
239 }
240
PropertyStorage_FindProperty(PropertyStorage_impl * This,DWORD propid)241 static PROPVARIANT *PropertyStorage_FindProperty(PropertyStorage_impl *This,
242 DWORD propid)
243 {
244 PROPVARIANT *ret = NULL;
245
246 dictionary_find(This->propid_to_prop, UlongToPtr(propid), (void **)&ret);
247 TRACE("returning %p\n", ret);
248 return ret;
249 }
250
251 /* Returns NULL if name is NULL. */
PropertyStorage_FindPropertyByName(PropertyStorage_impl * This,LPCWSTR name)252 static PROPVARIANT *PropertyStorage_FindPropertyByName(
253 PropertyStorage_impl *This, LPCWSTR name)
254 {
255 PROPVARIANT *ret = NULL;
256 void *propid;
257
258 if (!name)
259 return NULL;
260 if (This->codePage == CP_UNICODE)
261 {
262 if (dictionary_find(This->name_to_propid, name, &propid))
263 ret = PropertyStorage_FindProperty(This, PtrToUlong(propid));
264 }
265 else
266 {
267 LPSTR ansiName;
268 HRESULT hr = PropertyStorage_StringCopy((LPCSTR)name, CP_UNICODE,
269 &ansiName, This->codePage);
270
271 if (SUCCEEDED(hr))
272 {
273 if (dictionary_find(This->name_to_propid, ansiName, &propid))
274 ret = PropertyStorage_FindProperty(This, PtrToUlong(propid));
275 CoTaskMemFree(ansiName);
276 }
277 }
278 TRACE("returning %p\n", ret);
279 return ret;
280 }
281
PropertyStorage_FindPropertyNameById(PropertyStorage_impl * This,DWORD propid)282 static LPWSTR PropertyStorage_FindPropertyNameById(PropertyStorage_impl *This,
283 DWORD propid)
284 {
285 LPWSTR ret = NULL;
286
287 dictionary_find(This->propid_to_name, UlongToPtr(propid), (void **)&ret);
288 TRACE("returning %p\n", ret);
289 return ret;
290 }
291
292 /************************************************************************
293 * IPropertyStorage_fnReadMultiple (IPropertyStorage)
294 */
IPropertyStorage_fnReadMultiple(IPropertyStorage * iface,ULONG cpspec,const PROPSPEC rgpspec[],PROPVARIANT rgpropvar[])295 static HRESULT WINAPI IPropertyStorage_fnReadMultiple(
296 IPropertyStorage* iface,
297 ULONG cpspec,
298 const PROPSPEC rgpspec[],
299 PROPVARIANT rgpropvar[])
300 {
301 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
302 HRESULT hr = S_OK;
303 ULONG i;
304
305 TRACE("(%p, %d, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
306
307 if (!cpspec)
308 return S_FALSE;
309 if (!rgpspec || !rgpropvar)
310 return E_INVALIDARG;
311 EnterCriticalSection(&This->cs);
312 for (i = 0; i < cpspec; i++)
313 {
314 PropVariantInit(&rgpropvar[i]);
315 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
316 {
317 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
318 rgpspec[i].u.lpwstr);
319
320 if (prop)
321 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop, GetACP(),
322 This->codePage);
323 }
324 else
325 {
326 switch (rgpspec[i].u.propid)
327 {
328 case PID_CODEPAGE:
329 rgpropvar[i].vt = VT_I2;
330 rgpropvar[i].u.iVal = This->codePage;
331 break;
332 case PID_LOCALE:
333 rgpropvar[i].vt = VT_I4;
334 rgpropvar[i].u.lVal = This->locale;
335 break;
336 default:
337 {
338 PROPVARIANT *prop = PropertyStorage_FindProperty(This,
339 rgpspec[i].u.propid);
340
341 if (prop)
342 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop,
343 GetACP(), This->codePage);
344 else
345 hr = S_FALSE;
346 }
347 }
348 }
349 }
350 LeaveCriticalSection(&This->cs);
351 return hr;
352 }
353
PropertyStorage_StringCopy(LPCSTR src,LCID srcCP,LPSTR * dst,LCID dstCP)354 static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
355 LCID dstCP)
356 {
357 HRESULT hr = S_OK;
358 int len;
359
360 TRACE("%s, %p, %d, %d\n",
361 srcCP == CP_UNICODE ? debugstr_w((LPCWSTR)src) : debugstr_a(src), dst,
362 dstCP, srcCP);
363 assert(src);
364 assert(dst);
365 *dst = NULL;
366 if (dstCP == srcCP)
367 {
368 size_t len;
369
370 if (dstCP == CP_UNICODE)
371 len = (lstrlenW((LPCWSTR)src) + 1) * sizeof(WCHAR);
372 else
373 len = strlen(src) + 1;
374 *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
375 if (!*dst)
376 hr = STG_E_INSUFFICIENTMEMORY;
377 else
378 memcpy(*dst, src, len);
379 }
380 else
381 {
382 if (dstCP == CP_UNICODE)
383 {
384 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
385 *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
386 if (!*dst)
387 hr = STG_E_INSUFFICIENTMEMORY;
388 else
389 MultiByteToWideChar(srcCP, 0, src, -1, (LPWSTR)*dst, len);
390 }
391 else
392 {
393 LPCWSTR wideStr = NULL;
394 LPWSTR wideStr_tmp = NULL;
395
396 if (srcCP == CP_UNICODE)
397 wideStr = (LPCWSTR)src;
398 else
399 {
400 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
401 wideStr_tmp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
402 if (wideStr_tmp)
403 {
404 MultiByteToWideChar(srcCP, 0, src, -1, wideStr_tmp, len);
405 wideStr = wideStr_tmp;
406 }
407 else
408 hr = STG_E_INSUFFICIENTMEMORY;
409 }
410 if (SUCCEEDED(hr))
411 {
412 len = WideCharToMultiByte(dstCP, 0, wideStr, -1, NULL, 0,
413 NULL, NULL);
414 *dst = CoTaskMemAlloc(len);
415 if (!*dst)
416 hr = STG_E_INSUFFICIENTMEMORY;
417 else
418 {
419 BOOL defCharUsed = FALSE;
420
421 if (WideCharToMultiByte(dstCP, 0, wideStr, -1, *dst, len,
422 NULL, &defCharUsed) == 0 || defCharUsed)
423 {
424 CoTaskMemFree(*dst);
425 *dst = NULL;
426 hr = HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION);
427 }
428 }
429 }
430 HeapFree(GetProcessHeap(), 0, wideStr_tmp);
431 }
432 }
433 TRACE("returning 0x%08x (%s)\n", hr,
434 dstCP == CP_UNICODE ? debugstr_w((LPCWSTR)*dst) : debugstr_a(*dst));
435 return hr;
436 }
437
PropertyStorage_PropVariantCopy(PROPVARIANT * prop,const PROPVARIANT * propvar,LCID targetCP,LCID srcCP)438 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
439 const PROPVARIANT *propvar, LCID targetCP, LCID srcCP)
440 {
441 HRESULT hr = S_OK;
442
443 assert(prop);
444 assert(propvar);
445 if (propvar->vt == VT_LPSTR)
446 {
447 hr = PropertyStorage_StringCopy(propvar->u.pszVal, srcCP,
448 &prop->u.pszVal, targetCP);
449 if (SUCCEEDED(hr))
450 prop->vt = VT_LPSTR;
451 }
452 else
453 PropVariantCopy(prop, propvar);
454 return hr;
455 }
456
457 /* Stores the property with id propid and value propvar into this property
458 * storage. lcid is ignored if propvar's type is not VT_LPSTR. If propvar's
459 * type is VT_LPSTR, converts the string using lcid as the source code page
460 * and This->codePage as the target code page before storing.
461 * As a side effect, may change This->format to 1 if the type of propvar is
462 * a version 1-only property.
463 */
PropertyStorage_StorePropWithId(PropertyStorage_impl * This,PROPID propid,const PROPVARIANT * propvar,LCID lcid)464 static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
465 PROPID propid, const PROPVARIANT *propvar, LCID lcid)
466 {
467 HRESULT hr = S_OK;
468 PROPVARIANT *prop = PropertyStorage_FindProperty(This, propid);
469
470 assert(propvar);
471 if (propvar->vt & VT_BYREF || propvar->vt & VT_ARRAY)
472 This->format = 1;
473 switch (propvar->vt)
474 {
475 case VT_DECIMAL:
476 case VT_I1:
477 case VT_INT:
478 case VT_UINT:
479 case VT_VECTOR|VT_I1:
480 This->format = 1;
481 }
482 TRACE("Setting 0x%08x to type %d\n", propid, propvar->vt);
483 if (prop)
484 {
485 PropVariantClear(prop);
486 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
487 lcid);
488 }
489 else
490 {
491 prop = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
492 sizeof(PROPVARIANT));
493 if (prop)
494 {
495 hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
496 lcid);
497 if (SUCCEEDED(hr))
498 {
499 dictionary_insert(This->propid_to_prop, UlongToPtr(propid), prop);
500 if (propid > This->highestProp)
501 This->highestProp = propid;
502 }
503 else
504 HeapFree(GetProcessHeap(), 0, prop);
505 }
506 else
507 hr = STG_E_INSUFFICIENTMEMORY;
508 }
509 return hr;
510 }
511
512 /* Adds the name srcName to the name dictionaries, mapped to property ID id.
513 * srcName is encoded in code page cp, and is converted to This->codePage.
514 * If cp is CP_UNICODE, srcName is actually a unicode string.
515 * As a side effect, may change This->format to 1 if srcName is too long for
516 * a version 0 property storage.
517 * Doesn't validate id.
518 */
PropertyStorage_StoreNameWithId(PropertyStorage_impl * This,LPCSTR srcName,LCID cp,PROPID id)519 static HRESULT PropertyStorage_StoreNameWithId(PropertyStorage_impl *This,
520 LPCSTR srcName, LCID cp, PROPID id)
521 {
522 LPSTR name;
523 HRESULT hr;
524
525 assert(srcName);
526
527 hr = PropertyStorage_StringCopy(srcName, cp, &name, This->codePage);
528 if (SUCCEEDED(hr))
529 {
530 if (This->codePage == CP_UNICODE)
531 {
532 if (lstrlenW((LPWSTR)name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
533 This->format = 1;
534 }
535 else
536 {
537 if (strlen(name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
538 This->format = 1;
539 }
540 TRACE("Adding prop name %s, propid %d\n",
541 This->codePage == CP_UNICODE ? debugstr_w((LPCWSTR)name) :
542 debugstr_a(name), id);
543 dictionary_insert(This->name_to_propid, name, UlongToPtr(id));
544 dictionary_insert(This->propid_to_name, UlongToPtr(id), name);
545 }
546 return hr;
547 }
548
549 /************************************************************************
550 * IPropertyStorage_fnWriteMultiple (IPropertyStorage)
551 */
IPropertyStorage_fnWriteMultiple(IPropertyStorage * iface,ULONG cpspec,const PROPSPEC rgpspec[],const PROPVARIANT rgpropvar[],PROPID propidNameFirst)552 static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
553 IPropertyStorage* iface,
554 ULONG cpspec,
555 const PROPSPEC rgpspec[],
556 const PROPVARIANT rgpropvar[],
557 PROPID propidNameFirst)
558 {
559 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
560 HRESULT hr = S_OK;
561 ULONG i;
562
563 TRACE("(%p, %d, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
564
565 if (cpspec && (!rgpspec || !rgpropvar))
566 return E_INVALIDARG;
567 if (!(This->grfMode & STGM_READWRITE))
568 return STG_E_ACCESSDENIED;
569 EnterCriticalSection(&This->cs);
570 This->dirty = TRUE;
571 This->originatorOS = (DWORD)MAKELONG(LOWORD(GetVersion()),
572 PROPSETHDR_OSVER_KIND_WIN32) ;
573 for (i = 0; i < cpspec; i++)
574 {
575 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
576 {
577 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
578 rgpspec[i].u.lpwstr);
579
580 if (prop)
581 PropVariantCopy(prop, &rgpropvar[i]);
582 else
583 {
584 /* Note that I don't do the special cases here that I do below,
585 * because naming the special PIDs isn't supported.
586 */
587 if (propidNameFirst < PID_FIRST_USABLE ||
588 propidNameFirst >= PID_MIN_READONLY)
589 hr = STG_E_INVALIDPARAMETER;
590 else
591 {
592 PROPID nextId = max(propidNameFirst, This->highestProp + 1);
593
594 hr = PropertyStorage_StoreNameWithId(This,
595 (LPCSTR)rgpspec[i].u.lpwstr, CP_UNICODE, nextId);
596 if (SUCCEEDED(hr))
597 hr = PropertyStorage_StorePropWithId(This, nextId,
598 &rgpropvar[i], GetACP());
599 }
600 }
601 }
602 else
603 {
604 switch (rgpspec[i].u.propid)
605 {
606 case PID_DICTIONARY:
607 /* Can't set the dictionary */
608 hr = STG_E_INVALIDPARAMETER;
609 break;
610 case PID_CODEPAGE:
611 /* Can only set the code page if nothing else has been set */
612 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
613 rgpropvar[i].vt == VT_I2)
614 {
615 This->codePage = rgpropvar[i].u.iVal;
616 if (This->codePage == CP_UNICODE)
617 This->grfFlags &= ~PROPSETFLAG_ANSI;
618 else
619 This->grfFlags |= PROPSETFLAG_ANSI;
620 }
621 else
622 hr = STG_E_INVALIDPARAMETER;
623 break;
624 case PID_LOCALE:
625 /* Can only set the locale if nothing else has been set */
626 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
627 rgpropvar[i].vt == VT_I4)
628 This->locale = rgpropvar[i].u.lVal;
629 else
630 hr = STG_E_INVALIDPARAMETER;
631 break;
632 case PID_ILLEGAL:
633 /* silently ignore like MSDN says */
634 break;
635 default:
636 if (rgpspec[i].u.propid >= PID_MIN_READONLY)
637 hr = STG_E_INVALIDPARAMETER;
638 else
639 hr = PropertyStorage_StorePropWithId(This,
640 rgpspec[i].u.propid, &rgpropvar[i], GetACP());
641 }
642 }
643 }
644 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
645 IPropertyStorage_Commit(iface, STGC_DEFAULT);
646 LeaveCriticalSection(&This->cs);
647 return hr;
648 }
649
650 /************************************************************************
651 * IPropertyStorage_fnDeleteMultiple (IPropertyStorage)
652 */
IPropertyStorage_fnDeleteMultiple(IPropertyStorage * iface,ULONG cpspec,const PROPSPEC rgpspec[])653 static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
654 IPropertyStorage* iface,
655 ULONG cpspec,
656 const PROPSPEC rgpspec[])
657 {
658 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
659 ULONG i;
660 HRESULT hr;
661
662 TRACE("(%p, %d, %p)\n", iface, cpspec, rgpspec);
663
664 if (cpspec && !rgpspec)
665 return E_INVALIDARG;
666 if (!(This->grfMode & STGM_READWRITE))
667 return STG_E_ACCESSDENIED;
668 hr = S_OK;
669 EnterCriticalSection(&This->cs);
670 This->dirty = TRUE;
671 for (i = 0; i < cpspec; i++)
672 {
673 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
674 {
675 void *propid;
676
677 if (dictionary_find(This->name_to_propid, rgpspec[i].u.lpwstr, &propid))
678 dictionary_remove(This->propid_to_prop, propid);
679 }
680 else
681 {
682 if (rgpspec[i].u.propid >= PID_FIRST_USABLE &&
683 rgpspec[i].u.propid < PID_MIN_READONLY)
684 dictionary_remove(This->propid_to_prop, UlongToPtr(rgpspec[i].u.propid));
685 else
686 hr = STG_E_INVALIDPARAMETER;
687 }
688 }
689 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
690 IPropertyStorage_Commit(iface, STGC_DEFAULT);
691 LeaveCriticalSection(&This->cs);
692 return hr;
693 }
694
695 /************************************************************************
696 * IPropertyStorage_fnReadPropertyNames (IPropertyStorage)
697 */
IPropertyStorage_fnReadPropertyNames(IPropertyStorage * iface,ULONG cpropid,const PROPID rgpropid[],LPOLESTR rglpwstrName[])698 static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames(
699 IPropertyStorage* iface,
700 ULONG cpropid,
701 const PROPID rgpropid[],
702 LPOLESTR rglpwstrName[])
703 {
704 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
705 ULONG i;
706 HRESULT hr = S_FALSE;
707
708 TRACE("(%p, %d, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
709
710 if (cpropid && (!rgpropid || !rglpwstrName))
711 return E_INVALIDARG;
712 EnterCriticalSection(&This->cs);
713 for (i = 0; i < cpropid && SUCCEEDED(hr); i++)
714 {
715 LPWSTR name = PropertyStorage_FindPropertyNameById(This, rgpropid[i]);
716
717 if (name)
718 {
719 size_t len = lstrlenW(name);
720
721 hr = S_OK;
722 rglpwstrName[i] = CoTaskMemAlloc((len + 1) * sizeof(WCHAR));
723 if (rglpwstrName[i])
724 memcpy(rglpwstrName[i], name, (len + 1) * sizeof(WCHAR));
725 else
726 hr = STG_E_INSUFFICIENTMEMORY;
727 }
728 else
729 rglpwstrName[i] = NULL;
730 }
731 LeaveCriticalSection(&This->cs);
732 return hr;
733 }
734
735 /************************************************************************
736 * IPropertyStorage_fnWritePropertyNames (IPropertyStorage)
737 */
IPropertyStorage_fnWritePropertyNames(IPropertyStorage * iface,ULONG cpropid,const PROPID rgpropid[],const LPOLESTR rglpwstrName[])738 static HRESULT WINAPI IPropertyStorage_fnWritePropertyNames(
739 IPropertyStorage* iface,
740 ULONG cpropid,
741 const PROPID rgpropid[],
742 const LPOLESTR rglpwstrName[])
743 {
744 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
745 ULONG i;
746 HRESULT hr;
747
748 TRACE("(%p, %d, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
749
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++)
758 {
759 if (rgpropid[i] != PID_ILLEGAL)
760 hr = PropertyStorage_StoreNameWithId(This, (LPCSTR)rglpwstrName[i],
761 CP_UNICODE, rgpropid[i]);
762 }
763 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
764 IPropertyStorage_Commit(iface, STGC_DEFAULT);
765 LeaveCriticalSection(&This->cs);
766 return hr;
767 }
768
769 /************************************************************************
770 * IPropertyStorage_fnDeletePropertyNames (IPropertyStorage)
771 */
IPropertyStorage_fnDeletePropertyNames(IPropertyStorage * iface,ULONG cpropid,const PROPID rgpropid[])772 static HRESULT WINAPI IPropertyStorage_fnDeletePropertyNames(
773 IPropertyStorage* iface,
774 ULONG cpropid,
775 const PROPID rgpropid[])
776 {
777 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
778 ULONG i;
779 HRESULT hr;
780
781 TRACE("(%p, %d, %p)\n", iface, cpropid, rgpropid);
782
783 if (cpropid && !rgpropid)
784 return E_INVALIDARG;
785 if (!(This->grfMode & STGM_READWRITE))
786 return STG_E_ACCESSDENIED;
787 hr = S_OK;
788 EnterCriticalSection(&This->cs);
789 This->dirty = TRUE;
790 for (i = 0; i < cpropid; i++)
791 {
792 LPWSTR name = NULL;
793
794 if (dictionary_find(This->propid_to_name, UlongToPtr(rgpropid[i]), (void **)&name))
795 {
796 dictionary_remove(This->propid_to_name, UlongToPtr(rgpropid[i]));
797 dictionary_remove(This->name_to_propid, name);
798 }
799 }
800 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
801 IPropertyStorage_Commit(iface, STGC_DEFAULT);
802 LeaveCriticalSection(&This->cs);
803 return hr;
804 }
805
806 /************************************************************************
807 * IPropertyStorage_fnCommit (IPropertyStorage)
808 */
IPropertyStorage_fnCommit(IPropertyStorage * iface,DWORD grfCommitFlags)809 static HRESULT WINAPI IPropertyStorage_fnCommit(
810 IPropertyStorage* iface,
811 DWORD grfCommitFlags)
812 {
813 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
814 HRESULT hr;
815
816 TRACE("(%p, 0x%08x)\n", iface, grfCommitFlags);
817
818 if (!(This->grfMode & STGM_READWRITE))
819 return STG_E_ACCESSDENIED;
820 EnterCriticalSection(&This->cs);
821 if (This->dirty)
822 hr = PropertyStorage_WriteToStream(This);
823 else
824 hr = S_OK;
825 LeaveCriticalSection(&This->cs);
826 return hr;
827 }
828
829 /************************************************************************
830 * IPropertyStorage_fnRevert (IPropertyStorage)
831 */
IPropertyStorage_fnRevert(IPropertyStorage * iface)832 static HRESULT WINAPI IPropertyStorage_fnRevert(
833 IPropertyStorage* iface)
834 {
835 HRESULT hr;
836 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
837
838 TRACE("%p\n", iface);
839
840 EnterCriticalSection(&This->cs);
841 if (This->dirty)
842 {
843 PropertyStorage_DestroyDictionaries(This);
844 hr = PropertyStorage_CreateDictionaries(This);
845 if (SUCCEEDED(hr))
846 hr = PropertyStorage_ReadFromStream(This);
847 }
848 else
849 hr = S_OK;
850 LeaveCriticalSection(&This->cs);
851 return hr;
852 }
853
854 /************************************************************************
855 * IPropertyStorage_fnEnum (IPropertyStorage)
856 */
IPropertyStorage_fnEnum(IPropertyStorage * iface,IEnumSTATPROPSTG ** ppenum)857 static HRESULT WINAPI IPropertyStorage_fnEnum(
858 IPropertyStorage* iface,
859 IEnumSTATPROPSTG** ppenum)
860 {
861 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
862 return create_EnumSTATPROPSTG(This, ppenum);
863 }
864
865 /************************************************************************
866 * IPropertyStorage_fnSetTimes (IPropertyStorage)
867 */
IPropertyStorage_fnSetTimes(IPropertyStorage * iface,const FILETIME * pctime,const FILETIME * patime,const FILETIME * pmtime)868 static HRESULT WINAPI IPropertyStorage_fnSetTimes(
869 IPropertyStorage* iface,
870 const FILETIME* pctime,
871 const FILETIME* patime,
872 const FILETIME* pmtime)
873 {
874 FIXME("\n");
875 return E_NOTIMPL;
876 }
877
878 /************************************************************************
879 * IPropertyStorage_fnSetClass (IPropertyStorage)
880 */
IPropertyStorage_fnSetClass(IPropertyStorage * iface,REFCLSID clsid)881 static HRESULT WINAPI IPropertyStorage_fnSetClass(
882 IPropertyStorage* iface,
883 REFCLSID clsid)
884 {
885 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
886
887 TRACE("%p, %s\n", iface, debugstr_guid(clsid));
888
889 if (!clsid)
890 return E_INVALIDARG;
891 if (!(This->grfMode & STGM_READWRITE))
892 return STG_E_ACCESSDENIED;
893 This->clsid = *clsid;
894 This->dirty = TRUE;
895 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
896 IPropertyStorage_Commit(iface, STGC_DEFAULT);
897 return S_OK;
898 }
899
900 /************************************************************************
901 * IPropertyStorage_fnStat (IPropertyStorage)
902 */
IPropertyStorage_fnStat(IPropertyStorage * iface,STATPROPSETSTG * statpsstg)903 static HRESULT WINAPI IPropertyStorage_fnStat(
904 IPropertyStorage* iface,
905 STATPROPSETSTG* statpsstg)
906 {
907 PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
908 STATSTG stat;
909 HRESULT hr;
910
911 TRACE("%p, %p\n", iface, statpsstg);
912
913 if (!statpsstg)
914 return E_INVALIDARG;
915
916 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
917 if (SUCCEEDED(hr))
918 {
919 statpsstg->fmtid = This->fmtid;
920 statpsstg->clsid = This->clsid;
921 statpsstg->grfFlags = This->grfFlags;
922 statpsstg->mtime = stat.mtime;
923 statpsstg->ctime = stat.ctime;
924 statpsstg->atime = stat.atime;
925 statpsstg->dwOSVersion = This->originatorOS;
926 }
927 return hr;
928 }
929
PropertyStorage_PropNameCompare(const void * a,const void * b,void * extra)930 static int PropertyStorage_PropNameCompare(const void *a, const void *b,
931 void *extra)
932 {
933 PropertyStorage_impl *This = extra;
934
935 if (This->codePage == CP_UNICODE)
936 {
937 TRACE("(%s, %s)\n", debugstr_w(a), debugstr_w(b));
938 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
939 return wcscmp(a, b);
940 else
941 return lstrcmpiW(a, b);
942 }
943 else
944 {
945 TRACE("(%s, %s)\n", debugstr_a(a), debugstr_a(b));
946 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
947 return lstrcmpA(a, b);
948 else
949 return lstrcmpiA(a, b);
950 }
951 }
952
PropertyStorage_PropNameDestroy(void * k,void * d,void * extra)953 static void PropertyStorage_PropNameDestroy(void *k, void *d, void *extra)
954 {
955 CoTaskMemFree(k);
956 }
957
PropertyStorage_PropCompare(const void * a,const void * b,void * extra)958 static int PropertyStorage_PropCompare(const void *a, const void *b,
959 void *extra)
960 {
961 TRACE("(%d, %d)\n", PtrToUlong(a), PtrToUlong(b));
962 return PtrToUlong(a) - PtrToUlong(b);
963 }
964
PropertyStorage_PropertyDestroy(void * k,void * d,void * extra)965 static void PropertyStorage_PropertyDestroy(void *k, void *d, void *extra)
966 {
967 PropVariantClear(d);
968 HeapFree(GetProcessHeap(), 0, d);
969 }
970
971 #ifdef WORDS_BIGENDIAN
972 /* Swaps each character in str to or from little endian; assumes the conversion
973 * is symmetric, that is, that lendian16toh is equivalent to htole16.
974 */
PropertyStorage_ByteSwapString(LPWSTR str,size_t len)975 static void PropertyStorage_ByteSwapString(LPWSTR str, size_t len)
976 {
977 DWORD i;
978
979 /* Swap characters to host order.
980 * FIXME: alignment?
981 */
982 for (i = 0; i < len; i++)
983 str[i] = lendian16toh(str[i]);
984 }
985 #else
986 #define PropertyStorage_ByteSwapString(s, l)
987 #endif
988
989 /* Reads the dictionary from the memory buffer beginning at ptr. Interprets
990 * the entries according to the values of This->codePage and This->locale.
991 * FIXME: there isn't any checking whether the read property extends past the
992 * end of the buffer.
993 */
PropertyStorage_ReadDictionary(PropertyStorage_impl * This,BYTE * ptr)994 static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This,
995 BYTE *ptr)
996 {
997 DWORD numEntries, i;
998 HRESULT hr = S_OK;
999
1000 assert(This->name_to_propid);
1001 assert(This->propid_to_name);
1002
1003 StorageUtl_ReadDWord(ptr, 0, &numEntries);
1004 TRACE("Reading %d entries:\n", numEntries);
1005 ptr += sizeof(DWORD);
1006 for (i = 0; SUCCEEDED(hr) && i < numEntries; i++)
1007 {
1008 PROPID propid;
1009 DWORD cbEntry;
1010
1011 StorageUtl_ReadDWord(ptr, 0, &propid);
1012 ptr += sizeof(PROPID);
1013 StorageUtl_ReadDWord(ptr, 0, &cbEntry);
1014 ptr += sizeof(DWORD);
1015 TRACE("Reading entry with ID 0x%08x, %d bytes\n", propid, cbEntry);
1016 /* Make sure the source string is NULL-terminated */
1017 if (This->codePage != CP_UNICODE)
1018 ptr[cbEntry - 1] = '\0';
1019 else
1020 ((LPWSTR)ptr)[cbEntry - 1] = 0;
1021 hr = PropertyStorage_StoreNameWithId(This, (char*)ptr, This->codePage, propid);
1022 if (This->codePage == CP_UNICODE)
1023 {
1024 /* cbEntry is the number of characters */
1025 cbEntry *= 2;
1026
1027 /* Unicode entries are padded to DWORD boundaries */
1028 if (cbEntry % sizeof(DWORD))
1029 ptr += sizeof(DWORD) - (cbEntry % sizeof(DWORD));
1030 }
1031 ptr += cbEntry;
1032 }
1033 return hr;
1034 }
1035
1036 #ifdef __i386__
1037 #define __thiscall_wrapper __stdcall
1038 #else
1039 #define __thiscall_wrapper __cdecl
1040 #endif
1041
Allocate_CoTaskMemAlloc(void * this,ULONG size)1042 static void* __thiscall_wrapper Allocate_CoTaskMemAlloc(void *this, ULONG size)
1043 {
1044 return CoTaskMemAlloc(size);
1045 }
1046
1047 /* FIXME: there isn't any checking whether the read property extends past the
1048 * end of the buffer.
1049 */
PropertyStorage_ReadProperty(PROPVARIANT * prop,const BYTE * data,UINT codepage,void * (__thiscall_wrapper * allocate)(void * this,ULONG size),void * allocate_data)1050 static HRESULT PropertyStorage_ReadProperty(PROPVARIANT *prop, const BYTE *data,
1051 UINT codepage, void* (__thiscall_wrapper *allocate)(void *this, ULONG size), void *allocate_data)
1052 {
1053 HRESULT hr = S_OK;
1054 DWORD vt;
1055
1056 assert(prop);
1057 assert(data);
1058 StorageUtl_ReadDWord(data, 0, &vt);
1059 data += sizeof(DWORD);
1060 prop->vt = vt;
1061 switch (prop->vt)
1062 {
1063 case VT_EMPTY:
1064 case VT_NULL:
1065 break;
1066 case VT_I1:
1067 prop->u.cVal = *(const char *)data;
1068 TRACE("Read char 0x%x ('%c')\n", prop->u.cVal, prop->u.cVal);
1069 break;
1070 case VT_UI1:
1071 prop->u.bVal = *data;
1072 TRACE("Read byte 0x%x\n", prop->u.bVal);
1073 break;
1074 case VT_BOOL:
1075 StorageUtl_ReadWord(data, 0, (WORD*)&prop->u.boolVal);
1076 TRACE("Read bool %d\n", prop->u.boolVal);
1077 break;
1078 case VT_I2:
1079 StorageUtl_ReadWord(data, 0, (WORD*)&prop->u.iVal);
1080 TRACE("Read short %d\n", prop->u.iVal);
1081 break;
1082 case VT_UI2:
1083 StorageUtl_ReadWord(data, 0, &prop->u.uiVal);
1084 TRACE("Read ushort %d\n", prop->u.uiVal);
1085 break;
1086 case VT_INT:
1087 case VT_I4:
1088 StorageUtl_ReadDWord(data, 0, (DWORD*)&prop->u.lVal);
1089 TRACE("Read long %d\n", prop->u.lVal);
1090 break;
1091 case VT_UINT:
1092 case VT_UI4:
1093 StorageUtl_ReadDWord(data, 0, &prop->u.ulVal);
1094 TRACE("Read ulong %d\n", prop->u.ulVal);
1095 break;
1096 case VT_I8:
1097 StorageUtl_ReadULargeInteger(data, 0, (ULARGE_INTEGER *)&prop->u.hVal);
1098 TRACE("Read long long %s\n", wine_dbgstr_longlong(prop->u.hVal.QuadPart));
1099 break;
1100 case VT_UI8:
1101 StorageUtl_ReadULargeInteger(data, 0, &prop->u.uhVal);
1102 TRACE("Read ulong long %s\n", wine_dbgstr_longlong(prop->u.uhVal.QuadPart));
1103 break;
1104 case VT_R8:
1105 memcpy(&prop->u.dblVal, data, sizeof(double));
1106 TRACE("Read double %f\n", prop->u.dblVal);
1107 break;
1108 case VT_LPSTR:
1109 {
1110 DWORD count;
1111
1112 StorageUtl_ReadDWord(data, 0, &count);
1113 if (codepage == CP_UNICODE && count % 2)
1114 {
1115 WARN("Unicode string has odd number of bytes\n");
1116 hr = STG_E_INVALIDHEADER;
1117 }
1118 else
1119 {
1120 prop->u.pszVal = allocate(allocate_data, count);
1121 if (prop->u.pszVal)
1122 {
1123 memcpy(prop->u.pszVal, data + sizeof(DWORD), count);
1124 /* This is stored in the code page specified in codepage.
1125 * Don't convert it, the caller will just store it as-is.
1126 */
1127 if (codepage == CP_UNICODE)
1128 {
1129 /* Make sure it's NULL-terminated */
1130 prop->u.pszVal[count / sizeof(WCHAR) - 1] = '\0';
1131 TRACE("Read string value %s\n",
1132 debugstr_w(prop->u.pwszVal));
1133 }
1134 else
1135 {
1136 /* Make sure it's NULL-terminated */
1137 prop->u.pszVal[count - 1] = '\0';
1138 TRACE("Read string value %s\n", debugstr_a(prop->u.pszVal));
1139 }
1140 }
1141 else
1142 hr = STG_E_INSUFFICIENTMEMORY;
1143 }
1144 break;
1145 }
1146 case VT_BSTR:
1147 {
1148 DWORD count, wcount;
1149
1150 StorageUtl_ReadDWord(data, 0, &count);
1151 if (codepage == CP_UNICODE && count % 2)
1152 {
1153 WARN("Unicode string has odd number of bytes\n");
1154 hr = STG_E_INVALIDHEADER;
1155 }
1156 else
1157 {
1158 if (codepage == CP_UNICODE)
1159 wcount = count / 2;
1160 else
1161 wcount = MultiByteToWideChar(codepage, 0, (LPCSTR)(data + sizeof(DWORD)), count, NULL, 0);
1162
1163 prop->u.bstrVal = SysAllocStringLen(NULL, wcount); /* FIXME: use allocator? */
1164
1165 if (prop->u.bstrVal)
1166 {
1167 if (codepage == CP_UNICODE)
1168 memcpy(prop->u.bstrVal, data + sizeof(DWORD), count);
1169 else
1170 MultiByteToWideChar(codepage, 0, (LPCSTR)(data + sizeof(DWORD)), count, prop->u.bstrVal, wcount);
1171
1172 prop->u.bstrVal[wcount - 1] = '\0';
1173 TRACE("Read string value %s\n", debugstr_w(prop->u.bstrVal));
1174 }
1175 else
1176 hr = STG_E_INSUFFICIENTMEMORY;
1177 }
1178 break;
1179 }
1180 case VT_BLOB:
1181 {
1182 DWORD count;
1183
1184 StorageUtl_ReadDWord(data, 0, &count);
1185 prop->u.blob.cbSize = count;
1186 prop->u.blob.pBlobData = allocate(allocate_data, count);
1187 if (prop->u.blob.pBlobData)
1188 {
1189 memcpy(prop->u.blob.pBlobData, data + sizeof(DWORD), count);
1190 TRACE("Read blob value of size %d\n", count);
1191 }
1192 else
1193 hr = STG_E_INSUFFICIENTMEMORY;
1194 break;
1195 }
1196 case VT_LPWSTR:
1197 {
1198 DWORD count;
1199
1200 StorageUtl_ReadDWord(data, 0, &count);
1201 prop->u.pwszVal = allocate(allocate_data, count * sizeof(WCHAR));
1202 if (prop->u.pwszVal)
1203 {
1204 memcpy(prop->u.pwszVal, data + sizeof(DWORD),
1205 count * sizeof(WCHAR));
1206 /* make sure string is NULL-terminated */
1207 prop->u.pwszVal[count - 1] = '\0';
1208 PropertyStorage_ByteSwapString(prop->u.pwszVal, count);
1209 TRACE("Read string value %s\n", debugstr_w(prop->u.pwszVal));
1210 }
1211 else
1212 hr = STG_E_INSUFFICIENTMEMORY;
1213 break;
1214 }
1215 case VT_FILETIME:
1216 StorageUtl_ReadULargeInteger(data, 0,
1217 (ULARGE_INTEGER *)&prop->u.filetime);
1218 break;
1219 case VT_CF:
1220 {
1221 DWORD len = 0, tag = 0;
1222
1223 StorageUtl_ReadDWord(data, 0, &len);
1224 StorageUtl_ReadDWord(data, 4, &tag);
1225 if (len > 8)
1226 {
1227 len -= 8;
1228 prop->u.pclipdata = allocate(allocate_data, sizeof (CLIPDATA));
1229 prop->u.pclipdata->cbSize = len;
1230 prop->u.pclipdata->ulClipFmt = tag;
1231 prop->u.pclipdata->pClipData = allocate(allocate_data, len - sizeof(prop->u.pclipdata->ulClipFmt));
1232 memcpy(prop->u.pclipdata->pClipData, data+8, len - sizeof(prop->u.pclipdata->ulClipFmt));
1233 }
1234 else
1235 hr = STG_E_INVALIDPARAMETER;
1236 }
1237 break;
1238 default:
1239 FIXME("unsupported type %d\n", prop->vt);
1240 hr = STG_E_INVALIDPARAMETER;
1241 }
1242 return hr;
1243 }
1244
PropertyStorage_ReadHeaderFromStream(IStream * stm,PROPERTYSETHEADER * hdr)1245 static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
1246 PROPERTYSETHEADER *hdr)
1247 {
1248 BYTE buf[sizeof(PROPERTYSETHEADER)];
1249 ULONG count = 0;
1250 HRESULT hr;
1251
1252 assert(stm);
1253 assert(hdr);
1254 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1255 if (SUCCEEDED(hr))
1256 {
1257 if (count != sizeof(buf))
1258 {
1259 WARN("read only %d\n", count);
1260 hr = STG_E_INVALIDHEADER;
1261 }
1262 else
1263 {
1264 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder),
1265 &hdr->wByteOrder);
1266 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat),
1267 &hdr->wFormat);
1268 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer),
1269 &hdr->dwOSVer);
1270 StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid),
1271 &hdr->clsid);
1272 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved),
1273 &hdr->reserved);
1274 }
1275 }
1276 TRACE("returning 0x%08x\n", hr);
1277 return hr;
1278 }
1279
PropertyStorage_ReadFmtIdOffsetFromStream(IStream * stm,FORMATIDOFFSET * fmt)1280 static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm,
1281 FORMATIDOFFSET *fmt)
1282 {
1283 BYTE buf[sizeof(FORMATIDOFFSET)];
1284 ULONG count = 0;
1285 HRESULT hr;
1286
1287 assert(stm);
1288 assert(fmt);
1289 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1290 if (SUCCEEDED(hr))
1291 {
1292 if (count != sizeof(buf))
1293 {
1294 WARN("read only %d\n", count);
1295 hr = STG_E_INVALIDHEADER;
1296 }
1297 else
1298 {
1299 StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid),
1300 &fmt->fmtid);
1301 StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset),
1302 &fmt->dwOffset);
1303 }
1304 }
1305 TRACE("returning 0x%08x\n", hr);
1306 return hr;
1307 }
1308
PropertyStorage_ReadSectionHeaderFromStream(IStream * stm,PROPERTYSECTIONHEADER * hdr)1309 static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
1310 PROPERTYSECTIONHEADER *hdr)
1311 {
1312 BYTE buf[sizeof(PROPERTYSECTIONHEADER)];
1313 ULONG count = 0;
1314 HRESULT hr;
1315
1316 assert(stm);
1317 assert(hdr);
1318 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1319 if (SUCCEEDED(hr))
1320 {
1321 if (count != sizeof(buf))
1322 {
1323 WARN("read only %d\n", count);
1324 hr = STG_E_INVALIDHEADER;
1325 }
1326 else
1327 {
1328 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1329 cbSection), &hdr->cbSection);
1330 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1331 cProperties), &hdr->cProperties);
1332 }
1333 }
1334 TRACE("returning 0x%08x\n", hr);
1335 return hr;
1336 }
1337
PropertyStorage_ReadFromStream(PropertyStorage_impl * This)1338 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This)
1339 {
1340 PROPERTYSETHEADER hdr;
1341 FORMATIDOFFSET fmtOffset;
1342 PROPERTYSECTIONHEADER sectionHdr;
1343 LARGE_INTEGER seek;
1344 ULONG i;
1345 STATSTG stat;
1346 HRESULT hr;
1347 BYTE *buf = NULL;
1348 ULONG count = 0;
1349 DWORD dictOffset = 0;
1350
1351 This->dirty = FALSE;
1352 This->highestProp = 0;
1353 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
1354 if (FAILED(hr))
1355 goto end;
1356 if (stat.cbSize.u.HighPart)
1357 {
1358 WARN("stream too big\n");
1359 /* maximum size varies, but it can't be this big */
1360 hr = STG_E_INVALIDHEADER;
1361 goto end;
1362 }
1363 if (stat.cbSize.u.LowPart == 0)
1364 {
1365 /* empty stream is okay */
1366 hr = S_OK;
1367 goto end;
1368 }
1369 else if (stat.cbSize.u.LowPart < sizeof(PROPERTYSETHEADER) +
1370 sizeof(FORMATIDOFFSET))
1371 {
1372 WARN("stream too small\n");
1373 hr = STG_E_INVALIDHEADER;
1374 goto end;
1375 }
1376 seek.QuadPart = 0;
1377 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1378 if (FAILED(hr))
1379 goto end;
1380 hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr);
1381 /* I've only seen reserved == 1, but the article says I shouldn't disallow
1382 * higher values.
1383 */
1384 if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1)
1385 {
1386 WARN("bad magic in prop set header\n");
1387 hr = STG_E_INVALIDHEADER;
1388 goto end;
1389 }
1390 if (hdr.wFormat != 0 && hdr.wFormat != 1)
1391 {
1392 WARN("bad format version %d\n", hdr.wFormat);
1393 hr = STG_E_INVALIDHEADER;
1394 goto end;
1395 }
1396 This->format = hdr.wFormat;
1397 This->clsid = hdr.clsid;
1398 This->originatorOS = hdr.dwOSVer;
1399 if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC)
1400 WARN("File comes from a Mac, strings will probably be screwed up\n");
1401 hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset);
1402 if (FAILED(hr))
1403 goto end;
1404 if (fmtOffset.dwOffset > stat.cbSize.u.LowPart)
1405 {
1406 WARN("invalid offset %d (stream length is %d)\n", fmtOffset.dwOffset,
1407 stat.cbSize.u.LowPart);
1408 hr = STG_E_INVALIDHEADER;
1409 goto end;
1410 }
1411 /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
1412 * follows not one, but two sections. The first contains the standard properties
1413 * for the document summary information, and the second consists of user-defined
1414 * properties. This is the only case in which multiple sections are
1415 * allowed.
1416 * Reading the second stream isn't implemented yet.
1417 */
1418 hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, §ionHdr);
1419 if (FAILED(hr))
1420 goto end;
1421 /* The section size includes the section header, so check it */
1422 if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER))
1423 {
1424 WARN("section header too small, got %d\n", sectionHdr.cbSection);
1425 hr = STG_E_INVALIDHEADER;
1426 goto end;
1427 }
1428 buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection -
1429 sizeof(PROPERTYSECTIONHEADER));
1430 if (!buf)
1431 {
1432 hr = STG_E_INSUFFICIENTMEMORY;
1433 goto end;
1434 }
1435 hr = IStream_Read(This->stm, buf, sectionHdr.cbSection -
1436 sizeof(PROPERTYSECTIONHEADER), &count);
1437 if (FAILED(hr))
1438 goto end;
1439 TRACE("Reading %d properties:\n", sectionHdr.cProperties);
1440 for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
1441 {
1442 PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(buf +
1443 i * sizeof(PROPERTYIDOFFSET));
1444
1445 if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) ||
1446 idOffset->dwOffset > sectionHdr.cbSection - sizeof(DWORD))
1447 hr = STG_E_INVALIDPOINTER;
1448 else
1449 {
1450 if (idOffset->propid >= PID_FIRST_USABLE &&
1451 idOffset->propid < PID_MIN_READONLY && idOffset->propid >
1452 This->highestProp)
1453 This->highestProp = idOffset->propid;
1454 if (idOffset->propid == PID_DICTIONARY)
1455 {
1456 /* Don't read the dictionary yet, its entries depend on the
1457 * code page. Just store the offset so we know to read it
1458 * later.
1459 */
1460 dictOffset = idOffset->dwOffset;
1461 TRACE("Dictionary offset is %d\n", dictOffset);
1462 }
1463 else
1464 {
1465 PROPVARIANT prop;
1466
1467 PropVariantInit(&prop);
1468 if (SUCCEEDED(PropertyStorage_ReadProperty(&prop,
1469 buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER),
1470 This->codePage, Allocate_CoTaskMemAlloc, NULL)))
1471 {
1472 TRACE("Read property with ID 0x%08x, type %d\n",
1473 idOffset->propid, prop.vt);
1474 switch(idOffset->propid)
1475 {
1476 case PID_CODEPAGE:
1477 if (prop.vt == VT_I2)
1478 This->codePage = (UINT)prop.u.iVal;
1479 break;
1480 case PID_LOCALE:
1481 if (prop.vt == VT_I4)
1482 This->locale = (LCID)prop.u.lVal;
1483 break;
1484 case PID_BEHAVIOR:
1485 if (prop.vt == VT_I4 && prop.u.lVal)
1486 This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
1487 /* The format should already be 1, but just in case */
1488 This->format = 1;
1489 break;
1490 default:
1491 hr = PropertyStorage_StorePropWithId(This,
1492 idOffset->propid, &prop, This->codePage);
1493 }
1494 }
1495 PropVariantClear(&prop);
1496 }
1497 }
1498 }
1499 if (!This->codePage)
1500 {
1501 /* default to Unicode unless told not to, as specified on msdn */
1502 if (This->grfFlags & PROPSETFLAG_ANSI)
1503 This->codePage = GetACP();
1504 else
1505 This->codePage = CP_UNICODE;
1506 }
1507 if (!This->locale)
1508 This->locale = LOCALE_SYSTEM_DEFAULT;
1509 TRACE("Code page is %d, locale is %d\n", This->codePage, This->locale);
1510 if (dictOffset)
1511 hr = PropertyStorage_ReadDictionary(This,
1512 buf + dictOffset - sizeof(PROPERTYSECTIONHEADER));
1513
1514 end:
1515 HeapFree(GetProcessHeap(), 0, buf);
1516 if (FAILED(hr))
1517 {
1518 dictionary_destroy(This->name_to_propid);
1519 This->name_to_propid = NULL;
1520 dictionary_destroy(This->propid_to_name);
1521 This->propid_to_name = NULL;
1522 dictionary_destroy(This->propid_to_prop);
1523 This->propid_to_prop = NULL;
1524 }
1525 return hr;
1526 }
1527
PropertyStorage_MakeHeader(PropertyStorage_impl * This,PROPERTYSETHEADER * hdr)1528 static void PropertyStorage_MakeHeader(PropertyStorage_impl *This,
1529 PROPERTYSETHEADER *hdr)
1530 {
1531 assert(hdr);
1532 StorageUtl_WriteWord((BYTE *)&hdr->wByteOrder, 0,
1533 PROPSETHDR_BYTEORDER_MAGIC);
1534 StorageUtl_WriteWord((BYTE *)&hdr->wFormat, 0, This->format);
1535 StorageUtl_WriteDWord((BYTE *)&hdr->dwOSVer, 0, This->originatorOS);
1536 StorageUtl_WriteGUID((BYTE *)&hdr->clsid, 0, &This->clsid);
1537 StorageUtl_WriteDWord((BYTE *)&hdr->reserved, 0, 1);
1538 }
1539
PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl * This,FORMATIDOFFSET * fmtOffset)1540 static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl *This,
1541 FORMATIDOFFSET *fmtOffset)
1542 {
1543 assert(fmtOffset);
1544 StorageUtl_WriteGUID((BYTE *)fmtOffset, 0, &This->fmtid);
1545 StorageUtl_WriteDWord((BYTE *)fmtOffset, offsetof(FORMATIDOFFSET, dwOffset),
1546 sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET));
1547 }
1548
PropertyStorage_MakeSectionHdr(DWORD cbSection,DWORD numProps,PROPERTYSECTIONHEADER * hdr)1549 static void PropertyStorage_MakeSectionHdr(DWORD cbSection, DWORD numProps,
1550 PROPERTYSECTIONHEADER *hdr)
1551 {
1552 assert(hdr);
1553 StorageUtl_WriteDWord((BYTE *)hdr, 0, cbSection);
1554 StorageUtl_WriteDWord((BYTE *)hdr,
1555 offsetof(PROPERTYSECTIONHEADER, cProperties), numProps);
1556 }
1557
PropertyStorage_MakePropertyIdOffset(DWORD propid,DWORD dwOffset,PROPERTYIDOFFSET * propIdOffset)1558 static void PropertyStorage_MakePropertyIdOffset(DWORD propid, DWORD dwOffset,
1559 PROPERTYIDOFFSET *propIdOffset)
1560 {
1561 assert(propIdOffset);
1562 StorageUtl_WriteDWord((BYTE *)propIdOffset, 0, propid);
1563 StorageUtl_WriteDWord((BYTE *)propIdOffset,
1564 offsetof(PROPERTYIDOFFSET, dwOffset), dwOffset);
1565 }
1566
PropertStorage_WriteWStringToStream(IStream * stm,LPCWSTR str,DWORD len,DWORD * written)1567 static inline HRESULT PropertStorage_WriteWStringToStream(IStream *stm,
1568 LPCWSTR str, DWORD len, DWORD *written)
1569 {
1570 #ifdef WORDS_BIGENDIAN
1571 WCHAR *leStr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1572 HRESULT hr;
1573
1574 if (!leStr)
1575 return E_OUTOFMEMORY;
1576 memcpy(leStr, str, len * sizeof(WCHAR));
1577 PropertyStorage_ByteSwapString(leStr, len);
1578 hr = IStream_Write(stm, leStr, len, written);
1579 HeapFree(GetProcessHeap(), 0, leStr);
1580 return hr;
1581 #else
1582 return IStream_Write(stm, str, len, written);
1583 #endif
1584 }
1585
1586 struct DictionaryClosure
1587 {
1588 HRESULT hr;
1589 DWORD bytesWritten;
1590 };
1591
PropertyStorage_DictionaryWriter(const void * key,const void * value,void * extra,void * closure)1592 static BOOL PropertyStorage_DictionaryWriter(const void *key,
1593 const void *value, void *extra, void *closure)
1594 {
1595 PropertyStorage_impl *This = extra;
1596 struct DictionaryClosure *c = closure;
1597 DWORD propid;
1598 ULONG count;
1599
1600 assert(key);
1601 assert(closure);
1602 StorageUtl_WriteDWord((LPBYTE)&propid, 0, PtrToUlong(value));
1603 c->hr = IStream_Write(This->stm, &propid, sizeof(propid), &count);
1604 if (FAILED(c->hr))
1605 goto end;
1606 c->bytesWritten += sizeof(DWORD);
1607 if (This->codePage == CP_UNICODE)
1608 {
1609 DWORD keyLen, pad = 0;
1610
1611 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0,
1612 (lstrlenW((LPCWSTR)key) + 1) * sizeof(WCHAR));
1613 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1614 if (FAILED(c->hr))
1615 goto end;
1616 c->bytesWritten += sizeof(DWORD);
1617 c->hr = PropertStorage_WriteWStringToStream(This->stm, key, keyLen,
1618 &count);
1619 if (FAILED(c->hr))
1620 goto end;
1621 c->bytesWritten += keyLen * sizeof(WCHAR);
1622 if (keyLen % sizeof(DWORD))
1623 {
1624 c->hr = IStream_Write(This->stm, &pad,
1625 sizeof(DWORD) - keyLen % sizeof(DWORD), &count);
1626 if (FAILED(c->hr))
1627 goto end;
1628 c->bytesWritten += sizeof(DWORD) - keyLen % sizeof(DWORD);
1629 }
1630 }
1631 else
1632 {
1633 DWORD keyLen;
1634
1635 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0, strlen((LPCSTR)key) + 1);
1636 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1637 if (FAILED(c->hr))
1638 goto end;
1639 c->bytesWritten += sizeof(DWORD);
1640 c->hr = IStream_Write(This->stm, key, keyLen, &count);
1641 if (FAILED(c->hr))
1642 goto end;
1643 c->bytesWritten += keyLen;
1644 }
1645 end:
1646 return SUCCEEDED(c->hr);
1647 }
1648
1649 #define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)
1650
1651 /* Writes the dictionary to the stream. Assumes without checking that the
1652 * dictionary isn't empty.
1653 */
PropertyStorage_WriteDictionaryToStream(PropertyStorage_impl * This,DWORD * sectionOffset)1654 static HRESULT PropertyStorage_WriteDictionaryToStream(
1655 PropertyStorage_impl *This, DWORD *sectionOffset)
1656 {
1657 HRESULT hr;
1658 LARGE_INTEGER seek;
1659 PROPERTYIDOFFSET propIdOffset;
1660 ULONG count;
1661 DWORD dwTemp;
1662 struct DictionaryClosure closure;
1663
1664 assert(sectionOffset);
1665
1666 /* The dictionary's always the first property written, so seek to its
1667 * spot.
1668 */
1669 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER);
1670 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1671 if (FAILED(hr))
1672 goto end;
1673 PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY, *sectionOffset,
1674 &propIdOffset);
1675 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1676 if (FAILED(hr))
1677 goto end;
1678
1679 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1680 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1681 if (FAILED(hr))
1682 goto end;
1683 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0,
1684 dictionary_num_entries(This->name_to_propid));
1685 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1686 if (FAILED(hr))
1687 goto end;
1688 *sectionOffset += sizeof(dwTemp);
1689
1690 closure.hr = S_OK;
1691 closure.bytesWritten = 0;
1692 dictionary_enumerate(This->name_to_propid, PropertyStorage_DictionaryWriter,
1693 &closure);
1694 hr = closure.hr;
1695 if (FAILED(hr))
1696 goto end;
1697 *sectionOffset += closure.bytesWritten;
1698 if (closure.bytesWritten % sizeof(DWORD))
1699 {
1700 DWORD padding = sizeof(DWORD) - closure.bytesWritten % sizeof(DWORD);
1701 TRACE("adding %d bytes of padding\n", padding);
1702 *sectionOffset += padding;
1703 }
1704
1705 end:
1706 return hr;
1707 }
1708
PropertyStorage_WritePropertyToStream(PropertyStorage_impl * This,DWORD propNum,DWORD propid,const PROPVARIANT * var,DWORD * sectionOffset)1709 static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This,
1710 DWORD propNum, DWORD propid, const PROPVARIANT *var, DWORD *sectionOffset)
1711 {
1712 HRESULT hr;
1713 LARGE_INTEGER seek;
1714 PROPERTYIDOFFSET propIdOffset;
1715 ULONG count;
1716 DWORD dwType, bytesWritten;
1717
1718 assert(var);
1719 assert(sectionOffset);
1720
1721 TRACE("%p, %d, 0x%08x, (%d), (%d)\n", This, propNum, propid, var->vt,
1722 *sectionOffset);
1723
1724 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) +
1725 propNum * sizeof(PROPERTYIDOFFSET);
1726 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1727 if (FAILED(hr))
1728 goto end;
1729 PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset);
1730 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1731 if (FAILED(hr))
1732 goto end;
1733
1734 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1735 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1736 if (FAILED(hr))
1737 goto end;
1738 StorageUtl_WriteDWord((LPBYTE)&dwType, 0, var->vt);
1739 hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count);
1740 if (FAILED(hr))
1741 goto end;
1742 *sectionOffset += sizeof(dwType);
1743
1744 switch (var->vt)
1745 {
1746 case VT_EMPTY:
1747 case VT_NULL:
1748 bytesWritten = 0;
1749 break;
1750 case VT_I1:
1751 case VT_UI1:
1752 hr = IStream_Write(This->stm, &var->u.cVal, sizeof(var->u.cVal),
1753 &count);
1754 bytesWritten = count;
1755 break;
1756 case VT_I2:
1757 case VT_UI2:
1758 {
1759 WORD wTemp;
1760
1761 StorageUtl_WriteWord((LPBYTE)&wTemp, 0, var->u.iVal);
1762 hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count);
1763 bytesWritten = count;
1764 break;
1765 }
1766 case VT_I4:
1767 case VT_UI4:
1768 {
1769 DWORD dwTemp;
1770
1771 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, var->u.lVal);
1772 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1773 bytesWritten = count;
1774 break;
1775 }
1776 case VT_LPSTR:
1777 {
1778 DWORD len, dwTemp;
1779
1780 if (This->codePage == CP_UNICODE)
1781 len = (lstrlenW(var->u.pwszVal) + 1) * sizeof(WCHAR);
1782 else
1783 len = lstrlenA(var->u.pszVal) + 1;
1784 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1785 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1786 if (FAILED(hr))
1787 goto end;
1788 hr = IStream_Write(This->stm, var->u.pszVal, len, &count);
1789 bytesWritten = count + sizeof(DWORD);
1790 break;
1791 }
1792 case VT_LPWSTR:
1793 {
1794 DWORD len = lstrlenW(var->u.pwszVal) + 1, dwTemp;
1795
1796 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1797 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1798 if (FAILED(hr))
1799 goto end;
1800 hr = IStream_Write(This->stm, var->u.pwszVal, len * sizeof(WCHAR),
1801 &count);
1802 bytesWritten = count + sizeof(DWORD);
1803 break;
1804 }
1805 case VT_FILETIME:
1806 {
1807 FILETIME temp;
1808
1809 StorageUtl_WriteULargeInteger((BYTE *)&temp, 0,
1810 (const ULARGE_INTEGER *)&var->u.filetime);
1811 hr = IStream_Write(This->stm, &temp, sizeof(FILETIME), &count);
1812 bytesWritten = count;
1813 break;
1814 }
1815 case VT_CF:
1816 {
1817 DWORD cf_hdr[2], len;
1818
1819 len = var->u.pclipdata->cbSize;
1820 StorageUtl_WriteDWord((LPBYTE)&cf_hdr[0], 0, len + 8);
1821 StorageUtl_WriteDWord((LPBYTE)&cf_hdr[1], 0, var->u.pclipdata->ulClipFmt);
1822 hr = IStream_Write(This->stm, cf_hdr, sizeof(cf_hdr), &count);
1823 if (FAILED(hr))
1824 goto end;
1825 hr = IStream_Write(This->stm, var->u.pclipdata->pClipData,
1826 len - sizeof(var->u.pclipdata->ulClipFmt), &count);
1827 if (FAILED(hr))
1828 goto end;
1829 bytesWritten = count + sizeof cf_hdr;
1830 break;
1831 }
1832 case VT_CLSID:
1833 {
1834 CLSID temp;
1835
1836 StorageUtl_WriteGUID((BYTE *)&temp, 0, var->u.puuid);
1837 hr = IStream_Write(This->stm, &temp, sizeof(temp), &count);
1838 bytesWritten = count;
1839 break;
1840 }
1841 default:
1842 FIXME("unsupported type: %d\n", var->vt);
1843 return STG_E_INVALIDPARAMETER;
1844 }
1845
1846 if (SUCCEEDED(hr))
1847 {
1848 *sectionOffset += bytesWritten;
1849 if (bytesWritten % sizeof(DWORD))
1850 {
1851 DWORD padding = sizeof(DWORD) - bytesWritten % sizeof(DWORD);
1852 TRACE("adding %d bytes of padding\n", padding);
1853 *sectionOffset += padding;
1854 }
1855 }
1856
1857 end:
1858 return hr;
1859 }
1860
1861 struct PropertyClosure
1862 {
1863 HRESULT hr;
1864 DWORD propNum;
1865 DWORD *sectionOffset;
1866 };
1867
PropertyStorage_PropertiesWriter(const void * key,const void * value,void * extra,void * closure)1868 static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value,
1869 void *extra, void *closure)
1870 {
1871 PropertyStorage_impl *This = extra;
1872 struct PropertyClosure *c = closure;
1873
1874 assert(key);
1875 assert(value);
1876 assert(extra);
1877 assert(closure);
1878 c->hr = PropertyStorage_WritePropertyToStream(This, c->propNum++,
1879 PtrToUlong(key), value, c->sectionOffset);
1880 return SUCCEEDED(c->hr);
1881 }
1882
PropertyStorage_WritePropertiesToStream(PropertyStorage_impl * This,DWORD startingPropNum,DWORD * sectionOffset)1883 static HRESULT PropertyStorage_WritePropertiesToStream(
1884 PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset)
1885 {
1886 struct PropertyClosure closure;
1887
1888 assert(sectionOffset);
1889 closure.hr = S_OK;
1890 closure.propNum = startingPropNum;
1891 closure.sectionOffset = sectionOffset;
1892 dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter,
1893 &closure);
1894 return closure.hr;
1895 }
1896
PropertyStorage_WriteHeadersToStream(PropertyStorage_impl * This)1897 static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This)
1898 {
1899 HRESULT hr;
1900 ULONG count = 0;
1901 LARGE_INTEGER seek = { {0} };
1902 PROPERTYSETHEADER hdr;
1903 FORMATIDOFFSET fmtOffset;
1904
1905 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1906 if (FAILED(hr))
1907 goto end;
1908 PropertyStorage_MakeHeader(This, &hdr);
1909 hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count);
1910 if (FAILED(hr))
1911 goto end;
1912 if (count != sizeof(hdr))
1913 {
1914 hr = STG_E_WRITEFAULT;
1915 goto end;
1916 }
1917
1918 PropertyStorage_MakeFmtIdOffset(This, &fmtOffset);
1919 hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count);
1920 if (FAILED(hr))
1921 goto end;
1922 if (count != sizeof(fmtOffset))
1923 {
1924 hr = STG_E_WRITEFAULT;
1925 goto end;
1926 }
1927 hr = S_OK;
1928
1929 end:
1930 return hr;
1931 }
1932
PropertyStorage_WriteToStream(PropertyStorage_impl * This)1933 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This)
1934 {
1935 PROPERTYSECTIONHEADER sectionHdr;
1936 HRESULT hr;
1937 ULONG count;
1938 LARGE_INTEGER seek;
1939 DWORD numProps, prop, sectionOffset, dwTemp;
1940 PROPVARIANT var;
1941
1942 PropertyStorage_WriteHeadersToStream(This);
1943
1944 /* Count properties. Always at least one property, the code page */
1945 numProps = 1;
1946 if (dictionary_num_entries(This->name_to_propid))
1947 numProps++;
1948 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1949 numProps++;
1950 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1951 numProps++;
1952 numProps += dictionary_num_entries(This->propid_to_prop);
1953
1954 /* Write section header with 0 bytes right now, I'll adjust it after
1955 * writing properties.
1956 */
1957 PropertyStorage_MakeSectionHdr(0, numProps, §ionHdr);
1958 seek.QuadPart = SECTIONHEADER_OFFSET;
1959 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1960 if (FAILED(hr))
1961 goto end;
1962 hr = IStream_Write(This->stm, §ionHdr, sizeof(sectionHdr), &count);
1963 if (FAILED(hr))
1964 goto end;
1965
1966 prop = 0;
1967 sectionOffset = sizeof(PROPERTYSECTIONHEADER) +
1968 numProps * sizeof(PROPERTYIDOFFSET);
1969
1970 if (dictionary_num_entries(This->name_to_propid))
1971 {
1972 prop++;
1973 hr = PropertyStorage_WriteDictionaryToStream(This, §ionOffset);
1974 if (FAILED(hr))
1975 goto end;
1976 }
1977
1978 PropVariantInit(&var);
1979
1980 var.vt = VT_I2;
1981 var.u.iVal = This->codePage;
1982 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE,
1983 &var, §ionOffset);
1984 if (FAILED(hr))
1985 goto end;
1986
1987 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1988 {
1989 var.vt = VT_I4;
1990 var.u.lVal = This->locale;
1991 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE,
1992 &var, §ionOffset);
1993 if (FAILED(hr))
1994 goto end;
1995 }
1996
1997 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1998 {
1999 var.vt = VT_I4;
2000 var.u.lVal = 1;
2001 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR,
2002 &var, §ionOffset);
2003 if (FAILED(hr))
2004 goto end;
2005 }
2006
2007 hr = PropertyStorage_WritePropertiesToStream(This, prop, §ionOffset);
2008 if (FAILED(hr))
2009 goto end;
2010
2011 /* Now write the byte count of the section */
2012 seek.QuadPart = SECTIONHEADER_OFFSET;
2013 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2014 if (FAILED(hr))
2015 goto end;
2016 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, sectionOffset);
2017 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2018
2019 end:
2020 return hr;
2021 }
2022
2023 /***********************************************************************
2024 * PropertyStorage_Construct
2025 */
PropertyStorage_DestroyDictionaries(PropertyStorage_impl * This)2026 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This)
2027 {
2028 dictionary_destroy(This->name_to_propid);
2029 This->name_to_propid = NULL;
2030 dictionary_destroy(This->propid_to_name);
2031 This->propid_to_name = NULL;
2032 dictionary_destroy(This->propid_to_prop);
2033 This->propid_to_prop = NULL;
2034 }
2035
PropertyStorage_CreateDictionaries(PropertyStorage_impl * This)2036 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This)
2037 {
2038 HRESULT hr = S_OK;
2039
2040 This->name_to_propid = dictionary_create(
2041 PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy,
2042 This);
2043 if (!This->name_to_propid)
2044 {
2045 hr = STG_E_INSUFFICIENTMEMORY;
2046 goto end;
2047 }
2048 This->propid_to_name = dictionary_create(PropertyStorage_PropCompare,
2049 NULL, This);
2050 if (!This->propid_to_name)
2051 {
2052 hr = STG_E_INSUFFICIENTMEMORY;
2053 goto end;
2054 }
2055 This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
2056 PropertyStorage_PropertyDestroy, This);
2057 if (!This->propid_to_prop)
2058 {
2059 hr = STG_E_INSUFFICIENTMEMORY;
2060 goto end;
2061 }
2062 end:
2063 if (FAILED(hr))
2064 PropertyStorage_DestroyDictionaries(This);
2065 return hr;
2066 }
2067
PropertyStorage_BaseConstruct(IStream * stm,REFFMTID rfmtid,DWORD grfMode,PropertyStorage_impl ** pps)2068 static HRESULT PropertyStorage_BaseConstruct(IStream *stm,
2069 REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps)
2070 {
2071 HRESULT hr = S_OK;
2072
2073 assert(pps);
2074 assert(rfmtid);
2075 *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps);
2076 if (!*pps)
2077 return E_OUTOFMEMORY;
2078
2079 (*pps)->IPropertyStorage_iface.lpVtbl = &IPropertyStorage_Vtbl;
2080 (*pps)->ref = 1;
2081 InitializeCriticalSection(&(*pps)->cs);
2082 (*pps)->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PropertyStorage_impl.cs");
2083 (*pps)->stm = stm;
2084 (*pps)->fmtid = *rfmtid;
2085 (*pps)->grfMode = grfMode;
2086
2087 hr = PropertyStorage_CreateDictionaries(*pps);
2088 if (FAILED(hr))
2089 {
2090 (*pps)->cs.DebugInfo->Spare[0] = 0;
2091 DeleteCriticalSection(&(*pps)->cs);
2092 HeapFree(GetProcessHeap(), 0, *pps);
2093 *pps = NULL;
2094 }
2095 else IStream_AddRef( stm );
2096
2097 return hr;
2098 }
2099
PropertyStorage_ConstructFromStream(IStream * stm,REFFMTID rfmtid,DWORD grfMode,IPropertyStorage ** pps)2100 static HRESULT PropertyStorage_ConstructFromStream(IStream *stm,
2101 REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps)
2102 {
2103 PropertyStorage_impl *ps;
2104 HRESULT hr;
2105
2106 assert(pps);
2107 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2108 if (SUCCEEDED(hr))
2109 {
2110 hr = PropertyStorage_ReadFromStream(ps);
2111 if (SUCCEEDED(hr))
2112 {
2113 *pps = &ps->IPropertyStorage_iface;
2114 TRACE("PropertyStorage %p constructed\n", ps);
2115 hr = S_OK;
2116 }
2117 else IPropertyStorage_Release( &ps->IPropertyStorage_iface );
2118 }
2119 return hr;
2120 }
2121
PropertyStorage_ConstructEmpty(IStream * stm,REFFMTID rfmtid,DWORD grfFlags,DWORD grfMode,IPropertyStorage ** pps)2122 static HRESULT PropertyStorage_ConstructEmpty(IStream *stm,
2123 REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
2124 {
2125 PropertyStorage_impl *ps;
2126 HRESULT hr;
2127
2128 assert(pps);
2129 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2130 if (SUCCEEDED(hr))
2131 {
2132 ps->format = 0;
2133 ps->grfFlags = grfFlags;
2134 if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2135 ps->format = 1;
2136 /* default to Unicode unless told not to, as specified on msdn */
2137 if (ps->grfFlags & PROPSETFLAG_ANSI)
2138 ps->codePage = GetACP();
2139 else
2140 ps->codePage = CP_UNICODE;
2141 ps->locale = LOCALE_SYSTEM_DEFAULT;
2142 TRACE("Code page is %d, locale is %d\n", ps->codePage, ps->locale);
2143 *pps = &ps->IPropertyStorage_iface;
2144 TRACE("PropertyStorage %p constructed\n", ps);
2145 hr = S_OK;
2146 }
2147 return hr;
2148 }
2149
2150
2151 /***********************************************************************
2152 * Implementation of IPropertySetStorage
2153 */
2154
2155 /************************************************************************
2156 * IPropertySetStorage_fnQueryInterface (IUnknown)
2157 *
2158 * This method forwards to the common QueryInterface implementation
2159 */
IPropertySetStorage_fnQueryInterface(IPropertySetStorage * ppstg,REFIID riid,void ** ppvObject)2160 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
2161 IPropertySetStorage *ppstg,
2162 REFIID riid,
2163 void** ppvObject)
2164 {
2165 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2166 return IStorage_QueryInterface( &This->base.IStorage_iface, riid, ppvObject );
2167 }
2168
2169 /************************************************************************
2170 * IPropertySetStorage_fnAddRef (IUnknown)
2171 *
2172 * This method forwards to the common AddRef implementation
2173 */
IPropertySetStorage_fnAddRef(IPropertySetStorage * ppstg)2174 static ULONG WINAPI IPropertySetStorage_fnAddRef(
2175 IPropertySetStorage *ppstg)
2176 {
2177 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2178 return IStorage_AddRef( &This->base.IStorage_iface );
2179 }
2180
2181 /************************************************************************
2182 * IPropertySetStorage_fnRelease (IUnknown)
2183 *
2184 * This method forwards to the common Release implementation
2185 */
IPropertySetStorage_fnRelease(IPropertySetStorage * ppstg)2186 static ULONG WINAPI IPropertySetStorage_fnRelease(
2187 IPropertySetStorage *ppstg)
2188 {
2189 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2190 return IStorage_Release( &This->base.IStorage_iface );
2191 }
2192
2193 /************************************************************************
2194 * IPropertySetStorage_fnCreate (IPropertySetStorage)
2195 */
IPropertySetStorage_fnCreate(IPropertySetStorage * ppstg,REFFMTID rfmtid,const CLSID * pclsid,DWORD grfFlags,DWORD grfMode,IPropertyStorage ** ppprstg)2196 static HRESULT WINAPI IPropertySetStorage_fnCreate(
2197 IPropertySetStorage *ppstg,
2198 REFFMTID rfmtid,
2199 const CLSID* pclsid,
2200 DWORD grfFlags,
2201 DWORD grfMode,
2202 IPropertyStorage** ppprstg)
2203 {
2204 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2205 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2206 IStream *stm = NULL;
2207 HRESULT r;
2208
2209 TRACE("%p %s %08x %08x %p\n", This, debugstr_guid(rfmtid), grfFlags,
2210 grfMode, ppprstg);
2211
2212 /* be picky */
2213 if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
2214 {
2215 r = STG_E_INVALIDFLAG;
2216 goto end;
2217 }
2218
2219 if (!rfmtid)
2220 {
2221 r = E_INVALIDARG;
2222 goto end;
2223 }
2224
2225 /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
2226 * storage, not a stream. For now, disallow it.
2227 */
2228 if (grfFlags & PROPSETFLAG_NONSIMPLE)
2229 {
2230 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2231 r = STG_E_INVALIDFLAG;
2232 goto end;
2233 }
2234
2235 r = FmtIdToPropStgName(rfmtid, name);
2236 if (FAILED(r))
2237 goto end;
2238
2239 r = IStorage_CreateStream( &This->base.IStorage_iface, name, grfMode, 0, 0, &stm );
2240 if (FAILED(r))
2241 goto end;
2242
2243 r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg);
2244
2245 IStream_Release( stm );
2246
2247 end:
2248 TRACE("returning 0x%08x\n", r);
2249 return r;
2250 }
2251
2252 /************************************************************************
2253 * IPropertySetStorage_fnOpen (IPropertySetStorage)
2254 */
IPropertySetStorage_fnOpen(IPropertySetStorage * ppstg,REFFMTID rfmtid,DWORD grfMode,IPropertyStorage ** ppprstg)2255 static HRESULT WINAPI IPropertySetStorage_fnOpen(
2256 IPropertySetStorage *ppstg,
2257 REFFMTID rfmtid,
2258 DWORD grfMode,
2259 IPropertyStorage** ppprstg)
2260 {
2261 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2262 IStream *stm = NULL;
2263 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2264 HRESULT r;
2265
2266 TRACE("%p %s %08x %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
2267
2268 /* be picky */
2269 if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
2270 grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
2271 {
2272 r = STG_E_INVALIDFLAG;
2273 goto end;
2274 }
2275
2276 if (!rfmtid)
2277 {
2278 r = E_INVALIDARG;
2279 goto end;
2280 }
2281
2282 r = FmtIdToPropStgName(rfmtid, name);
2283 if (FAILED(r))
2284 goto end;
2285
2286 r = IStorage_OpenStream( &This->base.IStorage_iface, name, 0, grfMode, 0, &stm );
2287 if (FAILED(r))
2288 goto end;
2289
2290 r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg);
2291
2292 IStream_Release( stm );
2293
2294 end:
2295 TRACE("returning 0x%08x\n", r);
2296 return r;
2297 }
2298
2299 /************************************************************************
2300 * IPropertySetStorage_fnDelete (IPropertySetStorage)
2301 */
IPropertySetStorage_fnDelete(IPropertySetStorage * ppstg,REFFMTID rfmtid)2302 static HRESULT WINAPI IPropertySetStorage_fnDelete(
2303 IPropertySetStorage *ppstg,
2304 REFFMTID rfmtid)
2305 {
2306 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2307 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2308 HRESULT r;
2309
2310 TRACE("%p %s\n", This, debugstr_guid(rfmtid));
2311
2312 if (!rfmtid)
2313 return E_INVALIDARG;
2314
2315 r = FmtIdToPropStgName(rfmtid, name);
2316 if (FAILED(r))
2317 return r;
2318
2319 return IStorage_DestroyElement(&This->base.IStorage_iface, name);
2320 }
2321
2322 /************************************************************************
2323 * IPropertySetStorage_fnEnum (IPropertySetStorage)
2324 */
IPropertySetStorage_fnEnum(IPropertySetStorage * ppstg,IEnumSTATPROPSETSTG ** ppenum)2325 static HRESULT WINAPI IPropertySetStorage_fnEnum(
2326 IPropertySetStorage *ppstg,
2327 IEnumSTATPROPSETSTG** ppenum)
2328 {
2329 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2330 return create_EnumSTATPROPSETSTG(This, ppenum);
2331 }
2332
2333 /************************************************************************
2334 * Implement IEnumSTATPROPSETSTG using enumx
2335 */
IEnumSTATPROPSETSTG_fnQueryInterface(IEnumSTATPROPSETSTG * iface,REFIID riid,void ** ppvObject)2336 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnQueryInterface(
2337 IEnumSTATPROPSETSTG *iface,
2338 REFIID riid,
2339 void** ppvObject)
2340 {
2341 return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
2342 }
2343
IEnumSTATPROPSETSTG_fnAddRef(IEnumSTATPROPSETSTG * iface)2344 static ULONG WINAPI IEnumSTATPROPSETSTG_fnAddRef(
2345 IEnumSTATPROPSETSTG *iface)
2346 {
2347 return enumx_AddRef((enumx_impl*)iface);
2348 }
2349
IEnumSTATPROPSETSTG_fnRelease(IEnumSTATPROPSETSTG * iface)2350 static ULONG WINAPI IEnumSTATPROPSETSTG_fnRelease(
2351 IEnumSTATPROPSETSTG *iface)
2352 {
2353 return enumx_Release((enumx_impl*)iface);
2354 }
2355
IEnumSTATPROPSETSTG_fnNext(IEnumSTATPROPSETSTG * iface,ULONG celt,STATPROPSETSTG * rgelt,ULONG * pceltFetched)2356 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnNext(
2357 IEnumSTATPROPSETSTG *iface,
2358 ULONG celt,
2359 STATPROPSETSTG *rgelt,
2360 ULONG *pceltFetched)
2361 {
2362 return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
2363 }
2364
IEnumSTATPROPSETSTG_fnSkip(IEnumSTATPROPSETSTG * iface,ULONG celt)2365 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnSkip(
2366 IEnumSTATPROPSETSTG *iface,
2367 ULONG celt)
2368 {
2369 return enumx_Skip((enumx_impl*)iface, celt);
2370 }
2371
IEnumSTATPROPSETSTG_fnReset(IEnumSTATPROPSETSTG * iface)2372 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnReset(
2373 IEnumSTATPROPSETSTG *iface)
2374 {
2375 return enumx_Reset((enumx_impl*)iface);
2376 }
2377
IEnumSTATPROPSETSTG_fnClone(IEnumSTATPROPSETSTG * iface,IEnumSTATPROPSETSTG ** ppenum)2378 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnClone(
2379 IEnumSTATPROPSETSTG *iface,
2380 IEnumSTATPROPSETSTG **ppenum)
2381 {
2382 return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
2383 }
2384
create_EnumSTATPROPSETSTG(StorageImpl * This,IEnumSTATPROPSETSTG ** ppenum)2385 static HRESULT create_EnumSTATPROPSETSTG(
2386 StorageImpl *This,
2387 IEnumSTATPROPSETSTG** ppenum)
2388 {
2389 IStorage *stg = &This->base.IStorage_iface;
2390 IEnumSTATSTG *penum = NULL;
2391 STATSTG stat;
2392 ULONG count;
2393 HRESULT r;
2394 STATPROPSETSTG statpss;
2395 enumx_impl *enumx;
2396
2397 TRACE("%p %p\n", This, ppenum);
2398
2399 enumx = enumx_allocate(&IID_IEnumSTATPROPSETSTG,
2400 &IEnumSTATPROPSETSTG_Vtbl,
2401 sizeof (STATPROPSETSTG),
2402 (IUnknown*)&This->base.IStorage_iface,
2403 NULL);
2404
2405 /* add all the property set elements into a list */
2406 r = IStorage_EnumElements(stg, 0, NULL, 0, &penum);
2407 if (FAILED(r))
2408 {
2409 enumx_Release(enumx);
2410 return E_OUTOFMEMORY;
2411 }
2412
2413 while (1)
2414 {
2415 count = 0;
2416 r = IEnumSTATSTG_Next(penum, 1, &stat, &count);
2417 if (FAILED(r))
2418 break;
2419 if (!count)
2420 break;
2421 if (!stat.pwcsName)
2422 continue;
2423 if (stat.pwcsName[0] == 5 && stat.type == STGTY_STREAM)
2424 {
2425 PropStgNameToFmtId(stat.pwcsName, &statpss.fmtid);
2426 TRACE("adding %s (%s)\n", debugstr_w(stat.pwcsName),
2427 debugstr_guid(&statpss.fmtid));
2428 statpss.mtime = stat.mtime;
2429 statpss.atime = stat.atime;
2430 statpss.ctime = stat.ctime;
2431 statpss.grfFlags = stat.grfMode;
2432 statpss.clsid = stat.clsid;
2433 enumx_add_element(enumx, &statpss);
2434 }
2435 CoTaskMemFree(stat.pwcsName);
2436 }
2437 IEnumSTATSTG_Release(penum);
2438
2439 *ppenum = (IEnumSTATPROPSETSTG*) enumx;
2440
2441 return S_OK;
2442 }
2443
2444 /************************************************************************
2445 * Implement IEnumSTATPROPSTG using enumx
2446 */
IEnumSTATPROPSTG_fnQueryInterface(IEnumSTATPROPSTG * iface,REFIID riid,void ** ppvObject)2447 static HRESULT WINAPI IEnumSTATPROPSTG_fnQueryInterface(
2448 IEnumSTATPROPSTG *iface,
2449 REFIID riid,
2450 void** ppvObject)
2451 {
2452 return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
2453 }
2454
IEnumSTATPROPSTG_fnAddRef(IEnumSTATPROPSTG * iface)2455 static ULONG WINAPI IEnumSTATPROPSTG_fnAddRef(
2456 IEnumSTATPROPSTG *iface)
2457 {
2458 return enumx_AddRef((enumx_impl*)iface);
2459 }
2460
IEnumSTATPROPSTG_fnRelease(IEnumSTATPROPSTG * iface)2461 static ULONG WINAPI IEnumSTATPROPSTG_fnRelease(
2462 IEnumSTATPROPSTG *iface)
2463 {
2464 return enumx_Release((enumx_impl*)iface);
2465 }
2466
IEnumSTATPROPSTG_fnNext(IEnumSTATPROPSTG * iface,ULONG celt,STATPROPSTG * rgelt,ULONG * pceltFetched)2467 static HRESULT WINAPI IEnumSTATPROPSTG_fnNext(
2468 IEnumSTATPROPSTG *iface,
2469 ULONG celt,
2470 STATPROPSTG *rgelt,
2471 ULONG *pceltFetched)
2472 {
2473 return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
2474 }
2475
IEnumSTATPROPSTG_fnSkip(IEnumSTATPROPSTG * iface,ULONG celt)2476 static HRESULT WINAPI IEnumSTATPROPSTG_fnSkip(
2477 IEnumSTATPROPSTG *iface,
2478 ULONG celt)
2479 {
2480 return enumx_Skip((enumx_impl*)iface, celt);
2481 }
2482
IEnumSTATPROPSTG_fnReset(IEnumSTATPROPSTG * iface)2483 static HRESULT WINAPI IEnumSTATPROPSTG_fnReset(
2484 IEnumSTATPROPSTG *iface)
2485 {
2486 return enumx_Reset((enumx_impl*)iface);
2487 }
2488
IEnumSTATPROPSTG_fnClone(IEnumSTATPROPSTG * iface,IEnumSTATPROPSTG ** ppenum)2489 static HRESULT WINAPI IEnumSTATPROPSTG_fnClone(
2490 IEnumSTATPROPSTG *iface,
2491 IEnumSTATPROPSTG **ppenum)
2492 {
2493 return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
2494 }
2495
prop_enum_copy_cb(IUnknown * parent,void * orig,void * dest)2496 static void prop_enum_copy_cb(IUnknown *parent, void *orig, void *dest)
2497 {
2498 PropertyStorage_impl *storage = impl_from_IPropertyStorage((IPropertyStorage*)parent);
2499 STATPROPSTG *src_prop = orig;
2500 STATPROPSTG *dest_prop = dest;
2501 LPWSTR name;
2502
2503 dest_prop->propid = src_prop->propid;
2504 dest_prop->vt = src_prop->vt;
2505 dest_prop->lpwstrName = NULL;
2506
2507 if (dictionary_find(storage->propid_to_name, UlongToPtr(src_prop->propid), (void**)&name))
2508 {
2509 DWORD size = (lstrlenW(name) + 1) * sizeof(WCHAR);
2510
2511 dest_prop->lpwstrName = CoTaskMemAlloc(size);
2512 if (!dest_prop->lpwstrName) return;
2513 memcpy(dest_prop->lpwstrName, name, size);
2514 }
2515 }
2516
prop_enum_stat(const void * k,const void * v,void * extra,void * arg)2517 static BOOL prop_enum_stat(const void *k, const void *v, void *extra, void *arg)
2518 {
2519 enumx_impl *enumx = arg;
2520 PROPID propid = PtrToUlong(k);
2521 const PROPVARIANT *prop = v;
2522 STATPROPSTG stat;
2523
2524 stat.lpwstrName = NULL;
2525 stat.propid = propid;
2526 stat.vt = prop->vt;
2527
2528 enumx_add_element(enumx, &stat);
2529
2530 return TRUE;
2531 }
2532
create_EnumSTATPROPSTG(PropertyStorage_impl * This,IEnumSTATPROPSTG ** ppenum)2533 static HRESULT create_EnumSTATPROPSTG(
2534 PropertyStorage_impl *This,
2535 IEnumSTATPROPSTG** ppenum)
2536 {
2537 enumx_impl *enumx;
2538
2539 TRACE("%p %p\n", This, ppenum);
2540
2541 enumx = enumx_allocate(&IID_IEnumSTATPROPSTG,
2542 &IEnumSTATPROPSTG_Vtbl,
2543 sizeof (STATPROPSTG),
2544 (IUnknown*)&This->IPropertyStorage_iface,
2545 prop_enum_copy_cb);
2546
2547 dictionary_enumerate(This->propid_to_prop, prop_enum_stat, enumx);
2548
2549 *ppenum = (IEnumSTATPROPSTG*) enumx;
2550
2551 return S_OK;
2552 }
2553
2554 /***********************************************************************
2555 * vtables
2556 */
2557 const IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
2558 {
2559 IPropertySetStorage_fnQueryInterface,
2560 IPropertySetStorage_fnAddRef,
2561 IPropertySetStorage_fnRelease,
2562 IPropertySetStorage_fnCreate,
2563 IPropertySetStorage_fnOpen,
2564 IPropertySetStorage_fnDelete,
2565 IPropertySetStorage_fnEnum
2566 };
2567
2568 static const IPropertyStorageVtbl IPropertyStorage_Vtbl =
2569 {
2570 IPropertyStorage_fnQueryInterface,
2571 IPropertyStorage_fnAddRef,
2572 IPropertyStorage_fnRelease,
2573 IPropertyStorage_fnReadMultiple,
2574 IPropertyStorage_fnWriteMultiple,
2575 IPropertyStorage_fnDeleteMultiple,
2576 IPropertyStorage_fnReadPropertyNames,
2577 IPropertyStorage_fnWritePropertyNames,
2578 IPropertyStorage_fnDeletePropertyNames,
2579 IPropertyStorage_fnCommit,
2580 IPropertyStorage_fnRevert,
2581 IPropertyStorage_fnEnum,
2582 IPropertyStorage_fnSetTimes,
2583 IPropertyStorage_fnSetClass,
2584 IPropertyStorage_fnStat,
2585 };
2586
2587 static const IEnumSTATPROPSETSTGVtbl IEnumSTATPROPSETSTG_Vtbl =
2588 {
2589 IEnumSTATPROPSETSTG_fnQueryInterface,
2590 IEnumSTATPROPSETSTG_fnAddRef,
2591 IEnumSTATPROPSETSTG_fnRelease,
2592 IEnumSTATPROPSETSTG_fnNext,
2593 IEnumSTATPROPSETSTG_fnSkip,
2594 IEnumSTATPROPSETSTG_fnReset,
2595 IEnumSTATPROPSETSTG_fnClone,
2596 };
2597
2598 static const IEnumSTATPROPSTGVtbl IEnumSTATPROPSTG_Vtbl =
2599 {
2600 IEnumSTATPROPSTG_fnQueryInterface,
2601 IEnumSTATPROPSTG_fnAddRef,
2602 IEnumSTATPROPSTG_fnRelease,
2603 IEnumSTATPROPSTG_fnNext,
2604 IEnumSTATPROPSTG_fnSkip,
2605 IEnumSTATPROPSTG_fnReset,
2606 IEnumSTATPROPSTG_fnClone,
2607 };
2608
2609 /***********************************************************************
2610 * Format ID <-> name conversion
2611 */
2612 static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
2613 'I','n','f','o','r','m','a','t','i','o','n',0 };
2614 static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t',
2615 'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
2616
2617 #define BITS_PER_BYTE 8
2618 #define CHARMASK 0x1f
2619 #define BITS_IN_CHARMASK 5
2620 #define NUM_ALPHA_CHARS 26
2621
2622 /***********************************************************************
2623 * FmtIdToPropStgName [ole32.@]
2624 * Returns the storage name of the format ID rfmtid.
2625 * PARAMS
2626 * rfmtid [I] Format ID for which to return a storage name
2627 * str [O] Storage name associated with rfmtid.
2628 *
2629 * RETURNS
2630 * E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise.
2631 *
2632 * NOTES
2633 * str must be at least CCH_MAX_PROPSTG_NAME characters in length.
2634 */
FmtIdToPropStgName(const FMTID * rfmtid,LPOLESTR str)2635 HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str)
2636 {
2637 static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
2638
2639 TRACE("%s, %p\n", debugstr_guid(rfmtid), str);
2640
2641 if (!rfmtid) return E_INVALIDARG;
2642 if (!str) return E_INVALIDARG;
2643
2644 if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
2645 lstrcpyW(str, szSummaryInfo);
2646 else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
2647 lstrcpyW(str, szDocSummaryInfo);
2648 else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid))
2649 lstrcpyW(str, szDocSummaryInfo);
2650 else
2651 {
2652 const BYTE *fmtptr;
2653 WCHAR *pstr = str;
2654 ULONG bitsRemaining = BITS_PER_BYTE;
2655
2656 *pstr++ = 5;
2657 for (fmtptr = (const BYTE *)rfmtid; fmtptr < (const BYTE *)rfmtid + sizeof(FMTID); )
2658 {
2659 ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
2660
2661 if (bitsRemaining >= BITS_IN_CHARMASK)
2662 {
2663 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
2664 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
2665 *pstr <= 'z')
2666 *pstr += 'A' - 'a';
2667 pstr++;
2668 bitsRemaining -= BITS_IN_CHARMASK;
2669 if (bitsRemaining == 0)
2670 {
2671 fmtptr++;
2672 bitsRemaining = BITS_PER_BYTE;
2673 }
2674 }
2675 else
2676 {
2677 if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID))
2678 i |= *fmtptr << bitsRemaining;
2679 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
2680 bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK;
2681 }
2682 }
2683 *pstr = 0;
2684 }
2685 TRACE("returning %s\n", debugstr_w(str));
2686 return S_OK;
2687 }
2688
2689 /***********************************************************************
2690 * PropStgNameToFmtId [ole32.@]
2691 * Returns the format ID corresponding to the given name.
2692 * PARAMS
2693 * str [I] Storage name to convert to a format ID.
2694 * rfmtid [O] Format ID corresponding to str.
2695 *
2696 * RETURNS
2697 * E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to
2698 * a format ID, S_OK otherwise.
2699 */
PropStgNameToFmtId(const LPOLESTR str,FMTID * rfmtid)2700 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid)
2701 {
2702 HRESULT hr = STG_E_INVALIDNAME;
2703
2704 TRACE("%s, %p\n", debugstr_w(str), rfmtid);
2705
2706 if (!rfmtid) return E_INVALIDARG;
2707 if (!str) return STG_E_INVALIDNAME;
2708
2709 if (!lstrcmpiW(str, szDocSummaryInfo))
2710 {
2711 *rfmtid = FMTID_DocSummaryInformation;
2712 hr = S_OK;
2713 }
2714 else if (!lstrcmpiW(str, szSummaryInfo))
2715 {
2716 *rfmtid = FMTID_SummaryInformation;
2717 hr = S_OK;
2718 }
2719 else
2720 {
2721 ULONG bits;
2722 BYTE *fmtptr = (BYTE *)rfmtid - 1;
2723 const WCHAR *pstr = str;
2724
2725 memset(rfmtid, 0, sizeof(*rfmtid));
2726 for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE;
2727 bits += BITS_IN_CHARMASK)
2728 {
2729 ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored;
2730 WCHAR wc;
2731
2732 if (bitsUsed == 0)
2733 fmtptr++;
2734 wc = *++pstr - 'A';
2735 if (wc > NUM_ALPHA_CHARS)
2736 {
2737 wc += 'A' - 'a';
2738 if (wc > NUM_ALPHA_CHARS)
2739 {
2740 wc += 'a' - '0' + NUM_ALPHA_CHARS;
2741 if (wc > CHARMASK)
2742 {
2743 WARN("invalid character (%d)\n", *pstr);
2744 goto end;
2745 }
2746 }
2747 }
2748 *fmtptr |= wc << bitsUsed;
2749 bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK);
2750 if (bitsStored < BITS_IN_CHARMASK)
2751 {
2752 wc >>= BITS_PER_BYTE - bitsUsed;
2753 if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE)
2754 {
2755 if (wc != 0)
2756 {
2757 WARN("extra bits\n");
2758 goto end;
2759 }
2760 break;
2761 }
2762 fmtptr++;
2763 *fmtptr |= (BYTE)wc;
2764 }
2765 }
2766 hr = S_OK;
2767 }
2768 end:
2769 return hr;
2770 }
2771
2772 #ifdef __i386__ /* thiscall functions are i386-specific */
2773
2774 #define DEFINE_STDCALL_WRAPPER(num,func,args) \
2775 __ASM_STDCALL_FUNC(func, args, \
2776 "popl %eax\n\t" \
2777 "popl %ecx\n\t" \
2778 "pushl %eax\n\t" \
2779 "movl (%ecx), %eax\n\t" \
2780 "jmp *(4*(" #num "))(%eax)" )
2781
2782 DEFINE_STDCALL_WRAPPER(0,Allocate_PMemoryAllocator,8)
2783 extern void* __stdcall Allocate_PMemoryAllocator(void *this, ULONG cbSize);
2784
2785 #else
2786
Allocate_PMemoryAllocator(void * this,ULONG cbSize)2787 static void* __cdecl Allocate_PMemoryAllocator(void *this, ULONG cbSize)
2788 {
2789 void* (__cdecl *fn)(void*,ULONG) = **(void***)this;
2790 return fn(this, cbSize);
2791 }
2792
2793 #endif
2794
StgConvertPropertyToVariant(const SERIALIZEDPROPERTYVALUE * prop,USHORT CodePage,PROPVARIANT * pvar,void * pma)2795 BOOLEAN WINAPI StgConvertPropertyToVariant(const SERIALIZEDPROPERTYVALUE* prop,
2796 USHORT CodePage, PROPVARIANT* pvar, void* pma)
2797 {
2798 HRESULT hr;
2799
2800 hr = PropertyStorage_ReadProperty(pvar, (const BYTE*)prop, CodePage, Allocate_PMemoryAllocator, pma);
2801
2802 if (FAILED(hr))
2803 {
2804 FIXME("should raise C++ exception on failure\n");
2805 PropVariantInit(pvar);
2806 }
2807
2808 return FALSE;
2809 }
2810
StgConvertVariantToProperty(const PROPVARIANT * pvar,USHORT CodePage,SERIALIZEDPROPERTYVALUE * pprop,ULONG * pcb,PROPID pid,BOOLEAN fReserved,ULONG * pcIndirect)2811 SERIALIZEDPROPERTYVALUE* WINAPI StgConvertVariantToProperty(const PROPVARIANT *pvar,
2812 USHORT CodePage, SERIALIZEDPROPERTYVALUE *pprop, ULONG *pcb, PROPID pid,
2813 BOOLEAN fReserved, ULONG *pcIndirect)
2814 {
2815 FIXME("%p,%d,%p,%p,%d,%d,%p\n", pvar, CodePage, pprop, pcb, pid, fReserved, pcIndirect);
2816
2817 return NULL;
2818 }
2819
StgCreatePropStg(IUnknown * unk,REFFMTID fmt,const CLSID * clsid,DWORD flags,DWORD reserved,IPropertyStorage ** prop_stg)2820 HRESULT WINAPI StgCreatePropStg(IUnknown *unk, REFFMTID fmt, const CLSID *clsid,
2821 DWORD flags, DWORD reserved, IPropertyStorage **prop_stg)
2822 {
2823 IStorage *stg;
2824 IStream *stm;
2825 HRESULT r;
2826
2827 TRACE("%p %s %s %08x %d %p\n", unk, debugstr_guid(fmt), debugstr_guid(clsid), flags, reserved, prop_stg);
2828
2829 if (!fmt || reserved)
2830 {
2831 r = E_INVALIDARG;
2832 goto end;
2833 }
2834
2835 if (flags & PROPSETFLAG_NONSIMPLE)
2836 {
2837 r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
2838 if (FAILED(r))
2839 goto end;
2840
2841 /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to create a
2842 * storage, not a stream. For now, disallow it.
2843 */
2844 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2845 IStorage_Release(stg);
2846 r = STG_E_INVALIDFLAG;
2847 }
2848 else
2849 {
2850 r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
2851 if (FAILED(r))
2852 goto end;
2853
2854 r = PropertyStorage_ConstructEmpty(stm, fmt, flags,
2855 STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
2856
2857 IStream_Release( stm );
2858 }
2859
2860 end:
2861 TRACE("returning 0x%08x\n", r);
2862 return r;
2863 }
2864
StgOpenPropStg(IUnknown * unk,REFFMTID fmt,DWORD flags,DWORD reserved,IPropertyStorage ** prop_stg)2865 HRESULT WINAPI StgOpenPropStg(IUnknown *unk, REFFMTID fmt, DWORD flags,
2866 DWORD reserved, IPropertyStorage **prop_stg)
2867 {
2868 IStorage *stg;
2869 IStream *stm;
2870 HRESULT r;
2871
2872 TRACE("%p %s %08x %d %p\n", unk, debugstr_guid(fmt), flags, reserved, prop_stg);
2873
2874 if (!fmt || reserved)
2875 {
2876 r = E_INVALIDARG;
2877 goto end;
2878 }
2879
2880 if (flags & PROPSETFLAG_NONSIMPLE)
2881 {
2882 r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
2883 if (FAILED(r))
2884 goto end;
2885
2886 /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to open a
2887 * storage, not a stream. For now, disallow it.
2888 */
2889 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2890 IStorage_Release(stg);
2891 r = STG_E_INVALIDFLAG;
2892 }
2893 else
2894 {
2895 r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
2896 if (FAILED(r))
2897 goto end;
2898
2899 r = PropertyStorage_ConstructFromStream(stm, fmt,
2900 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
2901
2902 IStream_Release( stm );
2903 }
2904
2905 end:
2906 TRACE("returning 0x%08x\n", r);
2907 return r;
2908 }
2909