Also remove configure.lineno when performing distclean.
[wine/hacks.git] / dlls / ole32 / storage32.c
blobabf8c32adec64d3e92a9e8ef706bb5d7f0cdd465
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
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <assert.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
32 #include "winbase.h" /* for lstrlenW() and the likes */
33 #include "winnls.h"
34 #include "wine/unicode.h"
35 #include "wine/debug.h"
37 #include "storage32.h"
38 #include "ole2.h" /* For Write/ReadClassStm */
40 #include "winreg.h"
41 #include "wine/wingdi16.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(storage);
45 #define FILE_BEGIN 0
48 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
49 #define OLESTREAM_ID 0x501
50 #define OLESTREAM_MAX_STR_LEN 255
52 static const char rootPropertyName[] = "Root Entry";
55 /* OLESTREAM memory structure to use for Get and Put Routines */
56 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
57 typedef struct
59 DWORD dwOleID;
60 DWORD dwTypeID;
61 DWORD dwOleTypeNameLength;
62 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
63 CHAR *pstrOleObjFileName;
64 DWORD dwOleObjFileNameLength;
65 DWORD dwMetaFileWidth;
66 DWORD dwMetaFileHeight;
67 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
68 DWORD dwDataLength;
69 BYTE *pData;
70 }OLECONVERT_OLESTREAM_DATA;
72 /* CompObj Stream structure */
73 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
74 typedef struct
76 BYTE byUnknown1[12];
77 CLSID clsid;
78 DWORD dwCLSIDNameLength;
79 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
80 DWORD dwOleTypeNameLength;
81 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
82 DWORD dwProgIDNameLength;
83 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
84 BYTE byUnknown2[16];
85 }OLECONVERT_ISTORAGE_COMPOBJ;
88 /* Ole Presention Stream structure */
89 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
90 typedef struct
92 BYTE byUnknown1[28];
93 DWORD dwExtentX;
94 DWORD dwExtentY;
95 DWORD dwSize;
96 BYTE *pData;
97 }OLECONVERT_ISTORAGE_OLEPRES;
101 /***********************************************************************
102 * Forward declaration of internal functions used by the method DestroyElement
104 static HRESULT deleteStorageProperty(
105 StorageImpl *parentStorage,
106 ULONG foundPropertyIndexToDelete,
107 StgProperty propertyToDelete);
109 static HRESULT deleteStreamProperty(
110 StorageImpl *parentStorage,
111 ULONG foundPropertyIndexToDelete,
112 StgProperty propertyToDelete);
114 static HRESULT findPlaceholder(
115 StorageImpl *storage,
116 ULONG propertyIndexToStore,
117 ULONG storagePropertyIndex,
118 INT typeOfRelation);
120 static HRESULT adjustPropertyChain(
121 StorageImpl *This,
122 StgProperty propertyToDelete,
123 StgProperty parentProperty,
124 ULONG parentPropertyId,
125 INT typeOfRelation);
127 /***********************************************************************
128 * Declaration of the functions used to manipulate StgProperty
131 static ULONG getFreeProperty(
132 StorageImpl *storage);
134 static void updatePropertyChain(
135 StorageImpl *storage,
136 ULONG newPropertyIndex,
137 StgProperty newProperty);
139 static LONG propertyNameCmp(
140 OLECHAR *newProperty,
141 OLECHAR *currentProperty);
144 /***********************************************************************
145 * Declaration of miscellaneous functions...
147 static HRESULT validateSTGM(DWORD stgmValue);
149 static DWORD GetShareModeFromSTGM(DWORD stgm);
150 static DWORD GetAccessModeFromSTGM(DWORD stgm);
151 static DWORD GetCreationModeFromSTGM(DWORD stgm);
154 * Virtual function table for the IStorage32Impl class.
156 static ICOM_VTABLE(IStorage) Storage32Impl_Vtbl =
158 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
159 StorageBaseImpl_QueryInterface,
160 StorageBaseImpl_AddRef,
161 StorageBaseImpl_Release,
162 StorageBaseImpl_CreateStream,
163 StorageBaseImpl_OpenStream,
164 StorageImpl_CreateStorage,
165 StorageBaseImpl_OpenStorage,
166 StorageImpl_CopyTo,
167 StorageImpl_MoveElementTo,
168 StorageImpl_Commit,
169 StorageImpl_Revert,
170 StorageBaseImpl_EnumElements,
171 StorageImpl_DestroyElement,
172 StorageBaseImpl_RenameElement,
173 StorageImpl_SetElementTimes,
174 StorageBaseImpl_SetClass,
175 StorageImpl_SetStateBits,
176 StorageImpl_Stat
180 * Virtual function table for the Storage32InternalImpl class.
182 static ICOM_VTABLE(IStorage) Storage32InternalImpl_Vtbl =
184 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
185 StorageBaseImpl_QueryInterface,
186 StorageBaseImpl_AddRef,
187 StorageBaseImpl_Release,
188 StorageBaseImpl_CreateStream,
189 StorageBaseImpl_OpenStream,
190 StorageImpl_CreateStorage,
191 StorageBaseImpl_OpenStorage,
192 StorageImpl_CopyTo,
193 StorageImpl_MoveElementTo,
194 StorageInternalImpl_Commit,
195 StorageInternalImpl_Revert,
196 StorageBaseImpl_EnumElements,
197 StorageImpl_DestroyElement,
198 StorageBaseImpl_RenameElement,
199 StorageImpl_SetElementTimes,
200 StorageBaseImpl_SetClass,
201 StorageImpl_SetStateBits,
202 StorageBaseImpl_Stat
206 * Virtual function table for the IEnumSTATSTGImpl class.
208 static ICOM_VTABLE(IEnumSTATSTG) IEnumSTATSTGImpl_Vtbl =
210 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
211 IEnumSTATSTGImpl_QueryInterface,
212 IEnumSTATSTGImpl_AddRef,
213 IEnumSTATSTGImpl_Release,
214 IEnumSTATSTGImpl_Next,
215 IEnumSTATSTGImpl_Skip,
216 IEnumSTATSTGImpl_Reset,
217 IEnumSTATSTGImpl_Clone
224 /************************************************************************
225 ** Storage32BaseImpl implementatiion
228 /************************************************************************
229 * Storage32BaseImpl_QueryInterface (IUnknown)
231 * This method implements the common QueryInterface for all IStorage32
232 * implementations contained in this file.
234 * See Windows documentation for more details on IUnknown methods.
236 HRESULT WINAPI StorageBaseImpl_QueryInterface(
237 IStorage* iface,
238 REFIID riid,
239 void** ppvObject)
241 ICOM_THIS(StorageBaseImpl,iface);
243 * Perform a sanity check on the parameters.
245 if ( (This==0) || (ppvObject==0) )
246 return E_INVALIDARG;
249 * Initialize the return parameter.
251 *ppvObject = 0;
254 * Compare the riid with the interface IDs implemented by this object.
256 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
258 *ppvObject = (IStorage*)This;
260 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
262 *ppvObject = (IStorage*)This;
266 * Check that we obtained an interface.
268 if ((*ppvObject)==0)
269 return E_NOINTERFACE;
272 * Query Interface always increases the reference count by one when it is
273 * successful
275 StorageBaseImpl_AddRef(iface);
277 return S_OK;
280 /************************************************************************
281 * Storage32BaseImpl_AddRef (IUnknown)
283 * This method implements the common AddRef for all IStorage32
284 * implementations contained in this file.
286 * See Windows documentation for more details on IUnknown methods.
288 ULONG WINAPI StorageBaseImpl_AddRef(
289 IStorage* iface)
291 ICOM_THIS(StorageBaseImpl,iface);
292 This->ref++;
294 return This->ref;
297 /************************************************************************
298 * Storage32BaseImpl_Release (IUnknown)
300 * This method implements the common Release for all IStorage32
301 * implementations contained in this file.
303 * See Windows documentation for more details on IUnknown methods.
305 ULONG WINAPI StorageBaseImpl_Release(
306 IStorage* iface)
308 ICOM_THIS(StorageBaseImpl,iface);
310 * Decrease the reference count on this object.
312 This->ref--;
315 * If the reference count goes down to 0, perform suicide.
317 if (This->ref==0)
320 * Since we are using a system of base-classes, we want to call the
321 * destructor of the appropriate derived class. To do this, we are
322 * using virtual functions to implement the destructor.
324 This->v_destructor(This);
326 return 0;
329 return This->ref;
332 /************************************************************************
333 * Storage32BaseImpl_OpenStream (IStorage)
335 * This method will open the specified stream object from the current storage.
337 * See Windows documentation for more details on IStorage methods.
339 HRESULT WINAPI StorageBaseImpl_OpenStream(
340 IStorage* iface,
341 const OLECHAR* pwcsName, /* [string][in] */
342 void* reserved1, /* [unique][in] */
343 DWORD grfMode, /* [in] */
344 DWORD reserved2, /* [in] */
345 IStream** ppstm) /* [out] */
347 ICOM_THIS(StorageBaseImpl,iface);
348 IEnumSTATSTGImpl* propertyEnumeration;
349 StgStreamImpl* newStream;
350 StgProperty currentProperty;
351 ULONG foundPropertyIndex;
352 HRESULT res = STG_E_UNKNOWN;
354 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
355 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
358 * Perform a sanity check on the parameters.
360 if ( (pwcsName==NULL) || (ppstm==0) )
362 res = E_INVALIDARG;
363 goto end;
367 * Initialize the out parameter
369 *ppstm = NULL;
372 * Validate the STGM flags
374 if ( FAILED( validateSTGM(grfMode) ))
376 res = STG_E_INVALIDFLAG;
377 goto end;
381 * As documented.
383 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
384 (grfMode & STGM_DELETEONRELEASE) ||
385 (grfMode & STGM_TRANSACTED) )
387 res = STG_E_INVALIDFUNCTION;
388 goto end;
392 * Create a property enumeration to search the properties
394 propertyEnumeration = IEnumSTATSTGImpl_Construct(
395 This->ancestorStorage,
396 This->rootPropertySetIndex);
399 * Search the enumeration for the property with the given name
401 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
402 propertyEnumeration,
403 pwcsName,
404 &currentProperty);
407 * Delete the property enumeration since we don't need it anymore
409 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
412 * If it was found, construct the stream object and return a pointer to it.
414 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
415 (currentProperty.propertyType==PROPTYPE_STREAM) )
417 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
419 if (newStream!=0)
421 newStream->grfMode = grfMode;
422 *ppstm = (IStream*)newStream;
425 * Since we are returning a pointer to the interface, we have to
426 * nail down the reference.
428 StgStreamImpl_AddRef(*ppstm);
430 res = S_OK;
431 goto end;
434 res = E_OUTOFMEMORY;
435 goto end;
438 res = STG_E_FILENOTFOUND;
440 end:
441 if (res == S_OK)
442 TRACE("<-- IStream %p\n", *ppstm);
443 TRACE("<-- %08lx\n", res);
444 return res;
447 /************************************************************************
448 * Storage32BaseImpl_OpenStorage (IStorage)
450 * This method will open a new storage object from the current storage.
452 * See Windows documentation for more details on IStorage methods.
454 HRESULT WINAPI StorageBaseImpl_OpenStorage(
455 IStorage* iface,
456 const OLECHAR* pwcsName, /* [string][unique][in] */
457 IStorage* pstgPriority, /* [unique][in] */
458 DWORD grfMode, /* [in] */
459 SNB snbExclude, /* [unique][in] */
460 DWORD reserved, /* [in] */
461 IStorage** ppstg) /* [out] */
463 ICOM_THIS(StorageBaseImpl,iface);
464 StorageInternalImpl* newStorage;
465 IEnumSTATSTGImpl* propertyEnumeration;
466 StgProperty currentProperty;
467 ULONG foundPropertyIndex;
468 HRESULT res = STG_E_UNKNOWN;
470 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
471 iface, debugstr_w(pwcsName), pstgPriority,
472 grfMode, snbExclude, reserved, ppstg);
475 * Perform a sanity check on the parameters.
477 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
479 res = E_INVALIDARG;
480 goto end;
483 /* as documented */
484 if (snbExclude != NULL)
486 res = STG_E_INVALIDPARAMETER;
487 goto end;
491 * Validate the STGM flags
493 if ( FAILED( validateSTGM(grfMode) ))
495 res = STG_E_INVALIDFLAG;
496 goto end;
500 * As documented.
502 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
503 (grfMode & STGM_DELETEONRELEASE) ||
504 (grfMode & STGM_PRIORITY) )
506 res = STG_E_INVALIDFUNCTION;
507 goto end;
511 * Initialize the out parameter
513 *ppstg = NULL;
516 * Create a property enumeration to search the properties
518 propertyEnumeration = IEnumSTATSTGImpl_Construct(
519 This->ancestorStorage,
520 This->rootPropertySetIndex);
523 * Search the enumeration for the property with the given name
525 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
526 propertyEnumeration,
527 pwcsName,
528 &currentProperty);
531 * Delete the property enumeration since we don't need it anymore
533 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
536 * If it was found, construct the stream object and return a pointer to it.
538 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
539 (currentProperty.propertyType==PROPTYPE_STORAGE) )
542 * Construct a new Storage object
544 newStorage = StorageInternalImpl_Construct(
545 This->ancestorStorage,
546 foundPropertyIndex);
548 if (newStorage != 0)
550 *ppstg = (IStorage*)newStorage;
553 * Since we are returning a pointer to the interface,
554 * we have to nail down the reference.
556 StorageBaseImpl_AddRef(*ppstg);
558 res = S_OK;
559 goto end;
562 res = STG_E_INSUFFICIENTMEMORY;
563 goto end;
566 res = STG_E_FILENOTFOUND;
568 end:
569 TRACE("<-- %08lx\n", res);
570 return res;
573 /************************************************************************
574 * Storage32BaseImpl_EnumElements (IStorage)
576 * This method will create an enumerator object that can be used to
577 * retrieve informatino about all the properties in the storage object.
579 * See Windows documentation for more details on IStorage methods.
581 HRESULT WINAPI StorageBaseImpl_EnumElements(
582 IStorage* iface,
583 DWORD reserved1, /* [in] */
584 void* reserved2, /* [size_is][unique][in] */
585 DWORD reserved3, /* [in] */
586 IEnumSTATSTG** ppenum) /* [out] */
588 ICOM_THIS(StorageBaseImpl,iface);
589 IEnumSTATSTGImpl* newEnum;
591 TRACE("(%p, %ld, %p, %ld, %p)\n",
592 iface, reserved1, reserved2, reserved3, ppenum);
595 * Perform a sanity check on the parameters.
597 if ( (This==0) || (ppenum==0))
598 return E_INVALIDARG;
601 * Construct the enumerator.
603 newEnum = IEnumSTATSTGImpl_Construct(
604 This->ancestorStorage,
605 This->rootPropertySetIndex);
607 if (newEnum!=0)
609 *ppenum = (IEnumSTATSTG*)newEnum;
612 * Don't forget to nail down a reference to the new object before
613 * returning it.
615 IEnumSTATSTGImpl_AddRef(*ppenum);
617 return S_OK;
620 return E_OUTOFMEMORY;
623 /************************************************************************
624 * Storage32BaseImpl_Stat (IStorage)
626 * This method will retrieve information about this storage object.
628 * See Windows documentation for more details on IStorage methods.
630 HRESULT WINAPI StorageBaseImpl_Stat(
631 IStorage* iface,
632 STATSTG* pstatstg, /* [out] */
633 DWORD grfStatFlag) /* [in] */
635 ICOM_THIS(StorageBaseImpl,iface);
636 StgProperty curProperty;
637 BOOL readSuccessful;
638 HRESULT res = STG_E_UNKNOWN;
640 TRACE("(%p, %p, %lx)\n",
641 iface, pstatstg, grfStatFlag);
644 * Perform a sanity check on the parameters.
646 if ( (This==0) || (pstatstg==0))
648 res = E_INVALIDARG;
649 goto end;
653 * Read the information from the property.
655 readSuccessful = StorageImpl_ReadProperty(
656 This->ancestorStorage,
657 This->rootPropertySetIndex,
658 &curProperty);
660 if (readSuccessful)
662 StorageUtl_CopyPropertyToSTATSTG(
663 pstatstg,
664 &curProperty,
665 grfStatFlag);
667 res = S_OK;
668 goto end;
671 res = E_FAIL;
673 end:
674 if (res == S_OK)
676 TRACE("<-- STATSTG: pwcsName: %s, type: %ld, cbSize.Low/High: %ld/%ld, grfMode: %08lx, grfLocksSupported: %ld, grfStateBits: %08lx\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.s.LowPart, pstatstg->cbSize.s.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
678 TRACE("<-- %08lx\n", res);
679 return res;
682 /************************************************************************
683 * Storage32BaseImpl_RenameElement (IStorage)
685 * This method will rename the specified element.
687 * See Windows documentation for more details on IStorage methods.
689 * Implementation notes: The method used to rename consists of creating a clone
690 * of the deleted StgProperty object setting it with the new name and to
691 * perform a DestroyElement of the old StgProperty.
693 HRESULT WINAPI StorageBaseImpl_RenameElement(
694 IStorage* iface,
695 const OLECHAR* pwcsOldName, /* [in] */
696 const OLECHAR* pwcsNewName) /* [in] */
698 ICOM_THIS(StorageBaseImpl,iface);
699 IEnumSTATSTGImpl* propertyEnumeration;
700 StgProperty currentProperty;
701 ULONG foundPropertyIndex;
703 TRACE("(%p, %s, %s)\n",
704 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
707 * Create a property enumeration to search the properties
709 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
710 This->rootPropertySetIndex);
713 * Search the enumeration for the new property name
715 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
716 pwcsNewName,
717 &currentProperty);
719 if (foundPropertyIndex != PROPERTY_NULL)
722 * There is already a property with the new name
724 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
725 return STG_E_FILEALREADYEXISTS;
728 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
731 * Search the enumeration for the old property name
733 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
734 pwcsOldName,
735 &currentProperty);
738 * Delete the property enumeration since we don't need it anymore
740 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
742 if (foundPropertyIndex != PROPERTY_NULL)
744 StgProperty renamedProperty;
745 ULONG renamedPropertyIndex;
748 * Setup a new property for the renamed property
750 renamedProperty.sizeOfNameString =
751 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
753 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
754 return STG_E_INVALIDNAME;
756 strcpyW(renamedProperty.name, pwcsNewName);
758 renamedProperty.propertyType = currentProperty.propertyType;
759 renamedProperty.startingBlock = currentProperty.startingBlock;
760 renamedProperty.size.s.LowPart = currentProperty.size.s.LowPart;
761 renamedProperty.size.s.HighPart = currentProperty.size.s.HighPart;
763 renamedProperty.previousProperty = PROPERTY_NULL;
764 renamedProperty.nextProperty = PROPERTY_NULL;
767 * Bring the dirProperty link in case it is a storage and in which
768 * case the renamed storage elements don't require to be reorganized.
770 renamedProperty.dirProperty = currentProperty.dirProperty;
772 /* call CoFileTime to get the current time
773 renamedProperty.timeStampS1
774 renamedProperty.timeStampD1
775 renamedProperty.timeStampS2
776 renamedProperty.timeStampD2
777 renamedProperty.propertyUniqueID
781 * Obtain a free property in the property chain
783 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
786 * Save the new property into the new property spot
788 StorageImpl_WriteProperty(
789 This->ancestorStorage,
790 renamedPropertyIndex,
791 &renamedProperty);
794 * Find a spot in the property chain for our newly created property.
796 updatePropertyChain(
797 (StorageImpl*)This,
798 renamedPropertyIndex,
799 renamedProperty);
802 * At this point the renamed property has been inserted in the tree,
803 * now, before to Destroy the old property we must zeroed it's dirProperty
804 * otherwise the DestroyProperty below will zap it all and we do not want
805 * this to happen.
806 * Also, we fake that the old property is a storage so the DestroyProperty
807 * will not do a SetSize(0) on the stream data.
809 * This means that we need to tweek the StgProperty if it is a stream or a
810 * non empty storage.
812 StorageImpl_ReadProperty(This->ancestorStorage,
813 foundPropertyIndex,
814 &currentProperty);
816 currentProperty.dirProperty = PROPERTY_NULL;
817 currentProperty.propertyType = PROPTYPE_STORAGE;
818 StorageImpl_WriteProperty(
819 This->ancestorStorage,
820 foundPropertyIndex,
821 &currentProperty);
824 * Invoke Destroy to get rid of the ole property and automatically redo
825 * the linking of it's previous and next members...
827 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
830 else
833 * There is no property with the old name
835 return STG_E_FILENOTFOUND;
838 return S_OK;
841 /************************************************************************
842 * Storage32BaseImpl_CreateStream (IStorage)
844 * This method will create a stream object within this storage
846 * See Windows documentation for more details on IStorage methods.
848 HRESULT WINAPI StorageBaseImpl_CreateStream(
849 IStorage* iface,
850 const OLECHAR* pwcsName, /* [string][in] */
851 DWORD grfMode, /* [in] */
852 DWORD reserved1, /* [in] */
853 DWORD reserved2, /* [in] */
854 IStream** ppstm) /* [out] */
856 ICOM_THIS(StorageBaseImpl,iface);
857 IEnumSTATSTGImpl* propertyEnumeration;
858 StgStreamImpl* newStream;
859 StgProperty currentProperty, newStreamProperty;
860 ULONG foundPropertyIndex, newPropertyIndex;
862 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
863 iface, debugstr_w(pwcsName), grfMode,
864 reserved1, reserved2, ppstm);
867 * Validate parameters
869 if (ppstm == 0)
870 return STG_E_INVALIDPOINTER;
872 if (pwcsName == 0)
873 return STG_E_INVALIDNAME;
876 * Validate the STGM flags
878 if ( FAILED( validateSTGM(grfMode) ))
879 return STG_E_INVALIDFLAG;
882 * As documented.
884 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
885 (grfMode & STGM_DELETEONRELEASE) ||
886 (grfMode & STGM_TRANSACTED) )
887 return STG_E_INVALIDFUNCTION;
890 * Initialize the out parameter
892 *ppstm = 0;
895 * Create a property enumeration to search the properties
897 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
898 This->rootPropertySetIndex);
900 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
901 pwcsName,
902 &currentProperty);
904 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
906 if (foundPropertyIndex != PROPERTY_NULL)
909 * An element with this name already exists
911 if (grfMode & STGM_CREATE)
913 IStorage_DestroyElement(iface, pwcsName);
915 else
916 return STG_E_FILEALREADYEXISTS;
920 * memset the empty property
922 memset(&newStreamProperty, 0, sizeof(StgProperty));
924 newStreamProperty.sizeOfNameString =
925 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
927 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
928 return STG_E_INVALIDNAME;
930 strcpyW(newStreamProperty.name, pwcsName);
932 newStreamProperty.propertyType = PROPTYPE_STREAM;
933 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
934 newStreamProperty.size.s.LowPart = 0;
935 newStreamProperty.size.s.HighPart = 0;
937 newStreamProperty.previousProperty = PROPERTY_NULL;
938 newStreamProperty.nextProperty = PROPERTY_NULL;
939 newStreamProperty.dirProperty = PROPERTY_NULL;
941 /* call CoFileTime to get the current time
942 newStreamProperty.timeStampS1
943 newStreamProperty.timeStampD1
944 newStreamProperty.timeStampS2
945 newStreamProperty.timeStampD2
948 /* newStreamProperty.propertyUniqueID */
951 * Get a free property or create a new one
953 newPropertyIndex = getFreeProperty(This->ancestorStorage);
956 * Save the new property into the new property spot
958 StorageImpl_WriteProperty(
959 This->ancestorStorage,
960 newPropertyIndex,
961 &newStreamProperty);
964 * Find a spot in the property chain for our newly created property.
966 updatePropertyChain(
967 (StorageImpl*)This,
968 newPropertyIndex,
969 newStreamProperty);
972 * Open the stream to return it.
974 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
976 if (newStream != 0)
978 *ppstm = (IStream*)newStream;
981 * Since we are returning a pointer to the interface, we have to nail down
982 * the reference.
984 StgStreamImpl_AddRef(*ppstm);
986 else
988 return STG_E_INSUFFICIENTMEMORY;
991 return S_OK;
994 /************************************************************************
995 * Storage32BaseImpl_SetClass (IStorage)
997 * This method will write the specified CLSID in the property of this
998 * storage.
1000 * See Windows documentation for more details on IStorage methods.
1002 HRESULT WINAPI StorageBaseImpl_SetClass(
1003 IStorage* iface,
1004 REFCLSID clsid) /* [in] */
1006 ICOM_THIS(StorageBaseImpl,iface);
1007 HRESULT hRes = E_FAIL;
1008 StgProperty curProperty;
1009 BOOL success;
1011 TRACE("(%p, %p)\n", iface, clsid);
1013 success = StorageImpl_ReadProperty(This->ancestorStorage,
1014 This->rootPropertySetIndex,
1015 &curProperty);
1016 if (success)
1018 curProperty.propertyUniqueID = *clsid;
1020 success = StorageImpl_WriteProperty(This->ancestorStorage,
1021 This->rootPropertySetIndex,
1022 &curProperty);
1023 if (success)
1024 hRes = S_OK;
1027 return hRes;
1030 /************************************************************************
1031 ** Storage32Impl implementation
1034 /************************************************************************
1035 * Storage32Impl_CreateStorage (IStorage)
1037 * This method will create the storage object within the provided storage.
1039 * See Windows documentation for more details on IStorage methods.
1041 HRESULT WINAPI StorageImpl_CreateStorage(
1042 IStorage* iface,
1043 const OLECHAR *pwcsName, /* [string][in] */
1044 DWORD grfMode, /* [in] */
1045 DWORD reserved1, /* [in] */
1046 DWORD reserved2, /* [in] */
1047 IStorage **ppstg) /* [out] */
1049 StorageImpl* const This=(StorageImpl*)iface;
1051 IEnumSTATSTGImpl *propertyEnumeration;
1052 StgProperty currentProperty;
1053 StgProperty newProperty;
1054 ULONG foundPropertyIndex;
1055 ULONG newPropertyIndex;
1056 HRESULT hr;
1058 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1059 iface, debugstr_w(pwcsName), grfMode,
1060 reserved1, reserved2, ppstg);
1063 * Validate parameters
1065 if (ppstg == 0)
1066 return STG_E_INVALIDPOINTER;
1068 if (pwcsName == 0)
1069 return STG_E_INVALIDNAME;
1072 * Validate the STGM flags
1074 if ( FAILED( validateSTGM(grfMode) ) ||
1075 (grfMode & STGM_DELETEONRELEASE) )
1076 return STG_E_INVALIDFLAG;
1079 * Initialize the out parameter
1081 *ppstg = 0;
1084 * Create a property enumeration and search the properties
1086 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
1087 This->rootPropertySetIndex);
1089 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1090 pwcsName,
1091 &currentProperty);
1092 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1094 if (foundPropertyIndex != PROPERTY_NULL)
1097 * An element with this name already exists
1099 if (grfMode & STGM_CREATE)
1100 IStorage_DestroyElement(iface, pwcsName);
1101 else
1102 return STG_E_FILEALREADYEXISTS;
1106 * memset the empty property
1108 memset(&newProperty, 0, sizeof(StgProperty));
1110 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1112 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1113 return STG_E_INVALIDNAME;
1115 strcpyW(newProperty.name, pwcsName);
1117 newProperty.propertyType = PROPTYPE_STORAGE;
1118 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1119 newProperty.size.s.LowPart = 0;
1120 newProperty.size.s.HighPart = 0;
1122 newProperty.previousProperty = PROPERTY_NULL;
1123 newProperty.nextProperty = PROPERTY_NULL;
1124 newProperty.dirProperty = PROPERTY_NULL;
1126 /* call CoFileTime to get the current time
1127 newProperty.timeStampS1
1128 newProperty.timeStampD1
1129 newProperty.timeStampS2
1130 newProperty.timeStampD2
1133 /* newStorageProperty.propertyUniqueID */
1136 * Obtain a free property in the property chain
1138 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1141 * Save the new property into the new property spot
1143 StorageImpl_WriteProperty(
1144 This->ancestorStorage,
1145 newPropertyIndex,
1146 &newProperty);
1149 * Find a spot in the property chain for our newly created property.
1151 updatePropertyChain(
1152 This,
1153 newPropertyIndex,
1154 newProperty);
1157 * Open it to get a pointer to return.
1159 hr = IStorage_OpenStorage(
1160 iface,
1161 (OLECHAR*)pwcsName,
1163 grfMode,
1166 ppstg);
1168 if( (hr != S_OK) || (*ppstg == NULL))
1170 return hr;
1174 return S_OK;
1178 /***************************************************************************
1180 * Internal Method
1182 * Get a free property or create a new one.
1184 static ULONG getFreeProperty(
1185 StorageImpl *storage)
1187 ULONG currentPropertyIndex = 0;
1188 ULONG newPropertyIndex = PROPERTY_NULL;
1189 BOOL readSuccessful = TRUE;
1190 StgProperty currentProperty;
1195 * Start by reading the root property
1197 readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1198 currentPropertyIndex,
1199 &currentProperty);
1200 if (readSuccessful)
1202 if (currentProperty.sizeOfNameString == 0)
1205 * The property existis and is available, we found it.
1207 newPropertyIndex = currentPropertyIndex;
1210 else
1213 * We exhausted the property list, we will create more space below
1215 newPropertyIndex = currentPropertyIndex;
1217 currentPropertyIndex++;
1219 } while (newPropertyIndex == PROPERTY_NULL);
1222 * grow the property chain
1224 if (! readSuccessful)
1226 StgProperty emptyProperty;
1227 ULARGE_INTEGER newSize;
1228 ULONG propertyIndex;
1229 ULONG lastProperty = 0;
1230 ULONG blockCount = 0;
1233 * obtain the new count of property blocks
1235 blockCount = BlockChainStream_GetCount(
1236 storage->ancestorStorage->rootBlockChain)+1;
1239 * initialize the size used by the property stream
1241 newSize.s.HighPart = 0;
1242 newSize.s.LowPart = storage->bigBlockSize * blockCount;
1245 * add a property block to the property chain
1247 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1250 * memset the empty property in order to initialize the unused newly
1251 * created property
1253 memset(&emptyProperty, 0, sizeof(StgProperty));
1256 * initialize them
1258 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1260 for(
1261 propertyIndex = newPropertyIndex;
1262 propertyIndex < lastProperty;
1263 propertyIndex++)
1265 StorageImpl_WriteProperty(
1266 storage->ancestorStorage,
1267 propertyIndex,
1268 &emptyProperty);
1272 return newPropertyIndex;
1275 /****************************************************************************
1277 * Internal Method
1279 * Case insensitive comparaison of StgProperty.name by first considering
1280 * their size.
1282 * Returns <0 when newPrpoerty < currentProperty
1283 * >0 when newPrpoerty > currentProperty
1284 * 0 when newPrpoerty == currentProperty
1286 static LONG propertyNameCmp(
1287 OLECHAR *newProperty,
1288 OLECHAR *currentProperty)
1290 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1292 if (diff == 0)
1295 * We compare the string themselves only when they are of the same lenght
1297 diff = lstrcmpiW( newProperty, currentProperty);
1300 return diff;
1303 /****************************************************************************
1305 * Internal Method
1307 * Properly link this new element in the property chain.
1309 static void updatePropertyChain(
1310 StorageImpl *storage,
1311 ULONG newPropertyIndex,
1312 StgProperty newProperty)
1314 StgProperty currentProperty;
1317 * Read the root property
1319 StorageImpl_ReadProperty(storage->ancestorStorage,
1320 storage->rootPropertySetIndex,
1321 &currentProperty);
1323 if (currentProperty.dirProperty != PROPERTY_NULL)
1326 * The root storage contains some element, therefore, start the research
1327 * for the appropriate location.
1329 BOOL found = 0;
1330 ULONG current, next, previous, currentPropertyId;
1333 * Keep the StgProperty sequence number of the storage first property
1335 currentPropertyId = currentProperty.dirProperty;
1338 * Read
1340 StorageImpl_ReadProperty(storage->ancestorStorage,
1341 currentProperty.dirProperty,
1342 &currentProperty);
1344 previous = currentProperty.previousProperty;
1345 next = currentProperty.nextProperty;
1346 current = currentPropertyId;
1348 while (found == 0)
1350 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1352 if (diff < 0)
1354 if (previous != PROPERTY_NULL)
1356 StorageImpl_ReadProperty(storage->ancestorStorage,
1357 previous,
1358 &currentProperty);
1359 current = previous;
1361 else
1363 currentProperty.previousProperty = newPropertyIndex;
1364 StorageImpl_WriteProperty(storage->ancestorStorage,
1365 current,
1366 &currentProperty);
1367 found = 1;
1370 else if (diff > 0)
1372 if (next != PROPERTY_NULL)
1374 StorageImpl_ReadProperty(storage->ancestorStorage,
1375 next,
1376 &currentProperty);
1377 current = next;
1379 else
1381 currentProperty.nextProperty = newPropertyIndex;
1382 StorageImpl_WriteProperty(storage->ancestorStorage,
1383 current,
1384 &currentProperty);
1385 found = 1;
1388 else
1391 * Trying to insert an item with the same name in the
1392 * subtree structure.
1394 assert(FALSE);
1397 previous = currentProperty.previousProperty;
1398 next = currentProperty.nextProperty;
1401 else
1404 * The root storage is empty, link the new property to it's dir property
1406 currentProperty.dirProperty = newPropertyIndex;
1407 StorageImpl_WriteProperty(storage->ancestorStorage,
1408 storage->rootPropertySetIndex,
1409 &currentProperty);
1414 /*************************************************************************
1415 * CopyTo (IStorage)
1417 HRESULT WINAPI StorageImpl_CopyTo(
1418 IStorage* iface,
1419 DWORD ciidExclude, /* [in] */
1420 const IID* rgiidExclude, /* [size_is][unique][in] */
1421 SNB snbExclude, /* [unique][in] */
1422 IStorage* pstgDest) /* [unique][in] */
1424 IEnumSTATSTG *elements = 0;
1425 STATSTG curElement, strStat;
1426 HRESULT hr;
1427 IStorage *pstgTmp, *pstgChild;
1428 IStream *pstrTmp, *pstrChild;
1430 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1431 FIXME("Exclude option not implemented\n");
1433 TRACE("(%p, %ld, %p, %p, %p)\n",
1434 iface, ciidExclude, rgiidExclude,
1435 snbExclude, pstgDest);
1438 * Perform a sanity check
1440 if ( pstgDest == 0 )
1441 return STG_E_INVALIDPOINTER;
1444 * Enumerate the elements
1446 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1448 if ( hr != S_OK )
1449 return hr;
1452 * set the class ID
1454 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1455 IStorage_SetClass( pstgDest, &curElement.clsid );
1460 * Obtain the next element
1462 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1464 if ( hr == S_FALSE )
1466 hr = S_OK; /* done, every element has been copied */
1467 break;
1470 if (curElement.type == STGTY_STORAGE)
1473 * open child source storage
1475 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1476 STGM_READ|STGM_SHARE_EXCLUSIVE,
1477 NULL, 0, &pstgChild );
1479 if (hr != S_OK)
1480 break;
1483 * Check if destination storage is not a child of the source
1484 * storage, which will cause an infinite loop
1486 if (pstgChild == pstgDest)
1488 IEnumSTATSTG_Release(elements);
1490 return STG_E_ACCESSDENIED;
1494 * create a new storage in destination storage
1496 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1497 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1498 0, 0,
1499 &pstgTmp );
1501 * if it already exist, don't create a new one use this one
1503 if (hr == STG_E_FILEALREADYEXISTS)
1505 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1506 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1507 NULL, 0, &pstgTmp );
1510 if (hr != S_OK)
1511 break;
1515 * do the copy recursively
1517 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1518 snbExclude, pstgTmp );
1520 IStorage_Release( pstgTmp );
1521 IStorage_Release( pstgChild );
1523 else if (curElement.type == STGTY_STREAM)
1526 * create a new stream in destination storage. If the stream already
1527 * exist, it will be deleted and a new one will be created.
1529 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1530 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1531 0, 0, &pstrTmp );
1533 if (hr != S_OK)
1534 break;
1537 * open child stream storage
1539 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1540 STGM_READ|STGM_SHARE_EXCLUSIVE,
1541 0, &pstrChild );
1543 if (hr != S_OK)
1544 break;
1547 * Get the size of the source stream
1549 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1552 * Set the size of the destination stream.
1554 IStream_SetSize(pstrTmp, strStat.cbSize);
1557 * do the copy
1559 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1560 NULL, NULL );
1562 IStream_Release( pstrTmp );
1563 IStream_Release( pstrChild );
1565 else
1567 WARN("unknown element type: %ld\n", curElement.type);
1570 } while (hr == S_OK);
1573 * Clean-up
1575 IEnumSTATSTG_Release(elements);
1577 return hr;
1580 /*************************************************************************
1581 * MoveElementTo (IStorage)
1583 HRESULT WINAPI StorageImpl_MoveElementTo(
1584 IStorage* iface,
1585 const OLECHAR *pwcsName, /* [string][in] */
1586 IStorage *pstgDest, /* [unique][in] */
1587 const OLECHAR *pwcsNewName,/* [string][in] */
1588 DWORD grfFlags) /* [in] */
1590 FIXME("not implemented!\n");
1591 return E_NOTIMPL;
1594 /*************************************************************************
1595 * Commit (IStorage)
1597 HRESULT WINAPI StorageImpl_Commit(
1598 IStorage* iface,
1599 DWORD grfCommitFlags)/* [in] */
1601 FIXME("(%ld): stub!\n", grfCommitFlags);
1602 return S_OK;
1605 /*************************************************************************
1606 * Revert (IStorage)
1608 HRESULT WINAPI StorageImpl_Revert(
1609 IStorage* iface)
1611 FIXME("not implemented!\n");
1612 return E_NOTIMPL;
1615 /*************************************************************************
1616 * DestroyElement (IStorage)
1618 * Stategy: This implementation is build this way for simplicity not for speed.
1619 * I always delete the top most element of the enumeration and adjust
1620 * the deleted element pointer all the time. This takes longer to
1621 * do but allow to reinvoke DestroyElement whenever we encounter a
1622 * storage object. The optimisation reside in the usage of another
1623 * enumeration stategy that would give all the leaves of a storage
1624 * first. (postfix order)
1626 HRESULT WINAPI StorageImpl_DestroyElement(
1627 IStorage* iface,
1628 const OLECHAR *pwcsName)/* [string][in] */
1630 StorageImpl* const This=(StorageImpl*)iface;
1632 IEnumSTATSTGImpl* propertyEnumeration;
1633 HRESULT hr = S_OK;
1634 BOOL res;
1635 StgProperty propertyToDelete;
1636 StgProperty parentProperty;
1637 ULONG foundPropertyIndexToDelete;
1638 ULONG typeOfRelation;
1639 ULONG parentPropertyId;
1641 TRACE("(%p, %s)\n",
1642 iface, debugstr_w(pwcsName));
1645 * Perform a sanity check on the parameters.
1647 if (pwcsName==NULL)
1648 return STG_E_INVALIDPOINTER;
1651 * Create a property enumeration to search the property with the given name
1653 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1654 This->ancestorStorage,
1655 This->rootPropertySetIndex);
1657 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1658 propertyEnumeration,
1659 pwcsName,
1660 &propertyToDelete);
1662 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1664 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1666 return STG_E_FILENOTFOUND;
1670 * Find the parent property of the property to delete (the one that
1671 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1672 * the parent is This. Otherwise, the parent is one of it's sibling...
1676 * First, read This's StgProperty..
1678 res = StorageImpl_ReadProperty(
1679 This->ancestorStorage,
1680 This->rootPropertySetIndex,
1681 &parentProperty);
1683 assert(res==TRUE);
1686 * Second, check to see if by any chance the actual storage (This) is not
1687 * the parent of the property to delete... We never know...
1689 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1692 * Set data as it would have been done in the else part...
1694 typeOfRelation = PROPERTY_RELATION_DIR;
1695 parentPropertyId = This->rootPropertySetIndex;
1697 else
1700 * Create a property enumeration to search the parent properties, and
1701 * delete it once done.
1703 IEnumSTATSTGImpl* propertyEnumeration2;
1705 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1706 This->ancestorStorage,
1707 This->rootPropertySetIndex);
1709 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1710 propertyEnumeration2,
1711 foundPropertyIndexToDelete,
1712 &parentProperty,
1713 &parentPropertyId);
1715 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1718 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1720 hr = deleteStorageProperty(
1721 This,
1722 foundPropertyIndexToDelete,
1723 propertyToDelete);
1725 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1727 hr = deleteStreamProperty(
1728 This,
1729 foundPropertyIndexToDelete,
1730 propertyToDelete);
1733 if (hr!=S_OK)
1734 return hr;
1737 * Adjust the property chain
1739 hr = adjustPropertyChain(
1740 This,
1741 propertyToDelete,
1742 parentProperty,
1743 parentPropertyId,
1744 typeOfRelation);
1746 return hr;
1750 /************************************************************************
1751 * StorageImpl_Stat (IStorage)
1753 * This method will retrieve information about this storage object.
1755 * See Windows documentation for more details on IStorage methods.
1757 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1758 STATSTG* pstatstg, /* [out] */
1759 DWORD grfStatFlag) /* [in] */
1761 StorageImpl* const This = (StorageImpl*)iface;
1762 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1764 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1766 CoTaskMemFree(pstatstg->pwcsName);
1767 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1768 strcpyW(pstatstg->pwcsName, This->pwcsName);
1771 return result;
1776 /*********************************************************************
1778 * Internal Method
1780 * Perform the deletion of a complete storage node
1783 static HRESULT deleteStorageProperty(
1784 StorageImpl *parentStorage,
1785 ULONG indexOfPropertyToDelete,
1786 StgProperty propertyToDelete)
1788 IEnumSTATSTG *elements = 0;
1789 IStorage *childStorage = 0;
1790 STATSTG currentElement;
1791 HRESULT hr;
1792 HRESULT destroyHr = S_OK;
1795 * Open the storage and enumerate it
1797 hr = StorageBaseImpl_OpenStorage(
1798 (IStorage*)parentStorage,
1799 propertyToDelete.name,
1801 STGM_SHARE_EXCLUSIVE,
1804 &childStorage);
1806 if (hr != S_OK)
1808 return hr;
1812 * Enumerate the elements
1814 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1819 * Obtain the next element
1821 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1822 if (hr==S_OK)
1824 destroyHr = StorageImpl_DestroyElement(
1825 (IStorage*)childStorage,
1826 (OLECHAR*)currentElement.pwcsName);
1828 CoTaskMemFree(currentElement.pwcsName);
1832 * We need to Reset the enumeration every time because we delete elements
1833 * and the enumeration could be invalid
1835 IEnumSTATSTG_Reset(elements);
1837 } while ((hr == S_OK) && (destroyHr == S_OK));
1840 * Invalidate the property by zeroing it's name member.
1842 propertyToDelete.sizeOfNameString = 0;
1844 StorageImpl_WriteProperty(parentStorage->ancestorStorage,
1845 indexOfPropertyToDelete,
1846 &propertyToDelete);
1848 IStorage_Release(childStorage);
1849 IEnumSTATSTG_Release(elements);
1851 return destroyHr;
1854 /*********************************************************************
1856 * Internal Method
1858 * Perform the deletion of a stream node
1861 static HRESULT deleteStreamProperty(
1862 StorageImpl *parentStorage,
1863 ULONG indexOfPropertyToDelete,
1864 StgProperty propertyToDelete)
1866 IStream *pis;
1867 HRESULT hr;
1868 ULARGE_INTEGER size;
1870 size.s.HighPart = 0;
1871 size.s.LowPart = 0;
1873 hr = StorageBaseImpl_OpenStream(
1874 (IStorage*)parentStorage,
1875 (OLECHAR*)propertyToDelete.name,
1876 NULL,
1877 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1879 &pis);
1881 if (hr!=S_OK)
1883 return(hr);
1887 * Zap the stream
1889 hr = IStream_SetSize(pis, size);
1891 if(hr != S_OK)
1893 return hr;
1897 * Release the stream object.
1899 IStream_Release(pis);
1902 * Invalidate the property by zeroing it's name member.
1904 propertyToDelete.sizeOfNameString = 0;
1907 * Here we should re-read the property so we get the updated pointer
1908 * but since we are here to zap it, I don't do it...
1910 StorageImpl_WriteProperty(
1911 parentStorage->ancestorStorage,
1912 indexOfPropertyToDelete,
1913 &propertyToDelete);
1915 return S_OK;
1918 /*********************************************************************
1920 * Internal Method
1922 * Finds a placeholder for the StgProperty within the Storage
1925 static HRESULT findPlaceholder(
1926 StorageImpl *storage,
1927 ULONG propertyIndexToStore,
1928 ULONG storePropertyIndex,
1929 INT typeOfRelation)
1931 StgProperty storeProperty;
1932 HRESULT hr = S_OK;
1933 BOOL res = TRUE;
1936 * Read the storage property
1938 res = StorageImpl_ReadProperty(
1939 storage->ancestorStorage,
1940 storePropertyIndex,
1941 &storeProperty);
1943 if(! res)
1945 return E_FAIL;
1948 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1950 if (storeProperty.previousProperty != PROPERTY_NULL)
1952 return findPlaceholder(
1953 storage,
1954 propertyIndexToStore,
1955 storeProperty.previousProperty,
1956 typeOfRelation);
1958 else
1960 storeProperty.previousProperty = propertyIndexToStore;
1963 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1965 if (storeProperty.nextProperty != PROPERTY_NULL)
1967 return findPlaceholder(
1968 storage,
1969 propertyIndexToStore,
1970 storeProperty.nextProperty,
1971 typeOfRelation);
1973 else
1975 storeProperty.nextProperty = propertyIndexToStore;
1978 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1980 if (storeProperty.dirProperty != PROPERTY_NULL)
1982 return findPlaceholder(
1983 storage,
1984 propertyIndexToStore,
1985 storeProperty.dirProperty,
1986 typeOfRelation);
1988 else
1990 storeProperty.dirProperty = propertyIndexToStore;
1994 hr = StorageImpl_WriteProperty(
1995 storage->ancestorStorage,
1996 storePropertyIndex,
1997 &storeProperty);
1999 if(! hr)
2001 return E_FAIL;
2004 return S_OK;
2007 /*************************************************************************
2009 * Internal Method
2011 * This method takes the previous and the next property link of a property
2012 * to be deleted and find them a place in the Storage.
2014 static HRESULT adjustPropertyChain(
2015 StorageImpl *This,
2016 StgProperty propertyToDelete,
2017 StgProperty parentProperty,
2018 ULONG parentPropertyId,
2019 INT typeOfRelation)
2021 ULONG newLinkProperty = PROPERTY_NULL;
2022 BOOL needToFindAPlaceholder = FALSE;
2023 ULONG storeNode = PROPERTY_NULL;
2024 ULONG toStoreNode = PROPERTY_NULL;
2025 INT relationType = 0;
2026 HRESULT hr = S_OK;
2027 BOOL res = TRUE;
2029 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2031 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2034 * Set the parent previous to the property to delete previous
2036 newLinkProperty = propertyToDelete.previousProperty;
2038 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2041 * We also need to find a storage for the other link, setup variables
2042 * to do this at the end...
2044 needToFindAPlaceholder = TRUE;
2045 storeNode = propertyToDelete.previousProperty;
2046 toStoreNode = propertyToDelete.nextProperty;
2047 relationType = PROPERTY_RELATION_NEXT;
2050 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2053 * Set the parent previous to the property to delete next
2055 newLinkProperty = propertyToDelete.nextProperty;
2059 * Link it for real...
2061 parentProperty.previousProperty = newLinkProperty;
2064 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2066 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2069 * Set the parent next to the property to delete next previous
2071 newLinkProperty = propertyToDelete.previousProperty;
2073 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2076 * We also need to find a storage for the other link, setup variables
2077 * to do this at the end...
2079 needToFindAPlaceholder = TRUE;
2080 storeNode = propertyToDelete.previousProperty;
2081 toStoreNode = propertyToDelete.nextProperty;
2082 relationType = PROPERTY_RELATION_NEXT;
2085 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2088 * Set the parent next to the property to delete next
2090 newLinkProperty = propertyToDelete.nextProperty;
2094 * Link it for real...
2096 parentProperty.nextProperty = newLinkProperty;
2098 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2100 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2103 * Set the parent dir to the property to delete previous
2105 newLinkProperty = propertyToDelete.previousProperty;
2107 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2110 * We also need to find a storage for the other link, setup variables
2111 * to do this at the end...
2113 needToFindAPlaceholder = TRUE;
2114 storeNode = propertyToDelete.previousProperty;
2115 toStoreNode = propertyToDelete.nextProperty;
2116 relationType = PROPERTY_RELATION_NEXT;
2119 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2122 * Set the parent dir to the property to delete next
2124 newLinkProperty = propertyToDelete.nextProperty;
2128 * Link it for real...
2130 parentProperty.dirProperty = newLinkProperty;
2134 * Write back the parent property
2136 res = StorageImpl_WriteProperty(
2137 This->ancestorStorage,
2138 parentPropertyId,
2139 &parentProperty);
2140 if(! res)
2142 return E_FAIL;
2146 * If a placeholder is required for the other link, then, find one and
2147 * get out of here...
2149 if (needToFindAPlaceholder)
2151 hr = findPlaceholder(
2152 This,
2153 toStoreNode,
2154 storeNode,
2155 relationType);
2158 return hr;
2162 /******************************************************************************
2163 * SetElementTimes (IStorage)
2165 HRESULT WINAPI StorageImpl_SetElementTimes(
2166 IStorage* iface,
2167 const OLECHAR *pwcsName,/* [string][in] */
2168 const FILETIME *pctime, /* [in] */
2169 const FILETIME *patime, /* [in] */
2170 const FILETIME *pmtime) /* [in] */
2172 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2173 return S_OK;
2176 /******************************************************************************
2177 * SetStateBits (IStorage)
2179 HRESULT WINAPI StorageImpl_SetStateBits(
2180 IStorage* iface,
2181 DWORD grfStateBits,/* [in] */
2182 DWORD grfMask) /* [in] */
2184 FIXME("not implemented!\n");
2185 return E_NOTIMPL;
2188 HRESULT StorageImpl_Construct(
2189 StorageImpl* This,
2190 HANDLE hFile,
2191 LPCOLESTR pwcsName,
2192 ILockBytes* pLkbyt,
2193 DWORD openFlags,
2194 BOOL fileBased,
2195 BOOL fileCreate)
2197 HRESULT hr = S_OK;
2198 StgProperty currentProperty;
2199 BOOL readSuccessful;
2200 ULONG currentPropertyIndex;
2202 if ( FAILED( validateSTGM(openFlags) ))
2203 return STG_E_INVALIDFLAG;
2205 memset(This, 0, sizeof(StorageImpl));
2208 * Initialize the virtual function table.
2210 ICOM_VTBL(This) = &Storage32Impl_Vtbl;
2211 This->v_destructor = &StorageImpl_Destroy;
2214 * This is the top-level storage so initialize the ancestor pointer
2215 * to this.
2217 This->ancestorStorage = This;
2220 * Initialize the physical support of the storage.
2222 This->hFile = hFile;
2225 * Store copy of file path.
2227 if(pwcsName) {
2228 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2229 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2230 if (!This->pwcsName)
2231 return STG_E_INSUFFICIENTMEMORY;
2232 strcpyW(This->pwcsName, pwcsName);
2236 * Initialize the big block cache.
2238 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2239 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2240 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2241 pLkbyt,
2242 openFlags,
2243 This->bigBlockSize,
2244 fileBased);
2246 if (This->bigBlockFile == 0)
2247 return E_FAIL;
2249 if (fileCreate)
2251 ULARGE_INTEGER size;
2252 BYTE* bigBlockBuffer;
2255 * Initialize all header variables:
2256 * - The big block depot consists of one block and it is at block 0
2257 * - The properties start at block 1
2258 * - There is no small block depot
2260 memset( This->bigBlockDepotStart,
2261 BLOCK_UNUSED,
2262 sizeof(This->bigBlockDepotStart));
2264 This->bigBlockDepotCount = 1;
2265 This->bigBlockDepotStart[0] = 0;
2266 This->rootStartBlock = 1;
2267 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2268 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2269 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2270 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2271 This->extBigBlockDepotCount = 0;
2273 StorageImpl_SaveFileHeader(This);
2276 * Add one block for the big block depot and one block for the properties
2278 size.s.HighPart = 0;
2279 size.s.LowPart = This->bigBlockSize * 3;
2280 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2283 * Initialize the big block depot
2285 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2286 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2287 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2288 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2289 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2291 else
2294 * Load the header for the file.
2296 hr = StorageImpl_LoadFileHeader(This);
2298 if (FAILED(hr))
2300 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2302 return hr;
2307 * There is no block depot cached yet.
2309 This->indexBlockDepotCached = 0xFFFFFFFF;
2312 * Start searching for free blocks with block 0.
2314 This->prevFreeBlock = 0;
2317 * Create the block chain abstractions.
2319 if(!(This->rootBlockChain =
2320 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2321 return STG_E_READFAULT;
2323 if(!(This->smallBlockDepotChain =
2324 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2325 PROPERTY_NULL)))
2326 return STG_E_READFAULT;
2329 * Write the root property
2331 if (fileCreate)
2333 StgProperty rootProp;
2335 * Initialize the property chain
2337 memset(&rootProp, 0, sizeof(rootProp));
2338 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2339 sizeof(rootProp.name)/sizeof(WCHAR) );
2340 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2341 rootProp.propertyType = PROPTYPE_ROOT;
2342 rootProp.previousProperty = PROPERTY_NULL;
2343 rootProp.nextProperty = PROPERTY_NULL;
2344 rootProp.dirProperty = PROPERTY_NULL;
2345 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2346 rootProp.size.s.HighPart = 0;
2347 rootProp.size.s.LowPart = 0;
2349 StorageImpl_WriteProperty(This, 0, &rootProp);
2353 * Find the ID of the root in the property sets.
2355 currentPropertyIndex = 0;
2359 readSuccessful = StorageImpl_ReadProperty(
2360 This,
2361 currentPropertyIndex,
2362 &currentProperty);
2364 if (readSuccessful)
2366 if ( (currentProperty.sizeOfNameString != 0 ) &&
2367 (currentProperty.propertyType == PROPTYPE_ROOT) )
2369 This->rootPropertySetIndex = currentPropertyIndex;
2373 currentPropertyIndex++;
2375 } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2377 if (!readSuccessful)
2379 /* TODO CLEANUP */
2380 return STG_E_READFAULT;
2384 * Create the block chain abstraction for the small block root chain.
2386 if(!(This->smallBlockRootChain =
2387 BlockChainStream_Construct(This, NULL, This->rootPropertySetIndex)))
2388 return STG_E_READFAULT;
2390 return hr;
2393 void StorageImpl_Destroy(
2394 StorageImpl* This)
2396 TRACE("(%p)\n", This);
2398 if(This->pwcsName)
2399 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2401 BlockChainStream_Destroy(This->smallBlockRootChain);
2402 BlockChainStream_Destroy(This->rootBlockChain);
2403 BlockChainStream_Destroy(This->smallBlockDepotChain);
2405 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2406 return;
2409 /******************************************************************************
2410 * Storage32Impl_GetNextFreeBigBlock
2412 * Returns the index of the next free big block.
2413 * If the big block depot is filled, this method will enlarge it.
2416 ULONG StorageImpl_GetNextFreeBigBlock(
2417 StorageImpl* This)
2419 ULONG depotBlockIndexPos;
2420 void *depotBuffer;
2421 ULONG depotBlockOffset;
2422 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2423 ULONG nextBlockIndex = BLOCK_SPECIAL;
2424 int depotIndex = 0;
2425 ULONG freeBlock = BLOCK_UNUSED;
2427 depotIndex = This->prevFreeBlock / blocksPerDepot;
2428 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2431 * Scan the entire big block depot until we find a block marked free
2433 while (nextBlockIndex != BLOCK_UNUSED)
2435 if (depotIndex < COUNT_BBDEPOTINHEADER)
2437 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2440 * Grow the primary depot.
2442 if (depotBlockIndexPos == BLOCK_UNUSED)
2444 depotBlockIndexPos = depotIndex*blocksPerDepot;
2447 * Add a block depot.
2449 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2450 This->bigBlockDepotCount++;
2451 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2454 * Flag it as a block depot.
2456 StorageImpl_SetNextBlockInChain(This,
2457 depotBlockIndexPos,
2458 BLOCK_SPECIAL);
2460 /* Save new header information.
2462 StorageImpl_SaveFileHeader(This);
2465 else
2467 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2469 if (depotBlockIndexPos == BLOCK_UNUSED)
2472 * Grow the extended depot.
2474 ULONG extIndex = BLOCK_UNUSED;
2475 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2476 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2478 if (extBlockOffset == 0)
2480 /* We need an extended block.
2482 extIndex = Storage32Impl_AddExtBlockDepot(This);
2483 This->extBigBlockDepotCount++;
2484 depotBlockIndexPos = extIndex + 1;
2486 else
2487 depotBlockIndexPos = depotIndex * blocksPerDepot;
2490 * Add a block depot and mark it in the extended block.
2492 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2493 This->bigBlockDepotCount++;
2494 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2496 /* Flag the block depot.
2498 StorageImpl_SetNextBlockInChain(This,
2499 depotBlockIndexPos,
2500 BLOCK_SPECIAL);
2502 /* If necessary, flag the extended depot block.
2504 if (extIndex != BLOCK_UNUSED)
2505 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2507 /* Save header information.
2509 StorageImpl_SaveFileHeader(This);
2513 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2515 if (depotBuffer != 0)
2517 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2518 ( nextBlockIndex != BLOCK_UNUSED))
2520 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2522 if (nextBlockIndex == BLOCK_UNUSED)
2524 freeBlock = (depotIndex * blocksPerDepot) +
2525 (depotBlockOffset/sizeof(ULONG));
2528 depotBlockOffset += sizeof(ULONG);
2531 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2534 depotIndex++;
2535 depotBlockOffset = 0;
2538 This->prevFreeBlock = freeBlock;
2540 return freeBlock;
2543 /******************************************************************************
2544 * Storage32Impl_AddBlockDepot
2546 * This will create a depot block, essentially it is a block initialized
2547 * to BLOCK_UNUSEDs.
2549 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2551 BYTE* blockBuffer;
2553 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2556 * Initialize blocks as free
2558 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2560 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2563 /******************************************************************************
2564 * Storage32Impl_GetExtDepotBlock
2566 * Returns the index of the block that corresponds to the specified depot
2567 * index. This method is only for depot indexes equal or greater than
2568 * COUNT_BBDEPOTINHEADER.
2570 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2572 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2573 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2574 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2575 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2576 ULONG blockIndex = BLOCK_UNUSED;
2577 ULONG extBlockIndex = This->extBigBlockDepotStart;
2579 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2581 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2582 return BLOCK_UNUSED;
2584 while (extBlockCount > 0)
2586 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2587 extBlockCount--;
2590 if (extBlockIndex != BLOCK_UNUSED)
2592 BYTE* depotBuffer;
2594 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2596 if (depotBuffer != 0)
2598 StorageUtl_ReadDWord(depotBuffer,
2599 extBlockOffset * sizeof(ULONG),
2600 &blockIndex);
2602 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2606 return blockIndex;
2609 /******************************************************************************
2610 * Storage32Impl_SetExtDepotBlock
2612 * Associates the specified block index to the specified depot index.
2613 * This method is only for depot indexes equal or greater than
2614 * COUNT_BBDEPOTINHEADER.
2616 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2617 ULONG depotIndex,
2618 ULONG blockIndex)
2620 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2621 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2622 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2623 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2624 ULONG extBlockIndex = This->extBigBlockDepotStart;
2626 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2628 while (extBlockCount > 0)
2630 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2631 extBlockCount--;
2634 if (extBlockIndex != BLOCK_UNUSED)
2636 BYTE* depotBuffer;
2638 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2640 if (depotBuffer != 0)
2642 StorageUtl_WriteDWord(depotBuffer,
2643 extBlockOffset * sizeof(ULONG),
2644 blockIndex);
2646 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2651 /******************************************************************************
2652 * Storage32Impl_AddExtBlockDepot
2654 * Creates an extended depot block.
2656 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2658 ULONG numExtBlocks = This->extBigBlockDepotCount;
2659 ULONG nextExtBlock = This->extBigBlockDepotStart;
2660 BYTE* depotBuffer = NULL;
2661 ULONG index = BLOCK_UNUSED;
2662 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2663 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2664 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2666 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2667 blocksPerDepotBlock;
2669 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2672 * The first extended block.
2674 This->extBigBlockDepotStart = index;
2676 else
2678 int i;
2680 * Follow the chain to the last one.
2682 for (i = 0; i < (numExtBlocks - 1); i++)
2684 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2688 * Add the new extended block to the chain.
2690 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2691 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2692 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2696 * Initialize this block.
2698 depotBuffer = StorageImpl_GetBigBlock(This, index);
2699 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2700 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2702 return index;
2705 /******************************************************************************
2706 * Storage32Impl_FreeBigBlock
2708 * This method will flag the specified block as free in the big block depot.
2710 void StorageImpl_FreeBigBlock(
2711 StorageImpl* This,
2712 ULONG blockIndex)
2714 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2716 if (blockIndex < This->prevFreeBlock)
2717 This->prevFreeBlock = blockIndex;
2720 /************************************************************************
2721 * Storage32Impl_GetNextBlockInChain
2723 * This method will retrieve the block index of the next big block in
2724 * in the chain.
2726 * Params: This - Pointer to the Storage object.
2727 * blockIndex - Index of the block to retrieve the chain
2728 * for.
2729 * nextBlockIndex - receives the return value.
2731 * Returns: This method returns the index of the next block in the chain.
2732 * It will return the constants:
2733 * BLOCK_SPECIAL - If the block given was not part of a
2734 * chain.
2735 * BLOCK_END_OF_CHAIN - If the block given was the last in
2736 * a chain.
2737 * BLOCK_UNUSED - If the block given was not past of a chain
2738 * and is available.
2739 * BLOCK_EXTBBDEPOT - This block is part of the extended
2740 * big block depot.
2742 * See Windows documentation for more details on IStorage methods.
2744 HRESULT StorageImpl_GetNextBlockInChain(
2745 StorageImpl* This,
2746 ULONG blockIndex,
2747 ULONG* nextBlockIndex)
2749 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2750 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2751 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2752 void* depotBuffer;
2753 ULONG depotBlockIndexPos;
2754 int index;
2756 *nextBlockIndex = BLOCK_SPECIAL;
2758 if(depotBlockCount >= This->bigBlockDepotCount)
2760 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2761 This->bigBlockDepotCount);
2762 return STG_E_READFAULT;
2766 * Cache the currently accessed depot block.
2768 if (depotBlockCount != This->indexBlockDepotCached)
2770 This->indexBlockDepotCached = depotBlockCount;
2772 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2774 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2776 else
2779 * We have to look in the extended depot.
2781 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2784 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2786 if (!depotBuffer)
2787 return STG_E_READFAULT;
2789 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2791 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2792 This->blockDepotCached[index] = *nextBlockIndex;
2794 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2797 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2799 return S_OK;
2802 /******************************************************************************
2803 * Storage32Impl_GetNextExtendedBlock
2805 * Given an extended block this method will return the next extended block.
2807 * NOTES:
2808 * The last ULONG of an extended block is the block index of the next
2809 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2810 * depot.
2812 * Return values:
2813 * - The index of the next extended block
2814 * - BLOCK_UNUSED: there is no next extended block.
2815 * - Any other return values denotes failure.
2817 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2819 ULONG nextBlockIndex = BLOCK_SPECIAL;
2820 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2821 void* depotBuffer;
2823 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2825 if (depotBuffer!=0)
2827 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2829 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2832 return nextBlockIndex;
2835 /******************************************************************************
2836 * Storage32Impl_SetNextBlockInChain
2838 * This method will write the index of the specified block's next block
2839 * in the big block depot.
2841 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2842 * do the following
2844 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2845 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2846 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2849 void StorageImpl_SetNextBlockInChain(
2850 StorageImpl* This,
2851 ULONG blockIndex,
2852 ULONG nextBlock)
2854 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2855 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2856 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2857 ULONG depotBlockIndexPos;
2858 void* depotBuffer;
2860 assert(depotBlockCount < This->bigBlockDepotCount);
2861 assert(blockIndex != nextBlock);
2863 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2865 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2867 else
2870 * We have to look in the extended depot.
2872 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2875 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2877 if (depotBuffer!=0)
2879 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2880 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2884 * Update the cached block depot, if necessary.
2886 if (depotBlockCount == This->indexBlockDepotCached)
2888 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2892 /******************************************************************************
2893 * Storage32Impl_LoadFileHeader
2895 * This method will read in the file header, i.e. big block index -1.
2897 HRESULT StorageImpl_LoadFileHeader(
2898 StorageImpl* This)
2900 HRESULT hr = STG_E_FILENOTFOUND;
2901 void* headerBigBlock = NULL;
2902 int index;
2905 * Get a pointer to the big block of data containing the header.
2907 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2910 * Extract the information from the header.
2912 if (headerBigBlock!=0)
2915 * Check for the "magic number" signature and return an error if it is not
2916 * found.
2918 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2920 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2921 return STG_E_OLDFORMAT;
2924 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2926 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2927 return STG_E_INVALIDHEADER;
2930 StorageUtl_ReadWord(
2931 headerBigBlock,
2932 OFFSET_BIGBLOCKSIZEBITS,
2933 &This->bigBlockSizeBits);
2935 StorageUtl_ReadWord(
2936 headerBigBlock,
2937 OFFSET_SMALLBLOCKSIZEBITS,
2938 &This->smallBlockSizeBits);
2940 StorageUtl_ReadDWord(
2941 headerBigBlock,
2942 OFFSET_BBDEPOTCOUNT,
2943 &This->bigBlockDepotCount);
2945 StorageUtl_ReadDWord(
2946 headerBigBlock,
2947 OFFSET_ROOTSTARTBLOCK,
2948 &This->rootStartBlock);
2950 StorageUtl_ReadDWord(
2951 headerBigBlock,
2952 OFFSET_SBDEPOTSTART,
2953 &This->smallBlockDepotStart);
2955 StorageUtl_ReadDWord(
2956 headerBigBlock,
2957 OFFSET_EXTBBDEPOTSTART,
2958 &This->extBigBlockDepotStart);
2960 StorageUtl_ReadDWord(
2961 headerBigBlock,
2962 OFFSET_EXTBBDEPOTCOUNT,
2963 &This->extBigBlockDepotCount);
2965 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2967 StorageUtl_ReadDWord(
2968 headerBigBlock,
2969 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2970 &(This->bigBlockDepotStart[index]));
2974 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2976 if ((1 << 2) == 4)
2978 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2979 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2981 else
2983 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2984 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2988 * Right now, the code is making some assumptions about the size of the
2989 * blocks, just make sure they are what we're expecting.
2991 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
2992 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
2994 WARN("Broken OLE storage file\n");
2995 hr = STG_E_INVALIDHEADER;
2997 else
2998 hr = S_OK;
3001 * Release the block.
3003 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3006 return hr;
3009 /******************************************************************************
3010 * Storage32Impl_SaveFileHeader
3012 * This method will save to the file the header, i.e. big block -1.
3014 void StorageImpl_SaveFileHeader(
3015 StorageImpl* This)
3017 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3018 int index;
3019 BOOL success;
3022 * Get a pointer to the big block of data containing the header.
3024 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3027 * If the block read failed, the file is probably new.
3029 if (!success)
3032 * Initialize for all unknown fields.
3034 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3037 * Initialize the magic number.
3039 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3042 * And a bunch of things we don't know what they mean
3044 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3045 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3046 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3047 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3051 * Write the information to the header.
3053 StorageUtl_WriteWord(
3054 headerBigBlock,
3055 OFFSET_BIGBLOCKSIZEBITS,
3056 This->bigBlockSizeBits);
3058 StorageUtl_WriteWord(
3059 headerBigBlock,
3060 OFFSET_SMALLBLOCKSIZEBITS,
3061 This->smallBlockSizeBits);
3063 StorageUtl_WriteDWord(
3064 headerBigBlock,
3065 OFFSET_BBDEPOTCOUNT,
3066 This->bigBlockDepotCount);
3068 StorageUtl_WriteDWord(
3069 headerBigBlock,
3070 OFFSET_ROOTSTARTBLOCK,
3071 This->rootStartBlock);
3073 StorageUtl_WriteDWord(
3074 headerBigBlock,
3075 OFFSET_SBDEPOTSTART,
3076 This->smallBlockDepotStart);
3078 StorageUtl_WriteDWord(
3079 headerBigBlock,
3080 OFFSET_SBDEPOTCOUNT,
3081 This->smallBlockDepotChain ?
3082 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3084 StorageUtl_WriteDWord(
3085 headerBigBlock,
3086 OFFSET_EXTBBDEPOTSTART,
3087 This->extBigBlockDepotStart);
3089 StorageUtl_WriteDWord(
3090 headerBigBlock,
3091 OFFSET_EXTBBDEPOTCOUNT,
3092 This->extBigBlockDepotCount);
3094 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3096 StorageUtl_WriteDWord(
3097 headerBigBlock,
3098 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3099 (This->bigBlockDepotStart[index]));
3103 * Write the big block back to the file.
3105 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3108 /******************************************************************************
3109 * Storage32Impl_ReadProperty
3111 * This method will read the specified property from the property chain.
3113 BOOL StorageImpl_ReadProperty(
3114 StorageImpl* This,
3115 ULONG index,
3116 StgProperty* buffer)
3118 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3119 ULARGE_INTEGER offsetInPropSet;
3120 BOOL readSuccessful;
3121 ULONG bytesRead;
3123 offsetInPropSet.s.HighPart = 0;
3124 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3126 readSuccessful = BlockChainStream_ReadAt(
3127 This->rootBlockChain,
3128 offsetInPropSet,
3129 PROPSET_BLOCK_SIZE,
3130 currentProperty,
3131 &bytesRead);
3133 if (readSuccessful)
3135 /* replace the name of root entry (often "Root Entry") by the file name */
3136 WCHAR *propName = (index == This->rootPropertySetIndex) ?
3137 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3139 memset(buffer->name, 0, sizeof(buffer->name));
3140 memcpy(
3141 buffer->name,
3142 propName,
3143 PROPERTY_NAME_BUFFER_LEN );
3144 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3146 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3148 StorageUtl_ReadWord(
3149 currentProperty,
3150 OFFSET_PS_NAMELENGTH,
3151 &buffer->sizeOfNameString);
3153 StorageUtl_ReadDWord(
3154 currentProperty,
3155 OFFSET_PS_PREVIOUSPROP,
3156 &buffer->previousProperty);
3158 StorageUtl_ReadDWord(
3159 currentProperty,
3160 OFFSET_PS_NEXTPROP,
3161 &buffer->nextProperty);
3163 StorageUtl_ReadDWord(
3164 currentProperty,
3165 OFFSET_PS_DIRPROP,
3166 &buffer->dirProperty);
3168 StorageUtl_ReadGUID(
3169 currentProperty,
3170 OFFSET_PS_GUID,
3171 &buffer->propertyUniqueID);
3173 StorageUtl_ReadDWord(
3174 currentProperty,
3175 OFFSET_PS_TSS1,
3176 &buffer->timeStampS1);
3178 StorageUtl_ReadDWord(
3179 currentProperty,
3180 OFFSET_PS_TSD1,
3181 &buffer->timeStampD1);
3183 StorageUtl_ReadDWord(
3184 currentProperty,
3185 OFFSET_PS_TSS2,
3186 &buffer->timeStampS2);
3188 StorageUtl_ReadDWord(
3189 currentProperty,
3190 OFFSET_PS_TSD2,
3191 &buffer->timeStampD2);
3193 StorageUtl_ReadDWord(
3194 currentProperty,
3195 OFFSET_PS_STARTBLOCK,
3196 &buffer->startingBlock);
3198 StorageUtl_ReadDWord(
3199 currentProperty,
3200 OFFSET_PS_SIZE,
3201 &buffer->size.s.LowPart);
3203 buffer->size.s.HighPart = 0;
3206 return readSuccessful;
3209 /*********************************************************************
3210 * Write the specified property into the property chain
3212 BOOL StorageImpl_WriteProperty(
3213 StorageImpl* This,
3214 ULONG index,
3215 StgProperty* buffer)
3217 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3218 ULARGE_INTEGER offsetInPropSet;
3219 BOOL writeSuccessful;
3220 ULONG bytesWritten;
3222 offsetInPropSet.s.HighPart = 0;
3223 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3225 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3227 memcpy(
3228 currentProperty + OFFSET_PS_NAME,
3229 buffer->name,
3230 PROPERTY_NAME_BUFFER_LEN );
3232 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3234 StorageUtl_WriteWord(
3235 currentProperty,
3236 OFFSET_PS_NAMELENGTH,
3237 buffer->sizeOfNameString);
3239 StorageUtl_WriteDWord(
3240 currentProperty,
3241 OFFSET_PS_PREVIOUSPROP,
3242 buffer->previousProperty);
3244 StorageUtl_WriteDWord(
3245 currentProperty,
3246 OFFSET_PS_NEXTPROP,
3247 buffer->nextProperty);
3249 StorageUtl_WriteDWord(
3250 currentProperty,
3251 OFFSET_PS_DIRPROP,
3252 buffer->dirProperty);
3254 StorageUtl_WriteGUID(
3255 currentProperty,
3256 OFFSET_PS_GUID,
3257 &buffer->propertyUniqueID);
3259 StorageUtl_WriteDWord(
3260 currentProperty,
3261 OFFSET_PS_TSS1,
3262 buffer->timeStampS1);
3264 StorageUtl_WriteDWord(
3265 currentProperty,
3266 OFFSET_PS_TSD1,
3267 buffer->timeStampD1);
3269 StorageUtl_WriteDWord(
3270 currentProperty,
3271 OFFSET_PS_TSS2,
3272 buffer->timeStampS2);
3274 StorageUtl_WriteDWord(
3275 currentProperty,
3276 OFFSET_PS_TSD2,
3277 buffer->timeStampD2);
3279 StorageUtl_WriteDWord(
3280 currentProperty,
3281 OFFSET_PS_STARTBLOCK,
3282 buffer->startingBlock);
3284 StorageUtl_WriteDWord(
3285 currentProperty,
3286 OFFSET_PS_SIZE,
3287 buffer->size.s.LowPart);
3289 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3290 offsetInPropSet,
3291 PROPSET_BLOCK_SIZE,
3292 currentProperty,
3293 &bytesWritten);
3294 return writeSuccessful;
3297 BOOL StorageImpl_ReadBigBlock(
3298 StorageImpl* This,
3299 ULONG blockIndex,
3300 void* buffer)
3302 void* bigBlockBuffer;
3304 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3306 if (bigBlockBuffer!=0)
3308 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3310 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3312 return TRUE;
3315 return FALSE;
3318 BOOL StorageImpl_WriteBigBlock(
3319 StorageImpl* This,
3320 ULONG blockIndex,
3321 void* buffer)
3323 void* bigBlockBuffer;
3325 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3327 if (bigBlockBuffer!=0)
3329 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3331 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3333 return TRUE;
3336 return FALSE;
3339 void* StorageImpl_GetROBigBlock(
3340 StorageImpl* This,
3341 ULONG blockIndex)
3343 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3346 void* StorageImpl_GetBigBlock(
3347 StorageImpl* This,
3348 ULONG blockIndex)
3350 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3353 void StorageImpl_ReleaseBigBlock(
3354 StorageImpl* This,
3355 void* pBigBlock)
3357 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3360 /******************************************************************************
3361 * Storage32Impl_SmallBlocksToBigBlocks
3363 * This method will convert a small block chain to a big block chain.
3364 * The small block chain will be destroyed.
3366 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3367 StorageImpl* This,
3368 SmallBlockChainStream** ppsbChain)
3370 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3371 ULARGE_INTEGER size, offset;
3372 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3373 ULONG propertyIndex;
3374 BOOL successRead, successWrite;
3375 StgProperty chainProperty;
3376 BYTE *buffer;
3377 BlockChainStream *bbTempChain = NULL;
3378 BlockChainStream *bigBlockChain = NULL;
3381 * Create a temporary big block chain that doesn't have
3382 * an associated property. This temporary chain will be
3383 * used to copy data from small blocks to big blocks.
3385 bbTempChain = BlockChainStream_Construct(This,
3386 &bbHeadOfChain,
3387 PROPERTY_NULL);
3388 if(!bbTempChain) return NULL;
3390 * Grow the big block chain.
3392 size = SmallBlockChainStream_GetSize(*ppsbChain);
3393 BlockChainStream_SetSize(bbTempChain, size);
3396 * Copy the contents of the small block chain to the big block chain
3397 * by small block size increments.
3399 offset.s.LowPart = 0;
3400 offset.s.HighPart = 0;
3401 cbTotalRead = 0;
3402 cbTotalWritten = 0;
3404 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3407 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3408 offset,
3409 DEF_SMALL_BLOCK_SIZE,
3410 buffer,
3411 &cbRead);
3412 cbTotalRead += cbRead;
3414 successWrite = BlockChainStream_WriteAt(bbTempChain,
3415 offset,
3416 cbRead,
3417 buffer,
3418 &cbWritten);
3419 cbTotalWritten += cbWritten;
3421 offset.s.LowPart += This->smallBlockSize;
3423 } while (successRead && successWrite);
3424 HeapFree(GetProcessHeap(),0,buffer);
3426 assert(cbTotalRead == cbTotalWritten);
3429 * Destroy the small block chain.
3431 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3432 size.s.HighPart = 0;
3433 size.s.LowPart = 0;
3434 SmallBlockChainStream_SetSize(*ppsbChain, size);
3435 SmallBlockChainStream_Destroy(*ppsbChain);
3436 *ppsbChain = 0;
3439 * Change the property information. This chain is now a big block chain
3440 * and it doesn't reside in the small blocks chain anymore.
3442 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3444 chainProperty.startingBlock = bbHeadOfChain;
3446 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3449 * Destroy the temporary propertyless big block chain.
3450 * Create a new big block chain associated with this property.
3452 BlockChainStream_Destroy(bbTempChain);
3453 bigBlockChain = BlockChainStream_Construct(This,
3454 NULL,
3455 propertyIndex);
3457 return bigBlockChain;
3460 /******************************************************************************
3461 ** Storage32InternalImpl implementation
3464 StorageInternalImpl* StorageInternalImpl_Construct(
3465 StorageImpl* ancestorStorage,
3466 ULONG rootPropertyIndex)
3468 StorageInternalImpl* newStorage;
3471 * Allocate space for the new storage object
3473 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3475 if (newStorage!=0)
3477 memset(newStorage, 0, sizeof(StorageInternalImpl));
3480 * Initialize the virtual function table.
3482 ICOM_VTBL(newStorage) = &Storage32InternalImpl_Vtbl;
3483 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3486 * Keep the ancestor storage pointer and nail a reference to it.
3488 newStorage->ancestorStorage = ancestorStorage;
3489 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3492 * Keep the index of the root property set for this storage,
3494 newStorage->rootPropertySetIndex = rootPropertyIndex;
3496 return newStorage;
3499 return 0;
3502 void StorageInternalImpl_Destroy(
3503 StorageInternalImpl* This)
3505 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3506 HeapFree(GetProcessHeap(), 0, This);
3509 /******************************************************************************
3511 ** Storage32InternalImpl_Commit
3513 ** The non-root storages cannot be opened in transacted mode thus this function
3514 ** does nothing.
3516 HRESULT WINAPI StorageInternalImpl_Commit(
3517 IStorage* iface,
3518 DWORD grfCommitFlags) /* [in] */
3520 return S_OK;
3523 /******************************************************************************
3525 ** Storage32InternalImpl_Revert
3527 ** The non-root storages cannot be opened in transacted mode thus this function
3528 ** does nothing.
3530 HRESULT WINAPI StorageInternalImpl_Revert(
3531 IStorage* iface)
3533 return S_OK;
3536 /******************************************************************************
3537 ** IEnumSTATSTGImpl implementation
3540 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3541 StorageImpl* parentStorage,
3542 ULONG firstPropertyNode)
3544 IEnumSTATSTGImpl* newEnumeration;
3546 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3548 if (newEnumeration!=0)
3551 * Set-up the virtual function table and reference count.
3553 ICOM_VTBL(newEnumeration) = &IEnumSTATSTGImpl_Vtbl;
3554 newEnumeration->ref = 0;
3557 * We want to nail-down the reference to the storage in case the
3558 * enumeration out-lives the storage in the client application.
3560 newEnumeration->parentStorage = parentStorage;
3561 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3563 newEnumeration->firstPropertyNode = firstPropertyNode;
3566 * Initialize the search stack
3568 newEnumeration->stackSize = 0;
3569 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3570 newEnumeration->stackToVisit =
3571 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3574 * Make sure the current node of the iterator is the first one.
3576 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3579 return newEnumeration;
3582 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3584 IStorage_Release((IStorage*)This->parentStorage);
3585 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3586 HeapFree(GetProcessHeap(), 0, This);
3589 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3590 IEnumSTATSTG* iface,
3591 REFIID riid,
3592 void** ppvObject)
3594 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3597 * Perform a sanity check on the parameters.
3599 if (ppvObject==0)
3600 return E_INVALIDARG;
3603 * Initialize the return parameter.
3605 *ppvObject = 0;
3608 * Compare the riid with the interface IDs implemented by this object.
3610 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3612 *ppvObject = (IEnumSTATSTG*)This;
3614 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3616 *ppvObject = (IEnumSTATSTG*)This;
3620 * Check that we obtained an interface.
3622 if ((*ppvObject)==0)
3623 return E_NOINTERFACE;
3626 * Query Interface always increases the reference count by one when it is
3627 * successful
3629 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3631 return S_OK;
3634 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3635 IEnumSTATSTG* iface)
3637 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3639 This->ref++;
3640 return This->ref;
3643 ULONG WINAPI IEnumSTATSTGImpl_Release(
3644 IEnumSTATSTG* iface)
3646 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3648 ULONG newRef;
3650 This->ref--;
3651 newRef = This->ref;
3654 * If the reference count goes down to 0, perform suicide.
3656 if (newRef==0)
3658 IEnumSTATSTGImpl_Destroy(This);
3661 return newRef;
3664 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3665 IEnumSTATSTG* iface,
3666 ULONG celt,
3667 STATSTG* rgelt,
3668 ULONG* pceltFetched)
3670 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3672 StgProperty currentProperty;
3673 STATSTG* currentReturnStruct = rgelt;
3674 ULONG objectFetched = 0;
3675 ULONG currentSearchNode;
3678 * Perform a sanity check on the parameters.
3680 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3681 return E_INVALIDARG;
3684 * To avoid the special case, get another pointer to a ULONG value if
3685 * the caller didn't supply one.
3687 if (pceltFetched==0)
3688 pceltFetched = &objectFetched;
3691 * Start the iteration, we will iterate until we hit the end of the
3692 * linked list or until we hit the number of items to iterate through
3694 *pceltFetched = 0;
3697 * Start with the node at the top of the stack.
3699 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3701 while ( ( *pceltFetched < celt) &&
3702 ( currentSearchNode!=PROPERTY_NULL) )
3705 * Remove the top node from the stack
3707 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3710 * Read the property from the storage.
3712 StorageImpl_ReadProperty(This->parentStorage,
3713 currentSearchNode,
3714 &currentProperty);
3717 * Copy the information to the return buffer.
3719 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3720 &currentProperty,
3721 STATFLAG_DEFAULT);
3724 * Step to the next item in the iteration
3726 (*pceltFetched)++;
3727 currentReturnStruct++;
3730 * Push the next search node in the search stack.
3732 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3735 * continue the iteration.
3737 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3740 if (*pceltFetched == celt)
3741 return S_OK;
3743 return S_FALSE;
3747 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3748 IEnumSTATSTG* iface,
3749 ULONG celt)
3751 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3753 StgProperty currentProperty;
3754 ULONG objectFetched = 0;
3755 ULONG currentSearchNode;
3758 * Start with the node at the top of the stack.
3760 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3762 while ( (objectFetched < celt) &&
3763 (currentSearchNode!=PROPERTY_NULL) )
3766 * Remove the top node from the stack
3768 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3771 * Read the property from the storage.
3773 StorageImpl_ReadProperty(This->parentStorage,
3774 currentSearchNode,
3775 &currentProperty);
3778 * Step to the next item in the iteration
3780 objectFetched++;
3783 * Push the next search node in the search stack.
3785 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3788 * continue the iteration.
3790 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3793 if (objectFetched == celt)
3794 return S_OK;
3796 return S_FALSE;
3799 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3800 IEnumSTATSTG* iface)
3802 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3804 StgProperty rootProperty;
3805 BOOL readSuccessful;
3808 * Re-initialize the search stack to an empty stack
3810 This->stackSize = 0;
3813 * Read the root property from the storage.
3815 readSuccessful = StorageImpl_ReadProperty(
3816 This->parentStorage,
3817 This->firstPropertyNode,
3818 &rootProperty);
3820 if (readSuccessful)
3822 assert(rootProperty.sizeOfNameString!=0);
3825 * Push the search node in the search stack.
3827 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3830 return S_OK;
3833 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3834 IEnumSTATSTG* iface,
3835 IEnumSTATSTG** ppenum)
3837 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3839 IEnumSTATSTGImpl* newClone;
3842 * Perform a sanity check on the parameters.
3844 if (ppenum==0)
3845 return E_INVALIDARG;
3847 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3848 This->firstPropertyNode);
3852 * The new clone enumeration must point to the same current node as
3853 * the ole one.
3855 newClone->stackSize = This->stackSize ;
3856 newClone->stackMaxSize = This->stackMaxSize ;
3857 newClone->stackToVisit =
3858 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3860 memcpy(
3861 newClone->stackToVisit,
3862 This->stackToVisit,
3863 sizeof(ULONG) * newClone->stackSize);
3865 *ppenum = (IEnumSTATSTG*)newClone;
3868 * Don't forget to nail down a reference to the clone before
3869 * returning it.
3871 IEnumSTATSTGImpl_AddRef(*ppenum);
3873 return S_OK;
3876 INT IEnumSTATSTGImpl_FindParentProperty(
3877 IEnumSTATSTGImpl *This,
3878 ULONG childProperty,
3879 StgProperty *currentProperty,
3880 ULONG *thisNodeId)
3882 ULONG currentSearchNode;
3883 ULONG foundNode;
3886 * To avoid the special case, get another pointer to a ULONG value if
3887 * the caller didn't supply one.
3889 if (thisNodeId==0)
3890 thisNodeId = &foundNode;
3893 * Start with the node at the top of the stack.
3895 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3898 while (currentSearchNode!=PROPERTY_NULL)
3901 * Store the current node in the returned parameters
3903 *thisNodeId = currentSearchNode;
3906 * Remove the top node from the stack
3908 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3911 * Read the property from the storage.
3913 StorageImpl_ReadProperty(
3914 This->parentStorage,
3915 currentSearchNode,
3916 currentProperty);
3918 if (currentProperty->previousProperty == childProperty)
3919 return PROPERTY_RELATION_PREVIOUS;
3921 else if (currentProperty->nextProperty == childProperty)
3922 return PROPERTY_RELATION_NEXT;
3924 else if (currentProperty->dirProperty == childProperty)
3925 return PROPERTY_RELATION_DIR;
3928 * Push the next search node in the search stack.
3930 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3933 * continue the iteration.
3935 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3938 return PROPERTY_NULL;
3941 ULONG IEnumSTATSTGImpl_FindProperty(
3942 IEnumSTATSTGImpl* This,
3943 const OLECHAR* lpszPropName,
3944 StgProperty* currentProperty)
3946 ULONG currentSearchNode;
3949 * Start with the node at the top of the stack.
3951 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3953 while (currentSearchNode!=PROPERTY_NULL)
3956 * Remove the top node from the stack
3958 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3961 * Read the property from the storage.
3963 StorageImpl_ReadProperty(This->parentStorage,
3964 currentSearchNode,
3965 currentProperty);
3967 if ( propertyNameCmp(
3968 (OLECHAR*)currentProperty->name,
3969 (OLECHAR*)lpszPropName) == 0)
3970 return currentSearchNode;
3973 * Push the next search node in the search stack.
3975 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3978 * continue the iteration.
3980 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3983 return PROPERTY_NULL;
3986 void IEnumSTATSTGImpl_PushSearchNode(
3987 IEnumSTATSTGImpl* This,
3988 ULONG nodeToPush)
3990 StgProperty rootProperty;
3991 BOOL readSuccessful;
3994 * First, make sure we're not trying to push an unexisting node.
3996 if (nodeToPush==PROPERTY_NULL)
3997 return;
4000 * First push the node to the stack
4002 if (This->stackSize == This->stackMaxSize)
4004 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4006 This->stackToVisit = HeapReAlloc(
4007 GetProcessHeap(),
4009 This->stackToVisit,
4010 sizeof(ULONG) * This->stackMaxSize);
4013 This->stackToVisit[This->stackSize] = nodeToPush;
4014 This->stackSize++;
4017 * Read the root property from the storage.
4019 readSuccessful = StorageImpl_ReadProperty(
4020 This->parentStorage,
4021 nodeToPush,
4022 &rootProperty);
4024 if (readSuccessful)
4026 assert(rootProperty.sizeOfNameString!=0);
4029 * Push the previous search node in the search stack.
4031 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4035 ULONG IEnumSTATSTGImpl_PopSearchNode(
4036 IEnumSTATSTGImpl* This,
4037 BOOL remove)
4039 ULONG topNode;
4041 if (This->stackSize == 0)
4042 return PROPERTY_NULL;
4044 topNode = This->stackToVisit[This->stackSize-1];
4046 if (remove)
4047 This->stackSize--;
4049 return topNode;
4052 /******************************************************************************
4053 ** StorageUtl implementation
4056 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
4058 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
4061 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
4063 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
4066 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
4068 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
4071 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
4073 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
4076 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
4078 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4079 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4080 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4082 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
4085 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
4087 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4088 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4089 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4091 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
4094 void StorageUtl_CopyPropertyToSTATSTG(
4095 STATSTG* destination,
4096 StgProperty* source,
4097 int statFlags)
4100 * The copy of the string occurs only when the flag is not set
4102 if ((statFlags & STATFLAG_NONAME) != 0)
4104 destination->pwcsName = 0;
4106 else
4108 destination->pwcsName =
4109 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4111 strcpyW((LPWSTR)destination->pwcsName, source->name);
4114 switch (source->propertyType)
4116 case PROPTYPE_STORAGE:
4117 case PROPTYPE_ROOT:
4118 destination->type = STGTY_STORAGE;
4119 break;
4120 case PROPTYPE_STREAM:
4121 destination->type = STGTY_STREAM;
4122 break;
4123 default:
4124 destination->type = STGTY_STREAM;
4125 break;
4128 destination->cbSize = source->size;
4130 currentReturnStruct->mtime = {0}; TODO
4131 currentReturnStruct->ctime = {0};
4132 currentReturnStruct->atime = {0};
4134 destination->grfMode = 0;
4135 destination->grfLocksSupported = 0;
4136 destination->clsid = source->propertyUniqueID;
4137 destination->grfStateBits = 0;
4138 destination->reserved = 0;
4141 /******************************************************************************
4142 ** BlockChainStream implementation
4145 BlockChainStream* BlockChainStream_Construct(
4146 StorageImpl* parentStorage,
4147 ULONG* headOfStreamPlaceHolder,
4148 ULONG propertyIndex)
4150 BlockChainStream* newStream;
4151 ULONG blockIndex;
4153 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4155 newStream->parentStorage = parentStorage;
4156 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4157 newStream->ownerPropertyIndex = propertyIndex;
4158 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4159 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4160 newStream->numBlocks = 0;
4162 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4164 while (blockIndex != BLOCK_END_OF_CHAIN)
4166 newStream->numBlocks++;
4167 newStream->tailIndex = blockIndex;
4169 if(FAILED(StorageImpl_GetNextBlockInChain(
4170 parentStorage,
4171 blockIndex,
4172 &blockIndex)))
4174 HeapFree(GetProcessHeap(), 0, newStream);
4175 return NULL;
4179 return newStream;
4182 void BlockChainStream_Destroy(BlockChainStream* This)
4184 HeapFree(GetProcessHeap(), 0, This);
4187 /******************************************************************************
4188 * BlockChainStream_GetHeadOfChain
4190 * Returns the head of this stream chain.
4191 * Some special chains don't have properties, their heads are kept in
4192 * This->headOfStreamPlaceHolder.
4195 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4197 StgProperty chainProperty;
4198 BOOL readSuccessful;
4200 if (This->headOfStreamPlaceHolder != 0)
4201 return *(This->headOfStreamPlaceHolder);
4203 if (This->ownerPropertyIndex != PROPERTY_NULL)
4205 readSuccessful = StorageImpl_ReadProperty(
4206 This->parentStorage,
4207 This->ownerPropertyIndex,
4208 &chainProperty);
4210 if (readSuccessful)
4212 return chainProperty.startingBlock;
4216 return BLOCK_END_OF_CHAIN;
4219 /******************************************************************************
4220 * BlockChainStream_GetCount
4222 * Returns the number of blocks that comprises this chain.
4223 * This is not the size of the stream as the last block may not be full!
4226 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4228 ULONG blockIndex;
4229 ULONG count = 0;
4231 blockIndex = BlockChainStream_GetHeadOfChain(This);
4233 while (blockIndex != BLOCK_END_OF_CHAIN)
4235 count++;
4237 if(FAILED(StorageImpl_GetNextBlockInChain(
4238 This->parentStorage,
4239 blockIndex,
4240 &blockIndex)))
4241 return 0;
4244 return count;
4247 /******************************************************************************
4248 * BlockChainStream_ReadAt
4250 * Reads a specified number of bytes from this chain at the specified offset.
4251 * bytesRead may be NULL.
4252 * Failure will be returned if the specified number of bytes has not been read.
4254 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4255 ULARGE_INTEGER offset,
4256 ULONG size,
4257 void* buffer,
4258 ULONG* bytesRead)
4260 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4261 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4262 ULONG bytesToReadInBuffer;
4263 ULONG blockIndex;
4264 BYTE* bufferWalker;
4265 BYTE* bigBlockBuffer;
4268 * Find the first block in the stream that contains part of the buffer.
4270 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4271 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4272 (blockNoInSequence < This->lastBlockNoInSequence) )
4274 blockIndex = BlockChainStream_GetHeadOfChain(This);
4275 This->lastBlockNoInSequence = blockNoInSequence;
4277 else
4279 ULONG temp = blockNoInSequence;
4281 blockIndex = This->lastBlockNoInSequenceIndex;
4282 blockNoInSequence -= This->lastBlockNoInSequence;
4283 This->lastBlockNoInSequence = temp;
4286 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4288 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4289 return FALSE;
4290 blockNoInSequence--;
4293 This->lastBlockNoInSequenceIndex = blockIndex;
4296 * Start reading the buffer.
4298 *bytesRead = 0;
4299 bufferWalker = buffer;
4301 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4304 * Calculate how many bytes we can copy from this big block.
4306 bytesToReadInBuffer =
4307 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4310 * Copy those bytes to the buffer
4312 bigBlockBuffer =
4313 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4315 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4317 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4320 * Step to the next big block.
4322 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4323 return FALSE;
4325 bufferWalker += bytesToReadInBuffer;
4326 size -= bytesToReadInBuffer;
4327 *bytesRead += bytesToReadInBuffer;
4328 offsetInBlock = 0; /* There is no offset on the next block */
4332 return (size == 0);
4335 /******************************************************************************
4336 * BlockChainStream_WriteAt
4338 * Writes the specified number of bytes to this chain at the specified offset.
4339 * bytesWritten may be NULL.
4340 * Will fail if not all specified number of bytes have been written.
4342 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4343 ULARGE_INTEGER offset,
4344 ULONG size,
4345 const void* buffer,
4346 ULONG* bytesWritten)
4348 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4349 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4350 ULONG bytesToWrite;
4351 ULONG blockIndex;
4352 BYTE* bufferWalker;
4353 BYTE* bigBlockBuffer;
4356 * Find the first block in the stream that contains part of the buffer.
4358 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4359 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4360 (blockNoInSequence < This->lastBlockNoInSequence) )
4362 blockIndex = BlockChainStream_GetHeadOfChain(This);
4363 This->lastBlockNoInSequence = blockNoInSequence;
4365 else
4367 ULONG temp = blockNoInSequence;
4369 blockIndex = This->lastBlockNoInSequenceIndex;
4370 blockNoInSequence -= This->lastBlockNoInSequence;
4371 This->lastBlockNoInSequence = temp;
4374 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4376 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4377 &blockIndex)))
4378 return FALSE;
4379 blockNoInSequence--;
4382 This->lastBlockNoInSequenceIndex = blockIndex;
4385 * Here, I'm casting away the constness on the buffer variable
4386 * This is OK since we don't intend to modify that buffer.
4388 *bytesWritten = 0;
4389 bufferWalker = (BYTE*)buffer;
4391 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4394 * Calculate how many bytes we can copy from this big block.
4396 bytesToWrite =
4397 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4400 * Copy those bytes to the buffer
4402 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4404 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4406 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4409 * Step to the next big block.
4411 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4412 &blockIndex)))
4413 return FALSE;
4414 bufferWalker += bytesToWrite;
4415 size -= bytesToWrite;
4416 *bytesWritten += bytesToWrite;
4417 offsetInBlock = 0; /* There is no offset on the next block */
4420 return (size == 0);
4423 /******************************************************************************
4424 * BlockChainStream_Shrink
4426 * Shrinks this chain in the big block depot.
4428 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4429 ULARGE_INTEGER newSize)
4431 ULONG blockIndex, extraBlock;
4432 ULONG numBlocks;
4433 ULONG count = 1;
4436 * Reset the last accessed block cache.
4438 This->lastBlockNoInSequence = 0xFFFFFFFF;
4439 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4442 * Figure out how many blocks are needed to contain the new size
4444 numBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4446 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4447 numBlocks++;
4449 blockIndex = BlockChainStream_GetHeadOfChain(This);
4452 * Go to the new end of chain
4454 while (count < numBlocks)
4456 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4457 &blockIndex)))
4458 return FALSE;
4459 count++;
4462 /* Get the next block before marking the new end */
4463 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4464 &extraBlock)))
4465 return FALSE;
4467 /* Mark the new end of chain */
4468 StorageImpl_SetNextBlockInChain(
4469 This->parentStorage,
4470 blockIndex,
4471 BLOCK_END_OF_CHAIN);
4473 This->tailIndex = blockIndex;
4474 This->numBlocks = numBlocks;
4477 * Mark the extra blocks as free
4479 while (extraBlock != BLOCK_END_OF_CHAIN)
4481 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4482 &blockIndex)))
4483 return FALSE;
4484 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4485 extraBlock = blockIndex;
4488 return TRUE;
4491 /******************************************************************************
4492 * BlockChainStream_Enlarge
4494 * Grows this chain in the big block depot.
4496 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4497 ULARGE_INTEGER newSize)
4499 ULONG blockIndex, currentBlock;
4500 ULONG newNumBlocks;
4501 ULONG oldNumBlocks = 0;
4503 blockIndex = BlockChainStream_GetHeadOfChain(This);
4506 * Empty chain. Create the head.
4508 if (blockIndex == BLOCK_END_OF_CHAIN)
4510 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4511 StorageImpl_SetNextBlockInChain(This->parentStorage,
4512 blockIndex,
4513 BLOCK_END_OF_CHAIN);
4515 if (This->headOfStreamPlaceHolder != 0)
4517 *(This->headOfStreamPlaceHolder) = blockIndex;
4519 else
4521 StgProperty chainProp;
4522 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4524 StorageImpl_ReadProperty(
4525 This->parentStorage,
4526 This->ownerPropertyIndex,
4527 &chainProp);
4529 chainProp.startingBlock = blockIndex;
4531 StorageImpl_WriteProperty(
4532 This->parentStorage,
4533 This->ownerPropertyIndex,
4534 &chainProp);
4537 This->tailIndex = blockIndex;
4538 This->numBlocks = 1;
4542 * Figure out how many blocks are needed to contain this stream
4544 newNumBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4546 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4547 newNumBlocks++;
4550 * Go to the current end of chain
4552 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4554 currentBlock = blockIndex;
4556 while (blockIndex != BLOCK_END_OF_CHAIN)
4558 This->numBlocks++;
4559 currentBlock = blockIndex;
4561 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4562 &blockIndex)))
4563 return FALSE;
4566 This->tailIndex = currentBlock;
4569 currentBlock = This->tailIndex;
4570 oldNumBlocks = This->numBlocks;
4573 * Add new blocks to the chain
4575 if (oldNumBlocks < newNumBlocks)
4577 while (oldNumBlocks < newNumBlocks)
4579 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4581 StorageImpl_SetNextBlockInChain(
4582 This->parentStorage,
4583 currentBlock,
4584 blockIndex);
4586 StorageImpl_SetNextBlockInChain(
4587 This->parentStorage,
4588 blockIndex,
4589 BLOCK_END_OF_CHAIN);
4591 currentBlock = blockIndex;
4592 oldNumBlocks++;
4595 This->tailIndex = blockIndex;
4596 This->numBlocks = newNumBlocks;
4599 return TRUE;
4602 /******************************************************************************
4603 * BlockChainStream_SetSize
4605 * Sets the size of this stream. The big block depot will be updated.
4606 * The file will grow if we grow the chain.
4608 * TODO: Free the actual blocks in the file when we shrink the chain.
4609 * Currently, the blocks are still in the file. So the file size
4610 * doesn't shrink even if we shrink streams.
4612 BOOL BlockChainStream_SetSize(
4613 BlockChainStream* This,
4614 ULARGE_INTEGER newSize)
4616 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4618 if (newSize.s.LowPart == size.s.LowPart)
4619 return TRUE;
4621 if (newSize.s.LowPart < size.s.LowPart)
4623 BlockChainStream_Shrink(This, newSize);
4625 else
4627 ULARGE_INTEGER fileSize =
4628 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4630 ULONG diff = newSize.s.LowPart - size.s.LowPart;
4633 * Make sure the file stays a multiple of blocksize
4635 if ((diff % This->parentStorage->bigBlockSize) != 0)
4636 diff += (This->parentStorage->bigBlockSize -
4637 (diff % This->parentStorage->bigBlockSize) );
4639 fileSize.s.LowPart += diff;
4640 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4642 BlockChainStream_Enlarge(This, newSize);
4645 return TRUE;
4648 /******************************************************************************
4649 * BlockChainStream_GetSize
4651 * Returns the size of this chain.
4652 * Will return the block count if this chain doesn't have a property.
4654 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4656 StgProperty chainProperty;
4658 if(This->headOfStreamPlaceHolder == NULL)
4661 * This chain is a data stream read the property and return
4662 * the appropriate size
4664 StorageImpl_ReadProperty(
4665 This->parentStorage,
4666 This->ownerPropertyIndex,
4667 &chainProperty);
4669 return chainProperty.size;
4671 else
4674 * this chain is a chain that does not have a property, figure out the
4675 * size by making the product number of used blocks times the
4676 * size of them
4678 ULARGE_INTEGER result;
4679 result.s.HighPart = 0;
4681 result.s.LowPart =
4682 BlockChainStream_GetCount(This) *
4683 This->parentStorage->bigBlockSize;
4685 return result;
4689 /******************************************************************************
4690 ** SmallBlockChainStream implementation
4693 SmallBlockChainStream* SmallBlockChainStream_Construct(
4694 StorageImpl* parentStorage,
4695 ULONG propertyIndex)
4697 SmallBlockChainStream* newStream;
4699 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4701 newStream->parentStorage = parentStorage;
4702 newStream->ownerPropertyIndex = propertyIndex;
4704 return newStream;
4707 void SmallBlockChainStream_Destroy(
4708 SmallBlockChainStream* This)
4710 HeapFree(GetProcessHeap(), 0, This);
4713 /******************************************************************************
4714 * SmallBlockChainStream_GetHeadOfChain
4716 * Returns the head of this chain of small blocks.
4718 ULONG SmallBlockChainStream_GetHeadOfChain(
4719 SmallBlockChainStream* This)
4721 StgProperty chainProperty;
4722 BOOL readSuccessful;
4724 if (This->ownerPropertyIndex)
4726 readSuccessful = StorageImpl_ReadProperty(
4727 This->parentStorage,
4728 This->ownerPropertyIndex,
4729 &chainProperty);
4731 if (readSuccessful)
4733 return chainProperty.startingBlock;
4738 return BLOCK_END_OF_CHAIN;
4741 /******************************************************************************
4742 * SmallBlockChainStream_GetNextBlockInChain
4744 * Returns the index of the next small block in this chain.
4746 * Return Values:
4747 * - BLOCK_END_OF_CHAIN: end of this chain
4748 * - BLOCK_UNUSED: small block 'blockIndex' is free
4750 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4751 SmallBlockChainStream* This,
4752 ULONG blockIndex,
4753 ULONG* nextBlockInChain)
4755 ULARGE_INTEGER offsetOfBlockInDepot;
4756 DWORD buffer;
4757 ULONG bytesRead;
4758 BOOL success;
4760 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4762 offsetOfBlockInDepot.s.HighPart = 0;
4763 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4766 * Read those bytes in the buffer from the small block file.
4768 success = BlockChainStream_ReadAt(
4769 This->parentStorage->smallBlockDepotChain,
4770 offsetOfBlockInDepot,
4771 sizeof(DWORD),
4772 &buffer,
4773 &bytesRead);
4775 if (success)
4777 StorageUtl_ReadDWord(&buffer, 0, nextBlockInChain);
4778 return S_OK;
4781 return STG_E_READFAULT;
4784 /******************************************************************************
4785 * SmallBlockChainStream_SetNextBlockInChain
4787 * Writes the index of the next block of the specified block in the small
4788 * block depot.
4789 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4790 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4792 void SmallBlockChainStream_SetNextBlockInChain(
4793 SmallBlockChainStream* This,
4794 ULONG blockIndex,
4795 ULONG nextBlock)
4797 ULARGE_INTEGER offsetOfBlockInDepot;
4798 DWORD buffer;
4799 ULONG bytesWritten;
4801 offsetOfBlockInDepot.s.HighPart = 0;
4802 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4804 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4807 * Read those bytes in the buffer from the small block file.
4809 BlockChainStream_WriteAt(
4810 This->parentStorage->smallBlockDepotChain,
4811 offsetOfBlockInDepot,
4812 sizeof(DWORD),
4813 &buffer,
4814 &bytesWritten);
4817 /******************************************************************************
4818 * SmallBlockChainStream_FreeBlock
4820 * Flag small block 'blockIndex' as free in the small block depot.
4822 void SmallBlockChainStream_FreeBlock(
4823 SmallBlockChainStream* This,
4824 ULONG blockIndex)
4826 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4829 /******************************************************************************
4830 * SmallBlockChainStream_GetNextFreeBlock
4832 * Returns the index of a free small block. The small block depot will be
4833 * enlarged if necessary. The small block chain will also be enlarged if
4834 * necessary.
4836 ULONG SmallBlockChainStream_GetNextFreeBlock(
4837 SmallBlockChainStream* This)
4839 ULARGE_INTEGER offsetOfBlockInDepot;
4840 DWORD buffer;
4841 ULONG bytesRead;
4842 ULONG blockIndex = 0;
4843 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4844 BOOL success = TRUE;
4845 ULONG smallBlocksPerBigBlock;
4847 offsetOfBlockInDepot.s.HighPart = 0;
4850 * Scan the small block depot for a free block
4852 while (nextBlockIndex != BLOCK_UNUSED)
4854 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4856 success = BlockChainStream_ReadAt(
4857 This->parentStorage->smallBlockDepotChain,
4858 offsetOfBlockInDepot,
4859 sizeof(DWORD),
4860 &buffer,
4861 &bytesRead);
4864 * If we run out of space for the small block depot, enlarge it
4866 if (success)
4868 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4870 if (nextBlockIndex != BLOCK_UNUSED)
4871 blockIndex++;
4873 else
4875 ULONG count =
4876 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4878 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4879 ULONG nextBlock, newsbdIndex;
4880 BYTE* smallBlockDepot;
4882 nextBlock = sbdIndex;
4883 while (nextBlock != BLOCK_END_OF_CHAIN)
4885 sbdIndex = nextBlock;
4886 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4889 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4890 if (sbdIndex != BLOCK_END_OF_CHAIN)
4891 StorageImpl_SetNextBlockInChain(
4892 This->parentStorage,
4893 sbdIndex,
4894 newsbdIndex);
4896 StorageImpl_SetNextBlockInChain(
4897 This->parentStorage,
4898 newsbdIndex,
4899 BLOCK_END_OF_CHAIN);
4902 * Initialize all the small blocks to free
4904 smallBlockDepot =
4905 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4907 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4908 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4910 if (count == 0)
4913 * We have just created the small block depot.
4915 StgProperty rootProp;
4916 ULONG sbStartIndex;
4919 * Save it in the header
4921 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4922 StorageImpl_SaveFileHeader(This->parentStorage);
4925 * And allocate the first big block that will contain small blocks
4927 sbStartIndex =
4928 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4930 StorageImpl_SetNextBlockInChain(
4931 This->parentStorage,
4932 sbStartIndex,
4933 BLOCK_END_OF_CHAIN);
4935 StorageImpl_ReadProperty(
4936 This->parentStorage,
4937 This->parentStorage->rootPropertySetIndex,
4938 &rootProp);
4940 rootProp.startingBlock = sbStartIndex;
4941 rootProp.size.s.HighPart = 0;
4942 rootProp.size.s.LowPart = This->parentStorage->bigBlockSize;
4944 StorageImpl_WriteProperty(
4945 This->parentStorage,
4946 This->parentStorage->rootPropertySetIndex,
4947 &rootProp);
4952 smallBlocksPerBigBlock =
4953 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4956 * Verify if we have to allocate big blocks to contain small blocks
4958 if (blockIndex % smallBlocksPerBigBlock == 0)
4960 StgProperty rootProp;
4961 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4963 StorageImpl_ReadProperty(
4964 This->parentStorage,
4965 This->parentStorage->rootPropertySetIndex,
4966 &rootProp);
4968 if (rootProp.size.s.LowPart <
4969 (blocksRequired * This->parentStorage->bigBlockSize))
4971 rootProp.size.s.LowPart += This->parentStorage->bigBlockSize;
4973 BlockChainStream_SetSize(
4974 This->parentStorage->smallBlockRootChain,
4975 rootProp.size);
4977 StorageImpl_WriteProperty(
4978 This->parentStorage,
4979 This->parentStorage->rootPropertySetIndex,
4980 &rootProp);
4984 return blockIndex;
4987 /******************************************************************************
4988 * SmallBlockChainStream_ReadAt
4990 * Reads a specified number of bytes from this chain at the specified offset.
4991 * bytesRead may be NULL.
4992 * Failure will be returned if the specified number of bytes has not been read.
4994 BOOL SmallBlockChainStream_ReadAt(
4995 SmallBlockChainStream* This,
4996 ULARGE_INTEGER offset,
4997 ULONG size,
4998 void* buffer,
4999 ULONG* bytesRead)
5001 ULARGE_INTEGER offsetInBigBlockFile;
5002 ULONG blockNoInSequence =
5003 offset.s.LowPart / This->parentStorage->smallBlockSize;
5005 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
5006 ULONG bytesToReadInBuffer;
5007 ULONG blockIndex;
5008 ULONG bytesReadFromBigBlockFile;
5009 BYTE* bufferWalker;
5012 * This should never happen on a small block file.
5014 assert(offset.s.HighPart==0);
5017 * Find the first block in the stream that contains part of the buffer.
5019 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5021 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5023 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5024 &blockIndex)))
5025 return FALSE;
5026 blockNoInSequence--;
5030 * Start reading the buffer.
5032 *bytesRead = 0;
5033 bufferWalker = buffer;
5035 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5038 * Calculate how many bytes we can copy from this small block.
5040 bytesToReadInBuffer =
5041 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5044 * Calculate the offset of the small block in the small block file.
5046 offsetInBigBlockFile.s.HighPart = 0;
5047 offsetInBigBlockFile.s.LowPart =
5048 blockIndex * This->parentStorage->smallBlockSize;
5050 offsetInBigBlockFile.s.LowPart += offsetInBlock;
5053 * Read those bytes in the buffer from the small block file.
5055 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5056 offsetInBigBlockFile,
5057 bytesToReadInBuffer,
5058 bufferWalker,
5059 &bytesReadFromBigBlockFile);
5061 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5064 * Step to the next big block.
5066 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5067 return FALSE;
5068 bufferWalker += bytesToReadInBuffer;
5069 size -= bytesToReadInBuffer;
5070 *bytesRead += bytesToReadInBuffer;
5071 offsetInBlock = 0; /* There is no offset on the next block */
5074 return (size == 0);
5077 /******************************************************************************
5078 * SmallBlockChainStream_WriteAt
5080 * Writes the specified number of bytes to this chain at the specified offset.
5081 * bytesWritten may be NULL.
5082 * Will fail if not all specified number of bytes have been written.
5084 BOOL SmallBlockChainStream_WriteAt(
5085 SmallBlockChainStream* This,
5086 ULARGE_INTEGER offset,
5087 ULONG size,
5088 const void* buffer,
5089 ULONG* bytesWritten)
5091 ULARGE_INTEGER offsetInBigBlockFile;
5092 ULONG blockNoInSequence =
5093 offset.s.LowPart / This->parentStorage->smallBlockSize;
5095 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
5096 ULONG bytesToWriteInBuffer;
5097 ULONG blockIndex;
5098 ULONG bytesWrittenFromBigBlockFile;
5099 BYTE* bufferWalker;
5102 * This should never happen on a small block file.
5104 assert(offset.s.HighPart==0);
5107 * Find the first block in the stream that contains part of the buffer.
5109 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5111 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5113 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5114 return FALSE;
5115 blockNoInSequence--;
5119 * Start writing the buffer.
5121 * Here, I'm casting away the constness on the buffer variable
5122 * This is OK since we don't intend to modify that buffer.
5124 *bytesWritten = 0;
5125 bufferWalker = (BYTE*)buffer;
5126 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5129 * Calculate how many bytes we can copy to this small block.
5131 bytesToWriteInBuffer =
5132 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5135 * Calculate the offset of the small block in the small block file.
5137 offsetInBigBlockFile.s.HighPart = 0;
5138 offsetInBigBlockFile.s.LowPart =
5139 blockIndex * This->parentStorage->smallBlockSize;
5141 offsetInBigBlockFile.s.LowPart += offsetInBlock;
5144 * Write those bytes in the buffer to the small block file.
5146 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5147 offsetInBigBlockFile,
5148 bytesToWriteInBuffer,
5149 bufferWalker,
5150 &bytesWrittenFromBigBlockFile);
5152 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5155 * Step to the next big block.
5157 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5158 &blockIndex)))
5159 return FALSE;
5160 bufferWalker += bytesToWriteInBuffer;
5161 size -= bytesToWriteInBuffer;
5162 *bytesWritten += bytesToWriteInBuffer;
5163 offsetInBlock = 0; /* There is no offset on the next block */
5166 return (size == 0);
5169 /******************************************************************************
5170 * SmallBlockChainStream_Shrink
5172 * Shrinks this chain in the small block depot.
5174 BOOL SmallBlockChainStream_Shrink(
5175 SmallBlockChainStream* This,
5176 ULARGE_INTEGER newSize)
5178 ULONG blockIndex, extraBlock;
5179 ULONG numBlocks;
5180 ULONG count = 0;
5182 numBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5184 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5185 numBlocks++;
5187 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5190 * Go to the new end of chain
5192 while (count < numBlocks)
5194 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5195 &blockIndex)))
5196 return FALSE;
5197 count++;
5201 * If the count is 0, we have a special case, the head of the chain was
5202 * just freed.
5204 if (count == 0)
5206 StgProperty chainProp;
5208 StorageImpl_ReadProperty(This->parentStorage,
5209 This->ownerPropertyIndex,
5210 &chainProp);
5212 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5214 StorageImpl_WriteProperty(This->parentStorage,
5215 This->ownerPropertyIndex,
5216 &chainProp);
5219 * We start freeing the chain at the head block.
5221 extraBlock = blockIndex;
5223 else
5225 /* Get the next block before marking the new end */
5226 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5227 &extraBlock)))
5228 return FALSE;
5230 /* Mark the new end of chain */
5231 SmallBlockChainStream_SetNextBlockInChain(
5232 This,
5233 blockIndex,
5234 BLOCK_END_OF_CHAIN);
5238 * Mark the extra blocks as free
5240 while (extraBlock != BLOCK_END_OF_CHAIN)
5242 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5243 &blockIndex)))
5244 return FALSE;
5245 SmallBlockChainStream_FreeBlock(This, extraBlock);
5246 extraBlock = blockIndex;
5249 return TRUE;
5252 /******************************************************************************
5253 * SmallBlockChainStream_Enlarge
5255 * Grows this chain in the small block depot.
5257 BOOL SmallBlockChainStream_Enlarge(
5258 SmallBlockChainStream* This,
5259 ULARGE_INTEGER newSize)
5261 ULONG blockIndex, currentBlock;
5262 ULONG newNumBlocks;
5263 ULONG oldNumBlocks = 0;
5265 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5268 * Empty chain
5270 if (blockIndex == BLOCK_END_OF_CHAIN)
5273 StgProperty chainProp;
5275 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5276 &chainProp);
5278 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5280 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5281 &chainProp);
5283 blockIndex = chainProp.startingBlock;
5284 SmallBlockChainStream_SetNextBlockInChain(
5285 This,
5286 blockIndex,
5287 BLOCK_END_OF_CHAIN);
5290 currentBlock = blockIndex;
5293 * Figure out how many blocks are needed to contain this stream
5295 newNumBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5297 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5298 newNumBlocks++;
5301 * Go to the current end of chain
5303 while (blockIndex != BLOCK_END_OF_CHAIN)
5305 oldNumBlocks++;
5306 currentBlock = blockIndex;
5307 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5308 return FALSE;
5312 * Add new blocks to the chain
5314 while (oldNumBlocks < newNumBlocks)
5316 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5317 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5319 SmallBlockChainStream_SetNextBlockInChain(
5320 This,
5321 blockIndex,
5322 BLOCK_END_OF_CHAIN);
5324 currentBlock = blockIndex;
5325 oldNumBlocks++;
5328 return TRUE;
5331 /******************************************************************************
5332 * SmallBlockChainStream_GetCount
5334 * Returns the number of blocks that comprises this chain.
5335 * This is not the size of this chain as the last block may not be full!
5337 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5339 ULONG blockIndex;
5340 ULONG count = 0;
5342 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5344 while (blockIndex != BLOCK_END_OF_CHAIN)
5346 count++;
5348 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5349 return 0;
5352 return count;
5355 /******************************************************************************
5356 * SmallBlockChainStream_SetSize
5358 * Sets the size of this stream.
5359 * The file will grow if we grow the chain.
5361 * TODO: Free the actual blocks in the file when we shrink the chain.
5362 * Currently, the blocks are still in the file. So the file size
5363 * doesn't shrink even if we shrink streams.
5365 BOOL SmallBlockChainStream_SetSize(
5366 SmallBlockChainStream* This,
5367 ULARGE_INTEGER newSize)
5369 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5371 if (newSize.s.LowPart == size.s.LowPart)
5372 return TRUE;
5374 if (newSize.s.LowPart < size.s.LowPart)
5376 SmallBlockChainStream_Shrink(This, newSize);
5378 else
5380 SmallBlockChainStream_Enlarge(This, newSize);
5383 return TRUE;
5386 /******************************************************************************
5387 * SmallBlockChainStream_GetSize
5389 * Returns the size of this chain.
5391 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5393 StgProperty chainProperty;
5395 StorageImpl_ReadProperty(
5396 This->parentStorage,
5397 This->ownerPropertyIndex,
5398 &chainProperty);
5400 return chainProperty.size;
5403 /******************************************************************************
5404 * StgCreateDocfile [OLE32.144]
5406 HRESULT WINAPI StgCreateDocfile(
5407 LPCOLESTR pwcsName,
5408 DWORD grfMode,
5409 DWORD reserved,
5410 IStorage **ppstgOpen)
5412 StorageImpl* newStorage = 0;
5413 HANDLE hFile = INVALID_HANDLE_VALUE;
5414 HRESULT hr = S_OK;
5415 DWORD shareMode;
5416 DWORD accessMode;
5417 DWORD creationMode;
5418 DWORD fileAttributes;
5419 WCHAR tempFileName[MAX_PATH];
5421 TRACE("(%s, %lx, %ld, %p)\n",
5422 debugstr_w(pwcsName), grfMode,
5423 reserved, ppstgOpen);
5426 * Validate the parameters
5428 if (ppstgOpen == 0)
5429 return STG_E_INVALIDPOINTER;
5432 * Validate the STGM flags
5434 if ( FAILED( validateSTGM(grfMode) ))
5435 return STG_E_INVALIDFLAG;
5438 * Generate a unique name.
5440 if (pwcsName == 0)
5442 WCHAR tempPath[MAX_PATH];
5443 WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5445 if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5446 return STG_E_INVALIDFLAG;
5447 if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5448 return STG_E_INVALIDFLAG;
5450 memset(tempPath, 0, sizeof(tempPath));
5451 memset(tempFileName, 0, sizeof(tempFileName));
5453 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5454 tempPath[0] = '.';
5456 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5457 pwcsName = tempFileName;
5458 else
5459 return STG_E_INSUFFICIENTMEMORY;
5461 creationMode = TRUNCATE_EXISTING;
5463 else
5465 creationMode = GetCreationModeFromSTGM(grfMode);
5469 * Interpret the STGM value grfMode
5471 shareMode = GetShareModeFromSTGM(grfMode);
5472 accessMode = GetAccessModeFromSTGM(grfMode);
5474 if (grfMode & STGM_DELETEONRELEASE)
5475 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5476 else
5477 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5479 if (grfMode & STGM_TRANSACTED)
5480 FIXME("Transacted mode not implemented.\n");
5483 * Initialize the "out" parameter.
5485 *ppstgOpen = 0;
5487 hFile = CreateFileW(pwcsName,
5488 accessMode,
5489 shareMode,
5490 NULL,
5491 creationMode,
5492 fileAttributes,
5495 if (hFile == INVALID_HANDLE_VALUE)
5497 return E_FAIL;
5501 * Allocate and initialize the new IStorage32object.
5503 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5505 if (newStorage == 0)
5506 return STG_E_INSUFFICIENTMEMORY;
5508 hr = StorageImpl_Construct(
5509 newStorage,
5510 hFile,
5511 pwcsName,
5512 NULL,
5513 grfMode,
5514 TRUE,
5515 TRUE);
5517 if (FAILED(hr))
5519 HeapFree(GetProcessHeap(), 0, newStorage);
5520 return hr;
5524 * Get an "out" pointer for the caller.
5526 hr = StorageBaseImpl_QueryInterface(
5527 (IStorage*)newStorage,
5528 (REFIID)&IID_IStorage,
5529 (void**)ppstgOpen);
5531 return hr;
5534 /******************************************************************************
5535 * StgOpenStorage [OLE32.148]
5537 HRESULT WINAPI StgOpenStorage(
5538 const OLECHAR *pwcsName,
5539 IStorage *pstgPriority,
5540 DWORD grfMode,
5541 SNB snbExclude,
5542 DWORD reserved,
5543 IStorage **ppstgOpen)
5545 StorageImpl* newStorage = 0;
5546 HRESULT hr = S_OK;
5547 HANDLE hFile = 0;
5548 DWORD shareMode;
5549 DWORD accessMode;
5550 WCHAR fullname[MAX_PATH];
5551 DWORD length;
5553 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5554 debugstr_w(pwcsName), pstgPriority, grfMode,
5555 snbExclude, reserved, ppstgOpen);
5558 * Perform a sanity check
5560 if (( pwcsName == 0) || (ppstgOpen == 0) )
5562 hr = STG_E_INVALIDPOINTER;
5563 goto end;
5567 * Validate the STGM flags
5569 if ( FAILED( validateSTGM(grfMode) ))
5571 hr = STG_E_INVALIDFLAG;
5572 goto end;
5576 * Interpret the STGM value grfMode
5578 shareMode = GetShareModeFromSTGM(grfMode);
5579 accessMode = GetAccessModeFromSTGM(grfMode);
5582 * Initialize the "out" parameter.
5584 *ppstgOpen = 0;
5586 hFile = CreateFileW( pwcsName,
5587 accessMode,
5588 shareMode,
5589 NULL,
5590 OPEN_EXISTING,
5591 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5594 length = GetFileSize(hFile, NULL);
5596 if (hFile==INVALID_HANDLE_VALUE)
5598 DWORD last_error = GetLastError();
5600 hr = E_FAIL;
5602 switch (last_error)
5604 case ERROR_FILE_NOT_FOUND:
5605 hr = STG_E_FILENOTFOUND;
5606 break;
5608 case ERROR_PATH_NOT_FOUND:
5609 hr = STG_E_PATHNOTFOUND;
5610 break;
5612 case ERROR_ACCESS_DENIED:
5613 case ERROR_WRITE_PROTECT:
5614 hr = STG_E_ACCESSDENIED;
5615 break;
5617 case ERROR_SHARING_VIOLATION:
5618 hr = STG_E_SHAREVIOLATION;
5619 break;
5621 default:
5622 hr = E_FAIL;
5625 goto end;
5629 * Allocate and initialize the new IStorage32object.
5631 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5633 if (newStorage == 0)
5635 hr = STG_E_INSUFFICIENTMEMORY;
5636 goto end;
5639 /* if the file's length was zero, initialize the storage */
5640 hr = StorageImpl_Construct(
5641 newStorage,
5642 hFile,
5643 pwcsName,
5644 NULL,
5645 grfMode,
5646 TRUE,
5647 !length );
5649 if (FAILED(hr))
5651 HeapFree(GetProcessHeap(), 0, newStorage);
5653 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5655 if(hr == STG_E_INVALIDHEADER)
5656 hr = STG_E_FILEALREADYEXISTS;
5657 goto end;
5660 /* prepare the file name string given in lieu of the root property name */
5661 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5662 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5663 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5666 * Get an "out" pointer for the caller.
5668 hr = StorageBaseImpl_QueryInterface(
5669 (IStorage*)newStorage,
5670 (REFIID)&IID_IStorage,
5671 (void**)ppstgOpen);
5673 end:
5674 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5675 return hr;
5678 /******************************************************************************
5679 * StgCreateDocfileOnILockBytes [OLE32.145]
5681 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5682 ILockBytes *plkbyt,
5683 DWORD grfMode,
5684 DWORD reserved,
5685 IStorage** ppstgOpen)
5687 StorageImpl* newStorage = 0;
5688 HRESULT hr = S_OK;
5691 * Validate the parameters
5693 if ((ppstgOpen == 0) || (plkbyt == 0))
5694 return STG_E_INVALIDPOINTER;
5697 * Allocate and initialize the new IStorage object.
5699 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5701 if (newStorage == 0)
5702 return STG_E_INSUFFICIENTMEMORY;
5704 hr = StorageImpl_Construct(
5705 newStorage,
5708 plkbyt,
5709 grfMode,
5710 FALSE,
5711 TRUE);
5713 if (FAILED(hr))
5715 HeapFree(GetProcessHeap(), 0, newStorage);
5716 return hr;
5720 * Get an "out" pointer for the caller.
5722 hr = StorageBaseImpl_QueryInterface(
5723 (IStorage*)newStorage,
5724 (REFIID)&IID_IStorage,
5725 (void**)ppstgOpen);
5727 return hr;
5730 /******************************************************************************
5731 * StgOpenStorageOnILockBytes [OLE32.149]
5733 HRESULT WINAPI StgOpenStorageOnILockBytes(
5734 ILockBytes *plkbyt,
5735 IStorage *pstgPriority,
5736 DWORD grfMode,
5737 SNB snbExclude,
5738 DWORD reserved,
5739 IStorage **ppstgOpen)
5741 StorageImpl* newStorage = 0;
5742 HRESULT hr = S_OK;
5745 * Perform a sanity check
5747 if ((plkbyt == 0) || (ppstgOpen == 0))
5748 return STG_E_INVALIDPOINTER;
5751 * Validate the STGM flags
5753 if ( FAILED( validateSTGM(grfMode) ))
5754 return STG_E_INVALIDFLAG;
5757 * Initialize the "out" parameter.
5759 *ppstgOpen = 0;
5762 * Allocate and initialize the new IStorage object.
5764 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5766 if (newStorage == 0)
5767 return STG_E_INSUFFICIENTMEMORY;
5769 hr = StorageImpl_Construct(
5770 newStorage,
5773 plkbyt,
5774 grfMode,
5775 FALSE,
5776 FALSE);
5778 if (FAILED(hr))
5780 HeapFree(GetProcessHeap(), 0, newStorage);
5781 return hr;
5785 * Get an "out" pointer for the caller.
5787 hr = StorageBaseImpl_QueryInterface(
5788 (IStorage*)newStorage,
5789 (REFIID)&IID_IStorage,
5790 (void**)ppstgOpen);
5792 return hr;
5795 /******************************************************************************
5796 * StgSetTimes [ole32.150]
5797 * StgSetTimes [OLE32.150]
5801 HRESULT WINAPI StgSetTimes(OLECHAR *str, FILETIME *a, FILETIME *b, FILETIME *c )
5803 FIXME("(%s, %p, %p, %p),stub!\n", debugstr_w(str), a, b, c);
5804 return S_OK;
5807 /******************************************************************************
5808 * StgIsStorageILockBytes [OLE32.147]
5810 * Determines if the ILockBytes contains a storage object.
5812 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5814 BYTE sig[8];
5815 ULARGE_INTEGER offset;
5817 offset.s.HighPart = 0;
5818 offset.s.LowPart = 0;
5820 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5822 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5823 return S_OK;
5825 return S_FALSE;
5828 /******************************************************************************
5829 * WriteClassStg [OLE32.158]
5831 * This method will store the specified CLSID in the specified storage object
5833 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5835 HRESULT hRes;
5837 assert(pStg != 0);
5839 hRes = IStorage_SetClass(pStg, rclsid);
5841 return hRes;
5844 /***********************************************************************
5845 * ReadClassStg (OLE32.134)
5847 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5849 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5851 STATSTG pstatstg;
5852 HRESULT hRes;
5854 TRACE("()\n");
5856 if(pclsid==NULL)
5857 return E_POINTER;
5859 * read a STATSTG structure (contains the clsid) from the storage
5861 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5863 if(SUCCEEDED(hRes))
5864 *pclsid=pstatstg.clsid;
5866 return hRes;
5869 /***********************************************************************
5870 * OleLoadFromStream (OLE32.113)
5872 * This function loads an object from stream
5874 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5876 CLSID clsid;
5877 HRESULT res;
5878 LPPERSISTSTREAM xstm;
5880 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
5882 res=ReadClassStm(pStm,&clsid);
5883 if (!SUCCEEDED(res))
5884 return res;
5885 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5886 if (!SUCCEEDED(res))
5887 return res;
5888 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
5889 if (!SUCCEEDED(res)) {
5890 IUnknown_Release((IUnknown*)*ppvObj);
5891 return res;
5893 res=IPersistStream_Load(xstm,pStm);
5894 IPersistStream_Release(xstm);
5895 /* FIXME: all refcounts ok at this point? I think they should be:
5896 * pStm : unchanged
5897 * ppvObj : 1
5898 * xstm : 0 (released)
5900 return res;
5903 /***********************************************************************
5904 * OleSaveToStream (OLE32.125)
5906 * This function saves an object with the IPersistStream interface on it
5907 * to the specified stream.
5909 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5912 CLSID clsid;
5913 HRESULT res;
5915 TRACE("(%p,%p)\n",pPStm,pStm);
5917 res=IPersistStream_GetClassID(pPStm,&clsid);
5919 if (SUCCEEDED(res)){
5921 res=WriteClassStm(pStm,&clsid);
5923 if (SUCCEEDED(res))
5925 res=IPersistStream_Save(pPStm,pStm,TRUE);
5928 TRACE("Finished Save\n");
5929 return res;
5932 /****************************************************************************
5933 * This method validate a STGM parameter that can contain the values below
5935 * STGM_DIRECT 0x00000000
5936 * STGM_TRANSACTED 0x00010000
5937 * STGM_SIMPLE 0x08000000
5939 * STGM_READ 0x00000000
5940 * STGM_WRITE 0x00000001
5941 * STGM_READWRITE 0x00000002
5943 * STGM_SHARE_DENY_NONE 0x00000040
5944 * STGM_SHARE_DENY_READ 0x00000030
5945 * STGM_SHARE_DENY_WRITE 0x00000020
5946 * STGM_SHARE_EXCLUSIVE 0x00000010
5948 * STGM_PRIORITY 0x00040000
5949 * STGM_DELETEONRELEASE 0x04000000
5951 * STGM_CREATE 0x00001000
5952 * STGM_CONVERT 0x00020000
5953 * STGM_FAILIFTHERE 0x00000000
5955 * STGM_NOSCRATCH 0x00100000
5956 * STGM_NOSNAPSHOT 0x00200000
5958 static HRESULT validateSTGM(DWORD stgm)
5960 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5961 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5962 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5964 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5965 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5966 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5968 BOOL bSTGM_SHARE_DENY_NONE =
5969 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5971 BOOL bSTGM_SHARE_DENY_READ =
5972 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5974 BOOL bSTGM_SHARE_DENY_WRITE =
5975 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5977 BOOL bSTGM_SHARE_EXCLUSIVE =
5978 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5980 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5981 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5983 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5984 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5987 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5989 if ( ! bSTGM_DIRECT )
5990 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5991 return E_FAIL;
5994 * STGM_WRITE | STGM_READWRITE | STGM_READ
5996 if ( ! bSTGM_READ )
5997 if( bSTGM_WRITE && bSTGM_READWRITE )
5998 return E_FAIL;
6001 * STGM_SHARE_DENY_NONE | others
6002 * (I assume here that DENY_READ implies DENY_WRITE)
6004 if ( bSTGM_SHARE_DENY_NONE )
6005 if ( bSTGM_SHARE_DENY_READ ||
6006 bSTGM_SHARE_DENY_WRITE ||
6007 bSTGM_SHARE_EXCLUSIVE)
6008 return E_FAIL;
6011 * STGM_CREATE | STGM_CONVERT
6012 * if both are false, STGM_FAILIFTHERE is set to TRUE
6014 if ( bSTGM_CREATE && bSTGM_CONVERT )
6015 return E_FAIL;
6018 * STGM_NOSCRATCH requires STGM_TRANSACTED
6020 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
6021 return E_FAIL;
6024 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6025 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6027 if (bSTGM_NOSNAPSHOT)
6029 if ( ! ( bSTGM_TRANSACTED &&
6030 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
6031 return E_FAIL;
6034 return S_OK;
6037 /****************************************************************************
6038 * GetShareModeFromSTGM
6040 * This method will return a share mode flag from a STGM value.
6041 * The STGM value is assumed valid.
6043 static DWORD GetShareModeFromSTGM(DWORD stgm)
6045 DWORD dwShareMode = 0;
6046 BOOL bSTGM_SHARE_DENY_NONE =
6047 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
6049 BOOL bSTGM_SHARE_DENY_READ =
6050 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
6052 BOOL bSTGM_SHARE_DENY_WRITE =
6053 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
6055 BOOL bSTGM_SHARE_EXCLUSIVE =
6056 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
6058 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
6059 dwShareMode = 0;
6061 if (bSTGM_SHARE_DENY_NONE)
6062 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6064 if (bSTGM_SHARE_DENY_WRITE)
6065 dwShareMode = FILE_SHARE_READ;
6067 return dwShareMode;
6070 /****************************************************************************
6071 * GetAccessModeFromSTGM
6073 * This method will return an access mode flag from a STGM value.
6074 * The STGM value is assumed valid.
6076 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6078 DWORD dwDesiredAccess = GENERIC_READ;
6079 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
6080 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
6081 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
6083 if (bSTGM_READ)
6084 dwDesiredAccess = GENERIC_READ;
6086 if (bSTGM_WRITE)
6087 dwDesiredAccess |= GENERIC_WRITE;
6089 if (bSTGM_READWRITE)
6090 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
6092 return dwDesiredAccess;
6095 /****************************************************************************
6096 * GetCreationModeFromSTGM
6098 * This method will return a creation mode flag from a STGM value.
6099 * The STGM value is assumed valid.
6101 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6103 if ( stgm & STGM_CREATE)
6104 return CREATE_ALWAYS;
6105 if (stgm & STGM_CONVERT) {
6106 FIXME("STGM_CONVERT not implemented!\n");
6107 return CREATE_NEW;
6109 /* All other cases */
6110 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
6111 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
6112 return CREATE_NEW;
6116 /*************************************************************************
6117 * OLECONVERT_LoadOLE10 [Internal]
6119 * Loads the OLE10 STREAM to memory
6121 * PARAMS
6122 * pOleStream [I] The OLESTREAM
6123 * pData [I] Data Structure for the OLESTREAM Data
6125 * RETURNS
6126 * Success: S_OK
6127 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6128 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6130 * NOTES
6131 * This function is used by OleConvertOLESTREAMToIStorage only.
6133 * Memory allocated for pData must be freed by the caller
6135 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6137 DWORD dwSize;
6138 HRESULT hRes = S_OK;
6139 int nTryCnt=0;
6140 int max_try = 6;
6142 pData->pData = NULL;
6143 pData->pstrOleObjFileName = (CHAR *) NULL;
6145 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6147 /* Get the OleID */
6148 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6149 if(dwSize != sizeof(pData->dwOleID))
6151 hRes = CONVERT10_E_OLESTREAM_GET;
6153 else if(pData->dwOleID != OLESTREAM_ID)
6155 hRes = CONVERT10_E_OLESTREAM_FMT;
6157 else
6159 hRes = S_OK;
6160 break;
6164 if(hRes == S_OK)
6166 /* Get the TypeID...more info needed for this field */
6167 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6168 if(dwSize != sizeof(pData->dwTypeID))
6170 hRes = CONVERT10_E_OLESTREAM_GET;
6173 if(hRes == S_OK)
6175 if(pData->dwTypeID != 0)
6177 /* Get the lenght of the OleTypeName */
6178 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6179 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6181 hRes = CONVERT10_E_OLESTREAM_GET;
6184 if(hRes == S_OK)
6186 if(pData->dwOleTypeNameLength > 0)
6188 /* Get the OleTypeName */
6189 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6190 if(dwSize != pData->dwOleTypeNameLength)
6192 hRes = CONVERT10_E_OLESTREAM_GET;
6196 if(bStrem1)
6198 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6199 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6201 hRes = CONVERT10_E_OLESTREAM_GET;
6203 if(hRes == S_OK)
6205 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6206 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6207 pData->pstrOleObjFileName = (CHAR *)malloc(pData->dwOleObjFileNameLength);
6208 if(pData->pstrOleObjFileName)
6210 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6211 if(dwSize != pData->dwOleObjFileNameLength)
6213 hRes = CONVERT10_E_OLESTREAM_GET;
6216 else
6217 hRes = CONVERT10_E_OLESTREAM_GET;
6220 else
6222 /* Get the Width of the Metafile */
6223 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6224 if(dwSize != sizeof(pData->dwMetaFileWidth))
6226 hRes = CONVERT10_E_OLESTREAM_GET;
6228 if(hRes == S_OK)
6230 /* Get the Height of the Metafile */
6231 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6232 if(dwSize != sizeof(pData->dwMetaFileHeight))
6234 hRes = CONVERT10_E_OLESTREAM_GET;
6238 if(hRes == S_OK)
6240 /* Get the Lenght of the Data */
6241 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6242 if(dwSize != sizeof(pData->dwDataLength))
6244 hRes = CONVERT10_E_OLESTREAM_GET;
6248 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6250 if(!bStrem1) /* if it is a second OLE stream data */
6252 pData->dwDataLength -= 8;
6253 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6254 if(dwSize != sizeof(pData->strUnknown))
6256 hRes = CONVERT10_E_OLESTREAM_GET;
6260 if(hRes == S_OK)
6262 if(pData->dwDataLength > 0)
6264 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6266 /* Get Data (ex. IStorage, Metafile, or BMP) */
6267 if(pData->pData)
6269 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6270 if(dwSize != pData->dwDataLength)
6272 hRes = CONVERT10_E_OLESTREAM_GET;
6275 else
6277 hRes = CONVERT10_E_OLESTREAM_GET;
6283 return hRes;
6286 /*************************************************************************
6287 * OLECONVERT_SaveOLE10 [Internal]
6289 * Saves the OLE10 STREAM From memory
6291 * PARAMS
6292 * pData [I] Data Structure for the OLESTREAM Data
6293 * pOleStream [I] The OLESTREAM to save
6295 * RETURNS
6296 * Success: S_OK
6297 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6299 * NOTES
6300 * This function is used by OleConvertIStorageToOLESTREAM only.
6303 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6305 DWORD dwSize;
6306 HRESULT hRes = S_OK;
6309 /* Set the OleID */
6310 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6311 if(dwSize != sizeof(pData->dwOleID))
6313 hRes = CONVERT10_E_OLESTREAM_PUT;
6316 if(hRes == S_OK)
6318 /* Set the TypeID */
6319 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6320 if(dwSize != sizeof(pData->dwTypeID))
6322 hRes = CONVERT10_E_OLESTREAM_PUT;
6326 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6328 /* Set the Lenght of the OleTypeName */
6329 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6330 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6332 hRes = CONVERT10_E_OLESTREAM_PUT;
6335 if(hRes == S_OK)
6337 if(pData->dwOleTypeNameLength > 0)
6339 /* Set the OleTypeName */
6340 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6341 if(dwSize != pData->dwOleTypeNameLength)
6343 hRes = CONVERT10_E_OLESTREAM_PUT;
6348 if(hRes == S_OK)
6350 /* Set the width of the Metafile */
6351 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6352 if(dwSize != sizeof(pData->dwMetaFileWidth))
6354 hRes = CONVERT10_E_OLESTREAM_PUT;
6358 if(hRes == S_OK)
6360 /* Set the height of the Metafile */
6361 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6362 if(dwSize != sizeof(pData->dwMetaFileHeight))
6364 hRes = CONVERT10_E_OLESTREAM_PUT;
6368 if(hRes == S_OK)
6370 /* Set the lenght of the Data */
6371 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6372 if(dwSize != sizeof(pData->dwDataLength))
6374 hRes = CONVERT10_E_OLESTREAM_PUT;
6378 if(hRes == S_OK)
6380 if(pData->dwDataLength > 0)
6382 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6383 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6384 if(dwSize != pData->dwDataLength)
6386 hRes = CONVERT10_E_OLESTREAM_PUT;
6391 return hRes;
6394 /*************************************************************************
6395 * OLECONVERT_GetOLE20FromOLE10[Internal]
6397 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6398 * opens it, and copies the content to the dest IStorage for
6399 * OleConvertOLESTREAMToIStorage
6402 * PARAMS
6403 * pDestStorage [I] The IStorage to copy the data to
6404 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6405 * nBufferLength [I] The size of the buffer
6407 * RETURNS
6408 * Nothing
6410 * NOTES
6414 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6416 HRESULT hRes;
6417 HANDLE hFile;
6418 IStorage *pTempStorage;
6419 DWORD dwNumOfBytesWritten;
6420 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6421 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6423 /* Create a temp File */
6424 GetTempPathW(MAX_PATH, wstrTempDir);
6425 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6426 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6428 if(hFile != INVALID_HANDLE_VALUE)
6430 /* Write IStorage Data to File */
6431 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6432 CloseHandle(hFile);
6434 /* Open and copy temp storage to the Dest Storage */
6435 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6436 if(hRes == S_OK)
6438 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6439 StorageBaseImpl_Release(pTempStorage);
6441 DeleteFileW(wstrTempFile);
6446 /*************************************************************************
6447 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6449 * Saves the OLE10 STREAM From memory
6451 * PARAMS
6452 * pStorage [I] The Src IStorage to copy
6453 * pData [I] The Dest Memory to write to.
6455 * RETURNS
6456 * The size in bytes allocated for pData
6458 * NOTES
6459 * Memory allocated for pData must be freed by the caller
6461 * Used by OleConvertIStorageToOLESTREAM only.
6464 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6466 HANDLE hFile;
6467 HRESULT hRes;
6468 DWORD nDataLength = 0;
6469 IStorage *pTempStorage;
6470 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6471 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6473 *pData = NULL;
6475 /* Create temp Storage */
6476 GetTempPathW(MAX_PATH, wstrTempDir);
6477 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6478 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6480 if(hRes == S_OK)
6482 /* Copy Src Storage to the Temp Storage */
6483 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6484 StorageBaseImpl_Release(pTempStorage);
6486 /* Open Temp Storage as a file and copy to memory */
6487 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6488 if(hFile != INVALID_HANDLE_VALUE)
6490 nDataLength = GetFileSize(hFile, NULL);
6491 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6492 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6493 CloseHandle(hFile);
6495 DeleteFileW(wstrTempFile);
6497 return nDataLength;
6500 /*************************************************************************
6501 * OLECONVERT_CreateOleStream [Internal]
6503 * Creates the "\001OLE" stream in the IStorage if neccessary.
6505 * PARAMS
6506 * pStorage [I] Dest storage to create the stream in
6508 * RETURNS
6509 * Nothing
6511 * NOTES
6512 * This function is used by OleConvertOLESTREAMToIStorage only.
6514 * This stream is still unknown, MS Word seems to have extra data
6515 * but since the data is stored in the OLESTREAM there should be
6516 * no need to recreate the stream. If the stream is manually
6517 * deleted it will create it with this default data.
6520 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6522 HRESULT hRes;
6523 IStream *pStream;
6524 WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6525 BYTE pOleStreamHeader [] =
6527 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6528 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6529 0x00, 0x00, 0x00, 0x00
6532 /* Create stream if not present */
6533 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6534 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6536 if(hRes == S_OK)
6538 /* Write default Data */
6539 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6540 IStream_Release(pStream);
6545 /*************************************************************************
6546 * OLECONVERT_CreateCompObjStream [Internal]
6548 * Creates a "\001CompObj" is the destination IStorage if necessary.
6550 * PARAMS
6551 * pStorage [I] The dest IStorage to create the CompObj Stream
6552 * if necessary.
6553 * strOleTypeName [I] The ProgID
6555 * RETURNS
6556 * Success: S_OK
6557 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6559 * NOTES
6560 * This function is used by OleConvertOLESTREAMToIStorage only.
6562 * The stream data is stored in the OLESTREAM and there should be
6563 * no need to recreate the stream. If the stream is manually
6564 * deleted it will attempt to create it by querying the registry.
6568 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6570 IStream *pStream;
6571 HRESULT hStorageRes, hRes = S_OK;
6572 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6573 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6575 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6576 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6578 /* Initialize the CompObj structure */
6579 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6580 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6581 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6584 /* Create a CompObj stream if it doesn't exist */
6585 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6586 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6587 if(hStorageRes == S_OK)
6589 /* copy the OleTypeName to the compobj struct */
6590 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6591 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6593 /* copy the OleTypeName to the compobj struct */
6594 /* Note: in the test made, these were Identical */
6595 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6596 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6598 /* Get the CLSID */
6599 hRes = CLSIDFromProgID16(IStorageCompObj.strProgIDName, &(IStorageCompObj.clsid));
6601 if(hRes == S_OK)
6603 HKEY hKey;
6604 LONG hErr;
6605 /* Get the CLSID Default Name from the Registry */
6606 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6607 if(hErr == ERROR_SUCCESS)
6609 char strTemp[OLESTREAM_MAX_STR_LEN];
6610 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6611 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6612 if(hErr == ERROR_SUCCESS)
6614 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6616 RegCloseKey(hKey);
6620 /* Write CompObj Structure to stream */
6621 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6623 WriteClassStm(pStream,&(IStorageCompObj.clsid));
6625 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6626 if(IStorageCompObj.dwCLSIDNameLength > 0)
6628 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6630 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6631 if(IStorageCompObj.dwOleTypeNameLength > 0)
6633 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6635 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6636 if(IStorageCompObj.dwProgIDNameLength > 0)
6638 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6640 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6641 IStream_Release(pStream);
6643 return hRes;
6647 /*************************************************************************
6648 * OLECONVERT_CreateOlePresStream[Internal]
6650 * Creates the "\002OlePres000" Stream with the Metafile data
6652 * PARAMS
6653 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6654 * dwExtentX [I] Width of the Metafile
6655 * dwExtentY [I] Height of the Metafile
6656 * pData [I] Metafile data
6657 * dwDataLength [I] Size of the Metafile data
6659 * RETURNS
6660 * Success: S_OK
6661 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6663 * NOTES
6664 * This function is used by OleConvertOLESTREAMToIStorage only.
6667 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6669 HRESULT hRes;
6670 IStream *pStream;
6671 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6672 BYTE pOlePresStreamHeader [] =
6674 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6675 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6676 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6677 0x00, 0x00, 0x00, 0x00
6680 BYTE pOlePresStreamHeaderEmpty [] =
6682 0x00, 0x00, 0x00, 0x00,
6683 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6684 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6685 0x00, 0x00, 0x00, 0x00
6688 /* Create the OlePres000 Stream */
6689 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6690 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6692 if(hRes == S_OK)
6694 DWORD nHeaderSize;
6695 OLECONVERT_ISTORAGE_OLEPRES OlePres;
6697 memset(&OlePres, 0, sizeof(OlePres));
6698 /* Do we have any metafile data to save */
6699 if(dwDataLength > 0)
6701 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6702 nHeaderSize = sizeof(pOlePresStreamHeader);
6704 else
6706 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
6707 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
6709 /* Set width and height of the metafile */
6710 OlePres.dwExtentX = dwExtentX;
6711 OlePres.dwExtentY = -dwExtentY;
6713 /* Set Data and Lenght */
6714 if(dwDataLength > sizeof(METAFILEPICT16))
6716 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
6717 OlePres.pData = &(pData[8]);
6719 /* Save OlePres000 Data to Stream */
6720 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
6721 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
6722 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
6723 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
6724 if(OlePres.dwSize > 0)
6726 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
6728 IStream_Release(pStream);
6732 /*************************************************************************
6733 * OLECONVERT_CreateOle10NativeStream [Internal]
6735 * Creates the "\001Ole10Native" Stream (should contain a BMP)
6737 * PARAMS
6738 * pStorage [I] Dest storage to create the stream in
6739 * pData [I] Ole10 Native Data (ex. bmp)
6740 * dwDataLength [I] Size of the Ole10 Native Data
6742 * RETURNS
6743 * Nothing
6745 * NOTES
6746 * This function is used by OleConvertOLESTREAMToIStorage only.
6748 * Might need to verify the data and return appropriate error message
6751 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
6753 HRESULT hRes;
6754 IStream *pStream;
6755 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6757 /* Create the Ole10Native Stream */
6758 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6759 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6761 if(hRes == S_OK)
6763 /* Write info to stream */
6764 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
6765 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
6766 IStream_Release(pStream);
6771 /*************************************************************************
6772 * OLECONVERT_GetOLE10ProgID [Internal]
6774 * Finds the ProgID (or OleTypeID) from the IStorage
6776 * PARAMS
6777 * pStorage [I] The Src IStorage to get the ProgID
6778 * strProgID [I] the ProgID string to get
6779 * dwSize [I] the size of the string
6781 * RETURNS
6782 * Success: S_OK
6783 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6785 * NOTES
6786 * This function is used by OleConvertIStorageToOLESTREAM only.
6790 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
6792 HRESULT hRes;
6793 IStream *pStream;
6794 LARGE_INTEGER iSeekPos;
6795 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
6796 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6798 /* Open the CompObj Stream */
6799 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6800 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6801 if(hRes == S_OK)
6804 /*Get the OleType from the CompObj Stream */
6805 iSeekPos.s.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
6806 iSeekPos.s.HighPart = 0;
6808 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6809 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
6810 iSeekPos.s.LowPart = CompObj.dwCLSIDNameLength;
6811 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6812 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
6813 iSeekPos.s.LowPart = CompObj.dwOleTypeNameLength;
6814 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6816 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
6817 if(*dwSize > 0)
6819 IStream_Read(pStream, strProgID, *dwSize, NULL);
6821 IStream_Release(pStream);
6823 else
6825 STATSTG stat;
6826 LPOLESTR wstrProgID;
6828 /* Get the OleType from the registry */
6829 REFCLSID clsid = &(stat.clsid);
6830 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
6831 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
6832 if(hRes == S_OK)
6834 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
6838 return hRes;
6841 /*************************************************************************
6842 * OLECONVERT_GetOle10PresData [Internal]
6844 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
6846 * PARAMS
6847 * pStorage [I] Src IStroage
6848 * pOleStream [I] Dest OleStream Mem Struct
6850 * RETURNS
6851 * Nothing
6853 * NOTES
6854 * This function is used by OleConvertIStorageToOLESTREAM only.
6856 * Memory allocated for pData must be freed by the caller
6860 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6863 HRESULT hRes;
6864 IStream *pStream;
6865 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6867 /* Initialize Default data for OLESTREAM */
6868 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6869 pOleStreamData[0].dwTypeID = 2;
6870 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6871 pOleStreamData[1].dwTypeID = 0;
6872 pOleStreamData[0].dwMetaFileWidth = 0;
6873 pOleStreamData[0].dwMetaFileHeight = 0;
6874 pOleStreamData[0].pData = NULL;
6875 pOleStreamData[1].pData = NULL;
6877 /* Open Ole10Native Stream */
6878 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6879 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6880 if(hRes == S_OK)
6883 /* Read Size and Data */
6884 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
6885 if(pOleStreamData->dwDataLength > 0)
6887 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
6888 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
6890 IStream_Release(pStream);
6896 /*************************************************************************
6897 * OLECONVERT_GetOle20PresData[Internal]
6899 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
6901 * PARAMS
6902 * pStorage [I] Src IStroage
6903 * pOleStreamData [I] Dest OleStream Mem Struct
6905 * RETURNS
6906 * Nothing
6908 * NOTES
6909 * This function is used by OleConvertIStorageToOLESTREAM only.
6911 * Memory allocated for pData must be freed by the caller
6913 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6915 HRESULT hRes;
6916 IStream *pStream;
6917 OLECONVERT_ISTORAGE_OLEPRES olePress;
6918 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6920 /* Initialize Default data for OLESTREAM */
6921 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6922 pOleStreamData[0].dwTypeID = 2;
6923 pOleStreamData[0].dwMetaFileWidth = 0;
6924 pOleStreamData[0].dwMetaFileHeight = 0;
6925 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
6926 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6927 pOleStreamData[1].dwTypeID = 0;
6928 pOleStreamData[1].dwOleTypeNameLength = 0;
6929 pOleStreamData[1].strOleTypeName[0] = 0;
6930 pOleStreamData[1].dwMetaFileWidth = 0;
6931 pOleStreamData[1].dwMetaFileHeight = 0;
6932 pOleStreamData[1].pData = NULL;
6933 pOleStreamData[1].dwDataLength = 0;
6936 /* Open OlePress000 stream */
6937 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6938 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6939 if(hRes == S_OK)
6941 LARGE_INTEGER iSeekPos;
6942 METAFILEPICT16 MetaFilePict;
6943 char strMetafilePictName[] = "METAFILEPICT";
6945 /* Set the TypeID for a Metafile */
6946 pOleStreamData[1].dwTypeID = 5;
6948 /* Set the OleTypeName to Metafile */
6949 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
6950 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
6952 iSeekPos.s.HighPart = 0;
6953 iSeekPos.s.LowPart = sizeof(olePress.byUnknown1);
6955 /* Get Presentation Data */
6956 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6957 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
6958 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
6959 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
6961 /*Set width and Height */
6962 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
6963 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
6964 if(olePress.dwSize > 0)
6966 /* Set Length */
6967 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
6969 /* Set MetaFilePict struct */
6970 MetaFilePict.mm = 8;
6971 MetaFilePict.xExt = olePress.dwExtentX;
6972 MetaFilePict.yExt = olePress.dwExtentY;
6973 MetaFilePict.hMF = 0;
6975 /* Get Metafile Data */
6976 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
6977 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
6978 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
6980 IStream_Release(pStream);
6984 /*************************************************************************
6985 * OleConvertOLESTREAMToIStorage [OLE32.87]
6987 * Read info on MSDN
6989 * TODO
6990 * DVTARGETDEVICE paramenter is not handled
6991 * Still unsure of some mem fields for OLE 10 Stream
6992 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6993 * and "\001OLE" streams
6996 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
6997 LPOLESTREAM pOleStream,
6998 LPSTORAGE pstg,
6999 const DVTARGETDEVICE* ptd)
7001 int i;
7002 HRESULT hRes=S_OK;
7003 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7005 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7007 if(ptd != NULL)
7009 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7012 if(pstg == NULL || pOleStream == NULL)
7014 hRes = E_INVALIDARG;
7017 if(hRes == S_OK)
7019 /* Load the OLESTREAM to Memory */
7020 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7023 if(hRes == S_OK)
7025 /* Load the OLESTREAM to Memory (part 2)*/
7026 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7029 if(hRes == S_OK)
7032 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7034 /* Do we have the IStorage Data in the OLESTREAM */
7035 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7037 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7038 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7040 else
7042 /* It must be an original OLE 1.0 source */
7043 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7046 else
7048 /* It must be an original OLE 1.0 source */
7049 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7052 /* Create CompObj Stream if necessary */
7053 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7054 if(hRes == S_OK)
7056 /*Create the Ole Stream if necessary */
7057 OLECONVERT_CreateOleStream(pstg);
7062 /* Free allocated memory */
7063 for(i=0; i < 2; i++)
7065 if(pOleStreamData[i].pData != NULL)
7067 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7069 if(pOleStreamData[i].pstrOleObjFileName != NULL)
7071 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7072 pOleStreamData[i].pstrOleObjFileName = NULL;
7075 return hRes;
7078 /*************************************************************************
7079 * OleConvertIStorageToOLESTREAM [OLE32.85]
7081 * Read info on MSDN
7083 * Read info on MSDN
7085 * TODO
7086 * Still unsure of some mem fields for OLE 10 Stream
7087 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7088 * and "\001OLE" streams.
7091 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7092 LPSTORAGE pstg,
7093 LPOLESTREAM pOleStream)
7095 int i;
7096 HRESULT hRes = S_OK;
7097 IStream *pStream;
7098 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7099 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7102 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7104 if(pstg == NULL || pOleStream == NULL)
7106 hRes = E_INVALIDARG;
7108 if(hRes == S_OK)
7110 /* Get the ProgID */
7111 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7112 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7114 if(hRes == S_OK)
7116 /* Was it originally Ole10 */
7117 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7118 if(hRes == S_OK)
7120 IStream_Release(pStream);
7121 /* Get Presentation Data for Ole10Native */
7122 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7124 else
7126 /* Get Presentation Data (OLE20) */
7127 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7130 /* Save OLESTREAM */
7131 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7132 if(hRes == S_OK)
7134 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7139 /* Free allocated memory */
7140 for(i=0; i < 2; i++)
7142 if(pOleStreamData[i].pData != NULL)
7144 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7148 return hRes;
7151 /***********************************************************************
7152 * GetConvertStg (OLE32.68)
7154 HRESULT WINAPI GetConvertStg(LPGUID guid) {
7155 FIXME("(%s), unimplemented stub!\n",debugstr_guid(guid));
7156 return E_FAIL;