kernel32/tests: Add a test to check some fields in fake dlls.
[wine.git] / dlls / ole32 / stg_prop.c
blob221463bc05e2fc40918efd5f5b5621e0e143cd89
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;
1048 DWORD vt;
1050 assert(prop);
1051 assert(data);
1052 StorageUtl_ReadDWord(data, 0, &vt);
1053 data += sizeof(DWORD);
1054 prop->vt = vt;
1055 switch (prop->vt)
1057 case VT_EMPTY:
1058 case VT_NULL:
1059 break;
1060 case VT_I1:
1061 prop->u.cVal = *(const char *)data;
1062 TRACE("Read char 0x%x ('%c')\n", prop->u.cVal, prop->u.cVal);
1063 break;
1064 case VT_UI1:
1065 prop->u.bVal = *data;
1066 TRACE("Read byte 0x%x\n", prop->u.bVal);
1067 break;
1068 case VT_BOOL:
1069 StorageUtl_ReadWord(data, 0, (WORD*)&prop->u.boolVal);
1070 TRACE("Read bool %d\n", prop->u.boolVal);
1071 break;
1072 case VT_I2:
1073 StorageUtl_ReadWord(data, 0, (WORD*)&prop->u.iVal);
1074 TRACE("Read short %d\n", prop->u.iVal);
1075 break;
1076 case VT_UI2:
1077 StorageUtl_ReadWord(data, 0, &prop->u.uiVal);
1078 TRACE("Read ushort %d\n", prop->u.uiVal);
1079 break;
1080 case VT_INT:
1081 case VT_I4:
1082 StorageUtl_ReadDWord(data, 0, (DWORD*)&prop->u.lVal);
1083 TRACE("Read long %d\n", prop->u.lVal);
1084 break;
1085 case VT_UINT:
1086 case VT_UI4:
1087 StorageUtl_ReadDWord(data, 0, &prop->u.ulVal);
1088 TRACE("Read ulong %d\n", prop->u.ulVal);
1089 break;
1090 case VT_I8:
1091 StorageUtl_ReadULargeInteger(data, 0, (ULARGE_INTEGER *)&prop->u.hVal);
1092 TRACE("Read long long %s\n", wine_dbgstr_longlong(prop->u.hVal.QuadPart));
1093 break;
1094 case VT_UI8:
1095 StorageUtl_ReadULargeInteger(data, 0, &prop->u.uhVal);
1096 TRACE("Read ulong long %s\n", wine_dbgstr_longlong(prop->u.uhVal.QuadPart));
1097 break;
1098 case VT_R8:
1099 memcpy(&prop->u.dblVal, data, sizeof(double));
1100 TRACE("Read double %f\n", prop->u.dblVal);
1101 break;
1102 case VT_LPSTR:
1104 DWORD count;
1106 StorageUtl_ReadDWord(data, 0, &count);
1107 if (codepage == CP_UNICODE && count % 2)
1109 WARN("Unicode string has odd number of bytes\n");
1110 hr = STG_E_INVALIDHEADER;
1112 else
1114 prop->u.pszVal = allocate(allocate_data, count);
1115 if (prop->u.pszVal)
1117 memcpy(prop->u.pszVal, data + sizeof(DWORD), count);
1118 /* This is stored in the code page specified in codepage.
1119 * Don't convert it, the caller will just store it as-is.
1121 if (codepage == CP_UNICODE)
1123 /* Make sure it's NULL-terminated */
1124 prop->u.pszVal[count / sizeof(WCHAR) - 1] = '\0';
1125 TRACE("Read string value %s\n",
1126 debugstr_w(prop->u.pwszVal));
1128 else
1130 /* Make sure it's NULL-terminated */
1131 prop->u.pszVal[count - 1] = '\0';
1132 TRACE("Read string value %s\n", debugstr_a(prop->u.pszVal));
1135 else
1136 hr = STG_E_INSUFFICIENTMEMORY;
1138 break;
1140 case VT_BSTR:
1142 DWORD count, wcount;
1144 StorageUtl_ReadDWord(data, 0, &count);
1145 if (codepage == CP_UNICODE && count % 2)
1147 WARN("Unicode string has odd number of bytes\n");
1148 hr = STG_E_INVALIDHEADER;
1150 else
1152 if (codepage == CP_UNICODE)
1153 wcount = count / 2;
1154 else
1155 wcount = MultiByteToWideChar(codepage, 0, (LPCSTR)(data + sizeof(DWORD)), count, NULL, 0);
1157 prop->u.bstrVal = SysAllocStringLen(NULL, wcount); /* FIXME: use allocator? */
1159 if (prop->u.bstrVal)
1161 if (codepage == CP_UNICODE)
1162 memcpy(prop->u.bstrVal, data + sizeof(DWORD), count);
1163 else
1164 MultiByteToWideChar(codepage, 0, (LPCSTR)(data + sizeof(DWORD)), count, prop->u.bstrVal, wcount);
1166 prop->u.bstrVal[wcount - 1] = '\0';
1167 TRACE("Read string value %s\n", debugstr_w(prop->u.bstrVal));
1169 else
1170 hr = STG_E_INSUFFICIENTMEMORY;
1172 break;
1174 case VT_BLOB:
1176 DWORD count;
1178 StorageUtl_ReadDWord(data, 0, &count);
1179 prop->u.blob.cbSize = count;
1180 prop->u.blob.pBlobData = allocate(allocate_data, count);
1181 if (prop->u.blob.pBlobData)
1183 memcpy(prop->u.blob.pBlobData, data + sizeof(DWORD), count);
1184 TRACE("Read blob value of size %d\n", count);
1186 else
1187 hr = STG_E_INSUFFICIENTMEMORY;
1188 break;
1190 case VT_LPWSTR:
1192 DWORD count;
1194 StorageUtl_ReadDWord(data, 0, &count);
1195 prop->u.pwszVal = allocate(allocate_data, count * sizeof(WCHAR));
1196 if (prop->u.pwszVal)
1198 memcpy(prop->u.pwszVal, data + sizeof(DWORD),
1199 count * sizeof(WCHAR));
1200 /* make sure string is NULL-terminated */
1201 prop->u.pwszVal[count - 1] = '\0';
1202 PropertyStorage_ByteSwapString(prop->u.pwszVal, count);
1203 TRACE("Read string value %s\n", debugstr_w(prop->u.pwszVal));
1205 else
1206 hr = STG_E_INSUFFICIENTMEMORY;
1207 break;
1209 case VT_FILETIME:
1210 StorageUtl_ReadULargeInteger(data, 0,
1211 (ULARGE_INTEGER *)&prop->u.filetime);
1212 break;
1213 case VT_CF:
1215 DWORD len = 0, tag = 0;
1217 StorageUtl_ReadDWord(data, 0, &len);
1218 StorageUtl_ReadDWord(data, 4, &tag);
1219 if (len > 8)
1221 len -= 8;
1222 prop->u.pclipdata = allocate(allocate_data, sizeof (CLIPDATA));
1223 prop->u.pclipdata->cbSize = len;
1224 prop->u.pclipdata->ulClipFmt = tag;
1225 prop->u.pclipdata->pClipData = allocate(allocate_data, len - sizeof(prop->u.pclipdata->ulClipFmt));
1226 memcpy(prop->u.pclipdata->pClipData, data+8, len - sizeof(prop->u.pclipdata->ulClipFmt));
1228 else
1229 hr = STG_E_INVALIDPARAMETER;
1231 break;
1232 default:
1233 FIXME("unsupported type %d\n", prop->vt);
1234 hr = STG_E_INVALIDPARAMETER;
1236 return hr;
1239 static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
1240 PROPERTYSETHEADER *hdr)
1242 BYTE buf[sizeof(PROPERTYSETHEADER)];
1243 ULONG count = 0;
1244 HRESULT hr;
1246 assert(stm);
1247 assert(hdr);
1248 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1249 if (SUCCEEDED(hr))
1251 if (count != sizeof(buf))
1253 WARN("read only %d\n", count);
1254 hr = STG_E_INVALIDHEADER;
1256 else
1258 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder),
1259 &hdr->wByteOrder);
1260 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat),
1261 &hdr->wFormat);
1262 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer),
1263 &hdr->dwOSVer);
1264 StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid),
1265 &hdr->clsid);
1266 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved),
1267 &hdr->reserved);
1270 TRACE("returning 0x%08x\n", hr);
1271 return hr;
1274 static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm,
1275 FORMATIDOFFSET *fmt)
1277 BYTE buf[sizeof(FORMATIDOFFSET)];
1278 ULONG count = 0;
1279 HRESULT hr;
1281 assert(stm);
1282 assert(fmt);
1283 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1284 if (SUCCEEDED(hr))
1286 if (count != sizeof(buf))
1288 WARN("read only %d\n", count);
1289 hr = STG_E_INVALIDHEADER;
1291 else
1293 StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid),
1294 &fmt->fmtid);
1295 StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset),
1296 &fmt->dwOffset);
1299 TRACE("returning 0x%08x\n", hr);
1300 return hr;
1303 static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
1304 PROPERTYSECTIONHEADER *hdr)
1306 BYTE buf[sizeof(PROPERTYSECTIONHEADER)];
1307 ULONG count = 0;
1308 HRESULT hr;
1310 assert(stm);
1311 assert(hdr);
1312 hr = IStream_Read(stm, buf, sizeof(buf), &count);
1313 if (SUCCEEDED(hr))
1315 if (count != sizeof(buf))
1317 WARN("read only %d\n", count);
1318 hr = STG_E_INVALIDHEADER;
1320 else
1322 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1323 cbSection), &hdr->cbSection);
1324 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1325 cProperties), &hdr->cProperties);
1328 TRACE("returning 0x%08x\n", hr);
1329 return hr;
1332 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This)
1334 PROPERTYSETHEADER hdr;
1335 FORMATIDOFFSET fmtOffset;
1336 PROPERTYSECTIONHEADER sectionHdr;
1337 LARGE_INTEGER seek;
1338 ULONG i;
1339 STATSTG stat;
1340 HRESULT hr;
1341 BYTE *buf = NULL;
1342 ULONG count = 0;
1343 DWORD dictOffset = 0;
1345 This->dirty = FALSE;
1346 This->highestProp = 0;
1347 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
1348 if (FAILED(hr))
1349 goto end;
1350 if (stat.cbSize.u.HighPart)
1352 WARN("stream too big\n");
1353 /* maximum size varies, but it can't be this big */
1354 hr = STG_E_INVALIDHEADER;
1355 goto end;
1357 if (stat.cbSize.u.LowPart == 0)
1359 /* empty stream is okay */
1360 hr = S_OK;
1361 goto end;
1363 else if (stat.cbSize.u.LowPart < sizeof(PROPERTYSETHEADER) +
1364 sizeof(FORMATIDOFFSET))
1366 WARN("stream too small\n");
1367 hr = STG_E_INVALIDHEADER;
1368 goto end;
1370 seek.QuadPart = 0;
1371 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1372 if (FAILED(hr))
1373 goto end;
1374 hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr);
1375 /* I've only seen reserved == 1, but the article says I shouldn't disallow
1376 * higher values.
1378 if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1)
1380 WARN("bad magic in prop set header\n");
1381 hr = STG_E_INVALIDHEADER;
1382 goto end;
1384 if (hdr.wFormat != 0 && hdr.wFormat != 1)
1386 WARN("bad format version %d\n", hdr.wFormat);
1387 hr = STG_E_INVALIDHEADER;
1388 goto end;
1390 This->format = hdr.wFormat;
1391 This->clsid = hdr.clsid;
1392 This->originatorOS = hdr.dwOSVer;
1393 if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC)
1394 WARN("File comes from a Mac, strings will probably be screwed up\n");
1395 hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset);
1396 if (FAILED(hr))
1397 goto end;
1398 if (fmtOffset.dwOffset > stat.cbSize.u.LowPart)
1400 WARN("invalid offset %d (stream length is %d)\n", fmtOffset.dwOffset,
1401 stat.cbSize.u.LowPart);
1402 hr = STG_E_INVALIDHEADER;
1403 goto end;
1405 /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
1406 * follows not one, but two sections. The first contains the standard properties
1407 * for the document summary information, and the second consists of user-defined
1408 * properties. This is the only case in which multiple sections are
1409 * allowed.
1410 * Reading the second stream isn't implemented yet.
1412 hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, &sectionHdr);
1413 if (FAILED(hr))
1414 goto end;
1415 /* The section size includes the section header, so check it */
1416 if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER))
1418 WARN("section header too small, got %d\n", sectionHdr.cbSection);
1419 hr = STG_E_INVALIDHEADER;
1420 goto end;
1422 buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection -
1423 sizeof(PROPERTYSECTIONHEADER));
1424 if (!buf)
1426 hr = STG_E_INSUFFICIENTMEMORY;
1427 goto end;
1429 hr = IStream_Read(This->stm, buf, sectionHdr.cbSection -
1430 sizeof(PROPERTYSECTIONHEADER), &count);
1431 if (FAILED(hr))
1432 goto end;
1433 TRACE("Reading %d properties:\n", sectionHdr.cProperties);
1434 for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
1436 PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(buf +
1437 i * sizeof(PROPERTYIDOFFSET));
1439 if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) ||
1440 idOffset->dwOffset > sectionHdr.cbSection - sizeof(DWORD))
1441 hr = STG_E_INVALIDPOINTER;
1442 else
1444 if (idOffset->propid >= PID_FIRST_USABLE &&
1445 idOffset->propid < PID_MIN_READONLY && idOffset->propid >
1446 This->highestProp)
1447 This->highestProp = idOffset->propid;
1448 if (idOffset->propid == PID_DICTIONARY)
1450 /* Don't read the dictionary yet, its entries depend on the
1451 * code page. Just store the offset so we know to read it
1452 * later.
1454 dictOffset = idOffset->dwOffset;
1455 TRACE("Dictionary offset is %d\n", dictOffset);
1457 else
1459 PROPVARIANT prop;
1461 PropVariantInit(&prop);
1462 if (SUCCEEDED(PropertyStorage_ReadProperty(&prop,
1463 buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER),
1464 This->codePage, Allocate_CoTaskMemAlloc, NULL)))
1466 TRACE("Read property with ID 0x%08x, type %d\n",
1467 idOffset->propid, prop.vt);
1468 switch(idOffset->propid)
1470 case PID_CODEPAGE:
1471 if (prop.vt == VT_I2)
1472 This->codePage = (UINT)prop.u.iVal;
1473 break;
1474 case PID_LOCALE:
1475 if (prop.vt == VT_I4)
1476 This->locale = (LCID)prop.u.lVal;
1477 break;
1478 case PID_BEHAVIOR:
1479 if (prop.vt == VT_I4 && prop.u.lVal)
1480 This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
1481 /* The format should already be 1, but just in case */
1482 This->format = 1;
1483 break;
1484 default:
1485 hr = PropertyStorage_StorePropWithId(This,
1486 idOffset->propid, &prop, This->codePage);
1489 PropVariantClear(&prop);
1493 if (!This->codePage)
1495 /* default to Unicode unless told not to, as specified on msdn */
1496 if (This->grfFlags & PROPSETFLAG_ANSI)
1497 This->codePage = GetACP();
1498 else
1499 This->codePage = CP_UNICODE;
1501 if (!This->locale)
1502 This->locale = LOCALE_SYSTEM_DEFAULT;
1503 TRACE("Code page is %d, locale is %d\n", This->codePage, This->locale);
1504 if (dictOffset)
1505 hr = PropertyStorage_ReadDictionary(This,
1506 buf + dictOffset - sizeof(PROPERTYSECTIONHEADER));
1508 end:
1509 HeapFree(GetProcessHeap(), 0, buf);
1510 if (FAILED(hr))
1512 dictionary_destroy(This->name_to_propid);
1513 This->name_to_propid = NULL;
1514 dictionary_destroy(This->propid_to_name);
1515 This->propid_to_name = NULL;
1516 dictionary_destroy(This->propid_to_prop);
1517 This->propid_to_prop = NULL;
1519 return hr;
1522 static void PropertyStorage_MakeHeader(PropertyStorage_impl *This,
1523 PROPERTYSETHEADER *hdr)
1525 assert(hdr);
1526 StorageUtl_WriteWord((BYTE *)&hdr->wByteOrder, 0,
1527 PROPSETHDR_BYTEORDER_MAGIC);
1528 StorageUtl_WriteWord((BYTE *)&hdr->wFormat, 0, This->format);
1529 StorageUtl_WriteDWord((BYTE *)&hdr->dwOSVer, 0, This->originatorOS);
1530 StorageUtl_WriteGUID((BYTE *)&hdr->clsid, 0, &This->clsid);
1531 StorageUtl_WriteDWord((BYTE *)&hdr->reserved, 0, 1);
1534 static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl *This,
1535 FORMATIDOFFSET *fmtOffset)
1537 assert(fmtOffset);
1538 StorageUtl_WriteGUID((BYTE *)fmtOffset, 0, &This->fmtid);
1539 StorageUtl_WriteDWord((BYTE *)fmtOffset, offsetof(FORMATIDOFFSET, dwOffset),
1540 sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET));
1543 static void PropertyStorage_MakeSectionHdr(DWORD cbSection, DWORD numProps,
1544 PROPERTYSECTIONHEADER *hdr)
1546 assert(hdr);
1547 StorageUtl_WriteDWord((BYTE *)hdr, 0, cbSection);
1548 StorageUtl_WriteDWord((BYTE *)hdr,
1549 offsetof(PROPERTYSECTIONHEADER, cProperties), numProps);
1552 static void PropertyStorage_MakePropertyIdOffset(DWORD propid, DWORD dwOffset,
1553 PROPERTYIDOFFSET *propIdOffset)
1555 assert(propIdOffset);
1556 StorageUtl_WriteDWord((BYTE *)propIdOffset, 0, propid);
1557 StorageUtl_WriteDWord((BYTE *)propIdOffset,
1558 offsetof(PROPERTYIDOFFSET, dwOffset), dwOffset);
1561 static inline HRESULT PropertStorage_WriteWStringToStream(IStream *stm,
1562 LPCWSTR str, DWORD len, DWORD *written)
1564 #ifdef WORDS_BIGENDIAN
1565 WCHAR *leStr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1566 HRESULT hr;
1568 if (!leStr)
1569 return E_OUTOFMEMORY;
1570 memcpy(leStr, str, len * sizeof(WCHAR));
1571 PropertyStorage_ByteSwapString(leStr, len);
1572 hr = IStream_Write(stm, leStr, len, written);
1573 HeapFree(GetProcessHeap(), 0, leStr);
1574 return hr;
1575 #else
1576 return IStream_Write(stm, str, len, written);
1577 #endif
1580 struct DictionaryClosure
1582 HRESULT hr;
1583 DWORD bytesWritten;
1586 static BOOL PropertyStorage_DictionaryWriter(const void *key,
1587 const void *value, void *extra, void *closure)
1589 PropertyStorage_impl *This = extra;
1590 struct DictionaryClosure *c = closure;
1591 DWORD propid;
1592 ULONG count;
1594 assert(key);
1595 assert(closure);
1596 StorageUtl_WriteDWord((LPBYTE)&propid, 0, PtrToUlong(value));
1597 c->hr = IStream_Write(This->stm, &propid, sizeof(propid), &count);
1598 if (FAILED(c->hr))
1599 goto end;
1600 c->bytesWritten += sizeof(DWORD);
1601 if (This->codePage == CP_UNICODE)
1603 DWORD keyLen, pad = 0;
1605 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0,
1606 (lstrlenW((LPCWSTR)key) + 1) * sizeof(WCHAR));
1607 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1608 if (FAILED(c->hr))
1609 goto end;
1610 c->bytesWritten += sizeof(DWORD);
1611 c->hr = PropertStorage_WriteWStringToStream(This->stm, key, keyLen,
1612 &count);
1613 if (FAILED(c->hr))
1614 goto end;
1615 c->bytesWritten += keyLen * sizeof(WCHAR);
1616 if (keyLen % sizeof(DWORD))
1618 c->hr = IStream_Write(This->stm, &pad,
1619 sizeof(DWORD) - keyLen % sizeof(DWORD), &count);
1620 if (FAILED(c->hr))
1621 goto end;
1622 c->bytesWritten += sizeof(DWORD) - keyLen % sizeof(DWORD);
1625 else
1627 DWORD keyLen;
1629 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0, strlen((LPCSTR)key) + 1);
1630 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1631 if (FAILED(c->hr))
1632 goto end;
1633 c->bytesWritten += sizeof(DWORD);
1634 c->hr = IStream_Write(This->stm, key, keyLen, &count);
1635 if (FAILED(c->hr))
1636 goto end;
1637 c->bytesWritten += keyLen;
1639 end:
1640 return SUCCEEDED(c->hr);
1643 #define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)
1645 /* Writes the dictionary to the stream. Assumes without checking that the
1646 * dictionary isn't empty.
1648 static HRESULT PropertyStorage_WriteDictionaryToStream(
1649 PropertyStorage_impl *This, DWORD *sectionOffset)
1651 HRESULT hr;
1652 LARGE_INTEGER seek;
1653 PROPERTYIDOFFSET propIdOffset;
1654 ULONG count;
1655 DWORD dwTemp;
1656 struct DictionaryClosure closure;
1658 assert(sectionOffset);
1660 /* The dictionary's always the first property written, so seek to its
1661 * spot.
1663 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER);
1664 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1665 if (FAILED(hr))
1666 goto end;
1667 PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY, *sectionOffset,
1668 &propIdOffset);
1669 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1670 if (FAILED(hr))
1671 goto end;
1673 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1674 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1675 if (FAILED(hr))
1676 goto end;
1677 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0,
1678 dictionary_num_entries(This->name_to_propid));
1679 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1680 if (FAILED(hr))
1681 goto end;
1682 *sectionOffset += sizeof(dwTemp);
1684 closure.hr = S_OK;
1685 closure.bytesWritten = 0;
1686 dictionary_enumerate(This->name_to_propid, PropertyStorage_DictionaryWriter,
1687 &closure);
1688 hr = closure.hr;
1689 if (FAILED(hr))
1690 goto end;
1691 *sectionOffset += closure.bytesWritten;
1692 if (closure.bytesWritten % sizeof(DWORD))
1694 DWORD padding = sizeof(DWORD) - closure.bytesWritten % sizeof(DWORD);
1695 TRACE("adding %d bytes of padding\n", padding);
1696 *sectionOffset += padding;
1699 end:
1700 return hr;
1703 static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This,
1704 DWORD propNum, DWORD propid, const PROPVARIANT *var, DWORD *sectionOffset)
1706 HRESULT hr;
1707 LARGE_INTEGER seek;
1708 PROPERTYIDOFFSET propIdOffset;
1709 ULONG count;
1710 DWORD dwType, bytesWritten;
1712 assert(var);
1713 assert(sectionOffset);
1715 TRACE("%p, %d, 0x%08x, (%d), (%d)\n", This, propNum, propid, var->vt,
1716 *sectionOffset);
1718 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) +
1719 propNum * sizeof(PROPERTYIDOFFSET);
1720 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1721 if (FAILED(hr))
1722 goto end;
1723 PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset);
1724 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1725 if (FAILED(hr))
1726 goto end;
1728 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1729 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1730 if (FAILED(hr))
1731 goto end;
1732 StorageUtl_WriteDWord((LPBYTE)&dwType, 0, var->vt);
1733 hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count);
1734 if (FAILED(hr))
1735 goto end;
1736 *sectionOffset += sizeof(dwType);
1738 switch (var->vt)
1740 case VT_EMPTY:
1741 case VT_NULL:
1742 bytesWritten = 0;
1743 break;
1744 case VT_I1:
1745 case VT_UI1:
1746 hr = IStream_Write(This->stm, &var->u.cVal, sizeof(var->u.cVal),
1747 &count);
1748 bytesWritten = count;
1749 break;
1750 case VT_I2:
1751 case VT_UI2:
1753 WORD wTemp;
1755 StorageUtl_WriteWord((LPBYTE)&wTemp, 0, var->u.iVal);
1756 hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count);
1757 bytesWritten = count;
1758 break;
1760 case VT_I4:
1761 case VT_UI4:
1763 DWORD dwTemp;
1765 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, var->u.lVal);
1766 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1767 bytesWritten = count;
1768 break;
1770 case VT_LPSTR:
1772 DWORD len, dwTemp;
1774 if (This->codePage == CP_UNICODE)
1775 len = (lstrlenW(var->u.pwszVal) + 1) * sizeof(WCHAR);
1776 else
1777 len = lstrlenA(var->u.pszVal) + 1;
1778 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1779 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1780 if (FAILED(hr))
1781 goto end;
1782 hr = IStream_Write(This->stm, var->u.pszVal, len, &count);
1783 bytesWritten = count + sizeof(DWORD);
1784 break;
1786 case VT_LPWSTR:
1788 DWORD len = lstrlenW(var->u.pwszVal) + 1, dwTemp;
1790 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1791 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1792 if (FAILED(hr))
1793 goto end;
1794 hr = IStream_Write(This->stm, var->u.pwszVal, len * sizeof(WCHAR),
1795 &count);
1796 bytesWritten = count + sizeof(DWORD);
1797 break;
1799 case VT_FILETIME:
1801 FILETIME temp;
1803 StorageUtl_WriteULargeInteger((BYTE *)&temp, 0,
1804 (const ULARGE_INTEGER *)&var->u.filetime);
1805 hr = IStream_Write(This->stm, &temp, sizeof(FILETIME), &count);
1806 bytesWritten = count;
1807 break;
1809 case VT_CF:
1811 DWORD cf_hdr[2], len;
1813 len = var->u.pclipdata->cbSize;
1814 StorageUtl_WriteDWord((LPBYTE)&cf_hdr[0], 0, len + 8);
1815 StorageUtl_WriteDWord((LPBYTE)&cf_hdr[1], 0, var->u.pclipdata->ulClipFmt);
1816 hr = IStream_Write(This->stm, cf_hdr, sizeof(cf_hdr), &count);
1817 if (FAILED(hr))
1818 goto end;
1819 hr = IStream_Write(This->stm, var->u.pclipdata->pClipData,
1820 len - sizeof(var->u.pclipdata->ulClipFmt), &count);
1821 if (FAILED(hr))
1822 goto end;
1823 bytesWritten = count + sizeof cf_hdr;
1824 break;
1826 case VT_CLSID:
1828 CLSID temp;
1830 StorageUtl_WriteGUID((BYTE *)&temp, 0, var->u.puuid);
1831 hr = IStream_Write(This->stm, &temp, sizeof(temp), &count);
1832 bytesWritten = count;
1833 break;
1835 default:
1836 FIXME("unsupported type: %d\n", var->vt);
1837 return STG_E_INVALIDPARAMETER;
1840 if (SUCCEEDED(hr))
1842 *sectionOffset += bytesWritten;
1843 if (bytesWritten % sizeof(DWORD))
1845 DWORD padding = sizeof(DWORD) - bytesWritten % sizeof(DWORD);
1846 TRACE("adding %d bytes of padding\n", padding);
1847 *sectionOffset += padding;
1851 end:
1852 return hr;
1855 struct PropertyClosure
1857 HRESULT hr;
1858 DWORD propNum;
1859 DWORD *sectionOffset;
1862 static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value,
1863 void *extra, void *closure)
1865 PropertyStorage_impl *This = extra;
1866 struct PropertyClosure *c = closure;
1868 assert(key);
1869 assert(value);
1870 assert(extra);
1871 assert(closure);
1872 c->hr = PropertyStorage_WritePropertyToStream(This, c->propNum++,
1873 PtrToUlong(key), value, c->sectionOffset);
1874 return SUCCEEDED(c->hr);
1877 static HRESULT PropertyStorage_WritePropertiesToStream(
1878 PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset)
1880 struct PropertyClosure closure;
1882 assert(sectionOffset);
1883 closure.hr = S_OK;
1884 closure.propNum = startingPropNum;
1885 closure.sectionOffset = sectionOffset;
1886 dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter,
1887 &closure);
1888 return closure.hr;
1891 static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This)
1893 HRESULT hr;
1894 ULONG count = 0;
1895 LARGE_INTEGER seek = { {0} };
1896 PROPERTYSETHEADER hdr;
1897 FORMATIDOFFSET fmtOffset;
1899 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1900 if (FAILED(hr))
1901 goto end;
1902 PropertyStorage_MakeHeader(This, &hdr);
1903 hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count);
1904 if (FAILED(hr))
1905 goto end;
1906 if (count != sizeof(hdr))
1908 hr = STG_E_WRITEFAULT;
1909 goto end;
1912 PropertyStorage_MakeFmtIdOffset(This, &fmtOffset);
1913 hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count);
1914 if (FAILED(hr))
1915 goto end;
1916 if (count != sizeof(fmtOffset))
1918 hr = STG_E_WRITEFAULT;
1919 goto end;
1921 hr = S_OK;
1923 end:
1924 return hr;
1927 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This)
1929 PROPERTYSECTIONHEADER sectionHdr;
1930 HRESULT hr;
1931 ULONG count;
1932 LARGE_INTEGER seek;
1933 DWORD numProps, prop, sectionOffset, dwTemp;
1934 PROPVARIANT var;
1936 PropertyStorage_WriteHeadersToStream(This);
1938 /* Count properties. Always at least one property, the code page */
1939 numProps = 1;
1940 if (dictionary_num_entries(This->name_to_propid))
1941 numProps++;
1942 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1943 numProps++;
1944 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1945 numProps++;
1946 numProps += dictionary_num_entries(This->propid_to_prop);
1948 /* Write section header with 0 bytes right now, I'll adjust it after
1949 * writing properties.
1951 PropertyStorage_MakeSectionHdr(0, numProps, &sectionHdr);
1952 seek.QuadPart = SECTIONHEADER_OFFSET;
1953 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1954 if (FAILED(hr))
1955 goto end;
1956 hr = IStream_Write(This->stm, &sectionHdr, sizeof(sectionHdr), &count);
1957 if (FAILED(hr))
1958 goto end;
1960 prop = 0;
1961 sectionOffset = sizeof(PROPERTYSECTIONHEADER) +
1962 numProps * sizeof(PROPERTYIDOFFSET);
1964 if (dictionary_num_entries(This->name_to_propid))
1966 prop++;
1967 hr = PropertyStorage_WriteDictionaryToStream(This, &sectionOffset);
1968 if (FAILED(hr))
1969 goto end;
1972 PropVariantInit(&var);
1974 var.vt = VT_I2;
1975 var.u.iVal = This->codePage;
1976 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE,
1977 &var, &sectionOffset);
1978 if (FAILED(hr))
1979 goto end;
1981 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1983 var.vt = VT_I4;
1984 var.u.lVal = This->locale;
1985 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE,
1986 &var, &sectionOffset);
1987 if (FAILED(hr))
1988 goto end;
1991 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1993 var.vt = VT_I4;
1994 var.u.lVal = 1;
1995 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR,
1996 &var, &sectionOffset);
1997 if (FAILED(hr))
1998 goto end;
2001 hr = PropertyStorage_WritePropertiesToStream(This, prop, &sectionOffset);
2002 if (FAILED(hr))
2003 goto end;
2005 /* Now write the byte count of the section */
2006 seek.QuadPart = SECTIONHEADER_OFFSET;
2007 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2008 if (FAILED(hr))
2009 goto end;
2010 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, sectionOffset);
2011 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2013 end:
2014 return hr;
2017 /***********************************************************************
2018 * PropertyStorage_Construct
2020 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This)
2022 dictionary_destroy(This->name_to_propid);
2023 This->name_to_propid = NULL;
2024 dictionary_destroy(This->propid_to_name);
2025 This->propid_to_name = NULL;
2026 dictionary_destroy(This->propid_to_prop);
2027 This->propid_to_prop = NULL;
2030 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This)
2032 HRESULT hr = S_OK;
2034 This->name_to_propid = dictionary_create(
2035 PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy,
2036 This);
2037 if (!This->name_to_propid)
2039 hr = STG_E_INSUFFICIENTMEMORY;
2040 goto end;
2042 This->propid_to_name = dictionary_create(PropertyStorage_PropCompare,
2043 NULL, This);
2044 if (!This->propid_to_name)
2046 hr = STG_E_INSUFFICIENTMEMORY;
2047 goto end;
2049 This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
2050 PropertyStorage_PropertyDestroy, This);
2051 if (!This->propid_to_prop)
2053 hr = STG_E_INSUFFICIENTMEMORY;
2054 goto end;
2056 end:
2057 if (FAILED(hr))
2058 PropertyStorage_DestroyDictionaries(This);
2059 return hr;
2062 static HRESULT PropertyStorage_BaseConstruct(IStream *stm,
2063 REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps)
2065 HRESULT hr = S_OK;
2067 assert(pps);
2068 assert(rfmtid);
2069 *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps);
2070 if (!*pps)
2071 return E_OUTOFMEMORY;
2073 (*pps)->IPropertyStorage_iface.lpVtbl = &IPropertyStorage_Vtbl;
2074 (*pps)->ref = 1;
2075 InitializeCriticalSection(&(*pps)->cs);
2076 (*pps)->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PropertyStorage_impl.cs");
2077 (*pps)->stm = stm;
2078 (*pps)->fmtid = *rfmtid;
2079 (*pps)->grfMode = grfMode;
2081 hr = PropertyStorage_CreateDictionaries(*pps);
2082 if (FAILED(hr))
2084 (*pps)->cs.DebugInfo->Spare[0] = 0;
2085 DeleteCriticalSection(&(*pps)->cs);
2086 HeapFree(GetProcessHeap(), 0, *pps);
2087 *pps = NULL;
2089 else IStream_AddRef( stm );
2091 return hr;
2094 static HRESULT PropertyStorage_ConstructFromStream(IStream *stm,
2095 REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps)
2097 PropertyStorage_impl *ps;
2098 HRESULT hr;
2100 assert(pps);
2101 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2102 if (SUCCEEDED(hr))
2104 hr = PropertyStorage_ReadFromStream(ps);
2105 if (SUCCEEDED(hr))
2107 *pps = &ps->IPropertyStorage_iface;
2108 TRACE("PropertyStorage %p constructed\n", ps);
2109 hr = S_OK;
2111 else IPropertyStorage_Release( &ps->IPropertyStorage_iface );
2113 return hr;
2116 static HRESULT PropertyStorage_ConstructEmpty(IStream *stm,
2117 REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
2119 PropertyStorage_impl *ps;
2120 HRESULT hr;
2122 assert(pps);
2123 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2124 if (SUCCEEDED(hr))
2126 ps->format = 0;
2127 ps->grfFlags = grfFlags;
2128 if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2129 ps->format = 1;
2130 /* default to Unicode unless told not to, as specified on msdn */
2131 if (ps->grfFlags & PROPSETFLAG_ANSI)
2132 ps->codePage = GetACP();
2133 else
2134 ps->codePage = CP_UNICODE;
2135 ps->locale = LOCALE_SYSTEM_DEFAULT;
2136 TRACE("Code page is %d, locale is %d\n", ps->codePage, ps->locale);
2137 *pps = &ps->IPropertyStorage_iface;
2138 TRACE("PropertyStorage %p constructed\n", ps);
2139 hr = S_OK;
2141 return hr;
2145 /***********************************************************************
2146 * Implementation of IPropertySetStorage
2149 /************************************************************************
2150 * IPropertySetStorage_fnQueryInterface (IUnknown)
2152 * This method forwards to the common QueryInterface implementation
2154 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
2155 IPropertySetStorage *ppstg,
2156 REFIID riid,
2157 void** ppvObject)
2159 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2160 return IStorage_QueryInterface( &This->base.IStorage_iface, riid, ppvObject );
2163 /************************************************************************
2164 * IPropertySetStorage_fnAddRef (IUnknown)
2166 * This method forwards to the common AddRef implementation
2168 static ULONG WINAPI IPropertySetStorage_fnAddRef(
2169 IPropertySetStorage *ppstg)
2171 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2172 return IStorage_AddRef( &This->base.IStorage_iface );
2175 /************************************************************************
2176 * IPropertySetStorage_fnRelease (IUnknown)
2178 * This method forwards to the common Release implementation
2180 static ULONG WINAPI IPropertySetStorage_fnRelease(
2181 IPropertySetStorage *ppstg)
2183 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2184 return IStorage_Release( &This->base.IStorage_iface );
2187 /************************************************************************
2188 * IPropertySetStorage_fnCreate (IPropertySetStorage)
2190 static HRESULT WINAPI IPropertySetStorage_fnCreate(
2191 IPropertySetStorage *ppstg,
2192 REFFMTID rfmtid,
2193 const CLSID* pclsid,
2194 DWORD grfFlags,
2195 DWORD grfMode,
2196 IPropertyStorage** ppprstg)
2198 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2199 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2200 IStream *stm = NULL;
2201 HRESULT r;
2203 TRACE("%p %s %08x %08x %p\n", This, debugstr_guid(rfmtid), grfFlags,
2204 grfMode, ppprstg);
2206 /* be picky */
2207 if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
2209 r = STG_E_INVALIDFLAG;
2210 goto end;
2213 if (!rfmtid)
2215 r = E_INVALIDARG;
2216 goto end;
2219 /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
2220 * storage, not a stream. For now, disallow it.
2222 if (grfFlags & PROPSETFLAG_NONSIMPLE)
2224 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2225 r = STG_E_INVALIDFLAG;
2226 goto end;
2229 r = FmtIdToPropStgName(rfmtid, name);
2230 if (FAILED(r))
2231 goto end;
2233 r = IStorage_CreateStream( &This->base.IStorage_iface, name, grfMode, 0, 0, &stm );
2234 if (FAILED(r))
2235 goto end;
2237 r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg);
2239 IStream_Release( stm );
2241 end:
2242 TRACE("returning 0x%08x\n", r);
2243 return r;
2246 /************************************************************************
2247 * IPropertySetStorage_fnOpen (IPropertySetStorage)
2249 static HRESULT WINAPI IPropertySetStorage_fnOpen(
2250 IPropertySetStorage *ppstg,
2251 REFFMTID rfmtid,
2252 DWORD grfMode,
2253 IPropertyStorage** ppprstg)
2255 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2256 IStream *stm = NULL;
2257 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2258 HRESULT r;
2260 TRACE("%p %s %08x %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
2262 /* be picky */
2263 if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
2264 grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
2266 r = STG_E_INVALIDFLAG;
2267 goto end;
2270 if (!rfmtid)
2272 r = E_INVALIDARG;
2273 goto end;
2276 r = FmtIdToPropStgName(rfmtid, name);
2277 if (FAILED(r))
2278 goto end;
2280 r = IStorage_OpenStream( &This->base.IStorage_iface, name, 0, grfMode, 0, &stm );
2281 if (FAILED(r))
2282 goto end;
2284 r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg);
2286 IStream_Release( stm );
2288 end:
2289 TRACE("returning 0x%08x\n", r);
2290 return r;
2293 /************************************************************************
2294 * IPropertySetStorage_fnDelete (IPropertySetStorage)
2296 static HRESULT WINAPI IPropertySetStorage_fnDelete(
2297 IPropertySetStorage *ppstg,
2298 REFFMTID rfmtid)
2300 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2301 WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2302 HRESULT r;
2304 TRACE("%p %s\n", This, debugstr_guid(rfmtid));
2306 if (!rfmtid)
2307 return E_INVALIDARG;
2309 r = FmtIdToPropStgName(rfmtid, name);
2310 if (FAILED(r))
2311 return r;
2313 return IStorage_DestroyElement(&This->base.IStorage_iface, name);
2316 /************************************************************************
2317 * IPropertySetStorage_fnEnum (IPropertySetStorage)
2319 static HRESULT WINAPI IPropertySetStorage_fnEnum(
2320 IPropertySetStorage *ppstg,
2321 IEnumSTATPROPSETSTG** ppenum)
2323 StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2324 return create_EnumSTATPROPSETSTG(This, ppenum);
2327 /************************************************************************
2328 * Implement IEnumSTATPROPSETSTG using enumx
2330 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnQueryInterface(
2331 IEnumSTATPROPSETSTG *iface,
2332 REFIID riid,
2333 void** ppvObject)
2335 return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
2338 static ULONG WINAPI IEnumSTATPROPSETSTG_fnAddRef(
2339 IEnumSTATPROPSETSTG *iface)
2341 return enumx_AddRef((enumx_impl*)iface);
2344 static ULONG WINAPI IEnumSTATPROPSETSTG_fnRelease(
2345 IEnumSTATPROPSETSTG *iface)
2347 return enumx_Release((enumx_impl*)iface);
2350 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnNext(
2351 IEnumSTATPROPSETSTG *iface,
2352 ULONG celt,
2353 STATPROPSETSTG *rgelt,
2354 ULONG *pceltFetched)
2356 return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
2359 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnSkip(
2360 IEnumSTATPROPSETSTG *iface,
2361 ULONG celt)
2363 return enumx_Skip((enumx_impl*)iface, celt);
2366 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnReset(
2367 IEnumSTATPROPSETSTG *iface)
2369 return enumx_Reset((enumx_impl*)iface);
2372 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnClone(
2373 IEnumSTATPROPSETSTG *iface,
2374 IEnumSTATPROPSETSTG **ppenum)
2376 return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
2379 static HRESULT create_EnumSTATPROPSETSTG(
2380 StorageImpl *This,
2381 IEnumSTATPROPSETSTG** ppenum)
2383 IStorage *stg = &This->base.IStorage_iface;
2384 IEnumSTATSTG *penum = NULL;
2385 STATSTG stat;
2386 ULONG count;
2387 HRESULT r;
2388 STATPROPSETSTG statpss;
2389 enumx_impl *enumx;
2391 TRACE("%p %p\n", This, ppenum);
2393 enumx = enumx_allocate(&IID_IEnumSTATPROPSETSTG,
2394 &IEnumSTATPROPSETSTG_Vtbl,
2395 sizeof (STATPROPSETSTG));
2397 /* add all the property set elements into a list */
2398 r = IStorage_EnumElements(stg, 0, NULL, 0, &penum);
2399 if (FAILED(r))
2401 enumx_Release(enumx);
2402 return E_OUTOFMEMORY;
2405 while (1)
2407 count = 0;
2408 r = IEnumSTATSTG_Next(penum, 1, &stat, &count);
2409 if (FAILED(r))
2410 break;
2411 if (!count)
2412 break;
2413 if (!stat.pwcsName)
2414 continue;
2415 if (stat.pwcsName[0] == 5 && stat.type == STGTY_STREAM)
2417 PropStgNameToFmtId(stat.pwcsName, &statpss.fmtid);
2418 TRACE("adding %s (%s)\n", debugstr_w(stat.pwcsName),
2419 debugstr_guid(&statpss.fmtid));
2420 statpss.mtime = stat.mtime;
2421 statpss.atime = stat.atime;
2422 statpss.ctime = stat.ctime;
2423 statpss.grfFlags = stat.grfMode;
2424 statpss.clsid = stat.clsid;
2425 enumx_add_element(enumx, &statpss);
2427 CoTaskMemFree(stat.pwcsName);
2429 IEnumSTATSTG_Release(penum);
2431 *ppenum = (IEnumSTATPROPSETSTG*) enumx;
2433 return S_OK;
2436 /************************************************************************
2437 * Implement IEnumSTATPROPSTG using enumx
2439 static HRESULT WINAPI IEnumSTATPROPSTG_fnQueryInterface(
2440 IEnumSTATPROPSTG *iface,
2441 REFIID riid,
2442 void** ppvObject)
2444 return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
2447 static ULONG WINAPI IEnumSTATPROPSTG_fnAddRef(
2448 IEnumSTATPROPSTG *iface)
2450 return enumx_AddRef((enumx_impl*)iface);
2453 static ULONG WINAPI IEnumSTATPROPSTG_fnRelease(
2454 IEnumSTATPROPSTG *iface)
2456 return enumx_Release((enumx_impl*)iface);
2459 static HRESULT WINAPI IEnumSTATPROPSTG_fnNext(
2460 IEnumSTATPROPSTG *iface,
2461 ULONG celt,
2462 STATPROPSTG *rgelt,
2463 ULONG *pceltFetched)
2465 return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
2468 static HRESULT WINAPI IEnumSTATPROPSTG_fnSkip(
2469 IEnumSTATPROPSTG *iface,
2470 ULONG celt)
2472 return enumx_Skip((enumx_impl*)iface, celt);
2475 static HRESULT WINAPI IEnumSTATPROPSTG_fnReset(
2476 IEnumSTATPROPSTG *iface)
2478 return enumx_Reset((enumx_impl*)iface);
2481 static HRESULT WINAPI IEnumSTATPROPSTG_fnClone(
2482 IEnumSTATPROPSTG *iface,
2483 IEnumSTATPROPSTG **ppenum)
2485 return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
2488 static BOOL prop_enum_stat(const void *k, const void *v, void *extra, void *arg)
2490 enumx_impl *enumx = arg;
2491 PROPID propid = PtrToUlong(k);
2492 const PROPVARIANT *prop = v;
2493 STATPROPSTG stat;
2495 stat.lpwstrName = NULL;
2496 stat.propid = propid;
2497 stat.vt = prop->vt;
2499 enumx_add_element(enumx, &stat);
2501 return TRUE;
2504 static HRESULT create_EnumSTATPROPSTG(
2505 PropertyStorage_impl *This,
2506 IEnumSTATPROPSTG** ppenum)
2508 enumx_impl *enumx;
2510 TRACE("%p %p\n", This, ppenum);
2512 enumx = enumx_allocate(&IID_IEnumSTATPROPSTG,
2513 &IEnumSTATPROPSTG_Vtbl,
2514 sizeof (STATPROPSTG));
2516 dictionary_enumerate(This->propid_to_prop, prop_enum_stat, enumx);
2518 *ppenum = (IEnumSTATPROPSTG*) enumx;
2520 return S_OK;
2523 /***********************************************************************
2524 * vtables
2526 const IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
2528 IPropertySetStorage_fnQueryInterface,
2529 IPropertySetStorage_fnAddRef,
2530 IPropertySetStorage_fnRelease,
2531 IPropertySetStorage_fnCreate,
2532 IPropertySetStorage_fnOpen,
2533 IPropertySetStorage_fnDelete,
2534 IPropertySetStorage_fnEnum
2537 static const IPropertyStorageVtbl IPropertyStorage_Vtbl =
2539 IPropertyStorage_fnQueryInterface,
2540 IPropertyStorage_fnAddRef,
2541 IPropertyStorage_fnRelease,
2542 IPropertyStorage_fnReadMultiple,
2543 IPropertyStorage_fnWriteMultiple,
2544 IPropertyStorage_fnDeleteMultiple,
2545 IPropertyStorage_fnReadPropertyNames,
2546 IPropertyStorage_fnWritePropertyNames,
2547 IPropertyStorage_fnDeletePropertyNames,
2548 IPropertyStorage_fnCommit,
2549 IPropertyStorage_fnRevert,
2550 IPropertyStorage_fnEnum,
2551 IPropertyStorage_fnSetTimes,
2552 IPropertyStorage_fnSetClass,
2553 IPropertyStorage_fnStat,
2556 static const IEnumSTATPROPSETSTGVtbl IEnumSTATPROPSETSTG_Vtbl =
2558 IEnumSTATPROPSETSTG_fnQueryInterface,
2559 IEnumSTATPROPSETSTG_fnAddRef,
2560 IEnumSTATPROPSETSTG_fnRelease,
2561 IEnumSTATPROPSETSTG_fnNext,
2562 IEnumSTATPROPSETSTG_fnSkip,
2563 IEnumSTATPROPSETSTG_fnReset,
2564 IEnumSTATPROPSETSTG_fnClone,
2567 static const IEnumSTATPROPSTGVtbl IEnumSTATPROPSTG_Vtbl =
2569 IEnumSTATPROPSTG_fnQueryInterface,
2570 IEnumSTATPROPSTG_fnAddRef,
2571 IEnumSTATPROPSTG_fnRelease,
2572 IEnumSTATPROPSTG_fnNext,
2573 IEnumSTATPROPSTG_fnSkip,
2574 IEnumSTATPROPSTG_fnReset,
2575 IEnumSTATPROPSTG_fnClone,
2578 /***********************************************************************
2579 * Format ID <-> name conversion
2581 static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
2582 'I','n','f','o','r','m','a','t','i','o','n',0 };
2583 static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t',
2584 'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
2586 #define BITS_PER_BYTE 8
2587 #define CHARMASK 0x1f
2588 #define BITS_IN_CHARMASK 5
2589 #define NUM_ALPHA_CHARS 26
2591 /***********************************************************************
2592 * FmtIdToPropStgName [ole32.@]
2593 * Returns the storage name of the format ID rfmtid.
2594 * PARAMS
2595 * rfmtid [I] Format ID for which to return a storage name
2596 * str [O] Storage name associated with rfmtid.
2598 * RETURNS
2599 * E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise.
2601 * NOTES
2602 * str must be at least CCH_MAX_PROPSTG_NAME characters in length.
2604 HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str)
2606 static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
2608 TRACE("%s, %p\n", debugstr_guid(rfmtid), str);
2610 if (!rfmtid) return E_INVALIDARG;
2611 if (!str) return E_INVALIDARG;
2613 if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
2614 lstrcpyW(str, szSummaryInfo);
2615 else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
2616 lstrcpyW(str, szDocSummaryInfo);
2617 else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid))
2618 lstrcpyW(str, szDocSummaryInfo);
2619 else
2621 const BYTE *fmtptr;
2622 WCHAR *pstr = str;
2623 ULONG bitsRemaining = BITS_PER_BYTE;
2625 *pstr++ = 5;
2626 for (fmtptr = (const BYTE *)rfmtid; fmtptr < (const BYTE *)rfmtid + sizeof(FMTID); )
2628 ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
2630 if (bitsRemaining >= BITS_IN_CHARMASK)
2632 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
2633 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
2634 *pstr <= 'z')
2635 *pstr += 'A' - 'a';
2636 pstr++;
2637 bitsRemaining -= BITS_IN_CHARMASK;
2638 if (bitsRemaining == 0)
2640 fmtptr++;
2641 bitsRemaining = BITS_PER_BYTE;
2644 else
2646 if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID))
2647 i |= *fmtptr << bitsRemaining;
2648 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
2649 bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK;
2652 *pstr = 0;
2654 TRACE("returning %s\n", debugstr_w(str));
2655 return S_OK;
2658 /***********************************************************************
2659 * PropStgNameToFmtId [ole32.@]
2660 * Returns the format ID corresponding to the given name.
2661 * PARAMS
2662 * str [I] Storage name to convert to a format ID.
2663 * rfmtid [O] Format ID corresponding to str.
2665 * RETURNS
2666 * E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to
2667 * a format ID, S_OK otherwise.
2669 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid)
2671 HRESULT hr = STG_E_INVALIDNAME;
2673 TRACE("%s, %p\n", debugstr_w(str), rfmtid);
2675 if (!rfmtid) return E_INVALIDARG;
2676 if (!str) return STG_E_INVALIDNAME;
2678 if (!lstrcmpiW(str, szDocSummaryInfo))
2680 *rfmtid = FMTID_DocSummaryInformation;
2681 hr = S_OK;
2683 else if (!lstrcmpiW(str, szSummaryInfo))
2685 *rfmtid = FMTID_SummaryInformation;
2686 hr = S_OK;
2688 else
2690 ULONG bits;
2691 BYTE *fmtptr = (BYTE *)rfmtid - 1;
2692 const WCHAR *pstr = str;
2694 memset(rfmtid, 0, sizeof(*rfmtid));
2695 for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE;
2696 bits += BITS_IN_CHARMASK)
2698 ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored;
2699 WCHAR wc;
2701 if (bitsUsed == 0)
2702 fmtptr++;
2703 wc = *++pstr - 'A';
2704 if (wc > NUM_ALPHA_CHARS)
2706 wc += 'A' - 'a';
2707 if (wc > NUM_ALPHA_CHARS)
2709 wc += 'a' - '0' + NUM_ALPHA_CHARS;
2710 if (wc > CHARMASK)
2712 WARN("invalid character (%d)\n", *pstr);
2713 goto end;
2717 *fmtptr |= wc << bitsUsed;
2718 bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK);
2719 if (bitsStored < BITS_IN_CHARMASK)
2721 wc >>= BITS_PER_BYTE - bitsUsed;
2722 if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE)
2724 if (wc != 0)
2726 WARN("extra bits\n");
2727 goto end;
2729 break;
2731 fmtptr++;
2732 *fmtptr |= (BYTE)wc;
2735 hr = S_OK;
2737 end:
2738 return hr;
2741 #ifdef __i386__ /* thiscall functions are i386-specific */
2743 #define DEFINE_STDCALL_WRAPPER(num,func,args) \
2744 __ASM_STDCALL_FUNC(func, args, \
2745 "popl %eax\n\t" \
2746 "popl %ecx\n\t" \
2747 "pushl %eax\n\t" \
2748 "movl (%ecx), %eax\n\t" \
2749 "jmp *(4*(" #num "))(%eax)" )
2751 DEFINE_STDCALL_WRAPPER(0,Allocate_PMemoryAllocator,8)
2752 extern void* WINAPI Allocate_PMemoryAllocator(void *this, ULONG cbSize);
2754 #else
2756 static void* WINAPI Allocate_PMemoryAllocator(void *this, ULONG cbSize)
2758 void* (WINAPI *fn)(void*,ULONG) = **(void***)this;
2759 return fn(this, cbSize);
2762 #endif
2764 BOOLEAN WINAPI StgConvertPropertyToVariant(const SERIALIZEDPROPERTYVALUE* prop,
2765 USHORT CodePage, PROPVARIANT* pvar, void* pma)
2767 HRESULT hr;
2769 hr = PropertyStorage_ReadProperty(pvar, (const BYTE*)prop, CodePage, Allocate_PMemoryAllocator, pma);
2771 if (FAILED(hr))
2773 FIXME("should raise C++ exception on failure\n");
2774 PropVariantInit(pvar);
2777 return FALSE;
2780 SERIALIZEDPROPERTYVALUE* WINAPI StgConvertVariantToProperty(const PROPVARIANT *pvar,
2781 USHORT CodePage, SERIALIZEDPROPERTYVALUE *pprop, ULONG *pcb, PROPID pid,
2782 BOOLEAN fReserved, ULONG *pcIndirect)
2784 FIXME("%p,%d,%p,%p,%d,%d,%p\n", pvar, CodePage, pprop, pcb, pid, fReserved, pcIndirect);
2786 return NULL;
2789 HRESULT WINAPI StgCreatePropStg(IUnknown *unk, REFFMTID fmt, const CLSID *clsid,
2790 DWORD flags, DWORD reserved, IPropertyStorage **prop_stg)
2792 IStorage *stg;
2793 IStream *stm;
2794 HRESULT r;
2796 TRACE("%p %s %s %08x %d %p\n", unk, debugstr_guid(fmt), debugstr_guid(clsid), flags, reserved, prop_stg);
2798 if (!fmt || reserved)
2800 r = E_INVALIDARG;
2801 goto end;
2804 if (flags & PROPSETFLAG_NONSIMPLE)
2806 r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
2807 if (FAILED(r))
2808 goto end;
2810 /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to create a
2811 * storage, not a stream. For now, disallow it.
2813 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2814 IStorage_Release(stg);
2815 r = STG_E_INVALIDFLAG;
2817 else
2819 r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
2820 if (FAILED(r))
2821 goto end;
2823 r = PropertyStorage_ConstructEmpty(stm, fmt, flags,
2824 STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
2826 IStream_Release( stm );
2829 end:
2830 TRACE("returning 0x%08x\n", r);
2831 return r;
2834 HRESULT WINAPI StgOpenPropStg(IUnknown *unk, REFFMTID fmt, DWORD flags,
2835 DWORD reserved, IPropertyStorage **prop_stg)
2837 IStorage *stg;
2838 IStream *stm;
2839 HRESULT r;
2841 TRACE("%p %s %08x %d %p\n", unk, debugstr_guid(fmt), flags, reserved, prop_stg);
2843 if (!fmt || reserved)
2845 r = E_INVALIDARG;
2846 goto end;
2849 if (flags & PROPSETFLAG_NONSIMPLE)
2851 r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
2852 if (FAILED(r))
2853 goto end;
2855 /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to open a
2856 * storage, not a stream. For now, disallow it.
2858 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2859 IStorage_Release(stg);
2860 r = STG_E_INVALIDFLAG;
2862 else
2864 r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
2865 if (FAILED(r))
2866 goto end;
2868 r = PropertyStorage_ConstructFromStream(stm, fmt,
2869 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
2871 IStream_Release( stm );
2874 end:
2875 TRACE("returning 0x%08x\n", r);
2876 return r;