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 "wine/winestring.h"
22 #include "debugtools.h"
24 #include "storage32.h"
25 #include "ole2.h" /* For Write/ReadClassStm */
28 #include "wine/wingdi16.h"
30 DEFAULT_DEBUG_CHANNEL(storage);
35 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
36 #define OLESTREAM_ID 0x501
37 #define OLESTREAM_MAX_STR_LEN 255
39 static const char rootPropertyName[] = "Root Entry";
42 /* OLESTREAM memory structure to use for Get and Put Routines */
43 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
48 DWORD dwOleTypeNameLength;
49 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
50 CHAR *pstrOleObjFileName;
51 DWORD dwOleObjFileNameLength;
52 DWORD dwMetaFileWidth;
53 DWORD dwMetaFileHeight;
54 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
57 }OLECONVERT_OLESTREAM_DATA;
59 /* CompObj Stream structure */
60 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
65 DWORD dwCLSIDNameLength;
66 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
67 DWORD dwOleTypeNameLength;
68 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
69 DWORD dwProgIDNameLength;
70 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
72 }OLECONVERT_ISTORAGE_COMPOBJ;
75 /* Ole Presention Stream structure */
76 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
84 }OLECONVERT_ISTORAGE_OLEPRES;
88 /***********************************************************************
89 * Forward declaration of internal functions used by the method DestroyElement
91 static HRESULT deleteStorageProperty(
92 StorageImpl *parentStorage,
93 ULONG foundPropertyIndexToDelete,
94 StgProperty propertyToDelete);
96 static HRESULT deleteStreamProperty(
97 StorageImpl *parentStorage,
98 ULONG foundPropertyIndexToDelete,
99 StgProperty propertyToDelete);
101 static HRESULT findPlaceholder(
102 StorageImpl *storage,
103 ULONG propertyIndexToStore,
104 ULONG storagePropertyIndex,
107 static HRESULT adjustPropertyChain(
109 StgProperty propertyToDelete,
110 StgProperty parentProperty,
111 ULONG parentPropertyId,
114 /***********************************************************************
115 * Declaration of the functions used to manipulate StgProperty
118 static ULONG getFreeProperty(
119 StorageImpl *storage);
121 static void updatePropertyChain(
122 StorageImpl *storage,
123 ULONG newPropertyIndex,
124 StgProperty newProperty);
126 static LONG propertyNameCmp(
127 OLECHAR *newProperty,
128 OLECHAR *currentProperty);
131 /***********************************************************************
132 * Declaration of miscellaneous functions...
134 static HRESULT validateSTGM(DWORD stgmValue);
136 static DWORD GetShareModeFromSTGM(DWORD stgm);
137 static DWORD GetAccessModeFromSTGM(DWORD stgm);
138 static DWORD GetCreationModeFromSTGM(DWORD stgm);
141 * Virtual function table for the IStorage32Impl class.
143 static ICOM_VTABLE(IStorage) Storage32Impl_Vtbl =
145 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
146 StorageBaseImpl_QueryInterface,
147 StorageBaseImpl_AddRef,
148 StorageBaseImpl_Release,
149 StorageBaseImpl_CreateStream,
150 StorageBaseImpl_OpenStream,
151 StorageImpl_CreateStorage,
152 StorageBaseImpl_OpenStorage,
154 StorageImpl_MoveElementTo,
157 StorageBaseImpl_EnumElements,
158 StorageImpl_DestroyElement,
159 StorageBaseImpl_RenameElement,
160 StorageImpl_SetElementTimes,
161 StorageBaseImpl_SetClass,
162 StorageImpl_SetStateBits,
167 * Virtual function table for the Storage32InternalImpl class.
169 static ICOM_VTABLE(IStorage) Storage32InternalImpl_Vtbl =
171 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
172 StorageBaseImpl_QueryInterface,
173 StorageBaseImpl_AddRef,
174 StorageBaseImpl_Release,
175 StorageBaseImpl_CreateStream,
176 StorageBaseImpl_OpenStream,
177 StorageImpl_CreateStorage,
178 StorageBaseImpl_OpenStorage,
180 StorageImpl_MoveElementTo,
181 StorageInternalImpl_Commit,
182 StorageInternalImpl_Revert,
183 StorageBaseImpl_EnumElements,
184 StorageImpl_DestroyElement,
185 StorageBaseImpl_RenameElement,
186 StorageImpl_SetElementTimes,
187 StorageBaseImpl_SetClass,
188 StorageImpl_SetStateBits,
193 * Virtual function table for the IEnumSTATSTGImpl class.
195 static ICOM_VTABLE(IEnumSTATSTG) IEnumSTATSTGImpl_Vtbl =
197 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
198 IEnumSTATSTGImpl_QueryInterface,
199 IEnumSTATSTGImpl_AddRef,
200 IEnumSTATSTGImpl_Release,
201 IEnumSTATSTGImpl_Next,
202 IEnumSTATSTGImpl_Skip,
203 IEnumSTATSTGImpl_Reset,
204 IEnumSTATSTGImpl_Clone
211 /************************************************************************
212 ** Storage32BaseImpl implementatiion
215 /************************************************************************
216 * Storage32BaseImpl_QueryInterface (IUnknown)
218 * This method implements the common QueryInterface for all IStorage32
219 * implementations contained in this file.
221 * See Windows documentation for more details on IUnknown methods.
223 HRESULT WINAPI StorageBaseImpl_QueryInterface(
228 ICOM_THIS(StorageBaseImpl,iface);
230 * Perform a sanity check on the parameters.
232 if ( (This==0) || (ppvObject==0) )
236 * Initialize the return parameter.
241 * Compare the riid with the interface IDs implemented by this object.
243 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
245 *ppvObject = (IStorage*)This;
247 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
249 *ppvObject = (IStorage*)This;
253 * Check that we obtained an interface.
256 return E_NOINTERFACE;
259 * Query Interface always increases the reference count by one when it is
262 StorageBaseImpl_AddRef(iface);
267 /************************************************************************
268 * Storage32BaseImpl_AddRef (IUnknown)
270 * This method implements the common AddRef for all IStorage32
271 * implementations contained in this file.
273 * See Windows documentation for more details on IUnknown methods.
275 ULONG WINAPI StorageBaseImpl_AddRef(
278 ICOM_THIS(StorageBaseImpl,iface);
284 /************************************************************************
285 * Storage32BaseImpl_Release (IUnknown)
287 * This method implements the common Release for all IStorage32
288 * implementations contained in this file.
290 * See Windows documentation for more details on IUnknown methods.
292 ULONG WINAPI StorageBaseImpl_Release(
295 ICOM_THIS(StorageBaseImpl,iface);
297 * Decrease the reference count on this object.
302 * If the reference count goes down to 0, perform suicide.
307 * Since we are using a system of base-classes, we want to call the
308 * destructor of the appropriate derived class. To do this, we are
309 * using virtual functions to implement the destructor.
311 This->v_destructor(This);
319 /************************************************************************
320 * Storage32BaseImpl_OpenStream (IStorage)
322 * This method will open the specified stream object from the current storage.
324 * See Windows documentation for more details on IStorage methods.
326 HRESULT WINAPI StorageBaseImpl_OpenStream(
328 const OLECHAR* pwcsName, /* [string][in] */
329 void* reserved1, /* [unique][in] */
330 DWORD grfMode, /* [in] */
331 DWORD reserved2, /* [in] */
332 IStream** ppstm) /* [out] */
334 ICOM_THIS(StorageBaseImpl,iface);
335 IEnumSTATSTGImpl* propertyEnumeration;
336 StgStreamImpl* newStream;
337 StgProperty currentProperty;
338 ULONG foundPropertyIndex;
340 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
341 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
344 * Perform a sanity check on the parameters.
346 if ( (pwcsName==NULL) || (ppstm==0) )
350 * Initialize the out parameter
355 * Validate the STGM flags
357 if ( FAILED( validateSTGM(grfMode) ))
358 return STG_E_INVALIDFLAG;
363 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
364 (grfMode & STGM_DELETEONRELEASE) ||
365 (grfMode & STGM_TRANSACTED) )
366 return STG_E_INVALIDFUNCTION;
369 * Create a property enumeration to search the properties
371 propertyEnumeration = IEnumSTATSTGImpl_Construct(
372 This->ancestorStorage,
373 This->rootPropertySetIndex);
376 * Search the enumeration for the property with the given name
378 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
384 * Delete the property enumeration since we don't need it anymore
386 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
389 * If it was found, construct the stream object and return a pointer to it.
391 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
392 (currentProperty.propertyType==PROPTYPE_STREAM) )
394 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
398 newStream->grfMode = grfMode;
399 *ppstm = (IStream*)newStream;
402 * Since we are returning a pointer to the interface, we have to
403 * nail down the reference.
405 StgStreamImpl_AddRef(*ppstm);
410 return E_OUTOFMEMORY;
413 return STG_E_FILENOTFOUND;
416 /************************************************************************
417 * Storage32BaseImpl_OpenStorage (IStorage)
419 * This method will open a new storage object from the current storage.
421 * See Windows documentation for more details on IStorage methods.
423 HRESULT WINAPI StorageBaseImpl_OpenStorage(
425 const OLECHAR* pwcsName, /* [string][unique][in] */
426 IStorage* pstgPriority, /* [unique][in] */
427 DWORD grfMode, /* [in] */
428 SNB snbExclude, /* [unique][in] */
429 DWORD reserved, /* [in] */
430 IStorage** ppstg) /* [out] */
432 ICOM_THIS(StorageBaseImpl,iface);
433 StorageInternalImpl* newStorage;
434 IEnumSTATSTGImpl* propertyEnumeration;
435 StgProperty currentProperty;
436 ULONG foundPropertyIndex;
438 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
439 iface, debugstr_w(pwcsName), pstgPriority,
440 grfMode, snbExclude, reserved, ppstg);
443 * Perform a sanity check on the parameters.
445 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
449 * Validate the STGM flags
451 if ( FAILED( validateSTGM(grfMode) ))
452 return STG_E_INVALIDFLAG;
457 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
458 (grfMode & STGM_DELETEONRELEASE) ||
459 (grfMode & STGM_PRIORITY) )
460 return STG_E_INVALIDFUNCTION;
463 * Initialize the out parameter
468 * Create a property enumeration to search the properties
470 propertyEnumeration = IEnumSTATSTGImpl_Construct(
471 This->ancestorStorage,
472 This->rootPropertySetIndex);
475 * Search the enumeration for the property with the given name
477 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
483 * Delete the property enumeration since we don't need it anymore
485 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
488 * If it was found, construct the stream object and return a pointer to it.
490 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
491 (currentProperty.propertyType==PROPTYPE_STORAGE) )
494 * Construct a new Storage object
496 newStorage = StorageInternalImpl_Construct(
497 This->ancestorStorage,
502 *ppstg = (IStorage*)newStorage;
505 * Since we are returning a pointer to the interface,
506 * we have to nail down the reference.
508 StorageBaseImpl_AddRef(*ppstg);
513 return STG_E_INSUFFICIENTMEMORY;
516 return STG_E_FILENOTFOUND;
519 /************************************************************************
520 * Storage32BaseImpl_EnumElements (IStorage)
522 * This method will create an enumerator object that can be used to
523 * retrieve informatino about all the properties in the storage object.
525 * See Windows documentation for more details on IStorage methods.
527 HRESULT WINAPI StorageBaseImpl_EnumElements(
529 DWORD reserved1, /* [in] */
530 void* reserved2, /* [size_is][unique][in] */
531 DWORD reserved3, /* [in] */
532 IEnumSTATSTG** ppenum) /* [out] */
534 ICOM_THIS(StorageBaseImpl,iface);
535 IEnumSTATSTGImpl* newEnum;
537 TRACE("(%p, %ld, %p, %ld, %p)\n",
538 iface, reserved1, reserved2, reserved3, ppenum);
541 * Perform a sanity check on the parameters.
543 if ( (This==0) || (ppenum==0))
547 * Construct the enumerator.
549 newEnum = IEnumSTATSTGImpl_Construct(
550 This->ancestorStorage,
551 This->rootPropertySetIndex);
555 *ppenum = (IEnumSTATSTG*)newEnum;
558 * Don't forget to nail down a reference to the new object before
561 IEnumSTATSTGImpl_AddRef(*ppenum);
566 return E_OUTOFMEMORY;
569 /************************************************************************
570 * Storage32BaseImpl_Stat (IStorage)
572 * This method will retrieve information about this storage object.
574 * See Windows documentation for more details on IStorage methods.
576 HRESULT WINAPI StorageBaseImpl_Stat(
578 STATSTG* pstatstg, /* [out] */
579 DWORD grfStatFlag) /* [in] */
581 ICOM_THIS(StorageBaseImpl,iface);
582 StgProperty curProperty;
585 TRACE("(%p, %p, %lx)\n",
586 iface, pstatstg, grfStatFlag);
589 * Perform a sanity check on the parameters.
591 if ( (This==0) || (pstatstg==0))
595 * Read the information from the property.
597 readSuccessful = StorageImpl_ReadProperty(
598 This->ancestorStorage,
599 This->rootPropertySetIndex,
604 StorageUtl_CopyPropertyToSTATSTG(
615 /************************************************************************
616 * Storage32BaseImpl_RenameElement (IStorage)
618 * This method will rename the specified element.
620 * See Windows documentation for more details on IStorage methods.
622 * Implementation notes: The method used to rename consists of creating a clone
623 * of the deleted StgProperty object setting it with the new name and to
624 * perform a DestroyElement of the old StgProperty.
626 HRESULT WINAPI StorageBaseImpl_RenameElement(
628 const OLECHAR* pwcsOldName, /* [in] */
629 const OLECHAR* pwcsNewName) /* [in] */
631 ICOM_THIS(StorageBaseImpl,iface);
632 IEnumSTATSTGImpl* propertyEnumeration;
633 StgProperty currentProperty;
634 ULONG foundPropertyIndex;
636 TRACE("(%p, %s, %s)\n",
637 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
640 * Create a property enumeration to search the properties
642 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
643 This->rootPropertySetIndex);
646 * Search the enumeration for the new property name
648 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
652 if (foundPropertyIndex != PROPERTY_NULL)
655 * There is already a property with the new name
657 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
658 return STG_E_FILEALREADYEXISTS;
661 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
664 * Search the enumeration for the old property name
666 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
671 * Delete the property enumeration since we don't need it anymore
673 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
675 if (foundPropertyIndex != PROPERTY_NULL)
677 StgProperty renamedProperty;
678 ULONG renamedPropertyIndex;
681 * Setup a new property for the renamed property
683 renamedProperty.sizeOfNameString =
684 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
686 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
687 return STG_E_INVALIDNAME;
689 strcpyW(renamedProperty.name, pwcsNewName);
691 renamedProperty.propertyType = currentProperty.propertyType;
692 renamedProperty.startingBlock = currentProperty.startingBlock;
693 renamedProperty.size.s.LowPart = currentProperty.size.s.LowPart;
694 renamedProperty.size.s.HighPart = currentProperty.size.s.HighPart;
696 renamedProperty.previousProperty = PROPERTY_NULL;
697 renamedProperty.nextProperty = PROPERTY_NULL;
700 * Bring the dirProperty link in case it is a storage and in which
701 * case the renamed storage elements don't require to be reorganized.
703 renamedProperty.dirProperty = currentProperty.dirProperty;
705 /* call CoFileTime to get the current time
706 renamedProperty.timeStampS1
707 renamedProperty.timeStampD1
708 renamedProperty.timeStampS2
709 renamedProperty.timeStampD2
710 renamedProperty.propertyUniqueID
714 * Obtain a free property in the property chain
716 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
719 * Save the new property into the new property spot
721 StorageImpl_WriteProperty(
722 This->ancestorStorage,
723 renamedPropertyIndex,
727 * Find a spot in the property chain for our newly created property.
731 renamedPropertyIndex,
735 * At this point the renamed property has been inserted in the tree,
736 * now, before to Destroy the old property we must zeroed it's dirProperty
737 * otherwise the DestroyProperty below will zap it all and we do not want
739 * Also, we fake that the old property is a storage so the DestroyProperty
740 * will not do a SetSize(0) on the stream data.
742 * This means that we need to tweek the StgProperty if it is a stream or a
745 StorageImpl_ReadProperty(This->ancestorStorage,
749 currentProperty.dirProperty = PROPERTY_NULL;
750 currentProperty.propertyType = PROPTYPE_STORAGE;
751 StorageImpl_WriteProperty(
752 This->ancestorStorage,
757 * Invoke Destroy to get rid of the ole property and automatically redo
758 * the linking of it's previous and next members...
760 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
766 * There is no property with the old name
768 return STG_E_FILENOTFOUND;
774 /************************************************************************
775 * Storage32BaseImpl_CreateStream (IStorage)
777 * This method will create a stream object within this storage
779 * See Windows documentation for more details on IStorage methods.
781 HRESULT WINAPI StorageBaseImpl_CreateStream(
783 const OLECHAR* pwcsName, /* [string][in] */
784 DWORD grfMode, /* [in] */
785 DWORD reserved1, /* [in] */
786 DWORD reserved2, /* [in] */
787 IStream** ppstm) /* [out] */
789 ICOM_THIS(StorageBaseImpl,iface);
790 IEnumSTATSTGImpl* propertyEnumeration;
791 StgStreamImpl* newStream;
792 StgProperty currentProperty, newStreamProperty;
793 ULONG foundPropertyIndex, newPropertyIndex;
795 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
796 iface, debugstr_w(pwcsName), grfMode,
797 reserved1, reserved2, ppstm);
800 * Validate parameters
803 return STG_E_INVALIDPOINTER;
806 return STG_E_INVALIDNAME;
809 * Validate the STGM flags
811 if ( FAILED( validateSTGM(grfMode) ))
812 return STG_E_INVALIDFLAG;
817 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
818 (grfMode & STGM_DELETEONRELEASE) ||
819 (grfMode & STGM_TRANSACTED) )
820 return STG_E_INVALIDFUNCTION;
823 * Initialize the out parameter
828 * Create a property enumeration to search the properties
830 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
831 This->rootPropertySetIndex);
833 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
837 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
839 if (foundPropertyIndex != PROPERTY_NULL)
842 * An element with this name already exists
844 if (grfMode & STGM_CREATE)
846 IStorage_DestroyElement(iface, pwcsName);
849 return STG_E_FILEALREADYEXISTS;
853 * memset the empty property
855 memset(&newStreamProperty, 0, sizeof(StgProperty));
857 newStreamProperty.sizeOfNameString =
858 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
860 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
861 return STG_E_INVALIDNAME;
863 strcpyW(newStreamProperty.name, pwcsName);
865 newStreamProperty.propertyType = PROPTYPE_STREAM;
866 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
867 newStreamProperty.size.s.LowPart = 0;
868 newStreamProperty.size.s.HighPart = 0;
870 newStreamProperty.previousProperty = PROPERTY_NULL;
871 newStreamProperty.nextProperty = PROPERTY_NULL;
872 newStreamProperty.dirProperty = PROPERTY_NULL;
874 /* call CoFileTime to get the current time
875 newStreamProperty.timeStampS1
876 newStreamProperty.timeStampD1
877 newStreamProperty.timeStampS2
878 newStreamProperty.timeStampD2
881 /* newStreamProperty.propertyUniqueID */
884 * Get a free property or create a new one
886 newPropertyIndex = getFreeProperty(This->ancestorStorage);
889 * Save the new property into the new property spot
891 StorageImpl_WriteProperty(
892 This->ancestorStorage,
897 * Find a spot in the property chain for our newly created property.
905 * Open the stream to return it.
907 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
911 *ppstm = (IStream*)newStream;
914 * Since we are returning a pointer to the interface, we have to nail down
917 StgStreamImpl_AddRef(*ppstm);
921 return STG_E_INSUFFICIENTMEMORY;
927 /************************************************************************
928 * Storage32BaseImpl_SetClass (IStorage)
930 * This method will write the specified CLSID in the property of this
933 * See Windows documentation for more details on IStorage methods.
935 HRESULT WINAPI StorageBaseImpl_SetClass(
937 REFCLSID clsid) /* [in] */
939 ICOM_THIS(StorageBaseImpl,iface);
940 HRESULT hRes = E_FAIL;
941 StgProperty curProperty;
944 TRACE("(%p, %p)\n", iface, clsid);
946 success = StorageImpl_ReadProperty(This->ancestorStorage,
947 This->rootPropertySetIndex,
951 curProperty.propertyUniqueID = *clsid;
953 success = StorageImpl_WriteProperty(This->ancestorStorage,
954 This->rootPropertySetIndex,
963 /************************************************************************
964 ** Storage32Impl implementation
967 /************************************************************************
968 * Storage32Impl_CreateStorage (IStorage)
970 * This method will create the storage object within the provided storage.
972 * See Windows documentation for more details on IStorage methods.
974 HRESULT WINAPI StorageImpl_CreateStorage(
976 const OLECHAR *pwcsName, /* [string][in] */
977 DWORD grfMode, /* [in] */
978 DWORD reserved1, /* [in] */
979 DWORD reserved2, /* [in] */
980 IStorage **ppstg) /* [out] */
982 StorageImpl* const This=(StorageImpl*)iface;
984 IEnumSTATSTGImpl *propertyEnumeration;
985 StgProperty currentProperty;
986 StgProperty newProperty;
987 ULONG foundPropertyIndex;
988 ULONG newPropertyIndex;
991 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
992 iface, debugstr_w(pwcsName), grfMode,
993 reserved1, reserved2, ppstg);
996 * Validate parameters
999 return STG_E_INVALIDPOINTER;
1002 return STG_E_INVALIDNAME;
1005 * Validate the STGM flags
1007 if ( FAILED( validateSTGM(grfMode) ) ||
1008 (grfMode & STGM_DELETEONRELEASE) )
1009 return STG_E_INVALIDFLAG;
1012 * Initialize the out parameter
1017 * Create a property enumeration and search the properties
1019 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
1020 This->rootPropertySetIndex);
1022 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1025 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1027 if (foundPropertyIndex != PROPERTY_NULL)
1030 * An element with this name already exists
1032 if (grfMode & STGM_CREATE)
1033 IStorage_DestroyElement(iface, pwcsName);
1035 return STG_E_FILEALREADYEXISTS;
1039 * memset the empty property
1041 memset(&newProperty, 0, sizeof(StgProperty));
1043 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1045 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1046 return STG_E_INVALIDNAME;
1048 strcpyW(newProperty.name, pwcsName);
1050 newProperty.propertyType = PROPTYPE_STORAGE;
1051 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1052 newProperty.size.s.LowPart = 0;
1053 newProperty.size.s.HighPart = 0;
1055 newProperty.previousProperty = PROPERTY_NULL;
1056 newProperty.nextProperty = PROPERTY_NULL;
1057 newProperty.dirProperty = PROPERTY_NULL;
1059 /* call CoFileTime to get the current time
1060 newProperty.timeStampS1
1061 newProperty.timeStampD1
1062 newProperty.timeStampS2
1063 newProperty.timeStampD2
1066 /* newStorageProperty.propertyUniqueID */
1069 * Obtain a free property in the property chain
1071 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1074 * Save the new property into the new property spot
1076 StorageImpl_WriteProperty(
1077 This->ancestorStorage,
1082 * Find a spot in the property chain for our newly created property.
1084 updatePropertyChain(
1090 * Open it to get a pointer to return.
1092 hr = IStorage_OpenStorage(
1101 if( (hr != S_OK) || (*ppstg == NULL))
1111 /***************************************************************************
1115 * Get a free property or create a new one.
1117 static ULONG getFreeProperty(
1118 StorageImpl *storage)
1120 ULONG currentPropertyIndex = 0;
1121 ULONG newPropertyIndex = PROPERTY_NULL;
1122 BOOL readSuccessful = TRUE;
1123 StgProperty currentProperty;
1128 * Start by reading the root property
1130 readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1131 currentPropertyIndex,
1135 if (currentProperty.sizeOfNameString == 0)
1138 * The property existis and is available, we found it.
1140 newPropertyIndex = currentPropertyIndex;
1146 * We exhausted the property list, we will create more space below
1148 newPropertyIndex = currentPropertyIndex;
1150 currentPropertyIndex++;
1152 } while (newPropertyIndex == PROPERTY_NULL);
1155 * grow the property chain
1157 if (! readSuccessful)
1159 StgProperty emptyProperty;
1160 ULARGE_INTEGER newSize;
1161 ULONG propertyIndex;
1162 ULONG lastProperty = 0;
1163 ULONG blockCount = 0;
1166 * obtain the new count of property blocks
1168 blockCount = BlockChainStream_GetCount(
1169 storage->ancestorStorage->rootBlockChain)+1;
1172 * initialize the size used by the property stream
1174 newSize.s.HighPart = 0;
1175 newSize.s.LowPart = storage->bigBlockSize * blockCount;
1178 * add a property block to the property chain
1180 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1183 * memset the empty property in order to initialize the unused newly
1186 memset(&emptyProperty, 0, sizeof(StgProperty));
1191 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1194 propertyIndex = newPropertyIndex;
1195 propertyIndex < lastProperty;
1198 StorageImpl_WriteProperty(
1199 storage->ancestorStorage,
1205 return newPropertyIndex;
1208 /****************************************************************************
1212 * Case insensitive comparaison of StgProperty.name by first considering
1215 * Returns <0 when newPrpoerty < currentProperty
1216 * >0 when newPrpoerty > currentProperty
1217 * 0 when newPrpoerty == currentProperty
1219 static LONG propertyNameCmp(
1220 OLECHAR *newProperty,
1221 OLECHAR *currentProperty)
1223 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1228 * We compare the string themselves only when they are of the same lenght
1230 diff = lstrcmpiW( newProperty, currentProperty);
1236 /****************************************************************************
1240 * Properly link this new element in the property chain.
1242 static void updatePropertyChain(
1243 StorageImpl *storage,
1244 ULONG newPropertyIndex,
1245 StgProperty newProperty)
1247 StgProperty currentProperty;
1250 * Read the root property
1252 StorageImpl_ReadProperty(storage->ancestorStorage,
1253 storage->rootPropertySetIndex,
1256 if (currentProperty.dirProperty != PROPERTY_NULL)
1259 * The root storage contains some element, therefore, start the research
1260 * for the appropriate location.
1263 ULONG current, next, previous, currentPropertyId;
1266 * Keep the StgProperty sequence number of the storage first property
1268 currentPropertyId = currentProperty.dirProperty;
1273 StorageImpl_ReadProperty(storage->ancestorStorage,
1274 currentProperty.dirProperty,
1277 previous = currentProperty.previousProperty;
1278 next = currentProperty.nextProperty;
1279 current = currentPropertyId;
1283 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1287 if (previous != PROPERTY_NULL)
1289 StorageImpl_ReadProperty(storage->ancestorStorage,
1296 currentProperty.previousProperty = newPropertyIndex;
1297 StorageImpl_WriteProperty(storage->ancestorStorage,
1305 if (next != PROPERTY_NULL)
1307 StorageImpl_ReadProperty(storage->ancestorStorage,
1314 currentProperty.nextProperty = newPropertyIndex;
1315 StorageImpl_WriteProperty(storage->ancestorStorage,
1324 * Trying to insert an item with the same name in the
1325 * subtree structure.
1330 previous = currentProperty.previousProperty;
1331 next = currentProperty.nextProperty;
1337 * The root storage is empty, link the new property to it's dir property
1339 currentProperty.dirProperty = newPropertyIndex;
1340 StorageImpl_WriteProperty(storage->ancestorStorage,
1341 storage->rootPropertySetIndex,
1347 /*************************************************************************
1350 HRESULT WINAPI StorageImpl_CopyTo(
1352 DWORD ciidExclude, /* [in] */
1353 const IID* rgiidExclude, /* [size_is][unique][in] */
1354 SNB snbExclude, /* [unique][in] */
1355 IStorage* pstgDest) /* [unique][in] */
1357 IEnumSTATSTG *elements = 0;
1358 STATSTG curElement, strStat;
1360 IStorage *pstgTmp, *pstgChild;
1361 IStream *pstrTmp, *pstrChild;
1363 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1364 FIXME("Exclude option not implemented\n");
1366 TRACE("(%p, %ld, %p, %p, %p)\n",
1367 iface, ciidExclude, rgiidExclude,
1368 snbExclude, pstgDest);
1371 * Perform a sanity check
1373 if ( pstgDest == 0 )
1374 return STG_E_INVALIDPOINTER;
1377 * Enumerate the elements
1379 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1387 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1388 IStorage_SetClass( pstgDest, &curElement.clsid );
1393 * Obtain the next element
1395 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1397 if ( hr == S_FALSE )
1399 hr = S_OK; /* done, every element has been copied */
1403 if (curElement.type == STGTY_STORAGE)
1406 * open child source storage
1408 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1409 STGM_READ|STGM_SHARE_EXCLUSIVE,
1410 NULL, 0, &pstgChild );
1416 * Check if destination storage is not a child of the source
1417 * storage, which will cause an infinite loop
1419 if (pstgChild == pstgDest)
1421 IEnumSTATSTG_Release(elements);
1423 return STG_E_ACCESSDENIED;
1427 * create a new storage in destination storage
1429 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1430 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1434 * if it already exist, don't create a new one use this one
1436 if (hr == STG_E_FILEALREADYEXISTS)
1438 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1439 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1440 NULL, 0, &pstgTmp );
1448 * do the copy recursively
1450 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1451 snbExclude, pstgTmp );
1453 IStorage_Release( pstgTmp );
1454 IStorage_Release( pstgChild );
1456 else if (curElement.type == STGTY_STREAM)
1459 * create a new stream in destination storage. If the stream already
1460 * exist, it will be deleted and a new one will be created.
1462 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1463 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1470 * open child stream storage
1472 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1473 STGM_READ|STGM_SHARE_EXCLUSIVE,
1480 * Get the size of the source stream
1482 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1485 * Set the size of the destination stream.
1487 IStream_SetSize(pstrTmp, strStat.cbSize);
1492 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1495 IStream_Release( pstrTmp );
1496 IStream_Release( pstrChild );
1500 WARN("unknown element type: %ld\n", curElement.type);
1503 } while (hr == S_OK);
1508 IEnumSTATSTG_Release(elements);
1513 /*************************************************************************
1514 * MoveElementTo (IStorage)
1516 HRESULT WINAPI StorageImpl_MoveElementTo(
1518 const OLECHAR *pwcsName, /* [string][in] */
1519 IStorage *pstgDest, /* [unique][in] */
1520 const OLECHAR *pwcsNewName,/* [string][in] */
1521 DWORD grfFlags) /* [in] */
1523 FIXME("not implemented!\n");
1527 /*************************************************************************
1530 HRESULT WINAPI StorageImpl_Commit(
1532 DWORD grfCommitFlags)/* [in] */
1534 FIXME("(%ld): stub!\n", grfCommitFlags);
1538 /*************************************************************************
1541 HRESULT WINAPI StorageImpl_Revert(
1544 FIXME("not implemented!\n");
1548 /*************************************************************************
1549 * DestroyElement (IStorage)
1551 * Stategy: This implementation is build this way for simplicity not for speed.
1552 * I always delete the top most element of the enumeration and adjust
1553 * the deleted element pointer all the time. This takes longer to
1554 * do but allow to reinvoke DestroyElement whenever we encounter a
1555 * storage object. The optimisation reside in the usage of another
1556 * enumeration stategy that would give all the leaves of a storage
1557 * first. (postfix order)
1559 HRESULT WINAPI StorageImpl_DestroyElement(
1561 const OLECHAR *pwcsName)/* [string][in] */
1563 StorageImpl* const This=(StorageImpl*)iface;
1565 IEnumSTATSTGImpl* propertyEnumeration;
1568 StgProperty propertyToDelete;
1569 StgProperty parentProperty;
1570 ULONG foundPropertyIndexToDelete;
1571 ULONG typeOfRelation;
1572 ULONG parentPropertyId;
1575 iface, debugstr_w(pwcsName));
1578 * Perform a sanity check on the parameters.
1581 return STG_E_INVALIDPOINTER;
1584 * Create a property enumeration to search the property with the given name
1586 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1587 This->ancestorStorage,
1588 This->rootPropertySetIndex);
1590 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1591 propertyEnumeration,
1595 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1597 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1599 return STG_E_FILENOTFOUND;
1603 * Find the parent property of the property to delete (the one that
1604 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1605 * the parent is This. Otherwise, the parent is one of it's sibling...
1609 * First, read This's StgProperty..
1611 res = StorageImpl_ReadProperty(
1612 This->ancestorStorage,
1613 This->rootPropertySetIndex,
1619 * Second, check to see if by any chance the actual storage (This) is not
1620 * the parent of the property to delete... We never know...
1622 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1625 * Set data as it would have been done in the else part...
1627 typeOfRelation = PROPERTY_RELATION_DIR;
1628 parentPropertyId = This->rootPropertySetIndex;
1633 * Create a property enumeration to search the parent properties, and
1634 * delete it once done.
1636 IEnumSTATSTGImpl* propertyEnumeration2;
1638 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1639 This->ancestorStorage,
1640 This->rootPropertySetIndex);
1642 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1643 propertyEnumeration2,
1644 foundPropertyIndexToDelete,
1648 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1651 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1653 hr = deleteStorageProperty(
1655 foundPropertyIndexToDelete,
1658 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1660 hr = deleteStreamProperty(
1662 foundPropertyIndexToDelete,
1670 * Adjust the property chain
1672 hr = adjustPropertyChain(
1683 /*********************************************************************
1687 * Perform the deletion of a complete storage node
1690 static HRESULT deleteStorageProperty(
1691 StorageImpl *parentStorage,
1692 ULONG indexOfPropertyToDelete,
1693 StgProperty propertyToDelete)
1695 IEnumSTATSTG *elements = 0;
1696 IStorage *childStorage = 0;
1697 STATSTG currentElement;
1699 HRESULT destroyHr = S_OK;
1702 * Open the storage and enumerate it
1704 hr = StorageBaseImpl_OpenStorage(
1705 (IStorage*)parentStorage,
1706 propertyToDelete.name,
1708 STGM_SHARE_EXCLUSIVE,
1719 * Enumerate the elements
1721 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1726 * Obtain the next element
1728 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1731 destroyHr = StorageImpl_DestroyElement(
1732 (IStorage*)childStorage,
1733 (OLECHAR*)currentElement.pwcsName);
1735 CoTaskMemFree(currentElement.pwcsName);
1739 * We need to Reset the enumeration every time because we delete elements
1740 * and the enumeration could be invalid
1742 IEnumSTATSTG_Reset(elements);
1744 } while ((hr == S_OK) && (destroyHr == S_OK));
1747 * Invalidate the property by zeroing it's name member.
1749 propertyToDelete.sizeOfNameString = 0;
1751 StorageImpl_WriteProperty(parentStorage->ancestorStorage,
1752 indexOfPropertyToDelete,
1755 IStorage_Release(childStorage);
1756 IEnumSTATSTG_Release(elements);
1761 /*********************************************************************
1765 * Perform the deletion of a stream node
1768 static HRESULT deleteStreamProperty(
1769 StorageImpl *parentStorage,
1770 ULONG indexOfPropertyToDelete,
1771 StgProperty propertyToDelete)
1775 ULARGE_INTEGER size;
1777 size.s.HighPart = 0;
1780 hr = StorageBaseImpl_OpenStream(
1781 (IStorage*)parentStorage,
1782 (OLECHAR*)propertyToDelete.name,
1784 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1796 hr = IStream_SetSize(pis, size);
1804 * Release the stream object.
1806 IStream_Release(pis);
1809 * Invalidate the property by zeroing it's name member.
1811 propertyToDelete.sizeOfNameString = 0;
1814 * Here we should re-read the property so we get the updated pointer
1815 * but since we are here to zap it, I don't do it...
1817 StorageImpl_WriteProperty(
1818 parentStorage->ancestorStorage,
1819 indexOfPropertyToDelete,
1825 /*********************************************************************
1829 * Finds a placeholder for the StgProperty within the Storage
1832 static HRESULT findPlaceholder(
1833 StorageImpl *storage,
1834 ULONG propertyIndexToStore,
1835 ULONG storePropertyIndex,
1838 StgProperty storeProperty;
1843 * Read the storage property
1845 res = StorageImpl_ReadProperty(
1846 storage->ancestorStorage,
1855 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1857 if (storeProperty.previousProperty != PROPERTY_NULL)
1859 return findPlaceholder(
1861 propertyIndexToStore,
1862 storeProperty.previousProperty,
1867 storeProperty.previousProperty = propertyIndexToStore;
1870 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1872 if (storeProperty.nextProperty != PROPERTY_NULL)
1874 return findPlaceholder(
1876 propertyIndexToStore,
1877 storeProperty.nextProperty,
1882 storeProperty.nextProperty = propertyIndexToStore;
1885 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1887 if (storeProperty.dirProperty != PROPERTY_NULL)
1889 return findPlaceholder(
1891 propertyIndexToStore,
1892 storeProperty.dirProperty,
1897 storeProperty.dirProperty = propertyIndexToStore;
1901 hr = StorageImpl_WriteProperty(
1902 storage->ancestorStorage,
1914 /*************************************************************************
1918 * This method takes the previous and the next property link of a property
1919 * to be deleted and find them a place in the Storage.
1921 static HRESULT adjustPropertyChain(
1923 StgProperty propertyToDelete,
1924 StgProperty parentProperty,
1925 ULONG parentPropertyId,
1928 ULONG newLinkProperty = PROPERTY_NULL;
1929 BOOL needToFindAPlaceholder = FALSE;
1930 ULONG storeNode = PROPERTY_NULL;
1931 ULONG toStoreNode = PROPERTY_NULL;
1932 INT relationType = 0;
1936 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1938 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1941 * Set the parent previous to the property to delete previous
1943 newLinkProperty = propertyToDelete.previousProperty;
1945 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1948 * We also need to find a storage for the other link, setup variables
1949 * to do this at the end...
1951 needToFindAPlaceholder = TRUE;
1952 storeNode = propertyToDelete.previousProperty;
1953 toStoreNode = propertyToDelete.nextProperty;
1954 relationType = PROPERTY_RELATION_NEXT;
1957 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1960 * Set the parent previous to the property to delete next
1962 newLinkProperty = propertyToDelete.nextProperty;
1966 * Link it for real...
1968 parentProperty.previousProperty = newLinkProperty;
1971 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1973 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1976 * Set the parent next to the property to delete next previous
1978 newLinkProperty = propertyToDelete.previousProperty;
1980 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1983 * We also need to find a storage for the other link, setup variables
1984 * to do this at the end...
1986 needToFindAPlaceholder = TRUE;
1987 storeNode = propertyToDelete.previousProperty;
1988 toStoreNode = propertyToDelete.nextProperty;
1989 relationType = PROPERTY_RELATION_NEXT;
1992 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1995 * Set the parent next to the property to delete next
1997 newLinkProperty = propertyToDelete.nextProperty;
2001 * Link it for real...
2003 parentProperty.nextProperty = newLinkProperty;
2005 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2007 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2010 * Set the parent dir to the property to delete previous
2012 newLinkProperty = propertyToDelete.previousProperty;
2014 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2017 * We also need to find a storage for the other link, setup variables
2018 * to do this at the end...
2020 needToFindAPlaceholder = TRUE;
2021 storeNode = propertyToDelete.previousProperty;
2022 toStoreNode = propertyToDelete.nextProperty;
2023 relationType = PROPERTY_RELATION_NEXT;
2026 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2029 * Set the parent dir to the property to delete next
2031 newLinkProperty = propertyToDelete.nextProperty;
2035 * Link it for real...
2037 parentProperty.dirProperty = newLinkProperty;
2041 * Write back the parent property
2043 res = StorageImpl_WriteProperty(
2044 This->ancestorStorage,
2053 * If a placeholder is required for the other link, then, find one and
2054 * get out of here...
2056 if (needToFindAPlaceholder)
2058 hr = findPlaceholder(
2069 /******************************************************************************
2070 * SetElementTimes (IStorage)
2072 HRESULT WINAPI StorageImpl_SetElementTimes(
2074 const OLECHAR *pwcsName,/* [string][in] */
2075 const FILETIME *pctime, /* [in] */
2076 const FILETIME *patime, /* [in] */
2077 const FILETIME *pmtime) /* [in] */
2079 FIXME("not implemented!\n");
2083 /******************************************************************************
2084 * SetStateBits (IStorage)
2086 HRESULT WINAPI StorageImpl_SetStateBits(
2088 DWORD grfStateBits,/* [in] */
2089 DWORD grfMask) /* [in] */
2091 FIXME("not implemented!\n");
2095 HRESULT StorageImpl_Construct(
2104 StgProperty currentProperty;
2105 BOOL readSuccessful;
2106 ULONG currentPropertyIndex;
2108 if ( FAILED( validateSTGM(openFlags) ))
2109 return STG_E_INVALIDFLAG;
2111 memset(This, 0, sizeof(StorageImpl));
2114 * Initialize the virtual fgunction table.
2116 ICOM_VTBL(This) = &Storage32Impl_Vtbl;
2117 This->v_destructor = &StorageImpl_Destroy;
2120 * This is the top-level storage so initialize the ancester pointer
2123 This->ancestorStorage = This;
2126 * Initialize the physical support of the storage.
2128 This->hFile = hFile;
2131 * Initialize the big block cache.
2133 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2134 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2135 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2141 if (This->bigBlockFile == 0)
2146 ULARGE_INTEGER size;
2147 BYTE* bigBlockBuffer;
2150 * Initialize all header variables:
2151 * - The big block depot consists of one block and it is at block 0
2152 * - The properties start at block 1
2153 * - There is no small block depot
2155 memset( This->bigBlockDepotStart,
2157 sizeof(This->bigBlockDepotStart));
2159 This->bigBlockDepotCount = 1;
2160 This->bigBlockDepotStart[0] = 0;
2161 This->rootStartBlock = 1;
2162 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2163 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2164 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2165 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2166 This->extBigBlockDepotCount = 0;
2168 StorageImpl_SaveFileHeader(This);
2171 * Add one block for the big block depot and one block for the properties
2173 size.s.HighPart = 0;
2174 size.s.LowPart = This->bigBlockSize * 3;
2175 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2178 * Initialize the big block depot
2180 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2181 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2182 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2183 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2184 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2189 * Load the header for the file.
2191 hr = StorageImpl_LoadFileHeader(This);
2195 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2202 * There is no block depot cached yet.
2204 This->indexBlockDepotCached = 0xFFFFFFFF;
2207 * Start searching for free blocks with block 0.
2209 This->prevFreeBlock = 0;
2212 * Create the block chain abstractions.
2214 This->rootBlockChain =
2215 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL);
2217 This->smallBlockDepotChain = BlockChainStream_Construct(
2219 &This->smallBlockDepotStart,
2223 * Write the root property
2227 StgProperty rootProp;
2229 * Initialize the property chain
2231 memset(&rootProp, 0, sizeof(rootProp));
2232 lstrcpyAtoW(rootProp.name, rootPropertyName);
2234 rootProp.sizeOfNameString = (lstrlenW(rootProp.name)+1) * sizeof(WCHAR);
2235 rootProp.propertyType = PROPTYPE_ROOT;
2236 rootProp.previousProperty = PROPERTY_NULL;
2237 rootProp.nextProperty = PROPERTY_NULL;
2238 rootProp.dirProperty = PROPERTY_NULL;
2239 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2240 rootProp.size.s.HighPart = 0;
2241 rootProp.size.s.LowPart = 0;
2243 StorageImpl_WriteProperty(This, 0, &rootProp);
2247 * Find the ID of the root int he property sets.
2249 currentPropertyIndex = 0;
2253 readSuccessful = StorageImpl_ReadProperty(
2255 currentPropertyIndex,
2260 if ( (currentProperty.sizeOfNameString != 0 ) &&
2261 (currentProperty.propertyType == PROPTYPE_ROOT) )
2263 This->rootPropertySetIndex = currentPropertyIndex;
2267 currentPropertyIndex++;
2269 } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2271 if (!readSuccessful)
2278 * Create the block chain abstraction for the small block root chain.
2280 This->smallBlockRootChain = BlockChainStream_Construct(
2283 This->rootPropertySetIndex);
2288 void StorageImpl_Destroy(
2291 TRACE("(%p)\n", This);
2293 BlockChainStream_Destroy(This->smallBlockRootChain);
2294 BlockChainStream_Destroy(This->rootBlockChain);
2295 BlockChainStream_Destroy(This->smallBlockDepotChain);
2297 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2301 /******************************************************************************
2302 * Storage32Impl_GetNextFreeBigBlock
2304 * Returns the index of the next free big block.
2305 * If the big block depot is filled, this method will enlarge it.
2308 ULONG StorageImpl_GetNextFreeBigBlock(
2311 ULONG depotBlockIndexPos;
2313 ULONG depotBlockOffset;
2314 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2315 ULONG nextBlockIndex = BLOCK_SPECIAL;
2317 ULONG freeBlock = BLOCK_UNUSED;
2319 depotIndex = This->prevFreeBlock / blocksPerDepot;
2320 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2323 * Scan the entire big block depot until we find a block marked free
2325 while (nextBlockIndex != BLOCK_UNUSED)
2327 if (depotIndex < COUNT_BBDEPOTINHEADER)
2329 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2332 * Grow the primary depot.
2334 if (depotBlockIndexPos == BLOCK_UNUSED)
2336 depotBlockIndexPos = depotIndex*blocksPerDepot;
2339 * Add a block depot.
2341 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2342 This->bigBlockDepotCount++;
2343 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2346 * Flag it as a block depot.
2348 StorageImpl_SetNextBlockInChain(This,
2352 /* Save new header information.
2354 StorageImpl_SaveFileHeader(This);
2359 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2361 if (depotBlockIndexPos == BLOCK_UNUSED)
2364 * Grow the extended depot.
2366 ULONG extIndex = BLOCK_UNUSED;
2367 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2368 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2370 if (extBlockOffset == 0)
2372 /* We need an extended block.
2374 extIndex = Storage32Impl_AddExtBlockDepot(This);
2375 This->extBigBlockDepotCount++;
2376 depotBlockIndexPos = extIndex + 1;
2379 depotBlockIndexPos = depotIndex * blocksPerDepot;
2382 * Add a block depot and mark it in the extended block.
2384 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2385 This->bigBlockDepotCount++;
2386 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2388 /* Flag the block depot.
2390 StorageImpl_SetNextBlockInChain(This,
2394 /* If necessary, flag the extended depot block.
2396 if (extIndex != BLOCK_UNUSED)
2397 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2399 /* Save header information.
2401 StorageImpl_SaveFileHeader(This);
2405 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2407 if (depotBuffer != 0)
2409 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2410 ( nextBlockIndex != BLOCK_UNUSED))
2412 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2414 if (nextBlockIndex == BLOCK_UNUSED)
2416 freeBlock = (depotIndex * blocksPerDepot) +
2417 (depotBlockOffset/sizeof(ULONG));
2420 depotBlockOffset += sizeof(ULONG);
2423 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2427 depotBlockOffset = 0;
2430 This->prevFreeBlock = freeBlock;
2435 /******************************************************************************
2436 * Storage32Impl_AddBlockDepot
2438 * This will create a depot block, essentially it is a block initialized
2441 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2445 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2448 * Initialize blocks as free
2450 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2452 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2455 /******************************************************************************
2456 * Storage32Impl_GetExtDepotBlock
2458 * Returns the index of the block that corresponds to the specified depot
2459 * index. This method is only for depot indexes equal or greater than
2460 * COUNT_BBDEPOTINHEADER.
2462 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2464 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2465 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2466 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2467 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2468 ULONG blockIndex = BLOCK_UNUSED;
2469 ULONG extBlockIndex = This->extBigBlockDepotStart;
2471 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2473 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2474 return BLOCK_UNUSED;
2476 while (extBlockCount > 0)
2478 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2482 if (extBlockIndex != BLOCK_UNUSED)
2486 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2488 if (depotBuffer != 0)
2490 StorageUtl_ReadDWord(depotBuffer,
2491 extBlockOffset * sizeof(ULONG),
2494 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2501 /******************************************************************************
2502 * Storage32Impl_SetExtDepotBlock
2504 * Associates the specified block index to the specified depot index.
2505 * This method is only for depot indexes equal or greater than
2506 * COUNT_BBDEPOTINHEADER.
2508 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2512 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2513 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2514 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2515 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2516 ULONG extBlockIndex = This->extBigBlockDepotStart;
2518 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2520 while (extBlockCount > 0)
2522 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2526 if (extBlockIndex != BLOCK_UNUSED)
2530 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2532 if (depotBuffer != 0)
2534 StorageUtl_WriteDWord(depotBuffer,
2535 extBlockOffset * sizeof(ULONG),
2538 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2543 /******************************************************************************
2544 * Storage32Impl_AddExtBlockDepot
2546 * Creates an extended depot block.
2548 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2550 ULONG numExtBlocks = This->extBigBlockDepotCount;
2551 ULONG nextExtBlock = This->extBigBlockDepotStart;
2552 BYTE* depotBuffer = NULL;
2553 ULONG index = BLOCK_UNUSED;
2554 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2555 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2556 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2558 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2559 blocksPerDepotBlock;
2561 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2564 * The first extended block.
2566 This->extBigBlockDepotStart = index;
2572 * Follow the chain to the last one.
2574 for (i = 0; i < (numExtBlocks - 1); i++)
2576 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2580 * Add the new extended block to the chain.
2582 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2583 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2584 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2588 * Initialize this block.
2590 depotBuffer = StorageImpl_GetBigBlock(This, index);
2591 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2592 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2597 /******************************************************************************
2598 * Storage32Impl_FreeBigBlock
2600 * This method will flag the specified block as free in the big block depot.
2602 void StorageImpl_FreeBigBlock(
2606 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2608 if (blockIndex < This->prevFreeBlock)
2609 This->prevFreeBlock = blockIndex;
2612 /************************************************************************
2613 * Storage32Impl_GetNextBlockInChain
2615 * This method will retrieve the block index of the next big block in
2618 * Params: This - Pointer to the Storage object.
2619 * blockIndex - Index of the block to retrieve the chain
2622 * Returns: This method returns the index of the next block in the chain.
2623 * It will return the constants:
2624 * BLOCK_SPECIAL - If the block given was not part of a
2626 * BLOCK_END_OF_CHAIN - If the block given was the last in
2628 * BLOCK_UNUSED - If the block given was not past of a chain
2630 * BLOCK_EXTBBDEPOT - This block is part of the extended
2633 * See Windows documentation for more details on IStorage methods.
2635 ULONG StorageImpl_GetNextBlockInChain(
2639 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2640 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2641 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2642 ULONG nextBlockIndex = BLOCK_SPECIAL;
2644 ULONG depotBlockIndexPos;
2646 assert(depotBlockCount < This->bigBlockDepotCount);
2649 * Cache the currently accessed depot block.
2651 if (depotBlockCount != This->indexBlockDepotCached)
2653 This->indexBlockDepotCached = depotBlockCount;
2655 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2657 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2662 * We have to look in the extended depot.
2664 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2667 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2673 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2675 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &nextBlockIndex);
2676 This->blockDepotCached[index] = nextBlockIndex;
2679 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2683 nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2685 return nextBlockIndex;
2688 /******************************************************************************
2689 * Storage32Impl_GetNextExtendedBlock
2691 * Given an extended block this method will return the next extended block.
2694 * The last ULONG of an extended block is the block index of the next
2695 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2699 * - The index of the next extended block
2700 * - BLOCK_UNUSED: there is no next extended block.
2701 * - Any other return values denotes failure.
2703 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2705 ULONG nextBlockIndex = BLOCK_SPECIAL;
2706 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2709 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2713 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2715 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2718 return nextBlockIndex;
2721 /******************************************************************************
2722 * Storage32Impl_SetNextBlockInChain
2724 * This method will write the index of the specified block's next block
2725 * in the big block depot.
2727 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2730 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2731 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2732 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2735 void StorageImpl_SetNextBlockInChain(
2740 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2741 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2742 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2743 ULONG depotBlockIndexPos;
2746 assert(depotBlockCount < This->bigBlockDepotCount);
2747 assert(blockIndex != nextBlock);
2749 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2751 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2756 * We have to look in the extended depot.
2758 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2761 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2765 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2766 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2770 * Update the cached block depot, if necessary.
2772 if (depotBlockCount == This->indexBlockDepotCached)
2774 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2778 /******************************************************************************
2779 * Storage32Impl_LoadFileHeader
2781 * This method will read in the file header, i.e. big block index -1.
2783 HRESULT StorageImpl_LoadFileHeader(
2786 HRESULT hr = STG_E_FILENOTFOUND;
2787 void* headerBigBlock = NULL;
2791 * Get a pointer to the big block of data containing the header.
2793 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2796 * Extract the information from the header.
2798 if (headerBigBlock!=0)
2801 * Check for the "magic number" signature and return an error if it is not
2804 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2806 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2807 return STG_E_OLDFORMAT;
2810 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2812 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2813 return STG_E_INVALIDHEADER;
2816 StorageUtl_ReadWord(
2818 OFFSET_BIGBLOCKSIZEBITS,
2819 &This->bigBlockSizeBits);
2821 StorageUtl_ReadWord(
2823 OFFSET_SMALLBLOCKSIZEBITS,
2824 &This->smallBlockSizeBits);
2826 StorageUtl_ReadDWord(
2828 OFFSET_BBDEPOTCOUNT,
2829 &This->bigBlockDepotCount);
2831 StorageUtl_ReadDWord(
2833 OFFSET_ROOTSTARTBLOCK,
2834 &This->rootStartBlock);
2836 StorageUtl_ReadDWord(
2838 OFFSET_SBDEPOTSTART,
2839 &This->smallBlockDepotStart);
2841 StorageUtl_ReadDWord(
2843 OFFSET_EXTBBDEPOTSTART,
2844 &This->extBigBlockDepotStart);
2846 StorageUtl_ReadDWord(
2848 OFFSET_EXTBBDEPOTCOUNT,
2849 &This->extBigBlockDepotCount);
2851 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2853 StorageUtl_ReadDWord(
2855 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2856 &(This->bigBlockDepotStart[index]));
2860 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2864 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2865 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2869 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2870 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2874 * Right now, the code is making some assumptions about the size of the
2875 * blocks, just make sure they are what we're expecting.
2877 assert( (This->bigBlockSize==DEF_BIG_BLOCK_SIZE) &&
2878 (This->smallBlockSize==DEF_SMALL_BLOCK_SIZE));
2881 * Release the block.
2883 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2891 /******************************************************************************
2892 * Storage32Impl_SaveFileHeader
2894 * This method will save to the file the header, i.e. big block -1.
2896 void StorageImpl_SaveFileHeader(
2899 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2904 * Get a pointer to the big block of data containing the header.
2906 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2909 * If the block read failed, the file is probably new.
2914 * Initialize for all unknown fields.
2916 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
2919 * Initialize the magic number.
2921 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
2924 * And a bunch of things we don't know what they mean
2926 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
2927 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
2928 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
2929 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
2930 StorageUtl_WriteDWord(headerBigBlock, 0x40, (DWORD)0x0001);
2934 * Write the information to the header.
2936 if (headerBigBlock!=0)
2938 StorageUtl_WriteWord(
2940 OFFSET_BIGBLOCKSIZEBITS,
2941 This->bigBlockSizeBits);
2943 StorageUtl_WriteWord(
2945 OFFSET_SMALLBLOCKSIZEBITS,
2946 This->smallBlockSizeBits);
2948 StorageUtl_WriteDWord(
2950 OFFSET_BBDEPOTCOUNT,
2951 This->bigBlockDepotCount);
2953 StorageUtl_WriteDWord(
2955 OFFSET_ROOTSTARTBLOCK,
2956 This->rootStartBlock);
2958 StorageUtl_WriteDWord(
2960 OFFSET_SBDEPOTSTART,
2961 This->smallBlockDepotStart);
2963 StorageUtl_WriteDWord(
2965 OFFSET_EXTBBDEPOTSTART,
2966 This->extBigBlockDepotStart);
2968 StorageUtl_WriteDWord(
2970 OFFSET_EXTBBDEPOTCOUNT,
2971 This->extBigBlockDepotCount);
2973 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2975 StorageUtl_WriteDWord(
2977 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2978 (This->bigBlockDepotStart[index]));
2983 * Write the big block back to the file.
2985 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
2988 /******************************************************************************
2989 * Storage32Impl_ReadProperty
2991 * This method will read the specified property from the property chain.
2993 BOOL StorageImpl_ReadProperty(
2996 StgProperty* buffer)
2998 BYTE currentProperty[PROPSET_BLOCK_SIZE];
2999 ULARGE_INTEGER offsetInPropSet;
3000 BOOL readSuccessful;
3003 offsetInPropSet.s.HighPart = 0;
3004 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3006 readSuccessful = BlockChainStream_ReadAt(
3007 This->rootBlockChain,
3015 memset(buffer->name, 0, sizeof(buffer->name));
3018 currentProperty+OFFSET_PS_NAME,
3019 PROPERTY_NAME_BUFFER_LEN );
3021 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3023 StorageUtl_ReadWord(
3025 OFFSET_PS_NAMELENGTH,
3026 &buffer->sizeOfNameString);
3028 StorageUtl_ReadDWord(
3030 OFFSET_PS_PREVIOUSPROP,
3031 &buffer->previousProperty);
3033 StorageUtl_ReadDWord(
3036 &buffer->nextProperty);
3038 StorageUtl_ReadDWord(
3041 &buffer->dirProperty);
3043 StorageUtl_ReadGUID(
3046 &buffer->propertyUniqueID);
3048 StorageUtl_ReadDWord(
3051 &buffer->timeStampS1);
3053 StorageUtl_ReadDWord(
3056 &buffer->timeStampD1);
3058 StorageUtl_ReadDWord(
3061 &buffer->timeStampS2);
3063 StorageUtl_ReadDWord(
3066 &buffer->timeStampD2);
3068 StorageUtl_ReadDWord(
3070 OFFSET_PS_STARTBLOCK,
3071 &buffer->startingBlock);
3073 StorageUtl_ReadDWord(
3076 &buffer->size.s.LowPart);
3078 buffer->size.s.HighPart = 0;
3081 return readSuccessful;
3084 /*********************************************************************
3085 * Write the specified property into the property chain
3087 BOOL StorageImpl_WriteProperty(
3090 StgProperty* buffer)
3092 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3093 ULARGE_INTEGER offsetInPropSet;
3094 BOOL writeSuccessful;
3097 offsetInPropSet.s.HighPart = 0;
3098 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3100 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3103 currentProperty + OFFSET_PS_NAME,
3105 PROPERTY_NAME_BUFFER_LEN );
3107 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3109 StorageUtl_WriteWord(
3111 OFFSET_PS_NAMELENGTH,
3112 buffer->sizeOfNameString);
3114 StorageUtl_WriteDWord(
3116 OFFSET_PS_PREVIOUSPROP,
3117 buffer->previousProperty);
3119 StorageUtl_WriteDWord(
3122 buffer->nextProperty);
3124 StorageUtl_WriteDWord(
3127 buffer->dirProperty);
3129 StorageUtl_WriteGUID(
3132 &buffer->propertyUniqueID);
3134 StorageUtl_WriteDWord(
3137 buffer->timeStampS1);
3139 StorageUtl_WriteDWord(
3142 buffer->timeStampD1);
3144 StorageUtl_WriteDWord(
3147 buffer->timeStampS2);
3149 StorageUtl_WriteDWord(
3152 buffer->timeStampD2);
3154 StorageUtl_WriteDWord(
3156 OFFSET_PS_STARTBLOCK,
3157 buffer->startingBlock);
3159 StorageUtl_WriteDWord(
3162 buffer->size.s.LowPart);
3164 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3169 return writeSuccessful;
3172 BOOL StorageImpl_ReadBigBlock(
3177 void* bigBlockBuffer;
3179 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3181 if (bigBlockBuffer!=0)
3183 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3185 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3193 BOOL StorageImpl_WriteBigBlock(
3198 void* bigBlockBuffer;
3200 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3202 if (bigBlockBuffer!=0)
3204 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3206 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3214 void* StorageImpl_GetROBigBlock(
3218 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3221 void* StorageImpl_GetBigBlock(
3225 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3228 void StorageImpl_ReleaseBigBlock(
3232 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3235 /******************************************************************************
3236 * Storage32Impl_SmallBlocksToBigBlocks
3238 * This method will convert a small block chain to a big block chain.
3239 * The small block chain will be destroyed.
3241 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3243 SmallBlockChainStream** ppsbChain)
3245 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3246 ULARGE_INTEGER size, offset;
3247 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3248 ULONG propertyIndex;
3249 BOOL successRead, successWrite;
3250 StgProperty chainProperty;
3252 BlockChainStream *bbTempChain = NULL;
3253 BlockChainStream *bigBlockChain = NULL;
3256 * Create a temporary big block chain that doesn't have
3257 * an associated property. This temporary chain will be
3258 * used to copy data from small blocks to big blocks.
3260 bbTempChain = BlockChainStream_Construct(This,
3265 * Grow the big block chain.
3267 size = SmallBlockChainStream_GetSize(*ppsbChain);
3268 BlockChainStream_SetSize(bbTempChain, size);
3271 * Copy the contents of the small block chain to the big block chain
3272 * by small block size increments.
3274 offset.s.LowPart = 0;
3275 offset.s.HighPart = 0;
3279 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3282 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3284 DEF_SMALL_BLOCK_SIZE,
3287 cbTotalRead += cbRead;
3289 successWrite = BlockChainStream_WriteAt(bbTempChain,
3294 cbTotalWritten += cbWritten;
3296 offset.s.LowPart += This->smallBlockSize;
3298 } while (successRead && successWrite);
3299 HeapFree(GetProcessHeap(),0,buffer);
3301 assert(cbTotalRead == cbTotalWritten);
3304 * Destroy the small block chain.
3306 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3307 size.s.HighPart = 0;
3309 SmallBlockChainStream_SetSize(*ppsbChain, size);
3310 SmallBlockChainStream_Destroy(*ppsbChain);
3314 * Change the property information. This chain is now a big block chain
3315 * and it doesn't reside in the small blocks chain anymore.
3317 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3319 chainProperty.startingBlock = bbHeadOfChain;
3321 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3324 * Destroy the temporary propertyless big block chain.
3325 * Create a new big block chain associated with this property.
3327 BlockChainStream_Destroy(bbTempChain);
3328 bigBlockChain = BlockChainStream_Construct(This,
3332 return bigBlockChain;
3335 /******************************************************************************
3336 ** Storage32InternalImpl implementation
3339 StorageInternalImpl* StorageInternalImpl_Construct(
3340 StorageImpl* ancestorStorage,
3341 ULONG rootPropertyIndex)
3343 StorageInternalImpl* newStorage;
3346 * Allocate space for the new storage object
3348 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3352 memset(newStorage, 0, sizeof(StorageInternalImpl));
3355 * Initialize the virtual function table.
3357 ICOM_VTBL(newStorage) = &Storage32InternalImpl_Vtbl;
3358 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3361 * Keep the ancestor storage pointer and nail a reference to it.
3363 newStorage->ancestorStorage = ancestorStorage;
3364 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3367 * Keep the index of the root property set for this storage,
3369 newStorage->rootPropertySetIndex = rootPropertyIndex;
3377 void StorageInternalImpl_Destroy(
3378 StorageInternalImpl* This)
3380 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3381 HeapFree(GetProcessHeap(), 0, This);
3384 /******************************************************************************
3386 ** Storage32InternalImpl_Commit
3388 ** The non-root storages cannot be opened in transacted mode thus this function
3391 HRESULT WINAPI StorageInternalImpl_Commit(
3393 DWORD grfCommitFlags) /* [in] */
3398 /******************************************************************************
3400 ** Storage32InternalImpl_Revert
3402 ** The non-root storages cannot be opened in transacted mode thus this function
3405 HRESULT WINAPI StorageInternalImpl_Revert(
3411 /******************************************************************************
3412 ** IEnumSTATSTGImpl implementation
3415 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3416 StorageImpl* parentStorage,
3417 ULONG firstPropertyNode)
3419 IEnumSTATSTGImpl* newEnumeration;
3421 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3423 if (newEnumeration!=0)
3426 * Set-up the virtual function table and reference count.
3428 ICOM_VTBL(newEnumeration) = &IEnumSTATSTGImpl_Vtbl;
3429 newEnumeration->ref = 0;
3432 * We want to nail-down the reference to the storage in case the
3433 * enumeration out-lives the storage in the client application.
3435 newEnumeration->parentStorage = parentStorage;
3436 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3438 newEnumeration->firstPropertyNode = firstPropertyNode;
3441 * Initialize the search stack
3443 newEnumeration->stackSize = 0;
3444 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3445 newEnumeration->stackToVisit =
3446 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3449 * Make sure the current node of the iterator is the first one.
3451 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3454 return newEnumeration;
3457 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3459 IStorage_Release((IStorage*)This->parentStorage);
3460 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3461 HeapFree(GetProcessHeap(), 0, This);
3464 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3465 IEnumSTATSTG* iface,
3469 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3472 * Perform a sanity check on the parameters.
3475 return E_INVALIDARG;
3478 * Initialize the return parameter.
3483 * Compare the riid with the interface IDs implemented by this object.
3485 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3487 *ppvObject = (IEnumSTATSTG*)This;
3489 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3491 *ppvObject = (IEnumSTATSTG*)This;
3495 * Check that we obtained an interface.
3497 if ((*ppvObject)==0)
3498 return E_NOINTERFACE;
3501 * Query Interface always increases the reference count by one when it is
3504 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3509 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3510 IEnumSTATSTG* iface)
3512 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3518 ULONG WINAPI IEnumSTATSTGImpl_Release(
3519 IEnumSTATSTG* iface)
3521 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3529 * If the reference count goes down to 0, perform suicide.
3533 IEnumSTATSTGImpl_Destroy(This);
3539 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3540 IEnumSTATSTG* iface,
3543 ULONG* pceltFetched)
3545 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3547 StgProperty currentProperty;
3548 STATSTG* currentReturnStruct = rgelt;
3549 ULONG objectFetched = 0;
3550 ULONG currentSearchNode;
3553 * Perform a sanity check on the parameters.
3555 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3556 return E_INVALIDARG;
3559 * To avoid the special case, get another pointer to a ULONG value if
3560 * the caller didn't supply one.
3562 if (pceltFetched==0)
3563 pceltFetched = &objectFetched;
3566 * Start the iteration, we will iterate until we hit the end of the
3567 * linked list or until we hit the number of items to iterate through
3572 * Start with the node at the top of the stack.
3574 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3576 while ( ( *pceltFetched < celt) &&
3577 ( currentSearchNode!=PROPERTY_NULL) )
3580 * Remove the top node from the stack
3582 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3585 * Read the property from the storage.
3587 StorageImpl_ReadProperty(This->parentStorage,
3592 * Copy the information to the return buffer.
3594 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3599 * Step to the next item in the iteration
3602 currentReturnStruct++;
3605 * Push the next search node in the search stack.
3607 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3610 * continue the iteration.
3612 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3615 if (*pceltFetched == celt)
3622 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3623 IEnumSTATSTG* iface,
3626 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3628 StgProperty currentProperty;
3629 ULONG objectFetched = 0;
3630 ULONG currentSearchNode;
3633 * Start with the node at the top of the stack.
3635 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3637 while ( (objectFetched < celt) &&
3638 (currentSearchNode!=PROPERTY_NULL) )
3641 * Remove the top node from the stack
3643 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3646 * Read the property from the storage.
3648 StorageImpl_ReadProperty(This->parentStorage,
3653 * Step to the next item in the iteration
3658 * Push the next search node in the search stack.
3660 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3663 * continue the iteration.
3665 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3668 if (objectFetched == celt)
3674 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3675 IEnumSTATSTG* iface)
3677 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3679 StgProperty rootProperty;
3680 BOOL readSuccessful;
3683 * Re-initialize the search stack to an empty stack
3685 This->stackSize = 0;
3688 * Read the root property from the storage.
3690 readSuccessful = StorageImpl_ReadProperty(
3691 This->parentStorage,
3692 This->firstPropertyNode,
3697 assert(rootProperty.sizeOfNameString!=0);
3700 * Push the search node in the search stack.
3702 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3708 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3709 IEnumSTATSTG* iface,
3710 IEnumSTATSTG** ppenum)
3712 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3714 IEnumSTATSTGImpl* newClone;
3717 * Perform a sanity check on the parameters.
3720 return E_INVALIDARG;
3722 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3723 This->firstPropertyNode);
3727 * The new clone enumeration must point to the same current node as
3730 newClone->stackSize = This->stackSize ;
3731 newClone->stackMaxSize = This->stackMaxSize ;
3732 newClone->stackToVisit =
3733 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3736 newClone->stackToVisit,
3738 sizeof(ULONG) * newClone->stackSize);
3740 *ppenum = (IEnumSTATSTG*)newClone;
3743 * Don't forget to nail down a reference to the clone before
3746 IEnumSTATSTGImpl_AddRef(*ppenum);
3751 INT IEnumSTATSTGImpl_FindParentProperty(
3752 IEnumSTATSTGImpl *This,
3753 ULONG childProperty,
3754 StgProperty *currentProperty,
3757 ULONG currentSearchNode;
3761 * To avoid the special case, get another pointer to a ULONG value if
3762 * the caller didn't supply one.
3765 thisNodeId = &foundNode;
3768 * Start with the node at the top of the stack.
3770 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3773 while (currentSearchNode!=PROPERTY_NULL)
3776 * Store the current node in the returned parameters
3778 *thisNodeId = currentSearchNode;
3781 * Remove the top node from the stack
3783 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3786 * Read the property from the storage.
3788 StorageImpl_ReadProperty(
3789 This->parentStorage,
3793 if (currentProperty->previousProperty == childProperty)
3794 return PROPERTY_RELATION_PREVIOUS;
3796 else if (currentProperty->nextProperty == childProperty)
3797 return PROPERTY_RELATION_NEXT;
3799 else if (currentProperty->dirProperty == childProperty)
3800 return PROPERTY_RELATION_DIR;
3803 * Push the next search node in the search stack.
3805 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3808 * continue the iteration.
3810 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3813 return PROPERTY_NULL;
3816 ULONG IEnumSTATSTGImpl_FindProperty(
3817 IEnumSTATSTGImpl* This,
3818 const OLECHAR* lpszPropName,
3819 StgProperty* currentProperty)
3821 ULONG currentSearchNode;
3824 * Start with the node at the top of the stack.
3826 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3828 while (currentSearchNode!=PROPERTY_NULL)
3831 * Remove the top node from the stack
3833 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3836 * Read the property from the storage.
3838 StorageImpl_ReadProperty(This->parentStorage,
3842 if ( propertyNameCmp(
3843 (OLECHAR*)currentProperty->name,
3844 (OLECHAR*)lpszPropName) == 0)
3845 return currentSearchNode;
3848 * Push the next search node in the search stack.
3850 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3853 * continue the iteration.
3855 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3858 return PROPERTY_NULL;
3861 void IEnumSTATSTGImpl_PushSearchNode(
3862 IEnumSTATSTGImpl* This,
3865 StgProperty rootProperty;
3866 BOOL readSuccessful;
3869 * First, make sure we're not trying to push an unexisting node.
3871 if (nodeToPush==PROPERTY_NULL)
3875 * First push the node to the stack
3877 if (This->stackSize == This->stackMaxSize)
3879 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3881 This->stackToVisit = HeapReAlloc(
3885 sizeof(ULONG) * This->stackMaxSize);
3888 This->stackToVisit[This->stackSize] = nodeToPush;
3892 * Read the root property from the storage.
3894 readSuccessful = StorageImpl_ReadProperty(
3895 This->parentStorage,
3901 assert(rootProperty.sizeOfNameString!=0);
3904 * Push the previous search node in the search stack.
3906 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3910 ULONG IEnumSTATSTGImpl_PopSearchNode(
3911 IEnumSTATSTGImpl* This,
3916 if (This->stackSize == 0)
3917 return PROPERTY_NULL;
3919 topNode = This->stackToVisit[This->stackSize-1];
3927 /******************************************************************************
3928 ** StorageUtl implementation
3931 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
3933 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
3936 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
3938 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
3941 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
3943 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
3946 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
3948 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
3951 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
3953 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
3954 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
3955 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
3957 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
3960 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
3962 StorageUtl_WriteDWord(buffer, offset, value->Data1);
3963 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
3964 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
3966 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
3969 void StorageUtl_CopyPropertyToSTATSTG(
3970 STATSTG* destination,
3971 StgProperty* source,
3975 * The copy of the string occurs only when the flag is not set
3977 if ((statFlags & STATFLAG_NONAME) != 0)
3979 destination->pwcsName = 0;
3983 destination->pwcsName =
3984 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
3986 strcpyW((LPWSTR)destination->pwcsName, source->name);
3989 switch (source->propertyType)
3991 case PROPTYPE_STORAGE:
3993 destination->type = STGTY_STORAGE;
3995 case PROPTYPE_STREAM:
3996 destination->type = STGTY_STREAM;
3999 destination->type = STGTY_STREAM;
4003 destination->cbSize = source->size;
4005 currentReturnStruct->mtime = {0}; TODO
4006 currentReturnStruct->ctime = {0};
4007 currentReturnStruct->atime = {0};
4009 destination->grfMode = 0;
4010 destination->grfLocksSupported = 0;
4011 destination->clsid = source->propertyUniqueID;
4012 destination->grfStateBits = 0;
4013 destination->reserved = 0;
4016 /******************************************************************************
4017 ** BlockChainStream implementation
4020 BlockChainStream* BlockChainStream_Construct(
4021 StorageImpl* parentStorage,
4022 ULONG* headOfStreamPlaceHolder,
4023 ULONG propertyIndex)
4025 BlockChainStream* newStream;
4028 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4030 newStream->parentStorage = parentStorage;
4031 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4032 newStream->ownerPropertyIndex = propertyIndex;
4033 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4034 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4035 newStream->numBlocks = 0;
4037 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4039 while (blockIndex != BLOCK_END_OF_CHAIN)
4041 newStream->numBlocks++;
4042 newStream->tailIndex = blockIndex;
4044 blockIndex = StorageImpl_GetNextBlockInChain(
4052 void BlockChainStream_Destroy(BlockChainStream* This)
4054 HeapFree(GetProcessHeap(), 0, This);
4057 /******************************************************************************
4058 * BlockChainStream_GetHeadOfChain
4060 * Returns the head of this stream chain.
4061 * Some special chains don't have properties, their heads are kept in
4062 * This->headOfStreamPlaceHolder.
4065 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4067 StgProperty chainProperty;
4068 BOOL readSuccessful;
4070 if (This->headOfStreamPlaceHolder != 0)
4071 return *(This->headOfStreamPlaceHolder);
4073 if (This->ownerPropertyIndex != PROPERTY_NULL)
4075 readSuccessful = StorageImpl_ReadProperty(
4076 This->parentStorage,
4077 This->ownerPropertyIndex,
4082 return chainProperty.startingBlock;
4086 return BLOCK_END_OF_CHAIN;
4089 /******************************************************************************
4090 * BlockChainStream_GetCount
4092 * Returns the number of blocks that comprises this chain.
4093 * This is not the size of the stream as the last block may not be full!
4096 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4101 blockIndex = BlockChainStream_GetHeadOfChain(This);
4103 while (blockIndex != BLOCK_END_OF_CHAIN)
4107 blockIndex = StorageImpl_GetNextBlockInChain(
4108 This->parentStorage,
4115 /******************************************************************************
4116 * BlockChainStream_ReadAt
4118 * Reads a specified number of bytes from this chain at the specified offset.
4119 * bytesRead may be NULL.
4120 * Failure will be returned if the specified number of bytes has not been read.
4122 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4123 ULARGE_INTEGER offset,
4128 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4129 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4130 ULONG bytesToReadInBuffer;
4133 BYTE* bigBlockBuffer;
4136 * Find the first block in the stream that contains part of the buffer.
4138 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4139 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4140 (blockNoInSequence < This->lastBlockNoInSequence) )
4142 blockIndex = BlockChainStream_GetHeadOfChain(This);
4143 This->lastBlockNoInSequence = blockNoInSequence;
4147 ULONG temp = blockNoInSequence;
4149 blockIndex = This->lastBlockNoInSequenceIndex;
4150 blockNoInSequence -= This->lastBlockNoInSequence;
4151 This->lastBlockNoInSequence = temp;
4154 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4157 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4159 blockNoInSequence--;
4162 This->lastBlockNoInSequenceIndex = blockIndex;
4165 * Start reading the buffer.
4168 bufferWalker = buffer;
4170 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4173 * Calculate how many bytes we can copy from this big block.
4175 bytesToReadInBuffer =
4176 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4179 * Copy those bytes to the buffer
4182 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4184 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4186 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4189 * Step to the next big block.
4192 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4194 bufferWalker += bytesToReadInBuffer;
4195 size -= bytesToReadInBuffer;
4196 *bytesRead += bytesToReadInBuffer;
4197 offsetInBlock = 0; /* There is no offset on the next block */
4204 /******************************************************************************
4205 * BlockChainStream_WriteAt
4207 * Writes the specified number of bytes to this chain at the specified offset.
4208 * bytesWritten may be NULL.
4209 * Will fail if not all specified number of bytes have been written.
4211 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4212 ULARGE_INTEGER offset,
4215 ULONG* bytesWritten)
4217 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4218 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4222 BYTE* bigBlockBuffer;
4225 * Find the first block in the stream that contains part of the buffer.
4227 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4228 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4229 (blockNoInSequence < This->lastBlockNoInSequence) )
4231 blockIndex = BlockChainStream_GetHeadOfChain(This);
4232 This->lastBlockNoInSequence = blockNoInSequence;
4236 ULONG temp = blockNoInSequence;
4238 blockIndex = This->lastBlockNoInSequenceIndex;
4239 blockNoInSequence -= This->lastBlockNoInSequence;
4240 This->lastBlockNoInSequence = temp;
4243 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4246 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4248 blockNoInSequence--;
4251 This->lastBlockNoInSequenceIndex = blockIndex;
4254 * Here, I'm casting away the constness on the buffer variable
4255 * This is OK since we don't intend to modify that buffer.
4258 bufferWalker = (BYTE*)buffer;
4260 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4263 * Calculate how many bytes we can copy from this big block.
4266 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4269 * Copy those bytes to the buffer
4271 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4273 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4275 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4278 * Step to the next big block.
4281 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4283 bufferWalker += bytesToWrite;
4284 size -= bytesToWrite;
4285 *bytesWritten += bytesToWrite;
4286 offsetInBlock = 0; /* There is no offset on the next block */
4292 /******************************************************************************
4293 * BlockChainStream_Shrink
4295 * Shrinks this chain in the big block depot.
4297 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4298 ULARGE_INTEGER newSize)
4300 ULONG blockIndex, extraBlock;
4305 * Reset the last accessed block cache.
4307 This->lastBlockNoInSequence = 0xFFFFFFFF;
4308 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4311 * Figure out how many blocks are needed to contain the new size
4313 numBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4315 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4318 blockIndex = BlockChainStream_GetHeadOfChain(This);
4321 * Go to the new end of chain
4323 while (count < numBlocks)
4326 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4331 /* Get the next block before marking the new end */
4333 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4335 /* Mark the new end of chain */
4336 StorageImpl_SetNextBlockInChain(
4337 This->parentStorage,
4339 BLOCK_END_OF_CHAIN);
4341 This->tailIndex = blockIndex;
4342 This->numBlocks = numBlocks;
4345 * Mark the extra blocks as free
4347 while (extraBlock != BLOCK_END_OF_CHAIN)
4350 StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock);
4352 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4353 extraBlock = blockIndex;
4359 /******************************************************************************
4360 * BlockChainStream_Enlarge
4362 * Grows this chain in the big block depot.
4364 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4365 ULARGE_INTEGER newSize)
4367 ULONG blockIndex, currentBlock;
4369 ULONG oldNumBlocks = 0;
4371 blockIndex = BlockChainStream_GetHeadOfChain(This);
4374 * Empty chain. Create the head.
4376 if (blockIndex == BLOCK_END_OF_CHAIN)
4378 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4379 StorageImpl_SetNextBlockInChain(This->parentStorage,
4381 BLOCK_END_OF_CHAIN);
4383 if (This->headOfStreamPlaceHolder != 0)
4385 *(This->headOfStreamPlaceHolder) = blockIndex;
4389 StgProperty chainProp;
4390 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4392 StorageImpl_ReadProperty(
4393 This->parentStorage,
4394 This->ownerPropertyIndex,
4397 chainProp.startingBlock = blockIndex;
4399 StorageImpl_WriteProperty(
4400 This->parentStorage,
4401 This->ownerPropertyIndex,
4405 This->tailIndex = blockIndex;
4406 This->numBlocks = 1;
4410 * Figure out how many blocks are needed to contain this stream
4412 newNumBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4414 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4418 * Go to the current end of chain
4420 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4422 currentBlock = blockIndex;
4424 while (blockIndex != BLOCK_END_OF_CHAIN)
4427 currentBlock = blockIndex;
4430 StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock);
4433 This->tailIndex = currentBlock;
4436 currentBlock = This->tailIndex;
4437 oldNumBlocks = This->numBlocks;
4440 * Add new blocks to the chain
4442 if (oldNumBlocks < newNumBlocks)
4444 while (oldNumBlocks < newNumBlocks)
4446 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4448 StorageImpl_SetNextBlockInChain(
4449 This->parentStorage,
4453 StorageImpl_SetNextBlockInChain(
4454 This->parentStorage,
4456 BLOCK_END_OF_CHAIN);
4458 currentBlock = blockIndex;
4462 This->tailIndex = blockIndex;
4463 This->numBlocks = newNumBlocks;
4469 /******************************************************************************
4470 * BlockChainStream_SetSize
4472 * Sets the size of this stream. The big block depot will be updated.
4473 * The file will grow if we grow the chain.
4475 * TODO: Free the actual blocks in the file when we shrink the chain.
4476 * Currently, the blocks are still in the file. So the file size
4477 * doesn't shrink even if we shrink streams.
4479 BOOL BlockChainStream_SetSize(
4480 BlockChainStream* This,
4481 ULARGE_INTEGER newSize)
4483 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4485 if (newSize.s.LowPart == size.s.LowPart)
4488 if (newSize.s.LowPart < size.s.LowPart)
4490 BlockChainStream_Shrink(This, newSize);
4494 ULARGE_INTEGER fileSize =
4495 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4497 ULONG diff = newSize.s.LowPart - size.s.LowPart;
4500 * Make sure the file stays a multiple of blocksize
4502 if ((diff % This->parentStorage->bigBlockSize) != 0)
4503 diff += (This->parentStorage->bigBlockSize -
4504 (diff % This->parentStorage->bigBlockSize) );
4506 fileSize.s.LowPart += diff;
4507 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4509 BlockChainStream_Enlarge(This, newSize);
4515 /******************************************************************************
4516 * BlockChainStream_GetSize
4518 * Returns the size of this chain.
4519 * Will return the block count if this chain doesn't have a property.
4521 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4523 StgProperty chainProperty;
4525 if(This->headOfStreamPlaceHolder == NULL)
4528 * This chain is a data stream read the property and return
4529 * the appropriate size
4531 StorageImpl_ReadProperty(
4532 This->parentStorage,
4533 This->ownerPropertyIndex,
4536 return chainProperty.size;
4541 * this chain is a chain that does not have a property, figure out the
4542 * size by making the product number of used blocks times the
4545 ULARGE_INTEGER result;
4546 result.s.HighPart = 0;
4549 BlockChainStream_GetCount(This) *
4550 This->parentStorage->bigBlockSize;
4556 /******************************************************************************
4557 ** SmallBlockChainStream implementation
4560 SmallBlockChainStream* SmallBlockChainStream_Construct(
4561 StorageImpl* parentStorage,
4562 ULONG propertyIndex)
4564 SmallBlockChainStream* newStream;
4566 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4568 newStream->parentStorage = parentStorage;
4569 newStream->ownerPropertyIndex = propertyIndex;
4574 void SmallBlockChainStream_Destroy(
4575 SmallBlockChainStream* This)
4577 HeapFree(GetProcessHeap(), 0, This);
4580 /******************************************************************************
4581 * SmallBlockChainStream_GetHeadOfChain
4583 * Returns the head of this chain of small blocks.
4585 ULONG SmallBlockChainStream_GetHeadOfChain(
4586 SmallBlockChainStream* This)
4588 StgProperty chainProperty;
4589 BOOL readSuccessful;
4591 if (This->ownerPropertyIndex)
4593 readSuccessful = StorageImpl_ReadProperty(
4594 This->parentStorage,
4595 This->ownerPropertyIndex,
4600 return chainProperty.startingBlock;
4605 return BLOCK_END_OF_CHAIN;
4608 /******************************************************************************
4609 * SmallBlockChainStream_GetNextBlockInChain
4611 * Returns the index of the next small block in this chain.
4614 * - BLOCK_END_OF_CHAIN: end of this chain
4615 * - BLOCK_UNUSED: small block 'blockIndex' is free
4617 ULONG SmallBlockChainStream_GetNextBlockInChain(
4618 SmallBlockChainStream* This,
4621 ULARGE_INTEGER offsetOfBlockInDepot;
4623 ULONG nextBlockInChain = BLOCK_END_OF_CHAIN;
4627 offsetOfBlockInDepot.s.HighPart = 0;
4628 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4631 * Read those bytes in the buffer from the small block file.
4633 success = BlockChainStream_ReadAt(
4634 This->parentStorage->smallBlockDepotChain,
4635 offsetOfBlockInDepot,
4642 StorageUtl_ReadDWord(&buffer, 0, &nextBlockInChain);
4645 return nextBlockInChain;
4648 /******************************************************************************
4649 * SmallBlockChainStream_SetNextBlockInChain
4651 * Writes the index of the next block of the specified block in the small
4653 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4654 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4656 void SmallBlockChainStream_SetNextBlockInChain(
4657 SmallBlockChainStream* This,
4661 ULARGE_INTEGER offsetOfBlockInDepot;
4665 offsetOfBlockInDepot.s.HighPart = 0;
4666 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4668 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4671 * Read those bytes in the buffer from the small block file.
4673 BlockChainStream_WriteAt(
4674 This->parentStorage->smallBlockDepotChain,
4675 offsetOfBlockInDepot,
4681 /******************************************************************************
4682 * SmallBlockChainStream_FreeBlock
4684 * Flag small block 'blockIndex' as free in the small block depot.
4686 void SmallBlockChainStream_FreeBlock(
4687 SmallBlockChainStream* This,
4690 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4693 /******************************************************************************
4694 * SmallBlockChainStream_GetNextFreeBlock
4696 * Returns the index of a free small block. The small block depot will be
4697 * enlarged if necessary. The small block chain will also be enlarged if
4700 ULONG SmallBlockChainStream_GetNextFreeBlock(
4701 SmallBlockChainStream* This)
4703 ULARGE_INTEGER offsetOfBlockInDepot;
4706 ULONG blockIndex = 0;
4707 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4708 BOOL success = TRUE;
4709 ULONG smallBlocksPerBigBlock;
4711 offsetOfBlockInDepot.s.HighPart = 0;
4714 * Scan the small block depot for a free block
4716 while (nextBlockIndex != BLOCK_UNUSED)
4718 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4720 success = BlockChainStream_ReadAt(
4721 This->parentStorage->smallBlockDepotChain,
4722 offsetOfBlockInDepot,
4728 * If we run out of space for the small block depot, enlarge it
4732 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4734 if (nextBlockIndex != BLOCK_UNUSED)
4740 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4742 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4743 ULONG nextBlock, newsbdIndex;
4744 BYTE* smallBlockDepot;
4746 nextBlock = sbdIndex;
4747 while (nextBlock != BLOCK_END_OF_CHAIN)
4749 sbdIndex = nextBlock;
4751 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex);
4754 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4755 if (sbdIndex != BLOCK_END_OF_CHAIN)
4756 StorageImpl_SetNextBlockInChain(
4757 This->parentStorage,
4761 StorageImpl_SetNextBlockInChain(
4762 This->parentStorage,
4764 BLOCK_END_OF_CHAIN);
4767 * Initialize all the small blocks to free
4770 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4772 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4773 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4778 * We have just created the small block depot.
4780 StgProperty rootProp;
4784 * Save it in the header
4786 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4787 StorageImpl_SaveFileHeader(This->parentStorage);
4790 * And allocate the first big block that will contain small blocks
4793 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4795 StorageImpl_SetNextBlockInChain(
4796 This->parentStorage,
4798 BLOCK_END_OF_CHAIN);
4800 StorageImpl_ReadProperty(
4801 This->parentStorage,
4802 This->parentStorage->rootPropertySetIndex,
4805 rootProp.startingBlock = sbStartIndex;
4806 rootProp.size.s.HighPart = 0;
4807 rootProp.size.s.LowPart = This->parentStorage->bigBlockSize;
4809 StorageImpl_WriteProperty(
4810 This->parentStorage,
4811 This->parentStorage->rootPropertySetIndex,
4817 smallBlocksPerBigBlock =
4818 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4821 * Verify if we have to allocate big blocks to contain small blocks
4823 if (blockIndex % smallBlocksPerBigBlock == 0)
4825 StgProperty rootProp;
4826 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4828 StorageImpl_ReadProperty(
4829 This->parentStorage,
4830 This->parentStorage->rootPropertySetIndex,
4833 if (rootProp.size.s.LowPart <
4834 (blocksRequired * This->parentStorage->bigBlockSize))
4836 rootProp.size.s.LowPart += This->parentStorage->bigBlockSize;
4838 BlockChainStream_SetSize(
4839 This->parentStorage->smallBlockRootChain,
4842 StorageImpl_WriteProperty(
4843 This->parentStorage,
4844 This->parentStorage->rootPropertySetIndex,
4852 /******************************************************************************
4853 * SmallBlockChainStream_ReadAt
4855 * Reads a specified number of bytes from this chain at the specified offset.
4856 * bytesRead may be NULL.
4857 * Failure will be returned if the specified number of bytes has not been read.
4859 BOOL SmallBlockChainStream_ReadAt(
4860 SmallBlockChainStream* This,
4861 ULARGE_INTEGER offset,
4866 ULARGE_INTEGER offsetInBigBlockFile;
4867 ULONG blockNoInSequence =
4868 offset.s.LowPart / This->parentStorage->smallBlockSize;
4870 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
4871 ULONG bytesToReadInBuffer;
4873 ULONG bytesReadFromBigBlockFile;
4877 * This should never happen on a small block file.
4879 assert(offset.s.HighPart==0);
4882 * Find the first block in the stream that contains part of the buffer.
4884 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4886 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4888 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4890 blockNoInSequence--;
4894 * Start reading the buffer.
4897 bufferWalker = buffer;
4899 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4902 * Calculate how many bytes we can copy from this small block.
4904 bytesToReadInBuffer =
4905 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
4908 * Calculate the offset of the small block in the small block file.
4910 offsetInBigBlockFile.s.HighPart = 0;
4911 offsetInBigBlockFile.s.LowPart =
4912 blockIndex * This->parentStorage->smallBlockSize;
4914 offsetInBigBlockFile.s.LowPart += offsetInBlock;
4917 * Read those bytes in the buffer from the small block file.
4919 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
4920 offsetInBigBlockFile,
4921 bytesToReadInBuffer,
4923 &bytesReadFromBigBlockFile);
4925 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
4928 * Step to the next big block.
4930 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4931 bufferWalker += bytesToReadInBuffer;
4932 size -= bytesToReadInBuffer;
4933 *bytesRead += bytesToReadInBuffer;
4934 offsetInBlock = 0; /* There is no offset on the next block */
4940 /******************************************************************************
4941 * SmallBlockChainStream_WriteAt
4943 * Writes the specified number of bytes to this chain at the specified offset.
4944 * bytesWritten may be NULL.
4945 * Will fail if not all specified number of bytes have been written.
4947 BOOL SmallBlockChainStream_WriteAt(
4948 SmallBlockChainStream* This,
4949 ULARGE_INTEGER offset,
4952 ULONG* bytesWritten)
4954 ULARGE_INTEGER offsetInBigBlockFile;
4955 ULONG blockNoInSequence =
4956 offset.s.LowPart / This->parentStorage->smallBlockSize;
4958 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
4959 ULONG bytesToWriteInBuffer;
4961 ULONG bytesWrittenFromBigBlockFile;
4965 * This should never happen on a small block file.
4967 assert(offset.s.HighPart==0);
4970 * Find the first block in the stream that contains part of the buffer.
4972 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4974 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4976 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4978 blockNoInSequence--;
4982 * Start writing the buffer.
4984 * Here, I'm casting away the constness on the buffer variable
4985 * This is OK since we don't intend to modify that buffer.
4988 bufferWalker = (BYTE*)buffer;
4989 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4992 * Calculate how many bytes we can copy to this small block.
4994 bytesToWriteInBuffer =
4995 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
4998 * Calculate the offset of the small block in the small block file.
5000 offsetInBigBlockFile.s.HighPart = 0;
5001 offsetInBigBlockFile.s.LowPart =
5002 blockIndex * This->parentStorage->smallBlockSize;
5004 offsetInBigBlockFile.s.LowPart += offsetInBlock;
5007 * Write those bytes in the buffer to the small block file.
5009 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5010 offsetInBigBlockFile,
5011 bytesToWriteInBuffer,
5013 &bytesWrittenFromBigBlockFile);
5015 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5018 * Step to the next big block.
5020 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5021 bufferWalker += bytesToWriteInBuffer;
5022 size -= bytesToWriteInBuffer;
5023 *bytesWritten += bytesToWriteInBuffer;
5024 offsetInBlock = 0; /* There is no offset on the next block */
5030 /******************************************************************************
5031 * SmallBlockChainStream_Shrink
5033 * Shrinks this chain in the small block depot.
5035 BOOL SmallBlockChainStream_Shrink(
5036 SmallBlockChainStream* This,
5037 ULARGE_INTEGER newSize)
5039 ULONG blockIndex, extraBlock;
5043 numBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5045 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5048 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5051 * Go to the new end of chain
5053 while (count < numBlocks)
5055 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5060 * If the count is 0, we have a special case, the head of the chain was
5065 StgProperty chainProp;
5067 StorageImpl_ReadProperty(This->parentStorage,
5068 This->ownerPropertyIndex,
5071 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5073 StorageImpl_WriteProperty(This->parentStorage,
5074 This->ownerPropertyIndex,
5078 * We start freeing the chain at the head block.
5080 extraBlock = blockIndex;
5084 /* Get the next block before marking the new end */
5085 extraBlock = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5087 /* Mark the new end of chain */
5088 SmallBlockChainStream_SetNextBlockInChain(
5091 BLOCK_END_OF_CHAIN);
5095 * Mark the extra blocks as free
5097 while (extraBlock != BLOCK_END_OF_CHAIN)
5099 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, extraBlock);
5100 SmallBlockChainStream_FreeBlock(This, extraBlock);
5101 extraBlock = blockIndex;
5107 /******************************************************************************
5108 * SmallBlockChainStream_Enlarge
5110 * Grows this chain in the small block depot.
5112 BOOL SmallBlockChainStream_Enlarge(
5113 SmallBlockChainStream* This,
5114 ULARGE_INTEGER newSize)
5116 ULONG blockIndex, currentBlock;
5118 ULONG oldNumBlocks = 0;
5120 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5125 if (blockIndex == BLOCK_END_OF_CHAIN)
5128 StgProperty chainProp;
5130 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5133 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5135 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5138 blockIndex = chainProp.startingBlock;
5139 SmallBlockChainStream_SetNextBlockInChain(
5142 BLOCK_END_OF_CHAIN);
5145 currentBlock = blockIndex;
5148 * Figure out how many blocks are needed to contain this stream
5150 newNumBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5152 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5156 * Go to the current end of chain
5158 while (blockIndex != BLOCK_END_OF_CHAIN)
5161 currentBlock = blockIndex;
5162 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, currentBlock);
5166 * Add new blocks to the chain
5168 while (oldNumBlocks < newNumBlocks)
5170 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5171 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5173 SmallBlockChainStream_SetNextBlockInChain(
5176 BLOCK_END_OF_CHAIN);
5178 currentBlock = blockIndex;
5185 /******************************************************************************
5186 * SmallBlockChainStream_GetCount
5188 * Returns the number of blocks that comprises this chain.
5189 * This is not the size of this chain as the last block may not be full!
5191 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5196 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5198 while (blockIndex != BLOCK_END_OF_CHAIN)
5202 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5208 /******************************************************************************
5209 * SmallBlockChainStream_SetSize
5211 * Sets the size of this stream.
5212 * The file will grow if we grow the chain.
5214 * TODO: Free the actual blocks in the file when we shrink the chain.
5215 * Currently, the blocks are still in the file. So the file size
5216 * doesn't shrink even if we shrink streams.
5218 BOOL SmallBlockChainStream_SetSize(
5219 SmallBlockChainStream* This,
5220 ULARGE_INTEGER newSize)
5222 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5224 if (newSize.s.LowPart == size.s.LowPart)
5227 if (newSize.s.LowPart < size.s.LowPart)
5229 SmallBlockChainStream_Shrink(This, newSize);
5233 SmallBlockChainStream_Enlarge(This, newSize);
5239 /******************************************************************************
5240 * SmallBlockChainStream_GetSize
5242 * Returns the size of this chain.
5244 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5246 StgProperty chainProperty;
5248 StorageImpl_ReadProperty(
5249 This->parentStorage,
5250 This->ownerPropertyIndex,
5253 return chainProperty.size;
5256 /******************************************************************************
5257 * StgCreateDocfile [OLE32.144]
5259 HRESULT WINAPI StgCreateDocfile(
5263 IStorage **ppstgOpen)
5265 StorageImpl* newStorage = 0;
5266 HANDLE hFile = INVALID_HANDLE_VALUE;
5271 DWORD fileAttributes;
5272 WCHAR tempFileName[MAX_PATH];
5274 TRACE("(%s, %lx, %ld, %p)\n",
5275 debugstr_w(pwcsName), grfMode,
5276 reserved, ppstgOpen);
5279 * Validate the parameters
5282 return STG_E_INVALIDPOINTER;
5285 * Validate the STGM flags
5287 if ( FAILED( validateSTGM(grfMode) ))
5288 return STG_E_INVALIDFLAG;
5291 * Generate a unique name.
5295 WCHAR tempPath[MAX_PATH];
5296 WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5298 memset(tempPath, 0, sizeof(tempPath));
5299 memset(tempFileName, 0, sizeof(tempFileName));
5301 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5304 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5305 pwcsName = tempFileName;
5307 return STG_E_INSUFFICIENTMEMORY;
5311 * Interpret the STGM value grfMode
5313 shareMode = GetShareModeFromSTGM(grfMode);
5314 accessMode = GetAccessModeFromSTGM(grfMode);
5315 creationMode = GetCreationModeFromSTGM(grfMode);
5317 if (grfMode & STGM_DELETEONRELEASE)
5318 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5320 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5322 if (grfMode & STGM_TRANSACTED)
5323 FIXME("Transacted mode not implemented.\n");
5326 * Initialize the "out" parameter.
5330 hFile = CreateFileW(pwcsName,
5338 if (hFile == INVALID_HANDLE_VALUE)
5344 * Allocate and initialize the new IStorage32object.
5346 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5348 if (newStorage == 0)
5349 return STG_E_INSUFFICIENTMEMORY;
5351 hr = StorageImpl_Construct(
5361 HeapFree(GetProcessHeap(), 0, newStorage);
5366 * Get an "out" pointer for the caller.
5368 hr = StorageBaseImpl_QueryInterface(
5369 (IStorage*)newStorage,
5370 (REFIID)&IID_IStorage,
5376 /******************************************************************************
5377 * StgOpenStorage [OLE32.148]
5379 HRESULT WINAPI StgOpenStorage(
5380 const OLECHAR *pwcsName,
5381 IStorage *pstgPriority,
5385 IStorage **ppstgOpen)
5387 StorageImpl* newStorage = 0;
5393 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5394 debugstr_w(pwcsName), pstgPriority, grfMode,
5395 snbExclude, reserved, ppstgOpen);
5398 * Perform a sanity check
5400 if (( pwcsName == 0) || (ppstgOpen == 0) )
5401 return STG_E_INVALIDPOINTER;
5404 * Validate the STGM flags
5406 if ( FAILED( validateSTGM(grfMode) ))
5407 return STG_E_INVALIDFLAG;
5410 * Interpret the STGM value grfMode
5412 shareMode = GetShareModeFromSTGM(grfMode);
5413 accessMode = GetAccessModeFromSTGM(grfMode);
5416 * Initialize the "out" parameter.
5420 hFile = CreateFileW( pwcsName,
5425 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5429 if (hFile==INVALID_HANDLE_VALUE)
5431 HRESULT hr = E_FAIL;
5432 DWORD last_error = GetLastError();
5436 case ERROR_FILE_NOT_FOUND:
5437 hr = STG_E_FILENOTFOUND;
5440 case ERROR_PATH_NOT_FOUND:
5441 hr = STG_E_PATHNOTFOUND;
5444 case ERROR_ACCESS_DENIED:
5445 case ERROR_WRITE_PROTECT:
5446 hr = STG_E_ACCESSDENIED;
5449 case ERROR_SHARING_VIOLATION:
5450 hr = STG_E_SHAREVIOLATION;
5461 * Allocate and initialize the new IStorage32object.
5463 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5465 if (newStorage == 0)
5466 return STG_E_INSUFFICIENTMEMORY;
5468 hr = StorageImpl_Construct(
5478 HeapFree(GetProcessHeap(), 0, newStorage);
5483 * Get an "out" pointer for the caller.
5485 hr = StorageBaseImpl_QueryInterface(
5486 (IStorage*)newStorage,
5487 (REFIID)&IID_IStorage,
5493 /******************************************************************************
5494 * StgCreateDocfileOnILockBytes [OLE32.145]
5496 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5500 IStorage** ppstgOpen)
5502 StorageImpl* newStorage = 0;
5506 * Validate the parameters
5508 if ((ppstgOpen == 0) || (plkbyt == 0))
5509 return STG_E_INVALIDPOINTER;
5512 * Allocate and initialize the new IStorage object.
5514 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5516 if (newStorage == 0)
5517 return STG_E_INSUFFICIENTMEMORY;
5519 hr = StorageImpl_Construct(
5529 HeapFree(GetProcessHeap(), 0, newStorage);
5534 * Get an "out" pointer for the caller.
5536 hr = StorageBaseImpl_QueryInterface(
5537 (IStorage*)newStorage,
5538 (REFIID)&IID_IStorage,
5544 /******************************************************************************
5545 * StgOpenStorageOnILockBytes [OLE32.149]
5547 HRESULT WINAPI StgOpenStorageOnILockBytes(
5549 IStorage *pstgPriority,
5553 IStorage **ppstgOpen)
5555 StorageImpl* newStorage = 0;
5559 * Perform a sanity check
5561 if ((plkbyt == 0) || (ppstgOpen == 0))
5562 return STG_E_INVALIDPOINTER;
5565 * Validate the STGM flags
5567 if ( FAILED( validateSTGM(grfMode) ))
5568 return STG_E_INVALIDFLAG;
5571 * Initialize the "out" parameter.
5576 * Allocate and initialize the new IStorage object.
5578 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5580 if (newStorage == 0)
5581 return STG_E_INSUFFICIENTMEMORY;
5583 hr = StorageImpl_Construct(
5593 HeapFree(GetProcessHeap(), 0, newStorage);
5598 * Get an "out" pointer for the caller.
5600 hr = StorageBaseImpl_QueryInterface(
5601 (IStorage*)newStorage,
5602 (REFIID)&IID_IStorage,
5608 /******************************************************************************
5609 * StgSetTimes [ole32.150]
5613 HRESULT WINAPI StgSetTimes(WCHAR * str, FILETIME * a, FILETIME * b, FILETIME *c )
5616 FIXME("(%p, %p, %p, %p),stub!\n", str, a, b, c);
5620 /******************************************************************************
5621 * StgIsStorageILockBytes [OLE32.147]
5623 * Determines if the ILockBytes contains a storage object.
5625 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5628 ULARGE_INTEGER offset;
5630 offset.s.HighPart = 0;
5631 offset.s.LowPart = 0;
5633 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5635 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5641 /******************************************************************************
5642 * WriteClassStg [OLE32.158]
5644 * This method will store the specified CLSID in the specified storage object
5646 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5652 hRes = IStorage_SetClass(pStg, rclsid);
5657 /***********************************************************************
5660 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5662 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5672 * read a STATSTG structure (contains the clsid) from the storage
5674 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5677 *pclsid=pstatstg.clsid;
5682 /***********************************************************************
5685 * This function loads an object from stream
5687 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5692 FIXME("(),stub!\n");
5694 res=ReadClassStm(pStm,&clsid);
5696 if (SUCCEEDED(res)){
5698 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5702 res=IPersistStream_Load((IPersistStream*)ppvObj,pStm);
5708 /***********************************************************************
5711 * This function saves an object with the IPersistStream interface on it
5712 * to the specified stream.
5714 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5720 TRACE("(%p,%p)\n",pPStm,pStm);
5722 res=IPersistStream_GetClassID(pPStm,&clsid);
5724 if (SUCCEEDED(res)){
5726 res=WriteClassStm(pStm,&clsid);
5730 res=IPersistStream_Save(pPStm,pStm,TRUE);
5733 TRACE("Finished Save\n");
5737 /****************************************************************************
5738 * This method validate a STGM parameter that can contain the values below
5740 * STGM_DIRECT 0x00000000
5741 * STGM_TRANSACTED 0x00010000
5742 * STGM_SIMPLE 0x08000000
5744 * STGM_READ 0x00000000
5745 * STGM_WRITE 0x00000001
5746 * STGM_READWRITE 0x00000002
5748 * STGM_SHARE_DENY_NONE 0x00000040
5749 * STGM_SHARE_DENY_READ 0x00000030
5750 * STGM_SHARE_DENY_WRITE 0x00000020
5751 * STGM_SHARE_EXCLUSIVE 0x00000010
5753 * STGM_PRIORITY 0x00040000
5754 * STGM_DELETEONRELEASE 0x04000000
5756 * STGM_CREATE 0x00001000
5757 * STGM_CONVERT 0x00020000
5758 * STGM_FAILIFTHERE 0x00000000
5760 * STGM_NOSCRATCH 0x00100000
5761 * STGM_NOSNAPSHOT 0x00200000
5763 static HRESULT validateSTGM(DWORD stgm)
5765 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5766 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5767 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5769 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5770 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5771 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5773 BOOL bSTGM_SHARE_DENY_NONE =
5774 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5776 BOOL bSTGM_SHARE_DENY_READ =
5777 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5779 BOOL bSTGM_SHARE_DENY_WRITE =
5780 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5782 BOOL bSTGM_SHARE_EXCLUSIVE =
5783 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5785 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5786 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5788 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5789 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5792 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5794 if ( ! bSTGM_DIRECT )
5795 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5799 * STGM_WRITE | STGM_READWRITE | STGM_READ
5802 if( bSTGM_WRITE && bSTGM_READWRITE )
5806 * STGM_SHARE_DENY_NONE | others
5807 * (I assume here that DENY_READ implies DENY_WRITE)
5809 if ( bSTGM_SHARE_DENY_NONE )
5810 if ( bSTGM_SHARE_DENY_READ ||
5811 bSTGM_SHARE_DENY_WRITE ||
5812 bSTGM_SHARE_EXCLUSIVE)
5816 * STGM_CREATE | STGM_CONVERT
5817 * if both are false, STGM_FAILIFTHERE is set to TRUE
5819 if ( bSTGM_CREATE && bSTGM_CONVERT )
5823 * STGM_NOSCRATCH requires STGM_TRANSACTED
5825 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
5829 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
5830 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5832 if (bSTGM_NOSNAPSHOT)
5834 if ( ! ( bSTGM_TRANSACTED &&
5835 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
5842 /****************************************************************************
5843 * GetShareModeFromSTGM
5845 * This method will return a share mode flag from a STGM value.
5846 * The STGM value is assumed valid.
5848 static DWORD GetShareModeFromSTGM(DWORD stgm)
5850 DWORD dwShareMode = 0;
5851 BOOL bSTGM_SHARE_DENY_NONE =
5852 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5854 BOOL bSTGM_SHARE_DENY_READ =
5855 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5857 BOOL bSTGM_SHARE_DENY_WRITE =
5858 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5860 BOOL bSTGM_SHARE_EXCLUSIVE =
5861 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5863 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
5866 if (bSTGM_SHARE_DENY_NONE)
5867 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5869 if (bSTGM_SHARE_DENY_WRITE)
5870 dwShareMode = FILE_SHARE_READ;
5875 /****************************************************************************
5876 * GetAccessModeFromSTGM
5878 * This method will return an access mode flag from a STGM value.
5879 * The STGM value is assumed valid.
5881 static DWORD GetAccessModeFromSTGM(DWORD stgm)
5883 DWORD dwDesiredAccess = GENERIC_READ;
5884 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5885 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5886 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5889 dwDesiredAccess = GENERIC_READ;
5892 dwDesiredAccess |= GENERIC_WRITE;
5894 if (bSTGM_READWRITE)
5895 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
5897 return dwDesiredAccess;
5900 /****************************************************************************
5901 * GetCreationModeFromSTGM
5903 * This method will return a creation mode flag from a STGM value.
5904 * The STGM value is assumed valid.
5906 static DWORD GetCreationModeFromSTGM(DWORD stgm)
5908 if ( stgm & STGM_CREATE)
5909 return CREATE_ALWAYS;
5910 if (stgm & STGM_CONVERT) {
5911 FIXME("STGM_CONVERT not implemented!\n");
5914 /* All other cases */
5915 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
5916 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
5921 /*************************************************************************
5922 * OLECONVERT_LoadOLE10 [Internal]
5924 * Loads the OLE10 STREAM to memory
5927 * pOleStream [I] The OLESTREAM
5928 * pData [I] Data Structure for the OLESTREAM Data
5932 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
5933 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
5936 * This function is used by OleConvertOLESTREAMToIStorage only.
5938 * Memory allocated for pData must be freed by the caller
5940 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
5943 HRESULT hRes = S_OK;
5947 pData->pData = NULL;
5948 pData->pstrOleObjFileName = (CHAR *) NULL;
5950 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
5953 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
5954 if(dwSize != sizeof(pData->dwOleID))
5956 hRes = CONVERT10_E_OLESTREAM_GET;
5958 else if(pData->dwOleID != OLESTREAM_ID)
5960 hRes = CONVERT10_E_OLESTREAM_FMT;
5971 /* Get the TypeID...more info needed for this field */
5972 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
5973 if(dwSize != sizeof(pData->dwTypeID))
5975 hRes = CONVERT10_E_OLESTREAM_GET;
5980 if(pData->dwTypeID != 0)
5982 /* Get the lenght of the OleTypeName */
5983 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
5984 if(dwSize != sizeof(pData->dwOleTypeNameLength))
5986 hRes = CONVERT10_E_OLESTREAM_GET;
5991 if(pData->dwOleTypeNameLength > 0)
5993 /* Get the OleTypeName */
5994 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
5995 if(dwSize != pData->dwOleTypeNameLength)
5997 hRes = CONVERT10_E_OLESTREAM_GET;
6003 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6004 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6006 hRes = CONVERT10_E_OLESTREAM_GET;
6010 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6011 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6012 pData->pstrOleObjFileName = (CHAR *)malloc(pData->dwOleObjFileNameLength);
6013 if(pData->pstrOleObjFileName)
6015 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6016 if(dwSize != pData->dwOleObjFileNameLength)
6018 hRes = CONVERT10_E_OLESTREAM_GET;
6022 hRes = CONVERT10_E_OLESTREAM_GET;
6027 /* Get the Width of the Metafile */
6028 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6029 if(dwSize != sizeof(pData->dwMetaFileWidth))
6031 hRes = CONVERT10_E_OLESTREAM_GET;
6035 /* Get the Height of the Metafile */
6036 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6037 if(dwSize != sizeof(pData->dwMetaFileHeight))
6039 hRes = CONVERT10_E_OLESTREAM_GET;
6045 /* Get the Lenght of the Data */
6046 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6047 if(dwSize != sizeof(pData->dwDataLength))
6049 hRes = CONVERT10_E_OLESTREAM_GET;
6053 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6055 if(!bStrem1) /* if it is a second OLE stream data */
6057 pData->dwDataLength -= 8;
6058 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6059 if(dwSize != sizeof(pData->strUnknown))
6061 hRes = CONVERT10_E_OLESTREAM_GET;
6067 if(pData->dwDataLength > 0)
6069 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6071 /* Get Data (ex. IStorage, Metafile, or BMP) */
6074 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6075 if(dwSize != pData->dwDataLength)
6077 hRes = CONVERT10_E_OLESTREAM_GET;
6082 hRes = CONVERT10_E_OLESTREAM_GET;
6091 /*************************************************************************
6092 * OLECONVERT_SaveOLE10 [Internal]
6094 * Saves the OLE10 STREAM From memory
6097 * pData [I] Data Structure for the OLESTREAM Data
6098 * pOleStream [I] The OLESTREAM to save
6102 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6105 * This function is used by OleConvertIStorageToOLESTREAM only.
6108 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6111 HRESULT hRes = S_OK;
6115 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6116 if(dwSize != sizeof(pData->dwOleID))
6118 hRes = CONVERT10_E_OLESTREAM_PUT;
6123 /* Set the TypeID */
6124 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6125 if(dwSize != sizeof(pData->dwTypeID))
6127 hRes = CONVERT10_E_OLESTREAM_PUT;
6131 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6133 /* Set the Lenght of the OleTypeName */
6134 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6135 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6137 hRes = CONVERT10_E_OLESTREAM_PUT;
6142 if(pData->dwOleTypeNameLength > 0)
6144 /* Set the OleTypeName */
6145 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6146 if(dwSize != pData->dwOleTypeNameLength)
6148 hRes = CONVERT10_E_OLESTREAM_PUT;
6155 /* Set the width of the Metafile */
6156 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6157 if(dwSize != sizeof(pData->dwMetaFileWidth))
6159 hRes = CONVERT10_E_OLESTREAM_PUT;
6165 /* Set the height of the Metafile */
6166 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6167 if(dwSize != sizeof(pData->dwMetaFileHeight))
6169 hRes = CONVERT10_E_OLESTREAM_PUT;
6175 /* Set the lenght of the Data */
6176 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6177 if(dwSize != sizeof(pData->dwDataLength))
6179 hRes = CONVERT10_E_OLESTREAM_PUT;
6185 if(pData->dwDataLength > 0)
6187 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6188 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6189 if(dwSize != pData->dwDataLength)
6191 hRes = CONVERT10_E_OLESTREAM_PUT;
6199 /*************************************************************************
6200 * OLECONVERT_GetOLE20FromOLE10[Internal]
6202 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6203 * opens it, and copies the content to the dest IStorage for
6204 * OleConvertOLESTREAMToIStorage
6208 * pDestStorage [I] The IStorage to copy the data to
6209 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6210 * nBufferLength [I] The size of the buffer
6219 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6223 IStorage *pTempStorage;
6224 DWORD dwNumOfBytesWritten;
6225 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6226 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6228 /* Create a temp File */
6229 GetTempPathW(MAX_PATH, wstrTempDir);
6230 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6231 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6233 if(hFile != INVALID_HANDLE_VALUE)
6235 /* Write IStorage Data to File */
6236 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6239 /* Open and copy temp storage to the Dest Storage */
6240 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6243 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6244 StorageBaseImpl_Release(pTempStorage);
6246 DeleteFileW(wstrTempFile);
6251 /*************************************************************************
6252 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6254 * Saves the OLE10 STREAM From memory
6257 * pStorage [I] The Src IStorage to copy
6258 * pData [I] The Dest Memory to write to.
6261 * The size in bytes allocated for pData
6264 * Memory allocated for pData must be freed by the caller
6266 * Used by OleConvertIStorageToOLESTREAM only.
6269 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6273 DWORD nDataLength = 0;
6274 IStorage *pTempStorage;
6275 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6276 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6280 /* Create temp Storage */
6281 GetTempPathW(MAX_PATH, wstrTempDir);
6282 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6283 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6287 /* Copy Src Storage to the Temp Storage */
6288 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6289 StorageBaseImpl_Release(pTempStorage);
6291 /* Open Temp Storage as a file and copy to memory */
6292 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6293 if(hFile != INVALID_HANDLE_VALUE)
6295 nDataLength = GetFileSize(hFile, NULL);
6296 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6297 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6300 DeleteFileW(wstrTempFile);
6305 /*************************************************************************
6306 * OLECONVERT_CreateOleStream [Internal]
6308 * Creates the "\001OLE" stream in the IStorage if neccessary.
6311 * pStorage [I] Dest storage to create the stream in
6317 * This function is used by OleConvertOLESTREAMToIStorage only.
6319 * This stream is still unknown, MS Word seems to have extra data
6320 * but since the data is stored in the OLESTREAM there should be
6321 * no need to recreate the stream. If the stream is manually
6322 * deleted it will create it with this default data.
6325 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6329 WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6330 BYTE pOleStreamHeader [] =
6332 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6333 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6334 0x00, 0x00, 0x00, 0x00
6337 /* Create stream if not present */
6338 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6339 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6343 /* Write default Data */
6344 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6345 IStream_Release(pStream);
6350 /*************************************************************************
6351 * OLECONVERT_CreateCompObjStream [Internal]
6353 * Creates a "\001CompObj" is the destination IStorage if necessary.
6356 * pStorage [I] The dest IStorage to create the CompObj Stream
6358 * strOleTypeName [I] The ProgID
6362 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6365 * This function is used by OleConvertOLESTREAMToIStorage only.
6367 * The stream data is stored in the OLESTREAM and there should be
6368 * no need to recreate the stream. If the stream is manually
6369 * deleted it will attempt to create it by querying the registry.
6373 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6376 HRESULT hStorageRes, hRes = S_OK;
6377 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6378 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6380 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6381 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6383 /* Initialize the CompObj structure */
6384 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6385 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6386 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6389 /* Create a CompObj stream if it doesn't exist */
6390 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6391 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6392 if(hStorageRes == S_OK)
6394 /* copy the OleTypeName to the compobj struct */
6395 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6396 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6398 /* copy the OleTypeName to the compobj struct */
6399 /* Note: in the test made, these where Identical */
6400 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6401 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6404 hRes = CLSIDFromProgID16(IStorageCompObj.strProgIDName, &(IStorageCompObj.clsid));
6410 /* Get the CLSID Default Name from the Registry */
6411 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6412 if(hErr == ERROR_SUCCESS)
6414 char strTemp[OLESTREAM_MAX_STR_LEN];
6415 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6416 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6417 if(hErr == ERROR_SUCCESS)
6419 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6425 /* Write CompObj Structure to stream */
6426 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6428 WriteClassStm(pStream,&(IStorageCompObj.clsid));
6430 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6431 if(IStorageCompObj.dwCLSIDNameLength > 0)
6433 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6435 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6436 if(IStorageCompObj.dwOleTypeNameLength > 0)
6438 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6440 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6441 if(IStorageCompObj.dwProgIDNameLength > 0)
6443 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6445 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6446 IStream_Release(pStream);
6452 /*************************************************************************
6453 * OLECONVERT_CreateOlePresStream[Internal]
6455 * Creates the "\002OlePres000" Stream with the Metafile data
6458 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6459 * dwExtentX [I] Width of the Metafile
6460 * dwExtentY [I] Height of the Metafile
6461 * pData [I] Metafile data
6462 * dwDataLength [I] Size of the Metafile data
6466 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6469 * This function is used by OleConvertOLESTREAMToIStorage only.
6472 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6476 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6477 BYTE pOlePresStreamHeader [] =
6479 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6480 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6481 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6482 0x00, 0x00, 0x00, 0x00
6485 BYTE pOlePresStreamHeaderEmpty [] =
6487 0x00, 0x00, 0x00, 0x00,
6488 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6489 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6490 0x00, 0x00, 0x00, 0x00
6493 /* Create the OlePres000 Stream */
6494 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6495 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6500 OLECONVERT_ISTORAGE_OLEPRES OlePres;
6502 memset(&OlePres, 0, sizeof(OlePres));
6503 /* Do we have any metafile data to save */
6504 if(dwDataLength > 0)
6506 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6507 nHeaderSize = sizeof(pOlePresStreamHeader);
6511 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
6512 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
6514 /* Set width and height of the metafile */
6515 OlePres.dwExtentX = dwExtentX;
6516 OlePres.dwExtentY = -dwExtentY;
6518 /* Set Data and Lenght */
6519 if(dwDataLength > sizeof(METAFILEPICT16))
6521 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
6522 OlePres.pData = &(pData[8]);
6524 /* Save OlePres000 Data to Stream */
6525 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
6526 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
6527 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
6528 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
6529 if(OlePres.dwSize > 0)
6531 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
6533 IStream_Release(pStream);
6537 /*************************************************************************
6538 * OLECONVERT_CreateOle10NativeStream [Internal]
6540 * Creates the "\001Ole10Native" Stream (should contain a BMP)
6543 * pStorage [I] Dest storage to create the stream in
6544 * pData [I] Ole10 Native Data (ex. bmp)
6545 * dwDataLength [I] Size of the Ole10 Native Data
6551 * This function is used by OleConvertOLESTREAMToIStorage only.
6553 * Might need to verify the data and return appropriate error message
6556 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
6560 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6562 /* Create the Ole10Native Stream */
6563 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6564 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6568 /* Write info to stream */
6569 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
6570 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
6571 IStream_Release(pStream);
6576 /*************************************************************************
6577 * OLECONVERT_GetOLE10ProgID [Internal]
6579 * Finds the ProgID (or OleTypeID) from the IStorage
6582 * pStorage [I] The Src IStorage to get the ProgID
6583 * strProgID [I] the ProgID string to get
6584 * dwSize [I] the size of the string
6588 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6591 * This function is used by OleConvertIStorageToOLESTREAM only.
6595 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
6599 LARGE_INTEGER iSeekPos;
6600 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
6601 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6603 /* Open the CompObj Stream */
6604 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6605 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6609 /*Get the OleType from the CompObj Stream */
6610 iSeekPos.s.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
6611 iSeekPos.s.HighPart = 0;
6613 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6614 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
6615 iSeekPos.s.LowPart = CompObj.dwCLSIDNameLength;
6616 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6617 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
6618 iSeekPos.s.LowPart = CompObj.dwOleTypeNameLength;
6619 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6621 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
6624 IStream_Read(pStream, strProgID, *dwSize, NULL);
6626 IStream_Release(pStream);
6631 LPOLESTR wstrProgID;
6633 /* Get the OleType from the registry */
6634 REFCLSID clsid = &(stat.clsid);
6635 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
6636 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
6639 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
6646 /*************************************************************************
6647 * OLECONVERT_GetOle10PresData [Internal]
6649 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
6652 * pStorage [I] Src IStroage
6653 * pOleStream [I] Dest OleStream Mem Struct
6659 * This function is used by OleConvertIStorageToOLESTREAM only.
6661 * Memory allocated for pData must be freed by the caller
6665 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6670 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6672 /* Initialize Default data for OLESTREAM */
6673 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6674 pOleStreamData[0].dwTypeID = 2;
6675 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6676 pOleStreamData[1].dwTypeID = 0;
6677 pOleStreamData[0].dwMetaFileWidth = 0;
6678 pOleStreamData[0].dwMetaFileHeight = 0;
6679 pOleStreamData[0].pData = NULL;
6680 pOleStreamData[1].pData = NULL;
6682 /* Open Ole10Native Stream */
6683 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6684 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6688 /* Read Size and Data */
6689 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
6690 if(pOleStreamData->dwDataLength > 0)
6692 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
6693 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
6695 IStream_Release(pStream);
6701 /*************************************************************************
6702 * OLECONVERT_GetOle20PresData[Internal]
6704 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
6707 * pStorage [I] Src IStroage
6708 * pOleStreamData [I] Dest OleStream Mem Struct
6714 * This function is used by OleConvertIStorageToOLESTREAM only.
6716 * Memory allocated for pData must be freed by the caller
6718 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6722 OLECONVERT_ISTORAGE_OLEPRES olePress;
6723 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6725 /* Initialize Default data for OLESTREAM */
6726 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6727 pOleStreamData[0].dwTypeID = 2;
6728 pOleStreamData[0].dwMetaFileWidth = 0;
6729 pOleStreamData[0].dwMetaFileHeight = 0;
6730 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
6731 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6732 pOleStreamData[1].dwTypeID = 0;
6733 pOleStreamData[1].dwOleTypeNameLength = 0;
6734 pOleStreamData[1].strOleTypeName[0] = 0;
6735 pOleStreamData[1].dwMetaFileWidth = 0;
6736 pOleStreamData[1].dwMetaFileHeight = 0;
6737 pOleStreamData[1].pData = NULL;
6738 pOleStreamData[1].dwDataLength = 0;
6741 /* Open OlePress000 stream */
6742 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6743 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6746 LARGE_INTEGER iSeekPos;
6747 METAFILEPICT16 MetaFilePict;
6748 char strMetafilePictName[] = "METAFILEPICT";
6750 /* Set the TypeID for a Metafile */
6751 pOleStreamData[1].dwTypeID = 5;
6753 /* Set the OleTypeName to Metafile */
6754 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
6755 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
6757 iSeekPos.s.HighPart = 0;
6758 iSeekPos.s.LowPart = sizeof(olePress.byUnknown1);
6760 /* Get Presentation Data */
6761 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6762 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
6763 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
6764 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
6766 /*Set width and Height */
6767 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
6768 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
6769 if(olePress.dwSize > 0)
6772 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
6774 /* Set MetaFilePict struct */
6775 MetaFilePict.mm = 8;
6776 MetaFilePict.xExt = olePress.dwExtentX;
6777 MetaFilePict.yExt = olePress.dwExtentY;
6778 MetaFilePict.hMF = 0;
6780 /* Get Metafile Data */
6781 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
6782 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
6783 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
6785 IStream_Release(pStream);
6789 /*************************************************************************
6790 * OleConvertOLESTREAMToIStorage [OLE32.87]
6795 * DVTARGETDEVICE paramenter is not handled
6796 * Still unsure of some mem fields for OLE 10 Stream
6797 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6798 * and "\001OLE" streams
6801 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
6802 LPOLESTREAM pOleStream,
6804 const DVTARGETDEVICE* ptd)
6808 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
6810 memset(pOleStreamData, 0, sizeof(pOleStreamData));
6814 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
6817 if(pstg == NULL || pOleStream == NULL)
6819 hRes = E_INVALIDARG;
6824 /* Load the OLESTREAM to Memory */
6825 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
6830 /* Load the OLESTREAM to Memory (part 2)*/
6831 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
6837 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
6839 /* Do we have the IStorage Data in the OLESTREAM */
6840 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
6842 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6843 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
6847 /* It must be an original OLE 1.0 source */
6848 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6853 /* It must be an original OLE 1.0 source */
6854 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6857 /* Create CompObj Stream if necessary */
6858 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
6861 /*Create the Ole Stream if necessary */
6862 OLECONVERT_CreateOleStream(pstg);
6867 /* Free allocated memory */
6868 for(i=0; i < 2; i++)
6870 if(pOleStreamData[i].pData != NULL)
6872 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
6874 if(pOleStreamData[i].pstrOleObjFileName != NULL)
6876 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
6877 pOleStreamData[i].pstrOleObjFileName = NULL;
6883 /*************************************************************************
6884 * OleConvertIStorageToOLESTREAM [OLE32.85]
6891 * Still unsure of some mem fields for OLE 10 Stream
6892 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6893 * and "\001OLE" streams.
6896 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
6898 LPOLESTREAM pOleStream)
6901 HRESULT hRes = S_OK;
6903 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
6904 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6907 memset(pOleStreamData, 0, sizeof(pOleStreamData));
6909 if(pstg == NULL || pOleStream == NULL)
6911 hRes = E_INVALIDARG;
6915 /* Get the ProgID */
6916 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
6917 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
6921 /*Was it originaly Ole10 */
6922 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
6925 IStream_Release(pStream);
6926 /*Get Presentation Data for Ole10Native */
6927 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
6931 /*Get Presentation Data (OLE20)*/
6932 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
6935 /* Save OLESTREAM */
6936 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
6939 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
6944 /* Free allocated memory */
6945 for(i=0; i < 2; i++)
6947 if(pOleStreamData[i].pData != NULL)
6949 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);