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