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