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
32 #define NONAMELESSUNION
33 #define NONAMELESSSTRUCT
34 #include "winbase.h" /* for lstrlenW() and the likes */
36 #include "wine/unicode.h"
37 #include "wine/debug.h"
39 #include "storage32.h"
40 #include "ole2.h" /* For Write/ReadClassStm */
43 #include "wine/wingdi16.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(storage);
50 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
51 #define OLESTREAM_ID 0x501
52 #define OLESTREAM_MAX_STR_LEN 255
54 static const char rootPropertyName[] = "Root Entry";
57 /* OLESTREAM memory structure to use for Get and Put Routines */
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
63 DWORD dwOleTypeNameLength;
64 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
65 CHAR *pstrOleObjFileName;
66 DWORD dwOleObjFileNameLength;
67 DWORD dwMetaFileWidth;
68 DWORD dwMetaFileHeight;
69 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
72 }OLECONVERT_OLESTREAM_DATA;
74 /* CompObj Stream structure */
75 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
80 DWORD dwCLSIDNameLength;
81 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
82 DWORD dwOleTypeNameLength;
83 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
84 DWORD dwProgIDNameLength;
85 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
87 }OLECONVERT_ISTORAGE_COMPOBJ;
90 /* Ole Presention Stream structure */
91 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
99 }OLECONVERT_ISTORAGE_OLEPRES;
103 /***********************************************************************
104 * Forward declaration of internal functions used by the method DestroyElement
106 static HRESULT deleteStorageProperty(
107 StorageImpl *parentStorage,
108 ULONG foundPropertyIndexToDelete,
109 StgProperty propertyToDelete);
111 static HRESULT deleteStreamProperty(
112 StorageImpl *parentStorage,
113 ULONG foundPropertyIndexToDelete,
114 StgProperty propertyToDelete);
116 static HRESULT findPlaceholder(
117 StorageImpl *storage,
118 ULONG propertyIndexToStore,
119 ULONG storagePropertyIndex,
122 static HRESULT adjustPropertyChain(
124 StgProperty propertyToDelete,
125 StgProperty parentProperty,
126 ULONG parentPropertyId,
129 /***********************************************************************
130 * Declaration of the functions used to manipulate StgProperty
133 static ULONG getFreeProperty(
134 StorageImpl *storage);
136 static void updatePropertyChain(
137 StorageImpl *storage,
138 ULONG newPropertyIndex,
139 StgProperty newProperty);
141 static LONG propertyNameCmp(
142 OLECHAR *newProperty,
143 OLECHAR *currentProperty);
146 /***********************************************************************
147 * Declaration of miscellaneous functions...
149 static HRESULT validateSTGM(DWORD stgmValue);
151 static DWORD GetShareModeFromSTGM(DWORD stgm);
152 static DWORD GetAccessModeFromSTGM(DWORD stgm);
153 static DWORD GetCreationModeFromSTGM(DWORD stgm);
156 * Virtual function table for the IStorage32Impl class.
158 static ICOM_VTABLE(IStorage) Storage32Impl_Vtbl =
160 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
161 StorageBaseImpl_QueryInterface,
162 StorageBaseImpl_AddRef,
163 StorageBaseImpl_Release,
164 StorageBaseImpl_CreateStream,
165 StorageBaseImpl_OpenStream,
166 StorageImpl_CreateStorage,
167 StorageBaseImpl_OpenStorage,
169 StorageImpl_MoveElementTo,
172 StorageBaseImpl_EnumElements,
173 StorageImpl_DestroyElement,
174 StorageBaseImpl_RenameElement,
175 StorageImpl_SetElementTimes,
176 StorageBaseImpl_SetClass,
177 StorageImpl_SetStateBits,
182 * Virtual function table for the Storage32InternalImpl class.
184 static ICOM_VTABLE(IStorage) Storage32InternalImpl_Vtbl =
186 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
187 StorageBaseImpl_QueryInterface,
188 StorageBaseImpl_AddRef,
189 StorageBaseImpl_Release,
190 StorageBaseImpl_CreateStream,
191 StorageBaseImpl_OpenStream,
192 StorageImpl_CreateStorage,
193 StorageBaseImpl_OpenStorage,
195 StorageImpl_MoveElementTo,
196 StorageInternalImpl_Commit,
197 StorageInternalImpl_Revert,
198 StorageBaseImpl_EnumElements,
199 StorageImpl_DestroyElement,
200 StorageBaseImpl_RenameElement,
201 StorageImpl_SetElementTimes,
202 StorageBaseImpl_SetClass,
203 StorageImpl_SetStateBits,
208 * Virtual function table for the IEnumSTATSTGImpl class.
210 static ICOM_VTABLE(IEnumSTATSTG) IEnumSTATSTGImpl_Vtbl =
212 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
213 IEnumSTATSTGImpl_QueryInterface,
214 IEnumSTATSTGImpl_AddRef,
215 IEnumSTATSTGImpl_Release,
216 IEnumSTATSTGImpl_Next,
217 IEnumSTATSTGImpl_Skip,
218 IEnumSTATSTGImpl_Reset,
219 IEnumSTATSTGImpl_Clone
226 /************************************************************************
227 ** Storage32BaseImpl implementatiion
230 /************************************************************************
231 * Storage32BaseImpl_QueryInterface (IUnknown)
233 * This method implements the common QueryInterface for all IStorage32
234 * implementations contained in this file.
236 * See Windows documentation for more details on IUnknown methods.
238 HRESULT WINAPI StorageBaseImpl_QueryInterface(
243 ICOM_THIS(StorageBaseImpl,iface);
245 * Perform a sanity check on the parameters.
247 if ( (This==0) || (ppvObject==0) )
251 * Initialize the return parameter.
256 * Compare the riid with the interface IDs implemented by this object.
258 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
260 *ppvObject = (IStorage*)This;
262 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
264 *ppvObject = (IStorage*)This;
268 * Check that we obtained an interface.
271 return E_NOINTERFACE;
274 * Query Interface always increases the reference count by one when it is
277 StorageBaseImpl_AddRef(iface);
282 /************************************************************************
283 * Storage32BaseImpl_AddRef (IUnknown)
285 * This method implements the common AddRef for all IStorage32
286 * implementations contained in this file.
288 * See Windows documentation for more details on IUnknown methods.
290 ULONG WINAPI StorageBaseImpl_AddRef(
293 ICOM_THIS(StorageBaseImpl,iface);
299 /************************************************************************
300 * Storage32BaseImpl_Release (IUnknown)
302 * This method implements the common Release for all IStorage32
303 * implementations contained in this file.
305 * See Windows documentation for more details on IUnknown methods.
307 ULONG WINAPI StorageBaseImpl_Release(
310 ICOM_THIS(StorageBaseImpl,iface);
312 * Decrease the reference count on this object.
317 * If the reference count goes down to 0, perform suicide.
322 * Since we are using a system of base-classes, we want to call the
323 * destructor of the appropriate derived class. To do this, we are
324 * using virtual functions to implement the destructor.
326 This->v_destructor(This);
334 /************************************************************************
335 * Storage32BaseImpl_OpenStream (IStorage)
337 * This method will open the specified stream object from the current storage.
339 * See Windows documentation for more details on IStorage methods.
341 HRESULT WINAPI StorageBaseImpl_OpenStream(
343 const OLECHAR* pwcsName, /* [string][in] */
344 void* reserved1, /* [unique][in] */
345 DWORD grfMode, /* [in] */
346 DWORD reserved2, /* [in] */
347 IStream** ppstm) /* [out] */
349 ICOM_THIS(StorageBaseImpl,iface);
350 IEnumSTATSTGImpl* propertyEnumeration;
351 StgStreamImpl* newStream;
352 StgProperty currentProperty;
353 ULONG foundPropertyIndex;
354 HRESULT res = STG_E_UNKNOWN;
356 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
357 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
360 * Perform a sanity check on the parameters.
362 if ( (pwcsName==NULL) || (ppstm==0) )
369 * Initialize the out parameter
374 * Validate the STGM flags
376 if ( FAILED( validateSTGM(grfMode) ))
378 res = STG_E_INVALIDFLAG;
385 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
386 (grfMode & STGM_DELETEONRELEASE) ||
387 (grfMode & STGM_TRANSACTED) )
389 res = STG_E_INVALIDFUNCTION;
394 * Create a property enumeration to search the properties
396 propertyEnumeration = IEnumSTATSTGImpl_Construct(
397 This->ancestorStorage,
398 This->rootPropertySetIndex);
401 * Search the enumeration for the property with the given name
403 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
409 * Delete the property enumeration since we don't need it anymore
411 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
414 * If it was found, construct the stream object and return a pointer to it.
416 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
417 (currentProperty.propertyType==PROPTYPE_STREAM) )
419 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
423 newStream->grfMode = grfMode;
424 *ppstm = (IStream*)newStream;
427 * Since we are returning a pointer to the interface, we have to
428 * nail down the reference.
430 StgStreamImpl_AddRef(*ppstm);
440 res = STG_E_FILENOTFOUND;
444 TRACE("<-- IStream %p\n", *ppstm);
445 TRACE("<-- %08lx\n", res);
449 /************************************************************************
450 * Storage32BaseImpl_OpenStorage (IStorage)
452 * This method will open a new storage object from the current storage.
454 * See Windows documentation for more details on IStorage methods.
456 HRESULT WINAPI StorageBaseImpl_OpenStorage(
458 const OLECHAR* pwcsName, /* [string][unique][in] */
459 IStorage* pstgPriority, /* [unique][in] */
460 DWORD grfMode, /* [in] */
461 SNB snbExclude, /* [unique][in] */
462 DWORD reserved, /* [in] */
463 IStorage** ppstg) /* [out] */
465 ICOM_THIS(StorageBaseImpl,iface);
466 StorageInternalImpl* newStorage;
467 IEnumSTATSTGImpl* propertyEnumeration;
468 StgProperty currentProperty;
469 ULONG foundPropertyIndex;
470 HRESULT res = STG_E_UNKNOWN;
472 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
473 iface, debugstr_w(pwcsName), pstgPriority,
474 grfMode, snbExclude, reserved, ppstg);
477 * Perform a sanity check on the parameters.
479 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
486 if (snbExclude != NULL)
488 res = STG_E_INVALIDPARAMETER;
493 * Validate the STGM flags
495 if ( FAILED( validateSTGM(grfMode) ))
497 res = STG_E_INVALIDFLAG;
504 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
505 (grfMode & STGM_DELETEONRELEASE) ||
506 (grfMode & STGM_PRIORITY) )
508 res = STG_E_INVALIDFUNCTION;
513 * Initialize the out parameter
518 * Create a property enumeration to search the properties
520 propertyEnumeration = IEnumSTATSTGImpl_Construct(
521 This->ancestorStorage,
522 This->rootPropertySetIndex);
525 * Search the enumeration for the property with the given name
527 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
533 * Delete the property enumeration since we don't need it anymore
535 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
538 * If it was found, construct the stream object and return a pointer to it.
540 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
541 (currentProperty.propertyType==PROPTYPE_STORAGE) )
544 * Construct a new Storage object
546 newStorage = StorageInternalImpl_Construct(
547 This->ancestorStorage,
552 *ppstg = (IStorage*)newStorage;
555 * Since we are returning a pointer to the interface,
556 * we have to nail down the reference.
558 StorageBaseImpl_AddRef(*ppstg);
564 res = STG_E_INSUFFICIENTMEMORY;
568 res = STG_E_FILENOTFOUND;
571 TRACE("<-- %08lx\n", res);
575 /************************************************************************
576 * Storage32BaseImpl_EnumElements (IStorage)
578 * This method will create an enumerator object that can be used to
579 * retrieve informatino about all the properties in the storage object.
581 * See Windows documentation for more details on IStorage methods.
583 HRESULT WINAPI StorageBaseImpl_EnumElements(
585 DWORD reserved1, /* [in] */
586 void* reserved2, /* [size_is][unique][in] */
587 DWORD reserved3, /* [in] */
588 IEnumSTATSTG** ppenum) /* [out] */
590 ICOM_THIS(StorageBaseImpl,iface);
591 IEnumSTATSTGImpl* newEnum;
593 TRACE("(%p, %ld, %p, %ld, %p)\n",
594 iface, reserved1, reserved2, reserved3, ppenum);
597 * Perform a sanity check on the parameters.
599 if ( (This==0) || (ppenum==0))
603 * Construct the enumerator.
605 newEnum = IEnumSTATSTGImpl_Construct(
606 This->ancestorStorage,
607 This->rootPropertySetIndex);
611 *ppenum = (IEnumSTATSTG*)newEnum;
614 * Don't forget to nail down a reference to the new object before
617 IEnumSTATSTGImpl_AddRef(*ppenum);
622 return E_OUTOFMEMORY;
625 /************************************************************************
626 * Storage32BaseImpl_Stat (IStorage)
628 * This method will retrieve information about this storage object.
630 * See Windows documentation for more details on IStorage methods.
632 HRESULT WINAPI StorageBaseImpl_Stat(
634 STATSTG* pstatstg, /* [out] */
635 DWORD grfStatFlag) /* [in] */
637 ICOM_THIS(StorageBaseImpl,iface);
638 StgProperty curProperty;
640 HRESULT res = STG_E_UNKNOWN;
642 TRACE("(%p, %p, %lx)\n",
643 iface, pstatstg, grfStatFlag);
646 * Perform a sanity check on the parameters.
648 if ( (This==0) || (pstatstg==0))
655 * Read the information from the property.
657 readSuccessful = StorageImpl_ReadProperty(
658 This->ancestorStorage,
659 This->rootPropertySetIndex,
664 StorageUtl_CopyPropertyToSTATSTG(
678 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.s.LowPart, pstatstg->cbSize.s.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
680 TRACE("<-- %08lx\n", res);
684 /************************************************************************
685 * Storage32BaseImpl_RenameElement (IStorage)
687 * This method will rename the specified element.
689 * See Windows documentation for more details on IStorage methods.
691 * Implementation notes: The method used to rename consists of creating a clone
692 * of the deleted StgProperty object setting it with the new name and to
693 * perform a DestroyElement of the old StgProperty.
695 HRESULT WINAPI StorageBaseImpl_RenameElement(
697 const OLECHAR* pwcsOldName, /* [in] */
698 const OLECHAR* pwcsNewName) /* [in] */
700 ICOM_THIS(StorageBaseImpl,iface);
701 IEnumSTATSTGImpl* propertyEnumeration;
702 StgProperty currentProperty;
703 ULONG foundPropertyIndex;
705 TRACE("(%p, %s, %s)\n",
706 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
709 * Create a property enumeration to search the properties
711 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
712 This->rootPropertySetIndex);
715 * Search the enumeration for the new property name
717 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
721 if (foundPropertyIndex != PROPERTY_NULL)
724 * There is already a property with the new name
726 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
727 return STG_E_FILEALREADYEXISTS;
730 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
733 * Search the enumeration for the old property name
735 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
740 * Delete the property enumeration since we don't need it anymore
742 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
744 if (foundPropertyIndex != PROPERTY_NULL)
746 StgProperty renamedProperty;
747 ULONG renamedPropertyIndex;
750 * Setup a new property for the renamed property
752 renamedProperty.sizeOfNameString =
753 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
755 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
756 return STG_E_INVALIDNAME;
758 strcpyW(renamedProperty.name, pwcsNewName);
760 renamedProperty.propertyType = currentProperty.propertyType;
761 renamedProperty.startingBlock = currentProperty.startingBlock;
762 renamedProperty.size.s.LowPart = currentProperty.size.s.LowPart;
763 renamedProperty.size.s.HighPart = currentProperty.size.s.HighPart;
765 renamedProperty.previousProperty = PROPERTY_NULL;
766 renamedProperty.nextProperty = PROPERTY_NULL;
769 * Bring the dirProperty link in case it is a storage and in which
770 * case the renamed storage elements don't require to be reorganized.
772 renamedProperty.dirProperty = currentProperty.dirProperty;
774 /* call CoFileTime to get the current time
775 renamedProperty.timeStampS1
776 renamedProperty.timeStampD1
777 renamedProperty.timeStampS2
778 renamedProperty.timeStampD2
779 renamedProperty.propertyUniqueID
783 * Obtain a free property in the property chain
785 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
788 * Save the new property into the new property spot
790 StorageImpl_WriteProperty(
791 This->ancestorStorage,
792 renamedPropertyIndex,
796 * Find a spot in the property chain for our newly created property.
800 renamedPropertyIndex,
804 * At this point the renamed property has been inserted in the tree,
805 * now, before to Destroy the old property we must zeroed it's dirProperty
806 * otherwise the DestroyProperty below will zap it all and we do not want
808 * Also, we fake that the old property is a storage so the DestroyProperty
809 * will not do a SetSize(0) on the stream data.
811 * This means that we need to tweek the StgProperty if it is a stream or a
814 StorageImpl_ReadProperty(This->ancestorStorage,
818 currentProperty.dirProperty = PROPERTY_NULL;
819 currentProperty.propertyType = PROPTYPE_STORAGE;
820 StorageImpl_WriteProperty(
821 This->ancestorStorage,
826 * Invoke Destroy to get rid of the ole property and automatically redo
827 * the linking of it's previous and next members...
829 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
835 * There is no property with the old name
837 return STG_E_FILENOTFOUND;
843 /************************************************************************
844 * Storage32BaseImpl_CreateStream (IStorage)
846 * This method will create a stream object within this storage
848 * See Windows documentation for more details on IStorage methods.
850 HRESULT WINAPI StorageBaseImpl_CreateStream(
852 const OLECHAR* pwcsName, /* [string][in] */
853 DWORD grfMode, /* [in] */
854 DWORD reserved1, /* [in] */
855 DWORD reserved2, /* [in] */
856 IStream** ppstm) /* [out] */
858 ICOM_THIS(StorageBaseImpl,iface);
859 IEnumSTATSTGImpl* propertyEnumeration;
860 StgStreamImpl* newStream;
861 StgProperty currentProperty, newStreamProperty;
862 ULONG foundPropertyIndex, newPropertyIndex;
864 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
865 iface, debugstr_w(pwcsName), grfMode,
866 reserved1, reserved2, ppstm);
869 * Validate parameters
872 return STG_E_INVALIDPOINTER;
875 return STG_E_INVALIDNAME;
878 * Validate the STGM flags
880 if ( FAILED( validateSTGM(grfMode) ))
881 return STG_E_INVALIDFLAG;
886 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
887 (grfMode & STGM_DELETEONRELEASE) ||
888 (grfMode & STGM_TRANSACTED) )
889 return STG_E_INVALIDFUNCTION;
892 * Initialize the out parameter
897 * Create a property enumeration to search the properties
899 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
900 This->rootPropertySetIndex);
902 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
906 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
908 if (foundPropertyIndex != PROPERTY_NULL)
911 * An element with this name already exists
913 if (grfMode & STGM_CREATE)
915 IStorage_DestroyElement(iface, pwcsName);
918 return STG_E_FILEALREADYEXISTS;
922 * memset the empty property
924 memset(&newStreamProperty, 0, sizeof(StgProperty));
926 newStreamProperty.sizeOfNameString =
927 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
929 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
930 return STG_E_INVALIDNAME;
932 strcpyW(newStreamProperty.name, pwcsName);
934 newStreamProperty.propertyType = PROPTYPE_STREAM;
935 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
936 newStreamProperty.size.s.LowPart = 0;
937 newStreamProperty.size.s.HighPart = 0;
939 newStreamProperty.previousProperty = PROPERTY_NULL;
940 newStreamProperty.nextProperty = PROPERTY_NULL;
941 newStreamProperty.dirProperty = PROPERTY_NULL;
943 /* call CoFileTime to get the current time
944 newStreamProperty.timeStampS1
945 newStreamProperty.timeStampD1
946 newStreamProperty.timeStampS2
947 newStreamProperty.timeStampD2
950 /* newStreamProperty.propertyUniqueID */
953 * Get a free property or create a new one
955 newPropertyIndex = getFreeProperty(This->ancestorStorage);
958 * Save the new property into the new property spot
960 StorageImpl_WriteProperty(
961 This->ancestorStorage,
966 * Find a spot in the property chain for our newly created property.
974 * Open the stream to return it.
976 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
980 *ppstm = (IStream*)newStream;
983 * Since we are returning a pointer to the interface, we have to nail down
986 StgStreamImpl_AddRef(*ppstm);
990 return STG_E_INSUFFICIENTMEMORY;
996 /************************************************************************
997 * Storage32BaseImpl_SetClass (IStorage)
999 * This method will write the specified CLSID in the property of this
1002 * See Windows documentation for more details on IStorage methods.
1004 HRESULT WINAPI StorageBaseImpl_SetClass(
1006 REFCLSID clsid) /* [in] */
1008 ICOM_THIS(StorageBaseImpl,iface);
1009 HRESULT hRes = E_FAIL;
1010 StgProperty curProperty;
1013 TRACE("(%p, %p)\n", iface, clsid);
1015 success = StorageImpl_ReadProperty(This->ancestorStorage,
1016 This->rootPropertySetIndex,
1020 curProperty.propertyUniqueID = *clsid;
1022 success = StorageImpl_WriteProperty(This->ancestorStorage,
1023 This->rootPropertySetIndex,
1032 /************************************************************************
1033 ** Storage32Impl implementation
1036 /************************************************************************
1037 * Storage32Impl_CreateStorage (IStorage)
1039 * This method will create the storage object within the provided storage.
1041 * See Windows documentation for more details on IStorage methods.
1043 HRESULT WINAPI StorageImpl_CreateStorage(
1045 const OLECHAR *pwcsName, /* [string][in] */
1046 DWORD grfMode, /* [in] */
1047 DWORD reserved1, /* [in] */
1048 DWORD reserved2, /* [in] */
1049 IStorage **ppstg) /* [out] */
1051 StorageImpl* const This=(StorageImpl*)iface;
1053 IEnumSTATSTGImpl *propertyEnumeration;
1054 StgProperty currentProperty;
1055 StgProperty newProperty;
1056 ULONG foundPropertyIndex;
1057 ULONG newPropertyIndex;
1060 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1061 iface, debugstr_w(pwcsName), grfMode,
1062 reserved1, reserved2, ppstg);
1065 * Validate parameters
1068 return STG_E_INVALIDPOINTER;
1071 return STG_E_INVALIDNAME;
1074 * Validate the STGM flags
1076 if ( FAILED( validateSTGM(grfMode) ) ||
1077 (grfMode & STGM_DELETEONRELEASE) )
1078 return STG_E_INVALIDFLAG;
1081 * Initialize the out parameter
1086 * Create a property enumeration and search the properties
1088 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
1089 This->rootPropertySetIndex);
1091 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1094 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1096 if (foundPropertyIndex != PROPERTY_NULL)
1099 * An element with this name already exists
1101 if (grfMode & STGM_CREATE)
1102 IStorage_DestroyElement(iface, pwcsName);
1104 return STG_E_FILEALREADYEXISTS;
1108 * memset the empty property
1110 memset(&newProperty, 0, sizeof(StgProperty));
1112 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1114 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1115 return STG_E_INVALIDNAME;
1117 strcpyW(newProperty.name, pwcsName);
1119 newProperty.propertyType = PROPTYPE_STORAGE;
1120 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1121 newProperty.size.s.LowPart = 0;
1122 newProperty.size.s.HighPart = 0;
1124 newProperty.previousProperty = PROPERTY_NULL;
1125 newProperty.nextProperty = PROPERTY_NULL;
1126 newProperty.dirProperty = PROPERTY_NULL;
1128 /* call CoFileTime to get the current time
1129 newProperty.timeStampS1
1130 newProperty.timeStampD1
1131 newProperty.timeStampS2
1132 newProperty.timeStampD2
1135 /* newStorageProperty.propertyUniqueID */
1138 * Obtain a free property in the property chain
1140 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1143 * Save the new property into the new property spot
1145 StorageImpl_WriteProperty(
1146 This->ancestorStorage,
1151 * Find a spot in the property chain for our newly created property.
1153 updatePropertyChain(
1159 * Open it to get a pointer to return.
1161 hr = IStorage_OpenStorage(
1170 if( (hr != S_OK) || (*ppstg == NULL))
1180 /***************************************************************************
1184 * Get a free property or create a new one.
1186 static ULONG getFreeProperty(
1187 StorageImpl *storage)
1189 ULONG currentPropertyIndex = 0;
1190 ULONG newPropertyIndex = PROPERTY_NULL;
1191 BOOL readSuccessful = TRUE;
1192 StgProperty currentProperty;
1197 * Start by reading the root property
1199 readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1200 currentPropertyIndex,
1204 if (currentProperty.sizeOfNameString == 0)
1207 * The property existis and is available, we found it.
1209 newPropertyIndex = currentPropertyIndex;
1215 * We exhausted the property list, we will create more space below
1217 newPropertyIndex = currentPropertyIndex;
1219 currentPropertyIndex++;
1221 } while (newPropertyIndex == PROPERTY_NULL);
1224 * grow the property chain
1226 if (! readSuccessful)
1228 StgProperty emptyProperty;
1229 ULARGE_INTEGER newSize;
1230 ULONG propertyIndex;
1231 ULONG lastProperty = 0;
1232 ULONG blockCount = 0;
1235 * obtain the new count of property blocks
1237 blockCount = BlockChainStream_GetCount(
1238 storage->ancestorStorage->rootBlockChain)+1;
1241 * initialize the size used by the property stream
1243 newSize.s.HighPart = 0;
1244 newSize.s.LowPart = storage->bigBlockSize * blockCount;
1247 * add a property block to the property chain
1249 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1252 * memset the empty property in order to initialize the unused newly
1255 memset(&emptyProperty, 0, sizeof(StgProperty));
1260 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1263 propertyIndex = newPropertyIndex;
1264 propertyIndex < lastProperty;
1267 StorageImpl_WriteProperty(
1268 storage->ancestorStorage,
1274 return newPropertyIndex;
1277 /****************************************************************************
1281 * Case insensitive comparaison of StgProperty.name by first considering
1284 * Returns <0 when newPrpoerty < currentProperty
1285 * >0 when newPrpoerty > currentProperty
1286 * 0 when newPrpoerty == currentProperty
1288 static LONG propertyNameCmp(
1289 OLECHAR *newProperty,
1290 OLECHAR *currentProperty)
1292 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1297 * We compare the string themselves only when they are of the same lenght
1299 diff = lstrcmpiW( newProperty, currentProperty);
1305 /****************************************************************************
1309 * Properly link this new element in the property chain.
1311 static void updatePropertyChain(
1312 StorageImpl *storage,
1313 ULONG newPropertyIndex,
1314 StgProperty newProperty)
1316 StgProperty currentProperty;
1319 * Read the root property
1321 StorageImpl_ReadProperty(storage->ancestorStorage,
1322 storage->rootPropertySetIndex,
1325 if (currentProperty.dirProperty != PROPERTY_NULL)
1328 * The root storage contains some element, therefore, start the research
1329 * for the appropriate location.
1332 ULONG current, next, previous, currentPropertyId;
1335 * Keep the StgProperty sequence number of the storage first property
1337 currentPropertyId = currentProperty.dirProperty;
1342 StorageImpl_ReadProperty(storage->ancestorStorage,
1343 currentProperty.dirProperty,
1346 previous = currentProperty.previousProperty;
1347 next = currentProperty.nextProperty;
1348 current = currentPropertyId;
1352 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1356 if (previous != PROPERTY_NULL)
1358 StorageImpl_ReadProperty(storage->ancestorStorage,
1365 currentProperty.previousProperty = newPropertyIndex;
1366 StorageImpl_WriteProperty(storage->ancestorStorage,
1374 if (next != PROPERTY_NULL)
1376 StorageImpl_ReadProperty(storage->ancestorStorage,
1383 currentProperty.nextProperty = newPropertyIndex;
1384 StorageImpl_WriteProperty(storage->ancestorStorage,
1393 * Trying to insert an item with the same name in the
1394 * subtree structure.
1399 previous = currentProperty.previousProperty;
1400 next = currentProperty.nextProperty;
1406 * The root storage is empty, link the new property to it's dir property
1408 currentProperty.dirProperty = newPropertyIndex;
1409 StorageImpl_WriteProperty(storage->ancestorStorage,
1410 storage->rootPropertySetIndex,
1416 /*************************************************************************
1419 HRESULT WINAPI StorageImpl_CopyTo(
1421 DWORD ciidExclude, /* [in] */
1422 const IID* rgiidExclude, /* [size_is][unique][in] */
1423 SNB snbExclude, /* [unique][in] */
1424 IStorage* pstgDest) /* [unique][in] */
1426 IEnumSTATSTG *elements = 0;
1427 STATSTG curElement, strStat;
1429 IStorage *pstgTmp, *pstgChild;
1430 IStream *pstrTmp, *pstrChild;
1432 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1433 FIXME("Exclude option not implemented\n");
1435 TRACE("(%p, %ld, %p, %p, %p)\n",
1436 iface, ciidExclude, rgiidExclude,
1437 snbExclude, pstgDest);
1440 * Perform a sanity check
1442 if ( pstgDest == 0 )
1443 return STG_E_INVALIDPOINTER;
1446 * Enumerate the elements
1448 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1456 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1457 IStorage_SetClass( pstgDest, &curElement.clsid );
1462 * Obtain the next element
1464 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1466 if ( hr == S_FALSE )
1468 hr = S_OK; /* done, every element has been copied */
1472 if (curElement.type == STGTY_STORAGE)
1475 * open child source storage
1477 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1478 STGM_READ|STGM_SHARE_EXCLUSIVE,
1479 NULL, 0, &pstgChild );
1485 * Check if destination storage is not a child of the source
1486 * storage, which will cause an infinite loop
1488 if (pstgChild == pstgDest)
1490 IEnumSTATSTG_Release(elements);
1492 return STG_E_ACCESSDENIED;
1496 * create a new storage in destination storage
1498 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1499 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1503 * if it already exist, don't create a new one use this one
1505 if (hr == STG_E_FILEALREADYEXISTS)
1507 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1508 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1509 NULL, 0, &pstgTmp );
1517 * do the copy recursively
1519 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1520 snbExclude, pstgTmp );
1522 IStorage_Release( pstgTmp );
1523 IStorage_Release( pstgChild );
1525 else if (curElement.type == STGTY_STREAM)
1528 * create a new stream in destination storage. If the stream already
1529 * exist, it will be deleted and a new one will be created.
1531 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1532 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1539 * open child stream storage
1541 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1542 STGM_READ|STGM_SHARE_EXCLUSIVE,
1549 * Get the size of the source stream
1551 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1554 * Set the size of the destination stream.
1556 IStream_SetSize(pstrTmp, strStat.cbSize);
1561 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1564 IStream_Release( pstrTmp );
1565 IStream_Release( pstrChild );
1569 WARN("unknown element type: %ld\n", curElement.type);
1572 } while (hr == S_OK);
1577 IEnumSTATSTG_Release(elements);
1582 /*************************************************************************
1583 * MoveElementTo (IStorage)
1585 HRESULT WINAPI StorageImpl_MoveElementTo(
1587 const OLECHAR *pwcsName, /* [string][in] */
1588 IStorage *pstgDest, /* [unique][in] */
1589 const OLECHAR *pwcsNewName,/* [string][in] */
1590 DWORD grfFlags) /* [in] */
1592 FIXME("not implemented!\n");
1596 /*************************************************************************
1599 HRESULT WINAPI StorageImpl_Commit(
1601 DWORD grfCommitFlags)/* [in] */
1603 FIXME("(%ld): stub!\n", grfCommitFlags);
1607 /*************************************************************************
1610 HRESULT WINAPI StorageImpl_Revert(
1613 FIXME("not implemented!\n");
1617 /*************************************************************************
1618 * DestroyElement (IStorage)
1620 * Stategy: This implementation is build this way for simplicity not for speed.
1621 * I always delete the top most element of the enumeration and adjust
1622 * the deleted element pointer all the time. This takes longer to
1623 * do but allow to reinvoke DestroyElement whenever we encounter a
1624 * storage object. The optimisation reside in the usage of another
1625 * enumeration stategy that would give all the leaves of a storage
1626 * first. (postfix order)
1628 HRESULT WINAPI StorageImpl_DestroyElement(
1630 const OLECHAR *pwcsName)/* [string][in] */
1632 StorageImpl* const This=(StorageImpl*)iface;
1634 IEnumSTATSTGImpl* propertyEnumeration;
1637 StgProperty propertyToDelete;
1638 StgProperty parentProperty;
1639 ULONG foundPropertyIndexToDelete;
1640 ULONG typeOfRelation;
1641 ULONG parentPropertyId;
1644 iface, debugstr_w(pwcsName));
1647 * Perform a sanity check on the parameters.
1650 return STG_E_INVALIDPOINTER;
1653 * Create a property enumeration to search the property with the given name
1655 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1656 This->ancestorStorage,
1657 This->rootPropertySetIndex);
1659 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1660 propertyEnumeration,
1664 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1666 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1668 return STG_E_FILENOTFOUND;
1672 * Find the parent property of the property to delete (the one that
1673 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1674 * the parent is This. Otherwise, the parent is one of it's sibling...
1678 * First, read This's StgProperty..
1680 res = StorageImpl_ReadProperty(
1681 This->ancestorStorage,
1682 This->rootPropertySetIndex,
1688 * Second, check to see if by any chance the actual storage (This) is not
1689 * the parent of the property to delete... We never know...
1691 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1694 * Set data as it would have been done in the else part...
1696 typeOfRelation = PROPERTY_RELATION_DIR;
1697 parentPropertyId = This->rootPropertySetIndex;
1702 * Create a property enumeration to search the parent properties, and
1703 * delete it once done.
1705 IEnumSTATSTGImpl* propertyEnumeration2;
1707 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1708 This->ancestorStorage,
1709 This->rootPropertySetIndex);
1711 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1712 propertyEnumeration2,
1713 foundPropertyIndexToDelete,
1717 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1720 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1722 hr = deleteStorageProperty(
1724 foundPropertyIndexToDelete,
1727 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1729 hr = deleteStreamProperty(
1731 foundPropertyIndexToDelete,
1739 * Adjust the property chain
1741 hr = adjustPropertyChain(
1752 /************************************************************************
1753 * StorageImpl_Stat (IStorage)
1755 * This method will retrieve information about this storage object.
1757 * See Windows documentation for more details on IStorage methods.
1759 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1760 STATSTG* pstatstg, /* [out] */
1761 DWORD grfStatFlag) /* [in] */
1763 StorageImpl* const This = (StorageImpl*)iface;
1764 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1766 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1768 CoTaskMemFree(pstatstg->pwcsName);
1769 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1770 strcpyW(pstatstg->pwcsName, This->pwcsName);
1778 /*********************************************************************
1782 * Perform the deletion of a complete storage node
1785 static HRESULT deleteStorageProperty(
1786 StorageImpl *parentStorage,
1787 ULONG indexOfPropertyToDelete,
1788 StgProperty propertyToDelete)
1790 IEnumSTATSTG *elements = 0;
1791 IStorage *childStorage = 0;
1792 STATSTG currentElement;
1794 HRESULT destroyHr = S_OK;
1797 * Open the storage and enumerate it
1799 hr = StorageBaseImpl_OpenStorage(
1800 (IStorage*)parentStorage,
1801 propertyToDelete.name,
1803 STGM_SHARE_EXCLUSIVE,
1814 * Enumerate the elements
1816 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1821 * Obtain the next element
1823 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1826 destroyHr = StorageImpl_DestroyElement(
1827 (IStorage*)childStorage,
1828 (OLECHAR*)currentElement.pwcsName);
1830 CoTaskMemFree(currentElement.pwcsName);
1834 * We need to Reset the enumeration every time because we delete elements
1835 * and the enumeration could be invalid
1837 IEnumSTATSTG_Reset(elements);
1839 } while ((hr == S_OK) && (destroyHr == S_OK));
1842 * Invalidate the property by zeroing it's name member.
1844 propertyToDelete.sizeOfNameString = 0;
1846 StorageImpl_WriteProperty(parentStorage->ancestorStorage,
1847 indexOfPropertyToDelete,
1850 IStorage_Release(childStorage);
1851 IEnumSTATSTG_Release(elements);
1856 /*********************************************************************
1860 * Perform the deletion of a stream node
1863 static HRESULT deleteStreamProperty(
1864 StorageImpl *parentStorage,
1865 ULONG indexOfPropertyToDelete,
1866 StgProperty propertyToDelete)
1870 ULARGE_INTEGER size;
1872 size.s.HighPart = 0;
1875 hr = StorageBaseImpl_OpenStream(
1876 (IStorage*)parentStorage,
1877 (OLECHAR*)propertyToDelete.name,
1879 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1891 hr = IStream_SetSize(pis, size);
1899 * Release the stream object.
1901 IStream_Release(pis);
1904 * Invalidate the property by zeroing it's name member.
1906 propertyToDelete.sizeOfNameString = 0;
1909 * Here we should re-read the property so we get the updated pointer
1910 * but since we are here to zap it, I don't do it...
1912 StorageImpl_WriteProperty(
1913 parentStorage->ancestorStorage,
1914 indexOfPropertyToDelete,
1920 /*********************************************************************
1924 * Finds a placeholder for the StgProperty within the Storage
1927 static HRESULT findPlaceholder(
1928 StorageImpl *storage,
1929 ULONG propertyIndexToStore,
1930 ULONG storePropertyIndex,
1933 StgProperty storeProperty;
1938 * Read the storage property
1940 res = StorageImpl_ReadProperty(
1941 storage->ancestorStorage,
1950 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1952 if (storeProperty.previousProperty != PROPERTY_NULL)
1954 return findPlaceholder(
1956 propertyIndexToStore,
1957 storeProperty.previousProperty,
1962 storeProperty.previousProperty = propertyIndexToStore;
1965 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1967 if (storeProperty.nextProperty != PROPERTY_NULL)
1969 return findPlaceholder(
1971 propertyIndexToStore,
1972 storeProperty.nextProperty,
1977 storeProperty.nextProperty = propertyIndexToStore;
1980 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1982 if (storeProperty.dirProperty != PROPERTY_NULL)
1984 return findPlaceholder(
1986 propertyIndexToStore,
1987 storeProperty.dirProperty,
1992 storeProperty.dirProperty = propertyIndexToStore;
1996 hr = StorageImpl_WriteProperty(
1997 storage->ancestorStorage,
2009 /*************************************************************************
2013 * This method takes the previous and the next property link of a property
2014 * to be deleted and find them a place in the Storage.
2016 static HRESULT adjustPropertyChain(
2018 StgProperty propertyToDelete,
2019 StgProperty parentProperty,
2020 ULONG parentPropertyId,
2023 ULONG newLinkProperty = PROPERTY_NULL;
2024 BOOL needToFindAPlaceholder = FALSE;
2025 ULONG storeNode = PROPERTY_NULL;
2026 ULONG toStoreNode = PROPERTY_NULL;
2027 INT relationType = 0;
2031 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2033 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2036 * Set the parent previous to the property to delete previous
2038 newLinkProperty = propertyToDelete.previousProperty;
2040 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2043 * We also need to find a storage for the other link, setup variables
2044 * to do this at the end...
2046 needToFindAPlaceholder = TRUE;
2047 storeNode = propertyToDelete.previousProperty;
2048 toStoreNode = propertyToDelete.nextProperty;
2049 relationType = PROPERTY_RELATION_NEXT;
2052 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2055 * Set the parent previous to the property to delete next
2057 newLinkProperty = propertyToDelete.nextProperty;
2061 * Link it for real...
2063 parentProperty.previousProperty = newLinkProperty;
2066 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2068 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2071 * Set the parent next to the property to delete next previous
2073 newLinkProperty = propertyToDelete.previousProperty;
2075 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2078 * We also need to find a storage for the other link, setup variables
2079 * to do this at the end...
2081 needToFindAPlaceholder = TRUE;
2082 storeNode = propertyToDelete.previousProperty;
2083 toStoreNode = propertyToDelete.nextProperty;
2084 relationType = PROPERTY_RELATION_NEXT;
2087 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2090 * Set the parent next to the property to delete next
2092 newLinkProperty = propertyToDelete.nextProperty;
2096 * Link it for real...
2098 parentProperty.nextProperty = newLinkProperty;
2100 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2102 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2105 * Set the parent dir to the property to delete previous
2107 newLinkProperty = propertyToDelete.previousProperty;
2109 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2112 * We also need to find a storage for the other link, setup variables
2113 * to do this at the end...
2115 needToFindAPlaceholder = TRUE;
2116 storeNode = propertyToDelete.previousProperty;
2117 toStoreNode = propertyToDelete.nextProperty;
2118 relationType = PROPERTY_RELATION_NEXT;
2121 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2124 * Set the parent dir to the property to delete next
2126 newLinkProperty = propertyToDelete.nextProperty;
2130 * Link it for real...
2132 parentProperty.dirProperty = newLinkProperty;
2136 * Write back the parent property
2138 res = StorageImpl_WriteProperty(
2139 This->ancestorStorage,
2148 * If a placeholder is required for the other link, then, find one and
2149 * get out of here...
2151 if (needToFindAPlaceholder)
2153 hr = findPlaceholder(
2164 /******************************************************************************
2165 * SetElementTimes (IStorage)
2167 HRESULT WINAPI StorageImpl_SetElementTimes(
2169 const OLECHAR *pwcsName,/* [string][in] */
2170 const FILETIME *pctime, /* [in] */
2171 const FILETIME *patime, /* [in] */
2172 const FILETIME *pmtime) /* [in] */
2174 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2178 /******************************************************************************
2179 * SetStateBits (IStorage)
2181 HRESULT WINAPI StorageImpl_SetStateBits(
2183 DWORD grfStateBits,/* [in] */
2184 DWORD grfMask) /* [in] */
2186 FIXME("not implemented!\n");
2190 HRESULT StorageImpl_Construct(
2200 StgProperty currentProperty;
2201 BOOL readSuccessful;
2202 ULONG currentPropertyIndex;
2204 if ( FAILED( validateSTGM(openFlags) ))
2205 return STG_E_INVALIDFLAG;
2207 memset(This, 0, sizeof(StorageImpl));
2210 * Initialize the virtual function table.
2212 ICOM_VTBL(This) = &Storage32Impl_Vtbl;
2213 This->v_destructor = &StorageImpl_Destroy;
2216 * This is the top-level storage so initialize the ancestor pointer
2219 This->ancestorStorage = This;
2222 * Initialize the physical support of the storage.
2224 This->hFile = hFile;
2227 * Store copy of file path.
2230 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2231 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2232 if (!This->pwcsName)
2233 return STG_E_INSUFFICIENTMEMORY;
2234 strcpyW(This->pwcsName, pwcsName);
2238 * Initialize the big block cache.
2240 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2241 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2242 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2248 if (This->bigBlockFile == 0)
2253 ULARGE_INTEGER size;
2254 BYTE* bigBlockBuffer;
2257 * Initialize all header variables:
2258 * - The big block depot consists of one block and it is at block 0
2259 * - The properties start at block 1
2260 * - There is no small block depot
2262 memset( This->bigBlockDepotStart,
2264 sizeof(This->bigBlockDepotStart));
2266 This->bigBlockDepotCount = 1;
2267 This->bigBlockDepotStart[0] = 0;
2268 This->rootStartBlock = 1;
2269 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2270 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2271 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2272 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2273 This->extBigBlockDepotCount = 0;
2275 StorageImpl_SaveFileHeader(This);
2278 * Add one block for the big block depot and one block for the properties
2280 size.s.HighPart = 0;
2281 size.s.LowPart = This->bigBlockSize * 3;
2282 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2285 * Initialize the big block depot
2287 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2288 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2289 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2290 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2291 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2296 * Load the header for the file.
2298 hr = StorageImpl_LoadFileHeader(This);
2302 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2309 * There is no block depot cached yet.
2311 This->indexBlockDepotCached = 0xFFFFFFFF;
2314 * Start searching for free blocks with block 0.
2316 This->prevFreeBlock = 0;
2319 * Create the block chain abstractions.
2321 if(!(This->rootBlockChain =
2322 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2323 return STG_E_READFAULT;
2325 if(!(This->smallBlockDepotChain =
2326 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2328 return STG_E_READFAULT;
2331 * Write the root property
2335 StgProperty rootProp;
2337 * Initialize the property chain
2339 memset(&rootProp, 0, sizeof(rootProp));
2340 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2341 sizeof(rootProp.name)/sizeof(WCHAR) );
2342 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2343 rootProp.propertyType = PROPTYPE_ROOT;
2344 rootProp.previousProperty = PROPERTY_NULL;
2345 rootProp.nextProperty = PROPERTY_NULL;
2346 rootProp.dirProperty = PROPERTY_NULL;
2347 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2348 rootProp.size.s.HighPart = 0;
2349 rootProp.size.s.LowPart = 0;
2351 StorageImpl_WriteProperty(This, 0, &rootProp);
2355 * Find the ID of the root in the property sets.
2357 currentPropertyIndex = 0;
2361 readSuccessful = StorageImpl_ReadProperty(
2363 currentPropertyIndex,
2368 if ( (currentProperty.sizeOfNameString != 0 ) &&
2369 (currentProperty.propertyType == PROPTYPE_ROOT) )
2371 This->rootPropertySetIndex = currentPropertyIndex;
2375 currentPropertyIndex++;
2377 } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2379 if (!readSuccessful)
2382 return STG_E_READFAULT;
2386 * Create the block chain abstraction for the small block root chain.
2388 if(!(This->smallBlockRootChain =
2389 BlockChainStream_Construct(This, NULL, This->rootPropertySetIndex)))
2390 return STG_E_READFAULT;
2395 void StorageImpl_Destroy(
2398 TRACE("(%p)\n", This);
2401 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2403 BlockChainStream_Destroy(This->smallBlockRootChain);
2404 BlockChainStream_Destroy(This->rootBlockChain);
2405 BlockChainStream_Destroy(This->smallBlockDepotChain);
2407 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2411 /******************************************************************************
2412 * Storage32Impl_GetNextFreeBigBlock
2414 * Returns the index of the next free big block.
2415 * If the big block depot is filled, this method will enlarge it.
2418 ULONG StorageImpl_GetNextFreeBigBlock(
2421 ULONG depotBlockIndexPos;
2423 ULONG depotBlockOffset;
2424 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2425 ULONG nextBlockIndex = BLOCK_SPECIAL;
2427 ULONG freeBlock = BLOCK_UNUSED;
2429 depotIndex = This->prevFreeBlock / blocksPerDepot;
2430 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2433 * Scan the entire big block depot until we find a block marked free
2435 while (nextBlockIndex != BLOCK_UNUSED)
2437 if (depotIndex < COUNT_BBDEPOTINHEADER)
2439 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2442 * Grow the primary depot.
2444 if (depotBlockIndexPos == BLOCK_UNUSED)
2446 depotBlockIndexPos = depotIndex*blocksPerDepot;
2449 * Add a block depot.
2451 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2452 This->bigBlockDepotCount++;
2453 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2456 * Flag it as a block depot.
2458 StorageImpl_SetNextBlockInChain(This,
2462 /* Save new header information.
2464 StorageImpl_SaveFileHeader(This);
2469 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2471 if (depotBlockIndexPos == BLOCK_UNUSED)
2474 * Grow the extended depot.
2476 ULONG extIndex = BLOCK_UNUSED;
2477 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2478 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2480 if (extBlockOffset == 0)
2482 /* We need an extended block.
2484 extIndex = Storage32Impl_AddExtBlockDepot(This);
2485 This->extBigBlockDepotCount++;
2486 depotBlockIndexPos = extIndex + 1;
2489 depotBlockIndexPos = depotIndex * blocksPerDepot;
2492 * Add a block depot and mark it in the extended block.
2494 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2495 This->bigBlockDepotCount++;
2496 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2498 /* Flag the block depot.
2500 StorageImpl_SetNextBlockInChain(This,
2504 /* If necessary, flag the extended depot block.
2506 if (extIndex != BLOCK_UNUSED)
2507 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2509 /* Save header information.
2511 StorageImpl_SaveFileHeader(This);
2515 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2517 if (depotBuffer != 0)
2519 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2520 ( nextBlockIndex != BLOCK_UNUSED))
2522 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2524 if (nextBlockIndex == BLOCK_UNUSED)
2526 freeBlock = (depotIndex * blocksPerDepot) +
2527 (depotBlockOffset/sizeof(ULONG));
2530 depotBlockOffset += sizeof(ULONG);
2533 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2537 depotBlockOffset = 0;
2540 This->prevFreeBlock = freeBlock;
2545 /******************************************************************************
2546 * Storage32Impl_AddBlockDepot
2548 * This will create a depot block, essentially it is a block initialized
2551 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2555 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2558 * Initialize blocks as free
2560 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2562 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2565 /******************************************************************************
2566 * Storage32Impl_GetExtDepotBlock
2568 * Returns the index of the block that corresponds to the specified depot
2569 * index. This method is only for depot indexes equal or greater than
2570 * COUNT_BBDEPOTINHEADER.
2572 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2574 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2575 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2576 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2577 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2578 ULONG blockIndex = BLOCK_UNUSED;
2579 ULONG extBlockIndex = This->extBigBlockDepotStart;
2581 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2583 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2584 return BLOCK_UNUSED;
2586 while (extBlockCount > 0)
2588 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2592 if (extBlockIndex != BLOCK_UNUSED)
2596 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2598 if (depotBuffer != 0)
2600 StorageUtl_ReadDWord(depotBuffer,
2601 extBlockOffset * sizeof(ULONG),
2604 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2611 /******************************************************************************
2612 * Storage32Impl_SetExtDepotBlock
2614 * Associates the specified block index to the specified depot index.
2615 * This method is only for depot indexes equal or greater than
2616 * COUNT_BBDEPOTINHEADER.
2618 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2622 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2623 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2624 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2625 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2626 ULONG extBlockIndex = This->extBigBlockDepotStart;
2628 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2630 while (extBlockCount > 0)
2632 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2636 if (extBlockIndex != BLOCK_UNUSED)
2640 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2642 if (depotBuffer != 0)
2644 StorageUtl_WriteDWord(depotBuffer,
2645 extBlockOffset * sizeof(ULONG),
2648 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2653 /******************************************************************************
2654 * Storage32Impl_AddExtBlockDepot
2656 * Creates an extended depot block.
2658 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2660 ULONG numExtBlocks = This->extBigBlockDepotCount;
2661 ULONG nextExtBlock = This->extBigBlockDepotStart;
2662 BYTE* depotBuffer = NULL;
2663 ULONG index = BLOCK_UNUSED;
2664 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2665 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2666 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2668 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2669 blocksPerDepotBlock;
2671 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2674 * The first extended block.
2676 This->extBigBlockDepotStart = index;
2682 * Follow the chain to the last one.
2684 for (i = 0; i < (numExtBlocks - 1); i++)
2686 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2690 * Add the new extended block to the chain.
2692 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2693 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2694 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2698 * Initialize this block.
2700 depotBuffer = StorageImpl_GetBigBlock(This, index);
2701 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2702 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2707 /******************************************************************************
2708 * Storage32Impl_FreeBigBlock
2710 * This method will flag the specified block as free in the big block depot.
2712 void StorageImpl_FreeBigBlock(
2716 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2718 if (blockIndex < This->prevFreeBlock)
2719 This->prevFreeBlock = blockIndex;
2722 /************************************************************************
2723 * Storage32Impl_GetNextBlockInChain
2725 * This method will retrieve the block index of the next big block in
2728 * Params: This - Pointer to the Storage object.
2729 * blockIndex - Index of the block to retrieve the chain
2731 * nextBlockIndex - receives the return value.
2733 * Returns: This method returns the index of the next block in the chain.
2734 * It will return the constants:
2735 * BLOCK_SPECIAL - If the block given was not part of a
2737 * BLOCK_END_OF_CHAIN - If the block given was the last in
2739 * BLOCK_UNUSED - If the block given was not past of a chain
2741 * BLOCK_EXTBBDEPOT - This block is part of the extended
2744 * See Windows documentation for more details on IStorage methods.
2746 HRESULT StorageImpl_GetNextBlockInChain(
2749 ULONG* nextBlockIndex)
2751 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2752 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2753 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2755 ULONG depotBlockIndexPos;
2758 *nextBlockIndex = BLOCK_SPECIAL;
2760 if(depotBlockCount >= This->bigBlockDepotCount)
2762 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2763 This->bigBlockDepotCount);
2764 return STG_E_READFAULT;
2768 * Cache the currently accessed depot block.
2770 if (depotBlockCount != This->indexBlockDepotCached)
2772 This->indexBlockDepotCached = depotBlockCount;
2774 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2776 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2781 * We have to look in the extended depot.
2783 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2786 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2789 return STG_E_READFAULT;
2791 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2793 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2794 This->blockDepotCached[index] = *nextBlockIndex;
2796 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2799 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2804 /******************************************************************************
2805 * Storage32Impl_GetNextExtendedBlock
2807 * Given an extended block this method will return the next extended block.
2810 * The last ULONG of an extended block is the block index of the next
2811 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2815 * - The index of the next extended block
2816 * - BLOCK_UNUSED: there is no next extended block.
2817 * - Any other return values denotes failure.
2819 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2821 ULONG nextBlockIndex = BLOCK_SPECIAL;
2822 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2825 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2829 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2831 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2834 return nextBlockIndex;
2837 /******************************************************************************
2838 * Storage32Impl_SetNextBlockInChain
2840 * This method will write the index of the specified block's next block
2841 * in the big block depot.
2843 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2846 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2847 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2848 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2851 void StorageImpl_SetNextBlockInChain(
2856 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2857 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2858 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2859 ULONG depotBlockIndexPos;
2862 assert(depotBlockCount < This->bigBlockDepotCount);
2863 assert(blockIndex != nextBlock);
2865 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2867 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2872 * We have to look in the extended depot.
2874 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2877 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2881 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2882 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2886 * Update the cached block depot, if necessary.
2888 if (depotBlockCount == This->indexBlockDepotCached)
2890 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2894 /******************************************************************************
2895 * Storage32Impl_LoadFileHeader
2897 * This method will read in the file header, i.e. big block index -1.
2899 HRESULT StorageImpl_LoadFileHeader(
2902 HRESULT hr = STG_E_FILENOTFOUND;
2903 void* headerBigBlock = NULL;
2907 * Get a pointer to the big block of data containing the header.
2909 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2912 * Extract the information from the header.
2914 if (headerBigBlock!=0)
2917 * Check for the "magic number" signature and return an error if it is not
2920 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2922 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2923 return STG_E_OLDFORMAT;
2926 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2928 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2929 return STG_E_INVALIDHEADER;
2932 StorageUtl_ReadWord(
2934 OFFSET_BIGBLOCKSIZEBITS,
2935 &This->bigBlockSizeBits);
2937 StorageUtl_ReadWord(
2939 OFFSET_SMALLBLOCKSIZEBITS,
2940 &This->smallBlockSizeBits);
2942 StorageUtl_ReadDWord(
2944 OFFSET_BBDEPOTCOUNT,
2945 &This->bigBlockDepotCount);
2947 StorageUtl_ReadDWord(
2949 OFFSET_ROOTSTARTBLOCK,
2950 &This->rootStartBlock);
2952 StorageUtl_ReadDWord(
2954 OFFSET_SBDEPOTSTART,
2955 &This->smallBlockDepotStart);
2957 StorageUtl_ReadDWord(
2959 OFFSET_EXTBBDEPOTSTART,
2960 &This->extBigBlockDepotStart);
2962 StorageUtl_ReadDWord(
2964 OFFSET_EXTBBDEPOTCOUNT,
2965 &This->extBigBlockDepotCount);
2967 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2969 StorageUtl_ReadDWord(
2971 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2972 &(This->bigBlockDepotStart[index]));
2976 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2980 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2981 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2985 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2986 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2990 * Right now, the code is making some assumptions about the size of the
2991 * blocks, just make sure they are what we're expecting.
2993 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
2994 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
2996 WARN("Broken OLE storage file\n");
2997 hr = STG_E_INVALIDHEADER;
3003 * Release the block.
3005 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3011 /******************************************************************************
3012 * Storage32Impl_SaveFileHeader
3014 * This method will save to the file the header, i.e. big block -1.
3016 void StorageImpl_SaveFileHeader(
3019 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3024 * Get a pointer to the big block of data containing the header.
3026 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3029 * If the block read failed, the file is probably new.
3034 * Initialize for all unknown fields.
3036 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3039 * Initialize the magic number.
3041 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3044 * And a bunch of things we don't know what they mean
3046 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3047 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3048 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3049 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3053 * Write the information to the header.
3055 StorageUtl_WriteWord(
3057 OFFSET_BIGBLOCKSIZEBITS,
3058 This->bigBlockSizeBits);
3060 StorageUtl_WriteWord(
3062 OFFSET_SMALLBLOCKSIZEBITS,
3063 This->smallBlockSizeBits);
3065 StorageUtl_WriteDWord(
3067 OFFSET_BBDEPOTCOUNT,
3068 This->bigBlockDepotCount);
3070 StorageUtl_WriteDWord(
3072 OFFSET_ROOTSTARTBLOCK,
3073 This->rootStartBlock);
3075 StorageUtl_WriteDWord(
3077 OFFSET_SBDEPOTSTART,
3078 This->smallBlockDepotStart);
3080 StorageUtl_WriteDWord(
3082 OFFSET_SBDEPOTCOUNT,
3083 This->smallBlockDepotChain ?
3084 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3086 StorageUtl_WriteDWord(
3088 OFFSET_EXTBBDEPOTSTART,
3089 This->extBigBlockDepotStart);
3091 StorageUtl_WriteDWord(
3093 OFFSET_EXTBBDEPOTCOUNT,
3094 This->extBigBlockDepotCount);
3096 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3098 StorageUtl_WriteDWord(
3100 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3101 (This->bigBlockDepotStart[index]));
3105 * Write the big block back to the file.
3107 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3110 /******************************************************************************
3111 * Storage32Impl_ReadProperty
3113 * This method will read the specified property from the property chain.
3115 BOOL StorageImpl_ReadProperty(
3118 StgProperty* buffer)
3120 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3121 ULARGE_INTEGER offsetInPropSet;
3122 BOOL readSuccessful;
3125 offsetInPropSet.s.HighPart = 0;
3126 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3128 readSuccessful = BlockChainStream_ReadAt(
3129 This->rootBlockChain,
3137 /* replace the name of root entry (often "Root Entry") by the file name */
3138 WCHAR *propName = (index == This->rootPropertySetIndex) ?
3139 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3141 memset(buffer->name, 0, sizeof(buffer->name));
3145 PROPERTY_NAME_BUFFER_LEN );
3146 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3148 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3150 StorageUtl_ReadWord(
3152 OFFSET_PS_NAMELENGTH,
3153 &buffer->sizeOfNameString);
3155 StorageUtl_ReadDWord(
3157 OFFSET_PS_PREVIOUSPROP,
3158 &buffer->previousProperty);
3160 StorageUtl_ReadDWord(
3163 &buffer->nextProperty);
3165 StorageUtl_ReadDWord(
3168 &buffer->dirProperty);
3170 StorageUtl_ReadGUID(
3173 &buffer->propertyUniqueID);
3175 StorageUtl_ReadDWord(
3178 &buffer->timeStampS1);
3180 StorageUtl_ReadDWord(
3183 &buffer->timeStampD1);
3185 StorageUtl_ReadDWord(
3188 &buffer->timeStampS2);
3190 StorageUtl_ReadDWord(
3193 &buffer->timeStampD2);
3195 StorageUtl_ReadDWord(
3197 OFFSET_PS_STARTBLOCK,
3198 &buffer->startingBlock);
3200 StorageUtl_ReadDWord(
3203 &buffer->size.s.LowPart);
3205 buffer->size.s.HighPart = 0;
3208 return readSuccessful;
3211 /*********************************************************************
3212 * Write the specified property into the property chain
3214 BOOL StorageImpl_WriteProperty(
3217 StgProperty* buffer)
3219 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3220 ULARGE_INTEGER offsetInPropSet;
3221 BOOL writeSuccessful;
3224 offsetInPropSet.s.HighPart = 0;
3225 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3227 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3230 currentProperty + OFFSET_PS_NAME,
3232 PROPERTY_NAME_BUFFER_LEN );
3234 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3236 StorageUtl_WriteWord(
3238 OFFSET_PS_NAMELENGTH,
3239 buffer->sizeOfNameString);
3241 StorageUtl_WriteDWord(
3243 OFFSET_PS_PREVIOUSPROP,
3244 buffer->previousProperty);
3246 StorageUtl_WriteDWord(
3249 buffer->nextProperty);
3251 StorageUtl_WriteDWord(
3254 buffer->dirProperty);
3256 StorageUtl_WriteGUID(
3259 &buffer->propertyUniqueID);
3261 StorageUtl_WriteDWord(
3264 buffer->timeStampS1);
3266 StorageUtl_WriteDWord(
3269 buffer->timeStampD1);
3271 StorageUtl_WriteDWord(
3274 buffer->timeStampS2);
3276 StorageUtl_WriteDWord(
3279 buffer->timeStampD2);
3281 StorageUtl_WriteDWord(
3283 OFFSET_PS_STARTBLOCK,
3284 buffer->startingBlock);
3286 StorageUtl_WriteDWord(
3289 buffer->size.s.LowPart);
3291 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3296 return writeSuccessful;
3299 BOOL StorageImpl_ReadBigBlock(
3304 void* bigBlockBuffer;
3306 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3308 if (bigBlockBuffer!=0)
3310 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3312 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3320 BOOL StorageImpl_WriteBigBlock(
3325 void* bigBlockBuffer;
3327 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3329 if (bigBlockBuffer!=0)
3331 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3333 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3341 void* StorageImpl_GetROBigBlock(
3345 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3348 void* StorageImpl_GetBigBlock(
3352 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3355 void StorageImpl_ReleaseBigBlock(
3359 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3362 /******************************************************************************
3363 * Storage32Impl_SmallBlocksToBigBlocks
3365 * This method will convert a small block chain to a big block chain.
3366 * The small block chain will be destroyed.
3368 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3370 SmallBlockChainStream** ppsbChain)
3372 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3373 ULARGE_INTEGER size, offset;
3374 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3375 ULONG propertyIndex;
3376 BOOL successRead, successWrite;
3377 StgProperty chainProperty;
3379 BlockChainStream *bbTempChain = NULL;
3380 BlockChainStream *bigBlockChain = NULL;
3383 * Create a temporary big block chain that doesn't have
3384 * an associated property. This temporary chain will be
3385 * used to copy data from small blocks to big blocks.
3387 bbTempChain = BlockChainStream_Construct(This,
3390 if(!bbTempChain) return NULL;
3392 * Grow the big block chain.
3394 size = SmallBlockChainStream_GetSize(*ppsbChain);
3395 BlockChainStream_SetSize(bbTempChain, size);
3398 * Copy the contents of the small block chain to the big block chain
3399 * by small block size increments.
3401 offset.s.LowPart = 0;
3402 offset.s.HighPart = 0;
3406 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3409 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3411 DEF_SMALL_BLOCK_SIZE,
3414 cbTotalRead += cbRead;
3416 successWrite = BlockChainStream_WriteAt(bbTempChain,
3421 cbTotalWritten += cbWritten;
3423 offset.s.LowPart += This->smallBlockSize;
3425 } while (successRead && successWrite);
3426 HeapFree(GetProcessHeap(),0,buffer);
3428 assert(cbTotalRead == cbTotalWritten);
3431 * Destroy the small block chain.
3433 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3434 size.s.HighPart = 0;
3436 SmallBlockChainStream_SetSize(*ppsbChain, size);
3437 SmallBlockChainStream_Destroy(*ppsbChain);
3441 * Change the property information. This chain is now a big block chain
3442 * and it doesn't reside in the small blocks chain anymore.
3444 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3446 chainProperty.startingBlock = bbHeadOfChain;
3448 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3451 * Destroy the temporary propertyless big block chain.
3452 * Create a new big block chain associated with this property.
3454 BlockChainStream_Destroy(bbTempChain);
3455 bigBlockChain = BlockChainStream_Construct(This,
3459 return bigBlockChain;
3462 /******************************************************************************
3463 ** Storage32InternalImpl implementation
3466 StorageInternalImpl* StorageInternalImpl_Construct(
3467 StorageImpl* ancestorStorage,
3468 ULONG rootPropertyIndex)
3470 StorageInternalImpl* newStorage;
3473 * Allocate space for the new storage object
3475 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3479 memset(newStorage, 0, sizeof(StorageInternalImpl));
3482 * Initialize the virtual function table.
3484 ICOM_VTBL(newStorage) = &Storage32InternalImpl_Vtbl;
3485 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3488 * Keep the ancestor storage pointer and nail a reference to it.
3490 newStorage->ancestorStorage = ancestorStorage;
3491 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3494 * Keep the index of the root property set for this storage,
3496 newStorage->rootPropertySetIndex = rootPropertyIndex;
3504 void StorageInternalImpl_Destroy(
3505 StorageInternalImpl* This)
3507 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3508 HeapFree(GetProcessHeap(), 0, This);
3511 /******************************************************************************
3513 ** Storage32InternalImpl_Commit
3515 ** The non-root storages cannot be opened in transacted mode thus this function
3518 HRESULT WINAPI StorageInternalImpl_Commit(
3520 DWORD grfCommitFlags) /* [in] */
3525 /******************************************************************************
3527 ** Storage32InternalImpl_Revert
3529 ** The non-root storages cannot be opened in transacted mode thus this function
3532 HRESULT WINAPI StorageInternalImpl_Revert(
3538 /******************************************************************************
3539 ** IEnumSTATSTGImpl implementation
3542 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3543 StorageImpl* parentStorage,
3544 ULONG firstPropertyNode)
3546 IEnumSTATSTGImpl* newEnumeration;
3548 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3550 if (newEnumeration!=0)
3553 * Set-up the virtual function table and reference count.
3555 ICOM_VTBL(newEnumeration) = &IEnumSTATSTGImpl_Vtbl;
3556 newEnumeration->ref = 0;
3559 * We want to nail-down the reference to the storage in case the
3560 * enumeration out-lives the storage in the client application.
3562 newEnumeration->parentStorage = parentStorage;
3563 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3565 newEnumeration->firstPropertyNode = firstPropertyNode;
3568 * Initialize the search stack
3570 newEnumeration->stackSize = 0;
3571 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3572 newEnumeration->stackToVisit =
3573 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3576 * Make sure the current node of the iterator is the first one.
3578 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3581 return newEnumeration;
3584 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3586 IStorage_Release((IStorage*)This->parentStorage);
3587 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3588 HeapFree(GetProcessHeap(), 0, This);
3591 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3592 IEnumSTATSTG* iface,
3596 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3599 * Perform a sanity check on the parameters.
3602 return E_INVALIDARG;
3605 * Initialize the return parameter.
3610 * Compare the riid with the interface IDs implemented by this object.
3612 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3614 *ppvObject = (IEnumSTATSTG*)This;
3616 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3618 *ppvObject = (IEnumSTATSTG*)This;
3622 * Check that we obtained an interface.
3624 if ((*ppvObject)==0)
3625 return E_NOINTERFACE;
3628 * Query Interface always increases the reference count by one when it is
3631 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3636 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3637 IEnumSTATSTG* iface)
3639 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3645 ULONG WINAPI IEnumSTATSTGImpl_Release(
3646 IEnumSTATSTG* iface)
3648 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3656 * If the reference count goes down to 0, perform suicide.
3660 IEnumSTATSTGImpl_Destroy(This);
3666 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3667 IEnumSTATSTG* iface,
3670 ULONG* pceltFetched)
3672 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3674 StgProperty currentProperty;
3675 STATSTG* currentReturnStruct = rgelt;
3676 ULONG objectFetched = 0;
3677 ULONG currentSearchNode;
3680 * Perform a sanity check on the parameters.
3682 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3683 return E_INVALIDARG;
3686 * To avoid the special case, get another pointer to a ULONG value if
3687 * the caller didn't supply one.
3689 if (pceltFetched==0)
3690 pceltFetched = &objectFetched;
3693 * Start the iteration, we will iterate until we hit the end of the
3694 * linked list or until we hit the number of items to iterate through
3699 * Start with the node at the top of the stack.
3701 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3703 while ( ( *pceltFetched < celt) &&
3704 ( currentSearchNode!=PROPERTY_NULL) )
3707 * Remove the top node from the stack
3709 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3712 * Read the property from the storage.
3714 StorageImpl_ReadProperty(This->parentStorage,
3719 * Copy the information to the return buffer.
3721 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3726 * Step to the next item in the iteration
3729 currentReturnStruct++;
3732 * Push the next search node in the search stack.
3734 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3737 * continue the iteration.
3739 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3742 if (*pceltFetched == celt)
3749 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3750 IEnumSTATSTG* iface,
3753 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3755 StgProperty currentProperty;
3756 ULONG objectFetched = 0;
3757 ULONG currentSearchNode;
3760 * Start with the node at the top of the stack.
3762 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3764 while ( (objectFetched < celt) &&
3765 (currentSearchNode!=PROPERTY_NULL) )
3768 * Remove the top node from the stack
3770 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3773 * Read the property from the storage.
3775 StorageImpl_ReadProperty(This->parentStorage,
3780 * Step to the next item in the iteration
3785 * Push the next search node in the search stack.
3787 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3790 * continue the iteration.
3792 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3795 if (objectFetched == celt)
3801 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3802 IEnumSTATSTG* iface)
3804 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3806 StgProperty rootProperty;
3807 BOOL readSuccessful;
3810 * Re-initialize the search stack to an empty stack
3812 This->stackSize = 0;
3815 * Read the root property from the storage.
3817 readSuccessful = StorageImpl_ReadProperty(
3818 This->parentStorage,
3819 This->firstPropertyNode,
3824 assert(rootProperty.sizeOfNameString!=0);
3827 * Push the search node in the search stack.
3829 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3835 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3836 IEnumSTATSTG* iface,
3837 IEnumSTATSTG** ppenum)
3839 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3841 IEnumSTATSTGImpl* newClone;
3844 * Perform a sanity check on the parameters.
3847 return E_INVALIDARG;
3849 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3850 This->firstPropertyNode);
3854 * The new clone enumeration must point to the same current node as
3857 newClone->stackSize = This->stackSize ;
3858 newClone->stackMaxSize = This->stackMaxSize ;
3859 newClone->stackToVisit =
3860 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3863 newClone->stackToVisit,
3865 sizeof(ULONG) * newClone->stackSize);
3867 *ppenum = (IEnumSTATSTG*)newClone;
3870 * Don't forget to nail down a reference to the clone before
3873 IEnumSTATSTGImpl_AddRef(*ppenum);
3878 INT IEnumSTATSTGImpl_FindParentProperty(
3879 IEnumSTATSTGImpl *This,
3880 ULONG childProperty,
3881 StgProperty *currentProperty,
3884 ULONG currentSearchNode;
3888 * To avoid the special case, get another pointer to a ULONG value if
3889 * the caller didn't supply one.
3892 thisNodeId = &foundNode;
3895 * Start with the node at the top of the stack.
3897 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3900 while (currentSearchNode!=PROPERTY_NULL)
3903 * Store the current node in the returned parameters
3905 *thisNodeId = currentSearchNode;
3908 * Remove the top node from the stack
3910 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3913 * Read the property from the storage.
3915 StorageImpl_ReadProperty(
3916 This->parentStorage,
3920 if (currentProperty->previousProperty == childProperty)
3921 return PROPERTY_RELATION_PREVIOUS;
3923 else if (currentProperty->nextProperty == childProperty)
3924 return PROPERTY_RELATION_NEXT;
3926 else if (currentProperty->dirProperty == childProperty)
3927 return PROPERTY_RELATION_DIR;
3930 * Push the next search node in the search stack.
3932 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3935 * continue the iteration.
3937 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3940 return PROPERTY_NULL;
3943 ULONG IEnumSTATSTGImpl_FindProperty(
3944 IEnumSTATSTGImpl* This,
3945 const OLECHAR* lpszPropName,
3946 StgProperty* currentProperty)
3948 ULONG currentSearchNode;
3951 * Start with the node at the top of the stack.
3953 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3955 while (currentSearchNode!=PROPERTY_NULL)
3958 * Remove the top node from the stack
3960 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3963 * Read the property from the storage.
3965 StorageImpl_ReadProperty(This->parentStorage,
3969 if ( propertyNameCmp(
3970 (OLECHAR*)currentProperty->name,
3971 (OLECHAR*)lpszPropName) == 0)
3972 return currentSearchNode;
3975 * Push the next search node in the search stack.
3977 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3980 * continue the iteration.
3982 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3985 return PROPERTY_NULL;
3988 void IEnumSTATSTGImpl_PushSearchNode(
3989 IEnumSTATSTGImpl* This,
3992 StgProperty rootProperty;
3993 BOOL readSuccessful;
3996 * First, make sure we're not trying to push an unexisting node.
3998 if (nodeToPush==PROPERTY_NULL)
4002 * First push the node to the stack
4004 if (This->stackSize == This->stackMaxSize)
4006 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4008 This->stackToVisit = HeapReAlloc(
4012 sizeof(ULONG) * This->stackMaxSize);
4015 This->stackToVisit[This->stackSize] = nodeToPush;
4019 * Read the root property from the storage.
4021 readSuccessful = StorageImpl_ReadProperty(
4022 This->parentStorage,
4028 assert(rootProperty.sizeOfNameString!=0);
4031 * Push the previous search node in the search stack.
4033 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4037 ULONG IEnumSTATSTGImpl_PopSearchNode(
4038 IEnumSTATSTGImpl* This,
4043 if (This->stackSize == 0)
4044 return PROPERTY_NULL;
4046 topNode = This->stackToVisit[This->stackSize-1];
4054 /******************************************************************************
4055 ** StorageUtl implementation
4058 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
4060 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
4063 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
4065 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
4068 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
4070 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
4073 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
4075 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
4078 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
4080 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4081 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4082 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4084 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
4087 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
4089 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4090 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4091 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4093 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
4096 void StorageUtl_CopyPropertyToSTATSTG(
4097 STATSTG* destination,
4098 StgProperty* source,
4102 * The copy of the string occurs only when the flag is not set
4104 if ((statFlags & STATFLAG_NONAME) != 0)
4106 destination->pwcsName = 0;
4110 destination->pwcsName =
4111 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4113 strcpyW((LPWSTR)destination->pwcsName, source->name);
4116 switch (source->propertyType)
4118 case PROPTYPE_STORAGE:
4120 destination->type = STGTY_STORAGE;
4122 case PROPTYPE_STREAM:
4123 destination->type = STGTY_STREAM;
4126 destination->type = STGTY_STREAM;
4130 destination->cbSize = source->size;
4132 currentReturnStruct->mtime = {0}; TODO
4133 currentReturnStruct->ctime = {0};
4134 currentReturnStruct->atime = {0};
4136 destination->grfMode = 0;
4137 destination->grfLocksSupported = 0;
4138 destination->clsid = source->propertyUniqueID;
4139 destination->grfStateBits = 0;
4140 destination->reserved = 0;
4143 /******************************************************************************
4144 ** BlockChainStream implementation
4147 BlockChainStream* BlockChainStream_Construct(
4148 StorageImpl* parentStorage,
4149 ULONG* headOfStreamPlaceHolder,
4150 ULONG propertyIndex)
4152 BlockChainStream* newStream;
4155 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4157 newStream->parentStorage = parentStorage;
4158 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4159 newStream->ownerPropertyIndex = propertyIndex;
4160 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4161 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4162 newStream->numBlocks = 0;
4164 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4166 while (blockIndex != BLOCK_END_OF_CHAIN)
4168 newStream->numBlocks++;
4169 newStream->tailIndex = blockIndex;
4171 if(FAILED(StorageImpl_GetNextBlockInChain(
4176 HeapFree(GetProcessHeap(), 0, newStream);
4184 void BlockChainStream_Destroy(BlockChainStream* This)
4186 HeapFree(GetProcessHeap(), 0, This);
4189 /******************************************************************************
4190 * BlockChainStream_GetHeadOfChain
4192 * Returns the head of this stream chain.
4193 * Some special chains don't have properties, their heads are kept in
4194 * This->headOfStreamPlaceHolder.
4197 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4199 StgProperty chainProperty;
4200 BOOL readSuccessful;
4202 if (This->headOfStreamPlaceHolder != 0)
4203 return *(This->headOfStreamPlaceHolder);
4205 if (This->ownerPropertyIndex != PROPERTY_NULL)
4207 readSuccessful = StorageImpl_ReadProperty(
4208 This->parentStorage,
4209 This->ownerPropertyIndex,
4214 return chainProperty.startingBlock;
4218 return BLOCK_END_OF_CHAIN;
4221 /******************************************************************************
4222 * BlockChainStream_GetCount
4224 * Returns the number of blocks that comprises this chain.
4225 * This is not the size of the stream as the last block may not be full!
4228 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4233 blockIndex = BlockChainStream_GetHeadOfChain(This);
4235 while (blockIndex != BLOCK_END_OF_CHAIN)
4239 if(FAILED(StorageImpl_GetNextBlockInChain(
4240 This->parentStorage,
4249 /******************************************************************************
4250 * BlockChainStream_ReadAt
4252 * Reads a specified number of bytes from this chain at the specified offset.
4253 * bytesRead may be NULL.
4254 * Failure will be returned if the specified number of bytes has not been read.
4256 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4257 ULARGE_INTEGER offset,
4262 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4263 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4264 ULONG bytesToReadInBuffer;
4267 BYTE* bigBlockBuffer;
4270 * Find the first block in the stream that contains part of the buffer.
4272 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4273 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4274 (blockNoInSequence < This->lastBlockNoInSequence) )
4276 blockIndex = BlockChainStream_GetHeadOfChain(This);
4277 This->lastBlockNoInSequence = blockNoInSequence;
4281 ULONG temp = blockNoInSequence;
4283 blockIndex = This->lastBlockNoInSequenceIndex;
4284 blockNoInSequence -= This->lastBlockNoInSequence;
4285 This->lastBlockNoInSequence = temp;
4288 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4290 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4292 blockNoInSequence--;
4295 This->lastBlockNoInSequenceIndex = blockIndex;
4298 * Start reading the buffer.
4301 bufferWalker = buffer;
4303 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4306 * Calculate how many bytes we can copy from this big block.
4308 bytesToReadInBuffer =
4309 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4312 * Copy those bytes to the buffer
4315 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4317 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4319 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4322 * Step to the next big block.
4324 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4327 bufferWalker += bytesToReadInBuffer;
4328 size -= bytesToReadInBuffer;
4329 *bytesRead += bytesToReadInBuffer;
4330 offsetInBlock = 0; /* There is no offset on the next block */
4337 /******************************************************************************
4338 * BlockChainStream_WriteAt
4340 * Writes the specified number of bytes to this chain at the specified offset.
4341 * bytesWritten may be NULL.
4342 * Will fail if not all specified number of bytes have been written.
4344 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4345 ULARGE_INTEGER offset,
4348 ULONG* bytesWritten)
4350 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4351 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4355 BYTE* bigBlockBuffer;
4358 * Find the first block in the stream that contains part of the buffer.
4360 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4361 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4362 (blockNoInSequence < This->lastBlockNoInSequence) )
4364 blockIndex = BlockChainStream_GetHeadOfChain(This);
4365 This->lastBlockNoInSequence = blockNoInSequence;
4369 ULONG temp = blockNoInSequence;
4371 blockIndex = This->lastBlockNoInSequenceIndex;
4372 blockNoInSequence -= This->lastBlockNoInSequence;
4373 This->lastBlockNoInSequence = temp;
4376 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4378 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4381 blockNoInSequence--;
4384 This->lastBlockNoInSequenceIndex = blockIndex;
4387 * Here, I'm casting away the constness on the buffer variable
4388 * This is OK since we don't intend to modify that buffer.
4391 bufferWalker = (BYTE*)buffer;
4393 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4396 * Calculate how many bytes we can copy from this big block.
4399 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4402 * Copy those bytes to the buffer
4404 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4406 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4408 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4411 * Step to the next big block.
4413 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4416 bufferWalker += bytesToWrite;
4417 size -= bytesToWrite;
4418 *bytesWritten += bytesToWrite;
4419 offsetInBlock = 0; /* There is no offset on the next block */
4425 /******************************************************************************
4426 * BlockChainStream_Shrink
4428 * Shrinks this chain in the big block depot.
4430 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4431 ULARGE_INTEGER newSize)
4433 ULONG blockIndex, extraBlock;
4438 * Reset the last accessed block cache.
4440 This->lastBlockNoInSequence = 0xFFFFFFFF;
4441 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4444 * Figure out how many blocks are needed to contain the new size
4446 numBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4448 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4451 blockIndex = BlockChainStream_GetHeadOfChain(This);
4454 * Go to the new end of chain
4456 while (count < numBlocks)
4458 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4464 /* Get the next block before marking the new end */
4465 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4469 /* Mark the new end of chain */
4470 StorageImpl_SetNextBlockInChain(
4471 This->parentStorage,
4473 BLOCK_END_OF_CHAIN);
4475 This->tailIndex = blockIndex;
4476 This->numBlocks = numBlocks;
4479 * Mark the extra blocks as free
4481 while (extraBlock != BLOCK_END_OF_CHAIN)
4483 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4486 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4487 extraBlock = blockIndex;
4493 /******************************************************************************
4494 * BlockChainStream_Enlarge
4496 * Grows this chain in the big block depot.
4498 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4499 ULARGE_INTEGER newSize)
4501 ULONG blockIndex, currentBlock;
4503 ULONG oldNumBlocks = 0;
4505 blockIndex = BlockChainStream_GetHeadOfChain(This);
4508 * Empty chain. Create the head.
4510 if (blockIndex == BLOCK_END_OF_CHAIN)
4512 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4513 StorageImpl_SetNextBlockInChain(This->parentStorage,
4515 BLOCK_END_OF_CHAIN);
4517 if (This->headOfStreamPlaceHolder != 0)
4519 *(This->headOfStreamPlaceHolder) = blockIndex;
4523 StgProperty chainProp;
4524 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4526 StorageImpl_ReadProperty(
4527 This->parentStorage,
4528 This->ownerPropertyIndex,
4531 chainProp.startingBlock = blockIndex;
4533 StorageImpl_WriteProperty(
4534 This->parentStorage,
4535 This->ownerPropertyIndex,
4539 This->tailIndex = blockIndex;
4540 This->numBlocks = 1;
4544 * Figure out how many blocks are needed to contain this stream
4546 newNumBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4548 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4552 * Go to the current end of chain
4554 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4556 currentBlock = blockIndex;
4558 while (blockIndex != BLOCK_END_OF_CHAIN)
4561 currentBlock = blockIndex;
4563 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4568 This->tailIndex = currentBlock;
4571 currentBlock = This->tailIndex;
4572 oldNumBlocks = This->numBlocks;
4575 * Add new blocks to the chain
4577 if (oldNumBlocks < newNumBlocks)
4579 while (oldNumBlocks < newNumBlocks)
4581 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4583 StorageImpl_SetNextBlockInChain(
4584 This->parentStorage,
4588 StorageImpl_SetNextBlockInChain(
4589 This->parentStorage,
4591 BLOCK_END_OF_CHAIN);
4593 currentBlock = blockIndex;
4597 This->tailIndex = blockIndex;
4598 This->numBlocks = newNumBlocks;
4604 /******************************************************************************
4605 * BlockChainStream_SetSize
4607 * Sets the size of this stream. The big block depot will be updated.
4608 * The file will grow if we grow the chain.
4610 * TODO: Free the actual blocks in the file when we shrink the chain.
4611 * Currently, the blocks are still in the file. So the file size
4612 * doesn't shrink even if we shrink streams.
4614 BOOL BlockChainStream_SetSize(
4615 BlockChainStream* This,
4616 ULARGE_INTEGER newSize)
4618 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4620 if (newSize.s.LowPart == size.s.LowPart)
4623 if (newSize.s.LowPart < size.s.LowPart)
4625 BlockChainStream_Shrink(This, newSize);
4629 ULARGE_INTEGER fileSize =
4630 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4632 ULONG diff = newSize.s.LowPart - size.s.LowPart;
4635 * Make sure the file stays a multiple of blocksize
4637 if ((diff % This->parentStorage->bigBlockSize) != 0)
4638 diff += (This->parentStorage->bigBlockSize -
4639 (diff % This->parentStorage->bigBlockSize) );
4641 fileSize.s.LowPart += diff;
4642 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4644 BlockChainStream_Enlarge(This, newSize);
4650 /******************************************************************************
4651 * BlockChainStream_GetSize
4653 * Returns the size of this chain.
4654 * Will return the block count if this chain doesn't have a property.
4656 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4658 StgProperty chainProperty;
4660 if(This->headOfStreamPlaceHolder == NULL)
4663 * This chain is a data stream read the property and return
4664 * the appropriate size
4666 StorageImpl_ReadProperty(
4667 This->parentStorage,
4668 This->ownerPropertyIndex,
4671 return chainProperty.size;
4676 * this chain is a chain that does not have a property, figure out the
4677 * size by making the product number of used blocks times the
4680 ULARGE_INTEGER result;
4681 result.s.HighPart = 0;
4684 BlockChainStream_GetCount(This) *
4685 This->parentStorage->bigBlockSize;
4691 /******************************************************************************
4692 ** SmallBlockChainStream implementation
4695 SmallBlockChainStream* SmallBlockChainStream_Construct(
4696 StorageImpl* parentStorage,
4697 ULONG propertyIndex)
4699 SmallBlockChainStream* newStream;
4701 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4703 newStream->parentStorage = parentStorage;
4704 newStream->ownerPropertyIndex = propertyIndex;
4709 void SmallBlockChainStream_Destroy(
4710 SmallBlockChainStream* This)
4712 HeapFree(GetProcessHeap(), 0, This);
4715 /******************************************************************************
4716 * SmallBlockChainStream_GetHeadOfChain
4718 * Returns the head of this chain of small blocks.
4720 ULONG SmallBlockChainStream_GetHeadOfChain(
4721 SmallBlockChainStream* This)
4723 StgProperty chainProperty;
4724 BOOL readSuccessful;
4726 if (This->ownerPropertyIndex)
4728 readSuccessful = StorageImpl_ReadProperty(
4729 This->parentStorage,
4730 This->ownerPropertyIndex,
4735 return chainProperty.startingBlock;
4740 return BLOCK_END_OF_CHAIN;
4743 /******************************************************************************
4744 * SmallBlockChainStream_GetNextBlockInChain
4746 * Returns the index of the next small block in this chain.
4749 * - BLOCK_END_OF_CHAIN: end of this chain
4750 * - BLOCK_UNUSED: small block 'blockIndex' is free
4752 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4753 SmallBlockChainStream* This,
4755 ULONG* nextBlockInChain)
4757 ULARGE_INTEGER offsetOfBlockInDepot;
4762 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4764 offsetOfBlockInDepot.s.HighPart = 0;
4765 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4768 * Read those bytes in the buffer from the small block file.
4770 success = BlockChainStream_ReadAt(
4771 This->parentStorage->smallBlockDepotChain,
4772 offsetOfBlockInDepot,
4779 StorageUtl_ReadDWord(&buffer, 0, nextBlockInChain);
4783 return STG_E_READFAULT;
4786 /******************************************************************************
4787 * SmallBlockChainStream_SetNextBlockInChain
4789 * Writes the index of the next block of the specified block in the small
4791 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4792 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4794 void SmallBlockChainStream_SetNextBlockInChain(
4795 SmallBlockChainStream* This,
4799 ULARGE_INTEGER offsetOfBlockInDepot;
4803 offsetOfBlockInDepot.s.HighPart = 0;
4804 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4806 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4809 * Read those bytes in the buffer from the small block file.
4811 BlockChainStream_WriteAt(
4812 This->parentStorage->smallBlockDepotChain,
4813 offsetOfBlockInDepot,
4819 /******************************************************************************
4820 * SmallBlockChainStream_FreeBlock
4822 * Flag small block 'blockIndex' as free in the small block depot.
4824 void SmallBlockChainStream_FreeBlock(
4825 SmallBlockChainStream* This,
4828 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4831 /******************************************************************************
4832 * SmallBlockChainStream_GetNextFreeBlock
4834 * Returns the index of a free small block. The small block depot will be
4835 * enlarged if necessary. The small block chain will also be enlarged if
4838 ULONG SmallBlockChainStream_GetNextFreeBlock(
4839 SmallBlockChainStream* This)
4841 ULARGE_INTEGER offsetOfBlockInDepot;
4844 ULONG blockIndex = 0;
4845 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4846 BOOL success = TRUE;
4847 ULONG smallBlocksPerBigBlock;
4849 offsetOfBlockInDepot.s.HighPart = 0;
4852 * Scan the small block depot for a free block
4854 while (nextBlockIndex != BLOCK_UNUSED)
4856 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4858 success = BlockChainStream_ReadAt(
4859 This->parentStorage->smallBlockDepotChain,
4860 offsetOfBlockInDepot,
4866 * If we run out of space for the small block depot, enlarge it
4870 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4872 if (nextBlockIndex != BLOCK_UNUSED)
4878 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4880 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4881 ULONG nextBlock, newsbdIndex;
4882 BYTE* smallBlockDepot;
4884 nextBlock = sbdIndex;
4885 while (nextBlock != BLOCK_END_OF_CHAIN)
4887 sbdIndex = nextBlock;
4888 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4891 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4892 if (sbdIndex != BLOCK_END_OF_CHAIN)
4893 StorageImpl_SetNextBlockInChain(
4894 This->parentStorage,
4898 StorageImpl_SetNextBlockInChain(
4899 This->parentStorage,
4901 BLOCK_END_OF_CHAIN);
4904 * Initialize all the small blocks to free
4907 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4909 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4910 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4915 * We have just created the small block depot.
4917 StgProperty rootProp;
4921 * Save it in the header
4923 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4924 StorageImpl_SaveFileHeader(This->parentStorage);
4927 * And allocate the first big block that will contain small blocks
4930 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4932 StorageImpl_SetNextBlockInChain(
4933 This->parentStorage,
4935 BLOCK_END_OF_CHAIN);
4937 StorageImpl_ReadProperty(
4938 This->parentStorage,
4939 This->parentStorage->rootPropertySetIndex,
4942 rootProp.startingBlock = sbStartIndex;
4943 rootProp.size.s.HighPart = 0;
4944 rootProp.size.s.LowPart = This->parentStorage->bigBlockSize;
4946 StorageImpl_WriteProperty(
4947 This->parentStorage,
4948 This->parentStorage->rootPropertySetIndex,
4954 smallBlocksPerBigBlock =
4955 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4958 * Verify if we have to allocate big blocks to contain small blocks
4960 if (blockIndex % smallBlocksPerBigBlock == 0)
4962 StgProperty rootProp;
4963 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4965 StorageImpl_ReadProperty(
4966 This->parentStorage,
4967 This->parentStorage->rootPropertySetIndex,
4970 if (rootProp.size.s.LowPart <
4971 (blocksRequired * This->parentStorage->bigBlockSize))
4973 rootProp.size.s.LowPart += This->parentStorage->bigBlockSize;
4975 BlockChainStream_SetSize(
4976 This->parentStorage->smallBlockRootChain,
4979 StorageImpl_WriteProperty(
4980 This->parentStorage,
4981 This->parentStorage->rootPropertySetIndex,
4989 /******************************************************************************
4990 * SmallBlockChainStream_ReadAt
4992 * Reads a specified number of bytes from this chain at the specified offset.
4993 * bytesRead may be NULL.
4994 * Failure will be returned if the specified number of bytes has not been read.
4996 BOOL SmallBlockChainStream_ReadAt(
4997 SmallBlockChainStream* This,
4998 ULARGE_INTEGER offset,
5003 ULARGE_INTEGER offsetInBigBlockFile;
5004 ULONG blockNoInSequence =
5005 offset.s.LowPart / This->parentStorage->smallBlockSize;
5007 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
5008 ULONG bytesToReadInBuffer;
5010 ULONG bytesReadFromBigBlockFile;
5014 * This should never happen on a small block file.
5016 assert(offset.s.HighPart==0);
5019 * Find the first block in the stream that contains part of the buffer.
5021 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5023 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5025 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5028 blockNoInSequence--;
5032 * Start reading the buffer.
5035 bufferWalker = buffer;
5037 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5040 * Calculate how many bytes we can copy from this small block.
5042 bytesToReadInBuffer =
5043 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5046 * Calculate the offset of the small block in the small block file.
5048 offsetInBigBlockFile.s.HighPart = 0;
5049 offsetInBigBlockFile.s.LowPart =
5050 blockIndex * This->parentStorage->smallBlockSize;
5052 offsetInBigBlockFile.s.LowPart += offsetInBlock;
5055 * Read those bytes in the buffer from the small block file.
5057 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5058 offsetInBigBlockFile,
5059 bytesToReadInBuffer,
5061 &bytesReadFromBigBlockFile);
5063 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5066 * Step to the next big block.
5068 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5070 bufferWalker += bytesToReadInBuffer;
5071 size -= bytesToReadInBuffer;
5072 *bytesRead += bytesToReadInBuffer;
5073 offsetInBlock = 0; /* There is no offset on the next block */
5079 /******************************************************************************
5080 * SmallBlockChainStream_WriteAt
5082 * Writes the specified number of bytes to this chain at the specified offset.
5083 * bytesWritten may be NULL.
5084 * Will fail if not all specified number of bytes have been written.
5086 BOOL SmallBlockChainStream_WriteAt(
5087 SmallBlockChainStream* This,
5088 ULARGE_INTEGER offset,
5091 ULONG* bytesWritten)
5093 ULARGE_INTEGER offsetInBigBlockFile;
5094 ULONG blockNoInSequence =
5095 offset.s.LowPart / This->parentStorage->smallBlockSize;
5097 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
5098 ULONG bytesToWriteInBuffer;
5100 ULONG bytesWrittenFromBigBlockFile;
5104 * This should never happen on a small block file.
5106 assert(offset.s.HighPart==0);
5109 * Find the first block in the stream that contains part of the buffer.
5111 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5113 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5115 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5117 blockNoInSequence--;
5121 * Start writing the buffer.
5123 * Here, I'm casting away the constness on the buffer variable
5124 * This is OK since we don't intend to modify that buffer.
5127 bufferWalker = (BYTE*)buffer;
5128 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5131 * Calculate how many bytes we can copy to this small block.
5133 bytesToWriteInBuffer =
5134 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5137 * Calculate the offset of the small block in the small block file.
5139 offsetInBigBlockFile.s.HighPart = 0;
5140 offsetInBigBlockFile.s.LowPart =
5141 blockIndex * This->parentStorage->smallBlockSize;
5143 offsetInBigBlockFile.s.LowPart += offsetInBlock;
5146 * Write those bytes in the buffer to the small block file.
5148 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5149 offsetInBigBlockFile,
5150 bytesToWriteInBuffer,
5152 &bytesWrittenFromBigBlockFile);
5154 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5157 * Step to the next big block.
5159 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5162 bufferWalker += bytesToWriteInBuffer;
5163 size -= bytesToWriteInBuffer;
5164 *bytesWritten += bytesToWriteInBuffer;
5165 offsetInBlock = 0; /* There is no offset on the next block */
5171 /******************************************************************************
5172 * SmallBlockChainStream_Shrink
5174 * Shrinks this chain in the small block depot.
5176 BOOL SmallBlockChainStream_Shrink(
5177 SmallBlockChainStream* This,
5178 ULARGE_INTEGER newSize)
5180 ULONG blockIndex, extraBlock;
5184 numBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5186 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5189 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5192 * Go to the new end of chain
5194 while (count < numBlocks)
5196 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5203 * If the count is 0, we have a special case, the head of the chain was
5208 StgProperty chainProp;
5210 StorageImpl_ReadProperty(This->parentStorage,
5211 This->ownerPropertyIndex,
5214 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5216 StorageImpl_WriteProperty(This->parentStorage,
5217 This->ownerPropertyIndex,
5221 * We start freeing the chain at the head block.
5223 extraBlock = blockIndex;
5227 /* Get the next block before marking the new end */
5228 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5232 /* Mark the new end of chain */
5233 SmallBlockChainStream_SetNextBlockInChain(
5236 BLOCK_END_OF_CHAIN);
5240 * Mark the extra blocks as free
5242 while (extraBlock != BLOCK_END_OF_CHAIN)
5244 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5247 SmallBlockChainStream_FreeBlock(This, extraBlock);
5248 extraBlock = blockIndex;
5254 /******************************************************************************
5255 * SmallBlockChainStream_Enlarge
5257 * Grows this chain in the small block depot.
5259 BOOL SmallBlockChainStream_Enlarge(
5260 SmallBlockChainStream* This,
5261 ULARGE_INTEGER newSize)
5263 ULONG blockIndex, currentBlock;
5265 ULONG oldNumBlocks = 0;
5267 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5272 if (blockIndex == BLOCK_END_OF_CHAIN)
5275 StgProperty chainProp;
5277 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5280 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5282 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5285 blockIndex = chainProp.startingBlock;
5286 SmallBlockChainStream_SetNextBlockInChain(
5289 BLOCK_END_OF_CHAIN);
5292 currentBlock = blockIndex;
5295 * Figure out how many blocks are needed to contain this stream
5297 newNumBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5299 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5303 * Go to the current end of chain
5305 while (blockIndex != BLOCK_END_OF_CHAIN)
5308 currentBlock = blockIndex;
5309 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5314 * Add new blocks to the chain
5316 while (oldNumBlocks < newNumBlocks)
5318 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5319 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5321 SmallBlockChainStream_SetNextBlockInChain(
5324 BLOCK_END_OF_CHAIN);
5326 currentBlock = blockIndex;
5333 /******************************************************************************
5334 * SmallBlockChainStream_GetCount
5336 * Returns the number of blocks that comprises this chain.
5337 * This is not the size of this chain as the last block may not be full!
5339 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5344 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5346 while (blockIndex != BLOCK_END_OF_CHAIN)
5350 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5357 /******************************************************************************
5358 * SmallBlockChainStream_SetSize
5360 * Sets the size of this stream.
5361 * The file will grow if we grow the chain.
5363 * TODO: Free the actual blocks in the file when we shrink the chain.
5364 * Currently, the blocks are still in the file. So the file size
5365 * doesn't shrink even if we shrink streams.
5367 BOOL SmallBlockChainStream_SetSize(
5368 SmallBlockChainStream* This,
5369 ULARGE_INTEGER newSize)
5371 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5373 if (newSize.s.LowPart == size.s.LowPart)
5376 if (newSize.s.LowPart < size.s.LowPart)
5378 SmallBlockChainStream_Shrink(This, newSize);
5382 SmallBlockChainStream_Enlarge(This, newSize);
5388 /******************************************************************************
5389 * SmallBlockChainStream_GetSize
5391 * Returns the size of this chain.
5393 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5395 StgProperty chainProperty;
5397 StorageImpl_ReadProperty(
5398 This->parentStorage,
5399 This->ownerPropertyIndex,
5402 return chainProperty.size;
5405 /******************************************************************************
5406 * StgCreateDocfile [OLE32.144]
5408 HRESULT WINAPI StgCreateDocfile(
5412 IStorage **ppstgOpen)
5414 StorageImpl* newStorage = 0;
5415 HANDLE hFile = INVALID_HANDLE_VALUE;
5420 DWORD fileAttributes;
5421 WCHAR tempFileName[MAX_PATH];
5423 TRACE("(%s, %lx, %ld, %p)\n",
5424 debugstr_w(pwcsName), grfMode,
5425 reserved, ppstgOpen);
5428 * Validate the parameters
5431 return STG_E_INVALIDPOINTER;
5434 * Validate the STGM flags
5436 if ( FAILED( validateSTGM(grfMode) ))
5437 return STG_E_INVALIDFLAG;
5440 * Generate a unique name.
5444 WCHAR tempPath[MAX_PATH];
5445 WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5447 if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5448 return STG_E_INVALIDFLAG;
5449 if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5450 return STG_E_INVALIDFLAG;
5452 memset(tempPath, 0, sizeof(tempPath));
5453 memset(tempFileName, 0, sizeof(tempFileName));
5455 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5458 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5459 pwcsName = tempFileName;
5461 return STG_E_INSUFFICIENTMEMORY;
5463 creationMode = TRUNCATE_EXISTING;
5467 creationMode = GetCreationModeFromSTGM(grfMode);
5471 * Interpret the STGM value grfMode
5473 shareMode = GetShareModeFromSTGM(grfMode);
5474 accessMode = GetAccessModeFromSTGM(grfMode);
5476 if (grfMode & STGM_DELETEONRELEASE)
5477 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5479 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5481 if (grfMode & STGM_TRANSACTED)
5482 FIXME("Transacted mode not implemented.\n");
5485 * Initialize the "out" parameter.
5489 hFile = CreateFileW(pwcsName,
5497 if (hFile == INVALID_HANDLE_VALUE)
5503 * Allocate and initialize the new IStorage32object.
5505 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5507 if (newStorage == 0)
5508 return STG_E_INSUFFICIENTMEMORY;
5510 hr = StorageImpl_Construct(
5521 HeapFree(GetProcessHeap(), 0, newStorage);
5526 * Get an "out" pointer for the caller.
5528 hr = StorageBaseImpl_QueryInterface(
5529 (IStorage*)newStorage,
5530 (REFIID)&IID_IStorage,
5536 /******************************************************************************
5537 * StgOpenStorage [OLE32.148]
5539 HRESULT WINAPI StgOpenStorage(
5540 const OLECHAR *pwcsName,
5541 IStorage *pstgPriority,
5545 IStorage **ppstgOpen)
5547 StorageImpl* newStorage = 0;
5552 WCHAR fullname[MAX_PATH];
5555 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5556 debugstr_w(pwcsName), pstgPriority, grfMode,
5557 snbExclude, reserved, ppstgOpen);
5560 * Perform a sanity check
5562 if (( pwcsName == 0) || (ppstgOpen == 0) )
5564 hr = STG_E_INVALIDPOINTER;
5569 * Validate the STGM flags
5571 if ( FAILED( validateSTGM(grfMode) ))
5573 hr = STG_E_INVALIDFLAG;
5578 * Interpret the STGM value grfMode
5580 shareMode = GetShareModeFromSTGM(grfMode);
5581 accessMode = GetAccessModeFromSTGM(grfMode);
5584 * Initialize the "out" parameter.
5588 hFile = CreateFileW( pwcsName,
5593 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5596 length = GetFileSize(hFile, NULL);
5598 if (hFile==INVALID_HANDLE_VALUE)
5600 DWORD last_error = GetLastError();
5606 case ERROR_FILE_NOT_FOUND:
5607 hr = STG_E_FILENOTFOUND;
5610 case ERROR_PATH_NOT_FOUND:
5611 hr = STG_E_PATHNOTFOUND;
5614 case ERROR_ACCESS_DENIED:
5615 case ERROR_WRITE_PROTECT:
5616 hr = STG_E_ACCESSDENIED;
5619 case ERROR_SHARING_VIOLATION:
5620 hr = STG_E_SHAREVIOLATION;
5631 * Allocate and initialize the new IStorage32object.
5633 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5635 if (newStorage == 0)
5637 hr = STG_E_INSUFFICIENTMEMORY;
5641 /* if the file's length was zero, initialize the storage */
5642 hr = StorageImpl_Construct(
5653 HeapFree(GetProcessHeap(), 0, newStorage);
5655 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5657 if(hr == STG_E_INVALIDHEADER)
5658 hr = STG_E_FILEALREADYEXISTS;
5662 /* prepare the file name string given in lieu of the root property name */
5663 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5664 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5665 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5668 * Get an "out" pointer for the caller.
5670 hr = StorageBaseImpl_QueryInterface(
5671 (IStorage*)newStorage,
5672 (REFIID)&IID_IStorage,
5676 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5680 /******************************************************************************
5681 * StgCreateDocfileOnILockBytes [OLE32.145]
5683 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5687 IStorage** ppstgOpen)
5689 StorageImpl* newStorage = 0;
5693 * Validate the parameters
5695 if ((ppstgOpen == 0) || (plkbyt == 0))
5696 return STG_E_INVALIDPOINTER;
5699 * Allocate and initialize the new IStorage object.
5701 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5703 if (newStorage == 0)
5704 return STG_E_INSUFFICIENTMEMORY;
5706 hr = StorageImpl_Construct(
5717 HeapFree(GetProcessHeap(), 0, newStorage);
5722 * Get an "out" pointer for the caller.
5724 hr = StorageBaseImpl_QueryInterface(
5725 (IStorage*)newStorage,
5726 (REFIID)&IID_IStorage,
5732 /******************************************************************************
5733 * StgOpenStorageOnILockBytes [OLE32.149]
5735 HRESULT WINAPI StgOpenStorageOnILockBytes(
5737 IStorage *pstgPriority,
5741 IStorage **ppstgOpen)
5743 StorageImpl* newStorage = 0;
5747 * Perform a sanity check
5749 if ((plkbyt == 0) || (ppstgOpen == 0))
5750 return STG_E_INVALIDPOINTER;
5753 * Validate the STGM flags
5755 if ( FAILED( validateSTGM(grfMode) ))
5756 return STG_E_INVALIDFLAG;
5759 * Initialize the "out" parameter.
5764 * Allocate and initialize the new IStorage object.
5766 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5768 if (newStorage == 0)
5769 return STG_E_INSUFFICIENTMEMORY;
5771 hr = StorageImpl_Construct(
5782 HeapFree(GetProcessHeap(), 0, newStorage);
5787 * Get an "out" pointer for the caller.
5789 hr = StorageBaseImpl_QueryInterface(
5790 (IStorage*)newStorage,
5791 (REFIID)&IID_IStorage,
5797 /******************************************************************************
5798 * StgSetTimes [ole32.150]
5799 * StgSetTimes [OLE32.150]
5803 HRESULT WINAPI StgSetTimes(OLECHAR *str, FILETIME *a, FILETIME *b, FILETIME *c )
5805 FIXME("(%s, %p, %p, %p),stub!\n", debugstr_w(str), a, b, c);
5809 /******************************************************************************
5810 * StgIsStorageILockBytes [OLE32.147]
5812 * Determines if the ILockBytes contains a storage object.
5814 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5817 ULARGE_INTEGER offset;
5819 offset.s.HighPart = 0;
5820 offset.s.LowPart = 0;
5822 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5824 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5830 /******************************************************************************
5831 * WriteClassStg [OLE32.158]
5833 * This method will store the specified CLSID in the specified storage object
5835 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5841 hRes = IStorage_SetClass(pStg, rclsid);
5846 /***********************************************************************
5847 * ReadClassStg (OLE32.134)
5849 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5851 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5861 * read a STATSTG structure (contains the clsid) from the storage
5863 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5866 *pclsid=pstatstg.clsid;
5871 /***********************************************************************
5872 * OleLoadFromStream (OLE32.113)
5874 * This function loads an object from stream
5876 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5880 LPPERSISTSTREAM xstm;
5882 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
5884 res=ReadClassStm(pStm,&clsid);
5885 if (!SUCCEEDED(res))
5887 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5888 if (!SUCCEEDED(res))
5890 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
5891 if (!SUCCEEDED(res)) {
5892 IUnknown_Release((IUnknown*)*ppvObj);
5895 res=IPersistStream_Load(xstm,pStm);
5896 IPersistStream_Release(xstm);
5897 /* FIXME: all refcounts ok at this point? I think they should be:
5900 * xstm : 0 (released)
5905 /***********************************************************************
5906 * OleSaveToStream (OLE32.125)
5908 * This function saves an object with the IPersistStream interface on it
5909 * to the specified stream.
5911 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5917 TRACE("(%p,%p)\n",pPStm,pStm);
5919 res=IPersistStream_GetClassID(pPStm,&clsid);
5921 if (SUCCEEDED(res)){
5923 res=WriteClassStm(pStm,&clsid);
5927 res=IPersistStream_Save(pPStm,pStm,TRUE);
5930 TRACE("Finished Save\n");
5934 /****************************************************************************
5935 * This method validate a STGM parameter that can contain the values below
5937 * STGM_DIRECT 0x00000000
5938 * STGM_TRANSACTED 0x00010000
5939 * STGM_SIMPLE 0x08000000
5941 * STGM_READ 0x00000000
5942 * STGM_WRITE 0x00000001
5943 * STGM_READWRITE 0x00000002
5945 * STGM_SHARE_DENY_NONE 0x00000040
5946 * STGM_SHARE_DENY_READ 0x00000030
5947 * STGM_SHARE_DENY_WRITE 0x00000020
5948 * STGM_SHARE_EXCLUSIVE 0x00000010
5950 * STGM_PRIORITY 0x00040000
5951 * STGM_DELETEONRELEASE 0x04000000
5953 * STGM_CREATE 0x00001000
5954 * STGM_CONVERT 0x00020000
5955 * STGM_FAILIFTHERE 0x00000000
5957 * STGM_NOSCRATCH 0x00100000
5958 * STGM_NOSNAPSHOT 0x00200000
5960 static HRESULT validateSTGM(DWORD stgm)
5962 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5963 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5964 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5966 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5967 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5968 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5970 BOOL bSTGM_SHARE_DENY_NONE =
5971 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5973 BOOL bSTGM_SHARE_DENY_READ =
5974 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5976 BOOL bSTGM_SHARE_DENY_WRITE =
5977 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5979 BOOL bSTGM_SHARE_EXCLUSIVE =
5980 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5982 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5983 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5985 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5986 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5989 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5991 if ( ! bSTGM_DIRECT )
5992 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5996 * STGM_WRITE | STGM_READWRITE | STGM_READ
5999 if( bSTGM_WRITE && bSTGM_READWRITE )
6003 * STGM_SHARE_DENY_NONE | others
6004 * (I assume here that DENY_READ implies DENY_WRITE)
6006 if ( bSTGM_SHARE_DENY_NONE )
6007 if ( bSTGM_SHARE_DENY_READ ||
6008 bSTGM_SHARE_DENY_WRITE ||
6009 bSTGM_SHARE_EXCLUSIVE)
6013 * STGM_CREATE | STGM_CONVERT
6014 * if both are false, STGM_FAILIFTHERE is set to TRUE
6016 if ( bSTGM_CREATE && bSTGM_CONVERT )
6020 * STGM_NOSCRATCH requires STGM_TRANSACTED
6022 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
6026 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6027 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6029 if (bSTGM_NOSNAPSHOT)
6031 if ( ! ( bSTGM_TRANSACTED &&
6032 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
6039 /****************************************************************************
6040 * GetShareModeFromSTGM
6042 * This method will return a share mode flag from a STGM value.
6043 * The STGM value is assumed valid.
6045 static DWORD GetShareModeFromSTGM(DWORD stgm)
6047 DWORD dwShareMode = 0;
6048 BOOL bSTGM_SHARE_DENY_NONE =
6049 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
6051 BOOL bSTGM_SHARE_DENY_READ =
6052 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
6054 BOOL bSTGM_SHARE_DENY_WRITE =
6055 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
6057 BOOL bSTGM_SHARE_EXCLUSIVE =
6058 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
6060 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
6063 if (bSTGM_SHARE_DENY_NONE)
6064 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6066 if (bSTGM_SHARE_DENY_WRITE)
6067 dwShareMode = FILE_SHARE_READ;
6072 /****************************************************************************
6073 * GetAccessModeFromSTGM
6075 * This method will return an access mode flag from a STGM value.
6076 * The STGM value is assumed valid.
6078 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6080 DWORD dwDesiredAccess = GENERIC_READ;
6081 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
6082 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
6083 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
6086 dwDesiredAccess = GENERIC_READ;
6089 dwDesiredAccess |= GENERIC_WRITE;
6091 if (bSTGM_READWRITE)
6092 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
6094 return dwDesiredAccess;
6097 /****************************************************************************
6098 * GetCreationModeFromSTGM
6100 * This method will return a creation mode flag from a STGM value.
6101 * The STGM value is assumed valid.
6103 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6105 if ( stgm & STGM_CREATE)
6106 return CREATE_ALWAYS;
6107 if (stgm & STGM_CONVERT) {
6108 FIXME("STGM_CONVERT not implemented!\n");
6111 /* All other cases */
6112 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
6113 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
6118 /*************************************************************************
6119 * OLECONVERT_LoadOLE10 [Internal]
6121 * Loads the OLE10 STREAM to memory
6124 * pOleStream [I] The OLESTREAM
6125 * pData [I] Data Structure for the OLESTREAM Data
6129 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6130 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6133 * This function is used by OleConvertOLESTREAMToIStorage only.
6135 * Memory allocated for pData must be freed by the caller
6137 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6140 HRESULT hRes = S_OK;
6144 pData->pData = NULL;
6145 pData->pstrOleObjFileName = (CHAR *) NULL;
6147 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6150 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6151 if(dwSize != sizeof(pData->dwOleID))
6153 hRes = CONVERT10_E_OLESTREAM_GET;
6155 else if(pData->dwOleID != OLESTREAM_ID)
6157 hRes = CONVERT10_E_OLESTREAM_FMT;
6168 /* Get the TypeID...more info needed for this field */
6169 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6170 if(dwSize != sizeof(pData->dwTypeID))
6172 hRes = CONVERT10_E_OLESTREAM_GET;
6177 if(pData->dwTypeID != 0)
6179 /* Get the lenght of the OleTypeName */
6180 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6181 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6183 hRes = CONVERT10_E_OLESTREAM_GET;
6188 if(pData->dwOleTypeNameLength > 0)
6190 /* Get the OleTypeName */
6191 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6192 if(dwSize != pData->dwOleTypeNameLength)
6194 hRes = CONVERT10_E_OLESTREAM_GET;
6200 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6201 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6203 hRes = CONVERT10_E_OLESTREAM_GET;
6207 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6208 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6209 pData->pstrOleObjFileName = (CHAR *)malloc(pData->dwOleObjFileNameLength);
6210 if(pData->pstrOleObjFileName)
6212 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6213 if(dwSize != pData->dwOleObjFileNameLength)
6215 hRes = CONVERT10_E_OLESTREAM_GET;
6219 hRes = CONVERT10_E_OLESTREAM_GET;
6224 /* Get the Width of the Metafile */
6225 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6226 if(dwSize != sizeof(pData->dwMetaFileWidth))
6228 hRes = CONVERT10_E_OLESTREAM_GET;
6232 /* Get the Height of the Metafile */
6233 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6234 if(dwSize != sizeof(pData->dwMetaFileHeight))
6236 hRes = CONVERT10_E_OLESTREAM_GET;
6242 /* Get the Lenght of the Data */
6243 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6244 if(dwSize != sizeof(pData->dwDataLength))
6246 hRes = CONVERT10_E_OLESTREAM_GET;
6250 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6252 if(!bStrem1) /* if it is a second OLE stream data */
6254 pData->dwDataLength -= 8;
6255 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6256 if(dwSize != sizeof(pData->strUnknown))
6258 hRes = CONVERT10_E_OLESTREAM_GET;
6264 if(pData->dwDataLength > 0)
6266 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6268 /* Get Data (ex. IStorage, Metafile, or BMP) */
6271 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6272 if(dwSize != pData->dwDataLength)
6274 hRes = CONVERT10_E_OLESTREAM_GET;
6279 hRes = CONVERT10_E_OLESTREAM_GET;
6288 /*************************************************************************
6289 * OLECONVERT_SaveOLE10 [Internal]
6291 * Saves the OLE10 STREAM From memory
6294 * pData [I] Data Structure for the OLESTREAM Data
6295 * pOleStream [I] The OLESTREAM to save
6299 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6302 * This function is used by OleConvertIStorageToOLESTREAM only.
6305 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6308 HRESULT hRes = S_OK;
6312 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6313 if(dwSize != sizeof(pData->dwOleID))
6315 hRes = CONVERT10_E_OLESTREAM_PUT;
6320 /* Set the TypeID */
6321 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6322 if(dwSize != sizeof(pData->dwTypeID))
6324 hRes = CONVERT10_E_OLESTREAM_PUT;
6328 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6330 /* Set the Lenght of the OleTypeName */
6331 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6332 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6334 hRes = CONVERT10_E_OLESTREAM_PUT;
6339 if(pData->dwOleTypeNameLength > 0)
6341 /* Set the OleTypeName */
6342 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6343 if(dwSize != pData->dwOleTypeNameLength)
6345 hRes = CONVERT10_E_OLESTREAM_PUT;
6352 /* Set the width of the Metafile */
6353 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6354 if(dwSize != sizeof(pData->dwMetaFileWidth))
6356 hRes = CONVERT10_E_OLESTREAM_PUT;
6362 /* Set the height of the Metafile */
6363 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6364 if(dwSize != sizeof(pData->dwMetaFileHeight))
6366 hRes = CONVERT10_E_OLESTREAM_PUT;
6372 /* Set the lenght of the Data */
6373 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6374 if(dwSize != sizeof(pData->dwDataLength))
6376 hRes = CONVERT10_E_OLESTREAM_PUT;
6382 if(pData->dwDataLength > 0)
6384 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6385 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6386 if(dwSize != pData->dwDataLength)
6388 hRes = CONVERT10_E_OLESTREAM_PUT;
6396 /*************************************************************************
6397 * OLECONVERT_GetOLE20FromOLE10[Internal]
6399 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6400 * opens it, and copies the content to the dest IStorage for
6401 * OleConvertOLESTREAMToIStorage
6405 * pDestStorage [I] The IStorage to copy the data to
6406 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6407 * nBufferLength [I] The size of the buffer
6416 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6420 IStorage *pTempStorage;
6421 DWORD dwNumOfBytesWritten;
6422 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6423 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6425 /* Create a temp File */
6426 GetTempPathW(MAX_PATH, wstrTempDir);
6427 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6428 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6430 if(hFile != INVALID_HANDLE_VALUE)
6432 /* Write IStorage Data to File */
6433 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6436 /* Open and copy temp storage to the Dest Storage */
6437 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6440 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6441 StorageBaseImpl_Release(pTempStorage);
6443 DeleteFileW(wstrTempFile);
6448 /*************************************************************************
6449 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6451 * Saves the OLE10 STREAM From memory
6454 * pStorage [I] The Src IStorage to copy
6455 * pData [I] The Dest Memory to write to.
6458 * The size in bytes allocated for pData
6461 * Memory allocated for pData must be freed by the caller
6463 * Used by OleConvertIStorageToOLESTREAM only.
6466 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6470 DWORD nDataLength = 0;
6471 IStorage *pTempStorage;
6472 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6473 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6477 /* Create temp Storage */
6478 GetTempPathW(MAX_PATH, wstrTempDir);
6479 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6480 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6484 /* Copy Src Storage to the Temp Storage */
6485 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6486 StorageBaseImpl_Release(pTempStorage);
6488 /* Open Temp Storage as a file and copy to memory */
6489 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6490 if(hFile != INVALID_HANDLE_VALUE)
6492 nDataLength = GetFileSize(hFile, NULL);
6493 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6494 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6497 DeleteFileW(wstrTempFile);
6502 /*************************************************************************
6503 * OLECONVERT_CreateOleStream [Internal]
6505 * Creates the "\001OLE" stream in the IStorage if neccessary.
6508 * pStorage [I] Dest storage to create the stream in
6514 * This function is used by OleConvertOLESTREAMToIStorage only.
6516 * This stream is still unknown, MS Word seems to have extra data
6517 * but since the data is stored in the OLESTREAM there should be
6518 * no need to recreate the stream. If the stream is manually
6519 * deleted it will create it with this default data.
6522 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6526 WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6527 BYTE pOleStreamHeader [] =
6529 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6530 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6531 0x00, 0x00, 0x00, 0x00
6534 /* Create stream if not present */
6535 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6536 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6540 /* Write default Data */
6541 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6542 IStream_Release(pStream);
6547 /*************************************************************************
6548 * OLECONVERT_CreateCompObjStream [Internal]
6550 * Creates a "\001CompObj" is the destination IStorage if necessary.
6553 * pStorage [I] The dest IStorage to create the CompObj Stream
6555 * strOleTypeName [I] The ProgID
6559 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6562 * This function is used by OleConvertOLESTREAMToIStorage only.
6564 * The stream data is stored in the OLESTREAM and there should be
6565 * no need to recreate the stream. If the stream is manually
6566 * deleted it will attempt to create it by querying the registry.
6570 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6573 HRESULT hStorageRes, hRes = S_OK;
6574 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6575 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6577 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6578 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6580 /* Initialize the CompObj structure */
6581 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6582 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6583 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6586 /* Create a CompObj stream if it doesn't exist */
6587 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6588 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6589 if(hStorageRes == S_OK)
6591 /* copy the OleTypeName to the compobj struct */
6592 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6593 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6595 /* copy the OleTypeName to the compobj struct */
6596 /* Note: in the test made, these were Identical */
6597 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6598 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6601 hRes = CLSIDFromProgID16(IStorageCompObj.strProgIDName, &(IStorageCompObj.clsid));
6607 /* Get the CLSID Default Name from the Registry */
6608 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6609 if(hErr == ERROR_SUCCESS)
6611 char strTemp[OLESTREAM_MAX_STR_LEN];
6612 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6613 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6614 if(hErr == ERROR_SUCCESS)
6616 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6622 /* Write CompObj Structure to stream */
6623 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6625 WriteClassStm(pStream,&(IStorageCompObj.clsid));
6627 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6628 if(IStorageCompObj.dwCLSIDNameLength > 0)
6630 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6632 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6633 if(IStorageCompObj.dwOleTypeNameLength > 0)
6635 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6637 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6638 if(IStorageCompObj.dwProgIDNameLength > 0)
6640 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6642 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6643 IStream_Release(pStream);
6649 /*************************************************************************
6650 * OLECONVERT_CreateOlePresStream[Internal]
6652 * Creates the "\002OlePres000" Stream with the Metafile data
6655 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6656 * dwExtentX [I] Width of the Metafile
6657 * dwExtentY [I] Height of the Metafile
6658 * pData [I] Metafile data
6659 * dwDataLength [I] Size of the Metafile data
6663 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6666 * This function is used by OleConvertOLESTREAMToIStorage only.
6669 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6673 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6674 BYTE pOlePresStreamHeader [] =
6676 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6677 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6678 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6679 0x00, 0x00, 0x00, 0x00
6682 BYTE pOlePresStreamHeaderEmpty [] =
6684 0x00, 0x00, 0x00, 0x00,
6685 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6686 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6687 0x00, 0x00, 0x00, 0x00
6690 /* Create the OlePres000 Stream */
6691 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6692 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6697 OLECONVERT_ISTORAGE_OLEPRES OlePres;
6699 memset(&OlePres, 0, sizeof(OlePres));
6700 /* Do we have any metafile data to save */
6701 if(dwDataLength > 0)
6703 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6704 nHeaderSize = sizeof(pOlePresStreamHeader);
6708 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
6709 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
6711 /* Set width and height of the metafile */
6712 OlePres.dwExtentX = dwExtentX;
6713 OlePres.dwExtentY = -dwExtentY;
6715 /* Set Data and Lenght */
6716 if(dwDataLength > sizeof(METAFILEPICT16))
6718 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
6719 OlePres.pData = &(pData[8]);
6721 /* Save OlePres000 Data to Stream */
6722 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
6723 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
6724 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
6725 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
6726 if(OlePres.dwSize > 0)
6728 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
6730 IStream_Release(pStream);
6734 /*************************************************************************
6735 * OLECONVERT_CreateOle10NativeStream [Internal]
6737 * Creates the "\001Ole10Native" Stream (should contain a BMP)
6740 * pStorage [I] Dest storage to create the stream in
6741 * pData [I] Ole10 Native Data (ex. bmp)
6742 * dwDataLength [I] Size of the Ole10 Native Data
6748 * This function is used by OleConvertOLESTREAMToIStorage only.
6750 * Might need to verify the data and return appropriate error message
6753 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
6757 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6759 /* Create the Ole10Native Stream */
6760 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6761 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6765 /* Write info to stream */
6766 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
6767 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
6768 IStream_Release(pStream);
6773 /*************************************************************************
6774 * OLECONVERT_GetOLE10ProgID [Internal]
6776 * Finds the ProgID (or OleTypeID) from the IStorage
6779 * pStorage [I] The Src IStorage to get the ProgID
6780 * strProgID [I] the ProgID string to get
6781 * dwSize [I] the size of the string
6785 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6788 * This function is used by OleConvertIStorageToOLESTREAM only.
6792 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
6796 LARGE_INTEGER iSeekPos;
6797 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
6798 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6800 /* Open the CompObj Stream */
6801 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6802 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6806 /*Get the OleType from the CompObj Stream */
6807 iSeekPos.s.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
6808 iSeekPos.s.HighPart = 0;
6810 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6811 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
6812 iSeekPos.s.LowPart = CompObj.dwCLSIDNameLength;
6813 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6814 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
6815 iSeekPos.s.LowPart = CompObj.dwOleTypeNameLength;
6816 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6818 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
6821 IStream_Read(pStream, strProgID, *dwSize, NULL);
6823 IStream_Release(pStream);
6828 LPOLESTR wstrProgID;
6830 /* Get the OleType from the registry */
6831 REFCLSID clsid = &(stat.clsid);
6832 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
6833 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
6836 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
6843 /*************************************************************************
6844 * OLECONVERT_GetOle10PresData [Internal]
6846 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
6849 * pStorage [I] Src IStroage
6850 * pOleStream [I] Dest OleStream Mem Struct
6856 * This function is used by OleConvertIStorageToOLESTREAM only.
6858 * Memory allocated for pData must be freed by the caller
6862 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6867 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6869 /* Initialize Default data for OLESTREAM */
6870 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6871 pOleStreamData[0].dwTypeID = 2;
6872 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6873 pOleStreamData[1].dwTypeID = 0;
6874 pOleStreamData[0].dwMetaFileWidth = 0;
6875 pOleStreamData[0].dwMetaFileHeight = 0;
6876 pOleStreamData[0].pData = NULL;
6877 pOleStreamData[1].pData = NULL;
6879 /* Open Ole10Native Stream */
6880 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6881 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6885 /* Read Size and Data */
6886 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
6887 if(pOleStreamData->dwDataLength > 0)
6889 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
6890 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
6892 IStream_Release(pStream);
6898 /*************************************************************************
6899 * OLECONVERT_GetOle20PresData[Internal]
6901 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
6904 * pStorage [I] Src IStroage
6905 * pOleStreamData [I] Dest OleStream Mem Struct
6911 * This function is used by OleConvertIStorageToOLESTREAM only.
6913 * Memory allocated for pData must be freed by the caller
6915 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6919 OLECONVERT_ISTORAGE_OLEPRES olePress;
6920 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6922 /* Initialize Default data for OLESTREAM */
6923 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6924 pOleStreamData[0].dwTypeID = 2;
6925 pOleStreamData[0].dwMetaFileWidth = 0;
6926 pOleStreamData[0].dwMetaFileHeight = 0;
6927 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
6928 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6929 pOleStreamData[1].dwTypeID = 0;
6930 pOleStreamData[1].dwOleTypeNameLength = 0;
6931 pOleStreamData[1].strOleTypeName[0] = 0;
6932 pOleStreamData[1].dwMetaFileWidth = 0;
6933 pOleStreamData[1].dwMetaFileHeight = 0;
6934 pOleStreamData[1].pData = NULL;
6935 pOleStreamData[1].dwDataLength = 0;
6938 /* Open OlePress000 stream */
6939 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6940 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6943 LARGE_INTEGER iSeekPos;
6944 METAFILEPICT16 MetaFilePict;
6945 char strMetafilePictName[] = "METAFILEPICT";
6947 /* Set the TypeID for a Metafile */
6948 pOleStreamData[1].dwTypeID = 5;
6950 /* Set the OleTypeName to Metafile */
6951 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
6952 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
6954 iSeekPos.s.HighPart = 0;
6955 iSeekPos.s.LowPart = sizeof(olePress.byUnknown1);
6957 /* Get Presentation Data */
6958 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6959 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
6960 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
6961 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
6963 /*Set width and Height */
6964 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
6965 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
6966 if(olePress.dwSize > 0)
6969 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
6971 /* Set MetaFilePict struct */
6972 MetaFilePict.mm = 8;
6973 MetaFilePict.xExt = olePress.dwExtentX;
6974 MetaFilePict.yExt = olePress.dwExtentY;
6975 MetaFilePict.hMF = 0;
6977 /* Get Metafile Data */
6978 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
6979 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
6980 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
6982 IStream_Release(pStream);
6986 /*************************************************************************
6987 * OleConvertOLESTREAMToIStorage [OLE32.87]
6992 * DVTARGETDEVICE paramenter is not handled
6993 * Still unsure of some mem fields for OLE 10 Stream
6994 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6995 * and "\001OLE" streams
6998 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
6999 LPOLESTREAM pOleStream,
7001 const DVTARGETDEVICE* ptd)
7005 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7007 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7011 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7014 if(pstg == NULL || pOleStream == NULL)
7016 hRes = E_INVALIDARG;
7021 /* Load the OLESTREAM to Memory */
7022 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7027 /* Load the OLESTREAM to Memory (part 2)*/
7028 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7034 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7036 /* Do we have the IStorage Data in the OLESTREAM */
7037 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7039 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7040 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7044 /* It must be an original OLE 1.0 source */
7045 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7050 /* It must be an original OLE 1.0 source */
7051 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7054 /* Create CompObj Stream if necessary */
7055 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7058 /*Create the Ole Stream if necessary */
7059 OLECONVERT_CreateOleStream(pstg);
7064 /* Free allocated memory */
7065 for(i=0; i < 2; i++)
7067 if(pOleStreamData[i].pData != NULL)
7069 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7071 if(pOleStreamData[i].pstrOleObjFileName != NULL)
7073 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7074 pOleStreamData[i].pstrOleObjFileName = NULL;
7080 /*************************************************************************
7081 * OleConvertIStorageToOLESTREAM [OLE32.85]
7088 * Still unsure of some mem fields for OLE 10 Stream
7089 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7090 * and "\001OLE" streams.
7093 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7095 LPOLESTREAM pOleStream)
7098 HRESULT hRes = S_OK;
7100 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7101 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7104 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7106 if(pstg == NULL || pOleStream == NULL)
7108 hRes = E_INVALIDARG;
7112 /* Get the ProgID */
7113 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7114 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7118 /* Was it originally Ole10 */
7119 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7122 IStream_Release(pStream);
7123 /* Get Presentation Data for Ole10Native */
7124 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7128 /* Get Presentation Data (OLE20) */
7129 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7132 /* Save OLESTREAM */
7133 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7136 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7141 /* Free allocated memory */
7142 for(i=0; i < 2; i++)
7144 if(pOleStreamData[i].pData != NULL)
7146 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7153 /***********************************************************************
7154 * GetConvertStg (OLE32.68)
7156 HRESULT WINAPI GetConvertStg(LPGUID guid) {
7157 FIXME("(%s), unimplemented stub!\n",debugstr_guid(guid));
7161 /******************************************************************************
7162 * StgIsStorageFile [OLE32.146]
7165 StgIsStorageFile(LPCOLESTR fn)
7171 TRACE("(\'%s\')\n", debugstr_w(fn));
7172 hf = CreateFileW(fn, GENERIC_READ,
7173 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7174 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7176 if (hf == INVALID_HANDLE_VALUE)
7177 return STG_E_FILENOTFOUND;
7179 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7181 WARN(" unable to read file\n");
7188 if (bytes_read != 8) {
7189 WARN(" too short\n");
7193 if (!memcmp(magic,STORAGE_magic,8)) {
7198 WARN(" -> Invalid header.\n");