2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
33 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/istorage_compound_file_implementation.asp
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
50 #include "wine/unicode.h"
51 #include "wine/debug.h"
53 #include "storage32.h"
54 #include "ole2.h" /* For Write/ReadClassStm */
57 #include "wine/wingdi16.h"
59 WINE_DEFAULT_DEBUG_CHANNEL(storage);
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 HRESULT WINAPI StorageBaseImpl_QueryInterface(
187 StorageBaseImpl *This = (StorageBaseImpl *)iface;
189 * Perform a sanity check on the parameters.
191 if ( (This==0) || (ppvObject==0) )
195 * Initialize the return parameter.
200 * Compare the riid with the interface IDs implemented by this object.
202 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
204 *ppvObject = (IStorage*)This;
206 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
208 *ppvObject = (IStorage*)This;
210 else if (memcmp(&IID_IPropertySetStorage, riid, sizeof(IID_IPropertySetStorage)) == 0)
212 *ppvObject = (IStorage*)&This->pssVtbl;
216 * Check that we obtained an interface.
219 return E_NOINTERFACE;
222 * Query Interface always increases the reference count by one when it is
225 IStorage_AddRef(iface);
230 /************************************************************************
231 * Storage32BaseImpl_AddRef (IUnknown)
233 * This method implements the common AddRef for all IStorage32
234 * implementations contained in this file.
236 * See Windows documentation for more details on IUnknown methods.
238 ULONG WINAPI StorageBaseImpl_AddRef(
241 StorageBaseImpl *This = (StorageBaseImpl *)iface;
242 ULONG ref = InterlockedIncrement(&This->ref);
244 TRACE("(%p) AddRef to %ld\n", This, ref);
249 /************************************************************************
250 * Storage32BaseImpl_Release (IUnknown)
252 * This method implements the common Release for all IStorage32
253 * implementations contained in this file.
255 * See Windows documentation for more details on IUnknown methods.
257 ULONG WINAPI StorageBaseImpl_Release(
260 StorageBaseImpl *This = (StorageBaseImpl *)iface;
262 * Decrease the reference count on this object.
264 ULONG ref = InterlockedDecrement(&This->ref);
266 TRACE("(%p) ReleaseRef to %ld\n", This, ref);
269 * If the reference count goes down to 0, perform suicide.
274 * Since we are using a system of base-classes, we want to call the
275 * destructor of the appropriate derived class. To do this, we are
276 * using virtual functions to implement the destructor.
278 This->v_destructor(This);
284 /************************************************************************
285 * Storage32BaseImpl_OpenStream (IStorage)
287 * This method will open the specified stream object from the current storage.
289 * See Windows documentation for more details on IStorage methods.
291 HRESULT WINAPI StorageBaseImpl_OpenStream(
293 const OLECHAR* pwcsName, /* [string][in] */
294 void* reserved1, /* [unique][in] */
295 DWORD grfMode, /* [in] */
296 DWORD reserved2, /* [in] */
297 IStream** ppstm) /* [out] */
299 StorageBaseImpl *This = (StorageBaseImpl *)iface;
300 IEnumSTATSTGImpl* propertyEnumeration;
301 StgStreamImpl* newStream;
302 StgProperty currentProperty;
303 ULONG foundPropertyIndex;
304 HRESULT res = STG_E_UNKNOWN;
305 DWORD parent_grfMode;
307 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
308 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
311 * Perform a sanity check on the parameters.
313 if ( (pwcsName==NULL) || (ppstm==0) )
320 * Initialize the out parameter
325 * Validate the STGM flags
327 if ( FAILED( validateSTGM(grfMode) ))
329 res = STG_E_INVALIDFLAG;
336 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
337 (grfMode & STGM_DELETEONRELEASE) ||
338 (grfMode & STGM_TRANSACTED) )
340 res = STG_E_INVALIDFUNCTION;
345 * Check that we're compatible with the parent's storage mode
347 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
348 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
350 res = STG_E_ACCESSDENIED;
355 * Create a property enumeration to search the properties
357 propertyEnumeration = IEnumSTATSTGImpl_Construct(
358 This->ancestorStorage,
359 This->rootPropertySetIndex);
362 * Search the enumeration for the property with the given name
364 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
370 * Delete the property enumeration since we don't need it anymore
372 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
375 * If it was found, construct the stream object and return a pointer to it.
377 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
378 (currentProperty.propertyType==PROPTYPE_STREAM) )
380 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
384 newStream->grfMode = grfMode;
385 *ppstm = (IStream*)newStream;
388 * Since we are returning a pointer to the interface, we have to
389 * nail down the reference.
391 IStream_AddRef(*ppstm);
401 res = STG_E_FILENOTFOUND;
405 TRACE("<-- IStream %p\n", *ppstm);
406 TRACE("<-- %08lx\n", res);
410 /************************************************************************
411 * Storage32BaseImpl_OpenStorage (IStorage)
413 * This method will open a new storage object from the current storage.
415 * See Windows documentation for more details on IStorage methods.
417 HRESULT WINAPI StorageBaseImpl_OpenStorage(
419 const OLECHAR* pwcsName, /* [string][unique][in] */
420 IStorage* pstgPriority, /* [unique][in] */
421 DWORD grfMode, /* [in] */
422 SNB snbExclude, /* [unique][in] */
423 DWORD reserved, /* [in] */
424 IStorage** ppstg) /* [out] */
426 StorageBaseImpl *This = (StorageBaseImpl *)iface;
427 StorageInternalImpl* newStorage;
428 IEnumSTATSTGImpl* propertyEnumeration;
429 StgProperty currentProperty;
430 ULONG foundPropertyIndex;
431 HRESULT res = STG_E_UNKNOWN;
432 DWORD parent_grfMode;
434 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
435 iface, debugstr_w(pwcsName), pstgPriority,
436 grfMode, snbExclude, reserved, ppstg);
439 * Perform a sanity check on the parameters.
441 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
448 if (snbExclude != NULL)
450 res = STG_E_INVALIDPARAMETER;
455 * Validate the STGM flags
457 if ( FAILED( validateSTGM(grfMode) ))
459 res = STG_E_INVALIDFLAG;
466 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
467 (grfMode & STGM_DELETEONRELEASE) ||
468 (grfMode & STGM_PRIORITY) )
470 res = STG_E_INVALIDFUNCTION;
475 * Check that we're compatible with the parent's storage mode
477 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
478 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
480 res = STG_E_ACCESSDENIED;
485 * Initialize the out parameter
490 * Create a property enumeration to search the properties
492 propertyEnumeration = IEnumSTATSTGImpl_Construct(
493 This->ancestorStorage,
494 This->rootPropertySetIndex);
497 * Search the enumeration for the property with the given name
499 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
505 * Delete the property enumeration since we don't need it anymore
507 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
510 * If it was found, construct the stream object and return a pointer to it.
512 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
513 (currentProperty.propertyType==PROPTYPE_STORAGE) )
516 * Construct a new Storage object
518 newStorage = StorageInternalImpl_Construct(
519 This->ancestorStorage,
525 *ppstg = (IStorage*)newStorage;
528 * Since we are returning a pointer to the interface,
529 * we have to nail down the reference.
531 StorageBaseImpl_AddRef(*ppstg);
537 res = STG_E_INSUFFICIENTMEMORY;
541 res = STG_E_FILENOTFOUND;
544 TRACE("<-- %08lx\n", res);
548 /************************************************************************
549 * Storage32BaseImpl_EnumElements (IStorage)
551 * This method will create an enumerator object that can be used to
552 * retrieve informatino about all the properties in the storage object.
554 * See Windows documentation for more details on IStorage methods.
556 HRESULT WINAPI StorageBaseImpl_EnumElements(
558 DWORD reserved1, /* [in] */
559 void* reserved2, /* [size_is][unique][in] */
560 DWORD reserved3, /* [in] */
561 IEnumSTATSTG** ppenum) /* [out] */
563 StorageBaseImpl *This = (StorageBaseImpl *)iface;
564 IEnumSTATSTGImpl* newEnum;
566 TRACE("(%p, %ld, %p, %ld, %p)\n",
567 iface, reserved1, reserved2, reserved3, ppenum);
570 * Perform a sanity check on the parameters.
572 if ( (This==0) || (ppenum==0))
576 * Construct the enumerator.
578 newEnum = IEnumSTATSTGImpl_Construct(
579 This->ancestorStorage,
580 This->rootPropertySetIndex);
584 *ppenum = (IEnumSTATSTG*)newEnum;
587 * Don't forget to nail down a reference to the new object before
590 IEnumSTATSTG_AddRef(*ppenum);
595 return E_OUTOFMEMORY;
598 /************************************************************************
599 * Storage32BaseImpl_Stat (IStorage)
601 * This method will retrieve information about this storage object.
603 * See Windows documentation for more details on IStorage methods.
605 HRESULT WINAPI StorageBaseImpl_Stat(
607 STATSTG* pstatstg, /* [out] */
608 DWORD grfStatFlag) /* [in] */
610 StorageBaseImpl *This = (StorageBaseImpl *)iface;
611 StgProperty curProperty;
613 HRESULT res = STG_E_UNKNOWN;
615 TRACE("(%p, %p, %lx)\n",
616 iface, pstatstg, grfStatFlag);
619 * Perform a sanity check on the parameters.
621 if ( (This==0) || (pstatstg==0))
628 * Read the information from the property.
630 readSuccessful = StorageImpl_ReadProperty(
631 This->ancestorStorage,
632 This->rootPropertySetIndex,
637 StorageUtl_CopyPropertyToSTATSTG(
651 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);
653 TRACE("<-- %08lx\n", res);
657 /************************************************************************
658 * Storage32BaseImpl_RenameElement (IStorage)
660 * This method will rename the specified element.
662 * See Windows documentation for more details on IStorage methods.
664 * Implementation notes: The method used to rename consists of creating a clone
665 * of the deleted StgProperty object setting it with the new name and to
666 * perform a DestroyElement of the old StgProperty.
668 HRESULT WINAPI StorageBaseImpl_RenameElement(
670 const OLECHAR* pwcsOldName, /* [in] */
671 const OLECHAR* pwcsNewName) /* [in] */
673 StorageBaseImpl *This = (StorageBaseImpl *)iface;
674 IEnumSTATSTGImpl* propertyEnumeration;
675 StgProperty currentProperty;
676 ULONG foundPropertyIndex;
678 TRACE("(%p, %s, %s)\n",
679 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
682 * Create a property enumeration to search the properties
684 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
685 This->rootPropertySetIndex);
688 * Search the enumeration for the new property name
690 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
694 if (foundPropertyIndex != PROPERTY_NULL)
697 * There is already a property with the new name
699 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
700 return STG_E_FILEALREADYEXISTS;
703 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
706 * Search the enumeration for the old property name
708 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
713 * Delete the property enumeration since we don't need it anymore
715 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
717 if (foundPropertyIndex != PROPERTY_NULL)
719 StgProperty renamedProperty;
720 ULONG renamedPropertyIndex;
723 * Setup a new property for the renamed property
725 renamedProperty.sizeOfNameString =
726 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
728 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
729 return STG_E_INVALIDNAME;
731 strcpyW(renamedProperty.name, pwcsNewName);
733 renamedProperty.propertyType = currentProperty.propertyType;
734 renamedProperty.startingBlock = currentProperty.startingBlock;
735 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
736 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
738 renamedProperty.previousProperty = PROPERTY_NULL;
739 renamedProperty.nextProperty = PROPERTY_NULL;
742 * Bring the dirProperty link in case it is a storage and in which
743 * case the renamed storage elements don't require to be reorganized.
745 renamedProperty.dirProperty = currentProperty.dirProperty;
747 /* call CoFileTime to get the current time
748 renamedProperty.timeStampS1
749 renamedProperty.timeStampD1
750 renamedProperty.timeStampS2
751 renamedProperty.timeStampD2
752 renamedProperty.propertyUniqueID
756 * Obtain a free property in the property chain
758 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
761 * Save the new property into the new property spot
763 StorageImpl_WriteProperty(
764 This->ancestorStorage,
765 renamedPropertyIndex,
769 * Find a spot in the property chain for our newly created property.
773 renamedPropertyIndex,
777 * At this point the renamed property has been inserted in the tree,
778 * now, before to Destroy the old property we must zeroed it's dirProperty
779 * otherwise the DestroyProperty below will zap it all and we do not want
781 * Also, we fake that the old property is a storage so the DestroyProperty
782 * will not do a SetSize(0) on the stream data.
784 * This means that we need to tweek the StgProperty if it is a stream or a
787 StorageImpl_ReadProperty(This->ancestorStorage,
791 currentProperty.dirProperty = PROPERTY_NULL;
792 currentProperty.propertyType = PROPTYPE_STORAGE;
793 StorageImpl_WriteProperty(
794 This->ancestorStorage,
799 * Invoke Destroy to get rid of the ole property and automatically redo
800 * the linking of it's previous and next members...
802 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
808 * There is no property with the old name
810 return STG_E_FILENOTFOUND;
816 /************************************************************************
817 * Storage32BaseImpl_CreateStream (IStorage)
819 * This method will create a stream object within this storage
821 * See Windows documentation for more details on IStorage methods.
823 HRESULT WINAPI StorageBaseImpl_CreateStream(
825 const OLECHAR* pwcsName, /* [string][in] */
826 DWORD grfMode, /* [in] */
827 DWORD reserved1, /* [in] */
828 DWORD reserved2, /* [in] */
829 IStream** ppstm) /* [out] */
831 StorageBaseImpl *This = (StorageBaseImpl *)iface;
832 IEnumSTATSTGImpl* propertyEnumeration;
833 StgStreamImpl* newStream;
834 StgProperty currentProperty, newStreamProperty;
835 ULONG foundPropertyIndex, newPropertyIndex;
836 DWORD parent_grfMode;
838 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
839 iface, debugstr_w(pwcsName), grfMode,
840 reserved1, reserved2, ppstm);
843 * Validate parameters
846 return STG_E_INVALIDPOINTER;
849 return STG_E_INVALIDNAME;
851 if (reserved1 || reserved2)
852 return STG_E_INVALIDPARAMETER;
855 * Validate the STGM flags
857 if ( FAILED( validateSTGM(grfMode) ))
858 return STG_E_INVALIDFLAG;
860 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
861 return STG_E_INVALIDFLAG;
866 if ((grfMode & STGM_DELETEONRELEASE) ||
867 (grfMode & STGM_TRANSACTED))
868 return STG_E_INVALIDFUNCTION;
871 * Check that we're compatible with the parent's storage mode
873 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
874 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
875 return STG_E_ACCESSDENIED;
878 * Initialize the out parameter
883 * Create a property enumeration to search the properties
885 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
886 This->rootPropertySetIndex);
888 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
892 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
894 if (foundPropertyIndex != PROPERTY_NULL)
897 * An element with this name already exists
899 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
901 IStorage_DestroyElement(iface, pwcsName);
904 return STG_E_FILEALREADYEXISTS;
908 * memset the empty property
910 memset(&newStreamProperty, 0, sizeof(StgProperty));
912 newStreamProperty.sizeOfNameString =
913 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
915 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
916 return STG_E_INVALIDNAME;
918 strcpyW(newStreamProperty.name, pwcsName);
920 newStreamProperty.propertyType = PROPTYPE_STREAM;
921 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
922 newStreamProperty.size.u.LowPart = 0;
923 newStreamProperty.size.u.HighPart = 0;
925 newStreamProperty.previousProperty = PROPERTY_NULL;
926 newStreamProperty.nextProperty = PROPERTY_NULL;
927 newStreamProperty.dirProperty = PROPERTY_NULL;
929 /* call CoFileTime to get the current time
930 newStreamProperty.timeStampS1
931 newStreamProperty.timeStampD1
932 newStreamProperty.timeStampS2
933 newStreamProperty.timeStampD2
936 /* newStreamProperty.propertyUniqueID */
939 * Get a free property or create a new one
941 newPropertyIndex = getFreeProperty(This->ancestorStorage);
944 * Save the new property into the new property spot
946 StorageImpl_WriteProperty(
947 This->ancestorStorage,
952 * Find a spot in the property chain for our newly created property.
960 * Open the stream to return it.
962 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
966 *ppstm = (IStream*)newStream;
969 * Since we are returning a pointer to the interface, we have to nail down
972 IStream_AddRef(*ppstm);
976 return STG_E_INSUFFICIENTMEMORY;
982 /************************************************************************
983 * Storage32BaseImpl_SetClass (IStorage)
985 * This method will write the specified CLSID in the property of this
988 * See Windows documentation for more details on IStorage methods.
990 HRESULT WINAPI StorageBaseImpl_SetClass(
992 REFCLSID clsid) /* [in] */
994 StorageBaseImpl *This = (StorageBaseImpl *)iface;
995 HRESULT hRes = E_FAIL;
996 StgProperty curProperty;
999 TRACE("(%p, %p)\n", iface, clsid);
1001 success = StorageImpl_ReadProperty(This->ancestorStorage,
1002 This->rootPropertySetIndex,
1006 curProperty.propertyUniqueID = *clsid;
1008 success = StorageImpl_WriteProperty(This->ancestorStorage,
1009 This->rootPropertySetIndex,
1018 /************************************************************************
1019 ** Storage32Impl implementation
1022 /************************************************************************
1023 * Storage32Impl_CreateStorage (IStorage)
1025 * This method will create the storage object within the provided storage.
1027 * See Windows documentation for more details on IStorage methods.
1029 HRESULT WINAPI StorageImpl_CreateStorage(
1031 const OLECHAR *pwcsName, /* [string][in] */
1032 DWORD grfMode, /* [in] */
1033 DWORD reserved1, /* [in] */
1034 DWORD reserved2, /* [in] */
1035 IStorage **ppstg) /* [out] */
1037 StorageImpl* const This=(StorageImpl*)iface;
1039 IEnumSTATSTGImpl *propertyEnumeration;
1040 StgProperty currentProperty;
1041 StgProperty newProperty;
1042 ULONG foundPropertyIndex;
1043 ULONG newPropertyIndex;
1045 DWORD parent_grfMode;
1047 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1048 iface, debugstr_w(pwcsName), grfMode,
1049 reserved1, reserved2, ppstg);
1052 * Validate parameters
1055 return STG_E_INVALIDPOINTER;
1058 return STG_E_INVALIDNAME;
1061 * Validate the STGM flags
1063 if ( FAILED( validateSTGM(grfMode) ) ||
1064 (grfMode & STGM_DELETEONRELEASE) )
1065 return STG_E_INVALIDFLAG;
1068 * Check that we're compatible with the parent's storage mode
1070 parent_grfMode = STGM_ACCESS_MODE( This->base.ancestorStorage->base.openFlags );
1071 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
1072 return STG_E_ACCESSDENIED;
1075 * Initialize the out parameter
1080 * Create a property enumeration and search the properties
1082 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1083 This->base.rootPropertySetIndex);
1085 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1088 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1090 if (foundPropertyIndex != PROPERTY_NULL)
1093 * An element with this name already exists
1095 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1096 IStorage_DestroyElement(iface, pwcsName);
1098 return STG_E_FILEALREADYEXISTS;
1102 * memset the empty property
1104 memset(&newProperty, 0, sizeof(StgProperty));
1106 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1108 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1109 return STG_E_INVALIDNAME;
1111 strcpyW(newProperty.name, pwcsName);
1113 newProperty.propertyType = PROPTYPE_STORAGE;
1114 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1115 newProperty.size.u.LowPart = 0;
1116 newProperty.size.u.HighPart = 0;
1118 newProperty.previousProperty = PROPERTY_NULL;
1119 newProperty.nextProperty = PROPERTY_NULL;
1120 newProperty.dirProperty = PROPERTY_NULL;
1122 /* call CoFileTime to get the current time
1123 newProperty.timeStampS1
1124 newProperty.timeStampD1
1125 newProperty.timeStampS2
1126 newProperty.timeStampD2
1129 /* newStorageProperty.propertyUniqueID */
1132 * Obtain a free property in the property chain
1134 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1137 * Save the new property into the new property spot
1139 StorageImpl_WriteProperty(
1140 This->base.ancestorStorage,
1145 * Find a spot in the property chain for our newly created property.
1147 updatePropertyChain(
1153 * Open it to get a pointer to return.
1155 hr = IStorage_OpenStorage(
1157 (const OLECHAR*)pwcsName,
1164 if( (hr != S_OK) || (*ppstg == NULL))
1174 /***************************************************************************
1178 * Get a free property or create a new one.
1180 static ULONG getFreeProperty(
1181 StorageImpl *storage)
1183 ULONG currentPropertyIndex = 0;
1184 ULONG newPropertyIndex = PROPERTY_NULL;
1185 BOOL readSuccessful = TRUE;
1186 StgProperty currentProperty;
1191 * Start by reading the root property
1193 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1194 currentPropertyIndex,
1198 if (currentProperty.sizeOfNameString == 0)
1201 * The property existis and is available, we found it.
1203 newPropertyIndex = currentPropertyIndex;
1209 * We exhausted the property list, we will create more space below
1211 newPropertyIndex = currentPropertyIndex;
1213 currentPropertyIndex++;
1215 } while (newPropertyIndex == PROPERTY_NULL);
1218 * grow the property chain
1220 if (! readSuccessful)
1222 StgProperty emptyProperty;
1223 ULARGE_INTEGER newSize;
1224 ULONG propertyIndex;
1225 ULONG lastProperty = 0;
1226 ULONG blockCount = 0;
1229 * obtain the new count of property blocks
1231 blockCount = BlockChainStream_GetCount(
1232 storage->base.ancestorStorage->rootBlockChain)+1;
1235 * initialize the size used by the property stream
1237 newSize.u.HighPart = 0;
1238 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1241 * add a property block to the property chain
1243 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1246 * memset the empty property in order to initialize the unused newly
1249 memset(&emptyProperty, 0, sizeof(StgProperty));
1254 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1257 propertyIndex = newPropertyIndex;
1258 propertyIndex < lastProperty;
1261 StorageImpl_WriteProperty(
1262 storage->base.ancestorStorage,
1268 return newPropertyIndex;
1271 /****************************************************************************
1275 * Case insensitive comparaison of StgProperty.name by first considering
1278 * Returns <0 when newPrpoerty < currentProperty
1279 * >0 when newPrpoerty > currentProperty
1280 * 0 when newPrpoerty == currentProperty
1282 static LONG propertyNameCmp(
1283 const OLECHAR *newProperty,
1284 const OLECHAR *currentProperty)
1286 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1291 * We compare the string themselves only when they are of the same length
1293 diff = lstrcmpiW( newProperty, currentProperty);
1299 /****************************************************************************
1303 * Properly link this new element in the property chain.
1305 static void updatePropertyChain(
1306 StorageImpl *storage,
1307 ULONG newPropertyIndex,
1308 StgProperty newProperty)
1310 StgProperty currentProperty;
1313 * Read the root property
1315 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1316 storage->base.rootPropertySetIndex,
1319 if (currentProperty.dirProperty != PROPERTY_NULL)
1322 * The root storage contains some element, therefore, start the research
1323 * for the appropriate location.
1326 ULONG current, next, previous, currentPropertyId;
1329 * Keep the StgProperty sequence number of the storage first property
1331 currentPropertyId = currentProperty.dirProperty;
1336 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1337 currentProperty.dirProperty,
1340 previous = currentProperty.previousProperty;
1341 next = currentProperty.nextProperty;
1342 current = currentPropertyId;
1346 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1350 if (previous != PROPERTY_NULL)
1352 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1359 currentProperty.previousProperty = newPropertyIndex;
1360 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1368 if (next != PROPERTY_NULL)
1370 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1377 currentProperty.nextProperty = newPropertyIndex;
1378 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1387 * Trying to insert an item with the same name in the
1388 * subtree structure.
1393 previous = currentProperty.previousProperty;
1394 next = currentProperty.nextProperty;
1400 * The root storage is empty, link the new property to it's dir property
1402 currentProperty.dirProperty = newPropertyIndex;
1403 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1404 storage->base.rootPropertySetIndex,
1410 /*************************************************************************
1413 HRESULT WINAPI StorageImpl_CopyTo(
1415 DWORD ciidExclude, /* [in] */
1416 const IID* rgiidExclude, /* [size_is][unique][in] */
1417 SNB snbExclude, /* [unique][in] */
1418 IStorage* pstgDest) /* [unique][in] */
1420 IEnumSTATSTG *elements = 0;
1421 STATSTG curElement, strStat;
1423 IStorage *pstgTmp, *pstgChild;
1424 IStream *pstrTmp, *pstrChild;
1426 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1427 FIXME("Exclude option not implemented\n");
1429 TRACE("(%p, %ld, %p, %p, %p)\n",
1430 iface, ciidExclude, rgiidExclude,
1431 snbExclude, pstgDest);
1434 * Perform a sanity check
1436 if ( pstgDest == 0 )
1437 return STG_E_INVALIDPOINTER;
1440 * Enumerate the elements
1442 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1450 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1451 IStorage_SetClass( pstgDest, &curElement.clsid );
1456 * Obtain the next element
1458 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1460 if ( hr == S_FALSE )
1462 hr = S_OK; /* done, every element has been copied */
1466 if (curElement.type == STGTY_STORAGE)
1469 * open child source storage
1471 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1472 STGM_READ|STGM_SHARE_EXCLUSIVE,
1473 NULL, 0, &pstgChild );
1479 * Check if destination storage is not a child of the source
1480 * storage, which will cause an infinite loop
1482 if (pstgChild == pstgDest)
1484 IEnumSTATSTG_Release(elements);
1486 return STG_E_ACCESSDENIED;
1490 * create a new storage in destination storage
1492 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1493 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1497 * if it already exist, don't create a new one use this one
1499 if (hr == STG_E_FILEALREADYEXISTS)
1501 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1502 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1503 NULL, 0, &pstgTmp );
1511 * do the copy recursively
1513 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1514 snbExclude, pstgTmp );
1516 IStorage_Release( pstgTmp );
1517 IStorage_Release( pstgChild );
1519 else if (curElement.type == STGTY_STREAM)
1522 * create a new stream in destination storage. If the stream already
1523 * exist, it will be deleted and a new one will be created.
1525 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1526 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1533 * open child stream storage
1535 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1536 STGM_READ|STGM_SHARE_EXCLUSIVE,
1543 * Get the size of the source stream
1545 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1548 * Set the size of the destination stream.
1550 IStream_SetSize(pstrTmp, strStat.cbSize);
1555 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1558 IStream_Release( pstrTmp );
1559 IStream_Release( pstrChild );
1563 WARN("unknown element type: %ld\n", curElement.type);
1566 } while (hr == S_OK);
1571 IEnumSTATSTG_Release(elements);
1576 /*************************************************************************
1577 * MoveElementTo (IStorage)
1579 HRESULT WINAPI StorageImpl_MoveElementTo(
1581 const OLECHAR *pwcsName, /* [string][in] */
1582 IStorage *pstgDest, /* [unique][in] */
1583 const OLECHAR *pwcsNewName,/* [string][in] */
1584 DWORD grfFlags) /* [in] */
1586 FIXME("not implemented!\n");
1590 /*************************************************************************
1593 * Ensures that any changes made to a storage object open in transacted mode
1594 * are reflected in the parent storage
1597 * Wine doesn't implement transacted mode, which seems to be a basic
1598 * optimization, so we can ignore this stub for now.
1600 HRESULT WINAPI StorageImpl_Commit(
1602 DWORD grfCommitFlags)/* [in] */
1604 FIXME("(%ld): stub!\n", grfCommitFlags);
1608 /*************************************************************************
1611 * Discard all changes that have been made since the last commit operation
1613 HRESULT WINAPI StorageImpl_Revert(
1616 FIXME("not implemented!\n");
1620 /*************************************************************************
1621 * DestroyElement (IStorage)
1623 * Stategy: This implementation is build this way for simplicity not for speed.
1624 * I always delete the top most element of the enumeration and adjust
1625 * the deleted element pointer all the time. This takes longer to
1626 * do but allow to reinvoke DestroyElement whenever we encounter a
1627 * storage object. The optimisation reside in the usage of another
1628 * enumeration stategy that would give all the leaves of a storage
1629 * first. (postfix order)
1631 HRESULT WINAPI StorageImpl_DestroyElement(
1633 const OLECHAR *pwcsName)/* [string][in] */
1635 StorageImpl* const This=(StorageImpl*)iface;
1637 IEnumSTATSTGImpl* propertyEnumeration;
1640 StgProperty propertyToDelete;
1641 StgProperty parentProperty;
1642 ULONG foundPropertyIndexToDelete;
1643 ULONG typeOfRelation;
1644 ULONG parentPropertyId;
1647 iface, debugstr_w(pwcsName));
1650 * Perform a sanity check on the parameters.
1653 return STG_E_INVALIDPOINTER;
1656 * Create a property enumeration to search the property with the given name
1658 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1659 This->base.ancestorStorage,
1660 This->base.rootPropertySetIndex);
1662 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1663 propertyEnumeration,
1667 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1669 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1671 return STG_E_FILENOTFOUND;
1675 * Find the parent property of the property to delete (the one that
1676 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1677 * the parent is This. Otherwise, the parent is one of it's sibling...
1681 * First, read This's StgProperty..
1683 res = StorageImpl_ReadProperty(
1684 This->base.ancestorStorage,
1685 This->base.rootPropertySetIndex,
1691 * Second, check to see if by any chance the actual storage (This) is not
1692 * the parent of the property to delete... We never know...
1694 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1697 * Set data as it would have been done in the else part...
1699 typeOfRelation = PROPERTY_RELATION_DIR;
1700 parentPropertyId = This->base.rootPropertySetIndex;
1705 * Create a property enumeration to search the parent properties, and
1706 * delete it once done.
1708 IEnumSTATSTGImpl* propertyEnumeration2;
1710 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1711 This->base.ancestorStorage,
1712 This->base.rootPropertySetIndex);
1714 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1715 propertyEnumeration2,
1716 foundPropertyIndexToDelete,
1720 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1723 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1725 hr = deleteStorageProperty(
1727 foundPropertyIndexToDelete,
1730 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1732 hr = deleteStreamProperty(
1734 foundPropertyIndexToDelete,
1742 * Adjust the property chain
1744 hr = adjustPropertyChain(
1755 /************************************************************************
1756 * StorageImpl_Stat (IStorage)
1758 * This method will retrieve information about this storage object.
1760 * See Windows documentation for more details on IStorage methods.
1762 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1763 STATSTG* pstatstg, /* [out] */
1764 DWORD grfStatFlag) /* [in] */
1766 StorageImpl* const This = (StorageImpl*)iface;
1767 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1769 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1771 CoTaskMemFree(pstatstg->pwcsName);
1772 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1773 strcpyW(pstatstg->pwcsName, This->pwcsName);
1781 /*********************************************************************
1785 * Perform the deletion of a complete storage node
1788 static HRESULT deleteStorageProperty(
1789 StorageImpl *parentStorage,
1790 ULONG indexOfPropertyToDelete,
1791 StgProperty propertyToDelete)
1793 IEnumSTATSTG *elements = 0;
1794 IStorage *childStorage = 0;
1795 STATSTG currentElement;
1797 HRESULT destroyHr = S_OK;
1800 * Open the storage and enumerate it
1802 hr = StorageBaseImpl_OpenStorage(
1803 (IStorage*)parentStorage,
1804 propertyToDelete.name,
1806 STGM_SHARE_EXCLUSIVE,
1817 * Enumerate the elements
1819 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1824 * Obtain the next element
1826 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1829 destroyHr = StorageImpl_DestroyElement(
1830 (IStorage*)childStorage,
1831 (OLECHAR*)currentElement.pwcsName);
1833 CoTaskMemFree(currentElement.pwcsName);
1837 * We need to Reset the enumeration every time because we delete elements
1838 * and the enumeration could be invalid
1840 IEnumSTATSTG_Reset(elements);
1842 } while ((hr == S_OK) && (destroyHr == S_OK));
1845 * Invalidate the property by zeroing it's name member.
1847 propertyToDelete.sizeOfNameString = 0;
1849 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1850 indexOfPropertyToDelete,
1853 IStorage_Release(childStorage);
1854 IEnumSTATSTG_Release(elements);
1859 /*********************************************************************
1863 * Perform the deletion of a stream node
1866 static HRESULT deleteStreamProperty(
1867 StorageImpl *parentStorage,
1868 ULONG indexOfPropertyToDelete,
1869 StgProperty propertyToDelete)
1873 ULARGE_INTEGER size;
1875 size.u.HighPart = 0;
1878 hr = StorageBaseImpl_OpenStream(
1879 (IStorage*)parentStorage,
1880 (OLECHAR*)propertyToDelete.name,
1882 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1894 hr = IStream_SetSize(pis, size);
1902 * Release the stream object.
1904 IStream_Release(pis);
1907 * Invalidate the property by zeroing it's name member.
1909 propertyToDelete.sizeOfNameString = 0;
1912 * Here we should re-read the property so we get the updated pointer
1913 * but since we are here to zap it, I don't do it...
1915 StorageImpl_WriteProperty(
1916 parentStorage->base.ancestorStorage,
1917 indexOfPropertyToDelete,
1923 /*********************************************************************
1927 * Finds a placeholder for the StgProperty within the Storage
1930 static HRESULT findPlaceholder(
1931 StorageImpl *storage,
1932 ULONG propertyIndexToStore,
1933 ULONG storePropertyIndex,
1936 StgProperty storeProperty;
1941 * Read the storage property
1943 res = StorageImpl_ReadProperty(
1944 storage->base.ancestorStorage,
1953 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1955 if (storeProperty.previousProperty != PROPERTY_NULL)
1957 return findPlaceholder(
1959 propertyIndexToStore,
1960 storeProperty.previousProperty,
1965 storeProperty.previousProperty = propertyIndexToStore;
1968 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1970 if (storeProperty.nextProperty != PROPERTY_NULL)
1972 return findPlaceholder(
1974 propertyIndexToStore,
1975 storeProperty.nextProperty,
1980 storeProperty.nextProperty = propertyIndexToStore;
1983 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1985 if (storeProperty.dirProperty != PROPERTY_NULL)
1987 return findPlaceholder(
1989 propertyIndexToStore,
1990 storeProperty.dirProperty,
1995 storeProperty.dirProperty = propertyIndexToStore;
1999 hr = StorageImpl_WriteProperty(
2000 storage->base.ancestorStorage,
2012 /*************************************************************************
2016 * This method takes the previous and the next property link of a property
2017 * to be deleted and find them a place in the Storage.
2019 static HRESULT adjustPropertyChain(
2021 StgProperty propertyToDelete,
2022 StgProperty parentProperty,
2023 ULONG parentPropertyId,
2026 ULONG newLinkProperty = PROPERTY_NULL;
2027 BOOL needToFindAPlaceholder = FALSE;
2028 ULONG storeNode = PROPERTY_NULL;
2029 ULONG toStoreNode = PROPERTY_NULL;
2030 INT relationType = 0;
2034 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2036 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2039 * Set the parent previous to the property to delete previous
2041 newLinkProperty = propertyToDelete.previousProperty;
2043 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2046 * We also need to find a storage for the other link, setup variables
2047 * to do this at the end...
2049 needToFindAPlaceholder = TRUE;
2050 storeNode = propertyToDelete.previousProperty;
2051 toStoreNode = propertyToDelete.nextProperty;
2052 relationType = PROPERTY_RELATION_NEXT;
2055 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2058 * Set the parent previous to the property to delete next
2060 newLinkProperty = propertyToDelete.nextProperty;
2064 * Link it for real...
2066 parentProperty.previousProperty = newLinkProperty;
2069 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2071 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2074 * Set the parent next to the property to delete next previous
2076 newLinkProperty = propertyToDelete.previousProperty;
2078 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2081 * We also need to find a storage for the other link, setup variables
2082 * to do this at the end...
2084 needToFindAPlaceholder = TRUE;
2085 storeNode = propertyToDelete.previousProperty;
2086 toStoreNode = propertyToDelete.nextProperty;
2087 relationType = PROPERTY_RELATION_NEXT;
2090 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2093 * Set the parent next to the property to delete next
2095 newLinkProperty = propertyToDelete.nextProperty;
2099 * Link it for real...
2101 parentProperty.nextProperty = newLinkProperty;
2103 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2105 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2108 * Set the parent dir to the property to delete previous
2110 newLinkProperty = propertyToDelete.previousProperty;
2112 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2115 * We also need to find a storage for the other link, setup variables
2116 * to do this at the end...
2118 needToFindAPlaceholder = TRUE;
2119 storeNode = propertyToDelete.previousProperty;
2120 toStoreNode = propertyToDelete.nextProperty;
2121 relationType = PROPERTY_RELATION_NEXT;
2124 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2127 * Set the parent dir to the property to delete next
2129 newLinkProperty = propertyToDelete.nextProperty;
2133 * Link it for real...
2135 parentProperty.dirProperty = newLinkProperty;
2139 * Write back the parent property
2141 res = StorageImpl_WriteProperty(
2142 This->base.ancestorStorage,
2151 * If a placeholder is required for the other link, then, find one and
2152 * get out of here...
2154 if (needToFindAPlaceholder)
2156 hr = findPlaceholder(
2167 /******************************************************************************
2168 * SetElementTimes (IStorage)
2170 HRESULT WINAPI StorageImpl_SetElementTimes(
2172 const OLECHAR *pwcsName,/* [string][in] */
2173 const FILETIME *pctime, /* [in] */
2174 const FILETIME *patime, /* [in] */
2175 const FILETIME *pmtime) /* [in] */
2177 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2181 /******************************************************************************
2182 * SetStateBits (IStorage)
2184 HRESULT WINAPI StorageImpl_SetStateBits(
2186 DWORD grfStateBits,/* [in] */
2187 DWORD grfMask) /* [in] */
2189 FIXME("not implemented!\n");
2194 * Virtual function table for the IStorage32Impl class.
2196 static const IStorageVtbl Storage32Impl_Vtbl =
2198 StorageBaseImpl_QueryInterface,
2199 StorageBaseImpl_AddRef,
2200 StorageBaseImpl_Release,
2201 StorageBaseImpl_CreateStream,
2202 StorageBaseImpl_OpenStream,
2203 StorageImpl_CreateStorage,
2204 StorageBaseImpl_OpenStorage,
2206 StorageImpl_MoveElementTo,
2209 StorageBaseImpl_EnumElements,
2210 StorageImpl_DestroyElement,
2211 StorageBaseImpl_RenameElement,
2212 StorageImpl_SetElementTimes,
2213 StorageBaseImpl_SetClass,
2214 StorageImpl_SetStateBits,
2218 HRESULT StorageImpl_Construct(
2228 StgProperty currentProperty;
2229 BOOL readSuccessful;
2230 ULONG currentPropertyIndex;
2232 if ( FAILED( validateSTGM(openFlags) ))
2233 return STG_E_INVALIDFLAG;
2235 memset(This, 0, sizeof(StorageImpl));
2238 * Initialize the virtual function table.
2240 This->base.lpVtbl = &Storage32Impl_Vtbl;
2241 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2242 This->base.v_destructor = &StorageImpl_Destroy;
2243 This->base.openFlags = openFlags;
2246 * This is the top-level storage so initialize the ancestor pointer
2249 This->base.ancestorStorage = This;
2252 * Initialize the physical support of the storage.
2254 This->hFile = hFile;
2257 * Store copy of file path.
2260 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2261 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2262 if (!This->pwcsName)
2263 return STG_E_INSUFFICIENTMEMORY;
2264 strcpyW(This->pwcsName, pwcsName);
2268 * Initialize the big block cache.
2270 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2271 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2272 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2278 if (This->bigBlockFile == 0)
2283 ULARGE_INTEGER size;
2284 BYTE* bigBlockBuffer;
2287 * Initialize all header variables:
2288 * - The big block depot consists of one block and it is at block 0
2289 * - The properties start at block 1
2290 * - There is no small block depot
2292 memset( This->bigBlockDepotStart,
2294 sizeof(This->bigBlockDepotStart));
2296 This->bigBlockDepotCount = 1;
2297 This->bigBlockDepotStart[0] = 0;
2298 This->rootStartBlock = 1;
2299 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2300 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2301 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2302 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2303 This->extBigBlockDepotCount = 0;
2305 StorageImpl_SaveFileHeader(This);
2308 * Add one block for the big block depot and one block for the properties
2310 size.u.HighPart = 0;
2311 size.u.LowPart = This->bigBlockSize * 3;
2312 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2315 * Initialize the big block depot
2317 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2318 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2319 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2320 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2321 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2326 * Load the header for the file.
2328 hr = StorageImpl_LoadFileHeader(This);
2332 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2339 * There is no block depot cached yet.
2341 This->indexBlockDepotCached = 0xFFFFFFFF;
2344 * Start searching for free blocks with block 0.
2346 This->prevFreeBlock = 0;
2349 * Create the block chain abstractions.
2351 if(!(This->rootBlockChain =
2352 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2353 return STG_E_READFAULT;
2355 if(!(This->smallBlockDepotChain =
2356 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2358 return STG_E_READFAULT;
2361 * Write the root property
2365 StgProperty rootProp;
2367 * Initialize the property chain
2369 memset(&rootProp, 0, sizeof(rootProp));
2370 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2371 sizeof(rootProp.name)/sizeof(WCHAR) );
2372 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2373 rootProp.propertyType = PROPTYPE_ROOT;
2374 rootProp.previousProperty = PROPERTY_NULL;
2375 rootProp.nextProperty = PROPERTY_NULL;
2376 rootProp.dirProperty = PROPERTY_NULL;
2377 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2378 rootProp.size.u.HighPart = 0;
2379 rootProp.size.u.LowPart = 0;
2381 StorageImpl_WriteProperty(This, 0, &rootProp);
2385 * Find the ID of the root in the property sets.
2387 currentPropertyIndex = 0;
2391 readSuccessful = StorageImpl_ReadProperty(
2393 currentPropertyIndex,
2398 if ( (currentProperty.sizeOfNameString != 0 ) &&
2399 (currentProperty.propertyType == PROPTYPE_ROOT) )
2401 This->base.rootPropertySetIndex = currentPropertyIndex;
2405 currentPropertyIndex++;
2407 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2409 if (!readSuccessful)
2412 return STG_E_READFAULT;
2416 * Create the block chain abstraction for the small block root chain.
2418 if(!(This->smallBlockRootChain =
2419 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2420 return STG_E_READFAULT;
2425 void StorageImpl_Destroy(StorageBaseImpl* iface)
2427 StorageImpl *This = (StorageImpl*) iface;
2428 TRACE("(%p)\n", This);
2430 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2432 BlockChainStream_Destroy(This->smallBlockRootChain);
2433 BlockChainStream_Destroy(This->rootBlockChain);
2434 BlockChainStream_Destroy(This->smallBlockDepotChain);
2436 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2437 HeapFree(GetProcessHeap(), 0, This);
2440 /******************************************************************************
2441 * Storage32Impl_GetNextFreeBigBlock
2443 * Returns the index of the next free big block.
2444 * If the big block depot is filled, this method will enlarge it.
2447 ULONG StorageImpl_GetNextFreeBigBlock(
2450 ULONG depotBlockIndexPos;
2452 ULONG depotBlockOffset;
2453 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2454 ULONG nextBlockIndex = BLOCK_SPECIAL;
2456 ULONG freeBlock = BLOCK_UNUSED;
2458 depotIndex = This->prevFreeBlock / blocksPerDepot;
2459 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2462 * Scan the entire big block depot until we find a block marked free
2464 while (nextBlockIndex != BLOCK_UNUSED)
2466 if (depotIndex < COUNT_BBDEPOTINHEADER)
2468 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2471 * Grow the primary depot.
2473 if (depotBlockIndexPos == BLOCK_UNUSED)
2475 depotBlockIndexPos = depotIndex*blocksPerDepot;
2478 * Add a block depot.
2480 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2481 This->bigBlockDepotCount++;
2482 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2485 * Flag it as a block depot.
2487 StorageImpl_SetNextBlockInChain(This,
2491 /* Save new header information.
2493 StorageImpl_SaveFileHeader(This);
2498 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2500 if (depotBlockIndexPos == BLOCK_UNUSED)
2503 * Grow the extended depot.
2505 ULONG extIndex = BLOCK_UNUSED;
2506 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2507 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2509 if (extBlockOffset == 0)
2511 /* We need an extended block.
2513 extIndex = Storage32Impl_AddExtBlockDepot(This);
2514 This->extBigBlockDepotCount++;
2515 depotBlockIndexPos = extIndex + 1;
2518 depotBlockIndexPos = depotIndex * blocksPerDepot;
2521 * Add a block depot and mark it in the extended block.
2523 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2524 This->bigBlockDepotCount++;
2525 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2527 /* Flag the block depot.
2529 StorageImpl_SetNextBlockInChain(This,
2533 /* If necessary, flag the extended depot block.
2535 if (extIndex != BLOCK_UNUSED)
2536 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2538 /* Save header information.
2540 StorageImpl_SaveFileHeader(This);
2544 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2546 if (depotBuffer != 0)
2548 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2549 ( nextBlockIndex != BLOCK_UNUSED))
2551 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2553 if (nextBlockIndex == BLOCK_UNUSED)
2555 freeBlock = (depotIndex * blocksPerDepot) +
2556 (depotBlockOffset/sizeof(ULONG));
2559 depotBlockOffset += sizeof(ULONG);
2562 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2566 depotBlockOffset = 0;
2569 This->prevFreeBlock = freeBlock;
2574 /******************************************************************************
2575 * Storage32Impl_AddBlockDepot
2577 * This will create a depot block, essentially it is a block initialized
2580 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2584 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2587 * Initialize blocks as free
2589 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2591 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2594 /******************************************************************************
2595 * Storage32Impl_GetExtDepotBlock
2597 * Returns the index of the block that corresponds to the specified depot
2598 * index. This method is only for depot indexes equal or greater than
2599 * COUNT_BBDEPOTINHEADER.
2601 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2603 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2604 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2605 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2606 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2607 ULONG blockIndex = BLOCK_UNUSED;
2608 ULONG extBlockIndex = This->extBigBlockDepotStart;
2610 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2612 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2613 return BLOCK_UNUSED;
2615 while (extBlockCount > 0)
2617 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2621 if (extBlockIndex != BLOCK_UNUSED)
2625 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2627 if (depotBuffer != 0)
2629 StorageUtl_ReadDWord(depotBuffer,
2630 extBlockOffset * sizeof(ULONG),
2633 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2640 /******************************************************************************
2641 * Storage32Impl_SetExtDepotBlock
2643 * Associates the specified block index to the specified depot index.
2644 * This method is only for depot indexes equal or greater than
2645 * COUNT_BBDEPOTINHEADER.
2647 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2651 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2652 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2653 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2654 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2655 ULONG extBlockIndex = This->extBigBlockDepotStart;
2657 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2659 while (extBlockCount > 0)
2661 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2665 if (extBlockIndex != BLOCK_UNUSED)
2669 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2671 if (depotBuffer != 0)
2673 StorageUtl_WriteDWord(depotBuffer,
2674 extBlockOffset * sizeof(ULONG),
2677 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2682 /******************************************************************************
2683 * Storage32Impl_AddExtBlockDepot
2685 * Creates an extended depot block.
2687 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2689 ULONG numExtBlocks = This->extBigBlockDepotCount;
2690 ULONG nextExtBlock = This->extBigBlockDepotStart;
2691 BYTE* depotBuffer = NULL;
2692 ULONG index = BLOCK_UNUSED;
2693 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2694 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2695 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2697 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2698 blocksPerDepotBlock;
2700 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2703 * The first extended block.
2705 This->extBigBlockDepotStart = index;
2711 * Follow the chain to the last one.
2713 for (i = 0; i < (numExtBlocks - 1); i++)
2715 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2719 * Add the new extended block to the chain.
2721 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2722 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2723 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2727 * Initialize this block.
2729 depotBuffer = StorageImpl_GetBigBlock(This, index);
2730 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2731 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2736 /******************************************************************************
2737 * Storage32Impl_FreeBigBlock
2739 * This method will flag the specified block as free in the big block depot.
2741 void StorageImpl_FreeBigBlock(
2745 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2747 if (blockIndex < This->prevFreeBlock)
2748 This->prevFreeBlock = blockIndex;
2751 /************************************************************************
2752 * Storage32Impl_GetNextBlockInChain
2754 * This method will retrieve the block index of the next big block in
2757 * Params: This - Pointer to the Storage object.
2758 * blockIndex - Index of the block to retrieve the chain
2760 * nextBlockIndex - receives the return value.
2762 * Returns: This method returns the index of the next block in the chain.
2763 * It will return the constants:
2764 * BLOCK_SPECIAL - If the block given was not part of a
2766 * BLOCK_END_OF_CHAIN - If the block given was the last in
2768 * BLOCK_UNUSED - If the block given was not past of a chain
2770 * BLOCK_EXTBBDEPOT - This block is part of the extended
2773 * See Windows documentation for more details on IStorage methods.
2775 HRESULT StorageImpl_GetNextBlockInChain(
2778 ULONG* nextBlockIndex)
2780 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2781 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2782 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2784 ULONG depotBlockIndexPos;
2787 *nextBlockIndex = BLOCK_SPECIAL;
2789 if(depotBlockCount >= This->bigBlockDepotCount)
2791 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2792 This->bigBlockDepotCount);
2793 return STG_E_READFAULT;
2797 * Cache the currently accessed depot block.
2799 if (depotBlockCount != This->indexBlockDepotCached)
2801 This->indexBlockDepotCached = depotBlockCount;
2803 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2805 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2810 * We have to look in the extended depot.
2812 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2815 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2818 return STG_E_READFAULT;
2820 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2822 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2823 This->blockDepotCached[index] = *nextBlockIndex;
2825 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2828 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2833 /******************************************************************************
2834 * Storage32Impl_GetNextExtendedBlock
2836 * Given an extended block this method will return the next extended block.
2839 * The last ULONG of an extended block is the block index of the next
2840 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2844 * - The index of the next extended block
2845 * - BLOCK_UNUSED: there is no next extended block.
2846 * - Any other return values denotes failure.
2848 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2850 ULONG nextBlockIndex = BLOCK_SPECIAL;
2851 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2854 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2858 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2860 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2863 return nextBlockIndex;
2866 /******************************************************************************
2867 * Storage32Impl_SetNextBlockInChain
2869 * This method will write the index of the specified block's next block
2870 * in the big block depot.
2872 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2875 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2876 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2877 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2880 void StorageImpl_SetNextBlockInChain(
2885 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2886 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2887 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2888 ULONG depotBlockIndexPos;
2891 assert(depotBlockCount < This->bigBlockDepotCount);
2892 assert(blockIndex != nextBlock);
2894 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2896 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2901 * We have to look in the extended depot.
2903 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2906 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2910 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2911 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2915 * Update the cached block depot, if necessary.
2917 if (depotBlockCount == This->indexBlockDepotCached)
2919 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2923 /******************************************************************************
2924 * Storage32Impl_LoadFileHeader
2926 * This method will read in the file header, i.e. big block index -1.
2928 HRESULT StorageImpl_LoadFileHeader(
2931 HRESULT hr = STG_E_FILENOTFOUND;
2932 void* headerBigBlock = NULL;
2936 * Get a pointer to the big block of data containing the header.
2938 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2941 * Extract the information from the header.
2943 if (headerBigBlock!=0)
2946 * Check for the "magic number" signature and return an error if it is not
2949 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2951 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2952 return STG_E_OLDFORMAT;
2955 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2957 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2958 return STG_E_INVALIDHEADER;
2961 StorageUtl_ReadWord(
2963 OFFSET_BIGBLOCKSIZEBITS,
2964 &This->bigBlockSizeBits);
2966 StorageUtl_ReadWord(
2968 OFFSET_SMALLBLOCKSIZEBITS,
2969 &This->smallBlockSizeBits);
2971 StorageUtl_ReadDWord(
2973 OFFSET_BBDEPOTCOUNT,
2974 &This->bigBlockDepotCount);
2976 StorageUtl_ReadDWord(
2978 OFFSET_ROOTSTARTBLOCK,
2979 &This->rootStartBlock);
2981 StorageUtl_ReadDWord(
2983 OFFSET_SBDEPOTSTART,
2984 &This->smallBlockDepotStart);
2986 StorageUtl_ReadDWord(
2988 OFFSET_EXTBBDEPOTSTART,
2989 &This->extBigBlockDepotStart);
2991 StorageUtl_ReadDWord(
2993 OFFSET_EXTBBDEPOTCOUNT,
2994 &This->extBigBlockDepotCount);
2996 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2998 StorageUtl_ReadDWord(
3000 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3001 &(This->bigBlockDepotStart[index]));
3005 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3009 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3010 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3014 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3015 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3019 * Right now, the code is making some assumptions about the size of the
3020 * blocks, just make sure they are what we're expecting.
3022 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3023 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3025 WARN("Broken OLE storage file\n");
3026 hr = STG_E_INVALIDHEADER;
3032 * Release the block.
3034 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3040 /******************************************************************************
3041 * Storage32Impl_SaveFileHeader
3043 * This method will save to the file the header, i.e. big block -1.
3045 void StorageImpl_SaveFileHeader(
3048 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3053 * Get a pointer to the big block of data containing the header.
3055 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3058 * If the block read failed, the file is probably new.
3063 * Initialize for all unknown fields.
3065 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3068 * Initialize the magic number.
3070 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3073 * And a bunch of things we don't know what they mean
3075 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3076 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3077 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3078 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3082 * Write the information to the header.
3084 StorageUtl_WriteWord(
3086 OFFSET_BIGBLOCKSIZEBITS,
3087 This->bigBlockSizeBits);
3089 StorageUtl_WriteWord(
3091 OFFSET_SMALLBLOCKSIZEBITS,
3092 This->smallBlockSizeBits);
3094 StorageUtl_WriteDWord(
3096 OFFSET_BBDEPOTCOUNT,
3097 This->bigBlockDepotCount);
3099 StorageUtl_WriteDWord(
3101 OFFSET_ROOTSTARTBLOCK,
3102 This->rootStartBlock);
3104 StorageUtl_WriteDWord(
3106 OFFSET_SBDEPOTSTART,
3107 This->smallBlockDepotStart);
3109 StorageUtl_WriteDWord(
3111 OFFSET_SBDEPOTCOUNT,
3112 This->smallBlockDepotChain ?
3113 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3115 StorageUtl_WriteDWord(
3117 OFFSET_EXTBBDEPOTSTART,
3118 This->extBigBlockDepotStart);
3120 StorageUtl_WriteDWord(
3122 OFFSET_EXTBBDEPOTCOUNT,
3123 This->extBigBlockDepotCount);
3125 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3127 StorageUtl_WriteDWord(
3129 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3130 (This->bigBlockDepotStart[index]));
3134 * Write the big block back to the file.
3136 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3139 /******************************************************************************
3140 * Storage32Impl_ReadProperty
3142 * This method will read the specified property from the property chain.
3144 BOOL StorageImpl_ReadProperty(
3147 StgProperty* buffer)
3149 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3150 ULARGE_INTEGER offsetInPropSet;
3151 BOOL readSuccessful;
3154 offsetInPropSet.u.HighPart = 0;
3155 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3157 readSuccessful = BlockChainStream_ReadAt(
3158 This->rootBlockChain,
3166 /* replace the name of root entry (often "Root Entry") by the file name */
3167 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3168 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3170 memset(buffer->name, 0, sizeof(buffer->name));
3174 PROPERTY_NAME_BUFFER_LEN );
3175 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3177 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3179 StorageUtl_ReadWord(
3181 OFFSET_PS_NAMELENGTH,
3182 &buffer->sizeOfNameString);
3184 StorageUtl_ReadDWord(
3186 OFFSET_PS_PREVIOUSPROP,
3187 &buffer->previousProperty);
3189 StorageUtl_ReadDWord(
3192 &buffer->nextProperty);
3194 StorageUtl_ReadDWord(
3197 &buffer->dirProperty);
3199 StorageUtl_ReadGUID(
3202 &buffer->propertyUniqueID);
3204 StorageUtl_ReadDWord(
3207 &buffer->timeStampS1);
3209 StorageUtl_ReadDWord(
3212 &buffer->timeStampD1);
3214 StorageUtl_ReadDWord(
3217 &buffer->timeStampS2);
3219 StorageUtl_ReadDWord(
3222 &buffer->timeStampD2);
3224 StorageUtl_ReadDWord(
3226 OFFSET_PS_STARTBLOCK,
3227 &buffer->startingBlock);
3229 StorageUtl_ReadDWord(
3232 &buffer->size.u.LowPart);
3234 buffer->size.u.HighPart = 0;
3237 return readSuccessful;
3240 /*********************************************************************
3241 * Write the specified property into the property chain
3243 BOOL StorageImpl_WriteProperty(
3246 StgProperty* buffer)
3248 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3249 ULARGE_INTEGER offsetInPropSet;
3250 BOOL writeSuccessful;
3253 offsetInPropSet.u.HighPart = 0;
3254 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3256 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3259 currentProperty + OFFSET_PS_NAME,
3261 PROPERTY_NAME_BUFFER_LEN );
3263 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3265 StorageUtl_WriteWord(
3267 OFFSET_PS_NAMELENGTH,
3268 buffer->sizeOfNameString);
3270 StorageUtl_WriteDWord(
3272 OFFSET_PS_PREVIOUSPROP,
3273 buffer->previousProperty);
3275 StorageUtl_WriteDWord(
3278 buffer->nextProperty);
3280 StorageUtl_WriteDWord(
3283 buffer->dirProperty);
3285 StorageUtl_WriteGUID(
3288 &buffer->propertyUniqueID);
3290 StorageUtl_WriteDWord(
3293 buffer->timeStampS1);
3295 StorageUtl_WriteDWord(
3298 buffer->timeStampD1);
3300 StorageUtl_WriteDWord(
3303 buffer->timeStampS2);
3305 StorageUtl_WriteDWord(
3308 buffer->timeStampD2);
3310 StorageUtl_WriteDWord(
3312 OFFSET_PS_STARTBLOCK,
3313 buffer->startingBlock);
3315 StorageUtl_WriteDWord(
3318 buffer->size.u.LowPart);
3320 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3325 return writeSuccessful;
3328 BOOL StorageImpl_ReadBigBlock(
3333 void* bigBlockBuffer;
3335 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3337 if (bigBlockBuffer!=0)
3339 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3341 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3349 BOOL StorageImpl_WriteBigBlock(
3354 void* bigBlockBuffer;
3356 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3358 if (bigBlockBuffer!=0)
3360 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3362 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3370 void* StorageImpl_GetROBigBlock(
3374 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3377 void* StorageImpl_GetBigBlock(
3381 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3384 void StorageImpl_ReleaseBigBlock(
3388 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3391 /******************************************************************************
3392 * Storage32Impl_SmallBlocksToBigBlocks
3394 * This method will convert a small block chain to a big block chain.
3395 * The small block chain will be destroyed.
3397 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3399 SmallBlockChainStream** ppsbChain)
3401 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3402 ULARGE_INTEGER size, offset;
3403 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3404 ULONG propertyIndex;
3405 BOOL successRead, successWrite;
3406 StgProperty chainProperty;
3408 BlockChainStream *bbTempChain = NULL;
3409 BlockChainStream *bigBlockChain = NULL;
3412 * Create a temporary big block chain that doesn't have
3413 * an associated property. This temporary chain will be
3414 * used to copy data from small blocks to big blocks.
3416 bbTempChain = BlockChainStream_Construct(This,
3419 if(!bbTempChain) return NULL;
3421 * Grow the big block chain.
3423 size = SmallBlockChainStream_GetSize(*ppsbChain);
3424 BlockChainStream_SetSize(bbTempChain, size);
3427 * Copy the contents of the small block chain to the big block chain
3428 * by small block size increments.
3430 offset.u.LowPart = 0;
3431 offset.u.HighPart = 0;
3435 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3438 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3440 DEF_SMALL_BLOCK_SIZE,
3443 cbTotalRead += cbRead;
3445 successWrite = BlockChainStream_WriteAt(bbTempChain,
3450 cbTotalWritten += cbWritten;
3452 offset.u.LowPart += This->smallBlockSize;
3454 } while (successRead && successWrite);
3455 HeapFree(GetProcessHeap(),0,buffer);
3457 assert(cbTotalRead == cbTotalWritten);
3460 * Destroy the small block chain.
3462 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3463 size.u.HighPart = 0;
3465 SmallBlockChainStream_SetSize(*ppsbChain, size);
3466 SmallBlockChainStream_Destroy(*ppsbChain);
3470 * Change the property information. This chain is now a big block chain
3471 * and it doesn't reside in the small blocks chain anymore.
3473 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3475 chainProperty.startingBlock = bbHeadOfChain;
3477 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3480 * Destroy the temporary propertyless big block chain.
3481 * Create a new big block chain associated with this property.
3483 BlockChainStream_Destroy(bbTempChain);
3484 bigBlockChain = BlockChainStream_Construct(This,
3488 return bigBlockChain;
3491 void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3493 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3495 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3496 HeapFree(GetProcessHeap(), 0, This);
3499 /******************************************************************************
3501 ** Storage32InternalImpl_Commit
3503 ** The non-root storages cannot be opened in transacted mode thus this function
3506 HRESULT WINAPI StorageInternalImpl_Commit(
3508 DWORD grfCommitFlags) /* [in] */
3513 /******************************************************************************
3515 ** Storage32InternalImpl_Revert
3517 ** The non-root storages cannot be opened in transacted mode thus this function
3520 HRESULT WINAPI StorageInternalImpl_Revert(
3526 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3528 IStorage_Release((IStorage*)This->parentStorage);
3529 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3530 HeapFree(GetProcessHeap(), 0, This);
3533 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3534 IEnumSTATSTG* iface,
3538 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3541 * Perform a sanity check on the parameters.
3544 return E_INVALIDARG;
3547 * Initialize the return parameter.
3552 * Compare the riid with the interface IDs implemented by this object.
3554 if (IsEqualGUID(&IID_IUnknown, riid) ||
3555 IsEqualGUID(&IID_IStorage, riid))
3557 *ppvObject = (IEnumSTATSTG*)This;
3558 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3562 return E_NOINTERFACE;
3565 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3566 IEnumSTATSTG* iface)
3568 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3569 return InterlockedIncrement(&This->ref);
3572 ULONG WINAPI IEnumSTATSTGImpl_Release(
3573 IEnumSTATSTG* iface)
3575 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3579 newRef = InterlockedDecrement(&This->ref);
3582 * If the reference count goes down to 0, perform suicide.
3586 IEnumSTATSTGImpl_Destroy(This);
3592 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3593 IEnumSTATSTG* iface,
3596 ULONG* pceltFetched)
3598 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3600 StgProperty currentProperty;
3601 STATSTG* currentReturnStruct = rgelt;
3602 ULONG objectFetched = 0;
3603 ULONG currentSearchNode;
3606 * Perform a sanity check on the parameters.
3608 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3609 return E_INVALIDARG;
3612 * To avoid the special case, get another pointer to a ULONG value if
3613 * the caller didn't supply one.
3615 if (pceltFetched==0)
3616 pceltFetched = &objectFetched;
3619 * Start the iteration, we will iterate until we hit the end of the
3620 * linked list or until we hit the number of items to iterate through
3625 * Start with the node at the top of the stack.
3627 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3629 while ( ( *pceltFetched < celt) &&
3630 ( currentSearchNode!=PROPERTY_NULL) )
3633 * Remove the top node from the stack
3635 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3638 * Read the property from the storage.
3640 StorageImpl_ReadProperty(This->parentStorage,
3645 * Copy the information to the return buffer.
3647 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3652 * Step to the next item in the iteration
3655 currentReturnStruct++;
3658 * Push the next search node in the search stack.
3660 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3663 * continue the iteration.
3665 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3668 if (*pceltFetched == celt)
3675 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3676 IEnumSTATSTG* iface,
3679 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3681 StgProperty currentProperty;
3682 ULONG objectFetched = 0;
3683 ULONG currentSearchNode;
3686 * Start with the node at the top of the stack.
3688 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3690 while ( (objectFetched < celt) &&
3691 (currentSearchNode!=PROPERTY_NULL) )
3694 * Remove the top node from the stack
3696 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3699 * Read the property from the storage.
3701 StorageImpl_ReadProperty(This->parentStorage,
3706 * Step to the next item in the iteration
3711 * Push the next search node in the search stack.
3713 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3716 * continue the iteration.
3718 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3721 if (objectFetched == celt)
3727 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3728 IEnumSTATSTG* iface)
3730 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3732 StgProperty rootProperty;
3733 BOOL readSuccessful;
3736 * Re-initialize the search stack to an empty stack
3738 This->stackSize = 0;
3741 * Read the root property from the storage.
3743 readSuccessful = StorageImpl_ReadProperty(
3744 This->parentStorage,
3745 This->firstPropertyNode,
3750 assert(rootProperty.sizeOfNameString!=0);
3753 * Push the search node in the search stack.
3755 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3761 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3762 IEnumSTATSTG* iface,
3763 IEnumSTATSTG** ppenum)
3765 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3767 IEnumSTATSTGImpl* newClone;
3770 * Perform a sanity check on the parameters.
3773 return E_INVALIDARG;
3775 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3776 This->firstPropertyNode);
3780 * The new clone enumeration must point to the same current node as
3783 newClone->stackSize = This->stackSize ;
3784 newClone->stackMaxSize = This->stackMaxSize ;
3785 newClone->stackToVisit =
3786 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3789 newClone->stackToVisit,
3791 sizeof(ULONG) * newClone->stackSize);
3793 *ppenum = (IEnumSTATSTG*)newClone;
3796 * Don't forget to nail down a reference to the clone before
3799 IEnumSTATSTGImpl_AddRef(*ppenum);
3804 INT IEnumSTATSTGImpl_FindParentProperty(
3805 IEnumSTATSTGImpl *This,
3806 ULONG childProperty,
3807 StgProperty *currentProperty,
3810 ULONG currentSearchNode;
3814 * To avoid the special case, get another pointer to a ULONG value if
3815 * the caller didn't supply one.
3818 thisNodeId = &foundNode;
3821 * Start with the node at the top of the stack.
3823 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3826 while (currentSearchNode!=PROPERTY_NULL)
3829 * Store the current node in the returned parameters
3831 *thisNodeId = currentSearchNode;
3834 * Remove the top node from the stack
3836 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3839 * Read the property from the storage.
3841 StorageImpl_ReadProperty(
3842 This->parentStorage,
3846 if (currentProperty->previousProperty == childProperty)
3847 return PROPERTY_RELATION_PREVIOUS;
3849 else if (currentProperty->nextProperty == childProperty)
3850 return PROPERTY_RELATION_NEXT;
3852 else if (currentProperty->dirProperty == childProperty)
3853 return PROPERTY_RELATION_DIR;
3856 * Push the next search node in the search stack.
3858 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3861 * continue the iteration.
3863 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3866 return PROPERTY_NULL;
3869 ULONG IEnumSTATSTGImpl_FindProperty(
3870 IEnumSTATSTGImpl* This,
3871 const OLECHAR* lpszPropName,
3872 StgProperty* currentProperty)
3874 ULONG currentSearchNode;
3877 * Start with the node at the top of the stack.
3879 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3881 while (currentSearchNode!=PROPERTY_NULL)
3884 * Remove the top node from the stack
3886 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3889 * Read the property from the storage.
3891 StorageImpl_ReadProperty(This->parentStorage,
3895 if ( propertyNameCmp(
3896 (const OLECHAR*)currentProperty->name,
3897 (const OLECHAR*)lpszPropName) == 0)
3898 return currentSearchNode;
3901 * Push the next search node in the search stack.
3903 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3906 * continue the iteration.
3908 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3911 return PROPERTY_NULL;
3914 void IEnumSTATSTGImpl_PushSearchNode(
3915 IEnumSTATSTGImpl* This,
3918 StgProperty rootProperty;
3919 BOOL readSuccessful;
3922 * First, make sure we're not trying to push an unexisting node.
3924 if (nodeToPush==PROPERTY_NULL)
3928 * First push the node to the stack
3930 if (This->stackSize == This->stackMaxSize)
3932 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3934 This->stackToVisit = HeapReAlloc(
3938 sizeof(ULONG) * This->stackMaxSize);
3941 This->stackToVisit[This->stackSize] = nodeToPush;
3945 * Read the root property from the storage.
3947 readSuccessful = StorageImpl_ReadProperty(
3948 This->parentStorage,
3954 assert(rootProperty.sizeOfNameString!=0);
3957 * Push the previous search node in the search stack.
3959 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3963 ULONG IEnumSTATSTGImpl_PopSearchNode(
3964 IEnumSTATSTGImpl* This,
3969 if (This->stackSize == 0)
3970 return PROPERTY_NULL;
3972 topNode = This->stackToVisit[This->stackSize-1];
3981 * Virtual function table for the IEnumSTATSTGImpl class.
3983 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
3985 IEnumSTATSTGImpl_QueryInterface,
3986 IEnumSTATSTGImpl_AddRef,
3987 IEnumSTATSTGImpl_Release,
3988 IEnumSTATSTGImpl_Next,
3989 IEnumSTATSTGImpl_Skip,
3990 IEnumSTATSTGImpl_Reset,
3991 IEnumSTATSTGImpl_Clone
3994 /******************************************************************************
3995 ** IEnumSTATSTGImpl implementation
3998 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3999 StorageImpl* parentStorage,
4000 ULONG firstPropertyNode)
4002 IEnumSTATSTGImpl* newEnumeration;
4004 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4006 if (newEnumeration!=0)
4009 * Set-up the virtual function table and reference count.
4011 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4012 newEnumeration->ref = 0;
4015 * We want to nail-down the reference to the storage in case the
4016 * enumeration out-lives the storage in the client application.
4018 newEnumeration->parentStorage = parentStorage;
4019 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4021 newEnumeration->firstPropertyNode = firstPropertyNode;
4024 * Initialize the search stack
4026 newEnumeration->stackSize = 0;
4027 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4028 newEnumeration->stackToVisit =
4029 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4032 * Make sure the current node of the iterator is the first one.
4034 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4037 return newEnumeration;
4041 * Virtual function table for the Storage32InternalImpl class.
4043 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4045 StorageBaseImpl_QueryInterface,
4046 StorageBaseImpl_AddRef,
4047 StorageBaseImpl_Release,
4048 StorageBaseImpl_CreateStream,
4049 StorageBaseImpl_OpenStream,
4050 StorageImpl_CreateStorage,
4051 StorageBaseImpl_OpenStorage,
4053 StorageImpl_MoveElementTo,
4054 StorageInternalImpl_Commit,
4055 StorageInternalImpl_Revert,
4056 StorageBaseImpl_EnumElements,
4057 StorageImpl_DestroyElement,
4058 StorageBaseImpl_RenameElement,
4059 StorageImpl_SetElementTimes,
4060 StorageBaseImpl_SetClass,
4061 StorageImpl_SetStateBits,
4062 StorageBaseImpl_Stat
4065 /******************************************************************************
4066 ** Storage32InternalImpl implementation
4069 StorageInternalImpl* StorageInternalImpl_Construct(
4070 StorageImpl* ancestorStorage,
4072 ULONG rootPropertyIndex)
4074 StorageInternalImpl* newStorage;
4077 * Allocate space for the new storage object
4079 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
4083 memset(newStorage, 0, sizeof(StorageInternalImpl));
4086 * Initialize the virtual function table.
4088 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4089 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4090 newStorage->base.openFlags = openFlags;
4093 * Keep the ancestor storage pointer and nail a reference to it.
4095 newStorage->base.ancestorStorage = ancestorStorage;
4096 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4099 * Keep the index of the root property set for this storage,
4101 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4109 /******************************************************************************
4110 ** StorageUtl implementation
4113 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4117 memcpy(&tmp, buffer+offset, sizeof(WORD));
4118 *value = le16toh(tmp);
4121 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4123 value = htole16(value);
4124 memcpy(buffer+offset, &value, sizeof(WORD));
4127 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4131 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4132 *value = le32toh(tmp);
4135 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4137 value = htole32(value);
4138 memcpy(buffer+offset, &value, sizeof(DWORD));
4141 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4142 ULARGE_INTEGER* value)
4144 #ifdef WORDS_BIGENDIAN
4147 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4148 value->u.LowPart = htole32(tmp.u.HighPart);
4149 value->u.HighPart = htole32(tmp.u.LowPart);
4151 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4155 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4156 const ULARGE_INTEGER *value)
4158 #ifdef WORDS_BIGENDIAN
4161 tmp.u.LowPart = htole32(value->u.HighPart);
4162 tmp.u.HighPart = htole32(value->u.LowPart);
4163 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4165 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4169 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4171 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4172 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4173 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4175 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4178 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4180 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4181 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4182 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4184 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4187 void StorageUtl_CopyPropertyToSTATSTG(
4188 STATSTG* destination,
4189 StgProperty* source,
4193 * The copy of the string occurs only when the flag is not set
4195 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4196 (source->name == NULL) ||
4197 (source->name[0] == 0) )
4199 destination->pwcsName = 0;
4203 destination->pwcsName =
4204 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4206 strcpyW((LPWSTR)destination->pwcsName, source->name);
4209 switch (source->propertyType)
4211 case PROPTYPE_STORAGE:
4213 destination->type = STGTY_STORAGE;
4215 case PROPTYPE_STREAM:
4216 destination->type = STGTY_STREAM;
4219 destination->type = STGTY_STREAM;
4223 destination->cbSize = source->size;
4225 currentReturnStruct->mtime = {0}; TODO
4226 currentReturnStruct->ctime = {0};
4227 currentReturnStruct->atime = {0};
4229 destination->grfMode = 0;
4230 destination->grfLocksSupported = 0;
4231 destination->clsid = source->propertyUniqueID;
4232 destination->grfStateBits = 0;
4233 destination->reserved = 0;
4236 /******************************************************************************
4237 ** BlockChainStream implementation
4240 BlockChainStream* BlockChainStream_Construct(
4241 StorageImpl* parentStorage,
4242 ULONG* headOfStreamPlaceHolder,
4243 ULONG propertyIndex)
4245 BlockChainStream* newStream;
4248 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4250 newStream->parentStorage = parentStorage;
4251 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4252 newStream->ownerPropertyIndex = propertyIndex;
4253 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4254 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4255 newStream->numBlocks = 0;
4257 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4259 while (blockIndex != BLOCK_END_OF_CHAIN)
4261 newStream->numBlocks++;
4262 newStream->tailIndex = blockIndex;
4264 if(FAILED(StorageImpl_GetNextBlockInChain(
4269 HeapFree(GetProcessHeap(), 0, newStream);
4277 void BlockChainStream_Destroy(BlockChainStream* This)
4279 HeapFree(GetProcessHeap(), 0, This);
4282 /******************************************************************************
4283 * BlockChainStream_GetHeadOfChain
4285 * Returns the head of this stream chain.
4286 * Some special chains don't have properties, their heads are kept in
4287 * This->headOfStreamPlaceHolder.
4290 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4292 StgProperty chainProperty;
4293 BOOL readSuccessful;
4295 if (This->headOfStreamPlaceHolder != 0)
4296 return *(This->headOfStreamPlaceHolder);
4298 if (This->ownerPropertyIndex != PROPERTY_NULL)
4300 readSuccessful = StorageImpl_ReadProperty(
4301 This->parentStorage,
4302 This->ownerPropertyIndex,
4307 return chainProperty.startingBlock;
4311 return BLOCK_END_OF_CHAIN;
4314 /******************************************************************************
4315 * BlockChainStream_GetCount
4317 * Returns the number of blocks that comprises this chain.
4318 * This is not the size of the stream as the last block may not be full!
4321 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4326 blockIndex = BlockChainStream_GetHeadOfChain(This);
4328 while (blockIndex != BLOCK_END_OF_CHAIN)
4332 if(FAILED(StorageImpl_GetNextBlockInChain(
4333 This->parentStorage,
4342 /******************************************************************************
4343 * BlockChainStream_ReadAt
4345 * Reads a specified number of bytes from this chain at the specified offset.
4346 * bytesRead may be NULL.
4347 * Failure will be returned if the specified number of bytes has not been read.
4349 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4350 ULARGE_INTEGER offset,
4355 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4356 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4357 ULONG bytesToReadInBuffer;
4360 BYTE* bigBlockBuffer;
4363 * Find the first block in the stream that contains part of the buffer.
4365 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4366 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4367 (blockNoInSequence < This->lastBlockNoInSequence) )
4369 blockIndex = BlockChainStream_GetHeadOfChain(This);
4370 This->lastBlockNoInSequence = blockNoInSequence;
4374 ULONG temp = blockNoInSequence;
4376 blockIndex = This->lastBlockNoInSequenceIndex;
4377 blockNoInSequence -= This->lastBlockNoInSequence;
4378 This->lastBlockNoInSequence = temp;
4381 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4383 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4385 blockNoInSequence--;
4388 This->lastBlockNoInSequenceIndex = blockIndex;
4391 * Start reading the buffer.
4394 bufferWalker = buffer;
4396 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4399 * Calculate how many bytes we can copy from this big block.
4401 bytesToReadInBuffer =
4402 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4405 * Copy those bytes to the buffer
4408 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4410 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4412 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4415 * Step to the next big block.
4417 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4420 bufferWalker += bytesToReadInBuffer;
4421 size -= bytesToReadInBuffer;
4422 *bytesRead += bytesToReadInBuffer;
4423 offsetInBlock = 0; /* There is no offset on the next block */
4430 /******************************************************************************
4431 * BlockChainStream_WriteAt
4433 * Writes the specified number of bytes to this chain at the specified offset.
4434 * bytesWritten may be NULL.
4435 * Will fail if not all specified number of bytes have been written.
4437 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4438 ULARGE_INTEGER offset,
4441 ULONG* bytesWritten)
4443 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4444 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4447 const BYTE* bufferWalker;
4448 BYTE* bigBlockBuffer;
4451 * Find the first block in the stream that contains part of the buffer.
4453 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4454 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4455 (blockNoInSequence < This->lastBlockNoInSequence) )
4457 blockIndex = BlockChainStream_GetHeadOfChain(This);
4458 This->lastBlockNoInSequence = blockNoInSequence;
4462 ULONG temp = blockNoInSequence;
4464 blockIndex = This->lastBlockNoInSequenceIndex;
4465 blockNoInSequence -= This->lastBlockNoInSequence;
4466 This->lastBlockNoInSequence = temp;
4469 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4471 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4474 blockNoInSequence--;
4477 This->lastBlockNoInSequenceIndex = blockIndex;
4480 * Here, I'm casting away the constness on the buffer variable
4481 * This is OK since we don't intend to modify that buffer.
4484 bufferWalker = (const BYTE*)buffer;
4486 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4489 * Calculate how many bytes we can copy from this big block.
4492 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4495 * Copy those bytes to the buffer
4497 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4499 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4501 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4504 * Step to the next big block.
4506 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4509 bufferWalker += bytesToWrite;
4510 size -= bytesToWrite;
4511 *bytesWritten += bytesToWrite;
4512 offsetInBlock = 0; /* There is no offset on the next block */
4518 /******************************************************************************
4519 * BlockChainStream_Shrink
4521 * Shrinks this chain in the big block depot.
4523 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4524 ULARGE_INTEGER newSize)
4526 ULONG blockIndex, extraBlock;
4531 * Reset the last accessed block cache.
4533 This->lastBlockNoInSequence = 0xFFFFFFFF;
4534 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4537 * Figure out how many blocks are needed to contain the new size
4539 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4541 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4544 blockIndex = BlockChainStream_GetHeadOfChain(This);
4547 * Go to the new end of chain
4549 while (count < numBlocks)
4551 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4557 /* Get the next block before marking the new end */
4558 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4562 /* Mark the new end of chain */
4563 StorageImpl_SetNextBlockInChain(
4564 This->parentStorage,
4566 BLOCK_END_OF_CHAIN);
4568 This->tailIndex = blockIndex;
4569 This->numBlocks = numBlocks;
4572 * Mark the extra blocks as free
4574 while (extraBlock != BLOCK_END_OF_CHAIN)
4576 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4579 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4580 extraBlock = blockIndex;
4586 /******************************************************************************
4587 * BlockChainStream_Enlarge
4589 * Grows this chain in the big block depot.
4591 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4592 ULARGE_INTEGER newSize)
4594 ULONG blockIndex, currentBlock;
4596 ULONG oldNumBlocks = 0;
4598 blockIndex = BlockChainStream_GetHeadOfChain(This);
4601 * Empty chain. Create the head.
4603 if (blockIndex == BLOCK_END_OF_CHAIN)
4605 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4606 StorageImpl_SetNextBlockInChain(This->parentStorage,
4608 BLOCK_END_OF_CHAIN);
4610 if (This->headOfStreamPlaceHolder != 0)
4612 *(This->headOfStreamPlaceHolder) = blockIndex;
4616 StgProperty chainProp;
4617 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4619 StorageImpl_ReadProperty(
4620 This->parentStorage,
4621 This->ownerPropertyIndex,
4624 chainProp.startingBlock = blockIndex;
4626 StorageImpl_WriteProperty(
4627 This->parentStorage,
4628 This->ownerPropertyIndex,
4632 This->tailIndex = blockIndex;
4633 This->numBlocks = 1;
4637 * Figure out how many blocks are needed to contain this stream
4639 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4641 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4645 * Go to the current end of chain
4647 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4649 currentBlock = blockIndex;
4651 while (blockIndex != BLOCK_END_OF_CHAIN)
4654 currentBlock = blockIndex;
4656 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4661 This->tailIndex = currentBlock;
4664 currentBlock = This->tailIndex;
4665 oldNumBlocks = This->numBlocks;
4668 * Add new blocks to the chain
4670 if (oldNumBlocks < newNumBlocks)
4672 while (oldNumBlocks < newNumBlocks)
4674 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4676 StorageImpl_SetNextBlockInChain(
4677 This->parentStorage,
4681 StorageImpl_SetNextBlockInChain(
4682 This->parentStorage,
4684 BLOCK_END_OF_CHAIN);
4686 currentBlock = blockIndex;
4690 This->tailIndex = blockIndex;
4691 This->numBlocks = newNumBlocks;
4697 /******************************************************************************
4698 * BlockChainStream_SetSize
4700 * Sets the size of this stream. The big block depot will be updated.
4701 * The file will grow if we grow the chain.
4703 * TODO: Free the actual blocks in the file when we shrink the chain.
4704 * Currently, the blocks are still in the file. So the file size
4705 * doesn't shrink even if we shrink streams.
4707 BOOL BlockChainStream_SetSize(
4708 BlockChainStream* This,
4709 ULARGE_INTEGER newSize)
4711 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4713 if (newSize.u.LowPart == size.u.LowPart)
4716 if (newSize.u.LowPart < size.u.LowPart)
4718 BlockChainStream_Shrink(This, newSize);
4722 ULARGE_INTEGER fileSize =
4723 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4725 ULONG diff = newSize.u.LowPart - size.u.LowPart;
4728 * Make sure the file stays a multiple of blocksize
4730 if ((diff % This->parentStorage->bigBlockSize) != 0)
4731 diff += (This->parentStorage->bigBlockSize -
4732 (diff % This->parentStorage->bigBlockSize) );
4734 fileSize.u.LowPart += diff;
4735 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4737 BlockChainStream_Enlarge(This, newSize);
4743 /******************************************************************************
4744 * BlockChainStream_GetSize
4746 * Returns the size of this chain.
4747 * Will return the block count if this chain doesn't have a property.
4749 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4751 StgProperty chainProperty;
4753 if(This->headOfStreamPlaceHolder == NULL)
4756 * This chain is a data stream read the property and return
4757 * the appropriate size
4759 StorageImpl_ReadProperty(
4760 This->parentStorage,
4761 This->ownerPropertyIndex,
4764 return chainProperty.size;
4769 * this chain is a chain that does not have a property, figure out the
4770 * size by making the product number of used blocks times the
4773 ULARGE_INTEGER result;
4774 result.u.HighPart = 0;
4777 BlockChainStream_GetCount(This) *
4778 This->parentStorage->bigBlockSize;
4784 /******************************************************************************
4785 ** SmallBlockChainStream implementation
4788 SmallBlockChainStream* SmallBlockChainStream_Construct(
4789 StorageImpl* parentStorage,
4790 ULONG propertyIndex)
4792 SmallBlockChainStream* newStream;
4794 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4796 newStream->parentStorage = parentStorage;
4797 newStream->ownerPropertyIndex = propertyIndex;
4802 void SmallBlockChainStream_Destroy(
4803 SmallBlockChainStream* This)
4805 HeapFree(GetProcessHeap(), 0, This);
4808 /******************************************************************************
4809 * SmallBlockChainStream_GetHeadOfChain
4811 * Returns the head of this chain of small blocks.
4813 ULONG SmallBlockChainStream_GetHeadOfChain(
4814 SmallBlockChainStream* This)
4816 StgProperty chainProperty;
4817 BOOL readSuccessful;
4819 if (This->ownerPropertyIndex)
4821 readSuccessful = StorageImpl_ReadProperty(
4822 This->parentStorage,
4823 This->ownerPropertyIndex,
4828 return chainProperty.startingBlock;
4833 return BLOCK_END_OF_CHAIN;
4836 /******************************************************************************
4837 * SmallBlockChainStream_GetNextBlockInChain
4839 * Returns the index of the next small block in this chain.
4842 * - BLOCK_END_OF_CHAIN: end of this chain
4843 * - BLOCK_UNUSED: small block 'blockIndex' is free
4845 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4846 SmallBlockChainStream* This,
4848 ULONG* nextBlockInChain)
4850 ULARGE_INTEGER offsetOfBlockInDepot;
4855 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4857 offsetOfBlockInDepot.u.HighPart = 0;
4858 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4861 * Read those bytes in the buffer from the small block file.
4863 success = BlockChainStream_ReadAt(
4864 This->parentStorage->smallBlockDepotChain,
4865 offsetOfBlockInDepot,
4872 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4876 return STG_E_READFAULT;
4879 /******************************************************************************
4880 * SmallBlockChainStream_SetNextBlockInChain
4882 * Writes the index of the next block of the specified block in the small
4884 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4885 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4887 void SmallBlockChainStream_SetNextBlockInChain(
4888 SmallBlockChainStream* This,
4892 ULARGE_INTEGER offsetOfBlockInDepot;
4896 offsetOfBlockInDepot.u.HighPart = 0;
4897 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4899 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4902 * Read those bytes in the buffer from the small block file.
4904 BlockChainStream_WriteAt(
4905 This->parentStorage->smallBlockDepotChain,
4906 offsetOfBlockInDepot,
4912 /******************************************************************************
4913 * SmallBlockChainStream_FreeBlock
4915 * Flag small block 'blockIndex' as free in the small block depot.
4917 void SmallBlockChainStream_FreeBlock(
4918 SmallBlockChainStream* This,
4921 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4924 /******************************************************************************
4925 * SmallBlockChainStream_GetNextFreeBlock
4927 * Returns the index of a free small block. The small block depot will be
4928 * enlarged if necessary. The small block chain will also be enlarged if
4931 ULONG SmallBlockChainStream_GetNextFreeBlock(
4932 SmallBlockChainStream* This)
4934 ULARGE_INTEGER offsetOfBlockInDepot;
4937 ULONG blockIndex = 0;
4938 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4939 BOOL success = TRUE;
4940 ULONG smallBlocksPerBigBlock;
4942 offsetOfBlockInDepot.u.HighPart = 0;
4945 * Scan the small block depot for a free block
4947 while (nextBlockIndex != BLOCK_UNUSED)
4949 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4951 success = BlockChainStream_ReadAt(
4952 This->parentStorage->smallBlockDepotChain,
4953 offsetOfBlockInDepot,
4959 * If we run out of space for the small block depot, enlarge it
4963 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
4965 if (nextBlockIndex != BLOCK_UNUSED)
4971 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4973 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4974 ULONG nextBlock, newsbdIndex;
4975 BYTE* smallBlockDepot;
4977 nextBlock = sbdIndex;
4978 while (nextBlock != BLOCK_END_OF_CHAIN)
4980 sbdIndex = nextBlock;
4981 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4984 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4985 if (sbdIndex != BLOCK_END_OF_CHAIN)
4986 StorageImpl_SetNextBlockInChain(
4987 This->parentStorage,
4991 StorageImpl_SetNextBlockInChain(
4992 This->parentStorage,
4994 BLOCK_END_OF_CHAIN);
4997 * Initialize all the small blocks to free
5000 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
5002 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5003 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
5008 * We have just created the small block depot.
5010 StgProperty rootProp;
5014 * Save it in the header
5016 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5017 StorageImpl_SaveFileHeader(This->parentStorage);
5020 * And allocate the first big block that will contain small blocks
5023 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5025 StorageImpl_SetNextBlockInChain(
5026 This->parentStorage,
5028 BLOCK_END_OF_CHAIN);
5030 StorageImpl_ReadProperty(
5031 This->parentStorage,
5032 This->parentStorage->base.rootPropertySetIndex,
5035 rootProp.startingBlock = sbStartIndex;
5036 rootProp.size.u.HighPart = 0;
5037 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5039 StorageImpl_WriteProperty(
5040 This->parentStorage,
5041 This->parentStorage->base.rootPropertySetIndex,
5047 smallBlocksPerBigBlock =
5048 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5051 * Verify if we have to allocate big blocks to contain small blocks
5053 if (blockIndex % smallBlocksPerBigBlock == 0)
5055 StgProperty rootProp;
5056 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5058 StorageImpl_ReadProperty(
5059 This->parentStorage,
5060 This->parentStorage->base.rootPropertySetIndex,
5063 if (rootProp.size.u.LowPart <
5064 (blocksRequired * This->parentStorage->bigBlockSize))
5066 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5068 BlockChainStream_SetSize(
5069 This->parentStorage->smallBlockRootChain,
5072 StorageImpl_WriteProperty(
5073 This->parentStorage,
5074 This->parentStorage->base.rootPropertySetIndex,
5082 /******************************************************************************
5083 * SmallBlockChainStream_ReadAt
5085 * Reads a specified number of bytes from this chain at the specified offset.
5086 * bytesRead may be NULL.
5087 * Failure will be returned if the specified number of bytes has not been read.
5089 BOOL SmallBlockChainStream_ReadAt(
5090 SmallBlockChainStream* This,
5091 ULARGE_INTEGER offset,
5096 ULARGE_INTEGER offsetInBigBlockFile;
5097 ULONG blockNoInSequence =
5098 offset.u.LowPart / This->parentStorage->smallBlockSize;
5100 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5101 ULONG bytesToReadInBuffer;
5103 ULONG bytesReadFromBigBlockFile;
5107 * This should never happen on a small block file.
5109 assert(offset.u.HighPart==0);
5112 * Find the first block in the stream that contains part of the buffer.
5114 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5116 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5118 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5121 blockNoInSequence--;
5125 * Start reading the buffer.
5128 bufferWalker = buffer;
5130 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5133 * Calculate how many bytes we can copy from this small block.
5135 bytesToReadInBuffer =
5136 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5139 * Calculate the offset of the small block in the small block file.
5141 offsetInBigBlockFile.u.HighPart = 0;
5142 offsetInBigBlockFile.u.LowPart =
5143 blockIndex * This->parentStorage->smallBlockSize;
5145 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5148 * Read those bytes in the buffer from the small block file.
5150 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5151 offsetInBigBlockFile,
5152 bytesToReadInBuffer,
5154 &bytesReadFromBigBlockFile);
5156 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5159 * Step to the next big block.
5161 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5163 bufferWalker += bytesToReadInBuffer;
5164 size -= bytesToReadInBuffer;
5165 *bytesRead += bytesToReadInBuffer;
5166 offsetInBlock = 0; /* There is no offset on the next block */
5172 /******************************************************************************
5173 * SmallBlockChainStream_WriteAt
5175 * Writes the specified number of bytes to this chain at the specified offset.
5176 * bytesWritten may be NULL.
5177 * Will fail if not all specified number of bytes have been written.
5179 BOOL SmallBlockChainStream_WriteAt(
5180 SmallBlockChainStream* This,
5181 ULARGE_INTEGER offset,
5184 ULONG* bytesWritten)
5186 ULARGE_INTEGER offsetInBigBlockFile;
5187 ULONG blockNoInSequence =
5188 offset.u.LowPart / This->parentStorage->smallBlockSize;
5190 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5191 ULONG bytesToWriteInBuffer;
5193 ULONG bytesWrittenFromBigBlockFile;
5194 const BYTE* bufferWalker;
5197 * This should never happen on a small block file.
5199 assert(offset.u.HighPart==0);
5202 * Find the first block in the stream that contains part of the buffer.
5204 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5206 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5208 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5210 blockNoInSequence--;
5214 * Start writing the buffer.
5216 * Here, I'm casting away the constness on the buffer variable
5217 * This is OK since we don't intend to modify that buffer.
5220 bufferWalker = (const BYTE*)buffer;
5221 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5224 * Calculate how many bytes we can copy to this small block.
5226 bytesToWriteInBuffer =
5227 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5230 * Calculate the offset of the small block in the small block file.
5232 offsetInBigBlockFile.u.HighPart = 0;
5233 offsetInBigBlockFile.u.LowPart =
5234 blockIndex * This->parentStorage->smallBlockSize;
5236 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5239 * Write those bytes in the buffer to the small block file.
5241 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5242 offsetInBigBlockFile,
5243 bytesToWriteInBuffer,
5245 &bytesWrittenFromBigBlockFile);
5247 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5250 * Step to the next big block.
5252 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5255 bufferWalker += bytesToWriteInBuffer;
5256 size -= bytesToWriteInBuffer;
5257 *bytesWritten += bytesToWriteInBuffer;
5258 offsetInBlock = 0; /* There is no offset on the next block */
5264 /******************************************************************************
5265 * SmallBlockChainStream_Shrink
5267 * Shrinks this chain in the small block depot.
5269 BOOL SmallBlockChainStream_Shrink(
5270 SmallBlockChainStream* This,
5271 ULARGE_INTEGER newSize)
5273 ULONG blockIndex, extraBlock;
5277 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5279 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5282 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5285 * Go to the new end of chain
5287 while (count < numBlocks)
5289 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5296 * If the count is 0, we have a special case, the head of the chain was
5301 StgProperty chainProp;
5303 StorageImpl_ReadProperty(This->parentStorage,
5304 This->ownerPropertyIndex,
5307 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5309 StorageImpl_WriteProperty(This->parentStorage,
5310 This->ownerPropertyIndex,
5314 * We start freeing the chain at the head block.
5316 extraBlock = blockIndex;
5320 /* Get the next block before marking the new end */
5321 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5325 /* Mark the new end of chain */
5326 SmallBlockChainStream_SetNextBlockInChain(
5329 BLOCK_END_OF_CHAIN);
5333 * Mark the extra blocks as free
5335 while (extraBlock != BLOCK_END_OF_CHAIN)
5337 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5340 SmallBlockChainStream_FreeBlock(This, extraBlock);
5341 extraBlock = blockIndex;
5347 /******************************************************************************
5348 * SmallBlockChainStream_Enlarge
5350 * Grows this chain in the small block depot.
5352 BOOL SmallBlockChainStream_Enlarge(
5353 SmallBlockChainStream* This,
5354 ULARGE_INTEGER newSize)
5356 ULONG blockIndex, currentBlock;
5358 ULONG oldNumBlocks = 0;
5360 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5365 if (blockIndex == BLOCK_END_OF_CHAIN)
5368 StgProperty chainProp;
5370 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5373 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5375 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5378 blockIndex = chainProp.startingBlock;
5379 SmallBlockChainStream_SetNextBlockInChain(
5382 BLOCK_END_OF_CHAIN);
5385 currentBlock = blockIndex;
5388 * Figure out how many blocks are needed to contain this stream
5390 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5392 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5396 * Go to the current end of chain
5398 while (blockIndex != BLOCK_END_OF_CHAIN)
5401 currentBlock = blockIndex;
5402 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5407 * Add new blocks to the chain
5409 while (oldNumBlocks < newNumBlocks)
5411 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5412 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5414 SmallBlockChainStream_SetNextBlockInChain(
5417 BLOCK_END_OF_CHAIN);
5419 currentBlock = blockIndex;
5426 /******************************************************************************
5427 * SmallBlockChainStream_GetCount
5429 * Returns the number of blocks that comprises this chain.
5430 * This is not the size of this chain as the last block may not be full!
5432 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5437 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5439 while (blockIndex != BLOCK_END_OF_CHAIN)
5443 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5450 /******************************************************************************
5451 * SmallBlockChainStream_SetSize
5453 * Sets the size of this stream.
5454 * The file will grow if we grow the chain.
5456 * TODO: Free the actual blocks in the file when we shrink the chain.
5457 * Currently, the blocks are still in the file. So the file size
5458 * doesn't shrink even if we shrink streams.
5460 BOOL SmallBlockChainStream_SetSize(
5461 SmallBlockChainStream* This,
5462 ULARGE_INTEGER newSize)
5464 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5466 if (newSize.u.LowPart == size.u.LowPart)
5469 if (newSize.u.LowPart < size.u.LowPart)
5471 SmallBlockChainStream_Shrink(This, newSize);
5475 SmallBlockChainStream_Enlarge(This, newSize);
5481 /******************************************************************************
5482 * SmallBlockChainStream_GetSize
5484 * Returns the size of this chain.
5486 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5488 StgProperty chainProperty;
5490 StorageImpl_ReadProperty(
5491 This->parentStorage,
5492 This->ownerPropertyIndex,
5495 return chainProperty.size;
5498 /******************************************************************************
5499 * StgCreateDocfile [OLE32.@]
5501 HRESULT WINAPI StgCreateDocfile(
5505 IStorage **ppstgOpen)
5507 StorageImpl* newStorage = 0;
5508 HANDLE hFile = INVALID_HANDLE_VALUE;
5509 HRESULT hr = STG_E_INVALIDFLAG;
5513 DWORD fileAttributes;
5514 WCHAR tempFileName[MAX_PATH];
5516 TRACE("(%s, %lx, %ld, %p)\n",
5517 debugstr_w(pwcsName), grfMode,
5518 reserved, ppstgOpen);
5521 * Validate the parameters
5524 return STG_E_INVALIDPOINTER;
5526 return STG_E_INVALIDPARAMETER;
5529 * Validate the STGM flags
5531 if ( FAILED( validateSTGM(grfMode) ))
5534 /* StgCreateDocFile always opens for write */
5535 switch(STGM_ACCESS_MODE(grfMode))
5538 case STGM_READWRITE:
5544 /* can't share write */
5545 switch(STGM_SHARE_MODE(grfMode))
5547 case STGM_SHARE_EXCLUSIVE:
5548 case STGM_SHARE_DENY_WRITE:
5554 /* shared reading requires transacted mode */
5555 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5556 !(grfMode&STGM_TRANSACTED) )
5560 * Generate a unique name.
5564 WCHAR tempPath[MAX_PATH];
5565 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5567 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
5570 memset(tempPath, 0, sizeof(tempPath));
5571 memset(tempFileName, 0, sizeof(tempFileName));
5573 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5576 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5577 pwcsName = tempFileName;
5580 hr = STG_E_INSUFFICIENTMEMORY;
5584 creationMode = TRUNCATE_EXISTING;
5588 creationMode = GetCreationModeFromSTGM(grfMode);
5592 * Interpret the STGM value grfMode
5594 shareMode = GetShareModeFromSTGM(grfMode);
5595 accessMode = GetAccessModeFromSTGM(grfMode);
5597 if (grfMode & STGM_DELETEONRELEASE)
5598 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5600 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5602 if (grfMode & STGM_TRANSACTED)
5603 FIXME("Transacted mode not implemented.\n");
5606 * Initialize the "out" parameter.
5610 hFile = CreateFileW(pwcsName,
5618 if (hFile == INVALID_HANDLE_VALUE)
5620 if(GetLastError() == ERROR_FILE_EXISTS)
5621 hr = STG_E_FILEALREADYEXISTS;
5628 * Allocate and initialize the new IStorage32object.
5630 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5632 if (newStorage == 0)
5634 hr = STG_E_INSUFFICIENTMEMORY;
5638 hr = StorageImpl_Construct(
5649 HeapFree(GetProcessHeap(), 0, newStorage);
5654 * Get an "out" pointer for the caller.
5656 hr = StorageBaseImpl_QueryInterface(
5657 (IStorage*)newStorage,
5658 (REFIID)&IID_IStorage,
5661 TRACE("<-- %p r = %08lx\n", *ppstgOpen, hr);
5666 /******************************************************************************
5667 * StgCreateStorageEx [OLE32.@]
5669 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5671 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5672 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5674 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5676 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5677 return STG_E_INVALIDPARAMETER;
5680 if (stgfmt != STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5682 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5683 return STG_E_INVALIDPARAMETER;
5686 if (stgfmt == STGFMT_FILE)
5688 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5689 return STG_E_INVALIDPARAMETER;
5692 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5694 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5695 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5698 ERR("Invalid stgfmt argument\n");
5699 return STG_E_INVALIDPARAMETER;
5702 /******************************************************************************
5703 * StgCreatePropSetStg [OLE32.@]
5705 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5706 IPropertySetStorage **ppPropSetStg)
5710 TRACE("(%p, 0x%lx, %p): stub\n", pstg, reserved, ppPropSetStg);
5712 hr = STG_E_INVALIDPARAMETER;
5714 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5715 (void**)ppPropSetStg);
5719 /******************************************************************************
5720 * StgOpenStorageEx [OLE32.@]
5722 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5724 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5725 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5727 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5729 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5730 return STG_E_INVALIDPARAMETER;
5733 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5735 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5736 return STG_E_INVALIDPARAMETER;
5739 if (stgfmt == STGFMT_FILE)
5741 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5742 return STG_E_INVALIDPARAMETER;
5745 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE || stgfmt == STGFMT_ANY)
5747 if (stgfmt == STGFMT_ANY)
5748 WARN("STGFMT_ANY assuming storage\n");
5749 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5750 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5753 ERR("Invalid stgfmt argument\n");
5754 return STG_E_INVALIDPARAMETER;
5758 /******************************************************************************
5759 * StgOpenStorage [OLE32.@]
5761 HRESULT WINAPI StgOpenStorage(
5762 const OLECHAR *pwcsName,
5763 IStorage *pstgPriority,
5767 IStorage **ppstgOpen)
5769 StorageImpl* newStorage = 0;
5774 WCHAR fullname[MAX_PATH];
5777 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5778 debugstr_w(pwcsName), pstgPriority, grfMode,
5779 snbExclude, reserved, ppstgOpen);
5782 * Perform sanity checks
5786 hr = STG_E_INVALIDNAME;
5792 hr = STG_E_INVALIDPOINTER;
5798 hr = STG_E_INVALIDPARAMETER;
5803 * Validate the sharing mode
5805 switch(STGM_SHARE_MODE(grfMode))
5807 case STGM_SHARE_EXCLUSIVE:
5808 case STGM_SHARE_DENY_WRITE:
5811 hr = STG_E_INVALIDFLAG;
5816 * Validate the STGM flags
5818 if ( FAILED( validateSTGM(grfMode) ) ||
5819 (grfMode&STGM_CREATE))
5821 hr = STG_E_INVALIDFLAG;
5825 /* shared reading requires transacted mode */
5826 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5827 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5828 !(grfMode&STGM_TRANSACTED) )
5830 hr = STG_E_INVALIDFLAG;
5835 * Interpret the STGM value grfMode
5837 shareMode = GetShareModeFromSTGM(grfMode);
5838 accessMode = GetAccessModeFromSTGM(grfMode);
5841 * Initialize the "out" parameter.
5845 hFile = CreateFileW( pwcsName,
5850 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5853 if (hFile==INVALID_HANDLE_VALUE)
5855 DWORD last_error = GetLastError();
5861 case ERROR_FILE_NOT_FOUND:
5862 hr = STG_E_FILENOTFOUND;
5865 case ERROR_PATH_NOT_FOUND:
5866 hr = STG_E_PATHNOTFOUND;
5869 case ERROR_ACCESS_DENIED:
5870 case ERROR_WRITE_PROTECT:
5871 hr = STG_E_ACCESSDENIED;
5874 case ERROR_SHARING_VIOLATION:
5875 hr = STG_E_SHAREVIOLATION;
5886 * Refuse to open the file if it's too small to be a structured storage file
5887 * FIXME: verify the file when reading instead of here
5889 length = GetFileSize(hFile, NULL);
5893 hr = STG_E_FILEALREADYEXISTS;
5898 * Allocate and initialize the new IStorage32object.
5900 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5902 if (newStorage == 0)
5904 hr = STG_E_INSUFFICIENTMEMORY;
5908 /* if the file's length was zero, initialize the storage */
5909 hr = StorageImpl_Construct(
5920 HeapFree(GetProcessHeap(), 0, newStorage);
5922 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5924 if(hr == STG_E_INVALIDHEADER)
5925 hr = STG_E_FILEALREADYEXISTS;
5929 /* prepare the file name string given in lieu of the root property name */
5930 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5931 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5932 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5935 * Get an "out" pointer for the caller.
5937 hr = StorageBaseImpl_QueryInterface(
5938 (IStorage*)newStorage,
5939 (REFIID)&IID_IStorage,
5943 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5947 /******************************************************************************
5948 * StgCreateDocfileOnILockBytes [OLE32.@]
5950 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5954 IStorage** ppstgOpen)
5956 StorageImpl* newStorage = 0;
5960 * Validate the parameters
5962 if ((ppstgOpen == 0) || (plkbyt == 0))
5963 return STG_E_INVALIDPOINTER;
5966 * Allocate and initialize the new IStorage object.
5968 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5970 if (newStorage == 0)
5971 return STG_E_INSUFFICIENTMEMORY;
5973 hr = StorageImpl_Construct(
5984 HeapFree(GetProcessHeap(), 0, newStorage);
5989 * Get an "out" pointer for the caller.
5991 hr = StorageBaseImpl_QueryInterface(
5992 (IStorage*)newStorage,
5993 (REFIID)&IID_IStorage,
5999 /******************************************************************************
6000 * StgOpenStorageOnILockBytes [OLE32.@]
6002 HRESULT WINAPI StgOpenStorageOnILockBytes(
6004 IStorage *pstgPriority,
6008 IStorage **ppstgOpen)
6010 StorageImpl* newStorage = 0;
6014 * Perform a sanity check
6016 if ((plkbyt == 0) || (ppstgOpen == 0))
6017 return STG_E_INVALIDPOINTER;
6020 * Validate the STGM flags
6022 if ( FAILED( validateSTGM(grfMode) ))
6023 return STG_E_INVALIDFLAG;
6026 * Initialize the "out" parameter.
6031 * Allocate and initialize the new IStorage object.
6033 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6035 if (newStorage == 0)
6036 return STG_E_INSUFFICIENTMEMORY;
6038 hr = StorageImpl_Construct(
6049 HeapFree(GetProcessHeap(), 0, newStorage);
6054 * Get an "out" pointer for the caller.
6056 hr = StorageBaseImpl_QueryInterface(
6057 (IStorage*)newStorage,
6058 (REFIID)&IID_IStorage,
6064 /******************************************************************************
6065 * StgSetTimes [ole32.@]
6066 * StgSetTimes [OLE32.@]
6070 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6071 FILETIME const *patime, FILETIME const *pmtime)
6073 IStorage *stg = NULL;
6076 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6078 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6082 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6083 IStorage_Release(stg);
6089 /******************************************************************************
6090 * StgIsStorageILockBytes [OLE32.@]
6092 * Determines if the ILockBytes contains a storage object.
6094 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6097 ULARGE_INTEGER offset;
6099 offset.u.HighPart = 0;
6100 offset.u.LowPart = 0;
6102 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6104 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6110 /******************************************************************************
6111 * WriteClassStg [OLE32.@]
6113 * This method will store the specified CLSID in the specified storage object
6115 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6121 hRes = IStorage_SetClass(pStg, rclsid);
6126 /***********************************************************************
6127 * ReadClassStg (OLE32.@)
6129 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
6131 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6141 * read a STATSTG structure (contains the clsid) from the storage
6143 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6146 *pclsid=pstatstg.clsid;
6151 /***********************************************************************
6152 * OleLoadFromStream (OLE32.@)
6154 * This function loads an object from stream
6156 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6160 LPPERSISTSTREAM xstm;
6162 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6164 res=ReadClassStm(pStm,&clsid);
6165 if (!SUCCEEDED(res))
6167 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6168 if (!SUCCEEDED(res))
6170 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6171 if (!SUCCEEDED(res)) {
6172 IUnknown_Release((IUnknown*)*ppvObj);
6175 res=IPersistStream_Load(xstm,pStm);
6176 IPersistStream_Release(xstm);
6177 /* FIXME: all refcounts ok at this point? I think they should be:
6180 * xstm : 0 (released)
6185 /***********************************************************************
6186 * OleSaveToStream (OLE32.@)
6188 * This function saves an object with the IPersistStream interface on it
6189 * to the specified stream.
6191 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6197 TRACE("(%p,%p)\n",pPStm,pStm);
6199 res=IPersistStream_GetClassID(pPStm,&clsid);
6201 if (SUCCEEDED(res)){
6203 res=WriteClassStm(pStm,&clsid);
6207 res=IPersistStream_Save(pPStm,pStm,TRUE);
6210 TRACE("Finished Save\n");
6214 /****************************************************************************
6215 * This method validate a STGM parameter that can contain the values below
6217 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6218 * The stgm values contained in 0xffff0000 are bitmasks.
6220 * STGM_DIRECT 0x00000000
6221 * STGM_TRANSACTED 0x00010000
6222 * STGM_SIMPLE 0x08000000
6224 * STGM_READ 0x00000000
6225 * STGM_WRITE 0x00000001
6226 * STGM_READWRITE 0x00000002
6228 * STGM_SHARE_DENY_NONE 0x00000040
6229 * STGM_SHARE_DENY_READ 0x00000030
6230 * STGM_SHARE_DENY_WRITE 0x00000020
6231 * STGM_SHARE_EXCLUSIVE 0x00000010
6233 * STGM_PRIORITY 0x00040000
6234 * STGM_DELETEONRELEASE 0x04000000
6236 * STGM_CREATE 0x00001000
6237 * STGM_CONVERT 0x00020000
6238 * STGM_FAILIFTHERE 0x00000000
6240 * STGM_NOSCRATCH 0x00100000
6241 * STGM_NOSNAPSHOT 0x00200000
6243 static HRESULT validateSTGM(DWORD stgm)
6245 DWORD access = STGM_ACCESS_MODE(stgm);
6246 DWORD share = STGM_SHARE_MODE(stgm);
6247 DWORD create = STGM_CREATE_MODE(stgm);
6249 if (stgm&~STGM_KNOWN_FLAGS)
6251 ERR("unknown flags %08lx\n", stgm);
6259 case STGM_READWRITE:
6267 case STGM_SHARE_DENY_NONE:
6268 case STGM_SHARE_DENY_READ:
6269 case STGM_SHARE_DENY_WRITE:
6270 case STGM_SHARE_EXCLUSIVE:
6279 case STGM_FAILIFTHERE:
6286 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6288 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6292 * STGM_CREATE | STGM_CONVERT
6293 * if both are false, STGM_FAILIFTHERE is set to TRUE
6295 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6299 * STGM_NOSCRATCH requires STGM_TRANSACTED
6301 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6305 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6306 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6308 if ( (stgm & STGM_NOSNAPSHOT) &&
6309 (!(stgm & STGM_TRANSACTED) ||
6310 share == STGM_SHARE_EXCLUSIVE ||
6311 share == STGM_SHARE_DENY_WRITE) )
6317 /****************************************************************************
6318 * GetShareModeFromSTGM
6320 * This method will return a share mode flag from a STGM value.
6321 * The STGM value is assumed valid.
6323 static DWORD GetShareModeFromSTGM(DWORD stgm)
6325 switch (STGM_SHARE_MODE(stgm))
6327 case STGM_SHARE_DENY_NONE:
6328 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6329 case STGM_SHARE_DENY_READ:
6330 return FILE_SHARE_WRITE;
6331 case STGM_SHARE_DENY_WRITE:
6332 return FILE_SHARE_READ;
6333 case STGM_SHARE_EXCLUSIVE:
6336 ERR("Invalid share mode!\n");
6341 /****************************************************************************
6342 * GetAccessModeFromSTGM
6344 * This method will return an access mode flag from a STGM value.
6345 * The STGM value is assumed valid.
6347 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6349 switch (STGM_ACCESS_MODE(stgm))
6352 return GENERIC_READ;
6354 case STGM_READWRITE:
6355 return GENERIC_READ | GENERIC_WRITE;
6357 ERR("Invalid access mode!\n");
6362 /****************************************************************************
6363 * GetCreationModeFromSTGM
6365 * This method will return a creation mode flag from a STGM value.
6366 * The STGM value is assumed valid.
6368 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6370 switch(STGM_CREATE_MODE(stgm))
6373 return CREATE_ALWAYS;
6375 FIXME("STGM_CONVERT not implemented!\n");
6377 case STGM_FAILIFTHERE:
6380 ERR("Invalid create mode!\n");
6386 /*************************************************************************
6387 * OLECONVERT_LoadOLE10 [Internal]
6389 * Loads the OLE10 STREAM to memory
6392 * pOleStream [I] The OLESTREAM
6393 * pData [I] Data Structure for the OLESTREAM Data
6397 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6398 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6401 * This function is used by OleConvertOLESTREAMToIStorage only.
6403 * Memory allocated for pData must be freed by the caller
6405 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6408 HRESULT hRes = S_OK;
6412 pData->pData = NULL;
6413 pData->pstrOleObjFileName = (CHAR *) NULL;
6415 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6418 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6419 if(dwSize != sizeof(pData->dwOleID))
6421 hRes = CONVERT10_E_OLESTREAM_GET;
6423 else if(pData->dwOleID != OLESTREAM_ID)
6425 hRes = CONVERT10_E_OLESTREAM_FMT;
6436 /* Get the TypeID...more info needed for this field */
6437 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6438 if(dwSize != sizeof(pData->dwTypeID))
6440 hRes = CONVERT10_E_OLESTREAM_GET;
6445 if(pData->dwTypeID != 0)
6447 /* Get the length of the OleTypeName */
6448 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6449 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6451 hRes = CONVERT10_E_OLESTREAM_GET;
6456 if(pData->dwOleTypeNameLength > 0)
6458 /* Get the OleTypeName */
6459 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6460 if(dwSize != pData->dwOleTypeNameLength)
6462 hRes = CONVERT10_E_OLESTREAM_GET;
6468 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6469 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6471 hRes = CONVERT10_E_OLESTREAM_GET;
6475 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6476 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6477 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6478 if(pData->pstrOleObjFileName)
6480 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6481 if(dwSize != pData->dwOleObjFileNameLength)
6483 hRes = CONVERT10_E_OLESTREAM_GET;
6487 hRes = CONVERT10_E_OLESTREAM_GET;
6492 /* Get the Width of the Metafile */
6493 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6494 if(dwSize != sizeof(pData->dwMetaFileWidth))
6496 hRes = CONVERT10_E_OLESTREAM_GET;
6500 /* Get the Height of the Metafile */
6501 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6502 if(dwSize != sizeof(pData->dwMetaFileHeight))
6504 hRes = CONVERT10_E_OLESTREAM_GET;
6510 /* Get the Length of the Data */
6511 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6512 if(dwSize != sizeof(pData->dwDataLength))
6514 hRes = CONVERT10_E_OLESTREAM_GET;
6518 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6520 if(!bStrem1) /* if it is a second OLE stream data */
6522 pData->dwDataLength -= 8;
6523 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6524 if(dwSize != sizeof(pData->strUnknown))
6526 hRes = CONVERT10_E_OLESTREAM_GET;
6532 if(pData->dwDataLength > 0)
6534 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6536 /* Get Data (ex. IStorage, Metafile, or BMP) */
6539 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6540 if(dwSize != pData->dwDataLength)
6542 hRes = CONVERT10_E_OLESTREAM_GET;
6547 hRes = CONVERT10_E_OLESTREAM_GET;
6556 /*************************************************************************
6557 * OLECONVERT_SaveOLE10 [Internal]
6559 * Saves the OLE10 STREAM From memory
6562 * pData [I] Data Structure for the OLESTREAM Data
6563 * pOleStream [I] The OLESTREAM to save
6567 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6570 * This function is used by OleConvertIStorageToOLESTREAM only.
6573 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6576 HRESULT hRes = S_OK;
6580 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6581 if(dwSize != sizeof(pData->dwOleID))
6583 hRes = CONVERT10_E_OLESTREAM_PUT;
6588 /* Set the TypeID */
6589 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6590 if(dwSize != sizeof(pData->dwTypeID))
6592 hRes = CONVERT10_E_OLESTREAM_PUT;
6596 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6598 /* Set the Length of the OleTypeName */
6599 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6600 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6602 hRes = CONVERT10_E_OLESTREAM_PUT;
6607 if(pData->dwOleTypeNameLength > 0)
6609 /* Set the OleTypeName */
6610 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6611 if(dwSize != pData->dwOleTypeNameLength)
6613 hRes = CONVERT10_E_OLESTREAM_PUT;
6620 /* Set the width of the Metafile */
6621 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6622 if(dwSize != sizeof(pData->dwMetaFileWidth))
6624 hRes = CONVERT10_E_OLESTREAM_PUT;
6630 /* Set the height of the Metafile */
6631 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6632 if(dwSize != sizeof(pData->dwMetaFileHeight))
6634 hRes = CONVERT10_E_OLESTREAM_PUT;
6640 /* Set the length of the Data */
6641 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6642 if(dwSize != sizeof(pData->dwDataLength))
6644 hRes = CONVERT10_E_OLESTREAM_PUT;
6650 if(pData->dwDataLength > 0)
6652 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6653 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6654 if(dwSize != pData->dwDataLength)
6656 hRes = CONVERT10_E_OLESTREAM_PUT;
6664 /*************************************************************************
6665 * OLECONVERT_GetOLE20FromOLE10[Internal]
6667 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6668 * opens it, and copies the content to the dest IStorage for
6669 * OleConvertOLESTREAMToIStorage
6673 * pDestStorage [I] The IStorage to copy the data to
6674 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6675 * nBufferLength [I] The size of the buffer
6684 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6688 IStorage *pTempStorage;
6689 DWORD dwNumOfBytesWritten;
6690 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6691 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6693 /* Create a temp File */
6694 GetTempPathW(MAX_PATH, wstrTempDir);
6695 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6696 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6698 if(hFile != INVALID_HANDLE_VALUE)
6700 /* Write IStorage Data to File */
6701 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6704 /* Open and copy temp storage to the Dest Storage */
6705 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6708 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6709 StorageBaseImpl_Release(pTempStorage);
6711 DeleteFileW(wstrTempFile);
6716 /*************************************************************************
6717 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6719 * Saves the OLE10 STREAM From memory
6722 * pStorage [I] The Src IStorage to copy
6723 * pData [I] The Dest Memory to write to.
6726 * The size in bytes allocated for pData
6729 * Memory allocated for pData must be freed by the caller
6731 * Used by OleConvertIStorageToOLESTREAM only.
6734 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6738 DWORD nDataLength = 0;
6739 IStorage *pTempStorage;
6740 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6741 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6745 /* Create temp Storage */
6746 GetTempPathW(MAX_PATH, wstrTempDir);
6747 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6748 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6752 /* Copy Src Storage to the Temp Storage */
6753 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6754 StorageBaseImpl_Release(pTempStorage);
6756 /* Open Temp Storage as a file and copy to memory */
6757 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6758 if(hFile != INVALID_HANDLE_VALUE)
6760 nDataLength = GetFileSize(hFile, NULL);
6761 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6762 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6765 DeleteFileW(wstrTempFile);
6770 /*************************************************************************
6771 * OLECONVERT_CreateOleStream [Internal]
6773 * Creates the "\001OLE" stream in the IStorage if necessary.
6776 * pStorage [I] Dest storage to create the stream in
6782 * This function is used by OleConvertOLESTREAMToIStorage only.
6784 * This stream is still unknown, MS Word seems to have extra data
6785 * but since the data is stored in the OLESTREAM there should be
6786 * no need to recreate the stream. If the stream is manually
6787 * deleted it will create it with this default data.
6790 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6794 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6795 BYTE pOleStreamHeader [] =
6797 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6798 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6799 0x00, 0x00, 0x00, 0x00
6802 /* Create stream if not present */
6803 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6804 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6808 /* Write default Data */
6809 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6810 IStream_Release(pStream);
6814 /* write a string to a stream, preceded by its length */
6815 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6822 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6823 r = IStream_Write( stm, &len, sizeof(len), NULL);
6828 str = CoTaskMemAlloc( len );
6829 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6830 r = IStream_Write( stm, str, len, NULL);
6831 CoTaskMemFree( str );
6835 /* read a string preceded by its length from a stream */
6836 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6839 DWORD len, count = 0;
6843 r = IStream_Read( stm, &len, sizeof(len), &count );
6846 if( count != sizeof(len) )
6847 return E_OUTOFMEMORY;
6849 TRACE("%ld bytes\n",len);
6851 str = CoTaskMemAlloc( len );
6853 return E_OUTOFMEMORY;
6855 r = IStream_Read( stm, str, len, &count );
6860 CoTaskMemFree( str );
6861 return E_OUTOFMEMORY;
6864 TRACE("Read string %s\n",debugstr_an(str,len));
6866 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6867 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6869 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6870 CoTaskMemFree( str );
6878 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6879 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6883 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6885 static const BYTE unknown1[12] =
6886 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6887 0xFF, 0xFF, 0xFF, 0xFF};
6888 static const BYTE unknown2[16] =
6889 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6890 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6892 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6893 debugstr_w(lpszUserType), debugstr_w(szClipName),
6894 debugstr_w(szProgIDName));
6896 /* Create a CompObj stream if it doesn't exist */
6897 r = IStorage_CreateStream(pstg, szwStreamName,
6898 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6902 /* Write CompObj Structure to stream */
6903 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6905 if( SUCCEEDED( r ) )
6906 r = WriteClassStm( pstm, clsid );
6908 if( SUCCEEDED( r ) )
6909 r = STREAM_WriteString( pstm, lpszUserType );
6910 if( SUCCEEDED( r ) )
6911 r = STREAM_WriteString( pstm, szClipName );
6912 if( SUCCEEDED( r ) )
6913 r = STREAM_WriteString( pstm, szProgIDName );
6914 if( SUCCEEDED( r ) )
6915 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6917 IStream_Release( pstm );
6922 /***********************************************************************
6923 * WriteFmtUserTypeStg (OLE32.@)
6925 HRESULT WINAPI WriteFmtUserTypeStg(
6926 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6929 WCHAR szwClipName[0x40];
6930 CLSID clsid = CLSID_NULL;
6931 LPWSTR wstrProgID = NULL;
6934 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6936 /* get the clipboard format name */
6937 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
6940 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6942 /* FIXME: There's room to save a CLSID and its ProgID, but
6943 the CLSID is not looked up in the registry and in all the
6944 tests I wrote it was CLSID_NULL. Where does it come from?
6947 /* get the real program ID. This may fail, but that's fine */
6948 ProgIDFromCLSID(&clsid, &wstrProgID);
6950 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6952 r = STORAGE_WriteCompObj( pstg, &clsid,
6953 lpszUserType, szwClipName, wstrProgID );
6955 CoTaskMemFree(wstrProgID);
6961 /******************************************************************************
6962 * ReadFmtUserTypeStg [OLE32.@]
6964 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6968 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6969 unsigned char unknown1[12];
6970 unsigned char unknown2[16];
6972 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6975 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6977 r = IStorage_OpenStream( pstg, szCompObj, NULL,
6978 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6981 WARN("Failed to open stream r = %08lx\n", r);
6985 /* read the various parts of the structure */
6986 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
6987 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
6989 r = ReadClassStm( stm, &clsid );
6993 r = STREAM_ReadString( stm, &szCLSIDName );
6997 r = STREAM_ReadString( stm, &szOleTypeName );
7001 r = STREAM_ReadString( stm, &szProgIDName );
7005 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7006 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7009 /* ok, success... now we just need to store what we found */
7011 *pcf = RegisterClipboardFormatW( szOleTypeName );
7012 CoTaskMemFree( szOleTypeName );
7014 if( lplpszUserType )
7015 *lplpszUserType = szCLSIDName;
7016 CoTaskMemFree( szProgIDName );
7019 IStream_Release( stm );
7025 /*************************************************************************
7026 * OLECONVERT_CreateCompObjStream [Internal]
7028 * Creates a "\001CompObj" is the destination IStorage if necessary.
7031 * pStorage [I] The dest IStorage to create the CompObj Stream
7033 * strOleTypeName [I] The ProgID
7037 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7040 * This function is used by OleConvertOLESTREAMToIStorage only.
7042 * The stream data is stored in the OLESTREAM and there should be
7043 * no need to recreate the stream. If the stream is manually
7044 * deleted it will attempt to create it by querying the registry.
7048 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7051 HRESULT hStorageRes, hRes = S_OK;
7052 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7053 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7054 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7056 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7057 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7059 /* Initialize the CompObj structure */
7060 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7061 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7062 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7065 /* Create a CompObj stream if it doesn't exist */
7066 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7067 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7068 if(hStorageRes == S_OK)
7070 /* copy the OleTypeName to the compobj struct */
7071 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7072 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7074 /* copy the OleTypeName to the compobj struct */
7075 /* Note: in the test made, these were Identical */
7076 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7077 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7080 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7081 bufferW, OLESTREAM_MAX_STR_LEN );
7082 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7088 /* Get the CLSID Default Name from the Registry */
7089 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7090 if(hErr == ERROR_SUCCESS)
7092 char strTemp[OLESTREAM_MAX_STR_LEN];
7093 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7094 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7095 if(hErr == ERROR_SUCCESS)
7097 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7103 /* Write CompObj Structure to stream */
7104 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7106 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7108 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7109 if(IStorageCompObj.dwCLSIDNameLength > 0)
7111 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7113 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7114 if(IStorageCompObj.dwOleTypeNameLength > 0)
7116 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7118 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7119 if(IStorageCompObj.dwProgIDNameLength > 0)
7121 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7123 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7124 IStream_Release(pStream);
7130 /*************************************************************************
7131 * OLECONVERT_CreateOlePresStream[Internal]
7133 * Creates the "\002OlePres000" Stream with the Metafile data
7136 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7137 * dwExtentX [I] Width of the Metafile
7138 * dwExtentY [I] Height of the Metafile
7139 * pData [I] Metafile data
7140 * dwDataLength [I] Size of the Metafile data
7144 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7147 * This function is used by OleConvertOLESTREAMToIStorage only.
7150 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7154 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7155 BYTE pOlePresStreamHeader [] =
7157 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7158 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7159 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7160 0x00, 0x00, 0x00, 0x00
7163 BYTE pOlePresStreamHeaderEmpty [] =
7165 0x00, 0x00, 0x00, 0x00,
7166 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7167 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7168 0x00, 0x00, 0x00, 0x00
7171 /* Create the OlePres000 Stream */
7172 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7173 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7178 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7180 memset(&OlePres, 0, sizeof(OlePres));
7181 /* Do we have any metafile data to save */
7182 if(dwDataLength > 0)
7184 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7185 nHeaderSize = sizeof(pOlePresStreamHeader);
7189 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7190 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7192 /* Set width and height of the metafile */
7193 OlePres.dwExtentX = dwExtentX;
7194 OlePres.dwExtentY = -dwExtentY;
7196 /* Set Data and Length */
7197 if(dwDataLength > sizeof(METAFILEPICT16))
7199 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7200 OlePres.pData = &(pData[8]);
7202 /* Save OlePres000 Data to Stream */
7203 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7204 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7205 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7206 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7207 if(OlePres.dwSize > 0)
7209 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7211 IStream_Release(pStream);
7215 /*************************************************************************
7216 * OLECONVERT_CreateOle10NativeStream [Internal]
7218 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7221 * pStorage [I] Dest storage to create the stream in
7222 * pData [I] Ole10 Native Data (ex. bmp)
7223 * dwDataLength [I] Size of the Ole10 Native Data
7229 * This function is used by OleConvertOLESTREAMToIStorage only.
7231 * Might need to verify the data and return appropriate error message
7234 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7238 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7240 /* Create the Ole10Native Stream */
7241 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7242 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7246 /* Write info to stream */
7247 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7248 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7249 IStream_Release(pStream);
7254 /*************************************************************************
7255 * OLECONVERT_GetOLE10ProgID [Internal]
7257 * Finds the ProgID (or OleTypeID) from the IStorage
7260 * pStorage [I] The Src IStorage to get the ProgID
7261 * strProgID [I] the ProgID string to get
7262 * dwSize [I] the size of the string
7266 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7269 * This function is used by OleConvertIStorageToOLESTREAM only.
7273 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7277 LARGE_INTEGER iSeekPos;
7278 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7279 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7281 /* Open the CompObj Stream */
7282 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7283 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7287 /*Get the OleType from the CompObj Stream */
7288 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7289 iSeekPos.u.HighPart = 0;
7291 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7292 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7293 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7294 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7295 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7296 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7297 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7299 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7302 IStream_Read(pStream, strProgID, *dwSize, NULL);
7304 IStream_Release(pStream);
7309 LPOLESTR wstrProgID;
7311 /* Get the OleType from the registry */
7312 REFCLSID clsid = &(stat.clsid);
7313 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7314 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7317 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7324 /*************************************************************************
7325 * OLECONVERT_GetOle10PresData [Internal]
7327 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7330 * pStorage [I] Src IStroage
7331 * pOleStream [I] Dest OleStream Mem Struct
7337 * This function is used by OleConvertIStorageToOLESTREAM only.
7339 * Memory allocated for pData must be freed by the caller
7343 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7348 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7350 /* Initialize Default data for OLESTREAM */
7351 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7352 pOleStreamData[0].dwTypeID = 2;
7353 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7354 pOleStreamData[1].dwTypeID = 0;
7355 pOleStreamData[0].dwMetaFileWidth = 0;
7356 pOleStreamData[0].dwMetaFileHeight = 0;
7357 pOleStreamData[0].pData = NULL;
7358 pOleStreamData[1].pData = NULL;
7360 /* Open Ole10Native Stream */
7361 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7362 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7366 /* Read Size and Data */
7367 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7368 if(pOleStreamData->dwDataLength > 0)
7370 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7371 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7373 IStream_Release(pStream);
7379 /*************************************************************************
7380 * OLECONVERT_GetOle20PresData[Internal]
7382 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7385 * pStorage [I] Src IStroage
7386 * pOleStreamData [I] Dest OleStream Mem Struct
7392 * This function is used by OleConvertIStorageToOLESTREAM only.
7394 * Memory allocated for pData must be freed by the caller
7396 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7400 OLECONVERT_ISTORAGE_OLEPRES olePress;
7401 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7403 /* Initialize Default data for OLESTREAM */
7404 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7405 pOleStreamData[0].dwTypeID = 2;
7406 pOleStreamData[0].dwMetaFileWidth = 0;
7407 pOleStreamData[0].dwMetaFileHeight = 0;
7408 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7409 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7410 pOleStreamData[1].dwTypeID = 0;
7411 pOleStreamData[1].dwOleTypeNameLength = 0;
7412 pOleStreamData[1].strOleTypeName[0] = 0;
7413 pOleStreamData[1].dwMetaFileWidth = 0;
7414 pOleStreamData[1].dwMetaFileHeight = 0;
7415 pOleStreamData[1].pData = NULL;
7416 pOleStreamData[1].dwDataLength = 0;
7419 /* Open OlePress000 stream */
7420 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7421 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7424 LARGE_INTEGER iSeekPos;
7425 METAFILEPICT16 MetaFilePict;
7426 static const char strMetafilePictName[] = "METAFILEPICT";
7428 /* Set the TypeID for a Metafile */
7429 pOleStreamData[1].dwTypeID = 5;
7431 /* Set the OleTypeName to Metafile */
7432 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7433 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7435 iSeekPos.u.HighPart = 0;
7436 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7438 /* Get Presentation Data */
7439 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7440 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7441 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7442 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7444 /*Set width and Height */
7445 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7446 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7447 if(olePress.dwSize > 0)
7450 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7452 /* Set MetaFilePict struct */
7453 MetaFilePict.mm = 8;
7454 MetaFilePict.xExt = olePress.dwExtentX;
7455 MetaFilePict.yExt = olePress.dwExtentY;
7456 MetaFilePict.hMF = 0;
7458 /* Get Metafile Data */
7459 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7460 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7461 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7463 IStream_Release(pStream);
7467 /*************************************************************************
7468 * OleConvertOLESTREAMToIStorage [OLE32.@]
7473 * DVTARGETDEVICE paramenter is not handled
7474 * Still unsure of some mem fields for OLE 10 Stream
7475 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7476 * and "\001OLE" streams
7479 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7480 LPOLESTREAM pOleStream,
7482 const DVTARGETDEVICE* ptd)
7486 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7488 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7492 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7495 if(pstg == NULL || pOleStream == NULL)
7497 hRes = E_INVALIDARG;
7502 /* Load the OLESTREAM to Memory */
7503 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7508 /* Load the OLESTREAM to Memory (part 2)*/
7509 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7515 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7517 /* Do we have the IStorage Data in the OLESTREAM */
7518 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7520 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7521 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7525 /* It must be an original OLE 1.0 source */
7526 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7531 /* It must be an original OLE 1.0 source */
7532 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7535 /* Create CompObj Stream if necessary */
7536 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7539 /*Create the Ole Stream if necessary */
7540 OLECONVERT_CreateOleStream(pstg);
7545 /* Free allocated memory */
7546 for(i=0; i < 2; i++)
7548 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7549 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7550 pOleStreamData[i].pstrOleObjFileName = NULL;
7555 /*************************************************************************
7556 * OleConvertIStorageToOLESTREAM [OLE32.@]
7563 * Still unsure of some mem fields for OLE 10 Stream
7564 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7565 * and "\001OLE" streams.
7568 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7570 LPOLESTREAM pOleStream)
7573 HRESULT hRes = S_OK;
7575 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7576 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7579 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7581 if(pstg == NULL || pOleStream == NULL)
7583 hRes = E_INVALIDARG;
7587 /* Get the ProgID */
7588 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7589 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7593 /* Was it originally Ole10 */
7594 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7597 IStream_Release(pStream);
7598 /* Get Presentation Data for Ole10Native */
7599 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7603 /* Get Presentation Data (OLE20) */
7604 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7607 /* Save OLESTREAM */
7608 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7611 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7616 /* Free allocated memory */
7617 for(i=0; i < 2; i++)
7619 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7625 /***********************************************************************
7626 * GetConvertStg (OLE32.@)
7628 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7629 FIXME("unimplemented stub!\n");
7633 /******************************************************************************
7634 * StgIsStorageFile [OLE32.@]
7637 StgIsStorageFile(LPCOLESTR fn)
7643 TRACE("(\'%s\')\n", debugstr_w(fn));
7644 hf = CreateFileW(fn, GENERIC_READ,
7645 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7646 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7648 if (hf == INVALID_HANDLE_VALUE)
7649 return STG_E_FILENOTFOUND;
7651 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7653 WARN(" unable to read file\n");
7660 if (bytes_read != 8) {
7661 WARN(" too short\n");
7665 if (!memcmp(magic,STORAGE_magic,8)) {
7670 WARN(" -> Invalid header.\n");