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 * Initialize the out parameter
1066 * Validate the STGM flags
1068 if ( FAILED( validateSTGM(grfMode
) ) ||
1069 (grfMode
& STGM_DELETEONRELEASE
) )
1071 WARN("bad grfMode: 0x%lx\n", grfMode
);
1072 return STG_E_INVALIDFLAG
;
1076 * Check that we're compatible with the parent's storage mode
1078 parent_grfMode
= STGM_ACCESS_MODE( This
->base
.ancestorStorage
->base
.openFlags
);
1079 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( parent_grfMode
) )
1081 WARN("access denied\n");
1082 return STG_E_ACCESSDENIED
;
1086 * Create a property enumeration and search the properties
1088 propertyEnumeration
= IEnumSTATSTGImpl_Construct( This
->base
.ancestorStorage
,
1089 This
->base
.rootPropertySetIndex
);
1091 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
1094 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1096 if (foundPropertyIndex
!= PROPERTY_NULL
)
1099 * An element with this name already exists
1101 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
1102 IStorage_DestroyElement(iface
, pwcsName
);
1105 WARN("file already exists\n");
1106 return STG_E_FILEALREADYEXISTS
;
1111 * memset the empty property
1113 memset(&newProperty
, 0, sizeof(StgProperty
));
1115 newProperty
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1117 if (newProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
1119 FIXME("name too long\n");
1120 return STG_E_INVALIDNAME
;
1123 strcpyW(newProperty
.name
, pwcsName
);
1125 newProperty
.propertyType
= PROPTYPE_STORAGE
;
1126 newProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
1127 newProperty
.size
.u
.LowPart
= 0;
1128 newProperty
.size
.u
.HighPart
= 0;
1130 newProperty
.previousProperty
= PROPERTY_NULL
;
1131 newProperty
.nextProperty
= PROPERTY_NULL
;
1132 newProperty
.dirProperty
= PROPERTY_NULL
;
1134 /* call CoFileTime to get the current time
1135 newProperty.timeStampS1
1136 newProperty.timeStampD1
1137 newProperty.timeStampS2
1138 newProperty.timeStampD2
1141 /* newStorageProperty.propertyUniqueID */
1144 * Obtain a free property in the property chain
1146 newPropertyIndex
= getFreeProperty(This
->base
.ancestorStorage
);
1149 * Save the new property into the new property spot
1151 StorageImpl_WriteProperty(
1152 This
->base
.ancestorStorage
,
1157 * Find a spot in the property chain for our newly created property.
1159 updatePropertyChain(
1165 * Open it to get a pointer to return.
1167 hr
= IStorage_OpenStorage(
1169 (const OLECHAR
*)pwcsName
,
1176 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1186 /***************************************************************************
1190 * Get a free property or create a new one.
1192 static ULONG
getFreeProperty(
1193 StorageImpl
*storage
)
1195 ULONG currentPropertyIndex
= 0;
1196 ULONG newPropertyIndex
= PROPERTY_NULL
;
1197 BOOL readSuccessful
= TRUE
;
1198 StgProperty currentProperty
;
1203 * Start by reading the root property
1205 readSuccessful
= StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1206 currentPropertyIndex
,
1210 if (currentProperty
.sizeOfNameString
== 0)
1213 * The property existis and is available, we found it.
1215 newPropertyIndex
= currentPropertyIndex
;
1221 * We exhausted the property list, we will create more space below
1223 newPropertyIndex
= currentPropertyIndex
;
1225 currentPropertyIndex
++;
1227 } while (newPropertyIndex
== PROPERTY_NULL
);
1230 * grow the property chain
1232 if (! readSuccessful
)
1234 StgProperty emptyProperty
;
1235 ULARGE_INTEGER newSize
;
1236 ULONG propertyIndex
;
1237 ULONG lastProperty
= 0;
1238 ULONG blockCount
= 0;
1241 * obtain the new count of property blocks
1243 blockCount
= BlockChainStream_GetCount(
1244 storage
->base
.ancestorStorage
->rootBlockChain
)+1;
1247 * initialize the size used by the property stream
1249 newSize
.u
.HighPart
= 0;
1250 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1253 * add a property block to the property chain
1255 BlockChainStream_SetSize(storage
->base
.ancestorStorage
->rootBlockChain
, newSize
);
1258 * memset the empty property in order to initialize the unused newly
1261 memset(&emptyProperty
, 0, sizeof(StgProperty
));
1266 lastProperty
= storage
->bigBlockSize
/ PROPSET_BLOCK_SIZE
* blockCount
;
1269 propertyIndex
= newPropertyIndex
;
1270 propertyIndex
< lastProperty
;
1273 StorageImpl_WriteProperty(
1274 storage
->base
.ancestorStorage
,
1280 return newPropertyIndex
;
1283 /****************************************************************************
1287 * Case insensitive comparaison of StgProperty.name by first considering
1290 * Returns <0 when newPrpoerty < currentProperty
1291 * >0 when newPrpoerty > currentProperty
1292 * 0 when newPrpoerty == currentProperty
1294 static LONG
propertyNameCmp(
1295 const OLECHAR
*newProperty
,
1296 const OLECHAR
*currentProperty
)
1298 LONG diff
= lstrlenW(newProperty
) - lstrlenW(currentProperty
);
1303 * We compare the string themselves only when they are of the same length
1305 diff
= lstrcmpiW( newProperty
, currentProperty
);
1311 /****************************************************************************
1315 * Properly link this new element in the property chain.
1317 static void updatePropertyChain(
1318 StorageImpl
*storage
,
1319 ULONG newPropertyIndex
,
1320 StgProperty newProperty
)
1322 StgProperty currentProperty
;
1325 * Read the root property
1327 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1328 storage
->base
.rootPropertySetIndex
,
1331 if (currentProperty
.dirProperty
!= PROPERTY_NULL
)
1334 * The root storage contains some element, therefore, start the research
1335 * for the appropriate location.
1338 ULONG current
, next
, previous
, currentPropertyId
;
1341 * Keep the StgProperty sequence number of the storage first property
1343 currentPropertyId
= currentProperty
.dirProperty
;
1348 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1349 currentProperty
.dirProperty
,
1352 previous
= currentProperty
.previousProperty
;
1353 next
= currentProperty
.nextProperty
;
1354 current
= currentPropertyId
;
1358 LONG diff
= propertyNameCmp( newProperty
.name
, currentProperty
.name
);
1362 if (previous
!= PROPERTY_NULL
)
1364 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1371 currentProperty
.previousProperty
= newPropertyIndex
;
1372 StorageImpl_WriteProperty(storage
->base
.ancestorStorage
,
1380 if (next
!= PROPERTY_NULL
)
1382 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1389 currentProperty
.nextProperty
= newPropertyIndex
;
1390 StorageImpl_WriteProperty(storage
->base
.ancestorStorage
,
1399 * Trying to insert an item with the same name in the
1400 * subtree structure.
1405 previous
= currentProperty
.previousProperty
;
1406 next
= currentProperty
.nextProperty
;
1412 * The root storage is empty, link the new property to it's dir property
1414 currentProperty
.dirProperty
= newPropertyIndex
;
1415 StorageImpl_WriteProperty(storage
->base
.ancestorStorage
,
1416 storage
->base
.rootPropertySetIndex
,
1422 /*************************************************************************
1425 HRESULT WINAPI
StorageImpl_CopyTo(
1427 DWORD ciidExclude
, /* [in] */
1428 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1429 SNB snbExclude
, /* [unique][in] */
1430 IStorage
* pstgDest
) /* [unique][in] */
1432 IEnumSTATSTG
*elements
= 0;
1433 STATSTG curElement
, strStat
;
1435 IStorage
*pstgTmp
, *pstgChild
;
1436 IStream
*pstrTmp
, *pstrChild
;
1438 if ((ciidExclude
!= 0) || (rgiidExclude
!= NULL
) || (snbExclude
!= NULL
))
1439 FIXME("Exclude option not implemented\n");
1441 TRACE("(%p, %ld, %p, %p, %p)\n",
1442 iface
, ciidExclude
, rgiidExclude
,
1443 snbExclude
, pstgDest
);
1446 * Perform a sanity check
1448 if ( pstgDest
== 0 )
1449 return STG_E_INVALIDPOINTER
;
1452 * Enumerate the elements
1454 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1462 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1463 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1468 * Obtain the next element
1470 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1472 if ( hr
== S_FALSE
)
1474 hr
= S_OK
; /* done, every element has been copied */
1478 if (curElement
.type
== STGTY_STORAGE
)
1481 * open child source storage
1483 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1484 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1485 NULL
, 0, &pstgChild
);
1491 * Check if destination storage is not a child of the source
1492 * storage, which will cause an infinite loop
1494 if (pstgChild
== pstgDest
)
1496 IEnumSTATSTG_Release(elements
);
1498 return STG_E_ACCESSDENIED
;
1502 * create a new storage in destination storage
1504 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1505 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1509 * if it already exist, don't create a new one use this one
1511 if (hr
== STG_E_FILEALREADYEXISTS
)
1513 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1514 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1515 NULL
, 0, &pstgTmp
);
1523 * do the copy recursively
1525 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1526 snbExclude
, pstgTmp
);
1528 IStorage_Release( pstgTmp
);
1529 IStorage_Release( pstgChild
);
1531 else if (curElement
.type
== STGTY_STREAM
)
1534 * create a new stream in destination storage. If the stream already
1535 * exist, it will be deleted and a new one will be created.
1537 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1538 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1545 * open child stream storage
1547 hr
= IStorage_OpenStream( iface
, curElement
.pwcsName
, NULL
,
1548 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1555 * Get the size of the source stream
1557 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1560 * Set the size of the destination stream.
1562 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1567 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1570 IStream_Release( pstrTmp
);
1571 IStream_Release( pstrChild
);
1575 WARN("unknown element type: %ld\n", curElement
.type
);
1578 } while (hr
== S_OK
);
1583 IEnumSTATSTG_Release(elements
);
1588 /*************************************************************************
1589 * MoveElementTo (IStorage)
1591 HRESULT WINAPI
StorageImpl_MoveElementTo(
1593 const OLECHAR
*pwcsName
, /* [string][in] */
1594 IStorage
*pstgDest
, /* [unique][in] */
1595 const OLECHAR
*pwcsNewName
,/* [string][in] */
1596 DWORD grfFlags
) /* [in] */
1598 FIXME("not implemented!\n");
1602 /*************************************************************************
1605 * Ensures that any changes made to a storage object open in transacted mode
1606 * are reflected in the parent storage
1609 * Wine doesn't implement transacted mode, which seems to be a basic
1610 * optimization, so we can ignore this stub for now.
1612 HRESULT WINAPI
StorageImpl_Commit(
1614 DWORD grfCommitFlags
)/* [in] */
1616 FIXME("(%ld): stub!\n", grfCommitFlags
);
1620 /*************************************************************************
1623 * Discard all changes that have been made since the last commit operation
1625 HRESULT WINAPI
StorageImpl_Revert(
1628 FIXME("not implemented!\n");
1632 /*************************************************************************
1633 * DestroyElement (IStorage)
1635 * Stategy: This implementation is build this way for simplicity not for speed.
1636 * I always delete the top most element of the enumeration and adjust
1637 * the deleted element pointer all the time. This takes longer to
1638 * do but allow to reinvoke DestroyElement whenever we encounter a
1639 * storage object. The optimisation reside in the usage of another
1640 * enumeration stategy that would give all the leaves of a storage
1641 * first. (postfix order)
1643 HRESULT WINAPI
StorageImpl_DestroyElement(
1645 const OLECHAR
*pwcsName
)/* [string][in] */
1647 StorageImpl
* const This
=(StorageImpl
*)iface
;
1649 IEnumSTATSTGImpl
* propertyEnumeration
;
1652 StgProperty propertyToDelete
;
1653 StgProperty parentProperty
;
1654 ULONG foundPropertyIndexToDelete
;
1655 ULONG typeOfRelation
;
1656 ULONG parentPropertyId
;
1659 iface
, debugstr_w(pwcsName
));
1662 * Perform a sanity check on the parameters.
1665 return STG_E_INVALIDPOINTER
;
1668 * Create a property enumeration to search the property with the given name
1670 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
1671 This
->base
.ancestorStorage
,
1672 This
->base
.rootPropertySetIndex
);
1674 foundPropertyIndexToDelete
= IEnumSTATSTGImpl_FindProperty(
1675 propertyEnumeration
,
1679 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1681 if ( foundPropertyIndexToDelete
== PROPERTY_NULL
)
1683 return STG_E_FILENOTFOUND
;
1687 * Find the parent property of the property to delete (the one that
1688 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1689 * the parent is This. Otherwise, the parent is one of it's sibling...
1693 * First, read This's StgProperty..
1695 res
= StorageImpl_ReadProperty(
1696 This
->base
.ancestorStorage
,
1697 This
->base
.rootPropertySetIndex
,
1703 * Second, check to see if by any chance the actual storage (This) is not
1704 * the parent of the property to delete... We never know...
1706 if ( parentProperty
.dirProperty
== foundPropertyIndexToDelete
)
1709 * Set data as it would have been done in the else part...
1711 typeOfRelation
= PROPERTY_RELATION_DIR
;
1712 parentPropertyId
= This
->base
.rootPropertySetIndex
;
1717 * Create a property enumeration to search the parent properties, and
1718 * delete it once done.
1720 IEnumSTATSTGImpl
* propertyEnumeration2
;
1722 propertyEnumeration2
= IEnumSTATSTGImpl_Construct(
1723 This
->base
.ancestorStorage
,
1724 This
->base
.rootPropertySetIndex
);
1726 typeOfRelation
= IEnumSTATSTGImpl_FindParentProperty(
1727 propertyEnumeration2
,
1728 foundPropertyIndexToDelete
,
1732 IEnumSTATSTGImpl_Destroy(propertyEnumeration2
);
1735 if ( propertyToDelete
.propertyType
== PROPTYPE_STORAGE
)
1737 hr
= deleteStorageProperty(
1739 foundPropertyIndexToDelete
,
1742 else if ( propertyToDelete
.propertyType
== PROPTYPE_STREAM
)
1744 hr
= deleteStreamProperty(
1746 foundPropertyIndexToDelete
,
1754 * Adjust the property chain
1756 hr
= adjustPropertyChain(
1767 /************************************************************************
1768 * StorageImpl_Stat (IStorage)
1770 * This method will retrieve information about this storage object.
1772 * See Windows documentation for more details on IStorage methods.
1774 HRESULT WINAPI
StorageImpl_Stat( IStorage
* iface
,
1775 STATSTG
* pstatstg
, /* [out] */
1776 DWORD grfStatFlag
) /* [in] */
1778 StorageImpl
* const This
= (StorageImpl
*)iface
;
1779 HRESULT result
= StorageBaseImpl_Stat( iface
, pstatstg
, grfStatFlag
);
1781 if ( !FAILED(result
) && ((grfStatFlag
& STATFLAG_NONAME
) == 0) && This
->pwcsName
)
1783 CoTaskMemFree(pstatstg
->pwcsName
);
1784 pstatstg
->pwcsName
= CoTaskMemAlloc((lstrlenW(This
->pwcsName
)+1)*sizeof(WCHAR
));
1785 strcpyW(pstatstg
->pwcsName
, This
->pwcsName
);
1793 /*********************************************************************
1797 * Perform the deletion of a complete storage node
1800 static HRESULT
deleteStorageProperty(
1801 StorageImpl
*parentStorage
,
1802 ULONG indexOfPropertyToDelete
,
1803 StgProperty propertyToDelete
)
1805 IEnumSTATSTG
*elements
= 0;
1806 IStorage
*childStorage
= 0;
1807 STATSTG currentElement
;
1809 HRESULT destroyHr
= S_OK
;
1812 * Open the storage and enumerate it
1814 hr
= StorageBaseImpl_OpenStorage(
1815 (IStorage
*)parentStorage
,
1816 propertyToDelete
.name
,
1818 STGM_SHARE_EXCLUSIVE
,
1829 * Enumerate the elements
1831 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
1836 * Obtain the next element
1838 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
1841 destroyHr
= StorageImpl_DestroyElement(
1842 (IStorage
*)childStorage
,
1843 (OLECHAR
*)currentElement
.pwcsName
);
1845 CoTaskMemFree(currentElement
.pwcsName
);
1849 * We need to Reset the enumeration every time because we delete elements
1850 * and the enumeration could be invalid
1852 IEnumSTATSTG_Reset(elements
);
1854 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
1857 * Invalidate the property by zeroing it's name member.
1859 propertyToDelete
.sizeOfNameString
= 0;
1861 StorageImpl_WriteProperty(parentStorage
->base
.ancestorStorage
,
1862 indexOfPropertyToDelete
,
1865 IStorage_Release(childStorage
);
1866 IEnumSTATSTG_Release(elements
);
1871 /*********************************************************************
1875 * Perform the deletion of a stream node
1878 static HRESULT
deleteStreamProperty(
1879 StorageImpl
*parentStorage
,
1880 ULONG indexOfPropertyToDelete
,
1881 StgProperty propertyToDelete
)
1885 ULARGE_INTEGER size
;
1887 size
.u
.HighPart
= 0;
1890 hr
= StorageBaseImpl_OpenStream(
1891 (IStorage
*)parentStorage
,
1892 (OLECHAR
*)propertyToDelete
.name
,
1894 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
1906 hr
= IStream_SetSize(pis
, size
);
1914 * Release the stream object.
1916 IStream_Release(pis
);
1919 * Invalidate the property by zeroing it's name member.
1921 propertyToDelete
.sizeOfNameString
= 0;
1924 * Here we should re-read the property so we get the updated pointer
1925 * but since we are here to zap it, I don't do it...
1927 StorageImpl_WriteProperty(
1928 parentStorage
->base
.ancestorStorage
,
1929 indexOfPropertyToDelete
,
1935 /*********************************************************************
1939 * Finds a placeholder for the StgProperty within the Storage
1942 static HRESULT
findPlaceholder(
1943 StorageImpl
*storage
,
1944 ULONG propertyIndexToStore
,
1945 ULONG storePropertyIndex
,
1948 StgProperty storeProperty
;
1953 * Read the storage property
1955 res
= StorageImpl_ReadProperty(
1956 storage
->base
.ancestorStorage
,
1965 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
1967 if (storeProperty
.previousProperty
!= PROPERTY_NULL
)
1969 return findPlaceholder(
1971 propertyIndexToStore
,
1972 storeProperty
.previousProperty
,
1977 storeProperty
.previousProperty
= propertyIndexToStore
;
1980 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
1982 if (storeProperty
.nextProperty
!= PROPERTY_NULL
)
1984 return findPlaceholder(
1986 propertyIndexToStore
,
1987 storeProperty
.nextProperty
,
1992 storeProperty
.nextProperty
= propertyIndexToStore
;
1995 else if (typeOfRelation
== PROPERTY_RELATION_DIR
)
1997 if (storeProperty
.dirProperty
!= PROPERTY_NULL
)
1999 return findPlaceholder(
2001 propertyIndexToStore
,
2002 storeProperty
.dirProperty
,
2007 storeProperty
.dirProperty
= propertyIndexToStore
;
2011 hr
= StorageImpl_WriteProperty(
2012 storage
->base
.ancestorStorage
,
2024 /*************************************************************************
2028 * This method takes the previous and the next property link of a property
2029 * to be deleted and find them a place in the Storage.
2031 static HRESULT
adjustPropertyChain(
2033 StgProperty propertyToDelete
,
2034 StgProperty parentProperty
,
2035 ULONG parentPropertyId
,
2038 ULONG newLinkProperty
= PROPERTY_NULL
;
2039 BOOL needToFindAPlaceholder
= FALSE
;
2040 ULONG storeNode
= PROPERTY_NULL
;
2041 ULONG toStoreNode
= PROPERTY_NULL
;
2042 INT relationType
= 0;
2046 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
2048 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2051 * Set the parent previous to the property to delete previous
2053 newLinkProperty
= propertyToDelete
.previousProperty
;
2055 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2058 * We also need to find a storage for the other link, setup variables
2059 * to do this at the end...
2061 needToFindAPlaceholder
= TRUE
;
2062 storeNode
= propertyToDelete
.previousProperty
;
2063 toStoreNode
= propertyToDelete
.nextProperty
;
2064 relationType
= PROPERTY_RELATION_NEXT
;
2067 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2070 * Set the parent previous to the property to delete next
2072 newLinkProperty
= propertyToDelete
.nextProperty
;
2076 * Link it for real...
2078 parentProperty
.previousProperty
= newLinkProperty
;
2081 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
2083 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2086 * Set the parent next to the property to delete next previous
2088 newLinkProperty
= propertyToDelete
.previousProperty
;
2090 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2093 * We also need to find a storage for the other link, setup variables
2094 * to do this at the end...
2096 needToFindAPlaceholder
= TRUE
;
2097 storeNode
= propertyToDelete
.previousProperty
;
2098 toStoreNode
= propertyToDelete
.nextProperty
;
2099 relationType
= PROPERTY_RELATION_NEXT
;
2102 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2105 * Set the parent next to the property to delete next
2107 newLinkProperty
= propertyToDelete
.nextProperty
;
2111 * Link it for real...
2113 parentProperty
.nextProperty
= newLinkProperty
;
2115 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2117 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2120 * Set the parent dir to the property to delete previous
2122 newLinkProperty
= propertyToDelete
.previousProperty
;
2124 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2127 * We also need to find a storage for the other link, setup variables
2128 * to do this at the end...
2130 needToFindAPlaceholder
= TRUE
;
2131 storeNode
= propertyToDelete
.previousProperty
;
2132 toStoreNode
= propertyToDelete
.nextProperty
;
2133 relationType
= PROPERTY_RELATION_NEXT
;
2136 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2139 * Set the parent dir to the property to delete next
2141 newLinkProperty
= propertyToDelete
.nextProperty
;
2145 * Link it for real...
2147 parentProperty
.dirProperty
= newLinkProperty
;
2151 * Write back the parent property
2153 res
= StorageImpl_WriteProperty(
2154 This
->base
.ancestorStorage
,
2163 * If a placeholder is required for the other link, then, find one and
2164 * get out of here...
2166 if (needToFindAPlaceholder
)
2168 hr
= findPlaceholder(
2179 /******************************************************************************
2180 * SetElementTimes (IStorage)
2182 HRESULT WINAPI
StorageImpl_SetElementTimes(
2184 const OLECHAR
*pwcsName
,/* [string][in] */
2185 const FILETIME
*pctime
, /* [in] */
2186 const FILETIME
*patime
, /* [in] */
2187 const FILETIME
*pmtime
) /* [in] */
2189 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2193 /******************************************************************************
2194 * SetStateBits (IStorage)
2196 HRESULT WINAPI
StorageImpl_SetStateBits(
2198 DWORD grfStateBits
,/* [in] */
2199 DWORD grfMask
) /* [in] */
2201 FIXME("not implemented!\n");
2206 * Virtual function table for the IStorage32Impl class.
2208 static const IStorageVtbl Storage32Impl_Vtbl
=
2210 StorageBaseImpl_QueryInterface
,
2211 StorageBaseImpl_AddRef
,
2212 StorageBaseImpl_Release
,
2213 StorageBaseImpl_CreateStream
,
2214 StorageBaseImpl_OpenStream
,
2215 StorageImpl_CreateStorage
,
2216 StorageBaseImpl_OpenStorage
,
2218 StorageImpl_MoveElementTo
,
2221 StorageBaseImpl_EnumElements
,
2222 StorageImpl_DestroyElement
,
2223 StorageBaseImpl_RenameElement
,
2224 StorageImpl_SetElementTimes
,
2225 StorageBaseImpl_SetClass
,
2226 StorageImpl_SetStateBits
,
2230 HRESULT
StorageImpl_Construct(
2240 StgProperty currentProperty
;
2241 BOOL readSuccessful
;
2242 ULONG currentPropertyIndex
;
2244 if ( FAILED( validateSTGM(openFlags
) ))
2245 return STG_E_INVALIDFLAG
;
2247 memset(This
, 0, sizeof(StorageImpl
));
2250 * Initialize the virtual function table.
2252 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2253 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2254 This
->base
.v_destructor
= &StorageImpl_Destroy
;
2255 This
->base
.openFlags
= openFlags
;
2258 * This is the top-level storage so initialize the ancestor pointer
2261 This
->base
.ancestorStorage
= This
;
2264 * Initialize the physical support of the storage.
2266 This
->hFile
= hFile
;
2269 * Store copy of file path.
2272 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2273 (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
));
2274 if (!This
->pwcsName
)
2275 return STG_E_INSUFFICIENTMEMORY
;
2276 strcpyW(This
->pwcsName
, pwcsName
);
2280 * Initialize the big block cache.
2282 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
2283 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2284 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2290 if (This
->bigBlockFile
== 0)
2295 ULARGE_INTEGER size
;
2296 BYTE
* bigBlockBuffer
;
2299 * Initialize all header variables:
2300 * - The big block depot consists of one block and it is at block 0
2301 * - The properties start at block 1
2302 * - There is no small block depot
2304 memset( This
->bigBlockDepotStart
,
2306 sizeof(This
->bigBlockDepotStart
));
2308 This
->bigBlockDepotCount
= 1;
2309 This
->bigBlockDepotStart
[0] = 0;
2310 This
->rootStartBlock
= 1;
2311 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2312 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
2313 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2314 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2315 This
->extBigBlockDepotCount
= 0;
2317 StorageImpl_SaveFileHeader(This
);
2320 * Add one block for the big block depot and one block for the properties
2322 size
.u
.HighPart
= 0;
2323 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2324 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2327 * Initialize the big block depot
2329 bigBlockBuffer
= StorageImpl_GetBigBlock(This
, 0);
2330 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2331 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2332 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2333 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
2338 * Load the header for the file.
2340 hr
= StorageImpl_LoadFileHeader(This
);
2344 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2351 * There is no block depot cached yet.
2353 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2356 * Start searching for free blocks with block 0.
2358 This
->prevFreeBlock
= 0;
2361 * Create the block chain abstractions.
2363 if(!(This
->rootBlockChain
=
2364 BlockChainStream_Construct(This
, &This
->rootStartBlock
, PROPERTY_NULL
)))
2365 return STG_E_READFAULT
;
2367 if(!(This
->smallBlockDepotChain
=
2368 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2370 return STG_E_READFAULT
;
2373 * Write the root property
2377 StgProperty rootProp
;
2379 * Initialize the property chain
2381 memset(&rootProp
, 0, sizeof(rootProp
));
2382 MultiByteToWideChar( CP_ACP
, 0, rootPropertyName
, -1, rootProp
.name
,
2383 sizeof(rootProp
.name
)/sizeof(WCHAR
) );
2384 rootProp
.sizeOfNameString
= (strlenW(rootProp
.name
)+1) * sizeof(WCHAR
);
2385 rootProp
.propertyType
= PROPTYPE_ROOT
;
2386 rootProp
.previousProperty
= PROPERTY_NULL
;
2387 rootProp
.nextProperty
= PROPERTY_NULL
;
2388 rootProp
.dirProperty
= PROPERTY_NULL
;
2389 rootProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
2390 rootProp
.size
.u
.HighPart
= 0;
2391 rootProp
.size
.u
.LowPart
= 0;
2393 StorageImpl_WriteProperty(This
, 0, &rootProp
);
2397 * Find the ID of the root in the property sets.
2399 currentPropertyIndex
= 0;
2403 readSuccessful
= StorageImpl_ReadProperty(
2405 currentPropertyIndex
,
2410 if ( (currentProperty
.sizeOfNameString
!= 0 ) &&
2411 (currentProperty
.propertyType
== PROPTYPE_ROOT
) )
2413 This
->base
.rootPropertySetIndex
= currentPropertyIndex
;
2417 currentPropertyIndex
++;
2419 } while (readSuccessful
&& (This
->base
.rootPropertySetIndex
== PROPERTY_NULL
) );
2421 if (!readSuccessful
)
2424 return STG_E_READFAULT
;
2428 * Create the block chain abstraction for the small block root chain.
2430 if(!(This
->smallBlockRootChain
=
2431 BlockChainStream_Construct(This
, NULL
, This
->base
.rootPropertySetIndex
)))
2432 return STG_E_READFAULT
;
2437 void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2439 StorageImpl
*This
= (StorageImpl
*) iface
;
2440 TRACE("(%p)\n", This
);
2442 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2444 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2445 BlockChainStream_Destroy(This
->rootBlockChain
);
2446 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2448 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2449 HeapFree(GetProcessHeap(), 0, This
);
2452 /******************************************************************************
2453 * Storage32Impl_GetNextFreeBigBlock
2455 * Returns the index of the next free big block.
2456 * If the big block depot is filled, this method will enlarge it.
2459 ULONG
StorageImpl_GetNextFreeBigBlock(
2462 ULONG depotBlockIndexPos
;
2464 ULONG depotBlockOffset
;
2465 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2466 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2468 ULONG freeBlock
= BLOCK_UNUSED
;
2470 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2471 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2474 * Scan the entire big block depot until we find a block marked free
2476 while (nextBlockIndex
!= BLOCK_UNUSED
)
2478 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2480 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2483 * Grow the primary depot.
2485 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2487 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2490 * Add a block depot.
2492 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2493 This
->bigBlockDepotCount
++;
2494 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2497 * Flag it as a block depot.
2499 StorageImpl_SetNextBlockInChain(This
,
2503 /* Save new header information.
2505 StorageImpl_SaveFileHeader(This
);
2510 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2512 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2515 * Grow the extended depot.
2517 ULONG extIndex
= BLOCK_UNUSED
;
2518 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2519 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2521 if (extBlockOffset
== 0)
2523 /* We need an extended block.
2525 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2526 This
->extBigBlockDepotCount
++;
2527 depotBlockIndexPos
= extIndex
+ 1;
2530 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2533 * Add a block depot and mark it in the extended block.
2535 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2536 This
->bigBlockDepotCount
++;
2537 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2539 /* Flag the block depot.
2541 StorageImpl_SetNextBlockInChain(This
,
2545 /* If necessary, flag the extended depot block.
2547 if (extIndex
!= BLOCK_UNUSED
)
2548 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2550 /* Save header information.
2552 StorageImpl_SaveFileHeader(This
);
2556 depotBuffer
= StorageImpl_GetROBigBlock(This
, depotBlockIndexPos
);
2558 if (depotBuffer
!= 0)
2560 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2561 ( nextBlockIndex
!= BLOCK_UNUSED
))
2563 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2565 if (nextBlockIndex
== BLOCK_UNUSED
)
2567 freeBlock
= (depotIndex
* blocksPerDepot
) +
2568 (depotBlockOffset
/sizeof(ULONG
));
2571 depotBlockOffset
+= sizeof(ULONG
);
2574 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2578 depotBlockOffset
= 0;
2581 This
->prevFreeBlock
= freeBlock
;
2586 /******************************************************************************
2587 * Storage32Impl_AddBlockDepot
2589 * This will create a depot block, essentially it is a block initialized
2592 void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2596 blockBuffer
= StorageImpl_GetBigBlock(This
, blockIndex
);
2599 * Initialize blocks as free
2601 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2603 StorageImpl_ReleaseBigBlock(This
, blockBuffer
);
2606 /******************************************************************************
2607 * Storage32Impl_GetExtDepotBlock
2609 * Returns the index of the block that corresponds to the specified depot
2610 * index. This method is only for depot indexes equal or greater than
2611 * COUNT_BBDEPOTINHEADER.
2613 ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2615 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2616 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2617 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2618 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2619 ULONG blockIndex
= BLOCK_UNUSED
;
2620 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2622 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2624 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2625 return BLOCK_UNUSED
;
2627 while (extBlockCount
> 0)
2629 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2633 if (extBlockIndex
!= BLOCK_UNUSED
)
2637 depotBuffer
= StorageImpl_GetROBigBlock(This
, extBlockIndex
);
2639 if (depotBuffer
!= 0)
2641 StorageUtl_ReadDWord(depotBuffer
,
2642 extBlockOffset
* sizeof(ULONG
),
2645 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2652 /******************************************************************************
2653 * Storage32Impl_SetExtDepotBlock
2655 * Associates the specified block index to the specified depot index.
2656 * This method is only for depot indexes equal or greater than
2657 * COUNT_BBDEPOTINHEADER.
2659 void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
,
2663 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2664 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2665 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2666 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2667 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2669 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2671 while (extBlockCount
> 0)
2673 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2677 if (extBlockIndex
!= BLOCK_UNUSED
)
2681 depotBuffer
= StorageImpl_GetBigBlock(This
, extBlockIndex
);
2683 if (depotBuffer
!= 0)
2685 StorageUtl_WriteDWord(depotBuffer
,
2686 extBlockOffset
* sizeof(ULONG
),
2689 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2694 /******************************************************************************
2695 * Storage32Impl_AddExtBlockDepot
2697 * Creates an extended depot block.
2699 ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
2701 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
2702 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
2703 BYTE
* depotBuffer
= NULL
;
2704 ULONG index
= BLOCK_UNUSED
;
2705 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2706 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
2707 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
2709 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
2710 blocksPerDepotBlock
;
2712 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
2715 * The first extended block.
2717 This
->extBigBlockDepotStart
= index
;
2723 * Follow the chain to the last one.
2725 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
2727 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
2731 * Add the new extended block to the chain.
2733 depotBuffer
= StorageImpl_GetBigBlock(This
, nextExtBlock
);
2734 StorageUtl_WriteDWord(depotBuffer
, nextBlockOffset
, index
);
2735 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2739 * Initialize this block.
2741 depotBuffer
= StorageImpl_GetBigBlock(This
, index
);
2742 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2743 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2748 /******************************************************************************
2749 * Storage32Impl_FreeBigBlock
2751 * This method will flag the specified block as free in the big block depot.
2753 void StorageImpl_FreeBigBlock(
2757 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
2759 if (blockIndex
< This
->prevFreeBlock
)
2760 This
->prevFreeBlock
= blockIndex
;
2763 /************************************************************************
2764 * Storage32Impl_GetNextBlockInChain
2766 * This method will retrieve the block index of the next big block in
2769 * Params: This - Pointer to the Storage object.
2770 * blockIndex - Index of the block to retrieve the chain
2772 * nextBlockIndex - receives the return value.
2774 * Returns: This method returns the index of the next block in the chain.
2775 * It will return the constants:
2776 * BLOCK_SPECIAL - If the block given was not part of a
2778 * BLOCK_END_OF_CHAIN - If the block given was the last in
2780 * BLOCK_UNUSED - If the block given was not past of a chain
2782 * BLOCK_EXTBBDEPOT - This block is part of the extended
2785 * See Windows documentation for more details on IStorage methods.
2787 HRESULT
StorageImpl_GetNextBlockInChain(
2790 ULONG
* nextBlockIndex
)
2792 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2793 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2794 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2796 ULONG depotBlockIndexPos
;
2799 *nextBlockIndex
= BLOCK_SPECIAL
;
2801 if(depotBlockCount
>= This
->bigBlockDepotCount
)
2803 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount
,
2804 This
->bigBlockDepotCount
);
2805 return STG_E_READFAULT
;
2809 * Cache the currently accessed depot block.
2811 if (depotBlockCount
!= This
->indexBlockDepotCached
)
2813 This
->indexBlockDepotCached
= depotBlockCount
;
2815 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2817 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2822 * We have to look in the extended depot.
2824 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2827 depotBuffer
= StorageImpl_GetROBigBlock(This
, depotBlockIndexPos
);
2830 return STG_E_READFAULT
;
2832 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
2834 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
2835 This
->blockDepotCached
[index
] = *nextBlockIndex
;
2837 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2840 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
2845 /******************************************************************************
2846 * Storage32Impl_GetNextExtendedBlock
2848 * Given an extended block this method will return the next extended block.
2851 * The last ULONG of an extended block is the block index of the next
2852 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2856 * - The index of the next extended block
2857 * - BLOCK_UNUSED: there is no next extended block.
2858 * - Any other return values denotes failure.
2860 ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
2862 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2863 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2866 depotBuffer
= StorageImpl_GetROBigBlock(This
, blockIndex
);
2870 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2872 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2875 return nextBlockIndex
;
2878 /******************************************************************************
2879 * Storage32Impl_SetNextBlockInChain
2881 * This method will write the index of the specified block's next block
2882 * in the big block depot.
2884 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2887 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2888 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2889 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2892 void StorageImpl_SetNextBlockInChain(
2897 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2898 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2899 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2900 ULONG depotBlockIndexPos
;
2903 assert(depotBlockCount
< This
->bigBlockDepotCount
);
2904 assert(blockIndex
!= nextBlock
);
2906 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2908 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2913 * We have to look in the extended depot.
2915 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2918 depotBuffer
= StorageImpl_GetBigBlock(This
, depotBlockIndexPos
);
2922 StorageUtl_WriteDWord(depotBuffer
, depotBlockOffset
, nextBlock
);
2923 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2927 * Update the cached block depot, if necessary.
2929 if (depotBlockCount
== This
->indexBlockDepotCached
)
2931 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
2935 /******************************************************************************
2936 * Storage32Impl_LoadFileHeader
2938 * This method will read in the file header, i.e. big block index -1.
2940 HRESULT
StorageImpl_LoadFileHeader(
2943 HRESULT hr
= STG_E_FILENOTFOUND
;
2944 void* headerBigBlock
= NULL
;
2948 * Get a pointer to the big block of data containing the header.
2950 headerBigBlock
= StorageImpl_GetROBigBlock(This
, -1);
2953 * Extract the information from the header.
2955 if (headerBigBlock
!=0)
2958 * Check for the "magic number" signature and return an error if it is not
2961 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
2963 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2964 return STG_E_OLDFORMAT
;
2967 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
2969 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2970 return STG_E_INVALIDHEADER
;
2973 StorageUtl_ReadWord(
2975 OFFSET_BIGBLOCKSIZEBITS
,
2976 &This
->bigBlockSizeBits
);
2978 StorageUtl_ReadWord(
2980 OFFSET_SMALLBLOCKSIZEBITS
,
2981 &This
->smallBlockSizeBits
);
2983 StorageUtl_ReadDWord(
2985 OFFSET_BBDEPOTCOUNT
,
2986 &This
->bigBlockDepotCount
);
2988 StorageUtl_ReadDWord(
2990 OFFSET_ROOTSTARTBLOCK
,
2991 &This
->rootStartBlock
);
2993 StorageUtl_ReadDWord(
2995 OFFSET_SBDEPOTSTART
,
2996 &This
->smallBlockDepotStart
);
2998 StorageUtl_ReadDWord(
3000 OFFSET_EXTBBDEPOTSTART
,
3001 &This
->extBigBlockDepotStart
);
3003 StorageUtl_ReadDWord(
3005 OFFSET_EXTBBDEPOTCOUNT
,
3006 &This
->extBigBlockDepotCount
);
3008 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3010 StorageUtl_ReadDWord(
3012 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3013 &(This
->bigBlockDepotStart
[index
]));
3017 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3021 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3022 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3026 This
->bigBlockSize
= 0x000000001 >> (DWORD
)This
->bigBlockSizeBits
;
3027 This
->smallBlockSize
= 0x000000001 >> (DWORD
)This
->smallBlockSizeBits
;
3031 * Right now, the code is making some assumptions about the size of the
3032 * blocks, just make sure they are what we're expecting.
3034 if (This
->bigBlockSize
!= DEF_BIG_BLOCK_SIZE
||
3035 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
)
3037 WARN("Broken OLE storage file\n");
3038 hr
= STG_E_INVALIDHEADER
;
3044 * Release the block.
3046 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
3052 /******************************************************************************
3053 * Storage32Impl_SaveFileHeader
3055 * This method will save to the file the header, i.e. big block -1.
3057 void StorageImpl_SaveFileHeader(
3060 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3065 * Get a pointer to the big block of data containing the header.
3067 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3070 * If the block read failed, the file is probably new.
3075 * Initialize for all unknown fields.
3077 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
3080 * Initialize the magic number.
3082 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3085 * And a bunch of things we don't know what they mean
3087 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3088 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3089 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3090 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
3094 * Write the information to the header.
3096 StorageUtl_WriteWord(
3098 OFFSET_BIGBLOCKSIZEBITS
,
3099 This
->bigBlockSizeBits
);
3101 StorageUtl_WriteWord(
3103 OFFSET_SMALLBLOCKSIZEBITS
,
3104 This
->smallBlockSizeBits
);
3106 StorageUtl_WriteDWord(
3108 OFFSET_BBDEPOTCOUNT
,
3109 This
->bigBlockDepotCount
);
3111 StorageUtl_WriteDWord(
3113 OFFSET_ROOTSTARTBLOCK
,
3114 This
->rootStartBlock
);
3116 StorageUtl_WriteDWord(
3118 OFFSET_SBDEPOTSTART
,
3119 This
->smallBlockDepotStart
);
3121 StorageUtl_WriteDWord(
3123 OFFSET_SBDEPOTCOUNT
,
3124 This
->smallBlockDepotChain
?
3125 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3127 StorageUtl_WriteDWord(
3129 OFFSET_EXTBBDEPOTSTART
,
3130 This
->extBigBlockDepotStart
);
3132 StorageUtl_WriteDWord(
3134 OFFSET_EXTBBDEPOTCOUNT
,
3135 This
->extBigBlockDepotCount
);
3137 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3139 StorageUtl_WriteDWord(
3141 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3142 (This
->bigBlockDepotStart
[index
]));
3146 * Write the big block back to the file.
3148 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
3151 /******************************************************************************
3152 * Storage32Impl_ReadProperty
3154 * This method will read the specified property from the property chain.
3156 BOOL
StorageImpl_ReadProperty(
3159 StgProperty
* buffer
)
3161 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
3162 ULARGE_INTEGER offsetInPropSet
;
3163 BOOL readSuccessful
;
3166 offsetInPropSet
.u
.HighPart
= 0;
3167 offsetInPropSet
.u
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
3169 readSuccessful
= BlockChainStream_ReadAt(
3170 This
->rootBlockChain
,
3178 /* replace the name of root entry (often "Root Entry") by the file name */
3179 WCHAR
*propName
= (index
== This
->base
.rootPropertySetIndex
) ?
3180 This
->filename
: (WCHAR
*)currentProperty
+OFFSET_PS_NAME
;
3182 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3186 PROPERTY_NAME_BUFFER_LEN
);
3187 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3189 memcpy(&buffer
->propertyType
, currentProperty
+ OFFSET_PS_PROPERTYTYPE
, 1);
3191 StorageUtl_ReadWord(
3193 OFFSET_PS_NAMELENGTH
,
3194 &buffer
->sizeOfNameString
);
3196 StorageUtl_ReadDWord(
3198 OFFSET_PS_PREVIOUSPROP
,
3199 &buffer
->previousProperty
);
3201 StorageUtl_ReadDWord(
3204 &buffer
->nextProperty
);
3206 StorageUtl_ReadDWord(
3209 &buffer
->dirProperty
);
3211 StorageUtl_ReadGUID(
3214 &buffer
->propertyUniqueID
);
3216 StorageUtl_ReadDWord(
3219 &buffer
->timeStampS1
);
3221 StorageUtl_ReadDWord(
3224 &buffer
->timeStampD1
);
3226 StorageUtl_ReadDWord(
3229 &buffer
->timeStampS2
);
3231 StorageUtl_ReadDWord(
3234 &buffer
->timeStampD2
);
3236 StorageUtl_ReadDWord(
3238 OFFSET_PS_STARTBLOCK
,
3239 &buffer
->startingBlock
);
3241 StorageUtl_ReadDWord(
3244 &buffer
->size
.u
.LowPart
);
3246 buffer
->size
.u
.HighPart
= 0;
3249 return readSuccessful
;
3252 /*********************************************************************
3253 * Write the specified property into the property chain
3255 BOOL
StorageImpl_WriteProperty(
3258 StgProperty
* buffer
)
3260 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
3261 ULARGE_INTEGER offsetInPropSet
;
3262 BOOL writeSuccessful
;
3265 offsetInPropSet
.u
.HighPart
= 0;
3266 offsetInPropSet
.u
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
3268 memset(currentProperty
, 0, PROPSET_BLOCK_SIZE
);
3271 currentProperty
+ OFFSET_PS_NAME
,
3273 PROPERTY_NAME_BUFFER_LEN
);
3275 memcpy(currentProperty
+ OFFSET_PS_PROPERTYTYPE
, &buffer
->propertyType
, 1);
3277 StorageUtl_WriteWord(
3279 OFFSET_PS_NAMELENGTH
,
3280 buffer
->sizeOfNameString
);
3282 StorageUtl_WriteDWord(
3284 OFFSET_PS_PREVIOUSPROP
,
3285 buffer
->previousProperty
);
3287 StorageUtl_WriteDWord(
3290 buffer
->nextProperty
);
3292 StorageUtl_WriteDWord(
3295 buffer
->dirProperty
);
3297 StorageUtl_WriteGUID(
3300 &buffer
->propertyUniqueID
);
3302 StorageUtl_WriteDWord(
3305 buffer
->timeStampS1
);
3307 StorageUtl_WriteDWord(
3310 buffer
->timeStampD1
);
3312 StorageUtl_WriteDWord(
3315 buffer
->timeStampS2
);
3317 StorageUtl_WriteDWord(
3320 buffer
->timeStampD2
);
3322 StorageUtl_WriteDWord(
3324 OFFSET_PS_STARTBLOCK
,
3325 buffer
->startingBlock
);
3327 StorageUtl_WriteDWord(
3330 buffer
->size
.u
.LowPart
);
3332 writeSuccessful
= BlockChainStream_WriteAt(This
->rootBlockChain
,
3337 return writeSuccessful
;
3340 BOOL
StorageImpl_ReadBigBlock(
3345 void* bigBlockBuffer
;
3347 bigBlockBuffer
= StorageImpl_GetROBigBlock(This
, blockIndex
);
3349 if (bigBlockBuffer
!=0)
3351 memcpy(buffer
, bigBlockBuffer
, This
->bigBlockSize
);
3353 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
3361 BOOL
StorageImpl_WriteBigBlock(
3366 void* bigBlockBuffer
;
3368 bigBlockBuffer
= StorageImpl_GetBigBlock(This
, blockIndex
);
3370 if (bigBlockBuffer
!=0)
3372 memcpy(bigBlockBuffer
, buffer
, This
->bigBlockSize
);
3374 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
3382 void* StorageImpl_GetROBigBlock(
3386 return BIGBLOCKFILE_GetROBigBlock(This
->bigBlockFile
, blockIndex
);
3389 void* StorageImpl_GetBigBlock(
3393 return BIGBLOCKFILE_GetBigBlock(This
->bigBlockFile
, blockIndex
);
3396 void StorageImpl_ReleaseBigBlock(
3400 BIGBLOCKFILE_ReleaseBigBlock(This
->bigBlockFile
, pBigBlock
);
3403 /******************************************************************************
3404 * Storage32Impl_SmallBlocksToBigBlocks
3406 * This method will convert a small block chain to a big block chain.
3407 * The small block chain will be destroyed.
3409 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3411 SmallBlockChainStream
** ppsbChain
)
3413 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3414 ULARGE_INTEGER size
, offset
;
3415 ULONG cbRead
, cbWritten
, cbTotalRead
, cbTotalWritten
;
3416 ULONG propertyIndex
;
3417 BOOL successRead
, successWrite
;
3418 StgProperty chainProperty
;
3420 BlockChainStream
*bbTempChain
= NULL
;
3421 BlockChainStream
*bigBlockChain
= NULL
;
3424 * Create a temporary big block chain that doesn't have
3425 * an associated property. This temporary chain will be
3426 * used to copy data from small blocks to big blocks.
3428 bbTempChain
= BlockChainStream_Construct(This
,
3431 if(!bbTempChain
) return NULL
;
3433 * Grow the big block chain.
3435 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3436 BlockChainStream_SetSize(bbTempChain
, size
);
3439 * Copy the contents of the small block chain to the big block chain
3440 * by small block size increments.
3442 offset
.u
.LowPart
= 0;
3443 offset
.u
.HighPart
= 0;
3447 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3450 successRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3452 DEF_SMALL_BLOCK_SIZE
,
3455 cbTotalRead
+= cbRead
;
3457 successWrite
= BlockChainStream_WriteAt(bbTempChain
,
3462 cbTotalWritten
+= cbWritten
;
3464 offset
.u
.LowPart
+= This
->smallBlockSize
;
3466 } while (successRead
&& successWrite
);
3467 HeapFree(GetProcessHeap(),0,buffer
);
3469 assert(cbTotalRead
== cbTotalWritten
);
3472 * Destroy the small block chain.
3474 propertyIndex
= (*ppsbChain
)->ownerPropertyIndex
;
3475 size
.u
.HighPart
= 0;
3477 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3478 SmallBlockChainStream_Destroy(*ppsbChain
);
3482 * Change the property information. This chain is now a big block chain
3483 * and it doesn't reside in the small blocks chain anymore.
3485 StorageImpl_ReadProperty(This
, propertyIndex
, &chainProperty
);
3487 chainProperty
.startingBlock
= bbHeadOfChain
;
3489 StorageImpl_WriteProperty(This
, propertyIndex
, &chainProperty
);
3492 * Destroy the temporary propertyless big block chain.
3493 * Create a new big block chain associated with this property.
3495 BlockChainStream_Destroy(bbTempChain
);
3496 bigBlockChain
= BlockChainStream_Construct(This
,
3500 return bigBlockChain
;
3503 void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
3505 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
3507 StorageBaseImpl_Release((IStorage
*)This
->base
.ancestorStorage
);
3508 HeapFree(GetProcessHeap(), 0, This
);
3511 /******************************************************************************
3513 ** Storage32InternalImpl_Commit
3515 ** The non-root storages cannot be opened in transacted mode thus this function
3518 HRESULT WINAPI
StorageInternalImpl_Commit(
3520 DWORD grfCommitFlags
) /* [in] */
3525 /******************************************************************************
3527 ** Storage32InternalImpl_Revert
3529 ** The non-root storages cannot be opened in transacted mode thus this function
3532 HRESULT WINAPI
StorageInternalImpl_Revert(
3538 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
3540 IStorage_Release((IStorage
*)This
->parentStorage
);
3541 HeapFree(GetProcessHeap(), 0, This
->stackToVisit
);
3542 HeapFree(GetProcessHeap(), 0, This
);
3545 HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
3546 IEnumSTATSTG
* iface
,
3550 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3553 * Perform a sanity check on the parameters.
3556 return E_INVALIDARG
;
3559 * Initialize the return parameter.
3564 * Compare the riid with the interface IDs implemented by this object.
3566 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
3567 IsEqualGUID(&IID_IStorage
, riid
))
3569 *ppvObject
= (IEnumSTATSTG
*)This
;
3570 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
3574 return E_NOINTERFACE
;
3577 ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
3578 IEnumSTATSTG
* iface
)
3580 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3581 return InterlockedIncrement(&This
->ref
);
3584 ULONG WINAPI
IEnumSTATSTGImpl_Release(
3585 IEnumSTATSTG
* iface
)
3587 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3591 newRef
= InterlockedDecrement(&This
->ref
);
3594 * If the reference count goes down to 0, perform suicide.
3598 IEnumSTATSTGImpl_Destroy(This
);
3604 HRESULT WINAPI
IEnumSTATSTGImpl_Next(
3605 IEnumSTATSTG
* iface
,
3608 ULONG
* pceltFetched
)
3610 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3612 StgProperty currentProperty
;
3613 STATSTG
* currentReturnStruct
= rgelt
;
3614 ULONG objectFetched
= 0;
3615 ULONG currentSearchNode
;
3618 * Perform a sanity check on the parameters.
3620 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
3621 return E_INVALIDARG
;
3624 * To avoid the special case, get another pointer to a ULONG value if
3625 * the caller didn't supply one.
3627 if (pceltFetched
==0)
3628 pceltFetched
= &objectFetched
;
3631 * Start the iteration, we will iterate until we hit the end of the
3632 * linked list or until we hit the number of items to iterate through
3637 * Start with the node at the top of the stack.
3639 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3641 while ( ( *pceltFetched
< celt
) &&
3642 ( currentSearchNode
!=PROPERTY_NULL
) )
3645 * Remove the top node from the stack
3647 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3650 * Read the property from the storage.
3652 StorageImpl_ReadProperty(This
->parentStorage
,
3657 * Copy the information to the return buffer.
3659 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct
,
3664 * Step to the next item in the iteration
3667 currentReturnStruct
++;
3670 * Push the next search node in the search stack.
3672 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3675 * continue the iteration.
3677 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3680 if (*pceltFetched
== celt
)
3687 HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
3688 IEnumSTATSTG
* iface
,
3691 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3693 StgProperty currentProperty
;
3694 ULONG objectFetched
= 0;
3695 ULONG currentSearchNode
;
3698 * Start with the node at the top of the stack.
3700 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3702 while ( (objectFetched
< celt
) &&
3703 (currentSearchNode
!=PROPERTY_NULL
) )
3706 * Remove the top node from the stack
3708 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3711 * Read the property from the storage.
3713 StorageImpl_ReadProperty(This
->parentStorage
,
3718 * Step to the next item in the iteration
3723 * Push the next search node in the search stack.
3725 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3728 * continue the iteration.
3730 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3733 if (objectFetched
== celt
)
3739 HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
3740 IEnumSTATSTG
* iface
)
3742 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3744 StgProperty rootProperty
;
3745 BOOL readSuccessful
;
3748 * Re-initialize the search stack to an empty stack
3750 This
->stackSize
= 0;
3753 * Read the root property from the storage.
3755 readSuccessful
= StorageImpl_ReadProperty(
3756 This
->parentStorage
,
3757 This
->firstPropertyNode
,
3762 assert(rootProperty
.sizeOfNameString
!=0);
3765 * Push the search node in the search stack.
3767 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.dirProperty
);
3773 HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
3774 IEnumSTATSTG
* iface
,
3775 IEnumSTATSTG
** ppenum
)
3777 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3779 IEnumSTATSTGImpl
* newClone
;
3782 * Perform a sanity check on the parameters.
3785 return E_INVALIDARG
;
3787 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
3788 This
->firstPropertyNode
);
3792 * The new clone enumeration must point to the same current node as
3795 newClone
->stackSize
= This
->stackSize
;
3796 newClone
->stackMaxSize
= This
->stackMaxSize
;
3797 newClone
->stackToVisit
=
3798 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * newClone
->stackMaxSize
);
3801 newClone
->stackToVisit
,
3803 sizeof(ULONG
) * newClone
->stackSize
);
3805 *ppenum
= (IEnumSTATSTG
*)newClone
;
3808 * Don't forget to nail down a reference to the clone before
3811 IEnumSTATSTGImpl_AddRef(*ppenum
);
3816 INT
IEnumSTATSTGImpl_FindParentProperty(
3817 IEnumSTATSTGImpl
*This
,
3818 ULONG childProperty
,
3819 StgProperty
*currentProperty
,
3822 ULONG currentSearchNode
;
3826 * To avoid the special case, get another pointer to a ULONG value if
3827 * the caller didn't supply one.
3830 thisNodeId
= &foundNode
;
3833 * Start with the node at the top of the stack.
3835 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3838 while (currentSearchNode
!=PROPERTY_NULL
)
3841 * Store the current node in the returned parameters
3843 *thisNodeId
= currentSearchNode
;
3846 * Remove the top node from the stack
3848 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3851 * Read the property from the storage.
3853 StorageImpl_ReadProperty(
3854 This
->parentStorage
,
3858 if (currentProperty
->previousProperty
== childProperty
)
3859 return PROPERTY_RELATION_PREVIOUS
;
3861 else if (currentProperty
->nextProperty
== childProperty
)
3862 return PROPERTY_RELATION_NEXT
;
3864 else if (currentProperty
->dirProperty
== childProperty
)
3865 return PROPERTY_RELATION_DIR
;
3868 * Push the next search node in the search stack.
3870 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
3873 * continue the iteration.
3875 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3878 return PROPERTY_NULL
;
3881 ULONG
IEnumSTATSTGImpl_FindProperty(
3882 IEnumSTATSTGImpl
* This
,
3883 const OLECHAR
* lpszPropName
,
3884 StgProperty
* currentProperty
)
3886 ULONG currentSearchNode
;
3889 * Start with the node at the top of the stack.
3891 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3893 while (currentSearchNode
!=PROPERTY_NULL
)
3896 * Remove the top node from the stack
3898 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3901 * Read the property from the storage.
3903 StorageImpl_ReadProperty(This
->parentStorage
,
3907 if ( propertyNameCmp(
3908 (const OLECHAR
*)currentProperty
->name
,
3909 (const OLECHAR
*)lpszPropName
) == 0)
3910 return currentSearchNode
;
3913 * Push the next search node in the search stack.
3915 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
3918 * continue the iteration.
3920 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3923 return PROPERTY_NULL
;
3926 void IEnumSTATSTGImpl_PushSearchNode(
3927 IEnumSTATSTGImpl
* This
,
3930 StgProperty rootProperty
;
3931 BOOL readSuccessful
;
3934 * First, make sure we're not trying to push an unexisting node.
3936 if (nodeToPush
==PROPERTY_NULL
)
3940 * First push the node to the stack
3942 if (This
->stackSize
== This
->stackMaxSize
)
3944 This
->stackMaxSize
+= ENUMSTATSGT_SIZE_INCREMENT
;
3946 This
->stackToVisit
= HeapReAlloc(
3950 sizeof(ULONG
) * This
->stackMaxSize
);
3953 This
->stackToVisit
[This
->stackSize
] = nodeToPush
;
3957 * Read the root property from the storage.
3959 readSuccessful
= StorageImpl_ReadProperty(
3960 This
->parentStorage
,
3966 assert(rootProperty
.sizeOfNameString
!=0);
3969 * Push the previous search node in the search stack.
3971 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.previousProperty
);
3975 ULONG
IEnumSTATSTGImpl_PopSearchNode(
3976 IEnumSTATSTGImpl
* This
,
3981 if (This
->stackSize
== 0)
3982 return PROPERTY_NULL
;
3984 topNode
= This
->stackToVisit
[This
->stackSize
-1];
3993 * Virtual function table for the IEnumSTATSTGImpl class.
3995 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
3997 IEnumSTATSTGImpl_QueryInterface
,
3998 IEnumSTATSTGImpl_AddRef
,
3999 IEnumSTATSTGImpl_Release
,
4000 IEnumSTATSTGImpl_Next
,
4001 IEnumSTATSTGImpl_Skip
,
4002 IEnumSTATSTGImpl_Reset
,
4003 IEnumSTATSTGImpl_Clone
4006 /******************************************************************************
4007 ** IEnumSTATSTGImpl implementation
4010 IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
4011 StorageImpl
* parentStorage
,
4012 ULONG firstPropertyNode
)
4014 IEnumSTATSTGImpl
* newEnumeration
;
4016 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
4018 if (newEnumeration
!=0)
4021 * Set-up the virtual function table and reference count.
4023 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
4024 newEnumeration
->ref
= 0;
4027 * We want to nail-down the reference to the storage in case the
4028 * enumeration out-lives the storage in the client application.
4030 newEnumeration
->parentStorage
= parentStorage
;
4031 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
4033 newEnumeration
->firstPropertyNode
= firstPropertyNode
;
4036 * Initialize the search stack
4038 newEnumeration
->stackSize
= 0;
4039 newEnumeration
->stackMaxSize
= ENUMSTATSGT_SIZE_INCREMENT
;
4040 newEnumeration
->stackToVisit
=
4041 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
)*ENUMSTATSGT_SIZE_INCREMENT
);
4044 * Make sure the current node of the iterator is the first one.
4046 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
4049 return newEnumeration
;
4053 * Virtual function table for the Storage32InternalImpl class.
4055 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
4057 StorageBaseImpl_QueryInterface
,
4058 StorageBaseImpl_AddRef
,
4059 StorageBaseImpl_Release
,
4060 StorageBaseImpl_CreateStream
,
4061 StorageBaseImpl_OpenStream
,
4062 StorageImpl_CreateStorage
,
4063 StorageBaseImpl_OpenStorage
,
4065 StorageImpl_MoveElementTo
,
4066 StorageInternalImpl_Commit
,
4067 StorageInternalImpl_Revert
,
4068 StorageBaseImpl_EnumElements
,
4069 StorageImpl_DestroyElement
,
4070 StorageBaseImpl_RenameElement
,
4071 StorageImpl_SetElementTimes
,
4072 StorageBaseImpl_SetClass
,
4073 StorageImpl_SetStateBits
,
4074 StorageBaseImpl_Stat
4077 /******************************************************************************
4078 ** Storage32InternalImpl implementation
4081 StorageInternalImpl
* StorageInternalImpl_Construct(
4082 StorageImpl
* ancestorStorage
,
4084 ULONG rootPropertyIndex
)
4086 StorageInternalImpl
* newStorage
;
4089 * Allocate space for the new storage object
4091 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl
));
4095 memset(newStorage
, 0, sizeof(StorageInternalImpl
));
4098 * Initialize the virtual function table.
4100 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
4101 newStorage
->base
.v_destructor
= &StorageInternalImpl_Destroy
;
4102 newStorage
->base
.openFlags
= openFlags
;
4105 * Keep the ancestor storage pointer and nail a reference to it.
4107 newStorage
->base
.ancestorStorage
= ancestorStorage
;
4108 StorageBaseImpl_AddRef((IStorage
*)(newStorage
->base
.ancestorStorage
));
4111 * Keep the index of the root property set for this storage,
4113 newStorage
->base
.rootPropertySetIndex
= rootPropertyIndex
;
4121 /******************************************************************************
4122 ** StorageUtl implementation
4125 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
4129 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
4130 *value
= le16toh(tmp
);
4133 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
4135 value
= htole16(value
);
4136 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
4139 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
4143 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
4144 *value
= le32toh(tmp
);
4147 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
4149 value
= htole32(value
);
4150 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
4153 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
4154 ULARGE_INTEGER
* value
)
4156 #ifdef WORDS_BIGENDIAN
4159 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4160 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
4161 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
4163 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4167 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
4168 const ULARGE_INTEGER
*value
)
4170 #ifdef WORDS_BIGENDIAN
4173 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
4174 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
4175 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
4177 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
4181 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
4183 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
4184 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
4185 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
4187 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
4190 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
4192 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
4193 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
4194 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
4196 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
4199 void StorageUtl_CopyPropertyToSTATSTG(
4200 STATSTG
* destination
,
4201 StgProperty
* source
,
4205 * The copy of the string occurs only when the flag is not set
4207 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
4208 (source
->name
== NULL
) ||
4209 (source
->name
[0] == 0) )
4211 destination
->pwcsName
= 0;
4215 destination
->pwcsName
=
4216 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
4218 strcpyW((LPWSTR
)destination
->pwcsName
, source
->name
);
4221 switch (source
->propertyType
)
4223 case PROPTYPE_STORAGE
:
4225 destination
->type
= STGTY_STORAGE
;
4227 case PROPTYPE_STREAM
:
4228 destination
->type
= STGTY_STREAM
;
4231 destination
->type
= STGTY_STREAM
;
4235 destination
->cbSize
= source
->size
;
4237 currentReturnStruct->mtime = {0}; TODO
4238 currentReturnStruct->ctime = {0};
4239 currentReturnStruct->atime = {0};
4241 destination
->grfMode
= 0;
4242 destination
->grfLocksSupported
= 0;
4243 destination
->clsid
= source
->propertyUniqueID
;
4244 destination
->grfStateBits
= 0;
4245 destination
->reserved
= 0;
4248 /******************************************************************************
4249 ** BlockChainStream implementation
4252 BlockChainStream
* BlockChainStream_Construct(
4253 StorageImpl
* parentStorage
,
4254 ULONG
* headOfStreamPlaceHolder
,
4255 ULONG propertyIndex
)
4257 BlockChainStream
* newStream
;
4260 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
4262 newStream
->parentStorage
= parentStorage
;
4263 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
4264 newStream
->ownerPropertyIndex
= propertyIndex
;
4265 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
4266 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
4267 newStream
->numBlocks
= 0;
4269 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
4271 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4273 newStream
->numBlocks
++;
4274 newStream
->tailIndex
= blockIndex
;
4276 if(FAILED(StorageImpl_GetNextBlockInChain(
4281 HeapFree(GetProcessHeap(), 0, newStream
);
4289 void BlockChainStream_Destroy(BlockChainStream
* This
)
4291 HeapFree(GetProcessHeap(), 0, This
);
4294 /******************************************************************************
4295 * BlockChainStream_GetHeadOfChain
4297 * Returns the head of this stream chain.
4298 * Some special chains don't have properties, their heads are kept in
4299 * This->headOfStreamPlaceHolder.
4302 ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
4304 StgProperty chainProperty
;
4305 BOOL readSuccessful
;
4307 if (This
->headOfStreamPlaceHolder
!= 0)
4308 return *(This
->headOfStreamPlaceHolder
);
4310 if (This
->ownerPropertyIndex
!= PROPERTY_NULL
)
4312 readSuccessful
= StorageImpl_ReadProperty(
4313 This
->parentStorage
,
4314 This
->ownerPropertyIndex
,
4319 return chainProperty
.startingBlock
;
4323 return BLOCK_END_OF_CHAIN
;
4326 /******************************************************************************
4327 * BlockChainStream_GetCount
4329 * Returns the number of blocks that comprises this chain.
4330 * This is not the size of the stream as the last block may not be full!
4333 ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
4338 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4340 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4344 if(FAILED(StorageImpl_GetNextBlockInChain(
4345 This
->parentStorage
,
4354 /******************************************************************************
4355 * BlockChainStream_ReadAt
4357 * Reads a specified number of bytes from this chain at the specified offset.
4358 * bytesRead may be NULL.
4359 * Failure will be returned if the specified number of bytes has not been read.
4361 BOOL
BlockChainStream_ReadAt(BlockChainStream
* This
,
4362 ULARGE_INTEGER offset
,
4367 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4368 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4369 ULONG bytesToReadInBuffer
;
4372 BYTE
* bigBlockBuffer
;
4375 * Find the first block in the stream that contains part of the buffer.
4377 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4378 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4379 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4381 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4382 This
->lastBlockNoInSequence
= blockNoInSequence
;
4386 ULONG temp
= blockNoInSequence
;
4388 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4389 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4390 This
->lastBlockNoInSequence
= temp
;
4393 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4395 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4397 blockNoInSequence
--;
4400 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4403 * Start reading the buffer.
4406 bufferWalker
= buffer
;
4408 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4411 * Calculate how many bytes we can copy from this big block.
4413 bytesToReadInBuffer
=
4414 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4417 * Copy those bytes to the buffer
4420 StorageImpl_GetROBigBlock(This
->parentStorage
, blockIndex
);
4422 memcpy(bufferWalker
, bigBlockBuffer
+ offsetInBlock
, bytesToReadInBuffer
);
4424 StorageImpl_ReleaseBigBlock(This
->parentStorage
, bigBlockBuffer
);
4427 * Step to the next big block.
4429 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4432 bufferWalker
+= bytesToReadInBuffer
;
4433 size
-= bytesToReadInBuffer
;
4434 *bytesRead
+= bytesToReadInBuffer
;
4435 offsetInBlock
= 0; /* There is no offset on the next block */
4442 /******************************************************************************
4443 * BlockChainStream_WriteAt
4445 * Writes the specified number of bytes to this chain at the specified offset.
4446 * bytesWritten may be NULL.
4447 * Will fail if not all specified number of bytes have been written.
4449 BOOL
BlockChainStream_WriteAt(BlockChainStream
* This
,
4450 ULARGE_INTEGER offset
,
4453 ULONG
* bytesWritten
)
4455 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4456 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4459 const BYTE
* bufferWalker
;
4460 BYTE
* bigBlockBuffer
;
4463 * Find the first block in the stream that contains part of the buffer.
4465 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4466 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4467 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4469 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4470 This
->lastBlockNoInSequence
= blockNoInSequence
;
4474 ULONG temp
= blockNoInSequence
;
4476 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4477 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4478 This
->lastBlockNoInSequence
= temp
;
4481 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4483 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4486 blockNoInSequence
--;
4489 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4492 * Here, I'm casting away the constness on the buffer variable
4493 * This is OK since we don't intend to modify that buffer.
4496 bufferWalker
= (const BYTE
*)buffer
;
4498 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4501 * Calculate how many bytes we can copy from this big block.
4504 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4507 * Copy those bytes to the buffer
4509 bigBlockBuffer
= StorageImpl_GetBigBlock(This
->parentStorage
, blockIndex
);
4511 memcpy(bigBlockBuffer
+ offsetInBlock
, bufferWalker
, bytesToWrite
);
4513 StorageImpl_ReleaseBigBlock(This
->parentStorage
, bigBlockBuffer
);
4516 * Step to the next big block.
4518 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4521 bufferWalker
+= bytesToWrite
;
4522 size
-= bytesToWrite
;
4523 *bytesWritten
+= bytesToWrite
;
4524 offsetInBlock
= 0; /* There is no offset on the next block */
4530 /******************************************************************************
4531 * BlockChainStream_Shrink
4533 * Shrinks this chain in the big block depot.
4535 BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
4536 ULARGE_INTEGER newSize
)
4538 ULONG blockIndex
, extraBlock
;
4543 * Reset the last accessed block cache.
4545 This
->lastBlockNoInSequence
= 0xFFFFFFFF;
4546 This
->lastBlockNoInSequenceIndex
= BLOCK_END_OF_CHAIN
;
4549 * Figure out how many blocks are needed to contain the new size
4551 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4553 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4556 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4559 * Go to the new end of chain
4561 while (count
< numBlocks
)
4563 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4569 /* Get the next block before marking the new end */
4570 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4574 /* Mark the new end of chain */
4575 StorageImpl_SetNextBlockInChain(
4576 This
->parentStorage
,
4578 BLOCK_END_OF_CHAIN
);
4580 This
->tailIndex
= blockIndex
;
4581 This
->numBlocks
= numBlocks
;
4584 * Mark the extra blocks as free
4586 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4588 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
,
4591 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
4592 extraBlock
= blockIndex
;
4598 /******************************************************************************
4599 * BlockChainStream_Enlarge
4601 * Grows this chain in the big block depot.
4603 BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
4604 ULARGE_INTEGER newSize
)
4606 ULONG blockIndex
, currentBlock
;
4608 ULONG oldNumBlocks
= 0;
4610 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4613 * Empty chain. Create the head.
4615 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4617 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4618 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
4620 BLOCK_END_OF_CHAIN
);
4622 if (This
->headOfStreamPlaceHolder
!= 0)
4624 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
4628 StgProperty chainProp
;
4629 assert(This
->ownerPropertyIndex
!= PROPERTY_NULL
);
4631 StorageImpl_ReadProperty(
4632 This
->parentStorage
,
4633 This
->ownerPropertyIndex
,
4636 chainProp
.startingBlock
= blockIndex
;
4638 StorageImpl_WriteProperty(
4639 This
->parentStorage
,
4640 This
->ownerPropertyIndex
,
4644 This
->tailIndex
= blockIndex
;
4645 This
->numBlocks
= 1;
4649 * Figure out how many blocks are needed to contain this stream
4651 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4653 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4657 * Go to the current end of chain
4659 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
4661 currentBlock
= blockIndex
;
4663 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4666 currentBlock
= blockIndex
;
4668 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
4673 This
->tailIndex
= currentBlock
;
4676 currentBlock
= This
->tailIndex
;
4677 oldNumBlocks
= This
->numBlocks
;
4680 * Add new blocks to the chain
4682 if (oldNumBlocks
< newNumBlocks
)
4684 while (oldNumBlocks
< newNumBlocks
)
4686 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4688 StorageImpl_SetNextBlockInChain(
4689 This
->parentStorage
,
4693 StorageImpl_SetNextBlockInChain(
4694 This
->parentStorage
,
4696 BLOCK_END_OF_CHAIN
);
4698 currentBlock
= blockIndex
;
4702 This
->tailIndex
= blockIndex
;
4703 This
->numBlocks
= newNumBlocks
;
4709 /******************************************************************************
4710 * BlockChainStream_SetSize
4712 * Sets the size of this stream. The big block depot will be updated.
4713 * The file will grow if we grow the chain.
4715 * TODO: Free the actual blocks in the file when we shrink the chain.
4716 * Currently, the blocks are still in the file. So the file size
4717 * doesn't shrink even if we shrink streams.
4719 BOOL
BlockChainStream_SetSize(
4720 BlockChainStream
* This
,
4721 ULARGE_INTEGER newSize
)
4723 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
4725 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
4728 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
4730 BlockChainStream_Shrink(This
, newSize
);
4734 ULARGE_INTEGER fileSize
=
4735 BIGBLOCKFILE_GetSize(This
->parentStorage
->bigBlockFile
);
4737 ULONG diff
= newSize
.u
.LowPart
- size
.u
.LowPart
;
4740 * Make sure the file stays a multiple of blocksize
4742 if ((diff
% This
->parentStorage
->bigBlockSize
) != 0)
4743 diff
+= (This
->parentStorage
->bigBlockSize
-
4744 (diff
% This
->parentStorage
->bigBlockSize
) );
4746 fileSize
.u
.LowPart
+= diff
;
4747 BIGBLOCKFILE_SetSize(This
->parentStorage
->bigBlockFile
, fileSize
);
4749 BlockChainStream_Enlarge(This
, newSize
);
4755 /******************************************************************************
4756 * BlockChainStream_GetSize
4758 * Returns the size of this chain.
4759 * Will return the block count if this chain doesn't have a property.
4761 ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
4763 StgProperty chainProperty
;
4765 if(This
->headOfStreamPlaceHolder
== NULL
)
4768 * This chain is a data stream read the property and return
4769 * the appropriate size
4771 StorageImpl_ReadProperty(
4772 This
->parentStorage
,
4773 This
->ownerPropertyIndex
,
4776 return chainProperty
.size
;
4781 * this chain is a chain that does not have a property, figure out the
4782 * size by making the product number of used blocks times the
4785 ULARGE_INTEGER result
;
4786 result
.u
.HighPart
= 0;
4789 BlockChainStream_GetCount(This
) *
4790 This
->parentStorage
->bigBlockSize
;
4796 /******************************************************************************
4797 ** SmallBlockChainStream implementation
4800 SmallBlockChainStream
* SmallBlockChainStream_Construct(
4801 StorageImpl
* parentStorage
,
4802 ULONG propertyIndex
)
4804 SmallBlockChainStream
* newStream
;
4806 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
4808 newStream
->parentStorage
= parentStorage
;
4809 newStream
->ownerPropertyIndex
= propertyIndex
;
4814 void SmallBlockChainStream_Destroy(
4815 SmallBlockChainStream
* This
)
4817 HeapFree(GetProcessHeap(), 0, This
);
4820 /******************************************************************************
4821 * SmallBlockChainStream_GetHeadOfChain
4823 * Returns the head of this chain of small blocks.
4825 ULONG
SmallBlockChainStream_GetHeadOfChain(
4826 SmallBlockChainStream
* This
)
4828 StgProperty chainProperty
;
4829 BOOL readSuccessful
;
4831 if (This
->ownerPropertyIndex
)
4833 readSuccessful
= StorageImpl_ReadProperty(
4834 This
->parentStorage
,
4835 This
->ownerPropertyIndex
,
4840 return chainProperty
.startingBlock
;
4845 return BLOCK_END_OF_CHAIN
;
4848 /******************************************************************************
4849 * SmallBlockChainStream_GetNextBlockInChain
4851 * Returns the index of the next small block in this chain.
4854 * - BLOCK_END_OF_CHAIN: end of this chain
4855 * - BLOCK_UNUSED: small block 'blockIndex' is free
4857 HRESULT
SmallBlockChainStream_GetNextBlockInChain(
4858 SmallBlockChainStream
* This
,
4860 ULONG
* nextBlockInChain
)
4862 ULARGE_INTEGER offsetOfBlockInDepot
;
4867 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
4869 offsetOfBlockInDepot
.u
.HighPart
= 0;
4870 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4873 * Read those bytes in the buffer from the small block file.
4875 success
= BlockChainStream_ReadAt(
4876 This
->parentStorage
->smallBlockDepotChain
,
4877 offsetOfBlockInDepot
,
4884 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
4888 return STG_E_READFAULT
;
4891 /******************************************************************************
4892 * SmallBlockChainStream_SetNextBlockInChain
4894 * Writes the index of the next block of the specified block in the small
4896 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4897 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4899 void SmallBlockChainStream_SetNextBlockInChain(
4900 SmallBlockChainStream
* This
,
4904 ULARGE_INTEGER offsetOfBlockInDepot
;
4908 offsetOfBlockInDepot
.u
.HighPart
= 0;
4909 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4911 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
4914 * Read those bytes in the buffer from the small block file.
4916 BlockChainStream_WriteAt(
4917 This
->parentStorage
->smallBlockDepotChain
,
4918 offsetOfBlockInDepot
,
4924 /******************************************************************************
4925 * SmallBlockChainStream_FreeBlock
4927 * Flag small block 'blockIndex' as free in the small block depot.
4929 void SmallBlockChainStream_FreeBlock(
4930 SmallBlockChainStream
* This
,
4933 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
4936 /******************************************************************************
4937 * SmallBlockChainStream_GetNextFreeBlock
4939 * Returns the index of a free small block. The small block depot will be
4940 * enlarged if necessary. The small block chain will also be enlarged if
4943 ULONG
SmallBlockChainStream_GetNextFreeBlock(
4944 SmallBlockChainStream
* This
)
4946 ULARGE_INTEGER offsetOfBlockInDepot
;
4949 ULONG blockIndex
= 0;
4950 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
4951 BOOL success
= TRUE
;
4952 ULONG smallBlocksPerBigBlock
;
4954 offsetOfBlockInDepot
.u
.HighPart
= 0;
4957 * Scan the small block depot for a free block
4959 while (nextBlockIndex
!= BLOCK_UNUSED
)
4961 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4963 success
= BlockChainStream_ReadAt(
4964 This
->parentStorage
->smallBlockDepotChain
,
4965 offsetOfBlockInDepot
,
4971 * If we run out of space for the small block depot, enlarge it
4975 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
4977 if (nextBlockIndex
!= BLOCK_UNUSED
)
4983 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
4985 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
4986 ULONG nextBlock
, newsbdIndex
;
4987 BYTE
* smallBlockDepot
;
4989 nextBlock
= sbdIndex
;
4990 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
4992 sbdIndex
= nextBlock
;
4993 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
, &nextBlock
);
4996 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4997 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
4998 StorageImpl_SetNextBlockInChain(
4999 This
->parentStorage
,
5003 StorageImpl_SetNextBlockInChain(
5004 This
->parentStorage
,
5006 BLOCK_END_OF_CHAIN
);
5009 * Initialize all the small blocks to free
5012 StorageImpl_GetBigBlock(This
->parentStorage
, newsbdIndex
);
5014 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
5015 StorageImpl_ReleaseBigBlock(This
->parentStorage
, smallBlockDepot
);
5020 * We have just created the small block depot.
5022 StgProperty rootProp
;
5026 * Save it in the header
5028 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
5029 StorageImpl_SaveFileHeader(This
->parentStorage
);
5032 * And allocate the first big block that will contain small blocks
5035 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5037 StorageImpl_SetNextBlockInChain(
5038 This
->parentStorage
,
5040 BLOCK_END_OF_CHAIN
);
5042 StorageImpl_ReadProperty(
5043 This
->parentStorage
,
5044 This
->parentStorage
->base
.rootPropertySetIndex
,
5047 rootProp
.startingBlock
= sbStartIndex
;
5048 rootProp
.size
.u
.HighPart
= 0;
5049 rootProp
.size
.u
.LowPart
= This
->parentStorage
->bigBlockSize
;
5051 StorageImpl_WriteProperty(
5052 This
->parentStorage
,
5053 This
->parentStorage
->base
.rootPropertySetIndex
,
5059 smallBlocksPerBigBlock
=
5060 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
5063 * Verify if we have to allocate big blocks to contain small blocks
5065 if (blockIndex
% smallBlocksPerBigBlock
== 0)
5067 StgProperty rootProp
;
5068 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
5070 StorageImpl_ReadProperty(
5071 This
->parentStorage
,
5072 This
->parentStorage
->base
.rootPropertySetIndex
,
5075 if (rootProp
.size
.u
.LowPart
<
5076 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
5078 rootProp
.size
.u
.LowPart
+= This
->parentStorage
->bigBlockSize
;
5080 BlockChainStream_SetSize(
5081 This
->parentStorage
->smallBlockRootChain
,
5084 StorageImpl_WriteProperty(
5085 This
->parentStorage
,
5086 This
->parentStorage
->base
.rootPropertySetIndex
,
5094 /******************************************************************************
5095 * SmallBlockChainStream_ReadAt
5097 * Reads a specified number of bytes from this chain at the specified offset.
5098 * bytesRead may be NULL.
5099 * Failure will be returned if the specified number of bytes has not been read.
5101 BOOL
SmallBlockChainStream_ReadAt(
5102 SmallBlockChainStream
* This
,
5103 ULARGE_INTEGER offset
,
5108 ULARGE_INTEGER offsetInBigBlockFile
;
5109 ULONG blockNoInSequence
=
5110 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5112 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5113 ULONG bytesToReadInBuffer
;
5115 ULONG bytesReadFromBigBlockFile
;
5119 * This should never happen on a small block file.
5121 assert(offset
.u
.HighPart
==0);
5124 * Find the first block in the stream that contains part of the buffer.
5126 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5128 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5130 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5133 blockNoInSequence
--;
5137 * Start reading the buffer.
5140 bufferWalker
= buffer
;
5142 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5145 * Calculate how many bytes we can copy from this small block.
5147 bytesToReadInBuffer
=
5148 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5151 * Calculate the offset of the small block in the small block file.
5153 offsetInBigBlockFile
.u
.HighPart
= 0;
5154 offsetInBigBlockFile
.u
.LowPart
=
5155 blockIndex
* This
->parentStorage
->smallBlockSize
;
5157 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5160 * Read those bytes in the buffer from the small block file.
5162 BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
5163 offsetInBigBlockFile
,
5164 bytesToReadInBuffer
,
5166 &bytesReadFromBigBlockFile
);
5168 assert(bytesReadFromBigBlockFile
== bytesToReadInBuffer
);
5171 * Step to the next big block.
5173 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5175 bufferWalker
+= bytesToReadInBuffer
;
5176 size
-= bytesToReadInBuffer
;
5177 *bytesRead
+= bytesToReadInBuffer
;
5178 offsetInBlock
= 0; /* There is no offset on the next block */
5184 /******************************************************************************
5185 * SmallBlockChainStream_WriteAt
5187 * Writes the specified number of bytes to this chain at the specified offset.
5188 * bytesWritten may be NULL.
5189 * Will fail if not all specified number of bytes have been written.
5191 BOOL
SmallBlockChainStream_WriteAt(
5192 SmallBlockChainStream
* This
,
5193 ULARGE_INTEGER offset
,
5196 ULONG
* bytesWritten
)
5198 ULARGE_INTEGER offsetInBigBlockFile
;
5199 ULONG blockNoInSequence
=
5200 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5202 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5203 ULONG bytesToWriteInBuffer
;
5205 ULONG bytesWrittenFromBigBlockFile
;
5206 const BYTE
* bufferWalker
;
5209 * This should never happen on a small block file.
5211 assert(offset
.u
.HighPart
==0);
5214 * Find the first block in the stream that contains part of the buffer.
5216 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5218 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5220 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5222 blockNoInSequence
--;
5226 * Start writing the buffer.
5228 * Here, I'm casting away the constness on the buffer variable
5229 * This is OK since we don't intend to modify that buffer.
5232 bufferWalker
= (const BYTE
*)buffer
;
5233 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5236 * Calculate how many bytes we can copy to this small block.
5238 bytesToWriteInBuffer
=
5239 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5242 * Calculate the offset of the small block in the small block file.
5244 offsetInBigBlockFile
.u
.HighPart
= 0;
5245 offsetInBigBlockFile
.u
.LowPart
=
5246 blockIndex
* This
->parentStorage
->smallBlockSize
;
5248 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5251 * Write those bytes in the buffer to the small block file.
5253 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockRootChain
,
5254 offsetInBigBlockFile
,
5255 bytesToWriteInBuffer
,
5257 &bytesWrittenFromBigBlockFile
);
5259 assert(bytesWrittenFromBigBlockFile
== bytesToWriteInBuffer
);
5262 * Step to the next big block.
5264 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5267 bufferWalker
+= bytesToWriteInBuffer
;
5268 size
-= bytesToWriteInBuffer
;
5269 *bytesWritten
+= bytesToWriteInBuffer
;
5270 offsetInBlock
= 0; /* There is no offset on the next block */
5276 /******************************************************************************
5277 * SmallBlockChainStream_Shrink
5279 * Shrinks this chain in the small block depot.
5281 BOOL
SmallBlockChainStream_Shrink(
5282 SmallBlockChainStream
* This
,
5283 ULARGE_INTEGER newSize
)
5285 ULONG blockIndex
, extraBlock
;
5289 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5291 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5294 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5297 * Go to the new end of chain
5299 while (count
< numBlocks
)
5301 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5308 * If the count is 0, we have a special case, the head of the chain was
5313 StgProperty chainProp
;
5315 StorageImpl_ReadProperty(This
->parentStorage
,
5316 This
->ownerPropertyIndex
,
5319 chainProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
5321 StorageImpl_WriteProperty(This
->parentStorage
,
5322 This
->ownerPropertyIndex
,
5326 * We start freeing the chain at the head block.
5328 extraBlock
= blockIndex
;
5332 /* Get the next block before marking the new end */
5333 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5337 /* Mark the new end of chain */
5338 SmallBlockChainStream_SetNextBlockInChain(
5341 BLOCK_END_OF_CHAIN
);
5345 * Mark the extra blocks as free
5347 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
5349 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
5352 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
5353 extraBlock
= blockIndex
;
5359 /******************************************************************************
5360 * SmallBlockChainStream_Enlarge
5362 * Grows this chain in the small block depot.
5364 BOOL
SmallBlockChainStream_Enlarge(
5365 SmallBlockChainStream
* This
,
5366 ULARGE_INTEGER newSize
)
5368 ULONG blockIndex
, currentBlock
;
5370 ULONG oldNumBlocks
= 0;
5372 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5377 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5380 StgProperty chainProp
;
5382 StorageImpl_ReadProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
5385 chainProp
.startingBlock
= SmallBlockChainStream_GetNextFreeBlock(This
);
5387 StorageImpl_WriteProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
5390 blockIndex
= chainProp
.startingBlock
;
5391 SmallBlockChainStream_SetNextBlockInChain(
5394 BLOCK_END_OF_CHAIN
);
5397 currentBlock
= blockIndex
;
5400 * Figure out how many blocks are needed to contain this stream
5402 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5404 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5408 * Go to the current end of chain
5410 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5413 currentBlock
= blockIndex
;
5414 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
5419 * Add new blocks to the chain
5421 while (oldNumBlocks
< newNumBlocks
)
5423 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
5424 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
5426 SmallBlockChainStream_SetNextBlockInChain(
5429 BLOCK_END_OF_CHAIN
);
5431 currentBlock
= blockIndex
;
5438 /******************************************************************************
5439 * SmallBlockChainStream_GetCount
5441 * Returns the number of blocks that comprises this chain.
5442 * This is not the size of this chain as the last block may not be full!
5444 ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
5449 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5451 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5455 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5462 /******************************************************************************
5463 * SmallBlockChainStream_SetSize
5465 * Sets the size of this stream.
5466 * The file will grow if we grow the chain.
5468 * TODO: Free the actual blocks in the file when we shrink the chain.
5469 * Currently, the blocks are still in the file. So the file size
5470 * doesn't shrink even if we shrink streams.
5472 BOOL
SmallBlockChainStream_SetSize(
5473 SmallBlockChainStream
* This
,
5474 ULARGE_INTEGER newSize
)
5476 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
5478 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
5481 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
5483 SmallBlockChainStream_Shrink(This
, newSize
);
5487 SmallBlockChainStream_Enlarge(This
, newSize
);
5493 /******************************************************************************
5494 * SmallBlockChainStream_GetSize
5496 * Returns the size of this chain.
5498 ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
5500 StgProperty chainProperty
;
5502 StorageImpl_ReadProperty(
5503 This
->parentStorage
,
5504 This
->ownerPropertyIndex
,
5507 return chainProperty
.size
;
5510 /******************************************************************************
5511 * StgCreateDocfile [OLE32.@]
5513 HRESULT WINAPI
StgCreateDocfile(
5517 IStorage
**ppstgOpen
)
5519 StorageImpl
* newStorage
= 0;
5520 HANDLE hFile
= INVALID_HANDLE_VALUE
;
5521 HRESULT hr
= STG_E_INVALIDFLAG
;
5525 DWORD fileAttributes
;
5526 WCHAR tempFileName
[MAX_PATH
];
5528 TRACE("(%s, %lx, %ld, %p)\n",
5529 debugstr_w(pwcsName
), grfMode
,
5530 reserved
, ppstgOpen
);
5533 * Validate the parameters
5536 return STG_E_INVALIDPOINTER
;
5538 return STG_E_INVALIDPARAMETER
;
5541 * Validate the STGM flags
5543 if ( FAILED( validateSTGM(grfMode
) ))
5546 /* StgCreateDocFile always opens for write */
5547 switch(STGM_ACCESS_MODE(grfMode
))
5550 case STGM_READWRITE
:
5556 /* can't share write */
5557 switch(STGM_SHARE_MODE(grfMode
))
5559 case STGM_SHARE_EXCLUSIVE
:
5560 case STGM_SHARE_DENY_WRITE
:
5566 /* shared reading requires transacted mode */
5567 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
5568 !(grfMode
&STGM_TRANSACTED
) )
5572 * Generate a unique name.
5576 WCHAR tempPath
[MAX_PATH
];
5577 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
5579 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
5582 memset(tempPath
, 0, sizeof(tempPath
));
5583 memset(tempFileName
, 0, sizeof(tempFileName
));
5585 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
5588 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
5589 pwcsName
= tempFileName
;
5592 hr
= STG_E_INSUFFICIENTMEMORY
;
5596 creationMode
= TRUNCATE_EXISTING
;
5600 creationMode
= GetCreationModeFromSTGM(grfMode
);
5604 * Interpret the STGM value grfMode
5606 shareMode
= GetShareModeFromSTGM(grfMode
);
5607 accessMode
= GetAccessModeFromSTGM(grfMode
);
5609 if (grfMode
& STGM_DELETEONRELEASE
)
5610 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
5612 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
5614 if (grfMode
& STGM_TRANSACTED
)
5615 FIXME("Transacted mode not implemented.\n");
5618 * Initialize the "out" parameter.
5622 hFile
= CreateFileW(pwcsName
,
5630 if (hFile
== INVALID_HANDLE_VALUE
)
5632 if(GetLastError() == ERROR_FILE_EXISTS
)
5633 hr
= STG_E_FILEALREADYEXISTS
;
5640 * Allocate and initialize the new IStorage32object.
5642 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5644 if (newStorage
== 0)
5646 hr
= STG_E_INSUFFICIENTMEMORY
;
5650 hr
= StorageImpl_Construct(
5661 HeapFree(GetProcessHeap(), 0, newStorage
);
5666 * Get an "out" pointer for the caller.
5668 hr
= StorageBaseImpl_QueryInterface(
5669 (IStorage
*)newStorage
,
5670 (REFIID
)&IID_IStorage
,
5673 TRACE("<-- %p r = %08lx\n", *ppstgOpen
, hr
);
5678 /******************************************************************************
5679 * StgCreateStorageEx [OLE32.@]
5681 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
5683 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
5684 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
5686 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
5688 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5689 return STG_E_INVALIDPARAMETER
;
5692 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
5694 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5695 return STG_E_INVALIDPARAMETER
;
5698 if (stgfmt
== STGFMT_FILE
)
5700 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5701 return STG_E_INVALIDPARAMETER
;
5704 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
5706 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5707 return StgCreateDocfile(pwcsName
, grfMode
, 0, (IStorage
**)ppObjectOpen
);
5710 ERR("Invalid stgfmt argument\n");
5711 return STG_E_INVALIDPARAMETER
;
5714 /******************************************************************************
5715 * StgCreatePropSetStg [OLE32.@]
5717 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
5718 IPropertySetStorage
**ppPropSetStg
)
5722 TRACE("(%p, 0x%lx, %p): stub\n", pstg
, reserved
, ppPropSetStg
);
5724 hr
= STG_E_INVALIDPARAMETER
;
5726 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
5727 (void**)ppPropSetStg
);
5731 /******************************************************************************
5732 * StgOpenStorageEx [OLE32.@]
5734 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
5736 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
5737 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
5739 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
5741 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5742 return STG_E_INVALIDPARAMETER
;
5745 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
5747 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5748 return STG_E_INVALIDPARAMETER
;
5751 if (stgfmt
== STGFMT_FILE
)
5753 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5754 return STG_E_INVALIDPARAMETER
;
5757 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
|| stgfmt
== STGFMT_ANY
)
5759 if (stgfmt
== STGFMT_ANY
)
5760 WARN("STGFMT_ANY assuming storage\n");
5761 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5762 return StgOpenStorage(pwcsName
, NULL
, grfMode
, (SNB
)NULL
, 0, (IStorage
**)ppObjectOpen
);
5765 ERR("Invalid stgfmt argument\n");
5766 return STG_E_INVALIDPARAMETER
;
5770 /******************************************************************************
5771 * StgOpenStorage [OLE32.@]
5773 HRESULT WINAPI
StgOpenStorage(
5774 const OLECHAR
*pwcsName
,
5775 IStorage
*pstgPriority
,
5779 IStorage
**ppstgOpen
)
5781 StorageImpl
* newStorage
= 0;
5786 WCHAR fullname
[MAX_PATH
];
5789 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5790 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
5791 snbExclude
, reserved
, ppstgOpen
);
5794 * Perform sanity checks
5798 hr
= STG_E_INVALIDNAME
;
5804 hr
= STG_E_INVALIDPOINTER
;
5810 hr
= STG_E_INVALIDPARAMETER
;
5815 * Validate the sharing mode
5817 if (!(grfMode
& STGM_TRANSACTED
))
5818 switch(STGM_SHARE_MODE(grfMode
))
5820 case STGM_SHARE_EXCLUSIVE
:
5821 case STGM_SHARE_DENY_WRITE
:
5824 hr
= STG_E_INVALIDFLAG
;
5829 * Validate the STGM flags
5831 if ( FAILED( validateSTGM(grfMode
) ) ||
5832 (grfMode
&STGM_CREATE
))
5834 hr
= STG_E_INVALIDFLAG
;
5838 /* shared reading requires transacted mode */
5839 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
5840 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
5841 !(grfMode
&STGM_TRANSACTED
) )
5843 hr
= STG_E_INVALIDFLAG
;
5848 * Interpret the STGM value grfMode
5850 shareMode
= GetShareModeFromSTGM(grfMode
);
5851 accessMode
= GetAccessModeFromSTGM(grfMode
);
5854 * Initialize the "out" parameter.
5858 hFile
= CreateFileW( pwcsName
,
5863 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
5866 if (hFile
==INVALID_HANDLE_VALUE
)
5868 DWORD last_error
= GetLastError();
5874 case ERROR_FILE_NOT_FOUND
:
5875 hr
= STG_E_FILENOTFOUND
;
5878 case ERROR_PATH_NOT_FOUND
:
5879 hr
= STG_E_PATHNOTFOUND
;
5882 case ERROR_ACCESS_DENIED
:
5883 case ERROR_WRITE_PROTECT
:
5884 hr
= STG_E_ACCESSDENIED
;
5887 case ERROR_SHARING_VIOLATION
:
5888 hr
= STG_E_SHAREVIOLATION
;
5899 * Refuse to open the file if it's too small to be a structured storage file
5900 * FIXME: verify the file when reading instead of here
5902 length
= GetFileSize(hFile
, NULL
);
5906 hr
= STG_E_FILEALREADYEXISTS
;
5911 * Allocate and initialize the new IStorage32object.
5913 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5915 if (newStorage
== 0)
5917 hr
= STG_E_INSUFFICIENTMEMORY
;
5921 /* if the file's length was zero, initialize the storage */
5922 hr
= StorageImpl_Construct(
5933 HeapFree(GetProcessHeap(), 0, newStorage
);
5935 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5937 if(hr
== STG_E_INVALIDHEADER
)
5938 hr
= STG_E_FILEALREADYEXISTS
;
5942 /* prepare the file name string given in lieu of the root property name */
5943 GetFullPathNameW(pwcsName
, MAX_PATH
, fullname
, NULL
);
5944 memcpy(newStorage
->filename
, fullname
, PROPERTY_NAME_BUFFER_LEN
);
5945 newStorage
->filename
[PROPERTY_NAME_BUFFER_LEN
-1] = '\0';
5948 * Get an "out" pointer for the caller.
5950 hr
= StorageBaseImpl_QueryInterface(
5951 (IStorage
*)newStorage
,
5952 (REFIID
)&IID_IStorage
,
5956 TRACE("<-- %08lx, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
5960 /******************************************************************************
5961 * StgCreateDocfileOnILockBytes [OLE32.@]
5963 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
5967 IStorage
** ppstgOpen
)
5969 StorageImpl
* newStorage
= 0;
5973 * Validate the parameters
5975 if ((ppstgOpen
== 0) || (plkbyt
== 0))
5976 return STG_E_INVALIDPOINTER
;
5979 * Allocate and initialize the new IStorage object.
5981 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5983 if (newStorage
== 0)
5984 return STG_E_INSUFFICIENTMEMORY
;
5986 hr
= StorageImpl_Construct(
5997 HeapFree(GetProcessHeap(), 0, newStorage
);
6002 * Get an "out" pointer for the caller.
6004 hr
= StorageBaseImpl_QueryInterface(
6005 (IStorage
*)newStorage
,
6006 (REFIID
)&IID_IStorage
,
6012 /******************************************************************************
6013 * StgOpenStorageOnILockBytes [OLE32.@]
6015 HRESULT WINAPI
StgOpenStorageOnILockBytes(
6017 IStorage
*pstgPriority
,
6021 IStorage
**ppstgOpen
)
6023 StorageImpl
* newStorage
= 0;
6027 * Perform a sanity check
6029 if ((plkbyt
== 0) || (ppstgOpen
== 0))
6030 return STG_E_INVALIDPOINTER
;
6033 * Validate the STGM flags
6035 if ( FAILED( validateSTGM(grfMode
) ))
6036 return STG_E_INVALIDFLAG
;
6039 * Initialize the "out" parameter.
6044 * Allocate and initialize the new IStorage object.
6046 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
6048 if (newStorage
== 0)
6049 return STG_E_INSUFFICIENTMEMORY
;
6051 hr
= StorageImpl_Construct(
6062 HeapFree(GetProcessHeap(), 0, newStorage
);
6067 * Get an "out" pointer for the caller.
6069 hr
= StorageBaseImpl_QueryInterface(
6070 (IStorage
*)newStorage
,
6071 (REFIID
)&IID_IStorage
,
6077 /******************************************************************************
6078 * StgSetTimes [ole32.@]
6079 * StgSetTimes [OLE32.@]
6083 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
6084 FILETIME
const *patime
, FILETIME
const *pmtime
)
6086 IStorage
*stg
= NULL
;
6089 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
6091 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
6095 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
6096 IStorage_Release(stg
);
6102 /******************************************************************************
6103 * StgIsStorageILockBytes [OLE32.@]
6105 * Determines if the ILockBytes contains a storage object.
6107 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
6110 ULARGE_INTEGER offset
;
6112 offset
.u
.HighPart
= 0;
6113 offset
.u
.LowPart
= 0;
6115 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
6117 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
6123 /******************************************************************************
6124 * WriteClassStg [OLE32.@]
6126 * This method will store the specified CLSID in the specified storage object
6128 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
6133 return E_INVALIDARG
;
6135 hRes
= IStorage_SetClass(pStg
, rclsid
);
6140 /***********************************************************************
6141 * ReadClassStg (OLE32.@)
6143 * This method reads the CLSID previously written to a storage object with
6144 * the WriteClassStg.
6147 * pstg [I] IStorage pointer
6148 * pclsid [O] Pointer to where the CLSID is written
6152 * Failure: HRESULT code.
6154 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
6159 TRACE("(%p, %p)\n", pstg
, pclsid
);
6161 if(!pstg
|| !pclsid
)
6162 return E_INVALIDARG
;
6165 * read a STATSTG structure (contains the clsid) from the storage
6167 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_DEFAULT
);
6170 *pclsid
=pstatstg
.clsid
;
6175 /***********************************************************************
6176 * OleLoadFromStream (OLE32.@)
6178 * This function loads an object from stream
6180 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
6184 LPPERSISTSTREAM xstm
;
6186 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
6188 res
=ReadClassStm(pStm
,&clsid
);
6189 if (!SUCCEEDED(res
))
6191 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
6192 if (!SUCCEEDED(res
))
6194 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
6195 if (!SUCCEEDED(res
)) {
6196 IUnknown_Release((IUnknown
*)*ppvObj
);
6199 res
=IPersistStream_Load(xstm
,pStm
);
6200 IPersistStream_Release(xstm
);
6201 /* FIXME: all refcounts ok at this point? I think they should be:
6204 * xstm : 0 (released)
6209 /***********************************************************************
6210 * OleSaveToStream (OLE32.@)
6212 * This function saves an object with the IPersistStream interface on it
6213 * to the specified stream.
6215 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
6221 TRACE("(%p,%p)\n",pPStm
,pStm
);
6223 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
6225 if (SUCCEEDED(res
)){
6227 res
=WriteClassStm(pStm
,&clsid
);
6231 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
6234 TRACE("Finished Save\n");
6238 /****************************************************************************
6239 * This method validate a STGM parameter that can contain the values below
6241 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6242 * The stgm values contained in 0xffff0000 are bitmasks.
6244 * STGM_DIRECT 0x00000000
6245 * STGM_TRANSACTED 0x00010000
6246 * STGM_SIMPLE 0x08000000
6248 * STGM_READ 0x00000000
6249 * STGM_WRITE 0x00000001
6250 * STGM_READWRITE 0x00000002
6252 * STGM_SHARE_DENY_NONE 0x00000040
6253 * STGM_SHARE_DENY_READ 0x00000030
6254 * STGM_SHARE_DENY_WRITE 0x00000020
6255 * STGM_SHARE_EXCLUSIVE 0x00000010
6257 * STGM_PRIORITY 0x00040000
6258 * STGM_DELETEONRELEASE 0x04000000
6260 * STGM_CREATE 0x00001000
6261 * STGM_CONVERT 0x00020000
6262 * STGM_FAILIFTHERE 0x00000000
6264 * STGM_NOSCRATCH 0x00100000
6265 * STGM_NOSNAPSHOT 0x00200000
6267 static HRESULT
validateSTGM(DWORD stgm
)
6269 DWORD access
= STGM_ACCESS_MODE(stgm
);
6270 DWORD share
= STGM_SHARE_MODE(stgm
);
6271 DWORD create
= STGM_CREATE_MODE(stgm
);
6273 if (stgm
&~STGM_KNOWN_FLAGS
)
6275 ERR("unknown flags %08lx\n", stgm
);
6283 case STGM_READWRITE
:
6291 case STGM_SHARE_DENY_NONE
:
6292 case STGM_SHARE_DENY_READ
:
6293 case STGM_SHARE_DENY_WRITE
:
6294 case STGM_SHARE_EXCLUSIVE
:
6303 case STGM_FAILIFTHERE
:
6310 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6312 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
6316 * STGM_CREATE | STGM_CONVERT
6317 * if both are false, STGM_FAILIFTHERE is set to TRUE
6319 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
6323 * STGM_NOSCRATCH requires STGM_TRANSACTED
6325 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
6329 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6330 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6332 if ( (stgm
& STGM_NOSNAPSHOT
) &&
6333 (!(stgm
& STGM_TRANSACTED
) ||
6334 share
== STGM_SHARE_EXCLUSIVE
||
6335 share
== STGM_SHARE_DENY_WRITE
) )
6341 /****************************************************************************
6342 * GetShareModeFromSTGM
6344 * This method will return a share mode flag from a STGM value.
6345 * The STGM value is assumed valid.
6347 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
6349 switch (STGM_SHARE_MODE(stgm
))
6351 case STGM_SHARE_DENY_NONE
:
6352 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6353 case STGM_SHARE_DENY_READ
:
6354 return FILE_SHARE_WRITE
;
6355 case STGM_SHARE_DENY_WRITE
:
6356 return FILE_SHARE_READ
;
6357 case STGM_SHARE_EXCLUSIVE
:
6360 ERR("Invalid share mode!\n");
6365 /****************************************************************************
6366 * GetAccessModeFromSTGM
6368 * This method will return an access mode flag from a STGM value.
6369 * The STGM value is assumed valid.
6371 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
6373 switch (STGM_ACCESS_MODE(stgm
))
6376 return GENERIC_READ
;
6378 case STGM_READWRITE
:
6379 return GENERIC_READ
| GENERIC_WRITE
;
6381 ERR("Invalid access mode!\n");
6386 /****************************************************************************
6387 * GetCreationModeFromSTGM
6389 * This method will return a creation mode flag from a STGM value.
6390 * The STGM value is assumed valid.
6392 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
6394 switch(STGM_CREATE_MODE(stgm
))
6397 return CREATE_ALWAYS
;
6399 FIXME("STGM_CONVERT not implemented!\n");
6401 case STGM_FAILIFTHERE
:
6404 ERR("Invalid create mode!\n");
6410 /*************************************************************************
6411 * OLECONVERT_LoadOLE10 [Internal]
6413 * Loads the OLE10 STREAM to memory
6416 * pOleStream [I] The OLESTREAM
6417 * pData [I] Data Structure for the OLESTREAM Data
6421 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6422 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6425 * This function is used by OleConvertOLESTREAMToIStorage only.
6427 * Memory allocated for pData must be freed by the caller
6429 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
6432 HRESULT hRes
= S_OK
;
6436 pData
->pData
= NULL
;
6437 pData
->pstrOleObjFileName
= (CHAR
*) NULL
;
6439 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
6442 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6443 if(dwSize
!= sizeof(pData
->dwOleID
))
6445 hRes
= CONVERT10_E_OLESTREAM_GET
;
6447 else if(pData
->dwOleID
!= OLESTREAM_ID
)
6449 hRes
= CONVERT10_E_OLESTREAM_FMT
;
6460 /* Get the TypeID...more info needed for this field */
6461 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6462 if(dwSize
!= sizeof(pData
->dwTypeID
))
6464 hRes
= CONVERT10_E_OLESTREAM_GET
;
6469 if(pData
->dwTypeID
!= 0)
6471 /* Get the length of the OleTypeName */
6472 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6473 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6475 hRes
= CONVERT10_E_OLESTREAM_GET
;
6480 if(pData
->dwOleTypeNameLength
> 0)
6482 /* Get the OleTypeName */
6483 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6484 if(dwSize
!= pData
->dwOleTypeNameLength
)
6486 hRes
= CONVERT10_E_OLESTREAM_GET
;
6492 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
6493 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
6495 hRes
= CONVERT10_E_OLESTREAM_GET
;
6499 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
6500 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
6501 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
6502 if(pData
->pstrOleObjFileName
)
6504 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)(pData
->pstrOleObjFileName
),pData
->dwOleObjFileNameLength
);
6505 if(dwSize
!= pData
->dwOleObjFileNameLength
)
6507 hRes
= CONVERT10_E_OLESTREAM_GET
;
6511 hRes
= CONVERT10_E_OLESTREAM_GET
;
6516 /* Get the Width of the Metafile */
6517 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6518 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6520 hRes
= CONVERT10_E_OLESTREAM_GET
;
6524 /* Get the Height of the Metafile */
6525 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6526 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6528 hRes
= CONVERT10_E_OLESTREAM_GET
;
6534 /* Get the Length of the Data */
6535 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6536 if(dwSize
!= sizeof(pData
->dwDataLength
))
6538 hRes
= CONVERT10_E_OLESTREAM_GET
;
6542 if(hRes
== S_OK
) /* I don't know what is this 8 byts information is we have to figure out */
6544 if(!bStrem1
) /* if it is a second OLE stream data */
6546 pData
->dwDataLength
-= 8;
6547 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)(pData
->strUnknown
), sizeof(pData
->strUnknown
));
6548 if(dwSize
!= sizeof(pData
->strUnknown
))
6550 hRes
= CONVERT10_E_OLESTREAM_GET
;
6556 if(pData
->dwDataLength
> 0)
6558 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
6560 /* Get Data (ex. IStorage, Metafile, or BMP) */
6563 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
6564 if(dwSize
!= pData
->dwDataLength
)
6566 hRes
= CONVERT10_E_OLESTREAM_GET
;
6571 hRes
= CONVERT10_E_OLESTREAM_GET
;
6580 /*************************************************************************
6581 * OLECONVERT_SaveOLE10 [Internal]
6583 * Saves the OLE10 STREAM From memory
6586 * pData [I] Data Structure for the OLESTREAM Data
6587 * pOleStream [I] The OLESTREAM to save
6591 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6594 * This function is used by OleConvertIStorageToOLESTREAM only.
6597 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
6600 HRESULT hRes
= S_OK
;
6604 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6605 if(dwSize
!= sizeof(pData
->dwOleID
))
6607 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6612 /* Set the TypeID */
6613 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6614 if(dwSize
!= sizeof(pData
->dwTypeID
))
6616 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6620 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
6622 /* Set the Length of the OleTypeName */
6623 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6624 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6626 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6631 if(pData
->dwOleTypeNameLength
> 0)
6633 /* Set the OleTypeName */
6634 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6635 if(dwSize
!= pData
->dwOleTypeNameLength
)
6637 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6644 /* Set the width of the Metafile */
6645 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6646 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6648 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6654 /* Set the height of the Metafile */
6655 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6656 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6658 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6664 /* Set the length of the Data */
6665 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6666 if(dwSize
!= sizeof(pData
->dwDataLength
))
6668 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6674 if(pData
->dwDataLength
> 0)
6676 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6677 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
6678 if(dwSize
!= pData
->dwDataLength
)
6680 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6688 /*************************************************************************
6689 * OLECONVERT_GetOLE20FromOLE10[Internal]
6691 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6692 * opens it, and copies the content to the dest IStorage for
6693 * OleConvertOLESTREAMToIStorage
6697 * pDestStorage [I] The IStorage to copy the data to
6698 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6699 * nBufferLength [I] The size of the buffer
6708 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, BYTE
*pBuffer
, DWORD nBufferLength
)
6712 IStorage
*pTempStorage
;
6713 DWORD dwNumOfBytesWritten
;
6714 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6715 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6717 /* Create a temp File */
6718 GetTempPathW(MAX_PATH
, wstrTempDir
);
6719 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6720 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
6722 if(hFile
!= INVALID_HANDLE_VALUE
)
6724 /* Write IStorage Data to File */
6725 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
6728 /* Open and copy temp storage to the Dest Storage */
6729 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
6732 hRes
= StorageImpl_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
6733 StorageBaseImpl_Release(pTempStorage
);
6735 DeleteFileW(wstrTempFile
);
6740 /*************************************************************************
6741 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6743 * Saves the OLE10 STREAM From memory
6746 * pStorage [I] The Src IStorage to copy
6747 * pData [I] The Dest Memory to write to.
6750 * The size in bytes allocated for pData
6753 * Memory allocated for pData must be freed by the caller
6755 * Used by OleConvertIStorageToOLESTREAM only.
6758 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
6762 DWORD nDataLength
= 0;
6763 IStorage
*pTempStorage
;
6764 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6765 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6769 /* Create temp Storage */
6770 GetTempPathW(MAX_PATH
, wstrTempDir
);
6771 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6772 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
6776 /* Copy Src Storage to the Temp Storage */
6777 StorageImpl_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
6778 StorageBaseImpl_Release(pTempStorage
);
6780 /* Open Temp Storage as a file and copy to memory */
6781 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
6782 if(hFile
!= INVALID_HANDLE_VALUE
)
6784 nDataLength
= GetFileSize(hFile
, NULL
);
6785 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
6786 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
6789 DeleteFileW(wstrTempFile
);
6794 /*************************************************************************
6795 * OLECONVERT_CreateOleStream [Internal]
6797 * Creates the "\001OLE" stream in the IStorage if necessary.
6800 * pStorage [I] Dest storage to create the stream in
6806 * This function is used by OleConvertOLESTREAMToIStorage only.
6808 * This stream is still unknown, MS Word seems to have extra data
6809 * but since the data is stored in the OLESTREAM there should be
6810 * no need to recreate the stream. If the stream is manually
6811 * deleted it will create it with this default data.
6814 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
6818 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
6819 BYTE pOleStreamHeader
[] =
6821 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6822 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6823 0x00, 0x00, 0x00, 0x00
6826 /* Create stream if not present */
6827 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
6828 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
6832 /* Write default Data */
6833 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
6834 IStream_Release(pStream
);
6838 /* write a string to a stream, preceded by its length */
6839 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
6846 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
6847 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
6852 str
= CoTaskMemAlloc( len
);
6853 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
6854 r
= IStream_Write( stm
, str
, len
, NULL
);
6855 CoTaskMemFree( str
);
6859 /* read a string preceded by its length from a stream */
6860 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
6863 DWORD len
, count
= 0;
6867 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
6870 if( count
!= sizeof(len
) )
6871 return E_OUTOFMEMORY
;
6873 TRACE("%ld bytes\n",len
);
6875 str
= CoTaskMemAlloc( len
);
6877 return E_OUTOFMEMORY
;
6879 r
= IStream_Read( stm
, str
, len
, &count
);
6884 CoTaskMemFree( str
);
6885 return E_OUTOFMEMORY
;
6888 TRACE("Read string %s\n",debugstr_an(str
,len
));
6890 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
6891 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
6893 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
6894 CoTaskMemFree( str
);
6902 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
6903 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
6907 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6909 static const BYTE unknown1
[12] =
6910 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6911 0xFF, 0xFF, 0xFF, 0xFF};
6912 static const BYTE unknown2
[16] =
6913 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6914 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6916 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
6917 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
6918 debugstr_w(szProgIDName
));
6920 /* Create a CompObj stream if it doesn't exist */
6921 r
= IStorage_CreateStream(pstg
, szwStreamName
,
6922 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
6926 /* Write CompObj Structure to stream */
6927 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
6929 if( SUCCEEDED( r
) )
6930 r
= WriteClassStm( pstm
, clsid
);
6932 if( SUCCEEDED( r
) )
6933 r
= STREAM_WriteString( pstm
, lpszUserType
);
6934 if( SUCCEEDED( r
) )
6935 r
= STREAM_WriteString( pstm
, szClipName
);
6936 if( SUCCEEDED( r
) )
6937 r
= STREAM_WriteString( pstm
, szProgIDName
);
6938 if( SUCCEEDED( r
) )
6939 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
6941 IStream_Release( pstm
);
6946 /***********************************************************************
6947 * WriteFmtUserTypeStg (OLE32.@)
6949 HRESULT WINAPI
WriteFmtUserTypeStg(
6950 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
6953 WCHAR szwClipName
[0x40];
6954 CLSID clsid
= CLSID_NULL
;
6955 LPWSTR wstrProgID
= NULL
;
6958 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
6960 /* get the clipboard format name */
6961 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
) );
6964 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
6966 /* FIXME: There's room to save a CLSID and its ProgID, but
6967 the CLSID is not looked up in the registry and in all the
6968 tests I wrote it was CLSID_NULL. Where does it come from?
6971 /* get the real program ID. This may fail, but that's fine */
6972 ProgIDFromCLSID(&clsid
, &wstrProgID
);
6974 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
6976 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
6977 lpszUserType
, szwClipName
, wstrProgID
);
6979 CoTaskMemFree(wstrProgID
);
6985 /******************************************************************************
6986 * ReadFmtUserTypeStg [OLE32.@]
6988 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
6992 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
6993 unsigned char unknown1
[12];
6994 unsigned char unknown2
[16];
6996 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
6999 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
7001 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
7002 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
7005 WARN("Failed to open stream r = %08lx\n", r
);
7009 /* read the various parts of the structure */
7010 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
7011 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
7013 r
= ReadClassStm( stm
, &clsid
);
7017 r
= STREAM_ReadString( stm
, &szCLSIDName
);
7021 r
= STREAM_ReadString( stm
, &szOleTypeName
);
7025 r
= STREAM_ReadString( stm
, &szProgIDName
);
7029 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
7030 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
7033 /* ok, success... now we just need to store what we found */
7035 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
7036 CoTaskMemFree( szOleTypeName
);
7038 if( lplpszUserType
)
7039 *lplpszUserType
= szCLSIDName
;
7040 CoTaskMemFree( szProgIDName
);
7043 IStream_Release( stm
);
7049 /*************************************************************************
7050 * OLECONVERT_CreateCompObjStream [Internal]
7052 * Creates a "\001CompObj" is the destination IStorage if necessary.
7055 * pStorage [I] The dest IStorage to create the CompObj Stream
7057 * strOleTypeName [I] The ProgID
7061 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7064 * This function is used by OleConvertOLESTREAMToIStorage only.
7066 * The stream data is stored in the OLESTREAM and there should be
7067 * no need to recreate the stream. If the stream is manually
7068 * deleted it will attempt to create it by querying the registry.
7072 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
7075 HRESULT hStorageRes
, hRes
= S_OK
;
7076 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
7077 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7078 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
7080 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7081 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
7083 /* Initialize the CompObj structure */
7084 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
7085 memcpy(&(IStorageCompObj
.byUnknown1
), pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
7086 memcpy(&(IStorageCompObj
.byUnknown2
), pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
7089 /* Create a CompObj stream if it doesn't exist */
7090 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7091 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7092 if(hStorageRes
== S_OK
)
7094 /* copy the OleTypeName to the compobj struct */
7095 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
7096 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
7098 /* copy the OleTypeName to the compobj struct */
7099 /* Note: in the test made, these were Identical */
7100 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
7101 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
7104 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
7105 bufferW
, OLESTREAM_MAX_STR_LEN
);
7106 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
7112 /* Get the CLSID Default Name from the Registry */
7113 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
7114 if(hErr
== ERROR_SUCCESS
)
7116 char strTemp
[OLESTREAM_MAX_STR_LEN
];
7117 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
7118 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
7119 if(hErr
== ERROR_SUCCESS
)
7121 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
7127 /* Write CompObj Structure to stream */
7128 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
7130 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
7132 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
7133 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
7135 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
7137 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
7138 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
7140 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
7142 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
7143 if(IStorageCompObj
.dwProgIDNameLength
> 0)
7145 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
7147 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
7148 IStream_Release(pStream
);
7154 /*************************************************************************
7155 * OLECONVERT_CreateOlePresStream[Internal]
7157 * Creates the "\002OlePres000" Stream with the Metafile data
7160 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7161 * dwExtentX [I] Width of the Metafile
7162 * dwExtentY [I] Height of the Metafile
7163 * pData [I] Metafile data
7164 * dwDataLength [I] Size of the Metafile data
7168 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7171 * This function is used by OleConvertOLESTREAMToIStorage only.
7174 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
7178 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7179 BYTE pOlePresStreamHeader
[] =
7181 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7182 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7183 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7184 0x00, 0x00, 0x00, 0x00
7187 BYTE pOlePresStreamHeaderEmpty
[] =
7189 0x00, 0x00, 0x00, 0x00,
7190 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7191 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7192 0x00, 0x00, 0x00, 0x00
7195 /* Create the OlePres000 Stream */
7196 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7197 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7202 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
7204 memset(&OlePres
, 0, sizeof(OlePres
));
7205 /* Do we have any metafile data to save */
7206 if(dwDataLength
> 0)
7208 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
7209 nHeaderSize
= sizeof(pOlePresStreamHeader
);
7213 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
7214 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
7216 /* Set width and height of the metafile */
7217 OlePres
.dwExtentX
= dwExtentX
;
7218 OlePres
.dwExtentY
= -dwExtentY
;
7220 /* Set Data and Length */
7221 if(dwDataLength
> sizeof(METAFILEPICT16
))
7223 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
7224 OlePres
.pData
= &(pData
[8]);
7226 /* Save OlePres000 Data to Stream */
7227 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
7228 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
7229 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
7230 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
7231 if(OlePres
.dwSize
> 0)
7233 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
7235 IStream_Release(pStream
);
7239 /*************************************************************************
7240 * OLECONVERT_CreateOle10NativeStream [Internal]
7242 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7245 * pStorage [I] Dest storage to create the stream in
7246 * pData [I] Ole10 Native Data (ex. bmp)
7247 * dwDataLength [I] Size of the Ole10 Native Data
7253 * This function is used by OleConvertOLESTREAMToIStorage only.
7255 * Might need to verify the data and return appropriate error message
7258 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, BYTE
*pData
, DWORD dwDataLength
)
7262 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7264 /* Create the Ole10Native Stream */
7265 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7266 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7270 /* Write info to stream */
7271 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
7272 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
7273 IStream_Release(pStream
);
7278 /*************************************************************************
7279 * OLECONVERT_GetOLE10ProgID [Internal]
7281 * Finds the ProgID (or OleTypeID) from the IStorage
7284 * pStorage [I] The Src IStorage to get the ProgID
7285 * strProgID [I] the ProgID string to get
7286 * dwSize [I] the size of the string
7290 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7293 * This function is used by OleConvertIStorageToOLESTREAM only.
7297 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
7301 LARGE_INTEGER iSeekPos
;
7302 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
7303 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7305 /* Open the CompObj Stream */
7306 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7307 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7311 /*Get the OleType from the CompObj Stream */
7312 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
7313 iSeekPos
.u
.HighPart
= 0;
7315 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7316 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
7317 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
7318 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7319 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
7320 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
7321 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7323 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
7326 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
7328 IStream_Release(pStream
);
7333 LPOLESTR wstrProgID
;
7335 /* Get the OleType from the registry */
7336 REFCLSID clsid
= &(stat
.clsid
);
7337 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
7338 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
7341 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
7348 /*************************************************************************
7349 * OLECONVERT_GetOle10PresData [Internal]
7351 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7354 * pStorage [I] Src IStroage
7355 * pOleStream [I] Dest OleStream Mem Struct
7361 * This function is used by OleConvertIStorageToOLESTREAM only.
7363 * Memory allocated for pData must be freed by the caller
7367 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7372 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7374 /* Initialize Default data for OLESTREAM */
7375 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7376 pOleStreamData
[0].dwTypeID
= 2;
7377 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7378 pOleStreamData
[1].dwTypeID
= 0;
7379 pOleStreamData
[0].dwMetaFileWidth
= 0;
7380 pOleStreamData
[0].dwMetaFileHeight
= 0;
7381 pOleStreamData
[0].pData
= NULL
;
7382 pOleStreamData
[1].pData
= NULL
;
7384 /* Open Ole10Native Stream */
7385 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7386 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7390 /* Read Size and Data */
7391 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
7392 if(pOleStreamData
->dwDataLength
> 0)
7394 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
7395 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
7397 IStream_Release(pStream
);
7403 /*************************************************************************
7404 * OLECONVERT_GetOle20PresData[Internal]
7406 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7409 * pStorage [I] Src IStroage
7410 * pOleStreamData [I] Dest OleStream Mem Struct
7416 * This function is used by OleConvertIStorageToOLESTREAM only.
7418 * Memory allocated for pData must be freed by the caller
7420 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7424 OLECONVERT_ISTORAGE_OLEPRES olePress
;
7425 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7427 /* Initialize Default data for OLESTREAM */
7428 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7429 pOleStreamData
[0].dwTypeID
= 2;
7430 pOleStreamData
[0].dwMetaFileWidth
= 0;
7431 pOleStreamData
[0].dwMetaFileHeight
= 0;
7432 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
7433 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7434 pOleStreamData
[1].dwTypeID
= 0;
7435 pOleStreamData
[1].dwOleTypeNameLength
= 0;
7436 pOleStreamData
[1].strOleTypeName
[0] = 0;
7437 pOleStreamData
[1].dwMetaFileWidth
= 0;
7438 pOleStreamData
[1].dwMetaFileHeight
= 0;
7439 pOleStreamData
[1].pData
= NULL
;
7440 pOleStreamData
[1].dwDataLength
= 0;
7443 /* Open OlePress000 stream */
7444 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7445 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7448 LARGE_INTEGER iSeekPos
;
7449 METAFILEPICT16 MetaFilePict
;
7450 static const char strMetafilePictName
[] = "METAFILEPICT";
7452 /* Set the TypeID for a Metafile */
7453 pOleStreamData
[1].dwTypeID
= 5;
7455 /* Set the OleTypeName to Metafile */
7456 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
7457 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
7459 iSeekPos
.u
.HighPart
= 0;
7460 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
7462 /* Get Presentation Data */
7463 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7464 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
7465 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
7466 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
7468 /*Set width and Height */
7469 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
7470 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
7471 if(olePress
.dwSize
> 0)
7474 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
7476 /* Set MetaFilePict struct */
7477 MetaFilePict
.mm
= 8;
7478 MetaFilePict
.xExt
= olePress
.dwExtentX
;
7479 MetaFilePict
.yExt
= olePress
.dwExtentY
;
7480 MetaFilePict
.hMF
= 0;
7482 /* Get Metafile Data */
7483 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
7484 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
7485 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
7487 IStream_Release(pStream
);
7491 /*************************************************************************
7492 * OleConvertOLESTREAMToIStorage [OLE32.@]
7497 * DVTARGETDEVICE paramenter is not handled
7498 * Still unsure of some mem fields for OLE 10 Stream
7499 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7500 * and "\001OLE" streams
7503 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
7504 LPOLESTREAM pOleStream
,
7506 const DVTARGETDEVICE
* ptd
)
7510 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7512 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7516 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7519 if(pstg
== NULL
|| pOleStream
== NULL
)
7521 hRes
= E_INVALIDARG
;
7526 /* Load the OLESTREAM to Memory */
7527 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
7532 /* Load the OLESTREAM to Memory (part 2)*/
7533 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
7539 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
7541 /* Do we have the IStorage Data in the OLESTREAM */
7542 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
7544 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7545 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
7549 /* It must be an original OLE 1.0 source */
7550 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7555 /* It must be an original OLE 1.0 source */
7556 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7559 /* Create CompObj Stream if necessary */
7560 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
7563 /*Create the Ole Stream if necessary */
7564 OLECONVERT_CreateOleStream(pstg
);
7569 /* Free allocated memory */
7570 for(i
=0; i
< 2; i
++)
7572 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7573 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
7574 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
7579 /*************************************************************************
7580 * OleConvertIStorageToOLESTREAM [OLE32.@]
7587 * Still unsure of some mem fields for OLE 10 Stream
7588 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7589 * and "\001OLE" streams.
7592 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
7594 LPOLESTREAM pOleStream
)
7597 HRESULT hRes
= S_OK
;
7599 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7600 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7603 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7605 if(pstg
== NULL
|| pOleStream
== NULL
)
7607 hRes
= E_INVALIDARG
;
7611 /* Get the ProgID */
7612 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
7613 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
7617 /* Was it originally Ole10 */
7618 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7621 IStream_Release(pStream
);
7622 /* Get Presentation Data for Ole10Native */
7623 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
7627 /* Get Presentation Data (OLE20) */
7628 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
7631 /* Save OLESTREAM */
7632 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
7635 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
7640 /* Free allocated memory */
7641 for(i
=0; i
< 2; i
++)
7643 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7649 /***********************************************************************
7650 * GetConvertStg (OLE32.@)
7652 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
7653 FIXME("unimplemented stub!\n");
7657 /******************************************************************************
7658 * StgIsStorageFile [OLE32.@]
7661 StgIsStorageFile(LPCOLESTR fn
)
7667 TRACE("(\'%s\')\n", debugstr_w(fn
));
7668 hf
= CreateFileW(fn
, GENERIC_READ
,
7669 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
7670 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
7672 if (hf
== INVALID_HANDLE_VALUE
)
7673 return STG_E_FILENOTFOUND
;
7675 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
7677 WARN(" unable to read file\n");
7684 if (bytes_read
!= 8) {
7685 WARN(" too short\n");
7689 if (!memcmp(magic
,STORAGE_magic
,8)) {
7694 WARN(" -> Invalid header.\n");