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 %ld\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 %ld\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, %lx, %ld, %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("<-- %08lx\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, %lx, %p, %ld, %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("<-- %08lx\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, %ld, %p, %ld, %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, %lx)\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: %ld, cbSize.Low/High: %ld/%ld, grfMode: %08lx, grfLocksSupported: %ld, grfStateBits: %08lx\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
740 TRACE("<-- %08lx\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, %lx, %ld, %ld, %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, %lx, %ld, %ld, %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%lx\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, %ld, %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: %ld\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 %lu): 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 %ld): 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 %ld, bigBlockDepotCount %ld\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;
3306 BOOL readSuccessful;
3309 offsetInPropSet.u.HighPart = 0;
3310 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3312 readSuccessful = BlockChainStream_ReadAt(
3313 This->rootBlockChain,
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 readSuccessful;
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;
3405 BOOL writeSuccessful;
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 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3480 return writeSuccessful;
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;
3561 HRESULT successRead;
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 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3596 DEF_SMALL_BLOCK_SIZE,
3599 if (FAILED(successRead))
3604 cbTotalRead += cbRead;
3606 successWrite = BlockChainStream_WriteAt(bbTempChain,
3615 cbTotalWritten += cbWritten;
3616 offset.u.LowPart += This->smallBlockSize;
3618 } while (cbRead > 0);
3619 HeapFree(GetProcessHeap(),0,buffer);
3621 assert(cbTotalRead == cbTotalWritten);
3624 * Destroy the small block chain.
3626 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3627 size.u.HighPart = 0;
3629 SmallBlockChainStream_SetSize(*ppsbChain, size);
3630 SmallBlockChainStream_Destroy(*ppsbChain);
3634 * Change the property information. This chain is now a big block chain
3635 * and it doesn't reside in the small blocks chain anymore.
3637 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3639 chainProperty.startingBlock = bbHeadOfChain;
3641 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3644 * Destroy the temporary propertyless big block chain.
3645 * Create a new big block chain associated with this property.
3647 BlockChainStream_Destroy(bbTempChain);
3648 bigBlockChain = BlockChainStream_Construct(This,
3652 return bigBlockChain;
3655 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3657 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3659 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3660 HeapFree(GetProcessHeap(), 0, This);
3663 /******************************************************************************
3665 ** Storage32InternalImpl_Commit
3667 ** The non-root storages cannot be opened in transacted mode thus this function
3670 static HRESULT WINAPI StorageInternalImpl_Commit(
3672 DWORD grfCommitFlags) /* [in] */
3677 /******************************************************************************
3679 ** Storage32InternalImpl_Revert
3681 ** The non-root storages cannot be opened in transacted mode thus this function
3684 static HRESULT WINAPI StorageInternalImpl_Revert(
3690 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3692 IStorage_Release((IStorage*)This->parentStorage);
3693 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3694 HeapFree(GetProcessHeap(), 0, This);
3697 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3698 IEnumSTATSTG* iface,
3702 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3705 * Perform a sanity check on the parameters.
3708 return E_INVALIDARG;
3711 * Initialize the return parameter.
3716 * Compare the riid with the interface IDs implemented by this object.
3718 if (IsEqualGUID(&IID_IUnknown, riid) ||
3719 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3721 *ppvObject = (IEnumSTATSTG*)This;
3722 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3726 return E_NOINTERFACE;
3729 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3730 IEnumSTATSTG* iface)
3732 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3733 return InterlockedIncrement(&This->ref);
3736 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3737 IEnumSTATSTG* iface)
3739 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3743 newRef = InterlockedDecrement(&This->ref);
3746 * If the reference count goes down to 0, perform suicide.
3750 IEnumSTATSTGImpl_Destroy(This);
3756 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3757 IEnumSTATSTG* iface,
3760 ULONG* pceltFetched)
3762 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3764 StgProperty currentProperty;
3765 STATSTG* currentReturnStruct = rgelt;
3766 ULONG objectFetched = 0;
3767 ULONG currentSearchNode;
3770 * Perform a sanity check on the parameters.
3772 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3773 return E_INVALIDARG;
3776 * To avoid the special case, get another pointer to a ULONG value if
3777 * the caller didn't supply one.
3779 if (pceltFetched==0)
3780 pceltFetched = &objectFetched;
3783 * Start the iteration, we will iterate until we hit the end of the
3784 * linked list or until we hit the number of items to iterate through
3789 * Start with the node at the top of the stack.
3791 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3793 while ( ( *pceltFetched < celt) &&
3794 ( currentSearchNode!=PROPERTY_NULL) )
3797 * Remove the top node from the stack
3799 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3802 * Read the property from the storage.
3804 StorageImpl_ReadProperty(This->parentStorage,
3809 * Copy the information to the return buffer.
3811 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3816 * Step to the next item in the iteration
3819 currentReturnStruct++;
3822 * Push the next search node in the search stack.
3824 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3827 * continue the iteration.
3829 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3832 if (*pceltFetched == celt)
3839 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3840 IEnumSTATSTG* iface,
3843 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3845 StgProperty currentProperty;
3846 ULONG objectFetched = 0;
3847 ULONG currentSearchNode;
3850 * Start with the node at the top of the stack.
3852 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3854 while ( (objectFetched < celt) &&
3855 (currentSearchNode!=PROPERTY_NULL) )
3858 * Remove the top node from the stack
3860 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3863 * Read the property from the storage.
3865 StorageImpl_ReadProperty(This->parentStorage,
3870 * Step to the next item in the iteration
3875 * Push the next search node in the search stack.
3877 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3880 * continue the iteration.
3882 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3885 if (objectFetched == celt)
3891 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3892 IEnumSTATSTG* iface)
3894 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3896 StgProperty rootProperty;
3897 BOOL readSuccessful;
3900 * Re-initialize the search stack to an empty stack
3902 This->stackSize = 0;
3905 * Read the root property from the storage.
3907 readSuccessful = StorageImpl_ReadProperty(
3908 This->parentStorage,
3909 This->firstPropertyNode,
3914 assert(rootProperty.sizeOfNameString!=0);
3917 * Push the search node in the search stack.
3919 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3925 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3926 IEnumSTATSTG* iface,
3927 IEnumSTATSTG** ppenum)
3929 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3931 IEnumSTATSTGImpl* newClone;
3934 * Perform a sanity check on the parameters.
3937 return E_INVALIDARG;
3939 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3940 This->firstPropertyNode);
3944 * The new clone enumeration must point to the same current node as
3947 newClone->stackSize = This->stackSize ;
3948 newClone->stackMaxSize = This->stackMaxSize ;
3949 newClone->stackToVisit =
3950 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3953 newClone->stackToVisit,
3955 sizeof(ULONG) * newClone->stackSize);
3957 *ppenum = (IEnumSTATSTG*)newClone;
3960 * Don't forget to nail down a reference to the clone before
3963 IEnumSTATSTGImpl_AddRef(*ppenum);
3968 static INT IEnumSTATSTGImpl_FindParentProperty(
3969 IEnumSTATSTGImpl *This,
3970 ULONG childProperty,
3971 StgProperty *currentProperty,
3974 ULONG currentSearchNode;
3978 * To avoid the special case, get another pointer to a ULONG value if
3979 * the caller didn't supply one.
3982 thisNodeId = &foundNode;
3985 * Start with the node at the top of the stack.
3987 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3990 while (currentSearchNode!=PROPERTY_NULL)
3993 * Store the current node in the returned parameters
3995 *thisNodeId = currentSearchNode;
3998 * Remove the top node from the stack
4000 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4003 * Read the property from the storage.
4005 StorageImpl_ReadProperty(
4006 This->parentStorage,
4010 if (currentProperty->previousProperty == childProperty)
4011 return PROPERTY_RELATION_PREVIOUS;
4013 else if (currentProperty->nextProperty == childProperty)
4014 return PROPERTY_RELATION_NEXT;
4016 else if (currentProperty->dirProperty == childProperty)
4017 return PROPERTY_RELATION_DIR;
4020 * Push the next search node in the search stack.
4022 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4025 * continue the iteration.
4027 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4030 return PROPERTY_NULL;
4033 static ULONG IEnumSTATSTGImpl_FindProperty(
4034 IEnumSTATSTGImpl* This,
4035 const OLECHAR* lpszPropName,
4036 StgProperty* currentProperty)
4038 ULONG currentSearchNode;
4041 * Start with the node at the top of the stack.
4043 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4045 while (currentSearchNode!=PROPERTY_NULL)
4048 * Remove the top node from the stack
4050 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4053 * Read the property from the storage.
4055 StorageImpl_ReadProperty(This->parentStorage,
4059 if ( propertyNameCmp(
4060 (const OLECHAR*)currentProperty->name,
4061 (const OLECHAR*)lpszPropName) == 0)
4062 return currentSearchNode;
4065 * Push the next search node in the search stack.
4067 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4070 * continue the iteration.
4072 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4075 return PROPERTY_NULL;
4078 static void IEnumSTATSTGImpl_PushSearchNode(
4079 IEnumSTATSTGImpl* This,
4082 StgProperty rootProperty;
4083 BOOL readSuccessful;
4086 * First, make sure we're not trying to push an unexisting node.
4088 if (nodeToPush==PROPERTY_NULL)
4092 * First push the node to the stack
4094 if (This->stackSize == This->stackMaxSize)
4096 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4098 This->stackToVisit = HeapReAlloc(
4102 sizeof(ULONG) * This->stackMaxSize);
4105 This->stackToVisit[This->stackSize] = nodeToPush;
4109 * Read the root property from the storage.
4111 readSuccessful = StorageImpl_ReadProperty(
4112 This->parentStorage,
4118 assert(rootProperty.sizeOfNameString!=0);
4121 * Push the previous search node in the search stack.
4123 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4127 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4128 IEnumSTATSTGImpl* This,
4133 if (This->stackSize == 0)
4134 return PROPERTY_NULL;
4136 topNode = This->stackToVisit[This->stackSize-1];
4145 * Virtual function table for the IEnumSTATSTGImpl class.
4147 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4149 IEnumSTATSTGImpl_QueryInterface,
4150 IEnumSTATSTGImpl_AddRef,
4151 IEnumSTATSTGImpl_Release,
4152 IEnumSTATSTGImpl_Next,
4153 IEnumSTATSTGImpl_Skip,
4154 IEnumSTATSTGImpl_Reset,
4155 IEnumSTATSTGImpl_Clone
4158 /******************************************************************************
4159 ** IEnumSTATSTGImpl implementation
4162 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4163 StorageImpl* parentStorage,
4164 ULONG firstPropertyNode)
4166 IEnumSTATSTGImpl* newEnumeration;
4168 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4170 if (newEnumeration!=0)
4173 * Set-up the virtual function table and reference count.
4175 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4176 newEnumeration->ref = 0;
4179 * We want to nail-down the reference to the storage in case the
4180 * enumeration out-lives the storage in the client application.
4182 newEnumeration->parentStorage = parentStorage;
4183 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4185 newEnumeration->firstPropertyNode = firstPropertyNode;
4188 * Initialize the search stack
4190 newEnumeration->stackSize = 0;
4191 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4192 newEnumeration->stackToVisit =
4193 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4196 * Make sure the current node of the iterator is the first one.
4198 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4201 return newEnumeration;
4205 * Virtual function table for the Storage32InternalImpl class.
4207 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4209 StorageBaseImpl_QueryInterface,
4210 StorageBaseImpl_AddRef,
4211 StorageBaseImpl_Release,
4212 StorageBaseImpl_CreateStream,
4213 StorageBaseImpl_OpenStream,
4214 StorageImpl_CreateStorage,
4215 StorageBaseImpl_OpenStorage,
4217 StorageImpl_MoveElementTo,
4218 StorageInternalImpl_Commit,
4219 StorageInternalImpl_Revert,
4220 StorageBaseImpl_EnumElements,
4221 StorageImpl_DestroyElement,
4222 StorageBaseImpl_RenameElement,
4223 StorageImpl_SetElementTimes,
4224 StorageBaseImpl_SetClass,
4225 StorageImpl_SetStateBits,
4226 StorageBaseImpl_Stat
4229 /******************************************************************************
4230 ** Storage32InternalImpl implementation
4233 static StorageInternalImpl* StorageInternalImpl_Construct(
4234 StorageImpl* ancestorStorage,
4236 ULONG rootPropertyIndex)
4238 StorageInternalImpl* newStorage;
4241 * Allocate space for the new storage object
4243 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
4247 memset(newStorage, 0, sizeof(StorageInternalImpl));
4250 * Initialize the stream list
4253 list_init(&newStorage->base.strmHead);
4256 * Initialize the virtual function table.
4258 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4259 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4260 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4263 * Keep the ancestor storage pointer and nail a reference to it.
4265 newStorage->base.ancestorStorage = ancestorStorage;
4266 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4269 * Keep the index of the root property set for this storage,
4271 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4279 /******************************************************************************
4280 ** StorageUtl implementation
4283 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4287 memcpy(&tmp, buffer+offset, sizeof(WORD));
4288 *value = le16toh(tmp);
4291 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4293 value = htole16(value);
4294 memcpy(buffer+offset, &value, sizeof(WORD));
4297 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4301 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4302 *value = le32toh(tmp);
4305 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4307 value = htole32(value);
4308 memcpy(buffer+offset, &value, sizeof(DWORD));
4311 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4312 ULARGE_INTEGER* value)
4314 #ifdef WORDS_BIGENDIAN
4317 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4318 value->u.LowPart = htole32(tmp.u.HighPart);
4319 value->u.HighPart = htole32(tmp.u.LowPart);
4321 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4325 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4326 const ULARGE_INTEGER *value)
4328 #ifdef WORDS_BIGENDIAN
4331 tmp.u.LowPart = htole32(value->u.HighPart);
4332 tmp.u.HighPart = htole32(value->u.LowPart);
4333 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4335 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4339 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4341 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4342 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4343 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4345 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4348 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4350 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4351 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4352 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4354 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4357 void StorageUtl_CopyPropertyToSTATSTG(
4358 STATSTG* destination,
4359 StgProperty* source,
4363 * The copy of the string occurs only when the flag is not set
4365 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4366 (source->name == NULL) ||
4367 (source->name[0] == 0) )
4369 destination->pwcsName = 0;
4373 destination->pwcsName =
4374 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4376 strcpyW((LPWSTR)destination->pwcsName, source->name);
4379 switch (source->propertyType)
4381 case PROPTYPE_STORAGE:
4383 destination->type = STGTY_STORAGE;
4385 case PROPTYPE_STREAM:
4386 destination->type = STGTY_STREAM;
4389 destination->type = STGTY_STREAM;
4393 destination->cbSize = source->size;
4395 currentReturnStruct->mtime = {0}; TODO
4396 currentReturnStruct->ctime = {0};
4397 currentReturnStruct->atime = {0};
4399 destination->grfMode = 0;
4400 destination->grfLocksSupported = 0;
4401 destination->clsid = source->propertyUniqueID;
4402 destination->grfStateBits = 0;
4403 destination->reserved = 0;
4406 /******************************************************************************
4407 ** BlockChainStream implementation
4410 BlockChainStream* BlockChainStream_Construct(
4411 StorageImpl* parentStorage,
4412 ULONG* headOfStreamPlaceHolder,
4413 ULONG propertyIndex)
4415 BlockChainStream* newStream;
4418 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4420 newStream->parentStorage = parentStorage;
4421 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4422 newStream->ownerPropertyIndex = propertyIndex;
4423 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4424 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4425 newStream->numBlocks = 0;
4427 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4429 while (blockIndex != BLOCK_END_OF_CHAIN)
4431 newStream->numBlocks++;
4432 newStream->tailIndex = blockIndex;
4434 if(FAILED(StorageImpl_GetNextBlockInChain(
4439 HeapFree(GetProcessHeap(), 0, newStream);
4447 void BlockChainStream_Destroy(BlockChainStream* This)
4449 HeapFree(GetProcessHeap(), 0, This);
4452 /******************************************************************************
4453 * BlockChainStream_GetHeadOfChain
4455 * Returns the head of this stream chain.
4456 * Some special chains don't have properties, their heads are kept in
4457 * This->headOfStreamPlaceHolder.
4460 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4462 StgProperty chainProperty;
4463 BOOL readSuccessful;
4465 if (This->headOfStreamPlaceHolder != 0)
4466 return *(This->headOfStreamPlaceHolder);
4468 if (This->ownerPropertyIndex != PROPERTY_NULL)
4470 readSuccessful = StorageImpl_ReadProperty(
4471 This->parentStorage,
4472 This->ownerPropertyIndex,
4477 return chainProperty.startingBlock;
4481 return BLOCK_END_OF_CHAIN;
4484 /******************************************************************************
4485 * BlockChainStream_GetCount
4487 * Returns the number of blocks that comprises this chain.
4488 * This is not the size of the stream as the last block may not be full!
4491 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4496 blockIndex = BlockChainStream_GetHeadOfChain(This);
4498 while (blockIndex != BLOCK_END_OF_CHAIN)
4502 if(FAILED(StorageImpl_GetNextBlockInChain(
4503 This->parentStorage,
4512 /******************************************************************************
4513 * BlockChainStream_ReadAt
4515 * Reads a specified number of bytes from this chain at the specified offset.
4516 * bytesRead may be NULL.
4517 * Failure will be returned if the specified number of bytes has not been read.
4519 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4520 ULARGE_INTEGER offset,
4525 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4526 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4527 ULONG bytesToReadInBuffer;
4530 BYTE* bigBlockBuffer;
4533 * Find the first block in the stream that contains part of the buffer.
4535 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4536 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4537 (blockNoInSequence < This->lastBlockNoInSequence) )
4539 blockIndex = BlockChainStream_GetHeadOfChain(This);
4540 This->lastBlockNoInSequence = blockNoInSequence;
4544 ULONG temp = blockNoInSequence;
4546 blockIndex = This->lastBlockNoInSequenceIndex;
4547 blockNoInSequence -= This->lastBlockNoInSequence;
4548 This->lastBlockNoInSequence = temp;
4551 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4553 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4555 blockNoInSequence--;
4558 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4559 return FALSE; /* We failed to find the starting block */
4561 This->lastBlockNoInSequenceIndex = blockIndex;
4564 * Start reading the buffer.
4567 bufferWalker = buffer;
4569 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4572 * Calculate how many bytes we can copy from this big block.
4574 bytesToReadInBuffer =
4575 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4578 * Copy those bytes to the buffer
4581 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4582 if (!bigBlockBuffer)
4585 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4587 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4590 * Step to the next big block.
4592 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4595 bufferWalker += bytesToReadInBuffer;
4596 size -= bytesToReadInBuffer;
4597 *bytesRead += bytesToReadInBuffer;
4598 offsetInBlock = 0; /* There is no offset on the next block */
4605 /******************************************************************************
4606 * BlockChainStream_WriteAt
4608 * Writes the specified number of bytes to this chain at the specified offset.
4609 * bytesWritten may be NULL.
4610 * Will fail if not all specified number of bytes have been written.
4612 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4613 ULARGE_INTEGER offset,
4616 ULONG* bytesWritten)
4618 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4619 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4622 const BYTE* bufferWalker;
4623 BYTE* bigBlockBuffer;
4626 * Find the first block in the stream that contains part of the buffer.
4628 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4629 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4630 (blockNoInSequence < This->lastBlockNoInSequence) )
4632 blockIndex = BlockChainStream_GetHeadOfChain(This);
4633 This->lastBlockNoInSequence = blockNoInSequence;
4637 ULONG temp = blockNoInSequence;
4639 blockIndex = This->lastBlockNoInSequenceIndex;
4640 blockNoInSequence -= This->lastBlockNoInSequence;
4641 This->lastBlockNoInSequence = temp;
4644 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4646 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4649 blockNoInSequence--;
4652 This->lastBlockNoInSequenceIndex = blockIndex;
4655 * Here, I'm casting away the constness on the buffer variable
4656 * This is OK since we don't intend to modify that buffer.
4659 bufferWalker = (const BYTE*)buffer;
4661 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4664 * Calculate how many bytes we can copy from this big block.
4667 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4670 * Copy those bytes to the buffer
4672 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4674 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4676 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4679 * Step to the next big block.
4681 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4684 bufferWalker += bytesToWrite;
4685 size -= bytesToWrite;
4686 *bytesWritten += bytesToWrite;
4687 offsetInBlock = 0; /* There is no offset on the next block */
4693 /******************************************************************************
4694 * BlockChainStream_Shrink
4696 * Shrinks this chain in the big block depot.
4698 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4699 ULARGE_INTEGER newSize)
4701 ULONG blockIndex, extraBlock;
4706 * Reset the last accessed block cache.
4708 This->lastBlockNoInSequence = 0xFFFFFFFF;
4709 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4712 * Figure out how many blocks are needed to contain the new size
4714 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4716 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4719 blockIndex = BlockChainStream_GetHeadOfChain(This);
4722 * Go to the new end of chain
4724 while (count < numBlocks)
4726 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4732 /* Get the next block before marking the new end */
4733 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4737 /* Mark the new end of chain */
4738 StorageImpl_SetNextBlockInChain(
4739 This->parentStorage,
4741 BLOCK_END_OF_CHAIN);
4743 This->tailIndex = blockIndex;
4744 This->numBlocks = numBlocks;
4747 * Mark the extra blocks as free
4749 while (extraBlock != BLOCK_END_OF_CHAIN)
4751 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4754 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4755 extraBlock = blockIndex;
4761 /******************************************************************************
4762 * BlockChainStream_Enlarge
4764 * Grows this chain in the big block depot.
4766 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4767 ULARGE_INTEGER newSize)
4769 ULONG blockIndex, currentBlock;
4771 ULONG oldNumBlocks = 0;
4773 blockIndex = BlockChainStream_GetHeadOfChain(This);
4776 * Empty chain. Create the head.
4778 if (blockIndex == BLOCK_END_OF_CHAIN)
4780 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4781 StorageImpl_SetNextBlockInChain(This->parentStorage,
4783 BLOCK_END_OF_CHAIN);
4785 if (This->headOfStreamPlaceHolder != 0)
4787 *(This->headOfStreamPlaceHolder) = blockIndex;
4791 StgProperty chainProp;
4792 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4794 StorageImpl_ReadProperty(
4795 This->parentStorage,
4796 This->ownerPropertyIndex,
4799 chainProp.startingBlock = blockIndex;
4801 StorageImpl_WriteProperty(
4802 This->parentStorage,
4803 This->ownerPropertyIndex,
4807 This->tailIndex = blockIndex;
4808 This->numBlocks = 1;
4812 * Figure out how many blocks are needed to contain this stream
4814 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4816 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4820 * Go to the current end of chain
4822 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4824 currentBlock = blockIndex;
4826 while (blockIndex != BLOCK_END_OF_CHAIN)
4829 currentBlock = blockIndex;
4831 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4836 This->tailIndex = currentBlock;
4839 currentBlock = This->tailIndex;
4840 oldNumBlocks = This->numBlocks;
4843 * Add new blocks to the chain
4845 if (oldNumBlocks < newNumBlocks)
4847 while (oldNumBlocks < newNumBlocks)
4849 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4851 StorageImpl_SetNextBlockInChain(
4852 This->parentStorage,
4856 StorageImpl_SetNextBlockInChain(
4857 This->parentStorage,
4859 BLOCK_END_OF_CHAIN);
4861 currentBlock = blockIndex;
4865 This->tailIndex = blockIndex;
4866 This->numBlocks = newNumBlocks;
4872 /******************************************************************************
4873 * BlockChainStream_SetSize
4875 * Sets the size of this stream. The big block depot will be updated.
4876 * The file will grow if we grow the chain.
4878 * TODO: Free the actual blocks in the file when we shrink the chain.
4879 * Currently, the blocks are still in the file. So the file size
4880 * doesn't shrink even if we shrink streams.
4882 BOOL BlockChainStream_SetSize(
4883 BlockChainStream* This,
4884 ULARGE_INTEGER newSize)
4886 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4888 if (newSize.u.LowPart == size.u.LowPart)
4891 if (newSize.u.LowPart < size.u.LowPart)
4893 BlockChainStream_Shrink(This, newSize);
4897 BlockChainStream_Enlarge(This, newSize);
4903 /******************************************************************************
4904 * BlockChainStream_GetSize
4906 * Returns the size of this chain.
4907 * Will return the block count if this chain doesn't have a property.
4909 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4911 StgProperty chainProperty;
4913 if(This->headOfStreamPlaceHolder == NULL)
4916 * This chain is a data stream read the property and return
4917 * the appropriate size
4919 StorageImpl_ReadProperty(
4920 This->parentStorage,
4921 This->ownerPropertyIndex,
4924 return chainProperty.size;
4929 * this chain is a chain that does not have a property, figure out the
4930 * size by making the product number of used blocks times the
4933 ULARGE_INTEGER result;
4934 result.u.HighPart = 0;
4937 BlockChainStream_GetCount(This) *
4938 This->parentStorage->bigBlockSize;
4944 /******************************************************************************
4945 ** SmallBlockChainStream implementation
4948 SmallBlockChainStream* SmallBlockChainStream_Construct(
4949 StorageImpl* parentStorage,
4950 ULONG propertyIndex)
4952 SmallBlockChainStream* newStream;
4954 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4956 newStream->parentStorage = parentStorage;
4957 newStream->ownerPropertyIndex = propertyIndex;
4962 void SmallBlockChainStream_Destroy(
4963 SmallBlockChainStream* This)
4965 HeapFree(GetProcessHeap(), 0, This);
4968 /******************************************************************************
4969 * SmallBlockChainStream_GetHeadOfChain
4971 * Returns the head of this chain of small blocks.
4973 static ULONG SmallBlockChainStream_GetHeadOfChain(
4974 SmallBlockChainStream* This)
4976 StgProperty chainProperty;
4977 BOOL readSuccessful;
4979 if (This->ownerPropertyIndex)
4981 readSuccessful = StorageImpl_ReadProperty(
4982 This->parentStorage,
4983 This->ownerPropertyIndex,
4988 return chainProperty.startingBlock;
4993 return BLOCK_END_OF_CHAIN;
4996 /******************************************************************************
4997 * SmallBlockChainStream_GetNextBlockInChain
4999 * Returns the index of the next small block in this chain.
5002 * - BLOCK_END_OF_CHAIN: end of this chain
5003 * - BLOCK_UNUSED: small block 'blockIndex' is free
5005 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5006 SmallBlockChainStream* This,
5008 ULONG* nextBlockInChain)
5010 ULARGE_INTEGER offsetOfBlockInDepot;
5015 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5017 offsetOfBlockInDepot.u.HighPart = 0;
5018 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5021 * Read those bytes in the buffer from the small block file.
5023 success = BlockChainStream_ReadAt(
5024 This->parentStorage->smallBlockDepotChain,
5025 offsetOfBlockInDepot,
5032 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5036 return STG_E_READFAULT;
5039 /******************************************************************************
5040 * SmallBlockChainStream_SetNextBlockInChain
5042 * Writes the index of the next block of the specified block in the small
5044 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5045 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5047 static void SmallBlockChainStream_SetNextBlockInChain(
5048 SmallBlockChainStream* This,
5052 ULARGE_INTEGER offsetOfBlockInDepot;
5056 offsetOfBlockInDepot.u.HighPart = 0;
5057 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5059 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5062 * Read those bytes in the buffer from the small block file.
5064 BlockChainStream_WriteAt(
5065 This->parentStorage->smallBlockDepotChain,
5066 offsetOfBlockInDepot,
5072 /******************************************************************************
5073 * SmallBlockChainStream_FreeBlock
5075 * Flag small block 'blockIndex' as free in the small block depot.
5077 static void SmallBlockChainStream_FreeBlock(
5078 SmallBlockChainStream* This,
5081 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5084 /******************************************************************************
5085 * SmallBlockChainStream_GetNextFreeBlock
5087 * Returns the index of a free small block. The small block depot will be
5088 * enlarged if necessary. The small block chain will also be enlarged if
5091 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5092 SmallBlockChainStream* This)
5094 ULARGE_INTEGER offsetOfBlockInDepot;
5097 ULONG blockIndex = 0;
5098 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5099 BOOL success = TRUE;
5100 ULONG smallBlocksPerBigBlock;
5102 offsetOfBlockInDepot.u.HighPart = 0;
5105 * Scan the small block depot for a free block
5107 while (nextBlockIndex != BLOCK_UNUSED)
5109 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5111 success = BlockChainStream_ReadAt(
5112 This->parentStorage->smallBlockDepotChain,
5113 offsetOfBlockInDepot,
5119 * If we run out of space for the small block depot, enlarge it
5123 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5125 if (nextBlockIndex != BLOCK_UNUSED)
5131 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5133 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5134 ULONG nextBlock, newsbdIndex;
5135 BYTE* smallBlockDepot;
5137 nextBlock = sbdIndex;
5138 while (nextBlock != BLOCK_END_OF_CHAIN)
5140 sbdIndex = nextBlock;
5141 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5144 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5145 if (sbdIndex != BLOCK_END_OF_CHAIN)
5146 StorageImpl_SetNextBlockInChain(
5147 This->parentStorage,
5151 StorageImpl_SetNextBlockInChain(
5152 This->parentStorage,
5154 BLOCK_END_OF_CHAIN);
5157 * Initialize all the small blocks to free
5160 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
5162 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5163 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
5168 * We have just created the small block depot.
5170 StgProperty rootProp;
5174 * Save it in the header
5176 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5177 StorageImpl_SaveFileHeader(This->parentStorage);
5180 * And allocate the first big block that will contain small blocks
5183 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5185 StorageImpl_SetNextBlockInChain(
5186 This->parentStorage,
5188 BLOCK_END_OF_CHAIN);
5190 StorageImpl_ReadProperty(
5191 This->parentStorage,
5192 This->parentStorage->base.rootPropertySetIndex,
5195 rootProp.startingBlock = sbStartIndex;
5196 rootProp.size.u.HighPart = 0;
5197 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5199 StorageImpl_WriteProperty(
5200 This->parentStorage,
5201 This->parentStorage->base.rootPropertySetIndex,
5207 smallBlocksPerBigBlock =
5208 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5211 * Verify if we have to allocate big blocks to contain small blocks
5213 if (blockIndex % smallBlocksPerBigBlock == 0)
5215 StgProperty rootProp;
5216 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5218 StorageImpl_ReadProperty(
5219 This->parentStorage,
5220 This->parentStorage->base.rootPropertySetIndex,
5223 if (rootProp.size.u.LowPart <
5224 (blocksRequired * This->parentStorage->bigBlockSize))
5226 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5228 BlockChainStream_SetSize(
5229 This->parentStorage->smallBlockRootChain,
5232 StorageImpl_WriteProperty(
5233 This->parentStorage,
5234 This->parentStorage->base.rootPropertySetIndex,
5242 /******************************************************************************
5243 * SmallBlockChainStream_ReadAt
5245 * Reads a specified number of bytes from this chain at the specified offset.
5246 * bytesRead may be NULL.
5247 * Failure will be returned if the specified number of bytes has not been read.
5249 HRESULT SmallBlockChainStream_ReadAt(
5250 SmallBlockChainStream* This,
5251 ULARGE_INTEGER offset,
5257 ULARGE_INTEGER offsetInBigBlockFile;
5258 ULONG blockNoInSequence =
5259 offset.u.LowPart / This->parentStorage->smallBlockSize;
5261 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5262 ULONG bytesToReadInBuffer;
5264 ULONG bytesReadFromBigBlockFile;
5268 * This should never happen on a small block file.
5270 assert(offset.u.HighPart==0);
5273 * Find the first block in the stream that contains part of the buffer.
5275 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5277 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5279 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5282 blockNoInSequence--;
5286 * Start reading the buffer.
5289 bufferWalker = buffer;
5291 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5294 * Calculate how many bytes we can copy from this small block.
5296 bytesToReadInBuffer =
5297 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5300 * Calculate the offset of the small block in the small block file.
5302 offsetInBigBlockFile.u.HighPart = 0;
5303 offsetInBigBlockFile.u.LowPart =
5304 blockIndex * This->parentStorage->smallBlockSize;
5306 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5309 * Read those bytes in the buffer from the small block file.
5310 * The small block has already been identified so it shouldn't fail
5311 * unless the file is corrupt.
5313 if (!BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5314 offsetInBigBlockFile,
5315 bytesToReadInBuffer,
5317 &bytesReadFromBigBlockFile))
5318 return STG_E_DOCFILECORRUPT;
5320 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5323 * Step to the next big block.
5325 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5329 bufferWalker += bytesToReadInBuffer;
5330 size -= bytesToReadInBuffer;
5331 *bytesRead += bytesToReadInBuffer;
5332 offsetInBlock = 0; /* There is no offset on the next block */
5338 /******************************************************************************
5339 * SmallBlockChainStream_WriteAt
5341 * Writes the specified number of bytes to this chain at the specified offset.
5342 * bytesWritten may be NULL.
5343 * Will fail if not all specified number of bytes have been written.
5345 BOOL SmallBlockChainStream_WriteAt(
5346 SmallBlockChainStream* This,
5347 ULARGE_INTEGER offset,
5350 ULONG* bytesWritten)
5352 ULARGE_INTEGER offsetInBigBlockFile;
5353 ULONG blockNoInSequence =
5354 offset.u.LowPart / This->parentStorage->smallBlockSize;
5356 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5357 ULONG bytesToWriteInBuffer;
5359 ULONG bytesWrittenFromBigBlockFile;
5360 const BYTE* bufferWalker;
5363 * This should never happen on a small block file.
5365 assert(offset.u.HighPart==0);
5368 * Find the first block in the stream that contains part of the buffer.
5370 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5372 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5374 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5376 blockNoInSequence--;
5380 * Start writing the buffer.
5382 * Here, I'm casting away the constness on the buffer variable
5383 * This is OK since we don't intend to modify that buffer.
5386 bufferWalker = (const BYTE*)buffer;
5387 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5390 * Calculate how many bytes we can copy to this small block.
5392 bytesToWriteInBuffer =
5393 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5396 * Calculate the offset of the small block in the small block file.
5398 offsetInBigBlockFile.u.HighPart = 0;
5399 offsetInBigBlockFile.u.LowPart =
5400 blockIndex * This->parentStorage->smallBlockSize;
5402 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5405 * Write those bytes in the buffer to the small block file.
5407 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5408 offsetInBigBlockFile,
5409 bytesToWriteInBuffer,
5411 &bytesWrittenFromBigBlockFile);
5413 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5416 * Step to the next big block.
5418 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5421 bufferWalker += bytesToWriteInBuffer;
5422 size -= bytesToWriteInBuffer;
5423 *bytesWritten += bytesToWriteInBuffer;
5424 offsetInBlock = 0; /* There is no offset on the next block */
5430 /******************************************************************************
5431 * SmallBlockChainStream_Shrink
5433 * Shrinks this chain in the small block depot.
5435 static BOOL SmallBlockChainStream_Shrink(
5436 SmallBlockChainStream* This,
5437 ULARGE_INTEGER newSize)
5439 ULONG blockIndex, extraBlock;
5443 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5445 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5448 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5451 * Go to the new end of chain
5453 while (count < numBlocks)
5455 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5462 * If the count is 0, we have a special case, the head of the chain was
5467 StgProperty chainProp;
5469 StorageImpl_ReadProperty(This->parentStorage,
5470 This->ownerPropertyIndex,
5473 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5475 StorageImpl_WriteProperty(This->parentStorage,
5476 This->ownerPropertyIndex,
5480 * We start freeing the chain at the head block.
5482 extraBlock = blockIndex;
5486 /* Get the next block before marking the new end */
5487 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5491 /* Mark the new end of chain */
5492 SmallBlockChainStream_SetNextBlockInChain(
5495 BLOCK_END_OF_CHAIN);
5499 * Mark the extra blocks as free
5501 while (extraBlock != BLOCK_END_OF_CHAIN)
5503 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5506 SmallBlockChainStream_FreeBlock(This, extraBlock);
5507 extraBlock = blockIndex;
5513 /******************************************************************************
5514 * SmallBlockChainStream_Enlarge
5516 * Grows this chain in the small block depot.
5518 static BOOL SmallBlockChainStream_Enlarge(
5519 SmallBlockChainStream* This,
5520 ULARGE_INTEGER newSize)
5522 ULONG blockIndex, currentBlock;
5524 ULONG oldNumBlocks = 0;
5526 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5531 if (blockIndex == BLOCK_END_OF_CHAIN)
5534 StgProperty chainProp;
5536 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5539 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5541 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5544 blockIndex = chainProp.startingBlock;
5545 SmallBlockChainStream_SetNextBlockInChain(
5548 BLOCK_END_OF_CHAIN);
5551 currentBlock = blockIndex;
5554 * Figure out how many blocks are needed to contain this stream
5556 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5558 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5562 * Go to the current end of chain
5564 while (blockIndex != BLOCK_END_OF_CHAIN)
5567 currentBlock = blockIndex;
5568 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5573 * Add new blocks to the chain
5575 while (oldNumBlocks < newNumBlocks)
5577 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5578 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5580 SmallBlockChainStream_SetNextBlockInChain(
5583 BLOCK_END_OF_CHAIN);
5585 currentBlock = blockIndex;
5592 /******************************************************************************
5593 * SmallBlockChainStream_SetSize
5595 * Sets the size of this stream.
5596 * The file will grow if we grow the chain.
5598 * TODO: Free the actual blocks in the file when we shrink the chain.
5599 * Currently, the blocks are still in the file. So the file size
5600 * doesn't shrink even if we shrink streams.
5602 BOOL SmallBlockChainStream_SetSize(
5603 SmallBlockChainStream* This,
5604 ULARGE_INTEGER newSize)
5606 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5608 if (newSize.u.LowPart == size.u.LowPart)
5611 if (newSize.u.LowPart < size.u.LowPart)
5613 SmallBlockChainStream_Shrink(This, newSize);
5617 SmallBlockChainStream_Enlarge(This, newSize);
5623 /******************************************************************************
5624 * SmallBlockChainStream_GetSize
5626 * Returns the size of this chain.
5628 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5630 StgProperty chainProperty;
5632 StorageImpl_ReadProperty(
5633 This->parentStorage,
5634 This->ownerPropertyIndex,
5637 return chainProperty.size;
5640 /******************************************************************************
5641 * StgCreateDocfile [OLE32.@]
5642 * Creates a new compound file storage object
5645 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5646 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5647 * reserved [ ?] unused?, usually 0
5648 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5651 * S_OK if the file was successfully created
5652 * some STG_E_ value if error
5654 * if pwcsName is NULL, create file with new unique name
5655 * the function can returns
5656 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5659 HRESULT WINAPI StgCreateDocfile(
5663 IStorage **ppstgOpen)
5665 StorageImpl* newStorage = 0;
5666 HANDLE hFile = INVALID_HANDLE_VALUE;
5667 HRESULT hr = STG_E_INVALIDFLAG;
5671 DWORD fileAttributes;
5672 WCHAR tempFileName[MAX_PATH];
5674 TRACE("(%s, %lx, %ld, %p)\n",
5675 debugstr_w(pwcsName), grfMode,
5676 reserved, ppstgOpen);
5679 * Validate the parameters
5682 return STG_E_INVALIDPOINTER;
5684 return STG_E_INVALIDPARAMETER;
5687 * Validate the STGM flags
5689 if ( FAILED( validateSTGM(grfMode) ))
5692 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5693 switch(STGM_ACCESS_MODE(grfMode))
5696 case STGM_READWRITE:
5702 /* if no share mode given then DENY_NONE is the default */
5703 if (STGM_SHARE_MODE(grfMode) == 0)
5704 grfMode |= STGM_SHARE_DENY_NONE;
5706 /* must have at least one access mode */
5707 if (STGM_ACCESS_MODE(grfMode) == 0)
5710 /* in direct mode, can only use SHARE_EXCLUSIVE */
5711 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5714 /* but in transacted mode, any share mode is valid */
5717 * Generate a unique name.
5721 WCHAR tempPath[MAX_PATH];
5722 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5724 memset(tempPath, 0, sizeof(tempPath));
5725 memset(tempFileName, 0, sizeof(tempFileName));
5727 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5730 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5731 pwcsName = tempFileName;
5734 hr = STG_E_INSUFFICIENTMEMORY;
5738 creationMode = TRUNCATE_EXISTING;
5742 creationMode = GetCreationModeFromSTGM(grfMode);
5746 * Interpret the STGM value grfMode
5748 shareMode = GetShareModeFromSTGM(grfMode);
5749 accessMode = GetAccessModeFromSTGM(grfMode);
5751 if (grfMode & STGM_DELETEONRELEASE)
5752 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5754 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5756 if (grfMode & STGM_TRANSACTED)
5757 FIXME("Transacted mode not implemented.\n");
5760 * Initialize the "out" parameter.
5764 hFile = CreateFileW(pwcsName,
5772 if (hFile == INVALID_HANDLE_VALUE)
5774 if(GetLastError() == ERROR_FILE_EXISTS)
5775 hr = STG_E_FILEALREADYEXISTS;
5782 * Allocate and initialize the new IStorage32object.
5784 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5786 if (newStorage == 0)
5788 hr = STG_E_INSUFFICIENTMEMORY;
5792 hr = StorageImpl_Construct(
5803 HeapFree(GetProcessHeap(), 0, newStorage);
5808 * Get an "out" pointer for the caller.
5810 hr = StorageBaseImpl_QueryInterface(
5811 (IStorage*)newStorage,
5812 (REFIID)&IID_IStorage,
5815 TRACE("<-- %p r = %08lx\n", *ppstgOpen, hr);
5820 /******************************************************************************
5821 * StgCreateStorageEx [OLE32.@]
5823 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5825 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5826 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5828 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5830 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5831 return STG_E_INVALIDPARAMETER;
5834 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5836 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5837 return STG_E_INVALIDPARAMETER;
5840 if (stgfmt == STGFMT_FILE)
5842 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5843 return STG_E_INVALIDPARAMETER;
5846 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5848 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5849 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5852 ERR("Invalid stgfmt argument\n");
5853 return STG_E_INVALIDPARAMETER;
5856 /******************************************************************************
5857 * StgCreatePropSetStg [OLE32.@]
5859 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5860 IPropertySetStorage **ppPropSetStg)
5864 TRACE("(%p, 0x%lx, %p)\n", pstg, reserved, ppPropSetStg);
5866 hr = STG_E_INVALIDPARAMETER;
5868 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5869 (void**)ppPropSetStg);
5873 /******************************************************************************
5874 * StgOpenStorageEx [OLE32.@]
5876 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5878 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5879 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5881 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5883 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5884 return STG_E_INVALIDPARAMETER;
5890 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5891 return STG_E_INVALIDPARAMETER;
5893 case STGFMT_STORAGE:
5896 case STGFMT_DOCFILE:
5897 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5899 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5900 return STG_E_INVALIDPARAMETER;
5902 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5906 WARN("STGFMT_ANY assuming storage\n");
5910 return STG_E_INVALIDPARAMETER;
5913 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5917 /******************************************************************************
5918 * StgOpenStorage [OLE32.@]
5920 HRESULT WINAPI StgOpenStorage(
5921 const OLECHAR *pwcsName,
5922 IStorage *pstgPriority,
5926 IStorage **ppstgOpen)
5928 StorageImpl* newStorage = 0;
5933 WCHAR fullname[MAX_PATH];
5936 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5937 debugstr_w(pwcsName), pstgPriority, grfMode,
5938 snbExclude, reserved, ppstgOpen);
5941 * Perform sanity checks
5945 hr = STG_E_INVALIDNAME;
5951 hr = STG_E_INVALIDPOINTER;
5957 hr = STG_E_INVALIDPARAMETER;
5961 if (grfMode & STGM_PRIORITY)
5963 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5964 return STG_E_INVALIDFLAG;
5965 if (grfMode & STGM_DELETEONRELEASE)
5966 return STG_E_INVALIDFUNCTION;
5967 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5968 return STG_E_INVALIDFLAG;
5969 grfMode &= ~0xf0; /* remove the existing sharing mode */
5970 grfMode |= STGM_SHARE_DENY_NONE;
5972 /* STGM_PRIORITY stops other IStorage objects on the same file from
5973 * committing until the STGM_PRIORITY IStorage is closed. it also
5974 * stops non-transacted mode StgOpenStorage calls with write access from
5975 * succeeding. obviously, both of these cannot be achieved through just
5976 * file share flags */
5977 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5981 * Validate the sharing mode
5983 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5984 switch(STGM_SHARE_MODE(grfMode))
5986 case STGM_SHARE_EXCLUSIVE:
5987 case STGM_SHARE_DENY_WRITE:
5990 hr = STG_E_INVALIDFLAG;
5995 * Validate the STGM flags
5997 if ( FAILED( validateSTGM(grfMode) ) ||
5998 (grfMode&STGM_CREATE))
6000 hr = STG_E_INVALIDFLAG;
6004 /* shared reading requires transacted mode */
6005 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6006 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6007 !(grfMode&STGM_TRANSACTED) )
6009 hr = STG_E_INVALIDFLAG;
6014 * Interpret the STGM value grfMode
6016 shareMode = GetShareModeFromSTGM(grfMode);
6017 accessMode = GetAccessModeFromSTGM(grfMode);
6020 * Initialize the "out" parameter.
6024 hFile = CreateFileW( pwcsName,
6029 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6032 if (hFile==INVALID_HANDLE_VALUE)
6034 DWORD last_error = GetLastError();
6040 case ERROR_FILE_NOT_FOUND:
6041 hr = STG_E_FILENOTFOUND;
6044 case ERROR_PATH_NOT_FOUND:
6045 hr = STG_E_PATHNOTFOUND;
6048 case ERROR_ACCESS_DENIED:
6049 case ERROR_WRITE_PROTECT:
6050 hr = STG_E_ACCESSDENIED;
6053 case ERROR_SHARING_VIOLATION:
6054 hr = STG_E_SHAREVIOLATION;
6065 * Refuse to open the file if it's too small to be a structured storage file
6066 * FIXME: verify the file when reading instead of here
6068 length = GetFileSize(hFile, NULL);
6072 hr = STG_E_FILEALREADYEXISTS;
6077 * Allocate and initialize the new IStorage32object.
6079 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6081 if (newStorage == 0)
6083 hr = STG_E_INSUFFICIENTMEMORY;
6087 /* if the file's length was zero, initialize the storage */
6088 hr = StorageImpl_Construct(
6099 HeapFree(GetProcessHeap(), 0, newStorage);
6101 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6103 if(hr == STG_E_INVALIDHEADER)
6104 hr = STG_E_FILEALREADYEXISTS;
6108 /* prepare the file name string given in lieu of the root property name */
6109 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6110 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6111 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6114 * Get an "out" pointer for the caller.
6116 hr = StorageBaseImpl_QueryInterface(
6117 (IStorage*)newStorage,
6118 (REFIID)&IID_IStorage,
6122 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6126 /******************************************************************************
6127 * StgCreateDocfileOnILockBytes [OLE32.@]
6129 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6133 IStorage** ppstgOpen)
6135 StorageImpl* newStorage = 0;
6139 * Validate the parameters
6141 if ((ppstgOpen == 0) || (plkbyt == 0))
6142 return STG_E_INVALIDPOINTER;
6145 * Allocate and initialize the new IStorage object.
6147 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6149 if (newStorage == 0)
6150 return STG_E_INSUFFICIENTMEMORY;
6152 hr = StorageImpl_Construct(
6163 HeapFree(GetProcessHeap(), 0, newStorage);
6168 * Get an "out" pointer for the caller.
6170 hr = StorageBaseImpl_QueryInterface(
6171 (IStorage*)newStorage,
6172 (REFIID)&IID_IStorage,
6178 /******************************************************************************
6179 * StgOpenStorageOnILockBytes [OLE32.@]
6181 HRESULT WINAPI StgOpenStorageOnILockBytes(
6183 IStorage *pstgPriority,
6187 IStorage **ppstgOpen)
6189 StorageImpl* newStorage = 0;
6193 * Perform a sanity check
6195 if ((plkbyt == 0) || (ppstgOpen == 0))
6196 return STG_E_INVALIDPOINTER;
6199 * Validate the STGM flags
6201 if ( FAILED( validateSTGM(grfMode) ))
6202 return STG_E_INVALIDFLAG;
6205 * Initialize the "out" parameter.
6210 * Allocate and initialize the new IStorage object.
6212 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6214 if (newStorage == 0)
6215 return STG_E_INSUFFICIENTMEMORY;
6217 hr = StorageImpl_Construct(
6228 HeapFree(GetProcessHeap(), 0, newStorage);
6233 * Get an "out" pointer for the caller.
6235 hr = StorageBaseImpl_QueryInterface(
6236 (IStorage*)newStorage,
6237 (REFIID)&IID_IStorage,
6243 /******************************************************************************
6244 * StgSetTimes [ole32.@]
6245 * StgSetTimes [OLE32.@]
6249 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6250 FILETIME const *patime, FILETIME const *pmtime)
6252 IStorage *stg = NULL;
6255 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6257 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6261 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6262 IStorage_Release(stg);
6268 /******************************************************************************
6269 * StgIsStorageILockBytes [OLE32.@]
6271 * Determines if the ILockBytes contains a storage object.
6273 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6276 ULARGE_INTEGER offset;
6278 offset.u.HighPart = 0;
6279 offset.u.LowPart = 0;
6281 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6283 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6289 /******************************************************************************
6290 * WriteClassStg [OLE32.@]
6292 * This method will store the specified CLSID in the specified storage object
6294 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6299 return E_INVALIDARG;
6301 hRes = IStorage_SetClass(pStg, rclsid);
6306 /***********************************************************************
6307 * ReadClassStg (OLE32.@)
6309 * This method reads the CLSID previously written to a storage object with
6310 * the WriteClassStg.
6313 * pstg [I] IStorage pointer
6314 * pclsid [O] Pointer to where the CLSID is written
6318 * Failure: HRESULT code.
6320 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6325 TRACE("(%p, %p)\n", pstg, pclsid);
6327 if(!pstg || !pclsid)
6328 return E_INVALIDARG;
6331 * read a STATSTG structure (contains the clsid) from the storage
6333 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6336 *pclsid=pstatstg.clsid;
6341 /***********************************************************************
6342 * OleLoadFromStream (OLE32.@)
6344 * This function loads an object from stream
6346 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6350 LPPERSISTSTREAM xstm;
6352 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6354 res=ReadClassStm(pStm,&clsid);
6355 if (!SUCCEEDED(res))
6357 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6358 if (!SUCCEEDED(res))
6360 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6361 if (!SUCCEEDED(res)) {
6362 IUnknown_Release((IUnknown*)*ppvObj);
6365 res=IPersistStream_Load(xstm,pStm);
6366 IPersistStream_Release(xstm);
6367 /* FIXME: all refcounts ok at this point? I think they should be:
6370 * xstm : 0 (released)
6375 /***********************************************************************
6376 * OleSaveToStream (OLE32.@)
6378 * This function saves an object with the IPersistStream interface on it
6379 * to the specified stream.
6381 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6387 TRACE("(%p,%p)\n",pPStm,pStm);
6389 res=IPersistStream_GetClassID(pPStm,&clsid);
6391 if (SUCCEEDED(res)){
6393 res=WriteClassStm(pStm,&clsid);
6397 res=IPersistStream_Save(pPStm,pStm,TRUE);
6400 TRACE("Finished Save\n");
6404 /****************************************************************************
6405 * This method validate a STGM parameter that can contain the values below
6407 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6408 * The stgm values contained in 0xffff0000 are bitmasks.
6410 * STGM_DIRECT 0x00000000
6411 * STGM_TRANSACTED 0x00010000
6412 * STGM_SIMPLE 0x08000000
6414 * STGM_READ 0x00000000
6415 * STGM_WRITE 0x00000001
6416 * STGM_READWRITE 0x00000002
6418 * STGM_SHARE_DENY_NONE 0x00000040
6419 * STGM_SHARE_DENY_READ 0x00000030
6420 * STGM_SHARE_DENY_WRITE 0x00000020
6421 * STGM_SHARE_EXCLUSIVE 0x00000010
6423 * STGM_PRIORITY 0x00040000
6424 * STGM_DELETEONRELEASE 0x04000000
6426 * STGM_CREATE 0x00001000
6427 * STGM_CONVERT 0x00020000
6428 * STGM_FAILIFTHERE 0x00000000
6430 * STGM_NOSCRATCH 0x00100000
6431 * STGM_NOSNAPSHOT 0x00200000
6433 static HRESULT validateSTGM(DWORD stgm)
6435 DWORD access = STGM_ACCESS_MODE(stgm);
6436 DWORD share = STGM_SHARE_MODE(stgm);
6437 DWORD create = STGM_CREATE_MODE(stgm);
6439 if (stgm&~STGM_KNOWN_FLAGS)
6441 ERR("unknown flags %08lx\n", stgm);
6449 case STGM_READWRITE:
6457 case STGM_SHARE_DENY_NONE:
6458 case STGM_SHARE_DENY_READ:
6459 case STGM_SHARE_DENY_WRITE:
6460 case STGM_SHARE_EXCLUSIVE:
6469 case STGM_FAILIFTHERE:
6476 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6478 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6482 * STGM_CREATE | STGM_CONVERT
6483 * if both are false, STGM_FAILIFTHERE is set to TRUE
6485 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6489 * STGM_NOSCRATCH requires STGM_TRANSACTED
6491 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6495 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6496 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6498 if ( (stgm & STGM_NOSNAPSHOT) &&
6499 (!(stgm & STGM_TRANSACTED) ||
6500 share == STGM_SHARE_EXCLUSIVE ||
6501 share == STGM_SHARE_DENY_WRITE) )
6507 /****************************************************************************
6508 * GetShareModeFromSTGM
6510 * This method will return a share mode flag from a STGM value.
6511 * The STGM value is assumed valid.
6513 static DWORD GetShareModeFromSTGM(DWORD stgm)
6515 switch (STGM_SHARE_MODE(stgm))
6517 case STGM_SHARE_DENY_NONE:
6518 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6519 case STGM_SHARE_DENY_READ:
6520 return FILE_SHARE_WRITE;
6521 case STGM_SHARE_DENY_WRITE:
6522 return FILE_SHARE_READ;
6523 case STGM_SHARE_EXCLUSIVE:
6526 ERR("Invalid share mode!\n");
6531 /****************************************************************************
6532 * GetAccessModeFromSTGM
6534 * This method will return an access mode flag from a STGM value.
6535 * The STGM value is assumed valid.
6537 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6539 switch (STGM_ACCESS_MODE(stgm))
6542 return GENERIC_READ;
6544 case STGM_READWRITE:
6545 return GENERIC_READ | GENERIC_WRITE;
6547 ERR("Invalid access mode!\n");
6552 /****************************************************************************
6553 * GetCreationModeFromSTGM
6555 * This method will return a creation mode flag from a STGM value.
6556 * The STGM value is assumed valid.
6558 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6560 switch(STGM_CREATE_MODE(stgm))
6563 return CREATE_ALWAYS;
6565 FIXME("STGM_CONVERT not implemented!\n");
6567 case STGM_FAILIFTHERE:
6570 ERR("Invalid create mode!\n");
6576 /*************************************************************************
6577 * OLECONVERT_LoadOLE10 [Internal]
6579 * Loads the OLE10 STREAM to memory
6582 * pOleStream [I] The OLESTREAM
6583 * pData [I] Data Structure for the OLESTREAM Data
6587 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6588 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6591 * This function is used by OleConvertOLESTREAMToIStorage only.
6593 * Memory allocated for pData must be freed by the caller
6595 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6598 HRESULT hRes = S_OK;
6602 pData->pData = NULL;
6603 pData->pstrOleObjFileName = (CHAR *) NULL;
6605 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6608 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6609 if(dwSize != sizeof(pData->dwOleID))
6611 hRes = CONVERT10_E_OLESTREAM_GET;
6613 else if(pData->dwOleID != OLESTREAM_ID)
6615 hRes = CONVERT10_E_OLESTREAM_FMT;
6626 /* Get the TypeID...more info needed for this field */
6627 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6628 if(dwSize != sizeof(pData->dwTypeID))
6630 hRes = CONVERT10_E_OLESTREAM_GET;
6635 if(pData->dwTypeID != 0)
6637 /* Get the length of the OleTypeName */
6638 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6639 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6641 hRes = CONVERT10_E_OLESTREAM_GET;
6646 if(pData->dwOleTypeNameLength > 0)
6648 /* Get the OleTypeName */
6649 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6650 if(dwSize != pData->dwOleTypeNameLength)
6652 hRes = CONVERT10_E_OLESTREAM_GET;
6658 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6659 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6661 hRes = CONVERT10_E_OLESTREAM_GET;
6665 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6666 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6667 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6668 if(pData->pstrOleObjFileName)
6670 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6671 if(dwSize != pData->dwOleObjFileNameLength)
6673 hRes = CONVERT10_E_OLESTREAM_GET;
6677 hRes = CONVERT10_E_OLESTREAM_GET;
6682 /* Get the Width of the Metafile */
6683 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6684 if(dwSize != sizeof(pData->dwMetaFileWidth))
6686 hRes = CONVERT10_E_OLESTREAM_GET;
6690 /* Get the Height of the Metafile */
6691 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6692 if(dwSize != sizeof(pData->dwMetaFileHeight))
6694 hRes = CONVERT10_E_OLESTREAM_GET;
6700 /* Get the Length of the Data */
6701 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6702 if(dwSize != sizeof(pData->dwDataLength))
6704 hRes = CONVERT10_E_OLESTREAM_GET;
6708 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6710 if(!bStrem1) /* if it is a second OLE stream data */
6712 pData->dwDataLength -= 8;
6713 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6714 if(dwSize != sizeof(pData->strUnknown))
6716 hRes = CONVERT10_E_OLESTREAM_GET;
6722 if(pData->dwDataLength > 0)
6724 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6726 /* Get Data (ex. IStorage, Metafile, or BMP) */
6729 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6730 if(dwSize != pData->dwDataLength)
6732 hRes = CONVERT10_E_OLESTREAM_GET;
6737 hRes = CONVERT10_E_OLESTREAM_GET;
6746 /*************************************************************************
6747 * OLECONVERT_SaveOLE10 [Internal]
6749 * Saves the OLE10 STREAM From memory
6752 * pData [I] Data Structure for the OLESTREAM Data
6753 * pOleStream [I] The OLESTREAM to save
6757 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6760 * This function is used by OleConvertIStorageToOLESTREAM only.
6763 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6766 HRESULT hRes = S_OK;
6770 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6771 if(dwSize != sizeof(pData->dwOleID))
6773 hRes = CONVERT10_E_OLESTREAM_PUT;
6778 /* Set the TypeID */
6779 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6780 if(dwSize != sizeof(pData->dwTypeID))
6782 hRes = CONVERT10_E_OLESTREAM_PUT;
6786 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6788 /* Set the Length of the OleTypeName */
6789 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6790 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6792 hRes = CONVERT10_E_OLESTREAM_PUT;
6797 if(pData->dwOleTypeNameLength > 0)
6799 /* Set the OleTypeName */
6800 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6801 if(dwSize != pData->dwOleTypeNameLength)
6803 hRes = CONVERT10_E_OLESTREAM_PUT;
6810 /* Set the width of the Metafile */
6811 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6812 if(dwSize != sizeof(pData->dwMetaFileWidth))
6814 hRes = CONVERT10_E_OLESTREAM_PUT;
6820 /* Set the height of the Metafile */
6821 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6822 if(dwSize != sizeof(pData->dwMetaFileHeight))
6824 hRes = CONVERT10_E_OLESTREAM_PUT;
6830 /* Set the length of the Data */
6831 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6832 if(dwSize != sizeof(pData->dwDataLength))
6834 hRes = CONVERT10_E_OLESTREAM_PUT;
6840 if(pData->dwDataLength > 0)
6842 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6843 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6844 if(dwSize != pData->dwDataLength)
6846 hRes = CONVERT10_E_OLESTREAM_PUT;
6854 /*************************************************************************
6855 * OLECONVERT_GetOLE20FromOLE10[Internal]
6857 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6858 * opens it, and copies the content to the dest IStorage for
6859 * OleConvertOLESTREAMToIStorage
6863 * pDestStorage [I] The IStorage to copy the data to
6864 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6865 * nBufferLength [I] The size of the buffer
6874 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6878 IStorage *pTempStorage;
6879 DWORD dwNumOfBytesWritten;
6880 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6881 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6883 /* Create a temp File */
6884 GetTempPathW(MAX_PATH, wstrTempDir);
6885 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6886 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6888 if(hFile != INVALID_HANDLE_VALUE)
6890 /* Write IStorage Data to File */
6891 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6894 /* Open and copy temp storage to the Dest Storage */
6895 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6898 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6899 StorageBaseImpl_Release(pTempStorage);
6901 DeleteFileW(wstrTempFile);
6906 /*************************************************************************
6907 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6909 * Saves the OLE10 STREAM From memory
6912 * pStorage [I] The Src IStorage to copy
6913 * pData [I] The Dest Memory to write to.
6916 * The size in bytes allocated for pData
6919 * Memory allocated for pData must be freed by the caller
6921 * Used by OleConvertIStorageToOLESTREAM only.
6924 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6928 DWORD nDataLength = 0;
6929 IStorage *pTempStorage;
6930 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6931 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6935 /* Create temp Storage */
6936 GetTempPathW(MAX_PATH, wstrTempDir);
6937 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6938 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6942 /* Copy Src Storage to the Temp Storage */
6943 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6944 StorageBaseImpl_Release(pTempStorage);
6946 /* Open Temp Storage as a file and copy to memory */
6947 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6948 if(hFile != INVALID_HANDLE_VALUE)
6950 nDataLength = GetFileSize(hFile, NULL);
6951 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6952 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6955 DeleteFileW(wstrTempFile);
6960 /*************************************************************************
6961 * OLECONVERT_CreateOleStream [Internal]
6963 * Creates the "\001OLE" stream in the IStorage if necessary.
6966 * pStorage [I] Dest storage to create the stream in
6972 * This function is used by OleConvertOLESTREAMToIStorage only.
6974 * This stream is still unknown, MS Word seems to have extra data
6975 * but since the data is stored in the OLESTREAM there should be
6976 * no need to recreate the stream. If the stream is manually
6977 * deleted it will create it with this default data.
6980 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6984 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6985 BYTE pOleStreamHeader [] =
6987 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6988 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6989 0x00, 0x00, 0x00, 0x00
6992 /* Create stream if not present */
6993 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6994 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6998 /* Write default Data */
6999 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7000 IStream_Release(pStream);
7004 /* write a string to a stream, preceded by its length */
7005 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7012 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7013 r = IStream_Write( stm, &len, sizeof(len), NULL);
7018 str = CoTaskMemAlloc( len );
7019 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7020 r = IStream_Write( stm, str, len, NULL);
7021 CoTaskMemFree( str );
7025 /* read a string preceded by its length from a stream */
7026 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7029 DWORD len, count = 0;
7033 r = IStream_Read( stm, &len, sizeof(len), &count );
7036 if( count != sizeof(len) )
7037 return E_OUTOFMEMORY;
7039 TRACE("%ld bytes\n",len);
7041 str = CoTaskMemAlloc( len );
7043 return E_OUTOFMEMORY;
7045 r = IStream_Read( stm, str, len, &count );
7050 CoTaskMemFree( str );
7051 return E_OUTOFMEMORY;
7054 TRACE("Read string %s\n",debugstr_an(str,len));
7056 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7057 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7059 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7060 CoTaskMemFree( str );
7068 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7069 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7073 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7075 static const BYTE unknown1[12] =
7076 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7077 0xFF, 0xFF, 0xFF, 0xFF};
7078 static const BYTE unknown2[16] =
7079 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7080 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7082 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7083 debugstr_w(lpszUserType), debugstr_w(szClipName),
7084 debugstr_w(szProgIDName));
7086 /* Create a CompObj stream if it doesn't exist */
7087 r = IStorage_CreateStream(pstg, szwStreamName,
7088 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7092 /* Write CompObj Structure to stream */
7093 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7095 if( SUCCEEDED( r ) )
7096 r = WriteClassStm( pstm, clsid );
7098 if( SUCCEEDED( r ) )
7099 r = STREAM_WriteString( pstm, lpszUserType );
7100 if( SUCCEEDED( r ) )
7101 r = STREAM_WriteString( pstm, szClipName );
7102 if( SUCCEEDED( r ) )
7103 r = STREAM_WriteString( pstm, szProgIDName );
7104 if( SUCCEEDED( r ) )
7105 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7107 IStream_Release( pstm );
7112 /***********************************************************************
7113 * WriteFmtUserTypeStg (OLE32.@)
7115 HRESULT WINAPI WriteFmtUserTypeStg(
7116 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7119 WCHAR szwClipName[0x40];
7120 CLSID clsid = CLSID_NULL;
7121 LPWSTR wstrProgID = NULL;
7124 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7126 /* get the clipboard format name */
7127 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
7130 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7132 /* FIXME: There's room to save a CLSID and its ProgID, but
7133 the CLSID is not looked up in the registry and in all the
7134 tests I wrote it was CLSID_NULL. Where does it come from?
7137 /* get the real program ID. This may fail, but that's fine */
7138 ProgIDFromCLSID(&clsid, &wstrProgID);
7140 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7142 r = STORAGE_WriteCompObj( pstg, &clsid,
7143 lpszUserType, szwClipName, wstrProgID );
7145 CoTaskMemFree(wstrProgID);
7151 /******************************************************************************
7152 * ReadFmtUserTypeStg [OLE32.@]
7154 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7158 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7159 unsigned char unknown1[12];
7160 unsigned char unknown2[16];
7162 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7165 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7167 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7168 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7171 WARN("Failed to open stream r = %08lx\n", r);
7175 /* read the various parts of the structure */
7176 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7177 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7179 r = ReadClassStm( stm, &clsid );
7183 r = STREAM_ReadString( stm, &szCLSIDName );
7187 r = STREAM_ReadString( stm, &szOleTypeName );
7191 r = STREAM_ReadString( stm, &szProgIDName );
7195 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7196 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7199 /* ok, success... now we just need to store what we found */
7201 *pcf = RegisterClipboardFormatW( szOleTypeName );
7202 CoTaskMemFree( szOleTypeName );
7204 if( lplpszUserType )
7205 *lplpszUserType = szCLSIDName;
7206 CoTaskMemFree( szProgIDName );
7209 IStream_Release( stm );
7215 /*************************************************************************
7216 * OLECONVERT_CreateCompObjStream [Internal]
7218 * Creates a "\001CompObj" is the destination IStorage if necessary.
7221 * pStorage [I] The dest IStorage to create the CompObj Stream
7223 * strOleTypeName [I] The ProgID
7227 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7230 * This function is used by OleConvertOLESTREAMToIStorage only.
7232 * The stream data is stored in the OLESTREAM and there should be
7233 * no need to recreate the stream. If the stream is manually
7234 * deleted it will attempt to create it by querying the registry.
7238 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7241 HRESULT hStorageRes, hRes = S_OK;
7242 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7243 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7244 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7246 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7247 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7249 /* Initialize the CompObj structure */
7250 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7251 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7252 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7255 /* Create a CompObj stream if it doesn't exist */
7256 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7257 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7258 if(hStorageRes == S_OK)
7260 /* copy the OleTypeName to the compobj struct */
7261 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7262 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7264 /* copy the OleTypeName to the compobj struct */
7265 /* Note: in the test made, these were Identical */
7266 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7267 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7270 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7271 bufferW, OLESTREAM_MAX_STR_LEN );
7272 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7278 /* Get the CLSID Default Name from the Registry */
7279 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7280 if(hErr == ERROR_SUCCESS)
7282 char strTemp[OLESTREAM_MAX_STR_LEN];
7283 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7284 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7285 if(hErr == ERROR_SUCCESS)
7287 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7293 /* Write CompObj Structure to stream */
7294 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7296 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7298 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7299 if(IStorageCompObj.dwCLSIDNameLength > 0)
7301 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7303 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7304 if(IStorageCompObj.dwOleTypeNameLength > 0)
7306 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7308 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7309 if(IStorageCompObj.dwProgIDNameLength > 0)
7311 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7313 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7314 IStream_Release(pStream);
7320 /*************************************************************************
7321 * OLECONVERT_CreateOlePresStream[Internal]
7323 * Creates the "\002OlePres000" Stream with the Metafile data
7326 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7327 * dwExtentX [I] Width of the Metafile
7328 * dwExtentY [I] Height of the Metafile
7329 * pData [I] Metafile data
7330 * dwDataLength [I] Size of the Metafile data
7334 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7337 * This function is used by OleConvertOLESTREAMToIStorage only.
7340 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7344 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7345 BYTE pOlePresStreamHeader [] =
7347 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7348 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7349 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7350 0x00, 0x00, 0x00, 0x00
7353 BYTE pOlePresStreamHeaderEmpty [] =
7355 0x00, 0x00, 0x00, 0x00,
7356 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7357 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7358 0x00, 0x00, 0x00, 0x00
7361 /* Create the OlePres000 Stream */
7362 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7363 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7368 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7370 memset(&OlePres, 0, sizeof(OlePres));
7371 /* Do we have any metafile data to save */
7372 if(dwDataLength > 0)
7374 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7375 nHeaderSize = sizeof(pOlePresStreamHeader);
7379 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7380 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7382 /* Set width and height of the metafile */
7383 OlePres.dwExtentX = dwExtentX;
7384 OlePres.dwExtentY = -dwExtentY;
7386 /* Set Data and Length */
7387 if(dwDataLength > sizeof(METAFILEPICT16))
7389 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7390 OlePres.pData = &(pData[8]);
7392 /* Save OlePres000 Data to Stream */
7393 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7394 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7395 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7396 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7397 if(OlePres.dwSize > 0)
7399 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7401 IStream_Release(pStream);
7405 /*************************************************************************
7406 * OLECONVERT_CreateOle10NativeStream [Internal]
7408 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7411 * pStorage [I] Dest storage to create the stream in
7412 * pData [I] Ole10 Native Data (ex. bmp)
7413 * dwDataLength [I] Size of the Ole10 Native Data
7419 * This function is used by OleConvertOLESTREAMToIStorage only.
7421 * Might need to verify the data and return appropriate error message
7424 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7428 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7430 /* Create the Ole10Native Stream */
7431 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7432 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7436 /* Write info to stream */
7437 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7438 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7439 IStream_Release(pStream);
7444 /*************************************************************************
7445 * OLECONVERT_GetOLE10ProgID [Internal]
7447 * Finds the ProgID (or OleTypeID) from the IStorage
7450 * pStorage [I] The Src IStorage to get the ProgID
7451 * strProgID [I] the ProgID string to get
7452 * dwSize [I] the size of the string
7456 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7459 * This function is used by OleConvertIStorageToOLESTREAM only.
7463 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7467 LARGE_INTEGER iSeekPos;
7468 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7469 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7471 /* Open the CompObj Stream */
7472 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7473 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7477 /*Get the OleType from the CompObj Stream */
7478 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7479 iSeekPos.u.HighPart = 0;
7481 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7482 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7483 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7484 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7485 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7486 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7487 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7489 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7492 IStream_Read(pStream, strProgID, *dwSize, NULL);
7494 IStream_Release(pStream);
7499 LPOLESTR wstrProgID;
7501 /* Get the OleType from the registry */
7502 REFCLSID clsid = &(stat.clsid);
7503 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7504 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7507 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7514 /*************************************************************************
7515 * OLECONVERT_GetOle10PresData [Internal]
7517 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7520 * pStorage [I] Src IStroage
7521 * pOleStream [I] Dest OleStream Mem Struct
7527 * This function is used by OleConvertIStorageToOLESTREAM only.
7529 * Memory allocated for pData must be freed by the caller
7533 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7538 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7540 /* Initialize Default data for OLESTREAM */
7541 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7542 pOleStreamData[0].dwTypeID = 2;
7543 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7544 pOleStreamData[1].dwTypeID = 0;
7545 pOleStreamData[0].dwMetaFileWidth = 0;
7546 pOleStreamData[0].dwMetaFileHeight = 0;
7547 pOleStreamData[0].pData = NULL;
7548 pOleStreamData[1].pData = NULL;
7550 /* Open Ole10Native Stream */
7551 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7552 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7556 /* Read Size and Data */
7557 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7558 if(pOleStreamData->dwDataLength > 0)
7560 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7561 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7563 IStream_Release(pStream);
7569 /*************************************************************************
7570 * OLECONVERT_GetOle20PresData[Internal]
7572 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7575 * pStorage [I] Src IStroage
7576 * pOleStreamData [I] Dest OleStream Mem Struct
7582 * This function is used by OleConvertIStorageToOLESTREAM only.
7584 * Memory allocated for pData must be freed by the caller
7586 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7590 OLECONVERT_ISTORAGE_OLEPRES olePress;
7591 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7593 /* Initialize Default data for OLESTREAM */
7594 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7595 pOleStreamData[0].dwTypeID = 2;
7596 pOleStreamData[0].dwMetaFileWidth = 0;
7597 pOleStreamData[0].dwMetaFileHeight = 0;
7598 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7599 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7600 pOleStreamData[1].dwTypeID = 0;
7601 pOleStreamData[1].dwOleTypeNameLength = 0;
7602 pOleStreamData[1].strOleTypeName[0] = 0;
7603 pOleStreamData[1].dwMetaFileWidth = 0;
7604 pOleStreamData[1].dwMetaFileHeight = 0;
7605 pOleStreamData[1].pData = NULL;
7606 pOleStreamData[1].dwDataLength = 0;
7609 /* Open OlePress000 stream */
7610 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7611 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7614 LARGE_INTEGER iSeekPos;
7615 METAFILEPICT16 MetaFilePict;
7616 static const char strMetafilePictName[] = "METAFILEPICT";
7618 /* Set the TypeID for a Metafile */
7619 pOleStreamData[1].dwTypeID = 5;
7621 /* Set the OleTypeName to Metafile */
7622 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7623 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7625 iSeekPos.u.HighPart = 0;
7626 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7628 /* Get Presentation Data */
7629 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7630 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7631 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7632 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7634 /*Set width and Height */
7635 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7636 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7637 if(olePress.dwSize > 0)
7640 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7642 /* Set MetaFilePict struct */
7643 MetaFilePict.mm = 8;
7644 MetaFilePict.xExt = olePress.dwExtentX;
7645 MetaFilePict.yExt = olePress.dwExtentY;
7646 MetaFilePict.hMF = 0;
7648 /* Get Metafile Data */
7649 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7650 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7651 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7653 IStream_Release(pStream);
7657 /*************************************************************************
7658 * OleConvertOLESTREAMToIStorage [OLE32.@]
7663 * DVTARGETDEVICE paramenter is not handled
7664 * Still unsure of some mem fields for OLE 10 Stream
7665 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7666 * and "\001OLE" streams
7669 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7670 LPOLESTREAM pOleStream,
7672 const DVTARGETDEVICE* ptd)
7676 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7678 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7680 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7684 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7687 if(pstg == NULL || pOleStream == NULL)
7689 hRes = E_INVALIDARG;
7694 /* Load the OLESTREAM to Memory */
7695 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7700 /* Load the OLESTREAM to Memory (part 2)*/
7701 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7707 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7709 /* Do we have the IStorage Data in the OLESTREAM */
7710 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7712 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7713 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7717 /* It must be an original OLE 1.0 source */
7718 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7723 /* It must be an original OLE 1.0 source */
7724 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7727 /* Create CompObj Stream if necessary */
7728 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7731 /*Create the Ole Stream if necessary */
7732 OLECONVERT_CreateOleStream(pstg);
7737 /* Free allocated memory */
7738 for(i=0; i < 2; i++)
7740 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7741 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7742 pOleStreamData[i].pstrOleObjFileName = NULL;
7747 /*************************************************************************
7748 * OleConvertIStorageToOLESTREAM [OLE32.@]
7755 * Still unsure of some mem fields for OLE 10 Stream
7756 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7757 * and "\001OLE" streams.
7760 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7762 LPOLESTREAM pOleStream)
7765 HRESULT hRes = S_OK;
7767 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7768 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7770 TRACE("%p %p\n", pstg, pOleStream);
7772 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7774 if(pstg == NULL || pOleStream == NULL)
7776 hRes = E_INVALIDARG;
7780 /* Get the ProgID */
7781 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7782 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7786 /* Was it originally Ole10 */
7787 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7790 IStream_Release(pStream);
7791 /* Get Presentation Data for Ole10Native */
7792 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7796 /* Get Presentation Data (OLE20) */
7797 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7800 /* Save OLESTREAM */
7801 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7804 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7809 /* Free allocated memory */
7810 for(i=0; i < 2; i++)
7812 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7818 /***********************************************************************
7819 * GetConvertStg (OLE32.@)
7821 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7822 FIXME("unimplemented stub!\n");
7826 /******************************************************************************
7827 * StgIsStorageFile [OLE32.@]
7828 * Verify if the file contains a storage object
7834 * S_OK if file has magic bytes as a storage object
7835 * S_FALSE if file is not storage
7838 StgIsStorageFile(LPCOLESTR fn)
7844 TRACE("%s\n", debugstr_w(fn));
7845 hf = CreateFileW(fn, GENERIC_READ,
7846 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7847 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7849 if (hf == INVALID_HANDLE_VALUE)
7850 return STG_E_FILENOTFOUND;
7852 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7854 WARN(" unable to read file\n");
7861 if (bytes_read != 8) {
7862 WARN(" too short\n");
7866 if (!memcmp(magic,STORAGE_magic,8)) {
7871 WARN(" -> Invalid header.\n");