msxml3: Corrected Entity Reference Test.
[wine/hacks.git] / dlls / ole32 / storage32.c
bloba83c72904a552faf8d80ec1ed00ee2014aa4ac5f
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 * NOTES
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
32 * MSDN
33 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/istorage_compound_file_implementation.asp
36 #include <assert.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
42 #define COBJMACROS
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
46 #include "windef.h"
47 #include "winbase.h"
48 #include "winnls.h"
49 #include "winuser.h"
50 #include "wine/unicode.h"
51 #include "wine/debug.h"
53 #include "storage32.h"
54 #include "ole2.h" /* For Write/ReadClassStm */
56 #include "winreg.h"
57 #include "wine/wingdi16.h"
59 WINE_DEFAULT_DEBUG_CHANNEL(storage);
61 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
62 #define OLESTREAM_ID 0x501
63 #define OLESTREAM_MAX_STR_LEN 255
66 * These are signatures to detect the type of Document file.
68 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
69 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
71 static const char rootPropertyName[] = "Root Entry";
73 /****************************************************************************
74 * Storage32InternalImpl definitions.
76 * Definition of the implementation structure for the IStorage32 interface.
77 * This one implements the IStorage32 interface for storage that are
78 * inside another storage.
80 struct StorageInternalImpl
82 struct StorageBaseImpl base;
84 * There is no specific data for this class.
87 typedef struct StorageInternalImpl StorageInternalImpl;
89 /* Method definitions for the Storage32InternalImpl class. */
90 static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
91 DWORD openFlags, ULONG rootTropertyIndex);
92 static void StorageImpl_Destroy(StorageBaseImpl* iface);
93 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
94 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
95 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
96 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
97 static void StorageImpl_SaveFileHeader(StorageImpl* This);
99 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
100 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
101 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
102 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
103 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
105 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
106 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
107 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
109 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
110 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
111 ULONG blockIndex, ULONG offset, DWORD value);
112 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
113 ULONG blockIndex, ULONG offset, DWORD* value);
115 /* OLESTREAM memory structure to use for Get and Put Routines */
116 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
117 typedef struct
119 DWORD dwOleID;
120 DWORD dwTypeID;
121 DWORD dwOleTypeNameLength;
122 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
123 CHAR *pstrOleObjFileName;
124 DWORD dwOleObjFileNameLength;
125 DWORD dwMetaFileWidth;
126 DWORD dwMetaFileHeight;
127 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
128 DWORD dwDataLength;
129 BYTE *pData;
130 }OLECONVERT_OLESTREAM_DATA;
132 /* CompObj Stream structure */
133 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
134 typedef struct
136 BYTE byUnknown1[12];
137 CLSID clsid;
138 DWORD dwCLSIDNameLength;
139 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
140 DWORD dwOleTypeNameLength;
141 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
142 DWORD dwProgIDNameLength;
143 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
144 BYTE byUnknown2[16];
145 }OLECONVERT_ISTORAGE_COMPOBJ;
148 /* Ole Presention Stream structure */
149 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
150 typedef struct
152 BYTE byUnknown1[28];
153 DWORD dwExtentX;
154 DWORD dwExtentY;
155 DWORD dwSize;
156 BYTE *pData;
157 }OLECONVERT_ISTORAGE_OLEPRES;
161 /***********************************************************************
162 * Forward declaration of internal functions used by the method DestroyElement
164 static HRESULT deleteStorageProperty(
165 StorageImpl *parentStorage,
166 ULONG foundPropertyIndexToDelete,
167 StgProperty propertyToDelete);
169 static HRESULT deleteStreamProperty(
170 StorageImpl *parentStorage,
171 ULONG foundPropertyIndexToDelete,
172 StgProperty propertyToDelete);
174 static HRESULT findPlaceholder(
175 StorageImpl *storage,
176 ULONG propertyIndexToStore,
177 ULONG storagePropertyIndex,
178 INT typeOfRelation);
180 static HRESULT adjustPropertyChain(
181 StorageImpl *This,
182 StgProperty propertyToDelete,
183 StgProperty parentProperty,
184 ULONG parentPropertyId,
185 INT typeOfRelation);
187 /***********************************************************************
188 * Declaration of the functions used to manipulate StgProperty
191 static ULONG getFreeProperty(
192 StorageImpl *storage);
194 static void updatePropertyChain(
195 StorageImpl *storage,
196 ULONG newPropertyIndex,
197 StgProperty newProperty);
199 static LONG propertyNameCmp(
200 const OLECHAR *newProperty,
201 const OLECHAR *currentProperty);
204 /***********************************************************************
205 * Declaration of miscellaneous functions...
207 static HRESULT validateSTGM(DWORD stgmValue);
209 static DWORD GetShareModeFromSTGM(DWORD stgm);
210 static DWORD GetAccessModeFromSTGM(DWORD stgm);
211 static DWORD GetCreationModeFromSTGM(DWORD stgm);
213 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
216 /****************************************************************************
217 * IEnumSTATSTGImpl definitions.
219 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
220 * This class allows iterating through the content of a storage and to find
221 * specific items inside it.
223 struct IEnumSTATSTGImpl
225 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
226 * since we want to cast this in an IEnumSTATSTG pointer */
228 LONG ref; /* Reference count */
229 StorageImpl* parentStorage; /* Reference to the parent storage */
230 ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
233 * The current implementation of the IEnumSTATSTGImpl class uses a stack
234 * to walk the property sets to get the content of a storage. This stack
235 * is implemented by the following 3 data members
237 ULONG stackSize;
238 ULONG stackMaxSize;
239 ULONG* stackToVisit;
241 #define ENUMSTATSGT_SIZE_INCREMENT 10
245 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
246 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
247 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
248 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
249 static ULONG IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl* This, const OLECHAR* lpszPropName,
250 StgProperty* buffer);
251 static INT IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl *This, ULONG childProperty,
252 StgProperty *currentProperty, ULONG *propertyId);
254 /************************************************************************
255 ** Block Functions
258 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
260 if (index == 0xffffffff)
261 index = 0;
262 else
263 index ++;
265 return index * BIG_BLOCK_SIZE;
268 /************************************************************************
269 ** Storage32BaseImpl implementatiion
271 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
272 ULARGE_INTEGER offset,
273 void* buffer,
274 ULONG size,
275 ULONG* bytesRead)
277 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
280 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
281 ULARGE_INTEGER offset,
282 const void* buffer,
283 const ULONG size,
284 ULONG* bytesWritten)
286 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
289 /************************************************************************
290 * Storage32BaseImpl_QueryInterface (IUnknown)
292 * This method implements the common QueryInterface for all IStorage32
293 * implementations contained in this file.
295 * See Windows documentation for more details on IUnknown methods.
297 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
298 IStorage* iface,
299 REFIID riid,
300 void** ppvObject)
302 StorageBaseImpl *This = (StorageBaseImpl *)iface;
304 * Perform a sanity check on the parameters.
306 if ( (This==0) || (ppvObject==0) )
307 return E_INVALIDARG;
310 * Initialize the return parameter.
312 *ppvObject = 0;
315 * Compare the riid with the interface IDs implemented by this object.
317 if (IsEqualGUID(&IID_IUnknown, riid) ||
318 IsEqualGUID(&IID_IStorage, riid))
320 *ppvObject = (IStorage*)This;
322 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
324 *ppvObject = (IStorage*)&This->pssVtbl;
328 * Check that we obtained an interface.
330 if ((*ppvObject)==0)
331 return E_NOINTERFACE;
334 * Query Interface always increases the reference count by one when it is
335 * successful
337 IStorage_AddRef(iface);
339 return S_OK;
342 /************************************************************************
343 * Storage32BaseImpl_AddRef (IUnknown)
345 * This method implements the common AddRef for all IStorage32
346 * implementations contained in this file.
348 * See Windows documentation for more details on IUnknown methods.
350 static ULONG WINAPI StorageBaseImpl_AddRef(
351 IStorage* iface)
353 StorageBaseImpl *This = (StorageBaseImpl *)iface;
354 ULONG ref = InterlockedIncrement(&This->ref);
356 TRACE("(%p) AddRef to %d\n", This, ref);
358 return ref;
361 /************************************************************************
362 * Storage32BaseImpl_Release (IUnknown)
364 * This method implements the common Release for all IStorage32
365 * implementations contained in this file.
367 * See Windows documentation for more details on IUnknown methods.
369 static ULONG WINAPI StorageBaseImpl_Release(
370 IStorage* iface)
372 StorageBaseImpl *This = (StorageBaseImpl *)iface;
374 * Decrease the reference count on this object.
376 ULONG ref = InterlockedDecrement(&This->ref);
378 TRACE("(%p) ReleaseRef to %d\n", This, ref);
381 * If the reference count goes down to 0, perform suicide.
383 if (ref == 0)
386 * Since we are using a system of base-classes, we want to call the
387 * destructor of the appropriate derived class. To do this, we are
388 * using virtual functions to implement the destructor.
390 This->v_destructor(This);
393 return ref;
396 /************************************************************************
397 * Storage32BaseImpl_OpenStream (IStorage)
399 * This method will open the specified stream object from the current storage.
401 * See Windows documentation for more details on IStorage methods.
403 static HRESULT WINAPI StorageBaseImpl_OpenStream(
404 IStorage* iface,
405 const OLECHAR* pwcsName, /* [string][in] */
406 void* reserved1, /* [unique][in] */
407 DWORD grfMode, /* [in] */
408 DWORD reserved2, /* [in] */
409 IStream** ppstm) /* [out] */
411 StorageBaseImpl *This = (StorageBaseImpl *)iface;
412 IEnumSTATSTGImpl* propertyEnumeration;
413 StgStreamImpl* newStream;
414 StgProperty currentProperty;
415 ULONG foundPropertyIndex;
416 HRESULT res = STG_E_UNKNOWN;
418 TRACE("(%p, %s, %p, %x, %d, %p)\n",
419 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
422 * Perform a sanity check on the parameters.
424 if ( (pwcsName==NULL) || (ppstm==0) )
426 res = E_INVALIDARG;
427 goto end;
431 * Initialize the out parameter
433 *ppstm = NULL;
436 * Validate the STGM flags
438 if ( FAILED( validateSTGM(grfMode) ) ||
439 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
441 res = STG_E_INVALIDFLAG;
442 goto end;
446 * As documented.
448 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
450 res = STG_E_INVALIDFUNCTION;
451 goto end;
455 * Check that we're compatible with the parent's storage mode, but
456 * only if we are not in transacted mode
458 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
459 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
461 res = STG_E_ACCESSDENIED;
462 goto end;
467 * Create a property enumeration to search the properties
469 propertyEnumeration = IEnumSTATSTGImpl_Construct(
470 This->ancestorStorage,
471 This->rootPropertySetIndex);
474 * Search the enumeration for the property with the given name
476 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
477 propertyEnumeration,
478 pwcsName,
479 &currentProperty);
482 * Delete the property enumeration since we don't need it anymore
484 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
487 * If it was found, construct the stream object and return a pointer to it.
489 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
490 (currentProperty.propertyType==PROPTYPE_STREAM) )
492 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
494 if (newStream!=0)
496 newStream->grfMode = grfMode;
497 *ppstm = (IStream*)newStream;
500 * Since we are returning a pointer to the interface, we have to
501 * nail down the reference.
503 IStream_AddRef(*ppstm);
505 res = S_OK;
506 goto end;
509 res = E_OUTOFMEMORY;
510 goto end;
513 res = STG_E_FILENOTFOUND;
515 end:
516 if (res == S_OK)
517 TRACE("<-- IStream %p\n", *ppstm);
518 TRACE("<-- %08x\n", res);
519 return res;
522 /************************************************************************
523 * Storage32BaseImpl_OpenStorage (IStorage)
525 * This method will open a new storage object from the current storage.
527 * See Windows documentation for more details on IStorage methods.
529 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
530 IStorage* iface,
531 const OLECHAR* pwcsName, /* [string][unique][in] */
532 IStorage* pstgPriority, /* [unique][in] */
533 DWORD grfMode, /* [in] */
534 SNB snbExclude, /* [unique][in] */
535 DWORD reserved, /* [in] */
536 IStorage** ppstg) /* [out] */
538 StorageBaseImpl *This = (StorageBaseImpl *)iface;
539 StorageInternalImpl* newStorage;
540 IEnumSTATSTGImpl* propertyEnumeration;
541 StgProperty currentProperty;
542 ULONG foundPropertyIndex;
543 HRESULT res = STG_E_UNKNOWN;
545 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
546 iface, debugstr_w(pwcsName), pstgPriority,
547 grfMode, snbExclude, reserved, ppstg);
550 * Perform a sanity check on the parameters.
552 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
554 res = E_INVALIDARG;
555 goto end;
558 /* as documented */
559 if (snbExclude != NULL)
561 res = STG_E_INVALIDPARAMETER;
562 goto end;
566 * Validate the STGM flags
568 if ( FAILED( validateSTGM(grfMode) ))
570 res = STG_E_INVALIDFLAG;
571 goto end;
575 * As documented.
577 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
578 (grfMode & STGM_DELETEONRELEASE) ||
579 (grfMode & STGM_PRIORITY) )
581 res = STG_E_INVALIDFUNCTION;
582 goto end;
586 * Check that we're compatible with the parent's storage mode,
587 * but only if we are not transacted
589 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
590 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
592 res = STG_E_ACCESSDENIED;
593 goto end;
598 * Initialize the out parameter
600 *ppstg = NULL;
603 * Create a property enumeration to search the properties
605 propertyEnumeration = IEnumSTATSTGImpl_Construct(
606 This->ancestorStorage,
607 This->rootPropertySetIndex);
610 * Search the enumeration for the property with the given name
612 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
613 propertyEnumeration,
614 pwcsName,
615 &currentProperty);
618 * Delete the property enumeration since we don't need it anymore
620 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
623 * If it was found, construct the stream object and return a pointer to it.
625 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
626 (currentProperty.propertyType==PROPTYPE_STORAGE) )
629 * Construct a new Storage object
631 newStorage = StorageInternalImpl_Construct(
632 This->ancestorStorage,
633 grfMode,
634 foundPropertyIndex);
636 if (newStorage != 0)
638 *ppstg = (IStorage*)newStorage;
641 * Since we are returning a pointer to the interface,
642 * we have to nail down the reference.
644 StorageBaseImpl_AddRef(*ppstg);
646 res = S_OK;
647 goto end;
650 res = STG_E_INSUFFICIENTMEMORY;
651 goto end;
654 res = STG_E_FILENOTFOUND;
656 end:
657 TRACE("<-- %08x\n", res);
658 return res;
661 /************************************************************************
662 * Storage32BaseImpl_EnumElements (IStorage)
664 * This method will create an enumerator object that can be used to
665 * retrieve informatino about all the properties in the storage object.
667 * See Windows documentation for more details on IStorage methods.
669 static HRESULT WINAPI StorageBaseImpl_EnumElements(
670 IStorage* iface,
671 DWORD reserved1, /* [in] */
672 void* reserved2, /* [size_is][unique][in] */
673 DWORD reserved3, /* [in] */
674 IEnumSTATSTG** ppenum) /* [out] */
676 StorageBaseImpl *This = (StorageBaseImpl *)iface;
677 IEnumSTATSTGImpl* newEnum;
679 TRACE("(%p, %d, %p, %d, %p)\n",
680 iface, reserved1, reserved2, reserved3, ppenum);
683 * Perform a sanity check on the parameters.
685 if ( (This==0) || (ppenum==0))
686 return E_INVALIDARG;
689 * Construct the enumerator.
691 newEnum = IEnumSTATSTGImpl_Construct(
692 This->ancestorStorage,
693 This->rootPropertySetIndex);
695 if (newEnum!=0)
697 *ppenum = (IEnumSTATSTG*)newEnum;
700 * Don't forget to nail down a reference to the new object before
701 * returning it.
703 IEnumSTATSTG_AddRef(*ppenum);
705 return S_OK;
708 return E_OUTOFMEMORY;
711 /************************************************************************
712 * Storage32BaseImpl_Stat (IStorage)
714 * This method will retrieve information about this storage object.
716 * See Windows documentation for more details on IStorage methods.
718 static HRESULT WINAPI StorageBaseImpl_Stat(
719 IStorage* iface,
720 STATSTG* pstatstg, /* [out] */
721 DWORD grfStatFlag) /* [in] */
723 StorageBaseImpl *This = (StorageBaseImpl *)iface;
724 StgProperty curProperty;
725 BOOL readSuccessful;
726 HRESULT res = STG_E_UNKNOWN;
728 TRACE("(%p, %p, %x)\n",
729 iface, pstatstg, grfStatFlag);
732 * Perform a sanity check on the parameters.
734 if ( (This==0) || (pstatstg==0))
736 res = E_INVALIDARG;
737 goto end;
741 * Read the information from the property.
743 readSuccessful = StorageImpl_ReadProperty(
744 This->ancestorStorage,
745 This->rootPropertySetIndex,
746 &curProperty);
748 if (readSuccessful)
750 StorageUtl_CopyPropertyToSTATSTG(
751 pstatstg,
752 &curProperty,
753 grfStatFlag);
755 pstatstg->grfMode = This->openFlags;
756 pstatstg->grfStateBits = This->stateBits;
758 res = S_OK;
759 goto end;
762 res = E_FAIL;
764 end:
765 if (res == S_OK)
767 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
769 TRACE("<-- %08x\n", res);
770 return res;
773 /************************************************************************
774 * Storage32BaseImpl_RenameElement (IStorage)
776 * This method will rename the specified element.
778 * See Windows documentation for more details on IStorage methods.
780 * Implementation notes: The method used to rename consists of creating a clone
781 * of the deleted StgProperty object setting it with the new name and to
782 * perform a DestroyElement of the old StgProperty.
784 static HRESULT WINAPI StorageBaseImpl_RenameElement(
785 IStorage* iface,
786 const OLECHAR* pwcsOldName, /* [in] */
787 const OLECHAR* pwcsNewName) /* [in] */
789 StorageBaseImpl *This = (StorageBaseImpl *)iface;
790 IEnumSTATSTGImpl* propertyEnumeration;
791 StgProperty currentProperty;
792 ULONG foundPropertyIndex;
794 TRACE("(%p, %s, %s)\n",
795 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
798 * Create a property enumeration to search the properties
800 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
801 This->rootPropertySetIndex);
804 * Search the enumeration for the new property name
806 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
807 pwcsNewName,
808 &currentProperty);
810 if (foundPropertyIndex != PROPERTY_NULL)
813 * There is already a property with the new name
815 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
816 return STG_E_FILEALREADYEXISTS;
819 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
822 * Search the enumeration for the old property name
824 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
825 pwcsOldName,
826 &currentProperty);
829 * Delete the property enumeration since we don't need it anymore
831 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
833 if (foundPropertyIndex != PROPERTY_NULL)
835 StgProperty renamedProperty;
836 ULONG renamedPropertyIndex;
839 * Setup a new property for the renamed property
841 renamedProperty.sizeOfNameString =
842 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
844 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
845 return STG_E_INVALIDNAME;
847 strcpyW(renamedProperty.name, pwcsNewName);
849 renamedProperty.propertyType = currentProperty.propertyType;
850 renamedProperty.startingBlock = currentProperty.startingBlock;
851 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
852 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
854 renamedProperty.previousProperty = PROPERTY_NULL;
855 renamedProperty.nextProperty = PROPERTY_NULL;
858 * Bring the dirProperty link in case it is a storage and in which
859 * case the renamed storage elements don't require to be reorganized.
861 renamedProperty.dirProperty = currentProperty.dirProperty;
863 /* call CoFileTime to get the current time
864 renamedProperty.timeStampS1
865 renamedProperty.timeStampD1
866 renamedProperty.timeStampS2
867 renamedProperty.timeStampD2
868 renamedProperty.propertyUniqueID
872 * Obtain a free property in the property chain
874 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
877 * Save the new property into the new property spot
879 StorageImpl_WriteProperty(
880 This->ancestorStorage,
881 renamedPropertyIndex,
882 &renamedProperty);
885 * Find a spot in the property chain for our newly created property.
887 updatePropertyChain(
888 (StorageImpl*)This,
889 renamedPropertyIndex,
890 renamedProperty);
893 * At this point the renamed property has been inserted in the tree,
894 * now, before Destroying the old property we must zero its dirProperty
895 * otherwise the DestroyProperty below will zap it all and we do not want
896 * this to happen.
897 * Also, we fake that the old property is a storage so the DestroyProperty
898 * will not do a SetSize(0) on the stream data.
900 * This means that we need to tweak the StgProperty if it is a stream or a
901 * non empty storage.
903 StorageImpl_ReadProperty(This->ancestorStorage,
904 foundPropertyIndex,
905 &currentProperty);
907 currentProperty.dirProperty = PROPERTY_NULL;
908 currentProperty.propertyType = PROPTYPE_STORAGE;
909 StorageImpl_WriteProperty(
910 This->ancestorStorage,
911 foundPropertyIndex,
912 &currentProperty);
915 * Invoke Destroy to get rid of the ole property and automatically redo
916 * the linking of its previous and next members...
918 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
921 else
924 * There is no property with the old name
926 return STG_E_FILENOTFOUND;
929 return S_OK;
932 /************************************************************************
933 * Storage32BaseImpl_CreateStream (IStorage)
935 * This method will create a stream object within this storage
937 * See Windows documentation for more details on IStorage methods.
939 static HRESULT WINAPI StorageBaseImpl_CreateStream(
940 IStorage* iface,
941 const OLECHAR* pwcsName, /* [string][in] */
942 DWORD grfMode, /* [in] */
943 DWORD reserved1, /* [in] */
944 DWORD reserved2, /* [in] */
945 IStream** ppstm) /* [out] */
947 StorageBaseImpl *This = (StorageBaseImpl *)iface;
948 IEnumSTATSTGImpl* propertyEnumeration;
949 StgStreamImpl* newStream;
950 StgProperty currentProperty, newStreamProperty;
951 ULONG foundPropertyIndex, newPropertyIndex;
953 TRACE("(%p, %s, %x, %d, %d, %p)\n",
954 iface, debugstr_w(pwcsName), grfMode,
955 reserved1, reserved2, ppstm);
958 * Validate parameters
960 if (ppstm == 0)
961 return STG_E_INVALIDPOINTER;
963 if (pwcsName == 0)
964 return STG_E_INVALIDNAME;
966 if (reserved1 || reserved2)
967 return STG_E_INVALIDPARAMETER;
970 * Validate the STGM flags
972 if ( FAILED( validateSTGM(grfMode) ))
973 return STG_E_INVALIDFLAG;
975 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
976 return STG_E_INVALIDFLAG;
979 * As documented.
981 if ((grfMode & STGM_DELETEONRELEASE) ||
982 (grfMode & STGM_TRANSACTED))
983 return STG_E_INVALIDFUNCTION;
986 * Check that we're compatible with the parent's storage mode
987 * if not in transacted mode
989 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
990 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
991 return STG_E_ACCESSDENIED;
995 * Initialize the out parameter
997 *ppstm = 0;
1000 * Create a property enumeration to search the properties
1002 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
1003 This->rootPropertySetIndex);
1005 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1006 pwcsName,
1007 &currentProperty);
1009 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1011 if (foundPropertyIndex != PROPERTY_NULL)
1014 * An element with this name already exists
1016 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1018 StgStreamImpl *strm;
1020 LIST_FOR_EACH_ENTRY(strm, &This->strmHead, StgStreamImpl, StrmListEntry)
1022 if (strm->ownerProperty == foundPropertyIndex)
1024 TRACE("Stream deleted %p\n", strm);
1025 strm->parentStorage = NULL;
1026 list_remove(&strm->StrmListEntry);
1029 IStorage_DestroyElement(iface, pwcsName);
1031 else
1032 return STG_E_FILEALREADYEXISTS;
1034 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1036 WARN("read-only storage\n");
1037 return STG_E_ACCESSDENIED;
1041 * memset the empty property
1043 memset(&newStreamProperty, 0, sizeof(StgProperty));
1045 newStreamProperty.sizeOfNameString =
1046 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1048 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1049 return STG_E_INVALIDNAME;
1051 strcpyW(newStreamProperty.name, pwcsName);
1053 newStreamProperty.propertyType = PROPTYPE_STREAM;
1054 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1055 newStreamProperty.size.u.LowPart = 0;
1056 newStreamProperty.size.u.HighPart = 0;
1058 newStreamProperty.previousProperty = PROPERTY_NULL;
1059 newStreamProperty.nextProperty = PROPERTY_NULL;
1060 newStreamProperty.dirProperty = PROPERTY_NULL;
1062 /* call CoFileTime to get the current time
1063 newStreamProperty.timeStampS1
1064 newStreamProperty.timeStampD1
1065 newStreamProperty.timeStampS2
1066 newStreamProperty.timeStampD2
1069 /* newStreamProperty.propertyUniqueID */
1072 * Get a free property or create a new one
1074 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1077 * Save the new property into the new property spot
1079 StorageImpl_WriteProperty(
1080 This->ancestorStorage,
1081 newPropertyIndex,
1082 &newStreamProperty);
1085 * Find a spot in the property chain for our newly created property.
1087 updatePropertyChain(
1088 (StorageImpl*)This,
1089 newPropertyIndex,
1090 newStreamProperty);
1093 * Open the stream to return it.
1095 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1097 if (newStream != 0)
1099 *ppstm = (IStream*)newStream;
1102 * Since we are returning a pointer to the interface, we have to nail down
1103 * the reference.
1105 IStream_AddRef(*ppstm);
1107 else
1109 return STG_E_INSUFFICIENTMEMORY;
1112 return S_OK;
1115 /************************************************************************
1116 * Storage32BaseImpl_SetClass (IStorage)
1118 * This method will write the specified CLSID in the property of this
1119 * storage.
1121 * See Windows documentation for more details on IStorage methods.
1123 static HRESULT WINAPI StorageBaseImpl_SetClass(
1124 IStorage* iface,
1125 REFCLSID clsid) /* [in] */
1127 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1128 HRESULT hRes = E_FAIL;
1129 StgProperty curProperty;
1130 BOOL success;
1132 TRACE("(%p, %p)\n", iface, clsid);
1134 success = StorageImpl_ReadProperty(This->ancestorStorage,
1135 This->rootPropertySetIndex,
1136 &curProperty);
1137 if (success)
1139 curProperty.propertyUniqueID = *clsid;
1141 success = StorageImpl_WriteProperty(This->ancestorStorage,
1142 This->rootPropertySetIndex,
1143 &curProperty);
1144 if (success)
1145 hRes = S_OK;
1148 return hRes;
1151 /************************************************************************
1152 ** Storage32Impl implementation
1155 /************************************************************************
1156 * Storage32Impl_CreateStorage (IStorage)
1158 * This method will create the storage object within the provided storage.
1160 * See Windows documentation for more details on IStorage methods.
1162 static HRESULT WINAPI StorageImpl_CreateStorage(
1163 IStorage* iface,
1164 const OLECHAR *pwcsName, /* [string][in] */
1165 DWORD grfMode, /* [in] */
1166 DWORD reserved1, /* [in] */
1167 DWORD reserved2, /* [in] */
1168 IStorage **ppstg) /* [out] */
1170 StorageImpl* const This=(StorageImpl*)iface;
1172 IEnumSTATSTGImpl *propertyEnumeration;
1173 StgProperty currentProperty;
1174 StgProperty newProperty;
1175 ULONG foundPropertyIndex;
1176 ULONG newPropertyIndex;
1177 HRESULT hr;
1179 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1180 iface, debugstr_w(pwcsName), grfMode,
1181 reserved1, reserved2, ppstg);
1184 * Validate parameters
1186 if (ppstg == 0)
1187 return STG_E_INVALIDPOINTER;
1189 if (pwcsName == 0)
1190 return STG_E_INVALIDNAME;
1193 * Initialize the out parameter
1195 *ppstg = NULL;
1198 * Validate the STGM flags
1200 if ( FAILED( validateSTGM(grfMode) ) ||
1201 (grfMode & STGM_DELETEONRELEASE) )
1203 WARN("bad grfMode: 0x%x\n", grfMode);
1204 return STG_E_INVALIDFLAG;
1208 * Check that we're compatible with the parent's storage mode
1210 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1212 WARN("access denied\n");
1213 return STG_E_ACCESSDENIED;
1217 * Create a property enumeration and search the properties
1219 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1220 This->base.rootPropertySetIndex);
1222 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1223 pwcsName,
1224 &currentProperty);
1225 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1227 if (foundPropertyIndex != PROPERTY_NULL)
1230 * An element with this name already exists
1232 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1233 IStorage_DestroyElement(iface, pwcsName);
1234 else
1236 WARN("file already exists\n");
1237 return STG_E_FILEALREADYEXISTS;
1240 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1242 WARN("read-only storage\n");
1243 return STG_E_ACCESSDENIED;
1247 * memset the empty property
1249 memset(&newProperty, 0, sizeof(StgProperty));
1251 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1253 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1255 FIXME("name too long\n");
1256 return STG_E_INVALIDNAME;
1259 strcpyW(newProperty.name, pwcsName);
1261 newProperty.propertyType = PROPTYPE_STORAGE;
1262 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1263 newProperty.size.u.LowPart = 0;
1264 newProperty.size.u.HighPart = 0;
1266 newProperty.previousProperty = PROPERTY_NULL;
1267 newProperty.nextProperty = PROPERTY_NULL;
1268 newProperty.dirProperty = PROPERTY_NULL;
1270 /* call CoFileTime to get the current time
1271 newProperty.timeStampS1
1272 newProperty.timeStampD1
1273 newProperty.timeStampS2
1274 newProperty.timeStampD2
1277 /* newStorageProperty.propertyUniqueID */
1280 * Obtain a free property in the property chain
1282 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1285 * Save the new property into the new property spot
1287 StorageImpl_WriteProperty(
1288 This->base.ancestorStorage,
1289 newPropertyIndex,
1290 &newProperty);
1293 * Find a spot in the property chain for our newly created property.
1295 updatePropertyChain(
1296 This,
1297 newPropertyIndex,
1298 newProperty);
1301 * Open it to get a pointer to return.
1303 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1305 if( (hr != S_OK) || (*ppstg == NULL))
1307 return hr;
1311 return S_OK;
1315 /***************************************************************************
1317 * Internal Method
1319 * Get a free property or create a new one.
1321 static ULONG getFreeProperty(
1322 StorageImpl *storage)
1324 ULONG currentPropertyIndex = 0;
1325 ULONG newPropertyIndex = PROPERTY_NULL;
1326 BOOL readSuccessful = TRUE;
1327 StgProperty currentProperty;
1332 * Start by reading the root property
1334 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1335 currentPropertyIndex,
1336 &currentProperty);
1337 if (readSuccessful)
1339 if (currentProperty.sizeOfNameString == 0)
1342 * The property existis and is available, we found it.
1344 newPropertyIndex = currentPropertyIndex;
1347 else
1350 * We exhausted the property list, we will create more space below
1352 newPropertyIndex = currentPropertyIndex;
1354 currentPropertyIndex++;
1356 } while (newPropertyIndex == PROPERTY_NULL);
1359 * grow the property chain
1361 if (! readSuccessful)
1363 StgProperty emptyProperty;
1364 ULARGE_INTEGER newSize;
1365 ULONG propertyIndex;
1366 ULONG lastProperty = 0;
1367 ULONG blockCount = 0;
1370 * obtain the new count of property blocks
1372 blockCount = BlockChainStream_GetCount(
1373 storage->base.ancestorStorage->rootBlockChain)+1;
1376 * initialize the size used by the property stream
1378 newSize.u.HighPart = 0;
1379 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1382 * add a property block to the property chain
1384 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1387 * memset the empty property in order to initialize the unused newly
1388 * created property
1390 memset(&emptyProperty, 0, sizeof(StgProperty));
1393 * initialize them
1395 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1397 for(
1398 propertyIndex = newPropertyIndex;
1399 propertyIndex < lastProperty;
1400 propertyIndex++)
1402 StorageImpl_WriteProperty(
1403 storage->base.ancestorStorage,
1404 propertyIndex,
1405 &emptyProperty);
1409 return newPropertyIndex;
1412 /****************************************************************************
1414 * Internal Method
1416 * Case insensitive comparaison of StgProperty.name by first considering
1417 * their size.
1419 * Returns <0 when newPrpoerty < currentProperty
1420 * >0 when newPrpoerty > currentProperty
1421 * 0 when newPrpoerty == currentProperty
1423 static LONG propertyNameCmp(
1424 const OLECHAR *newProperty,
1425 const OLECHAR *currentProperty)
1427 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1429 if (diff == 0)
1432 * We compare the string themselves only when they are of the same length
1434 diff = lstrcmpiW( newProperty, currentProperty);
1437 return diff;
1440 /****************************************************************************
1442 * Internal Method
1444 * Properly link this new element in the property chain.
1446 static void updatePropertyChain(
1447 StorageImpl *storage,
1448 ULONG newPropertyIndex,
1449 StgProperty newProperty)
1451 StgProperty currentProperty;
1454 * Read the root property
1456 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1457 storage->base.rootPropertySetIndex,
1458 &currentProperty);
1460 if (currentProperty.dirProperty != PROPERTY_NULL)
1463 * The root storage contains some element, therefore, start the research
1464 * for the appropriate location.
1466 BOOL found = 0;
1467 ULONG current, next, previous, currentPropertyId;
1470 * Keep the StgProperty sequence number of the storage first property
1472 currentPropertyId = currentProperty.dirProperty;
1475 * Read
1477 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1478 currentProperty.dirProperty,
1479 &currentProperty);
1481 previous = currentProperty.previousProperty;
1482 next = currentProperty.nextProperty;
1483 current = currentPropertyId;
1485 while (found == 0)
1487 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1489 if (diff < 0)
1491 if (previous != PROPERTY_NULL)
1493 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1494 previous,
1495 &currentProperty);
1496 current = previous;
1498 else
1500 currentProperty.previousProperty = newPropertyIndex;
1501 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1502 current,
1503 &currentProperty);
1504 found = 1;
1507 else if (diff > 0)
1509 if (next != PROPERTY_NULL)
1511 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1512 next,
1513 &currentProperty);
1514 current = next;
1516 else
1518 currentProperty.nextProperty = newPropertyIndex;
1519 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1520 current,
1521 &currentProperty);
1522 found = 1;
1525 else
1528 * Trying to insert an item with the same name in the
1529 * subtree structure.
1531 assert(FALSE);
1534 previous = currentProperty.previousProperty;
1535 next = currentProperty.nextProperty;
1538 else
1541 * The root storage is empty, link the new property to its dir property
1543 currentProperty.dirProperty = newPropertyIndex;
1544 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1545 storage->base.rootPropertySetIndex,
1546 &currentProperty);
1551 /*************************************************************************
1552 * CopyTo (IStorage)
1554 static HRESULT WINAPI StorageImpl_CopyTo(
1555 IStorage* iface,
1556 DWORD ciidExclude, /* [in] */
1557 const IID* rgiidExclude, /* [size_is][unique][in] */
1558 SNB snbExclude, /* [unique][in] */
1559 IStorage* pstgDest) /* [unique][in] */
1561 IEnumSTATSTG *elements = 0;
1562 STATSTG curElement, strStat;
1563 HRESULT hr;
1564 IStorage *pstgTmp, *pstgChild;
1565 IStream *pstrTmp, *pstrChild;
1567 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1568 FIXME("Exclude option not implemented\n");
1570 TRACE("(%p, %d, %p, %p, %p)\n",
1571 iface, ciidExclude, rgiidExclude,
1572 snbExclude, pstgDest);
1575 * Perform a sanity check
1577 if ( pstgDest == 0 )
1578 return STG_E_INVALIDPOINTER;
1581 * Enumerate the elements
1583 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1585 if ( hr != S_OK )
1586 return hr;
1589 * set the class ID
1591 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1592 IStorage_SetClass( pstgDest, &curElement.clsid );
1597 * Obtain the next element
1599 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1601 if ( hr == S_FALSE )
1603 hr = S_OK; /* done, every element has been copied */
1604 break;
1607 if (curElement.type == STGTY_STORAGE)
1610 * open child source storage
1612 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1613 STGM_READ|STGM_SHARE_EXCLUSIVE,
1614 NULL, 0, &pstgChild );
1616 if (hr != S_OK)
1617 break;
1620 * Check if destination storage is not a child of the source
1621 * storage, which will cause an infinite loop
1623 if (pstgChild == pstgDest)
1625 IEnumSTATSTG_Release(elements);
1627 return STG_E_ACCESSDENIED;
1631 * create a new storage in destination storage
1633 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1634 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1635 0, 0,
1636 &pstgTmp );
1638 * if it already exist, don't create a new one use this one
1640 if (hr == STG_E_FILEALREADYEXISTS)
1642 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1643 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1644 NULL, 0, &pstgTmp );
1647 if (hr != S_OK)
1648 break;
1652 * do the copy recursively
1654 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1655 snbExclude, pstgTmp );
1657 IStorage_Release( pstgTmp );
1658 IStorage_Release( pstgChild );
1660 else if (curElement.type == STGTY_STREAM)
1663 * create a new stream in destination storage. If the stream already
1664 * exist, it will be deleted and a new one will be created.
1666 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1667 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1668 0, 0, &pstrTmp );
1670 if (hr != S_OK)
1671 break;
1674 * open child stream storage
1676 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1677 STGM_READ|STGM_SHARE_EXCLUSIVE,
1678 0, &pstrChild );
1680 if (hr != S_OK)
1681 break;
1684 * Get the size of the source stream
1686 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1689 * Set the size of the destination stream.
1691 IStream_SetSize(pstrTmp, strStat.cbSize);
1694 * do the copy
1696 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1697 NULL, NULL );
1699 IStream_Release( pstrTmp );
1700 IStream_Release( pstrChild );
1702 else
1704 WARN("unknown element type: %d\n", curElement.type);
1707 } while (hr == S_OK);
1710 * Clean-up
1712 IEnumSTATSTG_Release(elements);
1714 return hr;
1717 /*************************************************************************
1718 * MoveElementTo (IStorage)
1720 static HRESULT WINAPI StorageImpl_MoveElementTo(
1721 IStorage* iface,
1722 const OLECHAR *pwcsName, /* [string][in] */
1723 IStorage *pstgDest, /* [unique][in] */
1724 const OLECHAR *pwcsNewName,/* [string][in] */
1725 DWORD grfFlags) /* [in] */
1727 FIXME("(%p %s %p %s %u): stub\n", iface,
1728 debugstr_w(pwcsName), pstgDest,
1729 debugstr_w(pwcsNewName), grfFlags);
1730 return E_NOTIMPL;
1733 /*************************************************************************
1734 * Commit (IStorage)
1736 * Ensures that any changes made to a storage object open in transacted mode
1737 * are reflected in the parent storage
1739 * NOTES
1740 * Wine doesn't implement transacted mode, which seems to be a basic
1741 * optimization, so we can ignore this stub for now.
1743 static HRESULT WINAPI StorageImpl_Commit(
1744 IStorage* iface,
1745 DWORD grfCommitFlags)/* [in] */
1747 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1748 return S_OK;
1751 /*************************************************************************
1752 * Revert (IStorage)
1754 * Discard all changes that have been made since the last commit operation
1756 static HRESULT WINAPI StorageImpl_Revert(
1757 IStorage* iface)
1759 FIXME("(%p): stub\n", iface);
1760 return E_NOTIMPL;
1763 /*************************************************************************
1764 * DestroyElement (IStorage)
1766 * Strategy: This implementation is built this way for simplicity not for speed.
1767 * I always delete the topmost element of the enumeration and adjust
1768 * the deleted element pointer all the time. This takes longer to
1769 * do but allow to reinvoke DestroyElement whenever we encounter a
1770 * storage object. The optimisation resides in the usage of another
1771 * enumeration strategy that would give all the leaves of a storage
1772 * first. (postfix order)
1774 static HRESULT WINAPI StorageImpl_DestroyElement(
1775 IStorage* iface,
1776 const OLECHAR *pwcsName)/* [string][in] */
1778 StorageImpl* const This=(StorageImpl*)iface;
1780 IEnumSTATSTGImpl* propertyEnumeration;
1781 HRESULT hr = S_OK;
1782 BOOL res;
1783 StgProperty propertyToDelete;
1784 StgProperty parentProperty;
1785 ULONG foundPropertyIndexToDelete;
1786 ULONG typeOfRelation;
1787 ULONG parentPropertyId = 0;
1789 TRACE("(%p, %s)\n",
1790 iface, debugstr_w(pwcsName));
1793 * Perform a sanity check on the parameters.
1795 if (pwcsName==NULL)
1796 return STG_E_INVALIDPOINTER;
1799 * Create a property enumeration to search the property with the given name
1801 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1802 This->base.ancestorStorage,
1803 This->base.rootPropertySetIndex);
1805 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1806 propertyEnumeration,
1807 pwcsName,
1808 &propertyToDelete);
1810 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1812 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1814 return STG_E_FILENOTFOUND;
1818 * Find the parent property of the property to delete (the one that
1819 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1820 * the parent is This. Otherwise, the parent is one of its sibling...
1824 * First, read This's StgProperty..
1826 res = StorageImpl_ReadProperty(
1827 This->base.ancestorStorage,
1828 This->base.rootPropertySetIndex,
1829 &parentProperty);
1831 assert(res);
1834 * Second, check to see if by any chance the actual storage (This) is not
1835 * the parent of the property to delete... We never know...
1837 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1840 * Set data as it would have been done in the else part...
1842 typeOfRelation = PROPERTY_RELATION_DIR;
1843 parentPropertyId = This->base.rootPropertySetIndex;
1845 else
1848 * Create a property enumeration to search the parent properties, and
1849 * delete it once done.
1851 IEnumSTATSTGImpl* propertyEnumeration2;
1853 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1854 This->base.ancestorStorage,
1855 This->base.rootPropertySetIndex);
1857 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1858 propertyEnumeration2,
1859 foundPropertyIndexToDelete,
1860 &parentProperty,
1861 &parentPropertyId);
1863 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1866 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1868 hr = deleteStorageProperty(
1869 This,
1870 foundPropertyIndexToDelete,
1871 propertyToDelete);
1873 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1875 hr = deleteStreamProperty(
1876 This,
1877 foundPropertyIndexToDelete,
1878 propertyToDelete);
1881 if (hr!=S_OK)
1882 return hr;
1885 * Adjust the property chain
1887 hr = adjustPropertyChain(
1888 This,
1889 propertyToDelete,
1890 parentProperty,
1891 parentPropertyId,
1892 typeOfRelation);
1894 return hr;
1898 /************************************************************************
1899 * StorageImpl_Stat (IStorage)
1901 * This method will retrieve information about this storage object.
1903 * See Windows documentation for more details on IStorage methods.
1905 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1906 STATSTG* pstatstg, /* [out] */
1907 DWORD grfStatFlag) /* [in] */
1909 StorageImpl* const This = (StorageImpl*)iface;
1910 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1912 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1914 CoTaskMemFree(pstatstg->pwcsName);
1915 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1916 strcpyW(pstatstg->pwcsName, This->pwcsName);
1919 return result;
1922 /******************************************************************************
1923 * Internal stream list handlers
1926 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1928 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1929 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1932 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1934 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1935 list_remove(&(strm->StrmListEntry));
1938 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1940 struct list *cur, *cur2;
1941 StgStreamImpl *strm=NULL;
1943 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1944 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1945 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1946 strm->parentStorage = NULL;
1947 list_remove(cur);
1952 /*********************************************************************
1954 * Internal Method
1956 * Perform the deletion of a complete storage node
1959 static HRESULT deleteStorageProperty(
1960 StorageImpl *parentStorage,
1961 ULONG indexOfPropertyToDelete,
1962 StgProperty propertyToDelete)
1964 IEnumSTATSTG *elements = 0;
1965 IStorage *childStorage = 0;
1966 STATSTG currentElement;
1967 HRESULT hr;
1968 HRESULT destroyHr = S_OK;
1971 * Open the storage and enumerate it
1973 hr = StorageBaseImpl_OpenStorage(
1974 (IStorage*)parentStorage,
1975 propertyToDelete.name,
1977 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1980 &childStorage);
1982 if (hr != S_OK)
1984 return hr;
1988 * Enumerate the elements
1990 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1995 * Obtain the next element
1997 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1998 if (hr==S_OK)
2000 destroyHr = StorageImpl_DestroyElement(childStorage, currentElement.pwcsName);
2002 CoTaskMemFree(currentElement.pwcsName);
2006 * We need to Reset the enumeration every time because we delete elements
2007 * and the enumeration could be invalid
2009 IEnumSTATSTG_Reset(elements);
2011 } while ((hr == S_OK) && (destroyHr == S_OK));
2014 * Invalidate the property by zeroing its name member.
2016 propertyToDelete.sizeOfNameString = 0;
2018 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
2019 indexOfPropertyToDelete,
2020 &propertyToDelete);
2022 IStorage_Release(childStorage);
2023 IEnumSTATSTG_Release(elements);
2025 return destroyHr;
2028 /*********************************************************************
2030 * Internal Method
2032 * Perform the deletion of a stream node
2035 static HRESULT deleteStreamProperty(
2036 StorageImpl *parentStorage,
2037 ULONG indexOfPropertyToDelete,
2038 StgProperty propertyToDelete)
2040 IStream *pis;
2041 HRESULT hr;
2042 ULARGE_INTEGER size;
2044 size.u.HighPart = 0;
2045 size.u.LowPart = 0;
2047 hr = StorageBaseImpl_OpenStream(
2048 (IStorage*)parentStorage,
2049 (OLECHAR*)propertyToDelete.name,
2050 NULL,
2051 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2053 &pis);
2055 if (hr!=S_OK)
2057 return(hr);
2061 * Zap the stream
2063 hr = IStream_SetSize(pis, size);
2065 if(hr != S_OK)
2067 return hr;
2071 * Release the stream object.
2073 IStream_Release(pis);
2076 * Invalidate the property by zeroing its name member.
2078 propertyToDelete.sizeOfNameString = 0;
2081 * Here we should re-read the property so we get the updated pointer
2082 * but since we are here to zap it, I don't do it...
2084 StorageImpl_WriteProperty(
2085 parentStorage->base.ancestorStorage,
2086 indexOfPropertyToDelete,
2087 &propertyToDelete);
2089 return S_OK;
2092 /*********************************************************************
2094 * Internal Method
2096 * Finds a placeholder for the StgProperty within the Storage
2099 static HRESULT findPlaceholder(
2100 StorageImpl *storage,
2101 ULONG propertyIndexToStore,
2102 ULONG storePropertyIndex,
2103 INT typeOfRelation)
2105 StgProperty storeProperty;
2106 HRESULT hr = S_OK;
2107 BOOL res = TRUE;
2110 * Read the storage property
2112 res = StorageImpl_ReadProperty(
2113 storage->base.ancestorStorage,
2114 storePropertyIndex,
2115 &storeProperty);
2117 if(! res)
2119 return E_FAIL;
2122 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2124 if (storeProperty.previousProperty != PROPERTY_NULL)
2126 return findPlaceholder(
2127 storage,
2128 propertyIndexToStore,
2129 storeProperty.previousProperty,
2130 typeOfRelation);
2132 else
2134 storeProperty.previousProperty = propertyIndexToStore;
2137 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2139 if (storeProperty.nextProperty != PROPERTY_NULL)
2141 return findPlaceholder(
2142 storage,
2143 propertyIndexToStore,
2144 storeProperty.nextProperty,
2145 typeOfRelation);
2147 else
2149 storeProperty.nextProperty = propertyIndexToStore;
2152 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2154 if (storeProperty.dirProperty != PROPERTY_NULL)
2156 return findPlaceholder(
2157 storage,
2158 propertyIndexToStore,
2159 storeProperty.dirProperty,
2160 typeOfRelation);
2162 else
2164 storeProperty.dirProperty = propertyIndexToStore;
2168 hr = StorageImpl_WriteProperty(
2169 storage->base.ancestorStorage,
2170 storePropertyIndex,
2171 &storeProperty);
2173 if(! hr)
2175 return E_FAIL;
2178 return S_OK;
2181 /*************************************************************************
2183 * Internal Method
2185 * This method takes the previous and the next property link of a property
2186 * to be deleted and find them a place in the Storage.
2188 static HRESULT adjustPropertyChain(
2189 StorageImpl *This,
2190 StgProperty propertyToDelete,
2191 StgProperty parentProperty,
2192 ULONG parentPropertyId,
2193 INT typeOfRelation)
2195 ULONG newLinkProperty = PROPERTY_NULL;
2196 BOOL needToFindAPlaceholder = FALSE;
2197 ULONG storeNode = PROPERTY_NULL;
2198 ULONG toStoreNode = PROPERTY_NULL;
2199 INT relationType = 0;
2200 HRESULT hr = S_OK;
2201 BOOL res = TRUE;
2203 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2205 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2208 * Set the parent previous to the property to delete previous
2210 newLinkProperty = propertyToDelete.previousProperty;
2212 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2215 * We also need to find a storage for the other link, setup variables
2216 * to do this at the end...
2218 needToFindAPlaceholder = TRUE;
2219 storeNode = propertyToDelete.previousProperty;
2220 toStoreNode = propertyToDelete.nextProperty;
2221 relationType = PROPERTY_RELATION_NEXT;
2224 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2227 * Set the parent previous to the property to delete next
2229 newLinkProperty = propertyToDelete.nextProperty;
2233 * Link it for real...
2235 parentProperty.previousProperty = newLinkProperty;
2238 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2240 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2243 * Set the parent next to the property to delete next previous
2245 newLinkProperty = propertyToDelete.previousProperty;
2247 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2250 * We also need to find a storage for the other link, setup variables
2251 * to do this at the end...
2253 needToFindAPlaceholder = TRUE;
2254 storeNode = propertyToDelete.previousProperty;
2255 toStoreNode = propertyToDelete.nextProperty;
2256 relationType = PROPERTY_RELATION_NEXT;
2259 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2262 * Set the parent next to the property to delete next
2264 newLinkProperty = propertyToDelete.nextProperty;
2268 * Link it for real...
2270 parentProperty.nextProperty = newLinkProperty;
2272 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2274 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2277 * Set the parent dir to the property to delete previous
2279 newLinkProperty = propertyToDelete.previousProperty;
2281 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2284 * We also need to find a storage for the other link, setup variables
2285 * to do this at the end...
2287 needToFindAPlaceholder = TRUE;
2288 storeNode = propertyToDelete.previousProperty;
2289 toStoreNode = propertyToDelete.nextProperty;
2290 relationType = PROPERTY_RELATION_NEXT;
2293 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2296 * Set the parent dir to the property to delete next
2298 newLinkProperty = propertyToDelete.nextProperty;
2302 * Link it for real...
2304 parentProperty.dirProperty = newLinkProperty;
2308 * Write back the parent property
2310 res = StorageImpl_WriteProperty(
2311 This->base.ancestorStorage,
2312 parentPropertyId,
2313 &parentProperty);
2314 if(! res)
2316 return E_FAIL;
2320 * If a placeholder is required for the other link, then, find one and
2321 * get out of here...
2323 if (needToFindAPlaceholder)
2325 hr = findPlaceholder(
2326 This,
2327 toStoreNode,
2328 storeNode,
2329 relationType);
2332 return hr;
2336 /******************************************************************************
2337 * SetElementTimes (IStorage)
2339 static HRESULT WINAPI StorageImpl_SetElementTimes(
2340 IStorage* iface,
2341 const OLECHAR *pwcsName,/* [string][in] */
2342 const FILETIME *pctime, /* [in] */
2343 const FILETIME *patime, /* [in] */
2344 const FILETIME *pmtime) /* [in] */
2346 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2347 return S_OK;
2350 /******************************************************************************
2351 * SetStateBits (IStorage)
2353 static HRESULT WINAPI StorageImpl_SetStateBits(
2354 IStorage* iface,
2355 DWORD grfStateBits,/* [in] */
2356 DWORD grfMask) /* [in] */
2358 StorageImpl* const This = (StorageImpl*)iface;
2359 This->base.stateBits = (This->base.stateBits & ~grfMask) | (grfStateBits & grfMask);
2360 return S_OK;
2364 * Virtual function table for the IStorage32Impl class.
2366 static const IStorageVtbl Storage32Impl_Vtbl =
2368 StorageBaseImpl_QueryInterface,
2369 StorageBaseImpl_AddRef,
2370 StorageBaseImpl_Release,
2371 StorageBaseImpl_CreateStream,
2372 StorageBaseImpl_OpenStream,
2373 StorageImpl_CreateStorage,
2374 StorageBaseImpl_OpenStorage,
2375 StorageImpl_CopyTo,
2376 StorageImpl_MoveElementTo,
2377 StorageImpl_Commit,
2378 StorageImpl_Revert,
2379 StorageBaseImpl_EnumElements,
2380 StorageImpl_DestroyElement,
2381 StorageBaseImpl_RenameElement,
2382 StorageImpl_SetElementTimes,
2383 StorageBaseImpl_SetClass,
2384 StorageImpl_SetStateBits,
2385 StorageImpl_Stat
2388 static HRESULT StorageImpl_Construct(
2389 StorageImpl* This,
2390 HANDLE hFile,
2391 LPCOLESTR pwcsName,
2392 ILockBytes* pLkbyt,
2393 DWORD openFlags,
2394 BOOL fileBased,
2395 BOOL fileCreate)
2397 HRESULT hr = S_OK;
2398 StgProperty currentProperty;
2399 BOOL readSuccessful;
2400 ULONG currentPropertyIndex;
2402 if ( FAILED( validateSTGM(openFlags) ))
2403 return STG_E_INVALIDFLAG;
2405 memset(This, 0, sizeof(StorageImpl));
2408 * Initialize stream list
2411 list_init(&This->base.strmHead);
2414 * Initialize the virtual function table.
2416 This->base.lpVtbl = &Storage32Impl_Vtbl;
2417 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2418 This->base.v_destructor = &StorageImpl_Destroy;
2419 This->base.openFlags = (openFlags & ~STGM_CREATE);
2422 * This is the top-level storage so initialize the ancestor pointer
2423 * to this.
2425 This->base.ancestorStorage = This;
2428 * Initialize the physical support of the storage.
2430 This->hFile = hFile;
2433 * Store copy of file path.
2435 if(pwcsName) {
2436 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2437 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2438 if (!This->pwcsName)
2439 return STG_E_INSUFFICIENTMEMORY;
2440 strcpyW(This->pwcsName, pwcsName);
2444 * Initialize the big block cache.
2446 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2447 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2448 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2449 pLkbyt,
2450 openFlags,
2451 This->bigBlockSize,
2452 fileBased);
2454 if (This->bigBlockFile == 0)
2455 return E_FAIL;
2457 if (fileCreate)
2459 ULARGE_INTEGER size;
2460 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2463 * Initialize all header variables:
2464 * - The big block depot consists of one block and it is at block 0
2465 * - The properties start at block 1
2466 * - There is no small block depot
2468 memset( This->bigBlockDepotStart,
2469 BLOCK_UNUSED,
2470 sizeof(This->bigBlockDepotStart));
2472 This->bigBlockDepotCount = 1;
2473 This->bigBlockDepotStart[0] = 0;
2474 This->rootStartBlock = 1;
2475 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2476 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2477 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2478 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2479 This->extBigBlockDepotCount = 0;
2481 StorageImpl_SaveFileHeader(This);
2484 * Add one block for the big block depot and one block for the properties
2486 size.u.HighPart = 0;
2487 size.u.LowPart = This->bigBlockSize * 3;
2488 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2491 * Initialize the big block depot
2493 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2494 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2495 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2496 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2498 else
2501 * Load the header for the file.
2503 hr = StorageImpl_LoadFileHeader(This);
2505 if (FAILED(hr))
2507 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2509 return hr;
2514 * There is no block depot cached yet.
2516 This->indexBlockDepotCached = 0xFFFFFFFF;
2519 * Start searching for free blocks with block 0.
2521 This->prevFreeBlock = 0;
2524 * Create the block chain abstractions.
2526 if(!(This->rootBlockChain =
2527 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2528 return STG_E_READFAULT;
2530 if(!(This->smallBlockDepotChain =
2531 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2532 PROPERTY_NULL)))
2533 return STG_E_READFAULT;
2536 * Write the root property (memory only)
2538 if (fileCreate)
2540 StgProperty rootProp;
2542 * Initialize the property chain
2544 memset(&rootProp, 0, sizeof(rootProp));
2545 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2546 sizeof(rootProp.name)/sizeof(WCHAR) );
2547 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2548 rootProp.propertyType = PROPTYPE_ROOT;
2549 rootProp.previousProperty = PROPERTY_NULL;
2550 rootProp.nextProperty = PROPERTY_NULL;
2551 rootProp.dirProperty = PROPERTY_NULL;
2552 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2553 rootProp.size.u.HighPart = 0;
2554 rootProp.size.u.LowPart = 0;
2556 StorageImpl_WriteProperty(This, 0, &rootProp);
2560 * Find the ID of the root in the property sets.
2562 currentPropertyIndex = 0;
2566 readSuccessful = StorageImpl_ReadProperty(
2567 This,
2568 currentPropertyIndex,
2569 &currentProperty);
2571 if (readSuccessful)
2573 if ( (currentProperty.sizeOfNameString != 0 ) &&
2574 (currentProperty.propertyType == PROPTYPE_ROOT) )
2576 This->base.rootPropertySetIndex = currentPropertyIndex;
2580 currentPropertyIndex++;
2582 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2584 if (!readSuccessful)
2586 /* TODO CLEANUP */
2587 return STG_E_READFAULT;
2591 * Create the block chain abstraction for the small block root chain.
2593 if(!(This->smallBlockRootChain =
2594 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2595 return STG_E_READFAULT;
2597 return hr;
2600 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2602 StorageImpl *This = (StorageImpl*) iface;
2603 TRACE("(%p)\n", This);
2605 StorageBaseImpl_DeleteAll(&This->base);
2607 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2609 BlockChainStream_Destroy(This->smallBlockRootChain);
2610 BlockChainStream_Destroy(This->rootBlockChain);
2611 BlockChainStream_Destroy(This->smallBlockDepotChain);
2613 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2614 HeapFree(GetProcessHeap(), 0, This);
2617 /******************************************************************************
2618 * Storage32Impl_GetNextFreeBigBlock
2620 * Returns the index of the next free big block.
2621 * If the big block depot is filled, this method will enlarge it.
2624 static ULONG StorageImpl_GetNextFreeBigBlock(
2625 StorageImpl* This)
2627 ULONG depotBlockIndexPos;
2628 BYTE depotBuffer[BIG_BLOCK_SIZE];
2629 BOOL success;
2630 ULONG depotBlockOffset;
2631 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2632 ULONG nextBlockIndex = BLOCK_SPECIAL;
2633 int depotIndex = 0;
2634 ULONG freeBlock = BLOCK_UNUSED;
2636 depotIndex = This->prevFreeBlock / blocksPerDepot;
2637 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2640 * Scan the entire big block depot until we find a block marked free
2642 while (nextBlockIndex != BLOCK_UNUSED)
2644 if (depotIndex < COUNT_BBDEPOTINHEADER)
2646 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2649 * Grow the primary depot.
2651 if (depotBlockIndexPos == BLOCK_UNUSED)
2653 depotBlockIndexPos = depotIndex*blocksPerDepot;
2656 * Add a block depot.
2658 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2659 This->bigBlockDepotCount++;
2660 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2663 * Flag it as a block depot.
2665 StorageImpl_SetNextBlockInChain(This,
2666 depotBlockIndexPos,
2667 BLOCK_SPECIAL);
2669 /* Save new header information.
2671 StorageImpl_SaveFileHeader(This);
2674 else
2676 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2678 if (depotBlockIndexPos == BLOCK_UNUSED)
2681 * Grow the extended depot.
2683 ULONG extIndex = BLOCK_UNUSED;
2684 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2685 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2687 if (extBlockOffset == 0)
2689 /* We need an extended block.
2691 extIndex = Storage32Impl_AddExtBlockDepot(This);
2692 This->extBigBlockDepotCount++;
2693 depotBlockIndexPos = extIndex + 1;
2695 else
2696 depotBlockIndexPos = depotIndex * blocksPerDepot;
2699 * Add a block depot and mark it in the extended block.
2701 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2702 This->bigBlockDepotCount++;
2703 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2705 /* Flag the block depot.
2707 StorageImpl_SetNextBlockInChain(This,
2708 depotBlockIndexPos,
2709 BLOCK_SPECIAL);
2711 /* If necessary, flag the extended depot block.
2713 if (extIndex != BLOCK_UNUSED)
2714 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2716 /* Save header information.
2718 StorageImpl_SaveFileHeader(This);
2722 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2724 if (success)
2726 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2727 ( nextBlockIndex != BLOCK_UNUSED))
2729 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2731 if (nextBlockIndex == BLOCK_UNUSED)
2733 freeBlock = (depotIndex * blocksPerDepot) +
2734 (depotBlockOffset/sizeof(ULONG));
2737 depotBlockOffset += sizeof(ULONG);
2741 depotIndex++;
2742 depotBlockOffset = 0;
2746 * make sure that the block physically exists before using it
2748 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2750 This->prevFreeBlock = freeBlock;
2752 return freeBlock;
2755 /******************************************************************************
2756 * Storage32Impl_AddBlockDepot
2758 * This will create a depot block, essentially it is a block initialized
2759 * to BLOCK_UNUSEDs.
2761 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2763 BYTE blockBuffer[BIG_BLOCK_SIZE];
2766 * Initialize blocks as free
2768 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2769 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2772 /******************************************************************************
2773 * Storage32Impl_GetExtDepotBlock
2775 * Returns the index of the block that corresponds to the specified depot
2776 * index. This method is only for depot indexes equal or greater than
2777 * COUNT_BBDEPOTINHEADER.
2779 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2781 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2782 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2783 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2784 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2785 ULONG blockIndex = BLOCK_UNUSED;
2786 ULONG extBlockIndex = This->extBigBlockDepotStart;
2788 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2790 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2791 return BLOCK_UNUSED;
2793 while (extBlockCount > 0)
2795 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2796 extBlockCount--;
2799 if (extBlockIndex != BLOCK_UNUSED)
2800 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2801 extBlockOffset * sizeof(ULONG), &blockIndex);
2803 return blockIndex;
2806 /******************************************************************************
2807 * Storage32Impl_SetExtDepotBlock
2809 * Associates the specified block index to the specified depot index.
2810 * This method is only for depot indexes equal or greater than
2811 * COUNT_BBDEPOTINHEADER.
2813 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2815 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2816 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2817 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2818 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2819 ULONG extBlockIndex = This->extBigBlockDepotStart;
2821 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2823 while (extBlockCount > 0)
2825 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2826 extBlockCount--;
2829 if (extBlockIndex != BLOCK_UNUSED)
2831 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2832 extBlockOffset * sizeof(ULONG),
2833 blockIndex);
2837 /******************************************************************************
2838 * Storage32Impl_AddExtBlockDepot
2840 * Creates an extended depot block.
2842 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2844 ULONG numExtBlocks = This->extBigBlockDepotCount;
2845 ULONG nextExtBlock = This->extBigBlockDepotStart;
2846 BYTE depotBuffer[BIG_BLOCK_SIZE];
2847 ULONG index = BLOCK_UNUSED;
2848 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2849 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2850 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2852 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2853 blocksPerDepotBlock;
2855 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2858 * The first extended block.
2860 This->extBigBlockDepotStart = index;
2862 else
2864 unsigned int i;
2866 * Follow the chain to the last one.
2868 for (i = 0; i < (numExtBlocks - 1); i++)
2870 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2874 * Add the new extended block to the chain.
2876 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2877 index);
2881 * Initialize this block.
2883 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2884 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2886 return index;
2889 /******************************************************************************
2890 * Storage32Impl_FreeBigBlock
2892 * This method will flag the specified block as free in the big block depot.
2894 static void StorageImpl_FreeBigBlock(
2895 StorageImpl* This,
2896 ULONG blockIndex)
2898 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2900 if (blockIndex < This->prevFreeBlock)
2901 This->prevFreeBlock = blockIndex;
2904 /************************************************************************
2905 * Storage32Impl_GetNextBlockInChain
2907 * This method will retrieve the block index of the next big block in
2908 * in the chain.
2910 * Params: This - Pointer to the Storage object.
2911 * blockIndex - Index of the block to retrieve the chain
2912 * for.
2913 * nextBlockIndex - receives the return value.
2915 * Returns: This method returns the index of the next block in the chain.
2916 * It will return the constants:
2917 * BLOCK_SPECIAL - If the block given was not part of a
2918 * chain.
2919 * BLOCK_END_OF_CHAIN - If the block given was the last in
2920 * a chain.
2921 * BLOCK_UNUSED - If the block given was not past of a chain
2922 * and is available.
2923 * BLOCK_EXTBBDEPOT - This block is part of the extended
2924 * big block depot.
2926 * See Windows documentation for more details on IStorage methods.
2928 static HRESULT StorageImpl_GetNextBlockInChain(
2929 StorageImpl* This,
2930 ULONG blockIndex,
2931 ULONG* nextBlockIndex)
2933 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2934 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2935 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2936 BYTE depotBuffer[BIG_BLOCK_SIZE];
2937 BOOL success;
2938 ULONG depotBlockIndexPos;
2939 int index;
2941 *nextBlockIndex = BLOCK_SPECIAL;
2943 if(depotBlockCount >= This->bigBlockDepotCount)
2945 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2946 This->bigBlockDepotCount);
2947 return STG_E_READFAULT;
2951 * Cache the currently accessed depot block.
2953 if (depotBlockCount != This->indexBlockDepotCached)
2955 This->indexBlockDepotCached = depotBlockCount;
2957 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2959 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2961 else
2964 * We have to look in the extended depot.
2966 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2969 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2971 if (!success)
2972 return STG_E_READFAULT;
2974 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2976 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2977 This->blockDepotCached[index] = *nextBlockIndex;
2981 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2983 return S_OK;
2986 /******************************************************************************
2987 * Storage32Impl_GetNextExtendedBlock
2989 * Given an extended block this method will return the next extended block.
2991 * NOTES:
2992 * The last ULONG of an extended block is the block index of the next
2993 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2994 * depot.
2996 * Return values:
2997 * - The index of the next extended block
2998 * - BLOCK_UNUSED: there is no next extended block.
2999 * - Any other return values denotes failure.
3001 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3003 ULONG nextBlockIndex = BLOCK_SPECIAL;
3004 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3006 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3007 &nextBlockIndex);
3009 return nextBlockIndex;
3012 /******************************************************************************
3013 * Storage32Impl_SetNextBlockInChain
3015 * This method will write the index of the specified block's next block
3016 * in the big block depot.
3018 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3019 * do the following
3021 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3022 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3023 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3026 static void StorageImpl_SetNextBlockInChain(
3027 StorageImpl* This,
3028 ULONG blockIndex,
3029 ULONG nextBlock)
3031 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3032 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3033 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3034 ULONG depotBlockIndexPos;
3036 assert(depotBlockCount < This->bigBlockDepotCount);
3037 assert(blockIndex != nextBlock);
3039 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3041 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3043 else
3046 * We have to look in the extended depot.
3048 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3051 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3052 nextBlock);
3054 * Update the cached block depot, if necessary.
3056 if (depotBlockCount == This->indexBlockDepotCached)
3058 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3062 /******************************************************************************
3063 * Storage32Impl_LoadFileHeader
3065 * This method will read in the file header, i.e. big block index -1.
3067 static HRESULT StorageImpl_LoadFileHeader(
3068 StorageImpl* This)
3070 HRESULT hr = STG_E_FILENOTFOUND;
3071 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3072 BOOL success;
3073 int index;
3075 TRACE("\n");
3077 * Get a pointer to the big block of data containing the header.
3079 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3082 * Extract the information from the header.
3084 if (success)
3087 * Check for the "magic number" signature and return an error if it is not
3088 * found.
3090 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3092 return STG_E_OLDFORMAT;
3095 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3097 return STG_E_INVALIDHEADER;
3100 StorageUtl_ReadWord(
3101 headerBigBlock,
3102 OFFSET_BIGBLOCKSIZEBITS,
3103 &This->bigBlockSizeBits);
3105 StorageUtl_ReadWord(
3106 headerBigBlock,
3107 OFFSET_SMALLBLOCKSIZEBITS,
3108 &This->smallBlockSizeBits);
3110 StorageUtl_ReadDWord(
3111 headerBigBlock,
3112 OFFSET_BBDEPOTCOUNT,
3113 &This->bigBlockDepotCount);
3115 StorageUtl_ReadDWord(
3116 headerBigBlock,
3117 OFFSET_ROOTSTARTBLOCK,
3118 &This->rootStartBlock);
3120 StorageUtl_ReadDWord(
3121 headerBigBlock,
3122 OFFSET_SBDEPOTSTART,
3123 &This->smallBlockDepotStart);
3125 StorageUtl_ReadDWord(
3126 headerBigBlock,
3127 OFFSET_EXTBBDEPOTSTART,
3128 &This->extBigBlockDepotStart);
3130 StorageUtl_ReadDWord(
3131 headerBigBlock,
3132 OFFSET_EXTBBDEPOTCOUNT,
3133 &This->extBigBlockDepotCount);
3135 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3137 StorageUtl_ReadDWord(
3138 headerBigBlock,
3139 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3140 &(This->bigBlockDepotStart[index]));
3144 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3146 if ((1 << 2) == 4)
3148 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3149 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3151 else
3153 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3154 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3158 * Right now, the code is making some assumptions about the size of the
3159 * blocks, just make sure they are what we're expecting.
3161 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3162 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3164 WARN("Broken OLE storage file\n");
3165 hr = STG_E_INVALIDHEADER;
3167 else
3168 hr = S_OK;
3171 return hr;
3174 /******************************************************************************
3175 * Storage32Impl_SaveFileHeader
3177 * This method will save to the file the header, i.e. big block -1.
3179 static void StorageImpl_SaveFileHeader(
3180 StorageImpl* This)
3182 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3183 int index;
3184 BOOL success;
3187 * Get a pointer to the big block of data containing the header.
3189 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3192 * If the block read failed, the file is probably new.
3194 if (!success)
3197 * Initialize for all unknown fields.
3199 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3202 * Initialize the magic number.
3204 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3207 * And a bunch of things we don't know what they mean
3209 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3210 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3211 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3212 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3216 * Write the information to the header.
3218 StorageUtl_WriteWord(
3219 headerBigBlock,
3220 OFFSET_BIGBLOCKSIZEBITS,
3221 This->bigBlockSizeBits);
3223 StorageUtl_WriteWord(
3224 headerBigBlock,
3225 OFFSET_SMALLBLOCKSIZEBITS,
3226 This->smallBlockSizeBits);
3228 StorageUtl_WriteDWord(
3229 headerBigBlock,
3230 OFFSET_BBDEPOTCOUNT,
3231 This->bigBlockDepotCount);
3233 StorageUtl_WriteDWord(
3234 headerBigBlock,
3235 OFFSET_ROOTSTARTBLOCK,
3236 This->rootStartBlock);
3238 StorageUtl_WriteDWord(
3239 headerBigBlock,
3240 OFFSET_SBDEPOTSTART,
3241 This->smallBlockDepotStart);
3243 StorageUtl_WriteDWord(
3244 headerBigBlock,
3245 OFFSET_SBDEPOTCOUNT,
3246 This->smallBlockDepotChain ?
3247 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3249 StorageUtl_WriteDWord(
3250 headerBigBlock,
3251 OFFSET_EXTBBDEPOTSTART,
3252 This->extBigBlockDepotStart);
3254 StorageUtl_WriteDWord(
3255 headerBigBlock,
3256 OFFSET_EXTBBDEPOTCOUNT,
3257 This->extBigBlockDepotCount);
3259 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3261 StorageUtl_WriteDWord(
3262 headerBigBlock,
3263 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3264 (This->bigBlockDepotStart[index]));
3268 * Write the big block back to the file.
3270 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3273 /******************************************************************************
3274 * Storage32Impl_ReadProperty
3276 * This method will read the specified property from the property chain.
3278 BOOL StorageImpl_ReadProperty(
3279 StorageImpl* This,
3280 ULONG index,
3281 StgProperty* buffer)
3283 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3284 ULARGE_INTEGER offsetInPropSet;
3285 HRESULT readRes;
3286 ULONG bytesRead;
3288 offsetInPropSet.u.HighPart = 0;
3289 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3291 readRes = BlockChainStream_ReadAt(
3292 This->rootBlockChain,
3293 offsetInPropSet,
3294 PROPSET_BLOCK_SIZE,
3295 currentProperty,
3296 &bytesRead);
3298 if (SUCCEEDED(readRes))
3300 /* replace the name of root entry (often "Root Entry") by the file name */
3301 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3302 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3304 memset(buffer->name, 0, sizeof(buffer->name));
3305 memcpy(
3306 buffer->name,
3307 propName,
3308 PROPERTY_NAME_BUFFER_LEN );
3309 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3311 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3313 StorageUtl_ReadWord(
3314 currentProperty,
3315 OFFSET_PS_NAMELENGTH,
3316 &buffer->sizeOfNameString);
3318 StorageUtl_ReadDWord(
3319 currentProperty,
3320 OFFSET_PS_PREVIOUSPROP,
3321 &buffer->previousProperty);
3323 StorageUtl_ReadDWord(
3324 currentProperty,
3325 OFFSET_PS_NEXTPROP,
3326 &buffer->nextProperty);
3328 StorageUtl_ReadDWord(
3329 currentProperty,
3330 OFFSET_PS_DIRPROP,
3331 &buffer->dirProperty);
3333 StorageUtl_ReadGUID(
3334 currentProperty,
3335 OFFSET_PS_GUID,
3336 &buffer->propertyUniqueID);
3338 StorageUtl_ReadDWord(
3339 currentProperty,
3340 OFFSET_PS_TSS1,
3341 &buffer->timeStampS1);
3343 StorageUtl_ReadDWord(
3344 currentProperty,
3345 OFFSET_PS_TSD1,
3346 &buffer->timeStampD1);
3348 StorageUtl_ReadDWord(
3349 currentProperty,
3350 OFFSET_PS_TSS2,
3351 &buffer->timeStampS2);
3353 StorageUtl_ReadDWord(
3354 currentProperty,
3355 OFFSET_PS_TSD2,
3356 &buffer->timeStampD2);
3358 StorageUtl_ReadDWord(
3359 currentProperty,
3360 OFFSET_PS_STARTBLOCK,
3361 &buffer->startingBlock);
3363 StorageUtl_ReadDWord(
3364 currentProperty,
3365 OFFSET_PS_SIZE,
3366 &buffer->size.u.LowPart);
3368 buffer->size.u.HighPart = 0;
3371 return SUCCEEDED(readRes) ? TRUE : FALSE;
3374 /*********************************************************************
3375 * Write the specified property into the property chain
3377 BOOL StorageImpl_WriteProperty(
3378 StorageImpl* This,
3379 ULONG index,
3380 const StgProperty* buffer)
3382 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3383 ULARGE_INTEGER offsetInPropSet;
3384 HRESULT writeRes;
3385 ULONG bytesWritten;
3387 offsetInPropSet.u.HighPart = 0;
3388 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3390 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3392 memcpy(
3393 currentProperty + OFFSET_PS_NAME,
3394 buffer->name,
3395 PROPERTY_NAME_BUFFER_LEN );
3397 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3399 StorageUtl_WriteWord(
3400 currentProperty,
3401 OFFSET_PS_NAMELENGTH,
3402 buffer->sizeOfNameString);
3404 StorageUtl_WriteDWord(
3405 currentProperty,
3406 OFFSET_PS_PREVIOUSPROP,
3407 buffer->previousProperty);
3409 StorageUtl_WriteDWord(
3410 currentProperty,
3411 OFFSET_PS_NEXTPROP,
3412 buffer->nextProperty);
3414 StorageUtl_WriteDWord(
3415 currentProperty,
3416 OFFSET_PS_DIRPROP,
3417 buffer->dirProperty);
3419 StorageUtl_WriteGUID(
3420 currentProperty,
3421 OFFSET_PS_GUID,
3422 &buffer->propertyUniqueID);
3424 StorageUtl_WriteDWord(
3425 currentProperty,
3426 OFFSET_PS_TSS1,
3427 buffer->timeStampS1);
3429 StorageUtl_WriteDWord(
3430 currentProperty,
3431 OFFSET_PS_TSD1,
3432 buffer->timeStampD1);
3434 StorageUtl_WriteDWord(
3435 currentProperty,
3436 OFFSET_PS_TSS2,
3437 buffer->timeStampS2);
3439 StorageUtl_WriteDWord(
3440 currentProperty,
3441 OFFSET_PS_TSD2,
3442 buffer->timeStampD2);
3444 StorageUtl_WriteDWord(
3445 currentProperty,
3446 OFFSET_PS_STARTBLOCK,
3447 buffer->startingBlock);
3449 StorageUtl_WriteDWord(
3450 currentProperty,
3451 OFFSET_PS_SIZE,
3452 buffer->size.u.LowPart);
3454 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3455 offsetInPropSet,
3456 PROPSET_BLOCK_SIZE,
3457 currentProperty,
3458 &bytesWritten);
3459 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3462 static BOOL StorageImpl_ReadBigBlock(
3463 StorageImpl* This,
3464 ULONG blockIndex,
3465 void* buffer)
3467 ULARGE_INTEGER ulOffset;
3468 DWORD read;
3470 ulOffset.u.HighPart = 0;
3471 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3473 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3474 return (read == This->bigBlockSize);
3477 static BOOL StorageImpl_ReadDWordFromBigBlock(
3478 StorageImpl* This,
3479 ULONG blockIndex,
3480 ULONG offset,
3481 DWORD* value)
3483 ULARGE_INTEGER ulOffset;
3484 DWORD read;
3485 DWORD tmp;
3487 ulOffset.u.HighPart = 0;
3488 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3489 ulOffset.u.LowPart += offset;
3491 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3492 *value = le32toh(tmp);
3493 return (read == sizeof(DWORD));
3496 static BOOL StorageImpl_WriteBigBlock(
3497 StorageImpl* This,
3498 ULONG blockIndex,
3499 const void* buffer)
3501 ULARGE_INTEGER ulOffset;
3502 DWORD wrote;
3504 ulOffset.u.HighPart = 0;
3505 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3507 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3508 return (wrote == This->bigBlockSize);
3511 static BOOL StorageImpl_WriteDWordToBigBlock(
3512 StorageImpl* This,
3513 ULONG blockIndex,
3514 ULONG offset,
3515 DWORD value)
3517 ULARGE_INTEGER ulOffset;
3518 DWORD wrote;
3520 ulOffset.u.HighPart = 0;
3521 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3522 ulOffset.u.LowPart += offset;
3524 value = htole32(value);
3525 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3526 return (wrote == sizeof(DWORD));
3529 /******************************************************************************
3530 * Storage32Impl_SmallBlocksToBigBlocks
3532 * This method will convert a small block chain to a big block chain.
3533 * The small block chain will be destroyed.
3535 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3536 StorageImpl* This,
3537 SmallBlockChainStream** ppsbChain)
3539 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3540 ULARGE_INTEGER size, offset;
3541 ULONG cbRead, cbWritten;
3542 ULARGE_INTEGER cbTotalRead;
3543 ULONG propertyIndex;
3544 HRESULT resWrite = S_OK;
3545 HRESULT resRead;
3546 StgProperty chainProperty;
3547 BYTE *buffer;
3548 BlockChainStream *bbTempChain = NULL;
3549 BlockChainStream *bigBlockChain = NULL;
3552 * Create a temporary big block chain that doesn't have
3553 * an associated property. This temporary chain will be
3554 * used to copy data from small blocks to big blocks.
3556 bbTempChain = BlockChainStream_Construct(This,
3557 &bbHeadOfChain,
3558 PROPERTY_NULL);
3559 if(!bbTempChain) return NULL;
3561 * Grow the big block chain.
3563 size = SmallBlockChainStream_GetSize(*ppsbChain);
3564 BlockChainStream_SetSize(bbTempChain, size);
3567 * Copy the contents of the small block chain to the big block chain
3568 * by small block size increments.
3570 offset.u.LowPart = 0;
3571 offset.u.HighPart = 0;
3572 cbTotalRead.QuadPart = 0;
3574 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3577 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3578 offset,
3579 This->smallBlockSize,
3580 buffer,
3581 &cbRead);
3582 if (FAILED(resRead))
3583 break;
3585 if (cbRead > 0)
3587 cbTotalRead.QuadPart += cbRead;
3589 resWrite = BlockChainStream_WriteAt(bbTempChain,
3590 offset,
3591 cbRead,
3592 buffer,
3593 &cbWritten);
3595 if (FAILED(resWrite))
3596 break;
3598 offset.u.LowPart += This->smallBlockSize;
3600 } while (cbTotalRead.QuadPart < size.QuadPart);
3601 HeapFree(GetProcessHeap(),0,buffer);
3603 if (FAILED(resRead) || FAILED(resWrite))
3605 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3606 BlockChainStream_Destroy(bbTempChain);
3607 return NULL;
3611 * Destroy the small block chain.
3613 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3614 size.u.HighPart = 0;
3615 size.u.LowPart = 0;
3616 SmallBlockChainStream_SetSize(*ppsbChain, size);
3617 SmallBlockChainStream_Destroy(*ppsbChain);
3618 *ppsbChain = 0;
3621 * Change the property information. This chain is now a big block chain
3622 * and it doesn't reside in the small blocks chain anymore.
3624 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3626 chainProperty.startingBlock = bbHeadOfChain;
3628 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3631 * Destroy the temporary propertyless big block chain.
3632 * Create a new big block chain associated with this property.
3634 BlockChainStream_Destroy(bbTempChain);
3635 bigBlockChain = BlockChainStream_Construct(This,
3636 NULL,
3637 propertyIndex);
3639 return bigBlockChain;
3642 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3644 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3646 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3647 HeapFree(GetProcessHeap(), 0, This);
3650 /******************************************************************************
3652 ** Storage32InternalImpl_Commit
3654 ** The non-root storages cannot be opened in transacted mode thus this function
3655 ** does nothing.
3657 static HRESULT WINAPI StorageInternalImpl_Commit(
3658 IStorage* iface,
3659 DWORD grfCommitFlags) /* [in] */
3661 return S_OK;
3664 /******************************************************************************
3666 ** Storage32InternalImpl_Revert
3668 ** The non-root storages cannot be opened in transacted mode thus this function
3669 ** does nothing.
3671 static HRESULT WINAPI StorageInternalImpl_Revert(
3672 IStorage* iface)
3674 return S_OK;
3677 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3679 IStorage_Release((IStorage*)This->parentStorage);
3680 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3681 HeapFree(GetProcessHeap(), 0, This);
3684 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3685 IEnumSTATSTG* iface,
3686 REFIID riid,
3687 void** ppvObject)
3689 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3692 * Perform a sanity check on the parameters.
3694 if (ppvObject==0)
3695 return E_INVALIDARG;
3698 * Initialize the return parameter.
3700 *ppvObject = 0;
3703 * Compare the riid with the interface IDs implemented by this object.
3705 if (IsEqualGUID(&IID_IUnknown, riid) ||
3706 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3708 *ppvObject = (IEnumSTATSTG*)This;
3709 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3710 return S_OK;
3713 return E_NOINTERFACE;
3716 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3717 IEnumSTATSTG* iface)
3719 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3720 return InterlockedIncrement(&This->ref);
3723 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3724 IEnumSTATSTG* iface)
3726 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3728 ULONG newRef;
3730 newRef = InterlockedDecrement(&This->ref);
3733 * If the reference count goes down to 0, perform suicide.
3735 if (newRef==0)
3737 IEnumSTATSTGImpl_Destroy(This);
3740 return newRef;
3743 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3744 IEnumSTATSTG* iface,
3745 ULONG celt,
3746 STATSTG* rgelt,
3747 ULONG* pceltFetched)
3749 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3751 StgProperty currentProperty;
3752 STATSTG* currentReturnStruct = rgelt;
3753 ULONG objectFetched = 0;
3754 ULONG currentSearchNode;
3757 * Perform a sanity check on the parameters.
3759 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3760 return E_INVALIDARG;
3763 * To avoid the special case, get another pointer to a ULONG value if
3764 * the caller didn't supply one.
3766 if (pceltFetched==0)
3767 pceltFetched = &objectFetched;
3770 * Start the iteration, we will iterate until we hit the end of the
3771 * linked list or until we hit the number of items to iterate through
3773 *pceltFetched = 0;
3776 * Start with the node at the top of the stack.
3778 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3780 while ( ( *pceltFetched < celt) &&
3781 ( currentSearchNode!=PROPERTY_NULL) )
3784 * Remove the top node from the stack
3786 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3789 * Read the property from the storage.
3791 StorageImpl_ReadProperty(This->parentStorage,
3792 currentSearchNode,
3793 &currentProperty);
3796 * Copy the information to the return buffer.
3798 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3799 &currentProperty,
3800 STATFLAG_DEFAULT);
3803 * Step to the next item in the iteration
3805 (*pceltFetched)++;
3806 currentReturnStruct++;
3809 * Push the next search node in the search stack.
3811 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3814 * continue the iteration.
3816 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3819 if (*pceltFetched == celt)
3820 return S_OK;
3822 return S_FALSE;
3826 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3827 IEnumSTATSTG* iface,
3828 ULONG celt)
3830 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3832 StgProperty currentProperty;
3833 ULONG objectFetched = 0;
3834 ULONG currentSearchNode;
3837 * Start with the node at the top of the stack.
3839 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3841 while ( (objectFetched < celt) &&
3842 (currentSearchNode!=PROPERTY_NULL) )
3845 * Remove the top node from the stack
3847 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3850 * Read the property from the storage.
3852 StorageImpl_ReadProperty(This->parentStorage,
3853 currentSearchNode,
3854 &currentProperty);
3857 * Step to the next item in the iteration
3859 objectFetched++;
3862 * Push the next search node in the search stack.
3864 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3867 * continue the iteration.
3869 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3872 if (objectFetched == celt)
3873 return S_OK;
3875 return S_FALSE;
3878 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3879 IEnumSTATSTG* iface)
3881 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3883 StgProperty rootProperty;
3884 BOOL readSuccessful;
3887 * Re-initialize the search stack to an empty stack
3889 This->stackSize = 0;
3892 * Read the root property from the storage.
3894 readSuccessful = StorageImpl_ReadProperty(
3895 This->parentStorage,
3896 This->firstPropertyNode,
3897 &rootProperty);
3899 if (readSuccessful)
3901 assert(rootProperty.sizeOfNameString!=0);
3904 * Push the search node in the search stack.
3906 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3909 return S_OK;
3912 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3913 IEnumSTATSTG* iface,
3914 IEnumSTATSTG** ppenum)
3916 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3918 IEnumSTATSTGImpl* newClone;
3921 * Perform a sanity check on the parameters.
3923 if (ppenum==0)
3924 return E_INVALIDARG;
3926 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3927 This->firstPropertyNode);
3931 * The new clone enumeration must point to the same current node as
3932 * the ole one.
3934 newClone->stackSize = This->stackSize ;
3935 newClone->stackMaxSize = This->stackMaxSize ;
3936 newClone->stackToVisit =
3937 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3939 memcpy(
3940 newClone->stackToVisit,
3941 This->stackToVisit,
3942 sizeof(ULONG) * newClone->stackSize);
3944 *ppenum = (IEnumSTATSTG*)newClone;
3947 * Don't forget to nail down a reference to the clone before
3948 * returning it.
3950 IEnumSTATSTGImpl_AddRef(*ppenum);
3952 return S_OK;
3955 static INT IEnumSTATSTGImpl_FindParentProperty(
3956 IEnumSTATSTGImpl *This,
3957 ULONG childProperty,
3958 StgProperty *currentProperty,
3959 ULONG *thisNodeId)
3961 ULONG currentSearchNode;
3962 ULONG foundNode;
3965 * To avoid the special case, get another pointer to a ULONG value if
3966 * the caller didn't supply one.
3968 if (thisNodeId==0)
3969 thisNodeId = &foundNode;
3972 * Start with the node at the top of the stack.
3974 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3977 while (currentSearchNode!=PROPERTY_NULL)
3980 * Store the current node in the returned parameters
3982 *thisNodeId = currentSearchNode;
3985 * Remove the top node from the stack
3987 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3990 * Read the property from the storage.
3992 StorageImpl_ReadProperty(
3993 This->parentStorage,
3994 currentSearchNode,
3995 currentProperty);
3997 if (currentProperty->previousProperty == childProperty)
3998 return PROPERTY_RELATION_PREVIOUS;
4000 else if (currentProperty->nextProperty == childProperty)
4001 return PROPERTY_RELATION_NEXT;
4003 else if (currentProperty->dirProperty == childProperty)
4004 return PROPERTY_RELATION_DIR;
4007 * Push the next search node in the search stack.
4009 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4012 * continue the iteration.
4014 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4017 return PROPERTY_NULL;
4020 static ULONG IEnumSTATSTGImpl_FindProperty(
4021 IEnumSTATSTGImpl* This,
4022 const OLECHAR* lpszPropName,
4023 StgProperty* currentProperty)
4025 ULONG currentSearchNode;
4028 * Start with the node at the top of the stack.
4030 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4032 while (currentSearchNode!=PROPERTY_NULL)
4035 * Remove the top node from the stack
4037 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4040 * Read the property from the storage.
4042 StorageImpl_ReadProperty(This->parentStorage,
4043 currentSearchNode,
4044 currentProperty);
4046 if ( propertyNameCmp(
4047 (const OLECHAR*)currentProperty->name, lpszPropName) == 0)
4048 return currentSearchNode;
4051 * Push the next search node in the search stack.
4053 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4056 * continue the iteration.
4058 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4061 return PROPERTY_NULL;
4064 static void IEnumSTATSTGImpl_PushSearchNode(
4065 IEnumSTATSTGImpl* This,
4066 ULONG nodeToPush)
4068 StgProperty rootProperty;
4069 BOOL readSuccessful;
4072 * First, make sure we're not trying to push an unexisting node.
4074 if (nodeToPush==PROPERTY_NULL)
4075 return;
4078 * First push the node to the stack
4080 if (This->stackSize == This->stackMaxSize)
4082 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4084 This->stackToVisit = HeapReAlloc(
4085 GetProcessHeap(),
4087 This->stackToVisit,
4088 sizeof(ULONG) * This->stackMaxSize);
4091 This->stackToVisit[This->stackSize] = nodeToPush;
4092 This->stackSize++;
4095 * Read the root property from the storage.
4097 readSuccessful = StorageImpl_ReadProperty(
4098 This->parentStorage,
4099 nodeToPush,
4100 &rootProperty);
4102 if (readSuccessful)
4104 assert(rootProperty.sizeOfNameString!=0);
4107 * Push the previous search node in the search stack.
4109 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4113 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4114 IEnumSTATSTGImpl* This,
4115 BOOL remove)
4117 ULONG topNode;
4119 if (This->stackSize == 0)
4120 return PROPERTY_NULL;
4122 topNode = This->stackToVisit[This->stackSize-1];
4124 if (remove)
4125 This->stackSize--;
4127 return topNode;
4131 * Virtual function table for the IEnumSTATSTGImpl class.
4133 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4135 IEnumSTATSTGImpl_QueryInterface,
4136 IEnumSTATSTGImpl_AddRef,
4137 IEnumSTATSTGImpl_Release,
4138 IEnumSTATSTGImpl_Next,
4139 IEnumSTATSTGImpl_Skip,
4140 IEnumSTATSTGImpl_Reset,
4141 IEnumSTATSTGImpl_Clone
4144 /******************************************************************************
4145 ** IEnumSTATSTGImpl implementation
4148 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4149 StorageImpl* parentStorage,
4150 ULONG firstPropertyNode)
4152 IEnumSTATSTGImpl* newEnumeration;
4154 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4156 if (newEnumeration!=0)
4159 * Set-up the virtual function table and reference count.
4161 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4162 newEnumeration->ref = 0;
4165 * We want to nail-down the reference to the storage in case the
4166 * enumeration out-lives the storage in the client application.
4168 newEnumeration->parentStorage = parentStorage;
4169 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4171 newEnumeration->firstPropertyNode = firstPropertyNode;
4174 * Initialize the search stack
4176 newEnumeration->stackSize = 0;
4177 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4178 newEnumeration->stackToVisit =
4179 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4182 * Make sure the current node of the iterator is the first one.
4184 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4187 return newEnumeration;
4191 * Virtual function table for the Storage32InternalImpl class.
4193 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4195 StorageBaseImpl_QueryInterface,
4196 StorageBaseImpl_AddRef,
4197 StorageBaseImpl_Release,
4198 StorageBaseImpl_CreateStream,
4199 StorageBaseImpl_OpenStream,
4200 StorageImpl_CreateStorage,
4201 StorageBaseImpl_OpenStorage,
4202 StorageImpl_CopyTo,
4203 StorageImpl_MoveElementTo,
4204 StorageInternalImpl_Commit,
4205 StorageInternalImpl_Revert,
4206 StorageBaseImpl_EnumElements,
4207 StorageImpl_DestroyElement,
4208 StorageBaseImpl_RenameElement,
4209 StorageImpl_SetElementTimes,
4210 StorageBaseImpl_SetClass,
4211 StorageImpl_SetStateBits,
4212 StorageBaseImpl_Stat
4215 /******************************************************************************
4216 ** Storage32InternalImpl implementation
4219 static StorageInternalImpl* StorageInternalImpl_Construct(
4220 StorageImpl* ancestorStorage,
4221 DWORD openFlags,
4222 ULONG rootPropertyIndex)
4224 StorageInternalImpl* newStorage;
4227 * Allocate space for the new storage object
4229 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4231 if (newStorage!=0)
4234 * Initialize the stream list
4236 list_init(&newStorage->base.strmHead);
4239 * Initialize the virtual function table.
4241 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4242 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4243 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4246 * Keep the ancestor storage pointer and nail a reference to it.
4248 newStorage->base.ancestorStorage = ancestorStorage;
4249 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4252 * Keep the index of the root property set for this storage,
4254 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4256 return newStorage;
4259 return 0;
4262 /******************************************************************************
4263 ** StorageUtl implementation
4266 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4268 WORD tmp;
4270 memcpy(&tmp, buffer+offset, sizeof(WORD));
4271 *value = le16toh(tmp);
4274 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4276 value = htole16(value);
4277 memcpy(buffer+offset, &value, sizeof(WORD));
4280 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4282 DWORD tmp;
4284 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4285 *value = le32toh(tmp);
4288 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4290 value = htole32(value);
4291 memcpy(buffer+offset, &value, sizeof(DWORD));
4294 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4295 ULARGE_INTEGER* value)
4297 #ifdef WORDS_BIGENDIAN
4298 ULARGE_INTEGER tmp;
4300 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4301 value->u.LowPart = htole32(tmp.u.HighPart);
4302 value->u.HighPart = htole32(tmp.u.LowPart);
4303 #else
4304 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4305 #endif
4308 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4309 const ULARGE_INTEGER *value)
4311 #ifdef WORDS_BIGENDIAN
4312 ULARGE_INTEGER tmp;
4314 tmp.u.LowPart = htole32(value->u.HighPart);
4315 tmp.u.HighPart = htole32(value->u.LowPart);
4316 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4317 #else
4318 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4319 #endif
4322 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4324 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4325 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4326 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4328 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4331 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4333 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4334 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4335 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4337 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4340 void StorageUtl_CopyPropertyToSTATSTG(
4341 STATSTG* destination,
4342 const StgProperty* source,
4343 int statFlags)
4346 * The copy of the string occurs only when the flag is not set
4348 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4349 (source->name == NULL) ||
4350 (source->name[0] == 0) )
4352 destination->pwcsName = 0;
4354 else
4356 destination->pwcsName =
4357 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4359 strcpyW((LPWSTR)destination->pwcsName, source->name);
4362 switch (source->propertyType)
4364 case PROPTYPE_STORAGE:
4365 case PROPTYPE_ROOT:
4366 destination->type = STGTY_STORAGE;
4367 break;
4368 case PROPTYPE_STREAM:
4369 destination->type = STGTY_STREAM;
4370 break;
4371 default:
4372 destination->type = STGTY_STREAM;
4373 break;
4376 destination->cbSize = source->size;
4378 currentReturnStruct->mtime = {0}; TODO
4379 currentReturnStruct->ctime = {0};
4380 currentReturnStruct->atime = {0};
4382 destination->grfMode = 0;
4383 destination->grfLocksSupported = 0;
4384 destination->clsid = source->propertyUniqueID;
4385 destination->grfStateBits = 0;
4386 destination->reserved = 0;
4389 /******************************************************************************
4390 ** BlockChainStream implementation
4393 BlockChainStream* BlockChainStream_Construct(
4394 StorageImpl* parentStorage,
4395 ULONG* headOfStreamPlaceHolder,
4396 ULONG propertyIndex)
4398 BlockChainStream* newStream;
4399 ULONG blockIndex;
4401 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4403 newStream->parentStorage = parentStorage;
4404 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4405 newStream->ownerPropertyIndex = propertyIndex;
4406 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4407 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4408 newStream->numBlocks = 0;
4410 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4412 while (blockIndex != BLOCK_END_OF_CHAIN)
4414 newStream->numBlocks++;
4415 newStream->tailIndex = blockIndex;
4417 if(FAILED(StorageImpl_GetNextBlockInChain(
4418 parentStorage,
4419 blockIndex,
4420 &blockIndex)))
4422 HeapFree(GetProcessHeap(), 0, newStream);
4423 return NULL;
4427 return newStream;
4430 void BlockChainStream_Destroy(BlockChainStream* This)
4432 HeapFree(GetProcessHeap(), 0, This);
4435 /******************************************************************************
4436 * BlockChainStream_GetHeadOfChain
4438 * Returns the head of this stream chain.
4439 * Some special chains don't have properties, their heads are kept in
4440 * This->headOfStreamPlaceHolder.
4443 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4445 StgProperty chainProperty;
4446 BOOL readSuccessful;
4448 if (This->headOfStreamPlaceHolder != 0)
4449 return *(This->headOfStreamPlaceHolder);
4451 if (This->ownerPropertyIndex != PROPERTY_NULL)
4453 readSuccessful = StorageImpl_ReadProperty(
4454 This->parentStorage,
4455 This->ownerPropertyIndex,
4456 &chainProperty);
4458 if (readSuccessful)
4460 return chainProperty.startingBlock;
4464 return BLOCK_END_OF_CHAIN;
4467 /******************************************************************************
4468 * BlockChainStream_GetCount
4470 * Returns the number of blocks that comprises this chain.
4471 * This is not the size of the stream as the last block may not be full!
4474 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4476 ULONG blockIndex;
4477 ULONG count = 0;
4479 blockIndex = BlockChainStream_GetHeadOfChain(This);
4481 while (blockIndex != BLOCK_END_OF_CHAIN)
4483 count++;
4485 if(FAILED(StorageImpl_GetNextBlockInChain(
4486 This->parentStorage,
4487 blockIndex,
4488 &blockIndex)))
4489 return 0;
4492 return count;
4495 /******************************************************************************
4496 * BlockChainStream_ReadAt
4498 * Reads a specified number of bytes from this chain at the specified offset.
4499 * bytesRead may be NULL.
4500 * Failure will be returned if the specified number of bytes has not been read.
4502 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4503 ULARGE_INTEGER offset,
4504 ULONG size,
4505 void* buffer,
4506 ULONG* bytesRead)
4508 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4509 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4510 ULONG bytesToReadInBuffer;
4511 ULONG blockIndex;
4512 BYTE* bufferWalker;
4514 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4517 * Find the first block in the stream that contains part of the buffer.
4519 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4520 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4521 (blockNoInSequence < This->lastBlockNoInSequence) )
4523 blockIndex = BlockChainStream_GetHeadOfChain(This);
4524 This->lastBlockNoInSequence = blockNoInSequence;
4526 else
4528 ULONG temp = blockNoInSequence;
4530 blockIndex = This->lastBlockNoInSequenceIndex;
4531 blockNoInSequence -= This->lastBlockNoInSequence;
4532 This->lastBlockNoInSequence = temp;
4535 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4537 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4538 return STG_E_DOCFILECORRUPT;
4539 blockNoInSequence--;
4542 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4543 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4545 This->lastBlockNoInSequenceIndex = blockIndex;
4548 * Start reading the buffer.
4550 *bytesRead = 0;
4551 bufferWalker = buffer;
4553 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4555 ULARGE_INTEGER ulOffset;
4556 DWORD bytesReadAt;
4558 * Calculate how many bytes we can copy from this big block.
4560 bytesToReadInBuffer =
4561 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4563 TRACE("block %i\n",blockIndex);
4564 ulOffset.u.HighPart = 0;
4565 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4566 offsetInBlock;
4568 StorageImpl_ReadAt(This->parentStorage,
4569 ulOffset,
4570 bufferWalker,
4571 bytesToReadInBuffer,
4572 &bytesReadAt);
4574 * Step to the next big block.
4576 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4577 return STG_E_DOCFILECORRUPT;
4579 bufferWalker += bytesReadAt;
4580 size -= bytesReadAt;
4581 *bytesRead += bytesReadAt;
4582 offsetInBlock = 0; /* There is no offset on the next block */
4584 if (bytesToReadInBuffer != bytesReadAt)
4585 break;
4588 return (size == 0) ? S_OK : STG_E_READFAULT;
4591 /******************************************************************************
4592 * BlockChainStream_WriteAt
4594 * Writes the specified number of bytes to this chain at the specified offset.
4595 * bytesWritten may be NULL.
4596 * Will fail if not all specified number of bytes have been written.
4598 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4599 ULARGE_INTEGER offset,
4600 ULONG size,
4601 const void* buffer,
4602 ULONG* bytesWritten)
4604 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4605 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4606 ULONG bytesToWrite;
4607 ULONG blockIndex;
4608 const BYTE* bufferWalker;
4611 * Find the first block in the stream that contains part of the buffer.
4613 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4614 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4615 (blockNoInSequence < This->lastBlockNoInSequence) )
4617 blockIndex = BlockChainStream_GetHeadOfChain(This);
4618 This->lastBlockNoInSequence = blockNoInSequence;
4620 else
4622 ULONG temp = blockNoInSequence;
4624 blockIndex = This->lastBlockNoInSequenceIndex;
4625 blockNoInSequence -= This->lastBlockNoInSequence;
4626 This->lastBlockNoInSequence = temp;
4629 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4631 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4632 &blockIndex)))
4633 return STG_E_DOCFILECORRUPT;
4634 blockNoInSequence--;
4637 This->lastBlockNoInSequenceIndex = blockIndex;
4639 /* BlockChainStream_SetSize should have already been called to ensure we have
4640 * enough blocks in the chain to write into */
4641 if (blockIndex == BLOCK_END_OF_CHAIN)
4643 ERR("not enough blocks in chain to write data\n");
4644 return STG_E_DOCFILECORRUPT;
4647 *bytesWritten = 0;
4648 bufferWalker = (const BYTE*)buffer;
4650 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4652 ULARGE_INTEGER ulOffset;
4653 DWORD bytesWrittenAt;
4655 * Calculate how many bytes we can copy from this big block.
4657 bytesToWrite =
4658 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4660 TRACE("block %i\n",blockIndex);
4661 ulOffset.u.HighPart = 0;
4662 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4663 offsetInBlock;
4665 StorageImpl_WriteAt(This->parentStorage,
4666 ulOffset,
4667 bufferWalker,
4668 bytesToWrite,
4669 &bytesWrittenAt);
4672 * Step to the next big block.
4674 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4675 &blockIndex)))
4676 return STG_E_DOCFILECORRUPT;
4678 bufferWalker += bytesWrittenAt;
4679 size -= bytesWrittenAt;
4680 *bytesWritten += bytesWrittenAt;
4681 offsetInBlock = 0; /* There is no offset on the next block */
4683 if (bytesWrittenAt != bytesToWrite)
4684 break;
4687 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4690 /******************************************************************************
4691 * BlockChainStream_Shrink
4693 * Shrinks this chain in the big block depot.
4695 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4696 ULARGE_INTEGER newSize)
4698 ULONG blockIndex, extraBlock;
4699 ULONG numBlocks;
4700 ULONG count = 1;
4703 * Reset the last accessed block cache.
4705 This->lastBlockNoInSequence = 0xFFFFFFFF;
4706 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4709 * Figure out how many blocks are needed to contain the new size
4711 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4713 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4714 numBlocks++;
4716 blockIndex = BlockChainStream_GetHeadOfChain(This);
4719 * Go to the new end of chain
4721 while (count < numBlocks)
4723 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4724 &blockIndex)))
4725 return FALSE;
4726 count++;
4729 /* Get the next block before marking the new end */
4730 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4731 &extraBlock)))
4732 return FALSE;
4734 /* Mark the new end of chain */
4735 StorageImpl_SetNextBlockInChain(
4736 This->parentStorage,
4737 blockIndex,
4738 BLOCK_END_OF_CHAIN);
4740 This->tailIndex = blockIndex;
4741 This->numBlocks = numBlocks;
4744 * Mark the extra blocks as free
4746 while (extraBlock != BLOCK_END_OF_CHAIN)
4748 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4749 &blockIndex)))
4750 return FALSE;
4751 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4752 extraBlock = blockIndex;
4755 return TRUE;
4758 /******************************************************************************
4759 * BlockChainStream_Enlarge
4761 * Grows this chain in the big block depot.
4763 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4764 ULARGE_INTEGER newSize)
4766 ULONG blockIndex, currentBlock;
4767 ULONG newNumBlocks;
4768 ULONG oldNumBlocks = 0;
4770 blockIndex = BlockChainStream_GetHeadOfChain(This);
4773 * Empty chain. Create the head.
4775 if (blockIndex == BLOCK_END_OF_CHAIN)
4777 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4778 StorageImpl_SetNextBlockInChain(This->parentStorage,
4779 blockIndex,
4780 BLOCK_END_OF_CHAIN);
4782 if (This->headOfStreamPlaceHolder != 0)
4784 *(This->headOfStreamPlaceHolder) = blockIndex;
4786 else
4788 StgProperty chainProp;
4789 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4791 StorageImpl_ReadProperty(
4792 This->parentStorage,
4793 This->ownerPropertyIndex,
4794 &chainProp);
4796 chainProp.startingBlock = blockIndex;
4798 StorageImpl_WriteProperty(
4799 This->parentStorage,
4800 This->ownerPropertyIndex,
4801 &chainProp);
4804 This->tailIndex = blockIndex;
4805 This->numBlocks = 1;
4809 * Figure out how many blocks are needed to contain this stream
4811 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4813 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4814 newNumBlocks++;
4817 * Go to the current end of chain
4819 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4821 currentBlock = blockIndex;
4823 while (blockIndex != BLOCK_END_OF_CHAIN)
4825 This->numBlocks++;
4826 currentBlock = blockIndex;
4828 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4829 &blockIndex)))
4830 return FALSE;
4833 This->tailIndex = currentBlock;
4836 currentBlock = This->tailIndex;
4837 oldNumBlocks = This->numBlocks;
4840 * Add new blocks to the chain
4842 if (oldNumBlocks < newNumBlocks)
4844 while (oldNumBlocks < newNumBlocks)
4846 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4848 StorageImpl_SetNextBlockInChain(
4849 This->parentStorage,
4850 currentBlock,
4851 blockIndex);
4853 StorageImpl_SetNextBlockInChain(
4854 This->parentStorage,
4855 blockIndex,
4856 BLOCK_END_OF_CHAIN);
4858 currentBlock = blockIndex;
4859 oldNumBlocks++;
4862 This->tailIndex = blockIndex;
4863 This->numBlocks = newNumBlocks;
4866 return TRUE;
4869 /******************************************************************************
4870 * BlockChainStream_SetSize
4872 * Sets the size of this stream. The big block depot will be updated.
4873 * The file will grow if we grow the chain.
4875 * TODO: Free the actual blocks in the file when we shrink the chain.
4876 * Currently, the blocks are still in the file. So the file size
4877 * doesn't shrink even if we shrink streams.
4879 BOOL BlockChainStream_SetSize(
4880 BlockChainStream* This,
4881 ULARGE_INTEGER newSize)
4883 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4885 if (newSize.u.LowPart == size.u.LowPart)
4886 return TRUE;
4888 if (newSize.u.LowPart < size.u.LowPart)
4890 BlockChainStream_Shrink(This, newSize);
4892 else
4894 BlockChainStream_Enlarge(This, newSize);
4897 return TRUE;
4900 /******************************************************************************
4901 * BlockChainStream_GetSize
4903 * Returns the size of this chain.
4904 * Will return the block count if this chain doesn't have a property.
4906 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4908 StgProperty chainProperty;
4910 if(This->headOfStreamPlaceHolder == NULL)
4913 * This chain is a data stream read the property and return
4914 * the appropriate size
4916 StorageImpl_ReadProperty(
4917 This->parentStorage,
4918 This->ownerPropertyIndex,
4919 &chainProperty);
4921 return chainProperty.size;
4923 else
4926 * this chain is a chain that does not have a property, figure out the
4927 * size by making the product number of used blocks times the
4928 * size of them
4930 ULARGE_INTEGER result;
4931 result.u.HighPart = 0;
4933 result.u.LowPart =
4934 BlockChainStream_GetCount(This) *
4935 This->parentStorage->bigBlockSize;
4937 return result;
4941 /******************************************************************************
4942 ** SmallBlockChainStream implementation
4945 SmallBlockChainStream* SmallBlockChainStream_Construct(
4946 StorageImpl* parentStorage,
4947 ULONG propertyIndex)
4949 SmallBlockChainStream* newStream;
4951 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4953 newStream->parentStorage = parentStorage;
4954 newStream->ownerPropertyIndex = propertyIndex;
4956 return newStream;
4959 void SmallBlockChainStream_Destroy(
4960 SmallBlockChainStream* This)
4962 HeapFree(GetProcessHeap(), 0, This);
4965 /******************************************************************************
4966 * SmallBlockChainStream_GetHeadOfChain
4968 * Returns the head of this chain of small blocks.
4970 static ULONG SmallBlockChainStream_GetHeadOfChain(
4971 SmallBlockChainStream* This)
4973 StgProperty chainProperty;
4974 BOOL readSuccessful;
4976 if (This->ownerPropertyIndex)
4978 readSuccessful = StorageImpl_ReadProperty(
4979 This->parentStorage,
4980 This->ownerPropertyIndex,
4981 &chainProperty);
4983 if (readSuccessful)
4985 return chainProperty.startingBlock;
4990 return BLOCK_END_OF_CHAIN;
4993 /******************************************************************************
4994 * SmallBlockChainStream_GetNextBlockInChain
4996 * Returns the index of the next small block in this chain.
4998 * Return Values:
4999 * - BLOCK_END_OF_CHAIN: end of this chain
5000 * - BLOCK_UNUSED: small block 'blockIndex' is free
5002 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5003 SmallBlockChainStream* This,
5004 ULONG blockIndex,
5005 ULONG* nextBlockInChain)
5007 ULARGE_INTEGER offsetOfBlockInDepot;
5008 DWORD buffer;
5009 ULONG bytesRead;
5010 HRESULT res;
5012 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5014 offsetOfBlockInDepot.u.HighPart = 0;
5015 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5018 * Read those bytes in the buffer from the small block file.
5020 res = BlockChainStream_ReadAt(
5021 This->parentStorage->smallBlockDepotChain,
5022 offsetOfBlockInDepot,
5023 sizeof(DWORD),
5024 &buffer,
5025 &bytesRead);
5027 if (SUCCEEDED(res))
5029 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5030 return S_OK;
5033 return res;
5036 /******************************************************************************
5037 * SmallBlockChainStream_SetNextBlockInChain
5039 * Writes the index of the next block of the specified block in the small
5040 * block depot.
5041 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5042 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5044 static void SmallBlockChainStream_SetNextBlockInChain(
5045 SmallBlockChainStream* This,
5046 ULONG blockIndex,
5047 ULONG nextBlock)
5049 ULARGE_INTEGER offsetOfBlockInDepot;
5050 DWORD buffer;
5051 ULONG bytesWritten;
5053 offsetOfBlockInDepot.u.HighPart = 0;
5054 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5056 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5059 * Read those bytes in the buffer from the small block file.
5061 BlockChainStream_WriteAt(
5062 This->parentStorage->smallBlockDepotChain,
5063 offsetOfBlockInDepot,
5064 sizeof(DWORD),
5065 &buffer,
5066 &bytesWritten);
5069 /******************************************************************************
5070 * SmallBlockChainStream_FreeBlock
5072 * Flag small block 'blockIndex' as free in the small block depot.
5074 static void SmallBlockChainStream_FreeBlock(
5075 SmallBlockChainStream* This,
5076 ULONG blockIndex)
5078 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5081 /******************************************************************************
5082 * SmallBlockChainStream_GetNextFreeBlock
5084 * Returns the index of a free small block. The small block depot will be
5085 * enlarged if necessary. The small block chain will also be enlarged if
5086 * necessary.
5088 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5089 SmallBlockChainStream* This)
5091 ULARGE_INTEGER offsetOfBlockInDepot;
5092 DWORD buffer;
5093 ULONG bytesRead;
5094 ULONG blockIndex = 0;
5095 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5096 HRESULT res = S_OK;
5097 ULONG smallBlocksPerBigBlock;
5099 offsetOfBlockInDepot.u.HighPart = 0;
5102 * Scan the small block depot for a free block
5104 while (nextBlockIndex != BLOCK_UNUSED)
5106 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5108 res = BlockChainStream_ReadAt(
5109 This->parentStorage->smallBlockDepotChain,
5110 offsetOfBlockInDepot,
5111 sizeof(DWORD),
5112 &buffer,
5113 &bytesRead);
5116 * If we run out of space for the small block depot, enlarge it
5118 if (SUCCEEDED(res))
5120 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5122 if (nextBlockIndex != BLOCK_UNUSED)
5123 blockIndex++;
5125 else
5127 ULONG count =
5128 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5130 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5131 ULONG nextBlock, newsbdIndex;
5132 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5134 nextBlock = sbdIndex;
5135 while (nextBlock != BLOCK_END_OF_CHAIN)
5137 sbdIndex = nextBlock;
5138 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5141 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5142 if (sbdIndex != BLOCK_END_OF_CHAIN)
5143 StorageImpl_SetNextBlockInChain(
5144 This->parentStorage,
5145 sbdIndex,
5146 newsbdIndex);
5148 StorageImpl_SetNextBlockInChain(
5149 This->parentStorage,
5150 newsbdIndex,
5151 BLOCK_END_OF_CHAIN);
5154 * Initialize all the small blocks to free
5156 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5157 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5159 if (count == 0)
5162 * We have just created the small block depot.
5164 StgProperty rootProp;
5165 ULONG sbStartIndex;
5168 * Save it in the header
5170 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5171 StorageImpl_SaveFileHeader(This->parentStorage);
5174 * And allocate the first big block that will contain small blocks
5176 sbStartIndex =
5177 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5179 StorageImpl_SetNextBlockInChain(
5180 This->parentStorage,
5181 sbStartIndex,
5182 BLOCK_END_OF_CHAIN);
5184 StorageImpl_ReadProperty(
5185 This->parentStorage,
5186 This->parentStorage->base.rootPropertySetIndex,
5187 &rootProp);
5189 rootProp.startingBlock = sbStartIndex;
5190 rootProp.size.u.HighPart = 0;
5191 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5193 StorageImpl_WriteProperty(
5194 This->parentStorage,
5195 This->parentStorage->base.rootPropertySetIndex,
5196 &rootProp);
5198 else
5199 StorageImpl_SaveFileHeader(This->parentStorage);
5203 smallBlocksPerBigBlock =
5204 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5207 * Verify if we have to allocate big blocks to contain small blocks
5209 if (blockIndex % smallBlocksPerBigBlock == 0)
5211 StgProperty rootProp;
5212 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5214 StorageImpl_ReadProperty(
5215 This->parentStorage,
5216 This->parentStorage->base.rootPropertySetIndex,
5217 &rootProp);
5219 if (rootProp.size.u.LowPart <
5220 (blocksRequired * This->parentStorage->bigBlockSize))
5222 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5224 BlockChainStream_SetSize(
5225 This->parentStorage->smallBlockRootChain,
5226 rootProp.size);
5228 StorageImpl_WriteProperty(
5229 This->parentStorage,
5230 This->parentStorage->base.rootPropertySetIndex,
5231 &rootProp);
5235 return blockIndex;
5238 /******************************************************************************
5239 * SmallBlockChainStream_ReadAt
5241 * Reads a specified number of bytes from this chain at the specified offset.
5242 * bytesRead may be NULL.
5243 * Failure will be returned if the specified number of bytes has not been read.
5245 HRESULT SmallBlockChainStream_ReadAt(
5246 SmallBlockChainStream* This,
5247 ULARGE_INTEGER offset,
5248 ULONG size,
5249 void* buffer,
5250 ULONG* bytesRead)
5252 HRESULT rc = S_OK;
5253 ULARGE_INTEGER offsetInBigBlockFile;
5254 ULONG blockNoInSequence =
5255 offset.u.LowPart / This->parentStorage->smallBlockSize;
5257 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5258 ULONG bytesToReadInBuffer;
5259 ULONG blockIndex;
5260 ULONG bytesReadFromBigBlockFile;
5261 BYTE* bufferWalker;
5264 * This should never happen on a small block file.
5266 assert(offset.u.HighPart==0);
5269 * Find the first block in the stream that contains part of the buffer.
5271 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5273 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5275 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5276 if(FAILED(rc))
5277 return rc;
5278 blockNoInSequence--;
5282 * Start reading the buffer.
5284 *bytesRead = 0;
5285 bufferWalker = buffer;
5287 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5290 * Calculate how many bytes we can copy from this small block.
5292 bytesToReadInBuffer =
5293 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5296 * Calculate the offset of the small block in the small block file.
5298 offsetInBigBlockFile.u.HighPart = 0;
5299 offsetInBigBlockFile.u.LowPart =
5300 blockIndex * This->parentStorage->smallBlockSize;
5302 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5305 * Read those bytes in the buffer from the small block file.
5306 * The small block has already been identified so it shouldn't fail
5307 * unless the file is corrupt.
5309 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5310 offsetInBigBlockFile,
5311 bytesToReadInBuffer,
5312 bufferWalker,
5313 &bytesReadFromBigBlockFile);
5315 if (FAILED(rc))
5316 return rc;
5319 * Step to the next big block.
5321 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5322 if(FAILED(rc))
5323 return STG_E_DOCFILECORRUPT;
5325 bufferWalker += bytesReadFromBigBlockFile;
5326 size -= bytesReadFromBigBlockFile;
5327 *bytesRead += bytesReadFromBigBlockFile;
5328 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5331 return (size == 0) ? S_OK : STG_E_READFAULT;
5334 /******************************************************************************
5335 * SmallBlockChainStream_WriteAt
5337 * Writes the specified number of bytes to this chain at the specified offset.
5338 * bytesWritten may be NULL.
5339 * Will fail if not all specified number of bytes have been written.
5341 HRESULT SmallBlockChainStream_WriteAt(
5342 SmallBlockChainStream* This,
5343 ULARGE_INTEGER offset,
5344 ULONG size,
5345 const void* buffer,
5346 ULONG* bytesWritten)
5348 ULARGE_INTEGER offsetInBigBlockFile;
5349 ULONG blockNoInSequence =
5350 offset.u.LowPart / This->parentStorage->smallBlockSize;
5352 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5353 ULONG bytesToWriteInBuffer;
5354 ULONG blockIndex;
5355 ULONG bytesWrittenToBigBlockFile;
5356 const BYTE* bufferWalker;
5357 HRESULT res;
5360 * This should never happen on a small block file.
5362 assert(offset.u.HighPart==0);
5365 * Find the first block in the stream that contains part of the buffer.
5367 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5369 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5371 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5372 return STG_E_DOCFILECORRUPT;
5373 blockNoInSequence--;
5377 * Start writing the buffer.
5379 * Here, I'm casting away the constness on the buffer variable
5380 * This is OK since we don't intend to modify that buffer.
5382 *bytesWritten = 0;
5383 bufferWalker = (const BYTE*)buffer;
5384 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5387 * Calculate how many bytes we can copy to this small block.
5389 bytesToWriteInBuffer =
5390 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5393 * Calculate the offset of the small block in the small block file.
5395 offsetInBigBlockFile.u.HighPart = 0;
5396 offsetInBigBlockFile.u.LowPart =
5397 blockIndex * This->parentStorage->smallBlockSize;
5399 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5402 * Write those bytes in the buffer to the small block file.
5404 res = BlockChainStream_WriteAt(
5405 This->parentStorage->smallBlockRootChain,
5406 offsetInBigBlockFile,
5407 bytesToWriteInBuffer,
5408 bufferWalker,
5409 &bytesWrittenToBigBlockFile);
5410 if (FAILED(res))
5411 return res;
5414 * Step to the next big block.
5416 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5417 &blockIndex)))
5418 return FALSE;
5419 bufferWalker += bytesWrittenToBigBlockFile;
5420 size -= bytesWrittenToBigBlockFile;
5421 *bytesWritten += bytesWrittenToBigBlockFile;
5422 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5425 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5428 /******************************************************************************
5429 * SmallBlockChainStream_Shrink
5431 * Shrinks this chain in the small block depot.
5433 static BOOL SmallBlockChainStream_Shrink(
5434 SmallBlockChainStream* This,
5435 ULARGE_INTEGER newSize)
5437 ULONG blockIndex, extraBlock;
5438 ULONG numBlocks;
5439 ULONG count = 0;
5441 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5443 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5444 numBlocks++;
5446 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5449 * Go to the new end of chain
5451 while (count < numBlocks)
5453 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5454 &blockIndex)))
5455 return FALSE;
5456 count++;
5460 * If the count is 0, we have a special case, the head of the chain was
5461 * just freed.
5463 if (count == 0)
5465 StgProperty chainProp;
5467 StorageImpl_ReadProperty(This->parentStorage,
5468 This->ownerPropertyIndex,
5469 &chainProp);
5471 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5473 StorageImpl_WriteProperty(This->parentStorage,
5474 This->ownerPropertyIndex,
5475 &chainProp);
5478 * We start freeing the chain at the head block.
5480 extraBlock = blockIndex;
5482 else
5484 /* Get the next block before marking the new end */
5485 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5486 &extraBlock)))
5487 return FALSE;
5489 /* Mark the new end of chain */
5490 SmallBlockChainStream_SetNextBlockInChain(
5491 This,
5492 blockIndex,
5493 BLOCK_END_OF_CHAIN);
5497 * Mark the extra blocks as free
5499 while (extraBlock != BLOCK_END_OF_CHAIN)
5501 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5502 &blockIndex)))
5503 return FALSE;
5504 SmallBlockChainStream_FreeBlock(This, extraBlock);
5505 extraBlock = blockIndex;
5508 return TRUE;
5511 /******************************************************************************
5512 * SmallBlockChainStream_Enlarge
5514 * Grows this chain in the small block depot.
5516 static BOOL SmallBlockChainStream_Enlarge(
5517 SmallBlockChainStream* This,
5518 ULARGE_INTEGER newSize)
5520 ULONG blockIndex, currentBlock;
5521 ULONG newNumBlocks;
5522 ULONG oldNumBlocks = 0;
5524 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5527 * Empty chain
5529 if (blockIndex == BLOCK_END_OF_CHAIN)
5532 StgProperty chainProp;
5534 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5535 &chainProp);
5537 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5539 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5540 &chainProp);
5542 blockIndex = chainProp.startingBlock;
5543 SmallBlockChainStream_SetNextBlockInChain(
5544 This,
5545 blockIndex,
5546 BLOCK_END_OF_CHAIN);
5549 currentBlock = blockIndex;
5552 * Figure out how many blocks are needed to contain this stream
5554 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5556 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5557 newNumBlocks++;
5560 * Go to the current end of chain
5562 while (blockIndex != BLOCK_END_OF_CHAIN)
5564 oldNumBlocks++;
5565 currentBlock = blockIndex;
5566 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5567 return FALSE;
5571 * Add new blocks to the chain
5573 while (oldNumBlocks < newNumBlocks)
5575 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5576 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5578 SmallBlockChainStream_SetNextBlockInChain(
5579 This,
5580 blockIndex,
5581 BLOCK_END_OF_CHAIN);
5583 currentBlock = blockIndex;
5584 oldNumBlocks++;
5587 return TRUE;
5590 /******************************************************************************
5591 * SmallBlockChainStream_SetSize
5593 * Sets the size of this stream.
5594 * The file will grow if we grow the chain.
5596 * TODO: Free the actual blocks in the file when we shrink the chain.
5597 * Currently, the blocks are still in the file. So the file size
5598 * doesn't shrink even if we shrink streams.
5600 BOOL SmallBlockChainStream_SetSize(
5601 SmallBlockChainStream* This,
5602 ULARGE_INTEGER newSize)
5604 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5606 if (newSize.u.LowPart == size.u.LowPart)
5607 return TRUE;
5609 if (newSize.u.LowPart < size.u.LowPart)
5611 SmallBlockChainStream_Shrink(This, newSize);
5613 else
5615 SmallBlockChainStream_Enlarge(This, newSize);
5618 return TRUE;
5621 /******************************************************************************
5622 * SmallBlockChainStream_GetSize
5624 * Returns the size of this chain.
5626 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5628 StgProperty chainProperty;
5630 StorageImpl_ReadProperty(
5631 This->parentStorage,
5632 This->ownerPropertyIndex,
5633 &chainProperty);
5635 return chainProperty.size;
5638 /******************************************************************************
5639 * StgCreateDocfile [OLE32.@]
5640 * Creates a new compound file storage object
5642 * PARAMS
5643 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5644 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5645 * reserved [ ?] unused?, usually 0
5646 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5648 * RETURNS
5649 * S_OK if the file was successfully created
5650 * some STG_E_ value if error
5651 * NOTES
5652 * if pwcsName is NULL, create file with new unique name
5653 * the function can returns
5654 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5655 * (unrealized now)
5657 HRESULT WINAPI StgCreateDocfile(
5658 LPCOLESTR pwcsName,
5659 DWORD grfMode,
5660 DWORD reserved,
5661 IStorage **ppstgOpen)
5663 StorageImpl* newStorage = 0;
5664 HANDLE hFile = INVALID_HANDLE_VALUE;
5665 HRESULT hr = STG_E_INVALIDFLAG;
5666 DWORD shareMode;
5667 DWORD accessMode;
5668 DWORD creationMode;
5669 DWORD fileAttributes;
5670 WCHAR tempFileName[MAX_PATH];
5672 TRACE("(%s, %x, %d, %p)\n",
5673 debugstr_w(pwcsName), grfMode,
5674 reserved, ppstgOpen);
5677 * Validate the parameters
5679 if (ppstgOpen == 0)
5680 return STG_E_INVALIDPOINTER;
5681 if (reserved != 0)
5682 return STG_E_INVALIDPARAMETER;
5684 /* if no share mode given then DENY_NONE is the default */
5685 if (STGM_SHARE_MODE(grfMode) == 0)
5686 grfMode |= STGM_SHARE_DENY_NONE;
5689 * Validate the STGM flags
5691 if ( FAILED( validateSTGM(grfMode) ))
5692 goto end;
5694 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5695 switch(STGM_ACCESS_MODE(grfMode))
5697 case STGM_WRITE:
5698 case STGM_READWRITE:
5699 break;
5700 default:
5701 goto end;
5704 /* in direct mode, can only use SHARE_EXCLUSIVE */
5705 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5706 goto end;
5708 /* but in transacted mode, any share mode is valid */
5711 * Generate a unique name.
5713 if (pwcsName == 0)
5715 WCHAR tempPath[MAX_PATH];
5716 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5718 memset(tempPath, 0, sizeof(tempPath));
5719 memset(tempFileName, 0, sizeof(tempFileName));
5721 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5722 tempPath[0] = '.';
5724 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5725 pwcsName = tempFileName;
5726 else
5728 hr = STG_E_INSUFFICIENTMEMORY;
5729 goto end;
5732 creationMode = TRUNCATE_EXISTING;
5734 else
5736 creationMode = GetCreationModeFromSTGM(grfMode);
5740 * Interpret the STGM value grfMode
5742 shareMode = GetShareModeFromSTGM(grfMode);
5743 accessMode = GetAccessModeFromSTGM(grfMode);
5745 if (grfMode & STGM_DELETEONRELEASE)
5746 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5747 else
5748 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5750 if (grfMode & STGM_TRANSACTED)
5751 FIXME("Transacted mode not implemented.\n");
5754 * Initialize the "out" parameter.
5756 *ppstgOpen = 0;
5758 hFile = CreateFileW(pwcsName,
5759 accessMode,
5760 shareMode,
5761 NULL,
5762 creationMode,
5763 fileAttributes,
5766 if (hFile == INVALID_HANDLE_VALUE)
5768 if(GetLastError() == ERROR_FILE_EXISTS)
5769 hr = STG_E_FILEALREADYEXISTS;
5770 else
5771 hr = E_FAIL;
5772 goto end;
5776 * Allocate and initialize the new IStorage32object.
5778 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5780 if (newStorage == 0)
5782 hr = STG_E_INSUFFICIENTMEMORY;
5783 goto end;
5786 hr = StorageImpl_Construct(
5787 newStorage,
5788 hFile,
5789 pwcsName,
5790 NULL,
5791 grfMode,
5792 TRUE,
5793 TRUE);
5795 if (FAILED(hr))
5797 HeapFree(GetProcessHeap(), 0, newStorage);
5798 goto end;
5802 * Get an "out" pointer for the caller.
5804 hr = StorageBaseImpl_QueryInterface(
5805 (IStorage*)newStorage,
5806 (REFIID)&IID_IStorage,
5807 (void**)ppstgOpen);
5808 end:
5809 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5811 return hr;
5814 /******************************************************************************
5815 * StgCreateStorageEx [OLE32.@]
5817 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5819 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5820 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5822 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5824 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5825 return STG_E_INVALIDPARAMETER;
5828 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5830 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5831 return STG_E_INVALIDPARAMETER;
5834 if (stgfmt == STGFMT_FILE)
5836 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5837 return STG_E_INVALIDPARAMETER;
5840 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5842 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5843 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5846 ERR("Invalid stgfmt argument\n");
5847 return STG_E_INVALIDPARAMETER;
5850 /******************************************************************************
5851 * StgCreatePropSetStg [OLE32.@]
5853 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5854 IPropertySetStorage **ppPropSetStg)
5856 HRESULT hr;
5858 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5859 if (reserved)
5860 hr = STG_E_INVALIDPARAMETER;
5861 else
5862 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5863 (void**)ppPropSetStg);
5864 return hr;
5867 /******************************************************************************
5868 * StgOpenStorageEx [OLE32.@]
5870 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5872 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5873 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5875 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5877 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5878 return STG_E_INVALIDPARAMETER;
5881 switch (stgfmt)
5883 case STGFMT_FILE:
5884 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5885 return STG_E_INVALIDPARAMETER;
5887 case STGFMT_STORAGE:
5888 break;
5890 case STGFMT_DOCFILE:
5891 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5893 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5894 return STG_E_INVALIDPARAMETER;
5896 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5897 break;
5899 case STGFMT_ANY:
5900 WARN("STGFMT_ANY assuming storage\n");
5901 break;
5903 default:
5904 return STG_E_INVALIDPARAMETER;
5907 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5911 /******************************************************************************
5912 * StgOpenStorage [OLE32.@]
5914 HRESULT WINAPI StgOpenStorage(
5915 const OLECHAR *pwcsName,
5916 IStorage *pstgPriority,
5917 DWORD grfMode,
5918 SNB snbExclude,
5919 DWORD reserved,
5920 IStorage **ppstgOpen)
5922 StorageImpl* newStorage = 0;
5923 HRESULT hr = S_OK;
5924 HANDLE hFile = 0;
5925 DWORD shareMode;
5926 DWORD accessMode;
5927 WCHAR fullname[MAX_PATH];
5929 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5930 debugstr_w(pwcsName), pstgPriority, grfMode,
5931 snbExclude, reserved, ppstgOpen);
5934 * Perform sanity checks
5936 if (pwcsName == 0)
5938 hr = STG_E_INVALIDNAME;
5939 goto end;
5942 if (ppstgOpen == 0)
5944 hr = STG_E_INVALIDPOINTER;
5945 goto end;
5948 if (reserved)
5950 hr = STG_E_INVALIDPARAMETER;
5951 goto end;
5954 if (grfMode & STGM_PRIORITY)
5956 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5957 return STG_E_INVALIDFLAG;
5958 if (grfMode & STGM_DELETEONRELEASE)
5959 return STG_E_INVALIDFUNCTION;
5960 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5961 return STG_E_INVALIDFLAG;
5962 grfMode &= ~0xf0; /* remove the existing sharing mode */
5963 grfMode |= STGM_SHARE_DENY_NONE;
5965 /* STGM_PRIORITY stops other IStorage objects on the same file from
5966 * committing until the STGM_PRIORITY IStorage is closed. it also
5967 * stops non-transacted mode StgOpenStorage calls with write access from
5968 * succeeding. obviously, both of these cannot be achieved through just
5969 * file share flags */
5970 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5974 * Validate the sharing mode
5976 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5977 switch(STGM_SHARE_MODE(grfMode))
5979 case STGM_SHARE_EXCLUSIVE:
5980 case STGM_SHARE_DENY_WRITE:
5981 break;
5982 default:
5983 hr = STG_E_INVALIDFLAG;
5984 goto end;
5988 * Validate the STGM flags
5990 if ( FAILED( validateSTGM(grfMode) ) ||
5991 (grfMode&STGM_CREATE))
5993 hr = STG_E_INVALIDFLAG;
5994 goto end;
5997 /* shared reading requires transacted mode */
5998 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5999 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6000 !(grfMode&STGM_TRANSACTED) )
6002 hr = STG_E_INVALIDFLAG;
6003 goto end;
6007 * Interpret the STGM value grfMode
6009 shareMode = GetShareModeFromSTGM(grfMode);
6010 accessMode = GetAccessModeFromSTGM(grfMode);
6013 * Initialize the "out" parameter.
6015 *ppstgOpen = 0;
6017 hFile = CreateFileW( pwcsName,
6018 accessMode,
6019 shareMode,
6020 NULL,
6021 OPEN_EXISTING,
6022 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6025 if (hFile==INVALID_HANDLE_VALUE)
6027 DWORD last_error = GetLastError();
6029 hr = E_FAIL;
6031 switch (last_error)
6033 case ERROR_FILE_NOT_FOUND:
6034 hr = STG_E_FILENOTFOUND;
6035 break;
6037 case ERROR_PATH_NOT_FOUND:
6038 hr = STG_E_PATHNOTFOUND;
6039 break;
6041 case ERROR_ACCESS_DENIED:
6042 case ERROR_WRITE_PROTECT:
6043 hr = STG_E_ACCESSDENIED;
6044 break;
6046 case ERROR_SHARING_VIOLATION:
6047 hr = STG_E_SHAREVIOLATION;
6048 break;
6050 default:
6051 hr = E_FAIL;
6054 goto end;
6058 * Refuse to open the file if it's too small to be a structured storage file
6059 * FIXME: verify the file when reading instead of here
6061 if (GetFileSize(hFile, NULL) < 0x100)
6063 CloseHandle(hFile);
6064 hr = STG_E_FILEALREADYEXISTS;
6065 goto end;
6069 * Allocate and initialize the new IStorage32object.
6071 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6073 if (newStorage == 0)
6075 hr = STG_E_INSUFFICIENTMEMORY;
6076 goto end;
6079 /* Initialize the storage */
6080 hr = StorageImpl_Construct(
6081 newStorage,
6082 hFile,
6083 pwcsName,
6084 NULL,
6085 grfMode,
6086 TRUE,
6087 FALSE );
6089 if (FAILED(hr))
6091 HeapFree(GetProcessHeap(), 0, newStorage);
6093 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6095 if(hr == STG_E_INVALIDHEADER)
6096 hr = STG_E_FILEALREADYEXISTS;
6097 goto end;
6100 /* prepare the file name string given in lieu of the root property name */
6101 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6102 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6103 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6106 * Get an "out" pointer for the caller.
6108 hr = StorageBaseImpl_QueryInterface(
6109 (IStorage*)newStorage,
6110 (REFIID)&IID_IStorage,
6111 (void**)ppstgOpen);
6113 end:
6114 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6115 return hr;
6118 /******************************************************************************
6119 * StgCreateDocfileOnILockBytes [OLE32.@]
6121 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6122 ILockBytes *plkbyt,
6123 DWORD grfMode,
6124 DWORD reserved,
6125 IStorage** ppstgOpen)
6127 StorageImpl* newStorage = 0;
6128 HRESULT hr = S_OK;
6131 * Validate the parameters
6133 if ((ppstgOpen == 0) || (plkbyt == 0))
6134 return STG_E_INVALIDPOINTER;
6137 * Allocate and initialize the new IStorage object.
6139 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6141 if (newStorage == 0)
6142 return STG_E_INSUFFICIENTMEMORY;
6144 hr = StorageImpl_Construct(
6145 newStorage,
6148 plkbyt,
6149 grfMode,
6150 FALSE,
6151 TRUE);
6153 if (FAILED(hr))
6155 HeapFree(GetProcessHeap(), 0, newStorage);
6156 return hr;
6160 * Get an "out" pointer for the caller.
6162 hr = StorageBaseImpl_QueryInterface(
6163 (IStorage*)newStorage,
6164 (REFIID)&IID_IStorage,
6165 (void**)ppstgOpen);
6167 return hr;
6170 /******************************************************************************
6171 * StgOpenStorageOnILockBytes [OLE32.@]
6173 HRESULT WINAPI StgOpenStorageOnILockBytes(
6174 ILockBytes *plkbyt,
6175 IStorage *pstgPriority,
6176 DWORD grfMode,
6177 SNB snbExclude,
6178 DWORD reserved,
6179 IStorage **ppstgOpen)
6181 StorageImpl* newStorage = 0;
6182 HRESULT hr = S_OK;
6185 * Perform a sanity check
6187 if ((plkbyt == 0) || (ppstgOpen == 0))
6188 return STG_E_INVALIDPOINTER;
6191 * Validate the STGM flags
6193 if ( FAILED( validateSTGM(grfMode) ))
6194 return STG_E_INVALIDFLAG;
6197 * Initialize the "out" parameter.
6199 *ppstgOpen = 0;
6202 * Allocate and initialize the new IStorage object.
6204 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6206 if (newStorage == 0)
6207 return STG_E_INSUFFICIENTMEMORY;
6209 hr = StorageImpl_Construct(
6210 newStorage,
6213 plkbyt,
6214 grfMode,
6215 FALSE,
6216 FALSE);
6218 if (FAILED(hr))
6220 HeapFree(GetProcessHeap(), 0, newStorage);
6221 return hr;
6225 * Get an "out" pointer for the caller.
6227 hr = StorageBaseImpl_QueryInterface(
6228 (IStorage*)newStorage,
6229 (REFIID)&IID_IStorage,
6230 (void**)ppstgOpen);
6232 return hr;
6235 /******************************************************************************
6236 * StgSetTimes [ole32.@]
6237 * StgSetTimes [OLE32.@]
6241 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6242 FILETIME const *patime, FILETIME const *pmtime)
6244 IStorage *stg = NULL;
6245 HRESULT r;
6247 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6249 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6250 0, 0, &stg);
6251 if( SUCCEEDED(r) )
6253 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6254 IStorage_Release(stg);
6257 return r;
6260 /******************************************************************************
6261 * StgIsStorageILockBytes [OLE32.@]
6263 * Determines if the ILockBytes contains a storage object.
6265 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6267 BYTE sig[8];
6268 ULARGE_INTEGER offset;
6270 offset.u.HighPart = 0;
6271 offset.u.LowPart = 0;
6273 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6275 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6276 return S_OK;
6278 return S_FALSE;
6281 /******************************************************************************
6282 * WriteClassStg [OLE32.@]
6284 * This method will store the specified CLSID in the specified storage object
6286 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6288 HRESULT hRes;
6290 if(!pStg)
6291 return E_INVALIDARG;
6293 hRes = IStorage_SetClass(pStg, rclsid);
6295 return hRes;
6298 /***********************************************************************
6299 * ReadClassStg (OLE32.@)
6301 * This method reads the CLSID previously written to a storage object with
6302 * the WriteClassStg.
6304 * PARAMS
6305 * pstg [I] IStorage pointer
6306 * pclsid [O] Pointer to where the CLSID is written
6308 * RETURNS
6309 * Success: S_OK.
6310 * Failure: HRESULT code.
6312 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6314 STATSTG pstatstg;
6315 HRESULT hRes;
6317 TRACE("(%p, %p)\n", pstg, pclsid);
6319 if(!pstg || !pclsid)
6320 return E_INVALIDARG;
6323 * read a STATSTG structure (contains the clsid) from the storage
6325 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6327 if(SUCCEEDED(hRes))
6328 *pclsid=pstatstg.clsid;
6330 return hRes;
6333 /***********************************************************************
6334 * OleLoadFromStream (OLE32.@)
6336 * This function loads an object from stream
6338 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6340 CLSID clsid;
6341 HRESULT res;
6342 LPPERSISTSTREAM xstm;
6344 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6346 res=ReadClassStm(pStm,&clsid);
6347 if (!SUCCEEDED(res))
6348 return res;
6349 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6350 if (!SUCCEEDED(res))
6351 return res;
6352 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6353 if (!SUCCEEDED(res)) {
6354 IUnknown_Release((IUnknown*)*ppvObj);
6355 return res;
6357 res=IPersistStream_Load(xstm,pStm);
6358 IPersistStream_Release(xstm);
6359 /* FIXME: all refcounts ok at this point? I think they should be:
6360 * pStm : unchanged
6361 * ppvObj : 1
6362 * xstm : 0 (released)
6364 return res;
6367 /***********************************************************************
6368 * OleSaveToStream (OLE32.@)
6370 * This function saves an object with the IPersistStream interface on it
6371 * to the specified stream.
6373 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6376 CLSID clsid;
6377 HRESULT res;
6379 TRACE("(%p,%p)\n",pPStm,pStm);
6381 res=IPersistStream_GetClassID(pPStm,&clsid);
6383 if (SUCCEEDED(res)){
6385 res=WriteClassStm(pStm,&clsid);
6387 if (SUCCEEDED(res))
6389 res=IPersistStream_Save(pPStm,pStm,TRUE);
6392 TRACE("Finished Save\n");
6393 return res;
6396 /****************************************************************************
6397 * This method validate a STGM parameter that can contain the values below
6399 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6400 * The stgm values contained in 0xffff0000 are bitmasks.
6402 * STGM_DIRECT 0x00000000
6403 * STGM_TRANSACTED 0x00010000
6404 * STGM_SIMPLE 0x08000000
6406 * STGM_READ 0x00000000
6407 * STGM_WRITE 0x00000001
6408 * STGM_READWRITE 0x00000002
6410 * STGM_SHARE_DENY_NONE 0x00000040
6411 * STGM_SHARE_DENY_READ 0x00000030
6412 * STGM_SHARE_DENY_WRITE 0x00000020
6413 * STGM_SHARE_EXCLUSIVE 0x00000010
6415 * STGM_PRIORITY 0x00040000
6416 * STGM_DELETEONRELEASE 0x04000000
6418 * STGM_CREATE 0x00001000
6419 * STGM_CONVERT 0x00020000
6420 * STGM_FAILIFTHERE 0x00000000
6422 * STGM_NOSCRATCH 0x00100000
6423 * STGM_NOSNAPSHOT 0x00200000
6425 static HRESULT validateSTGM(DWORD stgm)
6427 DWORD access = STGM_ACCESS_MODE(stgm);
6428 DWORD share = STGM_SHARE_MODE(stgm);
6429 DWORD create = STGM_CREATE_MODE(stgm);
6431 if (stgm&~STGM_KNOWN_FLAGS)
6433 ERR("unknown flags %08x\n", stgm);
6434 return E_FAIL;
6437 switch (access)
6439 case STGM_READ:
6440 case STGM_WRITE:
6441 case STGM_READWRITE:
6442 break;
6443 default:
6444 return E_FAIL;
6447 switch (share)
6449 case STGM_SHARE_DENY_NONE:
6450 case STGM_SHARE_DENY_READ:
6451 case STGM_SHARE_DENY_WRITE:
6452 case STGM_SHARE_EXCLUSIVE:
6453 break;
6454 default:
6455 return E_FAIL;
6458 switch (create)
6460 case STGM_CREATE:
6461 case STGM_FAILIFTHERE:
6462 break;
6463 default:
6464 return E_FAIL;
6468 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6470 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6471 return E_FAIL;
6474 * STGM_CREATE | STGM_CONVERT
6475 * if both are false, STGM_FAILIFTHERE is set to TRUE
6477 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6478 return E_FAIL;
6481 * STGM_NOSCRATCH requires STGM_TRANSACTED
6483 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6484 return E_FAIL;
6487 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6488 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6490 if ( (stgm & STGM_NOSNAPSHOT) &&
6491 (!(stgm & STGM_TRANSACTED) ||
6492 share == STGM_SHARE_EXCLUSIVE ||
6493 share == STGM_SHARE_DENY_WRITE) )
6494 return E_FAIL;
6496 return S_OK;
6499 /****************************************************************************
6500 * GetShareModeFromSTGM
6502 * This method will return a share mode flag from a STGM value.
6503 * The STGM value is assumed valid.
6505 static DWORD GetShareModeFromSTGM(DWORD stgm)
6507 switch (STGM_SHARE_MODE(stgm))
6509 case STGM_SHARE_DENY_NONE:
6510 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6511 case STGM_SHARE_DENY_READ:
6512 return FILE_SHARE_WRITE;
6513 case STGM_SHARE_DENY_WRITE:
6514 return FILE_SHARE_READ;
6515 case STGM_SHARE_EXCLUSIVE:
6516 return 0;
6518 ERR("Invalid share mode!\n");
6519 assert(0);
6520 return 0;
6523 /****************************************************************************
6524 * GetAccessModeFromSTGM
6526 * This method will return an access mode flag from a STGM value.
6527 * The STGM value is assumed valid.
6529 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6531 switch (STGM_ACCESS_MODE(stgm))
6533 case STGM_READ:
6534 return GENERIC_READ;
6535 case STGM_WRITE:
6536 case STGM_READWRITE:
6537 return GENERIC_READ | GENERIC_WRITE;
6539 ERR("Invalid access mode!\n");
6540 assert(0);
6541 return 0;
6544 /****************************************************************************
6545 * GetCreationModeFromSTGM
6547 * This method will return a creation mode flag from a STGM value.
6548 * The STGM value is assumed valid.
6550 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6552 switch(STGM_CREATE_MODE(stgm))
6554 case STGM_CREATE:
6555 return CREATE_ALWAYS;
6556 case STGM_CONVERT:
6557 FIXME("STGM_CONVERT not implemented!\n");
6558 return CREATE_NEW;
6559 case STGM_FAILIFTHERE:
6560 return CREATE_NEW;
6562 ERR("Invalid create mode!\n");
6563 assert(0);
6564 return 0;
6568 /*************************************************************************
6569 * OLECONVERT_LoadOLE10 [Internal]
6571 * Loads the OLE10 STREAM to memory
6573 * PARAMS
6574 * pOleStream [I] The OLESTREAM
6575 * pData [I] Data Structure for the OLESTREAM Data
6577 * RETURNS
6578 * Success: S_OK
6579 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6580 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6582 * NOTES
6583 * This function is used by OleConvertOLESTREAMToIStorage only.
6585 * Memory allocated for pData must be freed by the caller
6587 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6589 DWORD dwSize;
6590 HRESULT hRes = S_OK;
6591 int nTryCnt=0;
6592 int max_try = 6;
6594 pData->pData = NULL;
6595 pData->pstrOleObjFileName = (CHAR *) NULL;
6597 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6599 /* Get the OleID */
6600 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6601 if(dwSize != sizeof(pData->dwOleID))
6603 hRes = CONVERT10_E_OLESTREAM_GET;
6605 else if(pData->dwOleID != OLESTREAM_ID)
6607 hRes = CONVERT10_E_OLESTREAM_FMT;
6609 else
6611 hRes = S_OK;
6612 break;
6616 if(hRes == S_OK)
6618 /* Get the TypeID...more info needed for this field */
6619 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6620 if(dwSize != sizeof(pData->dwTypeID))
6622 hRes = CONVERT10_E_OLESTREAM_GET;
6625 if(hRes == S_OK)
6627 if(pData->dwTypeID != 0)
6629 /* Get the length of the OleTypeName */
6630 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6631 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6633 hRes = CONVERT10_E_OLESTREAM_GET;
6636 if(hRes == S_OK)
6638 if(pData->dwOleTypeNameLength > 0)
6640 /* Get the OleTypeName */
6641 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6642 if(dwSize != pData->dwOleTypeNameLength)
6644 hRes = CONVERT10_E_OLESTREAM_GET;
6648 if(bStrem1)
6650 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6651 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6653 hRes = CONVERT10_E_OLESTREAM_GET;
6655 if(hRes == S_OK)
6657 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6658 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6659 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6660 if(pData->pstrOleObjFileName)
6662 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6663 if(dwSize != pData->dwOleObjFileNameLength)
6665 hRes = CONVERT10_E_OLESTREAM_GET;
6668 else
6669 hRes = CONVERT10_E_OLESTREAM_GET;
6672 else
6674 /* Get the Width of the Metafile */
6675 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6676 if(dwSize != sizeof(pData->dwMetaFileWidth))
6678 hRes = CONVERT10_E_OLESTREAM_GET;
6680 if(hRes == S_OK)
6682 /* Get the Height of the Metafile */
6683 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6684 if(dwSize != sizeof(pData->dwMetaFileHeight))
6686 hRes = CONVERT10_E_OLESTREAM_GET;
6690 if(hRes == S_OK)
6692 /* Get the Length of the Data */
6693 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6694 if(dwSize != sizeof(pData->dwDataLength))
6696 hRes = CONVERT10_E_OLESTREAM_GET;
6700 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6702 if(!bStrem1) /* if it is a second OLE stream data */
6704 pData->dwDataLength -= 8;
6705 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6706 if(dwSize != sizeof(pData->strUnknown))
6708 hRes = CONVERT10_E_OLESTREAM_GET;
6712 if(hRes == S_OK)
6714 if(pData->dwDataLength > 0)
6716 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6718 /* Get Data (ex. IStorage, Metafile, or BMP) */
6719 if(pData->pData)
6721 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6722 if(dwSize != pData->dwDataLength)
6724 hRes = CONVERT10_E_OLESTREAM_GET;
6727 else
6729 hRes = CONVERT10_E_OLESTREAM_GET;
6735 return hRes;
6738 /*************************************************************************
6739 * OLECONVERT_SaveOLE10 [Internal]
6741 * Saves the OLE10 STREAM From memory
6743 * PARAMS
6744 * pData [I] Data Structure for the OLESTREAM Data
6745 * pOleStream [I] The OLESTREAM to save
6747 * RETURNS
6748 * Success: S_OK
6749 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6751 * NOTES
6752 * This function is used by OleConvertIStorageToOLESTREAM only.
6755 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6757 DWORD dwSize;
6758 HRESULT hRes = S_OK;
6761 /* Set the OleID */
6762 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6763 if(dwSize != sizeof(pData->dwOleID))
6765 hRes = CONVERT10_E_OLESTREAM_PUT;
6768 if(hRes == S_OK)
6770 /* Set the TypeID */
6771 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6772 if(dwSize != sizeof(pData->dwTypeID))
6774 hRes = CONVERT10_E_OLESTREAM_PUT;
6778 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6780 /* Set the Length of the OleTypeName */
6781 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6782 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6784 hRes = CONVERT10_E_OLESTREAM_PUT;
6787 if(hRes == S_OK)
6789 if(pData->dwOleTypeNameLength > 0)
6791 /* Set the OleTypeName */
6792 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6793 if(dwSize != pData->dwOleTypeNameLength)
6795 hRes = CONVERT10_E_OLESTREAM_PUT;
6800 if(hRes == S_OK)
6802 /* Set the width of the Metafile */
6803 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6804 if(dwSize != sizeof(pData->dwMetaFileWidth))
6806 hRes = CONVERT10_E_OLESTREAM_PUT;
6810 if(hRes == S_OK)
6812 /* Set the height of the Metafile */
6813 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6814 if(dwSize != sizeof(pData->dwMetaFileHeight))
6816 hRes = CONVERT10_E_OLESTREAM_PUT;
6820 if(hRes == S_OK)
6822 /* Set the length of the Data */
6823 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6824 if(dwSize != sizeof(pData->dwDataLength))
6826 hRes = CONVERT10_E_OLESTREAM_PUT;
6830 if(hRes == S_OK)
6832 if(pData->dwDataLength > 0)
6834 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6835 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6836 if(dwSize != pData->dwDataLength)
6838 hRes = CONVERT10_E_OLESTREAM_PUT;
6843 return hRes;
6846 /*************************************************************************
6847 * OLECONVERT_GetOLE20FromOLE10[Internal]
6849 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6850 * opens it, and copies the content to the dest IStorage for
6851 * OleConvertOLESTREAMToIStorage
6854 * PARAMS
6855 * pDestStorage [I] The IStorage to copy the data to
6856 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6857 * nBufferLength [I] The size of the buffer
6859 * RETURNS
6860 * Nothing
6862 * NOTES
6866 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6868 HRESULT hRes;
6869 HANDLE hFile;
6870 IStorage *pTempStorage;
6871 DWORD dwNumOfBytesWritten;
6872 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6873 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6875 /* Create a temp File */
6876 GetTempPathW(MAX_PATH, wstrTempDir);
6877 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6878 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6880 if(hFile != INVALID_HANDLE_VALUE)
6882 /* Write IStorage Data to File */
6883 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6884 CloseHandle(hFile);
6886 /* Open and copy temp storage to the Dest Storage */
6887 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6888 if(hRes == S_OK)
6890 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6891 StorageBaseImpl_Release(pTempStorage);
6893 DeleteFileW(wstrTempFile);
6898 /*************************************************************************
6899 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6901 * Saves the OLE10 STREAM From memory
6903 * PARAMS
6904 * pStorage [I] The Src IStorage to copy
6905 * pData [I] The Dest Memory to write to.
6907 * RETURNS
6908 * The size in bytes allocated for pData
6910 * NOTES
6911 * Memory allocated for pData must be freed by the caller
6913 * Used by OleConvertIStorageToOLESTREAM only.
6916 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6918 HANDLE hFile;
6919 HRESULT hRes;
6920 DWORD nDataLength = 0;
6921 IStorage *pTempStorage;
6922 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6923 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6925 *pData = NULL;
6927 /* Create temp Storage */
6928 GetTempPathW(MAX_PATH, wstrTempDir);
6929 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6930 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6932 if(hRes == S_OK)
6934 /* Copy Src Storage to the Temp Storage */
6935 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6936 StorageBaseImpl_Release(pTempStorage);
6938 /* Open Temp Storage as a file and copy to memory */
6939 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6940 if(hFile != INVALID_HANDLE_VALUE)
6942 nDataLength = GetFileSize(hFile, NULL);
6943 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6944 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6945 CloseHandle(hFile);
6947 DeleteFileW(wstrTempFile);
6949 return nDataLength;
6952 /*************************************************************************
6953 * OLECONVERT_CreateOleStream [Internal]
6955 * Creates the "\001OLE" stream in the IStorage if necessary.
6957 * PARAMS
6958 * pStorage [I] Dest storage to create the stream in
6960 * RETURNS
6961 * Nothing
6963 * NOTES
6964 * This function is used by OleConvertOLESTREAMToIStorage only.
6966 * This stream is still unknown, MS Word seems to have extra data
6967 * but since the data is stored in the OLESTREAM there should be
6968 * no need to recreate the stream. If the stream is manually
6969 * deleted it will create it with this default data.
6972 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6974 HRESULT hRes;
6975 IStream *pStream;
6976 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6977 BYTE pOleStreamHeader [] =
6979 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6980 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6981 0x00, 0x00, 0x00, 0x00
6984 /* Create stream if not present */
6985 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6986 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6988 if(hRes == S_OK)
6990 /* Write default Data */
6991 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6992 IStream_Release(pStream);
6996 /* write a string to a stream, preceded by its length */
6997 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6999 HRESULT r;
7000 LPSTR str;
7001 DWORD len = 0;
7003 if( string )
7004 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7005 r = IStream_Write( stm, &len, sizeof(len), NULL);
7006 if( FAILED( r ) )
7007 return r;
7008 if(len == 0)
7009 return r;
7010 str = CoTaskMemAlloc( len );
7011 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7012 r = IStream_Write( stm, str, len, NULL);
7013 CoTaskMemFree( str );
7014 return r;
7017 /* read a string preceded by its length from a stream */
7018 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7020 HRESULT r;
7021 DWORD len, count = 0;
7022 LPSTR str;
7023 LPWSTR wstr;
7025 r = IStream_Read( stm, &len, sizeof(len), &count );
7026 if( FAILED( r ) )
7027 return r;
7028 if( count != sizeof(len) )
7029 return E_OUTOFMEMORY;
7031 TRACE("%d bytes\n",len);
7033 str = CoTaskMemAlloc( len );
7034 if( !str )
7035 return E_OUTOFMEMORY;
7036 count = 0;
7037 r = IStream_Read( stm, str, len, &count );
7038 if( FAILED( r ) )
7039 return r;
7040 if( count != len )
7042 CoTaskMemFree( str );
7043 return E_OUTOFMEMORY;
7046 TRACE("Read string %s\n",debugstr_an(str,len));
7048 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7049 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7050 if( wstr )
7051 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7052 CoTaskMemFree( str );
7054 *string = wstr;
7056 return r;
7060 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7061 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7063 IStream *pstm;
7064 HRESULT r = S_OK;
7065 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7067 static const BYTE unknown1[12] =
7068 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7069 0xFF, 0xFF, 0xFF, 0xFF};
7070 static const BYTE unknown2[16] =
7071 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7072 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7074 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7075 debugstr_w(lpszUserType), debugstr_w(szClipName),
7076 debugstr_w(szProgIDName));
7078 /* Create a CompObj stream if it doesn't exist */
7079 r = IStorage_CreateStream(pstg, szwStreamName,
7080 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7081 if( FAILED (r) )
7082 return r;
7084 /* Write CompObj Structure to stream */
7085 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7087 if( SUCCEEDED( r ) )
7088 r = WriteClassStm( pstm, clsid );
7090 if( SUCCEEDED( r ) )
7091 r = STREAM_WriteString( pstm, lpszUserType );
7092 if( SUCCEEDED( r ) )
7093 r = STREAM_WriteString( pstm, szClipName );
7094 if( SUCCEEDED( r ) )
7095 r = STREAM_WriteString( pstm, szProgIDName );
7096 if( SUCCEEDED( r ) )
7097 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7099 IStream_Release( pstm );
7101 return r;
7104 /***********************************************************************
7105 * WriteFmtUserTypeStg (OLE32.@)
7107 HRESULT WINAPI WriteFmtUserTypeStg(
7108 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7110 HRESULT r;
7111 WCHAR szwClipName[0x40];
7112 CLSID clsid = CLSID_NULL;
7113 LPWSTR wstrProgID = NULL;
7114 DWORD n;
7116 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7118 /* get the clipboard format name */
7119 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
7120 szwClipName[n]=0;
7122 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7124 /* FIXME: There's room to save a CLSID and its ProgID, but
7125 the CLSID is not looked up in the registry and in all the
7126 tests I wrote it was CLSID_NULL. Where does it come from?
7129 /* get the real program ID. This may fail, but that's fine */
7130 ProgIDFromCLSID(&clsid, &wstrProgID);
7132 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7134 r = STORAGE_WriteCompObj( pstg, &clsid,
7135 lpszUserType, szwClipName, wstrProgID );
7137 CoTaskMemFree(wstrProgID);
7139 return r;
7143 /******************************************************************************
7144 * ReadFmtUserTypeStg [OLE32.@]
7146 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7148 HRESULT r;
7149 IStream *stm = 0;
7150 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7151 unsigned char unknown1[12];
7152 unsigned char unknown2[16];
7153 DWORD count;
7154 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7155 CLSID clsid;
7157 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7159 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7160 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7161 if( FAILED ( r ) )
7163 WARN("Failed to open stream r = %08x\n", r);
7164 return r;
7167 /* read the various parts of the structure */
7168 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7169 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7170 goto end;
7171 r = ReadClassStm( stm, &clsid );
7172 if( FAILED( r ) )
7173 goto end;
7175 r = STREAM_ReadString( stm, &szCLSIDName );
7176 if( FAILED( r ) )
7177 goto end;
7179 r = STREAM_ReadString( stm, &szOleTypeName );
7180 if( FAILED( r ) )
7181 goto end;
7183 r = STREAM_ReadString( stm, &szProgIDName );
7184 if( FAILED( r ) )
7185 goto end;
7187 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7188 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7189 goto end;
7191 /* ok, success... now we just need to store what we found */
7192 if( pcf )
7193 *pcf = RegisterClipboardFormatW( szOleTypeName );
7194 CoTaskMemFree( szOleTypeName );
7196 if( lplpszUserType )
7197 *lplpszUserType = szCLSIDName;
7198 CoTaskMemFree( szProgIDName );
7200 end:
7201 IStream_Release( stm );
7203 return r;
7207 /*************************************************************************
7208 * OLECONVERT_CreateCompObjStream [Internal]
7210 * Creates a "\001CompObj" is the destination IStorage if necessary.
7212 * PARAMS
7213 * pStorage [I] The dest IStorage to create the CompObj Stream
7214 * if necessary.
7215 * strOleTypeName [I] The ProgID
7217 * RETURNS
7218 * Success: S_OK
7219 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7221 * NOTES
7222 * This function is used by OleConvertOLESTREAMToIStorage only.
7224 * The stream data is stored in the OLESTREAM and there should be
7225 * no need to recreate the stream. If the stream is manually
7226 * deleted it will attempt to create it by querying the registry.
7230 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7232 IStream *pStream;
7233 HRESULT hStorageRes, hRes = S_OK;
7234 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7235 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7236 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7238 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7239 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7241 /* Initialize the CompObj structure */
7242 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7243 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7244 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7247 /* Create a CompObj stream if it doesn't exist */
7248 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7249 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7250 if(hStorageRes == S_OK)
7252 /* copy the OleTypeName to the compobj struct */
7253 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7254 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7256 /* copy the OleTypeName to the compobj struct */
7257 /* Note: in the test made, these were Identical */
7258 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7259 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7261 /* Get the CLSID */
7262 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7263 bufferW, OLESTREAM_MAX_STR_LEN );
7264 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7266 if(hRes == S_OK)
7268 HKEY hKey;
7269 LONG hErr;
7270 /* Get the CLSID Default Name from the Registry */
7271 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7272 if(hErr == ERROR_SUCCESS)
7274 char strTemp[OLESTREAM_MAX_STR_LEN];
7275 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7276 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7277 if(hErr == ERROR_SUCCESS)
7279 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7281 RegCloseKey(hKey);
7285 /* Write CompObj Structure to stream */
7286 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7288 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7290 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7291 if(IStorageCompObj.dwCLSIDNameLength > 0)
7293 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7295 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7296 if(IStorageCompObj.dwOleTypeNameLength > 0)
7298 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7300 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7301 if(IStorageCompObj.dwProgIDNameLength > 0)
7303 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7305 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7306 IStream_Release(pStream);
7308 return hRes;
7312 /*************************************************************************
7313 * OLECONVERT_CreateOlePresStream[Internal]
7315 * Creates the "\002OlePres000" Stream with the Metafile data
7317 * PARAMS
7318 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7319 * dwExtentX [I] Width of the Metafile
7320 * dwExtentY [I] Height of the Metafile
7321 * pData [I] Metafile data
7322 * dwDataLength [I] Size of the Metafile data
7324 * RETURNS
7325 * Success: S_OK
7326 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7328 * NOTES
7329 * This function is used by OleConvertOLESTREAMToIStorage only.
7332 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7334 HRESULT hRes;
7335 IStream *pStream;
7336 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7337 BYTE pOlePresStreamHeader [] =
7339 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7340 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7341 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7342 0x00, 0x00, 0x00, 0x00
7345 BYTE pOlePresStreamHeaderEmpty [] =
7347 0x00, 0x00, 0x00, 0x00,
7348 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7349 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7350 0x00, 0x00, 0x00, 0x00
7353 /* Create the OlePres000 Stream */
7354 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7355 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7357 if(hRes == S_OK)
7359 DWORD nHeaderSize;
7360 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7362 memset(&OlePres, 0, sizeof(OlePres));
7363 /* Do we have any metafile data to save */
7364 if(dwDataLength > 0)
7366 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7367 nHeaderSize = sizeof(pOlePresStreamHeader);
7369 else
7371 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7372 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7374 /* Set width and height of the metafile */
7375 OlePres.dwExtentX = dwExtentX;
7376 OlePres.dwExtentY = -dwExtentY;
7378 /* Set Data and Length */
7379 if(dwDataLength > sizeof(METAFILEPICT16))
7381 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7382 OlePres.pData = &(pData[8]);
7384 /* Save OlePres000 Data to Stream */
7385 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7386 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7387 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7388 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7389 if(OlePres.dwSize > 0)
7391 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7393 IStream_Release(pStream);
7397 /*************************************************************************
7398 * OLECONVERT_CreateOle10NativeStream [Internal]
7400 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7402 * PARAMS
7403 * pStorage [I] Dest storage to create the stream in
7404 * pData [I] Ole10 Native Data (ex. bmp)
7405 * dwDataLength [I] Size of the Ole10 Native Data
7407 * RETURNS
7408 * Nothing
7410 * NOTES
7411 * This function is used by OleConvertOLESTREAMToIStorage only.
7413 * Might need to verify the data and return appropriate error message
7416 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7418 HRESULT hRes;
7419 IStream *pStream;
7420 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7422 /* Create the Ole10Native Stream */
7423 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7424 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7426 if(hRes == S_OK)
7428 /* Write info to stream */
7429 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7430 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7431 IStream_Release(pStream);
7436 /*************************************************************************
7437 * OLECONVERT_GetOLE10ProgID [Internal]
7439 * Finds the ProgID (or OleTypeID) from the IStorage
7441 * PARAMS
7442 * pStorage [I] The Src IStorage to get the ProgID
7443 * strProgID [I] the ProgID string to get
7444 * dwSize [I] the size of the string
7446 * RETURNS
7447 * Success: S_OK
7448 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7450 * NOTES
7451 * This function is used by OleConvertIStorageToOLESTREAM only.
7455 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7457 HRESULT hRes;
7458 IStream *pStream;
7459 LARGE_INTEGER iSeekPos;
7460 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7461 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7463 /* Open the CompObj Stream */
7464 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7465 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7466 if(hRes == S_OK)
7469 /*Get the OleType from the CompObj Stream */
7470 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7471 iSeekPos.u.HighPart = 0;
7473 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7474 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7475 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7476 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7477 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7478 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7479 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7481 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7482 if(*dwSize > 0)
7484 IStream_Read(pStream, strProgID, *dwSize, NULL);
7486 IStream_Release(pStream);
7488 else
7490 STATSTG stat;
7491 LPOLESTR wstrProgID;
7493 /* Get the OleType from the registry */
7494 REFCLSID clsid = &(stat.clsid);
7495 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7496 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7497 if(hRes == S_OK)
7499 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7503 return hRes;
7506 /*************************************************************************
7507 * OLECONVERT_GetOle10PresData [Internal]
7509 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7511 * PARAMS
7512 * pStorage [I] Src IStroage
7513 * pOleStream [I] Dest OleStream Mem Struct
7515 * RETURNS
7516 * Nothing
7518 * NOTES
7519 * This function is used by OleConvertIStorageToOLESTREAM only.
7521 * Memory allocated for pData must be freed by the caller
7525 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7528 HRESULT hRes;
7529 IStream *pStream;
7530 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7532 /* Initialize Default data for OLESTREAM */
7533 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7534 pOleStreamData[0].dwTypeID = 2;
7535 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7536 pOleStreamData[1].dwTypeID = 0;
7537 pOleStreamData[0].dwMetaFileWidth = 0;
7538 pOleStreamData[0].dwMetaFileHeight = 0;
7539 pOleStreamData[0].pData = NULL;
7540 pOleStreamData[1].pData = NULL;
7542 /* Open Ole10Native Stream */
7543 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7544 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7545 if(hRes == S_OK)
7548 /* Read Size and Data */
7549 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7550 if(pOleStreamData->dwDataLength > 0)
7552 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7553 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7555 IStream_Release(pStream);
7561 /*************************************************************************
7562 * OLECONVERT_GetOle20PresData[Internal]
7564 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7566 * PARAMS
7567 * pStorage [I] Src IStroage
7568 * pOleStreamData [I] Dest OleStream Mem Struct
7570 * RETURNS
7571 * Nothing
7573 * NOTES
7574 * This function is used by OleConvertIStorageToOLESTREAM only.
7576 * Memory allocated for pData must be freed by the caller
7578 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7580 HRESULT hRes;
7581 IStream *pStream;
7582 OLECONVERT_ISTORAGE_OLEPRES olePress;
7583 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7585 /* Initialize Default data for OLESTREAM */
7586 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7587 pOleStreamData[0].dwTypeID = 2;
7588 pOleStreamData[0].dwMetaFileWidth = 0;
7589 pOleStreamData[0].dwMetaFileHeight = 0;
7590 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7591 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7592 pOleStreamData[1].dwTypeID = 0;
7593 pOleStreamData[1].dwOleTypeNameLength = 0;
7594 pOleStreamData[1].strOleTypeName[0] = 0;
7595 pOleStreamData[1].dwMetaFileWidth = 0;
7596 pOleStreamData[1].dwMetaFileHeight = 0;
7597 pOleStreamData[1].pData = NULL;
7598 pOleStreamData[1].dwDataLength = 0;
7601 /* Open OlePress000 stream */
7602 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7603 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7604 if(hRes == S_OK)
7606 LARGE_INTEGER iSeekPos;
7607 METAFILEPICT16 MetaFilePict;
7608 static const char strMetafilePictName[] = "METAFILEPICT";
7610 /* Set the TypeID for a Metafile */
7611 pOleStreamData[1].dwTypeID = 5;
7613 /* Set the OleTypeName to Metafile */
7614 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7615 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7617 iSeekPos.u.HighPart = 0;
7618 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7620 /* Get Presentation Data */
7621 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7622 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7623 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7624 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7626 /*Set width and Height */
7627 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7628 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7629 if(olePress.dwSize > 0)
7631 /* Set Length */
7632 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7634 /* Set MetaFilePict struct */
7635 MetaFilePict.mm = 8;
7636 MetaFilePict.xExt = olePress.dwExtentX;
7637 MetaFilePict.yExt = olePress.dwExtentY;
7638 MetaFilePict.hMF = 0;
7640 /* Get Metafile Data */
7641 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7642 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7643 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7645 IStream_Release(pStream);
7649 /*************************************************************************
7650 * OleConvertOLESTREAMToIStorage [OLE32.@]
7652 * Read info on MSDN
7654 * TODO
7655 * DVTARGETDEVICE paramenter is not handled
7656 * Still unsure of some mem fields for OLE 10 Stream
7657 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7658 * and "\001OLE" streams
7661 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7662 LPOLESTREAM pOleStream,
7663 LPSTORAGE pstg,
7664 const DVTARGETDEVICE* ptd)
7666 int i;
7667 HRESULT hRes=S_OK;
7668 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7670 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7672 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7674 if(ptd != NULL)
7676 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7679 if(pstg == NULL || pOleStream == NULL)
7681 hRes = E_INVALIDARG;
7684 if(hRes == S_OK)
7686 /* Load the OLESTREAM to Memory */
7687 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7690 if(hRes == S_OK)
7692 /* Load the OLESTREAM to Memory (part 2)*/
7693 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7696 if(hRes == S_OK)
7699 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7701 /* Do we have the IStorage Data in the OLESTREAM */
7702 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7704 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7705 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7707 else
7709 /* It must be an original OLE 1.0 source */
7710 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7713 else
7715 /* It must be an original OLE 1.0 source */
7716 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7719 /* Create CompObj Stream if necessary */
7720 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7721 if(hRes == S_OK)
7723 /*Create the Ole Stream if necessary */
7724 OLECONVERT_CreateOleStream(pstg);
7729 /* Free allocated memory */
7730 for(i=0; i < 2; i++)
7732 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7733 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7734 pOleStreamData[i].pstrOleObjFileName = NULL;
7736 return hRes;
7739 /*************************************************************************
7740 * OleConvertIStorageToOLESTREAM [OLE32.@]
7742 * Read info on MSDN
7744 * Read info on MSDN
7746 * TODO
7747 * Still unsure of some mem fields for OLE 10 Stream
7748 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7749 * and "\001OLE" streams.
7752 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7753 LPSTORAGE pstg,
7754 LPOLESTREAM pOleStream)
7756 int i;
7757 HRESULT hRes = S_OK;
7758 IStream *pStream;
7759 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7760 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7762 TRACE("%p %p\n", pstg, pOleStream);
7764 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7766 if(pstg == NULL || pOleStream == NULL)
7768 hRes = E_INVALIDARG;
7770 if(hRes == S_OK)
7772 /* Get the ProgID */
7773 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7774 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7776 if(hRes == S_OK)
7778 /* Was it originally Ole10 */
7779 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7780 if(hRes == S_OK)
7782 IStream_Release(pStream);
7783 /* Get Presentation Data for Ole10Native */
7784 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7786 else
7788 /* Get Presentation Data (OLE20) */
7789 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7792 /* Save OLESTREAM */
7793 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7794 if(hRes == S_OK)
7796 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7801 /* Free allocated memory */
7802 for(i=0; i < 2; i++)
7804 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7807 return hRes;
7810 /***********************************************************************
7811 * GetConvertStg (OLE32.@)
7813 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7814 FIXME("unimplemented stub!\n");
7815 return E_FAIL;
7818 /******************************************************************************
7819 * StgIsStorageFile [OLE32.@]
7820 * Verify if the file contains a storage object
7822 * PARAMS
7823 * fn [ I] Filename
7825 * RETURNS
7826 * S_OK if file has magic bytes as a storage object
7827 * S_FALSE if file is not storage
7829 HRESULT WINAPI
7830 StgIsStorageFile(LPCOLESTR fn)
7832 HANDLE hf;
7833 BYTE magic[8];
7834 DWORD bytes_read;
7836 TRACE("%s\n", debugstr_w(fn));
7837 hf = CreateFileW(fn, GENERIC_READ,
7838 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7839 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7841 if (hf == INVALID_HANDLE_VALUE)
7842 return STG_E_FILENOTFOUND;
7844 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7846 WARN(" unable to read file\n");
7847 CloseHandle(hf);
7848 return S_FALSE;
7851 CloseHandle(hf);
7853 if (bytes_read != 8) {
7854 WARN(" too short\n");
7855 return S_FALSE;
7858 if (!memcmp(magic,STORAGE_magic,8)) {
7859 WARN(" -> YES\n");
7860 return S_OK;
7863 WARN(" -> Invalid header.\n");
7864 return S_FALSE;
7867 /***********************************************************************
7868 * WriteClassStm (OLE32.@)
7870 * Writes a CLSID to a stream.
7872 * PARAMS
7873 * pStm [I] Stream to write to.
7874 * rclsid [I] CLSID to write.
7876 * RETURNS
7877 * Success: S_OK.
7878 * Failure: HRESULT code.
7880 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7882 TRACE("(%p,%p)\n",pStm,rclsid);
7884 if (!pStm || !rclsid)
7885 return E_INVALIDARG;
7887 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7890 /***********************************************************************
7891 * ReadClassStm (OLE32.@)
7893 * Reads a CLSID from a stream.
7895 * PARAMS
7896 * pStm [I] Stream to read from.
7897 * rclsid [O] CLSID to read.
7899 * RETURNS
7900 * Success: S_OK.
7901 * Failure: HRESULT code.
7903 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7905 ULONG nbByte;
7906 HRESULT res;
7908 TRACE("(%p,%p)\n",pStm,pclsid);
7910 if (!pStm || !pclsid)
7911 return E_INVALIDARG;
7913 /* clear the output args */
7914 memcpy(pclsid, &CLSID_NULL, sizeof(*pclsid));
7916 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7918 if (FAILED(res))
7919 return res;
7921 if (nbByte != sizeof(CLSID))
7922 return STG_E_READFAULT;
7923 else
7924 return S_OK;