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
18 #include "winbase.h" /* for lstrlenW() and the likes */
20 #include "wine/unicode.h"
21 #include "debugtools.h"
23 #include "storage32.h"
24 #include "ole2.h" /* For Write/ReadClassStm */
27 #include "wine/wingdi16.h"
29 DEFAULT_DEBUG_CHANNEL(storage);
34 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
35 #define OLESTREAM_ID 0x501
36 #define OLESTREAM_MAX_STR_LEN 255
38 static const char rootPropertyName[] = "Root Entry";
41 /* OLESTREAM memory structure to use for Get and Put Routines */
42 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
47 DWORD dwOleTypeNameLength;
48 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
49 CHAR *pstrOleObjFileName;
50 DWORD dwOleObjFileNameLength;
51 DWORD dwMetaFileWidth;
52 DWORD dwMetaFileHeight;
53 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
56 }OLECONVERT_OLESTREAM_DATA;
58 /* CompObj Stream structure */
59 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
64 DWORD dwCLSIDNameLength;
65 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
66 DWORD dwOleTypeNameLength;
67 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
68 DWORD dwProgIDNameLength;
69 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
71 }OLECONVERT_ISTORAGE_COMPOBJ;
74 /* Ole Presention Stream structure */
75 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
83 }OLECONVERT_ISTORAGE_OLEPRES;
87 /***********************************************************************
88 * Forward declaration of internal functions used by the method DestroyElement
90 static HRESULT deleteStorageProperty(
91 StorageImpl *parentStorage,
92 ULONG foundPropertyIndexToDelete,
93 StgProperty propertyToDelete);
95 static HRESULT deleteStreamProperty(
96 StorageImpl *parentStorage,
97 ULONG foundPropertyIndexToDelete,
98 StgProperty propertyToDelete);
100 static HRESULT findPlaceholder(
101 StorageImpl *storage,
102 ULONG propertyIndexToStore,
103 ULONG storagePropertyIndex,
106 static HRESULT adjustPropertyChain(
108 StgProperty propertyToDelete,
109 StgProperty parentProperty,
110 ULONG parentPropertyId,
113 /***********************************************************************
114 * Declaration of the functions used to manipulate StgProperty
117 static ULONG getFreeProperty(
118 StorageImpl *storage);
120 static void updatePropertyChain(
121 StorageImpl *storage,
122 ULONG newPropertyIndex,
123 StgProperty newProperty);
125 static LONG propertyNameCmp(
126 OLECHAR *newProperty,
127 OLECHAR *currentProperty);
130 /***********************************************************************
131 * Declaration of miscellaneous functions...
133 static HRESULT validateSTGM(DWORD stgmValue);
135 static DWORD GetShareModeFromSTGM(DWORD stgm);
136 static DWORD GetAccessModeFromSTGM(DWORD stgm);
137 static DWORD GetCreationModeFromSTGM(DWORD stgm);
140 * Virtual function table for the IStorage32Impl class.
142 static ICOM_VTABLE(IStorage) Storage32Impl_Vtbl =
144 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
145 StorageBaseImpl_QueryInterface,
146 StorageBaseImpl_AddRef,
147 StorageBaseImpl_Release,
148 StorageBaseImpl_CreateStream,
149 StorageBaseImpl_OpenStream,
150 StorageImpl_CreateStorage,
151 StorageBaseImpl_OpenStorage,
153 StorageImpl_MoveElementTo,
156 StorageBaseImpl_EnumElements,
157 StorageImpl_DestroyElement,
158 StorageBaseImpl_RenameElement,
159 StorageImpl_SetElementTimes,
160 StorageBaseImpl_SetClass,
161 StorageImpl_SetStateBits,
166 * Virtual function table for the Storage32InternalImpl class.
168 static ICOM_VTABLE(IStorage) Storage32InternalImpl_Vtbl =
170 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
171 StorageBaseImpl_QueryInterface,
172 StorageBaseImpl_AddRef,
173 StorageBaseImpl_Release,
174 StorageBaseImpl_CreateStream,
175 StorageBaseImpl_OpenStream,
176 StorageImpl_CreateStorage,
177 StorageBaseImpl_OpenStorage,
179 StorageImpl_MoveElementTo,
180 StorageInternalImpl_Commit,
181 StorageInternalImpl_Revert,
182 StorageBaseImpl_EnumElements,
183 StorageImpl_DestroyElement,
184 StorageBaseImpl_RenameElement,
185 StorageImpl_SetElementTimes,
186 StorageBaseImpl_SetClass,
187 StorageImpl_SetStateBits,
192 * Virtual function table for the IEnumSTATSTGImpl class.
194 static ICOM_VTABLE(IEnumSTATSTG) IEnumSTATSTGImpl_Vtbl =
196 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
197 IEnumSTATSTGImpl_QueryInterface,
198 IEnumSTATSTGImpl_AddRef,
199 IEnumSTATSTGImpl_Release,
200 IEnumSTATSTGImpl_Next,
201 IEnumSTATSTGImpl_Skip,
202 IEnumSTATSTGImpl_Reset,
203 IEnumSTATSTGImpl_Clone
210 /************************************************************************
211 ** Storage32BaseImpl implementatiion
214 /************************************************************************
215 * Storage32BaseImpl_QueryInterface (IUnknown)
217 * This method implements the common QueryInterface for all IStorage32
218 * implementations contained in this file.
220 * See Windows documentation for more details on IUnknown methods.
222 HRESULT WINAPI StorageBaseImpl_QueryInterface(
227 ICOM_THIS(StorageBaseImpl,iface);
229 * Perform a sanity check on the parameters.
231 if ( (This==0) || (ppvObject==0) )
235 * Initialize the return parameter.
240 * Compare the riid with the interface IDs implemented by this object.
242 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
244 *ppvObject = (IStorage*)This;
246 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
248 *ppvObject = (IStorage*)This;
252 * Check that we obtained an interface.
255 return E_NOINTERFACE;
258 * Query Interface always increases the reference count by one when it is
261 StorageBaseImpl_AddRef(iface);
266 /************************************************************************
267 * Storage32BaseImpl_AddRef (IUnknown)
269 * This method implements the common AddRef for all IStorage32
270 * implementations contained in this file.
272 * See Windows documentation for more details on IUnknown methods.
274 ULONG WINAPI StorageBaseImpl_AddRef(
277 ICOM_THIS(StorageBaseImpl,iface);
283 /************************************************************************
284 * Storage32BaseImpl_Release (IUnknown)
286 * This method implements the common Release for all IStorage32
287 * implementations contained in this file.
289 * See Windows documentation for more details on IUnknown methods.
291 ULONG WINAPI StorageBaseImpl_Release(
294 ICOM_THIS(StorageBaseImpl,iface);
296 * Decrease the reference count on this object.
301 * If the reference count goes down to 0, perform suicide.
306 * Since we are using a system of base-classes, we want to call the
307 * destructor of the appropriate derived class. To do this, we are
308 * using virtual functions to implement the destructor.
310 This->v_destructor(This);
318 /************************************************************************
319 * Storage32BaseImpl_OpenStream (IStorage)
321 * This method will open the specified stream object from the current storage.
323 * See Windows documentation for more details on IStorage methods.
325 HRESULT WINAPI StorageBaseImpl_OpenStream(
327 const OLECHAR* pwcsName, /* [string][in] */
328 void* reserved1, /* [unique][in] */
329 DWORD grfMode, /* [in] */
330 DWORD reserved2, /* [in] */
331 IStream** ppstm) /* [out] */
333 ICOM_THIS(StorageBaseImpl,iface);
334 IEnumSTATSTGImpl* propertyEnumeration;
335 StgStreamImpl* newStream;
336 StgProperty currentProperty;
337 ULONG foundPropertyIndex;
338 HRESULT res = STG_E_UNKNOWN;
340 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
341 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
344 * Perform a sanity check on the parameters.
346 if ( (pwcsName==NULL) || (ppstm==0) )
353 * Initialize the out parameter
358 * Validate the STGM flags
360 if ( FAILED( validateSTGM(grfMode) ))
362 res = STG_E_INVALIDFLAG;
369 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
370 (grfMode & STGM_DELETEONRELEASE) ||
371 (grfMode & STGM_TRANSACTED) )
373 res = STG_E_INVALIDFUNCTION;
378 * Create a property enumeration to search the properties
380 propertyEnumeration = IEnumSTATSTGImpl_Construct(
381 This->ancestorStorage,
382 This->rootPropertySetIndex);
385 * Search the enumeration for the property with the given name
387 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
393 * Delete the property enumeration since we don't need it anymore
395 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
398 * If it was found, construct the stream object and return a pointer to it.
400 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
401 (currentProperty.propertyType==PROPTYPE_STREAM) )
403 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
407 newStream->grfMode = grfMode;
408 *ppstm = (IStream*)newStream;
411 * Since we are returning a pointer to the interface, we have to
412 * nail down the reference.
414 StgStreamImpl_AddRef(*ppstm);
424 res = STG_E_FILENOTFOUND;
428 TRACE("<-- IStream %p\n", *ppstm);
429 TRACE("<-- %08lx\n", res);
433 /************************************************************************
434 * Storage32BaseImpl_OpenStorage (IStorage)
436 * This method will open a new storage object from the current storage.
438 * See Windows documentation for more details on IStorage methods.
440 HRESULT WINAPI StorageBaseImpl_OpenStorage(
442 const OLECHAR* pwcsName, /* [string][unique][in] */
443 IStorage* pstgPriority, /* [unique][in] */
444 DWORD grfMode, /* [in] */
445 SNB snbExclude, /* [unique][in] */
446 DWORD reserved, /* [in] */
447 IStorage** ppstg) /* [out] */
449 ICOM_THIS(StorageBaseImpl,iface);
450 StorageInternalImpl* newStorage;
451 IEnumSTATSTGImpl* propertyEnumeration;
452 StgProperty currentProperty;
453 ULONG foundPropertyIndex;
454 HRESULT res = STG_E_UNKNOWN;
456 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
457 iface, debugstr_w(pwcsName), pstgPriority,
458 grfMode, snbExclude, reserved, ppstg);
461 * Perform a sanity check on the parameters.
463 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
470 if (snbExclude != NULL)
472 res = STG_E_INVALIDPARAMETER;
477 * Validate the STGM flags
479 if ( FAILED( validateSTGM(grfMode) ))
481 res = STG_E_INVALIDFLAG;
488 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
489 (grfMode & STGM_DELETEONRELEASE) ||
490 (grfMode & STGM_PRIORITY) )
492 res = STG_E_INVALIDFUNCTION;
497 * Initialize the out parameter
502 * Create a property enumeration to search the properties
504 propertyEnumeration = IEnumSTATSTGImpl_Construct(
505 This->ancestorStorage,
506 This->rootPropertySetIndex);
509 * Search the enumeration for the property with the given name
511 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
517 * Delete the property enumeration since we don't need it anymore
519 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
522 * If it was found, construct the stream object and return a pointer to it.
524 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
525 (currentProperty.propertyType==PROPTYPE_STORAGE) )
528 * Construct a new Storage object
530 newStorage = StorageInternalImpl_Construct(
531 This->ancestorStorage,
536 *ppstg = (IStorage*)newStorage;
539 * Since we are returning a pointer to the interface,
540 * we have to nail down the reference.
542 StorageBaseImpl_AddRef(*ppstg);
548 res = STG_E_INSUFFICIENTMEMORY;
552 res = STG_E_FILENOTFOUND;
555 TRACE("<-- %08lx\n", res);
559 /************************************************************************
560 * Storage32BaseImpl_EnumElements (IStorage)
562 * This method will create an enumerator object that can be used to
563 * retrieve informatino about all the properties in the storage object.
565 * See Windows documentation for more details on IStorage methods.
567 HRESULT WINAPI StorageBaseImpl_EnumElements(
569 DWORD reserved1, /* [in] */
570 void* reserved2, /* [size_is][unique][in] */
571 DWORD reserved3, /* [in] */
572 IEnumSTATSTG** ppenum) /* [out] */
574 ICOM_THIS(StorageBaseImpl,iface);
575 IEnumSTATSTGImpl* newEnum;
577 TRACE("(%p, %ld, %p, %ld, %p)\n",
578 iface, reserved1, reserved2, reserved3, ppenum);
581 * Perform a sanity check on the parameters.
583 if ( (This==0) || (ppenum==0))
587 * Construct the enumerator.
589 newEnum = IEnumSTATSTGImpl_Construct(
590 This->ancestorStorage,
591 This->rootPropertySetIndex);
595 *ppenum = (IEnumSTATSTG*)newEnum;
598 * Don't forget to nail down a reference to the new object before
601 IEnumSTATSTGImpl_AddRef(*ppenum);
606 return E_OUTOFMEMORY;
609 /************************************************************************
610 * Storage32BaseImpl_Stat (IStorage)
612 * This method will retrieve information about this storage object.
614 * See Windows documentation for more details on IStorage methods.
616 HRESULT WINAPI StorageBaseImpl_Stat(
618 STATSTG* pstatstg, /* [out] */
619 DWORD grfStatFlag) /* [in] */
621 ICOM_THIS(StorageBaseImpl,iface);
622 StgProperty curProperty;
624 HRESULT res = STG_E_UNKNOWN;
626 TRACE("(%p, %p, %lx)\n",
627 iface, pstatstg, grfStatFlag);
630 * Perform a sanity check on the parameters.
632 if ( (This==0) || (pstatstg==0))
639 * Read the information from the property.
641 readSuccessful = StorageImpl_ReadProperty(
642 This->ancestorStorage,
643 This->rootPropertySetIndex,
648 StorageUtl_CopyPropertyToSTATSTG(
662 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);
664 TRACE("<-- %08lx\n", res);
668 /************************************************************************
669 * Storage32BaseImpl_RenameElement (IStorage)
671 * This method will rename the specified element.
673 * See Windows documentation for more details on IStorage methods.
675 * Implementation notes: The method used to rename consists of creating a clone
676 * of the deleted StgProperty object setting it with the new name and to
677 * perform a DestroyElement of the old StgProperty.
679 HRESULT WINAPI StorageBaseImpl_RenameElement(
681 const OLECHAR* pwcsOldName, /* [in] */
682 const OLECHAR* pwcsNewName) /* [in] */
684 ICOM_THIS(StorageBaseImpl,iface);
685 IEnumSTATSTGImpl* propertyEnumeration;
686 StgProperty currentProperty;
687 ULONG foundPropertyIndex;
689 TRACE("(%p, %s, %s)\n",
690 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
693 * Create a property enumeration to search the properties
695 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
696 This->rootPropertySetIndex);
699 * Search the enumeration for the new property name
701 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
705 if (foundPropertyIndex != PROPERTY_NULL)
708 * There is already a property with the new name
710 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
711 return STG_E_FILEALREADYEXISTS;
714 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
717 * Search the enumeration for the old property name
719 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
724 * Delete the property enumeration since we don't need it anymore
726 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
728 if (foundPropertyIndex != PROPERTY_NULL)
730 StgProperty renamedProperty;
731 ULONG renamedPropertyIndex;
734 * Setup a new property for the renamed property
736 renamedProperty.sizeOfNameString =
737 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
739 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
740 return STG_E_INVALIDNAME;
742 strcpyW(renamedProperty.name, pwcsNewName);
744 renamedProperty.propertyType = currentProperty.propertyType;
745 renamedProperty.startingBlock = currentProperty.startingBlock;
746 renamedProperty.size.s.LowPart = currentProperty.size.s.LowPart;
747 renamedProperty.size.s.HighPart = currentProperty.size.s.HighPart;
749 renamedProperty.previousProperty = PROPERTY_NULL;
750 renamedProperty.nextProperty = PROPERTY_NULL;
753 * Bring the dirProperty link in case it is a storage and in which
754 * case the renamed storage elements don't require to be reorganized.
756 renamedProperty.dirProperty = currentProperty.dirProperty;
758 /* call CoFileTime to get the current time
759 renamedProperty.timeStampS1
760 renamedProperty.timeStampD1
761 renamedProperty.timeStampS2
762 renamedProperty.timeStampD2
763 renamedProperty.propertyUniqueID
767 * Obtain a free property in the property chain
769 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
772 * Save the new property into the new property spot
774 StorageImpl_WriteProperty(
775 This->ancestorStorage,
776 renamedPropertyIndex,
780 * Find a spot in the property chain for our newly created property.
784 renamedPropertyIndex,
788 * At this point the renamed property has been inserted in the tree,
789 * now, before to Destroy the old property we must zeroed it's dirProperty
790 * otherwise the DestroyProperty below will zap it all and we do not want
792 * Also, we fake that the old property is a storage so the DestroyProperty
793 * will not do a SetSize(0) on the stream data.
795 * This means that we need to tweek the StgProperty if it is a stream or a
798 StorageImpl_ReadProperty(This->ancestorStorage,
802 currentProperty.dirProperty = PROPERTY_NULL;
803 currentProperty.propertyType = PROPTYPE_STORAGE;
804 StorageImpl_WriteProperty(
805 This->ancestorStorage,
810 * Invoke Destroy to get rid of the ole property and automatically redo
811 * the linking of it's previous and next members...
813 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
819 * There is no property with the old name
821 return STG_E_FILENOTFOUND;
827 /************************************************************************
828 * Storage32BaseImpl_CreateStream (IStorage)
830 * This method will create a stream object within this storage
832 * See Windows documentation for more details on IStorage methods.
834 HRESULT WINAPI StorageBaseImpl_CreateStream(
836 const OLECHAR* pwcsName, /* [string][in] */
837 DWORD grfMode, /* [in] */
838 DWORD reserved1, /* [in] */
839 DWORD reserved2, /* [in] */
840 IStream** ppstm) /* [out] */
842 ICOM_THIS(StorageBaseImpl,iface);
843 IEnumSTATSTGImpl* propertyEnumeration;
844 StgStreamImpl* newStream;
845 StgProperty currentProperty, newStreamProperty;
846 ULONG foundPropertyIndex, newPropertyIndex;
848 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
849 iface, debugstr_w(pwcsName), grfMode,
850 reserved1, reserved2, ppstm);
853 * Validate parameters
856 return STG_E_INVALIDPOINTER;
859 return STG_E_INVALIDNAME;
862 * Validate the STGM flags
864 if ( FAILED( validateSTGM(grfMode) ))
865 return STG_E_INVALIDFLAG;
870 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
871 (grfMode & STGM_DELETEONRELEASE) ||
872 (grfMode & STGM_TRANSACTED) )
873 return STG_E_INVALIDFUNCTION;
876 * Initialize the out parameter
881 * Create a property enumeration to search the properties
883 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
884 This->rootPropertySetIndex);
886 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
890 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
892 if (foundPropertyIndex != PROPERTY_NULL)
895 * An element with this name already exists
897 if (grfMode & STGM_CREATE)
899 IStorage_DestroyElement(iface, pwcsName);
902 return STG_E_FILEALREADYEXISTS;
906 * memset the empty property
908 memset(&newStreamProperty, 0, sizeof(StgProperty));
910 newStreamProperty.sizeOfNameString =
911 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
913 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
914 return STG_E_INVALIDNAME;
916 strcpyW(newStreamProperty.name, pwcsName);
918 newStreamProperty.propertyType = PROPTYPE_STREAM;
919 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
920 newStreamProperty.size.s.LowPart = 0;
921 newStreamProperty.size.s.HighPart = 0;
923 newStreamProperty.previousProperty = PROPERTY_NULL;
924 newStreamProperty.nextProperty = PROPERTY_NULL;
925 newStreamProperty.dirProperty = PROPERTY_NULL;
927 /* call CoFileTime to get the current time
928 newStreamProperty.timeStampS1
929 newStreamProperty.timeStampD1
930 newStreamProperty.timeStampS2
931 newStreamProperty.timeStampD2
934 /* newStreamProperty.propertyUniqueID */
937 * Get a free property or create a new one
939 newPropertyIndex = getFreeProperty(This->ancestorStorage);
942 * Save the new property into the new property spot
944 StorageImpl_WriteProperty(
945 This->ancestorStorage,
950 * Find a spot in the property chain for our newly created property.
958 * Open the stream to return it.
960 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
964 *ppstm = (IStream*)newStream;
967 * Since we are returning a pointer to the interface, we have to nail down
970 StgStreamImpl_AddRef(*ppstm);
974 return STG_E_INSUFFICIENTMEMORY;
980 /************************************************************************
981 * Storage32BaseImpl_SetClass (IStorage)
983 * This method will write the specified CLSID in the property of this
986 * See Windows documentation for more details on IStorage methods.
988 HRESULT WINAPI StorageBaseImpl_SetClass(
990 REFCLSID clsid) /* [in] */
992 ICOM_THIS(StorageBaseImpl,iface);
993 HRESULT hRes = E_FAIL;
994 StgProperty curProperty;
997 TRACE("(%p, %p)\n", iface, clsid);
999 success = StorageImpl_ReadProperty(This->ancestorStorage,
1000 This->rootPropertySetIndex,
1004 curProperty.propertyUniqueID = *clsid;
1006 success = StorageImpl_WriteProperty(This->ancestorStorage,
1007 This->rootPropertySetIndex,
1016 /************************************************************************
1017 ** Storage32Impl implementation
1020 /************************************************************************
1021 * Storage32Impl_CreateStorage (IStorage)
1023 * This method will create the storage object within the provided storage.
1025 * See Windows documentation for more details on IStorage methods.
1027 HRESULT WINAPI StorageImpl_CreateStorage(
1029 const OLECHAR *pwcsName, /* [string][in] */
1030 DWORD grfMode, /* [in] */
1031 DWORD reserved1, /* [in] */
1032 DWORD reserved2, /* [in] */
1033 IStorage **ppstg) /* [out] */
1035 StorageImpl* const This=(StorageImpl*)iface;
1037 IEnumSTATSTGImpl *propertyEnumeration;
1038 StgProperty currentProperty;
1039 StgProperty newProperty;
1040 ULONG foundPropertyIndex;
1041 ULONG newPropertyIndex;
1044 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1045 iface, debugstr_w(pwcsName), grfMode,
1046 reserved1, reserved2, ppstg);
1049 * Validate parameters
1052 return STG_E_INVALIDPOINTER;
1055 return STG_E_INVALIDNAME;
1058 * Validate the STGM flags
1060 if ( FAILED( validateSTGM(grfMode) ) ||
1061 (grfMode & STGM_DELETEONRELEASE) )
1062 return STG_E_INVALIDFLAG;
1065 * Initialize the out parameter
1070 * Create a property enumeration and search the properties
1072 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
1073 This->rootPropertySetIndex);
1075 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1078 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1080 if (foundPropertyIndex != PROPERTY_NULL)
1083 * An element with this name already exists
1085 if (grfMode & STGM_CREATE)
1086 IStorage_DestroyElement(iface, pwcsName);
1088 return STG_E_FILEALREADYEXISTS;
1092 * memset the empty property
1094 memset(&newProperty, 0, sizeof(StgProperty));
1096 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1098 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1099 return STG_E_INVALIDNAME;
1101 strcpyW(newProperty.name, pwcsName);
1103 newProperty.propertyType = PROPTYPE_STORAGE;
1104 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1105 newProperty.size.s.LowPart = 0;
1106 newProperty.size.s.HighPart = 0;
1108 newProperty.previousProperty = PROPERTY_NULL;
1109 newProperty.nextProperty = PROPERTY_NULL;
1110 newProperty.dirProperty = PROPERTY_NULL;
1112 /* call CoFileTime to get the current time
1113 newProperty.timeStampS1
1114 newProperty.timeStampD1
1115 newProperty.timeStampS2
1116 newProperty.timeStampD2
1119 /* newStorageProperty.propertyUniqueID */
1122 * Obtain a free property in the property chain
1124 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1127 * Save the new property into the new property spot
1129 StorageImpl_WriteProperty(
1130 This->ancestorStorage,
1135 * Find a spot in the property chain for our newly created property.
1137 updatePropertyChain(
1143 * Open it to get a pointer to return.
1145 hr = IStorage_OpenStorage(
1154 if( (hr != S_OK) || (*ppstg == NULL))
1164 /***************************************************************************
1168 * Get a free property or create a new one.
1170 static ULONG getFreeProperty(
1171 StorageImpl *storage)
1173 ULONG currentPropertyIndex = 0;
1174 ULONG newPropertyIndex = PROPERTY_NULL;
1175 BOOL readSuccessful = TRUE;
1176 StgProperty currentProperty;
1181 * Start by reading the root property
1183 readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1184 currentPropertyIndex,
1188 if (currentProperty.sizeOfNameString == 0)
1191 * The property existis and is available, we found it.
1193 newPropertyIndex = currentPropertyIndex;
1199 * We exhausted the property list, we will create more space below
1201 newPropertyIndex = currentPropertyIndex;
1203 currentPropertyIndex++;
1205 } while (newPropertyIndex == PROPERTY_NULL);
1208 * grow the property chain
1210 if (! readSuccessful)
1212 StgProperty emptyProperty;
1213 ULARGE_INTEGER newSize;
1214 ULONG propertyIndex;
1215 ULONG lastProperty = 0;
1216 ULONG blockCount = 0;
1219 * obtain the new count of property blocks
1221 blockCount = BlockChainStream_GetCount(
1222 storage->ancestorStorage->rootBlockChain)+1;
1225 * initialize the size used by the property stream
1227 newSize.s.HighPart = 0;
1228 newSize.s.LowPart = storage->bigBlockSize * blockCount;
1231 * add a property block to the property chain
1233 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1236 * memset the empty property in order to initialize the unused newly
1239 memset(&emptyProperty, 0, sizeof(StgProperty));
1244 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1247 propertyIndex = newPropertyIndex;
1248 propertyIndex < lastProperty;
1251 StorageImpl_WriteProperty(
1252 storage->ancestorStorage,
1258 return newPropertyIndex;
1261 /****************************************************************************
1265 * Case insensitive comparaison of StgProperty.name by first considering
1268 * Returns <0 when newPrpoerty < currentProperty
1269 * >0 when newPrpoerty > currentProperty
1270 * 0 when newPrpoerty == currentProperty
1272 static LONG propertyNameCmp(
1273 OLECHAR *newProperty,
1274 OLECHAR *currentProperty)
1276 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1281 * We compare the string themselves only when they are of the same lenght
1283 diff = lstrcmpiW( newProperty, currentProperty);
1289 /****************************************************************************
1293 * Properly link this new element in the property chain.
1295 static void updatePropertyChain(
1296 StorageImpl *storage,
1297 ULONG newPropertyIndex,
1298 StgProperty newProperty)
1300 StgProperty currentProperty;
1303 * Read the root property
1305 StorageImpl_ReadProperty(storage->ancestorStorage,
1306 storage->rootPropertySetIndex,
1309 if (currentProperty.dirProperty != PROPERTY_NULL)
1312 * The root storage contains some element, therefore, start the research
1313 * for the appropriate location.
1316 ULONG current, next, previous, currentPropertyId;
1319 * Keep the StgProperty sequence number of the storage first property
1321 currentPropertyId = currentProperty.dirProperty;
1326 StorageImpl_ReadProperty(storage->ancestorStorage,
1327 currentProperty.dirProperty,
1330 previous = currentProperty.previousProperty;
1331 next = currentProperty.nextProperty;
1332 current = currentPropertyId;
1336 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1340 if (previous != PROPERTY_NULL)
1342 StorageImpl_ReadProperty(storage->ancestorStorage,
1349 currentProperty.previousProperty = newPropertyIndex;
1350 StorageImpl_WriteProperty(storage->ancestorStorage,
1358 if (next != PROPERTY_NULL)
1360 StorageImpl_ReadProperty(storage->ancestorStorage,
1367 currentProperty.nextProperty = newPropertyIndex;
1368 StorageImpl_WriteProperty(storage->ancestorStorage,
1377 * Trying to insert an item with the same name in the
1378 * subtree structure.
1383 previous = currentProperty.previousProperty;
1384 next = currentProperty.nextProperty;
1390 * The root storage is empty, link the new property to it's dir property
1392 currentProperty.dirProperty = newPropertyIndex;
1393 StorageImpl_WriteProperty(storage->ancestorStorage,
1394 storage->rootPropertySetIndex,
1400 /*************************************************************************
1403 HRESULT WINAPI StorageImpl_CopyTo(
1405 DWORD ciidExclude, /* [in] */
1406 const IID* rgiidExclude, /* [size_is][unique][in] */
1407 SNB snbExclude, /* [unique][in] */
1408 IStorage* pstgDest) /* [unique][in] */
1410 IEnumSTATSTG *elements = 0;
1411 STATSTG curElement, strStat;
1413 IStorage *pstgTmp, *pstgChild;
1414 IStream *pstrTmp, *pstrChild;
1416 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1417 FIXME("Exclude option not implemented\n");
1419 TRACE("(%p, %ld, %p, %p, %p)\n",
1420 iface, ciidExclude, rgiidExclude,
1421 snbExclude, pstgDest);
1424 * Perform a sanity check
1426 if ( pstgDest == 0 )
1427 return STG_E_INVALIDPOINTER;
1430 * Enumerate the elements
1432 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1440 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1441 IStorage_SetClass( pstgDest, &curElement.clsid );
1446 * Obtain the next element
1448 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1450 if ( hr == S_FALSE )
1452 hr = S_OK; /* done, every element has been copied */
1456 if (curElement.type == STGTY_STORAGE)
1459 * open child source storage
1461 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1462 STGM_READ|STGM_SHARE_EXCLUSIVE,
1463 NULL, 0, &pstgChild );
1469 * Check if destination storage is not a child of the source
1470 * storage, which will cause an infinite loop
1472 if (pstgChild == pstgDest)
1474 IEnumSTATSTG_Release(elements);
1476 return STG_E_ACCESSDENIED;
1480 * create a new storage in destination storage
1482 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1483 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1487 * if it already exist, don't create a new one use this one
1489 if (hr == STG_E_FILEALREADYEXISTS)
1491 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1492 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1493 NULL, 0, &pstgTmp );
1501 * do the copy recursively
1503 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1504 snbExclude, pstgTmp );
1506 IStorage_Release( pstgTmp );
1507 IStorage_Release( pstgChild );
1509 else if (curElement.type == STGTY_STREAM)
1512 * create a new stream in destination storage. If the stream already
1513 * exist, it will be deleted and a new one will be created.
1515 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1516 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1523 * open child stream storage
1525 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1526 STGM_READ|STGM_SHARE_EXCLUSIVE,
1533 * Get the size of the source stream
1535 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1538 * Set the size of the destination stream.
1540 IStream_SetSize(pstrTmp, strStat.cbSize);
1545 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1548 IStream_Release( pstrTmp );
1549 IStream_Release( pstrChild );
1553 WARN("unknown element type: %ld\n", curElement.type);
1556 } while (hr == S_OK);
1561 IEnumSTATSTG_Release(elements);
1566 /*************************************************************************
1567 * MoveElementTo (IStorage)
1569 HRESULT WINAPI StorageImpl_MoveElementTo(
1571 const OLECHAR *pwcsName, /* [string][in] */
1572 IStorage *pstgDest, /* [unique][in] */
1573 const OLECHAR *pwcsNewName,/* [string][in] */
1574 DWORD grfFlags) /* [in] */
1576 FIXME("not implemented!\n");
1580 /*************************************************************************
1583 HRESULT WINAPI StorageImpl_Commit(
1585 DWORD grfCommitFlags)/* [in] */
1587 FIXME("(%ld): stub!\n", grfCommitFlags);
1591 /*************************************************************************
1594 HRESULT WINAPI StorageImpl_Revert(
1597 FIXME("not implemented!\n");
1601 /*************************************************************************
1602 * DestroyElement (IStorage)
1604 * Stategy: This implementation is build this way for simplicity not for speed.
1605 * I always delete the top most element of the enumeration and adjust
1606 * the deleted element pointer all the time. This takes longer to
1607 * do but allow to reinvoke DestroyElement whenever we encounter a
1608 * storage object. The optimisation reside in the usage of another
1609 * enumeration stategy that would give all the leaves of a storage
1610 * first. (postfix order)
1612 HRESULT WINAPI StorageImpl_DestroyElement(
1614 const OLECHAR *pwcsName)/* [string][in] */
1616 StorageImpl* const This=(StorageImpl*)iface;
1618 IEnumSTATSTGImpl* propertyEnumeration;
1621 StgProperty propertyToDelete;
1622 StgProperty parentProperty;
1623 ULONG foundPropertyIndexToDelete;
1624 ULONG typeOfRelation;
1625 ULONG parentPropertyId;
1628 iface, debugstr_w(pwcsName));
1631 * Perform a sanity check on the parameters.
1634 return STG_E_INVALIDPOINTER;
1637 * Create a property enumeration to search the property with the given name
1639 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1640 This->ancestorStorage,
1641 This->rootPropertySetIndex);
1643 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1644 propertyEnumeration,
1648 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1650 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1652 return STG_E_FILENOTFOUND;
1656 * Find the parent property of the property to delete (the one that
1657 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1658 * the parent is This. Otherwise, the parent is one of it's sibling...
1662 * First, read This's StgProperty..
1664 res = StorageImpl_ReadProperty(
1665 This->ancestorStorage,
1666 This->rootPropertySetIndex,
1672 * Second, check to see if by any chance the actual storage (This) is not
1673 * the parent of the property to delete... We never know...
1675 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1678 * Set data as it would have been done in the else part...
1680 typeOfRelation = PROPERTY_RELATION_DIR;
1681 parentPropertyId = This->rootPropertySetIndex;
1686 * Create a property enumeration to search the parent properties, and
1687 * delete it once done.
1689 IEnumSTATSTGImpl* propertyEnumeration2;
1691 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1692 This->ancestorStorage,
1693 This->rootPropertySetIndex);
1695 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1696 propertyEnumeration2,
1697 foundPropertyIndexToDelete,
1701 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1704 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1706 hr = deleteStorageProperty(
1708 foundPropertyIndexToDelete,
1711 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1713 hr = deleteStreamProperty(
1715 foundPropertyIndexToDelete,
1723 * Adjust the property chain
1725 hr = adjustPropertyChain(
1736 /************************************************************************
1737 * StorageImpl_Stat (IStorage)
1739 * This method will retrieve information about this storage object.
1741 * See Windows documentation for more details on IStorage methods.
1743 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1744 STATSTG* pstatstg, /* [out] */
1745 DWORD grfStatFlag) /* [in] */
1747 StorageImpl* const This = (StorageImpl*)iface;
1748 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1750 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1752 CoTaskMemFree(pstatstg->pwcsName);
1753 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1754 strcpyW(pstatstg->pwcsName, This->pwcsName);
1762 /*********************************************************************
1766 * Perform the deletion of a complete storage node
1769 static HRESULT deleteStorageProperty(
1770 StorageImpl *parentStorage,
1771 ULONG indexOfPropertyToDelete,
1772 StgProperty propertyToDelete)
1774 IEnumSTATSTG *elements = 0;
1775 IStorage *childStorage = 0;
1776 STATSTG currentElement;
1778 HRESULT destroyHr = S_OK;
1781 * Open the storage and enumerate it
1783 hr = StorageBaseImpl_OpenStorage(
1784 (IStorage*)parentStorage,
1785 propertyToDelete.name,
1787 STGM_SHARE_EXCLUSIVE,
1798 * Enumerate the elements
1800 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1805 * Obtain the next element
1807 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1810 destroyHr = StorageImpl_DestroyElement(
1811 (IStorage*)childStorage,
1812 (OLECHAR*)currentElement.pwcsName);
1814 CoTaskMemFree(currentElement.pwcsName);
1818 * We need to Reset the enumeration every time because we delete elements
1819 * and the enumeration could be invalid
1821 IEnumSTATSTG_Reset(elements);
1823 } while ((hr == S_OK) && (destroyHr == S_OK));
1826 * Invalidate the property by zeroing it's name member.
1828 propertyToDelete.sizeOfNameString = 0;
1830 StorageImpl_WriteProperty(parentStorage->ancestorStorage,
1831 indexOfPropertyToDelete,
1834 IStorage_Release(childStorage);
1835 IEnumSTATSTG_Release(elements);
1840 /*********************************************************************
1844 * Perform the deletion of a stream node
1847 static HRESULT deleteStreamProperty(
1848 StorageImpl *parentStorage,
1849 ULONG indexOfPropertyToDelete,
1850 StgProperty propertyToDelete)
1854 ULARGE_INTEGER size;
1856 size.s.HighPart = 0;
1859 hr = StorageBaseImpl_OpenStream(
1860 (IStorage*)parentStorage,
1861 (OLECHAR*)propertyToDelete.name,
1863 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1875 hr = IStream_SetSize(pis, size);
1883 * Release the stream object.
1885 IStream_Release(pis);
1888 * Invalidate the property by zeroing it's name member.
1890 propertyToDelete.sizeOfNameString = 0;
1893 * Here we should re-read the property so we get the updated pointer
1894 * but since we are here to zap it, I don't do it...
1896 StorageImpl_WriteProperty(
1897 parentStorage->ancestorStorage,
1898 indexOfPropertyToDelete,
1904 /*********************************************************************
1908 * Finds a placeholder for the StgProperty within the Storage
1911 static HRESULT findPlaceholder(
1912 StorageImpl *storage,
1913 ULONG propertyIndexToStore,
1914 ULONG storePropertyIndex,
1917 StgProperty storeProperty;
1922 * Read the storage property
1924 res = StorageImpl_ReadProperty(
1925 storage->ancestorStorage,
1934 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1936 if (storeProperty.previousProperty != PROPERTY_NULL)
1938 return findPlaceholder(
1940 propertyIndexToStore,
1941 storeProperty.previousProperty,
1946 storeProperty.previousProperty = propertyIndexToStore;
1949 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1951 if (storeProperty.nextProperty != PROPERTY_NULL)
1953 return findPlaceholder(
1955 propertyIndexToStore,
1956 storeProperty.nextProperty,
1961 storeProperty.nextProperty = propertyIndexToStore;
1964 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1966 if (storeProperty.dirProperty != PROPERTY_NULL)
1968 return findPlaceholder(
1970 propertyIndexToStore,
1971 storeProperty.dirProperty,
1976 storeProperty.dirProperty = propertyIndexToStore;
1980 hr = StorageImpl_WriteProperty(
1981 storage->ancestorStorage,
1993 /*************************************************************************
1997 * This method takes the previous and the next property link of a property
1998 * to be deleted and find them a place in the Storage.
2000 static HRESULT adjustPropertyChain(
2002 StgProperty propertyToDelete,
2003 StgProperty parentProperty,
2004 ULONG parentPropertyId,
2007 ULONG newLinkProperty = PROPERTY_NULL;
2008 BOOL needToFindAPlaceholder = FALSE;
2009 ULONG storeNode = PROPERTY_NULL;
2010 ULONG toStoreNode = PROPERTY_NULL;
2011 INT relationType = 0;
2015 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2017 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2020 * Set the parent previous to the property to delete previous
2022 newLinkProperty = propertyToDelete.previousProperty;
2024 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2027 * We also need to find a storage for the other link, setup variables
2028 * to do this at the end...
2030 needToFindAPlaceholder = TRUE;
2031 storeNode = propertyToDelete.previousProperty;
2032 toStoreNode = propertyToDelete.nextProperty;
2033 relationType = PROPERTY_RELATION_NEXT;
2036 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2039 * Set the parent previous to the property to delete next
2041 newLinkProperty = propertyToDelete.nextProperty;
2045 * Link it for real...
2047 parentProperty.previousProperty = newLinkProperty;
2050 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2052 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2055 * Set the parent next to the property to delete next previous
2057 newLinkProperty = propertyToDelete.previousProperty;
2059 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2062 * We also need to find a storage for the other link, setup variables
2063 * to do this at the end...
2065 needToFindAPlaceholder = TRUE;
2066 storeNode = propertyToDelete.previousProperty;
2067 toStoreNode = propertyToDelete.nextProperty;
2068 relationType = PROPERTY_RELATION_NEXT;
2071 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2074 * Set the parent next to the property to delete next
2076 newLinkProperty = propertyToDelete.nextProperty;
2080 * Link it for real...
2082 parentProperty.nextProperty = newLinkProperty;
2084 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2086 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2089 * Set the parent dir to the property to delete previous
2091 newLinkProperty = propertyToDelete.previousProperty;
2093 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2096 * We also need to find a storage for the other link, setup variables
2097 * to do this at the end...
2099 needToFindAPlaceholder = TRUE;
2100 storeNode = propertyToDelete.previousProperty;
2101 toStoreNode = propertyToDelete.nextProperty;
2102 relationType = PROPERTY_RELATION_NEXT;
2105 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2108 * Set the parent dir to the property to delete next
2110 newLinkProperty = propertyToDelete.nextProperty;
2114 * Link it for real...
2116 parentProperty.dirProperty = newLinkProperty;
2120 * Write back the parent property
2122 res = StorageImpl_WriteProperty(
2123 This->ancestorStorage,
2132 * If a placeholder is required for the other link, then, find one and
2133 * get out of here...
2135 if (needToFindAPlaceholder)
2137 hr = findPlaceholder(
2148 /******************************************************************************
2149 * SetElementTimes (IStorage)
2151 HRESULT WINAPI StorageImpl_SetElementTimes(
2153 const OLECHAR *pwcsName,/* [string][in] */
2154 const FILETIME *pctime, /* [in] */
2155 const FILETIME *patime, /* [in] */
2156 const FILETIME *pmtime) /* [in] */
2158 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2162 /******************************************************************************
2163 * SetStateBits (IStorage)
2165 HRESULT WINAPI StorageImpl_SetStateBits(
2167 DWORD grfStateBits,/* [in] */
2168 DWORD grfMask) /* [in] */
2170 FIXME("not implemented!\n");
2174 HRESULT StorageImpl_Construct(
2184 StgProperty currentProperty;
2185 BOOL readSuccessful;
2186 ULONG currentPropertyIndex;
2188 if ( FAILED( validateSTGM(openFlags) ))
2189 return STG_E_INVALIDFLAG;
2191 memset(This, 0, sizeof(StorageImpl));
2194 * Initialize the virtual function table.
2196 ICOM_VTBL(This) = &Storage32Impl_Vtbl;
2197 This->v_destructor = &StorageImpl_Destroy;
2200 * This is the top-level storage so initialize the ancestor pointer
2203 This->ancestorStorage = This;
2206 * Initialize the physical support of the storage.
2208 This->hFile = hFile;
2211 * Store copy of file path.
2214 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2215 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2216 if (!This->pwcsName)
2217 return STG_E_INSUFFICIENTMEMORY;
2218 strcpyW(This->pwcsName, pwcsName);
2222 * Initialize the big block cache.
2224 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2225 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2226 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2232 if (This->bigBlockFile == 0)
2237 ULARGE_INTEGER size;
2238 BYTE* bigBlockBuffer;
2241 * Initialize all header variables:
2242 * - The big block depot consists of one block and it is at block 0
2243 * - The properties start at block 1
2244 * - There is no small block depot
2246 memset( This->bigBlockDepotStart,
2248 sizeof(This->bigBlockDepotStart));
2250 This->bigBlockDepotCount = 1;
2251 This->bigBlockDepotStart[0] = 0;
2252 This->rootStartBlock = 1;
2253 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2254 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2255 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2256 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2257 This->extBigBlockDepotCount = 0;
2259 StorageImpl_SaveFileHeader(This);
2262 * Add one block for the big block depot and one block for the properties
2264 size.s.HighPart = 0;
2265 size.s.LowPart = This->bigBlockSize * 3;
2266 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2269 * Initialize the big block depot
2271 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2272 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2273 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2274 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2275 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2280 * Load the header for the file.
2282 hr = StorageImpl_LoadFileHeader(This);
2286 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2293 * There is no block depot cached yet.
2295 This->indexBlockDepotCached = 0xFFFFFFFF;
2298 * Start searching for free blocks with block 0.
2300 This->prevFreeBlock = 0;
2303 * Create the block chain abstractions.
2305 This->rootBlockChain =
2306 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL);
2308 This->smallBlockDepotChain = BlockChainStream_Construct(
2310 &This->smallBlockDepotStart,
2314 * Write the root property
2318 StgProperty rootProp;
2320 * Initialize the property chain
2322 memset(&rootProp, 0, sizeof(rootProp));
2323 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2324 sizeof(rootProp.name)/sizeof(WCHAR) );
2325 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2326 rootProp.propertyType = PROPTYPE_ROOT;
2327 rootProp.previousProperty = PROPERTY_NULL;
2328 rootProp.nextProperty = PROPERTY_NULL;
2329 rootProp.dirProperty = PROPERTY_NULL;
2330 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2331 rootProp.size.s.HighPart = 0;
2332 rootProp.size.s.LowPart = 0;
2334 StorageImpl_WriteProperty(This, 0, &rootProp);
2338 * Find the ID of the root in the property sets.
2340 currentPropertyIndex = 0;
2344 readSuccessful = StorageImpl_ReadProperty(
2346 currentPropertyIndex,
2351 if ( (currentProperty.sizeOfNameString != 0 ) &&
2352 (currentProperty.propertyType == PROPTYPE_ROOT) )
2354 This->rootPropertySetIndex = currentPropertyIndex;
2358 currentPropertyIndex++;
2360 } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2362 if (!readSuccessful)
2369 * Create the block chain abstraction for the small block root chain.
2371 This->smallBlockRootChain = BlockChainStream_Construct(
2374 This->rootPropertySetIndex);
2379 void StorageImpl_Destroy(
2382 TRACE("(%p)\n", This);
2385 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2387 BlockChainStream_Destroy(This->smallBlockRootChain);
2388 BlockChainStream_Destroy(This->rootBlockChain);
2389 BlockChainStream_Destroy(This->smallBlockDepotChain);
2391 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2395 /******************************************************************************
2396 * Storage32Impl_GetNextFreeBigBlock
2398 * Returns the index of the next free big block.
2399 * If the big block depot is filled, this method will enlarge it.
2402 ULONG StorageImpl_GetNextFreeBigBlock(
2405 ULONG depotBlockIndexPos;
2407 ULONG depotBlockOffset;
2408 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2409 ULONG nextBlockIndex = BLOCK_SPECIAL;
2411 ULONG freeBlock = BLOCK_UNUSED;
2413 depotIndex = This->prevFreeBlock / blocksPerDepot;
2414 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2417 * Scan the entire big block depot until we find a block marked free
2419 while (nextBlockIndex != BLOCK_UNUSED)
2421 if (depotIndex < COUNT_BBDEPOTINHEADER)
2423 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2426 * Grow the primary depot.
2428 if (depotBlockIndexPos == BLOCK_UNUSED)
2430 depotBlockIndexPos = depotIndex*blocksPerDepot;
2433 * Add a block depot.
2435 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2436 This->bigBlockDepotCount++;
2437 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2440 * Flag it as a block depot.
2442 StorageImpl_SetNextBlockInChain(This,
2446 /* Save new header information.
2448 StorageImpl_SaveFileHeader(This);
2453 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2455 if (depotBlockIndexPos == BLOCK_UNUSED)
2458 * Grow the extended depot.
2460 ULONG extIndex = BLOCK_UNUSED;
2461 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2462 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2464 if (extBlockOffset == 0)
2466 /* We need an extended block.
2468 extIndex = Storage32Impl_AddExtBlockDepot(This);
2469 This->extBigBlockDepotCount++;
2470 depotBlockIndexPos = extIndex + 1;
2473 depotBlockIndexPos = depotIndex * blocksPerDepot;
2476 * Add a block depot and mark it in the extended block.
2478 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2479 This->bigBlockDepotCount++;
2480 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2482 /* Flag the block depot.
2484 StorageImpl_SetNextBlockInChain(This,
2488 /* If necessary, flag the extended depot block.
2490 if (extIndex != BLOCK_UNUSED)
2491 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2493 /* Save header information.
2495 StorageImpl_SaveFileHeader(This);
2499 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2501 if (depotBuffer != 0)
2503 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2504 ( nextBlockIndex != BLOCK_UNUSED))
2506 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2508 if (nextBlockIndex == BLOCK_UNUSED)
2510 freeBlock = (depotIndex * blocksPerDepot) +
2511 (depotBlockOffset/sizeof(ULONG));
2514 depotBlockOffset += sizeof(ULONG);
2517 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2521 depotBlockOffset = 0;
2524 This->prevFreeBlock = freeBlock;
2529 /******************************************************************************
2530 * Storage32Impl_AddBlockDepot
2532 * This will create a depot block, essentially it is a block initialized
2535 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2539 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2542 * Initialize blocks as free
2544 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2546 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2549 /******************************************************************************
2550 * Storage32Impl_GetExtDepotBlock
2552 * Returns the index of the block that corresponds to the specified depot
2553 * index. This method is only for depot indexes equal or greater than
2554 * COUNT_BBDEPOTINHEADER.
2556 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2558 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2559 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2560 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2561 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2562 ULONG blockIndex = BLOCK_UNUSED;
2563 ULONG extBlockIndex = This->extBigBlockDepotStart;
2565 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2567 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2568 return BLOCK_UNUSED;
2570 while (extBlockCount > 0)
2572 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2576 if (extBlockIndex != BLOCK_UNUSED)
2580 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2582 if (depotBuffer != 0)
2584 StorageUtl_ReadDWord(depotBuffer,
2585 extBlockOffset * sizeof(ULONG),
2588 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2595 /******************************************************************************
2596 * Storage32Impl_SetExtDepotBlock
2598 * Associates the specified block index to the specified depot index.
2599 * This method is only for depot indexes equal or greater than
2600 * COUNT_BBDEPOTINHEADER.
2602 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2606 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2607 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2608 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2609 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2610 ULONG extBlockIndex = This->extBigBlockDepotStart;
2612 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2614 while (extBlockCount > 0)
2616 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2620 if (extBlockIndex != BLOCK_UNUSED)
2624 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2626 if (depotBuffer != 0)
2628 StorageUtl_WriteDWord(depotBuffer,
2629 extBlockOffset * sizeof(ULONG),
2632 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2637 /******************************************************************************
2638 * Storage32Impl_AddExtBlockDepot
2640 * Creates an extended depot block.
2642 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2644 ULONG numExtBlocks = This->extBigBlockDepotCount;
2645 ULONG nextExtBlock = This->extBigBlockDepotStart;
2646 BYTE* depotBuffer = NULL;
2647 ULONG index = BLOCK_UNUSED;
2648 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2649 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2650 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2652 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2653 blocksPerDepotBlock;
2655 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2658 * The first extended block.
2660 This->extBigBlockDepotStart = index;
2666 * Follow the chain to the last one.
2668 for (i = 0; i < (numExtBlocks - 1); i++)
2670 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2674 * Add the new extended block to the chain.
2676 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2677 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2678 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2682 * Initialize this block.
2684 depotBuffer = StorageImpl_GetBigBlock(This, index);
2685 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2686 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2691 /******************************************************************************
2692 * Storage32Impl_FreeBigBlock
2694 * This method will flag the specified block as free in the big block depot.
2696 void StorageImpl_FreeBigBlock(
2700 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2702 if (blockIndex < This->prevFreeBlock)
2703 This->prevFreeBlock = blockIndex;
2706 /************************************************************************
2707 * Storage32Impl_GetNextBlockInChain
2709 * This method will retrieve the block index of the next big block in
2712 * Params: This - Pointer to the Storage object.
2713 * blockIndex - Index of the block to retrieve the chain
2716 * Returns: This method returns the index of the next block in the chain.
2717 * It will return the constants:
2718 * BLOCK_SPECIAL - If the block given was not part of a
2720 * BLOCK_END_OF_CHAIN - If the block given was the last in
2722 * BLOCK_UNUSED - If the block given was not past of a chain
2724 * BLOCK_EXTBBDEPOT - This block is part of the extended
2727 * See Windows documentation for more details on IStorage methods.
2729 ULONG StorageImpl_GetNextBlockInChain(
2733 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2734 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2735 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2736 ULONG nextBlockIndex = BLOCK_SPECIAL;
2738 ULONG depotBlockIndexPos;
2740 assert(depotBlockCount < This->bigBlockDepotCount);
2743 * Cache the currently accessed depot block.
2745 if (depotBlockCount != This->indexBlockDepotCached)
2747 This->indexBlockDepotCached = depotBlockCount;
2749 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2751 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2756 * We have to look in the extended depot.
2758 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2761 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2767 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2769 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &nextBlockIndex);
2770 This->blockDepotCached[index] = nextBlockIndex;
2773 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2777 nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2779 return nextBlockIndex;
2782 /******************************************************************************
2783 * Storage32Impl_GetNextExtendedBlock
2785 * Given an extended block this method will return the next extended block.
2788 * The last ULONG of an extended block is the block index of the next
2789 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2793 * - The index of the next extended block
2794 * - BLOCK_UNUSED: there is no next extended block.
2795 * - Any other return values denotes failure.
2797 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2799 ULONG nextBlockIndex = BLOCK_SPECIAL;
2800 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2803 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2807 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2809 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2812 return nextBlockIndex;
2815 /******************************************************************************
2816 * Storage32Impl_SetNextBlockInChain
2818 * This method will write the index of the specified block's next block
2819 * in the big block depot.
2821 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2824 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2825 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2826 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2829 void StorageImpl_SetNextBlockInChain(
2834 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2835 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2836 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2837 ULONG depotBlockIndexPos;
2840 assert(depotBlockCount < This->bigBlockDepotCount);
2841 assert(blockIndex != nextBlock);
2843 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2845 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2850 * We have to look in the extended depot.
2852 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2855 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2859 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2860 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2864 * Update the cached block depot, if necessary.
2866 if (depotBlockCount == This->indexBlockDepotCached)
2868 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2872 /******************************************************************************
2873 * Storage32Impl_LoadFileHeader
2875 * This method will read in the file header, i.e. big block index -1.
2877 HRESULT StorageImpl_LoadFileHeader(
2880 HRESULT hr = STG_E_FILENOTFOUND;
2881 void* headerBigBlock = NULL;
2885 * Get a pointer to the big block of data containing the header.
2887 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2890 * Extract the information from the header.
2892 if (headerBigBlock!=0)
2895 * Check for the "magic number" signature and return an error if it is not
2898 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2900 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2901 return STG_E_OLDFORMAT;
2904 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2906 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2907 return STG_E_INVALIDHEADER;
2910 StorageUtl_ReadWord(
2912 OFFSET_BIGBLOCKSIZEBITS,
2913 &This->bigBlockSizeBits);
2915 StorageUtl_ReadWord(
2917 OFFSET_SMALLBLOCKSIZEBITS,
2918 &This->smallBlockSizeBits);
2920 StorageUtl_ReadDWord(
2922 OFFSET_BBDEPOTCOUNT,
2923 &This->bigBlockDepotCount);
2925 StorageUtl_ReadDWord(
2927 OFFSET_ROOTSTARTBLOCK,
2928 &This->rootStartBlock);
2930 StorageUtl_ReadDWord(
2932 OFFSET_SBDEPOTSTART,
2933 &This->smallBlockDepotStart);
2935 StorageUtl_ReadDWord(
2937 OFFSET_EXTBBDEPOTSTART,
2938 &This->extBigBlockDepotStart);
2940 StorageUtl_ReadDWord(
2942 OFFSET_EXTBBDEPOTCOUNT,
2943 &This->extBigBlockDepotCount);
2945 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2947 StorageUtl_ReadDWord(
2949 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2950 &(This->bigBlockDepotStart[index]));
2954 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2958 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2959 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2963 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2964 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2968 * Right now, the code is making some assumptions about the size of the
2969 * blocks, just make sure they are what we're expecting.
2971 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
2972 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
2974 WARN("Broken OLE storage file\n");
2975 hr = STG_E_INVALIDHEADER;
2981 * Release the block.
2983 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2989 /******************************************************************************
2990 * Storage32Impl_SaveFileHeader
2992 * This method will save to the file the header, i.e. big block -1.
2994 void StorageImpl_SaveFileHeader(
2997 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3002 * Get a pointer to the big block of data containing the header.
3004 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3007 * If the block read failed, the file is probably new.
3012 * Initialize for all unknown fields.
3014 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3017 * Initialize the magic number.
3019 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3022 * And a bunch of things we don't know what they mean
3024 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3025 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3026 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3027 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3028 StorageUtl_WriteDWord(headerBigBlock, 0x40, (DWORD)0x0001);
3032 * Write the information to the header.
3034 if (headerBigBlock!=0)
3036 StorageUtl_WriteWord(
3038 OFFSET_BIGBLOCKSIZEBITS,
3039 This->bigBlockSizeBits);
3041 StorageUtl_WriteWord(
3043 OFFSET_SMALLBLOCKSIZEBITS,
3044 This->smallBlockSizeBits);
3046 StorageUtl_WriteDWord(
3048 OFFSET_BBDEPOTCOUNT,
3049 This->bigBlockDepotCount);
3051 StorageUtl_WriteDWord(
3053 OFFSET_ROOTSTARTBLOCK,
3054 This->rootStartBlock);
3056 StorageUtl_WriteDWord(
3058 OFFSET_SBDEPOTSTART,
3059 This->smallBlockDepotStart);
3061 StorageUtl_WriteDWord(
3063 OFFSET_EXTBBDEPOTSTART,
3064 This->extBigBlockDepotStart);
3066 StorageUtl_WriteDWord(
3068 OFFSET_EXTBBDEPOTCOUNT,
3069 This->extBigBlockDepotCount);
3071 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3073 StorageUtl_WriteDWord(
3075 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3076 (This->bigBlockDepotStart[index]));
3081 * Write the big block back to the file.
3083 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3086 /******************************************************************************
3087 * Storage32Impl_ReadProperty
3089 * This method will read the specified property from the property chain.
3091 BOOL StorageImpl_ReadProperty(
3094 StgProperty* buffer)
3096 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3097 ULARGE_INTEGER offsetInPropSet;
3098 BOOL readSuccessful;
3101 offsetInPropSet.s.HighPart = 0;
3102 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3104 readSuccessful = BlockChainStream_ReadAt(
3105 This->rootBlockChain,
3113 /* replace the name of root entry (often "Root Entry") by the file name */
3114 WCHAR *propName = (index == This->rootPropertySetIndex) ?
3115 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3117 memset(buffer->name, 0, sizeof(buffer->name));
3121 PROPERTY_NAME_BUFFER_LEN );
3122 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3124 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3126 StorageUtl_ReadWord(
3128 OFFSET_PS_NAMELENGTH,
3129 &buffer->sizeOfNameString);
3131 StorageUtl_ReadDWord(
3133 OFFSET_PS_PREVIOUSPROP,
3134 &buffer->previousProperty);
3136 StorageUtl_ReadDWord(
3139 &buffer->nextProperty);
3141 StorageUtl_ReadDWord(
3144 &buffer->dirProperty);
3146 StorageUtl_ReadGUID(
3149 &buffer->propertyUniqueID);
3151 StorageUtl_ReadDWord(
3154 &buffer->timeStampS1);
3156 StorageUtl_ReadDWord(
3159 &buffer->timeStampD1);
3161 StorageUtl_ReadDWord(
3164 &buffer->timeStampS2);
3166 StorageUtl_ReadDWord(
3169 &buffer->timeStampD2);
3171 StorageUtl_ReadDWord(
3173 OFFSET_PS_STARTBLOCK,
3174 &buffer->startingBlock);
3176 StorageUtl_ReadDWord(
3179 &buffer->size.s.LowPart);
3181 buffer->size.s.HighPart = 0;
3184 return readSuccessful;
3187 /*********************************************************************
3188 * Write the specified property into the property chain
3190 BOOL StorageImpl_WriteProperty(
3193 StgProperty* buffer)
3195 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3196 ULARGE_INTEGER offsetInPropSet;
3197 BOOL writeSuccessful;
3200 offsetInPropSet.s.HighPart = 0;
3201 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3203 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3206 currentProperty + OFFSET_PS_NAME,
3208 PROPERTY_NAME_BUFFER_LEN );
3210 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3212 StorageUtl_WriteWord(
3214 OFFSET_PS_NAMELENGTH,
3215 buffer->sizeOfNameString);
3217 StorageUtl_WriteDWord(
3219 OFFSET_PS_PREVIOUSPROP,
3220 buffer->previousProperty);
3222 StorageUtl_WriteDWord(
3225 buffer->nextProperty);
3227 StorageUtl_WriteDWord(
3230 buffer->dirProperty);
3232 StorageUtl_WriteGUID(
3235 &buffer->propertyUniqueID);
3237 StorageUtl_WriteDWord(
3240 buffer->timeStampS1);
3242 StorageUtl_WriteDWord(
3245 buffer->timeStampD1);
3247 StorageUtl_WriteDWord(
3250 buffer->timeStampS2);
3252 StorageUtl_WriteDWord(
3255 buffer->timeStampD2);
3257 StorageUtl_WriteDWord(
3259 OFFSET_PS_STARTBLOCK,
3260 buffer->startingBlock);
3262 StorageUtl_WriteDWord(
3265 buffer->size.s.LowPart);
3267 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3272 return writeSuccessful;
3275 BOOL StorageImpl_ReadBigBlock(
3280 void* bigBlockBuffer;
3282 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3284 if (bigBlockBuffer!=0)
3286 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3288 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3296 BOOL StorageImpl_WriteBigBlock(
3301 void* bigBlockBuffer;
3303 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3305 if (bigBlockBuffer!=0)
3307 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3309 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3317 void* StorageImpl_GetROBigBlock(
3321 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3324 void* StorageImpl_GetBigBlock(
3328 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3331 void StorageImpl_ReleaseBigBlock(
3335 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3338 /******************************************************************************
3339 * Storage32Impl_SmallBlocksToBigBlocks
3341 * This method will convert a small block chain to a big block chain.
3342 * The small block chain will be destroyed.
3344 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3346 SmallBlockChainStream** ppsbChain)
3348 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3349 ULARGE_INTEGER size, offset;
3350 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3351 ULONG propertyIndex;
3352 BOOL successRead, successWrite;
3353 StgProperty chainProperty;
3355 BlockChainStream *bbTempChain = NULL;
3356 BlockChainStream *bigBlockChain = NULL;
3359 * Create a temporary big block chain that doesn't have
3360 * an associated property. This temporary chain will be
3361 * used to copy data from small blocks to big blocks.
3363 bbTempChain = BlockChainStream_Construct(This,
3368 * Grow the big block chain.
3370 size = SmallBlockChainStream_GetSize(*ppsbChain);
3371 BlockChainStream_SetSize(bbTempChain, size);
3374 * Copy the contents of the small block chain to the big block chain
3375 * by small block size increments.
3377 offset.s.LowPart = 0;
3378 offset.s.HighPart = 0;
3382 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3385 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3387 DEF_SMALL_BLOCK_SIZE,
3390 cbTotalRead += cbRead;
3392 successWrite = BlockChainStream_WriteAt(bbTempChain,
3397 cbTotalWritten += cbWritten;
3399 offset.s.LowPart += This->smallBlockSize;
3401 } while (successRead && successWrite);
3402 HeapFree(GetProcessHeap(),0,buffer);
3404 assert(cbTotalRead == cbTotalWritten);
3407 * Destroy the small block chain.
3409 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3410 size.s.HighPart = 0;
3412 SmallBlockChainStream_SetSize(*ppsbChain, size);
3413 SmallBlockChainStream_Destroy(*ppsbChain);
3417 * Change the property information. This chain is now a big block chain
3418 * and it doesn't reside in the small blocks chain anymore.
3420 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3422 chainProperty.startingBlock = bbHeadOfChain;
3424 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3427 * Destroy the temporary propertyless big block chain.
3428 * Create a new big block chain associated with this property.
3430 BlockChainStream_Destroy(bbTempChain);
3431 bigBlockChain = BlockChainStream_Construct(This,
3435 return bigBlockChain;
3438 /******************************************************************************
3439 ** Storage32InternalImpl implementation
3442 StorageInternalImpl* StorageInternalImpl_Construct(
3443 StorageImpl* ancestorStorage,
3444 ULONG rootPropertyIndex)
3446 StorageInternalImpl* newStorage;
3449 * Allocate space for the new storage object
3451 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3455 memset(newStorage, 0, sizeof(StorageInternalImpl));
3458 * Initialize the virtual function table.
3460 ICOM_VTBL(newStorage) = &Storage32InternalImpl_Vtbl;
3461 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3464 * Keep the ancestor storage pointer and nail a reference to it.
3466 newStorage->ancestorStorage = ancestorStorage;
3467 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3470 * Keep the index of the root property set for this storage,
3472 newStorage->rootPropertySetIndex = rootPropertyIndex;
3480 void StorageInternalImpl_Destroy(
3481 StorageInternalImpl* This)
3483 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3484 HeapFree(GetProcessHeap(), 0, This);
3487 /******************************************************************************
3489 ** Storage32InternalImpl_Commit
3491 ** The non-root storages cannot be opened in transacted mode thus this function
3494 HRESULT WINAPI StorageInternalImpl_Commit(
3496 DWORD grfCommitFlags) /* [in] */
3501 /******************************************************************************
3503 ** Storage32InternalImpl_Revert
3505 ** The non-root storages cannot be opened in transacted mode thus this function
3508 HRESULT WINAPI StorageInternalImpl_Revert(
3514 /******************************************************************************
3515 ** IEnumSTATSTGImpl implementation
3518 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3519 StorageImpl* parentStorage,
3520 ULONG firstPropertyNode)
3522 IEnumSTATSTGImpl* newEnumeration;
3524 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3526 if (newEnumeration!=0)
3529 * Set-up the virtual function table and reference count.
3531 ICOM_VTBL(newEnumeration) = &IEnumSTATSTGImpl_Vtbl;
3532 newEnumeration->ref = 0;
3535 * We want to nail-down the reference to the storage in case the
3536 * enumeration out-lives the storage in the client application.
3538 newEnumeration->parentStorage = parentStorage;
3539 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3541 newEnumeration->firstPropertyNode = firstPropertyNode;
3544 * Initialize the search stack
3546 newEnumeration->stackSize = 0;
3547 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3548 newEnumeration->stackToVisit =
3549 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3552 * Make sure the current node of the iterator is the first one.
3554 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3557 return newEnumeration;
3560 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3562 IStorage_Release((IStorage*)This->parentStorage);
3563 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3564 HeapFree(GetProcessHeap(), 0, This);
3567 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3568 IEnumSTATSTG* iface,
3572 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3575 * Perform a sanity check on the parameters.
3578 return E_INVALIDARG;
3581 * Initialize the return parameter.
3586 * Compare the riid with the interface IDs implemented by this object.
3588 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3590 *ppvObject = (IEnumSTATSTG*)This;
3592 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3594 *ppvObject = (IEnumSTATSTG*)This;
3598 * Check that we obtained an interface.
3600 if ((*ppvObject)==0)
3601 return E_NOINTERFACE;
3604 * Query Interface always increases the reference count by one when it is
3607 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3612 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3613 IEnumSTATSTG* iface)
3615 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3621 ULONG WINAPI IEnumSTATSTGImpl_Release(
3622 IEnumSTATSTG* iface)
3624 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3632 * If the reference count goes down to 0, perform suicide.
3636 IEnumSTATSTGImpl_Destroy(This);
3642 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3643 IEnumSTATSTG* iface,
3646 ULONG* pceltFetched)
3648 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3650 StgProperty currentProperty;
3651 STATSTG* currentReturnStruct = rgelt;
3652 ULONG objectFetched = 0;
3653 ULONG currentSearchNode;
3656 * Perform a sanity check on the parameters.
3658 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3659 return E_INVALIDARG;
3662 * To avoid the special case, get another pointer to a ULONG value if
3663 * the caller didn't supply one.
3665 if (pceltFetched==0)
3666 pceltFetched = &objectFetched;
3669 * Start the iteration, we will iterate until we hit the end of the
3670 * linked list or until we hit the number of items to iterate through
3675 * Start with the node at the top of the stack.
3677 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3679 while ( ( *pceltFetched < celt) &&
3680 ( currentSearchNode!=PROPERTY_NULL) )
3683 * Remove the top node from the stack
3685 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3688 * Read the property from the storage.
3690 StorageImpl_ReadProperty(This->parentStorage,
3695 * Copy the information to the return buffer.
3697 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3702 * Step to the next item in the iteration
3705 currentReturnStruct++;
3708 * Push the next search node in the search stack.
3710 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3713 * continue the iteration.
3715 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3718 if (*pceltFetched == celt)
3725 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3726 IEnumSTATSTG* iface,
3729 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3731 StgProperty currentProperty;
3732 ULONG objectFetched = 0;
3733 ULONG currentSearchNode;
3736 * Start with the node at the top of the stack.
3738 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3740 while ( (objectFetched < celt) &&
3741 (currentSearchNode!=PROPERTY_NULL) )
3744 * Remove the top node from the stack
3746 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3749 * Read the property from the storage.
3751 StorageImpl_ReadProperty(This->parentStorage,
3756 * Step to the next item in the iteration
3761 * Push the next search node in the search stack.
3763 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3766 * continue the iteration.
3768 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3771 if (objectFetched == celt)
3777 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3778 IEnumSTATSTG* iface)
3780 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3782 StgProperty rootProperty;
3783 BOOL readSuccessful;
3786 * Re-initialize the search stack to an empty stack
3788 This->stackSize = 0;
3791 * Read the root property from the storage.
3793 readSuccessful = StorageImpl_ReadProperty(
3794 This->parentStorage,
3795 This->firstPropertyNode,
3800 assert(rootProperty.sizeOfNameString!=0);
3803 * Push the search node in the search stack.
3805 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3811 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3812 IEnumSTATSTG* iface,
3813 IEnumSTATSTG** ppenum)
3815 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3817 IEnumSTATSTGImpl* newClone;
3820 * Perform a sanity check on the parameters.
3823 return E_INVALIDARG;
3825 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3826 This->firstPropertyNode);
3830 * The new clone enumeration must point to the same current node as
3833 newClone->stackSize = This->stackSize ;
3834 newClone->stackMaxSize = This->stackMaxSize ;
3835 newClone->stackToVisit =
3836 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3839 newClone->stackToVisit,
3841 sizeof(ULONG) * newClone->stackSize);
3843 *ppenum = (IEnumSTATSTG*)newClone;
3846 * Don't forget to nail down a reference to the clone before
3849 IEnumSTATSTGImpl_AddRef(*ppenum);
3854 INT IEnumSTATSTGImpl_FindParentProperty(
3855 IEnumSTATSTGImpl *This,
3856 ULONG childProperty,
3857 StgProperty *currentProperty,
3860 ULONG currentSearchNode;
3864 * To avoid the special case, get another pointer to a ULONG value if
3865 * the caller didn't supply one.
3868 thisNodeId = &foundNode;
3871 * Start with the node at the top of the stack.
3873 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3876 while (currentSearchNode!=PROPERTY_NULL)
3879 * Store the current node in the returned parameters
3881 *thisNodeId = currentSearchNode;
3884 * Remove the top node from the stack
3886 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3889 * Read the property from the storage.
3891 StorageImpl_ReadProperty(
3892 This->parentStorage,
3896 if (currentProperty->previousProperty == childProperty)
3897 return PROPERTY_RELATION_PREVIOUS;
3899 else if (currentProperty->nextProperty == childProperty)
3900 return PROPERTY_RELATION_NEXT;
3902 else if (currentProperty->dirProperty == childProperty)
3903 return PROPERTY_RELATION_DIR;
3906 * Push the next search node in the search stack.
3908 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3911 * continue the iteration.
3913 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3916 return PROPERTY_NULL;
3919 ULONG IEnumSTATSTGImpl_FindProperty(
3920 IEnumSTATSTGImpl* This,
3921 const OLECHAR* lpszPropName,
3922 StgProperty* currentProperty)
3924 ULONG currentSearchNode;
3927 * Start with the node at the top of the stack.
3929 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3931 while (currentSearchNode!=PROPERTY_NULL)
3934 * Remove the top node from the stack
3936 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3939 * Read the property from the storage.
3941 StorageImpl_ReadProperty(This->parentStorage,
3945 if ( propertyNameCmp(
3946 (OLECHAR*)currentProperty->name,
3947 (OLECHAR*)lpszPropName) == 0)
3948 return currentSearchNode;
3951 * Push the next search node in the search stack.
3953 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3956 * continue the iteration.
3958 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3961 return PROPERTY_NULL;
3964 void IEnumSTATSTGImpl_PushSearchNode(
3965 IEnumSTATSTGImpl* This,
3968 StgProperty rootProperty;
3969 BOOL readSuccessful;
3972 * First, make sure we're not trying to push an unexisting node.
3974 if (nodeToPush==PROPERTY_NULL)
3978 * First push the node to the stack
3980 if (This->stackSize == This->stackMaxSize)
3982 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3984 This->stackToVisit = HeapReAlloc(
3988 sizeof(ULONG) * This->stackMaxSize);
3991 This->stackToVisit[This->stackSize] = nodeToPush;
3995 * Read the root property from the storage.
3997 readSuccessful = StorageImpl_ReadProperty(
3998 This->parentStorage,
4004 assert(rootProperty.sizeOfNameString!=0);
4007 * Push the previous search node in the search stack.
4009 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4013 ULONG IEnumSTATSTGImpl_PopSearchNode(
4014 IEnumSTATSTGImpl* This,
4019 if (This->stackSize == 0)
4020 return PROPERTY_NULL;
4022 topNode = This->stackToVisit[This->stackSize-1];
4030 /******************************************************************************
4031 ** StorageUtl implementation
4034 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
4036 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
4039 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
4041 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
4044 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
4046 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
4049 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
4051 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
4054 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
4056 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4057 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4058 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4060 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
4063 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
4065 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4066 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4067 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4069 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
4072 void StorageUtl_CopyPropertyToSTATSTG(
4073 STATSTG* destination,
4074 StgProperty* source,
4078 * The copy of the string occurs only when the flag is not set
4080 if ((statFlags & STATFLAG_NONAME) != 0)
4082 destination->pwcsName = 0;
4086 destination->pwcsName =
4087 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4089 strcpyW((LPWSTR)destination->pwcsName, source->name);
4092 switch (source->propertyType)
4094 case PROPTYPE_STORAGE:
4096 destination->type = STGTY_STORAGE;
4098 case PROPTYPE_STREAM:
4099 destination->type = STGTY_STREAM;
4102 destination->type = STGTY_STREAM;
4106 destination->cbSize = source->size;
4108 currentReturnStruct->mtime = {0}; TODO
4109 currentReturnStruct->ctime = {0};
4110 currentReturnStruct->atime = {0};
4112 destination->grfMode = 0;
4113 destination->grfLocksSupported = 0;
4114 destination->clsid = source->propertyUniqueID;
4115 destination->grfStateBits = 0;
4116 destination->reserved = 0;
4119 /******************************************************************************
4120 ** BlockChainStream implementation
4123 BlockChainStream* BlockChainStream_Construct(
4124 StorageImpl* parentStorage,
4125 ULONG* headOfStreamPlaceHolder,
4126 ULONG propertyIndex)
4128 BlockChainStream* newStream;
4131 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4133 newStream->parentStorage = parentStorage;
4134 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4135 newStream->ownerPropertyIndex = propertyIndex;
4136 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4137 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4138 newStream->numBlocks = 0;
4140 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4142 while (blockIndex != BLOCK_END_OF_CHAIN)
4144 newStream->numBlocks++;
4145 newStream->tailIndex = blockIndex;
4147 blockIndex = StorageImpl_GetNextBlockInChain(
4155 void BlockChainStream_Destroy(BlockChainStream* This)
4157 HeapFree(GetProcessHeap(), 0, This);
4160 /******************************************************************************
4161 * BlockChainStream_GetHeadOfChain
4163 * Returns the head of this stream chain.
4164 * Some special chains don't have properties, their heads are kept in
4165 * This->headOfStreamPlaceHolder.
4168 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4170 StgProperty chainProperty;
4171 BOOL readSuccessful;
4173 if (This->headOfStreamPlaceHolder != 0)
4174 return *(This->headOfStreamPlaceHolder);
4176 if (This->ownerPropertyIndex != PROPERTY_NULL)
4178 readSuccessful = StorageImpl_ReadProperty(
4179 This->parentStorage,
4180 This->ownerPropertyIndex,
4185 return chainProperty.startingBlock;
4189 return BLOCK_END_OF_CHAIN;
4192 /******************************************************************************
4193 * BlockChainStream_GetCount
4195 * Returns the number of blocks that comprises this chain.
4196 * This is not the size of the stream as the last block may not be full!
4199 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4204 blockIndex = BlockChainStream_GetHeadOfChain(This);
4206 while (blockIndex != BLOCK_END_OF_CHAIN)
4210 blockIndex = StorageImpl_GetNextBlockInChain(
4211 This->parentStorage,
4218 /******************************************************************************
4219 * BlockChainStream_ReadAt
4221 * Reads a specified number of bytes from this chain at the specified offset.
4222 * bytesRead may be NULL.
4223 * Failure will be returned if the specified number of bytes has not been read.
4225 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4226 ULARGE_INTEGER offset,
4231 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4232 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4233 ULONG bytesToReadInBuffer;
4236 BYTE* bigBlockBuffer;
4239 * Find the first block in the stream that contains part of the buffer.
4241 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4242 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4243 (blockNoInSequence < This->lastBlockNoInSequence) )
4245 blockIndex = BlockChainStream_GetHeadOfChain(This);
4246 This->lastBlockNoInSequence = blockNoInSequence;
4250 ULONG temp = blockNoInSequence;
4252 blockIndex = This->lastBlockNoInSequenceIndex;
4253 blockNoInSequence -= This->lastBlockNoInSequence;
4254 This->lastBlockNoInSequence = temp;
4257 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4260 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4262 blockNoInSequence--;
4265 This->lastBlockNoInSequenceIndex = blockIndex;
4268 * Start reading the buffer.
4271 bufferWalker = buffer;
4273 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4276 * Calculate how many bytes we can copy from this big block.
4278 bytesToReadInBuffer =
4279 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4282 * Copy those bytes to the buffer
4285 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4287 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4289 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4292 * Step to the next big block.
4295 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4297 bufferWalker += bytesToReadInBuffer;
4298 size -= bytesToReadInBuffer;
4299 *bytesRead += bytesToReadInBuffer;
4300 offsetInBlock = 0; /* There is no offset on the next block */
4307 /******************************************************************************
4308 * BlockChainStream_WriteAt
4310 * Writes the specified number of bytes to this chain at the specified offset.
4311 * bytesWritten may be NULL.
4312 * Will fail if not all specified number of bytes have been written.
4314 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4315 ULARGE_INTEGER offset,
4318 ULONG* bytesWritten)
4320 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4321 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4325 BYTE* bigBlockBuffer;
4328 * Find the first block in the stream that contains part of the buffer.
4330 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4331 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4332 (blockNoInSequence < This->lastBlockNoInSequence) )
4334 blockIndex = BlockChainStream_GetHeadOfChain(This);
4335 This->lastBlockNoInSequence = blockNoInSequence;
4339 ULONG temp = blockNoInSequence;
4341 blockIndex = This->lastBlockNoInSequenceIndex;
4342 blockNoInSequence -= This->lastBlockNoInSequence;
4343 This->lastBlockNoInSequence = temp;
4346 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4349 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4351 blockNoInSequence--;
4354 This->lastBlockNoInSequenceIndex = blockIndex;
4357 * Here, I'm casting away the constness on the buffer variable
4358 * This is OK since we don't intend to modify that buffer.
4361 bufferWalker = (BYTE*)buffer;
4363 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4366 * Calculate how many bytes we can copy from this big block.
4369 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4372 * Copy those bytes to the buffer
4374 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4376 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4378 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4381 * Step to the next big block.
4384 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4386 bufferWalker += bytesToWrite;
4387 size -= bytesToWrite;
4388 *bytesWritten += bytesToWrite;
4389 offsetInBlock = 0; /* There is no offset on the next block */
4395 /******************************************************************************
4396 * BlockChainStream_Shrink
4398 * Shrinks this chain in the big block depot.
4400 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4401 ULARGE_INTEGER newSize)
4403 ULONG blockIndex, extraBlock;
4408 * Reset the last accessed block cache.
4410 This->lastBlockNoInSequence = 0xFFFFFFFF;
4411 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4414 * Figure out how many blocks are needed to contain the new size
4416 numBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4418 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4421 blockIndex = BlockChainStream_GetHeadOfChain(This);
4424 * Go to the new end of chain
4426 while (count < numBlocks)
4429 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4434 /* Get the next block before marking the new end */
4436 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4438 /* Mark the new end of chain */
4439 StorageImpl_SetNextBlockInChain(
4440 This->parentStorage,
4442 BLOCK_END_OF_CHAIN);
4444 This->tailIndex = blockIndex;
4445 This->numBlocks = numBlocks;
4448 * Mark the extra blocks as free
4450 while (extraBlock != BLOCK_END_OF_CHAIN)
4453 StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock);
4455 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4456 extraBlock = blockIndex;
4462 /******************************************************************************
4463 * BlockChainStream_Enlarge
4465 * Grows this chain in the big block depot.
4467 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4468 ULARGE_INTEGER newSize)
4470 ULONG blockIndex, currentBlock;
4472 ULONG oldNumBlocks = 0;
4474 blockIndex = BlockChainStream_GetHeadOfChain(This);
4477 * Empty chain. Create the head.
4479 if (blockIndex == BLOCK_END_OF_CHAIN)
4481 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4482 StorageImpl_SetNextBlockInChain(This->parentStorage,
4484 BLOCK_END_OF_CHAIN);
4486 if (This->headOfStreamPlaceHolder != 0)
4488 *(This->headOfStreamPlaceHolder) = blockIndex;
4492 StgProperty chainProp;
4493 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4495 StorageImpl_ReadProperty(
4496 This->parentStorage,
4497 This->ownerPropertyIndex,
4500 chainProp.startingBlock = blockIndex;
4502 StorageImpl_WriteProperty(
4503 This->parentStorage,
4504 This->ownerPropertyIndex,
4508 This->tailIndex = blockIndex;
4509 This->numBlocks = 1;
4513 * Figure out how many blocks are needed to contain this stream
4515 newNumBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4517 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4521 * Go to the current end of chain
4523 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4525 currentBlock = blockIndex;
4527 while (blockIndex != BLOCK_END_OF_CHAIN)
4530 currentBlock = blockIndex;
4533 StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock);
4536 This->tailIndex = currentBlock;
4539 currentBlock = This->tailIndex;
4540 oldNumBlocks = This->numBlocks;
4543 * Add new blocks to the chain
4545 if (oldNumBlocks < newNumBlocks)
4547 while (oldNumBlocks < newNumBlocks)
4549 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4551 StorageImpl_SetNextBlockInChain(
4552 This->parentStorage,
4556 StorageImpl_SetNextBlockInChain(
4557 This->parentStorage,
4559 BLOCK_END_OF_CHAIN);
4561 currentBlock = blockIndex;
4565 This->tailIndex = blockIndex;
4566 This->numBlocks = newNumBlocks;
4572 /******************************************************************************
4573 * BlockChainStream_SetSize
4575 * Sets the size of this stream. The big block depot will be updated.
4576 * The file will grow if we grow the chain.
4578 * TODO: Free the actual blocks in the file when we shrink the chain.
4579 * Currently, the blocks are still in the file. So the file size
4580 * doesn't shrink even if we shrink streams.
4582 BOOL BlockChainStream_SetSize(
4583 BlockChainStream* This,
4584 ULARGE_INTEGER newSize)
4586 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4588 if (newSize.s.LowPart == size.s.LowPart)
4591 if (newSize.s.LowPart < size.s.LowPart)
4593 BlockChainStream_Shrink(This, newSize);
4597 ULARGE_INTEGER fileSize =
4598 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4600 ULONG diff = newSize.s.LowPart - size.s.LowPart;
4603 * Make sure the file stays a multiple of blocksize
4605 if ((diff % This->parentStorage->bigBlockSize) != 0)
4606 diff += (This->parentStorage->bigBlockSize -
4607 (diff % This->parentStorage->bigBlockSize) );
4609 fileSize.s.LowPart += diff;
4610 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4612 BlockChainStream_Enlarge(This, newSize);
4618 /******************************************************************************
4619 * BlockChainStream_GetSize
4621 * Returns the size of this chain.
4622 * Will return the block count if this chain doesn't have a property.
4624 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4626 StgProperty chainProperty;
4628 if(This->headOfStreamPlaceHolder == NULL)
4631 * This chain is a data stream read the property and return
4632 * the appropriate size
4634 StorageImpl_ReadProperty(
4635 This->parentStorage,
4636 This->ownerPropertyIndex,
4639 return chainProperty.size;
4644 * this chain is a chain that does not have a property, figure out the
4645 * size by making the product number of used blocks times the
4648 ULARGE_INTEGER result;
4649 result.s.HighPart = 0;
4652 BlockChainStream_GetCount(This) *
4653 This->parentStorage->bigBlockSize;
4659 /******************************************************************************
4660 ** SmallBlockChainStream implementation
4663 SmallBlockChainStream* SmallBlockChainStream_Construct(
4664 StorageImpl* parentStorage,
4665 ULONG propertyIndex)
4667 SmallBlockChainStream* newStream;
4669 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4671 newStream->parentStorage = parentStorage;
4672 newStream->ownerPropertyIndex = propertyIndex;
4677 void SmallBlockChainStream_Destroy(
4678 SmallBlockChainStream* This)
4680 HeapFree(GetProcessHeap(), 0, This);
4683 /******************************************************************************
4684 * SmallBlockChainStream_GetHeadOfChain
4686 * Returns the head of this chain of small blocks.
4688 ULONG SmallBlockChainStream_GetHeadOfChain(
4689 SmallBlockChainStream* This)
4691 StgProperty chainProperty;
4692 BOOL readSuccessful;
4694 if (This->ownerPropertyIndex)
4696 readSuccessful = StorageImpl_ReadProperty(
4697 This->parentStorage,
4698 This->ownerPropertyIndex,
4703 return chainProperty.startingBlock;
4708 return BLOCK_END_OF_CHAIN;
4711 /******************************************************************************
4712 * SmallBlockChainStream_GetNextBlockInChain
4714 * Returns the index of the next small block in this chain.
4717 * - BLOCK_END_OF_CHAIN: end of this chain
4718 * - BLOCK_UNUSED: small block 'blockIndex' is free
4720 ULONG SmallBlockChainStream_GetNextBlockInChain(
4721 SmallBlockChainStream* This,
4724 ULARGE_INTEGER offsetOfBlockInDepot;
4726 ULONG nextBlockInChain = BLOCK_END_OF_CHAIN;
4730 offsetOfBlockInDepot.s.HighPart = 0;
4731 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4734 * Read those bytes in the buffer from the small block file.
4736 success = BlockChainStream_ReadAt(
4737 This->parentStorage->smallBlockDepotChain,
4738 offsetOfBlockInDepot,
4745 StorageUtl_ReadDWord(&buffer, 0, &nextBlockInChain);
4748 return nextBlockInChain;
4751 /******************************************************************************
4752 * SmallBlockChainStream_SetNextBlockInChain
4754 * Writes the index of the next block of the specified block in the small
4756 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4757 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4759 void SmallBlockChainStream_SetNextBlockInChain(
4760 SmallBlockChainStream* This,
4764 ULARGE_INTEGER offsetOfBlockInDepot;
4768 offsetOfBlockInDepot.s.HighPart = 0;
4769 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4771 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4774 * Read those bytes in the buffer from the small block file.
4776 BlockChainStream_WriteAt(
4777 This->parentStorage->smallBlockDepotChain,
4778 offsetOfBlockInDepot,
4784 /******************************************************************************
4785 * SmallBlockChainStream_FreeBlock
4787 * Flag small block 'blockIndex' as free in the small block depot.
4789 void SmallBlockChainStream_FreeBlock(
4790 SmallBlockChainStream* This,
4793 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4796 /******************************************************************************
4797 * SmallBlockChainStream_GetNextFreeBlock
4799 * Returns the index of a free small block. The small block depot will be
4800 * enlarged if necessary. The small block chain will also be enlarged if
4803 ULONG SmallBlockChainStream_GetNextFreeBlock(
4804 SmallBlockChainStream* This)
4806 ULARGE_INTEGER offsetOfBlockInDepot;
4809 ULONG blockIndex = 0;
4810 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4811 BOOL success = TRUE;
4812 ULONG smallBlocksPerBigBlock;
4814 offsetOfBlockInDepot.s.HighPart = 0;
4817 * Scan the small block depot for a free block
4819 while (nextBlockIndex != BLOCK_UNUSED)
4821 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4823 success = BlockChainStream_ReadAt(
4824 This->parentStorage->smallBlockDepotChain,
4825 offsetOfBlockInDepot,
4831 * If we run out of space for the small block depot, enlarge it
4835 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4837 if (nextBlockIndex != BLOCK_UNUSED)
4843 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4845 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4846 ULONG nextBlock, newsbdIndex;
4847 BYTE* smallBlockDepot;
4849 nextBlock = sbdIndex;
4850 while (nextBlock != BLOCK_END_OF_CHAIN)
4852 sbdIndex = nextBlock;
4854 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex);
4857 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4858 if (sbdIndex != BLOCK_END_OF_CHAIN)
4859 StorageImpl_SetNextBlockInChain(
4860 This->parentStorage,
4864 StorageImpl_SetNextBlockInChain(
4865 This->parentStorage,
4867 BLOCK_END_OF_CHAIN);
4870 * Initialize all the small blocks to free
4873 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4875 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4876 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4881 * We have just created the small block depot.
4883 StgProperty rootProp;
4887 * Save it in the header
4889 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4890 StorageImpl_SaveFileHeader(This->parentStorage);
4893 * And allocate the first big block that will contain small blocks
4896 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4898 StorageImpl_SetNextBlockInChain(
4899 This->parentStorage,
4901 BLOCK_END_OF_CHAIN);
4903 StorageImpl_ReadProperty(
4904 This->parentStorage,
4905 This->parentStorage->rootPropertySetIndex,
4908 rootProp.startingBlock = sbStartIndex;
4909 rootProp.size.s.HighPart = 0;
4910 rootProp.size.s.LowPart = This->parentStorage->bigBlockSize;
4912 StorageImpl_WriteProperty(
4913 This->parentStorage,
4914 This->parentStorage->rootPropertySetIndex,
4920 smallBlocksPerBigBlock =
4921 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4924 * Verify if we have to allocate big blocks to contain small blocks
4926 if (blockIndex % smallBlocksPerBigBlock == 0)
4928 StgProperty rootProp;
4929 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4931 StorageImpl_ReadProperty(
4932 This->parentStorage,
4933 This->parentStorage->rootPropertySetIndex,
4936 if (rootProp.size.s.LowPart <
4937 (blocksRequired * This->parentStorage->bigBlockSize))
4939 rootProp.size.s.LowPart += This->parentStorage->bigBlockSize;
4941 BlockChainStream_SetSize(
4942 This->parentStorage->smallBlockRootChain,
4945 StorageImpl_WriteProperty(
4946 This->parentStorage,
4947 This->parentStorage->rootPropertySetIndex,
4955 /******************************************************************************
4956 * SmallBlockChainStream_ReadAt
4958 * Reads a specified number of bytes from this chain at the specified offset.
4959 * bytesRead may be NULL.
4960 * Failure will be returned if the specified number of bytes has not been read.
4962 BOOL SmallBlockChainStream_ReadAt(
4963 SmallBlockChainStream* This,
4964 ULARGE_INTEGER offset,
4969 ULARGE_INTEGER offsetInBigBlockFile;
4970 ULONG blockNoInSequence =
4971 offset.s.LowPart / This->parentStorage->smallBlockSize;
4973 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
4974 ULONG bytesToReadInBuffer;
4976 ULONG bytesReadFromBigBlockFile;
4980 * This should never happen on a small block file.
4982 assert(offset.s.HighPart==0);
4985 * Find the first block in the stream that contains part of the buffer.
4987 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4989 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4991 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4993 blockNoInSequence--;
4997 * Start reading the buffer.
5000 bufferWalker = buffer;
5002 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5005 * Calculate how many bytes we can copy from this small block.
5007 bytesToReadInBuffer =
5008 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5011 * Calculate the offset of the small block in the small block file.
5013 offsetInBigBlockFile.s.HighPart = 0;
5014 offsetInBigBlockFile.s.LowPart =
5015 blockIndex * This->parentStorage->smallBlockSize;
5017 offsetInBigBlockFile.s.LowPart += offsetInBlock;
5020 * Read those bytes in the buffer from the small block file.
5022 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5023 offsetInBigBlockFile,
5024 bytesToReadInBuffer,
5026 &bytesReadFromBigBlockFile);
5028 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5031 * Step to the next big block.
5033 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5034 bufferWalker += bytesToReadInBuffer;
5035 size -= bytesToReadInBuffer;
5036 *bytesRead += bytesToReadInBuffer;
5037 offsetInBlock = 0; /* There is no offset on the next block */
5043 /******************************************************************************
5044 * SmallBlockChainStream_WriteAt
5046 * Writes the specified number of bytes to this chain at the specified offset.
5047 * bytesWritten may be NULL.
5048 * Will fail if not all specified number of bytes have been written.
5050 BOOL SmallBlockChainStream_WriteAt(
5051 SmallBlockChainStream* This,
5052 ULARGE_INTEGER offset,
5055 ULONG* bytesWritten)
5057 ULARGE_INTEGER offsetInBigBlockFile;
5058 ULONG blockNoInSequence =
5059 offset.s.LowPart / This->parentStorage->smallBlockSize;
5061 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
5062 ULONG bytesToWriteInBuffer;
5064 ULONG bytesWrittenFromBigBlockFile;
5068 * This should never happen on a small block file.
5070 assert(offset.s.HighPart==0);
5073 * Find the first block in the stream that contains part of the buffer.
5075 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5077 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5079 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5081 blockNoInSequence--;
5085 * Start writing the buffer.
5087 * Here, I'm casting away the constness on the buffer variable
5088 * This is OK since we don't intend to modify that buffer.
5091 bufferWalker = (BYTE*)buffer;
5092 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5095 * Calculate how many bytes we can copy to this small block.
5097 bytesToWriteInBuffer =
5098 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5101 * Calculate the offset of the small block in the small block file.
5103 offsetInBigBlockFile.s.HighPart = 0;
5104 offsetInBigBlockFile.s.LowPart =
5105 blockIndex * This->parentStorage->smallBlockSize;
5107 offsetInBigBlockFile.s.LowPart += offsetInBlock;
5110 * Write those bytes in the buffer to the small block file.
5112 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5113 offsetInBigBlockFile,
5114 bytesToWriteInBuffer,
5116 &bytesWrittenFromBigBlockFile);
5118 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5121 * Step to the next big block.
5123 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5124 bufferWalker += bytesToWriteInBuffer;
5125 size -= bytesToWriteInBuffer;
5126 *bytesWritten += bytesToWriteInBuffer;
5127 offsetInBlock = 0; /* There is no offset on the next block */
5133 /******************************************************************************
5134 * SmallBlockChainStream_Shrink
5136 * Shrinks this chain in the small block depot.
5138 BOOL SmallBlockChainStream_Shrink(
5139 SmallBlockChainStream* This,
5140 ULARGE_INTEGER newSize)
5142 ULONG blockIndex, extraBlock;
5146 numBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5148 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5151 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5154 * Go to the new end of chain
5156 while (count < numBlocks)
5158 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5163 * If the count is 0, we have a special case, the head of the chain was
5168 StgProperty chainProp;
5170 StorageImpl_ReadProperty(This->parentStorage,
5171 This->ownerPropertyIndex,
5174 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5176 StorageImpl_WriteProperty(This->parentStorage,
5177 This->ownerPropertyIndex,
5181 * We start freeing the chain at the head block.
5183 extraBlock = blockIndex;
5187 /* Get the next block before marking the new end */
5188 extraBlock = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5190 /* Mark the new end of chain */
5191 SmallBlockChainStream_SetNextBlockInChain(
5194 BLOCK_END_OF_CHAIN);
5198 * Mark the extra blocks as free
5200 while (extraBlock != BLOCK_END_OF_CHAIN)
5202 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, extraBlock);
5203 SmallBlockChainStream_FreeBlock(This, extraBlock);
5204 extraBlock = blockIndex;
5210 /******************************************************************************
5211 * SmallBlockChainStream_Enlarge
5213 * Grows this chain in the small block depot.
5215 BOOL SmallBlockChainStream_Enlarge(
5216 SmallBlockChainStream* This,
5217 ULARGE_INTEGER newSize)
5219 ULONG blockIndex, currentBlock;
5221 ULONG oldNumBlocks = 0;
5223 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5228 if (blockIndex == BLOCK_END_OF_CHAIN)
5231 StgProperty chainProp;
5233 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5236 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5238 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5241 blockIndex = chainProp.startingBlock;
5242 SmallBlockChainStream_SetNextBlockInChain(
5245 BLOCK_END_OF_CHAIN);
5248 currentBlock = blockIndex;
5251 * Figure out how many blocks are needed to contain this stream
5253 newNumBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5255 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5259 * Go to the current end of chain
5261 while (blockIndex != BLOCK_END_OF_CHAIN)
5264 currentBlock = blockIndex;
5265 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, currentBlock);
5269 * Add new blocks to the chain
5271 while (oldNumBlocks < newNumBlocks)
5273 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5274 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5276 SmallBlockChainStream_SetNextBlockInChain(
5279 BLOCK_END_OF_CHAIN);
5281 currentBlock = blockIndex;
5288 /******************************************************************************
5289 * SmallBlockChainStream_GetCount
5291 * Returns the number of blocks that comprises this chain.
5292 * This is not the size of this chain as the last block may not be full!
5294 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5299 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5301 while (blockIndex != BLOCK_END_OF_CHAIN)
5305 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5311 /******************************************************************************
5312 * SmallBlockChainStream_SetSize
5314 * Sets the size of this stream.
5315 * The file will grow if we grow the chain.
5317 * TODO: Free the actual blocks in the file when we shrink the chain.
5318 * Currently, the blocks are still in the file. So the file size
5319 * doesn't shrink even if we shrink streams.
5321 BOOL SmallBlockChainStream_SetSize(
5322 SmallBlockChainStream* This,
5323 ULARGE_INTEGER newSize)
5325 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5327 if (newSize.s.LowPart == size.s.LowPart)
5330 if (newSize.s.LowPart < size.s.LowPart)
5332 SmallBlockChainStream_Shrink(This, newSize);
5336 SmallBlockChainStream_Enlarge(This, newSize);
5342 /******************************************************************************
5343 * SmallBlockChainStream_GetSize
5345 * Returns the size of this chain.
5347 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5349 StgProperty chainProperty;
5351 StorageImpl_ReadProperty(
5352 This->parentStorage,
5353 This->ownerPropertyIndex,
5356 return chainProperty.size;
5359 /******************************************************************************
5360 * StgCreateDocfile [OLE32.144]
5362 HRESULT WINAPI StgCreateDocfile(
5366 IStorage **ppstgOpen)
5368 StorageImpl* newStorage = 0;
5369 HANDLE hFile = INVALID_HANDLE_VALUE;
5374 DWORD fileAttributes;
5375 WCHAR tempFileName[MAX_PATH];
5377 TRACE("(%s, %lx, %ld, %p)\n",
5378 debugstr_w(pwcsName), grfMode,
5379 reserved, ppstgOpen);
5382 * Validate the parameters
5385 return STG_E_INVALIDPOINTER;
5388 * Validate the STGM flags
5390 if ( FAILED( validateSTGM(grfMode) ))
5391 return STG_E_INVALIDFLAG;
5394 * Generate a unique name.
5398 WCHAR tempPath[MAX_PATH];
5399 WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5401 if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5402 return STG_E_INVALIDFLAG;
5403 if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5404 return STG_E_INVALIDFLAG;
5406 memset(tempPath, 0, sizeof(tempPath));
5407 memset(tempFileName, 0, sizeof(tempFileName));
5409 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5412 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5413 pwcsName = tempFileName;
5415 return STG_E_INSUFFICIENTMEMORY;
5417 creationMode = TRUNCATE_EXISTING;
5421 creationMode = GetCreationModeFromSTGM(grfMode);
5425 * Interpret the STGM value grfMode
5427 shareMode = GetShareModeFromSTGM(grfMode);
5428 accessMode = GetAccessModeFromSTGM(grfMode);
5430 if (grfMode & STGM_DELETEONRELEASE)
5431 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5433 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5435 if (grfMode & STGM_TRANSACTED)
5436 FIXME("Transacted mode not implemented.\n");
5439 * Initialize the "out" parameter.
5443 hFile = CreateFileW(pwcsName,
5451 if (hFile == INVALID_HANDLE_VALUE)
5457 * Allocate and initialize the new IStorage32object.
5459 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5461 if (newStorage == 0)
5462 return STG_E_INSUFFICIENTMEMORY;
5464 hr = StorageImpl_Construct(
5475 HeapFree(GetProcessHeap(), 0, newStorage);
5480 * Get an "out" pointer for the caller.
5482 hr = StorageBaseImpl_QueryInterface(
5483 (IStorage*)newStorage,
5484 (REFIID)&IID_IStorage,
5490 /******************************************************************************
5491 * StgOpenStorage [OLE32.148]
5493 HRESULT WINAPI StgOpenStorage(
5494 const OLECHAR *pwcsName,
5495 IStorage *pstgPriority,
5499 IStorage **ppstgOpen)
5501 StorageImpl* newStorage = 0;
5506 WCHAR fullname[MAX_PATH];
5507 WIN32_FILE_ATTRIBUTE_DATA Fad;
5509 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5510 debugstr_w(pwcsName), pstgPriority, grfMode,
5511 snbExclude, reserved, ppstgOpen);
5514 * Perform a sanity check
5516 if (( pwcsName == 0) || (ppstgOpen == 0) )
5518 hr = STG_E_INVALIDPOINTER;
5523 * Validate the STGM flags
5525 if ( FAILED( validateSTGM(grfMode) ))
5527 hr = STG_E_INVALIDFLAG;
5532 * Interpret the STGM value grfMode
5534 shareMode = GetShareModeFromSTGM(grfMode);
5535 accessMode = GetAccessModeFromSTGM(grfMode);
5538 * Initialize the "out" parameter.
5542 hFile = CreateFileW( pwcsName,
5547 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5551 if (hFile==INVALID_HANDLE_VALUE)
5553 DWORD last_error = GetLastError();
5559 case ERROR_FILE_NOT_FOUND:
5560 hr = STG_E_FILENOTFOUND;
5563 case ERROR_PATH_NOT_FOUND:
5564 hr = STG_E_PATHNOTFOUND;
5567 case ERROR_ACCESS_DENIED:
5568 case ERROR_WRITE_PROTECT:
5569 hr = STG_E_ACCESSDENIED;
5572 case ERROR_SHARING_VIOLATION:
5573 hr = STG_E_SHAREVIOLATION;
5584 * Allocate and initialize the new IStorage32object.
5586 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5588 if (newStorage == 0)
5590 hr = STG_E_INSUFFICIENTMEMORY;
5594 /* if the file's length was zero, initialize the storage */
5595 hr = StorageImpl_Construct(
5602 !(Fad.nFileSizeHigh || Fad.nFileSizeLow) /* FALSE */ );
5606 HeapFree(GetProcessHeap(), 0, newStorage);
5608 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5610 if(hr == STG_E_INVALIDHEADER)
5611 hr = STG_E_FILEALREADYEXISTS;
5615 /* prepare the file name string given in lieu of the root property name */
5616 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5617 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5618 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5621 * Get an "out" pointer for the caller.
5623 hr = StorageBaseImpl_QueryInterface(
5624 (IStorage*)newStorage,
5625 (REFIID)&IID_IStorage,
5629 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5633 /******************************************************************************
5634 * StgCreateDocfileOnILockBytes [OLE32.145]
5636 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5640 IStorage** ppstgOpen)
5642 StorageImpl* newStorage = 0;
5646 * Validate the parameters
5648 if ((ppstgOpen == 0) || (plkbyt == 0))
5649 return STG_E_INVALIDPOINTER;
5652 * Allocate and initialize the new IStorage object.
5654 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5656 if (newStorage == 0)
5657 return STG_E_INSUFFICIENTMEMORY;
5659 hr = StorageImpl_Construct(
5670 HeapFree(GetProcessHeap(), 0, newStorage);
5675 * Get an "out" pointer for the caller.
5677 hr = StorageBaseImpl_QueryInterface(
5678 (IStorage*)newStorage,
5679 (REFIID)&IID_IStorage,
5685 /******************************************************************************
5686 * StgOpenStorageOnILockBytes [OLE32.149]
5688 HRESULT WINAPI StgOpenStorageOnILockBytes(
5690 IStorage *pstgPriority,
5694 IStorage **ppstgOpen)
5696 StorageImpl* newStorage = 0;
5700 * Perform a sanity check
5702 if ((plkbyt == 0) || (ppstgOpen == 0))
5703 return STG_E_INVALIDPOINTER;
5706 * Validate the STGM flags
5708 if ( FAILED( validateSTGM(grfMode) ))
5709 return STG_E_INVALIDFLAG;
5712 * Initialize the "out" parameter.
5717 * Allocate and initialize the new IStorage object.
5719 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5721 if (newStorage == 0)
5722 return STG_E_INSUFFICIENTMEMORY;
5724 hr = StorageImpl_Construct(
5735 HeapFree(GetProcessHeap(), 0, newStorage);
5740 * Get an "out" pointer for the caller.
5742 hr = StorageBaseImpl_QueryInterface(
5743 (IStorage*)newStorage,
5744 (REFIID)&IID_IStorage,
5750 /******************************************************************************
5751 * StgSetTimes [ole32.150]
5752 * StgSetTimes [OLE32.150]
5756 HRESULT WINAPI StgSetTimes(OLECHAR *str, FILETIME *a, FILETIME *b, FILETIME *c )
5758 FIXME("(%s, %p, %p, %p),stub!\n", debugstr_w(str), a, b, c);
5762 /******************************************************************************
5763 * StgIsStorageILockBytes [OLE32.147]
5765 * Determines if the ILockBytes contains a storage object.
5767 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5770 ULARGE_INTEGER offset;
5772 offset.s.HighPart = 0;
5773 offset.s.LowPart = 0;
5775 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5777 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5783 /******************************************************************************
5784 * WriteClassStg [OLE32.158]
5786 * This method will store the specified CLSID in the specified storage object
5788 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5794 hRes = IStorage_SetClass(pStg, rclsid);
5799 /***********************************************************************
5800 * ReadClassStg (OLE32.134)
5802 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5804 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5814 * read a STATSTG structure (contains the clsid) from the storage
5816 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5819 *pclsid=pstatstg.clsid;
5824 /***********************************************************************
5825 * OleLoadFromStream (OLE32.113)
5827 * This function loads an object from stream
5829 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5833 LPPERSISTSTREAM xstm;
5835 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
5837 res=ReadClassStm(pStm,&clsid);
5838 if (!SUCCEEDED(res))
5840 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5841 if (!SUCCEEDED(res))
5843 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
5844 if (!SUCCEEDED(res)) {
5845 IUnknown_Release((IUnknown*)*ppvObj);
5848 res=IPersistStream_Load(xstm,pStm);
5849 IPersistStream_Release(xstm);
5850 /* FIXME: all refcounts ok at this point? I think they should be:
5853 * xstm : 0 (released)
5858 /***********************************************************************
5859 * OleSaveToStream (OLE32.125)
5861 * This function saves an object with the IPersistStream interface on it
5862 * to the specified stream.
5864 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5870 TRACE("(%p,%p)\n",pPStm,pStm);
5872 res=IPersistStream_GetClassID(pPStm,&clsid);
5874 if (SUCCEEDED(res)){
5876 res=WriteClassStm(pStm,&clsid);
5880 res=IPersistStream_Save(pPStm,pStm,TRUE);
5883 TRACE("Finished Save\n");
5887 /****************************************************************************
5888 * This method validate a STGM parameter that can contain the values below
5890 * STGM_DIRECT 0x00000000
5891 * STGM_TRANSACTED 0x00010000
5892 * STGM_SIMPLE 0x08000000
5894 * STGM_READ 0x00000000
5895 * STGM_WRITE 0x00000001
5896 * STGM_READWRITE 0x00000002
5898 * STGM_SHARE_DENY_NONE 0x00000040
5899 * STGM_SHARE_DENY_READ 0x00000030
5900 * STGM_SHARE_DENY_WRITE 0x00000020
5901 * STGM_SHARE_EXCLUSIVE 0x00000010
5903 * STGM_PRIORITY 0x00040000
5904 * STGM_DELETEONRELEASE 0x04000000
5906 * STGM_CREATE 0x00001000
5907 * STGM_CONVERT 0x00020000
5908 * STGM_FAILIFTHERE 0x00000000
5910 * STGM_NOSCRATCH 0x00100000
5911 * STGM_NOSNAPSHOT 0x00200000
5913 static HRESULT validateSTGM(DWORD stgm)
5915 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5916 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5917 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5919 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5920 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5921 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5923 BOOL bSTGM_SHARE_DENY_NONE =
5924 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5926 BOOL bSTGM_SHARE_DENY_READ =
5927 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5929 BOOL bSTGM_SHARE_DENY_WRITE =
5930 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5932 BOOL bSTGM_SHARE_EXCLUSIVE =
5933 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5935 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5936 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5938 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5939 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5942 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5944 if ( ! bSTGM_DIRECT )
5945 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5949 * STGM_WRITE | STGM_READWRITE | STGM_READ
5952 if( bSTGM_WRITE && bSTGM_READWRITE )
5956 * STGM_SHARE_DENY_NONE | others
5957 * (I assume here that DENY_READ implies DENY_WRITE)
5959 if ( bSTGM_SHARE_DENY_NONE )
5960 if ( bSTGM_SHARE_DENY_READ ||
5961 bSTGM_SHARE_DENY_WRITE ||
5962 bSTGM_SHARE_EXCLUSIVE)
5966 * STGM_CREATE | STGM_CONVERT
5967 * if both are false, STGM_FAILIFTHERE is set to TRUE
5969 if ( bSTGM_CREATE && bSTGM_CONVERT )
5973 * STGM_NOSCRATCH requires STGM_TRANSACTED
5975 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
5979 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
5980 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5982 if (bSTGM_NOSNAPSHOT)
5984 if ( ! ( bSTGM_TRANSACTED &&
5985 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
5992 /****************************************************************************
5993 * GetShareModeFromSTGM
5995 * This method will return a share mode flag from a STGM value.
5996 * The STGM value is assumed valid.
5998 static DWORD GetShareModeFromSTGM(DWORD stgm)
6000 DWORD dwShareMode = 0;
6001 BOOL bSTGM_SHARE_DENY_NONE =
6002 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
6004 BOOL bSTGM_SHARE_DENY_READ =
6005 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
6007 BOOL bSTGM_SHARE_DENY_WRITE =
6008 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
6010 BOOL bSTGM_SHARE_EXCLUSIVE =
6011 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
6013 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
6016 if (bSTGM_SHARE_DENY_NONE)
6017 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6019 if (bSTGM_SHARE_DENY_WRITE)
6020 dwShareMode = FILE_SHARE_READ;
6025 /****************************************************************************
6026 * GetAccessModeFromSTGM
6028 * This method will return an access mode flag from a STGM value.
6029 * The STGM value is assumed valid.
6031 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6033 DWORD dwDesiredAccess = GENERIC_READ;
6034 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
6035 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
6036 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
6039 dwDesiredAccess = GENERIC_READ;
6042 dwDesiredAccess |= GENERIC_WRITE;
6044 if (bSTGM_READWRITE)
6045 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
6047 return dwDesiredAccess;
6050 /****************************************************************************
6051 * GetCreationModeFromSTGM
6053 * This method will return a creation mode flag from a STGM value.
6054 * The STGM value is assumed valid.
6056 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6058 if ( stgm & STGM_CREATE)
6059 return CREATE_ALWAYS;
6060 if (stgm & STGM_CONVERT) {
6061 FIXME("STGM_CONVERT not implemented!\n");
6064 /* All other cases */
6065 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
6066 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
6071 /*************************************************************************
6072 * OLECONVERT_LoadOLE10 [Internal]
6074 * Loads the OLE10 STREAM to memory
6077 * pOleStream [I] The OLESTREAM
6078 * pData [I] Data Structure for the OLESTREAM Data
6082 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6083 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6086 * This function is used by OleConvertOLESTREAMToIStorage only.
6088 * Memory allocated for pData must be freed by the caller
6090 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6093 HRESULT hRes = S_OK;
6097 pData->pData = NULL;
6098 pData->pstrOleObjFileName = (CHAR *) NULL;
6100 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6103 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6104 if(dwSize != sizeof(pData->dwOleID))
6106 hRes = CONVERT10_E_OLESTREAM_GET;
6108 else if(pData->dwOleID != OLESTREAM_ID)
6110 hRes = CONVERT10_E_OLESTREAM_FMT;
6121 /* Get the TypeID...more info needed for this field */
6122 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6123 if(dwSize != sizeof(pData->dwTypeID))
6125 hRes = CONVERT10_E_OLESTREAM_GET;
6130 if(pData->dwTypeID != 0)
6132 /* Get the lenght of the OleTypeName */
6133 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6134 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6136 hRes = CONVERT10_E_OLESTREAM_GET;
6141 if(pData->dwOleTypeNameLength > 0)
6143 /* Get the OleTypeName */
6144 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6145 if(dwSize != pData->dwOleTypeNameLength)
6147 hRes = CONVERT10_E_OLESTREAM_GET;
6153 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6154 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6156 hRes = CONVERT10_E_OLESTREAM_GET;
6160 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6161 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6162 pData->pstrOleObjFileName = (CHAR *)malloc(pData->dwOleObjFileNameLength);
6163 if(pData->pstrOleObjFileName)
6165 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6166 if(dwSize != pData->dwOleObjFileNameLength)
6168 hRes = CONVERT10_E_OLESTREAM_GET;
6172 hRes = CONVERT10_E_OLESTREAM_GET;
6177 /* Get the Width of the Metafile */
6178 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6179 if(dwSize != sizeof(pData->dwMetaFileWidth))
6181 hRes = CONVERT10_E_OLESTREAM_GET;
6185 /* Get the Height of the Metafile */
6186 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6187 if(dwSize != sizeof(pData->dwMetaFileHeight))
6189 hRes = CONVERT10_E_OLESTREAM_GET;
6195 /* Get the Lenght of the Data */
6196 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6197 if(dwSize != sizeof(pData->dwDataLength))
6199 hRes = CONVERT10_E_OLESTREAM_GET;
6203 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6205 if(!bStrem1) /* if it is a second OLE stream data */
6207 pData->dwDataLength -= 8;
6208 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6209 if(dwSize != sizeof(pData->strUnknown))
6211 hRes = CONVERT10_E_OLESTREAM_GET;
6217 if(pData->dwDataLength > 0)
6219 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6221 /* Get Data (ex. IStorage, Metafile, or BMP) */
6224 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6225 if(dwSize != pData->dwDataLength)
6227 hRes = CONVERT10_E_OLESTREAM_GET;
6232 hRes = CONVERT10_E_OLESTREAM_GET;
6241 /*************************************************************************
6242 * OLECONVERT_SaveOLE10 [Internal]
6244 * Saves the OLE10 STREAM From memory
6247 * pData [I] Data Structure for the OLESTREAM Data
6248 * pOleStream [I] The OLESTREAM to save
6252 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6255 * This function is used by OleConvertIStorageToOLESTREAM only.
6258 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6261 HRESULT hRes = S_OK;
6265 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6266 if(dwSize != sizeof(pData->dwOleID))
6268 hRes = CONVERT10_E_OLESTREAM_PUT;
6273 /* Set the TypeID */
6274 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6275 if(dwSize != sizeof(pData->dwTypeID))
6277 hRes = CONVERT10_E_OLESTREAM_PUT;
6281 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6283 /* Set the Lenght of the OleTypeName */
6284 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6285 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6287 hRes = CONVERT10_E_OLESTREAM_PUT;
6292 if(pData->dwOleTypeNameLength > 0)
6294 /* Set the OleTypeName */
6295 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6296 if(dwSize != pData->dwOleTypeNameLength)
6298 hRes = CONVERT10_E_OLESTREAM_PUT;
6305 /* Set the width of the Metafile */
6306 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6307 if(dwSize != sizeof(pData->dwMetaFileWidth))
6309 hRes = CONVERT10_E_OLESTREAM_PUT;
6315 /* Set the height of the Metafile */
6316 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6317 if(dwSize != sizeof(pData->dwMetaFileHeight))
6319 hRes = CONVERT10_E_OLESTREAM_PUT;
6325 /* Set the lenght of the Data */
6326 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6327 if(dwSize != sizeof(pData->dwDataLength))
6329 hRes = CONVERT10_E_OLESTREAM_PUT;
6335 if(pData->dwDataLength > 0)
6337 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6338 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6339 if(dwSize != pData->dwDataLength)
6341 hRes = CONVERT10_E_OLESTREAM_PUT;
6349 /*************************************************************************
6350 * OLECONVERT_GetOLE20FromOLE10[Internal]
6352 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6353 * opens it, and copies the content to the dest IStorage for
6354 * OleConvertOLESTREAMToIStorage
6358 * pDestStorage [I] The IStorage to copy the data to
6359 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6360 * nBufferLength [I] The size of the buffer
6369 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6373 IStorage *pTempStorage;
6374 DWORD dwNumOfBytesWritten;
6375 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6376 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6378 /* Create a temp File */
6379 GetTempPathW(MAX_PATH, wstrTempDir);
6380 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6381 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6383 if(hFile != INVALID_HANDLE_VALUE)
6385 /* Write IStorage Data to File */
6386 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6389 /* Open and copy temp storage to the Dest Storage */
6390 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6393 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6394 StorageBaseImpl_Release(pTempStorage);
6396 DeleteFileW(wstrTempFile);
6401 /*************************************************************************
6402 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6404 * Saves the OLE10 STREAM From memory
6407 * pStorage [I] The Src IStorage to copy
6408 * pData [I] The Dest Memory to write to.
6411 * The size in bytes allocated for pData
6414 * Memory allocated for pData must be freed by the caller
6416 * Used by OleConvertIStorageToOLESTREAM only.
6419 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6423 DWORD nDataLength = 0;
6424 IStorage *pTempStorage;
6425 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6426 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6430 /* Create temp Storage */
6431 GetTempPathW(MAX_PATH, wstrTempDir);
6432 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6433 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6437 /* Copy Src Storage to the Temp Storage */
6438 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6439 StorageBaseImpl_Release(pTempStorage);
6441 /* Open Temp Storage as a file and copy to memory */
6442 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6443 if(hFile != INVALID_HANDLE_VALUE)
6445 nDataLength = GetFileSize(hFile, NULL);
6446 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6447 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6450 DeleteFileW(wstrTempFile);
6455 /*************************************************************************
6456 * OLECONVERT_CreateOleStream [Internal]
6458 * Creates the "\001OLE" stream in the IStorage if neccessary.
6461 * pStorage [I] Dest storage to create the stream in
6467 * This function is used by OleConvertOLESTREAMToIStorage only.
6469 * This stream is still unknown, MS Word seems to have extra data
6470 * but since the data is stored in the OLESTREAM there should be
6471 * no need to recreate the stream. If the stream is manually
6472 * deleted it will create it with this default data.
6475 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6479 WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6480 BYTE pOleStreamHeader [] =
6482 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6483 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6484 0x00, 0x00, 0x00, 0x00
6487 /* Create stream if not present */
6488 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6489 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6493 /* Write default Data */
6494 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6495 IStream_Release(pStream);
6500 /*************************************************************************
6501 * OLECONVERT_CreateCompObjStream [Internal]
6503 * Creates a "\001CompObj" is the destination IStorage if necessary.
6506 * pStorage [I] The dest IStorage to create the CompObj Stream
6508 * strOleTypeName [I] The ProgID
6512 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6515 * This function is used by OleConvertOLESTREAMToIStorage only.
6517 * The stream data is stored in the OLESTREAM and there should be
6518 * no need to recreate the stream. If the stream is manually
6519 * deleted it will attempt to create it by querying the registry.
6523 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6526 HRESULT hStorageRes, hRes = S_OK;
6527 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6528 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6530 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6531 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6533 /* Initialize the CompObj structure */
6534 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6535 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6536 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6539 /* Create a CompObj stream if it doesn't exist */
6540 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6541 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6542 if(hStorageRes == S_OK)
6544 /* copy the OleTypeName to the compobj struct */
6545 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6546 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6548 /* copy the OleTypeName to the compobj struct */
6549 /* Note: in the test made, these were Identical */
6550 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6551 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6554 hRes = CLSIDFromProgID16(IStorageCompObj.strProgIDName, &(IStorageCompObj.clsid));
6560 /* Get the CLSID Default Name from the Registry */
6561 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6562 if(hErr == ERROR_SUCCESS)
6564 char strTemp[OLESTREAM_MAX_STR_LEN];
6565 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6566 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6567 if(hErr == ERROR_SUCCESS)
6569 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6575 /* Write CompObj Structure to stream */
6576 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6578 WriteClassStm(pStream,&(IStorageCompObj.clsid));
6580 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6581 if(IStorageCompObj.dwCLSIDNameLength > 0)
6583 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6585 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6586 if(IStorageCompObj.dwOleTypeNameLength > 0)
6588 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6590 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6591 if(IStorageCompObj.dwProgIDNameLength > 0)
6593 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6595 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6596 IStream_Release(pStream);
6602 /*************************************************************************
6603 * OLECONVERT_CreateOlePresStream[Internal]
6605 * Creates the "\002OlePres000" Stream with the Metafile data
6608 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6609 * dwExtentX [I] Width of the Metafile
6610 * dwExtentY [I] Height of the Metafile
6611 * pData [I] Metafile data
6612 * dwDataLength [I] Size of the Metafile data
6616 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6619 * This function is used by OleConvertOLESTREAMToIStorage only.
6622 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6626 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6627 BYTE pOlePresStreamHeader [] =
6629 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6630 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6631 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6632 0x00, 0x00, 0x00, 0x00
6635 BYTE pOlePresStreamHeaderEmpty [] =
6637 0x00, 0x00, 0x00, 0x00,
6638 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6639 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6640 0x00, 0x00, 0x00, 0x00
6643 /* Create the OlePres000 Stream */
6644 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6645 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6650 OLECONVERT_ISTORAGE_OLEPRES OlePres;
6652 memset(&OlePres, 0, sizeof(OlePres));
6653 /* Do we have any metafile data to save */
6654 if(dwDataLength > 0)
6656 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6657 nHeaderSize = sizeof(pOlePresStreamHeader);
6661 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
6662 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
6664 /* Set width and height of the metafile */
6665 OlePres.dwExtentX = dwExtentX;
6666 OlePres.dwExtentY = -dwExtentY;
6668 /* Set Data and Lenght */
6669 if(dwDataLength > sizeof(METAFILEPICT16))
6671 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
6672 OlePres.pData = &(pData[8]);
6674 /* Save OlePres000 Data to Stream */
6675 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
6676 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
6677 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
6678 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
6679 if(OlePres.dwSize > 0)
6681 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
6683 IStream_Release(pStream);
6687 /*************************************************************************
6688 * OLECONVERT_CreateOle10NativeStream [Internal]
6690 * Creates the "\001Ole10Native" Stream (should contain a BMP)
6693 * pStorage [I] Dest storage to create the stream in
6694 * pData [I] Ole10 Native Data (ex. bmp)
6695 * dwDataLength [I] Size of the Ole10 Native Data
6701 * This function is used by OleConvertOLESTREAMToIStorage only.
6703 * Might need to verify the data and return appropriate error message
6706 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
6710 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6712 /* Create the Ole10Native Stream */
6713 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6714 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6718 /* Write info to stream */
6719 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
6720 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
6721 IStream_Release(pStream);
6726 /*************************************************************************
6727 * OLECONVERT_GetOLE10ProgID [Internal]
6729 * Finds the ProgID (or OleTypeID) from the IStorage
6732 * pStorage [I] The Src IStorage to get the ProgID
6733 * strProgID [I] the ProgID string to get
6734 * dwSize [I] the size of the string
6738 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6741 * This function is used by OleConvertIStorageToOLESTREAM only.
6745 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
6749 LARGE_INTEGER iSeekPos;
6750 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
6751 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6753 /* Open the CompObj Stream */
6754 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6755 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6759 /*Get the OleType from the CompObj Stream */
6760 iSeekPos.s.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
6761 iSeekPos.s.HighPart = 0;
6763 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6764 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
6765 iSeekPos.s.LowPart = CompObj.dwCLSIDNameLength;
6766 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6767 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
6768 iSeekPos.s.LowPart = CompObj.dwOleTypeNameLength;
6769 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6771 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
6774 IStream_Read(pStream, strProgID, *dwSize, NULL);
6776 IStream_Release(pStream);
6781 LPOLESTR wstrProgID;
6783 /* Get the OleType from the registry */
6784 REFCLSID clsid = &(stat.clsid);
6785 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
6786 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
6789 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
6796 /*************************************************************************
6797 * OLECONVERT_GetOle10PresData [Internal]
6799 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
6802 * pStorage [I] Src IStroage
6803 * pOleStream [I] Dest OleStream Mem Struct
6809 * This function is used by OleConvertIStorageToOLESTREAM only.
6811 * Memory allocated for pData must be freed by the caller
6815 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6820 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6822 /* Initialize Default data for OLESTREAM */
6823 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6824 pOleStreamData[0].dwTypeID = 2;
6825 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6826 pOleStreamData[1].dwTypeID = 0;
6827 pOleStreamData[0].dwMetaFileWidth = 0;
6828 pOleStreamData[0].dwMetaFileHeight = 0;
6829 pOleStreamData[0].pData = NULL;
6830 pOleStreamData[1].pData = NULL;
6832 /* Open Ole10Native Stream */
6833 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6834 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6838 /* Read Size and Data */
6839 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
6840 if(pOleStreamData->dwDataLength > 0)
6842 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
6843 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
6845 IStream_Release(pStream);
6851 /*************************************************************************
6852 * OLECONVERT_GetOle20PresData[Internal]
6854 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
6857 * pStorage [I] Src IStroage
6858 * pOleStreamData [I] Dest OleStream Mem Struct
6864 * This function is used by OleConvertIStorageToOLESTREAM only.
6866 * Memory allocated for pData must be freed by the caller
6868 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6872 OLECONVERT_ISTORAGE_OLEPRES olePress;
6873 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6875 /* Initialize Default data for OLESTREAM */
6876 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6877 pOleStreamData[0].dwTypeID = 2;
6878 pOleStreamData[0].dwMetaFileWidth = 0;
6879 pOleStreamData[0].dwMetaFileHeight = 0;
6880 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
6881 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6882 pOleStreamData[1].dwTypeID = 0;
6883 pOleStreamData[1].dwOleTypeNameLength = 0;
6884 pOleStreamData[1].strOleTypeName[0] = 0;
6885 pOleStreamData[1].dwMetaFileWidth = 0;
6886 pOleStreamData[1].dwMetaFileHeight = 0;
6887 pOleStreamData[1].pData = NULL;
6888 pOleStreamData[1].dwDataLength = 0;
6891 /* Open OlePress000 stream */
6892 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6893 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6896 LARGE_INTEGER iSeekPos;
6897 METAFILEPICT16 MetaFilePict;
6898 char strMetafilePictName[] = "METAFILEPICT";
6900 /* Set the TypeID for a Metafile */
6901 pOleStreamData[1].dwTypeID = 5;
6903 /* Set the OleTypeName to Metafile */
6904 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
6905 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
6907 iSeekPos.s.HighPart = 0;
6908 iSeekPos.s.LowPart = sizeof(olePress.byUnknown1);
6910 /* Get Presentation Data */
6911 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6912 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
6913 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
6914 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
6916 /*Set width and Height */
6917 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
6918 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
6919 if(olePress.dwSize > 0)
6922 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
6924 /* Set MetaFilePict struct */
6925 MetaFilePict.mm = 8;
6926 MetaFilePict.xExt = olePress.dwExtentX;
6927 MetaFilePict.yExt = olePress.dwExtentY;
6928 MetaFilePict.hMF = 0;
6930 /* Get Metafile Data */
6931 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
6932 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
6933 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
6935 IStream_Release(pStream);
6939 /*************************************************************************
6940 * OleConvertOLESTREAMToIStorage [OLE32.87]
6945 * DVTARGETDEVICE paramenter is not handled
6946 * Still unsure of some mem fields for OLE 10 Stream
6947 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6948 * and "\001OLE" streams
6951 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
6952 LPOLESTREAM pOleStream,
6954 const DVTARGETDEVICE* ptd)
6958 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
6960 memset(pOleStreamData, 0, sizeof(pOleStreamData));
6964 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
6967 if(pstg == NULL || pOleStream == NULL)
6969 hRes = E_INVALIDARG;
6974 /* Load the OLESTREAM to Memory */
6975 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
6980 /* Load the OLESTREAM to Memory (part 2)*/
6981 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
6987 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
6989 /* Do we have the IStorage Data in the OLESTREAM */
6990 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
6992 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6993 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
6997 /* It must be an original OLE 1.0 source */
6998 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7003 /* It must be an original OLE 1.0 source */
7004 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7007 /* Create CompObj Stream if necessary */
7008 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7011 /*Create the Ole Stream if necessary */
7012 OLECONVERT_CreateOleStream(pstg);
7017 /* Free allocated memory */
7018 for(i=0; i < 2; i++)
7020 if(pOleStreamData[i].pData != NULL)
7022 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7024 if(pOleStreamData[i].pstrOleObjFileName != NULL)
7026 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7027 pOleStreamData[i].pstrOleObjFileName = NULL;
7033 /*************************************************************************
7034 * OleConvertIStorageToOLESTREAM [OLE32.85]
7041 * Still unsure of some mem fields for OLE 10 Stream
7042 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7043 * and "\001OLE" streams.
7046 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7048 LPOLESTREAM pOleStream)
7051 HRESULT hRes = S_OK;
7053 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7054 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7057 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7059 if(pstg == NULL || pOleStream == NULL)
7061 hRes = E_INVALIDARG;
7065 /* Get the ProgID */
7066 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7067 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7071 /* Was it originally Ole10 */
7072 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7075 IStream_Release(pStream);
7076 /* Get Presentation Data for Ole10Native */
7077 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7081 /* Get Presentation Data (OLE20) */
7082 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7085 /* Save OLESTREAM */
7086 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7089 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7094 /* Free allocated memory */
7095 for(i=0; i < 2; i++)
7097 if(pOleStreamData[i].pData != NULL)
7099 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7106 /***********************************************************************
7107 * GetConvertStg (OLE32.68)
7109 HRESULT WINAPI GetConvertStg(LPGUID guid) {
7110 FIXME("(%s), unimplemented stub!\n",debugstr_guid(guid));