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