Clean out the hack on BASS/TREBLE and suppress them in the mask as was
[wine/multimedia.git] / dlls / ole32 / storage32.c
blob52828da7346da518ef0d7c397dc1787f32e535b4
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 lstrcpyAtoW(rootProp.name, rootPropertyName);
2233 rootProp.sizeOfNameString = (lstrlenW(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 memset(tempPath, 0, sizeof(tempPath));
5298 memset(tempFileName, 0, sizeof(tempFileName));
5300 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5301 tempPath[0] = '.';
5303 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5304 pwcsName = tempFileName;
5305 else
5306 return STG_E_INSUFFICIENTMEMORY;
5310 * Interpret the STGM value grfMode
5312 shareMode = GetShareModeFromSTGM(grfMode);
5313 accessMode = GetAccessModeFromSTGM(grfMode);
5314 creationMode = GetCreationModeFromSTGM(grfMode);
5316 if (grfMode & STGM_DELETEONRELEASE)
5317 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5318 else
5319 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5321 if (grfMode & STGM_TRANSACTED)
5322 FIXME("Transacted mode not implemented.\n");
5325 * Initialize the "out" parameter.
5327 *ppstgOpen = 0;
5329 hFile = CreateFileW(pwcsName,
5330 accessMode,
5331 shareMode,
5332 NULL,
5333 creationMode,
5334 fileAttributes,
5337 if (hFile == INVALID_HANDLE_VALUE)
5339 return E_FAIL;
5343 * Allocate and initialize the new IStorage32object.
5345 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5347 if (newStorage == 0)
5348 return STG_E_INSUFFICIENTMEMORY;
5350 hr = StorageImpl_Construct(
5351 newStorage,
5352 hFile,
5353 NULL,
5354 grfMode,
5355 TRUE,
5356 TRUE);
5358 if (FAILED(hr))
5360 HeapFree(GetProcessHeap(), 0, newStorage);
5361 return hr;
5365 * Get an "out" pointer for the caller.
5367 hr = StorageBaseImpl_QueryInterface(
5368 (IStorage*)newStorage,
5369 (REFIID)&IID_IStorage,
5370 (void**)ppstgOpen);
5372 return hr;
5375 /******************************************************************************
5376 * StgOpenStorage [OLE32.148]
5378 HRESULT WINAPI StgOpenStorage(
5379 const OLECHAR *pwcsName,
5380 IStorage *pstgPriority,
5381 DWORD grfMode,
5382 SNB snbExclude,
5383 DWORD reserved,
5384 IStorage **ppstgOpen)
5386 StorageImpl* newStorage = 0;
5387 HRESULT hr = S_OK;
5388 HANDLE hFile = 0;
5389 DWORD shareMode;
5390 DWORD accessMode;
5392 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5393 debugstr_w(pwcsName), pstgPriority, grfMode,
5394 snbExclude, reserved, ppstgOpen);
5397 * Perform a sanity check
5399 if (( pwcsName == 0) || (ppstgOpen == 0) )
5400 return STG_E_INVALIDPOINTER;
5403 * Validate the STGM flags
5405 if ( FAILED( validateSTGM(grfMode) ))
5406 return STG_E_INVALIDFLAG;
5409 * Interpret the STGM value grfMode
5411 shareMode = GetShareModeFromSTGM(grfMode);
5412 accessMode = GetAccessModeFromSTGM(grfMode);
5415 * Initialize the "out" parameter.
5417 *ppstgOpen = 0;
5419 hFile = CreateFileW( pwcsName,
5420 accessMode,
5421 shareMode,
5422 NULL,
5423 OPEN_EXISTING,
5424 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5428 if (hFile==INVALID_HANDLE_VALUE)
5430 HRESULT hr = E_FAIL;
5431 DWORD last_error = GetLastError();
5433 switch (last_error)
5435 case ERROR_FILE_NOT_FOUND:
5436 hr = STG_E_FILENOTFOUND;
5437 break;
5439 case ERROR_PATH_NOT_FOUND:
5440 hr = STG_E_PATHNOTFOUND;
5441 break;
5443 case ERROR_ACCESS_DENIED:
5444 case ERROR_WRITE_PROTECT:
5445 hr = STG_E_ACCESSDENIED;
5446 break;
5448 case ERROR_SHARING_VIOLATION:
5449 hr = STG_E_SHAREVIOLATION;
5450 break;
5452 default:
5453 hr = E_FAIL;
5456 return hr;
5460 * Allocate and initialize the new IStorage32object.
5462 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5464 if (newStorage == 0)
5465 return STG_E_INSUFFICIENTMEMORY;
5467 hr = StorageImpl_Construct(
5468 newStorage,
5469 hFile,
5470 NULL,
5471 grfMode,
5472 TRUE,
5473 FALSE);
5475 if (FAILED(hr))
5477 HeapFree(GetProcessHeap(), 0, newStorage);
5478 return hr;
5482 * Get an "out" pointer for the caller.
5484 hr = StorageBaseImpl_QueryInterface(
5485 (IStorage*)newStorage,
5486 (REFIID)&IID_IStorage,
5487 (void**)ppstgOpen);
5489 return hr;
5492 /******************************************************************************
5493 * StgCreateDocfileOnILockBytes [OLE32.145]
5495 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5496 ILockBytes *plkbyt,
5497 DWORD grfMode,
5498 DWORD reserved,
5499 IStorage** ppstgOpen)
5501 StorageImpl* newStorage = 0;
5502 HRESULT hr = S_OK;
5505 * Validate the parameters
5507 if ((ppstgOpen == 0) || (plkbyt == 0))
5508 return STG_E_INVALIDPOINTER;
5511 * Allocate and initialize the new IStorage object.
5513 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5515 if (newStorage == 0)
5516 return STG_E_INSUFFICIENTMEMORY;
5518 hr = StorageImpl_Construct(
5519 newStorage,
5521 plkbyt,
5522 grfMode,
5523 FALSE,
5524 TRUE);
5526 if (FAILED(hr))
5528 HeapFree(GetProcessHeap(), 0, newStorage);
5529 return hr;
5533 * Get an "out" pointer for the caller.
5535 hr = StorageBaseImpl_QueryInterface(
5536 (IStorage*)newStorage,
5537 (REFIID)&IID_IStorage,
5538 (void**)ppstgOpen);
5540 return hr;
5543 /******************************************************************************
5544 * StgOpenStorageOnILockBytes [OLE32.149]
5546 HRESULT WINAPI StgOpenStorageOnILockBytes(
5547 ILockBytes *plkbyt,
5548 IStorage *pstgPriority,
5549 DWORD grfMode,
5550 SNB snbExclude,
5551 DWORD reserved,
5552 IStorage **ppstgOpen)
5554 StorageImpl* newStorage = 0;
5555 HRESULT hr = S_OK;
5558 * Perform a sanity check
5560 if ((plkbyt == 0) || (ppstgOpen == 0))
5561 return STG_E_INVALIDPOINTER;
5564 * Validate the STGM flags
5566 if ( FAILED( validateSTGM(grfMode) ))
5567 return STG_E_INVALIDFLAG;
5570 * Initialize the "out" parameter.
5572 *ppstgOpen = 0;
5575 * Allocate and initialize the new IStorage object.
5577 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5579 if (newStorage == 0)
5580 return STG_E_INSUFFICIENTMEMORY;
5582 hr = StorageImpl_Construct(
5583 newStorage,
5585 plkbyt,
5586 grfMode,
5587 FALSE,
5588 FALSE);
5590 if (FAILED(hr))
5592 HeapFree(GetProcessHeap(), 0, newStorage);
5593 return hr;
5597 * Get an "out" pointer for the caller.
5599 hr = StorageBaseImpl_QueryInterface(
5600 (IStorage*)newStorage,
5601 (REFIID)&IID_IStorage,
5602 (void**)ppstgOpen);
5604 return hr;
5607 /******************************************************************************
5608 * StgSetTimes [ole32.150]
5612 HRESULT WINAPI StgSetTimes(WCHAR * str, FILETIME * a, FILETIME * b, FILETIME *c )
5615 FIXME("(%p, %p, %p, %p),stub!\n", str, a, b, c);
5616 return FALSE;
5619 /******************************************************************************
5620 * StgIsStorageILockBytes [OLE32.147]
5622 * Determines if the ILockBytes contains a storage object.
5624 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5626 BYTE sig[8];
5627 ULARGE_INTEGER offset;
5629 offset.s.HighPart = 0;
5630 offset.s.LowPart = 0;
5632 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5634 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5635 return S_OK;
5637 return S_FALSE;
5640 /******************************************************************************
5641 * WriteClassStg [OLE32.158]
5643 * This method will store the specified CLSID in the specified storage object
5645 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5647 HRESULT hRes;
5649 assert(pStg != 0);
5651 hRes = IStorage_SetClass(pStg, rclsid);
5653 return hRes;
5656 /***********************************************************************
5657 * ReadClassStg
5659 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5661 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5663 STATSTG pstatstg;
5664 HRESULT hRes;
5666 TRACE("()\n");
5668 if(pclsid==NULL)
5669 return E_POINTER;
5671 * read a STATSTG structure (contains the clsid) from the storage
5673 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5675 if(SUCCEEDED(hRes))
5676 *pclsid=pstatstg.clsid;
5678 return hRes;
5681 /***********************************************************************
5682 * OleLoadFromStream
5684 * This function loads an object from stream
5686 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5688 CLSID clsid;
5689 HRESULT res;
5691 FIXME("(),stub!\n");
5693 res=ReadClassStm(pStm,&clsid);
5695 if (SUCCEEDED(res)){
5697 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5699 if (SUCCEEDED(res))
5701 res=IPersistStream_Load((IPersistStream*)ppvObj,pStm);
5704 return res;
5707 /***********************************************************************
5708 * OleSaveToStream
5710 * This function saves an object with the IPersistStream interface on it
5711 * to the specified stream.
5713 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5716 CLSID clsid;
5717 HRESULT res;
5719 TRACE("(%p,%p)\n",pPStm,pStm);
5721 res=IPersistStream_GetClassID(pPStm,&clsid);
5723 if (SUCCEEDED(res)){
5725 res=WriteClassStm(pStm,&clsid);
5727 if (SUCCEEDED(res))
5729 res=IPersistStream_Save(pPStm,pStm,TRUE);
5732 TRACE("Finished Save\n");
5733 return res;
5736 /****************************************************************************
5737 * This method validate a STGM parameter that can contain the values below
5739 * STGM_DIRECT 0x00000000
5740 * STGM_TRANSACTED 0x00010000
5741 * STGM_SIMPLE 0x08000000
5743 * STGM_READ 0x00000000
5744 * STGM_WRITE 0x00000001
5745 * STGM_READWRITE 0x00000002
5747 * STGM_SHARE_DENY_NONE 0x00000040
5748 * STGM_SHARE_DENY_READ 0x00000030
5749 * STGM_SHARE_DENY_WRITE 0x00000020
5750 * STGM_SHARE_EXCLUSIVE 0x00000010
5752 * STGM_PRIORITY 0x00040000
5753 * STGM_DELETEONRELEASE 0x04000000
5755 * STGM_CREATE 0x00001000
5756 * STGM_CONVERT 0x00020000
5757 * STGM_FAILIFTHERE 0x00000000
5759 * STGM_NOSCRATCH 0x00100000
5760 * STGM_NOSNAPSHOT 0x00200000
5762 static HRESULT validateSTGM(DWORD stgm)
5764 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5765 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5766 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5768 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5769 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5770 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5772 BOOL bSTGM_SHARE_DENY_NONE =
5773 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5775 BOOL bSTGM_SHARE_DENY_READ =
5776 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5778 BOOL bSTGM_SHARE_DENY_WRITE =
5779 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5781 BOOL bSTGM_SHARE_EXCLUSIVE =
5782 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5784 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5785 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5787 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5788 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5791 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5793 if ( ! bSTGM_DIRECT )
5794 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5795 return E_FAIL;
5798 * STGM_WRITE | STGM_READWRITE | STGM_READ
5800 if ( ! bSTGM_READ )
5801 if( bSTGM_WRITE && bSTGM_READWRITE )
5802 return E_FAIL;
5805 * STGM_SHARE_DENY_NONE | others
5806 * (I assume here that DENY_READ implies DENY_WRITE)
5808 if ( bSTGM_SHARE_DENY_NONE )
5809 if ( bSTGM_SHARE_DENY_READ ||
5810 bSTGM_SHARE_DENY_WRITE ||
5811 bSTGM_SHARE_EXCLUSIVE)
5812 return E_FAIL;
5815 * STGM_CREATE | STGM_CONVERT
5816 * if both are false, STGM_FAILIFTHERE is set to TRUE
5818 if ( bSTGM_CREATE && bSTGM_CONVERT )
5819 return E_FAIL;
5822 * STGM_NOSCRATCH requires STGM_TRANSACTED
5824 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
5825 return E_FAIL;
5828 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
5829 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5831 if (bSTGM_NOSNAPSHOT)
5833 if ( ! ( bSTGM_TRANSACTED &&
5834 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
5835 return E_FAIL;
5838 return S_OK;
5841 /****************************************************************************
5842 * GetShareModeFromSTGM
5844 * This method will return a share mode flag from a STGM value.
5845 * The STGM value is assumed valid.
5847 static DWORD GetShareModeFromSTGM(DWORD stgm)
5849 DWORD dwShareMode = 0;
5850 BOOL bSTGM_SHARE_DENY_NONE =
5851 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5853 BOOL bSTGM_SHARE_DENY_READ =
5854 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5856 BOOL bSTGM_SHARE_DENY_WRITE =
5857 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5859 BOOL bSTGM_SHARE_EXCLUSIVE =
5860 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5862 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
5863 dwShareMode = 0;
5865 if (bSTGM_SHARE_DENY_NONE)
5866 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5868 if (bSTGM_SHARE_DENY_WRITE)
5869 dwShareMode = FILE_SHARE_READ;
5871 return dwShareMode;
5874 /****************************************************************************
5875 * GetAccessModeFromSTGM
5877 * This method will return an access mode flag from a STGM value.
5878 * The STGM value is assumed valid.
5880 static DWORD GetAccessModeFromSTGM(DWORD stgm)
5882 DWORD dwDesiredAccess = GENERIC_READ;
5883 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5884 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5885 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5887 if (bSTGM_READ)
5888 dwDesiredAccess = GENERIC_READ;
5890 if (bSTGM_WRITE)
5891 dwDesiredAccess |= GENERIC_WRITE;
5893 if (bSTGM_READWRITE)
5894 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
5896 return dwDesiredAccess;
5899 /****************************************************************************
5900 * GetCreationModeFromSTGM
5902 * This method will return a creation mode flag from a STGM value.
5903 * The STGM value is assumed valid.
5905 static DWORD GetCreationModeFromSTGM(DWORD stgm)
5907 if ( stgm & STGM_CREATE)
5908 return CREATE_ALWAYS;
5909 if (stgm & STGM_CONVERT) {
5910 FIXME("STGM_CONVERT not implemented!\n");
5911 return CREATE_NEW;
5913 /* All other cases */
5914 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
5915 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
5916 return CREATE_NEW;
5920 /*************************************************************************
5921 * OLECONVERT_LoadOLE10 [Internal]
5923 * Loads the OLE10 STREAM to memory
5925 * PARAMS
5926 * pOleStream [I] The OLESTREAM
5927 * pData [I] Data Structure for the OLESTREAM Data
5929 * RETURNS
5930 * Success: S_OK
5931 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
5932 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
5934 * NOTES
5935 * This function is used by OleConvertOLESTREAMToIStorage only.
5937 * Memory allocated for pData must be freed by the caller
5939 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
5941 DWORD dwSize;
5942 HRESULT hRes = S_OK;
5943 int nTryCnt=0;
5944 int max_try = 6;
5946 pData->pData = NULL;
5947 pData->pstrOleObjFileName = (CHAR *) NULL;
5949 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
5951 /* Get the OleID */
5952 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
5953 if(dwSize != sizeof(pData->dwOleID))
5955 hRes = CONVERT10_E_OLESTREAM_GET;
5957 else if(pData->dwOleID != OLESTREAM_ID)
5959 hRes = CONVERT10_E_OLESTREAM_FMT;
5961 else
5963 hRes = S_OK;
5964 break;
5968 if(hRes == S_OK)
5970 /* Get the TypeID...more info needed for this field */
5971 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
5972 if(dwSize != sizeof(pData->dwTypeID))
5974 hRes = CONVERT10_E_OLESTREAM_GET;
5977 if(hRes == S_OK)
5979 if(pData->dwTypeID != 0)
5981 /* Get the lenght of the OleTypeName */
5982 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
5983 if(dwSize != sizeof(pData->dwOleTypeNameLength))
5985 hRes = CONVERT10_E_OLESTREAM_GET;
5988 if(hRes == S_OK)
5990 if(pData->dwOleTypeNameLength > 0)
5992 /* Get the OleTypeName */
5993 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
5994 if(dwSize != pData->dwOleTypeNameLength)
5996 hRes = CONVERT10_E_OLESTREAM_GET;
6000 if(bStrem1)
6002 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6003 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6005 hRes = CONVERT10_E_OLESTREAM_GET;
6007 if(hRes == S_OK)
6009 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6010 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6011 pData->pstrOleObjFileName = (CHAR *)malloc(pData->dwOleObjFileNameLength);
6012 if(pData->pstrOleObjFileName)
6014 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6015 if(dwSize != pData->dwOleObjFileNameLength)
6017 hRes = CONVERT10_E_OLESTREAM_GET;
6020 else
6021 hRes = CONVERT10_E_OLESTREAM_GET;
6024 else
6026 /* Get the Width of the Metafile */
6027 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6028 if(dwSize != sizeof(pData->dwMetaFileWidth))
6030 hRes = CONVERT10_E_OLESTREAM_GET;
6032 if(hRes == S_OK)
6034 /* Get the Height of the Metafile */
6035 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6036 if(dwSize != sizeof(pData->dwMetaFileHeight))
6038 hRes = CONVERT10_E_OLESTREAM_GET;
6042 if(hRes == S_OK)
6044 /* Get the Lenght of the Data */
6045 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6046 if(dwSize != sizeof(pData->dwDataLength))
6048 hRes = CONVERT10_E_OLESTREAM_GET;
6052 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6054 if(!bStrem1) /* if it is a second OLE stream data */
6056 pData->dwDataLength -= 8;
6057 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6058 if(dwSize != sizeof(pData->strUnknown))
6060 hRes = CONVERT10_E_OLESTREAM_GET;
6064 if(hRes == S_OK)
6066 if(pData->dwDataLength > 0)
6068 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6070 /* Get Data (ex. IStorage, Metafile, or BMP) */
6071 if(pData->pData)
6073 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6074 if(dwSize != pData->dwDataLength)
6076 hRes = CONVERT10_E_OLESTREAM_GET;
6079 else
6081 hRes = CONVERT10_E_OLESTREAM_GET;
6087 return hRes;
6090 /*************************************************************************
6091 * OLECONVERT_SaveOLE10 [Internal]
6093 * Saves the OLE10 STREAM From memory
6095 * PARAMS
6096 * pData [I] Data Structure for the OLESTREAM Data
6097 * pOleStream [I] The OLESTREAM to save
6099 * RETURNS
6100 * Success: S_OK
6101 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6103 * NOTES
6104 * This function is used by OleConvertIStorageToOLESTREAM only.
6107 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6109 DWORD dwSize;
6110 HRESULT hRes = S_OK;
6113 /* Set the OleID */
6114 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6115 if(dwSize != sizeof(pData->dwOleID))
6117 hRes = CONVERT10_E_OLESTREAM_PUT;
6120 if(hRes == S_OK)
6122 /* Set the TypeID */
6123 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6124 if(dwSize != sizeof(pData->dwTypeID))
6126 hRes = CONVERT10_E_OLESTREAM_PUT;
6130 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6132 /* Set the Lenght of the OleTypeName */
6133 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6134 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6136 hRes = CONVERT10_E_OLESTREAM_PUT;
6139 if(hRes == S_OK)
6141 if(pData->dwOleTypeNameLength > 0)
6143 /* Set the OleTypeName */
6144 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6145 if(dwSize != pData->dwOleTypeNameLength)
6147 hRes = CONVERT10_E_OLESTREAM_PUT;
6152 if(hRes == S_OK)
6154 /* Set the width of the Metafile */
6155 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6156 if(dwSize != sizeof(pData->dwMetaFileWidth))
6158 hRes = CONVERT10_E_OLESTREAM_PUT;
6162 if(hRes == S_OK)
6164 /* Set the height of the Metafile */
6165 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6166 if(dwSize != sizeof(pData->dwMetaFileHeight))
6168 hRes = CONVERT10_E_OLESTREAM_PUT;
6172 if(hRes == S_OK)
6174 /* Set the lenght of the Data */
6175 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6176 if(dwSize != sizeof(pData->dwDataLength))
6178 hRes = CONVERT10_E_OLESTREAM_PUT;
6182 if(hRes == S_OK)
6184 if(pData->dwDataLength > 0)
6186 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6187 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6188 if(dwSize != pData->dwDataLength)
6190 hRes = CONVERT10_E_OLESTREAM_PUT;
6195 return hRes;
6198 /*************************************************************************
6199 * OLECONVERT_GetOLE20FromOLE10[Internal]
6201 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6202 * opens it, and copies the content to the dest IStorage for
6203 * OleConvertOLESTREAMToIStorage
6206 * PARAMS
6207 * pDestStorage [I] The IStorage to copy the data to
6208 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6209 * nBufferLength [I] The size of the buffer
6211 * RETURNS
6212 * Nothing
6214 * NOTES
6218 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6220 HRESULT hRes;
6221 HANDLE hFile;
6222 IStorage *pTempStorage;
6223 DWORD dwNumOfBytesWritten;
6224 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6225 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6227 /* Create a temp File */
6228 GetTempPathW(MAX_PATH, wstrTempDir);
6229 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6230 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6232 if(hFile != INVALID_HANDLE_VALUE)
6234 /* Write IStorage Data to File */
6235 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6236 CloseHandle(hFile);
6238 /* Open and copy temp storage to the Dest Storage */
6239 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6240 if(hRes == S_OK)
6242 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6243 StorageBaseImpl_Release(pTempStorage);
6245 DeleteFileW(wstrTempFile);
6250 /*************************************************************************
6251 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6253 * Saves the OLE10 STREAM From memory
6255 * PARAMS
6256 * pStorage [I] The Src IStorage to copy
6257 * pData [I] The Dest Memory to write to.
6259 * RETURNS
6260 * The size in bytes allocated for pData
6262 * NOTES
6263 * Memory allocated for pData must be freed by the caller
6265 * Used by OleConvertIStorageToOLESTREAM only.
6268 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6270 HANDLE hFile;
6271 HRESULT hRes;
6272 DWORD nDataLength = 0;
6273 IStorage *pTempStorage;
6274 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6275 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6277 *pData = NULL;
6279 /* Create temp Storage */
6280 GetTempPathW(MAX_PATH, wstrTempDir);
6281 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6282 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6284 if(hRes == S_OK)
6286 /* Copy Src Storage to the Temp Storage */
6287 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6288 StorageBaseImpl_Release(pTempStorage);
6290 /* Open Temp Storage as a file and copy to memory */
6291 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6292 if(hFile != INVALID_HANDLE_VALUE)
6294 nDataLength = GetFileSize(hFile, NULL);
6295 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6296 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6297 CloseHandle(hFile);
6299 DeleteFileW(wstrTempFile);
6301 return nDataLength;
6304 /*************************************************************************
6305 * OLECONVERT_CreateOleStream [Internal]
6307 * Creates the "\001OLE" stream in the IStorage if neccessary.
6309 * PARAMS
6310 * pStorage [I] Dest storage to create the stream in
6312 * RETURNS
6313 * Nothing
6315 * NOTES
6316 * This function is used by OleConvertOLESTREAMToIStorage only.
6318 * This stream is still unknown, MS Word seems to have extra data
6319 * but since the data is stored in the OLESTREAM there should be
6320 * no need to recreate the stream. If the stream is manually
6321 * deleted it will create it with this default data.
6324 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6326 HRESULT hRes;
6327 IStream *pStream;
6328 WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6329 BYTE pOleStreamHeader [] =
6331 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6332 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6333 0x00, 0x00, 0x00, 0x00
6336 /* Create stream if not present */
6337 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6338 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6340 if(hRes == S_OK)
6342 /* Write default Data */
6343 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6344 IStream_Release(pStream);
6349 /*************************************************************************
6350 * OLECONVERT_CreateCompObjStream [Internal]
6352 * Creates a "\001CompObj" is the destination IStorage if necessary.
6354 * PARAMS
6355 * pStorage [I] The dest IStorage to create the CompObj Stream
6356 * if necessary.
6357 * strOleTypeName [I] The ProgID
6359 * RETURNS
6360 * Success: S_OK
6361 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6363 * NOTES
6364 * This function is used by OleConvertOLESTREAMToIStorage only.
6366 * The stream data is stored in the OLESTREAM and there should be
6367 * no need to recreate the stream. If the stream is manually
6368 * deleted it will attempt to create it by querying the registry.
6372 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6374 IStream *pStream;
6375 HRESULT hStorageRes, hRes = S_OK;
6376 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6377 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6379 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6380 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6382 /* Initialize the CompObj structure */
6383 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6384 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6385 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6388 /* Create a CompObj stream if it doesn't exist */
6389 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6390 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6391 if(hStorageRes == S_OK)
6393 /* copy the OleTypeName to the compobj struct */
6394 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6395 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6397 /* copy the OleTypeName to the compobj struct */
6398 /* Note: in the test made, these where Identical */
6399 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6400 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6402 /* Get the CLSID */
6403 hRes = CLSIDFromProgID16(IStorageCompObj.strProgIDName, &(IStorageCompObj.clsid));
6405 if(hRes == S_OK)
6407 HKEY hKey;
6408 LONG hErr;
6409 /* Get the CLSID Default Name from the Registry */
6410 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6411 if(hErr == ERROR_SUCCESS)
6413 char strTemp[OLESTREAM_MAX_STR_LEN];
6414 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6415 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6416 if(hErr == ERROR_SUCCESS)
6418 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6420 RegCloseKey(hKey);
6424 /* Write CompObj Structure to stream */
6425 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6427 WriteClassStm(pStream,&(IStorageCompObj.clsid));
6429 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6430 if(IStorageCompObj.dwCLSIDNameLength > 0)
6432 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6434 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6435 if(IStorageCompObj.dwOleTypeNameLength > 0)
6437 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6439 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6440 if(IStorageCompObj.dwProgIDNameLength > 0)
6442 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6444 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6445 IStream_Release(pStream);
6447 return hRes;
6451 /*************************************************************************
6452 * OLECONVERT_CreateOlePresStream[Internal]
6454 * Creates the "\002OlePres000" Stream with the Metafile data
6456 * PARAMS
6457 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6458 * dwExtentX [I] Width of the Metafile
6459 * dwExtentY [I] Height of the Metafile
6460 * pData [I] Metafile data
6461 * dwDataLength [I] Size of the Metafile data
6463 * RETURNS
6464 * Success: S_OK
6465 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6467 * NOTES
6468 * This function is used by OleConvertOLESTREAMToIStorage only.
6471 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6473 HRESULT hRes;
6474 IStream *pStream;
6475 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6476 BYTE pOlePresStreamHeader [] =
6478 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6479 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6480 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6481 0x00, 0x00, 0x00, 0x00
6484 BYTE pOlePresStreamHeaderEmpty [] =
6486 0x00, 0x00, 0x00, 0x00,
6487 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6488 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6489 0x00, 0x00, 0x00, 0x00
6492 /* Create the OlePres000 Stream */
6493 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6494 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6496 if(hRes == S_OK)
6498 DWORD nHeaderSize;
6499 OLECONVERT_ISTORAGE_OLEPRES OlePres;
6501 memset(&OlePres, 0, sizeof(OlePres));
6502 /* Do we have any metafile data to save */
6503 if(dwDataLength > 0)
6505 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6506 nHeaderSize = sizeof(pOlePresStreamHeader);
6508 else
6510 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
6511 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
6513 /* Set width and height of the metafile */
6514 OlePres.dwExtentX = dwExtentX;
6515 OlePres.dwExtentY = -dwExtentY;
6517 /* Set Data and Lenght */
6518 if(dwDataLength > sizeof(METAFILEPICT16))
6520 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
6521 OlePres.pData = &(pData[8]);
6523 /* Save OlePres000 Data to Stream */
6524 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
6525 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
6526 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
6527 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
6528 if(OlePres.dwSize > 0)
6530 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
6532 IStream_Release(pStream);
6536 /*************************************************************************
6537 * OLECONVERT_CreateOle10NativeStream [Internal]
6539 * Creates the "\001Ole10Native" Stream (should contain a BMP)
6541 * PARAMS
6542 * pStorage [I] Dest storage to create the stream in
6543 * pData [I] Ole10 Native Data (ex. bmp)
6544 * dwDataLength [I] Size of the Ole10 Native Data
6546 * RETURNS
6547 * Nothing
6549 * NOTES
6550 * This function is used by OleConvertOLESTREAMToIStorage only.
6552 * Might need to verify the data and return appropriate error message
6555 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
6557 HRESULT hRes;
6558 IStream *pStream;
6559 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6561 /* Create the Ole10Native Stream */
6562 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6563 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6565 if(hRes == S_OK)
6567 /* Write info to stream */
6568 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
6569 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
6570 IStream_Release(pStream);
6575 /*************************************************************************
6576 * OLECONVERT_GetOLE10ProgID [Internal]
6578 * Finds the ProgID (or OleTypeID) from the IStorage
6580 * PARAMS
6581 * pStorage [I] The Src IStorage to get the ProgID
6582 * strProgID [I] the ProgID string to get
6583 * dwSize [I] the size of the string
6585 * RETURNS
6586 * Success: S_OK
6587 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6589 * NOTES
6590 * This function is used by OleConvertIStorageToOLESTREAM only.
6594 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
6596 HRESULT hRes;
6597 IStream *pStream;
6598 LARGE_INTEGER iSeekPos;
6599 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
6600 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6602 /* Open the CompObj Stream */
6603 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6604 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6605 if(hRes == S_OK)
6608 /*Get the OleType from the CompObj Stream */
6609 iSeekPos.s.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
6610 iSeekPos.s.HighPart = 0;
6612 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6613 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
6614 iSeekPos.s.LowPart = CompObj.dwCLSIDNameLength;
6615 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6616 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
6617 iSeekPos.s.LowPart = CompObj.dwOleTypeNameLength;
6618 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6620 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
6621 if(*dwSize > 0)
6623 IStream_Read(pStream, strProgID, *dwSize, NULL);
6625 IStream_Release(pStream);
6627 else
6629 STATSTG stat;
6630 LPOLESTR wstrProgID;
6632 /* Get the OleType from the registry */
6633 REFCLSID clsid = &(stat.clsid);
6634 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
6635 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
6636 if(hRes == S_OK)
6638 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
6642 return hRes;
6645 /*************************************************************************
6646 * OLECONVERT_GetOle10PresData [Internal]
6648 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
6650 * PARAMS
6651 * pStorage [I] Src IStroage
6652 * pOleStream [I] Dest OleStream Mem Struct
6654 * RETURNS
6655 * Nothing
6657 * NOTES
6658 * This function is used by OleConvertIStorageToOLESTREAM only.
6660 * Memory allocated for pData must be freed by the caller
6664 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6667 HRESULT hRes;
6668 IStream *pStream;
6669 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6671 /* Initialize Default data for OLESTREAM */
6672 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6673 pOleStreamData[0].dwTypeID = 2;
6674 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6675 pOleStreamData[1].dwTypeID = 0;
6676 pOleStreamData[0].dwMetaFileWidth = 0;
6677 pOleStreamData[0].dwMetaFileHeight = 0;
6678 pOleStreamData[0].pData = NULL;
6679 pOleStreamData[1].pData = NULL;
6681 /* Open Ole10Native Stream */
6682 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6683 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6684 if(hRes == S_OK)
6687 /* Read Size and Data */
6688 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
6689 if(pOleStreamData->dwDataLength > 0)
6691 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
6692 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
6694 IStream_Release(pStream);
6700 /*************************************************************************
6701 * OLECONVERT_GetOle20PresData[Internal]
6703 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
6705 * PARAMS
6706 * pStorage [I] Src IStroage
6707 * pOleStreamData [I] Dest OleStream Mem Struct
6709 * RETURNS
6710 * Nothing
6712 * NOTES
6713 * This function is used by OleConvertIStorageToOLESTREAM only.
6715 * Memory allocated for pData must be freed by the caller
6717 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6719 HRESULT hRes;
6720 IStream *pStream;
6721 OLECONVERT_ISTORAGE_OLEPRES olePress;
6722 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6724 /* Initialize Default data for OLESTREAM */
6725 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6726 pOleStreamData[0].dwTypeID = 2;
6727 pOleStreamData[0].dwMetaFileWidth = 0;
6728 pOleStreamData[0].dwMetaFileHeight = 0;
6729 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
6730 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6731 pOleStreamData[1].dwTypeID = 0;
6732 pOleStreamData[1].dwOleTypeNameLength = 0;
6733 pOleStreamData[1].strOleTypeName[0] = 0;
6734 pOleStreamData[1].dwMetaFileWidth = 0;
6735 pOleStreamData[1].dwMetaFileHeight = 0;
6736 pOleStreamData[1].pData = NULL;
6737 pOleStreamData[1].dwDataLength = 0;
6740 /* Open OlePress000 stream */
6741 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6742 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6743 if(hRes == S_OK)
6745 LARGE_INTEGER iSeekPos;
6746 METAFILEPICT16 MetaFilePict;
6747 char strMetafilePictName[] = "METAFILEPICT";
6749 /* Set the TypeID for a Metafile */
6750 pOleStreamData[1].dwTypeID = 5;
6752 /* Set the OleTypeName to Metafile */
6753 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
6754 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
6756 iSeekPos.s.HighPart = 0;
6757 iSeekPos.s.LowPart = sizeof(olePress.byUnknown1);
6759 /* Get Presentation Data */
6760 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6761 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
6762 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
6763 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
6765 /*Set width and Height */
6766 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
6767 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
6768 if(olePress.dwSize > 0)
6770 /* Set Length */
6771 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
6773 /* Set MetaFilePict struct */
6774 MetaFilePict.mm = 8;
6775 MetaFilePict.xExt = olePress.dwExtentX;
6776 MetaFilePict.yExt = olePress.dwExtentY;
6777 MetaFilePict.hMF = 0;
6779 /* Get Metafile Data */
6780 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
6781 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
6782 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
6784 IStream_Release(pStream);
6788 /*************************************************************************
6789 * OleConvertOLESTREAMToIStorage [OLE32.87]
6791 * Read info on MSDN
6793 * TODO
6794 * DVTARGETDEVICE paramenter is not handled
6795 * Still unsure of some mem fields for OLE 10 Stream
6796 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6797 * and "\001OLE" streams
6800 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
6801 LPOLESTREAM pOleStream,
6802 LPSTORAGE pstg,
6803 const DVTARGETDEVICE* ptd)
6805 int i;
6806 HRESULT hRes=S_OK;
6807 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
6809 memset(pOleStreamData, 0, sizeof(pOleStreamData));
6811 if(ptd != NULL)
6813 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
6816 if(pstg == NULL || pOleStream == NULL)
6818 hRes = E_INVALIDARG;
6821 if(hRes == S_OK)
6823 /* Load the OLESTREAM to Memory */
6824 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
6827 if(hRes == S_OK)
6829 /* Load the OLESTREAM to Memory (part 2)*/
6830 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
6833 if(hRes == S_OK)
6836 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
6838 /* Do we have the IStorage Data in the OLESTREAM */
6839 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
6841 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6842 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
6844 else
6846 /* It must be an original OLE 1.0 source */
6847 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6850 else
6852 /* It must be an original OLE 1.0 source */
6853 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6856 /* Create CompObj Stream if necessary */
6857 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
6858 if(hRes == S_OK)
6860 /*Create the Ole Stream if necessary */
6861 OLECONVERT_CreateOleStream(pstg);
6866 /* Free allocated memory */
6867 for(i=0; i < 2; i++)
6869 if(pOleStreamData[i].pData != NULL)
6871 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
6873 if(pOleStreamData[i].pstrOleObjFileName != NULL)
6875 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
6876 pOleStreamData[i].pstrOleObjFileName = NULL;
6879 return hRes;
6882 /*************************************************************************
6883 * OleConvertIStorageToOLESTREAM [OLE32.85]
6885 * Read info on MSDN
6887 * Read info on MSDN
6889 * TODO
6890 * Still unsure of some mem fields for OLE 10 Stream
6891 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6892 * and "\001OLE" streams.
6895 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
6896 LPSTORAGE pstg,
6897 LPOLESTREAM pOleStream)
6899 int i;
6900 HRESULT hRes = S_OK;
6901 IStream *pStream;
6902 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
6903 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6906 memset(pOleStreamData, 0, sizeof(pOleStreamData));
6908 if(pstg == NULL || pOleStream == NULL)
6910 hRes = E_INVALIDARG;
6912 if(hRes == S_OK)
6914 /* Get the ProgID */
6915 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
6916 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
6918 if(hRes == S_OK)
6920 /*Was it originaly Ole10 */
6921 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
6922 if(hRes == S_OK)
6924 IStream_Release(pStream);
6925 /*Get Presentation Data for Ole10Native */
6926 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
6928 else
6930 /*Get Presentation Data (OLE20)*/
6931 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
6934 /* Save OLESTREAM */
6935 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
6936 if(hRes == S_OK)
6938 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
6943 /* Free allocated memory */
6944 for(i=0; i < 2; i++)
6946 if(pOleStreamData[i].pData != NULL)
6948 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
6952 return hRes;