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
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
33 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/istorage_compound_file_implementation.asp
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
50 #include "wine/unicode.h"
51 #include "wine/debug.h"
53 #include "storage32.h"
54 #include "ole2.h" /* For Write/ReadClassStm */
57 #include "wine/wingdi16.h"
59 WINE_DEFAULT_DEBUG_CHANNEL(storage);
61 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
62 #define OLESTREAM_ID 0x501
63 #define OLESTREAM_MAX_STR_LEN 255
65 static const char rootPropertyName[] = "Root Entry";
67 /****************************************************************************
68 * Storage32InternalImpl definitions.
70 * Definition of the implementation structure for the IStorage32 interface.
71 * This one implements the IStorage32 interface for storage that are
72 * inside another storage.
74 struct StorageInternalImpl
76 struct StorageBaseImpl base;
78 * There is no specific data for this class.
81 typedef struct StorageInternalImpl StorageInternalImpl;
83 /* Method definitions for the Storage32InternalImpl class. */
84 static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
85 DWORD openFlags, ULONG rootTropertyIndex);
86 static void StorageImpl_Destroy(StorageBaseImpl* iface);
87 static void* StorageImpl_GetBigBlock(StorageImpl* This, ULONG blockIndex);
88 static void* StorageImpl_GetROBigBlock(StorageImpl* This, ULONG blockIndex);
89 static void StorageImpl_ReleaseBigBlock(StorageImpl* This, void* pBigBlock);
90 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
91 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
92 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
93 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
94 static void StorageImpl_SaveFileHeader(StorageImpl* This);
96 static void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm);
98 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
99 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
100 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
101 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
102 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
104 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
105 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
106 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
108 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
111 /* OLESTREAM memory structure to use for Get and Put Routines */
112 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
117 DWORD dwOleTypeNameLength;
118 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
119 CHAR *pstrOleObjFileName;
120 DWORD dwOleObjFileNameLength;
121 DWORD dwMetaFileWidth;
122 DWORD dwMetaFileHeight;
123 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
126 }OLECONVERT_OLESTREAM_DATA;
128 /* CompObj Stream structure */
129 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
134 DWORD dwCLSIDNameLength;
135 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
136 DWORD dwOleTypeNameLength;
137 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
138 DWORD dwProgIDNameLength;
139 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
141 }OLECONVERT_ISTORAGE_COMPOBJ;
144 /* Ole Presention Stream structure */
145 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
153 }OLECONVERT_ISTORAGE_OLEPRES;
157 /***********************************************************************
158 * Forward declaration of internal functions used by the method DestroyElement
160 static HRESULT deleteStorageProperty(
161 StorageImpl *parentStorage,
162 ULONG foundPropertyIndexToDelete,
163 StgProperty propertyToDelete);
165 static HRESULT deleteStreamProperty(
166 StorageImpl *parentStorage,
167 ULONG foundPropertyIndexToDelete,
168 StgProperty propertyToDelete);
170 static HRESULT findPlaceholder(
171 StorageImpl *storage,
172 ULONG propertyIndexToStore,
173 ULONG storagePropertyIndex,
176 static HRESULT adjustPropertyChain(
178 StgProperty propertyToDelete,
179 StgProperty parentProperty,
180 ULONG parentPropertyId,
183 /***********************************************************************
184 * Declaration of the functions used to manipulate StgProperty
187 static ULONG getFreeProperty(
188 StorageImpl *storage);
190 static void updatePropertyChain(
191 StorageImpl *storage,
192 ULONG newPropertyIndex,
193 StgProperty newProperty);
195 static LONG propertyNameCmp(
196 const OLECHAR *newProperty,
197 const OLECHAR *currentProperty);
200 /***********************************************************************
201 * Declaration of miscellaneous functions...
203 static HRESULT validateSTGM(DWORD stgmValue);
205 static DWORD GetShareModeFromSTGM(DWORD stgm);
206 static DWORD GetAccessModeFromSTGM(DWORD stgm);
207 static DWORD GetCreationModeFromSTGM(DWORD stgm);
209 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
212 /****************************************************************************
213 * IEnumSTATSTGImpl definitions.
215 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
216 * This class allows iterating through the content of a storage and to find
217 * specific items inside it.
219 struct IEnumSTATSTGImpl
221 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
222 * since we want to cast this in an IEnumSTATSTG pointer */
224 LONG ref; /* Reference count */
225 StorageImpl* parentStorage; /* Reference to the parent storage */
226 ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
229 * The current implementation of the IEnumSTATSTGImpl class uses a stack
230 * to walk the property sets to get the content of a storage. This stack
231 * is implemented by the following 3 data members
237 #define ENUMSTATSGT_SIZE_INCREMENT 10
241 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
242 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
243 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
244 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
245 static ULONG IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl* This, const OLECHAR* lpszPropName,
246 StgProperty* buffer);
247 static INT IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl *This, ULONG childProperty,
248 StgProperty *currentProperty, ULONG *propertyId);
251 /************************************************************************
252 ** Storage32BaseImpl implementatiion
255 /************************************************************************
256 * Storage32BaseImpl_QueryInterface (IUnknown)
258 * This method implements the common QueryInterface for all IStorage32
259 * implementations contained in this file.
261 * See Windows documentation for more details on IUnknown methods.
263 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
268 StorageBaseImpl *This = (StorageBaseImpl *)iface;
270 * Perform a sanity check on the parameters.
272 if ( (This==0) || (ppvObject==0) )
276 * Initialize the return parameter.
281 * Compare the riid with the interface IDs implemented by this object.
283 if (IsEqualGUID(&IID_IUnknown, riid) ||
284 IsEqualGUID(&IID_IStorage, riid))
286 *ppvObject = (IStorage*)This;
288 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
290 *ppvObject = (IStorage*)&This->pssVtbl;
294 * Check that we obtained an interface.
297 return E_NOINTERFACE;
300 * Query Interface always increases the reference count by one when it is
303 IStorage_AddRef(iface);
308 /************************************************************************
309 * Storage32BaseImpl_AddRef (IUnknown)
311 * This method implements the common AddRef for all IStorage32
312 * implementations contained in this file.
314 * See Windows documentation for more details on IUnknown methods.
316 static ULONG WINAPI StorageBaseImpl_AddRef(
319 StorageBaseImpl *This = (StorageBaseImpl *)iface;
320 ULONG ref = InterlockedIncrement(&This->ref);
322 TRACE("(%p) AddRef to %d\n", This, ref);
327 /************************************************************************
328 * Storage32BaseImpl_Release (IUnknown)
330 * This method implements the common Release for all IStorage32
331 * implementations contained in this file.
333 * See Windows documentation for more details on IUnknown methods.
335 static ULONG WINAPI StorageBaseImpl_Release(
338 StorageBaseImpl *This = (StorageBaseImpl *)iface;
340 * Decrease the reference count on this object.
342 ULONG ref = InterlockedDecrement(&This->ref);
344 TRACE("(%p) ReleaseRef to %d\n", This, ref);
347 * If the reference count goes down to 0, perform suicide.
352 * Since we are using a system of base-classes, we want to call the
353 * destructor of the appropriate derived class. To do this, we are
354 * using virtual functions to implement the destructor.
356 This->v_destructor(This);
362 /************************************************************************
363 * Storage32BaseImpl_OpenStream (IStorage)
365 * This method will open the specified stream object from the current storage.
367 * See Windows documentation for more details on IStorage methods.
369 static HRESULT WINAPI StorageBaseImpl_OpenStream(
371 const OLECHAR* pwcsName, /* [string][in] */
372 void* reserved1, /* [unique][in] */
373 DWORD grfMode, /* [in] */
374 DWORD reserved2, /* [in] */
375 IStream** ppstm) /* [out] */
377 StorageBaseImpl *This = (StorageBaseImpl *)iface;
378 IEnumSTATSTGImpl* propertyEnumeration;
379 StgStreamImpl* newStream;
380 StgProperty currentProperty;
381 ULONG foundPropertyIndex;
382 HRESULT res = STG_E_UNKNOWN;
384 TRACE("(%p, %s, %p, %x, %d, %p)\n",
385 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
388 * Perform a sanity check on the parameters.
390 if ( (pwcsName==NULL) || (ppstm==0) )
397 * Initialize the out parameter
402 * Validate the STGM flags
404 if ( FAILED( validateSTGM(grfMode) ) ||
405 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
407 res = STG_E_INVALIDFLAG;
414 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
416 res = STG_E_INVALIDFUNCTION;
421 * Check that we're compatible with the parent's storage mode, but
422 * only if we are not in transacted mode
424 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
425 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
427 res = STG_E_ACCESSDENIED;
433 * Create a property enumeration to search the properties
435 propertyEnumeration = IEnumSTATSTGImpl_Construct(
436 This->ancestorStorage,
437 This->rootPropertySetIndex);
440 * Search the enumeration for the property with the given name
442 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
448 * Delete the property enumeration since we don't need it anymore
450 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
453 * If it was found, construct the stream object and return a pointer to it.
455 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
456 (currentProperty.propertyType==PROPTYPE_STREAM) )
458 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
462 newStream->grfMode = grfMode;
463 *ppstm = (IStream*)newStream;
466 * Since we are returning a pointer to the interface, we have to
467 * nail down the reference.
469 IStream_AddRef(*ppstm);
472 * add us to the storage's list of active streams
475 StorageBaseImpl_AddStream(This,newStream);
485 res = STG_E_FILENOTFOUND;
489 TRACE("<-- IStream %p\n", *ppstm);
490 TRACE("<-- %08x\n", res);
494 /************************************************************************
495 * Storage32BaseImpl_OpenStorage (IStorage)
497 * This method will open a new storage object from the current storage.
499 * See Windows documentation for more details on IStorage methods.
501 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
503 const OLECHAR* pwcsName, /* [string][unique][in] */
504 IStorage* pstgPriority, /* [unique][in] */
505 DWORD grfMode, /* [in] */
506 SNB snbExclude, /* [unique][in] */
507 DWORD reserved, /* [in] */
508 IStorage** ppstg) /* [out] */
510 StorageBaseImpl *This = (StorageBaseImpl *)iface;
511 StorageInternalImpl* newStorage;
512 IEnumSTATSTGImpl* propertyEnumeration;
513 StgProperty currentProperty;
514 ULONG foundPropertyIndex;
515 HRESULT res = STG_E_UNKNOWN;
517 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
518 iface, debugstr_w(pwcsName), pstgPriority,
519 grfMode, snbExclude, reserved, ppstg);
522 * Perform a sanity check on the parameters.
524 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
531 if (snbExclude != NULL)
533 res = STG_E_INVALIDPARAMETER;
538 * Validate the STGM flags
540 if ( FAILED( validateSTGM(grfMode) ))
542 res = STG_E_INVALIDFLAG;
549 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
550 (grfMode & STGM_DELETEONRELEASE) ||
551 (grfMode & STGM_PRIORITY) )
553 res = STG_E_INVALIDFUNCTION;
558 * Check that we're compatible with the parent's storage mode,
559 * but only if we are not transacted
561 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
562 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
564 res = STG_E_ACCESSDENIED;
570 * Initialize the out parameter
575 * Create a property enumeration to search the properties
577 propertyEnumeration = IEnumSTATSTGImpl_Construct(
578 This->ancestorStorage,
579 This->rootPropertySetIndex);
582 * Search the enumeration for the property with the given name
584 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
590 * Delete the property enumeration since we don't need it anymore
592 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
595 * If it was found, construct the stream object and return a pointer to it.
597 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
598 (currentProperty.propertyType==PROPTYPE_STORAGE) )
601 * Construct a new Storage object
603 newStorage = StorageInternalImpl_Construct(
604 This->ancestorStorage,
610 *ppstg = (IStorage*)newStorage;
613 * Since we are returning a pointer to the interface,
614 * we have to nail down the reference.
616 StorageBaseImpl_AddRef(*ppstg);
622 res = STG_E_INSUFFICIENTMEMORY;
626 res = STG_E_FILENOTFOUND;
629 TRACE("<-- %08x\n", res);
633 /************************************************************************
634 * Storage32BaseImpl_EnumElements (IStorage)
636 * This method will create an enumerator object that can be used to
637 * retrieve informatino about all the properties in the storage object.
639 * See Windows documentation for more details on IStorage methods.
641 static HRESULT WINAPI StorageBaseImpl_EnumElements(
643 DWORD reserved1, /* [in] */
644 void* reserved2, /* [size_is][unique][in] */
645 DWORD reserved3, /* [in] */
646 IEnumSTATSTG** ppenum) /* [out] */
648 StorageBaseImpl *This = (StorageBaseImpl *)iface;
649 IEnumSTATSTGImpl* newEnum;
651 TRACE("(%p, %d, %p, %d, %p)\n",
652 iface, reserved1, reserved2, reserved3, ppenum);
655 * Perform a sanity check on the parameters.
657 if ( (This==0) || (ppenum==0))
661 * Construct the enumerator.
663 newEnum = IEnumSTATSTGImpl_Construct(
664 This->ancestorStorage,
665 This->rootPropertySetIndex);
669 *ppenum = (IEnumSTATSTG*)newEnum;
672 * Don't forget to nail down a reference to the new object before
675 IEnumSTATSTG_AddRef(*ppenum);
680 return E_OUTOFMEMORY;
683 /************************************************************************
684 * Storage32BaseImpl_Stat (IStorage)
686 * This method will retrieve information about this storage object.
688 * See Windows documentation for more details on IStorage methods.
690 static HRESULT WINAPI StorageBaseImpl_Stat(
692 STATSTG* pstatstg, /* [out] */
693 DWORD grfStatFlag) /* [in] */
695 StorageBaseImpl *This = (StorageBaseImpl *)iface;
696 StgProperty curProperty;
698 HRESULT res = STG_E_UNKNOWN;
700 TRACE("(%p, %p, %x)\n",
701 iface, pstatstg, grfStatFlag);
704 * Perform a sanity check on the parameters.
706 if ( (This==0) || (pstatstg==0))
713 * Read the information from the property.
715 readSuccessful = StorageImpl_ReadProperty(
716 This->ancestorStorage,
717 This->rootPropertySetIndex,
722 StorageUtl_CopyPropertyToSTATSTG(
727 pstatstg->grfMode = This->openFlags;
738 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
740 TRACE("<-- %08x\n", res);
744 /************************************************************************
745 * Storage32BaseImpl_RenameElement (IStorage)
747 * This method will rename the specified element.
749 * See Windows documentation for more details on IStorage methods.
751 * Implementation notes: The method used to rename consists of creating a clone
752 * of the deleted StgProperty object setting it with the new name and to
753 * perform a DestroyElement of the old StgProperty.
755 static HRESULT WINAPI StorageBaseImpl_RenameElement(
757 const OLECHAR* pwcsOldName, /* [in] */
758 const OLECHAR* pwcsNewName) /* [in] */
760 StorageBaseImpl *This = (StorageBaseImpl *)iface;
761 IEnumSTATSTGImpl* propertyEnumeration;
762 StgProperty currentProperty;
763 ULONG foundPropertyIndex;
765 TRACE("(%p, %s, %s)\n",
766 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
769 * Create a property enumeration to search the properties
771 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
772 This->rootPropertySetIndex);
775 * Search the enumeration for the new property name
777 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
781 if (foundPropertyIndex != PROPERTY_NULL)
784 * There is already a property with the new name
786 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
787 return STG_E_FILEALREADYEXISTS;
790 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
793 * Search the enumeration for the old property name
795 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
800 * Delete the property enumeration since we don't need it anymore
802 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
804 if (foundPropertyIndex != PROPERTY_NULL)
806 StgProperty renamedProperty;
807 ULONG renamedPropertyIndex;
810 * Setup a new property for the renamed property
812 renamedProperty.sizeOfNameString =
813 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
815 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
816 return STG_E_INVALIDNAME;
818 strcpyW(renamedProperty.name, pwcsNewName);
820 renamedProperty.propertyType = currentProperty.propertyType;
821 renamedProperty.startingBlock = currentProperty.startingBlock;
822 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
823 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
825 renamedProperty.previousProperty = PROPERTY_NULL;
826 renamedProperty.nextProperty = PROPERTY_NULL;
829 * Bring the dirProperty link in case it is a storage and in which
830 * case the renamed storage elements don't require to be reorganized.
832 renamedProperty.dirProperty = currentProperty.dirProperty;
834 /* call CoFileTime to get the current time
835 renamedProperty.timeStampS1
836 renamedProperty.timeStampD1
837 renamedProperty.timeStampS2
838 renamedProperty.timeStampD2
839 renamedProperty.propertyUniqueID
843 * Obtain a free property in the property chain
845 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
848 * Save the new property into the new property spot
850 StorageImpl_WriteProperty(
851 This->ancestorStorage,
852 renamedPropertyIndex,
856 * Find a spot in the property chain for our newly created property.
860 renamedPropertyIndex,
864 * At this point the renamed property has been inserted in the tree,
865 * now, before Destroying the old property we must zero its dirProperty
866 * otherwise the DestroyProperty below will zap it all and we do not want
868 * Also, we fake that the old property is a storage so the DestroyProperty
869 * will not do a SetSize(0) on the stream data.
871 * This means that we need to tweak the StgProperty if it is a stream or a
874 StorageImpl_ReadProperty(This->ancestorStorage,
878 currentProperty.dirProperty = PROPERTY_NULL;
879 currentProperty.propertyType = PROPTYPE_STORAGE;
880 StorageImpl_WriteProperty(
881 This->ancestorStorage,
886 * Invoke Destroy to get rid of the ole property and automatically redo
887 * the linking of its previous and next members...
889 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
895 * There is no property with the old name
897 return STG_E_FILENOTFOUND;
903 /************************************************************************
904 * Storage32BaseImpl_CreateStream (IStorage)
906 * This method will create a stream object within this storage
908 * See Windows documentation for more details on IStorage methods.
910 static HRESULT WINAPI StorageBaseImpl_CreateStream(
912 const OLECHAR* pwcsName, /* [string][in] */
913 DWORD grfMode, /* [in] */
914 DWORD reserved1, /* [in] */
915 DWORD reserved2, /* [in] */
916 IStream** ppstm) /* [out] */
918 StorageBaseImpl *This = (StorageBaseImpl *)iface;
919 IEnumSTATSTGImpl* propertyEnumeration;
920 StgStreamImpl* newStream;
921 StgProperty currentProperty, newStreamProperty;
922 ULONG foundPropertyIndex, newPropertyIndex;
924 TRACE("(%p, %s, %x, %d, %d, %p)\n",
925 iface, debugstr_w(pwcsName), grfMode,
926 reserved1, reserved2, ppstm);
929 * Validate parameters
932 return STG_E_INVALIDPOINTER;
935 return STG_E_INVALIDNAME;
937 if (reserved1 || reserved2)
938 return STG_E_INVALIDPARAMETER;
941 * Validate the STGM flags
943 if ( FAILED( validateSTGM(grfMode) ))
944 return STG_E_INVALIDFLAG;
946 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
947 return STG_E_INVALIDFLAG;
952 if ((grfMode & STGM_DELETEONRELEASE) ||
953 (grfMode & STGM_TRANSACTED))
954 return STG_E_INVALIDFUNCTION;
957 * Check that we're compatible with the parent's storage mode
958 * if not in transacted mode
960 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
961 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
962 return STG_E_ACCESSDENIED;
966 * Initialize the out parameter
971 * Create a property enumeration to search the properties
973 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
974 This->rootPropertySetIndex);
976 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
980 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
982 if (foundPropertyIndex != PROPERTY_NULL)
985 * An element with this name already exists
987 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
989 IStorage_DestroyElement(iface, pwcsName);
992 return STG_E_FILEALREADYEXISTS;
994 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
996 WARN("read-only storage\n");
997 return STG_E_ACCESSDENIED;
1001 * memset the empty property
1003 memset(&newStreamProperty, 0, sizeof(StgProperty));
1005 newStreamProperty.sizeOfNameString =
1006 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1008 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1009 return STG_E_INVALIDNAME;
1011 strcpyW(newStreamProperty.name, pwcsName);
1013 newStreamProperty.propertyType = PROPTYPE_STREAM;
1014 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1015 newStreamProperty.size.u.LowPart = 0;
1016 newStreamProperty.size.u.HighPart = 0;
1018 newStreamProperty.previousProperty = PROPERTY_NULL;
1019 newStreamProperty.nextProperty = PROPERTY_NULL;
1020 newStreamProperty.dirProperty = PROPERTY_NULL;
1022 /* call CoFileTime to get the current time
1023 newStreamProperty.timeStampS1
1024 newStreamProperty.timeStampD1
1025 newStreamProperty.timeStampS2
1026 newStreamProperty.timeStampD2
1029 /* newStreamProperty.propertyUniqueID */
1032 * Get a free property or create a new one
1034 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1037 * Save the new property into the new property spot
1039 StorageImpl_WriteProperty(
1040 This->ancestorStorage,
1042 &newStreamProperty);
1045 * Find a spot in the property chain for our newly created property.
1047 updatePropertyChain(
1053 * Open the stream to return it.
1055 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1059 *ppstm = (IStream*)newStream;
1062 * Since we are returning a pointer to the interface, we have to nail down
1065 IStream_AddRef(*ppstm);
1067 /* add us to the storage's list of active streams
1069 StorageBaseImpl_AddStream(This,newStream);
1074 return STG_E_INSUFFICIENTMEMORY;
1080 /************************************************************************
1081 * Storage32BaseImpl_SetClass (IStorage)
1083 * This method will write the specified CLSID in the property of this
1086 * See Windows documentation for more details on IStorage methods.
1088 static HRESULT WINAPI StorageBaseImpl_SetClass(
1090 REFCLSID clsid) /* [in] */
1092 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1093 HRESULT hRes = E_FAIL;
1094 StgProperty curProperty;
1097 TRACE("(%p, %p)\n", iface, clsid);
1099 success = StorageImpl_ReadProperty(This->ancestorStorage,
1100 This->rootPropertySetIndex,
1104 curProperty.propertyUniqueID = *clsid;
1106 success = StorageImpl_WriteProperty(This->ancestorStorage,
1107 This->rootPropertySetIndex,
1116 /************************************************************************
1117 ** Storage32Impl implementation
1120 /************************************************************************
1121 * Storage32Impl_CreateStorage (IStorage)
1123 * This method will create the storage object within the provided storage.
1125 * See Windows documentation for more details on IStorage methods.
1127 static HRESULT WINAPI StorageImpl_CreateStorage(
1129 const OLECHAR *pwcsName, /* [string][in] */
1130 DWORD grfMode, /* [in] */
1131 DWORD reserved1, /* [in] */
1132 DWORD reserved2, /* [in] */
1133 IStorage **ppstg) /* [out] */
1135 StorageImpl* const This=(StorageImpl*)iface;
1137 IEnumSTATSTGImpl *propertyEnumeration;
1138 StgProperty currentProperty;
1139 StgProperty newProperty;
1140 ULONG foundPropertyIndex;
1141 ULONG newPropertyIndex;
1144 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1145 iface, debugstr_w(pwcsName), grfMode,
1146 reserved1, reserved2, ppstg);
1149 * Validate parameters
1152 return STG_E_INVALIDPOINTER;
1155 return STG_E_INVALIDNAME;
1158 * Initialize the out parameter
1163 * Validate the STGM flags
1165 if ( FAILED( validateSTGM(grfMode) ) ||
1166 (grfMode & STGM_DELETEONRELEASE) )
1168 WARN("bad grfMode: 0x%x\n", grfMode);
1169 return STG_E_INVALIDFLAG;
1173 * Check that we're compatible with the parent's storage mode
1175 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1177 WARN("access denied\n");
1178 return STG_E_ACCESSDENIED;
1182 * Create a property enumeration and search the properties
1184 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1185 This->base.rootPropertySetIndex);
1187 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1190 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1192 if (foundPropertyIndex != PROPERTY_NULL)
1195 * An element with this name already exists
1197 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1198 IStorage_DestroyElement(iface, pwcsName);
1201 WARN("file already exists\n");
1202 return STG_E_FILEALREADYEXISTS;
1205 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1207 WARN("read-only storage\n");
1208 return STG_E_ACCESSDENIED;
1212 * memset the empty property
1214 memset(&newProperty, 0, sizeof(StgProperty));
1216 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1218 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1220 FIXME("name too long\n");
1221 return STG_E_INVALIDNAME;
1224 strcpyW(newProperty.name, pwcsName);
1226 newProperty.propertyType = PROPTYPE_STORAGE;
1227 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1228 newProperty.size.u.LowPart = 0;
1229 newProperty.size.u.HighPart = 0;
1231 newProperty.previousProperty = PROPERTY_NULL;
1232 newProperty.nextProperty = PROPERTY_NULL;
1233 newProperty.dirProperty = PROPERTY_NULL;
1235 /* call CoFileTime to get the current time
1236 newProperty.timeStampS1
1237 newProperty.timeStampD1
1238 newProperty.timeStampS2
1239 newProperty.timeStampD2
1242 /* newStorageProperty.propertyUniqueID */
1245 * Obtain a free property in the property chain
1247 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1250 * Save the new property into the new property spot
1252 StorageImpl_WriteProperty(
1253 This->base.ancestorStorage,
1258 * Find a spot in the property chain for our newly created property.
1260 updatePropertyChain(
1266 * Open it to get a pointer to return.
1268 hr = IStorage_OpenStorage(
1270 (const OLECHAR*)pwcsName,
1277 if( (hr != S_OK) || (*ppstg == NULL))
1287 /***************************************************************************
1291 * Get a free property or create a new one.
1293 static ULONG getFreeProperty(
1294 StorageImpl *storage)
1296 ULONG currentPropertyIndex = 0;
1297 ULONG newPropertyIndex = PROPERTY_NULL;
1298 BOOL readSuccessful = TRUE;
1299 StgProperty currentProperty;
1304 * Start by reading the root property
1306 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1307 currentPropertyIndex,
1311 if (currentProperty.sizeOfNameString == 0)
1314 * The property existis and is available, we found it.
1316 newPropertyIndex = currentPropertyIndex;
1322 * We exhausted the property list, we will create more space below
1324 newPropertyIndex = currentPropertyIndex;
1326 currentPropertyIndex++;
1328 } while (newPropertyIndex == PROPERTY_NULL);
1331 * grow the property chain
1333 if (! readSuccessful)
1335 StgProperty emptyProperty;
1336 ULARGE_INTEGER newSize;
1337 ULONG propertyIndex;
1338 ULONG lastProperty = 0;
1339 ULONG blockCount = 0;
1342 * obtain the new count of property blocks
1344 blockCount = BlockChainStream_GetCount(
1345 storage->base.ancestorStorage->rootBlockChain)+1;
1348 * initialize the size used by the property stream
1350 newSize.u.HighPart = 0;
1351 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1354 * add a property block to the property chain
1356 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1359 * memset the empty property in order to initialize the unused newly
1362 memset(&emptyProperty, 0, sizeof(StgProperty));
1367 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1370 propertyIndex = newPropertyIndex;
1371 propertyIndex < lastProperty;
1374 StorageImpl_WriteProperty(
1375 storage->base.ancestorStorage,
1381 return newPropertyIndex;
1384 /****************************************************************************
1388 * Case insensitive comparaison of StgProperty.name by first considering
1391 * Returns <0 when newPrpoerty < currentProperty
1392 * >0 when newPrpoerty > currentProperty
1393 * 0 when newPrpoerty == currentProperty
1395 static LONG propertyNameCmp(
1396 const OLECHAR *newProperty,
1397 const OLECHAR *currentProperty)
1399 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1404 * We compare the string themselves only when they are of the same length
1406 diff = lstrcmpiW( newProperty, currentProperty);
1412 /****************************************************************************
1416 * Properly link this new element in the property chain.
1418 static void updatePropertyChain(
1419 StorageImpl *storage,
1420 ULONG newPropertyIndex,
1421 StgProperty newProperty)
1423 StgProperty currentProperty;
1426 * Read the root property
1428 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1429 storage->base.rootPropertySetIndex,
1432 if (currentProperty.dirProperty != PROPERTY_NULL)
1435 * The root storage contains some element, therefore, start the research
1436 * for the appropriate location.
1439 ULONG current, next, previous, currentPropertyId;
1442 * Keep the StgProperty sequence number of the storage first property
1444 currentPropertyId = currentProperty.dirProperty;
1449 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1450 currentProperty.dirProperty,
1453 previous = currentProperty.previousProperty;
1454 next = currentProperty.nextProperty;
1455 current = currentPropertyId;
1459 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1463 if (previous != PROPERTY_NULL)
1465 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1472 currentProperty.previousProperty = newPropertyIndex;
1473 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1481 if (next != PROPERTY_NULL)
1483 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1490 currentProperty.nextProperty = newPropertyIndex;
1491 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1500 * Trying to insert an item with the same name in the
1501 * subtree structure.
1506 previous = currentProperty.previousProperty;
1507 next = currentProperty.nextProperty;
1513 * The root storage is empty, link the new property to its dir property
1515 currentProperty.dirProperty = newPropertyIndex;
1516 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1517 storage->base.rootPropertySetIndex,
1523 /*************************************************************************
1526 static HRESULT WINAPI StorageImpl_CopyTo(
1528 DWORD ciidExclude, /* [in] */
1529 const IID* rgiidExclude, /* [size_is][unique][in] */
1530 SNB snbExclude, /* [unique][in] */
1531 IStorage* pstgDest) /* [unique][in] */
1533 IEnumSTATSTG *elements = 0;
1534 STATSTG curElement, strStat;
1536 IStorage *pstgTmp, *pstgChild;
1537 IStream *pstrTmp, *pstrChild;
1539 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1540 FIXME("Exclude option not implemented\n");
1542 TRACE("(%p, %d, %p, %p, %p)\n",
1543 iface, ciidExclude, rgiidExclude,
1544 snbExclude, pstgDest);
1547 * Perform a sanity check
1549 if ( pstgDest == 0 )
1550 return STG_E_INVALIDPOINTER;
1553 * Enumerate the elements
1555 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1563 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1564 IStorage_SetClass( pstgDest, &curElement.clsid );
1569 * Obtain the next element
1571 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1573 if ( hr == S_FALSE )
1575 hr = S_OK; /* done, every element has been copied */
1579 if (curElement.type == STGTY_STORAGE)
1582 * open child source storage
1584 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1585 STGM_READ|STGM_SHARE_EXCLUSIVE,
1586 NULL, 0, &pstgChild );
1592 * Check if destination storage is not a child of the source
1593 * storage, which will cause an infinite loop
1595 if (pstgChild == pstgDest)
1597 IEnumSTATSTG_Release(elements);
1599 return STG_E_ACCESSDENIED;
1603 * create a new storage in destination storage
1605 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1606 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1610 * if it already exist, don't create a new one use this one
1612 if (hr == STG_E_FILEALREADYEXISTS)
1614 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1615 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1616 NULL, 0, &pstgTmp );
1624 * do the copy recursively
1626 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1627 snbExclude, pstgTmp );
1629 IStorage_Release( pstgTmp );
1630 IStorage_Release( pstgChild );
1632 else if (curElement.type == STGTY_STREAM)
1635 * create a new stream in destination storage. If the stream already
1636 * exist, it will be deleted and a new one will be created.
1638 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1639 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1646 * open child stream storage
1648 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1649 STGM_READ|STGM_SHARE_EXCLUSIVE,
1656 * Get the size of the source stream
1658 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1661 * Set the size of the destination stream.
1663 IStream_SetSize(pstrTmp, strStat.cbSize);
1668 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1671 IStream_Release( pstrTmp );
1672 IStream_Release( pstrChild );
1676 WARN("unknown element type: %d\n", curElement.type);
1679 } while (hr == S_OK);
1684 IEnumSTATSTG_Release(elements);
1689 /*************************************************************************
1690 * MoveElementTo (IStorage)
1692 static HRESULT WINAPI StorageImpl_MoveElementTo(
1694 const OLECHAR *pwcsName, /* [string][in] */
1695 IStorage *pstgDest, /* [unique][in] */
1696 const OLECHAR *pwcsNewName,/* [string][in] */
1697 DWORD grfFlags) /* [in] */
1699 FIXME("(%p %s %p %s %u): stub\n", iface,
1700 debugstr_w(pwcsName), pstgDest,
1701 debugstr_w(pwcsNewName), grfFlags);
1705 /*************************************************************************
1708 * Ensures that any changes made to a storage object open in transacted mode
1709 * are reflected in the parent storage
1712 * Wine doesn't implement transacted mode, which seems to be a basic
1713 * optimization, so we can ignore this stub for now.
1715 static HRESULT WINAPI StorageImpl_Commit(
1717 DWORD grfCommitFlags)/* [in] */
1719 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1723 /*************************************************************************
1726 * Discard all changes that have been made since the last commit operation
1728 static HRESULT WINAPI StorageImpl_Revert(
1731 FIXME("(%p): stub\n", iface);
1735 /*************************************************************************
1736 * DestroyElement (IStorage)
1738 * Strategy: This implementation is built this way for simplicity not for speed.
1739 * I always delete the topmost element of the enumeration and adjust
1740 * the deleted element pointer all the time. This takes longer to
1741 * do but allow to reinvoke DestroyElement whenever we encounter a
1742 * storage object. The optimisation resides in the usage of another
1743 * enumeration strategy that would give all the leaves of a storage
1744 * first. (postfix order)
1746 static HRESULT WINAPI StorageImpl_DestroyElement(
1748 const OLECHAR *pwcsName)/* [string][in] */
1750 StorageImpl* const This=(StorageImpl*)iface;
1752 IEnumSTATSTGImpl* propertyEnumeration;
1755 StgProperty propertyToDelete;
1756 StgProperty parentProperty;
1757 ULONG foundPropertyIndexToDelete;
1758 ULONG typeOfRelation;
1759 ULONG parentPropertyId = 0;
1762 iface, debugstr_w(pwcsName));
1765 * Perform a sanity check on the parameters.
1768 return STG_E_INVALIDPOINTER;
1771 * Create a property enumeration to search the property with the given name
1773 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1774 This->base.ancestorStorage,
1775 This->base.rootPropertySetIndex);
1777 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1778 propertyEnumeration,
1782 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1784 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1786 return STG_E_FILENOTFOUND;
1790 * Find the parent property of the property to delete (the one that
1791 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1792 * the parent is This. Otherwise, the parent is one of its sibling...
1796 * First, read This's StgProperty..
1798 res = StorageImpl_ReadProperty(
1799 This->base.ancestorStorage,
1800 This->base.rootPropertySetIndex,
1806 * Second, check to see if by any chance the actual storage (This) is not
1807 * the parent of the property to delete... We never know...
1809 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1812 * Set data as it would have been done in the else part...
1814 typeOfRelation = PROPERTY_RELATION_DIR;
1815 parentPropertyId = This->base.rootPropertySetIndex;
1820 * Create a property enumeration to search the parent properties, and
1821 * delete it once done.
1823 IEnumSTATSTGImpl* propertyEnumeration2;
1825 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1826 This->base.ancestorStorage,
1827 This->base.rootPropertySetIndex);
1829 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1830 propertyEnumeration2,
1831 foundPropertyIndexToDelete,
1835 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1838 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1840 hr = deleteStorageProperty(
1842 foundPropertyIndexToDelete,
1845 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1847 hr = deleteStreamProperty(
1849 foundPropertyIndexToDelete,
1857 * Adjust the property chain
1859 hr = adjustPropertyChain(
1870 /************************************************************************
1871 * StorageImpl_Stat (IStorage)
1873 * This method will retrieve information about this storage object.
1875 * See Windows documentation for more details on IStorage methods.
1877 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1878 STATSTG* pstatstg, /* [out] */
1879 DWORD grfStatFlag) /* [in] */
1881 StorageImpl* const This = (StorageImpl*)iface;
1882 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1884 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1886 CoTaskMemFree(pstatstg->pwcsName);
1887 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1888 strcpyW(pstatstg->pwcsName, This->pwcsName);
1894 /******************************************************************************
1895 * Internal stream list handlers
1898 static void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1900 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1901 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1904 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1906 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1907 list_remove(&(strm->StrmListEntry));
1910 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1912 struct list *cur, *cur2;
1913 StgStreamImpl *strm=NULL;
1915 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1916 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1917 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1918 strm->parentStorage = NULL;
1924 /*********************************************************************
1928 * Perform the deletion of a complete storage node
1931 static HRESULT deleteStorageProperty(
1932 StorageImpl *parentStorage,
1933 ULONG indexOfPropertyToDelete,
1934 StgProperty propertyToDelete)
1936 IEnumSTATSTG *elements = 0;
1937 IStorage *childStorage = 0;
1938 STATSTG currentElement;
1940 HRESULT destroyHr = S_OK;
1943 * Open the storage and enumerate it
1945 hr = StorageBaseImpl_OpenStorage(
1946 (IStorage*)parentStorage,
1947 propertyToDelete.name,
1949 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1960 * Enumerate the elements
1962 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1967 * Obtain the next element
1969 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1972 destroyHr = StorageImpl_DestroyElement(
1973 (IStorage*)childStorage,
1974 (OLECHAR*)currentElement.pwcsName);
1976 CoTaskMemFree(currentElement.pwcsName);
1980 * We need to Reset the enumeration every time because we delete elements
1981 * and the enumeration could be invalid
1983 IEnumSTATSTG_Reset(elements);
1985 } while ((hr == S_OK) && (destroyHr == S_OK));
1988 * Invalidate the property by zeroing its name member.
1990 propertyToDelete.sizeOfNameString = 0;
1992 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1993 indexOfPropertyToDelete,
1996 IStorage_Release(childStorage);
1997 IEnumSTATSTG_Release(elements);
2002 /*********************************************************************
2006 * Perform the deletion of a stream node
2009 static HRESULT deleteStreamProperty(
2010 StorageImpl *parentStorage,
2011 ULONG indexOfPropertyToDelete,
2012 StgProperty propertyToDelete)
2016 ULARGE_INTEGER size;
2018 size.u.HighPart = 0;
2021 hr = StorageBaseImpl_OpenStream(
2022 (IStorage*)parentStorage,
2023 (OLECHAR*)propertyToDelete.name,
2025 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2037 hr = IStream_SetSize(pis, size);
2045 * Release the stream object.
2047 IStream_Release(pis);
2050 * Invalidate the property by zeroing its name member.
2052 propertyToDelete.sizeOfNameString = 0;
2055 * Here we should re-read the property so we get the updated pointer
2056 * but since we are here to zap it, I don't do it...
2058 StorageImpl_WriteProperty(
2059 parentStorage->base.ancestorStorage,
2060 indexOfPropertyToDelete,
2066 /*********************************************************************
2070 * Finds a placeholder for the StgProperty within the Storage
2073 static HRESULT findPlaceholder(
2074 StorageImpl *storage,
2075 ULONG propertyIndexToStore,
2076 ULONG storePropertyIndex,
2079 StgProperty storeProperty;
2084 * Read the storage property
2086 res = StorageImpl_ReadProperty(
2087 storage->base.ancestorStorage,
2096 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2098 if (storeProperty.previousProperty != PROPERTY_NULL)
2100 return findPlaceholder(
2102 propertyIndexToStore,
2103 storeProperty.previousProperty,
2108 storeProperty.previousProperty = propertyIndexToStore;
2111 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2113 if (storeProperty.nextProperty != PROPERTY_NULL)
2115 return findPlaceholder(
2117 propertyIndexToStore,
2118 storeProperty.nextProperty,
2123 storeProperty.nextProperty = propertyIndexToStore;
2126 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2128 if (storeProperty.dirProperty != PROPERTY_NULL)
2130 return findPlaceholder(
2132 propertyIndexToStore,
2133 storeProperty.dirProperty,
2138 storeProperty.dirProperty = propertyIndexToStore;
2142 hr = StorageImpl_WriteProperty(
2143 storage->base.ancestorStorage,
2155 /*************************************************************************
2159 * This method takes the previous and the next property link of a property
2160 * to be deleted and find them a place in the Storage.
2162 static HRESULT adjustPropertyChain(
2164 StgProperty propertyToDelete,
2165 StgProperty parentProperty,
2166 ULONG parentPropertyId,
2169 ULONG newLinkProperty = PROPERTY_NULL;
2170 BOOL needToFindAPlaceholder = FALSE;
2171 ULONG storeNode = PROPERTY_NULL;
2172 ULONG toStoreNode = PROPERTY_NULL;
2173 INT relationType = 0;
2177 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2179 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2182 * Set the parent previous to the property to delete previous
2184 newLinkProperty = propertyToDelete.previousProperty;
2186 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2189 * We also need to find a storage for the other link, setup variables
2190 * to do this at the end...
2192 needToFindAPlaceholder = TRUE;
2193 storeNode = propertyToDelete.previousProperty;
2194 toStoreNode = propertyToDelete.nextProperty;
2195 relationType = PROPERTY_RELATION_NEXT;
2198 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2201 * Set the parent previous to the property to delete next
2203 newLinkProperty = propertyToDelete.nextProperty;
2207 * Link it for real...
2209 parentProperty.previousProperty = newLinkProperty;
2212 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2214 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2217 * Set the parent next to the property to delete next previous
2219 newLinkProperty = propertyToDelete.previousProperty;
2221 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2224 * We also need to find a storage for the other link, setup variables
2225 * to do this at the end...
2227 needToFindAPlaceholder = TRUE;
2228 storeNode = propertyToDelete.previousProperty;
2229 toStoreNode = propertyToDelete.nextProperty;
2230 relationType = PROPERTY_RELATION_NEXT;
2233 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2236 * Set the parent next to the property to delete next
2238 newLinkProperty = propertyToDelete.nextProperty;
2242 * Link it for real...
2244 parentProperty.nextProperty = newLinkProperty;
2246 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2248 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2251 * Set the parent dir to the property to delete previous
2253 newLinkProperty = propertyToDelete.previousProperty;
2255 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2258 * We also need to find a storage for the other link, setup variables
2259 * to do this at the end...
2261 needToFindAPlaceholder = TRUE;
2262 storeNode = propertyToDelete.previousProperty;
2263 toStoreNode = propertyToDelete.nextProperty;
2264 relationType = PROPERTY_RELATION_NEXT;
2267 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2270 * Set the parent dir to the property to delete next
2272 newLinkProperty = propertyToDelete.nextProperty;
2276 * Link it for real...
2278 parentProperty.dirProperty = newLinkProperty;
2282 * Write back the parent property
2284 res = StorageImpl_WriteProperty(
2285 This->base.ancestorStorage,
2294 * If a placeholder is required for the other link, then, find one and
2295 * get out of here...
2297 if (needToFindAPlaceholder)
2299 hr = findPlaceholder(
2310 /******************************************************************************
2311 * SetElementTimes (IStorage)
2313 static HRESULT WINAPI StorageImpl_SetElementTimes(
2315 const OLECHAR *pwcsName,/* [string][in] */
2316 const FILETIME *pctime, /* [in] */
2317 const FILETIME *patime, /* [in] */
2318 const FILETIME *pmtime) /* [in] */
2320 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2324 /******************************************************************************
2325 * SetStateBits (IStorage)
2327 static HRESULT WINAPI StorageImpl_SetStateBits(
2329 DWORD grfStateBits,/* [in] */
2330 DWORD grfMask) /* [in] */
2332 FIXME("not implemented!\n");
2337 * Virtual function table for the IStorage32Impl class.
2339 static const IStorageVtbl Storage32Impl_Vtbl =
2341 StorageBaseImpl_QueryInterface,
2342 StorageBaseImpl_AddRef,
2343 StorageBaseImpl_Release,
2344 StorageBaseImpl_CreateStream,
2345 StorageBaseImpl_OpenStream,
2346 StorageImpl_CreateStorage,
2347 StorageBaseImpl_OpenStorage,
2349 StorageImpl_MoveElementTo,
2352 StorageBaseImpl_EnumElements,
2353 StorageImpl_DestroyElement,
2354 StorageBaseImpl_RenameElement,
2355 StorageImpl_SetElementTimes,
2356 StorageBaseImpl_SetClass,
2357 StorageImpl_SetStateBits,
2361 static HRESULT StorageImpl_Construct(
2371 StgProperty currentProperty;
2372 BOOL readSuccessful;
2373 ULONG currentPropertyIndex;
2375 if ( FAILED( validateSTGM(openFlags) ))
2376 return STG_E_INVALIDFLAG;
2378 memset(This, 0, sizeof(StorageImpl));
2381 * Initialize stream list
2384 list_init(&This->base.strmHead);
2387 * Initialize the virtual function table.
2389 This->base.lpVtbl = &Storage32Impl_Vtbl;
2390 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2391 This->base.v_destructor = &StorageImpl_Destroy;
2392 This->base.openFlags = (openFlags & ~STGM_CREATE);
2395 * This is the top-level storage so initialize the ancestor pointer
2398 This->base.ancestorStorage = This;
2401 * Initialize the physical support of the storage.
2403 This->hFile = hFile;
2406 * Store copy of file path.
2409 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2410 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2411 if (!This->pwcsName)
2412 return STG_E_INSUFFICIENTMEMORY;
2413 strcpyW(This->pwcsName, pwcsName);
2417 * Initialize the big block cache.
2419 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2420 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2421 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2427 if (This->bigBlockFile == 0)
2432 ULARGE_INTEGER size;
2433 BYTE* bigBlockBuffer;
2436 * Initialize all header variables:
2437 * - The big block depot consists of one block and it is at block 0
2438 * - The properties start at block 1
2439 * - There is no small block depot
2441 memset( This->bigBlockDepotStart,
2443 sizeof(This->bigBlockDepotStart));
2445 This->bigBlockDepotCount = 1;
2446 This->bigBlockDepotStart[0] = 0;
2447 This->rootStartBlock = 1;
2448 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2449 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2450 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2451 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2452 This->extBigBlockDepotCount = 0;
2454 StorageImpl_SaveFileHeader(This);
2457 * Add one block for the big block depot and one block for the properties
2459 size.u.HighPart = 0;
2460 size.u.LowPart = This->bigBlockSize * 3;
2461 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2464 * Initialize the big block depot
2466 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2467 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2468 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2469 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2470 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2475 * Load the header for the file.
2477 hr = StorageImpl_LoadFileHeader(This);
2481 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2488 * There is no block depot cached yet.
2490 This->indexBlockDepotCached = 0xFFFFFFFF;
2493 * Start searching for free blocks with block 0.
2495 This->prevFreeBlock = 0;
2498 * Create the block chain abstractions.
2500 if(!(This->rootBlockChain =
2501 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2502 return STG_E_READFAULT;
2504 if(!(This->smallBlockDepotChain =
2505 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2507 return STG_E_READFAULT;
2510 * Write the root property (memory only)
2514 StgProperty rootProp;
2516 * Initialize the property chain
2518 memset(&rootProp, 0, sizeof(rootProp));
2519 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2520 sizeof(rootProp.name)/sizeof(WCHAR) );
2521 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2522 rootProp.propertyType = PROPTYPE_ROOT;
2523 rootProp.previousProperty = PROPERTY_NULL;
2524 rootProp.nextProperty = PROPERTY_NULL;
2525 rootProp.dirProperty = PROPERTY_NULL;
2526 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2527 rootProp.size.u.HighPart = 0;
2528 rootProp.size.u.LowPart = 0;
2530 StorageImpl_WriteProperty(This, 0, &rootProp);
2534 * Find the ID of the root in the property sets.
2536 currentPropertyIndex = 0;
2540 readSuccessful = StorageImpl_ReadProperty(
2542 currentPropertyIndex,
2547 if ( (currentProperty.sizeOfNameString != 0 ) &&
2548 (currentProperty.propertyType == PROPTYPE_ROOT) )
2550 This->base.rootPropertySetIndex = currentPropertyIndex;
2554 currentPropertyIndex++;
2556 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2558 if (!readSuccessful)
2561 return STG_E_READFAULT;
2565 * Create the block chain abstraction for the small block root chain.
2567 if(!(This->smallBlockRootChain =
2568 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2569 return STG_E_READFAULT;
2574 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2576 StorageImpl *This = (StorageImpl*) iface;
2577 TRACE("(%p)\n", This);
2579 StorageBaseImpl_DeleteAll(&This->base);
2581 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2583 BlockChainStream_Destroy(This->smallBlockRootChain);
2584 BlockChainStream_Destroy(This->rootBlockChain);
2585 BlockChainStream_Destroy(This->smallBlockDepotChain);
2587 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2588 HeapFree(GetProcessHeap(), 0, This);
2591 /******************************************************************************
2592 * Storage32Impl_GetNextFreeBigBlock
2594 * Returns the index of the next free big block.
2595 * If the big block depot is filled, this method will enlarge it.
2598 static ULONG StorageImpl_GetNextFreeBigBlock(
2601 ULONG depotBlockIndexPos;
2603 ULONG depotBlockOffset;
2604 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2605 ULONG nextBlockIndex = BLOCK_SPECIAL;
2607 ULONG freeBlock = BLOCK_UNUSED;
2609 depotIndex = This->prevFreeBlock / blocksPerDepot;
2610 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2613 * Scan the entire big block depot until we find a block marked free
2615 while (nextBlockIndex != BLOCK_UNUSED)
2617 if (depotIndex < COUNT_BBDEPOTINHEADER)
2619 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2622 * Grow the primary depot.
2624 if (depotBlockIndexPos == BLOCK_UNUSED)
2626 depotBlockIndexPos = depotIndex*blocksPerDepot;
2629 * Add a block depot.
2631 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2632 This->bigBlockDepotCount++;
2633 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2636 * Flag it as a block depot.
2638 StorageImpl_SetNextBlockInChain(This,
2642 /* Save new header information.
2644 StorageImpl_SaveFileHeader(This);
2649 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2651 if (depotBlockIndexPos == BLOCK_UNUSED)
2654 * Grow the extended depot.
2656 ULONG extIndex = BLOCK_UNUSED;
2657 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2658 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2660 if (extBlockOffset == 0)
2662 /* We need an extended block.
2664 extIndex = Storage32Impl_AddExtBlockDepot(This);
2665 This->extBigBlockDepotCount++;
2666 depotBlockIndexPos = extIndex + 1;
2669 depotBlockIndexPos = depotIndex * blocksPerDepot;
2672 * Add a block depot and mark it in the extended block.
2674 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2675 This->bigBlockDepotCount++;
2676 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2678 /* Flag the block depot.
2680 StorageImpl_SetNextBlockInChain(This,
2684 /* If necessary, flag the extended depot block.
2686 if (extIndex != BLOCK_UNUSED)
2687 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2689 /* Save header information.
2691 StorageImpl_SaveFileHeader(This);
2695 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2697 if (depotBuffer != 0)
2699 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2700 ( nextBlockIndex != BLOCK_UNUSED))
2702 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2704 if (nextBlockIndex == BLOCK_UNUSED)
2706 freeBlock = (depotIndex * blocksPerDepot) +
2707 (depotBlockOffset/sizeof(ULONG));
2710 depotBlockOffset += sizeof(ULONG);
2713 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2717 depotBlockOffset = 0;
2721 * make sure that the block physically exists before using it
2723 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2725 This->prevFreeBlock = freeBlock;
2730 /******************************************************************************
2731 * Storage32Impl_AddBlockDepot
2733 * This will create a depot block, essentially it is a block initialized
2736 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2740 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2743 * Initialize blocks as free
2745 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2747 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2750 /******************************************************************************
2751 * Storage32Impl_GetExtDepotBlock
2753 * Returns the index of the block that corresponds to the specified depot
2754 * index. This method is only for depot indexes equal or greater than
2755 * COUNT_BBDEPOTINHEADER.
2757 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2759 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2760 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2761 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2762 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2763 ULONG blockIndex = BLOCK_UNUSED;
2764 ULONG extBlockIndex = This->extBigBlockDepotStart;
2766 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2768 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2769 return BLOCK_UNUSED;
2771 while (extBlockCount > 0)
2773 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2777 if (extBlockIndex != BLOCK_UNUSED)
2781 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2783 if (depotBuffer != 0)
2785 StorageUtl_ReadDWord(depotBuffer,
2786 extBlockOffset * sizeof(ULONG),
2789 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2796 /******************************************************************************
2797 * Storage32Impl_SetExtDepotBlock
2799 * Associates the specified block index to the specified depot index.
2800 * This method is only for depot indexes equal or greater than
2801 * COUNT_BBDEPOTINHEADER.
2803 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2805 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2806 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2807 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2808 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2809 ULONG extBlockIndex = This->extBigBlockDepotStart;
2811 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2813 while (extBlockCount > 0)
2815 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2819 if (extBlockIndex != BLOCK_UNUSED)
2823 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2825 if (depotBuffer != 0)
2827 StorageUtl_WriteDWord(depotBuffer,
2828 extBlockOffset * sizeof(ULONG),
2831 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2836 /******************************************************************************
2837 * Storage32Impl_AddExtBlockDepot
2839 * Creates an extended depot block.
2841 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2843 ULONG numExtBlocks = This->extBigBlockDepotCount;
2844 ULONG nextExtBlock = This->extBigBlockDepotStart;
2845 BYTE* depotBuffer = NULL;
2846 ULONG index = BLOCK_UNUSED;
2847 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2848 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2849 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2851 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2852 blocksPerDepotBlock;
2854 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2857 * The first extended block.
2859 This->extBigBlockDepotStart = index;
2865 * Follow the chain to the last one.
2867 for (i = 0; i < (numExtBlocks - 1); i++)
2869 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2873 * Add the new extended block to the chain.
2875 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2876 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2877 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2881 * Initialize this block.
2883 depotBuffer = StorageImpl_GetBigBlock(This, index);
2884 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2885 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2890 /******************************************************************************
2891 * Storage32Impl_FreeBigBlock
2893 * This method will flag the specified block as free in the big block depot.
2895 static void StorageImpl_FreeBigBlock(
2899 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2901 if (blockIndex < This->prevFreeBlock)
2902 This->prevFreeBlock = blockIndex;
2905 /************************************************************************
2906 * Storage32Impl_GetNextBlockInChain
2908 * This method will retrieve the block index of the next big block in
2911 * Params: This - Pointer to the Storage object.
2912 * blockIndex - Index of the block to retrieve the chain
2914 * nextBlockIndex - receives the return value.
2916 * Returns: This method returns the index of the next block in the chain.
2917 * It will return the constants:
2918 * BLOCK_SPECIAL - If the block given was not part of a
2920 * BLOCK_END_OF_CHAIN - If the block given was the last in
2922 * BLOCK_UNUSED - If the block given was not past of a chain
2924 * BLOCK_EXTBBDEPOT - This block is part of the extended
2927 * See Windows documentation for more details on IStorage methods.
2929 static HRESULT StorageImpl_GetNextBlockInChain(
2932 ULONG* nextBlockIndex)
2934 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2935 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2936 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2938 ULONG depotBlockIndexPos;
2941 *nextBlockIndex = BLOCK_SPECIAL;
2943 if(depotBlockCount >= This->bigBlockDepotCount)
2945 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2946 This->bigBlockDepotCount);
2947 return STG_E_READFAULT;
2951 * Cache the currently accessed depot block.
2953 if (depotBlockCount != This->indexBlockDepotCached)
2955 This->indexBlockDepotCached = depotBlockCount;
2957 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2959 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2964 * We have to look in the extended depot.
2966 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2969 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2972 return STG_E_READFAULT;
2974 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2976 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2977 This->blockDepotCached[index] = *nextBlockIndex;
2979 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2982 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2987 /******************************************************************************
2988 * Storage32Impl_GetNextExtendedBlock
2990 * Given an extended block this method will return the next extended block.
2993 * The last ULONG of an extended block is the block index of the next
2994 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2998 * - The index of the next extended block
2999 * - BLOCK_UNUSED: there is no next extended block.
3000 * - Any other return values denotes failure.
3002 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3004 ULONG nextBlockIndex = BLOCK_SPECIAL;
3005 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3008 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3012 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3014 StorageImpl_ReleaseBigBlock(This, depotBuffer);
3017 return nextBlockIndex;
3020 /******************************************************************************
3021 * Storage32Impl_SetNextBlockInChain
3023 * This method will write the index of the specified block's next block
3024 * in the big block depot.
3026 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3029 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3030 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3031 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3034 static void StorageImpl_SetNextBlockInChain(
3039 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3040 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3041 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3042 ULONG depotBlockIndexPos;
3045 assert(depotBlockCount < This->bigBlockDepotCount);
3046 assert(blockIndex != nextBlock);
3048 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3050 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3055 * We have to look in the extended depot.
3057 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3060 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
3064 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
3065 StorageImpl_ReleaseBigBlock(This, depotBuffer);
3069 * Update the cached block depot, if necessary.
3071 if (depotBlockCount == This->indexBlockDepotCached)
3073 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3077 /******************************************************************************
3078 * Storage32Impl_LoadFileHeader
3080 * This method will read in the file header, i.e. big block index -1.
3082 static HRESULT StorageImpl_LoadFileHeader(
3085 HRESULT hr = STG_E_FILENOTFOUND;
3086 void* headerBigBlock = NULL;
3091 * Get a pointer to the big block of data containing the header.
3093 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
3096 * Extract the information from the header.
3098 if (headerBigBlock!=0)
3101 * Check for the "magic number" signature and return an error if it is not
3104 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3106 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3107 return STG_E_OLDFORMAT;
3110 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3112 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3113 return STG_E_INVALIDHEADER;
3116 StorageUtl_ReadWord(
3118 OFFSET_BIGBLOCKSIZEBITS,
3119 &This->bigBlockSizeBits);
3121 StorageUtl_ReadWord(
3123 OFFSET_SMALLBLOCKSIZEBITS,
3124 &This->smallBlockSizeBits);
3126 StorageUtl_ReadDWord(
3128 OFFSET_BBDEPOTCOUNT,
3129 &This->bigBlockDepotCount);
3131 StorageUtl_ReadDWord(
3133 OFFSET_ROOTSTARTBLOCK,
3134 &This->rootStartBlock);
3136 StorageUtl_ReadDWord(
3138 OFFSET_SBDEPOTSTART,
3139 &This->smallBlockDepotStart);
3141 StorageUtl_ReadDWord(
3143 OFFSET_EXTBBDEPOTSTART,
3144 &This->extBigBlockDepotStart);
3146 StorageUtl_ReadDWord(
3148 OFFSET_EXTBBDEPOTCOUNT,
3149 &This->extBigBlockDepotCount);
3151 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3153 StorageUtl_ReadDWord(
3155 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3156 &(This->bigBlockDepotStart[index]));
3160 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3164 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3165 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3169 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3170 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3174 * Right now, the code is making some assumptions about the size of the
3175 * blocks, just make sure they are what we're expecting.
3177 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3178 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3180 WARN("Broken OLE storage file\n");
3181 hr = STG_E_INVALIDHEADER;
3187 * Release the block.
3189 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3195 /******************************************************************************
3196 * Storage32Impl_SaveFileHeader
3198 * This method will save to the file the header, i.e. big block -1.
3200 static void StorageImpl_SaveFileHeader(
3203 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3208 * Get a pointer to the big block of data containing the header.
3210 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3213 * If the block read failed, the file is probably new.
3218 * Initialize for all unknown fields.
3220 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3223 * Initialize the magic number.
3225 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3228 * And a bunch of things we don't know what they mean
3230 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3231 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3232 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3233 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3237 * Write the information to the header.
3239 StorageUtl_WriteWord(
3241 OFFSET_BIGBLOCKSIZEBITS,
3242 This->bigBlockSizeBits);
3244 StorageUtl_WriteWord(
3246 OFFSET_SMALLBLOCKSIZEBITS,
3247 This->smallBlockSizeBits);
3249 StorageUtl_WriteDWord(
3251 OFFSET_BBDEPOTCOUNT,
3252 This->bigBlockDepotCount);
3254 StorageUtl_WriteDWord(
3256 OFFSET_ROOTSTARTBLOCK,
3257 This->rootStartBlock);
3259 StorageUtl_WriteDWord(
3261 OFFSET_SBDEPOTSTART,
3262 This->smallBlockDepotStart);
3264 StorageUtl_WriteDWord(
3266 OFFSET_SBDEPOTCOUNT,
3267 This->smallBlockDepotChain ?
3268 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3270 StorageUtl_WriteDWord(
3272 OFFSET_EXTBBDEPOTSTART,
3273 This->extBigBlockDepotStart);
3275 StorageUtl_WriteDWord(
3277 OFFSET_EXTBBDEPOTCOUNT,
3278 This->extBigBlockDepotCount);
3280 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3282 StorageUtl_WriteDWord(
3284 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3285 (This->bigBlockDepotStart[index]));
3289 * Write the big block back to the file.
3291 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3294 /******************************************************************************
3295 * Storage32Impl_ReadProperty
3297 * This method will read the specified property from the property chain.
3299 BOOL StorageImpl_ReadProperty(
3302 StgProperty* buffer)
3304 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3305 ULARGE_INTEGER offsetInPropSet;
3309 offsetInPropSet.u.HighPart = 0;
3310 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3312 readRes = BlockChainStream_ReadAt(
3313 This->rootBlockChain,
3319 if (SUCCEEDED(readRes))
3321 /* replace the name of root entry (often "Root Entry") by the file name */
3322 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3323 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3325 memset(buffer->name, 0, sizeof(buffer->name));
3329 PROPERTY_NAME_BUFFER_LEN );
3330 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3332 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3334 StorageUtl_ReadWord(
3336 OFFSET_PS_NAMELENGTH,
3337 &buffer->sizeOfNameString);
3339 StorageUtl_ReadDWord(
3341 OFFSET_PS_PREVIOUSPROP,
3342 &buffer->previousProperty);
3344 StorageUtl_ReadDWord(
3347 &buffer->nextProperty);
3349 StorageUtl_ReadDWord(
3352 &buffer->dirProperty);
3354 StorageUtl_ReadGUID(
3357 &buffer->propertyUniqueID);
3359 StorageUtl_ReadDWord(
3362 &buffer->timeStampS1);
3364 StorageUtl_ReadDWord(
3367 &buffer->timeStampD1);
3369 StorageUtl_ReadDWord(
3372 &buffer->timeStampS2);
3374 StorageUtl_ReadDWord(
3377 &buffer->timeStampD2);
3379 StorageUtl_ReadDWord(
3381 OFFSET_PS_STARTBLOCK,
3382 &buffer->startingBlock);
3384 StorageUtl_ReadDWord(
3387 &buffer->size.u.LowPart);
3389 buffer->size.u.HighPart = 0;
3392 return SUCCEEDED(readRes) ? TRUE : FALSE;
3395 /*********************************************************************
3396 * Write the specified property into the property chain
3398 BOOL StorageImpl_WriteProperty(
3401 StgProperty* buffer)
3403 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3404 ULARGE_INTEGER offsetInPropSet;
3408 offsetInPropSet.u.HighPart = 0;
3409 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3411 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3414 currentProperty + OFFSET_PS_NAME,
3416 PROPERTY_NAME_BUFFER_LEN );
3418 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3420 StorageUtl_WriteWord(
3422 OFFSET_PS_NAMELENGTH,
3423 buffer->sizeOfNameString);
3425 StorageUtl_WriteDWord(
3427 OFFSET_PS_PREVIOUSPROP,
3428 buffer->previousProperty);
3430 StorageUtl_WriteDWord(
3433 buffer->nextProperty);
3435 StorageUtl_WriteDWord(
3438 buffer->dirProperty);
3440 StorageUtl_WriteGUID(
3443 &buffer->propertyUniqueID);
3445 StorageUtl_WriteDWord(
3448 buffer->timeStampS1);
3450 StorageUtl_WriteDWord(
3453 buffer->timeStampD1);
3455 StorageUtl_WriteDWord(
3458 buffer->timeStampS2);
3460 StorageUtl_WriteDWord(
3463 buffer->timeStampD2);
3465 StorageUtl_WriteDWord(
3467 OFFSET_PS_STARTBLOCK,
3468 buffer->startingBlock);
3470 StorageUtl_WriteDWord(
3473 buffer->size.u.LowPart);
3475 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3480 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3483 static BOOL StorageImpl_ReadBigBlock(
3488 void* bigBlockBuffer;
3490 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3492 if (bigBlockBuffer!=0)
3494 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3496 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3504 static BOOL StorageImpl_WriteBigBlock(
3509 void* bigBlockBuffer;
3511 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3513 if (bigBlockBuffer!=0)
3515 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3517 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3525 static void* StorageImpl_GetROBigBlock(
3529 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3532 static void* StorageImpl_GetBigBlock(
3536 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3539 static void StorageImpl_ReleaseBigBlock(
3543 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3546 /******************************************************************************
3547 * Storage32Impl_SmallBlocksToBigBlocks
3549 * This method will convert a small block chain to a big block chain.
3550 * The small block chain will be destroyed.
3552 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3554 SmallBlockChainStream** ppsbChain)
3556 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3557 ULARGE_INTEGER size, offset;
3558 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3559 ULONG propertyIndex;
3560 HRESULT resWrite = S_OK;
3562 StgProperty chainProperty;
3564 BlockChainStream *bbTempChain = NULL;
3565 BlockChainStream *bigBlockChain = NULL;
3568 * Create a temporary big block chain that doesn't have
3569 * an associated property. This temporary chain will be
3570 * used to copy data from small blocks to big blocks.
3572 bbTempChain = BlockChainStream_Construct(This,
3575 if(!bbTempChain) return NULL;
3577 * Grow the big block chain.
3579 size = SmallBlockChainStream_GetSize(*ppsbChain);
3580 BlockChainStream_SetSize(bbTempChain, size);
3583 * Copy the contents of the small block chain to the big block chain
3584 * by small block size increments.
3586 offset.u.LowPart = 0;
3587 offset.u.HighPart = 0;
3591 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3594 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3596 This->smallBlockSize,
3599 if (FAILED(resRead))
3604 cbTotalRead += cbRead;
3606 resWrite = BlockChainStream_WriteAt(bbTempChain,
3612 if (FAILED(resWrite))
3615 cbTotalWritten += cbWritten;
3616 offset.u.LowPart += This->smallBlockSize;
3618 } while (cbRead > 0);
3619 HeapFree(GetProcessHeap(),0,buffer);
3621 if (FAILED(resRead) || FAILED(resWrite))
3623 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3624 BlockChainStream_Destroy(bbTempChain);
3629 * Destroy the small block chain.
3631 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3632 size.u.HighPart = 0;
3634 SmallBlockChainStream_SetSize(*ppsbChain, size);
3635 SmallBlockChainStream_Destroy(*ppsbChain);
3639 * Change the property information. This chain is now a big block chain
3640 * and it doesn't reside in the small blocks chain anymore.
3642 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3644 chainProperty.startingBlock = bbHeadOfChain;
3646 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3649 * Destroy the temporary propertyless big block chain.
3650 * Create a new big block chain associated with this property.
3652 BlockChainStream_Destroy(bbTempChain);
3653 bigBlockChain = BlockChainStream_Construct(This,
3657 return bigBlockChain;
3660 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3662 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3664 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3665 HeapFree(GetProcessHeap(), 0, This);
3668 /******************************************************************************
3670 ** Storage32InternalImpl_Commit
3672 ** The non-root storages cannot be opened in transacted mode thus this function
3675 static HRESULT WINAPI StorageInternalImpl_Commit(
3677 DWORD grfCommitFlags) /* [in] */
3682 /******************************************************************************
3684 ** Storage32InternalImpl_Revert
3686 ** The non-root storages cannot be opened in transacted mode thus this function
3689 static HRESULT WINAPI StorageInternalImpl_Revert(
3695 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3697 IStorage_Release((IStorage*)This->parentStorage);
3698 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3699 HeapFree(GetProcessHeap(), 0, This);
3702 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3703 IEnumSTATSTG* iface,
3707 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3710 * Perform a sanity check on the parameters.
3713 return E_INVALIDARG;
3716 * Initialize the return parameter.
3721 * Compare the riid with the interface IDs implemented by this object.
3723 if (IsEqualGUID(&IID_IUnknown, riid) ||
3724 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3726 *ppvObject = (IEnumSTATSTG*)This;
3727 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3731 return E_NOINTERFACE;
3734 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3735 IEnumSTATSTG* iface)
3737 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3738 return InterlockedIncrement(&This->ref);
3741 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3742 IEnumSTATSTG* iface)
3744 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3748 newRef = InterlockedDecrement(&This->ref);
3751 * If the reference count goes down to 0, perform suicide.
3755 IEnumSTATSTGImpl_Destroy(This);
3761 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3762 IEnumSTATSTG* iface,
3765 ULONG* pceltFetched)
3767 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3769 StgProperty currentProperty;
3770 STATSTG* currentReturnStruct = rgelt;
3771 ULONG objectFetched = 0;
3772 ULONG currentSearchNode;
3775 * Perform a sanity check on the parameters.
3777 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3778 return E_INVALIDARG;
3781 * To avoid the special case, get another pointer to a ULONG value if
3782 * the caller didn't supply one.
3784 if (pceltFetched==0)
3785 pceltFetched = &objectFetched;
3788 * Start the iteration, we will iterate until we hit the end of the
3789 * linked list or until we hit the number of items to iterate through
3794 * Start with the node at the top of the stack.
3796 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3798 while ( ( *pceltFetched < celt) &&
3799 ( currentSearchNode!=PROPERTY_NULL) )
3802 * Remove the top node from the stack
3804 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3807 * Read the property from the storage.
3809 StorageImpl_ReadProperty(This->parentStorage,
3814 * Copy the information to the return buffer.
3816 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3821 * Step to the next item in the iteration
3824 currentReturnStruct++;
3827 * Push the next search node in the search stack.
3829 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3832 * continue the iteration.
3834 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3837 if (*pceltFetched == celt)
3844 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3845 IEnumSTATSTG* iface,
3848 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3850 StgProperty currentProperty;
3851 ULONG objectFetched = 0;
3852 ULONG currentSearchNode;
3855 * Start with the node at the top of the stack.
3857 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3859 while ( (objectFetched < celt) &&
3860 (currentSearchNode!=PROPERTY_NULL) )
3863 * Remove the top node from the stack
3865 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3868 * Read the property from the storage.
3870 StorageImpl_ReadProperty(This->parentStorage,
3875 * Step to the next item in the iteration
3880 * Push the next search node in the search stack.
3882 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3885 * continue the iteration.
3887 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3890 if (objectFetched == celt)
3896 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3897 IEnumSTATSTG* iface)
3899 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3901 StgProperty rootProperty;
3902 BOOL readSuccessful;
3905 * Re-initialize the search stack to an empty stack
3907 This->stackSize = 0;
3910 * Read the root property from the storage.
3912 readSuccessful = StorageImpl_ReadProperty(
3913 This->parentStorage,
3914 This->firstPropertyNode,
3919 assert(rootProperty.sizeOfNameString!=0);
3922 * Push the search node in the search stack.
3924 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3930 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3931 IEnumSTATSTG* iface,
3932 IEnumSTATSTG** ppenum)
3934 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3936 IEnumSTATSTGImpl* newClone;
3939 * Perform a sanity check on the parameters.
3942 return E_INVALIDARG;
3944 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3945 This->firstPropertyNode);
3949 * The new clone enumeration must point to the same current node as
3952 newClone->stackSize = This->stackSize ;
3953 newClone->stackMaxSize = This->stackMaxSize ;
3954 newClone->stackToVisit =
3955 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3958 newClone->stackToVisit,
3960 sizeof(ULONG) * newClone->stackSize);
3962 *ppenum = (IEnumSTATSTG*)newClone;
3965 * Don't forget to nail down a reference to the clone before
3968 IEnumSTATSTGImpl_AddRef(*ppenum);
3973 static INT IEnumSTATSTGImpl_FindParentProperty(
3974 IEnumSTATSTGImpl *This,
3975 ULONG childProperty,
3976 StgProperty *currentProperty,
3979 ULONG currentSearchNode;
3983 * To avoid the special case, get another pointer to a ULONG value if
3984 * the caller didn't supply one.
3987 thisNodeId = &foundNode;
3990 * Start with the node at the top of the stack.
3992 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3995 while (currentSearchNode!=PROPERTY_NULL)
3998 * Store the current node in the returned parameters
4000 *thisNodeId = currentSearchNode;
4003 * Remove the top node from the stack
4005 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4008 * Read the property from the storage.
4010 StorageImpl_ReadProperty(
4011 This->parentStorage,
4015 if (currentProperty->previousProperty == childProperty)
4016 return PROPERTY_RELATION_PREVIOUS;
4018 else if (currentProperty->nextProperty == childProperty)
4019 return PROPERTY_RELATION_NEXT;
4021 else if (currentProperty->dirProperty == childProperty)
4022 return PROPERTY_RELATION_DIR;
4025 * Push the next search node in the search stack.
4027 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4030 * continue the iteration.
4032 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4035 return PROPERTY_NULL;
4038 static ULONG IEnumSTATSTGImpl_FindProperty(
4039 IEnumSTATSTGImpl* This,
4040 const OLECHAR* lpszPropName,
4041 StgProperty* currentProperty)
4043 ULONG currentSearchNode;
4046 * Start with the node at the top of the stack.
4048 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4050 while (currentSearchNode!=PROPERTY_NULL)
4053 * Remove the top node from the stack
4055 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4058 * Read the property from the storage.
4060 StorageImpl_ReadProperty(This->parentStorage,
4064 if ( propertyNameCmp(
4065 (const OLECHAR*)currentProperty->name,
4066 (const OLECHAR*)lpszPropName) == 0)
4067 return currentSearchNode;
4070 * Push the next search node in the search stack.
4072 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4075 * continue the iteration.
4077 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4080 return PROPERTY_NULL;
4083 static void IEnumSTATSTGImpl_PushSearchNode(
4084 IEnumSTATSTGImpl* This,
4087 StgProperty rootProperty;
4088 BOOL readSuccessful;
4091 * First, make sure we're not trying to push an unexisting node.
4093 if (nodeToPush==PROPERTY_NULL)
4097 * First push the node to the stack
4099 if (This->stackSize == This->stackMaxSize)
4101 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4103 This->stackToVisit = HeapReAlloc(
4107 sizeof(ULONG) * This->stackMaxSize);
4110 This->stackToVisit[This->stackSize] = nodeToPush;
4114 * Read the root property from the storage.
4116 readSuccessful = StorageImpl_ReadProperty(
4117 This->parentStorage,
4123 assert(rootProperty.sizeOfNameString!=0);
4126 * Push the previous search node in the search stack.
4128 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4132 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4133 IEnumSTATSTGImpl* This,
4138 if (This->stackSize == 0)
4139 return PROPERTY_NULL;
4141 topNode = This->stackToVisit[This->stackSize-1];
4150 * Virtual function table for the IEnumSTATSTGImpl class.
4152 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4154 IEnumSTATSTGImpl_QueryInterface,
4155 IEnumSTATSTGImpl_AddRef,
4156 IEnumSTATSTGImpl_Release,
4157 IEnumSTATSTGImpl_Next,
4158 IEnumSTATSTGImpl_Skip,
4159 IEnumSTATSTGImpl_Reset,
4160 IEnumSTATSTGImpl_Clone
4163 /******************************************************************************
4164 ** IEnumSTATSTGImpl implementation
4167 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4168 StorageImpl* parentStorage,
4169 ULONG firstPropertyNode)
4171 IEnumSTATSTGImpl* newEnumeration;
4173 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4175 if (newEnumeration!=0)
4178 * Set-up the virtual function table and reference count.
4180 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4181 newEnumeration->ref = 0;
4184 * We want to nail-down the reference to the storage in case the
4185 * enumeration out-lives the storage in the client application.
4187 newEnumeration->parentStorage = parentStorage;
4188 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4190 newEnumeration->firstPropertyNode = firstPropertyNode;
4193 * Initialize the search stack
4195 newEnumeration->stackSize = 0;
4196 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4197 newEnumeration->stackToVisit =
4198 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4201 * Make sure the current node of the iterator is the first one.
4203 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4206 return newEnumeration;
4210 * Virtual function table for the Storage32InternalImpl class.
4212 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4214 StorageBaseImpl_QueryInterface,
4215 StorageBaseImpl_AddRef,
4216 StorageBaseImpl_Release,
4217 StorageBaseImpl_CreateStream,
4218 StorageBaseImpl_OpenStream,
4219 StorageImpl_CreateStorage,
4220 StorageBaseImpl_OpenStorage,
4222 StorageImpl_MoveElementTo,
4223 StorageInternalImpl_Commit,
4224 StorageInternalImpl_Revert,
4225 StorageBaseImpl_EnumElements,
4226 StorageImpl_DestroyElement,
4227 StorageBaseImpl_RenameElement,
4228 StorageImpl_SetElementTimes,
4229 StorageBaseImpl_SetClass,
4230 StorageImpl_SetStateBits,
4231 StorageBaseImpl_Stat
4234 /******************************************************************************
4235 ** Storage32InternalImpl implementation
4238 static StorageInternalImpl* StorageInternalImpl_Construct(
4239 StorageImpl* ancestorStorage,
4241 ULONG rootPropertyIndex)
4243 StorageInternalImpl* newStorage;
4246 * Allocate space for the new storage object
4248 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
4252 memset(newStorage, 0, sizeof(StorageInternalImpl));
4255 * Initialize the stream list
4258 list_init(&newStorage->base.strmHead);
4261 * Initialize the virtual function table.
4263 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4264 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4265 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4268 * Keep the ancestor storage pointer and nail a reference to it.
4270 newStorage->base.ancestorStorage = ancestorStorage;
4271 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4274 * Keep the index of the root property set for this storage,
4276 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4284 /******************************************************************************
4285 ** StorageUtl implementation
4288 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4292 memcpy(&tmp, buffer+offset, sizeof(WORD));
4293 *value = le16toh(tmp);
4296 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4298 value = htole16(value);
4299 memcpy(buffer+offset, &value, sizeof(WORD));
4302 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4306 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4307 *value = le32toh(tmp);
4310 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4312 value = htole32(value);
4313 memcpy(buffer+offset, &value, sizeof(DWORD));
4316 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4317 ULARGE_INTEGER* value)
4319 #ifdef WORDS_BIGENDIAN
4322 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4323 value->u.LowPart = htole32(tmp.u.HighPart);
4324 value->u.HighPart = htole32(tmp.u.LowPart);
4326 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4330 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4331 const ULARGE_INTEGER *value)
4333 #ifdef WORDS_BIGENDIAN
4336 tmp.u.LowPart = htole32(value->u.HighPart);
4337 tmp.u.HighPart = htole32(value->u.LowPart);
4338 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4340 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4344 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4346 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4347 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4348 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4350 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4353 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4355 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4356 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4357 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4359 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4362 void StorageUtl_CopyPropertyToSTATSTG(
4363 STATSTG* destination,
4364 StgProperty* source,
4368 * The copy of the string occurs only when the flag is not set
4370 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4371 (source->name == NULL) ||
4372 (source->name[0] == 0) )
4374 destination->pwcsName = 0;
4378 destination->pwcsName =
4379 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4381 strcpyW((LPWSTR)destination->pwcsName, source->name);
4384 switch (source->propertyType)
4386 case PROPTYPE_STORAGE:
4388 destination->type = STGTY_STORAGE;
4390 case PROPTYPE_STREAM:
4391 destination->type = STGTY_STREAM;
4394 destination->type = STGTY_STREAM;
4398 destination->cbSize = source->size;
4400 currentReturnStruct->mtime = {0}; TODO
4401 currentReturnStruct->ctime = {0};
4402 currentReturnStruct->atime = {0};
4404 destination->grfMode = 0;
4405 destination->grfLocksSupported = 0;
4406 destination->clsid = source->propertyUniqueID;
4407 destination->grfStateBits = 0;
4408 destination->reserved = 0;
4411 /******************************************************************************
4412 ** BlockChainStream implementation
4415 BlockChainStream* BlockChainStream_Construct(
4416 StorageImpl* parentStorage,
4417 ULONG* headOfStreamPlaceHolder,
4418 ULONG propertyIndex)
4420 BlockChainStream* newStream;
4423 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4425 newStream->parentStorage = parentStorage;
4426 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4427 newStream->ownerPropertyIndex = propertyIndex;
4428 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4429 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4430 newStream->numBlocks = 0;
4432 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4434 while (blockIndex != BLOCK_END_OF_CHAIN)
4436 newStream->numBlocks++;
4437 newStream->tailIndex = blockIndex;
4439 if(FAILED(StorageImpl_GetNextBlockInChain(
4444 HeapFree(GetProcessHeap(), 0, newStream);
4452 void BlockChainStream_Destroy(BlockChainStream* This)
4454 HeapFree(GetProcessHeap(), 0, This);
4457 /******************************************************************************
4458 * BlockChainStream_GetHeadOfChain
4460 * Returns the head of this stream chain.
4461 * Some special chains don't have properties, their heads are kept in
4462 * This->headOfStreamPlaceHolder.
4465 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4467 StgProperty chainProperty;
4468 BOOL readSuccessful;
4470 if (This->headOfStreamPlaceHolder != 0)
4471 return *(This->headOfStreamPlaceHolder);
4473 if (This->ownerPropertyIndex != PROPERTY_NULL)
4475 readSuccessful = StorageImpl_ReadProperty(
4476 This->parentStorage,
4477 This->ownerPropertyIndex,
4482 return chainProperty.startingBlock;
4486 return BLOCK_END_OF_CHAIN;
4489 /******************************************************************************
4490 * BlockChainStream_GetCount
4492 * Returns the number of blocks that comprises this chain.
4493 * This is not the size of the stream as the last block may not be full!
4496 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4501 blockIndex = BlockChainStream_GetHeadOfChain(This);
4503 while (blockIndex != BLOCK_END_OF_CHAIN)
4507 if(FAILED(StorageImpl_GetNextBlockInChain(
4508 This->parentStorage,
4517 /******************************************************************************
4518 * BlockChainStream_ReadAt
4520 * Reads a specified number of bytes from this chain at the specified offset.
4521 * bytesRead may be NULL.
4522 * Failure will be returned if the specified number of bytes has not been read.
4524 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4525 ULARGE_INTEGER offset,
4530 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4531 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4532 ULONG bytesToReadInBuffer;
4535 BYTE* bigBlockBuffer;
4538 * Find the first block in the stream that contains part of the buffer.
4540 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4541 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4542 (blockNoInSequence < This->lastBlockNoInSequence) )
4544 blockIndex = BlockChainStream_GetHeadOfChain(This);
4545 This->lastBlockNoInSequence = blockNoInSequence;
4549 ULONG temp = blockNoInSequence;
4551 blockIndex = This->lastBlockNoInSequenceIndex;
4552 blockNoInSequence -= This->lastBlockNoInSequence;
4553 This->lastBlockNoInSequence = temp;
4556 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4558 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4559 return STG_E_DOCFILECORRUPT;
4560 blockNoInSequence--;
4563 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4564 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4566 This->lastBlockNoInSequenceIndex = blockIndex;
4569 * Start reading the buffer.
4572 bufferWalker = buffer;
4574 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4577 * Calculate how many bytes we can copy from this big block.
4579 bytesToReadInBuffer =
4580 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4583 * Copy those bytes to the buffer
4586 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4587 if (!bigBlockBuffer)
4588 return STG_E_READFAULT;
4590 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4592 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4595 * Step to the next big block.
4597 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4598 return STG_E_DOCFILECORRUPT;
4600 bufferWalker += bytesToReadInBuffer;
4601 size -= bytesToReadInBuffer;
4602 *bytesRead += bytesToReadInBuffer;
4603 offsetInBlock = 0; /* There is no offset on the next block */
4607 return (size == 0) ? S_OK : STG_E_READFAULT;
4610 /******************************************************************************
4611 * BlockChainStream_WriteAt
4613 * Writes the specified number of bytes to this chain at the specified offset.
4614 * bytesWritten may be NULL.
4615 * Will fail if not all specified number of bytes have been written.
4617 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4618 ULARGE_INTEGER offset,
4621 ULONG* bytesWritten)
4623 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4624 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4627 const BYTE* bufferWalker;
4628 BYTE* bigBlockBuffer;
4631 * Find the first block in the stream that contains part of the buffer.
4633 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4634 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4635 (blockNoInSequence < This->lastBlockNoInSequence) )
4637 blockIndex = BlockChainStream_GetHeadOfChain(This);
4638 This->lastBlockNoInSequence = blockNoInSequence;
4642 ULONG temp = blockNoInSequence;
4644 blockIndex = This->lastBlockNoInSequenceIndex;
4645 blockNoInSequence -= This->lastBlockNoInSequence;
4646 This->lastBlockNoInSequence = temp;
4649 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4651 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4653 return STG_E_DOCFILECORRUPT;
4654 blockNoInSequence--;
4657 This->lastBlockNoInSequenceIndex = blockIndex;
4659 /* BlockChainStream_SetSize should have already been called to ensure we have
4660 * enough blocks in the chain to write into */
4661 if (blockIndex == BLOCK_END_OF_CHAIN)
4663 ERR("not enough blocks in chain to write data\n");
4664 return STG_E_DOCFILECORRUPT;
4668 * Here, I'm casting away the constness on the buffer variable
4669 * This is OK since we don't intend to modify that buffer.
4672 bufferWalker = (const BYTE*)buffer;
4674 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4677 * Calculate how many bytes we can copy from this big block.
4680 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4683 * Copy those bytes to the buffer
4685 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4687 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4689 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4692 * Step to the next big block.
4694 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4696 return STG_E_DOCFILECORRUPT;
4697 bufferWalker += bytesToWrite;
4698 size -= bytesToWrite;
4699 *bytesWritten += bytesToWrite;
4700 offsetInBlock = 0; /* There is no offset on the next block */
4703 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4706 /******************************************************************************
4707 * BlockChainStream_Shrink
4709 * Shrinks this chain in the big block depot.
4711 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4712 ULARGE_INTEGER newSize)
4714 ULONG blockIndex, extraBlock;
4719 * Reset the last accessed block cache.
4721 This->lastBlockNoInSequence = 0xFFFFFFFF;
4722 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4725 * Figure out how many blocks are needed to contain the new size
4727 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4729 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4732 blockIndex = BlockChainStream_GetHeadOfChain(This);
4735 * Go to the new end of chain
4737 while (count < numBlocks)
4739 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4745 /* Get the next block before marking the new end */
4746 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4750 /* Mark the new end of chain */
4751 StorageImpl_SetNextBlockInChain(
4752 This->parentStorage,
4754 BLOCK_END_OF_CHAIN);
4756 This->tailIndex = blockIndex;
4757 This->numBlocks = numBlocks;
4760 * Mark the extra blocks as free
4762 while (extraBlock != BLOCK_END_OF_CHAIN)
4764 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4767 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4768 extraBlock = blockIndex;
4774 /******************************************************************************
4775 * BlockChainStream_Enlarge
4777 * Grows this chain in the big block depot.
4779 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4780 ULARGE_INTEGER newSize)
4782 ULONG blockIndex, currentBlock;
4784 ULONG oldNumBlocks = 0;
4786 blockIndex = BlockChainStream_GetHeadOfChain(This);
4789 * Empty chain. Create the head.
4791 if (blockIndex == BLOCK_END_OF_CHAIN)
4793 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4794 StorageImpl_SetNextBlockInChain(This->parentStorage,
4796 BLOCK_END_OF_CHAIN);
4798 if (This->headOfStreamPlaceHolder != 0)
4800 *(This->headOfStreamPlaceHolder) = blockIndex;
4804 StgProperty chainProp;
4805 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4807 StorageImpl_ReadProperty(
4808 This->parentStorage,
4809 This->ownerPropertyIndex,
4812 chainProp.startingBlock = blockIndex;
4814 StorageImpl_WriteProperty(
4815 This->parentStorage,
4816 This->ownerPropertyIndex,
4820 This->tailIndex = blockIndex;
4821 This->numBlocks = 1;
4825 * Figure out how many blocks are needed to contain this stream
4827 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4829 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4833 * Go to the current end of chain
4835 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4837 currentBlock = blockIndex;
4839 while (blockIndex != BLOCK_END_OF_CHAIN)
4842 currentBlock = blockIndex;
4844 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4849 This->tailIndex = currentBlock;
4852 currentBlock = This->tailIndex;
4853 oldNumBlocks = This->numBlocks;
4856 * Add new blocks to the chain
4858 if (oldNumBlocks < newNumBlocks)
4860 while (oldNumBlocks < newNumBlocks)
4862 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4864 StorageImpl_SetNextBlockInChain(
4865 This->parentStorage,
4869 StorageImpl_SetNextBlockInChain(
4870 This->parentStorage,
4872 BLOCK_END_OF_CHAIN);
4874 currentBlock = blockIndex;
4878 This->tailIndex = blockIndex;
4879 This->numBlocks = newNumBlocks;
4885 /******************************************************************************
4886 * BlockChainStream_SetSize
4888 * Sets the size of this stream. The big block depot will be updated.
4889 * The file will grow if we grow the chain.
4891 * TODO: Free the actual blocks in the file when we shrink the chain.
4892 * Currently, the blocks are still in the file. So the file size
4893 * doesn't shrink even if we shrink streams.
4895 BOOL BlockChainStream_SetSize(
4896 BlockChainStream* This,
4897 ULARGE_INTEGER newSize)
4899 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4901 if (newSize.u.LowPart == size.u.LowPart)
4904 if (newSize.u.LowPart < size.u.LowPart)
4906 BlockChainStream_Shrink(This, newSize);
4910 BlockChainStream_Enlarge(This, newSize);
4916 /******************************************************************************
4917 * BlockChainStream_GetSize
4919 * Returns the size of this chain.
4920 * Will return the block count if this chain doesn't have a property.
4922 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4924 StgProperty chainProperty;
4926 if(This->headOfStreamPlaceHolder == NULL)
4929 * This chain is a data stream read the property and return
4930 * the appropriate size
4932 StorageImpl_ReadProperty(
4933 This->parentStorage,
4934 This->ownerPropertyIndex,
4937 return chainProperty.size;
4942 * this chain is a chain that does not have a property, figure out the
4943 * size by making the product number of used blocks times the
4946 ULARGE_INTEGER result;
4947 result.u.HighPart = 0;
4950 BlockChainStream_GetCount(This) *
4951 This->parentStorage->bigBlockSize;
4957 /******************************************************************************
4958 ** SmallBlockChainStream implementation
4961 SmallBlockChainStream* SmallBlockChainStream_Construct(
4962 StorageImpl* parentStorage,
4963 ULONG propertyIndex)
4965 SmallBlockChainStream* newStream;
4967 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4969 newStream->parentStorage = parentStorage;
4970 newStream->ownerPropertyIndex = propertyIndex;
4975 void SmallBlockChainStream_Destroy(
4976 SmallBlockChainStream* This)
4978 HeapFree(GetProcessHeap(), 0, This);
4981 /******************************************************************************
4982 * SmallBlockChainStream_GetHeadOfChain
4984 * Returns the head of this chain of small blocks.
4986 static ULONG SmallBlockChainStream_GetHeadOfChain(
4987 SmallBlockChainStream* This)
4989 StgProperty chainProperty;
4990 BOOL readSuccessful;
4992 if (This->ownerPropertyIndex)
4994 readSuccessful = StorageImpl_ReadProperty(
4995 This->parentStorage,
4996 This->ownerPropertyIndex,
5001 return chainProperty.startingBlock;
5006 return BLOCK_END_OF_CHAIN;
5009 /******************************************************************************
5010 * SmallBlockChainStream_GetNextBlockInChain
5012 * Returns the index of the next small block in this chain.
5015 * - BLOCK_END_OF_CHAIN: end of this chain
5016 * - BLOCK_UNUSED: small block 'blockIndex' is free
5018 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5019 SmallBlockChainStream* This,
5021 ULONG* nextBlockInChain)
5023 ULARGE_INTEGER offsetOfBlockInDepot;
5028 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5030 offsetOfBlockInDepot.u.HighPart = 0;
5031 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5034 * Read those bytes in the buffer from the small block file.
5036 res = BlockChainStream_ReadAt(
5037 This->parentStorage->smallBlockDepotChain,
5038 offsetOfBlockInDepot,
5045 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5052 /******************************************************************************
5053 * SmallBlockChainStream_SetNextBlockInChain
5055 * Writes the index of the next block of the specified block in the small
5057 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5058 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5060 static void SmallBlockChainStream_SetNextBlockInChain(
5061 SmallBlockChainStream* This,
5065 ULARGE_INTEGER offsetOfBlockInDepot;
5069 offsetOfBlockInDepot.u.HighPart = 0;
5070 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5072 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5075 * Read those bytes in the buffer from the small block file.
5077 BlockChainStream_WriteAt(
5078 This->parentStorage->smallBlockDepotChain,
5079 offsetOfBlockInDepot,
5085 /******************************************************************************
5086 * SmallBlockChainStream_FreeBlock
5088 * Flag small block 'blockIndex' as free in the small block depot.
5090 static void SmallBlockChainStream_FreeBlock(
5091 SmallBlockChainStream* This,
5094 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5097 /******************************************************************************
5098 * SmallBlockChainStream_GetNextFreeBlock
5100 * Returns the index of a free small block. The small block depot will be
5101 * enlarged if necessary. The small block chain will also be enlarged if
5104 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5105 SmallBlockChainStream* This)
5107 ULARGE_INTEGER offsetOfBlockInDepot;
5110 ULONG blockIndex = 0;
5111 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5113 ULONG smallBlocksPerBigBlock;
5115 offsetOfBlockInDepot.u.HighPart = 0;
5118 * Scan the small block depot for a free block
5120 while (nextBlockIndex != BLOCK_UNUSED)
5122 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5124 res = BlockChainStream_ReadAt(
5125 This->parentStorage->smallBlockDepotChain,
5126 offsetOfBlockInDepot,
5132 * If we run out of space for the small block depot, enlarge it
5136 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5138 if (nextBlockIndex != BLOCK_UNUSED)
5144 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5146 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5147 ULONG nextBlock, newsbdIndex;
5148 BYTE* smallBlockDepot;
5150 nextBlock = sbdIndex;
5151 while (nextBlock != BLOCK_END_OF_CHAIN)
5153 sbdIndex = nextBlock;
5154 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5157 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5158 if (sbdIndex != BLOCK_END_OF_CHAIN)
5159 StorageImpl_SetNextBlockInChain(
5160 This->parentStorage,
5164 StorageImpl_SetNextBlockInChain(
5165 This->parentStorage,
5167 BLOCK_END_OF_CHAIN);
5170 * Initialize all the small blocks to free
5173 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
5175 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5176 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
5181 * We have just created the small block depot.
5183 StgProperty rootProp;
5187 * Save it in the header
5189 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5190 StorageImpl_SaveFileHeader(This->parentStorage);
5193 * And allocate the first big block that will contain small blocks
5196 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5198 StorageImpl_SetNextBlockInChain(
5199 This->parentStorage,
5201 BLOCK_END_OF_CHAIN);
5203 StorageImpl_ReadProperty(
5204 This->parentStorage,
5205 This->parentStorage->base.rootPropertySetIndex,
5208 rootProp.startingBlock = sbStartIndex;
5209 rootProp.size.u.HighPart = 0;
5210 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5212 StorageImpl_WriteProperty(
5213 This->parentStorage,
5214 This->parentStorage->base.rootPropertySetIndex,
5220 smallBlocksPerBigBlock =
5221 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5224 * Verify if we have to allocate big blocks to contain small blocks
5226 if (blockIndex % smallBlocksPerBigBlock == 0)
5228 StgProperty rootProp;
5229 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5231 StorageImpl_ReadProperty(
5232 This->parentStorage,
5233 This->parentStorage->base.rootPropertySetIndex,
5236 if (rootProp.size.u.LowPart <
5237 (blocksRequired * This->parentStorage->bigBlockSize))
5239 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5241 BlockChainStream_SetSize(
5242 This->parentStorage->smallBlockRootChain,
5245 StorageImpl_WriteProperty(
5246 This->parentStorage,
5247 This->parentStorage->base.rootPropertySetIndex,
5255 /******************************************************************************
5256 * SmallBlockChainStream_ReadAt
5258 * Reads a specified number of bytes from this chain at the specified offset.
5259 * bytesRead may be NULL.
5260 * Failure will be returned if the specified number of bytes has not been read.
5262 HRESULT SmallBlockChainStream_ReadAt(
5263 SmallBlockChainStream* This,
5264 ULARGE_INTEGER offset,
5270 ULARGE_INTEGER offsetInBigBlockFile;
5271 ULONG blockNoInSequence =
5272 offset.u.LowPart / This->parentStorage->smallBlockSize;
5274 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5275 ULONG bytesToReadInBuffer;
5277 ULONG bytesReadFromBigBlockFile;
5281 * This should never happen on a small block file.
5283 assert(offset.u.HighPart==0);
5286 * Find the first block in the stream that contains part of the buffer.
5288 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5290 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5292 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5295 blockNoInSequence--;
5299 * Start reading the buffer.
5302 bufferWalker = buffer;
5304 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5307 * Calculate how many bytes we can copy from this small block.
5309 bytesToReadInBuffer =
5310 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5313 * Calculate the offset of the small block in the small block file.
5315 offsetInBigBlockFile.u.HighPart = 0;
5316 offsetInBigBlockFile.u.LowPart =
5317 blockIndex * This->parentStorage->smallBlockSize;
5319 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5322 * Read those bytes in the buffer from the small block file.
5323 * The small block has already been identified so it shouldn't fail
5324 * unless the file is corrupt.
5326 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5327 offsetInBigBlockFile,
5328 bytesToReadInBuffer,
5330 &bytesReadFromBigBlockFile);
5336 * Step to the next big block.
5338 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5340 return STG_E_DOCFILECORRUPT;
5342 bufferWalker += bytesReadFromBigBlockFile;
5343 size -= bytesReadFromBigBlockFile;
5344 *bytesRead += bytesReadFromBigBlockFile;
5345 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5348 return (size == 0) ? S_OK : STG_E_READFAULT;
5351 /******************************************************************************
5352 * SmallBlockChainStream_WriteAt
5354 * Writes the specified number of bytes to this chain at the specified offset.
5355 * bytesWritten may be NULL.
5356 * Will fail if not all specified number of bytes have been written.
5358 HRESULT SmallBlockChainStream_WriteAt(
5359 SmallBlockChainStream* This,
5360 ULARGE_INTEGER offset,
5363 ULONG* bytesWritten)
5365 ULARGE_INTEGER offsetInBigBlockFile;
5366 ULONG blockNoInSequence =
5367 offset.u.LowPart / This->parentStorage->smallBlockSize;
5369 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5370 ULONG bytesToWriteInBuffer;
5372 ULONG bytesWrittenToBigBlockFile;
5373 const BYTE* bufferWalker;
5377 * This should never happen on a small block file.
5379 assert(offset.u.HighPart==0);
5382 * Find the first block in the stream that contains part of the buffer.
5384 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5386 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5388 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5389 return STG_E_DOCFILECORRUPT;
5390 blockNoInSequence--;
5394 * Start writing the buffer.
5396 * Here, I'm casting away the constness on the buffer variable
5397 * This is OK since we don't intend to modify that buffer.
5400 bufferWalker = (const BYTE*)buffer;
5401 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5404 * Calculate how many bytes we can copy to this small block.
5406 bytesToWriteInBuffer =
5407 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5410 * Calculate the offset of the small block in the small block file.
5412 offsetInBigBlockFile.u.HighPart = 0;
5413 offsetInBigBlockFile.u.LowPart =
5414 blockIndex * This->parentStorage->smallBlockSize;
5416 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5419 * Write those bytes in the buffer to the small block file.
5421 res = BlockChainStream_WriteAt(
5422 This->parentStorage->smallBlockRootChain,
5423 offsetInBigBlockFile,
5424 bytesToWriteInBuffer,
5426 &bytesWrittenToBigBlockFile);
5431 * Step to the next big block.
5433 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5436 bufferWalker += bytesWrittenToBigBlockFile;
5437 size -= bytesWrittenToBigBlockFile;
5438 *bytesWritten += bytesWrittenToBigBlockFile;
5439 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5442 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5445 /******************************************************************************
5446 * SmallBlockChainStream_Shrink
5448 * Shrinks this chain in the small block depot.
5450 static BOOL SmallBlockChainStream_Shrink(
5451 SmallBlockChainStream* This,
5452 ULARGE_INTEGER newSize)
5454 ULONG blockIndex, extraBlock;
5458 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5460 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5463 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5466 * Go to the new end of chain
5468 while (count < numBlocks)
5470 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5477 * If the count is 0, we have a special case, the head of the chain was
5482 StgProperty chainProp;
5484 StorageImpl_ReadProperty(This->parentStorage,
5485 This->ownerPropertyIndex,
5488 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5490 StorageImpl_WriteProperty(This->parentStorage,
5491 This->ownerPropertyIndex,
5495 * We start freeing the chain at the head block.
5497 extraBlock = blockIndex;
5501 /* Get the next block before marking the new end */
5502 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5506 /* Mark the new end of chain */
5507 SmallBlockChainStream_SetNextBlockInChain(
5510 BLOCK_END_OF_CHAIN);
5514 * Mark the extra blocks as free
5516 while (extraBlock != BLOCK_END_OF_CHAIN)
5518 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5521 SmallBlockChainStream_FreeBlock(This, extraBlock);
5522 extraBlock = blockIndex;
5528 /******************************************************************************
5529 * SmallBlockChainStream_Enlarge
5531 * Grows this chain in the small block depot.
5533 static BOOL SmallBlockChainStream_Enlarge(
5534 SmallBlockChainStream* This,
5535 ULARGE_INTEGER newSize)
5537 ULONG blockIndex, currentBlock;
5539 ULONG oldNumBlocks = 0;
5541 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5546 if (blockIndex == BLOCK_END_OF_CHAIN)
5549 StgProperty chainProp;
5551 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5554 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5556 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5559 blockIndex = chainProp.startingBlock;
5560 SmallBlockChainStream_SetNextBlockInChain(
5563 BLOCK_END_OF_CHAIN);
5566 currentBlock = blockIndex;
5569 * Figure out how many blocks are needed to contain this stream
5571 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5573 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5577 * Go to the current end of chain
5579 while (blockIndex != BLOCK_END_OF_CHAIN)
5582 currentBlock = blockIndex;
5583 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5588 * Add new blocks to the chain
5590 while (oldNumBlocks < newNumBlocks)
5592 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5593 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5595 SmallBlockChainStream_SetNextBlockInChain(
5598 BLOCK_END_OF_CHAIN);
5600 currentBlock = blockIndex;
5607 /******************************************************************************
5608 * SmallBlockChainStream_SetSize
5610 * Sets the size of this stream.
5611 * The file will grow if we grow the chain.
5613 * TODO: Free the actual blocks in the file when we shrink the chain.
5614 * Currently, the blocks are still in the file. So the file size
5615 * doesn't shrink even if we shrink streams.
5617 BOOL SmallBlockChainStream_SetSize(
5618 SmallBlockChainStream* This,
5619 ULARGE_INTEGER newSize)
5621 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5623 if (newSize.u.LowPart == size.u.LowPart)
5626 if (newSize.u.LowPart < size.u.LowPart)
5628 SmallBlockChainStream_Shrink(This, newSize);
5632 SmallBlockChainStream_Enlarge(This, newSize);
5638 /******************************************************************************
5639 * SmallBlockChainStream_GetSize
5641 * Returns the size of this chain.
5643 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5645 StgProperty chainProperty;
5647 StorageImpl_ReadProperty(
5648 This->parentStorage,
5649 This->ownerPropertyIndex,
5652 return chainProperty.size;
5655 /******************************************************************************
5656 * StgCreateDocfile [OLE32.@]
5657 * Creates a new compound file storage object
5660 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5661 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5662 * reserved [ ?] unused?, usually 0
5663 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5666 * S_OK if the file was successfully created
5667 * some STG_E_ value if error
5669 * if pwcsName is NULL, create file with new unique name
5670 * the function can returns
5671 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5674 HRESULT WINAPI StgCreateDocfile(
5678 IStorage **ppstgOpen)
5680 StorageImpl* newStorage = 0;
5681 HANDLE hFile = INVALID_HANDLE_VALUE;
5682 HRESULT hr = STG_E_INVALIDFLAG;
5686 DWORD fileAttributes;
5687 WCHAR tempFileName[MAX_PATH];
5689 TRACE("(%s, %x, %d, %p)\n",
5690 debugstr_w(pwcsName), grfMode,
5691 reserved, ppstgOpen);
5694 * Validate the parameters
5697 return STG_E_INVALIDPOINTER;
5699 return STG_E_INVALIDPARAMETER;
5702 * Validate the STGM flags
5704 if ( FAILED( validateSTGM(grfMode) ))
5707 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5708 switch(STGM_ACCESS_MODE(grfMode))
5711 case STGM_READWRITE:
5717 /* if no share mode given then DENY_NONE is the default */
5718 if (STGM_SHARE_MODE(grfMode) == 0)
5719 grfMode |= STGM_SHARE_DENY_NONE;
5721 /* must have at least one access mode */
5722 if (STGM_ACCESS_MODE(grfMode) == 0)
5725 /* in direct mode, can only use SHARE_EXCLUSIVE */
5726 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5729 /* but in transacted mode, any share mode is valid */
5732 * Generate a unique name.
5736 WCHAR tempPath[MAX_PATH];
5737 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5739 memset(tempPath, 0, sizeof(tempPath));
5740 memset(tempFileName, 0, sizeof(tempFileName));
5742 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5745 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5746 pwcsName = tempFileName;
5749 hr = STG_E_INSUFFICIENTMEMORY;
5753 creationMode = TRUNCATE_EXISTING;
5757 creationMode = GetCreationModeFromSTGM(grfMode);
5761 * Interpret the STGM value grfMode
5763 shareMode = GetShareModeFromSTGM(grfMode);
5764 accessMode = GetAccessModeFromSTGM(grfMode);
5766 if (grfMode & STGM_DELETEONRELEASE)
5767 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5769 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5771 if (grfMode & STGM_TRANSACTED)
5772 FIXME("Transacted mode not implemented.\n");
5775 * Initialize the "out" parameter.
5779 hFile = CreateFileW(pwcsName,
5787 if (hFile == INVALID_HANDLE_VALUE)
5789 if(GetLastError() == ERROR_FILE_EXISTS)
5790 hr = STG_E_FILEALREADYEXISTS;
5797 * Allocate and initialize the new IStorage32object.
5799 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5801 if (newStorage == 0)
5803 hr = STG_E_INSUFFICIENTMEMORY;
5807 hr = StorageImpl_Construct(
5818 HeapFree(GetProcessHeap(), 0, newStorage);
5823 * Get an "out" pointer for the caller.
5825 hr = StorageBaseImpl_QueryInterface(
5826 (IStorage*)newStorage,
5827 (REFIID)&IID_IStorage,
5830 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5835 /******************************************************************************
5836 * StgCreateStorageEx [OLE32.@]
5838 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5840 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5841 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5843 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5845 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5846 return STG_E_INVALIDPARAMETER;
5849 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5851 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5852 return STG_E_INVALIDPARAMETER;
5855 if (stgfmt == STGFMT_FILE)
5857 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5858 return STG_E_INVALIDPARAMETER;
5861 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5863 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5864 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5867 ERR("Invalid stgfmt argument\n");
5868 return STG_E_INVALIDPARAMETER;
5871 /******************************************************************************
5872 * StgCreatePropSetStg [OLE32.@]
5874 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5875 IPropertySetStorage **ppPropSetStg)
5879 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5881 hr = STG_E_INVALIDPARAMETER;
5883 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5884 (void**)ppPropSetStg);
5888 /******************************************************************************
5889 * StgOpenStorageEx [OLE32.@]
5891 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5893 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5894 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5896 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5898 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5899 return STG_E_INVALIDPARAMETER;
5905 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5906 return STG_E_INVALIDPARAMETER;
5908 case STGFMT_STORAGE:
5911 case STGFMT_DOCFILE:
5912 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5914 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5915 return STG_E_INVALIDPARAMETER;
5917 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5921 WARN("STGFMT_ANY assuming storage\n");
5925 return STG_E_INVALIDPARAMETER;
5928 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5932 /******************************************************************************
5933 * StgOpenStorage [OLE32.@]
5935 HRESULT WINAPI StgOpenStorage(
5936 const OLECHAR *pwcsName,
5937 IStorage *pstgPriority,
5941 IStorage **ppstgOpen)
5943 StorageImpl* newStorage = 0;
5948 WCHAR fullname[MAX_PATH];
5951 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5952 debugstr_w(pwcsName), pstgPriority, grfMode,
5953 snbExclude, reserved, ppstgOpen);
5956 * Perform sanity checks
5960 hr = STG_E_INVALIDNAME;
5966 hr = STG_E_INVALIDPOINTER;
5972 hr = STG_E_INVALIDPARAMETER;
5976 if (grfMode & STGM_PRIORITY)
5978 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5979 return STG_E_INVALIDFLAG;
5980 if (grfMode & STGM_DELETEONRELEASE)
5981 return STG_E_INVALIDFUNCTION;
5982 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5983 return STG_E_INVALIDFLAG;
5984 grfMode &= ~0xf0; /* remove the existing sharing mode */
5985 grfMode |= STGM_SHARE_DENY_NONE;
5987 /* STGM_PRIORITY stops other IStorage objects on the same file from
5988 * committing until the STGM_PRIORITY IStorage is closed. it also
5989 * stops non-transacted mode StgOpenStorage calls with write access from
5990 * succeeding. obviously, both of these cannot be achieved through just
5991 * file share flags */
5992 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5996 * Validate the sharing mode
5998 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5999 switch(STGM_SHARE_MODE(grfMode))
6001 case STGM_SHARE_EXCLUSIVE:
6002 case STGM_SHARE_DENY_WRITE:
6005 hr = STG_E_INVALIDFLAG;
6010 * Validate the STGM flags
6012 if ( FAILED( validateSTGM(grfMode) ) ||
6013 (grfMode&STGM_CREATE))
6015 hr = STG_E_INVALIDFLAG;
6019 /* shared reading requires transacted mode */
6020 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6021 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6022 !(grfMode&STGM_TRANSACTED) )
6024 hr = STG_E_INVALIDFLAG;
6029 * Interpret the STGM value grfMode
6031 shareMode = GetShareModeFromSTGM(grfMode);
6032 accessMode = GetAccessModeFromSTGM(grfMode);
6035 * Initialize the "out" parameter.
6039 if ((accessMode & GENERIC_WRITE) && /* try to create a file if no yet exists */
6040 ((hFile = CreateFileW( pwcsName, accessMode, shareMode, NULL, CREATE_NEW,
6041 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, 0))
6042 != INVALID_HANDLE_VALUE))
6049 hFile = CreateFileW( pwcsName,
6054 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6058 if (hFile==INVALID_HANDLE_VALUE)
6060 DWORD last_error = GetLastError();
6066 case ERROR_FILE_NOT_FOUND:
6067 hr = STG_E_FILENOTFOUND;
6070 case ERROR_PATH_NOT_FOUND:
6071 hr = STG_E_PATHNOTFOUND;
6074 case ERROR_ACCESS_DENIED:
6075 case ERROR_WRITE_PROTECT:
6076 hr = STG_E_ACCESSDENIED;
6079 case ERROR_SHARING_VIOLATION:
6080 hr = STG_E_SHAREVIOLATION;
6091 * Refuse to open the file if it's too small to be a structured storage file
6092 * FIXME: verify the file when reading instead of here
6094 if (!newFile && GetFileSize(hFile, NULL) < 0x100)
6097 hr = STG_E_FILEALREADYEXISTS;
6102 * Allocate and initialize the new IStorage32object.
6104 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6106 if (newStorage == 0)
6108 hr = STG_E_INSUFFICIENTMEMORY;
6112 /* if we created new file, initialize the storage */
6113 hr = StorageImpl_Construct(
6124 HeapFree(GetProcessHeap(), 0, newStorage);
6126 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6128 if(hr == STG_E_INVALIDHEADER)
6129 hr = STG_E_FILEALREADYEXISTS;
6133 /* prepare the file name string given in lieu of the root property name */
6134 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6135 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6136 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6139 * Get an "out" pointer for the caller.
6141 hr = StorageBaseImpl_QueryInterface(
6142 (IStorage*)newStorage,
6143 (REFIID)&IID_IStorage,
6147 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6151 /******************************************************************************
6152 * StgCreateDocfileOnILockBytes [OLE32.@]
6154 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6158 IStorage** ppstgOpen)
6160 StorageImpl* newStorage = 0;
6164 * Validate the parameters
6166 if ((ppstgOpen == 0) || (plkbyt == 0))
6167 return STG_E_INVALIDPOINTER;
6170 * Allocate and initialize the new IStorage object.
6172 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6174 if (newStorage == 0)
6175 return STG_E_INSUFFICIENTMEMORY;
6177 hr = StorageImpl_Construct(
6188 HeapFree(GetProcessHeap(), 0, newStorage);
6193 * Get an "out" pointer for the caller.
6195 hr = StorageBaseImpl_QueryInterface(
6196 (IStorage*)newStorage,
6197 (REFIID)&IID_IStorage,
6203 /******************************************************************************
6204 * StgOpenStorageOnILockBytes [OLE32.@]
6206 HRESULT WINAPI StgOpenStorageOnILockBytes(
6208 IStorage *pstgPriority,
6212 IStorage **ppstgOpen)
6214 StorageImpl* newStorage = 0;
6218 * Perform a sanity check
6220 if ((plkbyt == 0) || (ppstgOpen == 0))
6221 return STG_E_INVALIDPOINTER;
6224 * Validate the STGM flags
6226 if ( FAILED( validateSTGM(grfMode) ))
6227 return STG_E_INVALIDFLAG;
6230 * Initialize the "out" parameter.
6235 * Allocate and initialize the new IStorage object.
6237 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6239 if (newStorage == 0)
6240 return STG_E_INSUFFICIENTMEMORY;
6242 hr = StorageImpl_Construct(
6253 HeapFree(GetProcessHeap(), 0, newStorage);
6258 * Get an "out" pointer for the caller.
6260 hr = StorageBaseImpl_QueryInterface(
6261 (IStorage*)newStorage,
6262 (REFIID)&IID_IStorage,
6268 /******************************************************************************
6269 * StgSetTimes [ole32.@]
6270 * StgSetTimes [OLE32.@]
6274 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6275 FILETIME const *patime, FILETIME const *pmtime)
6277 IStorage *stg = NULL;
6280 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6282 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6286 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6287 IStorage_Release(stg);
6293 /******************************************************************************
6294 * StgIsStorageILockBytes [OLE32.@]
6296 * Determines if the ILockBytes contains a storage object.
6298 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6301 ULARGE_INTEGER offset;
6303 offset.u.HighPart = 0;
6304 offset.u.LowPart = 0;
6306 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6308 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6314 /******************************************************************************
6315 * WriteClassStg [OLE32.@]
6317 * This method will store the specified CLSID in the specified storage object
6319 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6324 return E_INVALIDARG;
6326 hRes = IStorage_SetClass(pStg, rclsid);
6331 /***********************************************************************
6332 * ReadClassStg (OLE32.@)
6334 * This method reads the CLSID previously written to a storage object with
6335 * the WriteClassStg.
6338 * pstg [I] IStorage pointer
6339 * pclsid [O] Pointer to where the CLSID is written
6343 * Failure: HRESULT code.
6345 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6350 TRACE("(%p, %p)\n", pstg, pclsid);
6352 if(!pstg || !pclsid)
6353 return E_INVALIDARG;
6356 * read a STATSTG structure (contains the clsid) from the storage
6358 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6361 *pclsid=pstatstg.clsid;
6366 /***********************************************************************
6367 * OleLoadFromStream (OLE32.@)
6369 * This function loads an object from stream
6371 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6375 LPPERSISTSTREAM xstm;
6377 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6379 res=ReadClassStm(pStm,&clsid);
6380 if (!SUCCEEDED(res))
6382 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6383 if (!SUCCEEDED(res))
6385 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6386 if (!SUCCEEDED(res)) {
6387 IUnknown_Release((IUnknown*)*ppvObj);
6390 res=IPersistStream_Load(xstm,pStm);
6391 IPersistStream_Release(xstm);
6392 /* FIXME: all refcounts ok at this point? I think they should be:
6395 * xstm : 0 (released)
6400 /***********************************************************************
6401 * OleSaveToStream (OLE32.@)
6403 * This function saves an object with the IPersistStream interface on it
6404 * to the specified stream.
6406 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6412 TRACE("(%p,%p)\n",pPStm,pStm);
6414 res=IPersistStream_GetClassID(pPStm,&clsid);
6416 if (SUCCEEDED(res)){
6418 res=WriteClassStm(pStm,&clsid);
6422 res=IPersistStream_Save(pPStm,pStm,TRUE);
6425 TRACE("Finished Save\n");
6429 /****************************************************************************
6430 * This method validate a STGM parameter that can contain the values below
6432 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6433 * The stgm values contained in 0xffff0000 are bitmasks.
6435 * STGM_DIRECT 0x00000000
6436 * STGM_TRANSACTED 0x00010000
6437 * STGM_SIMPLE 0x08000000
6439 * STGM_READ 0x00000000
6440 * STGM_WRITE 0x00000001
6441 * STGM_READWRITE 0x00000002
6443 * STGM_SHARE_DENY_NONE 0x00000040
6444 * STGM_SHARE_DENY_READ 0x00000030
6445 * STGM_SHARE_DENY_WRITE 0x00000020
6446 * STGM_SHARE_EXCLUSIVE 0x00000010
6448 * STGM_PRIORITY 0x00040000
6449 * STGM_DELETEONRELEASE 0x04000000
6451 * STGM_CREATE 0x00001000
6452 * STGM_CONVERT 0x00020000
6453 * STGM_FAILIFTHERE 0x00000000
6455 * STGM_NOSCRATCH 0x00100000
6456 * STGM_NOSNAPSHOT 0x00200000
6458 static HRESULT validateSTGM(DWORD stgm)
6460 DWORD access = STGM_ACCESS_MODE(stgm);
6461 DWORD share = STGM_SHARE_MODE(stgm);
6462 DWORD create = STGM_CREATE_MODE(stgm);
6464 if (stgm&~STGM_KNOWN_FLAGS)
6466 ERR("unknown flags %08x\n", stgm);
6474 case STGM_READWRITE:
6482 case STGM_SHARE_DENY_NONE:
6483 case STGM_SHARE_DENY_READ:
6484 case STGM_SHARE_DENY_WRITE:
6485 case STGM_SHARE_EXCLUSIVE:
6494 case STGM_FAILIFTHERE:
6501 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6503 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6507 * STGM_CREATE | STGM_CONVERT
6508 * if both are false, STGM_FAILIFTHERE is set to TRUE
6510 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6514 * STGM_NOSCRATCH requires STGM_TRANSACTED
6516 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6520 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6521 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6523 if ( (stgm & STGM_NOSNAPSHOT) &&
6524 (!(stgm & STGM_TRANSACTED) ||
6525 share == STGM_SHARE_EXCLUSIVE ||
6526 share == STGM_SHARE_DENY_WRITE) )
6532 /****************************************************************************
6533 * GetShareModeFromSTGM
6535 * This method will return a share mode flag from a STGM value.
6536 * The STGM value is assumed valid.
6538 static DWORD GetShareModeFromSTGM(DWORD stgm)
6540 switch (STGM_SHARE_MODE(stgm))
6542 case STGM_SHARE_DENY_NONE:
6543 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6544 case STGM_SHARE_DENY_READ:
6545 return FILE_SHARE_WRITE;
6546 case STGM_SHARE_DENY_WRITE:
6547 return FILE_SHARE_READ;
6548 case STGM_SHARE_EXCLUSIVE:
6551 ERR("Invalid share mode!\n");
6556 /****************************************************************************
6557 * GetAccessModeFromSTGM
6559 * This method will return an access mode flag from a STGM value.
6560 * The STGM value is assumed valid.
6562 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6564 switch (STGM_ACCESS_MODE(stgm))
6567 return GENERIC_READ;
6569 case STGM_READWRITE:
6570 return GENERIC_READ | GENERIC_WRITE;
6572 ERR("Invalid access mode!\n");
6577 /****************************************************************************
6578 * GetCreationModeFromSTGM
6580 * This method will return a creation mode flag from a STGM value.
6581 * The STGM value is assumed valid.
6583 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6585 switch(STGM_CREATE_MODE(stgm))
6588 return CREATE_ALWAYS;
6590 FIXME("STGM_CONVERT not implemented!\n");
6592 case STGM_FAILIFTHERE:
6595 ERR("Invalid create mode!\n");
6601 /*************************************************************************
6602 * OLECONVERT_LoadOLE10 [Internal]
6604 * Loads the OLE10 STREAM to memory
6607 * pOleStream [I] The OLESTREAM
6608 * pData [I] Data Structure for the OLESTREAM Data
6612 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6613 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6616 * This function is used by OleConvertOLESTREAMToIStorage only.
6618 * Memory allocated for pData must be freed by the caller
6620 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6623 HRESULT hRes = S_OK;
6627 pData->pData = NULL;
6628 pData->pstrOleObjFileName = (CHAR *) NULL;
6630 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6633 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6634 if(dwSize != sizeof(pData->dwOleID))
6636 hRes = CONVERT10_E_OLESTREAM_GET;
6638 else if(pData->dwOleID != OLESTREAM_ID)
6640 hRes = CONVERT10_E_OLESTREAM_FMT;
6651 /* Get the TypeID...more info needed for this field */
6652 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6653 if(dwSize != sizeof(pData->dwTypeID))
6655 hRes = CONVERT10_E_OLESTREAM_GET;
6660 if(pData->dwTypeID != 0)
6662 /* Get the length of the OleTypeName */
6663 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6664 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6666 hRes = CONVERT10_E_OLESTREAM_GET;
6671 if(pData->dwOleTypeNameLength > 0)
6673 /* Get the OleTypeName */
6674 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6675 if(dwSize != pData->dwOleTypeNameLength)
6677 hRes = CONVERT10_E_OLESTREAM_GET;
6683 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6684 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6686 hRes = CONVERT10_E_OLESTREAM_GET;
6690 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6691 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6692 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6693 if(pData->pstrOleObjFileName)
6695 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6696 if(dwSize != pData->dwOleObjFileNameLength)
6698 hRes = CONVERT10_E_OLESTREAM_GET;
6702 hRes = CONVERT10_E_OLESTREAM_GET;
6707 /* Get the Width of the Metafile */
6708 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6709 if(dwSize != sizeof(pData->dwMetaFileWidth))
6711 hRes = CONVERT10_E_OLESTREAM_GET;
6715 /* Get the Height of the Metafile */
6716 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6717 if(dwSize != sizeof(pData->dwMetaFileHeight))
6719 hRes = CONVERT10_E_OLESTREAM_GET;
6725 /* Get the Length of the Data */
6726 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6727 if(dwSize != sizeof(pData->dwDataLength))
6729 hRes = CONVERT10_E_OLESTREAM_GET;
6733 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6735 if(!bStrem1) /* if it is a second OLE stream data */
6737 pData->dwDataLength -= 8;
6738 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6739 if(dwSize != sizeof(pData->strUnknown))
6741 hRes = CONVERT10_E_OLESTREAM_GET;
6747 if(pData->dwDataLength > 0)
6749 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6751 /* Get Data (ex. IStorage, Metafile, or BMP) */
6754 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6755 if(dwSize != pData->dwDataLength)
6757 hRes = CONVERT10_E_OLESTREAM_GET;
6762 hRes = CONVERT10_E_OLESTREAM_GET;
6771 /*************************************************************************
6772 * OLECONVERT_SaveOLE10 [Internal]
6774 * Saves the OLE10 STREAM From memory
6777 * pData [I] Data Structure for the OLESTREAM Data
6778 * pOleStream [I] The OLESTREAM to save
6782 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6785 * This function is used by OleConvertIStorageToOLESTREAM only.
6788 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6791 HRESULT hRes = S_OK;
6795 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6796 if(dwSize != sizeof(pData->dwOleID))
6798 hRes = CONVERT10_E_OLESTREAM_PUT;
6803 /* Set the TypeID */
6804 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6805 if(dwSize != sizeof(pData->dwTypeID))
6807 hRes = CONVERT10_E_OLESTREAM_PUT;
6811 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6813 /* Set the Length of the OleTypeName */
6814 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6815 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6817 hRes = CONVERT10_E_OLESTREAM_PUT;
6822 if(pData->dwOleTypeNameLength > 0)
6824 /* Set the OleTypeName */
6825 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6826 if(dwSize != pData->dwOleTypeNameLength)
6828 hRes = CONVERT10_E_OLESTREAM_PUT;
6835 /* Set the width of the Metafile */
6836 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6837 if(dwSize != sizeof(pData->dwMetaFileWidth))
6839 hRes = CONVERT10_E_OLESTREAM_PUT;
6845 /* Set the height of the Metafile */
6846 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6847 if(dwSize != sizeof(pData->dwMetaFileHeight))
6849 hRes = CONVERT10_E_OLESTREAM_PUT;
6855 /* Set the length of the Data */
6856 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6857 if(dwSize != sizeof(pData->dwDataLength))
6859 hRes = CONVERT10_E_OLESTREAM_PUT;
6865 if(pData->dwDataLength > 0)
6867 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6868 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6869 if(dwSize != pData->dwDataLength)
6871 hRes = CONVERT10_E_OLESTREAM_PUT;
6879 /*************************************************************************
6880 * OLECONVERT_GetOLE20FromOLE10[Internal]
6882 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6883 * opens it, and copies the content to the dest IStorage for
6884 * OleConvertOLESTREAMToIStorage
6888 * pDestStorage [I] The IStorage to copy the data to
6889 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6890 * nBufferLength [I] The size of the buffer
6899 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6903 IStorage *pTempStorage;
6904 DWORD dwNumOfBytesWritten;
6905 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6906 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6908 /* Create a temp File */
6909 GetTempPathW(MAX_PATH, wstrTempDir);
6910 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6911 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6913 if(hFile != INVALID_HANDLE_VALUE)
6915 /* Write IStorage Data to File */
6916 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6919 /* Open and copy temp storage to the Dest Storage */
6920 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6923 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6924 StorageBaseImpl_Release(pTempStorage);
6926 DeleteFileW(wstrTempFile);
6931 /*************************************************************************
6932 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6934 * Saves the OLE10 STREAM From memory
6937 * pStorage [I] The Src IStorage to copy
6938 * pData [I] The Dest Memory to write to.
6941 * The size in bytes allocated for pData
6944 * Memory allocated for pData must be freed by the caller
6946 * Used by OleConvertIStorageToOLESTREAM only.
6949 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6953 DWORD nDataLength = 0;
6954 IStorage *pTempStorage;
6955 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6956 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6960 /* Create temp Storage */
6961 GetTempPathW(MAX_PATH, wstrTempDir);
6962 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6963 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6967 /* Copy Src Storage to the Temp Storage */
6968 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6969 StorageBaseImpl_Release(pTempStorage);
6971 /* Open Temp Storage as a file and copy to memory */
6972 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6973 if(hFile != INVALID_HANDLE_VALUE)
6975 nDataLength = GetFileSize(hFile, NULL);
6976 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6977 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6980 DeleteFileW(wstrTempFile);
6985 /*************************************************************************
6986 * OLECONVERT_CreateOleStream [Internal]
6988 * Creates the "\001OLE" stream in the IStorage if necessary.
6991 * pStorage [I] Dest storage to create the stream in
6997 * This function is used by OleConvertOLESTREAMToIStorage only.
6999 * This stream is still unknown, MS Word seems to have extra data
7000 * but since the data is stored in the OLESTREAM there should be
7001 * no need to recreate the stream. If the stream is manually
7002 * deleted it will create it with this default data.
7005 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
7009 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
7010 BYTE pOleStreamHeader [] =
7012 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7013 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7014 0x00, 0x00, 0x00, 0x00
7017 /* Create stream if not present */
7018 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7019 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7023 /* Write default Data */
7024 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7025 IStream_Release(pStream);
7029 /* write a string to a stream, preceded by its length */
7030 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7037 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7038 r = IStream_Write( stm, &len, sizeof(len), NULL);
7043 str = CoTaskMemAlloc( len );
7044 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7045 r = IStream_Write( stm, str, len, NULL);
7046 CoTaskMemFree( str );
7050 /* read a string preceded by its length from a stream */
7051 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7054 DWORD len, count = 0;
7058 r = IStream_Read( stm, &len, sizeof(len), &count );
7061 if( count != sizeof(len) )
7062 return E_OUTOFMEMORY;
7064 TRACE("%d bytes\n",len);
7066 str = CoTaskMemAlloc( len );
7068 return E_OUTOFMEMORY;
7070 r = IStream_Read( stm, str, len, &count );
7075 CoTaskMemFree( str );
7076 return E_OUTOFMEMORY;
7079 TRACE("Read string %s\n",debugstr_an(str,len));
7081 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7082 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7084 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7085 CoTaskMemFree( str );
7093 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7094 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7098 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7100 static const BYTE unknown1[12] =
7101 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7102 0xFF, 0xFF, 0xFF, 0xFF};
7103 static const BYTE unknown2[16] =
7104 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7105 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7107 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7108 debugstr_w(lpszUserType), debugstr_w(szClipName),
7109 debugstr_w(szProgIDName));
7111 /* Create a CompObj stream if it doesn't exist */
7112 r = IStorage_CreateStream(pstg, szwStreamName,
7113 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7117 /* Write CompObj Structure to stream */
7118 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7120 if( SUCCEEDED( r ) )
7121 r = WriteClassStm( pstm, clsid );
7123 if( SUCCEEDED( r ) )
7124 r = STREAM_WriteString( pstm, lpszUserType );
7125 if( SUCCEEDED( r ) )
7126 r = STREAM_WriteString( pstm, szClipName );
7127 if( SUCCEEDED( r ) )
7128 r = STREAM_WriteString( pstm, szProgIDName );
7129 if( SUCCEEDED( r ) )
7130 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7132 IStream_Release( pstm );
7137 /***********************************************************************
7138 * WriteFmtUserTypeStg (OLE32.@)
7140 HRESULT WINAPI WriteFmtUserTypeStg(
7141 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7144 WCHAR szwClipName[0x40];
7145 CLSID clsid = CLSID_NULL;
7146 LPWSTR wstrProgID = NULL;
7149 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7151 /* get the clipboard format name */
7152 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
7155 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7157 /* FIXME: There's room to save a CLSID and its ProgID, but
7158 the CLSID is not looked up in the registry and in all the
7159 tests I wrote it was CLSID_NULL. Where does it come from?
7162 /* get the real program ID. This may fail, but that's fine */
7163 ProgIDFromCLSID(&clsid, &wstrProgID);
7165 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7167 r = STORAGE_WriteCompObj( pstg, &clsid,
7168 lpszUserType, szwClipName, wstrProgID );
7170 CoTaskMemFree(wstrProgID);
7176 /******************************************************************************
7177 * ReadFmtUserTypeStg [OLE32.@]
7179 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7183 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7184 unsigned char unknown1[12];
7185 unsigned char unknown2[16];
7187 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7190 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7192 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7193 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7196 WARN("Failed to open stream r = %08x\n", r);
7200 /* read the various parts of the structure */
7201 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7202 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7204 r = ReadClassStm( stm, &clsid );
7208 r = STREAM_ReadString( stm, &szCLSIDName );
7212 r = STREAM_ReadString( stm, &szOleTypeName );
7216 r = STREAM_ReadString( stm, &szProgIDName );
7220 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7221 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7224 /* ok, success... now we just need to store what we found */
7226 *pcf = RegisterClipboardFormatW( szOleTypeName );
7227 CoTaskMemFree( szOleTypeName );
7229 if( lplpszUserType )
7230 *lplpszUserType = szCLSIDName;
7231 CoTaskMemFree( szProgIDName );
7234 IStream_Release( stm );
7240 /*************************************************************************
7241 * OLECONVERT_CreateCompObjStream [Internal]
7243 * Creates a "\001CompObj" is the destination IStorage if necessary.
7246 * pStorage [I] The dest IStorage to create the CompObj Stream
7248 * strOleTypeName [I] The ProgID
7252 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7255 * This function is used by OleConvertOLESTREAMToIStorage only.
7257 * The stream data is stored in the OLESTREAM and there should be
7258 * no need to recreate the stream. If the stream is manually
7259 * deleted it will attempt to create it by querying the registry.
7263 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7266 HRESULT hStorageRes, hRes = S_OK;
7267 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7268 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7269 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7271 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7272 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7274 /* Initialize the CompObj structure */
7275 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7276 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7277 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7280 /* Create a CompObj stream if it doesn't exist */
7281 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7282 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7283 if(hStorageRes == S_OK)
7285 /* copy the OleTypeName to the compobj struct */
7286 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7287 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7289 /* copy the OleTypeName to the compobj struct */
7290 /* Note: in the test made, these were Identical */
7291 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7292 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7295 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7296 bufferW, OLESTREAM_MAX_STR_LEN );
7297 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7303 /* Get the CLSID Default Name from the Registry */
7304 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7305 if(hErr == ERROR_SUCCESS)
7307 char strTemp[OLESTREAM_MAX_STR_LEN];
7308 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7309 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7310 if(hErr == ERROR_SUCCESS)
7312 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7318 /* Write CompObj Structure to stream */
7319 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7321 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7323 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7324 if(IStorageCompObj.dwCLSIDNameLength > 0)
7326 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7328 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7329 if(IStorageCompObj.dwOleTypeNameLength > 0)
7331 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7333 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7334 if(IStorageCompObj.dwProgIDNameLength > 0)
7336 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7338 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7339 IStream_Release(pStream);
7345 /*************************************************************************
7346 * OLECONVERT_CreateOlePresStream[Internal]
7348 * Creates the "\002OlePres000" Stream with the Metafile data
7351 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7352 * dwExtentX [I] Width of the Metafile
7353 * dwExtentY [I] Height of the Metafile
7354 * pData [I] Metafile data
7355 * dwDataLength [I] Size of the Metafile data
7359 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7362 * This function is used by OleConvertOLESTREAMToIStorage only.
7365 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7369 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7370 BYTE pOlePresStreamHeader [] =
7372 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7373 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7374 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7375 0x00, 0x00, 0x00, 0x00
7378 BYTE pOlePresStreamHeaderEmpty [] =
7380 0x00, 0x00, 0x00, 0x00,
7381 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7382 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7383 0x00, 0x00, 0x00, 0x00
7386 /* Create the OlePres000 Stream */
7387 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7388 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7393 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7395 memset(&OlePres, 0, sizeof(OlePres));
7396 /* Do we have any metafile data to save */
7397 if(dwDataLength > 0)
7399 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7400 nHeaderSize = sizeof(pOlePresStreamHeader);
7404 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7405 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7407 /* Set width and height of the metafile */
7408 OlePres.dwExtentX = dwExtentX;
7409 OlePres.dwExtentY = -dwExtentY;
7411 /* Set Data and Length */
7412 if(dwDataLength > sizeof(METAFILEPICT16))
7414 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7415 OlePres.pData = &(pData[8]);
7417 /* Save OlePres000 Data to Stream */
7418 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7419 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7420 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7421 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7422 if(OlePres.dwSize > 0)
7424 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7426 IStream_Release(pStream);
7430 /*************************************************************************
7431 * OLECONVERT_CreateOle10NativeStream [Internal]
7433 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7436 * pStorage [I] Dest storage to create the stream in
7437 * pData [I] Ole10 Native Data (ex. bmp)
7438 * dwDataLength [I] Size of the Ole10 Native Data
7444 * This function is used by OleConvertOLESTREAMToIStorage only.
7446 * Might need to verify the data and return appropriate error message
7449 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7453 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7455 /* Create the Ole10Native Stream */
7456 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7457 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7461 /* Write info to stream */
7462 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7463 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7464 IStream_Release(pStream);
7469 /*************************************************************************
7470 * OLECONVERT_GetOLE10ProgID [Internal]
7472 * Finds the ProgID (or OleTypeID) from the IStorage
7475 * pStorage [I] The Src IStorage to get the ProgID
7476 * strProgID [I] the ProgID string to get
7477 * dwSize [I] the size of the string
7481 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7484 * This function is used by OleConvertIStorageToOLESTREAM only.
7488 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7492 LARGE_INTEGER iSeekPos;
7493 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7494 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7496 /* Open the CompObj Stream */
7497 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7498 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7502 /*Get the OleType from the CompObj Stream */
7503 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7504 iSeekPos.u.HighPart = 0;
7506 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7507 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7508 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7509 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7510 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7511 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7512 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7514 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7517 IStream_Read(pStream, strProgID, *dwSize, NULL);
7519 IStream_Release(pStream);
7524 LPOLESTR wstrProgID;
7526 /* Get the OleType from the registry */
7527 REFCLSID clsid = &(stat.clsid);
7528 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7529 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7532 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7539 /*************************************************************************
7540 * OLECONVERT_GetOle10PresData [Internal]
7542 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7545 * pStorage [I] Src IStroage
7546 * pOleStream [I] Dest OleStream Mem Struct
7552 * This function is used by OleConvertIStorageToOLESTREAM only.
7554 * Memory allocated for pData must be freed by the caller
7558 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7563 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7565 /* Initialize Default data for OLESTREAM */
7566 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7567 pOleStreamData[0].dwTypeID = 2;
7568 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7569 pOleStreamData[1].dwTypeID = 0;
7570 pOleStreamData[0].dwMetaFileWidth = 0;
7571 pOleStreamData[0].dwMetaFileHeight = 0;
7572 pOleStreamData[0].pData = NULL;
7573 pOleStreamData[1].pData = NULL;
7575 /* Open Ole10Native Stream */
7576 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7577 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7581 /* Read Size and Data */
7582 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7583 if(pOleStreamData->dwDataLength > 0)
7585 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7586 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7588 IStream_Release(pStream);
7594 /*************************************************************************
7595 * OLECONVERT_GetOle20PresData[Internal]
7597 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7600 * pStorage [I] Src IStroage
7601 * pOleStreamData [I] Dest OleStream Mem Struct
7607 * This function is used by OleConvertIStorageToOLESTREAM only.
7609 * Memory allocated for pData must be freed by the caller
7611 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7615 OLECONVERT_ISTORAGE_OLEPRES olePress;
7616 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7618 /* Initialize Default data for OLESTREAM */
7619 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7620 pOleStreamData[0].dwTypeID = 2;
7621 pOleStreamData[0].dwMetaFileWidth = 0;
7622 pOleStreamData[0].dwMetaFileHeight = 0;
7623 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7624 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7625 pOleStreamData[1].dwTypeID = 0;
7626 pOleStreamData[1].dwOleTypeNameLength = 0;
7627 pOleStreamData[1].strOleTypeName[0] = 0;
7628 pOleStreamData[1].dwMetaFileWidth = 0;
7629 pOleStreamData[1].dwMetaFileHeight = 0;
7630 pOleStreamData[1].pData = NULL;
7631 pOleStreamData[1].dwDataLength = 0;
7634 /* Open OlePress000 stream */
7635 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7636 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7639 LARGE_INTEGER iSeekPos;
7640 METAFILEPICT16 MetaFilePict;
7641 static const char strMetafilePictName[] = "METAFILEPICT";
7643 /* Set the TypeID for a Metafile */
7644 pOleStreamData[1].dwTypeID = 5;
7646 /* Set the OleTypeName to Metafile */
7647 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7648 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7650 iSeekPos.u.HighPart = 0;
7651 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7653 /* Get Presentation Data */
7654 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7655 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7656 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7657 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7659 /*Set width and Height */
7660 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7661 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7662 if(olePress.dwSize > 0)
7665 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7667 /* Set MetaFilePict struct */
7668 MetaFilePict.mm = 8;
7669 MetaFilePict.xExt = olePress.dwExtentX;
7670 MetaFilePict.yExt = olePress.dwExtentY;
7671 MetaFilePict.hMF = 0;
7673 /* Get Metafile Data */
7674 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7675 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7676 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7678 IStream_Release(pStream);
7682 /*************************************************************************
7683 * OleConvertOLESTREAMToIStorage [OLE32.@]
7688 * DVTARGETDEVICE paramenter is not handled
7689 * Still unsure of some mem fields for OLE 10 Stream
7690 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7691 * and "\001OLE" streams
7694 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7695 LPOLESTREAM pOleStream,
7697 const DVTARGETDEVICE* ptd)
7701 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7703 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7705 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7709 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7712 if(pstg == NULL || pOleStream == NULL)
7714 hRes = E_INVALIDARG;
7719 /* Load the OLESTREAM to Memory */
7720 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7725 /* Load the OLESTREAM to Memory (part 2)*/
7726 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7732 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7734 /* Do we have the IStorage Data in the OLESTREAM */
7735 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7737 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7738 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7742 /* It must be an original OLE 1.0 source */
7743 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7748 /* It must be an original OLE 1.0 source */
7749 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7752 /* Create CompObj Stream if necessary */
7753 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7756 /*Create the Ole Stream if necessary */
7757 OLECONVERT_CreateOleStream(pstg);
7762 /* Free allocated memory */
7763 for(i=0; i < 2; i++)
7765 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7766 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7767 pOleStreamData[i].pstrOleObjFileName = NULL;
7772 /*************************************************************************
7773 * OleConvertIStorageToOLESTREAM [OLE32.@]
7780 * Still unsure of some mem fields for OLE 10 Stream
7781 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7782 * and "\001OLE" streams.
7785 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7787 LPOLESTREAM pOleStream)
7790 HRESULT hRes = S_OK;
7792 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7793 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7795 TRACE("%p %p\n", pstg, pOleStream);
7797 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7799 if(pstg == NULL || pOleStream == NULL)
7801 hRes = E_INVALIDARG;
7805 /* Get the ProgID */
7806 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7807 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7811 /* Was it originally Ole10 */
7812 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7815 IStream_Release(pStream);
7816 /* Get Presentation Data for Ole10Native */
7817 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7821 /* Get Presentation Data (OLE20) */
7822 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7825 /* Save OLESTREAM */
7826 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7829 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7834 /* Free allocated memory */
7835 for(i=0; i < 2; i++)
7837 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7843 /***********************************************************************
7844 * GetConvertStg (OLE32.@)
7846 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7847 FIXME("unimplemented stub!\n");
7851 /******************************************************************************
7852 * StgIsStorageFile [OLE32.@]
7853 * Verify if the file contains a storage object
7859 * S_OK if file has magic bytes as a storage object
7860 * S_FALSE if file is not storage
7863 StgIsStorageFile(LPCOLESTR fn)
7869 TRACE("%s\n", debugstr_w(fn));
7870 hf = CreateFileW(fn, GENERIC_READ,
7871 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7872 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7874 if (hf == INVALID_HANDLE_VALUE)
7875 return STG_E_FILENOTFOUND;
7877 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7879 WARN(" unable to read file\n");
7886 if (bytes_read != 8) {
7887 WARN(" too short\n");
7891 if (!memcmp(magic,STORAGE_magic,8)) {
7896 WARN(" -> Invalid header.\n");
7900 /***********************************************************************
7901 * WriteClassStm (OLE32.@)
7903 * Writes a CLSID to a stream.
7906 * pStm [I] Stream to write to.
7907 * rclsid [I] CLSID to write.
7911 * Failure: HRESULT code.
7913 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7915 TRACE("(%p,%p)\n",pStm,rclsid);
7918 return E_INVALIDARG;
7920 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7923 /***********************************************************************
7924 * ReadClassStm (OLE32.@)
7926 * Reads a CLSID from a stream.
7929 * pStm [I] Stream to read from.
7930 * rclsid [O] CLSID to read.
7934 * Failure: HRESULT code.
7936 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7941 TRACE("(%p,%p)\n",pStm,pclsid);
7944 return E_INVALIDARG;
7946 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7951 if (nbByte != sizeof(CLSID))