Handle the %f case in wsprintf.
[wine.git] / dlls / ole32 / storage32.c
blob3472e6f9227e298822bd00c6e3b15397ca05d6ee
1 /*
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
13 #include <assert.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
18 #include "winbase.h" /* for lstrlenW() and the likes */
19 #include "winnls.h"
20 #include "wine/unicode.h"
21 #include "debugtools.h"
23 #include "storage32.h"
24 #include "ole2.h" /* For Write/ReadClassStm */
26 #include "winreg.h"
27 #include "wine/wingdi16.h"
29 DEFAULT_DEBUG_CHANNEL(storage);
31 #define FILE_BEGIN 0
34 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
35 #define OLESTREAM_ID 0x501
36 #define OLESTREAM_MAX_STR_LEN 255
38 static const char rootPropertyName[] = "Root Entry";
41 /* OLESTREAM memory structure to use for Get and Put Routines */
42 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
43 typedef struct
45 DWORD dwOleID;
46 DWORD dwTypeID;
47 DWORD dwOleTypeNameLength;
48 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
49 CHAR *pstrOleObjFileName;
50 DWORD dwOleObjFileNameLength;
51 DWORD dwMetaFileWidth;
52 DWORD dwMetaFileHeight;
53 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
54 DWORD dwDataLength;
55 BYTE *pData;
56 }OLECONVERT_OLESTREAM_DATA;
58 /* CompObj Stream structure */
59 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
60 typedef struct
62 BYTE byUnknown1[12];
63 CLSID clsid;
64 DWORD dwCLSIDNameLength;
65 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
66 DWORD dwOleTypeNameLength;
67 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
68 DWORD dwProgIDNameLength;
69 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
70 BYTE byUnknown2[16];
71 }OLECONVERT_ISTORAGE_COMPOBJ;
74 /* Ole Presention Stream structure */
75 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
76 typedef struct
78 BYTE byUnknown1[28];
79 DWORD dwExtentX;
80 DWORD dwExtentY;
81 DWORD dwSize;
82 BYTE *pData;
83 }OLECONVERT_ISTORAGE_OLEPRES;
87 /***********************************************************************
88 * Forward declaration of internal functions used by the method DestroyElement
90 static HRESULT deleteStorageProperty(
91 StorageImpl *parentStorage,
92 ULONG foundPropertyIndexToDelete,
93 StgProperty propertyToDelete);
95 static HRESULT deleteStreamProperty(
96 StorageImpl *parentStorage,
97 ULONG foundPropertyIndexToDelete,
98 StgProperty propertyToDelete);
100 static HRESULT findPlaceholder(
101 StorageImpl *storage,
102 ULONG propertyIndexToStore,
103 ULONG storagePropertyIndex,
104 INT typeOfRelation);
106 static HRESULT adjustPropertyChain(
107 StorageImpl *This,
108 StgProperty propertyToDelete,
109 StgProperty parentProperty,
110 ULONG parentPropertyId,
111 INT typeOfRelation);
113 /***********************************************************************
114 * Declaration of the functions used to manipulate StgProperty
117 static ULONG getFreeProperty(
118 StorageImpl *storage);
120 static void updatePropertyChain(
121 StorageImpl *storage,
122 ULONG newPropertyIndex,
123 StgProperty newProperty);
125 static LONG propertyNameCmp(
126 OLECHAR *newProperty,
127 OLECHAR *currentProperty);
130 /***********************************************************************
131 * Declaration of miscellaneous functions...
133 static HRESULT validateSTGM(DWORD stgmValue);
135 static DWORD GetShareModeFromSTGM(DWORD stgm);
136 static DWORD GetAccessModeFromSTGM(DWORD stgm);
137 static DWORD GetCreationModeFromSTGM(DWORD stgm);
140 * Virtual function table for the IStorage32Impl class.
142 static ICOM_VTABLE(IStorage) Storage32Impl_Vtbl =
144 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
145 StorageBaseImpl_QueryInterface,
146 StorageBaseImpl_AddRef,
147 StorageBaseImpl_Release,
148 StorageBaseImpl_CreateStream,
149 StorageBaseImpl_OpenStream,
150 StorageImpl_CreateStorage,
151 StorageBaseImpl_OpenStorage,
152 StorageImpl_CopyTo,
153 StorageImpl_MoveElementTo,
154 StorageImpl_Commit,
155 StorageImpl_Revert,
156 StorageBaseImpl_EnumElements,
157 StorageImpl_DestroyElement,
158 StorageBaseImpl_RenameElement,
159 StorageImpl_SetElementTimes,
160 StorageBaseImpl_SetClass,
161 StorageImpl_SetStateBits,
162 StorageBaseImpl_Stat
166 * Virtual function table for the Storage32InternalImpl class.
168 static ICOM_VTABLE(IStorage) Storage32InternalImpl_Vtbl =
170 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
171 StorageBaseImpl_QueryInterface,
172 StorageBaseImpl_AddRef,
173 StorageBaseImpl_Release,
174 StorageBaseImpl_CreateStream,
175 StorageBaseImpl_OpenStream,
176 StorageImpl_CreateStorage,
177 StorageBaseImpl_OpenStorage,
178 StorageImpl_CopyTo,
179 StorageImpl_MoveElementTo,
180 StorageInternalImpl_Commit,
181 StorageInternalImpl_Revert,
182 StorageBaseImpl_EnumElements,
183 StorageImpl_DestroyElement,
184 StorageBaseImpl_RenameElement,
185 StorageImpl_SetElementTimes,
186 StorageBaseImpl_SetClass,
187 StorageImpl_SetStateBits,
188 StorageBaseImpl_Stat
192 * Virtual function table for the IEnumSTATSTGImpl class.
194 static ICOM_VTABLE(IEnumSTATSTG) IEnumSTATSTGImpl_Vtbl =
196 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
197 IEnumSTATSTGImpl_QueryInterface,
198 IEnumSTATSTGImpl_AddRef,
199 IEnumSTATSTGImpl_Release,
200 IEnumSTATSTGImpl_Next,
201 IEnumSTATSTGImpl_Skip,
202 IEnumSTATSTGImpl_Reset,
203 IEnumSTATSTGImpl_Clone
210 /************************************************************************
211 ** Storage32BaseImpl implementatiion
214 /************************************************************************
215 * Storage32BaseImpl_QueryInterface (IUnknown)
217 * This method implements the common QueryInterface for all IStorage32
218 * implementations contained in this file.
220 * See Windows documentation for more details on IUnknown methods.
222 HRESULT WINAPI StorageBaseImpl_QueryInterface(
223 IStorage* iface,
224 REFIID riid,
225 void** ppvObject)
227 ICOM_THIS(StorageBaseImpl,iface);
229 * Perform a sanity check on the parameters.
231 if ( (This==0) || (ppvObject==0) )
232 return E_INVALIDARG;
235 * Initialize the return parameter.
237 *ppvObject = 0;
240 * Compare the riid with the interface IDs implemented by this object.
242 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
244 *ppvObject = (IStorage*)This;
246 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
248 *ppvObject = (IStorage*)This;
252 * Check that we obtained an interface.
254 if ((*ppvObject)==0)
255 return E_NOINTERFACE;
258 * Query Interface always increases the reference count by one when it is
259 * successful
261 StorageBaseImpl_AddRef(iface);
263 return S_OK;
266 /************************************************************************
267 * Storage32BaseImpl_AddRef (IUnknown)
269 * This method implements the common AddRef for all IStorage32
270 * implementations contained in this file.
272 * See Windows documentation for more details on IUnknown methods.
274 ULONG WINAPI StorageBaseImpl_AddRef(
275 IStorage* iface)
277 ICOM_THIS(StorageBaseImpl,iface);
278 This->ref++;
280 return This->ref;
283 /************************************************************************
284 * Storage32BaseImpl_Release (IUnknown)
286 * This method implements the common Release for all IStorage32
287 * implementations contained in this file.
289 * See Windows documentation for more details on IUnknown methods.
291 ULONG WINAPI StorageBaseImpl_Release(
292 IStorage* iface)
294 ICOM_THIS(StorageBaseImpl,iface);
296 * Decrease the reference count on this object.
298 This->ref--;
301 * If the reference count goes down to 0, perform suicide.
303 if (This->ref==0)
306 * Since we are using a system of base-classes, we want to call the
307 * destructor of the appropriate derived class. To do this, we are
308 * using virtual functions to implement the destructor.
310 This->v_destructor(This);
312 return 0;
315 return This->ref;
318 /************************************************************************
319 * Storage32BaseImpl_OpenStream (IStorage)
321 * This method will open the specified stream object from the current storage.
323 * See Windows documentation for more details on IStorage methods.
325 HRESULT WINAPI StorageBaseImpl_OpenStream(
326 IStorage* iface,
327 const OLECHAR* pwcsName, /* [string][in] */
328 void* reserved1, /* [unique][in] */
329 DWORD grfMode, /* [in] */
330 DWORD reserved2, /* [in] */
331 IStream** ppstm) /* [out] */
333 ICOM_THIS(StorageBaseImpl,iface);
334 IEnumSTATSTGImpl* propertyEnumeration;
335 StgStreamImpl* newStream;
336 StgProperty currentProperty;
337 ULONG foundPropertyIndex;
339 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
340 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
343 * Perform a sanity check on the parameters.
345 if ( (pwcsName==NULL) || (ppstm==0) )
346 return E_INVALIDARG;
349 * Initialize the out parameter
351 *ppstm = 0;
354 * Validate the STGM flags
356 if ( FAILED( validateSTGM(grfMode) ))
357 return STG_E_INVALIDFLAG;
360 * As documented.
362 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
363 (grfMode & STGM_DELETEONRELEASE) ||
364 (grfMode & STGM_TRANSACTED) )
365 return STG_E_INVALIDFUNCTION;
368 * Create a property enumeration to search the properties
370 propertyEnumeration = IEnumSTATSTGImpl_Construct(
371 This->ancestorStorage,
372 This->rootPropertySetIndex);
375 * Search the enumeration for the property with the given name
377 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
378 propertyEnumeration,
379 pwcsName,
380 &currentProperty);
383 * Delete the property enumeration since we don't need it anymore
385 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
388 * If it was found, construct the stream object and return a pointer to it.
390 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
391 (currentProperty.propertyType==PROPTYPE_STREAM) )
393 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
395 if (newStream!=0)
397 newStream->grfMode = grfMode;
398 *ppstm = (IStream*)newStream;
401 * Since we are returning a pointer to the interface, we have to
402 * nail down the reference.
404 StgStreamImpl_AddRef(*ppstm);
406 return S_OK;
409 return E_OUTOFMEMORY;
412 return STG_E_FILENOTFOUND;
415 /************************************************************************
416 * Storage32BaseImpl_OpenStorage (IStorage)
418 * This method will open a new storage object from the current storage.
420 * See Windows documentation for more details on IStorage methods.
422 HRESULT WINAPI StorageBaseImpl_OpenStorage(
423 IStorage* iface,
424 const OLECHAR* pwcsName, /* [string][unique][in] */
425 IStorage* pstgPriority, /* [unique][in] */
426 DWORD grfMode, /* [in] */
427 SNB snbExclude, /* [unique][in] */
428 DWORD reserved, /* [in] */
429 IStorage** ppstg) /* [out] */
431 ICOM_THIS(StorageBaseImpl,iface);
432 StorageInternalImpl* newStorage;
433 IEnumSTATSTGImpl* propertyEnumeration;
434 StgProperty currentProperty;
435 ULONG foundPropertyIndex;
437 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
438 iface, debugstr_w(pwcsName), pstgPriority,
439 grfMode, snbExclude, reserved, ppstg);
442 * Perform a sanity check on the parameters.
444 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
445 return E_INVALIDARG;
448 * Validate the STGM flags
450 if ( FAILED( validateSTGM(grfMode) ))
451 return STG_E_INVALIDFLAG;
454 * As documented.
456 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
457 (grfMode & STGM_DELETEONRELEASE) ||
458 (grfMode & STGM_PRIORITY) )
459 return STG_E_INVALIDFUNCTION;
462 * Initialize the out parameter
464 *ppstg = 0;
467 * Create a property enumeration to search the properties
469 propertyEnumeration = IEnumSTATSTGImpl_Construct(
470 This->ancestorStorage,
471 This->rootPropertySetIndex);
474 * Search the enumeration for the property with the given name
476 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
477 propertyEnumeration,
478 pwcsName,
479 &currentProperty);
482 * Delete the property enumeration since we don't need it anymore
484 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
487 * If it was found, construct the stream object and return a pointer to it.
489 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
490 (currentProperty.propertyType==PROPTYPE_STORAGE) )
493 * Construct a new Storage object
495 newStorage = StorageInternalImpl_Construct(
496 This->ancestorStorage,
497 foundPropertyIndex);
499 if (newStorage != 0)
501 *ppstg = (IStorage*)newStorage;
504 * Since we are returning a pointer to the interface,
505 * we have to nail down the reference.
507 StorageBaseImpl_AddRef(*ppstg);
509 return S_OK;
512 return STG_E_INSUFFICIENTMEMORY;
515 return STG_E_FILENOTFOUND;
518 /************************************************************************
519 * Storage32BaseImpl_EnumElements (IStorage)
521 * This method will create an enumerator object that can be used to
522 * retrieve informatino about all the properties in the storage object.
524 * See Windows documentation for more details on IStorage methods.
526 HRESULT WINAPI StorageBaseImpl_EnumElements(
527 IStorage* iface,
528 DWORD reserved1, /* [in] */
529 void* reserved2, /* [size_is][unique][in] */
530 DWORD reserved3, /* [in] */
531 IEnumSTATSTG** ppenum) /* [out] */
533 ICOM_THIS(StorageBaseImpl,iface);
534 IEnumSTATSTGImpl* newEnum;
536 TRACE("(%p, %ld, %p, %ld, %p)\n",
537 iface, reserved1, reserved2, reserved3, ppenum);
540 * Perform a sanity check on the parameters.
542 if ( (This==0) || (ppenum==0))
543 return E_INVALIDARG;
546 * Construct the enumerator.
548 newEnum = IEnumSTATSTGImpl_Construct(
549 This->ancestorStorage,
550 This->rootPropertySetIndex);
552 if (newEnum!=0)
554 *ppenum = (IEnumSTATSTG*)newEnum;
557 * Don't forget to nail down a reference to the new object before
558 * returning it.
560 IEnumSTATSTGImpl_AddRef(*ppenum);
562 return S_OK;
565 return E_OUTOFMEMORY;
568 /************************************************************************
569 * Storage32BaseImpl_Stat (IStorage)
571 * This method will retrieve information about this storage object.
573 * See Windows documentation for more details on IStorage methods.
575 HRESULT WINAPI StorageBaseImpl_Stat(
576 IStorage* iface,
577 STATSTG* pstatstg, /* [out] */
578 DWORD grfStatFlag) /* [in] */
580 ICOM_THIS(StorageBaseImpl,iface);
581 StgProperty curProperty;
582 BOOL readSuccessful;
584 TRACE("(%p, %p, %lx)\n",
585 iface, pstatstg, grfStatFlag);
588 * Perform a sanity check on the parameters.
590 if ( (This==0) || (pstatstg==0))
591 return E_INVALIDARG;
594 * Read the information from the property.
596 readSuccessful = StorageImpl_ReadProperty(
597 This->ancestorStorage,
598 This->rootPropertySetIndex,
599 &curProperty);
601 if (readSuccessful)
603 StorageUtl_CopyPropertyToSTATSTG(
604 pstatstg,
605 &curProperty,
606 grfStatFlag);
608 return S_OK;
611 return E_FAIL;
614 /************************************************************************
615 * Storage32BaseImpl_RenameElement (IStorage)
617 * This method will rename the specified element.
619 * See Windows documentation for more details on IStorage methods.
621 * Implementation notes: The method used to rename consists of creating a clone
622 * of the deleted StgProperty object setting it with the new name and to
623 * perform a DestroyElement of the old StgProperty.
625 HRESULT WINAPI StorageBaseImpl_RenameElement(
626 IStorage* iface,
627 const OLECHAR* pwcsOldName, /* [in] */
628 const OLECHAR* pwcsNewName) /* [in] */
630 ICOM_THIS(StorageBaseImpl,iface);
631 IEnumSTATSTGImpl* propertyEnumeration;
632 StgProperty currentProperty;
633 ULONG foundPropertyIndex;
635 TRACE("(%p, %s, %s)\n",
636 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
639 * Create a property enumeration to search the properties
641 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
642 This->rootPropertySetIndex);
645 * Search the enumeration for the new property name
647 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
648 pwcsNewName,
649 &currentProperty);
651 if (foundPropertyIndex != PROPERTY_NULL)
654 * There is already a property with the new name
656 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
657 return STG_E_FILEALREADYEXISTS;
660 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
663 * Search the enumeration for the old property name
665 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
666 pwcsOldName,
667 &currentProperty);
670 * Delete the property enumeration since we don't need it anymore
672 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
674 if (foundPropertyIndex != PROPERTY_NULL)
676 StgProperty renamedProperty;
677 ULONG renamedPropertyIndex;
680 * Setup a new property for the renamed property
682 renamedProperty.sizeOfNameString =
683 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
685 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
686 return STG_E_INVALIDNAME;
688 strcpyW(renamedProperty.name, pwcsNewName);
690 renamedProperty.propertyType = currentProperty.propertyType;
691 renamedProperty.startingBlock = currentProperty.startingBlock;
692 renamedProperty.size.s.LowPart = currentProperty.size.s.LowPart;
693 renamedProperty.size.s.HighPart = currentProperty.size.s.HighPart;
695 renamedProperty.previousProperty = PROPERTY_NULL;
696 renamedProperty.nextProperty = PROPERTY_NULL;
699 * Bring the dirProperty link in case it is a storage and in which
700 * case the renamed storage elements don't require to be reorganized.
702 renamedProperty.dirProperty = currentProperty.dirProperty;
704 /* call CoFileTime to get the current time
705 renamedProperty.timeStampS1
706 renamedProperty.timeStampD1
707 renamedProperty.timeStampS2
708 renamedProperty.timeStampD2
709 renamedProperty.propertyUniqueID
713 * Obtain a free property in the property chain
715 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
718 * Save the new property into the new property spot
720 StorageImpl_WriteProperty(
721 This->ancestorStorage,
722 renamedPropertyIndex,
723 &renamedProperty);
726 * Find a spot in the property chain for our newly created property.
728 updatePropertyChain(
729 (StorageImpl*)This,
730 renamedPropertyIndex,
731 renamedProperty);
734 * At this point the renamed property has been inserted in the tree,
735 * now, before to Destroy the old property we must zeroed it's dirProperty
736 * otherwise the DestroyProperty below will zap it all and we do not want
737 * this to happen.
738 * Also, we fake that the old property is a storage so the DestroyProperty
739 * will not do a SetSize(0) on the stream data.
741 * This means that we need to tweek the StgProperty if it is a stream or a
742 * non empty storage.
744 StorageImpl_ReadProperty(This->ancestorStorage,
745 foundPropertyIndex,
746 &currentProperty);
748 currentProperty.dirProperty = PROPERTY_NULL;
749 currentProperty.propertyType = PROPTYPE_STORAGE;
750 StorageImpl_WriteProperty(
751 This->ancestorStorage,
752 foundPropertyIndex,
753 &currentProperty);
756 * Invoke Destroy to get rid of the ole property and automatically redo
757 * the linking of it's previous and next members...
759 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
762 else
765 * There is no property with the old name
767 return STG_E_FILENOTFOUND;
770 return S_OK;
773 /************************************************************************
774 * Storage32BaseImpl_CreateStream (IStorage)
776 * This method will create a stream object within this storage
778 * See Windows documentation for more details on IStorage methods.
780 HRESULT WINAPI StorageBaseImpl_CreateStream(
781 IStorage* iface,
782 const OLECHAR* pwcsName, /* [string][in] */
783 DWORD grfMode, /* [in] */
784 DWORD reserved1, /* [in] */
785 DWORD reserved2, /* [in] */
786 IStream** ppstm) /* [out] */
788 ICOM_THIS(StorageBaseImpl,iface);
789 IEnumSTATSTGImpl* propertyEnumeration;
790 StgStreamImpl* newStream;
791 StgProperty currentProperty, newStreamProperty;
792 ULONG foundPropertyIndex, newPropertyIndex;
794 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
795 iface, debugstr_w(pwcsName), grfMode,
796 reserved1, reserved2, ppstm);
799 * Validate parameters
801 if (ppstm == 0)
802 return STG_E_INVALIDPOINTER;
804 if (pwcsName == 0)
805 return STG_E_INVALIDNAME;
808 * Validate the STGM flags
810 if ( FAILED( validateSTGM(grfMode) ))
811 return STG_E_INVALIDFLAG;
814 * As documented.
816 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
817 (grfMode & STGM_DELETEONRELEASE) ||
818 (grfMode & STGM_TRANSACTED) )
819 return STG_E_INVALIDFUNCTION;
822 * Initialize the out parameter
824 *ppstm = 0;
827 * Create a property enumeration to search the properties
829 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
830 This->rootPropertySetIndex);
832 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
833 pwcsName,
834 &currentProperty);
836 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
838 if (foundPropertyIndex != PROPERTY_NULL)
841 * An element with this name already exists
843 if (grfMode & STGM_CREATE)
845 IStorage_DestroyElement(iface, pwcsName);
847 else
848 return STG_E_FILEALREADYEXISTS;
852 * memset the empty property
854 memset(&newStreamProperty, 0, sizeof(StgProperty));
856 newStreamProperty.sizeOfNameString =
857 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
859 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
860 return STG_E_INVALIDNAME;
862 strcpyW(newStreamProperty.name, pwcsName);
864 newStreamProperty.propertyType = PROPTYPE_STREAM;
865 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
866 newStreamProperty.size.s.LowPart = 0;
867 newStreamProperty.size.s.HighPart = 0;
869 newStreamProperty.previousProperty = PROPERTY_NULL;
870 newStreamProperty.nextProperty = PROPERTY_NULL;
871 newStreamProperty.dirProperty = PROPERTY_NULL;
873 /* call CoFileTime to get the current time
874 newStreamProperty.timeStampS1
875 newStreamProperty.timeStampD1
876 newStreamProperty.timeStampS2
877 newStreamProperty.timeStampD2
880 /* newStreamProperty.propertyUniqueID */
883 * Get a free property or create a new one
885 newPropertyIndex = getFreeProperty(This->ancestorStorage);
888 * Save the new property into the new property spot
890 StorageImpl_WriteProperty(
891 This->ancestorStorage,
892 newPropertyIndex,
893 &newStreamProperty);
896 * Find a spot in the property chain for our newly created property.
898 updatePropertyChain(
899 (StorageImpl*)This,
900 newPropertyIndex,
901 newStreamProperty);
904 * Open the stream to return it.
906 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
908 if (newStream != 0)
910 *ppstm = (IStream*)newStream;
913 * Since we are returning a pointer to the interface, we have to nail down
914 * the reference.
916 StgStreamImpl_AddRef(*ppstm);
918 else
920 return STG_E_INSUFFICIENTMEMORY;
923 return S_OK;
926 /************************************************************************
927 * Storage32BaseImpl_SetClass (IStorage)
929 * This method will write the specified CLSID in the property of this
930 * storage.
932 * See Windows documentation for more details on IStorage methods.
934 HRESULT WINAPI StorageBaseImpl_SetClass(
935 IStorage* iface,
936 REFCLSID clsid) /* [in] */
938 ICOM_THIS(StorageBaseImpl,iface);
939 HRESULT hRes = E_FAIL;
940 StgProperty curProperty;
941 BOOL success;
943 TRACE("(%p, %p)\n", iface, clsid);
945 success = StorageImpl_ReadProperty(This->ancestorStorage,
946 This->rootPropertySetIndex,
947 &curProperty);
948 if (success)
950 curProperty.propertyUniqueID = *clsid;
952 success = StorageImpl_WriteProperty(This->ancestorStorage,
953 This->rootPropertySetIndex,
954 &curProperty);
955 if (success)
956 hRes = S_OK;
959 return hRes;
962 /************************************************************************
963 ** Storage32Impl implementation
966 /************************************************************************
967 * Storage32Impl_CreateStorage (IStorage)
969 * This method will create the storage object within the provided storage.
971 * See Windows documentation for more details on IStorage methods.
973 HRESULT WINAPI StorageImpl_CreateStorage(
974 IStorage* iface,
975 const OLECHAR *pwcsName, /* [string][in] */
976 DWORD grfMode, /* [in] */
977 DWORD reserved1, /* [in] */
978 DWORD reserved2, /* [in] */
979 IStorage **ppstg) /* [out] */
981 StorageImpl* const This=(StorageImpl*)iface;
983 IEnumSTATSTGImpl *propertyEnumeration;
984 StgProperty currentProperty;
985 StgProperty newProperty;
986 ULONG foundPropertyIndex;
987 ULONG newPropertyIndex;
988 HRESULT hr;
990 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
991 iface, debugstr_w(pwcsName), grfMode,
992 reserved1, reserved2, ppstg);
995 * Validate parameters
997 if (ppstg == 0)
998 return STG_E_INVALIDPOINTER;
1000 if (pwcsName == 0)
1001 return STG_E_INVALIDNAME;
1004 * Validate the STGM flags
1006 if ( FAILED( validateSTGM(grfMode) ) ||
1007 (grfMode & STGM_DELETEONRELEASE) )
1008 return STG_E_INVALIDFLAG;
1011 * Initialize the out parameter
1013 *ppstg = 0;
1016 * Create a property enumeration and search the properties
1018 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
1019 This->rootPropertySetIndex);
1021 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1022 pwcsName,
1023 &currentProperty);
1024 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1026 if (foundPropertyIndex != PROPERTY_NULL)
1029 * An element with this name already exists
1031 if (grfMode & STGM_CREATE)
1032 IStorage_DestroyElement(iface, pwcsName);
1033 else
1034 return STG_E_FILEALREADYEXISTS;
1038 * memset the empty property
1040 memset(&newProperty, 0, sizeof(StgProperty));
1042 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1044 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1045 return STG_E_INVALIDNAME;
1047 strcpyW(newProperty.name, pwcsName);
1049 newProperty.propertyType = PROPTYPE_STORAGE;
1050 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1051 newProperty.size.s.LowPart = 0;
1052 newProperty.size.s.HighPart = 0;
1054 newProperty.previousProperty = PROPERTY_NULL;
1055 newProperty.nextProperty = PROPERTY_NULL;
1056 newProperty.dirProperty = PROPERTY_NULL;
1058 /* call CoFileTime to get the current time
1059 newProperty.timeStampS1
1060 newProperty.timeStampD1
1061 newProperty.timeStampS2
1062 newProperty.timeStampD2
1065 /* newStorageProperty.propertyUniqueID */
1068 * Obtain a free property in the property chain
1070 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1073 * Save the new property into the new property spot
1075 StorageImpl_WriteProperty(
1076 This->ancestorStorage,
1077 newPropertyIndex,
1078 &newProperty);
1081 * Find a spot in the property chain for our newly created property.
1083 updatePropertyChain(
1084 This,
1085 newPropertyIndex,
1086 newProperty);
1089 * Open it to get a pointer to return.
1091 hr = IStorage_OpenStorage(
1092 iface,
1093 (OLECHAR*)pwcsName,
1095 grfMode,
1098 ppstg);
1100 if( (hr != S_OK) || (*ppstg == NULL))
1102 return hr;
1106 return S_OK;
1110 /***************************************************************************
1112 * Internal Method
1114 * Get a free property or create a new one.
1116 static ULONG getFreeProperty(
1117 StorageImpl *storage)
1119 ULONG currentPropertyIndex = 0;
1120 ULONG newPropertyIndex = PROPERTY_NULL;
1121 BOOL readSuccessful = TRUE;
1122 StgProperty currentProperty;
1127 * Start by reading the root property
1129 readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1130 currentPropertyIndex,
1131 &currentProperty);
1132 if (readSuccessful)
1134 if (currentProperty.sizeOfNameString == 0)
1137 * The property existis and is available, we found it.
1139 newPropertyIndex = currentPropertyIndex;
1142 else
1145 * We exhausted the property list, we will create more space below
1147 newPropertyIndex = currentPropertyIndex;
1149 currentPropertyIndex++;
1151 } while (newPropertyIndex == PROPERTY_NULL);
1154 * grow the property chain
1156 if (! readSuccessful)
1158 StgProperty emptyProperty;
1159 ULARGE_INTEGER newSize;
1160 ULONG propertyIndex;
1161 ULONG lastProperty = 0;
1162 ULONG blockCount = 0;
1165 * obtain the new count of property blocks
1167 blockCount = BlockChainStream_GetCount(
1168 storage->ancestorStorage->rootBlockChain)+1;
1171 * initialize the size used by the property stream
1173 newSize.s.HighPart = 0;
1174 newSize.s.LowPart = storage->bigBlockSize * blockCount;
1177 * add a property block to the property chain
1179 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1182 * memset the empty property in order to initialize the unused newly
1183 * created property
1185 memset(&emptyProperty, 0, sizeof(StgProperty));
1188 * initialize them
1190 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1192 for(
1193 propertyIndex = newPropertyIndex;
1194 propertyIndex < lastProperty;
1195 propertyIndex++)
1197 StorageImpl_WriteProperty(
1198 storage->ancestorStorage,
1199 propertyIndex,
1200 &emptyProperty);
1204 return newPropertyIndex;
1207 /****************************************************************************
1209 * Internal Method
1211 * Case insensitive comparaison of StgProperty.name by first considering
1212 * their size.
1214 * Returns <0 when newPrpoerty < currentProperty
1215 * >0 when newPrpoerty > currentProperty
1216 * 0 when newPrpoerty == currentProperty
1218 static LONG propertyNameCmp(
1219 OLECHAR *newProperty,
1220 OLECHAR *currentProperty)
1222 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1224 if (diff == 0)
1227 * We compare the string themselves only when they are of the same lenght
1229 diff = lstrcmpiW( newProperty, currentProperty);
1232 return diff;
1235 /****************************************************************************
1237 * Internal Method
1239 * Properly link this new element in the property chain.
1241 static void updatePropertyChain(
1242 StorageImpl *storage,
1243 ULONG newPropertyIndex,
1244 StgProperty newProperty)
1246 StgProperty currentProperty;
1249 * Read the root property
1251 StorageImpl_ReadProperty(storage->ancestorStorage,
1252 storage->rootPropertySetIndex,
1253 &currentProperty);
1255 if (currentProperty.dirProperty != PROPERTY_NULL)
1258 * The root storage contains some element, therefore, start the research
1259 * for the appropriate location.
1261 BOOL found = 0;
1262 ULONG current, next, previous, currentPropertyId;
1265 * Keep the StgProperty sequence number of the storage first property
1267 currentPropertyId = currentProperty.dirProperty;
1270 * Read
1272 StorageImpl_ReadProperty(storage->ancestorStorage,
1273 currentProperty.dirProperty,
1274 &currentProperty);
1276 previous = currentProperty.previousProperty;
1277 next = currentProperty.nextProperty;
1278 current = currentPropertyId;
1280 while (found == 0)
1282 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1284 if (diff < 0)
1286 if (previous != PROPERTY_NULL)
1288 StorageImpl_ReadProperty(storage->ancestorStorage,
1289 previous,
1290 &currentProperty);
1291 current = previous;
1293 else
1295 currentProperty.previousProperty = newPropertyIndex;
1296 StorageImpl_WriteProperty(storage->ancestorStorage,
1297 current,
1298 &currentProperty);
1299 found = 1;
1302 else if (diff > 0)
1304 if (next != PROPERTY_NULL)
1306 StorageImpl_ReadProperty(storage->ancestorStorage,
1307 next,
1308 &currentProperty);
1309 current = next;
1311 else
1313 currentProperty.nextProperty = newPropertyIndex;
1314 StorageImpl_WriteProperty(storage->ancestorStorage,
1315 current,
1316 &currentProperty);
1317 found = 1;
1320 else
1323 * Trying to insert an item with the same name in the
1324 * subtree structure.
1326 assert(FALSE);
1329 previous = currentProperty.previousProperty;
1330 next = currentProperty.nextProperty;
1333 else
1336 * The root storage is empty, link the new property to it's dir property
1338 currentProperty.dirProperty = newPropertyIndex;
1339 StorageImpl_WriteProperty(storage->ancestorStorage,
1340 storage->rootPropertySetIndex,
1341 &currentProperty);
1346 /*************************************************************************
1347 * CopyTo (IStorage)
1349 HRESULT WINAPI StorageImpl_CopyTo(
1350 IStorage* iface,
1351 DWORD ciidExclude, /* [in] */
1352 const IID* rgiidExclude, /* [size_is][unique][in] */
1353 SNB snbExclude, /* [unique][in] */
1354 IStorage* pstgDest) /* [unique][in] */
1356 IEnumSTATSTG *elements = 0;
1357 STATSTG curElement, strStat;
1358 HRESULT hr;
1359 IStorage *pstgTmp, *pstgChild;
1360 IStream *pstrTmp, *pstrChild;
1362 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1363 FIXME("Exclude option not implemented\n");
1365 TRACE("(%p, %ld, %p, %p, %p)\n",
1366 iface, ciidExclude, rgiidExclude,
1367 snbExclude, pstgDest);
1370 * Perform a sanity check
1372 if ( pstgDest == 0 )
1373 return STG_E_INVALIDPOINTER;
1376 * Enumerate the elements
1378 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1380 if ( hr != S_OK )
1381 return hr;
1384 * set the class ID
1386 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1387 IStorage_SetClass( pstgDest, &curElement.clsid );
1392 * Obtain the next element
1394 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1396 if ( hr == S_FALSE )
1398 hr = S_OK; /* done, every element has been copied */
1399 break;
1402 if (curElement.type == STGTY_STORAGE)
1405 * open child source storage
1407 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1408 STGM_READ|STGM_SHARE_EXCLUSIVE,
1409 NULL, 0, &pstgChild );
1411 if (hr != S_OK)
1412 break;
1415 * Check if destination storage is not a child of the source
1416 * storage, which will cause an infinite loop
1418 if (pstgChild == pstgDest)
1420 IEnumSTATSTG_Release(elements);
1422 return STG_E_ACCESSDENIED;
1426 * create a new storage in destination storage
1428 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1429 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1430 0, 0,
1431 &pstgTmp );
1433 * if it already exist, don't create a new one use this one
1435 if (hr == STG_E_FILEALREADYEXISTS)
1437 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1438 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1439 NULL, 0, &pstgTmp );
1442 if (hr != S_OK)
1443 break;
1447 * do the copy recursively
1449 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1450 snbExclude, pstgTmp );
1452 IStorage_Release( pstgTmp );
1453 IStorage_Release( pstgChild );
1455 else if (curElement.type == STGTY_STREAM)
1458 * create a new stream in destination storage. If the stream already
1459 * exist, it will be deleted and a new one will be created.
1461 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1462 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1463 0, 0, &pstrTmp );
1465 if (hr != S_OK)
1466 break;
1469 * open child stream storage
1471 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1472 STGM_READ|STGM_SHARE_EXCLUSIVE,
1473 0, &pstrChild );
1475 if (hr != S_OK)
1476 break;
1479 * Get the size of the source stream
1481 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1484 * Set the size of the destination stream.
1486 IStream_SetSize(pstrTmp, strStat.cbSize);
1489 * do the copy
1491 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1492 NULL, NULL );
1494 IStream_Release( pstrTmp );
1495 IStream_Release( pstrChild );
1497 else
1499 WARN("unknown element type: %ld\n", curElement.type);
1502 } while (hr == S_OK);
1505 * Clean-up
1507 IEnumSTATSTG_Release(elements);
1509 return hr;
1512 /*************************************************************************
1513 * MoveElementTo (IStorage)
1515 HRESULT WINAPI StorageImpl_MoveElementTo(
1516 IStorage* iface,
1517 const OLECHAR *pwcsName, /* [string][in] */
1518 IStorage *pstgDest, /* [unique][in] */
1519 const OLECHAR *pwcsNewName,/* [string][in] */
1520 DWORD grfFlags) /* [in] */
1522 FIXME("not implemented!\n");
1523 return E_NOTIMPL;
1526 /*************************************************************************
1527 * Commit (IStorage)
1529 HRESULT WINAPI StorageImpl_Commit(
1530 IStorage* iface,
1531 DWORD grfCommitFlags)/* [in] */
1533 FIXME("(%ld): stub!\n", grfCommitFlags);
1534 return S_OK;
1537 /*************************************************************************
1538 * Revert (IStorage)
1540 HRESULT WINAPI StorageImpl_Revert(
1541 IStorage* iface)
1543 FIXME("not implemented!\n");
1544 return E_NOTIMPL;
1547 /*************************************************************************
1548 * DestroyElement (IStorage)
1550 * Stategy: This implementation is build this way for simplicity not for speed.
1551 * I always delete the top most element of the enumeration and adjust
1552 * the deleted element pointer all the time. This takes longer to
1553 * do but allow to reinvoke DestroyElement whenever we encounter a
1554 * storage object. The optimisation reside in the usage of another
1555 * enumeration stategy that would give all the leaves of a storage
1556 * first. (postfix order)
1558 HRESULT WINAPI StorageImpl_DestroyElement(
1559 IStorage* iface,
1560 const OLECHAR *pwcsName)/* [string][in] */
1562 StorageImpl* const This=(StorageImpl*)iface;
1564 IEnumSTATSTGImpl* propertyEnumeration;
1565 HRESULT hr = S_OK;
1566 BOOL res;
1567 StgProperty propertyToDelete;
1568 StgProperty parentProperty;
1569 ULONG foundPropertyIndexToDelete;
1570 ULONG typeOfRelation;
1571 ULONG parentPropertyId;
1573 TRACE("(%p, %s)\n",
1574 iface, debugstr_w(pwcsName));
1577 * Perform a sanity check on the parameters.
1579 if (pwcsName==NULL)
1580 return STG_E_INVALIDPOINTER;
1583 * Create a property enumeration to search the property with the given name
1585 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1586 This->ancestorStorage,
1587 This->rootPropertySetIndex);
1589 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1590 propertyEnumeration,
1591 pwcsName,
1592 &propertyToDelete);
1594 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1596 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1598 return STG_E_FILENOTFOUND;
1602 * Find the parent property of the property to delete (the one that
1603 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1604 * the parent is This. Otherwise, the parent is one of it's sibling...
1608 * First, read This's StgProperty..
1610 res = StorageImpl_ReadProperty(
1611 This->ancestorStorage,
1612 This->rootPropertySetIndex,
1613 &parentProperty);
1615 assert(res==TRUE);
1618 * Second, check to see if by any chance the actual storage (This) is not
1619 * the parent of the property to delete... We never know...
1621 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1624 * Set data as it would have been done in the else part...
1626 typeOfRelation = PROPERTY_RELATION_DIR;
1627 parentPropertyId = This->rootPropertySetIndex;
1629 else
1632 * Create a property enumeration to search the parent properties, and
1633 * delete it once done.
1635 IEnumSTATSTGImpl* propertyEnumeration2;
1637 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1638 This->ancestorStorage,
1639 This->rootPropertySetIndex);
1641 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1642 propertyEnumeration2,
1643 foundPropertyIndexToDelete,
1644 &parentProperty,
1645 &parentPropertyId);
1647 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1650 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1652 hr = deleteStorageProperty(
1653 This,
1654 foundPropertyIndexToDelete,
1655 propertyToDelete);
1657 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1659 hr = deleteStreamProperty(
1660 This,
1661 foundPropertyIndexToDelete,
1662 propertyToDelete);
1665 if (hr!=S_OK)
1666 return hr;
1669 * Adjust the property chain
1671 hr = adjustPropertyChain(
1672 This,
1673 propertyToDelete,
1674 parentProperty,
1675 parentPropertyId,
1676 typeOfRelation);
1678 return hr;
1682 /*********************************************************************
1684 * Internal Method
1686 * Perform the deletion of a complete storage node
1689 static HRESULT deleteStorageProperty(
1690 StorageImpl *parentStorage,
1691 ULONG indexOfPropertyToDelete,
1692 StgProperty propertyToDelete)
1694 IEnumSTATSTG *elements = 0;
1695 IStorage *childStorage = 0;
1696 STATSTG currentElement;
1697 HRESULT hr;
1698 HRESULT destroyHr = S_OK;
1701 * Open the storage and enumerate it
1703 hr = StorageBaseImpl_OpenStorage(
1704 (IStorage*)parentStorage,
1705 propertyToDelete.name,
1707 STGM_SHARE_EXCLUSIVE,
1710 &childStorage);
1712 if (hr != S_OK)
1714 return hr;
1718 * Enumerate the elements
1720 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1725 * Obtain the next element
1727 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1728 if (hr==S_OK)
1730 destroyHr = StorageImpl_DestroyElement(
1731 (IStorage*)childStorage,
1732 (OLECHAR*)currentElement.pwcsName);
1734 CoTaskMemFree(currentElement.pwcsName);
1738 * We need to Reset the enumeration every time because we delete elements
1739 * and the enumeration could be invalid
1741 IEnumSTATSTG_Reset(elements);
1743 } while ((hr == S_OK) && (destroyHr == S_OK));
1746 * Invalidate the property by zeroing it's name member.
1748 propertyToDelete.sizeOfNameString = 0;
1750 StorageImpl_WriteProperty(parentStorage->ancestorStorage,
1751 indexOfPropertyToDelete,
1752 &propertyToDelete);
1754 IStorage_Release(childStorage);
1755 IEnumSTATSTG_Release(elements);
1757 return destroyHr;
1760 /*********************************************************************
1762 * Internal Method
1764 * Perform the deletion of a stream node
1767 static HRESULT deleteStreamProperty(
1768 StorageImpl *parentStorage,
1769 ULONG indexOfPropertyToDelete,
1770 StgProperty propertyToDelete)
1772 IStream *pis;
1773 HRESULT hr;
1774 ULARGE_INTEGER size;
1776 size.s.HighPart = 0;
1777 size.s.LowPart = 0;
1779 hr = StorageBaseImpl_OpenStream(
1780 (IStorage*)parentStorage,
1781 (OLECHAR*)propertyToDelete.name,
1782 NULL,
1783 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1785 &pis);
1787 if (hr!=S_OK)
1789 return(hr);
1793 * Zap the stream
1795 hr = IStream_SetSize(pis, size);
1797 if(hr != S_OK)
1799 return hr;
1803 * Release the stream object.
1805 IStream_Release(pis);
1808 * Invalidate the property by zeroing it's name member.
1810 propertyToDelete.sizeOfNameString = 0;
1813 * Here we should re-read the property so we get the updated pointer
1814 * but since we are here to zap it, I don't do it...
1816 StorageImpl_WriteProperty(
1817 parentStorage->ancestorStorage,
1818 indexOfPropertyToDelete,
1819 &propertyToDelete);
1821 return S_OK;
1824 /*********************************************************************
1826 * Internal Method
1828 * Finds a placeholder for the StgProperty within the Storage
1831 static HRESULT findPlaceholder(
1832 StorageImpl *storage,
1833 ULONG propertyIndexToStore,
1834 ULONG storePropertyIndex,
1835 INT typeOfRelation)
1837 StgProperty storeProperty;
1838 HRESULT hr = S_OK;
1839 BOOL res = TRUE;
1842 * Read the storage property
1844 res = StorageImpl_ReadProperty(
1845 storage->ancestorStorage,
1846 storePropertyIndex,
1847 &storeProperty);
1849 if(! res)
1851 return E_FAIL;
1854 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1856 if (storeProperty.previousProperty != PROPERTY_NULL)
1858 return findPlaceholder(
1859 storage,
1860 propertyIndexToStore,
1861 storeProperty.previousProperty,
1862 typeOfRelation);
1864 else
1866 storeProperty.previousProperty = propertyIndexToStore;
1869 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1871 if (storeProperty.nextProperty != PROPERTY_NULL)
1873 return findPlaceholder(
1874 storage,
1875 propertyIndexToStore,
1876 storeProperty.nextProperty,
1877 typeOfRelation);
1879 else
1881 storeProperty.nextProperty = propertyIndexToStore;
1884 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1886 if (storeProperty.dirProperty != PROPERTY_NULL)
1888 return findPlaceholder(
1889 storage,
1890 propertyIndexToStore,
1891 storeProperty.dirProperty,
1892 typeOfRelation);
1894 else
1896 storeProperty.dirProperty = propertyIndexToStore;
1900 hr = StorageImpl_WriteProperty(
1901 storage->ancestorStorage,
1902 storePropertyIndex,
1903 &storeProperty);
1905 if(! hr)
1907 return E_FAIL;
1910 return S_OK;
1913 /*************************************************************************
1915 * Internal Method
1917 * This method takes the previous and the next property link of a property
1918 * to be deleted and find them a place in the Storage.
1920 static HRESULT adjustPropertyChain(
1921 StorageImpl *This,
1922 StgProperty propertyToDelete,
1923 StgProperty parentProperty,
1924 ULONG parentPropertyId,
1925 INT typeOfRelation)
1927 ULONG newLinkProperty = PROPERTY_NULL;
1928 BOOL needToFindAPlaceholder = FALSE;
1929 ULONG storeNode = PROPERTY_NULL;
1930 ULONG toStoreNode = PROPERTY_NULL;
1931 INT relationType = 0;
1932 HRESULT hr = S_OK;
1933 BOOL res = TRUE;
1935 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1937 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1940 * Set the parent previous to the property to delete previous
1942 newLinkProperty = propertyToDelete.previousProperty;
1944 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1947 * We also need to find a storage for the other link, setup variables
1948 * to do this at the end...
1950 needToFindAPlaceholder = TRUE;
1951 storeNode = propertyToDelete.previousProperty;
1952 toStoreNode = propertyToDelete.nextProperty;
1953 relationType = PROPERTY_RELATION_NEXT;
1956 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1959 * Set the parent previous to the property to delete next
1961 newLinkProperty = propertyToDelete.nextProperty;
1965 * Link it for real...
1967 parentProperty.previousProperty = newLinkProperty;
1970 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1972 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1975 * Set the parent next to the property to delete next previous
1977 newLinkProperty = propertyToDelete.previousProperty;
1979 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1982 * We also need to find a storage for the other link, setup variables
1983 * to do this at the end...
1985 needToFindAPlaceholder = TRUE;
1986 storeNode = propertyToDelete.previousProperty;
1987 toStoreNode = propertyToDelete.nextProperty;
1988 relationType = PROPERTY_RELATION_NEXT;
1991 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1994 * Set the parent next to the property to delete next
1996 newLinkProperty = propertyToDelete.nextProperty;
2000 * Link it for real...
2002 parentProperty.nextProperty = newLinkProperty;
2004 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2006 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2009 * Set the parent dir to the property to delete previous
2011 newLinkProperty = propertyToDelete.previousProperty;
2013 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2016 * We also need to find a storage for the other link, setup variables
2017 * to do this at the end...
2019 needToFindAPlaceholder = TRUE;
2020 storeNode = propertyToDelete.previousProperty;
2021 toStoreNode = propertyToDelete.nextProperty;
2022 relationType = PROPERTY_RELATION_NEXT;
2025 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2028 * Set the parent dir to the property to delete next
2030 newLinkProperty = propertyToDelete.nextProperty;
2034 * Link it for real...
2036 parentProperty.dirProperty = newLinkProperty;
2040 * Write back the parent property
2042 res = StorageImpl_WriteProperty(
2043 This->ancestorStorage,
2044 parentPropertyId,
2045 &parentProperty);
2046 if(! res)
2048 return E_FAIL;
2052 * If a placeholder is required for the other link, then, find one and
2053 * get out of here...
2055 if (needToFindAPlaceholder)
2057 hr = findPlaceholder(
2058 This,
2059 toStoreNode,
2060 storeNode,
2061 relationType);
2064 return hr;
2068 /******************************************************************************
2069 * SetElementTimes (IStorage)
2071 HRESULT WINAPI StorageImpl_SetElementTimes(
2072 IStorage* iface,
2073 const OLECHAR *pwcsName,/* [string][in] */
2074 const FILETIME *pctime, /* [in] */
2075 const FILETIME *patime, /* [in] */
2076 const FILETIME *pmtime) /* [in] */
2078 FIXME("not implemented!\n");
2079 return E_NOTIMPL;
2082 /******************************************************************************
2083 * SetStateBits (IStorage)
2085 HRESULT WINAPI StorageImpl_SetStateBits(
2086 IStorage* iface,
2087 DWORD grfStateBits,/* [in] */
2088 DWORD grfMask) /* [in] */
2090 FIXME("not implemented!\n");
2091 return E_NOTIMPL;
2094 HRESULT StorageImpl_Construct(
2095 StorageImpl* This,
2096 HANDLE hFile,
2097 ILockBytes* pLkbyt,
2098 DWORD openFlags,
2099 BOOL fileBased,
2100 BOOL fileCreate)
2102 HRESULT hr = S_OK;
2103 StgProperty currentProperty;
2104 BOOL readSuccessful;
2105 ULONG currentPropertyIndex;
2107 if ( FAILED( validateSTGM(openFlags) ))
2108 return STG_E_INVALIDFLAG;
2110 memset(This, 0, sizeof(StorageImpl));
2113 * Initialize the virtual fgunction table.
2115 ICOM_VTBL(This) = &Storage32Impl_Vtbl;
2116 This->v_destructor = &StorageImpl_Destroy;
2119 * This is the top-level storage so initialize the ancester pointer
2120 * to this.
2122 This->ancestorStorage = This;
2125 * Initialize the physical support of the storage.
2127 This->hFile = hFile;
2130 * Initialize the big block cache.
2132 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2133 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2134 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2135 pLkbyt,
2136 openFlags,
2137 This->bigBlockSize,
2138 fileBased);
2140 if (This->bigBlockFile == 0)
2141 return E_FAIL;
2143 if (fileCreate)
2145 ULARGE_INTEGER size;
2146 BYTE* bigBlockBuffer;
2149 * Initialize all header variables:
2150 * - The big block depot consists of one block and it is at block 0
2151 * - The properties start at block 1
2152 * - There is no small block depot
2154 memset( This->bigBlockDepotStart,
2155 BLOCK_UNUSED,
2156 sizeof(This->bigBlockDepotStart));
2158 This->bigBlockDepotCount = 1;
2159 This->bigBlockDepotStart[0] = 0;
2160 This->rootStartBlock = 1;
2161 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2162 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2163 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2164 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2165 This->extBigBlockDepotCount = 0;
2167 StorageImpl_SaveFileHeader(This);
2170 * Add one block for the big block depot and one block for the properties
2172 size.s.HighPart = 0;
2173 size.s.LowPart = This->bigBlockSize * 3;
2174 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2177 * Initialize the big block depot
2179 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2180 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2181 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2182 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2183 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2185 else
2188 * Load the header for the file.
2190 hr = StorageImpl_LoadFileHeader(This);
2192 if (FAILED(hr))
2194 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2196 return hr;
2201 * There is no block depot cached yet.
2203 This->indexBlockDepotCached = 0xFFFFFFFF;
2206 * Start searching for free blocks with block 0.
2208 This->prevFreeBlock = 0;
2211 * Create the block chain abstractions.
2213 This->rootBlockChain =
2214 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL);
2216 This->smallBlockDepotChain = BlockChainStream_Construct(
2217 This,
2218 &This->smallBlockDepotStart,
2219 PROPERTY_NULL);
2222 * Write the root property
2224 if (fileCreate)
2226 StgProperty rootProp;
2228 * Initialize the property chain
2230 memset(&rootProp, 0, sizeof(rootProp));
2231 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2232 sizeof(rootProp.name)/sizeof(WCHAR) );
2233 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2234 rootProp.propertyType = PROPTYPE_ROOT;
2235 rootProp.previousProperty = PROPERTY_NULL;
2236 rootProp.nextProperty = PROPERTY_NULL;
2237 rootProp.dirProperty = PROPERTY_NULL;
2238 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2239 rootProp.size.s.HighPart = 0;
2240 rootProp.size.s.LowPart = 0;
2242 StorageImpl_WriteProperty(This, 0, &rootProp);
2246 * Find the ID of the root int he property sets.
2248 currentPropertyIndex = 0;
2252 readSuccessful = StorageImpl_ReadProperty(
2253 This,
2254 currentPropertyIndex,
2255 &currentProperty);
2257 if (readSuccessful)
2259 if ( (currentProperty.sizeOfNameString != 0 ) &&
2260 (currentProperty.propertyType == PROPTYPE_ROOT) )
2262 This->rootPropertySetIndex = currentPropertyIndex;
2266 currentPropertyIndex++;
2268 } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2270 if (!readSuccessful)
2272 /* TODO CLEANUP */
2273 return E_FAIL;
2277 * Create the block chain abstraction for the small block root chain.
2279 This->smallBlockRootChain = BlockChainStream_Construct(
2280 This,
2281 NULL,
2282 This->rootPropertySetIndex);
2284 return hr;
2287 void StorageImpl_Destroy(
2288 StorageImpl* This)
2290 TRACE("(%p)\n", This);
2292 BlockChainStream_Destroy(This->smallBlockRootChain);
2293 BlockChainStream_Destroy(This->rootBlockChain);
2294 BlockChainStream_Destroy(This->smallBlockDepotChain);
2296 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2297 return;
2300 /******************************************************************************
2301 * Storage32Impl_GetNextFreeBigBlock
2303 * Returns the index of the next free big block.
2304 * If the big block depot is filled, this method will enlarge it.
2307 ULONG StorageImpl_GetNextFreeBigBlock(
2308 StorageImpl* This)
2310 ULONG depotBlockIndexPos;
2311 void *depotBuffer;
2312 ULONG depotBlockOffset;
2313 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2314 ULONG nextBlockIndex = BLOCK_SPECIAL;
2315 int depotIndex = 0;
2316 ULONG freeBlock = BLOCK_UNUSED;
2318 depotIndex = This->prevFreeBlock / blocksPerDepot;
2319 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2322 * Scan the entire big block depot until we find a block marked free
2324 while (nextBlockIndex != BLOCK_UNUSED)
2326 if (depotIndex < COUNT_BBDEPOTINHEADER)
2328 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2331 * Grow the primary depot.
2333 if (depotBlockIndexPos == BLOCK_UNUSED)
2335 depotBlockIndexPos = depotIndex*blocksPerDepot;
2338 * Add a block depot.
2340 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2341 This->bigBlockDepotCount++;
2342 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2345 * Flag it as a block depot.
2347 StorageImpl_SetNextBlockInChain(This,
2348 depotBlockIndexPos,
2349 BLOCK_SPECIAL);
2351 /* Save new header information.
2353 StorageImpl_SaveFileHeader(This);
2356 else
2358 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2360 if (depotBlockIndexPos == BLOCK_UNUSED)
2363 * Grow the extended depot.
2365 ULONG extIndex = BLOCK_UNUSED;
2366 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2367 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2369 if (extBlockOffset == 0)
2371 /* We need an extended block.
2373 extIndex = Storage32Impl_AddExtBlockDepot(This);
2374 This->extBigBlockDepotCount++;
2375 depotBlockIndexPos = extIndex + 1;
2377 else
2378 depotBlockIndexPos = depotIndex * blocksPerDepot;
2381 * Add a block depot and mark it in the extended block.
2383 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2384 This->bigBlockDepotCount++;
2385 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2387 /* Flag the block depot.
2389 StorageImpl_SetNextBlockInChain(This,
2390 depotBlockIndexPos,
2391 BLOCK_SPECIAL);
2393 /* If necessary, flag the extended depot block.
2395 if (extIndex != BLOCK_UNUSED)
2396 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2398 /* Save header information.
2400 StorageImpl_SaveFileHeader(This);
2404 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2406 if (depotBuffer != 0)
2408 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2409 ( nextBlockIndex != BLOCK_UNUSED))
2411 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2413 if (nextBlockIndex == BLOCK_UNUSED)
2415 freeBlock = (depotIndex * blocksPerDepot) +
2416 (depotBlockOffset/sizeof(ULONG));
2419 depotBlockOffset += sizeof(ULONG);
2422 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2425 depotIndex++;
2426 depotBlockOffset = 0;
2429 This->prevFreeBlock = freeBlock;
2431 return freeBlock;
2434 /******************************************************************************
2435 * Storage32Impl_AddBlockDepot
2437 * This will create a depot block, essentially it is a block initialized
2438 * to BLOCK_UNUSEDs.
2440 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2442 BYTE* blockBuffer;
2444 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2447 * Initialize blocks as free
2449 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2451 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2454 /******************************************************************************
2455 * Storage32Impl_GetExtDepotBlock
2457 * Returns the index of the block that corresponds to the specified depot
2458 * index. This method is only for depot indexes equal or greater than
2459 * COUNT_BBDEPOTINHEADER.
2461 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2463 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2464 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2465 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2466 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2467 ULONG blockIndex = BLOCK_UNUSED;
2468 ULONG extBlockIndex = This->extBigBlockDepotStart;
2470 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2472 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2473 return BLOCK_UNUSED;
2475 while (extBlockCount > 0)
2477 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2478 extBlockCount--;
2481 if (extBlockIndex != BLOCK_UNUSED)
2483 BYTE* depotBuffer;
2485 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2487 if (depotBuffer != 0)
2489 StorageUtl_ReadDWord(depotBuffer,
2490 extBlockOffset * sizeof(ULONG),
2491 &blockIndex);
2493 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2497 return blockIndex;
2500 /******************************************************************************
2501 * Storage32Impl_SetExtDepotBlock
2503 * Associates the specified block index to the specified depot index.
2504 * This method is only for depot indexes equal or greater than
2505 * COUNT_BBDEPOTINHEADER.
2507 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2508 ULONG depotIndex,
2509 ULONG blockIndex)
2511 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2512 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2513 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2514 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2515 ULONG extBlockIndex = This->extBigBlockDepotStart;
2517 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2519 while (extBlockCount > 0)
2521 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2522 extBlockCount--;
2525 if (extBlockIndex != BLOCK_UNUSED)
2527 BYTE* depotBuffer;
2529 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2531 if (depotBuffer != 0)
2533 StorageUtl_WriteDWord(depotBuffer,
2534 extBlockOffset * sizeof(ULONG),
2535 blockIndex);
2537 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2542 /******************************************************************************
2543 * Storage32Impl_AddExtBlockDepot
2545 * Creates an extended depot block.
2547 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2549 ULONG numExtBlocks = This->extBigBlockDepotCount;
2550 ULONG nextExtBlock = This->extBigBlockDepotStart;
2551 BYTE* depotBuffer = NULL;
2552 ULONG index = BLOCK_UNUSED;
2553 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2554 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2555 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2557 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2558 blocksPerDepotBlock;
2560 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2563 * The first extended block.
2565 This->extBigBlockDepotStart = index;
2567 else
2569 int i;
2571 * Follow the chain to the last one.
2573 for (i = 0; i < (numExtBlocks - 1); i++)
2575 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2579 * Add the new extended block to the chain.
2581 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2582 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2583 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2587 * Initialize this block.
2589 depotBuffer = StorageImpl_GetBigBlock(This, index);
2590 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2591 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2593 return index;
2596 /******************************************************************************
2597 * Storage32Impl_FreeBigBlock
2599 * This method will flag the specified block as free in the big block depot.
2601 void StorageImpl_FreeBigBlock(
2602 StorageImpl* This,
2603 ULONG blockIndex)
2605 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2607 if (blockIndex < This->prevFreeBlock)
2608 This->prevFreeBlock = blockIndex;
2611 /************************************************************************
2612 * Storage32Impl_GetNextBlockInChain
2614 * This method will retrieve the block index of the next big block in
2615 * in the chain.
2617 * Params: This - Pointer to the Storage object.
2618 * blockIndex - Index of the block to retrieve the chain
2619 * for.
2621 * Returns: This method returns the index of the next block in the chain.
2622 * It will return the constants:
2623 * BLOCK_SPECIAL - If the block given was not part of a
2624 * chain.
2625 * BLOCK_END_OF_CHAIN - If the block given was the last in
2626 * a chain.
2627 * BLOCK_UNUSED - If the block given was not past of a chain
2628 * and is available.
2629 * BLOCK_EXTBBDEPOT - This block is part of the extended
2630 * big block depot.
2632 * See Windows documentation for more details on IStorage methods.
2634 ULONG StorageImpl_GetNextBlockInChain(
2635 StorageImpl* This,
2636 ULONG blockIndex)
2638 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2639 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2640 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2641 ULONG nextBlockIndex = BLOCK_SPECIAL;
2642 void* depotBuffer;
2643 ULONG depotBlockIndexPos;
2645 assert(depotBlockCount < This->bigBlockDepotCount);
2648 * Cache the currently accessed depot block.
2650 if (depotBlockCount != This->indexBlockDepotCached)
2652 This->indexBlockDepotCached = depotBlockCount;
2654 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2656 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2658 else
2661 * We have to look in the extended depot.
2663 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2666 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2668 if (depotBuffer!=0)
2670 int index;
2672 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2674 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &nextBlockIndex);
2675 This->blockDepotCached[index] = nextBlockIndex;
2678 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2682 nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2684 return nextBlockIndex;
2687 /******************************************************************************
2688 * Storage32Impl_GetNextExtendedBlock
2690 * Given an extended block this method will return the next extended block.
2692 * NOTES:
2693 * The last ULONG of an extended block is the block index of the next
2694 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2695 * depot.
2697 * Return values:
2698 * - The index of the next extended block
2699 * - BLOCK_UNUSED: there is no next extended block.
2700 * - Any other return values denotes failure.
2702 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2704 ULONG nextBlockIndex = BLOCK_SPECIAL;
2705 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2706 void* depotBuffer;
2708 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2710 if (depotBuffer!=0)
2712 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2714 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2717 return nextBlockIndex;
2720 /******************************************************************************
2721 * Storage32Impl_SetNextBlockInChain
2723 * This method will write the index of the specified block's next block
2724 * in the big block depot.
2726 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2727 * do the following
2729 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2730 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2731 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2734 void StorageImpl_SetNextBlockInChain(
2735 StorageImpl* This,
2736 ULONG blockIndex,
2737 ULONG nextBlock)
2739 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2740 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2741 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2742 ULONG depotBlockIndexPos;
2743 void* depotBuffer;
2745 assert(depotBlockCount < This->bigBlockDepotCount);
2746 assert(blockIndex != nextBlock);
2748 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2750 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2752 else
2755 * We have to look in the extended depot.
2757 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2760 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2762 if (depotBuffer!=0)
2764 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2765 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2769 * Update the cached block depot, if necessary.
2771 if (depotBlockCount == This->indexBlockDepotCached)
2773 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2777 /******************************************************************************
2778 * Storage32Impl_LoadFileHeader
2780 * This method will read in the file header, i.e. big block index -1.
2782 HRESULT StorageImpl_LoadFileHeader(
2783 StorageImpl* This)
2785 HRESULT hr = STG_E_FILENOTFOUND;
2786 void* headerBigBlock = NULL;
2787 int index;
2790 * Get a pointer to the big block of data containing the header.
2792 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2795 * Extract the information from the header.
2797 if (headerBigBlock!=0)
2800 * Check for the "magic number" signature and return an error if it is not
2801 * found.
2803 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2805 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2806 return STG_E_OLDFORMAT;
2809 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2811 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2812 return STG_E_INVALIDHEADER;
2815 StorageUtl_ReadWord(
2816 headerBigBlock,
2817 OFFSET_BIGBLOCKSIZEBITS,
2818 &This->bigBlockSizeBits);
2820 StorageUtl_ReadWord(
2821 headerBigBlock,
2822 OFFSET_SMALLBLOCKSIZEBITS,
2823 &This->smallBlockSizeBits);
2825 StorageUtl_ReadDWord(
2826 headerBigBlock,
2827 OFFSET_BBDEPOTCOUNT,
2828 &This->bigBlockDepotCount);
2830 StorageUtl_ReadDWord(
2831 headerBigBlock,
2832 OFFSET_ROOTSTARTBLOCK,
2833 &This->rootStartBlock);
2835 StorageUtl_ReadDWord(
2836 headerBigBlock,
2837 OFFSET_SBDEPOTSTART,
2838 &This->smallBlockDepotStart);
2840 StorageUtl_ReadDWord(
2841 headerBigBlock,
2842 OFFSET_EXTBBDEPOTSTART,
2843 &This->extBigBlockDepotStart);
2845 StorageUtl_ReadDWord(
2846 headerBigBlock,
2847 OFFSET_EXTBBDEPOTCOUNT,
2848 &This->extBigBlockDepotCount);
2850 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2852 StorageUtl_ReadDWord(
2853 headerBigBlock,
2854 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2855 &(This->bigBlockDepotStart[index]));
2859 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2861 if ((1 << 2) == 4)
2863 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2864 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2866 else
2868 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2869 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2873 * Right now, the code is making some assumptions about the size of the
2874 * blocks, just make sure they are what we're expecting.
2876 assert( (This->bigBlockSize==DEF_BIG_BLOCK_SIZE) &&
2877 (This->smallBlockSize==DEF_SMALL_BLOCK_SIZE));
2880 * Release the block.
2882 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2884 hr = S_OK;
2887 return hr;
2890 /******************************************************************************
2891 * Storage32Impl_SaveFileHeader
2893 * This method will save to the file the header, i.e. big block -1.
2895 void StorageImpl_SaveFileHeader(
2896 StorageImpl* This)
2898 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2899 int index;
2900 BOOL success;
2903 * Get a pointer to the big block of data containing the header.
2905 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2908 * If the block read failed, the file is probably new.
2910 if (!success)
2913 * Initialize for all unknown fields.
2915 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
2918 * Initialize the magic number.
2920 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
2923 * And a bunch of things we don't know what they mean
2925 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
2926 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
2927 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
2928 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
2929 StorageUtl_WriteDWord(headerBigBlock, 0x40, (DWORD)0x0001);
2933 * Write the information to the header.
2935 if (headerBigBlock!=0)
2937 StorageUtl_WriteWord(
2938 headerBigBlock,
2939 OFFSET_BIGBLOCKSIZEBITS,
2940 This->bigBlockSizeBits);
2942 StorageUtl_WriteWord(
2943 headerBigBlock,
2944 OFFSET_SMALLBLOCKSIZEBITS,
2945 This->smallBlockSizeBits);
2947 StorageUtl_WriteDWord(
2948 headerBigBlock,
2949 OFFSET_BBDEPOTCOUNT,
2950 This->bigBlockDepotCount);
2952 StorageUtl_WriteDWord(
2953 headerBigBlock,
2954 OFFSET_ROOTSTARTBLOCK,
2955 This->rootStartBlock);
2957 StorageUtl_WriteDWord(
2958 headerBigBlock,
2959 OFFSET_SBDEPOTSTART,
2960 This->smallBlockDepotStart);
2962 StorageUtl_WriteDWord(
2963 headerBigBlock,
2964 OFFSET_EXTBBDEPOTSTART,
2965 This->extBigBlockDepotStart);
2967 StorageUtl_WriteDWord(
2968 headerBigBlock,
2969 OFFSET_EXTBBDEPOTCOUNT,
2970 This->extBigBlockDepotCount);
2972 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2974 StorageUtl_WriteDWord(
2975 headerBigBlock,
2976 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2977 (This->bigBlockDepotStart[index]));
2982 * Write the big block back to the file.
2984 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
2987 /******************************************************************************
2988 * Storage32Impl_ReadProperty
2990 * This method will read the specified property from the property chain.
2992 BOOL StorageImpl_ReadProperty(
2993 StorageImpl* This,
2994 ULONG index,
2995 StgProperty* buffer)
2997 BYTE currentProperty[PROPSET_BLOCK_SIZE];
2998 ULARGE_INTEGER offsetInPropSet;
2999 BOOL readSuccessful;
3000 ULONG bytesRead;
3002 offsetInPropSet.s.HighPart = 0;
3003 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3005 readSuccessful = BlockChainStream_ReadAt(
3006 This->rootBlockChain,
3007 offsetInPropSet,
3008 PROPSET_BLOCK_SIZE,
3009 currentProperty,
3010 &bytesRead);
3012 if (readSuccessful)
3014 memset(buffer->name, 0, sizeof(buffer->name));
3015 memcpy(
3016 buffer->name,
3017 currentProperty+OFFSET_PS_NAME,
3018 PROPERTY_NAME_BUFFER_LEN );
3020 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3022 StorageUtl_ReadWord(
3023 currentProperty,
3024 OFFSET_PS_NAMELENGTH,
3025 &buffer->sizeOfNameString);
3027 StorageUtl_ReadDWord(
3028 currentProperty,
3029 OFFSET_PS_PREVIOUSPROP,
3030 &buffer->previousProperty);
3032 StorageUtl_ReadDWord(
3033 currentProperty,
3034 OFFSET_PS_NEXTPROP,
3035 &buffer->nextProperty);
3037 StorageUtl_ReadDWord(
3038 currentProperty,
3039 OFFSET_PS_DIRPROP,
3040 &buffer->dirProperty);
3042 StorageUtl_ReadGUID(
3043 currentProperty,
3044 OFFSET_PS_GUID,
3045 &buffer->propertyUniqueID);
3047 StorageUtl_ReadDWord(
3048 currentProperty,
3049 OFFSET_PS_TSS1,
3050 &buffer->timeStampS1);
3052 StorageUtl_ReadDWord(
3053 currentProperty,
3054 OFFSET_PS_TSD1,
3055 &buffer->timeStampD1);
3057 StorageUtl_ReadDWord(
3058 currentProperty,
3059 OFFSET_PS_TSS2,
3060 &buffer->timeStampS2);
3062 StorageUtl_ReadDWord(
3063 currentProperty,
3064 OFFSET_PS_TSD2,
3065 &buffer->timeStampD2);
3067 StorageUtl_ReadDWord(
3068 currentProperty,
3069 OFFSET_PS_STARTBLOCK,
3070 &buffer->startingBlock);
3072 StorageUtl_ReadDWord(
3073 currentProperty,
3074 OFFSET_PS_SIZE,
3075 &buffer->size.s.LowPart);
3077 buffer->size.s.HighPart = 0;
3080 return readSuccessful;
3083 /*********************************************************************
3084 * Write the specified property into the property chain
3086 BOOL StorageImpl_WriteProperty(
3087 StorageImpl* This,
3088 ULONG index,
3089 StgProperty* buffer)
3091 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3092 ULARGE_INTEGER offsetInPropSet;
3093 BOOL writeSuccessful;
3094 ULONG bytesWritten;
3096 offsetInPropSet.s.HighPart = 0;
3097 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3099 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3101 memcpy(
3102 currentProperty + OFFSET_PS_NAME,
3103 buffer->name,
3104 PROPERTY_NAME_BUFFER_LEN );
3106 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3108 StorageUtl_WriteWord(
3109 currentProperty,
3110 OFFSET_PS_NAMELENGTH,
3111 buffer->sizeOfNameString);
3113 StorageUtl_WriteDWord(
3114 currentProperty,
3115 OFFSET_PS_PREVIOUSPROP,
3116 buffer->previousProperty);
3118 StorageUtl_WriteDWord(
3119 currentProperty,
3120 OFFSET_PS_NEXTPROP,
3121 buffer->nextProperty);
3123 StorageUtl_WriteDWord(
3124 currentProperty,
3125 OFFSET_PS_DIRPROP,
3126 buffer->dirProperty);
3128 StorageUtl_WriteGUID(
3129 currentProperty,
3130 OFFSET_PS_GUID,
3131 &buffer->propertyUniqueID);
3133 StorageUtl_WriteDWord(
3134 currentProperty,
3135 OFFSET_PS_TSS1,
3136 buffer->timeStampS1);
3138 StorageUtl_WriteDWord(
3139 currentProperty,
3140 OFFSET_PS_TSD1,
3141 buffer->timeStampD1);
3143 StorageUtl_WriteDWord(
3144 currentProperty,
3145 OFFSET_PS_TSS2,
3146 buffer->timeStampS2);
3148 StorageUtl_WriteDWord(
3149 currentProperty,
3150 OFFSET_PS_TSD2,
3151 buffer->timeStampD2);
3153 StorageUtl_WriteDWord(
3154 currentProperty,
3155 OFFSET_PS_STARTBLOCK,
3156 buffer->startingBlock);
3158 StorageUtl_WriteDWord(
3159 currentProperty,
3160 OFFSET_PS_SIZE,
3161 buffer->size.s.LowPart);
3163 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3164 offsetInPropSet,
3165 PROPSET_BLOCK_SIZE,
3166 currentProperty,
3167 &bytesWritten);
3168 return writeSuccessful;
3171 BOOL StorageImpl_ReadBigBlock(
3172 StorageImpl* This,
3173 ULONG blockIndex,
3174 void* buffer)
3176 void* bigBlockBuffer;
3178 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3180 if (bigBlockBuffer!=0)
3182 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3184 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3186 return TRUE;
3189 return FALSE;
3192 BOOL StorageImpl_WriteBigBlock(
3193 StorageImpl* This,
3194 ULONG blockIndex,
3195 void* buffer)
3197 void* bigBlockBuffer;
3199 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3201 if (bigBlockBuffer!=0)
3203 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3205 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3207 return TRUE;
3210 return FALSE;
3213 void* StorageImpl_GetROBigBlock(
3214 StorageImpl* This,
3215 ULONG blockIndex)
3217 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3220 void* StorageImpl_GetBigBlock(
3221 StorageImpl* This,
3222 ULONG blockIndex)
3224 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3227 void StorageImpl_ReleaseBigBlock(
3228 StorageImpl* This,
3229 void* pBigBlock)
3231 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3234 /******************************************************************************
3235 * Storage32Impl_SmallBlocksToBigBlocks
3237 * This method will convert a small block chain to a big block chain.
3238 * The small block chain will be destroyed.
3240 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3241 StorageImpl* This,
3242 SmallBlockChainStream** ppsbChain)
3244 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3245 ULARGE_INTEGER size, offset;
3246 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3247 ULONG propertyIndex;
3248 BOOL successRead, successWrite;
3249 StgProperty chainProperty;
3250 BYTE *buffer;
3251 BlockChainStream *bbTempChain = NULL;
3252 BlockChainStream *bigBlockChain = NULL;
3255 * Create a temporary big block chain that doesn't have
3256 * an associated property. This temporary chain will be
3257 * used to copy data from small blocks to big blocks.
3259 bbTempChain = BlockChainStream_Construct(This,
3260 &bbHeadOfChain,
3261 PROPERTY_NULL);
3264 * Grow the big block chain.
3266 size = SmallBlockChainStream_GetSize(*ppsbChain);
3267 BlockChainStream_SetSize(bbTempChain, size);
3270 * Copy the contents of the small block chain to the big block chain
3271 * by small block size increments.
3273 offset.s.LowPart = 0;
3274 offset.s.HighPart = 0;
3275 cbTotalRead = 0;
3276 cbTotalWritten = 0;
3278 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3281 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3282 offset,
3283 DEF_SMALL_BLOCK_SIZE,
3284 buffer,
3285 &cbRead);
3286 cbTotalRead += cbRead;
3288 successWrite = BlockChainStream_WriteAt(bbTempChain,
3289 offset,
3290 cbRead,
3291 buffer,
3292 &cbWritten);
3293 cbTotalWritten += cbWritten;
3295 offset.s.LowPart += This->smallBlockSize;
3297 } while (successRead && successWrite);
3298 HeapFree(GetProcessHeap(),0,buffer);
3300 assert(cbTotalRead == cbTotalWritten);
3303 * Destroy the small block chain.
3305 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3306 size.s.HighPart = 0;
3307 size.s.LowPart = 0;
3308 SmallBlockChainStream_SetSize(*ppsbChain, size);
3309 SmallBlockChainStream_Destroy(*ppsbChain);
3310 *ppsbChain = 0;
3313 * Change the property information. This chain is now a big block chain
3314 * and it doesn't reside in the small blocks chain anymore.
3316 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3318 chainProperty.startingBlock = bbHeadOfChain;
3320 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3323 * Destroy the temporary propertyless big block chain.
3324 * Create a new big block chain associated with this property.
3326 BlockChainStream_Destroy(bbTempChain);
3327 bigBlockChain = BlockChainStream_Construct(This,
3328 NULL,
3329 propertyIndex);
3331 return bigBlockChain;
3334 /******************************************************************************
3335 ** Storage32InternalImpl implementation
3338 StorageInternalImpl* StorageInternalImpl_Construct(
3339 StorageImpl* ancestorStorage,
3340 ULONG rootPropertyIndex)
3342 StorageInternalImpl* newStorage;
3345 * Allocate space for the new storage object
3347 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3349 if (newStorage!=0)
3351 memset(newStorage, 0, sizeof(StorageInternalImpl));
3354 * Initialize the virtual function table.
3356 ICOM_VTBL(newStorage) = &Storage32InternalImpl_Vtbl;
3357 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3360 * Keep the ancestor storage pointer and nail a reference to it.
3362 newStorage->ancestorStorage = ancestorStorage;
3363 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3366 * Keep the index of the root property set for this storage,
3368 newStorage->rootPropertySetIndex = rootPropertyIndex;
3370 return newStorage;
3373 return 0;
3376 void StorageInternalImpl_Destroy(
3377 StorageInternalImpl* This)
3379 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3380 HeapFree(GetProcessHeap(), 0, This);
3383 /******************************************************************************
3385 ** Storage32InternalImpl_Commit
3387 ** The non-root storages cannot be opened in transacted mode thus this function
3388 ** does nothing.
3390 HRESULT WINAPI StorageInternalImpl_Commit(
3391 IStorage* iface,
3392 DWORD grfCommitFlags) /* [in] */
3394 return S_OK;
3397 /******************************************************************************
3399 ** Storage32InternalImpl_Revert
3401 ** The non-root storages cannot be opened in transacted mode thus this function
3402 ** does nothing.
3404 HRESULT WINAPI StorageInternalImpl_Revert(
3405 IStorage* iface)
3407 return S_OK;
3410 /******************************************************************************
3411 ** IEnumSTATSTGImpl implementation
3414 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3415 StorageImpl* parentStorage,
3416 ULONG firstPropertyNode)
3418 IEnumSTATSTGImpl* newEnumeration;
3420 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3422 if (newEnumeration!=0)
3425 * Set-up the virtual function table and reference count.
3427 ICOM_VTBL(newEnumeration) = &IEnumSTATSTGImpl_Vtbl;
3428 newEnumeration->ref = 0;
3431 * We want to nail-down the reference to the storage in case the
3432 * enumeration out-lives the storage in the client application.
3434 newEnumeration->parentStorage = parentStorage;
3435 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3437 newEnumeration->firstPropertyNode = firstPropertyNode;
3440 * Initialize the search stack
3442 newEnumeration->stackSize = 0;
3443 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3444 newEnumeration->stackToVisit =
3445 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3448 * Make sure the current node of the iterator is the first one.
3450 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3453 return newEnumeration;
3456 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3458 IStorage_Release((IStorage*)This->parentStorage);
3459 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3460 HeapFree(GetProcessHeap(), 0, This);
3463 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3464 IEnumSTATSTG* iface,
3465 REFIID riid,
3466 void** ppvObject)
3468 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3471 * Perform a sanity check on the parameters.
3473 if (ppvObject==0)
3474 return E_INVALIDARG;
3477 * Initialize the return parameter.
3479 *ppvObject = 0;
3482 * Compare the riid with the interface IDs implemented by this object.
3484 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3486 *ppvObject = (IEnumSTATSTG*)This;
3488 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3490 *ppvObject = (IEnumSTATSTG*)This;
3494 * Check that we obtained an interface.
3496 if ((*ppvObject)==0)
3497 return E_NOINTERFACE;
3500 * Query Interface always increases the reference count by one when it is
3501 * successful
3503 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3505 return S_OK;
3508 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3509 IEnumSTATSTG* iface)
3511 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3513 This->ref++;
3514 return This->ref;
3517 ULONG WINAPI IEnumSTATSTGImpl_Release(
3518 IEnumSTATSTG* iface)
3520 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3522 ULONG newRef;
3524 This->ref--;
3525 newRef = This->ref;
3528 * If the reference count goes down to 0, perform suicide.
3530 if (newRef==0)
3532 IEnumSTATSTGImpl_Destroy(This);
3535 return newRef;;
3538 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3539 IEnumSTATSTG* iface,
3540 ULONG celt,
3541 STATSTG* rgelt,
3542 ULONG* pceltFetched)
3544 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3546 StgProperty currentProperty;
3547 STATSTG* currentReturnStruct = rgelt;
3548 ULONG objectFetched = 0;
3549 ULONG currentSearchNode;
3552 * Perform a sanity check on the parameters.
3554 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3555 return E_INVALIDARG;
3558 * To avoid the special case, get another pointer to a ULONG value if
3559 * the caller didn't supply one.
3561 if (pceltFetched==0)
3562 pceltFetched = &objectFetched;
3565 * Start the iteration, we will iterate until we hit the end of the
3566 * linked list or until we hit the number of items to iterate through
3568 *pceltFetched = 0;
3571 * Start with the node at the top of the stack.
3573 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3575 while ( ( *pceltFetched < celt) &&
3576 ( currentSearchNode!=PROPERTY_NULL) )
3579 * Remove the top node from the stack
3581 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3584 * Read the property from the storage.
3586 StorageImpl_ReadProperty(This->parentStorage,
3587 currentSearchNode,
3588 &currentProperty);
3591 * Copy the information to the return buffer.
3593 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3594 &currentProperty,
3595 STATFLAG_DEFAULT);
3598 * Step to the next item in the iteration
3600 (*pceltFetched)++;
3601 currentReturnStruct++;
3604 * Push the next search node in the search stack.
3606 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3609 * continue the iteration.
3611 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3614 if (*pceltFetched == celt)
3615 return S_OK;
3617 return S_FALSE;
3621 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3622 IEnumSTATSTG* iface,
3623 ULONG celt)
3625 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3627 StgProperty currentProperty;
3628 ULONG objectFetched = 0;
3629 ULONG currentSearchNode;
3632 * Start with the node at the top of the stack.
3634 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3636 while ( (objectFetched < celt) &&
3637 (currentSearchNode!=PROPERTY_NULL) )
3640 * Remove the top node from the stack
3642 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3645 * Read the property from the storage.
3647 StorageImpl_ReadProperty(This->parentStorage,
3648 currentSearchNode,
3649 &currentProperty);
3652 * Step to the next item in the iteration
3654 objectFetched++;
3657 * Push the next search node in the search stack.
3659 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3662 * continue the iteration.
3664 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3667 if (objectFetched == celt)
3668 return S_OK;
3670 return S_FALSE;
3673 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3674 IEnumSTATSTG* iface)
3676 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3678 StgProperty rootProperty;
3679 BOOL readSuccessful;
3682 * Re-initialize the search stack to an empty stack
3684 This->stackSize = 0;
3687 * Read the root property from the storage.
3689 readSuccessful = StorageImpl_ReadProperty(
3690 This->parentStorage,
3691 This->firstPropertyNode,
3692 &rootProperty);
3694 if (readSuccessful)
3696 assert(rootProperty.sizeOfNameString!=0);
3699 * Push the search node in the search stack.
3701 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3704 return S_OK;
3707 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3708 IEnumSTATSTG* iface,
3709 IEnumSTATSTG** ppenum)
3711 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3713 IEnumSTATSTGImpl* newClone;
3716 * Perform a sanity check on the parameters.
3718 if (ppenum==0)
3719 return E_INVALIDARG;
3721 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3722 This->firstPropertyNode);
3726 * The new clone enumeration must point to the same current node as
3727 * the ole one.
3729 newClone->stackSize = This->stackSize ;
3730 newClone->stackMaxSize = This->stackMaxSize ;
3731 newClone->stackToVisit =
3732 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3734 memcpy(
3735 newClone->stackToVisit,
3736 This->stackToVisit,
3737 sizeof(ULONG) * newClone->stackSize);
3739 *ppenum = (IEnumSTATSTG*)newClone;
3742 * Don't forget to nail down a reference to the clone before
3743 * returning it.
3745 IEnumSTATSTGImpl_AddRef(*ppenum);
3747 return S_OK;
3750 INT IEnumSTATSTGImpl_FindParentProperty(
3751 IEnumSTATSTGImpl *This,
3752 ULONG childProperty,
3753 StgProperty *currentProperty,
3754 ULONG *thisNodeId)
3756 ULONG currentSearchNode;
3757 ULONG foundNode;
3760 * To avoid the special case, get another pointer to a ULONG value if
3761 * the caller didn't supply one.
3763 if (thisNodeId==0)
3764 thisNodeId = &foundNode;
3767 * Start with the node at the top of the stack.
3769 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3772 while (currentSearchNode!=PROPERTY_NULL)
3775 * Store the current node in the returned parameters
3777 *thisNodeId = currentSearchNode;
3780 * Remove the top node from the stack
3782 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3785 * Read the property from the storage.
3787 StorageImpl_ReadProperty(
3788 This->parentStorage,
3789 currentSearchNode,
3790 currentProperty);
3792 if (currentProperty->previousProperty == childProperty)
3793 return PROPERTY_RELATION_PREVIOUS;
3795 else if (currentProperty->nextProperty == childProperty)
3796 return PROPERTY_RELATION_NEXT;
3798 else if (currentProperty->dirProperty == childProperty)
3799 return PROPERTY_RELATION_DIR;
3802 * Push the next search node in the search stack.
3804 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3807 * continue the iteration.
3809 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3812 return PROPERTY_NULL;
3815 ULONG IEnumSTATSTGImpl_FindProperty(
3816 IEnumSTATSTGImpl* This,
3817 const OLECHAR* lpszPropName,
3818 StgProperty* currentProperty)
3820 ULONG currentSearchNode;
3823 * Start with the node at the top of the stack.
3825 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3827 while (currentSearchNode!=PROPERTY_NULL)
3830 * Remove the top node from the stack
3832 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3835 * Read the property from the storage.
3837 StorageImpl_ReadProperty(This->parentStorage,
3838 currentSearchNode,
3839 currentProperty);
3841 if ( propertyNameCmp(
3842 (OLECHAR*)currentProperty->name,
3843 (OLECHAR*)lpszPropName) == 0)
3844 return currentSearchNode;
3847 * Push the next search node in the search stack.
3849 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3852 * continue the iteration.
3854 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3857 return PROPERTY_NULL;
3860 void IEnumSTATSTGImpl_PushSearchNode(
3861 IEnumSTATSTGImpl* This,
3862 ULONG nodeToPush)
3864 StgProperty rootProperty;
3865 BOOL readSuccessful;
3868 * First, make sure we're not trying to push an unexisting node.
3870 if (nodeToPush==PROPERTY_NULL)
3871 return;
3874 * First push the node to the stack
3876 if (This->stackSize == This->stackMaxSize)
3878 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3880 This->stackToVisit = HeapReAlloc(
3881 GetProcessHeap(),
3883 This->stackToVisit,
3884 sizeof(ULONG) * This->stackMaxSize);
3887 This->stackToVisit[This->stackSize] = nodeToPush;
3888 This->stackSize++;
3891 * Read the root property from the storage.
3893 readSuccessful = StorageImpl_ReadProperty(
3894 This->parentStorage,
3895 nodeToPush,
3896 &rootProperty);
3898 if (readSuccessful)
3900 assert(rootProperty.sizeOfNameString!=0);
3903 * Push the previous search node in the search stack.
3905 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3909 ULONG IEnumSTATSTGImpl_PopSearchNode(
3910 IEnumSTATSTGImpl* This,
3911 BOOL remove)
3913 ULONG topNode;
3915 if (This->stackSize == 0)
3916 return PROPERTY_NULL;
3918 topNode = This->stackToVisit[This->stackSize-1];
3920 if (remove)
3921 This->stackSize--;
3923 return topNode;
3926 /******************************************************************************
3927 ** StorageUtl implementation
3930 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
3932 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
3935 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
3937 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
3940 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
3942 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
3945 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
3947 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
3950 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
3952 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
3953 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
3954 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
3956 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
3959 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
3961 StorageUtl_WriteDWord(buffer, offset, value->Data1);
3962 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
3963 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
3965 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
3968 void StorageUtl_CopyPropertyToSTATSTG(
3969 STATSTG* destination,
3970 StgProperty* source,
3971 int statFlags)
3974 * The copy of the string occurs only when the flag is not set
3976 if ((statFlags & STATFLAG_NONAME) != 0)
3978 destination->pwcsName = 0;
3980 else
3982 destination->pwcsName =
3983 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
3985 strcpyW((LPWSTR)destination->pwcsName, source->name);
3988 switch (source->propertyType)
3990 case PROPTYPE_STORAGE:
3991 case PROPTYPE_ROOT:
3992 destination->type = STGTY_STORAGE;
3993 break;
3994 case PROPTYPE_STREAM:
3995 destination->type = STGTY_STREAM;
3996 break;
3997 default:
3998 destination->type = STGTY_STREAM;
3999 break;
4002 destination->cbSize = source->size;
4004 currentReturnStruct->mtime = {0}; TODO
4005 currentReturnStruct->ctime = {0};
4006 currentReturnStruct->atime = {0};
4008 destination->grfMode = 0;
4009 destination->grfLocksSupported = 0;
4010 destination->clsid = source->propertyUniqueID;
4011 destination->grfStateBits = 0;
4012 destination->reserved = 0;
4015 /******************************************************************************
4016 ** BlockChainStream implementation
4019 BlockChainStream* BlockChainStream_Construct(
4020 StorageImpl* parentStorage,
4021 ULONG* headOfStreamPlaceHolder,
4022 ULONG propertyIndex)
4024 BlockChainStream* newStream;
4025 ULONG blockIndex;
4027 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4029 newStream->parentStorage = parentStorage;
4030 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4031 newStream->ownerPropertyIndex = propertyIndex;
4032 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4033 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4034 newStream->numBlocks = 0;
4036 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4038 while (blockIndex != BLOCK_END_OF_CHAIN)
4040 newStream->numBlocks++;
4041 newStream->tailIndex = blockIndex;
4043 blockIndex = StorageImpl_GetNextBlockInChain(
4044 parentStorage,
4045 blockIndex);
4048 return newStream;
4051 void BlockChainStream_Destroy(BlockChainStream* This)
4053 HeapFree(GetProcessHeap(), 0, This);
4056 /******************************************************************************
4057 * BlockChainStream_GetHeadOfChain
4059 * Returns the head of this stream chain.
4060 * Some special chains don't have properties, their heads are kept in
4061 * This->headOfStreamPlaceHolder.
4064 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4066 StgProperty chainProperty;
4067 BOOL readSuccessful;
4069 if (This->headOfStreamPlaceHolder != 0)
4070 return *(This->headOfStreamPlaceHolder);
4072 if (This->ownerPropertyIndex != PROPERTY_NULL)
4074 readSuccessful = StorageImpl_ReadProperty(
4075 This->parentStorage,
4076 This->ownerPropertyIndex,
4077 &chainProperty);
4079 if (readSuccessful)
4081 return chainProperty.startingBlock;
4085 return BLOCK_END_OF_CHAIN;
4088 /******************************************************************************
4089 * BlockChainStream_GetCount
4091 * Returns the number of blocks that comprises this chain.
4092 * This is not the size of the stream as the last block may not be full!
4095 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4097 ULONG blockIndex;
4098 ULONG count = 0;
4100 blockIndex = BlockChainStream_GetHeadOfChain(This);
4102 while (blockIndex != BLOCK_END_OF_CHAIN)
4104 count++;
4106 blockIndex = StorageImpl_GetNextBlockInChain(
4107 This->parentStorage,
4108 blockIndex);
4111 return count;
4114 /******************************************************************************
4115 * BlockChainStream_ReadAt
4117 * Reads a specified number of bytes from this chain at the specified offset.
4118 * bytesRead may be NULL.
4119 * Failure will be returned if the specified number of bytes has not been read.
4121 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4122 ULARGE_INTEGER offset,
4123 ULONG size,
4124 void* buffer,
4125 ULONG* bytesRead)
4127 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4128 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4129 ULONG bytesToReadInBuffer;
4130 ULONG blockIndex;
4131 BYTE* bufferWalker;
4132 BYTE* bigBlockBuffer;
4135 * Find the first block in the stream that contains part of the buffer.
4137 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4138 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4139 (blockNoInSequence < This->lastBlockNoInSequence) )
4141 blockIndex = BlockChainStream_GetHeadOfChain(This);
4142 This->lastBlockNoInSequence = blockNoInSequence;
4144 else
4146 ULONG temp = blockNoInSequence;
4148 blockIndex = This->lastBlockNoInSequenceIndex;
4149 blockNoInSequence -= This->lastBlockNoInSequence;
4150 This->lastBlockNoInSequence = temp;
4153 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4155 blockIndex =
4156 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4158 blockNoInSequence--;
4161 This->lastBlockNoInSequenceIndex = blockIndex;
4164 * Start reading the buffer.
4166 *bytesRead = 0;
4167 bufferWalker = buffer;
4169 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4172 * Calculate how many bytes we can copy from this big block.
4174 bytesToReadInBuffer =
4175 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4178 * Copy those bytes to the buffer
4180 bigBlockBuffer =
4181 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4183 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4185 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4188 * Step to the next big block.
4190 blockIndex =
4191 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4193 bufferWalker += bytesToReadInBuffer;
4194 size -= bytesToReadInBuffer;
4195 *bytesRead += bytesToReadInBuffer;
4196 offsetInBlock = 0; /* There is no offset on the next block */
4200 return (size == 0);
4203 /******************************************************************************
4204 * BlockChainStream_WriteAt
4206 * Writes the specified number of bytes to this chain at the specified offset.
4207 * bytesWritten may be NULL.
4208 * Will fail if not all specified number of bytes have been written.
4210 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4211 ULARGE_INTEGER offset,
4212 ULONG size,
4213 const void* buffer,
4214 ULONG* bytesWritten)
4216 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4217 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4218 ULONG bytesToWrite;
4219 ULONG blockIndex;
4220 BYTE* bufferWalker;
4221 BYTE* bigBlockBuffer;
4224 * Find the first block in the stream that contains part of the buffer.
4226 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4227 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4228 (blockNoInSequence < This->lastBlockNoInSequence) )
4230 blockIndex = BlockChainStream_GetHeadOfChain(This);
4231 This->lastBlockNoInSequence = blockNoInSequence;
4233 else
4235 ULONG temp = blockNoInSequence;
4237 blockIndex = This->lastBlockNoInSequenceIndex;
4238 blockNoInSequence -= This->lastBlockNoInSequence;
4239 This->lastBlockNoInSequence = temp;
4242 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4244 blockIndex =
4245 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4247 blockNoInSequence--;
4250 This->lastBlockNoInSequenceIndex = blockIndex;
4253 * Here, I'm casting away the constness on the buffer variable
4254 * This is OK since we don't intend to modify that buffer.
4256 *bytesWritten = 0;
4257 bufferWalker = (BYTE*)buffer;
4259 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4262 * Calculate how many bytes we can copy from this big block.
4264 bytesToWrite =
4265 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4268 * Copy those bytes to the buffer
4270 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4272 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4274 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4277 * Step to the next big block.
4279 blockIndex =
4280 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4282 bufferWalker += bytesToWrite;
4283 size -= bytesToWrite;
4284 *bytesWritten += bytesToWrite;
4285 offsetInBlock = 0; /* There is no offset on the next block */
4288 return (size == 0);
4291 /******************************************************************************
4292 * BlockChainStream_Shrink
4294 * Shrinks this chain in the big block depot.
4296 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4297 ULARGE_INTEGER newSize)
4299 ULONG blockIndex, extraBlock;
4300 ULONG numBlocks;
4301 ULONG count = 1;
4304 * Reset the last accessed block cache.
4306 This->lastBlockNoInSequence = 0xFFFFFFFF;
4307 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4310 * Figure out how many blocks are needed to contain the new size
4312 numBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4314 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4315 numBlocks++;
4317 blockIndex = BlockChainStream_GetHeadOfChain(This);
4320 * Go to the new end of chain
4322 while (count < numBlocks)
4324 blockIndex =
4325 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4327 count++;
4330 /* Get the next block before marking the new end */
4331 extraBlock =
4332 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4334 /* Mark the new end of chain */
4335 StorageImpl_SetNextBlockInChain(
4336 This->parentStorage,
4337 blockIndex,
4338 BLOCK_END_OF_CHAIN);
4340 This->tailIndex = blockIndex;
4341 This->numBlocks = numBlocks;
4344 * Mark the extra blocks as free
4346 while (extraBlock != BLOCK_END_OF_CHAIN)
4348 blockIndex =
4349 StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock);
4351 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4352 extraBlock = blockIndex;
4355 return TRUE;
4358 /******************************************************************************
4359 * BlockChainStream_Enlarge
4361 * Grows this chain in the big block depot.
4363 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4364 ULARGE_INTEGER newSize)
4366 ULONG blockIndex, currentBlock;
4367 ULONG newNumBlocks;
4368 ULONG oldNumBlocks = 0;
4370 blockIndex = BlockChainStream_GetHeadOfChain(This);
4373 * Empty chain. Create the head.
4375 if (blockIndex == BLOCK_END_OF_CHAIN)
4377 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4378 StorageImpl_SetNextBlockInChain(This->parentStorage,
4379 blockIndex,
4380 BLOCK_END_OF_CHAIN);
4382 if (This->headOfStreamPlaceHolder != 0)
4384 *(This->headOfStreamPlaceHolder) = blockIndex;
4386 else
4388 StgProperty chainProp;
4389 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4391 StorageImpl_ReadProperty(
4392 This->parentStorage,
4393 This->ownerPropertyIndex,
4394 &chainProp);
4396 chainProp.startingBlock = blockIndex;
4398 StorageImpl_WriteProperty(
4399 This->parentStorage,
4400 This->ownerPropertyIndex,
4401 &chainProp);
4404 This->tailIndex = blockIndex;
4405 This->numBlocks = 1;
4409 * Figure out how many blocks are needed to contain this stream
4411 newNumBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4413 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4414 newNumBlocks++;
4417 * Go to the current end of chain
4419 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4421 currentBlock = blockIndex;
4423 while (blockIndex != BLOCK_END_OF_CHAIN)
4425 This->numBlocks++;
4426 currentBlock = blockIndex;
4428 blockIndex =
4429 StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock);
4432 This->tailIndex = currentBlock;
4435 currentBlock = This->tailIndex;
4436 oldNumBlocks = This->numBlocks;
4439 * Add new blocks to the chain
4441 if (oldNumBlocks < newNumBlocks)
4443 while (oldNumBlocks < newNumBlocks)
4445 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4447 StorageImpl_SetNextBlockInChain(
4448 This->parentStorage,
4449 currentBlock,
4450 blockIndex);
4452 StorageImpl_SetNextBlockInChain(
4453 This->parentStorage,
4454 blockIndex,
4455 BLOCK_END_OF_CHAIN);
4457 currentBlock = blockIndex;
4458 oldNumBlocks++;
4461 This->tailIndex = blockIndex;
4462 This->numBlocks = newNumBlocks;
4465 return TRUE;
4468 /******************************************************************************
4469 * BlockChainStream_SetSize
4471 * Sets the size of this stream. The big block depot will be updated.
4472 * The file will grow if we grow the chain.
4474 * TODO: Free the actual blocks in the file when we shrink the chain.
4475 * Currently, the blocks are still in the file. So the file size
4476 * doesn't shrink even if we shrink streams.
4478 BOOL BlockChainStream_SetSize(
4479 BlockChainStream* This,
4480 ULARGE_INTEGER newSize)
4482 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4484 if (newSize.s.LowPart == size.s.LowPart)
4485 return TRUE;
4487 if (newSize.s.LowPart < size.s.LowPart)
4489 BlockChainStream_Shrink(This, newSize);
4491 else
4493 ULARGE_INTEGER fileSize =
4494 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4496 ULONG diff = newSize.s.LowPart - size.s.LowPart;
4499 * Make sure the file stays a multiple of blocksize
4501 if ((diff % This->parentStorage->bigBlockSize) != 0)
4502 diff += (This->parentStorage->bigBlockSize -
4503 (diff % This->parentStorage->bigBlockSize) );
4505 fileSize.s.LowPart += diff;
4506 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4508 BlockChainStream_Enlarge(This, newSize);
4511 return TRUE;
4514 /******************************************************************************
4515 * BlockChainStream_GetSize
4517 * Returns the size of this chain.
4518 * Will return the block count if this chain doesn't have a property.
4520 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4522 StgProperty chainProperty;
4524 if(This->headOfStreamPlaceHolder == NULL)
4527 * This chain is a data stream read the property and return
4528 * the appropriate size
4530 StorageImpl_ReadProperty(
4531 This->parentStorage,
4532 This->ownerPropertyIndex,
4533 &chainProperty);
4535 return chainProperty.size;
4537 else
4540 * this chain is a chain that does not have a property, figure out the
4541 * size by making the product number of used blocks times the
4542 * size of them
4544 ULARGE_INTEGER result;
4545 result.s.HighPart = 0;
4547 result.s.LowPart =
4548 BlockChainStream_GetCount(This) *
4549 This->parentStorage->bigBlockSize;
4551 return result;
4555 /******************************************************************************
4556 ** SmallBlockChainStream implementation
4559 SmallBlockChainStream* SmallBlockChainStream_Construct(
4560 StorageImpl* parentStorage,
4561 ULONG propertyIndex)
4563 SmallBlockChainStream* newStream;
4565 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4567 newStream->parentStorage = parentStorage;
4568 newStream->ownerPropertyIndex = propertyIndex;
4570 return newStream;
4573 void SmallBlockChainStream_Destroy(
4574 SmallBlockChainStream* This)
4576 HeapFree(GetProcessHeap(), 0, This);
4579 /******************************************************************************
4580 * SmallBlockChainStream_GetHeadOfChain
4582 * Returns the head of this chain of small blocks.
4584 ULONG SmallBlockChainStream_GetHeadOfChain(
4585 SmallBlockChainStream* This)
4587 StgProperty chainProperty;
4588 BOOL readSuccessful;
4590 if (This->ownerPropertyIndex)
4592 readSuccessful = StorageImpl_ReadProperty(
4593 This->parentStorage,
4594 This->ownerPropertyIndex,
4595 &chainProperty);
4597 if (readSuccessful)
4599 return chainProperty.startingBlock;
4604 return BLOCK_END_OF_CHAIN;
4607 /******************************************************************************
4608 * SmallBlockChainStream_GetNextBlockInChain
4610 * Returns the index of the next small block in this chain.
4612 * Return Values:
4613 * - BLOCK_END_OF_CHAIN: end of this chain
4614 * - BLOCK_UNUSED: small block 'blockIndex' is free
4616 ULONG SmallBlockChainStream_GetNextBlockInChain(
4617 SmallBlockChainStream* This,
4618 ULONG blockIndex)
4620 ULARGE_INTEGER offsetOfBlockInDepot;
4621 DWORD buffer;
4622 ULONG nextBlockInChain = BLOCK_END_OF_CHAIN;
4623 ULONG bytesRead;
4624 BOOL success;
4626 offsetOfBlockInDepot.s.HighPart = 0;
4627 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4630 * Read those bytes in the buffer from the small block file.
4632 success = BlockChainStream_ReadAt(
4633 This->parentStorage->smallBlockDepotChain,
4634 offsetOfBlockInDepot,
4635 sizeof(DWORD),
4636 &buffer,
4637 &bytesRead);
4639 if (success)
4641 StorageUtl_ReadDWord(&buffer, 0, &nextBlockInChain);
4644 return nextBlockInChain;
4647 /******************************************************************************
4648 * SmallBlockChainStream_SetNextBlockInChain
4650 * Writes the index of the next block of the specified block in the small
4651 * block depot.
4652 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4653 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4655 void SmallBlockChainStream_SetNextBlockInChain(
4656 SmallBlockChainStream* This,
4657 ULONG blockIndex,
4658 ULONG nextBlock)
4660 ULARGE_INTEGER offsetOfBlockInDepot;
4661 DWORD buffer;
4662 ULONG bytesWritten;
4664 offsetOfBlockInDepot.s.HighPart = 0;
4665 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4667 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4670 * Read those bytes in the buffer from the small block file.
4672 BlockChainStream_WriteAt(
4673 This->parentStorage->smallBlockDepotChain,
4674 offsetOfBlockInDepot,
4675 sizeof(DWORD),
4676 &buffer,
4677 &bytesWritten);
4680 /******************************************************************************
4681 * SmallBlockChainStream_FreeBlock
4683 * Flag small block 'blockIndex' as free in the small block depot.
4685 void SmallBlockChainStream_FreeBlock(
4686 SmallBlockChainStream* This,
4687 ULONG blockIndex)
4689 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4692 /******************************************************************************
4693 * SmallBlockChainStream_GetNextFreeBlock
4695 * Returns the index of a free small block. The small block depot will be
4696 * enlarged if necessary. The small block chain will also be enlarged if
4697 * necessary.
4699 ULONG SmallBlockChainStream_GetNextFreeBlock(
4700 SmallBlockChainStream* This)
4702 ULARGE_INTEGER offsetOfBlockInDepot;
4703 DWORD buffer;
4704 ULONG bytesRead;
4705 ULONG blockIndex = 0;
4706 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4707 BOOL success = TRUE;
4708 ULONG smallBlocksPerBigBlock;
4710 offsetOfBlockInDepot.s.HighPart = 0;
4713 * Scan the small block depot for a free block
4715 while (nextBlockIndex != BLOCK_UNUSED)
4717 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4719 success = BlockChainStream_ReadAt(
4720 This->parentStorage->smallBlockDepotChain,
4721 offsetOfBlockInDepot,
4722 sizeof(DWORD),
4723 &buffer,
4724 &bytesRead);
4727 * If we run out of space for the small block depot, enlarge it
4729 if (success)
4731 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4733 if (nextBlockIndex != BLOCK_UNUSED)
4734 blockIndex++;
4736 else
4738 ULONG count =
4739 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4741 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4742 ULONG nextBlock, newsbdIndex;
4743 BYTE* smallBlockDepot;
4745 nextBlock = sbdIndex;
4746 while (nextBlock != BLOCK_END_OF_CHAIN)
4748 sbdIndex = nextBlock;
4749 nextBlock =
4750 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex);
4753 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4754 if (sbdIndex != BLOCK_END_OF_CHAIN)
4755 StorageImpl_SetNextBlockInChain(
4756 This->parentStorage,
4757 sbdIndex,
4758 newsbdIndex);
4760 StorageImpl_SetNextBlockInChain(
4761 This->parentStorage,
4762 newsbdIndex,
4763 BLOCK_END_OF_CHAIN);
4766 * Initialize all the small blocks to free
4768 smallBlockDepot =
4769 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4771 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4772 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4774 if (count == 0)
4777 * We have just created the small block depot.
4779 StgProperty rootProp;
4780 ULONG sbStartIndex;
4783 * Save it in the header
4785 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4786 StorageImpl_SaveFileHeader(This->parentStorage);
4789 * And allocate the first big block that will contain small blocks
4791 sbStartIndex =
4792 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4794 StorageImpl_SetNextBlockInChain(
4795 This->parentStorage,
4796 sbStartIndex,
4797 BLOCK_END_OF_CHAIN);
4799 StorageImpl_ReadProperty(
4800 This->parentStorage,
4801 This->parentStorage->rootPropertySetIndex,
4802 &rootProp);
4804 rootProp.startingBlock = sbStartIndex;
4805 rootProp.size.s.HighPart = 0;
4806 rootProp.size.s.LowPart = This->parentStorage->bigBlockSize;
4808 StorageImpl_WriteProperty(
4809 This->parentStorage,
4810 This->parentStorage->rootPropertySetIndex,
4811 &rootProp);
4816 smallBlocksPerBigBlock =
4817 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4820 * Verify if we have to allocate big blocks to contain small blocks
4822 if (blockIndex % smallBlocksPerBigBlock == 0)
4824 StgProperty rootProp;
4825 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4827 StorageImpl_ReadProperty(
4828 This->parentStorage,
4829 This->parentStorage->rootPropertySetIndex,
4830 &rootProp);
4832 if (rootProp.size.s.LowPart <
4833 (blocksRequired * This->parentStorage->bigBlockSize))
4835 rootProp.size.s.LowPart += This->parentStorage->bigBlockSize;
4837 BlockChainStream_SetSize(
4838 This->parentStorage->smallBlockRootChain,
4839 rootProp.size);
4841 StorageImpl_WriteProperty(
4842 This->parentStorage,
4843 This->parentStorage->rootPropertySetIndex,
4844 &rootProp);
4848 return blockIndex;
4851 /******************************************************************************
4852 * SmallBlockChainStream_ReadAt
4854 * Reads a specified number of bytes from this chain at the specified offset.
4855 * bytesRead may be NULL.
4856 * Failure will be returned if the specified number of bytes has not been read.
4858 BOOL SmallBlockChainStream_ReadAt(
4859 SmallBlockChainStream* This,
4860 ULARGE_INTEGER offset,
4861 ULONG size,
4862 void* buffer,
4863 ULONG* bytesRead)
4865 ULARGE_INTEGER offsetInBigBlockFile;
4866 ULONG blockNoInSequence =
4867 offset.s.LowPart / This->parentStorage->smallBlockSize;
4869 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
4870 ULONG bytesToReadInBuffer;
4871 ULONG blockIndex;
4872 ULONG bytesReadFromBigBlockFile;
4873 BYTE* bufferWalker;
4876 * This should never happen on a small block file.
4878 assert(offset.s.HighPart==0);
4881 * Find the first block in the stream that contains part of the buffer.
4883 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4885 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4887 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4889 blockNoInSequence--;
4893 * Start reading the buffer.
4895 *bytesRead = 0;
4896 bufferWalker = buffer;
4898 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4901 * Calculate how many bytes we can copy from this small block.
4903 bytesToReadInBuffer =
4904 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
4907 * Calculate the offset of the small block in the small block file.
4909 offsetInBigBlockFile.s.HighPart = 0;
4910 offsetInBigBlockFile.s.LowPart =
4911 blockIndex * This->parentStorage->smallBlockSize;
4913 offsetInBigBlockFile.s.LowPart += offsetInBlock;
4916 * Read those bytes in the buffer from the small block file.
4918 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
4919 offsetInBigBlockFile,
4920 bytesToReadInBuffer,
4921 bufferWalker,
4922 &bytesReadFromBigBlockFile);
4924 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
4927 * Step to the next big block.
4929 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4930 bufferWalker += bytesToReadInBuffer;
4931 size -= bytesToReadInBuffer;
4932 *bytesRead += bytesToReadInBuffer;
4933 offsetInBlock = 0; /* There is no offset on the next block */
4936 return (size == 0);
4939 /******************************************************************************
4940 * SmallBlockChainStream_WriteAt
4942 * Writes the specified number of bytes to this chain at the specified offset.
4943 * bytesWritten may be NULL.
4944 * Will fail if not all specified number of bytes have been written.
4946 BOOL SmallBlockChainStream_WriteAt(
4947 SmallBlockChainStream* This,
4948 ULARGE_INTEGER offset,
4949 ULONG size,
4950 const void* buffer,
4951 ULONG* bytesWritten)
4953 ULARGE_INTEGER offsetInBigBlockFile;
4954 ULONG blockNoInSequence =
4955 offset.s.LowPart / This->parentStorage->smallBlockSize;
4957 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
4958 ULONG bytesToWriteInBuffer;
4959 ULONG blockIndex;
4960 ULONG bytesWrittenFromBigBlockFile;
4961 BYTE* bufferWalker;
4964 * This should never happen on a small block file.
4966 assert(offset.s.HighPart==0);
4969 * Find the first block in the stream that contains part of the buffer.
4971 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4973 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4975 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4977 blockNoInSequence--;
4981 * Start writing the buffer.
4983 * Here, I'm casting away the constness on the buffer variable
4984 * This is OK since we don't intend to modify that buffer.
4986 *bytesWritten = 0;
4987 bufferWalker = (BYTE*)buffer;
4988 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4991 * Calculate how many bytes we can copy to this small block.
4993 bytesToWriteInBuffer =
4994 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
4997 * Calculate the offset of the small block in the small block file.
4999 offsetInBigBlockFile.s.HighPart = 0;
5000 offsetInBigBlockFile.s.LowPart =
5001 blockIndex * This->parentStorage->smallBlockSize;
5003 offsetInBigBlockFile.s.LowPart += offsetInBlock;
5006 * Write those bytes in the buffer to the small block file.
5008 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5009 offsetInBigBlockFile,
5010 bytesToWriteInBuffer,
5011 bufferWalker,
5012 &bytesWrittenFromBigBlockFile);
5014 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5017 * Step to the next big block.
5019 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5020 bufferWalker += bytesToWriteInBuffer;
5021 size -= bytesToWriteInBuffer;
5022 *bytesWritten += bytesToWriteInBuffer;
5023 offsetInBlock = 0; /* There is no offset on the next block */
5026 return (size == 0);
5029 /******************************************************************************
5030 * SmallBlockChainStream_Shrink
5032 * Shrinks this chain in the small block depot.
5034 BOOL SmallBlockChainStream_Shrink(
5035 SmallBlockChainStream* This,
5036 ULARGE_INTEGER newSize)
5038 ULONG blockIndex, extraBlock;
5039 ULONG numBlocks;
5040 ULONG count = 0;
5042 numBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5044 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5045 numBlocks++;
5047 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5050 * Go to the new end of chain
5052 while (count < numBlocks)
5054 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5055 count++;
5059 * If the count is 0, we have a special case, the head of the chain was
5060 * just freed.
5062 if (count == 0)
5064 StgProperty chainProp;
5066 StorageImpl_ReadProperty(This->parentStorage,
5067 This->ownerPropertyIndex,
5068 &chainProp);
5070 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5072 StorageImpl_WriteProperty(This->parentStorage,
5073 This->ownerPropertyIndex,
5074 &chainProp);
5077 * We start freeing the chain at the head block.
5079 extraBlock = blockIndex;
5081 else
5083 /* Get the next block before marking the new end */
5084 extraBlock = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5086 /* Mark the new end of chain */
5087 SmallBlockChainStream_SetNextBlockInChain(
5088 This,
5089 blockIndex,
5090 BLOCK_END_OF_CHAIN);
5094 * Mark the extra blocks as free
5096 while (extraBlock != BLOCK_END_OF_CHAIN)
5098 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, extraBlock);
5099 SmallBlockChainStream_FreeBlock(This, extraBlock);
5100 extraBlock = blockIndex;
5103 return TRUE;
5106 /******************************************************************************
5107 * SmallBlockChainStream_Enlarge
5109 * Grows this chain in the small block depot.
5111 BOOL SmallBlockChainStream_Enlarge(
5112 SmallBlockChainStream* This,
5113 ULARGE_INTEGER newSize)
5115 ULONG blockIndex, currentBlock;
5116 ULONG newNumBlocks;
5117 ULONG oldNumBlocks = 0;
5119 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5122 * Empty chain
5124 if (blockIndex == BLOCK_END_OF_CHAIN)
5127 StgProperty chainProp;
5129 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5130 &chainProp);
5132 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5134 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5135 &chainProp);
5137 blockIndex = chainProp.startingBlock;
5138 SmallBlockChainStream_SetNextBlockInChain(
5139 This,
5140 blockIndex,
5141 BLOCK_END_OF_CHAIN);
5144 currentBlock = blockIndex;
5147 * Figure out how many blocks are needed to contain this stream
5149 newNumBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5151 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5152 newNumBlocks++;
5155 * Go to the current end of chain
5157 while (blockIndex != BLOCK_END_OF_CHAIN)
5159 oldNumBlocks++;
5160 currentBlock = blockIndex;
5161 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, currentBlock);
5165 * Add new blocks to the chain
5167 while (oldNumBlocks < newNumBlocks)
5169 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5170 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5172 SmallBlockChainStream_SetNextBlockInChain(
5173 This,
5174 blockIndex,
5175 BLOCK_END_OF_CHAIN);
5177 currentBlock = blockIndex;
5178 oldNumBlocks++;
5181 return TRUE;
5184 /******************************************************************************
5185 * SmallBlockChainStream_GetCount
5187 * Returns the number of blocks that comprises this chain.
5188 * This is not the size of this chain as the last block may not be full!
5190 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5192 ULONG blockIndex;
5193 ULONG count = 0;
5195 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5197 while (blockIndex != BLOCK_END_OF_CHAIN)
5199 count++;
5201 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5204 return count;
5207 /******************************************************************************
5208 * SmallBlockChainStream_SetSize
5210 * Sets the size of this stream.
5211 * The file will grow if we grow the chain.
5213 * TODO: Free the actual blocks in the file when we shrink the chain.
5214 * Currently, the blocks are still in the file. So the file size
5215 * doesn't shrink even if we shrink streams.
5217 BOOL SmallBlockChainStream_SetSize(
5218 SmallBlockChainStream* This,
5219 ULARGE_INTEGER newSize)
5221 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5223 if (newSize.s.LowPart == size.s.LowPart)
5224 return TRUE;
5226 if (newSize.s.LowPart < size.s.LowPart)
5228 SmallBlockChainStream_Shrink(This, newSize);
5230 else
5232 SmallBlockChainStream_Enlarge(This, newSize);
5235 return TRUE;
5238 /******************************************************************************
5239 * SmallBlockChainStream_GetSize
5241 * Returns the size of this chain.
5243 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5245 StgProperty chainProperty;
5247 StorageImpl_ReadProperty(
5248 This->parentStorage,
5249 This->ownerPropertyIndex,
5250 &chainProperty);
5252 return chainProperty.size;
5255 /******************************************************************************
5256 * StgCreateDocfile [OLE32.144]
5258 HRESULT WINAPI StgCreateDocfile(
5259 LPCOLESTR pwcsName,
5260 DWORD grfMode,
5261 DWORD reserved,
5262 IStorage **ppstgOpen)
5264 StorageImpl* newStorage = 0;
5265 HANDLE hFile = INVALID_HANDLE_VALUE;
5266 HRESULT hr = S_OK;
5267 DWORD shareMode;
5268 DWORD accessMode;
5269 DWORD creationMode;
5270 DWORD fileAttributes;
5271 WCHAR tempFileName[MAX_PATH];
5273 TRACE("(%s, %lx, %ld, %p)\n",
5274 debugstr_w(pwcsName), grfMode,
5275 reserved, ppstgOpen);
5278 * Validate the parameters
5280 if (ppstgOpen == 0)
5281 return STG_E_INVALIDPOINTER;
5284 * Validate the STGM flags
5286 if ( FAILED( validateSTGM(grfMode) ))
5287 return STG_E_INVALIDFLAG;
5290 * Generate a unique name.
5292 if (pwcsName == 0)
5294 WCHAR tempPath[MAX_PATH];
5295 WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5297 if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5298 return STG_E_INVALIDFLAG;
5299 if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5300 return STG_E_INVALIDFLAG;
5302 memset(tempPath, 0, sizeof(tempPath));
5303 memset(tempFileName, 0, sizeof(tempFileName));
5305 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5306 tempPath[0] = '.';
5308 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5309 pwcsName = tempFileName;
5310 else
5311 return STG_E_INSUFFICIENTMEMORY;
5313 creationMode = TRUNCATE_EXISTING;
5315 else
5317 creationMode = GetCreationModeFromSTGM(grfMode);
5321 * Interpret the STGM value grfMode
5323 shareMode = GetShareModeFromSTGM(grfMode);
5324 accessMode = GetAccessModeFromSTGM(grfMode);
5326 if (grfMode & STGM_DELETEONRELEASE)
5327 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5328 else
5329 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5331 if (grfMode & STGM_TRANSACTED)
5332 FIXME("Transacted mode not implemented.\n");
5335 * Initialize the "out" parameter.
5337 *ppstgOpen = 0;
5339 hFile = CreateFileW(pwcsName,
5340 accessMode,
5341 shareMode,
5342 NULL,
5343 creationMode,
5344 fileAttributes,
5347 if (hFile == INVALID_HANDLE_VALUE)
5349 return E_FAIL;
5353 * Allocate and initialize the new IStorage32object.
5355 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5357 if (newStorage == 0)
5358 return STG_E_INSUFFICIENTMEMORY;
5360 hr = StorageImpl_Construct(
5361 newStorage,
5362 hFile,
5363 NULL,
5364 grfMode,
5365 TRUE,
5366 TRUE);
5368 if (FAILED(hr))
5370 HeapFree(GetProcessHeap(), 0, newStorage);
5371 return hr;
5375 * Get an "out" pointer for the caller.
5377 hr = StorageBaseImpl_QueryInterface(
5378 (IStorage*)newStorage,
5379 (REFIID)&IID_IStorage,
5380 (void**)ppstgOpen);
5382 return hr;
5385 /******************************************************************************
5386 * StgOpenStorage [OLE32.148]
5388 HRESULT WINAPI StgOpenStorage(
5389 const OLECHAR *pwcsName,
5390 IStorage *pstgPriority,
5391 DWORD grfMode,
5392 SNB snbExclude,
5393 DWORD reserved,
5394 IStorage **ppstgOpen)
5396 StorageImpl* newStorage = 0;
5397 HRESULT hr = S_OK;
5398 HANDLE hFile = 0;
5399 DWORD shareMode;
5400 DWORD accessMode;
5402 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5403 debugstr_w(pwcsName), pstgPriority, grfMode,
5404 snbExclude, reserved, ppstgOpen);
5407 * Perform a sanity check
5409 if (( pwcsName == 0) || (ppstgOpen == 0) )
5410 return STG_E_INVALIDPOINTER;
5413 * Validate the STGM flags
5415 if ( FAILED( validateSTGM(grfMode) ))
5416 return STG_E_INVALIDFLAG;
5419 * Interpret the STGM value grfMode
5421 shareMode = GetShareModeFromSTGM(grfMode);
5422 accessMode = GetAccessModeFromSTGM(grfMode);
5425 * Initialize the "out" parameter.
5427 *ppstgOpen = 0;
5429 hFile = CreateFileW( pwcsName,
5430 accessMode,
5431 shareMode,
5432 NULL,
5433 OPEN_EXISTING,
5434 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5438 if (hFile==INVALID_HANDLE_VALUE)
5440 HRESULT hr = E_FAIL;
5441 DWORD last_error = GetLastError();
5443 switch (last_error)
5445 case ERROR_FILE_NOT_FOUND:
5446 hr = STG_E_FILENOTFOUND;
5447 break;
5449 case ERROR_PATH_NOT_FOUND:
5450 hr = STG_E_PATHNOTFOUND;
5451 break;
5453 case ERROR_ACCESS_DENIED:
5454 case ERROR_WRITE_PROTECT:
5455 hr = STG_E_ACCESSDENIED;
5456 break;
5458 case ERROR_SHARING_VIOLATION:
5459 hr = STG_E_SHAREVIOLATION;
5460 break;
5462 default:
5463 hr = E_FAIL;
5466 return hr;
5470 * Allocate and initialize the new IStorage32object.
5472 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5474 if (newStorage == 0)
5475 return STG_E_INSUFFICIENTMEMORY;
5477 hr = StorageImpl_Construct(
5478 newStorage,
5479 hFile,
5480 NULL,
5481 grfMode,
5482 TRUE,
5483 FALSE);
5485 if (FAILED(hr))
5487 HeapFree(GetProcessHeap(), 0, newStorage);
5489 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5491 if(hr == STG_E_INVALIDHEADER)
5492 return STG_E_FILEALREADYEXISTS;
5493 return hr;
5497 * Get an "out" pointer for the caller.
5499 hr = StorageBaseImpl_QueryInterface(
5500 (IStorage*)newStorage,
5501 (REFIID)&IID_IStorage,
5502 (void**)ppstgOpen);
5504 return hr;
5507 /******************************************************************************
5508 * StgCreateDocfileOnILockBytes [OLE32.145]
5510 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5511 ILockBytes *plkbyt,
5512 DWORD grfMode,
5513 DWORD reserved,
5514 IStorage** ppstgOpen)
5516 StorageImpl* newStorage = 0;
5517 HRESULT hr = S_OK;
5520 * Validate the parameters
5522 if ((ppstgOpen == 0) || (plkbyt == 0))
5523 return STG_E_INVALIDPOINTER;
5526 * Allocate and initialize the new IStorage object.
5528 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5530 if (newStorage == 0)
5531 return STG_E_INSUFFICIENTMEMORY;
5533 hr = StorageImpl_Construct(
5534 newStorage,
5536 plkbyt,
5537 grfMode,
5538 FALSE,
5539 TRUE);
5541 if (FAILED(hr))
5543 HeapFree(GetProcessHeap(), 0, newStorage);
5544 return hr;
5548 * Get an "out" pointer for the caller.
5550 hr = StorageBaseImpl_QueryInterface(
5551 (IStorage*)newStorage,
5552 (REFIID)&IID_IStorage,
5553 (void**)ppstgOpen);
5555 return hr;
5558 /******************************************************************************
5559 * StgOpenStorageOnILockBytes [OLE32.149]
5561 HRESULT WINAPI StgOpenStorageOnILockBytes(
5562 ILockBytes *plkbyt,
5563 IStorage *pstgPriority,
5564 DWORD grfMode,
5565 SNB snbExclude,
5566 DWORD reserved,
5567 IStorage **ppstgOpen)
5569 StorageImpl* newStorage = 0;
5570 HRESULT hr = S_OK;
5573 * Perform a sanity check
5575 if ((plkbyt == 0) || (ppstgOpen == 0))
5576 return STG_E_INVALIDPOINTER;
5579 * Validate the STGM flags
5581 if ( FAILED( validateSTGM(grfMode) ))
5582 return STG_E_INVALIDFLAG;
5585 * Initialize the "out" parameter.
5587 *ppstgOpen = 0;
5590 * Allocate and initialize the new IStorage object.
5592 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5594 if (newStorage == 0)
5595 return STG_E_INSUFFICIENTMEMORY;
5597 hr = StorageImpl_Construct(
5598 newStorage,
5600 plkbyt,
5601 grfMode,
5602 FALSE,
5603 FALSE);
5605 if (FAILED(hr))
5607 HeapFree(GetProcessHeap(), 0, newStorage);
5608 return hr;
5612 * Get an "out" pointer for the caller.
5614 hr = StorageBaseImpl_QueryInterface(
5615 (IStorage*)newStorage,
5616 (REFIID)&IID_IStorage,
5617 (void**)ppstgOpen);
5619 return hr;
5622 /******************************************************************************
5623 * StgSetTimes [ole32.150]
5627 HRESULT WINAPI StgSetTimes(WCHAR * str, FILETIME * a, FILETIME * b, FILETIME *c )
5630 FIXME("(%p, %p, %p, %p),stub!\n", str, a, b, c);
5631 return FALSE;
5634 /******************************************************************************
5635 * StgIsStorageILockBytes [OLE32.147]
5637 * Determines if the ILockBytes contains a storage object.
5639 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5641 BYTE sig[8];
5642 ULARGE_INTEGER offset;
5644 offset.s.HighPart = 0;
5645 offset.s.LowPart = 0;
5647 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5649 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5650 return S_OK;
5652 return S_FALSE;
5655 /******************************************************************************
5656 * WriteClassStg [OLE32.158]
5658 * This method will store the specified CLSID in the specified storage object
5660 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5662 HRESULT hRes;
5664 assert(pStg != 0);
5666 hRes = IStorage_SetClass(pStg, rclsid);
5668 return hRes;
5671 /***********************************************************************
5672 * ReadClassStg
5674 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5676 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5678 STATSTG pstatstg;
5679 HRESULT hRes;
5681 TRACE("()\n");
5683 if(pclsid==NULL)
5684 return E_POINTER;
5686 * read a STATSTG structure (contains the clsid) from the storage
5688 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5690 if(SUCCEEDED(hRes))
5691 *pclsid=pstatstg.clsid;
5693 return hRes;
5696 /***********************************************************************
5697 * OleLoadFromStream
5699 * This function loads an object from stream
5701 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5703 CLSID clsid;
5704 HRESULT res;
5705 LPPERSISTSTREAM xstm;
5707 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
5709 res=ReadClassStm(pStm,&clsid);
5710 if (!SUCCEEDED(res))
5711 return res;
5712 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5713 if (!SUCCEEDED(res))
5714 return res;
5715 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
5716 if (!SUCCEEDED(res)) {
5717 IUnknown_Release((IUnknown*)*ppvObj);
5718 return res;
5720 res=IPersistStream_Load(xstm,pStm);
5721 IPersistStream_Release(xstm);
5722 /* FIXME: all refcounts ok at this point? I think they should be:
5723 * pStm : unchanged
5724 * ppvObj : 1
5725 * xstm : 0 (released)
5727 return res;
5730 /***********************************************************************
5731 * OleSaveToStream
5733 * This function saves an object with the IPersistStream interface on it
5734 * to the specified stream.
5736 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5739 CLSID clsid;
5740 HRESULT res;
5742 TRACE("(%p,%p)\n",pPStm,pStm);
5744 res=IPersistStream_GetClassID(pPStm,&clsid);
5746 if (SUCCEEDED(res)){
5748 res=WriteClassStm(pStm,&clsid);
5750 if (SUCCEEDED(res))
5752 res=IPersistStream_Save(pPStm,pStm,TRUE);
5755 TRACE("Finished Save\n");
5756 return res;
5759 /****************************************************************************
5760 * This method validate a STGM parameter that can contain the values below
5762 * STGM_DIRECT 0x00000000
5763 * STGM_TRANSACTED 0x00010000
5764 * STGM_SIMPLE 0x08000000
5766 * STGM_READ 0x00000000
5767 * STGM_WRITE 0x00000001
5768 * STGM_READWRITE 0x00000002
5770 * STGM_SHARE_DENY_NONE 0x00000040
5771 * STGM_SHARE_DENY_READ 0x00000030
5772 * STGM_SHARE_DENY_WRITE 0x00000020
5773 * STGM_SHARE_EXCLUSIVE 0x00000010
5775 * STGM_PRIORITY 0x00040000
5776 * STGM_DELETEONRELEASE 0x04000000
5778 * STGM_CREATE 0x00001000
5779 * STGM_CONVERT 0x00020000
5780 * STGM_FAILIFTHERE 0x00000000
5782 * STGM_NOSCRATCH 0x00100000
5783 * STGM_NOSNAPSHOT 0x00200000
5785 static HRESULT validateSTGM(DWORD stgm)
5787 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5788 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5789 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5791 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5792 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5793 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5795 BOOL bSTGM_SHARE_DENY_NONE =
5796 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5798 BOOL bSTGM_SHARE_DENY_READ =
5799 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5801 BOOL bSTGM_SHARE_DENY_WRITE =
5802 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5804 BOOL bSTGM_SHARE_EXCLUSIVE =
5805 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5807 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5808 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5810 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5811 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5814 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5816 if ( ! bSTGM_DIRECT )
5817 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5818 return E_FAIL;
5821 * STGM_WRITE | STGM_READWRITE | STGM_READ
5823 if ( ! bSTGM_READ )
5824 if( bSTGM_WRITE && bSTGM_READWRITE )
5825 return E_FAIL;
5828 * STGM_SHARE_DENY_NONE | others
5829 * (I assume here that DENY_READ implies DENY_WRITE)
5831 if ( bSTGM_SHARE_DENY_NONE )
5832 if ( bSTGM_SHARE_DENY_READ ||
5833 bSTGM_SHARE_DENY_WRITE ||
5834 bSTGM_SHARE_EXCLUSIVE)
5835 return E_FAIL;
5838 * STGM_CREATE | STGM_CONVERT
5839 * if both are false, STGM_FAILIFTHERE is set to TRUE
5841 if ( bSTGM_CREATE && bSTGM_CONVERT )
5842 return E_FAIL;
5845 * STGM_NOSCRATCH requires STGM_TRANSACTED
5847 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
5848 return E_FAIL;
5851 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
5852 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5854 if (bSTGM_NOSNAPSHOT)
5856 if ( ! ( bSTGM_TRANSACTED &&
5857 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
5858 return E_FAIL;
5861 return S_OK;
5864 /****************************************************************************
5865 * GetShareModeFromSTGM
5867 * This method will return a share mode flag from a STGM value.
5868 * The STGM value is assumed valid.
5870 static DWORD GetShareModeFromSTGM(DWORD stgm)
5872 DWORD dwShareMode = 0;
5873 BOOL bSTGM_SHARE_DENY_NONE =
5874 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5876 BOOL bSTGM_SHARE_DENY_READ =
5877 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5879 BOOL bSTGM_SHARE_DENY_WRITE =
5880 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5882 BOOL bSTGM_SHARE_EXCLUSIVE =
5883 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5885 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
5886 dwShareMode = 0;
5888 if (bSTGM_SHARE_DENY_NONE)
5889 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5891 if (bSTGM_SHARE_DENY_WRITE)
5892 dwShareMode = FILE_SHARE_READ;
5894 return dwShareMode;
5897 /****************************************************************************
5898 * GetAccessModeFromSTGM
5900 * This method will return an access mode flag from a STGM value.
5901 * The STGM value is assumed valid.
5903 static DWORD GetAccessModeFromSTGM(DWORD stgm)
5905 DWORD dwDesiredAccess = GENERIC_READ;
5906 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5907 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5908 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5910 if (bSTGM_READ)
5911 dwDesiredAccess = GENERIC_READ;
5913 if (bSTGM_WRITE)
5914 dwDesiredAccess |= GENERIC_WRITE;
5916 if (bSTGM_READWRITE)
5917 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
5919 return dwDesiredAccess;
5922 /****************************************************************************
5923 * GetCreationModeFromSTGM
5925 * This method will return a creation mode flag from a STGM value.
5926 * The STGM value is assumed valid.
5928 static DWORD GetCreationModeFromSTGM(DWORD stgm)
5930 if ( stgm & STGM_CREATE)
5931 return CREATE_ALWAYS;
5932 if (stgm & STGM_CONVERT) {
5933 FIXME("STGM_CONVERT not implemented!\n");
5934 return CREATE_NEW;
5936 /* All other cases */
5937 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
5938 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
5939 return CREATE_NEW;
5943 /*************************************************************************
5944 * OLECONVERT_LoadOLE10 [Internal]
5946 * Loads the OLE10 STREAM to memory
5948 * PARAMS
5949 * pOleStream [I] The OLESTREAM
5950 * pData [I] Data Structure for the OLESTREAM Data
5952 * RETURNS
5953 * Success: S_OK
5954 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
5955 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
5957 * NOTES
5958 * This function is used by OleConvertOLESTREAMToIStorage only.
5960 * Memory allocated for pData must be freed by the caller
5962 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
5964 DWORD dwSize;
5965 HRESULT hRes = S_OK;
5966 int nTryCnt=0;
5967 int max_try = 6;
5969 pData->pData = NULL;
5970 pData->pstrOleObjFileName = (CHAR *) NULL;
5972 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
5974 /* Get the OleID */
5975 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
5976 if(dwSize != sizeof(pData->dwOleID))
5978 hRes = CONVERT10_E_OLESTREAM_GET;
5980 else if(pData->dwOleID != OLESTREAM_ID)
5982 hRes = CONVERT10_E_OLESTREAM_FMT;
5984 else
5986 hRes = S_OK;
5987 break;
5991 if(hRes == S_OK)
5993 /* Get the TypeID...more info needed for this field */
5994 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
5995 if(dwSize != sizeof(pData->dwTypeID))
5997 hRes = CONVERT10_E_OLESTREAM_GET;
6000 if(hRes == S_OK)
6002 if(pData->dwTypeID != 0)
6004 /* Get the lenght of the OleTypeName */
6005 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6006 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6008 hRes = CONVERT10_E_OLESTREAM_GET;
6011 if(hRes == S_OK)
6013 if(pData->dwOleTypeNameLength > 0)
6015 /* Get the OleTypeName */
6016 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6017 if(dwSize != pData->dwOleTypeNameLength)
6019 hRes = CONVERT10_E_OLESTREAM_GET;
6023 if(bStrem1)
6025 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6026 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6028 hRes = CONVERT10_E_OLESTREAM_GET;
6030 if(hRes == S_OK)
6032 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6033 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6034 pData->pstrOleObjFileName = (CHAR *)malloc(pData->dwOleObjFileNameLength);
6035 if(pData->pstrOleObjFileName)
6037 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6038 if(dwSize != pData->dwOleObjFileNameLength)
6040 hRes = CONVERT10_E_OLESTREAM_GET;
6043 else
6044 hRes = CONVERT10_E_OLESTREAM_GET;
6047 else
6049 /* Get the Width of the Metafile */
6050 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6051 if(dwSize != sizeof(pData->dwMetaFileWidth))
6053 hRes = CONVERT10_E_OLESTREAM_GET;
6055 if(hRes == S_OK)
6057 /* Get the Height of the Metafile */
6058 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6059 if(dwSize != sizeof(pData->dwMetaFileHeight))
6061 hRes = CONVERT10_E_OLESTREAM_GET;
6065 if(hRes == S_OK)
6067 /* Get the Lenght of the Data */
6068 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6069 if(dwSize != sizeof(pData->dwDataLength))
6071 hRes = CONVERT10_E_OLESTREAM_GET;
6075 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6077 if(!bStrem1) /* if it is a second OLE stream data */
6079 pData->dwDataLength -= 8;
6080 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6081 if(dwSize != sizeof(pData->strUnknown))
6083 hRes = CONVERT10_E_OLESTREAM_GET;
6087 if(hRes == S_OK)
6089 if(pData->dwDataLength > 0)
6091 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6093 /* Get Data (ex. IStorage, Metafile, or BMP) */
6094 if(pData->pData)
6096 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6097 if(dwSize != pData->dwDataLength)
6099 hRes = CONVERT10_E_OLESTREAM_GET;
6102 else
6104 hRes = CONVERT10_E_OLESTREAM_GET;
6110 return hRes;
6113 /*************************************************************************
6114 * OLECONVERT_SaveOLE10 [Internal]
6116 * Saves the OLE10 STREAM From memory
6118 * PARAMS
6119 * pData [I] Data Structure for the OLESTREAM Data
6120 * pOleStream [I] The OLESTREAM to save
6122 * RETURNS
6123 * Success: S_OK
6124 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6126 * NOTES
6127 * This function is used by OleConvertIStorageToOLESTREAM only.
6130 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6132 DWORD dwSize;
6133 HRESULT hRes = S_OK;
6136 /* Set the OleID */
6137 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6138 if(dwSize != sizeof(pData->dwOleID))
6140 hRes = CONVERT10_E_OLESTREAM_PUT;
6143 if(hRes == S_OK)
6145 /* Set the TypeID */
6146 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6147 if(dwSize != sizeof(pData->dwTypeID))
6149 hRes = CONVERT10_E_OLESTREAM_PUT;
6153 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6155 /* Set the Lenght of the OleTypeName */
6156 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6157 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6159 hRes = CONVERT10_E_OLESTREAM_PUT;
6162 if(hRes == S_OK)
6164 if(pData->dwOleTypeNameLength > 0)
6166 /* Set the OleTypeName */
6167 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6168 if(dwSize != pData->dwOleTypeNameLength)
6170 hRes = CONVERT10_E_OLESTREAM_PUT;
6175 if(hRes == S_OK)
6177 /* Set the width of the Metafile */
6178 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6179 if(dwSize != sizeof(pData->dwMetaFileWidth))
6181 hRes = CONVERT10_E_OLESTREAM_PUT;
6185 if(hRes == S_OK)
6187 /* Set the height of the Metafile */
6188 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6189 if(dwSize != sizeof(pData->dwMetaFileHeight))
6191 hRes = CONVERT10_E_OLESTREAM_PUT;
6195 if(hRes == S_OK)
6197 /* Set the lenght of the Data */
6198 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6199 if(dwSize != sizeof(pData->dwDataLength))
6201 hRes = CONVERT10_E_OLESTREAM_PUT;
6205 if(hRes == S_OK)
6207 if(pData->dwDataLength > 0)
6209 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6210 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6211 if(dwSize != pData->dwDataLength)
6213 hRes = CONVERT10_E_OLESTREAM_PUT;
6218 return hRes;
6221 /*************************************************************************
6222 * OLECONVERT_GetOLE20FromOLE10[Internal]
6224 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6225 * opens it, and copies the content to the dest IStorage for
6226 * OleConvertOLESTREAMToIStorage
6229 * PARAMS
6230 * pDestStorage [I] The IStorage to copy the data to
6231 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6232 * nBufferLength [I] The size of the buffer
6234 * RETURNS
6235 * Nothing
6237 * NOTES
6241 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6243 HRESULT hRes;
6244 HANDLE hFile;
6245 IStorage *pTempStorage;
6246 DWORD dwNumOfBytesWritten;
6247 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6248 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6250 /* Create a temp File */
6251 GetTempPathW(MAX_PATH, wstrTempDir);
6252 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6253 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6255 if(hFile != INVALID_HANDLE_VALUE)
6257 /* Write IStorage Data to File */
6258 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6259 CloseHandle(hFile);
6261 /* Open and copy temp storage to the Dest Storage */
6262 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6263 if(hRes == S_OK)
6265 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6266 StorageBaseImpl_Release(pTempStorage);
6268 DeleteFileW(wstrTempFile);
6273 /*************************************************************************
6274 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6276 * Saves the OLE10 STREAM From memory
6278 * PARAMS
6279 * pStorage [I] The Src IStorage to copy
6280 * pData [I] The Dest Memory to write to.
6282 * RETURNS
6283 * The size in bytes allocated for pData
6285 * NOTES
6286 * Memory allocated for pData must be freed by the caller
6288 * Used by OleConvertIStorageToOLESTREAM only.
6291 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6293 HANDLE hFile;
6294 HRESULT hRes;
6295 DWORD nDataLength = 0;
6296 IStorage *pTempStorage;
6297 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6298 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6300 *pData = NULL;
6302 /* Create temp Storage */
6303 GetTempPathW(MAX_PATH, wstrTempDir);
6304 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6305 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6307 if(hRes == S_OK)
6309 /* Copy Src Storage to the Temp Storage */
6310 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6311 StorageBaseImpl_Release(pTempStorage);
6313 /* Open Temp Storage as a file and copy to memory */
6314 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6315 if(hFile != INVALID_HANDLE_VALUE)
6317 nDataLength = GetFileSize(hFile, NULL);
6318 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6319 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6320 CloseHandle(hFile);
6322 DeleteFileW(wstrTempFile);
6324 return nDataLength;
6327 /*************************************************************************
6328 * OLECONVERT_CreateOleStream [Internal]
6330 * Creates the "\001OLE" stream in the IStorage if neccessary.
6332 * PARAMS
6333 * pStorage [I] Dest storage to create the stream in
6335 * RETURNS
6336 * Nothing
6338 * NOTES
6339 * This function is used by OleConvertOLESTREAMToIStorage only.
6341 * This stream is still unknown, MS Word seems to have extra data
6342 * but since the data is stored in the OLESTREAM there should be
6343 * no need to recreate the stream. If the stream is manually
6344 * deleted it will create it with this default data.
6347 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6349 HRESULT hRes;
6350 IStream *pStream;
6351 WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6352 BYTE pOleStreamHeader [] =
6354 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6355 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6356 0x00, 0x00, 0x00, 0x00
6359 /* Create stream if not present */
6360 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6361 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6363 if(hRes == S_OK)
6365 /* Write default Data */
6366 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6367 IStream_Release(pStream);
6372 /*************************************************************************
6373 * OLECONVERT_CreateCompObjStream [Internal]
6375 * Creates a "\001CompObj" is the destination IStorage if necessary.
6377 * PARAMS
6378 * pStorage [I] The dest IStorage to create the CompObj Stream
6379 * if necessary.
6380 * strOleTypeName [I] The ProgID
6382 * RETURNS
6383 * Success: S_OK
6384 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6386 * NOTES
6387 * This function is used by OleConvertOLESTREAMToIStorage only.
6389 * The stream data is stored in the OLESTREAM and there should be
6390 * no need to recreate the stream. If the stream is manually
6391 * deleted it will attempt to create it by querying the registry.
6395 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6397 IStream *pStream;
6398 HRESULT hStorageRes, hRes = S_OK;
6399 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6400 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6402 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6403 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6405 /* Initialize the CompObj structure */
6406 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6407 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6408 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6411 /* Create a CompObj stream if it doesn't exist */
6412 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6413 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6414 if(hStorageRes == S_OK)
6416 /* copy the OleTypeName to the compobj struct */
6417 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6418 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6420 /* copy the OleTypeName to the compobj struct */
6421 /* Note: in the test made, these were Identical */
6422 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6423 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6425 /* Get the CLSID */
6426 hRes = CLSIDFromProgID16(IStorageCompObj.strProgIDName, &(IStorageCompObj.clsid));
6428 if(hRes == S_OK)
6430 HKEY hKey;
6431 LONG hErr;
6432 /* Get the CLSID Default Name from the Registry */
6433 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6434 if(hErr == ERROR_SUCCESS)
6436 char strTemp[OLESTREAM_MAX_STR_LEN];
6437 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6438 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6439 if(hErr == ERROR_SUCCESS)
6441 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6443 RegCloseKey(hKey);
6447 /* Write CompObj Structure to stream */
6448 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6450 WriteClassStm(pStream,&(IStorageCompObj.clsid));
6452 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6453 if(IStorageCompObj.dwCLSIDNameLength > 0)
6455 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6457 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6458 if(IStorageCompObj.dwOleTypeNameLength > 0)
6460 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6462 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6463 if(IStorageCompObj.dwProgIDNameLength > 0)
6465 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6467 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6468 IStream_Release(pStream);
6470 return hRes;
6474 /*************************************************************************
6475 * OLECONVERT_CreateOlePresStream[Internal]
6477 * Creates the "\002OlePres000" Stream with the Metafile data
6479 * PARAMS
6480 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6481 * dwExtentX [I] Width of the Metafile
6482 * dwExtentY [I] Height of the Metafile
6483 * pData [I] Metafile data
6484 * dwDataLength [I] Size of the Metafile data
6486 * RETURNS
6487 * Success: S_OK
6488 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6490 * NOTES
6491 * This function is used by OleConvertOLESTREAMToIStorage only.
6494 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6496 HRESULT hRes;
6497 IStream *pStream;
6498 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6499 BYTE pOlePresStreamHeader [] =
6501 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6502 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6503 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6504 0x00, 0x00, 0x00, 0x00
6507 BYTE pOlePresStreamHeaderEmpty [] =
6509 0x00, 0x00, 0x00, 0x00,
6510 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6511 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6512 0x00, 0x00, 0x00, 0x00
6515 /* Create the OlePres000 Stream */
6516 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6517 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6519 if(hRes == S_OK)
6521 DWORD nHeaderSize;
6522 OLECONVERT_ISTORAGE_OLEPRES OlePres;
6524 memset(&OlePres, 0, sizeof(OlePres));
6525 /* Do we have any metafile data to save */
6526 if(dwDataLength > 0)
6528 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6529 nHeaderSize = sizeof(pOlePresStreamHeader);
6531 else
6533 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
6534 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
6536 /* Set width and height of the metafile */
6537 OlePres.dwExtentX = dwExtentX;
6538 OlePres.dwExtentY = -dwExtentY;
6540 /* Set Data and Lenght */
6541 if(dwDataLength > sizeof(METAFILEPICT16))
6543 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
6544 OlePres.pData = &(pData[8]);
6546 /* Save OlePres000 Data to Stream */
6547 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
6548 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
6549 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
6550 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
6551 if(OlePres.dwSize > 0)
6553 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
6555 IStream_Release(pStream);
6559 /*************************************************************************
6560 * OLECONVERT_CreateOle10NativeStream [Internal]
6562 * Creates the "\001Ole10Native" Stream (should contain a BMP)
6564 * PARAMS
6565 * pStorage [I] Dest storage to create the stream in
6566 * pData [I] Ole10 Native Data (ex. bmp)
6567 * dwDataLength [I] Size of the Ole10 Native Data
6569 * RETURNS
6570 * Nothing
6572 * NOTES
6573 * This function is used by OleConvertOLESTREAMToIStorage only.
6575 * Might need to verify the data and return appropriate error message
6578 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
6580 HRESULT hRes;
6581 IStream *pStream;
6582 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6584 /* Create the Ole10Native Stream */
6585 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6586 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6588 if(hRes == S_OK)
6590 /* Write info to stream */
6591 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
6592 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
6593 IStream_Release(pStream);
6598 /*************************************************************************
6599 * OLECONVERT_GetOLE10ProgID [Internal]
6601 * Finds the ProgID (or OleTypeID) from the IStorage
6603 * PARAMS
6604 * pStorage [I] The Src IStorage to get the ProgID
6605 * strProgID [I] the ProgID string to get
6606 * dwSize [I] the size of the string
6608 * RETURNS
6609 * Success: S_OK
6610 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6612 * NOTES
6613 * This function is used by OleConvertIStorageToOLESTREAM only.
6617 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
6619 HRESULT hRes;
6620 IStream *pStream;
6621 LARGE_INTEGER iSeekPos;
6622 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
6623 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6625 /* Open the CompObj Stream */
6626 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6627 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6628 if(hRes == S_OK)
6631 /*Get the OleType from the CompObj Stream */
6632 iSeekPos.s.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
6633 iSeekPos.s.HighPart = 0;
6635 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6636 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
6637 iSeekPos.s.LowPart = CompObj.dwCLSIDNameLength;
6638 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6639 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
6640 iSeekPos.s.LowPart = CompObj.dwOleTypeNameLength;
6641 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6643 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
6644 if(*dwSize > 0)
6646 IStream_Read(pStream, strProgID, *dwSize, NULL);
6648 IStream_Release(pStream);
6650 else
6652 STATSTG stat;
6653 LPOLESTR wstrProgID;
6655 /* Get the OleType from the registry */
6656 REFCLSID clsid = &(stat.clsid);
6657 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
6658 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
6659 if(hRes == S_OK)
6661 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
6665 return hRes;
6668 /*************************************************************************
6669 * OLECONVERT_GetOle10PresData [Internal]
6671 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
6673 * PARAMS
6674 * pStorage [I] Src IStroage
6675 * pOleStream [I] Dest OleStream Mem Struct
6677 * RETURNS
6678 * Nothing
6680 * NOTES
6681 * This function is used by OleConvertIStorageToOLESTREAM only.
6683 * Memory allocated for pData must be freed by the caller
6687 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6690 HRESULT hRes;
6691 IStream *pStream;
6692 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6694 /* Initialize Default data for OLESTREAM */
6695 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6696 pOleStreamData[0].dwTypeID = 2;
6697 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6698 pOleStreamData[1].dwTypeID = 0;
6699 pOleStreamData[0].dwMetaFileWidth = 0;
6700 pOleStreamData[0].dwMetaFileHeight = 0;
6701 pOleStreamData[0].pData = NULL;
6702 pOleStreamData[1].pData = NULL;
6704 /* Open Ole10Native Stream */
6705 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6706 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6707 if(hRes == S_OK)
6710 /* Read Size and Data */
6711 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
6712 if(pOleStreamData->dwDataLength > 0)
6714 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
6715 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
6717 IStream_Release(pStream);
6723 /*************************************************************************
6724 * OLECONVERT_GetOle20PresData[Internal]
6726 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
6728 * PARAMS
6729 * pStorage [I] Src IStroage
6730 * pOleStreamData [I] Dest OleStream Mem Struct
6732 * RETURNS
6733 * Nothing
6735 * NOTES
6736 * This function is used by OleConvertIStorageToOLESTREAM only.
6738 * Memory allocated for pData must be freed by the caller
6740 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6742 HRESULT hRes;
6743 IStream *pStream;
6744 OLECONVERT_ISTORAGE_OLEPRES olePress;
6745 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6747 /* Initialize Default data for OLESTREAM */
6748 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6749 pOleStreamData[0].dwTypeID = 2;
6750 pOleStreamData[0].dwMetaFileWidth = 0;
6751 pOleStreamData[0].dwMetaFileHeight = 0;
6752 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
6753 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6754 pOleStreamData[1].dwTypeID = 0;
6755 pOleStreamData[1].dwOleTypeNameLength = 0;
6756 pOleStreamData[1].strOleTypeName[0] = 0;
6757 pOleStreamData[1].dwMetaFileWidth = 0;
6758 pOleStreamData[1].dwMetaFileHeight = 0;
6759 pOleStreamData[1].pData = NULL;
6760 pOleStreamData[1].dwDataLength = 0;
6763 /* Open OlePress000 stream */
6764 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6765 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6766 if(hRes == S_OK)
6768 LARGE_INTEGER iSeekPos;
6769 METAFILEPICT16 MetaFilePict;
6770 char strMetafilePictName[] = "METAFILEPICT";
6772 /* Set the TypeID for a Metafile */
6773 pOleStreamData[1].dwTypeID = 5;
6775 /* Set the OleTypeName to Metafile */
6776 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
6777 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
6779 iSeekPos.s.HighPart = 0;
6780 iSeekPos.s.LowPart = sizeof(olePress.byUnknown1);
6782 /* Get Presentation Data */
6783 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6784 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
6785 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
6786 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
6788 /*Set width and Height */
6789 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
6790 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
6791 if(olePress.dwSize > 0)
6793 /* Set Length */
6794 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
6796 /* Set MetaFilePict struct */
6797 MetaFilePict.mm = 8;
6798 MetaFilePict.xExt = olePress.dwExtentX;
6799 MetaFilePict.yExt = olePress.dwExtentY;
6800 MetaFilePict.hMF = 0;
6802 /* Get Metafile Data */
6803 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
6804 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
6805 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
6807 IStream_Release(pStream);
6811 /*************************************************************************
6812 * OleConvertOLESTREAMToIStorage [OLE32.87]
6814 * Read info on MSDN
6816 * TODO
6817 * DVTARGETDEVICE paramenter is not handled
6818 * Still unsure of some mem fields for OLE 10 Stream
6819 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6820 * and "\001OLE" streams
6823 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
6824 LPOLESTREAM pOleStream,
6825 LPSTORAGE pstg,
6826 const DVTARGETDEVICE* ptd)
6828 int i;
6829 HRESULT hRes=S_OK;
6830 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
6832 memset(pOleStreamData, 0, sizeof(pOleStreamData));
6834 if(ptd != NULL)
6836 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
6839 if(pstg == NULL || pOleStream == NULL)
6841 hRes = E_INVALIDARG;
6844 if(hRes == S_OK)
6846 /* Load the OLESTREAM to Memory */
6847 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
6850 if(hRes == S_OK)
6852 /* Load the OLESTREAM to Memory (part 2)*/
6853 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
6856 if(hRes == S_OK)
6859 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
6861 /* Do we have the IStorage Data in the OLESTREAM */
6862 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
6864 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6865 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
6867 else
6869 /* It must be an original OLE 1.0 source */
6870 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6873 else
6875 /* It must be an original OLE 1.0 source */
6876 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6879 /* Create CompObj Stream if necessary */
6880 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
6881 if(hRes == S_OK)
6883 /*Create the Ole Stream if necessary */
6884 OLECONVERT_CreateOleStream(pstg);
6889 /* Free allocated memory */
6890 for(i=0; i < 2; i++)
6892 if(pOleStreamData[i].pData != NULL)
6894 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
6896 if(pOleStreamData[i].pstrOleObjFileName != NULL)
6898 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
6899 pOleStreamData[i].pstrOleObjFileName = NULL;
6902 return hRes;
6905 /*************************************************************************
6906 * OleConvertIStorageToOLESTREAM [OLE32.85]
6908 * Read info on MSDN
6910 * Read info on MSDN
6912 * TODO
6913 * Still unsure of some mem fields for OLE 10 Stream
6914 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6915 * and "\001OLE" streams.
6918 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
6919 LPSTORAGE pstg,
6920 LPOLESTREAM pOleStream)
6922 int i;
6923 HRESULT hRes = S_OK;
6924 IStream *pStream;
6925 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
6926 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6929 memset(pOleStreamData, 0, sizeof(pOleStreamData));
6931 if(pstg == NULL || pOleStream == NULL)
6933 hRes = E_INVALIDARG;
6935 if(hRes == S_OK)
6937 /* Get the ProgID */
6938 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
6939 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
6941 if(hRes == S_OK)
6943 /*Was it originaly Ole10 */
6944 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
6945 if(hRes == S_OK)
6947 IStream_Release(pStream);
6948 /*Get Presentation Data for Ole10Native */
6949 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
6951 else
6953 /*Get Presentation Data (OLE20)*/
6954 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
6957 /* Save OLESTREAM */
6958 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
6959 if(hRes == S_OK)
6961 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
6966 /* Free allocated memory */
6967 for(i=0; i < 2; i++)
6969 if(pOleStreamData[i].pData != NULL)
6971 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
6975 return hRes;
6978 HRESULT WINAPI GetConvertStg(LPGUID guid) {
6979 FIXME("(%s), unimplemented stub!\n",debugstr_guid(guid));
6980 return E_FAIL;