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
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.
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic
[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic
[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static const char rootPropertyName
[] = "Root Entry";
70 /****************************************************************************
71 * Storage32InternalImpl definitions.
73 * Definition of the implementation structure for the IStorage32 interface.
74 * This one implements the IStorage32 interface for storage that are
75 * inside another storage.
77 struct StorageInternalImpl
79 struct StorageBaseImpl base
;
81 * There is no specific data for this class.
84 typedef struct StorageInternalImpl StorageInternalImpl
;
86 /* Method definitions for the Storage32InternalImpl class. */
87 static StorageInternalImpl
* StorageInternalImpl_Construct(StorageImpl
* ancestorStorage
,
88 DWORD openFlags
, ULONG rootTropertyIndex
);
89 static void StorageImpl_Destroy(StorageBaseImpl
* iface
);
90 static BOOL
StorageImpl_ReadBigBlock(StorageImpl
* This
, ULONG blockIndex
, void* buffer
);
91 static BOOL
StorageImpl_WriteBigBlock(StorageImpl
* This
, ULONG blockIndex
, const void* buffer
);
92 static void StorageImpl_SetNextBlockInChain(StorageImpl
* This
, ULONG blockIndex
, ULONG nextBlock
);
93 static HRESULT
StorageImpl_LoadFileHeader(StorageImpl
* This
);
94 static void StorageImpl_SaveFileHeader(StorageImpl
* This
);
96 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
);
97 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
);
98 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
);
99 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
);
100 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
);
102 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
);
103 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
);
104 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
);
106 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
);
107 static BOOL
StorageImpl_WriteDWordToBigBlock( StorageImpl
* This
,
108 ULONG blockIndex
, ULONG offset
, DWORD value
);
109 static BOOL
StorageImpl_ReadDWordFromBigBlock( StorageImpl
* This
,
110 ULONG blockIndex
, ULONG offset
, DWORD
* value
);
112 /* OLESTREAM memory structure to use for Get and Put Routines */
113 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
118 DWORD dwOleTypeNameLength
;
119 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
120 CHAR
*pstrOleObjFileName
;
121 DWORD dwOleObjFileNameLength
;
122 DWORD dwMetaFileWidth
;
123 DWORD dwMetaFileHeight
;
124 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
127 }OLECONVERT_OLESTREAM_DATA
;
129 /* CompObj Stream structure */
130 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
135 DWORD dwCLSIDNameLength
;
136 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
137 DWORD dwOleTypeNameLength
;
138 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
139 DWORD dwProgIDNameLength
;
140 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
142 }OLECONVERT_ISTORAGE_COMPOBJ
;
145 /* Ole Presentation Stream structure */
146 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
154 }OLECONVERT_ISTORAGE_OLEPRES
;
158 /***********************************************************************
159 * Forward declaration of internal functions used by the method DestroyElement
161 static HRESULT
deleteStorageProperty(
162 StorageImpl
*parentStorage
,
163 ULONG foundPropertyIndexToDelete
,
164 StgProperty propertyToDelete
);
166 static HRESULT
deleteStreamProperty(
167 StorageImpl
*parentStorage
,
168 ULONG foundPropertyIndexToDelete
,
169 StgProperty propertyToDelete
);
171 static HRESULT
findPlaceholder(
172 StorageImpl
*storage
,
173 ULONG propertyIndexToStore
,
174 ULONG storagePropertyIndex
,
177 static HRESULT
adjustPropertyChain(
179 StgProperty propertyToDelete
,
180 StgProperty parentProperty
,
181 ULONG parentPropertyId
,
184 /***********************************************************************
185 * Declaration of the functions used to manipulate StgProperty
188 static ULONG
getFreeProperty(
189 StorageImpl
*storage
);
191 static void updatePropertyChain(
192 StorageImpl
*storage
,
193 ULONG newPropertyIndex
,
194 StgProperty newProperty
);
196 static LONG
propertyNameCmp(
197 const OLECHAR
*newProperty
,
198 const OLECHAR
*currentProperty
);
201 /***********************************************************************
202 * Declaration of miscellaneous functions...
204 static HRESULT
validateSTGM(DWORD stgmValue
);
206 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
207 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
208 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
210 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
213 /****************************************************************************
214 * IEnumSTATSTGImpl definitions.
216 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
217 * This class allows iterating through the content of a storage and to find
218 * specific items inside it.
220 struct IEnumSTATSTGImpl
222 const IEnumSTATSTGVtbl
*lpVtbl
; /* Needs to be the first item in the struct
223 * since we want to cast this in an IEnumSTATSTG pointer */
225 LONG ref
; /* Reference count */
226 StorageImpl
* parentStorage
; /* Reference to the parent storage */
227 ULONG firstPropertyNode
; /* Index of the root of the storage to enumerate */
230 * The current implementation of the IEnumSTATSTGImpl class uses a stack
231 * to walk the property sets to get the content of a storage. This stack
232 * is implemented by the following 3 data members
238 #define ENUMSTATSGT_SIZE_INCREMENT 10
242 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageImpl
* This
, ULONG firstPropertyNode
);
243 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
);
244 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl
* This
, ULONG nodeToPush
);
245 static ULONG
IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl
* This
, BOOL remove
);
246 static ULONG
IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl
* This
, const OLECHAR
* lpszPropName
,
247 StgProperty
* buffer
);
248 static INT
IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl
*This
, ULONG childProperty
,
249 StgProperty
*currentProperty
, ULONG
*propertyId
);
251 /************************************************************************
255 static ULONG
BLOCK_GetBigBlockOffset(ULONG index
)
257 if (index
== 0xffffffff)
262 return index
* BIG_BLOCK_SIZE
;
265 /************************************************************************
266 ** Storage32BaseImpl implementation
268 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
269 ULARGE_INTEGER offset
,
274 return BIGBLOCKFILE_ReadAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesRead
);
277 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
278 ULARGE_INTEGER offset
,
283 return BIGBLOCKFILE_WriteAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesWritten
);
286 /************************************************************************
287 * Storage32BaseImpl_QueryInterface (IUnknown)
289 * This method implements the common QueryInterface for all IStorage32
290 * implementations contained in this file.
292 * See Windows documentation for more details on IUnknown methods.
294 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
299 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
301 * Perform a sanity check on the parameters.
303 if ( (This
==0) || (ppvObject
==0) )
307 * Initialize the return parameter.
312 * Compare the riid with the interface IDs implemented by this object.
314 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
315 IsEqualGUID(&IID_IStorage
, riid
))
319 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
321 *ppvObject
= &This
->pssVtbl
;
325 * Check that we obtained an interface.
328 return E_NOINTERFACE
;
331 * Query Interface always increases the reference count by one when it is
334 IStorage_AddRef(iface
);
339 /************************************************************************
340 * Storage32BaseImpl_AddRef (IUnknown)
342 * This method implements the common AddRef for all IStorage32
343 * implementations contained in this file.
345 * See Windows documentation for more details on IUnknown methods.
347 static ULONG WINAPI
StorageBaseImpl_AddRef(
350 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
351 ULONG ref
= InterlockedIncrement(&This
->ref
);
353 TRACE("(%p) AddRef to %d\n", This
, ref
);
358 /************************************************************************
359 * Storage32BaseImpl_Release (IUnknown)
361 * This method implements the common Release for all IStorage32
362 * implementations contained in this file.
364 * See Windows documentation for more details on IUnknown methods.
366 static ULONG WINAPI
StorageBaseImpl_Release(
369 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
371 * Decrease the reference count on this object.
373 ULONG ref
= InterlockedDecrement(&This
->ref
);
375 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
378 * If the reference count goes down to 0, perform suicide.
383 * Since we are using a system of base-classes, we want to call the
384 * destructor of the appropriate derived class. To do this, we are
385 * using virtual functions to implement the destructor.
387 This
->v_destructor(This
);
393 /************************************************************************
394 * Storage32BaseImpl_OpenStream (IStorage)
396 * This method will open the specified stream object from the current storage.
398 * See Windows documentation for more details on IStorage methods.
400 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
402 const OLECHAR
* pwcsName
, /* [string][in] */
403 void* reserved1
, /* [unique][in] */
404 DWORD grfMode
, /* [in] */
405 DWORD reserved2
, /* [in] */
406 IStream
** ppstm
) /* [out] */
408 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
409 IEnumSTATSTGImpl
* propertyEnumeration
;
410 StgStreamImpl
* newStream
;
411 StgProperty currentProperty
;
412 ULONG foundPropertyIndex
;
413 HRESULT res
= STG_E_UNKNOWN
;
415 TRACE("(%p, %s, %p, %x, %d, %p)\n",
416 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
419 * Perform a sanity check on the parameters.
421 if ( (pwcsName
==NULL
) || (ppstm
==0) )
428 * Initialize the out parameter
433 * Validate the STGM flags
435 if ( FAILED( validateSTGM(grfMode
) ) ||
436 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
438 res
= STG_E_INVALIDFLAG
;
445 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
447 res
= STG_E_INVALIDFUNCTION
;
452 * Check that we're compatible with the parent's storage mode, but
453 * only if we are not in transacted mode
455 if(!(This
->ancestorStorage
->base
.openFlags
& STGM_TRANSACTED
)) {
456 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
458 res
= STG_E_ACCESSDENIED
;
464 * Create a property enumeration to search the properties
466 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
467 This
->ancestorStorage
,
468 This
->rootPropertySetIndex
);
471 * Search the enumeration for the property with the given name
473 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
479 * Delete the property enumeration since we don't need it anymore
481 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
484 * If it was found, construct the stream object and return a pointer to it.
486 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
487 (currentProperty
.propertyType
==PROPTYPE_STREAM
) )
489 newStream
= StgStreamImpl_Construct(This
, grfMode
, foundPropertyIndex
);
493 newStream
->grfMode
= grfMode
;
494 *ppstm
= (IStream
*)newStream
;
497 * Since we are returning a pointer to the interface, we have to
498 * nail down the reference.
500 IStream_AddRef(*ppstm
);
510 res
= STG_E_FILENOTFOUND
;
514 TRACE("<-- IStream %p\n", *ppstm
);
515 TRACE("<-- %08x\n", res
);
519 /************************************************************************
520 * Storage32BaseImpl_OpenStorage (IStorage)
522 * This method will open a new storage object from the current storage.
524 * See Windows documentation for more details on IStorage methods.
526 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
528 const OLECHAR
* pwcsName
, /* [string][unique][in] */
529 IStorage
* pstgPriority
, /* [unique][in] */
530 DWORD grfMode
, /* [in] */
531 SNB snbExclude
, /* [unique][in] */
532 DWORD reserved
, /* [in] */
533 IStorage
** ppstg
) /* [out] */
535 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
536 StorageInternalImpl
* newStorage
;
537 IEnumSTATSTGImpl
* propertyEnumeration
;
538 StgProperty currentProperty
;
539 ULONG foundPropertyIndex
;
540 HRESULT res
= STG_E_UNKNOWN
;
542 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
543 iface
, debugstr_w(pwcsName
), pstgPriority
,
544 grfMode
, snbExclude
, reserved
, ppstg
);
547 * Perform a sanity check on the parameters.
549 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
556 if (snbExclude
!= NULL
)
558 res
= STG_E_INVALIDPARAMETER
;
563 * Validate the STGM flags
565 if ( FAILED( validateSTGM(grfMode
) ))
567 res
= STG_E_INVALIDFLAG
;
574 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
575 (grfMode
& STGM_DELETEONRELEASE
) ||
576 (grfMode
& STGM_PRIORITY
) )
578 res
= STG_E_INVALIDFUNCTION
;
583 * Check that we're compatible with the parent's storage mode,
584 * but only if we are not transacted
586 if(!(This
->ancestorStorage
->base
.openFlags
& STGM_TRANSACTED
)) {
587 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
589 res
= STG_E_ACCESSDENIED
;
595 * Initialize the out parameter
600 * Create a property enumeration to search the properties
602 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
603 This
->ancestorStorage
,
604 This
->rootPropertySetIndex
);
607 * Search the enumeration for the property with the given name
609 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
615 * Delete the property enumeration since we don't need it anymore
617 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
620 * If it was found, construct the stream object and return a pointer to it.
622 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
623 (currentProperty
.propertyType
==PROPTYPE_STORAGE
) )
626 * Construct a new Storage object
628 newStorage
= StorageInternalImpl_Construct(
629 This
->ancestorStorage
,
635 *ppstg
= (IStorage
*)newStorage
;
638 * Since we are returning a pointer to the interface,
639 * we have to nail down the reference.
641 StorageBaseImpl_AddRef(*ppstg
);
647 res
= STG_E_INSUFFICIENTMEMORY
;
651 res
= STG_E_FILENOTFOUND
;
654 TRACE("<-- %08x\n", res
);
658 /************************************************************************
659 * Storage32BaseImpl_EnumElements (IStorage)
661 * This method will create an enumerator object that can be used to
662 * retrieve information about all the properties in the storage object.
664 * See Windows documentation for more details on IStorage methods.
666 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
668 DWORD reserved1
, /* [in] */
669 void* reserved2
, /* [size_is][unique][in] */
670 DWORD reserved3
, /* [in] */
671 IEnumSTATSTG
** ppenum
) /* [out] */
673 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
674 IEnumSTATSTGImpl
* newEnum
;
676 TRACE("(%p, %d, %p, %d, %p)\n",
677 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
680 * Perform a sanity check on the parameters.
682 if ( (This
==0) || (ppenum
==0))
686 * Construct the enumerator.
688 newEnum
= IEnumSTATSTGImpl_Construct(
689 This
->ancestorStorage
,
690 This
->rootPropertySetIndex
);
694 *ppenum
= (IEnumSTATSTG
*)newEnum
;
697 * Don't forget to nail down a reference to the new object before
700 IEnumSTATSTG_AddRef(*ppenum
);
705 return E_OUTOFMEMORY
;
708 /************************************************************************
709 * Storage32BaseImpl_Stat (IStorage)
711 * This method will retrieve information about this storage object.
713 * See Windows documentation for more details on IStorage methods.
715 static HRESULT WINAPI
StorageBaseImpl_Stat(
717 STATSTG
* pstatstg
, /* [out] */
718 DWORD grfStatFlag
) /* [in] */
720 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
721 StgProperty curProperty
;
723 HRESULT res
= STG_E_UNKNOWN
;
725 TRACE("(%p, %p, %x)\n",
726 iface
, pstatstg
, grfStatFlag
);
729 * Perform a sanity check on the parameters.
731 if ( (This
==0) || (pstatstg
==0))
738 * Read the information from the property.
740 readSuccessful
= StorageImpl_ReadProperty(
741 This
->ancestorStorage
,
742 This
->rootPropertySetIndex
,
747 StorageUtl_CopyPropertyToSTATSTG(
752 pstatstg
->grfMode
= This
->openFlags
;
753 pstatstg
->grfStateBits
= This
->stateBits
;
764 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
);
766 TRACE("<-- %08x\n", res
);
770 /************************************************************************
771 * Storage32BaseImpl_RenameElement (IStorage)
773 * This method will rename the specified element.
775 * See Windows documentation for more details on IStorage methods.
777 * Implementation notes: The method used to rename consists of creating a clone
778 * of the deleted StgProperty object setting it with the new name and to
779 * perform a DestroyElement of the old StgProperty.
781 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
783 const OLECHAR
* pwcsOldName
, /* [in] */
784 const OLECHAR
* pwcsNewName
) /* [in] */
786 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
787 IEnumSTATSTGImpl
* propertyEnumeration
;
788 StgProperty currentProperty
;
789 ULONG foundPropertyIndex
;
791 TRACE("(%p, %s, %s)\n",
792 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
795 * Create a property enumeration to search the properties
797 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
798 This
->rootPropertySetIndex
);
801 * Search the enumeration for the new property name
803 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
807 if (foundPropertyIndex
!= PROPERTY_NULL
)
810 * There is already a property with the new name
812 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
813 return STG_E_FILEALREADYEXISTS
;
816 IEnumSTATSTG_Reset((IEnumSTATSTG
*)propertyEnumeration
);
819 * Search the enumeration for the old property name
821 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
826 * Delete the property enumeration since we don't need it anymore
828 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
830 if (foundPropertyIndex
!= PROPERTY_NULL
)
832 StgProperty renamedProperty
;
833 ULONG renamedPropertyIndex
;
836 * Setup a new property for the renamed property
838 renamedProperty
.sizeOfNameString
=
839 ( lstrlenW(pwcsNewName
)+1 ) * sizeof(WCHAR
);
841 if (renamedProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
842 return STG_E_INVALIDNAME
;
844 strcpyW(renamedProperty
.name
, pwcsNewName
);
846 renamedProperty
.propertyType
= currentProperty
.propertyType
;
847 renamedProperty
.startingBlock
= currentProperty
.startingBlock
;
848 renamedProperty
.size
.u
.LowPart
= currentProperty
.size
.u
.LowPart
;
849 renamedProperty
.size
.u
.HighPart
= currentProperty
.size
.u
.HighPart
;
851 renamedProperty
.previousProperty
= PROPERTY_NULL
;
852 renamedProperty
.nextProperty
= PROPERTY_NULL
;
855 * Bring the dirProperty link in case it is a storage and in which
856 * case the renamed storage elements don't require to be reorganized.
858 renamedProperty
.dirProperty
= currentProperty
.dirProperty
;
860 /* call CoFileTime to get the current time
861 renamedProperty.timeStampS1
862 renamedProperty.timeStampD1
863 renamedProperty.timeStampS2
864 renamedProperty.timeStampD2
865 renamedProperty.propertyUniqueID
869 * Obtain a free property in the property chain
871 renamedPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
874 * Save the new property into the new property spot
876 StorageImpl_WriteProperty(
877 This
->ancestorStorage
,
878 renamedPropertyIndex
,
882 * Find a spot in the property chain for our newly created property.
886 renamedPropertyIndex
,
890 * At this point the renamed property has been inserted in the tree,
891 * now, before Destroying the old property we must zero its dirProperty
892 * otherwise the DestroyProperty below will zap it all and we do not want
894 * Also, we fake that the old property is a storage so the DestroyProperty
895 * will not do a SetSize(0) on the stream data.
897 * This means that we need to tweak the StgProperty if it is a stream or a
900 StorageImpl_ReadProperty(This
->ancestorStorage
,
904 currentProperty
.dirProperty
= PROPERTY_NULL
;
905 currentProperty
.propertyType
= PROPTYPE_STORAGE
;
906 StorageImpl_WriteProperty(
907 This
->ancestorStorage
,
912 * Invoke Destroy to get rid of the ole property and automatically redo
913 * the linking of its previous and next members...
915 IStorage_DestroyElement(iface
, pwcsOldName
);
921 * There is no property with the old name
923 return STG_E_FILENOTFOUND
;
929 /************************************************************************
930 * Storage32BaseImpl_CreateStream (IStorage)
932 * This method will create a stream object within this storage
934 * See Windows documentation for more details on IStorage methods.
936 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
938 const OLECHAR
* pwcsName
, /* [string][in] */
939 DWORD grfMode
, /* [in] */
940 DWORD reserved1
, /* [in] */
941 DWORD reserved2
, /* [in] */
942 IStream
** ppstm
) /* [out] */
944 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
945 IEnumSTATSTGImpl
* propertyEnumeration
;
946 StgStreamImpl
* newStream
;
947 StgProperty currentProperty
, newStreamProperty
;
948 ULONG foundPropertyIndex
, newPropertyIndex
;
950 TRACE("(%p, %s, %x, %d, %d, %p)\n",
951 iface
, debugstr_w(pwcsName
), grfMode
,
952 reserved1
, reserved2
, ppstm
);
955 * Validate parameters
958 return STG_E_INVALIDPOINTER
;
961 return STG_E_INVALIDNAME
;
963 if (reserved1
|| reserved2
)
964 return STG_E_INVALIDPARAMETER
;
967 * Validate the STGM flags
969 if ( FAILED( validateSTGM(grfMode
) ))
970 return STG_E_INVALIDFLAG
;
972 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
973 return STG_E_INVALIDFLAG
;
978 if ((grfMode
& STGM_DELETEONRELEASE
) ||
979 (grfMode
& STGM_TRANSACTED
))
980 return STG_E_INVALIDFUNCTION
;
982 /* Can't create a stream on read-only storage */
983 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
984 return STG_E_ACCESSDENIED
;
987 * Check that we're compatible with the parent's storage mode
988 * if not in transacted mode
990 if(!(This
->ancestorStorage
->base
.openFlags
& STGM_TRANSACTED
)) {
991 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
992 return STG_E_ACCESSDENIED
;
996 * Initialize the out parameter
1001 * Create a property enumeration to search the properties
1003 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
1004 This
->rootPropertySetIndex
);
1006 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
1010 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1012 if (foundPropertyIndex
!= PROPERTY_NULL
)
1015 * An element with this name already exists
1017 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
1019 StgStreamImpl
*strm
;
1021 LIST_FOR_EACH_ENTRY(strm
, &This
->strmHead
, StgStreamImpl
, StrmListEntry
)
1023 if (strm
->ownerProperty
== foundPropertyIndex
)
1025 TRACE("Stream deleted %p\n", strm
);
1026 strm
->parentStorage
= NULL
;
1027 list_remove(&strm
->StrmListEntry
);
1030 IStorage_DestroyElement(iface
, pwcsName
);
1033 return STG_E_FILEALREADYEXISTS
;
1035 else if (STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1037 WARN("read-only storage\n");
1038 return STG_E_ACCESSDENIED
;
1042 * memset the empty property
1044 memset(&newStreamProperty
, 0, sizeof(StgProperty
));
1046 newStreamProperty
.sizeOfNameString
=
1047 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
1049 if (newStreamProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
1050 return STG_E_INVALIDNAME
;
1052 strcpyW(newStreamProperty
.name
, pwcsName
);
1054 newStreamProperty
.propertyType
= PROPTYPE_STREAM
;
1055 newStreamProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
1056 newStreamProperty
.size
.u
.LowPart
= 0;
1057 newStreamProperty
.size
.u
.HighPart
= 0;
1059 newStreamProperty
.previousProperty
= PROPERTY_NULL
;
1060 newStreamProperty
.nextProperty
= PROPERTY_NULL
;
1061 newStreamProperty
.dirProperty
= PROPERTY_NULL
;
1063 /* call CoFileTime to get the current time
1064 newStreamProperty.timeStampS1
1065 newStreamProperty.timeStampD1
1066 newStreamProperty.timeStampS2
1067 newStreamProperty.timeStampD2
1070 /* newStreamProperty.propertyUniqueID */
1073 * Get a free property or create a new one
1075 newPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
1078 * Save the new property into the new property spot
1080 StorageImpl_WriteProperty(
1081 This
->ancestorStorage
,
1083 &newStreamProperty
);
1086 * Find a spot in the property chain for our newly created property.
1088 updatePropertyChain(
1094 * Open the stream to return it.
1096 newStream
= StgStreamImpl_Construct(This
, grfMode
, newPropertyIndex
);
1100 *ppstm
= (IStream
*)newStream
;
1103 * Since we are returning a pointer to the interface, we have to nail down
1106 IStream_AddRef(*ppstm
);
1110 return STG_E_INSUFFICIENTMEMORY
;
1116 /************************************************************************
1117 * Storage32BaseImpl_SetClass (IStorage)
1119 * This method will write the specified CLSID in the property of this
1122 * See Windows documentation for more details on IStorage methods.
1124 static HRESULT WINAPI
StorageBaseImpl_SetClass(
1126 REFCLSID clsid
) /* [in] */
1128 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
1129 HRESULT hRes
= E_FAIL
;
1130 StgProperty curProperty
;
1133 TRACE("(%p, %p)\n", iface
, clsid
);
1135 success
= StorageImpl_ReadProperty(This
->ancestorStorage
,
1136 This
->rootPropertySetIndex
,
1140 curProperty
.propertyUniqueID
= *clsid
;
1142 success
= StorageImpl_WriteProperty(This
->ancestorStorage
,
1143 This
->rootPropertySetIndex
,
1152 /************************************************************************
1153 ** Storage32Impl implementation
1156 /************************************************************************
1157 * Storage32Impl_CreateStorage (IStorage)
1159 * This method will create the storage object within the provided storage.
1161 * See Windows documentation for more details on IStorage methods.
1163 static HRESULT WINAPI
StorageImpl_CreateStorage(
1165 const OLECHAR
*pwcsName
, /* [string][in] */
1166 DWORD grfMode
, /* [in] */
1167 DWORD reserved1
, /* [in] */
1168 DWORD reserved2
, /* [in] */
1169 IStorage
**ppstg
) /* [out] */
1171 StorageImpl
* const This
=(StorageImpl
*)iface
;
1173 IEnumSTATSTGImpl
*propertyEnumeration
;
1174 StgProperty currentProperty
;
1175 StgProperty newProperty
;
1176 ULONG foundPropertyIndex
;
1177 ULONG newPropertyIndex
;
1180 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1181 iface
, debugstr_w(pwcsName
), grfMode
,
1182 reserved1
, reserved2
, ppstg
);
1185 * Validate parameters
1188 return STG_E_INVALIDPOINTER
;
1191 return STG_E_INVALIDNAME
;
1194 * Initialize the out parameter
1199 * Validate the STGM flags
1201 if ( FAILED( validateSTGM(grfMode
) ) ||
1202 (grfMode
& STGM_DELETEONRELEASE
) )
1204 WARN("bad grfMode: 0x%x\n", grfMode
);
1205 return STG_E_INVALIDFLAG
;
1209 * Check that we're compatible with the parent's storage mode
1211 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->base
.openFlags
) )
1213 WARN("access denied\n");
1214 return STG_E_ACCESSDENIED
;
1218 * Create a property enumeration and search the properties
1220 propertyEnumeration
= IEnumSTATSTGImpl_Construct( This
->base
.ancestorStorage
,
1221 This
->base
.rootPropertySetIndex
);
1223 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
1226 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1228 if (foundPropertyIndex
!= PROPERTY_NULL
)
1231 * An element with this name already exists
1233 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
1234 STGM_ACCESS_MODE(This
->base
.openFlags
) != STGM_READ
)
1236 hr
= IStorage_DestroyElement(iface
, pwcsName
);
1242 WARN("file already exists\n");
1243 return STG_E_FILEALREADYEXISTS
;
1246 else if (STGM_ACCESS_MODE(This
->base
.openFlags
) == STGM_READ
)
1248 WARN("read-only storage\n");
1249 return STG_E_ACCESSDENIED
;
1253 * memset the empty property
1255 memset(&newProperty
, 0, sizeof(StgProperty
));
1257 newProperty
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1259 if (newProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
1261 FIXME("name too long\n");
1262 return STG_E_INVALIDNAME
;
1265 strcpyW(newProperty
.name
, pwcsName
);
1267 newProperty
.propertyType
= PROPTYPE_STORAGE
;
1268 newProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
1269 newProperty
.size
.u
.LowPart
= 0;
1270 newProperty
.size
.u
.HighPart
= 0;
1272 newProperty
.previousProperty
= PROPERTY_NULL
;
1273 newProperty
.nextProperty
= PROPERTY_NULL
;
1274 newProperty
.dirProperty
= PROPERTY_NULL
;
1276 /* call CoFileTime to get the current time
1277 newProperty.timeStampS1
1278 newProperty.timeStampD1
1279 newProperty.timeStampS2
1280 newProperty.timeStampD2
1283 /* newStorageProperty.propertyUniqueID */
1286 * Obtain a free property in the property chain
1288 newPropertyIndex
= getFreeProperty(This
->base
.ancestorStorage
);
1291 * Save the new property into the new property spot
1293 StorageImpl_WriteProperty(
1294 This
->base
.ancestorStorage
,
1299 * Find a spot in the property chain for our newly created property.
1301 updatePropertyChain(
1307 * Open it to get a pointer to return.
1309 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1311 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1321 /***************************************************************************
1325 * Get a free property or create a new one.
1327 static ULONG
getFreeProperty(
1328 StorageImpl
*storage
)
1330 ULONG currentPropertyIndex
= 0;
1331 ULONG newPropertyIndex
= PROPERTY_NULL
;
1332 BOOL readSuccessful
= TRUE
;
1333 StgProperty currentProperty
;
1338 * Start by reading the root property
1340 readSuccessful
= StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1341 currentPropertyIndex
,
1345 if (currentProperty
.sizeOfNameString
== 0)
1348 * The property existis and is available, we found it.
1350 newPropertyIndex
= currentPropertyIndex
;
1356 * We exhausted the property list, we will create more space below
1358 newPropertyIndex
= currentPropertyIndex
;
1360 currentPropertyIndex
++;
1362 } while (newPropertyIndex
== PROPERTY_NULL
);
1365 * grow the property chain
1367 if (! readSuccessful
)
1369 StgProperty emptyProperty
;
1370 ULARGE_INTEGER newSize
;
1371 ULONG propertyIndex
;
1372 ULONG lastProperty
= 0;
1373 ULONG blockCount
= 0;
1376 * obtain the new count of property blocks
1378 blockCount
= BlockChainStream_GetCount(
1379 storage
->base
.ancestorStorage
->rootBlockChain
)+1;
1382 * initialize the size used by the property stream
1384 newSize
.u
.HighPart
= 0;
1385 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1388 * add a property block to the property chain
1390 BlockChainStream_SetSize(storage
->base
.ancestorStorage
->rootBlockChain
, newSize
);
1393 * memset the empty property in order to initialize the unused newly
1396 memset(&emptyProperty
, 0, sizeof(StgProperty
));
1401 lastProperty
= storage
->bigBlockSize
/ PROPSET_BLOCK_SIZE
* blockCount
;
1404 propertyIndex
= newPropertyIndex
;
1405 propertyIndex
< lastProperty
;
1408 StorageImpl_WriteProperty(
1409 storage
->base
.ancestorStorage
,
1415 return newPropertyIndex
;
1418 /****************************************************************************
1422 * Case insensitive comparison of StgProperty.name by first considering
1425 * Returns <0 when newProperty < currentProperty
1426 * >0 when newProperty > currentProperty
1427 * 0 when newProperty == currentProperty
1429 static LONG
propertyNameCmp(
1430 const OLECHAR
*newProperty
,
1431 const OLECHAR
*currentProperty
)
1433 LONG diff
= lstrlenW(newProperty
) - lstrlenW(currentProperty
);
1438 * We compare the string themselves only when they are of the same length
1440 diff
= lstrcmpiW( newProperty
, currentProperty
);
1446 /****************************************************************************
1450 * Properly link this new element in the property chain.
1452 static void updatePropertyChain(
1453 StorageImpl
*storage
,
1454 ULONG newPropertyIndex
,
1455 StgProperty newProperty
)
1457 StgProperty currentProperty
;
1460 * Read the root property
1462 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1463 storage
->base
.rootPropertySetIndex
,
1466 if (currentProperty
.dirProperty
!= PROPERTY_NULL
)
1469 * The root storage contains some element, therefore, start the research
1470 * for the appropriate location.
1473 ULONG current
, next
, previous
, currentPropertyId
;
1476 * Keep the StgProperty sequence number of the storage first property
1478 currentPropertyId
= currentProperty
.dirProperty
;
1483 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1484 currentProperty
.dirProperty
,
1487 previous
= currentProperty
.previousProperty
;
1488 next
= currentProperty
.nextProperty
;
1489 current
= currentPropertyId
;
1493 LONG diff
= propertyNameCmp( newProperty
.name
, currentProperty
.name
);
1497 if (previous
!= PROPERTY_NULL
)
1499 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1506 currentProperty
.previousProperty
= newPropertyIndex
;
1507 StorageImpl_WriteProperty(storage
->base
.ancestorStorage
,
1515 if (next
!= PROPERTY_NULL
)
1517 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1524 currentProperty
.nextProperty
= newPropertyIndex
;
1525 StorageImpl_WriteProperty(storage
->base
.ancestorStorage
,
1534 * Trying to insert an item with the same name in the
1535 * subtree structure.
1540 previous
= currentProperty
.previousProperty
;
1541 next
= currentProperty
.nextProperty
;
1547 * The root storage is empty, link the new property to its dir property
1549 currentProperty
.dirProperty
= newPropertyIndex
;
1550 StorageImpl_WriteProperty(storage
->base
.ancestorStorage
,
1551 storage
->base
.rootPropertySetIndex
,
1557 /*************************************************************************
1560 static HRESULT WINAPI
StorageImpl_CopyTo(
1562 DWORD ciidExclude
, /* [in] */
1563 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1564 SNB snbExclude
, /* [unique][in] */
1565 IStorage
* pstgDest
) /* [unique][in] */
1567 IEnumSTATSTG
*elements
= 0;
1568 STATSTG curElement
, strStat
;
1570 IStorage
*pstgTmp
, *pstgChild
;
1571 IStream
*pstrTmp
, *pstrChild
;
1573 if ((ciidExclude
!= 0) || (rgiidExclude
!= NULL
) || (snbExclude
!= NULL
))
1574 FIXME("Exclude option not implemented\n");
1576 TRACE("(%p, %d, %p, %p, %p)\n",
1577 iface
, ciidExclude
, rgiidExclude
,
1578 snbExclude
, pstgDest
);
1581 * Perform a sanity check
1583 if ( pstgDest
== 0 )
1584 return STG_E_INVALIDPOINTER
;
1587 * Enumerate the elements
1589 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1597 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1598 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1603 * Obtain the next element
1605 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1607 if ( hr
== S_FALSE
)
1609 hr
= S_OK
; /* done, every element has been copied */
1613 if (curElement
.type
== STGTY_STORAGE
)
1616 * open child source storage
1618 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1619 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1620 NULL
, 0, &pstgChild
);
1626 * Check if destination storage is not a child of the source
1627 * storage, which will cause an infinite loop
1629 if (pstgChild
== pstgDest
)
1631 IEnumSTATSTG_Release(elements
);
1633 return STG_E_ACCESSDENIED
;
1637 * create a new storage in destination storage
1639 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1640 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1644 * if it already exist, don't create a new one use this one
1646 if (hr
== STG_E_FILEALREADYEXISTS
)
1648 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1649 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1650 NULL
, 0, &pstgTmp
);
1658 * do the copy recursively
1660 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1661 snbExclude
, pstgTmp
);
1663 IStorage_Release( pstgTmp
);
1664 IStorage_Release( pstgChild
);
1666 else if (curElement
.type
== STGTY_STREAM
)
1669 * create a new stream in destination storage. If the stream already
1670 * exist, it will be deleted and a new one will be created.
1672 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1673 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1680 * open child stream storage
1682 hr
= IStorage_OpenStream( iface
, curElement
.pwcsName
, NULL
,
1683 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1690 * Get the size of the source stream
1692 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1695 * Set the size of the destination stream.
1697 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1702 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1705 IStream_Release( pstrTmp
);
1706 IStream_Release( pstrChild
);
1710 WARN("unknown element type: %d\n", curElement
.type
);
1713 } while (hr
== S_OK
);
1718 IEnumSTATSTG_Release(elements
);
1723 /*************************************************************************
1724 * MoveElementTo (IStorage)
1726 static HRESULT WINAPI
StorageImpl_MoveElementTo(
1728 const OLECHAR
*pwcsName
, /* [string][in] */
1729 IStorage
*pstgDest
, /* [unique][in] */
1730 const OLECHAR
*pwcsNewName
,/* [string][in] */
1731 DWORD grfFlags
) /* [in] */
1733 FIXME("(%p %s %p %s %u): stub\n", iface
,
1734 debugstr_w(pwcsName
), pstgDest
,
1735 debugstr_w(pwcsNewName
), grfFlags
);
1739 /*************************************************************************
1742 * Ensures that any changes made to a storage object open in transacted mode
1743 * are reflected in the parent storage
1746 * Wine doesn't implement transacted mode, which seems to be a basic
1747 * optimization, so we can ignore this stub for now.
1749 static HRESULT WINAPI
StorageImpl_Commit(
1751 DWORD grfCommitFlags
)/* [in] */
1753 FIXME("(%p %d): stub\n", iface
, grfCommitFlags
);
1757 /*************************************************************************
1760 * Discard all changes that have been made since the last commit operation
1762 static HRESULT WINAPI
StorageImpl_Revert(
1765 FIXME("(%p): stub\n", iface
);
1769 /*************************************************************************
1770 * DestroyElement (IStorage)
1772 * Strategy: This implementation is built this way for simplicity not for speed.
1773 * I always delete the topmost element of the enumeration and adjust
1774 * the deleted element pointer all the time. This takes longer to
1775 * do but allow to reinvoke DestroyElement whenever we encounter a
1776 * storage object. The optimisation resides in the usage of another
1777 * enumeration strategy that would give all the leaves of a storage
1778 * first. (postfix order)
1780 static HRESULT WINAPI
StorageImpl_DestroyElement(
1782 const OLECHAR
*pwcsName
)/* [string][in] */
1784 StorageImpl
* const This
=(StorageImpl
*)iface
;
1786 IEnumSTATSTGImpl
* propertyEnumeration
;
1789 StgProperty propertyToDelete
;
1790 StgProperty parentProperty
;
1791 ULONG foundPropertyIndexToDelete
;
1792 ULONG typeOfRelation
;
1793 ULONG parentPropertyId
= 0;
1796 iface
, debugstr_w(pwcsName
));
1799 * Perform a sanity check on the parameters.
1802 return STG_E_INVALIDPOINTER
;
1804 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
1805 return STG_E_ACCESSDENIED
;
1808 * Create a property enumeration to search the property with the given name
1810 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
1811 This
->base
.ancestorStorage
,
1812 This
->base
.rootPropertySetIndex
);
1814 foundPropertyIndexToDelete
= IEnumSTATSTGImpl_FindProperty(
1815 propertyEnumeration
,
1819 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1821 if ( foundPropertyIndexToDelete
== PROPERTY_NULL
)
1823 return STG_E_FILENOTFOUND
;
1827 * Find the parent property of the property to delete (the one that
1828 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1829 * the parent is This. Otherwise, the parent is one of its sibling...
1833 * First, read This's StgProperty..
1835 res
= StorageImpl_ReadProperty(
1836 This
->base
.ancestorStorage
,
1837 This
->base
.rootPropertySetIndex
,
1843 * Second, check to see if by any chance the actual storage (This) is not
1844 * the parent of the property to delete... We never know...
1846 if ( parentProperty
.dirProperty
== foundPropertyIndexToDelete
)
1849 * Set data as it would have been done in the else part...
1851 typeOfRelation
= PROPERTY_RELATION_DIR
;
1852 parentPropertyId
= This
->base
.rootPropertySetIndex
;
1857 * Create a property enumeration to search the parent properties, and
1858 * delete it once done.
1860 IEnumSTATSTGImpl
* propertyEnumeration2
;
1862 propertyEnumeration2
= IEnumSTATSTGImpl_Construct(
1863 This
->base
.ancestorStorage
,
1864 This
->base
.rootPropertySetIndex
);
1866 typeOfRelation
= IEnumSTATSTGImpl_FindParentProperty(
1867 propertyEnumeration2
,
1868 foundPropertyIndexToDelete
,
1872 IEnumSTATSTGImpl_Destroy(propertyEnumeration2
);
1875 if ( propertyToDelete
.propertyType
== PROPTYPE_STORAGE
)
1877 hr
= deleteStorageProperty(
1879 foundPropertyIndexToDelete
,
1882 else if ( propertyToDelete
.propertyType
== PROPTYPE_STREAM
)
1884 hr
= deleteStreamProperty(
1886 foundPropertyIndexToDelete
,
1894 * Adjust the property chain
1896 hr
= adjustPropertyChain(
1907 /************************************************************************
1908 * StorageImpl_Stat (IStorage)
1910 * This method will retrieve information about this storage object.
1912 * See Windows documentation for more details on IStorage methods.
1914 static HRESULT WINAPI
StorageImpl_Stat( IStorage
* iface
,
1915 STATSTG
* pstatstg
, /* [out] */
1916 DWORD grfStatFlag
) /* [in] */
1918 StorageImpl
* const This
= (StorageImpl
*)iface
;
1919 HRESULT result
= StorageBaseImpl_Stat( iface
, pstatstg
, grfStatFlag
);
1921 if ( SUCCEEDED(result
) && ((grfStatFlag
& STATFLAG_NONAME
) == 0) && This
->pwcsName
)
1923 CoTaskMemFree(pstatstg
->pwcsName
);
1924 pstatstg
->pwcsName
= CoTaskMemAlloc((lstrlenW(This
->pwcsName
)+1)*sizeof(WCHAR
));
1925 strcpyW(pstatstg
->pwcsName
, This
->pwcsName
);
1931 /******************************************************************************
1932 * Internal stream list handlers
1935 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1937 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1938 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1941 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1943 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1944 list_remove(&(strm
->StrmListEntry
));
1947 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
1949 struct list
*cur
, *cur2
;
1950 StgStreamImpl
*strm
=NULL
;
1952 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
1953 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
1954 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
1955 strm
->parentStorage
= NULL
;
1961 /*********************************************************************
1965 * Perform the deletion of a complete storage node
1968 static HRESULT
deleteStorageProperty(
1969 StorageImpl
*parentStorage
,
1970 ULONG indexOfPropertyToDelete
,
1971 StgProperty propertyToDelete
)
1973 IEnumSTATSTG
*elements
= 0;
1974 IStorage
*childStorage
= 0;
1975 STATSTG currentElement
;
1977 HRESULT destroyHr
= S_OK
;
1980 * Open the storage and enumerate it
1982 hr
= StorageBaseImpl_OpenStorage(
1983 (IStorage
*)parentStorage
,
1984 propertyToDelete
.name
,
1986 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
1997 * Enumerate the elements
1999 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
2004 * Obtain the next element
2006 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2009 destroyHr
= StorageImpl_DestroyElement(childStorage
, currentElement
.pwcsName
);
2011 CoTaskMemFree(currentElement
.pwcsName
);
2015 * We need to Reset the enumeration every time because we delete elements
2016 * and the enumeration could be invalid
2018 IEnumSTATSTG_Reset(elements
);
2020 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2023 * Invalidate the property by zeroing its name member.
2025 propertyToDelete
.sizeOfNameString
= 0;
2027 StorageImpl_WriteProperty(parentStorage
->base
.ancestorStorage
,
2028 indexOfPropertyToDelete
,
2031 IStorage_Release(childStorage
);
2032 IEnumSTATSTG_Release(elements
);
2037 /*********************************************************************
2041 * Perform the deletion of a stream node
2044 static HRESULT
deleteStreamProperty(
2045 StorageImpl
*parentStorage
,
2046 ULONG indexOfPropertyToDelete
,
2047 StgProperty propertyToDelete
)
2051 ULARGE_INTEGER size
;
2053 size
.u
.HighPart
= 0;
2056 hr
= StorageBaseImpl_OpenStream((IStorage
*)parentStorage
,
2057 propertyToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2067 hr
= IStream_SetSize(pis
, size
);
2075 * Release the stream object.
2077 IStream_Release(pis
);
2080 * Invalidate the property by zeroing its name member.
2082 propertyToDelete
.sizeOfNameString
= 0;
2085 * Here we should re-read the property so we get the updated pointer
2086 * but since we are here to zap it, I don't do it...
2088 StorageImpl_WriteProperty(
2089 parentStorage
->base
.ancestorStorage
,
2090 indexOfPropertyToDelete
,
2096 /*********************************************************************
2100 * Finds a placeholder for the StgProperty within the Storage
2103 static HRESULT
findPlaceholder(
2104 StorageImpl
*storage
,
2105 ULONG propertyIndexToStore
,
2106 ULONG storePropertyIndex
,
2109 StgProperty storeProperty
;
2113 * Read the storage property
2115 res
= StorageImpl_ReadProperty(
2116 storage
->base
.ancestorStorage
,
2125 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
2127 if (storeProperty
.previousProperty
!= PROPERTY_NULL
)
2129 return findPlaceholder(
2131 propertyIndexToStore
,
2132 storeProperty
.previousProperty
,
2137 storeProperty
.previousProperty
= propertyIndexToStore
;
2140 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
2142 if (storeProperty
.nextProperty
!= PROPERTY_NULL
)
2144 return findPlaceholder(
2146 propertyIndexToStore
,
2147 storeProperty
.nextProperty
,
2152 storeProperty
.nextProperty
= propertyIndexToStore
;
2155 else if (typeOfRelation
== PROPERTY_RELATION_DIR
)
2157 if (storeProperty
.dirProperty
!= PROPERTY_NULL
)
2159 return findPlaceholder(
2161 propertyIndexToStore
,
2162 storeProperty
.dirProperty
,
2167 storeProperty
.dirProperty
= propertyIndexToStore
;
2171 res
= StorageImpl_WriteProperty(
2172 storage
->base
.ancestorStorage
,
2184 /*************************************************************************
2188 * This method takes the previous and the next property link of a property
2189 * to be deleted and find them a place in the Storage.
2191 static HRESULT
adjustPropertyChain(
2193 StgProperty propertyToDelete
,
2194 StgProperty parentProperty
,
2195 ULONG parentPropertyId
,
2198 ULONG newLinkProperty
= PROPERTY_NULL
;
2199 BOOL needToFindAPlaceholder
= FALSE
;
2200 ULONG storeNode
= PROPERTY_NULL
;
2201 ULONG toStoreNode
= PROPERTY_NULL
;
2202 INT relationType
= 0;
2206 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
2208 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2211 * Set the parent previous to the property to delete previous
2213 newLinkProperty
= propertyToDelete
.previousProperty
;
2215 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2218 * We also need to find a storage for the other link, setup variables
2219 * to do this at the end...
2221 needToFindAPlaceholder
= TRUE
;
2222 storeNode
= propertyToDelete
.previousProperty
;
2223 toStoreNode
= propertyToDelete
.nextProperty
;
2224 relationType
= PROPERTY_RELATION_NEXT
;
2227 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2230 * Set the parent previous to the property to delete next
2232 newLinkProperty
= propertyToDelete
.nextProperty
;
2236 * Link it for real...
2238 parentProperty
.previousProperty
= newLinkProperty
;
2241 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
2243 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2246 * Set the parent next to the property to delete next previous
2248 newLinkProperty
= propertyToDelete
.previousProperty
;
2250 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2253 * We also need to find a storage for the other link, setup variables
2254 * to do this at the end...
2256 needToFindAPlaceholder
= TRUE
;
2257 storeNode
= propertyToDelete
.previousProperty
;
2258 toStoreNode
= propertyToDelete
.nextProperty
;
2259 relationType
= PROPERTY_RELATION_NEXT
;
2262 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2265 * Set the parent next to the property to delete next
2267 newLinkProperty
= propertyToDelete
.nextProperty
;
2271 * Link it for real...
2273 parentProperty
.nextProperty
= newLinkProperty
;
2275 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2277 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2280 * Set the parent dir to the property to delete previous
2282 newLinkProperty
= propertyToDelete
.previousProperty
;
2284 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2287 * We also need to find a storage for the other link, setup variables
2288 * to do this at the end...
2290 needToFindAPlaceholder
= TRUE
;
2291 storeNode
= propertyToDelete
.previousProperty
;
2292 toStoreNode
= propertyToDelete
.nextProperty
;
2293 relationType
= PROPERTY_RELATION_NEXT
;
2296 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2299 * Set the parent dir to the property to delete next
2301 newLinkProperty
= propertyToDelete
.nextProperty
;
2305 * Link it for real...
2307 parentProperty
.dirProperty
= newLinkProperty
;
2311 * Write back the parent property
2313 res
= StorageImpl_WriteProperty(
2314 This
->base
.ancestorStorage
,
2323 * If a placeholder is required for the other link, then, find one and
2324 * get out of here...
2326 if (needToFindAPlaceholder
)
2328 hr
= findPlaceholder(
2339 /******************************************************************************
2340 * SetElementTimes (IStorage)
2342 static HRESULT WINAPI
StorageImpl_SetElementTimes(
2344 const OLECHAR
*pwcsName
,/* [string][in] */
2345 const FILETIME
*pctime
, /* [in] */
2346 const FILETIME
*patime
, /* [in] */
2347 const FILETIME
*pmtime
) /* [in] */
2349 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2353 /******************************************************************************
2354 * SetStateBits (IStorage)
2356 static HRESULT WINAPI
StorageImpl_SetStateBits(
2358 DWORD grfStateBits
,/* [in] */
2359 DWORD grfMask
) /* [in] */
2361 StorageImpl
* const This
= (StorageImpl
*)iface
;
2362 This
->base
.stateBits
= (This
->base
.stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2367 * Virtual function table for the IStorage32Impl class.
2369 static const IStorageVtbl Storage32Impl_Vtbl
=
2371 StorageBaseImpl_QueryInterface
,
2372 StorageBaseImpl_AddRef
,
2373 StorageBaseImpl_Release
,
2374 StorageBaseImpl_CreateStream
,
2375 StorageBaseImpl_OpenStream
,
2376 StorageImpl_CreateStorage
,
2377 StorageBaseImpl_OpenStorage
,
2379 StorageImpl_MoveElementTo
,
2382 StorageBaseImpl_EnumElements
,
2383 StorageImpl_DestroyElement
,
2384 StorageBaseImpl_RenameElement
,
2385 StorageImpl_SetElementTimes
,
2386 StorageBaseImpl_SetClass
,
2387 StorageImpl_SetStateBits
,
2391 static HRESULT
StorageImpl_Construct(
2401 StgProperty currentProperty
;
2402 BOOL readSuccessful
;
2403 ULONG currentPropertyIndex
;
2405 if ( FAILED( validateSTGM(openFlags
) ))
2406 return STG_E_INVALIDFLAG
;
2408 memset(This
, 0, sizeof(StorageImpl
));
2411 * Initialize stream list
2414 list_init(&This
->base
.strmHead
);
2417 * Initialize the virtual function table.
2419 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2420 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2421 This
->base
.v_destructor
= StorageImpl_Destroy
;
2422 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2425 * This is the top-level storage so initialize the ancestor pointer
2428 This
->base
.ancestorStorage
= This
;
2431 * Initialize the physical support of the storage.
2433 This
->hFile
= hFile
;
2436 * Store copy of file path.
2439 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2440 (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
));
2441 if (!This
->pwcsName
)
2442 return STG_E_INSUFFICIENTMEMORY
;
2443 strcpyW(This
->pwcsName
, pwcsName
);
2447 * Initialize the big block cache.
2449 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
2450 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2451 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2457 if (This
->bigBlockFile
== 0)
2462 ULARGE_INTEGER size
;
2463 BYTE bigBlockBuffer
[BIG_BLOCK_SIZE
];
2466 * Initialize all header variables:
2467 * - The big block depot consists of one block and it is at block 0
2468 * - The properties start at block 1
2469 * - There is no small block depot
2471 memset( This
->bigBlockDepotStart
,
2473 sizeof(This
->bigBlockDepotStart
));
2475 This
->bigBlockDepotCount
= 1;
2476 This
->bigBlockDepotStart
[0] = 0;
2477 This
->rootStartBlock
= 1;
2478 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2479 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
2480 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2481 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2482 This
->extBigBlockDepotCount
= 0;
2484 StorageImpl_SaveFileHeader(This
);
2487 * Add one block for the big block depot and one block for the properties
2489 size
.u
.HighPart
= 0;
2490 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2491 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2494 * Initialize the big block depot
2496 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2497 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2498 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2499 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2504 * Load the header for the file.
2506 hr
= StorageImpl_LoadFileHeader(This
);
2510 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2517 * There is no block depot cached yet.
2519 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2522 * Start searching for free blocks with block 0.
2524 This
->prevFreeBlock
= 0;
2527 * Create the block chain abstractions.
2529 if(!(This
->rootBlockChain
=
2530 BlockChainStream_Construct(This
, &This
->rootStartBlock
, PROPERTY_NULL
)))
2531 return STG_E_READFAULT
;
2533 if(!(This
->smallBlockDepotChain
=
2534 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2536 return STG_E_READFAULT
;
2539 * Write the root property (memory only)
2543 StgProperty rootProp
;
2545 * Initialize the property chain
2547 memset(&rootProp
, 0, sizeof(rootProp
));
2548 MultiByteToWideChar( CP_ACP
, 0, rootPropertyName
, -1, rootProp
.name
,
2549 sizeof(rootProp
.name
)/sizeof(WCHAR
) );
2550 rootProp
.sizeOfNameString
= (strlenW(rootProp
.name
)+1) * sizeof(WCHAR
);
2551 rootProp
.propertyType
= PROPTYPE_ROOT
;
2552 rootProp
.previousProperty
= PROPERTY_NULL
;
2553 rootProp
.nextProperty
= PROPERTY_NULL
;
2554 rootProp
.dirProperty
= PROPERTY_NULL
;
2555 rootProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
2556 rootProp
.size
.u
.HighPart
= 0;
2557 rootProp
.size
.u
.LowPart
= 0;
2559 StorageImpl_WriteProperty(This
, 0, &rootProp
);
2563 * Find the ID of the root in the property sets.
2565 currentPropertyIndex
= 0;
2569 readSuccessful
= StorageImpl_ReadProperty(
2571 currentPropertyIndex
,
2576 if ( (currentProperty
.sizeOfNameString
!= 0 ) &&
2577 (currentProperty
.propertyType
== PROPTYPE_ROOT
) )
2579 This
->base
.rootPropertySetIndex
= currentPropertyIndex
;
2583 currentPropertyIndex
++;
2585 } while (readSuccessful
&& (This
->base
.rootPropertySetIndex
== PROPERTY_NULL
) );
2587 if (!readSuccessful
)
2590 return STG_E_READFAULT
;
2594 * Create the block chain abstraction for the small block root chain.
2596 if(!(This
->smallBlockRootChain
=
2597 BlockChainStream_Construct(This
, NULL
, This
->base
.rootPropertySetIndex
)))
2598 return STG_E_READFAULT
;
2603 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2605 StorageImpl
*This
= (StorageImpl
*) iface
;
2606 TRACE("(%p)\n", This
);
2608 StorageBaseImpl_DeleteAll(&This
->base
);
2610 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2612 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2613 BlockChainStream_Destroy(This
->rootBlockChain
);
2614 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2616 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2617 HeapFree(GetProcessHeap(), 0, This
);
2620 /******************************************************************************
2621 * Storage32Impl_GetNextFreeBigBlock
2623 * Returns the index of the next free big block.
2624 * If the big block depot is filled, this method will enlarge it.
2627 static ULONG
StorageImpl_GetNextFreeBigBlock(
2630 ULONG depotBlockIndexPos
;
2631 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2633 ULONG depotBlockOffset
;
2634 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2635 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2637 ULONG freeBlock
= BLOCK_UNUSED
;
2639 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2640 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2643 * Scan the entire big block depot until we find a block marked free
2645 while (nextBlockIndex
!= BLOCK_UNUSED
)
2647 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2649 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2652 * Grow the primary depot.
2654 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2656 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2659 * Add a block depot.
2661 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2662 This
->bigBlockDepotCount
++;
2663 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2666 * Flag it as a block depot.
2668 StorageImpl_SetNextBlockInChain(This
,
2672 /* Save new header information.
2674 StorageImpl_SaveFileHeader(This
);
2679 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2681 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2684 * Grow the extended depot.
2686 ULONG extIndex
= BLOCK_UNUSED
;
2687 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2688 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2690 if (extBlockOffset
== 0)
2692 /* We need an extended block.
2694 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2695 This
->extBigBlockDepotCount
++;
2696 depotBlockIndexPos
= extIndex
+ 1;
2699 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2702 * Add a block depot and mark it in the extended block.
2704 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2705 This
->bigBlockDepotCount
++;
2706 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2708 /* Flag the block depot.
2710 StorageImpl_SetNextBlockInChain(This
,
2714 /* If necessary, flag the extended depot block.
2716 if (extIndex
!= BLOCK_UNUSED
)
2717 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2719 /* Save header information.
2721 StorageImpl_SaveFileHeader(This
);
2725 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2729 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2730 ( nextBlockIndex
!= BLOCK_UNUSED
))
2732 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2734 if (nextBlockIndex
== BLOCK_UNUSED
)
2736 freeBlock
= (depotIndex
* blocksPerDepot
) +
2737 (depotBlockOffset
/sizeof(ULONG
));
2740 depotBlockOffset
+= sizeof(ULONG
);
2745 depotBlockOffset
= 0;
2749 * make sure that the block physically exists before using it
2751 BIGBLOCKFILE_EnsureExists(This
->bigBlockFile
, freeBlock
);
2753 This
->prevFreeBlock
= freeBlock
;
2758 /******************************************************************************
2759 * Storage32Impl_AddBlockDepot
2761 * This will create a depot block, essentially it is a block initialized
2764 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2766 BYTE blockBuffer
[BIG_BLOCK_SIZE
];
2769 * Initialize blocks as free
2771 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2772 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
2775 /******************************************************************************
2776 * Storage32Impl_GetExtDepotBlock
2778 * Returns the index of the block that corresponds to the specified depot
2779 * index. This method is only for depot indexes equal or greater than
2780 * COUNT_BBDEPOTINHEADER.
2782 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2784 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2785 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2786 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2787 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2788 ULONG blockIndex
= BLOCK_UNUSED
;
2789 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2791 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2793 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2794 return BLOCK_UNUSED
;
2796 while (extBlockCount
> 0)
2798 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2802 if (extBlockIndex
!= BLOCK_UNUSED
)
2803 StorageImpl_ReadDWordFromBigBlock(This
, extBlockIndex
,
2804 extBlockOffset
* sizeof(ULONG
), &blockIndex
);
2809 /******************************************************************************
2810 * Storage32Impl_SetExtDepotBlock
2812 * Associates the specified block index to the specified depot index.
2813 * This method is only for depot indexes equal or greater than
2814 * COUNT_BBDEPOTINHEADER.
2816 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
2818 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2819 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2820 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2821 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2822 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2824 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2826 while (extBlockCount
> 0)
2828 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2832 if (extBlockIndex
!= BLOCK_UNUSED
)
2834 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
2835 extBlockOffset
* sizeof(ULONG
),
2840 /******************************************************************************
2841 * Storage32Impl_AddExtBlockDepot
2843 * Creates an extended depot block.
2845 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
2847 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
2848 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
2849 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2850 ULONG index
= BLOCK_UNUSED
;
2851 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2852 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
2853 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
2855 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
2856 blocksPerDepotBlock
;
2858 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
2861 * The first extended block.
2863 This
->extBigBlockDepotStart
= index
;
2869 * Follow the chain to the last one.
2871 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
2873 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
2877 * Add the new extended block to the chain.
2879 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
2884 * Initialize this block.
2886 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2887 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
2892 /******************************************************************************
2893 * Storage32Impl_FreeBigBlock
2895 * This method will flag the specified block as free in the big block depot.
2897 static void StorageImpl_FreeBigBlock(
2901 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
2903 if (blockIndex
< This
->prevFreeBlock
)
2904 This
->prevFreeBlock
= blockIndex
;
2907 /************************************************************************
2908 * Storage32Impl_GetNextBlockInChain
2910 * This method will retrieve the block index of the next big block in
2913 * Params: This - Pointer to the Storage object.
2914 * blockIndex - Index of the block to retrieve the chain
2916 * nextBlockIndex - receives the return value.
2918 * Returns: This method returns the index of the next block in the chain.
2919 * It will return the constants:
2920 * BLOCK_SPECIAL - If the block given was not part of a
2922 * BLOCK_END_OF_CHAIN - If the block given was the last in
2924 * BLOCK_UNUSED - If the block given was not past of a chain
2926 * BLOCK_EXTBBDEPOT - This block is part of the extended
2929 * See Windows documentation for more details on IStorage methods.
2931 static HRESULT
StorageImpl_GetNextBlockInChain(
2934 ULONG
* nextBlockIndex
)
2936 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2937 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2938 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2939 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2941 ULONG depotBlockIndexPos
;
2944 *nextBlockIndex
= BLOCK_SPECIAL
;
2946 if(depotBlockCount
>= This
->bigBlockDepotCount
)
2948 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
2949 This
->bigBlockDepotCount
);
2950 return STG_E_READFAULT
;
2954 * Cache the currently accessed depot block.
2956 if (depotBlockCount
!= This
->indexBlockDepotCached
)
2958 This
->indexBlockDepotCached
= depotBlockCount
;
2960 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2962 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2967 * We have to look in the extended depot.
2969 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2972 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2975 return STG_E_READFAULT
;
2977 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
2979 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
2980 This
->blockDepotCached
[index
] = *nextBlockIndex
;
2984 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
2989 /******************************************************************************
2990 * Storage32Impl_GetNextExtendedBlock
2992 * Given an extended block this method will return the next extended block.
2995 * The last ULONG of an extended block is the block index of the next
2996 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3000 * - The index of the next extended block
3001 * - BLOCK_UNUSED: there is no next extended block.
3002 * - Any other return values denotes failure.
3004 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3006 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3007 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3009 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3012 return nextBlockIndex
;
3015 /******************************************************************************
3016 * Storage32Impl_SetNextBlockInChain
3018 * This method will write the index of the specified block's next block
3019 * in the big block depot.
3021 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3024 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3025 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3026 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3029 static void StorageImpl_SetNextBlockInChain(
3034 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3035 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3036 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3037 ULONG depotBlockIndexPos
;
3039 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3040 assert(blockIndex
!= nextBlock
);
3042 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3044 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3049 * We have to look in the extended depot.
3051 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3054 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3057 * Update the cached block depot, if necessary.
3059 if (depotBlockCount
== This
->indexBlockDepotCached
)
3061 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3065 /******************************************************************************
3066 * Storage32Impl_LoadFileHeader
3068 * This method will read in the file header, i.e. big block index -1.
3070 static HRESULT
StorageImpl_LoadFileHeader(
3073 HRESULT hr
= STG_E_FILENOTFOUND
;
3074 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3080 * Get a pointer to the big block of data containing the header.
3082 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3085 * Extract the information from the header.
3090 * Check for the "magic number" signature and return an error if it is not
3093 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3095 return STG_E_OLDFORMAT
;
3098 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3100 return STG_E_INVALIDHEADER
;
3103 StorageUtl_ReadWord(
3105 OFFSET_BIGBLOCKSIZEBITS
,
3106 &This
->bigBlockSizeBits
);
3108 StorageUtl_ReadWord(
3110 OFFSET_SMALLBLOCKSIZEBITS
,
3111 &This
->smallBlockSizeBits
);
3113 StorageUtl_ReadDWord(
3115 OFFSET_BBDEPOTCOUNT
,
3116 &This
->bigBlockDepotCount
);
3118 StorageUtl_ReadDWord(
3120 OFFSET_ROOTSTARTBLOCK
,
3121 &This
->rootStartBlock
);
3123 StorageUtl_ReadDWord(
3125 OFFSET_SBDEPOTSTART
,
3126 &This
->smallBlockDepotStart
);
3128 StorageUtl_ReadDWord(
3130 OFFSET_EXTBBDEPOTSTART
,
3131 &This
->extBigBlockDepotStart
);
3133 StorageUtl_ReadDWord(
3135 OFFSET_EXTBBDEPOTCOUNT
,
3136 &This
->extBigBlockDepotCount
);
3138 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3140 StorageUtl_ReadDWord(
3142 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3143 &(This
->bigBlockDepotStart
[index
]));
3147 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3149 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3150 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3153 * Right now, the code is making some assumptions about the size of the
3154 * blocks, just make sure they are what we're expecting.
3156 if (This
->bigBlockSize
!= DEF_BIG_BLOCK_SIZE
||
3157 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
)
3159 WARN("Broken OLE storage file\n");
3160 hr
= STG_E_INVALIDHEADER
;
3169 /******************************************************************************
3170 * Storage32Impl_SaveFileHeader
3172 * This method will save to the file the header, i.e. big block -1.
3174 static void StorageImpl_SaveFileHeader(
3177 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3182 * Get a pointer to the big block of data containing the header.
3184 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3187 * If the block read failed, the file is probably new.
3192 * Initialize for all unknown fields.
3194 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
3197 * Initialize the magic number.
3199 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3202 * And a bunch of things we don't know what they mean
3204 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3205 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3206 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3207 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
3211 * Write the information to the header.
3213 StorageUtl_WriteWord(
3215 OFFSET_BIGBLOCKSIZEBITS
,
3216 This
->bigBlockSizeBits
);
3218 StorageUtl_WriteWord(
3220 OFFSET_SMALLBLOCKSIZEBITS
,
3221 This
->smallBlockSizeBits
);
3223 StorageUtl_WriteDWord(
3225 OFFSET_BBDEPOTCOUNT
,
3226 This
->bigBlockDepotCount
);
3228 StorageUtl_WriteDWord(
3230 OFFSET_ROOTSTARTBLOCK
,
3231 This
->rootStartBlock
);
3233 StorageUtl_WriteDWord(
3235 OFFSET_SBDEPOTSTART
,
3236 This
->smallBlockDepotStart
);
3238 StorageUtl_WriteDWord(
3240 OFFSET_SBDEPOTCOUNT
,
3241 This
->smallBlockDepotChain
?
3242 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3244 StorageUtl_WriteDWord(
3246 OFFSET_EXTBBDEPOTSTART
,
3247 This
->extBigBlockDepotStart
);
3249 StorageUtl_WriteDWord(
3251 OFFSET_EXTBBDEPOTCOUNT
,
3252 This
->extBigBlockDepotCount
);
3254 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3256 StorageUtl_WriteDWord(
3258 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3259 (This
->bigBlockDepotStart
[index
]));
3263 * Write the big block back to the file.
3265 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
3268 /******************************************************************************
3269 * Storage32Impl_ReadProperty
3271 * This method will read the specified property from the property chain.
3273 BOOL
StorageImpl_ReadProperty(
3276 StgProperty
* buffer
)
3278 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
3279 ULARGE_INTEGER offsetInPropSet
;
3283 offsetInPropSet
.u
.HighPart
= 0;
3284 offsetInPropSet
.u
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
3286 readRes
= BlockChainStream_ReadAt(
3287 This
->rootBlockChain
,
3293 if (SUCCEEDED(readRes
))
3295 /* replace the name of root entry (often "Root Entry") by the file name */
3296 WCHAR
*propName
= (index
== This
->base
.rootPropertySetIndex
) ?
3297 This
->filename
: (WCHAR
*)currentProperty
+OFFSET_PS_NAME
;
3299 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3303 PROPERTY_NAME_BUFFER_LEN
);
3304 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3306 memcpy(&buffer
->propertyType
, currentProperty
+ OFFSET_PS_PROPERTYTYPE
, 1);
3308 StorageUtl_ReadWord(
3310 OFFSET_PS_NAMELENGTH
,
3311 &buffer
->sizeOfNameString
);
3313 StorageUtl_ReadDWord(
3315 OFFSET_PS_PREVIOUSPROP
,
3316 &buffer
->previousProperty
);
3318 StorageUtl_ReadDWord(
3321 &buffer
->nextProperty
);
3323 StorageUtl_ReadDWord(
3326 &buffer
->dirProperty
);
3328 StorageUtl_ReadGUID(
3331 &buffer
->propertyUniqueID
);
3333 StorageUtl_ReadDWord(
3336 &buffer
->timeStampS1
);
3338 StorageUtl_ReadDWord(
3341 &buffer
->timeStampD1
);
3343 StorageUtl_ReadDWord(
3346 &buffer
->timeStampS2
);
3348 StorageUtl_ReadDWord(
3351 &buffer
->timeStampD2
);
3353 StorageUtl_ReadDWord(
3355 OFFSET_PS_STARTBLOCK
,
3356 &buffer
->startingBlock
);
3358 StorageUtl_ReadDWord(
3361 &buffer
->size
.u
.LowPart
);
3363 buffer
->size
.u
.HighPart
= 0;
3366 return SUCCEEDED(readRes
) ? TRUE
: FALSE
;
3369 /*********************************************************************
3370 * Write the specified property into the property chain
3372 BOOL
StorageImpl_WriteProperty(
3375 const StgProperty
* buffer
)
3377 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
3378 ULARGE_INTEGER offsetInPropSet
;
3382 offsetInPropSet
.u
.HighPart
= 0;
3383 offsetInPropSet
.u
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
3385 memset(currentProperty
, 0, PROPSET_BLOCK_SIZE
);
3388 currentProperty
+ OFFSET_PS_NAME
,
3390 PROPERTY_NAME_BUFFER_LEN
);
3392 memcpy(currentProperty
+ OFFSET_PS_PROPERTYTYPE
, &buffer
->propertyType
, 1);
3394 StorageUtl_WriteWord(
3396 OFFSET_PS_NAMELENGTH
,
3397 buffer
->sizeOfNameString
);
3399 StorageUtl_WriteDWord(
3401 OFFSET_PS_PREVIOUSPROP
,
3402 buffer
->previousProperty
);
3404 StorageUtl_WriteDWord(
3407 buffer
->nextProperty
);
3409 StorageUtl_WriteDWord(
3412 buffer
->dirProperty
);
3414 StorageUtl_WriteGUID(
3417 &buffer
->propertyUniqueID
);
3419 StorageUtl_WriteDWord(
3422 buffer
->timeStampS1
);
3424 StorageUtl_WriteDWord(
3427 buffer
->timeStampD1
);
3429 StorageUtl_WriteDWord(
3432 buffer
->timeStampS2
);
3434 StorageUtl_WriteDWord(
3437 buffer
->timeStampD2
);
3439 StorageUtl_WriteDWord(
3441 OFFSET_PS_STARTBLOCK
,
3442 buffer
->startingBlock
);
3444 StorageUtl_WriteDWord(
3447 buffer
->size
.u
.LowPart
);
3449 writeRes
= BlockChainStream_WriteAt(This
->rootBlockChain
,
3454 return SUCCEEDED(writeRes
) ? TRUE
: FALSE
;
3457 static BOOL
StorageImpl_ReadBigBlock(
3462 ULARGE_INTEGER ulOffset
;
3465 ulOffset
.u
.HighPart
= 0;
3466 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3468 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3469 return (read
== This
->bigBlockSize
);
3472 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3478 ULARGE_INTEGER ulOffset
;
3482 ulOffset
.u
.HighPart
= 0;
3483 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3484 ulOffset
.u
.LowPart
+= offset
;
3486 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3487 *value
= lendian32toh(tmp
);
3488 return (read
== sizeof(DWORD
));
3491 static BOOL
StorageImpl_WriteBigBlock(
3496 ULARGE_INTEGER ulOffset
;
3499 ulOffset
.u
.HighPart
= 0;
3500 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3502 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3503 return (wrote
== This
->bigBlockSize
);
3506 static BOOL
StorageImpl_WriteDWordToBigBlock(
3512 ULARGE_INTEGER ulOffset
;
3515 ulOffset
.u
.HighPart
= 0;
3516 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3517 ulOffset
.u
.LowPart
+= offset
;
3519 value
= htole32(value
);
3520 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3521 return (wrote
== sizeof(DWORD
));
3524 /******************************************************************************
3525 * Storage32Impl_SmallBlocksToBigBlocks
3527 * This method will convert a small block chain to a big block chain.
3528 * The small block chain will be destroyed.
3530 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3532 SmallBlockChainStream
** ppsbChain
)
3534 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3535 ULARGE_INTEGER size
, offset
;
3536 ULONG cbRead
, cbWritten
;
3537 ULARGE_INTEGER cbTotalRead
;
3538 ULONG propertyIndex
;
3539 HRESULT resWrite
= S_OK
;
3541 StgProperty chainProperty
;
3543 BlockChainStream
*bbTempChain
= NULL
;
3544 BlockChainStream
*bigBlockChain
= NULL
;
3547 * Create a temporary big block chain that doesn't have
3548 * an associated property. This temporary chain will be
3549 * used to copy data from small blocks to big blocks.
3551 bbTempChain
= BlockChainStream_Construct(This
,
3554 if(!bbTempChain
) return NULL
;
3556 * Grow the big block chain.
3558 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3559 BlockChainStream_SetSize(bbTempChain
, size
);
3562 * Copy the contents of the small block chain to the big block chain
3563 * by small block size increments.
3565 offset
.u
.LowPart
= 0;
3566 offset
.u
.HighPart
= 0;
3567 cbTotalRead
.QuadPart
= 0;
3569 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3572 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3574 This
->smallBlockSize
,
3577 if (FAILED(resRead
))
3582 cbTotalRead
.QuadPart
+= cbRead
;
3584 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3590 if (FAILED(resWrite
))
3593 offset
.u
.LowPart
+= This
->smallBlockSize
;
3595 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3596 HeapFree(GetProcessHeap(),0,buffer
);
3598 if (FAILED(resRead
) || FAILED(resWrite
))
3600 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3601 BlockChainStream_Destroy(bbTempChain
);
3606 * Destroy the small block chain.
3608 propertyIndex
= (*ppsbChain
)->ownerPropertyIndex
;
3609 size
.u
.HighPart
= 0;
3611 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3612 SmallBlockChainStream_Destroy(*ppsbChain
);
3616 * Change the property information. This chain is now a big block chain
3617 * and it doesn't reside in the small blocks chain anymore.
3619 StorageImpl_ReadProperty(This
, propertyIndex
, &chainProperty
);
3621 chainProperty
.startingBlock
= bbHeadOfChain
;
3623 StorageImpl_WriteProperty(This
, propertyIndex
, &chainProperty
);
3626 * Destroy the temporary propertyless big block chain.
3627 * Create a new big block chain associated with this property.
3629 BlockChainStream_Destroy(bbTempChain
);
3630 bigBlockChain
= BlockChainStream_Construct(This
,
3634 return bigBlockChain
;
3637 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
3639 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
3641 StorageBaseImpl_Release((IStorage
*)This
->base
.ancestorStorage
);
3642 HeapFree(GetProcessHeap(), 0, This
);
3645 /******************************************************************************
3647 ** Storage32InternalImpl_Commit
3649 ** The non-root storages cannot be opened in transacted mode thus this function
3652 static HRESULT WINAPI
StorageInternalImpl_Commit(
3654 DWORD grfCommitFlags
) /* [in] */
3659 /******************************************************************************
3661 ** Storage32InternalImpl_Revert
3663 ** The non-root storages cannot be opened in transacted mode thus this function
3666 static HRESULT WINAPI
StorageInternalImpl_Revert(
3672 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
3674 IStorage_Release((IStorage
*)This
->parentStorage
);
3675 HeapFree(GetProcessHeap(), 0, This
->stackToVisit
);
3676 HeapFree(GetProcessHeap(), 0, This
);
3679 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
3680 IEnumSTATSTG
* iface
,
3684 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3687 * Perform a sanity check on the parameters.
3690 return E_INVALIDARG
;
3693 * Initialize the return parameter.
3698 * Compare the riid with the interface IDs implemented by this object.
3700 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
3701 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
3704 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
3708 return E_NOINTERFACE
;
3711 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
3712 IEnumSTATSTG
* iface
)
3714 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3715 return InterlockedIncrement(&This
->ref
);
3718 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
3719 IEnumSTATSTG
* iface
)
3721 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3725 newRef
= InterlockedDecrement(&This
->ref
);
3728 * If the reference count goes down to 0, perform suicide.
3732 IEnumSTATSTGImpl_Destroy(This
);
3738 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
3739 IEnumSTATSTG
* iface
,
3742 ULONG
* pceltFetched
)
3744 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3746 StgProperty currentProperty
;
3747 STATSTG
* currentReturnStruct
= rgelt
;
3748 ULONG objectFetched
= 0;
3749 ULONG currentSearchNode
;
3752 * Perform a sanity check on the parameters.
3754 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
3755 return E_INVALIDARG
;
3758 * To avoid the special case, get another pointer to a ULONG value if
3759 * the caller didn't supply one.
3761 if (pceltFetched
==0)
3762 pceltFetched
= &objectFetched
;
3765 * Start the iteration, we will iterate until we hit the end of the
3766 * linked list or until we hit the number of items to iterate through
3771 * Start with the node at the top of the stack.
3773 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3775 while ( ( *pceltFetched
< celt
) &&
3776 ( currentSearchNode
!=PROPERTY_NULL
) )
3779 * Remove the top node from the stack
3781 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3784 * Read the property from the storage.
3786 StorageImpl_ReadProperty(This
->parentStorage
,
3791 * Copy the information to the return buffer.
3793 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct
,
3798 * Step to the next item in the iteration
3801 currentReturnStruct
++;
3804 * Push the next search node in the search stack.
3806 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3809 * continue the iteration.
3811 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3814 if (*pceltFetched
== celt
)
3821 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
3822 IEnumSTATSTG
* iface
,
3825 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3827 StgProperty currentProperty
;
3828 ULONG objectFetched
= 0;
3829 ULONG currentSearchNode
;
3832 * Start with the node at the top of the stack.
3834 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3836 while ( (objectFetched
< celt
) &&
3837 (currentSearchNode
!=PROPERTY_NULL
) )
3840 * Remove the top node from the stack
3842 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3845 * Read the property from the storage.
3847 StorageImpl_ReadProperty(This
->parentStorage
,
3852 * Step to the next item in the iteration
3857 * Push the next search node in the search stack.
3859 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3862 * continue the iteration.
3864 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3867 if (objectFetched
== celt
)
3873 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
3874 IEnumSTATSTG
* iface
)
3876 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3878 StgProperty rootProperty
;
3879 BOOL readSuccessful
;
3882 * Re-initialize the search stack to an empty stack
3884 This
->stackSize
= 0;
3887 * Read the root property from the storage.
3889 readSuccessful
= StorageImpl_ReadProperty(
3890 This
->parentStorage
,
3891 This
->firstPropertyNode
,
3896 assert(rootProperty
.sizeOfNameString
!=0);
3899 * Push the search node in the search stack.
3901 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.dirProperty
);
3907 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
3908 IEnumSTATSTG
* iface
,
3909 IEnumSTATSTG
** ppenum
)
3911 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3913 IEnumSTATSTGImpl
* newClone
;
3916 * Perform a sanity check on the parameters.
3919 return E_INVALIDARG
;
3921 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
3922 This
->firstPropertyNode
);
3926 * The new clone enumeration must point to the same current node as
3929 newClone
->stackSize
= This
->stackSize
;
3930 newClone
->stackMaxSize
= This
->stackMaxSize
;
3931 newClone
->stackToVisit
=
3932 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * newClone
->stackMaxSize
);
3935 newClone
->stackToVisit
,
3937 sizeof(ULONG
) * newClone
->stackSize
);
3939 *ppenum
= (IEnumSTATSTG
*)newClone
;
3942 * Don't forget to nail down a reference to the clone before
3945 IEnumSTATSTGImpl_AddRef(*ppenum
);
3950 static INT
IEnumSTATSTGImpl_FindParentProperty(
3951 IEnumSTATSTGImpl
*This
,
3952 ULONG childProperty
,
3953 StgProperty
*currentProperty
,
3956 ULONG currentSearchNode
;
3960 * To avoid the special case, get another pointer to a ULONG value if
3961 * the caller didn't supply one.
3964 thisNodeId
= &foundNode
;
3967 * Start with the node at the top of the stack.
3969 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3972 while (currentSearchNode
!=PROPERTY_NULL
)
3975 * Store the current node in the returned parameters
3977 *thisNodeId
= currentSearchNode
;
3980 * Remove the top node from the stack
3982 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3985 * Read the property from the storage.
3987 StorageImpl_ReadProperty(
3988 This
->parentStorage
,
3992 if (currentProperty
->previousProperty
== childProperty
)
3993 return PROPERTY_RELATION_PREVIOUS
;
3995 else if (currentProperty
->nextProperty
== childProperty
)
3996 return PROPERTY_RELATION_NEXT
;
3998 else if (currentProperty
->dirProperty
== childProperty
)
3999 return PROPERTY_RELATION_DIR
;
4002 * Push the next search node in the search stack.
4004 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
4007 * continue the iteration.
4009 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
4012 return PROPERTY_NULL
;
4015 static ULONG
IEnumSTATSTGImpl_FindProperty(
4016 IEnumSTATSTGImpl
* This
,
4017 const OLECHAR
* lpszPropName
,
4018 StgProperty
* currentProperty
)
4020 ULONG currentSearchNode
;
4023 * Start with the node at the top of the stack.
4025 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
4027 while (currentSearchNode
!=PROPERTY_NULL
)
4030 * Remove the top node from the stack
4032 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
4035 * Read the property from the storage.
4037 StorageImpl_ReadProperty(This
->parentStorage
,
4041 if (propertyNameCmp(currentProperty
->name
, lpszPropName
) == 0)
4042 return currentSearchNode
;
4045 * Push the next search node in the search stack.
4047 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
4050 * continue the iteration.
4052 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
4055 return PROPERTY_NULL
;
4058 static void IEnumSTATSTGImpl_PushSearchNode(
4059 IEnumSTATSTGImpl
* This
,
4062 StgProperty rootProperty
;
4063 BOOL readSuccessful
;
4066 * First, make sure we're not trying to push an unexisting node.
4068 if (nodeToPush
==PROPERTY_NULL
)
4072 * First push the node to the stack
4074 if (This
->stackSize
== This
->stackMaxSize
)
4076 This
->stackMaxSize
+= ENUMSTATSGT_SIZE_INCREMENT
;
4078 This
->stackToVisit
= HeapReAlloc(
4082 sizeof(ULONG
) * This
->stackMaxSize
);
4085 This
->stackToVisit
[This
->stackSize
] = nodeToPush
;
4089 * Read the root property from the storage.
4091 readSuccessful
= StorageImpl_ReadProperty(
4092 This
->parentStorage
,
4098 assert(rootProperty
.sizeOfNameString
!=0);
4101 * Push the previous search node in the search stack.
4103 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.previousProperty
);
4107 static ULONG
IEnumSTATSTGImpl_PopSearchNode(
4108 IEnumSTATSTGImpl
* This
,
4113 if (This
->stackSize
== 0)
4114 return PROPERTY_NULL
;
4116 topNode
= This
->stackToVisit
[This
->stackSize
-1];
4125 * Virtual function table for the IEnumSTATSTGImpl class.
4127 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
4129 IEnumSTATSTGImpl_QueryInterface
,
4130 IEnumSTATSTGImpl_AddRef
,
4131 IEnumSTATSTGImpl_Release
,
4132 IEnumSTATSTGImpl_Next
,
4133 IEnumSTATSTGImpl_Skip
,
4134 IEnumSTATSTGImpl_Reset
,
4135 IEnumSTATSTGImpl_Clone
4138 /******************************************************************************
4139 ** IEnumSTATSTGImpl implementation
4142 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
4143 StorageImpl
* parentStorage
,
4144 ULONG firstPropertyNode
)
4146 IEnumSTATSTGImpl
* newEnumeration
;
4148 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
4150 if (newEnumeration
!=0)
4153 * Set-up the virtual function table and reference count.
4155 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
4156 newEnumeration
->ref
= 0;
4159 * We want to nail-down the reference to the storage in case the
4160 * enumeration out-lives the storage in the client application.
4162 newEnumeration
->parentStorage
= parentStorage
;
4163 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
4165 newEnumeration
->firstPropertyNode
= firstPropertyNode
;
4168 * Initialize the search stack
4170 newEnumeration
->stackSize
= 0;
4171 newEnumeration
->stackMaxSize
= ENUMSTATSGT_SIZE_INCREMENT
;
4172 newEnumeration
->stackToVisit
=
4173 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
)*ENUMSTATSGT_SIZE_INCREMENT
);
4176 * Make sure the current node of the iterator is the first one.
4178 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
4181 return newEnumeration
;
4185 * Virtual function table for the Storage32InternalImpl class.
4187 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
4189 StorageBaseImpl_QueryInterface
,
4190 StorageBaseImpl_AddRef
,
4191 StorageBaseImpl_Release
,
4192 StorageBaseImpl_CreateStream
,
4193 StorageBaseImpl_OpenStream
,
4194 StorageImpl_CreateStorage
,
4195 StorageBaseImpl_OpenStorage
,
4197 StorageImpl_MoveElementTo
,
4198 StorageInternalImpl_Commit
,
4199 StorageInternalImpl_Revert
,
4200 StorageBaseImpl_EnumElements
,
4201 StorageImpl_DestroyElement
,
4202 StorageBaseImpl_RenameElement
,
4203 StorageImpl_SetElementTimes
,
4204 StorageBaseImpl_SetClass
,
4205 StorageImpl_SetStateBits
,
4206 StorageBaseImpl_Stat
4209 /******************************************************************************
4210 ** Storage32InternalImpl implementation
4213 static StorageInternalImpl
* StorageInternalImpl_Construct(
4214 StorageImpl
* ancestorStorage
,
4216 ULONG rootPropertyIndex
)
4218 StorageInternalImpl
* newStorage
;
4221 * Allocate space for the new storage object
4223 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
4228 * Initialize the stream list
4230 list_init(&newStorage
->base
.strmHead
);
4233 * Initialize the virtual function table.
4235 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
4236 newStorage
->base
.v_destructor
= StorageInternalImpl_Destroy
;
4237 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
4240 * Keep the ancestor storage pointer and nail a reference to it.
4242 newStorage
->base
.ancestorStorage
= ancestorStorage
;
4243 StorageBaseImpl_AddRef((IStorage
*)(newStorage
->base
.ancestorStorage
));
4246 * Keep the index of the root property set for this storage,
4248 newStorage
->base
.rootPropertySetIndex
= rootPropertyIndex
;
4256 /******************************************************************************
4257 ** StorageUtl implementation
4260 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
4264 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
4265 *value
= lendian16toh(tmp
);
4268 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
4270 value
= htole16(value
);
4271 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
4274 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
4278 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
4279 *value
= lendian32toh(tmp
);
4282 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
4284 value
= htole32(value
);
4285 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
4288 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
4289 ULARGE_INTEGER
* value
)
4291 #ifdef WORDS_BIGENDIAN
4294 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4295 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
4296 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
4298 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4302 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
4303 const ULARGE_INTEGER
*value
)
4305 #ifdef WORDS_BIGENDIAN
4308 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
4309 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
4310 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
4312 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
4316 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
4318 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
4319 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
4320 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
4322 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
4325 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
4327 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
4328 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
4329 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
4331 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
4334 void StorageUtl_CopyPropertyToSTATSTG(
4335 STATSTG
* destination
,
4336 const StgProperty
* source
,
4340 * The copy of the string occurs only when the flag is not set
4342 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
4343 (source
->name
== NULL
) ||
4344 (source
->name
[0] == 0) )
4346 destination
->pwcsName
= 0;
4350 destination
->pwcsName
=
4351 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
4353 strcpyW(destination
->pwcsName
, source
->name
);
4356 switch (source
->propertyType
)
4358 case PROPTYPE_STORAGE
:
4360 destination
->type
= STGTY_STORAGE
;
4362 case PROPTYPE_STREAM
:
4363 destination
->type
= STGTY_STREAM
;
4366 destination
->type
= STGTY_STREAM
;
4370 destination
->cbSize
= source
->size
;
4372 currentReturnStruct->mtime = {0}; TODO
4373 currentReturnStruct->ctime = {0};
4374 currentReturnStruct->atime = {0};
4376 destination
->grfMode
= 0;
4377 destination
->grfLocksSupported
= 0;
4378 destination
->clsid
= source
->propertyUniqueID
;
4379 destination
->grfStateBits
= 0;
4380 destination
->reserved
= 0;
4383 /******************************************************************************
4384 ** BlockChainStream implementation
4387 BlockChainStream
* BlockChainStream_Construct(
4388 StorageImpl
* parentStorage
,
4389 ULONG
* headOfStreamPlaceHolder
,
4390 ULONG propertyIndex
)
4392 BlockChainStream
* newStream
;
4395 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
4397 newStream
->parentStorage
= parentStorage
;
4398 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
4399 newStream
->ownerPropertyIndex
= propertyIndex
;
4400 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
4401 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
4402 newStream
->numBlocks
= 0;
4404 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
4406 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4408 newStream
->numBlocks
++;
4409 newStream
->tailIndex
= blockIndex
;
4411 if(FAILED(StorageImpl_GetNextBlockInChain(
4416 HeapFree(GetProcessHeap(), 0, newStream
);
4424 void BlockChainStream_Destroy(BlockChainStream
* This
)
4426 HeapFree(GetProcessHeap(), 0, This
);
4429 /******************************************************************************
4430 * BlockChainStream_GetHeadOfChain
4432 * Returns the head of this stream chain.
4433 * Some special chains don't have properties, their heads are kept in
4434 * This->headOfStreamPlaceHolder.
4437 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
4439 StgProperty chainProperty
;
4440 BOOL readSuccessful
;
4442 if (This
->headOfStreamPlaceHolder
!= 0)
4443 return *(This
->headOfStreamPlaceHolder
);
4445 if (This
->ownerPropertyIndex
!= PROPERTY_NULL
)
4447 readSuccessful
= StorageImpl_ReadProperty(
4448 This
->parentStorage
,
4449 This
->ownerPropertyIndex
,
4454 return chainProperty
.startingBlock
;
4458 return BLOCK_END_OF_CHAIN
;
4461 /******************************************************************************
4462 * BlockChainStream_GetCount
4464 * Returns the number of blocks that comprises this chain.
4465 * This is not the size of the stream as the last block may not be full!
4468 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
4473 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4475 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4479 if(FAILED(StorageImpl_GetNextBlockInChain(
4480 This
->parentStorage
,
4489 /******************************************************************************
4490 * BlockChainStream_ReadAt
4492 * Reads a specified number of bytes from this chain at the specified offset.
4493 * bytesRead may be NULL.
4494 * Failure will be returned if the specified number of bytes has not been read.
4496 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
4497 ULARGE_INTEGER offset
,
4502 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4503 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4504 ULONG bytesToReadInBuffer
;
4508 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
4511 * Find the first block in the stream that contains part of the buffer.
4513 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4514 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4515 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4517 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4518 This
->lastBlockNoInSequence
= blockNoInSequence
;
4522 ULONG temp
= blockNoInSequence
;
4524 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4525 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4526 This
->lastBlockNoInSequence
= temp
;
4529 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4531 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4532 return STG_E_DOCFILECORRUPT
;
4533 blockNoInSequence
--;
4536 if ((blockNoInSequence
> 0) && (blockIndex
== BLOCK_END_OF_CHAIN
))
4537 return STG_E_DOCFILECORRUPT
; /* We failed to find the starting block */
4539 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4542 * Start reading the buffer.
4545 bufferWalker
= buffer
;
4547 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4549 ULARGE_INTEGER ulOffset
;
4552 * Calculate how many bytes we can copy from this big block.
4554 bytesToReadInBuffer
=
4555 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4557 TRACE("block %i\n",blockIndex
);
4558 ulOffset
.u
.HighPart
= 0;
4559 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
4562 StorageImpl_ReadAt(This
->parentStorage
,
4565 bytesToReadInBuffer
,
4568 * Step to the next big block.
4570 if( size
> bytesReadAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4571 return STG_E_DOCFILECORRUPT
;
4573 bufferWalker
+= bytesReadAt
;
4574 size
-= bytesReadAt
;
4575 *bytesRead
+= bytesReadAt
;
4576 offsetInBlock
= 0; /* There is no offset on the next block */
4578 if (bytesToReadInBuffer
!= bytesReadAt
)
4582 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
4585 /******************************************************************************
4586 * BlockChainStream_WriteAt
4588 * Writes the specified number of bytes to this chain at the specified offset.
4589 * bytesWritten may be NULL.
4590 * Will fail if not all specified number of bytes have been written.
4592 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
4593 ULARGE_INTEGER offset
,
4596 ULONG
* bytesWritten
)
4598 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4599 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4602 const BYTE
* bufferWalker
;
4605 * Find the first block in the stream that contains part of the buffer.
4607 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4608 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4609 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4611 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4612 This
->lastBlockNoInSequence
= blockNoInSequence
;
4616 ULONG temp
= blockNoInSequence
;
4618 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4619 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4620 This
->lastBlockNoInSequence
= temp
;
4623 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4625 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4627 return STG_E_DOCFILECORRUPT
;
4628 blockNoInSequence
--;
4631 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4633 /* BlockChainStream_SetSize should have already been called to ensure we have
4634 * enough blocks in the chain to write into */
4635 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4637 ERR("not enough blocks in chain to write data\n");
4638 return STG_E_DOCFILECORRUPT
;
4642 bufferWalker
= buffer
;
4644 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4646 ULARGE_INTEGER ulOffset
;
4647 DWORD bytesWrittenAt
;
4649 * Calculate how many bytes we can copy from this big block.
4652 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4654 TRACE("block %i\n",blockIndex
);
4655 ulOffset
.u
.HighPart
= 0;
4656 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
4659 StorageImpl_WriteAt(This
->parentStorage
,
4666 * Step to the next big block.
4668 if(size
> bytesWrittenAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4670 return STG_E_DOCFILECORRUPT
;
4672 bufferWalker
+= bytesWrittenAt
;
4673 size
-= bytesWrittenAt
;
4674 *bytesWritten
+= bytesWrittenAt
;
4675 offsetInBlock
= 0; /* There is no offset on the next block */
4677 if (bytesWrittenAt
!= bytesToWrite
)
4681 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
4684 /******************************************************************************
4685 * BlockChainStream_Shrink
4687 * Shrinks this chain in the big block depot.
4689 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
4690 ULARGE_INTEGER newSize
)
4692 ULONG blockIndex
, extraBlock
;
4697 * Reset the last accessed block cache.
4699 This
->lastBlockNoInSequence
= 0xFFFFFFFF;
4700 This
->lastBlockNoInSequenceIndex
= BLOCK_END_OF_CHAIN
;
4703 * Figure out how many blocks are needed to contain the new size
4705 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4707 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4710 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4713 * Go to the new end of chain
4715 while (count
< numBlocks
)
4717 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4723 /* Get the next block before marking the new end */
4724 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4728 /* Mark the new end of chain */
4729 StorageImpl_SetNextBlockInChain(
4730 This
->parentStorage
,
4732 BLOCK_END_OF_CHAIN
);
4734 This
->tailIndex
= blockIndex
;
4735 This
->numBlocks
= numBlocks
;
4738 * Mark the extra blocks as free
4740 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4742 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
,
4745 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
4746 extraBlock
= blockIndex
;
4752 /******************************************************************************
4753 * BlockChainStream_Enlarge
4755 * Grows this chain in the big block depot.
4757 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
4758 ULARGE_INTEGER newSize
)
4760 ULONG blockIndex
, currentBlock
;
4762 ULONG oldNumBlocks
= 0;
4764 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4767 * Empty chain. Create the head.
4769 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4771 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4772 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
4774 BLOCK_END_OF_CHAIN
);
4776 if (This
->headOfStreamPlaceHolder
!= 0)
4778 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
4782 StgProperty chainProp
;
4783 assert(This
->ownerPropertyIndex
!= PROPERTY_NULL
);
4785 StorageImpl_ReadProperty(
4786 This
->parentStorage
,
4787 This
->ownerPropertyIndex
,
4790 chainProp
.startingBlock
= blockIndex
;
4792 StorageImpl_WriteProperty(
4793 This
->parentStorage
,
4794 This
->ownerPropertyIndex
,
4798 This
->tailIndex
= blockIndex
;
4799 This
->numBlocks
= 1;
4803 * Figure out how many blocks are needed to contain this stream
4805 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4807 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4811 * Go to the current end of chain
4813 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
4815 currentBlock
= blockIndex
;
4817 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4820 currentBlock
= blockIndex
;
4822 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
4827 This
->tailIndex
= currentBlock
;
4830 currentBlock
= This
->tailIndex
;
4831 oldNumBlocks
= This
->numBlocks
;
4834 * Add new blocks to the chain
4836 if (oldNumBlocks
< newNumBlocks
)
4838 while (oldNumBlocks
< newNumBlocks
)
4840 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4842 StorageImpl_SetNextBlockInChain(
4843 This
->parentStorage
,
4847 StorageImpl_SetNextBlockInChain(
4848 This
->parentStorage
,
4850 BLOCK_END_OF_CHAIN
);
4852 currentBlock
= blockIndex
;
4856 This
->tailIndex
= blockIndex
;
4857 This
->numBlocks
= newNumBlocks
;
4863 /******************************************************************************
4864 * BlockChainStream_SetSize
4866 * Sets the size of this stream. The big block depot will be updated.
4867 * The file will grow if we grow the chain.
4869 * TODO: Free the actual blocks in the file when we shrink the chain.
4870 * Currently, the blocks are still in the file. So the file size
4871 * doesn't shrink even if we shrink streams.
4873 BOOL
BlockChainStream_SetSize(
4874 BlockChainStream
* This
,
4875 ULARGE_INTEGER newSize
)
4877 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
4879 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
4882 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
4884 BlockChainStream_Shrink(This
, newSize
);
4888 BlockChainStream_Enlarge(This
, newSize
);
4894 /******************************************************************************
4895 * BlockChainStream_GetSize
4897 * Returns the size of this chain.
4898 * Will return the block count if this chain doesn't have a property.
4900 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
4902 StgProperty chainProperty
;
4904 if(This
->headOfStreamPlaceHolder
== NULL
)
4907 * This chain is a data stream read the property and return
4908 * the appropriate size
4910 StorageImpl_ReadProperty(
4911 This
->parentStorage
,
4912 This
->ownerPropertyIndex
,
4915 return chainProperty
.size
;
4920 * this chain is a chain that does not have a property, figure out the
4921 * size by making the product number of used blocks times the
4924 ULARGE_INTEGER result
;
4925 result
.u
.HighPart
= 0;
4928 BlockChainStream_GetCount(This
) *
4929 This
->parentStorage
->bigBlockSize
;
4935 /******************************************************************************
4936 ** SmallBlockChainStream implementation
4939 SmallBlockChainStream
* SmallBlockChainStream_Construct(
4940 StorageImpl
* parentStorage
,
4941 ULONG propertyIndex
)
4943 SmallBlockChainStream
* newStream
;
4945 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
4947 newStream
->parentStorage
= parentStorage
;
4948 newStream
->ownerPropertyIndex
= propertyIndex
;
4953 void SmallBlockChainStream_Destroy(
4954 SmallBlockChainStream
* This
)
4956 HeapFree(GetProcessHeap(), 0, This
);
4959 /******************************************************************************
4960 * SmallBlockChainStream_GetHeadOfChain
4962 * Returns the head of this chain of small blocks.
4964 static ULONG
SmallBlockChainStream_GetHeadOfChain(
4965 SmallBlockChainStream
* This
)
4967 StgProperty chainProperty
;
4968 BOOL readSuccessful
;
4970 if (This
->ownerPropertyIndex
)
4972 readSuccessful
= StorageImpl_ReadProperty(
4973 This
->parentStorage
,
4974 This
->ownerPropertyIndex
,
4979 return chainProperty
.startingBlock
;
4984 return BLOCK_END_OF_CHAIN
;
4987 /******************************************************************************
4988 * SmallBlockChainStream_GetNextBlockInChain
4990 * Returns the index of the next small block in this chain.
4993 * - BLOCK_END_OF_CHAIN: end of this chain
4994 * - BLOCK_UNUSED: small block 'blockIndex' is free
4996 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
4997 SmallBlockChainStream
* This
,
4999 ULONG
* nextBlockInChain
)
5001 ULARGE_INTEGER offsetOfBlockInDepot
;
5006 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
5008 offsetOfBlockInDepot
.u
.HighPart
= 0;
5009 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5012 * Read those bytes in the buffer from the small block file.
5014 res
= BlockChainStream_ReadAt(
5015 This
->parentStorage
->smallBlockDepotChain
,
5016 offsetOfBlockInDepot
,
5023 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
5030 /******************************************************************************
5031 * SmallBlockChainStream_SetNextBlockInChain
5033 * Writes the index of the next block of the specified block in the small
5035 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5036 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5038 static void SmallBlockChainStream_SetNextBlockInChain(
5039 SmallBlockChainStream
* This
,
5043 ULARGE_INTEGER offsetOfBlockInDepot
;
5047 offsetOfBlockInDepot
.u
.HighPart
= 0;
5048 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5050 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
5053 * Read those bytes in the buffer from the small block file.
5055 BlockChainStream_WriteAt(
5056 This
->parentStorage
->smallBlockDepotChain
,
5057 offsetOfBlockInDepot
,
5063 /******************************************************************************
5064 * SmallBlockChainStream_FreeBlock
5066 * Flag small block 'blockIndex' as free in the small block depot.
5068 static void SmallBlockChainStream_FreeBlock(
5069 SmallBlockChainStream
* This
,
5072 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
5075 /******************************************************************************
5076 * SmallBlockChainStream_GetNextFreeBlock
5078 * Returns the index of a free small block. The small block depot will be
5079 * enlarged if necessary. The small block chain will also be enlarged if
5082 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
5083 SmallBlockChainStream
* This
)
5085 ULARGE_INTEGER offsetOfBlockInDepot
;
5088 ULONG blockIndex
= 0;
5089 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
5091 ULONG smallBlocksPerBigBlock
;
5093 offsetOfBlockInDepot
.u
.HighPart
= 0;
5096 * Scan the small block depot for a free block
5098 while (nextBlockIndex
!= BLOCK_UNUSED
)
5100 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5102 res
= BlockChainStream_ReadAt(
5103 This
->parentStorage
->smallBlockDepotChain
,
5104 offsetOfBlockInDepot
,
5110 * If we run out of space for the small block depot, enlarge it
5114 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
5116 if (nextBlockIndex
!= BLOCK_UNUSED
)
5122 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
5124 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
5125 ULONG nextBlock
, newsbdIndex
;
5126 BYTE smallBlockDepot
[BIG_BLOCK_SIZE
];
5128 nextBlock
= sbdIndex
;
5129 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
5131 sbdIndex
= nextBlock
;
5132 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
, &nextBlock
);
5135 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5136 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
5137 StorageImpl_SetNextBlockInChain(
5138 This
->parentStorage
,
5142 StorageImpl_SetNextBlockInChain(
5143 This
->parentStorage
,
5145 BLOCK_END_OF_CHAIN
);
5148 * Initialize all the small blocks to free
5150 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
5151 StorageImpl_WriteBigBlock(This
->parentStorage
, newsbdIndex
, smallBlockDepot
);
5156 * We have just created the small block depot.
5158 StgProperty rootProp
;
5162 * Save it in the header
5164 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
5165 StorageImpl_SaveFileHeader(This
->parentStorage
);
5168 * And allocate the first big block that will contain small blocks
5171 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5173 StorageImpl_SetNextBlockInChain(
5174 This
->parentStorage
,
5176 BLOCK_END_OF_CHAIN
);
5178 StorageImpl_ReadProperty(
5179 This
->parentStorage
,
5180 This
->parentStorage
->base
.rootPropertySetIndex
,
5183 rootProp
.startingBlock
= sbStartIndex
;
5184 rootProp
.size
.u
.HighPart
= 0;
5185 rootProp
.size
.u
.LowPart
= This
->parentStorage
->bigBlockSize
;
5187 StorageImpl_WriteProperty(
5188 This
->parentStorage
,
5189 This
->parentStorage
->base
.rootPropertySetIndex
,
5193 StorageImpl_SaveFileHeader(This
->parentStorage
);
5197 smallBlocksPerBigBlock
=
5198 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
5201 * Verify if we have to allocate big blocks to contain small blocks
5203 if (blockIndex
% smallBlocksPerBigBlock
== 0)
5205 StgProperty rootProp
;
5206 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
5208 StorageImpl_ReadProperty(
5209 This
->parentStorage
,
5210 This
->parentStorage
->base
.rootPropertySetIndex
,
5213 if (rootProp
.size
.u
.LowPart
<
5214 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
5216 rootProp
.size
.u
.LowPart
+= This
->parentStorage
->bigBlockSize
;
5218 BlockChainStream_SetSize(
5219 This
->parentStorage
->smallBlockRootChain
,
5222 StorageImpl_WriteProperty(
5223 This
->parentStorage
,
5224 This
->parentStorage
->base
.rootPropertySetIndex
,
5232 /******************************************************************************
5233 * SmallBlockChainStream_ReadAt
5235 * Reads a specified number of bytes from this chain at the specified offset.
5236 * bytesRead may be NULL.
5237 * Failure will be returned if the specified number of bytes has not been read.
5239 HRESULT
SmallBlockChainStream_ReadAt(
5240 SmallBlockChainStream
* This
,
5241 ULARGE_INTEGER offset
,
5247 ULARGE_INTEGER offsetInBigBlockFile
;
5248 ULONG blockNoInSequence
=
5249 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5251 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5252 ULONG bytesToReadInBuffer
;
5254 ULONG bytesReadFromBigBlockFile
;
5258 * This should never happen on a small block file.
5260 assert(offset
.u
.HighPart
==0);
5263 * Find the first block in the stream that contains part of the buffer.
5265 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5267 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5269 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5272 blockNoInSequence
--;
5276 * Start reading the buffer.
5279 bufferWalker
= buffer
;
5281 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5284 * Calculate how many bytes we can copy from this small block.
5286 bytesToReadInBuffer
=
5287 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5290 * Calculate the offset of the small block in the small block file.
5292 offsetInBigBlockFile
.u
.HighPart
= 0;
5293 offsetInBigBlockFile
.u
.LowPart
=
5294 blockIndex
* This
->parentStorage
->smallBlockSize
;
5296 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5299 * Read those bytes in the buffer from the small block file.
5300 * The small block has already been identified so it shouldn't fail
5301 * unless the file is corrupt.
5303 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
5304 offsetInBigBlockFile
,
5305 bytesToReadInBuffer
,
5307 &bytesReadFromBigBlockFile
);
5313 * Step to the next big block.
5315 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5317 return STG_E_DOCFILECORRUPT
;
5319 bufferWalker
+= bytesReadFromBigBlockFile
;
5320 size
-= bytesReadFromBigBlockFile
;
5321 *bytesRead
+= bytesReadFromBigBlockFile
;
5322 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5325 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5328 /******************************************************************************
5329 * SmallBlockChainStream_WriteAt
5331 * Writes the specified number of bytes to this chain at the specified offset.
5332 * bytesWritten may be NULL.
5333 * Will fail if not all specified number of bytes have been written.
5335 HRESULT
SmallBlockChainStream_WriteAt(
5336 SmallBlockChainStream
* This
,
5337 ULARGE_INTEGER offset
,
5340 ULONG
* bytesWritten
)
5342 ULARGE_INTEGER offsetInBigBlockFile
;
5343 ULONG blockNoInSequence
=
5344 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5346 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5347 ULONG bytesToWriteInBuffer
;
5349 ULONG bytesWrittenToBigBlockFile
;
5350 const BYTE
* bufferWalker
;
5354 * This should never happen on a small block file.
5356 assert(offset
.u
.HighPart
==0);
5359 * Find the first block in the stream that contains part of the buffer.
5361 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5363 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5365 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5366 return STG_E_DOCFILECORRUPT
;
5367 blockNoInSequence
--;
5371 * Start writing the buffer.
5373 * Here, I'm casting away the constness on the buffer variable
5374 * This is OK since we don't intend to modify that buffer.
5377 bufferWalker
= buffer
;
5378 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5381 * Calculate how many bytes we can copy to this small block.
5383 bytesToWriteInBuffer
=
5384 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5387 * Calculate the offset of the small block in the small block file.
5389 offsetInBigBlockFile
.u
.HighPart
= 0;
5390 offsetInBigBlockFile
.u
.LowPart
=
5391 blockIndex
* This
->parentStorage
->smallBlockSize
;
5393 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5396 * Write those bytes in the buffer to the small block file.
5398 res
= BlockChainStream_WriteAt(
5399 This
->parentStorage
->smallBlockRootChain
,
5400 offsetInBigBlockFile
,
5401 bytesToWriteInBuffer
,
5403 &bytesWrittenToBigBlockFile
);
5408 * Step to the next big block.
5410 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5413 bufferWalker
+= bytesWrittenToBigBlockFile
;
5414 size
-= bytesWrittenToBigBlockFile
;
5415 *bytesWritten
+= bytesWrittenToBigBlockFile
;
5416 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5419 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
5422 /******************************************************************************
5423 * SmallBlockChainStream_Shrink
5425 * Shrinks this chain in the small block depot.
5427 static BOOL
SmallBlockChainStream_Shrink(
5428 SmallBlockChainStream
* This
,
5429 ULARGE_INTEGER newSize
)
5431 ULONG blockIndex
, extraBlock
;
5435 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5437 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5440 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5443 * Go to the new end of chain
5445 while (count
< numBlocks
)
5447 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5454 * If the count is 0, we have a special case, the head of the chain was
5459 StgProperty chainProp
;
5461 StorageImpl_ReadProperty(This
->parentStorage
,
5462 This
->ownerPropertyIndex
,
5465 chainProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
5467 StorageImpl_WriteProperty(This
->parentStorage
,
5468 This
->ownerPropertyIndex
,
5472 * We start freeing the chain at the head block.
5474 extraBlock
= blockIndex
;
5478 /* Get the next block before marking the new end */
5479 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5483 /* Mark the new end of chain */
5484 SmallBlockChainStream_SetNextBlockInChain(
5487 BLOCK_END_OF_CHAIN
);
5491 * Mark the extra blocks as free
5493 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
5495 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
5498 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
5499 extraBlock
= blockIndex
;
5505 /******************************************************************************
5506 * SmallBlockChainStream_Enlarge
5508 * Grows this chain in the small block depot.
5510 static BOOL
SmallBlockChainStream_Enlarge(
5511 SmallBlockChainStream
* This
,
5512 ULARGE_INTEGER newSize
)
5514 ULONG blockIndex
, currentBlock
;
5516 ULONG oldNumBlocks
= 0;
5518 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5523 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5526 StgProperty chainProp
;
5528 StorageImpl_ReadProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
5531 chainProp
.startingBlock
= SmallBlockChainStream_GetNextFreeBlock(This
);
5533 StorageImpl_WriteProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
5536 blockIndex
= chainProp
.startingBlock
;
5537 SmallBlockChainStream_SetNextBlockInChain(
5540 BLOCK_END_OF_CHAIN
);
5543 currentBlock
= blockIndex
;
5546 * Figure out how many blocks are needed to contain this stream
5548 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5550 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5554 * Go to the current end of chain
5556 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5559 currentBlock
= blockIndex
;
5560 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
5565 * Add new blocks to the chain
5567 while (oldNumBlocks
< newNumBlocks
)
5569 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
5570 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
5572 SmallBlockChainStream_SetNextBlockInChain(
5575 BLOCK_END_OF_CHAIN
);
5577 currentBlock
= blockIndex
;
5584 /******************************************************************************
5585 * SmallBlockChainStream_SetSize
5587 * Sets the size of this stream.
5588 * The file will grow if we grow the chain.
5590 * TODO: Free the actual blocks in the file when we shrink the chain.
5591 * Currently, the blocks are still in the file. So the file size
5592 * doesn't shrink even if we shrink streams.
5594 BOOL
SmallBlockChainStream_SetSize(
5595 SmallBlockChainStream
* This
,
5596 ULARGE_INTEGER newSize
)
5598 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
5600 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
5603 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
5605 SmallBlockChainStream_Shrink(This
, newSize
);
5609 SmallBlockChainStream_Enlarge(This
, newSize
);
5615 /******************************************************************************
5616 * SmallBlockChainStream_GetSize
5618 * Returns the size of this chain.
5620 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
5622 StgProperty chainProperty
;
5624 StorageImpl_ReadProperty(
5625 This
->parentStorage
,
5626 This
->ownerPropertyIndex
,
5629 return chainProperty
.size
;
5632 /******************************************************************************
5633 * StgCreateDocfile [OLE32.@]
5634 * Creates a new compound file storage object
5637 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5638 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5639 * reserved [ ?] unused?, usually 0
5640 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5643 * S_OK if the file was successfully created
5644 * some STG_E_ value if error
5646 * if pwcsName is NULL, create file with new unique name
5647 * the function can returns
5648 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5651 HRESULT WINAPI
StgCreateDocfile(
5655 IStorage
**ppstgOpen
)
5657 StorageImpl
* newStorage
= 0;
5658 HANDLE hFile
= INVALID_HANDLE_VALUE
;
5659 HRESULT hr
= STG_E_INVALIDFLAG
;
5663 DWORD fileAttributes
;
5664 WCHAR tempFileName
[MAX_PATH
];
5666 TRACE("(%s, %x, %d, %p)\n",
5667 debugstr_w(pwcsName
), grfMode
,
5668 reserved
, ppstgOpen
);
5671 * Validate the parameters
5674 return STG_E_INVALIDPOINTER
;
5676 return STG_E_INVALIDPARAMETER
;
5678 /* if no share mode given then DENY_NONE is the default */
5679 if (STGM_SHARE_MODE(grfMode
) == 0)
5680 grfMode
|= STGM_SHARE_DENY_NONE
;
5683 * Validate the STGM flags
5685 if ( FAILED( validateSTGM(grfMode
) ))
5688 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5689 switch(STGM_ACCESS_MODE(grfMode
))
5692 case STGM_READWRITE
:
5698 /* in direct mode, can only use SHARE_EXCLUSIVE */
5699 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
5702 /* but in transacted mode, any share mode is valid */
5705 * Generate a unique name.
5709 WCHAR tempPath
[MAX_PATH
];
5710 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
5712 memset(tempPath
, 0, sizeof(tempPath
));
5713 memset(tempFileName
, 0, sizeof(tempFileName
));
5715 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
5718 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
5719 pwcsName
= tempFileName
;
5722 hr
= STG_E_INSUFFICIENTMEMORY
;
5726 creationMode
= TRUNCATE_EXISTING
;
5730 creationMode
= GetCreationModeFromSTGM(grfMode
);
5734 * Interpret the STGM value grfMode
5736 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
5737 accessMode
= GetAccessModeFromSTGM(grfMode
);
5739 if (grfMode
& STGM_DELETEONRELEASE
)
5740 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
5742 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
5744 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
5745 FIXME("Storage share mode not implemented.\n");
5747 if (grfMode
& STGM_TRANSACTED
)
5748 FIXME("Transacted mode not implemented.\n");
5751 * Initialize the "out" parameter.
5755 hFile
= CreateFileW(pwcsName
,
5763 if (hFile
== INVALID_HANDLE_VALUE
)
5765 if(GetLastError() == ERROR_FILE_EXISTS
)
5766 hr
= STG_E_FILEALREADYEXISTS
;
5773 * Allocate and initialize the new IStorage32object.
5775 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5777 if (newStorage
== 0)
5779 hr
= STG_E_INSUFFICIENTMEMORY
;
5783 hr
= StorageImpl_Construct(
5794 HeapFree(GetProcessHeap(), 0, newStorage
);
5799 * Get an "out" pointer for the caller.
5801 hr
= StorageBaseImpl_QueryInterface(
5802 (IStorage
*)newStorage
,
5803 (REFIID
)&IID_IStorage
,
5806 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
5811 /******************************************************************************
5812 * StgCreateStorageEx [OLE32.@]
5814 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
5816 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
5817 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
5819 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
5821 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5822 return STG_E_INVALIDPARAMETER
;
5825 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
5827 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5828 return STG_E_INVALIDPARAMETER
;
5831 if (stgfmt
== STGFMT_FILE
)
5833 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5834 return STG_E_INVALIDPARAMETER
;
5837 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
5839 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5840 return StgCreateDocfile(pwcsName
, grfMode
, 0, (IStorage
**)ppObjectOpen
);
5843 ERR("Invalid stgfmt argument\n");
5844 return STG_E_INVALIDPARAMETER
;
5847 /******************************************************************************
5848 * StgCreatePropSetStg [OLE32.@]
5850 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
5851 IPropertySetStorage
**ppPropSetStg
)
5855 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, ppPropSetStg
);
5857 hr
= STG_E_INVALIDPARAMETER
;
5859 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
5860 (void**)ppPropSetStg
);
5864 /******************************************************************************
5865 * StgOpenStorageEx [OLE32.@]
5867 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
5869 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
5870 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
5872 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
5874 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5875 return STG_E_INVALIDPARAMETER
;
5881 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5882 return STG_E_INVALIDPARAMETER
;
5884 case STGFMT_STORAGE
:
5887 case STGFMT_DOCFILE
:
5888 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
5890 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5891 return STG_E_INVALIDPARAMETER
;
5893 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5897 WARN("STGFMT_ANY assuming storage\n");
5901 return STG_E_INVALIDPARAMETER
;
5904 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
5908 /******************************************************************************
5909 * StgOpenStorage [OLE32.@]
5911 HRESULT WINAPI
StgOpenStorage(
5912 const OLECHAR
*pwcsName
,
5913 IStorage
*pstgPriority
,
5917 IStorage
**ppstgOpen
)
5919 StorageImpl
* newStorage
= 0;
5924 WCHAR fullname
[MAX_PATH
];
5926 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5927 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
5928 snbExclude
, reserved
, ppstgOpen
);
5931 * Perform sanity checks
5935 hr
= STG_E_INVALIDNAME
;
5941 hr
= STG_E_INVALIDPOINTER
;
5947 hr
= STG_E_INVALIDPARAMETER
;
5951 if (grfMode
& STGM_PRIORITY
)
5953 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
5954 return STG_E_INVALIDFLAG
;
5955 if (grfMode
& STGM_DELETEONRELEASE
)
5956 return STG_E_INVALIDFUNCTION
;
5957 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
5958 return STG_E_INVALIDFLAG
;
5959 grfMode
&= ~0xf0; /* remove the existing sharing mode */
5960 grfMode
|= STGM_SHARE_DENY_NONE
;
5962 /* STGM_PRIORITY stops other IStorage objects on the same file from
5963 * committing until the STGM_PRIORITY IStorage is closed. it also
5964 * stops non-transacted mode StgOpenStorage calls with write access from
5965 * succeeding. obviously, both of these cannot be achieved through just
5966 * file share flags */
5967 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5971 * Validate the sharing mode
5973 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
5974 switch(STGM_SHARE_MODE(grfMode
))
5976 case STGM_SHARE_EXCLUSIVE
:
5977 case STGM_SHARE_DENY_WRITE
:
5980 hr
= STG_E_INVALIDFLAG
;
5985 * Validate the STGM flags
5987 if ( FAILED( validateSTGM(grfMode
) ) ||
5988 (grfMode
&STGM_CREATE
))
5990 hr
= STG_E_INVALIDFLAG
;
5994 /* shared reading requires transacted mode */
5995 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
5996 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
5997 !(grfMode
&STGM_TRANSACTED
) )
5999 hr
= STG_E_INVALIDFLAG
;
6004 * Interpret the STGM value grfMode
6006 shareMode
= GetShareModeFromSTGM(grfMode
);
6007 accessMode
= GetAccessModeFromSTGM(grfMode
);
6010 * Initialize the "out" parameter.
6014 hFile
= CreateFileW( pwcsName
,
6019 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
6022 if (hFile
==INVALID_HANDLE_VALUE
)
6024 DWORD last_error
= GetLastError();
6030 case ERROR_FILE_NOT_FOUND
:
6031 hr
= STG_E_FILENOTFOUND
;
6034 case ERROR_PATH_NOT_FOUND
:
6035 hr
= STG_E_PATHNOTFOUND
;
6038 case ERROR_ACCESS_DENIED
:
6039 case ERROR_WRITE_PROTECT
:
6040 hr
= STG_E_ACCESSDENIED
;
6043 case ERROR_SHARING_VIOLATION
:
6044 hr
= STG_E_SHAREVIOLATION
;
6055 * Refuse to open the file if it's too small to be a structured storage file
6056 * FIXME: verify the file when reading instead of here
6058 if (GetFileSize(hFile
, NULL
) < 0x100)
6061 hr
= STG_E_FILEALREADYEXISTS
;
6066 * Allocate and initialize the new IStorage32object.
6068 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
6070 if (newStorage
== 0)
6072 hr
= STG_E_INSUFFICIENTMEMORY
;
6076 /* Initialize the storage */
6077 hr
= StorageImpl_Construct(
6088 HeapFree(GetProcessHeap(), 0, newStorage
);
6090 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6092 if(hr
== STG_E_INVALIDHEADER
)
6093 hr
= STG_E_FILEALREADYEXISTS
;
6097 /* prepare the file name string given in lieu of the root property name */
6098 GetFullPathNameW(pwcsName
, MAX_PATH
, fullname
, NULL
);
6099 memcpy(newStorage
->filename
, fullname
, PROPERTY_NAME_BUFFER_LEN
);
6100 newStorage
->filename
[PROPERTY_NAME_BUFFER_LEN
-1] = '\0';
6103 * Get an "out" pointer for the caller.
6105 hr
= StorageBaseImpl_QueryInterface(
6106 (IStorage
*)newStorage
,
6107 (REFIID
)&IID_IStorage
,
6111 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
6115 /******************************************************************************
6116 * StgCreateDocfileOnILockBytes [OLE32.@]
6118 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
6122 IStorage
** ppstgOpen
)
6124 StorageImpl
* newStorage
= 0;
6128 * Validate the parameters
6130 if ((ppstgOpen
== 0) || (plkbyt
== 0))
6131 return STG_E_INVALIDPOINTER
;
6134 * Allocate and initialize the new IStorage object.
6136 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
6138 if (newStorage
== 0)
6139 return STG_E_INSUFFICIENTMEMORY
;
6141 hr
= StorageImpl_Construct(
6152 HeapFree(GetProcessHeap(), 0, newStorage
);
6157 * Get an "out" pointer for the caller.
6159 hr
= StorageBaseImpl_QueryInterface(
6160 (IStorage
*)newStorage
,
6161 (REFIID
)&IID_IStorage
,
6167 /******************************************************************************
6168 * StgOpenStorageOnILockBytes [OLE32.@]
6170 HRESULT WINAPI
StgOpenStorageOnILockBytes(
6172 IStorage
*pstgPriority
,
6176 IStorage
**ppstgOpen
)
6178 StorageImpl
* newStorage
= 0;
6182 * Perform a sanity check
6184 if ((plkbyt
== 0) || (ppstgOpen
== 0))
6185 return STG_E_INVALIDPOINTER
;
6188 * Validate the STGM flags
6190 if ( FAILED( validateSTGM(grfMode
) ))
6191 return STG_E_INVALIDFLAG
;
6194 * Initialize the "out" parameter.
6199 * Allocate and initialize the new IStorage object.
6201 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
6203 if (newStorage
== 0)
6204 return STG_E_INSUFFICIENTMEMORY
;
6206 hr
= StorageImpl_Construct(
6217 HeapFree(GetProcessHeap(), 0, newStorage
);
6222 * Get an "out" pointer for the caller.
6224 hr
= StorageBaseImpl_QueryInterface(
6225 (IStorage
*)newStorage
,
6226 (REFIID
)&IID_IStorage
,
6232 /******************************************************************************
6233 * StgSetTimes [ole32.@]
6234 * StgSetTimes [OLE32.@]
6238 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
6239 FILETIME
const *patime
, FILETIME
const *pmtime
)
6241 IStorage
*stg
= NULL
;
6244 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
6246 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
6250 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
6251 IStorage_Release(stg
);
6257 /******************************************************************************
6258 * StgIsStorageILockBytes [OLE32.@]
6260 * Determines if the ILockBytes contains a storage object.
6262 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
6265 ULARGE_INTEGER offset
;
6267 offset
.u
.HighPart
= 0;
6268 offset
.u
.LowPart
= 0;
6270 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
6272 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
6278 /******************************************************************************
6279 * WriteClassStg [OLE32.@]
6281 * This method will store the specified CLSID in the specified storage object
6283 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
6288 return E_INVALIDARG
;
6291 return STG_E_INVALIDPOINTER
;
6293 hRes
= IStorage_SetClass(pStg
, rclsid
);
6298 /***********************************************************************
6299 * ReadClassStg (OLE32.@)
6301 * This method reads the CLSID previously written to a storage object with
6302 * the WriteClassStg.
6305 * pstg [I] IStorage pointer
6306 * pclsid [O] Pointer to where the CLSID is written
6310 * Failure: HRESULT code.
6312 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
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_NONAME
);
6328 *pclsid
=pstatstg
.clsid
;
6333 /***********************************************************************
6334 * OleLoadFromStream (OLE32.@)
6336 * This function loads an object from stream
6338 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
6342 LPPERSISTSTREAM xstm
;
6344 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
6346 res
=ReadClassStm(pStm
,&clsid
);
6349 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
6352 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
6354 IUnknown_Release((IUnknown
*)*ppvObj
);
6357 res
=IPersistStream_Load(xstm
,pStm
);
6358 IPersistStream_Release(xstm
);
6359 /* FIXME: all refcounts ok at this point? I think they should be:
6362 * xstm : 0 (released)
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
)
6379 TRACE("(%p,%p)\n",pPStm
,pStm
);
6381 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
6383 if (SUCCEEDED(res
)){
6385 res
=WriteClassStm(pStm
,&clsid
);
6389 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
6392 TRACE("Finished Save\n");
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
);
6441 case STGM_READWRITE
:
6449 case STGM_SHARE_DENY_NONE
:
6450 case STGM_SHARE_DENY_READ
:
6451 case STGM_SHARE_DENY_WRITE
:
6452 case STGM_SHARE_EXCLUSIVE
:
6461 case STGM_FAILIFTHERE
:
6468 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6470 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
6474 * STGM_CREATE | STGM_CONVERT
6475 * if both are false, STGM_FAILIFTHERE is set to TRUE
6477 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
6481 * STGM_NOSCRATCH requires STGM_TRANSACTED
6483 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
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
) )
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
:
6518 ERR("Invalid share mode!\n");
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
))
6534 return GENERIC_READ
;
6536 case STGM_READWRITE
:
6537 return GENERIC_READ
| GENERIC_WRITE
;
6539 ERR("Invalid access mode!\n");
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
))
6555 return CREATE_ALWAYS
;
6557 FIXME("STGM_CONVERT not implemented!\n");
6559 case STGM_FAILIFTHERE
:
6562 ERR("Invalid create mode!\n");
6568 /*************************************************************************
6569 * OLECONVERT_LoadOLE10 [Internal]
6571 * Loads the OLE10 STREAM to memory
6574 * pOleStream [I] The OLESTREAM
6575 * pData [I] Data Structure for the OLESTREAM Data
6579 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6580 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
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
)
6590 HRESULT hRes
= S_OK
;
6594 pData
->pData
= NULL
;
6595 pData
->pstrOleObjFileName
= NULL
;
6597 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
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
;
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
;
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
;
6638 if(pData
->dwOleTypeNameLength
> 0)
6640 /* Get the OleTypeName */
6641 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6642 if(dwSize
!= pData
->dwOleTypeNameLength
)
6644 hRes
= CONVERT10_E_OLESTREAM_GET
;
6650 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
6651 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
6653 hRes
= CONVERT10_E_OLESTREAM_GET
;
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
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
6663 if(dwSize
!= pData
->dwOleObjFileNameLength
)
6665 hRes
= CONVERT10_E_OLESTREAM_GET
;
6669 hRes
= CONVERT10_E_OLESTREAM_GET
;
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
;
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
;
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 this 8 byte 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
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
6706 if(dwSize
!= sizeof(pData
->strUnknown
))
6708 hRes
= CONVERT10_E_OLESTREAM_GET
;
6714 if(pData
->dwDataLength
> 0)
6716 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
6718 /* Get Data (ex. IStorage, Metafile, or BMP) */
6721 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
6722 if(dwSize
!= pData
->dwDataLength
)
6724 hRes
= CONVERT10_E_OLESTREAM_GET
;
6729 hRes
= CONVERT10_E_OLESTREAM_GET
;
6738 /*************************************************************************
6739 * OLECONVERT_SaveOLE10 [Internal]
6741 * Saves the OLE10 STREAM From memory
6744 * pData [I] Data Structure for the OLESTREAM Data
6745 * pOleStream [I] The OLESTREAM to save
6749 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6752 * This function is used by OleConvertIStorageToOLESTREAM only.
6755 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
6758 HRESULT hRes
= S_OK
;
6762 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6763 if(dwSize
!= sizeof(pData
->dwOleID
))
6765 hRes
= CONVERT10_E_OLESTREAM_PUT
;
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
;
6789 if(pData
->dwOleTypeNameLength
> 0)
6791 /* Set the OleTypeName */
6792 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6793 if(dwSize
!= pData
->dwOleTypeNameLength
)
6795 hRes
= CONVERT10_E_OLESTREAM_PUT
;
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
;
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
;
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
;
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
;
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
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
6866 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
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
);
6886 /* Open and copy temp storage to the Dest Storage */
6887 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
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
6904 * pStorage [I] The Src IStorage to copy
6905 * pData [I] The Dest Memory to write to.
6908 * The size in bytes allocated for pData
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
)
6920 DWORD nDataLength
= 0;
6921 IStorage
*pTempStorage
;
6922 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6923 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
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
);
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);
6947 DeleteFileW(wstrTempFile
);
6952 /*************************************************************************
6953 * OLECONVERT_CreateOleStream [Internal]
6955 * Creates the "\001OLE" stream in the IStorage if necessary.
6958 * pStorage [I] Dest storage to create the stream in
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
)
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
);
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
)
7004 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
7005 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
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
);
7017 /* read a string preceded by its length from a stream */
7018 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
7021 DWORD len
, count
= 0;
7025 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
7028 if( count
!= sizeof(len
) )
7029 return E_OUTOFMEMORY
;
7031 TRACE("%d bytes\n",len
);
7033 str
= CoTaskMemAlloc( len
);
7035 return E_OUTOFMEMORY
;
7037 r
= IStream_Read( stm
, str
, len
, &count
);
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
) );
7051 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
7052 CoTaskMemFree( str
);
7060 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
7061 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
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
);
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
);
7104 /***********************************************************************
7105 * WriteFmtUserTypeStg (OLE32.@)
7107 HRESULT WINAPI
WriteFmtUserTypeStg(
7108 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
7111 WCHAR szwClipName
[0x40];
7112 CLSID clsid
= CLSID_NULL
;
7113 LPWSTR wstrProgID
= NULL
;
7116 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
7118 /* get the clipboard format name */
7119 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[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
);
7143 /******************************************************************************
7144 * ReadFmtUserTypeStg [OLE32.@]
7146 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
7150 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
7151 unsigned char unknown1
[12];
7152 unsigned char unknown2
[16];
7154 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
7157 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
7159 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
7160 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
7163 WARN("Failed to open stream r = %08x\n", 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
) ) )
7171 r
= ReadClassStm( stm
, &clsid
);
7175 r
= STREAM_ReadString( stm
, &szCLSIDName
);
7179 r
= STREAM_ReadString( stm
, &szOleTypeName
);
7183 r
= STREAM_ReadString( stm
, &szProgIDName
);
7187 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
7188 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
7191 /* ok, success... now we just need to store what we found */
7193 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
7194 CoTaskMemFree( szOleTypeName
);
7196 if( lplpszUserType
)
7197 *lplpszUserType
= szCLSIDName
;
7198 CoTaskMemFree( szProgIDName
);
7201 IStream_Release( stm
);
7207 /*************************************************************************
7208 * OLECONVERT_CreateCompObjStream [Internal]
7210 * Creates a "\001CompObj" is the destination IStorage if necessary.
7213 * pStorage [I] The dest IStorage to create the CompObj Stream
7215 * strOleTypeName [I] The ProgID
7219 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
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
)
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
);
7262 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
7263 bufferW
, OLESTREAM_MAX_STR_LEN
);
7264 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
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
);
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
);
7312 /*************************************************************************
7313 * OLECONVERT_CreateOlePresStream[Internal]
7315 * Creates the "\002OlePres000" Stream with the Metafile data
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
7326 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7329 * This function is used by OleConvertOLESTREAMToIStorage only.
7332 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
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
);
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
);
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)
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
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
)
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
);
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
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
7448 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7451 * This function is used by OleConvertIStorageToOLESTREAM only.
7455 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
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
);
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
);
7484 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
7486 IStream_Release(pStream
);
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
);
7499 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
7506 /*************************************************************************
7507 * OLECONVERT_GetOle10PresData [Internal]
7509 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7512 * pStorage [I] Src IStroage
7513 * pOleStream [I] Dest OleStream Mem Struct
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
)
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
);
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
7567 * pStorage [I] Src IStroage
7568 * pOleStreamData [I] Dest OleStream Mem Struct
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
)
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
);
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)
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.@]
7655 * DVTARGETDEVICE parameter 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
,
7664 const DVTARGETDEVICE
* ptd
)
7668 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7670 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
7672 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7676 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7679 if(pstg
== NULL
|| pOleStream
== NULL
)
7681 hRes
= E_INVALIDARG
;
7686 /* Load the OLESTREAM to Memory */
7687 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
7692 /* Load the OLESTREAM to Memory (part 2)*/
7693 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
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
);
7709 /* It must be an original OLE 1.0 source */
7710 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
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
);
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
;
7739 /*************************************************************************
7740 * OleConvertIStorageToOLESTREAM [OLE32.@]
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 (
7754 LPOLESTREAM pOleStream
)
7757 HRESULT hRes
= S_OK
;
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
;
7772 /* Get the ProgID */
7773 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
7774 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
7778 /* Was it originally Ole10 */
7779 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7782 IStream_Release(pStream
);
7783 /* Get Presentation Data for Ole10Native */
7784 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
7788 /* Get Presentation Data (OLE20) */
7789 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
7792 /* Save OLESTREAM */
7793 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
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
);
7810 /***********************************************************************
7811 * GetConvertStg (OLE32.@)
7813 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
7814 FIXME("unimplemented stub!\n");
7818 /******************************************************************************
7819 * StgIsStorageFile [OLE32.@]
7820 * Verify if the file contains a storage object
7826 * S_OK if file has magic bytes as a storage object
7827 * S_FALSE if file is not storage
7830 StgIsStorageFile(LPCOLESTR fn
)
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");
7853 if (bytes_read
!= 8) {
7854 WARN(" too short\n");
7858 if (!memcmp(magic
,STORAGE_magic
,8)) {
7863 WARN(" -> Invalid header.\n");
7867 /***********************************************************************
7868 * WriteClassStm (OLE32.@)
7870 * Writes a CLSID to a stream.
7873 * pStm [I] Stream to write to.
7874 * rclsid [I] CLSID to write.
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.
7896 * pStm [I] Stream to read from.
7897 * rclsid [O] CLSID to read.
7901 * Failure: HRESULT code.
7903 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
7908 TRACE("(%p,%p)\n",pStm
,pclsid
);
7910 if (!pStm
|| !pclsid
)
7911 return E_INVALIDARG
;
7913 /* clear the output args */
7914 *pclsid
= CLSID_NULL
;
7916 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
7921 if (nbByte
!= sizeof(CLSID
))
7922 return STG_E_READFAULT
;