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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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
65 static const char rootPropertyName
[] = "Root Entry";
68 /* OLESTREAM memory structure to use for Get and Put Routines */
69 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
74 DWORD dwOleTypeNameLength
;
75 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
76 CHAR
*pstrOleObjFileName
;
77 DWORD dwOleObjFileNameLength
;
78 DWORD dwMetaFileWidth
;
79 DWORD dwMetaFileHeight
;
80 CHAR strUnknown
[8]; /* don't know what is this 8 byts information in OLE stream. */
83 }OLECONVERT_OLESTREAM_DATA
;
85 /* CompObj Stream structure */
86 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
91 DWORD dwCLSIDNameLength
;
92 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
93 DWORD dwOleTypeNameLength
;
94 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
95 DWORD dwProgIDNameLength
;
96 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
98 }OLECONVERT_ISTORAGE_COMPOBJ
;
101 /* Ole Presention Stream structure */
102 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
110 }OLECONVERT_ISTORAGE_OLEPRES
;
114 /***********************************************************************
115 * Forward declaration of internal functions used by the method DestroyElement
117 static HRESULT
deleteStorageProperty(
118 StorageImpl
*parentStorage
,
119 ULONG foundPropertyIndexToDelete
,
120 StgProperty propertyToDelete
);
122 static HRESULT
deleteStreamProperty(
123 StorageImpl
*parentStorage
,
124 ULONG foundPropertyIndexToDelete
,
125 StgProperty propertyToDelete
);
127 static HRESULT
findPlaceholder(
128 StorageImpl
*storage
,
129 ULONG propertyIndexToStore
,
130 ULONG storagePropertyIndex
,
133 static HRESULT
adjustPropertyChain(
135 StgProperty propertyToDelete
,
136 StgProperty parentProperty
,
137 ULONG parentPropertyId
,
140 /***********************************************************************
141 * Declaration of the functions used to manipulate StgProperty
144 static ULONG
getFreeProperty(
145 StorageImpl
*storage
);
147 static void updatePropertyChain(
148 StorageImpl
*storage
,
149 ULONG newPropertyIndex
,
150 StgProperty newProperty
);
152 static LONG
propertyNameCmp(
153 const OLECHAR
*newProperty
,
154 const OLECHAR
*currentProperty
);
157 /***********************************************************************
158 * Declaration of miscellaneous functions...
160 static HRESULT
validateSTGM(DWORD stgmValue
);
162 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
163 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
164 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
166 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
170 /************************************************************************
171 ** Storage32BaseImpl implementatiion
174 /************************************************************************
175 * Storage32BaseImpl_QueryInterface (IUnknown)
177 * This method implements the common QueryInterface for all IStorage32
178 * implementations contained in this file.
180 * See Windows documentation for more details on IUnknown methods.
182 HRESULT WINAPI
StorageBaseImpl_QueryInterface(
187 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
189 * Perform a sanity check on the parameters.
191 if ( (This
==0) || (ppvObject
==0) )
195 * Initialize the return parameter.
200 * Compare the riid with the interface IDs implemented by this object.
202 if (memcmp(&IID_IUnknown
, riid
, sizeof(IID_IUnknown
)) == 0)
204 *ppvObject
= (IStorage
*)This
;
206 else if (memcmp(&IID_IStorage
, riid
, sizeof(IID_IStorage
)) == 0)
208 *ppvObject
= (IStorage
*)This
;
210 else if (memcmp(&IID_IPropertySetStorage
, riid
, sizeof(IID_IPropertySetStorage
)) == 0)
212 *ppvObject
= (IStorage
*)&This
->pssVtbl
;
216 * Check that we obtained an interface.
219 return E_NOINTERFACE
;
222 * Query Interface always increases the reference count by one when it is
225 IStorage_AddRef(iface
);
230 /************************************************************************
231 * Storage32BaseImpl_AddRef (IUnknown)
233 * This method implements the common AddRef for all IStorage32
234 * implementations contained in this file.
236 * See Windows documentation for more details on IUnknown methods.
238 ULONG WINAPI
StorageBaseImpl_AddRef(
241 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
242 ULONG ref
= InterlockedIncrement(&This
->ref
);
244 TRACE("(%p) AddRef to %ld\n", This
, ref
);
249 /************************************************************************
250 * Storage32BaseImpl_Release (IUnknown)
252 * This method implements the common Release for all IStorage32
253 * implementations contained in this file.
255 * See Windows documentation for more details on IUnknown methods.
257 ULONG WINAPI
StorageBaseImpl_Release(
260 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
262 * Decrease the reference count on this object.
264 ULONG ref
= InterlockedDecrement(&This
->ref
);
266 TRACE("(%p) ReleaseRef to %ld\n", This
, ref
);
269 * If the reference count goes down to 0, perform suicide.
274 * Since we are using a system of base-classes, we want to call the
275 * destructor of the appropriate derived class. To do this, we are
276 * using virtual functions to implement the destructor.
278 This
->v_destructor(This
);
284 /************************************************************************
285 * Storage32BaseImpl_OpenStream (IStorage)
287 * This method will open the specified stream object from the current storage.
289 * See Windows documentation for more details on IStorage methods.
291 HRESULT WINAPI
StorageBaseImpl_OpenStream(
293 const OLECHAR
* pwcsName
, /* [string][in] */
294 void* reserved1
, /* [unique][in] */
295 DWORD grfMode
, /* [in] */
296 DWORD reserved2
, /* [in] */
297 IStream
** ppstm
) /* [out] */
299 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
300 IEnumSTATSTGImpl
* propertyEnumeration
;
301 StgStreamImpl
* newStream
;
302 StgProperty currentProperty
;
303 ULONG foundPropertyIndex
;
304 HRESULT res
= STG_E_UNKNOWN
;
305 DWORD parent_grfMode
;
307 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
308 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
311 * Perform a sanity check on the parameters.
313 if ( (pwcsName
==NULL
) || (ppstm
==0) )
320 * Initialize the out parameter
325 * Validate the STGM flags
327 if ( FAILED( validateSTGM(grfMode
) ))
329 res
= STG_E_INVALIDFLAG
;
336 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
337 (grfMode
& STGM_DELETEONRELEASE
) ||
338 (grfMode
& STGM_TRANSACTED
) )
340 res
= STG_E_INVALIDFUNCTION
;
345 * Check that we're compatible with the parent's storage mode
347 parent_grfMode
= STGM_ACCESS_MODE( This
->ancestorStorage
->base
.openFlags
);
348 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( parent_grfMode
) )
350 res
= STG_E_ACCESSDENIED
;
355 * Create a property enumeration to search the properties
357 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
358 This
->ancestorStorage
,
359 This
->rootPropertySetIndex
);
362 * Search the enumeration for the property with the given name
364 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
370 * Delete the property enumeration since we don't need it anymore
372 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
375 * If it was found, construct the stream object and return a pointer to it.
377 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
378 (currentProperty
.propertyType
==PROPTYPE_STREAM
) )
380 newStream
= StgStreamImpl_Construct(This
, grfMode
, foundPropertyIndex
);
384 newStream
->grfMode
= grfMode
;
385 *ppstm
= (IStream
*)newStream
;
388 * Since we are returning a pointer to the interface, we have to
389 * nail down the reference.
391 IStream_AddRef(*ppstm
);
401 res
= STG_E_FILENOTFOUND
;
405 TRACE("<-- IStream %p\n", *ppstm
);
406 TRACE("<-- %08lx\n", res
);
410 /************************************************************************
411 * Storage32BaseImpl_OpenStorage (IStorage)
413 * This method will open a new storage object from the current storage.
415 * See Windows documentation for more details on IStorage methods.
417 HRESULT WINAPI
StorageBaseImpl_OpenStorage(
419 const OLECHAR
* pwcsName
, /* [string][unique][in] */
420 IStorage
* pstgPriority
, /* [unique][in] */
421 DWORD grfMode
, /* [in] */
422 SNB snbExclude
, /* [unique][in] */
423 DWORD reserved
, /* [in] */
424 IStorage
** ppstg
) /* [out] */
426 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
427 StorageInternalImpl
* newStorage
;
428 IEnumSTATSTGImpl
* propertyEnumeration
;
429 StgProperty currentProperty
;
430 ULONG foundPropertyIndex
;
431 HRESULT res
= STG_E_UNKNOWN
;
432 DWORD parent_grfMode
;
434 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
435 iface
, debugstr_w(pwcsName
), pstgPriority
,
436 grfMode
, snbExclude
, reserved
, ppstg
);
439 * Perform a sanity check on the parameters.
441 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
448 if (snbExclude
!= NULL
)
450 res
= STG_E_INVALIDPARAMETER
;
455 * Validate the STGM flags
457 if ( FAILED( validateSTGM(grfMode
) ))
459 res
= STG_E_INVALIDFLAG
;
466 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
467 (grfMode
& STGM_DELETEONRELEASE
) ||
468 (grfMode
& STGM_PRIORITY
) )
470 res
= STG_E_INVALIDFUNCTION
;
475 * Check that we're compatible with the parent's storage mode
477 parent_grfMode
= STGM_ACCESS_MODE( This
->ancestorStorage
->base
.openFlags
);
478 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( parent_grfMode
) )
480 res
= STG_E_ACCESSDENIED
;
485 * Initialize the out parameter
490 * Create a property enumeration to search the properties
492 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
493 This
->ancestorStorage
,
494 This
->rootPropertySetIndex
);
497 * Search the enumeration for the property with the given name
499 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
505 * Delete the property enumeration since we don't need it anymore
507 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
510 * If it was found, construct the stream object and return a pointer to it.
512 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
513 (currentProperty
.propertyType
==PROPTYPE_STORAGE
) )
516 * Construct a new Storage object
518 newStorage
= StorageInternalImpl_Construct(
519 This
->ancestorStorage
,
525 *ppstg
= (IStorage
*)newStorage
;
528 * Since we are returning a pointer to the interface,
529 * we have to nail down the reference.
531 StorageBaseImpl_AddRef(*ppstg
);
537 res
= STG_E_INSUFFICIENTMEMORY
;
541 res
= STG_E_FILENOTFOUND
;
544 TRACE("<-- %08lx\n", res
);
548 /************************************************************************
549 * Storage32BaseImpl_EnumElements (IStorage)
551 * This method will create an enumerator object that can be used to
552 * retrieve informatino about all the properties in the storage object.
554 * See Windows documentation for more details on IStorage methods.
556 HRESULT WINAPI
StorageBaseImpl_EnumElements(
558 DWORD reserved1
, /* [in] */
559 void* reserved2
, /* [size_is][unique][in] */
560 DWORD reserved3
, /* [in] */
561 IEnumSTATSTG
** ppenum
) /* [out] */
563 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
564 IEnumSTATSTGImpl
* newEnum
;
566 TRACE("(%p, %ld, %p, %ld, %p)\n",
567 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
570 * Perform a sanity check on the parameters.
572 if ( (This
==0) || (ppenum
==0))
576 * Construct the enumerator.
578 newEnum
= IEnumSTATSTGImpl_Construct(
579 This
->ancestorStorage
,
580 This
->rootPropertySetIndex
);
584 *ppenum
= (IEnumSTATSTG
*)newEnum
;
587 * Don't forget to nail down a reference to the new object before
590 IEnumSTATSTG_AddRef(*ppenum
);
595 return E_OUTOFMEMORY
;
598 /************************************************************************
599 * Storage32BaseImpl_Stat (IStorage)
601 * This method will retrieve information about this storage object.
603 * See Windows documentation for more details on IStorage methods.
605 HRESULT WINAPI
StorageBaseImpl_Stat(
607 STATSTG
* pstatstg
, /* [out] */
608 DWORD grfStatFlag
) /* [in] */
610 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
611 StgProperty curProperty
;
613 HRESULT res
= STG_E_UNKNOWN
;
615 TRACE("(%p, %p, %lx)\n",
616 iface
, pstatstg
, grfStatFlag
);
619 * Perform a sanity check on the parameters.
621 if ( (This
==0) || (pstatstg
==0))
628 * Read the information from the property.
630 readSuccessful
= StorageImpl_ReadProperty(
631 This
->ancestorStorage
,
632 This
->rootPropertySetIndex
,
637 StorageUtl_CopyPropertyToSTATSTG(
651 TRACE("<-- STATSTG: pwcsName: %s, type: %ld, cbSize.Low/High: %ld/%ld, grfMode: %08lx, grfLocksSupported: %ld, grfStateBits: %08lx\n", debugstr_w(pstatstg
->pwcsName
), pstatstg
->type
, pstatstg
->cbSize
.u
.LowPart
, pstatstg
->cbSize
.u
.HighPart
, pstatstg
->grfMode
, pstatstg
->grfLocksSupported
, pstatstg
->grfStateBits
);
653 TRACE("<-- %08lx\n", res
);
657 /************************************************************************
658 * Storage32BaseImpl_RenameElement (IStorage)
660 * This method will rename the specified element.
662 * See Windows documentation for more details on IStorage methods.
664 * Implementation notes: The method used to rename consists of creating a clone
665 * of the deleted StgProperty object setting it with the new name and to
666 * perform a DestroyElement of the old StgProperty.
668 HRESULT WINAPI
StorageBaseImpl_RenameElement(
670 const OLECHAR
* pwcsOldName
, /* [in] */
671 const OLECHAR
* pwcsNewName
) /* [in] */
673 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
674 IEnumSTATSTGImpl
* propertyEnumeration
;
675 StgProperty currentProperty
;
676 ULONG foundPropertyIndex
;
678 TRACE("(%p, %s, %s)\n",
679 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
682 * Create a property enumeration to search the properties
684 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
685 This
->rootPropertySetIndex
);
688 * Search the enumeration for the new property name
690 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
694 if (foundPropertyIndex
!= PROPERTY_NULL
)
697 * There is already a property with the new name
699 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
700 return STG_E_FILEALREADYEXISTS
;
703 IEnumSTATSTG_Reset((IEnumSTATSTG
*)propertyEnumeration
);
706 * Search the enumeration for the old property name
708 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
713 * Delete the property enumeration since we don't need it anymore
715 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
717 if (foundPropertyIndex
!= PROPERTY_NULL
)
719 StgProperty renamedProperty
;
720 ULONG renamedPropertyIndex
;
723 * Setup a new property for the renamed property
725 renamedProperty
.sizeOfNameString
=
726 ( lstrlenW(pwcsNewName
)+1 ) * sizeof(WCHAR
);
728 if (renamedProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
729 return STG_E_INVALIDNAME
;
731 strcpyW(renamedProperty
.name
, pwcsNewName
);
733 renamedProperty
.propertyType
= currentProperty
.propertyType
;
734 renamedProperty
.startingBlock
= currentProperty
.startingBlock
;
735 renamedProperty
.size
.u
.LowPart
= currentProperty
.size
.u
.LowPart
;
736 renamedProperty
.size
.u
.HighPart
= currentProperty
.size
.u
.HighPart
;
738 renamedProperty
.previousProperty
= PROPERTY_NULL
;
739 renamedProperty
.nextProperty
= PROPERTY_NULL
;
742 * Bring the dirProperty link in case it is a storage and in which
743 * case the renamed storage elements don't require to be reorganized.
745 renamedProperty
.dirProperty
= currentProperty
.dirProperty
;
747 /* call CoFileTime to get the current time
748 renamedProperty.timeStampS1
749 renamedProperty.timeStampD1
750 renamedProperty.timeStampS2
751 renamedProperty.timeStampD2
752 renamedProperty.propertyUniqueID
756 * Obtain a free property in the property chain
758 renamedPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
761 * Save the new property into the new property spot
763 StorageImpl_WriteProperty(
764 This
->ancestorStorage
,
765 renamedPropertyIndex
,
769 * Find a spot in the property chain for our newly created property.
773 renamedPropertyIndex
,
777 * At this point the renamed property has been inserted in the tree,
778 * now, before to Destroy the old property we must zeroed it's dirProperty
779 * otherwise the DestroyProperty below will zap it all and we do not want
781 * Also, we fake that the old property is a storage so the DestroyProperty
782 * will not do a SetSize(0) on the stream data.
784 * This means that we need to tweek the StgProperty if it is a stream or a
787 StorageImpl_ReadProperty(This
->ancestorStorage
,
791 currentProperty
.dirProperty
= PROPERTY_NULL
;
792 currentProperty
.propertyType
= PROPTYPE_STORAGE
;
793 StorageImpl_WriteProperty(
794 This
->ancestorStorage
,
799 * Invoke Destroy to get rid of the ole property and automatically redo
800 * the linking of it's previous and next members...
802 IStorage_DestroyElement((IStorage
*)This
->ancestorStorage
, pwcsOldName
);
808 * There is no property with the old name
810 return STG_E_FILENOTFOUND
;
816 /************************************************************************
817 * Storage32BaseImpl_CreateStream (IStorage)
819 * This method will create a stream object within this storage
821 * See Windows documentation for more details on IStorage methods.
823 HRESULT WINAPI
StorageBaseImpl_CreateStream(
825 const OLECHAR
* pwcsName
, /* [string][in] */
826 DWORD grfMode
, /* [in] */
827 DWORD reserved1
, /* [in] */
828 DWORD reserved2
, /* [in] */
829 IStream
** ppstm
) /* [out] */
831 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
832 IEnumSTATSTGImpl
* propertyEnumeration
;
833 StgStreamImpl
* newStream
;
834 StgProperty currentProperty
, newStreamProperty
;
835 ULONG foundPropertyIndex
, newPropertyIndex
;
836 DWORD parent_grfMode
;
838 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
839 iface
, debugstr_w(pwcsName
), grfMode
,
840 reserved1
, reserved2
, ppstm
);
843 * Validate parameters
846 return STG_E_INVALIDPOINTER
;
849 return STG_E_INVALIDNAME
;
851 if (reserved1
|| reserved2
)
852 return STG_E_INVALIDPARAMETER
;
855 * Validate the STGM flags
857 if ( FAILED( validateSTGM(grfMode
) ))
858 return STG_E_INVALIDFLAG
;
860 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
861 return STG_E_INVALIDFLAG
;
866 if ((grfMode
& STGM_DELETEONRELEASE
) ||
867 (grfMode
& STGM_TRANSACTED
))
868 return STG_E_INVALIDFUNCTION
;
871 * Check that we're compatible with the parent's storage mode
873 parent_grfMode
= STGM_ACCESS_MODE( This
->ancestorStorage
->base
.openFlags
);
874 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( parent_grfMode
) )
875 return STG_E_ACCESSDENIED
;
878 * Initialize the out parameter
883 * Create a property enumeration to search the properties
885 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
886 This
->rootPropertySetIndex
);
888 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
892 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
894 if (foundPropertyIndex
!= PROPERTY_NULL
)
897 * An element with this name already exists
899 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
901 IStorage_DestroyElement(iface
, pwcsName
);
904 return STG_E_FILEALREADYEXISTS
;
908 * memset the empty property
910 memset(&newStreamProperty
, 0, sizeof(StgProperty
));
912 newStreamProperty
.sizeOfNameString
=
913 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
915 if (newStreamProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
916 return STG_E_INVALIDNAME
;
918 strcpyW(newStreamProperty
.name
, pwcsName
);
920 newStreamProperty
.propertyType
= PROPTYPE_STREAM
;
921 newStreamProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
922 newStreamProperty
.size
.u
.LowPart
= 0;
923 newStreamProperty
.size
.u
.HighPart
= 0;
925 newStreamProperty
.previousProperty
= PROPERTY_NULL
;
926 newStreamProperty
.nextProperty
= PROPERTY_NULL
;
927 newStreamProperty
.dirProperty
= PROPERTY_NULL
;
929 /* call CoFileTime to get the current time
930 newStreamProperty.timeStampS1
931 newStreamProperty.timeStampD1
932 newStreamProperty.timeStampS2
933 newStreamProperty.timeStampD2
936 /* newStreamProperty.propertyUniqueID */
939 * Get a free property or create a new one
941 newPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
944 * Save the new property into the new property spot
946 StorageImpl_WriteProperty(
947 This
->ancestorStorage
,
952 * Find a spot in the property chain for our newly created property.
960 * Open the stream to return it.
962 newStream
= StgStreamImpl_Construct(This
, grfMode
, newPropertyIndex
);
966 *ppstm
= (IStream
*)newStream
;
969 * Since we are returning a pointer to the interface, we have to nail down
972 IStream_AddRef(*ppstm
);
976 return STG_E_INSUFFICIENTMEMORY
;
982 /************************************************************************
983 * Storage32BaseImpl_SetClass (IStorage)
985 * This method will write the specified CLSID in the property of this
988 * See Windows documentation for more details on IStorage methods.
990 HRESULT WINAPI
StorageBaseImpl_SetClass(
992 REFCLSID clsid
) /* [in] */
994 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
995 HRESULT hRes
= E_FAIL
;
996 StgProperty curProperty
;
999 TRACE("(%p, %p)\n", iface
, clsid
);
1001 success
= StorageImpl_ReadProperty(This
->ancestorStorage
,
1002 This
->rootPropertySetIndex
,
1006 curProperty
.propertyUniqueID
= *clsid
;
1008 success
= StorageImpl_WriteProperty(This
->ancestorStorage
,
1009 This
->rootPropertySetIndex
,
1018 /************************************************************************
1019 ** Storage32Impl implementation
1022 /************************************************************************
1023 * Storage32Impl_CreateStorage (IStorage)
1025 * This method will create the storage object within the provided storage.
1027 * See Windows documentation for more details on IStorage methods.
1029 HRESULT WINAPI
StorageImpl_CreateStorage(
1031 const OLECHAR
*pwcsName
, /* [string][in] */
1032 DWORD grfMode
, /* [in] */
1033 DWORD reserved1
, /* [in] */
1034 DWORD reserved2
, /* [in] */
1035 IStorage
**ppstg
) /* [out] */
1037 StorageImpl
* const This
=(StorageImpl
*)iface
;
1039 IEnumSTATSTGImpl
*propertyEnumeration
;
1040 StgProperty currentProperty
;
1041 StgProperty newProperty
;
1042 ULONG foundPropertyIndex
;
1043 ULONG newPropertyIndex
;
1045 DWORD parent_grfMode
;
1047 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1048 iface
, debugstr_w(pwcsName
), grfMode
,
1049 reserved1
, reserved2
, ppstg
);
1052 * Validate parameters
1055 return STG_E_INVALIDPOINTER
;
1058 return STG_E_INVALIDNAME
;
1061 * Validate the STGM flags
1063 if ( FAILED( validateSTGM(grfMode
) ) ||
1064 (grfMode
& STGM_DELETEONRELEASE
) )
1065 return STG_E_INVALIDFLAG
;
1068 * Check that we're compatible with the parent's storage mode
1070 parent_grfMode
= STGM_ACCESS_MODE( This
->base
.ancestorStorage
->base
.openFlags
);
1071 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( parent_grfMode
) )
1072 return STG_E_ACCESSDENIED
;
1075 * Initialize the out parameter
1080 * Create a property enumeration and search the properties
1082 propertyEnumeration
= IEnumSTATSTGImpl_Construct( This
->base
.ancestorStorage
,
1083 This
->base
.rootPropertySetIndex
);
1085 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
1088 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1090 if (foundPropertyIndex
!= PROPERTY_NULL
)
1093 * An element with this name already exists
1095 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
1096 IStorage_DestroyElement(iface
, pwcsName
);
1098 return STG_E_FILEALREADYEXISTS
;
1102 * memset the empty property
1104 memset(&newProperty
, 0, sizeof(StgProperty
));
1106 newProperty
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1108 if (newProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
1109 return STG_E_INVALIDNAME
;
1111 strcpyW(newProperty
.name
, pwcsName
);
1113 newProperty
.propertyType
= PROPTYPE_STORAGE
;
1114 newProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
1115 newProperty
.size
.u
.LowPart
= 0;
1116 newProperty
.size
.u
.HighPart
= 0;
1118 newProperty
.previousProperty
= PROPERTY_NULL
;
1119 newProperty
.nextProperty
= PROPERTY_NULL
;
1120 newProperty
.dirProperty
= PROPERTY_NULL
;
1122 /* call CoFileTime to get the current time
1123 newProperty.timeStampS1
1124 newProperty.timeStampD1
1125 newProperty.timeStampS2
1126 newProperty.timeStampD2
1129 /* newStorageProperty.propertyUniqueID */
1132 * Obtain a free property in the property chain
1134 newPropertyIndex
= getFreeProperty(This
->base
.ancestorStorage
);
1137 * Save the new property into the new property spot
1139 StorageImpl_WriteProperty(
1140 This
->base
.ancestorStorage
,
1145 * Find a spot in the property chain for our newly created property.
1147 updatePropertyChain(
1153 * Open it to get a pointer to return.
1155 hr
= IStorage_OpenStorage(
1157 (const OLECHAR
*)pwcsName
,
1164 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1174 /***************************************************************************
1178 * Get a free property or create a new one.
1180 static ULONG
getFreeProperty(
1181 StorageImpl
*storage
)
1183 ULONG currentPropertyIndex
= 0;
1184 ULONG newPropertyIndex
= PROPERTY_NULL
;
1185 BOOL readSuccessful
= TRUE
;
1186 StgProperty currentProperty
;
1191 * Start by reading the root property
1193 readSuccessful
= StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1194 currentPropertyIndex
,
1198 if (currentProperty
.sizeOfNameString
== 0)
1201 * The property existis and is available, we found it.
1203 newPropertyIndex
= currentPropertyIndex
;
1209 * We exhausted the property list, we will create more space below
1211 newPropertyIndex
= currentPropertyIndex
;
1213 currentPropertyIndex
++;
1215 } while (newPropertyIndex
== PROPERTY_NULL
);
1218 * grow the property chain
1220 if (! readSuccessful
)
1222 StgProperty emptyProperty
;
1223 ULARGE_INTEGER newSize
;
1224 ULONG propertyIndex
;
1225 ULONG lastProperty
= 0;
1226 ULONG blockCount
= 0;
1229 * obtain the new count of property blocks
1231 blockCount
= BlockChainStream_GetCount(
1232 storage
->base
.ancestorStorage
->rootBlockChain
)+1;
1235 * initialize the size used by the property stream
1237 newSize
.u
.HighPart
= 0;
1238 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1241 * add a property block to the property chain
1243 BlockChainStream_SetSize(storage
->base
.ancestorStorage
->rootBlockChain
, newSize
);
1246 * memset the empty property in order to initialize the unused newly
1249 memset(&emptyProperty
, 0, sizeof(StgProperty
));
1254 lastProperty
= storage
->bigBlockSize
/ PROPSET_BLOCK_SIZE
* blockCount
;
1257 propertyIndex
= newPropertyIndex
;
1258 propertyIndex
< lastProperty
;
1261 StorageImpl_WriteProperty(
1262 storage
->base
.ancestorStorage
,
1268 return newPropertyIndex
;
1271 /****************************************************************************
1275 * Case insensitive comparaison of StgProperty.name by first considering
1278 * Returns <0 when newPrpoerty < currentProperty
1279 * >0 when newPrpoerty > currentProperty
1280 * 0 when newPrpoerty == currentProperty
1282 static LONG
propertyNameCmp(
1283 const OLECHAR
*newProperty
,
1284 const OLECHAR
*currentProperty
)
1286 LONG diff
= lstrlenW(newProperty
) - lstrlenW(currentProperty
);
1291 * We compare the string themselves only when they are of the same length
1293 diff
= lstrcmpiW( newProperty
, currentProperty
);
1299 /****************************************************************************
1303 * Properly link this new element in the property chain.
1305 static void updatePropertyChain(
1306 StorageImpl
*storage
,
1307 ULONG newPropertyIndex
,
1308 StgProperty newProperty
)
1310 StgProperty currentProperty
;
1313 * Read the root property
1315 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1316 storage
->base
.rootPropertySetIndex
,
1319 if (currentProperty
.dirProperty
!= PROPERTY_NULL
)
1322 * The root storage contains some element, therefore, start the research
1323 * for the appropriate location.
1326 ULONG current
, next
, previous
, currentPropertyId
;
1329 * Keep the StgProperty sequence number of the storage first property
1331 currentPropertyId
= currentProperty
.dirProperty
;
1336 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1337 currentProperty
.dirProperty
,
1340 previous
= currentProperty
.previousProperty
;
1341 next
= currentProperty
.nextProperty
;
1342 current
= currentPropertyId
;
1346 LONG diff
= propertyNameCmp( newProperty
.name
, currentProperty
.name
);
1350 if (previous
!= PROPERTY_NULL
)
1352 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1359 currentProperty
.previousProperty
= newPropertyIndex
;
1360 StorageImpl_WriteProperty(storage
->base
.ancestorStorage
,
1368 if (next
!= PROPERTY_NULL
)
1370 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1377 currentProperty
.nextProperty
= newPropertyIndex
;
1378 StorageImpl_WriteProperty(storage
->base
.ancestorStorage
,
1387 * Trying to insert an item with the same name in the
1388 * subtree structure.
1393 previous
= currentProperty
.previousProperty
;
1394 next
= currentProperty
.nextProperty
;
1400 * The root storage is empty, link the new property to it's dir property
1402 currentProperty
.dirProperty
= newPropertyIndex
;
1403 StorageImpl_WriteProperty(storage
->base
.ancestorStorage
,
1404 storage
->base
.rootPropertySetIndex
,
1410 /*************************************************************************
1413 HRESULT WINAPI
StorageImpl_CopyTo(
1415 DWORD ciidExclude
, /* [in] */
1416 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1417 SNB snbExclude
, /* [unique][in] */
1418 IStorage
* pstgDest
) /* [unique][in] */
1420 IEnumSTATSTG
*elements
= 0;
1421 STATSTG curElement
, strStat
;
1423 IStorage
*pstgTmp
, *pstgChild
;
1424 IStream
*pstrTmp
, *pstrChild
;
1426 if ((ciidExclude
!= 0) || (rgiidExclude
!= NULL
) || (snbExclude
!= NULL
))
1427 FIXME("Exclude option not implemented\n");
1429 TRACE("(%p, %ld, %p, %p, %p)\n",
1430 iface
, ciidExclude
, rgiidExclude
,
1431 snbExclude
, pstgDest
);
1434 * Perform a sanity check
1436 if ( pstgDest
== 0 )
1437 return STG_E_INVALIDPOINTER
;
1440 * Enumerate the elements
1442 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1450 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1451 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1456 * Obtain the next element
1458 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1460 if ( hr
== S_FALSE
)
1462 hr
= S_OK
; /* done, every element has been copied */
1466 if (curElement
.type
== STGTY_STORAGE
)
1469 * open child source storage
1471 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1472 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1473 NULL
, 0, &pstgChild
);
1479 * Check if destination storage is not a child of the source
1480 * storage, which will cause an infinite loop
1482 if (pstgChild
== pstgDest
)
1484 IEnumSTATSTG_Release(elements
);
1486 return STG_E_ACCESSDENIED
;
1490 * create a new storage in destination storage
1492 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1493 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1497 * if it already exist, don't create a new one use this one
1499 if (hr
== STG_E_FILEALREADYEXISTS
)
1501 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1502 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1503 NULL
, 0, &pstgTmp
);
1511 * do the copy recursively
1513 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1514 snbExclude
, pstgTmp
);
1516 IStorage_Release( pstgTmp
);
1517 IStorage_Release( pstgChild
);
1519 else if (curElement
.type
== STGTY_STREAM
)
1522 * create a new stream in destination storage. If the stream already
1523 * exist, it will be deleted and a new one will be created.
1525 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1526 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1533 * open child stream storage
1535 hr
= IStorage_OpenStream( iface
, curElement
.pwcsName
, NULL
,
1536 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1543 * Get the size of the source stream
1545 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1548 * Set the size of the destination stream.
1550 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1555 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1558 IStream_Release( pstrTmp
);
1559 IStream_Release( pstrChild
);
1563 WARN("unknown element type: %ld\n", curElement
.type
);
1566 } while (hr
== S_OK
);
1571 IEnumSTATSTG_Release(elements
);
1576 /*************************************************************************
1577 * MoveElementTo (IStorage)
1579 HRESULT WINAPI
StorageImpl_MoveElementTo(
1581 const OLECHAR
*pwcsName
, /* [string][in] */
1582 IStorage
*pstgDest
, /* [unique][in] */
1583 const OLECHAR
*pwcsNewName
,/* [string][in] */
1584 DWORD grfFlags
) /* [in] */
1586 FIXME("not implemented!\n");
1590 /*************************************************************************
1593 * Ensures that any changes made to a storage object open in transacted mode
1594 * are reflected in the parent storage
1597 * Wine doesn't implement transacted mode, which seems to be a basic
1598 * optimization, so we can ignore this stub for now.
1600 HRESULT WINAPI
StorageImpl_Commit(
1602 DWORD grfCommitFlags
)/* [in] */
1604 FIXME("(%ld): stub!\n", grfCommitFlags
);
1608 /*************************************************************************
1611 * Discard all changes that have been made since the last commit operation
1613 HRESULT WINAPI
StorageImpl_Revert(
1616 FIXME("not implemented!\n");
1620 /*************************************************************************
1621 * DestroyElement (IStorage)
1623 * Stategy: This implementation is build this way for simplicity not for speed.
1624 * I always delete the top most element of the enumeration and adjust
1625 * the deleted element pointer all the time. This takes longer to
1626 * do but allow to reinvoke DestroyElement whenever we encounter a
1627 * storage object. The optimisation reside in the usage of another
1628 * enumeration stategy that would give all the leaves of a storage
1629 * first. (postfix order)
1631 HRESULT WINAPI
StorageImpl_DestroyElement(
1633 const OLECHAR
*pwcsName
)/* [string][in] */
1635 StorageImpl
* const This
=(StorageImpl
*)iface
;
1637 IEnumSTATSTGImpl
* propertyEnumeration
;
1640 StgProperty propertyToDelete
;
1641 StgProperty parentProperty
;
1642 ULONG foundPropertyIndexToDelete
;
1643 ULONG typeOfRelation
;
1644 ULONG parentPropertyId
;
1647 iface
, debugstr_w(pwcsName
));
1650 * Perform a sanity check on the parameters.
1653 return STG_E_INVALIDPOINTER
;
1656 * Create a property enumeration to search the property with the given name
1658 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
1659 This
->base
.ancestorStorage
,
1660 This
->base
.rootPropertySetIndex
);
1662 foundPropertyIndexToDelete
= IEnumSTATSTGImpl_FindProperty(
1663 propertyEnumeration
,
1667 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1669 if ( foundPropertyIndexToDelete
== PROPERTY_NULL
)
1671 return STG_E_FILENOTFOUND
;
1675 * Find the parent property of the property to delete (the one that
1676 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1677 * the parent is This. Otherwise, the parent is one of it's sibling...
1681 * First, read This's StgProperty..
1683 res
= StorageImpl_ReadProperty(
1684 This
->base
.ancestorStorage
,
1685 This
->base
.rootPropertySetIndex
,
1691 * Second, check to see if by any chance the actual storage (This) is not
1692 * the parent of the property to delete... We never know...
1694 if ( parentProperty
.dirProperty
== foundPropertyIndexToDelete
)
1697 * Set data as it would have been done in the else part...
1699 typeOfRelation
= PROPERTY_RELATION_DIR
;
1700 parentPropertyId
= This
->base
.rootPropertySetIndex
;
1705 * Create a property enumeration to search the parent properties, and
1706 * delete it once done.
1708 IEnumSTATSTGImpl
* propertyEnumeration2
;
1710 propertyEnumeration2
= IEnumSTATSTGImpl_Construct(
1711 This
->base
.ancestorStorage
,
1712 This
->base
.rootPropertySetIndex
);
1714 typeOfRelation
= IEnumSTATSTGImpl_FindParentProperty(
1715 propertyEnumeration2
,
1716 foundPropertyIndexToDelete
,
1720 IEnumSTATSTGImpl_Destroy(propertyEnumeration2
);
1723 if ( propertyToDelete
.propertyType
== PROPTYPE_STORAGE
)
1725 hr
= deleteStorageProperty(
1727 foundPropertyIndexToDelete
,
1730 else if ( propertyToDelete
.propertyType
== PROPTYPE_STREAM
)
1732 hr
= deleteStreamProperty(
1734 foundPropertyIndexToDelete
,
1742 * Adjust the property chain
1744 hr
= adjustPropertyChain(
1755 /************************************************************************
1756 * StorageImpl_Stat (IStorage)
1758 * This method will retrieve information about this storage object.
1760 * See Windows documentation for more details on IStorage methods.
1762 HRESULT WINAPI
StorageImpl_Stat( IStorage
* iface
,
1763 STATSTG
* pstatstg
, /* [out] */
1764 DWORD grfStatFlag
) /* [in] */
1766 StorageImpl
* const This
= (StorageImpl
*)iface
;
1767 HRESULT result
= StorageBaseImpl_Stat( iface
, pstatstg
, grfStatFlag
);
1769 if ( !FAILED(result
) && ((grfStatFlag
& STATFLAG_NONAME
) == 0) && This
->pwcsName
)
1771 CoTaskMemFree(pstatstg
->pwcsName
);
1772 pstatstg
->pwcsName
= CoTaskMemAlloc((lstrlenW(This
->pwcsName
)+1)*sizeof(WCHAR
));
1773 strcpyW(pstatstg
->pwcsName
, This
->pwcsName
);
1781 /*********************************************************************
1785 * Perform the deletion of a complete storage node
1788 static HRESULT
deleteStorageProperty(
1789 StorageImpl
*parentStorage
,
1790 ULONG indexOfPropertyToDelete
,
1791 StgProperty propertyToDelete
)
1793 IEnumSTATSTG
*elements
= 0;
1794 IStorage
*childStorage
= 0;
1795 STATSTG currentElement
;
1797 HRESULT destroyHr
= S_OK
;
1800 * Open the storage and enumerate it
1802 hr
= StorageBaseImpl_OpenStorage(
1803 (IStorage
*)parentStorage
,
1804 propertyToDelete
.name
,
1806 STGM_SHARE_EXCLUSIVE
,
1817 * Enumerate the elements
1819 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
1824 * Obtain the next element
1826 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
1829 destroyHr
= StorageImpl_DestroyElement(
1830 (IStorage
*)childStorage
,
1831 (OLECHAR
*)currentElement
.pwcsName
);
1833 CoTaskMemFree(currentElement
.pwcsName
);
1837 * We need to Reset the enumeration every time because we delete elements
1838 * and the enumeration could be invalid
1840 IEnumSTATSTG_Reset(elements
);
1842 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
1845 * Invalidate the property by zeroing it's name member.
1847 propertyToDelete
.sizeOfNameString
= 0;
1849 StorageImpl_WriteProperty(parentStorage
->base
.ancestorStorage
,
1850 indexOfPropertyToDelete
,
1853 IStorage_Release(childStorage
);
1854 IEnumSTATSTG_Release(elements
);
1859 /*********************************************************************
1863 * Perform the deletion of a stream node
1866 static HRESULT
deleteStreamProperty(
1867 StorageImpl
*parentStorage
,
1868 ULONG indexOfPropertyToDelete
,
1869 StgProperty propertyToDelete
)
1873 ULARGE_INTEGER size
;
1875 size
.u
.HighPart
= 0;
1878 hr
= StorageBaseImpl_OpenStream(
1879 (IStorage
*)parentStorage
,
1880 (OLECHAR
*)propertyToDelete
.name
,
1882 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
1894 hr
= IStream_SetSize(pis
, size
);
1902 * Release the stream object.
1904 IStream_Release(pis
);
1907 * Invalidate the property by zeroing it's name member.
1909 propertyToDelete
.sizeOfNameString
= 0;
1912 * Here we should re-read the property so we get the updated pointer
1913 * but since we are here to zap it, I don't do it...
1915 StorageImpl_WriteProperty(
1916 parentStorage
->base
.ancestorStorage
,
1917 indexOfPropertyToDelete
,
1923 /*********************************************************************
1927 * Finds a placeholder for the StgProperty within the Storage
1930 static HRESULT
findPlaceholder(
1931 StorageImpl
*storage
,
1932 ULONG propertyIndexToStore
,
1933 ULONG storePropertyIndex
,
1936 StgProperty storeProperty
;
1941 * Read the storage property
1943 res
= StorageImpl_ReadProperty(
1944 storage
->base
.ancestorStorage
,
1953 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
1955 if (storeProperty
.previousProperty
!= PROPERTY_NULL
)
1957 return findPlaceholder(
1959 propertyIndexToStore
,
1960 storeProperty
.previousProperty
,
1965 storeProperty
.previousProperty
= propertyIndexToStore
;
1968 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
1970 if (storeProperty
.nextProperty
!= PROPERTY_NULL
)
1972 return findPlaceholder(
1974 propertyIndexToStore
,
1975 storeProperty
.nextProperty
,
1980 storeProperty
.nextProperty
= propertyIndexToStore
;
1983 else if (typeOfRelation
== PROPERTY_RELATION_DIR
)
1985 if (storeProperty
.dirProperty
!= PROPERTY_NULL
)
1987 return findPlaceholder(
1989 propertyIndexToStore
,
1990 storeProperty
.dirProperty
,
1995 storeProperty
.dirProperty
= propertyIndexToStore
;
1999 hr
= StorageImpl_WriteProperty(
2000 storage
->base
.ancestorStorage
,
2012 /*************************************************************************
2016 * This method takes the previous and the next property link of a property
2017 * to be deleted and find them a place in the Storage.
2019 static HRESULT
adjustPropertyChain(
2021 StgProperty propertyToDelete
,
2022 StgProperty parentProperty
,
2023 ULONG parentPropertyId
,
2026 ULONG newLinkProperty
= PROPERTY_NULL
;
2027 BOOL needToFindAPlaceholder
= FALSE
;
2028 ULONG storeNode
= PROPERTY_NULL
;
2029 ULONG toStoreNode
= PROPERTY_NULL
;
2030 INT relationType
= 0;
2034 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
2036 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2039 * Set the parent previous to the property to delete previous
2041 newLinkProperty
= propertyToDelete
.previousProperty
;
2043 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2046 * We also need to find a storage for the other link, setup variables
2047 * to do this at the end...
2049 needToFindAPlaceholder
= TRUE
;
2050 storeNode
= propertyToDelete
.previousProperty
;
2051 toStoreNode
= propertyToDelete
.nextProperty
;
2052 relationType
= PROPERTY_RELATION_NEXT
;
2055 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2058 * Set the parent previous to the property to delete next
2060 newLinkProperty
= propertyToDelete
.nextProperty
;
2064 * Link it for real...
2066 parentProperty
.previousProperty
= newLinkProperty
;
2069 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
2071 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2074 * Set the parent next to the property to delete next previous
2076 newLinkProperty
= propertyToDelete
.previousProperty
;
2078 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2081 * We also need to find a storage for the other link, setup variables
2082 * to do this at the end...
2084 needToFindAPlaceholder
= TRUE
;
2085 storeNode
= propertyToDelete
.previousProperty
;
2086 toStoreNode
= propertyToDelete
.nextProperty
;
2087 relationType
= PROPERTY_RELATION_NEXT
;
2090 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2093 * Set the parent next to the property to delete next
2095 newLinkProperty
= propertyToDelete
.nextProperty
;
2099 * Link it for real...
2101 parentProperty
.nextProperty
= newLinkProperty
;
2103 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2105 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2108 * Set the parent dir to the property to delete previous
2110 newLinkProperty
= propertyToDelete
.previousProperty
;
2112 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2115 * We also need to find a storage for the other link, setup variables
2116 * to do this at the end...
2118 needToFindAPlaceholder
= TRUE
;
2119 storeNode
= propertyToDelete
.previousProperty
;
2120 toStoreNode
= propertyToDelete
.nextProperty
;
2121 relationType
= PROPERTY_RELATION_NEXT
;
2124 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2127 * Set the parent dir to the property to delete next
2129 newLinkProperty
= propertyToDelete
.nextProperty
;
2133 * Link it for real...
2135 parentProperty
.dirProperty
= newLinkProperty
;
2139 * Write back the parent property
2141 res
= StorageImpl_WriteProperty(
2142 This
->base
.ancestorStorage
,
2151 * If a placeholder is required for the other link, then, find one and
2152 * get out of here...
2154 if (needToFindAPlaceholder
)
2156 hr
= findPlaceholder(
2167 /******************************************************************************
2168 * SetElementTimes (IStorage)
2170 HRESULT WINAPI
StorageImpl_SetElementTimes(
2172 const OLECHAR
*pwcsName
,/* [string][in] */
2173 const FILETIME
*pctime
, /* [in] */
2174 const FILETIME
*patime
, /* [in] */
2175 const FILETIME
*pmtime
) /* [in] */
2177 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2181 /******************************************************************************
2182 * SetStateBits (IStorage)
2184 HRESULT WINAPI
StorageImpl_SetStateBits(
2186 DWORD grfStateBits
,/* [in] */
2187 DWORD grfMask
) /* [in] */
2189 FIXME("not implemented!\n");
2194 * Virtual function table for the IStorage32Impl class.
2196 static const IStorageVtbl Storage32Impl_Vtbl
=
2198 StorageBaseImpl_QueryInterface
,
2199 StorageBaseImpl_AddRef
,
2200 StorageBaseImpl_Release
,
2201 StorageBaseImpl_CreateStream
,
2202 StorageBaseImpl_OpenStream
,
2203 StorageImpl_CreateStorage
,
2204 StorageBaseImpl_OpenStorage
,
2206 StorageImpl_MoveElementTo
,
2209 StorageBaseImpl_EnumElements
,
2210 StorageImpl_DestroyElement
,
2211 StorageBaseImpl_RenameElement
,
2212 StorageImpl_SetElementTimes
,
2213 StorageBaseImpl_SetClass
,
2214 StorageImpl_SetStateBits
,
2218 HRESULT
StorageImpl_Construct(
2228 StgProperty currentProperty
;
2229 BOOL readSuccessful
;
2230 ULONG currentPropertyIndex
;
2232 if ( FAILED( validateSTGM(openFlags
) ))
2233 return STG_E_INVALIDFLAG
;
2235 memset(This
, 0, sizeof(StorageImpl
));
2238 * Initialize the virtual function table.
2240 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2241 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2242 This
->base
.v_destructor
= &StorageImpl_Destroy
;
2243 This
->base
.openFlags
= openFlags
;
2246 * This is the top-level storage so initialize the ancestor pointer
2249 This
->base
.ancestorStorage
= This
;
2252 * Initialize the physical support of the storage.
2254 This
->hFile
= hFile
;
2257 * Store copy of file path.
2260 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2261 (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
));
2262 if (!This
->pwcsName
)
2263 return STG_E_INSUFFICIENTMEMORY
;
2264 strcpyW(This
->pwcsName
, pwcsName
);
2268 * Initialize the big block cache.
2270 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
2271 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2272 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2278 if (This
->bigBlockFile
== 0)
2283 ULARGE_INTEGER size
;
2284 BYTE
* bigBlockBuffer
;
2287 * Initialize all header variables:
2288 * - The big block depot consists of one block and it is at block 0
2289 * - The properties start at block 1
2290 * - There is no small block depot
2292 memset( This
->bigBlockDepotStart
,
2294 sizeof(This
->bigBlockDepotStart
));
2296 This
->bigBlockDepotCount
= 1;
2297 This
->bigBlockDepotStart
[0] = 0;
2298 This
->rootStartBlock
= 1;
2299 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2300 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
2301 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2302 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2303 This
->extBigBlockDepotCount
= 0;
2305 StorageImpl_SaveFileHeader(This
);
2308 * Add one block for the big block depot and one block for the properties
2310 size
.u
.HighPart
= 0;
2311 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2312 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2315 * Initialize the big block depot
2317 bigBlockBuffer
= StorageImpl_GetBigBlock(This
, 0);
2318 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2319 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2320 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2321 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
2326 * Load the header for the file.
2328 hr
= StorageImpl_LoadFileHeader(This
);
2332 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2339 * There is no block depot cached yet.
2341 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2344 * Start searching for free blocks with block 0.
2346 This
->prevFreeBlock
= 0;
2349 * Create the block chain abstractions.
2351 if(!(This
->rootBlockChain
=
2352 BlockChainStream_Construct(This
, &This
->rootStartBlock
, PROPERTY_NULL
)))
2353 return STG_E_READFAULT
;
2355 if(!(This
->smallBlockDepotChain
=
2356 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2358 return STG_E_READFAULT
;
2361 * Write the root property
2365 StgProperty rootProp
;
2367 * Initialize the property chain
2369 memset(&rootProp
, 0, sizeof(rootProp
));
2370 MultiByteToWideChar( CP_ACP
, 0, rootPropertyName
, -1, rootProp
.name
,
2371 sizeof(rootProp
.name
)/sizeof(WCHAR
) );
2372 rootProp
.sizeOfNameString
= (strlenW(rootProp
.name
)+1) * sizeof(WCHAR
);
2373 rootProp
.propertyType
= PROPTYPE_ROOT
;
2374 rootProp
.previousProperty
= PROPERTY_NULL
;
2375 rootProp
.nextProperty
= PROPERTY_NULL
;
2376 rootProp
.dirProperty
= PROPERTY_NULL
;
2377 rootProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
2378 rootProp
.size
.u
.HighPart
= 0;
2379 rootProp
.size
.u
.LowPart
= 0;
2381 StorageImpl_WriteProperty(This
, 0, &rootProp
);
2385 * Find the ID of the root in the property sets.
2387 currentPropertyIndex
= 0;
2391 readSuccessful
= StorageImpl_ReadProperty(
2393 currentPropertyIndex
,
2398 if ( (currentProperty
.sizeOfNameString
!= 0 ) &&
2399 (currentProperty
.propertyType
== PROPTYPE_ROOT
) )
2401 This
->base
.rootPropertySetIndex
= currentPropertyIndex
;
2405 currentPropertyIndex
++;
2407 } while (readSuccessful
&& (This
->base
.rootPropertySetIndex
== PROPERTY_NULL
) );
2409 if (!readSuccessful
)
2412 return STG_E_READFAULT
;
2416 * Create the block chain abstraction for the small block root chain.
2418 if(!(This
->smallBlockRootChain
=
2419 BlockChainStream_Construct(This
, NULL
, This
->base
.rootPropertySetIndex
)))
2420 return STG_E_READFAULT
;
2425 void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2427 StorageImpl
*This
= (StorageImpl
*) iface
;
2428 TRACE("(%p)\n", This
);
2430 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2432 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2433 BlockChainStream_Destroy(This
->rootBlockChain
);
2434 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2436 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2437 HeapFree(GetProcessHeap(), 0, This
);
2440 /******************************************************************************
2441 * Storage32Impl_GetNextFreeBigBlock
2443 * Returns the index of the next free big block.
2444 * If the big block depot is filled, this method will enlarge it.
2447 ULONG
StorageImpl_GetNextFreeBigBlock(
2450 ULONG depotBlockIndexPos
;
2452 ULONG depotBlockOffset
;
2453 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2454 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2456 ULONG freeBlock
= BLOCK_UNUSED
;
2458 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2459 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2462 * Scan the entire big block depot until we find a block marked free
2464 while (nextBlockIndex
!= BLOCK_UNUSED
)
2466 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2468 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2471 * Grow the primary depot.
2473 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2475 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2478 * Add a block depot.
2480 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2481 This
->bigBlockDepotCount
++;
2482 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2485 * Flag it as a block depot.
2487 StorageImpl_SetNextBlockInChain(This
,
2491 /* Save new header information.
2493 StorageImpl_SaveFileHeader(This
);
2498 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2500 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2503 * Grow the extended depot.
2505 ULONG extIndex
= BLOCK_UNUSED
;
2506 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2507 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2509 if (extBlockOffset
== 0)
2511 /* We need an extended block.
2513 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2514 This
->extBigBlockDepotCount
++;
2515 depotBlockIndexPos
= extIndex
+ 1;
2518 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2521 * Add a block depot and mark it in the extended block.
2523 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2524 This
->bigBlockDepotCount
++;
2525 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2527 /* Flag the block depot.
2529 StorageImpl_SetNextBlockInChain(This
,
2533 /* If necessary, flag the extended depot block.
2535 if (extIndex
!= BLOCK_UNUSED
)
2536 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2538 /* Save header information.
2540 StorageImpl_SaveFileHeader(This
);
2544 depotBuffer
= StorageImpl_GetROBigBlock(This
, depotBlockIndexPos
);
2546 if (depotBuffer
!= 0)
2548 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2549 ( nextBlockIndex
!= BLOCK_UNUSED
))
2551 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2553 if (nextBlockIndex
== BLOCK_UNUSED
)
2555 freeBlock
= (depotIndex
* blocksPerDepot
) +
2556 (depotBlockOffset
/sizeof(ULONG
));
2559 depotBlockOffset
+= sizeof(ULONG
);
2562 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2566 depotBlockOffset
= 0;
2569 This
->prevFreeBlock
= freeBlock
;
2574 /******************************************************************************
2575 * Storage32Impl_AddBlockDepot
2577 * This will create a depot block, essentially it is a block initialized
2580 void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2584 blockBuffer
= StorageImpl_GetBigBlock(This
, blockIndex
);
2587 * Initialize blocks as free
2589 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2591 StorageImpl_ReleaseBigBlock(This
, blockBuffer
);
2594 /******************************************************************************
2595 * Storage32Impl_GetExtDepotBlock
2597 * Returns the index of the block that corresponds to the specified depot
2598 * index. This method is only for depot indexes equal or greater than
2599 * COUNT_BBDEPOTINHEADER.
2601 ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2603 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2604 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2605 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2606 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2607 ULONG blockIndex
= BLOCK_UNUSED
;
2608 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2610 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2612 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2613 return BLOCK_UNUSED
;
2615 while (extBlockCount
> 0)
2617 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2621 if (extBlockIndex
!= BLOCK_UNUSED
)
2625 depotBuffer
= StorageImpl_GetROBigBlock(This
, extBlockIndex
);
2627 if (depotBuffer
!= 0)
2629 StorageUtl_ReadDWord(depotBuffer
,
2630 extBlockOffset
* sizeof(ULONG
),
2633 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2640 /******************************************************************************
2641 * Storage32Impl_SetExtDepotBlock
2643 * Associates the specified block index to the specified depot index.
2644 * This method is only for depot indexes equal or greater than
2645 * COUNT_BBDEPOTINHEADER.
2647 void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
,
2651 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2652 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2653 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2654 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2655 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2657 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2659 while (extBlockCount
> 0)
2661 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2665 if (extBlockIndex
!= BLOCK_UNUSED
)
2669 depotBuffer
= StorageImpl_GetBigBlock(This
, extBlockIndex
);
2671 if (depotBuffer
!= 0)
2673 StorageUtl_WriteDWord(depotBuffer
,
2674 extBlockOffset
* sizeof(ULONG
),
2677 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2682 /******************************************************************************
2683 * Storage32Impl_AddExtBlockDepot
2685 * Creates an extended depot block.
2687 ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
2689 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
2690 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
2691 BYTE
* depotBuffer
= NULL
;
2692 ULONG index
= BLOCK_UNUSED
;
2693 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2694 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
2695 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
2697 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
2698 blocksPerDepotBlock
;
2700 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
2703 * The first extended block.
2705 This
->extBigBlockDepotStart
= index
;
2711 * Follow the chain to the last one.
2713 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
2715 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
2719 * Add the new extended block to the chain.
2721 depotBuffer
= StorageImpl_GetBigBlock(This
, nextExtBlock
);
2722 StorageUtl_WriteDWord(depotBuffer
, nextBlockOffset
, index
);
2723 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2727 * Initialize this block.
2729 depotBuffer
= StorageImpl_GetBigBlock(This
, index
);
2730 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2731 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2736 /******************************************************************************
2737 * Storage32Impl_FreeBigBlock
2739 * This method will flag the specified block as free in the big block depot.
2741 void StorageImpl_FreeBigBlock(
2745 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
2747 if (blockIndex
< This
->prevFreeBlock
)
2748 This
->prevFreeBlock
= blockIndex
;
2751 /************************************************************************
2752 * Storage32Impl_GetNextBlockInChain
2754 * This method will retrieve the block index of the next big block in
2757 * Params: This - Pointer to the Storage object.
2758 * blockIndex - Index of the block to retrieve the chain
2760 * nextBlockIndex - receives the return value.
2762 * Returns: This method returns the index of the next block in the chain.
2763 * It will return the constants:
2764 * BLOCK_SPECIAL - If the block given was not part of a
2766 * BLOCK_END_OF_CHAIN - If the block given was the last in
2768 * BLOCK_UNUSED - If the block given was not past of a chain
2770 * BLOCK_EXTBBDEPOT - This block is part of the extended
2773 * See Windows documentation for more details on IStorage methods.
2775 HRESULT
StorageImpl_GetNextBlockInChain(
2778 ULONG
* nextBlockIndex
)
2780 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2781 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2782 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2784 ULONG depotBlockIndexPos
;
2787 *nextBlockIndex
= BLOCK_SPECIAL
;
2789 if(depotBlockCount
>= This
->bigBlockDepotCount
)
2791 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount
,
2792 This
->bigBlockDepotCount
);
2793 return STG_E_READFAULT
;
2797 * Cache the currently accessed depot block.
2799 if (depotBlockCount
!= This
->indexBlockDepotCached
)
2801 This
->indexBlockDepotCached
= depotBlockCount
;
2803 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2805 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2810 * We have to look in the extended depot.
2812 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2815 depotBuffer
= StorageImpl_GetROBigBlock(This
, depotBlockIndexPos
);
2818 return STG_E_READFAULT
;
2820 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
2822 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
2823 This
->blockDepotCached
[index
] = *nextBlockIndex
;
2825 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2828 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
2833 /******************************************************************************
2834 * Storage32Impl_GetNextExtendedBlock
2836 * Given an extended block this method will return the next extended block.
2839 * The last ULONG of an extended block is the block index of the next
2840 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2844 * - The index of the next extended block
2845 * - BLOCK_UNUSED: there is no next extended block.
2846 * - Any other return values denotes failure.
2848 ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
2850 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2851 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2854 depotBuffer
= StorageImpl_GetROBigBlock(This
, blockIndex
);
2858 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2860 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2863 return nextBlockIndex
;
2866 /******************************************************************************
2867 * Storage32Impl_SetNextBlockInChain
2869 * This method will write the index of the specified block's next block
2870 * in the big block depot.
2872 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2875 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2876 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2877 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2880 void StorageImpl_SetNextBlockInChain(
2885 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2886 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2887 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2888 ULONG depotBlockIndexPos
;
2891 assert(depotBlockCount
< This
->bigBlockDepotCount
);
2892 assert(blockIndex
!= nextBlock
);
2894 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2896 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2901 * We have to look in the extended depot.
2903 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2906 depotBuffer
= StorageImpl_GetBigBlock(This
, depotBlockIndexPos
);
2910 StorageUtl_WriteDWord(depotBuffer
, depotBlockOffset
, nextBlock
);
2911 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2915 * Update the cached block depot, if necessary.
2917 if (depotBlockCount
== This
->indexBlockDepotCached
)
2919 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
2923 /******************************************************************************
2924 * Storage32Impl_LoadFileHeader
2926 * This method will read in the file header, i.e. big block index -1.
2928 HRESULT
StorageImpl_LoadFileHeader(
2931 HRESULT hr
= STG_E_FILENOTFOUND
;
2932 void* headerBigBlock
= NULL
;
2936 * Get a pointer to the big block of data containing the header.
2938 headerBigBlock
= StorageImpl_GetROBigBlock(This
, -1);
2941 * Extract the information from the header.
2943 if (headerBigBlock
!=0)
2946 * Check for the "magic number" signature and return an error if it is not
2949 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
2951 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2952 return STG_E_OLDFORMAT
;
2955 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
2957 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2958 return STG_E_INVALIDHEADER
;
2961 StorageUtl_ReadWord(
2963 OFFSET_BIGBLOCKSIZEBITS
,
2964 &This
->bigBlockSizeBits
);
2966 StorageUtl_ReadWord(
2968 OFFSET_SMALLBLOCKSIZEBITS
,
2969 &This
->smallBlockSizeBits
);
2971 StorageUtl_ReadDWord(
2973 OFFSET_BBDEPOTCOUNT
,
2974 &This
->bigBlockDepotCount
);
2976 StorageUtl_ReadDWord(
2978 OFFSET_ROOTSTARTBLOCK
,
2979 &This
->rootStartBlock
);
2981 StorageUtl_ReadDWord(
2983 OFFSET_SBDEPOTSTART
,
2984 &This
->smallBlockDepotStart
);
2986 StorageUtl_ReadDWord(
2988 OFFSET_EXTBBDEPOTSTART
,
2989 &This
->extBigBlockDepotStart
);
2991 StorageUtl_ReadDWord(
2993 OFFSET_EXTBBDEPOTCOUNT
,
2994 &This
->extBigBlockDepotCount
);
2996 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2998 StorageUtl_ReadDWord(
3000 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3001 &(This
->bigBlockDepotStart
[index
]));
3005 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3009 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3010 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3014 This
->bigBlockSize
= 0x000000001 >> (DWORD
)This
->bigBlockSizeBits
;
3015 This
->smallBlockSize
= 0x000000001 >> (DWORD
)This
->smallBlockSizeBits
;
3019 * Right now, the code is making some assumptions about the size of the
3020 * blocks, just make sure they are what we're expecting.
3022 if (This
->bigBlockSize
!= DEF_BIG_BLOCK_SIZE
||
3023 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
)
3025 WARN("Broken OLE storage file\n");
3026 hr
= STG_E_INVALIDHEADER
;
3032 * Release the block.
3034 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
3040 /******************************************************************************
3041 * Storage32Impl_SaveFileHeader
3043 * This method will save to the file the header, i.e. big block -1.
3045 void StorageImpl_SaveFileHeader(
3048 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3053 * Get a pointer to the big block of data containing the header.
3055 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3058 * If the block read failed, the file is probably new.
3063 * Initialize for all unknown fields.
3065 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
3068 * Initialize the magic number.
3070 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3073 * And a bunch of things we don't know what they mean
3075 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3076 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3077 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3078 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
3082 * Write the information to the header.
3084 StorageUtl_WriteWord(
3086 OFFSET_BIGBLOCKSIZEBITS
,
3087 This
->bigBlockSizeBits
);
3089 StorageUtl_WriteWord(
3091 OFFSET_SMALLBLOCKSIZEBITS
,
3092 This
->smallBlockSizeBits
);
3094 StorageUtl_WriteDWord(
3096 OFFSET_BBDEPOTCOUNT
,
3097 This
->bigBlockDepotCount
);
3099 StorageUtl_WriteDWord(
3101 OFFSET_ROOTSTARTBLOCK
,
3102 This
->rootStartBlock
);
3104 StorageUtl_WriteDWord(
3106 OFFSET_SBDEPOTSTART
,
3107 This
->smallBlockDepotStart
);
3109 StorageUtl_WriteDWord(
3111 OFFSET_SBDEPOTCOUNT
,
3112 This
->smallBlockDepotChain
?
3113 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3115 StorageUtl_WriteDWord(
3117 OFFSET_EXTBBDEPOTSTART
,
3118 This
->extBigBlockDepotStart
);
3120 StorageUtl_WriteDWord(
3122 OFFSET_EXTBBDEPOTCOUNT
,
3123 This
->extBigBlockDepotCount
);
3125 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3127 StorageUtl_WriteDWord(
3129 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3130 (This
->bigBlockDepotStart
[index
]));
3134 * Write the big block back to the file.
3136 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
3139 /******************************************************************************
3140 * Storage32Impl_ReadProperty
3142 * This method will read the specified property from the property chain.
3144 BOOL
StorageImpl_ReadProperty(
3147 StgProperty
* buffer
)
3149 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
3150 ULARGE_INTEGER offsetInPropSet
;
3151 BOOL readSuccessful
;
3154 offsetInPropSet
.u
.HighPart
= 0;
3155 offsetInPropSet
.u
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
3157 readSuccessful
= BlockChainStream_ReadAt(
3158 This
->rootBlockChain
,
3166 /* replace the name of root entry (often "Root Entry") by the file name */
3167 WCHAR
*propName
= (index
== This
->base
.rootPropertySetIndex
) ?
3168 This
->filename
: (WCHAR
*)currentProperty
+OFFSET_PS_NAME
;
3170 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3174 PROPERTY_NAME_BUFFER_LEN
);
3175 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3177 memcpy(&buffer
->propertyType
, currentProperty
+ OFFSET_PS_PROPERTYTYPE
, 1);
3179 StorageUtl_ReadWord(
3181 OFFSET_PS_NAMELENGTH
,
3182 &buffer
->sizeOfNameString
);
3184 StorageUtl_ReadDWord(
3186 OFFSET_PS_PREVIOUSPROP
,
3187 &buffer
->previousProperty
);
3189 StorageUtl_ReadDWord(
3192 &buffer
->nextProperty
);
3194 StorageUtl_ReadDWord(
3197 &buffer
->dirProperty
);
3199 StorageUtl_ReadGUID(
3202 &buffer
->propertyUniqueID
);
3204 StorageUtl_ReadDWord(
3207 &buffer
->timeStampS1
);
3209 StorageUtl_ReadDWord(
3212 &buffer
->timeStampD1
);
3214 StorageUtl_ReadDWord(
3217 &buffer
->timeStampS2
);
3219 StorageUtl_ReadDWord(
3222 &buffer
->timeStampD2
);
3224 StorageUtl_ReadDWord(
3226 OFFSET_PS_STARTBLOCK
,
3227 &buffer
->startingBlock
);
3229 StorageUtl_ReadDWord(
3232 &buffer
->size
.u
.LowPart
);
3234 buffer
->size
.u
.HighPart
= 0;
3237 return readSuccessful
;
3240 /*********************************************************************
3241 * Write the specified property into the property chain
3243 BOOL
StorageImpl_WriteProperty(
3246 StgProperty
* buffer
)
3248 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
3249 ULARGE_INTEGER offsetInPropSet
;
3250 BOOL writeSuccessful
;
3253 offsetInPropSet
.u
.HighPart
= 0;
3254 offsetInPropSet
.u
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
3256 memset(currentProperty
, 0, PROPSET_BLOCK_SIZE
);
3259 currentProperty
+ OFFSET_PS_NAME
,
3261 PROPERTY_NAME_BUFFER_LEN
);
3263 memcpy(currentProperty
+ OFFSET_PS_PROPERTYTYPE
, &buffer
->propertyType
, 1);
3265 StorageUtl_WriteWord(
3267 OFFSET_PS_NAMELENGTH
,
3268 buffer
->sizeOfNameString
);
3270 StorageUtl_WriteDWord(
3272 OFFSET_PS_PREVIOUSPROP
,
3273 buffer
->previousProperty
);
3275 StorageUtl_WriteDWord(
3278 buffer
->nextProperty
);
3280 StorageUtl_WriteDWord(
3283 buffer
->dirProperty
);
3285 StorageUtl_WriteGUID(
3288 &buffer
->propertyUniqueID
);
3290 StorageUtl_WriteDWord(
3293 buffer
->timeStampS1
);
3295 StorageUtl_WriteDWord(
3298 buffer
->timeStampD1
);
3300 StorageUtl_WriteDWord(
3303 buffer
->timeStampS2
);
3305 StorageUtl_WriteDWord(
3308 buffer
->timeStampD2
);
3310 StorageUtl_WriteDWord(
3312 OFFSET_PS_STARTBLOCK
,
3313 buffer
->startingBlock
);
3315 StorageUtl_WriteDWord(
3318 buffer
->size
.u
.LowPart
);
3320 writeSuccessful
= BlockChainStream_WriteAt(This
->rootBlockChain
,
3325 return writeSuccessful
;
3328 BOOL
StorageImpl_ReadBigBlock(
3333 void* bigBlockBuffer
;
3335 bigBlockBuffer
= StorageImpl_GetROBigBlock(This
, blockIndex
);
3337 if (bigBlockBuffer
!=0)
3339 memcpy(buffer
, bigBlockBuffer
, This
->bigBlockSize
);
3341 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
3349 BOOL
StorageImpl_WriteBigBlock(
3354 void* bigBlockBuffer
;
3356 bigBlockBuffer
= StorageImpl_GetBigBlock(This
, blockIndex
);
3358 if (bigBlockBuffer
!=0)
3360 memcpy(bigBlockBuffer
, buffer
, This
->bigBlockSize
);
3362 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
3370 void* StorageImpl_GetROBigBlock(
3374 return BIGBLOCKFILE_GetROBigBlock(This
->bigBlockFile
, blockIndex
);
3377 void* StorageImpl_GetBigBlock(
3381 return BIGBLOCKFILE_GetBigBlock(This
->bigBlockFile
, blockIndex
);
3384 void StorageImpl_ReleaseBigBlock(
3388 BIGBLOCKFILE_ReleaseBigBlock(This
->bigBlockFile
, pBigBlock
);
3391 /******************************************************************************
3392 * Storage32Impl_SmallBlocksToBigBlocks
3394 * This method will convert a small block chain to a big block chain.
3395 * The small block chain will be destroyed.
3397 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3399 SmallBlockChainStream
** ppsbChain
)
3401 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3402 ULARGE_INTEGER size
, offset
;
3403 ULONG cbRead
, cbWritten
, cbTotalRead
, cbTotalWritten
;
3404 ULONG propertyIndex
;
3405 BOOL successRead
, successWrite
;
3406 StgProperty chainProperty
;
3408 BlockChainStream
*bbTempChain
= NULL
;
3409 BlockChainStream
*bigBlockChain
= NULL
;
3412 * Create a temporary big block chain that doesn't have
3413 * an associated property. This temporary chain will be
3414 * used to copy data from small blocks to big blocks.
3416 bbTempChain
= BlockChainStream_Construct(This
,
3419 if(!bbTempChain
) return NULL
;
3421 * Grow the big block chain.
3423 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3424 BlockChainStream_SetSize(bbTempChain
, size
);
3427 * Copy the contents of the small block chain to the big block chain
3428 * by small block size increments.
3430 offset
.u
.LowPart
= 0;
3431 offset
.u
.HighPart
= 0;
3435 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3438 successRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3440 DEF_SMALL_BLOCK_SIZE
,
3443 cbTotalRead
+= cbRead
;
3445 successWrite
= BlockChainStream_WriteAt(bbTempChain
,
3450 cbTotalWritten
+= cbWritten
;
3452 offset
.u
.LowPart
+= This
->smallBlockSize
;
3454 } while (successRead
&& successWrite
);
3455 HeapFree(GetProcessHeap(),0,buffer
);
3457 assert(cbTotalRead
== cbTotalWritten
);
3460 * Destroy the small block chain.
3462 propertyIndex
= (*ppsbChain
)->ownerPropertyIndex
;
3463 size
.u
.HighPart
= 0;
3465 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3466 SmallBlockChainStream_Destroy(*ppsbChain
);
3470 * Change the property information. This chain is now a big block chain
3471 * and it doesn't reside in the small blocks chain anymore.
3473 StorageImpl_ReadProperty(This
, propertyIndex
, &chainProperty
);
3475 chainProperty
.startingBlock
= bbHeadOfChain
;
3477 StorageImpl_WriteProperty(This
, propertyIndex
, &chainProperty
);
3480 * Destroy the temporary propertyless big block chain.
3481 * Create a new big block chain associated with this property.
3483 BlockChainStream_Destroy(bbTempChain
);
3484 bigBlockChain
= BlockChainStream_Construct(This
,
3488 return bigBlockChain
;
3491 void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
3493 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
3495 StorageBaseImpl_Release((IStorage
*)This
->base
.ancestorStorage
);
3496 HeapFree(GetProcessHeap(), 0, This
);
3499 /******************************************************************************
3501 ** Storage32InternalImpl_Commit
3503 ** The non-root storages cannot be opened in transacted mode thus this function
3506 HRESULT WINAPI
StorageInternalImpl_Commit(
3508 DWORD grfCommitFlags
) /* [in] */
3513 /******************************************************************************
3515 ** Storage32InternalImpl_Revert
3517 ** The non-root storages cannot be opened in transacted mode thus this function
3520 HRESULT WINAPI
StorageInternalImpl_Revert(
3526 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
3528 IStorage_Release((IStorage
*)This
->parentStorage
);
3529 HeapFree(GetProcessHeap(), 0, This
->stackToVisit
);
3530 HeapFree(GetProcessHeap(), 0, This
);
3533 HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
3534 IEnumSTATSTG
* iface
,
3538 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3541 * Perform a sanity check on the parameters.
3544 return E_INVALIDARG
;
3547 * Initialize the return parameter.
3552 * Compare the riid with the interface IDs implemented by this object.
3554 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
3555 IsEqualGUID(&IID_IStorage
, riid
))
3557 *ppvObject
= (IEnumSTATSTG
*)This
;
3558 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
3562 return E_NOINTERFACE
;
3565 ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
3566 IEnumSTATSTG
* iface
)
3568 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3569 return InterlockedIncrement(&This
->ref
);
3572 ULONG WINAPI
IEnumSTATSTGImpl_Release(
3573 IEnumSTATSTG
* iface
)
3575 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3579 newRef
= InterlockedDecrement(&This
->ref
);
3582 * If the reference count goes down to 0, perform suicide.
3586 IEnumSTATSTGImpl_Destroy(This
);
3592 HRESULT WINAPI
IEnumSTATSTGImpl_Next(
3593 IEnumSTATSTG
* iface
,
3596 ULONG
* pceltFetched
)
3598 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3600 StgProperty currentProperty
;
3601 STATSTG
* currentReturnStruct
= rgelt
;
3602 ULONG objectFetched
= 0;
3603 ULONG currentSearchNode
;
3606 * Perform a sanity check on the parameters.
3608 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
3609 return E_INVALIDARG
;
3612 * To avoid the special case, get another pointer to a ULONG value if
3613 * the caller didn't supply one.
3615 if (pceltFetched
==0)
3616 pceltFetched
= &objectFetched
;
3619 * Start the iteration, we will iterate until we hit the end of the
3620 * linked list or until we hit the number of items to iterate through
3625 * Start with the node at the top of the stack.
3627 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3629 while ( ( *pceltFetched
< celt
) &&
3630 ( currentSearchNode
!=PROPERTY_NULL
) )
3633 * Remove the top node from the stack
3635 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3638 * Read the property from the storage.
3640 StorageImpl_ReadProperty(This
->parentStorage
,
3645 * Copy the information to the return buffer.
3647 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct
,
3652 * Step to the next item in the iteration
3655 currentReturnStruct
++;
3658 * Push the next search node in the search stack.
3660 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3663 * continue the iteration.
3665 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3668 if (*pceltFetched
== celt
)
3675 HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
3676 IEnumSTATSTG
* iface
,
3679 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3681 StgProperty currentProperty
;
3682 ULONG objectFetched
= 0;
3683 ULONG currentSearchNode
;
3686 * Start with the node at the top of the stack.
3688 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3690 while ( (objectFetched
< celt
) &&
3691 (currentSearchNode
!=PROPERTY_NULL
) )
3694 * Remove the top node from the stack
3696 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3699 * Read the property from the storage.
3701 StorageImpl_ReadProperty(This
->parentStorage
,
3706 * Step to the next item in the iteration
3711 * Push the next search node in the search stack.
3713 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3716 * continue the iteration.
3718 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3721 if (objectFetched
== celt
)
3727 HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
3728 IEnumSTATSTG
* iface
)
3730 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3732 StgProperty rootProperty
;
3733 BOOL readSuccessful
;
3736 * Re-initialize the search stack to an empty stack
3738 This
->stackSize
= 0;
3741 * Read the root property from the storage.
3743 readSuccessful
= StorageImpl_ReadProperty(
3744 This
->parentStorage
,
3745 This
->firstPropertyNode
,
3750 assert(rootProperty
.sizeOfNameString
!=0);
3753 * Push the search node in the search stack.
3755 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.dirProperty
);
3761 HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
3762 IEnumSTATSTG
* iface
,
3763 IEnumSTATSTG
** ppenum
)
3765 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3767 IEnumSTATSTGImpl
* newClone
;
3770 * Perform a sanity check on the parameters.
3773 return E_INVALIDARG
;
3775 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
3776 This
->firstPropertyNode
);
3780 * The new clone enumeration must point to the same current node as
3783 newClone
->stackSize
= This
->stackSize
;
3784 newClone
->stackMaxSize
= This
->stackMaxSize
;
3785 newClone
->stackToVisit
=
3786 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * newClone
->stackMaxSize
);
3789 newClone
->stackToVisit
,
3791 sizeof(ULONG
) * newClone
->stackSize
);
3793 *ppenum
= (IEnumSTATSTG
*)newClone
;
3796 * Don't forget to nail down a reference to the clone before
3799 IEnumSTATSTGImpl_AddRef(*ppenum
);
3804 INT
IEnumSTATSTGImpl_FindParentProperty(
3805 IEnumSTATSTGImpl
*This
,
3806 ULONG childProperty
,
3807 StgProperty
*currentProperty
,
3810 ULONG currentSearchNode
;
3814 * To avoid the special case, get another pointer to a ULONG value if
3815 * the caller didn't supply one.
3818 thisNodeId
= &foundNode
;
3821 * Start with the node at the top of the stack.
3823 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3826 while (currentSearchNode
!=PROPERTY_NULL
)
3829 * Store the current node in the returned parameters
3831 *thisNodeId
= currentSearchNode
;
3834 * Remove the top node from the stack
3836 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3839 * Read the property from the storage.
3841 StorageImpl_ReadProperty(
3842 This
->parentStorage
,
3846 if (currentProperty
->previousProperty
== childProperty
)
3847 return PROPERTY_RELATION_PREVIOUS
;
3849 else if (currentProperty
->nextProperty
== childProperty
)
3850 return PROPERTY_RELATION_NEXT
;
3852 else if (currentProperty
->dirProperty
== childProperty
)
3853 return PROPERTY_RELATION_DIR
;
3856 * Push the next search node in the search stack.
3858 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
3861 * continue the iteration.
3863 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3866 return PROPERTY_NULL
;
3869 ULONG
IEnumSTATSTGImpl_FindProperty(
3870 IEnumSTATSTGImpl
* This
,
3871 const OLECHAR
* lpszPropName
,
3872 StgProperty
* currentProperty
)
3874 ULONG currentSearchNode
;
3877 * Start with the node at the top of the stack.
3879 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3881 while (currentSearchNode
!=PROPERTY_NULL
)
3884 * Remove the top node from the stack
3886 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3889 * Read the property from the storage.
3891 StorageImpl_ReadProperty(This
->parentStorage
,
3895 if ( propertyNameCmp(
3896 (const OLECHAR
*)currentProperty
->name
,
3897 (const OLECHAR
*)lpszPropName
) == 0)
3898 return currentSearchNode
;
3901 * Push the next search node in the search stack.
3903 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
3906 * continue the iteration.
3908 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3911 return PROPERTY_NULL
;
3914 void IEnumSTATSTGImpl_PushSearchNode(
3915 IEnumSTATSTGImpl
* This
,
3918 StgProperty rootProperty
;
3919 BOOL readSuccessful
;
3922 * First, make sure we're not trying to push an unexisting node.
3924 if (nodeToPush
==PROPERTY_NULL
)
3928 * First push the node to the stack
3930 if (This
->stackSize
== This
->stackMaxSize
)
3932 This
->stackMaxSize
+= ENUMSTATSGT_SIZE_INCREMENT
;
3934 This
->stackToVisit
= HeapReAlloc(
3938 sizeof(ULONG
) * This
->stackMaxSize
);
3941 This
->stackToVisit
[This
->stackSize
] = nodeToPush
;
3945 * Read the root property from the storage.
3947 readSuccessful
= StorageImpl_ReadProperty(
3948 This
->parentStorage
,
3954 assert(rootProperty
.sizeOfNameString
!=0);
3957 * Push the previous search node in the search stack.
3959 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.previousProperty
);
3963 ULONG
IEnumSTATSTGImpl_PopSearchNode(
3964 IEnumSTATSTGImpl
* This
,
3969 if (This
->stackSize
== 0)
3970 return PROPERTY_NULL
;
3972 topNode
= This
->stackToVisit
[This
->stackSize
-1];
3981 * Virtual function table for the IEnumSTATSTGImpl class.
3983 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
3985 IEnumSTATSTGImpl_QueryInterface
,
3986 IEnumSTATSTGImpl_AddRef
,
3987 IEnumSTATSTGImpl_Release
,
3988 IEnumSTATSTGImpl_Next
,
3989 IEnumSTATSTGImpl_Skip
,
3990 IEnumSTATSTGImpl_Reset
,
3991 IEnumSTATSTGImpl_Clone
3994 /******************************************************************************
3995 ** IEnumSTATSTGImpl implementation
3998 IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
3999 StorageImpl
* parentStorage
,
4000 ULONG firstPropertyNode
)
4002 IEnumSTATSTGImpl
* newEnumeration
;
4004 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
4006 if (newEnumeration
!=0)
4009 * Set-up the virtual function table and reference count.
4011 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
4012 newEnumeration
->ref
= 0;
4015 * We want to nail-down the reference to the storage in case the
4016 * enumeration out-lives the storage in the client application.
4018 newEnumeration
->parentStorage
= parentStorage
;
4019 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
4021 newEnumeration
->firstPropertyNode
= firstPropertyNode
;
4024 * Initialize the search stack
4026 newEnumeration
->stackSize
= 0;
4027 newEnumeration
->stackMaxSize
= ENUMSTATSGT_SIZE_INCREMENT
;
4028 newEnumeration
->stackToVisit
=
4029 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
)*ENUMSTATSGT_SIZE_INCREMENT
);
4032 * Make sure the current node of the iterator is the first one.
4034 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
4037 return newEnumeration
;
4041 * Virtual function table for the Storage32InternalImpl class.
4043 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
4045 StorageBaseImpl_QueryInterface
,
4046 StorageBaseImpl_AddRef
,
4047 StorageBaseImpl_Release
,
4048 StorageBaseImpl_CreateStream
,
4049 StorageBaseImpl_OpenStream
,
4050 StorageImpl_CreateStorage
,
4051 StorageBaseImpl_OpenStorage
,
4053 StorageImpl_MoveElementTo
,
4054 StorageInternalImpl_Commit
,
4055 StorageInternalImpl_Revert
,
4056 StorageBaseImpl_EnumElements
,
4057 StorageImpl_DestroyElement
,
4058 StorageBaseImpl_RenameElement
,
4059 StorageImpl_SetElementTimes
,
4060 StorageBaseImpl_SetClass
,
4061 StorageImpl_SetStateBits
,
4062 StorageBaseImpl_Stat
4065 /******************************************************************************
4066 ** Storage32InternalImpl implementation
4069 StorageInternalImpl
* StorageInternalImpl_Construct(
4070 StorageImpl
* ancestorStorage
,
4072 ULONG rootPropertyIndex
)
4074 StorageInternalImpl
* newStorage
;
4077 * Allocate space for the new storage object
4079 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl
));
4083 memset(newStorage
, 0, sizeof(StorageInternalImpl
));
4086 * Initialize the virtual function table.
4088 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
4089 newStorage
->base
.v_destructor
= &StorageInternalImpl_Destroy
;
4090 newStorage
->base
.openFlags
= openFlags
;
4093 * Keep the ancestor storage pointer and nail a reference to it.
4095 newStorage
->base
.ancestorStorage
= ancestorStorage
;
4096 StorageBaseImpl_AddRef((IStorage
*)(newStorage
->base
.ancestorStorage
));
4099 * Keep the index of the root property set for this storage,
4101 newStorage
->base
.rootPropertySetIndex
= rootPropertyIndex
;
4109 /******************************************************************************
4110 ** StorageUtl implementation
4113 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
4117 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
4118 *value
= le16toh(tmp
);
4121 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
4123 value
= htole16(value
);
4124 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
4127 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
4131 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
4132 *value
= le32toh(tmp
);
4135 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
4137 value
= htole32(value
);
4138 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
4141 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
4142 ULARGE_INTEGER
* value
)
4144 #ifdef WORDS_BIGENDIAN
4147 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4148 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
4149 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
4151 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4155 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
4156 const ULARGE_INTEGER
*value
)
4158 #ifdef WORDS_BIGENDIAN
4161 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
4162 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
4163 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
4165 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
4169 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
4171 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
4172 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
4173 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
4175 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
4178 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
4180 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
4181 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
4182 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
4184 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
4187 void StorageUtl_CopyPropertyToSTATSTG(
4188 STATSTG
* destination
,
4189 StgProperty
* source
,
4193 * The copy of the string occurs only when the flag is not set
4195 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
4196 (source
->name
== NULL
) ||
4197 (source
->name
[0] == 0) )
4199 destination
->pwcsName
= 0;
4203 destination
->pwcsName
=
4204 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
4206 strcpyW((LPWSTR
)destination
->pwcsName
, source
->name
);
4209 switch (source
->propertyType
)
4211 case PROPTYPE_STORAGE
:
4213 destination
->type
= STGTY_STORAGE
;
4215 case PROPTYPE_STREAM
:
4216 destination
->type
= STGTY_STREAM
;
4219 destination
->type
= STGTY_STREAM
;
4223 destination
->cbSize
= source
->size
;
4225 currentReturnStruct->mtime = {0}; TODO
4226 currentReturnStruct->ctime = {0};
4227 currentReturnStruct->atime = {0};
4229 destination
->grfMode
= 0;
4230 destination
->grfLocksSupported
= 0;
4231 destination
->clsid
= source
->propertyUniqueID
;
4232 destination
->grfStateBits
= 0;
4233 destination
->reserved
= 0;
4236 /******************************************************************************
4237 ** BlockChainStream implementation
4240 BlockChainStream
* BlockChainStream_Construct(
4241 StorageImpl
* parentStorage
,
4242 ULONG
* headOfStreamPlaceHolder
,
4243 ULONG propertyIndex
)
4245 BlockChainStream
* newStream
;
4248 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
4250 newStream
->parentStorage
= parentStorage
;
4251 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
4252 newStream
->ownerPropertyIndex
= propertyIndex
;
4253 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
4254 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
4255 newStream
->numBlocks
= 0;
4257 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
4259 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4261 newStream
->numBlocks
++;
4262 newStream
->tailIndex
= blockIndex
;
4264 if(FAILED(StorageImpl_GetNextBlockInChain(
4269 HeapFree(GetProcessHeap(), 0, newStream
);
4277 void BlockChainStream_Destroy(BlockChainStream
* This
)
4279 HeapFree(GetProcessHeap(), 0, This
);
4282 /******************************************************************************
4283 * BlockChainStream_GetHeadOfChain
4285 * Returns the head of this stream chain.
4286 * Some special chains don't have properties, their heads are kept in
4287 * This->headOfStreamPlaceHolder.
4290 ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
4292 StgProperty chainProperty
;
4293 BOOL readSuccessful
;
4295 if (This
->headOfStreamPlaceHolder
!= 0)
4296 return *(This
->headOfStreamPlaceHolder
);
4298 if (This
->ownerPropertyIndex
!= PROPERTY_NULL
)
4300 readSuccessful
= StorageImpl_ReadProperty(
4301 This
->parentStorage
,
4302 This
->ownerPropertyIndex
,
4307 return chainProperty
.startingBlock
;
4311 return BLOCK_END_OF_CHAIN
;
4314 /******************************************************************************
4315 * BlockChainStream_GetCount
4317 * Returns the number of blocks that comprises this chain.
4318 * This is not the size of the stream as the last block may not be full!
4321 ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
4326 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4328 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4332 if(FAILED(StorageImpl_GetNextBlockInChain(
4333 This
->parentStorage
,
4342 /******************************************************************************
4343 * BlockChainStream_ReadAt
4345 * Reads a specified number of bytes from this chain at the specified offset.
4346 * bytesRead may be NULL.
4347 * Failure will be returned if the specified number of bytes has not been read.
4349 BOOL
BlockChainStream_ReadAt(BlockChainStream
* This
,
4350 ULARGE_INTEGER offset
,
4355 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4356 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4357 ULONG bytesToReadInBuffer
;
4360 BYTE
* bigBlockBuffer
;
4363 * Find the first block in the stream that contains part of the buffer.
4365 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4366 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4367 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4369 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4370 This
->lastBlockNoInSequence
= blockNoInSequence
;
4374 ULONG temp
= blockNoInSequence
;
4376 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4377 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4378 This
->lastBlockNoInSequence
= temp
;
4381 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4383 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4385 blockNoInSequence
--;
4388 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4391 * Start reading the buffer.
4394 bufferWalker
= buffer
;
4396 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4399 * Calculate how many bytes we can copy from this big block.
4401 bytesToReadInBuffer
=
4402 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4405 * Copy those bytes to the buffer
4408 StorageImpl_GetROBigBlock(This
->parentStorage
, blockIndex
);
4410 memcpy(bufferWalker
, bigBlockBuffer
+ offsetInBlock
, bytesToReadInBuffer
);
4412 StorageImpl_ReleaseBigBlock(This
->parentStorage
, bigBlockBuffer
);
4415 * Step to the next big block.
4417 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4420 bufferWalker
+= bytesToReadInBuffer
;
4421 size
-= bytesToReadInBuffer
;
4422 *bytesRead
+= bytesToReadInBuffer
;
4423 offsetInBlock
= 0; /* There is no offset on the next block */
4430 /******************************************************************************
4431 * BlockChainStream_WriteAt
4433 * Writes the specified number of bytes to this chain at the specified offset.
4434 * bytesWritten may be NULL.
4435 * Will fail if not all specified number of bytes have been written.
4437 BOOL
BlockChainStream_WriteAt(BlockChainStream
* This
,
4438 ULARGE_INTEGER offset
,
4441 ULONG
* bytesWritten
)
4443 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4444 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4447 const BYTE
* bufferWalker
;
4448 BYTE
* bigBlockBuffer
;
4451 * Find the first block in the stream that contains part of the buffer.
4453 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4454 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4455 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4457 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4458 This
->lastBlockNoInSequence
= blockNoInSequence
;
4462 ULONG temp
= blockNoInSequence
;
4464 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4465 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4466 This
->lastBlockNoInSequence
= temp
;
4469 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4471 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4474 blockNoInSequence
--;
4477 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4480 * Here, I'm casting away the constness on the buffer variable
4481 * This is OK since we don't intend to modify that buffer.
4484 bufferWalker
= (const BYTE
*)buffer
;
4486 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4489 * Calculate how many bytes we can copy from this big block.
4492 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4495 * Copy those bytes to the buffer
4497 bigBlockBuffer
= StorageImpl_GetBigBlock(This
->parentStorage
, blockIndex
);
4499 memcpy(bigBlockBuffer
+ offsetInBlock
, bufferWalker
, bytesToWrite
);
4501 StorageImpl_ReleaseBigBlock(This
->parentStorage
, bigBlockBuffer
);
4504 * Step to the next big block.
4506 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4509 bufferWalker
+= bytesToWrite
;
4510 size
-= bytesToWrite
;
4511 *bytesWritten
+= bytesToWrite
;
4512 offsetInBlock
= 0; /* There is no offset on the next block */
4518 /******************************************************************************
4519 * BlockChainStream_Shrink
4521 * Shrinks this chain in the big block depot.
4523 BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
4524 ULARGE_INTEGER newSize
)
4526 ULONG blockIndex
, extraBlock
;
4531 * Reset the last accessed block cache.
4533 This
->lastBlockNoInSequence
= 0xFFFFFFFF;
4534 This
->lastBlockNoInSequenceIndex
= BLOCK_END_OF_CHAIN
;
4537 * Figure out how many blocks are needed to contain the new size
4539 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4541 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4544 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4547 * Go to the new end of chain
4549 while (count
< numBlocks
)
4551 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4557 /* Get the next block before marking the new end */
4558 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4562 /* Mark the new end of chain */
4563 StorageImpl_SetNextBlockInChain(
4564 This
->parentStorage
,
4566 BLOCK_END_OF_CHAIN
);
4568 This
->tailIndex
= blockIndex
;
4569 This
->numBlocks
= numBlocks
;
4572 * Mark the extra blocks as free
4574 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4576 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
,
4579 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
4580 extraBlock
= blockIndex
;
4586 /******************************************************************************
4587 * BlockChainStream_Enlarge
4589 * Grows this chain in the big block depot.
4591 BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
4592 ULARGE_INTEGER newSize
)
4594 ULONG blockIndex
, currentBlock
;
4596 ULONG oldNumBlocks
= 0;
4598 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4601 * Empty chain. Create the head.
4603 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4605 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4606 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
4608 BLOCK_END_OF_CHAIN
);
4610 if (This
->headOfStreamPlaceHolder
!= 0)
4612 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
4616 StgProperty chainProp
;
4617 assert(This
->ownerPropertyIndex
!= PROPERTY_NULL
);
4619 StorageImpl_ReadProperty(
4620 This
->parentStorage
,
4621 This
->ownerPropertyIndex
,
4624 chainProp
.startingBlock
= blockIndex
;
4626 StorageImpl_WriteProperty(
4627 This
->parentStorage
,
4628 This
->ownerPropertyIndex
,
4632 This
->tailIndex
= blockIndex
;
4633 This
->numBlocks
= 1;
4637 * Figure out how many blocks are needed to contain this stream
4639 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4641 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4645 * Go to the current end of chain
4647 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
4649 currentBlock
= blockIndex
;
4651 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4654 currentBlock
= blockIndex
;
4656 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
4661 This
->tailIndex
= currentBlock
;
4664 currentBlock
= This
->tailIndex
;
4665 oldNumBlocks
= This
->numBlocks
;
4668 * Add new blocks to the chain
4670 if (oldNumBlocks
< newNumBlocks
)
4672 while (oldNumBlocks
< newNumBlocks
)
4674 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4676 StorageImpl_SetNextBlockInChain(
4677 This
->parentStorage
,
4681 StorageImpl_SetNextBlockInChain(
4682 This
->parentStorage
,
4684 BLOCK_END_OF_CHAIN
);
4686 currentBlock
= blockIndex
;
4690 This
->tailIndex
= blockIndex
;
4691 This
->numBlocks
= newNumBlocks
;
4697 /******************************************************************************
4698 * BlockChainStream_SetSize
4700 * Sets the size of this stream. The big block depot will be updated.
4701 * The file will grow if we grow the chain.
4703 * TODO: Free the actual blocks in the file when we shrink the chain.
4704 * Currently, the blocks are still in the file. So the file size
4705 * doesn't shrink even if we shrink streams.
4707 BOOL
BlockChainStream_SetSize(
4708 BlockChainStream
* This
,
4709 ULARGE_INTEGER newSize
)
4711 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
4713 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
4716 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
4718 BlockChainStream_Shrink(This
, newSize
);
4722 ULARGE_INTEGER fileSize
=
4723 BIGBLOCKFILE_GetSize(This
->parentStorage
->bigBlockFile
);
4725 ULONG diff
= newSize
.u
.LowPart
- size
.u
.LowPart
;
4728 * Make sure the file stays a multiple of blocksize
4730 if ((diff
% This
->parentStorage
->bigBlockSize
) != 0)
4731 diff
+= (This
->parentStorage
->bigBlockSize
-
4732 (diff
% This
->parentStorage
->bigBlockSize
) );
4734 fileSize
.u
.LowPart
+= diff
;
4735 BIGBLOCKFILE_SetSize(This
->parentStorage
->bigBlockFile
, fileSize
);
4737 BlockChainStream_Enlarge(This
, newSize
);
4743 /******************************************************************************
4744 * BlockChainStream_GetSize
4746 * Returns the size of this chain.
4747 * Will return the block count if this chain doesn't have a property.
4749 ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
4751 StgProperty chainProperty
;
4753 if(This
->headOfStreamPlaceHolder
== NULL
)
4756 * This chain is a data stream read the property and return
4757 * the appropriate size
4759 StorageImpl_ReadProperty(
4760 This
->parentStorage
,
4761 This
->ownerPropertyIndex
,
4764 return chainProperty
.size
;
4769 * this chain is a chain that does not have a property, figure out the
4770 * size by making the product number of used blocks times the
4773 ULARGE_INTEGER result
;
4774 result
.u
.HighPart
= 0;
4777 BlockChainStream_GetCount(This
) *
4778 This
->parentStorage
->bigBlockSize
;
4784 /******************************************************************************
4785 ** SmallBlockChainStream implementation
4788 SmallBlockChainStream
* SmallBlockChainStream_Construct(
4789 StorageImpl
* parentStorage
,
4790 ULONG propertyIndex
)
4792 SmallBlockChainStream
* newStream
;
4794 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
4796 newStream
->parentStorage
= parentStorage
;
4797 newStream
->ownerPropertyIndex
= propertyIndex
;
4802 void SmallBlockChainStream_Destroy(
4803 SmallBlockChainStream
* This
)
4805 HeapFree(GetProcessHeap(), 0, This
);
4808 /******************************************************************************
4809 * SmallBlockChainStream_GetHeadOfChain
4811 * Returns the head of this chain of small blocks.
4813 ULONG
SmallBlockChainStream_GetHeadOfChain(
4814 SmallBlockChainStream
* This
)
4816 StgProperty chainProperty
;
4817 BOOL readSuccessful
;
4819 if (This
->ownerPropertyIndex
)
4821 readSuccessful
= StorageImpl_ReadProperty(
4822 This
->parentStorage
,
4823 This
->ownerPropertyIndex
,
4828 return chainProperty
.startingBlock
;
4833 return BLOCK_END_OF_CHAIN
;
4836 /******************************************************************************
4837 * SmallBlockChainStream_GetNextBlockInChain
4839 * Returns the index of the next small block in this chain.
4842 * - BLOCK_END_OF_CHAIN: end of this chain
4843 * - BLOCK_UNUSED: small block 'blockIndex' is free
4845 HRESULT
SmallBlockChainStream_GetNextBlockInChain(
4846 SmallBlockChainStream
* This
,
4848 ULONG
* nextBlockInChain
)
4850 ULARGE_INTEGER offsetOfBlockInDepot
;
4855 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
4857 offsetOfBlockInDepot
.u
.HighPart
= 0;
4858 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4861 * Read those bytes in the buffer from the small block file.
4863 success
= BlockChainStream_ReadAt(
4864 This
->parentStorage
->smallBlockDepotChain
,
4865 offsetOfBlockInDepot
,
4872 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
4876 return STG_E_READFAULT
;
4879 /******************************************************************************
4880 * SmallBlockChainStream_SetNextBlockInChain
4882 * Writes the index of the next block of the specified block in the small
4884 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4885 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4887 void SmallBlockChainStream_SetNextBlockInChain(
4888 SmallBlockChainStream
* This
,
4892 ULARGE_INTEGER offsetOfBlockInDepot
;
4896 offsetOfBlockInDepot
.u
.HighPart
= 0;
4897 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4899 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
4902 * Read those bytes in the buffer from the small block file.
4904 BlockChainStream_WriteAt(
4905 This
->parentStorage
->smallBlockDepotChain
,
4906 offsetOfBlockInDepot
,
4912 /******************************************************************************
4913 * SmallBlockChainStream_FreeBlock
4915 * Flag small block 'blockIndex' as free in the small block depot.
4917 void SmallBlockChainStream_FreeBlock(
4918 SmallBlockChainStream
* This
,
4921 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
4924 /******************************************************************************
4925 * SmallBlockChainStream_GetNextFreeBlock
4927 * Returns the index of a free small block. The small block depot will be
4928 * enlarged if necessary. The small block chain will also be enlarged if
4931 ULONG
SmallBlockChainStream_GetNextFreeBlock(
4932 SmallBlockChainStream
* This
)
4934 ULARGE_INTEGER offsetOfBlockInDepot
;
4937 ULONG blockIndex
= 0;
4938 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
4939 BOOL success
= TRUE
;
4940 ULONG smallBlocksPerBigBlock
;
4942 offsetOfBlockInDepot
.u
.HighPart
= 0;
4945 * Scan the small block depot for a free block
4947 while (nextBlockIndex
!= BLOCK_UNUSED
)
4949 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4951 success
= BlockChainStream_ReadAt(
4952 This
->parentStorage
->smallBlockDepotChain
,
4953 offsetOfBlockInDepot
,
4959 * If we run out of space for the small block depot, enlarge it
4963 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
4965 if (nextBlockIndex
!= BLOCK_UNUSED
)
4971 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
4973 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
4974 ULONG nextBlock
, newsbdIndex
;
4975 BYTE
* smallBlockDepot
;
4977 nextBlock
= sbdIndex
;
4978 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
4980 sbdIndex
= nextBlock
;
4981 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
, &nextBlock
);
4984 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4985 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
4986 StorageImpl_SetNextBlockInChain(
4987 This
->parentStorage
,
4991 StorageImpl_SetNextBlockInChain(
4992 This
->parentStorage
,
4994 BLOCK_END_OF_CHAIN
);
4997 * Initialize all the small blocks to free
5000 StorageImpl_GetBigBlock(This
->parentStorage
, newsbdIndex
);
5002 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
5003 StorageImpl_ReleaseBigBlock(This
->parentStorage
, smallBlockDepot
);
5008 * We have just created the small block depot.
5010 StgProperty rootProp
;
5014 * Save it in the header
5016 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
5017 StorageImpl_SaveFileHeader(This
->parentStorage
);
5020 * And allocate the first big block that will contain small blocks
5023 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5025 StorageImpl_SetNextBlockInChain(
5026 This
->parentStorage
,
5028 BLOCK_END_OF_CHAIN
);
5030 StorageImpl_ReadProperty(
5031 This
->parentStorage
,
5032 This
->parentStorage
->base
.rootPropertySetIndex
,
5035 rootProp
.startingBlock
= sbStartIndex
;
5036 rootProp
.size
.u
.HighPart
= 0;
5037 rootProp
.size
.u
.LowPart
= This
->parentStorage
->bigBlockSize
;
5039 StorageImpl_WriteProperty(
5040 This
->parentStorage
,
5041 This
->parentStorage
->base
.rootPropertySetIndex
,
5047 smallBlocksPerBigBlock
=
5048 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
5051 * Verify if we have to allocate big blocks to contain small blocks
5053 if (blockIndex
% smallBlocksPerBigBlock
== 0)
5055 StgProperty rootProp
;
5056 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
5058 StorageImpl_ReadProperty(
5059 This
->parentStorage
,
5060 This
->parentStorage
->base
.rootPropertySetIndex
,
5063 if (rootProp
.size
.u
.LowPart
<
5064 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
5066 rootProp
.size
.u
.LowPart
+= This
->parentStorage
->bigBlockSize
;
5068 BlockChainStream_SetSize(
5069 This
->parentStorage
->smallBlockRootChain
,
5072 StorageImpl_WriteProperty(
5073 This
->parentStorage
,
5074 This
->parentStorage
->base
.rootPropertySetIndex
,
5082 /******************************************************************************
5083 * SmallBlockChainStream_ReadAt
5085 * Reads a specified number of bytes from this chain at the specified offset.
5086 * bytesRead may be NULL.
5087 * Failure will be returned if the specified number of bytes has not been read.
5089 BOOL
SmallBlockChainStream_ReadAt(
5090 SmallBlockChainStream
* This
,
5091 ULARGE_INTEGER offset
,
5096 ULARGE_INTEGER offsetInBigBlockFile
;
5097 ULONG blockNoInSequence
=
5098 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5100 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5101 ULONG bytesToReadInBuffer
;
5103 ULONG bytesReadFromBigBlockFile
;
5107 * This should never happen on a small block file.
5109 assert(offset
.u
.HighPart
==0);
5112 * Find the first block in the stream that contains part of the buffer.
5114 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5116 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5118 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5121 blockNoInSequence
--;
5125 * Start reading the buffer.
5128 bufferWalker
= buffer
;
5130 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5133 * Calculate how many bytes we can copy from this small block.
5135 bytesToReadInBuffer
=
5136 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5139 * Calculate the offset of the small block in the small block file.
5141 offsetInBigBlockFile
.u
.HighPart
= 0;
5142 offsetInBigBlockFile
.u
.LowPart
=
5143 blockIndex
* This
->parentStorage
->smallBlockSize
;
5145 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5148 * Read those bytes in the buffer from the small block file.
5150 BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
5151 offsetInBigBlockFile
,
5152 bytesToReadInBuffer
,
5154 &bytesReadFromBigBlockFile
);
5156 assert(bytesReadFromBigBlockFile
== bytesToReadInBuffer
);
5159 * Step to the next big block.
5161 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5163 bufferWalker
+= bytesToReadInBuffer
;
5164 size
-= bytesToReadInBuffer
;
5165 *bytesRead
+= bytesToReadInBuffer
;
5166 offsetInBlock
= 0; /* There is no offset on the next block */
5172 /******************************************************************************
5173 * SmallBlockChainStream_WriteAt
5175 * Writes the specified number of bytes to this chain at the specified offset.
5176 * bytesWritten may be NULL.
5177 * Will fail if not all specified number of bytes have been written.
5179 BOOL
SmallBlockChainStream_WriteAt(
5180 SmallBlockChainStream
* This
,
5181 ULARGE_INTEGER offset
,
5184 ULONG
* bytesWritten
)
5186 ULARGE_INTEGER offsetInBigBlockFile
;
5187 ULONG blockNoInSequence
=
5188 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5190 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5191 ULONG bytesToWriteInBuffer
;
5193 ULONG bytesWrittenFromBigBlockFile
;
5194 const BYTE
* bufferWalker
;
5197 * This should never happen on a small block file.
5199 assert(offset
.u
.HighPart
==0);
5202 * Find the first block in the stream that contains part of the buffer.
5204 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5206 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5208 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5210 blockNoInSequence
--;
5214 * Start writing the buffer.
5216 * Here, I'm casting away the constness on the buffer variable
5217 * This is OK since we don't intend to modify that buffer.
5220 bufferWalker
= (const BYTE
*)buffer
;
5221 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5224 * Calculate how many bytes we can copy to this small block.
5226 bytesToWriteInBuffer
=
5227 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5230 * Calculate the offset of the small block in the small block file.
5232 offsetInBigBlockFile
.u
.HighPart
= 0;
5233 offsetInBigBlockFile
.u
.LowPart
=
5234 blockIndex
* This
->parentStorage
->smallBlockSize
;
5236 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5239 * Write those bytes in the buffer to the small block file.
5241 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockRootChain
,
5242 offsetInBigBlockFile
,
5243 bytesToWriteInBuffer
,
5245 &bytesWrittenFromBigBlockFile
);
5247 assert(bytesWrittenFromBigBlockFile
== bytesToWriteInBuffer
);
5250 * Step to the next big block.
5252 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5255 bufferWalker
+= bytesToWriteInBuffer
;
5256 size
-= bytesToWriteInBuffer
;
5257 *bytesWritten
+= bytesToWriteInBuffer
;
5258 offsetInBlock
= 0; /* There is no offset on the next block */
5264 /******************************************************************************
5265 * SmallBlockChainStream_Shrink
5267 * Shrinks this chain in the small block depot.
5269 BOOL
SmallBlockChainStream_Shrink(
5270 SmallBlockChainStream
* This
,
5271 ULARGE_INTEGER newSize
)
5273 ULONG blockIndex
, extraBlock
;
5277 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5279 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5282 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5285 * Go to the new end of chain
5287 while (count
< numBlocks
)
5289 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5296 * If the count is 0, we have a special case, the head of the chain was
5301 StgProperty chainProp
;
5303 StorageImpl_ReadProperty(This
->parentStorage
,
5304 This
->ownerPropertyIndex
,
5307 chainProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
5309 StorageImpl_WriteProperty(This
->parentStorage
,
5310 This
->ownerPropertyIndex
,
5314 * We start freeing the chain at the head block.
5316 extraBlock
= blockIndex
;
5320 /* Get the next block before marking the new end */
5321 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5325 /* Mark the new end of chain */
5326 SmallBlockChainStream_SetNextBlockInChain(
5329 BLOCK_END_OF_CHAIN
);
5333 * Mark the extra blocks as free
5335 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
5337 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
5340 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
5341 extraBlock
= blockIndex
;
5347 /******************************************************************************
5348 * SmallBlockChainStream_Enlarge
5350 * Grows this chain in the small block depot.
5352 BOOL
SmallBlockChainStream_Enlarge(
5353 SmallBlockChainStream
* This
,
5354 ULARGE_INTEGER newSize
)
5356 ULONG blockIndex
, currentBlock
;
5358 ULONG oldNumBlocks
= 0;
5360 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5365 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5368 StgProperty chainProp
;
5370 StorageImpl_ReadProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
5373 chainProp
.startingBlock
= SmallBlockChainStream_GetNextFreeBlock(This
);
5375 StorageImpl_WriteProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
5378 blockIndex
= chainProp
.startingBlock
;
5379 SmallBlockChainStream_SetNextBlockInChain(
5382 BLOCK_END_OF_CHAIN
);
5385 currentBlock
= blockIndex
;
5388 * Figure out how many blocks are needed to contain this stream
5390 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5392 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5396 * Go to the current end of chain
5398 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5401 currentBlock
= blockIndex
;
5402 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
5407 * Add new blocks to the chain
5409 while (oldNumBlocks
< newNumBlocks
)
5411 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
5412 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
5414 SmallBlockChainStream_SetNextBlockInChain(
5417 BLOCK_END_OF_CHAIN
);
5419 currentBlock
= blockIndex
;
5426 /******************************************************************************
5427 * SmallBlockChainStream_GetCount
5429 * Returns the number of blocks that comprises this chain.
5430 * This is not the size of this chain as the last block may not be full!
5432 ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
5437 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5439 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5443 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5450 /******************************************************************************
5451 * SmallBlockChainStream_SetSize
5453 * Sets the size of this stream.
5454 * The file will grow if we grow the chain.
5456 * TODO: Free the actual blocks in the file when we shrink the chain.
5457 * Currently, the blocks are still in the file. So the file size
5458 * doesn't shrink even if we shrink streams.
5460 BOOL
SmallBlockChainStream_SetSize(
5461 SmallBlockChainStream
* This
,
5462 ULARGE_INTEGER newSize
)
5464 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
5466 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
5469 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
5471 SmallBlockChainStream_Shrink(This
, newSize
);
5475 SmallBlockChainStream_Enlarge(This
, newSize
);
5481 /******************************************************************************
5482 * SmallBlockChainStream_GetSize
5484 * Returns the size of this chain.
5486 ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
5488 StgProperty chainProperty
;
5490 StorageImpl_ReadProperty(
5491 This
->parentStorage
,
5492 This
->ownerPropertyIndex
,
5495 return chainProperty
.size
;
5498 /******************************************************************************
5499 * StgCreateDocfile [OLE32.@]
5501 HRESULT WINAPI
StgCreateDocfile(
5505 IStorage
**ppstgOpen
)
5507 StorageImpl
* newStorage
= 0;
5508 HANDLE hFile
= INVALID_HANDLE_VALUE
;
5509 HRESULT hr
= STG_E_INVALIDFLAG
;
5513 DWORD fileAttributes
;
5514 WCHAR tempFileName
[MAX_PATH
];
5516 TRACE("(%s, %lx, %ld, %p)\n",
5517 debugstr_w(pwcsName
), grfMode
,
5518 reserved
, ppstgOpen
);
5521 * Validate the parameters
5524 return STG_E_INVALIDPOINTER
;
5526 return STG_E_INVALIDPARAMETER
;
5529 * Validate the STGM flags
5531 if ( FAILED( validateSTGM(grfMode
) ))
5534 /* StgCreateDocFile always opens for write */
5535 switch(STGM_ACCESS_MODE(grfMode
))
5538 case STGM_READWRITE
:
5544 /* can't share write */
5545 switch(STGM_SHARE_MODE(grfMode
))
5547 case STGM_SHARE_EXCLUSIVE
:
5548 case STGM_SHARE_DENY_WRITE
:
5554 /* shared reading requires transacted mode */
5555 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
5556 !(grfMode
&STGM_TRANSACTED
) )
5560 * Generate a unique name.
5564 WCHAR tempPath
[MAX_PATH
];
5565 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
5567 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
5570 memset(tempPath
, 0, sizeof(tempPath
));
5571 memset(tempFileName
, 0, sizeof(tempFileName
));
5573 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
5576 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
5577 pwcsName
= tempFileName
;
5580 hr
= STG_E_INSUFFICIENTMEMORY
;
5584 creationMode
= TRUNCATE_EXISTING
;
5588 creationMode
= GetCreationModeFromSTGM(grfMode
);
5592 * Interpret the STGM value grfMode
5594 shareMode
= GetShareModeFromSTGM(grfMode
);
5595 accessMode
= GetAccessModeFromSTGM(grfMode
);
5597 if (grfMode
& STGM_DELETEONRELEASE
)
5598 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
5600 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
5602 if (grfMode
& STGM_TRANSACTED
)
5603 FIXME("Transacted mode not implemented.\n");
5606 * Initialize the "out" parameter.
5610 hFile
= CreateFileW(pwcsName
,
5618 if (hFile
== INVALID_HANDLE_VALUE
)
5620 if(GetLastError() == ERROR_FILE_EXISTS
)
5621 hr
= STG_E_FILEALREADYEXISTS
;
5628 * Allocate and initialize the new IStorage32object.
5630 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5632 if (newStorage
== 0)
5634 hr
= STG_E_INSUFFICIENTMEMORY
;
5638 hr
= StorageImpl_Construct(
5649 HeapFree(GetProcessHeap(), 0, newStorage
);
5654 * Get an "out" pointer for the caller.
5656 hr
= StorageBaseImpl_QueryInterface(
5657 (IStorage
*)newStorage
,
5658 (REFIID
)&IID_IStorage
,
5661 TRACE("<-- %p r = %08lx\n", *ppstgOpen
, hr
);
5666 /******************************************************************************
5667 * StgCreateStorageEx [OLE32.@]
5669 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
5671 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
5672 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
5674 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
5676 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5677 return STG_E_INVALIDPARAMETER
;
5680 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
5682 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5683 return STG_E_INVALIDPARAMETER
;
5686 if (stgfmt
== STGFMT_FILE
)
5688 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5689 return STG_E_INVALIDPARAMETER
;
5692 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
5694 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5695 return StgCreateDocfile(pwcsName
, grfMode
, 0, (IStorage
**)ppObjectOpen
);
5698 ERR("Invalid stgfmt argument\n");
5699 return STG_E_INVALIDPARAMETER
;
5702 /******************************************************************************
5703 * StgCreatePropSetStg [OLE32.@]
5705 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
5706 IPropertySetStorage
**ppPropSetStg
)
5710 TRACE("(%p, 0x%lx, %p): stub\n", pstg
, reserved
, ppPropSetStg
);
5712 hr
= STG_E_INVALIDPARAMETER
;
5714 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
5715 (void**)ppPropSetStg
);
5719 /******************************************************************************
5720 * StgOpenStorageEx [OLE32.@]
5722 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
5724 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
5725 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
5727 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
5729 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5730 return STG_E_INVALIDPARAMETER
;
5733 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
5735 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5736 return STG_E_INVALIDPARAMETER
;
5739 if (stgfmt
== STGFMT_FILE
)
5741 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5742 return STG_E_INVALIDPARAMETER
;
5745 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
|| stgfmt
== STGFMT_ANY
)
5747 if (stgfmt
== STGFMT_ANY
)
5748 WARN("STGFMT_ANY assuming storage\n");
5749 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5750 return StgOpenStorage(pwcsName
, NULL
, grfMode
, (SNB
)NULL
, 0, (IStorage
**)ppObjectOpen
);
5753 ERR("Invalid stgfmt argument\n");
5754 return STG_E_INVALIDPARAMETER
;
5758 /******************************************************************************
5759 * StgOpenStorage [OLE32.@]
5761 HRESULT WINAPI
StgOpenStorage(
5762 const OLECHAR
*pwcsName
,
5763 IStorage
*pstgPriority
,
5767 IStorage
**ppstgOpen
)
5769 StorageImpl
* newStorage
= 0;
5774 WCHAR fullname
[MAX_PATH
];
5777 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5778 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
5779 snbExclude
, reserved
, ppstgOpen
);
5782 * Perform sanity checks
5786 hr
= STG_E_INVALIDNAME
;
5792 hr
= STG_E_INVALIDPOINTER
;
5798 hr
= STG_E_INVALIDPARAMETER
;
5803 * Validate the sharing mode
5805 if (!(grfMode
& STGM_TRANSACTED
))
5806 switch(STGM_SHARE_MODE(grfMode
))
5808 case STGM_SHARE_EXCLUSIVE
:
5809 case STGM_SHARE_DENY_WRITE
:
5812 hr
= STG_E_INVALIDFLAG
;
5817 * Validate the STGM flags
5819 if ( FAILED( validateSTGM(grfMode
) ) ||
5820 (grfMode
&STGM_CREATE
))
5822 hr
= STG_E_INVALIDFLAG
;
5826 /* shared reading requires transacted mode */
5827 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
5828 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
5829 !(grfMode
&STGM_TRANSACTED
) )
5831 hr
= STG_E_INVALIDFLAG
;
5836 * Interpret the STGM value grfMode
5838 shareMode
= GetShareModeFromSTGM(grfMode
);
5839 accessMode
= GetAccessModeFromSTGM(grfMode
);
5842 * Initialize the "out" parameter.
5846 hFile
= CreateFileW( pwcsName
,
5851 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
5854 if (hFile
==INVALID_HANDLE_VALUE
)
5856 DWORD last_error
= GetLastError();
5862 case ERROR_FILE_NOT_FOUND
:
5863 hr
= STG_E_FILENOTFOUND
;
5866 case ERROR_PATH_NOT_FOUND
:
5867 hr
= STG_E_PATHNOTFOUND
;
5870 case ERROR_ACCESS_DENIED
:
5871 case ERROR_WRITE_PROTECT
:
5872 hr
= STG_E_ACCESSDENIED
;
5875 case ERROR_SHARING_VIOLATION
:
5876 hr
= STG_E_SHAREVIOLATION
;
5887 * Refuse to open the file if it's too small to be a structured storage file
5888 * FIXME: verify the file when reading instead of here
5890 length
= GetFileSize(hFile
, NULL
);
5894 hr
= STG_E_FILEALREADYEXISTS
;
5899 * Allocate and initialize the new IStorage32object.
5901 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5903 if (newStorage
== 0)
5905 hr
= STG_E_INSUFFICIENTMEMORY
;
5909 /* if the file's length was zero, initialize the storage */
5910 hr
= StorageImpl_Construct(
5921 HeapFree(GetProcessHeap(), 0, newStorage
);
5923 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5925 if(hr
== STG_E_INVALIDHEADER
)
5926 hr
= STG_E_FILEALREADYEXISTS
;
5930 /* prepare the file name string given in lieu of the root property name */
5931 GetFullPathNameW(pwcsName
, MAX_PATH
, fullname
, NULL
);
5932 memcpy(newStorage
->filename
, fullname
, PROPERTY_NAME_BUFFER_LEN
);
5933 newStorage
->filename
[PROPERTY_NAME_BUFFER_LEN
-1] = '\0';
5936 * Get an "out" pointer for the caller.
5938 hr
= StorageBaseImpl_QueryInterface(
5939 (IStorage
*)newStorage
,
5940 (REFIID
)&IID_IStorage
,
5944 TRACE("<-- %08lx, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
5948 /******************************************************************************
5949 * StgCreateDocfileOnILockBytes [OLE32.@]
5951 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
5955 IStorage
** ppstgOpen
)
5957 StorageImpl
* newStorage
= 0;
5961 * Validate the parameters
5963 if ((ppstgOpen
== 0) || (plkbyt
== 0))
5964 return STG_E_INVALIDPOINTER
;
5967 * Allocate and initialize the new IStorage object.
5969 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5971 if (newStorage
== 0)
5972 return STG_E_INSUFFICIENTMEMORY
;
5974 hr
= StorageImpl_Construct(
5985 HeapFree(GetProcessHeap(), 0, newStorage
);
5990 * Get an "out" pointer for the caller.
5992 hr
= StorageBaseImpl_QueryInterface(
5993 (IStorage
*)newStorage
,
5994 (REFIID
)&IID_IStorage
,
6000 /******************************************************************************
6001 * StgOpenStorageOnILockBytes [OLE32.@]
6003 HRESULT WINAPI
StgOpenStorageOnILockBytes(
6005 IStorage
*pstgPriority
,
6009 IStorage
**ppstgOpen
)
6011 StorageImpl
* newStorage
= 0;
6015 * Perform a sanity check
6017 if ((plkbyt
== 0) || (ppstgOpen
== 0))
6018 return STG_E_INVALIDPOINTER
;
6021 * Validate the STGM flags
6023 if ( FAILED( validateSTGM(grfMode
) ))
6024 return STG_E_INVALIDFLAG
;
6027 * Initialize the "out" parameter.
6032 * Allocate and initialize the new IStorage object.
6034 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
6036 if (newStorage
== 0)
6037 return STG_E_INSUFFICIENTMEMORY
;
6039 hr
= StorageImpl_Construct(
6050 HeapFree(GetProcessHeap(), 0, newStorage
);
6055 * Get an "out" pointer for the caller.
6057 hr
= StorageBaseImpl_QueryInterface(
6058 (IStorage
*)newStorage
,
6059 (REFIID
)&IID_IStorage
,
6065 /******************************************************************************
6066 * StgSetTimes [ole32.@]
6067 * StgSetTimes [OLE32.@]
6071 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
6072 FILETIME
const *patime
, FILETIME
const *pmtime
)
6074 IStorage
*stg
= NULL
;
6077 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
6079 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
6083 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
6084 IStorage_Release(stg
);
6090 /******************************************************************************
6091 * StgIsStorageILockBytes [OLE32.@]
6093 * Determines if the ILockBytes contains a storage object.
6095 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
6098 ULARGE_INTEGER offset
;
6100 offset
.u
.HighPart
= 0;
6101 offset
.u
.LowPart
= 0;
6103 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
6105 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
6111 /******************************************************************************
6112 * WriteClassStg [OLE32.@]
6114 * This method will store the specified CLSID in the specified storage object
6116 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
6122 hRes
= IStorage_SetClass(pStg
, rclsid
);
6127 /***********************************************************************
6128 * ReadClassStg (OLE32.@)
6130 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
6132 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
6142 * read a STATSTG structure (contains the clsid) from the storage
6144 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_DEFAULT
);
6147 *pclsid
=pstatstg
.clsid
;
6152 /***********************************************************************
6153 * OleLoadFromStream (OLE32.@)
6155 * This function loads an object from stream
6157 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
6161 LPPERSISTSTREAM xstm
;
6163 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
6165 res
=ReadClassStm(pStm
,&clsid
);
6166 if (!SUCCEEDED(res
))
6168 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
6169 if (!SUCCEEDED(res
))
6171 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
6172 if (!SUCCEEDED(res
)) {
6173 IUnknown_Release((IUnknown
*)*ppvObj
);
6176 res
=IPersistStream_Load(xstm
,pStm
);
6177 IPersistStream_Release(xstm
);
6178 /* FIXME: all refcounts ok at this point? I think they should be:
6181 * xstm : 0 (released)
6186 /***********************************************************************
6187 * OleSaveToStream (OLE32.@)
6189 * This function saves an object with the IPersistStream interface on it
6190 * to the specified stream.
6192 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
6198 TRACE("(%p,%p)\n",pPStm
,pStm
);
6200 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
6202 if (SUCCEEDED(res
)){
6204 res
=WriteClassStm(pStm
,&clsid
);
6208 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
6211 TRACE("Finished Save\n");
6215 /****************************************************************************
6216 * This method validate a STGM parameter that can contain the values below
6218 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6219 * The stgm values contained in 0xffff0000 are bitmasks.
6221 * STGM_DIRECT 0x00000000
6222 * STGM_TRANSACTED 0x00010000
6223 * STGM_SIMPLE 0x08000000
6225 * STGM_READ 0x00000000
6226 * STGM_WRITE 0x00000001
6227 * STGM_READWRITE 0x00000002
6229 * STGM_SHARE_DENY_NONE 0x00000040
6230 * STGM_SHARE_DENY_READ 0x00000030
6231 * STGM_SHARE_DENY_WRITE 0x00000020
6232 * STGM_SHARE_EXCLUSIVE 0x00000010
6234 * STGM_PRIORITY 0x00040000
6235 * STGM_DELETEONRELEASE 0x04000000
6237 * STGM_CREATE 0x00001000
6238 * STGM_CONVERT 0x00020000
6239 * STGM_FAILIFTHERE 0x00000000
6241 * STGM_NOSCRATCH 0x00100000
6242 * STGM_NOSNAPSHOT 0x00200000
6244 static HRESULT
validateSTGM(DWORD stgm
)
6246 DWORD access
= STGM_ACCESS_MODE(stgm
);
6247 DWORD share
= STGM_SHARE_MODE(stgm
);
6248 DWORD create
= STGM_CREATE_MODE(stgm
);
6250 if (stgm
&~STGM_KNOWN_FLAGS
)
6252 ERR("unknown flags %08lx\n", stgm
);
6260 case STGM_READWRITE
:
6268 case STGM_SHARE_DENY_NONE
:
6269 case STGM_SHARE_DENY_READ
:
6270 case STGM_SHARE_DENY_WRITE
:
6271 case STGM_SHARE_EXCLUSIVE
:
6280 case STGM_FAILIFTHERE
:
6287 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6289 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
6293 * STGM_CREATE | STGM_CONVERT
6294 * if both are false, STGM_FAILIFTHERE is set to TRUE
6296 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
6300 * STGM_NOSCRATCH requires STGM_TRANSACTED
6302 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
6306 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6307 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6309 if ( (stgm
& STGM_NOSNAPSHOT
) &&
6310 (!(stgm
& STGM_TRANSACTED
) ||
6311 share
== STGM_SHARE_EXCLUSIVE
||
6312 share
== STGM_SHARE_DENY_WRITE
) )
6318 /****************************************************************************
6319 * GetShareModeFromSTGM
6321 * This method will return a share mode flag from a STGM value.
6322 * The STGM value is assumed valid.
6324 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
6326 switch (STGM_SHARE_MODE(stgm
))
6328 case STGM_SHARE_DENY_NONE
:
6329 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6330 case STGM_SHARE_DENY_READ
:
6331 return FILE_SHARE_WRITE
;
6332 case STGM_SHARE_DENY_WRITE
:
6333 return FILE_SHARE_READ
;
6334 case STGM_SHARE_EXCLUSIVE
:
6337 ERR("Invalid share mode!\n");
6342 /****************************************************************************
6343 * GetAccessModeFromSTGM
6345 * This method will return an access mode flag from a STGM value.
6346 * The STGM value is assumed valid.
6348 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
6350 switch (STGM_ACCESS_MODE(stgm
))
6353 return GENERIC_READ
;
6355 case STGM_READWRITE
:
6356 return GENERIC_READ
| GENERIC_WRITE
;
6358 ERR("Invalid access mode!\n");
6363 /****************************************************************************
6364 * GetCreationModeFromSTGM
6366 * This method will return a creation mode flag from a STGM value.
6367 * The STGM value is assumed valid.
6369 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
6371 switch(STGM_CREATE_MODE(stgm
))
6374 return CREATE_ALWAYS
;
6376 FIXME("STGM_CONVERT not implemented!\n");
6378 case STGM_FAILIFTHERE
:
6381 ERR("Invalid create mode!\n");
6387 /*************************************************************************
6388 * OLECONVERT_LoadOLE10 [Internal]
6390 * Loads the OLE10 STREAM to memory
6393 * pOleStream [I] The OLESTREAM
6394 * pData [I] Data Structure for the OLESTREAM Data
6398 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6399 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6402 * This function is used by OleConvertOLESTREAMToIStorage only.
6404 * Memory allocated for pData must be freed by the caller
6406 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
6409 HRESULT hRes
= S_OK
;
6413 pData
->pData
= NULL
;
6414 pData
->pstrOleObjFileName
= (CHAR
*) NULL
;
6416 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
6419 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6420 if(dwSize
!= sizeof(pData
->dwOleID
))
6422 hRes
= CONVERT10_E_OLESTREAM_GET
;
6424 else if(pData
->dwOleID
!= OLESTREAM_ID
)
6426 hRes
= CONVERT10_E_OLESTREAM_FMT
;
6437 /* Get the TypeID...more info needed for this field */
6438 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6439 if(dwSize
!= sizeof(pData
->dwTypeID
))
6441 hRes
= CONVERT10_E_OLESTREAM_GET
;
6446 if(pData
->dwTypeID
!= 0)
6448 /* Get the length of the OleTypeName */
6449 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6450 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6452 hRes
= CONVERT10_E_OLESTREAM_GET
;
6457 if(pData
->dwOleTypeNameLength
> 0)
6459 /* Get the OleTypeName */
6460 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6461 if(dwSize
!= pData
->dwOleTypeNameLength
)
6463 hRes
= CONVERT10_E_OLESTREAM_GET
;
6469 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
6470 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
6472 hRes
= CONVERT10_E_OLESTREAM_GET
;
6476 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
6477 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
6478 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
6479 if(pData
->pstrOleObjFileName
)
6481 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)(pData
->pstrOleObjFileName
),pData
->dwOleObjFileNameLength
);
6482 if(dwSize
!= pData
->dwOleObjFileNameLength
)
6484 hRes
= CONVERT10_E_OLESTREAM_GET
;
6488 hRes
= CONVERT10_E_OLESTREAM_GET
;
6493 /* Get the Width of the Metafile */
6494 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6495 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6497 hRes
= CONVERT10_E_OLESTREAM_GET
;
6501 /* Get the Height of the Metafile */
6502 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6503 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6505 hRes
= CONVERT10_E_OLESTREAM_GET
;
6511 /* Get the Length of the Data */
6512 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6513 if(dwSize
!= sizeof(pData
->dwDataLength
))
6515 hRes
= CONVERT10_E_OLESTREAM_GET
;
6519 if(hRes
== S_OK
) /* I don't know what is this 8 byts information is we have to figure out */
6521 if(!bStrem1
) /* if it is a second OLE stream data */
6523 pData
->dwDataLength
-= 8;
6524 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)(pData
->strUnknown
), sizeof(pData
->strUnknown
));
6525 if(dwSize
!= sizeof(pData
->strUnknown
))
6527 hRes
= CONVERT10_E_OLESTREAM_GET
;
6533 if(pData
->dwDataLength
> 0)
6535 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
6537 /* Get Data (ex. IStorage, Metafile, or BMP) */
6540 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
6541 if(dwSize
!= pData
->dwDataLength
)
6543 hRes
= CONVERT10_E_OLESTREAM_GET
;
6548 hRes
= CONVERT10_E_OLESTREAM_GET
;
6557 /*************************************************************************
6558 * OLECONVERT_SaveOLE10 [Internal]
6560 * Saves the OLE10 STREAM From memory
6563 * pData [I] Data Structure for the OLESTREAM Data
6564 * pOleStream [I] The OLESTREAM to save
6568 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6571 * This function is used by OleConvertIStorageToOLESTREAM only.
6574 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
6577 HRESULT hRes
= S_OK
;
6581 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6582 if(dwSize
!= sizeof(pData
->dwOleID
))
6584 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6589 /* Set the TypeID */
6590 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6591 if(dwSize
!= sizeof(pData
->dwTypeID
))
6593 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6597 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
6599 /* Set the Length of the OleTypeName */
6600 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6601 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6603 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6608 if(pData
->dwOleTypeNameLength
> 0)
6610 /* Set the OleTypeName */
6611 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6612 if(dwSize
!= pData
->dwOleTypeNameLength
)
6614 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6621 /* Set the width of the Metafile */
6622 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6623 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6625 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6631 /* Set the height of the Metafile */
6632 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6633 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6635 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6641 /* Set the length of the Data */
6642 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6643 if(dwSize
!= sizeof(pData
->dwDataLength
))
6645 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6651 if(pData
->dwDataLength
> 0)
6653 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6654 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
6655 if(dwSize
!= pData
->dwDataLength
)
6657 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6665 /*************************************************************************
6666 * OLECONVERT_GetOLE20FromOLE10[Internal]
6668 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6669 * opens it, and copies the content to the dest IStorage for
6670 * OleConvertOLESTREAMToIStorage
6674 * pDestStorage [I] The IStorage to copy the data to
6675 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6676 * nBufferLength [I] The size of the buffer
6685 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, BYTE
*pBuffer
, DWORD nBufferLength
)
6689 IStorage
*pTempStorage
;
6690 DWORD dwNumOfBytesWritten
;
6691 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6692 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6694 /* Create a temp File */
6695 GetTempPathW(MAX_PATH
, wstrTempDir
);
6696 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6697 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
6699 if(hFile
!= INVALID_HANDLE_VALUE
)
6701 /* Write IStorage Data to File */
6702 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
6705 /* Open and copy temp storage to the Dest Storage */
6706 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
6709 hRes
= StorageImpl_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
6710 StorageBaseImpl_Release(pTempStorage
);
6712 DeleteFileW(wstrTempFile
);
6717 /*************************************************************************
6718 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6720 * Saves the OLE10 STREAM From memory
6723 * pStorage [I] The Src IStorage to copy
6724 * pData [I] The Dest Memory to write to.
6727 * The size in bytes allocated for pData
6730 * Memory allocated for pData must be freed by the caller
6732 * Used by OleConvertIStorageToOLESTREAM only.
6735 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
6739 DWORD nDataLength
= 0;
6740 IStorage
*pTempStorage
;
6741 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6742 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6746 /* Create temp Storage */
6747 GetTempPathW(MAX_PATH
, wstrTempDir
);
6748 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6749 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
6753 /* Copy Src Storage to the Temp Storage */
6754 StorageImpl_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
6755 StorageBaseImpl_Release(pTempStorage
);
6757 /* Open Temp Storage as a file and copy to memory */
6758 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
6759 if(hFile
!= INVALID_HANDLE_VALUE
)
6761 nDataLength
= GetFileSize(hFile
, NULL
);
6762 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
6763 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
6766 DeleteFileW(wstrTempFile
);
6771 /*************************************************************************
6772 * OLECONVERT_CreateOleStream [Internal]
6774 * Creates the "\001OLE" stream in the IStorage if necessary.
6777 * pStorage [I] Dest storage to create the stream in
6783 * This function is used by OleConvertOLESTREAMToIStorage only.
6785 * This stream is still unknown, MS Word seems to have extra data
6786 * but since the data is stored in the OLESTREAM there should be
6787 * no need to recreate the stream. If the stream is manually
6788 * deleted it will create it with this default data.
6791 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
6795 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
6796 BYTE pOleStreamHeader
[] =
6798 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6799 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6800 0x00, 0x00, 0x00, 0x00
6803 /* Create stream if not present */
6804 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
6805 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
6809 /* Write default Data */
6810 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
6811 IStream_Release(pStream
);
6815 /* write a string to a stream, preceded by its length */
6816 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
6823 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
6824 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
6829 str
= CoTaskMemAlloc( len
);
6830 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
6831 r
= IStream_Write( stm
, str
, len
, NULL
);
6832 CoTaskMemFree( str
);
6836 /* read a string preceded by its length from a stream */
6837 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
6840 DWORD len
, count
= 0;
6844 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
6847 if( count
!= sizeof(len
) )
6848 return E_OUTOFMEMORY
;
6850 TRACE("%ld bytes\n",len
);
6852 str
= CoTaskMemAlloc( len
);
6854 return E_OUTOFMEMORY
;
6856 r
= IStream_Read( stm
, str
, len
, &count
);
6861 CoTaskMemFree( str
);
6862 return E_OUTOFMEMORY
;
6865 TRACE("Read string %s\n",debugstr_an(str
,len
));
6867 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
6868 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
6870 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
6871 CoTaskMemFree( str
);
6879 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
6880 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
6884 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6886 static const BYTE unknown1
[12] =
6887 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6888 0xFF, 0xFF, 0xFF, 0xFF};
6889 static const BYTE unknown2
[16] =
6890 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6891 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6893 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
6894 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
6895 debugstr_w(szProgIDName
));
6897 /* Create a CompObj stream if it doesn't exist */
6898 r
= IStorage_CreateStream(pstg
, szwStreamName
,
6899 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
6903 /* Write CompObj Structure to stream */
6904 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
6906 if( SUCCEEDED( r
) )
6907 r
= WriteClassStm( pstm
, clsid
);
6909 if( SUCCEEDED( r
) )
6910 r
= STREAM_WriteString( pstm
, lpszUserType
);
6911 if( SUCCEEDED( r
) )
6912 r
= STREAM_WriteString( pstm
, szClipName
);
6913 if( SUCCEEDED( r
) )
6914 r
= STREAM_WriteString( pstm
, szProgIDName
);
6915 if( SUCCEEDED( r
) )
6916 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
6918 IStream_Release( pstm
);
6923 /***********************************************************************
6924 * WriteFmtUserTypeStg (OLE32.@)
6926 HRESULT WINAPI
WriteFmtUserTypeStg(
6927 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
6930 WCHAR szwClipName
[0x40];
6931 CLSID clsid
= CLSID_NULL
;
6932 LPWSTR wstrProgID
= NULL
;
6935 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
6937 /* get the clipboard format name */
6938 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
) );
6941 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
6943 /* FIXME: There's room to save a CLSID and its ProgID, but
6944 the CLSID is not looked up in the registry and in all the
6945 tests I wrote it was CLSID_NULL. Where does it come from?
6948 /* get the real program ID. This may fail, but that's fine */
6949 ProgIDFromCLSID(&clsid
, &wstrProgID
);
6951 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
6953 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
6954 lpszUserType
, szwClipName
, wstrProgID
);
6956 CoTaskMemFree(wstrProgID
);
6962 /******************************************************************************
6963 * ReadFmtUserTypeStg [OLE32.@]
6965 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
6969 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
6970 unsigned char unknown1
[12];
6971 unsigned char unknown2
[16];
6973 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
6976 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
6978 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
6979 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
6982 WARN("Failed to open stream r = %08lx\n", r
);
6986 /* read the various parts of the structure */
6987 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
6988 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
6990 r
= ReadClassStm( stm
, &clsid
);
6994 r
= STREAM_ReadString( stm
, &szCLSIDName
);
6998 r
= STREAM_ReadString( stm
, &szOleTypeName
);
7002 r
= STREAM_ReadString( stm
, &szProgIDName
);
7006 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
7007 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
7010 /* ok, success... now we just need to store what we found */
7012 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
7013 CoTaskMemFree( szOleTypeName
);
7015 if( lplpszUserType
)
7016 *lplpszUserType
= szCLSIDName
;
7017 CoTaskMemFree( szProgIDName
);
7020 IStream_Release( stm
);
7026 /*************************************************************************
7027 * OLECONVERT_CreateCompObjStream [Internal]
7029 * Creates a "\001CompObj" is the destination IStorage if necessary.
7032 * pStorage [I] The dest IStorage to create the CompObj Stream
7034 * strOleTypeName [I] The ProgID
7038 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7041 * This function is used by OleConvertOLESTREAMToIStorage only.
7043 * The stream data is stored in the OLESTREAM and there should be
7044 * no need to recreate the stream. If the stream is manually
7045 * deleted it will attempt to create it by querying the registry.
7049 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
7052 HRESULT hStorageRes
, hRes
= S_OK
;
7053 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
7054 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7055 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
7057 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7058 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
7060 /* Initialize the CompObj structure */
7061 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
7062 memcpy(&(IStorageCompObj
.byUnknown1
), pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
7063 memcpy(&(IStorageCompObj
.byUnknown2
), pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
7066 /* Create a CompObj stream if it doesn't exist */
7067 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7068 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7069 if(hStorageRes
== S_OK
)
7071 /* copy the OleTypeName to the compobj struct */
7072 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
7073 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
7075 /* copy the OleTypeName to the compobj struct */
7076 /* Note: in the test made, these were Identical */
7077 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
7078 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
7081 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
7082 bufferW
, OLESTREAM_MAX_STR_LEN
);
7083 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
7089 /* Get the CLSID Default Name from the Registry */
7090 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
7091 if(hErr
== ERROR_SUCCESS
)
7093 char strTemp
[OLESTREAM_MAX_STR_LEN
];
7094 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
7095 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
7096 if(hErr
== ERROR_SUCCESS
)
7098 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
7104 /* Write CompObj Structure to stream */
7105 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
7107 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
7109 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
7110 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
7112 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
7114 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
7115 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
7117 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
7119 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
7120 if(IStorageCompObj
.dwProgIDNameLength
> 0)
7122 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
7124 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
7125 IStream_Release(pStream
);
7131 /*************************************************************************
7132 * OLECONVERT_CreateOlePresStream[Internal]
7134 * Creates the "\002OlePres000" Stream with the Metafile data
7137 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7138 * dwExtentX [I] Width of the Metafile
7139 * dwExtentY [I] Height of the Metafile
7140 * pData [I] Metafile data
7141 * dwDataLength [I] Size of the Metafile data
7145 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7148 * This function is used by OleConvertOLESTREAMToIStorage only.
7151 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
7155 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7156 BYTE pOlePresStreamHeader
[] =
7158 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7159 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7160 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7161 0x00, 0x00, 0x00, 0x00
7164 BYTE pOlePresStreamHeaderEmpty
[] =
7166 0x00, 0x00, 0x00, 0x00,
7167 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7168 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7169 0x00, 0x00, 0x00, 0x00
7172 /* Create the OlePres000 Stream */
7173 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7174 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7179 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
7181 memset(&OlePres
, 0, sizeof(OlePres
));
7182 /* Do we have any metafile data to save */
7183 if(dwDataLength
> 0)
7185 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
7186 nHeaderSize
= sizeof(pOlePresStreamHeader
);
7190 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
7191 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
7193 /* Set width and height of the metafile */
7194 OlePres
.dwExtentX
= dwExtentX
;
7195 OlePres
.dwExtentY
= -dwExtentY
;
7197 /* Set Data and Length */
7198 if(dwDataLength
> sizeof(METAFILEPICT16
))
7200 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
7201 OlePres
.pData
= &(pData
[8]);
7203 /* Save OlePres000 Data to Stream */
7204 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
7205 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
7206 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
7207 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
7208 if(OlePres
.dwSize
> 0)
7210 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
7212 IStream_Release(pStream
);
7216 /*************************************************************************
7217 * OLECONVERT_CreateOle10NativeStream [Internal]
7219 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7222 * pStorage [I] Dest storage to create the stream in
7223 * pData [I] Ole10 Native Data (ex. bmp)
7224 * dwDataLength [I] Size of the Ole10 Native Data
7230 * This function is used by OleConvertOLESTREAMToIStorage only.
7232 * Might need to verify the data and return appropriate error message
7235 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, BYTE
*pData
, DWORD dwDataLength
)
7239 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7241 /* Create the Ole10Native Stream */
7242 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7243 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7247 /* Write info to stream */
7248 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
7249 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
7250 IStream_Release(pStream
);
7255 /*************************************************************************
7256 * OLECONVERT_GetOLE10ProgID [Internal]
7258 * Finds the ProgID (or OleTypeID) from the IStorage
7261 * pStorage [I] The Src IStorage to get the ProgID
7262 * strProgID [I] the ProgID string to get
7263 * dwSize [I] the size of the string
7267 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7270 * This function is used by OleConvertIStorageToOLESTREAM only.
7274 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
7278 LARGE_INTEGER iSeekPos
;
7279 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
7280 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7282 /* Open the CompObj Stream */
7283 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7284 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7288 /*Get the OleType from the CompObj Stream */
7289 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
7290 iSeekPos
.u
.HighPart
= 0;
7292 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7293 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
7294 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
7295 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7296 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
7297 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
7298 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7300 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
7303 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
7305 IStream_Release(pStream
);
7310 LPOLESTR wstrProgID
;
7312 /* Get the OleType from the registry */
7313 REFCLSID clsid
= &(stat
.clsid
);
7314 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
7315 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
7318 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
7325 /*************************************************************************
7326 * OLECONVERT_GetOle10PresData [Internal]
7328 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7331 * pStorage [I] Src IStroage
7332 * pOleStream [I] Dest OleStream Mem Struct
7338 * This function is used by OleConvertIStorageToOLESTREAM only.
7340 * Memory allocated for pData must be freed by the caller
7344 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7349 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7351 /* Initialize Default data for OLESTREAM */
7352 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7353 pOleStreamData
[0].dwTypeID
= 2;
7354 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7355 pOleStreamData
[1].dwTypeID
= 0;
7356 pOleStreamData
[0].dwMetaFileWidth
= 0;
7357 pOleStreamData
[0].dwMetaFileHeight
= 0;
7358 pOleStreamData
[0].pData
= NULL
;
7359 pOleStreamData
[1].pData
= NULL
;
7361 /* Open Ole10Native Stream */
7362 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7363 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7367 /* Read Size and Data */
7368 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
7369 if(pOleStreamData
->dwDataLength
> 0)
7371 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
7372 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
7374 IStream_Release(pStream
);
7380 /*************************************************************************
7381 * OLECONVERT_GetOle20PresData[Internal]
7383 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7386 * pStorage [I] Src IStroage
7387 * pOleStreamData [I] Dest OleStream Mem Struct
7393 * This function is used by OleConvertIStorageToOLESTREAM only.
7395 * Memory allocated for pData must be freed by the caller
7397 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7401 OLECONVERT_ISTORAGE_OLEPRES olePress
;
7402 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7404 /* Initialize Default data for OLESTREAM */
7405 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7406 pOleStreamData
[0].dwTypeID
= 2;
7407 pOleStreamData
[0].dwMetaFileWidth
= 0;
7408 pOleStreamData
[0].dwMetaFileHeight
= 0;
7409 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
7410 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7411 pOleStreamData
[1].dwTypeID
= 0;
7412 pOleStreamData
[1].dwOleTypeNameLength
= 0;
7413 pOleStreamData
[1].strOleTypeName
[0] = 0;
7414 pOleStreamData
[1].dwMetaFileWidth
= 0;
7415 pOleStreamData
[1].dwMetaFileHeight
= 0;
7416 pOleStreamData
[1].pData
= NULL
;
7417 pOleStreamData
[1].dwDataLength
= 0;
7420 /* Open OlePress000 stream */
7421 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7422 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7425 LARGE_INTEGER iSeekPos
;
7426 METAFILEPICT16 MetaFilePict
;
7427 static const char strMetafilePictName
[] = "METAFILEPICT";
7429 /* Set the TypeID for a Metafile */
7430 pOleStreamData
[1].dwTypeID
= 5;
7432 /* Set the OleTypeName to Metafile */
7433 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
7434 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
7436 iSeekPos
.u
.HighPart
= 0;
7437 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
7439 /* Get Presentation Data */
7440 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7441 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
7442 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
7443 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
7445 /*Set width and Height */
7446 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
7447 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
7448 if(olePress
.dwSize
> 0)
7451 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
7453 /* Set MetaFilePict struct */
7454 MetaFilePict
.mm
= 8;
7455 MetaFilePict
.xExt
= olePress
.dwExtentX
;
7456 MetaFilePict
.yExt
= olePress
.dwExtentY
;
7457 MetaFilePict
.hMF
= 0;
7459 /* Get Metafile Data */
7460 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
7461 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
7462 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
7464 IStream_Release(pStream
);
7468 /*************************************************************************
7469 * OleConvertOLESTREAMToIStorage [OLE32.@]
7474 * DVTARGETDEVICE paramenter is not handled
7475 * Still unsure of some mem fields for OLE 10 Stream
7476 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7477 * and "\001OLE" streams
7480 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
7481 LPOLESTREAM pOleStream
,
7483 const DVTARGETDEVICE
* ptd
)
7487 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7489 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7493 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7496 if(pstg
== NULL
|| pOleStream
== NULL
)
7498 hRes
= E_INVALIDARG
;
7503 /* Load the OLESTREAM to Memory */
7504 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
7509 /* Load the OLESTREAM to Memory (part 2)*/
7510 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
7516 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
7518 /* Do we have the IStorage Data in the OLESTREAM */
7519 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
7521 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7522 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
7526 /* It must be an original OLE 1.0 source */
7527 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7532 /* It must be an original OLE 1.0 source */
7533 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7536 /* Create CompObj Stream if necessary */
7537 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
7540 /*Create the Ole Stream if necessary */
7541 OLECONVERT_CreateOleStream(pstg
);
7546 /* Free allocated memory */
7547 for(i
=0; i
< 2; i
++)
7549 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7550 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
7551 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
7556 /*************************************************************************
7557 * OleConvertIStorageToOLESTREAM [OLE32.@]
7564 * Still unsure of some mem fields for OLE 10 Stream
7565 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7566 * and "\001OLE" streams.
7569 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
7571 LPOLESTREAM pOleStream
)
7574 HRESULT hRes
= S_OK
;
7576 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7577 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7580 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7582 if(pstg
== NULL
|| pOleStream
== NULL
)
7584 hRes
= E_INVALIDARG
;
7588 /* Get the ProgID */
7589 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
7590 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
7594 /* Was it originally Ole10 */
7595 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7598 IStream_Release(pStream
);
7599 /* Get Presentation Data for Ole10Native */
7600 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
7604 /* Get Presentation Data (OLE20) */
7605 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
7608 /* Save OLESTREAM */
7609 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
7612 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
7617 /* Free allocated memory */
7618 for(i
=0; i
< 2; i
++)
7620 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7626 /***********************************************************************
7627 * GetConvertStg (OLE32.@)
7629 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
7630 FIXME("unimplemented stub!\n");
7634 /******************************************************************************
7635 * StgIsStorageFile [OLE32.@]
7638 StgIsStorageFile(LPCOLESTR fn
)
7644 TRACE("(\'%s\')\n", debugstr_w(fn
));
7645 hf
= CreateFileW(fn
, GENERIC_READ
,
7646 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
7647 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
7649 if (hf
== INVALID_HANDLE_VALUE
)
7650 return STG_E_FILENOTFOUND
;
7652 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
7654 WARN(" unable to read file\n");
7661 if (bytes_read
!= 8) {
7662 WARN(" too short\n");
7666 if (!memcmp(magic
,STORAGE_magic
,8)) {
7671 WARN(" -> Invalid header.\n");