2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
18 #include "winbase.h" /* for lstrlenW() and the likes */
20 #include "wine/unicode.h"
21 #include "debugtools.h"
23 #include "storage32.h"
24 #include "ole2.h" /* For Write/ReadClassStm */
27 #include "wine/wingdi16.h"
29 DEFAULT_DEBUG_CHANNEL(storage);
34 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
35 #define OLESTREAM_ID 0x501
36 #define OLESTREAM_MAX_STR_LEN 255
38 static const char rootPropertyName[] = "Root Entry";
41 /* OLESTREAM memory structure to use for Get and Put Routines */
42 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
47 DWORD dwOleTypeNameLength;
48 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
49 CHAR *pstrOleObjFileName;
50 DWORD dwOleObjFileNameLength;
51 DWORD dwMetaFileWidth;
52 DWORD dwMetaFileHeight;
53 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
56 }OLECONVERT_OLESTREAM_DATA;
58 /* CompObj Stream structure */
59 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
64 DWORD dwCLSIDNameLength;
65 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
66 DWORD dwOleTypeNameLength;
67 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
68 DWORD dwProgIDNameLength;
69 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
71 }OLECONVERT_ISTORAGE_COMPOBJ;
74 /* Ole Presention Stream structure */
75 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
83 }OLECONVERT_ISTORAGE_OLEPRES;
87 /***********************************************************************
88 * Forward declaration of internal functions used by the method DestroyElement
90 static HRESULT deleteStorageProperty(
91 StorageImpl *parentStorage,
92 ULONG foundPropertyIndexToDelete,
93 StgProperty propertyToDelete);
95 static HRESULT deleteStreamProperty(
96 StorageImpl *parentStorage,
97 ULONG foundPropertyIndexToDelete,
98 StgProperty propertyToDelete);
100 static HRESULT findPlaceholder(
101 StorageImpl *storage,
102 ULONG propertyIndexToStore,
103 ULONG storagePropertyIndex,
106 static HRESULT adjustPropertyChain(
108 StgProperty propertyToDelete,
109 StgProperty parentProperty,
110 ULONG parentPropertyId,
113 /***********************************************************************
114 * Declaration of the functions used to manipulate StgProperty
117 static ULONG getFreeProperty(
118 StorageImpl *storage);
120 static void updatePropertyChain(
121 StorageImpl *storage,
122 ULONG newPropertyIndex,
123 StgProperty newProperty);
125 static LONG propertyNameCmp(
126 OLECHAR *newProperty,
127 OLECHAR *currentProperty);
130 /***********************************************************************
131 * Declaration of miscellaneous functions...
133 static HRESULT validateSTGM(DWORD stgmValue);
135 static DWORD GetShareModeFromSTGM(DWORD stgm);
136 static DWORD GetAccessModeFromSTGM(DWORD stgm);
137 static DWORD GetCreationModeFromSTGM(DWORD stgm);
140 * Virtual function table for the IStorage32Impl class.
142 static ICOM_VTABLE(IStorage) Storage32Impl_Vtbl =
144 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
145 StorageBaseImpl_QueryInterface,
146 StorageBaseImpl_AddRef,
147 StorageBaseImpl_Release,
148 StorageBaseImpl_CreateStream,
149 StorageBaseImpl_OpenStream,
150 StorageImpl_CreateStorage,
151 StorageBaseImpl_OpenStorage,
153 StorageImpl_MoveElementTo,
156 StorageBaseImpl_EnumElements,
157 StorageImpl_DestroyElement,
158 StorageBaseImpl_RenameElement,
159 StorageImpl_SetElementTimes,
160 StorageBaseImpl_SetClass,
161 StorageImpl_SetStateBits,
166 * Virtual function table for the Storage32InternalImpl class.
168 static ICOM_VTABLE(IStorage) Storage32InternalImpl_Vtbl =
170 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
171 StorageBaseImpl_QueryInterface,
172 StorageBaseImpl_AddRef,
173 StorageBaseImpl_Release,
174 StorageBaseImpl_CreateStream,
175 StorageBaseImpl_OpenStream,
176 StorageImpl_CreateStorage,
177 StorageBaseImpl_OpenStorage,
179 StorageImpl_MoveElementTo,
180 StorageInternalImpl_Commit,
181 StorageInternalImpl_Revert,
182 StorageBaseImpl_EnumElements,
183 StorageImpl_DestroyElement,
184 StorageBaseImpl_RenameElement,
185 StorageImpl_SetElementTimes,
186 StorageBaseImpl_SetClass,
187 StorageImpl_SetStateBits,
192 * Virtual function table for the IEnumSTATSTGImpl class.
194 static ICOM_VTABLE(IEnumSTATSTG) IEnumSTATSTGImpl_Vtbl =
196 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
197 IEnumSTATSTGImpl_QueryInterface,
198 IEnumSTATSTGImpl_AddRef,
199 IEnumSTATSTGImpl_Release,
200 IEnumSTATSTGImpl_Next,
201 IEnumSTATSTGImpl_Skip,
202 IEnumSTATSTGImpl_Reset,
203 IEnumSTATSTGImpl_Clone
210 /************************************************************************
211 ** Storage32BaseImpl implementatiion
214 /************************************************************************
215 * Storage32BaseImpl_QueryInterface (IUnknown)
217 * This method implements the common QueryInterface for all IStorage32
218 * implementations contained in this file.
220 * See Windows documentation for more details on IUnknown methods.
222 HRESULT WINAPI StorageBaseImpl_QueryInterface(
227 ICOM_THIS(StorageBaseImpl,iface);
229 * Perform a sanity check on the parameters.
231 if ( (This==0) || (ppvObject==0) )
235 * Initialize the return parameter.
240 * Compare the riid with the interface IDs implemented by this object.
242 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
244 *ppvObject = (IStorage*)This;
246 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
248 *ppvObject = (IStorage*)This;
252 * Check that we obtained an interface.
255 return E_NOINTERFACE;
258 * Query Interface always increases the reference count by one when it is
261 StorageBaseImpl_AddRef(iface);
266 /************************************************************************
267 * Storage32BaseImpl_AddRef (IUnknown)
269 * This method implements the common AddRef for all IStorage32
270 * implementations contained in this file.
272 * See Windows documentation for more details on IUnknown methods.
274 ULONG WINAPI StorageBaseImpl_AddRef(
277 ICOM_THIS(StorageBaseImpl,iface);
283 /************************************************************************
284 * Storage32BaseImpl_Release (IUnknown)
286 * This method implements the common Release for all IStorage32
287 * implementations contained in this file.
289 * See Windows documentation for more details on IUnknown methods.
291 ULONG WINAPI StorageBaseImpl_Release(
294 ICOM_THIS(StorageBaseImpl,iface);
296 * Decrease the reference count on this object.
301 * If the reference count goes down to 0, perform suicide.
306 * Since we are using a system of base-classes, we want to call the
307 * destructor of the appropriate derived class. To do this, we are
308 * using virtual functions to implement the destructor.
310 This->v_destructor(This);
318 /************************************************************************
319 * Storage32BaseImpl_OpenStream (IStorage)
321 * This method will open the specified stream object from the current storage.
323 * See Windows documentation for more details on IStorage methods.
325 HRESULT WINAPI StorageBaseImpl_OpenStream(
327 const OLECHAR* pwcsName, /* [string][in] */
328 void* reserved1, /* [unique][in] */
329 DWORD grfMode, /* [in] */
330 DWORD reserved2, /* [in] */
331 IStream** ppstm) /* [out] */
333 ICOM_THIS(StorageBaseImpl,iface);
334 IEnumSTATSTGImpl* propertyEnumeration;
335 StgStreamImpl* newStream;
336 StgProperty currentProperty;
337 ULONG foundPropertyIndex;
339 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
340 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
343 * Perform a sanity check on the parameters.
345 if ( (pwcsName==NULL) || (ppstm==0) )
349 * Initialize the out parameter
354 * Validate the STGM flags
356 if ( FAILED( validateSTGM(grfMode) ))
357 return STG_E_INVALIDFLAG;
362 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
363 (grfMode & STGM_DELETEONRELEASE) ||
364 (grfMode & STGM_TRANSACTED) )
365 return STG_E_INVALIDFUNCTION;
368 * Create a property enumeration to search the properties
370 propertyEnumeration = IEnumSTATSTGImpl_Construct(
371 This->ancestorStorage,
372 This->rootPropertySetIndex);
375 * Search the enumeration for the property with the given name
377 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
383 * Delete the property enumeration since we don't need it anymore
385 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
388 * If it was found, construct the stream object and return a pointer to it.
390 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
391 (currentProperty.propertyType==PROPTYPE_STREAM) )
393 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
397 newStream->grfMode = grfMode;
398 *ppstm = (IStream*)newStream;
401 * Since we are returning a pointer to the interface, we have to
402 * nail down the reference.
404 StgStreamImpl_AddRef(*ppstm);
409 return E_OUTOFMEMORY;
412 return STG_E_FILENOTFOUND;
415 /************************************************************************
416 * Storage32BaseImpl_OpenStorage (IStorage)
418 * This method will open a new storage object from the current storage.
420 * See Windows documentation for more details on IStorage methods.
422 HRESULT WINAPI StorageBaseImpl_OpenStorage(
424 const OLECHAR* pwcsName, /* [string][unique][in] */
425 IStorage* pstgPriority, /* [unique][in] */
426 DWORD grfMode, /* [in] */
427 SNB snbExclude, /* [unique][in] */
428 DWORD reserved, /* [in] */
429 IStorage** ppstg) /* [out] */
431 ICOM_THIS(StorageBaseImpl,iface);
432 StorageInternalImpl* newStorage;
433 IEnumSTATSTGImpl* propertyEnumeration;
434 StgProperty currentProperty;
435 ULONG foundPropertyIndex;
437 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
438 iface, debugstr_w(pwcsName), pstgPriority,
439 grfMode, snbExclude, reserved, ppstg);
442 * Perform a sanity check on the parameters.
444 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
448 * Validate the STGM flags
450 if ( FAILED( validateSTGM(grfMode) ))
451 return STG_E_INVALIDFLAG;
456 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
457 (grfMode & STGM_DELETEONRELEASE) ||
458 (grfMode & STGM_PRIORITY) )
459 return STG_E_INVALIDFUNCTION;
462 * Initialize the out parameter
467 * Create a property enumeration to search the properties
469 propertyEnumeration = IEnumSTATSTGImpl_Construct(
470 This->ancestorStorage,
471 This->rootPropertySetIndex);
474 * Search the enumeration for the property with the given name
476 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
482 * Delete the property enumeration since we don't need it anymore
484 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
487 * If it was found, construct the stream object and return a pointer to it.
489 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
490 (currentProperty.propertyType==PROPTYPE_STORAGE) )
493 * Construct a new Storage object
495 newStorage = StorageInternalImpl_Construct(
496 This->ancestorStorage,
501 *ppstg = (IStorage*)newStorage;
504 * Since we are returning a pointer to the interface,
505 * we have to nail down the reference.
507 StorageBaseImpl_AddRef(*ppstg);
512 return STG_E_INSUFFICIENTMEMORY;
515 return STG_E_FILENOTFOUND;
518 /************************************************************************
519 * Storage32BaseImpl_EnumElements (IStorage)
521 * This method will create an enumerator object that can be used to
522 * retrieve informatino about all the properties in the storage object.
524 * See Windows documentation for more details on IStorage methods.
526 HRESULT WINAPI StorageBaseImpl_EnumElements(
528 DWORD reserved1, /* [in] */
529 void* reserved2, /* [size_is][unique][in] */
530 DWORD reserved3, /* [in] */
531 IEnumSTATSTG** ppenum) /* [out] */
533 ICOM_THIS(StorageBaseImpl,iface);
534 IEnumSTATSTGImpl* newEnum;
536 TRACE("(%p, %ld, %p, %ld, %p)\n",
537 iface, reserved1, reserved2, reserved3, ppenum);
540 * Perform a sanity check on the parameters.
542 if ( (This==0) || (ppenum==0))
546 * Construct the enumerator.
548 newEnum = IEnumSTATSTGImpl_Construct(
549 This->ancestorStorage,
550 This->rootPropertySetIndex);
554 *ppenum = (IEnumSTATSTG*)newEnum;
557 * Don't forget to nail down a reference to the new object before
560 IEnumSTATSTGImpl_AddRef(*ppenum);
565 return E_OUTOFMEMORY;
568 /************************************************************************
569 * Storage32BaseImpl_Stat (IStorage)
571 * This method will retrieve information about this storage object.
573 * See Windows documentation for more details on IStorage methods.
575 HRESULT WINAPI StorageBaseImpl_Stat(
577 STATSTG* pstatstg, /* [out] */
578 DWORD grfStatFlag) /* [in] */
580 ICOM_THIS(StorageBaseImpl,iface);
581 StgProperty curProperty;
584 TRACE("(%p, %p, %lx)\n",
585 iface, pstatstg, grfStatFlag);
588 * Perform a sanity check on the parameters.
590 if ( (This==0) || (pstatstg==0))
594 * Read the information from the property.
596 readSuccessful = StorageImpl_ReadProperty(
597 This->ancestorStorage,
598 This->rootPropertySetIndex,
603 StorageUtl_CopyPropertyToSTATSTG(
614 /************************************************************************
615 * Storage32BaseImpl_RenameElement (IStorage)
617 * This method will rename the specified element.
619 * See Windows documentation for more details on IStorage methods.
621 * Implementation notes: The method used to rename consists of creating a clone
622 * of the deleted StgProperty object setting it with the new name and to
623 * perform a DestroyElement of the old StgProperty.
625 HRESULT WINAPI StorageBaseImpl_RenameElement(
627 const OLECHAR* pwcsOldName, /* [in] */
628 const OLECHAR* pwcsNewName) /* [in] */
630 ICOM_THIS(StorageBaseImpl,iface);
631 IEnumSTATSTGImpl* propertyEnumeration;
632 StgProperty currentProperty;
633 ULONG foundPropertyIndex;
635 TRACE("(%p, %s, %s)\n",
636 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
639 * Create a property enumeration to search the properties
641 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
642 This->rootPropertySetIndex);
645 * Search the enumeration for the new property name
647 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
651 if (foundPropertyIndex != PROPERTY_NULL)
654 * There is already a property with the new name
656 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
657 return STG_E_FILEALREADYEXISTS;
660 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
663 * Search the enumeration for the old property name
665 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
670 * Delete the property enumeration since we don't need it anymore
672 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
674 if (foundPropertyIndex != PROPERTY_NULL)
676 StgProperty renamedProperty;
677 ULONG renamedPropertyIndex;
680 * Setup a new property for the renamed property
682 renamedProperty.sizeOfNameString =
683 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
685 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
686 return STG_E_INVALIDNAME;
688 strcpyW(renamedProperty.name, pwcsNewName);
690 renamedProperty.propertyType = currentProperty.propertyType;
691 renamedProperty.startingBlock = currentProperty.startingBlock;
692 renamedProperty.size.s.LowPart = currentProperty.size.s.LowPart;
693 renamedProperty.size.s.HighPart = currentProperty.size.s.HighPart;
695 renamedProperty.previousProperty = PROPERTY_NULL;
696 renamedProperty.nextProperty = PROPERTY_NULL;
699 * Bring the dirProperty link in case it is a storage and in which
700 * case the renamed storage elements don't require to be reorganized.
702 renamedProperty.dirProperty = currentProperty.dirProperty;
704 /* call CoFileTime to get the current time
705 renamedProperty.timeStampS1
706 renamedProperty.timeStampD1
707 renamedProperty.timeStampS2
708 renamedProperty.timeStampD2
709 renamedProperty.propertyUniqueID
713 * Obtain a free property in the property chain
715 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
718 * Save the new property into the new property spot
720 StorageImpl_WriteProperty(
721 This->ancestorStorage,
722 renamedPropertyIndex,
726 * Find a spot in the property chain for our newly created property.
730 renamedPropertyIndex,
734 * At this point the renamed property has been inserted in the tree,
735 * now, before to Destroy the old property we must zeroed it's dirProperty
736 * otherwise the DestroyProperty below will zap it all and we do not want
738 * Also, we fake that the old property is a storage so the DestroyProperty
739 * will not do a SetSize(0) on the stream data.
741 * This means that we need to tweek the StgProperty if it is a stream or a
744 StorageImpl_ReadProperty(This->ancestorStorage,
748 currentProperty.dirProperty = PROPERTY_NULL;
749 currentProperty.propertyType = PROPTYPE_STORAGE;
750 StorageImpl_WriteProperty(
751 This->ancestorStorage,
756 * Invoke Destroy to get rid of the ole property and automatically redo
757 * the linking of it's previous and next members...
759 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
765 * There is no property with the old name
767 return STG_E_FILENOTFOUND;
773 /************************************************************************
774 * Storage32BaseImpl_CreateStream (IStorage)
776 * This method will create a stream object within this storage
778 * See Windows documentation for more details on IStorage methods.
780 HRESULT WINAPI StorageBaseImpl_CreateStream(
782 const OLECHAR* pwcsName, /* [string][in] */
783 DWORD grfMode, /* [in] */
784 DWORD reserved1, /* [in] */
785 DWORD reserved2, /* [in] */
786 IStream** ppstm) /* [out] */
788 ICOM_THIS(StorageBaseImpl,iface);
789 IEnumSTATSTGImpl* propertyEnumeration;
790 StgStreamImpl* newStream;
791 StgProperty currentProperty, newStreamProperty;
792 ULONG foundPropertyIndex, newPropertyIndex;
794 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
795 iface, debugstr_w(pwcsName), grfMode,
796 reserved1, reserved2, ppstm);
799 * Validate parameters
802 return STG_E_INVALIDPOINTER;
805 return STG_E_INVALIDNAME;
808 * Validate the STGM flags
810 if ( FAILED( validateSTGM(grfMode) ))
811 return STG_E_INVALIDFLAG;
816 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
817 (grfMode & STGM_DELETEONRELEASE) ||
818 (grfMode & STGM_TRANSACTED) )
819 return STG_E_INVALIDFUNCTION;
822 * Initialize the out parameter
827 * Create a property enumeration to search the properties
829 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
830 This->rootPropertySetIndex);
832 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
836 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
838 if (foundPropertyIndex != PROPERTY_NULL)
841 * An element with this name already exists
843 if (grfMode & STGM_CREATE)
845 IStorage_DestroyElement(iface, pwcsName);
848 return STG_E_FILEALREADYEXISTS;
852 * memset the empty property
854 memset(&newStreamProperty, 0, sizeof(StgProperty));
856 newStreamProperty.sizeOfNameString =
857 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
859 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
860 return STG_E_INVALIDNAME;
862 strcpyW(newStreamProperty.name, pwcsName);
864 newStreamProperty.propertyType = PROPTYPE_STREAM;
865 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
866 newStreamProperty.size.s.LowPart = 0;
867 newStreamProperty.size.s.HighPart = 0;
869 newStreamProperty.previousProperty = PROPERTY_NULL;
870 newStreamProperty.nextProperty = PROPERTY_NULL;
871 newStreamProperty.dirProperty = PROPERTY_NULL;
873 /* call CoFileTime to get the current time
874 newStreamProperty.timeStampS1
875 newStreamProperty.timeStampD1
876 newStreamProperty.timeStampS2
877 newStreamProperty.timeStampD2
880 /* newStreamProperty.propertyUniqueID */
883 * Get a free property or create a new one
885 newPropertyIndex = getFreeProperty(This->ancestorStorage);
888 * Save the new property into the new property spot
890 StorageImpl_WriteProperty(
891 This->ancestorStorage,
896 * Find a spot in the property chain for our newly created property.
904 * Open the stream to return it.
906 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
910 *ppstm = (IStream*)newStream;
913 * Since we are returning a pointer to the interface, we have to nail down
916 StgStreamImpl_AddRef(*ppstm);
920 return STG_E_INSUFFICIENTMEMORY;
926 /************************************************************************
927 * Storage32BaseImpl_SetClass (IStorage)
929 * This method will write the specified CLSID in the property of this
932 * See Windows documentation for more details on IStorage methods.
934 HRESULT WINAPI StorageBaseImpl_SetClass(
936 REFCLSID clsid) /* [in] */
938 ICOM_THIS(StorageBaseImpl,iface);
939 HRESULT hRes = E_FAIL;
940 StgProperty curProperty;
943 TRACE("(%p, %p)\n", iface, clsid);
945 success = StorageImpl_ReadProperty(This->ancestorStorage,
946 This->rootPropertySetIndex,
950 curProperty.propertyUniqueID = *clsid;
952 success = StorageImpl_WriteProperty(This->ancestorStorage,
953 This->rootPropertySetIndex,
962 /************************************************************************
963 ** Storage32Impl implementation
966 /************************************************************************
967 * Storage32Impl_CreateStorage (IStorage)
969 * This method will create the storage object within the provided storage.
971 * See Windows documentation for more details on IStorage methods.
973 HRESULT WINAPI StorageImpl_CreateStorage(
975 const OLECHAR *pwcsName, /* [string][in] */
976 DWORD grfMode, /* [in] */
977 DWORD reserved1, /* [in] */
978 DWORD reserved2, /* [in] */
979 IStorage **ppstg) /* [out] */
981 StorageImpl* const This=(StorageImpl*)iface;
983 IEnumSTATSTGImpl *propertyEnumeration;
984 StgProperty currentProperty;
985 StgProperty newProperty;
986 ULONG foundPropertyIndex;
987 ULONG newPropertyIndex;
990 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
991 iface, debugstr_w(pwcsName), grfMode,
992 reserved1, reserved2, ppstg);
995 * Validate parameters
998 return STG_E_INVALIDPOINTER;
1001 return STG_E_INVALIDNAME;
1004 * Validate the STGM flags
1006 if ( FAILED( validateSTGM(grfMode) ) ||
1007 (grfMode & STGM_DELETEONRELEASE) )
1008 return STG_E_INVALIDFLAG;
1011 * Initialize the out parameter
1016 * Create a property enumeration and search the properties
1018 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
1019 This->rootPropertySetIndex);
1021 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1024 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1026 if (foundPropertyIndex != PROPERTY_NULL)
1029 * An element with this name already exists
1031 if (grfMode & STGM_CREATE)
1032 IStorage_DestroyElement(iface, pwcsName);
1034 return STG_E_FILEALREADYEXISTS;
1038 * memset the empty property
1040 memset(&newProperty, 0, sizeof(StgProperty));
1042 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1044 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1045 return STG_E_INVALIDNAME;
1047 strcpyW(newProperty.name, pwcsName);
1049 newProperty.propertyType = PROPTYPE_STORAGE;
1050 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1051 newProperty.size.s.LowPart = 0;
1052 newProperty.size.s.HighPart = 0;
1054 newProperty.previousProperty = PROPERTY_NULL;
1055 newProperty.nextProperty = PROPERTY_NULL;
1056 newProperty.dirProperty = PROPERTY_NULL;
1058 /* call CoFileTime to get the current time
1059 newProperty.timeStampS1
1060 newProperty.timeStampD1
1061 newProperty.timeStampS2
1062 newProperty.timeStampD2
1065 /* newStorageProperty.propertyUniqueID */
1068 * Obtain a free property in the property chain
1070 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1073 * Save the new property into the new property spot
1075 StorageImpl_WriteProperty(
1076 This->ancestorStorage,
1081 * Find a spot in the property chain for our newly created property.
1083 updatePropertyChain(
1089 * Open it to get a pointer to return.
1091 hr = IStorage_OpenStorage(
1100 if( (hr != S_OK) || (*ppstg == NULL))
1110 /***************************************************************************
1114 * Get a free property or create a new one.
1116 static ULONG getFreeProperty(
1117 StorageImpl *storage)
1119 ULONG currentPropertyIndex = 0;
1120 ULONG newPropertyIndex = PROPERTY_NULL;
1121 BOOL readSuccessful = TRUE;
1122 StgProperty currentProperty;
1127 * Start by reading the root property
1129 readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1130 currentPropertyIndex,
1134 if (currentProperty.sizeOfNameString == 0)
1137 * The property existis and is available, we found it.
1139 newPropertyIndex = currentPropertyIndex;
1145 * We exhausted the property list, we will create more space below
1147 newPropertyIndex = currentPropertyIndex;
1149 currentPropertyIndex++;
1151 } while (newPropertyIndex == PROPERTY_NULL);
1154 * grow the property chain
1156 if (! readSuccessful)
1158 StgProperty emptyProperty;
1159 ULARGE_INTEGER newSize;
1160 ULONG propertyIndex;
1161 ULONG lastProperty = 0;
1162 ULONG blockCount = 0;
1165 * obtain the new count of property blocks
1167 blockCount = BlockChainStream_GetCount(
1168 storage->ancestorStorage->rootBlockChain)+1;
1171 * initialize the size used by the property stream
1173 newSize.s.HighPart = 0;
1174 newSize.s.LowPart = storage->bigBlockSize * blockCount;
1177 * add a property block to the property chain
1179 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1182 * memset the empty property in order to initialize the unused newly
1185 memset(&emptyProperty, 0, sizeof(StgProperty));
1190 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1193 propertyIndex = newPropertyIndex;
1194 propertyIndex < lastProperty;
1197 StorageImpl_WriteProperty(
1198 storage->ancestorStorage,
1204 return newPropertyIndex;
1207 /****************************************************************************
1211 * Case insensitive comparaison of StgProperty.name by first considering
1214 * Returns <0 when newPrpoerty < currentProperty
1215 * >0 when newPrpoerty > currentProperty
1216 * 0 when newPrpoerty == currentProperty
1218 static LONG propertyNameCmp(
1219 OLECHAR *newProperty,
1220 OLECHAR *currentProperty)
1222 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1227 * We compare the string themselves only when they are of the same lenght
1229 diff = lstrcmpiW( newProperty, currentProperty);
1235 /****************************************************************************
1239 * Properly link this new element in the property chain.
1241 static void updatePropertyChain(
1242 StorageImpl *storage,
1243 ULONG newPropertyIndex,
1244 StgProperty newProperty)
1246 StgProperty currentProperty;
1249 * Read the root property
1251 StorageImpl_ReadProperty(storage->ancestorStorage,
1252 storage->rootPropertySetIndex,
1255 if (currentProperty.dirProperty != PROPERTY_NULL)
1258 * The root storage contains some element, therefore, start the research
1259 * for the appropriate location.
1262 ULONG current, next, previous, currentPropertyId;
1265 * Keep the StgProperty sequence number of the storage first property
1267 currentPropertyId = currentProperty.dirProperty;
1272 StorageImpl_ReadProperty(storage->ancestorStorage,
1273 currentProperty.dirProperty,
1276 previous = currentProperty.previousProperty;
1277 next = currentProperty.nextProperty;
1278 current = currentPropertyId;
1282 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1286 if (previous != PROPERTY_NULL)
1288 StorageImpl_ReadProperty(storage->ancestorStorage,
1295 currentProperty.previousProperty = newPropertyIndex;
1296 StorageImpl_WriteProperty(storage->ancestorStorage,
1304 if (next != PROPERTY_NULL)
1306 StorageImpl_ReadProperty(storage->ancestorStorage,
1313 currentProperty.nextProperty = newPropertyIndex;
1314 StorageImpl_WriteProperty(storage->ancestorStorage,
1323 * Trying to insert an item with the same name in the
1324 * subtree structure.
1329 previous = currentProperty.previousProperty;
1330 next = currentProperty.nextProperty;
1336 * The root storage is empty, link the new property to it's dir property
1338 currentProperty.dirProperty = newPropertyIndex;
1339 StorageImpl_WriteProperty(storage->ancestorStorage,
1340 storage->rootPropertySetIndex,
1346 /*************************************************************************
1349 HRESULT WINAPI StorageImpl_CopyTo(
1351 DWORD ciidExclude, /* [in] */
1352 const IID* rgiidExclude, /* [size_is][unique][in] */
1353 SNB snbExclude, /* [unique][in] */
1354 IStorage* pstgDest) /* [unique][in] */
1356 IEnumSTATSTG *elements = 0;
1357 STATSTG curElement, strStat;
1359 IStorage *pstgTmp, *pstgChild;
1360 IStream *pstrTmp, *pstrChild;
1362 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1363 FIXME("Exclude option not implemented\n");
1365 TRACE("(%p, %ld, %p, %p, %p)\n",
1366 iface, ciidExclude, rgiidExclude,
1367 snbExclude, pstgDest);
1370 * Perform a sanity check
1372 if ( pstgDest == 0 )
1373 return STG_E_INVALIDPOINTER;
1376 * Enumerate the elements
1378 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1386 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1387 IStorage_SetClass( pstgDest, &curElement.clsid );
1392 * Obtain the next element
1394 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1396 if ( hr == S_FALSE )
1398 hr = S_OK; /* done, every element has been copied */
1402 if (curElement.type == STGTY_STORAGE)
1405 * open child source storage
1407 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1408 STGM_READ|STGM_SHARE_EXCLUSIVE,
1409 NULL, 0, &pstgChild );
1415 * Check if destination storage is not a child of the source
1416 * storage, which will cause an infinite loop
1418 if (pstgChild == pstgDest)
1420 IEnumSTATSTG_Release(elements);
1422 return STG_E_ACCESSDENIED;
1426 * create a new storage in destination storage
1428 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1429 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1433 * if it already exist, don't create a new one use this one
1435 if (hr == STG_E_FILEALREADYEXISTS)
1437 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1438 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1439 NULL, 0, &pstgTmp );
1447 * do the copy recursively
1449 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1450 snbExclude, pstgTmp );
1452 IStorage_Release( pstgTmp );
1453 IStorage_Release( pstgChild );
1455 else if (curElement.type == STGTY_STREAM)
1458 * create a new stream in destination storage. If the stream already
1459 * exist, it will be deleted and a new one will be created.
1461 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1462 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1469 * open child stream storage
1471 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1472 STGM_READ|STGM_SHARE_EXCLUSIVE,
1479 * Get the size of the source stream
1481 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1484 * Set the size of the destination stream.
1486 IStream_SetSize(pstrTmp, strStat.cbSize);
1491 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1494 IStream_Release( pstrTmp );
1495 IStream_Release( pstrChild );
1499 WARN("unknown element type: %ld\n", curElement.type);
1502 } while (hr == S_OK);
1507 IEnumSTATSTG_Release(elements);
1512 /*************************************************************************
1513 * MoveElementTo (IStorage)
1515 HRESULT WINAPI StorageImpl_MoveElementTo(
1517 const OLECHAR *pwcsName, /* [string][in] */
1518 IStorage *pstgDest, /* [unique][in] */
1519 const OLECHAR *pwcsNewName,/* [string][in] */
1520 DWORD grfFlags) /* [in] */
1522 FIXME("not implemented!\n");
1526 /*************************************************************************
1529 HRESULT WINAPI StorageImpl_Commit(
1531 DWORD grfCommitFlags)/* [in] */
1533 FIXME("(%ld): stub!\n", grfCommitFlags);
1537 /*************************************************************************
1540 HRESULT WINAPI StorageImpl_Revert(
1543 FIXME("not implemented!\n");
1547 /*************************************************************************
1548 * DestroyElement (IStorage)
1550 * Stategy: This implementation is build this way for simplicity not for speed.
1551 * I always delete the top most element of the enumeration and adjust
1552 * the deleted element pointer all the time. This takes longer to
1553 * do but allow to reinvoke DestroyElement whenever we encounter a
1554 * storage object. The optimisation reside in the usage of another
1555 * enumeration stategy that would give all the leaves of a storage
1556 * first. (postfix order)
1558 HRESULT WINAPI StorageImpl_DestroyElement(
1560 const OLECHAR *pwcsName)/* [string][in] */
1562 StorageImpl* const This=(StorageImpl*)iface;
1564 IEnumSTATSTGImpl* propertyEnumeration;
1567 StgProperty propertyToDelete;
1568 StgProperty parentProperty;
1569 ULONG foundPropertyIndexToDelete;
1570 ULONG typeOfRelation;
1571 ULONG parentPropertyId;
1574 iface, debugstr_w(pwcsName));
1577 * Perform a sanity check on the parameters.
1580 return STG_E_INVALIDPOINTER;
1583 * Create a property enumeration to search the property with the given name
1585 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1586 This->ancestorStorage,
1587 This->rootPropertySetIndex);
1589 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1590 propertyEnumeration,
1594 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1596 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1598 return STG_E_FILENOTFOUND;
1602 * Find the parent property of the property to delete (the one that
1603 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1604 * the parent is This. Otherwise, the parent is one of it's sibling...
1608 * First, read This's StgProperty..
1610 res = StorageImpl_ReadProperty(
1611 This->ancestorStorage,
1612 This->rootPropertySetIndex,
1618 * Second, check to see if by any chance the actual storage (This) is not
1619 * the parent of the property to delete... We never know...
1621 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1624 * Set data as it would have been done in the else part...
1626 typeOfRelation = PROPERTY_RELATION_DIR;
1627 parentPropertyId = This->rootPropertySetIndex;
1632 * Create a property enumeration to search the parent properties, and
1633 * delete it once done.
1635 IEnumSTATSTGImpl* propertyEnumeration2;
1637 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1638 This->ancestorStorage,
1639 This->rootPropertySetIndex);
1641 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1642 propertyEnumeration2,
1643 foundPropertyIndexToDelete,
1647 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1650 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1652 hr = deleteStorageProperty(
1654 foundPropertyIndexToDelete,
1657 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1659 hr = deleteStreamProperty(
1661 foundPropertyIndexToDelete,
1669 * Adjust the property chain
1671 hr = adjustPropertyChain(
1682 /*********************************************************************
1686 * Perform the deletion of a complete storage node
1689 static HRESULT deleteStorageProperty(
1690 StorageImpl *parentStorage,
1691 ULONG indexOfPropertyToDelete,
1692 StgProperty propertyToDelete)
1694 IEnumSTATSTG *elements = 0;
1695 IStorage *childStorage = 0;
1696 STATSTG currentElement;
1698 HRESULT destroyHr = S_OK;
1701 * Open the storage and enumerate it
1703 hr = StorageBaseImpl_OpenStorage(
1704 (IStorage*)parentStorage,
1705 propertyToDelete.name,
1707 STGM_SHARE_EXCLUSIVE,
1718 * Enumerate the elements
1720 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1725 * Obtain the next element
1727 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1730 destroyHr = StorageImpl_DestroyElement(
1731 (IStorage*)childStorage,
1732 (OLECHAR*)currentElement.pwcsName);
1734 CoTaskMemFree(currentElement.pwcsName);
1738 * We need to Reset the enumeration every time because we delete elements
1739 * and the enumeration could be invalid
1741 IEnumSTATSTG_Reset(elements);
1743 } while ((hr == S_OK) && (destroyHr == S_OK));
1746 * Invalidate the property by zeroing it's name member.
1748 propertyToDelete.sizeOfNameString = 0;
1750 StorageImpl_WriteProperty(parentStorage->ancestorStorage,
1751 indexOfPropertyToDelete,
1754 IStorage_Release(childStorage);
1755 IEnumSTATSTG_Release(elements);
1760 /*********************************************************************
1764 * Perform the deletion of a stream node
1767 static HRESULT deleteStreamProperty(
1768 StorageImpl *parentStorage,
1769 ULONG indexOfPropertyToDelete,
1770 StgProperty propertyToDelete)
1774 ULARGE_INTEGER size;
1776 size.s.HighPart = 0;
1779 hr = StorageBaseImpl_OpenStream(
1780 (IStorage*)parentStorage,
1781 (OLECHAR*)propertyToDelete.name,
1783 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1795 hr = IStream_SetSize(pis, size);
1803 * Release the stream object.
1805 IStream_Release(pis);
1808 * Invalidate the property by zeroing it's name member.
1810 propertyToDelete.sizeOfNameString = 0;
1813 * Here we should re-read the property so we get the updated pointer
1814 * but since we are here to zap it, I don't do it...
1816 StorageImpl_WriteProperty(
1817 parentStorage->ancestorStorage,
1818 indexOfPropertyToDelete,
1824 /*********************************************************************
1828 * Finds a placeholder for the StgProperty within the Storage
1831 static HRESULT findPlaceholder(
1832 StorageImpl *storage,
1833 ULONG propertyIndexToStore,
1834 ULONG storePropertyIndex,
1837 StgProperty storeProperty;
1842 * Read the storage property
1844 res = StorageImpl_ReadProperty(
1845 storage->ancestorStorage,
1854 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1856 if (storeProperty.previousProperty != PROPERTY_NULL)
1858 return findPlaceholder(
1860 propertyIndexToStore,
1861 storeProperty.previousProperty,
1866 storeProperty.previousProperty = propertyIndexToStore;
1869 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1871 if (storeProperty.nextProperty != PROPERTY_NULL)
1873 return findPlaceholder(
1875 propertyIndexToStore,
1876 storeProperty.nextProperty,
1881 storeProperty.nextProperty = propertyIndexToStore;
1884 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1886 if (storeProperty.dirProperty != PROPERTY_NULL)
1888 return findPlaceholder(
1890 propertyIndexToStore,
1891 storeProperty.dirProperty,
1896 storeProperty.dirProperty = propertyIndexToStore;
1900 hr = StorageImpl_WriteProperty(
1901 storage->ancestorStorage,
1913 /*************************************************************************
1917 * This method takes the previous and the next property link of a property
1918 * to be deleted and find them a place in the Storage.
1920 static HRESULT adjustPropertyChain(
1922 StgProperty propertyToDelete,
1923 StgProperty parentProperty,
1924 ULONG parentPropertyId,
1927 ULONG newLinkProperty = PROPERTY_NULL;
1928 BOOL needToFindAPlaceholder = FALSE;
1929 ULONG storeNode = PROPERTY_NULL;
1930 ULONG toStoreNode = PROPERTY_NULL;
1931 INT relationType = 0;
1935 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1937 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1940 * Set the parent previous to the property to delete previous
1942 newLinkProperty = propertyToDelete.previousProperty;
1944 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1947 * We also need to find a storage for the other link, setup variables
1948 * to do this at the end...
1950 needToFindAPlaceholder = TRUE;
1951 storeNode = propertyToDelete.previousProperty;
1952 toStoreNode = propertyToDelete.nextProperty;
1953 relationType = PROPERTY_RELATION_NEXT;
1956 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1959 * Set the parent previous to the property to delete next
1961 newLinkProperty = propertyToDelete.nextProperty;
1965 * Link it for real...
1967 parentProperty.previousProperty = newLinkProperty;
1970 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1972 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1975 * Set the parent next to the property to delete next previous
1977 newLinkProperty = propertyToDelete.previousProperty;
1979 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1982 * We also need to find a storage for the other link, setup variables
1983 * to do this at the end...
1985 needToFindAPlaceholder = TRUE;
1986 storeNode = propertyToDelete.previousProperty;
1987 toStoreNode = propertyToDelete.nextProperty;
1988 relationType = PROPERTY_RELATION_NEXT;
1991 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1994 * Set the parent next to the property to delete next
1996 newLinkProperty = propertyToDelete.nextProperty;
2000 * Link it for real...
2002 parentProperty.nextProperty = newLinkProperty;
2004 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2006 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2009 * Set the parent dir to the property to delete previous
2011 newLinkProperty = propertyToDelete.previousProperty;
2013 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2016 * We also need to find a storage for the other link, setup variables
2017 * to do this at the end...
2019 needToFindAPlaceholder = TRUE;
2020 storeNode = propertyToDelete.previousProperty;
2021 toStoreNode = propertyToDelete.nextProperty;
2022 relationType = PROPERTY_RELATION_NEXT;
2025 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2028 * Set the parent dir to the property to delete next
2030 newLinkProperty = propertyToDelete.nextProperty;
2034 * Link it for real...
2036 parentProperty.dirProperty = newLinkProperty;
2040 * Write back the parent property
2042 res = StorageImpl_WriteProperty(
2043 This->ancestorStorage,
2052 * If a placeholder is required for the other link, then, find one and
2053 * get out of here...
2055 if (needToFindAPlaceholder)
2057 hr = findPlaceholder(
2068 /******************************************************************************
2069 * SetElementTimes (IStorage)
2071 HRESULT WINAPI StorageImpl_SetElementTimes(
2073 const OLECHAR *pwcsName,/* [string][in] */
2074 const FILETIME *pctime, /* [in] */
2075 const FILETIME *patime, /* [in] */
2076 const FILETIME *pmtime) /* [in] */
2078 FIXME("not implemented!\n");
2082 /******************************************************************************
2083 * SetStateBits (IStorage)
2085 HRESULT WINAPI StorageImpl_SetStateBits(
2087 DWORD grfStateBits,/* [in] */
2088 DWORD grfMask) /* [in] */
2090 FIXME("not implemented!\n");
2094 HRESULT StorageImpl_Construct(
2103 StgProperty currentProperty;
2104 BOOL readSuccessful;
2105 ULONG currentPropertyIndex;
2107 if ( FAILED( validateSTGM(openFlags) ))
2108 return STG_E_INVALIDFLAG;
2110 memset(This, 0, sizeof(StorageImpl));
2113 * Initialize the virtual fgunction table.
2115 ICOM_VTBL(This) = &Storage32Impl_Vtbl;
2116 This->v_destructor = &StorageImpl_Destroy;
2119 * This is the top-level storage so initialize the ancester pointer
2122 This->ancestorStorage = This;
2125 * Initialize the physical support of the storage.
2127 This->hFile = hFile;
2130 * Initialize the big block cache.
2132 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2133 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2134 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2140 if (This->bigBlockFile == 0)
2145 ULARGE_INTEGER size;
2146 BYTE* bigBlockBuffer;
2149 * Initialize all header variables:
2150 * - The big block depot consists of one block and it is at block 0
2151 * - The properties start at block 1
2152 * - There is no small block depot
2154 memset( This->bigBlockDepotStart,
2156 sizeof(This->bigBlockDepotStart));
2158 This->bigBlockDepotCount = 1;
2159 This->bigBlockDepotStart[0] = 0;
2160 This->rootStartBlock = 1;
2161 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2162 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2163 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2164 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2165 This->extBigBlockDepotCount = 0;
2167 StorageImpl_SaveFileHeader(This);
2170 * Add one block for the big block depot and one block for the properties
2172 size.s.HighPart = 0;
2173 size.s.LowPart = This->bigBlockSize * 3;
2174 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2177 * Initialize the big block depot
2179 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2180 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2181 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2182 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2183 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2188 * Load the header for the file.
2190 hr = StorageImpl_LoadFileHeader(This);
2194 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2201 * There is no block depot cached yet.
2203 This->indexBlockDepotCached = 0xFFFFFFFF;
2206 * Start searching for free blocks with block 0.
2208 This->prevFreeBlock = 0;
2211 * Create the block chain abstractions.
2213 This->rootBlockChain =
2214 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL);
2216 This->smallBlockDepotChain = BlockChainStream_Construct(
2218 &This->smallBlockDepotStart,
2222 * Write the root property
2226 StgProperty rootProp;
2228 * Initialize the property chain
2230 memset(&rootProp, 0, sizeof(rootProp));
2231 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2232 sizeof(rootProp.name)/sizeof(WCHAR) );
2233 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2234 rootProp.propertyType = PROPTYPE_ROOT;
2235 rootProp.previousProperty = PROPERTY_NULL;
2236 rootProp.nextProperty = PROPERTY_NULL;
2237 rootProp.dirProperty = PROPERTY_NULL;
2238 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2239 rootProp.size.s.HighPart = 0;
2240 rootProp.size.s.LowPart = 0;
2242 StorageImpl_WriteProperty(This, 0, &rootProp);
2246 * Find the ID of the root int he property sets.
2248 currentPropertyIndex = 0;
2252 readSuccessful = StorageImpl_ReadProperty(
2254 currentPropertyIndex,
2259 if ( (currentProperty.sizeOfNameString != 0 ) &&
2260 (currentProperty.propertyType == PROPTYPE_ROOT) )
2262 This->rootPropertySetIndex = currentPropertyIndex;
2266 currentPropertyIndex++;
2268 } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2270 if (!readSuccessful)
2277 * Create the block chain abstraction for the small block root chain.
2279 This->smallBlockRootChain = BlockChainStream_Construct(
2282 This->rootPropertySetIndex);
2287 void StorageImpl_Destroy(
2290 TRACE("(%p)\n", This);
2292 BlockChainStream_Destroy(This->smallBlockRootChain);
2293 BlockChainStream_Destroy(This->rootBlockChain);
2294 BlockChainStream_Destroy(This->smallBlockDepotChain);
2296 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2300 /******************************************************************************
2301 * Storage32Impl_GetNextFreeBigBlock
2303 * Returns the index of the next free big block.
2304 * If the big block depot is filled, this method will enlarge it.
2307 ULONG StorageImpl_GetNextFreeBigBlock(
2310 ULONG depotBlockIndexPos;
2312 ULONG depotBlockOffset;
2313 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2314 ULONG nextBlockIndex = BLOCK_SPECIAL;
2316 ULONG freeBlock = BLOCK_UNUSED;
2318 depotIndex = This->prevFreeBlock / blocksPerDepot;
2319 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2322 * Scan the entire big block depot until we find a block marked free
2324 while (nextBlockIndex != BLOCK_UNUSED)
2326 if (depotIndex < COUNT_BBDEPOTINHEADER)
2328 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2331 * Grow the primary depot.
2333 if (depotBlockIndexPos == BLOCK_UNUSED)
2335 depotBlockIndexPos = depotIndex*blocksPerDepot;
2338 * Add a block depot.
2340 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2341 This->bigBlockDepotCount++;
2342 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2345 * Flag it as a block depot.
2347 StorageImpl_SetNextBlockInChain(This,
2351 /* Save new header information.
2353 StorageImpl_SaveFileHeader(This);
2358 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2360 if (depotBlockIndexPos == BLOCK_UNUSED)
2363 * Grow the extended depot.
2365 ULONG extIndex = BLOCK_UNUSED;
2366 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2367 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2369 if (extBlockOffset == 0)
2371 /* We need an extended block.
2373 extIndex = Storage32Impl_AddExtBlockDepot(This);
2374 This->extBigBlockDepotCount++;
2375 depotBlockIndexPos = extIndex + 1;
2378 depotBlockIndexPos = depotIndex * blocksPerDepot;
2381 * Add a block depot and mark it in the extended block.
2383 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2384 This->bigBlockDepotCount++;
2385 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2387 /* Flag the block depot.
2389 StorageImpl_SetNextBlockInChain(This,
2393 /* If necessary, flag the extended depot block.
2395 if (extIndex != BLOCK_UNUSED)
2396 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2398 /* Save header information.
2400 StorageImpl_SaveFileHeader(This);
2404 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2406 if (depotBuffer != 0)
2408 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2409 ( nextBlockIndex != BLOCK_UNUSED))
2411 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2413 if (nextBlockIndex == BLOCK_UNUSED)
2415 freeBlock = (depotIndex * blocksPerDepot) +
2416 (depotBlockOffset/sizeof(ULONG));
2419 depotBlockOffset += sizeof(ULONG);
2422 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2426 depotBlockOffset = 0;
2429 This->prevFreeBlock = freeBlock;
2434 /******************************************************************************
2435 * Storage32Impl_AddBlockDepot
2437 * This will create a depot block, essentially it is a block initialized
2440 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2444 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2447 * Initialize blocks as free
2449 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2451 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2454 /******************************************************************************
2455 * Storage32Impl_GetExtDepotBlock
2457 * Returns the index of the block that corresponds to the specified depot
2458 * index. This method is only for depot indexes equal or greater than
2459 * COUNT_BBDEPOTINHEADER.
2461 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2463 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2464 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2465 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2466 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2467 ULONG blockIndex = BLOCK_UNUSED;
2468 ULONG extBlockIndex = This->extBigBlockDepotStart;
2470 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2472 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2473 return BLOCK_UNUSED;
2475 while (extBlockCount > 0)
2477 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2481 if (extBlockIndex != BLOCK_UNUSED)
2485 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2487 if (depotBuffer != 0)
2489 StorageUtl_ReadDWord(depotBuffer,
2490 extBlockOffset * sizeof(ULONG),
2493 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2500 /******************************************************************************
2501 * Storage32Impl_SetExtDepotBlock
2503 * Associates the specified block index to the specified depot index.
2504 * This method is only for depot indexes equal or greater than
2505 * COUNT_BBDEPOTINHEADER.
2507 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2511 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2512 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2513 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2514 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2515 ULONG extBlockIndex = This->extBigBlockDepotStart;
2517 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2519 while (extBlockCount > 0)
2521 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2525 if (extBlockIndex != BLOCK_UNUSED)
2529 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2531 if (depotBuffer != 0)
2533 StorageUtl_WriteDWord(depotBuffer,
2534 extBlockOffset * sizeof(ULONG),
2537 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2542 /******************************************************************************
2543 * Storage32Impl_AddExtBlockDepot
2545 * Creates an extended depot block.
2547 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2549 ULONG numExtBlocks = This->extBigBlockDepotCount;
2550 ULONG nextExtBlock = This->extBigBlockDepotStart;
2551 BYTE* depotBuffer = NULL;
2552 ULONG index = BLOCK_UNUSED;
2553 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2554 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2555 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2557 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2558 blocksPerDepotBlock;
2560 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2563 * The first extended block.
2565 This->extBigBlockDepotStart = index;
2571 * Follow the chain to the last one.
2573 for (i = 0; i < (numExtBlocks - 1); i++)
2575 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2579 * Add the new extended block to the chain.
2581 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2582 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2583 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2587 * Initialize this block.
2589 depotBuffer = StorageImpl_GetBigBlock(This, index);
2590 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2591 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2596 /******************************************************************************
2597 * Storage32Impl_FreeBigBlock
2599 * This method will flag the specified block as free in the big block depot.
2601 void StorageImpl_FreeBigBlock(
2605 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2607 if (blockIndex < This->prevFreeBlock)
2608 This->prevFreeBlock = blockIndex;
2611 /************************************************************************
2612 * Storage32Impl_GetNextBlockInChain
2614 * This method will retrieve the block index of the next big block in
2617 * Params: This - Pointer to the Storage object.
2618 * blockIndex - Index of the block to retrieve the chain
2621 * Returns: This method returns the index of the next block in the chain.
2622 * It will return the constants:
2623 * BLOCK_SPECIAL - If the block given was not part of a
2625 * BLOCK_END_OF_CHAIN - If the block given was the last in
2627 * BLOCK_UNUSED - If the block given was not past of a chain
2629 * BLOCK_EXTBBDEPOT - This block is part of the extended
2632 * See Windows documentation for more details on IStorage methods.
2634 ULONG StorageImpl_GetNextBlockInChain(
2638 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2639 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2640 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2641 ULONG nextBlockIndex = BLOCK_SPECIAL;
2643 ULONG depotBlockIndexPos;
2645 assert(depotBlockCount < This->bigBlockDepotCount);
2648 * Cache the currently accessed depot block.
2650 if (depotBlockCount != This->indexBlockDepotCached)
2652 This->indexBlockDepotCached = depotBlockCount;
2654 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2656 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2661 * We have to look in the extended depot.
2663 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2666 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2672 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2674 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &nextBlockIndex);
2675 This->blockDepotCached[index] = nextBlockIndex;
2678 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2682 nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2684 return nextBlockIndex;
2687 /******************************************************************************
2688 * Storage32Impl_GetNextExtendedBlock
2690 * Given an extended block this method will return the next extended block.
2693 * The last ULONG of an extended block is the block index of the next
2694 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2698 * - The index of the next extended block
2699 * - BLOCK_UNUSED: there is no next extended block.
2700 * - Any other return values denotes failure.
2702 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2704 ULONG nextBlockIndex = BLOCK_SPECIAL;
2705 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2708 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2712 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2714 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2717 return nextBlockIndex;
2720 /******************************************************************************
2721 * Storage32Impl_SetNextBlockInChain
2723 * This method will write the index of the specified block's next block
2724 * in the big block depot.
2726 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2729 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2730 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2731 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2734 void StorageImpl_SetNextBlockInChain(
2739 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2740 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2741 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2742 ULONG depotBlockIndexPos;
2745 assert(depotBlockCount < This->bigBlockDepotCount);
2746 assert(blockIndex != nextBlock);
2748 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2750 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2755 * We have to look in the extended depot.
2757 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2760 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2764 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2765 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2769 * Update the cached block depot, if necessary.
2771 if (depotBlockCount == This->indexBlockDepotCached)
2773 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2777 /******************************************************************************
2778 * Storage32Impl_LoadFileHeader
2780 * This method will read in the file header, i.e. big block index -1.
2782 HRESULT StorageImpl_LoadFileHeader(
2785 HRESULT hr = STG_E_FILENOTFOUND;
2786 void* headerBigBlock = NULL;
2790 * Get a pointer to the big block of data containing the header.
2792 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2795 * Extract the information from the header.
2797 if (headerBigBlock!=0)
2800 * Check for the "magic number" signature and return an error if it is not
2803 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2805 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2806 return STG_E_OLDFORMAT;
2809 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2811 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2812 return STG_E_INVALIDHEADER;
2815 StorageUtl_ReadWord(
2817 OFFSET_BIGBLOCKSIZEBITS,
2818 &This->bigBlockSizeBits);
2820 StorageUtl_ReadWord(
2822 OFFSET_SMALLBLOCKSIZEBITS,
2823 &This->smallBlockSizeBits);
2825 StorageUtl_ReadDWord(
2827 OFFSET_BBDEPOTCOUNT,
2828 &This->bigBlockDepotCount);
2830 StorageUtl_ReadDWord(
2832 OFFSET_ROOTSTARTBLOCK,
2833 &This->rootStartBlock);
2835 StorageUtl_ReadDWord(
2837 OFFSET_SBDEPOTSTART,
2838 &This->smallBlockDepotStart);
2840 StorageUtl_ReadDWord(
2842 OFFSET_EXTBBDEPOTSTART,
2843 &This->extBigBlockDepotStart);
2845 StorageUtl_ReadDWord(
2847 OFFSET_EXTBBDEPOTCOUNT,
2848 &This->extBigBlockDepotCount);
2850 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2852 StorageUtl_ReadDWord(
2854 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2855 &(This->bigBlockDepotStart[index]));
2859 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2863 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2864 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2868 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2869 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2873 * Right now, the code is making some assumptions about the size of the
2874 * blocks, just make sure they are what we're expecting.
2876 assert( (This->bigBlockSize==DEF_BIG_BLOCK_SIZE) &&
2877 (This->smallBlockSize==DEF_SMALL_BLOCK_SIZE));
2880 * Release the block.
2882 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2890 /******************************************************************************
2891 * Storage32Impl_SaveFileHeader
2893 * This method will save to the file the header, i.e. big block -1.
2895 void StorageImpl_SaveFileHeader(
2898 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2903 * Get a pointer to the big block of data containing the header.
2905 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2908 * If the block read failed, the file is probably new.
2913 * Initialize for all unknown fields.
2915 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
2918 * Initialize the magic number.
2920 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
2923 * And a bunch of things we don't know what they mean
2925 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
2926 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
2927 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
2928 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
2929 StorageUtl_WriteDWord(headerBigBlock, 0x40, (DWORD)0x0001);
2933 * Write the information to the header.
2935 if (headerBigBlock!=0)
2937 StorageUtl_WriteWord(
2939 OFFSET_BIGBLOCKSIZEBITS,
2940 This->bigBlockSizeBits);
2942 StorageUtl_WriteWord(
2944 OFFSET_SMALLBLOCKSIZEBITS,
2945 This->smallBlockSizeBits);
2947 StorageUtl_WriteDWord(
2949 OFFSET_BBDEPOTCOUNT,
2950 This->bigBlockDepotCount);
2952 StorageUtl_WriteDWord(
2954 OFFSET_ROOTSTARTBLOCK,
2955 This->rootStartBlock);
2957 StorageUtl_WriteDWord(
2959 OFFSET_SBDEPOTSTART,
2960 This->smallBlockDepotStart);
2962 StorageUtl_WriteDWord(
2964 OFFSET_EXTBBDEPOTSTART,
2965 This->extBigBlockDepotStart);
2967 StorageUtl_WriteDWord(
2969 OFFSET_EXTBBDEPOTCOUNT,
2970 This->extBigBlockDepotCount);
2972 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2974 StorageUtl_WriteDWord(
2976 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2977 (This->bigBlockDepotStart[index]));
2982 * Write the big block back to the file.
2984 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
2987 /******************************************************************************
2988 * Storage32Impl_ReadProperty
2990 * This method will read the specified property from the property chain.
2992 BOOL StorageImpl_ReadProperty(
2995 StgProperty* buffer)
2997 BYTE currentProperty[PROPSET_BLOCK_SIZE];
2998 ULARGE_INTEGER offsetInPropSet;
2999 BOOL readSuccessful;
3002 offsetInPropSet.s.HighPart = 0;
3003 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3005 readSuccessful = BlockChainStream_ReadAt(
3006 This->rootBlockChain,
3014 memset(buffer->name, 0, sizeof(buffer->name));
3017 currentProperty+OFFSET_PS_NAME,
3018 PROPERTY_NAME_BUFFER_LEN );
3020 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3022 StorageUtl_ReadWord(
3024 OFFSET_PS_NAMELENGTH,
3025 &buffer->sizeOfNameString);
3027 StorageUtl_ReadDWord(
3029 OFFSET_PS_PREVIOUSPROP,
3030 &buffer->previousProperty);
3032 StorageUtl_ReadDWord(
3035 &buffer->nextProperty);
3037 StorageUtl_ReadDWord(
3040 &buffer->dirProperty);
3042 StorageUtl_ReadGUID(
3045 &buffer->propertyUniqueID);
3047 StorageUtl_ReadDWord(
3050 &buffer->timeStampS1);
3052 StorageUtl_ReadDWord(
3055 &buffer->timeStampD1);
3057 StorageUtl_ReadDWord(
3060 &buffer->timeStampS2);
3062 StorageUtl_ReadDWord(
3065 &buffer->timeStampD2);
3067 StorageUtl_ReadDWord(
3069 OFFSET_PS_STARTBLOCK,
3070 &buffer->startingBlock);
3072 StorageUtl_ReadDWord(
3075 &buffer->size.s.LowPart);
3077 buffer->size.s.HighPart = 0;
3080 return readSuccessful;
3083 /*********************************************************************
3084 * Write the specified property into the property chain
3086 BOOL StorageImpl_WriteProperty(
3089 StgProperty* buffer)
3091 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3092 ULARGE_INTEGER offsetInPropSet;
3093 BOOL writeSuccessful;
3096 offsetInPropSet.s.HighPart = 0;
3097 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3099 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3102 currentProperty + OFFSET_PS_NAME,
3104 PROPERTY_NAME_BUFFER_LEN );
3106 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3108 StorageUtl_WriteWord(
3110 OFFSET_PS_NAMELENGTH,
3111 buffer->sizeOfNameString);
3113 StorageUtl_WriteDWord(
3115 OFFSET_PS_PREVIOUSPROP,
3116 buffer->previousProperty);
3118 StorageUtl_WriteDWord(
3121 buffer->nextProperty);
3123 StorageUtl_WriteDWord(
3126 buffer->dirProperty);
3128 StorageUtl_WriteGUID(
3131 &buffer->propertyUniqueID);
3133 StorageUtl_WriteDWord(
3136 buffer->timeStampS1);
3138 StorageUtl_WriteDWord(
3141 buffer->timeStampD1);
3143 StorageUtl_WriteDWord(
3146 buffer->timeStampS2);
3148 StorageUtl_WriteDWord(
3151 buffer->timeStampD2);
3153 StorageUtl_WriteDWord(
3155 OFFSET_PS_STARTBLOCK,
3156 buffer->startingBlock);
3158 StorageUtl_WriteDWord(
3161 buffer->size.s.LowPart);
3163 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3168 return writeSuccessful;
3171 BOOL StorageImpl_ReadBigBlock(
3176 void* bigBlockBuffer;
3178 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3180 if (bigBlockBuffer!=0)
3182 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3184 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3192 BOOL StorageImpl_WriteBigBlock(
3197 void* bigBlockBuffer;
3199 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3201 if (bigBlockBuffer!=0)
3203 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3205 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3213 void* StorageImpl_GetROBigBlock(
3217 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3220 void* StorageImpl_GetBigBlock(
3224 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3227 void StorageImpl_ReleaseBigBlock(
3231 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3234 /******************************************************************************
3235 * Storage32Impl_SmallBlocksToBigBlocks
3237 * This method will convert a small block chain to a big block chain.
3238 * The small block chain will be destroyed.
3240 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3242 SmallBlockChainStream** ppsbChain)
3244 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3245 ULARGE_INTEGER size, offset;
3246 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3247 ULONG propertyIndex;
3248 BOOL successRead, successWrite;
3249 StgProperty chainProperty;
3251 BlockChainStream *bbTempChain = NULL;
3252 BlockChainStream *bigBlockChain = NULL;
3255 * Create a temporary big block chain that doesn't have
3256 * an associated property. This temporary chain will be
3257 * used to copy data from small blocks to big blocks.
3259 bbTempChain = BlockChainStream_Construct(This,
3264 * Grow the big block chain.
3266 size = SmallBlockChainStream_GetSize(*ppsbChain);
3267 BlockChainStream_SetSize(bbTempChain, size);
3270 * Copy the contents of the small block chain to the big block chain
3271 * by small block size increments.
3273 offset.s.LowPart = 0;
3274 offset.s.HighPart = 0;
3278 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3281 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3283 DEF_SMALL_BLOCK_SIZE,
3286 cbTotalRead += cbRead;
3288 successWrite = BlockChainStream_WriteAt(bbTempChain,
3293 cbTotalWritten += cbWritten;
3295 offset.s.LowPart += This->smallBlockSize;
3297 } while (successRead && successWrite);
3298 HeapFree(GetProcessHeap(),0,buffer);
3300 assert(cbTotalRead == cbTotalWritten);
3303 * Destroy the small block chain.
3305 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3306 size.s.HighPart = 0;
3308 SmallBlockChainStream_SetSize(*ppsbChain, size);
3309 SmallBlockChainStream_Destroy(*ppsbChain);
3313 * Change the property information. This chain is now a big block chain
3314 * and it doesn't reside in the small blocks chain anymore.
3316 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3318 chainProperty.startingBlock = bbHeadOfChain;
3320 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3323 * Destroy the temporary propertyless big block chain.
3324 * Create a new big block chain associated with this property.
3326 BlockChainStream_Destroy(bbTempChain);
3327 bigBlockChain = BlockChainStream_Construct(This,
3331 return bigBlockChain;
3334 /******************************************************************************
3335 ** Storage32InternalImpl implementation
3338 StorageInternalImpl* StorageInternalImpl_Construct(
3339 StorageImpl* ancestorStorage,
3340 ULONG rootPropertyIndex)
3342 StorageInternalImpl* newStorage;
3345 * Allocate space for the new storage object
3347 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3351 memset(newStorage, 0, sizeof(StorageInternalImpl));
3354 * Initialize the virtual function table.
3356 ICOM_VTBL(newStorage) = &Storage32InternalImpl_Vtbl;
3357 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3360 * Keep the ancestor storage pointer and nail a reference to it.
3362 newStorage->ancestorStorage = ancestorStorage;
3363 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3366 * Keep the index of the root property set for this storage,
3368 newStorage->rootPropertySetIndex = rootPropertyIndex;
3376 void StorageInternalImpl_Destroy(
3377 StorageInternalImpl* This)
3379 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3380 HeapFree(GetProcessHeap(), 0, This);
3383 /******************************************************************************
3385 ** Storage32InternalImpl_Commit
3387 ** The non-root storages cannot be opened in transacted mode thus this function
3390 HRESULT WINAPI StorageInternalImpl_Commit(
3392 DWORD grfCommitFlags) /* [in] */
3397 /******************************************************************************
3399 ** Storage32InternalImpl_Revert
3401 ** The non-root storages cannot be opened in transacted mode thus this function
3404 HRESULT WINAPI StorageInternalImpl_Revert(
3410 /******************************************************************************
3411 ** IEnumSTATSTGImpl implementation
3414 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3415 StorageImpl* parentStorage,
3416 ULONG firstPropertyNode)
3418 IEnumSTATSTGImpl* newEnumeration;
3420 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3422 if (newEnumeration!=0)
3425 * Set-up the virtual function table and reference count.
3427 ICOM_VTBL(newEnumeration) = &IEnumSTATSTGImpl_Vtbl;
3428 newEnumeration->ref = 0;
3431 * We want to nail-down the reference to the storage in case the
3432 * enumeration out-lives the storage in the client application.
3434 newEnumeration->parentStorage = parentStorage;
3435 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3437 newEnumeration->firstPropertyNode = firstPropertyNode;
3440 * Initialize the search stack
3442 newEnumeration->stackSize = 0;
3443 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3444 newEnumeration->stackToVisit =
3445 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3448 * Make sure the current node of the iterator is the first one.
3450 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3453 return newEnumeration;
3456 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3458 IStorage_Release((IStorage*)This->parentStorage);
3459 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3460 HeapFree(GetProcessHeap(), 0, This);
3463 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3464 IEnumSTATSTG* iface,
3468 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3471 * Perform a sanity check on the parameters.
3474 return E_INVALIDARG;
3477 * Initialize the return parameter.
3482 * Compare the riid with the interface IDs implemented by this object.
3484 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3486 *ppvObject = (IEnumSTATSTG*)This;
3488 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3490 *ppvObject = (IEnumSTATSTG*)This;
3494 * Check that we obtained an interface.
3496 if ((*ppvObject)==0)
3497 return E_NOINTERFACE;
3500 * Query Interface always increases the reference count by one when it is
3503 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3508 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3509 IEnumSTATSTG* iface)
3511 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3517 ULONG WINAPI IEnumSTATSTGImpl_Release(
3518 IEnumSTATSTG* iface)
3520 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3528 * If the reference count goes down to 0, perform suicide.
3532 IEnumSTATSTGImpl_Destroy(This);
3538 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3539 IEnumSTATSTG* iface,
3542 ULONG* pceltFetched)
3544 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3546 StgProperty currentProperty;
3547 STATSTG* currentReturnStruct = rgelt;
3548 ULONG objectFetched = 0;
3549 ULONG currentSearchNode;
3552 * Perform a sanity check on the parameters.
3554 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3555 return E_INVALIDARG;
3558 * To avoid the special case, get another pointer to a ULONG value if
3559 * the caller didn't supply one.
3561 if (pceltFetched==0)
3562 pceltFetched = &objectFetched;
3565 * Start the iteration, we will iterate until we hit the end of the
3566 * linked list or until we hit the number of items to iterate through
3571 * Start with the node at the top of the stack.
3573 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3575 while ( ( *pceltFetched < celt) &&
3576 ( currentSearchNode!=PROPERTY_NULL) )
3579 * Remove the top node from the stack
3581 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3584 * Read the property from the storage.
3586 StorageImpl_ReadProperty(This->parentStorage,
3591 * Copy the information to the return buffer.
3593 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3598 * Step to the next item in the iteration
3601 currentReturnStruct++;
3604 * Push the next search node in the search stack.
3606 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3609 * continue the iteration.
3611 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3614 if (*pceltFetched == celt)
3621 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3622 IEnumSTATSTG* iface,
3625 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3627 StgProperty currentProperty;
3628 ULONG objectFetched = 0;
3629 ULONG currentSearchNode;
3632 * Start with the node at the top of the stack.
3634 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3636 while ( (objectFetched < celt) &&
3637 (currentSearchNode!=PROPERTY_NULL) )
3640 * Remove the top node from the stack
3642 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3645 * Read the property from the storage.
3647 StorageImpl_ReadProperty(This->parentStorage,
3652 * Step to the next item in the iteration
3657 * Push the next search node in the search stack.
3659 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3662 * continue the iteration.
3664 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3667 if (objectFetched == celt)
3673 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3674 IEnumSTATSTG* iface)
3676 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3678 StgProperty rootProperty;
3679 BOOL readSuccessful;
3682 * Re-initialize the search stack to an empty stack
3684 This->stackSize = 0;
3687 * Read the root property from the storage.
3689 readSuccessful = StorageImpl_ReadProperty(
3690 This->parentStorage,
3691 This->firstPropertyNode,
3696 assert(rootProperty.sizeOfNameString!=0);
3699 * Push the search node in the search stack.
3701 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3707 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3708 IEnumSTATSTG* iface,
3709 IEnumSTATSTG** ppenum)
3711 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3713 IEnumSTATSTGImpl* newClone;
3716 * Perform a sanity check on the parameters.
3719 return E_INVALIDARG;
3721 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3722 This->firstPropertyNode);
3726 * The new clone enumeration must point to the same current node as
3729 newClone->stackSize = This->stackSize ;
3730 newClone->stackMaxSize = This->stackMaxSize ;
3731 newClone->stackToVisit =
3732 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3735 newClone->stackToVisit,
3737 sizeof(ULONG) * newClone->stackSize);
3739 *ppenum = (IEnumSTATSTG*)newClone;
3742 * Don't forget to nail down a reference to the clone before
3745 IEnumSTATSTGImpl_AddRef(*ppenum);
3750 INT IEnumSTATSTGImpl_FindParentProperty(
3751 IEnumSTATSTGImpl *This,
3752 ULONG childProperty,
3753 StgProperty *currentProperty,
3756 ULONG currentSearchNode;
3760 * To avoid the special case, get another pointer to a ULONG value if
3761 * the caller didn't supply one.
3764 thisNodeId = &foundNode;
3767 * Start with the node at the top of the stack.
3769 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3772 while (currentSearchNode!=PROPERTY_NULL)
3775 * Store the current node in the returned parameters
3777 *thisNodeId = currentSearchNode;
3780 * Remove the top node from the stack
3782 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3785 * Read the property from the storage.
3787 StorageImpl_ReadProperty(
3788 This->parentStorage,
3792 if (currentProperty->previousProperty == childProperty)
3793 return PROPERTY_RELATION_PREVIOUS;
3795 else if (currentProperty->nextProperty == childProperty)
3796 return PROPERTY_RELATION_NEXT;
3798 else if (currentProperty->dirProperty == childProperty)
3799 return PROPERTY_RELATION_DIR;
3802 * Push the next search node in the search stack.
3804 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3807 * continue the iteration.
3809 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3812 return PROPERTY_NULL;
3815 ULONG IEnumSTATSTGImpl_FindProperty(
3816 IEnumSTATSTGImpl* This,
3817 const OLECHAR* lpszPropName,
3818 StgProperty* currentProperty)
3820 ULONG currentSearchNode;
3823 * Start with the node at the top of the stack.
3825 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3827 while (currentSearchNode!=PROPERTY_NULL)
3830 * Remove the top node from the stack
3832 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3835 * Read the property from the storage.
3837 StorageImpl_ReadProperty(This->parentStorage,
3841 if ( propertyNameCmp(
3842 (OLECHAR*)currentProperty->name,
3843 (OLECHAR*)lpszPropName) == 0)
3844 return currentSearchNode;
3847 * Push the next search node in the search stack.
3849 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3852 * continue the iteration.
3854 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3857 return PROPERTY_NULL;
3860 void IEnumSTATSTGImpl_PushSearchNode(
3861 IEnumSTATSTGImpl* This,
3864 StgProperty rootProperty;
3865 BOOL readSuccessful;
3868 * First, make sure we're not trying to push an unexisting node.
3870 if (nodeToPush==PROPERTY_NULL)
3874 * First push the node to the stack
3876 if (This->stackSize == This->stackMaxSize)
3878 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3880 This->stackToVisit = HeapReAlloc(
3884 sizeof(ULONG) * This->stackMaxSize);
3887 This->stackToVisit[This->stackSize] = nodeToPush;
3891 * Read the root property from the storage.
3893 readSuccessful = StorageImpl_ReadProperty(
3894 This->parentStorage,
3900 assert(rootProperty.sizeOfNameString!=0);
3903 * Push the previous search node in the search stack.
3905 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3909 ULONG IEnumSTATSTGImpl_PopSearchNode(
3910 IEnumSTATSTGImpl* This,
3915 if (This->stackSize == 0)
3916 return PROPERTY_NULL;
3918 topNode = This->stackToVisit[This->stackSize-1];
3926 /******************************************************************************
3927 ** StorageUtl implementation
3930 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
3932 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
3935 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
3937 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
3940 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
3942 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
3945 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
3947 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
3950 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
3952 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
3953 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
3954 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
3956 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
3959 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
3961 StorageUtl_WriteDWord(buffer, offset, value->Data1);
3962 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
3963 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
3965 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
3968 void StorageUtl_CopyPropertyToSTATSTG(
3969 STATSTG* destination,
3970 StgProperty* source,
3974 * The copy of the string occurs only when the flag is not set
3976 if ((statFlags & STATFLAG_NONAME) != 0)
3978 destination->pwcsName = 0;
3982 destination->pwcsName =
3983 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
3985 strcpyW((LPWSTR)destination->pwcsName, source->name);
3988 switch (source->propertyType)
3990 case PROPTYPE_STORAGE:
3992 destination->type = STGTY_STORAGE;
3994 case PROPTYPE_STREAM:
3995 destination->type = STGTY_STREAM;
3998 destination->type = STGTY_STREAM;
4002 destination->cbSize = source->size;
4004 currentReturnStruct->mtime = {0}; TODO
4005 currentReturnStruct->ctime = {0};
4006 currentReturnStruct->atime = {0};
4008 destination->grfMode = 0;
4009 destination->grfLocksSupported = 0;
4010 destination->clsid = source->propertyUniqueID;
4011 destination->grfStateBits = 0;
4012 destination->reserved = 0;
4015 /******************************************************************************
4016 ** BlockChainStream implementation
4019 BlockChainStream* BlockChainStream_Construct(
4020 StorageImpl* parentStorage,
4021 ULONG* headOfStreamPlaceHolder,
4022 ULONG propertyIndex)
4024 BlockChainStream* newStream;
4027 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4029 newStream->parentStorage = parentStorage;
4030 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4031 newStream->ownerPropertyIndex = propertyIndex;
4032 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4033 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4034 newStream->numBlocks = 0;
4036 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4038 while (blockIndex != BLOCK_END_OF_CHAIN)
4040 newStream->numBlocks++;
4041 newStream->tailIndex = blockIndex;
4043 blockIndex = StorageImpl_GetNextBlockInChain(
4051 void BlockChainStream_Destroy(BlockChainStream* This)
4053 HeapFree(GetProcessHeap(), 0, This);
4056 /******************************************************************************
4057 * BlockChainStream_GetHeadOfChain
4059 * Returns the head of this stream chain.
4060 * Some special chains don't have properties, their heads are kept in
4061 * This->headOfStreamPlaceHolder.
4064 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4066 StgProperty chainProperty;
4067 BOOL readSuccessful;
4069 if (This->headOfStreamPlaceHolder != 0)
4070 return *(This->headOfStreamPlaceHolder);
4072 if (This->ownerPropertyIndex != PROPERTY_NULL)
4074 readSuccessful = StorageImpl_ReadProperty(
4075 This->parentStorage,
4076 This->ownerPropertyIndex,
4081 return chainProperty.startingBlock;
4085 return BLOCK_END_OF_CHAIN;
4088 /******************************************************************************
4089 * BlockChainStream_GetCount
4091 * Returns the number of blocks that comprises this chain.
4092 * This is not the size of the stream as the last block may not be full!
4095 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4100 blockIndex = BlockChainStream_GetHeadOfChain(This);
4102 while (blockIndex != BLOCK_END_OF_CHAIN)
4106 blockIndex = StorageImpl_GetNextBlockInChain(
4107 This->parentStorage,
4114 /******************************************************************************
4115 * BlockChainStream_ReadAt
4117 * Reads a specified number of bytes from this chain at the specified offset.
4118 * bytesRead may be NULL.
4119 * Failure will be returned if the specified number of bytes has not been read.
4121 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4122 ULARGE_INTEGER offset,
4127 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4128 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4129 ULONG bytesToReadInBuffer;
4132 BYTE* bigBlockBuffer;
4135 * Find the first block in the stream that contains part of the buffer.
4137 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4138 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4139 (blockNoInSequence < This->lastBlockNoInSequence) )
4141 blockIndex = BlockChainStream_GetHeadOfChain(This);
4142 This->lastBlockNoInSequence = blockNoInSequence;
4146 ULONG temp = blockNoInSequence;
4148 blockIndex = This->lastBlockNoInSequenceIndex;
4149 blockNoInSequence -= This->lastBlockNoInSequence;
4150 This->lastBlockNoInSequence = temp;
4153 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4156 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4158 blockNoInSequence--;
4161 This->lastBlockNoInSequenceIndex = blockIndex;
4164 * Start reading the buffer.
4167 bufferWalker = buffer;
4169 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4172 * Calculate how many bytes we can copy from this big block.
4174 bytesToReadInBuffer =
4175 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4178 * Copy those bytes to the buffer
4181 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4183 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4185 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4188 * Step to the next big block.
4191 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4193 bufferWalker += bytesToReadInBuffer;
4194 size -= bytesToReadInBuffer;
4195 *bytesRead += bytesToReadInBuffer;
4196 offsetInBlock = 0; /* There is no offset on the next block */
4203 /******************************************************************************
4204 * BlockChainStream_WriteAt
4206 * Writes the specified number of bytes to this chain at the specified offset.
4207 * bytesWritten may be NULL.
4208 * Will fail if not all specified number of bytes have been written.
4210 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4211 ULARGE_INTEGER offset,
4214 ULONG* bytesWritten)
4216 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4217 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4221 BYTE* bigBlockBuffer;
4224 * Find the first block in the stream that contains part of the buffer.
4226 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4227 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4228 (blockNoInSequence < This->lastBlockNoInSequence) )
4230 blockIndex = BlockChainStream_GetHeadOfChain(This);
4231 This->lastBlockNoInSequence = blockNoInSequence;
4235 ULONG temp = blockNoInSequence;
4237 blockIndex = This->lastBlockNoInSequenceIndex;
4238 blockNoInSequence -= This->lastBlockNoInSequence;
4239 This->lastBlockNoInSequence = temp;
4242 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4245 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4247 blockNoInSequence--;
4250 This->lastBlockNoInSequenceIndex = blockIndex;
4253 * Here, I'm casting away the constness on the buffer variable
4254 * This is OK since we don't intend to modify that buffer.
4257 bufferWalker = (BYTE*)buffer;
4259 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4262 * Calculate how many bytes we can copy from this big block.
4265 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4268 * Copy those bytes to the buffer
4270 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4272 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4274 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4277 * Step to the next big block.
4280 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4282 bufferWalker += bytesToWrite;
4283 size -= bytesToWrite;
4284 *bytesWritten += bytesToWrite;
4285 offsetInBlock = 0; /* There is no offset on the next block */
4291 /******************************************************************************
4292 * BlockChainStream_Shrink
4294 * Shrinks this chain in the big block depot.
4296 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4297 ULARGE_INTEGER newSize)
4299 ULONG blockIndex, extraBlock;
4304 * Reset the last accessed block cache.
4306 This->lastBlockNoInSequence = 0xFFFFFFFF;
4307 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4310 * Figure out how many blocks are needed to contain the new size
4312 numBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4314 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4317 blockIndex = BlockChainStream_GetHeadOfChain(This);
4320 * Go to the new end of chain
4322 while (count < numBlocks)
4325 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4330 /* Get the next block before marking the new end */
4332 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4334 /* Mark the new end of chain */
4335 StorageImpl_SetNextBlockInChain(
4336 This->parentStorage,
4338 BLOCK_END_OF_CHAIN);
4340 This->tailIndex = blockIndex;
4341 This->numBlocks = numBlocks;
4344 * Mark the extra blocks as free
4346 while (extraBlock != BLOCK_END_OF_CHAIN)
4349 StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock);
4351 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4352 extraBlock = blockIndex;
4358 /******************************************************************************
4359 * BlockChainStream_Enlarge
4361 * Grows this chain in the big block depot.
4363 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4364 ULARGE_INTEGER newSize)
4366 ULONG blockIndex, currentBlock;
4368 ULONG oldNumBlocks = 0;
4370 blockIndex = BlockChainStream_GetHeadOfChain(This);
4373 * Empty chain. Create the head.
4375 if (blockIndex == BLOCK_END_OF_CHAIN)
4377 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4378 StorageImpl_SetNextBlockInChain(This->parentStorage,
4380 BLOCK_END_OF_CHAIN);
4382 if (This->headOfStreamPlaceHolder != 0)
4384 *(This->headOfStreamPlaceHolder) = blockIndex;
4388 StgProperty chainProp;
4389 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4391 StorageImpl_ReadProperty(
4392 This->parentStorage,
4393 This->ownerPropertyIndex,
4396 chainProp.startingBlock = blockIndex;
4398 StorageImpl_WriteProperty(
4399 This->parentStorage,
4400 This->ownerPropertyIndex,
4404 This->tailIndex = blockIndex;
4405 This->numBlocks = 1;
4409 * Figure out how many blocks are needed to contain this stream
4411 newNumBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4413 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4417 * Go to the current end of chain
4419 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4421 currentBlock = blockIndex;
4423 while (blockIndex != BLOCK_END_OF_CHAIN)
4426 currentBlock = blockIndex;
4429 StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock);
4432 This->tailIndex = currentBlock;
4435 currentBlock = This->tailIndex;
4436 oldNumBlocks = This->numBlocks;
4439 * Add new blocks to the chain
4441 if (oldNumBlocks < newNumBlocks)
4443 while (oldNumBlocks < newNumBlocks)
4445 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4447 StorageImpl_SetNextBlockInChain(
4448 This->parentStorage,
4452 StorageImpl_SetNextBlockInChain(
4453 This->parentStorage,
4455 BLOCK_END_OF_CHAIN);
4457 currentBlock = blockIndex;
4461 This->tailIndex = blockIndex;
4462 This->numBlocks = newNumBlocks;
4468 /******************************************************************************
4469 * BlockChainStream_SetSize
4471 * Sets the size of this stream. The big block depot will be updated.
4472 * The file will grow if we grow the chain.
4474 * TODO: Free the actual blocks in the file when we shrink the chain.
4475 * Currently, the blocks are still in the file. So the file size
4476 * doesn't shrink even if we shrink streams.
4478 BOOL BlockChainStream_SetSize(
4479 BlockChainStream* This,
4480 ULARGE_INTEGER newSize)
4482 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4484 if (newSize.s.LowPart == size.s.LowPart)
4487 if (newSize.s.LowPart < size.s.LowPart)
4489 BlockChainStream_Shrink(This, newSize);
4493 ULARGE_INTEGER fileSize =
4494 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4496 ULONG diff = newSize.s.LowPart - size.s.LowPart;
4499 * Make sure the file stays a multiple of blocksize
4501 if ((diff % This->parentStorage->bigBlockSize) != 0)
4502 diff += (This->parentStorage->bigBlockSize -
4503 (diff % This->parentStorage->bigBlockSize) );
4505 fileSize.s.LowPart += diff;
4506 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4508 BlockChainStream_Enlarge(This, newSize);
4514 /******************************************************************************
4515 * BlockChainStream_GetSize
4517 * Returns the size of this chain.
4518 * Will return the block count if this chain doesn't have a property.
4520 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4522 StgProperty chainProperty;
4524 if(This->headOfStreamPlaceHolder == NULL)
4527 * This chain is a data stream read the property and return
4528 * the appropriate size
4530 StorageImpl_ReadProperty(
4531 This->parentStorage,
4532 This->ownerPropertyIndex,
4535 return chainProperty.size;
4540 * this chain is a chain that does not have a property, figure out the
4541 * size by making the product number of used blocks times the
4544 ULARGE_INTEGER result;
4545 result.s.HighPart = 0;
4548 BlockChainStream_GetCount(This) *
4549 This->parentStorage->bigBlockSize;
4555 /******************************************************************************
4556 ** SmallBlockChainStream implementation
4559 SmallBlockChainStream* SmallBlockChainStream_Construct(
4560 StorageImpl* parentStorage,
4561 ULONG propertyIndex)
4563 SmallBlockChainStream* newStream;
4565 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4567 newStream->parentStorage = parentStorage;
4568 newStream->ownerPropertyIndex = propertyIndex;
4573 void SmallBlockChainStream_Destroy(
4574 SmallBlockChainStream* This)
4576 HeapFree(GetProcessHeap(), 0, This);
4579 /******************************************************************************
4580 * SmallBlockChainStream_GetHeadOfChain
4582 * Returns the head of this chain of small blocks.
4584 ULONG SmallBlockChainStream_GetHeadOfChain(
4585 SmallBlockChainStream* This)
4587 StgProperty chainProperty;
4588 BOOL readSuccessful;
4590 if (This->ownerPropertyIndex)
4592 readSuccessful = StorageImpl_ReadProperty(
4593 This->parentStorage,
4594 This->ownerPropertyIndex,
4599 return chainProperty.startingBlock;
4604 return BLOCK_END_OF_CHAIN;
4607 /******************************************************************************
4608 * SmallBlockChainStream_GetNextBlockInChain
4610 * Returns the index of the next small block in this chain.
4613 * - BLOCK_END_OF_CHAIN: end of this chain
4614 * - BLOCK_UNUSED: small block 'blockIndex' is free
4616 ULONG SmallBlockChainStream_GetNextBlockInChain(
4617 SmallBlockChainStream* This,
4620 ULARGE_INTEGER offsetOfBlockInDepot;
4622 ULONG nextBlockInChain = BLOCK_END_OF_CHAIN;
4626 offsetOfBlockInDepot.s.HighPart = 0;
4627 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4630 * Read those bytes in the buffer from the small block file.
4632 success = BlockChainStream_ReadAt(
4633 This->parentStorage->smallBlockDepotChain,
4634 offsetOfBlockInDepot,
4641 StorageUtl_ReadDWord(&buffer, 0, &nextBlockInChain);
4644 return nextBlockInChain;
4647 /******************************************************************************
4648 * SmallBlockChainStream_SetNextBlockInChain
4650 * Writes the index of the next block of the specified block in the small
4652 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4653 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4655 void SmallBlockChainStream_SetNextBlockInChain(
4656 SmallBlockChainStream* This,
4660 ULARGE_INTEGER offsetOfBlockInDepot;
4664 offsetOfBlockInDepot.s.HighPart = 0;
4665 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4667 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4670 * Read those bytes in the buffer from the small block file.
4672 BlockChainStream_WriteAt(
4673 This->parentStorage->smallBlockDepotChain,
4674 offsetOfBlockInDepot,
4680 /******************************************************************************
4681 * SmallBlockChainStream_FreeBlock
4683 * Flag small block 'blockIndex' as free in the small block depot.
4685 void SmallBlockChainStream_FreeBlock(
4686 SmallBlockChainStream* This,
4689 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4692 /******************************************************************************
4693 * SmallBlockChainStream_GetNextFreeBlock
4695 * Returns the index of a free small block. The small block depot will be
4696 * enlarged if necessary. The small block chain will also be enlarged if
4699 ULONG SmallBlockChainStream_GetNextFreeBlock(
4700 SmallBlockChainStream* This)
4702 ULARGE_INTEGER offsetOfBlockInDepot;
4705 ULONG blockIndex = 0;
4706 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4707 BOOL success = TRUE;
4708 ULONG smallBlocksPerBigBlock;
4710 offsetOfBlockInDepot.s.HighPart = 0;
4713 * Scan the small block depot for a free block
4715 while (nextBlockIndex != BLOCK_UNUSED)
4717 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4719 success = BlockChainStream_ReadAt(
4720 This->parentStorage->smallBlockDepotChain,
4721 offsetOfBlockInDepot,
4727 * If we run out of space for the small block depot, enlarge it
4731 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4733 if (nextBlockIndex != BLOCK_UNUSED)
4739 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4741 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4742 ULONG nextBlock, newsbdIndex;
4743 BYTE* smallBlockDepot;
4745 nextBlock = sbdIndex;
4746 while (nextBlock != BLOCK_END_OF_CHAIN)
4748 sbdIndex = nextBlock;
4750 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex);
4753 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4754 if (sbdIndex != BLOCK_END_OF_CHAIN)
4755 StorageImpl_SetNextBlockInChain(
4756 This->parentStorage,
4760 StorageImpl_SetNextBlockInChain(
4761 This->parentStorage,
4763 BLOCK_END_OF_CHAIN);
4766 * Initialize all the small blocks to free
4769 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4771 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4772 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4777 * We have just created the small block depot.
4779 StgProperty rootProp;
4783 * Save it in the header
4785 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4786 StorageImpl_SaveFileHeader(This->parentStorage);
4789 * And allocate the first big block that will contain small blocks
4792 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4794 StorageImpl_SetNextBlockInChain(
4795 This->parentStorage,
4797 BLOCK_END_OF_CHAIN);
4799 StorageImpl_ReadProperty(
4800 This->parentStorage,
4801 This->parentStorage->rootPropertySetIndex,
4804 rootProp.startingBlock = sbStartIndex;
4805 rootProp.size.s.HighPart = 0;
4806 rootProp.size.s.LowPart = This->parentStorage->bigBlockSize;
4808 StorageImpl_WriteProperty(
4809 This->parentStorage,
4810 This->parentStorage->rootPropertySetIndex,
4816 smallBlocksPerBigBlock =
4817 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4820 * Verify if we have to allocate big blocks to contain small blocks
4822 if (blockIndex % smallBlocksPerBigBlock == 0)
4824 StgProperty rootProp;
4825 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4827 StorageImpl_ReadProperty(
4828 This->parentStorage,
4829 This->parentStorage->rootPropertySetIndex,
4832 if (rootProp.size.s.LowPart <
4833 (blocksRequired * This->parentStorage->bigBlockSize))
4835 rootProp.size.s.LowPart += This->parentStorage->bigBlockSize;
4837 BlockChainStream_SetSize(
4838 This->parentStorage->smallBlockRootChain,
4841 StorageImpl_WriteProperty(
4842 This->parentStorage,
4843 This->parentStorage->rootPropertySetIndex,
4851 /******************************************************************************
4852 * SmallBlockChainStream_ReadAt
4854 * Reads a specified number of bytes from this chain at the specified offset.
4855 * bytesRead may be NULL.
4856 * Failure will be returned if the specified number of bytes has not been read.
4858 BOOL SmallBlockChainStream_ReadAt(
4859 SmallBlockChainStream* This,
4860 ULARGE_INTEGER offset,
4865 ULARGE_INTEGER offsetInBigBlockFile;
4866 ULONG blockNoInSequence =
4867 offset.s.LowPart / This->parentStorage->smallBlockSize;
4869 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
4870 ULONG bytesToReadInBuffer;
4872 ULONG bytesReadFromBigBlockFile;
4876 * This should never happen on a small block file.
4878 assert(offset.s.HighPart==0);
4881 * Find the first block in the stream that contains part of the buffer.
4883 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4885 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4887 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4889 blockNoInSequence--;
4893 * Start reading the buffer.
4896 bufferWalker = buffer;
4898 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4901 * Calculate how many bytes we can copy from this small block.
4903 bytesToReadInBuffer =
4904 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
4907 * Calculate the offset of the small block in the small block file.
4909 offsetInBigBlockFile.s.HighPart = 0;
4910 offsetInBigBlockFile.s.LowPart =
4911 blockIndex * This->parentStorage->smallBlockSize;
4913 offsetInBigBlockFile.s.LowPart += offsetInBlock;
4916 * Read those bytes in the buffer from the small block file.
4918 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
4919 offsetInBigBlockFile,
4920 bytesToReadInBuffer,
4922 &bytesReadFromBigBlockFile);
4924 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
4927 * Step to the next big block.
4929 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4930 bufferWalker += bytesToReadInBuffer;
4931 size -= bytesToReadInBuffer;
4932 *bytesRead += bytesToReadInBuffer;
4933 offsetInBlock = 0; /* There is no offset on the next block */
4939 /******************************************************************************
4940 * SmallBlockChainStream_WriteAt
4942 * Writes the specified number of bytes to this chain at the specified offset.
4943 * bytesWritten may be NULL.
4944 * Will fail if not all specified number of bytes have been written.
4946 BOOL SmallBlockChainStream_WriteAt(
4947 SmallBlockChainStream* This,
4948 ULARGE_INTEGER offset,
4951 ULONG* bytesWritten)
4953 ULARGE_INTEGER offsetInBigBlockFile;
4954 ULONG blockNoInSequence =
4955 offset.s.LowPart / This->parentStorage->smallBlockSize;
4957 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
4958 ULONG bytesToWriteInBuffer;
4960 ULONG bytesWrittenFromBigBlockFile;
4964 * This should never happen on a small block file.
4966 assert(offset.s.HighPart==0);
4969 * Find the first block in the stream that contains part of the buffer.
4971 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4973 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4975 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4977 blockNoInSequence--;
4981 * Start writing the buffer.
4983 * Here, I'm casting away the constness on the buffer variable
4984 * This is OK since we don't intend to modify that buffer.
4987 bufferWalker = (BYTE*)buffer;
4988 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4991 * Calculate how many bytes we can copy to this small block.
4993 bytesToWriteInBuffer =
4994 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
4997 * Calculate the offset of the small block in the small block file.
4999 offsetInBigBlockFile.s.HighPart = 0;
5000 offsetInBigBlockFile.s.LowPart =
5001 blockIndex * This->parentStorage->smallBlockSize;
5003 offsetInBigBlockFile.s.LowPart += offsetInBlock;
5006 * Write those bytes in the buffer to the small block file.
5008 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5009 offsetInBigBlockFile,
5010 bytesToWriteInBuffer,
5012 &bytesWrittenFromBigBlockFile);
5014 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5017 * Step to the next big block.
5019 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5020 bufferWalker += bytesToWriteInBuffer;
5021 size -= bytesToWriteInBuffer;
5022 *bytesWritten += bytesToWriteInBuffer;
5023 offsetInBlock = 0; /* There is no offset on the next block */
5029 /******************************************************************************
5030 * SmallBlockChainStream_Shrink
5032 * Shrinks this chain in the small block depot.
5034 BOOL SmallBlockChainStream_Shrink(
5035 SmallBlockChainStream* This,
5036 ULARGE_INTEGER newSize)
5038 ULONG blockIndex, extraBlock;
5042 numBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5044 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5047 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5050 * Go to the new end of chain
5052 while (count < numBlocks)
5054 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5059 * If the count is 0, we have a special case, the head of the chain was
5064 StgProperty chainProp;
5066 StorageImpl_ReadProperty(This->parentStorage,
5067 This->ownerPropertyIndex,
5070 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5072 StorageImpl_WriteProperty(This->parentStorage,
5073 This->ownerPropertyIndex,
5077 * We start freeing the chain at the head block.
5079 extraBlock = blockIndex;
5083 /* Get the next block before marking the new end */
5084 extraBlock = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5086 /* Mark the new end of chain */
5087 SmallBlockChainStream_SetNextBlockInChain(
5090 BLOCK_END_OF_CHAIN);
5094 * Mark the extra blocks as free
5096 while (extraBlock != BLOCK_END_OF_CHAIN)
5098 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, extraBlock);
5099 SmallBlockChainStream_FreeBlock(This, extraBlock);
5100 extraBlock = blockIndex;
5106 /******************************************************************************
5107 * SmallBlockChainStream_Enlarge
5109 * Grows this chain in the small block depot.
5111 BOOL SmallBlockChainStream_Enlarge(
5112 SmallBlockChainStream* This,
5113 ULARGE_INTEGER newSize)
5115 ULONG blockIndex, currentBlock;
5117 ULONG oldNumBlocks = 0;
5119 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5124 if (blockIndex == BLOCK_END_OF_CHAIN)
5127 StgProperty chainProp;
5129 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5132 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5134 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5137 blockIndex = chainProp.startingBlock;
5138 SmallBlockChainStream_SetNextBlockInChain(
5141 BLOCK_END_OF_CHAIN);
5144 currentBlock = blockIndex;
5147 * Figure out how many blocks are needed to contain this stream
5149 newNumBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5151 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5155 * Go to the current end of chain
5157 while (blockIndex != BLOCK_END_OF_CHAIN)
5160 currentBlock = blockIndex;
5161 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, currentBlock);
5165 * Add new blocks to the chain
5167 while (oldNumBlocks < newNumBlocks)
5169 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5170 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5172 SmallBlockChainStream_SetNextBlockInChain(
5175 BLOCK_END_OF_CHAIN);
5177 currentBlock = blockIndex;
5184 /******************************************************************************
5185 * SmallBlockChainStream_GetCount
5187 * Returns the number of blocks that comprises this chain.
5188 * This is not the size of this chain as the last block may not be full!
5190 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5195 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5197 while (blockIndex != BLOCK_END_OF_CHAIN)
5201 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5207 /******************************************************************************
5208 * SmallBlockChainStream_SetSize
5210 * Sets the size of this stream.
5211 * The file will grow if we grow the chain.
5213 * TODO: Free the actual blocks in the file when we shrink the chain.
5214 * Currently, the blocks are still in the file. So the file size
5215 * doesn't shrink even if we shrink streams.
5217 BOOL SmallBlockChainStream_SetSize(
5218 SmallBlockChainStream* This,
5219 ULARGE_INTEGER newSize)
5221 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5223 if (newSize.s.LowPart == size.s.LowPart)
5226 if (newSize.s.LowPart < size.s.LowPart)
5228 SmallBlockChainStream_Shrink(This, newSize);
5232 SmallBlockChainStream_Enlarge(This, newSize);
5238 /******************************************************************************
5239 * SmallBlockChainStream_GetSize
5241 * Returns the size of this chain.
5243 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5245 StgProperty chainProperty;
5247 StorageImpl_ReadProperty(
5248 This->parentStorage,
5249 This->ownerPropertyIndex,
5252 return chainProperty.size;
5255 /******************************************************************************
5256 * StgCreateDocfile [OLE32.144]
5258 HRESULT WINAPI StgCreateDocfile(
5262 IStorage **ppstgOpen)
5264 StorageImpl* newStorage = 0;
5265 HANDLE hFile = INVALID_HANDLE_VALUE;
5270 DWORD fileAttributes;
5271 WCHAR tempFileName[MAX_PATH];
5273 TRACE("(%s, %lx, %ld, %p)\n",
5274 debugstr_w(pwcsName), grfMode,
5275 reserved, ppstgOpen);
5278 * Validate the parameters
5281 return STG_E_INVALIDPOINTER;
5284 * Validate the STGM flags
5286 if ( FAILED( validateSTGM(grfMode) ))
5287 return STG_E_INVALIDFLAG;
5290 * Generate a unique name.
5294 WCHAR tempPath[MAX_PATH];
5295 WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5297 if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5298 return STG_E_INVALIDFLAG;
5299 if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5300 return STG_E_INVALIDFLAG;
5302 memset(tempPath, 0, sizeof(tempPath));
5303 memset(tempFileName, 0, sizeof(tempFileName));
5305 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5308 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5309 pwcsName = tempFileName;
5311 return STG_E_INSUFFICIENTMEMORY;
5313 creationMode = TRUNCATE_EXISTING;
5317 creationMode = GetCreationModeFromSTGM(grfMode);
5321 * Interpret the STGM value grfMode
5323 shareMode = GetShareModeFromSTGM(grfMode);
5324 accessMode = GetAccessModeFromSTGM(grfMode);
5326 if (grfMode & STGM_DELETEONRELEASE)
5327 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5329 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5331 if (grfMode & STGM_TRANSACTED)
5332 FIXME("Transacted mode not implemented.\n");
5335 * Initialize the "out" parameter.
5339 hFile = CreateFileW(pwcsName,
5347 if (hFile == INVALID_HANDLE_VALUE)
5353 * Allocate and initialize the new IStorage32object.
5355 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5357 if (newStorage == 0)
5358 return STG_E_INSUFFICIENTMEMORY;
5360 hr = StorageImpl_Construct(
5370 HeapFree(GetProcessHeap(), 0, newStorage);
5375 * Get an "out" pointer for the caller.
5377 hr = StorageBaseImpl_QueryInterface(
5378 (IStorage*)newStorage,
5379 (REFIID)&IID_IStorage,
5385 /******************************************************************************
5386 * StgOpenStorage [OLE32.148]
5388 HRESULT WINAPI StgOpenStorage(
5389 const OLECHAR *pwcsName,
5390 IStorage *pstgPriority,
5394 IStorage **ppstgOpen)
5396 StorageImpl* newStorage = 0;
5402 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5403 debugstr_w(pwcsName), pstgPriority, grfMode,
5404 snbExclude, reserved, ppstgOpen);
5407 * Perform a sanity check
5409 if (( pwcsName == 0) || (ppstgOpen == 0) )
5410 return STG_E_INVALIDPOINTER;
5413 * Validate the STGM flags
5415 if ( FAILED( validateSTGM(grfMode) ))
5416 return STG_E_INVALIDFLAG;
5419 * Interpret the STGM value grfMode
5421 shareMode = GetShareModeFromSTGM(grfMode);
5422 accessMode = GetAccessModeFromSTGM(grfMode);
5425 * Initialize the "out" parameter.
5429 hFile = CreateFileW( pwcsName,
5434 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5438 if (hFile==INVALID_HANDLE_VALUE)
5440 HRESULT hr = E_FAIL;
5441 DWORD last_error = GetLastError();
5445 case ERROR_FILE_NOT_FOUND:
5446 hr = STG_E_FILENOTFOUND;
5449 case ERROR_PATH_NOT_FOUND:
5450 hr = STG_E_PATHNOTFOUND;
5453 case ERROR_ACCESS_DENIED:
5454 case ERROR_WRITE_PROTECT:
5455 hr = STG_E_ACCESSDENIED;
5458 case ERROR_SHARING_VIOLATION:
5459 hr = STG_E_SHAREVIOLATION;
5470 * Allocate and initialize the new IStorage32object.
5472 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5474 if (newStorage == 0)
5475 return STG_E_INSUFFICIENTMEMORY;
5477 hr = StorageImpl_Construct(
5487 HeapFree(GetProcessHeap(), 0, newStorage);
5489 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5491 if(hr == STG_E_INVALIDHEADER)
5492 return STG_E_FILEALREADYEXISTS;
5497 * Get an "out" pointer for the caller.
5499 hr = StorageBaseImpl_QueryInterface(
5500 (IStorage*)newStorage,
5501 (REFIID)&IID_IStorage,
5507 /******************************************************************************
5508 * StgCreateDocfileOnILockBytes [OLE32.145]
5510 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5514 IStorage** ppstgOpen)
5516 StorageImpl* newStorage = 0;
5520 * Validate the parameters
5522 if ((ppstgOpen == 0) || (plkbyt == 0))
5523 return STG_E_INVALIDPOINTER;
5526 * Allocate and initialize the new IStorage object.
5528 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5530 if (newStorage == 0)
5531 return STG_E_INSUFFICIENTMEMORY;
5533 hr = StorageImpl_Construct(
5543 HeapFree(GetProcessHeap(), 0, newStorage);
5548 * Get an "out" pointer for the caller.
5550 hr = StorageBaseImpl_QueryInterface(
5551 (IStorage*)newStorage,
5552 (REFIID)&IID_IStorage,
5558 /******************************************************************************
5559 * StgOpenStorageOnILockBytes [OLE32.149]
5560 * StgOpenStorageOnILockBytes [STORAGE.4]
5562 HRESULT WINAPI StgOpenStorageOnILockBytes(
5564 IStorage *pstgPriority,
5568 IStorage **ppstgOpen)
5570 StorageImpl* newStorage = 0;
5574 * Perform a sanity check
5576 if ((plkbyt == 0) || (ppstgOpen == 0))
5577 return STG_E_INVALIDPOINTER;
5580 * Validate the STGM flags
5582 if ( FAILED( validateSTGM(grfMode) ))
5583 return STG_E_INVALIDFLAG;
5586 * Initialize the "out" parameter.
5591 * Allocate and initialize the new IStorage object.
5593 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5595 if (newStorage == 0)
5596 return STG_E_INSUFFICIENTMEMORY;
5598 hr = StorageImpl_Construct(
5608 HeapFree(GetProcessHeap(), 0, newStorage);
5613 * Get an "out" pointer for the caller.
5615 hr = StorageBaseImpl_QueryInterface(
5616 (IStorage*)newStorage,
5617 (REFIID)&IID_IStorage,
5623 /******************************************************************************
5624 * StgSetTimes [ole32.150]
5625 * StgSetTimes [OLE32.150]
5626 * StgSetTimes [STORAGE.7]
5630 HRESULT WINAPI StgSetTimes(WCHAR * str, FILETIME * a, FILETIME * b, FILETIME *c )
5633 FIXME("(%p, %p, %p, %p),stub!\n", str, a, b, c);
5637 /******************************************************************************
5638 * StgIsStorageILockBytes [OLE32.147]
5639 * StgIsStorageILockBytes [STORAGE.6]
5641 * Determines if the ILockBytes contains a storage object.
5643 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5646 ULARGE_INTEGER offset;
5648 offset.s.HighPart = 0;
5649 offset.s.LowPart = 0;
5651 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5653 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5659 /******************************************************************************
5660 * WriteClassStg [OLE32.158]
5662 * This method will store the specified CLSID in the specified storage object
5664 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5670 hRes = IStorage_SetClass(pStg, rclsid);
5675 /***********************************************************************
5676 * ReadClassStg (OLE32.134)
5678 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5680 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5690 * read a STATSTG structure (contains the clsid) from the storage
5692 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5695 *pclsid=pstatstg.clsid;
5700 /***********************************************************************
5701 * OleLoadFromStream (OLE32.113)
5703 * This function loads an object from stream
5705 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5709 LPPERSISTSTREAM xstm;
5711 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
5713 res=ReadClassStm(pStm,&clsid);
5714 if (!SUCCEEDED(res))
5716 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5717 if (!SUCCEEDED(res))
5719 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
5720 if (!SUCCEEDED(res)) {
5721 IUnknown_Release((IUnknown*)*ppvObj);
5724 res=IPersistStream_Load(xstm,pStm);
5725 IPersistStream_Release(xstm);
5726 /* FIXME: all refcounts ok at this point? I think they should be:
5729 * xstm : 0 (released)
5734 /***********************************************************************
5735 * OleSaveToStream (OLE32.125)
5737 * This function saves an object with the IPersistStream interface on it
5738 * to the specified stream.
5740 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5746 TRACE("(%p,%p)\n",pPStm,pStm);
5748 res=IPersistStream_GetClassID(pPStm,&clsid);
5750 if (SUCCEEDED(res)){
5752 res=WriteClassStm(pStm,&clsid);
5756 res=IPersistStream_Save(pPStm,pStm,TRUE);
5759 TRACE("Finished Save\n");
5763 /****************************************************************************
5764 * This method validate a STGM parameter that can contain the values below
5766 * STGM_DIRECT 0x00000000
5767 * STGM_TRANSACTED 0x00010000
5768 * STGM_SIMPLE 0x08000000
5770 * STGM_READ 0x00000000
5771 * STGM_WRITE 0x00000001
5772 * STGM_READWRITE 0x00000002
5774 * STGM_SHARE_DENY_NONE 0x00000040
5775 * STGM_SHARE_DENY_READ 0x00000030
5776 * STGM_SHARE_DENY_WRITE 0x00000020
5777 * STGM_SHARE_EXCLUSIVE 0x00000010
5779 * STGM_PRIORITY 0x00040000
5780 * STGM_DELETEONRELEASE 0x04000000
5782 * STGM_CREATE 0x00001000
5783 * STGM_CONVERT 0x00020000
5784 * STGM_FAILIFTHERE 0x00000000
5786 * STGM_NOSCRATCH 0x00100000
5787 * STGM_NOSNAPSHOT 0x00200000
5789 static HRESULT validateSTGM(DWORD stgm)
5791 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5792 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5793 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5795 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5796 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5797 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5799 BOOL bSTGM_SHARE_DENY_NONE =
5800 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5802 BOOL bSTGM_SHARE_DENY_READ =
5803 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5805 BOOL bSTGM_SHARE_DENY_WRITE =
5806 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5808 BOOL bSTGM_SHARE_EXCLUSIVE =
5809 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5811 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5812 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5814 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5815 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5818 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5820 if ( ! bSTGM_DIRECT )
5821 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5825 * STGM_WRITE | STGM_READWRITE | STGM_READ
5828 if( bSTGM_WRITE && bSTGM_READWRITE )
5832 * STGM_SHARE_DENY_NONE | others
5833 * (I assume here that DENY_READ implies DENY_WRITE)
5835 if ( bSTGM_SHARE_DENY_NONE )
5836 if ( bSTGM_SHARE_DENY_READ ||
5837 bSTGM_SHARE_DENY_WRITE ||
5838 bSTGM_SHARE_EXCLUSIVE)
5842 * STGM_CREATE | STGM_CONVERT
5843 * if both are false, STGM_FAILIFTHERE is set to TRUE
5845 if ( bSTGM_CREATE && bSTGM_CONVERT )
5849 * STGM_NOSCRATCH requires STGM_TRANSACTED
5851 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
5855 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
5856 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5858 if (bSTGM_NOSNAPSHOT)
5860 if ( ! ( bSTGM_TRANSACTED &&
5861 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
5868 /****************************************************************************
5869 * GetShareModeFromSTGM
5871 * This method will return a share mode flag from a STGM value.
5872 * The STGM value is assumed valid.
5874 static DWORD GetShareModeFromSTGM(DWORD stgm)
5876 DWORD dwShareMode = 0;
5877 BOOL bSTGM_SHARE_DENY_NONE =
5878 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5880 BOOL bSTGM_SHARE_DENY_READ =
5881 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5883 BOOL bSTGM_SHARE_DENY_WRITE =
5884 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5886 BOOL bSTGM_SHARE_EXCLUSIVE =
5887 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5889 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
5892 if (bSTGM_SHARE_DENY_NONE)
5893 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5895 if (bSTGM_SHARE_DENY_WRITE)
5896 dwShareMode = FILE_SHARE_READ;
5901 /****************************************************************************
5902 * GetAccessModeFromSTGM
5904 * This method will return an access mode flag from a STGM value.
5905 * The STGM value is assumed valid.
5907 static DWORD GetAccessModeFromSTGM(DWORD stgm)
5909 DWORD dwDesiredAccess = GENERIC_READ;
5910 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5911 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5912 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5915 dwDesiredAccess = GENERIC_READ;
5918 dwDesiredAccess |= GENERIC_WRITE;
5920 if (bSTGM_READWRITE)
5921 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
5923 return dwDesiredAccess;
5926 /****************************************************************************
5927 * GetCreationModeFromSTGM
5929 * This method will return a creation mode flag from a STGM value.
5930 * The STGM value is assumed valid.
5932 static DWORD GetCreationModeFromSTGM(DWORD stgm)
5934 if ( stgm & STGM_CREATE)
5935 return CREATE_ALWAYS;
5936 if (stgm & STGM_CONVERT) {
5937 FIXME("STGM_CONVERT not implemented!\n");
5940 /* All other cases */
5941 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
5942 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
5947 /*************************************************************************
5948 * OLECONVERT_LoadOLE10 [Internal]
5950 * Loads the OLE10 STREAM to memory
5953 * pOleStream [I] The OLESTREAM
5954 * pData [I] Data Structure for the OLESTREAM Data
5958 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
5959 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
5962 * This function is used by OleConvertOLESTREAMToIStorage only.
5964 * Memory allocated for pData must be freed by the caller
5966 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
5969 HRESULT hRes = S_OK;
5973 pData->pData = NULL;
5974 pData->pstrOleObjFileName = (CHAR *) NULL;
5976 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
5979 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
5980 if(dwSize != sizeof(pData->dwOleID))
5982 hRes = CONVERT10_E_OLESTREAM_GET;
5984 else if(pData->dwOleID != OLESTREAM_ID)
5986 hRes = CONVERT10_E_OLESTREAM_FMT;
5997 /* Get the TypeID...more info needed for this field */
5998 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
5999 if(dwSize != sizeof(pData->dwTypeID))
6001 hRes = CONVERT10_E_OLESTREAM_GET;
6006 if(pData->dwTypeID != 0)
6008 /* Get the lenght of the OleTypeName */
6009 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6010 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6012 hRes = CONVERT10_E_OLESTREAM_GET;
6017 if(pData->dwOleTypeNameLength > 0)
6019 /* Get the OleTypeName */
6020 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6021 if(dwSize != pData->dwOleTypeNameLength)
6023 hRes = CONVERT10_E_OLESTREAM_GET;
6029 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6030 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6032 hRes = CONVERT10_E_OLESTREAM_GET;
6036 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6037 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6038 pData->pstrOleObjFileName = (CHAR *)malloc(pData->dwOleObjFileNameLength);
6039 if(pData->pstrOleObjFileName)
6041 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6042 if(dwSize != pData->dwOleObjFileNameLength)
6044 hRes = CONVERT10_E_OLESTREAM_GET;
6048 hRes = CONVERT10_E_OLESTREAM_GET;
6053 /* Get the Width of the Metafile */
6054 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6055 if(dwSize != sizeof(pData->dwMetaFileWidth))
6057 hRes = CONVERT10_E_OLESTREAM_GET;
6061 /* Get the Height of the Metafile */
6062 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6063 if(dwSize != sizeof(pData->dwMetaFileHeight))
6065 hRes = CONVERT10_E_OLESTREAM_GET;
6071 /* Get the Lenght of the Data */
6072 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6073 if(dwSize != sizeof(pData->dwDataLength))
6075 hRes = CONVERT10_E_OLESTREAM_GET;
6079 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6081 if(!bStrem1) /* if it is a second OLE stream data */
6083 pData->dwDataLength -= 8;
6084 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6085 if(dwSize != sizeof(pData->strUnknown))
6087 hRes = CONVERT10_E_OLESTREAM_GET;
6093 if(pData->dwDataLength > 0)
6095 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6097 /* Get Data (ex. IStorage, Metafile, or BMP) */
6100 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6101 if(dwSize != pData->dwDataLength)
6103 hRes = CONVERT10_E_OLESTREAM_GET;
6108 hRes = CONVERT10_E_OLESTREAM_GET;
6117 /*************************************************************************
6118 * OLECONVERT_SaveOLE10 [Internal]
6120 * Saves the OLE10 STREAM From memory
6123 * pData [I] Data Structure for the OLESTREAM Data
6124 * pOleStream [I] The OLESTREAM to save
6128 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6131 * This function is used by OleConvertIStorageToOLESTREAM only.
6134 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6137 HRESULT hRes = S_OK;
6141 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6142 if(dwSize != sizeof(pData->dwOleID))
6144 hRes = CONVERT10_E_OLESTREAM_PUT;
6149 /* Set the TypeID */
6150 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6151 if(dwSize != sizeof(pData->dwTypeID))
6153 hRes = CONVERT10_E_OLESTREAM_PUT;
6157 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6159 /* Set the Lenght of the OleTypeName */
6160 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6161 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6163 hRes = CONVERT10_E_OLESTREAM_PUT;
6168 if(pData->dwOleTypeNameLength > 0)
6170 /* Set the OleTypeName */
6171 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6172 if(dwSize != pData->dwOleTypeNameLength)
6174 hRes = CONVERT10_E_OLESTREAM_PUT;
6181 /* Set the width of the Metafile */
6182 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6183 if(dwSize != sizeof(pData->dwMetaFileWidth))
6185 hRes = CONVERT10_E_OLESTREAM_PUT;
6191 /* Set the height of the Metafile */
6192 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6193 if(dwSize != sizeof(pData->dwMetaFileHeight))
6195 hRes = CONVERT10_E_OLESTREAM_PUT;
6201 /* Set the lenght of the Data */
6202 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6203 if(dwSize != sizeof(pData->dwDataLength))
6205 hRes = CONVERT10_E_OLESTREAM_PUT;
6211 if(pData->dwDataLength > 0)
6213 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6214 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6215 if(dwSize != pData->dwDataLength)
6217 hRes = CONVERT10_E_OLESTREAM_PUT;
6225 /*************************************************************************
6226 * OLECONVERT_GetOLE20FromOLE10[Internal]
6228 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6229 * opens it, and copies the content to the dest IStorage for
6230 * OleConvertOLESTREAMToIStorage
6234 * pDestStorage [I] The IStorage to copy the data to
6235 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6236 * nBufferLength [I] The size of the buffer
6245 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6249 IStorage *pTempStorage;
6250 DWORD dwNumOfBytesWritten;
6251 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6252 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6254 /* Create a temp File */
6255 GetTempPathW(MAX_PATH, wstrTempDir);
6256 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6257 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6259 if(hFile != INVALID_HANDLE_VALUE)
6261 /* Write IStorage Data to File */
6262 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6265 /* Open and copy temp storage to the Dest Storage */
6266 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6269 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6270 StorageBaseImpl_Release(pTempStorage);
6272 DeleteFileW(wstrTempFile);
6277 /*************************************************************************
6278 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6280 * Saves the OLE10 STREAM From memory
6283 * pStorage [I] The Src IStorage to copy
6284 * pData [I] The Dest Memory to write to.
6287 * The size in bytes allocated for pData
6290 * Memory allocated for pData must be freed by the caller
6292 * Used by OleConvertIStorageToOLESTREAM only.
6295 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6299 DWORD nDataLength = 0;
6300 IStorage *pTempStorage;
6301 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6302 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6306 /* Create temp Storage */
6307 GetTempPathW(MAX_PATH, wstrTempDir);
6308 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6309 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6313 /* Copy Src Storage to the Temp Storage */
6314 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6315 StorageBaseImpl_Release(pTempStorage);
6317 /* Open Temp Storage as a file and copy to memory */
6318 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6319 if(hFile != INVALID_HANDLE_VALUE)
6321 nDataLength = GetFileSize(hFile, NULL);
6322 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6323 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6326 DeleteFileW(wstrTempFile);
6331 /*************************************************************************
6332 * OLECONVERT_CreateOleStream [Internal]
6334 * Creates the "\001OLE" stream in the IStorage if neccessary.
6337 * pStorage [I] Dest storage to create the stream in
6343 * This function is used by OleConvertOLESTREAMToIStorage only.
6345 * This stream is still unknown, MS Word seems to have extra data
6346 * but since the data is stored in the OLESTREAM there should be
6347 * no need to recreate the stream. If the stream is manually
6348 * deleted it will create it with this default data.
6351 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6355 WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6356 BYTE pOleStreamHeader [] =
6358 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6359 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6360 0x00, 0x00, 0x00, 0x00
6363 /* Create stream if not present */
6364 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6365 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6369 /* Write default Data */
6370 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6371 IStream_Release(pStream);
6376 /*************************************************************************
6377 * OLECONVERT_CreateCompObjStream [Internal]
6379 * Creates a "\001CompObj" is the destination IStorage if necessary.
6382 * pStorage [I] The dest IStorage to create the CompObj Stream
6384 * strOleTypeName [I] The ProgID
6388 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6391 * This function is used by OleConvertOLESTREAMToIStorage only.
6393 * The stream data is stored in the OLESTREAM and there should be
6394 * no need to recreate the stream. If the stream is manually
6395 * deleted it will attempt to create it by querying the registry.
6399 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6402 HRESULT hStorageRes, hRes = S_OK;
6403 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6404 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6406 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6407 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6409 /* Initialize the CompObj structure */
6410 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6411 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6412 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6415 /* Create a CompObj stream if it doesn't exist */
6416 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6417 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6418 if(hStorageRes == S_OK)
6420 /* copy the OleTypeName to the compobj struct */
6421 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6422 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6424 /* copy the OleTypeName to the compobj struct */
6425 /* Note: in the test made, these were Identical */
6426 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6427 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6430 hRes = CLSIDFromProgID16(IStorageCompObj.strProgIDName, &(IStorageCompObj.clsid));
6436 /* Get the CLSID Default Name from the Registry */
6437 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6438 if(hErr == ERROR_SUCCESS)
6440 char strTemp[OLESTREAM_MAX_STR_LEN];
6441 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6442 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6443 if(hErr == ERROR_SUCCESS)
6445 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6451 /* Write CompObj Structure to stream */
6452 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6454 WriteClassStm(pStream,&(IStorageCompObj.clsid));
6456 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6457 if(IStorageCompObj.dwCLSIDNameLength > 0)
6459 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6461 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6462 if(IStorageCompObj.dwOleTypeNameLength > 0)
6464 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6466 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6467 if(IStorageCompObj.dwProgIDNameLength > 0)
6469 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6471 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6472 IStream_Release(pStream);
6478 /*************************************************************************
6479 * OLECONVERT_CreateOlePresStream[Internal]
6481 * Creates the "\002OlePres000" Stream with the Metafile data
6484 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6485 * dwExtentX [I] Width of the Metafile
6486 * dwExtentY [I] Height of the Metafile
6487 * pData [I] Metafile data
6488 * dwDataLength [I] Size of the Metafile data
6492 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6495 * This function is used by OleConvertOLESTREAMToIStorage only.
6498 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6502 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6503 BYTE pOlePresStreamHeader [] =
6505 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6506 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6507 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6508 0x00, 0x00, 0x00, 0x00
6511 BYTE pOlePresStreamHeaderEmpty [] =
6513 0x00, 0x00, 0x00, 0x00,
6514 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6515 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6516 0x00, 0x00, 0x00, 0x00
6519 /* Create the OlePres000 Stream */
6520 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6521 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6526 OLECONVERT_ISTORAGE_OLEPRES OlePres;
6528 memset(&OlePres, 0, sizeof(OlePres));
6529 /* Do we have any metafile data to save */
6530 if(dwDataLength > 0)
6532 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6533 nHeaderSize = sizeof(pOlePresStreamHeader);
6537 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
6538 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
6540 /* Set width and height of the metafile */
6541 OlePres.dwExtentX = dwExtentX;
6542 OlePres.dwExtentY = -dwExtentY;
6544 /* Set Data and Lenght */
6545 if(dwDataLength > sizeof(METAFILEPICT16))
6547 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
6548 OlePres.pData = &(pData[8]);
6550 /* Save OlePres000 Data to Stream */
6551 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
6552 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
6553 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
6554 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
6555 if(OlePres.dwSize > 0)
6557 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
6559 IStream_Release(pStream);
6563 /*************************************************************************
6564 * OLECONVERT_CreateOle10NativeStream [Internal]
6566 * Creates the "\001Ole10Native" Stream (should contain a BMP)
6569 * pStorage [I] Dest storage to create the stream in
6570 * pData [I] Ole10 Native Data (ex. bmp)
6571 * dwDataLength [I] Size of the Ole10 Native Data
6577 * This function is used by OleConvertOLESTREAMToIStorage only.
6579 * Might need to verify the data and return appropriate error message
6582 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
6586 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6588 /* Create the Ole10Native Stream */
6589 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6590 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6594 /* Write info to stream */
6595 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
6596 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
6597 IStream_Release(pStream);
6602 /*************************************************************************
6603 * OLECONVERT_GetOLE10ProgID [Internal]
6605 * Finds the ProgID (or OleTypeID) from the IStorage
6608 * pStorage [I] The Src IStorage to get the ProgID
6609 * strProgID [I] the ProgID string to get
6610 * dwSize [I] the size of the string
6614 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6617 * This function is used by OleConvertIStorageToOLESTREAM only.
6621 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
6625 LARGE_INTEGER iSeekPos;
6626 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
6627 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6629 /* Open the CompObj Stream */
6630 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6631 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6635 /*Get the OleType from the CompObj Stream */
6636 iSeekPos.s.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
6637 iSeekPos.s.HighPart = 0;
6639 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6640 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
6641 iSeekPos.s.LowPart = CompObj.dwCLSIDNameLength;
6642 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6643 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
6644 iSeekPos.s.LowPart = CompObj.dwOleTypeNameLength;
6645 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6647 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
6650 IStream_Read(pStream, strProgID, *dwSize, NULL);
6652 IStream_Release(pStream);
6657 LPOLESTR wstrProgID;
6659 /* Get the OleType from the registry */
6660 REFCLSID clsid = &(stat.clsid);
6661 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
6662 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
6665 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
6672 /*************************************************************************
6673 * OLECONVERT_GetOle10PresData [Internal]
6675 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
6678 * pStorage [I] Src IStroage
6679 * pOleStream [I] Dest OleStream Mem Struct
6685 * This function is used by OleConvertIStorageToOLESTREAM only.
6687 * Memory allocated for pData must be freed by the caller
6691 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6696 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6698 /* Initialize Default data for OLESTREAM */
6699 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6700 pOleStreamData[0].dwTypeID = 2;
6701 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6702 pOleStreamData[1].dwTypeID = 0;
6703 pOleStreamData[0].dwMetaFileWidth = 0;
6704 pOleStreamData[0].dwMetaFileHeight = 0;
6705 pOleStreamData[0].pData = NULL;
6706 pOleStreamData[1].pData = NULL;
6708 /* Open Ole10Native Stream */
6709 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6710 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6714 /* Read Size and Data */
6715 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
6716 if(pOleStreamData->dwDataLength > 0)
6718 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
6719 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
6721 IStream_Release(pStream);
6727 /*************************************************************************
6728 * OLECONVERT_GetOle20PresData[Internal]
6730 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
6733 * pStorage [I] Src IStroage
6734 * pOleStreamData [I] Dest OleStream Mem Struct
6740 * This function is used by OleConvertIStorageToOLESTREAM only.
6742 * Memory allocated for pData must be freed by the caller
6744 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6748 OLECONVERT_ISTORAGE_OLEPRES olePress;
6749 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6751 /* Initialize Default data for OLESTREAM */
6752 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6753 pOleStreamData[0].dwTypeID = 2;
6754 pOleStreamData[0].dwMetaFileWidth = 0;
6755 pOleStreamData[0].dwMetaFileHeight = 0;
6756 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
6757 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6758 pOleStreamData[1].dwTypeID = 0;
6759 pOleStreamData[1].dwOleTypeNameLength = 0;
6760 pOleStreamData[1].strOleTypeName[0] = 0;
6761 pOleStreamData[1].dwMetaFileWidth = 0;
6762 pOleStreamData[1].dwMetaFileHeight = 0;
6763 pOleStreamData[1].pData = NULL;
6764 pOleStreamData[1].dwDataLength = 0;
6767 /* Open OlePress000 stream */
6768 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6769 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6772 LARGE_INTEGER iSeekPos;
6773 METAFILEPICT16 MetaFilePict;
6774 char strMetafilePictName[] = "METAFILEPICT";
6776 /* Set the TypeID for a Metafile */
6777 pOleStreamData[1].dwTypeID = 5;
6779 /* Set the OleTypeName to Metafile */
6780 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
6781 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
6783 iSeekPos.s.HighPart = 0;
6784 iSeekPos.s.LowPart = sizeof(olePress.byUnknown1);
6786 /* Get Presentation Data */
6787 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6788 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
6789 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
6790 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
6792 /*Set width and Height */
6793 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
6794 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
6795 if(olePress.dwSize > 0)
6798 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
6800 /* Set MetaFilePict struct */
6801 MetaFilePict.mm = 8;
6802 MetaFilePict.xExt = olePress.dwExtentX;
6803 MetaFilePict.yExt = olePress.dwExtentY;
6804 MetaFilePict.hMF = 0;
6806 /* Get Metafile Data */
6807 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
6808 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
6809 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
6811 IStream_Release(pStream);
6815 /*************************************************************************
6816 * OleConvertOLESTREAMToIStorage [OLE32.87]
6821 * DVTARGETDEVICE paramenter is not handled
6822 * Still unsure of some mem fields for OLE 10 Stream
6823 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6824 * and "\001OLE" streams
6827 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
6828 LPOLESTREAM pOleStream,
6830 const DVTARGETDEVICE* ptd)
6834 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
6836 memset(pOleStreamData, 0, sizeof(pOleStreamData));
6840 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
6843 if(pstg == NULL || pOleStream == NULL)
6845 hRes = E_INVALIDARG;
6850 /* Load the OLESTREAM to Memory */
6851 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
6856 /* Load the OLESTREAM to Memory (part 2)*/
6857 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
6863 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
6865 /* Do we have the IStorage Data in the OLESTREAM */
6866 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
6868 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6869 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
6873 /* It must be an original OLE 1.0 source */
6874 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6879 /* It must be an original OLE 1.0 source */
6880 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6883 /* Create CompObj Stream if necessary */
6884 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
6887 /*Create the Ole Stream if necessary */
6888 OLECONVERT_CreateOleStream(pstg);
6893 /* Free allocated memory */
6894 for(i=0; i < 2; i++)
6896 if(pOleStreamData[i].pData != NULL)
6898 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
6900 if(pOleStreamData[i].pstrOleObjFileName != NULL)
6902 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
6903 pOleStreamData[i].pstrOleObjFileName = NULL;
6909 /*************************************************************************
6910 * OleConvertIStorageToOLESTREAM [OLE32.85]
6917 * Still unsure of some mem fields for OLE 10 Stream
6918 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6919 * and "\001OLE" streams.
6922 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
6924 LPOLESTREAM pOleStream)
6927 HRESULT hRes = S_OK;
6929 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
6930 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6933 memset(pOleStreamData, 0, sizeof(pOleStreamData));
6935 if(pstg == NULL || pOleStream == NULL)
6937 hRes = E_INVALIDARG;
6941 /* Get the ProgID */
6942 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
6943 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
6947 /*Was it originaly Ole10 */
6948 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
6951 IStream_Release(pStream);
6952 /*Get Presentation Data for Ole10Native */
6953 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
6957 /*Get Presentation Data (OLE20)*/
6958 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
6961 /* Save OLESTREAM */
6962 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
6965 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
6970 /* Free allocated memory */
6971 for(i=0; i < 2; i++)
6973 if(pOleStreamData[i].pData != NULL)
6975 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
6982 /***********************************************************************
6983 * GetConvertStg (OLE32.68)
6985 HRESULT WINAPI GetConvertStg(LPGUID guid) {
6986 FIXME("(%s), unimplemented stub!\n",debugstr_guid(guid));