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 DWORD vt; 1062 1063 assert(prop); 1064 assert(data); 1065 StorageUtl_ReadDWord(data, 0, &vt); 1066 data += sizeof(DWORD); 1067 prop->vt = vt; 1068 switch (prop->vt) 1069 { 1070 case VT_EMPTY: 1071 case VT_NULL: 1072 break; 1073 case VT_I1: 1074 prop->u.cVal = *(const char *)data; 1075 TRACE("Read char 0x%x ('%c')\n", prop->u.cVal, prop->u.cVal); 1076 break; 1077 case VT_UI1: 1078 prop->u.bVal = *data; 1079 TRACE("Read byte 0x%x\n", prop->u.bVal); 1080 break; 1081 case VT_BOOL: 1082 StorageUtl_ReadWord(data, 0, (WORD*)&prop->u.boolVal); 1083 TRACE("Read bool %d\n", prop->u.boolVal); 1084 break; 1085 case VT_I2: 1086 StorageUtl_ReadWord(data, 0, (WORD*)&prop->u.iVal); 1087 TRACE("Read short %d\n", prop->u.iVal); 1088 break; 1089 case VT_UI2: 1090 StorageUtl_ReadWord(data, 0, &prop->u.uiVal); 1091 TRACE("Read ushort %d\n", prop->u.uiVal); 1092 break; 1093 case VT_INT: 1094 case VT_I4: 1095 StorageUtl_ReadDWord(data, 0, (DWORD*)&prop->u.lVal); 1096 TRACE("Read long %d\n", prop->u.lVal); 1097 break; 1098 case VT_UINT: 1099 case VT_UI4: 1100 StorageUtl_ReadDWord(data, 0, &prop->u.ulVal); 1101 TRACE("Read ulong %d\n", prop->u.ulVal); 1102 break; 1103 case VT_I8: 1104 StorageUtl_ReadULargeInteger(data, 0, (ULARGE_INTEGER *)&prop->u.hVal); 1105 TRACE("Read long long %s\n", wine_dbgstr_longlong(prop->u.hVal.QuadPart)); 1106 break; 1107 case VT_UI8: 1108 StorageUtl_ReadULargeInteger(data, 0, &prop->u.uhVal); 1109 TRACE("Read ulong long %s\n", wine_dbgstr_longlong(prop->u.uhVal.QuadPart)); 1110 break; 1111 case VT_R8: 1112 memcpy(&prop->u.dblVal, data, sizeof(double)); 1113 TRACE("Read double %f\n", prop->u.dblVal); 1114 break; 1115 case VT_LPSTR: 1116 { 1117 DWORD count; 1118 1119 StorageUtl_ReadDWord(data, 0, &count); 1120 if (codepage == CP_UNICODE && count % 2) 1121 { 1122 WARN("Unicode string has odd number of bytes\n"); 1123 hr = STG_E_INVALIDHEADER; 1124 } 1125 else 1126 { 1127 prop->u.pszVal = allocate(allocate_data, count); 1128 if (prop->u.pszVal) 1129 { 1130 memcpy(prop->u.pszVal, data + sizeof(DWORD), count); 1131 /* This is stored in the code page specified in codepage. 1132 * Don't convert it, the caller will just store it as-is. 1133 */ 1134 if (codepage == CP_UNICODE) 1135 { 1136 /* Make sure it's NULL-terminated */ 1137 prop->u.pszVal[count / sizeof(WCHAR) - 1] = '\0'; 1138 TRACE("Read string value %s\n", 1139 debugstr_w(prop->u.pwszVal)); 1140 } 1141 else 1142 { 1143 /* Make sure it's NULL-terminated */ 1144 prop->u.pszVal[count - 1] = '\0'; 1145 TRACE("Read string value %s\n", debugstr_a(prop->u.pszVal)); 1146 } 1147 } 1148 else 1149 hr = STG_E_INSUFFICIENTMEMORY; 1150 } 1151 break; 1152 } 1153 case VT_BSTR: 1154 { 1155 DWORD count, wcount; 1156 1157 StorageUtl_ReadDWord(data, 0, &count); 1158 if (codepage == CP_UNICODE && count % 2) 1159 { 1160 WARN("Unicode string has odd number of bytes\n"); 1161 hr = STG_E_INVALIDHEADER; 1162 } 1163 else 1164 { 1165 if (codepage == CP_UNICODE) 1166 wcount = count / 2; 1167 else 1168 wcount = MultiByteToWideChar(codepage, 0, (LPCSTR)(data + sizeof(DWORD)), count, NULL, 0); 1169 1170 prop->u.bstrVal = SysAllocStringLen(NULL, wcount); /* FIXME: use allocator? */ 1171 1172 if (prop->u.bstrVal) 1173 { 1174 if (codepage == CP_UNICODE) 1175 memcpy(prop->u.bstrVal, data + sizeof(DWORD), count); 1176 else 1177 MultiByteToWideChar(codepage, 0, (LPCSTR)(data + sizeof(DWORD)), count, prop->u.bstrVal, wcount); 1178 1179 prop->u.bstrVal[wcount - 1] = '\0'; 1180 TRACE("Read string value %s\n", debugstr_w(prop->u.bstrVal)); 1181 } 1182 else 1183 hr = STG_E_INSUFFICIENTMEMORY; 1184 } 1185 break; 1186 } 1187 case VT_BLOB: 1188 { 1189 DWORD count; 1190 1191 StorageUtl_ReadDWord(data, 0, &count); 1192 prop->u.blob.cbSize = count; 1193 prop->u.blob.pBlobData = allocate(allocate_data, count); 1194 if (prop->u.blob.pBlobData) 1195 { 1196 memcpy(prop->u.blob.pBlobData, data + sizeof(DWORD), count); 1197 TRACE("Read blob value of size %d\n", count); 1198 } 1199 else 1200 hr = STG_E_INSUFFICIENTMEMORY; 1201 break; 1202 } 1203 case VT_LPWSTR: 1204 { 1205 DWORD count; 1206 1207 StorageUtl_ReadDWord(data, 0, &count); 1208 prop->u.pwszVal = allocate(allocate_data, count * sizeof(WCHAR)); 1209 if (prop->u.pwszVal) 1210 { 1211 memcpy(prop->u.pwszVal, data + sizeof(DWORD), 1212 count * sizeof(WCHAR)); 1213 /* make sure string is NULL-terminated */ 1214 prop->u.pwszVal[count - 1] = '\0'; 1215 PropertyStorage_ByteSwapString(prop->u.pwszVal, count); 1216 TRACE("Read string value %s\n", debugstr_w(prop->u.pwszVal)); 1217 } 1218 else 1219 hr = STG_E_INSUFFICIENTMEMORY; 1220 break; 1221 } 1222 case VT_FILETIME: 1223 StorageUtl_ReadULargeInteger(data, 0, 1224 (ULARGE_INTEGER *)&prop->u.filetime); 1225 break; 1226 case VT_CF: 1227 { 1228 DWORD len = 0, tag = 0; 1229 1230 StorageUtl_ReadDWord(data, 0, &len); 1231 StorageUtl_ReadDWord(data, 4, &tag); 1232 if (len > 8) 1233 { 1234 len -= 8; 1235 prop->u.pclipdata = allocate(allocate_data, sizeof (CLIPDATA)); 1236 prop->u.pclipdata->cbSize = len; 1237 prop->u.pclipdata->ulClipFmt = tag; 1238 prop->u.pclipdata->pClipData = allocate(allocate_data, len - sizeof(prop->u.pclipdata->ulClipFmt)); 1239 memcpy(prop->u.pclipdata->pClipData, data+8, len - sizeof(prop->u.pclipdata->ulClipFmt)); 1240 } 1241 else 1242 hr = STG_E_INVALIDPARAMETER; 1243 } 1244 break; 1245 default: 1246 FIXME("unsupported type %d\n", prop->vt); 1247 hr = STG_E_INVALIDPARAMETER; 1248 } 1249 return hr; 1250 } 1251 1252 static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm, 1253 PROPERTYSETHEADER *hdr) 1254 { 1255 BYTE buf[sizeof(PROPERTYSETHEADER)]; 1256 ULONG count = 0; 1257 HRESULT hr; 1258 1259 assert(stm); 1260 assert(hdr); 1261 hr = IStream_Read(stm, buf, sizeof(buf), &count); 1262 if (SUCCEEDED(hr)) 1263 { 1264 if (count != sizeof(buf)) 1265 { 1266 WARN("read only %d\n", count); 1267 hr = STG_E_INVALIDHEADER; 1268 } 1269 else 1270 { 1271 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder), 1272 &hdr->wByteOrder); 1273 StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat), 1274 &hdr->wFormat); 1275 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer), 1276 &hdr->dwOSVer); 1277 StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid), 1278 &hdr->clsid); 1279 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved), 1280 &hdr->reserved); 1281 } 1282 } 1283 TRACE("returning 0x%08x\n", hr); 1284 return hr; 1285 } 1286 1287 static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm, 1288 FORMATIDOFFSET *fmt) 1289 { 1290 BYTE buf[sizeof(FORMATIDOFFSET)]; 1291 ULONG count = 0; 1292 HRESULT hr; 1293 1294 assert(stm); 1295 assert(fmt); 1296 hr = IStream_Read(stm, buf, sizeof(buf), &count); 1297 if (SUCCEEDED(hr)) 1298 { 1299 if (count != sizeof(buf)) 1300 { 1301 WARN("read only %d\n", count); 1302 hr = STG_E_INVALIDHEADER; 1303 } 1304 else 1305 { 1306 StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid), 1307 &fmt->fmtid); 1308 StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset), 1309 &fmt->dwOffset); 1310 } 1311 } 1312 TRACE("returning 0x%08x\n", hr); 1313 return hr; 1314 } 1315 1316 static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm, 1317 PROPERTYSECTIONHEADER *hdr) 1318 { 1319 BYTE buf[sizeof(PROPERTYSECTIONHEADER)]; 1320 ULONG count = 0; 1321 HRESULT hr; 1322 1323 assert(stm); 1324 assert(hdr); 1325 hr = IStream_Read(stm, buf, sizeof(buf), &count); 1326 if (SUCCEEDED(hr)) 1327 { 1328 if (count != sizeof(buf)) 1329 { 1330 WARN("read only %d\n", count); 1331 hr = STG_E_INVALIDHEADER; 1332 } 1333 else 1334 { 1335 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER, 1336 cbSection), &hdr->cbSection); 1337 StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER, 1338 cProperties), &hdr->cProperties); 1339 } 1340 } 1341 TRACE("returning 0x%08x\n", hr); 1342 return hr; 1343 } 1344 1345 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This) 1346 { 1347 PROPERTYSETHEADER hdr; 1348 FORMATIDOFFSET fmtOffset; 1349 PROPERTYSECTIONHEADER sectionHdr; 1350 LARGE_INTEGER seek; 1351 ULONG i; 1352 STATSTG stat; 1353 HRESULT hr; 1354 BYTE *buf = NULL; 1355 ULONG count = 0; 1356 DWORD dictOffset = 0; 1357 1358 This->dirty = FALSE; 1359 This->highestProp = 0; 1360 hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME); 1361 if (FAILED(hr)) 1362 goto end; 1363 if (stat.cbSize.u.HighPart) 1364 { 1365 WARN("stream too big\n"); 1366 /* maximum size varies, but it can't be this big */ 1367 hr = STG_E_INVALIDHEADER; 1368 goto end; 1369 } 1370 if (stat.cbSize.u.LowPart == 0) 1371 { 1372 /* empty stream is okay */ 1373 hr = S_OK; 1374 goto end; 1375 } 1376 else if (stat.cbSize.u.LowPart < sizeof(PROPERTYSETHEADER) + 1377 sizeof(FORMATIDOFFSET)) 1378 { 1379 WARN("stream too small\n"); 1380 hr = STG_E_INVALIDHEADER; 1381 goto end; 1382 } 1383 seek.QuadPart = 0; 1384 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); 1385 if (FAILED(hr)) 1386 goto end; 1387 hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr); 1388 /* I've only seen reserved == 1, but the article says I shouldn't disallow 1389 * higher values. 1390 */ 1391 if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1) 1392 { 1393 WARN("bad magic in prop set header\n"); 1394 hr = STG_E_INVALIDHEADER; 1395 goto end; 1396 } 1397 if (hdr.wFormat != 0 && hdr.wFormat != 1) 1398 { 1399 WARN("bad format version %d\n", hdr.wFormat); 1400 hr = STG_E_INVALIDHEADER; 1401 goto end; 1402 } 1403 This->format = hdr.wFormat; 1404 This->clsid = hdr.clsid; 1405 This->originatorOS = hdr.dwOSVer; 1406 if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC) 1407 WARN("File comes from a Mac, strings will probably be screwed up\n"); 1408 hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset); 1409 if (FAILED(hr)) 1410 goto end; 1411 if (fmtOffset.dwOffset > stat.cbSize.u.LowPart) 1412 { 1413 WARN("invalid offset %d (stream length is %d)\n", fmtOffset.dwOffset, 1414 stat.cbSize.u.LowPart); 1415 hr = STG_E_INVALIDHEADER; 1416 goto end; 1417 } 1418 /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there 1419 * follows not one, but two sections. The first contains the standard properties 1420 * for the document summary information, and the second consists of user-defined 1421 * properties. This is the only case in which multiple sections are 1422 * allowed. 1423 * Reading the second stream isn't implemented yet. 1424 */ 1425 hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, §ionHdr); 1426 if (FAILED(hr)) 1427 goto end; 1428 /* The section size includes the section header, so check it */ 1429 if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER)) 1430 { 1431 WARN("section header too small, got %d\n", sectionHdr.cbSection); 1432 hr = STG_E_INVALIDHEADER; 1433 goto end; 1434 } 1435 buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection - 1436 sizeof(PROPERTYSECTIONHEADER)); 1437 if (!buf) 1438 { 1439 hr = STG_E_INSUFFICIENTMEMORY; 1440 goto end; 1441 } 1442 hr = IStream_Read(This->stm, buf, sectionHdr.cbSection - 1443 sizeof(PROPERTYSECTIONHEADER), &count); 1444 if (FAILED(hr)) 1445 goto end; 1446 TRACE("Reading %d properties:\n", sectionHdr.cProperties); 1447 for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++) 1448 { 1449 PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(buf + 1450 i * sizeof(PROPERTYIDOFFSET)); 1451 1452 if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) || 1453 idOffset->dwOffset > sectionHdr.cbSection - sizeof(DWORD)) 1454 hr = STG_E_INVALIDPOINTER; 1455 else 1456 { 1457 if (idOffset->propid >= PID_FIRST_USABLE && 1458 idOffset->propid < PID_MIN_READONLY && idOffset->propid > 1459 This->highestProp) 1460 This->highestProp = idOffset->propid; 1461 if (idOffset->propid == PID_DICTIONARY) 1462 { 1463 /* Don't read the dictionary yet, its entries depend on the 1464 * code page. Just store the offset so we know to read it 1465 * later. 1466 */ 1467 dictOffset = idOffset->dwOffset; 1468 TRACE("Dictionary offset is %d\n", dictOffset); 1469 } 1470 else 1471 { 1472 PROPVARIANT prop; 1473 1474 PropVariantInit(&prop); 1475 if (SUCCEEDED(PropertyStorage_ReadProperty(&prop, 1476 buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER), 1477 This->codePage, Allocate_CoTaskMemAlloc, NULL))) 1478 { 1479 TRACE("Read property with ID 0x%08x, type %d\n", 1480 idOffset->propid, prop.vt); 1481 switch(idOffset->propid) 1482 { 1483 case PID_CODEPAGE: 1484 if (prop.vt == VT_I2) 1485 This->codePage = (UINT)prop.u.iVal; 1486 break; 1487 case PID_LOCALE: 1488 if (prop.vt == VT_I4) 1489 This->locale = (LCID)prop.u.lVal; 1490 break; 1491 case PID_BEHAVIOR: 1492 if (prop.vt == VT_I4 && prop.u.lVal) 1493 This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE; 1494 /* The format should already be 1, but just in case */ 1495 This->format = 1; 1496 break; 1497 default: 1498 hr = PropertyStorage_StorePropWithId(This, 1499 idOffset->propid, &prop, This->codePage); 1500 } 1501 } 1502 PropVariantClear(&prop); 1503 } 1504 } 1505 } 1506 if (!This->codePage) 1507 { 1508 /* default to Unicode unless told not to, as specified on msdn */ 1509 if (This->grfFlags & PROPSETFLAG_ANSI) 1510 This->codePage = GetACP(); 1511 else 1512 This->codePage = CP_UNICODE; 1513 } 1514 if (!This->locale) 1515 This->locale = LOCALE_SYSTEM_DEFAULT; 1516 TRACE("Code page is %d, locale is %d\n", This->codePage, This->locale); 1517 if (dictOffset) 1518 hr = PropertyStorage_ReadDictionary(This, 1519 buf + dictOffset - sizeof(PROPERTYSECTIONHEADER)); 1520 1521 end: 1522 HeapFree(GetProcessHeap(), 0, buf); 1523 if (FAILED(hr)) 1524 { 1525 dictionary_destroy(This->name_to_propid); 1526 This->name_to_propid = NULL; 1527 dictionary_destroy(This->propid_to_name); 1528 This->propid_to_name = NULL; 1529 dictionary_destroy(This->propid_to_prop); 1530 This->propid_to_prop = NULL; 1531 } 1532 return hr; 1533 } 1534 1535 static void PropertyStorage_MakeHeader(PropertyStorage_impl *This, 1536 PROPERTYSETHEADER *hdr) 1537 { 1538 assert(hdr); 1539 StorageUtl_WriteWord((BYTE *)&hdr->wByteOrder, 0, 1540 PROPSETHDR_BYTEORDER_MAGIC); 1541 StorageUtl_WriteWord((BYTE *)&hdr->wFormat, 0, This->format); 1542 StorageUtl_WriteDWord((BYTE *)&hdr->dwOSVer, 0, This->originatorOS); 1543 StorageUtl_WriteGUID((BYTE *)&hdr->clsid, 0, &This->clsid); 1544 StorageUtl_WriteDWord((BYTE *)&hdr->reserved, 0, 1); 1545 } 1546 1547 static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl *This, 1548 FORMATIDOFFSET *fmtOffset) 1549 { 1550 assert(fmtOffset); 1551 StorageUtl_WriteGUID((BYTE *)fmtOffset, 0, &This->fmtid); 1552 StorageUtl_WriteDWord((BYTE *)fmtOffset, offsetof(FORMATIDOFFSET, dwOffset), 1553 sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)); 1554 } 1555 1556 static void PropertyStorage_MakeSectionHdr(DWORD cbSection, DWORD numProps, 1557 PROPERTYSECTIONHEADER *hdr) 1558 { 1559 assert(hdr); 1560 StorageUtl_WriteDWord((BYTE *)hdr, 0, cbSection); 1561 StorageUtl_WriteDWord((BYTE *)hdr, 1562 offsetof(PROPERTYSECTIONHEADER, cProperties), numProps); 1563 } 1564 1565 static void PropertyStorage_MakePropertyIdOffset(DWORD propid, DWORD dwOffset, 1566 PROPERTYIDOFFSET *propIdOffset) 1567 { 1568 assert(propIdOffset); 1569 StorageUtl_WriteDWord((BYTE *)propIdOffset, 0, propid); 1570 StorageUtl_WriteDWord((BYTE *)propIdOffset, 1571 offsetof(PROPERTYIDOFFSET, dwOffset), dwOffset); 1572 } 1573 1574 static inline HRESULT PropertStorage_WriteWStringToStream(IStream *stm, 1575 LPCWSTR str, DWORD len, DWORD *written) 1576 { 1577 #ifdef WORDS_BIGENDIAN 1578 WCHAR *leStr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 1579 HRESULT hr; 1580 1581 if (!leStr) 1582 return E_OUTOFMEMORY; 1583 memcpy(leStr, str, len * sizeof(WCHAR)); 1584 PropertyStorage_ByteSwapString(leStr, len); 1585 hr = IStream_Write(stm, leStr, len, written); 1586 HeapFree(GetProcessHeap(), 0, leStr); 1587 return hr; 1588 #else 1589 return IStream_Write(stm, str, len, written); 1590 #endif 1591 } 1592 1593 struct DictionaryClosure 1594 { 1595 HRESULT hr; 1596 DWORD bytesWritten; 1597 }; 1598 1599 static BOOL PropertyStorage_DictionaryWriter(const void *key, 1600 const void *value, void *extra, void *closure) 1601 { 1602 PropertyStorage_impl *This = extra; 1603 struct DictionaryClosure *c = closure; 1604 DWORD propid; 1605 ULONG count; 1606 1607 assert(key); 1608 assert(closure); 1609 StorageUtl_WriteDWord((LPBYTE)&propid, 0, PtrToUlong(value)); 1610 c->hr = IStream_Write(This->stm, &propid, sizeof(propid), &count); 1611 if (FAILED(c->hr)) 1612 goto end; 1613 c->bytesWritten += sizeof(DWORD); 1614 if (This->codePage == CP_UNICODE) 1615 { 1616 DWORD keyLen, pad = 0; 1617 1618 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0, 1619 (lstrlenW((LPCWSTR)key) + 1) * sizeof(WCHAR)); 1620 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count); 1621 if (FAILED(c->hr)) 1622 goto end; 1623 c->bytesWritten += sizeof(DWORD); 1624 c->hr = PropertStorage_WriteWStringToStream(This->stm, key, keyLen, 1625 &count); 1626 if (FAILED(c->hr)) 1627 goto end; 1628 c->bytesWritten += keyLen * sizeof(WCHAR); 1629 if (keyLen % sizeof(DWORD)) 1630 { 1631 c->hr = IStream_Write(This->stm, &pad, 1632 sizeof(DWORD) - keyLen % sizeof(DWORD), &count); 1633 if (FAILED(c->hr)) 1634 goto end; 1635 c->bytesWritten += sizeof(DWORD) - keyLen % sizeof(DWORD); 1636 } 1637 } 1638 else 1639 { 1640 DWORD keyLen; 1641 1642 StorageUtl_WriteDWord((LPBYTE)&keyLen, 0, strlen((LPCSTR)key) + 1); 1643 c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count); 1644 if (FAILED(c->hr)) 1645 goto end; 1646 c->bytesWritten += sizeof(DWORD); 1647 c->hr = IStream_Write(This->stm, key, keyLen, &count); 1648 if (FAILED(c->hr)) 1649 goto end; 1650 c->bytesWritten += keyLen; 1651 } 1652 end: 1653 return SUCCEEDED(c->hr); 1654 } 1655 1656 #define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET) 1657 1658 /* Writes the dictionary to the stream. Assumes without checking that the 1659 * dictionary isn't empty. 1660 */ 1661 static HRESULT PropertyStorage_WriteDictionaryToStream( 1662 PropertyStorage_impl *This, DWORD *sectionOffset) 1663 { 1664 HRESULT hr; 1665 LARGE_INTEGER seek; 1666 PROPERTYIDOFFSET propIdOffset; 1667 ULONG count; 1668 DWORD dwTemp; 1669 struct DictionaryClosure closure; 1670 1671 assert(sectionOffset); 1672 1673 /* The dictionary's always the first property written, so seek to its 1674 * spot. 1675 */ 1676 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER); 1677 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); 1678 if (FAILED(hr)) 1679 goto end; 1680 PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY, *sectionOffset, 1681 &propIdOffset); 1682 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count); 1683 if (FAILED(hr)) 1684 goto end; 1685 1686 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset; 1687 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); 1688 if (FAILED(hr)) 1689 goto end; 1690 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, 1691 dictionary_num_entries(This->name_to_propid)); 1692 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count); 1693 if (FAILED(hr)) 1694 goto end; 1695 *sectionOffset += sizeof(dwTemp); 1696 1697 closure.hr = S_OK; 1698 closure.bytesWritten = 0; 1699 dictionary_enumerate(This->name_to_propid, PropertyStorage_DictionaryWriter, 1700 &closure); 1701 hr = closure.hr; 1702 if (FAILED(hr)) 1703 goto end; 1704 *sectionOffset += closure.bytesWritten; 1705 if (closure.bytesWritten % sizeof(DWORD)) 1706 { 1707 DWORD padding = sizeof(DWORD) - closure.bytesWritten % sizeof(DWORD); 1708 TRACE("adding %d bytes of padding\n", padding); 1709 *sectionOffset += padding; 1710 } 1711 1712 end: 1713 return hr; 1714 } 1715 1716 static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This, 1717 DWORD propNum, DWORD propid, const PROPVARIANT *var, DWORD *sectionOffset) 1718 { 1719 HRESULT hr; 1720 LARGE_INTEGER seek; 1721 PROPERTYIDOFFSET propIdOffset; 1722 ULONG count; 1723 DWORD dwType, bytesWritten; 1724 1725 assert(var); 1726 assert(sectionOffset); 1727 1728 TRACE("%p, %d, 0x%08x, (%d), (%d)\n", This, propNum, propid, var->vt, 1729 *sectionOffset); 1730 1731 seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) + 1732 propNum * sizeof(PROPERTYIDOFFSET); 1733 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); 1734 if (FAILED(hr)) 1735 goto end; 1736 PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset); 1737 hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count); 1738 if (FAILED(hr)) 1739 goto end; 1740 1741 seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset; 1742 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); 1743 if (FAILED(hr)) 1744 goto end; 1745 StorageUtl_WriteDWord((LPBYTE)&dwType, 0, var->vt); 1746 hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count); 1747 if (FAILED(hr)) 1748 goto end; 1749 *sectionOffset += sizeof(dwType); 1750 1751 switch (var->vt) 1752 { 1753 case VT_EMPTY: 1754 case VT_NULL: 1755 bytesWritten = 0; 1756 break; 1757 case VT_I1: 1758 case VT_UI1: 1759 hr = IStream_Write(This->stm, &var->u.cVal, sizeof(var->u.cVal), 1760 &count); 1761 bytesWritten = count; 1762 break; 1763 case VT_I2: 1764 case VT_UI2: 1765 { 1766 WORD wTemp; 1767 1768 StorageUtl_WriteWord((LPBYTE)&wTemp, 0, var->u.iVal); 1769 hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count); 1770 bytesWritten = count; 1771 break; 1772 } 1773 case VT_I4: 1774 case VT_UI4: 1775 { 1776 DWORD dwTemp; 1777 1778 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, var->u.lVal); 1779 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count); 1780 bytesWritten = count; 1781 break; 1782 } 1783 case VT_LPSTR: 1784 { 1785 DWORD len, dwTemp; 1786 1787 if (This->codePage == CP_UNICODE) 1788 len = (lstrlenW(var->u.pwszVal) + 1) * sizeof(WCHAR); 1789 else 1790 len = lstrlenA(var->u.pszVal) + 1; 1791 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len); 1792 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count); 1793 if (FAILED(hr)) 1794 goto end; 1795 hr = IStream_Write(This->stm, var->u.pszVal, len, &count); 1796 bytesWritten = count + sizeof(DWORD); 1797 break; 1798 } 1799 case VT_LPWSTR: 1800 { 1801 DWORD len = lstrlenW(var->u.pwszVal) + 1, dwTemp; 1802 1803 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len); 1804 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count); 1805 if (FAILED(hr)) 1806 goto end; 1807 hr = IStream_Write(This->stm, var->u.pwszVal, len * sizeof(WCHAR), 1808 &count); 1809 bytesWritten = count + sizeof(DWORD); 1810 break; 1811 } 1812 case VT_FILETIME: 1813 { 1814 FILETIME temp; 1815 1816 StorageUtl_WriteULargeInteger((BYTE *)&temp, 0, 1817 (const ULARGE_INTEGER *)&var->u.filetime); 1818 hr = IStream_Write(This->stm, &temp, sizeof(FILETIME), &count); 1819 bytesWritten = count; 1820 break; 1821 } 1822 case VT_CF: 1823 { 1824 DWORD cf_hdr[2], len; 1825 1826 len = var->u.pclipdata->cbSize; 1827 StorageUtl_WriteDWord((LPBYTE)&cf_hdr[0], 0, len + 8); 1828 StorageUtl_WriteDWord((LPBYTE)&cf_hdr[1], 0, var->u.pclipdata->ulClipFmt); 1829 hr = IStream_Write(This->stm, cf_hdr, sizeof(cf_hdr), &count); 1830 if (FAILED(hr)) 1831 goto end; 1832 hr = IStream_Write(This->stm, var->u.pclipdata->pClipData, 1833 len - sizeof(var->u.pclipdata->ulClipFmt), &count); 1834 if (FAILED(hr)) 1835 goto end; 1836 bytesWritten = count + sizeof cf_hdr; 1837 break; 1838 } 1839 case VT_CLSID: 1840 { 1841 CLSID temp; 1842 1843 StorageUtl_WriteGUID((BYTE *)&temp, 0, var->u.puuid); 1844 hr = IStream_Write(This->stm, &temp, sizeof(temp), &count); 1845 bytesWritten = count; 1846 break; 1847 } 1848 default: 1849 FIXME("unsupported type: %d\n", var->vt); 1850 return STG_E_INVALIDPARAMETER; 1851 } 1852 1853 if (SUCCEEDED(hr)) 1854 { 1855 *sectionOffset += bytesWritten; 1856 if (bytesWritten % sizeof(DWORD)) 1857 { 1858 DWORD padding = sizeof(DWORD) - bytesWritten % sizeof(DWORD); 1859 TRACE("adding %d bytes of padding\n", padding); 1860 *sectionOffset += padding; 1861 } 1862 } 1863 1864 end: 1865 return hr; 1866 } 1867 1868 struct PropertyClosure 1869 { 1870 HRESULT hr; 1871 DWORD propNum; 1872 DWORD *sectionOffset; 1873 }; 1874 1875 static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value, 1876 void *extra, void *closure) 1877 { 1878 PropertyStorage_impl *This = extra; 1879 struct PropertyClosure *c = closure; 1880 1881 assert(key); 1882 assert(value); 1883 assert(extra); 1884 assert(closure); 1885 c->hr = PropertyStorage_WritePropertyToStream(This, c->propNum++, 1886 PtrToUlong(key), value, c->sectionOffset); 1887 return SUCCEEDED(c->hr); 1888 } 1889 1890 static HRESULT PropertyStorage_WritePropertiesToStream( 1891 PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset) 1892 { 1893 struct PropertyClosure closure; 1894 1895 assert(sectionOffset); 1896 closure.hr = S_OK; 1897 closure.propNum = startingPropNum; 1898 closure.sectionOffset = sectionOffset; 1899 dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter, 1900 &closure); 1901 return closure.hr; 1902 } 1903 1904 static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This) 1905 { 1906 HRESULT hr; 1907 ULONG count = 0; 1908 LARGE_INTEGER seek = { {0} }; 1909 PROPERTYSETHEADER hdr; 1910 FORMATIDOFFSET fmtOffset; 1911 1912 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); 1913 if (FAILED(hr)) 1914 goto end; 1915 PropertyStorage_MakeHeader(This, &hdr); 1916 hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count); 1917 if (FAILED(hr)) 1918 goto end; 1919 if (count != sizeof(hdr)) 1920 { 1921 hr = STG_E_WRITEFAULT; 1922 goto end; 1923 } 1924 1925 PropertyStorage_MakeFmtIdOffset(This, &fmtOffset); 1926 hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count); 1927 if (FAILED(hr)) 1928 goto end; 1929 if (count != sizeof(fmtOffset)) 1930 { 1931 hr = STG_E_WRITEFAULT; 1932 goto end; 1933 } 1934 hr = S_OK; 1935 1936 end: 1937 return hr; 1938 } 1939 1940 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This) 1941 { 1942 PROPERTYSECTIONHEADER sectionHdr; 1943 HRESULT hr; 1944 ULONG count; 1945 LARGE_INTEGER seek; 1946 DWORD numProps, prop, sectionOffset, dwTemp; 1947 PROPVARIANT var; 1948 1949 PropertyStorage_WriteHeadersToStream(This); 1950 1951 /* Count properties. Always at least one property, the code page */ 1952 numProps = 1; 1953 if (dictionary_num_entries(This->name_to_propid)) 1954 numProps++; 1955 if (This->locale != LOCALE_SYSTEM_DEFAULT) 1956 numProps++; 1957 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE) 1958 numProps++; 1959 numProps += dictionary_num_entries(This->propid_to_prop); 1960 1961 /* Write section header with 0 bytes right now, I'll adjust it after 1962 * writing properties. 1963 */ 1964 PropertyStorage_MakeSectionHdr(0, numProps, §ionHdr); 1965 seek.QuadPart = SECTIONHEADER_OFFSET; 1966 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); 1967 if (FAILED(hr)) 1968 goto end; 1969 hr = IStream_Write(This->stm, §ionHdr, sizeof(sectionHdr), &count); 1970 if (FAILED(hr)) 1971 goto end; 1972 1973 prop = 0; 1974 sectionOffset = sizeof(PROPERTYSECTIONHEADER) + 1975 numProps * sizeof(PROPERTYIDOFFSET); 1976 1977 if (dictionary_num_entries(This->name_to_propid)) 1978 { 1979 prop++; 1980 hr = PropertyStorage_WriteDictionaryToStream(This, §ionOffset); 1981 if (FAILED(hr)) 1982 goto end; 1983 } 1984 1985 PropVariantInit(&var); 1986 1987 var.vt = VT_I2; 1988 var.u.iVal = This->codePage; 1989 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE, 1990 &var, §ionOffset); 1991 if (FAILED(hr)) 1992 goto end; 1993 1994 if (This->locale != LOCALE_SYSTEM_DEFAULT) 1995 { 1996 var.vt = VT_I4; 1997 var.u.lVal = This->locale; 1998 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE, 1999 &var, §ionOffset); 2000 if (FAILED(hr)) 2001 goto end; 2002 } 2003 2004 if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE) 2005 { 2006 var.vt = VT_I4; 2007 var.u.lVal = 1; 2008 hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR, 2009 &var, §ionOffset); 2010 if (FAILED(hr)) 2011 goto end; 2012 } 2013 2014 hr = PropertyStorage_WritePropertiesToStream(This, prop, §ionOffset); 2015 if (FAILED(hr)) 2016 goto end; 2017 2018 /* Now write the byte count of the section */ 2019 seek.QuadPart = SECTIONHEADER_OFFSET; 2020 hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); 2021 if (FAILED(hr)) 2022 goto end; 2023 StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, sectionOffset); 2024 hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count); 2025 2026 end: 2027 return hr; 2028 } 2029 2030 /*********************************************************************** 2031 * PropertyStorage_Construct 2032 */ 2033 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This) 2034 { 2035 dictionary_destroy(This->name_to_propid); 2036 This->name_to_propid = NULL; 2037 dictionary_destroy(This->propid_to_name); 2038 This->propid_to_name = NULL; 2039 dictionary_destroy(This->propid_to_prop); 2040 This->propid_to_prop = NULL; 2041 } 2042 2043 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This) 2044 { 2045 HRESULT hr = S_OK; 2046 2047 This->name_to_propid = dictionary_create( 2048 PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy, 2049 This); 2050 if (!This->name_to_propid) 2051 { 2052 hr = STG_E_INSUFFICIENTMEMORY; 2053 goto end; 2054 } 2055 This->propid_to_name = dictionary_create(PropertyStorage_PropCompare, 2056 NULL, This); 2057 if (!This->propid_to_name) 2058 { 2059 hr = STG_E_INSUFFICIENTMEMORY; 2060 goto end; 2061 } 2062 This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare, 2063 PropertyStorage_PropertyDestroy, This); 2064 if (!This->propid_to_prop) 2065 { 2066 hr = STG_E_INSUFFICIENTMEMORY; 2067 goto end; 2068 } 2069 end: 2070 if (FAILED(hr)) 2071 PropertyStorage_DestroyDictionaries(This); 2072 return hr; 2073 } 2074 2075 static HRESULT PropertyStorage_BaseConstruct(IStream *stm, 2076 REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps) 2077 { 2078 HRESULT hr = S_OK; 2079 2080 assert(pps); 2081 assert(rfmtid); 2082 *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps); 2083 if (!*pps) 2084 return E_OUTOFMEMORY; 2085 2086 (*pps)->IPropertyStorage_iface.lpVtbl = &IPropertyStorage_Vtbl; 2087 (*pps)->ref = 1; 2088 InitializeCriticalSection(&(*pps)->cs); 2089 (*pps)->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PropertyStorage_impl.cs"); 2090 (*pps)->stm = stm; 2091 (*pps)->fmtid = *rfmtid; 2092 (*pps)->grfMode = grfMode; 2093 2094 hr = PropertyStorage_CreateDictionaries(*pps); 2095 if (FAILED(hr)) 2096 { 2097 (*pps)->cs.DebugInfo->Spare[0] = 0; 2098 DeleteCriticalSection(&(*pps)->cs); 2099 HeapFree(GetProcessHeap(), 0, *pps); 2100 *pps = NULL; 2101 } 2102 else IStream_AddRef( stm ); 2103 2104 return hr; 2105 } 2106 2107 static HRESULT PropertyStorage_ConstructFromStream(IStream *stm, 2108 REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps) 2109 { 2110 PropertyStorage_impl *ps; 2111 HRESULT hr; 2112 2113 assert(pps); 2114 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps); 2115 if (SUCCEEDED(hr)) 2116 { 2117 hr = PropertyStorage_ReadFromStream(ps); 2118 if (SUCCEEDED(hr)) 2119 { 2120 *pps = &ps->IPropertyStorage_iface; 2121 TRACE("PropertyStorage %p constructed\n", ps); 2122 hr = S_OK; 2123 } 2124 else IPropertyStorage_Release( &ps->IPropertyStorage_iface ); 2125 } 2126 return hr; 2127 } 2128 2129 static HRESULT PropertyStorage_ConstructEmpty(IStream *stm, 2130 REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps) 2131 { 2132 PropertyStorage_impl *ps; 2133 HRESULT hr; 2134 2135 assert(pps); 2136 hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps); 2137 if (SUCCEEDED(hr)) 2138 { 2139 ps->format = 0; 2140 ps->grfFlags = grfFlags; 2141 if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE) 2142 ps->format = 1; 2143 /* default to Unicode unless told not to, as specified on msdn */ 2144 if (ps->grfFlags & PROPSETFLAG_ANSI) 2145 ps->codePage = GetACP(); 2146 else 2147 ps->codePage = CP_UNICODE; 2148 ps->locale = LOCALE_SYSTEM_DEFAULT; 2149 TRACE("Code page is %d, locale is %d\n", ps->codePage, ps->locale); 2150 *pps = &ps->IPropertyStorage_iface; 2151 TRACE("PropertyStorage %p constructed\n", ps); 2152 hr = S_OK; 2153 } 2154 return hr; 2155 } 2156 2157 2158 /*********************************************************************** 2159 * Implementation of IPropertySetStorage 2160 */ 2161 2162 /************************************************************************ 2163 * IPropertySetStorage_fnQueryInterface (IUnknown) 2164 * 2165 * This method forwards to the common QueryInterface implementation 2166 */ 2167 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface( 2168 IPropertySetStorage *ppstg, 2169 REFIID riid, 2170 void** ppvObject) 2171 { 2172 StorageImpl *This = impl_from_IPropertySetStorage(ppstg); 2173 return IStorage_QueryInterface( &This->base.IStorage_iface, riid, ppvObject ); 2174 } 2175 2176 /************************************************************************ 2177 * IPropertySetStorage_fnAddRef (IUnknown) 2178 * 2179 * This method forwards to the common AddRef implementation 2180 */ 2181 static ULONG WINAPI IPropertySetStorage_fnAddRef( 2182 IPropertySetStorage *ppstg) 2183 { 2184 StorageImpl *This = impl_from_IPropertySetStorage(ppstg); 2185 return IStorage_AddRef( &This->base.IStorage_iface ); 2186 } 2187 2188 /************************************************************************ 2189 * IPropertySetStorage_fnRelease (IUnknown) 2190 * 2191 * This method forwards to the common Release implementation 2192 */ 2193 static ULONG WINAPI IPropertySetStorage_fnRelease( 2194 IPropertySetStorage *ppstg) 2195 { 2196 StorageImpl *This = impl_from_IPropertySetStorage(ppstg); 2197 return IStorage_Release( &This->base.IStorage_iface ); 2198 } 2199 2200 /************************************************************************ 2201 * IPropertySetStorage_fnCreate (IPropertySetStorage) 2202 */ 2203 static HRESULT WINAPI IPropertySetStorage_fnCreate( 2204 IPropertySetStorage *ppstg, 2205 REFFMTID rfmtid, 2206 const CLSID* pclsid, 2207 DWORD grfFlags, 2208 DWORD grfMode, 2209 IPropertyStorage** ppprstg) 2210 { 2211 StorageImpl *This = impl_from_IPropertySetStorage(ppstg); 2212 WCHAR name[CCH_MAX_PROPSTG_NAME + 1]; 2213 IStream *stm = NULL; 2214 HRESULT r; 2215 2216 TRACE("%p %s %08x %08x %p\n", This, debugstr_guid(rfmtid), grfFlags, 2217 grfMode, ppprstg); 2218 2219 /* be picky */ 2220 if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE)) 2221 { 2222 r = STG_E_INVALIDFLAG; 2223 goto end; 2224 } 2225 2226 if (!rfmtid) 2227 { 2228 r = E_INVALIDARG; 2229 goto end; 2230 } 2231 2232 /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a 2233 * storage, not a stream. For now, disallow it. 2234 */ 2235 if (grfFlags & PROPSETFLAG_NONSIMPLE) 2236 { 2237 FIXME("PROPSETFLAG_NONSIMPLE not supported\n"); 2238 r = STG_E_INVALIDFLAG; 2239 goto end; 2240 } 2241 2242 r = FmtIdToPropStgName(rfmtid, name); 2243 if (FAILED(r)) 2244 goto end; 2245 2246 r = IStorage_CreateStream( &This->base.IStorage_iface, name, grfMode, 0, 0, &stm ); 2247 if (FAILED(r)) 2248 goto end; 2249 2250 r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg); 2251 2252 IStream_Release( stm ); 2253 2254 end: 2255 TRACE("returning 0x%08x\n", r); 2256 return r; 2257 } 2258 2259 /************************************************************************ 2260 * IPropertySetStorage_fnOpen (IPropertySetStorage) 2261 */ 2262 static HRESULT WINAPI IPropertySetStorage_fnOpen( 2263 IPropertySetStorage *ppstg, 2264 REFFMTID rfmtid, 2265 DWORD grfMode, 2266 IPropertyStorage** ppprstg) 2267 { 2268 StorageImpl *This = impl_from_IPropertySetStorage(ppstg); 2269 IStream *stm = NULL; 2270 WCHAR name[CCH_MAX_PROPSTG_NAME + 1]; 2271 HRESULT r; 2272 2273 TRACE("%p %s %08x %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg); 2274 2275 /* be picky */ 2276 if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) && 2277 grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE)) 2278 { 2279 r = STG_E_INVALIDFLAG; 2280 goto end; 2281 } 2282 2283 if (!rfmtid) 2284 { 2285 r = E_INVALIDARG; 2286 goto end; 2287 } 2288 2289 r = FmtIdToPropStgName(rfmtid, name); 2290 if (FAILED(r)) 2291 goto end; 2292 2293 r = IStorage_OpenStream( &This->base.IStorage_iface, name, 0, grfMode, 0, &stm ); 2294 if (FAILED(r)) 2295 goto end; 2296 2297 r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg); 2298 2299 IStream_Release( stm ); 2300 2301 end: 2302 TRACE("returning 0x%08x\n", r); 2303 return r; 2304 } 2305 2306 /************************************************************************ 2307 * IPropertySetStorage_fnDelete (IPropertySetStorage) 2308 */ 2309 static HRESULT WINAPI IPropertySetStorage_fnDelete( 2310 IPropertySetStorage *ppstg, 2311 REFFMTID rfmtid) 2312 { 2313 StorageImpl *This = impl_from_IPropertySetStorage(ppstg); 2314 WCHAR name[CCH_MAX_PROPSTG_NAME + 1]; 2315 HRESULT r; 2316 2317 TRACE("%p %s\n", This, debugstr_guid(rfmtid)); 2318 2319 if (!rfmtid) 2320 return E_INVALIDARG; 2321 2322 r = FmtIdToPropStgName(rfmtid, name); 2323 if (FAILED(r)) 2324 return r; 2325 2326 return IStorage_DestroyElement(&This->base.IStorage_iface, name); 2327 } 2328 2329 /************************************************************************ 2330 * IPropertySetStorage_fnEnum (IPropertySetStorage) 2331 */ 2332 static HRESULT WINAPI IPropertySetStorage_fnEnum( 2333 IPropertySetStorage *ppstg, 2334 IEnumSTATPROPSETSTG** ppenum) 2335 { 2336 StorageImpl *This = impl_from_IPropertySetStorage(ppstg); 2337 return create_EnumSTATPROPSETSTG(This, ppenum); 2338 } 2339 2340 /************************************************************************ 2341 * Implement IEnumSTATPROPSETSTG using enumx 2342 */ 2343 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnQueryInterface( 2344 IEnumSTATPROPSETSTG *iface, 2345 REFIID riid, 2346 void** ppvObject) 2347 { 2348 return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject); 2349 } 2350 2351 static ULONG WINAPI IEnumSTATPROPSETSTG_fnAddRef( 2352 IEnumSTATPROPSETSTG *iface) 2353 { 2354 return enumx_AddRef((enumx_impl*)iface); 2355 } 2356 2357 static ULONG WINAPI IEnumSTATPROPSETSTG_fnRelease( 2358 IEnumSTATPROPSETSTG *iface) 2359 { 2360 return enumx_Release((enumx_impl*)iface); 2361 } 2362 2363 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnNext( 2364 IEnumSTATPROPSETSTG *iface, 2365 ULONG celt, 2366 STATPROPSETSTG *rgelt, 2367 ULONG *pceltFetched) 2368 { 2369 return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched); 2370 } 2371 2372 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnSkip( 2373 IEnumSTATPROPSETSTG *iface, 2374 ULONG celt) 2375 { 2376 return enumx_Skip((enumx_impl*)iface, celt); 2377 } 2378 2379 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnReset( 2380 IEnumSTATPROPSETSTG *iface) 2381 { 2382 return enumx_Reset((enumx_impl*)iface); 2383 } 2384 2385 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnClone( 2386 IEnumSTATPROPSETSTG *iface, 2387 IEnumSTATPROPSETSTG **ppenum) 2388 { 2389 return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum); 2390 } 2391 2392 static HRESULT create_EnumSTATPROPSETSTG( 2393 StorageImpl *This, 2394 IEnumSTATPROPSETSTG** ppenum) 2395 { 2396 IStorage *stg = &This->base.IStorage_iface; 2397 IEnumSTATSTG *penum = NULL; 2398 STATSTG stat; 2399 ULONG count; 2400 HRESULT r; 2401 STATPROPSETSTG statpss; 2402 enumx_impl *enumx; 2403 2404 TRACE("%p %p\n", This, ppenum); 2405 2406 enumx = enumx_allocate(&IID_IEnumSTATPROPSETSTG, 2407 &IEnumSTATPROPSETSTG_Vtbl, 2408 sizeof (STATPROPSETSTG), 2409 (IUnknown*)&This->base.IStorage_iface, 2410 NULL); 2411 2412 /* add all the property set elements into a list */ 2413 r = IStorage_EnumElements(stg, 0, NULL, 0, &penum); 2414 if (FAILED(r)) 2415 { 2416 enumx_Release(enumx); 2417 return E_OUTOFMEMORY; 2418 } 2419 2420 while (1) 2421 { 2422 count = 0; 2423 r = IEnumSTATSTG_Next(penum, 1, &stat, &count); 2424 if (FAILED(r)) 2425 break; 2426 if (!count) 2427 break; 2428 if (!stat.pwcsName) 2429 continue; 2430 if (stat.pwcsName[0] == 5 && stat.type == STGTY_STREAM) 2431 { 2432 PropStgNameToFmtId(stat.pwcsName, &statpss.fmtid); 2433 TRACE("adding %s (%s)\n", debugstr_w(stat.pwcsName), 2434 debugstr_guid(&statpss.fmtid)); 2435 statpss.mtime = stat.mtime; 2436 statpss.atime = stat.atime; 2437 statpss.ctime = stat.ctime; 2438 statpss.grfFlags = stat.grfMode; 2439 statpss.clsid = stat.clsid; 2440 enumx_add_element(enumx, &statpss); 2441 } 2442 CoTaskMemFree(stat.pwcsName); 2443 } 2444 IEnumSTATSTG_Release(penum); 2445 2446 *ppenum = (IEnumSTATPROPSETSTG*) enumx; 2447 2448 return S_OK; 2449 } 2450 2451 /************************************************************************ 2452 * Implement IEnumSTATPROPSTG using enumx 2453 */ 2454 static HRESULT WINAPI IEnumSTATPROPSTG_fnQueryInterface( 2455 IEnumSTATPROPSTG *iface, 2456 REFIID riid, 2457 void** ppvObject) 2458 { 2459 return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject); 2460 } 2461 2462 static ULONG WINAPI IEnumSTATPROPSTG_fnAddRef( 2463 IEnumSTATPROPSTG *iface) 2464 { 2465 return enumx_AddRef((enumx_impl*)iface); 2466 } 2467 2468 static ULONG WINAPI IEnumSTATPROPSTG_fnRelease( 2469 IEnumSTATPROPSTG *iface) 2470 { 2471 return enumx_Release((enumx_impl*)iface); 2472 } 2473 2474 static HRESULT WINAPI IEnumSTATPROPSTG_fnNext( 2475 IEnumSTATPROPSTG *iface, 2476 ULONG celt, 2477 STATPROPSTG *rgelt, 2478 ULONG *pceltFetched) 2479 { 2480 return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched); 2481 } 2482 2483 static HRESULT WINAPI IEnumSTATPROPSTG_fnSkip( 2484 IEnumSTATPROPSTG *iface, 2485 ULONG celt) 2486 { 2487 return enumx_Skip((enumx_impl*)iface, celt); 2488 } 2489 2490 static HRESULT WINAPI IEnumSTATPROPSTG_fnReset( 2491 IEnumSTATPROPSTG *iface) 2492 { 2493 return enumx_Reset((enumx_impl*)iface); 2494 } 2495 2496 static HRESULT WINAPI IEnumSTATPROPSTG_fnClone( 2497 IEnumSTATPROPSTG *iface, 2498 IEnumSTATPROPSTG **ppenum) 2499 { 2500 return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum); 2501 } 2502 2503 static void prop_enum_copy_cb(IUnknown *parent, void *orig, void *dest) 2504 { 2505 PropertyStorage_impl *storage = impl_from_IPropertyStorage((IPropertyStorage*)parent); 2506 STATPROPSTG *src_prop = orig; 2507 STATPROPSTG *dest_prop = dest; 2508 LPWSTR name; 2509 2510 dest_prop->propid = src_prop->propid; 2511 dest_prop->vt = src_prop->vt; 2512 dest_prop->lpwstrName = NULL; 2513 2514 if (dictionary_find(storage->propid_to_name, UlongToPtr(src_prop->propid), (void**)&name)) 2515 { 2516 DWORD size = (strlenW(name) + 1) * sizeof(WCHAR); 2517 2518 dest_prop->lpwstrName = CoTaskMemAlloc(size); 2519 if (!dest_prop->lpwstrName) return; 2520 memcpy(dest_prop->lpwstrName, name, size); 2521 } 2522 } 2523 2524 static BOOL prop_enum_stat(const void *k, const void *v, void *extra, void *arg) 2525 { 2526 enumx_impl *enumx = arg; 2527 PROPID propid = PtrToUlong(k); 2528 const PROPVARIANT *prop = v; 2529 STATPROPSTG stat; 2530 2531 stat.lpwstrName = NULL; 2532 stat.propid = propid; 2533 stat.vt = prop->vt; 2534 2535 enumx_add_element(enumx, &stat); 2536 2537 return TRUE; 2538 } 2539 2540 static HRESULT create_EnumSTATPROPSTG( 2541 PropertyStorage_impl *This, 2542 IEnumSTATPROPSTG** ppenum) 2543 { 2544 enumx_impl *enumx; 2545 2546 TRACE("%p %p\n", This, ppenum); 2547 2548 enumx = enumx_allocate(&IID_IEnumSTATPROPSTG, 2549 &IEnumSTATPROPSTG_Vtbl, 2550 sizeof (STATPROPSTG), 2551 (IUnknown*)&This->IPropertyStorage_iface, 2552 prop_enum_copy_cb); 2553 2554 dictionary_enumerate(This->propid_to_prop, prop_enum_stat, enumx); 2555 2556 *ppenum = (IEnumSTATPROPSTG*) enumx; 2557 2558 return S_OK; 2559 } 2560 2561 /*********************************************************************** 2562 * vtables 2563 */ 2564 const IPropertySetStorageVtbl IPropertySetStorage_Vtbl = 2565 { 2566 IPropertySetStorage_fnQueryInterface, 2567 IPropertySetStorage_fnAddRef, 2568 IPropertySetStorage_fnRelease, 2569 IPropertySetStorage_fnCreate, 2570 IPropertySetStorage_fnOpen, 2571 IPropertySetStorage_fnDelete, 2572 IPropertySetStorage_fnEnum 2573 }; 2574 2575 static const IPropertyStorageVtbl IPropertyStorage_Vtbl = 2576 { 2577 IPropertyStorage_fnQueryInterface, 2578 IPropertyStorage_fnAddRef, 2579 IPropertyStorage_fnRelease, 2580 IPropertyStorage_fnReadMultiple, 2581 IPropertyStorage_fnWriteMultiple, 2582 IPropertyStorage_fnDeleteMultiple, 2583 IPropertyStorage_fnReadPropertyNames, 2584 IPropertyStorage_fnWritePropertyNames, 2585 IPropertyStorage_fnDeletePropertyNames, 2586 IPropertyStorage_fnCommit, 2587 IPropertyStorage_fnRevert, 2588 IPropertyStorage_fnEnum, 2589 IPropertyStorage_fnSetTimes, 2590 IPropertyStorage_fnSetClass, 2591 IPropertyStorage_fnStat, 2592 }; 2593 2594 static const IEnumSTATPROPSETSTGVtbl IEnumSTATPROPSETSTG_Vtbl = 2595 { 2596 IEnumSTATPROPSETSTG_fnQueryInterface, 2597 IEnumSTATPROPSETSTG_fnAddRef, 2598 IEnumSTATPROPSETSTG_fnRelease, 2599 IEnumSTATPROPSETSTG_fnNext, 2600 IEnumSTATPROPSETSTG_fnSkip, 2601 IEnumSTATPROPSETSTG_fnReset, 2602 IEnumSTATPROPSETSTG_fnClone, 2603 }; 2604 2605 static const IEnumSTATPROPSTGVtbl IEnumSTATPROPSTG_Vtbl = 2606 { 2607 IEnumSTATPROPSTG_fnQueryInterface, 2608 IEnumSTATPROPSTG_fnAddRef, 2609 IEnumSTATPROPSTG_fnRelease, 2610 IEnumSTATPROPSTG_fnNext, 2611 IEnumSTATPROPSTG_fnSkip, 2612 IEnumSTATPROPSTG_fnReset, 2613 IEnumSTATPROPSTG_fnClone, 2614 }; 2615 2616 /*********************************************************************** 2617 * Format ID <-> name conversion 2618 */ 2619 static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y', 2620 'I','n','f','o','r','m','a','t','i','o','n',0 }; 2621 static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t', 2622 'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 }; 2623 2624 #define BITS_PER_BYTE 8 2625 #define CHARMASK 0x1f 2626 #define BITS_IN_CHARMASK 5 2627 #define NUM_ALPHA_CHARS 26 2628 2629 /*********************************************************************** 2630 * FmtIdToPropStgName [ole32.@] 2631 * Returns the storage name of the format ID rfmtid. 2632 * PARAMS 2633 * rfmtid [I] Format ID for which to return a storage name 2634 * str [O] Storage name associated with rfmtid. 2635 * 2636 * RETURNS 2637 * E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise. 2638 * 2639 * NOTES 2640 * str must be at least CCH_MAX_PROPSTG_NAME characters in length. 2641 */ 2642 HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str) 2643 { 2644 static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345"; 2645 2646 TRACE("%s, %p\n", debugstr_guid(rfmtid), str); 2647 2648 if (!rfmtid) return E_INVALIDARG; 2649 if (!str) return E_INVALIDARG; 2650 2651 if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid)) 2652 lstrcpyW(str, szSummaryInfo); 2653 else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid)) 2654 lstrcpyW(str, szDocSummaryInfo); 2655 else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid)) 2656 lstrcpyW(str, szDocSummaryInfo); 2657 else 2658 { 2659 const BYTE *fmtptr; 2660 WCHAR *pstr = str; 2661 ULONG bitsRemaining = BITS_PER_BYTE; 2662 2663 *pstr++ = 5; 2664 for (fmtptr = (const BYTE *)rfmtid; fmtptr < (const BYTE *)rfmtid + sizeof(FMTID); ) 2665 { 2666 ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining); 2667 2668 if (bitsRemaining >= BITS_IN_CHARMASK) 2669 { 2670 *pstr = (WCHAR)(fmtMap[i & CHARMASK]); 2671 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' && 2672 *pstr <= 'z') 2673 *pstr += 'A' - 'a'; 2674 pstr++; 2675 bitsRemaining -= BITS_IN_CHARMASK; 2676 if (bitsRemaining == 0) 2677 { 2678 fmtptr++; 2679 bitsRemaining = BITS_PER_BYTE; 2680 } 2681 } 2682 else 2683 { 2684 if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID)) 2685 i |= *fmtptr << bitsRemaining; 2686 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]); 2687 bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK; 2688 } 2689 } 2690 *pstr = 0; 2691 } 2692 TRACE("returning %s\n", debugstr_w(str)); 2693 return S_OK; 2694 } 2695 2696 /*********************************************************************** 2697 * PropStgNameToFmtId [ole32.@] 2698 * Returns the format ID corresponding to the given name. 2699 * PARAMS 2700 * str [I] Storage name to convert to a format ID. 2701 * rfmtid [O] Format ID corresponding to str. 2702 * 2703 * RETURNS 2704 * E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to 2705 * a format ID, S_OK otherwise. 2706 */ 2707 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid) 2708 { 2709 HRESULT hr = STG_E_INVALIDNAME; 2710 2711 TRACE("%s, %p\n", debugstr_w(str), rfmtid); 2712 2713 if (!rfmtid) return E_INVALIDARG; 2714 if (!str) return STG_E_INVALIDNAME; 2715 2716 if (!lstrcmpiW(str, szDocSummaryInfo)) 2717 { 2718 *rfmtid = FMTID_DocSummaryInformation; 2719 hr = S_OK; 2720 } 2721 else if (!lstrcmpiW(str, szSummaryInfo)) 2722 { 2723 *rfmtid = FMTID_SummaryInformation; 2724 hr = S_OK; 2725 } 2726 else 2727 { 2728 ULONG bits; 2729 BYTE *fmtptr = (BYTE *)rfmtid - 1; 2730 const WCHAR *pstr = str; 2731 2732 memset(rfmtid, 0, sizeof(*rfmtid)); 2733 for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE; 2734 bits += BITS_IN_CHARMASK) 2735 { 2736 ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored; 2737 WCHAR wc; 2738 2739 if (bitsUsed == 0) 2740 fmtptr++; 2741 wc = *++pstr - 'A'; 2742 if (wc > NUM_ALPHA_CHARS) 2743 { 2744 wc += 'A' - 'a'; 2745 if (wc > NUM_ALPHA_CHARS) 2746 { 2747 wc += 'a' - '0' + NUM_ALPHA_CHARS; 2748 if (wc > CHARMASK) 2749 { 2750 WARN("invalid character (%d)\n", *pstr); 2751 goto end; 2752 } 2753 } 2754 } 2755 *fmtptr |= wc << bitsUsed; 2756 bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK); 2757 if (bitsStored < BITS_IN_CHARMASK) 2758 { 2759 wc >>= BITS_PER_BYTE - bitsUsed; 2760 if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE) 2761 { 2762 if (wc != 0) 2763 { 2764 WARN("extra bits\n"); 2765 goto end; 2766 } 2767 break; 2768 } 2769 fmtptr++; 2770 *fmtptr |= (BYTE)wc; 2771 } 2772 } 2773 hr = S_OK; 2774 } 2775 end: 2776 return hr; 2777 } 2778 2779 #ifdef __i386__ /* thiscall functions are i386-specific */ 2780 2781 #define DEFINE_STDCALL_WRAPPER(num,func,args) \ 2782 __ASM_STDCALL_FUNC(func, args, \ 2783 "popl %eax\n\t" \ 2784 "popl %ecx\n\t" \ 2785 "pushl %eax\n\t" \ 2786 "movl (%ecx), %eax\n\t" \ 2787 "jmp *(4*(" #num "))(%eax)" ) 2788 2789 DEFINE_STDCALL_WRAPPER(0,Allocate_PMemoryAllocator,8) 2790 extern void* __stdcall Allocate_PMemoryAllocator(void *this, ULONG cbSize); 2791 2792 #else 2793 2794 static void* __cdecl Allocate_PMemoryAllocator(void *this, ULONG cbSize) 2795 { 2796 void* (__cdecl *fn)(void*,ULONG) = **(void***)this; 2797 return fn(this, cbSize); 2798 } 2799 2800 #endif 2801 2802 BOOLEAN WINAPI StgConvertPropertyToVariant(const SERIALIZEDPROPERTYVALUE* prop, 2803 USHORT CodePage, PROPVARIANT* pvar, void* pma) 2804 { 2805 HRESULT hr; 2806 2807 hr = PropertyStorage_ReadProperty(pvar, (const BYTE*)prop, CodePage, Allocate_PMemoryAllocator, pma); 2808 2809 if (FAILED(hr)) 2810 { 2811 FIXME("should raise C++ exception on failure\n"); 2812 PropVariantInit(pvar); 2813 } 2814 2815 return FALSE; 2816 } 2817 2818 SERIALIZEDPROPERTYVALUE* WINAPI StgConvertVariantToProperty(const PROPVARIANT *pvar, 2819 USHORT CodePage, SERIALIZEDPROPERTYVALUE *pprop, ULONG *pcb, PROPID pid, 2820 BOOLEAN fReserved, ULONG *pcIndirect) 2821 { 2822 FIXME("%p,%d,%p,%p,%d,%d,%p\n", pvar, CodePage, pprop, pcb, pid, fReserved, pcIndirect); 2823 2824 return NULL; 2825 } 2826 2827 HRESULT WINAPI StgCreatePropStg(IUnknown *unk, REFFMTID fmt, const CLSID *clsid, 2828 DWORD flags, DWORD reserved, IPropertyStorage **prop_stg) 2829 { 2830 IStorage *stg; 2831 IStream *stm; 2832 HRESULT r; 2833 2834 TRACE("%p %s %s %08x %d %p\n", unk, debugstr_guid(fmt), debugstr_guid(clsid), flags, reserved, prop_stg); 2835 2836 if (!fmt || reserved) 2837 { 2838 r = E_INVALIDARG; 2839 goto end; 2840 } 2841 2842 if (flags & PROPSETFLAG_NONSIMPLE) 2843 { 2844 r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg); 2845 if (FAILED(r)) 2846 goto end; 2847 2848 /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to create a 2849 * storage, not a stream. For now, disallow it. 2850 */ 2851 FIXME("PROPSETFLAG_NONSIMPLE not supported\n"); 2852 IStorage_Release(stg); 2853 r = STG_E_INVALIDFLAG; 2854 } 2855 else 2856 { 2857 r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm); 2858 if (FAILED(r)) 2859 goto end; 2860 2861 r = PropertyStorage_ConstructEmpty(stm, fmt, flags, 2862 STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg); 2863 2864 IStream_Release( stm ); 2865 } 2866 2867 end: 2868 TRACE("returning 0x%08x\n", r); 2869 return r; 2870 } 2871 2872 HRESULT WINAPI StgOpenPropStg(IUnknown *unk, REFFMTID fmt, DWORD flags, 2873 DWORD reserved, IPropertyStorage **prop_stg) 2874 { 2875 IStorage *stg; 2876 IStream *stm; 2877 HRESULT r; 2878 2879 TRACE("%p %s %08x %d %p\n", unk, debugstr_guid(fmt), flags, reserved, prop_stg); 2880 2881 if (!fmt || reserved) 2882 { 2883 r = E_INVALIDARG; 2884 goto end; 2885 } 2886 2887 if (flags & PROPSETFLAG_NONSIMPLE) 2888 { 2889 r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg); 2890 if (FAILED(r)) 2891 goto end; 2892 2893 /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to open a 2894 * storage, not a stream. For now, disallow it. 2895 */ 2896 FIXME("PROPSETFLAG_NONSIMPLE not supported\n"); 2897 IStorage_Release(stg); 2898 r = STG_E_INVALIDFLAG; 2899 } 2900 else 2901 { 2902 r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm); 2903 if (FAILED(r)) 2904 goto end; 2905 2906 r = PropertyStorage_ConstructFromStream(stm, fmt, 2907 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg); 2908 2909 IStream_Release( stm ); 2910 } 2911 2912 end: 2913 TRACE("returning 0x%08x\n", r); 2914 return r; 2915 } 2916