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
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 #define NONAMELESSUNION
34 #define NONAMELESSSTRUCT
39 #include "wine/unicode.h"
40 #include "wine/debug.h"
42 #include "storage32.h"
43 #include "ole2.h" /* For Write/ReadClassStm */
46 #include "wine/wingdi16.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
53 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
54 #define OLESTREAM_ID 0x501
55 #define OLESTREAM_MAX_STR_LEN 255
57 static const char rootPropertyName
[] = "Root Entry";
60 /* OLESTREAM memory structure to use for Get and Put Routines */
61 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
66 DWORD dwOleTypeNameLength
;
67 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
68 CHAR
*pstrOleObjFileName
;
69 DWORD dwOleObjFileNameLength
;
70 DWORD dwMetaFileWidth
;
71 DWORD dwMetaFileHeight
;
72 CHAR strUnknown
[8]; /* don't know what is this 8 byts information in OLE stream. */
75 }OLECONVERT_OLESTREAM_DATA
;
77 /* CompObj Stream structure */
78 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
83 DWORD dwCLSIDNameLength
;
84 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
85 DWORD dwOleTypeNameLength
;
86 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
87 DWORD dwProgIDNameLength
;
88 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
90 }OLECONVERT_ISTORAGE_COMPOBJ
;
93 /* Ole Presention Stream structure */
94 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
102 }OLECONVERT_ISTORAGE_OLEPRES
;
106 /***********************************************************************
107 * Forward declaration of internal functions used by the method DestroyElement
109 static HRESULT
deleteStorageProperty(
110 StorageImpl
*parentStorage
,
111 ULONG foundPropertyIndexToDelete
,
112 StgProperty propertyToDelete
);
114 static HRESULT
deleteStreamProperty(
115 StorageImpl
*parentStorage
,
116 ULONG foundPropertyIndexToDelete
,
117 StgProperty propertyToDelete
);
119 static HRESULT
findPlaceholder(
120 StorageImpl
*storage
,
121 ULONG propertyIndexToStore
,
122 ULONG storagePropertyIndex
,
125 static HRESULT
adjustPropertyChain(
127 StgProperty propertyToDelete
,
128 StgProperty parentProperty
,
129 ULONG parentPropertyId
,
132 /***********************************************************************
133 * Declaration of the functions used to manipulate StgProperty
136 static ULONG
getFreeProperty(
137 StorageImpl
*storage
);
139 static void updatePropertyChain(
140 StorageImpl
*storage
,
141 ULONG newPropertyIndex
,
142 StgProperty newProperty
);
144 static LONG
propertyNameCmp(
145 OLECHAR
*newProperty
,
146 OLECHAR
*currentProperty
);
149 /***********************************************************************
150 * Declaration of miscellaneous functions...
152 static HRESULT
validateSTGM(DWORD stgmValue
);
154 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
155 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
156 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
159 * Virtual function table for the IStorage32Impl class.
161 static IStorageVtbl Storage32Impl_Vtbl
=
163 StorageBaseImpl_QueryInterface
,
164 StorageBaseImpl_AddRef
,
165 StorageBaseImpl_Release
,
166 StorageBaseImpl_CreateStream
,
167 StorageBaseImpl_OpenStream
,
168 StorageImpl_CreateStorage
,
169 StorageBaseImpl_OpenStorage
,
171 StorageImpl_MoveElementTo
,
174 StorageBaseImpl_EnumElements
,
175 StorageImpl_DestroyElement
,
176 StorageBaseImpl_RenameElement
,
177 StorageImpl_SetElementTimes
,
178 StorageBaseImpl_SetClass
,
179 StorageImpl_SetStateBits
,
184 * Virtual function table for the Storage32InternalImpl class.
186 static IStorageVtbl Storage32InternalImpl_Vtbl
=
188 StorageBaseImpl_QueryInterface
,
189 StorageBaseImpl_AddRef
,
190 StorageBaseImpl_Release
,
191 StorageBaseImpl_CreateStream
,
192 StorageBaseImpl_OpenStream
,
193 StorageImpl_CreateStorage
,
194 StorageBaseImpl_OpenStorage
,
196 StorageImpl_MoveElementTo
,
197 StorageInternalImpl_Commit
,
198 StorageInternalImpl_Revert
,
199 StorageBaseImpl_EnumElements
,
200 StorageImpl_DestroyElement
,
201 StorageBaseImpl_RenameElement
,
202 StorageImpl_SetElementTimes
,
203 StorageBaseImpl_SetClass
,
204 StorageImpl_SetStateBits
,
209 * Virtual function table for the IEnumSTATSTGImpl class.
211 static IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
213 IEnumSTATSTGImpl_QueryInterface
,
214 IEnumSTATSTGImpl_AddRef
,
215 IEnumSTATSTGImpl_Release
,
216 IEnumSTATSTGImpl_Next
,
217 IEnumSTATSTGImpl_Skip
,
218 IEnumSTATSTGImpl_Reset
,
219 IEnumSTATSTGImpl_Clone
226 /************************************************************************
227 ** Storage32BaseImpl implementatiion
230 /************************************************************************
231 * Storage32BaseImpl_QueryInterface (IUnknown)
233 * This method implements the common QueryInterface for all IStorage32
234 * implementations contained in this file.
236 * See Windows documentation for more details on IUnknown methods.
238 HRESULT WINAPI
StorageBaseImpl_QueryInterface(
243 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
245 * Perform a sanity check on the parameters.
247 if ( (This
==0) || (ppvObject
==0) )
251 * Initialize the return parameter.
256 * Compare the riid with the interface IDs implemented by this object.
258 if (memcmp(&IID_IUnknown
, riid
, sizeof(IID_IUnknown
)) == 0)
260 *ppvObject
= (IStorage
*)This
;
262 else if (memcmp(&IID_IStorage
, riid
, sizeof(IID_IStorage
)) == 0)
264 *ppvObject
= (IStorage
*)This
;
268 * Check that we obtained an interface.
271 return E_NOINTERFACE
;
274 * Query Interface always increases the reference count by one when it is
277 StorageBaseImpl_AddRef(iface
);
282 /************************************************************************
283 * Storage32BaseImpl_AddRef (IUnknown)
285 * This method implements the common AddRef for all IStorage32
286 * implementations contained in this file.
288 * See Windows documentation for more details on IUnknown methods.
290 ULONG WINAPI
StorageBaseImpl_AddRef(
293 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
294 return InterlockedIncrement(&This
->ref
);
297 /************************************************************************
298 * Storage32BaseImpl_Release (IUnknown)
300 * This method implements the common Release for all IStorage32
301 * implementations contained in this file.
303 * See Windows documentation for more details on IUnknown methods.
305 ULONG WINAPI
StorageBaseImpl_Release(
308 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
310 * Decrease the reference count on this object.
312 ULONG ref
= InterlockedDecrement(&This
->ref
);
315 * If the reference count goes down to 0, perform suicide.
320 * Since we are using a system of base-classes, we want to call the
321 * destructor of the appropriate derived class. To do this, we are
322 * using virtual functions to implement the destructor.
324 This
->v_destructor(This
);
330 /************************************************************************
331 * Storage32BaseImpl_OpenStream (IStorage)
333 * This method will open the specified stream object from the current storage.
335 * See Windows documentation for more details on IStorage methods.
337 HRESULT WINAPI
StorageBaseImpl_OpenStream(
339 const OLECHAR
* pwcsName
, /* [string][in] */
340 void* reserved1
, /* [unique][in] */
341 DWORD grfMode
, /* [in] */
342 DWORD reserved2
, /* [in] */
343 IStream
** ppstm
) /* [out] */
345 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
346 IEnumSTATSTGImpl
* propertyEnumeration
;
347 StgStreamImpl
* newStream
;
348 StgProperty currentProperty
;
349 ULONG foundPropertyIndex
;
350 HRESULT res
= STG_E_UNKNOWN
;
352 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
353 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
356 * Perform a sanity check on the parameters.
358 if ( (pwcsName
==NULL
) || (ppstm
==0) )
365 * Initialize the out parameter
370 * Validate the STGM flags
372 if ( FAILED( validateSTGM(grfMode
) ))
374 res
= STG_E_INVALIDFLAG
;
381 if ( !(grfMode
& STGM_SHARE_EXCLUSIVE
) ||
382 (grfMode
& STGM_DELETEONRELEASE
) ||
383 (grfMode
& STGM_TRANSACTED
) )
385 res
= STG_E_INVALIDFUNCTION
;
390 * Create a property enumeration to search the properties
392 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
393 This
->ancestorStorage
,
394 This
->rootPropertySetIndex
);
397 * Search the enumeration for the property with the given name
399 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
405 * Delete the property enumeration since we don't need it anymore
407 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
410 * If it was found, construct the stream object and return a pointer to it.
412 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
413 (currentProperty
.propertyType
==PROPTYPE_STREAM
) )
415 newStream
= StgStreamImpl_Construct(This
, grfMode
, foundPropertyIndex
);
419 newStream
->grfMode
= grfMode
;
420 *ppstm
= (IStream
*)newStream
;
423 * Since we are returning a pointer to the interface, we have to
424 * nail down the reference.
426 StgStreamImpl_AddRef(*ppstm
);
436 res
= STG_E_FILENOTFOUND
;
440 TRACE("<-- IStream %p\n", *ppstm
);
441 TRACE("<-- %08lx\n", res
);
445 /************************************************************************
446 * Storage32BaseImpl_OpenStorage (IStorage)
448 * This method will open a new storage object from the current storage.
450 * See Windows documentation for more details on IStorage methods.
452 HRESULT WINAPI
StorageBaseImpl_OpenStorage(
454 const OLECHAR
* pwcsName
, /* [string][unique][in] */
455 IStorage
* pstgPriority
, /* [unique][in] */
456 DWORD grfMode
, /* [in] */
457 SNB snbExclude
, /* [unique][in] */
458 DWORD reserved
, /* [in] */
459 IStorage
** ppstg
) /* [out] */
461 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
462 StorageInternalImpl
* newStorage
;
463 IEnumSTATSTGImpl
* propertyEnumeration
;
464 StgProperty currentProperty
;
465 ULONG foundPropertyIndex
;
466 HRESULT res
= STG_E_UNKNOWN
;
468 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
469 iface
, debugstr_w(pwcsName
), pstgPriority
,
470 grfMode
, snbExclude
, reserved
, ppstg
);
473 * Perform a sanity check on the parameters.
475 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
482 if (snbExclude
!= NULL
)
484 res
= STG_E_INVALIDPARAMETER
;
489 * Validate the STGM flags
491 if ( FAILED( validateSTGM(grfMode
) ))
493 res
= STG_E_INVALIDFLAG
;
500 if ( !(grfMode
& STGM_SHARE_EXCLUSIVE
) ||
501 (grfMode
& STGM_DELETEONRELEASE
) ||
502 (grfMode
& STGM_PRIORITY
) )
504 res
= STG_E_INVALIDFUNCTION
;
509 * Initialize the out parameter
514 * Create a property enumeration to search the properties
516 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
517 This
->ancestorStorage
,
518 This
->rootPropertySetIndex
);
521 * Search the enumeration for the property with the given name
523 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
529 * Delete the property enumeration since we don't need it anymore
531 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
534 * If it was found, construct the stream object and return a pointer to it.
536 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
537 (currentProperty
.propertyType
==PROPTYPE_STORAGE
) )
540 * Construct a new Storage object
542 newStorage
= StorageInternalImpl_Construct(
543 This
->ancestorStorage
,
548 *ppstg
= (IStorage
*)newStorage
;
551 * Since we are returning a pointer to the interface,
552 * we have to nail down the reference.
554 StorageBaseImpl_AddRef(*ppstg
);
560 res
= STG_E_INSUFFICIENTMEMORY
;
564 res
= STG_E_FILENOTFOUND
;
567 TRACE("<-- %08lx\n", res
);
571 /************************************************************************
572 * Storage32BaseImpl_EnumElements (IStorage)
574 * This method will create an enumerator object that can be used to
575 * retrieve informatino about all the properties in the storage object.
577 * See Windows documentation for more details on IStorage methods.
579 HRESULT WINAPI
StorageBaseImpl_EnumElements(
581 DWORD reserved1
, /* [in] */
582 void* reserved2
, /* [size_is][unique][in] */
583 DWORD reserved3
, /* [in] */
584 IEnumSTATSTG
** ppenum
) /* [out] */
586 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
587 IEnumSTATSTGImpl
* newEnum
;
589 TRACE("(%p, %ld, %p, %ld, %p)\n",
590 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
593 * Perform a sanity check on the parameters.
595 if ( (This
==0) || (ppenum
==0))
599 * Construct the enumerator.
601 newEnum
= IEnumSTATSTGImpl_Construct(
602 This
->ancestorStorage
,
603 This
->rootPropertySetIndex
);
607 *ppenum
= (IEnumSTATSTG
*)newEnum
;
610 * Don't forget to nail down a reference to the new object before
613 IEnumSTATSTGImpl_AddRef(*ppenum
);
618 return E_OUTOFMEMORY
;
621 /************************************************************************
622 * Storage32BaseImpl_Stat (IStorage)
624 * This method will retrieve information about this storage object.
626 * See Windows documentation for more details on IStorage methods.
628 HRESULT WINAPI
StorageBaseImpl_Stat(
630 STATSTG
* pstatstg
, /* [out] */
631 DWORD grfStatFlag
) /* [in] */
633 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
634 StgProperty curProperty
;
636 HRESULT res
= STG_E_UNKNOWN
;
638 TRACE("(%p, %p, %lx)\n",
639 iface
, pstatstg
, grfStatFlag
);
642 * Perform a sanity check on the parameters.
644 if ( (This
==0) || (pstatstg
==0))
651 * Read the information from the property.
653 readSuccessful
= StorageImpl_ReadProperty(
654 This
->ancestorStorage
,
655 This
->rootPropertySetIndex
,
660 StorageUtl_CopyPropertyToSTATSTG(
674 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
);
676 TRACE("<-- %08lx\n", res
);
680 /************************************************************************
681 * Storage32BaseImpl_RenameElement (IStorage)
683 * This method will rename the specified element.
685 * See Windows documentation for more details on IStorage methods.
687 * Implementation notes: The method used to rename consists of creating a clone
688 * of the deleted StgProperty object setting it with the new name and to
689 * perform a DestroyElement of the old StgProperty.
691 HRESULT WINAPI
StorageBaseImpl_RenameElement(
693 const OLECHAR
* pwcsOldName
, /* [in] */
694 const OLECHAR
* pwcsNewName
) /* [in] */
696 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
697 IEnumSTATSTGImpl
* propertyEnumeration
;
698 StgProperty currentProperty
;
699 ULONG foundPropertyIndex
;
701 TRACE("(%p, %s, %s)\n",
702 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
705 * Create a property enumeration to search the properties
707 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
708 This
->rootPropertySetIndex
);
711 * Search the enumeration for the new property name
713 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
717 if (foundPropertyIndex
!= PROPERTY_NULL
)
720 * There is already a property with the new name
722 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
723 return STG_E_FILEALREADYEXISTS
;
726 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)propertyEnumeration
);
729 * Search the enumeration for the old property name
731 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
736 * Delete the property enumeration since we don't need it anymore
738 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
740 if (foundPropertyIndex
!= PROPERTY_NULL
)
742 StgProperty renamedProperty
;
743 ULONG renamedPropertyIndex
;
746 * Setup a new property for the renamed property
748 renamedProperty
.sizeOfNameString
=
749 ( lstrlenW(pwcsNewName
)+1 ) * sizeof(WCHAR
);
751 if (renamedProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
752 return STG_E_INVALIDNAME
;
754 strcpyW(renamedProperty
.name
, pwcsNewName
);
756 renamedProperty
.propertyType
= currentProperty
.propertyType
;
757 renamedProperty
.startingBlock
= currentProperty
.startingBlock
;
758 renamedProperty
.size
.u
.LowPart
= currentProperty
.size
.u
.LowPart
;
759 renamedProperty
.size
.u
.HighPart
= currentProperty
.size
.u
.HighPart
;
761 renamedProperty
.previousProperty
= PROPERTY_NULL
;
762 renamedProperty
.nextProperty
= PROPERTY_NULL
;
765 * Bring the dirProperty link in case it is a storage and in which
766 * case the renamed storage elements don't require to be reorganized.
768 renamedProperty
.dirProperty
= currentProperty
.dirProperty
;
770 /* call CoFileTime to get the current time
771 renamedProperty.timeStampS1
772 renamedProperty.timeStampD1
773 renamedProperty.timeStampS2
774 renamedProperty.timeStampD2
775 renamedProperty.propertyUniqueID
779 * Obtain a free property in the property chain
781 renamedPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
784 * Save the new property into the new property spot
786 StorageImpl_WriteProperty(
787 This
->ancestorStorage
,
788 renamedPropertyIndex
,
792 * Find a spot in the property chain for our newly created property.
796 renamedPropertyIndex
,
800 * At this point the renamed property has been inserted in the tree,
801 * now, before to Destroy the old property we must zeroed it's dirProperty
802 * otherwise the DestroyProperty below will zap it all and we do not want
804 * Also, we fake that the old property is a storage so the DestroyProperty
805 * will not do a SetSize(0) on the stream data.
807 * This means that we need to tweek the StgProperty if it is a stream or a
810 StorageImpl_ReadProperty(This
->ancestorStorage
,
814 currentProperty
.dirProperty
= PROPERTY_NULL
;
815 currentProperty
.propertyType
= PROPTYPE_STORAGE
;
816 StorageImpl_WriteProperty(
817 This
->ancestorStorage
,
822 * Invoke Destroy to get rid of the ole property and automatically redo
823 * the linking of it's previous and next members...
825 StorageImpl_DestroyElement((IStorage
*)This
->ancestorStorage
, pwcsOldName
);
831 * There is no property with the old name
833 return STG_E_FILENOTFOUND
;
839 /************************************************************************
840 * Storage32BaseImpl_CreateStream (IStorage)
842 * This method will create a stream object within this storage
844 * See Windows documentation for more details on IStorage methods.
846 HRESULT WINAPI
StorageBaseImpl_CreateStream(
848 const OLECHAR
* pwcsName
, /* [string][in] */
849 DWORD grfMode
, /* [in] */
850 DWORD reserved1
, /* [in] */
851 DWORD reserved2
, /* [in] */
852 IStream
** ppstm
) /* [out] */
854 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
855 IEnumSTATSTGImpl
* propertyEnumeration
;
856 StgStreamImpl
* newStream
;
857 StgProperty currentProperty
, newStreamProperty
;
858 ULONG foundPropertyIndex
, newPropertyIndex
;
860 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
861 iface
, debugstr_w(pwcsName
), grfMode
,
862 reserved1
, reserved2
, ppstm
);
865 * Validate parameters
868 return STG_E_INVALIDPOINTER
;
871 return STG_E_INVALIDNAME
;
874 * Validate the STGM flags
876 if ( FAILED( validateSTGM(grfMode
) ))
877 return STG_E_INVALIDFLAG
;
882 if ( !(grfMode
& STGM_SHARE_EXCLUSIVE
) ||
883 (grfMode
& STGM_DELETEONRELEASE
) ||
884 (grfMode
& STGM_TRANSACTED
) )
885 return STG_E_INVALIDFUNCTION
;
888 * Initialize the out parameter
893 * Create a property enumeration to search the properties
895 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
896 This
->rootPropertySetIndex
);
898 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
902 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
904 if (foundPropertyIndex
!= PROPERTY_NULL
)
907 * An element with this name already exists
909 if (grfMode
& STGM_CREATE
)
911 IStorage_DestroyElement(iface
, pwcsName
);
914 return STG_E_FILEALREADYEXISTS
;
918 * memset the empty property
920 memset(&newStreamProperty
, 0, sizeof(StgProperty
));
922 newStreamProperty
.sizeOfNameString
=
923 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
925 if (newStreamProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
926 return STG_E_INVALIDNAME
;
928 strcpyW(newStreamProperty
.name
, pwcsName
);
930 newStreamProperty
.propertyType
= PROPTYPE_STREAM
;
931 newStreamProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
932 newStreamProperty
.size
.u
.LowPart
= 0;
933 newStreamProperty
.size
.u
.HighPart
= 0;
935 newStreamProperty
.previousProperty
= PROPERTY_NULL
;
936 newStreamProperty
.nextProperty
= PROPERTY_NULL
;
937 newStreamProperty
.dirProperty
= PROPERTY_NULL
;
939 /* call CoFileTime to get the current time
940 newStreamProperty.timeStampS1
941 newStreamProperty.timeStampD1
942 newStreamProperty.timeStampS2
943 newStreamProperty.timeStampD2
946 /* newStreamProperty.propertyUniqueID */
949 * Get a free property or create a new one
951 newPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
954 * Save the new property into the new property spot
956 StorageImpl_WriteProperty(
957 This
->ancestorStorage
,
962 * Find a spot in the property chain for our newly created property.
970 * Open the stream to return it.
972 newStream
= StgStreamImpl_Construct(This
, grfMode
, newPropertyIndex
);
976 *ppstm
= (IStream
*)newStream
;
979 * Since we are returning a pointer to the interface, we have to nail down
982 StgStreamImpl_AddRef(*ppstm
);
986 return STG_E_INSUFFICIENTMEMORY
;
992 /************************************************************************
993 * Storage32BaseImpl_SetClass (IStorage)
995 * This method will write the specified CLSID in the property of this
998 * See Windows documentation for more details on IStorage methods.
1000 HRESULT WINAPI
StorageBaseImpl_SetClass(
1002 REFCLSID clsid
) /* [in] */
1004 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
1005 HRESULT hRes
= E_FAIL
;
1006 StgProperty curProperty
;
1009 TRACE("(%p, %p)\n", iface
, clsid
);
1011 success
= StorageImpl_ReadProperty(This
->ancestorStorage
,
1012 This
->rootPropertySetIndex
,
1016 curProperty
.propertyUniqueID
= *clsid
;
1018 success
= StorageImpl_WriteProperty(This
->ancestorStorage
,
1019 This
->rootPropertySetIndex
,
1028 /************************************************************************
1029 ** Storage32Impl implementation
1032 /************************************************************************
1033 * Storage32Impl_CreateStorage (IStorage)
1035 * This method will create the storage object within the provided storage.
1037 * See Windows documentation for more details on IStorage methods.
1039 HRESULT WINAPI
StorageImpl_CreateStorage(
1041 const OLECHAR
*pwcsName
, /* [string][in] */
1042 DWORD grfMode
, /* [in] */
1043 DWORD reserved1
, /* [in] */
1044 DWORD reserved2
, /* [in] */
1045 IStorage
**ppstg
) /* [out] */
1047 StorageImpl
* const This
=(StorageImpl
*)iface
;
1049 IEnumSTATSTGImpl
*propertyEnumeration
;
1050 StgProperty currentProperty
;
1051 StgProperty newProperty
;
1052 ULONG foundPropertyIndex
;
1053 ULONG newPropertyIndex
;
1056 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1057 iface
, debugstr_w(pwcsName
), grfMode
,
1058 reserved1
, reserved2
, ppstg
);
1061 * Validate parameters
1064 return STG_E_INVALIDPOINTER
;
1067 return STG_E_INVALIDNAME
;
1070 * Validate the STGM flags
1072 if ( FAILED( validateSTGM(grfMode
) ) ||
1073 (grfMode
& STGM_DELETEONRELEASE
) )
1074 return STG_E_INVALIDFLAG
;
1077 * Initialize the out parameter
1082 * Create a property enumeration and search the properties
1084 propertyEnumeration
= IEnumSTATSTGImpl_Construct( This
->ancestorStorage
,
1085 This
->rootPropertySetIndex
);
1087 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
1090 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1092 if (foundPropertyIndex
!= PROPERTY_NULL
)
1095 * An element with this name already exists
1097 if (grfMode
& STGM_CREATE
)
1098 IStorage_DestroyElement(iface
, pwcsName
);
1100 return STG_E_FILEALREADYEXISTS
;
1104 * memset the empty property
1106 memset(&newProperty
, 0, sizeof(StgProperty
));
1108 newProperty
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1110 if (newProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
1111 return STG_E_INVALIDNAME
;
1113 strcpyW(newProperty
.name
, pwcsName
);
1115 newProperty
.propertyType
= PROPTYPE_STORAGE
;
1116 newProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
1117 newProperty
.size
.u
.LowPart
= 0;
1118 newProperty
.size
.u
.HighPart
= 0;
1120 newProperty
.previousProperty
= PROPERTY_NULL
;
1121 newProperty
.nextProperty
= PROPERTY_NULL
;
1122 newProperty
.dirProperty
= PROPERTY_NULL
;
1124 /* call CoFileTime to get the current time
1125 newProperty.timeStampS1
1126 newProperty.timeStampD1
1127 newProperty.timeStampS2
1128 newProperty.timeStampD2
1131 /* newStorageProperty.propertyUniqueID */
1134 * Obtain a free property in the property chain
1136 newPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
1139 * Save the new property into the new property spot
1141 StorageImpl_WriteProperty(
1142 This
->ancestorStorage
,
1147 * Find a spot in the property chain for our newly created property.
1149 updatePropertyChain(
1155 * Open it to get a pointer to return.
1157 hr
= IStorage_OpenStorage(
1166 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1176 /***************************************************************************
1180 * Get a free property or create a new one.
1182 static ULONG
getFreeProperty(
1183 StorageImpl
*storage
)
1185 ULONG currentPropertyIndex
= 0;
1186 ULONG newPropertyIndex
= PROPERTY_NULL
;
1187 BOOL readSuccessful
= TRUE
;
1188 StgProperty currentProperty
;
1193 * Start by reading the root property
1195 readSuccessful
= StorageImpl_ReadProperty(storage
->ancestorStorage
,
1196 currentPropertyIndex
,
1200 if (currentProperty
.sizeOfNameString
== 0)
1203 * The property existis and is available, we found it.
1205 newPropertyIndex
= currentPropertyIndex
;
1211 * We exhausted the property list, we will create more space below
1213 newPropertyIndex
= currentPropertyIndex
;
1215 currentPropertyIndex
++;
1217 } while (newPropertyIndex
== PROPERTY_NULL
);
1220 * grow the property chain
1222 if (! readSuccessful
)
1224 StgProperty emptyProperty
;
1225 ULARGE_INTEGER newSize
;
1226 ULONG propertyIndex
;
1227 ULONG lastProperty
= 0;
1228 ULONG blockCount
= 0;
1231 * obtain the new count of property blocks
1233 blockCount
= BlockChainStream_GetCount(
1234 storage
->ancestorStorage
->rootBlockChain
)+1;
1237 * initialize the size used by the property stream
1239 newSize
.u
.HighPart
= 0;
1240 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1243 * add a property block to the property chain
1245 BlockChainStream_SetSize(storage
->ancestorStorage
->rootBlockChain
, newSize
);
1248 * memset the empty property in order to initialize the unused newly
1251 memset(&emptyProperty
, 0, sizeof(StgProperty
));
1256 lastProperty
= storage
->bigBlockSize
/ PROPSET_BLOCK_SIZE
* blockCount
;
1259 propertyIndex
= newPropertyIndex
;
1260 propertyIndex
< lastProperty
;
1263 StorageImpl_WriteProperty(
1264 storage
->ancestorStorage
,
1270 return newPropertyIndex
;
1273 /****************************************************************************
1277 * Case insensitive comparaison of StgProperty.name by first considering
1280 * Returns <0 when newPrpoerty < currentProperty
1281 * >0 when newPrpoerty > currentProperty
1282 * 0 when newPrpoerty == currentProperty
1284 static LONG
propertyNameCmp(
1285 OLECHAR
*newProperty
,
1286 OLECHAR
*currentProperty
)
1288 LONG diff
= lstrlenW(newProperty
) - lstrlenW(currentProperty
);
1293 * We compare the string themselves only when they are of the same length
1295 diff
= lstrcmpiW( newProperty
, currentProperty
);
1301 /****************************************************************************
1305 * Properly link this new element in the property chain.
1307 static void updatePropertyChain(
1308 StorageImpl
*storage
,
1309 ULONG newPropertyIndex
,
1310 StgProperty newProperty
)
1312 StgProperty currentProperty
;
1315 * Read the root property
1317 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1318 storage
->rootPropertySetIndex
,
1321 if (currentProperty
.dirProperty
!= PROPERTY_NULL
)
1324 * The root storage contains some element, therefore, start the research
1325 * for the appropriate location.
1328 ULONG current
, next
, previous
, currentPropertyId
;
1331 * Keep the StgProperty sequence number of the storage first property
1333 currentPropertyId
= currentProperty
.dirProperty
;
1338 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1339 currentProperty
.dirProperty
,
1342 previous
= currentProperty
.previousProperty
;
1343 next
= currentProperty
.nextProperty
;
1344 current
= currentPropertyId
;
1348 LONG diff
= propertyNameCmp( newProperty
.name
, currentProperty
.name
);
1352 if (previous
!= PROPERTY_NULL
)
1354 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1361 currentProperty
.previousProperty
= newPropertyIndex
;
1362 StorageImpl_WriteProperty(storage
->ancestorStorage
,
1370 if (next
!= PROPERTY_NULL
)
1372 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1379 currentProperty
.nextProperty
= newPropertyIndex
;
1380 StorageImpl_WriteProperty(storage
->ancestorStorage
,
1389 * Trying to insert an item with the same name in the
1390 * subtree structure.
1395 previous
= currentProperty
.previousProperty
;
1396 next
= currentProperty
.nextProperty
;
1402 * The root storage is empty, link the new property to it's dir property
1404 currentProperty
.dirProperty
= newPropertyIndex
;
1405 StorageImpl_WriteProperty(storage
->ancestorStorage
,
1406 storage
->rootPropertySetIndex
,
1412 /*************************************************************************
1415 HRESULT WINAPI
StorageImpl_CopyTo(
1417 DWORD ciidExclude
, /* [in] */
1418 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1419 SNB snbExclude
, /* [unique][in] */
1420 IStorage
* pstgDest
) /* [unique][in] */
1422 IEnumSTATSTG
*elements
= 0;
1423 STATSTG curElement
, strStat
;
1425 IStorage
*pstgTmp
, *pstgChild
;
1426 IStream
*pstrTmp
, *pstrChild
;
1428 if ((ciidExclude
!= 0) || (rgiidExclude
!= NULL
) || (snbExclude
!= NULL
))
1429 FIXME("Exclude option not implemented\n");
1431 TRACE("(%p, %ld, %p, %p, %p)\n",
1432 iface
, ciidExclude
, rgiidExclude
,
1433 snbExclude
, pstgDest
);
1436 * Perform a sanity check
1438 if ( pstgDest
== 0 )
1439 return STG_E_INVALIDPOINTER
;
1442 * Enumerate the elements
1444 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1452 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1453 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1458 * Obtain the next element
1460 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1462 if ( hr
== S_FALSE
)
1464 hr
= S_OK
; /* done, every element has been copied */
1468 if (curElement
.type
== STGTY_STORAGE
)
1471 * open child source storage
1473 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1474 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1475 NULL
, 0, &pstgChild
);
1481 * Check if destination storage is not a child of the source
1482 * storage, which will cause an infinite loop
1484 if (pstgChild
== pstgDest
)
1486 IEnumSTATSTG_Release(elements
);
1488 return STG_E_ACCESSDENIED
;
1492 * create a new storage in destination storage
1494 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1495 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1499 * if it already exist, don't create a new one use this one
1501 if (hr
== STG_E_FILEALREADYEXISTS
)
1503 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1504 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1505 NULL
, 0, &pstgTmp
);
1513 * do the copy recursively
1515 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1516 snbExclude
, pstgTmp
);
1518 IStorage_Release( pstgTmp
);
1519 IStorage_Release( pstgChild
);
1521 else if (curElement
.type
== STGTY_STREAM
)
1524 * create a new stream in destination storage. If the stream already
1525 * exist, it will be deleted and a new one will be created.
1527 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1528 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1535 * open child stream storage
1537 hr
= IStorage_OpenStream( iface
, curElement
.pwcsName
, NULL
,
1538 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1545 * Get the size of the source stream
1547 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1550 * Set the size of the destination stream.
1552 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1557 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1560 IStream_Release( pstrTmp
);
1561 IStream_Release( pstrChild
);
1565 WARN("unknown element type: %ld\n", curElement
.type
);
1568 } while (hr
== S_OK
);
1573 IEnumSTATSTG_Release(elements
);
1578 /*************************************************************************
1579 * MoveElementTo (IStorage)
1581 HRESULT WINAPI
StorageImpl_MoveElementTo(
1583 const OLECHAR
*pwcsName
, /* [string][in] */
1584 IStorage
*pstgDest
, /* [unique][in] */
1585 const OLECHAR
*pwcsNewName
,/* [string][in] */
1586 DWORD grfFlags
) /* [in] */
1588 FIXME("not implemented!\n");
1592 /*************************************************************************
1595 HRESULT WINAPI
StorageImpl_Commit(
1597 DWORD grfCommitFlags
)/* [in] */
1599 FIXME("(%ld): stub!\n", grfCommitFlags
);
1603 /*************************************************************************
1606 HRESULT WINAPI
StorageImpl_Revert(
1609 FIXME("not implemented!\n");
1613 /*************************************************************************
1614 * DestroyElement (IStorage)
1616 * Stategy: This implementation is build this way for simplicity not for speed.
1617 * I always delete the top most element of the enumeration and adjust
1618 * the deleted element pointer all the time. This takes longer to
1619 * do but allow to reinvoke DestroyElement whenever we encounter a
1620 * storage object. The optimisation reside in the usage of another
1621 * enumeration stategy that would give all the leaves of a storage
1622 * first. (postfix order)
1624 HRESULT WINAPI
StorageImpl_DestroyElement(
1626 const OLECHAR
*pwcsName
)/* [string][in] */
1628 StorageImpl
* const This
=(StorageImpl
*)iface
;
1630 IEnumSTATSTGImpl
* propertyEnumeration
;
1633 StgProperty propertyToDelete
;
1634 StgProperty parentProperty
;
1635 ULONG foundPropertyIndexToDelete
;
1636 ULONG typeOfRelation
;
1637 ULONG parentPropertyId
;
1640 iface
, debugstr_w(pwcsName
));
1643 * Perform a sanity check on the parameters.
1646 return STG_E_INVALIDPOINTER
;
1649 * Create a property enumeration to search the property with the given name
1651 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
1652 This
->ancestorStorage
,
1653 This
->rootPropertySetIndex
);
1655 foundPropertyIndexToDelete
= IEnumSTATSTGImpl_FindProperty(
1656 propertyEnumeration
,
1660 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1662 if ( foundPropertyIndexToDelete
== PROPERTY_NULL
)
1664 return STG_E_FILENOTFOUND
;
1668 * Find the parent property of the property to delete (the one that
1669 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1670 * the parent is This. Otherwise, the parent is one of it's sibling...
1674 * First, read This's StgProperty..
1676 res
= StorageImpl_ReadProperty(
1677 This
->ancestorStorage
,
1678 This
->rootPropertySetIndex
,
1684 * Second, check to see if by any chance the actual storage (This) is not
1685 * the parent of the property to delete... We never know...
1687 if ( parentProperty
.dirProperty
== foundPropertyIndexToDelete
)
1690 * Set data as it would have been done in the else part...
1692 typeOfRelation
= PROPERTY_RELATION_DIR
;
1693 parentPropertyId
= This
->rootPropertySetIndex
;
1698 * Create a property enumeration to search the parent properties, and
1699 * delete it once done.
1701 IEnumSTATSTGImpl
* propertyEnumeration2
;
1703 propertyEnumeration2
= IEnumSTATSTGImpl_Construct(
1704 This
->ancestorStorage
,
1705 This
->rootPropertySetIndex
);
1707 typeOfRelation
= IEnumSTATSTGImpl_FindParentProperty(
1708 propertyEnumeration2
,
1709 foundPropertyIndexToDelete
,
1713 IEnumSTATSTGImpl_Destroy(propertyEnumeration2
);
1716 if ( propertyToDelete
.propertyType
== PROPTYPE_STORAGE
)
1718 hr
= deleteStorageProperty(
1720 foundPropertyIndexToDelete
,
1723 else if ( propertyToDelete
.propertyType
== PROPTYPE_STREAM
)
1725 hr
= deleteStreamProperty(
1727 foundPropertyIndexToDelete
,
1735 * Adjust the property chain
1737 hr
= adjustPropertyChain(
1748 /************************************************************************
1749 * StorageImpl_Stat (IStorage)
1751 * This method will retrieve information about this storage object.
1753 * See Windows documentation for more details on IStorage methods.
1755 HRESULT WINAPI
StorageImpl_Stat( IStorage
* iface
,
1756 STATSTG
* pstatstg
, /* [out] */
1757 DWORD grfStatFlag
) /* [in] */
1759 StorageImpl
* const This
= (StorageImpl
*)iface
;
1760 HRESULT result
= StorageBaseImpl_Stat( iface
, pstatstg
, grfStatFlag
);
1762 if ( !FAILED(result
) && ((grfStatFlag
& STATFLAG_NONAME
) == 0) && This
->pwcsName
)
1764 CoTaskMemFree(pstatstg
->pwcsName
);
1765 pstatstg
->pwcsName
= CoTaskMemAlloc((lstrlenW(This
->pwcsName
)+1)*sizeof(WCHAR
));
1766 strcpyW(pstatstg
->pwcsName
, This
->pwcsName
);
1774 /*********************************************************************
1778 * Perform the deletion of a complete storage node
1781 static HRESULT
deleteStorageProperty(
1782 StorageImpl
*parentStorage
,
1783 ULONG indexOfPropertyToDelete
,
1784 StgProperty propertyToDelete
)
1786 IEnumSTATSTG
*elements
= 0;
1787 IStorage
*childStorage
= 0;
1788 STATSTG currentElement
;
1790 HRESULT destroyHr
= S_OK
;
1793 * Open the storage and enumerate it
1795 hr
= StorageBaseImpl_OpenStorage(
1796 (IStorage
*)parentStorage
,
1797 propertyToDelete
.name
,
1799 STGM_SHARE_EXCLUSIVE
,
1810 * Enumerate the elements
1812 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
1817 * Obtain the next element
1819 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
1822 destroyHr
= StorageImpl_DestroyElement(
1823 (IStorage
*)childStorage
,
1824 (OLECHAR
*)currentElement
.pwcsName
);
1826 CoTaskMemFree(currentElement
.pwcsName
);
1830 * We need to Reset the enumeration every time because we delete elements
1831 * and the enumeration could be invalid
1833 IEnumSTATSTG_Reset(elements
);
1835 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
1838 * Invalidate the property by zeroing it's name member.
1840 propertyToDelete
.sizeOfNameString
= 0;
1842 StorageImpl_WriteProperty(parentStorage
->ancestorStorage
,
1843 indexOfPropertyToDelete
,
1846 IStorage_Release(childStorage
);
1847 IEnumSTATSTG_Release(elements
);
1852 /*********************************************************************
1856 * Perform the deletion of a stream node
1859 static HRESULT
deleteStreamProperty(
1860 StorageImpl
*parentStorage
,
1861 ULONG indexOfPropertyToDelete
,
1862 StgProperty propertyToDelete
)
1866 ULARGE_INTEGER size
;
1868 size
.u
.HighPart
= 0;
1871 hr
= StorageBaseImpl_OpenStream(
1872 (IStorage
*)parentStorage
,
1873 (OLECHAR
*)propertyToDelete
.name
,
1875 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
1887 hr
= IStream_SetSize(pis
, size
);
1895 * Release the stream object.
1897 IStream_Release(pis
);
1900 * Invalidate the property by zeroing it's name member.
1902 propertyToDelete
.sizeOfNameString
= 0;
1905 * Here we should re-read the property so we get the updated pointer
1906 * but since we are here to zap it, I don't do it...
1908 StorageImpl_WriteProperty(
1909 parentStorage
->ancestorStorage
,
1910 indexOfPropertyToDelete
,
1916 /*********************************************************************
1920 * Finds a placeholder for the StgProperty within the Storage
1923 static HRESULT
findPlaceholder(
1924 StorageImpl
*storage
,
1925 ULONG propertyIndexToStore
,
1926 ULONG storePropertyIndex
,
1929 StgProperty storeProperty
;
1934 * Read the storage property
1936 res
= StorageImpl_ReadProperty(
1937 storage
->ancestorStorage
,
1946 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
1948 if (storeProperty
.previousProperty
!= PROPERTY_NULL
)
1950 return findPlaceholder(
1952 propertyIndexToStore
,
1953 storeProperty
.previousProperty
,
1958 storeProperty
.previousProperty
= propertyIndexToStore
;
1961 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
1963 if (storeProperty
.nextProperty
!= PROPERTY_NULL
)
1965 return findPlaceholder(
1967 propertyIndexToStore
,
1968 storeProperty
.nextProperty
,
1973 storeProperty
.nextProperty
= propertyIndexToStore
;
1976 else if (typeOfRelation
== PROPERTY_RELATION_DIR
)
1978 if (storeProperty
.dirProperty
!= PROPERTY_NULL
)
1980 return findPlaceholder(
1982 propertyIndexToStore
,
1983 storeProperty
.dirProperty
,
1988 storeProperty
.dirProperty
= propertyIndexToStore
;
1992 hr
= StorageImpl_WriteProperty(
1993 storage
->ancestorStorage
,
2005 /*************************************************************************
2009 * This method takes the previous and the next property link of a property
2010 * to be deleted and find them a place in the Storage.
2012 static HRESULT
adjustPropertyChain(
2014 StgProperty propertyToDelete
,
2015 StgProperty parentProperty
,
2016 ULONG parentPropertyId
,
2019 ULONG newLinkProperty
= PROPERTY_NULL
;
2020 BOOL needToFindAPlaceholder
= FALSE
;
2021 ULONG storeNode
= PROPERTY_NULL
;
2022 ULONG toStoreNode
= PROPERTY_NULL
;
2023 INT relationType
= 0;
2027 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
2029 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2032 * Set the parent previous to the property to delete previous
2034 newLinkProperty
= propertyToDelete
.previousProperty
;
2036 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2039 * We also need to find a storage for the other link, setup variables
2040 * to do this at the end...
2042 needToFindAPlaceholder
= TRUE
;
2043 storeNode
= propertyToDelete
.previousProperty
;
2044 toStoreNode
= propertyToDelete
.nextProperty
;
2045 relationType
= PROPERTY_RELATION_NEXT
;
2048 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2051 * Set the parent previous to the property to delete next
2053 newLinkProperty
= propertyToDelete
.nextProperty
;
2057 * Link it for real...
2059 parentProperty
.previousProperty
= newLinkProperty
;
2062 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
2064 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2067 * Set the parent next to the property to delete next previous
2069 newLinkProperty
= propertyToDelete
.previousProperty
;
2071 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2074 * We also need to find a storage for the other link, setup variables
2075 * to do this at the end...
2077 needToFindAPlaceholder
= TRUE
;
2078 storeNode
= propertyToDelete
.previousProperty
;
2079 toStoreNode
= propertyToDelete
.nextProperty
;
2080 relationType
= PROPERTY_RELATION_NEXT
;
2083 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2086 * Set the parent next to the property to delete next
2088 newLinkProperty
= propertyToDelete
.nextProperty
;
2092 * Link it for real...
2094 parentProperty
.nextProperty
= newLinkProperty
;
2096 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2098 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2101 * Set the parent dir to the property to delete previous
2103 newLinkProperty
= propertyToDelete
.previousProperty
;
2105 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2108 * We also need to find a storage for the other link, setup variables
2109 * to do this at the end...
2111 needToFindAPlaceholder
= TRUE
;
2112 storeNode
= propertyToDelete
.previousProperty
;
2113 toStoreNode
= propertyToDelete
.nextProperty
;
2114 relationType
= PROPERTY_RELATION_NEXT
;
2117 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2120 * Set the parent dir to the property to delete next
2122 newLinkProperty
= propertyToDelete
.nextProperty
;
2126 * Link it for real...
2128 parentProperty
.dirProperty
= newLinkProperty
;
2132 * Write back the parent property
2134 res
= StorageImpl_WriteProperty(
2135 This
->ancestorStorage
,
2144 * If a placeholder is required for the other link, then, find one and
2145 * get out of here...
2147 if (needToFindAPlaceholder
)
2149 hr
= findPlaceholder(
2160 /******************************************************************************
2161 * SetElementTimes (IStorage)
2163 HRESULT WINAPI
StorageImpl_SetElementTimes(
2165 const OLECHAR
*pwcsName
,/* [string][in] */
2166 const FILETIME
*pctime
, /* [in] */
2167 const FILETIME
*patime
, /* [in] */
2168 const FILETIME
*pmtime
) /* [in] */
2170 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2174 /******************************************************************************
2175 * SetStateBits (IStorage)
2177 HRESULT WINAPI
StorageImpl_SetStateBits(
2179 DWORD grfStateBits
,/* [in] */
2180 DWORD grfMask
) /* [in] */
2182 FIXME("not implemented!\n");
2186 HRESULT
StorageImpl_Construct(
2196 StgProperty currentProperty
;
2197 BOOL readSuccessful
;
2198 ULONG currentPropertyIndex
;
2200 if ( FAILED( validateSTGM(openFlags
) ))
2201 return STG_E_INVALIDFLAG
;
2203 memset(This
, 0, sizeof(StorageImpl
));
2206 * Initialize the virtual function table.
2208 This
->lpVtbl
= &Storage32Impl_Vtbl
;
2209 This
->v_destructor
= &StorageImpl_Destroy
;
2212 * This is the top-level storage so initialize the ancestor pointer
2215 This
->ancestorStorage
= This
;
2218 * Initialize the physical support of the storage.
2220 This
->hFile
= hFile
;
2223 * Store copy of file path.
2226 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2227 (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
));
2228 if (!This
->pwcsName
)
2229 return STG_E_INSUFFICIENTMEMORY
;
2230 strcpyW(This
->pwcsName
, pwcsName
);
2234 * Initialize the big block cache.
2236 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
2237 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2238 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2244 if (This
->bigBlockFile
== 0)
2249 ULARGE_INTEGER size
;
2250 BYTE
* bigBlockBuffer
;
2253 * Initialize all header variables:
2254 * - The big block depot consists of one block and it is at block 0
2255 * - The properties start at block 1
2256 * - There is no small block depot
2258 memset( This
->bigBlockDepotStart
,
2260 sizeof(This
->bigBlockDepotStart
));
2262 This
->bigBlockDepotCount
= 1;
2263 This
->bigBlockDepotStart
[0] = 0;
2264 This
->rootStartBlock
= 1;
2265 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2266 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
2267 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2268 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2269 This
->extBigBlockDepotCount
= 0;
2271 StorageImpl_SaveFileHeader(This
);
2274 * Add one block for the big block depot and one block for the properties
2276 size
.u
.HighPart
= 0;
2277 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2278 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2281 * Initialize the big block depot
2283 bigBlockBuffer
= StorageImpl_GetBigBlock(This
, 0);
2284 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2285 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2286 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2287 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
2292 * Load the header for the file.
2294 hr
= StorageImpl_LoadFileHeader(This
);
2298 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2305 * There is no block depot cached yet.
2307 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2310 * Start searching for free blocks with block 0.
2312 This
->prevFreeBlock
= 0;
2315 * Create the block chain abstractions.
2317 if(!(This
->rootBlockChain
=
2318 BlockChainStream_Construct(This
, &This
->rootStartBlock
, PROPERTY_NULL
)))
2319 return STG_E_READFAULT
;
2321 if(!(This
->smallBlockDepotChain
=
2322 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2324 return STG_E_READFAULT
;
2327 * Write the root property
2331 StgProperty rootProp
;
2333 * Initialize the property chain
2335 memset(&rootProp
, 0, sizeof(rootProp
));
2336 MultiByteToWideChar( CP_ACP
, 0, rootPropertyName
, -1, rootProp
.name
,
2337 sizeof(rootProp
.name
)/sizeof(WCHAR
) );
2338 rootProp
.sizeOfNameString
= (strlenW(rootProp
.name
)+1) * sizeof(WCHAR
);
2339 rootProp
.propertyType
= PROPTYPE_ROOT
;
2340 rootProp
.previousProperty
= PROPERTY_NULL
;
2341 rootProp
.nextProperty
= PROPERTY_NULL
;
2342 rootProp
.dirProperty
= PROPERTY_NULL
;
2343 rootProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
2344 rootProp
.size
.u
.HighPart
= 0;
2345 rootProp
.size
.u
.LowPart
= 0;
2347 StorageImpl_WriteProperty(This
, 0, &rootProp
);
2351 * Find the ID of the root in the property sets.
2353 currentPropertyIndex
= 0;
2357 readSuccessful
= StorageImpl_ReadProperty(
2359 currentPropertyIndex
,
2364 if ( (currentProperty
.sizeOfNameString
!= 0 ) &&
2365 (currentProperty
.propertyType
== PROPTYPE_ROOT
) )
2367 This
->rootPropertySetIndex
= currentPropertyIndex
;
2371 currentPropertyIndex
++;
2373 } while (readSuccessful
&& (This
->rootPropertySetIndex
== PROPERTY_NULL
) );
2375 if (!readSuccessful
)
2378 return STG_E_READFAULT
;
2382 * Create the block chain abstraction for the small block root chain.
2384 if(!(This
->smallBlockRootChain
=
2385 BlockChainStream_Construct(This
, NULL
, This
->rootPropertySetIndex
)))
2386 return STG_E_READFAULT
;
2391 void StorageImpl_Destroy(
2394 TRACE("(%p)\n", This
);
2397 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2399 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2400 BlockChainStream_Destroy(This
->rootBlockChain
);
2401 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2403 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2407 /******************************************************************************
2408 * Storage32Impl_GetNextFreeBigBlock
2410 * Returns the index of the next free big block.
2411 * If the big block depot is filled, this method will enlarge it.
2414 ULONG
StorageImpl_GetNextFreeBigBlock(
2417 ULONG depotBlockIndexPos
;
2419 ULONG depotBlockOffset
;
2420 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2421 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2423 ULONG freeBlock
= BLOCK_UNUSED
;
2425 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2426 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2429 * Scan the entire big block depot until we find a block marked free
2431 while (nextBlockIndex
!= BLOCK_UNUSED
)
2433 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2435 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2438 * Grow the primary depot.
2440 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2442 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2445 * Add a block depot.
2447 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2448 This
->bigBlockDepotCount
++;
2449 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2452 * Flag it as a block depot.
2454 StorageImpl_SetNextBlockInChain(This
,
2458 /* Save new header information.
2460 StorageImpl_SaveFileHeader(This
);
2465 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2467 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2470 * Grow the extended depot.
2472 ULONG extIndex
= BLOCK_UNUSED
;
2473 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2474 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2476 if (extBlockOffset
== 0)
2478 /* We need an extended block.
2480 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2481 This
->extBigBlockDepotCount
++;
2482 depotBlockIndexPos
= extIndex
+ 1;
2485 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2488 * Add a block depot and mark it in the extended block.
2490 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2491 This
->bigBlockDepotCount
++;
2492 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2494 /* Flag the block depot.
2496 StorageImpl_SetNextBlockInChain(This
,
2500 /* If necessary, flag the extended depot block.
2502 if (extIndex
!= BLOCK_UNUSED
)
2503 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2505 /* Save header information.
2507 StorageImpl_SaveFileHeader(This
);
2511 depotBuffer
= StorageImpl_GetROBigBlock(This
, depotBlockIndexPos
);
2513 if (depotBuffer
!= 0)
2515 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2516 ( nextBlockIndex
!= BLOCK_UNUSED
))
2518 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2520 if (nextBlockIndex
== BLOCK_UNUSED
)
2522 freeBlock
= (depotIndex
* blocksPerDepot
) +
2523 (depotBlockOffset
/sizeof(ULONG
));
2526 depotBlockOffset
+= sizeof(ULONG
);
2529 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2533 depotBlockOffset
= 0;
2536 This
->prevFreeBlock
= freeBlock
;
2541 /******************************************************************************
2542 * Storage32Impl_AddBlockDepot
2544 * This will create a depot block, essentially it is a block initialized
2547 void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2551 blockBuffer
= StorageImpl_GetBigBlock(This
, blockIndex
);
2554 * Initialize blocks as free
2556 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2558 StorageImpl_ReleaseBigBlock(This
, blockBuffer
);
2561 /******************************************************************************
2562 * Storage32Impl_GetExtDepotBlock
2564 * Returns the index of the block that corresponds to the specified depot
2565 * index. This method is only for depot indexes equal or greater than
2566 * COUNT_BBDEPOTINHEADER.
2568 ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2570 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2571 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2572 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2573 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2574 ULONG blockIndex
= BLOCK_UNUSED
;
2575 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2577 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2579 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2580 return BLOCK_UNUSED
;
2582 while (extBlockCount
> 0)
2584 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2588 if (extBlockIndex
!= BLOCK_UNUSED
)
2592 depotBuffer
= StorageImpl_GetROBigBlock(This
, extBlockIndex
);
2594 if (depotBuffer
!= 0)
2596 StorageUtl_ReadDWord(depotBuffer
,
2597 extBlockOffset
* sizeof(ULONG
),
2600 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2607 /******************************************************************************
2608 * Storage32Impl_SetExtDepotBlock
2610 * Associates the specified block index to the specified depot index.
2611 * This method is only for depot indexes equal or greater than
2612 * COUNT_BBDEPOTINHEADER.
2614 void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
,
2618 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2619 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2620 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2621 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2622 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2624 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2626 while (extBlockCount
> 0)
2628 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2632 if (extBlockIndex
!= BLOCK_UNUSED
)
2636 depotBuffer
= StorageImpl_GetBigBlock(This
, extBlockIndex
);
2638 if (depotBuffer
!= 0)
2640 StorageUtl_WriteDWord(depotBuffer
,
2641 extBlockOffset
* sizeof(ULONG
),
2644 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2649 /******************************************************************************
2650 * Storage32Impl_AddExtBlockDepot
2652 * Creates an extended depot block.
2654 ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
2656 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
2657 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
2658 BYTE
* depotBuffer
= NULL
;
2659 ULONG index
= BLOCK_UNUSED
;
2660 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2661 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
2662 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
2664 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
2665 blocksPerDepotBlock
;
2667 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
2670 * The first extended block.
2672 This
->extBigBlockDepotStart
= index
;
2678 * Follow the chain to the last one.
2680 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
2682 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
2686 * Add the new extended block to the chain.
2688 depotBuffer
= StorageImpl_GetBigBlock(This
, nextExtBlock
);
2689 StorageUtl_WriteDWord(depotBuffer
, nextBlockOffset
, index
);
2690 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2694 * Initialize this block.
2696 depotBuffer
= StorageImpl_GetBigBlock(This
, index
);
2697 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2698 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2703 /******************************************************************************
2704 * Storage32Impl_FreeBigBlock
2706 * This method will flag the specified block as free in the big block depot.
2708 void StorageImpl_FreeBigBlock(
2712 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
2714 if (blockIndex
< This
->prevFreeBlock
)
2715 This
->prevFreeBlock
= blockIndex
;
2718 /************************************************************************
2719 * Storage32Impl_GetNextBlockInChain
2721 * This method will retrieve the block index of the next big block in
2724 * Params: This - Pointer to the Storage object.
2725 * blockIndex - Index of the block to retrieve the chain
2727 * nextBlockIndex - receives the return value.
2729 * Returns: This method returns the index of the next block in the chain.
2730 * It will return the constants:
2731 * BLOCK_SPECIAL - If the block given was not part of a
2733 * BLOCK_END_OF_CHAIN - If the block given was the last in
2735 * BLOCK_UNUSED - If the block given was not past of a chain
2737 * BLOCK_EXTBBDEPOT - This block is part of the extended
2740 * See Windows documentation for more details on IStorage methods.
2742 HRESULT
StorageImpl_GetNextBlockInChain(
2745 ULONG
* nextBlockIndex
)
2747 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2748 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2749 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2751 ULONG depotBlockIndexPos
;
2754 *nextBlockIndex
= BLOCK_SPECIAL
;
2756 if(depotBlockCount
>= This
->bigBlockDepotCount
)
2758 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount
,
2759 This
->bigBlockDepotCount
);
2760 return STG_E_READFAULT
;
2764 * Cache the currently accessed depot block.
2766 if (depotBlockCount
!= This
->indexBlockDepotCached
)
2768 This
->indexBlockDepotCached
= depotBlockCount
;
2770 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2772 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2777 * We have to look in the extended depot.
2779 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2782 depotBuffer
= StorageImpl_GetROBigBlock(This
, depotBlockIndexPos
);
2785 return STG_E_READFAULT
;
2787 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
2789 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
2790 This
->blockDepotCached
[index
] = *nextBlockIndex
;
2792 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2795 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
2800 /******************************************************************************
2801 * Storage32Impl_GetNextExtendedBlock
2803 * Given an extended block this method will return the next extended block.
2806 * The last ULONG of an extended block is the block index of the next
2807 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2811 * - The index of the next extended block
2812 * - BLOCK_UNUSED: there is no next extended block.
2813 * - Any other return values denotes failure.
2815 ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
2817 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2818 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2821 depotBuffer
= StorageImpl_GetROBigBlock(This
, blockIndex
);
2825 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2827 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2830 return nextBlockIndex
;
2833 /******************************************************************************
2834 * Storage32Impl_SetNextBlockInChain
2836 * This method will write the index of the specified block's next block
2837 * in the big block depot.
2839 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2842 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2843 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2844 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2847 void StorageImpl_SetNextBlockInChain(
2852 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2853 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2854 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2855 ULONG depotBlockIndexPos
;
2858 assert(depotBlockCount
< This
->bigBlockDepotCount
);
2859 assert(blockIndex
!= nextBlock
);
2861 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2863 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2868 * We have to look in the extended depot.
2870 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2873 depotBuffer
= StorageImpl_GetBigBlock(This
, depotBlockIndexPos
);
2877 StorageUtl_WriteDWord(depotBuffer
, depotBlockOffset
, nextBlock
);
2878 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2882 * Update the cached block depot, if necessary.
2884 if (depotBlockCount
== This
->indexBlockDepotCached
)
2886 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
2890 /******************************************************************************
2891 * Storage32Impl_LoadFileHeader
2893 * This method will read in the file header, i.e. big block index -1.
2895 HRESULT
StorageImpl_LoadFileHeader(
2898 HRESULT hr
= STG_E_FILENOTFOUND
;
2899 void* headerBigBlock
= NULL
;
2903 * Get a pointer to the big block of data containing the header.
2905 headerBigBlock
= StorageImpl_GetROBigBlock(This
, -1);
2908 * Extract the information from the header.
2910 if (headerBigBlock
!=0)
2913 * Check for the "magic number" signature and return an error if it is not
2916 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
2918 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2919 return STG_E_OLDFORMAT
;
2922 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
2924 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2925 return STG_E_INVALIDHEADER
;
2928 StorageUtl_ReadWord(
2930 OFFSET_BIGBLOCKSIZEBITS
,
2931 &This
->bigBlockSizeBits
);
2933 StorageUtl_ReadWord(
2935 OFFSET_SMALLBLOCKSIZEBITS
,
2936 &This
->smallBlockSizeBits
);
2938 StorageUtl_ReadDWord(
2940 OFFSET_BBDEPOTCOUNT
,
2941 &This
->bigBlockDepotCount
);
2943 StorageUtl_ReadDWord(
2945 OFFSET_ROOTSTARTBLOCK
,
2946 &This
->rootStartBlock
);
2948 StorageUtl_ReadDWord(
2950 OFFSET_SBDEPOTSTART
,
2951 &This
->smallBlockDepotStart
);
2953 StorageUtl_ReadDWord(
2955 OFFSET_EXTBBDEPOTSTART
,
2956 &This
->extBigBlockDepotStart
);
2958 StorageUtl_ReadDWord(
2960 OFFSET_EXTBBDEPOTCOUNT
,
2961 &This
->extBigBlockDepotCount
);
2963 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2965 StorageUtl_ReadDWord(
2967 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
2968 &(This
->bigBlockDepotStart
[index
]));
2972 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2976 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
2977 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
2981 This
->bigBlockSize
= 0x000000001 >> (DWORD
)This
->bigBlockSizeBits
;
2982 This
->smallBlockSize
= 0x000000001 >> (DWORD
)This
->smallBlockSizeBits
;
2986 * Right now, the code is making some assumptions about the size of the
2987 * blocks, just make sure they are what we're expecting.
2989 if (This
->bigBlockSize
!= DEF_BIG_BLOCK_SIZE
||
2990 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
)
2992 WARN("Broken OLE storage file\n");
2993 hr
= STG_E_INVALIDHEADER
;
2999 * Release the block.
3001 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
3007 /******************************************************************************
3008 * Storage32Impl_SaveFileHeader
3010 * This method will save to the file the header, i.e. big block -1.
3012 void StorageImpl_SaveFileHeader(
3015 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3020 * Get a pointer to the big block of data containing the header.
3022 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3025 * If the block read failed, the file is probably new.
3030 * Initialize for all unknown fields.
3032 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
3035 * Initialize the magic number.
3037 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3040 * And a bunch of things we don't know what they mean
3042 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3043 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3044 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3045 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
3049 * Write the information to the header.
3051 StorageUtl_WriteWord(
3053 OFFSET_BIGBLOCKSIZEBITS
,
3054 This
->bigBlockSizeBits
);
3056 StorageUtl_WriteWord(
3058 OFFSET_SMALLBLOCKSIZEBITS
,
3059 This
->smallBlockSizeBits
);
3061 StorageUtl_WriteDWord(
3063 OFFSET_BBDEPOTCOUNT
,
3064 This
->bigBlockDepotCount
);
3066 StorageUtl_WriteDWord(
3068 OFFSET_ROOTSTARTBLOCK
,
3069 This
->rootStartBlock
);
3071 StorageUtl_WriteDWord(
3073 OFFSET_SBDEPOTSTART
,
3074 This
->smallBlockDepotStart
);
3076 StorageUtl_WriteDWord(
3078 OFFSET_SBDEPOTCOUNT
,
3079 This
->smallBlockDepotChain
?
3080 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3082 StorageUtl_WriteDWord(
3084 OFFSET_EXTBBDEPOTSTART
,
3085 This
->extBigBlockDepotStart
);
3087 StorageUtl_WriteDWord(
3089 OFFSET_EXTBBDEPOTCOUNT
,
3090 This
->extBigBlockDepotCount
);
3092 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3094 StorageUtl_WriteDWord(
3096 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3097 (This
->bigBlockDepotStart
[index
]));
3101 * Write the big block back to the file.
3103 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
3106 /******************************************************************************
3107 * Storage32Impl_ReadProperty
3109 * This method will read the specified property from the property chain.
3111 BOOL
StorageImpl_ReadProperty(
3114 StgProperty
* buffer
)
3116 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
3117 ULARGE_INTEGER offsetInPropSet
;
3118 BOOL readSuccessful
;
3121 offsetInPropSet
.u
.HighPart
= 0;
3122 offsetInPropSet
.u
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
3124 readSuccessful
= BlockChainStream_ReadAt(
3125 This
->rootBlockChain
,
3133 /* replace the name of root entry (often "Root Entry") by the file name */
3134 WCHAR
*propName
= (index
== This
->rootPropertySetIndex
) ?
3135 This
->filename
: (WCHAR
*)currentProperty
+OFFSET_PS_NAME
;
3137 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3141 PROPERTY_NAME_BUFFER_LEN
);
3142 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3144 memcpy(&buffer
->propertyType
, currentProperty
+ OFFSET_PS_PROPERTYTYPE
, 1);
3146 StorageUtl_ReadWord(
3148 OFFSET_PS_NAMELENGTH
,
3149 &buffer
->sizeOfNameString
);
3151 StorageUtl_ReadDWord(
3153 OFFSET_PS_PREVIOUSPROP
,
3154 &buffer
->previousProperty
);
3156 StorageUtl_ReadDWord(
3159 &buffer
->nextProperty
);
3161 StorageUtl_ReadDWord(
3164 &buffer
->dirProperty
);
3166 StorageUtl_ReadGUID(
3169 &buffer
->propertyUniqueID
);
3171 StorageUtl_ReadDWord(
3174 &buffer
->timeStampS1
);
3176 StorageUtl_ReadDWord(
3179 &buffer
->timeStampD1
);
3181 StorageUtl_ReadDWord(
3184 &buffer
->timeStampS2
);
3186 StorageUtl_ReadDWord(
3189 &buffer
->timeStampD2
);
3191 StorageUtl_ReadDWord(
3193 OFFSET_PS_STARTBLOCK
,
3194 &buffer
->startingBlock
);
3196 StorageUtl_ReadDWord(
3199 &buffer
->size
.u
.LowPart
);
3201 buffer
->size
.u
.HighPart
= 0;
3204 return readSuccessful
;
3207 /*********************************************************************
3208 * Write the specified property into the property chain
3210 BOOL
StorageImpl_WriteProperty(
3213 StgProperty
* buffer
)
3215 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
3216 ULARGE_INTEGER offsetInPropSet
;
3217 BOOL writeSuccessful
;
3220 offsetInPropSet
.u
.HighPart
= 0;
3221 offsetInPropSet
.u
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
3223 memset(currentProperty
, 0, PROPSET_BLOCK_SIZE
);
3226 currentProperty
+ OFFSET_PS_NAME
,
3228 PROPERTY_NAME_BUFFER_LEN
);
3230 memcpy(currentProperty
+ OFFSET_PS_PROPERTYTYPE
, &buffer
->propertyType
, 1);
3232 StorageUtl_WriteWord(
3234 OFFSET_PS_NAMELENGTH
,
3235 buffer
->sizeOfNameString
);
3237 StorageUtl_WriteDWord(
3239 OFFSET_PS_PREVIOUSPROP
,
3240 buffer
->previousProperty
);
3242 StorageUtl_WriteDWord(
3245 buffer
->nextProperty
);
3247 StorageUtl_WriteDWord(
3250 buffer
->dirProperty
);
3252 StorageUtl_WriteGUID(
3255 &buffer
->propertyUniqueID
);
3257 StorageUtl_WriteDWord(
3260 buffer
->timeStampS1
);
3262 StorageUtl_WriteDWord(
3265 buffer
->timeStampD1
);
3267 StorageUtl_WriteDWord(
3270 buffer
->timeStampS2
);
3272 StorageUtl_WriteDWord(
3275 buffer
->timeStampD2
);
3277 StorageUtl_WriteDWord(
3279 OFFSET_PS_STARTBLOCK
,
3280 buffer
->startingBlock
);
3282 StorageUtl_WriteDWord(
3285 buffer
->size
.u
.LowPart
);
3287 writeSuccessful
= BlockChainStream_WriteAt(This
->rootBlockChain
,
3292 return writeSuccessful
;
3295 BOOL
StorageImpl_ReadBigBlock(
3300 void* bigBlockBuffer
;
3302 bigBlockBuffer
= StorageImpl_GetROBigBlock(This
, blockIndex
);
3304 if (bigBlockBuffer
!=0)
3306 memcpy(buffer
, bigBlockBuffer
, This
->bigBlockSize
);
3308 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
3316 BOOL
StorageImpl_WriteBigBlock(
3321 void* bigBlockBuffer
;
3323 bigBlockBuffer
= StorageImpl_GetBigBlock(This
, blockIndex
);
3325 if (bigBlockBuffer
!=0)
3327 memcpy(bigBlockBuffer
, buffer
, This
->bigBlockSize
);
3329 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
3337 void* StorageImpl_GetROBigBlock(
3341 return BIGBLOCKFILE_GetROBigBlock(This
->bigBlockFile
, blockIndex
);
3344 void* StorageImpl_GetBigBlock(
3348 return BIGBLOCKFILE_GetBigBlock(This
->bigBlockFile
, blockIndex
);
3351 void StorageImpl_ReleaseBigBlock(
3355 BIGBLOCKFILE_ReleaseBigBlock(This
->bigBlockFile
, pBigBlock
);
3358 /******************************************************************************
3359 * Storage32Impl_SmallBlocksToBigBlocks
3361 * This method will convert a small block chain to a big block chain.
3362 * The small block chain will be destroyed.
3364 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3366 SmallBlockChainStream
** ppsbChain
)
3368 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3369 ULARGE_INTEGER size
, offset
;
3370 ULONG cbRead
, cbWritten
, cbTotalRead
, cbTotalWritten
;
3371 ULONG propertyIndex
;
3372 BOOL successRead
, successWrite
;
3373 StgProperty chainProperty
;
3375 BlockChainStream
*bbTempChain
= NULL
;
3376 BlockChainStream
*bigBlockChain
= NULL
;
3379 * Create a temporary big block chain that doesn't have
3380 * an associated property. This temporary chain will be
3381 * used to copy data from small blocks to big blocks.
3383 bbTempChain
= BlockChainStream_Construct(This
,
3386 if(!bbTempChain
) return NULL
;
3388 * Grow the big block chain.
3390 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3391 BlockChainStream_SetSize(bbTempChain
, size
);
3394 * Copy the contents of the small block chain to the big block chain
3395 * by small block size increments.
3397 offset
.u
.LowPart
= 0;
3398 offset
.u
.HighPart
= 0;
3402 buffer
= (BYTE
*) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3405 successRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3407 DEF_SMALL_BLOCK_SIZE
,
3410 cbTotalRead
+= cbRead
;
3412 successWrite
= BlockChainStream_WriteAt(bbTempChain
,
3417 cbTotalWritten
+= cbWritten
;
3419 offset
.u
.LowPart
+= This
->smallBlockSize
;
3421 } while (successRead
&& successWrite
);
3422 HeapFree(GetProcessHeap(),0,buffer
);
3424 assert(cbTotalRead
== cbTotalWritten
);
3427 * Destroy the small block chain.
3429 propertyIndex
= (*ppsbChain
)->ownerPropertyIndex
;
3430 size
.u
.HighPart
= 0;
3432 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3433 SmallBlockChainStream_Destroy(*ppsbChain
);
3437 * Change the property information. This chain is now a big block chain
3438 * and it doesn't reside in the small blocks chain anymore.
3440 StorageImpl_ReadProperty(This
, propertyIndex
, &chainProperty
);
3442 chainProperty
.startingBlock
= bbHeadOfChain
;
3444 StorageImpl_WriteProperty(This
, propertyIndex
, &chainProperty
);
3447 * Destroy the temporary propertyless big block chain.
3448 * Create a new big block chain associated with this property.
3450 BlockChainStream_Destroy(bbTempChain
);
3451 bigBlockChain
= BlockChainStream_Construct(This
,
3455 return bigBlockChain
;
3458 /******************************************************************************
3459 ** Storage32InternalImpl implementation
3462 StorageInternalImpl
* StorageInternalImpl_Construct(
3463 StorageImpl
* ancestorStorage
,
3464 ULONG rootPropertyIndex
)
3466 StorageInternalImpl
* newStorage
;
3469 * Allocate space for the new storage object
3471 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl
));
3475 memset(newStorage
, 0, sizeof(StorageInternalImpl
));
3478 * Initialize the virtual function table.
3480 newStorage
->lpVtbl
= &Storage32InternalImpl_Vtbl
;
3481 newStorage
->v_destructor
= &StorageInternalImpl_Destroy
;
3484 * Keep the ancestor storage pointer and nail a reference to it.
3486 newStorage
->ancestorStorage
= ancestorStorage
;
3487 StorageBaseImpl_AddRef((IStorage
*)(newStorage
->ancestorStorage
));
3490 * Keep the index of the root property set for this storage,
3492 newStorage
->rootPropertySetIndex
= rootPropertyIndex
;
3500 void StorageInternalImpl_Destroy(
3501 StorageInternalImpl
* This
)
3503 StorageBaseImpl_Release((IStorage
*)This
->ancestorStorage
);
3504 HeapFree(GetProcessHeap(), 0, This
);
3507 /******************************************************************************
3509 ** Storage32InternalImpl_Commit
3511 ** The non-root storages cannot be opened in transacted mode thus this function
3514 HRESULT WINAPI
StorageInternalImpl_Commit(
3516 DWORD grfCommitFlags
) /* [in] */
3521 /******************************************************************************
3523 ** Storage32InternalImpl_Revert
3525 ** The non-root storages cannot be opened in transacted mode thus this function
3528 HRESULT WINAPI
StorageInternalImpl_Revert(
3534 /******************************************************************************
3535 ** IEnumSTATSTGImpl implementation
3538 IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
3539 StorageImpl
* parentStorage
,
3540 ULONG firstPropertyNode
)
3542 IEnumSTATSTGImpl
* newEnumeration
;
3544 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
3546 if (newEnumeration
!=0)
3549 * Set-up the virtual function table and reference count.
3551 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
3552 newEnumeration
->ref
= 0;
3555 * We want to nail-down the reference to the storage in case the
3556 * enumeration out-lives the storage in the client application.
3558 newEnumeration
->parentStorage
= parentStorage
;
3559 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
3561 newEnumeration
->firstPropertyNode
= firstPropertyNode
;
3564 * Initialize the search stack
3566 newEnumeration
->stackSize
= 0;
3567 newEnumeration
->stackMaxSize
= ENUMSTATSGT_SIZE_INCREMENT
;
3568 newEnumeration
->stackToVisit
=
3569 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
)*ENUMSTATSGT_SIZE_INCREMENT
);
3572 * Make sure the current node of the iterator is the first one.
3574 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
3577 return newEnumeration
;
3580 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
3582 IStorage_Release((IStorage
*)This
->parentStorage
);
3583 HeapFree(GetProcessHeap(), 0, This
->stackToVisit
);
3584 HeapFree(GetProcessHeap(), 0, This
);
3587 HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
3588 IEnumSTATSTG
* iface
,
3592 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3595 * Perform a sanity check on the parameters.
3598 return E_INVALIDARG
;
3601 * Initialize the return parameter.
3606 * Compare the riid with the interface IDs implemented by this object.
3608 if (memcmp(&IID_IUnknown
, riid
, sizeof(IID_IUnknown
)) == 0)
3610 *ppvObject
= (IEnumSTATSTG
*)This
;
3612 else if (memcmp(&IID_IStorage
, riid
, sizeof(IID_IEnumSTATSTG
)) == 0)
3614 *ppvObject
= (IEnumSTATSTG
*)This
;
3618 * Check that we obtained an interface.
3620 if ((*ppvObject
)==0)
3621 return E_NOINTERFACE
;
3624 * Query Interface always increases the reference count by one when it is
3627 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG
*)This
);
3632 ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
3633 IEnumSTATSTG
* iface
)
3635 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3636 return InterlockedIncrement(&This
->ref
);
3639 ULONG WINAPI
IEnumSTATSTGImpl_Release(
3640 IEnumSTATSTG
* iface
)
3642 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3646 newRef
= InterlockedDecrement(&This
->ref
);
3649 * If the reference count goes down to 0, perform suicide.
3653 IEnumSTATSTGImpl_Destroy(This
);
3659 HRESULT WINAPI
IEnumSTATSTGImpl_Next(
3660 IEnumSTATSTG
* iface
,
3663 ULONG
* pceltFetched
)
3665 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3667 StgProperty currentProperty
;
3668 STATSTG
* currentReturnStruct
= rgelt
;
3669 ULONG objectFetched
= 0;
3670 ULONG currentSearchNode
;
3673 * Perform a sanity check on the parameters.
3675 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
3676 return E_INVALIDARG
;
3679 * To avoid the special case, get another pointer to a ULONG value if
3680 * the caller didn't supply one.
3682 if (pceltFetched
==0)
3683 pceltFetched
= &objectFetched
;
3686 * Start the iteration, we will iterate until we hit the end of the
3687 * linked list or until we hit the number of items to iterate through
3692 * Start with the node at the top of the stack.
3694 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3696 while ( ( *pceltFetched
< celt
) &&
3697 ( currentSearchNode
!=PROPERTY_NULL
) )
3700 * Remove the top node from the stack
3702 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3705 * Read the property from the storage.
3707 StorageImpl_ReadProperty(This
->parentStorage
,
3712 * Copy the information to the return buffer.
3714 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct
,
3719 * Step to the next item in the iteration
3722 currentReturnStruct
++;
3725 * Push the next search node in the search stack.
3727 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3730 * continue the iteration.
3732 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3735 if (*pceltFetched
== celt
)
3742 HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
3743 IEnumSTATSTG
* iface
,
3746 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3748 StgProperty currentProperty
;
3749 ULONG objectFetched
= 0;
3750 ULONG currentSearchNode
;
3753 * Start with the node at the top of the stack.
3755 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3757 while ( (objectFetched
< celt
) &&
3758 (currentSearchNode
!=PROPERTY_NULL
) )
3761 * Remove the top node from the stack
3763 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3766 * Read the property from the storage.
3768 StorageImpl_ReadProperty(This
->parentStorage
,
3773 * Step to the next item in the iteration
3778 * Push the next search node in the search stack.
3780 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3783 * continue the iteration.
3785 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3788 if (objectFetched
== celt
)
3794 HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
3795 IEnumSTATSTG
* iface
)
3797 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3799 StgProperty rootProperty
;
3800 BOOL readSuccessful
;
3803 * Re-initialize the search stack to an empty stack
3805 This
->stackSize
= 0;
3808 * Read the root property from the storage.
3810 readSuccessful
= StorageImpl_ReadProperty(
3811 This
->parentStorage
,
3812 This
->firstPropertyNode
,
3817 assert(rootProperty
.sizeOfNameString
!=0);
3820 * Push the search node in the search stack.
3822 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.dirProperty
);
3828 HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
3829 IEnumSTATSTG
* iface
,
3830 IEnumSTATSTG
** ppenum
)
3832 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3834 IEnumSTATSTGImpl
* newClone
;
3837 * Perform a sanity check on the parameters.
3840 return E_INVALIDARG
;
3842 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
3843 This
->firstPropertyNode
);
3847 * The new clone enumeration must point to the same current node as
3850 newClone
->stackSize
= This
->stackSize
;
3851 newClone
->stackMaxSize
= This
->stackMaxSize
;
3852 newClone
->stackToVisit
=
3853 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * newClone
->stackMaxSize
);
3856 newClone
->stackToVisit
,
3858 sizeof(ULONG
) * newClone
->stackSize
);
3860 *ppenum
= (IEnumSTATSTG
*)newClone
;
3863 * Don't forget to nail down a reference to the clone before
3866 IEnumSTATSTGImpl_AddRef(*ppenum
);
3871 INT
IEnumSTATSTGImpl_FindParentProperty(
3872 IEnumSTATSTGImpl
*This
,
3873 ULONG childProperty
,
3874 StgProperty
*currentProperty
,
3877 ULONG currentSearchNode
;
3881 * To avoid the special case, get another pointer to a ULONG value if
3882 * the caller didn't supply one.
3885 thisNodeId
= &foundNode
;
3888 * Start with the node at the top of the stack.
3890 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3893 while (currentSearchNode
!=PROPERTY_NULL
)
3896 * Store the current node in the returned parameters
3898 *thisNodeId
= currentSearchNode
;
3901 * Remove the top node from the stack
3903 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3906 * Read the property from the storage.
3908 StorageImpl_ReadProperty(
3909 This
->parentStorage
,
3913 if (currentProperty
->previousProperty
== childProperty
)
3914 return PROPERTY_RELATION_PREVIOUS
;
3916 else if (currentProperty
->nextProperty
== childProperty
)
3917 return PROPERTY_RELATION_NEXT
;
3919 else if (currentProperty
->dirProperty
== childProperty
)
3920 return PROPERTY_RELATION_DIR
;
3923 * Push the next search node in the search stack.
3925 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
3928 * continue the iteration.
3930 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3933 return PROPERTY_NULL
;
3936 ULONG
IEnumSTATSTGImpl_FindProperty(
3937 IEnumSTATSTGImpl
* This
,
3938 const OLECHAR
* lpszPropName
,
3939 StgProperty
* currentProperty
)
3941 ULONG currentSearchNode
;
3944 * Start with the node at the top of the stack.
3946 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3948 while (currentSearchNode
!=PROPERTY_NULL
)
3951 * Remove the top node from the stack
3953 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3956 * Read the property from the storage.
3958 StorageImpl_ReadProperty(This
->parentStorage
,
3962 if ( propertyNameCmp(
3963 (OLECHAR
*)currentProperty
->name
,
3964 (OLECHAR
*)lpszPropName
) == 0)
3965 return currentSearchNode
;
3968 * Push the next search node in the search stack.
3970 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
3973 * continue the iteration.
3975 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3978 return PROPERTY_NULL
;
3981 void IEnumSTATSTGImpl_PushSearchNode(
3982 IEnumSTATSTGImpl
* This
,
3985 StgProperty rootProperty
;
3986 BOOL readSuccessful
;
3989 * First, make sure we're not trying to push an unexisting node.
3991 if (nodeToPush
==PROPERTY_NULL
)
3995 * First push the node to the stack
3997 if (This
->stackSize
== This
->stackMaxSize
)
3999 This
->stackMaxSize
+= ENUMSTATSGT_SIZE_INCREMENT
;
4001 This
->stackToVisit
= HeapReAlloc(
4005 sizeof(ULONG
) * This
->stackMaxSize
);
4008 This
->stackToVisit
[This
->stackSize
] = nodeToPush
;
4012 * Read the root property from the storage.
4014 readSuccessful
= StorageImpl_ReadProperty(
4015 This
->parentStorage
,
4021 assert(rootProperty
.sizeOfNameString
!=0);
4024 * Push the previous search node in the search stack.
4026 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.previousProperty
);
4030 ULONG
IEnumSTATSTGImpl_PopSearchNode(
4031 IEnumSTATSTGImpl
* This
,
4036 if (This
->stackSize
== 0)
4037 return PROPERTY_NULL
;
4039 topNode
= This
->stackToVisit
[This
->stackSize
-1];
4047 /******************************************************************************
4048 ** StorageUtl implementation
4051 void StorageUtl_ReadWord(void* buffer
, ULONG offset
, WORD
* value
)
4053 memcpy(value
, (BYTE
*)buffer
+offset
, sizeof(WORD
));
4056 void StorageUtl_WriteWord(void* buffer
, ULONG offset
, WORD value
)
4058 memcpy((BYTE
*)buffer
+offset
, &value
, sizeof(WORD
));
4061 void StorageUtl_ReadDWord(void* buffer
, ULONG offset
, DWORD
* value
)
4063 memcpy(value
, (BYTE
*)buffer
+offset
, sizeof(DWORD
));
4066 void StorageUtl_WriteDWord(void* buffer
, ULONG offset
, DWORD value
)
4068 memcpy((BYTE
*)buffer
+offset
, &value
, sizeof(DWORD
));
4071 void StorageUtl_ReadGUID(void* buffer
, ULONG offset
, GUID
* value
)
4073 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
4074 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
4075 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
4077 memcpy(value
->Data4
, (BYTE
*)buffer
+offset
+8, sizeof(value
->Data4
));
4080 void StorageUtl_WriteGUID(void* buffer
, ULONG offset
, GUID
* value
)
4082 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
4083 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
4084 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
4086 memcpy((BYTE
*)buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
4089 void StorageUtl_CopyPropertyToSTATSTG(
4090 STATSTG
* destination
,
4091 StgProperty
* source
,
4095 * The copy of the string occurs only when the flag is not set
4097 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
4098 (source
->name
== NULL
) ||
4099 (source
->name
[0] == 0) )
4101 destination
->pwcsName
= 0;
4105 destination
->pwcsName
=
4106 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
4108 strcpyW((LPWSTR
)destination
->pwcsName
, source
->name
);
4111 switch (source
->propertyType
)
4113 case PROPTYPE_STORAGE
:
4115 destination
->type
= STGTY_STORAGE
;
4117 case PROPTYPE_STREAM
:
4118 destination
->type
= STGTY_STREAM
;
4121 destination
->type
= STGTY_STREAM
;
4125 destination
->cbSize
= source
->size
;
4127 currentReturnStruct->mtime = {0}; TODO
4128 currentReturnStruct->ctime = {0};
4129 currentReturnStruct->atime = {0};
4131 destination
->grfMode
= 0;
4132 destination
->grfLocksSupported
= 0;
4133 destination
->clsid
= source
->propertyUniqueID
;
4134 destination
->grfStateBits
= 0;
4135 destination
->reserved
= 0;
4138 /******************************************************************************
4139 ** BlockChainStream implementation
4142 BlockChainStream
* BlockChainStream_Construct(
4143 StorageImpl
* parentStorage
,
4144 ULONG
* headOfStreamPlaceHolder
,
4145 ULONG propertyIndex
)
4147 BlockChainStream
* newStream
;
4150 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
4152 newStream
->parentStorage
= parentStorage
;
4153 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
4154 newStream
->ownerPropertyIndex
= propertyIndex
;
4155 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
4156 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
4157 newStream
->numBlocks
= 0;
4159 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
4161 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4163 newStream
->numBlocks
++;
4164 newStream
->tailIndex
= blockIndex
;
4166 if(FAILED(StorageImpl_GetNextBlockInChain(
4171 HeapFree(GetProcessHeap(), 0, newStream
);
4179 void BlockChainStream_Destroy(BlockChainStream
* This
)
4181 HeapFree(GetProcessHeap(), 0, This
);
4184 /******************************************************************************
4185 * BlockChainStream_GetHeadOfChain
4187 * Returns the head of this stream chain.
4188 * Some special chains don't have properties, their heads are kept in
4189 * This->headOfStreamPlaceHolder.
4192 ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
4194 StgProperty chainProperty
;
4195 BOOL readSuccessful
;
4197 if (This
->headOfStreamPlaceHolder
!= 0)
4198 return *(This
->headOfStreamPlaceHolder
);
4200 if (This
->ownerPropertyIndex
!= PROPERTY_NULL
)
4202 readSuccessful
= StorageImpl_ReadProperty(
4203 This
->parentStorage
,
4204 This
->ownerPropertyIndex
,
4209 return chainProperty
.startingBlock
;
4213 return BLOCK_END_OF_CHAIN
;
4216 /******************************************************************************
4217 * BlockChainStream_GetCount
4219 * Returns the number of blocks that comprises this chain.
4220 * This is not the size of the stream as the last block may not be full!
4223 ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
4228 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4230 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4234 if(FAILED(StorageImpl_GetNextBlockInChain(
4235 This
->parentStorage
,
4244 /******************************************************************************
4245 * BlockChainStream_ReadAt
4247 * Reads a specified number of bytes from this chain at the specified offset.
4248 * bytesRead may be NULL.
4249 * Failure will be returned if the specified number of bytes has not been read.
4251 BOOL
BlockChainStream_ReadAt(BlockChainStream
* This
,
4252 ULARGE_INTEGER offset
,
4257 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4258 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4259 ULONG bytesToReadInBuffer
;
4262 BYTE
* bigBlockBuffer
;
4265 * Find the first block in the stream that contains part of the buffer.
4267 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4268 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4269 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4271 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4272 This
->lastBlockNoInSequence
= blockNoInSequence
;
4276 ULONG temp
= blockNoInSequence
;
4278 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4279 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4280 This
->lastBlockNoInSequence
= temp
;
4283 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4285 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4287 blockNoInSequence
--;
4290 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4293 * Start reading the buffer.
4296 bufferWalker
= buffer
;
4298 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4301 * Calculate how many bytes we can copy from this big block.
4303 bytesToReadInBuffer
=
4304 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4307 * Copy those bytes to the buffer
4310 StorageImpl_GetROBigBlock(This
->parentStorage
, blockIndex
);
4312 memcpy(bufferWalker
, bigBlockBuffer
+ offsetInBlock
, bytesToReadInBuffer
);
4314 StorageImpl_ReleaseBigBlock(This
->parentStorage
, bigBlockBuffer
);
4317 * Step to the next big block.
4319 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4322 bufferWalker
+= bytesToReadInBuffer
;
4323 size
-= bytesToReadInBuffer
;
4324 *bytesRead
+= bytesToReadInBuffer
;
4325 offsetInBlock
= 0; /* There is no offset on the next block */
4332 /******************************************************************************
4333 * BlockChainStream_WriteAt
4335 * Writes the specified number of bytes to this chain at the specified offset.
4336 * bytesWritten may be NULL.
4337 * Will fail if not all specified number of bytes have been written.
4339 BOOL
BlockChainStream_WriteAt(BlockChainStream
* This
,
4340 ULARGE_INTEGER offset
,
4343 ULONG
* bytesWritten
)
4345 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4346 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4350 BYTE
* bigBlockBuffer
;
4353 * Find the first block in the stream that contains part of the buffer.
4355 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4356 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4357 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4359 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4360 This
->lastBlockNoInSequence
= blockNoInSequence
;
4364 ULONG temp
= blockNoInSequence
;
4366 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4367 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4368 This
->lastBlockNoInSequence
= temp
;
4371 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4373 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4376 blockNoInSequence
--;
4379 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4382 * Here, I'm casting away the constness on the buffer variable
4383 * This is OK since we don't intend to modify that buffer.
4386 bufferWalker
= (BYTE
*)buffer
;
4388 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4391 * Calculate how many bytes we can copy from this big block.
4394 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4397 * Copy those bytes to the buffer
4399 bigBlockBuffer
= StorageImpl_GetBigBlock(This
->parentStorage
, blockIndex
);
4401 memcpy(bigBlockBuffer
+ offsetInBlock
, bufferWalker
, bytesToWrite
);
4403 StorageImpl_ReleaseBigBlock(This
->parentStorage
, bigBlockBuffer
);
4406 * Step to the next big block.
4408 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4411 bufferWalker
+= bytesToWrite
;
4412 size
-= bytesToWrite
;
4413 *bytesWritten
+= bytesToWrite
;
4414 offsetInBlock
= 0; /* There is no offset on the next block */
4420 /******************************************************************************
4421 * BlockChainStream_Shrink
4423 * Shrinks this chain in the big block depot.
4425 BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
4426 ULARGE_INTEGER newSize
)
4428 ULONG blockIndex
, extraBlock
;
4433 * Reset the last accessed block cache.
4435 This
->lastBlockNoInSequence
= 0xFFFFFFFF;
4436 This
->lastBlockNoInSequenceIndex
= BLOCK_END_OF_CHAIN
;
4439 * Figure out how many blocks are needed to contain the new size
4441 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4443 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4446 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4449 * Go to the new end of chain
4451 while (count
< numBlocks
)
4453 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4459 /* Get the next block before marking the new end */
4460 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4464 /* Mark the new end of chain */
4465 StorageImpl_SetNextBlockInChain(
4466 This
->parentStorage
,
4468 BLOCK_END_OF_CHAIN
);
4470 This
->tailIndex
= blockIndex
;
4471 This
->numBlocks
= numBlocks
;
4474 * Mark the extra blocks as free
4476 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4478 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
,
4481 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
4482 extraBlock
= blockIndex
;
4488 /******************************************************************************
4489 * BlockChainStream_Enlarge
4491 * Grows this chain in the big block depot.
4493 BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
4494 ULARGE_INTEGER newSize
)
4496 ULONG blockIndex
, currentBlock
;
4498 ULONG oldNumBlocks
= 0;
4500 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4503 * Empty chain. Create the head.
4505 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4507 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4508 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
4510 BLOCK_END_OF_CHAIN
);
4512 if (This
->headOfStreamPlaceHolder
!= 0)
4514 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
4518 StgProperty chainProp
;
4519 assert(This
->ownerPropertyIndex
!= PROPERTY_NULL
);
4521 StorageImpl_ReadProperty(
4522 This
->parentStorage
,
4523 This
->ownerPropertyIndex
,
4526 chainProp
.startingBlock
= blockIndex
;
4528 StorageImpl_WriteProperty(
4529 This
->parentStorage
,
4530 This
->ownerPropertyIndex
,
4534 This
->tailIndex
= blockIndex
;
4535 This
->numBlocks
= 1;
4539 * Figure out how many blocks are needed to contain this stream
4541 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4543 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4547 * Go to the current end of chain
4549 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
4551 currentBlock
= blockIndex
;
4553 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4556 currentBlock
= blockIndex
;
4558 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
4563 This
->tailIndex
= currentBlock
;
4566 currentBlock
= This
->tailIndex
;
4567 oldNumBlocks
= This
->numBlocks
;
4570 * Add new blocks to the chain
4572 if (oldNumBlocks
< newNumBlocks
)
4574 while (oldNumBlocks
< newNumBlocks
)
4576 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4578 StorageImpl_SetNextBlockInChain(
4579 This
->parentStorage
,
4583 StorageImpl_SetNextBlockInChain(
4584 This
->parentStorage
,
4586 BLOCK_END_OF_CHAIN
);
4588 currentBlock
= blockIndex
;
4592 This
->tailIndex
= blockIndex
;
4593 This
->numBlocks
= newNumBlocks
;
4599 /******************************************************************************
4600 * BlockChainStream_SetSize
4602 * Sets the size of this stream. The big block depot will be updated.
4603 * The file will grow if we grow the chain.
4605 * TODO: Free the actual blocks in the file when we shrink the chain.
4606 * Currently, the blocks are still in the file. So the file size
4607 * doesn't shrink even if we shrink streams.
4609 BOOL
BlockChainStream_SetSize(
4610 BlockChainStream
* This
,
4611 ULARGE_INTEGER newSize
)
4613 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
4615 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
4618 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
4620 BlockChainStream_Shrink(This
, newSize
);
4624 ULARGE_INTEGER fileSize
=
4625 BIGBLOCKFILE_GetSize(This
->parentStorage
->bigBlockFile
);
4627 ULONG diff
= newSize
.u
.LowPart
- size
.u
.LowPart
;
4630 * Make sure the file stays a multiple of blocksize
4632 if ((diff
% This
->parentStorage
->bigBlockSize
) != 0)
4633 diff
+= (This
->parentStorage
->bigBlockSize
-
4634 (diff
% This
->parentStorage
->bigBlockSize
) );
4636 fileSize
.u
.LowPart
+= diff
;
4637 BIGBLOCKFILE_SetSize(This
->parentStorage
->bigBlockFile
, fileSize
);
4639 BlockChainStream_Enlarge(This
, newSize
);
4645 /******************************************************************************
4646 * BlockChainStream_GetSize
4648 * Returns the size of this chain.
4649 * Will return the block count if this chain doesn't have a property.
4651 ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
4653 StgProperty chainProperty
;
4655 if(This
->headOfStreamPlaceHolder
== NULL
)
4658 * This chain is a data stream read the property and return
4659 * the appropriate size
4661 StorageImpl_ReadProperty(
4662 This
->parentStorage
,
4663 This
->ownerPropertyIndex
,
4666 return chainProperty
.size
;
4671 * this chain is a chain that does not have a property, figure out the
4672 * size by making the product number of used blocks times the
4675 ULARGE_INTEGER result
;
4676 result
.u
.HighPart
= 0;
4679 BlockChainStream_GetCount(This
) *
4680 This
->parentStorage
->bigBlockSize
;
4686 /******************************************************************************
4687 ** SmallBlockChainStream implementation
4690 SmallBlockChainStream
* SmallBlockChainStream_Construct(
4691 StorageImpl
* parentStorage
,
4692 ULONG propertyIndex
)
4694 SmallBlockChainStream
* newStream
;
4696 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
4698 newStream
->parentStorage
= parentStorage
;
4699 newStream
->ownerPropertyIndex
= propertyIndex
;
4704 void SmallBlockChainStream_Destroy(
4705 SmallBlockChainStream
* This
)
4707 HeapFree(GetProcessHeap(), 0, This
);
4710 /******************************************************************************
4711 * SmallBlockChainStream_GetHeadOfChain
4713 * Returns the head of this chain of small blocks.
4715 ULONG
SmallBlockChainStream_GetHeadOfChain(
4716 SmallBlockChainStream
* This
)
4718 StgProperty chainProperty
;
4719 BOOL readSuccessful
;
4721 if (This
->ownerPropertyIndex
)
4723 readSuccessful
= StorageImpl_ReadProperty(
4724 This
->parentStorage
,
4725 This
->ownerPropertyIndex
,
4730 return chainProperty
.startingBlock
;
4735 return BLOCK_END_OF_CHAIN
;
4738 /******************************************************************************
4739 * SmallBlockChainStream_GetNextBlockInChain
4741 * Returns the index of the next small block in this chain.
4744 * - BLOCK_END_OF_CHAIN: end of this chain
4745 * - BLOCK_UNUSED: small block 'blockIndex' is free
4747 HRESULT
SmallBlockChainStream_GetNextBlockInChain(
4748 SmallBlockChainStream
* This
,
4750 ULONG
* nextBlockInChain
)
4752 ULARGE_INTEGER offsetOfBlockInDepot
;
4757 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
4759 offsetOfBlockInDepot
.u
.HighPart
= 0;
4760 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4763 * Read those bytes in the buffer from the small block file.
4765 success
= BlockChainStream_ReadAt(
4766 This
->parentStorage
->smallBlockDepotChain
,
4767 offsetOfBlockInDepot
,
4774 StorageUtl_ReadDWord(&buffer
, 0, nextBlockInChain
);
4778 return STG_E_READFAULT
;
4781 /******************************************************************************
4782 * SmallBlockChainStream_SetNextBlockInChain
4784 * Writes the index of the next block of the specified block in the small
4786 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4787 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4789 void SmallBlockChainStream_SetNextBlockInChain(
4790 SmallBlockChainStream
* This
,
4794 ULARGE_INTEGER offsetOfBlockInDepot
;
4798 offsetOfBlockInDepot
.u
.HighPart
= 0;
4799 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4801 StorageUtl_WriteDWord(&buffer
, 0, nextBlock
);
4804 * Read those bytes in the buffer from the small block file.
4806 BlockChainStream_WriteAt(
4807 This
->parentStorage
->smallBlockDepotChain
,
4808 offsetOfBlockInDepot
,
4814 /******************************************************************************
4815 * SmallBlockChainStream_FreeBlock
4817 * Flag small block 'blockIndex' as free in the small block depot.
4819 void SmallBlockChainStream_FreeBlock(
4820 SmallBlockChainStream
* This
,
4823 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
4826 /******************************************************************************
4827 * SmallBlockChainStream_GetNextFreeBlock
4829 * Returns the index of a free small block. The small block depot will be
4830 * enlarged if necessary. The small block chain will also be enlarged if
4833 ULONG
SmallBlockChainStream_GetNextFreeBlock(
4834 SmallBlockChainStream
* This
)
4836 ULARGE_INTEGER offsetOfBlockInDepot
;
4839 ULONG blockIndex
= 0;
4840 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
4841 BOOL success
= TRUE
;
4842 ULONG smallBlocksPerBigBlock
;
4844 offsetOfBlockInDepot
.u
.HighPart
= 0;
4847 * Scan the small block depot for a free block
4849 while (nextBlockIndex
!= BLOCK_UNUSED
)
4851 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4853 success
= BlockChainStream_ReadAt(
4854 This
->parentStorage
->smallBlockDepotChain
,
4855 offsetOfBlockInDepot
,
4861 * If we run out of space for the small block depot, enlarge it
4865 StorageUtl_ReadDWord(&buffer
, 0, &nextBlockIndex
);
4867 if (nextBlockIndex
!= BLOCK_UNUSED
)
4873 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
4875 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
4876 ULONG nextBlock
, newsbdIndex
;
4877 BYTE
* smallBlockDepot
;
4879 nextBlock
= sbdIndex
;
4880 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
4882 sbdIndex
= nextBlock
;
4883 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
, &nextBlock
);
4886 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4887 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
4888 StorageImpl_SetNextBlockInChain(
4889 This
->parentStorage
,
4893 StorageImpl_SetNextBlockInChain(
4894 This
->parentStorage
,
4896 BLOCK_END_OF_CHAIN
);
4899 * Initialize all the small blocks to free
4902 StorageImpl_GetBigBlock(This
->parentStorage
, newsbdIndex
);
4904 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
4905 StorageImpl_ReleaseBigBlock(This
->parentStorage
, smallBlockDepot
);
4910 * We have just created the small block depot.
4912 StgProperty rootProp
;
4916 * Save it in the header
4918 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
4919 StorageImpl_SaveFileHeader(This
->parentStorage
);
4922 * And allocate the first big block that will contain small blocks
4925 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4927 StorageImpl_SetNextBlockInChain(
4928 This
->parentStorage
,
4930 BLOCK_END_OF_CHAIN
);
4932 StorageImpl_ReadProperty(
4933 This
->parentStorage
,
4934 This
->parentStorage
->rootPropertySetIndex
,
4937 rootProp
.startingBlock
= sbStartIndex
;
4938 rootProp
.size
.u
.HighPart
= 0;
4939 rootProp
.size
.u
.LowPart
= This
->parentStorage
->bigBlockSize
;
4941 StorageImpl_WriteProperty(
4942 This
->parentStorage
,
4943 This
->parentStorage
->rootPropertySetIndex
,
4949 smallBlocksPerBigBlock
=
4950 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
4953 * Verify if we have to allocate big blocks to contain small blocks
4955 if (blockIndex
% smallBlocksPerBigBlock
== 0)
4957 StgProperty rootProp
;
4958 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
4960 StorageImpl_ReadProperty(
4961 This
->parentStorage
,
4962 This
->parentStorage
->rootPropertySetIndex
,
4965 if (rootProp
.size
.u
.LowPart
<
4966 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
4968 rootProp
.size
.u
.LowPart
+= This
->parentStorage
->bigBlockSize
;
4970 BlockChainStream_SetSize(
4971 This
->parentStorage
->smallBlockRootChain
,
4974 StorageImpl_WriteProperty(
4975 This
->parentStorage
,
4976 This
->parentStorage
->rootPropertySetIndex
,
4984 /******************************************************************************
4985 * SmallBlockChainStream_ReadAt
4987 * Reads a specified number of bytes from this chain at the specified offset.
4988 * bytesRead may be NULL.
4989 * Failure will be returned if the specified number of bytes has not been read.
4991 BOOL
SmallBlockChainStream_ReadAt(
4992 SmallBlockChainStream
* This
,
4993 ULARGE_INTEGER offset
,
4998 ULARGE_INTEGER offsetInBigBlockFile
;
4999 ULONG blockNoInSequence
=
5000 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5002 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5003 ULONG bytesToReadInBuffer
;
5005 ULONG bytesReadFromBigBlockFile
;
5009 * This should never happen on a small block file.
5011 assert(offset
.u
.HighPart
==0);
5014 * Find the first block in the stream that contains part of the buffer.
5016 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5018 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5020 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5023 blockNoInSequence
--;
5027 * Start reading the buffer.
5030 bufferWalker
= buffer
;
5032 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5035 * Calculate how many bytes we can copy from this small block.
5037 bytesToReadInBuffer
=
5038 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5041 * Calculate the offset of the small block in the small block file.
5043 offsetInBigBlockFile
.u
.HighPart
= 0;
5044 offsetInBigBlockFile
.u
.LowPart
=
5045 blockIndex
* This
->parentStorage
->smallBlockSize
;
5047 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5050 * Read those bytes in the buffer from the small block file.
5052 BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
5053 offsetInBigBlockFile
,
5054 bytesToReadInBuffer
,
5056 &bytesReadFromBigBlockFile
);
5058 assert(bytesReadFromBigBlockFile
== bytesToReadInBuffer
);
5061 * Step to the next big block.
5063 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5065 bufferWalker
+= bytesToReadInBuffer
;
5066 size
-= bytesToReadInBuffer
;
5067 *bytesRead
+= bytesToReadInBuffer
;
5068 offsetInBlock
= 0; /* There is no offset on the next block */
5074 /******************************************************************************
5075 * SmallBlockChainStream_WriteAt
5077 * Writes the specified number of bytes to this chain at the specified offset.
5078 * bytesWritten may be NULL.
5079 * Will fail if not all specified number of bytes have been written.
5081 BOOL
SmallBlockChainStream_WriteAt(
5082 SmallBlockChainStream
* This
,
5083 ULARGE_INTEGER offset
,
5086 ULONG
* bytesWritten
)
5088 ULARGE_INTEGER offsetInBigBlockFile
;
5089 ULONG blockNoInSequence
=
5090 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5092 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5093 ULONG bytesToWriteInBuffer
;
5095 ULONG bytesWrittenFromBigBlockFile
;
5099 * This should never happen on a small block file.
5101 assert(offset
.u
.HighPart
==0);
5104 * Find the first block in the stream that contains part of the buffer.
5106 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5108 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5110 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5112 blockNoInSequence
--;
5116 * Start writing the buffer.
5118 * Here, I'm casting away the constness on the buffer variable
5119 * This is OK since we don't intend to modify that buffer.
5122 bufferWalker
= (BYTE
*)buffer
;
5123 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5126 * Calculate how many bytes we can copy to this small block.
5128 bytesToWriteInBuffer
=
5129 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5132 * Calculate the offset of the small block in the small block file.
5134 offsetInBigBlockFile
.u
.HighPart
= 0;
5135 offsetInBigBlockFile
.u
.LowPart
=
5136 blockIndex
* This
->parentStorage
->smallBlockSize
;
5138 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5141 * Write those bytes in the buffer to the small block file.
5143 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockRootChain
,
5144 offsetInBigBlockFile
,
5145 bytesToWriteInBuffer
,
5147 &bytesWrittenFromBigBlockFile
);
5149 assert(bytesWrittenFromBigBlockFile
== bytesToWriteInBuffer
);
5152 * Step to the next big block.
5154 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5157 bufferWalker
+= bytesToWriteInBuffer
;
5158 size
-= bytesToWriteInBuffer
;
5159 *bytesWritten
+= bytesToWriteInBuffer
;
5160 offsetInBlock
= 0; /* There is no offset on the next block */
5166 /******************************************************************************
5167 * SmallBlockChainStream_Shrink
5169 * Shrinks this chain in the small block depot.
5171 BOOL
SmallBlockChainStream_Shrink(
5172 SmallBlockChainStream
* This
,
5173 ULARGE_INTEGER newSize
)
5175 ULONG blockIndex
, extraBlock
;
5179 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5181 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5184 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5187 * Go to the new end of chain
5189 while (count
< numBlocks
)
5191 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5198 * If the count is 0, we have a special case, the head of the chain was
5203 StgProperty chainProp
;
5205 StorageImpl_ReadProperty(This
->parentStorage
,
5206 This
->ownerPropertyIndex
,
5209 chainProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
5211 StorageImpl_WriteProperty(This
->parentStorage
,
5212 This
->ownerPropertyIndex
,
5216 * We start freeing the chain at the head block.
5218 extraBlock
= blockIndex
;
5222 /* Get the next block before marking the new end */
5223 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5227 /* Mark the new end of chain */
5228 SmallBlockChainStream_SetNextBlockInChain(
5231 BLOCK_END_OF_CHAIN
);
5235 * Mark the extra blocks as free
5237 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
5239 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
5242 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
5243 extraBlock
= blockIndex
;
5249 /******************************************************************************
5250 * SmallBlockChainStream_Enlarge
5252 * Grows this chain in the small block depot.
5254 BOOL
SmallBlockChainStream_Enlarge(
5255 SmallBlockChainStream
* This
,
5256 ULARGE_INTEGER newSize
)
5258 ULONG blockIndex
, currentBlock
;
5260 ULONG oldNumBlocks
= 0;
5262 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5267 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5270 StgProperty chainProp
;
5272 StorageImpl_ReadProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
5275 chainProp
.startingBlock
= SmallBlockChainStream_GetNextFreeBlock(This
);
5277 StorageImpl_WriteProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
5280 blockIndex
= chainProp
.startingBlock
;
5281 SmallBlockChainStream_SetNextBlockInChain(
5284 BLOCK_END_OF_CHAIN
);
5287 currentBlock
= blockIndex
;
5290 * Figure out how many blocks are needed to contain this stream
5292 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5294 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5298 * Go to the current end of chain
5300 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5303 currentBlock
= blockIndex
;
5304 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
5309 * Add new blocks to the chain
5311 while (oldNumBlocks
< newNumBlocks
)
5313 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
5314 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
5316 SmallBlockChainStream_SetNextBlockInChain(
5319 BLOCK_END_OF_CHAIN
);
5321 currentBlock
= blockIndex
;
5328 /******************************************************************************
5329 * SmallBlockChainStream_GetCount
5331 * Returns the number of blocks that comprises this chain.
5332 * This is not the size of this chain as the last block may not be full!
5334 ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
5339 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5341 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5345 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5352 /******************************************************************************
5353 * SmallBlockChainStream_SetSize
5355 * Sets the size of this stream.
5356 * The file will grow if we grow the chain.
5358 * TODO: Free the actual blocks in the file when we shrink the chain.
5359 * Currently, the blocks are still in the file. So the file size
5360 * doesn't shrink even if we shrink streams.
5362 BOOL
SmallBlockChainStream_SetSize(
5363 SmallBlockChainStream
* This
,
5364 ULARGE_INTEGER newSize
)
5366 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
5368 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
5371 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
5373 SmallBlockChainStream_Shrink(This
, newSize
);
5377 SmallBlockChainStream_Enlarge(This
, newSize
);
5383 /******************************************************************************
5384 * SmallBlockChainStream_GetSize
5386 * Returns the size of this chain.
5388 ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
5390 StgProperty chainProperty
;
5392 StorageImpl_ReadProperty(
5393 This
->parentStorage
,
5394 This
->ownerPropertyIndex
,
5397 return chainProperty
.size
;
5400 /******************************************************************************
5401 * StgCreateDocfile [OLE32.@]
5403 HRESULT WINAPI
StgCreateDocfile(
5407 IStorage
**ppstgOpen
)
5409 StorageImpl
* newStorage
= 0;
5410 HANDLE hFile
= INVALID_HANDLE_VALUE
;
5415 DWORD fileAttributes
;
5416 WCHAR tempFileName
[MAX_PATH
];
5418 TRACE("(%s, %lx, %ld, %p)\n",
5419 debugstr_w(pwcsName
), grfMode
,
5420 reserved
, ppstgOpen
);
5423 * Validate the parameters
5426 return STG_E_INVALIDPOINTER
;
5429 * Validate the STGM flags
5431 if ( FAILED( validateSTGM(grfMode
) ))
5432 return STG_E_INVALIDFLAG
;
5435 * Generate a unique name.
5439 WCHAR tempPath
[MAX_PATH
];
5440 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
5442 if (!(grfMode
& STGM_SHARE_EXCLUSIVE
))
5443 return STG_E_INVALIDFLAG
;
5444 if (!(grfMode
& (STGM_WRITE
|STGM_READWRITE
)))
5445 return STG_E_INVALIDFLAG
;
5447 memset(tempPath
, 0, sizeof(tempPath
));
5448 memset(tempFileName
, 0, sizeof(tempFileName
));
5450 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
5453 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
5454 pwcsName
= tempFileName
;
5456 return STG_E_INSUFFICIENTMEMORY
;
5458 creationMode
= TRUNCATE_EXISTING
;
5462 creationMode
= GetCreationModeFromSTGM(grfMode
);
5466 * Interpret the STGM value grfMode
5468 shareMode
= GetShareModeFromSTGM(grfMode
);
5469 accessMode
= GetAccessModeFromSTGM(grfMode
);
5471 if (grfMode
& STGM_DELETEONRELEASE
)
5472 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
5474 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
5476 if (grfMode
& STGM_TRANSACTED
)
5477 FIXME("Transacted mode not implemented.\n");
5480 * Initialize the "out" parameter.
5484 hFile
= CreateFileW(pwcsName
,
5492 if (hFile
== INVALID_HANDLE_VALUE
)
5498 * Allocate and initialize the new IStorage32object.
5500 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5502 if (newStorage
== 0)
5503 return STG_E_INSUFFICIENTMEMORY
;
5505 hr
= StorageImpl_Construct(
5516 HeapFree(GetProcessHeap(), 0, newStorage
);
5521 * Get an "out" pointer for the caller.
5523 hr
= StorageBaseImpl_QueryInterface(
5524 (IStorage
*)newStorage
,
5525 (REFIID
)&IID_IStorage
,
5531 /******************************************************************************
5532 * StgCreateStorageEx [OLE32.@]
5534 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
5536 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
5537 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
5538 return STG_E_UNIMPLEMENTEDFUNCTION
;
5541 /******************************************************************************
5542 * StgOpenStorage [OLE32.@]
5544 HRESULT WINAPI
StgOpenStorage(
5545 const OLECHAR
*pwcsName
,
5546 IStorage
*pstgPriority
,
5550 IStorage
**ppstgOpen
)
5552 StorageImpl
* newStorage
= 0;
5557 WCHAR fullname
[MAX_PATH
];
5560 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5561 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
5562 snbExclude
, reserved
, ppstgOpen
);
5565 * Perform a sanity check
5567 if (( pwcsName
== 0) || (ppstgOpen
== 0) )
5569 hr
= STG_E_INVALIDPOINTER
;
5574 * Validate the STGM flags
5576 if ( FAILED( validateSTGM(grfMode
) ))
5578 hr
= STG_E_INVALIDFLAG
;
5583 * Interpret the STGM value grfMode
5585 shareMode
= GetShareModeFromSTGM(grfMode
);
5586 accessMode
= GetAccessModeFromSTGM(grfMode
);
5589 * Initialize the "out" parameter.
5593 hFile
= CreateFileW( pwcsName
,
5598 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
5601 length
= GetFileSize(hFile
, NULL
);
5603 if (hFile
==INVALID_HANDLE_VALUE
)
5605 DWORD last_error
= GetLastError();
5611 case ERROR_FILE_NOT_FOUND
:
5612 hr
= STG_E_FILENOTFOUND
;
5615 case ERROR_PATH_NOT_FOUND
:
5616 hr
= STG_E_PATHNOTFOUND
;
5619 case ERROR_ACCESS_DENIED
:
5620 case ERROR_WRITE_PROTECT
:
5621 hr
= STG_E_ACCESSDENIED
;
5624 case ERROR_SHARING_VIOLATION
:
5625 hr
= STG_E_SHAREVIOLATION
;
5636 * Allocate and initialize the new IStorage32object.
5638 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5640 if (newStorage
== 0)
5642 hr
= STG_E_INSUFFICIENTMEMORY
;
5646 /* if the file's length was zero, initialize the storage */
5647 hr
= StorageImpl_Construct(
5658 HeapFree(GetProcessHeap(), 0, newStorage
);
5660 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5662 if(hr
== STG_E_INVALIDHEADER
)
5663 hr
= STG_E_FILEALREADYEXISTS
;
5667 /* prepare the file name string given in lieu of the root property name */
5668 GetFullPathNameW(pwcsName
, MAX_PATH
, fullname
, NULL
);
5669 memcpy(newStorage
->filename
, fullname
, PROPERTY_NAME_BUFFER_LEN
);
5670 newStorage
->filename
[PROPERTY_NAME_BUFFER_LEN
-1] = '\0';
5673 * Get an "out" pointer for the caller.
5675 hr
= StorageBaseImpl_QueryInterface(
5676 (IStorage
*)newStorage
,
5677 (REFIID
)&IID_IStorage
,
5681 TRACE("<-- %08lx, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
5685 /******************************************************************************
5686 * StgCreateDocfileOnILockBytes [OLE32.@]
5688 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
5692 IStorage
** ppstgOpen
)
5694 StorageImpl
* newStorage
= 0;
5698 * Validate the parameters
5700 if ((ppstgOpen
== 0) || (plkbyt
== 0))
5701 return STG_E_INVALIDPOINTER
;
5704 * Allocate and initialize the new IStorage object.
5706 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5708 if (newStorage
== 0)
5709 return STG_E_INSUFFICIENTMEMORY
;
5711 hr
= StorageImpl_Construct(
5722 HeapFree(GetProcessHeap(), 0, newStorage
);
5727 * Get an "out" pointer for the caller.
5729 hr
= StorageBaseImpl_QueryInterface(
5730 (IStorage
*)newStorage
,
5731 (REFIID
)&IID_IStorage
,
5737 /******************************************************************************
5738 * StgOpenStorageOnILockBytes [OLE32.@]
5740 HRESULT WINAPI
StgOpenStorageOnILockBytes(
5742 IStorage
*pstgPriority
,
5746 IStorage
**ppstgOpen
)
5748 StorageImpl
* newStorage
= 0;
5752 * Perform a sanity check
5754 if ((plkbyt
== 0) || (ppstgOpen
== 0))
5755 return STG_E_INVALIDPOINTER
;
5758 * Validate the STGM flags
5760 if ( FAILED( validateSTGM(grfMode
) ))
5761 return STG_E_INVALIDFLAG
;
5764 * Initialize the "out" parameter.
5769 * Allocate and initialize the new IStorage object.
5771 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5773 if (newStorage
== 0)
5774 return STG_E_INSUFFICIENTMEMORY
;
5776 hr
= StorageImpl_Construct(
5787 HeapFree(GetProcessHeap(), 0, newStorage
);
5792 * Get an "out" pointer for the caller.
5794 hr
= StorageBaseImpl_QueryInterface(
5795 (IStorage
*)newStorage
,
5796 (REFIID
)&IID_IStorage
,
5802 /******************************************************************************
5803 * StgSetTimes [ole32.@]
5804 * StgSetTimes [OLE32.@]
5808 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *a
,
5809 FILETIME
const *b
, FILETIME
const *c
)
5811 FIXME("(%s, %p, %p, %p),stub!\n", debugstr_w(str
), a
, b
, c
);
5815 /******************************************************************************
5816 * StgIsStorageILockBytes [OLE32.@]
5818 * Determines if the ILockBytes contains a storage object.
5820 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
5823 ULARGE_INTEGER offset
;
5825 offset
.u
.HighPart
= 0;
5826 offset
.u
.LowPart
= 0;
5828 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
5830 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
5836 /******************************************************************************
5837 * WriteClassStg [OLE32.@]
5839 * This method will store the specified CLSID in the specified storage object
5841 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
5847 hRes
= IStorage_SetClass(pStg
, rclsid
);
5852 /***********************************************************************
5853 * ReadClassStg (OLE32.@)
5855 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5857 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
5867 * read a STATSTG structure (contains the clsid) from the storage
5869 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_DEFAULT
);
5872 *pclsid
=pstatstg
.clsid
;
5877 /***********************************************************************
5878 * OleLoadFromStream (OLE32.@)
5880 * This function loads an object from stream
5882 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
5886 LPPERSISTSTREAM xstm
;
5888 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
5890 res
=ReadClassStm(pStm
,&clsid
);
5891 if (!SUCCEEDED(res
))
5893 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
5894 if (!SUCCEEDED(res
))
5896 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
5897 if (!SUCCEEDED(res
)) {
5898 IUnknown_Release((IUnknown
*)*ppvObj
);
5901 res
=IPersistStream_Load(xstm
,pStm
);
5902 IPersistStream_Release(xstm
);
5903 /* FIXME: all refcounts ok at this point? I think they should be:
5906 * xstm : 0 (released)
5911 /***********************************************************************
5912 * OleSaveToStream (OLE32.@)
5914 * This function saves an object with the IPersistStream interface on it
5915 * to the specified stream.
5917 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
5923 TRACE("(%p,%p)\n",pPStm
,pStm
);
5925 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
5927 if (SUCCEEDED(res
)){
5929 res
=WriteClassStm(pStm
,&clsid
);
5933 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
5936 TRACE("Finished Save\n");
5940 /****************************************************************************
5941 * This method validate a STGM parameter that can contain the values below
5943 * STGM_DIRECT 0x00000000
5944 * STGM_TRANSACTED 0x00010000
5945 * STGM_SIMPLE 0x08000000
5947 * STGM_READ 0x00000000
5948 * STGM_WRITE 0x00000001
5949 * STGM_READWRITE 0x00000002
5951 * STGM_SHARE_DENY_NONE 0x00000040
5952 * STGM_SHARE_DENY_READ 0x00000030
5953 * STGM_SHARE_DENY_WRITE 0x00000020
5954 * STGM_SHARE_EXCLUSIVE 0x00000010
5956 * STGM_PRIORITY 0x00040000
5957 * STGM_DELETEONRELEASE 0x04000000
5959 * STGM_CREATE 0x00001000
5960 * STGM_CONVERT 0x00020000
5961 * STGM_FAILIFTHERE 0x00000000
5963 * STGM_NOSCRATCH 0x00100000
5964 * STGM_NOSNAPSHOT 0x00200000
5966 static HRESULT
validateSTGM(DWORD stgm
)
5968 BOOL bSTGM_TRANSACTED
= ((stgm
& STGM_TRANSACTED
) == STGM_TRANSACTED
);
5969 BOOL bSTGM_SIMPLE
= ((stgm
& STGM_SIMPLE
) == STGM_SIMPLE
);
5970 BOOL bSTGM_DIRECT
= ! (bSTGM_TRANSACTED
|| bSTGM_SIMPLE
);
5972 BOOL bSTGM_WRITE
= ((stgm
& STGM_WRITE
) == STGM_WRITE
);
5973 BOOL bSTGM_READWRITE
= ((stgm
& STGM_READWRITE
) == STGM_READWRITE
);
5974 BOOL bSTGM_READ
= ! (bSTGM_WRITE
|| bSTGM_READWRITE
);
5976 BOOL bSTGM_SHARE_DENY_NONE
=
5977 ((stgm
& STGM_SHARE_DENY_NONE
) == STGM_SHARE_DENY_NONE
);
5979 BOOL bSTGM_SHARE_DENY_READ
=
5980 ((stgm
& STGM_SHARE_DENY_READ
) == STGM_SHARE_DENY_READ
);
5982 BOOL bSTGM_SHARE_DENY_WRITE
=
5983 ((stgm
& STGM_SHARE_DENY_WRITE
) == STGM_SHARE_DENY_WRITE
);
5985 BOOL bSTGM_SHARE_EXCLUSIVE
=
5986 ((stgm
& STGM_SHARE_EXCLUSIVE
) == STGM_SHARE_EXCLUSIVE
);
5988 BOOL bSTGM_CREATE
= ((stgm
& STGM_CREATE
) == STGM_CREATE
);
5989 BOOL bSTGM_CONVERT
= ((stgm
& STGM_CONVERT
) == STGM_CONVERT
);
5991 BOOL bSTGM_NOSCRATCH
= ((stgm
& STGM_NOSCRATCH
) == STGM_NOSCRATCH
);
5992 BOOL bSTGM_NOSNAPSHOT
= ((stgm
& STGM_NOSNAPSHOT
) == STGM_NOSNAPSHOT
);
5995 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5997 if ( ! bSTGM_DIRECT
)
5998 if( bSTGM_TRANSACTED
&& bSTGM_SIMPLE
)
6002 * STGM_WRITE | STGM_READWRITE | STGM_READ
6005 if( bSTGM_WRITE
&& bSTGM_READWRITE
)
6009 * STGM_SHARE_DENY_NONE | others
6010 * (I assume here that DENY_READ implies DENY_WRITE)
6012 if ( bSTGM_SHARE_DENY_NONE
)
6013 if ( bSTGM_SHARE_DENY_READ
||
6014 bSTGM_SHARE_DENY_WRITE
||
6015 bSTGM_SHARE_EXCLUSIVE
)
6019 * STGM_CREATE | STGM_CONVERT
6020 * if both are false, STGM_FAILIFTHERE is set to TRUE
6022 if ( bSTGM_CREATE
&& bSTGM_CONVERT
)
6026 * STGM_NOSCRATCH requires STGM_TRANSACTED
6028 if ( bSTGM_NOSCRATCH
&& ! bSTGM_TRANSACTED
)
6032 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6033 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6035 if (bSTGM_NOSNAPSHOT
)
6037 if ( ! ( bSTGM_TRANSACTED
&&
6038 !(bSTGM_SHARE_EXCLUSIVE
|| bSTGM_SHARE_DENY_WRITE
)) )
6045 /****************************************************************************
6046 * GetShareModeFromSTGM
6048 * This method will return a share mode flag from a STGM value.
6049 * The STGM value is assumed valid.
6051 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
6053 DWORD dwShareMode
= 0;
6054 BOOL bSTGM_SHARE_DENY_NONE
=
6055 ((stgm
& STGM_SHARE_DENY_NONE
) == STGM_SHARE_DENY_NONE
);
6057 BOOL bSTGM_SHARE_DENY_READ
=
6058 ((stgm
& STGM_SHARE_DENY_READ
) == STGM_SHARE_DENY_READ
);
6060 BOOL bSTGM_SHARE_DENY_WRITE
=
6061 ((stgm
& STGM_SHARE_DENY_WRITE
) == STGM_SHARE_DENY_WRITE
);
6063 BOOL bSTGM_SHARE_EXCLUSIVE
=
6064 ((stgm
& STGM_SHARE_EXCLUSIVE
) == STGM_SHARE_EXCLUSIVE
);
6066 if ((bSTGM_SHARE_EXCLUSIVE
) || (bSTGM_SHARE_DENY_READ
))
6069 if (bSTGM_SHARE_DENY_NONE
)
6070 dwShareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6072 if (bSTGM_SHARE_DENY_WRITE
)
6073 dwShareMode
= FILE_SHARE_READ
;
6078 /****************************************************************************
6079 * GetAccessModeFromSTGM
6081 * This method will return an access mode flag from a STGM value.
6082 * The STGM value is assumed valid.
6084 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
6086 DWORD dwDesiredAccess
= GENERIC_READ
;
6087 BOOL bSTGM_WRITE
= ((stgm
& STGM_WRITE
) == STGM_WRITE
);
6088 BOOL bSTGM_READWRITE
= ((stgm
& STGM_READWRITE
) == STGM_READWRITE
);
6089 BOOL bSTGM_READ
= ! (bSTGM_WRITE
|| bSTGM_READWRITE
);
6092 dwDesiredAccess
= GENERIC_READ
;
6095 dwDesiredAccess
|= GENERIC_WRITE
;
6097 if (bSTGM_READWRITE
)
6098 dwDesiredAccess
= GENERIC_READ
| GENERIC_WRITE
;
6100 return dwDesiredAccess
;
6103 /****************************************************************************
6104 * GetCreationModeFromSTGM
6106 * This method will return a creation mode flag from a STGM value.
6107 * The STGM value is assumed valid.
6109 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
6111 if ( stgm
& STGM_CREATE
)
6112 return CREATE_ALWAYS
;
6113 if (stgm
& STGM_CONVERT
) {
6114 FIXME("STGM_CONVERT not implemented!\n");
6117 /* All other cases */
6118 if (stgm
& ~ (STGM_CREATE
|STGM_CONVERT
))
6119 FIXME("unhandled storage mode : 0x%08lx\n",stgm
& ~ (STGM_CREATE
|STGM_CONVERT
));
6124 /*************************************************************************
6125 * OLECONVERT_LoadOLE10 [Internal]
6127 * Loads the OLE10 STREAM to memory
6130 * pOleStream [I] The OLESTREAM
6131 * pData [I] Data Structure for the OLESTREAM Data
6135 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6136 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6139 * This function is used by OleConvertOLESTREAMToIStorage only.
6141 * Memory allocated for pData must be freed by the caller
6143 HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
6146 HRESULT hRes
= S_OK
;
6150 pData
->pData
= NULL
;
6151 pData
->pstrOleObjFileName
= (CHAR
*) NULL
;
6153 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
6156 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6157 if(dwSize
!= sizeof(pData
->dwOleID
))
6159 hRes
= CONVERT10_E_OLESTREAM_GET
;
6161 else if(pData
->dwOleID
!= OLESTREAM_ID
)
6163 hRes
= CONVERT10_E_OLESTREAM_FMT
;
6174 /* Get the TypeID...more info needed for this field */
6175 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6176 if(dwSize
!= sizeof(pData
->dwTypeID
))
6178 hRes
= CONVERT10_E_OLESTREAM_GET
;
6183 if(pData
->dwTypeID
!= 0)
6185 /* Get the length of the OleTypeName */
6186 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6187 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6189 hRes
= CONVERT10_E_OLESTREAM_GET
;
6194 if(pData
->dwOleTypeNameLength
> 0)
6196 /* Get the OleTypeName */
6197 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6198 if(dwSize
!= pData
->dwOleTypeNameLength
)
6200 hRes
= CONVERT10_E_OLESTREAM_GET
;
6206 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
6207 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
6209 hRes
= CONVERT10_E_OLESTREAM_GET
;
6213 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
6214 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
6215 pData
->pstrOleObjFileName
= (CHAR
*)HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
6216 if(pData
->pstrOleObjFileName
)
6218 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)(pData
->pstrOleObjFileName
),pData
->dwOleObjFileNameLength
);
6219 if(dwSize
!= pData
->dwOleObjFileNameLength
)
6221 hRes
= CONVERT10_E_OLESTREAM_GET
;
6225 hRes
= CONVERT10_E_OLESTREAM_GET
;
6230 /* Get the Width of the Metafile */
6231 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6232 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6234 hRes
= CONVERT10_E_OLESTREAM_GET
;
6238 /* Get the Height of the Metafile */
6239 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6240 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6242 hRes
= CONVERT10_E_OLESTREAM_GET
;
6248 /* Get the Length of the Data */
6249 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6250 if(dwSize
!= sizeof(pData
->dwDataLength
))
6252 hRes
= CONVERT10_E_OLESTREAM_GET
;
6256 if(hRes
== S_OK
) /* I don't know what is this 8 byts information is we have to figure out */
6258 if(!bStrem1
) /* if it is a second OLE stream data */
6260 pData
->dwDataLength
-= 8;
6261 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)(pData
->strUnknown
), sizeof(pData
->strUnknown
));
6262 if(dwSize
!= sizeof(pData
->strUnknown
))
6264 hRes
= CONVERT10_E_OLESTREAM_GET
;
6270 if(pData
->dwDataLength
> 0)
6272 pData
->pData
= (BYTE
*)HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
6274 /* Get Data (ex. IStorage, Metafile, or BMP) */
6277 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
6278 if(dwSize
!= pData
->dwDataLength
)
6280 hRes
= CONVERT10_E_OLESTREAM_GET
;
6285 hRes
= CONVERT10_E_OLESTREAM_GET
;
6294 /*************************************************************************
6295 * OLECONVERT_SaveOLE10 [Internal]
6297 * Saves the OLE10 STREAM From memory
6300 * pData [I] Data Structure for the OLESTREAM Data
6301 * pOleStream [I] The OLESTREAM to save
6305 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6308 * This function is used by OleConvertIStorageToOLESTREAM only.
6311 HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
6314 HRESULT hRes
= S_OK
;
6318 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6319 if(dwSize
!= sizeof(pData
->dwOleID
))
6321 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6326 /* Set the TypeID */
6327 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6328 if(dwSize
!= sizeof(pData
->dwTypeID
))
6330 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6334 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
6336 /* Set the Length of the OleTypeName */
6337 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6338 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6340 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6345 if(pData
->dwOleTypeNameLength
> 0)
6347 /* Set the OleTypeName */
6348 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6349 if(dwSize
!= pData
->dwOleTypeNameLength
)
6351 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6358 /* Set the width of the Metafile */
6359 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6360 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6362 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6368 /* Set the height of the Metafile */
6369 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6370 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6372 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6378 /* Set the length of the Data */
6379 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6380 if(dwSize
!= sizeof(pData
->dwDataLength
))
6382 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6388 if(pData
->dwDataLength
> 0)
6390 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6391 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
6392 if(dwSize
!= pData
->dwDataLength
)
6394 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6402 /*************************************************************************
6403 * OLECONVERT_GetOLE20FromOLE10[Internal]
6405 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6406 * opens it, and copies the content to the dest IStorage for
6407 * OleConvertOLESTREAMToIStorage
6411 * pDestStorage [I] The IStorage to copy the data to
6412 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6413 * nBufferLength [I] The size of the buffer
6422 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, BYTE
*pBuffer
, DWORD nBufferLength
)
6426 IStorage
*pTempStorage
;
6427 DWORD dwNumOfBytesWritten
;
6428 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6429 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6431 /* Create a temp File */
6432 GetTempPathW(MAX_PATH
, wstrTempDir
);
6433 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6434 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
6436 if(hFile
!= INVALID_HANDLE_VALUE
)
6438 /* Write IStorage Data to File */
6439 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
6442 /* Open and copy temp storage to the Dest Storage */
6443 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
6446 hRes
= StorageImpl_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
6447 StorageBaseImpl_Release(pTempStorage
);
6449 DeleteFileW(wstrTempFile
);
6454 /*************************************************************************
6455 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6457 * Saves the OLE10 STREAM From memory
6460 * pStorage [I] The Src IStorage to copy
6461 * pData [I] The Dest Memory to write to.
6464 * The size in bytes allocated for pData
6467 * Memory allocated for pData must be freed by the caller
6469 * Used by OleConvertIStorageToOLESTREAM only.
6472 DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
6476 DWORD nDataLength
= 0;
6477 IStorage
*pTempStorage
;
6478 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6479 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6483 /* Create temp Storage */
6484 GetTempPathW(MAX_PATH
, wstrTempDir
);
6485 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6486 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
6490 /* Copy Src Storage to the Temp Storage */
6491 StorageImpl_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
6492 StorageBaseImpl_Release(pTempStorage
);
6494 /* Open Temp Storage as a file and copy to memory */
6495 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
6496 if(hFile
!= INVALID_HANDLE_VALUE
)
6498 nDataLength
= GetFileSize(hFile
, NULL
);
6499 *pData
= (BYTE
*) HeapAlloc(GetProcessHeap(),0,nDataLength
);
6500 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
6503 DeleteFileW(wstrTempFile
);
6508 /*************************************************************************
6509 * OLECONVERT_CreateOleStream [Internal]
6511 * Creates the "\001OLE" stream in the IStorage if necessary.
6514 * pStorage [I] Dest storage to create the stream in
6520 * This function is used by OleConvertOLESTREAMToIStorage only.
6522 * This stream is still unknown, MS Word seems to have extra data
6523 * but since the data is stored in the OLESTREAM there should be
6524 * no need to recreate the stream. If the stream is manually
6525 * deleted it will create it with this default data.
6528 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
6532 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
6533 BYTE pOleStreamHeader
[] =
6535 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6536 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6537 0x00, 0x00, 0x00, 0x00
6540 /* Create stream if not present */
6541 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
6542 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
6546 /* Write default Data */
6547 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
6548 IStream_Release(pStream
);
6552 /* write a string to a stream, preceded by its length */
6553 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
6560 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
6561 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
6566 str
= CoTaskMemAlloc( len
);
6567 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
6568 r
= IStream_Write( stm
, str
, len
, NULL
);
6569 CoTaskMemFree( str
);
6573 /* read a string preceded by its length from a stream */
6574 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
6577 DWORD len
, count
= 0;
6581 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
6584 if( count
!= sizeof(len
) )
6585 return E_OUTOFMEMORY
;
6587 TRACE("%ld bytes\n",len
);
6589 str
= CoTaskMemAlloc( len
);
6591 return E_OUTOFMEMORY
;
6593 r
= IStream_Read( stm
, str
, len
, &count
);
6598 CoTaskMemFree( str
);
6599 return E_OUTOFMEMORY
;
6602 TRACE("Read string %s\n",debugstr_an(str
,len
));
6604 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
6605 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
6607 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
6608 CoTaskMemFree( str
);
6616 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
6617 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
6621 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6623 static const BYTE unknown1
[12] =
6624 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6625 0xFF, 0xFF, 0xFF, 0xFF};
6626 static const BYTE unknown2
[16] =
6627 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6628 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6630 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
6631 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
6632 debugstr_w(szProgIDName
));
6634 /* Create a CompObj stream if it doesn't exist */
6635 r
= IStorage_CreateStream(pstg
, szwStreamName
,
6636 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
6640 /* Write CompObj Structure to stream */
6641 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
6643 if( SUCCEEDED( r
) )
6644 r
= WriteClassStm( pstm
, clsid
);
6646 if( SUCCEEDED( r
) )
6647 r
= STREAM_WriteString( pstm
, lpszUserType
);
6648 if( SUCCEEDED( r
) )
6649 r
= STREAM_WriteString( pstm
, szClipName
);
6650 if( SUCCEEDED( r
) )
6651 r
= STREAM_WriteString( pstm
, szProgIDName
);
6652 if( SUCCEEDED( r
) )
6653 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
6655 IStream_Release( pstm
);
6660 /* enumerate HKEY_CLASSES_ROOT\\CLSID looking for a CLSID whose name matches */
6661 static HRESULT
CLSIDFromUserType(LPCWSTR lpszUserType
, CLSID
*clsid
)
6666 HKEY hkey
, hkeyclsid
;
6667 LPWSTR buffer
= NULL
;
6669 static const WCHAR szclsid
[] = { 'C','L','S','I','D',0 };
6671 TRACE("Finding CLSID for %s\n", debugstr_w(lpszUserType
));
6673 r
= RegOpenKeyW( HKEY_CLASSES_ROOT
, szclsid
, &hkeyclsid
);
6675 return E_INVALIDARG
;
6677 len
= lstrlenW( lpszUserType
) + 1;
6678 buffer
= CoTaskMemAlloc( len
* sizeof (WCHAR
) );
6682 for(i
=0; !found
; i
++ )
6684 r
= RegEnumKeyW( hkeyclsid
, i
, szKey
, sizeof(szKey
)/sizeof(WCHAR
));
6685 if( r
!= ERROR_SUCCESS
)
6688 r
= RegOpenKeyW( hkeyclsid
, szKey
, &hkey
);
6689 if( r
!= ERROR_SUCCESS
)
6691 count
= len
* sizeof (WCHAR
);
6692 r
= RegQueryValueW( hkey
, NULL
, buffer
, &count
);
6693 found
= ( r
== ERROR_SUCCESS
) &&
6694 ( count
== len
*sizeof(WCHAR
) ) &&
6695 !lstrcmpW( buffer
, lpszUserType
) ;
6696 RegCloseKey( hkey
);
6701 CoTaskMemFree( buffer
);
6702 RegCloseKey( hkeyclsid
);
6705 return E_INVALIDARG
;
6707 TRACE("clsid is %s\n", debugstr_w( szKey
) );
6709 r
= CLSIDFromString( szKey
, clsid
);
6715 /***********************************************************************
6716 * WriteFmtUserTypeStg (OLE32.@)
6718 HRESULT WINAPI
WriteFmtUserTypeStg(
6719 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
6722 WCHAR szwClipName
[0x40];
6723 WCHAR szCLSIDName
[OLESTREAM_MAX_STR_LEN
];
6727 LPMALLOC allocator
= NULL
;
6729 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
6731 r
= CoGetMalloc(0, &allocator
);
6733 return E_OUTOFMEMORY
;
6735 /* get the clipboard format name */
6736 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
) );
6739 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
6743 r
= CLSIDFromUserType(lpszUserType
, &clsid
);
6747 TRACE("CLSID is %s\n",debugstr_guid(&clsid
));
6749 /* get the real program ID */
6750 r
= ProgIDFromCLSID( &clsid
, &wstrProgID
);
6754 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
6756 /* if we have a good string, write the stream */
6758 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
6759 lpszUserType
, szwClipName
, wstrProgID
);
6763 IMalloc_Free( allocator
, wstrProgID
);
6769 /******************************************************************************
6770 * ReadFmtUserTypeStg [OLE32.@]
6772 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
6776 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
6777 unsigned char unknown1
[12];
6778 unsigned char unknown2
[16];
6780 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
6783 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
6785 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
6786 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
6789 ERR("Failed to open stream\n");
6793 /* read the various parts of the structure */
6794 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
6795 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
6797 r
= ReadClassStm( stm
, &clsid
);
6801 r
= STREAM_ReadString( stm
, &szCLSIDName
);
6805 r
= STREAM_ReadString( stm
, &szOleTypeName
);
6809 r
= STREAM_ReadString( stm
, &szProgIDName
);
6813 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
6814 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
6817 /* ok, success... now we just need to store what we found */
6819 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
6820 CoTaskMemFree( szOleTypeName
);
6822 if( lplpszUserType
)
6823 *lplpszUserType
= szCLSIDName
;
6824 CoTaskMemFree( szProgIDName
);
6827 IStream_Release( stm
);
6833 /*************************************************************************
6834 * OLECONVERT_CreateCompObjStream [Internal]
6836 * Creates a "\001CompObj" is the destination IStorage if necessary.
6839 * pStorage [I] The dest IStorage to create the CompObj Stream
6841 * strOleTypeName [I] The ProgID
6845 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6848 * This function is used by OleConvertOLESTREAMToIStorage only.
6850 * The stream data is stored in the OLESTREAM and there should be
6851 * no need to recreate the stream. If the stream is manually
6852 * deleted it will attempt to create it by querying the registry.
6856 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
6859 HRESULT hStorageRes
, hRes
= S_OK
;
6860 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
6861 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6862 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
6864 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6865 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
6867 /* Initialize the CompObj structure */
6868 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
6869 memcpy(&(IStorageCompObj
.byUnknown1
), pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
6870 memcpy(&(IStorageCompObj
.byUnknown2
), pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
6873 /* Create a CompObj stream if it doesn't exist */
6874 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
6875 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
6876 if(hStorageRes
== S_OK
)
6878 /* copy the OleTypeName to the compobj struct */
6879 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
6880 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
6882 /* copy the OleTypeName to the compobj struct */
6883 /* Note: in the test made, these were Identical */
6884 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
6885 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
6888 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
6889 bufferW
, OLESTREAM_MAX_STR_LEN
);
6890 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
6896 /* Get the CLSID Default Name from the Registry */
6897 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
6898 if(hErr
== ERROR_SUCCESS
)
6900 char strTemp
[OLESTREAM_MAX_STR_LEN
];
6901 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
6902 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, &(IStorageCompObj
.dwCLSIDNameLength
));
6903 if(hErr
== ERROR_SUCCESS
)
6905 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
6911 /* Write CompObj Structure to stream */
6912 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
6914 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
6916 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
6917 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
6919 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
6921 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
6922 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
6924 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
6926 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
6927 if(IStorageCompObj
.dwProgIDNameLength
> 0)
6929 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
6931 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
6932 IStream_Release(pStream
);
6938 /*************************************************************************
6939 * OLECONVERT_CreateOlePresStream[Internal]
6941 * Creates the "\002OlePres000" Stream with the Metafile data
6944 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6945 * dwExtentX [I] Width of the Metafile
6946 * dwExtentY [I] Height of the Metafile
6947 * pData [I] Metafile data
6948 * dwDataLength [I] Size of the Metafile data
6952 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6955 * This function is used by OleConvertOLESTREAMToIStorage only.
6958 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
6962 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6963 BYTE pOlePresStreamHeader
[] =
6965 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6966 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6967 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6968 0x00, 0x00, 0x00, 0x00
6971 BYTE pOlePresStreamHeaderEmpty
[] =
6973 0x00, 0x00, 0x00, 0x00,
6974 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6975 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6976 0x00, 0x00, 0x00, 0x00
6979 /* Create the OlePres000 Stream */
6980 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
6981 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
6986 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
6988 memset(&OlePres
, 0, sizeof(OlePres
));
6989 /* Do we have any metafile data to save */
6990 if(dwDataLength
> 0)
6992 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
6993 nHeaderSize
= sizeof(pOlePresStreamHeader
);
6997 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
6998 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
7000 /* Set width and height of the metafile */
7001 OlePres
.dwExtentX
= dwExtentX
;
7002 OlePres
.dwExtentY
= -dwExtentY
;
7004 /* Set Data and Length */
7005 if(dwDataLength
> sizeof(METAFILEPICT16
))
7007 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
7008 OlePres
.pData
= &(pData
[8]);
7010 /* Save OlePres000 Data to Stream */
7011 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
7012 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
7013 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
7014 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
7015 if(OlePres
.dwSize
> 0)
7017 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
7019 IStream_Release(pStream
);
7023 /*************************************************************************
7024 * OLECONVERT_CreateOle10NativeStream [Internal]
7026 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7029 * pStorage [I] Dest storage to create the stream in
7030 * pData [I] Ole10 Native Data (ex. bmp)
7031 * dwDataLength [I] Size of the Ole10 Native Data
7037 * This function is used by OleConvertOLESTREAMToIStorage only.
7039 * Might need to verify the data and return appropriate error message
7042 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, BYTE
*pData
, DWORD dwDataLength
)
7046 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7048 /* Create the Ole10Native Stream */
7049 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7050 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7054 /* Write info to stream */
7055 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
7056 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
7057 IStream_Release(pStream
);
7062 /*************************************************************************
7063 * OLECONVERT_GetOLE10ProgID [Internal]
7065 * Finds the ProgID (or OleTypeID) from the IStorage
7068 * pStorage [I] The Src IStorage to get the ProgID
7069 * strProgID [I] the ProgID string to get
7070 * dwSize [I] the size of the string
7074 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7077 * This function is used by OleConvertIStorageToOLESTREAM only.
7081 HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
7085 LARGE_INTEGER iSeekPos
;
7086 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
7087 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7089 /* Open the CompObj Stream */
7090 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7091 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7095 /*Get the OleType from the CompObj Stream */
7096 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
7097 iSeekPos
.u
.HighPart
= 0;
7099 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7100 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
7101 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
7102 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7103 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
7104 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
7105 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7107 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
7110 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
7112 IStream_Release(pStream
);
7117 LPOLESTR wstrProgID
;
7119 /* Get the OleType from the registry */
7120 REFCLSID clsid
= &(stat
.clsid
);
7121 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
7122 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
7125 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
7132 /*************************************************************************
7133 * OLECONVERT_GetOle10PresData [Internal]
7135 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7138 * pStorage [I] Src IStroage
7139 * pOleStream [I] Dest OleStream Mem Struct
7145 * This function is used by OleConvertIStorageToOLESTREAM only.
7147 * Memory allocated for pData must be freed by the caller
7151 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7156 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7158 /* Initialize Default data for OLESTREAM */
7159 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7160 pOleStreamData
[0].dwTypeID
= 2;
7161 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7162 pOleStreamData
[1].dwTypeID
= 0;
7163 pOleStreamData
[0].dwMetaFileWidth
= 0;
7164 pOleStreamData
[0].dwMetaFileHeight
= 0;
7165 pOleStreamData
[0].pData
= NULL
;
7166 pOleStreamData
[1].pData
= NULL
;
7168 /* Open Ole10Native Stream */
7169 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7170 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7174 /* Read Size and Data */
7175 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
7176 if(pOleStreamData
->dwDataLength
> 0)
7178 pOleStreamData
->pData
= (LPSTR
) HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
7179 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
7181 IStream_Release(pStream
);
7187 /*************************************************************************
7188 * OLECONVERT_GetOle20PresData[Internal]
7190 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7193 * pStorage [I] Src IStroage
7194 * pOleStreamData [I] Dest OleStream Mem Struct
7200 * This function is used by OleConvertIStorageToOLESTREAM only.
7202 * Memory allocated for pData must be freed by the caller
7204 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7208 OLECONVERT_ISTORAGE_OLEPRES olePress
;
7209 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7211 /* Initialize Default data for OLESTREAM */
7212 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7213 pOleStreamData
[0].dwTypeID
= 2;
7214 pOleStreamData
[0].dwMetaFileWidth
= 0;
7215 pOleStreamData
[0].dwMetaFileHeight
= 0;
7216 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
7217 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7218 pOleStreamData
[1].dwTypeID
= 0;
7219 pOleStreamData
[1].dwOleTypeNameLength
= 0;
7220 pOleStreamData
[1].strOleTypeName
[0] = 0;
7221 pOleStreamData
[1].dwMetaFileWidth
= 0;
7222 pOleStreamData
[1].dwMetaFileHeight
= 0;
7223 pOleStreamData
[1].pData
= NULL
;
7224 pOleStreamData
[1].dwDataLength
= 0;
7227 /* Open OlePress000 stream */
7228 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7229 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7232 LARGE_INTEGER iSeekPos
;
7233 METAFILEPICT16 MetaFilePict
;
7234 static const char strMetafilePictName
[] = "METAFILEPICT";
7236 /* Set the TypeID for a Metafile */
7237 pOleStreamData
[1].dwTypeID
= 5;
7239 /* Set the OleTypeName to Metafile */
7240 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
7241 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
7243 iSeekPos
.u
.HighPart
= 0;
7244 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
7246 /* Get Presentation Data */
7247 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7248 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
7249 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
7250 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
7252 /*Set width and Height */
7253 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
7254 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
7255 if(olePress
.dwSize
> 0)
7258 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
7260 /* Set MetaFilePict struct */
7261 MetaFilePict
.mm
= 8;
7262 MetaFilePict
.xExt
= olePress
.dwExtentX
;
7263 MetaFilePict
.yExt
= olePress
.dwExtentY
;
7264 MetaFilePict
.hMF
= 0;
7266 /* Get Metafile Data */
7267 pOleStreamData
[1].pData
= (BYTE
*) HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
7268 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
7269 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
7271 IStream_Release(pStream
);
7275 /*************************************************************************
7276 * OleConvertOLESTREAMToIStorage [OLE32.@]
7281 * DVTARGETDEVICE paramenter is not handled
7282 * Still unsure of some mem fields for OLE 10 Stream
7283 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7284 * and "\001OLE" streams
7287 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
7288 LPOLESTREAM pOleStream
,
7290 const DVTARGETDEVICE
* ptd
)
7294 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7296 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7300 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7303 if(pstg
== NULL
|| pOleStream
== NULL
)
7305 hRes
= E_INVALIDARG
;
7310 /* Load the OLESTREAM to Memory */
7311 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
7316 /* Load the OLESTREAM to Memory (part 2)*/
7317 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
7323 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
7325 /* Do we have the IStorage Data in the OLESTREAM */
7326 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
7328 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7329 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
7333 /* It must be an original OLE 1.0 source */
7334 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7339 /* It must be an original OLE 1.0 source */
7340 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7343 /* Create CompObj Stream if necessary */
7344 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
7347 /*Create the Ole Stream if necessary */
7348 OLECONVERT_CreateOleStream(pstg
);
7353 /* Free allocated memory */
7354 for(i
=0; i
< 2; i
++)
7356 if(pOleStreamData
[i
].pData
!= NULL
)
7358 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7360 if(pOleStreamData
[i
].pstrOleObjFileName
!= NULL
)
7362 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
7363 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
7369 /*************************************************************************
7370 * OleConvertIStorageToOLESTREAM [OLE32.@]
7377 * Still unsure of some mem fields for OLE 10 Stream
7378 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7379 * and "\001OLE" streams.
7382 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
7384 LPOLESTREAM pOleStream
)
7387 HRESULT hRes
= S_OK
;
7389 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7390 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7393 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7395 if(pstg
== NULL
|| pOleStream
== NULL
)
7397 hRes
= E_INVALIDARG
;
7401 /* Get the ProgID */
7402 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
7403 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
7407 /* Was it originally Ole10 */
7408 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7411 IStream_Release(pStream
);
7412 /* Get Presentation Data for Ole10Native */
7413 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
7417 /* Get Presentation Data (OLE20) */
7418 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
7421 /* Save OLESTREAM */
7422 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
7425 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
7430 /* Free allocated memory */
7431 for(i
=0; i
< 2; i
++)
7433 if(pOleStreamData
[i
].pData
!= NULL
)
7435 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7442 /***********************************************************************
7443 * GetConvertStg (OLE32.@)
7445 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
7446 FIXME("unimplemented stub!\n");
7450 /******************************************************************************
7451 * StgIsStorageFile [OLE32.@]
7454 StgIsStorageFile(LPCOLESTR fn
)
7460 TRACE("(\'%s\')\n", debugstr_w(fn
));
7461 hf
= CreateFileW(fn
, GENERIC_READ
,
7462 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
7463 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
7465 if (hf
== INVALID_HANDLE_VALUE
)
7466 return STG_E_FILENOTFOUND
;
7468 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
7470 WARN(" unable to read file\n");
7477 if (bytes_read
!= 8) {
7478 WARN(" too short\n");
7482 if (!memcmp(magic
,STORAGE_magic
,8)) {
7487 WARN(" -> Invalid header.\n");