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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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);
63 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
64 #define OLESTREAM_ID 0x501
65 #define OLESTREAM_MAX_STR_LEN 255
67 static const char rootPropertyName[] = "Root Entry";
70 /* OLESTREAM memory structure to use for Get and Put Routines */
71 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
76 DWORD dwOleTypeNameLength;
77 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
78 CHAR *pstrOleObjFileName;
79 DWORD dwOleObjFileNameLength;
80 DWORD dwMetaFileWidth;
81 DWORD dwMetaFileHeight;
82 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
85 }OLECONVERT_OLESTREAM_DATA;
87 /* CompObj Stream structure */
88 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
93 DWORD dwCLSIDNameLength;
94 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
95 DWORD dwOleTypeNameLength;
96 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
97 DWORD dwProgIDNameLength;
98 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
100 }OLECONVERT_ISTORAGE_COMPOBJ;
103 /* Ole Presention Stream structure */
104 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
112 }OLECONVERT_ISTORAGE_OLEPRES;
116 /***********************************************************************
117 * Forward declaration of internal functions used by the method DestroyElement
119 static HRESULT deleteStorageProperty(
120 StorageImpl *parentStorage,
121 ULONG foundPropertyIndexToDelete,
122 StgProperty propertyToDelete);
124 static HRESULT deleteStreamProperty(
125 StorageImpl *parentStorage,
126 ULONG foundPropertyIndexToDelete,
127 StgProperty propertyToDelete);
129 static HRESULT findPlaceholder(
130 StorageImpl *storage,
131 ULONG propertyIndexToStore,
132 ULONG storagePropertyIndex,
135 static HRESULT adjustPropertyChain(
137 StgProperty propertyToDelete,
138 StgProperty parentProperty,
139 ULONG parentPropertyId,
142 /***********************************************************************
143 * Declaration of the functions used to manipulate StgProperty
146 static ULONG getFreeProperty(
147 StorageImpl *storage);
149 static void updatePropertyChain(
150 StorageImpl *storage,
151 ULONG newPropertyIndex,
152 StgProperty newProperty);
154 static LONG propertyNameCmp(
155 const OLECHAR *newProperty,
156 const OLECHAR *currentProperty);
159 /***********************************************************************
160 * Declaration of miscellaneous functions...
162 static HRESULT validateSTGM(DWORD stgmValue);
164 static DWORD GetShareModeFromSTGM(DWORD stgm);
165 static DWORD GetAccessModeFromSTGM(DWORD stgm);
166 static DWORD GetCreationModeFromSTGM(DWORD stgm);
168 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
172 /************************************************************************
173 ** Storage32BaseImpl implementatiion
176 /************************************************************************
177 * Storage32BaseImpl_QueryInterface (IUnknown)
179 * This method implements the common QueryInterface for all IStorage32
180 * implementations contained in this file.
182 * See Windows documentation for more details on IUnknown methods.
184 HRESULT WINAPI StorageBaseImpl_QueryInterface(
189 StorageBaseImpl *This = (StorageBaseImpl *)iface;
191 * Perform a sanity check on the parameters.
193 if ( (This==0) || (ppvObject==0) )
197 * Initialize the return parameter.
202 * Compare the riid with the interface IDs implemented by this object.
204 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
206 *ppvObject = (IStorage*)This;
208 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
210 *ppvObject = (IStorage*)This;
212 else if (memcmp(&IID_IPropertySetStorage, riid, sizeof(IID_IPropertySetStorage)) == 0)
214 *ppvObject = (IStorage*)&This->pssVtbl;
218 * Check that we obtained an interface.
221 return E_NOINTERFACE;
224 * Query Interface always increases the reference count by one when it is
227 IStorage_AddRef(iface);
232 /************************************************************************
233 * Storage32BaseImpl_AddRef (IUnknown)
235 * This method implements the common AddRef for all IStorage32
236 * implementations contained in this file.
238 * See Windows documentation for more details on IUnknown methods.
240 ULONG WINAPI StorageBaseImpl_AddRef(
243 StorageBaseImpl *This = (StorageBaseImpl *)iface;
244 ULONG ref = InterlockedIncrement(&This->ref);
246 TRACE("(%p) AddRef to %ld\n", This, ref);
251 /************************************************************************
252 * Storage32BaseImpl_Release (IUnknown)
254 * This method implements the common Release for all IStorage32
255 * implementations contained in this file.
257 * See Windows documentation for more details on IUnknown methods.
259 ULONG WINAPI StorageBaseImpl_Release(
262 StorageBaseImpl *This = (StorageBaseImpl *)iface;
264 * Decrease the reference count on this object.
266 ULONG ref = InterlockedDecrement(&This->ref);
268 TRACE("(%p) ReleaseRef to %ld\n", This, ref);
271 * If the reference count goes down to 0, perform suicide.
276 * Since we are using a system of base-classes, we want to call the
277 * destructor of the appropriate derived class. To do this, we are
278 * using virtual functions to implement the destructor.
280 This->v_destructor(This);
286 /************************************************************************
287 * Storage32BaseImpl_OpenStream (IStorage)
289 * This method will open the specified stream object from the current storage.
291 * See Windows documentation for more details on IStorage methods.
293 HRESULT WINAPI StorageBaseImpl_OpenStream(
295 const OLECHAR* pwcsName, /* [string][in] */
296 void* reserved1, /* [unique][in] */
297 DWORD grfMode, /* [in] */
298 DWORD reserved2, /* [in] */
299 IStream** ppstm) /* [out] */
301 StorageBaseImpl *This = (StorageBaseImpl *)iface;
302 IEnumSTATSTGImpl* propertyEnumeration;
303 StgStreamImpl* newStream;
304 StgProperty currentProperty;
305 ULONG foundPropertyIndex;
306 HRESULT res = STG_E_UNKNOWN;
307 DWORD parent_grfMode;
309 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
310 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
313 * Perform a sanity check on the parameters.
315 if ( (pwcsName==NULL) || (ppstm==0) )
322 * Initialize the out parameter
327 * Validate the STGM flags
329 if ( FAILED( validateSTGM(grfMode) ))
331 res = STG_E_INVALIDFLAG;
338 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
339 (grfMode & STGM_DELETEONRELEASE) ||
340 (grfMode & STGM_TRANSACTED) )
342 res = STG_E_INVALIDFUNCTION;
347 * Check that we're compatible with the parent's storage mode
349 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
350 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
352 res = STG_E_ACCESSDENIED;
357 * Create a property enumeration to search the properties
359 propertyEnumeration = IEnumSTATSTGImpl_Construct(
360 This->ancestorStorage,
361 This->rootPropertySetIndex);
364 * Search the enumeration for the property with the given name
366 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
372 * Delete the property enumeration since we don't need it anymore
374 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
377 * If it was found, construct the stream object and return a pointer to it.
379 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
380 (currentProperty.propertyType==PROPTYPE_STREAM) )
382 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
386 newStream->grfMode = grfMode;
387 *ppstm = (IStream*)newStream;
390 * Since we are returning a pointer to the interface, we have to
391 * nail down the reference.
393 IStream_AddRef(*ppstm);
403 res = STG_E_FILENOTFOUND;
407 TRACE("<-- IStream %p\n", *ppstm);
408 TRACE("<-- %08lx\n", res);
412 /************************************************************************
413 * Storage32BaseImpl_OpenStorage (IStorage)
415 * This method will open a new storage object from the current storage.
417 * See Windows documentation for more details on IStorage methods.
419 HRESULT WINAPI StorageBaseImpl_OpenStorage(
421 const OLECHAR* pwcsName, /* [string][unique][in] */
422 IStorage* pstgPriority, /* [unique][in] */
423 DWORD grfMode, /* [in] */
424 SNB snbExclude, /* [unique][in] */
425 DWORD reserved, /* [in] */
426 IStorage** ppstg) /* [out] */
428 StorageBaseImpl *This = (StorageBaseImpl *)iface;
429 StorageInternalImpl* newStorage;
430 IEnumSTATSTGImpl* propertyEnumeration;
431 StgProperty currentProperty;
432 ULONG foundPropertyIndex;
433 HRESULT res = STG_E_UNKNOWN;
434 DWORD parent_grfMode;
436 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
437 iface, debugstr_w(pwcsName), pstgPriority,
438 grfMode, snbExclude, reserved, ppstg);
441 * Perform a sanity check on the parameters.
443 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
450 if (snbExclude != NULL)
452 res = STG_E_INVALIDPARAMETER;
457 * Validate the STGM flags
459 if ( FAILED( validateSTGM(grfMode) ))
461 res = STG_E_INVALIDFLAG;
468 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
469 (grfMode & STGM_DELETEONRELEASE) ||
470 (grfMode & STGM_PRIORITY) )
472 res = STG_E_INVALIDFUNCTION;
477 * Check that we're compatible with the parent's storage mode
479 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
480 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
482 res = STG_E_ACCESSDENIED;
487 * Initialize the out parameter
492 * Create a property enumeration to search the properties
494 propertyEnumeration = IEnumSTATSTGImpl_Construct(
495 This->ancestorStorage,
496 This->rootPropertySetIndex);
499 * Search the enumeration for the property with the given name
501 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
507 * Delete the property enumeration since we don't need it anymore
509 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
512 * If it was found, construct the stream object and return a pointer to it.
514 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
515 (currentProperty.propertyType==PROPTYPE_STORAGE) )
518 * Construct a new Storage object
520 newStorage = StorageInternalImpl_Construct(
521 This->ancestorStorage,
527 *ppstg = (IStorage*)newStorage;
530 * Since we are returning a pointer to the interface,
531 * we have to nail down the reference.
533 StorageBaseImpl_AddRef(*ppstg);
539 res = STG_E_INSUFFICIENTMEMORY;
543 res = STG_E_FILENOTFOUND;
546 TRACE("<-- %08lx\n", res);
550 /************************************************************************
551 * Storage32BaseImpl_EnumElements (IStorage)
553 * This method will create an enumerator object that can be used to
554 * retrieve informatino about all the properties in the storage object.
556 * See Windows documentation for more details on IStorage methods.
558 HRESULT WINAPI StorageBaseImpl_EnumElements(
560 DWORD reserved1, /* [in] */
561 void* reserved2, /* [size_is][unique][in] */
562 DWORD reserved3, /* [in] */
563 IEnumSTATSTG** ppenum) /* [out] */
565 StorageBaseImpl *This = (StorageBaseImpl *)iface;
566 IEnumSTATSTGImpl* newEnum;
568 TRACE("(%p, %ld, %p, %ld, %p)\n",
569 iface, reserved1, reserved2, reserved3, ppenum);
572 * Perform a sanity check on the parameters.
574 if ( (This==0) || (ppenum==0))
578 * Construct the enumerator.
580 newEnum = IEnumSTATSTGImpl_Construct(
581 This->ancestorStorage,
582 This->rootPropertySetIndex);
586 *ppenum = (IEnumSTATSTG*)newEnum;
589 * Don't forget to nail down a reference to the new object before
592 IEnumSTATSTG_AddRef(*ppenum);
597 return E_OUTOFMEMORY;
600 /************************************************************************
601 * Storage32BaseImpl_Stat (IStorage)
603 * This method will retrieve information about this storage object.
605 * See Windows documentation for more details on IStorage methods.
607 HRESULT WINAPI StorageBaseImpl_Stat(
609 STATSTG* pstatstg, /* [out] */
610 DWORD grfStatFlag) /* [in] */
612 StorageBaseImpl *This = (StorageBaseImpl *)iface;
613 StgProperty curProperty;
615 HRESULT res = STG_E_UNKNOWN;
617 TRACE("(%p, %p, %lx)\n",
618 iface, pstatstg, grfStatFlag);
621 * Perform a sanity check on the parameters.
623 if ( (This==0) || (pstatstg==0))
630 * Read the information from the property.
632 readSuccessful = StorageImpl_ReadProperty(
633 This->ancestorStorage,
634 This->rootPropertySetIndex,
639 StorageUtl_CopyPropertyToSTATSTG(
653 TRACE("<-- STATSTG: pwcsName: %s, type: %ld, cbSize.Low/High: %ld/%ld, grfMode: %08lx, grfLocksSupported: %ld, grfStateBits: %08lx\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
655 TRACE("<-- %08lx\n", res);
659 /************************************************************************
660 * Storage32BaseImpl_RenameElement (IStorage)
662 * This method will rename the specified element.
664 * See Windows documentation for more details on IStorage methods.
666 * Implementation notes: The method used to rename consists of creating a clone
667 * of the deleted StgProperty object setting it with the new name and to
668 * perform a DestroyElement of the old StgProperty.
670 HRESULT WINAPI StorageBaseImpl_RenameElement(
672 const OLECHAR* pwcsOldName, /* [in] */
673 const OLECHAR* pwcsNewName) /* [in] */
675 StorageBaseImpl *This = (StorageBaseImpl *)iface;
676 IEnumSTATSTGImpl* propertyEnumeration;
677 StgProperty currentProperty;
678 ULONG foundPropertyIndex;
680 TRACE("(%p, %s, %s)\n",
681 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
684 * Create a property enumeration to search the properties
686 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
687 This->rootPropertySetIndex);
690 * Search the enumeration for the new property name
692 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
696 if (foundPropertyIndex != PROPERTY_NULL)
699 * There is already a property with the new name
701 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
702 return STG_E_FILEALREADYEXISTS;
705 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
708 * Search the enumeration for the old property name
710 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
715 * Delete the property enumeration since we don't need it anymore
717 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
719 if (foundPropertyIndex != PROPERTY_NULL)
721 StgProperty renamedProperty;
722 ULONG renamedPropertyIndex;
725 * Setup a new property for the renamed property
727 renamedProperty.sizeOfNameString =
728 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
730 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
731 return STG_E_INVALIDNAME;
733 strcpyW(renamedProperty.name, pwcsNewName);
735 renamedProperty.propertyType = currentProperty.propertyType;
736 renamedProperty.startingBlock = currentProperty.startingBlock;
737 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
738 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
740 renamedProperty.previousProperty = PROPERTY_NULL;
741 renamedProperty.nextProperty = PROPERTY_NULL;
744 * Bring the dirProperty link in case it is a storage and in which
745 * case the renamed storage elements don't require to be reorganized.
747 renamedProperty.dirProperty = currentProperty.dirProperty;
749 /* call CoFileTime to get the current time
750 renamedProperty.timeStampS1
751 renamedProperty.timeStampD1
752 renamedProperty.timeStampS2
753 renamedProperty.timeStampD2
754 renamedProperty.propertyUniqueID
758 * Obtain a free property in the property chain
760 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
763 * Save the new property into the new property spot
765 StorageImpl_WriteProperty(
766 This->ancestorStorage,
767 renamedPropertyIndex,
771 * Find a spot in the property chain for our newly created property.
775 renamedPropertyIndex,
779 * At this point the renamed property has been inserted in the tree,
780 * now, before to Destroy the old property we must zeroed it's dirProperty
781 * otherwise the DestroyProperty below will zap it all and we do not want
783 * Also, we fake that the old property is a storage so the DestroyProperty
784 * will not do a SetSize(0) on the stream data.
786 * This means that we need to tweek the StgProperty if it is a stream or a
789 StorageImpl_ReadProperty(This->ancestorStorage,
793 currentProperty.dirProperty = PROPERTY_NULL;
794 currentProperty.propertyType = PROPTYPE_STORAGE;
795 StorageImpl_WriteProperty(
796 This->ancestorStorage,
801 * Invoke Destroy to get rid of the ole property and automatically redo
802 * the linking of it's previous and next members...
804 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
810 * There is no property with the old name
812 return STG_E_FILENOTFOUND;
818 /************************************************************************
819 * Storage32BaseImpl_CreateStream (IStorage)
821 * This method will create a stream object within this storage
823 * See Windows documentation for more details on IStorage methods.
825 HRESULT WINAPI StorageBaseImpl_CreateStream(
827 const OLECHAR* pwcsName, /* [string][in] */
828 DWORD grfMode, /* [in] */
829 DWORD reserved1, /* [in] */
830 DWORD reserved2, /* [in] */
831 IStream** ppstm) /* [out] */
833 StorageBaseImpl *This = (StorageBaseImpl *)iface;
834 IEnumSTATSTGImpl* propertyEnumeration;
835 StgStreamImpl* newStream;
836 StgProperty currentProperty, newStreamProperty;
837 ULONG foundPropertyIndex, newPropertyIndex;
838 DWORD parent_grfMode;
840 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
841 iface, debugstr_w(pwcsName), grfMode,
842 reserved1, reserved2, ppstm);
845 * Validate parameters
848 return STG_E_INVALIDPOINTER;
851 return STG_E_INVALIDNAME;
853 if (reserved1 || reserved2)
854 return STG_E_INVALIDPARAMETER;
857 * Validate the STGM flags
859 if ( FAILED( validateSTGM(grfMode) ))
860 return STG_E_INVALIDFLAG;
862 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
863 return STG_E_INVALIDFLAG;
868 if ((grfMode & STGM_DELETEONRELEASE) ||
869 (grfMode & STGM_TRANSACTED))
870 return STG_E_INVALIDFUNCTION;
873 * Check that we're compatible with the parent's storage mode
875 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
876 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
877 return STG_E_ACCESSDENIED;
880 * Initialize the out parameter
885 * Create a property enumeration to search the properties
887 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
888 This->rootPropertySetIndex);
890 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
894 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
896 if (foundPropertyIndex != PROPERTY_NULL)
899 * An element with this name already exists
901 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
903 IStorage_DestroyElement(iface, pwcsName);
906 return STG_E_FILEALREADYEXISTS;
910 * memset the empty property
912 memset(&newStreamProperty, 0, sizeof(StgProperty));
914 newStreamProperty.sizeOfNameString =
915 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
917 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
918 return STG_E_INVALIDNAME;
920 strcpyW(newStreamProperty.name, pwcsName);
922 newStreamProperty.propertyType = PROPTYPE_STREAM;
923 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
924 newStreamProperty.size.u.LowPart = 0;
925 newStreamProperty.size.u.HighPart = 0;
927 newStreamProperty.previousProperty = PROPERTY_NULL;
928 newStreamProperty.nextProperty = PROPERTY_NULL;
929 newStreamProperty.dirProperty = PROPERTY_NULL;
931 /* call CoFileTime to get the current time
932 newStreamProperty.timeStampS1
933 newStreamProperty.timeStampD1
934 newStreamProperty.timeStampS2
935 newStreamProperty.timeStampD2
938 /* newStreamProperty.propertyUniqueID */
941 * Get a free property or create a new one
943 newPropertyIndex = getFreeProperty(This->ancestorStorage);
946 * Save the new property into the new property spot
948 StorageImpl_WriteProperty(
949 This->ancestorStorage,
954 * Find a spot in the property chain for our newly created property.
962 * Open the stream to return it.
964 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
968 *ppstm = (IStream*)newStream;
971 * Since we are returning a pointer to the interface, we have to nail down
974 IStream_AddRef(*ppstm);
978 return STG_E_INSUFFICIENTMEMORY;
984 /************************************************************************
985 * Storage32BaseImpl_SetClass (IStorage)
987 * This method will write the specified CLSID in the property of this
990 * See Windows documentation for more details on IStorage methods.
992 HRESULT WINAPI StorageBaseImpl_SetClass(
994 REFCLSID clsid) /* [in] */
996 StorageBaseImpl *This = (StorageBaseImpl *)iface;
997 HRESULT hRes = E_FAIL;
998 StgProperty curProperty;
1001 TRACE("(%p, %p)\n", iface, clsid);
1003 success = StorageImpl_ReadProperty(This->ancestorStorage,
1004 This->rootPropertySetIndex,
1008 curProperty.propertyUniqueID = *clsid;
1010 success = StorageImpl_WriteProperty(This->ancestorStorage,
1011 This->rootPropertySetIndex,
1020 /************************************************************************
1021 ** Storage32Impl implementation
1024 /************************************************************************
1025 * Storage32Impl_CreateStorage (IStorage)
1027 * This method will create the storage object within the provided storage.
1029 * See Windows documentation for more details on IStorage methods.
1031 HRESULT WINAPI StorageImpl_CreateStorage(
1033 const OLECHAR *pwcsName, /* [string][in] */
1034 DWORD grfMode, /* [in] */
1035 DWORD reserved1, /* [in] */
1036 DWORD reserved2, /* [in] */
1037 IStorage **ppstg) /* [out] */
1039 StorageImpl* const This=(StorageImpl*)iface;
1041 IEnumSTATSTGImpl *propertyEnumeration;
1042 StgProperty currentProperty;
1043 StgProperty newProperty;
1044 ULONG foundPropertyIndex;
1045 ULONG newPropertyIndex;
1047 DWORD parent_grfMode;
1049 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1050 iface, debugstr_w(pwcsName), grfMode,
1051 reserved1, reserved2, ppstg);
1054 * Validate parameters
1057 return STG_E_INVALIDPOINTER;
1060 return STG_E_INVALIDNAME;
1063 * Validate the STGM flags
1065 if ( FAILED( validateSTGM(grfMode) ) ||
1066 (grfMode & STGM_DELETEONRELEASE) )
1067 return STG_E_INVALIDFLAG;
1070 * Check that we're compatible with the parent's storage mode
1072 parent_grfMode = STGM_ACCESS_MODE( This->base.ancestorStorage->base.openFlags );
1073 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
1074 return STG_E_ACCESSDENIED;
1077 * Initialize the out parameter
1082 * Create a property enumeration and search the properties
1084 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1085 This->base.rootPropertySetIndex);
1087 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1090 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1092 if (foundPropertyIndex != PROPERTY_NULL)
1095 * An element with this name already exists
1097 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1098 IStorage_DestroyElement(iface, pwcsName);
1100 return STG_E_FILEALREADYEXISTS;
1104 * memset the empty property
1106 memset(&newProperty, 0, sizeof(StgProperty));
1108 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1110 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1111 return STG_E_INVALIDNAME;
1113 strcpyW(newProperty.name, pwcsName);
1115 newProperty.propertyType = PROPTYPE_STORAGE;
1116 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1117 newProperty.size.u.LowPart = 0;
1118 newProperty.size.u.HighPart = 0;
1120 newProperty.previousProperty = PROPERTY_NULL;
1121 newProperty.nextProperty = PROPERTY_NULL;
1122 newProperty.dirProperty = PROPERTY_NULL;
1124 /* call CoFileTime to get the current time
1125 newProperty.timeStampS1
1126 newProperty.timeStampD1
1127 newProperty.timeStampS2
1128 newProperty.timeStampD2
1131 /* newStorageProperty.propertyUniqueID */
1134 * Obtain a free property in the property chain
1136 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1139 * Save the new property into the new property spot
1141 StorageImpl_WriteProperty(
1142 This->base.ancestorStorage,
1147 * Find a spot in the property chain for our newly created property.
1149 updatePropertyChain(
1155 * Open it to get a pointer to return.
1157 hr = IStorage_OpenStorage(
1159 (const OLECHAR*)pwcsName,
1166 if( (hr != S_OK) || (*ppstg == NULL))
1176 /***************************************************************************
1180 * Get a free property or create a new one.
1182 static ULONG getFreeProperty(
1183 StorageImpl *storage)
1185 ULONG currentPropertyIndex = 0;
1186 ULONG newPropertyIndex = PROPERTY_NULL;
1187 BOOL readSuccessful = TRUE;
1188 StgProperty currentProperty;
1193 * Start by reading the root property
1195 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1196 currentPropertyIndex,
1200 if (currentProperty.sizeOfNameString == 0)
1203 * The property existis and is available, we found it.
1205 newPropertyIndex = currentPropertyIndex;
1211 * We exhausted the property list, we will create more space below
1213 newPropertyIndex = currentPropertyIndex;
1215 currentPropertyIndex++;
1217 } while (newPropertyIndex == PROPERTY_NULL);
1220 * grow the property chain
1222 if (! readSuccessful)
1224 StgProperty emptyProperty;
1225 ULARGE_INTEGER newSize;
1226 ULONG propertyIndex;
1227 ULONG lastProperty = 0;
1228 ULONG blockCount = 0;
1231 * obtain the new count of property blocks
1233 blockCount = BlockChainStream_GetCount(
1234 storage->base.ancestorStorage->rootBlockChain)+1;
1237 * initialize the size used by the property stream
1239 newSize.u.HighPart = 0;
1240 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1243 * add a property block to the property chain
1245 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1248 * memset the empty property in order to initialize the unused newly
1251 memset(&emptyProperty, 0, sizeof(StgProperty));
1256 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1259 propertyIndex = newPropertyIndex;
1260 propertyIndex < lastProperty;
1263 StorageImpl_WriteProperty(
1264 storage->base.ancestorStorage,
1270 return newPropertyIndex;
1273 /****************************************************************************
1277 * Case insensitive comparaison of StgProperty.name by first considering
1280 * Returns <0 when newPrpoerty < currentProperty
1281 * >0 when newPrpoerty > currentProperty
1282 * 0 when newPrpoerty == currentProperty
1284 static LONG propertyNameCmp(
1285 const OLECHAR *newProperty,
1286 const OLECHAR *currentProperty)
1288 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1293 * We compare the string themselves only when they are of the same length
1295 diff = lstrcmpiW( newProperty, currentProperty);
1301 /****************************************************************************
1305 * Properly link this new element in the property chain.
1307 static void updatePropertyChain(
1308 StorageImpl *storage,
1309 ULONG newPropertyIndex,
1310 StgProperty newProperty)
1312 StgProperty currentProperty;
1315 * Read the root property
1317 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1318 storage->base.rootPropertySetIndex,
1321 if (currentProperty.dirProperty != PROPERTY_NULL)
1324 * The root storage contains some element, therefore, start the research
1325 * for the appropriate location.
1328 ULONG current, next, previous, currentPropertyId;
1331 * Keep the StgProperty sequence number of the storage first property
1333 currentPropertyId = currentProperty.dirProperty;
1338 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1339 currentProperty.dirProperty,
1342 previous = currentProperty.previousProperty;
1343 next = currentProperty.nextProperty;
1344 current = currentPropertyId;
1348 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1352 if (previous != PROPERTY_NULL)
1354 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1361 currentProperty.previousProperty = newPropertyIndex;
1362 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1370 if (next != PROPERTY_NULL)
1372 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1379 currentProperty.nextProperty = newPropertyIndex;
1380 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1389 * Trying to insert an item with the same name in the
1390 * subtree structure.
1395 previous = currentProperty.previousProperty;
1396 next = currentProperty.nextProperty;
1402 * The root storage is empty, link the new property to it's dir property
1404 currentProperty.dirProperty = newPropertyIndex;
1405 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1406 storage->base.rootPropertySetIndex,
1412 /*************************************************************************
1415 HRESULT WINAPI StorageImpl_CopyTo(
1417 DWORD ciidExclude, /* [in] */
1418 const IID* rgiidExclude, /* [size_is][unique][in] */
1419 SNB snbExclude, /* [unique][in] */
1420 IStorage* pstgDest) /* [unique][in] */
1422 IEnumSTATSTG *elements = 0;
1423 STATSTG curElement, strStat;
1425 IStorage *pstgTmp, *pstgChild;
1426 IStream *pstrTmp, *pstrChild;
1428 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1429 FIXME("Exclude option not implemented\n");
1431 TRACE("(%p, %ld, %p, %p, %p)\n",
1432 iface, ciidExclude, rgiidExclude,
1433 snbExclude, pstgDest);
1436 * Perform a sanity check
1438 if ( pstgDest == 0 )
1439 return STG_E_INVALIDPOINTER;
1442 * Enumerate the elements
1444 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1452 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1453 IStorage_SetClass( pstgDest, &curElement.clsid );
1458 * Obtain the next element
1460 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1462 if ( hr == S_FALSE )
1464 hr = S_OK; /* done, every element has been copied */
1468 if (curElement.type == STGTY_STORAGE)
1471 * open child source storage
1473 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1474 STGM_READ|STGM_SHARE_EXCLUSIVE,
1475 NULL, 0, &pstgChild );
1481 * Check if destination storage is not a child of the source
1482 * storage, which will cause an infinite loop
1484 if (pstgChild == pstgDest)
1486 IEnumSTATSTG_Release(elements);
1488 return STG_E_ACCESSDENIED;
1492 * create a new storage in destination storage
1494 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1495 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1499 * if it already exist, don't create a new one use this one
1501 if (hr == STG_E_FILEALREADYEXISTS)
1503 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1504 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1505 NULL, 0, &pstgTmp );
1513 * do the copy recursively
1515 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1516 snbExclude, pstgTmp );
1518 IStorage_Release( pstgTmp );
1519 IStorage_Release( pstgChild );
1521 else if (curElement.type == STGTY_STREAM)
1524 * create a new stream in destination storage. If the stream already
1525 * exist, it will be deleted and a new one will be created.
1527 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1528 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1535 * open child stream storage
1537 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1538 STGM_READ|STGM_SHARE_EXCLUSIVE,
1545 * Get the size of the source stream
1547 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1550 * Set the size of the destination stream.
1552 IStream_SetSize(pstrTmp, strStat.cbSize);
1557 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1560 IStream_Release( pstrTmp );
1561 IStream_Release( pstrChild );
1565 WARN("unknown element type: %ld\n", curElement.type);
1568 } while (hr == S_OK);
1573 IEnumSTATSTG_Release(elements);
1578 /*************************************************************************
1579 * MoveElementTo (IStorage)
1581 HRESULT WINAPI StorageImpl_MoveElementTo(
1583 const OLECHAR *pwcsName, /* [string][in] */
1584 IStorage *pstgDest, /* [unique][in] */
1585 const OLECHAR *pwcsNewName,/* [string][in] */
1586 DWORD grfFlags) /* [in] */
1588 FIXME("not implemented!\n");
1592 /*************************************************************************
1595 * Ensures that any changes made to a storage object open in transacted mode
1596 * are reflected in the parent storage
1599 * Wine doesn't implement transacted mode, which seems to be a basic
1600 * optimization, so we can ignore this stub for now.
1602 HRESULT WINAPI StorageImpl_Commit(
1604 DWORD grfCommitFlags)/* [in] */
1606 FIXME("(%ld): stub!\n", grfCommitFlags);
1610 /*************************************************************************
1613 * Discard all changes that have been made since the last commit operation
1615 HRESULT WINAPI StorageImpl_Revert(
1618 FIXME("not implemented!\n");
1622 /*************************************************************************
1623 * DestroyElement (IStorage)
1625 * Stategy: This implementation is build this way for simplicity not for speed.
1626 * I always delete the top most element of the enumeration and adjust
1627 * the deleted element pointer all the time. This takes longer to
1628 * do but allow to reinvoke DestroyElement whenever we encounter a
1629 * storage object. The optimisation reside in the usage of another
1630 * enumeration stategy that would give all the leaves of a storage
1631 * first. (postfix order)
1633 HRESULT WINAPI StorageImpl_DestroyElement(
1635 const OLECHAR *pwcsName)/* [string][in] */
1637 StorageImpl* const This=(StorageImpl*)iface;
1639 IEnumSTATSTGImpl* propertyEnumeration;
1642 StgProperty propertyToDelete;
1643 StgProperty parentProperty;
1644 ULONG foundPropertyIndexToDelete;
1645 ULONG typeOfRelation;
1646 ULONG parentPropertyId;
1649 iface, debugstr_w(pwcsName));
1652 * Perform a sanity check on the parameters.
1655 return STG_E_INVALIDPOINTER;
1658 * Create a property enumeration to search the property with the given name
1660 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1661 This->base.ancestorStorage,
1662 This->base.rootPropertySetIndex);
1664 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1665 propertyEnumeration,
1669 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1671 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1673 return STG_E_FILENOTFOUND;
1677 * Find the parent property of the property to delete (the one that
1678 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1679 * the parent is This. Otherwise, the parent is one of it's sibling...
1683 * First, read This's StgProperty..
1685 res = StorageImpl_ReadProperty(
1686 This->base.ancestorStorage,
1687 This->base.rootPropertySetIndex,
1693 * Second, check to see if by any chance the actual storage (This) is not
1694 * the parent of the property to delete... We never know...
1696 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1699 * Set data as it would have been done in the else part...
1701 typeOfRelation = PROPERTY_RELATION_DIR;
1702 parentPropertyId = This->base.rootPropertySetIndex;
1707 * Create a property enumeration to search the parent properties, and
1708 * delete it once done.
1710 IEnumSTATSTGImpl* propertyEnumeration2;
1712 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1713 This->base.ancestorStorage,
1714 This->base.rootPropertySetIndex);
1716 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1717 propertyEnumeration2,
1718 foundPropertyIndexToDelete,
1722 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1725 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1727 hr = deleteStorageProperty(
1729 foundPropertyIndexToDelete,
1732 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1734 hr = deleteStreamProperty(
1736 foundPropertyIndexToDelete,
1744 * Adjust the property chain
1746 hr = adjustPropertyChain(
1757 /************************************************************************
1758 * StorageImpl_Stat (IStorage)
1760 * This method will retrieve information about this storage object.
1762 * See Windows documentation for more details on IStorage methods.
1764 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1765 STATSTG* pstatstg, /* [out] */
1766 DWORD grfStatFlag) /* [in] */
1768 StorageImpl* const This = (StorageImpl*)iface;
1769 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1771 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1773 CoTaskMemFree(pstatstg->pwcsName);
1774 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1775 strcpyW(pstatstg->pwcsName, This->pwcsName);
1783 /*********************************************************************
1787 * Perform the deletion of a complete storage node
1790 static HRESULT deleteStorageProperty(
1791 StorageImpl *parentStorage,
1792 ULONG indexOfPropertyToDelete,
1793 StgProperty propertyToDelete)
1795 IEnumSTATSTG *elements = 0;
1796 IStorage *childStorage = 0;
1797 STATSTG currentElement;
1799 HRESULT destroyHr = S_OK;
1802 * Open the storage and enumerate it
1804 hr = StorageBaseImpl_OpenStorage(
1805 (IStorage*)parentStorage,
1806 propertyToDelete.name,
1808 STGM_SHARE_EXCLUSIVE,
1819 * Enumerate the elements
1821 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1826 * Obtain the next element
1828 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1831 destroyHr = StorageImpl_DestroyElement(
1832 (IStorage*)childStorage,
1833 (OLECHAR*)currentElement.pwcsName);
1835 CoTaskMemFree(currentElement.pwcsName);
1839 * We need to Reset the enumeration every time because we delete elements
1840 * and the enumeration could be invalid
1842 IEnumSTATSTG_Reset(elements);
1844 } while ((hr == S_OK) && (destroyHr == S_OK));
1847 * Invalidate the property by zeroing it's name member.
1849 propertyToDelete.sizeOfNameString = 0;
1851 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1852 indexOfPropertyToDelete,
1855 IStorage_Release(childStorage);
1856 IEnumSTATSTG_Release(elements);
1861 /*********************************************************************
1865 * Perform the deletion of a stream node
1868 static HRESULT deleteStreamProperty(
1869 StorageImpl *parentStorage,
1870 ULONG indexOfPropertyToDelete,
1871 StgProperty propertyToDelete)
1875 ULARGE_INTEGER size;
1877 size.u.HighPart = 0;
1880 hr = StorageBaseImpl_OpenStream(
1881 (IStorage*)parentStorage,
1882 (OLECHAR*)propertyToDelete.name,
1884 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1896 hr = IStream_SetSize(pis, size);
1904 * Release the stream object.
1906 IStream_Release(pis);
1909 * Invalidate the property by zeroing it's name member.
1911 propertyToDelete.sizeOfNameString = 0;
1914 * Here we should re-read the property so we get the updated pointer
1915 * but since we are here to zap it, I don't do it...
1917 StorageImpl_WriteProperty(
1918 parentStorage->base.ancestorStorage,
1919 indexOfPropertyToDelete,
1925 /*********************************************************************
1929 * Finds a placeholder for the StgProperty within the Storage
1932 static HRESULT findPlaceholder(
1933 StorageImpl *storage,
1934 ULONG propertyIndexToStore,
1935 ULONG storePropertyIndex,
1938 StgProperty storeProperty;
1943 * Read the storage property
1945 res = StorageImpl_ReadProperty(
1946 storage->base.ancestorStorage,
1955 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1957 if (storeProperty.previousProperty != PROPERTY_NULL)
1959 return findPlaceholder(
1961 propertyIndexToStore,
1962 storeProperty.previousProperty,
1967 storeProperty.previousProperty = propertyIndexToStore;
1970 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1972 if (storeProperty.nextProperty != PROPERTY_NULL)
1974 return findPlaceholder(
1976 propertyIndexToStore,
1977 storeProperty.nextProperty,
1982 storeProperty.nextProperty = propertyIndexToStore;
1985 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1987 if (storeProperty.dirProperty != PROPERTY_NULL)
1989 return findPlaceholder(
1991 propertyIndexToStore,
1992 storeProperty.dirProperty,
1997 storeProperty.dirProperty = propertyIndexToStore;
2001 hr = StorageImpl_WriteProperty(
2002 storage->base.ancestorStorage,
2014 /*************************************************************************
2018 * This method takes the previous and the next property link of a property
2019 * to be deleted and find them a place in the Storage.
2021 static HRESULT adjustPropertyChain(
2023 StgProperty propertyToDelete,
2024 StgProperty parentProperty,
2025 ULONG parentPropertyId,
2028 ULONG newLinkProperty = PROPERTY_NULL;
2029 BOOL needToFindAPlaceholder = FALSE;
2030 ULONG storeNode = PROPERTY_NULL;
2031 ULONG toStoreNode = PROPERTY_NULL;
2032 INT relationType = 0;
2036 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2038 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2041 * Set the parent previous to the property to delete previous
2043 newLinkProperty = propertyToDelete.previousProperty;
2045 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2048 * We also need to find a storage for the other link, setup variables
2049 * to do this at the end...
2051 needToFindAPlaceholder = TRUE;
2052 storeNode = propertyToDelete.previousProperty;
2053 toStoreNode = propertyToDelete.nextProperty;
2054 relationType = PROPERTY_RELATION_NEXT;
2057 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2060 * Set the parent previous to the property to delete next
2062 newLinkProperty = propertyToDelete.nextProperty;
2066 * Link it for real...
2068 parentProperty.previousProperty = newLinkProperty;
2071 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2073 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2076 * Set the parent next to the property to delete next previous
2078 newLinkProperty = propertyToDelete.previousProperty;
2080 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2083 * We also need to find a storage for the other link, setup variables
2084 * to do this at the end...
2086 needToFindAPlaceholder = TRUE;
2087 storeNode = propertyToDelete.previousProperty;
2088 toStoreNode = propertyToDelete.nextProperty;
2089 relationType = PROPERTY_RELATION_NEXT;
2092 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2095 * Set the parent next to the property to delete next
2097 newLinkProperty = propertyToDelete.nextProperty;
2101 * Link it for real...
2103 parentProperty.nextProperty = newLinkProperty;
2105 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2107 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2110 * Set the parent dir to the property to delete previous
2112 newLinkProperty = propertyToDelete.previousProperty;
2114 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2117 * We also need to find a storage for the other link, setup variables
2118 * to do this at the end...
2120 needToFindAPlaceholder = TRUE;
2121 storeNode = propertyToDelete.previousProperty;
2122 toStoreNode = propertyToDelete.nextProperty;
2123 relationType = PROPERTY_RELATION_NEXT;
2126 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2129 * Set the parent dir to the property to delete next
2131 newLinkProperty = propertyToDelete.nextProperty;
2135 * Link it for real...
2137 parentProperty.dirProperty = newLinkProperty;
2141 * Write back the parent property
2143 res = StorageImpl_WriteProperty(
2144 This->base.ancestorStorage,
2153 * If a placeholder is required for the other link, then, find one and
2154 * get out of here...
2156 if (needToFindAPlaceholder)
2158 hr = findPlaceholder(
2169 /******************************************************************************
2170 * SetElementTimes (IStorage)
2172 HRESULT WINAPI StorageImpl_SetElementTimes(
2174 const OLECHAR *pwcsName,/* [string][in] */
2175 const FILETIME *pctime, /* [in] */
2176 const FILETIME *patime, /* [in] */
2177 const FILETIME *pmtime) /* [in] */
2179 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2183 /******************************************************************************
2184 * SetStateBits (IStorage)
2186 HRESULT WINAPI StorageImpl_SetStateBits(
2188 DWORD grfStateBits,/* [in] */
2189 DWORD grfMask) /* [in] */
2191 FIXME("not implemented!\n");
2196 * Virtual function table for the IStorage32Impl class.
2198 static const IStorageVtbl Storage32Impl_Vtbl =
2200 StorageBaseImpl_QueryInterface,
2201 StorageBaseImpl_AddRef,
2202 StorageBaseImpl_Release,
2203 StorageBaseImpl_CreateStream,
2204 StorageBaseImpl_OpenStream,
2205 StorageImpl_CreateStorage,
2206 StorageBaseImpl_OpenStorage,
2208 StorageImpl_MoveElementTo,
2211 StorageBaseImpl_EnumElements,
2212 StorageImpl_DestroyElement,
2213 StorageBaseImpl_RenameElement,
2214 StorageImpl_SetElementTimes,
2215 StorageBaseImpl_SetClass,
2216 StorageImpl_SetStateBits,
2220 HRESULT StorageImpl_Construct(
2230 StgProperty currentProperty;
2231 BOOL readSuccessful;
2232 ULONG currentPropertyIndex;
2234 if ( FAILED( validateSTGM(openFlags) ))
2235 return STG_E_INVALIDFLAG;
2237 memset(This, 0, sizeof(StorageImpl));
2240 * Initialize the virtual function table.
2242 This->base.lpVtbl = &Storage32Impl_Vtbl;
2243 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2244 This->base.v_destructor = &StorageImpl_Destroy;
2245 This->base.openFlags = openFlags;
2248 * This is the top-level storage so initialize the ancestor pointer
2251 This->base.ancestorStorage = This;
2254 * Initialize the physical support of the storage.
2256 This->hFile = hFile;
2259 * Store copy of file path.
2262 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2263 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2264 if (!This->pwcsName)
2265 return STG_E_INSUFFICIENTMEMORY;
2266 strcpyW(This->pwcsName, pwcsName);
2270 * Initialize the big block cache.
2272 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2273 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2274 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2280 if (This->bigBlockFile == 0)
2285 ULARGE_INTEGER size;
2286 BYTE* bigBlockBuffer;
2289 * Initialize all header variables:
2290 * - The big block depot consists of one block and it is at block 0
2291 * - The properties start at block 1
2292 * - There is no small block depot
2294 memset( This->bigBlockDepotStart,
2296 sizeof(This->bigBlockDepotStart));
2298 This->bigBlockDepotCount = 1;
2299 This->bigBlockDepotStart[0] = 0;
2300 This->rootStartBlock = 1;
2301 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2302 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2303 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2304 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2305 This->extBigBlockDepotCount = 0;
2307 StorageImpl_SaveFileHeader(This);
2310 * Add one block for the big block depot and one block for the properties
2312 size.u.HighPart = 0;
2313 size.u.LowPart = This->bigBlockSize * 3;
2314 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2317 * Initialize the big block depot
2319 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2320 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2321 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2322 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2323 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2328 * Load the header for the file.
2330 hr = StorageImpl_LoadFileHeader(This);
2334 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2341 * There is no block depot cached yet.
2343 This->indexBlockDepotCached = 0xFFFFFFFF;
2346 * Start searching for free blocks with block 0.
2348 This->prevFreeBlock = 0;
2351 * Create the block chain abstractions.
2353 if(!(This->rootBlockChain =
2354 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2355 return STG_E_READFAULT;
2357 if(!(This->smallBlockDepotChain =
2358 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2360 return STG_E_READFAULT;
2363 * Write the root property
2367 StgProperty rootProp;
2369 * Initialize the property chain
2371 memset(&rootProp, 0, sizeof(rootProp));
2372 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2373 sizeof(rootProp.name)/sizeof(WCHAR) );
2374 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2375 rootProp.propertyType = PROPTYPE_ROOT;
2376 rootProp.previousProperty = PROPERTY_NULL;
2377 rootProp.nextProperty = PROPERTY_NULL;
2378 rootProp.dirProperty = PROPERTY_NULL;
2379 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2380 rootProp.size.u.HighPart = 0;
2381 rootProp.size.u.LowPart = 0;
2383 StorageImpl_WriteProperty(This, 0, &rootProp);
2387 * Find the ID of the root in the property sets.
2389 currentPropertyIndex = 0;
2393 readSuccessful = StorageImpl_ReadProperty(
2395 currentPropertyIndex,
2400 if ( (currentProperty.sizeOfNameString != 0 ) &&
2401 (currentProperty.propertyType == PROPTYPE_ROOT) )
2403 This->base.rootPropertySetIndex = currentPropertyIndex;
2407 currentPropertyIndex++;
2409 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2411 if (!readSuccessful)
2414 return STG_E_READFAULT;
2418 * Create the block chain abstraction for the small block root chain.
2420 if(!(This->smallBlockRootChain =
2421 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2422 return STG_E_READFAULT;
2427 void StorageImpl_Destroy(StorageBaseImpl* iface)
2429 StorageImpl *This = (StorageImpl*) iface;
2430 TRACE("(%p)\n", This);
2432 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2434 BlockChainStream_Destroy(This->smallBlockRootChain);
2435 BlockChainStream_Destroy(This->rootBlockChain);
2436 BlockChainStream_Destroy(This->smallBlockDepotChain);
2438 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2439 HeapFree(GetProcessHeap(), 0, This);
2442 /******************************************************************************
2443 * Storage32Impl_GetNextFreeBigBlock
2445 * Returns the index of the next free big block.
2446 * If the big block depot is filled, this method will enlarge it.
2449 ULONG StorageImpl_GetNextFreeBigBlock(
2452 ULONG depotBlockIndexPos;
2454 ULONG depotBlockOffset;
2455 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2456 ULONG nextBlockIndex = BLOCK_SPECIAL;
2458 ULONG freeBlock = BLOCK_UNUSED;
2460 depotIndex = This->prevFreeBlock / blocksPerDepot;
2461 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2464 * Scan the entire big block depot until we find a block marked free
2466 while (nextBlockIndex != BLOCK_UNUSED)
2468 if (depotIndex < COUNT_BBDEPOTINHEADER)
2470 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2473 * Grow the primary depot.
2475 if (depotBlockIndexPos == BLOCK_UNUSED)
2477 depotBlockIndexPos = depotIndex*blocksPerDepot;
2480 * Add a block depot.
2482 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2483 This->bigBlockDepotCount++;
2484 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2487 * Flag it as a block depot.
2489 StorageImpl_SetNextBlockInChain(This,
2493 /* Save new header information.
2495 StorageImpl_SaveFileHeader(This);
2500 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2502 if (depotBlockIndexPos == BLOCK_UNUSED)
2505 * Grow the extended depot.
2507 ULONG extIndex = BLOCK_UNUSED;
2508 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2509 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2511 if (extBlockOffset == 0)
2513 /* We need an extended block.
2515 extIndex = Storage32Impl_AddExtBlockDepot(This);
2516 This->extBigBlockDepotCount++;
2517 depotBlockIndexPos = extIndex + 1;
2520 depotBlockIndexPos = depotIndex * blocksPerDepot;
2523 * Add a block depot and mark it in the extended block.
2525 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2526 This->bigBlockDepotCount++;
2527 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2529 /* Flag the block depot.
2531 StorageImpl_SetNextBlockInChain(This,
2535 /* If necessary, flag the extended depot block.
2537 if (extIndex != BLOCK_UNUSED)
2538 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2540 /* Save header information.
2542 StorageImpl_SaveFileHeader(This);
2546 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2548 if (depotBuffer != 0)
2550 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2551 ( nextBlockIndex != BLOCK_UNUSED))
2553 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2555 if (nextBlockIndex == BLOCK_UNUSED)
2557 freeBlock = (depotIndex * blocksPerDepot) +
2558 (depotBlockOffset/sizeof(ULONG));
2561 depotBlockOffset += sizeof(ULONG);
2564 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2568 depotBlockOffset = 0;
2571 This->prevFreeBlock = freeBlock;
2576 /******************************************************************************
2577 * Storage32Impl_AddBlockDepot
2579 * This will create a depot block, essentially it is a block initialized
2582 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2586 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2589 * Initialize blocks as free
2591 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2593 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2596 /******************************************************************************
2597 * Storage32Impl_GetExtDepotBlock
2599 * Returns the index of the block that corresponds to the specified depot
2600 * index. This method is only for depot indexes equal or greater than
2601 * COUNT_BBDEPOTINHEADER.
2603 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2605 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2606 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2607 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2608 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2609 ULONG blockIndex = BLOCK_UNUSED;
2610 ULONG extBlockIndex = This->extBigBlockDepotStart;
2612 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2614 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2615 return BLOCK_UNUSED;
2617 while (extBlockCount > 0)
2619 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2623 if (extBlockIndex != BLOCK_UNUSED)
2627 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2629 if (depotBuffer != 0)
2631 StorageUtl_ReadDWord(depotBuffer,
2632 extBlockOffset * sizeof(ULONG),
2635 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2642 /******************************************************************************
2643 * Storage32Impl_SetExtDepotBlock
2645 * Associates the specified block index to the specified depot index.
2646 * This method is only for depot indexes equal or greater than
2647 * COUNT_BBDEPOTINHEADER.
2649 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2653 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2654 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2655 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2656 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2657 ULONG extBlockIndex = This->extBigBlockDepotStart;
2659 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2661 while (extBlockCount > 0)
2663 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2667 if (extBlockIndex != BLOCK_UNUSED)
2671 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2673 if (depotBuffer != 0)
2675 StorageUtl_WriteDWord(depotBuffer,
2676 extBlockOffset * sizeof(ULONG),
2679 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2684 /******************************************************************************
2685 * Storage32Impl_AddExtBlockDepot
2687 * Creates an extended depot block.
2689 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2691 ULONG numExtBlocks = This->extBigBlockDepotCount;
2692 ULONG nextExtBlock = This->extBigBlockDepotStart;
2693 BYTE* depotBuffer = NULL;
2694 ULONG index = BLOCK_UNUSED;
2695 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2696 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2697 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2699 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2700 blocksPerDepotBlock;
2702 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2705 * The first extended block.
2707 This->extBigBlockDepotStart = index;
2713 * Follow the chain to the last one.
2715 for (i = 0; i < (numExtBlocks - 1); i++)
2717 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2721 * Add the new extended block to the chain.
2723 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2724 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2725 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2729 * Initialize this block.
2731 depotBuffer = StorageImpl_GetBigBlock(This, index);
2732 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2733 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2738 /******************************************************************************
2739 * Storage32Impl_FreeBigBlock
2741 * This method will flag the specified block as free in the big block depot.
2743 void StorageImpl_FreeBigBlock(
2747 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2749 if (blockIndex < This->prevFreeBlock)
2750 This->prevFreeBlock = blockIndex;
2753 /************************************************************************
2754 * Storage32Impl_GetNextBlockInChain
2756 * This method will retrieve the block index of the next big block in
2759 * Params: This - Pointer to the Storage object.
2760 * blockIndex - Index of the block to retrieve the chain
2762 * nextBlockIndex - receives the return value.
2764 * Returns: This method returns the index of the next block in the chain.
2765 * It will return the constants:
2766 * BLOCK_SPECIAL - If the block given was not part of a
2768 * BLOCK_END_OF_CHAIN - If the block given was the last in
2770 * BLOCK_UNUSED - If the block given was not past of a chain
2772 * BLOCK_EXTBBDEPOT - This block is part of the extended
2775 * See Windows documentation for more details on IStorage methods.
2777 HRESULT StorageImpl_GetNextBlockInChain(
2780 ULONG* nextBlockIndex)
2782 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2783 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2784 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2786 ULONG depotBlockIndexPos;
2789 *nextBlockIndex = BLOCK_SPECIAL;
2791 if(depotBlockCount >= This->bigBlockDepotCount)
2793 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2794 This->bigBlockDepotCount);
2795 return STG_E_READFAULT;
2799 * Cache the currently accessed depot block.
2801 if (depotBlockCount != This->indexBlockDepotCached)
2803 This->indexBlockDepotCached = depotBlockCount;
2805 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2807 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2812 * We have to look in the extended depot.
2814 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2817 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2820 return STG_E_READFAULT;
2822 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2824 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2825 This->blockDepotCached[index] = *nextBlockIndex;
2827 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2830 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2835 /******************************************************************************
2836 * Storage32Impl_GetNextExtendedBlock
2838 * Given an extended block this method will return the next extended block.
2841 * The last ULONG of an extended block is the block index of the next
2842 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2846 * - The index of the next extended block
2847 * - BLOCK_UNUSED: there is no next extended block.
2848 * - Any other return values denotes failure.
2850 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2852 ULONG nextBlockIndex = BLOCK_SPECIAL;
2853 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2856 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2860 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2862 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2865 return nextBlockIndex;
2868 /******************************************************************************
2869 * Storage32Impl_SetNextBlockInChain
2871 * This method will write the index of the specified block's next block
2872 * in the big block depot.
2874 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2877 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2878 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2879 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2882 void StorageImpl_SetNextBlockInChain(
2887 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2888 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2889 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2890 ULONG depotBlockIndexPos;
2893 assert(depotBlockCount < This->bigBlockDepotCount);
2894 assert(blockIndex != nextBlock);
2896 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2898 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2903 * We have to look in the extended depot.
2905 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2908 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2912 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2913 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2917 * Update the cached block depot, if necessary.
2919 if (depotBlockCount == This->indexBlockDepotCached)
2921 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2925 /******************************************************************************
2926 * Storage32Impl_LoadFileHeader
2928 * This method will read in the file header, i.e. big block index -1.
2930 HRESULT StorageImpl_LoadFileHeader(
2933 HRESULT hr = STG_E_FILENOTFOUND;
2934 void* headerBigBlock = NULL;
2938 * Get a pointer to the big block of data containing the header.
2940 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2943 * Extract the information from the header.
2945 if (headerBigBlock!=0)
2948 * Check for the "magic number" signature and return an error if it is not
2951 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2953 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2954 return STG_E_OLDFORMAT;
2957 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2959 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2960 return STG_E_INVALIDHEADER;
2963 StorageUtl_ReadWord(
2965 OFFSET_BIGBLOCKSIZEBITS,
2966 &This->bigBlockSizeBits);
2968 StorageUtl_ReadWord(
2970 OFFSET_SMALLBLOCKSIZEBITS,
2971 &This->smallBlockSizeBits);
2973 StorageUtl_ReadDWord(
2975 OFFSET_BBDEPOTCOUNT,
2976 &This->bigBlockDepotCount);
2978 StorageUtl_ReadDWord(
2980 OFFSET_ROOTSTARTBLOCK,
2981 &This->rootStartBlock);
2983 StorageUtl_ReadDWord(
2985 OFFSET_SBDEPOTSTART,
2986 &This->smallBlockDepotStart);
2988 StorageUtl_ReadDWord(
2990 OFFSET_EXTBBDEPOTSTART,
2991 &This->extBigBlockDepotStart);
2993 StorageUtl_ReadDWord(
2995 OFFSET_EXTBBDEPOTCOUNT,
2996 &This->extBigBlockDepotCount);
2998 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3000 StorageUtl_ReadDWord(
3002 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3003 &(This->bigBlockDepotStart[index]));
3007 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3011 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3012 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3016 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3017 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3021 * Right now, the code is making some assumptions about the size of the
3022 * blocks, just make sure they are what we're expecting.
3024 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3025 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3027 WARN("Broken OLE storage file\n");
3028 hr = STG_E_INVALIDHEADER;
3034 * Release the block.
3036 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3042 /******************************************************************************
3043 * Storage32Impl_SaveFileHeader
3045 * This method will save to the file the header, i.e. big block -1.
3047 void StorageImpl_SaveFileHeader(
3050 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3055 * Get a pointer to the big block of data containing the header.
3057 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3060 * If the block read failed, the file is probably new.
3065 * Initialize for all unknown fields.
3067 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3070 * Initialize the magic number.
3072 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3075 * And a bunch of things we don't know what they mean
3077 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3078 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3079 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3080 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3084 * Write the information to the header.
3086 StorageUtl_WriteWord(
3088 OFFSET_BIGBLOCKSIZEBITS,
3089 This->bigBlockSizeBits);
3091 StorageUtl_WriteWord(
3093 OFFSET_SMALLBLOCKSIZEBITS,
3094 This->smallBlockSizeBits);
3096 StorageUtl_WriteDWord(
3098 OFFSET_BBDEPOTCOUNT,
3099 This->bigBlockDepotCount);
3101 StorageUtl_WriteDWord(
3103 OFFSET_ROOTSTARTBLOCK,
3104 This->rootStartBlock);
3106 StorageUtl_WriteDWord(
3108 OFFSET_SBDEPOTSTART,
3109 This->smallBlockDepotStart);
3111 StorageUtl_WriteDWord(
3113 OFFSET_SBDEPOTCOUNT,
3114 This->smallBlockDepotChain ?
3115 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3117 StorageUtl_WriteDWord(
3119 OFFSET_EXTBBDEPOTSTART,
3120 This->extBigBlockDepotStart);
3122 StorageUtl_WriteDWord(
3124 OFFSET_EXTBBDEPOTCOUNT,
3125 This->extBigBlockDepotCount);
3127 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3129 StorageUtl_WriteDWord(
3131 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3132 (This->bigBlockDepotStart[index]));
3136 * Write the big block back to the file.
3138 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3141 /******************************************************************************
3142 * Storage32Impl_ReadProperty
3144 * This method will read the specified property from the property chain.
3146 BOOL StorageImpl_ReadProperty(
3149 StgProperty* buffer)
3151 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3152 ULARGE_INTEGER offsetInPropSet;
3153 BOOL readSuccessful;
3156 offsetInPropSet.u.HighPart = 0;
3157 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3159 readSuccessful = BlockChainStream_ReadAt(
3160 This->rootBlockChain,
3168 /* replace the name of root entry (often "Root Entry") by the file name */
3169 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3170 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3172 memset(buffer->name, 0, sizeof(buffer->name));
3176 PROPERTY_NAME_BUFFER_LEN );
3177 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3179 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3181 StorageUtl_ReadWord(
3183 OFFSET_PS_NAMELENGTH,
3184 &buffer->sizeOfNameString);
3186 StorageUtl_ReadDWord(
3188 OFFSET_PS_PREVIOUSPROP,
3189 &buffer->previousProperty);
3191 StorageUtl_ReadDWord(
3194 &buffer->nextProperty);
3196 StorageUtl_ReadDWord(
3199 &buffer->dirProperty);
3201 StorageUtl_ReadGUID(
3204 &buffer->propertyUniqueID);
3206 StorageUtl_ReadDWord(
3209 &buffer->timeStampS1);
3211 StorageUtl_ReadDWord(
3214 &buffer->timeStampD1);
3216 StorageUtl_ReadDWord(
3219 &buffer->timeStampS2);
3221 StorageUtl_ReadDWord(
3224 &buffer->timeStampD2);
3226 StorageUtl_ReadDWord(
3228 OFFSET_PS_STARTBLOCK,
3229 &buffer->startingBlock);
3231 StorageUtl_ReadDWord(
3234 &buffer->size.u.LowPart);
3236 buffer->size.u.HighPart = 0;
3239 return readSuccessful;
3242 /*********************************************************************
3243 * Write the specified property into the property chain
3245 BOOL StorageImpl_WriteProperty(
3248 StgProperty* buffer)
3250 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3251 ULARGE_INTEGER offsetInPropSet;
3252 BOOL writeSuccessful;
3255 offsetInPropSet.u.HighPart = 0;
3256 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3258 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3261 currentProperty + OFFSET_PS_NAME,
3263 PROPERTY_NAME_BUFFER_LEN );
3265 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3267 StorageUtl_WriteWord(
3269 OFFSET_PS_NAMELENGTH,
3270 buffer->sizeOfNameString);
3272 StorageUtl_WriteDWord(
3274 OFFSET_PS_PREVIOUSPROP,
3275 buffer->previousProperty);
3277 StorageUtl_WriteDWord(
3280 buffer->nextProperty);
3282 StorageUtl_WriteDWord(
3285 buffer->dirProperty);
3287 StorageUtl_WriteGUID(
3290 &buffer->propertyUniqueID);
3292 StorageUtl_WriteDWord(
3295 buffer->timeStampS1);
3297 StorageUtl_WriteDWord(
3300 buffer->timeStampD1);
3302 StorageUtl_WriteDWord(
3305 buffer->timeStampS2);
3307 StorageUtl_WriteDWord(
3310 buffer->timeStampD2);
3312 StorageUtl_WriteDWord(
3314 OFFSET_PS_STARTBLOCK,
3315 buffer->startingBlock);
3317 StorageUtl_WriteDWord(
3320 buffer->size.u.LowPart);
3322 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3327 return writeSuccessful;
3330 BOOL StorageImpl_ReadBigBlock(
3335 void* bigBlockBuffer;
3337 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3339 if (bigBlockBuffer!=0)
3341 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3343 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3351 BOOL StorageImpl_WriteBigBlock(
3356 void* bigBlockBuffer;
3358 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3360 if (bigBlockBuffer!=0)
3362 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3364 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3372 void* StorageImpl_GetROBigBlock(
3376 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3379 void* StorageImpl_GetBigBlock(
3383 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3386 void StorageImpl_ReleaseBigBlock(
3390 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3393 /******************************************************************************
3394 * Storage32Impl_SmallBlocksToBigBlocks
3396 * This method will convert a small block chain to a big block chain.
3397 * The small block chain will be destroyed.
3399 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3401 SmallBlockChainStream** ppsbChain)
3403 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3404 ULARGE_INTEGER size, offset;
3405 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3406 ULONG propertyIndex;
3407 BOOL successRead, successWrite;
3408 StgProperty chainProperty;
3410 BlockChainStream *bbTempChain = NULL;
3411 BlockChainStream *bigBlockChain = NULL;
3414 * Create a temporary big block chain that doesn't have
3415 * an associated property. This temporary chain will be
3416 * used to copy data from small blocks to big blocks.
3418 bbTempChain = BlockChainStream_Construct(This,
3421 if(!bbTempChain) return NULL;
3423 * Grow the big block chain.
3425 size = SmallBlockChainStream_GetSize(*ppsbChain);
3426 BlockChainStream_SetSize(bbTempChain, size);
3429 * Copy the contents of the small block chain to the big block chain
3430 * by small block size increments.
3432 offset.u.LowPart = 0;
3433 offset.u.HighPart = 0;
3437 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3440 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3442 DEF_SMALL_BLOCK_SIZE,
3445 cbTotalRead += cbRead;
3447 successWrite = BlockChainStream_WriteAt(bbTempChain,
3452 cbTotalWritten += cbWritten;
3454 offset.u.LowPart += This->smallBlockSize;
3456 } while (successRead && successWrite);
3457 HeapFree(GetProcessHeap(),0,buffer);
3459 assert(cbTotalRead == cbTotalWritten);
3462 * Destroy the small block chain.
3464 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3465 size.u.HighPart = 0;
3467 SmallBlockChainStream_SetSize(*ppsbChain, size);
3468 SmallBlockChainStream_Destroy(*ppsbChain);
3472 * Change the property information. This chain is now a big block chain
3473 * and it doesn't reside in the small blocks chain anymore.
3475 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3477 chainProperty.startingBlock = bbHeadOfChain;
3479 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3482 * Destroy the temporary propertyless big block chain.
3483 * Create a new big block chain associated with this property.
3485 BlockChainStream_Destroy(bbTempChain);
3486 bigBlockChain = BlockChainStream_Construct(This,
3490 return bigBlockChain;
3493 void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3495 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3497 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3498 HeapFree(GetProcessHeap(), 0, This);
3501 /******************************************************************************
3503 ** Storage32InternalImpl_Commit
3505 ** The non-root storages cannot be opened in transacted mode thus this function
3508 HRESULT WINAPI StorageInternalImpl_Commit(
3510 DWORD grfCommitFlags) /* [in] */
3515 /******************************************************************************
3517 ** Storage32InternalImpl_Revert
3519 ** The non-root storages cannot be opened in transacted mode thus this function
3522 HRESULT WINAPI StorageInternalImpl_Revert(
3528 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3530 IStorage_Release((IStorage*)This->parentStorage);
3531 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3532 HeapFree(GetProcessHeap(), 0, This);
3535 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3536 IEnumSTATSTG* iface,
3540 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3543 * Perform a sanity check on the parameters.
3546 return E_INVALIDARG;
3549 * Initialize the return parameter.
3554 * Compare the riid with the interface IDs implemented by this object.
3556 if (IsEqualGUID(&IID_IUnknown, riid) ||
3557 IsEqualGUID(&IID_IStorage, riid))
3559 *ppvObject = (IEnumSTATSTG*)This;
3560 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3564 return E_NOINTERFACE;
3567 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3568 IEnumSTATSTG* iface)
3570 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3571 return InterlockedIncrement(&This->ref);
3574 ULONG WINAPI IEnumSTATSTGImpl_Release(
3575 IEnumSTATSTG* iface)
3577 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3581 newRef = InterlockedDecrement(&This->ref);
3584 * If the reference count goes down to 0, perform suicide.
3588 IEnumSTATSTGImpl_Destroy(This);
3594 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3595 IEnumSTATSTG* iface,
3598 ULONG* pceltFetched)
3600 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3602 StgProperty currentProperty;
3603 STATSTG* currentReturnStruct = rgelt;
3604 ULONG objectFetched = 0;
3605 ULONG currentSearchNode;
3608 * Perform a sanity check on the parameters.
3610 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3611 return E_INVALIDARG;
3614 * To avoid the special case, get another pointer to a ULONG value if
3615 * the caller didn't supply one.
3617 if (pceltFetched==0)
3618 pceltFetched = &objectFetched;
3621 * Start the iteration, we will iterate until we hit the end of the
3622 * linked list or until we hit the number of items to iterate through
3627 * Start with the node at the top of the stack.
3629 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3631 while ( ( *pceltFetched < celt) &&
3632 ( currentSearchNode!=PROPERTY_NULL) )
3635 * Remove the top node from the stack
3637 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3640 * Read the property from the storage.
3642 StorageImpl_ReadProperty(This->parentStorage,
3647 * Copy the information to the return buffer.
3649 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3654 * Step to the next item in the iteration
3657 currentReturnStruct++;
3660 * Push the next search node in the search stack.
3662 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3665 * continue the iteration.
3667 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3670 if (*pceltFetched == celt)
3677 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3678 IEnumSTATSTG* iface,
3681 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3683 StgProperty currentProperty;
3684 ULONG objectFetched = 0;
3685 ULONG currentSearchNode;
3688 * Start with the node at the top of the stack.
3690 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3692 while ( (objectFetched < celt) &&
3693 (currentSearchNode!=PROPERTY_NULL) )
3696 * Remove the top node from the stack
3698 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3701 * Read the property from the storage.
3703 StorageImpl_ReadProperty(This->parentStorage,
3708 * Step to the next item in the iteration
3713 * Push the next search node in the search stack.
3715 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3718 * continue the iteration.
3720 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3723 if (objectFetched == celt)
3729 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3730 IEnumSTATSTG* iface)
3732 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3734 StgProperty rootProperty;
3735 BOOL readSuccessful;
3738 * Re-initialize the search stack to an empty stack
3740 This->stackSize = 0;
3743 * Read the root property from the storage.
3745 readSuccessful = StorageImpl_ReadProperty(
3746 This->parentStorage,
3747 This->firstPropertyNode,
3752 assert(rootProperty.sizeOfNameString!=0);
3755 * Push the search node in the search stack.
3757 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3763 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3764 IEnumSTATSTG* iface,
3765 IEnumSTATSTG** ppenum)
3767 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3769 IEnumSTATSTGImpl* newClone;
3772 * Perform a sanity check on the parameters.
3775 return E_INVALIDARG;
3777 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3778 This->firstPropertyNode);
3782 * The new clone enumeration must point to the same current node as
3785 newClone->stackSize = This->stackSize ;
3786 newClone->stackMaxSize = This->stackMaxSize ;
3787 newClone->stackToVisit =
3788 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3791 newClone->stackToVisit,
3793 sizeof(ULONG) * newClone->stackSize);
3795 *ppenum = (IEnumSTATSTG*)newClone;
3798 * Don't forget to nail down a reference to the clone before
3801 IEnumSTATSTGImpl_AddRef(*ppenum);
3806 INT IEnumSTATSTGImpl_FindParentProperty(
3807 IEnumSTATSTGImpl *This,
3808 ULONG childProperty,
3809 StgProperty *currentProperty,
3812 ULONG currentSearchNode;
3816 * To avoid the special case, get another pointer to a ULONG value if
3817 * the caller didn't supply one.
3820 thisNodeId = &foundNode;
3823 * Start with the node at the top of the stack.
3825 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3828 while (currentSearchNode!=PROPERTY_NULL)
3831 * Store the current node in the returned parameters
3833 *thisNodeId = currentSearchNode;
3836 * Remove the top node from the stack
3838 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3841 * Read the property from the storage.
3843 StorageImpl_ReadProperty(
3844 This->parentStorage,
3848 if (currentProperty->previousProperty == childProperty)
3849 return PROPERTY_RELATION_PREVIOUS;
3851 else if (currentProperty->nextProperty == childProperty)
3852 return PROPERTY_RELATION_NEXT;
3854 else if (currentProperty->dirProperty == childProperty)
3855 return PROPERTY_RELATION_DIR;
3858 * Push the next search node in the search stack.
3860 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3863 * continue the iteration.
3865 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3868 return PROPERTY_NULL;
3871 ULONG IEnumSTATSTGImpl_FindProperty(
3872 IEnumSTATSTGImpl* This,
3873 const OLECHAR* lpszPropName,
3874 StgProperty* currentProperty)
3876 ULONG currentSearchNode;
3879 * Start with the node at the top of the stack.
3881 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3883 while (currentSearchNode!=PROPERTY_NULL)
3886 * Remove the top node from the stack
3888 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3891 * Read the property from the storage.
3893 StorageImpl_ReadProperty(This->parentStorage,
3897 if ( propertyNameCmp(
3898 (const OLECHAR*)currentProperty->name,
3899 (const OLECHAR*)lpszPropName) == 0)
3900 return currentSearchNode;
3903 * Push the next search node in the search stack.
3905 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3908 * continue the iteration.
3910 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3913 return PROPERTY_NULL;
3916 void IEnumSTATSTGImpl_PushSearchNode(
3917 IEnumSTATSTGImpl* This,
3920 StgProperty rootProperty;
3921 BOOL readSuccessful;
3924 * First, make sure we're not trying to push an unexisting node.
3926 if (nodeToPush==PROPERTY_NULL)
3930 * First push the node to the stack
3932 if (This->stackSize == This->stackMaxSize)
3934 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3936 This->stackToVisit = HeapReAlloc(
3940 sizeof(ULONG) * This->stackMaxSize);
3943 This->stackToVisit[This->stackSize] = nodeToPush;
3947 * Read the root property from the storage.
3949 readSuccessful = StorageImpl_ReadProperty(
3950 This->parentStorage,
3956 assert(rootProperty.sizeOfNameString!=0);
3959 * Push the previous search node in the search stack.
3961 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3965 ULONG IEnumSTATSTGImpl_PopSearchNode(
3966 IEnumSTATSTGImpl* This,
3971 if (This->stackSize == 0)
3972 return PROPERTY_NULL;
3974 topNode = This->stackToVisit[This->stackSize-1];
3983 * Virtual function table for the IEnumSTATSTGImpl class.
3985 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
3987 IEnumSTATSTGImpl_QueryInterface,
3988 IEnumSTATSTGImpl_AddRef,
3989 IEnumSTATSTGImpl_Release,
3990 IEnumSTATSTGImpl_Next,
3991 IEnumSTATSTGImpl_Skip,
3992 IEnumSTATSTGImpl_Reset,
3993 IEnumSTATSTGImpl_Clone
3996 /******************************************************************************
3997 ** IEnumSTATSTGImpl implementation
4000 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4001 StorageImpl* parentStorage,
4002 ULONG firstPropertyNode)
4004 IEnumSTATSTGImpl* newEnumeration;
4006 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4008 if (newEnumeration!=0)
4011 * Set-up the virtual function table and reference count.
4013 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4014 newEnumeration->ref = 0;
4017 * We want to nail-down the reference to the storage in case the
4018 * enumeration out-lives the storage in the client application.
4020 newEnumeration->parentStorage = parentStorage;
4021 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4023 newEnumeration->firstPropertyNode = firstPropertyNode;
4026 * Initialize the search stack
4028 newEnumeration->stackSize = 0;
4029 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4030 newEnumeration->stackToVisit =
4031 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4034 * Make sure the current node of the iterator is the first one.
4036 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4039 return newEnumeration;
4043 * Virtual function table for the Storage32InternalImpl class.
4045 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4047 StorageBaseImpl_QueryInterface,
4048 StorageBaseImpl_AddRef,
4049 StorageBaseImpl_Release,
4050 StorageBaseImpl_CreateStream,
4051 StorageBaseImpl_OpenStream,
4052 StorageImpl_CreateStorage,
4053 StorageBaseImpl_OpenStorage,
4055 StorageImpl_MoveElementTo,
4056 StorageInternalImpl_Commit,
4057 StorageInternalImpl_Revert,
4058 StorageBaseImpl_EnumElements,
4059 StorageImpl_DestroyElement,
4060 StorageBaseImpl_RenameElement,
4061 StorageImpl_SetElementTimes,
4062 StorageBaseImpl_SetClass,
4063 StorageImpl_SetStateBits,
4064 StorageBaseImpl_Stat
4067 /******************************************************************************
4068 ** Storage32InternalImpl implementation
4071 StorageInternalImpl* StorageInternalImpl_Construct(
4072 StorageImpl* ancestorStorage,
4074 ULONG rootPropertyIndex)
4076 StorageInternalImpl* newStorage;
4079 * Allocate space for the new storage object
4081 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
4085 memset(newStorage, 0, sizeof(StorageInternalImpl));
4088 * Initialize the virtual function table.
4090 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4091 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4092 newStorage->base.openFlags = openFlags;
4095 * Keep the ancestor storage pointer and nail a reference to it.
4097 newStorage->base.ancestorStorage = ancestorStorage;
4098 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4101 * Keep the index of the root property set for this storage,
4103 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4111 /******************************************************************************
4112 ** StorageUtl implementation
4115 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4119 memcpy(&tmp, buffer+offset, sizeof(WORD));
4120 *value = le16toh(tmp);
4123 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4125 value = htole16(value);
4126 memcpy(buffer+offset, &value, sizeof(WORD));
4129 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4133 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4134 *value = le32toh(tmp);
4137 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4139 value = htole32(value);
4140 memcpy(buffer+offset, &value, sizeof(DWORD));
4143 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4144 ULARGE_INTEGER* value)
4146 #ifdef WORDS_BIGENDIAN
4149 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4150 value->u.LowPart = htole32(tmp.u.HighPart);
4151 value->u.HighPart = htole32(tmp.u.LowPart);
4153 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4157 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4158 const ULARGE_INTEGER *value)
4160 #ifdef WORDS_BIGENDIAN
4163 tmp.u.LowPart = htole32(value->u.HighPart);
4164 tmp.u.HighPart = htole32(value->u.LowPart);
4165 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4167 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4171 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4173 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4174 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4175 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4177 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4180 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4182 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4183 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4184 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4186 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4189 void StorageUtl_CopyPropertyToSTATSTG(
4190 STATSTG* destination,
4191 StgProperty* source,
4195 * The copy of the string occurs only when the flag is not set
4197 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4198 (source->name == NULL) ||
4199 (source->name[0] == 0) )
4201 destination->pwcsName = 0;
4205 destination->pwcsName =
4206 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4208 strcpyW((LPWSTR)destination->pwcsName, source->name);
4211 switch (source->propertyType)
4213 case PROPTYPE_STORAGE:
4215 destination->type = STGTY_STORAGE;
4217 case PROPTYPE_STREAM:
4218 destination->type = STGTY_STREAM;
4221 destination->type = STGTY_STREAM;
4225 destination->cbSize = source->size;
4227 currentReturnStruct->mtime = {0}; TODO
4228 currentReturnStruct->ctime = {0};
4229 currentReturnStruct->atime = {0};
4231 destination->grfMode = 0;
4232 destination->grfLocksSupported = 0;
4233 destination->clsid = source->propertyUniqueID;
4234 destination->grfStateBits = 0;
4235 destination->reserved = 0;
4238 /******************************************************************************
4239 ** BlockChainStream implementation
4242 BlockChainStream* BlockChainStream_Construct(
4243 StorageImpl* parentStorage,
4244 ULONG* headOfStreamPlaceHolder,
4245 ULONG propertyIndex)
4247 BlockChainStream* newStream;
4250 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4252 newStream->parentStorage = parentStorage;
4253 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4254 newStream->ownerPropertyIndex = propertyIndex;
4255 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4256 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4257 newStream->numBlocks = 0;
4259 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4261 while (blockIndex != BLOCK_END_OF_CHAIN)
4263 newStream->numBlocks++;
4264 newStream->tailIndex = blockIndex;
4266 if(FAILED(StorageImpl_GetNextBlockInChain(
4271 HeapFree(GetProcessHeap(), 0, newStream);
4279 void BlockChainStream_Destroy(BlockChainStream* This)
4281 HeapFree(GetProcessHeap(), 0, This);
4284 /******************************************************************************
4285 * BlockChainStream_GetHeadOfChain
4287 * Returns the head of this stream chain.
4288 * Some special chains don't have properties, their heads are kept in
4289 * This->headOfStreamPlaceHolder.
4292 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4294 StgProperty chainProperty;
4295 BOOL readSuccessful;
4297 if (This->headOfStreamPlaceHolder != 0)
4298 return *(This->headOfStreamPlaceHolder);
4300 if (This->ownerPropertyIndex != PROPERTY_NULL)
4302 readSuccessful = StorageImpl_ReadProperty(
4303 This->parentStorage,
4304 This->ownerPropertyIndex,
4309 return chainProperty.startingBlock;
4313 return BLOCK_END_OF_CHAIN;
4316 /******************************************************************************
4317 * BlockChainStream_GetCount
4319 * Returns the number of blocks that comprises this chain.
4320 * This is not the size of the stream as the last block may not be full!
4323 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4328 blockIndex = BlockChainStream_GetHeadOfChain(This);
4330 while (blockIndex != BLOCK_END_OF_CHAIN)
4334 if(FAILED(StorageImpl_GetNextBlockInChain(
4335 This->parentStorage,
4344 /******************************************************************************
4345 * BlockChainStream_ReadAt
4347 * Reads a specified number of bytes from this chain at the specified offset.
4348 * bytesRead may be NULL.
4349 * Failure will be returned if the specified number of bytes has not been read.
4351 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4352 ULARGE_INTEGER offset,
4357 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4358 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4359 ULONG bytesToReadInBuffer;
4362 BYTE* bigBlockBuffer;
4365 * Find the first block in the stream that contains part of the buffer.
4367 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4368 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4369 (blockNoInSequence < This->lastBlockNoInSequence) )
4371 blockIndex = BlockChainStream_GetHeadOfChain(This);
4372 This->lastBlockNoInSequence = blockNoInSequence;
4376 ULONG temp = blockNoInSequence;
4378 blockIndex = This->lastBlockNoInSequenceIndex;
4379 blockNoInSequence -= This->lastBlockNoInSequence;
4380 This->lastBlockNoInSequence = temp;
4383 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4385 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4387 blockNoInSequence--;
4390 This->lastBlockNoInSequenceIndex = blockIndex;
4393 * Start reading the buffer.
4396 bufferWalker = buffer;
4398 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4401 * Calculate how many bytes we can copy from this big block.
4403 bytesToReadInBuffer =
4404 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4407 * Copy those bytes to the buffer
4410 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4412 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4414 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4417 * Step to the next big block.
4419 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4422 bufferWalker += bytesToReadInBuffer;
4423 size -= bytesToReadInBuffer;
4424 *bytesRead += bytesToReadInBuffer;
4425 offsetInBlock = 0; /* There is no offset on the next block */
4432 /******************************************************************************
4433 * BlockChainStream_WriteAt
4435 * Writes the specified number of bytes to this chain at the specified offset.
4436 * bytesWritten may be NULL.
4437 * Will fail if not all specified number of bytes have been written.
4439 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4440 ULARGE_INTEGER offset,
4443 ULONG* bytesWritten)
4445 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4446 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4449 const BYTE* bufferWalker;
4450 BYTE* bigBlockBuffer;
4453 * Find the first block in the stream that contains part of the buffer.
4455 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4456 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4457 (blockNoInSequence < This->lastBlockNoInSequence) )
4459 blockIndex = BlockChainStream_GetHeadOfChain(This);
4460 This->lastBlockNoInSequence = blockNoInSequence;
4464 ULONG temp = blockNoInSequence;
4466 blockIndex = This->lastBlockNoInSequenceIndex;
4467 blockNoInSequence -= This->lastBlockNoInSequence;
4468 This->lastBlockNoInSequence = temp;
4471 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4473 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4476 blockNoInSequence--;
4479 This->lastBlockNoInSequenceIndex = blockIndex;
4482 * Here, I'm casting away the constness on the buffer variable
4483 * This is OK since we don't intend to modify that buffer.
4486 bufferWalker = (const BYTE*)buffer;
4488 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4491 * Calculate how many bytes we can copy from this big block.
4494 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4497 * Copy those bytes to the buffer
4499 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4501 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4503 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4506 * Step to the next big block.
4508 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4511 bufferWalker += bytesToWrite;
4512 size -= bytesToWrite;
4513 *bytesWritten += bytesToWrite;
4514 offsetInBlock = 0; /* There is no offset on the next block */
4520 /******************************************************************************
4521 * BlockChainStream_Shrink
4523 * Shrinks this chain in the big block depot.
4525 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4526 ULARGE_INTEGER newSize)
4528 ULONG blockIndex, extraBlock;
4533 * Reset the last accessed block cache.
4535 This->lastBlockNoInSequence = 0xFFFFFFFF;
4536 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4539 * Figure out how many blocks are needed to contain the new size
4541 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4543 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4546 blockIndex = BlockChainStream_GetHeadOfChain(This);
4549 * Go to the new end of chain
4551 while (count < numBlocks)
4553 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4559 /* Get the next block before marking the new end */
4560 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4564 /* Mark the new end of chain */
4565 StorageImpl_SetNextBlockInChain(
4566 This->parentStorage,
4568 BLOCK_END_OF_CHAIN);
4570 This->tailIndex = blockIndex;
4571 This->numBlocks = numBlocks;
4574 * Mark the extra blocks as free
4576 while (extraBlock != BLOCK_END_OF_CHAIN)
4578 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4581 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4582 extraBlock = blockIndex;
4588 /******************************************************************************
4589 * BlockChainStream_Enlarge
4591 * Grows this chain in the big block depot.
4593 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4594 ULARGE_INTEGER newSize)
4596 ULONG blockIndex, currentBlock;
4598 ULONG oldNumBlocks = 0;
4600 blockIndex = BlockChainStream_GetHeadOfChain(This);
4603 * Empty chain. Create the head.
4605 if (blockIndex == BLOCK_END_OF_CHAIN)
4607 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4608 StorageImpl_SetNextBlockInChain(This->parentStorage,
4610 BLOCK_END_OF_CHAIN);
4612 if (This->headOfStreamPlaceHolder != 0)
4614 *(This->headOfStreamPlaceHolder) = blockIndex;
4618 StgProperty chainProp;
4619 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4621 StorageImpl_ReadProperty(
4622 This->parentStorage,
4623 This->ownerPropertyIndex,
4626 chainProp.startingBlock = blockIndex;
4628 StorageImpl_WriteProperty(
4629 This->parentStorage,
4630 This->ownerPropertyIndex,
4634 This->tailIndex = blockIndex;
4635 This->numBlocks = 1;
4639 * Figure out how many blocks are needed to contain this stream
4641 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4643 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4647 * Go to the current end of chain
4649 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4651 currentBlock = blockIndex;
4653 while (blockIndex != BLOCK_END_OF_CHAIN)
4656 currentBlock = blockIndex;
4658 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4663 This->tailIndex = currentBlock;
4666 currentBlock = This->tailIndex;
4667 oldNumBlocks = This->numBlocks;
4670 * Add new blocks to the chain
4672 if (oldNumBlocks < newNumBlocks)
4674 while (oldNumBlocks < newNumBlocks)
4676 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4678 StorageImpl_SetNextBlockInChain(
4679 This->parentStorage,
4683 StorageImpl_SetNextBlockInChain(
4684 This->parentStorage,
4686 BLOCK_END_OF_CHAIN);
4688 currentBlock = blockIndex;
4692 This->tailIndex = blockIndex;
4693 This->numBlocks = newNumBlocks;
4699 /******************************************************************************
4700 * BlockChainStream_SetSize
4702 * Sets the size of this stream. The big block depot will be updated.
4703 * The file will grow if we grow the chain.
4705 * TODO: Free the actual blocks in the file when we shrink the chain.
4706 * Currently, the blocks are still in the file. So the file size
4707 * doesn't shrink even if we shrink streams.
4709 BOOL BlockChainStream_SetSize(
4710 BlockChainStream* This,
4711 ULARGE_INTEGER newSize)
4713 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4715 if (newSize.u.LowPart == size.u.LowPart)
4718 if (newSize.u.LowPart < size.u.LowPart)
4720 BlockChainStream_Shrink(This, newSize);
4724 ULARGE_INTEGER fileSize =
4725 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4727 ULONG diff = newSize.u.LowPart - size.u.LowPart;
4730 * Make sure the file stays a multiple of blocksize
4732 if ((diff % This->parentStorage->bigBlockSize) != 0)
4733 diff += (This->parentStorage->bigBlockSize -
4734 (diff % This->parentStorage->bigBlockSize) );
4736 fileSize.u.LowPart += diff;
4737 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4739 BlockChainStream_Enlarge(This, newSize);
4745 /******************************************************************************
4746 * BlockChainStream_GetSize
4748 * Returns the size of this chain.
4749 * Will return the block count if this chain doesn't have a property.
4751 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4753 StgProperty chainProperty;
4755 if(This->headOfStreamPlaceHolder == NULL)
4758 * This chain is a data stream read the property and return
4759 * the appropriate size
4761 StorageImpl_ReadProperty(
4762 This->parentStorage,
4763 This->ownerPropertyIndex,
4766 return chainProperty.size;
4771 * this chain is a chain that does not have a property, figure out the
4772 * size by making the product number of used blocks times the
4775 ULARGE_INTEGER result;
4776 result.u.HighPart = 0;
4779 BlockChainStream_GetCount(This) *
4780 This->parentStorage->bigBlockSize;
4786 /******************************************************************************
4787 ** SmallBlockChainStream implementation
4790 SmallBlockChainStream* SmallBlockChainStream_Construct(
4791 StorageImpl* parentStorage,
4792 ULONG propertyIndex)
4794 SmallBlockChainStream* newStream;
4796 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4798 newStream->parentStorage = parentStorage;
4799 newStream->ownerPropertyIndex = propertyIndex;
4804 void SmallBlockChainStream_Destroy(
4805 SmallBlockChainStream* This)
4807 HeapFree(GetProcessHeap(), 0, This);
4810 /******************************************************************************
4811 * SmallBlockChainStream_GetHeadOfChain
4813 * Returns the head of this chain of small blocks.
4815 ULONG SmallBlockChainStream_GetHeadOfChain(
4816 SmallBlockChainStream* This)
4818 StgProperty chainProperty;
4819 BOOL readSuccessful;
4821 if (This->ownerPropertyIndex)
4823 readSuccessful = StorageImpl_ReadProperty(
4824 This->parentStorage,
4825 This->ownerPropertyIndex,
4830 return chainProperty.startingBlock;
4835 return BLOCK_END_OF_CHAIN;
4838 /******************************************************************************
4839 * SmallBlockChainStream_GetNextBlockInChain
4841 * Returns the index of the next small block in this chain.
4844 * - BLOCK_END_OF_CHAIN: end of this chain
4845 * - BLOCK_UNUSED: small block 'blockIndex' is free
4847 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4848 SmallBlockChainStream* This,
4850 ULONG* nextBlockInChain)
4852 ULARGE_INTEGER offsetOfBlockInDepot;
4857 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4859 offsetOfBlockInDepot.u.HighPart = 0;
4860 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4863 * Read those bytes in the buffer from the small block file.
4865 success = BlockChainStream_ReadAt(
4866 This->parentStorage->smallBlockDepotChain,
4867 offsetOfBlockInDepot,
4874 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4878 return STG_E_READFAULT;
4881 /******************************************************************************
4882 * SmallBlockChainStream_SetNextBlockInChain
4884 * Writes the index of the next block of the specified block in the small
4886 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4887 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4889 void SmallBlockChainStream_SetNextBlockInChain(
4890 SmallBlockChainStream* This,
4894 ULARGE_INTEGER offsetOfBlockInDepot;
4898 offsetOfBlockInDepot.u.HighPart = 0;
4899 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4901 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4904 * Read those bytes in the buffer from the small block file.
4906 BlockChainStream_WriteAt(
4907 This->parentStorage->smallBlockDepotChain,
4908 offsetOfBlockInDepot,
4914 /******************************************************************************
4915 * SmallBlockChainStream_FreeBlock
4917 * Flag small block 'blockIndex' as free in the small block depot.
4919 void SmallBlockChainStream_FreeBlock(
4920 SmallBlockChainStream* This,
4923 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4926 /******************************************************************************
4927 * SmallBlockChainStream_GetNextFreeBlock
4929 * Returns the index of a free small block. The small block depot will be
4930 * enlarged if necessary. The small block chain will also be enlarged if
4933 ULONG SmallBlockChainStream_GetNextFreeBlock(
4934 SmallBlockChainStream* This)
4936 ULARGE_INTEGER offsetOfBlockInDepot;
4939 ULONG blockIndex = 0;
4940 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4941 BOOL success = TRUE;
4942 ULONG smallBlocksPerBigBlock;
4944 offsetOfBlockInDepot.u.HighPart = 0;
4947 * Scan the small block depot for a free block
4949 while (nextBlockIndex != BLOCK_UNUSED)
4951 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4953 success = BlockChainStream_ReadAt(
4954 This->parentStorage->smallBlockDepotChain,
4955 offsetOfBlockInDepot,
4961 * If we run out of space for the small block depot, enlarge it
4965 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
4967 if (nextBlockIndex != BLOCK_UNUSED)
4973 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4975 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4976 ULONG nextBlock, newsbdIndex;
4977 BYTE* smallBlockDepot;
4979 nextBlock = sbdIndex;
4980 while (nextBlock != BLOCK_END_OF_CHAIN)
4982 sbdIndex = nextBlock;
4983 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4986 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4987 if (sbdIndex != BLOCK_END_OF_CHAIN)
4988 StorageImpl_SetNextBlockInChain(
4989 This->parentStorage,
4993 StorageImpl_SetNextBlockInChain(
4994 This->parentStorage,
4996 BLOCK_END_OF_CHAIN);
4999 * Initialize all the small blocks to free
5002 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
5004 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5005 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
5010 * We have just created the small block depot.
5012 StgProperty rootProp;
5016 * Save it in the header
5018 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5019 StorageImpl_SaveFileHeader(This->parentStorage);
5022 * And allocate the first big block that will contain small blocks
5025 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5027 StorageImpl_SetNextBlockInChain(
5028 This->parentStorage,
5030 BLOCK_END_OF_CHAIN);
5032 StorageImpl_ReadProperty(
5033 This->parentStorage,
5034 This->parentStorage->base.rootPropertySetIndex,
5037 rootProp.startingBlock = sbStartIndex;
5038 rootProp.size.u.HighPart = 0;
5039 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5041 StorageImpl_WriteProperty(
5042 This->parentStorage,
5043 This->parentStorage->base.rootPropertySetIndex,
5049 smallBlocksPerBigBlock =
5050 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5053 * Verify if we have to allocate big blocks to contain small blocks
5055 if (blockIndex % smallBlocksPerBigBlock == 0)
5057 StgProperty rootProp;
5058 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5060 StorageImpl_ReadProperty(
5061 This->parentStorage,
5062 This->parentStorage->base.rootPropertySetIndex,
5065 if (rootProp.size.u.LowPart <
5066 (blocksRequired * This->parentStorage->bigBlockSize))
5068 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5070 BlockChainStream_SetSize(
5071 This->parentStorage->smallBlockRootChain,
5074 StorageImpl_WriteProperty(
5075 This->parentStorage,
5076 This->parentStorage->base.rootPropertySetIndex,
5084 /******************************************************************************
5085 * SmallBlockChainStream_ReadAt
5087 * Reads a specified number of bytes from this chain at the specified offset.
5088 * bytesRead may be NULL.
5089 * Failure will be returned if the specified number of bytes has not been read.
5091 BOOL SmallBlockChainStream_ReadAt(
5092 SmallBlockChainStream* This,
5093 ULARGE_INTEGER offset,
5098 ULARGE_INTEGER offsetInBigBlockFile;
5099 ULONG blockNoInSequence =
5100 offset.u.LowPart / This->parentStorage->smallBlockSize;
5102 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5103 ULONG bytesToReadInBuffer;
5105 ULONG bytesReadFromBigBlockFile;
5109 * This should never happen on a small block file.
5111 assert(offset.u.HighPart==0);
5114 * Find the first block in the stream that contains part of the buffer.
5116 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5118 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5120 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5123 blockNoInSequence--;
5127 * Start reading the buffer.
5130 bufferWalker = buffer;
5132 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5135 * Calculate how many bytes we can copy from this small block.
5137 bytesToReadInBuffer =
5138 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5141 * Calculate the offset of the small block in the small block file.
5143 offsetInBigBlockFile.u.HighPart = 0;
5144 offsetInBigBlockFile.u.LowPart =
5145 blockIndex * This->parentStorage->smallBlockSize;
5147 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5150 * Read those bytes in the buffer from the small block file.
5152 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5153 offsetInBigBlockFile,
5154 bytesToReadInBuffer,
5156 &bytesReadFromBigBlockFile);
5158 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5161 * Step to the next big block.
5163 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5165 bufferWalker += bytesToReadInBuffer;
5166 size -= bytesToReadInBuffer;
5167 *bytesRead += bytesToReadInBuffer;
5168 offsetInBlock = 0; /* There is no offset on the next block */
5174 /******************************************************************************
5175 * SmallBlockChainStream_WriteAt
5177 * Writes the specified number of bytes to this chain at the specified offset.
5178 * bytesWritten may be NULL.
5179 * Will fail if not all specified number of bytes have been written.
5181 BOOL SmallBlockChainStream_WriteAt(
5182 SmallBlockChainStream* This,
5183 ULARGE_INTEGER offset,
5186 ULONG* bytesWritten)
5188 ULARGE_INTEGER offsetInBigBlockFile;
5189 ULONG blockNoInSequence =
5190 offset.u.LowPart / This->parentStorage->smallBlockSize;
5192 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5193 ULONG bytesToWriteInBuffer;
5195 ULONG bytesWrittenFromBigBlockFile;
5196 const BYTE* bufferWalker;
5199 * This should never happen on a small block file.
5201 assert(offset.u.HighPart==0);
5204 * Find the first block in the stream that contains part of the buffer.
5206 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5208 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5210 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5212 blockNoInSequence--;
5216 * Start writing the buffer.
5218 * Here, I'm casting away the constness on the buffer variable
5219 * This is OK since we don't intend to modify that buffer.
5222 bufferWalker = (const BYTE*)buffer;
5223 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5226 * Calculate how many bytes we can copy to this small block.
5228 bytesToWriteInBuffer =
5229 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5232 * Calculate the offset of the small block in the small block file.
5234 offsetInBigBlockFile.u.HighPart = 0;
5235 offsetInBigBlockFile.u.LowPart =
5236 blockIndex * This->parentStorage->smallBlockSize;
5238 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5241 * Write those bytes in the buffer to the small block file.
5243 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5244 offsetInBigBlockFile,
5245 bytesToWriteInBuffer,
5247 &bytesWrittenFromBigBlockFile);
5249 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5252 * Step to the next big block.
5254 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5257 bufferWalker += bytesToWriteInBuffer;
5258 size -= bytesToWriteInBuffer;
5259 *bytesWritten += bytesToWriteInBuffer;
5260 offsetInBlock = 0; /* There is no offset on the next block */
5266 /******************************************************************************
5267 * SmallBlockChainStream_Shrink
5269 * Shrinks this chain in the small block depot.
5271 BOOL SmallBlockChainStream_Shrink(
5272 SmallBlockChainStream* This,
5273 ULARGE_INTEGER newSize)
5275 ULONG blockIndex, extraBlock;
5279 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5281 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5284 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5287 * Go to the new end of chain
5289 while (count < numBlocks)
5291 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5298 * If the count is 0, we have a special case, the head of the chain was
5303 StgProperty chainProp;
5305 StorageImpl_ReadProperty(This->parentStorage,
5306 This->ownerPropertyIndex,
5309 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5311 StorageImpl_WriteProperty(This->parentStorage,
5312 This->ownerPropertyIndex,
5316 * We start freeing the chain at the head block.
5318 extraBlock = blockIndex;
5322 /* Get the next block before marking the new end */
5323 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5327 /* Mark the new end of chain */
5328 SmallBlockChainStream_SetNextBlockInChain(
5331 BLOCK_END_OF_CHAIN);
5335 * Mark the extra blocks as free
5337 while (extraBlock != BLOCK_END_OF_CHAIN)
5339 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5342 SmallBlockChainStream_FreeBlock(This, extraBlock);
5343 extraBlock = blockIndex;
5349 /******************************************************************************
5350 * SmallBlockChainStream_Enlarge
5352 * Grows this chain in the small block depot.
5354 BOOL SmallBlockChainStream_Enlarge(
5355 SmallBlockChainStream* This,
5356 ULARGE_INTEGER newSize)
5358 ULONG blockIndex, currentBlock;
5360 ULONG oldNumBlocks = 0;
5362 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5367 if (blockIndex == BLOCK_END_OF_CHAIN)
5370 StgProperty chainProp;
5372 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5375 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5377 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5380 blockIndex = chainProp.startingBlock;
5381 SmallBlockChainStream_SetNextBlockInChain(
5384 BLOCK_END_OF_CHAIN);
5387 currentBlock = blockIndex;
5390 * Figure out how many blocks are needed to contain this stream
5392 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5394 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5398 * Go to the current end of chain
5400 while (blockIndex != BLOCK_END_OF_CHAIN)
5403 currentBlock = blockIndex;
5404 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5409 * Add new blocks to the chain
5411 while (oldNumBlocks < newNumBlocks)
5413 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5414 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5416 SmallBlockChainStream_SetNextBlockInChain(
5419 BLOCK_END_OF_CHAIN);
5421 currentBlock = blockIndex;
5428 /******************************************************************************
5429 * SmallBlockChainStream_GetCount
5431 * Returns the number of blocks that comprises this chain.
5432 * This is not the size of this chain as the last block may not be full!
5434 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5439 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5441 while (blockIndex != BLOCK_END_OF_CHAIN)
5445 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5452 /******************************************************************************
5453 * SmallBlockChainStream_SetSize
5455 * Sets the size of this stream.
5456 * The file will grow if we grow the chain.
5458 * TODO: Free the actual blocks in the file when we shrink the chain.
5459 * Currently, the blocks are still in the file. So the file size
5460 * doesn't shrink even if we shrink streams.
5462 BOOL SmallBlockChainStream_SetSize(
5463 SmallBlockChainStream* This,
5464 ULARGE_INTEGER newSize)
5466 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5468 if (newSize.u.LowPart == size.u.LowPart)
5471 if (newSize.u.LowPart < size.u.LowPart)
5473 SmallBlockChainStream_Shrink(This, newSize);
5477 SmallBlockChainStream_Enlarge(This, newSize);
5483 /******************************************************************************
5484 * SmallBlockChainStream_GetSize
5486 * Returns the size of this chain.
5488 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5490 StgProperty chainProperty;
5492 StorageImpl_ReadProperty(
5493 This->parentStorage,
5494 This->ownerPropertyIndex,
5497 return chainProperty.size;
5500 /******************************************************************************
5501 * StgCreateDocfile [OLE32.@]
5503 HRESULT WINAPI StgCreateDocfile(
5507 IStorage **ppstgOpen)
5509 StorageImpl* newStorage = 0;
5510 HANDLE hFile = INVALID_HANDLE_VALUE;
5511 HRESULT hr = STG_E_INVALIDFLAG;
5515 DWORD fileAttributes;
5516 WCHAR tempFileName[MAX_PATH];
5518 TRACE("(%s, %lx, %ld, %p)\n",
5519 debugstr_w(pwcsName), grfMode,
5520 reserved, ppstgOpen);
5523 * Validate the parameters
5526 return STG_E_INVALIDPOINTER;
5528 return STG_E_INVALIDPARAMETER;
5531 * Validate the STGM flags
5533 if ( FAILED( validateSTGM(grfMode) ))
5536 /* StgCreateDocFile always opens for write */
5537 switch(STGM_ACCESS_MODE(grfMode))
5540 case STGM_READWRITE:
5546 /* can't share write */
5547 switch(STGM_SHARE_MODE(grfMode))
5549 case STGM_SHARE_EXCLUSIVE:
5550 case STGM_SHARE_DENY_WRITE:
5556 /* shared reading requires transacted mode */
5557 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5558 !(grfMode&STGM_TRANSACTED) )
5562 * Generate a unique name.
5566 WCHAR tempPath[MAX_PATH];
5567 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5569 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
5572 memset(tempPath, 0, sizeof(tempPath));
5573 memset(tempFileName, 0, sizeof(tempFileName));
5575 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5578 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5579 pwcsName = tempFileName;
5582 hr = STG_E_INSUFFICIENTMEMORY;
5586 creationMode = TRUNCATE_EXISTING;
5590 creationMode = GetCreationModeFromSTGM(grfMode);
5594 * Interpret the STGM value grfMode
5596 shareMode = GetShareModeFromSTGM(grfMode);
5597 accessMode = GetAccessModeFromSTGM(grfMode);
5599 if (grfMode & STGM_DELETEONRELEASE)
5600 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5602 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5604 if (grfMode & STGM_TRANSACTED)
5605 FIXME("Transacted mode not implemented.\n");
5608 * Initialize the "out" parameter.
5612 hFile = CreateFileW(pwcsName,
5620 if (hFile == INVALID_HANDLE_VALUE)
5622 if(GetLastError() == ERROR_FILE_EXISTS)
5623 hr = STG_E_FILEALREADYEXISTS;
5630 * Allocate and initialize the new IStorage32object.
5632 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5634 if (newStorage == 0)
5636 hr = STG_E_INSUFFICIENTMEMORY;
5640 hr = StorageImpl_Construct(
5651 HeapFree(GetProcessHeap(), 0, newStorage);
5656 * Get an "out" pointer for the caller.
5658 hr = StorageBaseImpl_QueryInterface(
5659 (IStorage*)newStorage,
5660 (REFIID)&IID_IStorage,
5663 TRACE("<-- %p r = %08lx\n", *ppstgOpen, hr);
5668 /******************************************************************************
5669 * StgCreateStorageEx [OLE32.@]
5671 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5673 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5674 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5676 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5678 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5679 return STG_E_INVALIDPARAMETER;
5682 if (stgfmt != STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5684 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5685 return STG_E_INVALIDPARAMETER;
5688 if (stgfmt == STGFMT_FILE)
5690 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5691 return STG_E_INVALIDPARAMETER;
5694 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5696 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5697 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5700 ERR("Invalid stgfmt argument\n");
5701 return STG_E_INVALIDPARAMETER;
5704 /******************************************************************************
5705 * StgCreatePropSetStg [OLE32.@]
5707 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5708 IPropertySetStorage **ppPropSetStg)
5712 TRACE("(%p, 0x%lx, %p): stub\n", pstg, reserved, ppPropSetStg);
5714 hr = STG_E_INVALIDPARAMETER;
5716 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5717 (void**)ppPropSetStg);
5721 /******************************************************************************
5722 * StgOpenStorageEx [OLE32.@]
5724 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5726 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5727 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5729 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5731 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5732 return STG_E_INVALIDPARAMETER;
5735 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5737 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5738 return STG_E_INVALIDPARAMETER;
5741 if (stgfmt == STGFMT_FILE)
5743 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5744 return STG_E_INVALIDPARAMETER;
5747 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE || stgfmt == STGFMT_ANY)
5749 if (stgfmt == STGFMT_ANY)
5750 WARN("STGFMT_ANY assuming storage\n");
5751 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5752 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5755 ERR("Invalid stgfmt argument\n");
5756 return STG_E_INVALIDPARAMETER;
5760 /******************************************************************************
5761 * StgOpenStorage [OLE32.@]
5763 HRESULT WINAPI StgOpenStorage(
5764 const OLECHAR *pwcsName,
5765 IStorage *pstgPriority,
5769 IStorage **ppstgOpen)
5771 StorageImpl* newStorage = 0;
5776 WCHAR fullname[MAX_PATH];
5779 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5780 debugstr_w(pwcsName), pstgPriority, grfMode,
5781 snbExclude, reserved, ppstgOpen);
5784 * Perform sanity checks
5788 hr = STG_E_INVALIDNAME;
5794 hr = STG_E_INVALIDPOINTER;
5800 hr = STG_E_INVALIDPARAMETER;
5805 * Validate the sharing mode
5807 switch(STGM_SHARE_MODE(grfMode))
5809 case STGM_SHARE_EXCLUSIVE:
5810 case STGM_SHARE_DENY_WRITE:
5813 hr = STG_E_INVALIDFLAG;
5818 * Validate the STGM flags
5820 if ( FAILED( validateSTGM(grfMode) ) ||
5821 (grfMode&STGM_CREATE))
5823 hr = STG_E_INVALIDFLAG;
5827 /* shared reading requires transacted mode */
5828 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5829 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5830 !(grfMode&STGM_TRANSACTED) )
5832 hr = STG_E_INVALIDFLAG;
5837 * Interpret the STGM value grfMode
5839 shareMode = GetShareModeFromSTGM(grfMode);
5840 accessMode = GetAccessModeFromSTGM(grfMode);
5843 * Initialize the "out" parameter.
5847 hFile = CreateFileW( pwcsName,
5852 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5855 if (hFile==INVALID_HANDLE_VALUE)
5857 DWORD last_error = GetLastError();
5863 case ERROR_FILE_NOT_FOUND:
5864 hr = STG_E_FILENOTFOUND;
5867 case ERROR_PATH_NOT_FOUND:
5868 hr = STG_E_PATHNOTFOUND;
5871 case ERROR_ACCESS_DENIED:
5872 case ERROR_WRITE_PROTECT:
5873 hr = STG_E_ACCESSDENIED;
5876 case ERROR_SHARING_VIOLATION:
5877 hr = STG_E_SHAREVIOLATION;
5888 * Refuse to open the file if it's too small to be a structured storage file
5889 * FIXME: verify the file when reading instead of here
5891 length = GetFileSize(hFile, NULL);
5895 hr = STG_E_FILEALREADYEXISTS;
5900 * Allocate and initialize the new IStorage32object.
5902 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5904 if (newStorage == 0)
5906 hr = STG_E_INSUFFICIENTMEMORY;
5910 /* if the file's length was zero, initialize the storage */
5911 hr = StorageImpl_Construct(
5922 HeapFree(GetProcessHeap(), 0, newStorage);
5924 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5926 if(hr == STG_E_INVALIDHEADER)
5927 hr = STG_E_FILEALREADYEXISTS;
5931 /* prepare the file name string given in lieu of the root property name */
5932 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5933 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5934 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5937 * Get an "out" pointer for the caller.
5939 hr = StorageBaseImpl_QueryInterface(
5940 (IStorage*)newStorage,
5941 (REFIID)&IID_IStorage,
5945 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5949 /******************************************************************************
5950 * StgCreateDocfileOnILockBytes [OLE32.@]
5952 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5956 IStorage** ppstgOpen)
5958 StorageImpl* newStorage = 0;
5962 * Validate the parameters
5964 if ((ppstgOpen == 0) || (plkbyt == 0))
5965 return STG_E_INVALIDPOINTER;
5968 * Allocate and initialize the new IStorage object.
5970 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5972 if (newStorage == 0)
5973 return STG_E_INSUFFICIENTMEMORY;
5975 hr = StorageImpl_Construct(
5986 HeapFree(GetProcessHeap(), 0, newStorage);
5991 * Get an "out" pointer for the caller.
5993 hr = StorageBaseImpl_QueryInterface(
5994 (IStorage*)newStorage,
5995 (REFIID)&IID_IStorage,
6001 /******************************************************************************
6002 * StgOpenStorageOnILockBytes [OLE32.@]
6004 HRESULT WINAPI StgOpenStorageOnILockBytes(
6006 IStorage *pstgPriority,
6010 IStorage **ppstgOpen)
6012 StorageImpl* newStorage = 0;
6016 * Perform a sanity check
6018 if ((plkbyt == 0) || (ppstgOpen == 0))
6019 return STG_E_INVALIDPOINTER;
6022 * Validate the STGM flags
6024 if ( FAILED( validateSTGM(grfMode) ))
6025 return STG_E_INVALIDFLAG;
6028 * Initialize the "out" parameter.
6033 * Allocate and initialize the new IStorage object.
6035 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6037 if (newStorage == 0)
6038 return STG_E_INSUFFICIENTMEMORY;
6040 hr = StorageImpl_Construct(
6051 HeapFree(GetProcessHeap(), 0, newStorage);
6056 * Get an "out" pointer for the caller.
6058 hr = StorageBaseImpl_QueryInterface(
6059 (IStorage*)newStorage,
6060 (REFIID)&IID_IStorage,
6066 /******************************************************************************
6067 * StgSetTimes [ole32.@]
6068 * StgSetTimes [OLE32.@]
6072 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6073 FILETIME const *patime, FILETIME const *pmtime)
6075 IStorage *stg = NULL;
6078 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6080 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6084 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6085 IStorage_Release(stg);
6091 /******************************************************************************
6092 * StgIsStorageILockBytes [OLE32.@]
6094 * Determines if the ILockBytes contains a storage object.
6096 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6099 ULARGE_INTEGER offset;
6101 offset.u.HighPart = 0;
6102 offset.u.LowPart = 0;
6104 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6106 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6112 /******************************************************************************
6113 * WriteClassStg [OLE32.@]
6115 * This method will store the specified CLSID in the specified storage object
6117 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6123 hRes = IStorage_SetClass(pStg, rclsid);
6128 /***********************************************************************
6129 * ReadClassStg (OLE32.@)
6131 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
6133 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6143 * read a STATSTG structure (contains the clsid) from the storage
6145 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6148 *pclsid=pstatstg.clsid;
6153 /***********************************************************************
6154 * OleLoadFromStream (OLE32.@)
6156 * This function loads an object from stream
6158 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6162 LPPERSISTSTREAM xstm;
6164 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6166 res=ReadClassStm(pStm,&clsid);
6167 if (!SUCCEEDED(res))
6169 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6170 if (!SUCCEEDED(res))
6172 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6173 if (!SUCCEEDED(res)) {
6174 IUnknown_Release((IUnknown*)*ppvObj);
6177 res=IPersistStream_Load(xstm,pStm);
6178 IPersistStream_Release(xstm);
6179 /* FIXME: all refcounts ok at this point? I think they should be:
6182 * xstm : 0 (released)
6187 /***********************************************************************
6188 * OleSaveToStream (OLE32.@)
6190 * This function saves an object with the IPersistStream interface on it
6191 * to the specified stream.
6193 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6199 TRACE("(%p,%p)\n",pPStm,pStm);
6201 res=IPersistStream_GetClassID(pPStm,&clsid);
6203 if (SUCCEEDED(res)){
6205 res=WriteClassStm(pStm,&clsid);
6209 res=IPersistStream_Save(pPStm,pStm,TRUE);
6212 TRACE("Finished Save\n");
6216 /****************************************************************************
6217 * This method validate a STGM parameter that can contain the values below
6219 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6220 * The stgm values contained in 0xffff0000 are bitmasks.
6222 * STGM_DIRECT 0x00000000
6223 * STGM_TRANSACTED 0x00010000
6224 * STGM_SIMPLE 0x08000000
6226 * STGM_READ 0x00000000
6227 * STGM_WRITE 0x00000001
6228 * STGM_READWRITE 0x00000002
6230 * STGM_SHARE_DENY_NONE 0x00000040
6231 * STGM_SHARE_DENY_READ 0x00000030
6232 * STGM_SHARE_DENY_WRITE 0x00000020
6233 * STGM_SHARE_EXCLUSIVE 0x00000010
6235 * STGM_PRIORITY 0x00040000
6236 * STGM_DELETEONRELEASE 0x04000000
6238 * STGM_CREATE 0x00001000
6239 * STGM_CONVERT 0x00020000
6240 * STGM_FAILIFTHERE 0x00000000
6242 * STGM_NOSCRATCH 0x00100000
6243 * STGM_NOSNAPSHOT 0x00200000
6245 static HRESULT validateSTGM(DWORD stgm)
6247 DWORD access = STGM_ACCESS_MODE(stgm);
6248 DWORD share = STGM_SHARE_MODE(stgm);
6249 DWORD create = STGM_CREATE_MODE(stgm);
6251 if (stgm&~STGM_KNOWN_FLAGS)
6253 ERR("unknown flags %08lx\n", stgm);
6261 case STGM_READWRITE:
6269 case STGM_SHARE_DENY_NONE:
6270 case STGM_SHARE_DENY_READ:
6271 case STGM_SHARE_DENY_WRITE:
6272 case STGM_SHARE_EXCLUSIVE:
6281 case STGM_FAILIFTHERE:
6288 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6290 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6294 * STGM_CREATE | STGM_CONVERT
6295 * if both are false, STGM_FAILIFTHERE is set to TRUE
6297 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6301 * STGM_NOSCRATCH requires STGM_TRANSACTED
6303 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6307 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6308 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6310 if ( (stgm & STGM_NOSNAPSHOT) &&
6311 (!(stgm & STGM_TRANSACTED) ||
6312 share == STGM_SHARE_EXCLUSIVE ||
6313 share == STGM_SHARE_DENY_WRITE) )
6319 /****************************************************************************
6320 * GetShareModeFromSTGM
6322 * This method will return a share mode flag from a STGM value.
6323 * The STGM value is assumed valid.
6325 static DWORD GetShareModeFromSTGM(DWORD stgm)
6327 switch (STGM_SHARE_MODE(stgm))
6329 case STGM_SHARE_DENY_NONE:
6330 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6331 case STGM_SHARE_DENY_READ:
6332 return FILE_SHARE_WRITE;
6333 case STGM_SHARE_DENY_WRITE:
6334 return FILE_SHARE_READ;
6335 case STGM_SHARE_EXCLUSIVE:
6338 ERR("Invalid share mode!\n");
6343 /****************************************************************************
6344 * GetAccessModeFromSTGM
6346 * This method will return an access mode flag from a STGM value.
6347 * The STGM value is assumed valid.
6349 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6351 switch (STGM_ACCESS_MODE(stgm))
6354 return GENERIC_READ;
6356 case STGM_READWRITE:
6357 return GENERIC_READ | GENERIC_WRITE;
6359 ERR("Invalid access mode!\n");
6364 /****************************************************************************
6365 * GetCreationModeFromSTGM
6367 * This method will return a creation mode flag from a STGM value.
6368 * The STGM value is assumed valid.
6370 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6372 switch(STGM_CREATE_MODE(stgm))
6375 return CREATE_ALWAYS;
6377 FIXME("STGM_CONVERT not implemented!\n");
6379 case STGM_FAILIFTHERE:
6382 ERR("Invalid create mode!\n");
6388 /*************************************************************************
6389 * OLECONVERT_LoadOLE10 [Internal]
6391 * Loads the OLE10 STREAM to memory
6394 * pOleStream [I] The OLESTREAM
6395 * pData [I] Data Structure for the OLESTREAM Data
6399 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6400 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6403 * This function is used by OleConvertOLESTREAMToIStorage only.
6405 * Memory allocated for pData must be freed by the caller
6407 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6410 HRESULT hRes = S_OK;
6414 pData->pData = NULL;
6415 pData->pstrOleObjFileName = (CHAR *) NULL;
6417 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6420 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6421 if(dwSize != sizeof(pData->dwOleID))
6423 hRes = CONVERT10_E_OLESTREAM_GET;
6425 else if(pData->dwOleID != OLESTREAM_ID)
6427 hRes = CONVERT10_E_OLESTREAM_FMT;
6438 /* Get the TypeID...more info needed for this field */
6439 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6440 if(dwSize != sizeof(pData->dwTypeID))
6442 hRes = CONVERT10_E_OLESTREAM_GET;
6447 if(pData->dwTypeID != 0)
6449 /* Get the length of the OleTypeName */
6450 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6451 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6453 hRes = CONVERT10_E_OLESTREAM_GET;
6458 if(pData->dwOleTypeNameLength > 0)
6460 /* Get the OleTypeName */
6461 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6462 if(dwSize != pData->dwOleTypeNameLength)
6464 hRes = CONVERT10_E_OLESTREAM_GET;
6470 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6471 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6473 hRes = CONVERT10_E_OLESTREAM_GET;
6477 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6478 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6479 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6480 if(pData->pstrOleObjFileName)
6482 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6483 if(dwSize != pData->dwOleObjFileNameLength)
6485 hRes = CONVERT10_E_OLESTREAM_GET;
6489 hRes = CONVERT10_E_OLESTREAM_GET;
6494 /* Get the Width of the Metafile */
6495 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6496 if(dwSize != sizeof(pData->dwMetaFileWidth))
6498 hRes = CONVERT10_E_OLESTREAM_GET;
6502 /* Get the Height of the Metafile */
6503 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6504 if(dwSize != sizeof(pData->dwMetaFileHeight))
6506 hRes = CONVERT10_E_OLESTREAM_GET;
6512 /* Get the Length of the Data */
6513 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6514 if(dwSize != sizeof(pData->dwDataLength))
6516 hRes = CONVERT10_E_OLESTREAM_GET;
6520 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6522 if(!bStrem1) /* if it is a second OLE stream data */
6524 pData->dwDataLength -= 8;
6525 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6526 if(dwSize != sizeof(pData->strUnknown))
6528 hRes = CONVERT10_E_OLESTREAM_GET;
6534 if(pData->dwDataLength > 0)
6536 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6538 /* Get Data (ex. IStorage, Metafile, or BMP) */
6541 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6542 if(dwSize != pData->dwDataLength)
6544 hRes = CONVERT10_E_OLESTREAM_GET;
6549 hRes = CONVERT10_E_OLESTREAM_GET;
6558 /*************************************************************************
6559 * OLECONVERT_SaveOLE10 [Internal]
6561 * Saves the OLE10 STREAM From memory
6564 * pData [I] Data Structure for the OLESTREAM Data
6565 * pOleStream [I] The OLESTREAM to save
6569 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6572 * This function is used by OleConvertIStorageToOLESTREAM only.
6575 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6578 HRESULT hRes = S_OK;
6582 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6583 if(dwSize != sizeof(pData->dwOleID))
6585 hRes = CONVERT10_E_OLESTREAM_PUT;
6590 /* Set the TypeID */
6591 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6592 if(dwSize != sizeof(pData->dwTypeID))
6594 hRes = CONVERT10_E_OLESTREAM_PUT;
6598 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6600 /* Set the Length of the OleTypeName */
6601 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6602 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6604 hRes = CONVERT10_E_OLESTREAM_PUT;
6609 if(pData->dwOleTypeNameLength > 0)
6611 /* Set the OleTypeName */
6612 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6613 if(dwSize != pData->dwOleTypeNameLength)
6615 hRes = CONVERT10_E_OLESTREAM_PUT;
6622 /* Set the width of the Metafile */
6623 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6624 if(dwSize != sizeof(pData->dwMetaFileWidth))
6626 hRes = CONVERT10_E_OLESTREAM_PUT;
6632 /* Set the height of the Metafile */
6633 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6634 if(dwSize != sizeof(pData->dwMetaFileHeight))
6636 hRes = CONVERT10_E_OLESTREAM_PUT;
6642 /* Set the length of the Data */
6643 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6644 if(dwSize != sizeof(pData->dwDataLength))
6646 hRes = CONVERT10_E_OLESTREAM_PUT;
6652 if(pData->dwDataLength > 0)
6654 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6655 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6656 if(dwSize != pData->dwDataLength)
6658 hRes = CONVERT10_E_OLESTREAM_PUT;
6666 /*************************************************************************
6667 * OLECONVERT_GetOLE20FromOLE10[Internal]
6669 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6670 * opens it, and copies the content to the dest IStorage for
6671 * OleConvertOLESTREAMToIStorage
6675 * pDestStorage [I] The IStorage to copy the data to
6676 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6677 * nBufferLength [I] The size of the buffer
6686 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6690 IStorage *pTempStorage;
6691 DWORD dwNumOfBytesWritten;
6692 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6693 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6695 /* Create a temp File */
6696 GetTempPathW(MAX_PATH, wstrTempDir);
6697 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6698 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6700 if(hFile != INVALID_HANDLE_VALUE)
6702 /* Write IStorage Data to File */
6703 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6706 /* Open and copy temp storage to the Dest Storage */
6707 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6710 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6711 StorageBaseImpl_Release(pTempStorage);
6713 DeleteFileW(wstrTempFile);
6718 /*************************************************************************
6719 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6721 * Saves the OLE10 STREAM From memory
6724 * pStorage [I] The Src IStorage to copy
6725 * pData [I] The Dest Memory to write to.
6728 * The size in bytes allocated for pData
6731 * Memory allocated for pData must be freed by the caller
6733 * Used by OleConvertIStorageToOLESTREAM only.
6736 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6740 DWORD nDataLength = 0;
6741 IStorage *pTempStorage;
6742 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6743 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6747 /* Create temp Storage */
6748 GetTempPathW(MAX_PATH, wstrTempDir);
6749 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6750 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6754 /* Copy Src Storage to the Temp Storage */
6755 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6756 StorageBaseImpl_Release(pTempStorage);
6758 /* Open Temp Storage as a file and copy to memory */
6759 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6760 if(hFile != INVALID_HANDLE_VALUE)
6762 nDataLength = GetFileSize(hFile, NULL);
6763 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6764 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6767 DeleteFileW(wstrTempFile);
6772 /*************************************************************************
6773 * OLECONVERT_CreateOleStream [Internal]
6775 * Creates the "\001OLE" stream in the IStorage if necessary.
6778 * pStorage [I] Dest storage to create the stream in
6784 * This function is used by OleConvertOLESTREAMToIStorage only.
6786 * This stream is still unknown, MS Word seems to have extra data
6787 * but since the data is stored in the OLESTREAM there should be
6788 * no need to recreate the stream. If the stream is manually
6789 * deleted it will create it with this default data.
6792 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6796 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6797 BYTE pOleStreamHeader [] =
6799 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6800 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6801 0x00, 0x00, 0x00, 0x00
6804 /* Create stream if not present */
6805 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6806 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6810 /* Write default Data */
6811 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6812 IStream_Release(pStream);
6816 /* write a string to a stream, preceded by its length */
6817 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6824 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6825 r = IStream_Write( stm, &len, sizeof(len), NULL);
6830 str = CoTaskMemAlloc( len );
6831 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6832 r = IStream_Write( stm, str, len, NULL);
6833 CoTaskMemFree( str );
6837 /* read a string preceded by its length from a stream */
6838 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6841 DWORD len, count = 0;
6845 r = IStream_Read( stm, &len, sizeof(len), &count );
6848 if( count != sizeof(len) )
6849 return E_OUTOFMEMORY;
6851 TRACE("%ld bytes\n",len);
6853 str = CoTaskMemAlloc( len );
6855 return E_OUTOFMEMORY;
6857 r = IStream_Read( stm, str, len, &count );
6862 CoTaskMemFree( str );
6863 return E_OUTOFMEMORY;
6866 TRACE("Read string %s\n",debugstr_an(str,len));
6868 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6869 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6871 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6872 CoTaskMemFree( str );
6880 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6881 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6885 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6887 static const BYTE unknown1[12] =
6888 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6889 0xFF, 0xFF, 0xFF, 0xFF};
6890 static const BYTE unknown2[16] =
6891 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6892 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6894 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6895 debugstr_w(lpszUserType), debugstr_w(szClipName),
6896 debugstr_w(szProgIDName));
6898 /* Create a CompObj stream if it doesn't exist */
6899 r = IStorage_CreateStream(pstg, szwStreamName,
6900 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6904 /* Write CompObj Structure to stream */
6905 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6907 if( SUCCEEDED( r ) )
6908 r = WriteClassStm( pstm, clsid );
6910 if( SUCCEEDED( r ) )
6911 r = STREAM_WriteString( pstm, lpszUserType );
6912 if( SUCCEEDED( r ) )
6913 r = STREAM_WriteString( pstm, szClipName );
6914 if( SUCCEEDED( r ) )
6915 r = STREAM_WriteString( pstm, szProgIDName );
6916 if( SUCCEEDED( r ) )
6917 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6919 IStream_Release( pstm );
6924 /***********************************************************************
6925 * WriteFmtUserTypeStg (OLE32.@)
6927 HRESULT WINAPI WriteFmtUserTypeStg(
6928 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6931 WCHAR szwClipName[0x40];
6932 CLSID clsid = CLSID_NULL;
6933 LPWSTR wstrProgID = NULL;
6936 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6938 /* get the clipboard format name */
6939 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
6942 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6944 /* FIXME: There's room to save a CLSID and its ProgID, but
6945 the CLSID is not looked up in the registry and in all the
6946 tests I wrote it was CLSID_NULL. Where does it come from?
6949 /* get the real program ID. This may fail, but that's fine */
6950 ProgIDFromCLSID(&clsid, &wstrProgID);
6952 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6954 r = STORAGE_WriteCompObj( pstg, &clsid,
6955 lpszUserType, szwClipName, wstrProgID );
6957 CoTaskMemFree(wstrProgID);
6963 /******************************************************************************
6964 * ReadFmtUserTypeStg [OLE32.@]
6966 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6970 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6971 unsigned char unknown1[12];
6972 unsigned char unknown2[16];
6974 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6977 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6979 r = IStorage_OpenStream( pstg, szCompObj, NULL,
6980 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6983 WARN("Failed to open stream r = %08lx\n", r);
6987 /* read the various parts of the structure */
6988 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
6989 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
6991 r = ReadClassStm( stm, &clsid );
6995 r = STREAM_ReadString( stm, &szCLSIDName );
6999 r = STREAM_ReadString( stm, &szOleTypeName );
7003 r = STREAM_ReadString( stm, &szProgIDName );
7007 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7008 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7011 /* ok, success... now we just need to store what we found */
7013 *pcf = RegisterClipboardFormatW( szOleTypeName );
7014 CoTaskMemFree( szOleTypeName );
7016 if( lplpszUserType )
7017 *lplpszUserType = szCLSIDName;
7018 CoTaskMemFree( szProgIDName );
7021 IStream_Release( stm );
7027 /*************************************************************************
7028 * OLECONVERT_CreateCompObjStream [Internal]
7030 * Creates a "\001CompObj" is the destination IStorage if necessary.
7033 * pStorage [I] The dest IStorage to create the CompObj Stream
7035 * strOleTypeName [I] The ProgID
7039 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7042 * This function is used by OleConvertOLESTREAMToIStorage only.
7044 * The stream data is stored in the OLESTREAM and there should be
7045 * no need to recreate the stream. If the stream is manually
7046 * deleted it will attempt to create it by querying the registry.
7050 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7053 HRESULT hStorageRes, hRes = S_OK;
7054 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7055 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7056 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7058 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7059 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7061 /* Initialize the CompObj structure */
7062 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7063 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7064 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7067 /* Create a CompObj stream if it doesn't exist */
7068 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7069 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7070 if(hStorageRes == S_OK)
7072 /* copy the OleTypeName to the compobj struct */
7073 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7074 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7076 /* copy the OleTypeName to the compobj struct */
7077 /* Note: in the test made, these were Identical */
7078 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7079 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7082 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7083 bufferW, OLESTREAM_MAX_STR_LEN );
7084 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7090 /* Get the CLSID Default Name from the Registry */
7091 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7092 if(hErr == ERROR_SUCCESS)
7094 char strTemp[OLESTREAM_MAX_STR_LEN];
7095 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7096 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
7097 if(hErr == ERROR_SUCCESS)
7099 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7105 /* Write CompObj Structure to stream */
7106 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7108 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7110 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7111 if(IStorageCompObj.dwCLSIDNameLength > 0)
7113 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7115 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7116 if(IStorageCompObj.dwOleTypeNameLength > 0)
7118 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7120 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7121 if(IStorageCompObj.dwProgIDNameLength > 0)
7123 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7125 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7126 IStream_Release(pStream);
7132 /*************************************************************************
7133 * OLECONVERT_CreateOlePresStream[Internal]
7135 * Creates the "\002OlePres000" Stream with the Metafile data
7138 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7139 * dwExtentX [I] Width of the Metafile
7140 * dwExtentY [I] Height of the Metafile
7141 * pData [I] Metafile data
7142 * dwDataLength [I] Size of the Metafile data
7146 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7149 * This function is used by OleConvertOLESTREAMToIStorage only.
7152 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7156 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7157 BYTE pOlePresStreamHeader [] =
7159 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7160 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7161 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7162 0x00, 0x00, 0x00, 0x00
7165 BYTE pOlePresStreamHeaderEmpty [] =
7167 0x00, 0x00, 0x00, 0x00,
7168 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7169 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7170 0x00, 0x00, 0x00, 0x00
7173 /* Create the OlePres000 Stream */
7174 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7175 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7180 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7182 memset(&OlePres, 0, sizeof(OlePres));
7183 /* Do we have any metafile data to save */
7184 if(dwDataLength > 0)
7186 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7187 nHeaderSize = sizeof(pOlePresStreamHeader);
7191 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7192 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7194 /* Set width and height of the metafile */
7195 OlePres.dwExtentX = dwExtentX;
7196 OlePres.dwExtentY = -dwExtentY;
7198 /* Set Data and Length */
7199 if(dwDataLength > sizeof(METAFILEPICT16))
7201 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7202 OlePres.pData = &(pData[8]);
7204 /* Save OlePres000 Data to Stream */
7205 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7206 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7207 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7208 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7209 if(OlePres.dwSize > 0)
7211 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7213 IStream_Release(pStream);
7217 /*************************************************************************
7218 * OLECONVERT_CreateOle10NativeStream [Internal]
7220 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7223 * pStorage [I] Dest storage to create the stream in
7224 * pData [I] Ole10 Native Data (ex. bmp)
7225 * dwDataLength [I] Size of the Ole10 Native Data
7231 * This function is used by OleConvertOLESTREAMToIStorage only.
7233 * Might need to verify the data and return appropriate error message
7236 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7240 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7242 /* Create the Ole10Native Stream */
7243 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7244 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7248 /* Write info to stream */
7249 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7250 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7251 IStream_Release(pStream);
7256 /*************************************************************************
7257 * OLECONVERT_GetOLE10ProgID [Internal]
7259 * Finds the ProgID (or OleTypeID) from the IStorage
7262 * pStorage [I] The Src IStorage to get the ProgID
7263 * strProgID [I] the ProgID string to get
7264 * dwSize [I] the size of the string
7268 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7271 * This function is used by OleConvertIStorageToOLESTREAM only.
7275 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7279 LARGE_INTEGER iSeekPos;
7280 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7281 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7283 /* Open the CompObj Stream */
7284 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7285 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7289 /*Get the OleType from the CompObj Stream */
7290 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7291 iSeekPos.u.HighPart = 0;
7293 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7294 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7295 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7296 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7297 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7298 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7299 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7301 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7304 IStream_Read(pStream, strProgID, *dwSize, NULL);
7306 IStream_Release(pStream);
7311 LPOLESTR wstrProgID;
7313 /* Get the OleType from the registry */
7314 REFCLSID clsid = &(stat.clsid);
7315 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7316 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7319 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7326 /*************************************************************************
7327 * OLECONVERT_GetOle10PresData [Internal]
7329 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7332 * pStorage [I] Src IStroage
7333 * pOleStream [I] Dest OleStream Mem Struct
7339 * This function is used by OleConvertIStorageToOLESTREAM only.
7341 * Memory allocated for pData must be freed by the caller
7345 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7350 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7352 /* Initialize Default data for OLESTREAM */
7353 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7354 pOleStreamData[0].dwTypeID = 2;
7355 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7356 pOleStreamData[1].dwTypeID = 0;
7357 pOleStreamData[0].dwMetaFileWidth = 0;
7358 pOleStreamData[0].dwMetaFileHeight = 0;
7359 pOleStreamData[0].pData = NULL;
7360 pOleStreamData[1].pData = NULL;
7362 /* Open Ole10Native Stream */
7363 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7364 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7368 /* Read Size and Data */
7369 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7370 if(pOleStreamData->dwDataLength > 0)
7372 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7373 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7375 IStream_Release(pStream);
7381 /*************************************************************************
7382 * OLECONVERT_GetOle20PresData[Internal]
7384 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7387 * pStorage [I] Src IStroage
7388 * pOleStreamData [I] Dest OleStream Mem Struct
7394 * This function is used by OleConvertIStorageToOLESTREAM only.
7396 * Memory allocated for pData must be freed by the caller
7398 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7402 OLECONVERT_ISTORAGE_OLEPRES olePress;
7403 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7405 /* Initialize Default data for OLESTREAM */
7406 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7407 pOleStreamData[0].dwTypeID = 2;
7408 pOleStreamData[0].dwMetaFileWidth = 0;
7409 pOleStreamData[0].dwMetaFileHeight = 0;
7410 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7411 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7412 pOleStreamData[1].dwTypeID = 0;
7413 pOleStreamData[1].dwOleTypeNameLength = 0;
7414 pOleStreamData[1].strOleTypeName[0] = 0;
7415 pOleStreamData[1].dwMetaFileWidth = 0;
7416 pOleStreamData[1].dwMetaFileHeight = 0;
7417 pOleStreamData[1].pData = NULL;
7418 pOleStreamData[1].dwDataLength = 0;
7421 /* Open OlePress000 stream */
7422 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7423 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7426 LARGE_INTEGER iSeekPos;
7427 METAFILEPICT16 MetaFilePict;
7428 static const char strMetafilePictName[] = "METAFILEPICT";
7430 /* Set the TypeID for a Metafile */
7431 pOleStreamData[1].dwTypeID = 5;
7433 /* Set the OleTypeName to Metafile */
7434 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7435 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7437 iSeekPos.u.HighPart = 0;
7438 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7440 /* Get Presentation Data */
7441 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7442 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7443 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7444 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7446 /*Set width and Height */
7447 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7448 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7449 if(olePress.dwSize > 0)
7452 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7454 /* Set MetaFilePict struct */
7455 MetaFilePict.mm = 8;
7456 MetaFilePict.xExt = olePress.dwExtentX;
7457 MetaFilePict.yExt = olePress.dwExtentY;
7458 MetaFilePict.hMF = 0;
7460 /* Get Metafile Data */
7461 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7462 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7463 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7465 IStream_Release(pStream);
7469 /*************************************************************************
7470 * OleConvertOLESTREAMToIStorage [OLE32.@]
7475 * DVTARGETDEVICE paramenter is not handled
7476 * Still unsure of some mem fields for OLE 10 Stream
7477 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7478 * and "\001OLE" streams
7481 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7482 LPOLESTREAM pOleStream,
7484 const DVTARGETDEVICE* ptd)
7488 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7490 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7494 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7497 if(pstg == NULL || pOleStream == NULL)
7499 hRes = E_INVALIDARG;
7504 /* Load the OLESTREAM to Memory */
7505 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7510 /* Load the OLESTREAM to Memory (part 2)*/
7511 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7517 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7519 /* Do we have the IStorage Data in the OLESTREAM */
7520 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7522 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7523 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7527 /* It must be an original OLE 1.0 source */
7528 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7533 /* It must be an original OLE 1.0 source */
7534 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7537 /* Create CompObj Stream if necessary */
7538 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7541 /*Create the Ole Stream if necessary */
7542 OLECONVERT_CreateOleStream(pstg);
7547 /* Free allocated memory */
7548 for(i=0; i < 2; i++)
7550 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7551 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7552 pOleStreamData[i].pstrOleObjFileName = NULL;
7557 /*************************************************************************
7558 * OleConvertIStorageToOLESTREAM [OLE32.@]
7565 * Still unsure of some mem fields for OLE 10 Stream
7566 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7567 * and "\001OLE" streams.
7570 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7572 LPOLESTREAM pOleStream)
7575 HRESULT hRes = S_OK;
7577 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7578 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7581 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7583 if(pstg == NULL || pOleStream == NULL)
7585 hRes = E_INVALIDARG;
7589 /* Get the ProgID */
7590 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7591 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7595 /* Was it originally Ole10 */
7596 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7599 IStream_Release(pStream);
7600 /* Get Presentation Data for Ole10Native */
7601 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7605 /* Get Presentation Data (OLE20) */
7606 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7609 /* Save OLESTREAM */
7610 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7613 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7618 /* Free allocated memory */
7619 for(i=0; i < 2; i++)
7621 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7627 /***********************************************************************
7628 * GetConvertStg (OLE32.@)
7630 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7631 FIXME("unimplemented stub!\n");
7635 /******************************************************************************
7636 * StgIsStorageFile [OLE32.@]
7639 StgIsStorageFile(LPCOLESTR fn)
7645 TRACE("(\'%s\')\n", debugstr_w(fn));
7646 hf = CreateFileW(fn, GENERIC_READ,
7647 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7648 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7650 if (hf == INVALID_HANDLE_VALUE)
7651 return STG_E_FILENOTFOUND;
7653 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7655 WARN(" unable to read file\n");
7662 if (bytes_read != 8) {
7663 WARN(" too short\n");
7667 if (!memcmp(magic,STORAGE_magic,8)) {
7672 WARN(" -> Invalid header.\n");