2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
33 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/istorage_compound_file_implementation.asp
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
50 #include "wine/unicode.h"
51 #include "wine/debug.h"
53 #include "storage32.h"
54 #include "ole2.h" /* For Write/ReadClassStm */
57 #include "wine/wingdi16.h"
59 WINE_DEFAULT_DEBUG_CHANNEL(storage);
61 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
62 #define OLESTREAM_ID 0x501
63 #define OLESTREAM_MAX_STR_LEN 255
65 static const char rootPropertyName[] = "Root Entry";
68 /* OLESTREAM memory structure to use for Get and Put Routines */
69 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
74 DWORD dwOleTypeNameLength;
75 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
76 CHAR *pstrOleObjFileName;
77 DWORD dwOleObjFileNameLength;
78 DWORD dwMetaFileWidth;
79 DWORD dwMetaFileHeight;
80 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
83 }OLECONVERT_OLESTREAM_DATA;
85 /* CompObj Stream structure */
86 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
91 DWORD dwCLSIDNameLength;
92 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
93 DWORD dwOleTypeNameLength;
94 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
95 DWORD dwProgIDNameLength;
96 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
98 }OLECONVERT_ISTORAGE_COMPOBJ;
101 /* Ole Presention Stream structure */
102 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
110 }OLECONVERT_ISTORAGE_OLEPRES;
114 /***********************************************************************
115 * Forward declaration of internal functions used by the method DestroyElement
117 static HRESULT deleteStorageProperty(
118 StorageImpl *parentStorage,
119 ULONG foundPropertyIndexToDelete,
120 StgProperty propertyToDelete);
122 static HRESULT deleteStreamProperty(
123 StorageImpl *parentStorage,
124 ULONG foundPropertyIndexToDelete,
125 StgProperty propertyToDelete);
127 static HRESULT findPlaceholder(
128 StorageImpl *storage,
129 ULONG propertyIndexToStore,
130 ULONG storagePropertyIndex,
133 static HRESULT adjustPropertyChain(
135 StgProperty propertyToDelete,
136 StgProperty parentProperty,
137 ULONG parentPropertyId,
140 /***********************************************************************
141 * Declaration of the functions used to manipulate StgProperty
144 static ULONG getFreeProperty(
145 StorageImpl *storage);
147 static void updatePropertyChain(
148 StorageImpl *storage,
149 ULONG newPropertyIndex,
150 StgProperty newProperty);
152 static LONG propertyNameCmp(
153 const OLECHAR *newProperty,
154 const OLECHAR *currentProperty);
157 /***********************************************************************
158 * Declaration of miscellaneous functions...
160 static HRESULT validateSTGM(DWORD stgmValue);
162 static DWORD GetShareModeFromSTGM(DWORD stgm);
163 static DWORD GetAccessModeFromSTGM(DWORD stgm);
164 static DWORD GetCreationModeFromSTGM(DWORD stgm);
166 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
170 /************************************************************************
171 ** Storage32BaseImpl implementatiion
174 /************************************************************************
175 * Storage32BaseImpl_QueryInterface (IUnknown)
177 * This method implements the common QueryInterface for all IStorage32
178 * implementations contained in this file.
180 * See Windows documentation for more details on IUnknown methods.
182 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
187 StorageBaseImpl *This = (StorageBaseImpl *)iface;
189 * Perform a sanity check on the parameters.
191 if ( (This==0) || (ppvObject==0) )
195 * Initialize the return parameter.
200 * Compare the riid with the interface IDs implemented by this object.
202 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
204 *ppvObject = (IStorage*)This;
206 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
208 *ppvObject = (IStorage*)This;
210 else if (memcmp(&IID_IPropertySetStorage, riid, sizeof(IID_IPropertySetStorage)) == 0)
212 *ppvObject = (IStorage*)&This->pssVtbl;
216 * Check that we obtained an interface.
219 return E_NOINTERFACE;
222 * Query Interface always increases the reference count by one when it is
225 IStorage_AddRef(iface);
230 /************************************************************************
231 * Storage32BaseImpl_AddRef (IUnknown)
233 * This method implements the common AddRef for all IStorage32
234 * implementations contained in this file.
236 * See Windows documentation for more details on IUnknown methods.
238 static ULONG WINAPI StorageBaseImpl_AddRef(
241 StorageBaseImpl *This = (StorageBaseImpl *)iface;
242 ULONG ref = InterlockedIncrement(&This->ref);
244 TRACE("(%p) AddRef to %ld\n", This, ref);
249 /************************************************************************
250 * Storage32BaseImpl_Release (IUnknown)
252 * This method implements the common Release for all IStorage32
253 * implementations contained in this file.
255 * See Windows documentation for more details on IUnknown methods.
257 static ULONG WINAPI StorageBaseImpl_Release(
260 StorageBaseImpl *This = (StorageBaseImpl *)iface;
262 * Decrease the reference count on this object.
264 ULONG ref = InterlockedDecrement(&This->ref);
266 TRACE("(%p) ReleaseRef to %ld\n", This, ref);
269 * If the reference count goes down to 0, perform suicide.
274 * Since we are using a system of base-classes, we want to call the
275 * destructor of the appropriate derived class. To do this, we are
276 * using virtual functions to implement the destructor.
278 This->v_destructor(This);
284 /************************************************************************
285 * Storage32BaseImpl_OpenStream (IStorage)
287 * This method will open the specified stream object from the current storage.
289 * See Windows documentation for more details on IStorage methods.
291 static HRESULT WINAPI StorageBaseImpl_OpenStream(
293 const OLECHAR* pwcsName, /* [string][in] */
294 void* reserved1, /* [unique][in] */
295 DWORD grfMode, /* [in] */
296 DWORD reserved2, /* [in] */
297 IStream** ppstm) /* [out] */
299 StorageBaseImpl *This = (StorageBaseImpl *)iface;
300 IEnumSTATSTGImpl* propertyEnumeration;
301 StgStreamImpl* newStream;
302 StgProperty currentProperty;
303 ULONG foundPropertyIndex;
304 HRESULT res = STG_E_UNKNOWN;
306 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
307 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
310 * Perform a sanity check on the parameters.
312 if ( (pwcsName==NULL) || (ppstm==0) )
319 * Initialize the out parameter
324 * Validate the STGM flags
326 if ( FAILED( validateSTGM(grfMode) ))
328 res = STG_E_INVALIDFLAG;
335 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
336 (grfMode & STGM_DELETEONRELEASE) ||
337 (grfMode & STGM_TRANSACTED) )
339 res = STG_E_INVALIDFUNCTION;
344 * Check that we're compatible with the parent's storage mode, but
345 * only if we are not in transacted mode
347 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
348 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
350 res = STG_E_ACCESSDENIED;
356 * Create a property enumeration to search the properties
358 propertyEnumeration = IEnumSTATSTGImpl_Construct(
359 This->ancestorStorage,
360 This->rootPropertySetIndex);
363 * Search the enumeration for the property with the given name
365 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
371 * Delete the property enumeration since we don't need it anymore
373 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
376 * If it was found, construct the stream object and return a pointer to it.
378 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
379 (currentProperty.propertyType==PROPTYPE_STREAM) )
381 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
385 newStream->grfMode = grfMode;
386 *ppstm = (IStream*)newStream;
389 * Since we are returning a pointer to the interface, we have to
390 * nail down the reference.
392 IStream_AddRef(*ppstm);
395 * add us to the storage's list of active streams
398 StorageBaseImpl_AddStream(This,newStream);
408 res = STG_E_FILENOTFOUND;
412 TRACE("<-- IStream %p\n", *ppstm);
413 TRACE("<-- %08lx\n", res);
417 /************************************************************************
418 * Storage32BaseImpl_OpenStorage (IStorage)
420 * This method will open a new storage object from the current storage.
422 * See Windows documentation for more details on IStorage methods.
424 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
426 const OLECHAR* pwcsName, /* [string][unique][in] */
427 IStorage* pstgPriority, /* [unique][in] */
428 DWORD grfMode, /* [in] */
429 SNB snbExclude, /* [unique][in] */
430 DWORD reserved, /* [in] */
431 IStorage** ppstg) /* [out] */
433 StorageBaseImpl *This = (StorageBaseImpl *)iface;
434 StorageInternalImpl* newStorage;
435 IEnumSTATSTGImpl* propertyEnumeration;
436 StgProperty currentProperty;
437 ULONG foundPropertyIndex;
438 HRESULT res = STG_E_UNKNOWN;
440 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
441 iface, debugstr_w(pwcsName), pstgPriority,
442 grfMode, snbExclude, reserved, ppstg);
445 * Perform a sanity check on the parameters.
447 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
454 if (snbExclude != NULL)
456 res = STG_E_INVALIDPARAMETER;
461 * Validate the STGM flags
463 if ( FAILED( validateSTGM(grfMode) ))
465 res = STG_E_INVALIDFLAG;
472 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
473 (grfMode & STGM_DELETEONRELEASE) ||
474 (grfMode & STGM_PRIORITY) )
476 res = STG_E_INVALIDFUNCTION;
481 * Check that we're compatible with the parent's storage mode,
482 * but only if we are not transacted
484 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
485 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
487 res = STG_E_ACCESSDENIED;
493 * Initialize the out parameter
498 * Create a property enumeration to search the properties
500 propertyEnumeration = IEnumSTATSTGImpl_Construct(
501 This->ancestorStorage,
502 This->rootPropertySetIndex);
505 * Search the enumeration for the property with the given name
507 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
513 * Delete the property enumeration since we don't need it anymore
515 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
518 * If it was found, construct the stream object and return a pointer to it.
520 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
521 (currentProperty.propertyType==PROPTYPE_STORAGE) )
524 * Construct a new Storage object
526 newStorage = StorageInternalImpl_Construct(
527 This->ancestorStorage,
533 *ppstg = (IStorage*)newStorage;
536 * Since we are returning a pointer to the interface,
537 * we have to nail down the reference.
539 StorageBaseImpl_AddRef(*ppstg);
545 res = STG_E_INSUFFICIENTMEMORY;
549 res = STG_E_FILENOTFOUND;
552 TRACE("<-- %08lx\n", res);
556 /************************************************************************
557 * Storage32BaseImpl_EnumElements (IStorage)
559 * This method will create an enumerator object that can be used to
560 * retrieve informatino about all the properties in the storage object.
562 * See Windows documentation for more details on IStorage methods.
564 static HRESULT WINAPI StorageBaseImpl_EnumElements(
566 DWORD reserved1, /* [in] */
567 void* reserved2, /* [size_is][unique][in] */
568 DWORD reserved3, /* [in] */
569 IEnumSTATSTG** ppenum) /* [out] */
571 StorageBaseImpl *This = (StorageBaseImpl *)iface;
572 IEnumSTATSTGImpl* newEnum;
574 TRACE("(%p, %ld, %p, %ld, %p)\n",
575 iface, reserved1, reserved2, reserved3, ppenum);
578 * Perform a sanity check on the parameters.
580 if ( (This==0) || (ppenum==0))
584 * Construct the enumerator.
586 newEnum = IEnumSTATSTGImpl_Construct(
587 This->ancestorStorage,
588 This->rootPropertySetIndex);
592 *ppenum = (IEnumSTATSTG*)newEnum;
595 * Don't forget to nail down a reference to the new object before
598 IEnumSTATSTG_AddRef(*ppenum);
603 return E_OUTOFMEMORY;
606 /************************************************************************
607 * Storage32BaseImpl_Stat (IStorage)
609 * This method will retrieve information about this storage object.
611 * See Windows documentation for more details on IStorage methods.
613 static HRESULT WINAPI StorageBaseImpl_Stat(
615 STATSTG* pstatstg, /* [out] */
616 DWORD grfStatFlag) /* [in] */
618 StorageBaseImpl *This = (StorageBaseImpl *)iface;
619 StgProperty curProperty;
621 HRESULT res = STG_E_UNKNOWN;
623 TRACE("(%p, %p, %lx)\n",
624 iface, pstatstg, grfStatFlag);
627 * Perform a sanity check on the parameters.
629 if ( (This==0) || (pstatstg==0))
636 * Read the information from the property.
638 readSuccessful = StorageImpl_ReadProperty(
639 This->ancestorStorage,
640 This->rootPropertySetIndex,
645 StorageUtl_CopyPropertyToSTATSTG(
650 pstatstg->grfMode = This->openFlags;
661 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);
663 TRACE("<-- %08lx\n", res);
667 /************************************************************************
668 * Storage32BaseImpl_RenameElement (IStorage)
670 * This method will rename the specified element.
672 * See Windows documentation for more details on IStorage methods.
674 * Implementation notes: The method used to rename consists of creating a clone
675 * of the deleted StgProperty object setting it with the new name and to
676 * perform a DestroyElement of the old StgProperty.
678 static HRESULT WINAPI StorageBaseImpl_RenameElement(
680 const OLECHAR* pwcsOldName, /* [in] */
681 const OLECHAR* pwcsNewName) /* [in] */
683 StorageBaseImpl *This = (StorageBaseImpl *)iface;
684 IEnumSTATSTGImpl* propertyEnumeration;
685 StgProperty currentProperty;
686 ULONG foundPropertyIndex;
688 TRACE("(%p, %s, %s)\n",
689 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
692 * Create a property enumeration to search the properties
694 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
695 This->rootPropertySetIndex);
698 * Search the enumeration for the new property name
700 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
704 if (foundPropertyIndex != PROPERTY_NULL)
707 * There is already a property with the new name
709 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
710 return STG_E_FILEALREADYEXISTS;
713 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
716 * Search the enumeration for the old property name
718 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
723 * Delete the property enumeration since we don't need it anymore
725 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
727 if (foundPropertyIndex != PROPERTY_NULL)
729 StgProperty renamedProperty;
730 ULONG renamedPropertyIndex;
733 * Setup a new property for the renamed property
735 renamedProperty.sizeOfNameString =
736 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
738 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
739 return STG_E_INVALIDNAME;
741 strcpyW(renamedProperty.name, pwcsNewName);
743 renamedProperty.propertyType = currentProperty.propertyType;
744 renamedProperty.startingBlock = currentProperty.startingBlock;
745 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
746 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
748 renamedProperty.previousProperty = PROPERTY_NULL;
749 renamedProperty.nextProperty = PROPERTY_NULL;
752 * Bring the dirProperty link in case it is a storage and in which
753 * case the renamed storage elements don't require to be reorganized.
755 renamedProperty.dirProperty = currentProperty.dirProperty;
757 /* call CoFileTime to get the current time
758 renamedProperty.timeStampS1
759 renamedProperty.timeStampD1
760 renamedProperty.timeStampS2
761 renamedProperty.timeStampD2
762 renamedProperty.propertyUniqueID
766 * Obtain a free property in the property chain
768 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
771 * Save the new property into the new property spot
773 StorageImpl_WriteProperty(
774 This->ancestorStorage,
775 renamedPropertyIndex,
779 * Find a spot in the property chain for our newly created property.
783 renamedPropertyIndex,
787 * At this point the renamed property has been inserted in the tree,
788 * now, before Destroying the old property we must zero its dirProperty
789 * otherwise the DestroyProperty below will zap it all and we do not want
791 * Also, we fake that the old property is a storage so the DestroyProperty
792 * will not do a SetSize(0) on the stream data.
794 * This means that we need to tweak the StgProperty if it is a stream or a
797 StorageImpl_ReadProperty(This->ancestorStorage,
801 currentProperty.dirProperty = PROPERTY_NULL;
802 currentProperty.propertyType = PROPTYPE_STORAGE;
803 StorageImpl_WriteProperty(
804 This->ancestorStorage,
809 * Invoke Destroy to get rid of the ole property and automatically redo
810 * the linking of its previous and next members...
812 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
818 * There is no property with the old name
820 return STG_E_FILENOTFOUND;
826 /************************************************************************
827 * Storage32BaseImpl_CreateStream (IStorage)
829 * This method will create a stream object within this storage
831 * See Windows documentation for more details on IStorage methods.
833 static HRESULT WINAPI StorageBaseImpl_CreateStream(
835 const OLECHAR* pwcsName, /* [string][in] */
836 DWORD grfMode, /* [in] */
837 DWORD reserved1, /* [in] */
838 DWORD reserved2, /* [in] */
839 IStream** ppstm) /* [out] */
841 StorageBaseImpl *This = (StorageBaseImpl *)iface;
842 IEnumSTATSTGImpl* propertyEnumeration;
843 StgStreamImpl* newStream;
844 StgProperty currentProperty, newStreamProperty;
845 ULONG foundPropertyIndex, newPropertyIndex;
847 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
848 iface, debugstr_w(pwcsName), grfMode,
849 reserved1, reserved2, ppstm);
852 * Validate parameters
855 return STG_E_INVALIDPOINTER;
858 return STG_E_INVALIDNAME;
860 if (reserved1 || reserved2)
861 return STG_E_INVALIDPARAMETER;
864 * Validate the STGM flags
866 if ( FAILED( validateSTGM(grfMode) ))
867 return STG_E_INVALIDFLAG;
869 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
870 return STG_E_INVALIDFLAG;
875 if ((grfMode & STGM_DELETEONRELEASE) ||
876 (grfMode & STGM_TRANSACTED))
877 return STG_E_INVALIDFUNCTION;
880 * Check that we're compatible with the parent's storage mode
881 * if not in transacted mode
883 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
884 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
885 return STG_E_ACCESSDENIED;
889 * Initialize the out parameter
894 * Create a property enumeration to search the properties
896 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
897 This->rootPropertySetIndex);
899 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
903 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
905 if (foundPropertyIndex != PROPERTY_NULL)
908 * An element with this name already exists
910 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
912 IStorage_DestroyElement(iface, pwcsName);
915 return STG_E_FILEALREADYEXISTS;
917 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
919 WARN("read-only storage\n");
920 return STG_E_ACCESSDENIED;
924 * memset the empty property
926 memset(&newStreamProperty, 0, sizeof(StgProperty));
928 newStreamProperty.sizeOfNameString =
929 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
931 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
932 return STG_E_INVALIDNAME;
934 strcpyW(newStreamProperty.name, pwcsName);
936 newStreamProperty.propertyType = PROPTYPE_STREAM;
937 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
938 newStreamProperty.size.u.LowPart = 0;
939 newStreamProperty.size.u.HighPart = 0;
941 newStreamProperty.previousProperty = PROPERTY_NULL;
942 newStreamProperty.nextProperty = PROPERTY_NULL;
943 newStreamProperty.dirProperty = PROPERTY_NULL;
945 /* call CoFileTime to get the current time
946 newStreamProperty.timeStampS1
947 newStreamProperty.timeStampD1
948 newStreamProperty.timeStampS2
949 newStreamProperty.timeStampD2
952 /* newStreamProperty.propertyUniqueID */
955 * Get a free property or create a new one
957 newPropertyIndex = getFreeProperty(This->ancestorStorage);
960 * Save the new property into the new property spot
962 StorageImpl_WriteProperty(
963 This->ancestorStorage,
968 * Find a spot in the property chain for our newly created property.
976 * Open the stream to return it.
978 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
982 *ppstm = (IStream*)newStream;
985 * Since we are returning a pointer to the interface, we have to nail down
988 IStream_AddRef(*ppstm);
990 /* add us to the storage's list of active streams
992 StorageBaseImpl_AddStream(This,newStream);
997 return STG_E_INSUFFICIENTMEMORY;
1003 /************************************************************************
1004 * Storage32BaseImpl_SetClass (IStorage)
1006 * This method will write the specified CLSID in the property of this
1009 * See Windows documentation for more details on IStorage methods.
1011 static HRESULT WINAPI StorageBaseImpl_SetClass(
1013 REFCLSID clsid) /* [in] */
1015 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1016 HRESULT hRes = E_FAIL;
1017 StgProperty curProperty;
1020 TRACE("(%p, %p)\n", iface, clsid);
1022 success = StorageImpl_ReadProperty(This->ancestorStorage,
1023 This->rootPropertySetIndex,
1027 curProperty.propertyUniqueID = *clsid;
1029 success = StorageImpl_WriteProperty(This->ancestorStorage,
1030 This->rootPropertySetIndex,
1039 /************************************************************************
1040 ** Storage32Impl implementation
1043 /************************************************************************
1044 * Storage32Impl_CreateStorage (IStorage)
1046 * This method will create the storage object within the provided storage.
1048 * See Windows documentation for more details on IStorage methods.
1050 static HRESULT WINAPI StorageImpl_CreateStorage(
1052 const OLECHAR *pwcsName, /* [string][in] */
1053 DWORD grfMode, /* [in] */
1054 DWORD reserved1, /* [in] */
1055 DWORD reserved2, /* [in] */
1056 IStorage **ppstg) /* [out] */
1058 StorageImpl* const This=(StorageImpl*)iface;
1060 IEnumSTATSTGImpl *propertyEnumeration;
1061 StgProperty currentProperty;
1062 StgProperty newProperty;
1063 ULONG foundPropertyIndex;
1064 ULONG newPropertyIndex;
1067 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1068 iface, debugstr_w(pwcsName), grfMode,
1069 reserved1, reserved2, ppstg);
1072 * Validate parameters
1075 return STG_E_INVALIDPOINTER;
1078 return STG_E_INVALIDNAME;
1081 * Initialize the out parameter
1086 * Validate the STGM flags
1088 if ( FAILED( validateSTGM(grfMode) ) ||
1089 (grfMode & STGM_DELETEONRELEASE) )
1091 WARN("bad grfMode: 0x%lx\n", grfMode);
1092 return STG_E_INVALIDFLAG;
1096 * Check that we're compatible with the parent's storage mode
1098 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1100 WARN("access denied\n");
1101 return STG_E_ACCESSDENIED;
1105 * Create a property enumeration and search the properties
1107 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1108 This->base.rootPropertySetIndex);
1110 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1113 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1115 if (foundPropertyIndex != PROPERTY_NULL)
1118 * An element with this name already exists
1120 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1121 IStorage_DestroyElement(iface, pwcsName);
1124 WARN("file already exists\n");
1125 return STG_E_FILEALREADYEXISTS;
1128 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1130 WARN("read-only storage\n");
1131 return STG_E_ACCESSDENIED;
1135 * memset the empty property
1137 memset(&newProperty, 0, sizeof(StgProperty));
1139 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1141 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1143 FIXME("name too long\n");
1144 return STG_E_INVALIDNAME;
1147 strcpyW(newProperty.name, pwcsName);
1149 newProperty.propertyType = PROPTYPE_STORAGE;
1150 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1151 newProperty.size.u.LowPart = 0;
1152 newProperty.size.u.HighPart = 0;
1154 newProperty.previousProperty = PROPERTY_NULL;
1155 newProperty.nextProperty = PROPERTY_NULL;
1156 newProperty.dirProperty = PROPERTY_NULL;
1158 /* call CoFileTime to get the current time
1159 newProperty.timeStampS1
1160 newProperty.timeStampD1
1161 newProperty.timeStampS2
1162 newProperty.timeStampD2
1165 /* newStorageProperty.propertyUniqueID */
1168 * Obtain a free property in the property chain
1170 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1173 * Save the new property into the new property spot
1175 StorageImpl_WriteProperty(
1176 This->base.ancestorStorage,
1181 * Find a spot in the property chain for our newly created property.
1183 updatePropertyChain(
1189 * Open it to get a pointer to return.
1191 hr = IStorage_OpenStorage(
1193 (const OLECHAR*)pwcsName,
1200 if( (hr != S_OK) || (*ppstg == NULL))
1210 /***************************************************************************
1214 * Get a free property or create a new one.
1216 static ULONG getFreeProperty(
1217 StorageImpl *storage)
1219 ULONG currentPropertyIndex = 0;
1220 ULONG newPropertyIndex = PROPERTY_NULL;
1221 BOOL readSuccessful = TRUE;
1222 StgProperty currentProperty;
1227 * Start by reading the root property
1229 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1230 currentPropertyIndex,
1234 if (currentProperty.sizeOfNameString == 0)
1237 * The property existis and is available, we found it.
1239 newPropertyIndex = currentPropertyIndex;
1245 * We exhausted the property list, we will create more space below
1247 newPropertyIndex = currentPropertyIndex;
1249 currentPropertyIndex++;
1251 } while (newPropertyIndex == PROPERTY_NULL);
1254 * grow the property chain
1256 if (! readSuccessful)
1258 StgProperty emptyProperty;
1259 ULARGE_INTEGER newSize;
1260 ULONG propertyIndex;
1261 ULONG lastProperty = 0;
1262 ULONG blockCount = 0;
1265 * obtain the new count of property blocks
1267 blockCount = BlockChainStream_GetCount(
1268 storage->base.ancestorStorage->rootBlockChain)+1;
1271 * initialize the size used by the property stream
1273 newSize.u.HighPart = 0;
1274 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1277 * add a property block to the property chain
1279 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1282 * memset the empty property in order to initialize the unused newly
1285 memset(&emptyProperty, 0, sizeof(StgProperty));
1290 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1293 propertyIndex = newPropertyIndex;
1294 propertyIndex < lastProperty;
1297 StorageImpl_WriteProperty(
1298 storage->base.ancestorStorage,
1304 return newPropertyIndex;
1307 /****************************************************************************
1311 * Case insensitive comparaison of StgProperty.name by first considering
1314 * Returns <0 when newPrpoerty < currentProperty
1315 * >0 when newPrpoerty > currentProperty
1316 * 0 when newPrpoerty == currentProperty
1318 static LONG propertyNameCmp(
1319 const OLECHAR *newProperty,
1320 const OLECHAR *currentProperty)
1322 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1327 * We compare the string themselves only when they are of the same length
1329 diff = lstrcmpiW( newProperty, currentProperty);
1335 /****************************************************************************
1339 * Properly link this new element in the property chain.
1341 static void updatePropertyChain(
1342 StorageImpl *storage,
1343 ULONG newPropertyIndex,
1344 StgProperty newProperty)
1346 StgProperty currentProperty;
1349 * Read the root property
1351 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1352 storage->base.rootPropertySetIndex,
1355 if (currentProperty.dirProperty != PROPERTY_NULL)
1358 * The root storage contains some element, therefore, start the research
1359 * for the appropriate location.
1362 ULONG current, next, previous, currentPropertyId;
1365 * Keep the StgProperty sequence number of the storage first property
1367 currentPropertyId = currentProperty.dirProperty;
1372 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1373 currentProperty.dirProperty,
1376 previous = currentProperty.previousProperty;
1377 next = currentProperty.nextProperty;
1378 current = currentPropertyId;
1382 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1386 if (previous != PROPERTY_NULL)
1388 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1395 currentProperty.previousProperty = newPropertyIndex;
1396 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1404 if (next != PROPERTY_NULL)
1406 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1413 currentProperty.nextProperty = newPropertyIndex;
1414 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1423 * Trying to insert an item with the same name in the
1424 * subtree structure.
1429 previous = currentProperty.previousProperty;
1430 next = currentProperty.nextProperty;
1436 * The root storage is empty, link the new property to its dir property
1438 currentProperty.dirProperty = newPropertyIndex;
1439 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1440 storage->base.rootPropertySetIndex,
1446 /*************************************************************************
1449 static HRESULT WINAPI StorageImpl_CopyTo(
1451 DWORD ciidExclude, /* [in] */
1452 const IID* rgiidExclude, /* [size_is][unique][in] */
1453 SNB snbExclude, /* [unique][in] */
1454 IStorage* pstgDest) /* [unique][in] */
1456 IEnumSTATSTG *elements = 0;
1457 STATSTG curElement, strStat;
1459 IStorage *pstgTmp, *pstgChild;
1460 IStream *pstrTmp, *pstrChild;
1462 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1463 FIXME("Exclude option not implemented\n");
1465 TRACE("(%p, %ld, %p, %p, %p)\n",
1466 iface, ciidExclude, rgiidExclude,
1467 snbExclude, pstgDest);
1470 * Perform a sanity check
1472 if ( pstgDest == 0 )
1473 return STG_E_INVALIDPOINTER;
1476 * Enumerate the elements
1478 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1486 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1487 IStorage_SetClass( pstgDest, &curElement.clsid );
1492 * Obtain the next element
1494 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1496 if ( hr == S_FALSE )
1498 hr = S_OK; /* done, every element has been copied */
1502 if (curElement.type == STGTY_STORAGE)
1505 * open child source storage
1507 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1508 STGM_READ|STGM_SHARE_EXCLUSIVE,
1509 NULL, 0, &pstgChild );
1515 * Check if destination storage is not a child of the source
1516 * storage, which will cause an infinite loop
1518 if (pstgChild == pstgDest)
1520 IEnumSTATSTG_Release(elements);
1522 return STG_E_ACCESSDENIED;
1526 * create a new storage in destination storage
1528 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1529 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1533 * if it already exist, don't create a new one use this one
1535 if (hr == STG_E_FILEALREADYEXISTS)
1537 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1538 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1539 NULL, 0, &pstgTmp );
1547 * do the copy recursively
1549 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1550 snbExclude, pstgTmp );
1552 IStorage_Release( pstgTmp );
1553 IStorage_Release( pstgChild );
1555 else if (curElement.type == STGTY_STREAM)
1558 * create a new stream in destination storage. If the stream already
1559 * exist, it will be deleted and a new one will be created.
1561 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1562 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1569 * open child stream storage
1571 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1572 STGM_READ|STGM_SHARE_EXCLUSIVE,
1579 * Get the size of the source stream
1581 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1584 * Set the size of the destination stream.
1586 IStream_SetSize(pstrTmp, strStat.cbSize);
1591 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1594 IStream_Release( pstrTmp );
1595 IStream_Release( pstrChild );
1599 WARN("unknown element type: %ld\n", curElement.type);
1602 } while (hr == S_OK);
1607 IEnumSTATSTG_Release(elements);
1612 /*************************************************************************
1613 * MoveElementTo (IStorage)
1615 static HRESULT WINAPI StorageImpl_MoveElementTo(
1617 const OLECHAR *pwcsName, /* [string][in] */
1618 IStorage *pstgDest, /* [unique][in] */
1619 const OLECHAR *pwcsNewName,/* [string][in] */
1620 DWORD grfFlags) /* [in] */
1622 FIXME("not implemented!\n");
1626 /*************************************************************************
1629 * Ensures that any changes made to a storage object open in transacted mode
1630 * are reflected in the parent storage
1633 * Wine doesn't implement transacted mode, which seems to be a basic
1634 * optimization, so we can ignore this stub for now.
1636 static HRESULT WINAPI StorageImpl_Commit(
1638 DWORD grfCommitFlags)/* [in] */
1640 FIXME("(%ld): stub!\n", grfCommitFlags);
1644 /*************************************************************************
1647 * Discard all changes that have been made since the last commit operation
1649 static HRESULT WINAPI StorageImpl_Revert(
1652 FIXME("not implemented!\n");
1656 /*************************************************************************
1657 * DestroyElement (IStorage)
1659 * Strategy: This implementation is built this way for simplicity not for speed.
1660 * I always delete the topmost element of the enumeration and adjust
1661 * the deleted element pointer all the time. This takes longer to
1662 * do but allow to reinvoke DestroyElement whenever we encounter a
1663 * storage object. The optimisation resides in the usage of another
1664 * enumeration strategy that would give all the leaves of a storage
1665 * first. (postfix order)
1667 static HRESULT WINAPI StorageImpl_DestroyElement(
1669 const OLECHAR *pwcsName)/* [string][in] */
1671 StorageImpl* const This=(StorageImpl*)iface;
1673 IEnumSTATSTGImpl* propertyEnumeration;
1676 StgProperty propertyToDelete;
1677 StgProperty parentProperty;
1678 ULONG foundPropertyIndexToDelete;
1679 ULONG typeOfRelation;
1680 ULONG parentPropertyId;
1683 iface, debugstr_w(pwcsName));
1686 * Perform a sanity check on the parameters.
1689 return STG_E_INVALIDPOINTER;
1692 * Create a property enumeration to search the property with the given name
1694 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1695 This->base.ancestorStorage,
1696 This->base.rootPropertySetIndex);
1698 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1699 propertyEnumeration,
1703 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1705 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1707 return STG_E_FILENOTFOUND;
1711 * Find the parent property of the property to delete (the one that
1712 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1713 * the parent is This. Otherwise, the parent is one of its sibling...
1717 * First, read This's StgProperty..
1719 res = StorageImpl_ReadProperty(
1720 This->base.ancestorStorage,
1721 This->base.rootPropertySetIndex,
1727 * Second, check to see if by any chance the actual storage (This) is not
1728 * the parent of the property to delete... We never know...
1730 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1733 * Set data as it would have been done in the else part...
1735 typeOfRelation = PROPERTY_RELATION_DIR;
1736 parentPropertyId = This->base.rootPropertySetIndex;
1741 * Create a property enumeration to search the parent properties, and
1742 * delete it once done.
1744 IEnumSTATSTGImpl* propertyEnumeration2;
1746 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1747 This->base.ancestorStorage,
1748 This->base.rootPropertySetIndex);
1750 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1751 propertyEnumeration2,
1752 foundPropertyIndexToDelete,
1756 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1759 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1761 hr = deleteStorageProperty(
1763 foundPropertyIndexToDelete,
1766 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1768 hr = deleteStreamProperty(
1770 foundPropertyIndexToDelete,
1778 * Adjust the property chain
1780 hr = adjustPropertyChain(
1791 /************************************************************************
1792 * StorageImpl_Stat (IStorage)
1794 * This method will retrieve information about this storage object.
1796 * See Windows documentation for more details on IStorage methods.
1798 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1799 STATSTG* pstatstg, /* [out] */
1800 DWORD grfStatFlag) /* [in] */
1802 StorageImpl* const This = (StorageImpl*)iface;
1803 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1805 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1807 CoTaskMemFree(pstatstg->pwcsName);
1808 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1809 strcpyW(pstatstg->pwcsName, This->pwcsName);
1815 /******************************************************************************
1816 * Internal stream list handlers
1819 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1821 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1822 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1825 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1827 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1828 list_remove(&(strm->StrmListEntry));
1831 void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1833 struct list *cur, *cur2;
1834 StgStreamImpl *strm=NULL;
1836 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1837 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1838 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1839 strm->parentStorage = NULL;
1845 /*********************************************************************
1849 * Perform the deletion of a complete storage node
1852 static HRESULT deleteStorageProperty(
1853 StorageImpl *parentStorage,
1854 ULONG indexOfPropertyToDelete,
1855 StgProperty propertyToDelete)
1857 IEnumSTATSTG *elements = 0;
1858 IStorage *childStorage = 0;
1859 STATSTG currentElement;
1861 HRESULT destroyHr = S_OK;
1864 * Open the storage and enumerate it
1866 hr = StorageBaseImpl_OpenStorage(
1867 (IStorage*)parentStorage,
1868 propertyToDelete.name,
1870 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1881 * Enumerate the elements
1883 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1888 * Obtain the next element
1890 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1893 destroyHr = StorageImpl_DestroyElement(
1894 (IStorage*)childStorage,
1895 (OLECHAR*)currentElement.pwcsName);
1897 CoTaskMemFree(currentElement.pwcsName);
1901 * We need to Reset the enumeration every time because we delete elements
1902 * and the enumeration could be invalid
1904 IEnumSTATSTG_Reset(elements);
1906 } while ((hr == S_OK) && (destroyHr == S_OK));
1909 * Invalidate the property by zeroing its name member.
1911 propertyToDelete.sizeOfNameString = 0;
1913 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1914 indexOfPropertyToDelete,
1917 IStorage_Release(childStorage);
1918 IEnumSTATSTG_Release(elements);
1923 /*********************************************************************
1927 * Perform the deletion of a stream node
1930 static HRESULT deleteStreamProperty(
1931 StorageImpl *parentStorage,
1932 ULONG indexOfPropertyToDelete,
1933 StgProperty propertyToDelete)
1937 ULARGE_INTEGER size;
1939 size.u.HighPart = 0;
1942 hr = StorageBaseImpl_OpenStream(
1943 (IStorage*)parentStorage,
1944 (OLECHAR*)propertyToDelete.name,
1946 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1958 hr = IStream_SetSize(pis, size);
1966 * Release the stream object.
1968 IStream_Release(pis);
1971 * Invalidate the property by zeroing its name member.
1973 propertyToDelete.sizeOfNameString = 0;
1976 * Here we should re-read the property so we get the updated pointer
1977 * but since we are here to zap it, I don't do it...
1979 StorageImpl_WriteProperty(
1980 parentStorage->base.ancestorStorage,
1981 indexOfPropertyToDelete,
1987 /*********************************************************************
1991 * Finds a placeholder for the StgProperty within the Storage
1994 static HRESULT findPlaceholder(
1995 StorageImpl *storage,
1996 ULONG propertyIndexToStore,
1997 ULONG storePropertyIndex,
2000 StgProperty storeProperty;
2005 * Read the storage property
2007 res = StorageImpl_ReadProperty(
2008 storage->base.ancestorStorage,
2017 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2019 if (storeProperty.previousProperty != PROPERTY_NULL)
2021 return findPlaceholder(
2023 propertyIndexToStore,
2024 storeProperty.previousProperty,
2029 storeProperty.previousProperty = propertyIndexToStore;
2032 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2034 if (storeProperty.nextProperty != PROPERTY_NULL)
2036 return findPlaceholder(
2038 propertyIndexToStore,
2039 storeProperty.nextProperty,
2044 storeProperty.nextProperty = propertyIndexToStore;
2047 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2049 if (storeProperty.dirProperty != PROPERTY_NULL)
2051 return findPlaceholder(
2053 propertyIndexToStore,
2054 storeProperty.dirProperty,
2059 storeProperty.dirProperty = propertyIndexToStore;
2063 hr = StorageImpl_WriteProperty(
2064 storage->base.ancestorStorage,
2076 /*************************************************************************
2080 * This method takes the previous and the next property link of a property
2081 * to be deleted and find them a place in the Storage.
2083 static HRESULT adjustPropertyChain(
2085 StgProperty propertyToDelete,
2086 StgProperty parentProperty,
2087 ULONG parentPropertyId,
2090 ULONG newLinkProperty = PROPERTY_NULL;
2091 BOOL needToFindAPlaceholder = FALSE;
2092 ULONG storeNode = PROPERTY_NULL;
2093 ULONG toStoreNode = PROPERTY_NULL;
2094 INT relationType = 0;
2098 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2100 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2103 * Set the parent previous to the property to delete previous
2105 newLinkProperty = propertyToDelete.previousProperty;
2107 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2110 * We also need to find a storage for the other link, setup variables
2111 * to do this at the end...
2113 needToFindAPlaceholder = TRUE;
2114 storeNode = propertyToDelete.previousProperty;
2115 toStoreNode = propertyToDelete.nextProperty;
2116 relationType = PROPERTY_RELATION_NEXT;
2119 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2122 * Set the parent previous to the property to delete next
2124 newLinkProperty = propertyToDelete.nextProperty;
2128 * Link it for real...
2130 parentProperty.previousProperty = newLinkProperty;
2133 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2135 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2138 * Set the parent next to the property to delete next previous
2140 newLinkProperty = propertyToDelete.previousProperty;
2142 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2145 * We also need to find a storage for the other link, setup variables
2146 * to do this at the end...
2148 needToFindAPlaceholder = TRUE;
2149 storeNode = propertyToDelete.previousProperty;
2150 toStoreNode = propertyToDelete.nextProperty;
2151 relationType = PROPERTY_RELATION_NEXT;
2154 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2157 * Set the parent next to the property to delete next
2159 newLinkProperty = propertyToDelete.nextProperty;
2163 * Link it for real...
2165 parentProperty.nextProperty = newLinkProperty;
2167 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2169 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2172 * Set the parent dir to the property to delete previous
2174 newLinkProperty = propertyToDelete.previousProperty;
2176 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2179 * We also need to find a storage for the other link, setup variables
2180 * to do this at the end...
2182 needToFindAPlaceholder = TRUE;
2183 storeNode = propertyToDelete.previousProperty;
2184 toStoreNode = propertyToDelete.nextProperty;
2185 relationType = PROPERTY_RELATION_NEXT;
2188 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2191 * Set the parent dir to the property to delete next
2193 newLinkProperty = propertyToDelete.nextProperty;
2197 * Link it for real...
2199 parentProperty.dirProperty = newLinkProperty;
2203 * Write back the parent property
2205 res = StorageImpl_WriteProperty(
2206 This->base.ancestorStorage,
2215 * If a placeholder is required for the other link, then, find one and
2216 * get out of here...
2218 if (needToFindAPlaceholder)
2220 hr = findPlaceholder(
2231 /******************************************************************************
2232 * SetElementTimes (IStorage)
2234 static HRESULT WINAPI StorageImpl_SetElementTimes(
2236 const OLECHAR *pwcsName,/* [string][in] */
2237 const FILETIME *pctime, /* [in] */
2238 const FILETIME *patime, /* [in] */
2239 const FILETIME *pmtime) /* [in] */
2241 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2245 /******************************************************************************
2246 * SetStateBits (IStorage)
2248 static HRESULT WINAPI StorageImpl_SetStateBits(
2250 DWORD grfStateBits,/* [in] */
2251 DWORD grfMask) /* [in] */
2253 FIXME("not implemented!\n");
2258 * Virtual function table for the IStorage32Impl class.
2260 static const IStorageVtbl Storage32Impl_Vtbl =
2262 StorageBaseImpl_QueryInterface,
2263 StorageBaseImpl_AddRef,
2264 StorageBaseImpl_Release,
2265 StorageBaseImpl_CreateStream,
2266 StorageBaseImpl_OpenStream,
2267 StorageImpl_CreateStorage,
2268 StorageBaseImpl_OpenStorage,
2270 StorageImpl_MoveElementTo,
2273 StorageBaseImpl_EnumElements,
2274 StorageImpl_DestroyElement,
2275 StorageBaseImpl_RenameElement,
2276 StorageImpl_SetElementTimes,
2277 StorageBaseImpl_SetClass,
2278 StorageImpl_SetStateBits,
2282 HRESULT StorageImpl_Construct(
2292 StgProperty currentProperty;
2293 BOOL readSuccessful;
2294 ULONG currentPropertyIndex;
2296 if ( FAILED( validateSTGM(openFlags) ))
2297 return STG_E_INVALIDFLAG;
2299 memset(This, 0, sizeof(StorageImpl));
2302 * Initialize stream list
2305 list_init(&This->base.strmHead);
2308 * Initialize the virtual function table.
2310 This->base.lpVtbl = &Storage32Impl_Vtbl;
2311 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2312 This->base.v_destructor = &StorageImpl_Destroy;
2313 This->base.openFlags = (openFlags & ~STGM_CREATE);
2316 * This is the top-level storage so initialize the ancestor pointer
2319 This->base.ancestorStorage = This;
2322 * Initialize the physical support of the storage.
2324 This->hFile = hFile;
2327 * Store copy of file path.
2330 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2331 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2332 if (!This->pwcsName)
2333 return STG_E_INSUFFICIENTMEMORY;
2334 strcpyW(This->pwcsName, pwcsName);
2338 * Initialize the big block cache.
2340 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2341 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2342 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2348 if (This->bigBlockFile == 0)
2353 ULARGE_INTEGER size;
2354 BYTE* bigBlockBuffer;
2357 * Initialize all header variables:
2358 * - The big block depot consists of one block and it is at block 0
2359 * - The properties start at block 1
2360 * - There is no small block depot
2362 memset( This->bigBlockDepotStart,
2364 sizeof(This->bigBlockDepotStart));
2366 This->bigBlockDepotCount = 1;
2367 This->bigBlockDepotStart[0] = 0;
2368 This->rootStartBlock = 1;
2369 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2370 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2371 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2372 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2373 This->extBigBlockDepotCount = 0;
2375 StorageImpl_SaveFileHeader(This);
2378 * Add one block for the big block depot and one block for the properties
2380 size.u.HighPart = 0;
2381 size.u.LowPart = This->bigBlockSize * 3;
2382 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2385 * Initialize the big block depot
2387 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2388 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2389 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2390 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2391 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2396 * Load the header for the file.
2398 hr = StorageImpl_LoadFileHeader(This);
2402 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2409 * There is no block depot cached yet.
2411 This->indexBlockDepotCached = 0xFFFFFFFF;
2414 * Start searching for free blocks with block 0.
2416 This->prevFreeBlock = 0;
2419 * Create the block chain abstractions.
2421 if(!(This->rootBlockChain =
2422 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2423 return STG_E_READFAULT;
2425 if(!(This->smallBlockDepotChain =
2426 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2428 return STG_E_READFAULT;
2431 * Write the root property (memory only)
2435 StgProperty rootProp;
2437 * Initialize the property chain
2439 memset(&rootProp, 0, sizeof(rootProp));
2440 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2441 sizeof(rootProp.name)/sizeof(WCHAR) );
2442 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2443 rootProp.propertyType = PROPTYPE_ROOT;
2444 rootProp.previousProperty = PROPERTY_NULL;
2445 rootProp.nextProperty = PROPERTY_NULL;
2446 rootProp.dirProperty = PROPERTY_NULL;
2447 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2448 rootProp.size.u.HighPart = 0;
2449 rootProp.size.u.LowPart = 0;
2451 StorageImpl_WriteProperty(This, 0, &rootProp);
2455 * Find the ID of the root in the property sets.
2457 currentPropertyIndex = 0;
2461 readSuccessful = StorageImpl_ReadProperty(
2463 currentPropertyIndex,
2468 if ( (currentProperty.sizeOfNameString != 0 ) &&
2469 (currentProperty.propertyType == PROPTYPE_ROOT) )
2471 This->base.rootPropertySetIndex = currentPropertyIndex;
2475 currentPropertyIndex++;
2477 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2479 if (!readSuccessful)
2482 return STG_E_READFAULT;
2486 * Create the block chain abstraction for the small block root chain.
2488 if(!(This->smallBlockRootChain =
2489 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2490 return STG_E_READFAULT;
2495 void StorageImpl_Destroy(StorageBaseImpl* iface)
2497 StorageImpl *This = (StorageImpl*) iface;
2498 TRACE("(%p)\n", This);
2500 StorageBaseImpl_DeleteAll(&This->base);
2502 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2504 BlockChainStream_Destroy(This->smallBlockRootChain);
2505 BlockChainStream_Destroy(This->rootBlockChain);
2506 BlockChainStream_Destroy(This->smallBlockDepotChain);
2508 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2509 HeapFree(GetProcessHeap(), 0, This);
2512 /******************************************************************************
2513 * Storage32Impl_GetNextFreeBigBlock
2515 * Returns the index of the next free big block.
2516 * If the big block depot is filled, this method will enlarge it.
2519 ULONG StorageImpl_GetNextFreeBigBlock(
2522 ULONG depotBlockIndexPos;
2524 ULONG depotBlockOffset;
2525 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2526 ULONG nextBlockIndex = BLOCK_SPECIAL;
2528 ULONG freeBlock = BLOCK_UNUSED;
2530 depotIndex = This->prevFreeBlock / blocksPerDepot;
2531 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2534 * Scan the entire big block depot until we find a block marked free
2536 while (nextBlockIndex != BLOCK_UNUSED)
2538 if (depotIndex < COUNT_BBDEPOTINHEADER)
2540 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2543 * Grow the primary depot.
2545 if (depotBlockIndexPos == BLOCK_UNUSED)
2547 depotBlockIndexPos = depotIndex*blocksPerDepot;
2550 * Add a block depot.
2552 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2553 This->bigBlockDepotCount++;
2554 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2557 * Flag it as a block depot.
2559 StorageImpl_SetNextBlockInChain(This,
2563 /* Save new header information.
2565 StorageImpl_SaveFileHeader(This);
2570 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2572 if (depotBlockIndexPos == BLOCK_UNUSED)
2575 * Grow the extended depot.
2577 ULONG extIndex = BLOCK_UNUSED;
2578 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2579 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2581 if (extBlockOffset == 0)
2583 /* We need an extended block.
2585 extIndex = Storage32Impl_AddExtBlockDepot(This);
2586 This->extBigBlockDepotCount++;
2587 depotBlockIndexPos = extIndex + 1;
2590 depotBlockIndexPos = depotIndex * blocksPerDepot;
2593 * Add a block depot and mark it in the extended block.
2595 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2596 This->bigBlockDepotCount++;
2597 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2599 /* Flag the block depot.
2601 StorageImpl_SetNextBlockInChain(This,
2605 /* If necessary, flag the extended depot block.
2607 if (extIndex != BLOCK_UNUSED)
2608 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2610 /* Save header information.
2612 StorageImpl_SaveFileHeader(This);
2616 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2618 if (depotBuffer != 0)
2620 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2621 ( nextBlockIndex != BLOCK_UNUSED))
2623 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2625 if (nextBlockIndex == BLOCK_UNUSED)
2627 freeBlock = (depotIndex * blocksPerDepot) +
2628 (depotBlockOffset/sizeof(ULONG));
2631 depotBlockOffset += sizeof(ULONG);
2634 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2638 depotBlockOffset = 0;
2641 This->prevFreeBlock = freeBlock;
2646 /******************************************************************************
2647 * Storage32Impl_AddBlockDepot
2649 * This will create a depot block, essentially it is a block initialized
2652 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2656 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2659 * Initialize blocks as free
2661 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2663 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2666 /******************************************************************************
2667 * Storage32Impl_GetExtDepotBlock
2669 * Returns the index of the block that corresponds to the specified depot
2670 * index. This method is only for depot indexes equal or greater than
2671 * COUNT_BBDEPOTINHEADER.
2673 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2675 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2676 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2677 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2678 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2679 ULONG blockIndex = BLOCK_UNUSED;
2680 ULONG extBlockIndex = This->extBigBlockDepotStart;
2682 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2684 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2685 return BLOCK_UNUSED;
2687 while (extBlockCount > 0)
2689 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2693 if (extBlockIndex != BLOCK_UNUSED)
2697 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2699 if (depotBuffer != 0)
2701 StorageUtl_ReadDWord(depotBuffer,
2702 extBlockOffset * sizeof(ULONG),
2705 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2712 /******************************************************************************
2713 * Storage32Impl_SetExtDepotBlock
2715 * Associates the specified block index to the specified depot index.
2716 * This method is only for depot indexes equal or greater than
2717 * COUNT_BBDEPOTINHEADER.
2719 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2723 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2724 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2725 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2726 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2727 ULONG extBlockIndex = This->extBigBlockDepotStart;
2729 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2731 while (extBlockCount > 0)
2733 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2737 if (extBlockIndex != BLOCK_UNUSED)
2741 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2743 if (depotBuffer != 0)
2745 StorageUtl_WriteDWord(depotBuffer,
2746 extBlockOffset * sizeof(ULONG),
2749 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2754 /******************************************************************************
2755 * Storage32Impl_AddExtBlockDepot
2757 * Creates an extended depot block.
2759 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2761 ULONG numExtBlocks = This->extBigBlockDepotCount;
2762 ULONG nextExtBlock = This->extBigBlockDepotStart;
2763 BYTE* depotBuffer = NULL;
2764 ULONG index = BLOCK_UNUSED;
2765 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2766 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2767 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2769 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2770 blocksPerDepotBlock;
2772 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2775 * The first extended block.
2777 This->extBigBlockDepotStart = index;
2783 * Follow the chain to the last one.
2785 for (i = 0; i < (numExtBlocks - 1); i++)
2787 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2791 * Add the new extended block to the chain.
2793 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2794 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2795 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2799 * Initialize this block.
2801 depotBuffer = StorageImpl_GetBigBlock(This, index);
2802 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2803 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2808 /******************************************************************************
2809 * Storage32Impl_FreeBigBlock
2811 * This method will flag the specified block as free in the big block depot.
2813 void StorageImpl_FreeBigBlock(
2817 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2819 if (blockIndex < This->prevFreeBlock)
2820 This->prevFreeBlock = blockIndex;
2823 /************************************************************************
2824 * Storage32Impl_GetNextBlockInChain
2826 * This method will retrieve the block index of the next big block in
2829 * Params: This - Pointer to the Storage object.
2830 * blockIndex - Index of the block to retrieve the chain
2832 * nextBlockIndex - receives the return value.
2834 * Returns: This method returns the index of the next block in the chain.
2835 * It will return the constants:
2836 * BLOCK_SPECIAL - If the block given was not part of a
2838 * BLOCK_END_OF_CHAIN - If the block given was the last in
2840 * BLOCK_UNUSED - If the block given was not past of a chain
2842 * BLOCK_EXTBBDEPOT - This block is part of the extended
2845 * See Windows documentation for more details on IStorage methods.
2847 HRESULT StorageImpl_GetNextBlockInChain(
2850 ULONG* nextBlockIndex)
2852 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2853 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2854 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2856 ULONG depotBlockIndexPos;
2859 *nextBlockIndex = BLOCK_SPECIAL;
2861 if(depotBlockCount >= This->bigBlockDepotCount)
2863 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2864 This->bigBlockDepotCount);
2865 return STG_E_READFAULT;
2869 * Cache the currently accessed depot block.
2871 if (depotBlockCount != This->indexBlockDepotCached)
2873 This->indexBlockDepotCached = depotBlockCount;
2875 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2877 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2882 * We have to look in the extended depot.
2884 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2887 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2890 return STG_E_READFAULT;
2892 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2894 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2895 This->blockDepotCached[index] = *nextBlockIndex;
2897 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2900 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2905 /******************************************************************************
2906 * Storage32Impl_GetNextExtendedBlock
2908 * Given an extended block this method will return the next extended block.
2911 * The last ULONG of an extended block is the block index of the next
2912 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2916 * - The index of the next extended block
2917 * - BLOCK_UNUSED: there is no next extended block.
2918 * - Any other return values denotes failure.
2920 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2922 ULONG nextBlockIndex = BLOCK_SPECIAL;
2923 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2926 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2930 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2932 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2935 return nextBlockIndex;
2938 /******************************************************************************
2939 * Storage32Impl_SetNextBlockInChain
2941 * This method will write the index of the specified block's next block
2942 * in the big block depot.
2944 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2947 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2948 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2949 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2952 void StorageImpl_SetNextBlockInChain(
2957 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2958 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2959 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2960 ULONG depotBlockIndexPos;
2963 assert(depotBlockCount < This->bigBlockDepotCount);
2964 assert(blockIndex != nextBlock);
2966 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2968 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2973 * We have to look in the extended depot.
2975 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2978 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2982 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2983 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2987 * Update the cached block depot, if necessary.
2989 if (depotBlockCount == This->indexBlockDepotCached)
2991 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2995 /******************************************************************************
2996 * Storage32Impl_LoadFileHeader
2998 * This method will read in the file header, i.e. big block index -1.
3000 HRESULT StorageImpl_LoadFileHeader(
3003 HRESULT hr = STG_E_FILENOTFOUND;
3004 void* headerBigBlock = NULL;
3009 * Get a pointer to the big block of data containing the header.
3011 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
3014 * Extract the information from the header.
3016 if (headerBigBlock!=0)
3019 * Check for the "magic number" signature and return an error if it is not
3022 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3024 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3025 return STG_E_OLDFORMAT;
3028 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3030 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3031 return STG_E_INVALIDHEADER;
3034 StorageUtl_ReadWord(
3036 OFFSET_BIGBLOCKSIZEBITS,
3037 &This->bigBlockSizeBits);
3039 StorageUtl_ReadWord(
3041 OFFSET_SMALLBLOCKSIZEBITS,
3042 &This->smallBlockSizeBits);
3044 StorageUtl_ReadDWord(
3046 OFFSET_BBDEPOTCOUNT,
3047 &This->bigBlockDepotCount);
3049 StorageUtl_ReadDWord(
3051 OFFSET_ROOTSTARTBLOCK,
3052 &This->rootStartBlock);
3054 StorageUtl_ReadDWord(
3056 OFFSET_SBDEPOTSTART,
3057 &This->smallBlockDepotStart);
3059 StorageUtl_ReadDWord(
3061 OFFSET_EXTBBDEPOTSTART,
3062 &This->extBigBlockDepotStart);
3064 StorageUtl_ReadDWord(
3066 OFFSET_EXTBBDEPOTCOUNT,
3067 &This->extBigBlockDepotCount);
3069 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3071 StorageUtl_ReadDWord(
3073 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3074 &(This->bigBlockDepotStart[index]));
3078 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3082 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3083 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3087 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3088 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3092 * Right now, the code is making some assumptions about the size of the
3093 * blocks, just make sure they are what we're expecting.
3095 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3096 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3098 WARN("Broken OLE storage file\n");
3099 hr = STG_E_INVALIDHEADER;
3105 * Release the block.
3107 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3113 /******************************************************************************
3114 * Storage32Impl_SaveFileHeader
3116 * This method will save to the file the header, i.e. big block -1.
3118 void StorageImpl_SaveFileHeader(
3121 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3126 * Get a pointer to the big block of data containing the header.
3128 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3131 * If the block read failed, the file is probably new.
3136 * Initialize for all unknown fields.
3138 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3141 * Initialize the magic number.
3143 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3146 * And a bunch of things we don't know what they mean
3148 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3149 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3150 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3151 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3155 * Write the information to the header.
3157 StorageUtl_WriteWord(
3159 OFFSET_BIGBLOCKSIZEBITS,
3160 This->bigBlockSizeBits);
3162 StorageUtl_WriteWord(
3164 OFFSET_SMALLBLOCKSIZEBITS,
3165 This->smallBlockSizeBits);
3167 StorageUtl_WriteDWord(
3169 OFFSET_BBDEPOTCOUNT,
3170 This->bigBlockDepotCount);
3172 StorageUtl_WriteDWord(
3174 OFFSET_ROOTSTARTBLOCK,
3175 This->rootStartBlock);
3177 StorageUtl_WriteDWord(
3179 OFFSET_SBDEPOTSTART,
3180 This->smallBlockDepotStart);
3182 StorageUtl_WriteDWord(
3184 OFFSET_SBDEPOTCOUNT,
3185 This->smallBlockDepotChain ?
3186 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3188 StorageUtl_WriteDWord(
3190 OFFSET_EXTBBDEPOTSTART,
3191 This->extBigBlockDepotStart);
3193 StorageUtl_WriteDWord(
3195 OFFSET_EXTBBDEPOTCOUNT,
3196 This->extBigBlockDepotCount);
3198 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3200 StorageUtl_WriteDWord(
3202 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3203 (This->bigBlockDepotStart[index]));
3207 * Write the big block back to the file.
3209 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3212 /******************************************************************************
3213 * Storage32Impl_ReadProperty
3215 * This method will read the specified property from the property chain.
3217 BOOL StorageImpl_ReadProperty(
3220 StgProperty* buffer)
3222 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3223 ULARGE_INTEGER offsetInPropSet;
3224 BOOL readSuccessful;
3227 offsetInPropSet.u.HighPart = 0;
3228 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3230 readSuccessful = BlockChainStream_ReadAt(
3231 This->rootBlockChain,
3239 /* replace the name of root entry (often "Root Entry") by the file name */
3240 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3241 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3243 memset(buffer->name, 0, sizeof(buffer->name));
3247 PROPERTY_NAME_BUFFER_LEN );
3248 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3250 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3252 StorageUtl_ReadWord(
3254 OFFSET_PS_NAMELENGTH,
3255 &buffer->sizeOfNameString);
3257 StorageUtl_ReadDWord(
3259 OFFSET_PS_PREVIOUSPROP,
3260 &buffer->previousProperty);
3262 StorageUtl_ReadDWord(
3265 &buffer->nextProperty);
3267 StorageUtl_ReadDWord(
3270 &buffer->dirProperty);
3272 StorageUtl_ReadGUID(
3275 &buffer->propertyUniqueID);
3277 StorageUtl_ReadDWord(
3280 &buffer->timeStampS1);
3282 StorageUtl_ReadDWord(
3285 &buffer->timeStampD1);
3287 StorageUtl_ReadDWord(
3290 &buffer->timeStampS2);
3292 StorageUtl_ReadDWord(
3295 &buffer->timeStampD2);
3297 StorageUtl_ReadDWord(
3299 OFFSET_PS_STARTBLOCK,
3300 &buffer->startingBlock);
3302 StorageUtl_ReadDWord(
3305 &buffer->size.u.LowPart);
3307 buffer->size.u.HighPart = 0;
3310 return readSuccessful;
3313 /*********************************************************************
3314 * Write the specified property into the property chain
3316 BOOL StorageImpl_WriteProperty(
3319 StgProperty* buffer)
3321 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3322 ULARGE_INTEGER offsetInPropSet;
3323 BOOL writeSuccessful;
3326 offsetInPropSet.u.HighPart = 0;
3327 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3329 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3332 currentProperty + OFFSET_PS_NAME,
3334 PROPERTY_NAME_BUFFER_LEN );
3336 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3338 StorageUtl_WriteWord(
3340 OFFSET_PS_NAMELENGTH,
3341 buffer->sizeOfNameString);
3343 StorageUtl_WriteDWord(
3345 OFFSET_PS_PREVIOUSPROP,
3346 buffer->previousProperty);
3348 StorageUtl_WriteDWord(
3351 buffer->nextProperty);
3353 StorageUtl_WriteDWord(
3356 buffer->dirProperty);
3358 StorageUtl_WriteGUID(
3361 &buffer->propertyUniqueID);
3363 StorageUtl_WriteDWord(
3366 buffer->timeStampS1);
3368 StorageUtl_WriteDWord(
3371 buffer->timeStampD1);
3373 StorageUtl_WriteDWord(
3376 buffer->timeStampS2);
3378 StorageUtl_WriteDWord(
3381 buffer->timeStampD2);
3383 StorageUtl_WriteDWord(
3385 OFFSET_PS_STARTBLOCK,
3386 buffer->startingBlock);
3388 StorageUtl_WriteDWord(
3391 buffer->size.u.LowPart);
3393 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3398 return writeSuccessful;
3401 BOOL StorageImpl_ReadBigBlock(
3406 void* bigBlockBuffer;
3408 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3410 if (bigBlockBuffer!=0)
3412 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3414 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3422 BOOL StorageImpl_WriteBigBlock(
3427 void* bigBlockBuffer;
3429 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3431 if (bigBlockBuffer!=0)
3433 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3435 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3443 void* StorageImpl_GetROBigBlock(
3447 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3450 void* StorageImpl_GetBigBlock(
3454 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3457 void StorageImpl_ReleaseBigBlock(
3461 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3464 /******************************************************************************
3465 * Storage32Impl_SmallBlocksToBigBlocks
3467 * This method will convert a small block chain to a big block chain.
3468 * The small block chain will be destroyed.
3470 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3472 SmallBlockChainStream** ppsbChain)
3474 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3475 ULARGE_INTEGER size, offset;
3476 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3477 ULONG propertyIndex;
3479 HRESULT successRead;
3480 StgProperty chainProperty;
3482 BlockChainStream *bbTempChain = NULL;
3483 BlockChainStream *bigBlockChain = NULL;
3486 * Create a temporary big block chain that doesn't have
3487 * an associated property. This temporary chain will be
3488 * used to copy data from small blocks to big blocks.
3490 bbTempChain = BlockChainStream_Construct(This,
3493 if(!bbTempChain) return NULL;
3495 * Grow the big block chain.
3497 size = SmallBlockChainStream_GetSize(*ppsbChain);
3498 BlockChainStream_SetSize(bbTempChain, size);
3501 * Copy the contents of the small block chain to the big block chain
3502 * by small block size increments.
3504 offset.u.LowPart = 0;
3505 offset.u.HighPart = 0;
3509 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3512 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3514 DEF_SMALL_BLOCK_SIZE,
3517 if (FAILED(successRead))
3522 cbTotalRead += cbRead;
3524 successWrite = BlockChainStream_WriteAt(bbTempChain,
3533 cbTotalWritten += cbWritten;
3534 offset.u.LowPart += This->smallBlockSize;
3536 } while (cbRead > 0);
3537 HeapFree(GetProcessHeap(),0,buffer);
3539 assert(cbTotalRead == cbTotalWritten);
3542 * Destroy the small block chain.
3544 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3545 size.u.HighPart = 0;
3547 SmallBlockChainStream_SetSize(*ppsbChain, size);
3548 SmallBlockChainStream_Destroy(*ppsbChain);
3552 * Change the property information. This chain is now a big block chain
3553 * and it doesn't reside in the small blocks chain anymore.
3555 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3557 chainProperty.startingBlock = bbHeadOfChain;
3559 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3562 * Destroy the temporary propertyless big block chain.
3563 * Create a new big block chain associated with this property.
3565 BlockChainStream_Destroy(bbTempChain);
3566 bigBlockChain = BlockChainStream_Construct(This,
3570 return bigBlockChain;
3573 void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3575 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3577 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3578 HeapFree(GetProcessHeap(), 0, This);
3581 /******************************************************************************
3583 ** Storage32InternalImpl_Commit
3585 ** The non-root storages cannot be opened in transacted mode thus this function
3588 static HRESULT WINAPI StorageInternalImpl_Commit(
3590 DWORD grfCommitFlags) /* [in] */
3595 /******************************************************************************
3597 ** Storage32InternalImpl_Revert
3599 ** The non-root storages cannot be opened in transacted mode thus this function
3602 static HRESULT WINAPI StorageInternalImpl_Revert(
3608 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3610 IStorage_Release((IStorage*)This->parentStorage);
3611 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3612 HeapFree(GetProcessHeap(), 0, This);
3615 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3616 IEnumSTATSTG* iface,
3620 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3623 * Perform a sanity check on the parameters.
3626 return E_INVALIDARG;
3629 * Initialize the return parameter.
3634 * Compare the riid with the interface IDs implemented by this object.
3636 if (IsEqualGUID(&IID_IUnknown, riid) ||
3637 IsEqualGUID(&IID_IStorage, riid))
3639 *ppvObject = (IEnumSTATSTG*)This;
3640 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3644 return E_NOINTERFACE;
3647 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3648 IEnumSTATSTG* iface)
3650 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3651 return InterlockedIncrement(&This->ref);
3654 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3655 IEnumSTATSTG* iface)
3657 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3661 newRef = InterlockedDecrement(&This->ref);
3664 * If the reference count goes down to 0, perform suicide.
3668 IEnumSTATSTGImpl_Destroy(This);
3674 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3675 IEnumSTATSTG* iface,
3678 ULONG* pceltFetched)
3680 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3682 StgProperty currentProperty;
3683 STATSTG* currentReturnStruct = rgelt;
3684 ULONG objectFetched = 0;
3685 ULONG currentSearchNode;
3688 * Perform a sanity check on the parameters.
3690 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3691 return E_INVALIDARG;
3694 * To avoid the special case, get another pointer to a ULONG value if
3695 * the caller didn't supply one.
3697 if (pceltFetched==0)
3698 pceltFetched = &objectFetched;
3701 * Start the iteration, we will iterate until we hit the end of the
3702 * linked list or until we hit the number of items to iterate through
3707 * Start with the node at the top of the stack.
3709 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3711 while ( ( *pceltFetched < celt) &&
3712 ( currentSearchNode!=PROPERTY_NULL) )
3715 * Remove the top node from the stack
3717 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3720 * Read the property from the storage.
3722 StorageImpl_ReadProperty(This->parentStorage,
3727 * Copy the information to the return buffer.
3729 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3734 * Step to the next item in the iteration
3737 currentReturnStruct++;
3740 * Push the next search node in the search stack.
3742 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3745 * continue the iteration.
3747 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3750 if (*pceltFetched == celt)
3757 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3758 IEnumSTATSTG* iface,
3761 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3763 StgProperty currentProperty;
3764 ULONG objectFetched = 0;
3765 ULONG currentSearchNode;
3768 * Start with the node at the top of the stack.
3770 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3772 while ( (objectFetched < celt) &&
3773 (currentSearchNode!=PROPERTY_NULL) )
3776 * Remove the top node from the stack
3778 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3781 * Read the property from the storage.
3783 StorageImpl_ReadProperty(This->parentStorage,
3788 * Step to the next item in the iteration
3793 * Push the next search node in the search stack.
3795 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3798 * continue the iteration.
3800 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3803 if (objectFetched == celt)
3809 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3810 IEnumSTATSTG* iface)
3812 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3814 StgProperty rootProperty;
3815 BOOL readSuccessful;
3818 * Re-initialize the search stack to an empty stack
3820 This->stackSize = 0;
3823 * Read the root property from the storage.
3825 readSuccessful = StorageImpl_ReadProperty(
3826 This->parentStorage,
3827 This->firstPropertyNode,
3832 assert(rootProperty.sizeOfNameString!=0);
3835 * Push the search node in the search stack.
3837 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3843 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3844 IEnumSTATSTG* iface,
3845 IEnumSTATSTG** ppenum)
3847 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3849 IEnumSTATSTGImpl* newClone;
3852 * Perform a sanity check on the parameters.
3855 return E_INVALIDARG;
3857 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3858 This->firstPropertyNode);
3862 * The new clone enumeration must point to the same current node as
3865 newClone->stackSize = This->stackSize ;
3866 newClone->stackMaxSize = This->stackMaxSize ;
3867 newClone->stackToVisit =
3868 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3871 newClone->stackToVisit,
3873 sizeof(ULONG) * newClone->stackSize);
3875 *ppenum = (IEnumSTATSTG*)newClone;
3878 * Don't forget to nail down a reference to the clone before
3881 IEnumSTATSTGImpl_AddRef(*ppenum);
3886 INT IEnumSTATSTGImpl_FindParentProperty(
3887 IEnumSTATSTGImpl *This,
3888 ULONG childProperty,
3889 StgProperty *currentProperty,
3892 ULONG currentSearchNode;
3896 * To avoid the special case, get another pointer to a ULONG value if
3897 * the caller didn't supply one.
3900 thisNodeId = &foundNode;
3903 * Start with the node at the top of the stack.
3905 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3908 while (currentSearchNode!=PROPERTY_NULL)
3911 * Store the current node in the returned parameters
3913 *thisNodeId = currentSearchNode;
3916 * Remove the top node from the stack
3918 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3921 * Read the property from the storage.
3923 StorageImpl_ReadProperty(
3924 This->parentStorage,
3928 if (currentProperty->previousProperty == childProperty)
3929 return PROPERTY_RELATION_PREVIOUS;
3931 else if (currentProperty->nextProperty == childProperty)
3932 return PROPERTY_RELATION_NEXT;
3934 else if (currentProperty->dirProperty == childProperty)
3935 return PROPERTY_RELATION_DIR;
3938 * Push the next search node in the search stack.
3940 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3943 * continue the iteration.
3945 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3948 return PROPERTY_NULL;
3951 ULONG IEnumSTATSTGImpl_FindProperty(
3952 IEnumSTATSTGImpl* This,
3953 const OLECHAR* lpszPropName,
3954 StgProperty* currentProperty)
3956 ULONG currentSearchNode;
3959 * Start with the node at the top of the stack.
3961 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3963 while (currentSearchNode!=PROPERTY_NULL)
3966 * Remove the top node from the stack
3968 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3971 * Read the property from the storage.
3973 StorageImpl_ReadProperty(This->parentStorage,
3977 if ( propertyNameCmp(
3978 (const OLECHAR*)currentProperty->name,
3979 (const OLECHAR*)lpszPropName) == 0)
3980 return currentSearchNode;
3983 * Push the next search node in the search stack.
3985 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3988 * continue the iteration.
3990 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3993 return PROPERTY_NULL;
3996 void IEnumSTATSTGImpl_PushSearchNode(
3997 IEnumSTATSTGImpl* This,
4000 StgProperty rootProperty;
4001 BOOL readSuccessful;
4004 * First, make sure we're not trying to push an unexisting node.
4006 if (nodeToPush==PROPERTY_NULL)
4010 * First push the node to the stack
4012 if (This->stackSize == This->stackMaxSize)
4014 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4016 This->stackToVisit = HeapReAlloc(
4020 sizeof(ULONG) * This->stackMaxSize);
4023 This->stackToVisit[This->stackSize] = nodeToPush;
4027 * Read the root property from the storage.
4029 readSuccessful = StorageImpl_ReadProperty(
4030 This->parentStorage,
4036 assert(rootProperty.sizeOfNameString!=0);
4039 * Push the previous search node in the search stack.
4041 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4045 ULONG IEnumSTATSTGImpl_PopSearchNode(
4046 IEnumSTATSTGImpl* This,
4051 if (This->stackSize == 0)
4052 return PROPERTY_NULL;
4054 topNode = This->stackToVisit[This->stackSize-1];
4063 * Virtual function table for the IEnumSTATSTGImpl class.
4065 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4067 IEnumSTATSTGImpl_QueryInterface,
4068 IEnumSTATSTGImpl_AddRef,
4069 IEnumSTATSTGImpl_Release,
4070 IEnumSTATSTGImpl_Next,
4071 IEnumSTATSTGImpl_Skip,
4072 IEnumSTATSTGImpl_Reset,
4073 IEnumSTATSTGImpl_Clone
4076 /******************************************************************************
4077 ** IEnumSTATSTGImpl implementation
4080 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4081 StorageImpl* parentStorage,
4082 ULONG firstPropertyNode)
4084 IEnumSTATSTGImpl* newEnumeration;
4086 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4088 if (newEnumeration!=0)
4091 * Set-up the virtual function table and reference count.
4093 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4094 newEnumeration->ref = 0;
4097 * We want to nail-down the reference to the storage in case the
4098 * enumeration out-lives the storage in the client application.
4100 newEnumeration->parentStorage = parentStorage;
4101 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4103 newEnumeration->firstPropertyNode = firstPropertyNode;
4106 * Initialize the search stack
4108 newEnumeration->stackSize = 0;
4109 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4110 newEnumeration->stackToVisit =
4111 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4114 * Make sure the current node of the iterator is the first one.
4116 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4119 return newEnumeration;
4123 * Virtual function table for the Storage32InternalImpl class.
4125 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4127 StorageBaseImpl_QueryInterface,
4128 StorageBaseImpl_AddRef,
4129 StorageBaseImpl_Release,
4130 StorageBaseImpl_CreateStream,
4131 StorageBaseImpl_OpenStream,
4132 StorageImpl_CreateStorage,
4133 StorageBaseImpl_OpenStorage,
4135 StorageImpl_MoveElementTo,
4136 StorageInternalImpl_Commit,
4137 StorageInternalImpl_Revert,
4138 StorageBaseImpl_EnumElements,
4139 StorageImpl_DestroyElement,
4140 StorageBaseImpl_RenameElement,
4141 StorageImpl_SetElementTimes,
4142 StorageBaseImpl_SetClass,
4143 StorageImpl_SetStateBits,
4144 StorageBaseImpl_Stat
4147 /******************************************************************************
4148 ** Storage32InternalImpl implementation
4151 StorageInternalImpl* StorageInternalImpl_Construct(
4152 StorageImpl* ancestorStorage,
4154 ULONG rootPropertyIndex)
4156 StorageInternalImpl* newStorage;
4159 * Allocate space for the new storage object
4161 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
4165 memset(newStorage, 0, sizeof(StorageInternalImpl));
4168 * Initialize the stream list
4171 list_init(&newStorage->base.strmHead);
4174 * Initialize the virtual function table.
4176 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4177 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4178 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4181 * Keep the ancestor storage pointer and nail a reference to it.
4183 newStorage->base.ancestorStorage = ancestorStorage;
4184 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4187 * Keep the index of the root property set for this storage,
4189 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4197 /******************************************************************************
4198 ** StorageUtl implementation
4201 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4205 memcpy(&tmp, buffer+offset, sizeof(WORD));
4206 *value = le16toh(tmp);
4209 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4211 value = htole16(value);
4212 memcpy(buffer+offset, &value, sizeof(WORD));
4215 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4219 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4220 *value = le32toh(tmp);
4223 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4225 value = htole32(value);
4226 memcpy(buffer+offset, &value, sizeof(DWORD));
4229 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4230 ULARGE_INTEGER* value)
4232 #ifdef WORDS_BIGENDIAN
4235 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4236 value->u.LowPart = htole32(tmp.u.HighPart);
4237 value->u.HighPart = htole32(tmp.u.LowPart);
4239 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4243 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4244 const ULARGE_INTEGER *value)
4246 #ifdef WORDS_BIGENDIAN
4249 tmp.u.LowPart = htole32(value->u.HighPart);
4250 tmp.u.HighPart = htole32(value->u.LowPart);
4251 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4253 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4257 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4259 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4260 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4261 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4263 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4266 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4268 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4269 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4270 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4272 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4275 void StorageUtl_CopyPropertyToSTATSTG(
4276 STATSTG* destination,
4277 StgProperty* source,
4281 * The copy of the string occurs only when the flag is not set
4283 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4284 (source->name == NULL) ||
4285 (source->name[0] == 0) )
4287 destination->pwcsName = 0;
4291 destination->pwcsName =
4292 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4294 strcpyW((LPWSTR)destination->pwcsName, source->name);
4297 switch (source->propertyType)
4299 case PROPTYPE_STORAGE:
4301 destination->type = STGTY_STORAGE;
4303 case PROPTYPE_STREAM:
4304 destination->type = STGTY_STREAM;
4307 destination->type = STGTY_STREAM;
4311 destination->cbSize = source->size;
4313 currentReturnStruct->mtime = {0}; TODO
4314 currentReturnStruct->ctime = {0};
4315 currentReturnStruct->atime = {0};
4317 destination->grfMode = 0;
4318 destination->grfLocksSupported = 0;
4319 destination->clsid = source->propertyUniqueID;
4320 destination->grfStateBits = 0;
4321 destination->reserved = 0;
4324 /******************************************************************************
4325 ** BlockChainStream implementation
4328 BlockChainStream* BlockChainStream_Construct(
4329 StorageImpl* parentStorage,
4330 ULONG* headOfStreamPlaceHolder,
4331 ULONG propertyIndex)
4333 BlockChainStream* newStream;
4336 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4338 newStream->parentStorage = parentStorage;
4339 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4340 newStream->ownerPropertyIndex = propertyIndex;
4341 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4342 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4343 newStream->numBlocks = 0;
4345 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4347 while (blockIndex != BLOCK_END_OF_CHAIN)
4349 newStream->numBlocks++;
4350 newStream->tailIndex = blockIndex;
4352 if(FAILED(StorageImpl_GetNextBlockInChain(
4357 HeapFree(GetProcessHeap(), 0, newStream);
4365 void BlockChainStream_Destroy(BlockChainStream* This)
4367 HeapFree(GetProcessHeap(), 0, This);
4370 /******************************************************************************
4371 * BlockChainStream_GetHeadOfChain
4373 * Returns the head of this stream chain.
4374 * Some special chains don't have properties, their heads are kept in
4375 * This->headOfStreamPlaceHolder.
4378 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4380 StgProperty chainProperty;
4381 BOOL readSuccessful;
4383 if (This->headOfStreamPlaceHolder != 0)
4384 return *(This->headOfStreamPlaceHolder);
4386 if (This->ownerPropertyIndex != PROPERTY_NULL)
4388 readSuccessful = StorageImpl_ReadProperty(
4389 This->parentStorage,
4390 This->ownerPropertyIndex,
4395 return chainProperty.startingBlock;
4399 return BLOCK_END_OF_CHAIN;
4402 /******************************************************************************
4403 * BlockChainStream_GetCount
4405 * Returns the number of blocks that comprises this chain.
4406 * This is not the size of the stream as the last block may not be full!
4409 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4414 blockIndex = BlockChainStream_GetHeadOfChain(This);
4416 while (blockIndex != BLOCK_END_OF_CHAIN)
4420 if(FAILED(StorageImpl_GetNextBlockInChain(
4421 This->parentStorage,
4430 /******************************************************************************
4431 * BlockChainStream_ReadAt
4433 * Reads a specified number of bytes from this chain at the specified offset.
4434 * bytesRead may be NULL.
4435 * Failure will be returned if the specified number of bytes has not been read.
4437 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4438 ULARGE_INTEGER offset,
4443 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4444 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4445 ULONG bytesToReadInBuffer;
4448 BYTE* bigBlockBuffer;
4451 * Find the first block in the stream that contains part of the buffer.
4453 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4454 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4455 (blockNoInSequence < This->lastBlockNoInSequence) )
4457 blockIndex = BlockChainStream_GetHeadOfChain(This);
4458 This->lastBlockNoInSequence = blockNoInSequence;
4462 ULONG temp = blockNoInSequence;
4464 blockIndex = This->lastBlockNoInSequenceIndex;
4465 blockNoInSequence -= This->lastBlockNoInSequence;
4466 This->lastBlockNoInSequence = temp;
4469 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4471 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4473 blockNoInSequence--;
4476 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4477 return FALSE; /* We failed to find the starting block */
4479 This->lastBlockNoInSequenceIndex = blockIndex;
4482 * Start reading the buffer.
4485 bufferWalker = buffer;
4487 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4490 * Calculate how many bytes we can copy from this big block.
4492 bytesToReadInBuffer =
4493 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4496 * Copy those bytes to the buffer
4499 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4500 if (!bigBlockBuffer)
4503 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4505 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4508 * Step to the next big block.
4510 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4513 bufferWalker += bytesToReadInBuffer;
4514 size -= bytesToReadInBuffer;
4515 *bytesRead += bytesToReadInBuffer;
4516 offsetInBlock = 0; /* There is no offset on the next block */
4523 /******************************************************************************
4524 * BlockChainStream_WriteAt
4526 * Writes the specified number of bytes to this chain at the specified offset.
4527 * bytesWritten may be NULL.
4528 * Will fail if not all specified number of bytes have been written.
4530 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4531 ULARGE_INTEGER offset,
4534 ULONG* bytesWritten)
4536 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4537 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4540 const BYTE* bufferWalker;
4541 BYTE* bigBlockBuffer;
4544 * Find the first block in the stream that contains part of the buffer.
4546 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4547 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4548 (blockNoInSequence < This->lastBlockNoInSequence) )
4550 blockIndex = BlockChainStream_GetHeadOfChain(This);
4551 This->lastBlockNoInSequence = blockNoInSequence;
4555 ULONG temp = blockNoInSequence;
4557 blockIndex = This->lastBlockNoInSequenceIndex;
4558 blockNoInSequence -= This->lastBlockNoInSequence;
4559 This->lastBlockNoInSequence = temp;
4562 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4564 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4567 blockNoInSequence--;
4570 This->lastBlockNoInSequenceIndex = blockIndex;
4573 * Here, I'm casting away the constness on the buffer variable
4574 * This is OK since we don't intend to modify that buffer.
4577 bufferWalker = (const BYTE*)buffer;
4579 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4582 * Calculate how many bytes we can copy from this big block.
4585 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4588 * Copy those bytes to the buffer
4590 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4592 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4594 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4597 * Step to the next big block.
4599 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4602 bufferWalker += bytesToWrite;
4603 size -= bytesToWrite;
4604 *bytesWritten += bytesToWrite;
4605 offsetInBlock = 0; /* There is no offset on the next block */
4611 /******************************************************************************
4612 * BlockChainStream_Shrink
4614 * Shrinks this chain in the big block depot.
4616 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4617 ULARGE_INTEGER newSize)
4619 ULONG blockIndex, extraBlock;
4624 * Reset the last accessed block cache.
4626 This->lastBlockNoInSequence = 0xFFFFFFFF;
4627 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4630 * Figure out how many blocks are needed to contain the new size
4632 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4634 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4637 blockIndex = BlockChainStream_GetHeadOfChain(This);
4640 * Go to the new end of chain
4642 while (count < numBlocks)
4644 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4650 /* Get the next block before marking the new end */
4651 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4655 /* Mark the new end of chain */
4656 StorageImpl_SetNextBlockInChain(
4657 This->parentStorage,
4659 BLOCK_END_OF_CHAIN);
4661 This->tailIndex = blockIndex;
4662 This->numBlocks = numBlocks;
4665 * Mark the extra blocks as free
4667 while (extraBlock != BLOCK_END_OF_CHAIN)
4669 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4672 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4673 extraBlock = blockIndex;
4679 /******************************************************************************
4680 * BlockChainStream_Enlarge
4682 * Grows this chain in the big block depot.
4684 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4685 ULARGE_INTEGER newSize)
4687 ULONG blockIndex, currentBlock;
4689 ULONG oldNumBlocks = 0;
4691 blockIndex = BlockChainStream_GetHeadOfChain(This);
4694 * Empty chain. Create the head.
4696 if (blockIndex == BLOCK_END_OF_CHAIN)
4698 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4699 StorageImpl_SetNextBlockInChain(This->parentStorage,
4701 BLOCK_END_OF_CHAIN);
4703 if (This->headOfStreamPlaceHolder != 0)
4705 *(This->headOfStreamPlaceHolder) = blockIndex;
4709 StgProperty chainProp;
4710 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4712 StorageImpl_ReadProperty(
4713 This->parentStorage,
4714 This->ownerPropertyIndex,
4717 chainProp.startingBlock = blockIndex;
4719 StorageImpl_WriteProperty(
4720 This->parentStorage,
4721 This->ownerPropertyIndex,
4725 This->tailIndex = blockIndex;
4726 This->numBlocks = 1;
4730 * Figure out how many blocks are needed to contain this stream
4732 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4734 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4738 * Go to the current end of chain
4740 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4742 currentBlock = blockIndex;
4744 while (blockIndex != BLOCK_END_OF_CHAIN)
4747 currentBlock = blockIndex;
4749 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4754 This->tailIndex = currentBlock;
4757 currentBlock = This->tailIndex;
4758 oldNumBlocks = This->numBlocks;
4761 * Add new blocks to the chain
4763 if (oldNumBlocks < newNumBlocks)
4765 while (oldNumBlocks < newNumBlocks)
4767 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4769 StorageImpl_SetNextBlockInChain(
4770 This->parentStorage,
4774 StorageImpl_SetNextBlockInChain(
4775 This->parentStorage,
4777 BLOCK_END_OF_CHAIN);
4779 currentBlock = blockIndex;
4783 This->tailIndex = blockIndex;
4784 This->numBlocks = newNumBlocks;
4790 /******************************************************************************
4791 * BlockChainStream_SetSize
4793 * Sets the size of this stream. The big block depot will be updated.
4794 * The file will grow if we grow the chain.
4796 * TODO: Free the actual blocks in the file when we shrink the chain.
4797 * Currently, the blocks are still in the file. So the file size
4798 * doesn't shrink even if we shrink streams.
4800 BOOL BlockChainStream_SetSize(
4801 BlockChainStream* This,
4802 ULARGE_INTEGER newSize)
4804 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4806 if (newSize.u.LowPart == size.u.LowPart)
4809 if (newSize.u.LowPart < size.u.LowPart)
4811 BlockChainStream_Shrink(This, newSize);
4815 BlockChainStream_Enlarge(This, newSize);
4821 /******************************************************************************
4822 * BlockChainStream_GetSize
4824 * Returns the size of this chain.
4825 * Will return the block count if this chain doesn't have a property.
4827 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4829 StgProperty chainProperty;
4831 if(This->headOfStreamPlaceHolder == NULL)
4834 * This chain is a data stream read the property and return
4835 * the appropriate size
4837 StorageImpl_ReadProperty(
4838 This->parentStorage,
4839 This->ownerPropertyIndex,
4842 return chainProperty.size;
4847 * this chain is a chain that does not have a property, figure out the
4848 * size by making the product number of used blocks times the
4851 ULARGE_INTEGER result;
4852 result.u.HighPart = 0;
4855 BlockChainStream_GetCount(This) *
4856 This->parentStorage->bigBlockSize;
4862 /******************************************************************************
4863 ** SmallBlockChainStream implementation
4866 SmallBlockChainStream* SmallBlockChainStream_Construct(
4867 StorageImpl* parentStorage,
4868 ULONG propertyIndex)
4870 SmallBlockChainStream* newStream;
4872 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4874 newStream->parentStorage = parentStorage;
4875 newStream->ownerPropertyIndex = propertyIndex;
4880 void SmallBlockChainStream_Destroy(
4881 SmallBlockChainStream* This)
4883 HeapFree(GetProcessHeap(), 0, This);
4886 /******************************************************************************
4887 * SmallBlockChainStream_GetHeadOfChain
4889 * Returns the head of this chain of small blocks.
4891 ULONG SmallBlockChainStream_GetHeadOfChain(
4892 SmallBlockChainStream* This)
4894 StgProperty chainProperty;
4895 BOOL readSuccessful;
4897 if (This->ownerPropertyIndex)
4899 readSuccessful = StorageImpl_ReadProperty(
4900 This->parentStorage,
4901 This->ownerPropertyIndex,
4906 return chainProperty.startingBlock;
4911 return BLOCK_END_OF_CHAIN;
4914 /******************************************************************************
4915 * SmallBlockChainStream_GetNextBlockInChain
4917 * Returns the index of the next small block in this chain.
4920 * - BLOCK_END_OF_CHAIN: end of this chain
4921 * - BLOCK_UNUSED: small block 'blockIndex' is free
4923 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4924 SmallBlockChainStream* This,
4926 ULONG* nextBlockInChain)
4928 ULARGE_INTEGER offsetOfBlockInDepot;
4933 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4935 offsetOfBlockInDepot.u.HighPart = 0;
4936 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4939 * Read those bytes in the buffer from the small block file.
4941 success = BlockChainStream_ReadAt(
4942 This->parentStorage->smallBlockDepotChain,
4943 offsetOfBlockInDepot,
4950 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4954 return STG_E_READFAULT;
4957 /******************************************************************************
4958 * SmallBlockChainStream_SetNextBlockInChain
4960 * Writes the index of the next block of the specified block in the small
4962 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4963 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4965 void SmallBlockChainStream_SetNextBlockInChain(
4966 SmallBlockChainStream* This,
4970 ULARGE_INTEGER offsetOfBlockInDepot;
4974 offsetOfBlockInDepot.u.HighPart = 0;
4975 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4977 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4980 * Read those bytes in the buffer from the small block file.
4982 BlockChainStream_WriteAt(
4983 This->parentStorage->smallBlockDepotChain,
4984 offsetOfBlockInDepot,
4990 /******************************************************************************
4991 * SmallBlockChainStream_FreeBlock
4993 * Flag small block 'blockIndex' as free in the small block depot.
4995 void SmallBlockChainStream_FreeBlock(
4996 SmallBlockChainStream* This,
4999 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5002 /******************************************************************************
5003 * SmallBlockChainStream_GetNextFreeBlock
5005 * Returns the index of a free small block. The small block depot will be
5006 * enlarged if necessary. The small block chain will also be enlarged if
5009 ULONG SmallBlockChainStream_GetNextFreeBlock(
5010 SmallBlockChainStream* This)
5012 ULARGE_INTEGER offsetOfBlockInDepot;
5015 ULONG blockIndex = 0;
5016 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5017 BOOL success = TRUE;
5018 ULONG smallBlocksPerBigBlock;
5020 offsetOfBlockInDepot.u.HighPart = 0;
5023 * Scan the small block depot for a free block
5025 while (nextBlockIndex != BLOCK_UNUSED)
5027 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5029 success = BlockChainStream_ReadAt(
5030 This->parentStorage->smallBlockDepotChain,
5031 offsetOfBlockInDepot,
5037 * If we run out of space for the small block depot, enlarge it
5041 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5043 if (nextBlockIndex != BLOCK_UNUSED)
5049 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5051 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5052 ULONG nextBlock, newsbdIndex;
5053 BYTE* smallBlockDepot;
5055 nextBlock = sbdIndex;
5056 while (nextBlock != BLOCK_END_OF_CHAIN)
5058 sbdIndex = nextBlock;
5059 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5062 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5063 if (sbdIndex != BLOCK_END_OF_CHAIN)
5064 StorageImpl_SetNextBlockInChain(
5065 This->parentStorage,
5069 StorageImpl_SetNextBlockInChain(
5070 This->parentStorage,
5072 BLOCK_END_OF_CHAIN);
5075 * Initialize all the small blocks to free
5078 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
5080 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5081 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
5086 * We have just created the small block depot.
5088 StgProperty rootProp;
5092 * Save it in the header
5094 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5095 StorageImpl_SaveFileHeader(This->parentStorage);
5098 * And allocate the first big block that will contain small blocks
5101 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5103 StorageImpl_SetNextBlockInChain(
5104 This->parentStorage,
5106 BLOCK_END_OF_CHAIN);
5108 StorageImpl_ReadProperty(
5109 This->parentStorage,
5110 This->parentStorage->base.rootPropertySetIndex,
5113 rootProp.startingBlock = sbStartIndex;
5114 rootProp.size.u.HighPart = 0;
5115 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5117 StorageImpl_WriteProperty(
5118 This->parentStorage,
5119 This->parentStorage->base.rootPropertySetIndex,
5125 smallBlocksPerBigBlock =
5126 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5129 * Verify if we have to allocate big blocks to contain small blocks
5131 if (blockIndex % smallBlocksPerBigBlock == 0)
5133 StgProperty rootProp;
5134 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5136 StorageImpl_ReadProperty(
5137 This->parentStorage,
5138 This->parentStorage->base.rootPropertySetIndex,
5141 if (rootProp.size.u.LowPart <
5142 (blocksRequired * This->parentStorage->bigBlockSize))
5144 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5146 BlockChainStream_SetSize(
5147 This->parentStorage->smallBlockRootChain,
5150 StorageImpl_WriteProperty(
5151 This->parentStorage,
5152 This->parentStorage->base.rootPropertySetIndex,
5160 /******************************************************************************
5161 * SmallBlockChainStream_ReadAt
5163 * Reads a specified number of bytes from this chain at the specified offset.
5164 * bytesRead may be NULL.
5165 * Failure will be returned if the specified number of bytes has not been read.
5167 HRESULT SmallBlockChainStream_ReadAt(
5168 SmallBlockChainStream* This,
5169 ULARGE_INTEGER offset,
5175 ULARGE_INTEGER offsetInBigBlockFile;
5176 ULONG blockNoInSequence =
5177 offset.u.LowPart / This->parentStorage->smallBlockSize;
5179 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5180 ULONG bytesToReadInBuffer;
5182 ULONG bytesReadFromBigBlockFile;
5186 * This should never happen on a small block file.
5188 assert(offset.u.HighPart==0);
5191 * Find the first block in the stream that contains part of the buffer.
5193 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5195 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5197 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5200 blockNoInSequence--;
5204 * Start reading the buffer.
5207 bufferWalker = buffer;
5209 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5212 * Calculate how many bytes we can copy from this small block.
5214 bytesToReadInBuffer =
5215 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5218 * Calculate the offset of the small block in the small block file.
5220 offsetInBigBlockFile.u.HighPart = 0;
5221 offsetInBigBlockFile.u.LowPart =
5222 blockIndex * This->parentStorage->smallBlockSize;
5224 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5227 * Read those bytes in the buffer from the small block file.
5228 * The small block has already been identified so it shouldn't fail
5229 * unless the file is corrupt.
5231 if (!BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5232 offsetInBigBlockFile,
5233 bytesToReadInBuffer,
5235 &bytesReadFromBigBlockFile))
5236 return STG_E_DOCFILECORRUPT;
5238 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5241 * Step to the next big block.
5243 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5247 bufferWalker += bytesToReadInBuffer;
5248 size -= bytesToReadInBuffer;
5249 *bytesRead += bytesToReadInBuffer;
5250 offsetInBlock = 0; /* There is no offset on the next block */
5256 /******************************************************************************
5257 * SmallBlockChainStream_WriteAt
5259 * Writes the specified number of bytes to this chain at the specified offset.
5260 * bytesWritten may be NULL.
5261 * Will fail if not all specified number of bytes have been written.
5263 BOOL SmallBlockChainStream_WriteAt(
5264 SmallBlockChainStream* This,
5265 ULARGE_INTEGER offset,
5268 ULONG* bytesWritten)
5270 ULARGE_INTEGER offsetInBigBlockFile;
5271 ULONG blockNoInSequence =
5272 offset.u.LowPart / This->parentStorage->smallBlockSize;
5274 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5275 ULONG bytesToWriteInBuffer;
5277 ULONG bytesWrittenFromBigBlockFile;
5278 const BYTE* bufferWalker;
5281 * This should never happen on a small block file.
5283 assert(offset.u.HighPart==0);
5286 * Find the first block in the stream that contains part of the buffer.
5288 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5290 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5292 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5294 blockNoInSequence--;
5298 * Start writing the buffer.
5300 * Here, I'm casting away the constness on the buffer variable
5301 * This is OK since we don't intend to modify that buffer.
5304 bufferWalker = (const BYTE*)buffer;
5305 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5308 * Calculate how many bytes we can copy to this small block.
5310 bytesToWriteInBuffer =
5311 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5314 * Calculate the offset of the small block in the small block file.
5316 offsetInBigBlockFile.u.HighPart = 0;
5317 offsetInBigBlockFile.u.LowPart =
5318 blockIndex * This->parentStorage->smallBlockSize;
5320 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5323 * Write those bytes in the buffer to the small block file.
5325 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5326 offsetInBigBlockFile,
5327 bytesToWriteInBuffer,
5329 &bytesWrittenFromBigBlockFile);
5331 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5334 * Step to the next big block.
5336 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5339 bufferWalker += bytesToWriteInBuffer;
5340 size -= bytesToWriteInBuffer;
5341 *bytesWritten += bytesToWriteInBuffer;
5342 offsetInBlock = 0; /* There is no offset on the next block */
5348 /******************************************************************************
5349 * SmallBlockChainStream_Shrink
5351 * Shrinks this chain in the small block depot.
5353 BOOL SmallBlockChainStream_Shrink(
5354 SmallBlockChainStream* This,
5355 ULARGE_INTEGER newSize)
5357 ULONG blockIndex, extraBlock;
5361 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5363 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5366 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5369 * Go to the new end of chain
5371 while (count < numBlocks)
5373 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5380 * If the count is 0, we have a special case, the head of the chain was
5385 StgProperty chainProp;
5387 StorageImpl_ReadProperty(This->parentStorage,
5388 This->ownerPropertyIndex,
5391 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5393 StorageImpl_WriteProperty(This->parentStorage,
5394 This->ownerPropertyIndex,
5398 * We start freeing the chain at the head block.
5400 extraBlock = blockIndex;
5404 /* Get the next block before marking the new end */
5405 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5409 /* Mark the new end of chain */
5410 SmallBlockChainStream_SetNextBlockInChain(
5413 BLOCK_END_OF_CHAIN);
5417 * Mark the extra blocks as free
5419 while (extraBlock != BLOCK_END_OF_CHAIN)
5421 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5424 SmallBlockChainStream_FreeBlock(This, extraBlock);
5425 extraBlock = blockIndex;
5431 /******************************************************************************
5432 * SmallBlockChainStream_Enlarge
5434 * Grows this chain in the small block depot.
5436 BOOL SmallBlockChainStream_Enlarge(
5437 SmallBlockChainStream* This,
5438 ULARGE_INTEGER newSize)
5440 ULONG blockIndex, currentBlock;
5442 ULONG oldNumBlocks = 0;
5444 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5449 if (blockIndex == BLOCK_END_OF_CHAIN)
5452 StgProperty chainProp;
5454 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5457 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5459 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5462 blockIndex = chainProp.startingBlock;
5463 SmallBlockChainStream_SetNextBlockInChain(
5466 BLOCK_END_OF_CHAIN);
5469 currentBlock = blockIndex;
5472 * Figure out how many blocks are needed to contain this stream
5474 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5476 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5480 * Go to the current end of chain
5482 while (blockIndex != BLOCK_END_OF_CHAIN)
5485 currentBlock = blockIndex;
5486 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5491 * Add new blocks to the chain
5493 while (oldNumBlocks < newNumBlocks)
5495 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5496 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5498 SmallBlockChainStream_SetNextBlockInChain(
5501 BLOCK_END_OF_CHAIN);
5503 currentBlock = blockIndex;
5510 /******************************************************************************
5511 * SmallBlockChainStream_GetCount
5513 * Returns the number of blocks that comprises this chain.
5514 * This is not the size of this chain as the last block may not be full!
5516 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5521 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5523 while (blockIndex != BLOCK_END_OF_CHAIN)
5527 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5534 /******************************************************************************
5535 * SmallBlockChainStream_SetSize
5537 * Sets the size of this stream.
5538 * The file will grow if we grow the chain.
5540 * TODO: Free the actual blocks in the file when we shrink the chain.
5541 * Currently, the blocks are still in the file. So the file size
5542 * doesn't shrink even if we shrink streams.
5544 BOOL SmallBlockChainStream_SetSize(
5545 SmallBlockChainStream* This,
5546 ULARGE_INTEGER newSize)
5548 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5550 if (newSize.u.LowPart == size.u.LowPart)
5553 if (newSize.u.LowPart < size.u.LowPart)
5555 SmallBlockChainStream_Shrink(This, newSize);
5559 SmallBlockChainStream_Enlarge(This, newSize);
5565 /******************************************************************************
5566 * SmallBlockChainStream_GetSize
5568 * Returns the size of this chain.
5570 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5572 StgProperty chainProperty;
5574 StorageImpl_ReadProperty(
5575 This->parentStorage,
5576 This->ownerPropertyIndex,
5579 return chainProperty.size;
5582 /******************************************************************************
5583 * StgCreateDocfile [OLE32.@]
5584 * Creates a new compound file storage object
5587 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5588 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5589 * reserved [ ?] unused?, usually 0
5590 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5593 * S_OK if the file was successfully created
5594 * some STG_E_ value if error
5596 * if pwcsName is NULL, create file with new unique name
5597 * the function can returns
5598 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5601 HRESULT WINAPI StgCreateDocfile(
5605 IStorage **ppstgOpen)
5607 StorageImpl* newStorage = 0;
5608 HANDLE hFile = INVALID_HANDLE_VALUE;
5609 HRESULT hr = STG_E_INVALIDFLAG;
5613 DWORD fileAttributes;
5614 WCHAR tempFileName[MAX_PATH];
5616 TRACE("(%s, %lx, %ld, %p)\n",
5617 debugstr_w(pwcsName), grfMode,
5618 reserved, ppstgOpen);
5621 * Validate the parameters
5624 return STG_E_INVALIDPOINTER;
5626 return STG_E_INVALIDPARAMETER;
5629 * Validate the STGM flags
5631 if ( FAILED( validateSTGM(grfMode) ))
5634 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5635 switch(STGM_ACCESS_MODE(grfMode))
5638 case STGM_READWRITE:
5644 /* if no share mode given then DENY_NONE is the default */
5645 if (STGM_SHARE_MODE(grfMode) == 0)
5646 grfMode |= STGM_SHARE_DENY_NONE;
5648 /* must have at least one access mode */
5649 if (STGM_ACCESS_MODE(grfMode) == 0)
5652 /* in direct mode, can only use SHARE_EXCLUSIVE */
5653 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5656 /* but in transacted mode, any share mode is valid */
5659 * Generate a unique name.
5663 WCHAR tempPath[MAX_PATH];
5664 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5666 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
5669 memset(tempPath, 0, sizeof(tempPath));
5670 memset(tempFileName, 0, sizeof(tempFileName));
5672 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5675 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5676 pwcsName = tempFileName;
5679 hr = STG_E_INSUFFICIENTMEMORY;
5683 creationMode = TRUNCATE_EXISTING;
5687 creationMode = GetCreationModeFromSTGM(grfMode);
5691 * Interpret the STGM value grfMode
5693 shareMode = GetShareModeFromSTGM(grfMode);
5694 accessMode = GetAccessModeFromSTGM(grfMode);
5696 if (grfMode & STGM_DELETEONRELEASE)
5697 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5699 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5701 if (grfMode & STGM_TRANSACTED)
5702 FIXME("Transacted mode not implemented.\n");
5705 * Initialize the "out" parameter.
5709 hFile = CreateFileW(pwcsName,
5717 if (hFile == INVALID_HANDLE_VALUE)
5719 if(GetLastError() == ERROR_FILE_EXISTS)
5720 hr = STG_E_FILEALREADYEXISTS;
5727 * Allocate and initialize the new IStorage32object.
5729 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5731 if (newStorage == 0)
5733 hr = STG_E_INSUFFICIENTMEMORY;
5737 hr = StorageImpl_Construct(
5748 HeapFree(GetProcessHeap(), 0, newStorage);
5753 * Get an "out" pointer for the caller.
5755 hr = StorageBaseImpl_QueryInterface(
5756 (IStorage*)newStorage,
5757 (REFIID)&IID_IStorage,
5760 TRACE("<-- %p r = %08lx\n", *ppstgOpen, hr);
5765 /******************************************************************************
5766 * StgCreateStorageEx [OLE32.@]
5768 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5770 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5771 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5773 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5775 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5776 return STG_E_INVALIDPARAMETER;
5779 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5781 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5782 return STG_E_INVALIDPARAMETER;
5785 if (stgfmt == STGFMT_FILE)
5787 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5788 return STG_E_INVALIDPARAMETER;
5791 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5793 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5794 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5797 ERR("Invalid stgfmt argument\n");
5798 return STG_E_INVALIDPARAMETER;
5801 /******************************************************************************
5802 * StgCreatePropSetStg [OLE32.@]
5804 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5805 IPropertySetStorage **ppPropSetStg)
5809 TRACE("(%p, 0x%lx, %p)\n", pstg, reserved, ppPropSetStg);
5811 hr = STG_E_INVALIDPARAMETER;
5813 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5814 (void**)ppPropSetStg);
5818 /******************************************************************************
5819 * StgOpenStorageEx [OLE32.@]
5821 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5823 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5824 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5826 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5828 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5829 return STG_E_INVALIDPARAMETER;
5835 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5836 return STG_E_INVALIDPARAMETER;
5838 case STGFMT_STORAGE:
5841 case STGFMT_DOCFILE:
5842 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5844 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5845 return STG_E_INVALIDPARAMETER;
5847 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5851 WARN("STGFMT_ANY assuming storage\n");
5855 return STG_E_INVALIDPARAMETER;
5858 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5862 /******************************************************************************
5863 * StgOpenStorage [OLE32.@]
5865 HRESULT WINAPI StgOpenStorage(
5866 const OLECHAR *pwcsName,
5867 IStorage *pstgPriority,
5871 IStorage **ppstgOpen)
5873 StorageImpl* newStorage = 0;
5878 WCHAR fullname[MAX_PATH];
5881 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5882 debugstr_w(pwcsName), pstgPriority, grfMode,
5883 snbExclude, reserved, ppstgOpen);
5886 * Perform sanity checks
5890 hr = STG_E_INVALIDNAME;
5896 hr = STG_E_INVALIDPOINTER;
5902 hr = STG_E_INVALIDPARAMETER;
5906 if (grfMode & STGM_PRIORITY)
5908 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5909 return STG_E_INVALIDFLAG;
5910 if (grfMode & STGM_DELETEONRELEASE)
5911 return STG_E_INVALIDFUNCTION;
5912 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5913 return STG_E_INVALIDFLAG;
5914 grfMode &= ~0xf0; /* remove the existing sharing mode */
5915 grfMode |= STGM_SHARE_DENY_NONE;
5917 /* STGM_PRIORITY stops other IStorage objects on the same file from
5918 * committing until the STGM_PRIORITY IStorage is closed. it also
5919 * stops non-transacted mode StgOpenStorage calls with write access from
5920 * succeeding. obviously, both of these cannot be achieved through just
5921 * file share flags */
5922 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5926 * Validate the sharing mode
5928 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5929 switch(STGM_SHARE_MODE(grfMode))
5931 case STGM_SHARE_EXCLUSIVE:
5932 case STGM_SHARE_DENY_WRITE:
5935 hr = STG_E_INVALIDFLAG;
5940 * Validate the STGM flags
5942 if ( FAILED( validateSTGM(grfMode) ) ||
5943 (grfMode&STGM_CREATE))
5945 hr = STG_E_INVALIDFLAG;
5949 /* shared reading requires transacted mode */
5950 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5951 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5952 !(grfMode&STGM_TRANSACTED) )
5954 hr = STG_E_INVALIDFLAG;
5959 * Interpret the STGM value grfMode
5961 shareMode = GetShareModeFromSTGM(grfMode);
5962 accessMode = GetAccessModeFromSTGM(grfMode);
5965 * Initialize the "out" parameter.
5969 hFile = CreateFileW( pwcsName,
5974 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5977 if (hFile==INVALID_HANDLE_VALUE)
5979 DWORD last_error = GetLastError();
5985 case ERROR_FILE_NOT_FOUND:
5986 hr = STG_E_FILENOTFOUND;
5989 case ERROR_PATH_NOT_FOUND:
5990 hr = STG_E_PATHNOTFOUND;
5993 case ERROR_ACCESS_DENIED:
5994 case ERROR_WRITE_PROTECT:
5995 hr = STG_E_ACCESSDENIED;
5998 case ERROR_SHARING_VIOLATION:
5999 hr = STG_E_SHAREVIOLATION;
6010 * Refuse to open the file if it's too small to be a structured storage file
6011 * FIXME: verify the file when reading instead of here
6013 length = GetFileSize(hFile, NULL);
6017 hr = STG_E_FILEALREADYEXISTS;
6022 * Allocate and initialize the new IStorage32object.
6024 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6026 if (newStorage == 0)
6028 hr = STG_E_INSUFFICIENTMEMORY;
6032 /* if the file's length was zero, initialize the storage */
6033 hr = StorageImpl_Construct(
6044 HeapFree(GetProcessHeap(), 0, newStorage);
6046 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6048 if(hr == STG_E_INVALIDHEADER)
6049 hr = STG_E_FILEALREADYEXISTS;
6053 /* prepare the file name string given in lieu of the root property name */
6054 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6055 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6056 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6059 * Get an "out" pointer for the caller.
6061 hr = StorageBaseImpl_QueryInterface(
6062 (IStorage*)newStorage,
6063 (REFIID)&IID_IStorage,
6067 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6071 /******************************************************************************
6072 * StgCreateDocfileOnILockBytes [OLE32.@]
6074 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6078 IStorage** ppstgOpen)
6080 StorageImpl* newStorage = 0;
6084 * Validate the parameters
6086 if ((ppstgOpen == 0) || (plkbyt == 0))
6087 return STG_E_INVALIDPOINTER;
6090 * Allocate and initialize the new IStorage object.
6092 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6094 if (newStorage == 0)
6095 return STG_E_INSUFFICIENTMEMORY;
6097 hr = StorageImpl_Construct(
6108 HeapFree(GetProcessHeap(), 0, newStorage);
6113 * Get an "out" pointer for the caller.
6115 hr = StorageBaseImpl_QueryInterface(
6116 (IStorage*)newStorage,
6117 (REFIID)&IID_IStorage,
6123 /******************************************************************************
6124 * StgOpenStorageOnILockBytes [OLE32.@]
6126 HRESULT WINAPI StgOpenStorageOnILockBytes(
6128 IStorage *pstgPriority,
6132 IStorage **ppstgOpen)
6134 StorageImpl* newStorage = 0;
6138 * Perform a sanity check
6140 if ((plkbyt == 0) || (ppstgOpen == 0))
6141 return STG_E_INVALIDPOINTER;
6144 * Validate the STGM flags
6146 if ( FAILED( validateSTGM(grfMode) ))
6147 return STG_E_INVALIDFLAG;
6150 * Initialize the "out" parameter.
6155 * Allocate and initialize the new IStorage object.
6157 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6159 if (newStorage == 0)
6160 return STG_E_INSUFFICIENTMEMORY;
6162 hr = StorageImpl_Construct(
6173 HeapFree(GetProcessHeap(), 0, newStorage);
6178 * Get an "out" pointer for the caller.
6180 hr = StorageBaseImpl_QueryInterface(
6181 (IStorage*)newStorage,
6182 (REFIID)&IID_IStorage,
6188 /******************************************************************************
6189 * StgSetTimes [ole32.@]
6190 * StgSetTimes [OLE32.@]
6194 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6195 FILETIME const *patime, FILETIME const *pmtime)
6197 IStorage *stg = NULL;
6200 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6202 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6206 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6207 IStorage_Release(stg);
6213 /******************************************************************************
6214 * StgIsStorageILockBytes [OLE32.@]
6216 * Determines if the ILockBytes contains a storage object.
6218 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6221 ULARGE_INTEGER offset;
6223 offset.u.HighPart = 0;
6224 offset.u.LowPart = 0;
6226 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6228 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6234 /******************************************************************************
6235 * WriteClassStg [OLE32.@]
6237 * This method will store the specified CLSID in the specified storage object
6239 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6244 return E_INVALIDARG;
6246 hRes = IStorage_SetClass(pStg, rclsid);
6251 /***********************************************************************
6252 * ReadClassStg (OLE32.@)
6254 * This method reads the CLSID previously written to a storage object with
6255 * the WriteClassStg.
6258 * pstg [I] IStorage pointer
6259 * pclsid [O] Pointer to where the CLSID is written
6263 * Failure: HRESULT code.
6265 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6270 TRACE("(%p, %p)\n", pstg, pclsid);
6272 if(!pstg || !pclsid)
6273 return E_INVALIDARG;
6276 * read a STATSTG structure (contains the clsid) from the storage
6278 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6281 *pclsid=pstatstg.clsid;
6286 /***********************************************************************
6287 * OleLoadFromStream (OLE32.@)
6289 * This function loads an object from stream
6291 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6295 LPPERSISTSTREAM xstm;
6297 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6299 res=ReadClassStm(pStm,&clsid);
6300 if (!SUCCEEDED(res))
6302 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6303 if (!SUCCEEDED(res))
6305 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6306 if (!SUCCEEDED(res)) {
6307 IUnknown_Release((IUnknown*)*ppvObj);
6310 res=IPersistStream_Load(xstm,pStm);
6311 IPersistStream_Release(xstm);
6312 /* FIXME: all refcounts ok at this point? I think they should be:
6315 * xstm : 0 (released)
6320 /***********************************************************************
6321 * OleSaveToStream (OLE32.@)
6323 * This function saves an object with the IPersistStream interface on it
6324 * to the specified stream.
6326 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6332 TRACE("(%p,%p)\n",pPStm,pStm);
6334 res=IPersistStream_GetClassID(pPStm,&clsid);
6336 if (SUCCEEDED(res)){
6338 res=WriteClassStm(pStm,&clsid);
6342 res=IPersistStream_Save(pPStm,pStm,TRUE);
6345 TRACE("Finished Save\n");
6349 /****************************************************************************
6350 * This method validate a STGM parameter that can contain the values below
6352 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6353 * The stgm values contained in 0xffff0000 are bitmasks.
6355 * STGM_DIRECT 0x00000000
6356 * STGM_TRANSACTED 0x00010000
6357 * STGM_SIMPLE 0x08000000
6359 * STGM_READ 0x00000000
6360 * STGM_WRITE 0x00000001
6361 * STGM_READWRITE 0x00000002
6363 * STGM_SHARE_DENY_NONE 0x00000040
6364 * STGM_SHARE_DENY_READ 0x00000030
6365 * STGM_SHARE_DENY_WRITE 0x00000020
6366 * STGM_SHARE_EXCLUSIVE 0x00000010
6368 * STGM_PRIORITY 0x00040000
6369 * STGM_DELETEONRELEASE 0x04000000
6371 * STGM_CREATE 0x00001000
6372 * STGM_CONVERT 0x00020000
6373 * STGM_FAILIFTHERE 0x00000000
6375 * STGM_NOSCRATCH 0x00100000
6376 * STGM_NOSNAPSHOT 0x00200000
6378 static HRESULT validateSTGM(DWORD stgm)
6380 DWORD access = STGM_ACCESS_MODE(stgm);
6381 DWORD share = STGM_SHARE_MODE(stgm);
6382 DWORD create = STGM_CREATE_MODE(stgm);
6384 if (stgm&~STGM_KNOWN_FLAGS)
6386 ERR("unknown flags %08lx\n", stgm);
6394 case STGM_READWRITE:
6402 case STGM_SHARE_DENY_NONE:
6403 case STGM_SHARE_DENY_READ:
6404 case STGM_SHARE_DENY_WRITE:
6405 case STGM_SHARE_EXCLUSIVE:
6414 case STGM_FAILIFTHERE:
6421 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6423 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6427 * STGM_CREATE | STGM_CONVERT
6428 * if both are false, STGM_FAILIFTHERE is set to TRUE
6430 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6434 * STGM_NOSCRATCH requires STGM_TRANSACTED
6436 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6440 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6441 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6443 if ( (stgm & STGM_NOSNAPSHOT) &&
6444 (!(stgm & STGM_TRANSACTED) ||
6445 share == STGM_SHARE_EXCLUSIVE ||
6446 share == STGM_SHARE_DENY_WRITE) )
6452 /****************************************************************************
6453 * GetShareModeFromSTGM
6455 * This method will return a share mode flag from a STGM value.
6456 * The STGM value is assumed valid.
6458 static DWORD GetShareModeFromSTGM(DWORD stgm)
6460 switch (STGM_SHARE_MODE(stgm))
6462 case STGM_SHARE_DENY_NONE:
6463 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6464 case STGM_SHARE_DENY_READ:
6465 return FILE_SHARE_WRITE;
6466 case STGM_SHARE_DENY_WRITE:
6467 return FILE_SHARE_READ;
6468 case STGM_SHARE_EXCLUSIVE:
6471 ERR("Invalid share mode!\n");
6476 /****************************************************************************
6477 * GetAccessModeFromSTGM
6479 * This method will return an access mode flag from a STGM value.
6480 * The STGM value is assumed valid.
6482 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6484 switch (STGM_ACCESS_MODE(stgm))
6487 return GENERIC_READ;
6489 case STGM_READWRITE:
6490 return GENERIC_READ | GENERIC_WRITE;
6492 ERR("Invalid access mode!\n");
6497 /****************************************************************************
6498 * GetCreationModeFromSTGM
6500 * This method will return a creation mode flag from a STGM value.
6501 * The STGM value is assumed valid.
6503 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6505 switch(STGM_CREATE_MODE(stgm))
6508 return CREATE_ALWAYS;
6510 FIXME("STGM_CONVERT not implemented!\n");
6512 case STGM_FAILIFTHERE:
6515 ERR("Invalid create mode!\n");
6521 /*************************************************************************
6522 * OLECONVERT_LoadOLE10 [Internal]
6524 * Loads the OLE10 STREAM to memory
6527 * pOleStream [I] The OLESTREAM
6528 * pData [I] Data Structure for the OLESTREAM Data
6532 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6533 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6536 * This function is used by OleConvertOLESTREAMToIStorage only.
6538 * Memory allocated for pData must be freed by the caller
6540 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6543 HRESULT hRes = S_OK;
6547 pData->pData = NULL;
6548 pData->pstrOleObjFileName = (CHAR *) NULL;
6550 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6553 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6554 if(dwSize != sizeof(pData->dwOleID))
6556 hRes = CONVERT10_E_OLESTREAM_GET;
6558 else if(pData->dwOleID != OLESTREAM_ID)
6560 hRes = CONVERT10_E_OLESTREAM_FMT;
6571 /* Get the TypeID...more info needed for this field */
6572 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6573 if(dwSize != sizeof(pData->dwTypeID))
6575 hRes = CONVERT10_E_OLESTREAM_GET;
6580 if(pData->dwTypeID != 0)
6582 /* Get the length of the OleTypeName */
6583 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6584 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6586 hRes = CONVERT10_E_OLESTREAM_GET;
6591 if(pData->dwOleTypeNameLength > 0)
6593 /* Get the OleTypeName */
6594 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6595 if(dwSize != pData->dwOleTypeNameLength)
6597 hRes = CONVERT10_E_OLESTREAM_GET;
6603 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6604 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6606 hRes = CONVERT10_E_OLESTREAM_GET;
6610 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6611 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6612 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6613 if(pData->pstrOleObjFileName)
6615 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6616 if(dwSize != pData->dwOleObjFileNameLength)
6618 hRes = CONVERT10_E_OLESTREAM_GET;
6622 hRes = CONVERT10_E_OLESTREAM_GET;
6627 /* Get the Width of the Metafile */
6628 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6629 if(dwSize != sizeof(pData->dwMetaFileWidth))
6631 hRes = CONVERT10_E_OLESTREAM_GET;
6635 /* Get the Height of the Metafile */
6636 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6637 if(dwSize != sizeof(pData->dwMetaFileHeight))
6639 hRes = CONVERT10_E_OLESTREAM_GET;
6645 /* Get the Length of the Data */
6646 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6647 if(dwSize != sizeof(pData->dwDataLength))
6649 hRes = CONVERT10_E_OLESTREAM_GET;
6653 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6655 if(!bStrem1) /* if it is a second OLE stream data */
6657 pData->dwDataLength -= 8;
6658 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6659 if(dwSize != sizeof(pData->strUnknown))
6661 hRes = CONVERT10_E_OLESTREAM_GET;
6667 if(pData->dwDataLength > 0)
6669 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6671 /* Get Data (ex. IStorage, Metafile, or BMP) */
6674 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6675 if(dwSize != pData->dwDataLength)
6677 hRes = CONVERT10_E_OLESTREAM_GET;
6682 hRes = CONVERT10_E_OLESTREAM_GET;
6691 /*************************************************************************
6692 * OLECONVERT_SaveOLE10 [Internal]
6694 * Saves the OLE10 STREAM From memory
6697 * pData [I] Data Structure for the OLESTREAM Data
6698 * pOleStream [I] The OLESTREAM to save
6702 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6705 * This function is used by OleConvertIStorageToOLESTREAM only.
6708 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6711 HRESULT hRes = S_OK;
6715 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6716 if(dwSize != sizeof(pData->dwOleID))
6718 hRes = CONVERT10_E_OLESTREAM_PUT;
6723 /* Set the TypeID */
6724 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6725 if(dwSize != sizeof(pData->dwTypeID))
6727 hRes = CONVERT10_E_OLESTREAM_PUT;
6731 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6733 /* Set the Length of the OleTypeName */
6734 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6735 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6737 hRes = CONVERT10_E_OLESTREAM_PUT;
6742 if(pData->dwOleTypeNameLength > 0)
6744 /* Set the OleTypeName */
6745 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6746 if(dwSize != pData->dwOleTypeNameLength)
6748 hRes = CONVERT10_E_OLESTREAM_PUT;
6755 /* Set the width of the Metafile */
6756 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6757 if(dwSize != sizeof(pData->dwMetaFileWidth))
6759 hRes = CONVERT10_E_OLESTREAM_PUT;
6765 /* Set the height of the Metafile */
6766 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6767 if(dwSize != sizeof(pData->dwMetaFileHeight))
6769 hRes = CONVERT10_E_OLESTREAM_PUT;
6775 /* Set the length of the Data */
6776 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6777 if(dwSize != sizeof(pData->dwDataLength))
6779 hRes = CONVERT10_E_OLESTREAM_PUT;
6785 if(pData->dwDataLength > 0)
6787 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6788 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6789 if(dwSize != pData->dwDataLength)
6791 hRes = CONVERT10_E_OLESTREAM_PUT;
6799 /*************************************************************************
6800 * OLECONVERT_GetOLE20FromOLE10[Internal]
6802 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6803 * opens it, and copies the content to the dest IStorage for
6804 * OleConvertOLESTREAMToIStorage
6808 * pDestStorage [I] The IStorage to copy the data to
6809 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6810 * nBufferLength [I] The size of the buffer
6819 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6823 IStorage *pTempStorage;
6824 DWORD dwNumOfBytesWritten;
6825 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6826 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6828 /* Create a temp File */
6829 GetTempPathW(MAX_PATH, wstrTempDir);
6830 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6831 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6833 if(hFile != INVALID_HANDLE_VALUE)
6835 /* Write IStorage Data to File */
6836 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6839 /* Open and copy temp storage to the Dest Storage */
6840 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6843 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6844 StorageBaseImpl_Release(pTempStorage);
6846 DeleteFileW(wstrTempFile);
6851 /*************************************************************************
6852 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6854 * Saves the OLE10 STREAM From memory
6857 * pStorage [I] The Src IStorage to copy
6858 * pData [I] The Dest Memory to write to.
6861 * The size in bytes allocated for pData
6864 * Memory allocated for pData must be freed by the caller
6866 * Used by OleConvertIStorageToOLESTREAM only.
6869 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6873 DWORD nDataLength = 0;
6874 IStorage *pTempStorage;
6875 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6876 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6880 /* Create temp Storage */
6881 GetTempPathW(MAX_PATH, wstrTempDir);
6882 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6883 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6887 /* Copy Src Storage to the Temp Storage */
6888 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6889 StorageBaseImpl_Release(pTempStorage);
6891 /* Open Temp Storage as a file and copy to memory */
6892 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6893 if(hFile != INVALID_HANDLE_VALUE)
6895 nDataLength = GetFileSize(hFile, NULL);
6896 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6897 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6900 DeleteFileW(wstrTempFile);
6905 /*************************************************************************
6906 * OLECONVERT_CreateOleStream [Internal]
6908 * Creates the "\001OLE" stream in the IStorage if necessary.
6911 * pStorage [I] Dest storage to create the stream in
6917 * This function is used by OleConvertOLESTREAMToIStorage only.
6919 * This stream is still unknown, MS Word seems to have extra data
6920 * but since the data is stored in the OLESTREAM there should be
6921 * no need to recreate the stream. If the stream is manually
6922 * deleted it will create it with this default data.
6925 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6929 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6930 BYTE pOleStreamHeader [] =
6932 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6933 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6934 0x00, 0x00, 0x00, 0x00
6937 /* Create stream if not present */
6938 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6939 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6943 /* Write default Data */
6944 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6945 IStream_Release(pStream);
6949 /* write a string to a stream, preceded by its length */
6950 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6957 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6958 r = IStream_Write( stm, &len, sizeof(len), NULL);
6963 str = CoTaskMemAlloc( len );
6964 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6965 r = IStream_Write( stm, str, len, NULL);
6966 CoTaskMemFree( str );
6970 /* read a string preceded by its length from a stream */
6971 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6974 DWORD len, count = 0;
6978 r = IStream_Read( stm, &len, sizeof(len), &count );
6981 if( count != sizeof(len) )
6982 return E_OUTOFMEMORY;
6984 TRACE("%ld bytes\n",len);
6986 str = CoTaskMemAlloc( len );
6988 return E_OUTOFMEMORY;
6990 r = IStream_Read( stm, str, len, &count );
6995 CoTaskMemFree( str );
6996 return E_OUTOFMEMORY;
6999 TRACE("Read string %s\n",debugstr_an(str,len));
7001 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7002 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7004 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7005 CoTaskMemFree( str );
7013 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7014 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7018 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7020 static const BYTE unknown1[12] =
7021 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7022 0xFF, 0xFF, 0xFF, 0xFF};
7023 static const BYTE unknown2[16] =
7024 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7025 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7027 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7028 debugstr_w(lpszUserType), debugstr_w(szClipName),
7029 debugstr_w(szProgIDName));
7031 /* Create a CompObj stream if it doesn't exist */
7032 r = IStorage_CreateStream(pstg, szwStreamName,
7033 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7037 /* Write CompObj Structure to stream */
7038 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7040 if( SUCCEEDED( r ) )
7041 r = WriteClassStm( pstm, clsid );
7043 if( SUCCEEDED( r ) )
7044 r = STREAM_WriteString( pstm, lpszUserType );
7045 if( SUCCEEDED( r ) )
7046 r = STREAM_WriteString( pstm, szClipName );
7047 if( SUCCEEDED( r ) )
7048 r = STREAM_WriteString( pstm, szProgIDName );
7049 if( SUCCEEDED( r ) )
7050 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7052 IStream_Release( pstm );
7057 /***********************************************************************
7058 * WriteFmtUserTypeStg (OLE32.@)
7060 HRESULT WINAPI WriteFmtUserTypeStg(
7061 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7064 WCHAR szwClipName[0x40];
7065 CLSID clsid = CLSID_NULL;
7066 LPWSTR wstrProgID = NULL;
7069 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7071 /* get the clipboard format name */
7072 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
7075 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7077 /* FIXME: There's room to save a CLSID and its ProgID, but
7078 the CLSID is not looked up in the registry and in all the
7079 tests I wrote it was CLSID_NULL. Where does it come from?
7082 /* get the real program ID. This may fail, but that's fine */
7083 ProgIDFromCLSID(&clsid, &wstrProgID);
7085 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7087 r = STORAGE_WriteCompObj( pstg, &clsid,
7088 lpszUserType, szwClipName, wstrProgID );
7090 CoTaskMemFree(wstrProgID);
7096 /******************************************************************************
7097 * ReadFmtUserTypeStg [OLE32.@]
7099 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7103 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7104 unsigned char unknown1[12];
7105 unsigned char unknown2[16];
7107 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7110 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7112 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7113 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7116 WARN("Failed to open stream r = %08lx\n", r);
7120 /* read the various parts of the structure */
7121 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7122 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7124 r = ReadClassStm( stm, &clsid );
7128 r = STREAM_ReadString( stm, &szCLSIDName );
7132 r = STREAM_ReadString( stm, &szOleTypeName );
7136 r = STREAM_ReadString( stm, &szProgIDName );
7140 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7141 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7144 /* ok, success... now we just need to store what we found */
7146 *pcf = RegisterClipboardFormatW( szOleTypeName );
7147 CoTaskMemFree( szOleTypeName );
7149 if( lplpszUserType )
7150 *lplpszUserType = szCLSIDName;
7151 CoTaskMemFree( szProgIDName );
7154 IStream_Release( stm );
7160 /*************************************************************************
7161 * OLECONVERT_CreateCompObjStream [Internal]
7163 * Creates a "\001CompObj" is the destination IStorage if necessary.
7166 * pStorage [I] The dest IStorage to create the CompObj Stream
7168 * strOleTypeName [I] The ProgID
7172 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7175 * This function is used by OleConvertOLESTREAMToIStorage only.
7177 * The stream data is stored in the OLESTREAM and there should be
7178 * no need to recreate the stream. If the stream is manually
7179 * deleted it will attempt to create it by querying the registry.
7183 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7186 HRESULT hStorageRes, hRes = S_OK;
7187 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7188 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7189 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7191 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7192 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7194 /* Initialize the CompObj structure */
7195 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7196 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7197 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7200 /* Create a CompObj stream if it doesn't exist */
7201 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7202 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7203 if(hStorageRes == S_OK)
7205 /* copy the OleTypeName to the compobj struct */
7206 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7207 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7209 /* copy the OleTypeName to the compobj struct */
7210 /* Note: in the test made, these were Identical */
7211 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7212 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7215 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7216 bufferW, OLESTREAM_MAX_STR_LEN );
7217 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7223 /* Get the CLSID Default Name from the Registry */
7224 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7225 if(hErr == ERROR_SUCCESS)
7227 char strTemp[OLESTREAM_MAX_STR_LEN];
7228 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7229 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7230 if(hErr == ERROR_SUCCESS)
7232 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7238 /* Write CompObj Structure to stream */
7239 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7241 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7243 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7244 if(IStorageCompObj.dwCLSIDNameLength > 0)
7246 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7248 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7249 if(IStorageCompObj.dwOleTypeNameLength > 0)
7251 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7253 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7254 if(IStorageCompObj.dwProgIDNameLength > 0)
7256 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7258 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7259 IStream_Release(pStream);
7265 /*************************************************************************
7266 * OLECONVERT_CreateOlePresStream[Internal]
7268 * Creates the "\002OlePres000" Stream with the Metafile data
7271 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7272 * dwExtentX [I] Width of the Metafile
7273 * dwExtentY [I] Height of the Metafile
7274 * pData [I] Metafile data
7275 * dwDataLength [I] Size of the Metafile data
7279 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7282 * This function is used by OleConvertOLESTREAMToIStorage only.
7285 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7289 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7290 BYTE pOlePresStreamHeader [] =
7292 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7293 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7294 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7295 0x00, 0x00, 0x00, 0x00
7298 BYTE pOlePresStreamHeaderEmpty [] =
7300 0x00, 0x00, 0x00, 0x00,
7301 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7302 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7303 0x00, 0x00, 0x00, 0x00
7306 /* Create the OlePres000 Stream */
7307 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7308 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7313 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7315 memset(&OlePres, 0, sizeof(OlePres));
7316 /* Do we have any metafile data to save */
7317 if(dwDataLength > 0)
7319 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7320 nHeaderSize = sizeof(pOlePresStreamHeader);
7324 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7325 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7327 /* Set width and height of the metafile */
7328 OlePres.dwExtentX = dwExtentX;
7329 OlePres.dwExtentY = -dwExtentY;
7331 /* Set Data and Length */
7332 if(dwDataLength > sizeof(METAFILEPICT16))
7334 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7335 OlePres.pData = &(pData[8]);
7337 /* Save OlePres000 Data to Stream */
7338 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7339 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7340 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7341 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7342 if(OlePres.dwSize > 0)
7344 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7346 IStream_Release(pStream);
7350 /*************************************************************************
7351 * OLECONVERT_CreateOle10NativeStream [Internal]
7353 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7356 * pStorage [I] Dest storage to create the stream in
7357 * pData [I] Ole10 Native Data (ex. bmp)
7358 * dwDataLength [I] Size of the Ole10 Native Data
7364 * This function is used by OleConvertOLESTREAMToIStorage only.
7366 * Might need to verify the data and return appropriate error message
7369 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7373 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7375 /* Create the Ole10Native Stream */
7376 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7377 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7381 /* Write info to stream */
7382 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7383 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7384 IStream_Release(pStream);
7389 /*************************************************************************
7390 * OLECONVERT_GetOLE10ProgID [Internal]
7392 * Finds the ProgID (or OleTypeID) from the IStorage
7395 * pStorage [I] The Src IStorage to get the ProgID
7396 * strProgID [I] the ProgID string to get
7397 * dwSize [I] the size of the string
7401 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7404 * This function is used by OleConvertIStorageToOLESTREAM only.
7408 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7412 LARGE_INTEGER iSeekPos;
7413 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7414 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7416 /* Open the CompObj Stream */
7417 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7418 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7422 /*Get the OleType from the CompObj Stream */
7423 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7424 iSeekPos.u.HighPart = 0;
7426 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7427 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7428 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7429 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7430 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7431 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7432 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7434 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7437 IStream_Read(pStream, strProgID, *dwSize, NULL);
7439 IStream_Release(pStream);
7444 LPOLESTR wstrProgID;
7446 /* Get the OleType from the registry */
7447 REFCLSID clsid = &(stat.clsid);
7448 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7449 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7452 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7459 /*************************************************************************
7460 * OLECONVERT_GetOle10PresData [Internal]
7462 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7465 * pStorage [I] Src IStroage
7466 * pOleStream [I] Dest OleStream Mem Struct
7472 * This function is used by OleConvertIStorageToOLESTREAM only.
7474 * Memory allocated for pData must be freed by the caller
7478 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7483 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7485 /* Initialize Default data for OLESTREAM */
7486 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7487 pOleStreamData[0].dwTypeID = 2;
7488 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7489 pOleStreamData[1].dwTypeID = 0;
7490 pOleStreamData[0].dwMetaFileWidth = 0;
7491 pOleStreamData[0].dwMetaFileHeight = 0;
7492 pOleStreamData[0].pData = NULL;
7493 pOleStreamData[1].pData = NULL;
7495 /* Open Ole10Native Stream */
7496 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7497 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7501 /* Read Size and Data */
7502 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7503 if(pOleStreamData->dwDataLength > 0)
7505 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7506 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7508 IStream_Release(pStream);
7514 /*************************************************************************
7515 * OLECONVERT_GetOle20PresData[Internal]
7517 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7520 * pStorage [I] Src IStroage
7521 * pOleStreamData [I] Dest OleStream Mem Struct
7527 * This function is used by OleConvertIStorageToOLESTREAM only.
7529 * Memory allocated for pData must be freed by the caller
7531 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7535 OLECONVERT_ISTORAGE_OLEPRES olePress;
7536 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7538 /* Initialize Default data for OLESTREAM */
7539 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7540 pOleStreamData[0].dwTypeID = 2;
7541 pOleStreamData[0].dwMetaFileWidth = 0;
7542 pOleStreamData[0].dwMetaFileHeight = 0;
7543 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7544 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7545 pOleStreamData[1].dwTypeID = 0;
7546 pOleStreamData[1].dwOleTypeNameLength = 0;
7547 pOleStreamData[1].strOleTypeName[0] = 0;
7548 pOleStreamData[1].dwMetaFileWidth = 0;
7549 pOleStreamData[1].dwMetaFileHeight = 0;
7550 pOleStreamData[1].pData = NULL;
7551 pOleStreamData[1].dwDataLength = 0;
7554 /* Open OlePress000 stream */
7555 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7556 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7559 LARGE_INTEGER iSeekPos;
7560 METAFILEPICT16 MetaFilePict;
7561 static const char strMetafilePictName[] = "METAFILEPICT";
7563 /* Set the TypeID for a Metafile */
7564 pOleStreamData[1].dwTypeID = 5;
7566 /* Set the OleTypeName to Metafile */
7567 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7568 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7570 iSeekPos.u.HighPart = 0;
7571 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7573 /* Get Presentation Data */
7574 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7575 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7576 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7577 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7579 /*Set width and Height */
7580 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7581 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7582 if(olePress.dwSize > 0)
7585 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7587 /* Set MetaFilePict struct */
7588 MetaFilePict.mm = 8;
7589 MetaFilePict.xExt = olePress.dwExtentX;
7590 MetaFilePict.yExt = olePress.dwExtentY;
7591 MetaFilePict.hMF = 0;
7593 /* Get Metafile Data */
7594 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7595 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7596 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7598 IStream_Release(pStream);
7602 /*************************************************************************
7603 * OleConvertOLESTREAMToIStorage [OLE32.@]
7608 * DVTARGETDEVICE paramenter is not handled
7609 * Still unsure of some mem fields for OLE 10 Stream
7610 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7611 * and "\001OLE" streams
7614 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7615 LPOLESTREAM pOleStream,
7617 const DVTARGETDEVICE* ptd)
7621 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7623 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7627 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7630 if(pstg == NULL || pOleStream == NULL)
7632 hRes = E_INVALIDARG;
7637 /* Load the OLESTREAM to Memory */
7638 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7643 /* Load the OLESTREAM to Memory (part 2)*/
7644 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7650 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7652 /* Do we have the IStorage Data in the OLESTREAM */
7653 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7655 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7656 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7660 /* It must be an original OLE 1.0 source */
7661 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7666 /* It must be an original OLE 1.0 source */
7667 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7670 /* Create CompObj Stream if necessary */
7671 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7674 /*Create the Ole Stream if necessary */
7675 OLECONVERT_CreateOleStream(pstg);
7680 /* Free allocated memory */
7681 for(i=0; i < 2; i++)
7683 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7684 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7685 pOleStreamData[i].pstrOleObjFileName = NULL;
7690 /*************************************************************************
7691 * OleConvertIStorageToOLESTREAM [OLE32.@]
7698 * Still unsure of some mem fields for OLE 10 Stream
7699 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7700 * and "\001OLE" streams.
7703 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7705 LPOLESTREAM pOleStream)
7708 HRESULT hRes = S_OK;
7710 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7711 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7714 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7716 if(pstg == NULL || pOleStream == NULL)
7718 hRes = E_INVALIDARG;
7722 /* Get the ProgID */
7723 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7724 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7728 /* Was it originally Ole10 */
7729 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7732 IStream_Release(pStream);
7733 /* Get Presentation Data for Ole10Native */
7734 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7738 /* Get Presentation Data (OLE20) */
7739 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7742 /* Save OLESTREAM */
7743 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7746 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7751 /* Free allocated memory */
7752 for(i=0; i < 2; i++)
7754 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7760 /***********************************************************************
7761 * GetConvertStg (OLE32.@)
7763 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7764 FIXME("unimplemented stub!\n");
7768 /******************************************************************************
7769 * StgIsStorageFile [OLE32.@]
7770 * Verify if the file contains a storage object
7776 * S_OK if file has magic bytes as a storage object
7777 * S_FALSE if file is not storage
7780 StgIsStorageFile(LPCOLESTR fn)
7786 TRACE("(\'%s\')\n", debugstr_w(fn));
7787 hf = CreateFileW(fn, GENERIC_READ,
7788 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7789 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7791 if (hf == INVALID_HANDLE_VALUE)
7792 return STG_E_FILENOTFOUND;
7794 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7796 WARN(" unable to read file\n");
7803 if (bytes_read != 8) {
7804 WARN(" too short\n");
7808 if (!memcmp(magic,STORAGE_magic,8)) {
7813 WARN(" -> Invalid header.\n");