- More implementation of view
[wine/testsucceed.git] / dlls / ole32 / stg_prop.c
blob302840ad6b5e0d7b7868921272ea0813f5e6379d
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
12 * Copyright 2005 Juan Lang
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 * There's a decent overview of property set storage here:
29 * http://msdn.microsoft.com/archive/en-us/dnarolegen/html/msdn_propset.asp
30 * It's a little bit out of date, and more definitive references are given
31 * below, but it gives the best "big picture" that I've found.
33 * TODO: There's a lot missing in here. Biggies:
34 * - There are all sorts of restricions I don't honor, like maximum property
35 * set byte size, maximum property name length
36 * - Certain bogus files could result in reading past the end of a buffer.
37 * - This will probably fail on big-endian machines, especially reading and
38 * writing strings.
39 * - Mac-generated files won't be read correctly, even if they're little
40 * endian, because I disregard whether the generator was a Mac. This means
41 * strings will probably be munged (as I don't understand Mac scripts.)
42 * - Not all PROPVARIANT types are supported.
43 * There are lots more unimplemented features, see FIXMEs below.
46 #include <assert.h>
47 #include <stdarg.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
52 #define COBJMACROS
53 #define NONAMELESSUNION
54 #define NONAMELESSSTRUCT
56 #include "windef.h"
57 #include "winbase.h"
58 #include "winnls.h"
59 #include "winuser.h"
60 #include "wine/unicode.h"
61 #include "wine/debug.h"
62 #include "dictionary.h"
63 #include "storage32.h"
65 WINE_DEFAULT_DEBUG_CHANNEL(storage);
67 #define _IPropertySetStorage_Offset ((int)(&(((StorageImpl*)0)->base.pssVtbl)))
68 #define _ICOM_THIS_From_IPropertySetStorage(class, name) \
69 class* This = (class*)(((char*)name)-_IPropertySetStorage_Offset)
71 /* These are documented in MSDN, e.g.
72 * http://msdn.microsoft.com/library/en-us/stg/stg/property_set_header.asp
73 * http://msdn.microsoft.com/library/library/en-us/stg/stg/section.asp
74 * but they don't seem to be in any header file.
76 #define PROPSETHDR_BYTEORDER_MAGIC 0xfffe
77 #define PROPSETHDR_OSVER_KIND_WIN16 0
78 #define PROPSETHDR_OSVER_KIND_MAC 1
79 #define PROPSETHDR_OSVER_KIND_WIN32 2
81 #define CP_UNICODE 1200
83 /* The format version (and what it implies) is described here:
84 * http://msdn.microsoft.com/library/en-us/stg/stg/format_version.asp
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 struct tagPropertyStorage_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(struct tagPropertyStorage_impl *);
121 static HRESULT PropertyStorage_WriteToStream(struct tagPropertyStorage_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(
128 struct tagPropertyStorage_impl *);
130 static void PropertyStorage_DestroyDictionaries(
131 struct tagPropertyStorage_impl *);
133 static IPropertyStorageVtbl IPropertyStorage_Vtbl;
135 /***********************************************************************
136 * Implementation of IPropertyStorage
138 typedef struct tagPropertyStorage_impl
140 IPropertyStorageVtbl *vtbl;
141 DWORD ref;
142 CRITICAL_SECTION cs;
143 IStream *stm;
144 BOOL dirty;
145 FMTID fmtid;
146 CLSID clsid;
147 DWORD originatorOS;
148 DWORD grfFlags;
149 DWORD grfMode;
150 UINT codePage;
151 LCID locale;
152 PROPID highestProp;
153 struct dictionary *name_to_propid;
154 struct dictionary *propid_to_name;
155 struct dictionary *propid_to_prop;
156 } PropertyStorage_impl;
158 /************************************************************************
159 * IPropertyStorage_fnQueryInterface (IPropertyStorage)
161 static HRESULT WINAPI IPropertyStorage_fnQueryInterface(
162 IPropertyStorage *iface,
163 REFIID riid,
164 void** ppvObject)
166 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
168 if ( (This==0) || (ppvObject==0) )
169 return E_INVALIDARG;
171 *ppvObject = 0;
173 if (IsEqualGUID(&IID_IUnknown, riid) ||
174 IsEqualGUID(&IID_IPropertyStorage, riid))
176 IPropertyStorage_AddRef(iface);
177 *ppvObject = (IPropertyStorage*)iface;
178 return S_OK;
181 return E_NOINTERFACE;
184 /************************************************************************
185 * IPropertyStorage_fnAddRef (IPropertyStorage)
187 static ULONG WINAPI IPropertyStorage_fnAddRef(
188 IPropertyStorage *iface)
190 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
191 return InterlockedIncrement(&This->ref);
194 /************************************************************************
195 * IPropertyStorage_fnRelease (IPropertyStorage)
197 static ULONG WINAPI IPropertyStorage_fnRelease(
198 IPropertyStorage *iface)
200 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
201 ULONG ref;
203 ref = InterlockedDecrement(&This->ref);
204 if (ref == 0)
206 TRACE("Destroying %p\n", This);
207 if (This->dirty)
208 IPropertyStorage_Commit(iface, STGC_DEFAULT);
209 IStream_Release(This->stm);
210 DeleteCriticalSection(&This->cs);
211 PropertyStorage_DestroyDictionaries(This);
212 HeapFree(GetProcessHeap(), 0, This);
214 return ref;
217 static PROPVARIANT *PropertyStorage_FindProperty(PropertyStorage_impl *This,
218 DWORD propid)
220 PROPVARIANT *ret = NULL;
222 assert(This);
223 dictionary_find(This->propid_to_prop, (void *)propid, (void **)&ret);
224 TRACE("returning %p\n", ret);
225 return ret;
228 /* Returns NULL if name is NULL. */
229 static PROPVARIANT *PropertyStorage_FindPropertyByName(
230 PropertyStorage_impl *This, LPCWSTR name)
232 PROPVARIANT *ret = NULL;
233 PROPID propid;
235 assert(This);
236 if (!name)
237 return NULL;
238 if (dictionary_find(This->name_to_propid, name, (void **)&propid))
239 ret = PropertyStorage_FindProperty(This, (PROPID)propid);
240 TRACE("returning %p\n", ret);
241 return ret;
244 static LPWSTR PropertyStorage_FindPropertyNameById(PropertyStorage_impl *This,
245 DWORD propid)
247 LPWSTR ret = NULL;
249 assert(This);
250 dictionary_find(This->propid_to_name, (void *)propid, (void **)&ret);
251 TRACE("returning %p\n", ret);
252 return ret;
255 /************************************************************************
256 * IPropertyStorage_fnReadMultiple (IPropertyStorage)
258 static HRESULT WINAPI IPropertyStorage_fnReadMultiple(
259 IPropertyStorage* iface,
260 ULONG cpspec,
261 const PROPSPEC rgpspec[],
262 PROPVARIANT rgpropvar[])
264 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
265 HRESULT hr = S_FALSE;
266 ULONG i;
268 TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
269 if (!This)
270 return E_INVALIDARG;
271 if (cpspec && (!rgpspec || !rgpropvar))
272 return E_INVALIDARG;
273 EnterCriticalSection(&This->cs);
274 for (i = 0; i < cpspec; i++)
276 PropVariantInit(&rgpropvar[i]);
277 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
279 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
280 rgpspec[i].u.lpwstr);
282 if (prop)
283 PropVariantCopy(&rgpropvar[i], prop);
285 else
287 PROPVARIANT *prop = PropertyStorage_FindProperty(This,
288 rgpspec[i].u.propid);
290 if (prop)
291 PropVariantCopy(&rgpropvar[i], prop);
294 LeaveCriticalSection(&This->cs);
295 return hr;
298 static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
299 PROPID propid, const PROPVARIANT *propvar)
301 HRESULT hr = S_OK;
302 PROPVARIANT *prop = PropertyStorage_FindProperty(This, propid);
304 assert(This);
305 assert(propvar);
306 TRACE("Setting 0x%08lx to type %d\n", propid, propvar->vt);
307 if (prop)
309 PropVariantClear(prop);
310 PropVariantCopy(prop, propvar);
312 else
314 prop = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
315 sizeof(PROPVARIANT));
316 if (prop)
318 PropVariantCopy(prop, propvar);
319 dictionary_insert(This->propid_to_prop, (void *)propid, prop);
320 if (propid > This->highestProp)
321 This->highestProp = propid;
323 else
324 hr = STG_E_INSUFFICIENTMEMORY;
326 return hr;
329 /************************************************************************
330 * IPropertyStorage_fnWriteMultiple (IPropertyStorage)
332 static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
333 IPropertyStorage* iface,
334 ULONG cpspec,
335 const PROPSPEC rgpspec[],
336 const PROPVARIANT rgpropvar[],
337 PROPID propidNameFirst)
339 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
340 HRESULT hr = S_OK;
341 ULONG i;
343 TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
344 if (!This)
345 return E_INVALIDARG;
346 if (cpspec && (!rgpspec || !rgpropvar))
347 return E_INVALIDARG;
348 if (!(This->grfMode & STGM_READWRITE))
349 return STG_E_ACCESSDENIED;
350 EnterCriticalSection(&This->cs);
351 This->dirty = TRUE;
352 This->originatorOS = (DWORD)MAKELONG(LOWORD(GetVersion()),
353 PROPSETHDR_OSVER_KIND_WIN32) ;
354 for (i = 0; i < cpspec; i++)
356 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
358 PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
359 rgpspec[i].u.lpwstr);
361 if (prop)
362 PropVariantCopy(prop, &rgpropvar[i]);
363 else
365 /* Note that I don't do the special cases here that I do below,
366 * because naming the special PIDs isn't supported.
368 if (propidNameFirst < PID_FIRST_USABLE ||
369 propidNameFirst >= PID_MIN_READONLY)
370 hr = STG_E_INVALIDPARAMETER;
371 else
373 PROPID nextId = max(propidNameFirst, This->highestProp + 1);
374 size_t len = strlenW(rgpspec[i].u.lpwstr) + 1;
375 LPWSTR name = HeapAlloc(GetProcessHeap(), 0,
376 len * sizeof(WCHAR));
378 strcpyW(name, rgpspec[i].u.lpwstr);
379 TRACE("Adding prop name %s, propid %ld\n", debugstr_w(name),
380 nextId);
381 dictionary_insert(This->name_to_propid, name,
382 (void *)nextId);
383 dictionary_insert(This->propid_to_name, (void *)nextId,
384 name);
385 hr = PropertyStorage_StorePropWithId(This, nextId,
386 &rgpropvar[i]);
390 else
392 switch (rgpspec[i].u.propid)
394 case PID_DICTIONARY:
395 /* Can't set the dictionary */
396 hr = STG_E_INVALIDPARAMETER;
397 break;
398 case PID_CODEPAGE:
399 /* Can only set the code page if nothing else has been set */
400 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
401 rgpropvar[i].vt == VT_I2)
402 This->codePage = rgpropvar[i].u.iVal;
403 else
404 hr = STG_E_INVALIDPARAMETER;
405 break;
406 case PID_LOCALE:
407 /* Can only set the locale if nothing else has been set */
408 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
409 rgpropvar[i].vt == VT_I4)
410 This->locale = rgpropvar[i].u.lVal;
411 else
412 hr = STG_E_INVALIDPARAMETER;
413 break;
414 case PID_ILLEGAL:
415 /* silently ignore like MSDN says */
416 break;
417 default:
418 if (rgpspec[i].u.propid >= PID_MIN_READONLY)
419 hr = STG_E_INVALIDPARAMETER;
420 else
421 hr = PropertyStorage_StorePropWithId(This,
422 rgpspec[i].u.propid, &rgpropvar[i]);
426 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
427 IPropertyStorage_Commit(iface, STGC_DEFAULT);
428 LeaveCriticalSection(&This->cs);
429 return hr;
432 /************************************************************************
433 * IPropertyStorage_fnDeleteMultiple (IPropertyStorage)
435 static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
436 IPropertyStorage* iface,
437 ULONG cpspec,
438 const PROPSPEC rgpspec[])
440 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
441 ULONG i;
442 HRESULT hr;
444 TRACE("(%p, %ld, %p)\n", iface, cpspec, rgpspec);
445 if (!This)
446 return E_INVALIDARG;
447 if (cpspec && !rgpspec)
448 return E_INVALIDARG;
449 if (!(This->grfMode & STGM_READWRITE))
450 return STG_E_ACCESSDENIED;
451 hr = S_OK;
452 EnterCriticalSection(&This->cs);
453 This->dirty = TRUE;
454 for (i = 0; i < cpspec; i++)
456 if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
458 PROPID propid;
460 if (dictionary_find(This->name_to_propid,
461 (void *)rgpspec[i].u.lpwstr, (void **)&propid))
462 dictionary_remove(This->propid_to_prop, (void *)propid);
464 else
466 if (rgpspec[i].u.propid >= PID_FIRST_USABLE &&
467 rgpspec[i].u.propid < PID_MIN_READONLY)
468 dictionary_remove(This->propid_to_prop,
469 (void *)rgpspec[i].u.propid);
470 else
471 hr = STG_E_INVALIDPARAMETER;
474 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
475 IPropertyStorage_Commit(iface, STGC_DEFAULT);
476 LeaveCriticalSection(&This->cs);
477 return hr;
480 /************************************************************************
481 * IPropertyStorage_fnReadPropertyNames (IPropertyStorage)
483 static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames(
484 IPropertyStorage* iface,
485 ULONG cpropid,
486 const PROPID rgpropid[],
487 LPOLESTR rglpwstrName[])
489 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
490 ULONG i;
491 HRESULT hr = S_FALSE;
493 TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
494 if (!This)
495 return E_INVALIDARG;
496 if (cpropid && (!rgpropid || !rglpwstrName))
497 return E_INVALIDARG;
498 EnterCriticalSection(&This->cs);
499 for (i = 0; i < cpropid && SUCCEEDED(hr); i++)
501 LPWSTR name = PropertyStorage_FindPropertyNameById(This, rgpropid[i]);
503 if (name)
505 size_t len = lstrlenW(name);
507 hr = S_OK;
508 rglpwstrName[i] = CoTaskMemAlloc((len + 1) * sizeof(WCHAR));
509 if (rglpwstrName)
510 memcpy(rglpwstrName, name, (len + 1) * sizeof(WCHAR));
511 else
512 hr = STG_E_INSUFFICIENTMEMORY;
514 else
515 rglpwstrName[i] = NULL;
517 LeaveCriticalSection(&This->cs);
518 return hr;
521 /************************************************************************
522 * IPropertyStorage_fnWritePropertyNames (IPropertyStorage)
524 static HRESULT WINAPI IPropertyStorage_fnWritePropertyNames(
525 IPropertyStorage* iface,
526 ULONG cpropid,
527 const PROPID rgpropid[],
528 const LPOLESTR rglpwstrName[])
530 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
531 ULONG i;
532 HRESULT hr;
534 TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
535 if (!This)
536 return E_INVALIDARG;
537 if (cpropid && (!rgpropid || !rglpwstrName))
538 return E_INVALIDARG;
539 if (!(This->grfMode & STGM_READWRITE))
540 return STG_E_ACCESSDENIED;
541 hr = S_OK;
542 EnterCriticalSection(&This->cs);
543 This->dirty = TRUE;
544 for (i = 0; i < cpropid; i++)
546 if (rgpropid[i] != PID_ILLEGAL)
548 size_t len = lstrlenW(rglpwstrName[i]) + 1;
549 LPWSTR name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
551 strcpyW(name, rglpwstrName[i]);
552 dictionary_insert(This->name_to_propid, name, (void *)rgpropid[i]);
553 dictionary_insert(This->propid_to_name, (void *)rgpropid[i], name);
556 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
557 IPropertyStorage_Commit(iface, STGC_DEFAULT);
558 LeaveCriticalSection(&This->cs);
559 return hr;
562 /************************************************************************
563 * IPropertyStorage_fnDeletePropertyNames (IPropertyStorage)
565 static HRESULT WINAPI IPropertyStorage_fnDeletePropertyNames(
566 IPropertyStorage* iface,
567 ULONG cpropid,
568 const PROPID rgpropid[])
570 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
571 ULONG i;
572 HRESULT hr;
574 TRACE("(%p, %ld, %p)\n", iface, cpropid, rgpropid);
575 if (!This)
576 return E_INVALIDARG;
577 if (cpropid && !rgpropid)
578 return E_INVALIDARG;
579 if (!(This->grfMode & STGM_READWRITE))
580 return STG_E_ACCESSDENIED;
581 hr = S_OK;
582 EnterCriticalSection(&This->cs);
583 This->dirty = TRUE;
584 for (i = 0; i < cpropid; i++)
586 LPWSTR name = NULL;
588 if (dictionary_find(This->propid_to_name, (void *)rgpropid[i],
589 (void **)&name))
591 dictionary_remove(This->propid_to_name, (void *)rgpropid[i]);
592 dictionary_remove(This->name_to_propid, name);
595 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
596 IPropertyStorage_Commit(iface, STGC_DEFAULT);
597 LeaveCriticalSection(&This->cs);
598 return hr;
601 /************************************************************************
602 * IPropertyStorage_fnCommit (IPropertyStorage)
604 static HRESULT WINAPI IPropertyStorage_fnCommit(
605 IPropertyStorage* iface,
606 DWORD grfCommitFlags)
608 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
609 HRESULT hr;
611 TRACE("(%p, 0x%08lx)\n", iface, grfCommitFlags);
612 if (!This)
613 return E_INVALIDARG;
614 if (!(This->grfMode & STGM_READWRITE))
615 return STG_E_ACCESSDENIED;
616 EnterCriticalSection(&This->cs);
617 if (This->dirty)
618 hr = PropertyStorage_WriteToStream(This);
619 else
620 hr = S_OK;
621 LeaveCriticalSection(&This->cs);
622 return hr;
625 /************************************************************************
626 * IPropertyStorage_fnRevert (IPropertyStorage)
628 static HRESULT WINAPI IPropertyStorage_fnRevert(
629 IPropertyStorage* iface)
631 HRESULT hr;
632 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
634 TRACE("%p\n", iface);
635 if (!This)
636 return E_INVALIDARG;
638 EnterCriticalSection(&This->cs);
639 if (This->dirty)
641 PropertyStorage_DestroyDictionaries(This);
642 hr = PropertyStorage_CreateDictionaries(This);
643 if (SUCCEEDED(hr))
644 hr = PropertyStorage_ReadFromStream(This);
646 else
647 hr = S_OK;
648 LeaveCriticalSection(&This->cs);
649 return hr;
652 /************************************************************************
653 * IPropertyStorage_fnEnum (IPropertyStorage)
655 static HRESULT WINAPI IPropertyStorage_fnEnum(
656 IPropertyStorage* iface,
657 IEnumSTATPROPSTG** ppenum)
659 FIXME("\n");
660 return E_NOTIMPL;
663 /************************************************************************
664 * IPropertyStorage_fnSetTimes (IPropertyStorage)
666 static HRESULT WINAPI IPropertyStorage_fnSetTimes(
667 IPropertyStorage* iface,
668 const FILETIME* pctime,
669 const FILETIME* patime,
670 const FILETIME* pmtime)
672 FIXME("\n");
673 return E_NOTIMPL;
676 /************************************************************************
677 * IPropertyStorage_fnSetClass (IPropertyStorage)
679 static HRESULT WINAPI IPropertyStorage_fnSetClass(
680 IPropertyStorage* iface,
681 REFCLSID clsid)
683 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
685 TRACE("%p, %s\n", iface, debugstr_guid(clsid));
686 if (!This || !clsid)
687 return E_INVALIDARG;
688 if (!(This->grfMode & STGM_READWRITE))
689 return STG_E_ACCESSDENIED;
690 memcpy(&This->clsid, clsid, sizeof(This->clsid));
691 This->dirty = TRUE;
692 if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
693 IPropertyStorage_Commit(iface, STGC_DEFAULT);
694 return S_OK;
697 /************************************************************************
698 * IPropertyStorage_fnStat (IPropertyStorage)
700 static HRESULT WINAPI IPropertyStorage_fnStat(
701 IPropertyStorage* iface,
702 STATPROPSETSTG* statpsstg)
704 PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
705 STATSTG stat;
706 HRESULT hr;
708 TRACE("%p, %p\n", iface, statpsstg);
709 if (!This || !statpsstg)
710 return E_INVALIDARG;
712 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
713 if (SUCCEEDED(hr))
715 memcpy(&statpsstg->fmtid, &This->fmtid, sizeof(statpsstg->fmtid));
716 memcpy(&statpsstg->clsid, &This->clsid, sizeof(statpsstg->clsid));
717 statpsstg->grfFlags = This->grfFlags;
718 memcpy(&statpsstg->mtime, &stat.mtime, sizeof(statpsstg->mtime));
719 memcpy(&statpsstg->ctime, &stat.ctime, sizeof(statpsstg->ctime));
720 memcpy(&statpsstg->atime, &stat.atime, sizeof(statpsstg->atime));
721 statpsstg->dwOSVersion = This->originatorOS;
723 return hr;
726 static int PropertyStorage_PropNameCompare(const void *a, const void *b,
727 void *extra)
729 PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
731 TRACE("(%s, %s)\n", debugstr_w(a), debugstr_w(b));
732 /* FIXME: this assumes property names are always Unicode, but they
733 * might be ANSI, depending on whether This->grfFlags & PROPSETFLAG_ANSI
734 * is true.
736 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
737 return strcmpW((LPCWSTR)a, (LPCWSTR)b);
738 else
739 return strcmpiW((LPCWSTR)a, (LPCWSTR)b);
742 static void PropertyStorage_PropNameDestroy(void *k, void *d, void *extra)
744 HeapFree(GetProcessHeap(), 0, k);
747 static int PropertyStorage_PropCompare(const void *a, const void *b,
748 void *extra)
750 TRACE("(%ld, %ld)\n", (PROPID)a, (PROPID)b);
751 return (PROPID)a - (PROPID)b;
754 static void PropertyStorage_PropertyDestroy(void *k, void *d, void *extra)
756 PropVariantClear((PROPVARIANT *)d);
757 HeapFree(GetProcessHeap(), 0, d);
760 /* Reads the dictionary from the memory buffer beginning at ptr. Interprets
761 * the entries according to the values of This->codePage and This->locale.
762 * FIXME: there isn't any checking whether the read property extends past the
763 * end of the buffer.
764 * FIXME: this always stores dictionary entries as Unicode, but it should store
765 * them as ANSI if (This->grfFlags & PROPSETFLAG_ANSI) is true.
767 static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This,
768 BYTE *ptr)
770 DWORD numEntries, i;
771 HRESULT hr = S_OK;
773 assert(This);
774 assert(This->name_to_propid);
775 assert(This->propid_to_name);
777 StorageUtl_ReadDWord(ptr, 0, &numEntries);
778 TRACE("Reading %ld entries:\n", numEntries);
779 ptr += sizeof(DWORD);
780 for (i = 0; SUCCEEDED(hr) && i < numEntries; i++)
782 PROPID propid;
783 DWORD cbEntry;
784 LPWSTR name = NULL;
786 StorageUtl_ReadDWord(ptr, 0, &propid);
787 ptr += sizeof(PROPID);
788 StorageUtl_ReadDWord(ptr, 0, &cbEntry);
789 ptr += sizeof(DWORD);
790 /* FIXME: if host is big-endian, this'll suck to convert */
791 TRACE("Reading entry with ID 0x%08lx, %ld bytes\n", propid, cbEntry);
792 if (This->codePage != CP_UNICODE)
794 int len = MultiByteToWideChar(This->codePage, 0, ptr, cbEntry,
795 NULL, 0);
797 if (!len)
798 hr = HRESULT_FROM_WIN32(GetLastError());
799 else
801 name = HeapAlloc(GetProcessHeap(), 0,
802 len * sizeof(WCHAR));
803 if (name)
804 MultiByteToWideChar(This->codePage, 0, ptr, cbEntry, name,
805 len);
806 else
807 hr = STG_E_INSUFFICIENTMEMORY;
810 else
812 name = HeapAlloc(GetProcessHeap(), 0, cbEntry);
813 if (name)
814 lstrcpyW(name, (LPWSTR)ptr);
815 else
816 hr = STG_E_INSUFFICIENTMEMORY;
817 /* Unicode entries are padded to DWORD boundaries */
818 if (cbEntry % sizeof(DWORD))
819 ptr += sizeof(DWORD) - (cbEntry % sizeof(DWORD));
821 if (name)
823 dictionary_insert(This->name_to_propid, name, (void *)propid);
824 dictionary_insert(This->propid_to_name, (void *)propid, name);
825 TRACE("Property %s maps to id %ld\n", debugstr_w(name), propid);
827 ptr += sizeof(DWORD) + cbEntry;
829 return hr;
832 /* FIXME: there isn't any checking whether the read property extends past the
833 * end of the buffer.
835 static HRESULT PropertyStorage_ReadProperty(PROPVARIANT *prop, const BYTE *data)
837 HRESULT hr = S_OK;
839 assert(prop);
840 assert(data);
841 StorageUtl_ReadDWord(data, 0, (DWORD *)&prop->vt);
842 data += sizeof(DWORD);
843 switch (prop->vt)
845 case VT_EMPTY:
846 case VT_NULL:
847 break;
848 case VT_I1:
849 prop->u.cVal = *(const char *)data;
850 TRACE("Read char 0x%x ('%c')\n", prop->u.cVal, prop->u.cVal);
851 break;
852 case VT_UI1:
853 prop->u.bVal = *(const UCHAR *)data;
854 TRACE("Read byte 0x%x\n", prop->u.bVal);
855 break;
856 case VT_I2:
857 StorageUtl_ReadWord(data, 0, &prop->u.iVal);
858 TRACE("Read short %d\n", prop->u.iVal);
859 break;
860 case VT_UI2:
861 StorageUtl_ReadWord(data, 0, &prop->u.uiVal);
862 TRACE("Read ushort %d\n", prop->u.uiVal);
863 break;
864 case VT_I4:
865 StorageUtl_ReadDWord(data, 0, &prop->u.lVal);
866 TRACE("Read long %ld\n", prop->u.lVal);
867 break;
868 case VT_UI4:
869 StorageUtl_ReadDWord(data, 0, &prop->u.ulVal);
870 TRACE("Read ulong %ld\n", prop->u.ulVal);
871 break;
872 case VT_LPSTR:
874 DWORD count;
876 StorageUtl_ReadDWord(data, 0, &count);
877 prop->u.pszVal = CoTaskMemAlloc(count);
878 if (prop->u.pszVal)
880 /* FIXME: if the host is big-endian, this'll suck */
881 memcpy(prop->u.pszVal, data + sizeof(DWORD), count);
882 /* FIXME: so far so good, but this may be Unicode or DBCS depending
883 * on This->codePage.
885 TRACE("Read string value %s\n", debugstr_a(prop->u.pszVal));
887 else
888 hr = STG_E_INSUFFICIENTMEMORY;
889 break;
891 case VT_LPWSTR:
893 DWORD count;
895 StorageUtl_ReadDWord(data, 0, &count);
896 prop->u.pwszVal = CoTaskMemAlloc(count * sizeof(WCHAR));
897 if (prop->u.pwszVal)
899 /* FIXME: if the host is big-endian, gotta swap every char */
900 memcpy(prop->u.pwszVal, data + sizeof(DWORD),
901 count * sizeof(WCHAR));
902 TRACE("Read string value %s\n", debugstr_w(prop->u.pwszVal));
904 else
905 hr = STG_E_INSUFFICIENTMEMORY;
906 break;
908 case VT_FILETIME:
909 /* FIXME: endianness */
910 memcpy(&prop->u.filetime, data, sizeof(FILETIME));
911 break;
912 default:
913 FIXME("unsupported type %d\n", prop->vt);
914 hr = STG_E_INVALIDPARAMETER;
916 return hr;
919 static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
920 PROPERTYSETHEADER *hdr)
922 BYTE buf[sizeof(PROPERTYSETHEADER)];
923 ULONG count = 0;
924 HRESULT hr;
926 assert(stm);
927 assert(hdr);
928 hr = IStream_Read(stm, buf, sizeof(buf), &count);
929 if (SUCCEEDED(hr))
931 if (count != sizeof(buf))
933 WARN("read %ld, expected %d\n", count, sizeof(buf));
934 hr = STG_E_INVALIDHEADER;
936 else
938 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder),
939 &hdr->wByteOrder);
940 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat),
941 &hdr->wFormat);
942 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer),
943 &hdr->dwOSVer);
944 StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid),
945 &hdr->clsid);
946 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved),
947 &hdr->reserved);
950 TRACE("returning 0x%08lx\n", hr);
951 return hr;
954 static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm,
955 FORMATIDOFFSET *fmt)
957 BYTE buf[sizeof(FORMATIDOFFSET)];
958 ULONG count = 0;
959 HRESULT hr;
961 assert(stm);
962 assert(fmt);
963 hr = IStream_Read(stm, buf, sizeof(buf), &count);
964 if (SUCCEEDED(hr))
966 if (count != sizeof(buf))
968 WARN("read %ld, expected %d\n", count, sizeof(buf));
969 hr = STG_E_INVALIDHEADER;
971 else
973 StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid),
974 &fmt->fmtid);
975 StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset),
976 &fmt->dwOffset);
979 TRACE("returning 0x%08lx\n", hr);
980 return hr;
983 static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
984 PROPERTYSECTIONHEADER *hdr)
986 BYTE buf[sizeof(PROPERTYSECTIONHEADER)];
987 ULONG count = 0;
988 HRESULT hr;
990 assert(stm);
991 assert(hdr);
992 hr = IStream_Read(stm, buf, sizeof(buf), &count);
993 if (SUCCEEDED(hr))
995 if (count != sizeof(buf))
997 WARN("read %ld, expected %d\n", count, sizeof(buf));
998 hr = STG_E_INVALIDHEADER;
1000 else
1002 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1003 cbSection), &hdr->cbSection);
1004 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1005 cProperties), &hdr->cProperties);
1008 TRACE("returning 0x%08lx\n", hr);
1009 return hr;
1012 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This)
1014 PROPERTYSETHEADER hdr;
1015 FORMATIDOFFSET fmtOffset;
1016 PROPERTYSECTIONHEADER sectionHdr;
1017 LARGE_INTEGER seek;
1018 ULONG i;
1019 STATSTG stat;
1020 HRESULT hr;
1021 BYTE *buf = NULL;
1022 ULONG count = 0;
1023 DWORD dictOffset = 0;
1025 assert(This);
1026 This->dirty = FALSE;
1027 This->highestProp = 0;
1028 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
1029 if (FAILED(hr))
1030 goto end;
1031 if (stat.cbSize.u.HighPart)
1033 WARN("stream too big\n");
1034 /* maximum size varies, but it can't be this big */
1035 hr = STG_E_INVALIDHEADER;
1036 goto end;
1038 if (stat.cbSize.u.LowPart == 0)
1040 /* empty stream is okay */
1041 hr = S_OK;
1042 goto end;
1044 else if (stat.cbSize.u.LowPart < sizeof(PROPERTYSETHEADER) +
1045 sizeof(FORMATIDOFFSET))
1047 WARN("stream too small\n");
1048 hr = STG_E_INVALIDHEADER;
1049 goto end;
1051 seek.QuadPart = 0;
1052 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1053 if (FAILED(hr))
1054 goto end;
1055 hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr);
1056 /* I've only seen reserved == 1, but the article says I shouldn't disallow
1057 * higher values.
1059 if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1)
1061 WARN("bad magic in prop set header\n");
1062 hr = STG_E_INVALIDHEADER;
1063 goto end;
1065 if (hdr.wFormat != 0 && hdr.wFormat != 1)
1067 WARN("bad format version %d\n", hdr.wFormat);
1068 hr = STG_E_INVALIDHEADER;
1069 goto end;
1071 memcpy(&This->clsid, &hdr.clsid, sizeof(This->clsid));
1072 This->originatorOS = hdr.dwOSVer;
1073 if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC)
1074 WARN("File comes from a Mac, strings will probably be screwed up\n");
1075 hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset);
1076 if (FAILED(hr))
1077 goto end;
1078 if (fmtOffset.dwOffset > stat.cbSize.u.LowPart)
1080 WARN("invalid offset %ld (stream length is %ld)\n", fmtOffset.dwOffset,
1081 stat.cbSize.u.LowPart);
1082 hr = STG_E_INVALIDHEADER;
1083 goto end;
1085 /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
1086 * follow not one, but two sections. The first is the standard properties
1087 * for the document summary information, and the second is user-defined
1088 * properties. This is the only case in which multiple sections are
1089 * allowed.
1090 * Reading the second stream isn't implemented yet.
1092 hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, &sectionHdr);
1093 if (FAILED(hr))
1094 goto end;
1095 /* The section size includes the section header, so check it */
1096 if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER))
1098 WARN("section header too small, got %ld, expected at least %d\n",
1099 sectionHdr.cbSection, sizeof(PROPERTYSECTIONHEADER));
1100 hr = STG_E_INVALIDHEADER;
1101 goto end;
1103 buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection -
1104 sizeof(PROPERTYSECTIONHEADER));
1105 if (!buf)
1107 hr = STG_E_INSUFFICIENTMEMORY;
1108 goto end;
1110 hr = IStream_Read(This->stm, buf, sectionHdr.cbSection -
1111 sizeof(PROPERTYSECTIONHEADER), &count);
1112 if (FAILED(hr))
1113 goto end;
1114 TRACE("Reading %ld properties:\n", sectionHdr.cProperties);
1115 for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
1117 PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(buf +
1118 i * sizeof(PROPERTYIDOFFSET));
1120 if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) ||
1121 idOffset->dwOffset >= sectionHdr.cbSection - sizeof(DWORD))
1122 hr = STG_E_INVALIDPOINTER;
1123 else
1125 if (idOffset->propid >= PID_FIRST_USABLE &&
1126 idOffset->propid < PID_MIN_READONLY && idOffset->propid >
1127 This->highestProp)
1128 This->highestProp = idOffset->propid;
1129 if (idOffset->propid == PID_DICTIONARY)
1131 /* Don't read the dictionary yet, its entries depend on the
1132 * code page. Just store the offset so we know to read it
1133 * later.
1135 dictOffset = idOffset->dwOffset;
1136 TRACE("Dictionary offset is %ld\n", dictOffset);
1138 else
1140 PROPVARIANT prop;
1142 if (SUCCEEDED(PropertyStorage_ReadProperty(&prop,
1143 buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER))))
1145 TRACE("Read property with ID 0x%08lx, type %d\n",
1146 idOffset->propid, prop.vt);
1147 switch(idOffset->propid)
1149 case PID_CODEPAGE:
1150 if (prop.vt == VT_I2)
1151 This->codePage = (UINT)prop.u.iVal;
1152 break;
1153 case PID_LOCALE:
1154 if (prop.vt == VT_I4)
1155 This->locale = (LCID)prop.u.lVal;
1156 break;
1157 case PID_BEHAVIOR:
1158 if (prop.vt == VT_I4 && prop.u.lVal)
1159 This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
1160 break;
1161 default:
1162 hr = PropertyStorage_StorePropWithId(This,
1163 idOffset->propid, &prop);
1169 if (!This->codePage)
1171 /* default to Unicode unless told not to, as specified here:
1172 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
1174 if (This->grfFlags & PROPSETFLAG_ANSI)
1175 This->codePage = GetACP();
1176 else
1177 This->codePage = CP_UNICODE;
1179 if (!This->locale)
1180 This->locale = LOCALE_SYSTEM_DEFAULT;
1181 TRACE("Code page is %d, locale is %ld\n", This->codePage, This->locale);
1182 if (dictOffset)
1183 hr = PropertyStorage_ReadDictionary(This,
1184 buf + dictOffset - sizeof(PROPERTYSECTIONHEADER));
1186 end:
1187 HeapFree(GetProcessHeap(), 0, buf);
1188 if (FAILED(hr))
1190 dictionary_destroy(This->name_to_propid);
1191 This->name_to_propid = NULL;
1192 dictionary_destroy(This->propid_to_name);
1193 This->propid_to_name = NULL;
1194 dictionary_destroy(This->propid_to_prop);
1195 This->propid_to_prop = NULL;
1197 return hr;
1200 static void PropertyStorage_MakeHeader(PropertyStorage_impl *This,
1201 PROPERTYSETHEADER *hdr)
1203 assert(This);
1204 assert(hdr);
1205 StorageUtl_WriteWord((BYTE *)&hdr->wByteOrder, 0,
1206 PROPSETHDR_BYTEORDER_MAGIC);
1207 /* FIXME: should be able to write format 0 property sets too, depending
1208 * on whether I have too long string names or if case-sensitivity is set.
1209 * For now always write format 1.
1211 StorageUtl_WriteWord((BYTE *)&hdr->wFormat, 0, 1);
1212 StorageUtl_WriteDWord((BYTE *)&hdr->dwOSVer, 0, This->originatorOS);
1213 StorageUtl_WriteGUID((BYTE *)&hdr->clsid, 0, &This->clsid);
1214 StorageUtl_WriteDWord((BYTE *)&hdr->reserved, 0, 1);
1217 static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl *This,
1218 FORMATIDOFFSET *fmtOffset)
1220 assert(This);
1221 assert(fmtOffset);
1222 StorageUtl_WriteGUID((BYTE *)fmtOffset, 0, &This->fmtid);
1223 StorageUtl_WriteDWord((BYTE *)fmtOffset, offsetof(FORMATIDOFFSET, dwOffset),
1224 sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET));
1227 static void PropertyStorage_MakeSectionHdr(DWORD cbSection, DWORD numProps,
1228 PROPERTYSECTIONHEADER *hdr)
1230 assert(hdr);
1231 StorageUtl_WriteDWord((BYTE *)hdr, 0, cbSection);
1232 StorageUtl_WriteDWord((BYTE *)hdr,
1233 offsetof(PROPERTYSECTIONHEADER, cProperties), numProps);
1236 static void PropertyStorage_MakePropertyIdOffset(DWORD propid, DWORD dwOffset,
1237 PROPERTYIDOFFSET *propIdOffset)
1239 assert(propIdOffset);
1240 StorageUtl_WriteDWord((BYTE *)propIdOffset, 0, propid);
1241 StorageUtl_WriteDWord((BYTE *)propIdOffset,
1242 offsetof(PROPERTYIDOFFSET, dwOffset), dwOffset);
1245 struct DictionaryClosure
1247 HRESULT hr;
1248 DWORD bytesWritten;
1251 static BOOL PropertyStorage_DictionaryWriter(const void *key,
1252 const void *value, void *extra, void *closure)
1254 PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
1255 struct DictionaryClosure *c = (struct DictionaryClosure *)closure;
1256 DWORD propid;
1257 ULONG count;
1259 assert(key);
1260 assert(This);
1261 assert(closure);
1262 StorageUtl_WriteDWord((LPBYTE)&propid, 0, (DWORD)value);
1263 c->hr = IStream_Write(This->stm, &propid, sizeof(propid), &count);
1264 if (FAILED(c->hr))
1265 goto end;
1266 c->bytesWritten += sizeof(DWORD);
1267 if (This->codePage == CP_UNICODE)
1269 DWORD keyLen, pad = 0;
1271 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0,
1272 (lstrlenW((LPWSTR)key) + 1) * sizeof(WCHAR));
1273 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1274 if (FAILED(c->hr))
1275 goto end;
1276 c->bytesWritten += sizeof(DWORD);
1277 /* FIXME: endian-convert every char (yuck) */
1278 c->hr = IStream_Write(This->stm, key, keyLen, &count);
1279 if (FAILED(c->hr))
1280 goto end;
1281 c->bytesWritten += keyLen;
1282 if (keyLen % sizeof(DWORD))
1284 c->hr = IStream_Write(This->stm, &pad,
1285 sizeof(DWORD) - keyLen % sizeof(DWORD), &count);
1286 if (FAILED(c->hr))
1287 goto end;
1288 c->bytesWritten += sizeof(DWORD) - keyLen % sizeof(DWORD);
1291 else
1293 int len = WideCharToMultiByte(This->codePage, 0, (LPWSTR)key, -1, NULL,
1294 0, NULL, NULL);
1295 LPBYTE buf = HeapAlloc(GetProcessHeap(), 0, len);
1296 DWORD dwLen;
1298 if (!buf)
1300 c->hr = STG_E_INSUFFICIENTMEMORY;
1301 goto end;
1303 /* FIXME: endian-convert multibyte chars? Ick! */
1304 WideCharToMultiByte(This->codePage, 0, (LPWSTR)key, -1, buf, len,
1305 NULL, NULL);
1306 StorageUtl_WriteDWord((LPBYTE)&dwLen, 0, len);
1307 c->hr = IStream_Write(This->stm, &dwLen, sizeof(dwLen), &count);
1308 if (FAILED(c->hr))
1310 HeapFree(GetProcessHeap(), 0, buf);
1311 goto end;
1313 c->bytesWritten += sizeof(DWORD);
1314 c->hr = IStream_Write(This->stm, buf, len, &count);
1315 if (FAILED(c->hr))
1317 HeapFree(GetProcessHeap(), 0, buf);
1318 goto end;
1320 c->bytesWritten += len;
1321 HeapFree(GetProcessHeap(), 0, buf);
1323 end:
1324 return SUCCEEDED(c->hr);
1327 #define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)
1329 /* Writes the dictionary to the stream. Assumes without checking that the
1330 * dictionary isn't empty.
1332 static HRESULT PropertyStorage_WriteDictionaryToStream(
1333 PropertyStorage_impl *This, DWORD *sectionOffset)
1335 HRESULT hr;
1336 LARGE_INTEGER seek;
1337 PROPERTYIDOFFSET propIdOffset;
1338 ULONG count;
1339 DWORD dwTemp;
1340 struct DictionaryClosure closure;
1342 assert(This);
1343 assert(sectionOffset);
1345 /* The dictionary's always the first property written, so seek to its
1346 * spot.
1348 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER);
1349 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1350 if (FAILED(hr))
1351 goto end;
1352 PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY, *sectionOffset,
1353 &propIdOffset);
1354 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1355 if (FAILED(hr))
1356 goto end;
1358 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1359 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1360 if (FAILED(hr))
1361 goto end;
1362 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0,
1363 dictionary_num_entries(This->name_to_propid));
1364 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1365 if (FAILED(hr))
1366 goto end;
1367 *sectionOffset += sizeof(dwTemp);
1369 closure.hr = S_OK;
1370 closure.bytesWritten = 0;
1371 dictionary_enumerate(This->name_to_propid, PropertyStorage_DictionaryWriter,
1372 &closure);
1373 hr = closure.hr;
1374 if (FAILED(hr))
1375 goto end;
1376 *sectionOffset += closure.bytesWritten;
1377 if (closure.bytesWritten % sizeof(DWORD))
1379 TRACE("adding %ld bytes of padding\n", sizeof(DWORD) -
1380 closure.bytesWritten % sizeof(DWORD));
1381 *sectionOffset += sizeof(DWORD) - closure.bytesWritten % sizeof(DWORD);
1384 end:
1385 return hr;
1388 static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This,
1389 DWORD propNum, DWORD propid, PROPVARIANT *var, DWORD *sectionOffset)
1391 HRESULT hr;
1392 LARGE_INTEGER seek;
1393 PROPERTYIDOFFSET propIdOffset;
1394 ULONG count;
1395 DWORD dwType, bytesWritten;
1397 assert(This);
1398 assert(var);
1399 assert(sectionOffset);
1401 TRACE("%p, %ld, 0x%08lx, (%d), (%ld)\n", This, propNum, propid, var->vt,
1402 *sectionOffset);
1404 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) +
1405 propNum * sizeof(PROPERTYIDOFFSET);
1406 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1407 if (FAILED(hr))
1408 goto end;
1409 PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset);
1410 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1411 if (FAILED(hr))
1412 goto end;
1414 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1415 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1416 if (FAILED(hr))
1417 goto end;
1418 StorageUtl_WriteDWord((LPBYTE)&dwType, 0, var->vt);
1419 hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count);
1420 if (FAILED(hr))
1421 goto end;
1422 *sectionOffset += sizeof(dwType);
1424 switch (var->vt)
1426 case VT_EMPTY:
1427 case VT_NULL:
1428 bytesWritten = 0;
1429 break;
1430 case VT_I1:
1431 case VT_UI1:
1432 hr = IStream_Write(This->stm, &var->u.cVal, sizeof(var->u.cVal),
1433 &count);
1434 bytesWritten = count;
1435 break;
1436 case VT_I2:
1437 case VT_UI2:
1439 WORD wTemp;
1441 StorageUtl_WriteWord((LPBYTE)&wTemp, 0, var->u.iVal);
1442 hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count);
1443 bytesWritten = count;
1444 break;
1446 case VT_I4:
1447 case VT_UI4:
1449 DWORD dwTemp;
1451 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, var->u.lVal);
1452 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1453 bytesWritten = count;
1454 break;
1456 case VT_LPSTR:
1458 DWORD len, dwTemp;
1460 if (This->codePage == CP_UNICODE)
1461 len = (lstrlenW(var->u.pwszVal) + 1) * sizeof(WCHAR);
1462 else
1463 len = lstrlenA(var->u.pszVal) + 1;
1464 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1465 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1466 if (FAILED(hr))
1467 goto end;
1468 hr = IStream_Write(This->stm, var->u.pszVal, len, &count);
1469 bytesWritten = count + sizeof(DWORD);
1470 break;
1472 case VT_LPWSTR:
1474 DWORD len = lstrlenW(var->u.pwszVal) + 1, dwTemp;
1476 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1477 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1478 if (FAILED(hr))
1479 goto end;
1480 hr = IStream_Write(This->stm, var->u.pwszVal, len * sizeof(WCHAR),
1481 &count);
1482 bytesWritten = count + sizeof(DWORD);
1483 break;
1485 default:
1486 FIXME("unsupported type: %d\n", var->vt);
1487 return STG_E_INVALIDPARAMETER;
1490 if (SUCCEEDED(hr))
1492 *sectionOffset += bytesWritten;
1493 if (bytesWritten % sizeof(DWORD))
1495 TRACE("adding %ld bytes of padding\n", sizeof(DWORD) -
1496 bytesWritten % sizeof(DWORD));
1497 *sectionOffset += sizeof(DWORD) - bytesWritten % sizeof(DWORD);
1501 end:
1502 return hr;
1505 struct PropertyClosure
1507 HRESULT hr;
1508 DWORD propNum;
1509 DWORD *sectionOffset;
1512 static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value,
1513 void *extra, void *closure)
1515 PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
1516 struct PropertyClosure *c = (struct PropertyClosure *)closure;
1518 assert(key);
1519 assert(value);
1520 assert(extra);
1521 assert(closure);
1522 c->hr = PropertyStorage_WritePropertyToStream(This,
1523 c->propNum++, (DWORD)key, (PROPVARIANT *)value, c->sectionOffset);
1524 return SUCCEEDED(c->hr);
1527 static HRESULT PropertyStorage_WritePropertiesToStream(
1528 PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset)
1530 struct PropertyClosure closure;
1532 assert(This);
1533 assert(sectionOffset);
1534 closure.hr = S_OK;
1535 closure.propNum = startingPropNum;
1536 closure.sectionOffset = sectionOffset;
1537 dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter,
1538 &closure);
1539 return closure.hr;
1542 static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This)
1544 HRESULT hr;
1545 ULONG count = 0;
1546 LARGE_INTEGER seek = { {0} };
1547 PROPERTYSETHEADER hdr;
1548 FORMATIDOFFSET fmtOffset;
1550 assert(This);
1551 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1552 if (FAILED(hr))
1553 goto end;
1554 PropertyStorage_MakeHeader(This, &hdr);
1555 hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count);
1556 if (FAILED(hr))
1557 goto end;
1558 if (count != sizeof(hdr))
1560 hr = STG_E_WRITEFAULT;
1561 goto end;
1564 PropertyStorage_MakeFmtIdOffset(This, &fmtOffset);
1565 hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count);
1566 if (FAILED(hr))
1567 goto end;
1568 if (count != sizeof(fmtOffset))
1570 hr = STG_E_WRITEFAULT;
1571 goto end;
1573 hr = S_OK;
1575 end:
1576 return hr;
1579 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This)
1581 PROPERTYSECTIONHEADER sectionHdr;
1582 HRESULT hr;
1583 ULONG count;
1584 LARGE_INTEGER seek;
1585 DWORD numProps, prop, sectionOffset, dwTemp;
1586 PROPVARIANT var;
1588 assert(This);
1590 PropertyStorage_WriteHeadersToStream(This);
1592 /* Count properties. Always at least one property, the code page */
1593 numProps = 1;
1594 if (dictionary_num_entries(This->name_to_propid))
1595 numProps++;
1596 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1597 numProps++;
1598 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1599 numProps++;
1600 numProps += dictionary_num_entries(This->propid_to_prop);
1602 /* Write section header with 0 bytes right now, I'll adjust it after
1603 * writing properties.
1605 PropertyStorage_MakeSectionHdr(0, numProps, &sectionHdr);
1606 seek.QuadPart = SECTIONHEADER_OFFSET;
1607 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1608 if (FAILED(hr))
1609 goto end;
1610 hr = IStream_Write(This->stm, &sectionHdr, sizeof(sectionHdr), &count);
1611 if (FAILED(hr))
1612 goto end;
1614 prop = 0;
1615 sectionOffset = sizeof(PROPERTYSECTIONHEADER) +
1616 numProps * sizeof(PROPERTYIDOFFSET);
1618 if (dictionary_num_entries(This->name_to_propid))
1620 prop++;
1621 hr = PropertyStorage_WriteDictionaryToStream(This, &sectionOffset);
1622 if (FAILED(hr))
1623 goto end;
1626 PropVariantInit(&var);
1628 var.vt = VT_I2;
1629 var.u.iVal = This->codePage;
1630 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE,
1631 &var, &sectionOffset);
1632 if (FAILED(hr))
1633 goto end;
1635 if (This->locale != LOCALE_SYSTEM_DEFAULT)
1637 var.vt = VT_I4;
1638 var.u.lVal = This->locale;
1639 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE,
1640 &var, &sectionOffset);
1641 if (FAILED(hr))
1642 goto end;
1645 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1647 var.vt = VT_I4;
1648 var.u.lVal = 1;
1649 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR,
1650 &var, &sectionOffset);
1651 if (FAILED(hr))
1652 goto end;
1655 hr = PropertyStorage_WritePropertiesToStream(This, prop, &sectionOffset);
1656 if (FAILED(hr))
1657 goto end;
1659 /* Now write the byte count of the section */
1660 seek.QuadPart = SECTIONHEADER_OFFSET;
1661 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1662 if (FAILED(hr))
1663 goto end;
1664 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, sectionOffset);
1665 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1667 end:
1668 return hr;
1671 /***********************************************************************
1672 * PropertyStorage_Construct
1674 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This)
1676 assert(This);
1677 dictionary_destroy(This->name_to_propid);
1678 This->name_to_propid = NULL;
1679 dictionary_destroy(This->propid_to_name);
1680 This->propid_to_name = NULL;
1681 dictionary_destroy(This->propid_to_prop);
1682 This->propid_to_prop = NULL;
1685 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This)
1687 HRESULT hr = S_OK;
1689 assert(This);
1690 This->name_to_propid = dictionary_create(
1691 PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy,
1692 This);
1693 if (!This->name_to_propid)
1695 hr = STG_E_INSUFFICIENTMEMORY;
1696 goto end;
1698 This->propid_to_name = dictionary_create(PropertyStorage_PropCompare,
1699 NULL, This);
1700 if (!This->propid_to_name)
1702 hr = STG_E_INSUFFICIENTMEMORY;
1703 goto end;
1705 This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
1706 PropertyStorage_PropertyDestroy, This);
1707 if (!This->propid_to_prop)
1709 hr = STG_E_INSUFFICIENTMEMORY;
1710 goto end;
1712 end:
1713 if (FAILED(hr))
1714 PropertyStorage_DestroyDictionaries(This);
1715 return hr;
1718 static HRESULT PropertyStorage_BaseConstruct(IStream *stm,
1719 REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps)
1721 HRESULT hr = S_OK;
1723 assert(pps);
1724 assert(rfmtid);
1725 *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps);
1726 if (!pps)
1727 return E_OUTOFMEMORY;
1729 (*pps)->vtbl = &IPropertyStorage_Vtbl;
1730 (*pps)->ref = 1;
1731 InitializeCriticalSection(&(*pps)->cs);
1732 (*pps)->stm = stm;
1733 memcpy(&(*pps)->fmtid, rfmtid, sizeof((*pps)->fmtid));
1734 (*pps)->grfMode = grfMode;
1736 hr = PropertyStorage_CreateDictionaries(*pps);
1738 return hr;
1741 static HRESULT PropertyStorage_ConstructFromStream(IStream *stm,
1742 REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps)
1744 PropertyStorage_impl *ps;
1745 HRESULT hr;
1747 assert(pps);
1748 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
1749 if (SUCCEEDED(hr))
1751 hr = PropertyStorage_ReadFromStream(ps);
1752 if (SUCCEEDED(hr))
1754 *pps = (IPropertyStorage *)ps;
1755 TRACE("PropertyStorage %p constructed\n", ps);
1756 hr = S_OK;
1758 else
1760 PropertyStorage_DestroyDictionaries(ps);
1761 HeapFree(GetProcessHeap(), 0, ps);
1764 return hr;
1767 static HRESULT PropertyStorage_ConstructEmpty(IStream *stm,
1768 REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
1770 PropertyStorage_impl *ps;
1771 HRESULT hr;
1773 assert(pps);
1774 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
1775 if (SUCCEEDED(hr))
1777 ps->grfFlags = grfFlags;
1778 /* default to Unicode unless told not to, as specified here:
1779 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
1781 if (ps->grfFlags & PROPSETFLAG_ANSI)
1782 ps->codePage = GetACP();
1783 else
1784 ps->codePage = CP_UNICODE;
1785 ps->locale = LOCALE_SYSTEM_DEFAULT;
1786 TRACE("Code page is %d, locale is %ld\n", ps->codePage, ps->locale);
1787 *pps = (IPropertyStorage *)ps;
1788 TRACE("PropertyStorage %p constructed\n", ps);
1789 hr = S_OK;
1791 return hr;
1795 /***********************************************************************
1796 * Implementation of IPropertySetStorage
1799 /************************************************************************
1800 * IPropertySetStorage_fnQueryInterface (IUnknown)
1802 * This method forwards to the common QueryInterface implementation
1804 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
1805 IPropertySetStorage *ppstg,
1806 REFIID riid,
1807 void** ppvObject)
1809 _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
1810 return IStorage_QueryInterface( (IStorage*)This, riid, ppvObject );
1813 /************************************************************************
1814 * IPropertySetStorage_fnAddRef (IUnknown)
1816 * This method forwards to the common AddRef implementation
1818 static ULONG WINAPI IPropertySetStorage_fnAddRef(
1819 IPropertySetStorage *ppstg)
1821 _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
1822 return IStorage_AddRef( (IStorage*)This );
1825 /************************************************************************
1826 * IPropertySetStorage_fnRelease (IUnknown)
1828 * This method forwards to the common Release implementation
1830 static ULONG WINAPI IPropertySetStorage_fnRelease(
1831 IPropertySetStorage *ppstg)
1833 _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
1834 return IStorage_Release( (IStorage*)This );
1837 /************************************************************************
1838 * IPropertySetStorage_fnCreate (IPropertySetStorage)
1840 static HRESULT WINAPI IPropertySetStorage_fnCreate(
1841 IPropertySetStorage *ppstg,
1842 REFFMTID rfmtid,
1843 const CLSID* pclsid,
1844 DWORD grfFlags,
1845 DWORD grfMode,
1846 IPropertyStorage** ppprstg)
1848 _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
1849 WCHAR name[CCH_MAX_PROPSTG_NAME];
1850 IStream *stm = NULL;
1851 HRESULT r;
1853 TRACE("%p %s %08lx %08lx %p\n", This, debugstr_guid(rfmtid), grfFlags,
1854 grfMode, ppprstg);
1856 /* be picky */
1857 if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
1859 r = STG_E_INVALIDFLAG;
1860 goto end;
1863 if (!rfmtid)
1865 r = E_INVALIDARG;
1866 goto end;
1869 /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
1870 * storage, not a stream. For now, disallow it.
1872 if (grfFlags & PROPSETFLAG_NONSIMPLE)
1874 FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
1875 r = STG_E_INVALIDFLAG;
1876 goto end;
1879 r = FmtIdToPropStgName(rfmtid, name);
1880 if (FAILED(r))
1881 goto end;
1883 r = IStorage_CreateStream( (IStorage*)This, name, grfMode, 0, 0, &stm );
1884 if (FAILED(r))
1885 goto end;
1887 r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg);
1889 end:
1890 TRACE("returning 0x%08lx\n", r);
1891 return r;
1894 /************************************************************************
1895 * IPropertySetStorage_fnOpen (IPropertySetStorage)
1897 static HRESULT WINAPI IPropertySetStorage_fnOpen(
1898 IPropertySetStorage *ppstg,
1899 REFFMTID rfmtid,
1900 DWORD grfMode,
1901 IPropertyStorage** ppprstg)
1903 _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
1904 IStream *stm = NULL;
1905 WCHAR name[CCH_MAX_PROPSTG_NAME];
1906 HRESULT r;
1908 TRACE("%p %s %08lx %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
1910 /* be picky */
1911 if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
1912 grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
1914 r = STG_E_INVALIDFLAG;
1915 goto end;
1918 if (!rfmtid)
1920 r = E_INVALIDARG;
1921 goto end;
1924 r = FmtIdToPropStgName(rfmtid, name);
1925 if (FAILED(r))
1926 goto end;
1928 r = IStorage_OpenStream((IStorage*) This, name, 0, grfMode, 0, &stm );
1929 if (FAILED(r))
1930 goto end;
1932 r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg);
1934 end:
1935 TRACE("returning 0x%08lx\n", r);
1936 return r;
1939 /************************************************************************
1940 * IPropertySetStorage_fnDelete (IPropertySetStorage)
1942 static HRESULT WINAPI IPropertySetStorage_fnDelete(
1943 IPropertySetStorage *ppstg,
1944 REFFMTID rfmtid)
1946 _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
1947 IStorage *stg = NULL;
1948 WCHAR name[CCH_MAX_PROPSTG_NAME];
1949 HRESULT r;
1951 TRACE("%p %s\n", This, debugstr_guid(rfmtid));
1953 if (!rfmtid)
1954 return E_INVALIDARG;
1956 r = FmtIdToPropStgName(rfmtid, name);
1957 if (FAILED(r))
1958 return r;
1960 stg = (IStorage*) This;
1961 return IStorage_DestroyElement(stg, name);
1964 /************************************************************************
1965 * IPropertySetStorage_fnEnum (IPropertySetStorage)
1967 static HRESULT WINAPI IPropertySetStorage_fnEnum(
1968 IPropertySetStorage *ppstg,
1969 IEnumSTATPROPSETSTG** ppenum)
1971 _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
1972 FIXME("%p\n", This);
1973 return E_NOTIMPL;
1977 /***********************************************************************
1978 * vtables
1980 IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
1982 IPropertySetStorage_fnQueryInterface,
1983 IPropertySetStorage_fnAddRef,
1984 IPropertySetStorage_fnRelease,
1985 IPropertySetStorage_fnCreate,
1986 IPropertySetStorage_fnOpen,
1987 IPropertySetStorage_fnDelete,
1988 IPropertySetStorage_fnEnum
1991 static IPropertyStorageVtbl IPropertyStorage_Vtbl =
1993 IPropertyStorage_fnQueryInterface,
1994 IPropertyStorage_fnAddRef,
1995 IPropertyStorage_fnRelease,
1996 IPropertyStorage_fnReadMultiple,
1997 IPropertyStorage_fnWriteMultiple,
1998 IPropertyStorage_fnDeleteMultiple,
1999 IPropertyStorage_fnReadPropertyNames,
2000 IPropertyStorage_fnWritePropertyNames,
2001 IPropertyStorage_fnDeletePropertyNames,
2002 IPropertyStorage_fnCommit,
2003 IPropertyStorage_fnRevert,
2004 IPropertyStorage_fnEnum,
2005 IPropertyStorage_fnSetTimes,
2006 IPropertyStorage_fnSetClass,
2007 IPropertyStorage_fnStat,
2010 /***********************************************************************
2011 * Format ID <-> name conversion
2013 static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
2014 'I','n','f','o','r','m','a','t','i','o','n',0 };
2015 static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t',
2016 'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
2018 #define BITS_PER_BYTE 8
2019 #define CHARMASK 0x1f
2020 #define BITS_IN_CHARMASK 5
2021 #define NUM_ALPHA_CHARS 26
2023 /***********************************************************************
2024 * FmtIdToPropStgName [ole32.@]
2025 * Returns the storage name of the format ID rfmtid.
2026 * PARAMS
2027 * rfmtid [I] Format ID for which to return a storage name
2028 * str [O] Storage name associated with rfmtid.
2030 * RETURNS
2031 * E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise.
2033 * NOTES
2034 * str must be at least CCH_MAX_PROPSTG_NAME characters in length.
2035 * Based on the algorithm described here:
2036 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2038 HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str)
2040 static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
2042 TRACE("%s, %p\n", debugstr_guid(rfmtid), str);
2044 if (!rfmtid) return E_INVALIDARG;
2045 if (!str) return E_INVALIDARG;
2047 if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
2048 lstrcpyW(str, szSummaryInfo);
2049 else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
2050 lstrcpyW(str, szDocSummaryInfo);
2051 else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid))
2052 lstrcpyW(str, szDocSummaryInfo);
2053 else
2055 BYTE *fmtptr;
2056 WCHAR *pstr = str;
2057 ULONG bitsRemaining = BITS_PER_BYTE;
2059 *pstr++ = 5;
2060 for (fmtptr = (BYTE *)rfmtid; fmtptr < (BYTE *)rfmtid + sizeof(FMTID); )
2062 ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
2064 if (bitsRemaining >= BITS_IN_CHARMASK)
2066 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
2067 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
2068 *pstr <= 'z')
2069 *pstr += 'A' - 'a';
2070 pstr++;
2071 bitsRemaining -= BITS_IN_CHARMASK;
2072 if (bitsRemaining == 0)
2074 fmtptr++;
2075 bitsRemaining = BITS_PER_BYTE;
2078 else
2080 if (++fmtptr < (BYTE *)rfmtid + sizeof(FMTID))
2081 i |= *fmtptr << bitsRemaining;
2082 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
2083 bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK;
2086 *pstr = 0;
2088 TRACE("returning %s\n", debugstr_w(str));
2089 return S_OK;
2092 /***********************************************************************
2093 * PropStgNameToFmtId [ole32.@]
2094 * Returns the format ID corresponding to the given name.
2095 * PARAMS
2096 * str [I] Storage name to convert to a format ID.
2097 * rfmtid [O] Format ID corresponding to str.
2099 * RETURNS
2100 * E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to
2101 * a format ID, S_OK otherwise.
2103 * NOTES
2104 * Based on the algorithm described here:
2105 * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
2107 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid)
2109 HRESULT hr = E_INVALIDARG;
2111 TRACE("%s, %p\n", debugstr_w(str), rfmtid);
2113 if (!rfmtid) return E_INVALIDARG;
2114 if (!str) return E_INVALIDARG;
2116 if (!lstrcmpiW(str, szDocSummaryInfo))
2118 memcpy(rfmtid, &FMTID_DocSummaryInformation, sizeof(*rfmtid));
2119 hr = S_OK;
2121 else if (!lstrcmpiW(str, szSummaryInfo))
2123 memcpy(rfmtid, &FMTID_SummaryInformation, sizeof(*rfmtid));
2124 hr = S_OK;
2126 else
2128 ULONG bits;
2129 BYTE *fmtptr = (BYTE *)rfmtid - 1;
2130 const WCHAR *pstr = str;
2132 memset(rfmtid, 0, sizeof(*rfmtid));
2133 for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE;
2134 bits += BITS_IN_CHARMASK)
2136 ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored;
2137 WCHAR wc;
2139 if (bitsUsed == 0)
2140 fmtptr++;
2141 wc = *++pstr - 'A';
2142 if (wc > NUM_ALPHA_CHARS)
2144 wc += 'A' - 'a';
2145 if (wc > NUM_ALPHA_CHARS)
2147 wc += 'a' - '0' + NUM_ALPHA_CHARS;
2148 if (wc > CHARMASK)
2150 WARN("invalid character (%d)\n", *pstr);
2151 goto end;
2155 *fmtptr |= wc << bitsUsed;
2156 bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK);
2157 if (bitsStored < BITS_IN_CHARMASK)
2159 wc >>= BITS_PER_BYTE - bitsUsed;
2160 if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE)
2162 if (wc != 0)
2164 WARN("extra bits\n");
2165 goto end;
2167 break;
2169 fmtptr++;
2170 *fmtptr |= (BYTE)wc;
2173 hr = S_OK;
2175 end:
2176 return hr;