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.
33 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/istorage_compound_file_implementation.asp
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
50 #include "wine/unicode.h"
51 #include "wine/debug.h"
53 #include "storage32.h"
54 #include "ole2.h" /* For Write/ReadClassStm */
57 #include "wine/wingdi16.h"
59 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
61 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
62 #define OLESTREAM_ID 0x501
63 #define OLESTREAM_MAX_STR_LEN 255
66 * These are signatures to detect the type of Document file.
68 static const BYTE STORAGE_magic
[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
69 static const BYTE STORAGE_oldmagic
[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
71 static const char rootPropertyName
[] = "Root Entry";
73 /****************************************************************************
74 * Storage32InternalImpl definitions.
76 * Definition of the implementation structure for the IStorage32 interface.
77 * This one implements the IStorage32 interface for storage that are
78 * inside another storage.
80 struct StorageInternalImpl
82 struct StorageBaseImpl base
;
84 * There is no specific data for this class.
87 typedef struct StorageInternalImpl StorageInternalImpl
;
89 /* Method definitions for the Storage32InternalImpl class. */
90 static StorageInternalImpl
* StorageInternalImpl_Construct(StorageImpl
* ancestorStorage
,
91 DWORD openFlags
, ULONG rootTropertyIndex
);
92 static void StorageImpl_Destroy(StorageBaseImpl
* iface
);
93 static BOOL
StorageImpl_ReadBigBlock(StorageImpl
* This
, ULONG blockIndex
, void* buffer
);
94 static BOOL
StorageImpl_WriteBigBlock(StorageImpl
* This
, ULONG blockIndex
, const void* buffer
);
95 static void StorageImpl_SetNextBlockInChain(StorageImpl
* This
, ULONG blockIndex
, ULONG nextBlock
);
96 static HRESULT
StorageImpl_LoadFileHeader(StorageImpl
* This
);
97 static void StorageImpl_SaveFileHeader(StorageImpl
* This
);
99 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
);
100 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
);
101 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
);
102 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
);
103 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
);
105 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
);
106 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
);
107 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
);
109 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
);
110 static BOOL
StorageImpl_WriteDWordToBigBlock( StorageImpl
* This
,
111 ULONG blockIndex
, ULONG offset
, DWORD value
);
112 static BOOL
StorageImpl_ReadDWordFromBigBlock( StorageImpl
* This
,
113 ULONG blockIndex
, ULONG offset
, DWORD
* value
);
115 /* OLESTREAM memory structure to use for Get and Put Routines */
116 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
121 DWORD dwOleTypeNameLength
;
122 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
123 CHAR
*pstrOleObjFileName
;
124 DWORD dwOleObjFileNameLength
;
125 DWORD dwMetaFileWidth
;
126 DWORD dwMetaFileHeight
;
127 CHAR strUnknown
[8]; /* don't know what is this 8 byts information in OLE stream. */
130 }OLECONVERT_OLESTREAM_DATA
;
132 /* CompObj Stream structure */
133 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
138 DWORD dwCLSIDNameLength
;
139 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
140 DWORD dwOleTypeNameLength
;
141 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
142 DWORD dwProgIDNameLength
;
143 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
145 }OLECONVERT_ISTORAGE_COMPOBJ
;
148 /* Ole Presention Stream structure */
149 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
157 }OLECONVERT_ISTORAGE_OLEPRES
;
161 /***********************************************************************
162 * Forward declaration of internal functions used by the method DestroyElement
164 static HRESULT
deleteStorageProperty(
165 StorageImpl
*parentStorage
,
166 ULONG foundPropertyIndexToDelete
,
167 StgProperty propertyToDelete
);
169 static HRESULT
deleteStreamProperty(
170 StorageImpl
*parentStorage
,
171 ULONG foundPropertyIndexToDelete
,
172 StgProperty propertyToDelete
);
174 static HRESULT
findPlaceholder(
175 StorageImpl
*storage
,
176 ULONG propertyIndexToStore
,
177 ULONG storagePropertyIndex
,
180 static HRESULT
adjustPropertyChain(
182 StgProperty propertyToDelete
,
183 StgProperty parentProperty
,
184 ULONG parentPropertyId
,
187 /***********************************************************************
188 * Declaration of the functions used to manipulate StgProperty
191 static ULONG
getFreeProperty(
192 StorageImpl
*storage
);
194 static void updatePropertyChain(
195 StorageImpl
*storage
,
196 ULONG newPropertyIndex
,
197 StgProperty newProperty
);
199 static LONG
propertyNameCmp(
200 const OLECHAR
*newProperty
,
201 const OLECHAR
*currentProperty
);
204 /***********************************************************************
205 * Declaration of miscellaneous functions...
207 static HRESULT
validateSTGM(DWORD stgmValue
);
209 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
210 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
211 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
213 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
216 /****************************************************************************
217 * IEnumSTATSTGImpl definitions.
219 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
220 * This class allows iterating through the content of a storage and to find
221 * specific items inside it.
223 struct IEnumSTATSTGImpl
225 const IEnumSTATSTGVtbl
*lpVtbl
; /* Needs to be the first item in the struct
226 * since we want to cast this in an IEnumSTATSTG pointer */
228 LONG ref
; /* Reference count */
229 StorageImpl
* parentStorage
; /* Reference to the parent storage */
230 ULONG firstPropertyNode
; /* Index of the root of the storage to enumerate */
233 * The current implementation of the IEnumSTATSTGImpl class uses a stack
234 * to walk the property sets to get the content of a storage. This stack
235 * is implemented by the following 3 data members
241 #define ENUMSTATSGT_SIZE_INCREMENT 10
245 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageImpl
* This
, ULONG firstPropertyNode
);
246 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
);
247 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl
* This
, ULONG nodeToPush
);
248 static ULONG
IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl
* This
, BOOL remove
);
249 static ULONG
IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl
* This
, const OLECHAR
* lpszPropName
,
250 StgProperty
* buffer
);
251 static INT
IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl
*This
, ULONG childProperty
,
252 StgProperty
*currentProperty
, ULONG
*propertyId
);
254 /************************************************************************
258 static ULONG
BLOCK_GetBigBlockOffset(ULONG index
)
260 if (index
== 0xffffffff)
265 return index
* BIG_BLOCK_SIZE
;
268 /************************************************************************
269 ** Storage32BaseImpl implementatiion
271 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
272 ULARGE_INTEGER offset
,
277 return BIGBLOCKFILE_ReadAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesRead
);
280 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
281 ULARGE_INTEGER offset
,
286 return BIGBLOCKFILE_WriteAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesWritten
);
289 /************************************************************************
290 * Storage32BaseImpl_QueryInterface (IUnknown)
292 * This method implements the common QueryInterface for all IStorage32
293 * implementations contained in this file.
295 * See Windows documentation for more details on IUnknown methods.
297 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
302 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
304 * Perform a sanity check on the parameters.
306 if ( (This
==0) || (ppvObject
==0) )
310 * Initialize the return parameter.
315 * Compare the riid with the interface IDs implemented by this object.
317 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
318 IsEqualGUID(&IID_IStorage
, riid
))
320 *ppvObject
= (IStorage
*)This
;
322 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
324 *ppvObject
= (IStorage
*)&This
->pssVtbl
;
328 * Check that we obtained an interface.
331 return E_NOINTERFACE
;
334 * Query Interface always increases the reference count by one when it is
337 IStorage_AddRef(iface
);
342 /************************************************************************
343 * Storage32BaseImpl_AddRef (IUnknown)
345 * This method implements the common AddRef for all IStorage32
346 * implementations contained in this file.
348 * See Windows documentation for more details on IUnknown methods.
350 static ULONG WINAPI
StorageBaseImpl_AddRef(
353 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
354 ULONG ref
= InterlockedIncrement(&This
->ref
);
356 TRACE("(%p) AddRef to %d\n", This
, ref
);
361 /************************************************************************
362 * Storage32BaseImpl_Release (IUnknown)
364 * This method implements the common Release for all IStorage32
365 * implementations contained in this file.
367 * See Windows documentation for more details on IUnknown methods.
369 static ULONG WINAPI
StorageBaseImpl_Release(
372 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
374 * Decrease the reference count on this object.
376 ULONG ref
= InterlockedDecrement(&This
->ref
);
378 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
381 * If the reference count goes down to 0, perform suicide.
386 * Since we are using a system of base-classes, we want to call the
387 * destructor of the appropriate derived class. To do this, we are
388 * using virtual functions to implement the destructor.
390 This
->v_destructor(This
);
396 /************************************************************************
397 * Storage32BaseImpl_OpenStream (IStorage)
399 * This method will open the specified stream object from the current storage.
401 * See Windows documentation for more details on IStorage methods.
403 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
405 const OLECHAR
* pwcsName
, /* [string][in] */
406 void* reserved1
, /* [unique][in] */
407 DWORD grfMode
, /* [in] */
408 DWORD reserved2
, /* [in] */
409 IStream
** ppstm
) /* [out] */
411 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
412 IEnumSTATSTGImpl
* propertyEnumeration
;
413 StgStreamImpl
* newStream
;
414 StgProperty currentProperty
;
415 ULONG foundPropertyIndex
;
416 HRESULT res
= STG_E_UNKNOWN
;
418 TRACE("(%p, %s, %p, %x, %d, %p)\n",
419 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
422 * Perform a sanity check on the parameters.
424 if ( (pwcsName
==NULL
) || (ppstm
==0) )
431 * Initialize the out parameter
436 * Validate the STGM flags
438 if ( FAILED( validateSTGM(grfMode
) ) ||
439 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
441 res
= STG_E_INVALIDFLAG
;
448 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
450 res
= STG_E_INVALIDFUNCTION
;
455 * Check that we're compatible with the parent's storage mode, but
456 * only if we are not in transacted mode
458 if(!(This
->ancestorStorage
->base
.openFlags
& STGM_TRANSACTED
)) {
459 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
461 res
= STG_E_ACCESSDENIED
;
467 * Create a property enumeration to search the properties
469 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
470 This
->ancestorStorage
,
471 This
->rootPropertySetIndex
);
474 * Search the enumeration for the property with the given name
476 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
482 * Delete the property enumeration since we don't need it anymore
484 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
487 * If it was found, construct the stream object and return a pointer to it.
489 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
490 (currentProperty
.propertyType
==PROPTYPE_STREAM
) )
492 newStream
= StgStreamImpl_Construct(This
, grfMode
, foundPropertyIndex
);
496 newStream
->grfMode
= grfMode
;
497 *ppstm
= (IStream
*)newStream
;
500 * Since we are returning a pointer to the interface, we have to
501 * nail down the reference.
503 IStream_AddRef(*ppstm
);
513 res
= STG_E_FILENOTFOUND
;
517 TRACE("<-- IStream %p\n", *ppstm
);
518 TRACE("<-- %08x\n", res
);
522 /************************************************************************
523 * Storage32BaseImpl_OpenStorage (IStorage)
525 * This method will open a new storage object from the current storage.
527 * See Windows documentation for more details on IStorage methods.
529 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
531 const OLECHAR
* pwcsName
, /* [string][unique][in] */
532 IStorage
* pstgPriority
, /* [unique][in] */
533 DWORD grfMode
, /* [in] */
534 SNB snbExclude
, /* [unique][in] */
535 DWORD reserved
, /* [in] */
536 IStorage
** ppstg
) /* [out] */
538 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
539 StorageInternalImpl
* newStorage
;
540 IEnumSTATSTGImpl
* propertyEnumeration
;
541 StgProperty currentProperty
;
542 ULONG foundPropertyIndex
;
543 HRESULT res
= STG_E_UNKNOWN
;
545 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
546 iface
, debugstr_w(pwcsName
), pstgPriority
,
547 grfMode
, snbExclude
, reserved
, ppstg
);
550 * Perform a sanity check on the parameters.
552 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
559 if (snbExclude
!= NULL
)
561 res
= STG_E_INVALIDPARAMETER
;
566 * Validate the STGM flags
568 if ( FAILED( validateSTGM(grfMode
) ))
570 res
= STG_E_INVALIDFLAG
;
577 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
578 (grfMode
& STGM_DELETEONRELEASE
) ||
579 (grfMode
& STGM_PRIORITY
) )
581 res
= STG_E_INVALIDFUNCTION
;
586 * Check that we're compatible with the parent's storage mode,
587 * but only if we are not transacted
589 if(!(This
->ancestorStorage
->base
.openFlags
& STGM_TRANSACTED
)) {
590 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
592 res
= STG_E_ACCESSDENIED
;
598 * Initialize the out parameter
603 * Create a property enumeration to search the properties
605 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
606 This
->ancestorStorage
,
607 This
->rootPropertySetIndex
);
610 * Search the enumeration for the property with the given name
612 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
618 * Delete the property enumeration since we don't need it anymore
620 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
623 * If it was found, construct the stream object and return a pointer to it.
625 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
626 (currentProperty
.propertyType
==PROPTYPE_STORAGE
) )
629 * Construct a new Storage object
631 newStorage
= StorageInternalImpl_Construct(
632 This
->ancestorStorage
,
638 *ppstg
= (IStorage
*)newStorage
;
641 * Since we are returning a pointer to the interface,
642 * we have to nail down the reference.
644 StorageBaseImpl_AddRef(*ppstg
);
650 res
= STG_E_INSUFFICIENTMEMORY
;
654 res
= STG_E_FILENOTFOUND
;
657 TRACE("<-- %08x\n", res
);
661 /************************************************************************
662 * Storage32BaseImpl_EnumElements (IStorage)
664 * This method will create an enumerator object that can be used to
665 * retrieve informatino about all the properties in the storage object.
667 * See Windows documentation for more details on IStorage methods.
669 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
671 DWORD reserved1
, /* [in] */
672 void* reserved2
, /* [size_is][unique][in] */
673 DWORD reserved3
, /* [in] */
674 IEnumSTATSTG
** ppenum
) /* [out] */
676 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
677 IEnumSTATSTGImpl
* newEnum
;
679 TRACE("(%p, %d, %p, %d, %p)\n",
680 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
683 * Perform a sanity check on the parameters.
685 if ( (This
==0) || (ppenum
==0))
689 * Construct the enumerator.
691 newEnum
= IEnumSTATSTGImpl_Construct(
692 This
->ancestorStorage
,
693 This
->rootPropertySetIndex
);
697 *ppenum
= (IEnumSTATSTG
*)newEnum
;
700 * Don't forget to nail down a reference to the new object before
703 IEnumSTATSTG_AddRef(*ppenum
);
708 return E_OUTOFMEMORY
;
711 /************************************************************************
712 * Storage32BaseImpl_Stat (IStorage)
714 * This method will retrieve information about this storage object.
716 * See Windows documentation for more details on IStorage methods.
718 static HRESULT WINAPI
StorageBaseImpl_Stat(
720 STATSTG
* pstatstg
, /* [out] */
721 DWORD grfStatFlag
) /* [in] */
723 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
724 StgProperty curProperty
;
726 HRESULT res
= STG_E_UNKNOWN
;
728 TRACE("(%p, %p, %x)\n",
729 iface
, pstatstg
, grfStatFlag
);
732 * Perform a sanity check on the parameters.
734 if ( (This
==0) || (pstatstg
==0))
741 * Read the information from the property.
743 readSuccessful
= StorageImpl_ReadProperty(
744 This
->ancestorStorage
,
745 This
->rootPropertySetIndex
,
750 StorageUtl_CopyPropertyToSTATSTG(
755 pstatstg
->grfMode
= This
->openFlags
;
756 pstatstg
->grfStateBits
= This
->stateBits
;
767 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg
->pwcsName
), pstatstg
->type
, pstatstg
->cbSize
.u
.LowPart
, pstatstg
->cbSize
.u
.HighPart
, pstatstg
->grfMode
, pstatstg
->grfLocksSupported
, pstatstg
->grfStateBits
);
769 TRACE("<-- %08x\n", res
);
773 /************************************************************************
774 * Storage32BaseImpl_RenameElement (IStorage)
776 * This method will rename the specified element.
778 * See Windows documentation for more details on IStorage methods.
780 * Implementation notes: The method used to rename consists of creating a clone
781 * of the deleted StgProperty object setting it with the new name and to
782 * perform a DestroyElement of the old StgProperty.
784 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
786 const OLECHAR
* pwcsOldName
, /* [in] */
787 const OLECHAR
* pwcsNewName
) /* [in] */
789 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
790 IEnumSTATSTGImpl
* propertyEnumeration
;
791 StgProperty currentProperty
;
792 ULONG foundPropertyIndex
;
794 TRACE("(%p, %s, %s)\n",
795 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
798 * Create a property enumeration to search the properties
800 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
801 This
->rootPropertySetIndex
);
804 * Search the enumeration for the new property name
806 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
810 if (foundPropertyIndex
!= PROPERTY_NULL
)
813 * There is already a property with the new name
815 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
816 return STG_E_FILEALREADYEXISTS
;
819 IEnumSTATSTG_Reset((IEnumSTATSTG
*)propertyEnumeration
);
822 * Search the enumeration for the old property name
824 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
829 * Delete the property enumeration since we don't need it anymore
831 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
833 if (foundPropertyIndex
!= PROPERTY_NULL
)
835 StgProperty renamedProperty
;
836 ULONG renamedPropertyIndex
;
839 * Setup a new property for the renamed property
841 renamedProperty
.sizeOfNameString
=
842 ( lstrlenW(pwcsNewName
)+1 ) * sizeof(WCHAR
);
844 if (renamedProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
845 return STG_E_INVALIDNAME
;
847 strcpyW(renamedProperty
.name
, pwcsNewName
);
849 renamedProperty
.propertyType
= currentProperty
.propertyType
;
850 renamedProperty
.startingBlock
= currentProperty
.startingBlock
;
851 renamedProperty
.size
.u
.LowPart
= currentProperty
.size
.u
.LowPart
;
852 renamedProperty
.size
.u
.HighPart
= currentProperty
.size
.u
.HighPart
;
854 renamedProperty
.previousProperty
= PROPERTY_NULL
;
855 renamedProperty
.nextProperty
= PROPERTY_NULL
;
858 * Bring the dirProperty link in case it is a storage and in which
859 * case the renamed storage elements don't require to be reorganized.
861 renamedProperty
.dirProperty
= currentProperty
.dirProperty
;
863 /* call CoFileTime to get the current time
864 renamedProperty.timeStampS1
865 renamedProperty.timeStampD1
866 renamedProperty.timeStampS2
867 renamedProperty.timeStampD2
868 renamedProperty.propertyUniqueID
872 * Obtain a free property in the property chain
874 renamedPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
877 * Save the new property into the new property spot
879 StorageImpl_WriteProperty(
880 This
->ancestorStorage
,
881 renamedPropertyIndex
,
885 * Find a spot in the property chain for our newly created property.
889 renamedPropertyIndex
,
893 * At this point the renamed property has been inserted in the tree,
894 * now, before Destroying the old property we must zero its dirProperty
895 * otherwise the DestroyProperty below will zap it all and we do not want
897 * Also, we fake that the old property is a storage so the DestroyProperty
898 * will not do a SetSize(0) on the stream data.
900 * This means that we need to tweak the StgProperty if it is a stream or a
903 StorageImpl_ReadProperty(This
->ancestorStorage
,
907 currentProperty
.dirProperty
= PROPERTY_NULL
;
908 currentProperty
.propertyType
= PROPTYPE_STORAGE
;
909 StorageImpl_WriteProperty(
910 This
->ancestorStorage
,
915 * Invoke Destroy to get rid of the ole property and automatically redo
916 * the linking of its previous and next members...
918 IStorage_DestroyElement((IStorage
*)This
->ancestorStorage
, pwcsOldName
);
924 * There is no property with the old name
926 return STG_E_FILENOTFOUND
;
932 /************************************************************************
933 * Storage32BaseImpl_CreateStream (IStorage)
935 * This method will create a stream object within this storage
937 * See Windows documentation for more details on IStorage methods.
939 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
941 const OLECHAR
* pwcsName
, /* [string][in] */
942 DWORD grfMode
, /* [in] */
943 DWORD reserved1
, /* [in] */
944 DWORD reserved2
, /* [in] */
945 IStream
** ppstm
) /* [out] */
947 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
948 IEnumSTATSTGImpl
* propertyEnumeration
;
949 StgStreamImpl
* newStream
;
950 StgProperty currentProperty
, newStreamProperty
;
951 ULONG foundPropertyIndex
, newPropertyIndex
;
953 TRACE("(%p, %s, %x, %d, %d, %p)\n",
954 iface
, debugstr_w(pwcsName
), grfMode
,
955 reserved1
, reserved2
, ppstm
);
958 * Validate parameters
961 return STG_E_INVALIDPOINTER
;
964 return STG_E_INVALIDNAME
;
966 if (reserved1
|| reserved2
)
967 return STG_E_INVALIDPARAMETER
;
970 * Validate the STGM flags
972 if ( FAILED( validateSTGM(grfMode
) ))
973 return STG_E_INVALIDFLAG
;
975 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
976 return STG_E_INVALIDFLAG
;
981 if ((grfMode
& STGM_DELETEONRELEASE
) ||
982 (grfMode
& STGM_TRANSACTED
))
983 return STG_E_INVALIDFUNCTION
;
986 * Check that we're compatible with the parent's storage mode
987 * if not in transacted mode
989 if(!(This
->ancestorStorage
->base
.openFlags
& STGM_TRANSACTED
)) {
990 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
991 return STG_E_ACCESSDENIED
;
995 * Initialize the out parameter
1000 * Create a property enumeration to search the properties
1002 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
1003 This
->rootPropertySetIndex
);
1005 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
1009 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1011 if (foundPropertyIndex
!= PROPERTY_NULL
)
1014 * An element with this name already exists
1016 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
1018 StgStreamImpl
*strm
;
1020 LIST_FOR_EACH_ENTRY(strm
, &This
->strmHead
, StgStreamImpl
, StrmListEntry
)
1022 if (strm
->ownerProperty
== foundPropertyIndex
)
1024 TRACE("Stream deleted %p\n", strm
);
1025 strm
->parentStorage
= NULL
;
1026 list_remove(&strm
->StrmListEntry
);
1029 IStorage_DestroyElement(iface
, pwcsName
);
1032 return STG_E_FILEALREADYEXISTS
;
1034 else if (STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1036 WARN("read-only storage\n");
1037 return STG_E_ACCESSDENIED
;
1041 * memset the empty property
1043 memset(&newStreamProperty
, 0, sizeof(StgProperty
));
1045 newStreamProperty
.sizeOfNameString
=
1046 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
1048 if (newStreamProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
1049 return STG_E_INVALIDNAME
;
1051 strcpyW(newStreamProperty
.name
, pwcsName
);
1053 newStreamProperty
.propertyType
= PROPTYPE_STREAM
;
1054 newStreamProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
1055 newStreamProperty
.size
.u
.LowPart
= 0;
1056 newStreamProperty
.size
.u
.HighPart
= 0;
1058 newStreamProperty
.previousProperty
= PROPERTY_NULL
;
1059 newStreamProperty
.nextProperty
= PROPERTY_NULL
;
1060 newStreamProperty
.dirProperty
= PROPERTY_NULL
;
1062 /* call CoFileTime to get the current time
1063 newStreamProperty.timeStampS1
1064 newStreamProperty.timeStampD1
1065 newStreamProperty.timeStampS2
1066 newStreamProperty.timeStampD2
1069 /* newStreamProperty.propertyUniqueID */
1072 * Get a free property or create a new one
1074 newPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
1077 * Save the new property into the new property spot
1079 StorageImpl_WriteProperty(
1080 This
->ancestorStorage
,
1082 &newStreamProperty
);
1085 * Find a spot in the property chain for our newly created property.
1087 updatePropertyChain(
1093 * Open the stream to return it.
1095 newStream
= StgStreamImpl_Construct(This
, grfMode
, newPropertyIndex
);
1099 *ppstm
= (IStream
*)newStream
;
1102 * Since we are returning a pointer to the interface, we have to nail down
1105 IStream_AddRef(*ppstm
);
1109 return STG_E_INSUFFICIENTMEMORY
;
1115 /************************************************************************
1116 * Storage32BaseImpl_SetClass (IStorage)
1118 * This method will write the specified CLSID in the property of this
1121 * See Windows documentation for more details on IStorage methods.
1123 static HRESULT WINAPI
StorageBaseImpl_SetClass(
1125 REFCLSID clsid
) /* [in] */
1127 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
1128 HRESULT hRes
= E_FAIL
;
1129 StgProperty curProperty
;
1132 TRACE("(%p, %p)\n", iface
, clsid
);
1134 success
= StorageImpl_ReadProperty(This
->ancestorStorage
,
1135 This
->rootPropertySetIndex
,
1139 curProperty
.propertyUniqueID
= *clsid
;
1141 success
= StorageImpl_WriteProperty(This
->ancestorStorage
,
1142 This
->rootPropertySetIndex
,
1151 /************************************************************************
1152 ** Storage32Impl implementation
1155 /************************************************************************
1156 * Storage32Impl_CreateStorage (IStorage)
1158 * This method will create the storage object within the provided storage.
1160 * See Windows documentation for more details on IStorage methods.
1162 static HRESULT WINAPI
StorageImpl_CreateStorage(
1164 const OLECHAR
*pwcsName
, /* [string][in] */
1165 DWORD grfMode
, /* [in] */
1166 DWORD reserved1
, /* [in] */
1167 DWORD reserved2
, /* [in] */
1168 IStorage
**ppstg
) /* [out] */
1170 StorageImpl
* const This
=(StorageImpl
*)iface
;
1172 IEnumSTATSTGImpl
*propertyEnumeration
;
1173 StgProperty currentProperty
;
1174 StgProperty newProperty
;
1175 ULONG foundPropertyIndex
;
1176 ULONG newPropertyIndex
;
1179 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1180 iface
, debugstr_w(pwcsName
), grfMode
,
1181 reserved1
, reserved2
, ppstg
);
1184 * Validate parameters
1187 return STG_E_INVALIDPOINTER
;
1190 return STG_E_INVALIDNAME
;
1193 * Initialize the out parameter
1198 * Validate the STGM flags
1200 if ( FAILED( validateSTGM(grfMode
) ) ||
1201 (grfMode
& STGM_DELETEONRELEASE
) )
1203 WARN("bad grfMode: 0x%x\n", grfMode
);
1204 return STG_E_INVALIDFLAG
;
1208 * Check that we're compatible with the parent's storage mode
1210 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->base
.openFlags
) )
1212 WARN("access denied\n");
1213 return STG_E_ACCESSDENIED
;
1217 * Create a property enumeration and search the properties
1219 propertyEnumeration
= IEnumSTATSTGImpl_Construct( This
->base
.ancestorStorage
,
1220 This
->base
.rootPropertySetIndex
);
1222 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
1225 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1227 if (foundPropertyIndex
!= PROPERTY_NULL
)
1230 * An element with this name already exists
1232 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
1233 IStorage_DestroyElement(iface
, pwcsName
);
1236 WARN("file already exists\n");
1237 return STG_E_FILEALREADYEXISTS
;
1240 else if (STGM_ACCESS_MODE(This
->base
.openFlags
) == STGM_READ
)
1242 WARN("read-only storage\n");
1243 return STG_E_ACCESSDENIED
;
1247 * memset the empty property
1249 memset(&newProperty
, 0, sizeof(StgProperty
));
1251 newProperty
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1253 if (newProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
1255 FIXME("name too long\n");
1256 return STG_E_INVALIDNAME
;
1259 strcpyW(newProperty
.name
, pwcsName
);
1261 newProperty
.propertyType
= PROPTYPE_STORAGE
;
1262 newProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
1263 newProperty
.size
.u
.LowPart
= 0;
1264 newProperty
.size
.u
.HighPart
= 0;
1266 newProperty
.previousProperty
= PROPERTY_NULL
;
1267 newProperty
.nextProperty
= PROPERTY_NULL
;
1268 newProperty
.dirProperty
= PROPERTY_NULL
;
1270 /* call CoFileTime to get the current time
1271 newProperty.timeStampS1
1272 newProperty.timeStampD1
1273 newProperty.timeStampS2
1274 newProperty.timeStampD2
1277 /* newStorageProperty.propertyUniqueID */
1280 * Obtain a free property in the property chain
1282 newPropertyIndex
= getFreeProperty(This
->base
.ancestorStorage
);
1285 * Save the new property into the new property spot
1287 StorageImpl_WriteProperty(
1288 This
->base
.ancestorStorage
,
1293 * Find a spot in the property chain for our newly created property.
1295 updatePropertyChain(
1301 * Open it to get a pointer to return.
1303 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1305 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1315 /***************************************************************************
1319 * Get a free property or create a new one.
1321 static ULONG
getFreeProperty(
1322 StorageImpl
*storage
)
1324 ULONG currentPropertyIndex
= 0;
1325 ULONG newPropertyIndex
= PROPERTY_NULL
;
1326 BOOL readSuccessful
= TRUE
;
1327 StgProperty currentProperty
;
1332 * Start by reading the root property
1334 readSuccessful
= StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1335 currentPropertyIndex
,
1339 if (currentProperty
.sizeOfNameString
== 0)
1342 * The property existis and is available, we found it.
1344 newPropertyIndex
= currentPropertyIndex
;
1350 * We exhausted the property list, we will create more space below
1352 newPropertyIndex
= currentPropertyIndex
;
1354 currentPropertyIndex
++;
1356 } while (newPropertyIndex
== PROPERTY_NULL
);
1359 * grow the property chain
1361 if (! readSuccessful
)
1363 StgProperty emptyProperty
;
1364 ULARGE_INTEGER newSize
;
1365 ULONG propertyIndex
;
1366 ULONG lastProperty
= 0;
1367 ULONG blockCount
= 0;
1370 * obtain the new count of property blocks
1372 blockCount
= BlockChainStream_GetCount(
1373 storage
->base
.ancestorStorage
->rootBlockChain
)+1;
1376 * initialize the size used by the property stream
1378 newSize
.u
.HighPart
= 0;
1379 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1382 * add a property block to the property chain
1384 BlockChainStream_SetSize(storage
->base
.ancestorStorage
->rootBlockChain
, newSize
);
1387 * memset the empty property in order to initialize the unused newly
1390 memset(&emptyProperty
, 0, sizeof(StgProperty
));
1395 lastProperty
= storage
->bigBlockSize
/ PROPSET_BLOCK_SIZE
* blockCount
;
1398 propertyIndex
= newPropertyIndex
;
1399 propertyIndex
< lastProperty
;
1402 StorageImpl_WriteProperty(
1403 storage
->base
.ancestorStorage
,
1409 return newPropertyIndex
;
1412 /****************************************************************************
1416 * Case insensitive comparaison of StgProperty.name by first considering
1419 * Returns <0 when newPrpoerty < currentProperty
1420 * >0 when newPrpoerty > currentProperty
1421 * 0 when newPrpoerty == currentProperty
1423 static LONG
propertyNameCmp(
1424 const OLECHAR
*newProperty
,
1425 const OLECHAR
*currentProperty
)
1427 LONG diff
= lstrlenW(newProperty
) - lstrlenW(currentProperty
);
1432 * We compare the string themselves only when they are of the same length
1434 diff
= lstrcmpiW( newProperty
, currentProperty
);
1440 /****************************************************************************
1444 * Properly link this new element in the property chain.
1446 static void updatePropertyChain(
1447 StorageImpl
*storage
,
1448 ULONG newPropertyIndex
,
1449 StgProperty newProperty
)
1451 StgProperty currentProperty
;
1454 * Read the root property
1456 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1457 storage
->base
.rootPropertySetIndex
,
1460 if (currentProperty
.dirProperty
!= PROPERTY_NULL
)
1463 * The root storage contains some element, therefore, start the research
1464 * for the appropriate location.
1467 ULONG current
, next
, previous
, currentPropertyId
;
1470 * Keep the StgProperty sequence number of the storage first property
1472 currentPropertyId
= currentProperty
.dirProperty
;
1477 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1478 currentProperty
.dirProperty
,
1481 previous
= currentProperty
.previousProperty
;
1482 next
= currentProperty
.nextProperty
;
1483 current
= currentPropertyId
;
1487 LONG diff
= propertyNameCmp( newProperty
.name
, currentProperty
.name
);
1491 if (previous
!= PROPERTY_NULL
)
1493 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1500 currentProperty
.previousProperty
= newPropertyIndex
;
1501 StorageImpl_WriteProperty(storage
->base
.ancestorStorage
,
1509 if (next
!= PROPERTY_NULL
)
1511 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1518 currentProperty
.nextProperty
= newPropertyIndex
;
1519 StorageImpl_WriteProperty(storage
->base
.ancestorStorage
,
1528 * Trying to insert an item with the same name in the
1529 * subtree structure.
1534 previous
= currentProperty
.previousProperty
;
1535 next
= currentProperty
.nextProperty
;
1541 * The root storage is empty, link the new property to its dir property
1543 currentProperty
.dirProperty
= newPropertyIndex
;
1544 StorageImpl_WriteProperty(storage
->base
.ancestorStorage
,
1545 storage
->base
.rootPropertySetIndex
,
1551 /*************************************************************************
1554 static HRESULT WINAPI
StorageImpl_CopyTo(
1556 DWORD ciidExclude
, /* [in] */
1557 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1558 SNB snbExclude
, /* [unique][in] */
1559 IStorage
* pstgDest
) /* [unique][in] */
1561 IEnumSTATSTG
*elements
= 0;
1562 STATSTG curElement
, strStat
;
1564 IStorage
*pstgTmp
, *pstgChild
;
1565 IStream
*pstrTmp
, *pstrChild
;
1567 if ((ciidExclude
!= 0) || (rgiidExclude
!= NULL
) || (snbExclude
!= NULL
))
1568 FIXME("Exclude option not implemented\n");
1570 TRACE("(%p, %d, %p, %p, %p)\n",
1571 iface
, ciidExclude
, rgiidExclude
,
1572 snbExclude
, pstgDest
);
1575 * Perform a sanity check
1577 if ( pstgDest
== 0 )
1578 return STG_E_INVALIDPOINTER
;
1581 * Enumerate the elements
1583 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1591 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1592 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1597 * Obtain the next element
1599 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1601 if ( hr
== S_FALSE
)
1603 hr
= S_OK
; /* done, every element has been copied */
1607 if (curElement
.type
== STGTY_STORAGE
)
1610 * open child source storage
1612 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1613 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1614 NULL
, 0, &pstgChild
);
1620 * Check if destination storage is not a child of the source
1621 * storage, which will cause an infinite loop
1623 if (pstgChild
== pstgDest
)
1625 IEnumSTATSTG_Release(elements
);
1627 return STG_E_ACCESSDENIED
;
1631 * create a new storage in destination storage
1633 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1634 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1638 * if it already exist, don't create a new one use this one
1640 if (hr
== STG_E_FILEALREADYEXISTS
)
1642 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1643 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1644 NULL
, 0, &pstgTmp
);
1652 * do the copy recursively
1654 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1655 snbExclude
, pstgTmp
);
1657 IStorage_Release( pstgTmp
);
1658 IStorage_Release( pstgChild
);
1660 else if (curElement
.type
== STGTY_STREAM
)
1663 * create a new stream in destination storage. If the stream already
1664 * exist, it will be deleted and a new one will be created.
1666 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1667 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1674 * open child stream storage
1676 hr
= IStorage_OpenStream( iface
, curElement
.pwcsName
, NULL
,
1677 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1684 * Get the size of the source stream
1686 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1689 * Set the size of the destination stream.
1691 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1696 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1699 IStream_Release( pstrTmp
);
1700 IStream_Release( pstrChild
);
1704 WARN("unknown element type: %d\n", curElement
.type
);
1707 } while (hr
== S_OK
);
1712 IEnumSTATSTG_Release(elements
);
1717 /*************************************************************************
1718 * MoveElementTo (IStorage)
1720 static HRESULT WINAPI
StorageImpl_MoveElementTo(
1722 const OLECHAR
*pwcsName
, /* [string][in] */
1723 IStorage
*pstgDest
, /* [unique][in] */
1724 const OLECHAR
*pwcsNewName
,/* [string][in] */
1725 DWORD grfFlags
) /* [in] */
1727 FIXME("(%p %s %p %s %u): stub\n", iface
,
1728 debugstr_w(pwcsName
), pstgDest
,
1729 debugstr_w(pwcsNewName
), grfFlags
);
1733 /*************************************************************************
1736 * Ensures that any changes made to a storage object open in transacted mode
1737 * are reflected in the parent storage
1740 * Wine doesn't implement transacted mode, which seems to be a basic
1741 * optimization, so we can ignore this stub for now.
1743 static HRESULT WINAPI
StorageImpl_Commit(
1745 DWORD grfCommitFlags
)/* [in] */
1747 FIXME("(%p %d): stub\n", iface
, grfCommitFlags
);
1751 /*************************************************************************
1754 * Discard all changes that have been made since the last commit operation
1756 static HRESULT WINAPI
StorageImpl_Revert(
1759 FIXME("(%p): stub\n", iface
);
1763 /*************************************************************************
1764 * DestroyElement (IStorage)
1766 * Strategy: This implementation is built this way for simplicity not for speed.
1767 * I always delete the topmost element of the enumeration and adjust
1768 * the deleted element pointer all the time. This takes longer to
1769 * do but allow to reinvoke DestroyElement whenever we encounter a
1770 * storage object. The optimisation resides in the usage of another
1771 * enumeration strategy that would give all the leaves of a storage
1772 * first. (postfix order)
1774 static HRESULT WINAPI
StorageImpl_DestroyElement(
1776 const OLECHAR
*pwcsName
)/* [string][in] */
1778 StorageImpl
* const This
=(StorageImpl
*)iface
;
1780 IEnumSTATSTGImpl
* propertyEnumeration
;
1783 StgProperty propertyToDelete
;
1784 StgProperty parentProperty
;
1785 ULONG foundPropertyIndexToDelete
;
1786 ULONG typeOfRelation
;
1787 ULONG parentPropertyId
= 0;
1790 iface
, debugstr_w(pwcsName
));
1793 * Perform a sanity check on the parameters.
1796 return STG_E_INVALIDPOINTER
;
1799 * Create a property enumeration to search the property with the given name
1801 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
1802 This
->base
.ancestorStorage
,
1803 This
->base
.rootPropertySetIndex
);
1805 foundPropertyIndexToDelete
= IEnumSTATSTGImpl_FindProperty(
1806 propertyEnumeration
,
1810 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1812 if ( foundPropertyIndexToDelete
== PROPERTY_NULL
)
1814 return STG_E_FILENOTFOUND
;
1818 * Find the parent property of the property to delete (the one that
1819 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1820 * the parent is This. Otherwise, the parent is one of its sibling...
1824 * First, read This's StgProperty..
1826 res
= StorageImpl_ReadProperty(
1827 This
->base
.ancestorStorage
,
1828 This
->base
.rootPropertySetIndex
,
1834 * Second, check to see if by any chance the actual storage (This) is not
1835 * the parent of the property to delete... We never know...
1837 if ( parentProperty
.dirProperty
== foundPropertyIndexToDelete
)
1840 * Set data as it would have been done in the else part...
1842 typeOfRelation
= PROPERTY_RELATION_DIR
;
1843 parentPropertyId
= This
->base
.rootPropertySetIndex
;
1848 * Create a property enumeration to search the parent properties, and
1849 * delete it once done.
1851 IEnumSTATSTGImpl
* propertyEnumeration2
;
1853 propertyEnumeration2
= IEnumSTATSTGImpl_Construct(
1854 This
->base
.ancestorStorage
,
1855 This
->base
.rootPropertySetIndex
);
1857 typeOfRelation
= IEnumSTATSTGImpl_FindParentProperty(
1858 propertyEnumeration2
,
1859 foundPropertyIndexToDelete
,
1863 IEnumSTATSTGImpl_Destroy(propertyEnumeration2
);
1866 if ( propertyToDelete
.propertyType
== PROPTYPE_STORAGE
)
1868 hr
= deleteStorageProperty(
1870 foundPropertyIndexToDelete
,
1873 else if ( propertyToDelete
.propertyType
== PROPTYPE_STREAM
)
1875 hr
= deleteStreamProperty(
1877 foundPropertyIndexToDelete
,
1885 * Adjust the property chain
1887 hr
= adjustPropertyChain(
1898 /************************************************************************
1899 * StorageImpl_Stat (IStorage)
1901 * This method will retrieve information about this storage object.
1903 * See Windows documentation for more details on IStorage methods.
1905 static HRESULT WINAPI
StorageImpl_Stat( IStorage
* iface
,
1906 STATSTG
* pstatstg
, /* [out] */
1907 DWORD grfStatFlag
) /* [in] */
1909 StorageImpl
* const This
= (StorageImpl
*)iface
;
1910 HRESULT result
= StorageBaseImpl_Stat( iface
, pstatstg
, grfStatFlag
);
1912 if ( !FAILED(result
) && ((grfStatFlag
& STATFLAG_NONAME
) == 0) && This
->pwcsName
)
1914 CoTaskMemFree(pstatstg
->pwcsName
);
1915 pstatstg
->pwcsName
= CoTaskMemAlloc((lstrlenW(This
->pwcsName
)+1)*sizeof(WCHAR
));
1916 strcpyW(pstatstg
->pwcsName
, This
->pwcsName
);
1922 /******************************************************************************
1923 * Internal stream list handlers
1926 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1928 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1929 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1932 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1934 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1935 list_remove(&(strm
->StrmListEntry
));
1938 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
1940 struct list
*cur
, *cur2
;
1941 StgStreamImpl
*strm
=NULL
;
1943 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
1944 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
1945 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
1946 strm
->parentStorage
= NULL
;
1952 /*********************************************************************
1956 * Perform the deletion of a complete storage node
1959 static HRESULT
deleteStorageProperty(
1960 StorageImpl
*parentStorage
,
1961 ULONG indexOfPropertyToDelete
,
1962 StgProperty propertyToDelete
)
1964 IEnumSTATSTG
*elements
= 0;
1965 IStorage
*childStorage
= 0;
1966 STATSTG currentElement
;
1968 HRESULT destroyHr
= S_OK
;
1971 * Open the storage and enumerate it
1973 hr
= StorageBaseImpl_OpenStorage(
1974 (IStorage
*)parentStorage
,
1975 propertyToDelete
.name
,
1977 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
1988 * Enumerate the elements
1990 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
1995 * Obtain the next element
1997 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2000 destroyHr
= StorageImpl_DestroyElement(childStorage
, currentElement
.pwcsName
);
2002 CoTaskMemFree(currentElement
.pwcsName
);
2006 * We need to Reset the enumeration every time because we delete elements
2007 * and the enumeration could be invalid
2009 IEnumSTATSTG_Reset(elements
);
2011 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2014 * Invalidate the property by zeroing its name member.
2016 propertyToDelete
.sizeOfNameString
= 0;
2018 StorageImpl_WriteProperty(parentStorage
->base
.ancestorStorage
,
2019 indexOfPropertyToDelete
,
2022 IStorage_Release(childStorage
);
2023 IEnumSTATSTG_Release(elements
);
2028 /*********************************************************************
2032 * Perform the deletion of a stream node
2035 static HRESULT
deleteStreamProperty(
2036 StorageImpl
*parentStorage
,
2037 ULONG indexOfPropertyToDelete
,
2038 StgProperty propertyToDelete
)
2042 ULARGE_INTEGER size
;
2044 size
.u
.HighPart
= 0;
2047 hr
= StorageBaseImpl_OpenStream(
2048 (IStorage
*)parentStorage
,
2049 (OLECHAR
*)propertyToDelete
.name
,
2051 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2063 hr
= IStream_SetSize(pis
, size
);
2071 * Release the stream object.
2073 IStream_Release(pis
);
2076 * Invalidate the property by zeroing its name member.
2078 propertyToDelete
.sizeOfNameString
= 0;
2081 * Here we should re-read the property so we get the updated pointer
2082 * but since we are here to zap it, I don't do it...
2084 StorageImpl_WriteProperty(
2085 parentStorage
->base
.ancestorStorage
,
2086 indexOfPropertyToDelete
,
2092 /*********************************************************************
2096 * Finds a placeholder for the StgProperty within the Storage
2099 static HRESULT
findPlaceholder(
2100 StorageImpl
*storage
,
2101 ULONG propertyIndexToStore
,
2102 ULONG storePropertyIndex
,
2105 StgProperty storeProperty
;
2110 * Read the storage property
2112 res
= StorageImpl_ReadProperty(
2113 storage
->base
.ancestorStorage
,
2122 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
2124 if (storeProperty
.previousProperty
!= PROPERTY_NULL
)
2126 return findPlaceholder(
2128 propertyIndexToStore
,
2129 storeProperty
.previousProperty
,
2134 storeProperty
.previousProperty
= propertyIndexToStore
;
2137 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
2139 if (storeProperty
.nextProperty
!= PROPERTY_NULL
)
2141 return findPlaceholder(
2143 propertyIndexToStore
,
2144 storeProperty
.nextProperty
,
2149 storeProperty
.nextProperty
= propertyIndexToStore
;
2152 else if (typeOfRelation
== PROPERTY_RELATION_DIR
)
2154 if (storeProperty
.dirProperty
!= PROPERTY_NULL
)
2156 return findPlaceholder(
2158 propertyIndexToStore
,
2159 storeProperty
.dirProperty
,
2164 storeProperty
.dirProperty
= propertyIndexToStore
;
2168 hr
= StorageImpl_WriteProperty(
2169 storage
->base
.ancestorStorage
,
2181 /*************************************************************************
2185 * This method takes the previous and the next property link of a property
2186 * to be deleted and find them a place in the Storage.
2188 static HRESULT
adjustPropertyChain(
2190 StgProperty propertyToDelete
,
2191 StgProperty parentProperty
,
2192 ULONG parentPropertyId
,
2195 ULONG newLinkProperty
= PROPERTY_NULL
;
2196 BOOL needToFindAPlaceholder
= FALSE
;
2197 ULONG storeNode
= PROPERTY_NULL
;
2198 ULONG toStoreNode
= PROPERTY_NULL
;
2199 INT relationType
= 0;
2203 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
2205 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2208 * Set the parent previous to the property to delete previous
2210 newLinkProperty
= propertyToDelete
.previousProperty
;
2212 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2215 * We also need to find a storage for the other link, setup variables
2216 * to do this at the end...
2218 needToFindAPlaceholder
= TRUE
;
2219 storeNode
= propertyToDelete
.previousProperty
;
2220 toStoreNode
= propertyToDelete
.nextProperty
;
2221 relationType
= PROPERTY_RELATION_NEXT
;
2224 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2227 * Set the parent previous to the property to delete next
2229 newLinkProperty
= propertyToDelete
.nextProperty
;
2233 * Link it for real...
2235 parentProperty
.previousProperty
= newLinkProperty
;
2238 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
2240 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2243 * Set the parent next to the property to delete next previous
2245 newLinkProperty
= propertyToDelete
.previousProperty
;
2247 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2250 * We also need to find a storage for the other link, setup variables
2251 * to do this at the end...
2253 needToFindAPlaceholder
= TRUE
;
2254 storeNode
= propertyToDelete
.previousProperty
;
2255 toStoreNode
= propertyToDelete
.nextProperty
;
2256 relationType
= PROPERTY_RELATION_NEXT
;
2259 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2262 * Set the parent next to the property to delete next
2264 newLinkProperty
= propertyToDelete
.nextProperty
;
2268 * Link it for real...
2270 parentProperty
.nextProperty
= newLinkProperty
;
2272 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2274 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2277 * Set the parent dir to the property to delete previous
2279 newLinkProperty
= propertyToDelete
.previousProperty
;
2281 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2284 * We also need to find a storage for the other link, setup variables
2285 * to do this at the end...
2287 needToFindAPlaceholder
= TRUE
;
2288 storeNode
= propertyToDelete
.previousProperty
;
2289 toStoreNode
= propertyToDelete
.nextProperty
;
2290 relationType
= PROPERTY_RELATION_NEXT
;
2293 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2296 * Set the parent dir to the property to delete next
2298 newLinkProperty
= propertyToDelete
.nextProperty
;
2302 * Link it for real...
2304 parentProperty
.dirProperty
= newLinkProperty
;
2308 * Write back the parent property
2310 res
= StorageImpl_WriteProperty(
2311 This
->base
.ancestorStorage
,
2320 * If a placeholder is required for the other link, then, find one and
2321 * get out of here...
2323 if (needToFindAPlaceholder
)
2325 hr
= findPlaceholder(
2336 /******************************************************************************
2337 * SetElementTimes (IStorage)
2339 static HRESULT WINAPI
StorageImpl_SetElementTimes(
2341 const OLECHAR
*pwcsName
,/* [string][in] */
2342 const FILETIME
*pctime
, /* [in] */
2343 const FILETIME
*patime
, /* [in] */
2344 const FILETIME
*pmtime
) /* [in] */
2346 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2350 /******************************************************************************
2351 * SetStateBits (IStorage)
2353 static HRESULT WINAPI
StorageImpl_SetStateBits(
2355 DWORD grfStateBits
,/* [in] */
2356 DWORD grfMask
) /* [in] */
2358 StorageImpl
* const This
= (StorageImpl
*)iface
;
2359 This
->base
.stateBits
= (This
->base
.stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2364 * Virtual function table for the IStorage32Impl class.
2366 static const IStorageVtbl Storage32Impl_Vtbl
=
2368 StorageBaseImpl_QueryInterface
,
2369 StorageBaseImpl_AddRef
,
2370 StorageBaseImpl_Release
,
2371 StorageBaseImpl_CreateStream
,
2372 StorageBaseImpl_OpenStream
,
2373 StorageImpl_CreateStorage
,
2374 StorageBaseImpl_OpenStorage
,
2376 StorageImpl_MoveElementTo
,
2379 StorageBaseImpl_EnumElements
,
2380 StorageImpl_DestroyElement
,
2381 StorageBaseImpl_RenameElement
,
2382 StorageImpl_SetElementTimes
,
2383 StorageBaseImpl_SetClass
,
2384 StorageImpl_SetStateBits
,
2388 static HRESULT
StorageImpl_Construct(
2398 StgProperty currentProperty
;
2399 BOOL readSuccessful
;
2400 ULONG currentPropertyIndex
;
2402 if ( FAILED( validateSTGM(openFlags
) ))
2403 return STG_E_INVALIDFLAG
;
2405 memset(This
, 0, sizeof(StorageImpl
));
2408 * Initialize stream list
2411 list_init(&This
->base
.strmHead
);
2414 * Initialize the virtual function table.
2416 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2417 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2418 This
->base
.v_destructor
= &StorageImpl_Destroy
;
2419 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2422 * This is the top-level storage so initialize the ancestor pointer
2425 This
->base
.ancestorStorage
= This
;
2428 * Initialize the physical support of the storage.
2430 This
->hFile
= hFile
;
2433 * Store copy of file path.
2436 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2437 (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
));
2438 if (!This
->pwcsName
)
2439 return STG_E_INSUFFICIENTMEMORY
;
2440 strcpyW(This
->pwcsName
, pwcsName
);
2444 * Initialize the big block cache.
2446 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
2447 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2448 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2454 if (This
->bigBlockFile
== 0)
2459 ULARGE_INTEGER size
;
2460 BYTE bigBlockBuffer
[BIG_BLOCK_SIZE
];
2463 * Initialize all header variables:
2464 * - The big block depot consists of one block and it is at block 0
2465 * - The properties start at block 1
2466 * - There is no small block depot
2468 memset( This
->bigBlockDepotStart
,
2470 sizeof(This
->bigBlockDepotStart
));
2472 This
->bigBlockDepotCount
= 1;
2473 This
->bigBlockDepotStart
[0] = 0;
2474 This
->rootStartBlock
= 1;
2475 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2476 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
2477 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2478 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2479 This
->extBigBlockDepotCount
= 0;
2481 StorageImpl_SaveFileHeader(This
);
2484 * Add one block for the big block depot and one block for the properties
2486 size
.u
.HighPart
= 0;
2487 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2488 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2491 * Initialize the big block depot
2493 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2494 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2495 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2496 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2501 * Load the header for the file.
2503 hr
= StorageImpl_LoadFileHeader(This
);
2507 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2514 * There is no block depot cached yet.
2516 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2519 * Start searching for free blocks with block 0.
2521 This
->prevFreeBlock
= 0;
2524 * Create the block chain abstractions.
2526 if(!(This
->rootBlockChain
=
2527 BlockChainStream_Construct(This
, &This
->rootStartBlock
, PROPERTY_NULL
)))
2528 return STG_E_READFAULT
;
2530 if(!(This
->smallBlockDepotChain
=
2531 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2533 return STG_E_READFAULT
;
2536 * Write the root property (memory only)
2540 StgProperty rootProp
;
2542 * Initialize the property chain
2544 memset(&rootProp
, 0, sizeof(rootProp
));
2545 MultiByteToWideChar( CP_ACP
, 0, rootPropertyName
, -1, rootProp
.name
,
2546 sizeof(rootProp
.name
)/sizeof(WCHAR
) );
2547 rootProp
.sizeOfNameString
= (strlenW(rootProp
.name
)+1) * sizeof(WCHAR
);
2548 rootProp
.propertyType
= PROPTYPE_ROOT
;
2549 rootProp
.previousProperty
= PROPERTY_NULL
;
2550 rootProp
.nextProperty
= PROPERTY_NULL
;
2551 rootProp
.dirProperty
= PROPERTY_NULL
;
2552 rootProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
2553 rootProp
.size
.u
.HighPart
= 0;
2554 rootProp
.size
.u
.LowPart
= 0;
2556 StorageImpl_WriteProperty(This
, 0, &rootProp
);
2560 * Find the ID of the root in the property sets.
2562 currentPropertyIndex
= 0;
2566 readSuccessful
= StorageImpl_ReadProperty(
2568 currentPropertyIndex
,
2573 if ( (currentProperty
.sizeOfNameString
!= 0 ) &&
2574 (currentProperty
.propertyType
== PROPTYPE_ROOT
) )
2576 This
->base
.rootPropertySetIndex
= currentPropertyIndex
;
2580 currentPropertyIndex
++;
2582 } while (readSuccessful
&& (This
->base
.rootPropertySetIndex
== PROPERTY_NULL
) );
2584 if (!readSuccessful
)
2587 return STG_E_READFAULT
;
2591 * Create the block chain abstraction for the small block root chain.
2593 if(!(This
->smallBlockRootChain
=
2594 BlockChainStream_Construct(This
, NULL
, This
->base
.rootPropertySetIndex
)))
2595 return STG_E_READFAULT
;
2600 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2602 StorageImpl
*This
= (StorageImpl
*) iface
;
2603 TRACE("(%p)\n", This
);
2605 StorageBaseImpl_DeleteAll(&This
->base
);
2607 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2609 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2610 BlockChainStream_Destroy(This
->rootBlockChain
);
2611 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2613 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2614 HeapFree(GetProcessHeap(), 0, This
);
2617 /******************************************************************************
2618 * Storage32Impl_GetNextFreeBigBlock
2620 * Returns the index of the next free big block.
2621 * If the big block depot is filled, this method will enlarge it.
2624 static ULONG
StorageImpl_GetNextFreeBigBlock(
2627 ULONG depotBlockIndexPos
;
2628 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2630 ULONG depotBlockOffset
;
2631 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2632 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2634 ULONG freeBlock
= BLOCK_UNUSED
;
2636 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2637 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2640 * Scan the entire big block depot until we find a block marked free
2642 while (nextBlockIndex
!= BLOCK_UNUSED
)
2644 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2646 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2649 * Grow the primary depot.
2651 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2653 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2656 * Add a block depot.
2658 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2659 This
->bigBlockDepotCount
++;
2660 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2663 * Flag it as a block depot.
2665 StorageImpl_SetNextBlockInChain(This
,
2669 /* Save new header information.
2671 StorageImpl_SaveFileHeader(This
);
2676 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2678 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2681 * Grow the extended depot.
2683 ULONG extIndex
= BLOCK_UNUSED
;
2684 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2685 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2687 if (extBlockOffset
== 0)
2689 /* We need an extended block.
2691 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2692 This
->extBigBlockDepotCount
++;
2693 depotBlockIndexPos
= extIndex
+ 1;
2696 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2699 * Add a block depot and mark it in the extended block.
2701 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2702 This
->bigBlockDepotCount
++;
2703 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2705 /* Flag the block depot.
2707 StorageImpl_SetNextBlockInChain(This
,
2711 /* If necessary, flag the extended depot block.
2713 if (extIndex
!= BLOCK_UNUSED
)
2714 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2716 /* Save header information.
2718 StorageImpl_SaveFileHeader(This
);
2722 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2726 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2727 ( nextBlockIndex
!= BLOCK_UNUSED
))
2729 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2731 if (nextBlockIndex
== BLOCK_UNUSED
)
2733 freeBlock
= (depotIndex
* blocksPerDepot
) +
2734 (depotBlockOffset
/sizeof(ULONG
));
2737 depotBlockOffset
+= sizeof(ULONG
);
2742 depotBlockOffset
= 0;
2746 * make sure that the block physically exists before using it
2748 BIGBLOCKFILE_EnsureExists(This
->bigBlockFile
, freeBlock
);
2750 This
->prevFreeBlock
= freeBlock
;
2755 /******************************************************************************
2756 * Storage32Impl_AddBlockDepot
2758 * This will create a depot block, essentially it is a block initialized
2761 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2763 BYTE blockBuffer
[BIG_BLOCK_SIZE
];
2766 * Initialize blocks as free
2768 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2769 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
2772 /******************************************************************************
2773 * Storage32Impl_GetExtDepotBlock
2775 * Returns the index of the block that corresponds to the specified depot
2776 * index. This method is only for depot indexes equal or greater than
2777 * COUNT_BBDEPOTINHEADER.
2779 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2781 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2782 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2783 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2784 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2785 ULONG blockIndex
= BLOCK_UNUSED
;
2786 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2788 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2790 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2791 return BLOCK_UNUSED
;
2793 while (extBlockCount
> 0)
2795 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2799 if (extBlockIndex
!= BLOCK_UNUSED
)
2800 StorageImpl_ReadDWordFromBigBlock(This
, extBlockIndex
,
2801 extBlockOffset
* sizeof(ULONG
), &blockIndex
);
2806 /******************************************************************************
2807 * Storage32Impl_SetExtDepotBlock
2809 * Associates the specified block index to the specified depot index.
2810 * This method is only for depot indexes equal or greater than
2811 * COUNT_BBDEPOTINHEADER.
2813 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
2815 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2816 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2817 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2818 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2819 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2821 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2823 while (extBlockCount
> 0)
2825 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2829 if (extBlockIndex
!= BLOCK_UNUSED
)
2831 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
2832 extBlockOffset
* sizeof(ULONG
),
2837 /******************************************************************************
2838 * Storage32Impl_AddExtBlockDepot
2840 * Creates an extended depot block.
2842 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
2844 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
2845 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
2846 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2847 ULONG index
= BLOCK_UNUSED
;
2848 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2849 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
2850 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
2852 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
2853 blocksPerDepotBlock
;
2855 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
2858 * The first extended block.
2860 This
->extBigBlockDepotStart
= index
;
2866 * Follow the chain to the last one.
2868 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
2870 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
2874 * Add the new extended block to the chain.
2876 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
2881 * Initialize this block.
2883 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2884 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
2889 /******************************************************************************
2890 * Storage32Impl_FreeBigBlock
2892 * This method will flag the specified block as free in the big block depot.
2894 static void StorageImpl_FreeBigBlock(
2898 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
2900 if (blockIndex
< This
->prevFreeBlock
)
2901 This
->prevFreeBlock
= blockIndex
;
2904 /************************************************************************
2905 * Storage32Impl_GetNextBlockInChain
2907 * This method will retrieve the block index of the next big block in
2910 * Params: This - Pointer to the Storage object.
2911 * blockIndex - Index of the block to retrieve the chain
2913 * nextBlockIndex - receives the return value.
2915 * Returns: This method returns the index of the next block in the chain.
2916 * It will return the constants:
2917 * BLOCK_SPECIAL - If the block given was not part of a
2919 * BLOCK_END_OF_CHAIN - If the block given was the last in
2921 * BLOCK_UNUSED - If the block given was not past of a chain
2923 * BLOCK_EXTBBDEPOT - This block is part of the extended
2926 * See Windows documentation for more details on IStorage methods.
2928 static HRESULT
StorageImpl_GetNextBlockInChain(
2931 ULONG
* nextBlockIndex
)
2933 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2934 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2935 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2936 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2938 ULONG depotBlockIndexPos
;
2941 *nextBlockIndex
= BLOCK_SPECIAL
;
2943 if(depotBlockCount
>= This
->bigBlockDepotCount
)
2945 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
2946 This
->bigBlockDepotCount
);
2947 return STG_E_READFAULT
;
2951 * Cache the currently accessed depot block.
2953 if (depotBlockCount
!= This
->indexBlockDepotCached
)
2955 This
->indexBlockDepotCached
= depotBlockCount
;
2957 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2959 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2964 * We have to look in the extended depot.
2966 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2969 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2972 return STG_E_READFAULT
;
2974 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
2976 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
2977 This
->blockDepotCached
[index
] = *nextBlockIndex
;
2981 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
2986 /******************************************************************************
2987 * Storage32Impl_GetNextExtendedBlock
2989 * Given an extended block this method will return the next extended block.
2992 * The last ULONG of an extended block is the block index of the next
2993 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2997 * - The index of the next extended block
2998 * - BLOCK_UNUSED: there is no next extended block.
2999 * - Any other return values denotes failure.
3001 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3003 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3004 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3006 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3009 return nextBlockIndex
;
3012 /******************************************************************************
3013 * Storage32Impl_SetNextBlockInChain
3015 * This method will write the index of the specified block's next block
3016 * in the big block depot.
3018 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3021 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3022 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3023 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3026 static void StorageImpl_SetNextBlockInChain(
3031 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3032 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3033 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3034 ULONG depotBlockIndexPos
;
3036 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3037 assert(blockIndex
!= nextBlock
);
3039 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3041 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3046 * We have to look in the extended depot.
3048 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3051 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3054 * Update the cached block depot, if necessary.
3056 if (depotBlockCount
== This
->indexBlockDepotCached
)
3058 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3062 /******************************************************************************
3063 * Storage32Impl_LoadFileHeader
3065 * This method will read in the file header, i.e. big block index -1.
3067 static HRESULT
StorageImpl_LoadFileHeader(
3070 HRESULT hr
= STG_E_FILENOTFOUND
;
3071 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3077 * Get a pointer to the big block of data containing the header.
3079 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3082 * Extract the information from the header.
3087 * Check for the "magic number" signature and return an error if it is not
3090 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3092 return STG_E_OLDFORMAT
;
3095 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3097 return STG_E_INVALIDHEADER
;
3100 StorageUtl_ReadWord(
3102 OFFSET_BIGBLOCKSIZEBITS
,
3103 &This
->bigBlockSizeBits
);
3105 StorageUtl_ReadWord(
3107 OFFSET_SMALLBLOCKSIZEBITS
,
3108 &This
->smallBlockSizeBits
);
3110 StorageUtl_ReadDWord(
3112 OFFSET_BBDEPOTCOUNT
,
3113 &This
->bigBlockDepotCount
);
3115 StorageUtl_ReadDWord(
3117 OFFSET_ROOTSTARTBLOCK
,
3118 &This
->rootStartBlock
);
3120 StorageUtl_ReadDWord(
3122 OFFSET_SBDEPOTSTART
,
3123 &This
->smallBlockDepotStart
);
3125 StorageUtl_ReadDWord(
3127 OFFSET_EXTBBDEPOTSTART
,
3128 &This
->extBigBlockDepotStart
);
3130 StorageUtl_ReadDWord(
3132 OFFSET_EXTBBDEPOTCOUNT
,
3133 &This
->extBigBlockDepotCount
);
3135 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3137 StorageUtl_ReadDWord(
3139 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3140 &(This
->bigBlockDepotStart
[index
]));
3144 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3148 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3149 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3153 This
->bigBlockSize
= 0x000000001 >> (DWORD
)This
->bigBlockSizeBits
;
3154 This
->smallBlockSize
= 0x000000001 >> (DWORD
)This
->smallBlockSizeBits
;
3158 * Right now, the code is making some assumptions about the size of the
3159 * blocks, just make sure they are what we're expecting.
3161 if (This
->bigBlockSize
!= DEF_BIG_BLOCK_SIZE
||
3162 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
)
3164 WARN("Broken OLE storage file\n");
3165 hr
= STG_E_INVALIDHEADER
;
3174 /******************************************************************************
3175 * Storage32Impl_SaveFileHeader
3177 * This method will save to the file the header, i.e. big block -1.
3179 static void StorageImpl_SaveFileHeader(
3182 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3187 * Get a pointer to the big block of data containing the header.
3189 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3192 * If the block read failed, the file is probably new.
3197 * Initialize for all unknown fields.
3199 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
3202 * Initialize the magic number.
3204 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3207 * And a bunch of things we don't know what they mean
3209 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3210 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3211 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3212 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
3216 * Write the information to the header.
3218 StorageUtl_WriteWord(
3220 OFFSET_BIGBLOCKSIZEBITS
,
3221 This
->bigBlockSizeBits
);
3223 StorageUtl_WriteWord(
3225 OFFSET_SMALLBLOCKSIZEBITS
,
3226 This
->smallBlockSizeBits
);
3228 StorageUtl_WriteDWord(
3230 OFFSET_BBDEPOTCOUNT
,
3231 This
->bigBlockDepotCount
);
3233 StorageUtl_WriteDWord(
3235 OFFSET_ROOTSTARTBLOCK
,
3236 This
->rootStartBlock
);
3238 StorageUtl_WriteDWord(
3240 OFFSET_SBDEPOTSTART
,
3241 This
->smallBlockDepotStart
);
3243 StorageUtl_WriteDWord(
3245 OFFSET_SBDEPOTCOUNT
,
3246 This
->smallBlockDepotChain
?
3247 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3249 StorageUtl_WriteDWord(
3251 OFFSET_EXTBBDEPOTSTART
,
3252 This
->extBigBlockDepotStart
);
3254 StorageUtl_WriteDWord(
3256 OFFSET_EXTBBDEPOTCOUNT
,
3257 This
->extBigBlockDepotCount
);
3259 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3261 StorageUtl_WriteDWord(
3263 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3264 (This
->bigBlockDepotStart
[index
]));
3268 * Write the big block back to the file.
3270 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
3273 /******************************************************************************
3274 * Storage32Impl_ReadProperty
3276 * This method will read the specified property from the property chain.
3278 BOOL
StorageImpl_ReadProperty(
3281 StgProperty
* buffer
)
3283 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
3284 ULARGE_INTEGER offsetInPropSet
;
3288 offsetInPropSet
.u
.HighPart
= 0;
3289 offsetInPropSet
.u
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
3291 readRes
= BlockChainStream_ReadAt(
3292 This
->rootBlockChain
,
3298 if (SUCCEEDED(readRes
))
3300 /* replace the name of root entry (often "Root Entry") by the file name */
3301 WCHAR
*propName
= (index
== This
->base
.rootPropertySetIndex
) ?
3302 This
->filename
: (WCHAR
*)currentProperty
+OFFSET_PS_NAME
;
3304 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3308 PROPERTY_NAME_BUFFER_LEN
);
3309 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3311 memcpy(&buffer
->propertyType
, currentProperty
+ OFFSET_PS_PROPERTYTYPE
, 1);
3313 StorageUtl_ReadWord(
3315 OFFSET_PS_NAMELENGTH
,
3316 &buffer
->sizeOfNameString
);
3318 StorageUtl_ReadDWord(
3320 OFFSET_PS_PREVIOUSPROP
,
3321 &buffer
->previousProperty
);
3323 StorageUtl_ReadDWord(
3326 &buffer
->nextProperty
);
3328 StorageUtl_ReadDWord(
3331 &buffer
->dirProperty
);
3333 StorageUtl_ReadGUID(
3336 &buffer
->propertyUniqueID
);
3338 StorageUtl_ReadDWord(
3341 &buffer
->timeStampS1
);
3343 StorageUtl_ReadDWord(
3346 &buffer
->timeStampD1
);
3348 StorageUtl_ReadDWord(
3351 &buffer
->timeStampS2
);
3353 StorageUtl_ReadDWord(
3356 &buffer
->timeStampD2
);
3358 StorageUtl_ReadDWord(
3360 OFFSET_PS_STARTBLOCK
,
3361 &buffer
->startingBlock
);
3363 StorageUtl_ReadDWord(
3366 &buffer
->size
.u
.LowPart
);
3368 buffer
->size
.u
.HighPart
= 0;
3371 return SUCCEEDED(readRes
) ? TRUE
: FALSE
;
3374 /*********************************************************************
3375 * Write the specified property into the property chain
3377 BOOL
StorageImpl_WriteProperty(
3380 const StgProperty
* buffer
)
3382 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
3383 ULARGE_INTEGER offsetInPropSet
;
3387 offsetInPropSet
.u
.HighPart
= 0;
3388 offsetInPropSet
.u
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
3390 memset(currentProperty
, 0, PROPSET_BLOCK_SIZE
);
3393 currentProperty
+ OFFSET_PS_NAME
,
3395 PROPERTY_NAME_BUFFER_LEN
);
3397 memcpy(currentProperty
+ OFFSET_PS_PROPERTYTYPE
, &buffer
->propertyType
, 1);
3399 StorageUtl_WriteWord(
3401 OFFSET_PS_NAMELENGTH
,
3402 buffer
->sizeOfNameString
);
3404 StorageUtl_WriteDWord(
3406 OFFSET_PS_PREVIOUSPROP
,
3407 buffer
->previousProperty
);
3409 StorageUtl_WriteDWord(
3412 buffer
->nextProperty
);
3414 StorageUtl_WriteDWord(
3417 buffer
->dirProperty
);
3419 StorageUtl_WriteGUID(
3422 &buffer
->propertyUniqueID
);
3424 StorageUtl_WriteDWord(
3427 buffer
->timeStampS1
);
3429 StorageUtl_WriteDWord(
3432 buffer
->timeStampD1
);
3434 StorageUtl_WriteDWord(
3437 buffer
->timeStampS2
);
3439 StorageUtl_WriteDWord(
3442 buffer
->timeStampD2
);
3444 StorageUtl_WriteDWord(
3446 OFFSET_PS_STARTBLOCK
,
3447 buffer
->startingBlock
);
3449 StorageUtl_WriteDWord(
3452 buffer
->size
.u
.LowPart
);
3454 writeRes
= BlockChainStream_WriteAt(This
->rootBlockChain
,
3459 return SUCCEEDED(writeRes
) ? TRUE
: FALSE
;
3462 static BOOL
StorageImpl_ReadBigBlock(
3467 ULARGE_INTEGER ulOffset
;
3470 ulOffset
.u
.HighPart
= 0;
3471 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3473 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3474 return (read
== This
->bigBlockSize
);
3477 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3483 ULARGE_INTEGER ulOffset
;
3487 ulOffset
.u
.HighPart
= 0;
3488 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3489 ulOffset
.u
.LowPart
+= offset
;
3491 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3492 *value
= le32toh(tmp
);
3493 return (read
== sizeof(DWORD
));
3496 static BOOL
StorageImpl_WriteBigBlock(
3501 ULARGE_INTEGER ulOffset
;
3504 ulOffset
.u
.HighPart
= 0;
3505 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3507 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3508 return (wrote
== This
->bigBlockSize
);
3511 static BOOL
StorageImpl_WriteDWordToBigBlock(
3517 ULARGE_INTEGER ulOffset
;
3520 ulOffset
.u
.HighPart
= 0;
3521 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3522 ulOffset
.u
.LowPart
+= offset
;
3524 value
= htole32(value
);
3525 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3526 return (wrote
== sizeof(DWORD
));
3529 /******************************************************************************
3530 * Storage32Impl_SmallBlocksToBigBlocks
3532 * This method will convert a small block chain to a big block chain.
3533 * The small block chain will be destroyed.
3535 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3537 SmallBlockChainStream
** ppsbChain
)
3539 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3540 ULARGE_INTEGER size
, offset
;
3541 ULONG cbRead
, cbWritten
;
3542 ULARGE_INTEGER cbTotalRead
;
3543 ULONG propertyIndex
;
3544 HRESULT resWrite
= S_OK
;
3546 StgProperty chainProperty
;
3548 BlockChainStream
*bbTempChain
= NULL
;
3549 BlockChainStream
*bigBlockChain
= NULL
;
3552 * Create a temporary big block chain that doesn't have
3553 * an associated property. This temporary chain will be
3554 * used to copy data from small blocks to big blocks.
3556 bbTempChain
= BlockChainStream_Construct(This
,
3559 if(!bbTempChain
) return NULL
;
3561 * Grow the big block chain.
3563 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3564 BlockChainStream_SetSize(bbTempChain
, size
);
3567 * Copy the contents of the small block chain to the big block chain
3568 * by small block size increments.
3570 offset
.u
.LowPart
= 0;
3571 offset
.u
.HighPart
= 0;
3572 cbTotalRead
.QuadPart
= 0;
3574 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3577 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3579 This
->smallBlockSize
,
3582 if (FAILED(resRead
))
3587 cbTotalRead
.QuadPart
+= cbRead
;
3589 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3595 if (FAILED(resWrite
))
3598 offset
.u
.LowPart
+= This
->smallBlockSize
;
3600 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3601 HeapFree(GetProcessHeap(),0,buffer
);
3603 if (FAILED(resRead
) || FAILED(resWrite
))
3605 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3606 BlockChainStream_Destroy(bbTempChain
);
3611 * Destroy the small block chain.
3613 propertyIndex
= (*ppsbChain
)->ownerPropertyIndex
;
3614 size
.u
.HighPart
= 0;
3616 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3617 SmallBlockChainStream_Destroy(*ppsbChain
);
3621 * Change the property information. This chain is now a big block chain
3622 * and it doesn't reside in the small blocks chain anymore.
3624 StorageImpl_ReadProperty(This
, propertyIndex
, &chainProperty
);
3626 chainProperty
.startingBlock
= bbHeadOfChain
;
3628 StorageImpl_WriteProperty(This
, propertyIndex
, &chainProperty
);
3631 * Destroy the temporary propertyless big block chain.
3632 * Create a new big block chain associated with this property.
3634 BlockChainStream_Destroy(bbTempChain
);
3635 bigBlockChain
= BlockChainStream_Construct(This
,
3639 return bigBlockChain
;
3642 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
3644 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
3646 StorageBaseImpl_Release((IStorage
*)This
->base
.ancestorStorage
);
3647 HeapFree(GetProcessHeap(), 0, This
);
3650 /******************************************************************************
3652 ** Storage32InternalImpl_Commit
3654 ** The non-root storages cannot be opened in transacted mode thus this function
3657 static HRESULT WINAPI
StorageInternalImpl_Commit(
3659 DWORD grfCommitFlags
) /* [in] */
3664 /******************************************************************************
3666 ** Storage32InternalImpl_Revert
3668 ** The non-root storages cannot be opened in transacted mode thus this function
3671 static HRESULT WINAPI
StorageInternalImpl_Revert(
3677 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
3679 IStorage_Release((IStorage
*)This
->parentStorage
);
3680 HeapFree(GetProcessHeap(), 0, This
->stackToVisit
);
3681 HeapFree(GetProcessHeap(), 0, This
);
3684 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
3685 IEnumSTATSTG
* iface
,
3689 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3692 * Perform a sanity check on the parameters.
3695 return E_INVALIDARG
;
3698 * Initialize the return parameter.
3703 * Compare the riid with the interface IDs implemented by this object.
3705 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
3706 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
3708 *ppvObject
= (IEnumSTATSTG
*)This
;
3709 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
3713 return E_NOINTERFACE
;
3716 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
3717 IEnumSTATSTG
* iface
)
3719 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3720 return InterlockedIncrement(&This
->ref
);
3723 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
3724 IEnumSTATSTG
* iface
)
3726 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3730 newRef
= InterlockedDecrement(&This
->ref
);
3733 * If the reference count goes down to 0, perform suicide.
3737 IEnumSTATSTGImpl_Destroy(This
);
3743 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
3744 IEnumSTATSTG
* iface
,
3747 ULONG
* pceltFetched
)
3749 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3751 StgProperty currentProperty
;
3752 STATSTG
* currentReturnStruct
= rgelt
;
3753 ULONG objectFetched
= 0;
3754 ULONG currentSearchNode
;
3757 * Perform a sanity check on the parameters.
3759 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
3760 return E_INVALIDARG
;
3763 * To avoid the special case, get another pointer to a ULONG value if
3764 * the caller didn't supply one.
3766 if (pceltFetched
==0)
3767 pceltFetched
= &objectFetched
;
3770 * Start the iteration, we will iterate until we hit the end of the
3771 * linked list or until we hit the number of items to iterate through
3776 * Start with the node at the top of the stack.
3778 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3780 while ( ( *pceltFetched
< celt
) &&
3781 ( currentSearchNode
!=PROPERTY_NULL
) )
3784 * Remove the top node from the stack
3786 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3789 * Read the property from the storage.
3791 StorageImpl_ReadProperty(This
->parentStorage
,
3796 * Copy the information to the return buffer.
3798 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct
,
3803 * Step to the next item in the iteration
3806 currentReturnStruct
++;
3809 * Push the next search node in the search stack.
3811 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3814 * continue the iteration.
3816 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3819 if (*pceltFetched
== celt
)
3826 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
3827 IEnumSTATSTG
* iface
,
3830 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3832 StgProperty currentProperty
;
3833 ULONG objectFetched
= 0;
3834 ULONG currentSearchNode
;
3837 * Start with the node at the top of the stack.
3839 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3841 while ( (objectFetched
< celt
) &&
3842 (currentSearchNode
!=PROPERTY_NULL
) )
3845 * Remove the top node from the stack
3847 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3850 * Read the property from the storage.
3852 StorageImpl_ReadProperty(This
->parentStorage
,
3857 * Step to the next item in the iteration
3862 * Push the next search node in the search stack.
3864 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3867 * continue the iteration.
3869 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3872 if (objectFetched
== celt
)
3878 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
3879 IEnumSTATSTG
* iface
)
3881 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3883 StgProperty rootProperty
;
3884 BOOL readSuccessful
;
3887 * Re-initialize the search stack to an empty stack
3889 This
->stackSize
= 0;
3892 * Read the root property from the storage.
3894 readSuccessful
= StorageImpl_ReadProperty(
3895 This
->parentStorage
,
3896 This
->firstPropertyNode
,
3901 assert(rootProperty
.sizeOfNameString
!=0);
3904 * Push the search node in the search stack.
3906 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.dirProperty
);
3912 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
3913 IEnumSTATSTG
* iface
,
3914 IEnumSTATSTG
** ppenum
)
3916 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3918 IEnumSTATSTGImpl
* newClone
;
3921 * Perform a sanity check on the parameters.
3924 return E_INVALIDARG
;
3926 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
3927 This
->firstPropertyNode
);
3931 * The new clone enumeration must point to the same current node as
3934 newClone
->stackSize
= This
->stackSize
;
3935 newClone
->stackMaxSize
= This
->stackMaxSize
;
3936 newClone
->stackToVisit
=
3937 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * newClone
->stackMaxSize
);
3940 newClone
->stackToVisit
,
3942 sizeof(ULONG
) * newClone
->stackSize
);
3944 *ppenum
= (IEnumSTATSTG
*)newClone
;
3947 * Don't forget to nail down a reference to the clone before
3950 IEnumSTATSTGImpl_AddRef(*ppenum
);
3955 static INT
IEnumSTATSTGImpl_FindParentProperty(
3956 IEnumSTATSTGImpl
*This
,
3957 ULONG childProperty
,
3958 StgProperty
*currentProperty
,
3961 ULONG currentSearchNode
;
3965 * To avoid the special case, get another pointer to a ULONG value if
3966 * the caller didn't supply one.
3969 thisNodeId
= &foundNode
;
3972 * Start with the node at the top of the stack.
3974 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3977 while (currentSearchNode
!=PROPERTY_NULL
)
3980 * Store the current node in the returned parameters
3982 *thisNodeId
= currentSearchNode
;
3985 * Remove the top node from the stack
3987 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3990 * Read the property from the storage.
3992 StorageImpl_ReadProperty(
3993 This
->parentStorage
,
3997 if (currentProperty
->previousProperty
== childProperty
)
3998 return PROPERTY_RELATION_PREVIOUS
;
4000 else if (currentProperty
->nextProperty
== childProperty
)
4001 return PROPERTY_RELATION_NEXT
;
4003 else if (currentProperty
->dirProperty
== childProperty
)
4004 return PROPERTY_RELATION_DIR
;
4007 * Push the next search node in the search stack.
4009 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
4012 * continue the iteration.
4014 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
4017 return PROPERTY_NULL
;
4020 static ULONG
IEnumSTATSTGImpl_FindProperty(
4021 IEnumSTATSTGImpl
* This
,
4022 const OLECHAR
* lpszPropName
,
4023 StgProperty
* currentProperty
)
4025 ULONG currentSearchNode
;
4028 * Start with the node at the top of the stack.
4030 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
4032 while (currentSearchNode
!=PROPERTY_NULL
)
4035 * Remove the top node from the stack
4037 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
4040 * Read the property from the storage.
4042 StorageImpl_ReadProperty(This
->parentStorage
,
4046 if ( propertyNameCmp(
4047 (const OLECHAR
*)currentProperty
->name
, lpszPropName
) == 0)
4048 return currentSearchNode
;
4051 * Push the next search node in the search stack.
4053 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
4056 * continue the iteration.
4058 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
4061 return PROPERTY_NULL
;
4064 static void IEnumSTATSTGImpl_PushSearchNode(
4065 IEnumSTATSTGImpl
* This
,
4068 StgProperty rootProperty
;
4069 BOOL readSuccessful
;
4072 * First, make sure we're not trying to push an unexisting node.
4074 if (nodeToPush
==PROPERTY_NULL
)
4078 * First push the node to the stack
4080 if (This
->stackSize
== This
->stackMaxSize
)
4082 This
->stackMaxSize
+= ENUMSTATSGT_SIZE_INCREMENT
;
4084 This
->stackToVisit
= HeapReAlloc(
4088 sizeof(ULONG
) * This
->stackMaxSize
);
4091 This
->stackToVisit
[This
->stackSize
] = nodeToPush
;
4095 * Read the root property from the storage.
4097 readSuccessful
= StorageImpl_ReadProperty(
4098 This
->parentStorage
,
4104 assert(rootProperty
.sizeOfNameString
!=0);
4107 * Push the previous search node in the search stack.
4109 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.previousProperty
);
4113 static ULONG
IEnumSTATSTGImpl_PopSearchNode(
4114 IEnumSTATSTGImpl
* This
,
4119 if (This
->stackSize
== 0)
4120 return PROPERTY_NULL
;
4122 topNode
= This
->stackToVisit
[This
->stackSize
-1];
4131 * Virtual function table for the IEnumSTATSTGImpl class.
4133 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
4135 IEnumSTATSTGImpl_QueryInterface
,
4136 IEnumSTATSTGImpl_AddRef
,
4137 IEnumSTATSTGImpl_Release
,
4138 IEnumSTATSTGImpl_Next
,
4139 IEnumSTATSTGImpl_Skip
,
4140 IEnumSTATSTGImpl_Reset
,
4141 IEnumSTATSTGImpl_Clone
4144 /******************************************************************************
4145 ** IEnumSTATSTGImpl implementation
4148 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
4149 StorageImpl
* parentStorage
,
4150 ULONG firstPropertyNode
)
4152 IEnumSTATSTGImpl
* newEnumeration
;
4154 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
4156 if (newEnumeration
!=0)
4159 * Set-up the virtual function table and reference count.
4161 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
4162 newEnumeration
->ref
= 0;
4165 * We want to nail-down the reference to the storage in case the
4166 * enumeration out-lives the storage in the client application.
4168 newEnumeration
->parentStorage
= parentStorage
;
4169 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
4171 newEnumeration
->firstPropertyNode
= firstPropertyNode
;
4174 * Initialize the search stack
4176 newEnumeration
->stackSize
= 0;
4177 newEnumeration
->stackMaxSize
= ENUMSTATSGT_SIZE_INCREMENT
;
4178 newEnumeration
->stackToVisit
=
4179 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
)*ENUMSTATSGT_SIZE_INCREMENT
);
4182 * Make sure the current node of the iterator is the first one.
4184 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
4187 return newEnumeration
;
4191 * Virtual function table for the Storage32InternalImpl class.
4193 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
4195 StorageBaseImpl_QueryInterface
,
4196 StorageBaseImpl_AddRef
,
4197 StorageBaseImpl_Release
,
4198 StorageBaseImpl_CreateStream
,
4199 StorageBaseImpl_OpenStream
,
4200 StorageImpl_CreateStorage
,
4201 StorageBaseImpl_OpenStorage
,
4203 StorageImpl_MoveElementTo
,
4204 StorageInternalImpl_Commit
,
4205 StorageInternalImpl_Revert
,
4206 StorageBaseImpl_EnumElements
,
4207 StorageImpl_DestroyElement
,
4208 StorageBaseImpl_RenameElement
,
4209 StorageImpl_SetElementTimes
,
4210 StorageBaseImpl_SetClass
,
4211 StorageImpl_SetStateBits
,
4212 StorageBaseImpl_Stat
4215 /******************************************************************************
4216 ** Storage32InternalImpl implementation
4219 static StorageInternalImpl
* StorageInternalImpl_Construct(
4220 StorageImpl
* ancestorStorage
,
4222 ULONG rootPropertyIndex
)
4224 StorageInternalImpl
* newStorage
;
4227 * Allocate space for the new storage object
4229 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
4234 * Initialize the stream list
4236 list_init(&newStorage
->base
.strmHead
);
4239 * Initialize the virtual function table.
4241 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
4242 newStorage
->base
.v_destructor
= &StorageInternalImpl_Destroy
;
4243 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
4246 * Keep the ancestor storage pointer and nail a reference to it.
4248 newStorage
->base
.ancestorStorage
= ancestorStorage
;
4249 StorageBaseImpl_AddRef((IStorage
*)(newStorage
->base
.ancestorStorage
));
4252 * Keep the index of the root property set for this storage,
4254 newStorage
->base
.rootPropertySetIndex
= rootPropertyIndex
;
4262 /******************************************************************************
4263 ** StorageUtl implementation
4266 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
4270 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
4271 *value
= le16toh(tmp
);
4274 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
4276 value
= htole16(value
);
4277 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
4280 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
4284 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
4285 *value
= le32toh(tmp
);
4288 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
4290 value
= htole32(value
);
4291 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
4294 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
4295 ULARGE_INTEGER
* value
)
4297 #ifdef WORDS_BIGENDIAN
4300 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4301 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
4302 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
4304 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4308 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
4309 const ULARGE_INTEGER
*value
)
4311 #ifdef WORDS_BIGENDIAN
4314 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
4315 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
4316 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
4318 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
4322 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
4324 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
4325 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
4326 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
4328 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
4331 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
4333 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
4334 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
4335 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
4337 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
4340 void StorageUtl_CopyPropertyToSTATSTG(
4341 STATSTG
* destination
,
4342 const StgProperty
* source
,
4346 * The copy of the string occurs only when the flag is not set
4348 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
4349 (source
->name
== NULL
) ||
4350 (source
->name
[0] == 0) )
4352 destination
->pwcsName
= 0;
4356 destination
->pwcsName
=
4357 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
4359 strcpyW((LPWSTR
)destination
->pwcsName
, source
->name
);
4362 switch (source
->propertyType
)
4364 case PROPTYPE_STORAGE
:
4366 destination
->type
= STGTY_STORAGE
;
4368 case PROPTYPE_STREAM
:
4369 destination
->type
= STGTY_STREAM
;
4372 destination
->type
= STGTY_STREAM
;
4376 destination
->cbSize
= source
->size
;
4378 currentReturnStruct->mtime = {0}; TODO
4379 currentReturnStruct->ctime = {0};
4380 currentReturnStruct->atime = {0};
4382 destination
->grfMode
= 0;
4383 destination
->grfLocksSupported
= 0;
4384 destination
->clsid
= source
->propertyUniqueID
;
4385 destination
->grfStateBits
= 0;
4386 destination
->reserved
= 0;
4389 /******************************************************************************
4390 ** BlockChainStream implementation
4393 BlockChainStream
* BlockChainStream_Construct(
4394 StorageImpl
* parentStorage
,
4395 ULONG
* headOfStreamPlaceHolder
,
4396 ULONG propertyIndex
)
4398 BlockChainStream
* newStream
;
4401 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
4403 newStream
->parentStorage
= parentStorage
;
4404 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
4405 newStream
->ownerPropertyIndex
= propertyIndex
;
4406 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
4407 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
4408 newStream
->numBlocks
= 0;
4410 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
4412 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4414 newStream
->numBlocks
++;
4415 newStream
->tailIndex
= blockIndex
;
4417 if(FAILED(StorageImpl_GetNextBlockInChain(
4422 HeapFree(GetProcessHeap(), 0, newStream
);
4430 void BlockChainStream_Destroy(BlockChainStream
* This
)
4432 HeapFree(GetProcessHeap(), 0, This
);
4435 /******************************************************************************
4436 * BlockChainStream_GetHeadOfChain
4438 * Returns the head of this stream chain.
4439 * Some special chains don't have properties, their heads are kept in
4440 * This->headOfStreamPlaceHolder.
4443 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
4445 StgProperty chainProperty
;
4446 BOOL readSuccessful
;
4448 if (This
->headOfStreamPlaceHolder
!= 0)
4449 return *(This
->headOfStreamPlaceHolder
);
4451 if (This
->ownerPropertyIndex
!= PROPERTY_NULL
)
4453 readSuccessful
= StorageImpl_ReadProperty(
4454 This
->parentStorage
,
4455 This
->ownerPropertyIndex
,
4460 return chainProperty
.startingBlock
;
4464 return BLOCK_END_OF_CHAIN
;
4467 /******************************************************************************
4468 * BlockChainStream_GetCount
4470 * Returns the number of blocks that comprises this chain.
4471 * This is not the size of the stream as the last block may not be full!
4474 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
4479 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4481 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4485 if(FAILED(StorageImpl_GetNextBlockInChain(
4486 This
->parentStorage
,
4495 /******************************************************************************
4496 * BlockChainStream_ReadAt
4498 * Reads a specified number of bytes from this chain at the specified offset.
4499 * bytesRead may be NULL.
4500 * Failure will be returned if the specified number of bytes has not been read.
4502 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
4503 ULARGE_INTEGER offset
,
4508 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4509 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4510 ULONG bytesToReadInBuffer
;
4514 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
4517 * Find the first block in the stream that contains part of the buffer.
4519 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4520 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4521 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4523 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4524 This
->lastBlockNoInSequence
= blockNoInSequence
;
4528 ULONG temp
= blockNoInSequence
;
4530 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4531 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4532 This
->lastBlockNoInSequence
= temp
;
4535 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4537 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4538 return STG_E_DOCFILECORRUPT
;
4539 blockNoInSequence
--;
4542 if ((blockNoInSequence
> 0) && (blockIndex
== BLOCK_END_OF_CHAIN
))
4543 return STG_E_DOCFILECORRUPT
; /* We failed to find the starting block */
4545 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4548 * Start reading the buffer.
4551 bufferWalker
= buffer
;
4553 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4555 ULARGE_INTEGER ulOffset
;
4558 * Calculate how many bytes we can copy from this big block.
4560 bytesToReadInBuffer
=
4561 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4563 TRACE("block %i\n",blockIndex
);
4564 ulOffset
.u
.HighPart
= 0;
4565 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
4568 StorageImpl_ReadAt(This
->parentStorage
,
4571 bytesToReadInBuffer
,
4574 * Step to the next big block.
4576 if( size
> bytesReadAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4577 return STG_E_DOCFILECORRUPT
;
4579 bufferWalker
+= bytesReadAt
;
4580 size
-= bytesReadAt
;
4581 *bytesRead
+= bytesReadAt
;
4582 offsetInBlock
= 0; /* There is no offset on the next block */
4584 if (bytesToReadInBuffer
!= bytesReadAt
)
4588 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
4591 /******************************************************************************
4592 * BlockChainStream_WriteAt
4594 * Writes the specified number of bytes to this chain at the specified offset.
4595 * bytesWritten may be NULL.
4596 * Will fail if not all specified number of bytes have been written.
4598 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
4599 ULARGE_INTEGER offset
,
4602 ULONG
* bytesWritten
)
4604 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4605 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4608 const BYTE
* bufferWalker
;
4611 * Find the first block in the stream that contains part of the buffer.
4613 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4614 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4615 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4617 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4618 This
->lastBlockNoInSequence
= blockNoInSequence
;
4622 ULONG temp
= blockNoInSequence
;
4624 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4625 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4626 This
->lastBlockNoInSequence
= temp
;
4629 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4631 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4633 return STG_E_DOCFILECORRUPT
;
4634 blockNoInSequence
--;
4637 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4639 /* BlockChainStream_SetSize should have already been called to ensure we have
4640 * enough blocks in the chain to write into */
4641 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4643 ERR("not enough blocks in chain to write data\n");
4644 return STG_E_DOCFILECORRUPT
;
4648 bufferWalker
= (const BYTE
*)buffer
;
4650 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4652 ULARGE_INTEGER ulOffset
;
4653 DWORD bytesWrittenAt
;
4655 * Calculate how many bytes we can copy from this big block.
4658 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4660 TRACE("block %i\n",blockIndex
);
4661 ulOffset
.u
.HighPart
= 0;
4662 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
4665 StorageImpl_WriteAt(This
->parentStorage
,
4672 * Step to the next big block.
4674 if(size
> bytesWrittenAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4676 return STG_E_DOCFILECORRUPT
;
4678 bufferWalker
+= bytesWrittenAt
;
4679 size
-= bytesWrittenAt
;
4680 *bytesWritten
+= bytesWrittenAt
;
4681 offsetInBlock
= 0; /* There is no offset on the next block */
4683 if (bytesWrittenAt
!= bytesToWrite
)
4687 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
4690 /******************************************************************************
4691 * BlockChainStream_Shrink
4693 * Shrinks this chain in the big block depot.
4695 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
4696 ULARGE_INTEGER newSize
)
4698 ULONG blockIndex
, extraBlock
;
4703 * Reset the last accessed block cache.
4705 This
->lastBlockNoInSequence
= 0xFFFFFFFF;
4706 This
->lastBlockNoInSequenceIndex
= BLOCK_END_OF_CHAIN
;
4709 * Figure out how many blocks are needed to contain the new size
4711 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4713 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4716 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4719 * Go to the new end of chain
4721 while (count
< numBlocks
)
4723 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4729 /* Get the next block before marking the new end */
4730 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4734 /* Mark the new end of chain */
4735 StorageImpl_SetNextBlockInChain(
4736 This
->parentStorage
,
4738 BLOCK_END_OF_CHAIN
);
4740 This
->tailIndex
= blockIndex
;
4741 This
->numBlocks
= numBlocks
;
4744 * Mark the extra blocks as free
4746 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4748 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
,
4751 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
4752 extraBlock
= blockIndex
;
4758 /******************************************************************************
4759 * BlockChainStream_Enlarge
4761 * Grows this chain in the big block depot.
4763 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
4764 ULARGE_INTEGER newSize
)
4766 ULONG blockIndex
, currentBlock
;
4768 ULONG oldNumBlocks
= 0;
4770 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4773 * Empty chain. Create the head.
4775 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4777 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4778 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
4780 BLOCK_END_OF_CHAIN
);
4782 if (This
->headOfStreamPlaceHolder
!= 0)
4784 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
4788 StgProperty chainProp
;
4789 assert(This
->ownerPropertyIndex
!= PROPERTY_NULL
);
4791 StorageImpl_ReadProperty(
4792 This
->parentStorage
,
4793 This
->ownerPropertyIndex
,
4796 chainProp
.startingBlock
= blockIndex
;
4798 StorageImpl_WriteProperty(
4799 This
->parentStorage
,
4800 This
->ownerPropertyIndex
,
4804 This
->tailIndex
= blockIndex
;
4805 This
->numBlocks
= 1;
4809 * Figure out how many blocks are needed to contain this stream
4811 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4813 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4817 * Go to the current end of chain
4819 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
4821 currentBlock
= blockIndex
;
4823 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4826 currentBlock
= blockIndex
;
4828 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
4833 This
->tailIndex
= currentBlock
;
4836 currentBlock
= This
->tailIndex
;
4837 oldNumBlocks
= This
->numBlocks
;
4840 * Add new blocks to the chain
4842 if (oldNumBlocks
< newNumBlocks
)
4844 while (oldNumBlocks
< newNumBlocks
)
4846 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4848 StorageImpl_SetNextBlockInChain(
4849 This
->parentStorage
,
4853 StorageImpl_SetNextBlockInChain(
4854 This
->parentStorage
,
4856 BLOCK_END_OF_CHAIN
);
4858 currentBlock
= blockIndex
;
4862 This
->tailIndex
= blockIndex
;
4863 This
->numBlocks
= newNumBlocks
;
4869 /******************************************************************************
4870 * BlockChainStream_SetSize
4872 * Sets the size of this stream. The big block depot will be updated.
4873 * The file will grow if we grow the chain.
4875 * TODO: Free the actual blocks in the file when we shrink the chain.
4876 * Currently, the blocks are still in the file. So the file size
4877 * doesn't shrink even if we shrink streams.
4879 BOOL
BlockChainStream_SetSize(
4880 BlockChainStream
* This
,
4881 ULARGE_INTEGER newSize
)
4883 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
4885 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
4888 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
4890 BlockChainStream_Shrink(This
, newSize
);
4894 BlockChainStream_Enlarge(This
, newSize
);
4900 /******************************************************************************
4901 * BlockChainStream_GetSize
4903 * Returns the size of this chain.
4904 * Will return the block count if this chain doesn't have a property.
4906 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
4908 StgProperty chainProperty
;
4910 if(This
->headOfStreamPlaceHolder
== NULL
)
4913 * This chain is a data stream read the property and return
4914 * the appropriate size
4916 StorageImpl_ReadProperty(
4917 This
->parentStorage
,
4918 This
->ownerPropertyIndex
,
4921 return chainProperty
.size
;
4926 * this chain is a chain that does not have a property, figure out the
4927 * size by making the product number of used blocks times the
4930 ULARGE_INTEGER result
;
4931 result
.u
.HighPart
= 0;
4934 BlockChainStream_GetCount(This
) *
4935 This
->parentStorage
->bigBlockSize
;
4941 /******************************************************************************
4942 ** SmallBlockChainStream implementation
4945 SmallBlockChainStream
* SmallBlockChainStream_Construct(
4946 StorageImpl
* parentStorage
,
4947 ULONG propertyIndex
)
4949 SmallBlockChainStream
* newStream
;
4951 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
4953 newStream
->parentStorage
= parentStorage
;
4954 newStream
->ownerPropertyIndex
= propertyIndex
;
4959 void SmallBlockChainStream_Destroy(
4960 SmallBlockChainStream
* This
)
4962 HeapFree(GetProcessHeap(), 0, This
);
4965 /******************************************************************************
4966 * SmallBlockChainStream_GetHeadOfChain
4968 * Returns the head of this chain of small blocks.
4970 static ULONG
SmallBlockChainStream_GetHeadOfChain(
4971 SmallBlockChainStream
* This
)
4973 StgProperty chainProperty
;
4974 BOOL readSuccessful
;
4976 if (This
->ownerPropertyIndex
)
4978 readSuccessful
= StorageImpl_ReadProperty(
4979 This
->parentStorage
,
4980 This
->ownerPropertyIndex
,
4985 return chainProperty
.startingBlock
;
4990 return BLOCK_END_OF_CHAIN
;
4993 /******************************************************************************
4994 * SmallBlockChainStream_GetNextBlockInChain
4996 * Returns the index of the next small block in this chain.
4999 * - BLOCK_END_OF_CHAIN: end of this chain
5000 * - BLOCK_UNUSED: small block 'blockIndex' is free
5002 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
5003 SmallBlockChainStream
* This
,
5005 ULONG
* nextBlockInChain
)
5007 ULARGE_INTEGER offsetOfBlockInDepot
;
5012 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
5014 offsetOfBlockInDepot
.u
.HighPart
= 0;
5015 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5018 * Read those bytes in the buffer from the small block file.
5020 res
= BlockChainStream_ReadAt(
5021 This
->parentStorage
->smallBlockDepotChain
,
5022 offsetOfBlockInDepot
,
5029 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
5036 /******************************************************************************
5037 * SmallBlockChainStream_SetNextBlockInChain
5039 * Writes the index of the next block of the specified block in the small
5041 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5042 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5044 static void SmallBlockChainStream_SetNextBlockInChain(
5045 SmallBlockChainStream
* This
,
5049 ULARGE_INTEGER offsetOfBlockInDepot
;
5053 offsetOfBlockInDepot
.u
.HighPart
= 0;
5054 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5056 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
5059 * Read those bytes in the buffer from the small block file.
5061 BlockChainStream_WriteAt(
5062 This
->parentStorage
->smallBlockDepotChain
,
5063 offsetOfBlockInDepot
,
5069 /******************************************************************************
5070 * SmallBlockChainStream_FreeBlock
5072 * Flag small block 'blockIndex' as free in the small block depot.
5074 static void SmallBlockChainStream_FreeBlock(
5075 SmallBlockChainStream
* This
,
5078 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
5081 /******************************************************************************
5082 * SmallBlockChainStream_GetNextFreeBlock
5084 * Returns the index of a free small block. The small block depot will be
5085 * enlarged if necessary. The small block chain will also be enlarged if
5088 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
5089 SmallBlockChainStream
* This
)
5091 ULARGE_INTEGER offsetOfBlockInDepot
;
5094 ULONG blockIndex
= 0;
5095 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
5097 ULONG smallBlocksPerBigBlock
;
5099 offsetOfBlockInDepot
.u
.HighPart
= 0;
5102 * Scan the small block depot for a free block
5104 while (nextBlockIndex
!= BLOCK_UNUSED
)
5106 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5108 res
= BlockChainStream_ReadAt(
5109 This
->parentStorage
->smallBlockDepotChain
,
5110 offsetOfBlockInDepot
,
5116 * If we run out of space for the small block depot, enlarge it
5120 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
5122 if (nextBlockIndex
!= BLOCK_UNUSED
)
5128 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
5130 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
5131 ULONG nextBlock
, newsbdIndex
;
5132 BYTE smallBlockDepot
[BIG_BLOCK_SIZE
];
5134 nextBlock
= sbdIndex
;
5135 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
5137 sbdIndex
= nextBlock
;
5138 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
, &nextBlock
);
5141 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5142 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
5143 StorageImpl_SetNextBlockInChain(
5144 This
->parentStorage
,
5148 StorageImpl_SetNextBlockInChain(
5149 This
->parentStorage
,
5151 BLOCK_END_OF_CHAIN
);
5154 * Initialize all the small blocks to free
5156 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
5157 StorageImpl_WriteBigBlock(This
->parentStorage
, newsbdIndex
, smallBlockDepot
);
5162 * We have just created the small block depot.
5164 StgProperty rootProp
;
5168 * Save it in the header
5170 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
5171 StorageImpl_SaveFileHeader(This
->parentStorage
);
5174 * And allocate the first big block that will contain small blocks
5177 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5179 StorageImpl_SetNextBlockInChain(
5180 This
->parentStorage
,
5182 BLOCK_END_OF_CHAIN
);
5184 StorageImpl_ReadProperty(
5185 This
->parentStorage
,
5186 This
->parentStorage
->base
.rootPropertySetIndex
,
5189 rootProp
.startingBlock
= sbStartIndex
;
5190 rootProp
.size
.u
.HighPart
= 0;
5191 rootProp
.size
.u
.LowPart
= This
->parentStorage
->bigBlockSize
;
5193 StorageImpl_WriteProperty(
5194 This
->parentStorage
,
5195 This
->parentStorage
->base
.rootPropertySetIndex
,
5199 StorageImpl_SaveFileHeader(This
->parentStorage
);
5203 smallBlocksPerBigBlock
=
5204 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
5207 * Verify if we have to allocate big blocks to contain small blocks
5209 if (blockIndex
% smallBlocksPerBigBlock
== 0)
5211 StgProperty rootProp
;
5212 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
5214 StorageImpl_ReadProperty(
5215 This
->parentStorage
,
5216 This
->parentStorage
->base
.rootPropertySetIndex
,
5219 if (rootProp
.size
.u
.LowPart
<
5220 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
5222 rootProp
.size
.u
.LowPart
+= This
->parentStorage
->bigBlockSize
;
5224 BlockChainStream_SetSize(
5225 This
->parentStorage
->smallBlockRootChain
,
5228 StorageImpl_WriteProperty(
5229 This
->parentStorage
,
5230 This
->parentStorage
->base
.rootPropertySetIndex
,
5238 /******************************************************************************
5239 * SmallBlockChainStream_ReadAt
5241 * Reads a specified number of bytes from this chain at the specified offset.
5242 * bytesRead may be NULL.
5243 * Failure will be returned if the specified number of bytes has not been read.
5245 HRESULT
SmallBlockChainStream_ReadAt(
5246 SmallBlockChainStream
* This
,
5247 ULARGE_INTEGER offset
,
5253 ULARGE_INTEGER offsetInBigBlockFile
;
5254 ULONG blockNoInSequence
=
5255 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5257 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5258 ULONG bytesToReadInBuffer
;
5260 ULONG bytesReadFromBigBlockFile
;
5264 * This should never happen on a small block file.
5266 assert(offset
.u
.HighPart
==0);
5269 * Find the first block in the stream that contains part of the buffer.
5271 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5273 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5275 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5278 blockNoInSequence
--;
5282 * Start reading the buffer.
5285 bufferWalker
= buffer
;
5287 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5290 * Calculate how many bytes we can copy from this small block.
5292 bytesToReadInBuffer
=
5293 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5296 * Calculate the offset of the small block in the small block file.
5298 offsetInBigBlockFile
.u
.HighPart
= 0;
5299 offsetInBigBlockFile
.u
.LowPart
=
5300 blockIndex
* This
->parentStorage
->smallBlockSize
;
5302 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5305 * Read those bytes in the buffer from the small block file.
5306 * The small block has already been identified so it shouldn't fail
5307 * unless the file is corrupt.
5309 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
5310 offsetInBigBlockFile
,
5311 bytesToReadInBuffer
,
5313 &bytesReadFromBigBlockFile
);
5319 * Step to the next big block.
5321 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5323 return STG_E_DOCFILECORRUPT
;
5325 bufferWalker
+= bytesReadFromBigBlockFile
;
5326 size
-= bytesReadFromBigBlockFile
;
5327 *bytesRead
+= bytesReadFromBigBlockFile
;
5328 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5331 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5334 /******************************************************************************
5335 * SmallBlockChainStream_WriteAt
5337 * Writes the specified number of bytes to this chain at the specified offset.
5338 * bytesWritten may be NULL.
5339 * Will fail if not all specified number of bytes have been written.
5341 HRESULT
SmallBlockChainStream_WriteAt(
5342 SmallBlockChainStream
* This
,
5343 ULARGE_INTEGER offset
,
5346 ULONG
* bytesWritten
)
5348 ULARGE_INTEGER offsetInBigBlockFile
;
5349 ULONG blockNoInSequence
=
5350 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5352 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5353 ULONG bytesToWriteInBuffer
;
5355 ULONG bytesWrittenToBigBlockFile
;
5356 const BYTE
* bufferWalker
;
5360 * This should never happen on a small block file.
5362 assert(offset
.u
.HighPart
==0);
5365 * Find the first block in the stream that contains part of the buffer.
5367 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5369 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5371 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5372 return STG_E_DOCFILECORRUPT
;
5373 blockNoInSequence
--;
5377 * Start writing the buffer.
5379 * Here, I'm casting away the constness on the buffer variable
5380 * This is OK since we don't intend to modify that buffer.
5383 bufferWalker
= (const BYTE
*)buffer
;
5384 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5387 * Calculate how many bytes we can copy to this small block.
5389 bytesToWriteInBuffer
=
5390 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5393 * Calculate the offset of the small block in the small block file.
5395 offsetInBigBlockFile
.u
.HighPart
= 0;
5396 offsetInBigBlockFile
.u
.LowPart
=
5397 blockIndex
* This
->parentStorage
->smallBlockSize
;
5399 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5402 * Write those bytes in the buffer to the small block file.
5404 res
= BlockChainStream_WriteAt(
5405 This
->parentStorage
->smallBlockRootChain
,
5406 offsetInBigBlockFile
,
5407 bytesToWriteInBuffer
,
5409 &bytesWrittenToBigBlockFile
);
5414 * Step to the next big block.
5416 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5419 bufferWalker
+= bytesWrittenToBigBlockFile
;
5420 size
-= bytesWrittenToBigBlockFile
;
5421 *bytesWritten
+= bytesWrittenToBigBlockFile
;
5422 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5425 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
5428 /******************************************************************************
5429 * SmallBlockChainStream_Shrink
5431 * Shrinks this chain in the small block depot.
5433 static BOOL
SmallBlockChainStream_Shrink(
5434 SmallBlockChainStream
* This
,
5435 ULARGE_INTEGER newSize
)
5437 ULONG blockIndex
, extraBlock
;
5441 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5443 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5446 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5449 * Go to the new end of chain
5451 while (count
< numBlocks
)
5453 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5460 * If the count is 0, we have a special case, the head of the chain was
5465 StgProperty chainProp
;
5467 StorageImpl_ReadProperty(This
->parentStorage
,
5468 This
->ownerPropertyIndex
,
5471 chainProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
5473 StorageImpl_WriteProperty(This
->parentStorage
,
5474 This
->ownerPropertyIndex
,
5478 * We start freeing the chain at the head block.
5480 extraBlock
= blockIndex
;
5484 /* Get the next block before marking the new end */
5485 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5489 /* Mark the new end of chain */
5490 SmallBlockChainStream_SetNextBlockInChain(
5493 BLOCK_END_OF_CHAIN
);
5497 * Mark the extra blocks as free
5499 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
5501 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
5504 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
5505 extraBlock
= blockIndex
;
5511 /******************************************************************************
5512 * SmallBlockChainStream_Enlarge
5514 * Grows this chain in the small block depot.
5516 static BOOL
SmallBlockChainStream_Enlarge(
5517 SmallBlockChainStream
* This
,
5518 ULARGE_INTEGER newSize
)
5520 ULONG blockIndex
, currentBlock
;
5522 ULONG oldNumBlocks
= 0;
5524 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5529 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5532 StgProperty chainProp
;
5534 StorageImpl_ReadProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
5537 chainProp
.startingBlock
= SmallBlockChainStream_GetNextFreeBlock(This
);
5539 StorageImpl_WriteProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
5542 blockIndex
= chainProp
.startingBlock
;
5543 SmallBlockChainStream_SetNextBlockInChain(
5546 BLOCK_END_OF_CHAIN
);
5549 currentBlock
= blockIndex
;
5552 * Figure out how many blocks are needed to contain this stream
5554 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5556 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5560 * Go to the current end of chain
5562 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5565 currentBlock
= blockIndex
;
5566 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
5571 * Add new blocks to the chain
5573 while (oldNumBlocks
< newNumBlocks
)
5575 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
5576 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
5578 SmallBlockChainStream_SetNextBlockInChain(
5581 BLOCK_END_OF_CHAIN
);
5583 currentBlock
= blockIndex
;
5590 /******************************************************************************
5591 * SmallBlockChainStream_SetSize
5593 * Sets the size of this stream.
5594 * The file will grow if we grow the chain.
5596 * TODO: Free the actual blocks in the file when we shrink the chain.
5597 * Currently, the blocks are still in the file. So the file size
5598 * doesn't shrink even if we shrink streams.
5600 BOOL
SmallBlockChainStream_SetSize(
5601 SmallBlockChainStream
* This
,
5602 ULARGE_INTEGER newSize
)
5604 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
5606 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
5609 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
5611 SmallBlockChainStream_Shrink(This
, newSize
);
5615 SmallBlockChainStream_Enlarge(This
, newSize
);
5621 /******************************************************************************
5622 * SmallBlockChainStream_GetSize
5624 * Returns the size of this chain.
5626 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
5628 StgProperty chainProperty
;
5630 StorageImpl_ReadProperty(
5631 This
->parentStorage
,
5632 This
->ownerPropertyIndex
,
5635 return chainProperty
.size
;
5638 /******************************************************************************
5639 * StgCreateDocfile [OLE32.@]
5640 * Creates a new compound file storage object
5643 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5644 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5645 * reserved [ ?] unused?, usually 0
5646 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5649 * S_OK if the file was successfully created
5650 * some STG_E_ value if error
5652 * if pwcsName is NULL, create file with new unique name
5653 * the function can returns
5654 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5657 HRESULT WINAPI
StgCreateDocfile(
5661 IStorage
**ppstgOpen
)
5663 StorageImpl
* newStorage
= 0;
5664 HANDLE hFile
= INVALID_HANDLE_VALUE
;
5665 HRESULT hr
= STG_E_INVALIDFLAG
;
5669 DWORD fileAttributes
;
5670 WCHAR tempFileName
[MAX_PATH
];
5672 TRACE("(%s, %x, %d, %p)\n",
5673 debugstr_w(pwcsName
), grfMode
,
5674 reserved
, ppstgOpen
);
5677 * Validate the parameters
5680 return STG_E_INVALIDPOINTER
;
5682 return STG_E_INVALIDPARAMETER
;
5684 /* if no share mode given then DENY_NONE is the default */
5685 if (STGM_SHARE_MODE(grfMode
) == 0)
5686 grfMode
|= STGM_SHARE_DENY_NONE
;
5689 * Validate the STGM flags
5691 if ( FAILED( validateSTGM(grfMode
) ))
5694 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5695 switch(STGM_ACCESS_MODE(grfMode
))
5698 case STGM_READWRITE
:
5704 /* in direct mode, can only use SHARE_EXCLUSIVE */
5705 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
5708 /* but in transacted mode, any share mode is valid */
5711 * Generate a unique name.
5715 WCHAR tempPath
[MAX_PATH
];
5716 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
5718 memset(tempPath
, 0, sizeof(tempPath
));
5719 memset(tempFileName
, 0, sizeof(tempFileName
));
5721 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
5724 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
5725 pwcsName
= tempFileName
;
5728 hr
= STG_E_INSUFFICIENTMEMORY
;
5732 creationMode
= TRUNCATE_EXISTING
;
5736 creationMode
= GetCreationModeFromSTGM(grfMode
);
5740 * Interpret the STGM value grfMode
5742 shareMode
= GetShareModeFromSTGM(grfMode
);
5743 accessMode
= GetAccessModeFromSTGM(grfMode
);
5745 if (grfMode
& STGM_DELETEONRELEASE
)
5746 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
5748 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
5750 if (grfMode
& STGM_TRANSACTED
)
5751 FIXME("Transacted mode not implemented.\n");
5754 * Initialize the "out" parameter.
5758 hFile
= CreateFileW(pwcsName
,
5766 if (hFile
== INVALID_HANDLE_VALUE
)
5768 if(GetLastError() == ERROR_FILE_EXISTS
)
5769 hr
= STG_E_FILEALREADYEXISTS
;
5776 * Allocate and initialize the new IStorage32object.
5778 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5780 if (newStorage
== 0)
5782 hr
= STG_E_INSUFFICIENTMEMORY
;
5786 hr
= StorageImpl_Construct(
5797 HeapFree(GetProcessHeap(), 0, newStorage
);
5802 * Get an "out" pointer for the caller.
5804 hr
= StorageBaseImpl_QueryInterface(
5805 (IStorage
*)newStorage
,
5806 (REFIID
)&IID_IStorage
,
5809 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
5814 /******************************************************************************
5815 * StgCreateStorageEx [OLE32.@]
5817 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
5819 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
5820 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
5822 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
5824 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5825 return STG_E_INVALIDPARAMETER
;
5828 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
5830 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5831 return STG_E_INVALIDPARAMETER
;
5834 if (stgfmt
== STGFMT_FILE
)
5836 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5837 return STG_E_INVALIDPARAMETER
;
5840 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
5842 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5843 return StgCreateDocfile(pwcsName
, grfMode
, 0, (IStorage
**)ppObjectOpen
);
5846 ERR("Invalid stgfmt argument\n");
5847 return STG_E_INVALIDPARAMETER
;
5850 /******************************************************************************
5851 * StgCreatePropSetStg [OLE32.@]
5853 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
5854 IPropertySetStorage
**ppPropSetStg
)
5858 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, ppPropSetStg
);
5860 hr
= STG_E_INVALIDPARAMETER
;
5862 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
5863 (void**)ppPropSetStg
);
5867 /******************************************************************************
5868 * StgOpenStorageEx [OLE32.@]
5870 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
5872 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
5873 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
5875 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
5877 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5878 return STG_E_INVALIDPARAMETER
;
5884 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5885 return STG_E_INVALIDPARAMETER
;
5887 case STGFMT_STORAGE
:
5890 case STGFMT_DOCFILE
:
5891 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
5893 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5894 return STG_E_INVALIDPARAMETER
;
5896 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5900 WARN("STGFMT_ANY assuming storage\n");
5904 return STG_E_INVALIDPARAMETER
;
5907 return StgOpenStorage(pwcsName
, NULL
, grfMode
, (SNB
)NULL
, 0, (IStorage
**)ppObjectOpen
);
5911 /******************************************************************************
5912 * StgOpenStorage [OLE32.@]
5914 HRESULT WINAPI
StgOpenStorage(
5915 const OLECHAR
*pwcsName
,
5916 IStorage
*pstgPriority
,
5920 IStorage
**ppstgOpen
)
5922 StorageImpl
* newStorage
= 0;
5927 WCHAR fullname
[MAX_PATH
];
5929 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5930 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
5931 snbExclude
, reserved
, ppstgOpen
);
5934 * Perform sanity checks
5938 hr
= STG_E_INVALIDNAME
;
5944 hr
= STG_E_INVALIDPOINTER
;
5950 hr
= STG_E_INVALIDPARAMETER
;
5954 if (grfMode
& STGM_PRIORITY
)
5956 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
5957 return STG_E_INVALIDFLAG
;
5958 if (grfMode
& STGM_DELETEONRELEASE
)
5959 return STG_E_INVALIDFUNCTION
;
5960 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
5961 return STG_E_INVALIDFLAG
;
5962 grfMode
&= ~0xf0; /* remove the existing sharing mode */
5963 grfMode
|= STGM_SHARE_DENY_NONE
;
5965 /* STGM_PRIORITY stops other IStorage objects on the same file from
5966 * committing until the STGM_PRIORITY IStorage is closed. it also
5967 * stops non-transacted mode StgOpenStorage calls with write access from
5968 * succeeding. obviously, both of these cannot be achieved through just
5969 * file share flags */
5970 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5974 * Validate the sharing mode
5976 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
5977 switch(STGM_SHARE_MODE(grfMode
))
5979 case STGM_SHARE_EXCLUSIVE
:
5980 case STGM_SHARE_DENY_WRITE
:
5983 hr
= STG_E_INVALIDFLAG
;
5988 * Validate the STGM flags
5990 if ( FAILED( validateSTGM(grfMode
) ) ||
5991 (grfMode
&STGM_CREATE
))
5993 hr
= STG_E_INVALIDFLAG
;
5997 /* shared reading requires transacted mode */
5998 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
5999 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
6000 !(grfMode
&STGM_TRANSACTED
) )
6002 hr
= STG_E_INVALIDFLAG
;
6007 * Interpret the STGM value grfMode
6009 shareMode
= GetShareModeFromSTGM(grfMode
);
6010 accessMode
= GetAccessModeFromSTGM(grfMode
);
6013 * Initialize the "out" parameter.
6017 hFile
= CreateFileW( pwcsName
,
6022 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
6025 if (hFile
==INVALID_HANDLE_VALUE
)
6027 DWORD last_error
= GetLastError();
6033 case ERROR_FILE_NOT_FOUND
:
6034 hr
= STG_E_FILENOTFOUND
;
6037 case ERROR_PATH_NOT_FOUND
:
6038 hr
= STG_E_PATHNOTFOUND
;
6041 case ERROR_ACCESS_DENIED
:
6042 case ERROR_WRITE_PROTECT
:
6043 hr
= STG_E_ACCESSDENIED
;
6046 case ERROR_SHARING_VIOLATION
:
6047 hr
= STG_E_SHAREVIOLATION
;
6058 * Refuse to open the file if it's too small to be a structured storage file
6059 * FIXME: verify the file when reading instead of here
6061 if (GetFileSize(hFile
, NULL
) < 0x100)
6064 hr
= STG_E_FILEALREADYEXISTS
;
6069 * Allocate and initialize the new IStorage32object.
6071 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
6073 if (newStorage
== 0)
6075 hr
= STG_E_INSUFFICIENTMEMORY
;
6079 /* Initialize the storage */
6080 hr
= StorageImpl_Construct(
6091 HeapFree(GetProcessHeap(), 0, newStorage
);
6093 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6095 if(hr
== STG_E_INVALIDHEADER
)
6096 hr
= STG_E_FILEALREADYEXISTS
;
6100 /* prepare the file name string given in lieu of the root property name */
6101 GetFullPathNameW(pwcsName
, MAX_PATH
, fullname
, NULL
);
6102 memcpy(newStorage
->filename
, fullname
, PROPERTY_NAME_BUFFER_LEN
);
6103 newStorage
->filename
[PROPERTY_NAME_BUFFER_LEN
-1] = '\0';
6106 * Get an "out" pointer for the caller.
6108 hr
= StorageBaseImpl_QueryInterface(
6109 (IStorage
*)newStorage
,
6110 (REFIID
)&IID_IStorage
,
6114 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
6118 /******************************************************************************
6119 * StgCreateDocfileOnILockBytes [OLE32.@]
6121 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
6125 IStorage
** ppstgOpen
)
6127 StorageImpl
* newStorage
= 0;
6131 * Validate the parameters
6133 if ((ppstgOpen
== 0) || (plkbyt
== 0))
6134 return STG_E_INVALIDPOINTER
;
6137 * Allocate and initialize the new IStorage object.
6139 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
6141 if (newStorage
== 0)
6142 return STG_E_INSUFFICIENTMEMORY
;
6144 hr
= StorageImpl_Construct(
6155 HeapFree(GetProcessHeap(), 0, newStorage
);
6160 * Get an "out" pointer for the caller.
6162 hr
= StorageBaseImpl_QueryInterface(
6163 (IStorage
*)newStorage
,
6164 (REFIID
)&IID_IStorage
,
6170 /******************************************************************************
6171 * StgOpenStorageOnILockBytes [OLE32.@]
6173 HRESULT WINAPI
StgOpenStorageOnILockBytes(
6175 IStorage
*pstgPriority
,
6179 IStorage
**ppstgOpen
)
6181 StorageImpl
* newStorage
= 0;
6185 * Perform a sanity check
6187 if ((plkbyt
== 0) || (ppstgOpen
== 0))
6188 return STG_E_INVALIDPOINTER
;
6191 * Validate the STGM flags
6193 if ( FAILED( validateSTGM(grfMode
) ))
6194 return STG_E_INVALIDFLAG
;
6197 * Initialize the "out" parameter.
6202 * Allocate and initialize the new IStorage object.
6204 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
6206 if (newStorage
== 0)
6207 return STG_E_INSUFFICIENTMEMORY
;
6209 hr
= StorageImpl_Construct(
6220 HeapFree(GetProcessHeap(), 0, newStorage
);
6225 * Get an "out" pointer for the caller.
6227 hr
= StorageBaseImpl_QueryInterface(
6228 (IStorage
*)newStorage
,
6229 (REFIID
)&IID_IStorage
,
6235 /******************************************************************************
6236 * StgSetTimes [ole32.@]
6237 * StgSetTimes [OLE32.@]
6241 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
6242 FILETIME
const *patime
, FILETIME
const *pmtime
)
6244 IStorage
*stg
= NULL
;
6247 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
6249 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
6253 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
6254 IStorage_Release(stg
);
6260 /******************************************************************************
6261 * StgIsStorageILockBytes [OLE32.@]
6263 * Determines if the ILockBytes contains a storage object.
6265 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
6268 ULARGE_INTEGER offset
;
6270 offset
.u
.HighPart
= 0;
6271 offset
.u
.LowPart
= 0;
6273 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
6275 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
6281 /******************************************************************************
6282 * WriteClassStg [OLE32.@]
6284 * This method will store the specified CLSID in the specified storage object
6286 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
6291 return E_INVALIDARG
;
6293 hRes
= IStorage_SetClass(pStg
, rclsid
);
6298 /***********************************************************************
6299 * ReadClassStg (OLE32.@)
6301 * This method reads the CLSID previously written to a storage object with
6302 * the WriteClassStg.
6305 * pstg [I] IStorage pointer
6306 * pclsid [O] Pointer to where the CLSID is written
6310 * Failure: HRESULT code.
6312 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
6317 TRACE("(%p, %p)\n", pstg
, pclsid
);
6319 if(!pstg
|| !pclsid
)
6320 return E_INVALIDARG
;
6323 * read a STATSTG structure (contains the clsid) from the storage
6325 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_DEFAULT
);
6328 *pclsid
=pstatstg
.clsid
;
6333 /***********************************************************************
6334 * OleLoadFromStream (OLE32.@)
6336 * This function loads an object from stream
6338 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
6342 LPPERSISTSTREAM xstm
;
6344 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
6346 res
=ReadClassStm(pStm
,&clsid
);
6347 if (!SUCCEEDED(res
))
6349 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
6350 if (!SUCCEEDED(res
))
6352 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
6353 if (!SUCCEEDED(res
)) {
6354 IUnknown_Release((IUnknown
*)*ppvObj
);
6357 res
=IPersistStream_Load(xstm
,pStm
);
6358 IPersistStream_Release(xstm
);
6359 /* FIXME: all refcounts ok at this point? I think they should be:
6362 * xstm : 0 (released)
6367 /***********************************************************************
6368 * OleSaveToStream (OLE32.@)
6370 * This function saves an object with the IPersistStream interface on it
6371 * to the specified stream.
6373 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
6379 TRACE("(%p,%p)\n",pPStm
,pStm
);
6381 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
6383 if (SUCCEEDED(res
)){
6385 res
=WriteClassStm(pStm
,&clsid
);
6389 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
6392 TRACE("Finished Save\n");
6396 /****************************************************************************
6397 * This method validate a STGM parameter that can contain the values below
6399 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6400 * The stgm values contained in 0xffff0000 are bitmasks.
6402 * STGM_DIRECT 0x00000000
6403 * STGM_TRANSACTED 0x00010000
6404 * STGM_SIMPLE 0x08000000
6406 * STGM_READ 0x00000000
6407 * STGM_WRITE 0x00000001
6408 * STGM_READWRITE 0x00000002
6410 * STGM_SHARE_DENY_NONE 0x00000040
6411 * STGM_SHARE_DENY_READ 0x00000030
6412 * STGM_SHARE_DENY_WRITE 0x00000020
6413 * STGM_SHARE_EXCLUSIVE 0x00000010
6415 * STGM_PRIORITY 0x00040000
6416 * STGM_DELETEONRELEASE 0x04000000
6418 * STGM_CREATE 0x00001000
6419 * STGM_CONVERT 0x00020000
6420 * STGM_FAILIFTHERE 0x00000000
6422 * STGM_NOSCRATCH 0x00100000
6423 * STGM_NOSNAPSHOT 0x00200000
6425 static HRESULT
validateSTGM(DWORD stgm
)
6427 DWORD access
= STGM_ACCESS_MODE(stgm
);
6428 DWORD share
= STGM_SHARE_MODE(stgm
);
6429 DWORD create
= STGM_CREATE_MODE(stgm
);
6431 if (stgm
&~STGM_KNOWN_FLAGS
)
6433 ERR("unknown flags %08x\n", stgm
);
6441 case STGM_READWRITE
:
6449 case STGM_SHARE_DENY_NONE
:
6450 case STGM_SHARE_DENY_READ
:
6451 case STGM_SHARE_DENY_WRITE
:
6452 case STGM_SHARE_EXCLUSIVE
:
6461 case STGM_FAILIFTHERE
:
6468 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6470 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
6474 * STGM_CREATE | STGM_CONVERT
6475 * if both are false, STGM_FAILIFTHERE is set to TRUE
6477 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
6481 * STGM_NOSCRATCH requires STGM_TRANSACTED
6483 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
6487 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6488 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6490 if ( (stgm
& STGM_NOSNAPSHOT
) &&
6491 (!(stgm
& STGM_TRANSACTED
) ||
6492 share
== STGM_SHARE_EXCLUSIVE
||
6493 share
== STGM_SHARE_DENY_WRITE
) )
6499 /****************************************************************************
6500 * GetShareModeFromSTGM
6502 * This method will return a share mode flag from a STGM value.
6503 * The STGM value is assumed valid.
6505 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
6507 switch (STGM_SHARE_MODE(stgm
))
6509 case STGM_SHARE_DENY_NONE
:
6510 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6511 case STGM_SHARE_DENY_READ
:
6512 return FILE_SHARE_WRITE
;
6513 case STGM_SHARE_DENY_WRITE
:
6514 return FILE_SHARE_READ
;
6515 case STGM_SHARE_EXCLUSIVE
:
6518 ERR("Invalid share mode!\n");
6523 /****************************************************************************
6524 * GetAccessModeFromSTGM
6526 * This method will return an access mode flag from a STGM value.
6527 * The STGM value is assumed valid.
6529 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
6531 switch (STGM_ACCESS_MODE(stgm
))
6534 return GENERIC_READ
;
6536 case STGM_READWRITE
:
6537 return GENERIC_READ
| GENERIC_WRITE
;
6539 ERR("Invalid access mode!\n");
6544 /****************************************************************************
6545 * GetCreationModeFromSTGM
6547 * This method will return a creation mode flag from a STGM value.
6548 * The STGM value is assumed valid.
6550 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
6552 switch(STGM_CREATE_MODE(stgm
))
6555 return CREATE_ALWAYS
;
6557 FIXME("STGM_CONVERT not implemented!\n");
6559 case STGM_FAILIFTHERE
:
6562 ERR("Invalid create mode!\n");
6568 /*************************************************************************
6569 * OLECONVERT_LoadOLE10 [Internal]
6571 * Loads the OLE10 STREAM to memory
6574 * pOleStream [I] The OLESTREAM
6575 * pData [I] Data Structure for the OLESTREAM Data
6579 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6580 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6583 * This function is used by OleConvertOLESTREAMToIStorage only.
6585 * Memory allocated for pData must be freed by the caller
6587 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
6590 HRESULT hRes
= S_OK
;
6594 pData
->pData
= NULL
;
6595 pData
->pstrOleObjFileName
= (CHAR
*) NULL
;
6597 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
6600 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6601 if(dwSize
!= sizeof(pData
->dwOleID
))
6603 hRes
= CONVERT10_E_OLESTREAM_GET
;
6605 else if(pData
->dwOleID
!= OLESTREAM_ID
)
6607 hRes
= CONVERT10_E_OLESTREAM_FMT
;
6618 /* Get the TypeID...more info needed for this field */
6619 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6620 if(dwSize
!= sizeof(pData
->dwTypeID
))
6622 hRes
= CONVERT10_E_OLESTREAM_GET
;
6627 if(pData
->dwTypeID
!= 0)
6629 /* Get the length of the OleTypeName */
6630 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6631 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6633 hRes
= CONVERT10_E_OLESTREAM_GET
;
6638 if(pData
->dwOleTypeNameLength
> 0)
6640 /* Get the OleTypeName */
6641 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6642 if(dwSize
!= pData
->dwOleTypeNameLength
)
6644 hRes
= CONVERT10_E_OLESTREAM_GET
;
6650 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
6651 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
6653 hRes
= CONVERT10_E_OLESTREAM_GET
;
6657 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
6658 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
6659 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
6660 if(pData
->pstrOleObjFileName
)
6662 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)(pData
->pstrOleObjFileName
),pData
->dwOleObjFileNameLength
);
6663 if(dwSize
!= pData
->dwOleObjFileNameLength
)
6665 hRes
= CONVERT10_E_OLESTREAM_GET
;
6669 hRes
= CONVERT10_E_OLESTREAM_GET
;
6674 /* Get the Width of the Metafile */
6675 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6676 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6678 hRes
= CONVERT10_E_OLESTREAM_GET
;
6682 /* Get the Height of the Metafile */
6683 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6684 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6686 hRes
= CONVERT10_E_OLESTREAM_GET
;
6692 /* Get the Length of the Data */
6693 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6694 if(dwSize
!= sizeof(pData
->dwDataLength
))
6696 hRes
= CONVERT10_E_OLESTREAM_GET
;
6700 if(hRes
== S_OK
) /* I don't know what is this 8 byts information is we have to figure out */
6702 if(!bStrem1
) /* if it is a second OLE stream data */
6704 pData
->dwDataLength
-= 8;
6705 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)(pData
->strUnknown
), sizeof(pData
->strUnknown
));
6706 if(dwSize
!= sizeof(pData
->strUnknown
))
6708 hRes
= CONVERT10_E_OLESTREAM_GET
;
6714 if(pData
->dwDataLength
> 0)
6716 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
6718 /* Get Data (ex. IStorage, Metafile, or BMP) */
6721 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
6722 if(dwSize
!= pData
->dwDataLength
)
6724 hRes
= CONVERT10_E_OLESTREAM_GET
;
6729 hRes
= CONVERT10_E_OLESTREAM_GET
;
6738 /*************************************************************************
6739 * OLECONVERT_SaveOLE10 [Internal]
6741 * Saves the OLE10 STREAM From memory
6744 * pData [I] Data Structure for the OLESTREAM Data
6745 * pOleStream [I] The OLESTREAM to save
6749 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6752 * This function is used by OleConvertIStorageToOLESTREAM only.
6755 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
6758 HRESULT hRes
= S_OK
;
6762 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6763 if(dwSize
!= sizeof(pData
->dwOleID
))
6765 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6770 /* Set the TypeID */
6771 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6772 if(dwSize
!= sizeof(pData
->dwTypeID
))
6774 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6778 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
6780 /* Set the Length of the OleTypeName */
6781 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6782 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6784 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6789 if(pData
->dwOleTypeNameLength
> 0)
6791 /* Set the OleTypeName */
6792 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6793 if(dwSize
!= pData
->dwOleTypeNameLength
)
6795 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6802 /* Set the width of the Metafile */
6803 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6804 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6806 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6812 /* Set the height of the Metafile */
6813 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6814 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6816 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6822 /* Set the length of the Data */
6823 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6824 if(dwSize
!= sizeof(pData
->dwDataLength
))
6826 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6832 if(pData
->dwDataLength
> 0)
6834 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6835 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
6836 if(dwSize
!= pData
->dwDataLength
)
6838 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6846 /*************************************************************************
6847 * OLECONVERT_GetOLE20FromOLE10[Internal]
6849 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6850 * opens it, and copies the content to the dest IStorage for
6851 * OleConvertOLESTREAMToIStorage
6855 * pDestStorage [I] The IStorage to copy the data to
6856 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6857 * nBufferLength [I] The size of the buffer
6866 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
6870 IStorage
*pTempStorage
;
6871 DWORD dwNumOfBytesWritten
;
6872 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6873 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6875 /* Create a temp File */
6876 GetTempPathW(MAX_PATH
, wstrTempDir
);
6877 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6878 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
6880 if(hFile
!= INVALID_HANDLE_VALUE
)
6882 /* Write IStorage Data to File */
6883 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
6886 /* Open and copy temp storage to the Dest Storage */
6887 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
6890 hRes
= StorageImpl_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
6891 StorageBaseImpl_Release(pTempStorage
);
6893 DeleteFileW(wstrTempFile
);
6898 /*************************************************************************
6899 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6901 * Saves the OLE10 STREAM From memory
6904 * pStorage [I] The Src IStorage to copy
6905 * pData [I] The Dest Memory to write to.
6908 * The size in bytes allocated for pData
6911 * Memory allocated for pData must be freed by the caller
6913 * Used by OleConvertIStorageToOLESTREAM only.
6916 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
6920 DWORD nDataLength
= 0;
6921 IStorage
*pTempStorage
;
6922 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6923 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6927 /* Create temp Storage */
6928 GetTempPathW(MAX_PATH
, wstrTempDir
);
6929 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6930 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
6934 /* Copy Src Storage to the Temp Storage */
6935 StorageImpl_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
6936 StorageBaseImpl_Release(pTempStorage
);
6938 /* Open Temp Storage as a file and copy to memory */
6939 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
6940 if(hFile
!= INVALID_HANDLE_VALUE
)
6942 nDataLength
= GetFileSize(hFile
, NULL
);
6943 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
6944 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
6947 DeleteFileW(wstrTempFile
);
6952 /*************************************************************************
6953 * OLECONVERT_CreateOleStream [Internal]
6955 * Creates the "\001OLE" stream in the IStorage if necessary.
6958 * pStorage [I] Dest storage to create the stream in
6964 * This function is used by OleConvertOLESTREAMToIStorage only.
6966 * This stream is still unknown, MS Word seems to have extra data
6967 * but since the data is stored in the OLESTREAM there should be
6968 * no need to recreate the stream. If the stream is manually
6969 * deleted it will create it with this default data.
6972 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
6976 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
6977 BYTE pOleStreamHeader
[] =
6979 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6980 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6981 0x00, 0x00, 0x00, 0x00
6984 /* Create stream if not present */
6985 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
6986 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
6990 /* Write default Data */
6991 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
6992 IStream_Release(pStream
);
6996 /* write a string to a stream, preceded by its length */
6997 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
7004 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
7005 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
7010 str
= CoTaskMemAlloc( len
);
7011 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
7012 r
= IStream_Write( stm
, str
, len
, NULL
);
7013 CoTaskMemFree( str
);
7017 /* read a string preceded by its length from a stream */
7018 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
7021 DWORD len
, count
= 0;
7025 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
7028 if( count
!= sizeof(len
) )
7029 return E_OUTOFMEMORY
;
7031 TRACE("%d bytes\n",len
);
7033 str
= CoTaskMemAlloc( len
);
7035 return E_OUTOFMEMORY
;
7037 r
= IStream_Read( stm
, str
, len
, &count
);
7042 CoTaskMemFree( str
);
7043 return E_OUTOFMEMORY
;
7046 TRACE("Read string %s\n",debugstr_an(str
,len
));
7048 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
7049 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
7051 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
7052 CoTaskMemFree( str
);
7060 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
7061 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
7065 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7067 static const BYTE unknown1
[12] =
7068 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7069 0xFF, 0xFF, 0xFF, 0xFF};
7070 static const BYTE unknown2
[16] =
7071 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7072 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7074 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
7075 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
7076 debugstr_w(szProgIDName
));
7078 /* Create a CompObj stream if it doesn't exist */
7079 r
= IStorage_CreateStream(pstg
, szwStreamName
,
7080 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
7084 /* Write CompObj Structure to stream */
7085 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
7087 if( SUCCEEDED( r
) )
7088 r
= WriteClassStm( pstm
, clsid
);
7090 if( SUCCEEDED( r
) )
7091 r
= STREAM_WriteString( pstm
, lpszUserType
);
7092 if( SUCCEEDED( r
) )
7093 r
= STREAM_WriteString( pstm
, szClipName
);
7094 if( SUCCEEDED( r
) )
7095 r
= STREAM_WriteString( pstm
, szProgIDName
);
7096 if( SUCCEEDED( r
) )
7097 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
7099 IStream_Release( pstm
);
7104 /***********************************************************************
7105 * WriteFmtUserTypeStg (OLE32.@)
7107 HRESULT WINAPI
WriteFmtUserTypeStg(
7108 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
7111 WCHAR szwClipName
[0x40];
7112 CLSID clsid
= CLSID_NULL
;
7113 LPWSTR wstrProgID
= NULL
;
7116 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
7118 /* get the clipboard format name */
7119 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
) );
7122 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
7124 /* FIXME: There's room to save a CLSID and its ProgID, but
7125 the CLSID is not looked up in the registry and in all the
7126 tests I wrote it was CLSID_NULL. Where does it come from?
7129 /* get the real program ID. This may fail, but that's fine */
7130 ProgIDFromCLSID(&clsid
, &wstrProgID
);
7132 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
7134 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
7135 lpszUserType
, szwClipName
, wstrProgID
);
7137 CoTaskMemFree(wstrProgID
);
7143 /******************************************************************************
7144 * ReadFmtUserTypeStg [OLE32.@]
7146 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
7150 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
7151 unsigned char unknown1
[12];
7152 unsigned char unknown2
[16];
7154 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
7157 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
7159 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
7160 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
7163 WARN("Failed to open stream r = %08x\n", r
);
7167 /* read the various parts of the structure */
7168 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
7169 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
7171 r
= ReadClassStm( stm
, &clsid
);
7175 r
= STREAM_ReadString( stm
, &szCLSIDName
);
7179 r
= STREAM_ReadString( stm
, &szOleTypeName
);
7183 r
= STREAM_ReadString( stm
, &szProgIDName
);
7187 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
7188 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
7191 /* ok, success... now we just need to store what we found */
7193 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
7194 CoTaskMemFree( szOleTypeName
);
7196 if( lplpszUserType
)
7197 *lplpszUserType
= szCLSIDName
;
7198 CoTaskMemFree( szProgIDName
);
7201 IStream_Release( stm
);
7207 /*************************************************************************
7208 * OLECONVERT_CreateCompObjStream [Internal]
7210 * Creates a "\001CompObj" is the destination IStorage if necessary.
7213 * pStorage [I] The dest IStorage to create the CompObj Stream
7215 * strOleTypeName [I] The ProgID
7219 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7222 * This function is used by OleConvertOLESTREAMToIStorage only.
7224 * The stream data is stored in the OLESTREAM and there should be
7225 * no need to recreate the stream. If the stream is manually
7226 * deleted it will attempt to create it by querying the registry.
7230 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
7233 HRESULT hStorageRes
, hRes
= S_OK
;
7234 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
7235 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7236 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
7238 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7239 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
7241 /* Initialize the CompObj structure */
7242 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
7243 memcpy(&(IStorageCompObj
.byUnknown1
), pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
7244 memcpy(&(IStorageCompObj
.byUnknown2
), pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
7247 /* Create a CompObj stream if it doesn't exist */
7248 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7249 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7250 if(hStorageRes
== S_OK
)
7252 /* copy the OleTypeName to the compobj struct */
7253 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
7254 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
7256 /* copy the OleTypeName to the compobj struct */
7257 /* Note: in the test made, these were Identical */
7258 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
7259 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
7262 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
7263 bufferW
, OLESTREAM_MAX_STR_LEN
);
7264 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
7270 /* Get the CLSID Default Name from the Registry */
7271 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
7272 if(hErr
== ERROR_SUCCESS
)
7274 char strTemp
[OLESTREAM_MAX_STR_LEN
];
7275 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
7276 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
7277 if(hErr
== ERROR_SUCCESS
)
7279 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
7285 /* Write CompObj Structure to stream */
7286 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
7288 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
7290 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
7291 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
7293 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
7295 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
7296 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
7298 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
7300 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
7301 if(IStorageCompObj
.dwProgIDNameLength
> 0)
7303 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
7305 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
7306 IStream_Release(pStream
);
7312 /*************************************************************************
7313 * OLECONVERT_CreateOlePresStream[Internal]
7315 * Creates the "\002OlePres000" Stream with the Metafile data
7318 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7319 * dwExtentX [I] Width of the Metafile
7320 * dwExtentY [I] Height of the Metafile
7321 * pData [I] Metafile data
7322 * dwDataLength [I] Size of the Metafile data
7326 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7329 * This function is used by OleConvertOLESTREAMToIStorage only.
7332 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
7336 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7337 BYTE pOlePresStreamHeader
[] =
7339 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7340 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7341 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7342 0x00, 0x00, 0x00, 0x00
7345 BYTE pOlePresStreamHeaderEmpty
[] =
7347 0x00, 0x00, 0x00, 0x00,
7348 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7349 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7350 0x00, 0x00, 0x00, 0x00
7353 /* Create the OlePres000 Stream */
7354 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7355 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7360 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
7362 memset(&OlePres
, 0, sizeof(OlePres
));
7363 /* Do we have any metafile data to save */
7364 if(dwDataLength
> 0)
7366 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
7367 nHeaderSize
= sizeof(pOlePresStreamHeader
);
7371 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
7372 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
7374 /* Set width and height of the metafile */
7375 OlePres
.dwExtentX
= dwExtentX
;
7376 OlePres
.dwExtentY
= -dwExtentY
;
7378 /* Set Data and Length */
7379 if(dwDataLength
> sizeof(METAFILEPICT16
))
7381 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
7382 OlePres
.pData
= &(pData
[8]);
7384 /* Save OlePres000 Data to Stream */
7385 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
7386 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
7387 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
7388 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
7389 if(OlePres
.dwSize
> 0)
7391 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
7393 IStream_Release(pStream
);
7397 /*************************************************************************
7398 * OLECONVERT_CreateOle10NativeStream [Internal]
7400 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7403 * pStorage [I] Dest storage to create the stream in
7404 * pData [I] Ole10 Native Data (ex. bmp)
7405 * dwDataLength [I] Size of the Ole10 Native Data
7411 * This function is used by OleConvertOLESTREAMToIStorage only.
7413 * Might need to verify the data and return appropriate error message
7416 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
7420 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7422 /* Create the Ole10Native Stream */
7423 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7424 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7428 /* Write info to stream */
7429 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
7430 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
7431 IStream_Release(pStream
);
7436 /*************************************************************************
7437 * OLECONVERT_GetOLE10ProgID [Internal]
7439 * Finds the ProgID (or OleTypeID) from the IStorage
7442 * pStorage [I] The Src IStorage to get the ProgID
7443 * strProgID [I] the ProgID string to get
7444 * dwSize [I] the size of the string
7448 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7451 * This function is used by OleConvertIStorageToOLESTREAM only.
7455 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
7459 LARGE_INTEGER iSeekPos
;
7460 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
7461 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7463 /* Open the CompObj Stream */
7464 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7465 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7469 /*Get the OleType from the CompObj Stream */
7470 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
7471 iSeekPos
.u
.HighPart
= 0;
7473 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7474 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
7475 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
7476 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7477 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
7478 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
7479 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7481 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
7484 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
7486 IStream_Release(pStream
);
7491 LPOLESTR wstrProgID
;
7493 /* Get the OleType from the registry */
7494 REFCLSID clsid
= &(stat
.clsid
);
7495 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
7496 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
7499 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
7506 /*************************************************************************
7507 * OLECONVERT_GetOle10PresData [Internal]
7509 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7512 * pStorage [I] Src IStroage
7513 * pOleStream [I] Dest OleStream Mem Struct
7519 * This function is used by OleConvertIStorageToOLESTREAM only.
7521 * Memory allocated for pData must be freed by the caller
7525 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7530 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7532 /* Initialize Default data for OLESTREAM */
7533 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7534 pOleStreamData
[0].dwTypeID
= 2;
7535 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7536 pOleStreamData
[1].dwTypeID
= 0;
7537 pOleStreamData
[0].dwMetaFileWidth
= 0;
7538 pOleStreamData
[0].dwMetaFileHeight
= 0;
7539 pOleStreamData
[0].pData
= NULL
;
7540 pOleStreamData
[1].pData
= NULL
;
7542 /* Open Ole10Native Stream */
7543 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7544 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7548 /* Read Size and Data */
7549 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
7550 if(pOleStreamData
->dwDataLength
> 0)
7552 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
7553 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
7555 IStream_Release(pStream
);
7561 /*************************************************************************
7562 * OLECONVERT_GetOle20PresData[Internal]
7564 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7567 * pStorage [I] Src IStroage
7568 * pOleStreamData [I] Dest OleStream Mem Struct
7574 * This function is used by OleConvertIStorageToOLESTREAM only.
7576 * Memory allocated for pData must be freed by the caller
7578 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7582 OLECONVERT_ISTORAGE_OLEPRES olePress
;
7583 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7585 /* Initialize Default data for OLESTREAM */
7586 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7587 pOleStreamData
[0].dwTypeID
= 2;
7588 pOleStreamData
[0].dwMetaFileWidth
= 0;
7589 pOleStreamData
[0].dwMetaFileHeight
= 0;
7590 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
7591 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7592 pOleStreamData
[1].dwTypeID
= 0;
7593 pOleStreamData
[1].dwOleTypeNameLength
= 0;
7594 pOleStreamData
[1].strOleTypeName
[0] = 0;
7595 pOleStreamData
[1].dwMetaFileWidth
= 0;
7596 pOleStreamData
[1].dwMetaFileHeight
= 0;
7597 pOleStreamData
[1].pData
= NULL
;
7598 pOleStreamData
[1].dwDataLength
= 0;
7601 /* Open OlePress000 stream */
7602 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7603 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7606 LARGE_INTEGER iSeekPos
;
7607 METAFILEPICT16 MetaFilePict
;
7608 static const char strMetafilePictName
[] = "METAFILEPICT";
7610 /* Set the TypeID for a Metafile */
7611 pOleStreamData
[1].dwTypeID
= 5;
7613 /* Set the OleTypeName to Metafile */
7614 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
7615 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
7617 iSeekPos
.u
.HighPart
= 0;
7618 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
7620 /* Get Presentation Data */
7621 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7622 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
7623 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
7624 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
7626 /*Set width and Height */
7627 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
7628 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
7629 if(olePress
.dwSize
> 0)
7632 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
7634 /* Set MetaFilePict struct */
7635 MetaFilePict
.mm
= 8;
7636 MetaFilePict
.xExt
= olePress
.dwExtentX
;
7637 MetaFilePict
.yExt
= olePress
.dwExtentY
;
7638 MetaFilePict
.hMF
= 0;
7640 /* Get Metafile Data */
7641 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
7642 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
7643 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
7645 IStream_Release(pStream
);
7649 /*************************************************************************
7650 * OleConvertOLESTREAMToIStorage [OLE32.@]
7655 * DVTARGETDEVICE paramenter is not handled
7656 * Still unsure of some mem fields for OLE 10 Stream
7657 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7658 * and "\001OLE" streams
7661 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
7662 LPOLESTREAM pOleStream
,
7664 const DVTARGETDEVICE
* ptd
)
7668 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7670 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
7672 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7676 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7679 if(pstg
== NULL
|| pOleStream
== NULL
)
7681 hRes
= E_INVALIDARG
;
7686 /* Load the OLESTREAM to Memory */
7687 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
7692 /* Load the OLESTREAM to Memory (part 2)*/
7693 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
7699 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
7701 /* Do we have the IStorage Data in the OLESTREAM */
7702 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
7704 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7705 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
7709 /* It must be an original OLE 1.0 source */
7710 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7715 /* It must be an original OLE 1.0 source */
7716 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7719 /* Create CompObj Stream if necessary */
7720 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
7723 /*Create the Ole Stream if necessary */
7724 OLECONVERT_CreateOleStream(pstg
);
7729 /* Free allocated memory */
7730 for(i
=0; i
< 2; i
++)
7732 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7733 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
7734 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
7739 /*************************************************************************
7740 * OleConvertIStorageToOLESTREAM [OLE32.@]
7747 * Still unsure of some mem fields for OLE 10 Stream
7748 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7749 * and "\001OLE" streams.
7752 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
7754 LPOLESTREAM pOleStream
)
7757 HRESULT hRes
= S_OK
;
7759 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7760 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7762 TRACE("%p %p\n", pstg
, pOleStream
);
7764 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7766 if(pstg
== NULL
|| pOleStream
== NULL
)
7768 hRes
= E_INVALIDARG
;
7772 /* Get the ProgID */
7773 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
7774 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
7778 /* Was it originally Ole10 */
7779 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7782 IStream_Release(pStream
);
7783 /* Get Presentation Data for Ole10Native */
7784 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
7788 /* Get Presentation Data (OLE20) */
7789 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
7792 /* Save OLESTREAM */
7793 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
7796 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
7801 /* Free allocated memory */
7802 for(i
=0; i
< 2; i
++)
7804 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7810 /***********************************************************************
7811 * GetConvertStg (OLE32.@)
7813 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
7814 FIXME("unimplemented stub!\n");
7818 /******************************************************************************
7819 * StgIsStorageFile [OLE32.@]
7820 * Verify if the file contains a storage object
7826 * S_OK if file has magic bytes as a storage object
7827 * S_FALSE if file is not storage
7830 StgIsStorageFile(LPCOLESTR fn
)
7836 TRACE("%s\n", debugstr_w(fn
));
7837 hf
= CreateFileW(fn
, GENERIC_READ
,
7838 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
7839 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
7841 if (hf
== INVALID_HANDLE_VALUE
)
7842 return STG_E_FILENOTFOUND
;
7844 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
7846 WARN(" unable to read file\n");
7853 if (bytes_read
!= 8) {
7854 WARN(" too short\n");
7858 if (!memcmp(magic
,STORAGE_magic
,8)) {
7863 WARN(" -> Invalid header.\n");
7867 /***********************************************************************
7868 * WriteClassStm (OLE32.@)
7870 * Writes a CLSID to a stream.
7873 * pStm [I] Stream to write to.
7874 * rclsid [I] CLSID to write.
7878 * Failure: HRESULT code.
7880 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
7882 TRACE("(%p,%p)\n",pStm
,rclsid
);
7884 if (!pStm
|| !rclsid
)
7885 return E_INVALIDARG
;
7887 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
7890 /***********************************************************************
7891 * ReadClassStm (OLE32.@)
7893 * Reads a CLSID from a stream.
7896 * pStm [I] Stream to read from.
7897 * rclsid [O] CLSID to read.
7901 * Failure: HRESULT code.
7903 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
7908 TRACE("(%p,%p)\n",pStm
,pclsid
);
7910 if (!pStm
|| !pclsid
)
7911 return E_INVALIDARG
;
7913 /* clear the output args */
7914 memcpy(pclsid
, &CLSID_NULL
, sizeof(*pclsid
));
7916 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
7921 if (nbByte
!= sizeof(CLSID
))
7922 return STG_E_READFAULT
;