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 "debugtools.h"
22 #include "storage32.h"
23 #include "ole2.h" /* For Write/ReadClassStm */
26 #include "wine/wingdi16.h"
28 DEFAULT_DEBUG_CHANNEL(storage)
33 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
34 #define OLESTREAM_ID 0x501
35 #define OLESTREAM_MAX_STR_LEN 255
37 static const char rootPropertyName[] = "Root Entry";
40 /* OLESTREAM memory structure to use for Get and Put Routines */
41 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
46 DWORD dwOleTypeNameLength;
47 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
48 CHAR *pstrOleObjFileName;
49 DWORD dwOleObjFileNameLength;
50 DWORD dwMetaFileWidth;
51 DWORD dwMetaFileHeight;
52 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
55 }OLECONVERT_OLESTREAM_DATA;
57 /* CompObj Stream structure */
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
63 DWORD dwCLSIDNameLength;
64 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
65 DWORD dwOleTypeNameLength;
66 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
67 DWORD dwProgIDNameLength;
68 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
70 }OLECONVERT_ISTORAGE_COMPOBJ;
73 /* Ole Presention Stream structure */
74 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
82 }OLECONVERT_ISTORAGE_OLEPRES;
86 /***********************************************************************
87 * Forward declaration of internal functions used by the method DestroyElement
89 static HRESULT deleteStorageProperty(
90 StorageImpl *parentStorage,
91 ULONG foundPropertyIndexToDelete,
92 StgProperty propertyToDelete);
94 static HRESULT deleteStreamProperty(
95 StorageImpl *parentStorage,
96 ULONG foundPropertyIndexToDelete,
97 StgProperty propertyToDelete);
99 static HRESULT findPlaceholder(
100 StorageImpl *storage,
101 ULONG propertyIndexToStore,
102 ULONG storagePropertyIndex,
105 static HRESULT adjustPropertyChain(
107 StgProperty propertyToDelete,
108 StgProperty parentProperty,
109 ULONG parentPropertyId,
112 /***********************************************************************
113 * Declaration of the functions used to manipulate StgProperty
116 static ULONG getFreeProperty(
117 StorageImpl *storage);
119 static void updatePropertyChain(
120 StorageImpl *storage,
121 ULONG newPropertyIndex,
122 StgProperty newProperty);
124 static LONG propertyNameCmp(
125 OLECHAR *newProperty,
126 OLECHAR *currentProperty);
129 /***********************************************************************
130 * Declaration of miscellaneous functions...
132 static HRESULT validateSTGM(DWORD stgmValue);
134 static DWORD GetShareModeFromSTGM(DWORD stgm);
135 static DWORD GetAccessModeFromSTGM(DWORD stgm);
136 static DWORD GetCreationModeFromSTGM(DWORD stgm);
139 * Virtual function table for the IStorage32Impl class.
141 static ICOM_VTABLE(IStorage) Storage32Impl_Vtbl =
143 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
144 StorageBaseImpl_QueryInterface,
145 StorageBaseImpl_AddRef,
146 StorageBaseImpl_Release,
147 StorageBaseImpl_CreateStream,
148 StorageBaseImpl_OpenStream,
149 StorageImpl_CreateStorage,
150 StorageBaseImpl_OpenStorage,
152 StorageImpl_MoveElementTo,
155 StorageBaseImpl_EnumElements,
156 StorageImpl_DestroyElement,
157 StorageBaseImpl_RenameElement,
158 StorageImpl_SetElementTimes,
159 StorageBaseImpl_SetClass,
160 StorageImpl_SetStateBits,
165 * Virtual function table for the Storage32InternalImpl class.
167 static ICOM_VTABLE(IStorage) Storage32InternalImpl_Vtbl =
169 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
170 StorageBaseImpl_QueryInterface,
171 StorageBaseImpl_AddRef,
172 StorageBaseImpl_Release,
173 StorageBaseImpl_CreateStream,
174 StorageBaseImpl_OpenStream,
175 StorageImpl_CreateStorage,
176 StorageBaseImpl_OpenStorage,
178 StorageImpl_MoveElementTo,
179 StorageInternalImpl_Commit,
180 StorageInternalImpl_Revert,
181 StorageBaseImpl_EnumElements,
182 StorageImpl_DestroyElement,
183 StorageBaseImpl_RenameElement,
184 StorageImpl_SetElementTimes,
185 StorageBaseImpl_SetClass,
186 StorageImpl_SetStateBits,
191 * Virtual function table for the IEnumSTATSTGImpl class.
193 static ICOM_VTABLE(IEnumSTATSTG) IEnumSTATSTGImpl_Vtbl =
195 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
196 IEnumSTATSTGImpl_QueryInterface,
197 IEnumSTATSTGImpl_AddRef,
198 IEnumSTATSTGImpl_Release,
199 IEnumSTATSTGImpl_Next,
200 IEnumSTATSTGImpl_Skip,
201 IEnumSTATSTGImpl_Reset,
202 IEnumSTATSTGImpl_Clone
209 /************************************************************************
210 ** Storage32BaseImpl implementatiion
213 /************************************************************************
214 * Storage32BaseImpl_QueryInterface (IUnknown)
216 * This method implements the common QueryInterface for all IStorage32
217 * implementations contained in this file.
219 * See Windows documentation for more details on IUnknown methods.
221 HRESULT WINAPI StorageBaseImpl_QueryInterface(
226 ICOM_THIS(StorageBaseImpl,iface);
228 * Perform a sanity check on the parameters.
230 if ( (This==0) || (ppvObject==0) )
234 * Initialize the return parameter.
239 * Compare the riid with the interface IDs implemented by this object.
241 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
243 *ppvObject = (IStorage*)This;
245 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
247 *ppvObject = (IStorage*)This;
251 * Check that we obtained an interface.
254 return E_NOINTERFACE;
257 * Query Interface always increases the reference count by one when it is
260 StorageBaseImpl_AddRef(iface);
265 /************************************************************************
266 * Storage32BaseImpl_AddRef (IUnknown)
268 * This method implements the common AddRef for all IStorage32
269 * implementations contained in this file.
271 * See Windows documentation for more details on IUnknown methods.
273 ULONG WINAPI StorageBaseImpl_AddRef(
276 ICOM_THIS(StorageBaseImpl,iface);
282 /************************************************************************
283 * Storage32BaseImpl_Release (IUnknown)
285 * This method implements the common Release for all IStorage32
286 * implementations contained in this file.
288 * See Windows documentation for more details on IUnknown methods.
290 ULONG WINAPI StorageBaseImpl_Release(
293 ICOM_THIS(StorageBaseImpl,iface);
295 * Decrease the reference count on this object.
300 * If the reference count goes down to 0, perform suicide.
305 * Since we are using a system of base-classes, we want to call the
306 * destructor of the appropriate derived class. To do this, we are
307 * using virtual functions to implement the destructor.
309 This->v_destructor(This);
317 /************************************************************************
318 * Storage32BaseImpl_OpenStream (IStorage)
320 * This method will open the specified stream object from the current storage.
322 * See Windows documentation for more details on IStorage methods.
324 HRESULT WINAPI StorageBaseImpl_OpenStream(
326 const OLECHAR* pwcsName, /* [string][in] */
327 void* reserved1, /* [unique][in] */
328 DWORD grfMode, /* [in] */
329 DWORD reserved2, /* [in] */
330 IStream** ppstm) /* [out] */
332 ICOM_THIS(StorageBaseImpl,iface);
333 IEnumSTATSTGImpl* propertyEnumeration;
334 StgStreamImpl* newStream;
335 StgProperty currentProperty;
336 ULONG foundPropertyIndex;
338 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
339 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
342 * Perform a sanity check on the parameters.
344 if ( (pwcsName==NULL) || (ppstm==0) )
348 * Initialize the out parameter
353 * Validate the STGM flags
355 if ( FAILED( validateSTGM(grfMode) ))
356 return STG_E_INVALIDFLAG;
361 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
362 (grfMode & STGM_DELETEONRELEASE) ||
363 (grfMode & STGM_TRANSACTED) )
364 return STG_E_INVALIDFUNCTION;
367 * Create a property enumeration to search the properties
369 propertyEnumeration = IEnumSTATSTGImpl_Construct(
370 This->ancestorStorage,
371 This->rootPropertySetIndex);
374 * Search the enumeration for the property with the given name
376 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
382 * Delete the property enumeration since we don't need it anymore
384 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
387 * If it was found, construct the stream object and return a pointer to it.
389 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
390 (currentProperty.propertyType==PROPTYPE_STREAM) )
392 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
396 newStream->grfMode = grfMode;
397 *ppstm = (IStream*)newStream;
400 * Since we are returning a pointer to the interface, we have to
401 * nail down the reference.
403 StgStreamImpl_AddRef(*ppstm);
408 return E_OUTOFMEMORY;
411 return STG_E_FILENOTFOUND;
414 /************************************************************************
415 * Storage32BaseImpl_OpenStorage (IStorage)
417 * This method will open a new storage object from the current storage.
419 * See Windows documentation for more details on IStorage methods.
421 HRESULT WINAPI StorageBaseImpl_OpenStorage(
423 const OLECHAR* pwcsName, /* [string][unique][in] */
424 IStorage* pstgPriority, /* [unique][in] */
425 DWORD grfMode, /* [in] */
426 SNB snbExclude, /* [unique][in] */
427 DWORD reserved, /* [in] */
428 IStorage** ppstg) /* [out] */
430 ICOM_THIS(StorageBaseImpl,iface);
431 StorageInternalImpl* newStorage;
432 IEnumSTATSTGImpl* propertyEnumeration;
433 StgProperty currentProperty;
434 ULONG foundPropertyIndex;
436 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
437 iface, debugstr_w(pwcsName), pstgPriority,
438 grfMode, snbExclude, reserved, ppstg);
441 * Perform a sanity check on the parameters.
443 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
447 * Validate the STGM flags
449 if ( FAILED( validateSTGM(grfMode) ))
450 return STG_E_INVALIDFLAG;
455 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
456 (grfMode & STGM_DELETEONRELEASE) ||
457 (grfMode & STGM_PRIORITY) )
458 return STG_E_INVALIDFUNCTION;
461 * Initialize the out parameter
466 * Create a property enumeration to search the properties
468 propertyEnumeration = IEnumSTATSTGImpl_Construct(
469 This->ancestorStorage,
470 This->rootPropertySetIndex);
473 * Search the enumeration for the property with the given name
475 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
481 * Delete the property enumeration since we don't need it anymore
483 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
486 * If it was found, construct the stream object and return a pointer to it.
488 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
489 (currentProperty.propertyType==PROPTYPE_STORAGE) )
492 * Construct a new Storage object
494 newStorage = StorageInternalImpl_Construct(
495 This->ancestorStorage,
500 *ppstg = (IStorage*)newStorage;
503 * Since we are returning a pointer to the interface,
504 * we have to nail down the reference.
506 StorageBaseImpl_AddRef(*ppstg);
511 return STG_E_INSUFFICIENTMEMORY;
514 return STG_E_FILENOTFOUND;
517 /************************************************************************
518 * Storage32BaseImpl_EnumElements (IStorage)
520 * This method will create an enumerator object that can be used to
521 * retrieve informatino about all the properties in the storage object.
523 * See Windows documentation for more details on IStorage methods.
525 HRESULT WINAPI StorageBaseImpl_EnumElements(
527 DWORD reserved1, /* [in] */
528 void* reserved2, /* [size_is][unique][in] */
529 DWORD reserved3, /* [in] */
530 IEnumSTATSTG** ppenum) /* [out] */
532 ICOM_THIS(StorageBaseImpl,iface);
533 IEnumSTATSTGImpl* newEnum;
535 TRACE("(%p, %ld, %p, %ld, %p)\n",
536 iface, reserved1, reserved2, reserved3, ppenum);
539 * Perform a sanity check on the parameters.
541 if ( (This==0) || (ppenum==0))
545 * Construct the enumerator.
547 newEnum = IEnumSTATSTGImpl_Construct(
548 This->ancestorStorage,
549 This->rootPropertySetIndex);
553 *ppenum = (IEnumSTATSTG*)newEnum;
556 * Don't forget to nail down a reference to the new object before
559 IEnumSTATSTGImpl_AddRef(*ppenum);
564 return E_OUTOFMEMORY;
567 /************************************************************************
568 * Storage32BaseImpl_Stat (IStorage)
570 * This method will retrieve information about this storage object.
572 * See Windows documentation for more details on IStorage methods.
574 HRESULT WINAPI StorageBaseImpl_Stat(
576 STATSTG* pstatstg, /* [out] */
577 DWORD grfStatFlag) /* [in] */
579 ICOM_THIS(StorageBaseImpl,iface);
580 StgProperty curProperty;
583 TRACE("(%p, %p, %lx)\n",
584 iface, pstatstg, grfStatFlag);
587 * Perform a sanity check on the parameters.
589 if ( (This==0) || (pstatstg==0))
593 * Read the information from the property.
595 readSuccessful = StorageImpl_ReadProperty(
596 This->ancestorStorage,
597 This->rootPropertySetIndex,
602 StorageUtl_CopyPropertyToSTATSTG(
613 /************************************************************************
614 * Storage32BaseImpl_RenameElement (IStorage)
616 * This method will rename the specified element.
618 * See Windows documentation for more details on IStorage methods.
620 * Implementation notes: The method used to rename consists of creating a clone
621 * of the deleted StgProperty object setting it with the new name and to
622 * perform a DestroyElement of the old StgProperty.
624 HRESULT WINAPI StorageBaseImpl_RenameElement(
626 const OLECHAR* pwcsOldName, /* [in] */
627 const OLECHAR* pwcsNewName) /* [in] */
629 ICOM_THIS(StorageBaseImpl,iface);
630 IEnumSTATSTGImpl* propertyEnumeration;
631 StgProperty currentProperty;
632 ULONG foundPropertyIndex;
634 TRACE("(%p, %s, %s)\n",
635 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
638 * Create a property enumeration to search the properties
640 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
641 This->rootPropertySetIndex);
644 * Search the enumeration for the new property name
646 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
650 if (foundPropertyIndex != PROPERTY_NULL)
653 * There is already a property with the new name
655 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
656 return STG_E_FILEALREADYEXISTS;
659 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
662 * Search the enumeration for the old property name
664 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
669 * Delete the property enumeration since we don't need it anymore
671 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
673 if (foundPropertyIndex != PROPERTY_NULL)
675 StgProperty renamedProperty;
676 ULONG renamedPropertyIndex;
679 * Setup a new property for the renamed property
681 renamedProperty.sizeOfNameString =
682 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
684 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
685 return STG_E_INVALIDNAME;
687 lstrcpyW(renamedProperty.name, pwcsNewName);
689 renamedProperty.propertyType = currentProperty.propertyType;
690 renamedProperty.startingBlock = currentProperty.startingBlock;
691 renamedProperty.size.s.LowPart = currentProperty.size.s.LowPart;
692 renamedProperty.size.s.HighPart = currentProperty.size.s.HighPart;
694 renamedProperty.previousProperty = PROPERTY_NULL;
695 renamedProperty.nextProperty = PROPERTY_NULL;
698 * Bring the dirProperty link in case it is a storage and in which
699 * case the renamed storage elements don't require to be reorganized.
701 renamedProperty.dirProperty = currentProperty.dirProperty;
703 /* call CoFileTime to get the current time
704 renamedProperty.timeStampS1
705 renamedProperty.timeStampD1
706 renamedProperty.timeStampS2
707 renamedProperty.timeStampD2
708 renamedProperty.propertyUniqueID
712 * Obtain a free property in the property chain
714 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
717 * Save the new property into the new property spot
719 StorageImpl_WriteProperty(
720 This->ancestorStorage,
721 renamedPropertyIndex,
725 * Find a spot in the property chain for our newly created property.
729 renamedPropertyIndex,
733 * At this point the renamed property has been inserted in the tree,
734 * now, before to Destroy the old property we must zeroed it's dirProperty
735 * otherwise the DestroyProperty below will zap it all and we do not want
737 * Also, we fake that the old property is a storage so the DestroyProperty
738 * will not do a SetSize(0) on the stream data.
740 * This means that we need to tweek the StgProperty if it is a stream or a
743 StorageImpl_ReadProperty(This->ancestorStorage,
747 currentProperty.dirProperty = PROPERTY_NULL;
748 currentProperty.propertyType = PROPTYPE_STORAGE;
749 StorageImpl_WriteProperty(
750 This->ancestorStorage,
755 * Invoke Destroy to get rid of the ole property and automatically redo
756 * the linking of it's previous and next members...
758 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
764 * There is no property with the old name
766 return STG_E_FILENOTFOUND;
772 /************************************************************************
773 * Storage32BaseImpl_CreateStream (IStorage)
775 * This method will create a stream object within this storage
777 * See Windows documentation for more details on IStorage methods.
779 HRESULT WINAPI StorageBaseImpl_CreateStream(
781 const OLECHAR* pwcsName, /* [string][in] */
782 DWORD grfMode, /* [in] */
783 DWORD reserved1, /* [in] */
784 DWORD reserved2, /* [in] */
785 IStream** ppstm) /* [out] */
787 ICOM_THIS(StorageBaseImpl,iface);
788 IEnumSTATSTGImpl* propertyEnumeration;
789 StgStreamImpl* newStream;
790 StgProperty currentProperty, newStreamProperty;
791 ULONG foundPropertyIndex, newPropertyIndex;
793 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
794 iface, debugstr_w(pwcsName), grfMode,
795 reserved1, reserved2, ppstm);
798 * Validate parameters
801 return STG_E_INVALIDPOINTER;
804 return STG_E_INVALIDNAME;
807 * Validate the STGM flags
809 if ( FAILED( validateSTGM(grfMode) ))
810 return STG_E_INVALIDFLAG;
815 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
816 (grfMode & STGM_DELETEONRELEASE) ||
817 (grfMode & STGM_TRANSACTED) )
818 return STG_E_INVALIDFUNCTION;
821 * Initialize the out parameter
826 * Create a property enumeration to search the properties
828 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
829 This->rootPropertySetIndex);
831 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
835 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
837 if (foundPropertyIndex != PROPERTY_NULL)
840 * An element with this name already exists
842 if (grfMode & STGM_CREATE)
844 IStorage_DestroyElement(iface, pwcsName);
847 return STG_E_FILEALREADYEXISTS;
851 * memset the empty property
853 memset(&newStreamProperty, 0, sizeof(StgProperty));
855 newStreamProperty.sizeOfNameString =
856 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
858 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
859 return STG_E_INVALIDNAME;
861 lstrcpyW(newStreamProperty.name, pwcsName);
863 newStreamProperty.propertyType = PROPTYPE_STREAM;
864 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
865 newStreamProperty.size.s.LowPart = 0;
866 newStreamProperty.size.s.HighPart = 0;
868 newStreamProperty.previousProperty = PROPERTY_NULL;
869 newStreamProperty.nextProperty = PROPERTY_NULL;
870 newStreamProperty.dirProperty = PROPERTY_NULL;
872 /* call CoFileTime to get the current time
873 newStreamProperty.timeStampS1
874 newStreamProperty.timeStampD1
875 newStreamProperty.timeStampS2
876 newStreamProperty.timeStampD2
879 /* newStreamProperty.propertyUniqueID */
882 * Get a free property or create a new one
884 newPropertyIndex = getFreeProperty(This->ancestorStorage);
887 * Save the new property into the new property spot
889 StorageImpl_WriteProperty(
890 This->ancestorStorage,
895 * Find a spot in the property chain for our newly created property.
903 * Open the stream to return it.
905 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
909 *ppstm = (IStream*)newStream;
912 * Since we are returning a pointer to the interface, we have to nail down
915 StgStreamImpl_AddRef(*ppstm);
919 return STG_E_INSUFFICIENTMEMORY;
925 /************************************************************************
926 * Storage32BaseImpl_SetClass (IStorage)
928 * This method will write the specified CLSID in the property of this
931 * See Windows documentation for more details on IStorage methods.
933 HRESULT WINAPI StorageBaseImpl_SetClass(
935 REFCLSID clsid) /* [in] */
937 ICOM_THIS(StorageBaseImpl,iface);
938 HRESULT hRes = E_FAIL;
939 StgProperty curProperty;
942 TRACE("(%p, %p)\n", iface, clsid);
944 success = StorageImpl_ReadProperty(This->ancestorStorage,
945 This->rootPropertySetIndex,
949 curProperty.propertyUniqueID = *clsid;
951 success = StorageImpl_WriteProperty(This->ancestorStorage,
952 This->rootPropertySetIndex,
961 /************************************************************************
962 ** Storage32Impl implementation
965 /************************************************************************
966 * Storage32Impl_CreateStorage (IStorage)
968 * This method will create the storage object within the provided storage.
970 * See Windows documentation for more details on IStorage methods.
972 HRESULT WINAPI StorageImpl_CreateStorage(
974 const OLECHAR *pwcsName, /* [string][in] */
975 DWORD grfMode, /* [in] */
976 DWORD reserved1, /* [in] */
977 DWORD reserved2, /* [in] */
978 IStorage **ppstg) /* [out] */
980 StorageImpl* const This=(StorageImpl*)iface;
982 IEnumSTATSTGImpl *propertyEnumeration;
983 StgProperty currentProperty;
984 StgProperty newProperty;
985 ULONG foundPropertyIndex;
986 ULONG newPropertyIndex;
989 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
990 iface, debugstr_w(pwcsName), grfMode,
991 reserved1, reserved2, ppstg);
994 * Validate parameters
997 return STG_E_INVALIDPOINTER;
1000 return STG_E_INVALIDNAME;
1003 * Validate the STGM flags
1005 if ( FAILED( validateSTGM(grfMode) ) ||
1006 (grfMode & STGM_DELETEONRELEASE) )
1007 return STG_E_INVALIDFLAG;
1010 * Initialize the out parameter
1015 * Create a property enumeration and search the properties
1017 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
1018 This->rootPropertySetIndex);
1020 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1023 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1025 if (foundPropertyIndex != PROPERTY_NULL)
1028 * An element with this name already exists
1030 if (grfMode & STGM_CREATE)
1031 IStorage_DestroyElement(iface, pwcsName);
1033 return STG_E_FILEALREADYEXISTS;
1037 * memset the empty property
1039 memset(&newProperty, 0, sizeof(StgProperty));
1041 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1043 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1044 return STG_E_INVALIDNAME;
1046 lstrcpyW(newProperty.name, pwcsName);
1048 newProperty.propertyType = PROPTYPE_STORAGE;
1049 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1050 newProperty.size.s.LowPart = 0;
1051 newProperty.size.s.HighPart = 0;
1053 newProperty.previousProperty = PROPERTY_NULL;
1054 newProperty.nextProperty = PROPERTY_NULL;
1055 newProperty.dirProperty = PROPERTY_NULL;
1057 /* call CoFileTime to get the current time
1058 newProperty.timeStampS1
1059 newProperty.timeStampD1
1060 newProperty.timeStampS2
1061 newProperty.timeStampD2
1064 /* newStorageProperty.propertyUniqueID */
1067 * Obtain a free property in the property chain
1069 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1072 * Save the new property into the new property spot
1074 StorageImpl_WriteProperty(
1075 This->ancestorStorage,
1080 * Find a spot in the property chain for our newly created property.
1082 updatePropertyChain(
1088 * Open it to get a pointer to return.
1090 hr = IStorage_OpenStorage(
1099 if( (hr != S_OK) || (*ppstg == NULL))
1109 /***************************************************************************
1113 * Get a free property or create a new one.
1115 static ULONG getFreeProperty(
1116 StorageImpl *storage)
1118 ULONG currentPropertyIndex = 0;
1119 ULONG newPropertyIndex = PROPERTY_NULL;
1120 BOOL readSuccessful = TRUE;
1121 StgProperty currentProperty;
1126 * Start by reading the root property
1128 readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1129 currentPropertyIndex,
1133 if (currentProperty.sizeOfNameString == 0)
1136 * The property existis and is available, we found it.
1138 newPropertyIndex = currentPropertyIndex;
1144 * We exhausted the property list, we will create more space below
1146 newPropertyIndex = currentPropertyIndex;
1148 currentPropertyIndex++;
1150 } while (newPropertyIndex == PROPERTY_NULL);
1153 * grow the property chain
1155 if (! readSuccessful)
1157 StgProperty emptyProperty;
1158 ULARGE_INTEGER newSize;
1159 ULONG propertyIndex;
1160 ULONG lastProperty = 0;
1161 ULONG blockCount = 0;
1164 * obtain the new count of property blocks
1166 blockCount = BlockChainStream_GetCount(
1167 storage->ancestorStorage->rootBlockChain)+1;
1170 * initialize the size used by the property stream
1172 newSize.s.HighPart = 0;
1173 newSize.s.LowPart = storage->bigBlockSize * blockCount;
1176 * add a property block to the property chain
1178 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1181 * memset the empty property in order to initialize the unused newly
1184 memset(&emptyProperty, 0, sizeof(StgProperty));
1189 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1192 propertyIndex = newPropertyIndex;
1193 propertyIndex < lastProperty;
1196 StorageImpl_WriteProperty(
1197 storage->ancestorStorage,
1203 return newPropertyIndex;
1206 /****************************************************************************
1210 * Case insensitive comparaison of StgProperty.name by first considering
1213 * Returns <0 when newPrpoerty < currentProperty
1214 * >0 when newPrpoerty > currentProperty
1215 * 0 when newPrpoerty == currentProperty
1217 static LONG propertyNameCmp(
1218 OLECHAR *newProperty,
1219 OLECHAR *currentProperty)
1221 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1226 * We compare the string themselves only when they are of the same lenght
1228 diff = lstrcmpiW( newProperty, currentProperty);
1234 /****************************************************************************
1238 * Properly link this new element in the property chain.
1240 static void updatePropertyChain(
1241 StorageImpl *storage,
1242 ULONG newPropertyIndex,
1243 StgProperty newProperty)
1245 StgProperty currentProperty;
1248 * Read the root property
1250 StorageImpl_ReadProperty(storage->ancestorStorage,
1251 storage->rootPropertySetIndex,
1254 if (currentProperty.dirProperty != PROPERTY_NULL)
1257 * The root storage contains some element, therefore, start the research
1258 * for the appropriate location.
1261 ULONG current, next, previous, currentPropertyId;
1264 * Keep the StgProperty sequence number of the storage first property
1266 currentPropertyId = currentProperty.dirProperty;
1271 StorageImpl_ReadProperty(storage->ancestorStorage,
1272 currentProperty.dirProperty,
1275 previous = currentProperty.previousProperty;
1276 next = currentProperty.nextProperty;
1277 current = currentPropertyId;
1281 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1285 if (previous != PROPERTY_NULL)
1287 StorageImpl_ReadProperty(storage->ancestorStorage,
1294 currentProperty.previousProperty = newPropertyIndex;
1295 StorageImpl_WriteProperty(storage->ancestorStorage,
1303 if (next != PROPERTY_NULL)
1305 StorageImpl_ReadProperty(storage->ancestorStorage,
1312 currentProperty.nextProperty = newPropertyIndex;
1313 StorageImpl_WriteProperty(storage->ancestorStorage,
1322 * Trying to insert an item with the same name in the
1323 * subtree structure.
1328 previous = currentProperty.previousProperty;
1329 next = currentProperty.nextProperty;
1335 * The root storage is empty, link the new property to it's dir property
1337 currentProperty.dirProperty = newPropertyIndex;
1338 StorageImpl_WriteProperty(storage->ancestorStorage,
1339 storage->rootPropertySetIndex,
1345 /*************************************************************************
1348 HRESULT WINAPI StorageImpl_CopyTo(
1350 DWORD ciidExclude, /* [in] */
1351 const IID* rgiidExclude, /* [size_is][unique][in] */
1352 SNB snbExclude, /* [unique][in] */
1353 IStorage* pstgDest) /* [unique][in] */
1355 IEnumSTATSTG *elements = 0;
1356 STATSTG curElement, strStat;
1358 IStorage *pstgTmp, *pstgChild;
1359 IStream *pstrTmp, *pstrChild;
1361 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1362 FIXME("Exclude option not implemented\n");
1364 TRACE("(%p, %ld, %p, %p, %p)\n",
1365 iface, ciidExclude, rgiidExclude,
1366 snbExclude, pstgDest);
1369 * Perform a sanity check
1371 if ( pstgDest == 0 )
1372 return STG_E_INVALIDPOINTER;
1375 * Enumerate the elements
1377 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1385 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1386 IStorage_SetClass( pstgDest, &curElement.clsid );
1391 * Obtain the next element
1393 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1395 if ( hr == S_FALSE )
1397 hr = S_OK; /* done, every element has been copied */
1401 if (curElement.type == STGTY_STORAGE)
1404 * open child source storage
1406 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1407 STGM_READ|STGM_SHARE_EXCLUSIVE,
1408 NULL, 0, &pstgChild );
1414 * Check if destination storage is not a child of the source
1415 * storage, which will cause an infinite loop
1417 if (pstgChild == pstgDest)
1419 IEnumSTATSTG_Release(elements);
1421 return STG_E_ACCESSDENIED;
1425 * create a new storage in destination storage
1427 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1428 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1432 * if it already exist, don't create a new one use this one
1434 if (hr == STG_E_FILEALREADYEXISTS)
1436 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1437 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1438 NULL, 0, &pstgTmp );
1446 * do the copy recursively
1448 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1449 snbExclude, pstgTmp );
1451 IStorage_Release( pstgTmp );
1452 IStorage_Release( pstgChild );
1454 else if (curElement.type == STGTY_STREAM)
1457 * create a new stream in destination storage. If the stream already
1458 * exist, it will be deleted and a new one will be created.
1460 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1461 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1468 * open child stream storage
1470 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1471 STGM_READ|STGM_SHARE_EXCLUSIVE,
1478 * Get the size of the source stream
1480 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1483 * Set the size of the destination stream.
1485 IStream_SetSize(pstrTmp, strStat.cbSize);
1490 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1493 IStream_Release( pstrTmp );
1494 IStream_Release( pstrChild );
1498 WARN("unknown element type: %ld\n", curElement.type);
1501 } while (hr == S_OK);
1506 IEnumSTATSTG_Release(elements);
1511 /*************************************************************************
1512 * MoveElementTo (IStorage)
1514 HRESULT WINAPI StorageImpl_MoveElementTo(
1516 const OLECHAR *pwcsName, /* [string][in] */
1517 IStorage *pstgDest, /* [unique][in] */
1518 const OLECHAR *pwcsNewName,/* [string][in] */
1519 DWORD grfFlags) /* [in] */
1521 FIXME("not implemented!\n");
1525 /*************************************************************************
1528 HRESULT WINAPI StorageImpl_Commit(
1530 DWORD grfCommitFlags)/* [in] */
1532 FIXME("(%ld): stub!\n", grfCommitFlags);
1536 /*************************************************************************
1539 HRESULT WINAPI StorageImpl_Revert(
1542 FIXME("not implemented!\n");
1546 /*************************************************************************
1547 * DestroyElement (IStorage)
1549 * Stategy: This implementation is build this way for simplicity not for speed.
1550 * I always delete the top most element of the enumeration and adjust
1551 * the deleted element pointer all the time. This takes longer to
1552 * do but allow to reinvoke DestroyElement whenever we encounter a
1553 * storage object. The optimisation reside in the usage of another
1554 * enumeration stategy that would give all the leaves of a storage
1555 * first. (postfix order)
1557 HRESULT WINAPI StorageImpl_DestroyElement(
1559 const OLECHAR *pwcsName)/* [string][in] */
1561 StorageImpl* const This=(StorageImpl*)iface;
1563 IEnumSTATSTGImpl* propertyEnumeration;
1566 StgProperty propertyToDelete;
1567 StgProperty parentProperty;
1568 ULONG foundPropertyIndexToDelete;
1569 ULONG typeOfRelation;
1570 ULONG parentPropertyId;
1573 iface, debugstr_w(pwcsName));
1576 * Perform a sanity check on the parameters.
1579 return STG_E_INVALIDPOINTER;
1582 * Create a property enumeration to search the property with the given name
1584 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1585 This->ancestorStorage,
1586 This->rootPropertySetIndex);
1588 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1589 propertyEnumeration,
1593 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1595 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1597 return STG_E_FILENOTFOUND;
1601 * Find the parent property of the property to delete (the one that
1602 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1603 * the parent is This. Otherwise, the parent is one of it's sibling...
1607 * First, read This's StgProperty..
1609 res = StorageImpl_ReadProperty(
1610 This->ancestorStorage,
1611 This->rootPropertySetIndex,
1617 * Second, check to see if by any chance the actual storage (This) is not
1618 * the parent of the property to delete... We never know...
1620 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1623 * Set data as it would have been done in the else part...
1625 typeOfRelation = PROPERTY_RELATION_DIR;
1626 parentPropertyId = This->rootPropertySetIndex;
1631 * Create a property enumeration to search the parent properties, and
1632 * delete it once done.
1634 IEnumSTATSTGImpl* propertyEnumeration2;
1636 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1637 This->ancestorStorage,
1638 This->rootPropertySetIndex);
1640 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1641 propertyEnumeration2,
1642 foundPropertyIndexToDelete,
1646 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1649 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1651 hr = deleteStorageProperty(
1653 foundPropertyIndexToDelete,
1656 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1658 hr = deleteStreamProperty(
1660 foundPropertyIndexToDelete,
1668 * Adjust the property chain
1670 hr = adjustPropertyChain(
1681 /*********************************************************************
1685 * Perform the deletion of a complete storage node
1688 static HRESULT deleteStorageProperty(
1689 StorageImpl *parentStorage,
1690 ULONG indexOfPropertyToDelete,
1691 StgProperty propertyToDelete)
1693 IEnumSTATSTG *elements = 0;
1694 IStorage *childStorage = 0;
1695 STATSTG currentElement;
1697 HRESULT destroyHr = S_OK;
1700 * Open the storage and enumerate it
1702 hr = StorageBaseImpl_OpenStorage(
1703 (IStorage*)parentStorage,
1704 propertyToDelete.name,
1706 STGM_SHARE_EXCLUSIVE,
1717 * Enumerate the elements
1719 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1724 * Obtain the next element
1726 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1729 destroyHr = StorageImpl_DestroyElement(
1730 (IStorage*)childStorage,
1731 (OLECHAR*)currentElement.pwcsName);
1733 CoTaskMemFree(currentElement.pwcsName);
1737 * We need to Reset the enumeration every time because we delete elements
1738 * and the enumeration could be invalid
1740 IEnumSTATSTG_Reset(elements);
1742 } while ((hr == S_OK) && (destroyHr == S_OK));
1745 * Invalidate the property by zeroing it's name member.
1747 propertyToDelete.sizeOfNameString = 0;
1749 StorageImpl_WriteProperty(parentStorage->ancestorStorage,
1750 indexOfPropertyToDelete,
1753 IStorage_Release(childStorage);
1754 IEnumSTATSTG_Release(elements);
1759 /*********************************************************************
1763 * Perform the deletion of a stream node
1766 static HRESULT deleteStreamProperty(
1767 StorageImpl *parentStorage,
1768 ULONG indexOfPropertyToDelete,
1769 StgProperty propertyToDelete)
1773 ULARGE_INTEGER size;
1775 size.s.HighPart = 0;
1778 hr = StorageBaseImpl_OpenStream(
1779 (IStorage*)parentStorage,
1780 (OLECHAR*)propertyToDelete.name,
1782 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1794 hr = IStream_SetSize(pis, size);
1802 * Release the stream object.
1804 IStream_Release(pis);
1807 * Invalidate the property by zeroing it's name member.
1809 propertyToDelete.sizeOfNameString = 0;
1812 * Here we should re-read the property so we get the updated pointer
1813 * but since we are here to zap it, I don't do it...
1815 StorageImpl_WriteProperty(
1816 parentStorage->ancestorStorage,
1817 indexOfPropertyToDelete,
1823 /*********************************************************************
1827 * Finds a placeholder for the StgProperty within the Storage
1830 static HRESULT findPlaceholder(
1831 StorageImpl *storage,
1832 ULONG propertyIndexToStore,
1833 ULONG storePropertyIndex,
1836 StgProperty storeProperty;
1841 * Read the storage property
1843 res = StorageImpl_ReadProperty(
1844 storage->ancestorStorage,
1853 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1855 if (storeProperty.previousProperty != PROPERTY_NULL)
1857 return findPlaceholder(
1859 propertyIndexToStore,
1860 storeProperty.previousProperty,
1865 storeProperty.previousProperty = propertyIndexToStore;
1868 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1870 if (storeProperty.nextProperty != PROPERTY_NULL)
1872 return findPlaceholder(
1874 propertyIndexToStore,
1875 storeProperty.nextProperty,
1880 storeProperty.nextProperty = propertyIndexToStore;
1883 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1885 if (storeProperty.dirProperty != PROPERTY_NULL)
1887 return findPlaceholder(
1889 propertyIndexToStore,
1890 storeProperty.dirProperty,
1895 storeProperty.dirProperty = propertyIndexToStore;
1899 hr = StorageImpl_WriteProperty(
1900 storage->ancestorStorage,
1912 /*************************************************************************
1916 * This method takes the previous and the next property link of a property
1917 * to be deleted and find them a place in the Storage.
1919 static HRESULT adjustPropertyChain(
1921 StgProperty propertyToDelete,
1922 StgProperty parentProperty,
1923 ULONG parentPropertyId,
1926 ULONG newLinkProperty = PROPERTY_NULL;
1927 BOOL needToFindAPlaceholder = FALSE;
1928 ULONG storeNode = PROPERTY_NULL;
1929 ULONG toStoreNode = PROPERTY_NULL;
1930 INT relationType = 0;
1934 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1936 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1939 * Set the parent previous to the property to delete previous
1941 newLinkProperty = propertyToDelete.previousProperty;
1943 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1946 * We also need to find a storage for the other link, setup variables
1947 * to do this at the end...
1949 needToFindAPlaceholder = TRUE;
1950 storeNode = propertyToDelete.previousProperty;
1951 toStoreNode = propertyToDelete.nextProperty;
1952 relationType = PROPERTY_RELATION_NEXT;
1955 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1958 * Set the parent previous to the property to delete next
1960 newLinkProperty = propertyToDelete.nextProperty;
1964 * Link it for real...
1966 parentProperty.previousProperty = newLinkProperty;
1969 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1971 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1974 * Set the parent next to the property to delete next previous
1976 newLinkProperty = propertyToDelete.previousProperty;
1978 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1981 * We also need to find a storage for the other link, setup variables
1982 * to do this at the end...
1984 needToFindAPlaceholder = TRUE;
1985 storeNode = propertyToDelete.previousProperty;
1986 toStoreNode = propertyToDelete.nextProperty;
1987 relationType = PROPERTY_RELATION_NEXT;
1990 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1993 * Set the parent next to the property to delete next
1995 newLinkProperty = propertyToDelete.nextProperty;
1999 * Link it for real...
2001 parentProperty.nextProperty = newLinkProperty;
2003 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2005 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2008 * Set the parent dir to the property to delete previous
2010 newLinkProperty = propertyToDelete.previousProperty;
2012 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2015 * We also need to find a storage for the other link, setup variables
2016 * to do this at the end...
2018 needToFindAPlaceholder = TRUE;
2019 storeNode = propertyToDelete.previousProperty;
2020 toStoreNode = propertyToDelete.nextProperty;
2021 relationType = PROPERTY_RELATION_NEXT;
2024 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2027 * Set the parent dir to the property to delete next
2029 newLinkProperty = propertyToDelete.nextProperty;
2033 * Link it for real...
2035 parentProperty.dirProperty = newLinkProperty;
2039 * Write back the parent property
2041 res = StorageImpl_WriteProperty(
2042 This->ancestorStorage,
2051 * If a placeholder is required for the other link, then, find one and
2052 * get out of here...
2054 if (needToFindAPlaceholder)
2056 hr = findPlaceholder(
2067 /******************************************************************************
2068 * SetElementTimes (IStorage)
2070 HRESULT WINAPI StorageImpl_SetElementTimes(
2072 const OLECHAR *pwcsName,/* [string][in] */
2073 const FILETIME *pctime, /* [in] */
2074 const FILETIME *patime, /* [in] */
2075 const FILETIME *pmtime) /* [in] */
2077 FIXME("not implemented!\n");
2081 /******************************************************************************
2082 * SetStateBits (IStorage)
2084 HRESULT WINAPI StorageImpl_SetStateBits(
2086 DWORD grfStateBits,/* [in] */
2087 DWORD grfMask) /* [in] */
2089 FIXME("not implemented!\n");
2093 HRESULT StorageImpl_Construct(
2102 StgProperty currentProperty;
2103 BOOL readSuccessful;
2104 ULONG currentPropertyIndex;
2106 if ( FAILED( validateSTGM(openFlags) ))
2107 return STG_E_INVALIDFLAG;
2109 memset(This, 0, sizeof(StorageImpl));
2112 * Initialize the virtual fgunction table.
2114 ICOM_VTBL(This) = &Storage32Impl_Vtbl;
2115 This->v_destructor = &StorageImpl_Destroy;
2118 * This is the top-level storage so initialize the ancester pointer
2121 This->ancestorStorage = This;
2124 * Initialize the physical support of the storage.
2126 This->hFile = hFile;
2129 * Initialize the big block cache.
2131 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2132 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2133 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2139 if (This->bigBlockFile == 0)
2144 ULARGE_INTEGER size;
2145 BYTE* bigBlockBuffer;
2148 * Initialize all header variables:
2149 * - The big block depot consists of one block and it is at block 0
2150 * - The properties start at block 1
2151 * - There is no small block depot
2153 memset( This->bigBlockDepotStart,
2155 sizeof(This->bigBlockDepotStart));
2157 This->bigBlockDepotCount = 1;
2158 This->bigBlockDepotStart[0] = 0;
2159 This->rootStartBlock = 1;
2160 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2161 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2162 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2163 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2164 This->extBigBlockDepotCount = 0;
2166 StorageImpl_SaveFileHeader(This);
2169 * Add one block for the big block depot and one block for the properties
2171 size.s.HighPart = 0;
2172 size.s.LowPart = This->bigBlockSize * 3;
2173 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2176 * Initialize the big block depot
2178 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2179 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2180 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2181 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2182 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2187 * Load the header for the file.
2189 hr = StorageImpl_LoadFileHeader(This);
2193 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2200 * There is no block depot cached yet.
2202 This->indexBlockDepotCached = 0xFFFFFFFF;
2205 * Start searching for free blocks with block 0.
2207 This->prevFreeBlock = 0;
2210 * Create the block chain abstractions.
2212 This->rootBlockChain =
2213 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL);
2215 This->smallBlockDepotChain = BlockChainStream_Construct(
2217 &This->smallBlockDepotStart,
2221 * Write the root property
2225 StgProperty rootProp;
2227 * Initialize the property chain
2229 memset(&rootProp, 0, sizeof(rootProp));
2230 lstrcpyAtoW(rootProp.name, rootPropertyName);
2232 rootProp.sizeOfNameString = (lstrlenW(rootProp.name)+1) * sizeof(WCHAR);
2233 rootProp.propertyType = PROPTYPE_ROOT;
2234 rootProp.previousProperty = PROPERTY_NULL;
2235 rootProp.nextProperty = PROPERTY_NULL;
2236 rootProp.dirProperty = PROPERTY_NULL;
2237 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2238 rootProp.size.s.HighPart = 0;
2239 rootProp.size.s.LowPart = 0;
2241 StorageImpl_WriteProperty(This, 0, &rootProp);
2245 * Find the ID of the root int he property sets.
2247 currentPropertyIndex = 0;
2251 readSuccessful = StorageImpl_ReadProperty(
2253 currentPropertyIndex,
2258 if ( (currentProperty.sizeOfNameString != 0 ) &&
2259 (currentProperty.propertyType == PROPTYPE_ROOT) )
2261 This->rootPropertySetIndex = currentPropertyIndex;
2265 currentPropertyIndex++;
2267 } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2269 if (!readSuccessful)
2276 * Create the block chain abstraction for the small block root chain.
2278 This->smallBlockRootChain = BlockChainStream_Construct(
2281 This->rootPropertySetIndex);
2286 void StorageImpl_Destroy(
2289 TRACE("(%p)\n", This);
2291 BlockChainStream_Destroy(This->smallBlockRootChain);
2292 BlockChainStream_Destroy(This->rootBlockChain);
2293 BlockChainStream_Destroy(This->smallBlockDepotChain);
2295 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2299 /******************************************************************************
2300 * Storage32Impl_GetNextFreeBigBlock
2302 * Returns the index of the next free big block.
2303 * If the big block depot is filled, this method will enlarge it.
2306 ULONG StorageImpl_GetNextFreeBigBlock(
2309 ULONG depotBlockIndexPos;
2311 ULONG depotBlockOffset;
2312 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2313 ULONG nextBlockIndex = BLOCK_SPECIAL;
2315 ULONG freeBlock = BLOCK_UNUSED;
2317 depotIndex = This->prevFreeBlock / blocksPerDepot;
2318 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2321 * Scan the entire big block depot until we find a block marked free
2323 while (nextBlockIndex != BLOCK_UNUSED)
2325 if (depotIndex < COUNT_BBDEPOTINHEADER)
2327 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2330 * Grow the primary depot.
2332 if (depotBlockIndexPos == BLOCK_UNUSED)
2334 depotBlockIndexPos = depotIndex*blocksPerDepot;
2337 * Add a block depot.
2339 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2340 This->bigBlockDepotCount++;
2341 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2344 * Flag it as a block depot.
2346 StorageImpl_SetNextBlockInChain(This,
2350 /* Save new header information.
2352 StorageImpl_SaveFileHeader(This);
2357 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2359 if (depotBlockIndexPos == BLOCK_UNUSED)
2362 * Grow the extended depot.
2364 ULONG extIndex = BLOCK_UNUSED;
2365 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2366 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2368 if (extBlockOffset == 0)
2370 /* We need an extended block.
2372 extIndex = Storage32Impl_AddExtBlockDepot(This);
2373 This->extBigBlockDepotCount++;
2374 depotBlockIndexPos = extIndex + 1;
2377 depotBlockIndexPos = depotIndex * blocksPerDepot;
2380 * Add a block depot and mark it in the extended block.
2382 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2383 This->bigBlockDepotCount++;
2384 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2386 /* Flag the block depot.
2388 StorageImpl_SetNextBlockInChain(This,
2392 /* If necessary, flag the extended depot block.
2394 if (extIndex != BLOCK_UNUSED)
2395 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2397 /* Save header information.
2399 StorageImpl_SaveFileHeader(This);
2403 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2405 if (depotBuffer != 0)
2407 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2408 ( nextBlockIndex != BLOCK_UNUSED))
2410 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2412 if (nextBlockIndex == BLOCK_UNUSED)
2414 freeBlock = (depotIndex * blocksPerDepot) +
2415 (depotBlockOffset/sizeof(ULONG));
2418 depotBlockOffset += sizeof(ULONG);
2421 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2425 depotBlockOffset = 0;
2428 This->prevFreeBlock = freeBlock;
2433 /******************************************************************************
2434 * Storage32Impl_AddBlockDepot
2436 * This will create a depot block, essentially it is a block initialized
2439 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2443 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2446 * Initialize blocks as free
2448 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2450 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2453 /******************************************************************************
2454 * Storage32Impl_GetExtDepotBlock
2456 * Returns the index of the block that corresponds to the specified depot
2457 * index. This method is only for depot indexes equal or greater than
2458 * COUNT_BBDEPOTINHEADER.
2460 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2462 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2463 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2464 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2465 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2466 ULONG blockIndex = BLOCK_UNUSED;
2467 ULONG extBlockIndex = This->extBigBlockDepotStart;
2469 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2471 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2472 return BLOCK_UNUSED;
2474 while (extBlockCount > 0)
2476 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2480 if (extBlockIndex != BLOCK_UNUSED)
2484 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2486 if (depotBuffer != 0)
2488 StorageUtl_ReadDWord(depotBuffer,
2489 extBlockOffset * sizeof(ULONG),
2492 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2499 /******************************************************************************
2500 * Storage32Impl_SetExtDepotBlock
2502 * Associates the specified block index to the specified depot index.
2503 * This method is only for depot indexes equal or greater than
2504 * COUNT_BBDEPOTINHEADER.
2506 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2510 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2511 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2512 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2513 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2514 ULONG extBlockIndex = This->extBigBlockDepotStart;
2516 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2518 while (extBlockCount > 0)
2520 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2524 if (extBlockIndex != BLOCK_UNUSED)
2528 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2530 if (depotBuffer != 0)
2532 StorageUtl_WriteDWord(depotBuffer,
2533 extBlockOffset * sizeof(ULONG),
2536 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2541 /******************************************************************************
2542 * Storage32Impl_AddExtBlockDepot
2544 * Creates an extended depot block.
2546 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2548 ULONG numExtBlocks = This->extBigBlockDepotCount;
2549 ULONG nextExtBlock = This->extBigBlockDepotStart;
2550 BYTE* depotBuffer = NULL;
2551 ULONG index = BLOCK_UNUSED;
2552 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2553 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2554 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2556 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2557 blocksPerDepotBlock;
2559 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2562 * The first extended block.
2564 This->extBigBlockDepotStart = index;
2570 * Follow the chain to the last one.
2572 for (i = 0; i < (numExtBlocks - 1); i++)
2574 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2578 * Add the new extended block to the chain.
2580 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2581 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2582 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2586 * Initialize this block.
2588 depotBuffer = StorageImpl_GetBigBlock(This, index);
2589 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2590 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2595 /******************************************************************************
2596 * Storage32Impl_FreeBigBlock
2598 * This method will flag the specified block as free in the big block depot.
2600 void StorageImpl_FreeBigBlock(
2604 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2606 if (blockIndex < This->prevFreeBlock)
2607 This->prevFreeBlock = blockIndex;
2610 /************************************************************************
2611 * Storage32Impl_GetNextBlockInChain
2613 * This method will retrieve the block index of the next big block in
2616 * Params: This - Pointer to the Storage object.
2617 * blockIndex - Index of the block to retrieve the chain
2620 * Returns: This method returns the index of the next block in the chain.
2621 * It will return the constants:
2622 * BLOCK_SPECIAL - If the block given was not part of a
2624 * BLOCK_END_OF_CHAIN - If the block given was the last in
2626 * BLOCK_UNUSED - If the block given was not past of a chain
2628 * BLOCK_EXTBBDEPOT - This block is part of the extended
2631 * See Windows documentation for more details on IStorage methods.
2633 ULONG StorageImpl_GetNextBlockInChain(
2637 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2638 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2639 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2640 ULONG nextBlockIndex = BLOCK_SPECIAL;
2642 ULONG depotBlockIndexPos;
2644 assert(depotBlockCount < This->bigBlockDepotCount);
2647 * Cache the currently accessed depot block.
2649 if (depotBlockCount != This->indexBlockDepotCached)
2651 This->indexBlockDepotCached = depotBlockCount;
2653 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2655 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2660 * We have to look in the extended depot.
2662 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2665 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2671 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2673 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &nextBlockIndex);
2674 This->blockDepotCached[index] = nextBlockIndex;
2677 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2681 nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2683 return nextBlockIndex;
2686 /******************************************************************************
2687 * Storage32Impl_GetNextExtendedBlock
2689 * Given an extended block this method will return the next extended block.
2692 * The last ULONG of an extended block is the block index of the next
2693 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2697 * - The index of the next extended block
2698 * - BLOCK_UNUSED: there is no next extended block.
2699 * - Any other return values denotes failure.
2701 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2703 ULONG nextBlockIndex = BLOCK_SPECIAL;
2704 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2707 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2711 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2713 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2716 return nextBlockIndex;
2719 /******************************************************************************
2720 * Storage32Impl_SetNextBlockInChain
2722 * This method will write the index of the specified block's next block
2723 * in the big block depot.
2725 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2728 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2729 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2730 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2733 void StorageImpl_SetNextBlockInChain(
2738 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2739 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2740 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2741 ULONG depotBlockIndexPos;
2744 assert(depotBlockCount < This->bigBlockDepotCount);
2745 assert(blockIndex != nextBlock);
2747 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2749 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2754 * We have to look in the extended depot.
2756 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2759 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2763 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2764 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2768 * Update the cached block depot, if necessary.
2770 if (depotBlockCount == This->indexBlockDepotCached)
2772 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2776 /******************************************************************************
2777 * Storage32Impl_LoadFileHeader
2779 * This method will read in the file header, i.e. big block index -1.
2781 HRESULT StorageImpl_LoadFileHeader(
2784 HRESULT hr = STG_E_FILENOTFOUND;
2785 void* headerBigBlock = NULL;
2789 * Get a pointer to the big block of data containing the header.
2791 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2794 * Extract the information from the header.
2796 if (headerBigBlock!=0)
2799 * Check for the "magic number" signature and return an error if it is not
2802 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2804 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2805 return STG_E_OLDFORMAT;
2808 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2810 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2811 return STG_E_INVALIDHEADER;
2814 StorageUtl_ReadWord(
2816 OFFSET_BIGBLOCKSIZEBITS,
2817 &This->bigBlockSizeBits);
2819 StorageUtl_ReadWord(
2821 OFFSET_SMALLBLOCKSIZEBITS,
2822 &This->smallBlockSizeBits);
2824 StorageUtl_ReadDWord(
2826 OFFSET_BBDEPOTCOUNT,
2827 &This->bigBlockDepotCount);
2829 StorageUtl_ReadDWord(
2831 OFFSET_ROOTSTARTBLOCK,
2832 &This->rootStartBlock);
2834 StorageUtl_ReadDWord(
2836 OFFSET_SBDEPOTSTART,
2837 &This->smallBlockDepotStart);
2839 StorageUtl_ReadDWord(
2841 OFFSET_EXTBBDEPOTSTART,
2842 &This->extBigBlockDepotStart);
2844 StorageUtl_ReadDWord(
2846 OFFSET_EXTBBDEPOTCOUNT,
2847 &This->extBigBlockDepotCount);
2849 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2851 StorageUtl_ReadDWord(
2853 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2854 &(This->bigBlockDepotStart[index]));
2858 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2862 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2863 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2867 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2868 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2872 * Right now, the code is making some assumptions about the size of the
2873 * blocks, just make sure they are what we're expecting.
2875 assert( (This->bigBlockSize==DEF_BIG_BLOCK_SIZE) &&
2876 (This->smallBlockSize==DEF_SMALL_BLOCK_SIZE));
2879 * Release the block.
2881 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2889 /******************************************************************************
2890 * Storage32Impl_SaveFileHeader
2892 * This method will save to the file the header, i.e. big block -1.
2894 void StorageImpl_SaveFileHeader(
2897 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2902 * Get a pointer to the big block of data containing the header.
2904 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2907 * If the block read failed, the file is probably new.
2912 * Initialize for all unknown fields.
2914 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
2917 * Initialize the magic number.
2919 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
2922 * And a bunch of things we don't know what they mean
2924 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
2925 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
2926 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
2927 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
2928 StorageUtl_WriteDWord(headerBigBlock, 0x40, (DWORD)0x0001);
2932 * Write the information to the header.
2934 if (headerBigBlock!=0)
2936 StorageUtl_WriteWord(
2938 OFFSET_BIGBLOCKSIZEBITS,
2939 This->bigBlockSizeBits);
2941 StorageUtl_WriteWord(
2943 OFFSET_SMALLBLOCKSIZEBITS,
2944 This->smallBlockSizeBits);
2946 StorageUtl_WriteDWord(
2948 OFFSET_BBDEPOTCOUNT,
2949 This->bigBlockDepotCount);
2951 StorageUtl_WriteDWord(
2953 OFFSET_ROOTSTARTBLOCK,
2954 This->rootStartBlock);
2956 StorageUtl_WriteDWord(
2958 OFFSET_SBDEPOTSTART,
2959 This->smallBlockDepotStart);
2961 StorageUtl_WriteDWord(
2963 OFFSET_EXTBBDEPOTSTART,
2964 This->extBigBlockDepotStart);
2966 StorageUtl_WriteDWord(
2968 OFFSET_EXTBBDEPOTCOUNT,
2969 This->extBigBlockDepotCount);
2971 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2973 StorageUtl_WriteDWord(
2975 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2976 (This->bigBlockDepotStart[index]));
2981 * Write the big block back to the file.
2983 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
2986 /******************************************************************************
2987 * Storage32Impl_ReadProperty
2989 * This method will read the specified property from the property chain.
2991 BOOL StorageImpl_ReadProperty(
2994 StgProperty* buffer)
2996 BYTE currentProperty[PROPSET_BLOCK_SIZE];
2997 ULARGE_INTEGER offsetInPropSet;
2998 BOOL readSuccessful;
3001 offsetInPropSet.s.HighPart = 0;
3002 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3004 readSuccessful = BlockChainStream_ReadAt(
3005 This->rootBlockChain,
3013 memset(buffer->name, 0, sizeof(buffer->name));
3016 currentProperty+OFFSET_PS_NAME,
3017 PROPERTY_NAME_BUFFER_LEN );
3019 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3021 StorageUtl_ReadWord(
3023 OFFSET_PS_NAMELENGTH,
3024 &buffer->sizeOfNameString);
3026 StorageUtl_ReadDWord(
3028 OFFSET_PS_PREVIOUSPROP,
3029 &buffer->previousProperty);
3031 StorageUtl_ReadDWord(
3034 &buffer->nextProperty);
3036 StorageUtl_ReadDWord(
3039 &buffer->dirProperty);
3041 StorageUtl_ReadGUID(
3044 &buffer->propertyUniqueID);
3046 StorageUtl_ReadDWord(
3049 &buffer->timeStampS1);
3051 StorageUtl_ReadDWord(
3054 &buffer->timeStampD1);
3056 StorageUtl_ReadDWord(
3059 &buffer->timeStampS2);
3061 StorageUtl_ReadDWord(
3064 &buffer->timeStampD2);
3066 StorageUtl_ReadDWord(
3068 OFFSET_PS_STARTBLOCK,
3069 &buffer->startingBlock);
3071 StorageUtl_ReadDWord(
3074 &buffer->size.s.LowPart);
3076 buffer->size.s.HighPart = 0;
3079 return readSuccessful;
3082 /*********************************************************************
3083 * Write the specified property into the property chain
3085 BOOL StorageImpl_WriteProperty(
3088 StgProperty* buffer)
3090 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3091 ULARGE_INTEGER offsetInPropSet;
3092 BOOL writeSuccessful;
3095 offsetInPropSet.s.HighPart = 0;
3096 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3098 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3101 currentProperty + OFFSET_PS_NAME,
3103 PROPERTY_NAME_BUFFER_LEN );
3105 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3107 StorageUtl_WriteWord(
3109 OFFSET_PS_NAMELENGTH,
3110 buffer->sizeOfNameString);
3112 StorageUtl_WriteDWord(
3114 OFFSET_PS_PREVIOUSPROP,
3115 buffer->previousProperty);
3117 StorageUtl_WriteDWord(
3120 buffer->nextProperty);
3122 StorageUtl_WriteDWord(
3125 buffer->dirProperty);
3127 StorageUtl_WriteGUID(
3130 &buffer->propertyUniqueID);
3132 StorageUtl_WriteDWord(
3135 buffer->timeStampS1);
3137 StorageUtl_WriteDWord(
3140 buffer->timeStampD1);
3142 StorageUtl_WriteDWord(
3145 buffer->timeStampS2);
3147 StorageUtl_WriteDWord(
3150 buffer->timeStampD2);
3152 StorageUtl_WriteDWord(
3154 OFFSET_PS_STARTBLOCK,
3155 buffer->startingBlock);
3157 StorageUtl_WriteDWord(
3160 buffer->size.s.LowPart);
3162 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3167 return writeSuccessful;
3170 BOOL StorageImpl_ReadBigBlock(
3175 void* bigBlockBuffer;
3177 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3179 if (bigBlockBuffer!=0)
3181 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3183 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3191 BOOL StorageImpl_WriteBigBlock(
3196 void* bigBlockBuffer;
3198 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3200 if (bigBlockBuffer!=0)
3202 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3204 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3212 void* StorageImpl_GetROBigBlock(
3216 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3219 void* StorageImpl_GetBigBlock(
3223 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3226 void StorageImpl_ReleaseBigBlock(
3230 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3233 /******************************************************************************
3234 * Storage32Impl_SmallBlocksToBigBlocks
3236 * This method will convert a small block chain to a big block chain.
3237 * The small block chain will be destroyed.
3239 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3241 SmallBlockChainStream** ppsbChain)
3243 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3244 ULARGE_INTEGER size, offset;
3245 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3246 ULONG propertyIndex;
3247 BOOL successRead, successWrite;
3248 StgProperty chainProperty;
3250 BlockChainStream *bbTempChain = NULL;
3251 BlockChainStream *bigBlockChain = NULL;
3254 * Create a temporary big block chain that doesn't have
3255 * an associated property. This temporary chain will be
3256 * used to copy data from small blocks to big blocks.
3258 bbTempChain = BlockChainStream_Construct(This,
3263 * Grow the big block chain.
3265 size = SmallBlockChainStream_GetSize(*ppsbChain);
3266 BlockChainStream_SetSize(bbTempChain, size);
3269 * Copy the contents of the small block chain to the big block chain
3270 * by small block size increments.
3272 offset.s.LowPart = 0;
3273 offset.s.HighPart = 0;
3277 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3280 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3282 DEF_SMALL_BLOCK_SIZE,
3285 cbTotalRead += cbRead;
3287 successWrite = BlockChainStream_WriteAt(bbTempChain,
3292 cbTotalWritten += cbWritten;
3294 offset.s.LowPart += This->smallBlockSize;
3296 } while (successRead && successWrite);
3297 HeapFree(GetProcessHeap(),0,buffer);
3299 assert(cbTotalRead == cbTotalWritten);
3302 * Destroy the small block chain.
3304 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3305 size.s.HighPart = 0;
3307 SmallBlockChainStream_SetSize(*ppsbChain, size);
3308 SmallBlockChainStream_Destroy(*ppsbChain);
3312 * Change the property information. This chain is now a big block chain
3313 * and it doesn't reside in the small blocks chain anymore.
3315 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3317 chainProperty.startingBlock = bbHeadOfChain;
3319 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3322 * Destroy the temporary propertyless big block chain.
3323 * Create a new big block chain associated with this property.
3325 BlockChainStream_Destroy(bbTempChain);
3326 bigBlockChain = BlockChainStream_Construct(This,
3330 return bigBlockChain;
3333 /******************************************************************************
3334 ** Storage32InternalImpl implementation
3337 StorageInternalImpl* StorageInternalImpl_Construct(
3338 StorageImpl* ancestorStorage,
3339 ULONG rootPropertyIndex)
3341 StorageInternalImpl* newStorage;
3344 * Allocate space for the new storage object
3346 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3350 memset(newStorage, 0, sizeof(StorageInternalImpl));
3353 * Initialize the virtual function table.
3355 ICOM_VTBL(newStorage) = &Storage32InternalImpl_Vtbl;
3356 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3359 * Keep the ancestor storage pointer and nail a reference to it.
3361 newStorage->ancestorStorage = ancestorStorage;
3362 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3365 * Keep the index of the root property set for this storage,
3367 newStorage->rootPropertySetIndex = rootPropertyIndex;
3375 void StorageInternalImpl_Destroy(
3376 StorageInternalImpl* This)
3378 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3379 HeapFree(GetProcessHeap(), 0, This);
3382 /******************************************************************************
3384 ** Storage32InternalImpl_Commit
3386 ** The non-root storages cannot be opened in transacted mode thus this function
3389 HRESULT WINAPI StorageInternalImpl_Commit(
3391 DWORD grfCommitFlags) /* [in] */
3396 /******************************************************************************
3398 ** Storage32InternalImpl_Revert
3400 ** The non-root storages cannot be opened in transacted mode thus this function
3403 HRESULT WINAPI StorageInternalImpl_Revert(
3409 /******************************************************************************
3410 ** IEnumSTATSTGImpl implementation
3413 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3414 StorageImpl* parentStorage,
3415 ULONG firstPropertyNode)
3417 IEnumSTATSTGImpl* newEnumeration;
3419 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3421 if (newEnumeration!=0)
3424 * Set-up the virtual function table and reference count.
3426 ICOM_VTBL(newEnumeration) = &IEnumSTATSTGImpl_Vtbl;
3427 newEnumeration->ref = 0;
3430 * We want to nail-down the reference to the storage in case the
3431 * enumeration out-lives the storage in the client application.
3433 newEnumeration->parentStorage = parentStorage;
3434 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3436 newEnumeration->firstPropertyNode = firstPropertyNode;
3439 * Initialize the search stack
3441 newEnumeration->stackSize = 0;
3442 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3443 newEnumeration->stackToVisit =
3444 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3447 * Make sure the current node of the iterator is the first one.
3449 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3452 return newEnumeration;
3455 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3457 IStorage_Release((IStorage*)This->parentStorage);
3458 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3459 HeapFree(GetProcessHeap(), 0, This);
3462 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3463 IEnumSTATSTG* iface,
3467 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3470 * Perform a sanity check on the parameters.
3473 return E_INVALIDARG;
3476 * Initialize the return parameter.
3481 * Compare the riid with the interface IDs implemented by this object.
3483 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3485 *ppvObject = (IEnumSTATSTG*)This;
3487 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3489 *ppvObject = (IEnumSTATSTG*)This;
3493 * Check that we obtained an interface.
3495 if ((*ppvObject)==0)
3496 return E_NOINTERFACE;
3499 * Query Interface always increases the reference count by one when it is
3502 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3507 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3508 IEnumSTATSTG* iface)
3510 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3516 ULONG WINAPI IEnumSTATSTGImpl_Release(
3517 IEnumSTATSTG* iface)
3519 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3527 * If the reference count goes down to 0, perform suicide.
3531 IEnumSTATSTGImpl_Destroy(This);
3537 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3538 IEnumSTATSTG* iface,
3541 ULONG* pceltFetched)
3543 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3545 StgProperty currentProperty;
3546 STATSTG* currentReturnStruct = rgelt;
3547 ULONG objectFetched = 0;
3548 ULONG currentSearchNode;
3551 * Perform a sanity check on the parameters.
3553 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3554 return E_INVALIDARG;
3557 * To avoid the special case, get another pointer to a ULONG value if
3558 * the caller didn't supply one.
3560 if (pceltFetched==0)
3561 pceltFetched = &objectFetched;
3564 * Start the iteration, we will iterate until we hit the end of the
3565 * linked list or until we hit the number of items to iterate through
3570 * Start with the node at the top of the stack.
3572 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3574 while ( ( *pceltFetched < celt) &&
3575 ( currentSearchNode!=PROPERTY_NULL) )
3578 * Remove the top node from the stack
3580 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3583 * Read the property from the storage.
3585 StorageImpl_ReadProperty(This->parentStorage,
3590 * Copy the information to the return buffer.
3592 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3597 * Step to the next item in the iteration
3600 currentReturnStruct++;
3603 * Push the next search node in the search stack.
3605 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3608 * continue the iteration.
3610 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3613 if (*pceltFetched == celt)
3620 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3621 IEnumSTATSTG* iface,
3624 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3626 StgProperty currentProperty;
3627 ULONG objectFetched = 0;
3628 ULONG currentSearchNode;
3631 * Start with the node at the top of the stack.
3633 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3635 while ( (objectFetched < celt) &&
3636 (currentSearchNode!=PROPERTY_NULL) )
3639 * Remove the top node from the stack
3641 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3644 * Read the property from the storage.
3646 StorageImpl_ReadProperty(This->parentStorage,
3651 * Step to the next item in the iteration
3656 * Push the next search node in the search stack.
3658 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3661 * continue the iteration.
3663 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3666 if (objectFetched == celt)
3672 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3673 IEnumSTATSTG* iface)
3675 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3677 StgProperty rootProperty;
3678 BOOL readSuccessful;
3681 * Re-initialize the search stack to an empty stack
3683 This->stackSize = 0;
3686 * Read the root property from the storage.
3688 readSuccessful = StorageImpl_ReadProperty(
3689 This->parentStorage,
3690 This->firstPropertyNode,
3695 assert(rootProperty.sizeOfNameString!=0);
3698 * Push the search node in the search stack.
3700 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3706 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3707 IEnumSTATSTG* iface,
3708 IEnumSTATSTG** ppenum)
3710 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3712 IEnumSTATSTGImpl* newClone;
3715 * Perform a sanity check on the parameters.
3718 return E_INVALIDARG;
3720 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3721 This->firstPropertyNode);
3725 * The new clone enumeration must point to the same current node as
3728 newClone->stackSize = This->stackSize ;
3729 newClone->stackMaxSize = This->stackMaxSize ;
3730 newClone->stackToVisit =
3731 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3734 newClone->stackToVisit,
3736 sizeof(ULONG) * newClone->stackSize);
3738 *ppenum = (IEnumSTATSTG*)newClone;
3741 * Don't forget to nail down a reference to the clone before
3744 IEnumSTATSTGImpl_AddRef(*ppenum);
3749 INT IEnumSTATSTGImpl_FindParentProperty(
3750 IEnumSTATSTGImpl *This,
3751 ULONG childProperty,
3752 StgProperty *currentProperty,
3755 ULONG currentSearchNode;
3759 * To avoid the special case, get another pointer to a ULONG value if
3760 * the caller didn't supply one.
3763 thisNodeId = &foundNode;
3766 * Start with the node at the top of the stack.
3768 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3771 while (currentSearchNode!=PROPERTY_NULL)
3774 * Store the current node in the returned parameters
3776 *thisNodeId = currentSearchNode;
3779 * Remove the top node from the stack
3781 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3784 * Read the property from the storage.
3786 StorageImpl_ReadProperty(
3787 This->parentStorage,
3791 if (currentProperty->previousProperty == childProperty)
3792 return PROPERTY_RELATION_PREVIOUS;
3794 else if (currentProperty->nextProperty == childProperty)
3795 return PROPERTY_RELATION_NEXT;
3797 else if (currentProperty->dirProperty == childProperty)
3798 return PROPERTY_RELATION_DIR;
3801 * Push the next search node in the search stack.
3803 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3806 * continue the iteration.
3808 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3811 return PROPERTY_NULL;
3814 ULONG IEnumSTATSTGImpl_FindProperty(
3815 IEnumSTATSTGImpl* This,
3816 const OLECHAR* lpszPropName,
3817 StgProperty* currentProperty)
3819 ULONG currentSearchNode;
3822 * Start with the node at the top of the stack.
3824 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3826 while (currentSearchNode!=PROPERTY_NULL)
3829 * Remove the top node from the stack
3831 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3834 * Read the property from the storage.
3836 StorageImpl_ReadProperty(This->parentStorage,
3840 if ( propertyNameCmp(
3841 (OLECHAR*)currentProperty->name,
3842 (OLECHAR*)lpszPropName) == 0)
3843 return currentSearchNode;
3846 * Push the next search node in the search stack.
3848 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3851 * continue the iteration.
3853 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3856 return PROPERTY_NULL;
3859 void IEnumSTATSTGImpl_PushSearchNode(
3860 IEnumSTATSTGImpl* This,
3863 StgProperty rootProperty;
3864 BOOL readSuccessful;
3867 * First, make sure we're not trying to push an unexisting node.
3869 if (nodeToPush==PROPERTY_NULL)
3873 * First push the node to the stack
3875 if (This->stackSize == This->stackMaxSize)
3877 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3879 This->stackToVisit = HeapReAlloc(
3883 sizeof(ULONG) * This->stackMaxSize);
3886 This->stackToVisit[This->stackSize] = nodeToPush;
3890 * Read the root property from the storage.
3892 readSuccessful = StorageImpl_ReadProperty(
3893 This->parentStorage,
3899 assert(rootProperty.sizeOfNameString!=0);
3902 * Push the previous search node in the search stack.
3904 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3908 ULONG IEnumSTATSTGImpl_PopSearchNode(
3909 IEnumSTATSTGImpl* This,
3914 if (This->stackSize == 0)
3915 return PROPERTY_NULL;
3917 topNode = This->stackToVisit[This->stackSize-1];
3925 /******************************************************************************
3926 ** StorageUtl implementation
3929 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
3931 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
3934 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
3936 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
3939 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
3941 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
3944 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
3946 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
3949 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
3951 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
3952 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
3953 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
3955 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
3958 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
3960 StorageUtl_WriteDWord(buffer, offset, value->Data1);
3961 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
3962 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
3964 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
3967 void StorageUtl_CopyPropertyToSTATSTG(
3968 STATSTG* destination,
3969 StgProperty* source,
3973 * The copy of the string occurs only when the flag is not set
3975 if ((statFlags & STATFLAG_NONAME) != 0)
3977 destination->pwcsName = 0;
3981 destination->pwcsName =
3982 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
3984 lstrcpyW((LPWSTR)destination->pwcsName, source->name);
3987 switch (source->propertyType)
3989 case PROPTYPE_STORAGE:
3991 destination->type = STGTY_STORAGE;
3993 case PROPTYPE_STREAM:
3994 destination->type = STGTY_STREAM;
3997 destination->type = STGTY_STREAM;
4001 destination->cbSize = source->size;
4003 currentReturnStruct->mtime = {0}; TODO
4004 currentReturnStruct->ctime = {0};
4005 currentReturnStruct->atime = {0};
4007 destination->grfMode = 0;
4008 destination->grfLocksSupported = 0;
4009 destination->clsid = source->propertyUniqueID;
4010 destination->grfStateBits = 0;
4011 destination->reserved = 0;
4014 /******************************************************************************
4015 ** BlockChainStream implementation
4018 BlockChainStream* BlockChainStream_Construct(
4019 StorageImpl* parentStorage,
4020 ULONG* headOfStreamPlaceHolder,
4021 ULONG propertyIndex)
4023 BlockChainStream* newStream;
4026 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4028 newStream->parentStorage = parentStorage;
4029 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4030 newStream->ownerPropertyIndex = propertyIndex;
4031 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4032 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4033 newStream->numBlocks = 0;
4035 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4037 while (blockIndex != BLOCK_END_OF_CHAIN)
4039 newStream->numBlocks++;
4040 newStream->tailIndex = blockIndex;
4042 blockIndex = StorageImpl_GetNextBlockInChain(
4050 void BlockChainStream_Destroy(BlockChainStream* This)
4052 HeapFree(GetProcessHeap(), 0, This);
4055 /******************************************************************************
4056 * BlockChainStream_GetHeadOfChain
4058 * Returns the head of this stream chain.
4059 * Some special chains don't have properties, their heads are kept in
4060 * This->headOfStreamPlaceHolder.
4063 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4065 StgProperty chainProperty;
4066 BOOL readSuccessful;
4068 if (This->headOfStreamPlaceHolder != 0)
4069 return *(This->headOfStreamPlaceHolder);
4071 if (This->ownerPropertyIndex != PROPERTY_NULL)
4073 readSuccessful = StorageImpl_ReadProperty(
4074 This->parentStorage,
4075 This->ownerPropertyIndex,
4080 return chainProperty.startingBlock;
4084 return BLOCK_END_OF_CHAIN;
4087 /******************************************************************************
4088 * BlockChainStream_GetCount
4090 * Returns the number of blocks that comprises this chain.
4091 * This is not the size of the stream as the last block may not be full!
4094 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4099 blockIndex = BlockChainStream_GetHeadOfChain(This);
4101 while (blockIndex != BLOCK_END_OF_CHAIN)
4105 blockIndex = StorageImpl_GetNextBlockInChain(
4106 This->parentStorage,
4113 /******************************************************************************
4114 * BlockChainStream_ReadAt
4116 * Reads a specified number of bytes from this chain at the specified offset.
4117 * bytesRead may be NULL.
4118 * Failure will be returned if the specified number of bytes has not been read.
4120 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4121 ULARGE_INTEGER offset,
4126 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4127 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4128 ULONG bytesToReadInBuffer;
4131 BYTE* bigBlockBuffer;
4134 * Find the first block in the stream that contains part of the buffer.
4136 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4137 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4138 (blockNoInSequence < This->lastBlockNoInSequence) )
4140 blockIndex = BlockChainStream_GetHeadOfChain(This);
4141 This->lastBlockNoInSequence = blockNoInSequence;
4145 ULONG temp = blockNoInSequence;
4147 blockIndex = This->lastBlockNoInSequenceIndex;
4148 blockNoInSequence -= This->lastBlockNoInSequence;
4149 This->lastBlockNoInSequence = temp;
4152 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4155 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4157 blockNoInSequence--;
4160 This->lastBlockNoInSequenceIndex = blockIndex;
4163 * Start reading the buffer.
4166 bufferWalker = buffer;
4168 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4171 * Calculate how many bytes we can copy from this big block.
4173 bytesToReadInBuffer =
4174 MIN(This->parentStorage->bigBlockSize - offsetInBlock, size);
4177 * Copy those bytes to the buffer
4180 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4182 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4184 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4187 * Step to the next big block.
4190 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4192 bufferWalker += bytesToReadInBuffer;
4193 size -= bytesToReadInBuffer;
4194 *bytesRead += bytesToReadInBuffer;
4195 offsetInBlock = 0; /* There is no offset on the next block */
4202 /******************************************************************************
4203 * BlockChainStream_WriteAt
4205 * Writes the specified number of bytes to this chain at the specified offset.
4206 * bytesWritten may be NULL.
4207 * Will fail if not all specified number of bytes have been written.
4209 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4210 ULARGE_INTEGER offset,
4213 ULONG* bytesWritten)
4215 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4216 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4220 BYTE* bigBlockBuffer;
4223 * Find the first block in the stream that contains part of the buffer.
4225 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4226 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4227 (blockNoInSequence < This->lastBlockNoInSequence) )
4229 blockIndex = BlockChainStream_GetHeadOfChain(This);
4230 This->lastBlockNoInSequence = blockNoInSequence;
4234 ULONG temp = blockNoInSequence;
4236 blockIndex = This->lastBlockNoInSequenceIndex;
4237 blockNoInSequence -= This->lastBlockNoInSequence;
4238 This->lastBlockNoInSequence = temp;
4241 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4244 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4246 blockNoInSequence--;
4249 This->lastBlockNoInSequenceIndex = blockIndex;
4252 * Here, I'm casting away the constness on the buffer variable
4253 * This is OK since we don't intend to modify that buffer.
4256 bufferWalker = (BYTE*)buffer;
4258 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4261 * Calculate how many bytes we can copy from this big block.
4264 MIN(This->parentStorage->bigBlockSize - offsetInBlock, size);
4267 * Copy those bytes to the buffer
4269 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4271 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4273 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4276 * Step to the next big block.
4279 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4281 bufferWalker += bytesToWrite;
4282 size -= bytesToWrite;
4283 *bytesWritten += bytesToWrite;
4284 offsetInBlock = 0; /* There is no offset on the next block */
4290 /******************************************************************************
4291 * BlockChainStream_Shrink
4293 * Shrinks this chain in the big block depot.
4295 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4296 ULARGE_INTEGER newSize)
4298 ULONG blockIndex, extraBlock;
4303 * Reset the last accessed block cache.
4305 This->lastBlockNoInSequence = 0xFFFFFFFF;
4306 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4309 * Figure out how many blocks are needed to contain the new size
4311 numBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4313 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4316 blockIndex = BlockChainStream_GetHeadOfChain(This);
4319 * Go to the new end of chain
4321 while (count < numBlocks)
4324 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4329 /* Get the next block before marking the new end */
4331 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4333 /* Mark the new end of chain */
4334 StorageImpl_SetNextBlockInChain(
4335 This->parentStorage,
4337 BLOCK_END_OF_CHAIN);
4339 This->tailIndex = blockIndex;
4340 This->numBlocks = numBlocks;
4343 * Mark the extra blocks as free
4345 while (extraBlock != BLOCK_END_OF_CHAIN)
4348 StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock);
4350 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4351 extraBlock = blockIndex;
4357 /******************************************************************************
4358 * BlockChainStream_Enlarge
4360 * Grows this chain in the big block depot.
4362 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4363 ULARGE_INTEGER newSize)
4365 ULONG blockIndex, currentBlock;
4367 ULONG oldNumBlocks = 0;
4369 blockIndex = BlockChainStream_GetHeadOfChain(This);
4372 * Empty chain. Create the head.
4374 if (blockIndex == BLOCK_END_OF_CHAIN)
4376 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4377 StorageImpl_SetNextBlockInChain(This->parentStorage,
4379 BLOCK_END_OF_CHAIN);
4381 if (This->headOfStreamPlaceHolder != 0)
4383 *(This->headOfStreamPlaceHolder) = blockIndex;
4387 StgProperty chainProp;
4388 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4390 StorageImpl_ReadProperty(
4391 This->parentStorage,
4392 This->ownerPropertyIndex,
4395 chainProp.startingBlock = blockIndex;
4397 StorageImpl_WriteProperty(
4398 This->parentStorage,
4399 This->ownerPropertyIndex,
4403 This->tailIndex = blockIndex;
4404 This->numBlocks = 1;
4408 * Figure out how many blocks are needed to contain this stream
4410 newNumBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4412 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4416 * Go to the current end of chain
4418 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4420 currentBlock = blockIndex;
4422 while (blockIndex != BLOCK_END_OF_CHAIN)
4425 currentBlock = blockIndex;
4428 StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock);
4431 This->tailIndex = currentBlock;
4434 currentBlock = This->tailIndex;
4435 oldNumBlocks = This->numBlocks;
4438 * Add new blocks to the chain
4440 if (oldNumBlocks < newNumBlocks)
4442 while (oldNumBlocks < newNumBlocks)
4444 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4446 StorageImpl_SetNextBlockInChain(
4447 This->parentStorage,
4451 StorageImpl_SetNextBlockInChain(
4452 This->parentStorage,
4454 BLOCK_END_OF_CHAIN);
4456 currentBlock = blockIndex;
4460 This->tailIndex = blockIndex;
4461 This->numBlocks = newNumBlocks;
4467 /******************************************************************************
4468 * BlockChainStream_SetSize
4470 * Sets the size of this stream. The big block depot will be updated.
4471 * The file will grow if we grow the chain.
4473 * TODO: Free the actual blocks in the file when we shrink the chain.
4474 * Currently, the blocks are still in the file. So the file size
4475 * doesn't shrink even if we shrink streams.
4477 BOOL BlockChainStream_SetSize(
4478 BlockChainStream* This,
4479 ULARGE_INTEGER newSize)
4481 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4483 if (newSize.s.LowPart == size.s.LowPart)
4486 if (newSize.s.LowPart < size.s.LowPart)
4488 BlockChainStream_Shrink(This, newSize);
4492 ULARGE_INTEGER fileSize =
4493 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4495 ULONG diff = newSize.s.LowPart - size.s.LowPart;
4498 * Make sure the file stays a multiple of blocksize
4500 if ((diff % This->parentStorage->bigBlockSize) != 0)
4501 diff += (This->parentStorage->bigBlockSize -
4502 (diff % This->parentStorage->bigBlockSize) );
4504 fileSize.s.LowPart += diff;
4505 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4507 BlockChainStream_Enlarge(This, newSize);
4513 /******************************************************************************
4514 * BlockChainStream_GetSize
4516 * Returns the size of this chain.
4517 * Will return the block count if this chain doesn't have a property.
4519 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4521 StgProperty chainProperty;
4523 if(This->headOfStreamPlaceHolder == NULL)
4526 * This chain is a data stream read the property and return
4527 * the appropriate size
4529 StorageImpl_ReadProperty(
4530 This->parentStorage,
4531 This->ownerPropertyIndex,
4534 return chainProperty.size;
4539 * this chain is a chain that does not have a property, figure out the
4540 * size by making the product number of used blocks times the
4543 ULARGE_INTEGER result;
4544 result.s.HighPart = 0;
4547 BlockChainStream_GetCount(This) *
4548 This->parentStorage->bigBlockSize;
4554 /******************************************************************************
4555 ** SmallBlockChainStream implementation
4558 SmallBlockChainStream* SmallBlockChainStream_Construct(
4559 StorageImpl* parentStorage,
4560 ULONG propertyIndex)
4562 SmallBlockChainStream* newStream;
4564 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4566 newStream->parentStorage = parentStorage;
4567 newStream->ownerPropertyIndex = propertyIndex;
4572 void SmallBlockChainStream_Destroy(
4573 SmallBlockChainStream* This)
4575 HeapFree(GetProcessHeap(), 0, This);
4578 /******************************************************************************
4579 * SmallBlockChainStream_GetHeadOfChain
4581 * Returns the head of this chain of small blocks.
4583 ULONG SmallBlockChainStream_GetHeadOfChain(
4584 SmallBlockChainStream* This)
4586 StgProperty chainProperty;
4587 BOOL readSuccessful;
4589 if (This->ownerPropertyIndex)
4591 readSuccessful = StorageImpl_ReadProperty(
4592 This->parentStorage,
4593 This->ownerPropertyIndex,
4598 return chainProperty.startingBlock;
4603 return BLOCK_END_OF_CHAIN;
4606 /******************************************************************************
4607 * SmallBlockChainStream_GetNextBlockInChain
4609 * Returns the index of the next small block in this chain.
4612 * - BLOCK_END_OF_CHAIN: end of this chain
4613 * - BLOCK_UNUSED: small block 'blockIndex' is free
4615 ULONG SmallBlockChainStream_GetNextBlockInChain(
4616 SmallBlockChainStream* This,
4619 ULARGE_INTEGER offsetOfBlockInDepot;
4621 ULONG nextBlockInChain = BLOCK_END_OF_CHAIN;
4625 offsetOfBlockInDepot.s.HighPart = 0;
4626 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4629 * Read those bytes in the buffer from the small block file.
4631 success = BlockChainStream_ReadAt(
4632 This->parentStorage->smallBlockDepotChain,
4633 offsetOfBlockInDepot,
4640 StorageUtl_ReadDWord(&buffer, 0, &nextBlockInChain);
4643 return nextBlockInChain;
4646 /******************************************************************************
4647 * SmallBlockChainStream_SetNextBlockInChain
4649 * Writes the index of the next block of the specified block in the small
4651 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4652 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4654 void SmallBlockChainStream_SetNextBlockInChain(
4655 SmallBlockChainStream* This,
4659 ULARGE_INTEGER offsetOfBlockInDepot;
4663 offsetOfBlockInDepot.s.HighPart = 0;
4664 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4666 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4669 * Read those bytes in the buffer from the small block file.
4671 BlockChainStream_WriteAt(
4672 This->parentStorage->smallBlockDepotChain,
4673 offsetOfBlockInDepot,
4679 /******************************************************************************
4680 * SmallBlockChainStream_FreeBlock
4682 * Flag small block 'blockIndex' as free in the small block depot.
4684 void SmallBlockChainStream_FreeBlock(
4685 SmallBlockChainStream* This,
4688 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4691 /******************************************************************************
4692 * SmallBlockChainStream_GetNextFreeBlock
4694 * Returns the index of a free small block. The small block depot will be
4695 * enlarged if necessary. The small block chain will also be enlarged if
4698 ULONG SmallBlockChainStream_GetNextFreeBlock(
4699 SmallBlockChainStream* This)
4701 ULARGE_INTEGER offsetOfBlockInDepot;
4704 ULONG blockIndex = 0;
4705 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4706 BOOL success = TRUE;
4707 ULONG smallBlocksPerBigBlock;
4709 offsetOfBlockInDepot.s.HighPart = 0;
4712 * Scan the small block depot for a free block
4714 while (nextBlockIndex != BLOCK_UNUSED)
4716 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4718 success = BlockChainStream_ReadAt(
4719 This->parentStorage->smallBlockDepotChain,
4720 offsetOfBlockInDepot,
4726 * If we run out of space for the small block depot, enlarge it
4730 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4732 if (nextBlockIndex != BLOCK_UNUSED)
4738 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4740 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4741 ULONG nextBlock, newsbdIndex;
4742 BYTE* smallBlockDepot;
4744 nextBlock = sbdIndex;
4745 while (nextBlock != BLOCK_END_OF_CHAIN)
4747 sbdIndex = nextBlock;
4749 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex);
4752 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4753 if (sbdIndex != BLOCK_END_OF_CHAIN)
4754 StorageImpl_SetNextBlockInChain(
4755 This->parentStorage,
4759 StorageImpl_SetNextBlockInChain(
4760 This->parentStorage,
4762 BLOCK_END_OF_CHAIN);
4765 * Initialize all the small blocks to free
4768 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4770 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4771 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4776 * We have just created the small block depot.
4778 StgProperty rootProp;
4782 * Save it in the header
4784 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4785 StorageImpl_SaveFileHeader(This->parentStorage);
4788 * And allocate the first big block that will contain small blocks
4791 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4793 StorageImpl_SetNextBlockInChain(
4794 This->parentStorage,
4796 BLOCK_END_OF_CHAIN);
4798 StorageImpl_ReadProperty(
4799 This->parentStorage,
4800 This->parentStorage->rootPropertySetIndex,
4803 rootProp.startingBlock = sbStartIndex;
4804 rootProp.size.s.HighPart = 0;
4805 rootProp.size.s.LowPart = This->parentStorage->bigBlockSize;
4807 StorageImpl_WriteProperty(
4808 This->parentStorage,
4809 This->parentStorage->rootPropertySetIndex,
4815 smallBlocksPerBigBlock =
4816 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4819 * Verify if we have to allocate big blocks to contain small blocks
4821 if (blockIndex % smallBlocksPerBigBlock == 0)
4823 StgProperty rootProp;
4824 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4826 StorageImpl_ReadProperty(
4827 This->parentStorage,
4828 This->parentStorage->rootPropertySetIndex,
4831 if (rootProp.size.s.LowPart <
4832 (blocksRequired * This->parentStorage->bigBlockSize))
4834 rootProp.size.s.LowPart += This->parentStorage->bigBlockSize;
4836 BlockChainStream_SetSize(
4837 This->parentStorage->smallBlockRootChain,
4840 StorageImpl_WriteProperty(
4841 This->parentStorage,
4842 This->parentStorage->rootPropertySetIndex,
4850 /******************************************************************************
4851 * SmallBlockChainStream_ReadAt
4853 * Reads a specified number of bytes from this chain at the specified offset.
4854 * bytesRead may be NULL.
4855 * Failure will be returned if the specified number of bytes has not been read.
4857 BOOL SmallBlockChainStream_ReadAt(
4858 SmallBlockChainStream* This,
4859 ULARGE_INTEGER offset,
4864 ULARGE_INTEGER offsetInBigBlockFile;
4865 ULONG blockNoInSequence =
4866 offset.s.LowPart / This->parentStorage->smallBlockSize;
4868 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
4869 ULONG bytesToReadInBuffer;
4871 ULONG bytesReadFromBigBlockFile;
4875 * This should never happen on a small block file.
4877 assert(offset.s.HighPart==0);
4880 * Find the first block in the stream that contains part of the buffer.
4882 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4884 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4886 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4888 blockNoInSequence--;
4892 * Start reading the buffer.
4895 bufferWalker = buffer;
4897 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4900 * Calculate how many bytes we can copy from this small block.
4902 bytesToReadInBuffer =
4903 MIN(This->parentStorage->smallBlockSize - offsetInBlock, size);
4906 * Calculate the offset of the small block in the small block file.
4908 offsetInBigBlockFile.s.HighPart = 0;
4909 offsetInBigBlockFile.s.LowPart =
4910 blockIndex * This->parentStorage->smallBlockSize;
4912 offsetInBigBlockFile.s.LowPart += offsetInBlock;
4915 * Read those bytes in the buffer from the small block file.
4917 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
4918 offsetInBigBlockFile,
4919 bytesToReadInBuffer,
4921 &bytesReadFromBigBlockFile);
4923 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
4926 * Step to the next big block.
4928 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4929 bufferWalker += bytesToReadInBuffer;
4930 size -= bytesToReadInBuffer;
4931 *bytesRead += bytesToReadInBuffer;
4932 offsetInBlock = 0; /* There is no offset on the next block */
4938 /******************************************************************************
4939 * SmallBlockChainStream_WriteAt
4941 * Writes the specified number of bytes to this chain at the specified offset.
4942 * bytesWritten may be NULL.
4943 * Will fail if not all specified number of bytes have been written.
4945 BOOL SmallBlockChainStream_WriteAt(
4946 SmallBlockChainStream* This,
4947 ULARGE_INTEGER offset,
4950 ULONG* bytesWritten)
4952 ULARGE_INTEGER offsetInBigBlockFile;
4953 ULONG blockNoInSequence =
4954 offset.s.LowPart / This->parentStorage->smallBlockSize;
4956 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
4957 ULONG bytesToWriteInBuffer;
4959 ULONG bytesWrittenFromBigBlockFile;
4963 * This should never happen on a small block file.
4965 assert(offset.s.HighPart==0);
4968 * Find the first block in the stream that contains part of the buffer.
4970 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4972 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4974 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4976 blockNoInSequence--;
4980 * Start writing the buffer.
4982 * Here, I'm casting away the constness on the buffer variable
4983 * This is OK since we don't intend to modify that buffer.
4986 bufferWalker = (BYTE*)buffer;
4987 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4990 * Calculate how many bytes we can copy to this small block.
4992 bytesToWriteInBuffer =
4993 MIN(This->parentStorage->smallBlockSize - offsetInBlock, size);
4996 * Calculate the offset of the small block in the small block file.
4998 offsetInBigBlockFile.s.HighPart = 0;
4999 offsetInBigBlockFile.s.LowPart =
5000 blockIndex * This->parentStorage->smallBlockSize;
5002 offsetInBigBlockFile.s.LowPart += offsetInBlock;
5005 * Write those bytes in the buffer to the small block file.
5007 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5008 offsetInBigBlockFile,
5009 bytesToWriteInBuffer,
5011 &bytesWrittenFromBigBlockFile);
5013 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5016 * Step to the next big block.
5018 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5019 bufferWalker += bytesToWriteInBuffer;
5020 size -= bytesToWriteInBuffer;
5021 *bytesWritten += bytesToWriteInBuffer;
5022 offsetInBlock = 0; /* There is no offset on the next block */
5028 /******************************************************************************
5029 * SmallBlockChainStream_Shrink
5031 * Shrinks this chain in the small block depot.
5033 BOOL SmallBlockChainStream_Shrink(
5034 SmallBlockChainStream* This,
5035 ULARGE_INTEGER newSize)
5037 ULONG blockIndex, extraBlock;
5041 numBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5043 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5046 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5049 * Go to the new end of chain
5051 while (count < numBlocks)
5053 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5058 * If the count is 0, we have a special case, the head of the chain was
5063 StgProperty chainProp;
5065 StorageImpl_ReadProperty(This->parentStorage,
5066 This->ownerPropertyIndex,
5069 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5071 StorageImpl_WriteProperty(This->parentStorage,
5072 This->ownerPropertyIndex,
5076 * We start freeing the chain at the head block.
5078 extraBlock = blockIndex;
5082 /* Get the next block before marking the new end */
5083 extraBlock = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5085 /* Mark the new end of chain */
5086 SmallBlockChainStream_SetNextBlockInChain(
5089 BLOCK_END_OF_CHAIN);
5093 * Mark the extra blocks as free
5095 while (extraBlock != BLOCK_END_OF_CHAIN)
5097 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, extraBlock);
5098 SmallBlockChainStream_FreeBlock(This, extraBlock);
5099 extraBlock = blockIndex;
5105 /******************************************************************************
5106 * SmallBlockChainStream_Enlarge
5108 * Grows this chain in the small block depot.
5110 BOOL SmallBlockChainStream_Enlarge(
5111 SmallBlockChainStream* This,
5112 ULARGE_INTEGER newSize)
5114 ULONG blockIndex, currentBlock;
5116 ULONG oldNumBlocks = 0;
5118 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5123 if (blockIndex == BLOCK_END_OF_CHAIN)
5126 StgProperty chainProp;
5128 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5131 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5133 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5136 blockIndex = chainProp.startingBlock;
5137 SmallBlockChainStream_SetNextBlockInChain(
5140 BLOCK_END_OF_CHAIN);
5143 currentBlock = blockIndex;
5146 * Figure out how many blocks are needed to contain this stream
5148 newNumBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5150 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5154 * Go to the current end of chain
5156 while (blockIndex != BLOCK_END_OF_CHAIN)
5159 currentBlock = blockIndex;
5160 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, currentBlock);
5164 * Add new blocks to the chain
5166 while (oldNumBlocks < newNumBlocks)
5168 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5169 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5171 SmallBlockChainStream_SetNextBlockInChain(
5174 BLOCK_END_OF_CHAIN);
5176 currentBlock = blockIndex;
5183 /******************************************************************************
5184 * SmallBlockChainStream_GetCount
5186 * Returns the number of blocks that comprises this chain.
5187 * This is not the size of this chain as the last block may not be full!
5189 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5194 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5196 while (blockIndex != BLOCK_END_OF_CHAIN)
5200 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5206 /******************************************************************************
5207 * SmallBlockChainStream_SetSize
5209 * Sets the size of this stream.
5210 * The file will grow if we grow the chain.
5212 * TODO: Free the actual blocks in the file when we shrink the chain.
5213 * Currently, the blocks are still in the file. So the file size
5214 * doesn't shrink even if we shrink streams.
5216 BOOL SmallBlockChainStream_SetSize(
5217 SmallBlockChainStream* This,
5218 ULARGE_INTEGER newSize)
5220 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5222 if (newSize.s.LowPart == size.s.LowPart)
5225 if (newSize.s.LowPart < size.s.LowPart)
5227 SmallBlockChainStream_Shrink(This, newSize);
5231 SmallBlockChainStream_Enlarge(This, newSize);
5237 /******************************************************************************
5238 * SmallBlockChainStream_GetSize
5240 * Returns the size of this chain.
5242 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5244 StgProperty chainProperty;
5246 StorageImpl_ReadProperty(
5247 This->parentStorage,
5248 This->ownerPropertyIndex,
5251 return chainProperty.size;
5254 /******************************************************************************
5255 * StgCreateDocfile32 [OLE32.144]
5257 HRESULT WINAPI StgCreateDocfile(
5261 IStorage **ppstgOpen)
5263 StorageImpl* newStorage = 0;
5264 HANDLE hFile = INVALID_HANDLE_VALUE;
5269 DWORD fileAttributes;
5270 WCHAR tempFileName[MAX_PATH];
5272 TRACE("(%s, %lx, %ld, %p)\n",
5273 debugstr_w(pwcsName), grfMode,
5274 reserved, ppstgOpen);
5277 * Validate the parameters
5280 return STG_E_INVALIDPOINTER;
5283 * Validate the STGM flags
5285 if ( FAILED( validateSTGM(grfMode) ))
5286 return STG_E_INVALIDFLAG;
5289 * Generate a unique name.
5293 WCHAR tempPath[MAX_PATH];
5294 WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5296 memset(tempPath, 0, sizeof(tempPath));
5297 memset(tempFileName, 0, sizeof(tempFileName));
5299 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5302 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5303 pwcsName = tempFileName;
5305 return STG_E_INSUFFICIENTMEMORY;
5309 * Interpret the STGM value grfMode
5311 shareMode = GetShareModeFromSTGM(grfMode);
5312 accessMode = GetAccessModeFromSTGM(grfMode);
5313 creationMode = GetCreationModeFromSTGM(grfMode);
5315 if (grfMode & STGM_DELETEONRELEASE)
5316 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5318 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5320 if (grfMode & STGM_TRANSACTED)
5321 FIXME("Transacted mode not implemented.\n");
5324 * Initialize the "out" parameter.
5328 hFile = CreateFileW(pwcsName,
5336 if (hFile == INVALID_HANDLE_VALUE)
5342 * Allocate and initialize the new IStorage32object.
5344 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5346 if (newStorage == 0)
5347 return STG_E_INSUFFICIENTMEMORY;
5349 hr = StorageImpl_Construct(
5359 HeapFree(GetProcessHeap(), 0, newStorage);
5364 * Get an "out" pointer for the caller.
5366 hr = StorageBaseImpl_QueryInterface(
5367 (IStorage*)newStorage,
5368 (REFIID)&IID_IStorage,
5374 /******************************************************************************
5375 * StgOpenStorage32 [OLE32.148]
5377 HRESULT WINAPI StgOpenStorage(
5378 const OLECHAR *pwcsName,
5379 IStorage *pstgPriority,
5383 IStorage **ppstgOpen)
5385 StorageImpl* newStorage = 0;
5391 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5392 debugstr_w(pwcsName), pstgPriority, grfMode,
5393 snbExclude, reserved, ppstgOpen);
5396 * Perform a sanity check
5398 if (( pwcsName == 0) || (ppstgOpen == 0) )
5399 return STG_E_INVALIDPOINTER;
5402 * Validate the STGM flags
5404 if ( FAILED( validateSTGM(grfMode) ))
5405 return STG_E_INVALIDFLAG;
5408 * Interpret the STGM value grfMode
5410 shareMode = GetShareModeFromSTGM(grfMode);
5411 accessMode = GetAccessModeFromSTGM(grfMode);
5414 * Initialize the "out" parameter.
5418 hFile = CreateFileW( pwcsName,
5423 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5427 if (hFile==INVALID_HANDLE_VALUE)
5429 HRESULT hr = E_FAIL;
5430 DWORD last_error = GetLastError();
5434 case ERROR_FILE_NOT_FOUND:
5435 hr = STG_E_FILENOTFOUND;
5438 case ERROR_PATH_NOT_FOUND:
5439 hr = STG_E_PATHNOTFOUND;
5442 case ERROR_ACCESS_DENIED:
5443 case ERROR_WRITE_PROTECT:
5444 hr = STG_E_ACCESSDENIED;
5447 case ERROR_SHARING_VIOLATION:
5448 hr = STG_E_SHAREVIOLATION;
5459 * Allocate and initialize the new IStorage32object.
5461 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5463 if (newStorage == 0)
5464 return STG_E_INSUFFICIENTMEMORY;
5466 hr = StorageImpl_Construct(
5476 HeapFree(GetProcessHeap(), 0, newStorage);
5481 * Get an "out" pointer for the caller.
5483 hr = StorageBaseImpl_QueryInterface(
5484 (IStorage*)newStorage,
5485 (REFIID)&IID_IStorage,
5491 /******************************************************************************
5492 * StgCreateDocfileOnILockBytes [OLE32.145]
5494 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5498 IStorage** ppstgOpen)
5500 StorageImpl* newStorage = 0;
5504 * Validate the parameters
5506 if ((ppstgOpen == 0) || (plkbyt == 0))
5507 return STG_E_INVALIDPOINTER;
5510 * Allocate and initialize the new IStorage object.
5512 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5514 if (newStorage == 0)
5515 return STG_E_INSUFFICIENTMEMORY;
5517 hr = StorageImpl_Construct(
5527 HeapFree(GetProcessHeap(), 0, newStorage);
5532 * Get an "out" pointer for the caller.
5534 hr = StorageBaseImpl_QueryInterface(
5535 (IStorage*)newStorage,
5536 (REFIID)&IID_IStorage,
5542 /******************************************************************************
5543 * StgOpenStorageOnILockBytes [OLE32.149]
5545 HRESULT WINAPI StgOpenStorageOnILockBytes(
5547 IStorage *pstgPriority,
5551 IStorage **ppstgOpen)
5553 StorageImpl* newStorage = 0;
5557 * Perform a sanity check
5559 if ((plkbyt == 0) || (ppstgOpen == 0))
5560 return STG_E_INVALIDPOINTER;
5563 * Validate the STGM flags
5565 if ( FAILED( validateSTGM(grfMode) ))
5566 return STG_E_INVALIDFLAG;
5569 * Initialize the "out" parameter.
5574 * Allocate and initialize the new IStorage object.
5576 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5578 if (newStorage == 0)
5579 return STG_E_INSUFFICIENTMEMORY;
5581 hr = StorageImpl_Construct(
5591 HeapFree(GetProcessHeap(), 0, newStorage);
5596 * Get an "out" pointer for the caller.
5598 hr = StorageBaseImpl_QueryInterface(
5599 (IStorage*)newStorage,
5600 (REFIID)&IID_IStorage,
5606 /******************************************************************************
5607 * StgSetTimes [ole32.150]
5611 HRESULT WINAPI StgSetTimes(WCHAR * str, FILETIME * a, FILETIME * b, FILETIME *c )
5614 FIXME("(%p, %p, %p, %p),stub!\n", str, a, b, c);
5618 /******************************************************************************
5619 * StgIsStorageILockBytes [OLE32.147]
5621 * Determines if the ILockBytes contains a storage object.
5623 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5626 ULARGE_INTEGER offset;
5628 offset.s.HighPart = 0;
5629 offset.s.LowPart = 0;
5631 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5633 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5639 /******************************************************************************
5640 * WriteClassStg32 [OLE32.158]
5642 * This method will store the specified CLSID in the specified storage object
5644 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5650 hRes = IStorage_SetClass(pStg, rclsid);
5655 /*******************************************************************************************
5658 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5660 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5670 * read a STATSTG structure (contains the clsid) from the storage
5672 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5675 *pclsid=pstatstg.clsid;
5680 /*************************************************************************************
5683 * This function loads an object from stream
5685 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5690 FIXME("(),stub!\n");
5692 res=ReadClassStm(pStm,&clsid);
5694 if (SUCCEEDED(res)){
5696 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5700 res=IPersistStream_Load((IPersistStream*)ppvObj,pStm);
5706 /************************************************************************************************
5709 * This function saves an object with the IPersistStream interface on it to the specified stream
5711 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5717 TRACE("(%p,%p)\n",pPStm,pStm);
5719 res=IPersistStream_GetClassID(pPStm,&clsid);
5721 if (SUCCEEDED(res)){
5723 res=WriteClassStm(pStm,&clsid);
5727 res=IPersistStream_Save(pPStm,pStm,TRUE);
5730 TRACE("Finished Save\n");
5734 /****************************************************************************
5735 * This method validate a STGM parameter that can contain the values below
5737 * STGM_DIRECT 0x00000000
5738 * STGM_TRANSACTED 0x00010000
5739 * STGM_SIMPLE 0x08000000
5741 * STGM_READ 0x00000000
5742 * STGM_WRITE 0x00000001
5743 * STGM_READWRITE 0x00000002
5745 * STGM_SHARE_DENY_NONE 0x00000040
5746 * STGM_SHARE_DENY_READ 0x00000030
5747 * STGM_SHARE_DENY_WRITE 0x00000020
5748 * STGM_SHARE_EXCLUSIVE 0x00000010
5750 * STGM_PRIORITY 0x00040000
5751 * STGM_DELETEONRELEASE 0x04000000
5753 * STGM_CREATE 0x00001000
5754 * STGM_CONVERT 0x00020000
5755 * STGM_FAILIFTHERE 0x00000000
5757 * STGM_NOSCRATCH 0x00100000
5758 * STGM_NOSNAPSHOT 0x00200000
5760 static HRESULT validateSTGM(DWORD stgm)
5762 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5763 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5764 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5766 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5767 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5768 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5770 BOOL bSTGM_SHARE_DENY_NONE =
5771 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5773 BOOL bSTGM_SHARE_DENY_READ =
5774 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5776 BOOL bSTGM_SHARE_DENY_WRITE =
5777 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5779 BOOL bSTGM_SHARE_EXCLUSIVE =
5780 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5782 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5783 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5785 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5786 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5789 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5791 if ( ! bSTGM_DIRECT )
5792 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5796 * STGM_WRITE | STGM_READWRITE | STGM_READ
5799 if( bSTGM_WRITE && bSTGM_READWRITE )
5803 * STGM_SHARE_DENY_NONE | others
5804 * (I assume here that DENY_READ implies DENY_WRITE)
5806 if ( bSTGM_SHARE_DENY_NONE )
5807 if ( bSTGM_SHARE_DENY_READ ||
5808 bSTGM_SHARE_DENY_WRITE ||
5809 bSTGM_SHARE_EXCLUSIVE)
5813 * STGM_CREATE | STGM_CONVERT
5814 * if both are false, STGM_FAILIFTHERE is set to TRUE
5816 if ( bSTGM_CREATE && bSTGM_CONVERT )
5820 * STGM_NOSCRATCH requires STGM_TRANSACTED
5822 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
5826 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
5827 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5829 if (bSTGM_NOSNAPSHOT)
5831 if ( ! ( bSTGM_TRANSACTED &&
5832 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
5839 /****************************************************************************
5840 * GetShareModeFromSTGM
5842 * This method will return a share mode flag from a STGM value.
5843 * The STGM value is assumed valid.
5845 static DWORD GetShareModeFromSTGM(DWORD stgm)
5847 DWORD dwShareMode = 0;
5848 BOOL bSTGM_SHARE_DENY_NONE =
5849 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5851 BOOL bSTGM_SHARE_DENY_READ =
5852 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5854 BOOL bSTGM_SHARE_DENY_WRITE =
5855 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5857 BOOL bSTGM_SHARE_EXCLUSIVE =
5858 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5860 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
5863 if (bSTGM_SHARE_DENY_NONE)
5864 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5866 if (bSTGM_SHARE_DENY_WRITE)
5867 dwShareMode = FILE_SHARE_READ;
5872 /****************************************************************************
5873 * GetAccessModeFromSTGM
5875 * This method will return an access mode flag from a STGM value.
5876 * The STGM value is assumed valid.
5878 static DWORD GetAccessModeFromSTGM(DWORD stgm)
5880 DWORD dwDesiredAccess = GENERIC_READ;
5881 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5882 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5883 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5886 dwDesiredAccess = GENERIC_READ;
5889 dwDesiredAccess |= GENERIC_WRITE;
5891 if (bSTGM_READWRITE)
5892 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
5894 return dwDesiredAccess;
5897 /****************************************************************************
5898 * GetCreationModeFromSTGM
5900 * This method will return a creation mode flag from a STGM value.
5901 * The STGM value is assumed valid.
5903 static DWORD GetCreationModeFromSTGM(DWORD stgm)
5905 if ( stgm & STGM_CREATE)
5906 return CREATE_ALWAYS;
5907 if (stgm & STGM_CONVERT) {
5908 FIXME("STGM_CONVERT not implemented!\n");
5911 /* All other cases */
5912 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
5913 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
5918 /*************************************************************************
5919 * OLECONVERT_LoadOLE10 [Internal]
5921 * Loads the OLE10 STREAM to memory
5924 * pOleStream [I] The OLESTREAM
5925 * pData [I] Data Structure for the OLESTREAM Data
5929 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
5930 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
5933 * This function is used by OleConvertOLESTREAMToIStorage only.
5935 * Memory allocated for pData must be freed by the caller
5937 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
5940 HRESULT hRes = S_OK;
5944 pData->pData = NULL;
5945 pData->pstrOleObjFileName = (CHAR *) NULL;
5947 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
5950 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
5951 if(dwSize != sizeof(pData->dwOleID))
5953 hRes = CONVERT10_E_OLESTREAM_GET;
5955 else if(pData->dwOleID != OLESTREAM_ID)
5957 hRes = CONVERT10_E_OLESTREAM_FMT;
5968 /* Get the TypeID...more info needed for this field */
5969 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
5970 if(dwSize != sizeof(pData->dwTypeID))
5972 hRes = CONVERT10_E_OLESTREAM_GET;
5977 if(pData->dwTypeID != 0)
5979 /* Get the lenght of the OleTypeName */
5980 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
5981 if(dwSize != sizeof(pData->dwOleTypeNameLength))
5983 hRes = CONVERT10_E_OLESTREAM_GET;
5988 if(pData->dwOleTypeNameLength > 0)
5990 /* Get the OleTypeName */
5991 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
5992 if(dwSize != pData->dwOleTypeNameLength)
5994 hRes = CONVERT10_E_OLESTREAM_GET;
6000 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6001 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6003 hRes = CONVERT10_E_OLESTREAM_GET;
6007 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6008 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6009 pData->pstrOleObjFileName = (CHAR *)malloc(pData->dwOleObjFileNameLength);
6010 if(pData->pstrOleObjFileName)
6012 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6013 if(dwSize != pData->dwOleObjFileNameLength)
6015 hRes = CONVERT10_E_OLESTREAM_GET;
6019 hRes = CONVERT10_E_OLESTREAM_GET;
6024 /* Get the Width of the Metafile */
6025 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6026 if(dwSize != sizeof(pData->dwMetaFileWidth))
6028 hRes = CONVERT10_E_OLESTREAM_GET;
6032 /* Get the Height of the Metafile */
6033 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6034 if(dwSize != sizeof(pData->dwMetaFileHeight))
6036 hRes = CONVERT10_E_OLESTREAM_GET;
6042 /* Get the Lenght of the Data */
6043 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6044 if(dwSize != sizeof(pData->dwDataLength))
6046 hRes = CONVERT10_E_OLESTREAM_GET;
6050 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6052 if(!bStrem1) /* if it is a second OLE stream data */
6054 pData->dwDataLength -= 8;
6055 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6056 if(dwSize != sizeof(pData->strUnknown))
6058 hRes = CONVERT10_E_OLESTREAM_GET;
6064 if(pData->dwDataLength > 0)
6066 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6068 /* Get Data (ex. IStorage, Metafile, or BMP) */
6071 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6072 if(dwSize != pData->dwDataLength)
6074 hRes = CONVERT10_E_OLESTREAM_GET;
6079 hRes = CONVERT10_E_OLESTREAM_GET;
6088 /*************************************************************************
6089 * OLECONVERT_SaveOLE10 [Internal]
6091 * Saves the OLE10 STREAM From memory
6094 * pData [I] Data Structure for the OLESTREAM Data
6095 * pOleStream [I] The OLESTREAM to save
6099 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6102 * This function is used by OleConvertIStorageToOLESTREAM only.
6105 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6108 HRESULT hRes = S_OK;
6112 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6113 if(dwSize != sizeof(pData->dwOleID))
6115 hRes = CONVERT10_E_OLESTREAM_PUT;
6120 /* Set the TypeID */
6121 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6122 if(dwSize != sizeof(pData->dwTypeID))
6124 hRes = CONVERT10_E_OLESTREAM_PUT;
6128 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6130 /* Set the Lenght of the OleTypeName */
6131 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6132 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6134 hRes = CONVERT10_E_OLESTREAM_PUT;
6139 if(pData->dwOleTypeNameLength > 0)
6141 /* Set the OleTypeName */
6142 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6143 if(dwSize != pData->dwOleTypeNameLength)
6145 hRes = CONVERT10_E_OLESTREAM_PUT;
6152 /* Set the width of the Metafile */
6153 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6154 if(dwSize != sizeof(pData->dwMetaFileWidth))
6156 hRes = CONVERT10_E_OLESTREAM_PUT;
6162 /* Set the height of the Metafile */
6163 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6164 if(dwSize != sizeof(pData->dwMetaFileHeight))
6166 hRes = CONVERT10_E_OLESTREAM_PUT;
6172 /* Set the lenght of the Data */
6173 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6174 if(dwSize != sizeof(pData->dwDataLength))
6176 hRes = CONVERT10_E_OLESTREAM_PUT;
6182 if(pData->dwDataLength > 0)
6184 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6185 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6186 if(dwSize != pData->dwDataLength)
6188 hRes = CONVERT10_E_OLESTREAM_PUT;
6196 /*************************************************************************
6197 * OLECONVERT_GetOLE20FromOLE10[Internal]
6199 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6200 * opens it, and copies the content to the dest IStorage for
6201 * OleConvertOLESTREAMToIStorage
6205 * pDestStorage [I] The IStorage to copy the data to
6206 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6207 * nBufferLength [I] The size of the buffer
6216 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6220 IStorage *pTempStorage;
6221 DWORD dwNumOfBytesWritten;
6222 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6223 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6225 /* Create a temp File */
6226 GetTempPathW(MAX_PATH, wstrTempDir);
6227 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6228 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6230 if(hFile != INVALID_HANDLE_VALUE)
6232 /* Write IStorage Data to File */
6233 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6236 /* Open and copy temp storage to the Dest Storage */
6237 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6240 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6241 StorageBaseImpl_Release(pTempStorage);
6243 DeleteFileW(wstrTempFile);
6248 /*************************************************************************
6249 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6251 * Saves the OLE10 STREAM From memory
6254 * pStorage [I] The Src IStorage to copy
6255 * pData [I] The Dest Memory to write to.
6258 * The size in bytes allocated for pData
6261 * Memory allocated for pData must be freed by the caller
6263 * Used by OleConvertIStorageToOLESTREAM only.
6266 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6270 DWORD nDataLength = 0;
6271 IStorage *pTempStorage;
6272 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6273 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6277 /* Create temp Storage */
6278 GetTempPathW(MAX_PATH, wstrTempDir);
6279 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6280 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6284 /* Copy Src Storage to the Temp Storage */
6285 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6286 StorageBaseImpl_Release(pTempStorage);
6288 /* Open Temp Storage as a file and copy to memory */
6289 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6290 if(hFile != INVALID_HANDLE_VALUE)
6292 nDataLength = GetFileSize(hFile, NULL);
6293 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6294 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6297 DeleteFileW(wstrTempFile);
6302 /*************************************************************************
6303 * OLECONVERT_CreateOleStream [Internal]
6305 * Creates the "\001OLE" stream in the IStorage if neccessary.
6308 * pStorage [I] Dest storage to create the stream in
6314 * This function is used by OleConvertOLESTREAMToIStorage only.
6316 * This stream is still unknown, MS Word seems to have extra data
6317 * but since the data is stored in the OLESTREAM there should be
6318 * no need to recreate the stream. If the stream is manually
6319 * deleted it will create it with this default data.
6322 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6326 WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6327 BYTE pOleStreamHeader [] =
6329 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6330 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6331 0x00, 0x00, 0x00, 0x00
6334 /* Create stream if not present */
6335 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6336 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6340 /* Write default Data */
6341 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6342 IStream_Release(pStream);
6347 /*************************************************************************
6348 * OLECONVERT_CreateCompObjStream [Internal]
6350 * Creates a "\001CompObj" is the destination IStorage if necessary.
6353 * pStorage [I] The dest IStorage to create the CompObj Stream
6355 * strOleTypeName [I] The ProgID
6359 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6362 * This function is used by OleConvertOLESTREAMToIStorage only.
6364 * The stream data is stored in the OLESTREAM and there should be
6365 * no need to recreate the stream. If the stream is manually
6366 * deleted it will attempt to create it by querying the registry.
6370 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6373 HRESULT hStorageRes, hRes = S_OK;
6374 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6375 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6377 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6378 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6380 /* Initialize the CompObj structure */
6381 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6382 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6383 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6386 /* Create a CompObj stream if it doesn't exist */
6387 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6388 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6389 if(hStorageRes == S_OK)
6391 /* copy the OleTypeName to the compobj struct */
6392 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6393 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6395 /* copy the OleTypeName to the compobj struct */
6396 /* Note: in the test made, these where Identical */
6397 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6398 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6401 hRes = CLSIDFromProgID16(IStorageCompObj.strProgIDName, &(IStorageCompObj.clsid));
6405 hRes = REGDB_E_CLASSNOTREG;
6411 /* Get the CLSID Default Name from the Registry */
6412 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6413 if(hErr == ERROR_SUCCESS)
6415 char strTemp[OLESTREAM_MAX_STR_LEN];
6416 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6417 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6418 if(hErr == ERROR_SUCCESS)
6420 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6424 if(hErr != ERROR_SUCCESS)
6426 hRes = REGDB_E_CLASSNOTREG;
6432 /* Write CompObj Structure to stream */
6433 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6434 hRes = IStream_Write(pStream, &(IStorageCompObj.clsid) , sizeof(IStorageCompObj.clsid ), NULL);
6435 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6436 if(IStorageCompObj.dwCLSIDNameLength > 0)
6438 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6440 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6441 if(IStorageCompObj.dwOleTypeNameLength > 0)
6443 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6445 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6446 if(IStorageCompObj.dwProgIDNameLength > 0)
6448 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6450 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6452 IStream_Release(pStream);
6458 /*************************************************************************
6459 * OLECONVERT_CreateOlePresStream[Internal]
6461 * Creates the "\002OlePres000" Stream with the Metafile data
6464 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6465 * dwExtentX [I] Width of the Metafile
6466 * dwExtentY [I] Height of the Metafile
6467 * pData [I] Metafile data
6468 * dwDataLength [I] Size of the Metafile data
6472 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6475 * This function is used by OleConvertOLESTREAMToIStorage only.
6478 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6482 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6483 BYTE pOlePresStreamHeader [] =
6485 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6486 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6487 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6488 0x00, 0x00, 0x00, 0x00
6491 BYTE pOlePresStreamHeaderEmpty [] =
6493 0x00, 0x00, 0x00, 0x00,
6494 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6495 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6496 0x00, 0x00, 0x00, 0x00
6499 /* Create the OlePres000 Stream */
6500 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6501 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6506 OLECONVERT_ISTORAGE_OLEPRES OlePres;
6508 memset(&OlePres, 0, sizeof(OlePres));
6509 /* Do we have any metafile data to save */
6510 if(dwDataLength > 0)
6512 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6513 nHeaderSize = sizeof(pOlePresStreamHeader);
6517 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
6518 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
6520 /* Set width and height of the metafile */
6521 OlePres.dwExtentX = dwExtentX;
6522 OlePres.dwExtentY = -dwExtentY;
6524 /* Set Data and Lenght */
6525 if(dwDataLength > sizeof(METAFILEPICT16))
6527 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
6528 OlePres.pData = &(pData[8]);
6530 /* Save OlePres000 Data to Stream */
6531 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
6532 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
6533 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
6534 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
6535 if(OlePres.dwSize > 0)
6537 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
6539 IStream_Release(pStream);
6543 /*************************************************************************
6544 * OLECONVERT_CreateOle10NativeStream [Internal]
6546 * Creates the "\001Ole10Native" Stream (should contain a BMP)
6549 * pStorage [I] Dest storage to create the stream in
6550 * pData [I] Ole10 Native Data (ex. bmp)
6551 * dwDataLength [I] Size of the Ole10 Native Data
6557 * This function is used by OleConvertOLESTREAMToIStorage only.
6559 * Might need to verify the data and return appropriate error message
6562 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
6566 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6568 /* Create the Ole10Native Stream */
6569 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6570 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6574 /* Write info to stream */
6575 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
6576 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
6577 IStream_Release(pStream);
6582 /*************************************************************************
6583 * OLECONVERT_GetOLE10ProgID [Internal]
6585 * Finds the ProgID (or OleTypeID) from the IStorage
6588 * pStorage [I] The Src IStorage to get the ProgID
6589 * strProgID [I] the ProgID string to get
6590 * dwSize [I] the size of the string
6594 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6597 * This function is used by OleConvertIStorageToOLESTREAM only.
6601 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
6605 LARGE_INTEGER iSeekPos;
6606 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
6607 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6609 /* Open the CompObj Stream */
6610 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6611 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6615 /*Get the OleType from the CompObj Stream */
6616 iSeekPos.s.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
6617 iSeekPos.s.HighPart = 0;
6619 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6620 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
6621 iSeekPos.s.LowPart = CompObj.dwCLSIDNameLength;
6622 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6623 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
6624 iSeekPos.s.LowPart = CompObj.dwOleTypeNameLength;
6625 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6627 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
6630 IStream_Read(pStream, strProgID, *dwSize, NULL);
6632 IStream_Release(pStream);
6637 LPOLESTR wstrProgID;
6639 /* Get the OleType from the registry */
6640 REFCLSID clsid = &(stat.clsid);
6641 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
6642 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
6645 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
6652 /*************************************************************************
6653 * OLECONVERT_GetOle10PresData [Internal]
6655 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
6658 * pStorage [I] Src IStroage
6659 * pOleStream [I] Dest OleStream Mem Struct
6665 * This function is used by OleConvertIStorageToOLESTREAM only.
6667 * Memory allocated for pData must be freed by the caller
6671 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6676 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6678 /* Initialize Default data for OLESTREAM */
6679 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6680 pOleStreamData[0].dwTypeID = 2;
6681 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6682 pOleStreamData[1].dwTypeID = 0;
6683 pOleStreamData[0].dwMetaFileWidth = 0;
6684 pOleStreamData[0].dwMetaFileHeight = 0;
6685 pOleStreamData[0].pData = NULL;
6686 pOleStreamData[1].pData = NULL;
6688 /* Open Ole10Native Stream */
6689 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6690 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6694 /* Read Size and Data */
6695 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
6696 if(pOleStreamData->dwDataLength > 0)
6698 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
6699 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
6701 IStream_Release(pStream);
6707 /*************************************************************************
6708 * OLECONVERT_GetOle20PresData[Internal]
6710 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
6713 * pStorage [I] Src IStroage
6714 * pOleStreamData [I] Dest OleStream Mem Struct
6720 * This function is used by OleConvertIStorageToOLESTREAM only.
6722 * Memory allocated for pData must be freed by the caller
6724 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6728 OLECONVERT_ISTORAGE_OLEPRES olePress;
6729 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6731 /* Initialize Default data for OLESTREAM */
6732 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6733 pOleStreamData[0].dwTypeID = 2;
6734 pOleStreamData[0].dwMetaFileWidth = 0;
6735 pOleStreamData[0].dwMetaFileHeight = 0;
6736 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
6737 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6738 pOleStreamData[1].dwTypeID = 0;
6739 pOleStreamData[1].dwOleTypeNameLength = 0;
6740 pOleStreamData[1].strOleTypeName[0] = 0;
6741 pOleStreamData[1].dwMetaFileWidth = 0;
6742 pOleStreamData[1].dwMetaFileHeight = 0;
6743 pOleStreamData[1].pData = NULL;
6744 pOleStreamData[1].dwDataLength = 0;
6747 /* Open OlePress000 stream */
6748 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6749 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6752 LARGE_INTEGER iSeekPos;
6753 METAFILEPICT16 MetaFilePict;
6754 char strMetafilePictName[] = "METAFILEPICT";
6756 /* Set the TypeID for a Metafile */
6757 pOleStreamData[1].dwTypeID = 5;
6759 /* Set the OleTypeName to Metafile */
6760 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
6761 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
6763 iSeekPos.s.HighPart = 0;
6764 iSeekPos.s.LowPart = sizeof(olePress.byUnknown1);
6766 /* Get Presentation Data */
6767 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6768 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
6769 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
6770 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
6772 /*Set width and Height */
6773 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
6774 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
6775 if(olePress.dwSize > 0)
6778 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
6780 /* Set MetaFilePict struct */
6781 MetaFilePict.mm = 8;
6782 MetaFilePict.xExt = olePress.dwExtentX;
6783 MetaFilePict.yExt = olePress.dwExtentY;
6784 MetaFilePict.hMF = 0;
6786 /* Get Metafile Data */
6787 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
6788 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
6789 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
6791 IStream_Release(pStream);
6795 /*************************************************************************
6796 * OleConvertOLESTREAMToIStorage [OLE32.87]
6801 * DVTARGETDEVICE paramenter is not handled
6802 * Still unsure of some mem fields for OLE 10 Stream
6803 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6804 * and "\001OLE" streams
6807 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
6808 LPOLESTREAM pOleStream,
6810 const DVTARGETDEVICE* ptd)
6814 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
6816 memset(pOleStreamData, 0, sizeof(pOleStreamData));
6820 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
6823 if(pstg == NULL || pOleStream == NULL)
6825 hRes = E_INVALIDARG;
6830 /* Load the OLESTREAM to Memory */
6831 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
6836 /* Load the OLESTREAM to Memory (part 2)*/
6837 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
6843 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
6845 /* Do we have the IStorage Data in the OLESTREAM */
6846 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
6848 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6849 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
6853 /* It must be an original OLE 1.0 source */
6854 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6859 /* It must be an original OLE 1.0 source */
6860 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6863 /* Create CompObj Stream if necessary */
6864 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
6867 /*Create the Ole Stream if necessary */
6868 OLECONVERT_CreateOleStream(pstg);
6873 /* Free allocated memory */
6874 for(i=0; i < 2; i++)
6876 if(pOleStreamData[i].pData != NULL)
6878 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
6880 if(pOleStreamData[i].pstrOleObjFileName != NULL)
6882 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
6883 pOleStreamData[i].pstrOleObjFileName = NULL;
6889 /*************************************************************************
6890 * OleConvertIStorageToOLESTREAM [OLE32.85]
6897 * Still unsure of some mem fields for OLE 10 Stream
6898 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6899 * and "\001OLE" streams.
6902 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
6904 LPOLESTREAM pOleStream)
6907 HRESULT hRes = S_OK;
6909 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
6910 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6913 memset(pOleStreamData, 0, sizeof(pOleStreamData));
6915 if(pstg == NULL || pOleStream == NULL)
6917 hRes = E_INVALIDARG;
6921 /* Get the ProgID */
6922 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
6923 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
6927 /*Was it originaly Ole10 */
6928 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
6931 IStream_Release(pStream);
6932 /*Get Presentation Data for Ole10Native */
6933 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
6937 /*Get Presentation Data (OLE20)*/
6938 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
6941 /* Save OLESTREAM */
6942 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
6945 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
6950 /* Free allocated memory */
6951 for(i=0; i < 2; i++)
6953 if(pOleStreamData[i].pData != NULL)
6955 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);