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 #include "winbase.h" /* for lstrlenW() and the likes */
34 #include "wine/unicode.h"
35 #include "wine/debug.h"
37 #include "storage32.h"
38 #include "ole2.h" /* For Write/ReadClassStm */
41 #include "wine/wingdi16.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(storage);
48 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
49 #define OLESTREAM_ID 0x501
50 #define OLESTREAM_MAX_STR_LEN 255
52 static const char rootPropertyName[] = "Root Entry";
55 /* OLESTREAM memory structure to use for Get and Put Routines */
56 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
61 DWORD dwOleTypeNameLength;
62 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
63 CHAR *pstrOleObjFileName;
64 DWORD dwOleObjFileNameLength;
65 DWORD dwMetaFileWidth;
66 DWORD dwMetaFileHeight;
67 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
70 }OLECONVERT_OLESTREAM_DATA;
72 /* CompObj Stream structure */
73 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
78 DWORD dwCLSIDNameLength;
79 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
80 DWORD dwOleTypeNameLength;
81 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
82 DWORD dwProgIDNameLength;
83 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
85 }OLECONVERT_ISTORAGE_COMPOBJ;
88 /* Ole Presention Stream structure */
89 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
97 }OLECONVERT_ISTORAGE_OLEPRES;
101 /***********************************************************************
102 * Forward declaration of internal functions used by the method DestroyElement
104 static HRESULT deleteStorageProperty(
105 StorageImpl *parentStorage,
106 ULONG foundPropertyIndexToDelete,
107 StgProperty propertyToDelete);
109 static HRESULT deleteStreamProperty(
110 StorageImpl *parentStorage,
111 ULONG foundPropertyIndexToDelete,
112 StgProperty propertyToDelete);
114 static HRESULT findPlaceholder(
115 StorageImpl *storage,
116 ULONG propertyIndexToStore,
117 ULONG storagePropertyIndex,
120 static HRESULT adjustPropertyChain(
122 StgProperty propertyToDelete,
123 StgProperty parentProperty,
124 ULONG parentPropertyId,
127 /***********************************************************************
128 * Declaration of the functions used to manipulate StgProperty
131 static ULONG getFreeProperty(
132 StorageImpl *storage);
134 static void updatePropertyChain(
135 StorageImpl *storage,
136 ULONG newPropertyIndex,
137 StgProperty newProperty);
139 static LONG propertyNameCmp(
140 OLECHAR *newProperty,
141 OLECHAR *currentProperty);
144 /***********************************************************************
145 * Declaration of miscellaneous functions...
147 static HRESULT validateSTGM(DWORD stgmValue);
149 static DWORD GetShareModeFromSTGM(DWORD stgm);
150 static DWORD GetAccessModeFromSTGM(DWORD stgm);
151 static DWORD GetCreationModeFromSTGM(DWORD stgm);
154 * Virtual function table for the IStorage32Impl class.
156 static ICOM_VTABLE(IStorage) Storage32Impl_Vtbl =
158 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
159 StorageBaseImpl_QueryInterface,
160 StorageBaseImpl_AddRef,
161 StorageBaseImpl_Release,
162 StorageBaseImpl_CreateStream,
163 StorageBaseImpl_OpenStream,
164 StorageImpl_CreateStorage,
165 StorageBaseImpl_OpenStorage,
167 StorageImpl_MoveElementTo,
170 StorageBaseImpl_EnumElements,
171 StorageImpl_DestroyElement,
172 StorageBaseImpl_RenameElement,
173 StorageImpl_SetElementTimes,
174 StorageBaseImpl_SetClass,
175 StorageImpl_SetStateBits,
180 * Virtual function table for the Storage32InternalImpl class.
182 static ICOM_VTABLE(IStorage) Storage32InternalImpl_Vtbl =
184 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
185 StorageBaseImpl_QueryInterface,
186 StorageBaseImpl_AddRef,
187 StorageBaseImpl_Release,
188 StorageBaseImpl_CreateStream,
189 StorageBaseImpl_OpenStream,
190 StorageImpl_CreateStorage,
191 StorageBaseImpl_OpenStorage,
193 StorageImpl_MoveElementTo,
194 StorageInternalImpl_Commit,
195 StorageInternalImpl_Revert,
196 StorageBaseImpl_EnumElements,
197 StorageImpl_DestroyElement,
198 StorageBaseImpl_RenameElement,
199 StorageImpl_SetElementTimes,
200 StorageBaseImpl_SetClass,
201 StorageImpl_SetStateBits,
206 * Virtual function table for the IEnumSTATSTGImpl class.
208 static ICOM_VTABLE(IEnumSTATSTG) IEnumSTATSTGImpl_Vtbl =
210 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
211 IEnumSTATSTGImpl_QueryInterface,
212 IEnumSTATSTGImpl_AddRef,
213 IEnumSTATSTGImpl_Release,
214 IEnumSTATSTGImpl_Next,
215 IEnumSTATSTGImpl_Skip,
216 IEnumSTATSTGImpl_Reset,
217 IEnumSTATSTGImpl_Clone
224 /************************************************************************
225 ** Storage32BaseImpl implementatiion
228 /************************************************************************
229 * Storage32BaseImpl_QueryInterface (IUnknown)
231 * This method implements the common QueryInterface for all IStorage32
232 * implementations contained in this file.
234 * See Windows documentation for more details on IUnknown methods.
236 HRESULT WINAPI StorageBaseImpl_QueryInterface(
241 ICOM_THIS(StorageBaseImpl,iface);
243 * Perform a sanity check on the parameters.
245 if ( (This==0) || (ppvObject==0) )
249 * Initialize the return parameter.
254 * Compare the riid with the interface IDs implemented by this object.
256 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
258 *ppvObject = (IStorage*)This;
260 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
262 *ppvObject = (IStorage*)This;
266 * Check that we obtained an interface.
269 return E_NOINTERFACE;
272 * Query Interface always increases the reference count by one when it is
275 StorageBaseImpl_AddRef(iface);
280 /************************************************************************
281 * Storage32BaseImpl_AddRef (IUnknown)
283 * This method implements the common AddRef for all IStorage32
284 * implementations contained in this file.
286 * See Windows documentation for more details on IUnknown methods.
288 ULONG WINAPI StorageBaseImpl_AddRef(
291 ICOM_THIS(StorageBaseImpl,iface);
297 /************************************************************************
298 * Storage32BaseImpl_Release (IUnknown)
300 * This method implements the common Release for all IStorage32
301 * implementations contained in this file.
303 * See Windows documentation for more details on IUnknown methods.
305 ULONG WINAPI StorageBaseImpl_Release(
308 ICOM_THIS(StorageBaseImpl,iface);
310 * Decrease the reference count on this object.
315 * If the reference count goes down to 0, perform suicide.
320 * Since we are using a system of base-classes, we want to call the
321 * destructor of the appropriate derived class. To do this, we are
322 * using virtual functions to implement the destructor.
324 This->v_destructor(This);
332 /************************************************************************
333 * Storage32BaseImpl_OpenStream (IStorage)
335 * This method will open the specified stream object from the current storage.
337 * See Windows documentation for more details on IStorage methods.
339 HRESULT WINAPI StorageBaseImpl_OpenStream(
341 const OLECHAR* pwcsName, /* [string][in] */
342 void* reserved1, /* [unique][in] */
343 DWORD grfMode, /* [in] */
344 DWORD reserved2, /* [in] */
345 IStream** ppstm) /* [out] */
347 ICOM_THIS(StorageBaseImpl,iface);
348 IEnumSTATSTGImpl* propertyEnumeration;
349 StgStreamImpl* newStream;
350 StgProperty currentProperty;
351 ULONG foundPropertyIndex;
352 HRESULT res = STG_E_UNKNOWN;
354 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
355 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
358 * Perform a sanity check on the parameters.
360 if ( (pwcsName==NULL) || (ppstm==0) )
367 * Initialize the out parameter
372 * Validate the STGM flags
374 if ( FAILED( validateSTGM(grfMode) ))
376 res = STG_E_INVALIDFLAG;
383 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
384 (grfMode & STGM_DELETEONRELEASE) ||
385 (grfMode & STGM_TRANSACTED) )
387 res = STG_E_INVALIDFUNCTION;
392 * Create a property enumeration to search the properties
394 propertyEnumeration = IEnumSTATSTGImpl_Construct(
395 This->ancestorStorage,
396 This->rootPropertySetIndex);
399 * Search the enumeration for the property with the given name
401 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
407 * Delete the property enumeration since we don't need it anymore
409 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
412 * If it was found, construct the stream object and return a pointer to it.
414 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
415 (currentProperty.propertyType==PROPTYPE_STREAM) )
417 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
421 newStream->grfMode = grfMode;
422 *ppstm = (IStream*)newStream;
425 * Since we are returning a pointer to the interface, we have to
426 * nail down the reference.
428 StgStreamImpl_AddRef(*ppstm);
438 res = STG_E_FILENOTFOUND;
442 TRACE("<-- IStream %p\n", *ppstm);
443 TRACE("<-- %08lx\n", res);
447 /************************************************************************
448 * Storage32BaseImpl_OpenStorage (IStorage)
450 * This method will open a new storage object from the current storage.
452 * See Windows documentation for more details on IStorage methods.
454 HRESULT WINAPI StorageBaseImpl_OpenStorage(
456 const OLECHAR* pwcsName, /* [string][unique][in] */
457 IStorage* pstgPriority, /* [unique][in] */
458 DWORD grfMode, /* [in] */
459 SNB snbExclude, /* [unique][in] */
460 DWORD reserved, /* [in] */
461 IStorage** ppstg) /* [out] */
463 ICOM_THIS(StorageBaseImpl,iface);
464 StorageInternalImpl* newStorage;
465 IEnumSTATSTGImpl* propertyEnumeration;
466 StgProperty currentProperty;
467 ULONG foundPropertyIndex;
468 HRESULT res = STG_E_UNKNOWN;
470 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
471 iface, debugstr_w(pwcsName), pstgPriority,
472 grfMode, snbExclude, reserved, ppstg);
475 * Perform a sanity check on the parameters.
477 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
484 if (snbExclude != NULL)
486 res = STG_E_INVALIDPARAMETER;
491 * Validate the STGM flags
493 if ( FAILED( validateSTGM(grfMode) ))
495 res = STG_E_INVALIDFLAG;
502 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
503 (grfMode & STGM_DELETEONRELEASE) ||
504 (grfMode & STGM_PRIORITY) )
506 res = STG_E_INVALIDFUNCTION;
511 * Initialize the out parameter
516 * Create a property enumeration to search the properties
518 propertyEnumeration = IEnumSTATSTGImpl_Construct(
519 This->ancestorStorage,
520 This->rootPropertySetIndex);
523 * Search the enumeration for the property with the given name
525 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
531 * Delete the property enumeration since we don't need it anymore
533 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
536 * If it was found, construct the stream object and return a pointer to it.
538 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
539 (currentProperty.propertyType==PROPTYPE_STORAGE) )
542 * Construct a new Storage object
544 newStorage = StorageInternalImpl_Construct(
545 This->ancestorStorage,
550 *ppstg = (IStorage*)newStorage;
553 * Since we are returning a pointer to the interface,
554 * we have to nail down the reference.
556 StorageBaseImpl_AddRef(*ppstg);
562 res = STG_E_INSUFFICIENTMEMORY;
566 res = STG_E_FILENOTFOUND;
569 TRACE("<-- %08lx\n", res);
573 /************************************************************************
574 * Storage32BaseImpl_EnumElements (IStorage)
576 * This method will create an enumerator object that can be used to
577 * retrieve informatino about all the properties in the storage object.
579 * See Windows documentation for more details on IStorage methods.
581 HRESULT WINAPI StorageBaseImpl_EnumElements(
583 DWORD reserved1, /* [in] */
584 void* reserved2, /* [size_is][unique][in] */
585 DWORD reserved3, /* [in] */
586 IEnumSTATSTG** ppenum) /* [out] */
588 ICOM_THIS(StorageBaseImpl,iface);
589 IEnumSTATSTGImpl* newEnum;
591 TRACE("(%p, %ld, %p, %ld, %p)\n",
592 iface, reserved1, reserved2, reserved3, ppenum);
595 * Perform a sanity check on the parameters.
597 if ( (This==0) || (ppenum==0))
601 * Construct the enumerator.
603 newEnum = IEnumSTATSTGImpl_Construct(
604 This->ancestorStorage,
605 This->rootPropertySetIndex);
609 *ppenum = (IEnumSTATSTG*)newEnum;
612 * Don't forget to nail down a reference to the new object before
615 IEnumSTATSTGImpl_AddRef(*ppenum);
620 return E_OUTOFMEMORY;
623 /************************************************************************
624 * Storage32BaseImpl_Stat (IStorage)
626 * This method will retrieve information about this storage object.
628 * See Windows documentation for more details on IStorage methods.
630 HRESULT WINAPI StorageBaseImpl_Stat(
632 STATSTG* pstatstg, /* [out] */
633 DWORD grfStatFlag) /* [in] */
635 ICOM_THIS(StorageBaseImpl,iface);
636 StgProperty curProperty;
638 HRESULT res = STG_E_UNKNOWN;
640 TRACE("(%p, %p, %lx)\n",
641 iface, pstatstg, grfStatFlag);
644 * Perform a sanity check on the parameters.
646 if ( (This==0) || (pstatstg==0))
653 * Read the information from the property.
655 readSuccessful = StorageImpl_ReadProperty(
656 This->ancestorStorage,
657 This->rootPropertySetIndex,
662 StorageUtl_CopyPropertyToSTATSTG(
676 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);
678 TRACE("<-- %08lx\n", res);
682 /************************************************************************
683 * Storage32BaseImpl_RenameElement (IStorage)
685 * This method will rename the specified element.
687 * See Windows documentation for more details on IStorage methods.
689 * Implementation notes: The method used to rename consists of creating a clone
690 * of the deleted StgProperty object setting it with the new name and to
691 * perform a DestroyElement of the old StgProperty.
693 HRESULT WINAPI StorageBaseImpl_RenameElement(
695 const OLECHAR* pwcsOldName, /* [in] */
696 const OLECHAR* pwcsNewName) /* [in] */
698 ICOM_THIS(StorageBaseImpl,iface);
699 IEnumSTATSTGImpl* propertyEnumeration;
700 StgProperty currentProperty;
701 ULONG foundPropertyIndex;
703 TRACE("(%p, %s, %s)\n",
704 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
707 * Create a property enumeration to search the properties
709 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
710 This->rootPropertySetIndex);
713 * Search the enumeration for the new property name
715 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
719 if (foundPropertyIndex != PROPERTY_NULL)
722 * There is already a property with the new name
724 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
725 return STG_E_FILEALREADYEXISTS;
728 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
731 * Search the enumeration for the old property name
733 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
738 * Delete the property enumeration since we don't need it anymore
740 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
742 if (foundPropertyIndex != PROPERTY_NULL)
744 StgProperty renamedProperty;
745 ULONG renamedPropertyIndex;
748 * Setup a new property for the renamed property
750 renamedProperty.sizeOfNameString =
751 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
753 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
754 return STG_E_INVALIDNAME;
756 strcpyW(renamedProperty.name, pwcsNewName);
758 renamedProperty.propertyType = currentProperty.propertyType;
759 renamedProperty.startingBlock = currentProperty.startingBlock;
760 renamedProperty.size.s.LowPart = currentProperty.size.s.LowPart;
761 renamedProperty.size.s.HighPart = currentProperty.size.s.HighPart;
763 renamedProperty.previousProperty = PROPERTY_NULL;
764 renamedProperty.nextProperty = PROPERTY_NULL;
767 * Bring the dirProperty link in case it is a storage and in which
768 * case the renamed storage elements don't require to be reorganized.
770 renamedProperty.dirProperty = currentProperty.dirProperty;
772 /* call CoFileTime to get the current time
773 renamedProperty.timeStampS1
774 renamedProperty.timeStampD1
775 renamedProperty.timeStampS2
776 renamedProperty.timeStampD2
777 renamedProperty.propertyUniqueID
781 * Obtain a free property in the property chain
783 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
786 * Save the new property into the new property spot
788 StorageImpl_WriteProperty(
789 This->ancestorStorage,
790 renamedPropertyIndex,
794 * Find a spot in the property chain for our newly created property.
798 renamedPropertyIndex,
802 * At this point the renamed property has been inserted in the tree,
803 * now, before to Destroy the old property we must zeroed it's dirProperty
804 * otherwise the DestroyProperty below will zap it all and we do not want
806 * Also, we fake that the old property is a storage so the DestroyProperty
807 * will not do a SetSize(0) on the stream data.
809 * This means that we need to tweek the StgProperty if it is a stream or a
812 StorageImpl_ReadProperty(This->ancestorStorage,
816 currentProperty.dirProperty = PROPERTY_NULL;
817 currentProperty.propertyType = PROPTYPE_STORAGE;
818 StorageImpl_WriteProperty(
819 This->ancestorStorage,
824 * Invoke Destroy to get rid of the ole property and automatically redo
825 * the linking of it's previous and next members...
827 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
833 * There is no property with the old name
835 return STG_E_FILENOTFOUND;
841 /************************************************************************
842 * Storage32BaseImpl_CreateStream (IStorage)
844 * This method will create a stream object within this storage
846 * See Windows documentation for more details on IStorage methods.
848 HRESULT WINAPI StorageBaseImpl_CreateStream(
850 const OLECHAR* pwcsName, /* [string][in] */
851 DWORD grfMode, /* [in] */
852 DWORD reserved1, /* [in] */
853 DWORD reserved2, /* [in] */
854 IStream** ppstm) /* [out] */
856 ICOM_THIS(StorageBaseImpl,iface);
857 IEnumSTATSTGImpl* propertyEnumeration;
858 StgStreamImpl* newStream;
859 StgProperty currentProperty, newStreamProperty;
860 ULONG foundPropertyIndex, newPropertyIndex;
862 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
863 iface, debugstr_w(pwcsName), grfMode,
864 reserved1, reserved2, ppstm);
867 * Validate parameters
870 return STG_E_INVALIDPOINTER;
873 return STG_E_INVALIDNAME;
876 * Validate the STGM flags
878 if ( FAILED( validateSTGM(grfMode) ))
879 return STG_E_INVALIDFLAG;
884 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
885 (grfMode & STGM_DELETEONRELEASE) ||
886 (grfMode & STGM_TRANSACTED) )
887 return STG_E_INVALIDFUNCTION;
890 * Initialize the out parameter
895 * Create a property enumeration to search the properties
897 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
898 This->rootPropertySetIndex);
900 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
904 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
906 if (foundPropertyIndex != PROPERTY_NULL)
909 * An element with this name already exists
911 if (grfMode & STGM_CREATE)
913 IStorage_DestroyElement(iface, pwcsName);
916 return STG_E_FILEALREADYEXISTS;
920 * memset the empty property
922 memset(&newStreamProperty, 0, sizeof(StgProperty));
924 newStreamProperty.sizeOfNameString =
925 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
927 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
928 return STG_E_INVALIDNAME;
930 strcpyW(newStreamProperty.name, pwcsName);
932 newStreamProperty.propertyType = PROPTYPE_STREAM;
933 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
934 newStreamProperty.size.s.LowPart = 0;
935 newStreamProperty.size.s.HighPart = 0;
937 newStreamProperty.previousProperty = PROPERTY_NULL;
938 newStreamProperty.nextProperty = PROPERTY_NULL;
939 newStreamProperty.dirProperty = PROPERTY_NULL;
941 /* call CoFileTime to get the current time
942 newStreamProperty.timeStampS1
943 newStreamProperty.timeStampD1
944 newStreamProperty.timeStampS2
945 newStreamProperty.timeStampD2
948 /* newStreamProperty.propertyUniqueID */
951 * Get a free property or create a new one
953 newPropertyIndex = getFreeProperty(This->ancestorStorage);
956 * Save the new property into the new property spot
958 StorageImpl_WriteProperty(
959 This->ancestorStorage,
964 * Find a spot in the property chain for our newly created property.
972 * Open the stream to return it.
974 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
978 *ppstm = (IStream*)newStream;
981 * Since we are returning a pointer to the interface, we have to nail down
984 StgStreamImpl_AddRef(*ppstm);
988 return STG_E_INSUFFICIENTMEMORY;
994 /************************************************************************
995 * Storage32BaseImpl_SetClass (IStorage)
997 * This method will write the specified CLSID in the property of this
1000 * See Windows documentation for more details on IStorage methods.
1002 HRESULT WINAPI StorageBaseImpl_SetClass(
1004 REFCLSID clsid) /* [in] */
1006 ICOM_THIS(StorageBaseImpl,iface);
1007 HRESULT hRes = E_FAIL;
1008 StgProperty curProperty;
1011 TRACE("(%p, %p)\n", iface, clsid);
1013 success = StorageImpl_ReadProperty(This->ancestorStorage,
1014 This->rootPropertySetIndex,
1018 curProperty.propertyUniqueID = *clsid;
1020 success = StorageImpl_WriteProperty(This->ancestorStorage,
1021 This->rootPropertySetIndex,
1030 /************************************************************************
1031 ** Storage32Impl implementation
1034 /************************************************************************
1035 * Storage32Impl_CreateStorage (IStorage)
1037 * This method will create the storage object within the provided storage.
1039 * See Windows documentation for more details on IStorage methods.
1041 HRESULT WINAPI StorageImpl_CreateStorage(
1043 const OLECHAR *pwcsName, /* [string][in] */
1044 DWORD grfMode, /* [in] */
1045 DWORD reserved1, /* [in] */
1046 DWORD reserved2, /* [in] */
1047 IStorage **ppstg) /* [out] */
1049 StorageImpl* const This=(StorageImpl*)iface;
1051 IEnumSTATSTGImpl *propertyEnumeration;
1052 StgProperty currentProperty;
1053 StgProperty newProperty;
1054 ULONG foundPropertyIndex;
1055 ULONG newPropertyIndex;
1058 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1059 iface, debugstr_w(pwcsName), grfMode,
1060 reserved1, reserved2, ppstg);
1063 * Validate parameters
1066 return STG_E_INVALIDPOINTER;
1069 return STG_E_INVALIDNAME;
1072 * Validate the STGM flags
1074 if ( FAILED( validateSTGM(grfMode) ) ||
1075 (grfMode & STGM_DELETEONRELEASE) )
1076 return STG_E_INVALIDFLAG;
1079 * Initialize the out parameter
1084 * Create a property enumeration and search the properties
1086 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
1087 This->rootPropertySetIndex);
1089 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1092 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1094 if (foundPropertyIndex != PROPERTY_NULL)
1097 * An element with this name already exists
1099 if (grfMode & STGM_CREATE)
1100 IStorage_DestroyElement(iface, pwcsName);
1102 return STG_E_FILEALREADYEXISTS;
1106 * memset the empty property
1108 memset(&newProperty, 0, sizeof(StgProperty));
1110 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1112 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1113 return STG_E_INVALIDNAME;
1115 strcpyW(newProperty.name, pwcsName);
1117 newProperty.propertyType = PROPTYPE_STORAGE;
1118 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1119 newProperty.size.s.LowPart = 0;
1120 newProperty.size.s.HighPart = 0;
1122 newProperty.previousProperty = PROPERTY_NULL;
1123 newProperty.nextProperty = PROPERTY_NULL;
1124 newProperty.dirProperty = PROPERTY_NULL;
1126 /* call CoFileTime to get the current time
1127 newProperty.timeStampS1
1128 newProperty.timeStampD1
1129 newProperty.timeStampS2
1130 newProperty.timeStampD2
1133 /* newStorageProperty.propertyUniqueID */
1136 * Obtain a free property in the property chain
1138 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1141 * Save the new property into the new property spot
1143 StorageImpl_WriteProperty(
1144 This->ancestorStorage,
1149 * Find a spot in the property chain for our newly created property.
1151 updatePropertyChain(
1157 * Open it to get a pointer to return.
1159 hr = IStorage_OpenStorage(
1168 if( (hr != S_OK) || (*ppstg == NULL))
1178 /***************************************************************************
1182 * Get a free property or create a new one.
1184 static ULONG getFreeProperty(
1185 StorageImpl *storage)
1187 ULONG currentPropertyIndex = 0;
1188 ULONG newPropertyIndex = PROPERTY_NULL;
1189 BOOL readSuccessful = TRUE;
1190 StgProperty currentProperty;
1195 * Start by reading the root property
1197 readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1198 currentPropertyIndex,
1202 if (currentProperty.sizeOfNameString == 0)
1205 * The property existis and is available, we found it.
1207 newPropertyIndex = currentPropertyIndex;
1213 * We exhausted the property list, we will create more space below
1215 newPropertyIndex = currentPropertyIndex;
1217 currentPropertyIndex++;
1219 } while (newPropertyIndex == PROPERTY_NULL);
1222 * grow the property chain
1224 if (! readSuccessful)
1226 StgProperty emptyProperty;
1227 ULARGE_INTEGER newSize;
1228 ULONG propertyIndex;
1229 ULONG lastProperty = 0;
1230 ULONG blockCount = 0;
1233 * obtain the new count of property blocks
1235 blockCount = BlockChainStream_GetCount(
1236 storage->ancestorStorage->rootBlockChain)+1;
1239 * initialize the size used by the property stream
1241 newSize.s.HighPart = 0;
1242 newSize.s.LowPart = storage->bigBlockSize * blockCount;
1245 * add a property block to the property chain
1247 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1250 * memset the empty property in order to initialize the unused newly
1253 memset(&emptyProperty, 0, sizeof(StgProperty));
1258 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1261 propertyIndex = newPropertyIndex;
1262 propertyIndex < lastProperty;
1265 StorageImpl_WriteProperty(
1266 storage->ancestorStorage,
1272 return newPropertyIndex;
1275 /****************************************************************************
1279 * Case insensitive comparaison of StgProperty.name by first considering
1282 * Returns <0 when newPrpoerty < currentProperty
1283 * >0 when newPrpoerty > currentProperty
1284 * 0 when newPrpoerty == currentProperty
1286 static LONG propertyNameCmp(
1287 OLECHAR *newProperty,
1288 OLECHAR *currentProperty)
1290 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1295 * We compare the string themselves only when they are of the same lenght
1297 diff = lstrcmpiW( newProperty, currentProperty);
1303 /****************************************************************************
1307 * Properly link this new element in the property chain.
1309 static void updatePropertyChain(
1310 StorageImpl *storage,
1311 ULONG newPropertyIndex,
1312 StgProperty newProperty)
1314 StgProperty currentProperty;
1317 * Read the root property
1319 StorageImpl_ReadProperty(storage->ancestorStorage,
1320 storage->rootPropertySetIndex,
1323 if (currentProperty.dirProperty != PROPERTY_NULL)
1326 * The root storage contains some element, therefore, start the research
1327 * for the appropriate location.
1330 ULONG current, next, previous, currentPropertyId;
1333 * Keep the StgProperty sequence number of the storage first property
1335 currentPropertyId = currentProperty.dirProperty;
1340 StorageImpl_ReadProperty(storage->ancestorStorage,
1341 currentProperty.dirProperty,
1344 previous = currentProperty.previousProperty;
1345 next = currentProperty.nextProperty;
1346 current = currentPropertyId;
1350 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1354 if (previous != PROPERTY_NULL)
1356 StorageImpl_ReadProperty(storage->ancestorStorage,
1363 currentProperty.previousProperty = newPropertyIndex;
1364 StorageImpl_WriteProperty(storage->ancestorStorage,
1372 if (next != PROPERTY_NULL)
1374 StorageImpl_ReadProperty(storage->ancestorStorage,
1381 currentProperty.nextProperty = newPropertyIndex;
1382 StorageImpl_WriteProperty(storage->ancestorStorage,
1391 * Trying to insert an item with the same name in the
1392 * subtree structure.
1397 previous = currentProperty.previousProperty;
1398 next = currentProperty.nextProperty;
1404 * The root storage is empty, link the new property to it's dir property
1406 currentProperty.dirProperty = newPropertyIndex;
1407 StorageImpl_WriteProperty(storage->ancestorStorage,
1408 storage->rootPropertySetIndex,
1414 /*************************************************************************
1417 HRESULT WINAPI StorageImpl_CopyTo(
1419 DWORD ciidExclude, /* [in] */
1420 const IID* rgiidExclude, /* [size_is][unique][in] */
1421 SNB snbExclude, /* [unique][in] */
1422 IStorage* pstgDest) /* [unique][in] */
1424 IEnumSTATSTG *elements = 0;
1425 STATSTG curElement, strStat;
1427 IStorage *pstgTmp, *pstgChild;
1428 IStream *pstrTmp, *pstrChild;
1430 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1431 FIXME("Exclude option not implemented\n");
1433 TRACE("(%p, %ld, %p, %p, %p)\n",
1434 iface, ciidExclude, rgiidExclude,
1435 snbExclude, pstgDest);
1438 * Perform a sanity check
1440 if ( pstgDest == 0 )
1441 return STG_E_INVALIDPOINTER;
1444 * Enumerate the elements
1446 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1454 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1455 IStorage_SetClass( pstgDest, &curElement.clsid );
1460 * Obtain the next element
1462 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1464 if ( hr == S_FALSE )
1466 hr = S_OK; /* done, every element has been copied */
1470 if (curElement.type == STGTY_STORAGE)
1473 * open child source storage
1475 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1476 STGM_READ|STGM_SHARE_EXCLUSIVE,
1477 NULL, 0, &pstgChild );
1483 * Check if destination storage is not a child of the source
1484 * storage, which will cause an infinite loop
1486 if (pstgChild == pstgDest)
1488 IEnumSTATSTG_Release(elements);
1490 return STG_E_ACCESSDENIED;
1494 * create a new storage in destination storage
1496 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1497 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1501 * if it already exist, don't create a new one use this one
1503 if (hr == STG_E_FILEALREADYEXISTS)
1505 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1506 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1507 NULL, 0, &pstgTmp );
1515 * do the copy recursively
1517 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1518 snbExclude, pstgTmp );
1520 IStorage_Release( pstgTmp );
1521 IStorage_Release( pstgChild );
1523 else if (curElement.type == STGTY_STREAM)
1526 * create a new stream in destination storage. If the stream already
1527 * exist, it will be deleted and a new one will be created.
1529 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1530 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1537 * open child stream storage
1539 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1540 STGM_READ|STGM_SHARE_EXCLUSIVE,
1547 * Get the size of the source stream
1549 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1552 * Set the size of the destination stream.
1554 IStream_SetSize(pstrTmp, strStat.cbSize);
1559 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1562 IStream_Release( pstrTmp );
1563 IStream_Release( pstrChild );
1567 WARN("unknown element type: %ld\n", curElement.type);
1570 } while (hr == S_OK);
1575 IEnumSTATSTG_Release(elements);
1580 /*************************************************************************
1581 * MoveElementTo (IStorage)
1583 HRESULT WINAPI StorageImpl_MoveElementTo(
1585 const OLECHAR *pwcsName, /* [string][in] */
1586 IStorage *pstgDest, /* [unique][in] */
1587 const OLECHAR *pwcsNewName,/* [string][in] */
1588 DWORD grfFlags) /* [in] */
1590 FIXME("not implemented!\n");
1594 /*************************************************************************
1597 HRESULT WINAPI StorageImpl_Commit(
1599 DWORD grfCommitFlags)/* [in] */
1601 FIXME("(%ld): stub!\n", grfCommitFlags);
1605 /*************************************************************************
1608 HRESULT WINAPI StorageImpl_Revert(
1611 FIXME("not implemented!\n");
1615 /*************************************************************************
1616 * DestroyElement (IStorage)
1618 * Stategy: This implementation is build this way for simplicity not for speed.
1619 * I always delete the top most element of the enumeration and adjust
1620 * the deleted element pointer all the time. This takes longer to
1621 * do but allow to reinvoke DestroyElement whenever we encounter a
1622 * storage object. The optimisation reside in the usage of another
1623 * enumeration stategy that would give all the leaves of a storage
1624 * first. (postfix order)
1626 HRESULT WINAPI StorageImpl_DestroyElement(
1628 const OLECHAR *pwcsName)/* [string][in] */
1630 StorageImpl* const This=(StorageImpl*)iface;
1632 IEnumSTATSTGImpl* propertyEnumeration;
1635 StgProperty propertyToDelete;
1636 StgProperty parentProperty;
1637 ULONG foundPropertyIndexToDelete;
1638 ULONG typeOfRelation;
1639 ULONG parentPropertyId;
1642 iface, debugstr_w(pwcsName));
1645 * Perform a sanity check on the parameters.
1648 return STG_E_INVALIDPOINTER;
1651 * Create a property enumeration to search the property with the given name
1653 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1654 This->ancestorStorage,
1655 This->rootPropertySetIndex);
1657 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1658 propertyEnumeration,
1662 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1664 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1666 return STG_E_FILENOTFOUND;
1670 * Find the parent property of the property to delete (the one that
1671 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1672 * the parent is This. Otherwise, the parent is one of it's sibling...
1676 * First, read This's StgProperty..
1678 res = StorageImpl_ReadProperty(
1679 This->ancestorStorage,
1680 This->rootPropertySetIndex,
1686 * Second, check to see if by any chance the actual storage (This) is not
1687 * the parent of the property to delete... We never know...
1689 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1692 * Set data as it would have been done in the else part...
1694 typeOfRelation = PROPERTY_RELATION_DIR;
1695 parentPropertyId = This->rootPropertySetIndex;
1700 * Create a property enumeration to search the parent properties, and
1701 * delete it once done.
1703 IEnumSTATSTGImpl* propertyEnumeration2;
1705 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1706 This->ancestorStorage,
1707 This->rootPropertySetIndex);
1709 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1710 propertyEnumeration2,
1711 foundPropertyIndexToDelete,
1715 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1718 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1720 hr = deleteStorageProperty(
1722 foundPropertyIndexToDelete,
1725 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1727 hr = deleteStreamProperty(
1729 foundPropertyIndexToDelete,
1737 * Adjust the property chain
1739 hr = adjustPropertyChain(
1750 /************************************************************************
1751 * StorageImpl_Stat (IStorage)
1753 * This method will retrieve information about this storage object.
1755 * See Windows documentation for more details on IStorage methods.
1757 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1758 STATSTG* pstatstg, /* [out] */
1759 DWORD grfStatFlag) /* [in] */
1761 StorageImpl* const This = (StorageImpl*)iface;
1762 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1764 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1766 CoTaskMemFree(pstatstg->pwcsName);
1767 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1768 strcpyW(pstatstg->pwcsName, This->pwcsName);
1776 /*********************************************************************
1780 * Perform the deletion of a complete storage node
1783 static HRESULT deleteStorageProperty(
1784 StorageImpl *parentStorage,
1785 ULONG indexOfPropertyToDelete,
1786 StgProperty propertyToDelete)
1788 IEnumSTATSTG *elements = 0;
1789 IStorage *childStorage = 0;
1790 STATSTG currentElement;
1792 HRESULT destroyHr = S_OK;
1795 * Open the storage and enumerate it
1797 hr = StorageBaseImpl_OpenStorage(
1798 (IStorage*)parentStorage,
1799 propertyToDelete.name,
1801 STGM_SHARE_EXCLUSIVE,
1812 * Enumerate the elements
1814 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1819 * Obtain the next element
1821 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1824 destroyHr = StorageImpl_DestroyElement(
1825 (IStorage*)childStorage,
1826 (OLECHAR*)currentElement.pwcsName);
1828 CoTaskMemFree(currentElement.pwcsName);
1832 * We need to Reset the enumeration every time because we delete elements
1833 * and the enumeration could be invalid
1835 IEnumSTATSTG_Reset(elements);
1837 } while ((hr == S_OK) && (destroyHr == S_OK));
1840 * Invalidate the property by zeroing it's name member.
1842 propertyToDelete.sizeOfNameString = 0;
1844 StorageImpl_WriteProperty(parentStorage->ancestorStorage,
1845 indexOfPropertyToDelete,
1848 IStorage_Release(childStorage);
1849 IEnumSTATSTG_Release(elements);
1854 /*********************************************************************
1858 * Perform the deletion of a stream node
1861 static HRESULT deleteStreamProperty(
1862 StorageImpl *parentStorage,
1863 ULONG indexOfPropertyToDelete,
1864 StgProperty propertyToDelete)
1868 ULARGE_INTEGER size;
1870 size.s.HighPart = 0;
1873 hr = StorageBaseImpl_OpenStream(
1874 (IStorage*)parentStorage,
1875 (OLECHAR*)propertyToDelete.name,
1877 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1889 hr = IStream_SetSize(pis, size);
1897 * Release the stream object.
1899 IStream_Release(pis);
1902 * Invalidate the property by zeroing it's name member.
1904 propertyToDelete.sizeOfNameString = 0;
1907 * Here we should re-read the property so we get the updated pointer
1908 * but since we are here to zap it, I don't do it...
1910 StorageImpl_WriteProperty(
1911 parentStorage->ancestorStorage,
1912 indexOfPropertyToDelete,
1918 /*********************************************************************
1922 * Finds a placeholder for the StgProperty within the Storage
1925 static HRESULT findPlaceholder(
1926 StorageImpl *storage,
1927 ULONG propertyIndexToStore,
1928 ULONG storePropertyIndex,
1931 StgProperty storeProperty;
1936 * Read the storage property
1938 res = StorageImpl_ReadProperty(
1939 storage->ancestorStorage,
1948 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1950 if (storeProperty.previousProperty != PROPERTY_NULL)
1952 return findPlaceholder(
1954 propertyIndexToStore,
1955 storeProperty.previousProperty,
1960 storeProperty.previousProperty = propertyIndexToStore;
1963 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1965 if (storeProperty.nextProperty != PROPERTY_NULL)
1967 return findPlaceholder(
1969 propertyIndexToStore,
1970 storeProperty.nextProperty,
1975 storeProperty.nextProperty = propertyIndexToStore;
1978 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1980 if (storeProperty.dirProperty != PROPERTY_NULL)
1982 return findPlaceholder(
1984 propertyIndexToStore,
1985 storeProperty.dirProperty,
1990 storeProperty.dirProperty = propertyIndexToStore;
1994 hr = StorageImpl_WriteProperty(
1995 storage->ancestorStorage,
2007 /*************************************************************************
2011 * This method takes the previous and the next property link of a property
2012 * to be deleted and find them a place in the Storage.
2014 static HRESULT adjustPropertyChain(
2016 StgProperty propertyToDelete,
2017 StgProperty parentProperty,
2018 ULONG parentPropertyId,
2021 ULONG newLinkProperty = PROPERTY_NULL;
2022 BOOL needToFindAPlaceholder = FALSE;
2023 ULONG storeNode = PROPERTY_NULL;
2024 ULONG toStoreNode = PROPERTY_NULL;
2025 INT relationType = 0;
2029 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2031 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2034 * Set the parent previous to the property to delete previous
2036 newLinkProperty = propertyToDelete.previousProperty;
2038 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2041 * We also need to find a storage for the other link, setup variables
2042 * to do this at the end...
2044 needToFindAPlaceholder = TRUE;
2045 storeNode = propertyToDelete.previousProperty;
2046 toStoreNode = propertyToDelete.nextProperty;
2047 relationType = PROPERTY_RELATION_NEXT;
2050 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2053 * Set the parent previous to the property to delete next
2055 newLinkProperty = propertyToDelete.nextProperty;
2059 * Link it for real...
2061 parentProperty.previousProperty = newLinkProperty;
2064 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2066 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2069 * Set the parent next to the property to delete next previous
2071 newLinkProperty = propertyToDelete.previousProperty;
2073 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2076 * We also need to find a storage for the other link, setup variables
2077 * to do this at the end...
2079 needToFindAPlaceholder = TRUE;
2080 storeNode = propertyToDelete.previousProperty;
2081 toStoreNode = propertyToDelete.nextProperty;
2082 relationType = PROPERTY_RELATION_NEXT;
2085 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2088 * Set the parent next to the property to delete next
2090 newLinkProperty = propertyToDelete.nextProperty;
2094 * Link it for real...
2096 parentProperty.nextProperty = newLinkProperty;
2098 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2100 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2103 * Set the parent dir to the property to delete previous
2105 newLinkProperty = propertyToDelete.previousProperty;
2107 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2110 * We also need to find a storage for the other link, setup variables
2111 * to do this at the end...
2113 needToFindAPlaceholder = TRUE;
2114 storeNode = propertyToDelete.previousProperty;
2115 toStoreNode = propertyToDelete.nextProperty;
2116 relationType = PROPERTY_RELATION_NEXT;
2119 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2122 * Set the parent dir to the property to delete next
2124 newLinkProperty = propertyToDelete.nextProperty;
2128 * Link it for real...
2130 parentProperty.dirProperty = newLinkProperty;
2134 * Write back the parent property
2136 res = StorageImpl_WriteProperty(
2137 This->ancestorStorage,
2146 * If a placeholder is required for the other link, then, find one and
2147 * get out of here...
2149 if (needToFindAPlaceholder)
2151 hr = findPlaceholder(
2162 /******************************************************************************
2163 * SetElementTimes (IStorage)
2165 HRESULT WINAPI StorageImpl_SetElementTimes(
2167 const OLECHAR *pwcsName,/* [string][in] */
2168 const FILETIME *pctime, /* [in] */
2169 const FILETIME *patime, /* [in] */
2170 const FILETIME *pmtime) /* [in] */
2172 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2176 /******************************************************************************
2177 * SetStateBits (IStorage)
2179 HRESULT WINAPI StorageImpl_SetStateBits(
2181 DWORD grfStateBits,/* [in] */
2182 DWORD grfMask) /* [in] */
2184 FIXME("not implemented!\n");
2188 HRESULT StorageImpl_Construct(
2198 StgProperty currentProperty;
2199 BOOL readSuccessful;
2200 ULONG currentPropertyIndex;
2202 if ( FAILED( validateSTGM(openFlags) ))
2203 return STG_E_INVALIDFLAG;
2205 memset(This, 0, sizeof(StorageImpl));
2208 * Initialize the virtual function table.
2210 ICOM_VTBL(This) = &Storage32Impl_Vtbl;
2211 This->v_destructor = &StorageImpl_Destroy;
2214 * This is the top-level storage so initialize the ancestor pointer
2217 This->ancestorStorage = This;
2220 * Initialize the physical support of the storage.
2222 This->hFile = hFile;
2225 * Store copy of file path.
2228 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2229 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2230 if (!This->pwcsName)
2231 return STG_E_INSUFFICIENTMEMORY;
2232 strcpyW(This->pwcsName, pwcsName);
2236 * Initialize the big block cache.
2238 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2239 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2240 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2246 if (This->bigBlockFile == 0)
2251 ULARGE_INTEGER size;
2252 BYTE* bigBlockBuffer;
2255 * Initialize all header variables:
2256 * - The big block depot consists of one block and it is at block 0
2257 * - The properties start at block 1
2258 * - There is no small block depot
2260 memset( This->bigBlockDepotStart,
2262 sizeof(This->bigBlockDepotStart));
2264 This->bigBlockDepotCount = 1;
2265 This->bigBlockDepotStart[0] = 0;
2266 This->rootStartBlock = 1;
2267 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2268 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2269 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2270 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2271 This->extBigBlockDepotCount = 0;
2273 StorageImpl_SaveFileHeader(This);
2276 * Add one block for the big block depot and one block for the properties
2278 size.s.HighPart = 0;
2279 size.s.LowPart = This->bigBlockSize * 3;
2280 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2283 * Initialize the big block depot
2285 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2286 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2287 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2288 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2289 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2294 * Load the header for the file.
2296 hr = StorageImpl_LoadFileHeader(This);
2300 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2307 * There is no block depot cached yet.
2309 This->indexBlockDepotCached = 0xFFFFFFFF;
2312 * Start searching for free blocks with block 0.
2314 This->prevFreeBlock = 0;
2317 * Create the block chain abstractions.
2319 if(!(This->rootBlockChain =
2320 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2321 return STG_E_READFAULT;
2323 if(!(This->smallBlockDepotChain =
2324 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2326 return STG_E_READFAULT;
2329 * Write the root property
2333 StgProperty rootProp;
2335 * Initialize the property chain
2337 memset(&rootProp, 0, sizeof(rootProp));
2338 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2339 sizeof(rootProp.name)/sizeof(WCHAR) );
2340 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2341 rootProp.propertyType = PROPTYPE_ROOT;
2342 rootProp.previousProperty = PROPERTY_NULL;
2343 rootProp.nextProperty = PROPERTY_NULL;
2344 rootProp.dirProperty = PROPERTY_NULL;
2345 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2346 rootProp.size.s.HighPart = 0;
2347 rootProp.size.s.LowPart = 0;
2349 StorageImpl_WriteProperty(This, 0, &rootProp);
2353 * Find the ID of the root in the property sets.
2355 currentPropertyIndex = 0;
2359 readSuccessful = StorageImpl_ReadProperty(
2361 currentPropertyIndex,
2366 if ( (currentProperty.sizeOfNameString != 0 ) &&
2367 (currentProperty.propertyType == PROPTYPE_ROOT) )
2369 This->rootPropertySetIndex = currentPropertyIndex;
2373 currentPropertyIndex++;
2375 } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2377 if (!readSuccessful)
2380 return STG_E_READFAULT;
2384 * Create the block chain abstraction for the small block root chain.
2386 if(!(This->smallBlockRootChain =
2387 BlockChainStream_Construct(This, NULL, This->rootPropertySetIndex)))
2388 return STG_E_READFAULT;
2393 void StorageImpl_Destroy(
2396 TRACE("(%p)\n", This);
2399 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2401 BlockChainStream_Destroy(This->smallBlockRootChain);
2402 BlockChainStream_Destroy(This->rootBlockChain);
2403 BlockChainStream_Destroy(This->smallBlockDepotChain);
2405 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2409 /******************************************************************************
2410 * Storage32Impl_GetNextFreeBigBlock
2412 * Returns the index of the next free big block.
2413 * If the big block depot is filled, this method will enlarge it.
2416 ULONG StorageImpl_GetNextFreeBigBlock(
2419 ULONG depotBlockIndexPos;
2421 ULONG depotBlockOffset;
2422 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2423 ULONG nextBlockIndex = BLOCK_SPECIAL;
2425 ULONG freeBlock = BLOCK_UNUSED;
2427 depotIndex = This->prevFreeBlock / blocksPerDepot;
2428 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2431 * Scan the entire big block depot until we find a block marked free
2433 while (nextBlockIndex != BLOCK_UNUSED)
2435 if (depotIndex < COUNT_BBDEPOTINHEADER)
2437 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2440 * Grow the primary depot.
2442 if (depotBlockIndexPos == BLOCK_UNUSED)
2444 depotBlockIndexPos = depotIndex*blocksPerDepot;
2447 * Add a block depot.
2449 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2450 This->bigBlockDepotCount++;
2451 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2454 * Flag it as a block depot.
2456 StorageImpl_SetNextBlockInChain(This,
2460 /* Save new header information.
2462 StorageImpl_SaveFileHeader(This);
2467 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2469 if (depotBlockIndexPos == BLOCK_UNUSED)
2472 * Grow the extended depot.
2474 ULONG extIndex = BLOCK_UNUSED;
2475 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2476 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2478 if (extBlockOffset == 0)
2480 /* We need an extended block.
2482 extIndex = Storage32Impl_AddExtBlockDepot(This);
2483 This->extBigBlockDepotCount++;
2484 depotBlockIndexPos = extIndex + 1;
2487 depotBlockIndexPos = depotIndex * blocksPerDepot;
2490 * Add a block depot and mark it in the extended block.
2492 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2493 This->bigBlockDepotCount++;
2494 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2496 /* Flag the block depot.
2498 StorageImpl_SetNextBlockInChain(This,
2502 /* If necessary, flag the extended depot block.
2504 if (extIndex != BLOCK_UNUSED)
2505 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2507 /* Save header information.
2509 StorageImpl_SaveFileHeader(This);
2513 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2515 if (depotBuffer != 0)
2517 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2518 ( nextBlockIndex != BLOCK_UNUSED))
2520 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2522 if (nextBlockIndex == BLOCK_UNUSED)
2524 freeBlock = (depotIndex * blocksPerDepot) +
2525 (depotBlockOffset/sizeof(ULONG));
2528 depotBlockOffset += sizeof(ULONG);
2531 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2535 depotBlockOffset = 0;
2538 This->prevFreeBlock = freeBlock;
2543 /******************************************************************************
2544 * Storage32Impl_AddBlockDepot
2546 * This will create a depot block, essentially it is a block initialized
2549 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2553 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2556 * Initialize blocks as free
2558 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2560 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2563 /******************************************************************************
2564 * Storage32Impl_GetExtDepotBlock
2566 * Returns the index of the block that corresponds to the specified depot
2567 * index. This method is only for depot indexes equal or greater than
2568 * COUNT_BBDEPOTINHEADER.
2570 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2572 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2573 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2574 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2575 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2576 ULONG blockIndex = BLOCK_UNUSED;
2577 ULONG extBlockIndex = This->extBigBlockDepotStart;
2579 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2581 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2582 return BLOCK_UNUSED;
2584 while (extBlockCount > 0)
2586 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2590 if (extBlockIndex != BLOCK_UNUSED)
2594 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2596 if (depotBuffer != 0)
2598 StorageUtl_ReadDWord(depotBuffer,
2599 extBlockOffset * sizeof(ULONG),
2602 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2609 /******************************************************************************
2610 * Storage32Impl_SetExtDepotBlock
2612 * Associates the specified block index to the specified depot index.
2613 * This method is only for depot indexes equal or greater than
2614 * COUNT_BBDEPOTINHEADER.
2616 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2620 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2621 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2622 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2623 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2624 ULONG extBlockIndex = This->extBigBlockDepotStart;
2626 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2628 while (extBlockCount > 0)
2630 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2634 if (extBlockIndex != BLOCK_UNUSED)
2638 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2640 if (depotBuffer != 0)
2642 StorageUtl_WriteDWord(depotBuffer,
2643 extBlockOffset * sizeof(ULONG),
2646 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2651 /******************************************************************************
2652 * Storage32Impl_AddExtBlockDepot
2654 * Creates an extended depot block.
2656 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2658 ULONG numExtBlocks = This->extBigBlockDepotCount;
2659 ULONG nextExtBlock = This->extBigBlockDepotStart;
2660 BYTE* depotBuffer = NULL;
2661 ULONG index = BLOCK_UNUSED;
2662 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2663 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2664 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2666 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2667 blocksPerDepotBlock;
2669 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2672 * The first extended block.
2674 This->extBigBlockDepotStart = index;
2680 * Follow the chain to the last one.
2682 for (i = 0; i < (numExtBlocks - 1); i++)
2684 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2688 * Add the new extended block to the chain.
2690 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2691 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2692 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2696 * Initialize this block.
2698 depotBuffer = StorageImpl_GetBigBlock(This, index);
2699 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2700 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2705 /******************************************************************************
2706 * Storage32Impl_FreeBigBlock
2708 * This method will flag the specified block as free in the big block depot.
2710 void StorageImpl_FreeBigBlock(
2714 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2716 if (blockIndex < This->prevFreeBlock)
2717 This->prevFreeBlock = blockIndex;
2720 /************************************************************************
2721 * Storage32Impl_GetNextBlockInChain
2723 * This method will retrieve the block index of the next big block in
2726 * Params: This - Pointer to the Storage object.
2727 * blockIndex - Index of the block to retrieve the chain
2729 * nextBlockIndex - receives the return value.
2731 * Returns: This method returns the index of the next block in the chain.
2732 * It will return the constants:
2733 * BLOCK_SPECIAL - If the block given was not part of a
2735 * BLOCK_END_OF_CHAIN - If the block given was the last in
2737 * BLOCK_UNUSED - If the block given was not past of a chain
2739 * BLOCK_EXTBBDEPOT - This block is part of the extended
2742 * See Windows documentation for more details on IStorage methods.
2744 HRESULT StorageImpl_GetNextBlockInChain(
2747 ULONG* nextBlockIndex)
2749 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2750 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2751 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2753 ULONG depotBlockIndexPos;
2756 *nextBlockIndex = BLOCK_SPECIAL;
2758 if(depotBlockCount >= This->bigBlockDepotCount)
2760 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2761 This->bigBlockDepotCount);
2762 return STG_E_READFAULT;
2766 * Cache the currently accessed depot block.
2768 if (depotBlockCount != This->indexBlockDepotCached)
2770 This->indexBlockDepotCached = depotBlockCount;
2772 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2774 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2779 * We have to look in the extended depot.
2781 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2784 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2787 return STG_E_READFAULT;
2789 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2791 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2792 This->blockDepotCached[index] = *nextBlockIndex;
2794 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2797 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2802 /******************************************************************************
2803 * Storage32Impl_GetNextExtendedBlock
2805 * Given an extended block this method will return the next extended block.
2808 * The last ULONG of an extended block is the block index of the next
2809 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2813 * - The index of the next extended block
2814 * - BLOCK_UNUSED: there is no next extended block.
2815 * - Any other return values denotes failure.
2817 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2819 ULONG nextBlockIndex = BLOCK_SPECIAL;
2820 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2823 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2827 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2829 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2832 return nextBlockIndex;
2835 /******************************************************************************
2836 * Storage32Impl_SetNextBlockInChain
2838 * This method will write the index of the specified block's next block
2839 * in the big block depot.
2841 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2844 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2845 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2846 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2849 void StorageImpl_SetNextBlockInChain(
2854 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2855 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2856 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2857 ULONG depotBlockIndexPos;
2860 assert(depotBlockCount < This->bigBlockDepotCount);
2861 assert(blockIndex != nextBlock);
2863 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2865 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2870 * We have to look in the extended depot.
2872 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2875 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2879 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2880 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2884 * Update the cached block depot, if necessary.
2886 if (depotBlockCount == This->indexBlockDepotCached)
2888 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2892 /******************************************************************************
2893 * Storage32Impl_LoadFileHeader
2895 * This method will read in the file header, i.e. big block index -1.
2897 HRESULT StorageImpl_LoadFileHeader(
2900 HRESULT hr = STG_E_FILENOTFOUND;
2901 void* headerBigBlock = NULL;
2905 * Get a pointer to the big block of data containing the header.
2907 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2910 * Extract the information from the header.
2912 if (headerBigBlock!=0)
2915 * Check for the "magic number" signature and return an error if it is not
2918 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2920 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2921 return STG_E_OLDFORMAT;
2924 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2926 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2927 return STG_E_INVALIDHEADER;
2930 StorageUtl_ReadWord(
2932 OFFSET_BIGBLOCKSIZEBITS,
2933 &This->bigBlockSizeBits);
2935 StorageUtl_ReadWord(
2937 OFFSET_SMALLBLOCKSIZEBITS,
2938 &This->smallBlockSizeBits);
2940 StorageUtl_ReadDWord(
2942 OFFSET_BBDEPOTCOUNT,
2943 &This->bigBlockDepotCount);
2945 StorageUtl_ReadDWord(
2947 OFFSET_ROOTSTARTBLOCK,
2948 &This->rootStartBlock);
2950 StorageUtl_ReadDWord(
2952 OFFSET_SBDEPOTSTART,
2953 &This->smallBlockDepotStart);
2955 StorageUtl_ReadDWord(
2957 OFFSET_EXTBBDEPOTSTART,
2958 &This->extBigBlockDepotStart);
2960 StorageUtl_ReadDWord(
2962 OFFSET_EXTBBDEPOTCOUNT,
2963 &This->extBigBlockDepotCount);
2965 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2967 StorageUtl_ReadDWord(
2969 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2970 &(This->bigBlockDepotStart[index]));
2974 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2978 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2979 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2983 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2984 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2988 * Right now, the code is making some assumptions about the size of the
2989 * blocks, just make sure they are what we're expecting.
2991 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
2992 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
2994 WARN("Broken OLE storage file\n");
2995 hr = STG_E_INVALIDHEADER;
3001 * Release the block.
3003 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3009 /******************************************************************************
3010 * Storage32Impl_SaveFileHeader
3012 * This method will save to the file the header, i.e. big block -1.
3014 void StorageImpl_SaveFileHeader(
3017 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3022 * Get a pointer to the big block of data containing the header.
3024 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3027 * If the block read failed, the file is probably new.
3032 * Initialize for all unknown fields.
3034 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3037 * Initialize the magic number.
3039 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3042 * And a bunch of things we don't know what they mean
3044 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3045 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3046 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3047 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3051 * Write the information to the header.
3053 StorageUtl_WriteWord(
3055 OFFSET_BIGBLOCKSIZEBITS,
3056 This->bigBlockSizeBits);
3058 StorageUtl_WriteWord(
3060 OFFSET_SMALLBLOCKSIZEBITS,
3061 This->smallBlockSizeBits);
3063 StorageUtl_WriteDWord(
3065 OFFSET_BBDEPOTCOUNT,
3066 This->bigBlockDepotCount);
3068 StorageUtl_WriteDWord(
3070 OFFSET_ROOTSTARTBLOCK,
3071 This->rootStartBlock);
3073 StorageUtl_WriteDWord(
3075 OFFSET_SBDEPOTSTART,
3076 This->smallBlockDepotStart);
3078 StorageUtl_WriteDWord(
3080 OFFSET_SBDEPOTCOUNT,
3081 This->smallBlockDepotChain ?
3082 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3084 StorageUtl_WriteDWord(
3086 OFFSET_EXTBBDEPOTSTART,
3087 This->extBigBlockDepotStart);
3089 StorageUtl_WriteDWord(
3091 OFFSET_EXTBBDEPOTCOUNT,
3092 This->extBigBlockDepotCount);
3094 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3096 StorageUtl_WriteDWord(
3098 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3099 (This->bigBlockDepotStart[index]));
3103 * Write the big block back to the file.
3105 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3108 /******************************************************************************
3109 * Storage32Impl_ReadProperty
3111 * This method will read the specified property from the property chain.
3113 BOOL StorageImpl_ReadProperty(
3116 StgProperty* buffer)
3118 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3119 ULARGE_INTEGER offsetInPropSet;
3120 BOOL readSuccessful;
3123 offsetInPropSet.s.HighPart = 0;
3124 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3126 readSuccessful = BlockChainStream_ReadAt(
3127 This->rootBlockChain,
3135 /* replace the name of root entry (often "Root Entry") by the file name */
3136 WCHAR *propName = (index == This->rootPropertySetIndex) ?
3137 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3139 memset(buffer->name, 0, sizeof(buffer->name));
3143 PROPERTY_NAME_BUFFER_LEN );
3144 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3146 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3148 StorageUtl_ReadWord(
3150 OFFSET_PS_NAMELENGTH,
3151 &buffer->sizeOfNameString);
3153 StorageUtl_ReadDWord(
3155 OFFSET_PS_PREVIOUSPROP,
3156 &buffer->previousProperty);
3158 StorageUtl_ReadDWord(
3161 &buffer->nextProperty);
3163 StorageUtl_ReadDWord(
3166 &buffer->dirProperty);
3168 StorageUtl_ReadGUID(
3171 &buffer->propertyUniqueID);
3173 StorageUtl_ReadDWord(
3176 &buffer->timeStampS1);
3178 StorageUtl_ReadDWord(
3181 &buffer->timeStampD1);
3183 StorageUtl_ReadDWord(
3186 &buffer->timeStampS2);
3188 StorageUtl_ReadDWord(
3191 &buffer->timeStampD2);
3193 StorageUtl_ReadDWord(
3195 OFFSET_PS_STARTBLOCK,
3196 &buffer->startingBlock);
3198 StorageUtl_ReadDWord(
3201 &buffer->size.s.LowPart);
3203 buffer->size.s.HighPart = 0;
3206 return readSuccessful;
3209 /*********************************************************************
3210 * Write the specified property into the property chain
3212 BOOL StorageImpl_WriteProperty(
3215 StgProperty* buffer)
3217 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3218 ULARGE_INTEGER offsetInPropSet;
3219 BOOL writeSuccessful;
3222 offsetInPropSet.s.HighPart = 0;
3223 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3225 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3228 currentProperty + OFFSET_PS_NAME,
3230 PROPERTY_NAME_BUFFER_LEN );
3232 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3234 StorageUtl_WriteWord(
3236 OFFSET_PS_NAMELENGTH,
3237 buffer->sizeOfNameString);
3239 StorageUtl_WriteDWord(
3241 OFFSET_PS_PREVIOUSPROP,
3242 buffer->previousProperty);
3244 StorageUtl_WriteDWord(
3247 buffer->nextProperty);
3249 StorageUtl_WriteDWord(
3252 buffer->dirProperty);
3254 StorageUtl_WriteGUID(
3257 &buffer->propertyUniqueID);
3259 StorageUtl_WriteDWord(
3262 buffer->timeStampS1);
3264 StorageUtl_WriteDWord(
3267 buffer->timeStampD1);
3269 StorageUtl_WriteDWord(
3272 buffer->timeStampS2);
3274 StorageUtl_WriteDWord(
3277 buffer->timeStampD2);
3279 StorageUtl_WriteDWord(
3281 OFFSET_PS_STARTBLOCK,
3282 buffer->startingBlock);
3284 StorageUtl_WriteDWord(
3287 buffer->size.s.LowPart);
3289 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3294 return writeSuccessful;
3297 BOOL StorageImpl_ReadBigBlock(
3302 void* bigBlockBuffer;
3304 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3306 if (bigBlockBuffer!=0)
3308 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3310 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3318 BOOL StorageImpl_WriteBigBlock(
3323 void* bigBlockBuffer;
3325 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3327 if (bigBlockBuffer!=0)
3329 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3331 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3339 void* StorageImpl_GetROBigBlock(
3343 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3346 void* StorageImpl_GetBigBlock(
3350 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3353 void StorageImpl_ReleaseBigBlock(
3357 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3360 /******************************************************************************
3361 * Storage32Impl_SmallBlocksToBigBlocks
3363 * This method will convert a small block chain to a big block chain.
3364 * The small block chain will be destroyed.
3366 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3368 SmallBlockChainStream** ppsbChain)
3370 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3371 ULARGE_INTEGER size, offset;
3372 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3373 ULONG propertyIndex;
3374 BOOL successRead, successWrite;
3375 StgProperty chainProperty;
3377 BlockChainStream *bbTempChain = NULL;
3378 BlockChainStream *bigBlockChain = NULL;
3381 * Create a temporary big block chain that doesn't have
3382 * an associated property. This temporary chain will be
3383 * used to copy data from small blocks to big blocks.
3385 bbTempChain = BlockChainStream_Construct(This,
3388 if(!bbTempChain) return NULL;
3390 * Grow the big block chain.
3392 size = SmallBlockChainStream_GetSize(*ppsbChain);
3393 BlockChainStream_SetSize(bbTempChain, size);
3396 * Copy the contents of the small block chain to the big block chain
3397 * by small block size increments.
3399 offset.s.LowPart = 0;
3400 offset.s.HighPart = 0;
3404 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3407 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3409 DEF_SMALL_BLOCK_SIZE,
3412 cbTotalRead += cbRead;
3414 successWrite = BlockChainStream_WriteAt(bbTempChain,
3419 cbTotalWritten += cbWritten;
3421 offset.s.LowPart += This->smallBlockSize;
3423 } while (successRead && successWrite);
3424 HeapFree(GetProcessHeap(),0,buffer);
3426 assert(cbTotalRead == cbTotalWritten);
3429 * Destroy the small block chain.
3431 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3432 size.s.HighPart = 0;
3434 SmallBlockChainStream_SetSize(*ppsbChain, size);
3435 SmallBlockChainStream_Destroy(*ppsbChain);
3439 * Change the property information. This chain is now a big block chain
3440 * and it doesn't reside in the small blocks chain anymore.
3442 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3444 chainProperty.startingBlock = bbHeadOfChain;
3446 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3449 * Destroy the temporary propertyless big block chain.
3450 * Create a new big block chain associated with this property.
3452 BlockChainStream_Destroy(bbTempChain);
3453 bigBlockChain = BlockChainStream_Construct(This,
3457 return bigBlockChain;
3460 /******************************************************************************
3461 ** Storage32InternalImpl implementation
3464 StorageInternalImpl* StorageInternalImpl_Construct(
3465 StorageImpl* ancestorStorage,
3466 ULONG rootPropertyIndex)
3468 StorageInternalImpl* newStorage;
3471 * Allocate space for the new storage object
3473 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3477 memset(newStorage, 0, sizeof(StorageInternalImpl));
3480 * Initialize the virtual function table.
3482 ICOM_VTBL(newStorage) = &Storage32InternalImpl_Vtbl;
3483 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3486 * Keep the ancestor storage pointer and nail a reference to it.
3488 newStorage->ancestorStorage = ancestorStorage;
3489 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3492 * Keep the index of the root property set for this storage,
3494 newStorage->rootPropertySetIndex = rootPropertyIndex;
3502 void StorageInternalImpl_Destroy(
3503 StorageInternalImpl* This)
3505 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3506 HeapFree(GetProcessHeap(), 0, This);
3509 /******************************************************************************
3511 ** Storage32InternalImpl_Commit
3513 ** The non-root storages cannot be opened in transacted mode thus this function
3516 HRESULT WINAPI StorageInternalImpl_Commit(
3518 DWORD grfCommitFlags) /* [in] */
3523 /******************************************************************************
3525 ** Storage32InternalImpl_Revert
3527 ** The non-root storages cannot be opened in transacted mode thus this function
3530 HRESULT WINAPI StorageInternalImpl_Revert(
3536 /******************************************************************************
3537 ** IEnumSTATSTGImpl implementation
3540 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3541 StorageImpl* parentStorage,
3542 ULONG firstPropertyNode)
3544 IEnumSTATSTGImpl* newEnumeration;
3546 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3548 if (newEnumeration!=0)
3551 * Set-up the virtual function table and reference count.
3553 ICOM_VTBL(newEnumeration) = &IEnumSTATSTGImpl_Vtbl;
3554 newEnumeration->ref = 0;
3557 * We want to nail-down the reference to the storage in case the
3558 * enumeration out-lives the storage in the client application.
3560 newEnumeration->parentStorage = parentStorage;
3561 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3563 newEnumeration->firstPropertyNode = firstPropertyNode;
3566 * Initialize the search stack
3568 newEnumeration->stackSize = 0;
3569 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3570 newEnumeration->stackToVisit =
3571 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3574 * Make sure the current node of the iterator is the first one.
3576 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3579 return newEnumeration;
3582 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3584 IStorage_Release((IStorage*)This->parentStorage);
3585 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3586 HeapFree(GetProcessHeap(), 0, This);
3589 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3590 IEnumSTATSTG* iface,
3594 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3597 * Perform a sanity check on the parameters.
3600 return E_INVALIDARG;
3603 * Initialize the return parameter.
3608 * Compare the riid with the interface IDs implemented by this object.
3610 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3612 *ppvObject = (IEnumSTATSTG*)This;
3614 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3616 *ppvObject = (IEnumSTATSTG*)This;
3620 * Check that we obtained an interface.
3622 if ((*ppvObject)==0)
3623 return E_NOINTERFACE;
3626 * Query Interface always increases the reference count by one when it is
3629 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3634 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3635 IEnumSTATSTG* iface)
3637 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3643 ULONG WINAPI IEnumSTATSTGImpl_Release(
3644 IEnumSTATSTG* iface)
3646 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3654 * If the reference count goes down to 0, perform suicide.
3658 IEnumSTATSTGImpl_Destroy(This);
3664 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3665 IEnumSTATSTG* iface,
3668 ULONG* pceltFetched)
3670 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3672 StgProperty currentProperty;
3673 STATSTG* currentReturnStruct = rgelt;
3674 ULONG objectFetched = 0;
3675 ULONG currentSearchNode;
3678 * Perform a sanity check on the parameters.
3680 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3681 return E_INVALIDARG;
3684 * To avoid the special case, get another pointer to a ULONG value if
3685 * the caller didn't supply one.
3687 if (pceltFetched==0)
3688 pceltFetched = &objectFetched;
3691 * Start the iteration, we will iterate until we hit the end of the
3692 * linked list or until we hit the number of items to iterate through
3697 * Start with the node at the top of the stack.
3699 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3701 while ( ( *pceltFetched < celt) &&
3702 ( currentSearchNode!=PROPERTY_NULL) )
3705 * Remove the top node from the stack
3707 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3710 * Read the property from the storage.
3712 StorageImpl_ReadProperty(This->parentStorage,
3717 * Copy the information to the return buffer.
3719 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3724 * Step to the next item in the iteration
3727 currentReturnStruct++;
3730 * Push the next search node in the search stack.
3732 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3735 * continue the iteration.
3737 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3740 if (*pceltFetched == celt)
3747 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3748 IEnumSTATSTG* iface,
3751 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3753 StgProperty currentProperty;
3754 ULONG objectFetched = 0;
3755 ULONG currentSearchNode;
3758 * Start with the node at the top of the stack.
3760 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3762 while ( (objectFetched < celt) &&
3763 (currentSearchNode!=PROPERTY_NULL) )
3766 * Remove the top node from the stack
3768 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3771 * Read the property from the storage.
3773 StorageImpl_ReadProperty(This->parentStorage,
3778 * Step to the next item in the iteration
3783 * Push the next search node in the search stack.
3785 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3788 * continue the iteration.
3790 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3793 if (objectFetched == celt)
3799 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3800 IEnumSTATSTG* iface)
3802 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3804 StgProperty rootProperty;
3805 BOOL readSuccessful;
3808 * Re-initialize the search stack to an empty stack
3810 This->stackSize = 0;
3813 * Read the root property from the storage.
3815 readSuccessful = StorageImpl_ReadProperty(
3816 This->parentStorage,
3817 This->firstPropertyNode,
3822 assert(rootProperty.sizeOfNameString!=0);
3825 * Push the search node in the search stack.
3827 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3833 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3834 IEnumSTATSTG* iface,
3835 IEnumSTATSTG** ppenum)
3837 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3839 IEnumSTATSTGImpl* newClone;
3842 * Perform a sanity check on the parameters.
3845 return E_INVALIDARG;
3847 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3848 This->firstPropertyNode);
3852 * The new clone enumeration must point to the same current node as
3855 newClone->stackSize = This->stackSize ;
3856 newClone->stackMaxSize = This->stackMaxSize ;
3857 newClone->stackToVisit =
3858 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3861 newClone->stackToVisit,
3863 sizeof(ULONG) * newClone->stackSize);
3865 *ppenum = (IEnumSTATSTG*)newClone;
3868 * Don't forget to nail down a reference to the clone before
3871 IEnumSTATSTGImpl_AddRef(*ppenum);
3876 INT IEnumSTATSTGImpl_FindParentProperty(
3877 IEnumSTATSTGImpl *This,
3878 ULONG childProperty,
3879 StgProperty *currentProperty,
3882 ULONG currentSearchNode;
3886 * To avoid the special case, get another pointer to a ULONG value if
3887 * the caller didn't supply one.
3890 thisNodeId = &foundNode;
3893 * Start with the node at the top of the stack.
3895 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3898 while (currentSearchNode!=PROPERTY_NULL)
3901 * Store the current node in the returned parameters
3903 *thisNodeId = currentSearchNode;
3906 * Remove the top node from the stack
3908 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3911 * Read the property from the storage.
3913 StorageImpl_ReadProperty(
3914 This->parentStorage,
3918 if (currentProperty->previousProperty == childProperty)
3919 return PROPERTY_RELATION_PREVIOUS;
3921 else if (currentProperty->nextProperty == childProperty)
3922 return PROPERTY_RELATION_NEXT;
3924 else if (currentProperty->dirProperty == childProperty)
3925 return PROPERTY_RELATION_DIR;
3928 * Push the next search node in the search stack.
3930 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3933 * continue the iteration.
3935 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3938 return PROPERTY_NULL;
3941 ULONG IEnumSTATSTGImpl_FindProperty(
3942 IEnumSTATSTGImpl* This,
3943 const OLECHAR* lpszPropName,
3944 StgProperty* currentProperty)
3946 ULONG currentSearchNode;
3949 * Start with the node at the top of the stack.
3951 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3953 while (currentSearchNode!=PROPERTY_NULL)
3956 * Remove the top node from the stack
3958 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3961 * Read the property from the storage.
3963 StorageImpl_ReadProperty(This->parentStorage,
3967 if ( propertyNameCmp(
3968 (OLECHAR*)currentProperty->name,
3969 (OLECHAR*)lpszPropName) == 0)
3970 return currentSearchNode;
3973 * Push the next search node in the search stack.
3975 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3978 * continue the iteration.
3980 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3983 return PROPERTY_NULL;
3986 void IEnumSTATSTGImpl_PushSearchNode(
3987 IEnumSTATSTGImpl* This,
3990 StgProperty rootProperty;
3991 BOOL readSuccessful;
3994 * First, make sure we're not trying to push an unexisting node.
3996 if (nodeToPush==PROPERTY_NULL)
4000 * First push the node to the stack
4002 if (This->stackSize == This->stackMaxSize)
4004 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4006 This->stackToVisit = HeapReAlloc(
4010 sizeof(ULONG) * This->stackMaxSize);
4013 This->stackToVisit[This->stackSize] = nodeToPush;
4017 * Read the root property from the storage.
4019 readSuccessful = StorageImpl_ReadProperty(
4020 This->parentStorage,
4026 assert(rootProperty.sizeOfNameString!=0);
4029 * Push the previous search node in the search stack.
4031 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4035 ULONG IEnumSTATSTGImpl_PopSearchNode(
4036 IEnumSTATSTGImpl* This,
4041 if (This->stackSize == 0)
4042 return PROPERTY_NULL;
4044 topNode = This->stackToVisit[This->stackSize-1];
4052 /******************************************************************************
4053 ** StorageUtl implementation
4056 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
4058 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
4061 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
4063 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
4066 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
4068 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
4071 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
4073 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
4076 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
4078 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4079 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4080 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4082 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
4085 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
4087 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4088 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4089 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4091 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
4094 void StorageUtl_CopyPropertyToSTATSTG(
4095 STATSTG* destination,
4096 StgProperty* source,
4100 * The copy of the string occurs only when the flag is not set
4102 if ((statFlags & STATFLAG_NONAME) != 0)
4104 destination->pwcsName = 0;
4108 destination->pwcsName =
4109 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4111 strcpyW((LPWSTR)destination->pwcsName, source->name);
4114 switch (source->propertyType)
4116 case PROPTYPE_STORAGE:
4118 destination->type = STGTY_STORAGE;
4120 case PROPTYPE_STREAM:
4121 destination->type = STGTY_STREAM;
4124 destination->type = STGTY_STREAM;
4128 destination->cbSize = source->size;
4130 currentReturnStruct->mtime = {0}; TODO
4131 currentReturnStruct->ctime = {0};
4132 currentReturnStruct->atime = {0};
4134 destination->grfMode = 0;
4135 destination->grfLocksSupported = 0;
4136 destination->clsid = source->propertyUniqueID;
4137 destination->grfStateBits = 0;
4138 destination->reserved = 0;
4141 /******************************************************************************
4142 ** BlockChainStream implementation
4145 BlockChainStream* BlockChainStream_Construct(
4146 StorageImpl* parentStorage,
4147 ULONG* headOfStreamPlaceHolder,
4148 ULONG propertyIndex)
4150 BlockChainStream* newStream;
4153 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4155 newStream->parentStorage = parentStorage;
4156 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4157 newStream->ownerPropertyIndex = propertyIndex;
4158 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4159 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4160 newStream->numBlocks = 0;
4162 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4164 while (blockIndex != BLOCK_END_OF_CHAIN)
4166 newStream->numBlocks++;
4167 newStream->tailIndex = blockIndex;
4169 if(FAILED(StorageImpl_GetNextBlockInChain(
4174 HeapFree(GetProcessHeap(), 0, newStream);
4182 void BlockChainStream_Destroy(BlockChainStream* This)
4184 HeapFree(GetProcessHeap(), 0, This);
4187 /******************************************************************************
4188 * BlockChainStream_GetHeadOfChain
4190 * Returns the head of this stream chain.
4191 * Some special chains don't have properties, their heads are kept in
4192 * This->headOfStreamPlaceHolder.
4195 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4197 StgProperty chainProperty;
4198 BOOL readSuccessful;
4200 if (This->headOfStreamPlaceHolder != 0)
4201 return *(This->headOfStreamPlaceHolder);
4203 if (This->ownerPropertyIndex != PROPERTY_NULL)
4205 readSuccessful = StorageImpl_ReadProperty(
4206 This->parentStorage,
4207 This->ownerPropertyIndex,
4212 return chainProperty.startingBlock;
4216 return BLOCK_END_OF_CHAIN;
4219 /******************************************************************************
4220 * BlockChainStream_GetCount
4222 * Returns the number of blocks that comprises this chain.
4223 * This is not the size of the stream as the last block may not be full!
4226 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4231 blockIndex = BlockChainStream_GetHeadOfChain(This);
4233 while (blockIndex != BLOCK_END_OF_CHAIN)
4237 if(FAILED(StorageImpl_GetNextBlockInChain(
4238 This->parentStorage,
4247 /******************************************************************************
4248 * BlockChainStream_ReadAt
4250 * Reads a specified number of bytes from this chain at the specified offset.
4251 * bytesRead may be NULL.
4252 * Failure will be returned if the specified number of bytes has not been read.
4254 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4255 ULARGE_INTEGER offset,
4260 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4261 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4262 ULONG bytesToReadInBuffer;
4265 BYTE* bigBlockBuffer;
4268 * Find the first block in the stream that contains part of the buffer.
4270 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4271 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4272 (blockNoInSequence < This->lastBlockNoInSequence) )
4274 blockIndex = BlockChainStream_GetHeadOfChain(This);
4275 This->lastBlockNoInSequence = blockNoInSequence;
4279 ULONG temp = blockNoInSequence;
4281 blockIndex = This->lastBlockNoInSequenceIndex;
4282 blockNoInSequence -= This->lastBlockNoInSequence;
4283 This->lastBlockNoInSequence = temp;
4286 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4288 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4290 blockNoInSequence--;
4293 This->lastBlockNoInSequenceIndex = blockIndex;
4296 * Start reading the buffer.
4299 bufferWalker = buffer;
4301 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4304 * Calculate how many bytes we can copy from this big block.
4306 bytesToReadInBuffer =
4307 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4310 * Copy those bytes to the buffer
4313 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4315 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4317 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4320 * Step to the next big block.
4322 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4325 bufferWalker += bytesToReadInBuffer;
4326 size -= bytesToReadInBuffer;
4327 *bytesRead += bytesToReadInBuffer;
4328 offsetInBlock = 0; /* There is no offset on the next block */
4335 /******************************************************************************
4336 * BlockChainStream_WriteAt
4338 * Writes the specified number of bytes to this chain at the specified offset.
4339 * bytesWritten may be NULL.
4340 * Will fail if not all specified number of bytes have been written.
4342 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4343 ULARGE_INTEGER offset,
4346 ULONG* bytesWritten)
4348 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4349 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4353 BYTE* bigBlockBuffer;
4356 * Find the first block in the stream that contains part of the buffer.
4358 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4359 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4360 (blockNoInSequence < This->lastBlockNoInSequence) )
4362 blockIndex = BlockChainStream_GetHeadOfChain(This);
4363 This->lastBlockNoInSequence = blockNoInSequence;
4367 ULONG temp = blockNoInSequence;
4369 blockIndex = This->lastBlockNoInSequenceIndex;
4370 blockNoInSequence -= This->lastBlockNoInSequence;
4371 This->lastBlockNoInSequence = temp;
4374 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4376 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4379 blockNoInSequence--;
4382 This->lastBlockNoInSequenceIndex = blockIndex;
4385 * Here, I'm casting away the constness on the buffer variable
4386 * This is OK since we don't intend to modify that buffer.
4389 bufferWalker = (BYTE*)buffer;
4391 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4394 * Calculate how many bytes we can copy from this big block.
4397 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4400 * Copy those bytes to the buffer
4402 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4404 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4406 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4409 * Step to the next big block.
4411 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4414 bufferWalker += bytesToWrite;
4415 size -= bytesToWrite;
4416 *bytesWritten += bytesToWrite;
4417 offsetInBlock = 0; /* There is no offset on the next block */
4423 /******************************************************************************
4424 * BlockChainStream_Shrink
4426 * Shrinks this chain in the big block depot.
4428 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4429 ULARGE_INTEGER newSize)
4431 ULONG blockIndex, extraBlock;
4436 * Reset the last accessed block cache.
4438 This->lastBlockNoInSequence = 0xFFFFFFFF;
4439 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4442 * Figure out how many blocks are needed to contain the new size
4444 numBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4446 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4449 blockIndex = BlockChainStream_GetHeadOfChain(This);
4452 * Go to the new end of chain
4454 while (count < numBlocks)
4456 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4462 /* Get the next block before marking the new end */
4463 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4467 /* Mark the new end of chain */
4468 StorageImpl_SetNextBlockInChain(
4469 This->parentStorage,
4471 BLOCK_END_OF_CHAIN);
4473 This->tailIndex = blockIndex;
4474 This->numBlocks = numBlocks;
4477 * Mark the extra blocks as free
4479 while (extraBlock != BLOCK_END_OF_CHAIN)
4481 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4484 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4485 extraBlock = blockIndex;
4491 /******************************************************************************
4492 * BlockChainStream_Enlarge
4494 * Grows this chain in the big block depot.
4496 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4497 ULARGE_INTEGER newSize)
4499 ULONG blockIndex, currentBlock;
4501 ULONG oldNumBlocks = 0;
4503 blockIndex = BlockChainStream_GetHeadOfChain(This);
4506 * Empty chain. Create the head.
4508 if (blockIndex == BLOCK_END_OF_CHAIN)
4510 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4511 StorageImpl_SetNextBlockInChain(This->parentStorage,
4513 BLOCK_END_OF_CHAIN);
4515 if (This->headOfStreamPlaceHolder != 0)
4517 *(This->headOfStreamPlaceHolder) = blockIndex;
4521 StgProperty chainProp;
4522 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4524 StorageImpl_ReadProperty(
4525 This->parentStorage,
4526 This->ownerPropertyIndex,
4529 chainProp.startingBlock = blockIndex;
4531 StorageImpl_WriteProperty(
4532 This->parentStorage,
4533 This->ownerPropertyIndex,
4537 This->tailIndex = blockIndex;
4538 This->numBlocks = 1;
4542 * Figure out how many blocks are needed to contain this stream
4544 newNumBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4546 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4550 * Go to the current end of chain
4552 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4554 currentBlock = blockIndex;
4556 while (blockIndex != BLOCK_END_OF_CHAIN)
4559 currentBlock = blockIndex;
4561 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4566 This->tailIndex = currentBlock;
4569 currentBlock = This->tailIndex;
4570 oldNumBlocks = This->numBlocks;
4573 * Add new blocks to the chain
4575 if (oldNumBlocks < newNumBlocks)
4577 while (oldNumBlocks < newNumBlocks)
4579 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4581 StorageImpl_SetNextBlockInChain(
4582 This->parentStorage,
4586 StorageImpl_SetNextBlockInChain(
4587 This->parentStorage,
4589 BLOCK_END_OF_CHAIN);
4591 currentBlock = blockIndex;
4595 This->tailIndex = blockIndex;
4596 This->numBlocks = newNumBlocks;
4602 /******************************************************************************
4603 * BlockChainStream_SetSize
4605 * Sets the size of this stream. The big block depot will be updated.
4606 * The file will grow if we grow the chain.
4608 * TODO: Free the actual blocks in the file when we shrink the chain.
4609 * Currently, the blocks are still in the file. So the file size
4610 * doesn't shrink even if we shrink streams.
4612 BOOL BlockChainStream_SetSize(
4613 BlockChainStream* This,
4614 ULARGE_INTEGER newSize)
4616 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4618 if (newSize.s.LowPart == size.s.LowPart)
4621 if (newSize.s.LowPart < size.s.LowPart)
4623 BlockChainStream_Shrink(This, newSize);
4627 ULARGE_INTEGER fileSize =
4628 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4630 ULONG diff = newSize.s.LowPart - size.s.LowPart;
4633 * Make sure the file stays a multiple of blocksize
4635 if ((diff % This->parentStorage->bigBlockSize) != 0)
4636 diff += (This->parentStorage->bigBlockSize -
4637 (diff % This->parentStorage->bigBlockSize) );
4639 fileSize.s.LowPart += diff;
4640 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4642 BlockChainStream_Enlarge(This, newSize);
4648 /******************************************************************************
4649 * BlockChainStream_GetSize
4651 * Returns the size of this chain.
4652 * Will return the block count if this chain doesn't have a property.
4654 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4656 StgProperty chainProperty;
4658 if(This->headOfStreamPlaceHolder == NULL)
4661 * This chain is a data stream read the property and return
4662 * the appropriate size
4664 StorageImpl_ReadProperty(
4665 This->parentStorage,
4666 This->ownerPropertyIndex,
4669 return chainProperty.size;
4674 * this chain is a chain that does not have a property, figure out the
4675 * size by making the product number of used blocks times the
4678 ULARGE_INTEGER result;
4679 result.s.HighPart = 0;
4682 BlockChainStream_GetCount(This) *
4683 This->parentStorage->bigBlockSize;
4689 /******************************************************************************
4690 ** SmallBlockChainStream implementation
4693 SmallBlockChainStream* SmallBlockChainStream_Construct(
4694 StorageImpl* parentStorage,
4695 ULONG propertyIndex)
4697 SmallBlockChainStream* newStream;
4699 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4701 newStream->parentStorage = parentStorage;
4702 newStream->ownerPropertyIndex = propertyIndex;
4707 void SmallBlockChainStream_Destroy(
4708 SmallBlockChainStream* This)
4710 HeapFree(GetProcessHeap(), 0, This);
4713 /******************************************************************************
4714 * SmallBlockChainStream_GetHeadOfChain
4716 * Returns the head of this chain of small blocks.
4718 ULONG SmallBlockChainStream_GetHeadOfChain(
4719 SmallBlockChainStream* This)
4721 StgProperty chainProperty;
4722 BOOL readSuccessful;
4724 if (This->ownerPropertyIndex)
4726 readSuccessful = StorageImpl_ReadProperty(
4727 This->parentStorage,
4728 This->ownerPropertyIndex,
4733 return chainProperty.startingBlock;
4738 return BLOCK_END_OF_CHAIN;
4741 /******************************************************************************
4742 * SmallBlockChainStream_GetNextBlockInChain
4744 * Returns the index of the next small block in this chain.
4747 * - BLOCK_END_OF_CHAIN: end of this chain
4748 * - BLOCK_UNUSED: small block 'blockIndex' is free
4750 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4751 SmallBlockChainStream* This,
4753 ULONG* nextBlockInChain)
4755 ULARGE_INTEGER offsetOfBlockInDepot;
4760 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4762 offsetOfBlockInDepot.s.HighPart = 0;
4763 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4766 * Read those bytes in the buffer from the small block file.
4768 success = BlockChainStream_ReadAt(
4769 This->parentStorage->smallBlockDepotChain,
4770 offsetOfBlockInDepot,
4777 StorageUtl_ReadDWord(&buffer, 0, nextBlockInChain);
4781 return STG_E_READFAULT;
4784 /******************************************************************************
4785 * SmallBlockChainStream_SetNextBlockInChain
4787 * Writes the index of the next block of the specified block in the small
4789 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4790 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4792 void SmallBlockChainStream_SetNextBlockInChain(
4793 SmallBlockChainStream* This,
4797 ULARGE_INTEGER offsetOfBlockInDepot;
4801 offsetOfBlockInDepot.s.HighPart = 0;
4802 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4804 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4807 * Read those bytes in the buffer from the small block file.
4809 BlockChainStream_WriteAt(
4810 This->parentStorage->smallBlockDepotChain,
4811 offsetOfBlockInDepot,
4817 /******************************************************************************
4818 * SmallBlockChainStream_FreeBlock
4820 * Flag small block 'blockIndex' as free in the small block depot.
4822 void SmallBlockChainStream_FreeBlock(
4823 SmallBlockChainStream* This,
4826 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4829 /******************************************************************************
4830 * SmallBlockChainStream_GetNextFreeBlock
4832 * Returns the index of a free small block. The small block depot will be
4833 * enlarged if necessary. The small block chain will also be enlarged if
4836 ULONG SmallBlockChainStream_GetNextFreeBlock(
4837 SmallBlockChainStream* This)
4839 ULARGE_INTEGER offsetOfBlockInDepot;
4842 ULONG blockIndex = 0;
4843 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4844 BOOL success = TRUE;
4845 ULONG smallBlocksPerBigBlock;
4847 offsetOfBlockInDepot.s.HighPart = 0;
4850 * Scan the small block depot for a free block
4852 while (nextBlockIndex != BLOCK_UNUSED)
4854 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4856 success = BlockChainStream_ReadAt(
4857 This->parentStorage->smallBlockDepotChain,
4858 offsetOfBlockInDepot,
4864 * If we run out of space for the small block depot, enlarge it
4868 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4870 if (nextBlockIndex != BLOCK_UNUSED)
4876 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4878 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4879 ULONG nextBlock, newsbdIndex;
4880 BYTE* smallBlockDepot;
4882 nextBlock = sbdIndex;
4883 while (nextBlock != BLOCK_END_OF_CHAIN)
4885 sbdIndex = nextBlock;
4886 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4889 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4890 if (sbdIndex != BLOCK_END_OF_CHAIN)
4891 StorageImpl_SetNextBlockInChain(
4892 This->parentStorage,
4896 StorageImpl_SetNextBlockInChain(
4897 This->parentStorage,
4899 BLOCK_END_OF_CHAIN);
4902 * Initialize all the small blocks to free
4905 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4907 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4908 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4913 * We have just created the small block depot.
4915 StgProperty rootProp;
4919 * Save it in the header
4921 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4922 StorageImpl_SaveFileHeader(This->parentStorage);
4925 * And allocate the first big block that will contain small blocks
4928 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4930 StorageImpl_SetNextBlockInChain(
4931 This->parentStorage,
4933 BLOCK_END_OF_CHAIN);
4935 StorageImpl_ReadProperty(
4936 This->parentStorage,
4937 This->parentStorage->rootPropertySetIndex,
4940 rootProp.startingBlock = sbStartIndex;
4941 rootProp.size.s.HighPart = 0;
4942 rootProp.size.s.LowPart = This->parentStorage->bigBlockSize;
4944 StorageImpl_WriteProperty(
4945 This->parentStorage,
4946 This->parentStorage->rootPropertySetIndex,
4952 smallBlocksPerBigBlock =
4953 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4956 * Verify if we have to allocate big blocks to contain small blocks
4958 if (blockIndex % smallBlocksPerBigBlock == 0)
4960 StgProperty rootProp;
4961 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4963 StorageImpl_ReadProperty(
4964 This->parentStorage,
4965 This->parentStorage->rootPropertySetIndex,
4968 if (rootProp.size.s.LowPart <
4969 (blocksRequired * This->parentStorage->bigBlockSize))
4971 rootProp.size.s.LowPart += This->parentStorage->bigBlockSize;
4973 BlockChainStream_SetSize(
4974 This->parentStorage->smallBlockRootChain,
4977 StorageImpl_WriteProperty(
4978 This->parentStorage,
4979 This->parentStorage->rootPropertySetIndex,
4987 /******************************************************************************
4988 * SmallBlockChainStream_ReadAt
4990 * Reads a specified number of bytes from this chain at the specified offset.
4991 * bytesRead may be NULL.
4992 * Failure will be returned if the specified number of bytes has not been read.
4994 BOOL SmallBlockChainStream_ReadAt(
4995 SmallBlockChainStream* This,
4996 ULARGE_INTEGER offset,
5001 ULARGE_INTEGER offsetInBigBlockFile;
5002 ULONG blockNoInSequence =
5003 offset.s.LowPart / This->parentStorage->smallBlockSize;
5005 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
5006 ULONG bytesToReadInBuffer;
5008 ULONG bytesReadFromBigBlockFile;
5012 * This should never happen on a small block file.
5014 assert(offset.s.HighPart==0);
5017 * Find the first block in the stream that contains part of the buffer.
5019 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5021 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5023 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5026 blockNoInSequence--;
5030 * Start reading the buffer.
5033 bufferWalker = buffer;
5035 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5038 * Calculate how many bytes we can copy from this small block.
5040 bytesToReadInBuffer =
5041 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5044 * Calculate the offset of the small block in the small block file.
5046 offsetInBigBlockFile.s.HighPart = 0;
5047 offsetInBigBlockFile.s.LowPart =
5048 blockIndex * This->parentStorage->smallBlockSize;
5050 offsetInBigBlockFile.s.LowPart += offsetInBlock;
5053 * Read those bytes in the buffer from the small block file.
5055 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5056 offsetInBigBlockFile,
5057 bytesToReadInBuffer,
5059 &bytesReadFromBigBlockFile);
5061 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5064 * Step to the next big block.
5066 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5068 bufferWalker += bytesToReadInBuffer;
5069 size -= bytesToReadInBuffer;
5070 *bytesRead += bytesToReadInBuffer;
5071 offsetInBlock = 0; /* There is no offset on the next block */
5077 /******************************************************************************
5078 * SmallBlockChainStream_WriteAt
5080 * Writes the specified number of bytes to this chain at the specified offset.
5081 * bytesWritten may be NULL.
5082 * Will fail if not all specified number of bytes have been written.
5084 BOOL SmallBlockChainStream_WriteAt(
5085 SmallBlockChainStream* This,
5086 ULARGE_INTEGER offset,
5089 ULONG* bytesWritten)
5091 ULARGE_INTEGER offsetInBigBlockFile;
5092 ULONG blockNoInSequence =
5093 offset.s.LowPart / This->parentStorage->smallBlockSize;
5095 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
5096 ULONG bytesToWriteInBuffer;
5098 ULONG bytesWrittenFromBigBlockFile;
5102 * This should never happen on a small block file.
5104 assert(offset.s.HighPart==0);
5107 * Find the first block in the stream that contains part of the buffer.
5109 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5111 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5113 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5115 blockNoInSequence--;
5119 * Start writing the buffer.
5121 * Here, I'm casting away the constness on the buffer variable
5122 * This is OK since we don't intend to modify that buffer.
5125 bufferWalker = (BYTE*)buffer;
5126 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5129 * Calculate how many bytes we can copy to this small block.
5131 bytesToWriteInBuffer =
5132 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5135 * Calculate the offset of the small block in the small block file.
5137 offsetInBigBlockFile.s.HighPart = 0;
5138 offsetInBigBlockFile.s.LowPart =
5139 blockIndex * This->parentStorage->smallBlockSize;
5141 offsetInBigBlockFile.s.LowPart += offsetInBlock;
5144 * Write those bytes in the buffer to the small block file.
5146 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5147 offsetInBigBlockFile,
5148 bytesToWriteInBuffer,
5150 &bytesWrittenFromBigBlockFile);
5152 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5155 * Step to the next big block.
5157 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5160 bufferWalker += bytesToWriteInBuffer;
5161 size -= bytesToWriteInBuffer;
5162 *bytesWritten += bytesToWriteInBuffer;
5163 offsetInBlock = 0; /* There is no offset on the next block */
5169 /******************************************************************************
5170 * SmallBlockChainStream_Shrink
5172 * Shrinks this chain in the small block depot.
5174 BOOL SmallBlockChainStream_Shrink(
5175 SmallBlockChainStream* This,
5176 ULARGE_INTEGER newSize)
5178 ULONG blockIndex, extraBlock;
5182 numBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5184 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5187 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5190 * Go to the new end of chain
5192 while (count < numBlocks)
5194 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5201 * If the count is 0, we have a special case, the head of the chain was
5206 StgProperty chainProp;
5208 StorageImpl_ReadProperty(This->parentStorage,
5209 This->ownerPropertyIndex,
5212 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5214 StorageImpl_WriteProperty(This->parentStorage,
5215 This->ownerPropertyIndex,
5219 * We start freeing the chain at the head block.
5221 extraBlock = blockIndex;
5225 /* Get the next block before marking the new end */
5226 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5230 /* Mark the new end of chain */
5231 SmallBlockChainStream_SetNextBlockInChain(
5234 BLOCK_END_OF_CHAIN);
5238 * Mark the extra blocks as free
5240 while (extraBlock != BLOCK_END_OF_CHAIN)
5242 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5245 SmallBlockChainStream_FreeBlock(This, extraBlock);
5246 extraBlock = blockIndex;
5252 /******************************************************************************
5253 * SmallBlockChainStream_Enlarge
5255 * Grows this chain in the small block depot.
5257 BOOL SmallBlockChainStream_Enlarge(
5258 SmallBlockChainStream* This,
5259 ULARGE_INTEGER newSize)
5261 ULONG blockIndex, currentBlock;
5263 ULONG oldNumBlocks = 0;
5265 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5270 if (blockIndex == BLOCK_END_OF_CHAIN)
5273 StgProperty chainProp;
5275 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5278 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5280 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5283 blockIndex = chainProp.startingBlock;
5284 SmallBlockChainStream_SetNextBlockInChain(
5287 BLOCK_END_OF_CHAIN);
5290 currentBlock = blockIndex;
5293 * Figure out how many blocks are needed to contain this stream
5295 newNumBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5297 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5301 * Go to the current end of chain
5303 while (blockIndex != BLOCK_END_OF_CHAIN)
5306 currentBlock = blockIndex;
5307 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5312 * Add new blocks to the chain
5314 while (oldNumBlocks < newNumBlocks)
5316 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5317 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5319 SmallBlockChainStream_SetNextBlockInChain(
5322 BLOCK_END_OF_CHAIN);
5324 currentBlock = blockIndex;
5331 /******************************************************************************
5332 * SmallBlockChainStream_GetCount
5334 * Returns the number of blocks that comprises this chain.
5335 * This is not the size of this chain as the last block may not be full!
5337 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5342 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5344 while (blockIndex != BLOCK_END_OF_CHAIN)
5348 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5355 /******************************************************************************
5356 * SmallBlockChainStream_SetSize
5358 * Sets the size of this stream.
5359 * The file will grow if we grow the chain.
5361 * TODO: Free the actual blocks in the file when we shrink the chain.
5362 * Currently, the blocks are still in the file. So the file size
5363 * doesn't shrink even if we shrink streams.
5365 BOOL SmallBlockChainStream_SetSize(
5366 SmallBlockChainStream* This,
5367 ULARGE_INTEGER newSize)
5369 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5371 if (newSize.s.LowPart == size.s.LowPart)
5374 if (newSize.s.LowPart < size.s.LowPart)
5376 SmallBlockChainStream_Shrink(This, newSize);
5380 SmallBlockChainStream_Enlarge(This, newSize);
5386 /******************************************************************************
5387 * SmallBlockChainStream_GetSize
5389 * Returns the size of this chain.
5391 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5393 StgProperty chainProperty;
5395 StorageImpl_ReadProperty(
5396 This->parentStorage,
5397 This->ownerPropertyIndex,
5400 return chainProperty.size;
5403 /******************************************************************************
5404 * StgCreateDocfile [OLE32.144]
5406 HRESULT WINAPI StgCreateDocfile(
5410 IStorage **ppstgOpen)
5412 StorageImpl* newStorage = 0;
5413 HANDLE hFile = INVALID_HANDLE_VALUE;
5418 DWORD fileAttributes;
5419 WCHAR tempFileName[MAX_PATH];
5421 TRACE("(%s, %lx, %ld, %p)\n",
5422 debugstr_w(pwcsName), grfMode,
5423 reserved, ppstgOpen);
5426 * Validate the parameters
5429 return STG_E_INVALIDPOINTER;
5432 * Validate the STGM flags
5434 if ( FAILED( validateSTGM(grfMode) ))
5435 return STG_E_INVALIDFLAG;
5438 * Generate a unique name.
5442 WCHAR tempPath[MAX_PATH];
5443 WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5445 if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5446 return STG_E_INVALIDFLAG;
5447 if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5448 return STG_E_INVALIDFLAG;
5450 memset(tempPath, 0, sizeof(tempPath));
5451 memset(tempFileName, 0, sizeof(tempFileName));
5453 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5456 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5457 pwcsName = tempFileName;
5459 return STG_E_INSUFFICIENTMEMORY;
5461 creationMode = TRUNCATE_EXISTING;
5465 creationMode = GetCreationModeFromSTGM(grfMode);
5469 * Interpret the STGM value grfMode
5471 shareMode = GetShareModeFromSTGM(grfMode);
5472 accessMode = GetAccessModeFromSTGM(grfMode);
5474 if (grfMode & STGM_DELETEONRELEASE)
5475 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5477 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5479 if (grfMode & STGM_TRANSACTED)
5480 FIXME("Transacted mode not implemented.\n");
5483 * Initialize the "out" parameter.
5487 hFile = CreateFileW(pwcsName,
5495 if (hFile == INVALID_HANDLE_VALUE)
5501 * Allocate and initialize the new IStorage32object.
5503 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5505 if (newStorage == 0)
5506 return STG_E_INSUFFICIENTMEMORY;
5508 hr = StorageImpl_Construct(
5519 HeapFree(GetProcessHeap(), 0, newStorage);
5524 * Get an "out" pointer for the caller.
5526 hr = StorageBaseImpl_QueryInterface(
5527 (IStorage*)newStorage,
5528 (REFIID)&IID_IStorage,
5534 /******************************************************************************
5535 * StgOpenStorage [OLE32.148]
5537 HRESULT WINAPI StgOpenStorage(
5538 const OLECHAR *pwcsName,
5539 IStorage *pstgPriority,
5543 IStorage **ppstgOpen)
5545 StorageImpl* newStorage = 0;
5550 WCHAR fullname[MAX_PATH];
5553 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5554 debugstr_w(pwcsName), pstgPriority, grfMode,
5555 snbExclude, reserved, ppstgOpen);
5558 * Perform a sanity check
5560 if (( pwcsName == 0) || (ppstgOpen == 0) )
5562 hr = STG_E_INVALIDPOINTER;
5567 * Validate the STGM flags
5569 if ( FAILED( validateSTGM(grfMode) ))
5571 hr = STG_E_INVALIDFLAG;
5576 * Interpret the STGM value grfMode
5578 shareMode = GetShareModeFromSTGM(grfMode);
5579 accessMode = GetAccessModeFromSTGM(grfMode);
5582 * Initialize the "out" parameter.
5586 hFile = CreateFileW( pwcsName,
5591 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5594 length = GetFileSize(hFile, NULL);
5596 if (hFile==INVALID_HANDLE_VALUE)
5598 DWORD last_error = GetLastError();
5604 case ERROR_FILE_NOT_FOUND:
5605 hr = STG_E_FILENOTFOUND;
5608 case ERROR_PATH_NOT_FOUND:
5609 hr = STG_E_PATHNOTFOUND;
5612 case ERROR_ACCESS_DENIED:
5613 case ERROR_WRITE_PROTECT:
5614 hr = STG_E_ACCESSDENIED;
5617 case ERROR_SHARING_VIOLATION:
5618 hr = STG_E_SHAREVIOLATION;
5629 * Allocate and initialize the new IStorage32object.
5631 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5633 if (newStorage == 0)
5635 hr = STG_E_INSUFFICIENTMEMORY;
5639 /* if the file's length was zero, initialize the storage */
5640 hr = StorageImpl_Construct(
5651 HeapFree(GetProcessHeap(), 0, newStorage);
5653 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5655 if(hr == STG_E_INVALIDHEADER)
5656 hr = STG_E_FILEALREADYEXISTS;
5660 /* prepare the file name string given in lieu of the root property name */
5661 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5662 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5663 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5666 * Get an "out" pointer for the caller.
5668 hr = StorageBaseImpl_QueryInterface(
5669 (IStorage*)newStorage,
5670 (REFIID)&IID_IStorage,
5674 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5678 /******************************************************************************
5679 * StgCreateDocfileOnILockBytes [OLE32.145]
5681 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5685 IStorage** ppstgOpen)
5687 StorageImpl* newStorage = 0;
5691 * Validate the parameters
5693 if ((ppstgOpen == 0) || (plkbyt == 0))
5694 return STG_E_INVALIDPOINTER;
5697 * Allocate and initialize the new IStorage object.
5699 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5701 if (newStorage == 0)
5702 return STG_E_INSUFFICIENTMEMORY;
5704 hr = StorageImpl_Construct(
5715 HeapFree(GetProcessHeap(), 0, newStorage);
5720 * Get an "out" pointer for the caller.
5722 hr = StorageBaseImpl_QueryInterface(
5723 (IStorage*)newStorage,
5724 (REFIID)&IID_IStorage,
5730 /******************************************************************************
5731 * StgOpenStorageOnILockBytes [OLE32.149]
5733 HRESULT WINAPI StgOpenStorageOnILockBytes(
5735 IStorage *pstgPriority,
5739 IStorage **ppstgOpen)
5741 StorageImpl* newStorage = 0;
5745 * Perform a sanity check
5747 if ((plkbyt == 0) || (ppstgOpen == 0))
5748 return STG_E_INVALIDPOINTER;
5751 * Validate the STGM flags
5753 if ( FAILED( validateSTGM(grfMode) ))
5754 return STG_E_INVALIDFLAG;
5757 * Initialize the "out" parameter.
5762 * Allocate and initialize the new IStorage object.
5764 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5766 if (newStorage == 0)
5767 return STG_E_INSUFFICIENTMEMORY;
5769 hr = StorageImpl_Construct(
5780 HeapFree(GetProcessHeap(), 0, newStorage);
5785 * Get an "out" pointer for the caller.
5787 hr = StorageBaseImpl_QueryInterface(
5788 (IStorage*)newStorage,
5789 (REFIID)&IID_IStorage,
5795 /******************************************************************************
5796 * StgSetTimes [ole32.150]
5797 * StgSetTimes [OLE32.150]
5801 HRESULT WINAPI StgSetTimes(OLECHAR *str, FILETIME *a, FILETIME *b, FILETIME *c )
5803 FIXME("(%s, %p, %p, %p),stub!\n", debugstr_w(str), a, b, c);
5807 /******************************************************************************
5808 * StgIsStorageILockBytes [OLE32.147]
5810 * Determines if the ILockBytes contains a storage object.
5812 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5815 ULARGE_INTEGER offset;
5817 offset.s.HighPart = 0;
5818 offset.s.LowPart = 0;
5820 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5822 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5828 /******************************************************************************
5829 * WriteClassStg [OLE32.158]
5831 * This method will store the specified CLSID in the specified storage object
5833 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5839 hRes = IStorage_SetClass(pStg, rclsid);
5844 /***********************************************************************
5845 * ReadClassStg (OLE32.134)
5847 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5849 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5859 * read a STATSTG structure (contains the clsid) from the storage
5861 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5864 *pclsid=pstatstg.clsid;
5869 /***********************************************************************
5870 * OleLoadFromStream (OLE32.113)
5872 * This function loads an object from stream
5874 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5878 LPPERSISTSTREAM xstm;
5880 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
5882 res=ReadClassStm(pStm,&clsid);
5883 if (!SUCCEEDED(res))
5885 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5886 if (!SUCCEEDED(res))
5888 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
5889 if (!SUCCEEDED(res)) {
5890 IUnknown_Release((IUnknown*)*ppvObj);
5893 res=IPersistStream_Load(xstm,pStm);
5894 IPersistStream_Release(xstm);
5895 /* FIXME: all refcounts ok at this point? I think they should be:
5898 * xstm : 0 (released)
5903 /***********************************************************************
5904 * OleSaveToStream (OLE32.125)
5906 * This function saves an object with the IPersistStream interface on it
5907 * to the specified stream.
5909 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5915 TRACE("(%p,%p)\n",pPStm,pStm);
5917 res=IPersistStream_GetClassID(pPStm,&clsid);
5919 if (SUCCEEDED(res)){
5921 res=WriteClassStm(pStm,&clsid);
5925 res=IPersistStream_Save(pPStm,pStm,TRUE);
5928 TRACE("Finished Save\n");
5932 /****************************************************************************
5933 * This method validate a STGM parameter that can contain the values below
5935 * STGM_DIRECT 0x00000000
5936 * STGM_TRANSACTED 0x00010000
5937 * STGM_SIMPLE 0x08000000
5939 * STGM_READ 0x00000000
5940 * STGM_WRITE 0x00000001
5941 * STGM_READWRITE 0x00000002
5943 * STGM_SHARE_DENY_NONE 0x00000040
5944 * STGM_SHARE_DENY_READ 0x00000030
5945 * STGM_SHARE_DENY_WRITE 0x00000020
5946 * STGM_SHARE_EXCLUSIVE 0x00000010
5948 * STGM_PRIORITY 0x00040000
5949 * STGM_DELETEONRELEASE 0x04000000
5951 * STGM_CREATE 0x00001000
5952 * STGM_CONVERT 0x00020000
5953 * STGM_FAILIFTHERE 0x00000000
5955 * STGM_NOSCRATCH 0x00100000
5956 * STGM_NOSNAPSHOT 0x00200000
5958 static HRESULT validateSTGM(DWORD stgm)
5960 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5961 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5962 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5964 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5965 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5966 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5968 BOOL bSTGM_SHARE_DENY_NONE =
5969 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5971 BOOL bSTGM_SHARE_DENY_READ =
5972 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5974 BOOL bSTGM_SHARE_DENY_WRITE =
5975 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5977 BOOL bSTGM_SHARE_EXCLUSIVE =
5978 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5980 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5981 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5983 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5984 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5987 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5989 if ( ! bSTGM_DIRECT )
5990 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5994 * STGM_WRITE | STGM_READWRITE | STGM_READ
5997 if( bSTGM_WRITE && bSTGM_READWRITE )
6001 * STGM_SHARE_DENY_NONE | others
6002 * (I assume here that DENY_READ implies DENY_WRITE)
6004 if ( bSTGM_SHARE_DENY_NONE )
6005 if ( bSTGM_SHARE_DENY_READ ||
6006 bSTGM_SHARE_DENY_WRITE ||
6007 bSTGM_SHARE_EXCLUSIVE)
6011 * STGM_CREATE | STGM_CONVERT
6012 * if both are false, STGM_FAILIFTHERE is set to TRUE
6014 if ( bSTGM_CREATE && bSTGM_CONVERT )
6018 * STGM_NOSCRATCH requires STGM_TRANSACTED
6020 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
6024 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6025 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6027 if (bSTGM_NOSNAPSHOT)
6029 if ( ! ( bSTGM_TRANSACTED &&
6030 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
6037 /****************************************************************************
6038 * GetShareModeFromSTGM
6040 * This method will return a share mode flag from a STGM value.
6041 * The STGM value is assumed valid.
6043 static DWORD GetShareModeFromSTGM(DWORD stgm)
6045 DWORD dwShareMode = 0;
6046 BOOL bSTGM_SHARE_DENY_NONE =
6047 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
6049 BOOL bSTGM_SHARE_DENY_READ =
6050 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
6052 BOOL bSTGM_SHARE_DENY_WRITE =
6053 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
6055 BOOL bSTGM_SHARE_EXCLUSIVE =
6056 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
6058 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
6061 if (bSTGM_SHARE_DENY_NONE)
6062 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6064 if (bSTGM_SHARE_DENY_WRITE)
6065 dwShareMode = FILE_SHARE_READ;
6070 /****************************************************************************
6071 * GetAccessModeFromSTGM
6073 * This method will return an access mode flag from a STGM value.
6074 * The STGM value is assumed valid.
6076 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6078 DWORD dwDesiredAccess = GENERIC_READ;
6079 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
6080 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
6081 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
6084 dwDesiredAccess = GENERIC_READ;
6087 dwDesiredAccess |= GENERIC_WRITE;
6089 if (bSTGM_READWRITE)
6090 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
6092 return dwDesiredAccess;
6095 /****************************************************************************
6096 * GetCreationModeFromSTGM
6098 * This method will return a creation mode flag from a STGM value.
6099 * The STGM value is assumed valid.
6101 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6103 if ( stgm & STGM_CREATE)
6104 return CREATE_ALWAYS;
6105 if (stgm & STGM_CONVERT) {
6106 FIXME("STGM_CONVERT not implemented!\n");
6109 /* All other cases */
6110 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
6111 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
6116 /*************************************************************************
6117 * OLECONVERT_LoadOLE10 [Internal]
6119 * Loads the OLE10 STREAM to memory
6122 * pOleStream [I] The OLESTREAM
6123 * pData [I] Data Structure for the OLESTREAM Data
6127 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6128 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6131 * This function is used by OleConvertOLESTREAMToIStorage only.
6133 * Memory allocated for pData must be freed by the caller
6135 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6138 HRESULT hRes = S_OK;
6142 pData->pData = NULL;
6143 pData->pstrOleObjFileName = (CHAR *) NULL;
6145 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6148 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6149 if(dwSize != sizeof(pData->dwOleID))
6151 hRes = CONVERT10_E_OLESTREAM_GET;
6153 else if(pData->dwOleID != OLESTREAM_ID)
6155 hRes = CONVERT10_E_OLESTREAM_FMT;
6166 /* Get the TypeID...more info needed for this field */
6167 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6168 if(dwSize != sizeof(pData->dwTypeID))
6170 hRes = CONVERT10_E_OLESTREAM_GET;
6175 if(pData->dwTypeID != 0)
6177 /* Get the lenght of the OleTypeName */
6178 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6179 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6181 hRes = CONVERT10_E_OLESTREAM_GET;
6186 if(pData->dwOleTypeNameLength > 0)
6188 /* Get the OleTypeName */
6189 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6190 if(dwSize != pData->dwOleTypeNameLength)
6192 hRes = CONVERT10_E_OLESTREAM_GET;
6198 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6199 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6201 hRes = CONVERT10_E_OLESTREAM_GET;
6205 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6206 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6207 pData->pstrOleObjFileName = (CHAR *)malloc(pData->dwOleObjFileNameLength);
6208 if(pData->pstrOleObjFileName)
6210 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6211 if(dwSize != pData->dwOleObjFileNameLength)
6213 hRes = CONVERT10_E_OLESTREAM_GET;
6217 hRes = CONVERT10_E_OLESTREAM_GET;
6222 /* Get the Width of the Metafile */
6223 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6224 if(dwSize != sizeof(pData->dwMetaFileWidth))
6226 hRes = CONVERT10_E_OLESTREAM_GET;
6230 /* Get the Height of the Metafile */
6231 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6232 if(dwSize != sizeof(pData->dwMetaFileHeight))
6234 hRes = CONVERT10_E_OLESTREAM_GET;
6240 /* Get the Lenght of the Data */
6241 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6242 if(dwSize != sizeof(pData->dwDataLength))
6244 hRes = CONVERT10_E_OLESTREAM_GET;
6248 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6250 if(!bStrem1) /* if it is a second OLE stream data */
6252 pData->dwDataLength -= 8;
6253 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6254 if(dwSize != sizeof(pData->strUnknown))
6256 hRes = CONVERT10_E_OLESTREAM_GET;
6262 if(pData->dwDataLength > 0)
6264 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6266 /* Get Data (ex. IStorage, Metafile, or BMP) */
6269 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6270 if(dwSize != pData->dwDataLength)
6272 hRes = CONVERT10_E_OLESTREAM_GET;
6277 hRes = CONVERT10_E_OLESTREAM_GET;
6286 /*************************************************************************
6287 * OLECONVERT_SaveOLE10 [Internal]
6289 * Saves the OLE10 STREAM From memory
6292 * pData [I] Data Structure for the OLESTREAM Data
6293 * pOleStream [I] The OLESTREAM to save
6297 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6300 * This function is used by OleConvertIStorageToOLESTREAM only.
6303 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6306 HRESULT hRes = S_OK;
6310 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6311 if(dwSize != sizeof(pData->dwOleID))
6313 hRes = CONVERT10_E_OLESTREAM_PUT;
6318 /* Set the TypeID */
6319 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6320 if(dwSize != sizeof(pData->dwTypeID))
6322 hRes = CONVERT10_E_OLESTREAM_PUT;
6326 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6328 /* Set the Lenght of the OleTypeName */
6329 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6330 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6332 hRes = CONVERT10_E_OLESTREAM_PUT;
6337 if(pData->dwOleTypeNameLength > 0)
6339 /* Set the OleTypeName */
6340 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6341 if(dwSize != pData->dwOleTypeNameLength)
6343 hRes = CONVERT10_E_OLESTREAM_PUT;
6350 /* Set the width of the Metafile */
6351 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6352 if(dwSize != sizeof(pData->dwMetaFileWidth))
6354 hRes = CONVERT10_E_OLESTREAM_PUT;
6360 /* Set the height of the Metafile */
6361 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6362 if(dwSize != sizeof(pData->dwMetaFileHeight))
6364 hRes = CONVERT10_E_OLESTREAM_PUT;
6370 /* Set the lenght of the Data */
6371 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6372 if(dwSize != sizeof(pData->dwDataLength))
6374 hRes = CONVERT10_E_OLESTREAM_PUT;
6380 if(pData->dwDataLength > 0)
6382 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6383 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6384 if(dwSize != pData->dwDataLength)
6386 hRes = CONVERT10_E_OLESTREAM_PUT;
6394 /*************************************************************************
6395 * OLECONVERT_GetOLE20FromOLE10[Internal]
6397 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6398 * opens it, and copies the content to the dest IStorage for
6399 * OleConvertOLESTREAMToIStorage
6403 * pDestStorage [I] The IStorage to copy the data to
6404 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6405 * nBufferLength [I] The size of the buffer
6414 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6418 IStorage *pTempStorage;
6419 DWORD dwNumOfBytesWritten;
6420 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6421 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6423 /* Create a temp File */
6424 GetTempPathW(MAX_PATH, wstrTempDir);
6425 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6426 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6428 if(hFile != INVALID_HANDLE_VALUE)
6430 /* Write IStorage Data to File */
6431 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6434 /* Open and copy temp storage to the Dest Storage */
6435 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6438 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6439 StorageBaseImpl_Release(pTempStorage);
6441 DeleteFileW(wstrTempFile);
6446 /*************************************************************************
6447 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6449 * Saves the OLE10 STREAM From memory
6452 * pStorage [I] The Src IStorage to copy
6453 * pData [I] The Dest Memory to write to.
6456 * The size in bytes allocated for pData
6459 * Memory allocated for pData must be freed by the caller
6461 * Used by OleConvertIStorageToOLESTREAM only.
6464 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6468 DWORD nDataLength = 0;
6469 IStorage *pTempStorage;
6470 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6471 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6475 /* Create temp Storage */
6476 GetTempPathW(MAX_PATH, wstrTempDir);
6477 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6478 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6482 /* Copy Src Storage to the Temp Storage */
6483 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6484 StorageBaseImpl_Release(pTempStorage);
6486 /* Open Temp Storage as a file and copy to memory */
6487 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6488 if(hFile != INVALID_HANDLE_VALUE)
6490 nDataLength = GetFileSize(hFile, NULL);
6491 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6492 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6495 DeleteFileW(wstrTempFile);
6500 /*************************************************************************
6501 * OLECONVERT_CreateOleStream [Internal]
6503 * Creates the "\001OLE" stream in the IStorage if neccessary.
6506 * pStorage [I] Dest storage to create the stream in
6512 * This function is used by OleConvertOLESTREAMToIStorage only.
6514 * This stream is still unknown, MS Word seems to have extra data
6515 * but since the data is stored in the OLESTREAM there should be
6516 * no need to recreate the stream. If the stream is manually
6517 * deleted it will create it with this default data.
6520 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6524 WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6525 BYTE pOleStreamHeader [] =
6527 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6528 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6529 0x00, 0x00, 0x00, 0x00
6532 /* Create stream if not present */
6533 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6534 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6538 /* Write default Data */
6539 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6540 IStream_Release(pStream);
6545 /*************************************************************************
6546 * OLECONVERT_CreateCompObjStream [Internal]
6548 * Creates a "\001CompObj" is the destination IStorage if necessary.
6551 * pStorage [I] The dest IStorage to create the CompObj Stream
6553 * strOleTypeName [I] The ProgID
6557 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6560 * This function is used by OleConvertOLESTREAMToIStorage only.
6562 * The stream data is stored in the OLESTREAM and there should be
6563 * no need to recreate the stream. If the stream is manually
6564 * deleted it will attempt to create it by querying the registry.
6568 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6571 HRESULT hStorageRes, hRes = S_OK;
6572 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6573 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6575 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6576 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6578 /* Initialize the CompObj structure */
6579 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6580 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6581 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6584 /* Create a CompObj stream if it doesn't exist */
6585 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6586 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6587 if(hStorageRes == S_OK)
6589 /* copy the OleTypeName to the compobj struct */
6590 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6591 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6593 /* copy the OleTypeName to the compobj struct */
6594 /* Note: in the test made, these were Identical */
6595 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6596 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6599 hRes = CLSIDFromProgID16(IStorageCompObj.strProgIDName, &(IStorageCompObj.clsid));
6605 /* Get the CLSID Default Name from the Registry */
6606 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6607 if(hErr == ERROR_SUCCESS)
6609 char strTemp[OLESTREAM_MAX_STR_LEN];
6610 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6611 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6612 if(hErr == ERROR_SUCCESS)
6614 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6620 /* Write CompObj Structure to stream */
6621 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6623 WriteClassStm(pStream,&(IStorageCompObj.clsid));
6625 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6626 if(IStorageCompObj.dwCLSIDNameLength > 0)
6628 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6630 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6631 if(IStorageCompObj.dwOleTypeNameLength > 0)
6633 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6635 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6636 if(IStorageCompObj.dwProgIDNameLength > 0)
6638 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6640 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6641 IStream_Release(pStream);
6647 /*************************************************************************
6648 * OLECONVERT_CreateOlePresStream[Internal]
6650 * Creates the "\002OlePres000" Stream with the Metafile data
6653 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6654 * dwExtentX [I] Width of the Metafile
6655 * dwExtentY [I] Height of the Metafile
6656 * pData [I] Metafile data
6657 * dwDataLength [I] Size of the Metafile data
6661 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6664 * This function is used by OleConvertOLESTREAMToIStorage only.
6667 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6671 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6672 BYTE pOlePresStreamHeader [] =
6674 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6675 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6676 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6677 0x00, 0x00, 0x00, 0x00
6680 BYTE pOlePresStreamHeaderEmpty [] =
6682 0x00, 0x00, 0x00, 0x00,
6683 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6684 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6685 0x00, 0x00, 0x00, 0x00
6688 /* Create the OlePres000 Stream */
6689 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6690 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6695 OLECONVERT_ISTORAGE_OLEPRES OlePres;
6697 memset(&OlePres, 0, sizeof(OlePres));
6698 /* Do we have any metafile data to save */
6699 if(dwDataLength > 0)
6701 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6702 nHeaderSize = sizeof(pOlePresStreamHeader);
6706 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
6707 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
6709 /* Set width and height of the metafile */
6710 OlePres.dwExtentX = dwExtentX;
6711 OlePres.dwExtentY = -dwExtentY;
6713 /* Set Data and Lenght */
6714 if(dwDataLength > sizeof(METAFILEPICT16))
6716 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
6717 OlePres.pData = &(pData[8]);
6719 /* Save OlePres000 Data to Stream */
6720 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
6721 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
6722 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
6723 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
6724 if(OlePres.dwSize > 0)
6726 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
6728 IStream_Release(pStream);
6732 /*************************************************************************
6733 * OLECONVERT_CreateOle10NativeStream [Internal]
6735 * Creates the "\001Ole10Native" Stream (should contain a BMP)
6738 * pStorage [I] Dest storage to create the stream in
6739 * pData [I] Ole10 Native Data (ex. bmp)
6740 * dwDataLength [I] Size of the Ole10 Native Data
6746 * This function is used by OleConvertOLESTREAMToIStorage only.
6748 * Might need to verify the data and return appropriate error message
6751 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
6755 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6757 /* Create the Ole10Native Stream */
6758 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6759 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6763 /* Write info to stream */
6764 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
6765 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
6766 IStream_Release(pStream);
6771 /*************************************************************************
6772 * OLECONVERT_GetOLE10ProgID [Internal]
6774 * Finds the ProgID (or OleTypeID) from the IStorage
6777 * pStorage [I] The Src IStorage to get the ProgID
6778 * strProgID [I] the ProgID string to get
6779 * dwSize [I] the size of the string
6783 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6786 * This function is used by OleConvertIStorageToOLESTREAM only.
6790 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
6794 LARGE_INTEGER iSeekPos;
6795 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
6796 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6798 /* Open the CompObj Stream */
6799 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6800 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6804 /*Get the OleType from the CompObj Stream */
6805 iSeekPos.s.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
6806 iSeekPos.s.HighPart = 0;
6808 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6809 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
6810 iSeekPos.s.LowPart = CompObj.dwCLSIDNameLength;
6811 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6812 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
6813 iSeekPos.s.LowPart = CompObj.dwOleTypeNameLength;
6814 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6816 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
6819 IStream_Read(pStream, strProgID, *dwSize, NULL);
6821 IStream_Release(pStream);
6826 LPOLESTR wstrProgID;
6828 /* Get the OleType from the registry */
6829 REFCLSID clsid = &(stat.clsid);
6830 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
6831 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
6834 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
6841 /*************************************************************************
6842 * OLECONVERT_GetOle10PresData [Internal]
6844 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
6847 * pStorage [I] Src IStroage
6848 * pOleStream [I] Dest OleStream Mem Struct
6854 * This function is used by OleConvertIStorageToOLESTREAM only.
6856 * Memory allocated for pData must be freed by the caller
6860 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6865 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6867 /* Initialize Default data for OLESTREAM */
6868 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6869 pOleStreamData[0].dwTypeID = 2;
6870 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6871 pOleStreamData[1].dwTypeID = 0;
6872 pOleStreamData[0].dwMetaFileWidth = 0;
6873 pOleStreamData[0].dwMetaFileHeight = 0;
6874 pOleStreamData[0].pData = NULL;
6875 pOleStreamData[1].pData = NULL;
6877 /* Open Ole10Native Stream */
6878 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6879 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6883 /* Read Size and Data */
6884 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
6885 if(pOleStreamData->dwDataLength > 0)
6887 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
6888 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
6890 IStream_Release(pStream);
6896 /*************************************************************************
6897 * OLECONVERT_GetOle20PresData[Internal]
6899 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
6902 * pStorage [I] Src IStroage
6903 * pOleStreamData [I] Dest OleStream Mem Struct
6909 * This function is used by OleConvertIStorageToOLESTREAM only.
6911 * Memory allocated for pData must be freed by the caller
6913 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6917 OLECONVERT_ISTORAGE_OLEPRES olePress;
6918 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6920 /* Initialize Default data for OLESTREAM */
6921 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6922 pOleStreamData[0].dwTypeID = 2;
6923 pOleStreamData[0].dwMetaFileWidth = 0;
6924 pOleStreamData[0].dwMetaFileHeight = 0;
6925 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
6926 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6927 pOleStreamData[1].dwTypeID = 0;
6928 pOleStreamData[1].dwOleTypeNameLength = 0;
6929 pOleStreamData[1].strOleTypeName[0] = 0;
6930 pOleStreamData[1].dwMetaFileWidth = 0;
6931 pOleStreamData[1].dwMetaFileHeight = 0;
6932 pOleStreamData[1].pData = NULL;
6933 pOleStreamData[1].dwDataLength = 0;
6936 /* Open OlePress000 stream */
6937 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6938 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6941 LARGE_INTEGER iSeekPos;
6942 METAFILEPICT16 MetaFilePict;
6943 char strMetafilePictName[] = "METAFILEPICT";
6945 /* Set the TypeID for a Metafile */
6946 pOleStreamData[1].dwTypeID = 5;
6948 /* Set the OleTypeName to Metafile */
6949 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
6950 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
6952 iSeekPos.s.HighPart = 0;
6953 iSeekPos.s.LowPart = sizeof(olePress.byUnknown1);
6955 /* Get Presentation Data */
6956 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6957 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
6958 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
6959 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
6961 /*Set width and Height */
6962 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
6963 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
6964 if(olePress.dwSize > 0)
6967 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
6969 /* Set MetaFilePict struct */
6970 MetaFilePict.mm = 8;
6971 MetaFilePict.xExt = olePress.dwExtentX;
6972 MetaFilePict.yExt = olePress.dwExtentY;
6973 MetaFilePict.hMF = 0;
6975 /* Get Metafile Data */
6976 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
6977 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
6978 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
6980 IStream_Release(pStream);
6984 /*************************************************************************
6985 * OleConvertOLESTREAMToIStorage [OLE32.87]
6990 * DVTARGETDEVICE paramenter is not handled
6991 * Still unsure of some mem fields for OLE 10 Stream
6992 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6993 * and "\001OLE" streams
6996 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
6997 LPOLESTREAM pOleStream,
6999 const DVTARGETDEVICE* ptd)
7003 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7005 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7009 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7012 if(pstg == NULL || pOleStream == NULL)
7014 hRes = E_INVALIDARG;
7019 /* Load the OLESTREAM to Memory */
7020 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7025 /* Load the OLESTREAM to Memory (part 2)*/
7026 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7032 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7034 /* Do we have the IStorage Data in the OLESTREAM */
7035 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7037 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7038 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7042 /* It must be an original OLE 1.0 source */
7043 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7048 /* It must be an original OLE 1.0 source */
7049 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7052 /* Create CompObj Stream if necessary */
7053 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7056 /*Create the Ole Stream if necessary */
7057 OLECONVERT_CreateOleStream(pstg);
7062 /* Free allocated memory */
7063 for(i=0; i < 2; i++)
7065 if(pOleStreamData[i].pData != NULL)
7067 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7069 if(pOleStreamData[i].pstrOleObjFileName != NULL)
7071 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7072 pOleStreamData[i].pstrOleObjFileName = NULL;
7078 /*************************************************************************
7079 * OleConvertIStorageToOLESTREAM [OLE32.85]
7086 * Still unsure of some mem fields for OLE 10 Stream
7087 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7088 * and "\001OLE" streams.
7091 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7093 LPOLESTREAM pOleStream)
7096 HRESULT hRes = S_OK;
7098 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7099 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7102 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7104 if(pstg == NULL || pOleStream == NULL)
7106 hRes = E_INVALIDARG;
7110 /* Get the ProgID */
7111 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7112 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7116 /* Was it originally Ole10 */
7117 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7120 IStream_Release(pStream);
7121 /* Get Presentation Data for Ole10Native */
7122 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7126 /* Get Presentation Data (OLE20) */
7127 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7130 /* Save OLESTREAM */
7131 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7134 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7139 /* Free allocated memory */
7140 for(i=0; i < 2; i++)
7142 if(pOleStreamData[i].pData != NULL)
7144 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7151 /***********************************************************************
7152 * GetConvertStg (OLE32.68)
7154 HRESULT WINAPI GetConvertStg(LPGUID guid) {
7155 FIXME("(%s), unimplemented stub!\n",debugstr_guid(guid));
7159 /******************************************************************************
7160 * StgIsStorageFile [OLE32.146]
7163 StgIsStorageFile(LPCOLESTR fn)
7169 TRACE("(\'%s\')\n", debugstr_w(fn));
7170 hf = CreateFileW(fn, GENERIC_READ,
7171 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7172 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7174 if (hf == INVALID_HANDLE_VALUE)
7175 return STG_E_FILENOTFOUND;
7177 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7179 WARN(" unable to read file\n");
7186 if (bytes_read != 8) {
7187 WARN(" too short\n");
7191 if (!memcmp(magic,STORAGE_magic,8)) {
7196 WARN(" -> Invalid header.\n");