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 the WriteClassStg.
6145 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
6150 TRACE("(%p, %p)\n", pstg
, pclsid
);
6152 if(!pstg
|| !pclsid
)
6153 return E_INVALIDARG
;
6156 * read a STATSTG structure (contains the clsid) from the storage
6158 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_DEFAULT
);
6161 *pclsid
=pstatstg
.clsid
;
6166 /***********************************************************************
6167 * OleLoadFromStream (OLE32.@)
6169 * This function loads an object from stream
6171 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
6175 LPPERSISTSTREAM xstm
;
6177 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
6179 res
=ReadClassStm(pStm
,&clsid
);
6180 if (!SUCCEEDED(res
))
6182 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
6183 if (!SUCCEEDED(res
))
6185 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
6186 if (!SUCCEEDED(res
)) {
6187 IUnknown_Release((IUnknown
*)*ppvObj
);
6190 res
=IPersistStream_Load(xstm
,pStm
);
6191 IPersistStream_Release(xstm
);
6192 /* FIXME: all refcounts ok at this point? I think they should be:
6195 * xstm : 0 (released)
6200 /***********************************************************************
6201 * OleSaveToStream (OLE32.@)
6203 * This function saves an object with the IPersistStream interface on it
6204 * to the specified stream.
6206 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
6212 TRACE("(%p,%p)\n",pPStm
,pStm
);
6214 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
6216 if (SUCCEEDED(res
)){
6218 res
=WriteClassStm(pStm
,&clsid
);
6222 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
6225 TRACE("Finished Save\n");
6229 /****************************************************************************
6230 * This method validate a STGM parameter that can contain the values below
6232 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6233 * The stgm values contained in 0xffff0000 are bitmasks.
6235 * STGM_DIRECT 0x00000000
6236 * STGM_TRANSACTED 0x00010000
6237 * STGM_SIMPLE 0x08000000
6239 * STGM_READ 0x00000000
6240 * STGM_WRITE 0x00000001
6241 * STGM_READWRITE 0x00000002
6243 * STGM_SHARE_DENY_NONE 0x00000040
6244 * STGM_SHARE_DENY_READ 0x00000030
6245 * STGM_SHARE_DENY_WRITE 0x00000020
6246 * STGM_SHARE_EXCLUSIVE 0x00000010
6248 * STGM_PRIORITY 0x00040000
6249 * STGM_DELETEONRELEASE 0x04000000
6251 * STGM_CREATE 0x00001000
6252 * STGM_CONVERT 0x00020000
6253 * STGM_FAILIFTHERE 0x00000000
6255 * STGM_NOSCRATCH 0x00100000
6256 * STGM_NOSNAPSHOT 0x00200000
6258 static HRESULT
validateSTGM(DWORD stgm
)
6260 DWORD access
= STGM_ACCESS_MODE(stgm
);
6261 DWORD share
= STGM_SHARE_MODE(stgm
);
6262 DWORD create
= STGM_CREATE_MODE(stgm
);
6264 if (stgm
&~STGM_KNOWN_FLAGS
)
6266 ERR("unknown flags %08lx\n", stgm
);
6274 case STGM_READWRITE
:
6282 case STGM_SHARE_DENY_NONE
:
6283 case STGM_SHARE_DENY_READ
:
6284 case STGM_SHARE_DENY_WRITE
:
6285 case STGM_SHARE_EXCLUSIVE
:
6294 case STGM_FAILIFTHERE
:
6301 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6303 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
6307 * STGM_CREATE | STGM_CONVERT
6308 * if both are false, STGM_FAILIFTHERE is set to TRUE
6310 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
6314 * STGM_NOSCRATCH requires STGM_TRANSACTED
6316 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
6320 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6321 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6323 if ( (stgm
& STGM_NOSNAPSHOT
) &&
6324 (!(stgm
& STGM_TRANSACTED
) ||
6325 share
== STGM_SHARE_EXCLUSIVE
||
6326 share
== STGM_SHARE_DENY_WRITE
) )
6332 /****************************************************************************
6333 * GetShareModeFromSTGM
6335 * This method will return a share mode flag from a STGM value.
6336 * The STGM value is assumed valid.
6338 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
6340 switch (STGM_SHARE_MODE(stgm
))
6342 case STGM_SHARE_DENY_NONE
:
6343 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6344 case STGM_SHARE_DENY_READ
:
6345 return FILE_SHARE_WRITE
;
6346 case STGM_SHARE_DENY_WRITE
:
6347 return FILE_SHARE_READ
;
6348 case STGM_SHARE_EXCLUSIVE
:
6351 ERR("Invalid share mode!\n");
6356 /****************************************************************************
6357 * GetAccessModeFromSTGM
6359 * This method will return an access mode flag from a STGM value.
6360 * The STGM value is assumed valid.
6362 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
6364 switch (STGM_ACCESS_MODE(stgm
))
6367 return GENERIC_READ
;
6369 case STGM_READWRITE
:
6370 return GENERIC_READ
| GENERIC_WRITE
;
6372 ERR("Invalid access mode!\n");
6377 /****************************************************************************
6378 * GetCreationModeFromSTGM
6380 * This method will return a creation mode flag from a STGM value.
6381 * The STGM value is assumed valid.
6383 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
6385 switch(STGM_CREATE_MODE(stgm
))
6388 return CREATE_ALWAYS
;
6390 FIXME("STGM_CONVERT not implemented!\n");
6392 case STGM_FAILIFTHERE
:
6395 ERR("Invalid create mode!\n");
6401 /*************************************************************************
6402 * OLECONVERT_LoadOLE10 [Internal]
6404 * Loads the OLE10 STREAM to memory
6407 * pOleStream [I] The OLESTREAM
6408 * pData [I] Data Structure for the OLESTREAM Data
6412 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6413 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6416 * This function is used by OleConvertOLESTREAMToIStorage only.
6418 * Memory allocated for pData must be freed by the caller
6420 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
6423 HRESULT hRes
= S_OK
;
6427 pData
->pData
= NULL
;
6428 pData
->pstrOleObjFileName
= (CHAR
*) NULL
;
6430 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
6433 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6434 if(dwSize
!= sizeof(pData
->dwOleID
))
6436 hRes
= CONVERT10_E_OLESTREAM_GET
;
6438 else if(pData
->dwOleID
!= OLESTREAM_ID
)
6440 hRes
= CONVERT10_E_OLESTREAM_FMT
;
6451 /* Get the TypeID...more info needed for this field */
6452 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6453 if(dwSize
!= sizeof(pData
->dwTypeID
))
6455 hRes
= CONVERT10_E_OLESTREAM_GET
;
6460 if(pData
->dwTypeID
!= 0)
6462 /* Get the length of the OleTypeName */
6463 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6464 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6466 hRes
= CONVERT10_E_OLESTREAM_GET
;
6471 if(pData
->dwOleTypeNameLength
> 0)
6473 /* Get the OleTypeName */
6474 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6475 if(dwSize
!= pData
->dwOleTypeNameLength
)
6477 hRes
= CONVERT10_E_OLESTREAM_GET
;
6483 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
6484 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
6486 hRes
= CONVERT10_E_OLESTREAM_GET
;
6490 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
6491 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
6492 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
6493 if(pData
->pstrOleObjFileName
)
6495 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)(pData
->pstrOleObjFileName
),pData
->dwOleObjFileNameLength
);
6496 if(dwSize
!= pData
->dwOleObjFileNameLength
)
6498 hRes
= CONVERT10_E_OLESTREAM_GET
;
6502 hRes
= CONVERT10_E_OLESTREAM_GET
;
6507 /* Get the Width of the Metafile */
6508 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6509 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6511 hRes
= CONVERT10_E_OLESTREAM_GET
;
6515 /* Get the Height of the Metafile */
6516 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6517 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6519 hRes
= CONVERT10_E_OLESTREAM_GET
;
6525 /* Get the Length of the Data */
6526 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6527 if(dwSize
!= sizeof(pData
->dwDataLength
))
6529 hRes
= CONVERT10_E_OLESTREAM_GET
;
6533 if(hRes
== S_OK
) /* I don't know what is this 8 byts information is we have to figure out */
6535 if(!bStrem1
) /* if it is a second OLE stream data */
6537 pData
->dwDataLength
-= 8;
6538 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)(pData
->strUnknown
), sizeof(pData
->strUnknown
));
6539 if(dwSize
!= sizeof(pData
->strUnknown
))
6541 hRes
= CONVERT10_E_OLESTREAM_GET
;
6547 if(pData
->dwDataLength
> 0)
6549 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
6551 /* Get Data (ex. IStorage, Metafile, or BMP) */
6554 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
6555 if(dwSize
!= pData
->dwDataLength
)
6557 hRes
= CONVERT10_E_OLESTREAM_GET
;
6562 hRes
= CONVERT10_E_OLESTREAM_GET
;
6571 /*************************************************************************
6572 * OLECONVERT_SaveOLE10 [Internal]
6574 * Saves the OLE10 STREAM From memory
6577 * pData [I] Data Structure for the OLESTREAM Data
6578 * pOleStream [I] The OLESTREAM to save
6582 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6585 * This function is used by OleConvertIStorageToOLESTREAM only.
6588 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
6591 HRESULT hRes
= S_OK
;
6595 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6596 if(dwSize
!= sizeof(pData
->dwOleID
))
6598 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6603 /* Set the TypeID */
6604 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6605 if(dwSize
!= sizeof(pData
->dwTypeID
))
6607 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6611 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
6613 /* Set the Length of the OleTypeName */
6614 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6615 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6617 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6622 if(pData
->dwOleTypeNameLength
> 0)
6624 /* Set the OleTypeName */
6625 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6626 if(dwSize
!= pData
->dwOleTypeNameLength
)
6628 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6635 /* Set the width of the Metafile */
6636 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6637 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6639 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6645 /* Set the height of the Metafile */
6646 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6647 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6649 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6655 /* Set the length of the Data */
6656 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6657 if(dwSize
!= sizeof(pData
->dwDataLength
))
6659 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6665 if(pData
->dwDataLength
> 0)
6667 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6668 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
6669 if(dwSize
!= pData
->dwDataLength
)
6671 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6679 /*************************************************************************
6680 * OLECONVERT_GetOLE20FromOLE10[Internal]
6682 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6683 * opens it, and copies the content to the dest IStorage for
6684 * OleConvertOLESTREAMToIStorage
6688 * pDestStorage [I] The IStorage to copy the data to
6689 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6690 * nBufferLength [I] The size of the buffer
6699 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, BYTE
*pBuffer
, DWORD nBufferLength
)
6703 IStorage
*pTempStorage
;
6704 DWORD dwNumOfBytesWritten
;
6705 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6706 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6708 /* Create a temp File */
6709 GetTempPathW(MAX_PATH
, wstrTempDir
);
6710 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6711 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
6713 if(hFile
!= INVALID_HANDLE_VALUE
)
6715 /* Write IStorage Data to File */
6716 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
6719 /* Open and copy temp storage to the Dest Storage */
6720 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
6723 hRes
= StorageImpl_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
6724 StorageBaseImpl_Release(pTempStorage
);
6726 DeleteFileW(wstrTempFile
);
6731 /*************************************************************************
6732 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6734 * Saves the OLE10 STREAM From memory
6737 * pStorage [I] The Src IStorage to copy
6738 * pData [I] The Dest Memory to write to.
6741 * The size in bytes allocated for pData
6744 * Memory allocated for pData must be freed by the caller
6746 * Used by OleConvertIStorageToOLESTREAM only.
6749 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
6753 DWORD nDataLength
= 0;
6754 IStorage
*pTempStorage
;
6755 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6756 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6760 /* Create temp Storage */
6761 GetTempPathW(MAX_PATH
, wstrTempDir
);
6762 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6763 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
6767 /* Copy Src Storage to the Temp Storage */
6768 StorageImpl_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
6769 StorageBaseImpl_Release(pTempStorage
);
6771 /* Open Temp Storage as a file and copy to memory */
6772 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
6773 if(hFile
!= INVALID_HANDLE_VALUE
)
6775 nDataLength
= GetFileSize(hFile
, NULL
);
6776 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
6777 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
6780 DeleteFileW(wstrTempFile
);
6785 /*************************************************************************
6786 * OLECONVERT_CreateOleStream [Internal]
6788 * Creates the "\001OLE" stream in the IStorage if necessary.
6791 * pStorage [I] Dest storage to create the stream in
6797 * This function is used by OleConvertOLESTREAMToIStorage only.
6799 * This stream is still unknown, MS Word seems to have extra data
6800 * but since the data is stored in the OLESTREAM there should be
6801 * no need to recreate the stream. If the stream is manually
6802 * deleted it will create it with this default data.
6805 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
6809 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
6810 BYTE pOleStreamHeader
[] =
6812 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6813 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6814 0x00, 0x00, 0x00, 0x00
6817 /* Create stream if not present */
6818 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
6819 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
6823 /* Write default Data */
6824 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
6825 IStream_Release(pStream
);
6829 /* write a string to a stream, preceded by its length */
6830 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
6837 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
6838 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
6843 str
= CoTaskMemAlloc( len
);
6844 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
6845 r
= IStream_Write( stm
, str
, len
, NULL
);
6846 CoTaskMemFree( str
);
6850 /* read a string preceded by its length from a stream */
6851 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
6854 DWORD len
, count
= 0;
6858 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
6861 if( count
!= sizeof(len
) )
6862 return E_OUTOFMEMORY
;
6864 TRACE("%ld bytes\n",len
);
6866 str
= CoTaskMemAlloc( len
);
6868 return E_OUTOFMEMORY
;
6870 r
= IStream_Read( stm
, str
, len
, &count
);
6875 CoTaskMemFree( str
);
6876 return E_OUTOFMEMORY
;
6879 TRACE("Read string %s\n",debugstr_an(str
,len
));
6881 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
6882 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
6884 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
6885 CoTaskMemFree( str
);
6893 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
6894 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
6898 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6900 static const BYTE unknown1
[12] =
6901 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6902 0xFF, 0xFF, 0xFF, 0xFF};
6903 static const BYTE unknown2
[16] =
6904 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6905 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6907 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
6908 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
6909 debugstr_w(szProgIDName
));
6911 /* Create a CompObj stream if it doesn't exist */
6912 r
= IStorage_CreateStream(pstg
, szwStreamName
,
6913 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
6917 /* Write CompObj Structure to stream */
6918 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
6920 if( SUCCEEDED( r
) )
6921 r
= WriteClassStm( pstm
, clsid
);
6923 if( SUCCEEDED( r
) )
6924 r
= STREAM_WriteString( pstm
, lpszUserType
);
6925 if( SUCCEEDED( r
) )
6926 r
= STREAM_WriteString( pstm
, szClipName
);
6927 if( SUCCEEDED( r
) )
6928 r
= STREAM_WriteString( pstm
, szProgIDName
);
6929 if( SUCCEEDED( r
) )
6930 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
6932 IStream_Release( pstm
);
6937 /***********************************************************************
6938 * WriteFmtUserTypeStg (OLE32.@)
6940 HRESULT WINAPI
WriteFmtUserTypeStg(
6941 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
6944 WCHAR szwClipName
[0x40];
6945 CLSID clsid
= CLSID_NULL
;
6946 LPWSTR wstrProgID
= NULL
;
6949 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
6951 /* get the clipboard format name */
6952 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
) );
6955 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
6957 /* FIXME: There's room to save a CLSID and its ProgID, but
6958 the CLSID is not looked up in the registry and in all the
6959 tests I wrote it was CLSID_NULL. Where does it come from?
6962 /* get the real program ID. This may fail, but that's fine */
6963 ProgIDFromCLSID(&clsid
, &wstrProgID
);
6965 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
6967 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
6968 lpszUserType
, szwClipName
, wstrProgID
);
6970 CoTaskMemFree(wstrProgID
);
6976 /******************************************************************************
6977 * ReadFmtUserTypeStg [OLE32.@]
6979 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
6983 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
6984 unsigned char unknown1
[12];
6985 unsigned char unknown2
[16];
6987 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
6990 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
6992 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
6993 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
6996 WARN("Failed to open stream r = %08lx\n", r
);
7000 /* read the various parts of the structure */
7001 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
7002 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
7004 r
= ReadClassStm( stm
, &clsid
);
7008 r
= STREAM_ReadString( stm
, &szCLSIDName
);
7012 r
= STREAM_ReadString( stm
, &szOleTypeName
);
7016 r
= STREAM_ReadString( stm
, &szProgIDName
);
7020 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
7021 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
7024 /* ok, success... now we just need to store what we found */
7026 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
7027 CoTaskMemFree( szOleTypeName
);
7029 if( lplpszUserType
)
7030 *lplpszUserType
= szCLSIDName
;
7031 CoTaskMemFree( szProgIDName
);
7034 IStream_Release( stm
);
7040 /*************************************************************************
7041 * OLECONVERT_CreateCompObjStream [Internal]
7043 * Creates a "\001CompObj" is the destination IStorage if necessary.
7046 * pStorage [I] The dest IStorage to create the CompObj Stream
7048 * strOleTypeName [I] The ProgID
7052 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7055 * This function is used by OleConvertOLESTREAMToIStorage only.
7057 * The stream data is stored in the OLESTREAM and there should be
7058 * no need to recreate the stream. If the stream is manually
7059 * deleted it will attempt to create it by querying the registry.
7063 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
7066 HRESULT hStorageRes
, hRes
= S_OK
;
7067 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
7068 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7069 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
7071 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7072 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
7074 /* Initialize the CompObj structure */
7075 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
7076 memcpy(&(IStorageCompObj
.byUnknown1
), pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
7077 memcpy(&(IStorageCompObj
.byUnknown2
), pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
7080 /* Create a CompObj stream if it doesn't exist */
7081 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7082 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7083 if(hStorageRes
== S_OK
)
7085 /* copy the OleTypeName to the compobj struct */
7086 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
7087 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
7089 /* copy the OleTypeName to the compobj struct */
7090 /* Note: in the test made, these were Identical */
7091 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
7092 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
7095 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
7096 bufferW
, OLESTREAM_MAX_STR_LEN
);
7097 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
7103 /* Get the CLSID Default Name from the Registry */
7104 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
7105 if(hErr
== ERROR_SUCCESS
)
7107 char strTemp
[OLESTREAM_MAX_STR_LEN
];
7108 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
7109 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
7110 if(hErr
== ERROR_SUCCESS
)
7112 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
7118 /* Write CompObj Structure to stream */
7119 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
7121 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
7123 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
7124 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
7126 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
7128 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
7129 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
7131 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
7133 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
7134 if(IStorageCompObj
.dwProgIDNameLength
> 0)
7136 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
7138 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
7139 IStream_Release(pStream
);
7145 /*************************************************************************
7146 * OLECONVERT_CreateOlePresStream[Internal]
7148 * Creates the "\002OlePres000" Stream with the Metafile data
7151 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7152 * dwExtentX [I] Width of the Metafile
7153 * dwExtentY [I] Height of the Metafile
7154 * pData [I] Metafile data
7155 * dwDataLength [I] Size of the Metafile data
7159 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7162 * This function is used by OleConvertOLESTREAMToIStorage only.
7165 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
7169 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7170 BYTE pOlePresStreamHeader
[] =
7172 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7173 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7174 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7175 0x00, 0x00, 0x00, 0x00
7178 BYTE pOlePresStreamHeaderEmpty
[] =
7180 0x00, 0x00, 0x00, 0x00,
7181 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7182 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7183 0x00, 0x00, 0x00, 0x00
7186 /* Create the OlePres000 Stream */
7187 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7188 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7193 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
7195 memset(&OlePres
, 0, sizeof(OlePres
));
7196 /* Do we have any metafile data to save */
7197 if(dwDataLength
> 0)
7199 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
7200 nHeaderSize
= sizeof(pOlePresStreamHeader
);
7204 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
7205 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
7207 /* Set width and height of the metafile */
7208 OlePres
.dwExtentX
= dwExtentX
;
7209 OlePres
.dwExtentY
= -dwExtentY
;
7211 /* Set Data and Length */
7212 if(dwDataLength
> sizeof(METAFILEPICT16
))
7214 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
7215 OlePres
.pData
= &(pData
[8]);
7217 /* Save OlePres000 Data to Stream */
7218 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
7219 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
7220 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
7221 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
7222 if(OlePres
.dwSize
> 0)
7224 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
7226 IStream_Release(pStream
);
7230 /*************************************************************************
7231 * OLECONVERT_CreateOle10NativeStream [Internal]
7233 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7236 * pStorage [I] Dest storage to create the stream in
7237 * pData [I] Ole10 Native Data (ex. bmp)
7238 * dwDataLength [I] Size of the Ole10 Native Data
7244 * This function is used by OleConvertOLESTREAMToIStorage only.
7246 * Might need to verify the data and return appropriate error message
7249 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, BYTE
*pData
, DWORD dwDataLength
)
7253 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7255 /* Create the Ole10Native Stream */
7256 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7257 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7261 /* Write info to stream */
7262 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
7263 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
7264 IStream_Release(pStream
);
7269 /*************************************************************************
7270 * OLECONVERT_GetOLE10ProgID [Internal]
7272 * Finds the ProgID (or OleTypeID) from the IStorage
7275 * pStorage [I] The Src IStorage to get the ProgID
7276 * strProgID [I] the ProgID string to get
7277 * dwSize [I] the size of the string
7281 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7284 * This function is used by OleConvertIStorageToOLESTREAM only.
7288 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
7292 LARGE_INTEGER iSeekPos
;
7293 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
7294 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7296 /* Open the CompObj Stream */
7297 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7298 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7302 /*Get the OleType from the CompObj Stream */
7303 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
7304 iSeekPos
.u
.HighPart
= 0;
7306 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7307 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
7308 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
7309 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7310 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
7311 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
7312 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7314 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
7317 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
7319 IStream_Release(pStream
);
7324 LPOLESTR wstrProgID
;
7326 /* Get the OleType from the registry */
7327 REFCLSID clsid
= &(stat
.clsid
);
7328 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
7329 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
7332 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
7339 /*************************************************************************
7340 * OLECONVERT_GetOle10PresData [Internal]
7342 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7345 * pStorage [I] Src IStroage
7346 * pOleStream [I] Dest OleStream Mem Struct
7352 * This function is used by OleConvertIStorageToOLESTREAM only.
7354 * Memory allocated for pData must be freed by the caller
7358 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7363 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7365 /* Initialize Default data for OLESTREAM */
7366 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7367 pOleStreamData
[0].dwTypeID
= 2;
7368 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7369 pOleStreamData
[1].dwTypeID
= 0;
7370 pOleStreamData
[0].dwMetaFileWidth
= 0;
7371 pOleStreamData
[0].dwMetaFileHeight
= 0;
7372 pOleStreamData
[0].pData
= NULL
;
7373 pOleStreamData
[1].pData
= NULL
;
7375 /* Open Ole10Native Stream */
7376 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7377 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7381 /* Read Size and Data */
7382 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
7383 if(pOleStreamData
->dwDataLength
> 0)
7385 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
7386 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
7388 IStream_Release(pStream
);
7394 /*************************************************************************
7395 * OLECONVERT_GetOle20PresData[Internal]
7397 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7400 * pStorage [I] Src IStroage
7401 * pOleStreamData [I] Dest OleStream Mem Struct
7407 * This function is used by OleConvertIStorageToOLESTREAM only.
7409 * Memory allocated for pData must be freed by the caller
7411 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7415 OLECONVERT_ISTORAGE_OLEPRES olePress
;
7416 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7418 /* Initialize Default data for OLESTREAM */
7419 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7420 pOleStreamData
[0].dwTypeID
= 2;
7421 pOleStreamData
[0].dwMetaFileWidth
= 0;
7422 pOleStreamData
[0].dwMetaFileHeight
= 0;
7423 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
7424 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7425 pOleStreamData
[1].dwTypeID
= 0;
7426 pOleStreamData
[1].dwOleTypeNameLength
= 0;
7427 pOleStreamData
[1].strOleTypeName
[0] = 0;
7428 pOleStreamData
[1].dwMetaFileWidth
= 0;
7429 pOleStreamData
[1].dwMetaFileHeight
= 0;
7430 pOleStreamData
[1].pData
= NULL
;
7431 pOleStreamData
[1].dwDataLength
= 0;
7434 /* Open OlePress000 stream */
7435 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7436 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7439 LARGE_INTEGER iSeekPos
;
7440 METAFILEPICT16 MetaFilePict
;
7441 static const char strMetafilePictName
[] = "METAFILEPICT";
7443 /* Set the TypeID for a Metafile */
7444 pOleStreamData
[1].dwTypeID
= 5;
7446 /* Set the OleTypeName to Metafile */
7447 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
7448 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
7450 iSeekPos
.u
.HighPart
= 0;
7451 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
7453 /* Get Presentation Data */
7454 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7455 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
7456 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
7457 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
7459 /*Set width and Height */
7460 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
7461 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
7462 if(olePress
.dwSize
> 0)
7465 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
7467 /* Set MetaFilePict struct */
7468 MetaFilePict
.mm
= 8;
7469 MetaFilePict
.xExt
= olePress
.dwExtentX
;
7470 MetaFilePict
.yExt
= olePress
.dwExtentY
;
7471 MetaFilePict
.hMF
= 0;
7473 /* Get Metafile Data */
7474 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
7475 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
7476 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
7478 IStream_Release(pStream
);
7482 /*************************************************************************
7483 * OleConvertOLESTREAMToIStorage [OLE32.@]
7488 * DVTARGETDEVICE paramenter is not handled
7489 * Still unsure of some mem fields for OLE 10 Stream
7490 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7491 * and "\001OLE" streams
7494 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
7495 LPOLESTREAM pOleStream
,
7497 const DVTARGETDEVICE
* ptd
)
7501 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7503 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7507 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7510 if(pstg
== NULL
|| pOleStream
== NULL
)
7512 hRes
= E_INVALIDARG
;
7517 /* Load the OLESTREAM to Memory */
7518 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
7523 /* Load the OLESTREAM to Memory (part 2)*/
7524 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
7530 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
7532 /* Do we have the IStorage Data in the OLESTREAM */
7533 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
7535 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7536 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
7540 /* It must be an original OLE 1.0 source */
7541 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7546 /* It must be an original OLE 1.0 source */
7547 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7550 /* Create CompObj Stream if necessary */
7551 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
7554 /*Create the Ole Stream if necessary */
7555 OLECONVERT_CreateOleStream(pstg
);
7560 /* Free allocated memory */
7561 for(i
=0; i
< 2; i
++)
7563 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7564 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
7565 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
7570 /*************************************************************************
7571 * OleConvertIStorageToOLESTREAM [OLE32.@]
7578 * Still unsure of some mem fields for OLE 10 Stream
7579 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7580 * and "\001OLE" streams.
7583 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
7585 LPOLESTREAM pOleStream
)
7588 HRESULT hRes
= S_OK
;
7590 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7591 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7594 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7596 if(pstg
== NULL
|| pOleStream
== NULL
)
7598 hRes
= E_INVALIDARG
;
7602 /* Get the ProgID */
7603 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
7604 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
7608 /* Was it originally Ole10 */
7609 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7612 IStream_Release(pStream
);
7613 /* Get Presentation Data for Ole10Native */
7614 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
7618 /* Get Presentation Data (OLE20) */
7619 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
7622 /* Save OLESTREAM */
7623 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
7626 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
7631 /* Free allocated memory */
7632 for(i
=0; i
< 2; i
++)
7634 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7640 /***********************************************************************
7641 * GetConvertStg (OLE32.@)
7643 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
7644 FIXME("unimplemented stub!\n");
7648 /******************************************************************************
7649 * StgIsStorageFile [OLE32.@]
7652 StgIsStorageFile(LPCOLESTR fn
)
7658 TRACE("(\'%s\')\n", debugstr_w(fn
));
7659 hf
= CreateFileW(fn
, GENERIC_READ
,
7660 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
7661 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
7663 if (hf
== INVALID_HANDLE_VALUE
)
7664 return STG_E_FILENOTFOUND
;
7666 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
7668 WARN(" unable to read file\n");
7675 if (bytes_read
!= 8) {
7676 WARN(" too short\n");
7680 if (!memcmp(magic
,STORAGE_magic
,8)) {
7685 WARN(" -> Invalid header.\n");