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 (IsEqualGUID(&IID_IUnknown, riid) ||
203 IsEqualGUID(&IID_IStorage, riid))
205 *ppvObject = (IStorage*)This;
207 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
209 *ppvObject = (IStorage*)&This->pssVtbl;
213 * Check that we obtained an interface.
216 return E_NOINTERFACE;
219 * Query Interface always increases the reference count by one when it is
222 IStorage_AddRef(iface);
227 /************************************************************************
228 * Storage32BaseImpl_AddRef (IUnknown)
230 * This method implements the common AddRef for all IStorage32
231 * implementations contained in this file.
233 * See Windows documentation for more details on IUnknown methods.
235 static ULONG WINAPI StorageBaseImpl_AddRef(
238 StorageBaseImpl *This = (StorageBaseImpl *)iface;
239 ULONG ref = InterlockedIncrement(&This->ref);
241 TRACE("(%p) AddRef to %ld\n", This, ref);
246 /************************************************************************
247 * Storage32BaseImpl_Release (IUnknown)
249 * This method implements the common Release for all IStorage32
250 * implementations contained in this file.
252 * See Windows documentation for more details on IUnknown methods.
254 static ULONG WINAPI StorageBaseImpl_Release(
257 StorageBaseImpl *This = (StorageBaseImpl *)iface;
259 * Decrease the reference count on this object.
261 ULONG ref = InterlockedDecrement(&This->ref);
263 TRACE("(%p) ReleaseRef to %ld\n", This, ref);
266 * If the reference count goes down to 0, perform suicide.
271 * Since we are using a system of base-classes, we want to call the
272 * destructor of the appropriate derived class. To do this, we are
273 * using virtual functions to implement the destructor.
275 This->v_destructor(This);
281 /************************************************************************
282 * Storage32BaseImpl_OpenStream (IStorage)
284 * This method will open the specified stream object from the current storage.
286 * See Windows documentation for more details on IStorage methods.
288 static HRESULT WINAPI StorageBaseImpl_OpenStream(
290 const OLECHAR* pwcsName, /* [string][in] */
291 void* reserved1, /* [unique][in] */
292 DWORD grfMode, /* [in] */
293 DWORD reserved2, /* [in] */
294 IStream** ppstm) /* [out] */
296 StorageBaseImpl *This = (StorageBaseImpl *)iface;
297 IEnumSTATSTGImpl* propertyEnumeration;
298 StgStreamImpl* newStream;
299 StgProperty currentProperty;
300 ULONG foundPropertyIndex;
301 HRESULT res = STG_E_UNKNOWN;
303 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
304 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
307 * Perform a sanity check on the parameters.
309 if ( (pwcsName==NULL) || (ppstm==0) )
316 * Initialize the out parameter
321 * Validate the STGM flags
323 if ( FAILED( validateSTGM(grfMode) ) ||
324 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
326 res = STG_E_INVALIDFLAG;
333 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
335 res = STG_E_INVALIDFUNCTION;
340 * Check that we're compatible with the parent's storage mode, but
341 * only if we are not in transacted mode
343 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
344 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
346 res = STG_E_ACCESSDENIED;
352 * Create a property enumeration to search the properties
354 propertyEnumeration = IEnumSTATSTGImpl_Construct(
355 This->ancestorStorage,
356 This->rootPropertySetIndex);
359 * Search the enumeration for the property with the given name
361 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
367 * Delete the property enumeration since we don't need it anymore
369 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
372 * If it was found, construct the stream object and return a pointer to it.
374 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
375 (currentProperty.propertyType==PROPTYPE_STREAM) )
377 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
381 newStream->grfMode = grfMode;
382 *ppstm = (IStream*)newStream;
385 * Since we are returning a pointer to the interface, we have to
386 * nail down the reference.
388 IStream_AddRef(*ppstm);
391 * add us to the storage's list of active streams
394 StorageBaseImpl_AddStream(This,newStream);
404 res = STG_E_FILENOTFOUND;
408 TRACE("<-- IStream %p\n", *ppstm);
409 TRACE("<-- %08lx\n", res);
413 /************************************************************************
414 * Storage32BaseImpl_OpenStorage (IStorage)
416 * This method will open a new storage object from the current storage.
418 * See Windows documentation for more details on IStorage methods.
420 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
422 const OLECHAR* pwcsName, /* [string][unique][in] */
423 IStorage* pstgPriority, /* [unique][in] */
424 DWORD grfMode, /* [in] */
425 SNB snbExclude, /* [unique][in] */
426 DWORD reserved, /* [in] */
427 IStorage** ppstg) /* [out] */
429 StorageBaseImpl *This = (StorageBaseImpl *)iface;
430 StorageInternalImpl* newStorage;
431 IEnumSTATSTGImpl* propertyEnumeration;
432 StgProperty currentProperty;
433 ULONG foundPropertyIndex;
434 HRESULT res = STG_E_UNKNOWN;
436 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
437 iface, debugstr_w(pwcsName), pstgPriority,
438 grfMode, snbExclude, reserved, ppstg);
441 * Perform a sanity check on the parameters.
443 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
450 if (snbExclude != NULL)
452 res = STG_E_INVALIDPARAMETER;
457 * Validate the STGM flags
459 if ( FAILED( validateSTGM(grfMode) ))
461 res = STG_E_INVALIDFLAG;
468 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
469 (grfMode & STGM_DELETEONRELEASE) ||
470 (grfMode & STGM_PRIORITY) )
472 res = STG_E_INVALIDFUNCTION;
477 * Check that we're compatible with the parent's storage mode,
478 * but only if we are not transacted
480 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
481 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
483 res = STG_E_ACCESSDENIED;
489 * Initialize the out parameter
494 * Create a property enumeration to search the properties
496 propertyEnumeration = IEnumSTATSTGImpl_Construct(
497 This->ancestorStorage,
498 This->rootPropertySetIndex);
501 * Search the enumeration for the property with the given name
503 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
509 * Delete the property enumeration since we don't need it anymore
511 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
514 * If it was found, construct the stream object and return a pointer to it.
516 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
517 (currentProperty.propertyType==PROPTYPE_STORAGE) )
520 * Construct a new Storage object
522 newStorage = StorageInternalImpl_Construct(
523 This->ancestorStorage,
529 *ppstg = (IStorage*)newStorage;
532 * Since we are returning a pointer to the interface,
533 * we have to nail down the reference.
535 StorageBaseImpl_AddRef(*ppstg);
541 res = STG_E_INSUFFICIENTMEMORY;
545 res = STG_E_FILENOTFOUND;
548 TRACE("<-- %08lx\n", res);
552 /************************************************************************
553 * Storage32BaseImpl_EnumElements (IStorage)
555 * This method will create an enumerator object that can be used to
556 * retrieve informatino about all the properties in the storage object.
558 * See Windows documentation for more details on IStorage methods.
560 static HRESULT WINAPI StorageBaseImpl_EnumElements(
562 DWORD reserved1, /* [in] */
563 void* reserved2, /* [size_is][unique][in] */
564 DWORD reserved3, /* [in] */
565 IEnumSTATSTG** ppenum) /* [out] */
567 StorageBaseImpl *This = (StorageBaseImpl *)iface;
568 IEnumSTATSTGImpl* newEnum;
570 TRACE("(%p, %ld, %p, %ld, %p)\n",
571 iface, reserved1, reserved2, reserved3, ppenum);
574 * Perform a sanity check on the parameters.
576 if ( (This==0) || (ppenum==0))
580 * Construct the enumerator.
582 newEnum = IEnumSTATSTGImpl_Construct(
583 This->ancestorStorage,
584 This->rootPropertySetIndex);
588 *ppenum = (IEnumSTATSTG*)newEnum;
591 * Don't forget to nail down a reference to the new object before
594 IEnumSTATSTG_AddRef(*ppenum);
599 return E_OUTOFMEMORY;
602 /************************************************************************
603 * Storage32BaseImpl_Stat (IStorage)
605 * This method will retrieve information about this storage object.
607 * See Windows documentation for more details on IStorage methods.
609 static HRESULT WINAPI StorageBaseImpl_Stat(
611 STATSTG* pstatstg, /* [out] */
612 DWORD grfStatFlag) /* [in] */
614 StorageBaseImpl *This = (StorageBaseImpl *)iface;
615 StgProperty curProperty;
617 HRESULT res = STG_E_UNKNOWN;
619 TRACE("(%p, %p, %lx)\n",
620 iface, pstatstg, grfStatFlag);
623 * Perform a sanity check on the parameters.
625 if ( (This==0) || (pstatstg==0))
632 * Read the information from the property.
634 readSuccessful = StorageImpl_ReadProperty(
635 This->ancestorStorage,
636 This->rootPropertySetIndex,
641 StorageUtl_CopyPropertyToSTATSTG(
646 pstatstg->grfMode = This->openFlags;
657 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);
659 TRACE("<-- %08lx\n", res);
663 /************************************************************************
664 * Storage32BaseImpl_RenameElement (IStorage)
666 * This method will rename the specified element.
668 * See Windows documentation for more details on IStorage methods.
670 * Implementation notes: The method used to rename consists of creating a clone
671 * of the deleted StgProperty object setting it with the new name and to
672 * perform a DestroyElement of the old StgProperty.
674 static HRESULT WINAPI StorageBaseImpl_RenameElement(
676 const OLECHAR* pwcsOldName, /* [in] */
677 const OLECHAR* pwcsNewName) /* [in] */
679 StorageBaseImpl *This = (StorageBaseImpl *)iface;
680 IEnumSTATSTGImpl* propertyEnumeration;
681 StgProperty currentProperty;
682 ULONG foundPropertyIndex;
684 TRACE("(%p, %s, %s)\n",
685 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
688 * Create a property enumeration to search the properties
690 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
691 This->rootPropertySetIndex);
694 * Search the enumeration for the new property name
696 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
700 if (foundPropertyIndex != PROPERTY_NULL)
703 * There is already a property with the new name
705 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
706 return STG_E_FILEALREADYEXISTS;
709 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
712 * Search the enumeration for the old property name
714 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
719 * Delete the property enumeration since we don't need it anymore
721 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
723 if (foundPropertyIndex != PROPERTY_NULL)
725 StgProperty renamedProperty;
726 ULONG renamedPropertyIndex;
729 * Setup a new property for the renamed property
731 renamedProperty.sizeOfNameString =
732 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
734 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
735 return STG_E_INVALIDNAME;
737 strcpyW(renamedProperty.name, pwcsNewName);
739 renamedProperty.propertyType = currentProperty.propertyType;
740 renamedProperty.startingBlock = currentProperty.startingBlock;
741 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
742 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
744 renamedProperty.previousProperty = PROPERTY_NULL;
745 renamedProperty.nextProperty = PROPERTY_NULL;
748 * Bring the dirProperty link in case it is a storage and in which
749 * case the renamed storage elements don't require to be reorganized.
751 renamedProperty.dirProperty = currentProperty.dirProperty;
753 /* call CoFileTime to get the current time
754 renamedProperty.timeStampS1
755 renamedProperty.timeStampD1
756 renamedProperty.timeStampS2
757 renamedProperty.timeStampD2
758 renamedProperty.propertyUniqueID
762 * Obtain a free property in the property chain
764 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
767 * Save the new property into the new property spot
769 StorageImpl_WriteProperty(
770 This->ancestorStorage,
771 renamedPropertyIndex,
775 * Find a spot in the property chain for our newly created property.
779 renamedPropertyIndex,
783 * At this point the renamed property has been inserted in the tree,
784 * now, before Destroying the old property we must zero its dirProperty
785 * otherwise the DestroyProperty below will zap it all and we do not want
787 * Also, we fake that the old property is a storage so the DestroyProperty
788 * will not do a SetSize(0) on the stream data.
790 * This means that we need to tweak the StgProperty if it is a stream or a
793 StorageImpl_ReadProperty(This->ancestorStorage,
797 currentProperty.dirProperty = PROPERTY_NULL;
798 currentProperty.propertyType = PROPTYPE_STORAGE;
799 StorageImpl_WriteProperty(
800 This->ancestorStorage,
805 * Invoke Destroy to get rid of the ole property and automatically redo
806 * the linking of its previous and next members...
808 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
814 * There is no property with the old name
816 return STG_E_FILENOTFOUND;
822 /************************************************************************
823 * Storage32BaseImpl_CreateStream (IStorage)
825 * This method will create a stream object within this storage
827 * See Windows documentation for more details on IStorage methods.
829 static HRESULT WINAPI StorageBaseImpl_CreateStream(
831 const OLECHAR* pwcsName, /* [string][in] */
832 DWORD grfMode, /* [in] */
833 DWORD reserved1, /* [in] */
834 DWORD reserved2, /* [in] */
835 IStream** ppstm) /* [out] */
837 StorageBaseImpl *This = (StorageBaseImpl *)iface;
838 IEnumSTATSTGImpl* propertyEnumeration;
839 StgStreamImpl* newStream;
840 StgProperty currentProperty, newStreamProperty;
841 ULONG foundPropertyIndex, newPropertyIndex;
843 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
844 iface, debugstr_w(pwcsName), grfMode,
845 reserved1, reserved2, ppstm);
848 * Validate parameters
851 return STG_E_INVALIDPOINTER;
854 return STG_E_INVALIDNAME;
856 if (reserved1 || reserved2)
857 return STG_E_INVALIDPARAMETER;
860 * Validate the STGM flags
862 if ( FAILED( validateSTGM(grfMode) ))
863 return STG_E_INVALIDFLAG;
865 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
866 return STG_E_INVALIDFLAG;
871 if ((grfMode & STGM_DELETEONRELEASE) ||
872 (grfMode & STGM_TRANSACTED))
873 return STG_E_INVALIDFUNCTION;
876 * Check that we're compatible with the parent's storage mode
877 * if not in transacted mode
879 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
880 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
881 return STG_E_ACCESSDENIED;
885 * Initialize the out parameter
890 * Create a property enumeration to search the properties
892 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
893 This->rootPropertySetIndex);
895 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
899 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
901 if (foundPropertyIndex != PROPERTY_NULL)
904 * An element with this name already exists
906 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
908 IStorage_DestroyElement(iface, pwcsName);
911 return STG_E_FILEALREADYEXISTS;
913 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
915 WARN("read-only storage\n");
916 return STG_E_ACCESSDENIED;
920 * memset the empty property
922 memset(&newStreamProperty, 0, sizeof(StgProperty));
924 newStreamProperty.sizeOfNameString =
925 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
927 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
928 return STG_E_INVALIDNAME;
930 strcpyW(newStreamProperty.name, pwcsName);
932 newStreamProperty.propertyType = PROPTYPE_STREAM;
933 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
934 newStreamProperty.size.u.LowPart = 0;
935 newStreamProperty.size.u.HighPart = 0;
937 newStreamProperty.previousProperty = PROPERTY_NULL;
938 newStreamProperty.nextProperty = PROPERTY_NULL;
939 newStreamProperty.dirProperty = PROPERTY_NULL;
941 /* call CoFileTime to get the current time
942 newStreamProperty.timeStampS1
943 newStreamProperty.timeStampD1
944 newStreamProperty.timeStampS2
945 newStreamProperty.timeStampD2
948 /* newStreamProperty.propertyUniqueID */
951 * Get a free property or create a new one
953 newPropertyIndex = getFreeProperty(This->ancestorStorage);
956 * Save the new property into the new property spot
958 StorageImpl_WriteProperty(
959 This->ancestorStorage,
964 * Find a spot in the property chain for our newly created property.
972 * Open the stream to return it.
974 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
978 *ppstm = (IStream*)newStream;
981 * Since we are returning a pointer to the interface, we have to nail down
984 IStream_AddRef(*ppstm);
986 /* add us to the storage's list of active streams
988 StorageBaseImpl_AddStream(This,newStream);
993 return STG_E_INSUFFICIENTMEMORY;
999 /************************************************************************
1000 * Storage32BaseImpl_SetClass (IStorage)
1002 * This method will write the specified CLSID in the property of this
1005 * See Windows documentation for more details on IStorage methods.
1007 static HRESULT WINAPI StorageBaseImpl_SetClass(
1009 REFCLSID clsid) /* [in] */
1011 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1012 HRESULT hRes = E_FAIL;
1013 StgProperty curProperty;
1016 TRACE("(%p, %p)\n", iface, clsid);
1018 success = StorageImpl_ReadProperty(This->ancestorStorage,
1019 This->rootPropertySetIndex,
1023 curProperty.propertyUniqueID = *clsid;
1025 success = StorageImpl_WriteProperty(This->ancestorStorage,
1026 This->rootPropertySetIndex,
1035 /************************************************************************
1036 ** Storage32Impl implementation
1039 /************************************************************************
1040 * Storage32Impl_CreateStorage (IStorage)
1042 * This method will create the storage object within the provided storage.
1044 * See Windows documentation for more details on IStorage methods.
1046 static HRESULT WINAPI StorageImpl_CreateStorage(
1048 const OLECHAR *pwcsName, /* [string][in] */
1049 DWORD grfMode, /* [in] */
1050 DWORD reserved1, /* [in] */
1051 DWORD reserved2, /* [in] */
1052 IStorage **ppstg) /* [out] */
1054 StorageImpl* const This=(StorageImpl*)iface;
1056 IEnumSTATSTGImpl *propertyEnumeration;
1057 StgProperty currentProperty;
1058 StgProperty newProperty;
1059 ULONG foundPropertyIndex;
1060 ULONG newPropertyIndex;
1063 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1064 iface, debugstr_w(pwcsName), grfMode,
1065 reserved1, reserved2, ppstg);
1068 * Validate parameters
1071 return STG_E_INVALIDPOINTER;
1074 return STG_E_INVALIDNAME;
1077 * Initialize the out parameter
1082 * Validate the STGM flags
1084 if ( FAILED( validateSTGM(grfMode) ) ||
1085 (grfMode & STGM_DELETEONRELEASE) )
1087 WARN("bad grfMode: 0x%lx\n", grfMode);
1088 return STG_E_INVALIDFLAG;
1092 * Check that we're compatible with the parent's storage mode
1094 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1096 WARN("access denied\n");
1097 return STG_E_ACCESSDENIED;
1101 * Create a property enumeration and search the properties
1103 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1104 This->base.rootPropertySetIndex);
1106 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1109 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1111 if (foundPropertyIndex != PROPERTY_NULL)
1114 * An element with this name already exists
1116 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1117 IStorage_DestroyElement(iface, pwcsName);
1120 WARN("file already exists\n");
1121 return STG_E_FILEALREADYEXISTS;
1124 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1126 WARN("read-only storage\n");
1127 return STG_E_ACCESSDENIED;
1131 * memset the empty property
1133 memset(&newProperty, 0, sizeof(StgProperty));
1135 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1137 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1139 FIXME("name too long\n");
1140 return STG_E_INVALIDNAME;
1143 strcpyW(newProperty.name, pwcsName);
1145 newProperty.propertyType = PROPTYPE_STORAGE;
1146 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1147 newProperty.size.u.LowPart = 0;
1148 newProperty.size.u.HighPart = 0;
1150 newProperty.previousProperty = PROPERTY_NULL;
1151 newProperty.nextProperty = PROPERTY_NULL;
1152 newProperty.dirProperty = PROPERTY_NULL;
1154 /* call CoFileTime to get the current time
1155 newProperty.timeStampS1
1156 newProperty.timeStampD1
1157 newProperty.timeStampS2
1158 newProperty.timeStampD2
1161 /* newStorageProperty.propertyUniqueID */
1164 * Obtain a free property in the property chain
1166 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1169 * Save the new property into the new property spot
1171 StorageImpl_WriteProperty(
1172 This->base.ancestorStorage,
1177 * Find a spot in the property chain for our newly created property.
1179 updatePropertyChain(
1185 * Open it to get a pointer to return.
1187 hr = IStorage_OpenStorage(
1189 (const OLECHAR*)pwcsName,
1196 if( (hr != S_OK) || (*ppstg == NULL))
1206 /***************************************************************************
1210 * Get a free property or create a new one.
1212 static ULONG getFreeProperty(
1213 StorageImpl *storage)
1215 ULONG currentPropertyIndex = 0;
1216 ULONG newPropertyIndex = PROPERTY_NULL;
1217 BOOL readSuccessful = TRUE;
1218 StgProperty currentProperty;
1223 * Start by reading the root property
1225 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1226 currentPropertyIndex,
1230 if (currentProperty.sizeOfNameString == 0)
1233 * The property existis and is available, we found it.
1235 newPropertyIndex = currentPropertyIndex;
1241 * We exhausted the property list, we will create more space below
1243 newPropertyIndex = currentPropertyIndex;
1245 currentPropertyIndex++;
1247 } while (newPropertyIndex == PROPERTY_NULL);
1250 * grow the property chain
1252 if (! readSuccessful)
1254 StgProperty emptyProperty;
1255 ULARGE_INTEGER newSize;
1256 ULONG propertyIndex;
1257 ULONG lastProperty = 0;
1258 ULONG blockCount = 0;
1261 * obtain the new count of property blocks
1263 blockCount = BlockChainStream_GetCount(
1264 storage->base.ancestorStorage->rootBlockChain)+1;
1267 * initialize the size used by the property stream
1269 newSize.u.HighPart = 0;
1270 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1273 * add a property block to the property chain
1275 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1278 * memset the empty property in order to initialize the unused newly
1281 memset(&emptyProperty, 0, sizeof(StgProperty));
1286 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1289 propertyIndex = newPropertyIndex;
1290 propertyIndex < lastProperty;
1293 StorageImpl_WriteProperty(
1294 storage->base.ancestorStorage,
1300 return newPropertyIndex;
1303 /****************************************************************************
1307 * Case insensitive comparaison of StgProperty.name by first considering
1310 * Returns <0 when newPrpoerty < currentProperty
1311 * >0 when newPrpoerty > currentProperty
1312 * 0 when newPrpoerty == currentProperty
1314 static LONG propertyNameCmp(
1315 const OLECHAR *newProperty,
1316 const OLECHAR *currentProperty)
1318 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1323 * We compare the string themselves only when they are of the same length
1325 diff = lstrcmpiW( newProperty, currentProperty);
1331 /****************************************************************************
1335 * Properly link this new element in the property chain.
1337 static void updatePropertyChain(
1338 StorageImpl *storage,
1339 ULONG newPropertyIndex,
1340 StgProperty newProperty)
1342 StgProperty currentProperty;
1345 * Read the root property
1347 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1348 storage->base.rootPropertySetIndex,
1351 if (currentProperty.dirProperty != PROPERTY_NULL)
1354 * The root storage contains some element, therefore, start the research
1355 * for the appropriate location.
1358 ULONG current, next, previous, currentPropertyId;
1361 * Keep the StgProperty sequence number of the storage first property
1363 currentPropertyId = currentProperty.dirProperty;
1368 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1369 currentProperty.dirProperty,
1372 previous = currentProperty.previousProperty;
1373 next = currentProperty.nextProperty;
1374 current = currentPropertyId;
1378 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1382 if (previous != PROPERTY_NULL)
1384 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1391 currentProperty.previousProperty = newPropertyIndex;
1392 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1400 if (next != PROPERTY_NULL)
1402 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1409 currentProperty.nextProperty = newPropertyIndex;
1410 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1419 * Trying to insert an item with the same name in the
1420 * subtree structure.
1425 previous = currentProperty.previousProperty;
1426 next = currentProperty.nextProperty;
1432 * The root storage is empty, link the new property to its dir property
1434 currentProperty.dirProperty = newPropertyIndex;
1435 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1436 storage->base.rootPropertySetIndex,
1442 /*************************************************************************
1445 static HRESULT WINAPI StorageImpl_CopyTo(
1447 DWORD ciidExclude, /* [in] */
1448 const IID* rgiidExclude, /* [size_is][unique][in] */
1449 SNB snbExclude, /* [unique][in] */
1450 IStorage* pstgDest) /* [unique][in] */
1452 IEnumSTATSTG *elements = 0;
1453 STATSTG curElement, strStat;
1455 IStorage *pstgTmp, *pstgChild;
1456 IStream *pstrTmp, *pstrChild;
1458 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1459 FIXME("Exclude option not implemented\n");
1461 TRACE("(%p, %ld, %p, %p, %p)\n",
1462 iface, ciidExclude, rgiidExclude,
1463 snbExclude, pstgDest);
1466 * Perform a sanity check
1468 if ( pstgDest == 0 )
1469 return STG_E_INVALIDPOINTER;
1472 * Enumerate the elements
1474 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1482 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1483 IStorage_SetClass( pstgDest, &curElement.clsid );
1488 * Obtain the next element
1490 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1492 if ( hr == S_FALSE )
1494 hr = S_OK; /* done, every element has been copied */
1498 if (curElement.type == STGTY_STORAGE)
1501 * open child source storage
1503 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1504 STGM_READ|STGM_SHARE_EXCLUSIVE,
1505 NULL, 0, &pstgChild );
1511 * Check if destination storage is not a child of the source
1512 * storage, which will cause an infinite loop
1514 if (pstgChild == pstgDest)
1516 IEnumSTATSTG_Release(elements);
1518 return STG_E_ACCESSDENIED;
1522 * create a new storage in destination storage
1524 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1525 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1529 * if it already exist, don't create a new one use this one
1531 if (hr == STG_E_FILEALREADYEXISTS)
1533 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1534 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1535 NULL, 0, &pstgTmp );
1543 * do the copy recursively
1545 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1546 snbExclude, pstgTmp );
1548 IStorage_Release( pstgTmp );
1549 IStorage_Release( pstgChild );
1551 else if (curElement.type == STGTY_STREAM)
1554 * create a new stream in destination storage. If the stream already
1555 * exist, it will be deleted and a new one will be created.
1557 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1558 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1565 * open child stream storage
1567 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1568 STGM_READ|STGM_SHARE_EXCLUSIVE,
1575 * Get the size of the source stream
1577 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1580 * Set the size of the destination stream.
1582 IStream_SetSize(pstrTmp, strStat.cbSize);
1587 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1590 IStream_Release( pstrTmp );
1591 IStream_Release( pstrChild );
1595 WARN("unknown element type: %ld\n", curElement.type);
1598 } while (hr == S_OK);
1603 IEnumSTATSTG_Release(elements);
1608 /*************************************************************************
1609 * MoveElementTo (IStorage)
1611 static HRESULT WINAPI StorageImpl_MoveElementTo(
1613 const OLECHAR *pwcsName, /* [string][in] */
1614 IStorage *pstgDest, /* [unique][in] */
1615 const OLECHAR *pwcsNewName,/* [string][in] */
1616 DWORD grfFlags) /* [in] */
1618 FIXME("(%p %s %p %s %lu): stub\n", iface,
1619 debugstr_w(pwcsName), pstgDest,
1620 debugstr_w(pwcsNewName), grfFlags);
1624 /*************************************************************************
1627 * Ensures that any changes made to a storage object open in transacted mode
1628 * are reflected in the parent storage
1631 * Wine doesn't implement transacted mode, which seems to be a basic
1632 * optimization, so we can ignore this stub for now.
1634 static HRESULT WINAPI StorageImpl_Commit(
1636 DWORD grfCommitFlags)/* [in] */
1638 FIXME("(%p %ld): stub\n", iface, grfCommitFlags);
1642 /*************************************************************************
1645 * Discard all changes that have been made since the last commit operation
1647 static HRESULT WINAPI StorageImpl_Revert(
1650 FIXME("(%p): stub\n", iface);
1654 /*************************************************************************
1655 * DestroyElement (IStorage)
1657 * Strategy: This implementation is built this way for simplicity not for speed.
1658 * I always delete the topmost element of the enumeration and adjust
1659 * the deleted element pointer all the time. This takes longer to
1660 * do but allow to reinvoke DestroyElement whenever we encounter a
1661 * storage object. The optimisation resides in the usage of another
1662 * enumeration strategy that would give all the leaves of a storage
1663 * first. (postfix order)
1665 static HRESULT WINAPI StorageImpl_DestroyElement(
1667 const OLECHAR *pwcsName)/* [string][in] */
1669 StorageImpl* const This=(StorageImpl*)iface;
1671 IEnumSTATSTGImpl* propertyEnumeration;
1674 StgProperty propertyToDelete;
1675 StgProperty parentProperty;
1676 ULONG foundPropertyIndexToDelete;
1677 ULONG typeOfRelation;
1678 ULONG parentPropertyId;
1681 iface, debugstr_w(pwcsName));
1684 * Perform a sanity check on the parameters.
1687 return STG_E_INVALIDPOINTER;
1690 * Create a property enumeration to search the property with the given name
1692 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1693 This->base.ancestorStorage,
1694 This->base.rootPropertySetIndex);
1696 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1697 propertyEnumeration,
1701 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1703 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1705 return STG_E_FILENOTFOUND;
1709 * Find the parent property of the property to delete (the one that
1710 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1711 * the parent is This. Otherwise, the parent is one of its sibling...
1715 * First, read This's StgProperty..
1717 res = StorageImpl_ReadProperty(
1718 This->base.ancestorStorage,
1719 This->base.rootPropertySetIndex,
1725 * Second, check to see if by any chance the actual storage (This) is not
1726 * the parent of the property to delete... We never know...
1728 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1731 * Set data as it would have been done in the else part...
1733 typeOfRelation = PROPERTY_RELATION_DIR;
1734 parentPropertyId = This->base.rootPropertySetIndex;
1739 * Create a property enumeration to search the parent properties, and
1740 * delete it once done.
1742 IEnumSTATSTGImpl* propertyEnumeration2;
1744 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1745 This->base.ancestorStorage,
1746 This->base.rootPropertySetIndex);
1748 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1749 propertyEnumeration2,
1750 foundPropertyIndexToDelete,
1754 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1757 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1759 hr = deleteStorageProperty(
1761 foundPropertyIndexToDelete,
1764 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1766 hr = deleteStreamProperty(
1768 foundPropertyIndexToDelete,
1776 * Adjust the property chain
1778 hr = adjustPropertyChain(
1789 /************************************************************************
1790 * StorageImpl_Stat (IStorage)
1792 * This method will retrieve information about this storage object.
1794 * See Windows documentation for more details on IStorage methods.
1796 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1797 STATSTG* pstatstg, /* [out] */
1798 DWORD grfStatFlag) /* [in] */
1800 StorageImpl* const This = (StorageImpl*)iface;
1801 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1803 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1805 CoTaskMemFree(pstatstg->pwcsName);
1806 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1807 strcpyW(pstatstg->pwcsName, This->pwcsName);
1813 /******************************************************************************
1814 * Internal stream list handlers
1817 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1819 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1820 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1823 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1825 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1826 list_remove(&(strm->StrmListEntry));
1829 void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1831 struct list *cur, *cur2;
1832 StgStreamImpl *strm=NULL;
1834 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1835 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1836 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1837 strm->parentStorage = NULL;
1843 /*********************************************************************
1847 * Perform the deletion of a complete storage node
1850 static HRESULT deleteStorageProperty(
1851 StorageImpl *parentStorage,
1852 ULONG indexOfPropertyToDelete,
1853 StgProperty propertyToDelete)
1855 IEnumSTATSTG *elements = 0;
1856 IStorage *childStorage = 0;
1857 STATSTG currentElement;
1859 HRESULT destroyHr = S_OK;
1862 * Open the storage and enumerate it
1864 hr = StorageBaseImpl_OpenStorage(
1865 (IStorage*)parentStorage,
1866 propertyToDelete.name,
1868 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1879 * Enumerate the elements
1881 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1886 * Obtain the next element
1888 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1891 destroyHr = StorageImpl_DestroyElement(
1892 (IStorage*)childStorage,
1893 (OLECHAR*)currentElement.pwcsName);
1895 CoTaskMemFree(currentElement.pwcsName);
1899 * We need to Reset the enumeration every time because we delete elements
1900 * and the enumeration could be invalid
1902 IEnumSTATSTG_Reset(elements);
1904 } while ((hr == S_OK) && (destroyHr == S_OK));
1907 * Invalidate the property by zeroing its name member.
1909 propertyToDelete.sizeOfNameString = 0;
1911 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1912 indexOfPropertyToDelete,
1915 IStorage_Release(childStorage);
1916 IEnumSTATSTG_Release(elements);
1921 /*********************************************************************
1925 * Perform the deletion of a stream node
1928 static HRESULT deleteStreamProperty(
1929 StorageImpl *parentStorage,
1930 ULONG indexOfPropertyToDelete,
1931 StgProperty propertyToDelete)
1935 ULARGE_INTEGER size;
1937 size.u.HighPart = 0;
1940 hr = StorageBaseImpl_OpenStream(
1941 (IStorage*)parentStorage,
1942 (OLECHAR*)propertyToDelete.name,
1944 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1956 hr = IStream_SetSize(pis, size);
1964 * Release the stream object.
1966 IStream_Release(pis);
1969 * Invalidate the property by zeroing its name member.
1971 propertyToDelete.sizeOfNameString = 0;
1974 * Here we should re-read the property so we get the updated pointer
1975 * but since we are here to zap it, I don't do it...
1977 StorageImpl_WriteProperty(
1978 parentStorage->base.ancestorStorage,
1979 indexOfPropertyToDelete,
1985 /*********************************************************************
1989 * Finds a placeholder for the StgProperty within the Storage
1992 static HRESULT findPlaceholder(
1993 StorageImpl *storage,
1994 ULONG propertyIndexToStore,
1995 ULONG storePropertyIndex,
1998 StgProperty storeProperty;
2003 * Read the storage property
2005 res = StorageImpl_ReadProperty(
2006 storage->base.ancestorStorage,
2015 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2017 if (storeProperty.previousProperty != PROPERTY_NULL)
2019 return findPlaceholder(
2021 propertyIndexToStore,
2022 storeProperty.previousProperty,
2027 storeProperty.previousProperty = propertyIndexToStore;
2030 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2032 if (storeProperty.nextProperty != PROPERTY_NULL)
2034 return findPlaceholder(
2036 propertyIndexToStore,
2037 storeProperty.nextProperty,
2042 storeProperty.nextProperty = propertyIndexToStore;
2045 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2047 if (storeProperty.dirProperty != PROPERTY_NULL)
2049 return findPlaceholder(
2051 propertyIndexToStore,
2052 storeProperty.dirProperty,
2057 storeProperty.dirProperty = propertyIndexToStore;
2061 hr = StorageImpl_WriteProperty(
2062 storage->base.ancestorStorage,
2074 /*************************************************************************
2078 * This method takes the previous and the next property link of a property
2079 * to be deleted and find them a place in the Storage.
2081 static HRESULT adjustPropertyChain(
2083 StgProperty propertyToDelete,
2084 StgProperty parentProperty,
2085 ULONG parentPropertyId,
2088 ULONG newLinkProperty = PROPERTY_NULL;
2089 BOOL needToFindAPlaceholder = FALSE;
2090 ULONG storeNode = PROPERTY_NULL;
2091 ULONG toStoreNode = PROPERTY_NULL;
2092 INT relationType = 0;
2096 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2098 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2101 * Set the parent previous to the property to delete previous
2103 newLinkProperty = propertyToDelete.previousProperty;
2105 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2108 * We also need to find a storage for the other link, setup variables
2109 * to do this at the end...
2111 needToFindAPlaceholder = TRUE;
2112 storeNode = propertyToDelete.previousProperty;
2113 toStoreNode = propertyToDelete.nextProperty;
2114 relationType = PROPERTY_RELATION_NEXT;
2117 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2120 * Set the parent previous to the property to delete next
2122 newLinkProperty = propertyToDelete.nextProperty;
2126 * Link it for real...
2128 parentProperty.previousProperty = newLinkProperty;
2131 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2133 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2136 * Set the parent next to the property to delete next previous
2138 newLinkProperty = propertyToDelete.previousProperty;
2140 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2143 * We also need to find a storage for the other link, setup variables
2144 * to do this at the end...
2146 needToFindAPlaceholder = TRUE;
2147 storeNode = propertyToDelete.previousProperty;
2148 toStoreNode = propertyToDelete.nextProperty;
2149 relationType = PROPERTY_RELATION_NEXT;
2152 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2155 * Set the parent next to the property to delete next
2157 newLinkProperty = propertyToDelete.nextProperty;
2161 * Link it for real...
2163 parentProperty.nextProperty = newLinkProperty;
2165 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2167 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2170 * Set the parent dir to the property to delete previous
2172 newLinkProperty = propertyToDelete.previousProperty;
2174 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2177 * We also need to find a storage for the other link, setup variables
2178 * to do this at the end...
2180 needToFindAPlaceholder = TRUE;
2181 storeNode = propertyToDelete.previousProperty;
2182 toStoreNode = propertyToDelete.nextProperty;
2183 relationType = PROPERTY_RELATION_NEXT;
2186 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2189 * Set the parent dir to the property to delete next
2191 newLinkProperty = propertyToDelete.nextProperty;
2195 * Link it for real...
2197 parentProperty.dirProperty = newLinkProperty;
2201 * Write back the parent property
2203 res = StorageImpl_WriteProperty(
2204 This->base.ancestorStorage,
2213 * If a placeholder is required for the other link, then, find one and
2214 * get out of here...
2216 if (needToFindAPlaceholder)
2218 hr = findPlaceholder(
2229 /******************************************************************************
2230 * SetElementTimes (IStorage)
2232 static HRESULT WINAPI StorageImpl_SetElementTimes(
2234 const OLECHAR *pwcsName,/* [string][in] */
2235 const FILETIME *pctime, /* [in] */
2236 const FILETIME *patime, /* [in] */
2237 const FILETIME *pmtime) /* [in] */
2239 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2243 /******************************************************************************
2244 * SetStateBits (IStorage)
2246 static HRESULT WINAPI StorageImpl_SetStateBits(
2248 DWORD grfStateBits,/* [in] */
2249 DWORD grfMask) /* [in] */
2251 FIXME("not implemented!\n");
2256 * Virtual function table for the IStorage32Impl class.
2258 static const IStorageVtbl Storage32Impl_Vtbl =
2260 StorageBaseImpl_QueryInterface,
2261 StorageBaseImpl_AddRef,
2262 StorageBaseImpl_Release,
2263 StorageBaseImpl_CreateStream,
2264 StorageBaseImpl_OpenStream,
2265 StorageImpl_CreateStorage,
2266 StorageBaseImpl_OpenStorage,
2268 StorageImpl_MoveElementTo,
2271 StorageBaseImpl_EnumElements,
2272 StorageImpl_DestroyElement,
2273 StorageBaseImpl_RenameElement,
2274 StorageImpl_SetElementTimes,
2275 StorageBaseImpl_SetClass,
2276 StorageImpl_SetStateBits,
2280 HRESULT StorageImpl_Construct(
2290 StgProperty currentProperty;
2291 BOOL readSuccessful;
2292 ULONG currentPropertyIndex;
2294 if ( FAILED( validateSTGM(openFlags) ))
2295 return STG_E_INVALIDFLAG;
2297 memset(This, 0, sizeof(StorageImpl));
2300 * Initialize stream list
2303 list_init(&This->base.strmHead);
2306 * Initialize the virtual function table.
2308 This->base.lpVtbl = &Storage32Impl_Vtbl;
2309 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2310 This->base.v_destructor = &StorageImpl_Destroy;
2311 This->base.openFlags = (openFlags & ~STGM_CREATE);
2314 * This is the top-level storage so initialize the ancestor pointer
2317 This->base.ancestorStorage = This;
2320 * Initialize the physical support of the storage.
2322 This->hFile = hFile;
2325 * Store copy of file path.
2328 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2329 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2330 if (!This->pwcsName)
2331 return STG_E_INSUFFICIENTMEMORY;
2332 strcpyW(This->pwcsName, pwcsName);
2336 * Initialize the big block cache.
2338 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2339 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2340 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2346 if (This->bigBlockFile == 0)
2351 ULARGE_INTEGER size;
2352 BYTE* bigBlockBuffer;
2355 * Initialize all header variables:
2356 * - The big block depot consists of one block and it is at block 0
2357 * - The properties start at block 1
2358 * - There is no small block depot
2360 memset( This->bigBlockDepotStart,
2362 sizeof(This->bigBlockDepotStart));
2364 This->bigBlockDepotCount = 1;
2365 This->bigBlockDepotStart[0] = 0;
2366 This->rootStartBlock = 1;
2367 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2368 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2369 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2370 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2371 This->extBigBlockDepotCount = 0;
2373 StorageImpl_SaveFileHeader(This);
2376 * Add one block for the big block depot and one block for the properties
2378 size.u.HighPart = 0;
2379 size.u.LowPart = This->bigBlockSize * 3;
2380 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2383 * Initialize the big block depot
2385 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2386 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2387 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2388 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2389 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2394 * Load the header for the file.
2396 hr = StorageImpl_LoadFileHeader(This);
2400 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2407 * There is no block depot cached yet.
2409 This->indexBlockDepotCached = 0xFFFFFFFF;
2412 * Start searching for free blocks with block 0.
2414 This->prevFreeBlock = 0;
2417 * Create the block chain abstractions.
2419 if(!(This->rootBlockChain =
2420 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2421 return STG_E_READFAULT;
2423 if(!(This->smallBlockDepotChain =
2424 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2426 return STG_E_READFAULT;
2429 * Write the root property (memory only)
2433 StgProperty rootProp;
2435 * Initialize the property chain
2437 memset(&rootProp, 0, sizeof(rootProp));
2438 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2439 sizeof(rootProp.name)/sizeof(WCHAR) );
2440 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2441 rootProp.propertyType = PROPTYPE_ROOT;
2442 rootProp.previousProperty = PROPERTY_NULL;
2443 rootProp.nextProperty = PROPERTY_NULL;
2444 rootProp.dirProperty = PROPERTY_NULL;
2445 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2446 rootProp.size.u.HighPart = 0;
2447 rootProp.size.u.LowPart = 0;
2449 StorageImpl_WriteProperty(This, 0, &rootProp);
2453 * Find the ID of the root in the property sets.
2455 currentPropertyIndex = 0;
2459 readSuccessful = StorageImpl_ReadProperty(
2461 currentPropertyIndex,
2466 if ( (currentProperty.sizeOfNameString != 0 ) &&
2467 (currentProperty.propertyType == PROPTYPE_ROOT) )
2469 This->base.rootPropertySetIndex = currentPropertyIndex;
2473 currentPropertyIndex++;
2475 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2477 if (!readSuccessful)
2480 return STG_E_READFAULT;
2484 * Create the block chain abstraction for the small block root chain.
2486 if(!(This->smallBlockRootChain =
2487 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2488 return STG_E_READFAULT;
2493 void StorageImpl_Destroy(StorageBaseImpl* iface)
2495 StorageImpl *This = (StorageImpl*) iface;
2496 TRACE("(%p)\n", This);
2498 StorageBaseImpl_DeleteAll(&This->base);
2500 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2502 BlockChainStream_Destroy(This->smallBlockRootChain);
2503 BlockChainStream_Destroy(This->rootBlockChain);
2504 BlockChainStream_Destroy(This->smallBlockDepotChain);
2506 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2507 HeapFree(GetProcessHeap(), 0, This);
2510 /******************************************************************************
2511 * Storage32Impl_GetNextFreeBigBlock
2513 * Returns the index of the next free big block.
2514 * If the big block depot is filled, this method will enlarge it.
2517 ULONG StorageImpl_GetNextFreeBigBlock(
2520 ULONG depotBlockIndexPos;
2522 ULONG depotBlockOffset;
2523 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2524 ULONG nextBlockIndex = BLOCK_SPECIAL;
2526 ULONG freeBlock = BLOCK_UNUSED;
2528 depotIndex = This->prevFreeBlock / blocksPerDepot;
2529 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2532 * Scan the entire big block depot until we find a block marked free
2534 while (nextBlockIndex != BLOCK_UNUSED)
2536 if (depotIndex < COUNT_BBDEPOTINHEADER)
2538 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2541 * Grow the primary depot.
2543 if (depotBlockIndexPos == BLOCK_UNUSED)
2545 depotBlockIndexPos = depotIndex*blocksPerDepot;
2548 * Add a block depot.
2550 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2551 This->bigBlockDepotCount++;
2552 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2555 * Flag it as a block depot.
2557 StorageImpl_SetNextBlockInChain(This,
2561 /* Save new header information.
2563 StorageImpl_SaveFileHeader(This);
2568 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2570 if (depotBlockIndexPos == BLOCK_UNUSED)
2573 * Grow the extended depot.
2575 ULONG extIndex = BLOCK_UNUSED;
2576 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2577 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2579 if (extBlockOffset == 0)
2581 /* We need an extended block.
2583 extIndex = Storage32Impl_AddExtBlockDepot(This);
2584 This->extBigBlockDepotCount++;
2585 depotBlockIndexPos = extIndex + 1;
2588 depotBlockIndexPos = depotIndex * blocksPerDepot;
2591 * Add a block depot and mark it in the extended block.
2593 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2594 This->bigBlockDepotCount++;
2595 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2597 /* Flag the block depot.
2599 StorageImpl_SetNextBlockInChain(This,
2603 /* If necessary, flag the extended depot block.
2605 if (extIndex != BLOCK_UNUSED)
2606 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2608 /* Save header information.
2610 StorageImpl_SaveFileHeader(This);
2614 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2616 if (depotBuffer != 0)
2618 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2619 ( nextBlockIndex != BLOCK_UNUSED))
2621 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2623 if (nextBlockIndex == BLOCK_UNUSED)
2625 freeBlock = (depotIndex * blocksPerDepot) +
2626 (depotBlockOffset/sizeof(ULONG));
2629 depotBlockOffset += sizeof(ULONG);
2632 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2636 depotBlockOffset = 0;
2639 This->prevFreeBlock = freeBlock;
2644 /******************************************************************************
2645 * Storage32Impl_AddBlockDepot
2647 * This will create a depot block, essentially it is a block initialized
2650 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2654 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2657 * Initialize blocks as free
2659 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2661 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2664 /******************************************************************************
2665 * Storage32Impl_GetExtDepotBlock
2667 * Returns the index of the block that corresponds to the specified depot
2668 * index. This method is only for depot indexes equal or greater than
2669 * COUNT_BBDEPOTINHEADER.
2671 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2673 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2674 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2675 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2676 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2677 ULONG blockIndex = BLOCK_UNUSED;
2678 ULONG extBlockIndex = This->extBigBlockDepotStart;
2680 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2682 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2683 return BLOCK_UNUSED;
2685 while (extBlockCount > 0)
2687 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2691 if (extBlockIndex != BLOCK_UNUSED)
2695 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2697 if (depotBuffer != 0)
2699 StorageUtl_ReadDWord(depotBuffer,
2700 extBlockOffset * sizeof(ULONG),
2703 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2710 /******************************************************************************
2711 * Storage32Impl_SetExtDepotBlock
2713 * Associates the specified block index to the specified depot index.
2714 * This method is only for depot indexes equal or greater than
2715 * COUNT_BBDEPOTINHEADER.
2717 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2721 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2722 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2723 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2724 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2725 ULONG extBlockIndex = This->extBigBlockDepotStart;
2727 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2729 while (extBlockCount > 0)
2731 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2735 if (extBlockIndex != BLOCK_UNUSED)
2739 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2741 if (depotBuffer != 0)
2743 StorageUtl_WriteDWord(depotBuffer,
2744 extBlockOffset * sizeof(ULONG),
2747 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2752 /******************************************************************************
2753 * Storage32Impl_AddExtBlockDepot
2755 * Creates an extended depot block.
2757 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2759 ULONG numExtBlocks = This->extBigBlockDepotCount;
2760 ULONG nextExtBlock = This->extBigBlockDepotStart;
2761 BYTE* depotBuffer = NULL;
2762 ULONG index = BLOCK_UNUSED;
2763 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2764 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2765 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2767 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2768 blocksPerDepotBlock;
2770 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2773 * The first extended block.
2775 This->extBigBlockDepotStart = index;
2781 * Follow the chain to the last one.
2783 for (i = 0; i < (numExtBlocks - 1); i++)
2785 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2789 * Add the new extended block to the chain.
2791 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2792 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2793 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2797 * Initialize this block.
2799 depotBuffer = StorageImpl_GetBigBlock(This, index);
2800 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2801 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2806 /******************************************************************************
2807 * Storage32Impl_FreeBigBlock
2809 * This method will flag the specified block as free in the big block depot.
2811 void StorageImpl_FreeBigBlock(
2815 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2817 if (blockIndex < This->prevFreeBlock)
2818 This->prevFreeBlock = blockIndex;
2821 /************************************************************************
2822 * Storage32Impl_GetNextBlockInChain
2824 * This method will retrieve the block index of the next big block in
2827 * Params: This - Pointer to the Storage object.
2828 * blockIndex - Index of the block to retrieve the chain
2830 * nextBlockIndex - receives the return value.
2832 * Returns: This method returns the index of the next block in the chain.
2833 * It will return the constants:
2834 * BLOCK_SPECIAL - If the block given was not part of a
2836 * BLOCK_END_OF_CHAIN - If the block given was the last in
2838 * BLOCK_UNUSED - If the block given was not past of a chain
2840 * BLOCK_EXTBBDEPOT - This block is part of the extended
2843 * See Windows documentation for more details on IStorage methods.
2845 HRESULT StorageImpl_GetNextBlockInChain(
2848 ULONG* nextBlockIndex)
2850 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2851 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2852 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2854 ULONG depotBlockIndexPos;
2857 *nextBlockIndex = BLOCK_SPECIAL;
2859 if(depotBlockCount >= This->bigBlockDepotCount)
2861 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2862 This->bigBlockDepotCount);
2863 return STG_E_READFAULT;
2867 * Cache the currently accessed depot block.
2869 if (depotBlockCount != This->indexBlockDepotCached)
2871 This->indexBlockDepotCached = depotBlockCount;
2873 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2875 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2880 * We have to look in the extended depot.
2882 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2885 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2888 return STG_E_READFAULT;
2890 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2892 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2893 This->blockDepotCached[index] = *nextBlockIndex;
2895 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2898 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2903 /******************************************************************************
2904 * Storage32Impl_GetNextExtendedBlock
2906 * Given an extended block this method will return the next extended block.
2909 * The last ULONG of an extended block is the block index of the next
2910 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2914 * - The index of the next extended block
2915 * - BLOCK_UNUSED: there is no next extended block.
2916 * - Any other return values denotes failure.
2918 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2920 ULONG nextBlockIndex = BLOCK_SPECIAL;
2921 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2924 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2928 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2930 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2933 return nextBlockIndex;
2936 /******************************************************************************
2937 * Storage32Impl_SetNextBlockInChain
2939 * This method will write the index of the specified block's next block
2940 * in the big block depot.
2942 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2945 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2946 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2947 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2950 void StorageImpl_SetNextBlockInChain(
2955 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2956 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2957 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2958 ULONG depotBlockIndexPos;
2961 assert(depotBlockCount < This->bigBlockDepotCount);
2962 assert(blockIndex != nextBlock);
2964 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2966 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2971 * We have to look in the extended depot.
2973 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2976 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2980 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2981 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2985 * Update the cached block depot, if necessary.
2987 if (depotBlockCount == This->indexBlockDepotCached)
2989 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2993 /******************************************************************************
2994 * Storage32Impl_LoadFileHeader
2996 * This method will read in the file header, i.e. big block index -1.
2998 HRESULT StorageImpl_LoadFileHeader(
3001 HRESULT hr = STG_E_FILENOTFOUND;
3002 void* headerBigBlock = NULL;
3007 * Get a pointer to the big block of data containing the header.
3009 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
3012 * Extract the information from the header.
3014 if (headerBigBlock!=0)
3017 * Check for the "magic number" signature and return an error if it is not
3020 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3022 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3023 return STG_E_OLDFORMAT;
3026 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3028 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3029 return STG_E_INVALIDHEADER;
3032 StorageUtl_ReadWord(
3034 OFFSET_BIGBLOCKSIZEBITS,
3035 &This->bigBlockSizeBits);
3037 StorageUtl_ReadWord(
3039 OFFSET_SMALLBLOCKSIZEBITS,
3040 &This->smallBlockSizeBits);
3042 StorageUtl_ReadDWord(
3044 OFFSET_BBDEPOTCOUNT,
3045 &This->bigBlockDepotCount);
3047 StorageUtl_ReadDWord(
3049 OFFSET_ROOTSTARTBLOCK,
3050 &This->rootStartBlock);
3052 StorageUtl_ReadDWord(
3054 OFFSET_SBDEPOTSTART,
3055 &This->smallBlockDepotStart);
3057 StorageUtl_ReadDWord(
3059 OFFSET_EXTBBDEPOTSTART,
3060 &This->extBigBlockDepotStart);
3062 StorageUtl_ReadDWord(
3064 OFFSET_EXTBBDEPOTCOUNT,
3065 &This->extBigBlockDepotCount);
3067 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3069 StorageUtl_ReadDWord(
3071 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3072 &(This->bigBlockDepotStart[index]));
3076 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3080 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3081 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3085 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3086 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3090 * Right now, the code is making some assumptions about the size of the
3091 * blocks, just make sure they are what we're expecting.
3093 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3094 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3096 WARN("Broken OLE storage file\n");
3097 hr = STG_E_INVALIDHEADER;
3103 * Release the block.
3105 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3111 /******************************************************************************
3112 * Storage32Impl_SaveFileHeader
3114 * This method will save to the file the header, i.e. big block -1.
3116 void StorageImpl_SaveFileHeader(
3119 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3124 * Get a pointer to the big block of data containing the header.
3126 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3129 * If the block read failed, the file is probably new.
3134 * Initialize for all unknown fields.
3136 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3139 * Initialize the magic number.
3141 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3144 * And a bunch of things we don't know what they mean
3146 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3147 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3148 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3149 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3153 * Write the information to the header.
3155 StorageUtl_WriteWord(
3157 OFFSET_BIGBLOCKSIZEBITS,
3158 This->bigBlockSizeBits);
3160 StorageUtl_WriteWord(
3162 OFFSET_SMALLBLOCKSIZEBITS,
3163 This->smallBlockSizeBits);
3165 StorageUtl_WriteDWord(
3167 OFFSET_BBDEPOTCOUNT,
3168 This->bigBlockDepotCount);
3170 StorageUtl_WriteDWord(
3172 OFFSET_ROOTSTARTBLOCK,
3173 This->rootStartBlock);
3175 StorageUtl_WriteDWord(
3177 OFFSET_SBDEPOTSTART,
3178 This->smallBlockDepotStart);
3180 StorageUtl_WriteDWord(
3182 OFFSET_SBDEPOTCOUNT,
3183 This->smallBlockDepotChain ?
3184 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3186 StorageUtl_WriteDWord(
3188 OFFSET_EXTBBDEPOTSTART,
3189 This->extBigBlockDepotStart);
3191 StorageUtl_WriteDWord(
3193 OFFSET_EXTBBDEPOTCOUNT,
3194 This->extBigBlockDepotCount);
3196 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3198 StorageUtl_WriteDWord(
3200 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3201 (This->bigBlockDepotStart[index]));
3205 * Write the big block back to the file.
3207 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3210 /******************************************************************************
3211 * Storage32Impl_ReadProperty
3213 * This method will read the specified property from the property chain.
3215 BOOL StorageImpl_ReadProperty(
3218 StgProperty* buffer)
3220 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3221 ULARGE_INTEGER offsetInPropSet;
3222 BOOL readSuccessful;
3225 offsetInPropSet.u.HighPart = 0;
3226 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3228 readSuccessful = BlockChainStream_ReadAt(
3229 This->rootBlockChain,
3237 /* replace the name of root entry (often "Root Entry") by the file name */
3238 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3239 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3241 memset(buffer->name, 0, sizeof(buffer->name));
3245 PROPERTY_NAME_BUFFER_LEN );
3246 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3248 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3250 StorageUtl_ReadWord(
3252 OFFSET_PS_NAMELENGTH,
3253 &buffer->sizeOfNameString);
3255 StorageUtl_ReadDWord(
3257 OFFSET_PS_PREVIOUSPROP,
3258 &buffer->previousProperty);
3260 StorageUtl_ReadDWord(
3263 &buffer->nextProperty);
3265 StorageUtl_ReadDWord(
3268 &buffer->dirProperty);
3270 StorageUtl_ReadGUID(
3273 &buffer->propertyUniqueID);
3275 StorageUtl_ReadDWord(
3278 &buffer->timeStampS1);
3280 StorageUtl_ReadDWord(
3283 &buffer->timeStampD1);
3285 StorageUtl_ReadDWord(
3288 &buffer->timeStampS2);
3290 StorageUtl_ReadDWord(
3293 &buffer->timeStampD2);
3295 StorageUtl_ReadDWord(
3297 OFFSET_PS_STARTBLOCK,
3298 &buffer->startingBlock);
3300 StorageUtl_ReadDWord(
3303 &buffer->size.u.LowPart);
3305 buffer->size.u.HighPart = 0;
3308 return readSuccessful;
3311 /*********************************************************************
3312 * Write the specified property into the property chain
3314 BOOL StorageImpl_WriteProperty(
3317 StgProperty* buffer)
3319 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3320 ULARGE_INTEGER offsetInPropSet;
3321 BOOL writeSuccessful;
3324 offsetInPropSet.u.HighPart = 0;
3325 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3327 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3330 currentProperty + OFFSET_PS_NAME,
3332 PROPERTY_NAME_BUFFER_LEN );
3334 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3336 StorageUtl_WriteWord(
3338 OFFSET_PS_NAMELENGTH,
3339 buffer->sizeOfNameString);
3341 StorageUtl_WriteDWord(
3343 OFFSET_PS_PREVIOUSPROP,
3344 buffer->previousProperty);
3346 StorageUtl_WriteDWord(
3349 buffer->nextProperty);
3351 StorageUtl_WriteDWord(
3354 buffer->dirProperty);
3356 StorageUtl_WriteGUID(
3359 &buffer->propertyUniqueID);
3361 StorageUtl_WriteDWord(
3364 buffer->timeStampS1);
3366 StorageUtl_WriteDWord(
3369 buffer->timeStampD1);
3371 StorageUtl_WriteDWord(
3374 buffer->timeStampS2);
3376 StorageUtl_WriteDWord(
3379 buffer->timeStampD2);
3381 StorageUtl_WriteDWord(
3383 OFFSET_PS_STARTBLOCK,
3384 buffer->startingBlock);
3386 StorageUtl_WriteDWord(
3389 buffer->size.u.LowPart);
3391 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3396 return writeSuccessful;
3399 BOOL StorageImpl_ReadBigBlock(
3404 void* bigBlockBuffer;
3406 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3408 if (bigBlockBuffer!=0)
3410 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3412 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3420 BOOL StorageImpl_WriteBigBlock(
3425 void* bigBlockBuffer;
3427 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3429 if (bigBlockBuffer!=0)
3431 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3433 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3441 void* StorageImpl_GetROBigBlock(
3445 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3448 void* StorageImpl_GetBigBlock(
3452 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3455 void StorageImpl_ReleaseBigBlock(
3459 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3462 /******************************************************************************
3463 * Storage32Impl_SmallBlocksToBigBlocks
3465 * This method will convert a small block chain to a big block chain.
3466 * The small block chain will be destroyed.
3468 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3470 SmallBlockChainStream** ppsbChain)
3472 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3473 ULARGE_INTEGER size, offset;
3474 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3475 ULONG propertyIndex;
3477 HRESULT successRead;
3478 StgProperty chainProperty;
3480 BlockChainStream *bbTempChain = NULL;
3481 BlockChainStream *bigBlockChain = NULL;
3484 * Create a temporary big block chain that doesn't have
3485 * an associated property. This temporary chain will be
3486 * used to copy data from small blocks to big blocks.
3488 bbTempChain = BlockChainStream_Construct(This,
3491 if(!bbTempChain) return NULL;
3493 * Grow the big block chain.
3495 size = SmallBlockChainStream_GetSize(*ppsbChain);
3496 BlockChainStream_SetSize(bbTempChain, size);
3499 * Copy the contents of the small block chain to the big block chain
3500 * by small block size increments.
3502 offset.u.LowPart = 0;
3503 offset.u.HighPart = 0;
3507 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3510 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3512 DEF_SMALL_BLOCK_SIZE,
3515 if (FAILED(successRead))
3520 cbTotalRead += cbRead;
3522 successWrite = BlockChainStream_WriteAt(bbTempChain,
3531 cbTotalWritten += cbWritten;
3532 offset.u.LowPart += This->smallBlockSize;
3534 } while (cbRead > 0);
3535 HeapFree(GetProcessHeap(),0,buffer);
3537 assert(cbTotalRead == cbTotalWritten);
3540 * Destroy the small block chain.
3542 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3543 size.u.HighPart = 0;
3545 SmallBlockChainStream_SetSize(*ppsbChain, size);
3546 SmallBlockChainStream_Destroy(*ppsbChain);
3550 * Change the property information. This chain is now a big block chain
3551 * and it doesn't reside in the small blocks chain anymore.
3553 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3555 chainProperty.startingBlock = bbHeadOfChain;
3557 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3560 * Destroy the temporary propertyless big block chain.
3561 * Create a new big block chain associated with this property.
3563 BlockChainStream_Destroy(bbTempChain);
3564 bigBlockChain = BlockChainStream_Construct(This,
3568 return bigBlockChain;
3571 void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3573 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3575 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3576 HeapFree(GetProcessHeap(), 0, This);
3579 /******************************************************************************
3581 ** Storage32InternalImpl_Commit
3583 ** The non-root storages cannot be opened in transacted mode thus this function
3586 static HRESULT WINAPI StorageInternalImpl_Commit(
3588 DWORD grfCommitFlags) /* [in] */
3593 /******************************************************************************
3595 ** Storage32InternalImpl_Revert
3597 ** The non-root storages cannot be opened in transacted mode thus this function
3600 static HRESULT WINAPI StorageInternalImpl_Revert(
3606 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3608 IStorage_Release((IStorage*)This->parentStorage);
3609 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3610 HeapFree(GetProcessHeap(), 0, This);
3613 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3614 IEnumSTATSTG* iface,
3618 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3621 * Perform a sanity check on the parameters.
3624 return E_INVALIDARG;
3627 * Initialize the return parameter.
3632 * Compare the riid with the interface IDs implemented by this object.
3634 if (IsEqualGUID(&IID_IUnknown, riid) ||
3635 IsEqualGUID(&IID_IStorage, riid))
3637 *ppvObject = (IEnumSTATSTG*)This;
3638 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3642 return E_NOINTERFACE;
3645 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3646 IEnumSTATSTG* iface)
3648 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3649 return InterlockedIncrement(&This->ref);
3652 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3653 IEnumSTATSTG* iface)
3655 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3659 newRef = InterlockedDecrement(&This->ref);
3662 * If the reference count goes down to 0, perform suicide.
3666 IEnumSTATSTGImpl_Destroy(This);
3672 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3673 IEnumSTATSTG* iface,
3676 ULONG* pceltFetched)
3678 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3680 StgProperty currentProperty;
3681 STATSTG* currentReturnStruct = rgelt;
3682 ULONG objectFetched = 0;
3683 ULONG currentSearchNode;
3686 * Perform a sanity check on the parameters.
3688 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3689 return E_INVALIDARG;
3692 * To avoid the special case, get another pointer to a ULONG value if
3693 * the caller didn't supply one.
3695 if (pceltFetched==0)
3696 pceltFetched = &objectFetched;
3699 * Start the iteration, we will iterate until we hit the end of the
3700 * linked list or until we hit the number of items to iterate through
3705 * Start with the node at the top of the stack.
3707 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3709 while ( ( *pceltFetched < celt) &&
3710 ( currentSearchNode!=PROPERTY_NULL) )
3713 * Remove the top node from the stack
3715 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3718 * Read the property from the storage.
3720 StorageImpl_ReadProperty(This->parentStorage,
3725 * Copy the information to the return buffer.
3727 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3732 * Step to the next item in the iteration
3735 currentReturnStruct++;
3738 * Push the next search node in the search stack.
3740 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3743 * continue the iteration.
3745 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3748 if (*pceltFetched == celt)
3755 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3756 IEnumSTATSTG* iface,
3759 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3761 StgProperty currentProperty;
3762 ULONG objectFetched = 0;
3763 ULONG currentSearchNode;
3766 * Start with the node at the top of the stack.
3768 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3770 while ( (objectFetched < celt) &&
3771 (currentSearchNode!=PROPERTY_NULL) )
3774 * Remove the top node from the stack
3776 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3779 * Read the property from the storage.
3781 StorageImpl_ReadProperty(This->parentStorage,
3786 * Step to the next item in the iteration
3791 * Push the next search node in the search stack.
3793 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3796 * continue the iteration.
3798 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3801 if (objectFetched == celt)
3807 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3808 IEnumSTATSTG* iface)
3810 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3812 StgProperty rootProperty;
3813 BOOL readSuccessful;
3816 * Re-initialize the search stack to an empty stack
3818 This->stackSize = 0;
3821 * Read the root property from the storage.
3823 readSuccessful = StorageImpl_ReadProperty(
3824 This->parentStorage,
3825 This->firstPropertyNode,
3830 assert(rootProperty.sizeOfNameString!=0);
3833 * Push the search node in the search stack.
3835 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3841 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3842 IEnumSTATSTG* iface,
3843 IEnumSTATSTG** ppenum)
3845 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3847 IEnumSTATSTGImpl* newClone;
3850 * Perform a sanity check on the parameters.
3853 return E_INVALIDARG;
3855 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3856 This->firstPropertyNode);
3860 * The new clone enumeration must point to the same current node as
3863 newClone->stackSize = This->stackSize ;
3864 newClone->stackMaxSize = This->stackMaxSize ;
3865 newClone->stackToVisit =
3866 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3869 newClone->stackToVisit,
3871 sizeof(ULONG) * newClone->stackSize);
3873 *ppenum = (IEnumSTATSTG*)newClone;
3876 * Don't forget to nail down a reference to the clone before
3879 IEnumSTATSTGImpl_AddRef(*ppenum);
3884 INT IEnumSTATSTGImpl_FindParentProperty(
3885 IEnumSTATSTGImpl *This,
3886 ULONG childProperty,
3887 StgProperty *currentProperty,
3890 ULONG currentSearchNode;
3894 * To avoid the special case, get another pointer to a ULONG value if
3895 * the caller didn't supply one.
3898 thisNodeId = &foundNode;
3901 * Start with the node at the top of the stack.
3903 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3906 while (currentSearchNode!=PROPERTY_NULL)
3909 * Store the current node in the returned parameters
3911 *thisNodeId = currentSearchNode;
3914 * Remove the top node from the stack
3916 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3919 * Read the property from the storage.
3921 StorageImpl_ReadProperty(
3922 This->parentStorage,
3926 if (currentProperty->previousProperty == childProperty)
3927 return PROPERTY_RELATION_PREVIOUS;
3929 else if (currentProperty->nextProperty == childProperty)
3930 return PROPERTY_RELATION_NEXT;
3932 else if (currentProperty->dirProperty == childProperty)
3933 return PROPERTY_RELATION_DIR;
3936 * Push the next search node in the search stack.
3938 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3941 * continue the iteration.
3943 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3946 return PROPERTY_NULL;
3949 ULONG IEnumSTATSTGImpl_FindProperty(
3950 IEnumSTATSTGImpl* This,
3951 const OLECHAR* lpszPropName,
3952 StgProperty* currentProperty)
3954 ULONG currentSearchNode;
3957 * Start with the node at the top of the stack.
3959 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3961 while (currentSearchNode!=PROPERTY_NULL)
3964 * Remove the top node from the stack
3966 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3969 * Read the property from the storage.
3971 StorageImpl_ReadProperty(This->parentStorage,
3975 if ( propertyNameCmp(
3976 (const OLECHAR*)currentProperty->name,
3977 (const OLECHAR*)lpszPropName) == 0)
3978 return currentSearchNode;
3981 * Push the next search node in the search stack.
3983 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3986 * continue the iteration.
3988 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3991 return PROPERTY_NULL;
3994 void IEnumSTATSTGImpl_PushSearchNode(
3995 IEnumSTATSTGImpl* This,
3998 StgProperty rootProperty;
3999 BOOL readSuccessful;
4002 * First, make sure we're not trying to push an unexisting node.
4004 if (nodeToPush==PROPERTY_NULL)
4008 * First push the node to the stack
4010 if (This->stackSize == This->stackMaxSize)
4012 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4014 This->stackToVisit = HeapReAlloc(
4018 sizeof(ULONG) * This->stackMaxSize);
4021 This->stackToVisit[This->stackSize] = nodeToPush;
4025 * Read the root property from the storage.
4027 readSuccessful = StorageImpl_ReadProperty(
4028 This->parentStorage,
4034 assert(rootProperty.sizeOfNameString!=0);
4037 * Push the previous search node in the search stack.
4039 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4043 ULONG IEnumSTATSTGImpl_PopSearchNode(
4044 IEnumSTATSTGImpl* This,
4049 if (This->stackSize == 0)
4050 return PROPERTY_NULL;
4052 topNode = This->stackToVisit[This->stackSize-1];
4061 * Virtual function table for the IEnumSTATSTGImpl class.
4063 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4065 IEnumSTATSTGImpl_QueryInterface,
4066 IEnumSTATSTGImpl_AddRef,
4067 IEnumSTATSTGImpl_Release,
4068 IEnumSTATSTGImpl_Next,
4069 IEnumSTATSTGImpl_Skip,
4070 IEnumSTATSTGImpl_Reset,
4071 IEnumSTATSTGImpl_Clone
4074 /******************************************************************************
4075 ** IEnumSTATSTGImpl implementation
4078 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4079 StorageImpl* parentStorage,
4080 ULONG firstPropertyNode)
4082 IEnumSTATSTGImpl* newEnumeration;
4084 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4086 if (newEnumeration!=0)
4089 * Set-up the virtual function table and reference count.
4091 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4092 newEnumeration->ref = 0;
4095 * We want to nail-down the reference to the storage in case the
4096 * enumeration out-lives the storage in the client application.
4098 newEnumeration->parentStorage = parentStorage;
4099 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4101 newEnumeration->firstPropertyNode = firstPropertyNode;
4104 * Initialize the search stack
4106 newEnumeration->stackSize = 0;
4107 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4108 newEnumeration->stackToVisit =
4109 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4112 * Make sure the current node of the iterator is the first one.
4114 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4117 return newEnumeration;
4121 * Virtual function table for the Storage32InternalImpl class.
4123 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4125 StorageBaseImpl_QueryInterface,
4126 StorageBaseImpl_AddRef,
4127 StorageBaseImpl_Release,
4128 StorageBaseImpl_CreateStream,
4129 StorageBaseImpl_OpenStream,
4130 StorageImpl_CreateStorage,
4131 StorageBaseImpl_OpenStorage,
4133 StorageImpl_MoveElementTo,
4134 StorageInternalImpl_Commit,
4135 StorageInternalImpl_Revert,
4136 StorageBaseImpl_EnumElements,
4137 StorageImpl_DestroyElement,
4138 StorageBaseImpl_RenameElement,
4139 StorageImpl_SetElementTimes,
4140 StorageBaseImpl_SetClass,
4141 StorageImpl_SetStateBits,
4142 StorageBaseImpl_Stat
4145 /******************************************************************************
4146 ** Storage32InternalImpl implementation
4149 StorageInternalImpl* StorageInternalImpl_Construct(
4150 StorageImpl* ancestorStorage,
4152 ULONG rootPropertyIndex)
4154 StorageInternalImpl* newStorage;
4157 * Allocate space for the new storage object
4159 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
4163 memset(newStorage, 0, sizeof(StorageInternalImpl));
4166 * Initialize the stream list
4169 list_init(&newStorage->base.strmHead);
4172 * Initialize the virtual function table.
4174 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4175 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4176 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4179 * Keep the ancestor storage pointer and nail a reference to it.
4181 newStorage->base.ancestorStorage = ancestorStorage;
4182 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4185 * Keep the index of the root property set for this storage,
4187 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4195 /******************************************************************************
4196 ** StorageUtl implementation
4199 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4203 memcpy(&tmp, buffer+offset, sizeof(WORD));
4204 *value = le16toh(tmp);
4207 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4209 value = htole16(value);
4210 memcpy(buffer+offset, &value, sizeof(WORD));
4213 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4217 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4218 *value = le32toh(tmp);
4221 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4223 value = htole32(value);
4224 memcpy(buffer+offset, &value, sizeof(DWORD));
4227 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4228 ULARGE_INTEGER* value)
4230 #ifdef WORDS_BIGENDIAN
4233 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4234 value->u.LowPart = htole32(tmp.u.HighPart);
4235 value->u.HighPart = htole32(tmp.u.LowPart);
4237 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4241 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4242 const ULARGE_INTEGER *value)
4244 #ifdef WORDS_BIGENDIAN
4247 tmp.u.LowPart = htole32(value->u.HighPart);
4248 tmp.u.HighPart = htole32(value->u.LowPart);
4249 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4251 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4255 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4257 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4258 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4259 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4261 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4264 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4266 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4267 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4268 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4270 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4273 void StorageUtl_CopyPropertyToSTATSTG(
4274 STATSTG* destination,
4275 StgProperty* source,
4279 * The copy of the string occurs only when the flag is not set
4281 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4282 (source->name == NULL) ||
4283 (source->name[0] == 0) )
4285 destination->pwcsName = 0;
4289 destination->pwcsName =
4290 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4292 strcpyW((LPWSTR)destination->pwcsName, source->name);
4295 switch (source->propertyType)
4297 case PROPTYPE_STORAGE:
4299 destination->type = STGTY_STORAGE;
4301 case PROPTYPE_STREAM:
4302 destination->type = STGTY_STREAM;
4305 destination->type = STGTY_STREAM;
4309 destination->cbSize = source->size;
4311 currentReturnStruct->mtime = {0}; TODO
4312 currentReturnStruct->ctime = {0};
4313 currentReturnStruct->atime = {0};
4315 destination->grfMode = 0;
4316 destination->grfLocksSupported = 0;
4317 destination->clsid = source->propertyUniqueID;
4318 destination->grfStateBits = 0;
4319 destination->reserved = 0;
4322 /******************************************************************************
4323 ** BlockChainStream implementation
4326 BlockChainStream* BlockChainStream_Construct(
4327 StorageImpl* parentStorage,
4328 ULONG* headOfStreamPlaceHolder,
4329 ULONG propertyIndex)
4331 BlockChainStream* newStream;
4334 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4336 newStream->parentStorage = parentStorage;
4337 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4338 newStream->ownerPropertyIndex = propertyIndex;
4339 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4340 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4341 newStream->numBlocks = 0;
4343 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4345 while (blockIndex != BLOCK_END_OF_CHAIN)
4347 newStream->numBlocks++;
4348 newStream->tailIndex = blockIndex;
4350 if(FAILED(StorageImpl_GetNextBlockInChain(
4355 HeapFree(GetProcessHeap(), 0, newStream);
4363 void BlockChainStream_Destroy(BlockChainStream* This)
4365 HeapFree(GetProcessHeap(), 0, This);
4368 /******************************************************************************
4369 * BlockChainStream_GetHeadOfChain
4371 * Returns the head of this stream chain.
4372 * Some special chains don't have properties, their heads are kept in
4373 * This->headOfStreamPlaceHolder.
4376 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4378 StgProperty chainProperty;
4379 BOOL readSuccessful;
4381 if (This->headOfStreamPlaceHolder != 0)
4382 return *(This->headOfStreamPlaceHolder);
4384 if (This->ownerPropertyIndex != PROPERTY_NULL)
4386 readSuccessful = StorageImpl_ReadProperty(
4387 This->parentStorage,
4388 This->ownerPropertyIndex,
4393 return chainProperty.startingBlock;
4397 return BLOCK_END_OF_CHAIN;
4400 /******************************************************************************
4401 * BlockChainStream_GetCount
4403 * Returns the number of blocks that comprises this chain.
4404 * This is not the size of the stream as the last block may not be full!
4407 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4412 blockIndex = BlockChainStream_GetHeadOfChain(This);
4414 while (blockIndex != BLOCK_END_OF_CHAIN)
4418 if(FAILED(StorageImpl_GetNextBlockInChain(
4419 This->parentStorage,
4428 /******************************************************************************
4429 * BlockChainStream_ReadAt
4431 * Reads a specified number of bytes from this chain at the specified offset.
4432 * bytesRead may be NULL.
4433 * Failure will be returned if the specified number of bytes has not been read.
4435 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4436 ULARGE_INTEGER offset,
4441 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4442 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4443 ULONG bytesToReadInBuffer;
4446 BYTE* bigBlockBuffer;
4449 * Find the first block in the stream that contains part of the buffer.
4451 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4452 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4453 (blockNoInSequence < This->lastBlockNoInSequence) )
4455 blockIndex = BlockChainStream_GetHeadOfChain(This);
4456 This->lastBlockNoInSequence = blockNoInSequence;
4460 ULONG temp = blockNoInSequence;
4462 blockIndex = This->lastBlockNoInSequenceIndex;
4463 blockNoInSequence -= This->lastBlockNoInSequence;
4464 This->lastBlockNoInSequence = temp;
4467 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4469 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4471 blockNoInSequence--;
4474 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4475 return FALSE; /* We failed to find the starting block */
4477 This->lastBlockNoInSequenceIndex = blockIndex;
4480 * Start reading the buffer.
4483 bufferWalker = buffer;
4485 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4488 * Calculate how many bytes we can copy from this big block.
4490 bytesToReadInBuffer =
4491 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4494 * Copy those bytes to the buffer
4497 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4498 if (!bigBlockBuffer)
4501 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4503 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4506 * Step to the next big block.
4508 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4511 bufferWalker += bytesToReadInBuffer;
4512 size -= bytesToReadInBuffer;
4513 *bytesRead += bytesToReadInBuffer;
4514 offsetInBlock = 0; /* There is no offset on the next block */
4521 /******************************************************************************
4522 * BlockChainStream_WriteAt
4524 * Writes the specified number of bytes to this chain at the specified offset.
4525 * bytesWritten may be NULL.
4526 * Will fail if not all specified number of bytes have been written.
4528 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4529 ULARGE_INTEGER offset,
4532 ULONG* bytesWritten)
4534 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4535 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4538 const BYTE* bufferWalker;
4539 BYTE* bigBlockBuffer;
4542 * Find the first block in the stream that contains part of the buffer.
4544 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4545 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4546 (blockNoInSequence < This->lastBlockNoInSequence) )
4548 blockIndex = BlockChainStream_GetHeadOfChain(This);
4549 This->lastBlockNoInSequence = blockNoInSequence;
4553 ULONG temp = blockNoInSequence;
4555 blockIndex = This->lastBlockNoInSequenceIndex;
4556 blockNoInSequence -= This->lastBlockNoInSequence;
4557 This->lastBlockNoInSequence = temp;
4560 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4562 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4565 blockNoInSequence--;
4568 This->lastBlockNoInSequenceIndex = blockIndex;
4571 * Here, I'm casting away the constness on the buffer variable
4572 * This is OK since we don't intend to modify that buffer.
4575 bufferWalker = (const BYTE*)buffer;
4577 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4580 * Calculate how many bytes we can copy from this big block.
4583 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4586 * Copy those bytes to the buffer
4588 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4590 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4592 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4595 * Step to the next big block.
4597 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4600 bufferWalker += bytesToWrite;
4601 size -= bytesToWrite;
4602 *bytesWritten += bytesToWrite;
4603 offsetInBlock = 0; /* There is no offset on the next block */
4609 /******************************************************************************
4610 * BlockChainStream_Shrink
4612 * Shrinks this chain in the big block depot.
4614 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4615 ULARGE_INTEGER newSize)
4617 ULONG blockIndex, extraBlock;
4622 * Reset the last accessed block cache.
4624 This->lastBlockNoInSequence = 0xFFFFFFFF;
4625 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4628 * Figure out how many blocks are needed to contain the new size
4630 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4632 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4635 blockIndex = BlockChainStream_GetHeadOfChain(This);
4638 * Go to the new end of chain
4640 while (count < numBlocks)
4642 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4648 /* Get the next block before marking the new end */
4649 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4653 /* Mark the new end of chain */
4654 StorageImpl_SetNextBlockInChain(
4655 This->parentStorage,
4657 BLOCK_END_OF_CHAIN);
4659 This->tailIndex = blockIndex;
4660 This->numBlocks = numBlocks;
4663 * Mark the extra blocks as free
4665 while (extraBlock != BLOCK_END_OF_CHAIN)
4667 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4670 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4671 extraBlock = blockIndex;
4677 /******************************************************************************
4678 * BlockChainStream_Enlarge
4680 * Grows this chain in the big block depot.
4682 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4683 ULARGE_INTEGER newSize)
4685 ULONG blockIndex, currentBlock;
4687 ULONG oldNumBlocks = 0;
4689 blockIndex = BlockChainStream_GetHeadOfChain(This);
4692 * Empty chain. Create the head.
4694 if (blockIndex == BLOCK_END_OF_CHAIN)
4696 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4697 StorageImpl_SetNextBlockInChain(This->parentStorage,
4699 BLOCK_END_OF_CHAIN);
4701 if (This->headOfStreamPlaceHolder != 0)
4703 *(This->headOfStreamPlaceHolder) = blockIndex;
4707 StgProperty chainProp;
4708 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4710 StorageImpl_ReadProperty(
4711 This->parentStorage,
4712 This->ownerPropertyIndex,
4715 chainProp.startingBlock = blockIndex;
4717 StorageImpl_WriteProperty(
4718 This->parentStorage,
4719 This->ownerPropertyIndex,
4723 This->tailIndex = blockIndex;
4724 This->numBlocks = 1;
4728 * Figure out how many blocks are needed to contain this stream
4730 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4732 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4736 * Go to the current end of chain
4738 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4740 currentBlock = blockIndex;
4742 while (blockIndex != BLOCK_END_OF_CHAIN)
4745 currentBlock = blockIndex;
4747 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4752 This->tailIndex = currentBlock;
4755 currentBlock = This->tailIndex;
4756 oldNumBlocks = This->numBlocks;
4759 * Add new blocks to the chain
4761 if (oldNumBlocks < newNumBlocks)
4763 while (oldNumBlocks < newNumBlocks)
4765 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4767 StorageImpl_SetNextBlockInChain(
4768 This->parentStorage,
4772 StorageImpl_SetNextBlockInChain(
4773 This->parentStorage,
4775 BLOCK_END_OF_CHAIN);
4777 currentBlock = blockIndex;
4781 This->tailIndex = blockIndex;
4782 This->numBlocks = newNumBlocks;
4788 /******************************************************************************
4789 * BlockChainStream_SetSize
4791 * Sets the size of this stream. The big block depot will be updated.
4792 * The file will grow if we grow the chain.
4794 * TODO: Free the actual blocks in the file when we shrink the chain.
4795 * Currently, the blocks are still in the file. So the file size
4796 * doesn't shrink even if we shrink streams.
4798 BOOL BlockChainStream_SetSize(
4799 BlockChainStream* This,
4800 ULARGE_INTEGER newSize)
4802 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4804 if (newSize.u.LowPart == size.u.LowPart)
4807 if (newSize.u.LowPart < size.u.LowPart)
4809 BlockChainStream_Shrink(This, newSize);
4813 BlockChainStream_Enlarge(This, newSize);
4819 /******************************************************************************
4820 * BlockChainStream_GetSize
4822 * Returns the size of this chain.
4823 * Will return the block count if this chain doesn't have a property.
4825 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4827 StgProperty chainProperty;
4829 if(This->headOfStreamPlaceHolder == NULL)
4832 * This chain is a data stream read the property and return
4833 * the appropriate size
4835 StorageImpl_ReadProperty(
4836 This->parentStorage,
4837 This->ownerPropertyIndex,
4840 return chainProperty.size;
4845 * this chain is a chain that does not have a property, figure out the
4846 * size by making the product number of used blocks times the
4849 ULARGE_INTEGER result;
4850 result.u.HighPart = 0;
4853 BlockChainStream_GetCount(This) *
4854 This->parentStorage->bigBlockSize;
4860 /******************************************************************************
4861 ** SmallBlockChainStream implementation
4864 SmallBlockChainStream* SmallBlockChainStream_Construct(
4865 StorageImpl* parentStorage,
4866 ULONG propertyIndex)
4868 SmallBlockChainStream* newStream;
4870 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4872 newStream->parentStorage = parentStorage;
4873 newStream->ownerPropertyIndex = propertyIndex;
4878 void SmallBlockChainStream_Destroy(
4879 SmallBlockChainStream* This)
4881 HeapFree(GetProcessHeap(), 0, This);
4884 /******************************************************************************
4885 * SmallBlockChainStream_GetHeadOfChain
4887 * Returns the head of this chain of small blocks.
4889 ULONG SmallBlockChainStream_GetHeadOfChain(
4890 SmallBlockChainStream* This)
4892 StgProperty chainProperty;
4893 BOOL readSuccessful;
4895 if (This->ownerPropertyIndex)
4897 readSuccessful = StorageImpl_ReadProperty(
4898 This->parentStorage,
4899 This->ownerPropertyIndex,
4904 return chainProperty.startingBlock;
4909 return BLOCK_END_OF_CHAIN;
4912 /******************************************************************************
4913 * SmallBlockChainStream_GetNextBlockInChain
4915 * Returns the index of the next small block in this chain.
4918 * - BLOCK_END_OF_CHAIN: end of this chain
4919 * - BLOCK_UNUSED: small block 'blockIndex' is free
4921 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4922 SmallBlockChainStream* This,
4924 ULONG* nextBlockInChain)
4926 ULARGE_INTEGER offsetOfBlockInDepot;
4931 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4933 offsetOfBlockInDepot.u.HighPart = 0;
4934 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4937 * Read those bytes in the buffer from the small block file.
4939 success = BlockChainStream_ReadAt(
4940 This->parentStorage->smallBlockDepotChain,
4941 offsetOfBlockInDepot,
4948 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4952 return STG_E_READFAULT;
4955 /******************************************************************************
4956 * SmallBlockChainStream_SetNextBlockInChain
4958 * Writes the index of the next block of the specified block in the small
4960 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4961 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4963 void SmallBlockChainStream_SetNextBlockInChain(
4964 SmallBlockChainStream* This,
4968 ULARGE_INTEGER offsetOfBlockInDepot;
4972 offsetOfBlockInDepot.u.HighPart = 0;
4973 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4975 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4978 * Read those bytes in the buffer from the small block file.
4980 BlockChainStream_WriteAt(
4981 This->parentStorage->smallBlockDepotChain,
4982 offsetOfBlockInDepot,
4988 /******************************************************************************
4989 * SmallBlockChainStream_FreeBlock
4991 * Flag small block 'blockIndex' as free in the small block depot.
4993 void SmallBlockChainStream_FreeBlock(
4994 SmallBlockChainStream* This,
4997 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5000 /******************************************************************************
5001 * SmallBlockChainStream_GetNextFreeBlock
5003 * Returns the index of a free small block. The small block depot will be
5004 * enlarged if necessary. The small block chain will also be enlarged if
5007 ULONG SmallBlockChainStream_GetNextFreeBlock(
5008 SmallBlockChainStream* This)
5010 ULARGE_INTEGER offsetOfBlockInDepot;
5013 ULONG blockIndex = 0;
5014 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5015 BOOL success = TRUE;
5016 ULONG smallBlocksPerBigBlock;
5018 offsetOfBlockInDepot.u.HighPart = 0;
5021 * Scan the small block depot for a free block
5023 while (nextBlockIndex != BLOCK_UNUSED)
5025 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5027 success = BlockChainStream_ReadAt(
5028 This->parentStorage->smallBlockDepotChain,
5029 offsetOfBlockInDepot,
5035 * If we run out of space for the small block depot, enlarge it
5039 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5041 if (nextBlockIndex != BLOCK_UNUSED)
5047 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5049 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5050 ULONG nextBlock, newsbdIndex;
5051 BYTE* smallBlockDepot;
5053 nextBlock = sbdIndex;
5054 while (nextBlock != BLOCK_END_OF_CHAIN)
5056 sbdIndex = nextBlock;
5057 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5060 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5061 if (sbdIndex != BLOCK_END_OF_CHAIN)
5062 StorageImpl_SetNextBlockInChain(
5063 This->parentStorage,
5067 StorageImpl_SetNextBlockInChain(
5068 This->parentStorage,
5070 BLOCK_END_OF_CHAIN);
5073 * Initialize all the small blocks to free
5076 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
5078 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5079 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
5084 * We have just created the small block depot.
5086 StgProperty rootProp;
5090 * Save it in the header
5092 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5093 StorageImpl_SaveFileHeader(This->parentStorage);
5096 * And allocate the first big block that will contain small blocks
5099 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5101 StorageImpl_SetNextBlockInChain(
5102 This->parentStorage,
5104 BLOCK_END_OF_CHAIN);
5106 StorageImpl_ReadProperty(
5107 This->parentStorage,
5108 This->parentStorage->base.rootPropertySetIndex,
5111 rootProp.startingBlock = sbStartIndex;
5112 rootProp.size.u.HighPart = 0;
5113 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5115 StorageImpl_WriteProperty(
5116 This->parentStorage,
5117 This->parentStorage->base.rootPropertySetIndex,
5123 smallBlocksPerBigBlock =
5124 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5127 * Verify if we have to allocate big blocks to contain small blocks
5129 if (blockIndex % smallBlocksPerBigBlock == 0)
5131 StgProperty rootProp;
5132 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5134 StorageImpl_ReadProperty(
5135 This->parentStorage,
5136 This->parentStorage->base.rootPropertySetIndex,
5139 if (rootProp.size.u.LowPart <
5140 (blocksRequired * This->parentStorage->bigBlockSize))
5142 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5144 BlockChainStream_SetSize(
5145 This->parentStorage->smallBlockRootChain,
5148 StorageImpl_WriteProperty(
5149 This->parentStorage,
5150 This->parentStorage->base.rootPropertySetIndex,
5158 /******************************************************************************
5159 * SmallBlockChainStream_ReadAt
5161 * Reads a specified number of bytes from this chain at the specified offset.
5162 * bytesRead may be NULL.
5163 * Failure will be returned if the specified number of bytes has not been read.
5165 HRESULT SmallBlockChainStream_ReadAt(
5166 SmallBlockChainStream* This,
5167 ULARGE_INTEGER offset,
5173 ULARGE_INTEGER offsetInBigBlockFile;
5174 ULONG blockNoInSequence =
5175 offset.u.LowPart / This->parentStorage->smallBlockSize;
5177 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5178 ULONG bytesToReadInBuffer;
5180 ULONG bytesReadFromBigBlockFile;
5184 * This should never happen on a small block file.
5186 assert(offset.u.HighPart==0);
5189 * Find the first block in the stream that contains part of the buffer.
5191 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5193 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5195 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5198 blockNoInSequence--;
5202 * Start reading the buffer.
5205 bufferWalker = buffer;
5207 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5210 * Calculate how many bytes we can copy from this small block.
5212 bytesToReadInBuffer =
5213 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5216 * Calculate the offset of the small block in the small block file.
5218 offsetInBigBlockFile.u.HighPart = 0;
5219 offsetInBigBlockFile.u.LowPart =
5220 blockIndex * This->parentStorage->smallBlockSize;
5222 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5225 * Read those bytes in the buffer from the small block file.
5226 * The small block has already been identified so it shouldn't fail
5227 * unless the file is corrupt.
5229 if (!BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5230 offsetInBigBlockFile,
5231 bytesToReadInBuffer,
5233 &bytesReadFromBigBlockFile))
5234 return STG_E_DOCFILECORRUPT;
5236 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5239 * Step to the next big block.
5241 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5245 bufferWalker += bytesToReadInBuffer;
5246 size -= bytesToReadInBuffer;
5247 *bytesRead += bytesToReadInBuffer;
5248 offsetInBlock = 0; /* There is no offset on the next block */
5254 /******************************************************************************
5255 * SmallBlockChainStream_WriteAt
5257 * Writes the specified number of bytes to this chain at the specified offset.
5258 * bytesWritten may be NULL.
5259 * Will fail if not all specified number of bytes have been written.
5261 BOOL SmallBlockChainStream_WriteAt(
5262 SmallBlockChainStream* This,
5263 ULARGE_INTEGER offset,
5266 ULONG* bytesWritten)
5268 ULARGE_INTEGER offsetInBigBlockFile;
5269 ULONG blockNoInSequence =
5270 offset.u.LowPart / This->parentStorage->smallBlockSize;
5272 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5273 ULONG bytesToWriteInBuffer;
5275 ULONG bytesWrittenFromBigBlockFile;
5276 const BYTE* bufferWalker;
5279 * This should never happen on a small block file.
5281 assert(offset.u.HighPart==0);
5284 * Find the first block in the stream that contains part of the buffer.
5286 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5288 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5290 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5292 blockNoInSequence--;
5296 * Start writing the buffer.
5298 * Here, I'm casting away the constness on the buffer variable
5299 * This is OK since we don't intend to modify that buffer.
5302 bufferWalker = (const BYTE*)buffer;
5303 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5306 * Calculate how many bytes we can copy to this small block.
5308 bytesToWriteInBuffer =
5309 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5312 * Calculate the offset of the small block in the small block file.
5314 offsetInBigBlockFile.u.HighPart = 0;
5315 offsetInBigBlockFile.u.LowPart =
5316 blockIndex * This->parentStorage->smallBlockSize;
5318 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5321 * Write those bytes in the buffer to the small block file.
5323 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5324 offsetInBigBlockFile,
5325 bytesToWriteInBuffer,
5327 &bytesWrittenFromBigBlockFile);
5329 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5332 * Step to the next big block.
5334 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5337 bufferWalker += bytesToWriteInBuffer;
5338 size -= bytesToWriteInBuffer;
5339 *bytesWritten += bytesToWriteInBuffer;
5340 offsetInBlock = 0; /* There is no offset on the next block */
5346 /******************************************************************************
5347 * SmallBlockChainStream_Shrink
5349 * Shrinks this chain in the small block depot.
5351 BOOL SmallBlockChainStream_Shrink(
5352 SmallBlockChainStream* This,
5353 ULARGE_INTEGER newSize)
5355 ULONG blockIndex, extraBlock;
5359 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5361 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5364 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5367 * Go to the new end of chain
5369 while (count < numBlocks)
5371 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5378 * If the count is 0, we have a special case, the head of the chain was
5383 StgProperty chainProp;
5385 StorageImpl_ReadProperty(This->parentStorage,
5386 This->ownerPropertyIndex,
5389 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5391 StorageImpl_WriteProperty(This->parentStorage,
5392 This->ownerPropertyIndex,
5396 * We start freeing the chain at the head block.
5398 extraBlock = blockIndex;
5402 /* Get the next block before marking the new end */
5403 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5407 /* Mark the new end of chain */
5408 SmallBlockChainStream_SetNextBlockInChain(
5411 BLOCK_END_OF_CHAIN);
5415 * Mark the extra blocks as free
5417 while (extraBlock != BLOCK_END_OF_CHAIN)
5419 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5422 SmallBlockChainStream_FreeBlock(This, extraBlock);
5423 extraBlock = blockIndex;
5429 /******************************************************************************
5430 * SmallBlockChainStream_Enlarge
5432 * Grows this chain in the small block depot.
5434 BOOL SmallBlockChainStream_Enlarge(
5435 SmallBlockChainStream* This,
5436 ULARGE_INTEGER newSize)
5438 ULONG blockIndex, currentBlock;
5440 ULONG oldNumBlocks = 0;
5442 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5447 if (blockIndex == BLOCK_END_OF_CHAIN)
5450 StgProperty chainProp;
5452 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5455 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5457 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5460 blockIndex = chainProp.startingBlock;
5461 SmallBlockChainStream_SetNextBlockInChain(
5464 BLOCK_END_OF_CHAIN);
5467 currentBlock = blockIndex;
5470 * Figure out how many blocks are needed to contain this stream
5472 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5474 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5478 * Go to the current end of chain
5480 while (blockIndex != BLOCK_END_OF_CHAIN)
5483 currentBlock = blockIndex;
5484 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5489 * Add new blocks to the chain
5491 while (oldNumBlocks < newNumBlocks)
5493 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5494 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5496 SmallBlockChainStream_SetNextBlockInChain(
5499 BLOCK_END_OF_CHAIN);
5501 currentBlock = blockIndex;
5508 /******************************************************************************
5509 * SmallBlockChainStream_GetCount
5511 * Returns the number of blocks that comprises this chain.
5512 * This is not the size of this chain as the last block may not be full!
5514 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5519 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5521 while (blockIndex != BLOCK_END_OF_CHAIN)
5525 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5532 /******************************************************************************
5533 * SmallBlockChainStream_SetSize
5535 * Sets the size of this stream.
5536 * The file will grow if we grow the chain.
5538 * TODO: Free the actual blocks in the file when we shrink the chain.
5539 * Currently, the blocks are still in the file. So the file size
5540 * doesn't shrink even if we shrink streams.
5542 BOOL SmallBlockChainStream_SetSize(
5543 SmallBlockChainStream* This,
5544 ULARGE_INTEGER newSize)
5546 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5548 if (newSize.u.LowPart == size.u.LowPart)
5551 if (newSize.u.LowPart < size.u.LowPart)
5553 SmallBlockChainStream_Shrink(This, newSize);
5557 SmallBlockChainStream_Enlarge(This, newSize);
5563 /******************************************************************************
5564 * SmallBlockChainStream_GetSize
5566 * Returns the size of this chain.
5568 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5570 StgProperty chainProperty;
5572 StorageImpl_ReadProperty(
5573 This->parentStorage,
5574 This->ownerPropertyIndex,
5577 return chainProperty.size;
5580 /******************************************************************************
5581 * StgCreateDocfile [OLE32.@]
5582 * Creates a new compound file storage object
5585 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5586 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5587 * reserved [ ?] unused?, usually 0
5588 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5591 * S_OK if the file was successfully created
5592 * some STG_E_ value if error
5594 * if pwcsName is NULL, create file with new unique name
5595 * the function can returns
5596 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5599 HRESULT WINAPI StgCreateDocfile(
5603 IStorage **ppstgOpen)
5605 StorageImpl* newStorage = 0;
5606 HANDLE hFile = INVALID_HANDLE_VALUE;
5607 HRESULT hr = STG_E_INVALIDFLAG;
5611 DWORD fileAttributes;
5612 WCHAR tempFileName[MAX_PATH];
5614 TRACE("(%s, %lx, %ld, %p)\n",
5615 debugstr_w(pwcsName), grfMode,
5616 reserved, ppstgOpen);
5619 * Validate the parameters
5622 return STG_E_INVALIDPOINTER;
5624 return STG_E_INVALIDPARAMETER;
5627 * Validate the STGM flags
5629 if ( FAILED( validateSTGM(grfMode) ))
5632 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5633 switch(STGM_ACCESS_MODE(grfMode))
5636 case STGM_READWRITE:
5642 /* if no share mode given then DENY_NONE is the default */
5643 if (STGM_SHARE_MODE(grfMode) == 0)
5644 grfMode |= STGM_SHARE_DENY_NONE;
5646 /* must have at least one access mode */
5647 if (STGM_ACCESS_MODE(grfMode) == 0)
5650 /* in direct mode, can only use SHARE_EXCLUSIVE */
5651 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5654 /* but in transacted mode, any share mode is valid */
5657 * Generate a unique name.
5661 WCHAR tempPath[MAX_PATH];
5662 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5664 memset(tempPath, 0, sizeof(tempPath));
5665 memset(tempFileName, 0, sizeof(tempFileName));
5667 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5670 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5671 pwcsName = tempFileName;
5674 hr = STG_E_INSUFFICIENTMEMORY;
5678 creationMode = TRUNCATE_EXISTING;
5682 creationMode = GetCreationModeFromSTGM(grfMode);
5686 * Interpret the STGM value grfMode
5688 shareMode = GetShareModeFromSTGM(grfMode);
5689 accessMode = GetAccessModeFromSTGM(grfMode);
5691 if (grfMode & STGM_DELETEONRELEASE)
5692 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5694 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5696 if (grfMode & STGM_TRANSACTED)
5697 FIXME("Transacted mode not implemented.\n");
5700 * Initialize the "out" parameter.
5704 hFile = CreateFileW(pwcsName,
5712 if (hFile == INVALID_HANDLE_VALUE)
5714 if(GetLastError() == ERROR_FILE_EXISTS)
5715 hr = STG_E_FILEALREADYEXISTS;
5722 * Allocate and initialize the new IStorage32object.
5724 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5726 if (newStorage == 0)
5728 hr = STG_E_INSUFFICIENTMEMORY;
5732 hr = StorageImpl_Construct(
5743 HeapFree(GetProcessHeap(), 0, newStorage);
5748 * Get an "out" pointer for the caller.
5750 hr = StorageBaseImpl_QueryInterface(
5751 (IStorage*)newStorage,
5752 (REFIID)&IID_IStorage,
5755 TRACE("<-- %p r = %08lx\n", *ppstgOpen, hr);
5760 /******************************************************************************
5761 * StgCreateStorageEx [OLE32.@]
5763 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5765 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5766 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5768 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5770 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5771 return STG_E_INVALIDPARAMETER;
5774 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5776 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5777 return STG_E_INVALIDPARAMETER;
5780 if (stgfmt == STGFMT_FILE)
5782 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5783 return STG_E_INVALIDPARAMETER;
5786 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5788 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5789 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5792 ERR("Invalid stgfmt argument\n");
5793 return STG_E_INVALIDPARAMETER;
5796 /******************************************************************************
5797 * StgCreatePropSetStg [OLE32.@]
5799 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5800 IPropertySetStorage **ppPropSetStg)
5804 TRACE("(%p, 0x%lx, %p)\n", pstg, reserved, ppPropSetStg);
5806 hr = STG_E_INVALIDPARAMETER;
5808 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5809 (void**)ppPropSetStg);
5813 /******************************************************************************
5814 * StgOpenStorageEx [OLE32.@]
5816 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5818 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5819 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5821 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5823 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5824 return STG_E_INVALIDPARAMETER;
5830 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5831 return STG_E_INVALIDPARAMETER;
5833 case STGFMT_STORAGE:
5836 case STGFMT_DOCFILE:
5837 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5839 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5840 return STG_E_INVALIDPARAMETER;
5842 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5846 WARN("STGFMT_ANY assuming storage\n");
5850 return STG_E_INVALIDPARAMETER;
5853 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5857 /******************************************************************************
5858 * StgOpenStorage [OLE32.@]
5860 HRESULT WINAPI StgOpenStorage(
5861 const OLECHAR *pwcsName,
5862 IStorage *pstgPriority,
5866 IStorage **ppstgOpen)
5868 StorageImpl* newStorage = 0;
5873 WCHAR fullname[MAX_PATH];
5876 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5877 debugstr_w(pwcsName), pstgPriority, grfMode,
5878 snbExclude, reserved, ppstgOpen);
5881 * Perform sanity checks
5885 hr = STG_E_INVALIDNAME;
5891 hr = STG_E_INVALIDPOINTER;
5897 hr = STG_E_INVALIDPARAMETER;
5901 if (grfMode & STGM_PRIORITY)
5903 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5904 return STG_E_INVALIDFLAG;
5905 if (grfMode & STGM_DELETEONRELEASE)
5906 return STG_E_INVALIDFUNCTION;
5907 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5908 return STG_E_INVALIDFLAG;
5909 grfMode &= ~0xf0; /* remove the existing sharing mode */
5910 grfMode |= STGM_SHARE_DENY_NONE;
5912 /* STGM_PRIORITY stops other IStorage objects on the same file from
5913 * committing until the STGM_PRIORITY IStorage is closed. it also
5914 * stops non-transacted mode StgOpenStorage calls with write access from
5915 * succeeding. obviously, both of these cannot be achieved through just
5916 * file share flags */
5917 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5921 * Validate the sharing mode
5923 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5924 switch(STGM_SHARE_MODE(grfMode))
5926 case STGM_SHARE_EXCLUSIVE:
5927 case STGM_SHARE_DENY_WRITE:
5930 hr = STG_E_INVALIDFLAG;
5935 * Validate the STGM flags
5937 if ( FAILED( validateSTGM(grfMode) ) ||
5938 (grfMode&STGM_CREATE))
5940 hr = STG_E_INVALIDFLAG;
5944 /* shared reading requires transacted mode */
5945 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5946 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5947 !(grfMode&STGM_TRANSACTED) )
5949 hr = STG_E_INVALIDFLAG;
5954 * Interpret the STGM value grfMode
5956 shareMode = GetShareModeFromSTGM(grfMode);
5957 accessMode = GetAccessModeFromSTGM(grfMode);
5960 * Initialize the "out" parameter.
5964 hFile = CreateFileW( pwcsName,
5969 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5972 if (hFile==INVALID_HANDLE_VALUE)
5974 DWORD last_error = GetLastError();
5980 case ERROR_FILE_NOT_FOUND:
5981 hr = STG_E_FILENOTFOUND;
5984 case ERROR_PATH_NOT_FOUND:
5985 hr = STG_E_PATHNOTFOUND;
5988 case ERROR_ACCESS_DENIED:
5989 case ERROR_WRITE_PROTECT:
5990 hr = STG_E_ACCESSDENIED;
5993 case ERROR_SHARING_VIOLATION:
5994 hr = STG_E_SHAREVIOLATION;
6005 * Refuse to open the file if it's too small to be a structured storage file
6006 * FIXME: verify the file when reading instead of here
6008 length = GetFileSize(hFile, NULL);
6012 hr = STG_E_FILEALREADYEXISTS;
6017 * Allocate and initialize the new IStorage32object.
6019 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6021 if (newStorage == 0)
6023 hr = STG_E_INSUFFICIENTMEMORY;
6027 /* if the file's length was zero, initialize the storage */
6028 hr = StorageImpl_Construct(
6039 HeapFree(GetProcessHeap(), 0, newStorage);
6041 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6043 if(hr == STG_E_INVALIDHEADER)
6044 hr = STG_E_FILEALREADYEXISTS;
6048 /* prepare the file name string given in lieu of the root property name */
6049 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6050 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6051 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6054 * Get an "out" pointer for the caller.
6056 hr = StorageBaseImpl_QueryInterface(
6057 (IStorage*)newStorage,
6058 (REFIID)&IID_IStorage,
6062 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6066 /******************************************************************************
6067 * StgCreateDocfileOnILockBytes [OLE32.@]
6069 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6073 IStorage** ppstgOpen)
6075 StorageImpl* newStorage = 0;
6079 * Validate the parameters
6081 if ((ppstgOpen == 0) || (plkbyt == 0))
6082 return STG_E_INVALIDPOINTER;
6085 * Allocate and initialize the new IStorage object.
6087 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6089 if (newStorage == 0)
6090 return STG_E_INSUFFICIENTMEMORY;
6092 hr = StorageImpl_Construct(
6103 HeapFree(GetProcessHeap(), 0, newStorage);
6108 * Get an "out" pointer for the caller.
6110 hr = StorageBaseImpl_QueryInterface(
6111 (IStorage*)newStorage,
6112 (REFIID)&IID_IStorage,
6118 /******************************************************************************
6119 * StgOpenStorageOnILockBytes [OLE32.@]
6121 HRESULT WINAPI StgOpenStorageOnILockBytes(
6123 IStorage *pstgPriority,
6127 IStorage **ppstgOpen)
6129 StorageImpl* newStorage = 0;
6133 * Perform a sanity check
6135 if ((plkbyt == 0) || (ppstgOpen == 0))
6136 return STG_E_INVALIDPOINTER;
6139 * Validate the STGM flags
6141 if ( FAILED( validateSTGM(grfMode) ))
6142 return STG_E_INVALIDFLAG;
6145 * Initialize the "out" parameter.
6150 * Allocate and initialize the new IStorage object.
6152 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6154 if (newStorage == 0)
6155 return STG_E_INSUFFICIENTMEMORY;
6157 hr = StorageImpl_Construct(
6168 HeapFree(GetProcessHeap(), 0, newStorage);
6173 * Get an "out" pointer for the caller.
6175 hr = StorageBaseImpl_QueryInterface(
6176 (IStorage*)newStorage,
6177 (REFIID)&IID_IStorage,
6183 /******************************************************************************
6184 * StgSetTimes [ole32.@]
6185 * StgSetTimes [OLE32.@]
6189 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6190 FILETIME const *patime, FILETIME const *pmtime)
6192 IStorage *stg = NULL;
6195 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6197 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6201 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6202 IStorage_Release(stg);
6208 /******************************************************************************
6209 * StgIsStorageILockBytes [OLE32.@]
6211 * Determines if the ILockBytes contains a storage object.
6213 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6216 ULARGE_INTEGER offset;
6218 offset.u.HighPart = 0;
6219 offset.u.LowPart = 0;
6221 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6223 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6229 /******************************************************************************
6230 * WriteClassStg [OLE32.@]
6232 * This method will store the specified CLSID in the specified storage object
6234 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6239 return E_INVALIDARG;
6241 hRes = IStorage_SetClass(pStg, rclsid);
6246 /***********************************************************************
6247 * ReadClassStg (OLE32.@)
6249 * This method reads the CLSID previously written to a storage object with
6250 * the WriteClassStg.
6253 * pstg [I] IStorage pointer
6254 * pclsid [O] Pointer to where the CLSID is written
6258 * Failure: HRESULT code.
6260 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6265 TRACE("(%p, %p)\n", pstg, pclsid);
6267 if(!pstg || !pclsid)
6268 return E_INVALIDARG;
6271 * read a STATSTG structure (contains the clsid) from the storage
6273 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6276 *pclsid=pstatstg.clsid;
6281 /***********************************************************************
6282 * OleLoadFromStream (OLE32.@)
6284 * This function loads an object from stream
6286 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6290 LPPERSISTSTREAM xstm;
6292 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6294 res=ReadClassStm(pStm,&clsid);
6295 if (!SUCCEEDED(res))
6297 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6298 if (!SUCCEEDED(res))
6300 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6301 if (!SUCCEEDED(res)) {
6302 IUnknown_Release((IUnknown*)*ppvObj);
6305 res=IPersistStream_Load(xstm,pStm);
6306 IPersistStream_Release(xstm);
6307 /* FIXME: all refcounts ok at this point? I think they should be:
6310 * xstm : 0 (released)
6315 /***********************************************************************
6316 * OleSaveToStream (OLE32.@)
6318 * This function saves an object with the IPersistStream interface on it
6319 * to the specified stream.
6321 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6327 TRACE("(%p,%p)\n",pPStm,pStm);
6329 res=IPersistStream_GetClassID(pPStm,&clsid);
6331 if (SUCCEEDED(res)){
6333 res=WriteClassStm(pStm,&clsid);
6337 res=IPersistStream_Save(pPStm,pStm,TRUE);
6340 TRACE("Finished Save\n");
6344 /****************************************************************************
6345 * This method validate a STGM parameter that can contain the values below
6347 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6348 * The stgm values contained in 0xffff0000 are bitmasks.
6350 * STGM_DIRECT 0x00000000
6351 * STGM_TRANSACTED 0x00010000
6352 * STGM_SIMPLE 0x08000000
6354 * STGM_READ 0x00000000
6355 * STGM_WRITE 0x00000001
6356 * STGM_READWRITE 0x00000002
6358 * STGM_SHARE_DENY_NONE 0x00000040
6359 * STGM_SHARE_DENY_READ 0x00000030
6360 * STGM_SHARE_DENY_WRITE 0x00000020
6361 * STGM_SHARE_EXCLUSIVE 0x00000010
6363 * STGM_PRIORITY 0x00040000
6364 * STGM_DELETEONRELEASE 0x04000000
6366 * STGM_CREATE 0x00001000
6367 * STGM_CONVERT 0x00020000
6368 * STGM_FAILIFTHERE 0x00000000
6370 * STGM_NOSCRATCH 0x00100000
6371 * STGM_NOSNAPSHOT 0x00200000
6373 static HRESULT validateSTGM(DWORD stgm)
6375 DWORD access = STGM_ACCESS_MODE(stgm);
6376 DWORD share = STGM_SHARE_MODE(stgm);
6377 DWORD create = STGM_CREATE_MODE(stgm);
6379 if (stgm&~STGM_KNOWN_FLAGS)
6381 ERR("unknown flags %08lx\n", stgm);
6389 case STGM_READWRITE:
6397 case STGM_SHARE_DENY_NONE:
6398 case STGM_SHARE_DENY_READ:
6399 case STGM_SHARE_DENY_WRITE:
6400 case STGM_SHARE_EXCLUSIVE:
6409 case STGM_FAILIFTHERE:
6416 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6418 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6422 * STGM_CREATE | STGM_CONVERT
6423 * if both are false, STGM_FAILIFTHERE is set to TRUE
6425 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6429 * STGM_NOSCRATCH requires STGM_TRANSACTED
6431 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6435 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6436 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6438 if ( (stgm & STGM_NOSNAPSHOT) &&
6439 (!(stgm & STGM_TRANSACTED) ||
6440 share == STGM_SHARE_EXCLUSIVE ||
6441 share == STGM_SHARE_DENY_WRITE) )
6447 /****************************************************************************
6448 * GetShareModeFromSTGM
6450 * This method will return a share mode flag from a STGM value.
6451 * The STGM value is assumed valid.
6453 static DWORD GetShareModeFromSTGM(DWORD stgm)
6455 switch (STGM_SHARE_MODE(stgm))
6457 case STGM_SHARE_DENY_NONE:
6458 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6459 case STGM_SHARE_DENY_READ:
6460 return FILE_SHARE_WRITE;
6461 case STGM_SHARE_DENY_WRITE:
6462 return FILE_SHARE_READ;
6463 case STGM_SHARE_EXCLUSIVE:
6466 ERR("Invalid share mode!\n");
6471 /****************************************************************************
6472 * GetAccessModeFromSTGM
6474 * This method will return an access mode flag from a STGM value.
6475 * The STGM value is assumed valid.
6477 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6479 switch (STGM_ACCESS_MODE(stgm))
6482 return GENERIC_READ;
6484 case STGM_READWRITE:
6485 return GENERIC_READ | GENERIC_WRITE;
6487 ERR("Invalid access mode!\n");
6492 /****************************************************************************
6493 * GetCreationModeFromSTGM
6495 * This method will return a creation mode flag from a STGM value.
6496 * The STGM value is assumed valid.
6498 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6500 switch(STGM_CREATE_MODE(stgm))
6503 return CREATE_ALWAYS;
6505 FIXME("STGM_CONVERT not implemented!\n");
6507 case STGM_FAILIFTHERE:
6510 ERR("Invalid create mode!\n");
6516 /*************************************************************************
6517 * OLECONVERT_LoadOLE10 [Internal]
6519 * Loads the OLE10 STREAM to memory
6522 * pOleStream [I] The OLESTREAM
6523 * pData [I] Data Structure for the OLESTREAM Data
6527 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6528 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6531 * This function is used by OleConvertOLESTREAMToIStorage only.
6533 * Memory allocated for pData must be freed by the caller
6535 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6538 HRESULT hRes = S_OK;
6542 pData->pData = NULL;
6543 pData->pstrOleObjFileName = (CHAR *) NULL;
6545 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6548 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6549 if(dwSize != sizeof(pData->dwOleID))
6551 hRes = CONVERT10_E_OLESTREAM_GET;
6553 else if(pData->dwOleID != OLESTREAM_ID)
6555 hRes = CONVERT10_E_OLESTREAM_FMT;
6566 /* Get the TypeID...more info needed for this field */
6567 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6568 if(dwSize != sizeof(pData->dwTypeID))
6570 hRes = CONVERT10_E_OLESTREAM_GET;
6575 if(pData->dwTypeID != 0)
6577 /* Get the length of the OleTypeName */
6578 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6579 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6581 hRes = CONVERT10_E_OLESTREAM_GET;
6586 if(pData->dwOleTypeNameLength > 0)
6588 /* Get the OleTypeName */
6589 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6590 if(dwSize != pData->dwOleTypeNameLength)
6592 hRes = CONVERT10_E_OLESTREAM_GET;
6598 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6599 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6601 hRes = CONVERT10_E_OLESTREAM_GET;
6605 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6606 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6607 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6608 if(pData->pstrOleObjFileName)
6610 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6611 if(dwSize != pData->dwOleObjFileNameLength)
6613 hRes = CONVERT10_E_OLESTREAM_GET;
6617 hRes = CONVERT10_E_OLESTREAM_GET;
6622 /* Get the Width of the Metafile */
6623 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6624 if(dwSize != sizeof(pData->dwMetaFileWidth))
6626 hRes = CONVERT10_E_OLESTREAM_GET;
6630 /* Get the Height of the Metafile */
6631 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6632 if(dwSize != sizeof(pData->dwMetaFileHeight))
6634 hRes = CONVERT10_E_OLESTREAM_GET;
6640 /* Get the Length of the Data */
6641 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6642 if(dwSize != sizeof(pData->dwDataLength))
6644 hRes = CONVERT10_E_OLESTREAM_GET;
6648 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6650 if(!bStrem1) /* if it is a second OLE stream data */
6652 pData->dwDataLength -= 8;
6653 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6654 if(dwSize != sizeof(pData->strUnknown))
6656 hRes = CONVERT10_E_OLESTREAM_GET;
6662 if(pData->dwDataLength > 0)
6664 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6666 /* Get Data (ex. IStorage, Metafile, or BMP) */
6669 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6670 if(dwSize != pData->dwDataLength)
6672 hRes = CONVERT10_E_OLESTREAM_GET;
6677 hRes = CONVERT10_E_OLESTREAM_GET;
6686 /*************************************************************************
6687 * OLECONVERT_SaveOLE10 [Internal]
6689 * Saves the OLE10 STREAM From memory
6692 * pData [I] Data Structure for the OLESTREAM Data
6693 * pOleStream [I] The OLESTREAM to save
6697 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6700 * This function is used by OleConvertIStorageToOLESTREAM only.
6703 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6706 HRESULT hRes = S_OK;
6710 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6711 if(dwSize != sizeof(pData->dwOleID))
6713 hRes = CONVERT10_E_OLESTREAM_PUT;
6718 /* Set the TypeID */
6719 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6720 if(dwSize != sizeof(pData->dwTypeID))
6722 hRes = CONVERT10_E_OLESTREAM_PUT;
6726 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6728 /* Set the Length of the OleTypeName */
6729 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6730 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6732 hRes = CONVERT10_E_OLESTREAM_PUT;
6737 if(pData->dwOleTypeNameLength > 0)
6739 /* Set the OleTypeName */
6740 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6741 if(dwSize != pData->dwOleTypeNameLength)
6743 hRes = CONVERT10_E_OLESTREAM_PUT;
6750 /* Set the width of the Metafile */
6751 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6752 if(dwSize != sizeof(pData->dwMetaFileWidth))
6754 hRes = CONVERT10_E_OLESTREAM_PUT;
6760 /* Set the height of the Metafile */
6761 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6762 if(dwSize != sizeof(pData->dwMetaFileHeight))
6764 hRes = CONVERT10_E_OLESTREAM_PUT;
6770 /* Set the length of the Data */
6771 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6772 if(dwSize != sizeof(pData->dwDataLength))
6774 hRes = CONVERT10_E_OLESTREAM_PUT;
6780 if(pData->dwDataLength > 0)
6782 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6783 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6784 if(dwSize != pData->dwDataLength)
6786 hRes = CONVERT10_E_OLESTREAM_PUT;
6794 /*************************************************************************
6795 * OLECONVERT_GetOLE20FromOLE10[Internal]
6797 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6798 * opens it, and copies the content to the dest IStorage for
6799 * OleConvertOLESTREAMToIStorage
6803 * pDestStorage [I] The IStorage to copy the data to
6804 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6805 * nBufferLength [I] The size of the buffer
6814 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6818 IStorage *pTempStorage;
6819 DWORD dwNumOfBytesWritten;
6820 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6821 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6823 /* Create a temp File */
6824 GetTempPathW(MAX_PATH, wstrTempDir);
6825 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6826 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6828 if(hFile != INVALID_HANDLE_VALUE)
6830 /* Write IStorage Data to File */
6831 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6834 /* Open and copy temp storage to the Dest Storage */
6835 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6838 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6839 StorageBaseImpl_Release(pTempStorage);
6841 DeleteFileW(wstrTempFile);
6846 /*************************************************************************
6847 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6849 * Saves the OLE10 STREAM From memory
6852 * pStorage [I] The Src IStorage to copy
6853 * pData [I] The Dest Memory to write to.
6856 * The size in bytes allocated for pData
6859 * Memory allocated for pData must be freed by the caller
6861 * Used by OleConvertIStorageToOLESTREAM only.
6864 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6868 DWORD nDataLength = 0;
6869 IStorage *pTempStorage;
6870 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6871 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6875 /* Create temp Storage */
6876 GetTempPathW(MAX_PATH, wstrTempDir);
6877 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6878 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6882 /* Copy Src Storage to the Temp Storage */
6883 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6884 StorageBaseImpl_Release(pTempStorage);
6886 /* Open Temp Storage as a file and copy to memory */
6887 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6888 if(hFile != INVALID_HANDLE_VALUE)
6890 nDataLength = GetFileSize(hFile, NULL);
6891 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6892 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6895 DeleteFileW(wstrTempFile);
6900 /*************************************************************************
6901 * OLECONVERT_CreateOleStream [Internal]
6903 * Creates the "\001OLE" stream in the IStorage if necessary.
6906 * pStorage [I] Dest storage to create the stream in
6912 * This function is used by OleConvertOLESTREAMToIStorage only.
6914 * This stream is still unknown, MS Word seems to have extra data
6915 * but since the data is stored in the OLESTREAM there should be
6916 * no need to recreate the stream. If the stream is manually
6917 * deleted it will create it with this default data.
6920 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6924 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6925 BYTE pOleStreamHeader [] =
6927 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6928 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6929 0x00, 0x00, 0x00, 0x00
6932 /* Create stream if not present */
6933 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6934 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6938 /* Write default Data */
6939 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6940 IStream_Release(pStream);
6944 /* write a string to a stream, preceded by its length */
6945 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6952 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6953 r = IStream_Write( stm, &len, sizeof(len), NULL);
6958 str = CoTaskMemAlloc( len );
6959 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6960 r = IStream_Write( stm, str, len, NULL);
6961 CoTaskMemFree( str );
6965 /* read a string preceded by its length from a stream */
6966 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6969 DWORD len, count = 0;
6973 r = IStream_Read( stm, &len, sizeof(len), &count );
6976 if( count != sizeof(len) )
6977 return E_OUTOFMEMORY;
6979 TRACE("%ld bytes\n",len);
6981 str = CoTaskMemAlloc( len );
6983 return E_OUTOFMEMORY;
6985 r = IStream_Read( stm, str, len, &count );
6990 CoTaskMemFree( str );
6991 return E_OUTOFMEMORY;
6994 TRACE("Read string %s\n",debugstr_an(str,len));
6996 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6997 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6999 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7000 CoTaskMemFree( str );
7008 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7009 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7013 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7015 static const BYTE unknown1[12] =
7016 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7017 0xFF, 0xFF, 0xFF, 0xFF};
7018 static const BYTE unknown2[16] =
7019 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7020 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7022 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7023 debugstr_w(lpszUserType), debugstr_w(szClipName),
7024 debugstr_w(szProgIDName));
7026 /* Create a CompObj stream if it doesn't exist */
7027 r = IStorage_CreateStream(pstg, szwStreamName,
7028 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7032 /* Write CompObj Structure to stream */
7033 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7035 if( SUCCEEDED( r ) )
7036 r = WriteClassStm( pstm, clsid );
7038 if( SUCCEEDED( r ) )
7039 r = STREAM_WriteString( pstm, lpszUserType );
7040 if( SUCCEEDED( r ) )
7041 r = STREAM_WriteString( pstm, szClipName );
7042 if( SUCCEEDED( r ) )
7043 r = STREAM_WriteString( pstm, szProgIDName );
7044 if( SUCCEEDED( r ) )
7045 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7047 IStream_Release( pstm );
7052 /***********************************************************************
7053 * WriteFmtUserTypeStg (OLE32.@)
7055 HRESULT WINAPI WriteFmtUserTypeStg(
7056 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7059 WCHAR szwClipName[0x40];
7060 CLSID clsid = CLSID_NULL;
7061 LPWSTR wstrProgID = NULL;
7064 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7066 /* get the clipboard format name */
7067 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
7070 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7072 /* FIXME: There's room to save a CLSID and its ProgID, but
7073 the CLSID is not looked up in the registry and in all the
7074 tests I wrote it was CLSID_NULL. Where does it come from?
7077 /* get the real program ID. This may fail, but that's fine */
7078 ProgIDFromCLSID(&clsid, &wstrProgID);
7080 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7082 r = STORAGE_WriteCompObj( pstg, &clsid,
7083 lpszUserType, szwClipName, wstrProgID );
7085 CoTaskMemFree(wstrProgID);
7091 /******************************************************************************
7092 * ReadFmtUserTypeStg [OLE32.@]
7094 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7098 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7099 unsigned char unknown1[12];
7100 unsigned char unknown2[16];
7102 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7105 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7107 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7108 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7111 WARN("Failed to open stream r = %08lx\n", r);
7115 /* read the various parts of the structure */
7116 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7117 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7119 r = ReadClassStm( stm, &clsid );
7123 r = STREAM_ReadString( stm, &szCLSIDName );
7127 r = STREAM_ReadString( stm, &szOleTypeName );
7131 r = STREAM_ReadString( stm, &szProgIDName );
7135 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7136 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7139 /* ok, success... now we just need to store what we found */
7141 *pcf = RegisterClipboardFormatW( szOleTypeName );
7142 CoTaskMemFree( szOleTypeName );
7144 if( lplpszUserType )
7145 *lplpszUserType = szCLSIDName;
7146 CoTaskMemFree( szProgIDName );
7149 IStream_Release( stm );
7155 /*************************************************************************
7156 * OLECONVERT_CreateCompObjStream [Internal]
7158 * Creates a "\001CompObj" is the destination IStorage if necessary.
7161 * pStorage [I] The dest IStorage to create the CompObj Stream
7163 * strOleTypeName [I] The ProgID
7167 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7170 * This function is used by OleConvertOLESTREAMToIStorage only.
7172 * The stream data is stored in the OLESTREAM and there should be
7173 * no need to recreate the stream. If the stream is manually
7174 * deleted it will attempt to create it by querying the registry.
7178 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7181 HRESULT hStorageRes, hRes = S_OK;
7182 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7183 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7184 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7186 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7187 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7189 /* Initialize the CompObj structure */
7190 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7191 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7192 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7195 /* Create a CompObj stream if it doesn't exist */
7196 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7197 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7198 if(hStorageRes == S_OK)
7200 /* copy the OleTypeName to the compobj struct */
7201 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7202 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7204 /* copy the OleTypeName to the compobj struct */
7205 /* Note: in the test made, these were Identical */
7206 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7207 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7210 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7211 bufferW, OLESTREAM_MAX_STR_LEN );
7212 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7218 /* Get the CLSID Default Name from the Registry */
7219 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7220 if(hErr == ERROR_SUCCESS)
7222 char strTemp[OLESTREAM_MAX_STR_LEN];
7223 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7224 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7225 if(hErr == ERROR_SUCCESS)
7227 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7233 /* Write CompObj Structure to stream */
7234 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7236 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7238 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7239 if(IStorageCompObj.dwCLSIDNameLength > 0)
7241 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7243 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7244 if(IStorageCompObj.dwOleTypeNameLength > 0)
7246 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7248 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7249 if(IStorageCompObj.dwProgIDNameLength > 0)
7251 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7253 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7254 IStream_Release(pStream);
7260 /*************************************************************************
7261 * OLECONVERT_CreateOlePresStream[Internal]
7263 * Creates the "\002OlePres000" Stream with the Metafile data
7266 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7267 * dwExtentX [I] Width of the Metafile
7268 * dwExtentY [I] Height of the Metafile
7269 * pData [I] Metafile data
7270 * dwDataLength [I] Size of the Metafile data
7274 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7277 * This function is used by OleConvertOLESTREAMToIStorage only.
7280 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7284 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7285 BYTE pOlePresStreamHeader [] =
7287 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7288 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7289 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7290 0x00, 0x00, 0x00, 0x00
7293 BYTE pOlePresStreamHeaderEmpty [] =
7295 0x00, 0x00, 0x00, 0x00,
7296 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7297 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7298 0x00, 0x00, 0x00, 0x00
7301 /* Create the OlePres000 Stream */
7302 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7303 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7308 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7310 memset(&OlePres, 0, sizeof(OlePres));
7311 /* Do we have any metafile data to save */
7312 if(dwDataLength > 0)
7314 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7315 nHeaderSize = sizeof(pOlePresStreamHeader);
7319 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7320 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7322 /* Set width and height of the metafile */
7323 OlePres.dwExtentX = dwExtentX;
7324 OlePres.dwExtentY = -dwExtentY;
7326 /* Set Data and Length */
7327 if(dwDataLength > sizeof(METAFILEPICT16))
7329 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7330 OlePres.pData = &(pData[8]);
7332 /* Save OlePres000 Data to Stream */
7333 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7334 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7335 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7336 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7337 if(OlePres.dwSize > 0)
7339 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7341 IStream_Release(pStream);
7345 /*************************************************************************
7346 * OLECONVERT_CreateOle10NativeStream [Internal]
7348 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7351 * pStorage [I] Dest storage to create the stream in
7352 * pData [I] Ole10 Native Data (ex. bmp)
7353 * dwDataLength [I] Size of the Ole10 Native Data
7359 * This function is used by OleConvertOLESTREAMToIStorage only.
7361 * Might need to verify the data and return appropriate error message
7364 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7368 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7370 /* Create the Ole10Native Stream */
7371 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7372 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7376 /* Write info to stream */
7377 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7378 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7379 IStream_Release(pStream);
7384 /*************************************************************************
7385 * OLECONVERT_GetOLE10ProgID [Internal]
7387 * Finds the ProgID (or OleTypeID) from the IStorage
7390 * pStorage [I] The Src IStorage to get the ProgID
7391 * strProgID [I] the ProgID string to get
7392 * dwSize [I] the size of the string
7396 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7399 * This function is used by OleConvertIStorageToOLESTREAM only.
7403 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7407 LARGE_INTEGER iSeekPos;
7408 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7409 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7411 /* Open the CompObj Stream */
7412 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7413 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7417 /*Get the OleType from the CompObj Stream */
7418 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7419 iSeekPos.u.HighPart = 0;
7421 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7422 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7423 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7424 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7425 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7426 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7427 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7429 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7432 IStream_Read(pStream, strProgID, *dwSize, NULL);
7434 IStream_Release(pStream);
7439 LPOLESTR wstrProgID;
7441 /* Get the OleType from the registry */
7442 REFCLSID clsid = &(stat.clsid);
7443 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7444 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7447 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7454 /*************************************************************************
7455 * OLECONVERT_GetOle10PresData [Internal]
7457 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7460 * pStorage [I] Src IStroage
7461 * pOleStream [I] Dest OleStream Mem Struct
7467 * This function is used by OleConvertIStorageToOLESTREAM only.
7469 * Memory allocated for pData must be freed by the caller
7473 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7478 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7480 /* Initialize Default data for OLESTREAM */
7481 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7482 pOleStreamData[0].dwTypeID = 2;
7483 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7484 pOleStreamData[1].dwTypeID = 0;
7485 pOleStreamData[0].dwMetaFileWidth = 0;
7486 pOleStreamData[0].dwMetaFileHeight = 0;
7487 pOleStreamData[0].pData = NULL;
7488 pOleStreamData[1].pData = NULL;
7490 /* Open Ole10Native Stream */
7491 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7492 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7496 /* Read Size and Data */
7497 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7498 if(pOleStreamData->dwDataLength > 0)
7500 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7501 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7503 IStream_Release(pStream);
7509 /*************************************************************************
7510 * OLECONVERT_GetOle20PresData[Internal]
7512 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7515 * pStorage [I] Src IStroage
7516 * pOleStreamData [I] Dest OleStream Mem Struct
7522 * This function is used by OleConvertIStorageToOLESTREAM only.
7524 * Memory allocated for pData must be freed by the caller
7526 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7530 OLECONVERT_ISTORAGE_OLEPRES olePress;
7531 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7533 /* Initialize Default data for OLESTREAM */
7534 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7535 pOleStreamData[0].dwTypeID = 2;
7536 pOleStreamData[0].dwMetaFileWidth = 0;
7537 pOleStreamData[0].dwMetaFileHeight = 0;
7538 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7539 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7540 pOleStreamData[1].dwTypeID = 0;
7541 pOleStreamData[1].dwOleTypeNameLength = 0;
7542 pOleStreamData[1].strOleTypeName[0] = 0;
7543 pOleStreamData[1].dwMetaFileWidth = 0;
7544 pOleStreamData[1].dwMetaFileHeight = 0;
7545 pOleStreamData[1].pData = NULL;
7546 pOleStreamData[1].dwDataLength = 0;
7549 /* Open OlePress000 stream */
7550 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7551 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7554 LARGE_INTEGER iSeekPos;
7555 METAFILEPICT16 MetaFilePict;
7556 static const char strMetafilePictName[] = "METAFILEPICT";
7558 /* Set the TypeID for a Metafile */
7559 pOleStreamData[1].dwTypeID = 5;
7561 /* Set the OleTypeName to Metafile */
7562 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7563 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7565 iSeekPos.u.HighPart = 0;
7566 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7568 /* Get Presentation Data */
7569 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7570 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7571 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7572 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7574 /*Set width and Height */
7575 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7576 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7577 if(olePress.dwSize > 0)
7580 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7582 /* Set MetaFilePict struct */
7583 MetaFilePict.mm = 8;
7584 MetaFilePict.xExt = olePress.dwExtentX;
7585 MetaFilePict.yExt = olePress.dwExtentY;
7586 MetaFilePict.hMF = 0;
7588 /* Get Metafile Data */
7589 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7590 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7591 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7593 IStream_Release(pStream);
7597 /*************************************************************************
7598 * OleConvertOLESTREAMToIStorage [OLE32.@]
7603 * DVTARGETDEVICE paramenter is not handled
7604 * Still unsure of some mem fields for OLE 10 Stream
7605 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7606 * and "\001OLE" streams
7609 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7610 LPOLESTREAM pOleStream,
7612 const DVTARGETDEVICE* ptd)
7616 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7618 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7620 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7624 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7627 if(pstg == NULL || pOleStream == NULL)
7629 hRes = E_INVALIDARG;
7634 /* Load the OLESTREAM to Memory */
7635 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7640 /* Load the OLESTREAM to Memory (part 2)*/
7641 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7647 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7649 /* Do we have the IStorage Data in the OLESTREAM */
7650 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7652 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7653 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7657 /* It must be an original OLE 1.0 source */
7658 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7663 /* It must be an original OLE 1.0 source */
7664 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7667 /* Create CompObj Stream if necessary */
7668 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7671 /*Create the Ole Stream if necessary */
7672 OLECONVERT_CreateOleStream(pstg);
7677 /* Free allocated memory */
7678 for(i=0; i < 2; i++)
7680 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7681 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7682 pOleStreamData[i].pstrOleObjFileName = NULL;
7687 /*************************************************************************
7688 * OleConvertIStorageToOLESTREAM [OLE32.@]
7695 * Still unsure of some mem fields for OLE 10 Stream
7696 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7697 * and "\001OLE" streams.
7700 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7702 LPOLESTREAM pOleStream)
7705 HRESULT hRes = S_OK;
7707 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7708 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7710 TRACE("%p %p\n", pstg, pOleStream);
7712 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7714 if(pstg == NULL || pOleStream == NULL)
7716 hRes = E_INVALIDARG;
7720 /* Get the ProgID */
7721 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7722 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7726 /* Was it originally Ole10 */
7727 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7730 IStream_Release(pStream);
7731 /* Get Presentation Data for Ole10Native */
7732 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7736 /* Get Presentation Data (OLE20) */
7737 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7740 /* Save OLESTREAM */
7741 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7744 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7749 /* Free allocated memory */
7750 for(i=0; i < 2; i++)
7752 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7758 /***********************************************************************
7759 * GetConvertStg (OLE32.@)
7761 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7762 FIXME("unimplemented stub!\n");
7766 /******************************************************************************
7767 * StgIsStorageFile [OLE32.@]
7768 * Verify if the file contains a storage object
7774 * S_OK if file has magic bytes as a storage object
7775 * S_FALSE if file is not storage
7778 StgIsStorageFile(LPCOLESTR fn)
7784 TRACE("%s\n", debugstr_w(fn));
7785 hf = CreateFileW(fn, GENERIC_READ,
7786 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7787 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7789 if (hf == INVALID_HANDLE_VALUE)
7790 return STG_E_FILENOTFOUND;
7792 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7794 WARN(" unable to read file\n");
7801 if (bytes_read != 8) {
7802 WARN(" too short\n");
7806 if (!memcmp(magic,STORAGE_magic,8)) {
7811 WARN(" -> Invalid header.\n");