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