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
))
317 *ppvObject
= (IStorage
*)This
;
319 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
321 *ppvObject
= (IStorage
*)&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((IStorage
*)This
->ancestorStorage
, 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
;
983 * Check that we're compatible with the parent's storage mode
984 * if not in transacted mode
986 if(!(This
->ancestorStorage
->base
.openFlags
& STGM_TRANSACTED
)) {
987 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
988 return STG_E_ACCESSDENIED
;
992 * Initialize the out parameter
997 * Create a property enumeration to search the properties
999 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
1000 This
->rootPropertySetIndex
);
1002 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
1006 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1008 if (foundPropertyIndex
!= PROPERTY_NULL
)
1011 * An element with this name already exists
1013 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
1015 StgStreamImpl
*strm
;
1017 LIST_FOR_EACH_ENTRY(strm
, &This
->strmHead
, StgStreamImpl
, StrmListEntry
)
1019 if (strm
->ownerProperty
== foundPropertyIndex
)
1021 TRACE("Stream deleted %p\n", strm
);
1022 strm
->parentStorage
= NULL
;
1023 list_remove(&strm
->StrmListEntry
);
1026 IStorage_DestroyElement(iface
, pwcsName
);
1029 return STG_E_FILEALREADYEXISTS
;
1031 else if (STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1033 WARN("read-only storage\n");
1034 return STG_E_ACCESSDENIED
;
1038 * memset the empty property
1040 memset(&newStreamProperty
, 0, sizeof(StgProperty
));
1042 newStreamProperty
.sizeOfNameString
=
1043 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
1045 if (newStreamProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
1046 return STG_E_INVALIDNAME
;
1048 strcpyW(newStreamProperty
.name
, pwcsName
);
1050 newStreamProperty
.propertyType
= PROPTYPE_STREAM
;
1051 newStreamProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
1052 newStreamProperty
.size
.u
.LowPart
= 0;
1053 newStreamProperty
.size
.u
.HighPart
= 0;
1055 newStreamProperty
.previousProperty
= PROPERTY_NULL
;
1056 newStreamProperty
.nextProperty
= PROPERTY_NULL
;
1057 newStreamProperty
.dirProperty
= PROPERTY_NULL
;
1059 /* call CoFileTime to get the current time
1060 newStreamProperty.timeStampS1
1061 newStreamProperty.timeStampD1
1062 newStreamProperty.timeStampS2
1063 newStreamProperty.timeStampD2
1066 /* newStreamProperty.propertyUniqueID */
1069 * Get a free property or create a new one
1071 newPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
1074 * Save the new property into the new property spot
1076 StorageImpl_WriteProperty(
1077 This
->ancestorStorage
,
1079 &newStreamProperty
);
1082 * Find a spot in the property chain for our newly created property.
1084 updatePropertyChain(
1090 * Open the stream to return it.
1092 newStream
= StgStreamImpl_Construct(This
, grfMode
, newPropertyIndex
);
1096 *ppstm
= (IStream
*)newStream
;
1099 * Since we are returning a pointer to the interface, we have to nail down
1102 IStream_AddRef(*ppstm
);
1106 return STG_E_INSUFFICIENTMEMORY
;
1112 /************************************************************************
1113 * Storage32BaseImpl_SetClass (IStorage)
1115 * This method will write the specified CLSID in the property of this
1118 * See Windows documentation for more details on IStorage methods.
1120 static HRESULT WINAPI
StorageBaseImpl_SetClass(
1122 REFCLSID clsid
) /* [in] */
1124 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
1125 HRESULT hRes
= E_FAIL
;
1126 StgProperty curProperty
;
1129 TRACE("(%p, %p)\n", iface
, clsid
);
1131 success
= StorageImpl_ReadProperty(This
->ancestorStorage
,
1132 This
->rootPropertySetIndex
,
1136 curProperty
.propertyUniqueID
= *clsid
;
1138 success
= StorageImpl_WriteProperty(This
->ancestorStorage
,
1139 This
->rootPropertySetIndex
,
1148 /************************************************************************
1149 ** Storage32Impl implementation
1152 /************************************************************************
1153 * Storage32Impl_CreateStorage (IStorage)
1155 * This method will create the storage object within the provided storage.
1157 * See Windows documentation for more details on IStorage methods.
1159 static HRESULT WINAPI
StorageImpl_CreateStorage(
1161 const OLECHAR
*pwcsName
, /* [string][in] */
1162 DWORD grfMode
, /* [in] */
1163 DWORD reserved1
, /* [in] */
1164 DWORD reserved2
, /* [in] */
1165 IStorage
**ppstg
) /* [out] */
1167 StorageImpl
* const This
=(StorageImpl
*)iface
;
1169 IEnumSTATSTGImpl
*propertyEnumeration
;
1170 StgProperty currentProperty
;
1171 StgProperty newProperty
;
1172 ULONG foundPropertyIndex
;
1173 ULONG newPropertyIndex
;
1176 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1177 iface
, debugstr_w(pwcsName
), grfMode
,
1178 reserved1
, reserved2
, ppstg
);
1181 * Validate parameters
1184 return STG_E_INVALIDPOINTER
;
1187 return STG_E_INVALIDNAME
;
1190 * Initialize the out parameter
1195 * Validate the STGM flags
1197 if ( FAILED( validateSTGM(grfMode
) ) ||
1198 (grfMode
& STGM_DELETEONRELEASE
) )
1200 WARN("bad grfMode: 0x%x\n", grfMode
);
1201 return STG_E_INVALIDFLAG
;
1205 * Check that we're compatible with the parent's storage mode
1207 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->base
.openFlags
) )
1209 WARN("access denied\n");
1210 return STG_E_ACCESSDENIED
;
1214 * Create a property enumeration and search the properties
1216 propertyEnumeration
= IEnumSTATSTGImpl_Construct( This
->base
.ancestorStorage
,
1217 This
->base
.rootPropertySetIndex
);
1219 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
1222 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1224 if (foundPropertyIndex
!= PROPERTY_NULL
)
1227 * An element with this name already exists
1229 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
1230 IStorage_DestroyElement(iface
, pwcsName
);
1233 WARN("file already exists\n");
1234 return STG_E_FILEALREADYEXISTS
;
1237 else if (STGM_ACCESS_MODE(This
->base
.openFlags
) == STGM_READ
)
1239 WARN("read-only storage\n");
1240 return STG_E_ACCESSDENIED
;
1244 * memset the empty property
1246 memset(&newProperty
, 0, sizeof(StgProperty
));
1248 newProperty
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1250 if (newProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
1252 FIXME("name too long\n");
1253 return STG_E_INVALIDNAME
;
1256 strcpyW(newProperty
.name
, pwcsName
);
1258 newProperty
.propertyType
= PROPTYPE_STORAGE
;
1259 newProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
1260 newProperty
.size
.u
.LowPart
= 0;
1261 newProperty
.size
.u
.HighPart
= 0;
1263 newProperty
.previousProperty
= PROPERTY_NULL
;
1264 newProperty
.nextProperty
= PROPERTY_NULL
;
1265 newProperty
.dirProperty
= PROPERTY_NULL
;
1267 /* call CoFileTime to get the current time
1268 newProperty.timeStampS1
1269 newProperty.timeStampD1
1270 newProperty.timeStampS2
1271 newProperty.timeStampD2
1274 /* newStorageProperty.propertyUniqueID */
1277 * Obtain a free property in the property chain
1279 newPropertyIndex
= getFreeProperty(This
->base
.ancestorStorage
);
1282 * Save the new property into the new property spot
1284 StorageImpl_WriteProperty(
1285 This
->base
.ancestorStorage
,
1290 * Find a spot in the property chain for our newly created property.
1292 updatePropertyChain(
1298 * Open it to get a pointer to return.
1300 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1302 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1312 /***************************************************************************
1316 * Get a free property or create a new one.
1318 static ULONG
getFreeProperty(
1319 StorageImpl
*storage
)
1321 ULONG currentPropertyIndex
= 0;
1322 ULONG newPropertyIndex
= PROPERTY_NULL
;
1323 BOOL readSuccessful
= TRUE
;
1324 StgProperty currentProperty
;
1329 * Start by reading the root property
1331 readSuccessful
= StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1332 currentPropertyIndex
,
1336 if (currentProperty
.sizeOfNameString
== 0)
1339 * The property existis and is available, we found it.
1341 newPropertyIndex
= currentPropertyIndex
;
1347 * We exhausted the property list, we will create more space below
1349 newPropertyIndex
= currentPropertyIndex
;
1351 currentPropertyIndex
++;
1353 } while (newPropertyIndex
== PROPERTY_NULL
);
1356 * grow the property chain
1358 if (! readSuccessful
)
1360 StgProperty emptyProperty
;
1361 ULARGE_INTEGER newSize
;
1362 ULONG propertyIndex
;
1363 ULONG lastProperty
= 0;
1364 ULONG blockCount
= 0;
1367 * obtain the new count of property blocks
1369 blockCount
= BlockChainStream_GetCount(
1370 storage
->base
.ancestorStorage
->rootBlockChain
)+1;
1373 * initialize the size used by the property stream
1375 newSize
.u
.HighPart
= 0;
1376 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1379 * add a property block to the property chain
1381 BlockChainStream_SetSize(storage
->base
.ancestorStorage
->rootBlockChain
, newSize
);
1384 * memset the empty property in order to initialize the unused newly
1387 memset(&emptyProperty
, 0, sizeof(StgProperty
));
1392 lastProperty
= storage
->bigBlockSize
/ PROPSET_BLOCK_SIZE
* blockCount
;
1395 propertyIndex
= newPropertyIndex
;
1396 propertyIndex
< lastProperty
;
1399 StorageImpl_WriteProperty(
1400 storage
->base
.ancestorStorage
,
1406 return newPropertyIndex
;
1409 /****************************************************************************
1413 * Case insensitive comparison of StgProperty.name by first considering
1416 * Returns <0 when newProperty < currentProperty
1417 * >0 when newProperty > currentProperty
1418 * 0 when newProperty == currentProperty
1420 static LONG
propertyNameCmp(
1421 const OLECHAR
*newProperty
,
1422 const OLECHAR
*currentProperty
)
1424 LONG diff
= lstrlenW(newProperty
) - lstrlenW(currentProperty
);
1429 * We compare the string themselves only when they are of the same length
1431 diff
= lstrcmpiW( newProperty
, currentProperty
);
1437 /****************************************************************************
1441 * Properly link this new element in the property chain.
1443 static void updatePropertyChain(
1444 StorageImpl
*storage
,
1445 ULONG newPropertyIndex
,
1446 StgProperty newProperty
)
1448 StgProperty currentProperty
;
1451 * Read the root property
1453 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1454 storage
->base
.rootPropertySetIndex
,
1457 if (currentProperty
.dirProperty
!= PROPERTY_NULL
)
1460 * The root storage contains some element, therefore, start the research
1461 * for the appropriate location.
1464 ULONG current
, next
, previous
, currentPropertyId
;
1467 * Keep the StgProperty sequence number of the storage first property
1469 currentPropertyId
= currentProperty
.dirProperty
;
1474 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1475 currentProperty
.dirProperty
,
1478 previous
= currentProperty
.previousProperty
;
1479 next
= currentProperty
.nextProperty
;
1480 current
= currentPropertyId
;
1484 LONG diff
= propertyNameCmp( newProperty
.name
, currentProperty
.name
);
1488 if (previous
!= PROPERTY_NULL
)
1490 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1497 currentProperty
.previousProperty
= newPropertyIndex
;
1498 StorageImpl_WriteProperty(storage
->base
.ancestorStorage
,
1506 if (next
!= PROPERTY_NULL
)
1508 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1515 currentProperty
.nextProperty
= newPropertyIndex
;
1516 StorageImpl_WriteProperty(storage
->base
.ancestorStorage
,
1525 * Trying to insert an item with the same name in the
1526 * subtree structure.
1531 previous
= currentProperty
.previousProperty
;
1532 next
= currentProperty
.nextProperty
;
1538 * The root storage is empty, link the new property to its dir property
1540 currentProperty
.dirProperty
= newPropertyIndex
;
1541 StorageImpl_WriteProperty(storage
->base
.ancestorStorage
,
1542 storage
->base
.rootPropertySetIndex
,
1548 /*************************************************************************
1551 static HRESULT WINAPI
StorageImpl_CopyTo(
1553 DWORD ciidExclude
, /* [in] */
1554 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1555 SNB snbExclude
, /* [unique][in] */
1556 IStorage
* pstgDest
) /* [unique][in] */
1558 IEnumSTATSTG
*elements
= 0;
1559 STATSTG curElement
, strStat
;
1561 IStorage
*pstgTmp
, *pstgChild
;
1562 IStream
*pstrTmp
, *pstrChild
;
1564 if ((ciidExclude
!= 0) || (rgiidExclude
!= NULL
) || (snbExclude
!= NULL
))
1565 FIXME("Exclude option not implemented\n");
1567 TRACE("(%p, %d, %p, %p, %p)\n",
1568 iface
, ciidExclude
, rgiidExclude
,
1569 snbExclude
, pstgDest
);
1572 * Perform a sanity check
1574 if ( pstgDest
== 0 )
1575 return STG_E_INVALIDPOINTER
;
1578 * Enumerate the elements
1580 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1588 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1589 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1594 * Obtain the next element
1596 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1598 if ( hr
== S_FALSE
)
1600 hr
= S_OK
; /* done, every element has been copied */
1604 if (curElement
.type
== STGTY_STORAGE
)
1607 * open child source storage
1609 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1610 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1611 NULL
, 0, &pstgChild
);
1617 * Check if destination storage is not a child of the source
1618 * storage, which will cause an infinite loop
1620 if (pstgChild
== pstgDest
)
1622 IEnumSTATSTG_Release(elements
);
1624 return STG_E_ACCESSDENIED
;
1628 * create a new storage in destination storage
1630 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1631 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1635 * if it already exist, don't create a new one use this one
1637 if (hr
== STG_E_FILEALREADYEXISTS
)
1639 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1640 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1641 NULL
, 0, &pstgTmp
);
1649 * do the copy recursively
1651 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1652 snbExclude
, pstgTmp
);
1654 IStorage_Release( pstgTmp
);
1655 IStorage_Release( pstgChild
);
1657 else if (curElement
.type
== STGTY_STREAM
)
1660 * create a new stream in destination storage. If the stream already
1661 * exist, it will be deleted and a new one will be created.
1663 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1664 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1671 * open child stream storage
1673 hr
= IStorage_OpenStream( iface
, curElement
.pwcsName
, NULL
,
1674 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1681 * Get the size of the source stream
1683 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1686 * Set the size of the destination stream.
1688 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1693 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1696 IStream_Release( pstrTmp
);
1697 IStream_Release( pstrChild
);
1701 WARN("unknown element type: %d\n", curElement
.type
);
1704 } while (hr
== S_OK
);
1709 IEnumSTATSTG_Release(elements
);
1714 /*************************************************************************
1715 * MoveElementTo (IStorage)
1717 static HRESULT WINAPI
StorageImpl_MoveElementTo(
1719 const OLECHAR
*pwcsName
, /* [string][in] */
1720 IStorage
*pstgDest
, /* [unique][in] */
1721 const OLECHAR
*pwcsNewName
,/* [string][in] */
1722 DWORD grfFlags
) /* [in] */
1724 FIXME("(%p %s %p %s %u): stub\n", iface
,
1725 debugstr_w(pwcsName
), pstgDest
,
1726 debugstr_w(pwcsNewName
), grfFlags
);
1730 /*************************************************************************
1733 * Ensures that any changes made to a storage object open in transacted mode
1734 * are reflected in the parent storage
1737 * Wine doesn't implement transacted mode, which seems to be a basic
1738 * optimization, so we can ignore this stub for now.
1740 static HRESULT WINAPI
StorageImpl_Commit(
1742 DWORD grfCommitFlags
)/* [in] */
1744 FIXME("(%p %d): stub\n", iface
, grfCommitFlags
);
1748 /*************************************************************************
1751 * Discard all changes that have been made since the last commit operation
1753 static HRESULT WINAPI
StorageImpl_Revert(
1756 FIXME("(%p): stub\n", iface
);
1760 /*************************************************************************
1761 * DestroyElement (IStorage)
1763 * Strategy: This implementation is built this way for simplicity not for speed.
1764 * I always delete the topmost element of the enumeration and adjust
1765 * the deleted element pointer all the time. This takes longer to
1766 * do but allow to reinvoke DestroyElement whenever we encounter a
1767 * storage object. The optimisation resides in the usage of another
1768 * enumeration strategy that would give all the leaves of a storage
1769 * first. (postfix order)
1771 static HRESULT WINAPI
StorageImpl_DestroyElement(
1773 const OLECHAR
*pwcsName
)/* [string][in] */
1775 StorageImpl
* const This
=(StorageImpl
*)iface
;
1777 IEnumSTATSTGImpl
* propertyEnumeration
;
1780 StgProperty propertyToDelete
;
1781 StgProperty parentProperty
;
1782 ULONG foundPropertyIndexToDelete
;
1783 ULONG typeOfRelation
;
1784 ULONG parentPropertyId
= 0;
1787 iface
, debugstr_w(pwcsName
));
1790 * Perform a sanity check on the parameters.
1793 return STG_E_INVALIDPOINTER
;
1796 * Create a property enumeration to search the property with the given name
1798 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
1799 This
->base
.ancestorStorage
,
1800 This
->base
.rootPropertySetIndex
);
1802 foundPropertyIndexToDelete
= IEnumSTATSTGImpl_FindProperty(
1803 propertyEnumeration
,
1807 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1809 if ( foundPropertyIndexToDelete
== PROPERTY_NULL
)
1811 return STG_E_FILENOTFOUND
;
1815 * Find the parent property of the property to delete (the one that
1816 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1817 * the parent is This. Otherwise, the parent is one of its sibling...
1821 * First, read This's StgProperty..
1823 res
= StorageImpl_ReadProperty(
1824 This
->base
.ancestorStorage
,
1825 This
->base
.rootPropertySetIndex
,
1831 * Second, check to see if by any chance the actual storage (This) is not
1832 * the parent of the property to delete... We never know...
1834 if ( parentProperty
.dirProperty
== foundPropertyIndexToDelete
)
1837 * Set data as it would have been done in the else part...
1839 typeOfRelation
= PROPERTY_RELATION_DIR
;
1840 parentPropertyId
= This
->base
.rootPropertySetIndex
;
1845 * Create a property enumeration to search the parent properties, and
1846 * delete it once done.
1848 IEnumSTATSTGImpl
* propertyEnumeration2
;
1850 propertyEnumeration2
= IEnumSTATSTGImpl_Construct(
1851 This
->base
.ancestorStorage
,
1852 This
->base
.rootPropertySetIndex
);
1854 typeOfRelation
= IEnumSTATSTGImpl_FindParentProperty(
1855 propertyEnumeration2
,
1856 foundPropertyIndexToDelete
,
1860 IEnumSTATSTGImpl_Destroy(propertyEnumeration2
);
1863 if ( propertyToDelete
.propertyType
== PROPTYPE_STORAGE
)
1865 hr
= deleteStorageProperty(
1867 foundPropertyIndexToDelete
,
1870 else if ( propertyToDelete
.propertyType
== PROPTYPE_STREAM
)
1872 hr
= deleteStreamProperty(
1874 foundPropertyIndexToDelete
,
1882 * Adjust the property chain
1884 hr
= adjustPropertyChain(
1895 /************************************************************************
1896 * StorageImpl_Stat (IStorage)
1898 * This method will retrieve information about this storage object.
1900 * See Windows documentation for more details on IStorage methods.
1902 static HRESULT WINAPI
StorageImpl_Stat( IStorage
* iface
,
1903 STATSTG
* pstatstg
, /* [out] */
1904 DWORD grfStatFlag
) /* [in] */
1906 StorageImpl
* const This
= (StorageImpl
*)iface
;
1907 HRESULT result
= StorageBaseImpl_Stat( iface
, pstatstg
, grfStatFlag
);
1909 if ( !FAILED(result
) && ((grfStatFlag
& STATFLAG_NONAME
) == 0) && This
->pwcsName
)
1911 CoTaskMemFree(pstatstg
->pwcsName
);
1912 pstatstg
->pwcsName
= CoTaskMemAlloc((lstrlenW(This
->pwcsName
)+1)*sizeof(WCHAR
));
1913 strcpyW(pstatstg
->pwcsName
, This
->pwcsName
);
1919 /******************************************************************************
1920 * Internal stream list handlers
1923 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1925 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1926 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1929 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1931 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1932 list_remove(&(strm
->StrmListEntry
));
1935 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
1937 struct list
*cur
, *cur2
;
1938 StgStreamImpl
*strm
=NULL
;
1940 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
1941 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
1942 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
1943 strm
->parentStorage
= NULL
;
1949 /*********************************************************************
1953 * Perform the deletion of a complete storage node
1956 static HRESULT
deleteStorageProperty(
1957 StorageImpl
*parentStorage
,
1958 ULONG indexOfPropertyToDelete
,
1959 StgProperty propertyToDelete
)
1961 IEnumSTATSTG
*elements
= 0;
1962 IStorage
*childStorage
= 0;
1963 STATSTG currentElement
;
1965 HRESULT destroyHr
= S_OK
;
1968 * Open the storage and enumerate it
1970 hr
= StorageBaseImpl_OpenStorage(
1971 (IStorage
*)parentStorage
,
1972 propertyToDelete
.name
,
1974 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
1985 * Enumerate the elements
1987 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
1992 * Obtain the next element
1994 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
1997 destroyHr
= StorageImpl_DestroyElement(childStorage
, currentElement
.pwcsName
);
1999 CoTaskMemFree(currentElement
.pwcsName
);
2003 * We need to Reset the enumeration every time because we delete elements
2004 * and the enumeration could be invalid
2006 IEnumSTATSTG_Reset(elements
);
2008 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2011 * Invalidate the property by zeroing its name member.
2013 propertyToDelete
.sizeOfNameString
= 0;
2015 StorageImpl_WriteProperty(parentStorage
->base
.ancestorStorage
,
2016 indexOfPropertyToDelete
,
2019 IStorage_Release(childStorage
);
2020 IEnumSTATSTG_Release(elements
);
2025 /*********************************************************************
2029 * Perform the deletion of a stream node
2032 static HRESULT
deleteStreamProperty(
2033 StorageImpl
*parentStorage
,
2034 ULONG indexOfPropertyToDelete
,
2035 StgProperty propertyToDelete
)
2039 ULARGE_INTEGER size
;
2041 size
.u
.HighPart
= 0;
2044 hr
= StorageBaseImpl_OpenStream(
2045 (IStorage
*)parentStorage
,
2046 (OLECHAR
*)propertyToDelete
.name
,
2048 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2060 hr
= IStream_SetSize(pis
, size
);
2068 * Release the stream object.
2070 IStream_Release(pis
);
2073 * Invalidate the property by zeroing its name member.
2075 propertyToDelete
.sizeOfNameString
= 0;
2078 * Here we should re-read the property so we get the updated pointer
2079 * but since we are here to zap it, I don't do it...
2081 StorageImpl_WriteProperty(
2082 parentStorage
->base
.ancestorStorage
,
2083 indexOfPropertyToDelete
,
2089 /*********************************************************************
2093 * Finds a placeholder for the StgProperty within the Storage
2096 static HRESULT
findPlaceholder(
2097 StorageImpl
*storage
,
2098 ULONG propertyIndexToStore
,
2099 ULONG storePropertyIndex
,
2102 StgProperty storeProperty
;
2106 * Read the storage property
2108 res
= StorageImpl_ReadProperty(
2109 storage
->base
.ancestorStorage
,
2118 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
2120 if (storeProperty
.previousProperty
!= PROPERTY_NULL
)
2122 return findPlaceholder(
2124 propertyIndexToStore
,
2125 storeProperty
.previousProperty
,
2130 storeProperty
.previousProperty
= propertyIndexToStore
;
2133 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
2135 if (storeProperty
.nextProperty
!= PROPERTY_NULL
)
2137 return findPlaceholder(
2139 propertyIndexToStore
,
2140 storeProperty
.nextProperty
,
2145 storeProperty
.nextProperty
= propertyIndexToStore
;
2148 else if (typeOfRelation
== PROPERTY_RELATION_DIR
)
2150 if (storeProperty
.dirProperty
!= PROPERTY_NULL
)
2152 return findPlaceholder(
2154 propertyIndexToStore
,
2155 storeProperty
.dirProperty
,
2160 storeProperty
.dirProperty
= propertyIndexToStore
;
2164 res
= StorageImpl_WriteProperty(
2165 storage
->base
.ancestorStorage
,
2177 /*************************************************************************
2181 * This method takes the previous and the next property link of a property
2182 * to be deleted and find them a place in the Storage.
2184 static HRESULT
adjustPropertyChain(
2186 StgProperty propertyToDelete
,
2187 StgProperty parentProperty
,
2188 ULONG parentPropertyId
,
2191 ULONG newLinkProperty
= PROPERTY_NULL
;
2192 BOOL needToFindAPlaceholder
= FALSE
;
2193 ULONG storeNode
= PROPERTY_NULL
;
2194 ULONG toStoreNode
= PROPERTY_NULL
;
2195 INT relationType
= 0;
2199 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
2201 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2204 * Set the parent previous to the property to delete previous
2206 newLinkProperty
= propertyToDelete
.previousProperty
;
2208 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2211 * We also need to find a storage for the other link, setup variables
2212 * to do this at the end...
2214 needToFindAPlaceholder
= TRUE
;
2215 storeNode
= propertyToDelete
.previousProperty
;
2216 toStoreNode
= propertyToDelete
.nextProperty
;
2217 relationType
= PROPERTY_RELATION_NEXT
;
2220 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2223 * Set the parent previous to the property to delete next
2225 newLinkProperty
= propertyToDelete
.nextProperty
;
2229 * Link it for real...
2231 parentProperty
.previousProperty
= newLinkProperty
;
2234 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
2236 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2239 * Set the parent next to the property to delete next previous
2241 newLinkProperty
= propertyToDelete
.previousProperty
;
2243 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2246 * We also need to find a storage for the other link, setup variables
2247 * to do this at the end...
2249 needToFindAPlaceholder
= TRUE
;
2250 storeNode
= propertyToDelete
.previousProperty
;
2251 toStoreNode
= propertyToDelete
.nextProperty
;
2252 relationType
= PROPERTY_RELATION_NEXT
;
2255 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2258 * Set the parent next to the property to delete next
2260 newLinkProperty
= propertyToDelete
.nextProperty
;
2264 * Link it for real...
2266 parentProperty
.nextProperty
= newLinkProperty
;
2268 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2270 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2273 * Set the parent dir to the property to delete previous
2275 newLinkProperty
= propertyToDelete
.previousProperty
;
2277 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2280 * We also need to find a storage for the other link, setup variables
2281 * to do this at the end...
2283 needToFindAPlaceholder
= TRUE
;
2284 storeNode
= propertyToDelete
.previousProperty
;
2285 toStoreNode
= propertyToDelete
.nextProperty
;
2286 relationType
= PROPERTY_RELATION_NEXT
;
2289 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2292 * Set the parent dir to the property to delete next
2294 newLinkProperty
= propertyToDelete
.nextProperty
;
2298 * Link it for real...
2300 parentProperty
.dirProperty
= newLinkProperty
;
2304 * Write back the parent property
2306 res
= StorageImpl_WriteProperty(
2307 This
->base
.ancestorStorage
,
2316 * If a placeholder is required for the other link, then, find one and
2317 * get out of here...
2319 if (needToFindAPlaceholder
)
2321 hr
= findPlaceholder(
2332 /******************************************************************************
2333 * SetElementTimes (IStorage)
2335 static HRESULT WINAPI
StorageImpl_SetElementTimes(
2337 const OLECHAR
*pwcsName
,/* [string][in] */
2338 const FILETIME
*pctime
, /* [in] */
2339 const FILETIME
*patime
, /* [in] */
2340 const FILETIME
*pmtime
) /* [in] */
2342 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2346 /******************************************************************************
2347 * SetStateBits (IStorage)
2349 static HRESULT WINAPI
StorageImpl_SetStateBits(
2351 DWORD grfStateBits
,/* [in] */
2352 DWORD grfMask
) /* [in] */
2354 StorageImpl
* const This
= (StorageImpl
*)iface
;
2355 This
->base
.stateBits
= (This
->base
.stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2360 * Virtual function table for the IStorage32Impl class.
2362 static const IStorageVtbl Storage32Impl_Vtbl
=
2364 StorageBaseImpl_QueryInterface
,
2365 StorageBaseImpl_AddRef
,
2366 StorageBaseImpl_Release
,
2367 StorageBaseImpl_CreateStream
,
2368 StorageBaseImpl_OpenStream
,
2369 StorageImpl_CreateStorage
,
2370 StorageBaseImpl_OpenStorage
,
2372 StorageImpl_MoveElementTo
,
2375 StorageBaseImpl_EnumElements
,
2376 StorageImpl_DestroyElement
,
2377 StorageBaseImpl_RenameElement
,
2378 StorageImpl_SetElementTimes
,
2379 StorageBaseImpl_SetClass
,
2380 StorageImpl_SetStateBits
,
2384 static HRESULT
StorageImpl_Construct(
2394 StgProperty currentProperty
;
2395 BOOL readSuccessful
;
2396 ULONG currentPropertyIndex
;
2398 if ( FAILED( validateSTGM(openFlags
) ))
2399 return STG_E_INVALIDFLAG
;
2401 memset(This
, 0, sizeof(StorageImpl
));
2404 * Initialize stream list
2407 list_init(&This
->base
.strmHead
);
2410 * Initialize the virtual function table.
2412 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2413 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2414 This
->base
.v_destructor
= StorageImpl_Destroy
;
2415 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2418 * This is the top-level storage so initialize the ancestor pointer
2421 This
->base
.ancestorStorage
= This
;
2424 * Initialize the physical support of the storage.
2426 This
->hFile
= hFile
;
2429 * Store copy of file path.
2432 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2433 (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
));
2434 if (!This
->pwcsName
)
2435 return STG_E_INSUFFICIENTMEMORY
;
2436 strcpyW(This
->pwcsName
, pwcsName
);
2440 * Initialize the big block cache.
2442 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
2443 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2444 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2450 if (This
->bigBlockFile
== 0)
2455 ULARGE_INTEGER size
;
2456 BYTE bigBlockBuffer
[BIG_BLOCK_SIZE
];
2459 * Initialize all header variables:
2460 * - The big block depot consists of one block and it is at block 0
2461 * - The properties start at block 1
2462 * - There is no small block depot
2464 memset( This
->bigBlockDepotStart
,
2466 sizeof(This
->bigBlockDepotStart
));
2468 This
->bigBlockDepotCount
= 1;
2469 This
->bigBlockDepotStart
[0] = 0;
2470 This
->rootStartBlock
= 1;
2471 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2472 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
2473 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2474 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2475 This
->extBigBlockDepotCount
= 0;
2477 StorageImpl_SaveFileHeader(This
);
2480 * Add one block for the big block depot and one block for the properties
2482 size
.u
.HighPart
= 0;
2483 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2484 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2487 * Initialize the big block depot
2489 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2490 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2491 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2492 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2497 * Load the header for the file.
2499 hr
= StorageImpl_LoadFileHeader(This
);
2503 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2510 * There is no block depot cached yet.
2512 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2515 * Start searching for free blocks with block 0.
2517 This
->prevFreeBlock
= 0;
2520 * Create the block chain abstractions.
2522 if(!(This
->rootBlockChain
=
2523 BlockChainStream_Construct(This
, &This
->rootStartBlock
, PROPERTY_NULL
)))
2524 return STG_E_READFAULT
;
2526 if(!(This
->smallBlockDepotChain
=
2527 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2529 return STG_E_READFAULT
;
2532 * Write the root property (memory only)
2536 StgProperty rootProp
;
2538 * Initialize the property chain
2540 memset(&rootProp
, 0, sizeof(rootProp
));
2541 MultiByteToWideChar( CP_ACP
, 0, rootPropertyName
, -1, rootProp
.name
,
2542 sizeof(rootProp
.name
)/sizeof(WCHAR
) );
2543 rootProp
.sizeOfNameString
= (strlenW(rootProp
.name
)+1) * sizeof(WCHAR
);
2544 rootProp
.propertyType
= PROPTYPE_ROOT
;
2545 rootProp
.previousProperty
= PROPERTY_NULL
;
2546 rootProp
.nextProperty
= PROPERTY_NULL
;
2547 rootProp
.dirProperty
= PROPERTY_NULL
;
2548 rootProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
2549 rootProp
.size
.u
.HighPart
= 0;
2550 rootProp
.size
.u
.LowPart
= 0;
2552 StorageImpl_WriteProperty(This
, 0, &rootProp
);
2556 * Find the ID of the root in the property sets.
2558 currentPropertyIndex
= 0;
2562 readSuccessful
= StorageImpl_ReadProperty(
2564 currentPropertyIndex
,
2569 if ( (currentProperty
.sizeOfNameString
!= 0 ) &&
2570 (currentProperty
.propertyType
== PROPTYPE_ROOT
) )
2572 This
->base
.rootPropertySetIndex
= currentPropertyIndex
;
2576 currentPropertyIndex
++;
2578 } while (readSuccessful
&& (This
->base
.rootPropertySetIndex
== PROPERTY_NULL
) );
2580 if (!readSuccessful
)
2583 return STG_E_READFAULT
;
2587 * Create the block chain abstraction for the small block root chain.
2589 if(!(This
->smallBlockRootChain
=
2590 BlockChainStream_Construct(This
, NULL
, This
->base
.rootPropertySetIndex
)))
2591 return STG_E_READFAULT
;
2596 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2598 StorageImpl
*This
= (StorageImpl
*) iface
;
2599 TRACE("(%p)\n", This
);
2601 StorageBaseImpl_DeleteAll(&This
->base
);
2603 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2605 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2606 BlockChainStream_Destroy(This
->rootBlockChain
);
2607 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2609 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2610 HeapFree(GetProcessHeap(), 0, This
);
2613 /******************************************************************************
2614 * Storage32Impl_GetNextFreeBigBlock
2616 * Returns the index of the next free big block.
2617 * If the big block depot is filled, this method will enlarge it.
2620 static ULONG
StorageImpl_GetNextFreeBigBlock(
2623 ULONG depotBlockIndexPos
;
2624 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2626 ULONG depotBlockOffset
;
2627 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2628 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2630 ULONG freeBlock
= BLOCK_UNUSED
;
2632 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2633 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2636 * Scan the entire big block depot until we find a block marked free
2638 while (nextBlockIndex
!= BLOCK_UNUSED
)
2640 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2642 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2645 * Grow the primary depot.
2647 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2649 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2652 * Add a block depot.
2654 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2655 This
->bigBlockDepotCount
++;
2656 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2659 * Flag it as a block depot.
2661 StorageImpl_SetNextBlockInChain(This
,
2665 /* Save new header information.
2667 StorageImpl_SaveFileHeader(This
);
2672 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2674 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2677 * Grow the extended depot.
2679 ULONG extIndex
= BLOCK_UNUSED
;
2680 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2681 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2683 if (extBlockOffset
== 0)
2685 /* We need an extended block.
2687 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2688 This
->extBigBlockDepotCount
++;
2689 depotBlockIndexPos
= extIndex
+ 1;
2692 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2695 * Add a block depot and mark it in the extended block.
2697 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2698 This
->bigBlockDepotCount
++;
2699 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2701 /* Flag the block depot.
2703 StorageImpl_SetNextBlockInChain(This
,
2707 /* If necessary, flag the extended depot block.
2709 if (extIndex
!= BLOCK_UNUSED
)
2710 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2712 /* Save header information.
2714 StorageImpl_SaveFileHeader(This
);
2718 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2722 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2723 ( nextBlockIndex
!= BLOCK_UNUSED
))
2725 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2727 if (nextBlockIndex
== BLOCK_UNUSED
)
2729 freeBlock
= (depotIndex
* blocksPerDepot
) +
2730 (depotBlockOffset
/sizeof(ULONG
));
2733 depotBlockOffset
+= sizeof(ULONG
);
2738 depotBlockOffset
= 0;
2742 * make sure that the block physically exists before using it
2744 BIGBLOCKFILE_EnsureExists(This
->bigBlockFile
, freeBlock
);
2746 This
->prevFreeBlock
= freeBlock
;
2751 /******************************************************************************
2752 * Storage32Impl_AddBlockDepot
2754 * This will create a depot block, essentially it is a block initialized
2757 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2759 BYTE blockBuffer
[BIG_BLOCK_SIZE
];
2762 * Initialize blocks as free
2764 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2765 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
2768 /******************************************************************************
2769 * Storage32Impl_GetExtDepotBlock
2771 * Returns the index of the block that corresponds to the specified depot
2772 * index. This method is only for depot indexes equal or greater than
2773 * COUNT_BBDEPOTINHEADER.
2775 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2777 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2778 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2779 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2780 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2781 ULONG blockIndex
= BLOCK_UNUSED
;
2782 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2784 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2786 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2787 return BLOCK_UNUSED
;
2789 while (extBlockCount
> 0)
2791 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2795 if (extBlockIndex
!= BLOCK_UNUSED
)
2796 StorageImpl_ReadDWordFromBigBlock(This
, extBlockIndex
,
2797 extBlockOffset
* sizeof(ULONG
), &blockIndex
);
2802 /******************************************************************************
2803 * Storage32Impl_SetExtDepotBlock
2805 * Associates the specified block index to the specified depot index.
2806 * This method is only for depot indexes equal or greater than
2807 * COUNT_BBDEPOTINHEADER.
2809 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
2811 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2812 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2813 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2814 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2815 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2817 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2819 while (extBlockCount
> 0)
2821 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2825 if (extBlockIndex
!= BLOCK_UNUSED
)
2827 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
2828 extBlockOffset
* sizeof(ULONG
),
2833 /******************************************************************************
2834 * Storage32Impl_AddExtBlockDepot
2836 * Creates an extended depot block.
2838 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
2840 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
2841 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
2842 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2843 ULONG index
= BLOCK_UNUSED
;
2844 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2845 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
2846 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
2848 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
2849 blocksPerDepotBlock
;
2851 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
2854 * The first extended block.
2856 This
->extBigBlockDepotStart
= index
;
2862 * Follow the chain to the last one.
2864 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
2866 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
2870 * Add the new extended block to the chain.
2872 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
2877 * Initialize this block.
2879 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2880 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
2885 /******************************************************************************
2886 * Storage32Impl_FreeBigBlock
2888 * This method will flag the specified block as free in the big block depot.
2890 static void StorageImpl_FreeBigBlock(
2894 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
2896 if (blockIndex
< This
->prevFreeBlock
)
2897 This
->prevFreeBlock
= blockIndex
;
2900 /************************************************************************
2901 * Storage32Impl_GetNextBlockInChain
2903 * This method will retrieve the block index of the next big block in
2906 * Params: This - Pointer to the Storage object.
2907 * blockIndex - Index of the block to retrieve the chain
2909 * nextBlockIndex - receives the return value.
2911 * Returns: This method returns the index of the next block in the chain.
2912 * It will return the constants:
2913 * BLOCK_SPECIAL - If the block given was not part of a
2915 * BLOCK_END_OF_CHAIN - If the block given was the last in
2917 * BLOCK_UNUSED - If the block given was not past of a chain
2919 * BLOCK_EXTBBDEPOT - This block is part of the extended
2922 * See Windows documentation for more details on IStorage methods.
2924 static HRESULT
StorageImpl_GetNextBlockInChain(
2927 ULONG
* nextBlockIndex
)
2929 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2930 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2931 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2932 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2934 ULONG depotBlockIndexPos
;
2937 *nextBlockIndex
= BLOCK_SPECIAL
;
2939 if(depotBlockCount
>= This
->bigBlockDepotCount
)
2941 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
2942 This
->bigBlockDepotCount
);
2943 return STG_E_READFAULT
;
2947 * Cache the currently accessed depot block.
2949 if (depotBlockCount
!= This
->indexBlockDepotCached
)
2951 This
->indexBlockDepotCached
= depotBlockCount
;
2953 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2955 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2960 * We have to look in the extended depot.
2962 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2965 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2968 return STG_E_READFAULT
;
2970 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
2972 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
2973 This
->blockDepotCached
[index
] = *nextBlockIndex
;
2977 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
2982 /******************************************************************************
2983 * Storage32Impl_GetNextExtendedBlock
2985 * Given an extended block this method will return the next extended block.
2988 * The last ULONG of an extended block is the block index of the next
2989 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2993 * - The index of the next extended block
2994 * - BLOCK_UNUSED: there is no next extended block.
2995 * - Any other return values denotes failure.
2997 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
2999 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3000 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3002 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3005 return nextBlockIndex
;
3008 /******************************************************************************
3009 * Storage32Impl_SetNextBlockInChain
3011 * This method will write the index of the specified block's next block
3012 * in the big block depot.
3014 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3017 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3018 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3019 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3022 static void StorageImpl_SetNextBlockInChain(
3027 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3028 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3029 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3030 ULONG depotBlockIndexPos
;
3032 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3033 assert(blockIndex
!= nextBlock
);
3035 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3037 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3042 * We have to look in the extended depot.
3044 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3047 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3050 * Update the cached block depot, if necessary.
3052 if (depotBlockCount
== This
->indexBlockDepotCached
)
3054 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3058 /******************************************************************************
3059 * Storage32Impl_LoadFileHeader
3061 * This method will read in the file header, i.e. big block index -1.
3063 static HRESULT
StorageImpl_LoadFileHeader(
3066 HRESULT hr
= STG_E_FILENOTFOUND
;
3067 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3073 * Get a pointer to the big block of data containing the header.
3075 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3078 * Extract the information from the header.
3083 * Check for the "magic number" signature and return an error if it is not
3086 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3088 return STG_E_OLDFORMAT
;
3091 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3093 return STG_E_INVALIDHEADER
;
3096 StorageUtl_ReadWord(
3098 OFFSET_BIGBLOCKSIZEBITS
,
3099 &This
->bigBlockSizeBits
);
3101 StorageUtl_ReadWord(
3103 OFFSET_SMALLBLOCKSIZEBITS
,
3104 &This
->smallBlockSizeBits
);
3106 StorageUtl_ReadDWord(
3108 OFFSET_BBDEPOTCOUNT
,
3109 &This
->bigBlockDepotCount
);
3111 StorageUtl_ReadDWord(
3113 OFFSET_ROOTSTARTBLOCK
,
3114 &This
->rootStartBlock
);
3116 StorageUtl_ReadDWord(
3118 OFFSET_SBDEPOTSTART
,
3119 &This
->smallBlockDepotStart
);
3121 StorageUtl_ReadDWord(
3123 OFFSET_EXTBBDEPOTSTART
,
3124 &This
->extBigBlockDepotStart
);
3126 StorageUtl_ReadDWord(
3128 OFFSET_EXTBBDEPOTCOUNT
,
3129 &This
->extBigBlockDepotCount
);
3131 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3133 StorageUtl_ReadDWord(
3135 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3136 &(This
->bigBlockDepotStart
[index
]));
3140 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3142 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3143 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3146 * Right now, the code is making some assumptions about the size of the
3147 * blocks, just make sure they are what we're expecting.
3149 if (This
->bigBlockSize
!= DEF_BIG_BLOCK_SIZE
||
3150 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
)
3152 WARN("Broken OLE storage file\n");
3153 hr
= STG_E_INVALIDHEADER
;
3162 /******************************************************************************
3163 * Storage32Impl_SaveFileHeader
3165 * This method will save to the file the header, i.e. big block -1.
3167 static void StorageImpl_SaveFileHeader(
3170 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3175 * Get a pointer to the big block of data containing the header.
3177 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3180 * If the block read failed, the file is probably new.
3185 * Initialize for all unknown fields.
3187 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
3190 * Initialize the magic number.
3192 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3195 * And a bunch of things we don't know what they mean
3197 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3198 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3199 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3200 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
3204 * Write the information to the header.
3206 StorageUtl_WriteWord(
3208 OFFSET_BIGBLOCKSIZEBITS
,
3209 This
->bigBlockSizeBits
);
3211 StorageUtl_WriteWord(
3213 OFFSET_SMALLBLOCKSIZEBITS
,
3214 This
->smallBlockSizeBits
);
3216 StorageUtl_WriteDWord(
3218 OFFSET_BBDEPOTCOUNT
,
3219 This
->bigBlockDepotCount
);
3221 StorageUtl_WriteDWord(
3223 OFFSET_ROOTSTARTBLOCK
,
3224 This
->rootStartBlock
);
3226 StorageUtl_WriteDWord(
3228 OFFSET_SBDEPOTSTART
,
3229 This
->smallBlockDepotStart
);
3231 StorageUtl_WriteDWord(
3233 OFFSET_SBDEPOTCOUNT
,
3234 This
->smallBlockDepotChain
?
3235 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3237 StorageUtl_WriteDWord(
3239 OFFSET_EXTBBDEPOTSTART
,
3240 This
->extBigBlockDepotStart
);
3242 StorageUtl_WriteDWord(
3244 OFFSET_EXTBBDEPOTCOUNT
,
3245 This
->extBigBlockDepotCount
);
3247 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3249 StorageUtl_WriteDWord(
3251 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3252 (This
->bigBlockDepotStart
[index
]));
3256 * Write the big block back to the file.
3258 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
3261 /******************************************************************************
3262 * Storage32Impl_ReadProperty
3264 * This method will read the specified property from the property chain.
3266 BOOL
StorageImpl_ReadProperty(
3269 StgProperty
* buffer
)
3271 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
3272 ULARGE_INTEGER offsetInPropSet
;
3276 offsetInPropSet
.u
.HighPart
= 0;
3277 offsetInPropSet
.u
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
3279 readRes
= BlockChainStream_ReadAt(
3280 This
->rootBlockChain
,
3286 if (SUCCEEDED(readRes
))
3288 /* replace the name of root entry (often "Root Entry") by the file name */
3289 WCHAR
*propName
= (index
== This
->base
.rootPropertySetIndex
) ?
3290 This
->filename
: (WCHAR
*)currentProperty
+OFFSET_PS_NAME
;
3292 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3296 PROPERTY_NAME_BUFFER_LEN
);
3297 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3299 memcpy(&buffer
->propertyType
, currentProperty
+ OFFSET_PS_PROPERTYTYPE
, 1);
3301 StorageUtl_ReadWord(
3303 OFFSET_PS_NAMELENGTH
,
3304 &buffer
->sizeOfNameString
);
3306 StorageUtl_ReadDWord(
3308 OFFSET_PS_PREVIOUSPROP
,
3309 &buffer
->previousProperty
);
3311 StorageUtl_ReadDWord(
3314 &buffer
->nextProperty
);
3316 StorageUtl_ReadDWord(
3319 &buffer
->dirProperty
);
3321 StorageUtl_ReadGUID(
3324 &buffer
->propertyUniqueID
);
3326 StorageUtl_ReadDWord(
3329 &buffer
->timeStampS1
);
3331 StorageUtl_ReadDWord(
3334 &buffer
->timeStampD1
);
3336 StorageUtl_ReadDWord(
3339 &buffer
->timeStampS2
);
3341 StorageUtl_ReadDWord(
3344 &buffer
->timeStampD2
);
3346 StorageUtl_ReadDWord(
3348 OFFSET_PS_STARTBLOCK
,
3349 &buffer
->startingBlock
);
3351 StorageUtl_ReadDWord(
3354 &buffer
->size
.u
.LowPart
);
3356 buffer
->size
.u
.HighPart
= 0;
3359 return SUCCEEDED(readRes
) ? TRUE
: FALSE
;
3362 /*********************************************************************
3363 * Write the specified property into the property chain
3365 BOOL
StorageImpl_WriteProperty(
3368 const StgProperty
* buffer
)
3370 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
3371 ULARGE_INTEGER offsetInPropSet
;
3375 offsetInPropSet
.u
.HighPart
= 0;
3376 offsetInPropSet
.u
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
3378 memset(currentProperty
, 0, PROPSET_BLOCK_SIZE
);
3381 currentProperty
+ OFFSET_PS_NAME
,
3383 PROPERTY_NAME_BUFFER_LEN
);
3385 memcpy(currentProperty
+ OFFSET_PS_PROPERTYTYPE
, &buffer
->propertyType
, 1);
3387 StorageUtl_WriteWord(
3389 OFFSET_PS_NAMELENGTH
,
3390 buffer
->sizeOfNameString
);
3392 StorageUtl_WriteDWord(
3394 OFFSET_PS_PREVIOUSPROP
,
3395 buffer
->previousProperty
);
3397 StorageUtl_WriteDWord(
3400 buffer
->nextProperty
);
3402 StorageUtl_WriteDWord(
3405 buffer
->dirProperty
);
3407 StorageUtl_WriteGUID(
3410 &buffer
->propertyUniqueID
);
3412 StorageUtl_WriteDWord(
3415 buffer
->timeStampS1
);
3417 StorageUtl_WriteDWord(
3420 buffer
->timeStampD1
);
3422 StorageUtl_WriteDWord(
3425 buffer
->timeStampS2
);
3427 StorageUtl_WriteDWord(
3430 buffer
->timeStampD2
);
3432 StorageUtl_WriteDWord(
3434 OFFSET_PS_STARTBLOCK
,
3435 buffer
->startingBlock
);
3437 StorageUtl_WriteDWord(
3440 buffer
->size
.u
.LowPart
);
3442 writeRes
= BlockChainStream_WriteAt(This
->rootBlockChain
,
3447 return SUCCEEDED(writeRes
) ? TRUE
: FALSE
;
3450 static BOOL
StorageImpl_ReadBigBlock(
3455 ULARGE_INTEGER ulOffset
;
3458 ulOffset
.u
.HighPart
= 0;
3459 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3461 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3462 return (read
== This
->bigBlockSize
);
3465 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3471 ULARGE_INTEGER ulOffset
;
3475 ulOffset
.u
.HighPart
= 0;
3476 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3477 ulOffset
.u
.LowPart
+= offset
;
3479 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3480 *value
= le32toh(tmp
);
3481 return (read
== sizeof(DWORD
));
3484 static BOOL
StorageImpl_WriteBigBlock(
3489 ULARGE_INTEGER ulOffset
;
3492 ulOffset
.u
.HighPart
= 0;
3493 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3495 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3496 return (wrote
== This
->bigBlockSize
);
3499 static BOOL
StorageImpl_WriteDWordToBigBlock(
3505 ULARGE_INTEGER ulOffset
;
3508 ulOffset
.u
.HighPart
= 0;
3509 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3510 ulOffset
.u
.LowPart
+= offset
;
3512 value
= htole32(value
);
3513 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3514 return (wrote
== sizeof(DWORD
));
3517 /******************************************************************************
3518 * Storage32Impl_SmallBlocksToBigBlocks
3520 * This method will convert a small block chain to a big block chain.
3521 * The small block chain will be destroyed.
3523 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3525 SmallBlockChainStream
** ppsbChain
)
3527 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3528 ULARGE_INTEGER size
, offset
;
3529 ULONG cbRead
, cbWritten
;
3530 ULARGE_INTEGER cbTotalRead
;
3531 ULONG propertyIndex
;
3532 HRESULT resWrite
= S_OK
;
3534 StgProperty chainProperty
;
3536 BlockChainStream
*bbTempChain
= NULL
;
3537 BlockChainStream
*bigBlockChain
= NULL
;
3540 * Create a temporary big block chain that doesn't have
3541 * an associated property. This temporary chain will be
3542 * used to copy data from small blocks to big blocks.
3544 bbTempChain
= BlockChainStream_Construct(This
,
3547 if(!bbTempChain
) return NULL
;
3549 * Grow the big block chain.
3551 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3552 BlockChainStream_SetSize(bbTempChain
, size
);
3555 * Copy the contents of the small block chain to the big block chain
3556 * by small block size increments.
3558 offset
.u
.LowPart
= 0;
3559 offset
.u
.HighPart
= 0;
3560 cbTotalRead
.QuadPart
= 0;
3562 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3565 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3567 This
->smallBlockSize
,
3570 if (FAILED(resRead
))
3575 cbTotalRead
.QuadPart
+= cbRead
;
3577 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3583 if (FAILED(resWrite
))
3586 offset
.u
.LowPart
+= This
->smallBlockSize
;
3588 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3589 HeapFree(GetProcessHeap(),0,buffer
);
3591 if (FAILED(resRead
) || FAILED(resWrite
))
3593 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3594 BlockChainStream_Destroy(bbTempChain
);
3599 * Destroy the small block chain.
3601 propertyIndex
= (*ppsbChain
)->ownerPropertyIndex
;
3602 size
.u
.HighPart
= 0;
3604 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3605 SmallBlockChainStream_Destroy(*ppsbChain
);
3609 * Change the property information. This chain is now a big block chain
3610 * and it doesn't reside in the small blocks chain anymore.
3612 StorageImpl_ReadProperty(This
, propertyIndex
, &chainProperty
);
3614 chainProperty
.startingBlock
= bbHeadOfChain
;
3616 StorageImpl_WriteProperty(This
, propertyIndex
, &chainProperty
);
3619 * Destroy the temporary propertyless big block chain.
3620 * Create a new big block chain associated with this property.
3622 BlockChainStream_Destroy(bbTempChain
);
3623 bigBlockChain
= BlockChainStream_Construct(This
,
3627 return bigBlockChain
;
3630 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
3632 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
3634 StorageBaseImpl_Release((IStorage
*)This
->base
.ancestorStorage
);
3635 HeapFree(GetProcessHeap(), 0, This
);
3638 /******************************************************************************
3640 ** Storage32InternalImpl_Commit
3642 ** The non-root storages cannot be opened in transacted mode thus this function
3645 static HRESULT WINAPI
StorageInternalImpl_Commit(
3647 DWORD grfCommitFlags
) /* [in] */
3652 /******************************************************************************
3654 ** Storage32InternalImpl_Revert
3656 ** The non-root storages cannot be opened in transacted mode thus this function
3659 static HRESULT WINAPI
StorageInternalImpl_Revert(
3665 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
3667 IStorage_Release((IStorage
*)This
->parentStorage
);
3668 HeapFree(GetProcessHeap(), 0, This
->stackToVisit
);
3669 HeapFree(GetProcessHeap(), 0, This
);
3672 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
3673 IEnumSTATSTG
* iface
,
3677 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3680 * Perform a sanity check on the parameters.
3683 return E_INVALIDARG
;
3686 * Initialize the return parameter.
3691 * Compare the riid with the interface IDs implemented by this object.
3693 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
3694 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
3696 *ppvObject
= (IEnumSTATSTG
*)This
;
3697 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
3701 return E_NOINTERFACE
;
3704 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
3705 IEnumSTATSTG
* iface
)
3707 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3708 return InterlockedIncrement(&This
->ref
);
3711 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
3712 IEnumSTATSTG
* iface
)
3714 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3718 newRef
= InterlockedDecrement(&This
->ref
);
3721 * If the reference count goes down to 0, perform suicide.
3725 IEnumSTATSTGImpl_Destroy(This
);
3731 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
3732 IEnumSTATSTG
* iface
,
3735 ULONG
* pceltFetched
)
3737 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3739 StgProperty currentProperty
;
3740 STATSTG
* currentReturnStruct
= rgelt
;
3741 ULONG objectFetched
= 0;
3742 ULONG currentSearchNode
;
3745 * Perform a sanity check on the parameters.
3747 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
3748 return E_INVALIDARG
;
3751 * To avoid the special case, get another pointer to a ULONG value if
3752 * the caller didn't supply one.
3754 if (pceltFetched
==0)
3755 pceltFetched
= &objectFetched
;
3758 * Start the iteration, we will iterate until we hit the end of the
3759 * linked list or until we hit the number of items to iterate through
3764 * Start with the node at the top of the stack.
3766 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3768 while ( ( *pceltFetched
< celt
) &&
3769 ( currentSearchNode
!=PROPERTY_NULL
) )
3772 * Remove the top node from the stack
3774 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3777 * Read the property from the storage.
3779 StorageImpl_ReadProperty(This
->parentStorage
,
3784 * Copy the information to the return buffer.
3786 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct
,
3791 * Step to the next item in the iteration
3794 currentReturnStruct
++;
3797 * Push the next search node in the search stack.
3799 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3802 * continue the iteration.
3804 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3807 if (*pceltFetched
== celt
)
3814 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
3815 IEnumSTATSTG
* iface
,
3818 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3820 StgProperty currentProperty
;
3821 ULONG objectFetched
= 0;
3822 ULONG currentSearchNode
;
3825 * Start with the node at the top of the stack.
3827 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3829 while ( (objectFetched
< celt
) &&
3830 (currentSearchNode
!=PROPERTY_NULL
) )
3833 * Remove the top node from the stack
3835 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3838 * Read the property from the storage.
3840 StorageImpl_ReadProperty(This
->parentStorage
,
3845 * Step to the next item in the iteration
3850 * Push the next search node in the search stack.
3852 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3855 * continue the iteration.
3857 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3860 if (objectFetched
== celt
)
3866 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
3867 IEnumSTATSTG
* iface
)
3869 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3871 StgProperty rootProperty
;
3872 BOOL readSuccessful
;
3875 * Re-initialize the search stack to an empty stack
3877 This
->stackSize
= 0;
3880 * Read the root property from the storage.
3882 readSuccessful
= StorageImpl_ReadProperty(
3883 This
->parentStorage
,
3884 This
->firstPropertyNode
,
3889 assert(rootProperty
.sizeOfNameString
!=0);
3892 * Push the search node in the search stack.
3894 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.dirProperty
);
3900 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
3901 IEnumSTATSTG
* iface
,
3902 IEnumSTATSTG
** ppenum
)
3904 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3906 IEnumSTATSTGImpl
* newClone
;
3909 * Perform a sanity check on the parameters.
3912 return E_INVALIDARG
;
3914 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
3915 This
->firstPropertyNode
);
3919 * The new clone enumeration must point to the same current node as
3922 newClone
->stackSize
= This
->stackSize
;
3923 newClone
->stackMaxSize
= This
->stackMaxSize
;
3924 newClone
->stackToVisit
=
3925 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * newClone
->stackMaxSize
);
3928 newClone
->stackToVisit
,
3930 sizeof(ULONG
) * newClone
->stackSize
);
3932 *ppenum
= (IEnumSTATSTG
*)newClone
;
3935 * Don't forget to nail down a reference to the clone before
3938 IEnumSTATSTGImpl_AddRef(*ppenum
);
3943 static INT
IEnumSTATSTGImpl_FindParentProperty(
3944 IEnumSTATSTGImpl
*This
,
3945 ULONG childProperty
,
3946 StgProperty
*currentProperty
,
3949 ULONG currentSearchNode
;
3953 * To avoid the special case, get another pointer to a ULONG value if
3954 * the caller didn't supply one.
3957 thisNodeId
= &foundNode
;
3960 * Start with the node at the top of the stack.
3962 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3965 while (currentSearchNode
!=PROPERTY_NULL
)
3968 * Store the current node in the returned parameters
3970 *thisNodeId
= currentSearchNode
;
3973 * Remove the top node from the stack
3975 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3978 * Read the property from the storage.
3980 StorageImpl_ReadProperty(
3981 This
->parentStorage
,
3985 if (currentProperty
->previousProperty
== childProperty
)
3986 return PROPERTY_RELATION_PREVIOUS
;
3988 else if (currentProperty
->nextProperty
== childProperty
)
3989 return PROPERTY_RELATION_NEXT
;
3991 else if (currentProperty
->dirProperty
== childProperty
)
3992 return PROPERTY_RELATION_DIR
;
3995 * Push the next search node in the search stack.
3997 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
4000 * continue the iteration.
4002 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
4005 return PROPERTY_NULL
;
4008 static ULONG
IEnumSTATSTGImpl_FindProperty(
4009 IEnumSTATSTGImpl
* This
,
4010 const OLECHAR
* lpszPropName
,
4011 StgProperty
* currentProperty
)
4013 ULONG currentSearchNode
;
4016 * Start with the node at the top of the stack.
4018 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
4020 while (currentSearchNode
!=PROPERTY_NULL
)
4023 * Remove the top node from the stack
4025 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
4028 * Read the property from the storage.
4030 StorageImpl_ReadProperty(This
->parentStorage
,
4034 if ( propertyNameCmp(
4035 (const OLECHAR
*)currentProperty
->name
, lpszPropName
) == 0)
4036 return currentSearchNode
;
4039 * Push the next search node in the search stack.
4041 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
4044 * continue the iteration.
4046 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
4049 return PROPERTY_NULL
;
4052 static void IEnumSTATSTGImpl_PushSearchNode(
4053 IEnumSTATSTGImpl
* This
,
4056 StgProperty rootProperty
;
4057 BOOL readSuccessful
;
4060 * First, make sure we're not trying to push an unexisting node.
4062 if (nodeToPush
==PROPERTY_NULL
)
4066 * First push the node to the stack
4068 if (This
->stackSize
== This
->stackMaxSize
)
4070 This
->stackMaxSize
+= ENUMSTATSGT_SIZE_INCREMENT
;
4072 This
->stackToVisit
= HeapReAlloc(
4076 sizeof(ULONG
) * This
->stackMaxSize
);
4079 This
->stackToVisit
[This
->stackSize
] = nodeToPush
;
4083 * Read the root property from the storage.
4085 readSuccessful
= StorageImpl_ReadProperty(
4086 This
->parentStorage
,
4092 assert(rootProperty
.sizeOfNameString
!=0);
4095 * Push the previous search node in the search stack.
4097 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.previousProperty
);
4101 static ULONG
IEnumSTATSTGImpl_PopSearchNode(
4102 IEnumSTATSTGImpl
* This
,
4107 if (This
->stackSize
== 0)
4108 return PROPERTY_NULL
;
4110 topNode
= This
->stackToVisit
[This
->stackSize
-1];
4119 * Virtual function table for the IEnumSTATSTGImpl class.
4121 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
4123 IEnumSTATSTGImpl_QueryInterface
,
4124 IEnumSTATSTGImpl_AddRef
,
4125 IEnumSTATSTGImpl_Release
,
4126 IEnumSTATSTGImpl_Next
,
4127 IEnumSTATSTGImpl_Skip
,
4128 IEnumSTATSTGImpl_Reset
,
4129 IEnumSTATSTGImpl_Clone
4132 /******************************************************************************
4133 ** IEnumSTATSTGImpl implementation
4136 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
4137 StorageImpl
* parentStorage
,
4138 ULONG firstPropertyNode
)
4140 IEnumSTATSTGImpl
* newEnumeration
;
4142 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
4144 if (newEnumeration
!=0)
4147 * Set-up the virtual function table and reference count.
4149 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
4150 newEnumeration
->ref
= 0;
4153 * We want to nail-down the reference to the storage in case the
4154 * enumeration out-lives the storage in the client application.
4156 newEnumeration
->parentStorage
= parentStorage
;
4157 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
4159 newEnumeration
->firstPropertyNode
= firstPropertyNode
;
4162 * Initialize the search stack
4164 newEnumeration
->stackSize
= 0;
4165 newEnumeration
->stackMaxSize
= ENUMSTATSGT_SIZE_INCREMENT
;
4166 newEnumeration
->stackToVisit
=
4167 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
)*ENUMSTATSGT_SIZE_INCREMENT
);
4170 * Make sure the current node of the iterator is the first one.
4172 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
4175 return newEnumeration
;
4179 * Virtual function table for the Storage32InternalImpl class.
4181 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
4183 StorageBaseImpl_QueryInterface
,
4184 StorageBaseImpl_AddRef
,
4185 StorageBaseImpl_Release
,
4186 StorageBaseImpl_CreateStream
,
4187 StorageBaseImpl_OpenStream
,
4188 StorageImpl_CreateStorage
,
4189 StorageBaseImpl_OpenStorage
,
4191 StorageImpl_MoveElementTo
,
4192 StorageInternalImpl_Commit
,
4193 StorageInternalImpl_Revert
,
4194 StorageBaseImpl_EnumElements
,
4195 StorageImpl_DestroyElement
,
4196 StorageBaseImpl_RenameElement
,
4197 StorageImpl_SetElementTimes
,
4198 StorageBaseImpl_SetClass
,
4199 StorageImpl_SetStateBits
,
4200 StorageBaseImpl_Stat
4203 /******************************************************************************
4204 ** Storage32InternalImpl implementation
4207 static StorageInternalImpl
* StorageInternalImpl_Construct(
4208 StorageImpl
* ancestorStorage
,
4210 ULONG rootPropertyIndex
)
4212 StorageInternalImpl
* newStorage
;
4215 * Allocate space for the new storage object
4217 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
4222 * Initialize the stream list
4224 list_init(&newStorage
->base
.strmHead
);
4227 * Initialize the virtual function table.
4229 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
4230 newStorage
->base
.v_destructor
= StorageInternalImpl_Destroy
;
4231 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
4234 * Keep the ancestor storage pointer and nail a reference to it.
4236 newStorage
->base
.ancestorStorage
= ancestorStorage
;
4237 StorageBaseImpl_AddRef((IStorage
*)(newStorage
->base
.ancestorStorage
));
4240 * Keep the index of the root property set for this storage,
4242 newStorage
->base
.rootPropertySetIndex
= rootPropertyIndex
;
4250 /******************************************************************************
4251 ** StorageUtl implementation
4254 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
4258 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
4259 *value
= le16toh(tmp
);
4262 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
4264 value
= htole16(value
);
4265 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
4268 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
4272 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
4273 *value
= le32toh(tmp
);
4276 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
4278 value
= htole32(value
);
4279 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
4282 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
4283 ULARGE_INTEGER
* value
)
4285 #ifdef WORDS_BIGENDIAN
4288 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4289 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
4290 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
4292 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4296 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
4297 const ULARGE_INTEGER
*value
)
4299 #ifdef WORDS_BIGENDIAN
4302 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
4303 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
4304 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
4306 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
4310 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
4312 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
4313 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
4314 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
4316 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
4319 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
4321 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
4322 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
4323 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
4325 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
4328 void StorageUtl_CopyPropertyToSTATSTG(
4329 STATSTG
* destination
,
4330 const StgProperty
* source
,
4334 * The copy of the string occurs only when the flag is not set
4336 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
4337 (source
->name
== NULL
) ||
4338 (source
->name
[0] == 0) )
4340 destination
->pwcsName
= 0;
4344 destination
->pwcsName
=
4345 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
4347 strcpyW((LPWSTR
)destination
->pwcsName
, source
->name
);
4350 switch (source
->propertyType
)
4352 case PROPTYPE_STORAGE
:
4354 destination
->type
= STGTY_STORAGE
;
4356 case PROPTYPE_STREAM
:
4357 destination
->type
= STGTY_STREAM
;
4360 destination
->type
= STGTY_STREAM
;
4364 destination
->cbSize
= source
->size
;
4366 currentReturnStruct->mtime = {0}; TODO
4367 currentReturnStruct->ctime = {0};
4368 currentReturnStruct->atime = {0};
4370 destination
->grfMode
= 0;
4371 destination
->grfLocksSupported
= 0;
4372 destination
->clsid
= source
->propertyUniqueID
;
4373 destination
->grfStateBits
= 0;
4374 destination
->reserved
= 0;
4377 /******************************************************************************
4378 ** BlockChainStream implementation
4381 BlockChainStream
* BlockChainStream_Construct(
4382 StorageImpl
* parentStorage
,
4383 ULONG
* headOfStreamPlaceHolder
,
4384 ULONG propertyIndex
)
4386 BlockChainStream
* newStream
;
4389 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
4391 newStream
->parentStorage
= parentStorage
;
4392 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
4393 newStream
->ownerPropertyIndex
= propertyIndex
;
4394 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
4395 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
4396 newStream
->numBlocks
= 0;
4398 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
4400 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4402 newStream
->numBlocks
++;
4403 newStream
->tailIndex
= blockIndex
;
4405 if(FAILED(StorageImpl_GetNextBlockInChain(
4410 HeapFree(GetProcessHeap(), 0, newStream
);
4418 void BlockChainStream_Destroy(BlockChainStream
* This
)
4420 HeapFree(GetProcessHeap(), 0, This
);
4423 /******************************************************************************
4424 * BlockChainStream_GetHeadOfChain
4426 * Returns the head of this stream chain.
4427 * Some special chains don't have properties, their heads are kept in
4428 * This->headOfStreamPlaceHolder.
4431 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
4433 StgProperty chainProperty
;
4434 BOOL readSuccessful
;
4436 if (This
->headOfStreamPlaceHolder
!= 0)
4437 return *(This
->headOfStreamPlaceHolder
);
4439 if (This
->ownerPropertyIndex
!= PROPERTY_NULL
)
4441 readSuccessful
= StorageImpl_ReadProperty(
4442 This
->parentStorage
,
4443 This
->ownerPropertyIndex
,
4448 return chainProperty
.startingBlock
;
4452 return BLOCK_END_OF_CHAIN
;
4455 /******************************************************************************
4456 * BlockChainStream_GetCount
4458 * Returns the number of blocks that comprises this chain.
4459 * This is not the size of the stream as the last block may not be full!
4462 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
4467 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4469 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4473 if(FAILED(StorageImpl_GetNextBlockInChain(
4474 This
->parentStorage
,
4483 /******************************************************************************
4484 * BlockChainStream_ReadAt
4486 * Reads a specified number of bytes from this chain at the specified offset.
4487 * bytesRead may be NULL.
4488 * Failure will be returned if the specified number of bytes has not been read.
4490 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
4491 ULARGE_INTEGER offset
,
4496 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4497 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4498 ULONG bytesToReadInBuffer
;
4502 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
4505 * Find the first block in the stream that contains part of the buffer.
4507 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4508 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4509 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4511 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4512 This
->lastBlockNoInSequence
= blockNoInSequence
;
4516 ULONG temp
= blockNoInSequence
;
4518 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4519 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4520 This
->lastBlockNoInSequence
= temp
;
4523 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4525 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4526 return STG_E_DOCFILECORRUPT
;
4527 blockNoInSequence
--;
4530 if ((blockNoInSequence
> 0) && (blockIndex
== BLOCK_END_OF_CHAIN
))
4531 return STG_E_DOCFILECORRUPT
; /* We failed to find the starting block */
4533 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4536 * Start reading the buffer.
4539 bufferWalker
= buffer
;
4541 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4543 ULARGE_INTEGER ulOffset
;
4546 * Calculate how many bytes we can copy from this big block.
4548 bytesToReadInBuffer
=
4549 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4551 TRACE("block %i\n",blockIndex
);
4552 ulOffset
.u
.HighPart
= 0;
4553 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
4556 StorageImpl_ReadAt(This
->parentStorage
,
4559 bytesToReadInBuffer
,
4562 * Step to the next big block.
4564 if( size
> bytesReadAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4565 return STG_E_DOCFILECORRUPT
;
4567 bufferWalker
+= bytesReadAt
;
4568 size
-= bytesReadAt
;
4569 *bytesRead
+= bytesReadAt
;
4570 offsetInBlock
= 0; /* There is no offset on the next block */
4572 if (bytesToReadInBuffer
!= bytesReadAt
)
4576 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
4579 /******************************************************************************
4580 * BlockChainStream_WriteAt
4582 * Writes the specified number of bytes to this chain at the specified offset.
4583 * bytesWritten may be NULL.
4584 * Will fail if not all specified number of bytes have been written.
4586 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
4587 ULARGE_INTEGER offset
,
4590 ULONG
* bytesWritten
)
4592 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4593 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4596 const BYTE
* bufferWalker
;
4599 * Find the first block in the stream that contains part of the buffer.
4601 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4602 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4603 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4605 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4606 This
->lastBlockNoInSequence
= blockNoInSequence
;
4610 ULONG temp
= blockNoInSequence
;
4612 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4613 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4614 This
->lastBlockNoInSequence
= temp
;
4617 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4619 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4621 return STG_E_DOCFILECORRUPT
;
4622 blockNoInSequence
--;
4625 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4627 /* BlockChainStream_SetSize should have already been called to ensure we have
4628 * enough blocks in the chain to write into */
4629 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4631 ERR("not enough blocks in chain to write data\n");
4632 return STG_E_DOCFILECORRUPT
;
4636 bufferWalker
= (const BYTE
*)buffer
;
4638 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4640 ULARGE_INTEGER ulOffset
;
4641 DWORD bytesWrittenAt
;
4643 * Calculate how many bytes we can copy from this big block.
4646 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4648 TRACE("block %i\n",blockIndex
);
4649 ulOffset
.u
.HighPart
= 0;
4650 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
4653 StorageImpl_WriteAt(This
->parentStorage
,
4660 * Step to the next big block.
4662 if(size
> bytesWrittenAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4664 return STG_E_DOCFILECORRUPT
;
4666 bufferWalker
+= bytesWrittenAt
;
4667 size
-= bytesWrittenAt
;
4668 *bytesWritten
+= bytesWrittenAt
;
4669 offsetInBlock
= 0; /* There is no offset on the next block */
4671 if (bytesWrittenAt
!= bytesToWrite
)
4675 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
4678 /******************************************************************************
4679 * BlockChainStream_Shrink
4681 * Shrinks this chain in the big block depot.
4683 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
4684 ULARGE_INTEGER newSize
)
4686 ULONG blockIndex
, extraBlock
;
4691 * Reset the last accessed block cache.
4693 This
->lastBlockNoInSequence
= 0xFFFFFFFF;
4694 This
->lastBlockNoInSequenceIndex
= BLOCK_END_OF_CHAIN
;
4697 * Figure out how many blocks are needed to contain the new size
4699 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4701 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4704 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4707 * Go to the new end of chain
4709 while (count
< numBlocks
)
4711 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4717 /* Get the next block before marking the new end */
4718 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4722 /* Mark the new end of chain */
4723 StorageImpl_SetNextBlockInChain(
4724 This
->parentStorage
,
4726 BLOCK_END_OF_CHAIN
);
4728 This
->tailIndex
= blockIndex
;
4729 This
->numBlocks
= numBlocks
;
4732 * Mark the extra blocks as free
4734 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4736 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
,
4739 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
4740 extraBlock
= blockIndex
;
4746 /******************************************************************************
4747 * BlockChainStream_Enlarge
4749 * Grows this chain in the big block depot.
4751 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
4752 ULARGE_INTEGER newSize
)
4754 ULONG blockIndex
, currentBlock
;
4756 ULONG oldNumBlocks
= 0;
4758 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4761 * Empty chain. Create the head.
4763 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4765 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4766 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
4768 BLOCK_END_OF_CHAIN
);
4770 if (This
->headOfStreamPlaceHolder
!= 0)
4772 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
4776 StgProperty chainProp
;
4777 assert(This
->ownerPropertyIndex
!= PROPERTY_NULL
);
4779 StorageImpl_ReadProperty(
4780 This
->parentStorage
,
4781 This
->ownerPropertyIndex
,
4784 chainProp
.startingBlock
= blockIndex
;
4786 StorageImpl_WriteProperty(
4787 This
->parentStorage
,
4788 This
->ownerPropertyIndex
,
4792 This
->tailIndex
= blockIndex
;
4793 This
->numBlocks
= 1;
4797 * Figure out how many blocks are needed to contain this stream
4799 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4801 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4805 * Go to the current end of chain
4807 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
4809 currentBlock
= blockIndex
;
4811 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4814 currentBlock
= blockIndex
;
4816 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
4821 This
->tailIndex
= currentBlock
;
4824 currentBlock
= This
->tailIndex
;
4825 oldNumBlocks
= This
->numBlocks
;
4828 * Add new blocks to the chain
4830 if (oldNumBlocks
< newNumBlocks
)
4832 while (oldNumBlocks
< newNumBlocks
)
4834 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4836 StorageImpl_SetNextBlockInChain(
4837 This
->parentStorage
,
4841 StorageImpl_SetNextBlockInChain(
4842 This
->parentStorage
,
4844 BLOCK_END_OF_CHAIN
);
4846 currentBlock
= blockIndex
;
4850 This
->tailIndex
= blockIndex
;
4851 This
->numBlocks
= newNumBlocks
;
4857 /******************************************************************************
4858 * BlockChainStream_SetSize
4860 * Sets the size of this stream. The big block depot will be updated.
4861 * The file will grow if we grow the chain.
4863 * TODO: Free the actual blocks in the file when we shrink the chain.
4864 * Currently, the blocks are still in the file. So the file size
4865 * doesn't shrink even if we shrink streams.
4867 BOOL
BlockChainStream_SetSize(
4868 BlockChainStream
* This
,
4869 ULARGE_INTEGER newSize
)
4871 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
4873 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
4876 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
4878 BlockChainStream_Shrink(This
, newSize
);
4882 BlockChainStream_Enlarge(This
, newSize
);
4888 /******************************************************************************
4889 * BlockChainStream_GetSize
4891 * Returns the size of this chain.
4892 * Will return the block count if this chain doesn't have a property.
4894 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
4896 StgProperty chainProperty
;
4898 if(This
->headOfStreamPlaceHolder
== NULL
)
4901 * This chain is a data stream read the property and return
4902 * the appropriate size
4904 StorageImpl_ReadProperty(
4905 This
->parentStorage
,
4906 This
->ownerPropertyIndex
,
4909 return chainProperty
.size
;
4914 * this chain is a chain that does not have a property, figure out the
4915 * size by making the product number of used blocks times the
4918 ULARGE_INTEGER result
;
4919 result
.u
.HighPart
= 0;
4922 BlockChainStream_GetCount(This
) *
4923 This
->parentStorage
->bigBlockSize
;
4929 /******************************************************************************
4930 ** SmallBlockChainStream implementation
4933 SmallBlockChainStream
* SmallBlockChainStream_Construct(
4934 StorageImpl
* parentStorage
,
4935 ULONG propertyIndex
)
4937 SmallBlockChainStream
* newStream
;
4939 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
4941 newStream
->parentStorage
= parentStorage
;
4942 newStream
->ownerPropertyIndex
= propertyIndex
;
4947 void SmallBlockChainStream_Destroy(
4948 SmallBlockChainStream
* This
)
4950 HeapFree(GetProcessHeap(), 0, This
);
4953 /******************************************************************************
4954 * SmallBlockChainStream_GetHeadOfChain
4956 * Returns the head of this chain of small blocks.
4958 static ULONG
SmallBlockChainStream_GetHeadOfChain(
4959 SmallBlockChainStream
* This
)
4961 StgProperty chainProperty
;
4962 BOOL readSuccessful
;
4964 if (This
->ownerPropertyIndex
)
4966 readSuccessful
= StorageImpl_ReadProperty(
4967 This
->parentStorage
,
4968 This
->ownerPropertyIndex
,
4973 return chainProperty
.startingBlock
;
4978 return BLOCK_END_OF_CHAIN
;
4981 /******************************************************************************
4982 * SmallBlockChainStream_GetNextBlockInChain
4984 * Returns the index of the next small block in this chain.
4987 * - BLOCK_END_OF_CHAIN: end of this chain
4988 * - BLOCK_UNUSED: small block 'blockIndex' is free
4990 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
4991 SmallBlockChainStream
* This
,
4993 ULONG
* nextBlockInChain
)
4995 ULARGE_INTEGER offsetOfBlockInDepot
;
5000 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
5002 offsetOfBlockInDepot
.u
.HighPart
= 0;
5003 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5006 * Read those bytes in the buffer from the small block file.
5008 res
= BlockChainStream_ReadAt(
5009 This
->parentStorage
->smallBlockDepotChain
,
5010 offsetOfBlockInDepot
,
5017 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
5024 /******************************************************************************
5025 * SmallBlockChainStream_SetNextBlockInChain
5027 * Writes the index of the next block of the specified block in the small
5029 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5030 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5032 static void SmallBlockChainStream_SetNextBlockInChain(
5033 SmallBlockChainStream
* This
,
5037 ULARGE_INTEGER offsetOfBlockInDepot
;
5041 offsetOfBlockInDepot
.u
.HighPart
= 0;
5042 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5044 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
5047 * Read those bytes in the buffer from the small block file.
5049 BlockChainStream_WriteAt(
5050 This
->parentStorage
->smallBlockDepotChain
,
5051 offsetOfBlockInDepot
,
5057 /******************************************************************************
5058 * SmallBlockChainStream_FreeBlock
5060 * Flag small block 'blockIndex' as free in the small block depot.
5062 static void SmallBlockChainStream_FreeBlock(
5063 SmallBlockChainStream
* This
,
5066 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
5069 /******************************************************************************
5070 * SmallBlockChainStream_GetNextFreeBlock
5072 * Returns the index of a free small block. The small block depot will be
5073 * enlarged if necessary. The small block chain will also be enlarged if
5076 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
5077 SmallBlockChainStream
* This
)
5079 ULARGE_INTEGER offsetOfBlockInDepot
;
5082 ULONG blockIndex
= 0;
5083 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
5085 ULONG smallBlocksPerBigBlock
;
5087 offsetOfBlockInDepot
.u
.HighPart
= 0;
5090 * Scan the small block depot for a free block
5092 while (nextBlockIndex
!= BLOCK_UNUSED
)
5094 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5096 res
= BlockChainStream_ReadAt(
5097 This
->parentStorage
->smallBlockDepotChain
,
5098 offsetOfBlockInDepot
,
5104 * If we run out of space for the small block depot, enlarge it
5108 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
5110 if (nextBlockIndex
!= BLOCK_UNUSED
)
5116 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
5118 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
5119 ULONG nextBlock
, newsbdIndex
;
5120 BYTE smallBlockDepot
[BIG_BLOCK_SIZE
];
5122 nextBlock
= sbdIndex
;
5123 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
5125 sbdIndex
= nextBlock
;
5126 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
, &nextBlock
);
5129 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5130 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
5131 StorageImpl_SetNextBlockInChain(
5132 This
->parentStorage
,
5136 StorageImpl_SetNextBlockInChain(
5137 This
->parentStorage
,
5139 BLOCK_END_OF_CHAIN
);
5142 * Initialize all the small blocks to free
5144 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
5145 StorageImpl_WriteBigBlock(This
->parentStorage
, newsbdIndex
, smallBlockDepot
);
5150 * We have just created the small block depot.
5152 StgProperty rootProp
;
5156 * Save it in the header
5158 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
5159 StorageImpl_SaveFileHeader(This
->parentStorage
);
5162 * And allocate the first big block that will contain small blocks
5165 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5167 StorageImpl_SetNextBlockInChain(
5168 This
->parentStorage
,
5170 BLOCK_END_OF_CHAIN
);
5172 StorageImpl_ReadProperty(
5173 This
->parentStorage
,
5174 This
->parentStorage
->base
.rootPropertySetIndex
,
5177 rootProp
.startingBlock
= sbStartIndex
;
5178 rootProp
.size
.u
.HighPart
= 0;
5179 rootProp
.size
.u
.LowPart
= This
->parentStorage
->bigBlockSize
;
5181 StorageImpl_WriteProperty(
5182 This
->parentStorage
,
5183 This
->parentStorage
->base
.rootPropertySetIndex
,
5187 StorageImpl_SaveFileHeader(This
->parentStorage
);
5191 smallBlocksPerBigBlock
=
5192 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
5195 * Verify if we have to allocate big blocks to contain small blocks
5197 if (blockIndex
% smallBlocksPerBigBlock
== 0)
5199 StgProperty rootProp
;
5200 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
5202 StorageImpl_ReadProperty(
5203 This
->parentStorage
,
5204 This
->parentStorage
->base
.rootPropertySetIndex
,
5207 if (rootProp
.size
.u
.LowPart
<
5208 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
5210 rootProp
.size
.u
.LowPart
+= This
->parentStorage
->bigBlockSize
;
5212 BlockChainStream_SetSize(
5213 This
->parentStorage
->smallBlockRootChain
,
5216 StorageImpl_WriteProperty(
5217 This
->parentStorage
,
5218 This
->parentStorage
->base
.rootPropertySetIndex
,
5226 /******************************************************************************
5227 * SmallBlockChainStream_ReadAt
5229 * Reads a specified number of bytes from this chain at the specified offset.
5230 * bytesRead may be NULL.
5231 * Failure will be returned if the specified number of bytes has not been read.
5233 HRESULT
SmallBlockChainStream_ReadAt(
5234 SmallBlockChainStream
* This
,
5235 ULARGE_INTEGER offset
,
5241 ULARGE_INTEGER offsetInBigBlockFile
;
5242 ULONG blockNoInSequence
=
5243 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5245 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5246 ULONG bytesToReadInBuffer
;
5248 ULONG bytesReadFromBigBlockFile
;
5252 * This should never happen on a small block file.
5254 assert(offset
.u
.HighPart
==0);
5257 * Find the first block in the stream that contains part of the buffer.
5259 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5261 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5263 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5266 blockNoInSequence
--;
5270 * Start reading the buffer.
5273 bufferWalker
= buffer
;
5275 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5278 * Calculate how many bytes we can copy from this small block.
5280 bytesToReadInBuffer
=
5281 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5284 * Calculate the offset of the small block in the small block file.
5286 offsetInBigBlockFile
.u
.HighPart
= 0;
5287 offsetInBigBlockFile
.u
.LowPart
=
5288 blockIndex
* This
->parentStorage
->smallBlockSize
;
5290 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5293 * Read those bytes in the buffer from the small block file.
5294 * The small block has already been identified so it shouldn't fail
5295 * unless the file is corrupt.
5297 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
5298 offsetInBigBlockFile
,
5299 bytesToReadInBuffer
,
5301 &bytesReadFromBigBlockFile
);
5307 * Step to the next big block.
5309 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5311 return STG_E_DOCFILECORRUPT
;
5313 bufferWalker
+= bytesReadFromBigBlockFile
;
5314 size
-= bytesReadFromBigBlockFile
;
5315 *bytesRead
+= bytesReadFromBigBlockFile
;
5316 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5319 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5322 /******************************************************************************
5323 * SmallBlockChainStream_WriteAt
5325 * Writes the specified number of bytes to this chain at the specified offset.
5326 * bytesWritten may be NULL.
5327 * Will fail if not all specified number of bytes have been written.
5329 HRESULT
SmallBlockChainStream_WriteAt(
5330 SmallBlockChainStream
* This
,
5331 ULARGE_INTEGER offset
,
5334 ULONG
* bytesWritten
)
5336 ULARGE_INTEGER offsetInBigBlockFile
;
5337 ULONG blockNoInSequence
=
5338 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5340 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5341 ULONG bytesToWriteInBuffer
;
5343 ULONG bytesWrittenToBigBlockFile
;
5344 const BYTE
* bufferWalker
;
5348 * This should never happen on a small block file.
5350 assert(offset
.u
.HighPart
==0);
5353 * Find the first block in the stream that contains part of the buffer.
5355 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5357 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5359 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5360 return STG_E_DOCFILECORRUPT
;
5361 blockNoInSequence
--;
5365 * Start writing the buffer.
5367 * Here, I'm casting away the constness on the buffer variable
5368 * This is OK since we don't intend to modify that buffer.
5371 bufferWalker
= (const BYTE
*)buffer
;
5372 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5375 * Calculate how many bytes we can copy to this small block.
5377 bytesToWriteInBuffer
=
5378 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5381 * Calculate the offset of the small block in the small block file.
5383 offsetInBigBlockFile
.u
.HighPart
= 0;
5384 offsetInBigBlockFile
.u
.LowPart
=
5385 blockIndex
* This
->parentStorage
->smallBlockSize
;
5387 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5390 * Write those bytes in the buffer to the small block file.
5392 res
= BlockChainStream_WriteAt(
5393 This
->parentStorage
->smallBlockRootChain
,
5394 offsetInBigBlockFile
,
5395 bytesToWriteInBuffer
,
5397 &bytesWrittenToBigBlockFile
);
5402 * Step to the next big block.
5404 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5407 bufferWalker
+= bytesWrittenToBigBlockFile
;
5408 size
-= bytesWrittenToBigBlockFile
;
5409 *bytesWritten
+= bytesWrittenToBigBlockFile
;
5410 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5413 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
5416 /******************************************************************************
5417 * SmallBlockChainStream_Shrink
5419 * Shrinks this chain in the small block depot.
5421 static BOOL
SmallBlockChainStream_Shrink(
5422 SmallBlockChainStream
* This
,
5423 ULARGE_INTEGER newSize
)
5425 ULONG blockIndex
, extraBlock
;
5429 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5431 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5434 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5437 * Go to the new end of chain
5439 while (count
< numBlocks
)
5441 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5448 * If the count is 0, we have a special case, the head of the chain was
5453 StgProperty chainProp
;
5455 StorageImpl_ReadProperty(This
->parentStorage
,
5456 This
->ownerPropertyIndex
,
5459 chainProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
5461 StorageImpl_WriteProperty(This
->parentStorage
,
5462 This
->ownerPropertyIndex
,
5466 * We start freeing the chain at the head block.
5468 extraBlock
= blockIndex
;
5472 /* Get the next block before marking the new end */
5473 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5477 /* Mark the new end of chain */
5478 SmallBlockChainStream_SetNextBlockInChain(
5481 BLOCK_END_OF_CHAIN
);
5485 * Mark the extra blocks as free
5487 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
5489 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
5492 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
5493 extraBlock
= blockIndex
;
5499 /******************************************************************************
5500 * SmallBlockChainStream_Enlarge
5502 * Grows this chain in the small block depot.
5504 static BOOL
SmallBlockChainStream_Enlarge(
5505 SmallBlockChainStream
* This
,
5506 ULARGE_INTEGER newSize
)
5508 ULONG blockIndex
, currentBlock
;
5510 ULONG oldNumBlocks
= 0;
5512 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5517 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5520 StgProperty chainProp
;
5522 StorageImpl_ReadProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
5525 chainProp
.startingBlock
= SmallBlockChainStream_GetNextFreeBlock(This
);
5527 StorageImpl_WriteProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
5530 blockIndex
= chainProp
.startingBlock
;
5531 SmallBlockChainStream_SetNextBlockInChain(
5534 BLOCK_END_OF_CHAIN
);
5537 currentBlock
= blockIndex
;
5540 * Figure out how many blocks are needed to contain this stream
5542 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5544 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5548 * Go to the current end of chain
5550 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5553 currentBlock
= blockIndex
;
5554 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
5559 * Add new blocks to the chain
5561 while (oldNumBlocks
< newNumBlocks
)
5563 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
5564 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
5566 SmallBlockChainStream_SetNextBlockInChain(
5569 BLOCK_END_OF_CHAIN
);
5571 currentBlock
= blockIndex
;
5578 /******************************************************************************
5579 * SmallBlockChainStream_SetSize
5581 * Sets the size of this stream.
5582 * The file will grow if we grow the chain.
5584 * TODO: Free the actual blocks in the file when we shrink the chain.
5585 * Currently, the blocks are still in the file. So the file size
5586 * doesn't shrink even if we shrink streams.
5588 BOOL
SmallBlockChainStream_SetSize(
5589 SmallBlockChainStream
* This
,
5590 ULARGE_INTEGER newSize
)
5592 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
5594 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
5597 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
5599 SmallBlockChainStream_Shrink(This
, newSize
);
5603 SmallBlockChainStream_Enlarge(This
, newSize
);
5609 /******************************************************************************
5610 * SmallBlockChainStream_GetSize
5612 * Returns the size of this chain.
5614 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
5616 StgProperty chainProperty
;
5618 StorageImpl_ReadProperty(
5619 This
->parentStorage
,
5620 This
->ownerPropertyIndex
,
5623 return chainProperty
.size
;
5626 /******************************************************************************
5627 * StgCreateDocfile [OLE32.@]
5628 * Creates a new compound file storage object
5631 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5632 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5633 * reserved [ ?] unused?, usually 0
5634 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5637 * S_OK if the file was successfully created
5638 * some STG_E_ value if error
5640 * if pwcsName is NULL, create file with new unique name
5641 * the function can returns
5642 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5645 HRESULT WINAPI
StgCreateDocfile(
5649 IStorage
**ppstgOpen
)
5651 StorageImpl
* newStorage
= 0;
5652 HANDLE hFile
= INVALID_HANDLE_VALUE
;
5653 HRESULT hr
= STG_E_INVALIDFLAG
;
5657 DWORD fileAttributes
;
5658 WCHAR tempFileName
[MAX_PATH
];
5660 TRACE("(%s, %x, %d, %p)\n",
5661 debugstr_w(pwcsName
), grfMode
,
5662 reserved
, ppstgOpen
);
5665 * Validate the parameters
5668 return STG_E_INVALIDPOINTER
;
5670 return STG_E_INVALIDPARAMETER
;
5672 /* if no share mode given then DENY_NONE is the default */
5673 if (STGM_SHARE_MODE(grfMode
) == 0)
5674 grfMode
|= STGM_SHARE_DENY_NONE
;
5677 * Validate the STGM flags
5679 if ( FAILED( validateSTGM(grfMode
) ))
5682 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5683 switch(STGM_ACCESS_MODE(grfMode
))
5686 case STGM_READWRITE
:
5692 /* in direct mode, can only use SHARE_EXCLUSIVE */
5693 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
5696 /* but in transacted mode, any share mode is valid */
5699 * Generate a unique name.
5703 WCHAR tempPath
[MAX_PATH
];
5704 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
5706 memset(tempPath
, 0, sizeof(tempPath
));
5707 memset(tempFileName
, 0, sizeof(tempFileName
));
5709 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
5712 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
5713 pwcsName
= tempFileName
;
5716 hr
= STG_E_INSUFFICIENTMEMORY
;
5720 creationMode
= TRUNCATE_EXISTING
;
5724 creationMode
= GetCreationModeFromSTGM(grfMode
);
5728 * Interpret the STGM value grfMode
5730 shareMode
= GetShareModeFromSTGM(grfMode
);
5731 accessMode
= GetAccessModeFromSTGM(grfMode
);
5733 if (grfMode
& STGM_DELETEONRELEASE
)
5734 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
5736 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
5738 if (grfMode
& STGM_TRANSACTED
)
5739 FIXME("Transacted mode not implemented.\n");
5742 * Initialize the "out" parameter.
5746 hFile
= CreateFileW(pwcsName
,
5754 if (hFile
== INVALID_HANDLE_VALUE
)
5756 if(GetLastError() == ERROR_FILE_EXISTS
)
5757 hr
= STG_E_FILEALREADYEXISTS
;
5764 * Allocate and initialize the new IStorage32object.
5766 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5768 if (newStorage
== 0)
5770 hr
= STG_E_INSUFFICIENTMEMORY
;
5774 hr
= StorageImpl_Construct(
5785 HeapFree(GetProcessHeap(), 0, newStorage
);
5790 * Get an "out" pointer for the caller.
5792 hr
= StorageBaseImpl_QueryInterface(
5793 (IStorage
*)newStorage
,
5794 (REFIID
)&IID_IStorage
,
5797 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
5802 /******************************************************************************
5803 * StgCreateStorageEx [OLE32.@]
5805 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
5807 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
5808 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
5810 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
5812 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5813 return STG_E_INVALIDPARAMETER
;
5816 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
5818 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5819 return STG_E_INVALIDPARAMETER
;
5822 if (stgfmt
== STGFMT_FILE
)
5824 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5825 return STG_E_INVALIDPARAMETER
;
5828 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
5830 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5831 return StgCreateDocfile(pwcsName
, grfMode
, 0, (IStorage
**)ppObjectOpen
);
5834 ERR("Invalid stgfmt argument\n");
5835 return STG_E_INVALIDPARAMETER
;
5838 /******************************************************************************
5839 * StgCreatePropSetStg [OLE32.@]
5841 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
5842 IPropertySetStorage
**ppPropSetStg
)
5846 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, ppPropSetStg
);
5848 hr
= STG_E_INVALIDPARAMETER
;
5850 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
5851 (void**)ppPropSetStg
);
5855 /******************************************************************************
5856 * StgOpenStorageEx [OLE32.@]
5858 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
5860 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
5861 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
5863 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
5865 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5866 return STG_E_INVALIDPARAMETER
;
5872 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5873 return STG_E_INVALIDPARAMETER
;
5875 case STGFMT_STORAGE
:
5878 case STGFMT_DOCFILE
:
5879 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
5881 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5882 return STG_E_INVALIDPARAMETER
;
5884 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5888 WARN("STGFMT_ANY assuming storage\n");
5892 return STG_E_INVALIDPARAMETER
;
5895 return StgOpenStorage(pwcsName
, NULL
, grfMode
, (SNB
)NULL
, 0, (IStorage
**)ppObjectOpen
);
5899 /******************************************************************************
5900 * StgOpenStorage [OLE32.@]
5902 HRESULT WINAPI
StgOpenStorage(
5903 const OLECHAR
*pwcsName
,
5904 IStorage
*pstgPriority
,
5908 IStorage
**ppstgOpen
)
5910 StorageImpl
* newStorage
= 0;
5915 WCHAR fullname
[MAX_PATH
];
5917 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5918 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
5919 snbExclude
, reserved
, ppstgOpen
);
5922 * Perform sanity checks
5926 hr
= STG_E_INVALIDNAME
;
5932 hr
= STG_E_INVALIDPOINTER
;
5938 hr
= STG_E_INVALIDPARAMETER
;
5942 if (grfMode
& STGM_PRIORITY
)
5944 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
5945 return STG_E_INVALIDFLAG
;
5946 if (grfMode
& STGM_DELETEONRELEASE
)
5947 return STG_E_INVALIDFUNCTION
;
5948 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
5949 return STG_E_INVALIDFLAG
;
5950 grfMode
&= ~0xf0; /* remove the existing sharing mode */
5951 grfMode
|= STGM_SHARE_DENY_NONE
;
5953 /* STGM_PRIORITY stops other IStorage objects on the same file from
5954 * committing until the STGM_PRIORITY IStorage is closed. it also
5955 * stops non-transacted mode StgOpenStorage calls with write access from
5956 * succeeding. obviously, both of these cannot be achieved through just
5957 * file share flags */
5958 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5962 * Validate the sharing mode
5964 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
5965 switch(STGM_SHARE_MODE(grfMode
))
5967 case STGM_SHARE_EXCLUSIVE
:
5968 case STGM_SHARE_DENY_WRITE
:
5971 hr
= STG_E_INVALIDFLAG
;
5976 * Validate the STGM flags
5978 if ( FAILED( validateSTGM(grfMode
) ) ||
5979 (grfMode
&STGM_CREATE
))
5981 hr
= STG_E_INVALIDFLAG
;
5985 /* shared reading requires transacted mode */
5986 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
5987 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
5988 !(grfMode
&STGM_TRANSACTED
) )
5990 hr
= STG_E_INVALIDFLAG
;
5995 * Interpret the STGM value grfMode
5997 shareMode
= GetShareModeFromSTGM(grfMode
);
5998 accessMode
= GetAccessModeFromSTGM(grfMode
);
6001 * Initialize the "out" parameter.
6005 hFile
= CreateFileW( pwcsName
,
6010 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
6013 if (hFile
==INVALID_HANDLE_VALUE
)
6015 DWORD last_error
= GetLastError();
6021 case ERROR_FILE_NOT_FOUND
:
6022 hr
= STG_E_FILENOTFOUND
;
6025 case ERROR_PATH_NOT_FOUND
:
6026 hr
= STG_E_PATHNOTFOUND
;
6029 case ERROR_ACCESS_DENIED
:
6030 case ERROR_WRITE_PROTECT
:
6031 hr
= STG_E_ACCESSDENIED
;
6034 case ERROR_SHARING_VIOLATION
:
6035 hr
= STG_E_SHAREVIOLATION
;
6046 * Refuse to open the file if it's too small to be a structured storage file
6047 * FIXME: verify the file when reading instead of here
6049 if (GetFileSize(hFile
, NULL
) < 0x100)
6052 hr
= STG_E_FILEALREADYEXISTS
;
6057 * Allocate and initialize the new IStorage32object.
6059 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
6061 if (newStorage
== 0)
6063 hr
= STG_E_INSUFFICIENTMEMORY
;
6067 /* Initialize the storage */
6068 hr
= StorageImpl_Construct(
6079 HeapFree(GetProcessHeap(), 0, newStorage
);
6081 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6083 if(hr
== STG_E_INVALIDHEADER
)
6084 hr
= STG_E_FILEALREADYEXISTS
;
6088 /* prepare the file name string given in lieu of the root property name */
6089 GetFullPathNameW(pwcsName
, MAX_PATH
, fullname
, NULL
);
6090 memcpy(newStorage
->filename
, fullname
, PROPERTY_NAME_BUFFER_LEN
);
6091 newStorage
->filename
[PROPERTY_NAME_BUFFER_LEN
-1] = '\0';
6094 * Get an "out" pointer for the caller.
6096 hr
= StorageBaseImpl_QueryInterface(
6097 (IStorage
*)newStorage
,
6098 (REFIID
)&IID_IStorage
,
6102 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
6106 /******************************************************************************
6107 * StgCreateDocfileOnILockBytes [OLE32.@]
6109 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
6113 IStorage
** ppstgOpen
)
6115 StorageImpl
* newStorage
= 0;
6119 * Validate the parameters
6121 if ((ppstgOpen
== 0) || (plkbyt
== 0))
6122 return STG_E_INVALIDPOINTER
;
6125 * Allocate and initialize the new IStorage object.
6127 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
6129 if (newStorage
== 0)
6130 return STG_E_INSUFFICIENTMEMORY
;
6132 hr
= StorageImpl_Construct(
6143 HeapFree(GetProcessHeap(), 0, newStorage
);
6148 * Get an "out" pointer for the caller.
6150 hr
= StorageBaseImpl_QueryInterface(
6151 (IStorage
*)newStorage
,
6152 (REFIID
)&IID_IStorage
,
6158 /******************************************************************************
6159 * StgOpenStorageOnILockBytes [OLE32.@]
6161 HRESULT WINAPI
StgOpenStorageOnILockBytes(
6163 IStorage
*pstgPriority
,
6167 IStorage
**ppstgOpen
)
6169 StorageImpl
* newStorage
= 0;
6173 * Perform a sanity check
6175 if ((plkbyt
== 0) || (ppstgOpen
== 0))
6176 return STG_E_INVALIDPOINTER
;
6179 * Validate the STGM flags
6181 if ( FAILED( validateSTGM(grfMode
) ))
6182 return STG_E_INVALIDFLAG
;
6185 * Initialize the "out" parameter.
6190 * Allocate and initialize the new IStorage object.
6192 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
6194 if (newStorage
== 0)
6195 return STG_E_INSUFFICIENTMEMORY
;
6197 hr
= StorageImpl_Construct(
6208 HeapFree(GetProcessHeap(), 0, newStorage
);
6213 * Get an "out" pointer for the caller.
6215 hr
= StorageBaseImpl_QueryInterface(
6216 (IStorage
*)newStorage
,
6217 (REFIID
)&IID_IStorage
,
6223 /******************************************************************************
6224 * StgSetTimes [ole32.@]
6225 * StgSetTimes [OLE32.@]
6229 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
6230 FILETIME
const *patime
, FILETIME
const *pmtime
)
6232 IStorage
*stg
= NULL
;
6235 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
6237 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
6241 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
6242 IStorage_Release(stg
);
6248 /******************************************************************************
6249 * StgIsStorageILockBytes [OLE32.@]
6251 * Determines if the ILockBytes contains a storage object.
6253 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
6256 ULARGE_INTEGER offset
;
6258 offset
.u
.HighPart
= 0;
6259 offset
.u
.LowPart
= 0;
6261 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
6263 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
6269 /******************************************************************************
6270 * WriteClassStg [OLE32.@]
6272 * This method will store the specified CLSID in the specified storage object
6274 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
6279 return E_INVALIDARG
;
6281 hRes
= IStorage_SetClass(pStg
, rclsid
);
6286 /***********************************************************************
6287 * ReadClassStg (OLE32.@)
6289 * This method reads the CLSID previously written to a storage object with
6290 * the WriteClassStg.
6293 * pstg [I] IStorage pointer
6294 * pclsid [O] Pointer to where the CLSID is written
6298 * Failure: HRESULT code.
6300 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
6305 TRACE("(%p, %p)\n", pstg
, pclsid
);
6307 if(!pstg
|| !pclsid
)
6308 return E_INVALIDARG
;
6311 * read a STATSTG structure (contains the clsid) from the storage
6313 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_DEFAULT
);
6316 *pclsid
=pstatstg
.clsid
;
6321 /***********************************************************************
6322 * OleLoadFromStream (OLE32.@)
6324 * This function loads an object from stream
6326 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
6330 LPPERSISTSTREAM xstm
;
6332 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
6334 res
=ReadClassStm(pStm
,&clsid
);
6335 if (!SUCCEEDED(res
))
6337 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
6338 if (!SUCCEEDED(res
))
6340 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
6341 if (!SUCCEEDED(res
)) {
6342 IUnknown_Release((IUnknown
*)*ppvObj
);
6345 res
=IPersistStream_Load(xstm
,pStm
);
6346 IPersistStream_Release(xstm
);
6347 /* FIXME: all refcounts ok at this point? I think they should be:
6350 * xstm : 0 (released)
6355 /***********************************************************************
6356 * OleSaveToStream (OLE32.@)
6358 * This function saves an object with the IPersistStream interface on it
6359 * to the specified stream.
6361 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
6367 TRACE("(%p,%p)\n",pPStm
,pStm
);
6369 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
6371 if (SUCCEEDED(res
)){
6373 res
=WriteClassStm(pStm
,&clsid
);
6377 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
6380 TRACE("Finished Save\n");
6384 /****************************************************************************
6385 * This method validate a STGM parameter that can contain the values below
6387 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6388 * The stgm values contained in 0xffff0000 are bitmasks.
6390 * STGM_DIRECT 0x00000000
6391 * STGM_TRANSACTED 0x00010000
6392 * STGM_SIMPLE 0x08000000
6394 * STGM_READ 0x00000000
6395 * STGM_WRITE 0x00000001
6396 * STGM_READWRITE 0x00000002
6398 * STGM_SHARE_DENY_NONE 0x00000040
6399 * STGM_SHARE_DENY_READ 0x00000030
6400 * STGM_SHARE_DENY_WRITE 0x00000020
6401 * STGM_SHARE_EXCLUSIVE 0x00000010
6403 * STGM_PRIORITY 0x00040000
6404 * STGM_DELETEONRELEASE 0x04000000
6406 * STGM_CREATE 0x00001000
6407 * STGM_CONVERT 0x00020000
6408 * STGM_FAILIFTHERE 0x00000000
6410 * STGM_NOSCRATCH 0x00100000
6411 * STGM_NOSNAPSHOT 0x00200000
6413 static HRESULT
validateSTGM(DWORD stgm
)
6415 DWORD access
= STGM_ACCESS_MODE(stgm
);
6416 DWORD share
= STGM_SHARE_MODE(stgm
);
6417 DWORD create
= STGM_CREATE_MODE(stgm
);
6419 if (stgm
&~STGM_KNOWN_FLAGS
)
6421 ERR("unknown flags %08x\n", stgm
);
6429 case STGM_READWRITE
:
6437 case STGM_SHARE_DENY_NONE
:
6438 case STGM_SHARE_DENY_READ
:
6439 case STGM_SHARE_DENY_WRITE
:
6440 case STGM_SHARE_EXCLUSIVE
:
6449 case STGM_FAILIFTHERE
:
6456 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6458 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
6462 * STGM_CREATE | STGM_CONVERT
6463 * if both are false, STGM_FAILIFTHERE is set to TRUE
6465 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
6469 * STGM_NOSCRATCH requires STGM_TRANSACTED
6471 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
6475 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6476 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6478 if ( (stgm
& STGM_NOSNAPSHOT
) &&
6479 (!(stgm
& STGM_TRANSACTED
) ||
6480 share
== STGM_SHARE_EXCLUSIVE
||
6481 share
== STGM_SHARE_DENY_WRITE
) )
6487 /****************************************************************************
6488 * GetShareModeFromSTGM
6490 * This method will return a share mode flag from a STGM value.
6491 * The STGM value is assumed valid.
6493 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
6495 switch (STGM_SHARE_MODE(stgm
))
6497 case STGM_SHARE_DENY_NONE
:
6498 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6499 case STGM_SHARE_DENY_READ
:
6500 return FILE_SHARE_WRITE
;
6501 case STGM_SHARE_DENY_WRITE
:
6502 return FILE_SHARE_READ
;
6503 case STGM_SHARE_EXCLUSIVE
:
6506 ERR("Invalid share mode!\n");
6511 /****************************************************************************
6512 * GetAccessModeFromSTGM
6514 * This method will return an access mode flag from a STGM value.
6515 * The STGM value is assumed valid.
6517 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
6519 switch (STGM_ACCESS_MODE(stgm
))
6522 return GENERIC_READ
;
6524 case STGM_READWRITE
:
6525 return GENERIC_READ
| GENERIC_WRITE
;
6527 ERR("Invalid access mode!\n");
6532 /****************************************************************************
6533 * GetCreationModeFromSTGM
6535 * This method will return a creation mode flag from a STGM value.
6536 * The STGM value is assumed valid.
6538 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
6540 switch(STGM_CREATE_MODE(stgm
))
6543 return CREATE_ALWAYS
;
6545 FIXME("STGM_CONVERT not implemented!\n");
6547 case STGM_FAILIFTHERE
:
6550 ERR("Invalid create mode!\n");
6556 /*************************************************************************
6557 * OLECONVERT_LoadOLE10 [Internal]
6559 * Loads the OLE10 STREAM to memory
6562 * pOleStream [I] The OLESTREAM
6563 * pData [I] Data Structure for the OLESTREAM Data
6567 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6568 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
6571 * This function is used by OleConvertOLESTREAMToIStorage only.
6573 * Memory allocated for pData must be freed by the caller
6575 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
6578 HRESULT hRes
= S_OK
;
6582 pData
->pData
= NULL
;
6583 pData
->pstrOleObjFileName
= (CHAR
*) NULL
;
6585 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
6588 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6589 if(dwSize
!= sizeof(pData
->dwOleID
))
6591 hRes
= CONVERT10_E_OLESTREAM_GET
;
6593 else if(pData
->dwOleID
!= OLESTREAM_ID
)
6595 hRes
= CONVERT10_E_OLESTREAM_FMT
;
6606 /* Get the TypeID...more info needed for this field */
6607 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6608 if(dwSize
!= sizeof(pData
->dwTypeID
))
6610 hRes
= CONVERT10_E_OLESTREAM_GET
;
6615 if(pData
->dwTypeID
!= 0)
6617 /* Get the length of the OleTypeName */
6618 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6619 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6621 hRes
= CONVERT10_E_OLESTREAM_GET
;
6626 if(pData
->dwOleTypeNameLength
> 0)
6628 /* Get the OleTypeName */
6629 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6630 if(dwSize
!= pData
->dwOleTypeNameLength
)
6632 hRes
= CONVERT10_E_OLESTREAM_GET
;
6638 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
6639 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
6641 hRes
= CONVERT10_E_OLESTREAM_GET
;
6645 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
6646 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
6647 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
6648 if(pData
->pstrOleObjFileName
)
6650 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)(pData
->pstrOleObjFileName
),pData
->dwOleObjFileNameLength
);
6651 if(dwSize
!= pData
->dwOleObjFileNameLength
)
6653 hRes
= CONVERT10_E_OLESTREAM_GET
;
6657 hRes
= CONVERT10_E_OLESTREAM_GET
;
6662 /* Get the Width of the Metafile */
6663 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6664 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6666 hRes
= CONVERT10_E_OLESTREAM_GET
;
6670 /* Get the Height of the Metafile */
6671 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6672 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6674 hRes
= CONVERT10_E_OLESTREAM_GET
;
6680 /* Get the Length of the Data */
6681 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6682 if(dwSize
!= sizeof(pData
->dwDataLength
))
6684 hRes
= CONVERT10_E_OLESTREAM_GET
;
6688 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
6690 if(!bStrem1
) /* if it is a second OLE stream data */
6692 pData
->dwDataLength
-= 8;
6693 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)(pData
->strUnknown
), sizeof(pData
->strUnknown
));
6694 if(dwSize
!= sizeof(pData
->strUnknown
))
6696 hRes
= CONVERT10_E_OLESTREAM_GET
;
6702 if(pData
->dwDataLength
> 0)
6704 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
6706 /* Get Data (ex. IStorage, Metafile, or BMP) */
6709 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
6710 if(dwSize
!= pData
->dwDataLength
)
6712 hRes
= CONVERT10_E_OLESTREAM_GET
;
6717 hRes
= CONVERT10_E_OLESTREAM_GET
;
6726 /*************************************************************************
6727 * OLECONVERT_SaveOLE10 [Internal]
6729 * Saves the OLE10 STREAM From memory
6732 * pData [I] Data Structure for the OLESTREAM Data
6733 * pOleStream [I] The OLESTREAM to save
6737 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6740 * This function is used by OleConvertIStorageToOLESTREAM only.
6743 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
6746 HRESULT hRes
= S_OK
;
6750 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6751 if(dwSize
!= sizeof(pData
->dwOleID
))
6753 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6758 /* Set the TypeID */
6759 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6760 if(dwSize
!= sizeof(pData
->dwTypeID
))
6762 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6766 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
6768 /* Set the Length of the OleTypeName */
6769 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6770 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6772 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6777 if(pData
->dwOleTypeNameLength
> 0)
6779 /* Set the OleTypeName */
6780 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6781 if(dwSize
!= pData
->dwOleTypeNameLength
)
6783 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6790 /* Set the width of the Metafile */
6791 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6792 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6794 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6800 /* Set the height of the Metafile */
6801 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6802 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6804 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6810 /* Set the length of the Data */
6811 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6812 if(dwSize
!= sizeof(pData
->dwDataLength
))
6814 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6820 if(pData
->dwDataLength
> 0)
6822 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6823 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
6824 if(dwSize
!= pData
->dwDataLength
)
6826 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6834 /*************************************************************************
6835 * OLECONVERT_GetOLE20FromOLE10[Internal]
6837 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6838 * opens it, and copies the content to the dest IStorage for
6839 * OleConvertOLESTREAMToIStorage
6843 * pDestStorage [I] The IStorage to copy the data to
6844 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6845 * nBufferLength [I] The size of the buffer
6854 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
6858 IStorage
*pTempStorage
;
6859 DWORD dwNumOfBytesWritten
;
6860 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6861 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6863 /* Create a temp File */
6864 GetTempPathW(MAX_PATH
, wstrTempDir
);
6865 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6866 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
6868 if(hFile
!= INVALID_HANDLE_VALUE
)
6870 /* Write IStorage Data to File */
6871 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
6874 /* Open and copy temp storage to the Dest Storage */
6875 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
6878 hRes
= StorageImpl_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
6879 StorageBaseImpl_Release(pTempStorage
);
6881 DeleteFileW(wstrTempFile
);
6886 /*************************************************************************
6887 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6889 * Saves the OLE10 STREAM From memory
6892 * pStorage [I] The Src IStorage to copy
6893 * pData [I] The Dest Memory to write to.
6896 * The size in bytes allocated for pData
6899 * Memory allocated for pData must be freed by the caller
6901 * Used by OleConvertIStorageToOLESTREAM only.
6904 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
6908 DWORD nDataLength
= 0;
6909 IStorage
*pTempStorage
;
6910 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6911 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6915 /* Create temp Storage */
6916 GetTempPathW(MAX_PATH
, wstrTempDir
);
6917 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6918 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
6922 /* Copy Src Storage to the Temp Storage */
6923 StorageImpl_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
6924 StorageBaseImpl_Release(pTempStorage
);
6926 /* Open Temp Storage as a file and copy to memory */
6927 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
6928 if(hFile
!= INVALID_HANDLE_VALUE
)
6930 nDataLength
= GetFileSize(hFile
, NULL
);
6931 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
6932 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
6935 DeleteFileW(wstrTempFile
);
6940 /*************************************************************************
6941 * OLECONVERT_CreateOleStream [Internal]
6943 * Creates the "\001OLE" stream in the IStorage if necessary.
6946 * pStorage [I] Dest storage to create the stream in
6952 * This function is used by OleConvertOLESTREAMToIStorage only.
6954 * This stream is still unknown, MS Word seems to have extra data
6955 * but since the data is stored in the OLESTREAM there should be
6956 * no need to recreate the stream. If the stream is manually
6957 * deleted it will create it with this default data.
6960 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
6964 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
6965 BYTE pOleStreamHeader
[] =
6967 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6968 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6969 0x00, 0x00, 0x00, 0x00
6972 /* Create stream if not present */
6973 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
6974 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
6978 /* Write default Data */
6979 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
6980 IStream_Release(pStream
);
6984 /* write a string to a stream, preceded by its length */
6985 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
6992 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
6993 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
6998 str
= CoTaskMemAlloc( len
);
6999 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
7000 r
= IStream_Write( stm
, str
, len
, NULL
);
7001 CoTaskMemFree( str
);
7005 /* read a string preceded by its length from a stream */
7006 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
7009 DWORD len
, count
= 0;
7013 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
7016 if( count
!= sizeof(len
) )
7017 return E_OUTOFMEMORY
;
7019 TRACE("%d bytes\n",len
);
7021 str
= CoTaskMemAlloc( len
);
7023 return E_OUTOFMEMORY
;
7025 r
= IStream_Read( stm
, str
, len
, &count
);
7030 CoTaskMemFree( str
);
7031 return E_OUTOFMEMORY
;
7034 TRACE("Read string %s\n",debugstr_an(str
,len
));
7036 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
7037 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
7039 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
7040 CoTaskMemFree( str
);
7048 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
7049 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
7053 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7055 static const BYTE unknown1
[12] =
7056 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7057 0xFF, 0xFF, 0xFF, 0xFF};
7058 static const BYTE unknown2
[16] =
7059 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7060 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7062 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
7063 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
7064 debugstr_w(szProgIDName
));
7066 /* Create a CompObj stream if it doesn't exist */
7067 r
= IStorage_CreateStream(pstg
, szwStreamName
,
7068 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
7072 /* Write CompObj Structure to stream */
7073 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
7075 if( SUCCEEDED( r
) )
7076 r
= WriteClassStm( pstm
, clsid
);
7078 if( SUCCEEDED( r
) )
7079 r
= STREAM_WriteString( pstm
, lpszUserType
);
7080 if( SUCCEEDED( r
) )
7081 r
= STREAM_WriteString( pstm
, szClipName
);
7082 if( SUCCEEDED( r
) )
7083 r
= STREAM_WriteString( pstm
, szProgIDName
);
7084 if( SUCCEEDED( r
) )
7085 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
7087 IStream_Release( pstm
);
7092 /***********************************************************************
7093 * WriteFmtUserTypeStg (OLE32.@)
7095 HRESULT WINAPI
WriteFmtUserTypeStg(
7096 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
7099 WCHAR szwClipName
[0x40];
7100 CLSID clsid
= CLSID_NULL
;
7101 LPWSTR wstrProgID
= NULL
;
7104 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
7106 /* get the clipboard format name */
7107 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
7110 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
7112 /* FIXME: There's room to save a CLSID and its ProgID, but
7113 the CLSID is not looked up in the registry and in all the
7114 tests I wrote it was CLSID_NULL. Where does it come from?
7117 /* get the real program ID. This may fail, but that's fine */
7118 ProgIDFromCLSID(&clsid
, &wstrProgID
);
7120 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
7122 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
7123 lpszUserType
, szwClipName
, wstrProgID
);
7125 CoTaskMemFree(wstrProgID
);
7131 /******************************************************************************
7132 * ReadFmtUserTypeStg [OLE32.@]
7134 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
7138 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
7139 unsigned char unknown1
[12];
7140 unsigned char unknown2
[16];
7142 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
7145 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
7147 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
7148 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
7151 WARN("Failed to open stream r = %08x\n", r
);
7155 /* read the various parts of the structure */
7156 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
7157 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
7159 r
= ReadClassStm( stm
, &clsid
);
7163 r
= STREAM_ReadString( stm
, &szCLSIDName
);
7167 r
= STREAM_ReadString( stm
, &szOleTypeName
);
7171 r
= STREAM_ReadString( stm
, &szProgIDName
);
7175 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
7176 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
7179 /* ok, success... now we just need to store what we found */
7181 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
7182 CoTaskMemFree( szOleTypeName
);
7184 if( lplpszUserType
)
7185 *lplpszUserType
= szCLSIDName
;
7186 CoTaskMemFree( szProgIDName
);
7189 IStream_Release( stm
);
7195 /*************************************************************************
7196 * OLECONVERT_CreateCompObjStream [Internal]
7198 * Creates a "\001CompObj" is the destination IStorage if necessary.
7201 * pStorage [I] The dest IStorage to create the CompObj Stream
7203 * strOleTypeName [I] The ProgID
7207 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7210 * This function is used by OleConvertOLESTREAMToIStorage only.
7212 * The stream data is stored in the OLESTREAM and there should be
7213 * no need to recreate the stream. If the stream is manually
7214 * deleted it will attempt to create it by querying the registry.
7218 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
7221 HRESULT hStorageRes
, hRes
= S_OK
;
7222 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
7223 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7224 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
7226 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7227 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
7229 /* Initialize the CompObj structure */
7230 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
7231 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
7232 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
7235 /* Create a CompObj stream if it doesn't exist */
7236 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7237 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7238 if(hStorageRes
== S_OK
)
7240 /* copy the OleTypeName to the compobj struct */
7241 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
7242 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
7244 /* copy the OleTypeName to the compobj struct */
7245 /* Note: in the test made, these were Identical */
7246 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
7247 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
7250 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
7251 bufferW
, OLESTREAM_MAX_STR_LEN
);
7252 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
7258 /* Get the CLSID Default Name from the Registry */
7259 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
7260 if(hErr
== ERROR_SUCCESS
)
7262 char strTemp
[OLESTREAM_MAX_STR_LEN
];
7263 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
7264 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
7265 if(hErr
== ERROR_SUCCESS
)
7267 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
7273 /* Write CompObj Structure to stream */
7274 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
7276 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
7278 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
7279 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
7281 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
7283 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
7284 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
7286 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
7288 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
7289 if(IStorageCompObj
.dwProgIDNameLength
> 0)
7291 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
7293 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
7294 IStream_Release(pStream
);
7300 /*************************************************************************
7301 * OLECONVERT_CreateOlePresStream[Internal]
7303 * Creates the "\002OlePres000" Stream with the Metafile data
7306 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7307 * dwExtentX [I] Width of the Metafile
7308 * dwExtentY [I] Height of the Metafile
7309 * pData [I] Metafile data
7310 * dwDataLength [I] Size of the Metafile data
7314 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7317 * This function is used by OleConvertOLESTREAMToIStorage only.
7320 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
7324 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7325 BYTE pOlePresStreamHeader
[] =
7327 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7328 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7329 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7330 0x00, 0x00, 0x00, 0x00
7333 BYTE pOlePresStreamHeaderEmpty
[] =
7335 0x00, 0x00, 0x00, 0x00,
7336 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7337 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7338 0x00, 0x00, 0x00, 0x00
7341 /* Create the OlePres000 Stream */
7342 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7343 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7348 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
7350 memset(&OlePres
, 0, sizeof(OlePres
));
7351 /* Do we have any metafile data to save */
7352 if(dwDataLength
> 0)
7354 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
7355 nHeaderSize
= sizeof(pOlePresStreamHeader
);
7359 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
7360 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
7362 /* Set width and height of the metafile */
7363 OlePres
.dwExtentX
= dwExtentX
;
7364 OlePres
.dwExtentY
= -dwExtentY
;
7366 /* Set Data and Length */
7367 if(dwDataLength
> sizeof(METAFILEPICT16
))
7369 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
7370 OlePres
.pData
= &(pData
[8]);
7372 /* Save OlePres000 Data to Stream */
7373 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
7374 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
7375 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
7376 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
7377 if(OlePres
.dwSize
> 0)
7379 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
7381 IStream_Release(pStream
);
7385 /*************************************************************************
7386 * OLECONVERT_CreateOle10NativeStream [Internal]
7388 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7391 * pStorage [I] Dest storage to create the stream in
7392 * pData [I] Ole10 Native Data (ex. bmp)
7393 * dwDataLength [I] Size of the Ole10 Native Data
7399 * This function is used by OleConvertOLESTREAMToIStorage only.
7401 * Might need to verify the data and return appropriate error message
7404 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
7408 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7410 /* Create the Ole10Native Stream */
7411 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7412 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7416 /* Write info to stream */
7417 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
7418 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
7419 IStream_Release(pStream
);
7424 /*************************************************************************
7425 * OLECONVERT_GetOLE10ProgID [Internal]
7427 * Finds the ProgID (or OleTypeID) from the IStorage
7430 * pStorage [I] The Src IStorage to get the ProgID
7431 * strProgID [I] the ProgID string to get
7432 * dwSize [I] the size of the string
7436 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7439 * This function is used by OleConvertIStorageToOLESTREAM only.
7443 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
7447 LARGE_INTEGER iSeekPos
;
7448 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
7449 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7451 /* Open the CompObj Stream */
7452 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7453 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7457 /*Get the OleType from the CompObj Stream */
7458 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
7459 iSeekPos
.u
.HighPart
= 0;
7461 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7462 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
7463 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
7464 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7465 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
7466 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
7467 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7469 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
7472 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
7474 IStream_Release(pStream
);
7479 LPOLESTR wstrProgID
;
7481 /* Get the OleType from the registry */
7482 REFCLSID clsid
= &(stat
.clsid
);
7483 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
7484 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
7487 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
7494 /*************************************************************************
7495 * OLECONVERT_GetOle10PresData [Internal]
7497 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7500 * pStorage [I] Src IStroage
7501 * pOleStream [I] Dest OleStream Mem Struct
7507 * This function is used by OleConvertIStorageToOLESTREAM only.
7509 * Memory allocated for pData must be freed by the caller
7513 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7518 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7520 /* Initialize Default data for OLESTREAM */
7521 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7522 pOleStreamData
[0].dwTypeID
= 2;
7523 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7524 pOleStreamData
[1].dwTypeID
= 0;
7525 pOleStreamData
[0].dwMetaFileWidth
= 0;
7526 pOleStreamData
[0].dwMetaFileHeight
= 0;
7527 pOleStreamData
[0].pData
= NULL
;
7528 pOleStreamData
[1].pData
= NULL
;
7530 /* Open Ole10Native Stream */
7531 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7532 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7536 /* Read Size and Data */
7537 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
7538 if(pOleStreamData
->dwDataLength
> 0)
7540 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
7541 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
7543 IStream_Release(pStream
);
7549 /*************************************************************************
7550 * OLECONVERT_GetOle20PresData[Internal]
7552 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7555 * pStorage [I] Src IStroage
7556 * pOleStreamData [I] Dest OleStream Mem Struct
7562 * This function is used by OleConvertIStorageToOLESTREAM only.
7564 * Memory allocated for pData must be freed by the caller
7566 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7570 OLECONVERT_ISTORAGE_OLEPRES olePress
;
7571 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7573 /* Initialize Default data for OLESTREAM */
7574 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7575 pOleStreamData
[0].dwTypeID
= 2;
7576 pOleStreamData
[0].dwMetaFileWidth
= 0;
7577 pOleStreamData
[0].dwMetaFileHeight
= 0;
7578 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
7579 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7580 pOleStreamData
[1].dwTypeID
= 0;
7581 pOleStreamData
[1].dwOleTypeNameLength
= 0;
7582 pOleStreamData
[1].strOleTypeName
[0] = 0;
7583 pOleStreamData
[1].dwMetaFileWidth
= 0;
7584 pOleStreamData
[1].dwMetaFileHeight
= 0;
7585 pOleStreamData
[1].pData
= NULL
;
7586 pOleStreamData
[1].dwDataLength
= 0;
7589 /* Open OlePress000 stream */
7590 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7591 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7594 LARGE_INTEGER iSeekPos
;
7595 METAFILEPICT16 MetaFilePict
;
7596 static const char strMetafilePictName
[] = "METAFILEPICT";
7598 /* Set the TypeID for a Metafile */
7599 pOleStreamData
[1].dwTypeID
= 5;
7601 /* Set the OleTypeName to Metafile */
7602 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
7603 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
7605 iSeekPos
.u
.HighPart
= 0;
7606 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
7608 /* Get Presentation Data */
7609 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7610 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
7611 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
7612 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
7614 /*Set width and Height */
7615 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
7616 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
7617 if(olePress
.dwSize
> 0)
7620 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
7622 /* Set MetaFilePict struct */
7623 MetaFilePict
.mm
= 8;
7624 MetaFilePict
.xExt
= olePress
.dwExtentX
;
7625 MetaFilePict
.yExt
= olePress
.dwExtentY
;
7626 MetaFilePict
.hMF
= 0;
7628 /* Get Metafile Data */
7629 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
7630 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
7631 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
7633 IStream_Release(pStream
);
7637 /*************************************************************************
7638 * OleConvertOLESTREAMToIStorage [OLE32.@]
7643 * DVTARGETDEVICE parameter is not handled
7644 * Still unsure of some mem fields for OLE 10 Stream
7645 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7646 * and "\001OLE" streams
7649 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
7650 LPOLESTREAM pOleStream
,
7652 const DVTARGETDEVICE
* ptd
)
7656 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7658 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
7660 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7664 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7667 if(pstg
== NULL
|| pOleStream
== NULL
)
7669 hRes
= E_INVALIDARG
;
7674 /* Load the OLESTREAM to Memory */
7675 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
7680 /* Load the OLESTREAM to Memory (part 2)*/
7681 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
7687 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
7689 /* Do we have the IStorage Data in the OLESTREAM */
7690 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
7692 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7693 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
7697 /* It must be an original OLE 1.0 source */
7698 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7703 /* It must be an original OLE 1.0 source */
7704 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7707 /* Create CompObj Stream if necessary */
7708 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
7711 /*Create the Ole Stream if necessary */
7712 OLECONVERT_CreateOleStream(pstg
);
7717 /* Free allocated memory */
7718 for(i
=0; i
< 2; i
++)
7720 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7721 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
7722 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
7727 /*************************************************************************
7728 * OleConvertIStorageToOLESTREAM [OLE32.@]
7735 * Still unsure of some mem fields for OLE 10 Stream
7736 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7737 * and "\001OLE" streams.
7740 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
7742 LPOLESTREAM pOleStream
)
7745 HRESULT hRes
= S_OK
;
7747 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7748 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7750 TRACE("%p %p\n", pstg
, pOleStream
);
7752 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7754 if(pstg
== NULL
|| pOleStream
== NULL
)
7756 hRes
= E_INVALIDARG
;
7760 /* Get the ProgID */
7761 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
7762 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
7766 /* Was it originally Ole10 */
7767 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7770 IStream_Release(pStream
);
7771 /* Get Presentation Data for Ole10Native */
7772 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
7776 /* Get Presentation Data (OLE20) */
7777 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
7780 /* Save OLESTREAM */
7781 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
7784 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
7789 /* Free allocated memory */
7790 for(i
=0; i
< 2; i
++)
7792 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7798 /***********************************************************************
7799 * GetConvertStg (OLE32.@)
7801 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
7802 FIXME("unimplemented stub!\n");
7806 /******************************************************************************
7807 * StgIsStorageFile [OLE32.@]
7808 * Verify if the file contains a storage object
7814 * S_OK if file has magic bytes as a storage object
7815 * S_FALSE if file is not storage
7818 StgIsStorageFile(LPCOLESTR fn
)
7824 TRACE("%s\n", debugstr_w(fn
));
7825 hf
= CreateFileW(fn
, GENERIC_READ
,
7826 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
7827 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
7829 if (hf
== INVALID_HANDLE_VALUE
)
7830 return STG_E_FILENOTFOUND
;
7832 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
7834 WARN(" unable to read file\n");
7841 if (bytes_read
!= 8) {
7842 WARN(" too short\n");
7846 if (!memcmp(magic
,STORAGE_magic
,8)) {
7851 WARN(" -> Invalid header.\n");
7855 /***********************************************************************
7856 * WriteClassStm (OLE32.@)
7858 * Writes a CLSID to a stream.
7861 * pStm [I] Stream to write to.
7862 * rclsid [I] CLSID to write.
7866 * Failure: HRESULT code.
7868 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
7870 TRACE("(%p,%p)\n",pStm
,rclsid
);
7872 if (!pStm
|| !rclsid
)
7873 return E_INVALIDARG
;
7875 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
7878 /***********************************************************************
7879 * ReadClassStm (OLE32.@)
7881 * Reads a CLSID from a stream.
7884 * pStm [I] Stream to read from.
7885 * rclsid [O] CLSID to read.
7889 * Failure: HRESULT code.
7891 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
7896 TRACE("(%p,%p)\n",pStm
,pclsid
);
7898 if (!pStm
|| !pclsid
)
7899 return E_INVALIDARG
;
7901 /* clear the output args */
7902 *pclsid
= CLSID_NULL
;
7904 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
7909 if (nbByte
!= sizeof(CLSID
))
7910 return STG_E_READFAULT
;