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 Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
97 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
98 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
99 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
100 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
102 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
103 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
104 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
106 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
109 /* OLESTREAM memory structure to use for Get and Put Routines */
110 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
115 DWORD dwOleTypeNameLength;
116 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
117 CHAR *pstrOleObjFileName;
118 DWORD dwOleObjFileNameLength;
119 DWORD dwMetaFileWidth;
120 DWORD dwMetaFileHeight;
121 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
124 }OLECONVERT_OLESTREAM_DATA;
126 /* CompObj Stream structure */
127 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
132 DWORD dwCLSIDNameLength;
133 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
134 DWORD dwOleTypeNameLength;
135 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
136 DWORD dwProgIDNameLength;
137 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
139 }OLECONVERT_ISTORAGE_COMPOBJ;
142 /* Ole Presention Stream structure */
143 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
151 }OLECONVERT_ISTORAGE_OLEPRES;
155 /***********************************************************************
156 * Forward declaration of internal functions used by the method DestroyElement
158 static HRESULT deleteStorageProperty(
159 StorageImpl *parentStorage,
160 ULONG foundPropertyIndexToDelete,
161 StgProperty propertyToDelete);
163 static HRESULT deleteStreamProperty(
164 StorageImpl *parentStorage,
165 ULONG foundPropertyIndexToDelete,
166 StgProperty propertyToDelete);
168 static HRESULT findPlaceholder(
169 StorageImpl *storage,
170 ULONG propertyIndexToStore,
171 ULONG storagePropertyIndex,
174 static HRESULT adjustPropertyChain(
176 StgProperty propertyToDelete,
177 StgProperty parentProperty,
178 ULONG parentPropertyId,
181 /***********************************************************************
182 * Declaration of the functions used to manipulate StgProperty
185 static ULONG getFreeProperty(
186 StorageImpl *storage);
188 static void updatePropertyChain(
189 StorageImpl *storage,
190 ULONG newPropertyIndex,
191 StgProperty newProperty);
193 static LONG propertyNameCmp(
194 const OLECHAR *newProperty,
195 const OLECHAR *currentProperty);
198 /***********************************************************************
199 * Declaration of miscellaneous functions...
201 static HRESULT validateSTGM(DWORD stgmValue);
203 static DWORD GetShareModeFromSTGM(DWORD stgm);
204 static DWORD GetAccessModeFromSTGM(DWORD stgm);
205 static DWORD GetCreationModeFromSTGM(DWORD stgm);
207 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
210 /****************************************************************************
211 * IEnumSTATSTGImpl definitions.
213 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
214 * This class allows iterating through the content of a storage and to find
215 * specific items inside it.
217 struct IEnumSTATSTGImpl
219 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
220 * since we want to cast this in an IEnumSTATSTG pointer */
222 LONG ref; /* Reference count */
223 StorageImpl* parentStorage; /* Reference to the parent storage */
224 ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
227 * The current implementation of the IEnumSTATSTGImpl class uses a stack
228 * to walk the property sets to get the content of a storage. This stack
229 * is implemented by the following 3 data members
235 #define ENUMSTATSGT_SIZE_INCREMENT 10
239 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
240 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
241 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
242 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
243 static ULONG IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl* This, const OLECHAR* lpszPropName,
244 StgProperty* buffer);
245 static INT IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl *This, ULONG childProperty,
246 StgProperty *currentProperty, ULONG *propertyId);
249 /************************************************************************
250 ** Storage32BaseImpl implementatiion
253 /************************************************************************
254 * Storage32BaseImpl_QueryInterface (IUnknown)
256 * This method implements the common QueryInterface for all IStorage32
257 * implementations contained in this file.
259 * See Windows documentation for more details on IUnknown methods.
261 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
266 StorageBaseImpl *This = (StorageBaseImpl *)iface;
268 * Perform a sanity check on the parameters.
270 if ( (This==0) || (ppvObject==0) )
274 * Initialize the return parameter.
279 * Compare the riid with the interface IDs implemented by this object.
281 if (IsEqualGUID(&IID_IUnknown, riid) ||
282 IsEqualGUID(&IID_IStorage, riid))
284 *ppvObject = (IStorage*)This;
286 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
288 *ppvObject = (IStorage*)&This->pssVtbl;
292 * Check that we obtained an interface.
295 return E_NOINTERFACE;
298 * Query Interface always increases the reference count by one when it is
301 IStorage_AddRef(iface);
306 /************************************************************************
307 * Storage32BaseImpl_AddRef (IUnknown)
309 * This method implements the common AddRef for all IStorage32
310 * implementations contained in this file.
312 * See Windows documentation for more details on IUnknown methods.
314 static ULONG WINAPI StorageBaseImpl_AddRef(
317 StorageBaseImpl *This = (StorageBaseImpl *)iface;
318 ULONG ref = InterlockedIncrement(&This->ref);
320 TRACE("(%p) AddRef to %d\n", This, ref);
325 /************************************************************************
326 * Storage32BaseImpl_Release (IUnknown)
328 * This method implements the common Release for all IStorage32
329 * implementations contained in this file.
331 * See Windows documentation for more details on IUnknown methods.
333 static ULONG WINAPI StorageBaseImpl_Release(
336 StorageBaseImpl *This = (StorageBaseImpl *)iface;
338 * Decrease the reference count on this object.
340 ULONG ref = InterlockedDecrement(&This->ref);
342 TRACE("(%p) ReleaseRef to %d\n", This, ref);
345 * If the reference count goes down to 0, perform suicide.
350 * Since we are using a system of base-classes, we want to call the
351 * destructor of the appropriate derived class. To do this, we are
352 * using virtual functions to implement the destructor.
354 This->v_destructor(This);
360 /************************************************************************
361 * Storage32BaseImpl_OpenStream (IStorage)
363 * This method will open the specified stream object from the current storage.
365 * See Windows documentation for more details on IStorage methods.
367 static HRESULT WINAPI StorageBaseImpl_OpenStream(
369 const OLECHAR* pwcsName, /* [string][in] */
370 void* reserved1, /* [unique][in] */
371 DWORD grfMode, /* [in] */
372 DWORD reserved2, /* [in] */
373 IStream** ppstm) /* [out] */
375 StorageBaseImpl *This = (StorageBaseImpl *)iface;
376 IEnumSTATSTGImpl* propertyEnumeration;
377 StgStreamImpl* newStream;
378 StgProperty currentProperty;
379 ULONG foundPropertyIndex;
380 HRESULT res = STG_E_UNKNOWN;
382 TRACE("(%p, %s, %p, %x, %d, %p)\n",
383 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
386 * Perform a sanity check on the parameters.
388 if ( (pwcsName==NULL) || (ppstm==0) )
395 * Initialize the out parameter
400 * Validate the STGM flags
402 if ( FAILED( validateSTGM(grfMode) ) ||
403 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
405 res = STG_E_INVALIDFLAG;
412 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
414 res = STG_E_INVALIDFUNCTION;
419 * Check that we're compatible with the parent's storage mode, but
420 * only if we are not in transacted mode
422 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
423 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
425 res = STG_E_ACCESSDENIED;
431 * Create a property enumeration to search the properties
433 propertyEnumeration = IEnumSTATSTGImpl_Construct(
434 This->ancestorStorage,
435 This->rootPropertySetIndex);
438 * Search the enumeration for the property with the given name
440 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
446 * Delete the property enumeration since we don't need it anymore
448 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
451 * If it was found, construct the stream object and return a pointer to it.
453 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
454 (currentProperty.propertyType==PROPTYPE_STREAM) )
456 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
460 newStream->grfMode = grfMode;
461 *ppstm = (IStream*)newStream;
464 * Since we are returning a pointer to the interface, we have to
465 * nail down the reference.
467 IStream_AddRef(*ppstm);
477 res = STG_E_FILENOTFOUND;
481 TRACE("<-- IStream %p\n", *ppstm);
482 TRACE("<-- %08x\n", res);
486 /************************************************************************
487 * Storage32BaseImpl_OpenStorage (IStorage)
489 * This method will open a new storage object from the current storage.
491 * See Windows documentation for more details on IStorage methods.
493 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
495 const OLECHAR* pwcsName, /* [string][unique][in] */
496 IStorage* pstgPriority, /* [unique][in] */
497 DWORD grfMode, /* [in] */
498 SNB snbExclude, /* [unique][in] */
499 DWORD reserved, /* [in] */
500 IStorage** ppstg) /* [out] */
502 StorageBaseImpl *This = (StorageBaseImpl *)iface;
503 StorageInternalImpl* newStorage;
504 IEnumSTATSTGImpl* propertyEnumeration;
505 StgProperty currentProperty;
506 ULONG foundPropertyIndex;
507 HRESULT res = STG_E_UNKNOWN;
509 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
510 iface, debugstr_w(pwcsName), pstgPriority,
511 grfMode, snbExclude, reserved, ppstg);
514 * Perform a sanity check on the parameters.
516 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
523 if (snbExclude != NULL)
525 res = STG_E_INVALIDPARAMETER;
530 * Validate the STGM flags
532 if ( FAILED( validateSTGM(grfMode) ))
534 res = STG_E_INVALIDFLAG;
541 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
542 (grfMode & STGM_DELETEONRELEASE) ||
543 (grfMode & STGM_PRIORITY) )
545 res = STG_E_INVALIDFUNCTION;
550 * Check that we're compatible with the parent's storage mode,
551 * but only if we are not transacted
553 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
554 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
556 res = STG_E_ACCESSDENIED;
562 * Initialize the out parameter
567 * Create a property enumeration to search the properties
569 propertyEnumeration = IEnumSTATSTGImpl_Construct(
570 This->ancestorStorage,
571 This->rootPropertySetIndex);
574 * Search the enumeration for the property with the given name
576 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
582 * Delete the property enumeration since we don't need it anymore
584 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
587 * If it was found, construct the stream object and return a pointer to it.
589 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
590 (currentProperty.propertyType==PROPTYPE_STORAGE) )
593 * Construct a new Storage object
595 newStorage = StorageInternalImpl_Construct(
596 This->ancestorStorage,
602 *ppstg = (IStorage*)newStorage;
605 * Since we are returning a pointer to the interface,
606 * we have to nail down the reference.
608 StorageBaseImpl_AddRef(*ppstg);
614 res = STG_E_INSUFFICIENTMEMORY;
618 res = STG_E_FILENOTFOUND;
621 TRACE("<-- %08x\n", res);
625 /************************************************************************
626 * Storage32BaseImpl_EnumElements (IStorage)
628 * This method will create an enumerator object that can be used to
629 * retrieve informatino about all the properties in the storage object.
631 * See Windows documentation for more details on IStorage methods.
633 static HRESULT WINAPI StorageBaseImpl_EnumElements(
635 DWORD reserved1, /* [in] */
636 void* reserved2, /* [size_is][unique][in] */
637 DWORD reserved3, /* [in] */
638 IEnumSTATSTG** ppenum) /* [out] */
640 StorageBaseImpl *This = (StorageBaseImpl *)iface;
641 IEnumSTATSTGImpl* newEnum;
643 TRACE("(%p, %d, %p, %d, %p)\n",
644 iface, reserved1, reserved2, reserved3, ppenum);
647 * Perform a sanity check on the parameters.
649 if ( (This==0) || (ppenum==0))
653 * Construct the enumerator.
655 newEnum = IEnumSTATSTGImpl_Construct(
656 This->ancestorStorage,
657 This->rootPropertySetIndex);
661 *ppenum = (IEnumSTATSTG*)newEnum;
664 * Don't forget to nail down a reference to the new object before
667 IEnumSTATSTG_AddRef(*ppenum);
672 return E_OUTOFMEMORY;
675 /************************************************************************
676 * Storage32BaseImpl_Stat (IStorage)
678 * This method will retrieve information about this storage object.
680 * See Windows documentation for more details on IStorage methods.
682 static HRESULT WINAPI StorageBaseImpl_Stat(
684 STATSTG* pstatstg, /* [out] */
685 DWORD grfStatFlag) /* [in] */
687 StorageBaseImpl *This = (StorageBaseImpl *)iface;
688 StgProperty curProperty;
690 HRESULT res = STG_E_UNKNOWN;
692 TRACE("(%p, %p, %x)\n",
693 iface, pstatstg, grfStatFlag);
696 * Perform a sanity check on the parameters.
698 if ( (This==0) || (pstatstg==0))
705 * Read the information from the property.
707 readSuccessful = StorageImpl_ReadProperty(
708 This->ancestorStorage,
709 This->rootPropertySetIndex,
714 StorageUtl_CopyPropertyToSTATSTG(
719 pstatstg->grfMode = This->openFlags;
730 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
732 TRACE("<-- %08x\n", res);
736 /************************************************************************
737 * Storage32BaseImpl_RenameElement (IStorage)
739 * This method will rename the specified element.
741 * See Windows documentation for more details on IStorage methods.
743 * Implementation notes: The method used to rename consists of creating a clone
744 * of the deleted StgProperty object setting it with the new name and to
745 * perform a DestroyElement of the old StgProperty.
747 static HRESULT WINAPI StorageBaseImpl_RenameElement(
749 const OLECHAR* pwcsOldName, /* [in] */
750 const OLECHAR* pwcsNewName) /* [in] */
752 StorageBaseImpl *This = (StorageBaseImpl *)iface;
753 IEnumSTATSTGImpl* propertyEnumeration;
754 StgProperty currentProperty;
755 ULONG foundPropertyIndex;
757 TRACE("(%p, %s, %s)\n",
758 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
761 * Create a property enumeration to search the properties
763 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
764 This->rootPropertySetIndex);
767 * Search the enumeration for the new property name
769 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
773 if (foundPropertyIndex != PROPERTY_NULL)
776 * There is already a property with the new name
778 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
779 return STG_E_FILEALREADYEXISTS;
782 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
785 * Search the enumeration for the old property name
787 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
792 * Delete the property enumeration since we don't need it anymore
794 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
796 if (foundPropertyIndex != PROPERTY_NULL)
798 StgProperty renamedProperty;
799 ULONG renamedPropertyIndex;
802 * Setup a new property for the renamed property
804 renamedProperty.sizeOfNameString =
805 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
807 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
808 return STG_E_INVALIDNAME;
810 strcpyW(renamedProperty.name, pwcsNewName);
812 renamedProperty.propertyType = currentProperty.propertyType;
813 renamedProperty.startingBlock = currentProperty.startingBlock;
814 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
815 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
817 renamedProperty.previousProperty = PROPERTY_NULL;
818 renamedProperty.nextProperty = PROPERTY_NULL;
821 * Bring the dirProperty link in case it is a storage and in which
822 * case the renamed storage elements don't require to be reorganized.
824 renamedProperty.dirProperty = currentProperty.dirProperty;
826 /* call CoFileTime to get the current time
827 renamedProperty.timeStampS1
828 renamedProperty.timeStampD1
829 renamedProperty.timeStampS2
830 renamedProperty.timeStampD2
831 renamedProperty.propertyUniqueID
835 * Obtain a free property in the property chain
837 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
840 * Save the new property into the new property spot
842 StorageImpl_WriteProperty(
843 This->ancestorStorage,
844 renamedPropertyIndex,
848 * Find a spot in the property chain for our newly created property.
852 renamedPropertyIndex,
856 * At this point the renamed property has been inserted in the tree,
857 * now, before Destroying the old property we must zero its dirProperty
858 * otherwise the DestroyProperty below will zap it all and we do not want
860 * Also, we fake that the old property is a storage so the DestroyProperty
861 * will not do a SetSize(0) on the stream data.
863 * This means that we need to tweak the StgProperty if it is a stream or a
866 StorageImpl_ReadProperty(This->ancestorStorage,
870 currentProperty.dirProperty = PROPERTY_NULL;
871 currentProperty.propertyType = PROPTYPE_STORAGE;
872 StorageImpl_WriteProperty(
873 This->ancestorStorage,
878 * Invoke Destroy to get rid of the ole property and automatically redo
879 * the linking of its previous and next members...
881 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
887 * There is no property with the old name
889 return STG_E_FILENOTFOUND;
895 /************************************************************************
896 * Storage32BaseImpl_CreateStream (IStorage)
898 * This method will create a stream object within this storage
900 * See Windows documentation for more details on IStorage methods.
902 static HRESULT WINAPI StorageBaseImpl_CreateStream(
904 const OLECHAR* pwcsName, /* [string][in] */
905 DWORD grfMode, /* [in] */
906 DWORD reserved1, /* [in] */
907 DWORD reserved2, /* [in] */
908 IStream** ppstm) /* [out] */
910 StorageBaseImpl *This = (StorageBaseImpl *)iface;
911 IEnumSTATSTGImpl* propertyEnumeration;
912 StgStreamImpl* newStream;
913 StgProperty currentProperty, newStreamProperty;
914 ULONG foundPropertyIndex, newPropertyIndex;
916 TRACE("(%p, %s, %x, %d, %d, %p)\n",
917 iface, debugstr_w(pwcsName), grfMode,
918 reserved1, reserved2, ppstm);
921 * Validate parameters
924 return STG_E_INVALIDPOINTER;
927 return STG_E_INVALIDNAME;
929 if (reserved1 || reserved2)
930 return STG_E_INVALIDPARAMETER;
933 * Validate the STGM flags
935 if ( FAILED( validateSTGM(grfMode) ))
936 return STG_E_INVALIDFLAG;
938 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
939 return STG_E_INVALIDFLAG;
944 if ((grfMode & STGM_DELETEONRELEASE) ||
945 (grfMode & STGM_TRANSACTED))
946 return STG_E_INVALIDFUNCTION;
949 * Check that we're compatible with the parent's storage mode
950 * if not in transacted mode
952 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
953 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
954 return STG_E_ACCESSDENIED;
958 * Initialize the out parameter
963 * Create a property enumeration to search the properties
965 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
966 This->rootPropertySetIndex);
968 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
972 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
974 if (foundPropertyIndex != PROPERTY_NULL)
977 * An element with this name already exists
979 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
981 IStorage_DestroyElement(iface, pwcsName);
984 return STG_E_FILEALREADYEXISTS;
986 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
988 WARN("read-only storage\n");
989 return STG_E_ACCESSDENIED;
993 * memset the empty property
995 memset(&newStreamProperty, 0, sizeof(StgProperty));
997 newStreamProperty.sizeOfNameString =
998 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1000 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1001 return STG_E_INVALIDNAME;
1003 strcpyW(newStreamProperty.name, pwcsName);
1005 newStreamProperty.propertyType = PROPTYPE_STREAM;
1006 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1007 newStreamProperty.size.u.LowPart = 0;
1008 newStreamProperty.size.u.HighPart = 0;
1010 newStreamProperty.previousProperty = PROPERTY_NULL;
1011 newStreamProperty.nextProperty = PROPERTY_NULL;
1012 newStreamProperty.dirProperty = PROPERTY_NULL;
1014 /* call CoFileTime to get the current time
1015 newStreamProperty.timeStampS1
1016 newStreamProperty.timeStampD1
1017 newStreamProperty.timeStampS2
1018 newStreamProperty.timeStampD2
1021 /* newStreamProperty.propertyUniqueID */
1024 * Get a free property or create a new one
1026 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1029 * Save the new property into the new property spot
1031 StorageImpl_WriteProperty(
1032 This->ancestorStorage,
1034 &newStreamProperty);
1037 * Find a spot in the property chain for our newly created property.
1039 updatePropertyChain(
1045 * Open the stream to return it.
1047 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1051 *ppstm = (IStream*)newStream;
1054 * Since we are returning a pointer to the interface, we have to nail down
1057 IStream_AddRef(*ppstm);
1061 return STG_E_INSUFFICIENTMEMORY;
1067 /************************************************************************
1068 * Storage32BaseImpl_SetClass (IStorage)
1070 * This method will write the specified CLSID in the property of this
1073 * See Windows documentation for more details on IStorage methods.
1075 static HRESULT WINAPI StorageBaseImpl_SetClass(
1077 REFCLSID clsid) /* [in] */
1079 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1080 HRESULT hRes = E_FAIL;
1081 StgProperty curProperty;
1084 TRACE("(%p, %p)\n", iface, clsid);
1086 success = StorageImpl_ReadProperty(This->ancestorStorage,
1087 This->rootPropertySetIndex,
1091 curProperty.propertyUniqueID = *clsid;
1093 success = StorageImpl_WriteProperty(This->ancestorStorage,
1094 This->rootPropertySetIndex,
1103 /************************************************************************
1104 ** Storage32Impl implementation
1107 /************************************************************************
1108 * Storage32Impl_CreateStorage (IStorage)
1110 * This method will create the storage object within the provided storage.
1112 * See Windows documentation for more details on IStorage methods.
1114 static HRESULT WINAPI StorageImpl_CreateStorage(
1116 const OLECHAR *pwcsName, /* [string][in] */
1117 DWORD grfMode, /* [in] */
1118 DWORD reserved1, /* [in] */
1119 DWORD reserved2, /* [in] */
1120 IStorage **ppstg) /* [out] */
1122 StorageImpl* const This=(StorageImpl*)iface;
1124 IEnumSTATSTGImpl *propertyEnumeration;
1125 StgProperty currentProperty;
1126 StgProperty newProperty;
1127 ULONG foundPropertyIndex;
1128 ULONG newPropertyIndex;
1131 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1132 iface, debugstr_w(pwcsName), grfMode,
1133 reserved1, reserved2, ppstg);
1136 * Validate parameters
1139 return STG_E_INVALIDPOINTER;
1142 return STG_E_INVALIDNAME;
1145 * Initialize the out parameter
1150 * Validate the STGM flags
1152 if ( FAILED( validateSTGM(grfMode) ) ||
1153 (grfMode & STGM_DELETEONRELEASE) )
1155 WARN("bad grfMode: 0x%x\n", grfMode);
1156 return STG_E_INVALIDFLAG;
1160 * Check that we're compatible with the parent's storage mode
1162 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1164 WARN("access denied\n");
1165 return STG_E_ACCESSDENIED;
1169 * Create a property enumeration and search the properties
1171 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1172 This->base.rootPropertySetIndex);
1174 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1177 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1179 if (foundPropertyIndex != PROPERTY_NULL)
1182 * An element with this name already exists
1184 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1185 IStorage_DestroyElement(iface, pwcsName);
1188 WARN("file already exists\n");
1189 return STG_E_FILEALREADYEXISTS;
1192 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1194 WARN("read-only storage\n");
1195 return STG_E_ACCESSDENIED;
1199 * memset the empty property
1201 memset(&newProperty, 0, sizeof(StgProperty));
1203 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1205 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1207 FIXME("name too long\n");
1208 return STG_E_INVALIDNAME;
1211 strcpyW(newProperty.name, pwcsName);
1213 newProperty.propertyType = PROPTYPE_STORAGE;
1214 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1215 newProperty.size.u.LowPart = 0;
1216 newProperty.size.u.HighPart = 0;
1218 newProperty.previousProperty = PROPERTY_NULL;
1219 newProperty.nextProperty = PROPERTY_NULL;
1220 newProperty.dirProperty = PROPERTY_NULL;
1222 /* call CoFileTime to get the current time
1223 newProperty.timeStampS1
1224 newProperty.timeStampD1
1225 newProperty.timeStampS2
1226 newProperty.timeStampD2
1229 /* newStorageProperty.propertyUniqueID */
1232 * Obtain a free property in the property chain
1234 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1237 * Save the new property into the new property spot
1239 StorageImpl_WriteProperty(
1240 This->base.ancestorStorage,
1245 * Find a spot in the property chain for our newly created property.
1247 updatePropertyChain(
1253 * Open it to get a pointer to return.
1255 hr = IStorage_OpenStorage(
1257 (const OLECHAR*)pwcsName,
1264 if( (hr != S_OK) || (*ppstg == NULL))
1274 /***************************************************************************
1278 * Get a free property or create a new one.
1280 static ULONG getFreeProperty(
1281 StorageImpl *storage)
1283 ULONG currentPropertyIndex = 0;
1284 ULONG newPropertyIndex = PROPERTY_NULL;
1285 BOOL readSuccessful = TRUE;
1286 StgProperty currentProperty;
1291 * Start by reading the root property
1293 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1294 currentPropertyIndex,
1298 if (currentProperty.sizeOfNameString == 0)
1301 * The property existis and is available, we found it.
1303 newPropertyIndex = currentPropertyIndex;
1309 * We exhausted the property list, we will create more space below
1311 newPropertyIndex = currentPropertyIndex;
1313 currentPropertyIndex++;
1315 } while (newPropertyIndex == PROPERTY_NULL);
1318 * grow the property chain
1320 if (! readSuccessful)
1322 StgProperty emptyProperty;
1323 ULARGE_INTEGER newSize;
1324 ULONG propertyIndex;
1325 ULONG lastProperty = 0;
1326 ULONG blockCount = 0;
1329 * obtain the new count of property blocks
1331 blockCount = BlockChainStream_GetCount(
1332 storage->base.ancestorStorage->rootBlockChain)+1;
1335 * initialize the size used by the property stream
1337 newSize.u.HighPart = 0;
1338 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1341 * add a property block to the property chain
1343 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1346 * memset the empty property in order to initialize the unused newly
1349 memset(&emptyProperty, 0, sizeof(StgProperty));
1354 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1357 propertyIndex = newPropertyIndex;
1358 propertyIndex < lastProperty;
1361 StorageImpl_WriteProperty(
1362 storage->base.ancestorStorage,
1368 return newPropertyIndex;
1371 /****************************************************************************
1375 * Case insensitive comparaison of StgProperty.name by first considering
1378 * Returns <0 when newPrpoerty < currentProperty
1379 * >0 when newPrpoerty > currentProperty
1380 * 0 when newPrpoerty == currentProperty
1382 static LONG propertyNameCmp(
1383 const OLECHAR *newProperty,
1384 const OLECHAR *currentProperty)
1386 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1391 * We compare the string themselves only when they are of the same length
1393 diff = lstrcmpiW( newProperty, currentProperty);
1399 /****************************************************************************
1403 * Properly link this new element in the property chain.
1405 static void updatePropertyChain(
1406 StorageImpl *storage,
1407 ULONG newPropertyIndex,
1408 StgProperty newProperty)
1410 StgProperty currentProperty;
1413 * Read the root property
1415 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1416 storage->base.rootPropertySetIndex,
1419 if (currentProperty.dirProperty != PROPERTY_NULL)
1422 * The root storage contains some element, therefore, start the research
1423 * for the appropriate location.
1426 ULONG current, next, previous, currentPropertyId;
1429 * Keep the StgProperty sequence number of the storage first property
1431 currentPropertyId = currentProperty.dirProperty;
1436 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1437 currentProperty.dirProperty,
1440 previous = currentProperty.previousProperty;
1441 next = currentProperty.nextProperty;
1442 current = currentPropertyId;
1446 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1450 if (previous != PROPERTY_NULL)
1452 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1459 currentProperty.previousProperty = newPropertyIndex;
1460 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1468 if (next != PROPERTY_NULL)
1470 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1477 currentProperty.nextProperty = newPropertyIndex;
1478 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1487 * Trying to insert an item with the same name in the
1488 * subtree structure.
1493 previous = currentProperty.previousProperty;
1494 next = currentProperty.nextProperty;
1500 * The root storage is empty, link the new property to its dir property
1502 currentProperty.dirProperty = newPropertyIndex;
1503 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1504 storage->base.rootPropertySetIndex,
1510 /*************************************************************************
1513 static HRESULT WINAPI StorageImpl_CopyTo(
1515 DWORD ciidExclude, /* [in] */
1516 const IID* rgiidExclude, /* [size_is][unique][in] */
1517 SNB snbExclude, /* [unique][in] */
1518 IStorage* pstgDest) /* [unique][in] */
1520 IEnumSTATSTG *elements = 0;
1521 STATSTG curElement, strStat;
1523 IStorage *pstgTmp, *pstgChild;
1524 IStream *pstrTmp, *pstrChild;
1526 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1527 FIXME("Exclude option not implemented\n");
1529 TRACE("(%p, %d, %p, %p, %p)\n",
1530 iface, ciidExclude, rgiidExclude,
1531 snbExclude, pstgDest);
1534 * Perform a sanity check
1536 if ( pstgDest == 0 )
1537 return STG_E_INVALIDPOINTER;
1540 * Enumerate the elements
1542 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1550 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1551 IStorage_SetClass( pstgDest, &curElement.clsid );
1556 * Obtain the next element
1558 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1560 if ( hr == S_FALSE )
1562 hr = S_OK; /* done, every element has been copied */
1566 if (curElement.type == STGTY_STORAGE)
1569 * open child source storage
1571 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1572 STGM_READ|STGM_SHARE_EXCLUSIVE,
1573 NULL, 0, &pstgChild );
1579 * Check if destination storage is not a child of the source
1580 * storage, which will cause an infinite loop
1582 if (pstgChild == pstgDest)
1584 IEnumSTATSTG_Release(elements);
1586 return STG_E_ACCESSDENIED;
1590 * create a new storage in destination storage
1592 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1593 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1597 * if it already exist, don't create a new one use this one
1599 if (hr == STG_E_FILEALREADYEXISTS)
1601 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1602 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1603 NULL, 0, &pstgTmp );
1611 * do the copy recursively
1613 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1614 snbExclude, pstgTmp );
1616 IStorage_Release( pstgTmp );
1617 IStorage_Release( pstgChild );
1619 else if (curElement.type == STGTY_STREAM)
1622 * create a new stream in destination storage. If the stream already
1623 * exist, it will be deleted and a new one will be created.
1625 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1626 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1633 * open child stream storage
1635 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1636 STGM_READ|STGM_SHARE_EXCLUSIVE,
1643 * Get the size of the source stream
1645 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1648 * Set the size of the destination stream.
1650 IStream_SetSize(pstrTmp, strStat.cbSize);
1655 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1658 IStream_Release( pstrTmp );
1659 IStream_Release( pstrChild );
1663 WARN("unknown element type: %d\n", curElement.type);
1666 } while (hr == S_OK);
1671 IEnumSTATSTG_Release(elements);
1676 /*************************************************************************
1677 * MoveElementTo (IStorage)
1679 static HRESULT WINAPI StorageImpl_MoveElementTo(
1681 const OLECHAR *pwcsName, /* [string][in] */
1682 IStorage *pstgDest, /* [unique][in] */
1683 const OLECHAR *pwcsNewName,/* [string][in] */
1684 DWORD grfFlags) /* [in] */
1686 FIXME("(%p %s %p %s %u): stub\n", iface,
1687 debugstr_w(pwcsName), pstgDest,
1688 debugstr_w(pwcsNewName), grfFlags);
1692 /*************************************************************************
1695 * Ensures that any changes made to a storage object open in transacted mode
1696 * are reflected in the parent storage
1699 * Wine doesn't implement transacted mode, which seems to be a basic
1700 * optimization, so we can ignore this stub for now.
1702 static HRESULT WINAPI StorageImpl_Commit(
1704 DWORD grfCommitFlags)/* [in] */
1706 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1710 /*************************************************************************
1713 * Discard all changes that have been made since the last commit operation
1715 static HRESULT WINAPI StorageImpl_Revert(
1718 FIXME("(%p): stub\n", iface);
1722 /*************************************************************************
1723 * DestroyElement (IStorage)
1725 * Strategy: This implementation is built this way for simplicity not for speed.
1726 * I always delete the topmost element of the enumeration and adjust
1727 * the deleted element pointer all the time. This takes longer to
1728 * do but allow to reinvoke DestroyElement whenever we encounter a
1729 * storage object. The optimisation resides in the usage of another
1730 * enumeration strategy that would give all the leaves of a storage
1731 * first. (postfix order)
1733 static HRESULT WINAPI StorageImpl_DestroyElement(
1735 const OLECHAR *pwcsName)/* [string][in] */
1737 StorageImpl* const This=(StorageImpl*)iface;
1739 IEnumSTATSTGImpl* propertyEnumeration;
1742 StgProperty propertyToDelete;
1743 StgProperty parentProperty;
1744 ULONG foundPropertyIndexToDelete;
1745 ULONG typeOfRelation;
1746 ULONG parentPropertyId = 0;
1749 iface, debugstr_w(pwcsName));
1752 * Perform a sanity check on the parameters.
1755 return STG_E_INVALIDPOINTER;
1758 * Create a property enumeration to search the property with the given name
1760 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1761 This->base.ancestorStorage,
1762 This->base.rootPropertySetIndex);
1764 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1765 propertyEnumeration,
1769 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1771 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1773 return STG_E_FILENOTFOUND;
1777 * Find the parent property of the property to delete (the one that
1778 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1779 * the parent is This. Otherwise, the parent is one of its sibling...
1783 * First, read This's StgProperty..
1785 res = StorageImpl_ReadProperty(
1786 This->base.ancestorStorage,
1787 This->base.rootPropertySetIndex,
1793 * Second, check to see if by any chance the actual storage (This) is not
1794 * the parent of the property to delete... We never know...
1796 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1799 * Set data as it would have been done in the else part...
1801 typeOfRelation = PROPERTY_RELATION_DIR;
1802 parentPropertyId = This->base.rootPropertySetIndex;
1807 * Create a property enumeration to search the parent properties, and
1808 * delete it once done.
1810 IEnumSTATSTGImpl* propertyEnumeration2;
1812 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1813 This->base.ancestorStorage,
1814 This->base.rootPropertySetIndex);
1816 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1817 propertyEnumeration2,
1818 foundPropertyIndexToDelete,
1822 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1825 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1827 hr = deleteStorageProperty(
1829 foundPropertyIndexToDelete,
1832 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1834 hr = deleteStreamProperty(
1836 foundPropertyIndexToDelete,
1844 * Adjust the property chain
1846 hr = adjustPropertyChain(
1857 /************************************************************************
1858 * StorageImpl_Stat (IStorage)
1860 * This method will retrieve information about this storage object.
1862 * See Windows documentation for more details on IStorage methods.
1864 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1865 STATSTG* pstatstg, /* [out] */
1866 DWORD grfStatFlag) /* [in] */
1868 StorageImpl* const This = (StorageImpl*)iface;
1869 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1871 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1873 CoTaskMemFree(pstatstg->pwcsName);
1874 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1875 strcpyW(pstatstg->pwcsName, This->pwcsName);
1881 /******************************************************************************
1882 * Internal stream list handlers
1885 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1887 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1888 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1891 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1893 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1894 list_remove(&(strm->StrmListEntry));
1897 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1899 struct list *cur, *cur2;
1900 StgStreamImpl *strm=NULL;
1902 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1903 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1904 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1905 strm->parentStorage = NULL;
1911 /*********************************************************************
1915 * Perform the deletion of a complete storage node
1918 static HRESULT deleteStorageProperty(
1919 StorageImpl *parentStorage,
1920 ULONG indexOfPropertyToDelete,
1921 StgProperty propertyToDelete)
1923 IEnumSTATSTG *elements = 0;
1924 IStorage *childStorage = 0;
1925 STATSTG currentElement;
1927 HRESULT destroyHr = S_OK;
1930 * Open the storage and enumerate it
1932 hr = StorageBaseImpl_OpenStorage(
1933 (IStorage*)parentStorage,
1934 propertyToDelete.name,
1936 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1947 * Enumerate the elements
1949 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1954 * Obtain the next element
1956 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1959 destroyHr = StorageImpl_DestroyElement(
1960 (IStorage*)childStorage,
1961 (OLECHAR*)currentElement.pwcsName);
1963 CoTaskMemFree(currentElement.pwcsName);
1967 * We need to Reset the enumeration every time because we delete elements
1968 * and the enumeration could be invalid
1970 IEnumSTATSTG_Reset(elements);
1972 } while ((hr == S_OK) && (destroyHr == S_OK));
1975 * Invalidate the property by zeroing its name member.
1977 propertyToDelete.sizeOfNameString = 0;
1979 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1980 indexOfPropertyToDelete,
1983 IStorage_Release(childStorage);
1984 IEnumSTATSTG_Release(elements);
1989 /*********************************************************************
1993 * Perform the deletion of a stream node
1996 static HRESULT deleteStreamProperty(
1997 StorageImpl *parentStorage,
1998 ULONG indexOfPropertyToDelete,
1999 StgProperty propertyToDelete)
2003 ULARGE_INTEGER size;
2005 size.u.HighPart = 0;
2008 hr = StorageBaseImpl_OpenStream(
2009 (IStorage*)parentStorage,
2010 (OLECHAR*)propertyToDelete.name,
2012 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2024 hr = IStream_SetSize(pis, size);
2032 * Release the stream object.
2034 IStream_Release(pis);
2037 * Invalidate the property by zeroing its name member.
2039 propertyToDelete.sizeOfNameString = 0;
2042 * Here we should re-read the property so we get the updated pointer
2043 * but since we are here to zap it, I don't do it...
2045 StorageImpl_WriteProperty(
2046 parentStorage->base.ancestorStorage,
2047 indexOfPropertyToDelete,
2053 /*********************************************************************
2057 * Finds a placeholder for the StgProperty within the Storage
2060 static HRESULT findPlaceholder(
2061 StorageImpl *storage,
2062 ULONG propertyIndexToStore,
2063 ULONG storePropertyIndex,
2066 StgProperty storeProperty;
2071 * Read the storage property
2073 res = StorageImpl_ReadProperty(
2074 storage->base.ancestorStorage,
2083 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2085 if (storeProperty.previousProperty != PROPERTY_NULL)
2087 return findPlaceholder(
2089 propertyIndexToStore,
2090 storeProperty.previousProperty,
2095 storeProperty.previousProperty = propertyIndexToStore;
2098 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2100 if (storeProperty.nextProperty != PROPERTY_NULL)
2102 return findPlaceholder(
2104 propertyIndexToStore,
2105 storeProperty.nextProperty,
2110 storeProperty.nextProperty = propertyIndexToStore;
2113 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2115 if (storeProperty.dirProperty != PROPERTY_NULL)
2117 return findPlaceholder(
2119 propertyIndexToStore,
2120 storeProperty.dirProperty,
2125 storeProperty.dirProperty = propertyIndexToStore;
2129 hr = StorageImpl_WriteProperty(
2130 storage->base.ancestorStorage,
2142 /*************************************************************************
2146 * This method takes the previous and the next property link of a property
2147 * to be deleted and find them a place in the Storage.
2149 static HRESULT adjustPropertyChain(
2151 StgProperty propertyToDelete,
2152 StgProperty parentProperty,
2153 ULONG parentPropertyId,
2156 ULONG newLinkProperty = PROPERTY_NULL;
2157 BOOL needToFindAPlaceholder = FALSE;
2158 ULONG storeNode = PROPERTY_NULL;
2159 ULONG toStoreNode = PROPERTY_NULL;
2160 INT relationType = 0;
2164 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2166 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2169 * Set the parent previous to the property to delete previous
2171 newLinkProperty = propertyToDelete.previousProperty;
2173 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2176 * We also need to find a storage for the other link, setup variables
2177 * to do this at the end...
2179 needToFindAPlaceholder = TRUE;
2180 storeNode = propertyToDelete.previousProperty;
2181 toStoreNode = propertyToDelete.nextProperty;
2182 relationType = PROPERTY_RELATION_NEXT;
2185 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2188 * Set the parent previous to the property to delete next
2190 newLinkProperty = propertyToDelete.nextProperty;
2194 * Link it for real...
2196 parentProperty.previousProperty = newLinkProperty;
2199 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2201 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2204 * Set the parent next to the property to delete next previous
2206 newLinkProperty = propertyToDelete.previousProperty;
2208 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2211 * We also need to find a storage for the other link, setup variables
2212 * to do this at the end...
2214 needToFindAPlaceholder = TRUE;
2215 storeNode = propertyToDelete.previousProperty;
2216 toStoreNode = propertyToDelete.nextProperty;
2217 relationType = PROPERTY_RELATION_NEXT;
2220 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2223 * Set the parent next to the property to delete next
2225 newLinkProperty = propertyToDelete.nextProperty;
2229 * Link it for real...
2231 parentProperty.nextProperty = newLinkProperty;
2233 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2235 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2238 * Set the parent dir to the property to delete previous
2240 newLinkProperty = propertyToDelete.previousProperty;
2242 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2245 * We also need to find a storage for the other link, setup variables
2246 * to do this at the end...
2248 needToFindAPlaceholder = TRUE;
2249 storeNode = propertyToDelete.previousProperty;
2250 toStoreNode = propertyToDelete.nextProperty;
2251 relationType = PROPERTY_RELATION_NEXT;
2254 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2257 * Set the parent dir to the property to delete next
2259 newLinkProperty = propertyToDelete.nextProperty;
2263 * Link it for real...
2265 parentProperty.dirProperty = newLinkProperty;
2269 * Write back the parent property
2271 res = StorageImpl_WriteProperty(
2272 This->base.ancestorStorage,
2281 * If a placeholder is required for the other link, then, find one and
2282 * get out of here...
2284 if (needToFindAPlaceholder)
2286 hr = findPlaceholder(
2297 /******************************************************************************
2298 * SetElementTimes (IStorage)
2300 static HRESULT WINAPI StorageImpl_SetElementTimes(
2302 const OLECHAR *pwcsName,/* [string][in] */
2303 const FILETIME *pctime, /* [in] */
2304 const FILETIME *patime, /* [in] */
2305 const FILETIME *pmtime) /* [in] */
2307 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2311 /******************************************************************************
2312 * SetStateBits (IStorage)
2314 static HRESULT WINAPI StorageImpl_SetStateBits(
2316 DWORD grfStateBits,/* [in] */
2317 DWORD grfMask) /* [in] */
2319 FIXME("not implemented!\n");
2324 * Virtual function table for the IStorage32Impl class.
2326 static const IStorageVtbl Storage32Impl_Vtbl =
2328 StorageBaseImpl_QueryInterface,
2329 StorageBaseImpl_AddRef,
2330 StorageBaseImpl_Release,
2331 StorageBaseImpl_CreateStream,
2332 StorageBaseImpl_OpenStream,
2333 StorageImpl_CreateStorage,
2334 StorageBaseImpl_OpenStorage,
2336 StorageImpl_MoveElementTo,
2339 StorageBaseImpl_EnumElements,
2340 StorageImpl_DestroyElement,
2341 StorageBaseImpl_RenameElement,
2342 StorageImpl_SetElementTimes,
2343 StorageBaseImpl_SetClass,
2344 StorageImpl_SetStateBits,
2348 static HRESULT StorageImpl_Construct(
2358 StgProperty currentProperty;
2359 BOOL readSuccessful;
2360 ULONG currentPropertyIndex;
2362 if ( FAILED( validateSTGM(openFlags) ))
2363 return STG_E_INVALIDFLAG;
2365 memset(This, 0, sizeof(StorageImpl));
2368 * Initialize stream list
2371 list_init(&This->base.strmHead);
2374 * Initialize the virtual function table.
2376 This->base.lpVtbl = &Storage32Impl_Vtbl;
2377 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2378 This->base.v_destructor = &StorageImpl_Destroy;
2379 This->base.openFlags = (openFlags & ~STGM_CREATE);
2382 * This is the top-level storage so initialize the ancestor pointer
2385 This->base.ancestorStorage = This;
2388 * Initialize the physical support of the storage.
2390 This->hFile = hFile;
2393 * Store copy of file path.
2396 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2397 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2398 if (!This->pwcsName)
2399 return STG_E_INSUFFICIENTMEMORY;
2400 strcpyW(This->pwcsName, pwcsName);
2404 * Initialize the big block cache.
2406 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2407 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2408 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2414 if (This->bigBlockFile == 0)
2419 ULARGE_INTEGER size;
2420 BYTE* bigBlockBuffer;
2423 * Initialize all header variables:
2424 * - The big block depot consists of one block and it is at block 0
2425 * - The properties start at block 1
2426 * - There is no small block depot
2428 memset( This->bigBlockDepotStart,
2430 sizeof(This->bigBlockDepotStart));
2432 This->bigBlockDepotCount = 1;
2433 This->bigBlockDepotStart[0] = 0;
2434 This->rootStartBlock = 1;
2435 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2436 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2437 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2438 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2439 This->extBigBlockDepotCount = 0;
2441 StorageImpl_SaveFileHeader(This);
2444 * Add one block for the big block depot and one block for the properties
2446 size.u.HighPart = 0;
2447 size.u.LowPart = This->bigBlockSize * 3;
2448 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2451 * Initialize the big block depot
2453 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2454 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2455 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2456 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2457 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2462 * Load the header for the file.
2464 hr = StorageImpl_LoadFileHeader(This);
2468 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2475 * There is no block depot cached yet.
2477 This->indexBlockDepotCached = 0xFFFFFFFF;
2480 * Start searching for free blocks with block 0.
2482 This->prevFreeBlock = 0;
2485 * Create the block chain abstractions.
2487 if(!(This->rootBlockChain =
2488 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2489 return STG_E_READFAULT;
2491 if(!(This->smallBlockDepotChain =
2492 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2494 return STG_E_READFAULT;
2497 * Write the root property (memory only)
2501 StgProperty rootProp;
2503 * Initialize the property chain
2505 memset(&rootProp, 0, sizeof(rootProp));
2506 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2507 sizeof(rootProp.name)/sizeof(WCHAR) );
2508 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2509 rootProp.propertyType = PROPTYPE_ROOT;
2510 rootProp.previousProperty = PROPERTY_NULL;
2511 rootProp.nextProperty = PROPERTY_NULL;
2512 rootProp.dirProperty = PROPERTY_NULL;
2513 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2514 rootProp.size.u.HighPart = 0;
2515 rootProp.size.u.LowPart = 0;
2517 StorageImpl_WriteProperty(This, 0, &rootProp);
2521 * Find the ID of the root in the property sets.
2523 currentPropertyIndex = 0;
2527 readSuccessful = StorageImpl_ReadProperty(
2529 currentPropertyIndex,
2534 if ( (currentProperty.sizeOfNameString != 0 ) &&
2535 (currentProperty.propertyType == PROPTYPE_ROOT) )
2537 This->base.rootPropertySetIndex = currentPropertyIndex;
2541 currentPropertyIndex++;
2543 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2545 if (!readSuccessful)
2548 return STG_E_READFAULT;
2552 * Create the block chain abstraction for the small block root chain.
2554 if(!(This->smallBlockRootChain =
2555 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2556 return STG_E_READFAULT;
2561 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2563 StorageImpl *This = (StorageImpl*) iface;
2564 TRACE("(%p)\n", This);
2566 StorageBaseImpl_DeleteAll(&This->base);
2568 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2570 BlockChainStream_Destroy(This->smallBlockRootChain);
2571 BlockChainStream_Destroy(This->rootBlockChain);
2572 BlockChainStream_Destroy(This->smallBlockDepotChain);
2574 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2575 HeapFree(GetProcessHeap(), 0, This);
2578 /******************************************************************************
2579 * Storage32Impl_GetNextFreeBigBlock
2581 * Returns the index of the next free big block.
2582 * If the big block depot is filled, this method will enlarge it.
2585 static ULONG StorageImpl_GetNextFreeBigBlock(
2588 ULONG depotBlockIndexPos;
2590 ULONG depotBlockOffset;
2591 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2592 ULONG nextBlockIndex = BLOCK_SPECIAL;
2594 ULONG freeBlock = BLOCK_UNUSED;
2596 depotIndex = This->prevFreeBlock / blocksPerDepot;
2597 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2600 * Scan the entire big block depot until we find a block marked free
2602 while (nextBlockIndex != BLOCK_UNUSED)
2604 if (depotIndex < COUNT_BBDEPOTINHEADER)
2606 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2609 * Grow the primary depot.
2611 if (depotBlockIndexPos == BLOCK_UNUSED)
2613 depotBlockIndexPos = depotIndex*blocksPerDepot;
2616 * Add a block depot.
2618 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2619 This->bigBlockDepotCount++;
2620 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2623 * Flag it as a block depot.
2625 StorageImpl_SetNextBlockInChain(This,
2629 /* Save new header information.
2631 StorageImpl_SaveFileHeader(This);
2636 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2638 if (depotBlockIndexPos == BLOCK_UNUSED)
2641 * Grow the extended depot.
2643 ULONG extIndex = BLOCK_UNUSED;
2644 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2645 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2647 if (extBlockOffset == 0)
2649 /* We need an extended block.
2651 extIndex = Storage32Impl_AddExtBlockDepot(This);
2652 This->extBigBlockDepotCount++;
2653 depotBlockIndexPos = extIndex + 1;
2656 depotBlockIndexPos = depotIndex * blocksPerDepot;
2659 * Add a block depot and mark it in the extended block.
2661 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2662 This->bigBlockDepotCount++;
2663 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2665 /* Flag the block depot.
2667 StorageImpl_SetNextBlockInChain(This,
2671 /* If necessary, flag the extended depot block.
2673 if (extIndex != BLOCK_UNUSED)
2674 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2676 /* Save header information.
2678 StorageImpl_SaveFileHeader(This);
2682 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2684 if (depotBuffer != 0)
2686 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2687 ( nextBlockIndex != BLOCK_UNUSED))
2689 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2691 if (nextBlockIndex == BLOCK_UNUSED)
2693 freeBlock = (depotIndex * blocksPerDepot) +
2694 (depotBlockOffset/sizeof(ULONG));
2697 depotBlockOffset += sizeof(ULONG);
2700 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2704 depotBlockOffset = 0;
2708 * make sure that the block physically exists before using it
2710 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2712 This->prevFreeBlock = freeBlock;
2717 /******************************************************************************
2718 * Storage32Impl_AddBlockDepot
2720 * This will create a depot block, essentially it is a block initialized
2723 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2727 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2730 * Initialize blocks as free
2732 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2734 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2737 /******************************************************************************
2738 * Storage32Impl_GetExtDepotBlock
2740 * Returns the index of the block that corresponds to the specified depot
2741 * index. This method is only for depot indexes equal or greater than
2742 * COUNT_BBDEPOTINHEADER.
2744 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2746 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2747 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2748 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2749 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2750 ULONG blockIndex = BLOCK_UNUSED;
2751 ULONG extBlockIndex = This->extBigBlockDepotStart;
2753 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2755 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2756 return BLOCK_UNUSED;
2758 while (extBlockCount > 0)
2760 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2764 if (extBlockIndex != BLOCK_UNUSED)
2768 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2770 if (depotBuffer != 0)
2772 StorageUtl_ReadDWord(depotBuffer,
2773 extBlockOffset * sizeof(ULONG),
2776 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2783 /******************************************************************************
2784 * Storage32Impl_SetExtDepotBlock
2786 * Associates the specified block index to the specified depot index.
2787 * This method is only for depot indexes equal or greater than
2788 * COUNT_BBDEPOTINHEADER.
2790 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2792 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2793 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2794 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2795 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2796 ULONG extBlockIndex = This->extBigBlockDepotStart;
2798 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2800 while (extBlockCount > 0)
2802 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2806 if (extBlockIndex != BLOCK_UNUSED)
2810 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2812 if (depotBuffer != 0)
2814 StorageUtl_WriteDWord(depotBuffer,
2815 extBlockOffset * sizeof(ULONG),
2818 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2823 /******************************************************************************
2824 * Storage32Impl_AddExtBlockDepot
2826 * Creates an extended depot block.
2828 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2830 ULONG numExtBlocks = This->extBigBlockDepotCount;
2831 ULONG nextExtBlock = This->extBigBlockDepotStart;
2832 BYTE* depotBuffer = NULL;
2833 ULONG index = BLOCK_UNUSED;
2834 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2835 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2836 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2838 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2839 blocksPerDepotBlock;
2841 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2844 * The first extended block.
2846 This->extBigBlockDepotStart = index;
2852 * Follow the chain to the last one.
2854 for (i = 0; i < (numExtBlocks - 1); i++)
2856 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2860 * Add the new extended block to the chain.
2862 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2863 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2864 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2868 * Initialize this block.
2870 depotBuffer = StorageImpl_GetBigBlock(This, index);
2871 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2872 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2877 /******************************************************************************
2878 * Storage32Impl_FreeBigBlock
2880 * This method will flag the specified block as free in the big block depot.
2882 static void StorageImpl_FreeBigBlock(
2886 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2888 if (blockIndex < This->prevFreeBlock)
2889 This->prevFreeBlock = blockIndex;
2892 /************************************************************************
2893 * Storage32Impl_GetNextBlockInChain
2895 * This method will retrieve the block index of the next big block in
2898 * Params: This - Pointer to the Storage object.
2899 * blockIndex - Index of the block to retrieve the chain
2901 * nextBlockIndex - receives the return value.
2903 * Returns: This method returns the index of the next block in the chain.
2904 * It will return the constants:
2905 * BLOCK_SPECIAL - If the block given was not part of a
2907 * BLOCK_END_OF_CHAIN - If the block given was the last in
2909 * BLOCK_UNUSED - If the block given was not past of a chain
2911 * BLOCK_EXTBBDEPOT - This block is part of the extended
2914 * See Windows documentation for more details on IStorage methods.
2916 static HRESULT StorageImpl_GetNextBlockInChain(
2919 ULONG* nextBlockIndex)
2921 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2922 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2923 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2925 ULONG depotBlockIndexPos;
2928 *nextBlockIndex = BLOCK_SPECIAL;
2930 if(depotBlockCount >= This->bigBlockDepotCount)
2932 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2933 This->bigBlockDepotCount);
2934 return STG_E_READFAULT;
2938 * Cache the currently accessed depot block.
2940 if (depotBlockCount != This->indexBlockDepotCached)
2942 This->indexBlockDepotCached = depotBlockCount;
2944 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2946 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2951 * We have to look in the extended depot.
2953 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2956 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2959 return STG_E_READFAULT;
2961 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2963 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2964 This->blockDepotCached[index] = *nextBlockIndex;
2966 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2969 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2974 /******************************************************************************
2975 * Storage32Impl_GetNextExtendedBlock
2977 * Given an extended block this method will return the next extended block.
2980 * The last ULONG of an extended block is the block index of the next
2981 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2985 * - The index of the next extended block
2986 * - BLOCK_UNUSED: there is no next extended block.
2987 * - Any other return values denotes failure.
2989 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2991 ULONG nextBlockIndex = BLOCK_SPECIAL;
2992 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2995 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2999 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3001 StorageImpl_ReleaseBigBlock(This, depotBuffer);
3004 return nextBlockIndex;
3007 /******************************************************************************
3008 * Storage32Impl_SetNextBlockInChain
3010 * This method will write the index of the specified block's next block
3011 * in the big block depot.
3013 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3016 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3017 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3018 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3021 static void StorageImpl_SetNextBlockInChain(
3026 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3027 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3028 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3029 ULONG depotBlockIndexPos;
3032 assert(depotBlockCount < This->bigBlockDepotCount);
3033 assert(blockIndex != nextBlock);
3035 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3037 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3042 * We have to look in the extended depot.
3044 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3047 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
3051 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
3052 StorageImpl_ReleaseBigBlock(This, depotBuffer);
3056 * Update the cached block depot, if necessary.
3058 if (depotBlockCount == This->indexBlockDepotCached)
3060 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3064 /******************************************************************************
3065 * Storage32Impl_LoadFileHeader
3067 * This method will read in the file header, i.e. big block index -1.
3069 static HRESULT StorageImpl_LoadFileHeader(
3072 HRESULT hr = STG_E_FILENOTFOUND;
3073 void* headerBigBlock = NULL;
3078 * Get a pointer to the big block of data containing the header.
3080 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
3083 * Extract the information from the header.
3085 if (headerBigBlock!=0)
3088 * Check for the "magic number" signature and return an error if it is not
3091 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3093 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3094 return STG_E_OLDFORMAT;
3097 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3099 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3100 return STG_E_INVALIDHEADER;
3103 StorageUtl_ReadWord(
3105 OFFSET_BIGBLOCKSIZEBITS,
3106 &This->bigBlockSizeBits);
3108 StorageUtl_ReadWord(
3110 OFFSET_SMALLBLOCKSIZEBITS,
3111 &This->smallBlockSizeBits);
3113 StorageUtl_ReadDWord(
3115 OFFSET_BBDEPOTCOUNT,
3116 &This->bigBlockDepotCount);
3118 StorageUtl_ReadDWord(
3120 OFFSET_ROOTSTARTBLOCK,
3121 &This->rootStartBlock);
3123 StorageUtl_ReadDWord(
3125 OFFSET_SBDEPOTSTART,
3126 &This->smallBlockDepotStart);
3128 StorageUtl_ReadDWord(
3130 OFFSET_EXTBBDEPOTSTART,
3131 &This->extBigBlockDepotStart);
3133 StorageUtl_ReadDWord(
3135 OFFSET_EXTBBDEPOTCOUNT,
3136 &This->extBigBlockDepotCount);
3138 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3140 StorageUtl_ReadDWord(
3142 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3143 &(This->bigBlockDepotStart[index]));
3147 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3151 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3152 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3156 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3157 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3161 * Right now, the code is making some assumptions about the size of the
3162 * blocks, just make sure they are what we're expecting.
3164 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3165 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3167 WARN("Broken OLE storage file\n");
3168 hr = STG_E_INVALIDHEADER;
3174 * Release the block.
3176 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3182 /******************************************************************************
3183 * Storage32Impl_SaveFileHeader
3185 * This method will save to the file the header, i.e. big block -1.
3187 static void StorageImpl_SaveFileHeader(
3190 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3195 * Get a pointer to the big block of data containing the header.
3197 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3200 * If the block read failed, the file is probably new.
3205 * Initialize for all unknown fields.
3207 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3210 * Initialize the magic number.
3212 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3215 * And a bunch of things we don't know what they mean
3217 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3218 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3219 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3220 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3224 * Write the information to the header.
3226 StorageUtl_WriteWord(
3228 OFFSET_BIGBLOCKSIZEBITS,
3229 This->bigBlockSizeBits);
3231 StorageUtl_WriteWord(
3233 OFFSET_SMALLBLOCKSIZEBITS,
3234 This->smallBlockSizeBits);
3236 StorageUtl_WriteDWord(
3238 OFFSET_BBDEPOTCOUNT,
3239 This->bigBlockDepotCount);
3241 StorageUtl_WriteDWord(
3243 OFFSET_ROOTSTARTBLOCK,
3244 This->rootStartBlock);
3246 StorageUtl_WriteDWord(
3248 OFFSET_SBDEPOTSTART,
3249 This->smallBlockDepotStart);
3251 StorageUtl_WriteDWord(
3253 OFFSET_SBDEPOTCOUNT,
3254 This->smallBlockDepotChain ?
3255 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3257 StorageUtl_WriteDWord(
3259 OFFSET_EXTBBDEPOTSTART,
3260 This->extBigBlockDepotStart);
3262 StorageUtl_WriteDWord(
3264 OFFSET_EXTBBDEPOTCOUNT,
3265 This->extBigBlockDepotCount);
3267 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3269 StorageUtl_WriteDWord(
3271 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3272 (This->bigBlockDepotStart[index]));
3276 * Write the big block back to the file.
3278 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3281 /******************************************************************************
3282 * Storage32Impl_ReadProperty
3284 * This method will read the specified property from the property chain.
3286 BOOL StorageImpl_ReadProperty(
3289 StgProperty* buffer)
3291 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3292 ULARGE_INTEGER offsetInPropSet;
3296 offsetInPropSet.u.HighPart = 0;
3297 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3299 readRes = BlockChainStream_ReadAt(
3300 This->rootBlockChain,
3306 if (SUCCEEDED(readRes))
3308 /* replace the name of root entry (often "Root Entry") by the file name */
3309 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3310 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3312 memset(buffer->name, 0, sizeof(buffer->name));
3316 PROPERTY_NAME_BUFFER_LEN );
3317 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3319 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3321 StorageUtl_ReadWord(
3323 OFFSET_PS_NAMELENGTH,
3324 &buffer->sizeOfNameString);
3326 StorageUtl_ReadDWord(
3328 OFFSET_PS_PREVIOUSPROP,
3329 &buffer->previousProperty);
3331 StorageUtl_ReadDWord(
3334 &buffer->nextProperty);
3336 StorageUtl_ReadDWord(
3339 &buffer->dirProperty);
3341 StorageUtl_ReadGUID(
3344 &buffer->propertyUniqueID);
3346 StorageUtl_ReadDWord(
3349 &buffer->timeStampS1);
3351 StorageUtl_ReadDWord(
3354 &buffer->timeStampD1);
3356 StorageUtl_ReadDWord(
3359 &buffer->timeStampS2);
3361 StorageUtl_ReadDWord(
3364 &buffer->timeStampD2);
3366 StorageUtl_ReadDWord(
3368 OFFSET_PS_STARTBLOCK,
3369 &buffer->startingBlock);
3371 StorageUtl_ReadDWord(
3374 &buffer->size.u.LowPart);
3376 buffer->size.u.HighPart = 0;
3379 return SUCCEEDED(readRes) ? TRUE : FALSE;
3382 /*********************************************************************
3383 * Write the specified property into the property chain
3385 BOOL StorageImpl_WriteProperty(
3388 StgProperty* buffer)
3390 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3391 ULARGE_INTEGER offsetInPropSet;
3395 offsetInPropSet.u.HighPart = 0;
3396 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3398 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3401 currentProperty + OFFSET_PS_NAME,
3403 PROPERTY_NAME_BUFFER_LEN );
3405 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3407 StorageUtl_WriteWord(
3409 OFFSET_PS_NAMELENGTH,
3410 buffer->sizeOfNameString);
3412 StorageUtl_WriteDWord(
3414 OFFSET_PS_PREVIOUSPROP,
3415 buffer->previousProperty);
3417 StorageUtl_WriteDWord(
3420 buffer->nextProperty);
3422 StorageUtl_WriteDWord(
3425 buffer->dirProperty);
3427 StorageUtl_WriteGUID(
3430 &buffer->propertyUniqueID);
3432 StorageUtl_WriteDWord(
3435 buffer->timeStampS1);
3437 StorageUtl_WriteDWord(
3440 buffer->timeStampD1);
3442 StorageUtl_WriteDWord(
3445 buffer->timeStampS2);
3447 StorageUtl_WriteDWord(
3450 buffer->timeStampD2);
3452 StorageUtl_WriteDWord(
3454 OFFSET_PS_STARTBLOCK,
3455 buffer->startingBlock);
3457 StorageUtl_WriteDWord(
3460 buffer->size.u.LowPart);
3462 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3467 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3470 static BOOL StorageImpl_ReadBigBlock(
3475 void* bigBlockBuffer;
3477 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3479 if (bigBlockBuffer!=0)
3481 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3483 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3491 static BOOL StorageImpl_WriteBigBlock(
3496 void* bigBlockBuffer;
3498 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3500 if (bigBlockBuffer!=0)
3502 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3504 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3512 static void* StorageImpl_GetROBigBlock(
3516 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3519 static void* StorageImpl_GetBigBlock(
3523 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3526 static void StorageImpl_ReleaseBigBlock(
3530 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3533 /******************************************************************************
3534 * Storage32Impl_SmallBlocksToBigBlocks
3536 * This method will convert a small block chain to a big block chain.
3537 * The small block chain will be destroyed.
3539 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3541 SmallBlockChainStream** ppsbChain)
3543 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3544 ULARGE_INTEGER size, offset;
3545 ULONG cbRead, cbWritten;
3546 ULARGE_INTEGER cbTotalRead;
3547 ULONG propertyIndex;
3548 HRESULT resWrite = S_OK;
3550 StgProperty chainProperty;
3552 BlockChainStream *bbTempChain = NULL;
3553 BlockChainStream *bigBlockChain = NULL;
3556 * Create a temporary big block chain that doesn't have
3557 * an associated property. This temporary chain will be
3558 * used to copy data from small blocks to big blocks.
3560 bbTempChain = BlockChainStream_Construct(This,
3563 if(!bbTempChain) return NULL;
3565 * Grow the big block chain.
3567 size = SmallBlockChainStream_GetSize(*ppsbChain);
3568 BlockChainStream_SetSize(bbTempChain, size);
3571 * Copy the contents of the small block chain to the big block chain
3572 * by small block size increments.
3574 offset.u.LowPart = 0;
3575 offset.u.HighPart = 0;
3576 cbTotalRead.QuadPart = 0;
3578 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3581 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3583 This->smallBlockSize,
3586 if (FAILED(resRead))
3591 cbTotalRead.QuadPart += cbRead;
3593 resWrite = BlockChainStream_WriteAt(bbTempChain,
3599 if (FAILED(resWrite))
3602 offset.u.LowPart += This->smallBlockSize;
3604 } while (cbTotalRead.QuadPart < size.QuadPart);
3605 HeapFree(GetProcessHeap(),0,buffer);
3607 if (FAILED(resRead) || FAILED(resWrite))
3609 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3610 BlockChainStream_Destroy(bbTempChain);
3615 * Destroy the small block chain.
3617 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3618 size.u.HighPart = 0;
3620 SmallBlockChainStream_SetSize(*ppsbChain, size);
3621 SmallBlockChainStream_Destroy(*ppsbChain);
3625 * Change the property information. This chain is now a big block chain
3626 * and it doesn't reside in the small blocks chain anymore.
3628 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3630 chainProperty.startingBlock = bbHeadOfChain;
3632 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3635 * Destroy the temporary propertyless big block chain.
3636 * Create a new big block chain associated with this property.
3638 BlockChainStream_Destroy(bbTempChain);
3639 bigBlockChain = BlockChainStream_Construct(This,
3643 return bigBlockChain;
3646 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3648 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3650 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3651 HeapFree(GetProcessHeap(), 0, This);
3654 /******************************************************************************
3656 ** Storage32InternalImpl_Commit
3658 ** The non-root storages cannot be opened in transacted mode thus this function
3661 static HRESULT WINAPI StorageInternalImpl_Commit(
3663 DWORD grfCommitFlags) /* [in] */
3668 /******************************************************************************
3670 ** Storage32InternalImpl_Revert
3672 ** The non-root storages cannot be opened in transacted mode thus this function
3675 static HRESULT WINAPI StorageInternalImpl_Revert(
3681 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3683 IStorage_Release((IStorage*)This->parentStorage);
3684 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3685 HeapFree(GetProcessHeap(), 0, This);
3688 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3689 IEnumSTATSTG* iface,
3693 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3696 * Perform a sanity check on the parameters.
3699 return E_INVALIDARG;
3702 * Initialize the return parameter.
3707 * Compare the riid with the interface IDs implemented by this object.
3709 if (IsEqualGUID(&IID_IUnknown, riid) ||
3710 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3712 *ppvObject = (IEnumSTATSTG*)This;
3713 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3717 return E_NOINTERFACE;
3720 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3721 IEnumSTATSTG* iface)
3723 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3724 return InterlockedIncrement(&This->ref);
3727 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3728 IEnumSTATSTG* iface)
3730 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3734 newRef = InterlockedDecrement(&This->ref);
3737 * If the reference count goes down to 0, perform suicide.
3741 IEnumSTATSTGImpl_Destroy(This);
3747 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3748 IEnumSTATSTG* iface,
3751 ULONG* pceltFetched)
3753 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3755 StgProperty currentProperty;
3756 STATSTG* currentReturnStruct = rgelt;
3757 ULONG objectFetched = 0;
3758 ULONG currentSearchNode;
3761 * Perform a sanity check on the parameters.
3763 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3764 return E_INVALIDARG;
3767 * To avoid the special case, get another pointer to a ULONG value if
3768 * the caller didn't supply one.
3770 if (pceltFetched==0)
3771 pceltFetched = &objectFetched;
3774 * Start the iteration, we will iterate until we hit the end of the
3775 * linked list or until we hit the number of items to iterate through
3780 * Start with the node at the top of the stack.
3782 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3784 while ( ( *pceltFetched < celt) &&
3785 ( currentSearchNode!=PROPERTY_NULL) )
3788 * Remove the top node from the stack
3790 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3793 * Read the property from the storage.
3795 StorageImpl_ReadProperty(This->parentStorage,
3800 * Copy the information to the return buffer.
3802 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3807 * Step to the next item in the iteration
3810 currentReturnStruct++;
3813 * Push the next search node in the search stack.
3815 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3818 * continue the iteration.
3820 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3823 if (*pceltFetched == celt)
3830 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3831 IEnumSTATSTG* iface,
3834 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3836 StgProperty currentProperty;
3837 ULONG objectFetched = 0;
3838 ULONG currentSearchNode;
3841 * Start with the node at the top of the stack.
3843 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3845 while ( (objectFetched < celt) &&
3846 (currentSearchNode!=PROPERTY_NULL) )
3849 * Remove the top node from the stack
3851 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3854 * Read the property from the storage.
3856 StorageImpl_ReadProperty(This->parentStorage,
3861 * Step to the next item in the iteration
3866 * Push the next search node in the search stack.
3868 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3871 * continue the iteration.
3873 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3876 if (objectFetched == celt)
3882 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3883 IEnumSTATSTG* iface)
3885 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3887 StgProperty rootProperty;
3888 BOOL readSuccessful;
3891 * Re-initialize the search stack to an empty stack
3893 This->stackSize = 0;
3896 * Read the root property from the storage.
3898 readSuccessful = StorageImpl_ReadProperty(
3899 This->parentStorage,
3900 This->firstPropertyNode,
3905 assert(rootProperty.sizeOfNameString!=0);
3908 * Push the search node in the search stack.
3910 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3916 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3917 IEnumSTATSTG* iface,
3918 IEnumSTATSTG** ppenum)
3920 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3922 IEnumSTATSTGImpl* newClone;
3925 * Perform a sanity check on the parameters.
3928 return E_INVALIDARG;
3930 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3931 This->firstPropertyNode);
3935 * The new clone enumeration must point to the same current node as
3938 newClone->stackSize = This->stackSize ;
3939 newClone->stackMaxSize = This->stackMaxSize ;
3940 newClone->stackToVisit =
3941 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3944 newClone->stackToVisit,
3946 sizeof(ULONG) * newClone->stackSize);
3948 *ppenum = (IEnumSTATSTG*)newClone;
3951 * Don't forget to nail down a reference to the clone before
3954 IEnumSTATSTGImpl_AddRef(*ppenum);
3959 static INT IEnumSTATSTGImpl_FindParentProperty(
3960 IEnumSTATSTGImpl *This,
3961 ULONG childProperty,
3962 StgProperty *currentProperty,
3965 ULONG currentSearchNode;
3969 * To avoid the special case, get another pointer to a ULONG value if
3970 * the caller didn't supply one.
3973 thisNodeId = &foundNode;
3976 * Start with the node at the top of the stack.
3978 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3981 while (currentSearchNode!=PROPERTY_NULL)
3984 * Store the current node in the returned parameters
3986 *thisNodeId = currentSearchNode;
3989 * Remove the top node from the stack
3991 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3994 * Read the property from the storage.
3996 StorageImpl_ReadProperty(
3997 This->parentStorage,
4001 if (currentProperty->previousProperty == childProperty)
4002 return PROPERTY_RELATION_PREVIOUS;
4004 else if (currentProperty->nextProperty == childProperty)
4005 return PROPERTY_RELATION_NEXT;
4007 else if (currentProperty->dirProperty == childProperty)
4008 return PROPERTY_RELATION_DIR;
4011 * Push the next search node in the search stack.
4013 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4016 * continue the iteration.
4018 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4021 return PROPERTY_NULL;
4024 static ULONG IEnumSTATSTGImpl_FindProperty(
4025 IEnumSTATSTGImpl* This,
4026 const OLECHAR* lpszPropName,
4027 StgProperty* currentProperty)
4029 ULONG currentSearchNode;
4032 * Start with the node at the top of the stack.
4034 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4036 while (currentSearchNode!=PROPERTY_NULL)
4039 * Remove the top node from the stack
4041 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4044 * Read the property from the storage.
4046 StorageImpl_ReadProperty(This->parentStorage,
4050 if ( propertyNameCmp(
4051 (const OLECHAR*)currentProperty->name,
4052 (const OLECHAR*)lpszPropName) == 0)
4053 return currentSearchNode;
4056 * Push the next search node in the search stack.
4058 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4061 * continue the iteration.
4063 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4066 return PROPERTY_NULL;
4069 static void IEnumSTATSTGImpl_PushSearchNode(
4070 IEnumSTATSTGImpl* This,
4073 StgProperty rootProperty;
4074 BOOL readSuccessful;
4077 * First, make sure we're not trying to push an unexisting node.
4079 if (nodeToPush==PROPERTY_NULL)
4083 * First push the node to the stack
4085 if (This->stackSize == This->stackMaxSize)
4087 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4089 This->stackToVisit = HeapReAlloc(
4093 sizeof(ULONG) * This->stackMaxSize);
4096 This->stackToVisit[This->stackSize] = nodeToPush;
4100 * Read the root property from the storage.
4102 readSuccessful = StorageImpl_ReadProperty(
4103 This->parentStorage,
4109 assert(rootProperty.sizeOfNameString!=0);
4112 * Push the previous search node in the search stack.
4114 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4118 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4119 IEnumSTATSTGImpl* This,
4124 if (This->stackSize == 0)
4125 return PROPERTY_NULL;
4127 topNode = This->stackToVisit[This->stackSize-1];
4136 * Virtual function table for the IEnumSTATSTGImpl class.
4138 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4140 IEnumSTATSTGImpl_QueryInterface,
4141 IEnumSTATSTGImpl_AddRef,
4142 IEnumSTATSTGImpl_Release,
4143 IEnumSTATSTGImpl_Next,
4144 IEnumSTATSTGImpl_Skip,
4145 IEnumSTATSTGImpl_Reset,
4146 IEnumSTATSTGImpl_Clone
4149 /******************************************************************************
4150 ** IEnumSTATSTGImpl implementation
4153 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4154 StorageImpl* parentStorage,
4155 ULONG firstPropertyNode)
4157 IEnumSTATSTGImpl* newEnumeration;
4159 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4161 if (newEnumeration!=0)
4164 * Set-up the virtual function table and reference count.
4166 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4167 newEnumeration->ref = 0;
4170 * We want to nail-down the reference to the storage in case the
4171 * enumeration out-lives the storage in the client application.
4173 newEnumeration->parentStorage = parentStorage;
4174 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4176 newEnumeration->firstPropertyNode = firstPropertyNode;
4179 * Initialize the search stack
4181 newEnumeration->stackSize = 0;
4182 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4183 newEnumeration->stackToVisit =
4184 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4187 * Make sure the current node of the iterator is the first one.
4189 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4192 return newEnumeration;
4196 * Virtual function table for the Storage32InternalImpl class.
4198 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4200 StorageBaseImpl_QueryInterface,
4201 StorageBaseImpl_AddRef,
4202 StorageBaseImpl_Release,
4203 StorageBaseImpl_CreateStream,
4204 StorageBaseImpl_OpenStream,
4205 StorageImpl_CreateStorage,
4206 StorageBaseImpl_OpenStorage,
4208 StorageImpl_MoveElementTo,
4209 StorageInternalImpl_Commit,
4210 StorageInternalImpl_Revert,
4211 StorageBaseImpl_EnumElements,
4212 StorageImpl_DestroyElement,
4213 StorageBaseImpl_RenameElement,
4214 StorageImpl_SetElementTimes,
4215 StorageBaseImpl_SetClass,
4216 StorageImpl_SetStateBits,
4217 StorageBaseImpl_Stat
4220 /******************************************************************************
4221 ** Storage32InternalImpl implementation
4224 static StorageInternalImpl* StorageInternalImpl_Construct(
4225 StorageImpl* ancestorStorage,
4227 ULONG rootPropertyIndex)
4229 StorageInternalImpl* newStorage;
4232 * Allocate space for the new storage object
4234 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
4238 memset(newStorage, 0, sizeof(StorageInternalImpl));
4241 * Initialize the stream list
4244 list_init(&newStorage->base.strmHead);
4247 * Initialize the virtual function table.
4249 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4250 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4251 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4254 * Keep the ancestor storage pointer and nail a reference to it.
4256 newStorage->base.ancestorStorage = ancestorStorage;
4257 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4260 * Keep the index of the root property set for this storage,
4262 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4270 /******************************************************************************
4271 ** StorageUtl implementation
4274 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4278 memcpy(&tmp, buffer+offset, sizeof(WORD));
4279 *value = le16toh(tmp);
4282 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4284 value = htole16(value);
4285 memcpy(buffer+offset, &value, sizeof(WORD));
4288 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4292 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4293 *value = le32toh(tmp);
4296 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4298 value = htole32(value);
4299 memcpy(buffer+offset, &value, sizeof(DWORD));
4302 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4303 ULARGE_INTEGER* value)
4305 #ifdef WORDS_BIGENDIAN
4308 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4309 value->u.LowPart = htole32(tmp.u.HighPart);
4310 value->u.HighPart = htole32(tmp.u.LowPart);
4312 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4316 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4317 const ULARGE_INTEGER *value)
4319 #ifdef WORDS_BIGENDIAN
4322 tmp.u.LowPart = htole32(value->u.HighPart);
4323 tmp.u.HighPart = htole32(value->u.LowPart);
4324 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4326 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4330 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4332 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4333 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4334 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4336 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4339 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4341 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4342 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4343 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4345 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4348 void StorageUtl_CopyPropertyToSTATSTG(
4349 STATSTG* destination,
4350 StgProperty* source,
4354 * The copy of the string occurs only when the flag is not set
4356 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4357 (source->name == NULL) ||
4358 (source->name[0] == 0) )
4360 destination->pwcsName = 0;
4364 destination->pwcsName =
4365 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4367 strcpyW((LPWSTR)destination->pwcsName, source->name);
4370 switch (source->propertyType)
4372 case PROPTYPE_STORAGE:
4374 destination->type = STGTY_STORAGE;
4376 case PROPTYPE_STREAM:
4377 destination->type = STGTY_STREAM;
4380 destination->type = STGTY_STREAM;
4384 destination->cbSize = source->size;
4386 currentReturnStruct->mtime = {0}; TODO
4387 currentReturnStruct->ctime = {0};
4388 currentReturnStruct->atime = {0};
4390 destination->grfMode = 0;
4391 destination->grfLocksSupported = 0;
4392 destination->clsid = source->propertyUniqueID;
4393 destination->grfStateBits = 0;
4394 destination->reserved = 0;
4397 /******************************************************************************
4398 ** BlockChainStream implementation
4401 BlockChainStream* BlockChainStream_Construct(
4402 StorageImpl* parentStorage,
4403 ULONG* headOfStreamPlaceHolder,
4404 ULONG propertyIndex)
4406 BlockChainStream* newStream;
4409 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4411 newStream->parentStorage = parentStorage;
4412 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4413 newStream->ownerPropertyIndex = propertyIndex;
4414 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4415 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4416 newStream->numBlocks = 0;
4418 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4420 while (blockIndex != BLOCK_END_OF_CHAIN)
4422 newStream->numBlocks++;
4423 newStream->tailIndex = blockIndex;
4425 if(FAILED(StorageImpl_GetNextBlockInChain(
4430 HeapFree(GetProcessHeap(), 0, newStream);
4438 void BlockChainStream_Destroy(BlockChainStream* This)
4440 HeapFree(GetProcessHeap(), 0, This);
4443 /******************************************************************************
4444 * BlockChainStream_GetHeadOfChain
4446 * Returns the head of this stream chain.
4447 * Some special chains don't have properties, their heads are kept in
4448 * This->headOfStreamPlaceHolder.
4451 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4453 StgProperty chainProperty;
4454 BOOL readSuccessful;
4456 if (This->headOfStreamPlaceHolder != 0)
4457 return *(This->headOfStreamPlaceHolder);
4459 if (This->ownerPropertyIndex != PROPERTY_NULL)
4461 readSuccessful = StorageImpl_ReadProperty(
4462 This->parentStorage,
4463 This->ownerPropertyIndex,
4468 return chainProperty.startingBlock;
4472 return BLOCK_END_OF_CHAIN;
4475 /******************************************************************************
4476 * BlockChainStream_GetCount
4478 * Returns the number of blocks that comprises this chain.
4479 * This is not the size of the stream as the last block may not be full!
4482 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4487 blockIndex = BlockChainStream_GetHeadOfChain(This);
4489 while (blockIndex != BLOCK_END_OF_CHAIN)
4493 if(FAILED(StorageImpl_GetNextBlockInChain(
4494 This->parentStorage,
4503 /******************************************************************************
4504 * BlockChainStream_ReadAt
4506 * Reads a specified number of bytes from this chain at the specified offset.
4507 * bytesRead may be NULL.
4508 * Failure will be returned if the specified number of bytes has not been read.
4510 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4511 ULARGE_INTEGER offset,
4516 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4517 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4518 ULONG bytesToReadInBuffer;
4521 BYTE* bigBlockBuffer;
4524 * Find the first block in the stream that contains part of the buffer.
4526 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4527 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4528 (blockNoInSequence < This->lastBlockNoInSequence) )
4530 blockIndex = BlockChainStream_GetHeadOfChain(This);
4531 This->lastBlockNoInSequence = blockNoInSequence;
4535 ULONG temp = blockNoInSequence;
4537 blockIndex = This->lastBlockNoInSequenceIndex;
4538 blockNoInSequence -= This->lastBlockNoInSequence;
4539 This->lastBlockNoInSequence = temp;
4542 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4544 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4545 return STG_E_DOCFILECORRUPT;
4546 blockNoInSequence--;
4549 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4550 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4552 This->lastBlockNoInSequenceIndex = blockIndex;
4555 * Start reading the buffer.
4558 bufferWalker = buffer;
4560 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4563 * Calculate how many bytes we can copy from this big block.
4565 bytesToReadInBuffer =
4566 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4569 * Copy those bytes to the buffer
4572 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4573 if (!bigBlockBuffer)
4574 return STG_E_READFAULT;
4576 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4578 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4581 * Step to the next big block.
4583 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4584 return STG_E_DOCFILECORRUPT;
4586 bufferWalker += bytesToReadInBuffer;
4587 size -= bytesToReadInBuffer;
4588 *bytesRead += bytesToReadInBuffer;
4589 offsetInBlock = 0; /* There is no offset on the next block */
4593 return (size == 0) ? S_OK : STG_E_READFAULT;
4596 /******************************************************************************
4597 * BlockChainStream_WriteAt
4599 * Writes the specified number of bytes to this chain at the specified offset.
4600 * bytesWritten may be NULL.
4601 * Will fail if not all specified number of bytes have been written.
4603 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4604 ULARGE_INTEGER offset,
4607 ULONG* bytesWritten)
4609 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4610 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4613 const BYTE* bufferWalker;
4614 BYTE* bigBlockBuffer;
4617 * Find the first block in the stream that contains part of the buffer.
4619 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4620 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4621 (blockNoInSequence < This->lastBlockNoInSequence) )
4623 blockIndex = BlockChainStream_GetHeadOfChain(This);
4624 This->lastBlockNoInSequence = blockNoInSequence;
4628 ULONG temp = blockNoInSequence;
4630 blockIndex = This->lastBlockNoInSequenceIndex;
4631 blockNoInSequence -= This->lastBlockNoInSequence;
4632 This->lastBlockNoInSequence = temp;
4635 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4637 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4639 return STG_E_DOCFILECORRUPT;
4640 blockNoInSequence--;
4643 This->lastBlockNoInSequenceIndex = blockIndex;
4645 /* BlockChainStream_SetSize should have already been called to ensure we have
4646 * enough blocks in the chain to write into */
4647 if (blockIndex == BLOCK_END_OF_CHAIN)
4649 ERR("not enough blocks in chain to write data\n");
4650 return STG_E_DOCFILECORRUPT;
4654 * Here, I'm casting away the constness on the buffer variable
4655 * This is OK since we don't intend to modify that buffer.
4658 bufferWalker = (const BYTE*)buffer;
4660 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4663 * Calculate how many bytes we can copy from this big block.
4666 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4669 * Copy those bytes to the buffer
4671 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4673 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4675 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4678 * Step to the next big block.
4680 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4682 return STG_E_DOCFILECORRUPT;
4683 bufferWalker += bytesToWrite;
4684 size -= bytesToWrite;
4685 *bytesWritten += bytesToWrite;
4686 offsetInBlock = 0; /* There is no offset on the next block */
4689 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4692 /******************************************************************************
4693 * BlockChainStream_Shrink
4695 * Shrinks this chain in the big block depot.
4697 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4698 ULARGE_INTEGER newSize)
4700 ULONG blockIndex, extraBlock;
4705 * Reset the last accessed block cache.
4707 This->lastBlockNoInSequence = 0xFFFFFFFF;
4708 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4711 * Figure out how many blocks are needed to contain the new size
4713 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4715 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4718 blockIndex = BlockChainStream_GetHeadOfChain(This);
4721 * Go to the new end of chain
4723 while (count < numBlocks)
4725 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4731 /* Get the next block before marking the new end */
4732 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4736 /* Mark the new end of chain */
4737 StorageImpl_SetNextBlockInChain(
4738 This->parentStorage,
4740 BLOCK_END_OF_CHAIN);
4742 This->tailIndex = blockIndex;
4743 This->numBlocks = numBlocks;
4746 * Mark the extra blocks as free
4748 while (extraBlock != BLOCK_END_OF_CHAIN)
4750 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4753 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4754 extraBlock = blockIndex;
4760 /******************************************************************************
4761 * BlockChainStream_Enlarge
4763 * Grows this chain in the big block depot.
4765 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4766 ULARGE_INTEGER newSize)
4768 ULONG blockIndex, currentBlock;
4770 ULONG oldNumBlocks = 0;
4772 blockIndex = BlockChainStream_GetHeadOfChain(This);
4775 * Empty chain. Create the head.
4777 if (blockIndex == BLOCK_END_OF_CHAIN)
4779 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4780 StorageImpl_SetNextBlockInChain(This->parentStorage,
4782 BLOCK_END_OF_CHAIN);
4784 if (This->headOfStreamPlaceHolder != 0)
4786 *(This->headOfStreamPlaceHolder) = blockIndex;
4790 StgProperty chainProp;
4791 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4793 StorageImpl_ReadProperty(
4794 This->parentStorage,
4795 This->ownerPropertyIndex,
4798 chainProp.startingBlock = blockIndex;
4800 StorageImpl_WriteProperty(
4801 This->parentStorage,
4802 This->ownerPropertyIndex,
4806 This->tailIndex = blockIndex;
4807 This->numBlocks = 1;
4811 * Figure out how many blocks are needed to contain this stream
4813 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4815 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4819 * Go to the current end of chain
4821 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4823 currentBlock = blockIndex;
4825 while (blockIndex != BLOCK_END_OF_CHAIN)
4828 currentBlock = blockIndex;
4830 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4835 This->tailIndex = currentBlock;
4838 currentBlock = This->tailIndex;
4839 oldNumBlocks = This->numBlocks;
4842 * Add new blocks to the chain
4844 if (oldNumBlocks < newNumBlocks)
4846 while (oldNumBlocks < newNumBlocks)
4848 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4850 StorageImpl_SetNextBlockInChain(
4851 This->parentStorage,
4855 StorageImpl_SetNextBlockInChain(
4856 This->parentStorage,
4858 BLOCK_END_OF_CHAIN);
4860 currentBlock = blockIndex;
4864 This->tailIndex = blockIndex;
4865 This->numBlocks = newNumBlocks;
4871 /******************************************************************************
4872 * BlockChainStream_SetSize
4874 * Sets the size of this stream. The big block depot will be updated.
4875 * The file will grow if we grow the chain.
4877 * TODO: Free the actual blocks in the file when we shrink the chain.
4878 * Currently, the blocks are still in the file. So the file size
4879 * doesn't shrink even if we shrink streams.
4881 BOOL BlockChainStream_SetSize(
4882 BlockChainStream* This,
4883 ULARGE_INTEGER newSize)
4885 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4887 if (newSize.u.LowPart == size.u.LowPart)
4890 if (newSize.u.LowPart < size.u.LowPart)
4892 BlockChainStream_Shrink(This, newSize);
4896 BlockChainStream_Enlarge(This, newSize);
4902 /******************************************************************************
4903 * BlockChainStream_GetSize
4905 * Returns the size of this chain.
4906 * Will return the block count if this chain doesn't have a property.
4908 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4910 StgProperty chainProperty;
4912 if(This->headOfStreamPlaceHolder == NULL)
4915 * This chain is a data stream read the property and return
4916 * the appropriate size
4918 StorageImpl_ReadProperty(
4919 This->parentStorage,
4920 This->ownerPropertyIndex,
4923 return chainProperty.size;
4928 * this chain is a chain that does not have a property, figure out the
4929 * size by making the product number of used blocks times the
4932 ULARGE_INTEGER result;
4933 result.u.HighPart = 0;
4936 BlockChainStream_GetCount(This) *
4937 This->parentStorage->bigBlockSize;
4943 /******************************************************************************
4944 ** SmallBlockChainStream implementation
4947 SmallBlockChainStream* SmallBlockChainStream_Construct(
4948 StorageImpl* parentStorage,
4949 ULONG propertyIndex)
4951 SmallBlockChainStream* newStream;
4953 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4955 newStream->parentStorage = parentStorage;
4956 newStream->ownerPropertyIndex = propertyIndex;
4961 void SmallBlockChainStream_Destroy(
4962 SmallBlockChainStream* This)
4964 HeapFree(GetProcessHeap(), 0, This);
4967 /******************************************************************************
4968 * SmallBlockChainStream_GetHeadOfChain
4970 * Returns the head of this chain of small blocks.
4972 static ULONG SmallBlockChainStream_GetHeadOfChain(
4973 SmallBlockChainStream* This)
4975 StgProperty chainProperty;
4976 BOOL readSuccessful;
4978 if (This->ownerPropertyIndex)
4980 readSuccessful = StorageImpl_ReadProperty(
4981 This->parentStorage,
4982 This->ownerPropertyIndex,
4987 return chainProperty.startingBlock;
4992 return BLOCK_END_OF_CHAIN;
4995 /******************************************************************************
4996 * SmallBlockChainStream_GetNextBlockInChain
4998 * Returns the index of the next small block in this chain.
5001 * - BLOCK_END_OF_CHAIN: end of this chain
5002 * - BLOCK_UNUSED: small block 'blockIndex' is free
5004 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5005 SmallBlockChainStream* This,
5007 ULONG* nextBlockInChain)
5009 ULARGE_INTEGER offsetOfBlockInDepot;
5014 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5016 offsetOfBlockInDepot.u.HighPart = 0;
5017 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5020 * Read those bytes in the buffer from the small block file.
5022 res = BlockChainStream_ReadAt(
5023 This->parentStorage->smallBlockDepotChain,
5024 offsetOfBlockInDepot,
5031 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5038 /******************************************************************************
5039 * SmallBlockChainStream_SetNextBlockInChain
5041 * Writes the index of the next block of the specified block in the small
5043 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5044 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5046 static void SmallBlockChainStream_SetNextBlockInChain(
5047 SmallBlockChainStream* This,
5051 ULARGE_INTEGER offsetOfBlockInDepot;
5055 offsetOfBlockInDepot.u.HighPart = 0;
5056 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5058 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5061 * Read those bytes in the buffer from the small block file.
5063 BlockChainStream_WriteAt(
5064 This->parentStorage->smallBlockDepotChain,
5065 offsetOfBlockInDepot,
5071 /******************************************************************************
5072 * SmallBlockChainStream_FreeBlock
5074 * Flag small block 'blockIndex' as free in the small block depot.
5076 static void SmallBlockChainStream_FreeBlock(
5077 SmallBlockChainStream* This,
5080 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5083 /******************************************************************************
5084 * SmallBlockChainStream_GetNextFreeBlock
5086 * Returns the index of a free small block. The small block depot will be
5087 * enlarged if necessary. The small block chain will also be enlarged if
5090 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5091 SmallBlockChainStream* This)
5093 ULARGE_INTEGER offsetOfBlockInDepot;
5096 ULONG blockIndex = 0;
5097 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5099 ULONG smallBlocksPerBigBlock;
5101 offsetOfBlockInDepot.u.HighPart = 0;
5104 * Scan the small block depot for a free block
5106 while (nextBlockIndex != BLOCK_UNUSED)
5108 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5110 res = BlockChainStream_ReadAt(
5111 This->parentStorage->smallBlockDepotChain,
5112 offsetOfBlockInDepot,
5118 * If we run out of space for the small block depot, enlarge it
5122 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5124 if (nextBlockIndex != BLOCK_UNUSED)
5130 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5132 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5133 ULONG nextBlock, newsbdIndex;
5134 BYTE* smallBlockDepot;
5136 nextBlock = sbdIndex;
5137 while (nextBlock != BLOCK_END_OF_CHAIN)
5139 sbdIndex = nextBlock;
5140 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5143 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5144 if (sbdIndex != BLOCK_END_OF_CHAIN)
5145 StorageImpl_SetNextBlockInChain(
5146 This->parentStorage,
5150 StorageImpl_SetNextBlockInChain(
5151 This->parentStorage,
5153 BLOCK_END_OF_CHAIN);
5156 * Initialize all the small blocks to free
5159 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
5161 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5162 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
5167 * We have just created the small block depot.
5169 StgProperty rootProp;
5173 * Save it in the header
5175 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5176 StorageImpl_SaveFileHeader(This->parentStorage);
5179 * And allocate the first big block that will contain small blocks
5182 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5184 StorageImpl_SetNextBlockInChain(
5185 This->parentStorage,
5187 BLOCK_END_OF_CHAIN);
5189 StorageImpl_ReadProperty(
5190 This->parentStorage,
5191 This->parentStorage->base.rootPropertySetIndex,
5194 rootProp.startingBlock = sbStartIndex;
5195 rootProp.size.u.HighPart = 0;
5196 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5198 StorageImpl_WriteProperty(
5199 This->parentStorage,
5200 This->parentStorage->base.rootPropertySetIndex,
5206 smallBlocksPerBigBlock =
5207 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5210 * Verify if we have to allocate big blocks to contain small blocks
5212 if (blockIndex % smallBlocksPerBigBlock == 0)
5214 StgProperty rootProp;
5215 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5217 StorageImpl_ReadProperty(
5218 This->parentStorage,
5219 This->parentStorage->base.rootPropertySetIndex,
5222 if (rootProp.size.u.LowPart <
5223 (blocksRequired * This->parentStorage->bigBlockSize))
5225 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5227 BlockChainStream_SetSize(
5228 This->parentStorage->smallBlockRootChain,
5231 StorageImpl_WriteProperty(
5232 This->parentStorage,
5233 This->parentStorage->base.rootPropertySetIndex,
5241 /******************************************************************************
5242 * SmallBlockChainStream_ReadAt
5244 * Reads a specified number of bytes from this chain at the specified offset.
5245 * bytesRead may be NULL.
5246 * Failure will be returned if the specified number of bytes has not been read.
5248 HRESULT SmallBlockChainStream_ReadAt(
5249 SmallBlockChainStream* This,
5250 ULARGE_INTEGER offset,
5256 ULARGE_INTEGER offsetInBigBlockFile;
5257 ULONG blockNoInSequence =
5258 offset.u.LowPart / This->parentStorage->smallBlockSize;
5260 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5261 ULONG bytesToReadInBuffer;
5263 ULONG bytesReadFromBigBlockFile;
5267 * This should never happen on a small block file.
5269 assert(offset.u.HighPart==0);
5272 * Find the first block in the stream that contains part of the buffer.
5274 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5276 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5278 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5281 blockNoInSequence--;
5285 * Start reading the buffer.
5288 bufferWalker = buffer;
5290 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5293 * Calculate how many bytes we can copy from this small block.
5295 bytesToReadInBuffer =
5296 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5299 * Calculate the offset of the small block in the small block file.
5301 offsetInBigBlockFile.u.HighPart = 0;
5302 offsetInBigBlockFile.u.LowPart =
5303 blockIndex * This->parentStorage->smallBlockSize;
5305 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5308 * Read those bytes in the buffer from the small block file.
5309 * The small block has already been identified so it shouldn't fail
5310 * unless the file is corrupt.
5312 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5313 offsetInBigBlockFile,
5314 bytesToReadInBuffer,
5316 &bytesReadFromBigBlockFile);
5322 * Step to the next big block.
5324 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5326 return STG_E_DOCFILECORRUPT;
5328 bufferWalker += bytesReadFromBigBlockFile;
5329 size -= bytesReadFromBigBlockFile;
5330 *bytesRead += bytesReadFromBigBlockFile;
5331 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5334 return (size == 0) ? S_OK : STG_E_READFAULT;
5337 /******************************************************************************
5338 * SmallBlockChainStream_WriteAt
5340 * Writes the specified number of bytes to this chain at the specified offset.
5341 * bytesWritten may be NULL.
5342 * Will fail if not all specified number of bytes have been written.
5344 HRESULT SmallBlockChainStream_WriteAt(
5345 SmallBlockChainStream* This,
5346 ULARGE_INTEGER offset,
5349 ULONG* bytesWritten)
5351 ULARGE_INTEGER offsetInBigBlockFile;
5352 ULONG blockNoInSequence =
5353 offset.u.LowPart / This->parentStorage->smallBlockSize;
5355 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5356 ULONG bytesToWriteInBuffer;
5358 ULONG bytesWrittenToBigBlockFile;
5359 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)))
5375 return STG_E_DOCFILECORRUPT;
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 res = BlockChainStream_WriteAt(
5408 This->parentStorage->smallBlockRootChain,
5409 offsetInBigBlockFile,
5410 bytesToWriteInBuffer,
5412 &bytesWrittenToBigBlockFile);
5417 * Step to the next big block.
5419 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5422 bufferWalker += bytesWrittenToBigBlockFile;
5423 size -= bytesWrittenToBigBlockFile;
5424 *bytesWritten += bytesWrittenToBigBlockFile;
5425 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5428 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5431 /******************************************************************************
5432 * SmallBlockChainStream_Shrink
5434 * Shrinks this chain in the small block depot.
5436 static BOOL SmallBlockChainStream_Shrink(
5437 SmallBlockChainStream* This,
5438 ULARGE_INTEGER newSize)
5440 ULONG blockIndex, extraBlock;
5444 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5446 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5449 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5452 * Go to the new end of chain
5454 while (count < numBlocks)
5456 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5463 * If the count is 0, we have a special case, the head of the chain was
5468 StgProperty chainProp;
5470 StorageImpl_ReadProperty(This->parentStorage,
5471 This->ownerPropertyIndex,
5474 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5476 StorageImpl_WriteProperty(This->parentStorage,
5477 This->ownerPropertyIndex,
5481 * We start freeing the chain at the head block.
5483 extraBlock = blockIndex;
5487 /* Get the next block before marking the new end */
5488 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5492 /* Mark the new end of chain */
5493 SmallBlockChainStream_SetNextBlockInChain(
5496 BLOCK_END_OF_CHAIN);
5500 * Mark the extra blocks as free
5502 while (extraBlock != BLOCK_END_OF_CHAIN)
5504 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5507 SmallBlockChainStream_FreeBlock(This, extraBlock);
5508 extraBlock = blockIndex;
5514 /******************************************************************************
5515 * SmallBlockChainStream_Enlarge
5517 * Grows this chain in the small block depot.
5519 static BOOL SmallBlockChainStream_Enlarge(
5520 SmallBlockChainStream* This,
5521 ULARGE_INTEGER newSize)
5523 ULONG blockIndex, currentBlock;
5525 ULONG oldNumBlocks = 0;
5527 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5532 if (blockIndex == BLOCK_END_OF_CHAIN)
5535 StgProperty chainProp;
5537 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5540 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5542 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5545 blockIndex = chainProp.startingBlock;
5546 SmallBlockChainStream_SetNextBlockInChain(
5549 BLOCK_END_OF_CHAIN);
5552 currentBlock = blockIndex;
5555 * Figure out how many blocks are needed to contain this stream
5557 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5559 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5563 * Go to the current end of chain
5565 while (blockIndex != BLOCK_END_OF_CHAIN)
5568 currentBlock = blockIndex;
5569 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5574 * Add new blocks to the chain
5576 while (oldNumBlocks < newNumBlocks)
5578 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5579 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5581 SmallBlockChainStream_SetNextBlockInChain(
5584 BLOCK_END_OF_CHAIN);
5586 currentBlock = blockIndex;
5593 /******************************************************************************
5594 * SmallBlockChainStream_SetSize
5596 * Sets the size of this stream.
5597 * The file will grow if we grow the chain.
5599 * TODO: Free the actual blocks in the file when we shrink the chain.
5600 * Currently, the blocks are still in the file. So the file size
5601 * doesn't shrink even if we shrink streams.
5603 BOOL SmallBlockChainStream_SetSize(
5604 SmallBlockChainStream* This,
5605 ULARGE_INTEGER newSize)
5607 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5609 if (newSize.u.LowPart == size.u.LowPart)
5612 if (newSize.u.LowPart < size.u.LowPart)
5614 SmallBlockChainStream_Shrink(This, newSize);
5618 SmallBlockChainStream_Enlarge(This, newSize);
5624 /******************************************************************************
5625 * SmallBlockChainStream_GetSize
5627 * Returns the size of this chain.
5629 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5631 StgProperty chainProperty;
5633 StorageImpl_ReadProperty(
5634 This->parentStorage,
5635 This->ownerPropertyIndex,
5638 return chainProperty.size;
5641 /******************************************************************************
5642 * StgCreateDocfile [OLE32.@]
5643 * Creates a new compound file storage object
5646 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5647 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5648 * reserved [ ?] unused?, usually 0
5649 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5652 * S_OK if the file was successfully created
5653 * some STG_E_ value if error
5655 * if pwcsName is NULL, create file with new unique name
5656 * the function can returns
5657 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5660 HRESULT WINAPI StgCreateDocfile(
5664 IStorage **ppstgOpen)
5666 StorageImpl* newStorage = 0;
5667 HANDLE hFile = INVALID_HANDLE_VALUE;
5668 HRESULT hr = STG_E_INVALIDFLAG;
5672 DWORD fileAttributes;
5673 WCHAR tempFileName[MAX_PATH];
5675 TRACE("(%s, %x, %d, %p)\n",
5676 debugstr_w(pwcsName), grfMode,
5677 reserved, ppstgOpen);
5680 * Validate the parameters
5683 return STG_E_INVALIDPOINTER;
5685 return STG_E_INVALIDPARAMETER;
5688 * Validate the STGM flags
5690 if ( FAILED( validateSTGM(grfMode) ))
5693 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5694 switch(STGM_ACCESS_MODE(grfMode))
5697 case STGM_READWRITE:
5703 /* if no share mode given then DENY_NONE is the default */
5704 if (STGM_SHARE_MODE(grfMode) == 0)
5705 grfMode |= STGM_SHARE_DENY_NONE;
5707 /* must have at least one access mode */
5708 if (STGM_ACCESS_MODE(grfMode) == 0)
5711 /* in direct mode, can only use SHARE_EXCLUSIVE */
5712 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5715 /* but in transacted mode, any share mode is valid */
5718 * Generate a unique name.
5722 WCHAR tempPath[MAX_PATH];
5723 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5725 memset(tempPath, 0, sizeof(tempPath));
5726 memset(tempFileName, 0, sizeof(tempFileName));
5728 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5731 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5732 pwcsName = tempFileName;
5735 hr = STG_E_INSUFFICIENTMEMORY;
5739 creationMode = TRUNCATE_EXISTING;
5743 creationMode = GetCreationModeFromSTGM(grfMode);
5747 * Interpret the STGM value grfMode
5749 shareMode = GetShareModeFromSTGM(grfMode);
5750 accessMode = GetAccessModeFromSTGM(grfMode);
5752 if (grfMode & STGM_DELETEONRELEASE)
5753 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5755 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5757 if (grfMode & STGM_TRANSACTED)
5758 FIXME("Transacted mode not implemented.\n");
5761 * Initialize the "out" parameter.
5765 hFile = CreateFileW(pwcsName,
5773 if (hFile == INVALID_HANDLE_VALUE)
5775 if(GetLastError() == ERROR_FILE_EXISTS)
5776 hr = STG_E_FILEALREADYEXISTS;
5783 * Allocate and initialize the new IStorage32object.
5785 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5787 if (newStorage == 0)
5789 hr = STG_E_INSUFFICIENTMEMORY;
5793 hr = StorageImpl_Construct(
5804 HeapFree(GetProcessHeap(), 0, newStorage);
5809 * Get an "out" pointer for the caller.
5811 hr = StorageBaseImpl_QueryInterface(
5812 (IStorage*)newStorage,
5813 (REFIID)&IID_IStorage,
5816 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5821 /******************************************************************************
5822 * StgCreateStorageEx [OLE32.@]
5824 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5826 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5827 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5829 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5831 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5832 return STG_E_INVALIDPARAMETER;
5835 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5837 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5838 return STG_E_INVALIDPARAMETER;
5841 if (stgfmt == STGFMT_FILE)
5843 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5844 return STG_E_INVALIDPARAMETER;
5847 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5849 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5850 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5853 ERR("Invalid stgfmt argument\n");
5854 return STG_E_INVALIDPARAMETER;
5857 /******************************************************************************
5858 * StgCreatePropSetStg [OLE32.@]
5860 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5861 IPropertySetStorage **ppPropSetStg)
5865 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5867 hr = STG_E_INVALIDPARAMETER;
5869 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5870 (void**)ppPropSetStg);
5874 /******************************************************************************
5875 * StgOpenStorageEx [OLE32.@]
5877 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5879 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5880 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5882 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5884 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5885 return STG_E_INVALIDPARAMETER;
5891 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5892 return STG_E_INVALIDPARAMETER;
5894 case STGFMT_STORAGE:
5897 case STGFMT_DOCFILE:
5898 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5900 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5901 return STG_E_INVALIDPARAMETER;
5903 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5907 WARN("STGFMT_ANY assuming storage\n");
5911 return STG_E_INVALIDPARAMETER;
5914 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5918 /******************************************************************************
5919 * StgOpenStorage [OLE32.@]
5921 HRESULT WINAPI StgOpenStorage(
5922 const OLECHAR *pwcsName,
5923 IStorage *pstgPriority,
5927 IStorage **ppstgOpen)
5929 StorageImpl* newStorage = 0;
5934 WCHAR fullname[MAX_PATH];
5936 TRACE("(%s, %p, %x, %p, %d, %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 if (GetFileSize(hFile, NULL) < 0x100)
6071 hr = STG_E_FILEALREADYEXISTS;
6076 * Allocate and initialize the new IStorage32object.
6078 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6080 if (newStorage == 0)
6082 hr = STG_E_INSUFFICIENTMEMORY;
6086 /* Initialize the storage */
6087 hr = StorageImpl_Construct(
6098 HeapFree(GetProcessHeap(), 0, newStorage);
6100 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6102 if(hr == STG_E_INVALIDHEADER)
6103 hr = STG_E_FILEALREADYEXISTS;
6107 /* prepare the file name string given in lieu of the root property name */
6108 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6109 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6110 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6113 * Get an "out" pointer for the caller.
6115 hr = StorageBaseImpl_QueryInterface(
6116 (IStorage*)newStorage,
6117 (REFIID)&IID_IStorage,
6121 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6125 /******************************************************************************
6126 * StgCreateDocfileOnILockBytes [OLE32.@]
6128 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6132 IStorage** ppstgOpen)
6134 StorageImpl* newStorage = 0;
6138 * Validate the parameters
6140 if ((ppstgOpen == 0) || (plkbyt == 0))
6141 return STG_E_INVALIDPOINTER;
6144 * Allocate and initialize the new IStorage object.
6146 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6148 if (newStorage == 0)
6149 return STG_E_INSUFFICIENTMEMORY;
6151 hr = StorageImpl_Construct(
6162 HeapFree(GetProcessHeap(), 0, newStorage);
6167 * Get an "out" pointer for the caller.
6169 hr = StorageBaseImpl_QueryInterface(
6170 (IStorage*)newStorage,
6171 (REFIID)&IID_IStorage,
6177 /******************************************************************************
6178 * StgOpenStorageOnILockBytes [OLE32.@]
6180 HRESULT WINAPI StgOpenStorageOnILockBytes(
6182 IStorage *pstgPriority,
6186 IStorage **ppstgOpen)
6188 StorageImpl* newStorage = 0;
6192 * Perform a sanity check
6194 if ((plkbyt == 0) || (ppstgOpen == 0))
6195 return STG_E_INVALIDPOINTER;
6198 * Validate the STGM flags
6200 if ( FAILED( validateSTGM(grfMode) ))
6201 return STG_E_INVALIDFLAG;
6204 * Initialize the "out" parameter.
6209 * Allocate and initialize the new IStorage object.
6211 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6213 if (newStorage == 0)
6214 return STG_E_INSUFFICIENTMEMORY;
6216 hr = StorageImpl_Construct(
6227 HeapFree(GetProcessHeap(), 0, newStorage);
6232 * Get an "out" pointer for the caller.
6234 hr = StorageBaseImpl_QueryInterface(
6235 (IStorage*)newStorage,
6236 (REFIID)&IID_IStorage,
6242 /******************************************************************************
6243 * StgSetTimes [ole32.@]
6244 * StgSetTimes [OLE32.@]
6248 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6249 FILETIME const *patime, FILETIME const *pmtime)
6251 IStorage *stg = NULL;
6254 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6256 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6260 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6261 IStorage_Release(stg);
6267 /******************************************************************************
6268 * StgIsStorageILockBytes [OLE32.@]
6270 * Determines if the ILockBytes contains a storage object.
6272 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6275 ULARGE_INTEGER offset;
6277 offset.u.HighPart = 0;
6278 offset.u.LowPart = 0;
6280 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6282 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6288 /******************************************************************************
6289 * WriteClassStg [OLE32.@]
6291 * This method will store the specified CLSID in the specified storage object
6293 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6298 return E_INVALIDARG;
6300 hRes = IStorage_SetClass(pStg, rclsid);
6305 /***********************************************************************
6306 * ReadClassStg (OLE32.@)
6308 * This method reads the CLSID previously written to a storage object with
6309 * the WriteClassStg.
6312 * pstg [I] IStorage pointer
6313 * pclsid [O] Pointer to where the CLSID is written
6317 * Failure: HRESULT code.
6319 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6324 TRACE("(%p, %p)\n", pstg, pclsid);
6326 if(!pstg || !pclsid)
6327 return E_INVALIDARG;
6330 * read a STATSTG structure (contains the clsid) from the storage
6332 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6335 *pclsid=pstatstg.clsid;
6340 /***********************************************************************
6341 * OleLoadFromStream (OLE32.@)
6343 * This function loads an object from stream
6345 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6349 LPPERSISTSTREAM xstm;
6351 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6353 res=ReadClassStm(pStm,&clsid);
6354 if (!SUCCEEDED(res))
6356 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6357 if (!SUCCEEDED(res))
6359 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6360 if (!SUCCEEDED(res)) {
6361 IUnknown_Release((IUnknown*)*ppvObj);
6364 res=IPersistStream_Load(xstm,pStm);
6365 IPersistStream_Release(xstm);
6366 /* FIXME: all refcounts ok at this point? I think they should be:
6369 * xstm : 0 (released)
6374 /***********************************************************************
6375 * OleSaveToStream (OLE32.@)
6377 * This function saves an object with the IPersistStream interface on it
6378 * to the specified stream.
6380 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6386 TRACE("(%p,%p)\n",pPStm,pStm);
6388 res=IPersistStream_GetClassID(pPStm,&clsid);
6390 if (SUCCEEDED(res)){
6392 res=WriteClassStm(pStm,&clsid);
6396 res=IPersistStream_Save(pPStm,pStm,TRUE);
6399 TRACE("Finished Save\n");
6403 /****************************************************************************
6404 * This method validate a STGM parameter that can contain the values below
6406 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6407 * The stgm values contained in 0xffff0000 are bitmasks.
6409 * STGM_DIRECT 0x00000000
6410 * STGM_TRANSACTED 0x00010000
6411 * STGM_SIMPLE 0x08000000
6413 * STGM_READ 0x00000000
6414 * STGM_WRITE 0x00000001
6415 * STGM_READWRITE 0x00000002
6417 * STGM_SHARE_DENY_NONE 0x00000040
6418 * STGM_SHARE_DENY_READ 0x00000030
6419 * STGM_SHARE_DENY_WRITE 0x00000020
6420 * STGM_SHARE_EXCLUSIVE 0x00000010
6422 * STGM_PRIORITY 0x00040000
6423 * STGM_DELETEONRELEASE 0x04000000
6425 * STGM_CREATE 0x00001000
6426 * STGM_CONVERT 0x00020000
6427 * STGM_FAILIFTHERE 0x00000000
6429 * STGM_NOSCRATCH 0x00100000
6430 * STGM_NOSNAPSHOT 0x00200000
6432 static HRESULT validateSTGM(DWORD stgm)
6434 DWORD access = STGM_ACCESS_MODE(stgm);
6435 DWORD share = STGM_SHARE_MODE(stgm);
6436 DWORD create = STGM_CREATE_MODE(stgm);
6438 if (stgm&~STGM_KNOWN_FLAGS)
6440 ERR("unknown flags %08x\n", stgm);
6448 case STGM_READWRITE:
6456 case STGM_SHARE_DENY_NONE:
6457 case STGM_SHARE_DENY_READ:
6458 case STGM_SHARE_DENY_WRITE:
6459 case STGM_SHARE_EXCLUSIVE:
6468 case STGM_FAILIFTHERE:
6475 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6477 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6481 * STGM_CREATE | STGM_CONVERT
6482 * if both are false, STGM_FAILIFTHERE is set to TRUE
6484 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6488 * STGM_NOSCRATCH requires STGM_TRANSACTED
6490 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6494 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6495 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6497 if ( (stgm & STGM_NOSNAPSHOT) &&
6498 (!(stgm & STGM_TRANSACTED) ||
6499 share == STGM_SHARE_EXCLUSIVE ||
6500 share == STGM_SHARE_DENY_WRITE) )
6506 /****************************************************************************
6507 * GetShareModeFromSTGM
6509 * This method will return a share mode flag from a STGM value.
6510 * The STGM value is assumed valid.
6512 static DWORD GetShareModeFromSTGM(DWORD stgm)
6514 switch (STGM_SHARE_MODE(stgm))
6516 case STGM_SHARE_DENY_NONE:
6517 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6518 case STGM_SHARE_DENY_READ:
6519 return FILE_SHARE_WRITE;
6520 case STGM_SHARE_DENY_WRITE:
6521 return FILE_SHARE_READ;
6522 case STGM_SHARE_EXCLUSIVE:
6525 ERR("Invalid share mode!\n");
6530 /****************************************************************************
6531 * GetAccessModeFromSTGM
6533 * This method will return an access mode flag from a STGM value.
6534 * The STGM value is assumed valid.
6536 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6538 switch (STGM_ACCESS_MODE(stgm))
6541 return GENERIC_READ;
6543 case STGM_READWRITE:
6544 return GENERIC_READ | GENERIC_WRITE;
6546 ERR("Invalid access mode!\n");
6551 /****************************************************************************
6552 * GetCreationModeFromSTGM
6554 * This method will return a creation mode flag from a STGM value.
6555 * The STGM value is assumed valid.
6557 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6559 switch(STGM_CREATE_MODE(stgm))
6562 return CREATE_ALWAYS;
6564 FIXME("STGM_CONVERT not implemented!\n");
6566 case STGM_FAILIFTHERE:
6569 ERR("Invalid create mode!\n");
6575 /*************************************************************************
6576 * OLECONVERT_LoadOLE10 [Internal]
6578 * Loads the OLE10 STREAM to memory
6581 * pOleStream [I] The OLESTREAM
6582 * pData [I] Data Structure for the OLESTREAM Data
6586 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6587 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6590 * This function is used by OleConvertOLESTREAMToIStorage only.
6592 * Memory allocated for pData must be freed by the caller
6594 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6597 HRESULT hRes = S_OK;
6601 pData->pData = NULL;
6602 pData->pstrOleObjFileName = (CHAR *) NULL;
6604 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6607 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6608 if(dwSize != sizeof(pData->dwOleID))
6610 hRes = CONVERT10_E_OLESTREAM_GET;
6612 else if(pData->dwOleID != OLESTREAM_ID)
6614 hRes = CONVERT10_E_OLESTREAM_FMT;
6625 /* Get the TypeID...more info needed for this field */
6626 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6627 if(dwSize != sizeof(pData->dwTypeID))
6629 hRes = CONVERT10_E_OLESTREAM_GET;
6634 if(pData->dwTypeID != 0)
6636 /* Get the length of the OleTypeName */
6637 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6638 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6640 hRes = CONVERT10_E_OLESTREAM_GET;
6645 if(pData->dwOleTypeNameLength > 0)
6647 /* Get the OleTypeName */
6648 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6649 if(dwSize != pData->dwOleTypeNameLength)
6651 hRes = CONVERT10_E_OLESTREAM_GET;
6657 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6658 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6660 hRes = CONVERT10_E_OLESTREAM_GET;
6664 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6665 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6666 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6667 if(pData->pstrOleObjFileName)
6669 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6670 if(dwSize != pData->dwOleObjFileNameLength)
6672 hRes = CONVERT10_E_OLESTREAM_GET;
6676 hRes = CONVERT10_E_OLESTREAM_GET;
6681 /* Get the Width of the Metafile */
6682 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6683 if(dwSize != sizeof(pData->dwMetaFileWidth))
6685 hRes = CONVERT10_E_OLESTREAM_GET;
6689 /* Get the Height of the Metafile */
6690 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6691 if(dwSize != sizeof(pData->dwMetaFileHeight))
6693 hRes = CONVERT10_E_OLESTREAM_GET;
6699 /* Get the Length of the Data */
6700 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6701 if(dwSize != sizeof(pData->dwDataLength))
6703 hRes = CONVERT10_E_OLESTREAM_GET;
6707 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6709 if(!bStrem1) /* if it is a second OLE stream data */
6711 pData->dwDataLength -= 8;
6712 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6713 if(dwSize != sizeof(pData->strUnknown))
6715 hRes = CONVERT10_E_OLESTREAM_GET;
6721 if(pData->dwDataLength > 0)
6723 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6725 /* Get Data (ex. IStorage, Metafile, or BMP) */
6728 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6729 if(dwSize != pData->dwDataLength)
6731 hRes = CONVERT10_E_OLESTREAM_GET;
6736 hRes = CONVERT10_E_OLESTREAM_GET;
6745 /*************************************************************************
6746 * OLECONVERT_SaveOLE10 [Internal]
6748 * Saves the OLE10 STREAM From memory
6751 * pData [I] Data Structure for the OLESTREAM Data
6752 * pOleStream [I] The OLESTREAM to save
6756 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6759 * This function is used by OleConvertIStorageToOLESTREAM only.
6762 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6765 HRESULT hRes = S_OK;
6769 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6770 if(dwSize != sizeof(pData->dwOleID))
6772 hRes = CONVERT10_E_OLESTREAM_PUT;
6777 /* Set the TypeID */
6778 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6779 if(dwSize != sizeof(pData->dwTypeID))
6781 hRes = CONVERT10_E_OLESTREAM_PUT;
6785 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6787 /* Set the Length of the OleTypeName */
6788 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6789 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6791 hRes = CONVERT10_E_OLESTREAM_PUT;
6796 if(pData->dwOleTypeNameLength > 0)
6798 /* Set the OleTypeName */
6799 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6800 if(dwSize != pData->dwOleTypeNameLength)
6802 hRes = CONVERT10_E_OLESTREAM_PUT;
6809 /* Set the width of the Metafile */
6810 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6811 if(dwSize != sizeof(pData->dwMetaFileWidth))
6813 hRes = CONVERT10_E_OLESTREAM_PUT;
6819 /* Set the height of the Metafile */
6820 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6821 if(dwSize != sizeof(pData->dwMetaFileHeight))
6823 hRes = CONVERT10_E_OLESTREAM_PUT;
6829 /* Set the length of the Data */
6830 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6831 if(dwSize != sizeof(pData->dwDataLength))
6833 hRes = CONVERT10_E_OLESTREAM_PUT;
6839 if(pData->dwDataLength > 0)
6841 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6842 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6843 if(dwSize != pData->dwDataLength)
6845 hRes = CONVERT10_E_OLESTREAM_PUT;
6853 /*************************************************************************
6854 * OLECONVERT_GetOLE20FromOLE10[Internal]
6856 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6857 * opens it, and copies the content to the dest IStorage for
6858 * OleConvertOLESTREAMToIStorage
6862 * pDestStorage [I] The IStorage to copy the data to
6863 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6864 * nBufferLength [I] The size of the buffer
6873 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6877 IStorage *pTempStorage;
6878 DWORD dwNumOfBytesWritten;
6879 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6880 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6882 /* Create a temp File */
6883 GetTempPathW(MAX_PATH, wstrTempDir);
6884 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6885 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6887 if(hFile != INVALID_HANDLE_VALUE)
6889 /* Write IStorage Data to File */
6890 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6893 /* Open and copy temp storage to the Dest Storage */
6894 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6897 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6898 StorageBaseImpl_Release(pTempStorage);
6900 DeleteFileW(wstrTempFile);
6905 /*************************************************************************
6906 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6908 * Saves the OLE10 STREAM From memory
6911 * pStorage [I] The Src IStorage to copy
6912 * pData [I] The Dest Memory to write to.
6915 * The size in bytes allocated for pData
6918 * Memory allocated for pData must be freed by the caller
6920 * Used by OleConvertIStorageToOLESTREAM only.
6923 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6927 DWORD nDataLength = 0;
6928 IStorage *pTempStorage;
6929 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6930 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6934 /* Create temp Storage */
6935 GetTempPathW(MAX_PATH, wstrTempDir);
6936 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6937 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6941 /* Copy Src Storage to the Temp Storage */
6942 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6943 StorageBaseImpl_Release(pTempStorage);
6945 /* Open Temp Storage as a file and copy to memory */
6946 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6947 if(hFile != INVALID_HANDLE_VALUE)
6949 nDataLength = GetFileSize(hFile, NULL);
6950 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6951 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6954 DeleteFileW(wstrTempFile);
6959 /*************************************************************************
6960 * OLECONVERT_CreateOleStream [Internal]
6962 * Creates the "\001OLE" stream in the IStorage if necessary.
6965 * pStorage [I] Dest storage to create the stream in
6971 * This function is used by OleConvertOLESTREAMToIStorage only.
6973 * This stream is still unknown, MS Word seems to have extra data
6974 * but since the data is stored in the OLESTREAM there should be
6975 * no need to recreate the stream. If the stream is manually
6976 * deleted it will create it with this default data.
6979 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6983 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6984 BYTE pOleStreamHeader [] =
6986 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6987 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6988 0x00, 0x00, 0x00, 0x00
6991 /* Create stream if not present */
6992 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6993 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6997 /* Write default Data */
6998 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6999 IStream_Release(pStream);
7003 /* write a string to a stream, preceded by its length */
7004 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7011 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7012 r = IStream_Write( stm, &len, sizeof(len), NULL);
7017 str = CoTaskMemAlloc( len );
7018 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7019 r = IStream_Write( stm, str, len, NULL);
7020 CoTaskMemFree( str );
7024 /* read a string preceded by its length from a stream */
7025 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7028 DWORD len, count = 0;
7032 r = IStream_Read( stm, &len, sizeof(len), &count );
7035 if( count != sizeof(len) )
7036 return E_OUTOFMEMORY;
7038 TRACE("%d bytes\n",len);
7040 str = CoTaskMemAlloc( len );
7042 return E_OUTOFMEMORY;
7044 r = IStream_Read( stm, str, len, &count );
7049 CoTaskMemFree( str );
7050 return E_OUTOFMEMORY;
7053 TRACE("Read string %s\n",debugstr_an(str,len));
7055 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7056 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7058 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7059 CoTaskMemFree( str );
7067 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7068 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7072 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7074 static const BYTE unknown1[12] =
7075 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7076 0xFF, 0xFF, 0xFF, 0xFF};
7077 static const BYTE unknown2[16] =
7078 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7079 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7081 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7082 debugstr_w(lpszUserType), debugstr_w(szClipName),
7083 debugstr_w(szProgIDName));
7085 /* Create a CompObj stream if it doesn't exist */
7086 r = IStorage_CreateStream(pstg, szwStreamName,
7087 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7091 /* Write CompObj Structure to stream */
7092 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7094 if( SUCCEEDED( r ) )
7095 r = WriteClassStm( pstm, clsid );
7097 if( SUCCEEDED( r ) )
7098 r = STREAM_WriteString( pstm, lpszUserType );
7099 if( SUCCEEDED( r ) )
7100 r = STREAM_WriteString( pstm, szClipName );
7101 if( SUCCEEDED( r ) )
7102 r = STREAM_WriteString( pstm, szProgIDName );
7103 if( SUCCEEDED( r ) )
7104 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7106 IStream_Release( pstm );
7111 /***********************************************************************
7112 * WriteFmtUserTypeStg (OLE32.@)
7114 HRESULT WINAPI WriteFmtUserTypeStg(
7115 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7118 WCHAR szwClipName[0x40];
7119 CLSID clsid = CLSID_NULL;
7120 LPWSTR wstrProgID = NULL;
7123 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7125 /* get the clipboard format name */
7126 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
7129 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7131 /* FIXME: There's room to save a CLSID and its ProgID, but
7132 the CLSID is not looked up in the registry and in all the
7133 tests I wrote it was CLSID_NULL. Where does it come from?
7136 /* get the real program ID. This may fail, but that's fine */
7137 ProgIDFromCLSID(&clsid, &wstrProgID);
7139 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7141 r = STORAGE_WriteCompObj( pstg, &clsid,
7142 lpszUserType, szwClipName, wstrProgID );
7144 CoTaskMemFree(wstrProgID);
7150 /******************************************************************************
7151 * ReadFmtUserTypeStg [OLE32.@]
7153 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7157 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7158 unsigned char unknown1[12];
7159 unsigned char unknown2[16];
7161 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7164 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7166 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7167 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7170 WARN("Failed to open stream r = %08x\n", r);
7174 /* read the various parts of the structure */
7175 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7176 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7178 r = ReadClassStm( stm, &clsid );
7182 r = STREAM_ReadString( stm, &szCLSIDName );
7186 r = STREAM_ReadString( stm, &szOleTypeName );
7190 r = STREAM_ReadString( stm, &szProgIDName );
7194 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7195 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7198 /* ok, success... now we just need to store what we found */
7200 *pcf = RegisterClipboardFormatW( szOleTypeName );
7201 CoTaskMemFree( szOleTypeName );
7203 if( lplpszUserType )
7204 *lplpszUserType = szCLSIDName;
7205 CoTaskMemFree( szProgIDName );
7208 IStream_Release( stm );
7214 /*************************************************************************
7215 * OLECONVERT_CreateCompObjStream [Internal]
7217 * Creates a "\001CompObj" is the destination IStorage if necessary.
7220 * pStorage [I] The dest IStorage to create the CompObj Stream
7222 * strOleTypeName [I] The ProgID
7226 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7229 * This function is used by OleConvertOLESTREAMToIStorage only.
7231 * The stream data is stored in the OLESTREAM and there should be
7232 * no need to recreate the stream. If the stream is manually
7233 * deleted it will attempt to create it by querying the registry.
7237 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7240 HRESULT hStorageRes, hRes = S_OK;
7241 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7242 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7243 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7245 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7246 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7248 /* Initialize the CompObj structure */
7249 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7250 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7251 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7254 /* Create a CompObj stream if it doesn't exist */
7255 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7256 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7257 if(hStorageRes == S_OK)
7259 /* copy the OleTypeName to the compobj struct */
7260 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7261 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7263 /* copy the OleTypeName to the compobj struct */
7264 /* Note: in the test made, these were Identical */
7265 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7266 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7269 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7270 bufferW, OLESTREAM_MAX_STR_LEN );
7271 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7277 /* Get the CLSID Default Name from the Registry */
7278 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7279 if(hErr == ERROR_SUCCESS)
7281 char strTemp[OLESTREAM_MAX_STR_LEN];
7282 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7283 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7284 if(hErr == ERROR_SUCCESS)
7286 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7292 /* Write CompObj Structure to stream */
7293 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7295 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7297 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7298 if(IStorageCompObj.dwCLSIDNameLength > 0)
7300 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7302 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7303 if(IStorageCompObj.dwOleTypeNameLength > 0)
7305 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7307 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7308 if(IStorageCompObj.dwProgIDNameLength > 0)
7310 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7312 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7313 IStream_Release(pStream);
7319 /*************************************************************************
7320 * OLECONVERT_CreateOlePresStream[Internal]
7322 * Creates the "\002OlePres000" Stream with the Metafile data
7325 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7326 * dwExtentX [I] Width of the Metafile
7327 * dwExtentY [I] Height of the Metafile
7328 * pData [I] Metafile data
7329 * dwDataLength [I] Size of the Metafile data
7333 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7336 * This function is used by OleConvertOLESTREAMToIStorage only.
7339 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7343 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7344 BYTE pOlePresStreamHeader [] =
7346 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7347 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7348 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7349 0x00, 0x00, 0x00, 0x00
7352 BYTE pOlePresStreamHeaderEmpty [] =
7354 0x00, 0x00, 0x00, 0x00,
7355 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7356 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7357 0x00, 0x00, 0x00, 0x00
7360 /* Create the OlePres000 Stream */
7361 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7362 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7367 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7369 memset(&OlePres, 0, sizeof(OlePres));
7370 /* Do we have any metafile data to save */
7371 if(dwDataLength > 0)
7373 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7374 nHeaderSize = sizeof(pOlePresStreamHeader);
7378 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7379 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7381 /* Set width and height of the metafile */
7382 OlePres.dwExtentX = dwExtentX;
7383 OlePres.dwExtentY = -dwExtentY;
7385 /* Set Data and Length */
7386 if(dwDataLength > sizeof(METAFILEPICT16))
7388 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7389 OlePres.pData = &(pData[8]);
7391 /* Save OlePres000 Data to Stream */
7392 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7393 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7394 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7395 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7396 if(OlePres.dwSize > 0)
7398 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7400 IStream_Release(pStream);
7404 /*************************************************************************
7405 * OLECONVERT_CreateOle10NativeStream [Internal]
7407 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7410 * pStorage [I] Dest storage to create the stream in
7411 * pData [I] Ole10 Native Data (ex. bmp)
7412 * dwDataLength [I] Size of the Ole10 Native Data
7418 * This function is used by OleConvertOLESTREAMToIStorage only.
7420 * Might need to verify the data and return appropriate error message
7423 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7427 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7429 /* Create the Ole10Native Stream */
7430 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7431 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7435 /* Write info to stream */
7436 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7437 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7438 IStream_Release(pStream);
7443 /*************************************************************************
7444 * OLECONVERT_GetOLE10ProgID [Internal]
7446 * Finds the ProgID (or OleTypeID) from the IStorage
7449 * pStorage [I] The Src IStorage to get the ProgID
7450 * strProgID [I] the ProgID string to get
7451 * dwSize [I] the size of the string
7455 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7458 * This function is used by OleConvertIStorageToOLESTREAM only.
7462 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7466 LARGE_INTEGER iSeekPos;
7467 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7468 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7470 /* Open the CompObj Stream */
7471 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7472 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7476 /*Get the OleType from the CompObj Stream */
7477 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7478 iSeekPos.u.HighPart = 0;
7480 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7481 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7482 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7483 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7484 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7485 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7486 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7488 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7491 IStream_Read(pStream, strProgID, *dwSize, NULL);
7493 IStream_Release(pStream);
7498 LPOLESTR wstrProgID;
7500 /* Get the OleType from the registry */
7501 REFCLSID clsid = &(stat.clsid);
7502 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7503 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7506 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7513 /*************************************************************************
7514 * OLECONVERT_GetOle10PresData [Internal]
7516 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7519 * pStorage [I] Src IStroage
7520 * pOleStream [I] Dest OleStream Mem Struct
7526 * This function is used by OleConvertIStorageToOLESTREAM only.
7528 * Memory allocated for pData must be freed by the caller
7532 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7537 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7539 /* Initialize Default data for OLESTREAM */
7540 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7541 pOleStreamData[0].dwTypeID = 2;
7542 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7543 pOleStreamData[1].dwTypeID = 0;
7544 pOleStreamData[0].dwMetaFileWidth = 0;
7545 pOleStreamData[0].dwMetaFileHeight = 0;
7546 pOleStreamData[0].pData = NULL;
7547 pOleStreamData[1].pData = NULL;
7549 /* Open Ole10Native Stream */
7550 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7551 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7555 /* Read Size and Data */
7556 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7557 if(pOleStreamData->dwDataLength > 0)
7559 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7560 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7562 IStream_Release(pStream);
7568 /*************************************************************************
7569 * OLECONVERT_GetOle20PresData[Internal]
7571 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7574 * pStorage [I] Src IStroage
7575 * pOleStreamData [I] Dest OleStream Mem Struct
7581 * This function is used by OleConvertIStorageToOLESTREAM only.
7583 * Memory allocated for pData must be freed by the caller
7585 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7589 OLECONVERT_ISTORAGE_OLEPRES olePress;
7590 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7592 /* Initialize Default data for OLESTREAM */
7593 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7594 pOleStreamData[0].dwTypeID = 2;
7595 pOleStreamData[0].dwMetaFileWidth = 0;
7596 pOleStreamData[0].dwMetaFileHeight = 0;
7597 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7598 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7599 pOleStreamData[1].dwTypeID = 0;
7600 pOleStreamData[1].dwOleTypeNameLength = 0;
7601 pOleStreamData[1].strOleTypeName[0] = 0;
7602 pOleStreamData[1].dwMetaFileWidth = 0;
7603 pOleStreamData[1].dwMetaFileHeight = 0;
7604 pOleStreamData[1].pData = NULL;
7605 pOleStreamData[1].dwDataLength = 0;
7608 /* Open OlePress000 stream */
7609 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7610 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7613 LARGE_INTEGER iSeekPos;
7614 METAFILEPICT16 MetaFilePict;
7615 static const char strMetafilePictName[] = "METAFILEPICT";
7617 /* Set the TypeID for a Metafile */
7618 pOleStreamData[1].dwTypeID = 5;
7620 /* Set the OleTypeName to Metafile */
7621 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7622 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7624 iSeekPos.u.HighPart = 0;
7625 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7627 /* Get Presentation Data */
7628 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7629 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7630 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7631 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7633 /*Set width and Height */
7634 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7635 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7636 if(olePress.dwSize > 0)
7639 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7641 /* Set MetaFilePict struct */
7642 MetaFilePict.mm = 8;
7643 MetaFilePict.xExt = olePress.dwExtentX;
7644 MetaFilePict.yExt = olePress.dwExtentY;
7645 MetaFilePict.hMF = 0;
7647 /* Get Metafile Data */
7648 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7649 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7650 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7652 IStream_Release(pStream);
7656 /*************************************************************************
7657 * OleConvertOLESTREAMToIStorage [OLE32.@]
7662 * DVTARGETDEVICE paramenter is not handled
7663 * Still unsure of some mem fields for OLE 10 Stream
7664 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7665 * and "\001OLE" streams
7668 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7669 LPOLESTREAM pOleStream,
7671 const DVTARGETDEVICE* ptd)
7675 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7677 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7679 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7683 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7686 if(pstg == NULL || pOleStream == NULL)
7688 hRes = E_INVALIDARG;
7693 /* Load the OLESTREAM to Memory */
7694 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7699 /* Load the OLESTREAM to Memory (part 2)*/
7700 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7706 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7708 /* Do we have the IStorage Data in the OLESTREAM */
7709 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7711 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7712 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7716 /* It must be an original OLE 1.0 source */
7717 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7722 /* It must be an original OLE 1.0 source */
7723 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7726 /* Create CompObj Stream if necessary */
7727 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7730 /*Create the Ole Stream if necessary */
7731 OLECONVERT_CreateOleStream(pstg);
7736 /* Free allocated memory */
7737 for(i=0; i < 2; i++)
7739 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7740 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7741 pOleStreamData[i].pstrOleObjFileName = NULL;
7746 /*************************************************************************
7747 * OleConvertIStorageToOLESTREAM [OLE32.@]
7754 * Still unsure of some mem fields for OLE 10 Stream
7755 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7756 * and "\001OLE" streams.
7759 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7761 LPOLESTREAM pOleStream)
7764 HRESULT hRes = S_OK;
7766 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7767 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7769 TRACE("%p %p\n", pstg, pOleStream);
7771 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7773 if(pstg == NULL || pOleStream == NULL)
7775 hRes = E_INVALIDARG;
7779 /* Get the ProgID */
7780 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7781 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7785 /* Was it originally Ole10 */
7786 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7789 IStream_Release(pStream);
7790 /* Get Presentation Data for Ole10Native */
7791 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7795 /* Get Presentation Data (OLE20) */
7796 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7799 /* Save OLESTREAM */
7800 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7803 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7808 /* Free allocated memory */
7809 for(i=0; i < 2; i++)
7811 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7817 /***********************************************************************
7818 * GetConvertStg (OLE32.@)
7820 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7821 FIXME("unimplemented stub!\n");
7825 /******************************************************************************
7826 * StgIsStorageFile [OLE32.@]
7827 * Verify if the file contains a storage object
7833 * S_OK if file has magic bytes as a storage object
7834 * S_FALSE if file is not storage
7837 StgIsStorageFile(LPCOLESTR fn)
7843 TRACE("%s\n", debugstr_w(fn));
7844 hf = CreateFileW(fn, GENERIC_READ,
7845 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7846 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7848 if (hf == INVALID_HANDLE_VALUE)
7849 return STG_E_FILENOTFOUND;
7851 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7853 WARN(" unable to read file\n");
7860 if (bytes_read != 8) {
7861 WARN(" too short\n");
7865 if (!memcmp(magic,STORAGE_magic,8)) {
7870 WARN(" -> Invalid header.\n");
7874 /***********************************************************************
7875 * WriteClassStm (OLE32.@)
7877 * Writes a CLSID to a stream.
7880 * pStm [I] Stream to write to.
7881 * rclsid [I] CLSID to write.
7885 * Failure: HRESULT code.
7887 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7889 TRACE("(%p,%p)\n",pStm,rclsid);
7891 if (!pStm || !rclsid)
7892 return E_INVALIDARG;
7894 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7897 /***********************************************************************
7898 * ReadClassStm (OLE32.@)
7900 * Reads a CLSID from a stream.
7903 * pStm [I] Stream to read from.
7904 * rclsid [O] CLSID to read.
7908 * Failure: HRESULT code.
7910 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7915 TRACE("(%p,%p)\n",pStm,pclsid);
7917 if (!pStm || !pclsid)
7918 return E_INVALIDARG;
7920 /* clear the output args */
7921 memcpy(pclsid, &CLSID_NULL, sizeof(*pclsid));
7923 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7928 if (nbByte != sizeof(CLSID))
7929 return STG_E_READFAULT;