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 ICOM_VTABLE(IStorage
) Storage32Impl_Vtbl
=
163 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
164 StorageBaseImpl_QueryInterface
,
165 StorageBaseImpl_AddRef
,
166 StorageBaseImpl_Release
,
167 StorageBaseImpl_CreateStream
,
168 StorageBaseImpl_OpenStream
,
169 StorageImpl_CreateStorage
,
170 StorageBaseImpl_OpenStorage
,
172 StorageImpl_MoveElementTo
,
175 StorageBaseImpl_EnumElements
,
176 StorageImpl_DestroyElement
,
177 StorageBaseImpl_RenameElement
,
178 StorageImpl_SetElementTimes
,
179 StorageBaseImpl_SetClass
,
180 StorageImpl_SetStateBits
,
185 * Virtual function table for the Storage32InternalImpl class.
187 static ICOM_VTABLE(IStorage
) Storage32InternalImpl_Vtbl
=
189 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
190 StorageBaseImpl_QueryInterface
,
191 StorageBaseImpl_AddRef
,
192 StorageBaseImpl_Release
,
193 StorageBaseImpl_CreateStream
,
194 StorageBaseImpl_OpenStream
,
195 StorageImpl_CreateStorage
,
196 StorageBaseImpl_OpenStorage
,
198 StorageImpl_MoveElementTo
,
199 StorageInternalImpl_Commit
,
200 StorageInternalImpl_Revert
,
201 StorageBaseImpl_EnumElements
,
202 StorageImpl_DestroyElement
,
203 StorageBaseImpl_RenameElement
,
204 StorageImpl_SetElementTimes
,
205 StorageBaseImpl_SetClass
,
206 StorageImpl_SetStateBits
,
211 * Virtual function table for the IEnumSTATSTGImpl class.
213 static ICOM_VTABLE(IEnumSTATSTG
) IEnumSTATSTGImpl_Vtbl
=
215 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
216 IEnumSTATSTGImpl_QueryInterface
,
217 IEnumSTATSTGImpl_AddRef
,
218 IEnumSTATSTGImpl_Release
,
219 IEnumSTATSTGImpl_Next
,
220 IEnumSTATSTGImpl_Skip
,
221 IEnumSTATSTGImpl_Reset
,
222 IEnumSTATSTGImpl_Clone
229 /************************************************************************
230 ** Storage32BaseImpl implementatiion
233 /************************************************************************
234 * Storage32BaseImpl_QueryInterface (IUnknown)
236 * This method implements the common QueryInterface for all IStorage32
237 * implementations contained in this file.
239 * See Windows documentation for more details on IUnknown methods.
241 HRESULT WINAPI
StorageBaseImpl_QueryInterface(
246 ICOM_THIS(StorageBaseImpl
,iface
);
248 * Perform a sanity check on the parameters.
250 if ( (This
==0) || (ppvObject
==0) )
254 * Initialize the return parameter.
259 * Compare the riid with the interface IDs implemented by this object.
261 if (memcmp(&IID_IUnknown
, riid
, sizeof(IID_IUnknown
)) == 0)
263 *ppvObject
= (IStorage
*)This
;
265 else if (memcmp(&IID_IStorage
, riid
, sizeof(IID_IStorage
)) == 0)
267 *ppvObject
= (IStorage
*)This
;
271 * Check that we obtained an interface.
274 return E_NOINTERFACE
;
277 * Query Interface always increases the reference count by one when it is
280 StorageBaseImpl_AddRef(iface
);
285 /************************************************************************
286 * Storage32BaseImpl_AddRef (IUnknown)
288 * This method implements the common AddRef for all IStorage32
289 * implementations contained in this file.
291 * See Windows documentation for more details on IUnknown methods.
293 ULONG WINAPI
StorageBaseImpl_AddRef(
296 ICOM_THIS(StorageBaseImpl
,iface
);
302 /************************************************************************
303 * Storage32BaseImpl_Release (IUnknown)
305 * This method implements the common Release for all IStorage32
306 * implementations contained in this file.
308 * See Windows documentation for more details on IUnknown methods.
310 ULONG WINAPI
StorageBaseImpl_Release(
313 ICOM_THIS(StorageBaseImpl
,iface
);
315 * Decrease the reference count on this object.
320 * If the reference count goes down to 0, perform suicide.
325 * Since we are using a system of base-classes, we want to call the
326 * destructor of the appropriate derived class. To do this, we are
327 * using virtual functions to implement the destructor.
329 This
->v_destructor(This
);
337 /************************************************************************
338 * Storage32BaseImpl_OpenStream (IStorage)
340 * This method will open the specified stream object from the current storage.
342 * See Windows documentation for more details on IStorage methods.
344 HRESULT WINAPI
StorageBaseImpl_OpenStream(
346 const OLECHAR
* pwcsName
, /* [string][in] */
347 void* reserved1
, /* [unique][in] */
348 DWORD grfMode
, /* [in] */
349 DWORD reserved2
, /* [in] */
350 IStream
** ppstm
) /* [out] */
352 ICOM_THIS(StorageBaseImpl
,iface
);
353 IEnumSTATSTGImpl
* propertyEnumeration
;
354 StgStreamImpl
* newStream
;
355 StgProperty currentProperty
;
356 ULONG foundPropertyIndex
;
357 HRESULT res
= STG_E_UNKNOWN
;
359 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
360 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
363 * Perform a sanity check on the parameters.
365 if ( (pwcsName
==NULL
) || (ppstm
==0) )
372 * Initialize the out parameter
377 * Validate the STGM flags
379 if ( FAILED( validateSTGM(grfMode
) ))
381 res
= STG_E_INVALIDFLAG
;
388 if ( !(grfMode
& STGM_SHARE_EXCLUSIVE
) ||
389 (grfMode
& STGM_DELETEONRELEASE
) ||
390 (grfMode
& STGM_TRANSACTED
) )
392 res
= STG_E_INVALIDFUNCTION
;
397 * Create a property enumeration to search the properties
399 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
400 This
->ancestorStorage
,
401 This
->rootPropertySetIndex
);
404 * Search the enumeration for the property with the given name
406 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
412 * Delete the property enumeration since we don't need it anymore
414 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
417 * If it was found, construct the stream object and return a pointer to it.
419 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
420 (currentProperty
.propertyType
==PROPTYPE_STREAM
) )
422 newStream
= StgStreamImpl_Construct(This
, grfMode
, foundPropertyIndex
);
426 newStream
->grfMode
= grfMode
;
427 *ppstm
= (IStream
*)newStream
;
430 * Since we are returning a pointer to the interface, we have to
431 * nail down the reference.
433 StgStreamImpl_AddRef(*ppstm
);
443 res
= STG_E_FILENOTFOUND
;
447 TRACE("<-- IStream %p\n", *ppstm
);
448 TRACE("<-- %08lx\n", res
);
452 /************************************************************************
453 * Storage32BaseImpl_OpenStorage (IStorage)
455 * This method will open a new storage object from the current storage.
457 * See Windows documentation for more details on IStorage methods.
459 HRESULT WINAPI
StorageBaseImpl_OpenStorage(
461 const OLECHAR
* pwcsName
, /* [string][unique][in] */
462 IStorage
* pstgPriority
, /* [unique][in] */
463 DWORD grfMode
, /* [in] */
464 SNB snbExclude
, /* [unique][in] */
465 DWORD reserved
, /* [in] */
466 IStorage
** ppstg
) /* [out] */
468 ICOM_THIS(StorageBaseImpl
,iface
);
469 StorageInternalImpl
* newStorage
;
470 IEnumSTATSTGImpl
* propertyEnumeration
;
471 StgProperty currentProperty
;
472 ULONG foundPropertyIndex
;
473 HRESULT res
= STG_E_UNKNOWN
;
475 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
476 iface
, debugstr_w(pwcsName
), pstgPriority
,
477 grfMode
, snbExclude
, reserved
, ppstg
);
480 * Perform a sanity check on the parameters.
482 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
489 if (snbExclude
!= NULL
)
491 res
= STG_E_INVALIDPARAMETER
;
496 * Validate the STGM flags
498 if ( FAILED( validateSTGM(grfMode
) ))
500 res
= STG_E_INVALIDFLAG
;
507 if ( !(grfMode
& STGM_SHARE_EXCLUSIVE
) ||
508 (grfMode
& STGM_DELETEONRELEASE
) ||
509 (grfMode
& STGM_PRIORITY
) )
511 res
= STG_E_INVALIDFUNCTION
;
516 * Initialize the out parameter
521 * Create a property enumeration to search the properties
523 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
524 This
->ancestorStorage
,
525 This
->rootPropertySetIndex
);
528 * Search the enumeration for the property with the given name
530 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
536 * Delete the property enumeration since we don't need it anymore
538 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
541 * If it was found, construct the stream object and return a pointer to it.
543 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
544 (currentProperty
.propertyType
==PROPTYPE_STORAGE
) )
547 * Construct a new Storage object
549 newStorage
= StorageInternalImpl_Construct(
550 This
->ancestorStorage
,
555 *ppstg
= (IStorage
*)newStorage
;
558 * Since we are returning a pointer to the interface,
559 * we have to nail down the reference.
561 StorageBaseImpl_AddRef(*ppstg
);
567 res
= STG_E_INSUFFICIENTMEMORY
;
571 res
= STG_E_FILENOTFOUND
;
574 TRACE("<-- %08lx\n", res
);
578 /************************************************************************
579 * Storage32BaseImpl_EnumElements (IStorage)
581 * This method will create an enumerator object that can be used to
582 * retrieve informatino about all the properties in the storage object.
584 * See Windows documentation for more details on IStorage methods.
586 HRESULT WINAPI
StorageBaseImpl_EnumElements(
588 DWORD reserved1
, /* [in] */
589 void* reserved2
, /* [size_is][unique][in] */
590 DWORD reserved3
, /* [in] */
591 IEnumSTATSTG
** ppenum
) /* [out] */
593 ICOM_THIS(StorageBaseImpl
,iface
);
594 IEnumSTATSTGImpl
* newEnum
;
596 TRACE("(%p, %ld, %p, %ld, %p)\n",
597 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
600 * Perform a sanity check on the parameters.
602 if ( (This
==0) || (ppenum
==0))
606 * Construct the enumerator.
608 newEnum
= IEnumSTATSTGImpl_Construct(
609 This
->ancestorStorage
,
610 This
->rootPropertySetIndex
);
614 *ppenum
= (IEnumSTATSTG
*)newEnum
;
617 * Don't forget to nail down a reference to the new object before
620 IEnumSTATSTGImpl_AddRef(*ppenum
);
625 return E_OUTOFMEMORY
;
628 /************************************************************************
629 * Storage32BaseImpl_Stat (IStorage)
631 * This method will retrieve information about this storage object.
633 * See Windows documentation for more details on IStorage methods.
635 HRESULT WINAPI
StorageBaseImpl_Stat(
637 STATSTG
* pstatstg
, /* [out] */
638 DWORD grfStatFlag
) /* [in] */
640 ICOM_THIS(StorageBaseImpl
,iface
);
641 StgProperty curProperty
;
643 HRESULT res
= STG_E_UNKNOWN
;
645 TRACE("(%p, %p, %lx)\n",
646 iface
, pstatstg
, grfStatFlag
);
649 * Perform a sanity check on the parameters.
651 if ( (This
==0) || (pstatstg
==0))
658 * Read the information from the property.
660 readSuccessful
= StorageImpl_ReadProperty(
661 This
->ancestorStorage
,
662 This
->rootPropertySetIndex
,
667 StorageUtl_CopyPropertyToSTATSTG(
681 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
);
683 TRACE("<-- %08lx\n", res
);
687 /************************************************************************
688 * Storage32BaseImpl_RenameElement (IStorage)
690 * This method will rename the specified element.
692 * See Windows documentation for more details on IStorage methods.
694 * Implementation notes: The method used to rename consists of creating a clone
695 * of the deleted StgProperty object setting it with the new name and to
696 * perform a DestroyElement of the old StgProperty.
698 HRESULT WINAPI
StorageBaseImpl_RenameElement(
700 const OLECHAR
* pwcsOldName
, /* [in] */
701 const OLECHAR
* pwcsNewName
) /* [in] */
703 ICOM_THIS(StorageBaseImpl
,iface
);
704 IEnumSTATSTGImpl
* propertyEnumeration
;
705 StgProperty currentProperty
;
706 ULONG foundPropertyIndex
;
708 TRACE("(%p, %s, %s)\n",
709 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
712 * Create a property enumeration to search the properties
714 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
715 This
->rootPropertySetIndex
);
718 * Search the enumeration for the new property name
720 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
724 if (foundPropertyIndex
!= PROPERTY_NULL
)
727 * There is already a property with the new name
729 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
730 return STG_E_FILEALREADYEXISTS
;
733 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)propertyEnumeration
);
736 * Search the enumeration for the old property name
738 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
743 * Delete the property enumeration since we don't need it anymore
745 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
747 if (foundPropertyIndex
!= PROPERTY_NULL
)
749 StgProperty renamedProperty
;
750 ULONG renamedPropertyIndex
;
753 * Setup a new property for the renamed property
755 renamedProperty
.sizeOfNameString
=
756 ( lstrlenW(pwcsNewName
)+1 ) * sizeof(WCHAR
);
758 if (renamedProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
759 return STG_E_INVALIDNAME
;
761 strcpyW(renamedProperty
.name
, pwcsNewName
);
763 renamedProperty
.propertyType
= currentProperty
.propertyType
;
764 renamedProperty
.startingBlock
= currentProperty
.startingBlock
;
765 renamedProperty
.size
.u
.LowPart
= currentProperty
.size
.u
.LowPart
;
766 renamedProperty
.size
.u
.HighPart
= currentProperty
.size
.u
.HighPart
;
768 renamedProperty
.previousProperty
= PROPERTY_NULL
;
769 renamedProperty
.nextProperty
= PROPERTY_NULL
;
772 * Bring the dirProperty link in case it is a storage and in which
773 * case the renamed storage elements don't require to be reorganized.
775 renamedProperty
.dirProperty
= currentProperty
.dirProperty
;
777 /* call CoFileTime to get the current time
778 renamedProperty.timeStampS1
779 renamedProperty.timeStampD1
780 renamedProperty.timeStampS2
781 renamedProperty.timeStampD2
782 renamedProperty.propertyUniqueID
786 * Obtain a free property in the property chain
788 renamedPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
791 * Save the new property into the new property spot
793 StorageImpl_WriteProperty(
794 This
->ancestorStorage
,
795 renamedPropertyIndex
,
799 * Find a spot in the property chain for our newly created property.
803 renamedPropertyIndex
,
807 * At this point the renamed property has been inserted in the tree,
808 * now, before to Destroy the old property we must zeroed it's dirProperty
809 * otherwise the DestroyProperty below will zap it all and we do not want
811 * Also, we fake that the old property is a storage so the DestroyProperty
812 * will not do a SetSize(0) on the stream data.
814 * This means that we need to tweek the StgProperty if it is a stream or a
817 StorageImpl_ReadProperty(This
->ancestorStorage
,
821 currentProperty
.dirProperty
= PROPERTY_NULL
;
822 currentProperty
.propertyType
= PROPTYPE_STORAGE
;
823 StorageImpl_WriteProperty(
824 This
->ancestorStorage
,
829 * Invoke Destroy to get rid of the ole property and automatically redo
830 * the linking of it's previous and next members...
832 StorageImpl_DestroyElement((IStorage
*)This
->ancestorStorage
, pwcsOldName
);
838 * There is no property with the old name
840 return STG_E_FILENOTFOUND
;
846 /************************************************************************
847 * Storage32BaseImpl_CreateStream (IStorage)
849 * This method will create a stream object within this storage
851 * See Windows documentation for more details on IStorage methods.
853 HRESULT WINAPI
StorageBaseImpl_CreateStream(
855 const OLECHAR
* pwcsName
, /* [string][in] */
856 DWORD grfMode
, /* [in] */
857 DWORD reserved1
, /* [in] */
858 DWORD reserved2
, /* [in] */
859 IStream
** ppstm
) /* [out] */
861 ICOM_THIS(StorageBaseImpl
,iface
);
862 IEnumSTATSTGImpl
* propertyEnumeration
;
863 StgStreamImpl
* newStream
;
864 StgProperty currentProperty
, newStreamProperty
;
865 ULONG foundPropertyIndex
, newPropertyIndex
;
867 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
868 iface
, debugstr_w(pwcsName
), grfMode
,
869 reserved1
, reserved2
, ppstm
);
872 * Validate parameters
875 return STG_E_INVALIDPOINTER
;
878 return STG_E_INVALIDNAME
;
881 * Validate the STGM flags
883 if ( FAILED( validateSTGM(grfMode
) ))
884 return STG_E_INVALIDFLAG
;
889 if ( !(grfMode
& STGM_SHARE_EXCLUSIVE
) ||
890 (grfMode
& STGM_DELETEONRELEASE
) ||
891 (grfMode
& STGM_TRANSACTED
) )
892 return STG_E_INVALIDFUNCTION
;
895 * Initialize the out parameter
900 * Create a property enumeration to search the properties
902 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
903 This
->rootPropertySetIndex
);
905 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
909 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
911 if (foundPropertyIndex
!= PROPERTY_NULL
)
914 * An element with this name already exists
916 if (grfMode
& STGM_CREATE
)
918 IStorage_DestroyElement(iface
, pwcsName
);
921 return STG_E_FILEALREADYEXISTS
;
925 * memset the empty property
927 memset(&newStreamProperty
, 0, sizeof(StgProperty
));
929 newStreamProperty
.sizeOfNameString
=
930 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
932 if (newStreamProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
933 return STG_E_INVALIDNAME
;
935 strcpyW(newStreamProperty
.name
, pwcsName
);
937 newStreamProperty
.propertyType
= PROPTYPE_STREAM
;
938 newStreamProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
939 newStreamProperty
.size
.u
.LowPart
= 0;
940 newStreamProperty
.size
.u
.HighPart
= 0;
942 newStreamProperty
.previousProperty
= PROPERTY_NULL
;
943 newStreamProperty
.nextProperty
= PROPERTY_NULL
;
944 newStreamProperty
.dirProperty
= PROPERTY_NULL
;
946 /* call CoFileTime to get the current time
947 newStreamProperty.timeStampS1
948 newStreamProperty.timeStampD1
949 newStreamProperty.timeStampS2
950 newStreamProperty.timeStampD2
953 /* newStreamProperty.propertyUniqueID */
956 * Get a free property or create a new one
958 newPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
961 * Save the new property into the new property spot
963 StorageImpl_WriteProperty(
964 This
->ancestorStorage
,
969 * Find a spot in the property chain for our newly created property.
977 * Open the stream to return it.
979 newStream
= StgStreamImpl_Construct(This
, grfMode
, newPropertyIndex
);
983 *ppstm
= (IStream
*)newStream
;
986 * Since we are returning a pointer to the interface, we have to nail down
989 StgStreamImpl_AddRef(*ppstm
);
993 return STG_E_INSUFFICIENTMEMORY
;
999 /************************************************************************
1000 * Storage32BaseImpl_SetClass (IStorage)
1002 * This method will write the specified CLSID in the property of this
1005 * See Windows documentation for more details on IStorage methods.
1007 HRESULT WINAPI
StorageBaseImpl_SetClass(
1009 REFCLSID clsid
) /* [in] */
1011 ICOM_THIS(StorageBaseImpl
,iface
);
1012 HRESULT hRes
= E_FAIL
;
1013 StgProperty curProperty
;
1016 TRACE("(%p, %p)\n", iface
, clsid
);
1018 success
= StorageImpl_ReadProperty(This
->ancestorStorage
,
1019 This
->rootPropertySetIndex
,
1023 curProperty
.propertyUniqueID
= *clsid
;
1025 success
= StorageImpl_WriteProperty(This
->ancestorStorage
,
1026 This
->rootPropertySetIndex
,
1035 /************************************************************************
1036 ** Storage32Impl implementation
1039 /************************************************************************
1040 * Storage32Impl_CreateStorage (IStorage)
1042 * This method will create the storage object within the provided storage.
1044 * See Windows documentation for more details on IStorage methods.
1046 HRESULT WINAPI
StorageImpl_CreateStorage(
1048 const OLECHAR
*pwcsName
, /* [string][in] */
1049 DWORD grfMode
, /* [in] */
1050 DWORD reserved1
, /* [in] */
1051 DWORD reserved2
, /* [in] */
1052 IStorage
**ppstg
) /* [out] */
1054 StorageImpl
* const This
=(StorageImpl
*)iface
;
1056 IEnumSTATSTGImpl
*propertyEnumeration
;
1057 StgProperty currentProperty
;
1058 StgProperty newProperty
;
1059 ULONG foundPropertyIndex
;
1060 ULONG newPropertyIndex
;
1063 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1064 iface
, debugstr_w(pwcsName
), grfMode
,
1065 reserved1
, reserved2
, ppstg
);
1068 * Validate parameters
1071 return STG_E_INVALIDPOINTER
;
1074 return STG_E_INVALIDNAME
;
1077 * Validate the STGM flags
1079 if ( FAILED( validateSTGM(grfMode
) ) ||
1080 (grfMode
& STGM_DELETEONRELEASE
) )
1081 return STG_E_INVALIDFLAG
;
1084 * Initialize the out parameter
1089 * Create a property enumeration and search the properties
1091 propertyEnumeration
= IEnumSTATSTGImpl_Construct( This
->ancestorStorage
,
1092 This
->rootPropertySetIndex
);
1094 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
1097 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1099 if (foundPropertyIndex
!= PROPERTY_NULL
)
1102 * An element with this name already exists
1104 if (grfMode
& STGM_CREATE
)
1105 IStorage_DestroyElement(iface
, pwcsName
);
1107 return STG_E_FILEALREADYEXISTS
;
1111 * memset the empty property
1113 memset(&newProperty
, 0, sizeof(StgProperty
));
1115 newProperty
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1117 if (newProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
1118 return STG_E_INVALIDNAME
;
1120 strcpyW(newProperty
.name
, pwcsName
);
1122 newProperty
.propertyType
= PROPTYPE_STORAGE
;
1123 newProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
1124 newProperty
.size
.u
.LowPart
= 0;
1125 newProperty
.size
.u
.HighPart
= 0;
1127 newProperty
.previousProperty
= PROPERTY_NULL
;
1128 newProperty
.nextProperty
= PROPERTY_NULL
;
1129 newProperty
.dirProperty
= PROPERTY_NULL
;
1131 /* call CoFileTime to get the current time
1132 newProperty.timeStampS1
1133 newProperty.timeStampD1
1134 newProperty.timeStampS2
1135 newProperty.timeStampD2
1138 /* newStorageProperty.propertyUniqueID */
1141 * Obtain a free property in the property chain
1143 newPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
1146 * Save the new property into the new property spot
1148 StorageImpl_WriteProperty(
1149 This
->ancestorStorage
,
1154 * Find a spot in the property chain for our newly created property.
1156 updatePropertyChain(
1162 * Open it to get a pointer to return.
1164 hr
= IStorage_OpenStorage(
1173 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1183 /***************************************************************************
1187 * Get a free property or create a new one.
1189 static ULONG
getFreeProperty(
1190 StorageImpl
*storage
)
1192 ULONG currentPropertyIndex
= 0;
1193 ULONG newPropertyIndex
= PROPERTY_NULL
;
1194 BOOL readSuccessful
= TRUE
;
1195 StgProperty currentProperty
;
1200 * Start by reading the root property
1202 readSuccessful
= StorageImpl_ReadProperty(storage
->ancestorStorage
,
1203 currentPropertyIndex
,
1207 if (currentProperty
.sizeOfNameString
== 0)
1210 * The property existis and is available, we found it.
1212 newPropertyIndex
= currentPropertyIndex
;
1218 * We exhausted the property list, we will create more space below
1220 newPropertyIndex
= currentPropertyIndex
;
1222 currentPropertyIndex
++;
1224 } while (newPropertyIndex
== PROPERTY_NULL
);
1227 * grow the property chain
1229 if (! readSuccessful
)
1231 StgProperty emptyProperty
;
1232 ULARGE_INTEGER newSize
;
1233 ULONG propertyIndex
;
1234 ULONG lastProperty
= 0;
1235 ULONG blockCount
= 0;
1238 * obtain the new count of property blocks
1240 blockCount
= BlockChainStream_GetCount(
1241 storage
->ancestorStorage
->rootBlockChain
)+1;
1244 * initialize the size used by the property stream
1246 newSize
.u
.HighPart
= 0;
1247 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1250 * add a property block to the property chain
1252 BlockChainStream_SetSize(storage
->ancestorStorage
->rootBlockChain
, newSize
);
1255 * memset the empty property in order to initialize the unused newly
1258 memset(&emptyProperty
, 0, sizeof(StgProperty
));
1263 lastProperty
= storage
->bigBlockSize
/ PROPSET_BLOCK_SIZE
* blockCount
;
1266 propertyIndex
= newPropertyIndex
;
1267 propertyIndex
< lastProperty
;
1270 StorageImpl_WriteProperty(
1271 storage
->ancestorStorage
,
1277 return newPropertyIndex
;
1280 /****************************************************************************
1284 * Case insensitive comparaison of StgProperty.name by first considering
1287 * Returns <0 when newPrpoerty < currentProperty
1288 * >0 when newPrpoerty > currentProperty
1289 * 0 when newPrpoerty == currentProperty
1291 static LONG
propertyNameCmp(
1292 OLECHAR
*newProperty
,
1293 OLECHAR
*currentProperty
)
1295 LONG diff
= lstrlenW(newProperty
) - lstrlenW(currentProperty
);
1300 * We compare the string themselves only when they are of the same length
1302 diff
= lstrcmpiW( newProperty
, currentProperty
);
1308 /****************************************************************************
1312 * Properly link this new element in the property chain.
1314 static void updatePropertyChain(
1315 StorageImpl
*storage
,
1316 ULONG newPropertyIndex
,
1317 StgProperty newProperty
)
1319 StgProperty currentProperty
;
1322 * Read the root property
1324 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1325 storage
->rootPropertySetIndex
,
1328 if (currentProperty
.dirProperty
!= PROPERTY_NULL
)
1331 * The root storage contains some element, therefore, start the research
1332 * for the appropriate location.
1335 ULONG current
, next
, previous
, currentPropertyId
;
1338 * Keep the StgProperty sequence number of the storage first property
1340 currentPropertyId
= currentProperty
.dirProperty
;
1345 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1346 currentProperty
.dirProperty
,
1349 previous
= currentProperty
.previousProperty
;
1350 next
= currentProperty
.nextProperty
;
1351 current
= currentPropertyId
;
1355 LONG diff
= propertyNameCmp( newProperty
.name
, currentProperty
.name
);
1359 if (previous
!= PROPERTY_NULL
)
1361 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1368 currentProperty
.previousProperty
= newPropertyIndex
;
1369 StorageImpl_WriteProperty(storage
->ancestorStorage
,
1377 if (next
!= PROPERTY_NULL
)
1379 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1386 currentProperty
.nextProperty
= newPropertyIndex
;
1387 StorageImpl_WriteProperty(storage
->ancestorStorage
,
1396 * Trying to insert an item with the same name in the
1397 * subtree structure.
1402 previous
= currentProperty
.previousProperty
;
1403 next
= currentProperty
.nextProperty
;
1409 * The root storage is empty, link the new property to it's dir property
1411 currentProperty
.dirProperty
= newPropertyIndex
;
1412 StorageImpl_WriteProperty(storage
->ancestorStorage
,
1413 storage
->rootPropertySetIndex
,
1419 /*************************************************************************
1422 HRESULT WINAPI
StorageImpl_CopyTo(
1424 DWORD ciidExclude
, /* [in] */
1425 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1426 SNB snbExclude
, /* [unique][in] */
1427 IStorage
* pstgDest
) /* [unique][in] */
1429 IEnumSTATSTG
*elements
= 0;
1430 STATSTG curElement
, strStat
;
1432 IStorage
*pstgTmp
, *pstgChild
;
1433 IStream
*pstrTmp
, *pstrChild
;
1435 if ((ciidExclude
!= 0) || (rgiidExclude
!= NULL
) || (snbExclude
!= NULL
))
1436 FIXME("Exclude option not implemented\n");
1438 TRACE("(%p, %ld, %p, %p, %p)\n",
1439 iface
, ciidExclude
, rgiidExclude
,
1440 snbExclude
, pstgDest
);
1443 * Perform a sanity check
1445 if ( pstgDest
== 0 )
1446 return STG_E_INVALIDPOINTER
;
1449 * Enumerate the elements
1451 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1459 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1460 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1465 * Obtain the next element
1467 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1469 if ( hr
== S_FALSE
)
1471 hr
= S_OK
; /* done, every element has been copied */
1475 if (curElement
.type
== STGTY_STORAGE
)
1478 * open child source storage
1480 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1481 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1482 NULL
, 0, &pstgChild
);
1488 * Check if destination storage is not a child of the source
1489 * storage, which will cause an infinite loop
1491 if (pstgChild
== pstgDest
)
1493 IEnumSTATSTG_Release(elements
);
1495 return STG_E_ACCESSDENIED
;
1499 * create a new storage in destination storage
1501 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1502 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1506 * if it already exist, don't create a new one use this one
1508 if (hr
== STG_E_FILEALREADYEXISTS
)
1510 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1511 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1512 NULL
, 0, &pstgTmp
);
1520 * do the copy recursively
1522 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1523 snbExclude
, pstgTmp
);
1525 IStorage_Release( pstgTmp
);
1526 IStorage_Release( pstgChild
);
1528 else if (curElement
.type
== STGTY_STREAM
)
1531 * create a new stream in destination storage. If the stream already
1532 * exist, it will be deleted and a new one will be created.
1534 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1535 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1542 * open child stream storage
1544 hr
= IStorage_OpenStream( iface
, curElement
.pwcsName
, NULL
,
1545 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1552 * Get the size of the source stream
1554 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1557 * Set the size of the destination stream.
1559 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1564 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1567 IStream_Release( pstrTmp
);
1568 IStream_Release( pstrChild
);
1572 WARN("unknown element type: %ld\n", curElement
.type
);
1575 } while (hr
== S_OK
);
1580 IEnumSTATSTG_Release(elements
);
1585 /*************************************************************************
1586 * MoveElementTo (IStorage)
1588 HRESULT WINAPI
StorageImpl_MoveElementTo(
1590 const OLECHAR
*pwcsName
, /* [string][in] */
1591 IStorage
*pstgDest
, /* [unique][in] */
1592 const OLECHAR
*pwcsNewName
,/* [string][in] */
1593 DWORD grfFlags
) /* [in] */
1595 FIXME("not implemented!\n");
1599 /*************************************************************************
1602 HRESULT WINAPI
StorageImpl_Commit(
1604 DWORD grfCommitFlags
)/* [in] */
1606 FIXME("(%ld): stub!\n", grfCommitFlags
);
1610 /*************************************************************************
1613 HRESULT WINAPI
StorageImpl_Revert(
1616 FIXME("not implemented!\n");
1620 /*************************************************************************
1621 * DestroyElement (IStorage)
1623 * Stategy: This implementation is build this way for simplicity not for speed.
1624 * I always delete the top most element of the enumeration and adjust
1625 * the deleted element pointer all the time. This takes longer to
1626 * do but allow to reinvoke DestroyElement whenever we encounter a
1627 * storage object. The optimisation reside in the usage of another
1628 * enumeration stategy that would give all the leaves of a storage
1629 * first. (postfix order)
1631 HRESULT WINAPI
StorageImpl_DestroyElement(
1633 const OLECHAR
*pwcsName
)/* [string][in] */
1635 StorageImpl
* const This
=(StorageImpl
*)iface
;
1637 IEnumSTATSTGImpl
* propertyEnumeration
;
1640 StgProperty propertyToDelete
;
1641 StgProperty parentProperty
;
1642 ULONG foundPropertyIndexToDelete
;
1643 ULONG typeOfRelation
;
1644 ULONG parentPropertyId
;
1647 iface
, debugstr_w(pwcsName
));
1650 * Perform a sanity check on the parameters.
1653 return STG_E_INVALIDPOINTER
;
1656 * Create a property enumeration to search the property with the given name
1658 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
1659 This
->ancestorStorage
,
1660 This
->rootPropertySetIndex
);
1662 foundPropertyIndexToDelete
= IEnumSTATSTGImpl_FindProperty(
1663 propertyEnumeration
,
1667 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1669 if ( foundPropertyIndexToDelete
== PROPERTY_NULL
)
1671 return STG_E_FILENOTFOUND
;
1675 * Find the parent property of the property to delete (the one that
1676 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1677 * the parent is This. Otherwise, the parent is one of it's sibling...
1681 * First, read This's StgProperty..
1683 res
= StorageImpl_ReadProperty(
1684 This
->ancestorStorage
,
1685 This
->rootPropertySetIndex
,
1691 * Second, check to see if by any chance the actual storage (This) is not
1692 * the parent of the property to delete... We never know...
1694 if ( parentProperty
.dirProperty
== foundPropertyIndexToDelete
)
1697 * Set data as it would have been done in the else part...
1699 typeOfRelation
= PROPERTY_RELATION_DIR
;
1700 parentPropertyId
= This
->rootPropertySetIndex
;
1705 * Create a property enumeration to search the parent properties, and
1706 * delete it once done.
1708 IEnumSTATSTGImpl
* propertyEnumeration2
;
1710 propertyEnumeration2
= IEnumSTATSTGImpl_Construct(
1711 This
->ancestorStorage
,
1712 This
->rootPropertySetIndex
);
1714 typeOfRelation
= IEnumSTATSTGImpl_FindParentProperty(
1715 propertyEnumeration2
,
1716 foundPropertyIndexToDelete
,
1720 IEnumSTATSTGImpl_Destroy(propertyEnumeration2
);
1723 if ( propertyToDelete
.propertyType
== PROPTYPE_STORAGE
)
1725 hr
= deleteStorageProperty(
1727 foundPropertyIndexToDelete
,
1730 else if ( propertyToDelete
.propertyType
== PROPTYPE_STREAM
)
1732 hr
= deleteStreamProperty(
1734 foundPropertyIndexToDelete
,
1742 * Adjust the property chain
1744 hr
= adjustPropertyChain(
1755 /************************************************************************
1756 * StorageImpl_Stat (IStorage)
1758 * This method will retrieve information about this storage object.
1760 * See Windows documentation for more details on IStorage methods.
1762 HRESULT WINAPI
StorageImpl_Stat( IStorage
* iface
,
1763 STATSTG
* pstatstg
, /* [out] */
1764 DWORD grfStatFlag
) /* [in] */
1766 StorageImpl
* const This
= (StorageImpl
*)iface
;
1767 HRESULT result
= StorageBaseImpl_Stat( iface
, pstatstg
, grfStatFlag
);
1769 if ( !FAILED(result
) && ((grfStatFlag
& STATFLAG_NONAME
) == 0) && This
->pwcsName
)
1771 CoTaskMemFree(pstatstg
->pwcsName
);
1772 pstatstg
->pwcsName
= CoTaskMemAlloc((lstrlenW(This
->pwcsName
)+1)*sizeof(WCHAR
));
1773 strcpyW(pstatstg
->pwcsName
, This
->pwcsName
);
1781 /*********************************************************************
1785 * Perform the deletion of a complete storage node
1788 static HRESULT
deleteStorageProperty(
1789 StorageImpl
*parentStorage
,
1790 ULONG indexOfPropertyToDelete
,
1791 StgProperty propertyToDelete
)
1793 IEnumSTATSTG
*elements
= 0;
1794 IStorage
*childStorage
= 0;
1795 STATSTG currentElement
;
1797 HRESULT destroyHr
= S_OK
;
1800 * Open the storage and enumerate it
1802 hr
= StorageBaseImpl_OpenStorage(
1803 (IStorage
*)parentStorage
,
1804 propertyToDelete
.name
,
1806 STGM_SHARE_EXCLUSIVE
,
1817 * Enumerate the elements
1819 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
1824 * Obtain the next element
1826 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
1829 destroyHr
= StorageImpl_DestroyElement(
1830 (IStorage
*)childStorage
,
1831 (OLECHAR
*)currentElement
.pwcsName
);
1833 CoTaskMemFree(currentElement
.pwcsName
);
1837 * We need to Reset the enumeration every time because we delete elements
1838 * and the enumeration could be invalid
1840 IEnumSTATSTG_Reset(elements
);
1842 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
1845 * Invalidate the property by zeroing it's name member.
1847 propertyToDelete
.sizeOfNameString
= 0;
1849 StorageImpl_WriteProperty(parentStorage
->ancestorStorage
,
1850 indexOfPropertyToDelete
,
1853 IStorage_Release(childStorage
);
1854 IEnumSTATSTG_Release(elements
);
1859 /*********************************************************************
1863 * Perform the deletion of a stream node
1866 static HRESULT
deleteStreamProperty(
1867 StorageImpl
*parentStorage
,
1868 ULONG indexOfPropertyToDelete
,
1869 StgProperty propertyToDelete
)
1873 ULARGE_INTEGER size
;
1875 size
.u
.HighPart
= 0;
1878 hr
= StorageBaseImpl_OpenStream(
1879 (IStorage
*)parentStorage
,
1880 (OLECHAR
*)propertyToDelete
.name
,
1882 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
1894 hr
= IStream_SetSize(pis
, size
);
1902 * Release the stream object.
1904 IStream_Release(pis
);
1907 * Invalidate the property by zeroing it's name member.
1909 propertyToDelete
.sizeOfNameString
= 0;
1912 * Here we should re-read the property so we get the updated pointer
1913 * but since we are here to zap it, I don't do it...
1915 StorageImpl_WriteProperty(
1916 parentStorage
->ancestorStorage
,
1917 indexOfPropertyToDelete
,
1923 /*********************************************************************
1927 * Finds a placeholder for the StgProperty within the Storage
1930 static HRESULT
findPlaceholder(
1931 StorageImpl
*storage
,
1932 ULONG propertyIndexToStore
,
1933 ULONG storePropertyIndex
,
1936 StgProperty storeProperty
;
1941 * Read the storage property
1943 res
= StorageImpl_ReadProperty(
1944 storage
->ancestorStorage
,
1953 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
1955 if (storeProperty
.previousProperty
!= PROPERTY_NULL
)
1957 return findPlaceholder(
1959 propertyIndexToStore
,
1960 storeProperty
.previousProperty
,
1965 storeProperty
.previousProperty
= propertyIndexToStore
;
1968 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
1970 if (storeProperty
.nextProperty
!= PROPERTY_NULL
)
1972 return findPlaceholder(
1974 propertyIndexToStore
,
1975 storeProperty
.nextProperty
,
1980 storeProperty
.nextProperty
= propertyIndexToStore
;
1983 else if (typeOfRelation
== PROPERTY_RELATION_DIR
)
1985 if (storeProperty
.dirProperty
!= PROPERTY_NULL
)
1987 return findPlaceholder(
1989 propertyIndexToStore
,
1990 storeProperty
.dirProperty
,
1995 storeProperty
.dirProperty
= propertyIndexToStore
;
1999 hr
= StorageImpl_WriteProperty(
2000 storage
->ancestorStorage
,
2012 /*************************************************************************
2016 * This method takes the previous and the next property link of a property
2017 * to be deleted and find them a place in the Storage.
2019 static HRESULT
adjustPropertyChain(
2021 StgProperty propertyToDelete
,
2022 StgProperty parentProperty
,
2023 ULONG parentPropertyId
,
2026 ULONG newLinkProperty
= PROPERTY_NULL
;
2027 BOOL needToFindAPlaceholder
= FALSE
;
2028 ULONG storeNode
= PROPERTY_NULL
;
2029 ULONG toStoreNode
= PROPERTY_NULL
;
2030 INT relationType
= 0;
2034 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
2036 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2039 * Set the parent previous to the property to delete previous
2041 newLinkProperty
= propertyToDelete
.previousProperty
;
2043 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2046 * We also need to find a storage for the other link, setup variables
2047 * to do this at the end...
2049 needToFindAPlaceholder
= TRUE
;
2050 storeNode
= propertyToDelete
.previousProperty
;
2051 toStoreNode
= propertyToDelete
.nextProperty
;
2052 relationType
= PROPERTY_RELATION_NEXT
;
2055 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2058 * Set the parent previous to the property to delete next
2060 newLinkProperty
= propertyToDelete
.nextProperty
;
2064 * Link it for real...
2066 parentProperty
.previousProperty
= newLinkProperty
;
2069 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
2071 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2074 * Set the parent next to the property to delete next previous
2076 newLinkProperty
= propertyToDelete
.previousProperty
;
2078 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2081 * We also need to find a storage for the other link, setup variables
2082 * to do this at the end...
2084 needToFindAPlaceholder
= TRUE
;
2085 storeNode
= propertyToDelete
.previousProperty
;
2086 toStoreNode
= propertyToDelete
.nextProperty
;
2087 relationType
= PROPERTY_RELATION_NEXT
;
2090 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2093 * Set the parent next to the property to delete next
2095 newLinkProperty
= propertyToDelete
.nextProperty
;
2099 * Link it for real...
2101 parentProperty
.nextProperty
= newLinkProperty
;
2103 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2105 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2108 * Set the parent dir to the property to delete previous
2110 newLinkProperty
= propertyToDelete
.previousProperty
;
2112 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2115 * We also need to find a storage for the other link, setup variables
2116 * to do this at the end...
2118 needToFindAPlaceholder
= TRUE
;
2119 storeNode
= propertyToDelete
.previousProperty
;
2120 toStoreNode
= propertyToDelete
.nextProperty
;
2121 relationType
= PROPERTY_RELATION_NEXT
;
2124 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2127 * Set the parent dir to the property to delete next
2129 newLinkProperty
= propertyToDelete
.nextProperty
;
2133 * Link it for real...
2135 parentProperty
.dirProperty
= newLinkProperty
;
2139 * Write back the parent property
2141 res
= StorageImpl_WriteProperty(
2142 This
->ancestorStorage
,
2151 * If a placeholder is required for the other link, then, find one and
2152 * get out of here...
2154 if (needToFindAPlaceholder
)
2156 hr
= findPlaceholder(
2167 /******************************************************************************
2168 * SetElementTimes (IStorage)
2170 HRESULT WINAPI
StorageImpl_SetElementTimes(
2172 const OLECHAR
*pwcsName
,/* [string][in] */
2173 const FILETIME
*pctime
, /* [in] */
2174 const FILETIME
*patime
, /* [in] */
2175 const FILETIME
*pmtime
) /* [in] */
2177 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2181 /******************************************************************************
2182 * SetStateBits (IStorage)
2184 HRESULT WINAPI
StorageImpl_SetStateBits(
2186 DWORD grfStateBits
,/* [in] */
2187 DWORD grfMask
) /* [in] */
2189 FIXME("not implemented!\n");
2193 HRESULT
StorageImpl_Construct(
2203 StgProperty currentProperty
;
2204 BOOL readSuccessful
;
2205 ULONG currentPropertyIndex
;
2207 if ( FAILED( validateSTGM(openFlags
) ))
2208 return STG_E_INVALIDFLAG
;
2210 memset(This
, 0, sizeof(StorageImpl
));
2213 * Initialize the virtual function table.
2215 This
->lpVtbl
= &Storage32Impl_Vtbl
;
2216 This
->v_destructor
= &StorageImpl_Destroy
;
2219 * This is the top-level storage so initialize the ancestor pointer
2222 This
->ancestorStorage
= This
;
2225 * Initialize the physical support of the storage.
2227 This
->hFile
= hFile
;
2230 * Store copy of file path.
2233 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2234 (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
));
2235 if (!This
->pwcsName
)
2236 return STG_E_INSUFFICIENTMEMORY
;
2237 strcpyW(This
->pwcsName
, pwcsName
);
2241 * Initialize the big block cache.
2243 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
2244 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2245 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2251 if (This
->bigBlockFile
== 0)
2256 ULARGE_INTEGER size
;
2257 BYTE
* bigBlockBuffer
;
2260 * Initialize all header variables:
2261 * - The big block depot consists of one block and it is at block 0
2262 * - The properties start at block 1
2263 * - There is no small block depot
2265 memset( This
->bigBlockDepotStart
,
2267 sizeof(This
->bigBlockDepotStart
));
2269 This
->bigBlockDepotCount
= 1;
2270 This
->bigBlockDepotStart
[0] = 0;
2271 This
->rootStartBlock
= 1;
2272 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2273 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
2274 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2275 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2276 This
->extBigBlockDepotCount
= 0;
2278 StorageImpl_SaveFileHeader(This
);
2281 * Add one block for the big block depot and one block for the properties
2283 size
.u
.HighPart
= 0;
2284 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2285 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2288 * Initialize the big block depot
2290 bigBlockBuffer
= StorageImpl_GetBigBlock(This
, 0);
2291 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2292 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2293 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2294 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
2299 * Load the header for the file.
2301 hr
= StorageImpl_LoadFileHeader(This
);
2305 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2312 * There is no block depot cached yet.
2314 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2317 * Start searching for free blocks with block 0.
2319 This
->prevFreeBlock
= 0;
2322 * Create the block chain abstractions.
2324 if(!(This
->rootBlockChain
=
2325 BlockChainStream_Construct(This
, &This
->rootStartBlock
, PROPERTY_NULL
)))
2326 return STG_E_READFAULT
;
2328 if(!(This
->smallBlockDepotChain
=
2329 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2331 return STG_E_READFAULT
;
2334 * Write the root property
2338 StgProperty rootProp
;
2340 * Initialize the property chain
2342 memset(&rootProp
, 0, sizeof(rootProp
));
2343 MultiByteToWideChar( CP_ACP
, 0, rootPropertyName
, -1, rootProp
.name
,
2344 sizeof(rootProp
.name
)/sizeof(WCHAR
) );
2345 rootProp
.sizeOfNameString
= (strlenW(rootProp
.name
)+1) * sizeof(WCHAR
);
2346 rootProp
.propertyType
= PROPTYPE_ROOT
;
2347 rootProp
.previousProperty
= PROPERTY_NULL
;
2348 rootProp
.nextProperty
= PROPERTY_NULL
;
2349 rootProp
.dirProperty
= PROPERTY_NULL
;
2350 rootProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
2351 rootProp
.size
.u
.HighPart
= 0;
2352 rootProp
.size
.u
.LowPart
= 0;
2354 StorageImpl_WriteProperty(This
, 0, &rootProp
);
2358 * Find the ID of the root in the property sets.
2360 currentPropertyIndex
= 0;
2364 readSuccessful
= StorageImpl_ReadProperty(
2366 currentPropertyIndex
,
2371 if ( (currentProperty
.sizeOfNameString
!= 0 ) &&
2372 (currentProperty
.propertyType
== PROPTYPE_ROOT
) )
2374 This
->rootPropertySetIndex
= currentPropertyIndex
;
2378 currentPropertyIndex
++;
2380 } while (readSuccessful
&& (This
->rootPropertySetIndex
== PROPERTY_NULL
) );
2382 if (!readSuccessful
)
2385 return STG_E_READFAULT
;
2389 * Create the block chain abstraction for the small block root chain.
2391 if(!(This
->smallBlockRootChain
=
2392 BlockChainStream_Construct(This
, NULL
, This
->rootPropertySetIndex
)))
2393 return STG_E_READFAULT
;
2398 void StorageImpl_Destroy(
2401 TRACE("(%p)\n", This
);
2404 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2406 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2407 BlockChainStream_Destroy(This
->rootBlockChain
);
2408 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2410 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2414 /******************************************************************************
2415 * Storage32Impl_GetNextFreeBigBlock
2417 * Returns the index of the next free big block.
2418 * If the big block depot is filled, this method will enlarge it.
2421 ULONG
StorageImpl_GetNextFreeBigBlock(
2424 ULONG depotBlockIndexPos
;
2426 ULONG depotBlockOffset
;
2427 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2428 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2430 ULONG freeBlock
= BLOCK_UNUSED
;
2432 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2433 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2436 * Scan the entire big block depot until we find a block marked free
2438 while (nextBlockIndex
!= BLOCK_UNUSED
)
2440 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2442 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2445 * Grow the primary depot.
2447 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2449 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2452 * Add a block depot.
2454 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2455 This
->bigBlockDepotCount
++;
2456 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2459 * Flag it as a block depot.
2461 StorageImpl_SetNextBlockInChain(This
,
2465 /* Save new header information.
2467 StorageImpl_SaveFileHeader(This
);
2472 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2474 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2477 * Grow the extended depot.
2479 ULONG extIndex
= BLOCK_UNUSED
;
2480 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2481 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2483 if (extBlockOffset
== 0)
2485 /* We need an extended block.
2487 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2488 This
->extBigBlockDepotCount
++;
2489 depotBlockIndexPos
= extIndex
+ 1;
2492 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2495 * Add a block depot and mark it in the extended block.
2497 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2498 This
->bigBlockDepotCount
++;
2499 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2501 /* Flag the block depot.
2503 StorageImpl_SetNextBlockInChain(This
,
2507 /* If necessary, flag the extended depot block.
2509 if (extIndex
!= BLOCK_UNUSED
)
2510 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2512 /* Save header information.
2514 StorageImpl_SaveFileHeader(This
);
2518 depotBuffer
= StorageImpl_GetROBigBlock(This
, depotBlockIndexPos
);
2520 if (depotBuffer
!= 0)
2522 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2523 ( nextBlockIndex
!= BLOCK_UNUSED
))
2525 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2527 if (nextBlockIndex
== BLOCK_UNUSED
)
2529 freeBlock
= (depotIndex
* blocksPerDepot
) +
2530 (depotBlockOffset
/sizeof(ULONG
));
2533 depotBlockOffset
+= sizeof(ULONG
);
2536 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2540 depotBlockOffset
= 0;
2543 This
->prevFreeBlock
= freeBlock
;
2548 /******************************************************************************
2549 * Storage32Impl_AddBlockDepot
2551 * This will create a depot block, essentially it is a block initialized
2554 void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2558 blockBuffer
= StorageImpl_GetBigBlock(This
, blockIndex
);
2561 * Initialize blocks as free
2563 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2565 StorageImpl_ReleaseBigBlock(This
, blockBuffer
);
2568 /******************************************************************************
2569 * Storage32Impl_GetExtDepotBlock
2571 * Returns the index of the block that corresponds to the specified depot
2572 * index. This method is only for depot indexes equal or greater than
2573 * COUNT_BBDEPOTINHEADER.
2575 ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2577 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2578 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2579 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2580 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2581 ULONG blockIndex
= BLOCK_UNUSED
;
2582 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2584 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2586 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2587 return BLOCK_UNUSED
;
2589 while (extBlockCount
> 0)
2591 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2595 if (extBlockIndex
!= BLOCK_UNUSED
)
2599 depotBuffer
= StorageImpl_GetROBigBlock(This
, extBlockIndex
);
2601 if (depotBuffer
!= 0)
2603 StorageUtl_ReadDWord(depotBuffer
,
2604 extBlockOffset
* sizeof(ULONG
),
2607 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2614 /******************************************************************************
2615 * Storage32Impl_SetExtDepotBlock
2617 * Associates the specified block index to the specified depot index.
2618 * This method is only for depot indexes equal or greater than
2619 * COUNT_BBDEPOTINHEADER.
2621 void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
,
2625 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2626 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2627 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2628 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2629 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2631 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2633 while (extBlockCount
> 0)
2635 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2639 if (extBlockIndex
!= BLOCK_UNUSED
)
2643 depotBuffer
= StorageImpl_GetBigBlock(This
, extBlockIndex
);
2645 if (depotBuffer
!= 0)
2647 StorageUtl_WriteDWord(depotBuffer
,
2648 extBlockOffset
* sizeof(ULONG
),
2651 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2656 /******************************************************************************
2657 * Storage32Impl_AddExtBlockDepot
2659 * Creates an extended depot block.
2661 ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
2663 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
2664 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
2665 BYTE
* depotBuffer
= NULL
;
2666 ULONG index
= BLOCK_UNUSED
;
2667 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2668 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
2669 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
2671 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
2672 blocksPerDepotBlock
;
2674 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
2677 * The first extended block.
2679 This
->extBigBlockDepotStart
= index
;
2685 * Follow the chain to the last one.
2687 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
2689 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
2693 * Add the new extended block to the chain.
2695 depotBuffer
= StorageImpl_GetBigBlock(This
, nextExtBlock
);
2696 StorageUtl_WriteDWord(depotBuffer
, nextBlockOffset
, index
);
2697 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2701 * Initialize this block.
2703 depotBuffer
= StorageImpl_GetBigBlock(This
, index
);
2704 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2705 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2710 /******************************************************************************
2711 * Storage32Impl_FreeBigBlock
2713 * This method will flag the specified block as free in the big block depot.
2715 void StorageImpl_FreeBigBlock(
2719 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
2721 if (blockIndex
< This
->prevFreeBlock
)
2722 This
->prevFreeBlock
= blockIndex
;
2725 /************************************************************************
2726 * Storage32Impl_GetNextBlockInChain
2728 * This method will retrieve the block index of the next big block in
2731 * Params: This - Pointer to the Storage object.
2732 * blockIndex - Index of the block to retrieve the chain
2734 * nextBlockIndex - receives the return value.
2736 * Returns: This method returns the index of the next block in the chain.
2737 * It will return the constants:
2738 * BLOCK_SPECIAL - If the block given was not part of a
2740 * BLOCK_END_OF_CHAIN - If the block given was the last in
2742 * BLOCK_UNUSED - If the block given was not past of a chain
2744 * BLOCK_EXTBBDEPOT - This block is part of the extended
2747 * See Windows documentation for more details on IStorage methods.
2749 HRESULT
StorageImpl_GetNextBlockInChain(
2752 ULONG
* nextBlockIndex
)
2754 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2755 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2756 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2758 ULONG depotBlockIndexPos
;
2761 *nextBlockIndex
= BLOCK_SPECIAL
;
2763 if(depotBlockCount
>= This
->bigBlockDepotCount
)
2765 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount
,
2766 This
->bigBlockDepotCount
);
2767 return STG_E_READFAULT
;
2771 * Cache the currently accessed depot block.
2773 if (depotBlockCount
!= This
->indexBlockDepotCached
)
2775 This
->indexBlockDepotCached
= depotBlockCount
;
2777 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2779 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2784 * We have to look in the extended depot.
2786 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2789 depotBuffer
= StorageImpl_GetROBigBlock(This
, depotBlockIndexPos
);
2792 return STG_E_READFAULT
;
2794 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
2796 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
2797 This
->blockDepotCached
[index
] = *nextBlockIndex
;
2799 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2802 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
2807 /******************************************************************************
2808 * Storage32Impl_GetNextExtendedBlock
2810 * Given an extended block this method will return the next extended block.
2813 * The last ULONG of an extended block is the block index of the next
2814 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2818 * - The index of the next extended block
2819 * - BLOCK_UNUSED: there is no next extended block.
2820 * - Any other return values denotes failure.
2822 ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
2824 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2825 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2828 depotBuffer
= StorageImpl_GetROBigBlock(This
, blockIndex
);
2832 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2834 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2837 return nextBlockIndex
;
2840 /******************************************************************************
2841 * Storage32Impl_SetNextBlockInChain
2843 * This method will write the index of the specified block's next block
2844 * in the big block depot.
2846 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2849 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2850 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2851 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2854 void StorageImpl_SetNextBlockInChain(
2859 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2860 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2861 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2862 ULONG depotBlockIndexPos
;
2865 assert(depotBlockCount
< This
->bigBlockDepotCount
);
2866 assert(blockIndex
!= nextBlock
);
2868 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2870 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2875 * We have to look in the extended depot.
2877 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2880 depotBuffer
= StorageImpl_GetBigBlock(This
, depotBlockIndexPos
);
2884 StorageUtl_WriteDWord(depotBuffer
, depotBlockOffset
, nextBlock
);
2885 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2889 * Update the cached block depot, if necessary.
2891 if (depotBlockCount
== This
->indexBlockDepotCached
)
2893 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
2897 /******************************************************************************
2898 * Storage32Impl_LoadFileHeader
2900 * This method will read in the file header, i.e. big block index -1.
2902 HRESULT
StorageImpl_LoadFileHeader(
2905 HRESULT hr
= STG_E_FILENOTFOUND
;
2906 void* headerBigBlock
= NULL
;
2910 * Get a pointer to the big block of data containing the header.
2912 headerBigBlock
= StorageImpl_GetROBigBlock(This
, -1);
2915 * Extract the information from the header.
2917 if (headerBigBlock
!=0)
2920 * Check for the "magic number" signature and return an error if it is not
2923 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
2925 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2926 return STG_E_OLDFORMAT
;
2929 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
2931 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2932 return STG_E_INVALIDHEADER
;
2935 StorageUtl_ReadWord(
2937 OFFSET_BIGBLOCKSIZEBITS
,
2938 &This
->bigBlockSizeBits
);
2940 StorageUtl_ReadWord(
2942 OFFSET_SMALLBLOCKSIZEBITS
,
2943 &This
->smallBlockSizeBits
);
2945 StorageUtl_ReadDWord(
2947 OFFSET_BBDEPOTCOUNT
,
2948 &This
->bigBlockDepotCount
);
2950 StorageUtl_ReadDWord(
2952 OFFSET_ROOTSTARTBLOCK
,
2953 &This
->rootStartBlock
);
2955 StorageUtl_ReadDWord(
2957 OFFSET_SBDEPOTSTART
,
2958 &This
->smallBlockDepotStart
);
2960 StorageUtl_ReadDWord(
2962 OFFSET_EXTBBDEPOTSTART
,
2963 &This
->extBigBlockDepotStart
);
2965 StorageUtl_ReadDWord(
2967 OFFSET_EXTBBDEPOTCOUNT
,
2968 &This
->extBigBlockDepotCount
);
2970 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2972 StorageUtl_ReadDWord(
2974 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
2975 &(This
->bigBlockDepotStart
[index
]));
2979 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2983 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
2984 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
2988 This
->bigBlockSize
= 0x000000001 >> (DWORD
)This
->bigBlockSizeBits
;
2989 This
->smallBlockSize
= 0x000000001 >> (DWORD
)This
->smallBlockSizeBits
;
2993 * Right now, the code is making some assumptions about the size of the
2994 * blocks, just make sure they are what we're expecting.
2996 if (This
->bigBlockSize
!= DEF_BIG_BLOCK_SIZE
||
2997 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
)
2999 WARN("Broken OLE storage file\n");
3000 hr
= STG_E_INVALIDHEADER
;
3006 * Release the block.
3008 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
3014 /******************************************************************************
3015 * Storage32Impl_SaveFileHeader
3017 * This method will save to the file the header, i.e. big block -1.
3019 void StorageImpl_SaveFileHeader(
3022 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3027 * Get a pointer to the big block of data containing the header.
3029 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3032 * If the block read failed, the file is probably new.
3037 * Initialize for all unknown fields.
3039 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
3042 * Initialize the magic number.
3044 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3047 * And a bunch of things we don't know what they mean
3049 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3050 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3051 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3052 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
3056 * Write the information to the header.
3058 StorageUtl_WriteWord(
3060 OFFSET_BIGBLOCKSIZEBITS
,
3061 This
->bigBlockSizeBits
);
3063 StorageUtl_WriteWord(
3065 OFFSET_SMALLBLOCKSIZEBITS
,
3066 This
->smallBlockSizeBits
);
3068 StorageUtl_WriteDWord(
3070 OFFSET_BBDEPOTCOUNT
,
3071 This
->bigBlockDepotCount
);
3073 StorageUtl_WriteDWord(
3075 OFFSET_ROOTSTARTBLOCK
,
3076 This
->rootStartBlock
);
3078 StorageUtl_WriteDWord(
3080 OFFSET_SBDEPOTSTART
,
3081 This
->smallBlockDepotStart
);
3083 StorageUtl_WriteDWord(
3085 OFFSET_SBDEPOTCOUNT
,
3086 This
->smallBlockDepotChain
?
3087 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3089 StorageUtl_WriteDWord(
3091 OFFSET_EXTBBDEPOTSTART
,
3092 This
->extBigBlockDepotStart
);
3094 StorageUtl_WriteDWord(
3096 OFFSET_EXTBBDEPOTCOUNT
,
3097 This
->extBigBlockDepotCount
);
3099 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3101 StorageUtl_WriteDWord(
3103 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3104 (This
->bigBlockDepotStart
[index
]));
3108 * Write the big block back to the file.
3110 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
3113 /******************************************************************************
3114 * Storage32Impl_ReadProperty
3116 * This method will read the specified property from the property chain.
3118 BOOL
StorageImpl_ReadProperty(
3121 StgProperty
* buffer
)
3123 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
3124 ULARGE_INTEGER offsetInPropSet
;
3125 BOOL readSuccessful
;
3128 offsetInPropSet
.u
.HighPart
= 0;
3129 offsetInPropSet
.u
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
3131 readSuccessful
= BlockChainStream_ReadAt(
3132 This
->rootBlockChain
,
3140 /* replace the name of root entry (often "Root Entry") by the file name */
3141 WCHAR
*propName
= (index
== This
->rootPropertySetIndex
) ?
3142 This
->filename
: (WCHAR
*)currentProperty
+OFFSET_PS_NAME
;
3144 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3148 PROPERTY_NAME_BUFFER_LEN
);
3149 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3151 memcpy(&buffer
->propertyType
, currentProperty
+ OFFSET_PS_PROPERTYTYPE
, 1);
3153 StorageUtl_ReadWord(
3155 OFFSET_PS_NAMELENGTH
,
3156 &buffer
->sizeOfNameString
);
3158 StorageUtl_ReadDWord(
3160 OFFSET_PS_PREVIOUSPROP
,
3161 &buffer
->previousProperty
);
3163 StorageUtl_ReadDWord(
3166 &buffer
->nextProperty
);
3168 StorageUtl_ReadDWord(
3171 &buffer
->dirProperty
);
3173 StorageUtl_ReadGUID(
3176 &buffer
->propertyUniqueID
);
3178 StorageUtl_ReadDWord(
3181 &buffer
->timeStampS1
);
3183 StorageUtl_ReadDWord(
3186 &buffer
->timeStampD1
);
3188 StorageUtl_ReadDWord(
3191 &buffer
->timeStampS2
);
3193 StorageUtl_ReadDWord(
3196 &buffer
->timeStampD2
);
3198 StorageUtl_ReadDWord(
3200 OFFSET_PS_STARTBLOCK
,
3201 &buffer
->startingBlock
);
3203 StorageUtl_ReadDWord(
3206 &buffer
->size
.u
.LowPart
);
3208 buffer
->size
.u
.HighPart
= 0;
3211 return readSuccessful
;
3214 /*********************************************************************
3215 * Write the specified property into the property chain
3217 BOOL
StorageImpl_WriteProperty(
3220 StgProperty
* buffer
)
3222 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
3223 ULARGE_INTEGER offsetInPropSet
;
3224 BOOL writeSuccessful
;
3227 offsetInPropSet
.u
.HighPart
= 0;
3228 offsetInPropSet
.u
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
3230 memset(currentProperty
, 0, PROPSET_BLOCK_SIZE
);
3233 currentProperty
+ OFFSET_PS_NAME
,
3235 PROPERTY_NAME_BUFFER_LEN
);
3237 memcpy(currentProperty
+ OFFSET_PS_PROPERTYTYPE
, &buffer
->propertyType
, 1);
3239 StorageUtl_WriteWord(
3241 OFFSET_PS_NAMELENGTH
,
3242 buffer
->sizeOfNameString
);
3244 StorageUtl_WriteDWord(
3246 OFFSET_PS_PREVIOUSPROP
,
3247 buffer
->previousProperty
);
3249 StorageUtl_WriteDWord(
3252 buffer
->nextProperty
);
3254 StorageUtl_WriteDWord(
3257 buffer
->dirProperty
);
3259 StorageUtl_WriteGUID(
3262 &buffer
->propertyUniqueID
);
3264 StorageUtl_WriteDWord(
3267 buffer
->timeStampS1
);
3269 StorageUtl_WriteDWord(
3272 buffer
->timeStampD1
);
3274 StorageUtl_WriteDWord(
3277 buffer
->timeStampS2
);
3279 StorageUtl_WriteDWord(
3282 buffer
->timeStampD2
);
3284 StorageUtl_WriteDWord(
3286 OFFSET_PS_STARTBLOCK
,
3287 buffer
->startingBlock
);
3289 StorageUtl_WriteDWord(
3292 buffer
->size
.u
.LowPart
);
3294 writeSuccessful
= BlockChainStream_WriteAt(This
->rootBlockChain
,
3299 return writeSuccessful
;
3302 BOOL
StorageImpl_ReadBigBlock(
3307 void* bigBlockBuffer
;
3309 bigBlockBuffer
= StorageImpl_GetROBigBlock(This
, blockIndex
);
3311 if (bigBlockBuffer
!=0)
3313 memcpy(buffer
, bigBlockBuffer
, This
->bigBlockSize
);
3315 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
3323 BOOL
StorageImpl_WriteBigBlock(
3328 void* bigBlockBuffer
;
3330 bigBlockBuffer
= StorageImpl_GetBigBlock(This
, blockIndex
);
3332 if (bigBlockBuffer
!=0)
3334 memcpy(bigBlockBuffer
, buffer
, This
->bigBlockSize
);
3336 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
3344 void* StorageImpl_GetROBigBlock(
3348 return BIGBLOCKFILE_GetROBigBlock(This
->bigBlockFile
, blockIndex
);
3351 void* StorageImpl_GetBigBlock(
3355 return BIGBLOCKFILE_GetBigBlock(This
->bigBlockFile
, blockIndex
);
3358 void StorageImpl_ReleaseBigBlock(
3362 BIGBLOCKFILE_ReleaseBigBlock(This
->bigBlockFile
, pBigBlock
);
3365 /******************************************************************************
3366 * Storage32Impl_SmallBlocksToBigBlocks
3368 * This method will convert a small block chain to a big block chain.
3369 * The small block chain will be destroyed.
3371 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3373 SmallBlockChainStream
** ppsbChain
)
3375 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3376 ULARGE_INTEGER size
, offset
;
3377 ULONG cbRead
, cbWritten
, cbTotalRead
, cbTotalWritten
;
3378 ULONG propertyIndex
;
3379 BOOL successRead
, successWrite
;
3380 StgProperty chainProperty
;
3382 BlockChainStream
*bbTempChain
= NULL
;
3383 BlockChainStream
*bigBlockChain
= NULL
;
3386 * Create a temporary big block chain that doesn't have
3387 * an associated property. This temporary chain will be
3388 * used to copy data from small blocks to big blocks.
3390 bbTempChain
= BlockChainStream_Construct(This
,
3393 if(!bbTempChain
) return NULL
;
3395 * Grow the big block chain.
3397 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3398 BlockChainStream_SetSize(bbTempChain
, size
);
3401 * Copy the contents of the small block chain to the big block chain
3402 * by small block size increments.
3404 offset
.u
.LowPart
= 0;
3405 offset
.u
.HighPart
= 0;
3409 buffer
= (BYTE
*) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3412 successRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3414 DEF_SMALL_BLOCK_SIZE
,
3417 cbTotalRead
+= cbRead
;
3419 successWrite
= BlockChainStream_WriteAt(bbTempChain
,
3424 cbTotalWritten
+= cbWritten
;
3426 offset
.u
.LowPart
+= This
->smallBlockSize
;
3428 } while (successRead
&& successWrite
);
3429 HeapFree(GetProcessHeap(),0,buffer
);
3431 assert(cbTotalRead
== cbTotalWritten
);
3434 * Destroy the small block chain.
3436 propertyIndex
= (*ppsbChain
)->ownerPropertyIndex
;
3437 size
.u
.HighPart
= 0;
3439 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3440 SmallBlockChainStream_Destroy(*ppsbChain
);
3444 * Change the property information. This chain is now a big block chain
3445 * and it doesn't reside in the small blocks chain anymore.
3447 StorageImpl_ReadProperty(This
, propertyIndex
, &chainProperty
);
3449 chainProperty
.startingBlock
= bbHeadOfChain
;
3451 StorageImpl_WriteProperty(This
, propertyIndex
, &chainProperty
);
3454 * Destroy the temporary propertyless big block chain.
3455 * Create a new big block chain associated with this property.
3457 BlockChainStream_Destroy(bbTempChain
);
3458 bigBlockChain
= BlockChainStream_Construct(This
,
3462 return bigBlockChain
;
3465 /******************************************************************************
3466 ** Storage32InternalImpl implementation
3469 StorageInternalImpl
* StorageInternalImpl_Construct(
3470 StorageImpl
* ancestorStorage
,
3471 ULONG rootPropertyIndex
)
3473 StorageInternalImpl
* newStorage
;
3476 * Allocate space for the new storage object
3478 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl
));
3482 memset(newStorage
, 0, sizeof(StorageInternalImpl
));
3485 * Initialize the virtual function table.
3487 newStorage
->lpVtbl
= &Storage32InternalImpl_Vtbl
;
3488 newStorage
->v_destructor
= &StorageInternalImpl_Destroy
;
3491 * Keep the ancestor storage pointer and nail a reference to it.
3493 newStorage
->ancestorStorage
= ancestorStorage
;
3494 StorageBaseImpl_AddRef((IStorage
*)(newStorage
->ancestorStorage
));
3497 * Keep the index of the root property set for this storage,
3499 newStorage
->rootPropertySetIndex
= rootPropertyIndex
;
3507 void StorageInternalImpl_Destroy(
3508 StorageInternalImpl
* This
)
3510 StorageBaseImpl_Release((IStorage
*)This
->ancestorStorage
);
3511 HeapFree(GetProcessHeap(), 0, This
);
3514 /******************************************************************************
3516 ** Storage32InternalImpl_Commit
3518 ** The non-root storages cannot be opened in transacted mode thus this function
3521 HRESULT WINAPI
StorageInternalImpl_Commit(
3523 DWORD grfCommitFlags
) /* [in] */
3528 /******************************************************************************
3530 ** Storage32InternalImpl_Revert
3532 ** The non-root storages cannot be opened in transacted mode thus this function
3535 HRESULT WINAPI
StorageInternalImpl_Revert(
3541 /******************************************************************************
3542 ** IEnumSTATSTGImpl implementation
3545 IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
3546 StorageImpl
* parentStorage
,
3547 ULONG firstPropertyNode
)
3549 IEnumSTATSTGImpl
* newEnumeration
;
3551 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
3553 if (newEnumeration
!=0)
3556 * Set-up the virtual function table and reference count.
3558 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
3559 newEnumeration
->ref
= 0;
3562 * We want to nail-down the reference to the storage in case the
3563 * enumeration out-lives the storage in the client application.
3565 newEnumeration
->parentStorage
= parentStorage
;
3566 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
3568 newEnumeration
->firstPropertyNode
= firstPropertyNode
;
3571 * Initialize the search stack
3573 newEnumeration
->stackSize
= 0;
3574 newEnumeration
->stackMaxSize
= ENUMSTATSGT_SIZE_INCREMENT
;
3575 newEnumeration
->stackToVisit
=
3576 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
)*ENUMSTATSGT_SIZE_INCREMENT
);
3579 * Make sure the current node of the iterator is the first one.
3581 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
3584 return newEnumeration
;
3587 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
3589 IStorage_Release((IStorage
*)This
->parentStorage
);
3590 HeapFree(GetProcessHeap(), 0, This
->stackToVisit
);
3591 HeapFree(GetProcessHeap(), 0, This
);
3594 HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
3595 IEnumSTATSTG
* iface
,
3599 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3602 * Perform a sanity check on the parameters.
3605 return E_INVALIDARG
;
3608 * Initialize the return parameter.
3613 * Compare the riid with the interface IDs implemented by this object.
3615 if (memcmp(&IID_IUnknown
, riid
, sizeof(IID_IUnknown
)) == 0)
3617 *ppvObject
= (IEnumSTATSTG
*)This
;
3619 else if (memcmp(&IID_IStorage
, riid
, sizeof(IID_IEnumSTATSTG
)) == 0)
3621 *ppvObject
= (IEnumSTATSTG
*)This
;
3625 * Check that we obtained an interface.
3627 if ((*ppvObject
)==0)
3628 return E_NOINTERFACE
;
3631 * Query Interface always increases the reference count by one when it is
3634 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG
*)This
);
3639 ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
3640 IEnumSTATSTG
* iface
)
3642 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3648 ULONG WINAPI
IEnumSTATSTGImpl_Release(
3649 IEnumSTATSTG
* iface
)
3651 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3659 * If the reference count goes down to 0, perform suicide.
3663 IEnumSTATSTGImpl_Destroy(This
);
3669 HRESULT WINAPI
IEnumSTATSTGImpl_Next(
3670 IEnumSTATSTG
* iface
,
3673 ULONG
* pceltFetched
)
3675 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3677 StgProperty currentProperty
;
3678 STATSTG
* currentReturnStruct
= rgelt
;
3679 ULONG objectFetched
= 0;
3680 ULONG currentSearchNode
;
3683 * Perform a sanity check on the parameters.
3685 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
3686 return E_INVALIDARG
;
3689 * To avoid the special case, get another pointer to a ULONG value if
3690 * the caller didn't supply one.
3692 if (pceltFetched
==0)
3693 pceltFetched
= &objectFetched
;
3696 * Start the iteration, we will iterate until we hit the end of the
3697 * linked list or until we hit the number of items to iterate through
3702 * Start with the node at the top of the stack.
3704 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3706 while ( ( *pceltFetched
< celt
) &&
3707 ( currentSearchNode
!=PROPERTY_NULL
) )
3710 * Remove the top node from the stack
3712 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3715 * Read the property from the storage.
3717 StorageImpl_ReadProperty(This
->parentStorage
,
3722 * Copy the information to the return buffer.
3724 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct
,
3729 * Step to the next item in the iteration
3732 currentReturnStruct
++;
3735 * Push the next search node in the search stack.
3737 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3740 * continue the iteration.
3742 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3745 if (*pceltFetched
== celt
)
3752 HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
3753 IEnumSTATSTG
* iface
,
3756 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3758 StgProperty currentProperty
;
3759 ULONG objectFetched
= 0;
3760 ULONG currentSearchNode
;
3763 * Start with the node at the top of the stack.
3765 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3767 while ( (objectFetched
< celt
) &&
3768 (currentSearchNode
!=PROPERTY_NULL
) )
3771 * Remove the top node from the stack
3773 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3776 * Read the property from the storage.
3778 StorageImpl_ReadProperty(This
->parentStorage
,
3783 * Step to the next item in the iteration
3788 * Push the next search node in the search stack.
3790 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3793 * continue the iteration.
3795 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3798 if (objectFetched
== celt
)
3804 HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
3805 IEnumSTATSTG
* iface
)
3807 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3809 StgProperty rootProperty
;
3810 BOOL readSuccessful
;
3813 * Re-initialize the search stack to an empty stack
3815 This
->stackSize
= 0;
3818 * Read the root property from the storage.
3820 readSuccessful
= StorageImpl_ReadProperty(
3821 This
->parentStorage
,
3822 This
->firstPropertyNode
,
3827 assert(rootProperty
.sizeOfNameString
!=0);
3830 * Push the search node in the search stack.
3832 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.dirProperty
);
3838 HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
3839 IEnumSTATSTG
* iface
,
3840 IEnumSTATSTG
** ppenum
)
3842 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3844 IEnumSTATSTGImpl
* newClone
;
3847 * Perform a sanity check on the parameters.
3850 return E_INVALIDARG
;
3852 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
3853 This
->firstPropertyNode
);
3857 * The new clone enumeration must point to the same current node as
3860 newClone
->stackSize
= This
->stackSize
;
3861 newClone
->stackMaxSize
= This
->stackMaxSize
;
3862 newClone
->stackToVisit
=
3863 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * newClone
->stackMaxSize
);
3866 newClone
->stackToVisit
,
3868 sizeof(ULONG
) * newClone
->stackSize
);
3870 *ppenum
= (IEnumSTATSTG
*)newClone
;
3873 * Don't forget to nail down a reference to the clone before
3876 IEnumSTATSTGImpl_AddRef(*ppenum
);
3881 INT
IEnumSTATSTGImpl_FindParentProperty(
3882 IEnumSTATSTGImpl
*This
,
3883 ULONG childProperty
,
3884 StgProperty
*currentProperty
,
3887 ULONG currentSearchNode
;
3891 * To avoid the special case, get another pointer to a ULONG value if
3892 * the caller didn't supply one.
3895 thisNodeId
= &foundNode
;
3898 * Start with the node at the top of the stack.
3900 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3903 while (currentSearchNode
!=PROPERTY_NULL
)
3906 * Store the current node in the returned parameters
3908 *thisNodeId
= currentSearchNode
;
3911 * Remove the top node from the stack
3913 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3916 * Read the property from the storage.
3918 StorageImpl_ReadProperty(
3919 This
->parentStorage
,
3923 if (currentProperty
->previousProperty
== childProperty
)
3924 return PROPERTY_RELATION_PREVIOUS
;
3926 else if (currentProperty
->nextProperty
== childProperty
)
3927 return PROPERTY_RELATION_NEXT
;
3929 else if (currentProperty
->dirProperty
== childProperty
)
3930 return PROPERTY_RELATION_DIR
;
3933 * Push the next search node in the search stack.
3935 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
3938 * continue the iteration.
3940 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3943 return PROPERTY_NULL
;
3946 ULONG
IEnumSTATSTGImpl_FindProperty(
3947 IEnumSTATSTGImpl
* This
,
3948 const OLECHAR
* lpszPropName
,
3949 StgProperty
* currentProperty
)
3951 ULONG currentSearchNode
;
3954 * Start with the node at the top of the stack.
3956 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3958 while (currentSearchNode
!=PROPERTY_NULL
)
3961 * Remove the top node from the stack
3963 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3966 * Read the property from the storage.
3968 StorageImpl_ReadProperty(This
->parentStorage
,
3972 if ( propertyNameCmp(
3973 (OLECHAR
*)currentProperty
->name
,
3974 (OLECHAR
*)lpszPropName
) == 0)
3975 return currentSearchNode
;
3978 * Push the next search node in the search stack.
3980 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
3983 * continue the iteration.
3985 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3988 return PROPERTY_NULL
;
3991 void IEnumSTATSTGImpl_PushSearchNode(
3992 IEnumSTATSTGImpl
* This
,
3995 StgProperty rootProperty
;
3996 BOOL readSuccessful
;
3999 * First, make sure we're not trying to push an unexisting node.
4001 if (nodeToPush
==PROPERTY_NULL
)
4005 * First push the node to the stack
4007 if (This
->stackSize
== This
->stackMaxSize
)
4009 This
->stackMaxSize
+= ENUMSTATSGT_SIZE_INCREMENT
;
4011 This
->stackToVisit
= HeapReAlloc(
4015 sizeof(ULONG
) * This
->stackMaxSize
);
4018 This
->stackToVisit
[This
->stackSize
] = nodeToPush
;
4022 * Read the root property from the storage.
4024 readSuccessful
= StorageImpl_ReadProperty(
4025 This
->parentStorage
,
4031 assert(rootProperty
.sizeOfNameString
!=0);
4034 * Push the previous search node in the search stack.
4036 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.previousProperty
);
4040 ULONG
IEnumSTATSTGImpl_PopSearchNode(
4041 IEnumSTATSTGImpl
* This
,
4046 if (This
->stackSize
== 0)
4047 return PROPERTY_NULL
;
4049 topNode
= This
->stackToVisit
[This
->stackSize
-1];
4057 /******************************************************************************
4058 ** StorageUtl implementation
4061 void StorageUtl_ReadWord(void* buffer
, ULONG offset
, WORD
* value
)
4063 memcpy(value
, (BYTE
*)buffer
+offset
, sizeof(WORD
));
4066 void StorageUtl_WriteWord(void* buffer
, ULONG offset
, WORD value
)
4068 memcpy((BYTE
*)buffer
+offset
, &value
, sizeof(WORD
));
4071 void StorageUtl_ReadDWord(void* buffer
, ULONG offset
, DWORD
* value
)
4073 memcpy(value
, (BYTE
*)buffer
+offset
, sizeof(DWORD
));
4076 void StorageUtl_WriteDWord(void* buffer
, ULONG offset
, DWORD value
)
4078 memcpy((BYTE
*)buffer
+offset
, &value
, sizeof(DWORD
));
4081 void StorageUtl_ReadGUID(void* buffer
, ULONG offset
, GUID
* value
)
4083 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
4084 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
4085 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
4087 memcpy(value
->Data4
, (BYTE
*)buffer
+offset
+8, sizeof(value
->Data4
));
4090 void StorageUtl_WriteGUID(void* buffer
, ULONG offset
, GUID
* value
)
4092 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
4093 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
4094 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
4096 memcpy((BYTE
*)buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
4099 void StorageUtl_CopyPropertyToSTATSTG(
4100 STATSTG
* destination
,
4101 StgProperty
* source
,
4105 * The copy of the string occurs only when the flag is not set
4107 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
4108 (source
->name
== NULL
) ||
4109 (source
->name
[0] == 0) )
4111 destination
->pwcsName
= 0;
4115 destination
->pwcsName
=
4116 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
4118 strcpyW((LPWSTR
)destination
->pwcsName
, source
->name
);
4121 switch (source
->propertyType
)
4123 case PROPTYPE_STORAGE
:
4125 destination
->type
= STGTY_STORAGE
;
4127 case PROPTYPE_STREAM
:
4128 destination
->type
= STGTY_STREAM
;
4131 destination
->type
= STGTY_STREAM
;
4135 destination
->cbSize
= source
->size
;
4137 currentReturnStruct->mtime = {0}; TODO
4138 currentReturnStruct->ctime = {0};
4139 currentReturnStruct->atime = {0};
4141 destination
->grfMode
= 0;
4142 destination
->grfLocksSupported
= 0;
4143 destination
->clsid
= source
->propertyUniqueID
;
4144 destination
->grfStateBits
= 0;
4145 destination
->reserved
= 0;
4148 /******************************************************************************
4149 ** BlockChainStream implementation
4152 BlockChainStream
* BlockChainStream_Construct(
4153 StorageImpl
* parentStorage
,
4154 ULONG
* headOfStreamPlaceHolder
,
4155 ULONG propertyIndex
)
4157 BlockChainStream
* newStream
;
4160 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
4162 newStream
->parentStorage
= parentStorage
;
4163 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
4164 newStream
->ownerPropertyIndex
= propertyIndex
;
4165 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
4166 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
4167 newStream
->numBlocks
= 0;
4169 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
4171 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4173 newStream
->numBlocks
++;
4174 newStream
->tailIndex
= blockIndex
;
4176 if(FAILED(StorageImpl_GetNextBlockInChain(
4181 HeapFree(GetProcessHeap(), 0, newStream
);
4189 void BlockChainStream_Destroy(BlockChainStream
* This
)
4191 HeapFree(GetProcessHeap(), 0, This
);
4194 /******************************************************************************
4195 * BlockChainStream_GetHeadOfChain
4197 * Returns the head of this stream chain.
4198 * Some special chains don't have properties, their heads are kept in
4199 * This->headOfStreamPlaceHolder.
4202 ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
4204 StgProperty chainProperty
;
4205 BOOL readSuccessful
;
4207 if (This
->headOfStreamPlaceHolder
!= 0)
4208 return *(This
->headOfStreamPlaceHolder
);
4210 if (This
->ownerPropertyIndex
!= PROPERTY_NULL
)
4212 readSuccessful
= StorageImpl_ReadProperty(
4213 This
->parentStorage
,
4214 This
->ownerPropertyIndex
,
4219 return chainProperty
.startingBlock
;
4223 return BLOCK_END_OF_CHAIN
;
4226 /******************************************************************************
4227 * BlockChainStream_GetCount
4229 * Returns the number of blocks that comprises this chain.
4230 * This is not the size of the stream as the last block may not be full!
4233 ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
4238 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4240 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4244 if(FAILED(StorageImpl_GetNextBlockInChain(
4245 This
->parentStorage
,
4254 /******************************************************************************
4255 * BlockChainStream_ReadAt
4257 * Reads a specified number of bytes from this chain at the specified offset.
4258 * bytesRead may be NULL.
4259 * Failure will be returned if the specified number of bytes has not been read.
4261 BOOL
BlockChainStream_ReadAt(BlockChainStream
* This
,
4262 ULARGE_INTEGER offset
,
4267 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4268 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4269 ULONG bytesToReadInBuffer
;
4272 BYTE
* bigBlockBuffer
;
4275 * Find the first block in the stream that contains part of the buffer.
4277 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4278 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4279 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4281 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4282 This
->lastBlockNoInSequence
= blockNoInSequence
;
4286 ULONG temp
= blockNoInSequence
;
4288 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4289 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4290 This
->lastBlockNoInSequence
= temp
;
4293 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4295 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4297 blockNoInSequence
--;
4300 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4303 * Start reading the buffer.
4306 bufferWalker
= buffer
;
4308 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4311 * Calculate how many bytes we can copy from this big block.
4313 bytesToReadInBuffer
=
4314 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4317 * Copy those bytes to the buffer
4320 StorageImpl_GetROBigBlock(This
->parentStorage
, blockIndex
);
4322 memcpy(bufferWalker
, bigBlockBuffer
+ offsetInBlock
, bytesToReadInBuffer
);
4324 StorageImpl_ReleaseBigBlock(This
->parentStorage
, bigBlockBuffer
);
4327 * Step to the next big block.
4329 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4332 bufferWalker
+= bytesToReadInBuffer
;
4333 size
-= bytesToReadInBuffer
;
4334 *bytesRead
+= bytesToReadInBuffer
;
4335 offsetInBlock
= 0; /* There is no offset on the next block */
4342 /******************************************************************************
4343 * BlockChainStream_WriteAt
4345 * Writes the specified number of bytes to this chain at the specified offset.
4346 * bytesWritten may be NULL.
4347 * Will fail if not all specified number of bytes have been written.
4349 BOOL
BlockChainStream_WriteAt(BlockChainStream
* This
,
4350 ULARGE_INTEGER offset
,
4353 ULONG
* bytesWritten
)
4355 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4356 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4360 BYTE
* bigBlockBuffer
;
4363 * Find the first block in the stream that contains part of the buffer.
4365 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4366 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4367 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4369 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4370 This
->lastBlockNoInSequence
= blockNoInSequence
;
4374 ULONG temp
= blockNoInSequence
;
4376 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4377 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4378 This
->lastBlockNoInSequence
= temp
;
4381 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4383 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4386 blockNoInSequence
--;
4389 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4392 * Here, I'm casting away the constness on the buffer variable
4393 * This is OK since we don't intend to modify that buffer.
4396 bufferWalker
= (BYTE
*)buffer
;
4398 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4401 * Calculate how many bytes we can copy from this big block.
4404 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4407 * Copy those bytes to the buffer
4409 bigBlockBuffer
= StorageImpl_GetBigBlock(This
->parentStorage
, blockIndex
);
4411 memcpy(bigBlockBuffer
+ offsetInBlock
, bufferWalker
, bytesToWrite
);
4413 StorageImpl_ReleaseBigBlock(This
->parentStorage
, bigBlockBuffer
);
4416 * Step to the next big block.
4418 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4421 bufferWalker
+= bytesToWrite
;
4422 size
-= bytesToWrite
;
4423 *bytesWritten
+= bytesToWrite
;
4424 offsetInBlock
= 0; /* There is no offset on the next block */
4430 /******************************************************************************
4431 * BlockChainStream_Shrink
4433 * Shrinks this chain in the big block depot.
4435 BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
4436 ULARGE_INTEGER newSize
)
4438 ULONG blockIndex
, extraBlock
;
4443 * Reset the last accessed block cache.
4445 This
->lastBlockNoInSequence
= 0xFFFFFFFF;
4446 This
->lastBlockNoInSequenceIndex
= BLOCK_END_OF_CHAIN
;
4449 * Figure out how many blocks are needed to contain the new size
4451 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4453 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4456 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4459 * Go to the new end of chain
4461 while (count
< numBlocks
)
4463 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4469 /* Get the next block before marking the new end */
4470 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4474 /* Mark the new end of chain */
4475 StorageImpl_SetNextBlockInChain(
4476 This
->parentStorage
,
4478 BLOCK_END_OF_CHAIN
);
4480 This
->tailIndex
= blockIndex
;
4481 This
->numBlocks
= numBlocks
;
4484 * Mark the extra blocks as free
4486 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4488 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
,
4491 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
4492 extraBlock
= blockIndex
;
4498 /******************************************************************************
4499 * BlockChainStream_Enlarge
4501 * Grows this chain in the big block depot.
4503 BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
4504 ULARGE_INTEGER newSize
)
4506 ULONG blockIndex
, currentBlock
;
4508 ULONG oldNumBlocks
= 0;
4510 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4513 * Empty chain. Create the head.
4515 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4517 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4518 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
4520 BLOCK_END_OF_CHAIN
);
4522 if (This
->headOfStreamPlaceHolder
!= 0)
4524 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
4528 StgProperty chainProp
;
4529 assert(This
->ownerPropertyIndex
!= PROPERTY_NULL
);
4531 StorageImpl_ReadProperty(
4532 This
->parentStorage
,
4533 This
->ownerPropertyIndex
,
4536 chainProp
.startingBlock
= blockIndex
;
4538 StorageImpl_WriteProperty(
4539 This
->parentStorage
,
4540 This
->ownerPropertyIndex
,
4544 This
->tailIndex
= blockIndex
;
4545 This
->numBlocks
= 1;
4549 * Figure out how many blocks are needed to contain this stream
4551 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4553 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4557 * Go to the current end of chain
4559 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
4561 currentBlock
= blockIndex
;
4563 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4566 currentBlock
= blockIndex
;
4568 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
4573 This
->tailIndex
= currentBlock
;
4576 currentBlock
= This
->tailIndex
;
4577 oldNumBlocks
= This
->numBlocks
;
4580 * Add new blocks to the chain
4582 if (oldNumBlocks
< newNumBlocks
)
4584 while (oldNumBlocks
< newNumBlocks
)
4586 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4588 StorageImpl_SetNextBlockInChain(
4589 This
->parentStorage
,
4593 StorageImpl_SetNextBlockInChain(
4594 This
->parentStorage
,
4596 BLOCK_END_OF_CHAIN
);
4598 currentBlock
= blockIndex
;
4602 This
->tailIndex
= blockIndex
;
4603 This
->numBlocks
= newNumBlocks
;
4609 /******************************************************************************
4610 * BlockChainStream_SetSize
4612 * Sets the size of this stream. The big block depot will be updated.
4613 * The file will grow if we grow the chain.
4615 * TODO: Free the actual blocks in the file when we shrink the chain.
4616 * Currently, the blocks are still in the file. So the file size
4617 * doesn't shrink even if we shrink streams.
4619 BOOL
BlockChainStream_SetSize(
4620 BlockChainStream
* This
,
4621 ULARGE_INTEGER newSize
)
4623 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
4625 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
4628 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
4630 BlockChainStream_Shrink(This
, newSize
);
4634 ULARGE_INTEGER fileSize
=
4635 BIGBLOCKFILE_GetSize(This
->parentStorage
->bigBlockFile
);
4637 ULONG diff
= newSize
.u
.LowPart
- size
.u
.LowPart
;
4640 * Make sure the file stays a multiple of blocksize
4642 if ((diff
% This
->parentStorage
->bigBlockSize
) != 0)
4643 diff
+= (This
->parentStorage
->bigBlockSize
-
4644 (diff
% This
->parentStorage
->bigBlockSize
) );
4646 fileSize
.u
.LowPart
+= diff
;
4647 BIGBLOCKFILE_SetSize(This
->parentStorage
->bigBlockFile
, fileSize
);
4649 BlockChainStream_Enlarge(This
, newSize
);
4655 /******************************************************************************
4656 * BlockChainStream_GetSize
4658 * Returns the size of this chain.
4659 * Will return the block count if this chain doesn't have a property.
4661 ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
4663 StgProperty chainProperty
;
4665 if(This
->headOfStreamPlaceHolder
== NULL
)
4668 * This chain is a data stream read the property and return
4669 * the appropriate size
4671 StorageImpl_ReadProperty(
4672 This
->parentStorage
,
4673 This
->ownerPropertyIndex
,
4676 return chainProperty
.size
;
4681 * this chain is a chain that does not have a property, figure out the
4682 * size by making the product number of used blocks times the
4685 ULARGE_INTEGER result
;
4686 result
.u
.HighPart
= 0;
4689 BlockChainStream_GetCount(This
) *
4690 This
->parentStorage
->bigBlockSize
;
4696 /******************************************************************************
4697 ** SmallBlockChainStream implementation
4700 SmallBlockChainStream
* SmallBlockChainStream_Construct(
4701 StorageImpl
* parentStorage
,
4702 ULONG propertyIndex
)
4704 SmallBlockChainStream
* newStream
;
4706 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
4708 newStream
->parentStorage
= parentStorage
;
4709 newStream
->ownerPropertyIndex
= propertyIndex
;
4714 void SmallBlockChainStream_Destroy(
4715 SmallBlockChainStream
* This
)
4717 HeapFree(GetProcessHeap(), 0, This
);
4720 /******************************************************************************
4721 * SmallBlockChainStream_GetHeadOfChain
4723 * Returns the head of this chain of small blocks.
4725 ULONG
SmallBlockChainStream_GetHeadOfChain(
4726 SmallBlockChainStream
* This
)
4728 StgProperty chainProperty
;
4729 BOOL readSuccessful
;
4731 if (This
->ownerPropertyIndex
)
4733 readSuccessful
= StorageImpl_ReadProperty(
4734 This
->parentStorage
,
4735 This
->ownerPropertyIndex
,
4740 return chainProperty
.startingBlock
;
4745 return BLOCK_END_OF_CHAIN
;
4748 /******************************************************************************
4749 * SmallBlockChainStream_GetNextBlockInChain
4751 * Returns the index of the next small block in this chain.
4754 * - BLOCK_END_OF_CHAIN: end of this chain
4755 * - BLOCK_UNUSED: small block 'blockIndex' is free
4757 HRESULT
SmallBlockChainStream_GetNextBlockInChain(
4758 SmallBlockChainStream
* This
,
4760 ULONG
* nextBlockInChain
)
4762 ULARGE_INTEGER offsetOfBlockInDepot
;
4767 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
4769 offsetOfBlockInDepot
.u
.HighPart
= 0;
4770 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4773 * Read those bytes in the buffer from the small block file.
4775 success
= BlockChainStream_ReadAt(
4776 This
->parentStorage
->smallBlockDepotChain
,
4777 offsetOfBlockInDepot
,
4784 StorageUtl_ReadDWord(&buffer
, 0, nextBlockInChain
);
4788 return STG_E_READFAULT
;
4791 /******************************************************************************
4792 * SmallBlockChainStream_SetNextBlockInChain
4794 * Writes the index of the next block of the specified block in the small
4796 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4797 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4799 void SmallBlockChainStream_SetNextBlockInChain(
4800 SmallBlockChainStream
* This
,
4804 ULARGE_INTEGER offsetOfBlockInDepot
;
4808 offsetOfBlockInDepot
.u
.HighPart
= 0;
4809 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4811 StorageUtl_WriteDWord(&buffer
, 0, nextBlock
);
4814 * Read those bytes in the buffer from the small block file.
4816 BlockChainStream_WriteAt(
4817 This
->parentStorage
->smallBlockDepotChain
,
4818 offsetOfBlockInDepot
,
4824 /******************************************************************************
4825 * SmallBlockChainStream_FreeBlock
4827 * Flag small block 'blockIndex' as free in the small block depot.
4829 void SmallBlockChainStream_FreeBlock(
4830 SmallBlockChainStream
* This
,
4833 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
4836 /******************************************************************************
4837 * SmallBlockChainStream_GetNextFreeBlock
4839 * Returns the index of a free small block. The small block depot will be
4840 * enlarged if necessary. The small block chain will also be enlarged if
4843 ULONG
SmallBlockChainStream_GetNextFreeBlock(
4844 SmallBlockChainStream
* This
)
4846 ULARGE_INTEGER offsetOfBlockInDepot
;
4849 ULONG blockIndex
= 0;
4850 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
4851 BOOL success
= TRUE
;
4852 ULONG smallBlocksPerBigBlock
;
4854 offsetOfBlockInDepot
.u
.HighPart
= 0;
4857 * Scan the small block depot for a free block
4859 while (nextBlockIndex
!= BLOCK_UNUSED
)
4861 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4863 success
= BlockChainStream_ReadAt(
4864 This
->parentStorage
->smallBlockDepotChain
,
4865 offsetOfBlockInDepot
,
4871 * If we run out of space for the small block depot, enlarge it
4875 StorageUtl_ReadDWord(&buffer
, 0, &nextBlockIndex
);
4877 if (nextBlockIndex
!= BLOCK_UNUSED
)
4883 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
4885 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
4886 ULONG nextBlock
, newsbdIndex
;
4887 BYTE
* smallBlockDepot
;
4889 nextBlock
= sbdIndex
;
4890 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
4892 sbdIndex
= nextBlock
;
4893 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
, &nextBlock
);
4896 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4897 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
4898 StorageImpl_SetNextBlockInChain(
4899 This
->parentStorage
,
4903 StorageImpl_SetNextBlockInChain(
4904 This
->parentStorage
,
4906 BLOCK_END_OF_CHAIN
);
4909 * Initialize all the small blocks to free
4912 StorageImpl_GetBigBlock(This
->parentStorage
, newsbdIndex
);
4914 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
4915 StorageImpl_ReleaseBigBlock(This
->parentStorage
, smallBlockDepot
);
4920 * We have just created the small block depot.
4922 StgProperty rootProp
;
4926 * Save it in the header
4928 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
4929 StorageImpl_SaveFileHeader(This
->parentStorage
);
4932 * And allocate the first big block that will contain small blocks
4935 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4937 StorageImpl_SetNextBlockInChain(
4938 This
->parentStorage
,
4940 BLOCK_END_OF_CHAIN
);
4942 StorageImpl_ReadProperty(
4943 This
->parentStorage
,
4944 This
->parentStorage
->rootPropertySetIndex
,
4947 rootProp
.startingBlock
= sbStartIndex
;
4948 rootProp
.size
.u
.HighPart
= 0;
4949 rootProp
.size
.u
.LowPart
= This
->parentStorage
->bigBlockSize
;
4951 StorageImpl_WriteProperty(
4952 This
->parentStorage
,
4953 This
->parentStorage
->rootPropertySetIndex
,
4959 smallBlocksPerBigBlock
=
4960 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
4963 * Verify if we have to allocate big blocks to contain small blocks
4965 if (blockIndex
% smallBlocksPerBigBlock
== 0)
4967 StgProperty rootProp
;
4968 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
4970 StorageImpl_ReadProperty(
4971 This
->parentStorage
,
4972 This
->parentStorage
->rootPropertySetIndex
,
4975 if (rootProp
.size
.u
.LowPart
<
4976 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
4978 rootProp
.size
.u
.LowPart
+= This
->parentStorage
->bigBlockSize
;
4980 BlockChainStream_SetSize(
4981 This
->parentStorage
->smallBlockRootChain
,
4984 StorageImpl_WriteProperty(
4985 This
->parentStorage
,
4986 This
->parentStorage
->rootPropertySetIndex
,
4994 /******************************************************************************
4995 * SmallBlockChainStream_ReadAt
4997 * Reads a specified number of bytes from this chain at the specified offset.
4998 * bytesRead may be NULL.
4999 * Failure will be returned if the specified number of bytes has not been read.
5001 BOOL
SmallBlockChainStream_ReadAt(
5002 SmallBlockChainStream
* This
,
5003 ULARGE_INTEGER offset
,
5008 ULARGE_INTEGER offsetInBigBlockFile
;
5009 ULONG blockNoInSequence
=
5010 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5012 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5013 ULONG bytesToReadInBuffer
;
5015 ULONG bytesReadFromBigBlockFile
;
5019 * This should never happen on a small block file.
5021 assert(offset
.u
.HighPart
==0);
5024 * Find the first block in the stream that contains part of the buffer.
5026 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5028 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5030 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5033 blockNoInSequence
--;
5037 * Start reading the buffer.
5040 bufferWalker
= buffer
;
5042 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5045 * Calculate how many bytes we can copy from this small block.
5047 bytesToReadInBuffer
=
5048 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5051 * Calculate the offset of the small block in the small block file.
5053 offsetInBigBlockFile
.u
.HighPart
= 0;
5054 offsetInBigBlockFile
.u
.LowPart
=
5055 blockIndex
* This
->parentStorage
->smallBlockSize
;
5057 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5060 * Read those bytes in the buffer from the small block file.
5062 BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
5063 offsetInBigBlockFile
,
5064 bytesToReadInBuffer
,
5066 &bytesReadFromBigBlockFile
);
5068 assert(bytesReadFromBigBlockFile
== bytesToReadInBuffer
);
5071 * Step to the next big block.
5073 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5075 bufferWalker
+= bytesToReadInBuffer
;
5076 size
-= bytesToReadInBuffer
;
5077 *bytesRead
+= bytesToReadInBuffer
;
5078 offsetInBlock
= 0; /* There is no offset on the next block */
5084 /******************************************************************************
5085 * SmallBlockChainStream_WriteAt
5087 * Writes the specified number of bytes to this chain at the specified offset.
5088 * bytesWritten may be NULL.
5089 * Will fail if not all specified number of bytes have been written.
5091 BOOL
SmallBlockChainStream_WriteAt(
5092 SmallBlockChainStream
* This
,
5093 ULARGE_INTEGER offset
,
5096 ULONG
* bytesWritten
)
5098 ULARGE_INTEGER offsetInBigBlockFile
;
5099 ULONG blockNoInSequence
=
5100 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5102 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5103 ULONG bytesToWriteInBuffer
;
5105 ULONG bytesWrittenFromBigBlockFile
;
5109 * This should never happen on a small block file.
5111 assert(offset
.u
.HighPart
==0);
5114 * Find the first block in the stream that contains part of the buffer.
5116 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5118 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5120 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5122 blockNoInSequence
--;
5126 * Start writing the buffer.
5128 * Here, I'm casting away the constness on the buffer variable
5129 * This is OK since we don't intend to modify that buffer.
5132 bufferWalker
= (BYTE
*)buffer
;
5133 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5136 * Calculate how many bytes we can copy to this small block.
5138 bytesToWriteInBuffer
=
5139 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5142 * Calculate the offset of the small block in the small block file.
5144 offsetInBigBlockFile
.u
.HighPart
= 0;
5145 offsetInBigBlockFile
.u
.LowPart
=
5146 blockIndex
* This
->parentStorage
->smallBlockSize
;
5148 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5151 * Write those bytes in the buffer to the small block file.
5153 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockRootChain
,
5154 offsetInBigBlockFile
,
5155 bytesToWriteInBuffer
,
5157 &bytesWrittenFromBigBlockFile
);
5159 assert(bytesWrittenFromBigBlockFile
== bytesToWriteInBuffer
);
5162 * Step to the next big block.
5164 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5167 bufferWalker
+= bytesToWriteInBuffer
;
5168 size
-= bytesToWriteInBuffer
;
5169 *bytesWritten
+= bytesToWriteInBuffer
;
5170 offsetInBlock
= 0; /* There is no offset on the next block */
5176 /******************************************************************************
5177 * SmallBlockChainStream_Shrink
5179 * Shrinks this chain in the small block depot.
5181 BOOL
SmallBlockChainStream_Shrink(
5182 SmallBlockChainStream
* This
,
5183 ULARGE_INTEGER newSize
)
5185 ULONG blockIndex
, extraBlock
;
5189 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5191 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5194 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5197 * Go to the new end of chain
5199 while (count
< numBlocks
)
5201 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5208 * If the count is 0, we have a special case, the head of the chain was
5213 StgProperty chainProp
;
5215 StorageImpl_ReadProperty(This
->parentStorage
,
5216 This
->ownerPropertyIndex
,
5219 chainProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
5221 StorageImpl_WriteProperty(This
->parentStorage
,
5222 This
->ownerPropertyIndex
,
5226 * We start freeing the chain at the head block.
5228 extraBlock
= blockIndex
;
5232 /* Get the next block before marking the new end */
5233 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5237 /* Mark the new end of chain */
5238 SmallBlockChainStream_SetNextBlockInChain(
5241 BLOCK_END_OF_CHAIN
);
5245 * Mark the extra blocks as free
5247 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
5249 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
5252 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
5253 extraBlock
= blockIndex
;
5259 /******************************************************************************
5260 * SmallBlockChainStream_Enlarge
5262 * Grows this chain in the small block depot.
5264 BOOL
SmallBlockChainStream_Enlarge(
5265 SmallBlockChainStream
* This
,
5266 ULARGE_INTEGER newSize
)
5268 ULONG blockIndex
, currentBlock
;
5270 ULONG oldNumBlocks
= 0;
5272 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5277 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5280 StgProperty chainProp
;
5282 StorageImpl_ReadProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
5285 chainProp
.startingBlock
= SmallBlockChainStream_GetNextFreeBlock(This
);
5287 StorageImpl_WriteProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
5290 blockIndex
= chainProp
.startingBlock
;
5291 SmallBlockChainStream_SetNextBlockInChain(
5294 BLOCK_END_OF_CHAIN
);
5297 currentBlock
= blockIndex
;
5300 * Figure out how many blocks are needed to contain this stream
5302 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5304 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5308 * Go to the current end of chain
5310 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5313 currentBlock
= blockIndex
;
5314 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
5319 * Add new blocks to the chain
5321 while (oldNumBlocks
< newNumBlocks
)
5323 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
5324 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
5326 SmallBlockChainStream_SetNextBlockInChain(
5329 BLOCK_END_OF_CHAIN
);
5331 currentBlock
= blockIndex
;
5338 /******************************************************************************
5339 * SmallBlockChainStream_GetCount
5341 * Returns the number of blocks that comprises this chain.
5342 * This is not the size of this chain as the last block may not be full!
5344 ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
5349 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5351 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5355 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5362 /******************************************************************************
5363 * SmallBlockChainStream_SetSize
5365 * Sets the size of this stream.
5366 * The file will grow if we grow the chain.
5368 * TODO: Free the actual blocks in the file when we shrink the chain.
5369 * Currently, the blocks are still in the file. So the file size
5370 * doesn't shrink even if we shrink streams.
5372 BOOL
SmallBlockChainStream_SetSize(
5373 SmallBlockChainStream
* This
,
5374 ULARGE_INTEGER newSize
)
5376 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
5378 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
5381 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
5383 SmallBlockChainStream_Shrink(This
, newSize
);
5387 SmallBlockChainStream_Enlarge(This
, newSize
);
5393 /******************************************************************************
5394 * SmallBlockChainStream_GetSize
5396 * Returns the size of this chain.
5398 ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
5400 StgProperty chainProperty
;
5402 StorageImpl_ReadProperty(
5403 This
->parentStorage
,
5404 This
->ownerPropertyIndex
,
5407 return chainProperty
.size
;
5410 /******************************************************************************
5411 * StgCreateDocfile [OLE32.@]
5413 HRESULT WINAPI
StgCreateDocfile(
5417 IStorage
**ppstgOpen
)
5419 StorageImpl
* newStorage
= 0;
5420 HANDLE hFile
= INVALID_HANDLE_VALUE
;
5425 DWORD fileAttributes
;
5426 WCHAR tempFileName
[MAX_PATH
];
5428 TRACE("(%s, %lx, %ld, %p)\n",
5429 debugstr_w(pwcsName
), grfMode
,
5430 reserved
, ppstgOpen
);
5433 * Validate the parameters
5436 return STG_E_INVALIDPOINTER
;
5439 * Validate the STGM flags
5441 if ( FAILED( validateSTGM(grfMode
) ))
5442 return STG_E_INVALIDFLAG
;
5445 * Generate a unique name.
5449 WCHAR tempPath
[MAX_PATH
];
5450 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
5452 if (!(grfMode
& STGM_SHARE_EXCLUSIVE
))
5453 return STG_E_INVALIDFLAG
;
5454 if (!(grfMode
& (STGM_WRITE
|STGM_READWRITE
)))
5455 return STG_E_INVALIDFLAG
;
5457 memset(tempPath
, 0, sizeof(tempPath
));
5458 memset(tempFileName
, 0, sizeof(tempFileName
));
5460 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
5463 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
5464 pwcsName
= tempFileName
;
5466 return STG_E_INSUFFICIENTMEMORY
;
5468 creationMode
= TRUNCATE_EXISTING
;
5472 creationMode
= GetCreationModeFromSTGM(grfMode
);
5476 * Interpret the STGM value grfMode
5478 shareMode
= GetShareModeFromSTGM(grfMode
);
5479 accessMode
= GetAccessModeFromSTGM(grfMode
);
5481 if (grfMode
& STGM_DELETEONRELEASE
)
5482 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
5484 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
5486 if (grfMode
& STGM_TRANSACTED
)
5487 FIXME("Transacted mode not implemented.\n");
5490 * Initialize the "out" parameter.
5494 hFile
= CreateFileW(pwcsName
,
5502 if (hFile
== INVALID_HANDLE_VALUE
)
5508 * Allocate and initialize the new IStorage32object.
5510 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5512 if (newStorage
== 0)
5513 return STG_E_INSUFFICIENTMEMORY
;
5515 hr
= StorageImpl_Construct(
5526 HeapFree(GetProcessHeap(), 0, newStorage
);
5531 * Get an "out" pointer for the caller.
5533 hr
= StorageBaseImpl_QueryInterface(
5534 (IStorage
*)newStorage
,
5535 (REFIID
)&IID_IStorage
,
5541 /******************************************************************************
5542 * StgCreateStorageEx [OLE32.@]
5544 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
5546 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
5547 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
5548 return STG_E_UNIMPLEMENTEDFUNCTION
;
5551 /******************************************************************************
5552 * StgOpenStorage [OLE32.@]
5554 HRESULT WINAPI
StgOpenStorage(
5555 const OLECHAR
*pwcsName
,
5556 IStorage
*pstgPriority
,
5560 IStorage
**ppstgOpen
)
5562 StorageImpl
* newStorage
= 0;
5567 WCHAR fullname
[MAX_PATH
];
5570 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5571 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
5572 snbExclude
, reserved
, ppstgOpen
);
5575 * Perform a sanity check
5577 if (( pwcsName
== 0) || (ppstgOpen
== 0) )
5579 hr
= STG_E_INVALIDPOINTER
;
5584 * Validate the STGM flags
5586 if ( FAILED( validateSTGM(grfMode
) ))
5588 hr
= STG_E_INVALIDFLAG
;
5593 * Interpret the STGM value grfMode
5595 shareMode
= GetShareModeFromSTGM(grfMode
);
5596 accessMode
= GetAccessModeFromSTGM(grfMode
);
5599 * Initialize the "out" parameter.
5603 hFile
= CreateFileW( pwcsName
,
5608 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
5611 length
= GetFileSize(hFile
, NULL
);
5613 if (hFile
==INVALID_HANDLE_VALUE
)
5615 DWORD last_error
= GetLastError();
5621 case ERROR_FILE_NOT_FOUND
:
5622 hr
= STG_E_FILENOTFOUND
;
5625 case ERROR_PATH_NOT_FOUND
:
5626 hr
= STG_E_PATHNOTFOUND
;
5629 case ERROR_ACCESS_DENIED
:
5630 case ERROR_WRITE_PROTECT
:
5631 hr
= STG_E_ACCESSDENIED
;
5634 case ERROR_SHARING_VIOLATION
:
5635 hr
= STG_E_SHAREVIOLATION
;
5646 * Allocate and initialize the new IStorage32object.
5648 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5650 if (newStorage
== 0)
5652 hr
= STG_E_INSUFFICIENTMEMORY
;
5656 /* if the file's length was zero, initialize the storage */
5657 hr
= StorageImpl_Construct(
5668 HeapFree(GetProcessHeap(), 0, newStorage
);
5670 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5672 if(hr
== STG_E_INVALIDHEADER
)
5673 hr
= STG_E_FILEALREADYEXISTS
;
5677 /* prepare the file name string given in lieu of the root property name */
5678 GetFullPathNameW(pwcsName
, MAX_PATH
, fullname
, NULL
);
5679 memcpy(newStorage
->filename
, fullname
, PROPERTY_NAME_BUFFER_LEN
);
5680 newStorage
->filename
[PROPERTY_NAME_BUFFER_LEN
-1] = '\0';
5683 * Get an "out" pointer for the caller.
5685 hr
= StorageBaseImpl_QueryInterface(
5686 (IStorage
*)newStorage
,
5687 (REFIID
)&IID_IStorage
,
5691 TRACE("<-- %08lx, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
5695 /******************************************************************************
5696 * StgCreateDocfileOnILockBytes [OLE32.@]
5698 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
5702 IStorage
** ppstgOpen
)
5704 StorageImpl
* newStorage
= 0;
5708 * Validate the parameters
5710 if ((ppstgOpen
== 0) || (plkbyt
== 0))
5711 return STG_E_INVALIDPOINTER
;
5714 * Allocate and initialize the new IStorage object.
5716 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5718 if (newStorage
== 0)
5719 return STG_E_INSUFFICIENTMEMORY
;
5721 hr
= StorageImpl_Construct(
5732 HeapFree(GetProcessHeap(), 0, newStorage
);
5737 * Get an "out" pointer for the caller.
5739 hr
= StorageBaseImpl_QueryInterface(
5740 (IStorage
*)newStorage
,
5741 (REFIID
)&IID_IStorage
,
5747 /******************************************************************************
5748 * StgOpenStorageOnILockBytes [OLE32.@]
5750 HRESULT WINAPI
StgOpenStorageOnILockBytes(
5752 IStorage
*pstgPriority
,
5756 IStorage
**ppstgOpen
)
5758 StorageImpl
* newStorage
= 0;
5762 * Perform a sanity check
5764 if ((plkbyt
== 0) || (ppstgOpen
== 0))
5765 return STG_E_INVALIDPOINTER
;
5768 * Validate the STGM flags
5770 if ( FAILED( validateSTGM(grfMode
) ))
5771 return STG_E_INVALIDFLAG
;
5774 * Initialize the "out" parameter.
5779 * Allocate and initialize the new IStorage object.
5781 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5783 if (newStorage
== 0)
5784 return STG_E_INSUFFICIENTMEMORY
;
5786 hr
= StorageImpl_Construct(
5797 HeapFree(GetProcessHeap(), 0, newStorage
);
5802 * Get an "out" pointer for the caller.
5804 hr
= StorageBaseImpl_QueryInterface(
5805 (IStorage
*)newStorage
,
5806 (REFIID
)&IID_IStorage
,
5812 /******************************************************************************
5813 * StgSetTimes [ole32.@]
5814 * StgSetTimes [OLE32.@]
5818 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *a
,
5819 FILETIME
const *b
, FILETIME
const *c
)
5821 FIXME("(%s, %p, %p, %p),stub!\n", debugstr_w(str
), a
, b
, c
);
5825 /******************************************************************************
5826 * StgIsStorageILockBytes [OLE32.@]
5828 * Determines if the ILockBytes contains a storage object.
5830 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
5833 ULARGE_INTEGER offset
;
5835 offset
.u
.HighPart
= 0;
5836 offset
.u
.LowPart
= 0;
5838 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
5840 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
5846 /******************************************************************************
5847 * WriteClassStg [OLE32.@]
5849 * This method will store the specified CLSID in the specified storage object
5851 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
5857 hRes
= IStorage_SetClass(pStg
, rclsid
);
5862 /***********************************************************************
5863 * ReadClassStg (OLE32.@)
5865 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5867 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
5877 * read a STATSTG structure (contains the clsid) from the storage
5879 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_DEFAULT
);
5882 *pclsid
=pstatstg
.clsid
;
5887 /***********************************************************************
5888 * OleLoadFromStream (OLE32.@)
5890 * This function loads an object from stream
5892 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
5896 LPPERSISTSTREAM xstm
;
5898 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
5900 res
=ReadClassStm(pStm
,&clsid
);
5901 if (!SUCCEEDED(res
))
5903 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
5904 if (!SUCCEEDED(res
))
5906 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
5907 if (!SUCCEEDED(res
)) {
5908 IUnknown_Release((IUnknown
*)*ppvObj
);
5911 res
=IPersistStream_Load(xstm
,pStm
);
5912 IPersistStream_Release(xstm
);
5913 /* FIXME: all refcounts ok at this point? I think they should be:
5916 * xstm : 0 (released)
5921 /***********************************************************************
5922 * OleSaveToStream (OLE32.@)
5924 * This function saves an object with the IPersistStream interface on it
5925 * to the specified stream.
5927 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
5933 TRACE("(%p,%p)\n",pPStm
,pStm
);
5935 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
5937 if (SUCCEEDED(res
)){
5939 res
=WriteClassStm(pStm
,&clsid
);
5943 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
5946 TRACE("Finished Save\n");
5950 /****************************************************************************
5951 * This method validate a STGM parameter that can contain the values below
5953 * STGM_DIRECT 0x00000000
5954 * STGM_TRANSACTED 0x00010000
5955 * STGM_SIMPLE 0x08000000
5957 * STGM_READ 0x00000000
5958 * STGM_WRITE 0x00000001
5959 * STGM_READWRITE 0x00000002
5961 * STGM_SHARE_DENY_NONE 0x00000040
5962 * STGM_SHARE_DENY_READ 0x00000030
5963 * STGM_SHARE_DENY_WRITE 0x00000020
5964 * STGM_SHARE_EXCLUSIVE 0x00000010
5966 * STGM_PRIORITY 0x00040000
5967 * STGM_DELETEONRELEASE 0x04000000
5969 * STGM_CREATE 0x00001000
5970 * STGM_CONVERT 0x00020000
5971 * STGM_FAILIFTHERE 0x00000000
5973 * STGM_NOSCRATCH 0x00100000
5974 * STGM_NOSNAPSHOT 0x00200000
5976 static HRESULT
validateSTGM(DWORD stgm
)
5978 BOOL bSTGM_TRANSACTED
= ((stgm
& STGM_TRANSACTED
) == STGM_TRANSACTED
);
5979 BOOL bSTGM_SIMPLE
= ((stgm
& STGM_SIMPLE
) == STGM_SIMPLE
);
5980 BOOL bSTGM_DIRECT
= ! (bSTGM_TRANSACTED
|| bSTGM_SIMPLE
);
5982 BOOL bSTGM_WRITE
= ((stgm
& STGM_WRITE
) == STGM_WRITE
);
5983 BOOL bSTGM_READWRITE
= ((stgm
& STGM_READWRITE
) == STGM_READWRITE
);
5984 BOOL bSTGM_READ
= ! (bSTGM_WRITE
|| bSTGM_READWRITE
);
5986 BOOL bSTGM_SHARE_DENY_NONE
=
5987 ((stgm
& STGM_SHARE_DENY_NONE
) == STGM_SHARE_DENY_NONE
);
5989 BOOL bSTGM_SHARE_DENY_READ
=
5990 ((stgm
& STGM_SHARE_DENY_READ
) == STGM_SHARE_DENY_READ
);
5992 BOOL bSTGM_SHARE_DENY_WRITE
=
5993 ((stgm
& STGM_SHARE_DENY_WRITE
) == STGM_SHARE_DENY_WRITE
);
5995 BOOL bSTGM_SHARE_EXCLUSIVE
=
5996 ((stgm
& STGM_SHARE_EXCLUSIVE
) == STGM_SHARE_EXCLUSIVE
);
5998 BOOL bSTGM_CREATE
= ((stgm
& STGM_CREATE
) == STGM_CREATE
);
5999 BOOL bSTGM_CONVERT
= ((stgm
& STGM_CONVERT
) == STGM_CONVERT
);
6001 BOOL bSTGM_NOSCRATCH
= ((stgm
& STGM_NOSCRATCH
) == STGM_NOSCRATCH
);
6002 BOOL bSTGM_NOSNAPSHOT
= ((stgm
& STGM_NOSNAPSHOT
) == STGM_NOSNAPSHOT
);
6005 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6007 if ( ! bSTGM_DIRECT
)
6008 if( bSTGM_TRANSACTED
&& bSTGM_SIMPLE
)
6012 * STGM_WRITE | STGM_READWRITE | STGM_READ
6015 if( bSTGM_WRITE
&& bSTGM_READWRITE
)
6019 * STGM_SHARE_DENY_NONE | others
6020 * (I assume here that DENY_READ implies DENY_WRITE)
6022 if ( bSTGM_SHARE_DENY_NONE
)
6023 if ( bSTGM_SHARE_DENY_READ
||
6024 bSTGM_SHARE_DENY_WRITE
||
6025 bSTGM_SHARE_EXCLUSIVE
)
6029 * STGM_CREATE | STGM_CONVERT
6030 * if both are false, STGM_FAILIFTHERE is set to TRUE
6032 if ( bSTGM_CREATE
&& bSTGM_CONVERT
)
6036 * STGM_NOSCRATCH requires STGM_TRANSACTED
6038 if ( bSTGM_NOSCRATCH
&& ! bSTGM_TRANSACTED
)
6042 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6043 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6045 if (bSTGM_NOSNAPSHOT
)
6047 if ( ! ( bSTGM_TRANSACTED
&&
6048 !(bSTGM_SHARE_EXCLUSIVE
|| bSTGM_SHARE_DENY_WRITE
)) )
6055 /****************************************************************************
6056 * GetShareModeFromSTGM
6058 * This method will return a share mode flag from a STGM value.
6059 * The STGM value is assumed valid.
6061 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
6063 DWORD dwShareMode
= 0;
6064 BOOL bSTGM_SHARE_DENY_NONE
=
6065 ((stgm
& STGM_SHARE_DENY_NONE
) == STGM_SHARE_DENY_NONE
);
6067 BOOL bSTGM_SHARE_DENY_READ
=
6068 ((stgm
& STGM_SHARE_DENY_READ
) == STGM_SHARE_DENY_READ
);
6070 BOOL bSTGM_SHARE_DENY_WRITE
=
6071 ((stgm
& STGM_SHARE_DENY_WRITE
) == STGM_SHARE_DENY_WRITE
);
6073 BOOL bSTGM_SHARE_EXCLUSIVE
=
6074 ((stgm
& STGM_SHARE_EXCLUSIVE
) == STGM_SHARE_EXCLUSIVE
);
6076 if ((bSTGM_SHARE_EXCLUSIVE
) || (bSTGM_SHARE_DENY_READ
))
6079 if (bSTGM_SHARE_DENY_NONE
)
6080 dwShareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6082 if (bSTGM_SHARE_DENY_WRITE
)
6083 dwShareMode
= FILE_SHARE_READ
;
6088 /****************************************************************************
6089 * GetAccessModeFromSTGM
6091 * This method will return an access mode flag from a STGM value.
6092 * The STGM value is assumed valid.
6094 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
6096 DWORD dwDesiredAccess
= GENERIC_READ
;
6097 BOOL bSTGM_WRITE
= ((stgm
& STGM_WRITE
) == STGM_WRITE
);
6098 BOOL bSTGM_READWRITE
= ((stgm
& STGM_READWRITE
) == STGM_READWRITE
);
6099 BOOL bSTGM_READ
= ! (bSTGM_WRITE
|| bSTGM_READWRITE
);
6102 dwDesiredAccess
= GENERIC_READ
;
6105 dwDesiredAccess
|= GENERIC_WRITE
;
6107 if (bSTGM_READWRITE
)
6108 dwDesiredAccess
= GENERIC_READ
| GENERIC_WRITE
;
6110 return dwDesiredAccess
;
6113 /****************************************************************************
6114 * GetCreationModeFromSTGM
6116 * This method will return a creation mode flag from a STGM value.
6117 * The STGM value is assumed valid.
6119 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
6121 if ( stgm
& STGM_CREATE
)
6122 return CREATE_ALWAYS
;
6123 if (stgm
& STGM_CONVERT
) {
6124 FIXME("STGM_CONVERT not implemented!\n");
6127 /* All other cases */
6128 if (stgm
& ~ (STGM_CREATE
|STGM_CONVERT
))
6129 FIXME("unhandled storage mode : 0x%08lx\n",stgm
& ~ (STGM_CREATE
|STGM_CONVERT
));
6134 /*************************************************************************
6135 * OLECONVERT_LoadOLE10 [Internal]
6137 * Loads the OLE10 STREAM to memory
6140 * pOleStream [I] The OLESTREAM
6141 * pData [I] Data Structure for the OLESTREAM Data
6145 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6146 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6149 * This function is used by OleConvertOLESTREAMToIStorage only.
6151 * Memory allocated for pData must be freed by the caller
6153 HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
6156 HRESULT hRes
= S_OK
;
6160 pData
->pData
= NULL
;
6161 pData
->pstrOleObjFileName
= (CHAR
*) NULL
;
6163 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
6166 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6167 if(dwSize
!= sizeof(pData
->dwOleID
))
6169 hRes
= CONVERT10_E_OLESTREAM_GET
;
6171 else if(pData
->dwOleID
!= OLESTREAM_ID
)
6173 hRes
= CONVERT10_E_OLESTREAM_FMT
;
6184 /* Get the TypeID...more info needed for this field */
6185 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6186 if(dwSize
!= sizeof(pData
->dwTypeID
))
6188 hRes
= CONVERT10_E_OLESTREAM_GET
;
6193 if(pData
->dwTypeID
!= 0)
6195 /* Get the length of the OleTypeName */
6196 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6197 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6199 hRes
= CONVERT10_E_OLESTREAM_GET
;
6204 if(pData
->dwOleTypeNameLength
> 0)
6206 /* Get the OleTypeName */
6207 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6208 if(dwSize
!= pData
->dwOleTypeNameLength
)
6210 hRes
= CONVERT10_E_OLESTREAM_GET
;
6216 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
6217 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
6219 hRes
= CONVERT10_E_OLESTREAM_GET
;
6223 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
6224 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
6225 pData
->pstrOleObjFileName
= (CHAR
*)HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
6226 if(pData
->pstrOleObjFileName
)
6228 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)(pData
->pstrOleObjFileName
),pData
->dwOleObjFileNameLength
);
6229 if(dwSize
!= pData
->dwOleObjFileNameLength
)
6231 hRes
= CONVERT10_E_OLESTREAM_GET
;
6235 hRes
= CONVERT10_E_OLESTREAM_GET
;
6240 /* Get the Width of the Metafile */
6241 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6242 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6244 hRes
= CONVERT10_E_OLESTREAM_GET
;
6248 /* Get the Height of the Metafile */
6249 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6250 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6252 hRes
= CONVERT10_E_OLESTREAM_GET
;
6258 /* Get the Length of the Data */
6259 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6260 if(dwSize
!= sizeof(pData
->dwDataLength
))
6262 hRes
= CONVERT10_E_OLESTREAM_GET
;
6266 if(hRes
== S_OK
) /* I don't know what is this 8 byts information is we have to figure out */
6268 if(!bStrem1
) /* if it is a second OLE stream data */
6270 pData
->dwDataLength
-= 8;
6271 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)(pData
->strUnknown
), sizeof(pData
->strUnknown
));
6272 if(dwSize
!= sizeof(pData
->strUnknown
))
6274 hRes
= CONVERT10_E_OLESTREAM_GET
;
6280 if(pData
->dwDataLength
> 0)
6282 pData
->pData
= (BYTE
*)HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
6284 /* Get Data (ex. IStorage, Metafile, or BMP) */
6287 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
6288 if(dwSize
!= pData
->dwDataLength
)
6290 hRes
= CONVERT10_E_OLESTREAM_GET
;
6295 hRes
= CONVERT10_E_OLESTREAM_GET
;
6304 /*************************************************************************
6305 * OLECONVERT_SaveOLE10 [Internal]
6307 * Saves the OLE10 STREAM From memory
6310 * pData [I] Data Structure for the OLESTREAM Data
6311 * pOleStream [I] The OLESTREAM to save
6315 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6318 * This function is used by OleConvertIStorageToOLESTREAM only.
6321 HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
6324 HRESULT hRes
= S_OK
;
6328 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6329 if(dwSize
!= sizeof(pData
->dwOleID
))
6331 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6336 /* Set the TypeID */
6337 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6338 if(dwSize
!= sizeof(pData
->dwTypeID
))
6340 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6344 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
6346 /* Set the Length of the OleTypeName */
6347 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6348 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6350 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6355 if(pData
->dwOleTypeNameLength
> 0)
6357 /* Set the OleTypeName */
6358 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6359 if(dwSize
!= pData
->dwOleTypeNameLength
)
6361 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6368 /* Set the width of the Metafile */
6369 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6370 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6372 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6378 /* Set the height of the Metafile */
6379 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6380 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6382 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6388 /* Set the length of the Data */
6389 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6390 if(dwSize
!= sizeof(pData
->dwDataLength
))
6392 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6398 if(pData
->dwDataLength
> 0)
6400 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6401 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
6402 if(dwSize
!= pData
->dwDataLength
)
6404 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6412 /*************************************************************************
6413 * OLECONVERT_GetOLE20FromOLE10[Internal]
6415 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6416 * opens it, and copies the content to the dest IStorage for
6417 * OleConvertOLESTREAMToIStorage
6421 * pDestStorage [I] The IStorage to copy the data to
6422 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6423 * nBufferLength [I] The size of the buffer
6432 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, BYTE
*pBuffer
, DWORD nBufferLength
)
6436 IStorage
*pTempStorage
;
6437 DWORD dwNumOfBytesWritten
;
6438 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6439 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6441 /* Create a temp File */
6442 GetTempPathW(MAX_PATH
, wstrTempDir
);
6443 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6444 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
6446 if(hFile
!= INVALID_HANDLE_VALUE
)
6448 /* Write IStorage Data to File */
6449 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
6452 /* Open and copy temp storage to the Dest Storage */
6453 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
6456 hRes
= StorageImpl_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
6457 StorageBaseImpl_Release(pTempStorage
);
6459 DeleteFileW(wstrTempFile
);
6464 /*************************************************************************
6465 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6467 * Saves the OLE10 STREAM From memory
6470 * pStorage [I] The Src IStorage to copy
6471 * pData [I] The Dest Memory to write to.
6474 * The size in bytes allocated for pData
6477 * Memory allocated for pData must be freed by the caller
6479 * Used by OleConvertIStorageToOLESTREAM only.
6482 DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
6486 DWORD nDataLength
= 0;
6487 IStorage
*pTempStorage
;
6488 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6489 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6493 /* Create temp Storage */
6494 GetTempPathW(MAX_PATH
, wstrTempDir
);
6495 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6496 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
6500 /* Copy Src Storage to the Temp Storage */
6501 StorageImpl_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
6502 StorageBaseImpl_Release(pTempStorage
);
6504 /* Open Temp Storage as a file and copy to memory */
6505 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
6506 if(hFile
!= INVALID_HANDLE_VALUE
)
6508 nDataLength
= GetFileSize(hFile
, NULL
);
6509 *pData
= (BYTE
*) HeapAlloc(GetProcessHeap(),0,nDataLength
);
6510 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
6513 DeleteFileW(wstrTempFile
);
6518 /*************************************************************************
6519 * OLECONVERT_CreateOleStream [Internal]
6521 * Creates the "\001OLE" stream in the IStorage if necessary.
6524 * pStorage [I] Dest storage to create the stream in
6530 * This function is used by OleConvertOLESTREAMToIStorage only.
6532 * This stream is still unknown, MS Word seems to have extra data
6533 * but since the data is stored in the OLESTREAM there should be
6534 * no need to recreate the stream. If the stream is manually
6535 * deleted it will create it with this default data.
6538 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
6542 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
6543 BYTE pOleStreamHeader
[] =
6545 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6546 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6547 0x00, 0x00, 0x00, 0x00
6550 /* Create stream if not present */
6551 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
6552 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
6556 /* Write default Data */
6557 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
6558 IStream_Release(pStream
);
6562 /* write a string to a stream, preceded by its length */
6563 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
6570 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
6571 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
6576 str
= CoTaskMemAlloc( len
);
6577 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
6578 r
= IStream_Write( stm
, str
, len
, NULL
);
6579 CoTaskMemFree( str
);
6583 /* read a string preceded by its length from a stream */
6584 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
6587 DWORD len
, count
= 0;
6591 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
6594 if( count
!= sizeof(len
) )
6595 return E_OUTOFMEMORY
;
6597 TRACE("%ld bytes\n",len
);
6599 str
= CoTaskMemAlloc( len
);
6601 return E_OUTOFMEMORY
;
6603 r
= IStream_Read( stm
, str
, len
, &count
);
6608 CoTaskMemFree( str
);
6609 return E_OUTOFMEMORY
;
6612 TRACE("Read string %s\n",debugstr_an(str
,len
));
6614 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
6615 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
6617 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
6618 CoTaskMemFree( str
);
6626 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
6627 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
6631 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6633 static const BYTE unknown1
[12] =
6634 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6635 0xFF, 0xFF, 0xFF, 0xFF};
6636 static const BYTE unknown2
[16] =
6637 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6638 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6640 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
6641 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
6642 debugstr_w(szProgIDName
));
6644 /* Create a CompObj stream if it doesn't exist */
6645 r
= IStorage_CreateStream(pstg
, szwStreamName
,
6646 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
6650 /* Write CompObj Structure to stream */
6651 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
6653 if( SUCCEEDED( r
) )
6654 r
= WriteClassStm( pstm
, clsid
);
6656 if( SUCCEEDED( r
) )
6657 r
= STREAM_WriteString( pstm
, lpszUserType
);
6658 if( SUCCEEDED( r
) )
6659 r
= STREAM_WriteString( pstm
, szClipName
);
6660 if( SUCCEEDED( r
) )
6661 r
= STREAM_WriteString( pstm
, szProgIDName
);
6662 if( SUCCEEDED( r
) )
6663 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
6665 IStream_Release( pstm
);
6670 /* enumerate HKEY_CLASSES_ROOT\\CLSID looking for a CLSID whose name matches */
6671 static HRESULT
CLSIDFromUserType(LPCWSTR lpszUserType
, CLSID
*clsid
)
6673 LONG r
, count
, i
, len
;
6675 HKEY hkey
, hkeyclsid
;
6676 LPWSTR buffer
= NULL
;
6678 static const WCHAR szclsid
[] = { 'C','L','S','I','D',0 };
6680 TRACE("Finding CLSID for %s\n", debugstr_w(lpszUserType
));
6682 r
= RegOpenKeyW( HKEY_CLASSES_ROOT
, szclsid
, &hkeyclsid
);
6684 return E_INVALIDARG
;
6686 len
= lstrlenW( lpszUserType
) + 1;
6687 buffer
= CoTaskMemAlloc( len
* sizeof (WCHAR
) );
6691 for(i
=0; !found
; i
++ )
6693 r
= RegEnumKeyW( hkeyclsid
, i
, szKey
, sizeof(szKey
)/sizeof(WCHAR
));
6694 if( r
!= ERROR_SUCCESS
)
6697 r
= RegOpenKeyW( hkeyclsid
, szKey
, &hkey
);
6698 if( r
!= ERROR_SUCCESS
)
6700 count
= len
* sizeof (WCHAR
);
6701 r
= RegQueryValueW( hkey
, NULL
, buffer
, &count
);
6702 found
= ( r
== ERROR_SUCCESS
) &&
6703 ( count
== len
*sizeof(WCHAR
) ) &&
6704 !lstrcmpW( buffer
, lpszUserType
) ;
6705 RegCloseKey( hkey
);
6710 CoTaskMemFree( buffer
);
6711 RegCloseKey( hkeyclsid
);
6714 return E_INVALIDARG
;
6716 TRACE("clsid is %s\n", debugstr_w( szKey
) );
6718 r
= CLSIDFromString( szKey
, clsid
);
6724 /***********************************************************************
6725 * WriteFmtUserTypeStg (OLE32.@)
6727 HRESULT WINAPI
WriteFmtUserTypeStg(
6728 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
6731 WCHAR szwClipName
[0x40];
6732 WCHAR szCLSIDName
[OLESTREAM_MAX_STR_LEN
];
6736 LPMALLOC allocator
= NULL
;
6738 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
6740 r
= CoGetMalloc(0, &allocator
);
6742 return E_OUTOFMEMORY
;
6744 /* get the clipboard format name */
6745 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
) );
6748 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
6752 r
= CLSIDFromUserType(lpszUserType
, &clsid
);
6756 TRACE("CLSID is %s\n",debugstr_guid(&clsid
));
6758 /* get the real program ID */
6759 r
= ProgIDFromCLSID( &clsid
, &wstrProgID
);
6763 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
6765 /* if we have a good string, write the stream */
6767 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
6768 lpszUserType
, szwClipName
, wstrProgID
);
6772 IMalloc_Free( allocator
, wstrProgID
);
6778 /******************************************************************************
6779 * ReadFmtUserTypeStg [OLE32.@]
6781 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
6785 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
6786 unsigned char unknown1
[12];
6787 unsigned char unknown2
[16];
6789 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
6792 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
6794 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
6795 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
6798 ERR("Failed to open stream\n");
6802 /* read the various parts of the structure */
6803 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
6804 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
6806 r
= ReadClassStm( stm
, &clsid
);
6810 r
= STREAM_ReadString( stm
, &szCLSIDName
);
6814 r
= STREAM_ReadString( stm
, &szOleTypeName
);
6818 r
= STREAM_ReadString( stm
, &szProgIDName
);
6822 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
6823 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
6826 /* ok, success... now we just need to store what we found */
6828 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
6829 CoTaskMemFree( szOleTypeName
);
6831 if( lplpszUserType
)
6832 *lplpszUserType
= szCLSIDName
;
6833 CoTaskMemFree( szProgIDName
);
6836 IStream_Release( stm
);
6842 /*************************************************************************
6843 * OLECONVERT_CreateCompObjStream [Internal]
6845 * Creates a "\001CompObj" is the destination IStorage if necessary.
6848 * pStorage [I] The dest IStorage to create the CompObj Stream
6850 * strOleTypeName [I] The ProgID
6854 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6857 * This function is used by OleConvertOLESTREAMToIStorage only.
6859 * The stream data is stored in the OLESTREAM and there should be
6860 * no need to recreate the stream. If the stream is manually
6861 * deleted it will attempt to create it by querying the registry.
6865 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
6868 HRESULT hStorageRes
, hRes
= S_OK
;
6869 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
6870 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6871 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
6873 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6874 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
6876 /* Initialize the CompObj structure */
6877 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
6878 memcpy(&(IStorageCompObj
.byUnknown1
), pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
6879 memcpy(&(IStorageCompObj
.byUnknown2
), pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
6882 /* Create a CompObj stream if it doesn't exist */
6883 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
6884 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
6885 if(hStorageRes
== S_OK
)
6887 /* copy the OleTypeName to the compobj struct */
6888 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
6889 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
6891 /* copy the OleTypeName to the compobj struct */
6892 /* Note: in the test made, these were Identical */
6893 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
6894 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
6897 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
6898 bufferW
, OLESTREAM_MAX_STR_LEN
);
6899 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
6905 /* Get the CLSID Default Name from the Registry */
6906 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
6907 if(hErr
== ERROR_SUCCESS
)
6909 char strTemp
[OLESTREAM_MAX_STR_LEN
];
6910 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
6911 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, &(IStorageCompObj
.dwCLSIDNameLength
));
6912 if(hErr
== ERROR_SUCCESS
)
6914 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
6920 /* Write CompObj Structure to stream */
6921 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
6923 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
6925 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
6926 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
6928 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
6930 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
6931 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
6933 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
6935 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
6936 if(IStorageCompObj
.dwProgIDNameLength
> 0)
6938 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
6940 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
6941 IStream_Release(pStream
);
6947 /*************************************************************************
6948 * OLECONVERT_CreateOlePresStream[Internal]
6950 * Creates the "\002OlePres000" Stream with the Metafile data
6953 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6954 * dwExtentX [I] Width of the Metafile
6955 * dwExtentY [I] Height of the Metafile
6956 * pData [I] Metafile data
6957 * dwDataLength [I] Size of the Metafile data
6961 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6964 * This function is used by OleConvertOLESTREAMToIStorage only.
6967 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
6971 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6972 BYTE pOlePresStreamHeader
[] =
6974 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6975 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6976 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6977 0x00, 0x00, 0x00, 0x00
6980 BYTE pOlePresStreamHeaderEmpty
[] =
6982 0x00, 0x00, 0x00, 0x00,
6983 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6984 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6985 0x00, 0x00, 0x00, 0x00
6988 /* Create the OlePres000 Stream */
6989 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
6990 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
6995 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
6997 memset(&OlePres
, 0, sizeof(OlePres
));
6998 /* Do we have any metafile data to save */
6999 if(dwDataLength
> 0)
7001 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
7002 nHeaderSize
= sizeof(pOlePresStreamHeader
);
7006 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
7007 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
7009 /* Set width and height of the metafile */
7010 OlePres
.dwExtentX
= dwExtentX
;
7011 OlePres
.dwExtentY
= -dwExtentY
;
7013 /* Set Data and Length */
7014 if(dwDataLength
> sizeof(METAFILEPICT16
))
7016 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
7017 OlePres
.pData
= &(pData
[8]);
7019 /* Save OlePres000 Data to Stream */
7020 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
7021 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
7022 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
7023 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
7024 if(OlePres
.dwSize
> 0)
7026 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
7028 IStream_Release(pStream
);
7032 /*************************************************************************
7033 * OLECONVERT_CreateOle10NativeStream [Internal]
7035 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7038 * pStorage [I] Dest storage to create the stream in
7039 * pData [I] Ole10 Native Data (ex. bmp)
7040 * dwDataLength [I] Size of the Ole10 Native Data
7046 * This function is used by OleConvertOLESTREAMToIStorage only.
7048 * Might need to verify the data and return appropriate error message
7051 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, BYTE
*pData
, DWORD dwDataLength
)
7055 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7057 /* Create the Ole10Native Stream */
7058 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7059 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7063 /* Write info to stream */
7064 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
7065 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
7066 IStream_Release(pStream
);
7071 /*************************************************************************
7072 * OLECONVERT_GetOLE10ProgID [Internal]
7074 * Finds the ProgID (or OleTypeID) from the IStorage
7077 * pStorage [I] The Src IStorage to get the ProgID
7078 * strProgID [I] the ProgID string to get
7079 * dwSize [I] the size of the string
7083 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7086 * This function is used by OleConvertIStorageToOLESTREAM only.
7090 HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
7094 LARGE_INTEGER iSeekPos
;
7095 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
7096 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7098 /* Open the CompObj Stream */
7099 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7100 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7104 /*Get the OleType from the CompObj Stream */
7105 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
7106 iSeekPos
.u
.HighPart
= 0;
7108 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7109 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
7110 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
7111 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7112 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
7113 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
7114 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7116 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
7119 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
7121 IStream_Release(pStream
);
7126 LPOLESTR wstrProgID
;
7128 /* Get the OleType from the registry */
7129 REFCLSID clsid
= &(stat
.clsid
);
7130 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
7131 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
7134 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
7141 /*************************************************************************
7142 * OLECONVERT_GetOle10PresData [Internal]
7144 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7147 * pStorage [I] Src IStroage
7148 * pOleStream [I] Dest OleStream Mem Struct
7154 * This function is used by OleConvertIStorageToOLESTREAM only.
7156 * Memory allocated for pData must be freed by the caller
7160 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7165 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7167 /* Initialize Default data for OLESTREAM */
7168 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7169 pOleStreamData
[0].dwTypeID
= 2;
7170 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7171 pOleStreamData
[1].dwTypeID
= 0;
7172 pOleStreamData
[0].dwMetaFileWidth
= 0;
7173 pOleStreamData
[0].dwMetaFileHeight
= 0;
7174 pOleStreamData
[0].pData
= NULL
;
7175 pOleStreamData
[1].pData
= NULL
;
7177 /* Open Ole10Native Stream */
7178 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7179 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7183 /* Read Size and Data */
7184 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
7185 if(pOleStreamData
->dwDataLength
> 0)
7187 pOleStreamData
->pData
= (LPSTR
) HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
7188 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
7190 IStream_Release(pStream
);
7196 /*************************************************************************
7197 * OLECONVERT_GetOle20PresData[Internal]
7199 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7202 * pStorage [I] Src IStroage
7203 * pOleStreamData [I] Dest OleStream Mem Struct
7209 * This function is used by OleConvertIStorageToOLESTREAM only.
7211 * Memory allocated for pData must be freed by the caller
7213 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7217 OLECONVERT_ISTORAGE_OLEPRES olePress
;
7218 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7220 /* Initialize Default data for OLESTREAM */
7221 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7222 pOleStreamData
[0].dwTypeID
= 2;
7223 pOleStreamData
[0].dwMetaFileWidth
= 0;
7224 pOleStreamData
[0].dwMetaFileHeight
= 0;
7225 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
7226 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7227 pOleStreamData
[1].dwTypeID
= 0;
7228 pOleStreamData
[1].dwOleTypeNameLength
= 0;
7229 pOleStreamData
[1].strOleTypeName
[0] = 0;
7230 pOleStreamData
[1].dwMetaFileWidth
= 0;
7231 pOleStreamData
[1].dwMetaFileHeight
= 0;
7232 pOleStreamData
[1].pData
= NULL
;
7233 pOleStreamData
[1].dwDataLength
= 0;
7236 /* Open OlePress000 stream */
7237 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7238 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7241 LARGE_INTEGER iSeekPos
;
7242 METAFILEPICT16 MetaFilePict
;
7243 static const char strMetafilePictName
[] = "METAFILEPICT";
7245 /* Set the TypeID for a Metafile */
7246 pOleStreamData
[1].dwTypeID
= 5;
7248 /* Set the OleTypeName to Metafile */
7249 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
7250 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
7252 iSeekPos
.u
.HighPart
= 0;
7253 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
7255 /* Get Presentation Data */
7256 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7257 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
7258 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
7259 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
7261 /*Set width and Height */
7262 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
7263 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
7264 if(olePress
.dwSize
> 0)
7267 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
7269 /* Set MetaFilePict struct */
7270 MetaFilePict
.mm
= 8;
7271 MetaFilePict
.xExt
= olePress
.dwExtentX
;
7272 MetaFilePict
.yExt
= olePress
.dwExtentY
;
7273 MetaFilePict
.hMF
= 0;
7275 /* Get Metafile Data */
7276 pOleStreamData
[1].pData
= (BYTE
*) HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
7277 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
7278 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
7280 IStream_Release(pStream
);
7284 /*************************************************************************
7285 * OleConvertOLESTREAMToIStorage [OLE32.@]
7290 * DVTARGETDEVICE paramenter is not handled
7291 * Still unsure of some mem fields for OLE 10 Stream
7292 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7293 * and "\001OLE" streams
7296 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
7297 LPOLESTREAM pOleStream
,
7299 const DVTARGETDEVICE
* ptd
)
7303 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7305 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7309 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7312 if(pstg
== NULL
|| pOleStream
== NULL
)
7314 hRes
= E_INVALIDARG
;
7319 /* Load the OLESTREAM to Memory */
7320 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
7325 /* Load the OLESTREAM to Memory (part 2)*/
7326 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
7332 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
7334 /* Do we have the IStorage Data in the OLESTREAM */
7335 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
7337 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7338 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
7342 /* It must be an original OLE 1.0 source */
7343 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7348 /* It must be an original OLE 1.0 source */
7349 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7352 /* Create CompObj Stream if necessary */
7353 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
7356 /*Create the Ole Stream if necessary */
7357 OLECONVERT_CreateOleStream(pstg
);
7362 /* Free allocated memory */
7363 for(i
=0; i
< 2; i
++)
7365 if(pOleStreamData
[i
].pData
!= NULL
)
7367 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7369 if(pOleStreamData
[i
].pstrOleObjFileName
!= NULL
)
7371 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
7372 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
7378 /*************************************************************************
7379 * OleConvertIStorageToOLESTREAM [OLE32.@]
7386 * Still unsure of some mem fields for OLE 10 Stream
7387 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7388 * and "\001OLE" streams.
7391 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
7393 LPOLESTREAM pOleStream
)
7396 HRESULT hRes
= S_OK
;
7398 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7399 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7402 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7404 if(pstg
== NULL
|| pOleStream
== NULL
)
7406 hRes
= E_INVALIDARG
;
7410 /* Get the ProgID */
7411 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
7412 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
7416 /* Was it originally Ole10 */
7417 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7420 IStream_Release(pStream
);
7421 /* Get Presentation Data for Ole10Native */
7422 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
7426 /* Get Presentation Data (OLE20) */
7427 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
7430 /* Save OLESTREAM */
7431 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
7434 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
7439 /* Free allocated memory */
7440 for(i
=0; i
< 2; i
++)
7442 if(pOleStreamData
[i
].pData
!= NULL
)
7444 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7451 /***********************************************************************
7452 * GetConvertStg (OLE32.@)
7454 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
7455 FIXME("unimplemented stub!\n");
7459 /******************************************************************************
7460 * StgIsStorageFile [OLE32.@]
7463 StgIsStorageFile(LPCOLESTR fn
)
7469 TRACE("(\'%s\')\n", debugstr_w(fn
));
7470 hf
= CreateFileW(fn
, GENERIC_READ
,
7471 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
7472 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
7474 if (hf
== INVALID_HANDLE_VALUE
)
7475 return STG_E_FILENOTFOUND
;
7477 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
7479 WARN(" unable to read file\n");
7486 if (bytes_read
!= 8) {
7487 WARN(" too short\n");
7491 if (!memcmp(magic
,STORAGE_magic
,8)) {
7496 WARN(" -> Invalid header.\n");