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
, 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 IStorage_DestroyElement(iface
, pwcsName
);
1021 return STG_E_FILEALREADYEXISTS
;
1023 else if (STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1025 WARN("read-only storage\n");
1026 return STG_E_ACCESSDENIED
;
1030 * memset the empty property
1032 memset(&newStreamProperty
, 0, sizeof(StgProperty
));
1034 newStreamProperty
.sizeOfNameString
=
1035 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
1037 if (newStreamProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
1038 return STG_E_INVALIDNAME
;
1040 strcpyW(newStreamProperty
.name
, pwcsName
);
1042 newStreamProperty
.propertyType
= PROPTYPE_STREAM
;
1043 newStreamProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
1044 newStreamProperty
.size
.u
.LowPart
= 0;
1045 newStreamProperty
.size
.u
.HighPart
= 0;
1047 newStreamProperty
.previousProperty
= PROPERTY_NULL
;
1048 newStreamProperty
.nextProperty
= PROPERTY_NULL
;
1049 newStreamProperty
.dirProperty
= PROPERTY_NULL
;
1051 /* call CoFileTime to get the current time
1052 newStreamProperty.timeStampS1
1053 newStreamProperty.timeStampD1
1054 newStreamProperty.timeStampS2
1055 newStreamProperty.timeStampD2
1058 /* newStreamProperty.propertyUniqueID */
1061 * Get a free property or create a new one
1063 newPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
1066 * Save the new property into the new property spot
1068 StorageImpl_WriteProperty(
1069 This
->ancestorStorage
,
1071 &newStreamProperty
);
1074 * Find a spot in the property chain for our newly created property.
1076 updatePropertyChain(
1082 * Open the stream to return it.
1084 newStream
= StgStreamImpl_Construct(This
, grfMode
, newPropertyIndex
);
1088 *ppstm
= (IStream
*)newStream
;
1091 * Since we are returning a pointer to the interface, we have to nail down
1094 IStream_AddRef(*ppstm
);
1098 return STG_E_INSUFFICIENTMEMORY
;
1104 /************************************************************************
1105 * Storage32BaseImpl_SetClass (IStorage)
1107 * This method will write the specified CLSID in the property of this
1110 * See Windows documentation for more details on IStorage methods.
1112 static HRESULT WINAPI
StorageBaseImpl_SetClass(
1114 REFCLSID clsid
) /* [in] */
1116 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
1117 HRESULT hRes
= E_FAIL
;
1118 StgProperty curProperty
;
1121 TRACE("(%p, %p)\n", iface
, clsid
);
1123 success
= StorageImpl_ReadProperty(This
->ancestorStorage
,
1124 This
->rootPropertySetIndex
,
1128 curProperty
.propertyUniqueID
= *clsid
;
1130 success
= StorageImpl_WriteProperty(This
->ancestorStorage
,
1131 This
->rootPropertySetIndex
,
1140 /************************************************************************
1141 ** Storage32Impl implementation
1144 /************************************************************************
1145 * Storage32Impl_CreateStorage (IStorage)
1147 * This method will create the storage object within the provided storage.
1149 * See Windows documentation for more details on IStorage methods.
1151 static HRESULT WINAPI
StorageImpl_CreateStorage(
1153 const OLECHAR
*pwcsName
, /* [string][in] */
1154 DWORD grfMode
, /* [in] */
1155 DWORD reserved1
, /* [in] */
1156 DWORD reserved2
, /* [in] */
1157 IStorage
**ppstg
) /* [out] */
1159 StorageImpl
* const This
=(StorageImpl
*)iface
;
1161 IEnumSTATSTGImpl
*propertyEnumeration
;
1162 StgProperty currentProperty
;
1163 StgProperty newProperty
;
1164 ULONG foundPropertyIndex
;
1165 ULONG newPropertyIndex
;
1168 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1169 iface
, debugstr_w(pwcsName
), grfMode
,
1170 reserved1
, reserved2
, ppstg
);
1173 * Validate parameters
1176 return STG_E_INVALIDPOINTER
;
1179 return STG_E_INVALIDNAME
;
1182 * Initialize the out parameter
1187 * Validate the STGM flags
1189 if ( FAILED( validateSTGM(grfMode
) ) ||
1190 (grfMode
& STGM_DELETEONRELEASE
) )
1192 WARN("bad grfMode: 0x%x\n", grfMode
);
1193 return STG_E_INVALIDFLAG
;
1197 * Check that we're compatible with the parent's storage mode
1199 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->base
.openFlags
) )
1201 WARN("access denied\n");
1202 return STG_E_ACCESSDENIED
;
1206 * Create a property enumeration and search the properties
1208 propertyEnumeration
= IEnumSTATSTGImpl_Construct( This
->base
.ancestorStorage
,
1209 This
->base
.rootPropertySetIndex
);
1211 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
1214 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1216 if (foundPropertyIndex
!= PROPERTY_NULL
)
1219 * An element with this name already exists
1221 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
1222 IStorage_DestroyElement(iface
, pwcsName
);
1225 WARN("file already exists\n");
1226 return STG_E_FILEALREADYEXISTS
;
1229 else if (STGM_ACCESS_MODE(This
->base
.openFlags
) == STGM_READ
)
1231 WARN("read-only storage\n");
1232 return STG_E_ACCESSDENIED
;
1236 * memset the empty property
1238 memset(&newProperty
, 0, sizeof(StgProperty
));
1240 newProperty
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1242 if (newProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
1244 FIXME("name too long\n");
1245 return STG_E_INVALIDNAME
;
1248 strcpyW(newProperty
.name
, pwcsName
);
1250 newProperty
.propertyType
= PROPTYPE_STORAGE
;
1251 newProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
1252 newProperty
.size
.u
.LowPart
= 0;
1253 newProperty
.size
.u
.HighPart
= 0;
1255 newProperty
.previousProperty
= PROPERTY_NULL
;
1256 newProperty
.nextProperty
= PROPERTY_NULL
;
1257 newProperty
.dirProperty
= PROPERTY_NULL
;
1259 /* call CoFileTime to get the current time
1260 newProperty.timeStampS1
1261 newProperty.timeStampD1
1262 newProperty.timeStampS2
1263 newProperty.timeStampD2
1266 /* newStorageProperty.propertyUniqueID */
1269 * Obtain a free property in the property chain
1271 newPropertyIndex
= getFreeProperty(This
->base
.ancestorStorage
);
1274 * Save the new property into the new property spot
1276 StorageImpl_WriteProperty(
1277 This
->base
.ancestorStorage
,
1282 * Find a spot in the property chain for our newly created property.
1284 updatePropertyChain(
1290 * Open it to get a pointer to return.
1292 hr
= IStorage_OpenStorage(
1294 (const OLECHAR
*)pwcsName
,
1301 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1311 /***************************************************************************
1315 * Get a free property or create a new one.
1317 static ULONG
getFreeProperty(
1318 StorageImpl
*storage
)
1320 ULONG currentPropertyIndex
= 0;
1321 ULONG newPropertyIndex
= PROPERTY_NULL
;
1322 BOOL readSuccessful
= TRUE
;
1323 StgProperty currentProperty
;
1328 * Start by reading the root property
1330 readSuccessful
= StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1331 currentPropertyIndex
,
1335 if (currentProperty
.sizeOfNameString
== 0)
1338 * The property existis and is available, we found it.
1340 newPropertyIndex
= currentPropertyIndex
;
1346 * We exhausted the property list, we will create more space below
1348 newPropertyIndex
= currentPropertyIndex
;
1350 currentPropertyIndex
++;
1352 } while (newPropertyIndex
== PROPERTY_NULL
);
1355 * grow the property chain
1357 if (! readSuccessful
)
1359 StgProperty emptyProperty
;
1360 ULARGE_INTEGER newSize
;
1361 ULONG propertyIndex
;
1362 ULONG lastProperty
= 0;
1363 ULONG blockCount
= 0;
1366 * obtain the new count of property blocks
1368 blockCount
= BlockChainStream_GetCount(
1369 storage
->base
.ancestorStorage
->rootBlockChain
)+1;
1372 * initialize the size used by the property stream
1374 newSize
.u
.HighPart
= 0;
1375 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1378 * add a property block to the property chain
1380 BlockChainStream_SetSize(storage
->base
.ancestorStorage
->rootBlockChain
, newSize
);
1383 * memset the empty property in order to initialize the unused newly
1386 memset(&emptyProperty
, 0, sizeof(StgProperty
));
1391 lastProperty
= storage
->bigBlockSize
/ PROPSET_BLOCK_SIZE
* blockCount
;
1394 propertyIndex
= newPropertyIndex
;
1395 propertyIndex
< lastProperty
;
1398 StorageImpl_WriteProperty(
1399 storage
->base
.ancestorStorage
,
1405 return newPropertyIndex
;
1408 /****************************************************************************
1412 * Case insensitive comparaison of StgProperty.name by first considering
1415 * Returns <0 when newPrpoerty < currentProperty
1416 * >0 when newPrpoerty > currentProperty
1417 * 0 when newPrpoerty == currentProperty
1419 static LONG
propertyNameCmp(
1420 const OLECHAR
*newProperty
,
1421 const OLECHAR
*currentProperty
)
1423 LONG diff
= lstrlenW(newProperty
) - lstrlenW(currentProperty
);
1428 * We compare the string themselves only when they are of the same length
1430 diff
= lstrcmpiW( newProperty
, currentProperty
);
1436 /****************************************************************************
1440 * Properly link this new element in the property chain.
1442 static void updatePropertyChain(
1443 StorageImpl
*storage
,
1444 ULONG newPropertyIndex
,
1445 StgProperty newProperty
)
1447 StgProperty currentProperty
;
1450 * Read the root property
1452 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1453 storage
->base
.rootPropertySetIndex
,
1456 if (currentProperty
.dirProperty
!= PROPERTY_NULL
)
1459 * The root storage contains some element, therefore, start the research
1460 * for the appropriate location.
1463 ULONG current
, next
, previous
, currentPropertyId
;
1466 * Keep the StgProperty sequence number of the storage first property
1468 currentPropertyId
= currentProperty
.dirProperty
;
1473 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1474 currentProperty
.dirProperty
,
1477 previous
= currentProperty
.previousProperty
;
1478 next
= currentProperty
.nextProperty
;
1479 current
= currentPropertyId
;
1483 LONG diff
= propertyNameCmp( newProperty
.name
, currentProperty
.name
);
1487 if (previous
!= PROPERTY_NULL
)
1489 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1496 currentProperty
.previousProperty
= newPropertyIndex
;
1497 StorageImpl_WriteProperty(storage
->base
.ancestorStorage
,
1505 if (next
!= PROPERTY_NULL
)
1507 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1514 currentProperty
.nextProperty
= newPropertyIndex
;
1515 StorageImpl_WriteProperty(storage
->base
.ancestorStorage
,
1524 * Trying to insert an item with the same name in the
1525 * subtree structure.
1530 previous
= currentProperty
.previousProperty
;
1531 next
= currentProperty
.nextProperty
;
1537 * The root storage is empty, link the new property to its dir property
1539 currentProperty
.dirProperty
= newPropertyIndex
;
1540 StorageImpl_WriteProperty(storage
->base
.ancestorStorage
,
1541 storage
->base
.rootPropertySetIndex
,
1547 /*************************************************************************
1550 static HRESULT WINAPI
StorageImpl_CopyTo(
1552 DWORD ciidExclude
, /* [in] */
1553 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1554 SNB snbExclude
, /* [unique][in] */
1555 IStorage
* pstgDest
) /* [unique][in] */
1557 IEnumSTATSTG
*elements
= 0;
1558 STATSTG curElement
, strStat
;
1560 IStorage
*pstgTmp
, *pstgChild
;
1561 IStream
*pstrTmp
, *pstrChild
;
1563 if ((ciidExclude
!= 0) || (rgiidExclude
!= NULL
) || (snbExclude
!= NULL
))
1564 FIXME("Exclude option not implemented\n");
1566 TRACE("(%p, %d, %p, %p, %p)\n",
1567 iface
, ciidExclude
, rgiidExclude
,
1568 snbExclude
, pstgDest
);
1571 * Perform a sanity check
1573 if ( pstgDest
== 0 )
1574 return STG_E_INVALIDPOINTER
;
1577 * Enumerate the elements
1579 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1587 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1588 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1593 * Obtain the next element
1595 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1597 if ( hr
== S_FALSE
)
1599 hr
= S_OK
; /* done, every element has been copied */
1603 if (curElement
.type
== STGTY_STORAGE
)
1606 * open child source storage
1608 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1609 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1610 NULL
, 0, &pstgChild
);
1616 * Check if destination storage is not a child of the source
1617 * storage, which will cause an infinite loop
1619 if (pstgChild
== pstgDest
)
1621 IEnumSTATSTG_Release(elements
);
1623 return STG_E_ACCESSDENIED
;
1627 * create a new storage in destination storage
1629 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1630 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1634 * if it already exist, don't create a new one use this one
1636 if (hr
== STG_E_FILEALREADYEXISTS
)
1638 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1639 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1640 NULL
, 0, &pstgTmp
);
1648 * do the copy recursively
1650 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1651 snbExclude
, pstgTmp
);
1653 IStorage_Release( pstgTmp
);
1654 IStorage_Release( pstgChild
);
1656 else if (curElement
.type
== STGTY_STREAM
)
1659 * create a new stream in destination storage. If the stream already
1660 * exist, it will be deleted and a new one will be created.
1662 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1663 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1670 * open child stream storage
1672 hr
= IStorage_OpenStream( iface
, curElement
.pwcsName
, NULL
,
1673 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1680 * Get the size of the source stream
1682 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1685 * Set the size of the destination stream.
1687 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1692 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1695 IStream_Release( pstrTmp
);
1696 IStream_Release( pstrChild
);
1700 WARN("unknown element type: %d\n", curElement
.type
);
1703 } while (hr
== S_OK
);
1708 IEnumSTATSTG_Release(elements
);
1713 /*************************************************************************
1714 * MoveElementTo (IStorage)
1716 static HRESULT WINAPI
StorageImpl_MoveElementTo(
1718 const OLECHAR
*pwcsName
, /* [string][in] */
1719 IStorage
*pstgDest
, /* [unique][in] */
1720 const OLECHAR
*pwcsNewName
,/* [string][in] */
1721 DWORD grfFlags
) /* [in] */
1723 FIXME("(%p %s %p %s %u): stub\n", iface
,
1724 debugstr_w(pwcsName
), pstgDest
,
1725 debugstr_w(pwcsNewName
), grfFlags
);
1729 /*************************************************************************
1732 * Ensures that any changes made to a storage object open in transacted mode
1733 * are reflected in the parent storage
1736 * Wine doesn't implement transacted mode, which seems to be a basic
1737 * optimization, so we can ignore this stub for now.
1739 static HRESULT WINAPI
StorageImpl_Commit(
1741 DWORD grfCommitFlags
)/* [in] */
1743 FIXME("(%p %d): stub\n", iface
, grfCommitFlags
);
1747 /*************************************************************************
1750 * Discard all changes that have been made since the last commit operation
1752 static HRESULT WINAPI
StorageImpl_Revert(
1755 FIXME("(%p): stub\n", iface
);
1759 /*************************************************************************
1760 * DestroyElement (IStorage)
1762 * Strategy: This implementation is built this way for simplicity not for speed.
1763 * I always delete the topmost element of the enumeration and adjust
1764 * the deleted element pointer all the time. This takes longer to
1765 * do but allow to reinvoke DestroyElement whenever we encounter a
1766 * storage object. The optimisation resides in the usage of another
1767 * enumeration strategy that would give all the leaves of a storage
1768 * first. (postfix order)
1770 static HRESULT WINAPI
StorageImpl_DestroyElement(
1772 const OLECHAR
*pwcsName
)/* [string][in] */
1774 StorageImpl
* const This
=(StorageImpl
*)iface
;
1776 IEnumSTATSTGImpl
* propertyEnumeration
;
1779 StgProperty propertyToDelete
;
1780 StgProperty parentProperty
;
1781 ULONG foundPropertyIndexToDelete
;
1782 ULONG typeOfRelation
;
1783 ULONG parentPropertyId
= 0;
1786 iface
, debugstr_w(pwcsName
));
1789 * Perform a sanity check on the parameters.
1792 return STG_E_INVALIDPOINTER
;
1795 * Create a property enumeration to search the property with the given name
1797 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
1798 This
->base
.ancestorStorage
,
1799 This
->base
.rootPropertySetIndex
);
1801 foundPropertyIndexToDelete
= IEnumSTATSTGImpl_FindProperty(
1802 propertyEnumeration
,
1806 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1808 if ( foundPropertyIndexToDelete
== PROPERTY_NULL
)
1810 return STG_E_FILENOTFOUND
;
1814 * Find the parent property of the property to delete (the one that
1815 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1816 * the parent is This. Otherwise, the parent is one of its sibling...
1820 * First, read This's StgProperty..
1822 res
= StorageImpl_ReadProperty(
1823 This
->base
.ancestorStorage
,
1824 This
->base
.rootPropertySetIndex
,
1830 * Second, check to see if by any chance the actual storage (This) is not
1831 * the parent of the property to delete... We never know...
1833 if ( parentProperty
.dirProperty
== foundPropertyIndexToDelete
)
1836 * Set data as it would have been done in the else part...
1838 typeOfRelation
= PROPERTY_RELATION_DIR
;
1839 parentPropertyId
= This
->base
.rootPropertySetIndex
;
1844 * Create a property enumeration to search the parent properties, and
1845 * delete it once done.
1847 IEnumSTATSTGImpl
* propertyEnumeration2
;
1849 propertyEnumeration2
= IEnumSTATSTGImpl_Construct(
1850 This
->base
.ancestorStorage
,
1851 This
->base
.rootPropertySetIndex
);
1853 typeOfRelation
= IEnumSTATSTGImpl_FindParentProperty(
1854 propertyEnumeration2
,
1855 foundPropertyIndexToDelete
,
1859 IEnumSTATSTGImpl_Destroy(propertyEnumeration2
);
1862 if ( propertyToDelete
.propertyType
== PROPTYPE_STORAGE
)
1864 hr
= deleteStorageProperty(
1866 foundPropertyIndexToDelete
,
1869 else if ( propertyToDelete
.propertyType
== PROPTYPE_STREAM
)
1871 hr
= deleteStreamProperty(
1873 foundPropertyIndexToDelete
,
1881 * Adjust the property chain
1883 hr
= adjustPropertyChain(
1894 /************************************************************************
1895 * StorageImpl_Stat (IStorage)
1897 * This method will retrieve information about this storage object.
1899 * See Windows documentation for more details on IStorage methods.
1901 static HRESULT WINAPI
StorageImpl_Stat( IStorage
* iface
,
1902 STATSTG
* pstatstg
, /* [out] */
1903 DWORD grfStatFlag
) /* [in] */
1905 StorageImpl
* const This
= (StorageImpl
*)iface
;
1906 HRESULT result
= StorageBaseImpl_Stat( iface
, pstatstg
, grfStatFlag
);
1908 if ( !FAILED(result
) && ((grfStatFlag
& STATFLAG_NONAME
) == 0) && This
->pwcsName
)
1910 CoTaskMemFree(pstatstg
->pwcsName
);
1911 pstatstg
->pwcsName
= CoTaskMemAlloc((lstrlenW(This
->pwcsName
)+1)*sizeof(WCHAR
));
1912 strcpyW(pstatstg
->pwcsName
, This
->pwcsName
);
1918 /******************************************************************************
1919 * Internal stream list handlers
1922 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1924 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1925 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1928 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1930 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1931 list_remove(&(strm
->StrmListEntry
));
1934 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
1936 struct list
*cur
, *cur2
;
1937 StgStreamImpl
*strm
=NULL
;
1939 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
1940 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
1941 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
1942 strm
->parentStorage
= NULL
;
1948 /*********************************************************************
1952 * Perform the deletion of a complete storage node
1955 static HRESULT
deleteStorageProperty(
1956 StorageImpl
*parentStorage
,
1957 ULONG indexOfPropertyToDelete
,
1958 StgProperty propertyToDelete
)
1960 IEnumSTATSTG
*elements
= 0;
1961 IStorage
*childStorage
= 0;
1962 STATSTG currentElement
;
1964 HRESULT destroyHr
= S_OK
;
1967 * Open the storage and enumerate it
1969 hr
= StorageBaseImpl_OpenStorage(
1970 (IStorage
*)parentStorage
,
1971 propertyToDelete
.name
,
1973 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
1984 * Enumerate the elements
1986 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
1991 * Obtain the next element
1993 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
1996 destroyHr
= StorageImpl_DestroyElement(
1997 (IStorage
*)childStorage
,
1998 (OLECHAR
*)currentElement
.pwcsName
);
2000 CoTaskMemFree(currentElement
.pwcsName
);
2004 * We need to Reset the enumeration every time because we delete elements
2005 * and the enumeration could be invalid
2007 IEnumSTATSTG_Reset(elements
);
2009 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2012 * Invalidate the property by zeroing its name member.
2014 propertyToDelete
.sizeOfNameString
= 0;
2016 StorageImpl_WriteProperty(parentStorage
->base
.ancestorStorage
,
2017 indexOfPropertyToDelete
,
2020 IStorage_Release(childStorage
);
2021 IEnumSTATSTG_Release(elements
);
2026 /*********************************************************************
2030 * Perform the deletion of a stream node
2033 static HRESULT
deleteStreamProperty(
2034 StorageImpl
*parentStorage
,
2035 ULONG indexOfPropertyToDelete
,
2036 StgProperty propertyToDelete
)
2040 ULARGE_INTEGER size
;
2042 size
.u
.HighPart
= 0;
2045 hr
= StorageBaseImpl_OpenStream(
2046 (IStorage
*)parentStorage
,
2047 (OLECHAR
*)propertyToDelete
.name
,
2049 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2061 hr
= IStream_SetSize(pis
, size
);
2069 * Release the stream object.
2071 IStream_Release(pis
);
2074 * Invalidate the property by zeroing its name member.
2076 propertyToDelete
.sizeOfNameString
= 0;
2079 * Here we should re-read the property so we get the updated pointer
2080 * but since we are here to zap it, I don't do it...
2082 StorageImpl_WriteProperty(
2083 parentStorage
->base
.ancestorStorage
,
2084 indexOfPropertyToDelete
,
2090 /*********************************************************************
2094 * Finds a placeholder for the StgProperty within the Storage
2097 static HRESULT
findPlaceholder(
2098 StorageImpl
*storage
,
2099 ULONG propertyIndexToStore
,
2100 ULONG storePropertyIndex
,
2103 StgProperty storeProperty
;
2108 * Read the storage property
2110 res
= StorageImpl_ReadProperty(
2111 storage
->base
.ancestorStorage
,
2120 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
2122 if (storeProperty
.previousProperty
!= PROPERTY_NULL
)
2124 return findPlaceholder(
2126 propertyIndexToStore
,
2127 storeProperty
.previousProperty
,
2132 storeProperty
.previousProperty
= propertyIndexToStore
;
2135 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
2137 if (storeProperty
.nextProperty
!= PROPERTY_NULL
)
2139 return findPlaceholder(
2141 propertyIndexToStore
,
2142 storeProperty
.nextProperty
,
2147 storeProperty
.nextProperty
= propertyIndexToStore
;
2150 else if (typeOfRelation
== PROPERTY_RELATION_DIR
)
2152 if (storeProperty
.dirProperty
!= PROPERTY_NULL
)
2154 return findPlaceholder(
2156 propertyIndexToStore
,
2157 storeProperty
.dirProperty
,
2162 storeProperty
.dirProperty
= propertyIndexToStore
;
2166 hr
= StorageImpl_WriteProperty(
2167 storage
->base
.ancestorStorage
,
2179 /*************************************************************************
2183 * This method takes the previous and the next property link of a property
2184 * to be deleted and find them a place in the Storage.
2186 static HRESULT
adjustPropertyChain(
2188 StgProperty propertyToDelete
,
2189 StgProperty parentProperty
,
2190 ULONG parentPropertyId
,
2193 ULONG newLinkProperty
= PROPERTY_NULL
;
2194 BOOL needToFindAPlaceholder
= FALSE
;
2195 ULONG storeNode
= PROPERTY_NULL
;
2196 ULONG toStoreNode
= PROPERTY_NULL
;
2197 INT relationType
= 0;
2201 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
2203 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2206 * Set the parent previous to the property to delete previous
2208 newLinkProperty
= propertyToDelete
.previousProperty
;
2210 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2213 * We also need to find a storage for the other link, setup variables
2214 * to do this at the end...
2216 needToFindAPlaceholder
= TRUE
;
2217 storeNode
= propertyToDelete
.previousProperty
;
2218 toStoreNode
= propertyToDelete
.nextProperty
;
2219 relationType
= PROPERTY_RELATION_NEXT
;
2222 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2225 * Set the parent previous to the property to delete next
2227 newLinkProperty
= propertyToDelete
.nextProperty
;
2231 * Link it for real...
2233 parentProperty
.previousProperty
= newLinkProperty
;
2236 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
2238 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2241 * Set the parent next to the property to delete next previous
2243 newLinkProperty
= propertyToDelete
.previousProperty
;
2245 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2248 * We also need to find a storage for the other link, setup variables
2249 * to do this at the end...
2251 needToFindAPlaceholder
= TRUE
;
2252 storeNode
= propertyToDelete
.previousProperty
;
2253 toStoreNode
= propertyToDelete
.nextProperty
;
2254 relationType
= PROPERTY_RELATION_NEXT
;
2257 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2260 * Set the parent next to the property to delete next
2262 newLinkProperty
= propertyToDelete
.nextProperty
;
2266 * Link it for real...
2268 parentProperty
.nextProperty
= newLinkProperty
;
2270 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2272 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2275 * Set the parent dir to the property to delete previous
2277 newLinkProperty
= propertyToDelete
.previousProperty
;
2279 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2282 * We also need to find a storage for the other link, setup variables
2283 * to do this at the end...
2285 needToFindAPlaceholder
= TRUE
;
2286 storeNode
= propertyToDelete
.previousProperty
;
2287 toStoreNode
= propertyToDelete
.nextProperty
;
2288 relationType
= PROPERTY_RELATION_NEXT
;
2291 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2294 * Set the parent dir to the property to delete next
2296 newLinkProperty
= propertyToDelete
.nextProperty
;
2300 * Link it for real...
2302 parentProperty
.dirProperty
= newLinkProperty
;
2306 * Write back the parent property
2308 res
= StorageImpl_WriteProperty(
2309 This
->base
.ancestorStorage
,
2318 * If a placeholder is required for the other link, then, find one and
2319 * get out of here...
2321 if (needToFindAPlaceholder
)
2323 hr
= findPlaceholder(
2334 /******************************************************************************
2335 * SetElementTimes (IStorage)
2337 static HRESULT WINAPI
StorageImpl_SetElementTimes(
2339 const OLECHAR
*pwcsName
,/* [string][in] */
2340 const FILETIME
*pctime
, /* [in] */
2341 const FILETIME
*patime
, /* [in] */
2342 const FILETIME
*pmtime
) /* [in] */
2344 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2348 /******************************************************************************
2349 * SetStateBits (IStorage)
2351 static HRESULT WINAPI
StorageImpl_SetStateBits(
2353 DWORD grfStateBits
,/* [in] */
2354 DWORD grfMask
) /* [in] */
2356 StorageImpl
* const This
= (StorageImpl
*)iface
;
2357 This
->base
.stateBits
= (This
->base
.stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2362 * Virtual function table for the IStorage32Impl class.
2364 static const IStorageVtbl Storage32Impl_Vtbl
=
2366 StorageBaseImpl_QueryInterface
,
2367 StorageBaseImpl_AddRef
,
2368 StorageBaseImpl_Release
,
2369 StorageBaseImpl_CreateStream
,
2370 StorageBaseImpl_OpenStream
,
2371 StorageImpl_CreateStorage
,
2372 StorageBaseImpl_OpenStorage
,
2374 StorageImpl_MoveElementTo
,
2377 StorageBaseImpl_EnumElements
,
2378 StorageImpl_DestroyElement
,
2379 StorageBaseImpl_RenameElement
,
2380 StorageImpl_SetElementTimes
,
2381 StorageBaseImpl_SetClass
,
2382 StorageImpl_SetStateBits
,
2386 static HRESULT
StorageImpl_Construct(
2396 StgProperty currentProperty
;
2397 BOOL readSuccessful
;
2398 ULONG currentPropertyIndex
;
2400 if ( FAILED( validateSTGM(openFlags
) ))
2401 return STG_E_INVALIDFLAG
;
2403 memset(This
, 0, sizeof(StorageImpl
));
2406 * Initialize stream list
2409 list_init(&This
->base
.strmHead
);
2412 * Initialize the virtual function table.
2414 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2415 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2416 This
->base
.v_destructor
= &StorageImpl_Destroy
;
2417 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2420 * This is the top-level storage so initialize the ancestor pointer
2423 This
->base
.ancestorStorage
= This
;
2426 * Initialize the physical support of the storage.
2428 This
->hFile
= hFile
;
2431 * Store copy of file path.
2434 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2435 (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
));
2436 if (!This
->pwcsName
)
2437 return STG_E_INSUFFICIENTMEMORY
;
2438 strcpyW(This
->pwcsName
, pwcsName
);
2442 * Initialize the big block cache.
2444 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
2445 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2446 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2452 if (This
->bigBlockFile
== 0)
2457 ULARGE_INTEGER size
;
2458 BYTE bigBlockBuffer
[BIG_BLOCK_SIZE
];
2461 * Initialize all header variables:
2462 * - The big block depot consists of one block and it is at block 0
2463 * - The properties start at block 1
2464 * - There is no small block depot
2466 memset( This
->bigBlockDepotStart
,
2468 sizeof(This
->bigBlockDepotStart
));
2470 This
->bigBlockDepotCount
= 1;
2471 This
->bigBlockDepotStart
[0] = 0;
2472 This
->rootStartBlock
= 1;
2473 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2474 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
2475 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2476 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2477 This
->extBigBlockDepotCount
= 0;
2479 StorageImpl_SaveFileHeader(This
);
2482 * Add one block for the big block depot and one block for the properties
2484 size
.u
.HighPart
= 0;
2485 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2486 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2489 * Initialize the big block depot
2491 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2492 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2493 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2494 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2499 * Load the header for the file.
2501 hr
= StorageImpl_LoadFileHeader(This
);
2505 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2512 * There is no block depot cached yet.
2514 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2517 * Start searching for free blocks with block 0.
2519 This
->prevFreeBlock
= 0;
2522 * Create the block chain abstractions.
2524 if(!(This
->rootBlockChain
=
2525 BlockChainStream_Construct(This
, &This
->rootStartBlock
, PROPERTY_NULL
)))
2526 return STG_E_READFAULT
;
2528 if(!(This
->smallBlockDepotChain
=
2529 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2531 return STG_E_READFAULT
;
2534 * Write the root property (memory only)
2538 StgProperty rootProp
;
2540 * Initialize the property chain
2542 memset(&rootProp
, 0, sizeof(rootProp
));
2543 MultiByteToWideChar( CP_ACP
, 0, rootPropertyName
, -1, rootProp
.name
,
2544 sizeof(rootProp
.name
)/sizeof(WCHAR
) );
2545 rootProp
.sizeOfNameString
= (strlenW(rootProp
.name
)+1) * sizeof(WCHAR
);
2546 rootProp
.propertyType
= PROPTYPE_ROOT
;
2547 rootProp
.previousProperty
= PROPERTY_NULL
;
2548 rootProp
.nextProperty
= PROPERTY_NULL
;
2549 rootProp
.dirProperty
= PROPERTY_NULL
;
2550 rootProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
2551 rootProp
.size
.u
.HighPart
= 0;
2552 rootProp
.size
.u
.LowPart
= 0;
2554 StorageImpl_WriteProperty(This
, 0, &rootProp
);
2558 * Find the ID of the root in the property sets.
2560 currentPropertyIndex
= 0;
2564 readSuccessful
= StorageImpl_ReadProperty(
2566 currentPropertyIndex
,
2571 if ( (currentProperty
.sizeOfNameString
!= 0 ) &&
2572 (currentProperty
.propertyType
== PROPTYPE_ROOT
) )
2574 This
->base
.rootPropertySetIndex
= currentPropertyIndex
;
2578 currentPropertyIndex
++;
2580 } while (readSuccessful
&& (This
->base
.rootPropertySetIndex
== PROPERTY_NULL
) );
2582 if (!readSuccessful
)
2585 return STG_E_READFAULT
;
2589 * Create the block chain abstraction for the small block root chain.
2591 if(!(This
->smallBlockRootChain
=
2592 BlockChainStream_Construct(This
, NULL
, This
->base
.rootPropertySetIndex
)))
2593 return STG_E_READFAULT
;
2598 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2600 StorageImpl
*This
= (StorageImpl
*) iface
;
2601 TRACE("(%p)\n", This
);
2603 StorageBaseImpl_DeleteAll(&This
->base
);
2605 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2607 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2608 BlockChainStream_Destroy(This
->rootBlockChain
);
2609 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2611 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2612 HeapFree(GetProcessHeap(), 0, This
);
2615 /******************************************************************************
2616 * Storage32Impl_GetNextFreeBigBlock
2618 * Returns the index of the next free big block.
2619 * If the big block depot is filled, this method will enlarge it.
2622 static ULONG
StorageImpl_GetNextFreeBigBlock(
2625 ULONG depotBlockIndexPos
;
2626 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2628 ULONG depotBlockOffset
;
2629 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2630 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2632 ULONG freeBlock
= BLOCK_UNUSED
;
2634 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2635 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2638 * Scan the entire big block depot until we find a block marked free
2640 while (nextBlockIndex
!= BLOCK_UNUSED
)
2642 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2644 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2647 * Grow the primary depot.
2649 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2651 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2654 * Add a block depot.
2656 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2657 This
->bigBlockDepotCount
++;
2658 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2661 * Flag it as a block depot.
2663 StorageImpl_SetNextBlockInChain(This
,
2667 /* Save new header information.
2669 StorageImpl_SaveFileHeader(This
);
2674 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2676 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2679 * Grow the extended depot.
2681 ULONG extIndex
= BLOCK_UNUSED
;
2682 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2683 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2685 if (extBlockOffset
== 0)
2687 /* We need an extended block.
2689 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2690 This
->extBigBlockDepotCount
++;
2691 depotBlockIndexPos
= extIndex
+ 1;
2694 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2697 * Add a block depot and mark it in the extended block.
2699 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2700 This
->bigBlockDepotCount
++;
2701 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2703 /* Flag the block depot.
2705 StorageImpl_SetNextBlockInChain(This
,
2709 /* If necessary, flag the extended depot block.
2711 if (extIndex
!= BLOCK_UNUSED
)
2712 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2714 /* Save header information.
2716 StorageImpl_SaveFileHeader(This
);
2720 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2724 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2725 ( nextBlockIndex
!= BLOCK_UNUSED
))
2727 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2729 if (nextBlockIndex
== BLOCK_UNUSED
)
2731 freeBlock
= (depotIndex
* blocksPerDepot
) +
2732 (depotBlockOffset
/sizeof(ULONG
));
2735 depotBlockOffset
+= sizeof(ULONG
);
2740 depotBlockOffset
= 0;
2744 * make sure that the block physically exists before using it
2746 BIGBLOCKFILE_EnsureExists(This
->bigBlockFile
, freeBlock
);
2748 This
->prevFreeBlock
= freeBlock
;
2753 /******************************************************************************
2754 * Storage32Impl_AddBlockDepot
2756 * This will create a depot block, essentially it is a block initialized
2759 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2761 BYTE blockBuffer
[BIG_BLOCK_SIZE
];
2764 * Initialize blocks as free
2766 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2767 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
2770 /******************************************************************************
2771 * Storage32Impl_GetExtDepotBlock
2773 * Returns the index of the block that corresponds to the specified depot
2774 * index. This method is only for depot indexes equal or greater than
2775 * COUNT_BBDEPOTINHEADER.
2777 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2779 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2780 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2781 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2782 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2783 ULONG blockIndex
= BLOCK_UNUSED
;
2784 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2786 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2788 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2789 return BLOCK_UNUSED
;
2791 while (extBlockCount
> 0)
2793 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2797 if (extBlockIndex
!= BLOCK_UNUSED
)
2798 StorageImpl_ReadDWordFromBigBlock(This
, extBlockIndex
,
2799 extBlockOffset
* sizeof(ULONG
), &blockIndex
);
2804 /******************************************************************************
2805 * Storage32Impl_SetExtDepotBlock
2807 * Associates the specified block index to the specified depot index.
2808 * This method is only for depot indexes equal or greater than
2809 * COUNT_BBDEPOTINHEADER.
2811 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
2813 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2814 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2815 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2816 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2817 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2819 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2821 while (extBlockCount
> 0)
2823 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2827 if (extBlockIndex
!= BLOCK_UNUSED
)
2829 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
2830 extBlockOffset
* sizeof(ULONG
),
2835 /******************************************************************************
2836 * Storage32Impl_AddExtBlockDepot
2838 * Creates an extended depot block.
2840 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
2842 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
2843 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
2844 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2845 ULONG index
= BLOCK_UNUSED
;
2846 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2847 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
2848 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
2850 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
2851 blocksPerDepotBlock
;
2853 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
2856 * The first extended block.
2858 This
->extBigBlockDepotStart
= index
;
2864 * Follow the chain to the last one.
2866 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
2868 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
2872 * Add the new extended block to the chain.
2874 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
2879 * Initialize this block.
2881 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2882 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
2887 /******************************************************************************
2888 * Storage32Impl_FreeBigBlock
2890 * This method will flag the specified block as free in the big block depot.
2892 static void StorageImpl_FreeBigBlock(
2896 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
2898 if (blockIndex
< This
->prevFreeBlock
)
2899 This
->prevFreeBlock
= blockIndex
;
2902 /************************************************************************
2903 * Storage32Impl_GetNextBlockInChain
2905 * This method will retrieve the block index of the next big block in
2908 * Params: This - Pointer to the Storage object.
2909 * blockIndex - Index of the block to retrieve the chain
2911 * nextBlockIndex - receives the return value.
2913 * Returns: This method returns the index of the next block in the chain.
2914 * It will return the constants:
2915 * BLOCK_SPECIAL - If the block given was not part of a
2917 * BLOCK_END_OF_CHAIN - If the block given was the last in
2919 * BLOCK_UNUSED - If the block given was not past of a chain
2921 * BLOCK_EXTBBDEPOT - This block is part of the extended
2924 * See Windows documentation for more details on IStorage methods.
2926 static HRESULT
StorageImpl_GetNextBlockInChain(
2929 ULONG
* nextBlockIndex
)
2931 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2932 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2933 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2934 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2936 ULONG depotBlockIndexPos
;
2939 *nextBlockIndex
= BLOCK_SPECIAL
;
2941 if(depotBlockCount
>= This
->bigBlockDepotCount
)
2943 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
2944 This
->bigBlockDepotCount
);
2945 return STG_E_READFAULT
;
2949 * Cache the currently accessed depot block.
2951 if (depotBlockCount
!= This
->indexBlockDepotCached
)
2953 This
->indexBlockDepotCached
= depotBlockCount
;
2955 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2957 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2962 * We have to look in the extended depot.
2964 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2967 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2970 return STG_E_READFAULT
;
2972 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
2974 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
2975 This
->blockDepotCached
[index
] = *nextBlockIndex
;
2979 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
2984 /******************************************************************************
2985 * Storage32Impl_GetNextExtendedBlock
2987 * Given an extended block this method will return the next extended block.
2990 * The last ULONG of an extended block is the block index of the next
2991 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2995 * - The index of the next extended block
2996 * - BLOCK_UNUSED: there is no next extended block.
2997 * - Any other return values denotes failure.
2999 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3001 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3002 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3004 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3007 return nextBlockIndex
;
3010 /******************************************************************************
3011 * Storage32Impl_SetNextBlockInChain
3013 * This method will write the index of the specified block's next block
3014 * in the big block depot.
3016 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3019 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3020 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3021 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3024 static void StorageImpl_SetNextBlockInChain(
3029 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3030 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3031 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3032 ULONG depotBlockIndexPos
;
3034 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3035 assert(blockIndex
!= nextBlock
);
3037 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3039 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3044 * We have to look in the extended depot.
3046 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3049 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3052 * Update the cached block depot, if necessary.
3054 if (depotBlockCount
== This
->indexBlockDepotCached
)
3056 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3060 /******************************************************************************
3061 * Storage32Impl_LoadFileHeader
3063 * This method will read in the file header, i.e. big block index -1.
3065 static HRESULT
StorageImpl_LoadFileHeader(
3068 HRESULT hr
= STG_E_FILENOTFOUND
;
3069 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3075 * Get a pointer to the big block of data containing the header.
3077 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3080 * Extract the information from the header.
3085 * Check for the "magic number" signature and return an error if it is not
3088 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3090 return STG_E_OLDFORMAT
;
3093 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3095 return STG_E_INVALIDHEADER
;
3098 StorageUtl_ReadWord(
3100 OFFSET_BIGBLOCKSIZEBITS
,
3101 &This
->bigBlockSizeBits
);
3103 StorageUtl_ReadWord(
3105 OFFSET_SMALLBLOCKSIZEBITS
,
3106 &This
->smallBlockSizeBits
);
3108 StorageUtl_ReadDWord(
3110 OFFSET_BBDEPOTCOUNT
,
3111 &This
->bigBlockDepotCount
);
3113 StorageUtl_ReadDWord(
3115 OFFSET_ROOTSTARTBLOCK
,
3116 &This
->rootStartBlock
);
3118 StorageUtl_ReadDWord(
3120 OFFSET_SBDEPOTSTART
,
3121 &This
->smallBlockDepotStart
);
3123 StorageUtl_ReadDWord(
3125 OFFSET_EXTBBDEPOTSTART
,
3126 &This
->extBigBlockDepotStart
);
3128 StorageUtl_ReadDWord(
3130 OFFSET_EXTBBDEPOTCOUNT
,
3131 &This
->extBigBlockDepotCount
);
3133 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3135 StorageUtl_ReadDWord(
3137 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3138 &(This
->bigBlockDepotStart
[index
]));
3142 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3146 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3147 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3151 This
->bigBlockSize
= 0x000000001 >> (DWORD
)This
->bigBlockSizeBits
;
3152 This
->smallBlockSize
= 0x000000001 >> (DWORD
)This
->smallBlockSizeBits
;
3156 * Right now, the code is making some assumptions about the size of the
3157 * blocks, just make sure they are what we're expecting.
3159 if (This
->bigBlockSize
!= DEF_BIG_BLOCK_SIZE
||
3160 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
)
3162 WARN("Broken OLE storage file\n");
3163 hr
= STG_E_INVALIDHEADER
;
3172 /******************************************************************************
3173 * Storage32Impl_SaveFileHeader
3175 * This method will save to the file the header, i.e. big block -1.
3177 static void StorageImpl_SaveFileHeader(
3180 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3185 * Get a pointer to the big block of data containing the header.
3187 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3190 * If the block read failed, the file is probably new.
3195 * Initialize for all unknown fields.
3197 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
3200 * Initialize the magic number.
3202 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3205 * And a bunch of things we don't know what they mean
3207 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3208 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3209 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3210 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
3214 * Write the information to the header.
3216 StorageUtl_WriteWord(
3218 OFFSET_BIGBLOCKSIZEBITS
,
3219 This
->bigBlockSizeBits
);
3221 StorageUtl_WriteWord(
3223 OFFSET_SMALLBLOCKSIZEBITS
,
3224 This
->smallBlockSizeBits
);
3226 StorageUtl_WriteDWord(
3228 OFFSET_BBDEPOTCOUNT
,
3229 This
->bigBlockDepotCount
);
3231 StorageUtl_WriteDWord(
3233 OFFSET_ROOTSTARTBLOCK
,
3234 This
->rootStartBlock
);
3236 StorageUtl_WriteDWord(
3238 OFFSET_SBDEPOTSTART
,
3239 This
->smallBlockDepotStart
);
3241 StorageUtl_WriteDWord(
3243 OFFSET_SBDEPOTCOUNT
,
3244 This
->smallBlockDepotChain
?
3245 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3247 StorageUtl_WriteDWord(
3249 OFFSET_EXTBBDEPOTSTART
,
3250 This
->extBigBlockDepotStart
);
3252 StorageUtl_WriteDWord(
3254 OFFSET_EXTBBDEPOTCOUNT
,
3255 This
->extBigBlockDepotCount
);
3257 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3259 StorageUtl_WriteDWord(
3261 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3262 (This
->bigBlockDepotStart
[index
]));
3266 * Write the big block back to the file.
3268 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
3271 /******************************************************************************
3272 * Storage32Impl_ReadProperty
3274 * This method will read the specified property from the property chain.
3276 BOOL
StorageImpl_ReadProperty(
3279 StgProperty
* buffer
)
3281 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
3282 ULARGE_INTEGER offsetInPropSet
;
3286 offsetInPropSet
.u
.HighPart
= 0;
3287 offsetInPropSet
.u
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
3289 readRes
= BlockChainStream_ReadAt(
3290 This
->rootBlockChain
,
3296 if (SUCCEEDED(readRes
))
3298 /* replace the name of root entry (often "Root Entry") by the file name */
3299 WCHAR
*propName
= (index
== This
->base
.rootPropertySetIndex
) ?
3300 This
->filename
: (WCHAR
*)currentProperty
+OFFSET_PS_NAME
;
3302 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3306 PROPERTY_NAME_BUFFER_LEN
);
3307 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3309 memcpy(&buffer
->propertyType
, currentProperty
+ OFFSET_PS_PROPERTYTYPE
, 1);
3311 StorageUtl_ReadWord(
3313 OFFSET_PS_NAMELENGTH
,
3314 &buffer
->sizeOfNameString
);
3316 StorageUtl_ReadDWord(
3318 OFFSET_PS_PREVIOUSPROP
,
3319 &buffer
->previousProperty
);
3321 StorageUtl_ReadDWord(
3324 &buffer
->nextProperty
);
3326 StorageUtl_ReadDWord(
3329 &buffer
->dirProperty
);
3331 StorageUtl_ReadGUID(
3334 &buffer
->propertyUniqueID
);
3336 StorageUtl_ReadDWord(
3339 &buffer
->timeStampS1
);
3341 StorageUtl_ReadDWord(
3344 &buffer
->timeStampD1
);
3346 StorageUtl_ReadDWord(
3349 &buffer
->timeStampS2
);
3351 StorageUtl_ReadDWord(
3354 &buffer
->timeStampD2
);
3356 StorageUtl_ReadDWord(
3358 OFFSET_PS_STARTBLOCK
,
3359 &buffer
->startingBlock
);
3361 StorageUtl_ReadDWord(
3364 &buffer
->size
.u
.LowPart
);
3366 buffer
->size
.u
.HighPart
= 0;
3369 return SUCCEEDED(readRes
) ? TRUE
: FALSE
;
3372 /*********************************************************************
3373 * Write the specified property into the property chain
3375 BOOL
StorageImpl_WriteProperty(
3378 StgProperty
* buffer
)
3380 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
3381 ULARGE_INTEGER offsetInPropSet
;
3385 offsetInPropSet
.u
.HighPart
= 0;
3386 offsetInPropSet
.u
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
3388 memset(currentProperty
, 0, PROPSET_BLOCK_SIZE
);
3391 currentProperty
+ OFFSET_PS_NAME
,
3393 PROPERTY_NAME_BUFFER_LEN
);
3395 memcpy(currentProperty
+ OFFSET_PS_PROPERTYTYPE
, &buffer
->propertyType
, 1);
3397 StorageUtl_WriteWord(
3399 OFFSET_PS_NAMELENGTH
,
3400 buffer
->sizeOfNameString
);
3402 StorageUtl_WriteDWord(
3404 OFFSET_PS_PREVIOUSPROP
,
3405 buffer
->previousProperty
);
3407 StorageUtl_WriteDWord(
3410 buffer
->nextProperty
);
3412 StorageUtl_WriteDWord(
3415 buffer
->dirProperty
);
3417 StorageUtl_WriteGUID(
3420 &buffer
->propertyUniqueID
);
3422 StorageUtl_WriteDWord(
3425 buffer
->timeStampS1
);
3427 StorageUtl_WriteDWord(
3430 buffer
->timeStampD1
);
3432 StorageUtl_WriteDWord(
3435 buffer
->timeStampS2
);
3437 StorageUtl_WriteDWord(
3440 buffer
->timeStampD2
);
3442 StorageUtl_WriteDWord(
3444 OFFSET_PS_STARTBLOCK
,
3445 buffer
->startingBlock
);
3447 StorageUtl_WriteDWord(
3450 buffer
->size
.u
.LowPart
);
3452 writeRes
= BlockChainStream_WriteAt(This
->rootBlockChain
,
3457 return SUCCEEDED(writeRes
) ? TRUE
: FALSE
;
3460 static BOOL
StorageImpl_ReadBigBlock(
3465 ULARGE_INTEGER ulOffset
;
3468 ulOffset
.u
.HighPart
= 0;
3469 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3471 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3472 return (read
== This
->bigBlockSize
);
3475 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3481 ULARGE_INTEGER ulOffset
;
3485 ulOffset
.u
.HighPart
= 0;
3486 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3487 ulOffset
.u
.LowPart
+= offset
;
3489 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3490 *value
= le32toh(tmp
);
3491 return (read
== sizeof(DWORD
));
3494 static BOOL
StorageImpl_WriteBigBlock(
3499 ULARGE_INTEGER ulOffset
;
3502 ulOffset
.u
.HighPart
= 0;
3503 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3505 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3506 return (wrote
== This
->bigBlockSize
);
3509 static BOOL
StorageImpl_WriteDWordToBigBlock(
3515 ULARGE_INTEGER ulOffset
;
3518 ulOffset
.u
.HighPart
= 0;
3519 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3520 ulOffset
.u
.LowPart
+= offset
;
3522 value
= htole32(value
);
3523 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3524 return (wrote
== sizeof(DWORD
));
3527 /******************************************************************************
3528 * Storage32Impl_SmallBlocksToBigBlocks
3530 * This method will convert a small block chain to a big block chain.
3531 * The small block chain will be destroyed.
3533 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3535 SmallBlockChainStream
** ppsbChain
)
3537 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3538 ULARGE_INTEGER size
, offset
;
3539 ULONG cbRead
, cbWritten
;
3540 ULARGE_INTEGER cbTotalRead
;
3541 ULONG propertyIndex
;
3542 HRESULT resWrite
= S_OK
;
3544 StgProperty chainProperty
;
3546 BlockChainStream
*bbTempChain
= NULL
;
3547 BlockChainStream
*bigBlockChain
= NULL
;
3550 * Create a temporary big block chain that doesn't have
3551 * an associated property. This temporary chain will be
3552 * used to copy data from small blocks to big blocks.
3554 bbTempChain
= BlockChainStream_Construct(This
,
3557 if(!bbTempChain
) return NULL
;
3559 * Grow the big block chain.
3561 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3562 BlockChainStream_SetSize(bbTempChain
, size
);
3565 * Copy the contents of the small block chain to the big block chain
3566 * by small block size increments.
3568 offset
.u
.LowPart
= 0;
3569 offset
.u
.HighPart
= 0;
3570 cbTotalRead
.QuadPart
= 0;
3572 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3575 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3577 This
->smallBlockSize
,
3580 if (FAILED(resRead
))
3585 cbTotalRead
.QuadPart
+= cbRead
;
3587 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3593 if (FAILED(resWrite
))
3596 offset
.u
.LowPart
+= This
->smallBlockSize
;
3598 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3599 HeapFree(GetProcessHeap(),0,buffer
);
3601 if (FAILED(resRead
) || FAILED(resWrite
))
3603 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3604 BlockChainStream_Destroy(bbTempChain
);
3609 * Destroy the small block chain.
3611 propertyIndex
= (*ppsbChain
)->ownerPropertyIndex
;
3612 size
.u
.HighPart
= 0;
3614 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3615 SmallBlockChainStream_Destroy(*ppsbChain
);
3619 * Change the property information. This chain is now a big block chain
3620 * and it doesn't reside in the small blocks chain anymore.
3622 StorageImpl_ReadProperty(This
, propertyIndex
, &chainProperty
);
3624 chainProperty
.startingBlock
= bbHeadOfChain
;
3626 StorageImpl_WriteProperty(This
, propertyIndex
, &chainProperty
);
3629 * Destroy the temporary propertyless big block chain.
3630 * Create a new big block chain associated with this property.
3632 BlockChainStream_Destroy(bbTempChain
);
3633 bigBlockChain
= BlockChainStream_Construct(This
,
3637 return bigBlockChain
;
3640 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
3642 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
3644 StorageBaseImpl_Release((IStorage
*)This
->base
.ancestorStorage
);
3645 HeapFree(GetProcessHeap(), 0, This
);
3648 /******************************************************************************
3650 ** Storage32InternalImpl_Commit
3652 ** The non-root storages cannot be opened in transacted mode thus this function
3655 static HRESULT WINAPI
StorageInternalImpl_Commit(
3657 DWORD grfCommitFlags
) /* [in] */
3662 /******************************************************************************
3664 ** Storage32InternalImpl_Revert
3666 ** The non-root storages cannot be opened in transacted mode thus this function
3669 static HRESULT WINAPI
StorageInternalImpl_Revert(
3675 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
3677 IStorage_Release((IStorage
*)This
->parentStorage
);
3678 HeapFree(GetProcessHeap(), 0, This
->stackToVisit
);
3679 HeapFree(GetProcessHeap(), 0, This
);
3682 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
3683 IEnumSTATSTG
* iface
,
3687 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3690 * Perform a sanity check on the parameters.
3693 return E_INVALIDARG
;
3696 * Initialize the return parameter.
3701 * Compare the riid with the interface IDs implemented by this object.
3703 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
3704 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
3706 *ppvObject
= (IEnumSTATSTG
*)This
;
3707 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
3711 return E_NOINTERFACE
;
3714 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
3715 IEnumSTATSTG
* iface
)
3717 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3718 return InterlockedIncrement(&This
->ref
);
3721 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
3722 IEnumSTATSTG
* iface
)
3724 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3728 newRef
= InterlockedDecrement(&This
->ref
);
3731 * If the reference count goes down to 0, perform suicide.
3735 IEnumSTATSTGImpl_Destroy(This
);
3741 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
3742 IEnumSTATSTG
* iface
,
3745 ULONG
* pceltFetched
)
3747 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3749 StgProperty currentProperty
;
3750 STATSTG
* currentReturnStruct
= rgelt
;
3751 ULONG objectFetched
= 0;
3752 ULONG currentSearchNode
;
3755 * Perform a sanity check on the parameters.
3757 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
3758 return E_INVALIDARG
;
3761 * To avoid the special case, get another pointer to a ULONG value if
3762 * the caller didn't supply one.
3764 if (pceltFetched
==0)
3765 pceltFetched
= &objectFetched
;
3768 * Start the iteration, we will iterate until we hit the end of the
3769 * linked list or until we hit the number of items to iterate through
3774 * Start with the node at the top of the stack.
3776 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3778 while ( ( *pceltFetched
< celt
) &&
3779 ( currentSearchNode
!=PROPERTY_NULL
) )
3782 * Remove the top node from the stack
3784 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3787 * Read the property from the storage.
3789 StorageImpl_ReadProperty(This
->parentStorage
,
3794 * Copy the information to the return buffer.
3796 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct
,
3801 * Step to the next item in the iteration
3804 currentReturnStruct
++;
3807 * Push the next search node in the search stack.
3809 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3812 * continue the iteration.
3814 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3817 if (*pceltFetched
== celt
)
3824 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
3825 IEnumSTATSTG
* iface
,
3828 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3830 StgProperty currentProperty
;
3831 ULONG objectFetched
= 0;
3832 ULONG currentSearchNode
;
3835 * Start with the node at the top of the stack.
3837 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3839 while ( (objectFetched
< celt
) &&
3840 (currentSearchNode
!=PROPERTY_NULL
) )
3843 * Remove the top node from the stack
3845 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3848 * Read the property from the storage.
3850 StorageImpl_ReadProperty(This
->parentStorage
,
3855 * Step to the next item in the iteration
3860 * Push the next search node in the search stack.
3862 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3865 * continue the iteration.
3867 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3870 if (objectFetched
== celt
)
3876 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
3877 IEnumSTATSTG
* iface
)
3879 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3881 StgProperty rootProperty
;
3882 BOOL readSuccessful
;
3885 * Re-initialize the search stack to an empty stack
3887 This
->stackSize
= 0;
3890 * Read the root property from the storage.
3892 readSuccessful
= StorageImpl_ReadProperty(
3893 This
->parentStorage
,
3894 This
->firstPropertyNode
,
3899 assert(rootProperty
.sizeOfNameString
!=0);
3902 * Push the search node in the search stack.
3904 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.dirProperty
);
3910 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
3911 IEnumSTATSTG
* iface
,
3912 IEnumSTATSTG
** ppenum
)
3914 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3916 IEnumSTATSTGImpl
* newClone
;
3919 * Perform a sanity check on the parameters.
3922 return E_INVALIDARG
;
3924 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
3925 This
->firstPropertyNode
);
3929 * The new clone enumeration must point to the same current node as
3932 newClone
->stackSize
= This
->stackSize
;
3933 newClone
->stackMaxSize
= This
->stackMaxSize
;
3934 newClone
->stackToVisit
=
3935 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * newClone
->stackMaxSize
);
3938 newClone
->stackToVisit
,
3940 sizeof(ULONG
) * newClone
->stackSize
);
3942 *ppenum
= (IEnumSTATSTG
*)newClone
;
3945 * Don't forget to nail down a reference to the clone before
3948 IEnumSTATSTGImpl_AddRef(*ppenum
);
3953 static INT
IEnumSTATSTGImpl_FindParentProperty(
3954 IEnumSTATSTGImpl
*This
,
3955 ULONG childProperty
,
3956 StgProperty
*currentProperty
,
3959 ULONG currentSearchNode
;
3963 * To avoid the special case, get another pointer to a ULONG value if
3964 * the caller didn't supply one.
3967 thisNodeId
= &foundNode
;
3970 * Start with the node at the top of the stack.
3972 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3975 while (currentSearchNode
!=PROPERTY_NULL
)
3978 * Store the current node in the returned parameters
3980 *thisNodeId
= currentSearchNode
;
3983 * Remove the top node from the stack
3985 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3988 * Read the property from the storage.
3990 StorageImpl_ReadProperty(
3991 This
->parentStorage
,
3995 if (currentProperty
->previousProperty
== childProperty
)
3996 return PROPERTY_RELATION_PREVIOUS
;
3998 else if (currentProperty
->nextProperty
== childProperty
)
3999 return PROPERTY_RELATION_NEXT
;
4001 else if (currentProperty
->dirProperty
== childProperty
)
4002 return PROPERTY_RELATION_DIR
;
4005 * Push the next search node in the search stack.
4007 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
4010 * continue the iteration.
4012 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
4015 return PROPERTY_NULL
;
4018 static ULONG
IEnumSTATSTGImpl_FindProperty(
4019 IEnumSTATSTGImpl
* This
,
4020 const OLECHAR
* lpszPropName
,
4021 StgProperty
* currentProperty
)
4023 ULONG currentSearchNode
;
4026 * Start with the node at the top of the stack.
4028 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
4030 while (currentSearchNode
!=PROPERTY_NULL
)
4033 * Remove the top node from the stack
4035 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
4038 * Read the property from the storage.
4040 StorageImpl_ReadProperty(This
->parentStorage
,
4044 if ( propertyNameCmp(
4045 (const OLECHAR
*)currentProperty
->name
,
4046 (const OLECHAR
*)lpszPropName
) == 0)
4047 return currentSearchNode
;
4050 * Push the next search node in the search stack.
4052 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
4055 * continue the iteration.
4057 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
4060 return PROPERTY_NULL
;
4063 static void IEnumSTATSTGImpl_PushSearchNode(
4064 IEnumSTATSTGImpl
* This
,
4067 StgProperty rootProperty
;
4068 BOOL readSuccessful
;
4071 * First, make sure we're not trying to push an unexisting node.
4073 if (nodeToPush
==PROPERTY_NULL
)
4077 * First push the node to the stack
4079 if (This
->stackSize
== This
->stackMaxSize
)
4081 This
->stackMaxSize
+= ENUMSTATSGT_SIZE_INCREMENT
;
4083 This
->stackToVisit
= HeapReAlloc(
4087 sizeof(ULONG
) * This
->stackMaxSize
);
4090 This
->stackToVisit
[This
->stackSize
] = nodeToPush
;
4094 * Read the root property from the storage.
4096 readSuccessful
= StorageImpl_ReadProperty(
4097 This
->parentStorage
,
4103 assert(rootProperty
.sizeOfNameString
!=0);
4106 * Push the previous search node in the search stack.
4108 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.previousProperty
);
4112 static ULONG
IEnumSTATSTGImpl_PopSearchNode(
4113 IEnumSTATSTGImpl
* This
,
4118 if (This
->stackSize
== 0)
4119 return PROPERTY_NULL
;
4121 topNode
= This
->stackToVisit
[This
->stackSize
-1];
4130 * Virtual function table for the IEnumSTATSTGImpl class.
4132 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
4134 IEnumSTATSTGImpl_QueryInterface
,
4135 IEnumSTATSTGImpl_AddRef
,
4136 IEnumSTATSTGImpl_Release
,
4137 IEnumSTATSTGImpl_Next
,
4138 IEnumSTATSTGImpl_Skip
,
4139 IEnumSTATSTGImpl_Reset
,
4140 IEnumSTATSTGImpl_Clone
4143 /******************************************************************************
4144 ** IEnumSTATSTGImpl implementation
4147 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
4148 StorageImpl
* parentStorage
,
4149 ULONG firstPropertyNode
)
4151 IEnumSTATSTGImpl
* newEnumeration
;
4153 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
4155 if (newEnumeration
!=0)
4158 * Set-up the virtual function table and reference count.
4160 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
4161 newEnumeration
->ref
= 0;
4164 * We want to nail-down the reference to the storage in case the
4165 * enumeration out-lives the storage in the client application.
4167 newEnumeration
->parentStorage
= parentStorage
;
4168 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
4170 newEnumeration
->firstPropertyNode
= firstPropertyNode
;
4173 * Initialize the search stack
4175 newEnumeration
->stackSize
= 0;
4176 newEnumeration
->stackMaxSize
= ENUMSTATSGT_SIZE_INCREMENT
;
4177 newEnumeration
->stackToVisit
=
4178 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
)*ENUMSTATSGT_SIZE_INCREMENT
);
4181 * Make sure the current node of the iterator is the first one.
4183 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
4186 return newEnumeration
;
4190 * Virtual function table for the Storage32InternalImpl class.
4192 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
4194 StorageBaseImpl_QueryInterface
,
4195 StorageBaseImpl_AddRef
,
4196 StorageBaseImpl_Release
,
4197 StorageBaseImpl_CreateStream
,
4198 StorageBaseImpl_OpenStream
,
4199 StorageImpl_CreateStorage
,
4200 StorageBaseImpl_OpenStorage
,
4202 StorageImpl_MoveElementTo
,
4203 StorageInternalImpl_Commit
,
4204 StorageInternalImpl_Revert
,
4205 StorageBaseImpl_EnumElements
,
4206 StorageImpl_DestroyElement
,
4207 StorageBaseImpl_RenameElement
,
4208 StorageImpl_SetElementTimes
,
4209 StorageBaseImpl_SetClass
,
4210 StorageImpl_SetStateBits
,
4211 StorageBaseImpl_Stat
4214 /******************************************************************************
4215 ** Storage32InternalImpl implementation
4218 static StorageInternalImpl
* StorageInternalImpl_Construct(
4219 StorageImpl
* ancestorStorage
,
4221 ULONG rootPropertyIndex
)
4223 StorageInternalImpl
* newStorage
;
4226 * Allocate space for the new storage object
4228 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
4233 * Initialize the stream list
4235 list_init(&newStorage
->base
.strmHead
);
4238 * Initialize the virtual function table.
4240 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
4241 newStorage
->base
.v_destructor
= &StorageInternalImpl_Destroy
;
4242 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
4245 * Keep the ancestor storage pointer and nail a reference to it.
4247 newStorage
->base
.ancestorStorage
= ancestorStorage
;
4248 StorageBaseImpl_AddRef((IStorage
*)(newStorage
->base
.ancestorStorage
));
4251 * Keep the index of the root property set for this storage,
4253 newStorage
->base
.rootPropertySetIndex
= rootPropertyIndex
;
4261 /******************************************************************************
4262 ** StorageUtl implementation
4265 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
4269 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
4270 *value
= le16toh(tmp
);
4273 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
4275 value
= htole16(value
);
4276 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
4279 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
4283 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
4284 *value
= le32toh(tmp
);
4287 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
4289 value
= htole32(value
);
4290 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
4293 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
4294 ULARGE_INTEGER
* value
)
4296 #ifdef WORDS_BIGENDIAN
4299 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4300 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
4301 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
4303 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4307 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
4308 const ULARGE_INTEGER
*value
)
4310 #ifdef WORDS_BIGENDIAN
4313 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
4314 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
4315 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
4317 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
4321 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
4323 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
4324 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
4325 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
4327 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
4330 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
4332 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
4333 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
4334 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
4336 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
4339 void StorageUtl_CopyPropertyToSTATSTG(
4340 STATSTG
* destination
,
4341 StgProperty
* source
,
4345 * The copy of the string occurs only when the flag is not set
4347 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
4348 (source
->name
== NULL
) ||
4349 (source
->name
[0] == 0) )
4351 destination
->pwcsName
= 0;
4355 destination
->pwcsName
=
4356 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
4358 strcpyW((LPWSTR
)destination
->pwcsName
, source
->name
);
4361 switch (source
->propertyType
)
4363 case PROPTYPE_STORAGE
:
4365 destination
->type
= STGTY_STORAGE
;
4367 case PROPTYPE_STREAM
:
4368 destination
->type
= STGTY_STREAM
;
4371 destination
->type
= STGTY_STREAM
;
4375 destination
->cbSize
= source
->size
;
4377 currentReturnStruct->mtime = {0}; TODO
4378 currentReturnStruct->ctime = {0};
4379 currentReturnStruct->atime = {0};
4381 destination
->grfMode
= 0;
4382 destination
->grfLocksSupported
= 0;
4383 destination
->clsid
= source
->propertyUniqueID
;
4384 destination
->grfStateBits
= 0;
4385 destination
->reserved
= 0;
4388 /******************************************************************************
4389 ** BlockChainStream implementation
4392 BlockChainStream
* BlockChainStream_Construct(
4393 StorageImpl
* parentStorage
,
4394 ULONG
* headOfStreamPlaceHolder
,
4395 ULONG propertyIndex
)
4397 BlockChainStream
* newStream
;
4400 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
4402 newStream
->parentStorage
= parentStorage
;
4403 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
4404 newStream
->ownerPropertyIndex
= propertyIndex
;
4405 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
4406 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
4407 newStream
->numBlocks
= 0;
4409 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
4411 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4413 newStream
->numBlocks
++;
4414 newStream
->tailIndex
= blockIndex
;
4416 if(FAILED(StorageImpl_GetNextBlockInChain(
4421 HeapFree(GetProcessHeap(), 0, newStream
);
4429 void BlockChainStream_Destroy(BlockChainStream
* This
)
4431 HeapFree(GetProcessHeap(), 0, This
);
4434 /******************************************************************************
4435 * BlockChainStream_GetHeadOfChain
4437 * Returns the head of this stream chain.
4438 * Some special chains don't have properties, their heads are kept in
4439 * This->headOfStreamPlaceHolder.
4442 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
4444 StgProperty chainProperty
;
4445 BOOL readSuccessful
;
4447 if (This
->headOfStreamPlaceHolder
!= 0)
4448 return *(This
->headOfStreamPlaceHolder
);
4450 if (This
->ownerPropertyIndex
!= PROPERTY_NULL
)
4452 readSuccessful
= StorageImpl_ReadProperty(
4453 This
->parentStorage
,
4454 This
->ownerPropertyIndex
,
4459 return chainProperty
.startingBlock
;
4463 return BLOCK_END_OF_CHAIN
;
4466 /******************************************************************************
4467 * BlockChainStream_GetCount
4469 * Returns the number of blocks that comprises this chain.
4470 * This is not the size of the stream as the last block may not be full!
4473 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
4478 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4480 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4484 if(FAILED(StorageImpl_GetNextBlockInChain(
4485 This
->parentStorage
,
4494 /******************************************************************************
4495 * BlockChainStream_ReadAt
4497 * Reads a specified number of bytes from this chain at the specified offset.
4498 * bytesRead may be NULL.
4499 * Failure will be returned if the specified number of bytes has not been read.
4501 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
4502 ULARGE_INTEGER offset
,
4507 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4508 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4509 ULONG bytesToReadInBuffer
;
4513 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
4516 * Find the first block in the stream that contains part of the buffer.
4518 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4519 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4520 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4522 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4523 This
->lastBlockNoInSequence
= blockNoInSequence
;
4527 ULONG temp
= blockNoInSequence
;
4529 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4530 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4531 This
->lastBlockNoInSequence
= temp
;
4534 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4536 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4537 return STG_E_DOCFILECORRUPT
;
4538 blockNoInSequence
--;
4541 if ((blockNoInSequence
> 0) && (blockIndex
== BLOCK_END_OF_CHAIN
))
4542 return STG_E_DOCFILECORRUPT
; /* We failed to find the starting block */
4544 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4547 * Start reading the buffer.
4550 bufferWalker
= buffer
;
4552 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4554 ULARGE_INTEGER ulOffset
;
4557 * Calculate how many bytes we can copy from this big block.
4559 bytesToReadInBuffer
=
4560 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4562 TRACE("block %i\n",blockIndex
);
4563 ulOffset
.u
.HighPart
= 0;
4564 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
4567 StorageImpl_ReadAt(This
->parentStorage
,
4570 bytesToReadInBuffer
,
4573 * Step to the next big block.
4575 if( size
> bytesReadAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4576 return STG_E_DOCFILECORRUPT
;
4578 bufferWalker
+= bytesReadAt
;
4579 size
-= bytesReadAt
;
4580 *bytesRead
+= bytesReadAt
;
4581 offsetInBlock
= 0; /* There is no offset on the next block */
4583 if (bytesToReadInBuffer
!= bytesReadAt
)
4587 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
4590 /******************************************************************************
4591 * BlockChainStream_WriteAt
4593 * Writes the specified number of bytes to this chain at the specified offset.
4594 * bytesWritten may be NULL.
4595 * Will fail if not all specified number of bytes have been written.
4597 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
4598 ULARGE_INTEGER offset
,
4601 ULONG
* bytesWritten
)
4603 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4604 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4607 const BYTE
* bufferWalker
;
4610 * Find the first block in the stream that contains part of the buffer.
4612 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4613 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4614 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4616 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4617 This
->lastBlockNoInSequence
= blockNoInSequence
;
4621 ULONG temp
= blockNoInSequence
;
4623 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4624 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4625 This
->lastBlockNoInSequence
= temp
;
4628 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4630 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4632 return STG_E_DOCFILECORRUPT
;
4633 blockNoInSequence
--;
4636 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4638 /* BlockChainStream_SetSize should have already been called to ensure we have
4639 * enough blocks in the chain to write into */
4640 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4642 ERR("not enough blocks in chain to write data\n");
4643 return STG_E_DOCFILECORRUPT
;
4647 * Here, I'm casting away the constness on the buffer variable
4648 * This is OK since we don't intend to modify that buffer.
4651 bufferWalker
= (const BYTE
*)buffer
;
4653 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4655 ULARGE_INTEGER ulOffset
;
4656 DWORD bytesWrittenAt
;
4658 * Calculate how many bytes we can copy from this big block.
4661 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4663 TRACE("block %i\n",blockIndex
);
4664 ulOffset
.u
.HighPart
= 0;
4665 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
4668 StorageImpl_WriteAt(This
->parentStorage
,
4670 (BYTE
*)bufferWalker
,
4675 * Step to the next big block.
4677 if(size
> bytesWrittenAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4679 return STG_E_DOCFILECORRUPT
;
4681 bufferWalker
+= bytesWrittenAt
;
4682 size
-= bytesWrittenAt
;
4683 *bytesWritten
+= bytesWrittenAt
;
4684 offsetInBlock
= 0; /* There is no offset on the next block */
4686 if (bytesWrittenAt
!= bytesToWrite
)
4690 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
4693 /******************************************************************************
4694 * BlockChainStream_Shrink
4696 * Shrinks this chain in the big block depot.
4698 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
4699 ULARGE_INTEGER newSize
)
4701 ULONG blockIndex
, extraBlock
;
4706 * Reset the last accessed block cache.
4708 This
->lastBlockNoInSequence
= 0xFFFFFFFF;
4709 This
->lastBlockNoInSequenceIndex
= BLOCK_END_OF_CHAIN
;
4712 * Figure out how many blocks are needed to contain the new size
4714 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4716 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4719 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4722 * Go to the new end of chain
4724 while (count
< numBlocks
)
4726 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4732 /* Get the next block before marking the new end */
4733 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4737 /* Mark the new end of chain */
4738 StorageImpl_SetNextBlockInChain(
4739 This
->parentStorage
,
4741 BLOCK_END_OF_CHAIN
);
4743 This
->tailIndex
= blockIndex
;
4744 This
->numBlocks
= numBlocks
;
4747 * Mark the extra blocks as free
4749 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4751 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
,
4754 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
4755 extraBlock
= blockIndex
;
4761 /******************************************************************************
4762 * BlockChainStream_Enlarge
4764 * Grows this chain in the big block depot.
4766 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
4767 ULARGE_INTEGER newSize
)
4769 ULONG blockIndex
, currentBlock
;
4771 ULONG oldNumBlocks
= 0;
4773 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4776 * Empty chain. Create the head.
4778 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4780 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4781 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
4783 BLOCK_END_OF_CHAIN
);
4785 if (This
->headOfStreamPlaceHolder
!= 0)
4787 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
4791 StgProperty chainProp
;
4792 assert(This
->ownerPropertyIndex
!= PROPERTY_NULL
);
4794 StorageImpl_ReadProperty(
4795 This
->parentStorage
,
4796 This
->ownerPropertyIndex
,
4799 chainProp
.startingBlock
= blockIndex
;
4801 StorageImpl_WriteProperty(
4802 This
->parentStorage
,
4803 This
->ownerPropertyIndex
,
4807 This
->tailIndex
= blockIndex
;
4808 This
->numBlocks
= 1;
4812 * Figure out how many blocks are needed to contain this stream
4814 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4816 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4820 * Go to the current end of chain
4822 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
4824 currentBlock
= blockIndex
;
4826 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4829 currentBlock
= blockIndex
;
4831 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
4836 This
->tailIndex
= currentBlock
;
4839 currentBlock
= This
->tailIndex
;
4840 oldNumBlocks
= This
->numBlocks
;
4843 * Add new blocks to the chain
4845 if (oldNumBlocks
< newNumBlocks
)
4847 while (oldNumBlocks
< newNumBlocks
)
4849 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4851 StorageImpl_SetNextBlockInChain(
4852 This
->parentStorage
,
4856 StorageImpl_SetNextBlockInChain(
4857 This
->parentStorage
,
4859 BLOCK_END_OF_CHAIN
);
4861 currentBlock
= blockIndex
;
4865 This
->tailIndex
= blockIndex
;
4866 This
->numBlocks
= newNumBlocks
;
4872 /******************************************************************************
4873 * BlockChainStream_SetSize
4875 * Sets the size of this stream. The big block depot will be updated.
4876 * The file will grow if we grow the chain.
4878 * TODO: Free the actual blocks in the file when we shrink the chain.
4879 * Currently, the blocks are still in the file. So the file size
4880 * doesn't shrink even if we shrink streams.
4882 BOOL
BlockChainStream_SetSize(
4883 BlockChainStream
* This
,
4884 ULARGE_INTEGER newSize
)
4886 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
4888 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
4891 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
4893 BlockChainStream_Shrink(This
, newSize
);
4897 BlockChainStream_Enlarge(This
, newSize
);
4903 /******************************************************************************
4904 * BlockChainStream_GetSize
4906 * Returns the size of this chain.
4907 * Will return the block count if this chain doesn't have a property.
4909 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
4911 StgProperty chainProperty
;
4913 if(This
->headOfStreamPlaceHolder
== NULL
)
4916 * This chain is a data stream read the property and return
4917 * the appropriate size
4919 StorageImpl_ReadProperty(
4920 This
->parentStorage
,
4921 This
->ownerPropertyIndex
,
4924 return chainProperty
.size
;
4929 * this chain is a chain that does not have a property, figure out the
4930 * size by making the product number of used blocks times the
4933 ULARGE_INTEGER result
;
4934 result
.u
.HighPart
= 0;
4937 BlockChainStream_GetCount(This
) *
4938 This
->parentStorage
->bigBlockSize
;
4944 /******************************************************************************
4945 ** SmallBlockChainStream implementation
4948 SmallBlockChainStream
* SmallBlockChainStream_Construct(
4949 StorageImpl
* parentStorage
,
4950 ULONG propertyIndex
)
4952 SmallBlockChainStream
* newStream
;
4954 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
4956 newStream
->parentStorage
= parentStorage
;
4957 newStream
->ownerPropertyIndex
= propertyIndex
;
4962 void SmallBlockChainStream_Destroy(
4963 SmallBlockChainStream
* This
)
4965 HeapFree(GetProcessHeap(), 0, This
);
4968 /******************************************************************************
4969 * SmallBlockChainStream_GetHeadOfChain
4971 * Returns the head of this chain of small blocks.
4973 static ULONG
SmallBlockChainStream_GetHeadOfChain(
4974 SmallBlockChainStream
* This
)
4976 StgProperty chainProperty
;
4977 BOOL readSuccessful
;
4979 if (This
->ownerPropertyIndex
)
4981 readSuccessful
= StorageImpl_ReadProperty(
4982 This
->parentStorage
,
4983 This
->ownerPropertyIndex
,
4988 return chainProperty
.startingBlock
;
4993 return BLOCK_END_OF_CHAIN
;
4996 /******************************************************************************
4997 * SmallBlockChainStream_GetNextBlockInChain
4999 * Returns the index of the next small block in this chain.
5002 * - BLOCK_END_OF_CHAIN: end of this chain
5003 * - BLOCK_UNUSED: small block 'blockIndex' is free
5005 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
5006 SmallBlockChainStream
* This
,
5008 ULONG
* nextBlockInChain
)
5010 ULARGE_INTEGER offsetOfBlockInDepot
;
5015 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
5017 offsetOfBlockInDepot
.u
.HighPart
= 0;
5018 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5021 * Read those bytes in the buffer from the small block file.
5023 res
= BlockChainStream_ReadAt(
5024 This
->parentStorage
->smallBlockDepotChain
,
5025 offsetOfBlockInDepot
,
5032 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
5039 /******************************************************************************
5040 * SmallBlockChainStream_SetNextBlockInChain
5042 * Writes the index of the next block of the specified block in the small
5044 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5045 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5047 static void SmallBlockChainStream_SetNextBlockInChain(
5048 SmallBlockChainStream
* This
,
5052 ULARGE_INTEGER offsetOfBlockInDepot
;
5056 offsetOfBlockInDepot
.u
.HighPart
= 0;
5057 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5059 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
5062 * Read those bytes in the buffer from the small block file.
5064 BlockChainStream_WriteAt(
5065 This
->parentStorage
->smallBlockDepotChain
,
5066 offsetOfBlockInDepot
,
5072 /******************************************************************************
5073 * SmallBlockChainStream_FreeBlock
5075 * Flag small block 'blockIndex' as free in the small block depot.
5077 static void SmallBlockChainStream_FreeBlock(
5078 SmallBlockChainStream
* This
,
5081 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
5084 /******************************************************************************
5085 * SmallBlockChainStream_GetNextFreeBlock
5087 * Returns the index of a free small block. The small block depot will be
5088 * enlarged if necessary. The small block chain will also be enlarged if
5091 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
5092 SmallBlockChainStream
* This
)
5094 ULARGE_INTEGER offsetOfBlockInDepot
;
5097 ULONG blockIndex
= 0;
5098 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
5100 ULONG smallBlocksPerBigBlock
;
5102 offsetOfBlockInDepot
.u
.HighPart
= 0;
5105 * Scan the small block depot for a free block
5107 while (nextBlockIndex
!= BLOCK_UNUSED
)
5109 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
5111 res
= BlockChainStream_ReadAt(
5112 This
->parentStorage
->smallBlockDepotChain
,
5113 offsetOfBlockInDepot
,
5119 * If we run out of space for the small block depot, enlarge it
5123 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
5125 if (nextBlockIndex
!= BLOCK_UNUSED
)
5131 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
5133 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
5134 ULONG nextBlock
, newsbdIndex
;
5135 BYTE smallBlockDepot
[BIG_BLOCK_SIZE
];
5137 nextBlock
= sbdIndex
;
5138 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
5140 sbdIndex
= nextBlock
;
5141 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
, &nextBlock
);
5144 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5145 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
5146 StorageImpl_SetNextBlockInChain(
5147 This
->parentStorage
,
5151 StorageImpl_SetNextBlockInChain(
5152 This
->parentStorage
,
5154 BLOCK_END_OF_CHAIN
);
5157 * Initialize all the small blocks to free
5159 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
5160 StorageImpl_WriteBigBlock(This
->parentStorage
, newsbdIndex
, smallBlockDepot
);
5165 * We have just created the small block depot.
5167 StgProperty rootProp
;
5171 * Save it in the header
5173 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
5174 StorageImpl_SaveFileHeader(This
->parentStorage
);
5177 * And allocate the first big block that will contain small blocks
5180 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5182 StorageImpl_SetNextBlockInChain(
5183 This
->parentStorage
,
5185 BLOCK_END_OF_CHAIN
);
5187 StorageImpl_ReadProperty(
5188 This
->parentStorage
,
5189 This
->parentStorage
->base
.rootPropertySetIndex
,
5192 rootProp
.startingBlock
= sbStartIndex
;
5193 rootProp
.size
.u
.HighPart
= 0;
5194 rootProp
.size
.u
.LowPart
= This
->parentStorage
->bigBlockSize
;
5196 StorageImpl_WriteProperty(
5197 This
->parentStorage
,
5198 This
->parentStorage
->base
.rootPropertySetIndex
,
5204 smallBlocksPerBigBlock
=
5205 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
5208 * Verify if we have to allocate big blocks to contain small blocks
5210 if (blockIndex
% smallBlocksPerBigBlock
== 0)
5212 StgProperty rootProp
;
5213 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
5215 StorageImpl_ReadProperty(
5216 This
->parentStorage
,
5217 This
->parentStorage
->base
.rootPropertySetIndex
,
5220 if (rootProp
.size
.u
.LowPart
<
5221 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
5223 rootProp
.size
.u
.LowPart
+= This
->parentStorage
->bigBlockSize
;
5225 BlockChainStream_SetSize(
5226 This
->parentStorage
->smallBlockRootChain
,
5229 StorageImpl_WriteProperty(
5230 This
->parentStorage
,
5231 This
->parentStorage
->base
.rootPropertySetIndex
,
5239 /******************************************************************************
5240 * SmallBlockChainStream_ReadAt
5242 * Reads a specified number of bytes from this chain at the specified offset.
5243 * bytesRead may be NULL.
5244 * Failure will be returned if the specified number of bytes has not been read.
5246 HRESULT
SmallBlockChainStream_ReadAt(
5247 SmallBlockChainStream
* This
,
5248 ULARGE_INTEGER offset
,
5254 ULARGE_INTEGER offsetInBigBlockFile
;
5255 ULONG blockNoInSequence
=
5256 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5258 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5259 ULONG bytesToReadInBuffer
;
5261 ULONG bytesReadFromBigBlockFile
;
5265 * This should never happen on a small block file.
5267 assert(offset
.u
.HighPart
==0);
5270 * Find the first block in the stream that contains part of the buffer.
5272 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5274 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5276 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5279 blockNoInSequence
--;
5283 * Start reading the buffer.
5286 bufferWalker
= buffer
;
5288 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5291 * Calculate how many bytes we can copy from this small block.
5293 bytesToReadInBuffer
=
5294 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5297 * Calculate the offset of the small block in the small block file.
5299 offsetInBigBlockFile
.u
.HighPart
= 0;
5300 offsetInBigBlockFile
.u
.LowPart
=
5301 blockIndex
* This
->parentStorage
->smallBlockSize
;
5303 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5306 * Read those bytes in the buffer from the small block file.
5307 * The small block has already been identified so it shouldn't fail
5308 * unless the file is corrupt.
5310 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
5311 offsetInBigBlockFile
,
5312 bytesToReadInBuffer
,
5314 &bytesReadFromBigBlockFile
);
5320 * Step to the next big block.
5322 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5324 return STG_E_DOCFILECORRUPT
;
5326 bufferWalker
+= bytesReadFromBigBlockFile
;
5327 size
-= bytesReadFromBigBlockFile
;
5328 *bytesRead
+= bytesReadFromBigBlockFile
;
5329 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5332 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5335 /******************************************************************************
5336 * SmallBlockChainStream_WriteAt
5338 * Writes the specified number of bytes to this chain at the specified offset.
5339 * bytesWritten may be NULL.
5340 * Will fail if not all specified number of bytes have been written.
5342 HRESULT
SmallBlockChainStream_WriteAt(
5343 SmallBlockChainStream
* This
,
5344 ULARGE_INTEGER offset
,
5347 ULONG
* bytesWritten
)
5349 ULARGE_INTEGER offsetInBigBlockFile
;
5350 ULONG blockNoInSequence
=
5351 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5353 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5354 ULONG bytesToWriteInBuffer
;
5356 ULONG bytesWrittenToBigBlockFile
;
5357 const BYTE
* bufferWalker
;
5361 * This should never happen on a small block file.
5363 assert(offset
.u
.HighPart
==0);
5366 * Find the first block in the stream that contains part of the buffer.
5368 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5370 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5372 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5373 return STG_E_DOCFILECORRUPT
;
5374 blockNoInSequence
--;
5378 * Start writing the buffer.
5380 * Here, I'm casting away the constness on the buffer variable
5381 * This is OK since we don't intend to modify that buffer.
5384 bufferWalker
= (const BYTE
*)buffer
;
5385 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5388 * Calculate how many bytes we can copy to this small block.
5390 bytesToWriteInBuffer
=
5391 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5394 * Calculate the offset of the small block in the small block file.
5396 offsetInBigBlockFile
.u
.HighPart
= 0;
5397 offsetInBigBlockFile
.u
.LowPart
=
5398 blockIndex
* This
->parentStorage
->smallBlockSize
;
5400 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5403 * Write those bytes in the buffer to the small block file.
5405 res
= BlockChainStream_WriteAt(
5406 This
->parentStorage
->smallBlockRootChain
,
5407 offsetInBigBlockFile
,
5408 bytesToWriteInBuffer
,
5410 &bytesWrittenToBigBlockFile
);
5415 * Step to the next big block.
5417 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5420 bufferWalker
+= bytesWrittenToBigBlockFile
;
5421 size
-= bytesWrittenToBigBlockFile
;
5422 *bytesWritten
+= bytesWrittenToBigBlockFile
;
5423 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5426 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
5429 /******************************************************************************
5430 * SmallBlockChainStream_Shrink
5432 * Shrinks this chain in the small block depot.
5434 static BOOL
SmallBlockChainStream_Shrink(
5435 SmallBlockChainStream
* This
,
5436 ULARGE_INTEGER newSize
)
5438 ULONG blockIndex
, extraBlock
;
5442 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5444 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5447 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5450 * Go to the new end of chain
5452 while (count
< numBlocks
)
5454 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5461 * If the count is 0, we have a special case, the head of the chain was
5466 StgProperty chainProp
;
5468 StorageImpl_ReadProperty(This
->parentStorage
,
5469 This
->ownerPropertyIndex
,
5472 chainProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
5474 StorageImpl_WriteProperty(This
->parentStorage
,
5475 This
->ownerPropertyIndex
,
5479 * We start freeing the chain at the head block.
5481 extraBlock
= blockIndex
;
5485 /* Get the next block before marking the new end */
5486 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5490 /* Mark the new end of chain */
5491 SmallBlockChainStream_SetNextBlockInChain(
5494 BLOCK_END_OF_CHAIN
);
5498 * Mark the extra blocks as free
5500 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
5502 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
5505 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
5506 extraBlock
= blockIndex
;
5512 /******************************************************************************
5513 * SmallBlockChainStream_Enlarge
5515 * Grows this chain in the small block depot.
5517 static BOOL
SmallBlockChainStream_Enlarge(
5518 SmallBlockChainStream
* This
,
5519 ULARGE_INTEGER newSize
)
5521 ULONG blockIndex
, currentBlock
;
5523 ULONG oldNumBlocks
= 0;
5525 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5530 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5533 StgProperty chainProp
;
5535 StorageImpl_ReadProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
5538 chainProp
.startingBlock
= SmallBlockChainStream_GetNextFreeBlock(This
);
5540 StorageImpl_WriteProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
5543 blockIndex
= chainProp
.startingBlock
;
5544 SmallBlockChainStream_SetNextBlockInChain(
5547 BLOCK_END_OF_CHAIN
);
5550 currentBlock
= blockIndex
;
5553 * Figure out how many blocks are needed to contain this stream
5555 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5557 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5561 * Go to the current end of chain
5563 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5566 currentBlock
= blockIndex
;
5567 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
5572 * Add new blocks to the chain
5574 while (oldNumBlocks
< newNumBlocks
)
5576 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
5577 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
5579 SmallBlockChainStream_SetNextBlockInChain(
5582 BLOCK_END_OF_CHAIN
);
5584 currentBlock
= blockIndex
;
5591 /******************************************************************************
5592 * SmallBlockChainStream_SetSize
5594 * Sets the size of this stream.
5595 * The file will grow if we grow the chain.
5597 * TODO: Free the actual blocks in the file when we shrink the chain.
5598 * Currently, the blocks are still in the file. So the file size
5599 * doesn't shrink even if we shrink streams.
5601 BOOL
SmallBlockChainStream_SetSize(
5602 SmallBlockChainStream
* This
,
5603 ULARGE_INTEGER newSize
)
5605 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
5607 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
5610 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
5612 SmallBlockChainStream_Shrink(This
, newSize
);
5616 SmallBlockChainStream_Enlarge(This
, newSize
);
5622 /******************************************************************************
5623 * SmallBlockChainStream_GetSize
5625 * Returns the size of this chain.
5627 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
5629 StgProperty chainProperty
;
5631 StorageImpl_ReadProperty(
5632 This
->parentStorage
,
5633 This
->ownerPropertyIndex
,
5636 return chainProperty
.size
;
5639 /******************************************************************************
5640 * StgCreateDocfile [OLE32.@]
5641 * Creates a new compound file storage object
5644 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5645 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5646 * reserved [ ?] unused?, usually 0
5647 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5650 * S_OK if the file was successfully created
5651 * some STG_E_ value if error
5653 * if pwcsName is NULL, create file with new unique name
5654 * the function can returns
5655 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5658 HRESULT WINAPI
StgCreateDocfile(
5662 IStorage
**ppstgOpen
)
5664 StorageImpl
* newStorage
= 0;
5665 HANDLE hFile
= INVALID_HANDLE_VALUE
;
5666 HRESULT hr
= STG_E_INVALIDFLAG
;
5670 DWORD fileAttributes
;
5671 WCHAR tempFileName
[MAX_PATH
];
5673 TRACE("(%s, %x, %d, %p)\n",
5674 debugstr_w(pwcsName
), grfMode
,
5675 reserved
, ppstgOpen
);
5678 * Validate the parameters
5681 return STG_E_INVALIDPOINTER
;
5683 return STG_E_INVALIDPARAMETER
;
5685 /* if no share mode given then DENY_NONE is the default */
5686 if (STGM_SHARE_MODE(grfMode
) == 0)
5687 grfMode
|= STGM_SHARE_DENY_NONE
;
5690 * Validate the STGM flags
5692 if ( FAILED( validateSTGM(grfMode
) ))
5695 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5696 switch(STGM_ACCESS_MODE(grfMode
))
5699 case STGM_READWRITE
:
5705 /* in direct mode, can only use SHARE_EXCLUSIVE */
5706 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
5709 /* but in transacted mode, any share mode is valid */
5712 * Generate a unique name.
5716 WCHAR tempPath
[MAX_PATH
];
5717 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
5719 memset(tempPath
, 0, sizeof(tempPath
));
5720 memset(tempFileName
, 0, sizeof(tempFileName
));
5722 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
5725 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
5726 pwcsName
= tempFileName
;
5729 hr
= STG_E_INSUFFICIENTMEMORY
;
5733 creationMode
= TRUNCATE_EXISTING
;
5737 creationMode
= GetCreationModeFromSTGM(grfMode
);
5741 * Interpret the STGM value grfMode
5743 shareMode
= GetShareModeFromSTGM(grfMode
);
5744 accessMode
= GetAccessModeFromSTGM(grfMode
);
5746 if (grfMode
& STGM_DELETEONRELEASE
)
5747 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
5749 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
5751 if (grfMode
& STGM_TRANSACTED
)
5752 FIXME("Transacted mode not implemented.\n");
5755 * Initialize the "out" parameter.
5759 hFile
= CreateFileW(pwcsName
,
5767 if (hFile
== INVALID_HANDLE_VALUE
)
5769 if(GetLastError() == ERROR_FILE_EXISTS
)
5770 hr
= STG_E_FILEALREADYEXISTS
;
5777 * Allocate and initialize the new IStorage32object.
5779 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5781 if (newStorage
== 0)
5783 hr
= STG_E_INSUFFICIENTMEMORY
;
5787 hr
= StorageImpl_Construct(
5798 HeapFree(GetProcessHeap(), 0, newStorage
);
5803 * Get an "out" pointer for the caller.
5805 hr
= StorageBaseImpl_QueryInterface(
5806 (IStorage
*)newStorage
,
5807 (REFIID
)&IID_IStorage
,
5810 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
5815 /******************************************************************************
5816 * StgCreateStorageEx [OLE32.@]
5818 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
5820 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
5821 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
5823 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
5825 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5826 return STG_E_INVALIDPARAMETER
;
5829 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
5831 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5832 return STG_E_INVALIDPARAMETER
;
5835 if (stgfmt
== STGFMT_FILE
)
5837 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5838 return STG_E_INVALIDPARAMETER
;
5841 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
5843 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5844 return StgCreateDocfile(pwcsName
, grfMode
, 0, (IStorage
**)ppObjectOpen
);
5847 ERR("Invalid stgfmt argument\n");
5848 return STG_E_INVALIDPARAMETER
;
5851 /******************************************************************************
5852 * StgCreatePropSetStg [OLE32.@]
5854 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
5855 IPropertySetStorage
**ppPropSetStg
)
5859 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, ppPropSetStg
);
5861 hr
= STG_E_INVALIDPARAMETER
;
5863 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
5864 (void**)ppPropSetStg
);
5868 /******************************************************************************
5869 * StgOpenStorageEx [OLE32.@]
5871 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
5873 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
5874 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
5876 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
5878 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5879 return STG_E_INVALIDPARAMETER
;
5885 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5886 return STG_E_INVALIDPARAMETER
;
5888 case STGFMT_STORAGE
:
5891 case STGFMT_DOCFILE
:
5892 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
5894 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5895 return STG_E_INVALIDPARAMETER
;
5897 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5901 WARN("STGFMT_ANY assuming storage\n");
5905 return STG_E_INVALIDPARAMETER
;
5908 return StgOpenStorage(pwcsName
, NULL
, grfMode
, (SNB
)NULL
, 0, (IStorage
**)ppObjectOpen
);
5912 /******************************************************************************
5913 * StgOpenStorage [OLE32.@]
5915 HRESULT WINAPI
StgOpenStorage(
5916 const OLECHAR
*pwcsName
,
5917 IStorage
*pstgPriority
,
5921 IStorage
**ppstgOpen
)
5923 StorageImpl
* newStorage
= 0;
5928 WCHAR fullname
[MAX_PATH
];
5930 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5931 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
5932 snbExclude
, reserved
, ppstgOpen
);
5935 * Perform sanity checks
5939 hr
= STG_E_INVALIDNAME
;
5945 hr
= STG_E_INVALIDPOINTER
;
5951 hr
= STG_E_INVALIDPARAMETER
;
5955 if (grfMode
& STGM_PRIORITY
)
5957 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
5958 return STG_E_INVALIDFLAG
;
5959 if (grfMode
& STGM_DELETEONRELEASE
)
5960 return STG_E_INVALIDFUNCTION
;
5961 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
5962 return STG_E_INVALIDFLAG
;
5963 grfMode
&= ~0xf0; /* remove the existing sharing mode */
5964 grfMode
|= STGM_SHARE_DENY_NONE
;
5966 /* STGM_PRIORITY stops other IStorage objects on the same file from
5967 * committing until the STGM_PRIORITY IStorage is closed. it also
5968 * stops non-transacted mode StgOpenStorage calls with write access from
5969 * succeeding. obviously, both of these cannot be achieved through just
5970 * file share flags */
5971 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5975 * Validate the sharing mode
5977 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
5978 switch(STGM_SHARE_MODE(grfMode
))
5980 case STGM_SHARE_EXCLUSIVE
:
5981 case STGM_SHARE_DENY_WRITE
:
5984 hr
= STG_E_INVALIDFLAG
;
5989 * Validate the STGM flags
5991 if ( FAILED( validateSTGM(grfMode
) ) ||
5992 (grfMode
&STGM_CREATE
))
5994 hr
= STG_E_INVALIDFLAG
;
5998 /* shared reading requires transacted mode */
5999 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
6000 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
6001 !(grfMode
&STGM_TRANSACTED
) )
6003 hr
= STG_E_INVALIDFLAG
;
6008 * Interpret the STGM value grfMode
6010 shareMode
= GetShareModeFromSTGM(grfMode
);
6011 accessMode
= GetAccessModeFromSTGM(grfMode
);
6014 * Initialize the "out" parameter.
6018 hFile
= CreateFileW( pwcsName
,
6023 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
6026 if (hFile
==INVALID_HANDLE_VALUE
)
6028 DWORD last_error
= GetLastError();
6034 case ERROR_FILE_NOT_FOUND
:
6035 hr
= STG_E_FILENOTFOUND
;
6038 case ERROR_PATH_NOT_FOUND
:
6039 hr
= STG_E_PATHNOTFOUND
;
6042 case ERROR_ACCESS_DENIED
:
6043 case ERROR_WRITE_PROTECT
:
6044 hr
= STG_E_ACCESSDENIED
;
6047 case ERROR_SHARING_VIOLATION
:
6048 hr
= STG_E_SHAREVIOLATION
;
6059 * Refuse to open the file if it's too small to be a structured storage file
6060 * FIXME: verify the file when reading instead of here
6062 if (GetFileSize(hFile
, NULL
) < 0x100)
6065 hr
= STG_E_FILEALREADYEXISTS
;
6070 * Allocate and initialize the new IStorage32object.
6072 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
6074 if (newStorage
== 0)
6076 hr
= STG_E_INSUFFICIENTMEMORY
;
6080 /* Initialize the storage */
6081 hr
= StorageImpl_Construct(
6092 HeapFree(GetProcessHeap(), 0, newStorage
);
6094 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6096 if(hr
== STG_E_INVALIDHEADER
)
6097 hr
= STG_E_FILEALREADYEXISTS
;
6101 /* prepare the file name string given in lieu of the root property name */
6102 GetFullPathNameW(pwcsName
, MAX_PATH
, fullname
, NULL
);
6103 memcpy(newStorage
->filename
, fullname
, PROPERTY_NAME_BUFFER_LEN
);
6104 newStorage
->filename
[PROPERTY_NAME_BUFFER_LEN
-1] = '\0';
6107 * Get an "out" pointer for the caller.
6109 hr
= StorageBaseImpl_QueryInterface(
6110 (IStorage
*)newStorage
,
6111 (REFIID
)&IID_IStorage
,
6115 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
6119 /******************************************************************************
6120 * StgCreateDocfileOnILockBytes [OLE32.@]
6122 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
6126 IStorage
** ppstgOpen
)
6128 StorageImpl
* newStorage
= 0;
6132 * Validate the parameters
6134 if ((ppstgOpen
== 0) || (plkbyt
== 0))
6135 return STG_E_INVALIDPOINTER
;
6138 * Allocate and initialize the new IStorage object.
6140 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
6142 if (newStorage
== 0)
6143 return STG_E_INSUFFICIENTMEMORY
;
6145 hr
= StorageImpl_Construct(
6156 HeapFree(GetProcessHeap(), 0, newStorage
);
6161 * Get an "out" pointer for the caller.
6163 hr
= StorageBaseImpl_QueryInterface(
6164 (IStorage
*)newStorage
,
6165 (REFIID
)&IID_IStorage
,
6171 /******************************************************************************
6172 * StgOpenStorageOnILockBytes [OLE32.@]
6174 HRESULT WINAPI
StgOpenStorageOnILockBytes(
6176 IStorage
*pstgPriority
,
6180 IStorage
**ppstgOpen
)
6182 StorageImpl
* newStorage
= 0;
6186 * Perform a sanity check
6188 if ((plkbyt
== 0) || (ppstgOpen
== 0))
6189 return STG_E_INVALIDPOINTER
;
6192 * Validate the STGM flags
6194 if ( FAILED( validateSTGM(grfMode
) ))
6195 return STG_E_INVALIDFLAG
;
6198 * Initialize the "out" parameter.
6203 * Allocate and initialize the new IStorage object.
6205 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
6207 if (newStorage
== 0)
6208 return STG_E_INSUFFICIENTMEMORY
;
6210 hr
= StorageImpl_Construct(
6221 HeapFree(GetProcessHeap(), 0, newStorage
);
6226 * Get an "out" pointer for the caller.
6228 hr
= StorageBaseImpl_QueryInterface(
6229 (IStorage
*)newStorage
,
6230 (REFIID
)&IID_IStorage
,
6236 /******************************************************************************
6237 * StgSetTimes [ole32.@]
6238 * StgSetTimes [OLE32.@]
6242 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
6243 FILETIME
const *patime
, FILETIME
const *pmtime
)
6245 IStorage
*stg
= NULL
;
6248 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
6250 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
6254 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
6255 IStorage_Release(stg
);
6261 /******************************************************************************
6262 * StgIsStorageILockBytes [OLE32.@]
6264 * Determines if the ILockBytes contains a storage object.
6266 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
6269 ULARGE_INTEGER offset
;
6271 offset
.u
.HighPart
= 0;
6272 offset
.u
.LowPart
= 0;
6274 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
6276 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
6282 /******************************************************************************
6283 * WriteClassStg [OLE32.@]
6285 * This method will store the specified CLSID in the specified storage object
6287 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
6292 return E_INVALIDARG
;
6294 hRes
= IStorage_SetClass(pStg
, rclsid
);
6299 /***********************************************************************
6300 * ReadClassStg (OLE32.@)
6302 * This method reads the CLSID previously written to a storage object with
6303 * the WriteClassStg.
6306 * pstg [I] IStorage pointer
6307 * pclsid [O] Pointer to where the CLSID is written
6311 * Failure: HRESULT code.
6313 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
6318 TRACE("(%p, %p)\n", pstg
, pclsid
);
6320 if(!pstg
|| !pclsid
)
6321 return E_INVALIDARG
;
6324 * read a STATSTG structure (contains the clsid) from the storage
6326 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_DEFAULT
);
6329 *pclsid
=pstatstg
.clsid
;
6334 /***********************************************************************
6335 * OleLoadFromStream (OLE32.@)
6337 * This function loads an object from stream
6339 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
6343 LPPERSISTSTREAM xstm
;
6345 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
6347 res
=ReadClassStm(pStm
,&clsid
);
6348 if (!SUCCEEDED(res
))
6350 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
6351 if (!SUCCEEDED(res
))
6353 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
6354 if (!SUCCEEDED(res
)) {
6355 IUnknown_Release((IUnknown
*)*ppvObj
);
6358 res
=IPersistStream_Load(xstm
,pStm
);
6359 IPersistStream_Release(xstm
);
6360 /* FIXME: all refcounts ok at this point? I think they should be:
6363 * xstm : 0 (released)
6368 /***********************************************************************
6369 * OleSaveToStream (OLE32.@)
6371 * This function saves an object with the IPersistStream interface on it
6372 * to the specified stream.
6374 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
6380 TRACE("(%p,%p)\n",pPStm
,pStm
);
6382 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
6384 if (SUCCEEDED(res
)){
6386 res
=WriteClassStm(pStm
,&clsid
);
6390 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
6393 TRACE("Finished Save\n");
6397 /****************************************************************************
6398 * This method validate a STGM parameter that can contain the values below
6400 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6401 * The stgm values contained in 0xffff0000 are bitmasks.
6403 * STGM_DIRECT 0x00000000
6404 * STGM_TRANSACTED 0x00010000
6405 * STGM_SIMPLE 0x08000000
6407 * STGM_READ 0x00000000
6408 * STGM_WRITE 0x00000001
6409 * STGM_READWRITE 0x00000002
6411 * STGM_SHARE_DENY_NONE 0x00000040
6412 * STGM_SHARE_DENY_READ 0x00000030
6413 * STGM_SHARE_DENY_WRITE 0x00000020
6414 * STGM_SHARE_EXCLUSIVE 0x00000010
6416 * STGM_PRIORITY 0x00040000
6417 * STGM_DELETEONRELEASE 0x04000000
6419 * STGM_CREATE 0x00001000
6420 * STGM_CONVERT 0x00020000
6421 * STGM_FAILIFTHERE 0x00000000
6423 * STGM_NOSCRATCH 0x00100000
6424 * STGM_NOSNAPSHOT 0x00200000
6426 static HRESULT
validateSTGM(DWORD stgm
)
6428 DWORD access
= STGM_ACCESS_MODE(stgm
);
6429 DWORD share
= STGM_SHARE_MODE(stgm
);
6430 DWORD create
= STGM_CREATE_MODE(stgm
);
6432 if (stgm
&~STGM_KNOWN_FLAGS
)
6434 ERR("unknown flags %08x\n", stgm
);
6442 case STGM_READWRITE
:
6450 case STGM_SHARE_DENY_NONE
:
6451 case STGM_SHARE_DENY_READ
:
6452 case STGM_SHARE_DENY_WRITE
:
6453 case STGM_SHARE_EXCLUSIVE
:
6462 case STGM_FAILIFTHERE
:
6469 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6471 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
6475 * STGM_CREATE | STGM_CONVERT
6476 * if both are false, STGM_FAILIFTHERE is set to TRUE
6478 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
6482 * STGM_NOSCRATCH requires STGM_TRANSACTED
6484 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
6488 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6489 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6491 if ( (stgm
& STGM_NOSNAPSHOT
) &&
6492 (!(stgm
& STGM_TRANSACTED
) ||
6493 share
== STGM_SHARE_EXCLUSIVE
||
6494 share
== STGM_SHARE_DENY_WRITE
) )
6500 /****************************************************************************
6501 * GetShareModeFromSTGM
6503 * This method will return a share mode flag from a STGM value.
6504 * The STGM value is assumed valid.
6506 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
6508 switch (STGM_SHARE_MODE(stgm
))
6510 case STGM_SHARE_DENY_NONE
:
6511 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6512 case STGM_SHARE_DENY_READ
:
6513 return FILE_SHARE_WRITE
;
6514 case STGM_SHARE_DENY_WRITE
:
6515 return FILE_SHARE_READ
;
6516 case STGM_SHARE_EXCLUSIVE
:
6519 ERR("Invalid share mode!\n");
6524 /****************************************************************************
6525 * GetAccessModeFromSTGM
6527 * This method will return an access mode flag from a STGM value.
6528 * The STGM value is assumed valid.
6530 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
6532 switch (STGM_ACCESS_MODE(stgm
))
6535 return GENERIC_READ
;
6537 case STGM_READWRITE
:
6538 return GENERIC_READ
| GENERIC_WRITE
;
6540 ERR("Invalid access mode!\n");
6545 /****************************************************************************
6546 * GetCreationModeFromSTGM
6548 * This method will return a creation mode flag from a STGM value.
6549 * The STGM value is assumed valid.
6551 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
6553 switch(STGM_CREATE_MODE(stgm
))
6556 return CREATE_ALWAYS
;
6558 FIXME("STGM_CONVERT not implemented!\n");
6560 case STGM_FAILIFTHERE
:
6563 ERR("Invalid create mode!\n");
6569 /*************************************************************************
6570 * OLECONVERT_LoadOLE10 [Internal]
6572 * Loads the OLE10 STREAM to memory
6575 * pOleStream [I] The OLESTREAM
6576 * pData [I] Data Structure for the OLESTREAM Data
6580 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6581 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6584 * This function is used by OleConvertOLESTREAMToIStorage only.
6586 * Memory allocated for pData must be freed by the caller
6588 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
6591 HRESULT hRes
= S_OK
;
6595 pData
->pData
= NULL
;
6596 pData
->pstrOleObjFileName
= (CHAR
*) NULL
;
6598 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
6601 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6602 if(dwSize
!= sizeof(pData
->dwOleID
))
6604 hRes
= CONVERT10_E_OLESTREAM_GET
;
6606 else if(pData
->dwOleID
!= OLESTREAM_ID
)
6608 hRes
= CONVERT10_E_OLESTREAM_FMT
;
6619 /* Get the TypeID...more info needed for this field */
6620 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6621 if(dwSize
!= sizeof(pData
->dwTypeID
))
6623 hRes
= CONVERT10_E_OLESTREAM_GET
;
6628 if(pData
->dwTypeID
!= 0)
6630 /* Get the length of the OleTypeName */
6631 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6632 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6634 hRes
= CONVERT10_E_OLESTREAM_GET
;
6639 if(pData
->dwOleTypeNameLength
> 0)
6641 /* Get the OleTypeName */
6642 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6643 if(dwSize
!= pData
->dwOleTypeNameLength
)
6645 hRes
= CONVERT10_E_OLESTREAM_GET
;
6651 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
6652 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
6654 hRes
= CONVERT10_E_OLESTREAM_GET
;
6658 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
6659 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
6660 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
6661 if(pData
->pstrOleObjFileName
)
6663 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)(pData
->pstrOleObjFileName
),pData
->dwOleObjFileNameLength
);
6664 if(dwSize
!= pData
->dwOleObjFileNameLength
)
6666 hRes
= CONVERT10_E_OLESTREAM_GET
;
6670 hRes
= CONVERT10_E_OLESTREAM_GET
;
6675 /* Get the Width of the Metafile */
6676 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6677 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6679 hRes
= CONVERT10_E_OLESTREAM_GET
;
6683 /* Get the Height of the Metafile */
6684 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6685 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6687 hRes
= CONVERT10_E_OLESTREAM_GET
;
6693 /* Get the Length of the Data */
6694 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6695 if(dwSize
!= sizeof(pData
->dwDataLength
))
6697 hRes
= CONVERT10_E_OLESTREAM_GET
;
6701 if(hRes
== S_OK
) /* I don't know what is this 8 byts information is we have to figure out */
6703 if(!bStrem1
) /* if it is a second OLE stream data */
6705 pData
->dwDataLength
-= 8;
6706 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)(pData
->strUnknown
), sizeof(pData
->strUnknown
));
6707 if(dwSize
!= sizeof(pData
->strUnknown
))
6709 hRes
= CONVERT10_E_OLESTREAM_GET
;
6715 if(pData
->dwDataLength
> 0)
6717 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
6719 /* Get Data (ex. IStorage, Metafile, or BMP) */
6722 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
6723 if(dwSize
!= pData
->dwDataLength
)
6725 hRes
= CONVERT10_E_OLESTREAM_GET
;
6730 hRes
= CONVERT10_E_OLESTREAM_GET
;
6739 /*************************************************************************
6740 * OLECONVERT_SaveOLE10 [Internal]
6742 * Saves the OLE10 STREAM From memory
6745 * pData [I] Data Structure for the OLESTREAM Data
6746 * pOleStream [I] The OLESTREAM to save
6750 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6753 * This function is used by OleConvertIStorageToOLESTREAM only.
6756 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
6759 HRESULT hRes
= S_OK
;
6763 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6764 if(dwSize
!= sizeof(pData
->dwOleID
))
6766 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6771 /* Set the TypeID */
6772 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6773 if(dwSize
!= sizeof(pData
->dwTypeID
))
6775 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6779 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
6781 /* Set the Length of the OleTypeName */
6782 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6783 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6785 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6790 if(pData
->dwOleTypeNameLength
> 0)
6792 /* Set the OleTypeName */
6793 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6794 if(dwSize
!= pData
->dwOleTypeNameLength
)
6796 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6803 /* Set the width of the Metafile */
6804 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6805 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6807 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6813 /* Set the height of the Metafile */
6814 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6815 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6817 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6823 /* Set the length of the Data */
6824 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6825 if(dwSize
!= sizeof(pData
->dwDataLength
))
6827 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6833 if(pData
->dwDataLength
> 0)
6835 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6836 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
6837 if(dwSize
!= pData
->dwDataLength
)
6839 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6847 /*************************************************************************
6848 * OLECONVERT_GetOLE20FromOLE10[Internal]
6850 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6851 * opens it, and copies the content to the dest IStorage for
6852 * OleConvertOLESTREAMToIStorage
6856 * pDestStorage [I] The IStorage to copy the data to
6857 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6858 * nBufferLength [I] The size of the buffer
6867 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, BYTE
*pBuffer
, DWORD nBufferLength
)
6871 IStorage
*pTempStorage
;
6872 DWORD dwNumOfBytesWritten
;
6873 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6874 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6876 /* Create a temp File */
6877 GetTempPathW(MAX_PATH
, wstrTempDir
);
6878 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6879 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
6881 if(hFile
!= INVALID_HANDLE_VALUE
)
6883 /* Write IStorage Data to File */
6884 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
6887 /* Open and copy temp storage to the Dest Storage */
6888 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
6891 hRes
= StorageImpl_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
6892 StorageBaseImpl_Release(pTempStorage
);
6894 DeleteFileW(wstrTempFile
);
6899 /*************************************************************************
6900 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6902 * Saves the OLE10 STREAM From memory
6905 * pStorage [I] The Src IStorage to copy
6906 * pData [I] The Dest Memory to write to.
6909 * The size in bytes allocated for pData
6912 * Memory allocated for pData must be freed by the caller
6914 * Used by OleConvertIStorageToOLESTREAM only.
6917 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
6921 DWORD nDataLength
= 0;
6922 IStorage
*pTempStorage
;
6923 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6924 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6928 /* Create temp Storage */
6929 GetTempPathW(MAX_PATH
, wstrTempDir
);
6930 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6931 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
6935 /* Copy Src Storage to the Temp Storage */
6936 StorageImpl_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
6937 StorageBaseImpl_Release(pTempStorage
);
6939 /* Open Temp Storage as a file and copy to memory */
6940 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
6941 if(hFile
!= INVALID_HANDLE_VALUE
)
6943 nDataLength
= GetFileSize(hFile
, NULL
);
6944 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
6945 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
6948 DeleteFileW(wstrTempFile
);
6953 /*************************************************************************
6954 * OLECONVERT_CreateOleStream [Internal]
6956 * Creates the "\001OLE" stream in the IStorage if necessary.
6959 * pStorage [I] Dest storage to create the stream in
6965 * This function is used by OleConvertOLESTREAMToIStorage only.
6967 * This stream is still unknown, MS Word seems to have extra data
6968 * but since the data is stored in the OLESTREAM there should be
6969 * no need to recreate the stream. If the stream is manually
6970 * deleted it will create it with this default data.
6973 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
6977 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
6978 BYTE pOleStreamHeader
[] =
6980 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6981 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6982 0x00, 0x00, 0x00, 0x00
6985 /* Create stream if not present */
6986 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
6987 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
6991 /* Write default Data */
6992 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
6993 IStream_Release(pStream
);
6997 /* write a string to a stream, preceded by its length */
6998 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
7005 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
7006 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
7011 str
= CoTaskMemAlloc( len
);
7012 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
7013 r
= IStream_Write( stm
, str
, len
, NULL
);
7014 CoTaskMemFree( str
);
7018 /* read a string preceded by its length from a stream */
7019 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
7022 DWORD len
, count
= 0;
7026 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
7029 if( count
!= sizeof(len
) )
7030 return E_OUTOFMEMORY
;
7032 TRACE("%d bytes\n",len
);
7034 str
= CoTaskMemAlloc( len
);
7036 return E_OUTOFMEMORY
;
7038 r
= IStream_Read( stm
, str
, len
, &count
);
7043 CoTaskMemFree( str
);
7044 return E_OUTOFMEMORY
;
7047 TRACE("Read string %s\n",debugstr_an(str
,len
));
7049 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
7050 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
7052 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
7053 CoTaskMemFree( str
);
7061 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
7062 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
7066 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7068 static const BYTE unknown1
[12] =
7069 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7070 0xFF, 0xFF, 0xFF, 0xFF};
7071 static const BYTE unknown2
[16] =
7072 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7073 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7075 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
7076 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
7077 debugstr_w(szProgIDName
));
7079 /* Create a CompObj stream if it doesn't exist */
7080 r
= IStorage_CreateStream(pstg
, szwStreamName
,
7081 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
7085 /* Write CompObj Structure to stream */
7086 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
7088 if( SUCCEEDED( r
) )
7089 r
= WriteClassStm( pstm
, clsid
);
7091 if( SUCCEEDED( r
) )
7092 r
= STREAM_WriteString( pstm
, lpszUserType
);
7093 if( SUCCEEDED( r
) )
7094 r
= STREAM_WriteString( pstm
, szClipName
);
7095 if( SUCCEEDED( r
) )
7096 r
= STREAM_WriteString( pstm
, szProgIDName
);
7097 if( SUCCEEDED( r
) )
7098 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
7100 IStream_Release( pstm
);
7105 /***********************************************************************
7106 * WriteFmtUserTypeStg (OLE32.@)
7108 HRESULT WINAPI
WriteFmtUserTypeStg(
7109 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
7112 WCHAR szwClipName
[0x40];
7113 CLSID clsid
= CLSID_NULL
;
7114 LPWSTR wstrProgID
= NULL
;
7117 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
7119 /* get the clipboard format name */
7120 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
) );
7123 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
7125 /* FIXME: There's room to save a CLSID and its ProgID, but
7126 the CLSID is not looked up in the registry and in all the
7127 tests I wrote it was CLSID_NULL. Where does it come from?
7130 /* get the real program ID. This may fail, but that's fine */
7131 ProgIDFromCLSID(&clsid
, &wstrProgID
);
7133 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
7135 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
7136 lpszUserType
, szwClipName
, wstrProgID
);
7138 CoTaskMemFree(wstrProgID
);
7144 /******************************************************************************
7145 * ReadFmtUserTypeStg [OLE32.@]
7147 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
7151 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
7152 unsigned char unknown1
[12];
7153 unsigned char unknown2
[16];
7155 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
7158 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
7160 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
7161 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
7164 WARN("Failed to open stream r = %08x\n", r
);
7168 /* read the various parts of the structure */
7169 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
7170 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
7172 r
= ReadClassStm( stm
, &clsid
);
7176 r
= STREAM_ReadString( stm
, &szCLSIDName
);
7180 r
= STREAM_ReadString( stm
, &szOleTypeName
);
7184 r
= STREAM_ReadString( stm
, &szProgIDName
);
7188 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
7189 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
7192 /* ok, success... now we just need to store what we found */
7194 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
7195 CoTaskMemFree( szOleTypeName
);
7197 if( lplpszUserType
)
7198 *lplpszUserType
= szCLSIDName
;
7199 CoTaskMemFree( szProgIDName
);
7202 IStream_Release( stm
);
7208 /*************************************************************************
7209 * OLECONVERT_CreateCompObjStream [Internal]
7211 * Creates a "\001CompObj" is the destination IStorage if necessary.
7214 * pStorage [I] The dest IStorage to create the CompObj Stream
7216 * strOleTypeName [I] The ProgID
7220 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7223 * This function is used by OleConvertOLESTREAMToIStorage only.
7225 * The stream data is stored in the OLESTREAM and there should be
7226 * no need to recreate the stream. If the stream is manually
7227 * deleted it will attempt to create it by querying the registry.
7231 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
7234 HRESULT hStorageRes
, hRes
= S_OK
;
7235 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
7236 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7237 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
7239 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7240 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
7242 /* Initialize the CompObj structure */
7243 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
7244 memcpy(&(IStorageCompObj
.byUnknown1
), pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
7245 memcpy(&(IStorageCompObj
.byUnknown2
), pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
7248 /* Create a CompObj stream if it doesn't exist */
7249 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7250 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7251 if(hStorageRes
== S_OK
)
7253 /* copy the OleTypeName to the compobj struct */
7254 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
7255 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
7257 /* copy the OleTypeName to the compobj struct */
7258 /* Note: in the test made, these were Identical */
7259 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
7260 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
7263 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
7264 bufferW
, OLESTREAM_MAX_STR_LEN
);
7265 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
7271 /* Get the CLSID Default Name from the Registry */
7272 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
7273 if(hErr
== ERROR_SUCCESS
)
7275 char strTemp
[OLESTREAM_MAX_STR_LEN
];
7276 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
7277 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
7278 if(hErr
== ERROR_SUCCESS
)
7280 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
7286 /* Write CompObj Structure to stream */
7287 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
7289 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
7291 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
7292 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
7294 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
7296 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
7297 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
7299 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
7301 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
7302 if(IStorageCompObj
.dwProgIDNameLength
> 0)
7304 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
7306 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
7307 IStream_Release(pStream
);
7313 /*************************************************************************
7314 * OLECONVERT_CreateOlePresStream[Internal]
7316 * Creates the "\002OlePres000" Stream with the Metafile data
7319 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7320 * dwExtentX [I] Width of the Metafile
7321 * dwExtentY [I] Height of the Metafile
7322 * pData [I] Metafile data
7323 * dwDataLength [I] Size of the Metafile data
7327 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7330 * This function is used by OleConvertOLESTREAMToIStorage only.
7333 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
7337 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7338 BYTE pOlePresStreamHeader
[] =
7340 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7341 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7342 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7343 0x00, 0x00, 0x00, 0x00
7346 BYTE pOlePresStreamHeaderEmpty
[] =
7348 0x00, 0x00, 0x00, 0x00,
7349 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7350 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7351 0x00, 0x00, 0x00, 0x00
7354 /* Create the OlePres000 Stream */
7355 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7356 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7361 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
7363 memset(&OlePres
, 0, sizeof(OlePres
));
7364 /* Do we have any metafile data to save */
7365 if(dwDataLength
> 0)
7367 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
7368 nHeaderSize
= sizeof(pOlePresStreamHeader
);
7372 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
7373 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
7375 /* Set width and height of the metafile */
7376 OlePres
.dwExtentX
= dwExtentX
;
7377 OlePres
.dwExtentY
= -dwExtentY
;
7379 /* Set Data and Length */
7380 if(dwDataLength
> sizeof(METAFILEPICT16
))
7382 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
7383 OlePres
.pData
= &(pData
[8]);
7385 /* Save OlePres000 Data to Stream */
7386 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
7387 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
7388 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
7389 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
7390 if(OlePres
.dwSize
> 0)
7392 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
7394 IStream_Release(pStream
);
7398 /*************************************************************************
7399 * OLECONVERT_CreateOle10NativeStream [Internal]
7401 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7404 * pStorage [I] Dest storage to create the stream in
7405 * pData [I] Ole10 Native Data (ex. bmp)
7406 * dwDataLength [I] Size of the Ole10 Native Data
7412 * This function is used by OleConvertOLESTREAMToIStorage only.
7414 * Might need to verify the data and return appropriate error message
7417 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, BYTE
*pData
, DWORD dwDataLength
)
7421 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7423 /* Create the Ole10Native Stream */
7424 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7425 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7429 /* Write info to stream */
7430 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
7431 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
7432 IStream_Release(pStream
);
7437 /*************************************************************************
7438 * OLECONVERT_GetOLE10ProgID [Internal]
7440 * Finds the ProgID (or OleTypeID) from the IStorage
7443 * pStorage [I] The Src IStorage to get the ProgID
7444 * strProgID [I] the ProgID string to get
7445 * dwSize [I] the size of the string
7449 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7452 * This function is used by OleConvertIStorageToOLESTREAM only.
7456 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
7460 LARGE_INTEGER iSeekPos
;
7461 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
7462 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7464 /* Open the CompObj Stream */
7465 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7466 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7470 /*Get the OleType from the CompObj Stream */
7471 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
7472 iSeekPos
.u
.HighPart
= 0;
7474 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7475 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
7476 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
7477 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7478 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
7479 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
7480 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7482 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
7485 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
7487 IStream_Release(pStream
);
7492 LPOLESTR wstrProgID
;
7494 /* Get the OleType from the registry */
7495 REFCLSID clsid
= &(stat
.clsid
);
7496 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
7497 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
7500 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
7507 /*************************************************************************
7508 * OLECONVERT_GetOle10PresData [Internal]
7510 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7513 * pStorage [I] Src IStroage
7514 * pOleStream [I] Dest OleStream Mem Struct
7520 * This function is used by OleConvertIStorageToOLESTREAM only.
7522 * Memory allocated for pData must be freed by the caller
7526 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7531 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7533 /* Initialize Default data for OLESTREAM */
7534 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7535 pOleStreamData
[0].dwTypeID
= 2;
7536 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7537 pOleStreamData
[1].dwTypeID
= 0;
7538 pOleStreamData
[0].dwMetaFileWidth
= 0;
7539 pOleStreamData
[0].dwMetaFileHeight
= 0;
7540 pOleStreamData
[0].pData
= NULL
;
7541 pOleStreamData
[1].pData
= NULL
;
7543 /* Open Ole10Native Stream */
7544 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7545 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7549 /* Read Size and Data */
7550 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
7551 if(pOleStreamData
->dwDataLength
> 0)
7553 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
7554 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
7556 IStream_Release(pStream
);
7562 /*************************************************************************
7563 * OLECONVERT_GetOle20PresData[Internal]
7565 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7568 * pStorage [I] Src IStroage
7569 * pOleStreamData [I] Dest OleStream Mem Struct
7575 * This function is used by OleConvertIStorageToOLESTREAM only.
7577 * Memory allocated for pData must be freed by the caller
7579 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7583 OLECONVERT_ISTORAGE_OLEPRES olePress
;
7584 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7586 /* Initialize Default data for OLESTREAM */
7587 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7588 pOleStreamData
[0].dwTypeID
= 2;
7589 pOleStreamData
[0].dwMetaFileWidth
= 0;
7590 pOleStreamData
[0].dwMetaFileHeight
= 0;
7591 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
7592 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7593 pOleStreamData
[1].dwTypeID
= 0;
7594 pOleStreamData
[1].dwOleTypeNameLength
= 0;
7595 pOleStreamData
[1].strOleTypeName
[0] = 0;
7596 pOleStreamData
[1].dwMetaFileWidth
= 0;
7597 pOleStreamData
[1].dwMetaFileHeight
= 0;
7598 pOleStreamData
[1].pData
= NULL
;
7599 pOleStreamData
[1].dwDataLength
= 0;
7602 /* Open OlePress000 stream */
7603 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7604 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7607 LARGE_INTEGER iSeekPos
;
7608 METAFILEPICT16 MetaFilePict
;
7609 static const char strMetafilePictName
[] = "METAFILEPICT";
7611 /* Set the TypeID for a Metafile */
7612 pOleStreamData
[1].dwTypeID
= 5;
7614 /* Set the OleTypeName to Metafile */
7615 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
7616 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
7618 iSeekPos
.u
.HighPart
= 0;
7619 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
7621 /* Get Presentation Data */
7622 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7623 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
7624 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
7625 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
7627 /*Set width and Height */
7628 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
7629 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
7630 if(olePress
.dwSize
> 0)
7633 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
7635 /* Set MetaFilePict struct */
7636 MetaFilePict
.mm
= 8;
7637 MetaFilePict
.xExt
= olePress
.dwExtentX
;
7638 MetaFilePict
.yExt
= olePress
.dwExtentY
;
7639 MetaFilePict
.hMF
= 0;
7641 /* Get Metafile Data */
7642 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
7643 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
7644 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
7646 IStream_Release(pStream
);
7650 /*************************************************************************
7651 * OleConvertOLESTREAMToIStorage [OLE32.@]
7656 * DVTARGETDEVICE paramenter is not handled
7657 * Still unsure of some mem fields for OLE 10 Stream
7658 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7659 * and "\001OLE" streams
7662 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
7663 LPOLESTREAM pOleStream
,
7665 const DVTARGETDEVICE
* ptd
)
7669 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7671 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
7673 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7677 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7680 if(pstg
== NULL
|| pOleStream
== NULL
)
7682 hRes
= E_INVALIDARG
;
7687 /* Load the OLESTREAM to Memory */
7688 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
7693 /* Load the OLESTREAM to Memory (part 2)*/
7694 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
7700 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
7702 /* Do we have the IStorage Data in the OLESTREAM */
7703 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
7705 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7706 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
7710 /* It must be an original OLE 1.0 source */
7711 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7716 /* It must be an original OLE 1.0 source */
7717 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7720 /* Create CompObj Stream if necessary */
7721 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
7724 /*Create the Ole Stream if necessary */
7725 OLECONVERT_CreateOleStream(pstg
);
7730 /* Free allocated memory */
7731 for(i
=0; i
< 2; i
++)
7733 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7734 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
7735 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
7740 /*************************************************************************
7741 * OleConvertIStorageToOLESTREAM [OLE32.@]
7748 * Still unsure of some mem fields for OLE 10 Stream
7749 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7750 * and "\001OLE" streams.
7753 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
7755 LPOLESTREAM pOleStream
)
7758 HRESULT hRes
= S_OK
;
7760 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7761 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7763 TRACE("%p %p\n", pstg
, pOleStream
);
7765 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7767 if(pstg
== NULL
|| pOleStream
== NULL
)
7769 hRes
= E_INVALIDARG
;
7773 /* Get the ProgID */
7774 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
7775 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
7779 /* Was it originally Ole10 */
7780 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7783 IStream_Release(pStream
);
7784 /* Get Presentation Data for Ole10Native */
7785 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
7789 /* Get Presentation Data (OLE20) */
7790 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
7793 /* Save OLESTREAM */
7794 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
7797 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
7802 /* Free allocated memory */
7803 for(i
=0; i
< 2; i
++)
7805 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7811 /***********************************************************************
7812 * GetConvertStg (OLE32.@)
7814 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
7815 FIXME("unimplemented stub!\n");
7819 /******************************************************************************
7820 * StgIsStorageFile [OLE32.@]
7821 * Verify if the file contains a storage object
7827 * S_OK if file has magic bytes as a storage object
7828 * S_FALSE if file is not storage
7831 StgIsStorageFile(LPCOLESTR fn
)
7837 TRACE("%s\n", debugstr_w(fn
));
7838 hf
= CreateFileW(fn
, GENERIC_READ
,
7839 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
7840 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
7842 if (hf
== INVALID_HANDLE_VALUE
)
7843 return STG_E_FILENOTFOUND
;
7845 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
7847 WARN(" unable to read file\n");
7854 if (bytes_read
!= 8) {
7855 WARN(" too short\n");
7859 if (!memcmp(magic
,STORAGE_magic
,8)) {
7864 WARN(" -> Invalid header.\n");
7868 /***********************************************************************
7869 * WriteClassStm (OLE32.@)
7871 * Writes a CLSID to a stream.
7874 * pStm [I] Stream to write to.
7875 * rclsid [I] CLSID to write.
7879 * Failure: HRESULT code.
7881 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
7883 TRACE("(%p,%p)\n",pStm
,rclsid
);
7885 if (!pStm
|| !rclsid
)
7886 return E_INVALIDARG
;
7888 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
7891 /***********************************************************************
7892 * ReadClassStm (OLE32.@)
7894 * Reads a CLSID from a stream.
7897 * pStm [I] Stream to read from.
7898 * rclsid [O] CLSID to read.
7902 * Failure: HRESULT code.
7904 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
7909 TRACE("(%p,%p)\n",pStm
,pclsid
);
7911 if (!pStm
|| !pclsid
)
7912 return E_INVALIDARG
;
7914 /* clear the output args */
7915 memcpy(pclsid
, &CLSID_NULL
, sizeof(*pclsid
));
7917 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
7922 if (nbByte
!= sizeof(CLSID
))
7923 return STG_E_READFAULT
;